Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
 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
Exemplo n.º 5
0
    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))
Exemplo n.º 6
0
    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")
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
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])
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
    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)
Exemplo n.º 13
0
 def defaultdir():
     localappdata = kpu.shell_known_folder_path("{f1b32785-6fba-4fcf-9d55-7b8e7f157091}")
     return localappdata + "\\Fork\\"
Exemplo n.º 14
0
    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))