jaromail

a commandline tool to easily and privately handle your e-mail
git clone git://parazyd.org/jaromail.git
Log | Files | Refs | Submodules | README

commit 3e5d8243ddc78134ab5d8dbc18fdf8a660157130
parent 8d2823f10f7caf07b8e7545e52d06ae10ffe6b42
Author: Jaromil <jaromil@dyne.org>
Date:   Fri, 26 Dec 2014 19:18:09 +0100

list addresses in gpg key signatures, more fixes

Diffstat:
Msrc/jaro | 24+++++++++++-------------
Msrc/zlibs/addressbook | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/zlibs/email | 4++--
Msrc/zlibs/helpers | 11+++++++++++
Msrc/zlibs/maildirs | 6+++---
5 files changed, 148 insertions(+), 28 deletions(-)

diff --git a/src/jaro b/src/jaro @@ -443,20 +443,19 @@ a pipe | in front indicate they take an email body from stdin import import entries from a VCard file in the $list - export export the $list to VCard file (addressbook.vcf) + export export the $list to a VCard file - abook edit the $list using abook terminal curses editor + abook edit the $list using abook terminal console editor |learn learn addresses from mails piped in stdin -|forget remove addresses found in mails piped in stdin - - list prints to console all the entries in a $list + list prints to console all the entries in $list search search into $list using a string parameter |isknown read e-mail from stdin, return 0 if sender is known + extract list all recipients or senders found in a maildir == Operational commands (use -a to indicate which $account) account names correspond to the filenames, i.e. imap.default @@ -466,8 +465,8 @@ account names correspond to the filenames, i.e. imap.default send send all emails queued in outbox/ to an smtp.$account - peek connect to an imap.$account with ncurses terminal mutt - remote folders supported. use to delete without download + peek connect to an imap folder with ncurses terminal mutt + use for server-side operations on emails without download passwd set account passwords in the OS native keyring or into a simple, file based, gpg encrypted database. @@ -492,15 +491,14 @@ maildirs are directories of mails downloaded in Mail/ update updates Filters.txt generating rules (sieve format) - ramdisk [open|close] activates fast cache in RAM on Mac/OSX (root) - Experimental commands: stat prints a statistical overview on stored maildirs cert import most common SSL certificates from the Internet -For a complete introductory documentation, see the User Manual in LaTeX -Website on <http://jaromail.dyne.org> Report bugs to <https://bugs.dyne.org> +For a complete introductory documentation, see the User Manual (PDF) +Website on <http://dyne.org/software/jaro-mail> +Report bugs to <https://github.com/dyne/JaroMail/issues> EOF } @@ -696,9 +694,9 @@ main() isknown) CLEANEXIT=0; sender_isknown ${PARAM} ;; learn) CLEANEXIT=0; learn ${PARAM} ;; forget) CLEANEXIT=0; forget ${PARAM} ;; - list) CLEANEXIT=0; edit_abook ${PARAM} ;; + list) CLEANEXIT=0; list_addresses ${PARAM} ;; - import) import_addressbook ${PARAM} ;; + import) import_addressbook "${PARAM}" ;; "export") case "$PARAM" in abook) diff --git a/src/zlibs/addressbook b/src/zlibs/addressbook @@ -68,15 +68,6 @@ insert_address() { return 0 } -# update_name() { -# func "update address: $1, $2" -# cat <<EOF | ${SQL} -batch $ADDRESSBOOK 2> /dev/null -# UPDATE $list SET name="${2}" WHERE email LIKE "${1}"; -# EOF -# { test $? != 0 } && { -# func "address not found or error occurred" } -# } - remove_address() { warning "remove_address() TODO in abook branch" return 0 @@ -230,9 +221,129 @@ forget() { # remove_address "${head[(ws:,:)1]}" } +# list all entries in addressbook or a file +list_addresses() { + [[ "$1" = "" ]] && { + # TODO: list the default addressbook + edit_abook + } + + # a map to eliminate duplicates + typeset -AU result + + ######### GPG + [[ `file "$1"` =~ "GPG key public ring" ]] && { + + notice "Listing addresses found in GPG keyring: $1" + _addrs=`gpg --list-keys --with-colons | awk -F: '{print $10}'` + for i in ${(f)_addrs}; do + _parsed=`print "From: $i" | ${WORKDIR}/bin/fetchaddr -a -x from` + _e="${_parsed[(ws:,:)1]}" + isemail "$_e" + [[ $? = 0 ]] || continue + # check if the email is not already parsed + [[ "${result[$_e]}" = "" ]] && { + _n="${_parsed[(ws:,:)2]}" + result+=("$_e" "$_n") + print - "$_n <$_e>" + } + done + + notice "Unique addresses found: ${#result}" + # counts which addresses are known to us + _known=0 + for i in ${(k)result}; do + lookup_email ${i} + [[ $? = 0 ]] || { + _known=$(( $_known + 1 )) } + done + act "new addresses: $_known" + } + + [[ `file "$1"` =~ "PGP public key" ]] && { + _gpg="gpg --no-default-keyring --keyring $MAILDIRS/cache/pubkey.gpg --batch --with-colons" + rm -f $MAILDIRS/cache/pubkey.gpg + ${=_gpg} --import "$1" + # first make sure all unknown keys are imported + _addrs=`${=_gpg} --list-sigs | awk -F: '{print $5 " " $10}'` + for i in ${(f)_addrs}; do + [[ "$i" =~ "[User ID not found]" ]] && { + act "looking up: $i" + ${=_gpg} --recv-key ${i[(w)1]} + } + done + + _addrs=`${=_gpg} --list-sigs | awk -F: '{print $10}'` + for i in ${(f)_addrs}; do + _parsed=`print "From: $i" | ${WORKDIR}/bin/fetchaddr -a -x from` + _e="${_parsed[(ws:,:)1]}" + isemail "$_e" + [[ $? = 0 ]] || continue + # check if the email is not already parsed + [[ "${result[$_e]}" = "" ]] && { + _n="${_parsed[(ws:,:)2]}" + result+=("$_e" "$_n") + print - "$_n <$_e>" + } + done + + notice "Unique addresses found: ${#result}" + # counts which addresses are known to us + _known=0 + for i in ${(k)result}; do + lookup_email ${i} + [[ $? = 0 ]] || { + _known=$(( $_known + 1 )) } + done + act "new addresses: $_known" + } + +} + + # import an addressbook, autodetect its type import_addressbook() { notice "Importing addressbook" + func "$1" + # a map to eliminate duplicates + typeset -AU result + + # stdin + [[ "$1" = "stdin" ]] && { + act "reading entries from stdin" + _stdin=`cat` + _new=0 + for i in ${(f)_stdin}; do + + # skip comments starting with # + [[ "$i[1]" = "#" ]] && continue + + _parsed=`print - "From: $i" | ${WORKDIR}/bin/fetchaddr -a -x from` + _e="${_parsed[(ws:,:)1]}" + + # check if is really an email + isemail "$_e" + [[ $? = 0 ]] || continue + + # check if the email is not a duplicate + [[ "${result[$_e]}" = "" ]] || continue + + _n="${_parsed[(ws:,:)2]}" + result+=("$_e" "$_n") + + # check if the email is not already known + lookup_email "$_e" + [[ $? = 0 ]] && continue + + [[ $DRYRUN = 0 ]] && insert_address "$_e" "$_n" + act "new entry imported: $_n <$_e>" + _new=$(( $_new + 1 )) + done + notice "Valid unique entries parsed: ${#result}" + act "new addresses found: ${_new}" + return 0 + } + if [[ "$1" != "" ]]; then func "file specified: $1" # a file was given as argument @@ -428,7 +539,7 @@ edit_abook() { abook --config <(cat <<EOF set autosave=true -set mutt_command=jaro +set mutt_command=jaro compose set sort_field=name EOF ) --datafile "$ADDRESSBOOK" diff --git a/src/zlibs/email b/src/zlibs/email @@ -355,8 +355,8 @@ VERBOSE=2 EOF act "Sending out anonymous email via mixmaster" - recipients=(`hdr $qbody | fetchaddr -a -x to | cut -d, -f1`) - recipients+=(`hdr $qbody | fetchaddr -a -x cc | cut -d, -f1`) + recipients=(`hdr $qbody | ${WORKDIR}/bin/fetchaddr -a -x to | cut -d, -f1`) + recipients+=(`hdr $qbody | ${WORKDIR}/bin/fetchaddr -a -x cc | cut -d, -f1`) for r in ${recipients}; do act "Sending to: ${r}" diff --git a/src/zlibs/helpers b/src/zlibs/helpers @@ -40,6 +40,17 @@ awk '{ for (i=1;i<=NF;i++) gsub(/<|>|,/ , "" , $i); print $i } }' } +isemail() { + [[ "$1" = "" ]] && { + func "isemail() called on an empty string" + return 1 + } + print "$1" | \ + grep -E "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" \ + > /dev/null + return $? +} + # parses stdin and converts some characters to html escape_html() { sed -e ' diff --git a/src/zlibs/maildirs b/src/zlibs/maildirs @@ -321,7 +321,7 @@ md_extract() { [[ "${result[$_e]}" = "" ]] && { _n=${i[(ws:,:)2]} result+=("$_e" "$_n") - print "$_n <$_e>" + print - "$_n <$_e>" } done notice "Unique $_action found: ${#result}" @@ -329,8 +329,8 @@ md_extract() { # counts which addresses are known to us _known=0 for i in ${(k)result}; do - lookup="`lookup_email ${i}`" - [[ "$lookup" = "" ]] || { + lookup_email ${i} + [[ $? = 0 ]] && { _known=$(( $_known + 1 )) } done act "addresses known: $_known"