Created
January 2, 2018 05:02
-
-
Save cryptorick/b8167cbf9b5633e7f7ba2a4b53bda3cc to your computer and use it in GitHub Desktop.
lp2pass.l -- Convert LastPass vault to password store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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