pwman-tools

Check-in [81d3818ee0]
Login

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

Overview
Comment:Initial commit of pwman2op

It "works", but doesn't yet. Main issues are:

- Seems to send everything as a Login item... but maybe that's correct based on
my test data, need to check
- Not setting titles
- Not setting tags

Update README to reflect that this repo now includes this file as well.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256: 81d3818ee0f3f8721410bbd35eff68516334d2f1cce122f0b250d62eff74bf65
User & Date: atomicules 2019-06-25 11:12:36
Context
2019-06-25
12:07
Correctly identify Secure Notes, correctly send optional args

- I had SECURE-NOTE whereas in my pwman it's SECURE-NOTES. Dur!
- For creating items with op need to use args like: `--title=Title` not
`--title Title`. Will have to see if things need quoting as well.
- Remove some print statements check-in: 11718ac61a user: atomicules tags: master, trunk

11:12
Initial commit of pwman2op

It "works", but doesn't yet. Main issues are:

- Seems to send everything as a Login item... but maybe that's correct based on
my test data, need to check
- Not setting titles
- Not setting tags

Update README to reflect that this repo now includes this file as well. check-in: 81d3818ee0 user: atomicules tags: master, trunk

2014-02-11
12:15
Re-license as BSD 2-Clause

I might change my mind again. I don't know. I'm the same with photos. check-in: 2cba61af1a user: atomicules@users.noreply.github.com tags: master, trunk

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to README.markdown.

1
2


3

4
5

6




7
8
9
10
11
12
13
14
15
16
17


















#Lastpass2pwman



