def _autodetect_startmenu(self, exe_name, name_pattern): known_folders = ( "{625b53c3-ab48-4ec1-ba1f-a1ef4146fc19}", # FOLDERID_StartMenu "{a4115719-d62e-491d-aa7c-e74b8be3b067}") # FOLDERID_CommonStartMenu found_link_files = [] for kf_guid in known_folders: try: known_dir = kpu.shell_known_folder_path(kf_guid) found_link_files += [ os.path.join(known_dir, f) for f in kpu.scan_directory( known_dir, name_pattern, kpu.ScanFlags.FILES, -1)] except Exception as exc: self.dbg(str(exc)) pass for link_file in found_link_files: try: link_props = kpu.read_link(link_file) if (link_props['target'].lower().endswith(exe_name) and os.path.exists(link_props['target'])): return link_props['target'] except Exception as exc: self.dbg(str(exc)) pass return None
def _add_from_recent_directory(self, catalog): recent_directory = os.path.join( kpu.shell_known_folder_path(FOLDERID.RoamingAppData.value), "Microsoft", "Windows", "Recent" ) try: recent_links = kpu.scan_directory( recent_directory, "*.lnk", kpu.ScanFlags.FILES, max_level=0 ) except OSError as e: self.dbg(str(e)) return for link_name in recent_links: link_path = os.path.join(recent_directory, link_name) item = self.create_item( category=kp.ItemCategory.FILE, label=os.path.splitext(link_name)[0], short_desc="Recent link: " + link_path, target=link_path, args_hint=kp.ItemArgsHint.ACCEPTED, hit_hint=kp.ItemHitHint.KEEPALL ) catalog.append(item)
def __init__(self, *args): super().__init__(*args) try: self.localappdata_dir = kpu.shell_known_folder_path( "{f1b32785-6fba-4fcf-9d55-7b8e7f157091}") except OSError: self.plugin.warn("Failed to get LocalAppData directory!")
def _autodetect_startmenu(self, exe_name, name_pattern): known_folders = ( "{625b53c3-ab48-4ec1-ba1f-a1ef4146fc19}", # FOLDERID_StartMenu "{a4115719-d62e-491d-aa7c-e74b8be3b067}") # FOLDERID_CommonStartMenu found_link_files = [] for kf_guid in known_folders: try: known_dir = kpu.shell_known_folder_path(kf_guid) found_link_files += [ os.path.join(known_dir, f) for f in kpu.scan_directory( known_dir, name_pattern, kpu.ScanFlags.FILES, -1)] except Exception as e: self.dbg(e) pass for link_file in found_link_files: try: link_props = kpu.read_link(link_file) if (link_props['target'].lower().endswith(exe_name) and os.path.exists(link_props['target'])): return link_props['target'] except Exception as e: self.dbg(e) pass return None
def _read_config(self): settings = self.load_settings() self.show_recents = settings.get_bool("show_recents", "main", self.DEFAULT_SHOW_RECENTS) self.show_dirs_first = settings.get_bool("show_dirs_first", "main", self.DEFAULT_SHOW_DIRS_FIRST) self.show_hidden_files = settings.get_bool( "show_hidden_files", "main", self.DEFAULT_SHOW_HIDDEN_FILES) self.show_system_files = settings.get_bool( "show_system_files", "main", self.DEFAULT_SHOW_SYSTEM_FILES) self.show_network_files = settings.get_bool( "show_network_files", "main", self.DEFAULT_SHOW_NETWORK_FILES) self.follow_shell_links = settings.get_bool( "follow_shell_links", "main", self.DEFAULT_FOLLOW_SHELL_LINKS) self.home_trigger = settings.get_stripped("trigger", "home", self.DEFAULT_HOME_TRIGGER) self.home = [] if len(self.home_trigger) > 0: home_value_lines = settings.get_multiline("home", "home", []) # apply default "home" value if needed if not home_value_lines: try: home_value_lines = [ kpwt.get_known_folder_path(kpwt.FOLDERID.Profile) ] except OSError as exc: self.warn(str(exc)) home_value_lines = [] for idx in range(len(home_value_lines)): home_dir = home_value_lines[idx].replace("/", os.sep) # If home_dir is prefixed by a "::{guid}" sequence if home_dir.startswith('::') and len(home_dir) >= 38: (guid, tail) = (home_dir[2:].split(os.sep, maxsplit=1) + [None] * 2)[:2] try: kf_path = kpu.shell_known_folder_path(guid) if tail is not None: self.home.append(os.path.join(kf_path, tail)) else: self.home.append(kf_path) except OSError: self.warn( "Failed to get path of known folder from setting \"{}\"" .format(home_dir)) continue # Otherwise, home_dir is assumed to be a valid path to a # directory. In order to be as flexible as possible, we must # not assume it already exists. else: self.home.append(os.path.normpath(home_dir))
def _read_config(self): settings = self.load_settings() # It's the folder FOLDERID_Documents ("%USERPROFILE%\Documents") # https://docs.microsoft.com/sv-se/windows/win32/shell/knownfolderid?redirectedfrom=MSDN default_path = kpu.shell_known_folder_path( "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}") self._filepath = settings.get_stripped("file_path", "main", default_path) if os.path.isdir(self._filepath): self._filepath = os.path.join(self._filepath, "todo.md")
def list_bookmarks(self): favorites_dirs = self.settings.get_multiline("favorites_dirs", self.config_section) if not favorites_dirs: favorites_dirs = [ kpu.shell_known_folder_path( "{1777f761-68ad-4d8a-87bd-30b759fa33dd}") ] bookmarks = [] for favdir in favorites_dirs: favdir = os.path.normpath(favdir) try: url_files = kpu.scan_directory(favdir, "*.url", kpu.ScanFlags.FILES, -1) except OSError as exc: print(self.__class__.__name__ + ":", exc, file=sys.stderr) continue for url_file in url_files: url_file = os.path.join(favdir, url_file) #bookmarks.append(self.plugin.create_item( # category=kp.ItemCategory.FILE, # label=os.path.splitext(os.path.basename(url_file))[0], # short_desc=self.label, # trick to transport provider's label # target=url_file, # args_hint=kp.ItemArgsHint.FORBIDDEN, # hit_hint=kp.ItemHitHint.NOARGS)) try: bk_url = None with kpu.chardet_open(url_file, mode="rt") as fh: in_url_section = False for line in fh: line = line.strip() if not in_url_section: if line.lower() == "[internetshortcut]": in_url_section = True else: if line.lower().startswith("url="): bk_url = line[len("url="):].strip() break if bk_url: bk_label = os.path.splitext( os.path.basename(url_file))[0] bookmarks.append(Bookmark(self.label, bk_label, bk_url)) except Exception as exc: self.plugin.warn( "Failed to read URL file: {}. Error: {}".format( url_file, exc)) return bookmarks
class WindowsTerminalWrapper: LOCALAPPDATA = kpu.shell_known_folder_path( "{f1b32785-6fba-4fcf-9d55-7b8e7f157091}") def settings_file(): terminal_package = "Microsoft.WindowsTerminal_8wekyb3d8bbwe" return WindowsTerminalWrapper.LOCALAPPDATA + \ "\\Packages\\" + terminal_package + "\\LocalState\\settings.json" def executable(): return WindowsTerminalWrapper.LOCALAPPDATA + "\\Microsoft\\WindowsApps\\wt.exe" def __init__(self, settings, executable): if not os.path.exists(settings): raise ValueError("Could not find Windows Terminal settings at %s" % (settings)) if not os.path.exists(executable) and not os.path.lexists(executable): raise ValueError("Could not find Windows Terminal at %s" % (executable)) self._wt_settings = settings self._wt_executable = executable def profiles(self): with kpu.chardet_open(self._wt_settings, mode="rt") as terminal_settings: data = json.loads(jsmin(terminal_settings.read())) profiles = data.get("profiles") if not profiles: return [] # the profile list can be 'profiles' itself, or nested under 'list' profilesList = profiles.get("list", []) \ if isinstance(profiles, dict) else profiles return [p for p in profilesList if p.get('hidden', False) == False] def openprofile(self, guid, elevate=False): if elevate: kpu.shell_execute("cmd.exe", args=[ '/c', 'start', '', '/b', self._wt_executable, '--profile', guid ], verb="runas") else: kpu.shell_execute(self._wt_executable, args=['--profile', guid])
def _catalog_knownfolder(self, kf_guid, kf_label, kf_desc, recursive_scan): try: kf_path = kpu.shell_known_folder_path(kf_guid) except OSError: self.warn("Failed to get path of known folder {}".format(kf_label)) return [] if self.should_terminate(): return [] max_scan_level = -1 if recursive_scan else 0 try: files = kpu.scan_directory(kf_path, ('*'), flags=kpu.ScanFlags.FILES, max_level=max_scan_level) except IOError: return [] if self.should_terminate(): return [] catalog = [] for relative in files: f = os.path.normpath(os.path.join(kf_path, relative)) label = os.path.splitext(os.path.basename(f))[0] desc = os.path.dirname(relative) if not len(desc): desc = os.path.basename(kf_path) if not len(desc): desc = label desc = kf_desc + ": " + desc catalog.append( self.create_item(category=kp.ItemCategory.FILE, label=label, short_desc=desc, target=f, args_hint=kp.ItemArgsHint.ACCEPTED, hit_hint=kp.ItemHitHint.KEEPALL)) return catalog
def list_bookmarks(self): places_files = self.settings.get_multiline("places_files", self.config_section) if not places_files: roaming_app_data_dir = kpu.shell_known_folder_path( "{3eb685db-65f9-4cf6-a03a-e3ef65729f3d}") profiles_file = os.path.join(roaming_app_data_dir, "Mozilla", "Firefox", "profiles.ini") if not os.path.isfile(profiles_file): return [] profiles_db = FirefoxProfilesDb() try: profiles_db.read(profiles_file) except Exception as exc: self.plugin.warn( "Failed to read Firefox's profiles file \"{}\". Error: {}". format(profiles_file, exc)) return [] places_files = [] for profile in profiles_db.profiles: places_file = os.path.join(profile.path, "places.sqlite") if os.path.isfile(places_file): places_files.append(places_file) else: self.plugin.warn("places.sqlite file not found in", profile.path) bookmarks = [] for f in places_files: try: bookmarks += self._read_places_file(os.path.normpath(f)) except Exception as exc: self.plugin.warn( "Failed to read Firefox's bookmarks file \"{}\". Error: {}" .format(f, exc)) continue return bookmarks
def _detect_distro_official(self, given_enabled, given_label, given_path): dist_props = { 'enabled': given_enabled, 'label': given_label, 'exe_file': None, 'cmd_args': ['%1'], 'sessions': []} # label if not dist_props['label']: dist_props['label'] = "WinSCP" # enabled? don't go further if not if dist_props['enabled'] is None: dist_props['enabled'] = True if not dist_props['enabled']: return dist_props # find executable exe_file = None if given_path: exe_file = os.path.normpath(os.path.join(given_path, self.EXE_NAME_OFFICIAL)) if not os.path.exists(exe_file): exe_file = None if not exe_file: exe_file = self._autodetect_official_installreg() if not exe_file: exe_file = self._autodetect_startmenu(self.EXE_NAME_OFFICIAL, "WinSCP.lnk") if not exe_file: exe_file = self._autodetect_official_progfiles() if not exe_file: exe_file = self._autodetect_path(self.EXE_NAME_OFFICIAL) #if not exe_file: # exe_file = self._autodetect_startmenu(self.EXE_NAME_OFFICIAL, "*winscp*.lnk") if not exe_file: return None dist_props['exe_file'] = exe_file # List configured sessions # To do that, we first have to detect if WinSCP is in Installed or # Portable mode. The steps are described by the official documentation # at: https://winscp.net/eng/docs/config#auto expected_ini_locations = ( # along with the exe file os.path.join(os.path.dirname(exe_file), self.INI_NAME_OFFICIAL), # in "%USERPROFILE%\AppData\Roaming" folder (DIRECTLY) os.path.join( kpu.shell_known_folder_path( "{3eb685db-65f9-4cf6-a03a-e3ef65729f3d}"), self.INI_NAME_OFFICIAL)) ini_file = None for ini_location in expected_ini_locations: if os.path.isfile(ini_location): ini_file = ini_location break if ini_file is not None: # While the ini file format seems to be standard (i.e.: parseable # with the configparser Python module), we choose the lazy way for # performance reasons and because we only need the name of the # configured sessions. ini_section_prefix = "[sessions\\" try: ini_content = kpu.chardet_slurp(ini_file) except Exception: self.err("Failed to read file:", ini_file) return None for ini_line in iter(ini_content.splitlines()): if ini_line.lower().startswith(ini_section_prefix) and ini_line.endswith(']'): dist_props['sessions'].append( urllib.parse.unquote( ini_line[len(ini_section_prefix):-1])) ini_content = None else: try: hkey = winreg.OpenKey( winreg.HKEY_CURRENT_USER, 'Software\\Martin Prikryl\\WinSCP 2\\Sessions', access=winreg.KEY_READ | winreg.KEY_ENUMERATE_SUB_KEYS) index = 0 while True: try: session_name = winreg.EnumKey(hkey, index) index += 1 if '/' in session_name: continue # ignore workspaces dist_props['sessions'].append( urllib.parse.unquote( session_name, encoding='utf-8')) except OSError: break winreg.CloseKey(hkey) except OSError: pass return dist_props
class PluginSettings: """Wrapper for the plugin configuration file.""" INSTANCE_PREFIX = "terminal/" DEFAULT_ITEM_PREFIX = "Windows Terminal (%s): " LOCALAPPDATA = kpu.shell_known_folder_path( "{f1b32785-6fba-4fcf-9d55-7b8e7f157091}") WINDOWSAPPS = LOCALAPPDATA + "\\Microsoft\\WindowsApps" PACKAGED_SETTINGS = LOCALAPPDATA + "\\Packages\\%s\\LocalState\\settings.json" PACKAGED_EXECUTABLE = WINDOWSAPPS + "\\%s\\wt.exe" MISSING_KEY_ERROR = """ ⚠ Config section [%s] defines a custom installation, but the value for '%s' is missing. """ def __init__(self, plugin): self._settings = plugin.load_settings() self._logger = plugin def use_profile_icons(self): """True if we should show try to load per-profile icons.""" return self._settings.get_bool(key="use_profile_icons", section="items", fallback=True) def terminal_instances(self): """Return the list of terminal instances in the configuration file.""" for section_name in self._instancesections(): instance_name = section_name[len(self.INSTANCE_PREFIX):] # Skip an instance if it defines 'enabled = false' if not self._settings.get_bool( key="enabled", section=section_name, fallback=True): continue prefix = self._get(section_name, "prefix", "Windows Terminal (%s)" % (instance_name)) app_package = self._get(section_name, "app_package") if app_package and not self._package_exists(app_package): self._logger.info("Skipping '%s', package %s does not exist" % (instance_name, app_package)) continue # For packaged instances, paths are derived from the package id... packaged_settings_file = self.PACKAGED_SETTINGS % ( app_package) if app_package else None packaged_executable = self.PACKAGED_EXECUTABLE % ( app_package) if app_package else None # ...but you can still override them settings_file = self._get(section_name, "settings_file", packaged_settings_file) executable = self._get(section_name, "executable", packaged_executable) # For custom instances, settings_file and executable are required if not app_package: if not settings_file: self._logger.warn(self.MISSING_KEY_ERROR % (section_name, "settings_file")) continue if not executable: self._logger.warn(self.MISSING_KEY_ERROR % (section_name, "executable")) continue self._logger.info("Adding profiles for '%s' (%s)" % (instance_name, app_package or "custom")) try: wrapper = WindowsTerminalWrapper(settings_file, executable) yield (instance_name, { "name": instance_name, "prefix": prefix, "wrapper": wrapper }) except ValueError: message = sys.exc_info()[1] self._logger.warn(message) def _instancesections(self): return [ s for s in self._settings.sections() \ if s.lower().startswith(self.INSTANCE_PREFIX) ] def _get(self, section, key, fallback=None): return self._settings.get(key=key, section=section, fallback=fallback, unquote=True) def _package_exists(self, app_package): return os.path.exists(self.WINDOWSAPPS + "\\" + app_package)
def defaultdir(): localappdata = kpu.shell_known_folder_path("{f1b32785-6fba-4fcf-9d55-7b8e7f157091}") return localappdata + "\\Fork\\"
def on_catalog(self): start = time.perf_counter() self.pathext_str, self.pathext = self._read_env_pathext() catalog = [] for user_extra_path in self.extra_paths: user_extra_path = user_extra_path.replace("/", os.sep) has_trailing_sep = user_extra_path.endswith(os.sep) if user_extra_path.startswith('::') and len(user_extra_path) >= 38: (guid, tail) = (user_extra_path[2:].split(os.sep, maxsplit=1) + [None] * 2)[:2] try: kf_path = kpu.shell_known_folder_path(guid) if tail is not None: user_extra_path = os.path.normpath( os.path.join(kf_path, tail)) else: user_extra_path = kf_path except OSError: self.warn( "Failed to get path of known folder from setting \"{}\"" .format(user_extra_path)) continue else: user_extra_path = os.path.normpath(user_extra_path) #user_extra_path = os.path.expandvars(user_extra_path) # not needed if has_trailing_sep: user_extra_path += os.sep recursive_glob = "**" in user_extra_path for globbed_path in glob.iglob(user_extra_path, recursive=recursive_glob): if self.should_terminate(): return files = [] if os.path.isdir(globbed_path): try: files = kpu.scan_directory(globbed_path, self.pathext, kpu.ScanFlags.FILES, max_level=0) except IOError as exc: self.warn(exc) continue files = [os.path.join(globbed_path, f) for f in files] elif os.path.isfile(globbed_path): files = [globbed_path] else: continue # duh?! if self.should_terminate(): return for f in files: catalog.append( self.create_item(category=kp.ItemCategory.FILE, label=os.path.basename(f), short_desc=f, target=f, args_hint=kp.ItemArgsHint.ACCEPTED, hit_hint=kp.ItemHitHint.KEEPALL)) if len(catalog) % 100 == 0 and self.should_terminate(): return self.set_catalog(catalog) if len(self.extra_paths): self._log_catalog_duration(start, len(catalog))