Skip to content

Instantly share code, notes, and snippets.

@cryptorick
Created January 2, 2018 05:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cryptorick/b8167cbf9b5633e7f7ba2a4b53bda3cc to your computer and use it in GitHub Desktop.
Save cryptorick/b8167cbf9b5633e7f7ba2a4b53bda3cc to your computer and use it in GitHub Desktop.
lp2pass.l -- Convert LastPass vault to password store
#!/usr/local/bin/pil
#
# lp2pass.l -- Convert LastPass vault to password store (pass db).
# This script processes Secure Notes as well as regular site entries.
#
# Usage: Given the exported LastPass db (vault records) are in a CSV
# file called lp.csv, call this script in any one of the following
# ways.
#
# $ cat lp.csv | lp2pass.l
# $ cat lp.csv | pil lp2pass.l
# $ cat lp.csv | /path/to/pil lp2pass.l
#
# TODO
# - [ ] Determine what to do with duplicate entries. They probably
# only need further differentiation.
# Done
# - [X] Secure note processing
#----------------------------------------------------------------------
# CSV file processing (tankfeeder code)
#
# From:
# https://bitbucket.org/mihailp/tankfeeder/raw/b508c0c72acb3c9b3d6b09170a1592e5014e2036/csv.l
(de read-csv-record NIL
(let (Scanning-Quoted-Field NIL
Contents-Of-Field)
(make
(loop
(T (eof))
(T (unless Scanning-Quoted-Field (= "^J" (peek)))
(link (pack (flip Contents-Of-Field)))
(char))
(case (peek)
(","
(if Scanning-Quoted-Field
(push 'Contents-Of-Field (char))
(link (pack (flip Contents-Of-Field)))
(off Contents-Of-Field)
(char)))
("^M"
(if Scanning-Quoted-Field
(push 'Contents-Of-Field (char))
(char)))
("\""
(ifn Scanning-Quoted-Field
(prog (on Scanning-Quoted-Field) (char))
(char)
(if (= "\"" (peek))
(push 'Contents-Of-Field (char))
(off Scanning-Quoted-Field))))
(T (push 'Contents-Of-Field (char))))))))
(de read-csv-file (INFILE)
(in INFILE (make (while (read-csv-record) (link @)))))
#----------------------------------------------------------------------
# URL parsing
(de domain-name<-url (URL)
(pack (caddr (split (chop URL) "/"))))
#----------------------------------------------------------------------
# Application
(de main NIL
(for Record (cdr (read-csv-file)) # cdr here since first one is just headers
(let (Url (car Record)
Username (car (nth Record 2))
Password (car (nth Record 3))
Extra (car (nth Record 4))
Name (car (nth Record 5))
Grouping (car (nth Record 6))
Fav (car (nth Record 7))
Domain (domain-name<-url Url)
Category (if (= "sn" Domain) "Note" Domain)
Pass-Key (pack Category "/"
# Name is what we need, if processing a
# Secure Note; otherwise, Username.
(if (= "Note" Category)
Name
Username)))
(unless Domain
(prin "+++ Skipping entry " )
(println Record)
(prinl " (Malformed URL: no domain)"))
(when Domain
(prinl ">>> Processing entry " Pass-Key)
(out (list "pass" "insert" "-m" Pass-Key)
# Don't include the following fields if processing a Secure
# Note.
(unless (= "Note" Category)
(prinl Password)
(prinl "login: " Username)
(prinl "url: " Url)
(prinl "name: " Name)
(prinl "grouping: " Grouping)
(prinl "fav: " Fav))
(prinl "extra: " Extra))))))
(main)
(bye)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment