kwallet2pass.py (3626B)
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # 4 # Copyright (C) 2012 Juhamatti Niemelä <iiska@iki.fi>. All Rights Reserved. 5 # Copyright (C) 2014 Diggory Hardy <diggory.hardy@gmail.com>. All Rights Reserved. 6 # This file is licensed under the GPLv2+. Please see COPYING for more information. 7 8 import sys 9 import re 10 11 from subprocess import Popen, PIPE 12 from xml.etree import ElementTree 13 14 HEAD = '/passwords/' 15 16 def insert_data(path,text): 17 """ Insert data into the password store. 18 (1) removes HEAD from path 19 (2) ensures text ends with a new line and encodes in UTF-8 20 (3) inserts 21 """ 22 global HEAD 23 if path.startswith(HEAD): 24 path = path[len(HEAD):] 25 26 if not text.endswith('\n'): 27 text = text + '\n' 28 text = text.encode('utf8') 29 30 #print "Import: " + path + ": " + text 31 proc = Popen(['pass', 'insert', '--multiline', '--force', path], 32 stdin=PIPE, stdout=PIPE) 33 proc.communicate(text) 34 proc.wait() 35 36 def space_to_camelcase(value): 37 output = "" 38 first_word_passed = False 39 for word in value.split(" "): 40 if not word: 41 output += "_" 42 continue 43 if first_word_passed: 44 output += word.capitalize() 45 else: 46 output += word.lower() 47 first_word_passed = True 48 return output 49 50 def cleanTitle(title): 51 # make the title more command line friendly 52 title = re.sub("(\\|\||\(|\)|/)", "-", title) 53 title = re.sub("-$", "", title) 54 title = re.sub("\@", "At", title) 55 title = re.sub("'", "", title) 56 return title 57 58 def path_for(element, path=''): 59 """ Generate path name from elements title and current path """ 60 title = cleanTitle(space_to_camelcase(element.attrib['name'])) 61 return '/'.join([path, title]) 62 63 def unexpected(element, path): 64 print "Unexpected element: " + path + '/' + element.tag + "\tAttributes: " + str(element.attrib) 65 66 def import_map(element, path): 67 npath = path_for(element, path) 68 nEntries = 0 69 text = 'Map' 70 for child in element: 71 if child.tag == 'mapentry': 72 name = child.attrib['name'] 73 text = text + '\n\n' + name + '\n' + child.text 74 nEntries += 1 75 for child2 in child: 76 unexpected(child, path_for(child, npath)) 77 else: 78 unexpected(child, npath) 79 80 insert_data(npath, text) 81 print "Map " + npath + " [" + str(nEntries) + " entries]" 82 83 def import_password(element, path=''): 84 """ Import new password entry to password-store using pass insert 85 command """ 86 npath = path_for(element, path) 87 text = element.text 88 if text == None: 89 print "Password " + npath + ": no text" 90 text = "" 91 insert_data(npath, text) 92 for child in element: 93 unexpected(child, npath) 94 95 def import_folder(element, path=''): 96 """ Import all entries and folders from given folder """ 97 npath = path_for(element, path) 98 print "Importing folder " + npath 99 nPasswords = 0 100 for child in element: 101 if child.tag == 'folder': 102 import_folder(child, npath) 103 elif child.tag == 'password': 104 import_password(child, npath) 105 nPasswords += 1 106 elif child.tag == 'map': 107 import_map(child, npath) 108 else: 109 unexpected(child, npath) 110 111 if nPasswords > 0: 112 print "[" + str(nPasswords) + " passwords]" 113 114 def main(xml_file): 115 """ Parse XML entries from a KWallet """ 116 element = ElementTree.parse(xml_file).getroot() 117 assert element.tag == 'wallet' 118 import_folder(element) 119 120 if __name__ == '__main__': 121 main(sys.argv[1])