Merge lp:~francesco-marella/pasaffe/figaro-xml-importer into lp:~mdeslaur/pasaffe/trunk

Proposed by Francesco Marella
Status: Merged
Merged at revision: 157
Proposed branch: lp:~francesco-marella/pasaffe/figaro-xml-importer
Merge into: lp:~mdeslaur/pasaffe/trunk
Diff against target: 256 lines (+247/-0)
2 files modified
bin/pasaffe-import-figaroxml (+180/-0)
pasaffe_lib/figaroxml.py (+67/-0)
To merge this branch: bzr merge lp:~francesco-marella/pasaffe/figaro-xml-importer
Reviewer Review Type Date Requested Status
Marc Deslauriers Approve
Review via email: mp+76183@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Looks good, thanks for the contribution!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bin/pasaffe-import-figaroxml'
2--- bin/pasaffe-import-figaroxml 1970-01-01 00:00:00 +0000
3+++ bin/pasaffe-import-figaroxml 2011-09-20 10:10:31 +0000
4@@ -0,0 +1,180 @@
5+#!/usr/bin/python
6+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
7+### BEGIN LICENSE
8+# Copyright (C) 2011 Marc Deslauriers <marc.deslauriers@canonical.com>
9+# Copyright (C) 2011 Francesco Marella <francesco.marella@gmail.com>
10+# This program is free software: you can redistribute it and/or modify it
11+# under the terms of the GNU General Public License version 3, as published
12+# by the Free Software Foundation.
13+#
14+# This program is distributed in the hope that it will be useful, but
15+# WITHOUT ANY WARRANTY; without even the implied warranties of
16+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
17+# PURPOSE. See the GNU General Public License for more details.
18+#
19+# You should have received a copy of the GNU General Public License along
20+# with this program. If not, see <http://www.gnu.org/licenses/>.
21+### END LICENSE
22+
23+import sys
24+import os
25+import getpass
26+import shutil
27+from optparse import OptionParser
28+
29+import gettext
30+from gettext import gettext as _
31+gettext.textdomain('pasaffe')
32+
33+# Add project root directory (enable symlink and trunk execution)
34+PROJECT_ROOT_DIRECTORY = os.path.abspath(
35+ os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
36+
37+python_path = []
38+if os.path.abspath(__file__).startswith('/opt'):
39+ syspath = sys.path[:] # copy to avoid infinite loop in pending objects
40+ for path in syspath:
41+ opt_path = path.replace('/usr', '/opt/extras.ubuntu.com/pasaffe')
42+ python_path.insert(0, opt_path)
43+ sys.path.insert(0, opt_path)
44+if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'pasaffe'))
45+ and PROJECT_ROOT_DIRECTORY not in sys.path):
46+ python_path.insert(0, PROJECT_ROOT_DIRECTORY)
47+ sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
48+if python_path:
49+ os.putenv('PYTHONPATH', "%s:%s" % (os.getenv('PYTHONPATH', ''), ':'.join(python_path))) # for subprocess
50+
51+from pasaffe_lib import figaroxml
52+from pasaffe_lib import readdb
53+
54+parser = OptionParser()
55+parser.add_option("-f", "--file", dest="filename",
56+ help="specify alternate FPM2 XML database file", metavar="FILE")
57+parser.add_option("-o", "--overwrite", dest="overwrite", action="store_true",
58+ default=False, help="overwrite existing Pasaffe password store")
59+
60+(options, args) = parser.parse_args()
61+
62+
63+def confirm(prompt=None, resp=False):
64+ """prompts for yes or no response from the user. Returns True for yes and
65+ False for no.
66+
67+ 'resp' should be set to the default value assumed by the caller when
68+ user simply types ENTER.
69+
70+ >>> confirm(prompt='Create Directory?', resp=True)
71+ Create Directory? [y]|n:
72+ True
73+ >>> confirm(prompt='Create Directory?', resp=False)
74+ Create Directory? [n]|y:
75+ False
76+ >>> confirm(prompt='Create Directory?', resp=False)
77+ Create Directory? [n]|y: y
78+ True
79+
80+ """
81+
82+ if prompt is None:
83+ prompt = 'Confirm'
84+
85+ if resp:
86+ prompt = '%s [%s]|%s: ' % (prompt, 'y', 'n')
87+ else:
88+ prompt = '%s [%s]|%s: ' % (prompt, 'n', 'y')
89+
90+ while True:
91+ ans = raw_input(prompt)
92+ if not ans:
93+ return resp
94+ if ans not in ['y', 'Y', 'n', 'N']:
95+ print 'please enter y or n.'
96+ continue
97+ if ans == 'y' or ans == 'Y':
98+ return True
99+ if ans == 'n' or ans == 'N':
100+ return False
101+
102+if options.filename == None:
103+ filename = None
104+else:
105+ filename = options.filename
106+
107+print "Attempting to import FPM2 XML passwords..."
108+print "Database filename is %s" % filename
109+print
110+
111+if not os.path.exists(filename):
112+ print "Could not locate database file!"
113+ sys.exit(1)
114+
115+if 'XDG_DATA_HOME' in os.environ:
116+ basedir = os.path.join(os.environ['XDG_DATA_HOME'], 'pasaffe')
117+else:
118+ basedir = os.path.join(os.environ['HOME'], '.local/share/pasaffe')
119+
120+if not os.path.exists(basedir):
121+ os.mkdir(basedir, 0700)
122+
123+db_filename = os.path.join(basedir, 'pasaffe.psafe3')
124+
125+password = None
126+
127+fpmxml = figaroxml.FigaroXML(filename, password)
128+
129+items = len(fpmxml.records)
130+
131+if items == 0:
132+ print "Database was empty!"
133+ sys.exit(1)
134+else:
135+ print "Located %s passwords in the database!" % items
136+
137+if not os.path.exists(db_filename) and options.overwrite != True:
138+ print "WARNING: Could not locate a Pasaffe database."
139+ response = confirm(prompt='Create a new database?', resp=False)
140+ options.overwrite = True
141+elif options.overwrite == True:
142+ print "If you continue, your current Pasaffe database will be DELETED."
143+ response = confirm(prompt='Overwrite database?', resp=False)
144+else:
145+ print "If you continue, passwords will be imported into Pasaffe."
146+ response = confirm(prompt='Import to database?', resp=False)
147+
148+if response == False:
149+ print "Aborting."
150+ sys.exit(1)
151+
152+
153+# Create backup if exists
154+if os.path.exists(db_filename):
155+ shutil.copy(db_filename, db_filename + ".bak")
156+
157+if options.overwrite == True and os.path.exists(db_filename):
158+ os.unlink(db_filename)
159+
160+# Get password for Pasaffe database
161+if options.overwrite == True:
162+ print "You now must enter a master password for the new Pasaffe database"
163+ while(1):
164+ password = getpass.getpass("New password: ")
165+ password_conf = getpass.getpass("Confirm password: ")
166+ if password != password_conf:
167+ print "ERROR: passwords don't match, try again.\n\n"
168+ else:
169+ break
170+ passsafe = readdb.PassSafeFile()
171+ passsafe.new_db(password)
172+ passsafe.records = fpmxml.records
173+ passsafe.writefile(db_filename)
174+
175+else:
176+ print "You must now enter the Pasaffe database password."
177+ password = getpass.getpass()
178+
179+ passsafe = readdb.PassSafeFile(db_filename, password)
180+ for entry in fpmxml.records:
181+ passsafe.records.append(entry)
182+ passsafe.writefile(db_filename, backup=True)
183+
184+print "Success!"
185
186=== added file 'pasaffe_lib/figaroxml.py'
187--- pasaffe_lib/figaroxml.py 1970-01-01 00:00:00 +0000
188+++ pasaffe_lib/figaroxml.py 2011-09-20 10:10:31 +0000
189@@ -0,0 +1,67 @@
190+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
191+### BEGIN LICENSE
192+# Copyright (C) 2011 Francesco Marella <francesco.marella@gmail.com>
193+# This program is free software: you can redistribute it and/or modify it
194+# under the terms of the GNU General Public License version 3, as published
195+# by the Free Software Foundation.
196+#
197+# This program is distributed in the hope that it will be useful, but
198+# WITHOUT ANY WARRANTY; without even the implied warranties of
199+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
200+# PURPOSE. See the GNU General Public License for more details.
201+#
202+# You should have received a copy of the GNU General Public License along
203+# with this program. If not, see <http://www.gnu.org/licenses/>.
204+### END LICENSE
205+
206+import sys
207+import struct
208+import hashlib
209+import os
210+import time
211+from xml.etree import cElementTree as ET
212+import logging
213+logger = logging.getLogger('pasaffe')
214+
215+
216+class FigaroXML:
217+ records = []
218+ index = 0
219+
220+ cipher = None
221+
222+ def __init__(self, filename=None, password=None):
223+ """ Reads a FPM2 file"""
224+
225+ if filename != None:
226+ self.readfile(filename, password)
227+
228+ def readfile(self, filename, password):
229+ """ Parses database file"""
230+ try:
231+ element = ET.parse(filename)
232+ except Exception:
233+ raise RuntimeError("Could not open %s. Aborting." % filename)
234+
235+ if element.getroot().tag != 'FPM':
236+ raise RuntimeError("Not a valid FPM2 XML file")
237+
238+ for pwitem in element.findall('./PasswordList/PasswordItem'):
239+ uuid = os.urandom(16)
240+ timestamp = struct.pack("<I", int(time.time()))
241+ new_entry = {1: uuid, 3: '', 4: '', 6: '',
242+ 7: timestamp, 8: timestamp, 12: timestamp}
243+
244+ for x in list(pwitem):
245+ if x.tag == 'title':
246+ new_entry[3] = x.text or 'Untitled item'
247+ elif x.tag == 'user':
248+ new_entry[4] = x.text or ''
249+ elif x.tag == 'password':
250+ new_entry[6] = x.text or ''
251+ elif x.tag == 'url':
252+ new_entry[13] = x.text or ''
253+ elif x.tag == 'notes':
254+ new_entry[5] = x.text or ''
255+
256+ self.records.append(new_entry)

Subscribers

People subscribed via source and target branches