CLisp script for SBCL to convert [Lastpass](https://lastpass.com) CSV export to [PWman](http://pwman.sourceforge.net) format.


Mainly done as a little learning exercise in Lisp for me, but also because I use Lastpass and also want to use a command line based password manager. Unfortunately there doesn't seem to be a command line client for Lastpass so I'm going to use PWman and every so often do a one way update of passwords.






## How to use

Developed and tested in SBCL. Use as follows

	sbcl --script /path/to/this/script <gpg id used with pwman> </path/to/lastpass/export>

The path to the Lastpass export is optional. If not supplied assumes file is called "lastpass.csv" and is in current directory. The script works by first exporting a plain text file called "pwman.txt" to current directory, however, this is then immediately encrypted via GPG and replaces the `.pwman.db` file. Both plain text files (the Lastpass export and `pwman.txt`) are then deleted - unless an error occurs with the encryption, in which case the script notifies the user and leaves the files.

## Known issues

PWman has a maximum password length of 64 characters therefore secure notes are stored in the launch field of PWman. Since the whole file is encrypted it doesn't really matter which field is used. The launch field allows up to 256 characters. If your secure notes are longer than this, then PWman will truncate them when saving. Also, note that although PWman will store 256 characters correctly it won't necessarily display them all correctly (the field was intended for displaying a single line of text), but the data will be in the file.


















|

>
>
|
>

<
>

>
>
>
>
|







|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# PWman Tools

Two little SBCL CLisp scripts.

1. The first to convert a [Lastpass](https://lastpass.com) CSV export to [PWman](http://pwman.sourceforge.net) XML format.
2. And then more recently to convert the PWman XML to [1password](https://1password.com/en) via the [op cli](https://support.1password.com/command-line/).


Originally done as a little learning exercise in Lisp for me, but also because I use(d) Lastpass and also wanted to use a command line based password manager. At the time there wasn't a command line client for Lastpass so I planned on every so often doing a one way update of passwords. As it turned out I just switched to PWman entirely.

However... a few years on and 1password is now pretty cross platform - there is a NetBSD command line client! And coupled with an effort/attempt to move my family to using 1password it seemed like a good time to try moving off PWman to 1password; Although the op cli is very bare bones. I'd try to re-use some of this work to write a script to help move my data to 1password (would have been quicker to just shell script it... maybe).

## lastpass2pwman

### How to use

Developed and tested in SBCL. Use as follows

	sbcl --script /path/to/this/script <gpg id used with pwman> </path/to/lastpass/export>

The path to the Lastpass export is optional. If not supplied assumes file is called "lastpass.csv" and is in current directory. The script works by first exporting a plain text file called "pwman.txt" to current directory, however, this is then immediately encrypted via GPG and replaces the `.pwman.db` file. Both plain text files (the Lastpass export and `pwman.txt`) are then deleted - unless an error occurs with the encryption, in which case the script notifies the user and leaves the files.

### Known issues

PWman has a maximum password length of 64 characters therefore secure notes are stored in the launch field of PWman. Since the whole file is encrypted it doesn't really matter which field is used. The launch field allows up to 256 characters. If your secure notes are longer than this, then PWman will truncate them when saving. Also, note that although PWman will store 256 characters correctly it won't necessarily display them all correctly (the field was intended for displaying a single line of text), but the data will be in the file.

## pwman2op

### How to use

Developed and tested in SBCL. Use as follows:

	sbcl --script /path/to/this/script

### Known issues

It's work-in-progress

- Assumes two-level hierarchy of data in pwman
- Only uses Login and Secure Note op templates
- Secure Notes have to be in a "SECURE-NOTES" category/heading in PWman
- Ignores stuff in a "INACTIVE" category/heading in PWman
- Assumes an existing authenticated op session

Added pwman2op.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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
; CLisp script for SBCL to export PWman to 1password
; Converts items to 1pass using 1password cli to import rather than an intermediate format; assumes an existing authenticated session
; See README for usage instructions

; Load quicklisp
(load "~/.sbclrc")
; Load libraries
; Had parsing errors with xmls so use cxml instead
(ql:quickload "cxml")

; Just assume two template types
; Obtained from `op get template <category>`
(defun template-secure-note
  (content)
  (concatenate 'string "{\"notesPlain\":\"" content "\",\"sections\":[]}"))

(defun template-login
  (username password content)
  (concatenate 'string "{\"notesPlain\":\"" content "\",\"sections\":[],\"passwordHistory\":[],\"fields\":[{\"value\":\"" username "\",\"name\":\"username\",\"type\":\"T\",\"designation\":\"username\"},{\"value\":\"" password "\",\"name\":\"password\",\"type\":\"P\",\"designation\":\"password\"}]}"))

(defun send-to-op
	(op-category pwman-category name host username passwd launch)
	(progn
		; Case would be better if I can figure that out
		; Need to fill in correct template
		(print "op-category:")
		(print op-category)
		(print "pwman-category")
		(print pwman-category)
		(defparameter template
			(if (string= op-category "Login")
				(template-login username passwd launch)
				; Errr... I guess just assume it's... need to do this and not multiple ifs in progn as otherwise template picks up NIL from progmn
				; Need to combine fields for Secure notes
				(template-secure-note (concatenate 'string "host: " host "; user: " username "; password: " passwd "; launch: " launch))))
		(print "template:")
		(print template)
		; Encode first and get return
		; Probably many better ways to do this...
		; Myabe base64 encode directly here... subtle differences though it seems with op
		; Need to single quote encode the json
		(defparameter extproc (sb-ext:run-program "sh" (list "-c" (concatenate 'string "echo '" template "' | op encode")) :search :environment :output :stream))
		(defparameter encoded-item (read-line (sb-ext:process-output extproc)))
		(print encoded-item)
		; Create item
		; Just going to send blanks, etc if that's what some fields are. It doesn't seem to matter.
		; ALMOST working...
		; Need to pick up correct env... I.e. thinks not logged in...
		; Maybe pass session variable to this? Maybe no need...?
		(defparameter cproc (sb-ext:run-program "op" (list  "create" "item" op-category encoded-item "--title" name "--url" host "--tags" pwman-category) :search :environment :output :stream))
		(defparameter output (read-line (sb-ext:process-output cproc)))
		(print output)))


; Mappings are as follows:
; `op create item <category> <encodeditem> [--title=<title>] [--url=<url>] [--vault=<vault>] [--tags=<tags>]`
;
; PWman -> 1pass
; ==============
;
; For Logins
; ==========
; name -> title
; host -> url
; user -> username
; passwd -> password
; launch -> notesPlain
; category -> tag
;
; For Secure Notes
; ================
; name -> title
; host -> notesPlain
; user -> notesPlain
; passwd -> notesPlain
; launch -> notesPlain
; category -> tag

; decrpyt the file
;(sb-ext:run-program "gpg" (list "-d" (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db") ">" ".pwman.db.decrypt") :search :environment)
; read file
(defparameter *pwman* (cxml:parse-file  (concatenate 'string (sb-unix::posix-getenv "HOME") "/.pwman.db.decrypt") (cxml-dom:make-dom-builder)))
(defparameter *categories* (dom:child-nodes (dom:item (dom:child-nodes (dom:document-element  *pwman* )) 0 )))

; Kept for ref
;(dom:do-node-list (category *categories*) (print (dom:get-attribute category "name")))

(dom:do-node-list (category *categories*)
	(progn
		(defparameter category-name (dom:get-attribute category "name"))
		(defparameter pwlists (dom:child-nodes category))
		; Kept for ref - prints category
		(print category-name)
		(dom:do-node-list (pwlist pwlists)
			(progn
				; Rather than loop through at this point, call things explicitly so can then build the op command
				(defparameter pwitems (dom:child-nodes pwlist))
				; Kept for ref - prints Name
				(print (dom:tag-name (dom:item pwitems 0)))
				(print (dom:data (dom:item (dom:child-nodes (dom:item pwitems 0)) 0)))
				; Need to check for NIL and only then set paremter. E.g:
				(defparameter name
					(if (dom:item (dom:child-nodes (dom:item pwitems 0)) 0)
						(dom:data (dom:item (dom:child-nodes (dom:item pwitems 0)) 0))
						"")))
				(defparameter host
					(if (dom:item (dom:child-nodes (dom:item pwitems 1)) 0)
						(dom:data (dom:item (dom:child-nodes (dom:item pwitems 1)) 0))
						""))
				(defparameter username
					(if (dom:item (dom:child-nodes (dom:item pwitems 2)) 0)
						(dom:data (dom:item (dom:child-nodes (dom:item pwitems 2)) 0))
						""))
				(defparameter passwd
					(if (dom:item (dom:child-nodes (dom:item pwitems 3)) 0)
						(dom:data (dom:item (dom:child-nodes (dom:item pwitems 3)) 0))
						""))
				(defparameter launch
					(if (dom:item (dom:child-nodes (dom:item pwitems 4)) 0)
						(dom:data (dom:item (dom:child-nodes (dom:item pwitems 4)) 0))
						""))
				(if (string= category-name "SECURE-NOTE")
					(send-to-op "Secure Note" category-name name host username passwd launch)
					(progn
						(if (string/= category-name "INACTIVE")
							(send-to-op "Login" category-name name host username passwd launch))))))))

; Only right at end do we want to delete file
;	(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"))))