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 977d918b491c7ab88a66e9d668af57a5ee3313ff
parent ba223a21786bc4c526bc711616f29d1056edd293
Author: Jaromil <jaromil@dyne.org>
Date:   Wed, 31 Dec 2014 18:59:03 +0100

fixes to deliver with notmuch and search with alot

Diffstat:
Mbuild/build-gnu.sh | 38+++++++++-----------------------------
Msrc/jaro | 11+++++++++--
Msrc/zlibs/email | 2+-
Msrc/zlibs/filters | 57+++++++++++++++++++++++++++++++--------------------------
Msrc/zlibs/maildirs | 24++++++------------------
Msrc/zlibs/search | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
6 files changed, 173 insertions(+), 96 deletions(-)

diff --git a/build/build-gnu.sh b/build/build-gnu.sh @@ -27,10 +27,11 @@ mkdir -p build/gnu which fetchmail >/dev/null || deps+=(fetchmail) which msmtp >/dev/null || deps+=(msmtp) which mutt >/dev/null || deps+=(mutt) - which mairix >/dev/null || deps+=(mairix) which pinentry >/dev/null || deps+=(pinentry-curses) which abook >/dev/null || deps+=(abook) which wipe >/dev/null || deps+=(wipe) + which notmuch >/dev/null || deps+=(notmuch) + which alot >/dev/null || deps+=(alot) print "Checking build dependencies" which gcc >/dev/null || deps+=(gcc) @@ -64,6 +65,8 @@ mkdir -p build/gnu which fetchmail || sudo yum install fetchmail which wipe || sudo yum install wipe which abook || sudo yum install abook + which notmuch || sudo yum install notmuch + which alot || sudo yum install alot print "Checking build dependencies" which gcc || sudo yum install gcc @@ -87,7 +90,7 @@ mkdir -p build/gnu { test "$target" = "fetchaddr" } || { test "$target" = "all" } && { pushd src - print -n "Compiling the address parser... " + print -n "Compiling the address parser (RFC2047) ... " ${=cc} -c helpers.c ${=cc} -c rfc2047.c ${=cc} -c rfc822_mutt.c; @@ -98,17 +101,9 @@ mkdir -p build/gnu print OK } -{ test "$target" = "dfasyn" } || { - test "$target" = "all" } && { - print "Compiling the generator for deterministic finite state automata... " - pushd src/mairix/dfasyn - make - popd -} - { test "$target" = "parsedate" } || { test "$target" = "all" } && { - print "Compiling the minimalistic RFC822 date re-formatter..." + print -n "Compiling the date parsers (RFC822) ... " pushd src ${=cc} -o parsedate parsedate.c popd @@ -116,25 +111,9 @@ mkdir -p build/gnu print OK } -{ test "$target" = "fetchdate" } || { - test "$target" = "all" } && { - print "Compiling the date parser... " - pushd src - # then the C files made by dfasyn - ./mairix/dfasyn/dfasyn -o nvpscan.c -ho nvpscan.h -r nvpscan.report -u nvp.nfa - # then the utilities - ${=cc} -c rfc822_mairix.c - ${=cc} -c nvp.c nvpscan.c - ${=cc} -I . -c fetchdate.c - ${=cc} -o fetchdate rfc822_mairix.o nvpscan.o nvp.o fetchdate.o - popd - cp src/fetchdate build/gnu/ - print OK -} - { test "$target" = "dotlock" } || { test "$target" = "all" } && { - print "Compiling the file dotlock... " + print -n "Compiling the file dotlock... " pushd src ${=cc} -c dotlock.c -I . -DDL_STANDALONE ${=cc} -o dotlock dotlock.o @@ -146,12 +125,13 @@ mkdir -p build/gnu { test "$target" = "gnome-keyring" } || { test "$target" = "all" } && { - print "Compiling gnome-keyring" + print -n "Compiling gnome-keyring... " pushd src/gnome-keyring ${=cc} jaro-gnome-keyring.c -o jaro-gnome-keyring \ `pkg-config --cflags --libs glib-2.0 gnome-keyring-1` popd cp src/gnome-keyring/jaro-gnome-keyring build/gnu/ + print OK } # build mixmaster only if specified diff --git a/src/jaro b/src/jaro @@ -537,6 +537,7 @@ main() subcommands_opts[index]="" subcommands_opts[search]="" + subcommands_opts[notmuch]="" subcommands_opts[learn]="" subcommands_opts[forget]="" @@ -548,6 +549,7 @@ main() subcommands_opts[export]="" subcommands_opts[abook]="" + subcommands_opts[vim]="" subcommands_opts[edit]="" subcommands_opts[preview]="" @@ -692,9 +694,14 @@ main() help) CLEANEXIT=0; usage ;; - index) CLEANEXIT=0; nm_index ${PARAM} ;; + index) CLEANEXIT=0; nm_index ;; search) CLEANEXIT=0; search ${PARAM} ;; + notmuch) CLEANEXIT=0; + notice "Command: notmuch ${PARAM}" + nm ${PARAM} + ;; + stat) CLEANEXIT=0; stats ${PARAM} ;; complete) CLEANEXIT=0; complete ${PARAM} ;; @@ -717,7 +724,7 @@ main() abook) edit_abook ${PARAM} ;; - edit) CLEANEXIT=0; edit_file ${PARAM} ;; + edit|vim) CLEANEXIT=0; edit_file ${PARAM} ;; open) CLEANEXIT=0; open_folder ${PARAM} ;; preview) CLEANEXIT=0; preview_file ${PARAM} ;; diff --git a/src/zlibs/email b/src/zlibs/email @@ -169,7 +169,7 @@ fetch() { } # updates the notmuch configuration - nm_setup + nm_setup unread notice "Fetching email for account ${account}" diff --git a/src/zlibs/filters b/src/zlibs/filters @@ -80,8 +80,8 @@ init_inbox() { # the cache consists of array and maps declarations for zsh update_filters() { { test -r "$MAILDIRS/Filters.txt" } || { - error "Filters not found in $MAILDIRS/Filters.txt" - return 1 } + error "Filters not found in $MAILDIRS/Filters.txt" + return 1 } notice "Updating filters..." @@ -106,51 +106,56 @@ EOF # insert filter rules in the cache for f in ${(f)ffilters}; do - header="${f[(ws:;:)1]}" - regexp="${f[(ws:;:)2]}" - action="${f[(ws:;:)3]}" - destination="${f[(ws:;:)4]}" - case $header in - to) - cat <<EOF >> "$ff" + header="${f[(ws:;:)1]}" + regexp="${f[(ws:;:)2]}" + action="${f[(ws:;:)3]}" + destination="${f[(ws:;:)4]}" + case $header in + to) + cat <<EOF >> "$ff" filter_to+=("${regexp}" "${destination}") EOF - func "from: <${regexp}> -> ${destination}" - maildirmake $MAILDIRS/$destination - ;; - from) - cat <<EOF >> "$ff" + func "from: <${regexp}> -> ${destination}" + maildirmake $MAILDIRS/$destination + ;; + from) + cat <<EOF >> "$ff" filter_from+=("${regexp}" "${destination}") EOF - func "to: <${regexp}> -> ${destination}" - maildirmake $MAILDIRS/$destination - ;; - - *) - error "invalid filter: $f" - ;; - esac + func "to: <${regexp}> -> ${destination}" + maildirmake $MAILDIRS/$destination + ;; + + *) + error "invalid filter: $f" + ;; + esac done + # create the notmuch database if not present + notice "Indexing emails in the search database" + nm_index + nm compact + # compile the list of own addresses and aliases for i in `awk ' /^#/ { next } /^$/ { next } /^email/ { print $2 }' \ "$MAILDIRS"/Accounts/*`; do - cat <<EOF >> "$ff" + cat <<EOF >> "$ff" filter_own+=($i) EOF done { test -r $MAILDIRS/Aliases.txt } && { - for i in `awk ' + for i in `awk ' /^#/ { next } /^$/ { next } { print $1 }' "$MAILDIRS/Aliases.txt"`; do - cat <<EOF >> "$ff" + cat <<EOF >> "$ff" filter_own+=($i) EOF - done + done } unlock "$ff" diff --git a/src/zlibs/maildirs b/src/zlibs/maildirs @@ -215,10 +215,6 @@ deliver() { dest="incoming" else dest="$1" - { test -d "$MAILDIRS/$dest" } || { - error "delivery destination path invalid: $1" - return 1 - } fi # create destination maildir if not existing @@ -234,21 +230,14 @@ deliver() { return 1 } - [[ $DEBUG = 0 ]] || { - func "Delivery successful, log: $MAILDIRS/logs/jaro-deliver.log" - awk ' -BEGIN { print "Delivery to maildir: '"$1"'" } -{ print $0 } -/^$/ { exit } -' "$MAILDIRS/$dest/new/$base" >> "$MAILDIRS/logs/jaro-deliver.log" - } + func "deliver to $dest" # destinations excluded from notmuch indexing [[ "$dest" = "outbox" ]] \ || [[ "$dest" =~ "^zz." ]] \ || [[ "$dest" = "incoming" ]] && { - - base="`hostname`_jaro_`date +%Y-%m-%d_%H-%M-%S`_$RANDOM" + + base="`hostname`_jaro_`date +%Y-%m-%d_%H-%M-%S`_$RANDOM" cat > "$MAILDIRS/$dest/new/$base" [[ $? = 0 ]] || { @@ -261,14 +250,13 @@ BEGIN { print "Delivery to maildir: '"$1"'" } ######### # notmuch indexing from here - NOTMUCH_CONFIG="$MAILDIRS"/cache/notmuch/rc # tag +inbox [[ "$dest" = "known" ]] \ || [[ "$dest" = "priv" ]] \ || [[ "$dest" = "sent" ]] && { - cat | notmuch insert --folder="$dest" +inbox + cat | nm insert --folder="$dest" +inbox [[ $? = 0 ]] || { error "Could not write email file into maildir $dest using notmuch insert." func "Returning error to caller." @@ -281,7 +269,7 @@ BEGIN { print "Delivery to maildir: '"$1"'" } [[ "$dest" = "unsorted" ]] \ || [[ "$dest" =~ "^lists." ]] && { - cat | notmuch insert --folder="$dest" +unsorted + cat | nm insert --folder="$dest" +unsorted [[ $? = 0 ]] || { error "Could not write email file into maildir $dest using notmuch insert." func "Returning error to caller." @@ -291,7 +279,7 @@ BEGIN { print "Delivery to maildir: '"$1"'" } } # anything else +filters - cat | notmuch insert --folder="$dest" +filters + cat | nm insert --folder="$dest" +filters [[ $? = 0 ]] || { error "Could not write email file into maildir $dest using notmuch insert." func "Returning error to caller." diff --git a/src/zlibs/search b/src/zlibs/search @@ -24,11 +24,24 @@ ## Search into maildirs # using notmuch -nm_dir="$MAILDIRS"/cache/notmuch + +# internal use +nm() { + [[ -r "$MAILDIRS"/cache/notmuch/rc ]] || nm_setup + notmuch --config="$MAILDIRS"/cache/notmuch/rc ${@} \ + 2>&1 | grep -v '^Note: Ignoring non-mail' + +} nm_setup() { - mkdir -p "$nm_dir" + nm_dir="$MAILDIRS"/cache/notmuch + mkdir -p $nm_dir + + # setup the default tags for all new messages + deftags=${1} + act "notmuch setup $deftags" + # read if there are other email aliases configured [[ -r "$MAILDIRS"/Aliases.txt ]] && { other_email="other_email" @@ -45,26 +58,43 @@ nm_setup() { [database] path=$MAILDIRS -[user] -name=$name -primary_email=$email -$other_email - [new] -tags=unread -ignore=zz.;log;cache;Accounts;Groups;.mutt;webnomad;.abook;.txt;.pdf;.html;.png;.js +tags=$deftags +ignore=tmp;cache;logs;outbox;incoming;zz.social;zz.bounces;zz.blacklist;zz.spam;Accounts;Groups;.mutt;webnomad;Aliases.txt;Filters;Identity.txt;Keyring;Manual.pdf;Mutt.txt;whitelist.abook;blacklist.abook;Applications.txt;Filters.sieve;Filters.txt [maildir] synchronize_flags=true EOF + [[ "$name" = "" ]] || { + cat <<EOF >> "$nm_dir"/rc + +[user] +name=$name +primary_email=$email +$other_email +EOF + } } nm_index() { + # init the environment read_account + notice "Indexing all mail archive" + act "please wait, this may take a while..." nm_setup - func "notmuch --config=${nm_dir}/rc new" - - notmuch --config="${nm_dir}/rc" new + nm new + act "tagging inbox" + nm tag +inbox +priv folder:known + nm tag +inbox +priv folder:sent + act "tagging priv" + nm tag +priv folder:priv + act "tagging unsorted" + nm tag +unsorted folder:unsorted + nm tag +unsorted folder:unsorted.ml + nm tag +unsorted folder:lists. + act "compressing database" + nm compact + notice "Indexing completed" } nm_search() { @@ -72,18 +102,90 @@ nm_search() { nm_setup notice "Searching emails for: $=PARAM" - local search_results + + # read if there are other email aliases configured + [[ -r "$MAILDIRS"/Aliases.txt ]] && { + other_email="aliases" + _aliases=`cat "$MAILDIRS"/Aliases.txt` + _sep=" = " + for i in ${(f)_aliases}; do + other_email+="${_sep}${i}" + _sep="," + done + } + + # setup alot to show results + mkdir -p "$MAILDIRS"/cache/alot + cat <<EOF > "$MAILDIRS"/cache/alot/rc + +editor_cmd = jaro edit +edit_headers_whitelist = From,To,Cc,Subject +displayed_headers = From,To,Cc,Bcc,Subject,User-Agent +auto_remove_unread = True +honor_followup_to = True +hooksfile = $MAILDIRS/cache/alot/hooks.py +initial_command = search tag:inbox +prefer_plaintext = True +tabwidth = 4 +user_agent = Jaro Mail <http://jaromail.dyne.org> + +[bindings] + i = search tag:inbox + p = search tag:priv + u = search tag:unsorted + / = prompt 'search ' + l = prompt 'search ' + +[accounts] + [[$account]] + realname = $name + address = $email + sendmail_command = jaro queue + sent_box = maildir:///$MAILDIRS/sent + $other_email + [[[abook]]] + type = shellcommand + command = jaro -q search addr + regexp = (?P<name>.+)\s*<(?P<email>.*.+?@.+?)> + +[tags] + [[inbox]] + translated = ★ + [[priv]] + translated = ⚡ + [[attachment]] + translated = ⚓ + [[unsorted]] + translated = ? + [[flagged]] + translated = ⚑ + normal = '','','light red','','light red','' + focus = '','','light red','','light red','' + [[unread]] + translated = ☉ + [[replied]] + translated = ⏎ + [[encrypted]] + translated = ⚷ + [[signed]] + translated = ✍ +EOF + + alot -c "$MAILDIRS"/cache/alot/rc -n "$MAILDIRS"/cache/notmuch/rc \ + search ${=PARAM} + return $? + func "notmuch --config=${nm_dir}/rc search --output=files ${=PARAM}" # launch the search with notmuch - search_results=`notmuch --config="${nm_dir}/rc" search --output=files ${=PARAM}` + search_results=`nm search --output=files ${=PARAM}` act "`print ${search_results} | wc -l` results found" [[ $? = 0 ]] || { error "notmuch search failed with an error" return 1 } # populate the maildir with results - _resdir="${nm_dir}/results" + _resdir="$MAILDIRS"/cache/notmuch/results func "notmuch results in $_resdir" rm -rf "$_resdir" act "populating a maildir with results" @@ -125,11 +227,6 @@ search() { # run search across emails nm_search ${=PARAM} - - # open the results maildir - ${=mutt} -F "$MUTTDIR"/rc ${=muttflags} \ - -f "$MAILDIRS"/cache/notmuch/results - } backup() {