Пример #1
0
    def display_field(self, account, field):
        # get string to display
        value = account.get_value(field)
        value = dedent(str(value)).strip()

        log('writing secret to keyboard writer.')
        self._autotype(value)
Пример #2
0
    def recognize(cls, data, verbose):
        # try the specified recognizers
        discovery = getattr(cls, 'discovery', ())
        for recognizer in Collection(discovery):
            if isinstance(recognizer, Recognizer):
                script = recognizer.match(data, cls, verbose)
                name = getattr(recognizer, 'name', None)
                if script:
                    yield name, script
        if discovery:
            return

        # If no recognizers specified, just check the urls
        for url in Collection(cls.get_composite('urls', default=[])):
            url = str(url)  # this is needed because url may be Script
            url = url if '//' in url else ('//' + url)
            url_components = urlparse(url)
            if url_components.scheme:
                protocol = url_components.scheme
            else:
                protocol = get_setting('default_protocol')
            host = url_components.netloc
            if host == data.get('host'):
                if (protocol == data.get('protocol')):
                    if verbose:
                        log('    %s: matches.' % cls.get_name())
                    yield None, True
                    return
                else:
                    msg = 'URL matches, but uses wrong protocol.'
                    notify(msg)
                    raise PasswordError(msg, culprit=cls.get_name())
Пример #3
0
 def __str__(self):
     "Returns value as string."
     secret = str(self.value)
     if hasattr(self.value, 'entropy'):
         entropy = round(self.value.entropy)
         log('Entropy of {} = {} bits.'.format(self.name, entropy))
     return secret
Пример #4
0
 def signal_start(self):
     url = self.START_URL.format(url=self.url, uuid=self.uuid)
     log(f'signaling start of backups to {self.NAME}: {url}.')
     try:
         requests.get(url)
     except requests.exceptions.RequestException as e:
         raise Error(f'{self.NAME} connection error.', codicil=full_stop(e))
Пример #5
0
    def run(self, url, name=None):
        name = name if name else self.name
        if url:
            if '://' not in url:
                url = 'https://' + url
            try:
                cmd = self.cmd if self.cmd else get_setting('browsers')[name]
            except KeyError:
                raise PasswordError("unknown browser, choose from %s." %
                                    (', '.join(get_setting('browsers', name))),
                                    culprit=name)

            try:
                cmd = cmd.format(url=url)
            except TypeError:
                pass
            except KeyError as e:
                raise PasswordError('unknown key {%s}.  Choose from: {url}.' %
                                    e.args[0],
                                    culprit=setting_path('browsers', name))

            browser = Cmd(cmd, 'sOE')
            # capture both stdout and stderr because browsers are
            # notoriously noisy
            log("running '%s'" % str(browser))
            browser.start()
        else:
            raise PasswordError('URL not available from account.')
Пример #6
0
    def run(self):
        global ActiveFile
        ActiveFile = self.path
        path = self.path
        self.encrypted = path.suffix in ['.gpg', '.asc']
        log('reading.', culprit=path)
        try:
            self.code = self.read()
                # need to save the code for the new command
        except OSError as err:
            raise Error(os_error(err))

        try:
            compiled = compile(self.code, str(path), 'exec')
        except SyntaxError as err:
            raise Error(
                err.msg + ':', err.text, (err.offset-1)*' ' + '^',
                culprit=(err.filename, err.lineno), sep='\n'
            )
            # File "/home/ken/.config/avendesora/config", line 18
            #   'g': 'google-chrome %s'
            #      ^

        contents = {}
        exec(compiled, contents)
        ActiveFile = None
        return contents
Пример #7
0
    def display_field(self, account, field):

        # get string to display
        value, is_secret, name, desc, = tuple(account.get_value(field))
        value = dedent(str(value)).strip()

        # Use 'xsel' to put the information on the clipboard.
        # This represents a vulnerability, if someone were to replace xsel they
        # could steal my passwords. This is why I use an absolute path. I tried
        # to access the clipboard directly using GTK but I cannot get the code
        # to work.
        base_cmd = split_cmd(get_setting('xsel_executable'))

        log('Writing to clipboard.')
        try:
            Run(base_cmd + ['-i'], 'soEW', stdin=value)
        except Error as e:
            e.reraise(culprit=base_cmd)

        if is_secret:
            # produce count down
            try:
                for t in range(get_setting('display_time'), -1, -1):
                    cursor.write(str(t))
                    sleep(1)
                    cursor.clear()
            except KeyboardInterrupt:
                cursor.clear()

            # clear the clipboard
            try:
                Run(base_cmd + ['-c'], 'soEw')
            except Error as e:
                e.reraise(culprit=base_cmd)
