OI Safe Importer

Mark Feit mfeit+passwordstore at notonthe.net
Sat Jun 12 12:59:34 UTC 2021


Attached is a Python program that imports CSV files exported by OI Safe 
for the contrib/importers directory.

Enjoy.

--Mark


-------------- next part --------------
#!/usr/bin/env python3
'''

Import an OI Safe-exported CSV to pass

See 'oisafe2pass --help' for usage.

Entry format:

    <password>        Empty if not present
    User:  <user>     Not provided if not present
    Site:  <site>     Not provided if not present
    <notes>           Not provided if not present


Copyright 2021 Mark Feit <mfeit+pass at notonthe.net>. All Rights
Reserved.  This file is licensed under the GPLv2+. Please see COPYING
for more information.

'''

import csv
import argparse
import subprocess
import sys


def run_program(args, stdin=None):
    '''
    Run a program from args, provide stdin
    Returns (status, stdout, stderr)
    '''

    try:
        proc = subprocess.Popen(args,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        output, error = proc.communicate(stdin.encode('utf8')
                                         if stdin is not None else '')
        proc.wait()
        return proc.returncode, output, error
    except Exception as ex:
        return 1, None, str(ex)


def import_record(record, dry_run=False, verbose=False):
    '''
    Import a single record as inhaled by the CSV module
    '''

    (category, description, site, user, password, notes, _edited) = record

    path = '/'.join([
        category.replace('/', '_'),
        description.replace('/', '_')
        ])

    if path in [ '', '/' ]:
        print('Skipping record missing path and description', file=sys.stderr)
        return

    lines = [password or '']

    if len(user):
        lines.append('{:6s}{}'.format('User:', user))

    if len(site):
        lines.append('{:6s}{}'.format('Site:', site))

    if notes:
        lines.append('')
        lines.append(notes)

    # This forces a newline at the end
    lines.append('')

    args = ['pass', 'insert', '--multiline', '--force', path]
    content = '\n'.join(lines)

    if dry_run:

        print(' '.join(args))
        print(content)
        print()

    else:

        if verbose:
            print("Inserting {}".format(path))

        status, stdout, stderr = run_program(args, stdin=content)

        if status != 0:
            print('Inserting {} failed:\n{}'.format(path, stderr), file=sys.stderr)
            exit(1)



#
# Main Program
#

parser = argparse.ArgumentParser(description='Import a CSV exported from OI Safe')

parser.add_argument('infile', nargs='?',
                    help='Input file (stdin if not provided)')
parser.add_argument('--dry-run', dest='dry_run', action='store_true', default=False,
                    help='Print commands and content to stdout')
parser.add_argument('--verbose', dest='verbose', action='store_true', default=False,
                    help='Show progress on stdout')

args = parser.parse_args()


try:
    if args.infile is None:
        raise IndexError()
    input_file = open(args.infile, 'r')
except IndexError:
    input_file = sys.stdin

first = True
        
for line in csv.reader(input_file, dialect='excel'):

    if first:
        # First line contains headers
        first = False
        continue

    import_record(line, dry_run=args.dry_run, verbose=args.verbose)

exit(0)


More information about the Password-Store mailing list