pwman-tools

Check-in [adf34078f1]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Remove spurious spaces
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256: adf34078f17c850360a1dd25db0c0e8f7a84e93333ff2c16800d430a824888b7
User & Date: atomicules@lavabit.com 2012-12-10 10:02:25
Context
2012-12-10
10:04
Fix identation check-in: e9eafc3f15 user: atomicules@lavabit.com tags: master, trunk
10:02
Remove spurious spaces check-in: adf34078f1 user: atomicules@lavabit.com tags: master, trunk
09:47
Use sublists in PWman to match groups in Lastpass.

I hadn't realised PWman supports sublists (groups), therefore can match
the grouping structure used in Lastpass.

- Now reads in CSV file to hashtable. Since CSV file is not ordered by
group, using a hashtable means we can loop through and create a key
for a group if it doesn't exists, or collate entries against a
existing group/key.
- Writing out hasn't changed much. Loops through the keys of the hash
table and loops within each key/group as well. check-in: 05989bbd9b user: atomicules@lavabit.com tags: master, trunk

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lastpass2pwman.lisp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


21

22


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
;CLisp script for SBCL to convert lastpass CSV export to pwman format
;See README for usage instructions

;Load quicklisp
(load "~/.sbclrc")
;Load libraries
(ql:quickload "csv-parser")
(ql:quickload "xmls")

;Note to self: Start with 2nd *posix-argv* as first is always /path/to/sbcl, etc.
;gpgid to encrypt file with
(defparameter gpgid (second *posix-argv*))
;Check for supplied filename, otherwise assume called lastpass.csv
(defparameter infile (if (null (third *posix-argv*)) "lastpass.csv" (third *posix-argv*)))
;read the lastpass file and build up hash for each group, then write lists at the end.
(defparameter *lastpass* (make-hash-table))
(csv-parser:map-csv-file infile
	(lambda (ln)
		(if (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) ;Group
			;Note, without (intern (string-upcase or (read-from-string keys aren't set properly and something bizarre happens.


			(setf (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) (append (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) (list ln))) ;append key

			(setf (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) (list ln)))) :skip-lines 1) ;create key


(with-open-file (stream "pwman.txt" :direction :output :if-exists :supersede)
	(format stream "<?xml version=\"1.0\"?><PWMan_PasswordList version=\"3\"><PwList name=\"Main\">")
	;For each key in hash
	(with-hash-table-iterator (group *lastpass*)
		(loop
			(multiple-value-bind (returned? key value) (group)
			(if returned? 
				(progn
					(format stream "<PwList name=\"~a\">" key)	
					;Then loop through each entry in value
					(loop for entry in value
						  do (format stream
								"<PwItem><name>~a</name><host>~a</host><user>~a</user><passwd>~a</passwd><launch></launch></PwItem>"
								(xmls:toxml (fifth entry))
								(xmls:toxml (first entry))
								(xmls:toxml (second entry))
								(if (null (third entry)) ;Secure Notes have no password 
									(xmls:toxml (fourth entry)) ;Use extra field if Secure Note
									(xmls:toxml (third entry)))))
					(format stream "</PwList>"))
				(return)))))
	(format stream "</PwList></PWMan_PasswordList>"))
;Move original file to backup
(rename-file (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db") (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db.bak")) 
;gpg encrpyt the file
(let ((proc (sb-ext:run-program "gpg" (list "-r" gpgid "-o" (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db") "-e" "pwman.txt") :search :environment)))
	(if (= 0 (sb-ext:process-exit-code proc))
		;If that was successful, then delete the un-encrypted files
		(progn 
			(delete-file infile) 
			(delete-file "pwman.txt"))
		;If not restore backup and leave plain text files (otherwise will fail next time on above rename)
		(progn
			(rename-file (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db.bak") (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db")) 
			(print "Couldn't encrypt file, plain text files have not been deleted"))))
|








|




|




|
>
>
|
>
|
>
>





|
|

|
|
|
|




|






|




|
|



|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
;CLisp script for SBCL to convert lastpass CSV export to PWman format
;See README for usage instructions

;Load quicklisp
(load "~/.sbclrc")
;Load libraries
(ql:quickload "csv-parser")
(ql:quickload "xmls")

;Note to self: Start with 2nd *posix-argv* as first is always /path/to/sbcl, etc
;gpgid to encrypt file with
(defparameter gpgid (second *posix-argv*))
;Check for supplied filename, otherwise assume called lastpass.csv
(defparameter infile (if (null (third *posix-argv*)) "lastpass.csv" (third *posix-argv*)))
;read the lastpass file and build up hash for each group
(defparameter *lastpass* (make-hash-table))
(csv-parser:map-csv-file infile
	(lambda (ln)
		(if (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) ;Group
			;Note, without (intern (string-upcase or (read-from-string keys aren't set properly and something bizarre happens
			(setf
				(gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*)
				(append (gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*) (list ln))) ;append key
			(setf
				(gethash (read-from-string (substitute #\- #\Space (sixth ln))) *lastpass*)
				(list ln)))) :skip-lines 1) ;create key
;Write hash out as XML
(with-open-file (stream "pwman.txt" :direction :output :if-exists :supersede)
	(format stream "<?xml version=\"1.0\"?><PWMan_PasswordList version=\"3\"><PwList name=\"Main\">")
	;For each key in hash
	(with-hash-table-iterator (group *lastpass*)
		(loop
			(multiple-value-bind (returned? groupname groupentries) (group)
			(if returned?
				(progn
					(format stream "<PwList name=\"~a\">" groupname)
					;Then loop through each entry in group
					(loop for entry in groupentries
						do (format stream
								"<PwItem><name>~a</name><host>~a</host><user>~a</user><passwd>~a</passwd><launch></launch></PwItem>"
								(xmls:toxml (fifth entry))
								(xmls:toxml (first entry))
								(xmls:toxml (second entry))
								(if (null (third entry)) ;Secure Notes have no password
									(xmls:toxml (fourth entry)) ;Use extra field if Secure Note
									(xmls:toxml (third entry)))))
					(format stream "</PwList>"))
				(return)))))
	(format stream "</PwList></PWMan_PasswordList>"))
;Move original file to backup
(rename-file (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db") (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db.bak"))
;gpg encrpyt the file
(let ((proc (sb-ext:run-program "gpg" (list "-r" gpgid "-o" (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db") "-e" "pwman.txt") :search :environment)))
	(if (= 0 (sb-ext:process-exit-code proc))
		;If that was successful, then delete the un-encrypted files
		(progn
			(delete-file infile)
			(delete-file "pwman.txt"))
		;If not restore backup and leave plain text files (otherwise will fail next time on above rename)
		(progn
			(rename-file (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db.bak") (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db"))
			(print "Couldn't encrypt file, plain text files have not been deleted"))))