Пример #8
0
    def wait(self):
        """
        Wait for command to terminate.

        This should only be used if wait-for-termination is False.

        Returns exit status of the command.
        """
        process = self.process

        stdin = self.stdin if self.stdin else ''
        stdout, stderr = process.communicate(stdin.encode(self.encoding))
        self.stdout = None if stdout is None else stdout.decode(self.encoding)
        self.stderr = None if stderr is None else stderr.decode(self.encoding)
        self.status = process.returncode

        if _use_log(self.log):
            inform.log('exit status:', self.status)

        # check return code
        if self.accept.unacceptable(self.status):
            if self.stderr:
                msg = self.stderr.strip()
            else:
                msg = 'unexpected exit status (%d)' % self.status
            if PREFERENCES['use_inform']:
                raise inform.Error(
                    msg=msg,
                    status=self.status,
                    stdout=self.stdout.rstrip() if self.stdout else None,
                    stderr=self.stderr.rstrip() if self.stderr else None,
                    cmd=render_command(self.cmd),
                    template='{msg}')
            raise OSError(None, msg)
        return self.status
Пример #9
0
 def signal_start(self):
     url = f'{self.url}/{self.uuid}/start'
     log(f'signaling start of backups to {self.NAME}: {url}.')
     try:
         requests.post(url)
     except requests.exceptions.RequestException as e:
         raise Error('{self.NAME} connection error.', codicil=full_stop(e))
Пример #10
0
    def generate(self, field_name, field_key, account):
        try:
            if self.secret:
                return
        except AttributeError:
            pass
        account_name = account.get_name()
        account_seed = account.get_seed()
        if self.master is None:
            master = account.get_field("master", default=None)
            master_source = account.get_field("_master_source", default=None)
        else:
            master = self.master
            master_source = "secret"
        if not master:
            master = get_setting("user_key")
            master_source = "user_key"
        if not master:
            try:
                try:
                    master = getpass.getpass("master password for %s: " % account_name)
                    master_source = "user"
                except EOFError:
                    output()
                if not master:
                    warn("master password is empty.")
            except (EOFError, KeyboardInterrupt):
                terminate()
        log("Generating secret, source of master seed:", master_source)
        field_key = self.get_key(field_key)
        if self.version:
            version = self.version
        else:
            version = account.get_field("version", default="")

        if account.request_seed():
            try:
                try:
                    interactive_seed = getpass.getpass("seed for %s: " % account_name)
                except EOFError:
                    output()
                if not interactive_seed:
                    warn("seed is empty.")
            except (EOFError, KeyboardInterrupt):
                terminate()
        else:
            interactive_seed = ""

        seeds = [master, account_seed, field_name, field_key, version, interactive_seed]
        key = " ".join([str(seed) for seed in seeds])

        # Convert the key into 512 bit number
        digest = hashlib.sha512((key).encode("utf-8")).digest()
        bits_per_byte = 8
        radix = 1 << bits_per_byte
        bits = 0
        for byte in digest:
            bits = radix * bits + byte
        self.pool = bits
Пример #11
0
    def start(self, stdin=None):
        """
        Start the command, will not wait for it to terminate.

        If stdin is given, it should be a string. Otherwise, no connection is
        made to stdin of the command.
        """
        self.stdin = None
        import subprocess

        if is_str(self.cmd):
            cmd = self.cmd if self.use_shell else split_cmd(self.cmd)
        else:
            cmd = self.cmd
        if _use_log(self.log):
            from inform import log

            log("running:", render_command(cmd, option_args=self.option_args))

        if self.save_stdout or self.save_stderr:
            try:
                DEVNULL = subprocess.DEVNULL
            except AttributeError:
                DEVNULL = open(os.devnull, "wb")
        assert self.merge_stderr_into_stdout is False, "M not supported, use E"

        streams = {}
        if stdin is not None:
            streams["stdin"] = subprocess.PIPE
        if self.save_stdout:
            streams["stdout"] = DEVNULL
        if self.save_stderr:
            streams["stderr"] = DEVNULL

        # run the command
        try:
            process = subprocess.Popen(cmd,
                                       shell=self.use_shell,
                                       env=self.env,
                                       **streams)
        except OSError as e:
            if PREFERENCES["use_inform"]:
                from inform import Error, os_error

                raise Error(msg=os_error(e),
                            cmd=render_command(self.cmd),
                            template="{msg}")
            else:
                raise
        self.running = True

        # store needed information and wait for termination if desired
        self.pid = process.pid
        self.process = process

        # write to stdin
        if stdin is not None:
            process.stdin.write(stdin.encode(self.encoding))
            process.stdin.close()
Пример #12
0
    def display_field(self, account, field):
        # get string to display
        value = account.get_value(field)
        value = dedent(str(value)).strip()

        # don't use inform otherwise password shows up in logfile
        print(value)
        log('writing secret to standard output.')
Пример #13
0
    def run(self, stdin=None):
        """
        Run the command, will wait for it to terminate.

        If stdin is given, it should be a string. Otherwise, no connection is
        made to stdin of the command.

        Returns exit status if wait_for_termination is True.
        If wait_for_termination is False, you must call wait(), otherwise stdin
        is not be applied.  If you don't want to wait, call start() instead.
        """
        self.stdin = stdin
        import subprocess

        if is_str(self.cmd):
            cmd = self.cmd if self.use_shell else split_cmd(self.cmd)
        else:
            # cannot use to_str() because it can change some arguments when not intended.
            # this is particularly problematic the duplicity arguments in embalm
            cmd = [str(c) for c in self.cmd]
        if _use_log(self.log):
            from inform import log

            log("running:", render_command(cmd, option_args=self.option_args))

        # indicate streams to intercept
        streams = {}
        if stdin is not None:
            streams["stdin"] = subprocess.PIPE
        if self.save_stdout:
            streams["stdout"] = subprocess.PIPE
        if self.save_stderr:
            streams["stderr"] = subprocess.PIPE
        if self.merge_stderr_into_stdout:
            streams["stderr"] = subprocess.STDOUT

        # run the command
        try:
            process = subprocess.Popen(cmd,
                                       shell=self.use_shell,
                                       env=self.env,
                                       **streams)
        except OSError as e:
            if PREFERENCES["use_inform"]:
                from inform import Error, os_error

                raise Error(msg=os_error(e),
                            cmd=render_command(self.cmd),
                            template="{msg}")
            else:
                raise
        self.running = True

        # store needed information and wait for termination if desired
        self.pid = process.pid
        self.process = process
        if self.wait_for_termination:
            return self.wait()
Пример #14
0
 def initialize(cls, interactive_seed=False, stealth_name=None):
     cls._interactive_seed_ = interactive_seed
     cls._stealth_name = stealth_name
     log('Initializing stealth account:', cls.get_name())
     for key, value in cls.items():
         # reset the secrets so they honor stealth_name
         try:
             value.reset()
         except AttributeError:
             pass
Пример #15
0
 def match(self, given, account, verbose=False):
     try:
         if self.name in os.environ and self.value == os.environ[self.name]:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as e:
         raise PasswordError(str(e), culprit=e.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #16
0
 def match(self, data, account, verbose=False):
     try:
         if name in os.environ and value == os.environ[name]:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #17
0
 def initialize(cls, interactive_seed=False):
     cls._interactive_seed = interactive_seed
     log("initializing", cls.get_name())
     try:
         if cls.master.is_secure():
             if not cls._file_info.encrypted:
                 warn(
                     "high value master password not contained in encrypted", "account file.", culprit=cls.get_name()
                 )
     except AttributeError as err:
         pass
Пример #18
0
 def match(self, given, account, verbose=False):
     try:
         username = getusername()
         if username in self.users:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as e:
         raise PasswordError(str(e), culprit=e.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #19
0
 def match(self, data, account, verbose=False):
     try:
         username = getusername()
         if username in self.users:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #20
0
 def match(self, data, account, verbose=False):
     try:
         hostname = gethostname()
         for host in self.hosts:
             if host == hostname:
                 if verbose:
                     log('    %s: matches.' % self.get_name())
                 return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #21
0
 def signal_end(self, exception):
     if exception:
         url = self.FAIL_URL.format(url=self.url, uuid=self.uuid)
         result = 'failure'
     else:
         url = self.SUCCESS_URL.format(url=self.url, uuid=self.uuid)
         result = 'success'
     log(f'signaling {result} of backups to {self.NAME}: {url}.')
     try:
         response = requests.get(url)
     except requests.exceptions.RequestException as e:
         raise Error('{self.NAME} connection error.', codicil=full_stop(e))
Пример #22
0
 def match(self, data, account, verbose=False):
     try:
         cwd = cwd()
         for directory in self.dirs:
             if cwd.samefile(to_path(directory)):
                 if verbose:
                     log('    %s: matches.' % self.get_name())
                 return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #23
0
 def match(self, given, account, verbose=False):
     try:
         hostname = gethostname()
         for host in self.hosts:
             if host == hostname:
                 if verbose:
                     log('    %s: matches.' % self.get_name())
                 return self.script
     except Exception as e:
         raise PasswordError(str(e), culprit=e.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #24
0
 def match(self, given, account, verbose=False):
     try:
         cwd = os.cwd()
         for directory in self.dirs:
             if cwd.samefile(to_path(directory)):
                 if verbose:
                     log('    %s: matches.' % self.get_name())
                 return self.script
     except Exception as e:
         raise PasswordError(str(e), culprit=e.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #25
0
    def match(self, given, account, verbose=False):
        try:
            for url in self.urls:
                url = url if '//' in url else ('//' + url)
                url_components = urlparse(url)
                if url_components.scheme:
                    protocol = url_components.scheme
                else:
                    protocol = get_setting('default_protocol')
                host = url_components.netloc
                path = url_components.path

                # given may contain the following fields after successful title
                # recognition:
                #     rawdata: the original title
                #     title: the processed title
                #     url: the full url
                #     browser: the name of the browser
                #     protocol: the url scheme (ex. http, https, ...)
                #     host: the url host name or IP address
                #     path: the path component of the url
                #           does not include options or anchor
                if host == given.get('host'):

                    def path_matches(expected, actual):
                        if not expected:
                            # path was not specified, treat it as don't care
                            return True
                        if self.exact_path:
                            # exact path match expected
                            return expected == actual
                        else:
                            # otherwise just match what was given
                            return actual.startswith(expected)

                    if path_matches(path, given.get('path')):
                        if self.fragment and given.get(
                                'fragment') != self.fragment:
                            continue
                        if (protocol == given.get('protocol')):
                            if verbose:
                                log('    %s: matches.' % self.get_name())
                            return self.script
                        else:
                            msg = 'url matches, but uses wrong protocol.'
                            notify(msg)
                            raise PasswordError(msg,
                                                culprit=account.get_name())
        except Exception as e:
            raise PasswordError(str(e), culprit=e.__class__.__name__)
        if verbose:
            log('    %s: no match.' % self.get_name())
Пример #26
0
 def match(self, data, account, verbose=False):
     try:
         actual = data.get('rawtitle')
         if actual:
             for candidate in self.titles:
                 if fnmatch(actual, candidate):
                     if verbose:
                         log('    %s: matches.' % self.get_name())
                     return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #27
0
 def match(self, data, account, verbose=False):
     try:
         match = Any([
             each.match(data, account, verbose) for each in self.recognizers
         ])
         if match:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as err:
         raise Error(str(err), culprit=err.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #28
0
def test_grove():
    with messenger() as (msg, stdout, stderr, logfile):
        stimulus = 'hey now!'
        log(stimulus)
        assert msg.errors_accrued() == 0
        assert errors_accrued() == 0
        assert strip(stdout) == ''
        assert strip(stderr) == ''
        assert log_strip(logfile) == dedent('''
            ack: invoked as: <exe>
            ack: invoked on: <date>
            {expected}
        ''').strip().format(expected=stimulus)
Пример #29
0
 def _process(cls, title, data):
     match = cls.PATTERN.match(title)
     if match:
         found = match.groupdict()
         if 'url' in found:
             components = urlparse(found.get('url'))
             if components.netloc:
                 log('title matched.', culprit=cls.__name__)
                 data.update(found)
                 data['protocol'] = components.scheme
                 data['host'] = components.netloc
                 data['path'] = components.path
                 return True
Пример #30
0
    def __init__(self, override=None):
        if override:
            title = override
        else:
            xdotool = get_setting('xdotool_executable')
            if not xdotool:
                raise Error(
                    "must set xdotool_executable'.",
                    culprit=get_setting('config_file')
                )
            try:
                output = Run(
                    [xdotool, 'getactivewindow', 'getwindowname'],
                    'sOeW'
                )
            except OSError as err:
                raise Error(str(err))
            title = output.stdout.strip()
        log('Focused window title: %s' % title)
        data = {'rawtitle': title}
        for sub in sorted(Title.__subclasses__(), key=lambda c: c.PRIORITY):
            matched = sub._process(title, data)
            log('%s: %s.' % (sub.__name__, 'matched' if matched else 'no match'))
            if matched:
                break

        # log the components of the title
        log('Recognized title components ...')
        for k, v in data.items():
            log('    %s: %s' % (k, v))

        self.data = data
Пример #31
0
    def __init__(self, url=None, override=None):
        if url:
            self.data = self._process_url(url)
            return
        if override:
            title = override
        else:
            xdotool = get_setting('xdotool_executable')
            if not xdotool:
                raise PasswordError("must set xdotool_executable'.",
                                    culprit=setting_path('xdotool_executable'))
            cmd = [xdotool, 'getactivewindow', 'getwindowname']
            try:
                output = Run(cmd, 'sOEW')
            except Error as e:
                e.reraise(culprit=xdotool)
            title = output.stdout.strip()
        log('Focused window title:\n    %s' % title)
        data = {'rawtitle': title}
        for sub in sorted(Title.__subclasses__(), key=lambda c: c.PRIORITY):
            matched = sub._process(title, data)
            log('%s: %s.' %
                (sub.__name__, 'matched' if matched else 'no match'))
            if matched:
                break

        # log the components of the title
        log('Recognized title components;')
        for k, v in data.items():
            log('    %s: %s' % (k, v))

        self.data = data
Пример #32
0
    def display_field(self, account, field):
        # get string to display
        value, is_secret, name, desc = tuple(account.get_value(field))
        label = '%s (%s)' % (name, desc) if desc else name
        value = dedent(str(value)).strip()
        label_color = get_setting('_label_color')

        # indent multiline outputs
        sep = ' '
        if '\n' in value:
            if is_secret:
                warn('secret contains newlines, will not be fully concealed.')
            value = indent(value, get_setting('indent')).strip('\n')
            sep = '\n'

        if label:
            if label[0] == '_':
                # hidden field
                label = '!' + label[1:]
            text = label_color(label.replace('_', ' ') + ':') + sep + value
        else:
            text = value
            label = field
        log('Writing to TTY:', label)

        if is_secret:
            if Color.isTTY():
                # Write only if output is a TTY. This is a security feature.
                # The ideas is that when the TTY writer is called it is because
                # the user is expecting the output to go to the tty. This
                # eliminates the chance that the output can be intercepted and
                # recorded by replacing Avendesora with an alias or shell
                # script. If the user really want the output to go to something
                # other than the TTY, the user should use the --stdout option.
                try:
                    cursor.write(text)
                    cursor.conceal()
                    sleep(get_setting('display_time'))
                except KeyboardInterrupt:
                    pass
                cursor.reveal()
                cursor.clear()
            else:
                error('output is not a TTY.')
                codicil(
                    'Use --stdout option if you want to send secret',
                    'to a file or a pipe.'
                )
        else:
            output(text)
Пример #33
0
 def match(self, given, account, verbose=False):
     try:
         match = any([
             each.match(given, account, verbose)
             for each in self.recognizers
         ])
         if match:
             if verbose:
                 log('    %s: matches.' % self.get_name())
             return self.script
     except Exception as e:
         raise PasswordError(str(e), culprit=e.__class__.__name__)
     if verbose:
         log('    %s: no match.' % self.get_name())
Пример #34
0
    def run_script(self, script, dryrun=False):
        out = []
        scrubbed = []
        sleep(INITIAL_AUTOTYPE_DELAY)
        ms_per_char = None
        for cmd, val in script.components(True):
            if cmd in ['tab', 'return', 'text', 'value']:
                out.append(val)
                scrubbed.append(val)
            elif cmd.startswith('sleep '):
                scrubbed.append('<%s>' % cmd)
                try:
                    kw, seconds = cmd.split()
                    assert kw == 'sleep'
                    if out:
                        # drain the buffer before sleeping
                        if not dryrun:
                            self._autotype(''.join(out), ms_per_char)
                        out = []
                    sleep(float(seconds))
                except (TypeError, ValueError):
                    raise PasswordError('syntax error in keyboard script.',
                                        culprit=cmd)
            elif cmd.startswith('rate '):
                scrubbed.append('<%s>' % cmd)
                try:
                    kw, rate = cmd.split()
                    assert kw == 'rate'
                    if out:
                        # drain the buffer before changing rate
                        if not dryrun:
                            self._autotype(''.join(out), ms_per_char)
                        out = []
                    ms_per_char = int(rate)
                except (TypeError, ValueError):
                    raise PasswordError('syntax error in keyboard script.',
                                        culprit=cmd)
            elif cmd.startswith('remind '):
                notify(cmd[7:])
            else:
                out.append(val)
                scrubbed.append('<%s>' % cmd)

        scrubbed = ''.join(scrubbed).replace('\t', '→').replace('\n', '↲')
        log('Autotyping "%s"%s.' % (scrubbed, ' -- dry run' if dryrun else ''))
        if dryrun:
            output(script.account.get_name(), scrubbed, sep=': ')
        else:
            self._autotype(''.join(out), ms_per_char)
Пример #35
0
def check_root(root, working_dir):
    if str(root) == '.':
        log("Unable to determine whether paths are contained in a root,",
            "because '.' is a root.")
    abs_root = working_dir / root
    if not abs_root.exists():
        raise Error("not found.", culprit=root)
    # the following is a bit cleaner, but no available until python 3.9
    #if not abs_root.is_relative_to(working_dir):
    #    raise Error("not in working directory:", working_dir, culprit=root)
    try:
        abs_root.relative_to(working_dir)
    except ValueError:
        raise Error("not in working directory:", working_dir, culprit=root)
    return to_path(root).is_absolute()
Пример #36
0
    def start(self, stdin=None):
        """
        Start the command, will not wait for it to terminate.

        If stdin is given, it should be a string. Otherwise, no connection is
        made to stdin of the command.

        """
        self.stdin = None
        import subprocess

        if is_str(self.cmd):
            cmd = self.cmd if self.use_shell else split_cmd(self.cmd)
        else:
            cmd = self.cmd
        if _use_log(self.log):
            from inform import log
            log('running:', render_command(cmd, option_args=self.option_args))

        if self.save_stdout or self.save_stderr:
            try:
                DEVNULL = subprocess.DEVNULL
            except AttributeError:
                DEVNULL = open(os.devnull, 'wb')
        assert self.merge_stderr_into_stdout is False, 'M not supported, use E'

        streams = {}
        if stdin is not None:
            streams['stdin'] = subprocess.PIPE
        if self.save_stdout:
            streams['stdout'] = DEVNULL
        if self.save_stderr:
            streams['stderr'] = DEVNULL

        # run the command
        process = subprocess.Popen(cmd,
                                   shell=self.use_shell,
                                   env=self.env,
                                   **streams)

        # store needed information and wait for termination if desired
        self.pid = process.pid
        self.process = process

        # write to stdin
        if stdin is not None:
            process.stdin.write(stdin.encode(self.encoding))
            process.stdin.close()
Пример #37
0
    def match(self, data, account, verbose=False):
        try:
            for url in self.urls:
                url = urlparse(url)
                protocol = url.scheme
                host = url.netloc
                path = url.path

                # data may contain the following fields after successful title
                # recognition:
                #     rawdata: the original title
                #     title: the processed title
                #     url: the full url
                #     browser: the name of the browser
                #     protocol: the url scheme (ex. http, https, ...)
                #     host: the url host name or IP address
                #     path: the path component of the url
                #           does not include options or anchor
                if host == data.get('host'):

                    def path_matches(expected, actual):
                        if not expected:
                            # path was not specified, treat it as don't care
                            return True
                        if self.exact_path:
                            # exact path match expected
                            return expected == actual
                        else:
                            # otherwise just match what was given
                            return actual.startswith(expected)

                    if path_matches(path, data.get('path')):
                        if (
                            protocol == data.get('protocol') or
                            protocol not in REQUIRED_PROTOCOLS
                        ):
                            if verbose:
                                log('    %s: matches.' % self.get_name())
                            return self.script
                        else:
                            msg = 'url matches, but uses wrong protocol.'
                            notify(msg)
                            raise Error(msg, culprit=account.get_name())
        except Exception as err:
            raise Error(str(err), culprit=err.__class__.__name__)
        if verbose:
            log('    %s: no match.' % self.get_name())
Пример #38
0
    def open_and_search(cls, filepath, account = None):
        args = {
            'filepath': filepath,
            'account': account,
            'section': '{' '{' '{' '1',
        }
        try:
            editor_setting = 'edit_account' if account else 'edit_template'
            cmd = get_setting(editor_setting)
            cmd = [e.format(**args) for e in cmd]

            log("running '%s'." % ' '.join(cmd))
            Run(cmd, 'soeW')
        except OSError as err:
            raise Error(os_error(err))
        except KeyError as err:
            raise Error('invalid field: %s.' % err, culprit=editor_setting)
Пример #39
0
    def match(self, given, account, verbose=False):
        try:
            actual = given.get('rawtitle')
            if actual:
                for candidate in self.titles:
                    if isinstance(candidate, str):
                        hit = fnmatch(actual, candidate)
                    else:
                        hit = candidate(actual)

                    if hit:
                        if verbose:
                            log('    %s: matches.' % self.get_name())
                        return self.script
        except Exception as e:
            raise PasswordError(str(e), culprit=e.__class__.__name__)
        if verbose:
            log('    %s: no match.' % self.get_name())
Пример #40
0
    def signal_end(self, exception):
        if exception:
            result = 'failure'
            status = exception.status
            payload = exception.stderr
        else:
            result = 'success'
            status = 0
            payload = ''

        url = f'{self.url}/{self.uuid}/{status}'
        log(f'signaling {result} of backups to {self.NAME}: {url}.')
        try:
            if payload:
                response = requests.post(url, data=payload.encode('utf-8'))
            else:
                response = requests.post(url)
        except requests.exceptions.RequestException as e:
            raise Error('{self.NAME} connection error.', codicil=full_stop(e))
Пример #41
0
    def wait(self):
        """
        Wait for command to terminate.

        This should only be used if wait-for-termination is False.

        Returns exit status of the command.
        """
        process = self.process

        stdin = self.stdin if self.stdin else ""
        stdout, stderr = process.communicate(stdin.encode(self.encoding))
        self.stdout = None if stdout is None else stdout.decode(self.encoding)
        self.stderr = None if stderr is None else stderr.decode(self.encoding)
        self.status = process.returncode
        self.running = False

        if _use_log(self.log):
            from inform import log

            log("exit status:", self.status)

        # check return code
        if self.accept.unacceptable(self.status):
            if self.stderr:
                msg = self.stderr.strip()
            else:
                msg = "unexpected exit status (%d)." % self.status
            if PREFERENCES["use_inform"]:
                from inform import Error

                raise Error(
                    msg=msg,
                    status=self.status,
                    stdout=self.stdout.rstrip() if self.stdout else None,
                    stderr=self.stderr.rstrip() if self.stderr else None,
                    cmd=render_command(self.cmd),
                    template="{msg}",
                )
            else:
                raise OSError(None, msg)
        return self.status
Пример #42
0
    def display_field(self, account, field):
        name, key = account.split_name(field)
        is_secret = account.is_secret(name, key)
        try:
            value = account.get_field(name, key)
            tvalue = dedent(str(value)).strip()
        except Error as err:
            err.terminate()
        sep = ' '
        if '\n' in tvalue:
            if is_secret:
                warn(
                    'secret contains newlines, will not be fully concealed.',
                    culprit=key
                )
            else:
                tvalue = indent(dedent(tvalue), get_setting('indent')).strip('\n')
                sep = '\n'

        # build output string
        label = account.combine_name(name, key)
        log('Writing to TTY:', label)
        try:
            alt_name = value.get_key()
            if alt_name:
                label += ' (%s)' % alt_name
        except AttributeError:
            pass
        text = LabelColor(label + ':') + sep + tvalue

        if is_secret:
            try:
                cursor.write(text)
                cursor.conceal()
                sleep(get_setting('display_time'))
            except KeyboardInterrupt:
                pass
            cursor.reveal()
            cursor.clear()
        else:
            output(text)
Пример #43
0
 def run(self, url, name=None):
     name = name if name else self.name
     if url:
         if '://' not in url:
             url = 'https://' + url
         try:
             cmd = self.cmd if self.cmd else get_setting('browsers')[name]
             try:
                 cmd = cmd.format(url=url)
             except TypeError:
                 pass
             log("running '%s'" % cmd)
             Run(cmd, 'sOew')
         except KeyError:
             error('unknown browser, choose from %s.' % (
                 name, ', '.join(get_setting('browsers'))
             ))
         except OSError as err:
             error(os_error(err))
     else:
         error('url not available.')
Пример #44
0
    def run_borg_raw(self, args):

        # prepare the command
        os.environ.update(self.publish_passcode())
        os.environ["BORG_DISPLAY_PASSPHRASE"] = "no"
        executable = self.value("borg_executable", BORG)
        remote_path = self.value("remote_path")
        remote_path = ["--remote-path", remote_path] if remote_path else []
        repository = str(self.repository)
        command = ([executable] + remote_path +
                   [a.replace('@repo', repository) for a in args])

        # run the command
        narrate("running:\n{}".format(
            indent(render_command(command, borg_options_arg_count))))
        with cd(self.working_dir):
            narrate("running in:", cwd())
            starts_at = arrow.now()
            log("starts at: {!s}".format(starts_at))
            borg = Run(command, modes="soeW", env=os.environ, log=False)
            ends_at = arrow.now()
            log("ends at: {!s}".format(ends_at))
            log("elapsed = {!s}".format(ends_at - starts_at))
        if borg.stdout:
            narrate("Borg stdout:")
            narrate(indent(borg.stdout))
        if borg.stderr:
            narrate("Borg stderr:")
            narrate(indent(borg.stderr))
        if borg.status:
            narrate("Borg exit status:", borg.status)

        return borg
Пример #45
0
    def discover_account(self, title=None, verbose=False):
        log('Account Discovery ...')
        if get_setting('verbose'):
             verbose = True

        # get and parse the title
        data = Title(override=title).get_data()

        # sweep through accounts to see if any recognize this title data
        matches = {}
        for account in self.all_accounts:
            name = account.get_name()
            if verbose:
                log('Trying:', name)
            for key, script in account.recognize(data, verbose):
                ident = '%s (%s)' % (name, key) if key else name
                matches[ident] = name, script
                if verbose:
                    log('    %s matches' % ident)

        if not matches:
            msg = 'cannot find appropriate account.'
            notify(msg)
            raise Error(msg)
        if len(matches) > 1:
            choice = show_list_dialog(sorted(matches.keys()))
            return matches[choice]
        else:
            return matches.popitem()[1]
Пример #46
0
    def initialize(cls, interactive_seed=False, stealth_name=None):
        cls._interactive_seed_ = interactive_seed
        log('Initializing account:', cls.get_name())
        try:
            if cls.master_seed.is_secure():
                if not cls._file_info_.encrypted:
                    warn('high value master seed not contained in encrypted',
                         'account file.',
                         culprit=cls.get_name())
        except AttributeError:
            pass
        for bad, good in get_setting('commonly_mistaken_attributes').items():
            if hasattr(cls, bad):
                warn('{} attribute found,'.format(bad),
                     'should probably be {}.'.format(good),
                     culprit=cls.get_name())

        # do some more error checking
        if isinstance(getattr(cls, 'master_seed', None), GeneratedSecret):
            raise PasswordError(
                'master must not be a subclass of GeneratedSecret.',
                culprit=cls.get_name())
Пример #47
0
    def display_field(self, account, field):
        name, key = account.split_name(field)
        is_secret = account.is_secret(name, key)
        try:
            value = dedent(str(account.get_field(name, key))).strip()
        except Error as err:
            err.terminate()

        # Use 'xsel' to put the information on the clipboard.
        # This represents a vulnerability, if someone were to replace xsel they
        # could steal my passwords. This is why I use an absolute path. I tried
        # to access the clipboard directly using GTK but I cannot get the code
        # to work.

        log('Writing to clipboard.')
        try:
            Run(
                get_setting('xsel_executable').split() + ['-i'],
                'soew',
                stdin=value+'\n'
            )
        except Error as err:
            err.terminate()

        if is_secret:
            # produce count down
            try:
                for t in range(get_setting('display_time'), -1, -1):
                    cursor.write(str(t))
                    sleep(1)
                    cursor.clear()
            except KeyboardInterrupt:
                cursor.clear()

            # clear the clipboard
            try:
                Run(get_setting('xsel_executable').split() + ['-c'], 'soew')
            except Error as err:
                err.terminate()
Пример #48
0
    def run_script(self, account, script):

        def run_xdotool(args):
            try:
                #[get_setting('xdotool_executable'), 'getactivewindow'] +
                Run(get_setting('xdotool_executable').split() + args, 'soeW')
            except OSError as err:
                fatal(os_error(err))

        def autotype(text):
            # Split the text into individual key strokes and convert the special
            # characters to their xkeysym names
            keysyms = []
            for char in text:
                if char in string.ascii_letters + string.digits:
                    keysym = char
                else:
                    keysym = KEYSYMS.get(char)
                if not keysym:
                    error('cannot map to keysym, unknown', culprit=char)
                else:
                    keysyms.append(keysym)
            run_xdotool('key --clearmodifiers'.split() + keysyms)

        # Create the default script if a script was not given
        if script is True:
            # this bit of trickery gets the name of the default field
            name, key = account.split_name(None)
            name = account.combine_name(name, key)
            script = "{%s}{return}" % name

        # Run the script
        regex = re.compile(r'({\w+})')
        out = []
        scrubbed = []
        sleep(INITIAL_AUTOTYPE_DELAY)
        for term in regex.split(script):
            if term and term[0] == '{' and term[-1] == '}':
                # we have found a command
                cmd = term[1:-1].lower()
                if cmd == 'tab':
                    out.append('\t')
                    scrubbed.append('\t')
                elif cmd == 'return':
                    out.append('\n')
                    scrubbed.append('\n')
                elif cmd.startswith('sleep '):
                    cmd = cmd.split()
                    try:
                        assert cmd[0] == 'sleep'
                        assert len(cmd) == 2
                        if out:
                            autotype(''.join(out))
                            out = []
                        sleep(float(cmd[1]))
                        scrubbed.append('<sleep %s>' % cmd[1])
                    except (AssertionError, TypeError):
                        raise
                        fatal('syntax error in keyboard script.', culprit=term)
                else:
                    name, key = account.split_name(cmd)
                    try:
                        value = dedent(str(account.get_field(name, key))).strip()
                        out.append(value)
                        if account.is_secret(name, key):
                            scrubbed.append('<%s>' % cmd)
                        else:
                            scrubbed.append('%s' % value)
                    except Error as err:
                        err.terminate()
            else:
                out.append(term)
        log('Autotyping "%s".' % ''.join(scrubbed).replace('\t', '→').replace('\n', '↲'))
        autotype(''.join(out))
Пример #49
0
    def initialize(self, gpg_ids, filename):
        def split(s, l=72):
            # Break long string into a series of adjacent shorter strings
            if len(s) < l:
                return '"%s"' % s
            chunks = ['    "%s"' % s[i:i+l] for i in range(0, len(s), l)]
            return '\n' + '\n'.join(chunks) + '\n'

        def dict_to_str(d):
            lines = ['{']
            for k in sorted(d):
                lines.append("    '%s': '%s'," % (k, d[k]))
            lines.append('}')
            return '\n'.join(lines)

        # Create dictionary of available substitutions for CONTENTS strings
        fields = {}
        for key in CONFIG_DEFAULTS:
            value = get_setting(key, expand=False)
            value = to_python(str(value) if isinstance(value, Path) else value)
            fields.update({key: value})
        for key in NONCONFIG_SETTINGS:
            value = get_setting(key, expand=False)
            value = to_python(str(value) if isinstance(value, Path) else value)
            fields.update({key: value})
        gpg_ids = gpg_ids if gpg_ids else get_setting('gpg_ids', [])
        fields.update({
            'section': '{''{''{''1',
            'master_password': split(Hidden.conceal(generate_random_string(72))),
            'master_password2': split(Hidden.conceal(generate_random_string(72))),
            'user_key': split(Hidden.conceal(generate_random_string(72))),
            'gpg_ids': repr(' '.join(gpg_ids)),
        })

        # create the initial versions of the files in the settings directory
        if filename is True:
            # Assure that the default initial set of files is present
            for path, contents in [
                (get_setting('config_file'), CONFIG_FILE_INITIAL_CONTENTS),
                (get_setting('hashes_file'), HASH_FILE_INITIAL_CONTENTS),
                (get_setting('user_key_file'), USER_KEY_FILE_INITIAL_CONTENTS),
                (get_setting('default_accounts_file'), ACCOUNTS_FILE_INITIAL_CONTENTS),
                (get_setting('default_stealth_accounts_file'), STEALTH_ACCOUNTS_FILE_INITIAL_CONTENTS),
            ]:
                if path:
                    log('creating initial version.', culprit=path)
                    f = PythonFile(path)
                    f.create(contents.format(**fields), gpg_ids)
        else:
            # Create a new accounts file
            fields['accounts_files'] = get_setting('accounts_files', []) + [filename]
            path = to_path(get_setting('settings_dir'), filename)
            if path.exists():
                raise Error('exists.', culprit=path)
            if path.suffix in GPG_EXTENSIONS and not gpg_ids:
                raise Error('Must specify GPG IDs.')
            log('creating accounts file.', culprit=path)
            f = PythonFile(path)
            f.create(ACCOUNTS_FILE_INITIAL_CONTENTS.format(**fields), gpg_ids)

        # Create a new accounts file
        path = to_path(get_setting('account_list_file'))
        if path.suffix in GPG_EXTENSIONS:
            raise Error('encryption is not supported.', culprit=path)
        try:
            log('writing.', culprit=path)
            path.write_text(ACCOUNT_LIST_FILE_CONTENTS.format(**fields))
        except OSError as err:
            raise Error(os_error(err))