[pass] [PATCH] Added alternative converter for keepassx stores, which does not do CamelCase and can slightly more elegantly handle entries with duplicate names.
Boi Sletterink
boi at sletterink.nl
Wed Jun 10 11:36:01 CEST 2015
---
contrib/importers/keepassx2pass-no_camel_case.py | 107 +++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/contrib/importers/keepassx2pass-no_camel_case.py b/contrib/importers/keepassx2pass-no_camel_case.py
new file mode 100755
index 0000000..8d028b1
--- /dev/null
+++ b/contrib/importers/keepassx2pass-no_camel_case.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 Juhamatti Niemelä <iiska at iki.fi>. All Rights Reserved.
+# This file is licensed under the GPLv2+. Please see COPYING for more information.
+
+import sys
+import re
+import os
+
+from subprocess import Popen, PIPE
+from xml.etree import ElementTree
+
+
+def cleanTitle(title):
+ # make the title more command line friendly
+
+ title = re.sub(" ", "_", title)
+ title = re.sub("'", "", title)
+ title = re.sub("\W", "-", title)
+
+ total_changes = 1
+ while total_changes > 0:
+ total_changes = 0
+ (title, changes) = re.subn("__+", "_", title)
+ total_changes = total_changes + changes
+ (title, changes) = re.subn("(--+|_-|-_)", "-", title)
+ total_changes = total_changes + changes
+
+ title = re.sub("[-_]+$", "", title)
+ title = re.sub("^[-_]+", "", title)
+
+ return title
+
+
+def pass_entry_exists(entry):
+ full_path = os.environ['HOME'] + '/.password-store' + entry + '.gpg'
+ print full_path + "\n"
+
+ try:
+ file = open(full_path);
+ except IOError as e:
+ return False
+
+ return True
+
+
+def path_for(element, path=''):
+ """ Generate path name from elements title and current path """
+ title_text = element.find('title').text
+ if title_text is None:
+ title_text = "unknown"
+ title = cleanTitle(title_text)
+
+ # Check for uniqueness, and make it unique if we have to
+ entry_path = '/'.join([path,title])
+ serial_number = 1
+ while pass_entry_exists(entry_path):
+ serial_number = serial_number + 1
+ entry_path = '/'.join([path, "{0}-{1:d}".format(title, serial_number)])
+
+ return entry_path
+
+
+def password_data(element):
+ """ Return password data and additional info if available from
+ password entry element. """
+ passwd = element.find('password').text
+ ret = passwd + "\n" if passwd else "\n"
+ for field in ['title','username', 'url', 'comment']:
+ fel = element.find(field)
+ children = [unicode(e.text or '') + unicode(e.tail or '') for e in list(fel)]
+ if len(children) > 0:
+ children.insert(0, '')
+ text = (fel.text or '') + "\n".join(children)
+ if len(text) > 0:
+ ret = "%s%s: %s\n" % (ret, fel.tag, text)
+ return ret
+
+
+def import_entry(element, path=''):
+ """ Import new password entry to password-store using pass insert
+ command """
+ print "Importing " + path_for(element, path)
+ proc = Popen(['pass', 'insert', '--multiline', '--force',
+ path_for(element, path)],
+ stdin=PIPE, stdout=PIPE)
+ proc.communicate(password_data(element).encode('utf8'))
+ proc.wait()
+
+
+def import_group(element, path=''):
+ """ Import all entries and sub-groups from given group """
+ npath = path_for(element, path)
+ for group in element.findall('group'):
+ import_group(group, npath)
+ for entry in element.findall('entry'):
+ import_entry(entry, npath)
+
+
+def main(xml_file):
+ """ Parse given KeepassX XML file and import password groups from it """
+ for group in ElementTree.parse(xml_file).findall('group'):
+ import_group(group)
+
+if __name__ == '__main__':
+ main(sys.argv[1])
--
2.1.4
More information about the Password-Store
mailing list