Exemplo n.º 1
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())
Exemplo n.º 2
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]
Exemplo n.º 3
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())
Exemplo n.º 4
0
    def render_script(account, field):

        # if field was not given
        if not field:
            name, key = account.split_field(field)
            field = '.'.join(cull([name, key]))

        # treat field as name rather than script if it there are not attributes
        if '{' not in field:
            name, key = account.split_field(field)
            value = account.get_scalar(name, key)
            is_secret = account.is_secret(name, key)
            label = account.combine_field(name, key)
            try:
                alt_name = value.get_key()
                if alt_name:
                    label += ' (%s)' % alt_name
            except AttributeError:
                pass
            return dedent(str(value)).strip(), is_secret, label
        script = field

        # Run the script
        regex = re.compile(r'({[\w. ]+})')
        out = []
        is_secret = False
        for term in regex.split(script):
            if term and term[0] == '{' and term[-1] == '}':
                # we have found a command
                cmd = term[1:-1].lower().strip()
                if cmd == 'tab':
                    out.append('\t')
                elif cmd == 'return':
                    out.append('\n')
                elif cmd.startswith('sleep '):
                    pass
                elif cmd.startswith('rate '):
                    pass
                elif cmd.startswith('remind '):
                    notify(term[8:-1].strip())
                else:
                    if cmd.startswith('paste '):
                        cmd = cmd[6:]
                    name, key = account.split_field(cmd.strip())
                    value = account.get_scalar(name, key)
                    out.append(dedent(str(value)).strip())
                    if account.is_secret(name, key):
                        is_secret = True
            else:
                out.append(term)
        return ''.join(out), is_secret, None
Exemplo n.º 5
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)
Exemplo n.º 6
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())
Exemplo n.º 7
0
    def components(self, ask=False):
        """Iterates through the script.

        Yields a tuple for each component of a script. The tuple consists of the
        type of the component and the value of the component.  The type may be
        'tab' (a tab character), 'return' (a return character), 'text' (raw
        text), 'value' (the value of a field that is not a secret), 'sleep N' (a
        request to sleep N seconds), 'rate N' (set the autotype to 1 keystroke
        every N milliseconds), and finally a field name (the value of a
        field that is secret)..

        Args:
            ask (bool):
                Determines what happens when a composite field is encountered.
                If ask is true a dialog window is opened that that allows the
                user to select the desired member of the collection. If false, a
                PasswordError is raised.

        Raises:
            :exc:`avendesora.PasswordError`: attribute not found.
        """
        account = self.account
        for term in self.SPLITTER.split(self.script):
            if term and term[0] == '{' and term[-1] == '}':
                # we have found a command
                cmd = term[1:-1].lower()

                if cmd == 'tab':
                    val = '\t'
                elif cmd == 'return':
                    val = '\n'
                elif cmd.startswith('sleep '):
                    val = ''
                elif cmd.startswith('rate '):
                    val = ''
                elif cmd.startswith('remind '):
                    notify(cmd[7:])
                    val = ''
                else:
                    if cmd.startswith('paste '):
                        _, field = cmd.split(maxsplit=1)
                    else:
                        field = cmd
                        cmd = None
                    name, key = account.split_field(field)
                    try:
                        value = account.get_scalar(name, key)
                    except PasswordError as e:
                        if ask and e.is_collection and len(e.collection):
                            # is composite value, ask user which one is desired
                            choices = e.collection
                            if len(choices) == 1:
                                choice = choices.keys()[0]
                            else:
                                choice = show_list_dialog(
                                    'Choose from %s' % name,
                                    sorted(choices.keys()))
                                if choice is None:
                                    raise PasswordError(
                                        'user abort.',
                                        culprit=(account.get_name(),
                                                 account.combine_field(
                                                     name, key)))
                            key = choices[choice]
                            value = account.get_scalar(name, key)
                        else:
                            raise
                    try:
                        val = dedent(str(value)).strip()
                    except RecursionError:
                        raise PasswordError(
                            'script must not reference itself.', culprit=field)

                    if not cmd:
                        if account.is_secret(name, key):
                            cmd = 'secret'
                        else:
                            cmd = 'value'
            else:
                cmd = 'text'
                val = term
                if not term:
                    continue
            yield cmd, val
Exemplo n.º 8
0
 def show_list_dialog(options):
     msg = 'selection dialog not available, you must install python3-gobject.'
     notify(msg)
     fatal(msg)
Exemplo n.º 9
0
 def show_error_dialog(message):
     msg = 'error dialog not available, you must install python3-gobject.'
     notify(msg)
     fatal(msg)
Exemplo n.º 10
0
    def run_script(self, script, dryrun=False):
        out = []
        scrubbed = []
        sleep(INITIAL_AUTOTYPE_DELAY)
        ms_per_char = None
        try:
            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)
                    kw, seconds = cmd.split()
                    if out:
                        # drain the buffer before sleeping
                        if not dryrun:
                            self._autotype(''.join(out), ms_per_char)
                        out = []
                    sleep(float(seconds))
                elif cmd.startswith('rate '):
                    scrubbed.append('<%s>' % cmd)
                    kw, rate = cmd.split()
                    if out:
                        # drain the buffer before changing rate
                        if not dryrun:
                            self._autotype(''.join(out), ms_per_char)
                        out = []
                    ms_per_char = int(rate)
                elif cmd.startswith('paste '):
                    # This is an experiment.  It is an attempt to get wellsfargo
                    # to work without forcing me to solve captchas.  The idea is
                    # that if wellsfargo is timing my keystrokes, then I could
                    # try replacing the typing with a paste operation. To make
                    # this work, you have to click on the password field and
                    # then hover over the password field before typing the
                    # autotype hotkey.
                    scrubbed.append('<%s>' % cmd)
                    kw, field = cmd.split()
                    if out:
                        # drain the buffer before pasting
                        if not dryrun:
                            self._autotype(''.join(out), ms_per_char)
                        out = []
                    if not dryrun:
                        log(f'Writing {field} to primary selection and pasting.')
                        xsel = split_cmd(get_setting('xsel_executable'))
                        xdotool = split_cmd(get_setting('xdotool_executable'))
                        try:
                            Run(xsel + ['--input', '--primary'], 'soEW', stdin=val)
                            sleep(0.1)
                            Run(xdotool + ['click', '--clearmodifiers', '2'], 'soEW')
                        finally:
                            sleep(0.1)
                            Run(xsel + ['--clear', '--primary'], 'soEW')
                elif cmd.startswith('remind '):
                    notify(cmd[7:])
                else:
                    out.append(val)
                    scrubbed.append('<%s>' % cmd)
        except (TypeError, ValueError):
            raise PasswordError(
                'syntax error in keyboard script.', culprit=cmd
            )

        scrubbed = ''.join(scrubbed).replace('\t', '→').replace('\n', '↲')
        log(f'Autotyping "{scrubbed}"{" -- dry run" if dryrun else ""}.')
        if dryrun:
            output(script.account.get_name(), scrubbed, sep=': ')
        else:
            self._autotype(''.join(out), ms_per_char)