def save(path=None, *, myst: Mystic, timer: ResettableTimer, source_path=None, **kwargs): """ Will save the mystic to a file. Accepts a path for the file. If the path is not provided, the mystic's source path will be used, if available. Will also reset the auto-close timer. If tk is installed, if the path is "?", then a dialog box will open for the file's path. """ if path == '?': if not tk: raise ImportError( 'tkinter module not imported, this functionality is unavailable' ) initialdir = None if source_path: initialdir = os.path.dirname(source_path) path = tk_filedialog.asksaveasfilename( initialdir=initialdir, filetypes=(("mystic files", "*.scm"), ("all files", "*.*"))) if not path: return 'save cancelled' if path is None: path = source_path if path is None: return 'A path must be entered for newly created files' with open(path, 'wb') as dst: myst.to_stream(dst) timer.reset() return f'saved to {path}, timer reset.'
def del_password(*, myst: Mystic, **kwargs): """ Delete a password from the mystic. Takes no arguments. Yuo will be prompted to enter the password to delete. You cannot delete the only password, add a password beforehand. """ if not myst.mutable: return 'the myst is in read-only mode, use the enable_write command to enable editing' myst.del_password() return 'password deleted'
def add_password(*, myst: Mystic, **kwargs): """ Add a password to the mystic. Takes no arguments. You will be prompted to enter the new password. If a password already exists, you will be prompted to enter it too. """ if not myst.mutable: return 'the myst is in read-only mode, use the enable_write command to enable editing' myst.add_password() return 'new password added'
def process_input(raw_source, password, pre_load_filter, check_weak=True): stream = BytesIO(raw_source) try: mystic = Mystic.from_stream(stream) except (ValueError, EOFError) as e: raise DumpError from e mystic.password_callback = lambda x: password try: try: mystic.mutable = True except Exception: pass if pre_load_filter: d = ((k, str(v)) for (k, v) in mystic.items() if fuzzy_in(pre_load_filter, k)) else: d = ((k, str(v)) for (k, v) in mystic.items()) d = list(d) except BadKey: raise DumpError('a bad password was entered') if check_weak: p_str = pass_strength(password) if p_str is not None: add_warning( f'your password has been rated as {p_str}, consider changing it!' ) return render_template('dump.html', results=d)
def enable_write(*, myst: Mystic, **kwargs): """ Set the mystic to write mode. """ if myst.mutable: return 'already in write mode' myst.mutable = True return 'myst in write mode'
def dump(pattern='', separator=': ', *, myst: Mystic, **kwargs): """ Display all the entries in the myst. Accepts an optional pattern and an optional separator. Only keys matching the pattern will be returned, and the separator will be between every key and value. """ p = re.compile(pattern) ret = [] for k, v in myst.items(): if p.search(k): ret.append(f'{k}{separator}{v}') return '\n'.join(ret)
def load_gen_no_mutable(self, buffer): buffer.seek(0) loaded = Mystic.from_stream(buffer) loaded.password_callback = self.pass_callback self.assertEqual(loaded['one'], '1') self.assertEqual(loaded['two'], '2') self.assertEqual(loaded['three'], 'שלוש') self.assertIsNone(loaded.cached_dict) self.assertFalse(loaded.changed()) return buffer
def dump_clip(pattern='', separator=': ', *, myst: Mystic, **kwargs): """ Get all the entries in the myst and copy thm to clipboard. Accepts an optional pattern and an optional separator. Only keys matching the pattern will be returned, and the separator will be between every key and value. This command will only be present if pyperclip is installed. """ p = re.compile(pattern) ret = [] for k, v in myst.items(): if p.search(k): ret.append(f'{k}{separator}{v}') pyperclip.copy('\n'.join(ret)) return 'dump copied to clipboard'
def quit(*, myst: Mystic, **kwargs): """ Exit the mysticCLI. Will prompt if any unsaved changes are recorded. """ if myst.changed(): response = input( 'unsaved changes are recorded, press enter to continue, enter anything to cancel\n' ).lower() if response: return '' return False
def main(args=None): kwargs = {} if tk: tk_root = tk.Tk() tk_root.withdraw() kwargs['tk_root'] = tk_root del tk_root args = parser.parse_args(args) while args.source == ':': args.source = input('input file or method. * is for new file.' f'{" ? for a pop-up dialog" if tk else ""}' '\n') if args.source == '?': if not tk: raise ImportError('the tkinter module could not be imported, this functionality is not available') args.source = tk_filedialog.askopenfilename(filetypes=(("mystic files", "*.scm"), ("all files", "*.*"))) if not args.source: exit() if args.source.startswith('*'): form = args.source[1:] if form == '': form = 'scm' myst = Mystic.new_from_format(form) else: kwargs['source_path'] = args.source with open(args.source, mode='br') as source: myst = Mystic.from_stream(source) if args.timeout < 0: timer = GreyHole() else: timer = ResettableTimer(args.timeout * 60, lambda: os._exit(0)) kwargs['commands'] = Command if args.nsecure: myst.password_callback = input else: myst.password_callback = partial(getpass, stream=sys.stdout) kwargs['getpass'] = myst.password_callback if (not myst.mutable) and args.write: try: myst.mutable = True except Exception as e: if args.write is ...: warnings.warn('opening in read-only mode, reason: ' + str(e)) else: raise timer.start() print( f'Welcome to the mystic CLI console version {__version__}' '\nNEVER enter your master password as a function argument!' '\nTo see all the available functions, enter help') try: while True: line = input() if not handle_line(line, throw=args.throw, myst=myst, timer=timer, **kwargs): break finally: timer.cancel()