Created
March 31, 2014 21:42
-
-
Save sbirch/9903010 to your computer and use it in GitHub Desktop.
A script to convert KeepassX XML exports to a CSV as consumed by 1Password. Will read from keepass.xml and write to keepass.csv in current working directory.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import xml.etree.ElementTree as ET | |
| import csv | |
| from collections import defaultdict | |
| # Note that 1Password is quite particular about the kind of escaping you use. | |
| # The default Excel dialect blows it up & there is in fact no Python dialect | |
| # corresponding to 1Password's requirements due to http://bugs.python.org/issue12178 | |
| output = csv.writer(open('keepass.csv', 'wb'), doublequote=False, escapechar='\\', quoting=csv.QUOTE_ALL) | |
| dom = ET.parse('keepass.xml') | |
| entries = defaultdict(list) | |
| # The Keepass XML file often has the same entries duplicated for past values | |
| # of your password. You would think that you might figure out which one is | |
| # correct via <creation>, <lastaccess> or <lastmod> but apparently this is | |
| # not the case. As far as I can tell the most recent (i.e. the current entry) | |
| # is the one which is written first in the file. | |
| # This also means: if you have multiple entries that actually have the same | |
| # title then only one will be kept. | |
| for element in dom.iter('entry'): | |
| assert set([child.tag for child in element]) == set([ | |
| 'expire', 'icon', 'lastmod', 'creation', 'lastaccess', 'lastmod', | |
| 'comment', 'url', 'title', 'username', 'password']) | |
| title = element.find('title').text | |
| if title is None and element.find('username').text is None: | |
| print 'Entry with no title and no username. Skipping:' | |
| print ET.tostring(element) | |
| continue | |
| elif title is None: | |
| title = element.find('username').text | |
| entries[title].append(element) | |
| #http://learn.agilebits.com/1Password4/Mac/en/KB/import.html#csv--comma-separated-values | |
| for i, (title, elements) in enumerate(entries.items()): | |
| element = elements[0] | |
| location = element.find('url').text or '' | |
| username = element.find('username').text or '' | |
| password = element.find('password').text or '' | |
| notes = '\n'.join(list(element.find('comment').itertext() or [])) | |
| output.writerow([x.replace('\\', '\\\\') for x in [title, location, username, password, notes]]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment