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)
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())
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
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))
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.')
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
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)
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
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))
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
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()
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.')
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()
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
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())
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())
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
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())
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())
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())
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))
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())
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())
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())
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())
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())
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())
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)
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
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
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
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)
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())
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)
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()
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()
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())
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)
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())
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))
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
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)
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.')
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
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]
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())
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()
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))
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))