Skip to content

Instantly share code, notes, and snippets.

@tr37ion
Forked from firecat53/keepass_kppy_pass.py
Last active August 29, 2015 14:17
Show Gist options
  • Save tr37ion/8670cd1b129263ce4181 to your computer and use it in GitHub Desktop.
Save tr37ion/8670cd1b129263ce4181 to your computer and use it in GitHub Desktop.
#!/bin/env python
"""Import keepass 1.x compatible password database into GNU pass
http://www.passwordstore.org/.
Uses the kppy (https://pypi.python.org/pypi/kppy) library to open and decode
the database. Python 2.7+ and Python 3.x compatible. Best results with Python3
for any unicode issues.
Usage: keepass2pass.py <keepass db> [--keyfile <key file if necessary>]
Based on http://git.zx2c4.com/password-store/tree/contrib/importers/keepassx2pass.py
keepassx2pass.py: Juhamatti Niemelä <iiska@iki.fi>
This file is licensed under the GPLv2+.
keepass_kppy_pass.py: Copyright (C) 2014 Scott Hansen <firecat4153@gmail.com>
"""
import argparse
import os.path
import re
from getpass import getpass
from subprocess import Popen, PIPE
from kppy.database import KPDBv1
from kppy.exceptions import KPError
def open_db(fn, pw, key=None, ro=True):
"""Open and load the database.
"""
db = KPDBv1(fn, pw, key, ro)
db.load()
return db
def clean_title(title):
"""Make the title more command line friendly.
"""
title = re.sub("(\\|\||\(|\)|/)", "-", title)
title = re.sub("-$", "", title)
title = re.sub("\@", "At", title)
title = re.sub("'", "", title)
return title
def path_for(title, path=''):
""" Generate path name from entry title and current path.
"""
title = clean_title(title)
return os.path.join(path, title)
def password_data(entry):
"""Return password data and additional info if available from
password entry element.
"""
ret = []
for field in ['password', 'username', 'url', 'comment']:
ret.append(getattr(entry, field))
return "\n".join(ret)
def import_group(group, path=''):
"""Import all entries and sub-groups from given group.
"""
npath = path_for(group.title, path)
for child_group in group.children:
import_group(child_group, npath)
for entry in group.entries:
import_entry(entry, npath)
def import_entry(entry, path=''):
"""Import new password entry to password-store using pass insert
command.
"""
print("Importing: {}".format(entry.title))
try:
data = password_data(entry).encode()
except UnicodeEncodeError:
try:
data = password_data(entry).encode('latin1')
except UnicodeEncodeError:
print("Unicode error. Unable to import {}".format(entry.title))
return
proc = Popen(['pass', 'insert', '--multiline', '--force',
path_for(entry.title, path)], stdin=PIPE, stdout=PIPE)
proc.communicate(data)
proc.wait()
def parse_args():
"""Command line arguments.
Returns: db-file: path to database file
key-file: path to key file, if necessary
"""
parser = argparse.ArgumentParser()
parser.add_argument('dbfile')
parser.add_argument('--keyfile')
args = parser.parse_args()
key = args.keyfile or None
return args.dbfile, key
def main():
fn, key = parse_args()
pw = getpass('Database passphrase: ')
try:
db = open_db(fn, pw, key)
except KPError as e:
print("{}".format(e))
else:
for group in db.root_group.children:
import_group(group)
db.close()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment