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 c8fbefbb5a92e2285621a24431582fec04686fc5
parent 3dcefa72ffb432c96f6af3fcee59ccc361ccaeca
Author: Jaromil <jaromil@dyne.org>
Date:   Wed,  7 Jan 2015 20:37:40 +0100

new tmpfile system and endgame() plus cleanups to addressbook import code

Diffstat:
Mdoc/Accounts/default.txt | 2+-
Msrc/jaro | 196++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/zlibs/addressbook | 281+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/zlibs/email | 157+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/zlibs/filters | 22++++++++++++----------
Msrc/zlibs/helpers | 2+-
Msrc/zlibs/maildirs | 84+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/zlibs/publish | 57+++++++++++++++++++++++++++++----------------------------
8 files changed, 389 insertions(+), 412 deletions(-)

diff --git a/doc/Accounts/default.txt b/doc/Accounts/default.txt @@ -34,7 +34,7 @@ auth plain # or kerberos, etc cert ignore # Transport protocol: ssl, tls or plain -transport ssl +transport tls # Options when fetching diff --git a/src/jaro b/src/jaro @@ -34,7 +34,6 @@ DEBUG=0 DRYRUN=0 CLEANEXIT=1 CALLMUTT=1 -TMPRAM=0 # global variables for binaries called typeset -h rm mkdir mutt @@ -88,6 +87,71 @@ typeset -h muttflags autoload colors; colors +# temporary directory +typeset TMPPREFIX=${TMPPREFIX:-/tmp} +typeset -H JAROTMP # Filename of secure temp just created (see _tmp_create()) +typeset -aH JAROTMPFILES # Keep track of temporary files + +# Provide a random filename in shared memory +_tmp_create() { + [[ -d "$TMPPREFIX" ]] || { + # we create the tempdir with the sticky bit on + sudo mkdir -m 1777 "$TMPPREFIX" + [[ $? == 0 ]] || _failure "Fatal error creating the temporary directory: ::1 temp dir::" "$TMPPREFIX" + } + + # We're going to add one more $RANDOM for each time someone complain + # about this being too weak of a random. + tfile="${TMPPREFIX}/$RANDOM$RANDOM$RANDOM$RANDOM" # Temporary file + umask 066 + [[ $? == 0 ]] || { + error "Fatal error setting the permission umask for temporary files" + return 1 + } + + [[ -r "$tfile" ]] && { + error "Someone is messing up with us trying to hijack temporary files." + return 1 + } + + touch "$tfile" + [[ $? == 0 ]] || { + error "Fatal error creating a temporary file: ::1 temp file::" "$tfile" + return 1 + } + + [[ $? == 0 ]] || { + error "Fatal error setting ownership on temporary file: ::1 temp file::" "$tfile" + return 1 + } + + func "Created tempfile: $tfile" + JAROTMP="$tfile" + JAROTMPFILES+=("$tfile") + + return 0 +} + +# Cleanup anything sensitive before exiting. +_endgame() { + # Clear temporary files + for f in $JAROTMPFILES; do + func "endgame() cleaning tempfile $f" + rm -f "$f" + done + unset JAROTMPFILES +} +# Trap functions for the _endgame event +TRAPINT() { _endgame INT } +TRAPEXIT() { _endgame EXIT } +TRAPHUP() { _endgame HUP } +TRAPQUIT() { _endgame QUIT } +TRAPABRT() { _endgame ABORT } +TRAPKILL() { _endgame KILL } +TRAPPIPE() { _endgame PIPE } +TRAPTERM() { _endgame TERM } +TRAPSTOP() { _endgame STOP } + # standard output message routines # it's always useful to wrap them, in case we change behaviour later notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[default] $1" >&2; fi } @@ -197,48 +261,24 @@ fi ACCOUNTS="$MAILDIRS/Accounts" KEYRING="$MAILDIRS/Keyring" -# temporary directory -TMPDIR="$MAILDIRS/tmp/jaromil.$USER" -case $OS in - GNU) - touch /dev/shm/test.jaromail.$USER > /dev/null - { test $? = 0 } && { - # we can use volatile ram - rm /dev/shm/test.jaromail.$USER - TMPDIR=/dev/shm/tmp.jaromail.$USER - TMPRAM=1 - } - - # backward compatibility tests for old paths in JaroMail <1.3 - { test -d $WORKDIR/Accounts } && { test ! -d $ACCOUNTS } && { - act "Updating accounts location: $ACCOUNTS" - cp -ra $WORKDIR/Accounts $ACCOUNTS } - - { test -r "$WORKDIR/keyring" } && { test ! -r "$KEYRING" } && { - act "Updating keyring location: $KEYRING" - cp $WORKDIR/keyring "$KEYRING" } +case $OS in + GNU) + # backward compatibility tests for old paths in JaroMail <1.3 + { test -d $WORKDIR/Accounts } && { test ! -d $ACCOUNTS } && { + act "Updating accounts location: $ACCOUNTS" + cp -ra $WORKDIR/Accounts $ACCOUNTS + } + + { test -r "$WORKDIR/keyring" } && { test ! -r "$KEYRING" } && { + act "Updating keyring location: $KEYRING" + cp $WORKDIR/keyring "$KEYRING" + } ;; MAC) - mount | grep 'JaroTmp' > /dev/null - { test $? = 0 } && { - # our RAM temp directory is active - TMPDIR=/Volumes/JaroTmp/jaromail.$USER - TMPRAM=1 - } ;; esac -# use the TMP in RAM if possible, for acceleration -{ test $TMPRAM = 1 } && { - act "Using temporary directory in volatile RAM" } - -# make sure we have a temp dir -${=mkdir} "$TMPDIR" -{ test $? != 0 } && { - error "Cannot create temporary directory: $TMPDIR" - return 1 } - hostname=$(hostname) # gather the current hostname # make sure we have a directory for account configurations @@ -275,65 +315,6 @@ pidof gnome-keyring-daemon > /dev/null && { act "using gnome-keyring to store secrets" GNOMEKEY=1 } - -cleanexit() { - func "Clean exit procedures" - - # security check - { test "$TMPDIR" = "" } && { - error "Temporary directory not defined" - act "skipping cleanup, this might leave private traces." - return 1 - } - - # first delete dirs - tmpdirs=`${=find} "$TMPDIR" -maxdepth 1 -type d` - for d in ${(f)tmpdirs}; do - { test "$d" = "$TMPDIR" } || { - func "deleting dir: $d" - ${=rm} -r "${d}" } - done - - # then locks, with a warning - llist=`${=find} "$TMPDIR" -maxdepth 1 -type f -name '*.lock'` - for l in ${(f)llist}; do - lname=`basename ${(s:.lock:)l}` - func "cleaning lock for $lname" - - # skip if in course of unlink - parallel operation, see unlink() - pidfile="${TMPDIR}/$lname.pid" - if [ -r ${pidfile} ]; then - pid=`cat $pidfile` - func "forced removal of lock left by pid $pid: $lname" - rm -f ${pidfile} - else - func "forced removal of lock left by unknown pid: $lname" - fi - rm -f "${TMPDIR}/${lname}.lock" - - # remove the actual file - if [ -r "${TMPDIR}/$lname" ]; then - func "deleting temp file: ${TMPDIR}/$lname" - ${=rm} "${TMPDIR}/$lname" - else func "empty lock: file was already removed"; fi - - done - - -# { test $TMPRAM = 1 } && { rmdir $TMPDIR } -} -# make sure tmp is wiped from sensitive data in case of sigINT -TRAPINT() { - error "Caught signal, aborting operations." - - { test $CLEANEXIT = 1 } && { - func "Forcing removal of locks" - cleanexit & } - - if [ "$DEBUG" = "1" ]; then return 1 - else exit 1; fi -} - # binary programs recognition check_bin() { @@ -388,7 +369,7 @@ check_bin() { fi func "Mutt binary: $mutt" # make sure there is always a muttpass file even if empty - touch $TMPDIR/muttpass + touch $MAILDIR/tmp/muttpass return 0 } @@ -713,7 +694,7 @@ main() learn) CLEANEXIT=0; learn ${PARAM} ;; forget) CLEANEXIT=0; forget ${PARAM} ;; - import) import_addressbook "${PARAM}" ;; + import) import ${PARAM} ;; "export") case "$PARAM" in abook) @@ -728,9 +709,9 @@ main() abook) edit_abook ${PARAM} ;; - edit|vim) CLEANEXIT=0; edit_file ${PARAM} ;; - open) CLEANEXIT=0; open_folder ${PARAM} ;; - preview) CLEANEXIT=0; preview_file ${PARAM} ;; + edit|vim) CLEANEXIT=0; edit_file ${PARAM} ;; + open) CLEANEXIT=0; open_folder ${PARAM} ;; + preview) CLEANEXIT=0; preview_file ${PARAM} ;; backup) backup ${PARAM} ;; rmdupes) rmdupes ${PARAM} ;; @@ -801,18 +782,18 @@ main() ;; list|extract) - extract ${PARAM} - exitcode=$? - ;; + extract ${PARAM} + exitcode=$? + ;; *) # unknown command, pass it to autostart func "unknown command, remote check" autostart ${PARAM} exitcode=$? { $exitcode != 0 } && { - error "command \"$subcommand\" not recognized" - act "try -h for help" - CLEANEXIT=0 + error "command \"$subcommand\" not recognized" + act "try -h for help" + CLEANEXIT=0 } ;; esac @@ -822,5 +803,4 @@ main() check_bin main $@ -{ test "$CLEANEXIT" = "1" } && { cleanexit & } return $exitcode diff --git a/src/zlibs/addressbook b/src/zlibs/addressbook @@ -282,6 +282,90 @@ extract_maildir() { return 0 } + +# import emails from VCard into abook +# checks if the emails are already known +import_vcard() { + act "import VCard from file: $1" + + [[ -r "$1" ]] || { + error "File not found: $1" + return 1 + } + + vcard="$1" + head -n1 $vcard | grep '^BEGIN:VCARD' > /dev/null + + [[ $? = 0 ]] || { + error "File to import is not a VCard: $vcard" + return 1 + } + + notice "Import VCard in addressbook: ${vcard}" + + # parse the vcard and print a simple name and email list + # each value on a single line, entry tuples followed by a # + # we skip entries that don't have an email + addresses=`cat ${vcard} | awk ' +BEGIN { newcard=0; c=0; name=""; email=""; } +/^BEGIN:VCARD/ { newcard=1 } +/^FN:/ { if(newcard = 1) name=$0 } +/^EMAIL/ { if(newcard = 1) email=$0 } +/^END:VCARD/ { + if(newcard = 1) { + newcard=0 + if(email != "") { + c+=1 + print name + print email + print "# " c + } + email="" + next + } +} +' | cut -d: -f2` + + # now parse the temporary list of name and emails + # made of name, email and a hash for each, newline separated + + # ${=rm} $tmp + + lock $ADDRESSBOOK + + newa=1; _name=""; _email="" + for a in ${(f)addresses}; do + { test "${a[1]}" = "#" } && { + newa=1; # its the end of the entry + + # handle lines with multiple emails in vcard + # TODO: generate Groups/${_name} from this + for ee in ${=_email}; do + # check if we have this email already + _e=`print ${ee} | extract_emails` + func "lookup_email: ${_e}" + lookup_email "${_e}" + + [[ $? = 0 ]] || { + insert_address "${_e}" "${_name}" + act "${a} ${_name} <${_e}>" + } + done + + continue } + if [[ $newa -eq 1 ]]; then + # (V) makes special chars visible, we need to remove them.. + _name="${(V)a[(ws:^:)1]}"; newa=0; continue + elif [[ $newa -eq 0 ]]; then + _email="${(V)a[(ws:^:)1]}" + fi + done + + unlock $ADDRESSBOOK + + notice "Done importing addresses" +} + # extract all entries in addressbook or all addresses in a pgp keyring # or all signatures on a pgp key (even without importing it) extract() { @@ -289,36 +373,41 @@ extract() { # without arguments just list all entries in the active list # default is whitelist - [[ "$1" = "" ]] && { - notice "Extracting all addresses in whitelist" + + arg=${PARAM[1]} + # no arg means print all entries from adressbook + [[ "$arg" = "" ]] && { + notice "Extracting all addresses in $list" awk -F'=' ' /^name/ { printf("%s ",$2) } /^email/ { printf("<%s>\n",$2) } -' "$MAILDIRS"/$list.abook +' $ADDRESSBOOK return 0 } - [[ -r "$1" ]] && { # first arg is a file - - # a map to eliminate duplicates - typeset -AU result + [[ -r "$arg" ]] && { + # if first arg is a file, could be a maildir, a gpg keyring, + # a gpg pubkey or a vcard # if first arg is a directory then extract from maildir - [[ -d "$1" ]] && { + [[ -d "$arg" ]] && { notice "Extracting $2 addresses from maildir $1" extract_maildir "$1" "$2" return $? } func "testing argument with file magic" - _magic=`file "$1"` + _magic=`file "$arg"` + + # a map to eliminate duplicates + typeset -AU result ######### GPG # first arg is a GnuPG key ring [[ "$_magic" =~ "GPG key public ring" ]] && { - notice "Extracting addresses found in GPG keyring: $1" + notice "Extracting addresses found in GPG keyring: $arg" _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` @@ -347,10 +436,10 @@ extract() { # first arg is a GnuPG public key [[ "$_magic" =~ "PGP public key" ]] && { - notice "Extracting addresses from sigs on GPG key $1" + notice "Extracting addresses from sigs on GPG key $arg" _gpg="gpg --no-default-keyring --keyring $MAILDIRS/cache/pubkey.gpg --batch --with-colons" rm -f $MAILDIRS/cache/pubkey.gpg - ${=_gpg} --import "$1" + ${=_gpg} --import "$arg" # first make sure all unknown keys are imported _addrs=`${=_gpg} --list-sigs | awk -F: '{print $5 " " $10}'` for i in ${(f)_addrs}; do @@ -395,135 +484,69 @@ extract() { } -# import an addressbook, autodetect its type -import_addressbook() { - [[ "$1" = "" ]] || { - notice "Import addressbook from vCard: $1" - import_vcard "$1" - return $? - } +# import address lists from stdin +import() { + + arg=${PARAM[1]} + # remove options and trim + arg=`trim ${arg//-?/}` # a map to eliminate duplicates typeset -AU result - # stdin - notice "Importing addressbook from stdin list of addresses" - _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 -} - - - -# import emails from VCard into abook -# checks if the emails are already known -import_vcard() { - act "import VCard from file: $1" - - [[ -r "$1" ]] || { - error "File not found: $1" - return 1 - } + [[ "$arg" = "" ]] || { + notice "Import address list from stdin into addressbook" + _stdin=`cat` + print - $_stdin + _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 ]] || { + func "not an email: $_e" + continue + } - vcard="$1" - head -n1 $vcard | grep '^BEGIN:VCARD' > /dev/null + # check if the email is not a duplicate + [[ "${result[$_e]}" = "" ]] || { + func "duplicate email: $_e" + continue + } + + _n="${_parsed[(ws:,:)2]}" + result+=("$_e" "$_n") + + # check if the email is not already known + lookup_email "$_e" + [[ $? = 0 ]] && { + func "email already known: $_e" + continue + } - [[ $? = 0 ]] || { - error "File to import is not a VCard: $vcard" - return 1 + act "import new entry: $_n <$_e>" + [[ $DRYRUN = 0 ]] && { + insert_address "$_e" "$_n" + } + _new=$(( $_new + 1 )) + done + notice "Valid unique entries parsed: ${#result}" + act "new addresses found: ${_new}" + return $? } - notice "Import VCard in addressbook: ${vcard}" - # parse the vcard and print a simple name and email list - # each value on a single line, entry tuples followed by a # - # we skip entries that don't have an email - addresses=`cat ${vcard} | awk ' -BEGIN { newcard=0; c=0; name=""; email=""; } -/^BEGIN:VCARD/ { newcard=1 } -/^FN:/ { if(newcard = 1) name=$0 } -/^EMAIL/ { if(newcard = 1) email=$0 } -/^END:VCARD/ { - if(newcard = 1) { - newcard=0 - if(email != "") { - c+=1 - print name - print email - print "# " c - } - email="" - next - } + # stdin + notice "Importing addressbook from stdin list of addresses" + return 0 } -' | cut -d: -f2` - # now parse the temporary list of name and emails - # made of name, email and a hash for each, newline separated - # ${=rm} $tmp - - lock $ADDRESSBOOK - - newa=1; _name=""; _email="" - for a in ${(f)addresses}; do - { test "${a[1]}" = "#" } && { - newa=1; # its the end of the entry - - # handle lines with multiple emails in vcard - # TODO: generate Groups/${_name} from this - for ee in ${=_email}; do - # check if we have this email already - _e=`print ${ee} | extract_emails` - func "lookup_email: ${_e}" - lookup_email "${_e}" - - [[ $? = 0 ]] || { - insert_address "${_e}" "${_name}" - act "${a} ${_name} <${_e}>" - } - done - - continue } - if [[ $newa -eq 1 ]]; then - # (V) makes special chars visible, we need to remove them.. - _name="${(V)a[(ws:^:)1]}"; newa=0; continue - elif [[ $newa -eq 0 ]]; then - _email="${(V)a[(ws:^:)1]}" - fi - done - - unlock $ADDRESSBOOK - - notice "Done importing addresses" -} # export old addressbook format to abook export_abook() { diff --git a/src/zlibs/email b/src/zlibs/email @@ -38,104 +38,73 @@ queue() { # set it ready for saving in outbux queue_body="$base" + _tmp_create + tmpqueue="$JAROTMP" + # pre-processing of the email headers - cat | awk ' + awk ' /User-Agent:/ { print "User-Agent: Jaro Mail <http://jaromail.dyne.org>"; next } { print $0 } -' > "$TMPDIR/${queue_body}.mail" +' > "$tmpqueue" maildirmake "$MAILDIRS/outbox" - { test $? = 0 } || { - act "updating outbox format to Maildir" - # silently migrate the outbox from the old format to the new - tmpp=(`find "$MAILDIRS/outbox" -type f`) - if [ ${#tmpp} = 0 ]; then - # the old format outbox is just empty - rmdir "$MAILDIRS/outbox" - maildirmake "$MAILDIRS/outbox" - else - # there are some mails to be sent in the old format outbox - # preserve them while migrating to the new format - # this code is less interesting, since it handles an old - # format in JaroMail - tmppp="$TMPDIR/jaro-outbox-migration-$RANDOM" - mkdir -p $tmppp - mv "$MAILDIRS/outbox/*" $tmppp/ - rmdir "$MAILDIRS/outbox" - maildirmake "$MAILDIRS/outbox" - # here we devince two useful arrays: - # bodies: the list of mail bodies to send - # corresponding to files with same name and extension .msmtp - bodies=(`find "$tmppp" -type f -name '*.mail'`) - for i in ${bodies}; do - cat "$i" | deliver outbox - { test $? = 0 } && { rm "$i" } - done - rmdir $tmppp - fi - } ${=mkdir} "$MAILDIRS/outbox/send" - lock "$MAILDIRS/outbox" # check if recipients are a Group if [[ "${=queue_to}" =~ '@jaromail.group' ]]; then - - - groupfile="`print ${=queue_to}|cut -d@ -f1`" - act "email recipients are in group ${groupfile}" - - { test -r "$MAILDIRS/Groups/$groupfile" } || { - maildirmake "$MAILDIRS/postponed" - mv "${TMPDIR}/${queue_body}.mail" "$MAILDIRS/postponed/new" - unlock "$MAILDIRS/outbox" - error "Group not found: $groupfile" - return 1 } - - recipients=`cat "$MAILDIRS/Groups/$groupfile" | grep -v '^#'` - groupnum=`print ${recipients} | wc -l` - groupmode=`head -n1 "$MAILDIRS/Groups/$groupfile" | awk '/^#mode/ { print $2 } { next }'` - { test "$groupmode" = "" } && { groupmode="individual" } - act "$groupnum recipients in total, sending mode $groupmode" - - - case $groupmode in - - # individual group mode hides other recipients and send - # multiple mail envelopes with each single recipient in To: - individual) - for i in ${(f)recipients}; do - ig=${base}-${RANDOM} - cat "${TMPDIR}/${queue_body}.mail" | \ - awk '/^To:/ { print "'"To: $i"'"; next } { print $0 }' \ - > "${MAILDIRS}/outbox/new/${ig}.mail" - done - ;; - - # carboncopy group mode sends a single envelope where all - # recipients can see and reply to each other - carboncopy|cc) - cc="" - for i in ${(f)recipients}; do - if [ "$cc" = "" ]; then cc="$i" - else cc+=", $i"; fi - done - ig=${base}-${RANDOM} - cat "${TMPDIR}/${queue_body}.mail" | \ - awk '/^To:/ { print "'"To: $cc"'"; print "'"Reply-To: $cc"'"; next } + + groupfile="`print ${=queue_to}|cut -d@ -f1`" + act "email recipients are in group ${groupfile}" + + [[ -r "$MAILDIRS/Groups/$groupfile" ]] || { + maildirmake "$MAILDIRS/postponed" + mv "$tmpqueue" "$MAILDIRS/postponed/new" + unlock "$MAILDIRS/outbox" + error "Group not found: $groupfile" + return 1 } + + recipients=`cat "$MAILDIRS/Groups/$groupfile" | grep -v '^#'` + groupnum=`print ${recipients} | wc -l` + groupmode=`head -n1 "$MAILDIRS/Groups/$groupfile" | awk '/^#mode/ { print $2 } { next }'` + [[ "$groupmode" = "" ]] && { groupmode="individual" } + act "$groupnum recipients in total, sending mode $groupmode" + + case $groupmode in + + # individual group mode hides other recipients and send + # multiple mail envelopes with each single recipient in To: + individual) + for i in ${(f)recipients}; do + ig=${base}-${RANDOM} + cat "$tmpqueue" | \ + awk '/^To:/ { print "'"To: $i"'"; next } { print $0 }' \ + > "${MAILDIRS}/outbox/new/${ig}.mail" + done + ;; + + # carboncopy group mode sends a single envelope where all + # recipients can see and reply to each other + carboncopy|cc) + cc="" + for i in ${(f)recipients}; do + if [ "$cc" = "" ]; then cc="$i" + else cc+=", $i"; fi + done + ig=${base}-${RANDOM} + cat "$tmpqueue" | \ + awk '/^To:/ { print "'"To: $cc"'"; print "'"Reply-To: $cc"'"; next } { print $0 }' \ > "${MAILDIRS}/outbox/new/${ig}.mail" - ;; - esac - + ;; + esac + else - # recipients are set in the email envelope - cat "$TMPDIR/$queue_body.mail" | deliver "outbox" + # recipients are set in the email envelope + cat "$tmpqueue" | deliver "outbox" fi - + unlock "$MAILDIRS/outbox" - { test -r "${TMPDIR}/${queue_body}.mail" } && { - ${=rm} "${TMPDIR}/${queue_body}.mail" } return 0 } @@ -338,7 +307,10 @@ send() { # check if this is an anonymous mail hdr "$qbody" | grep -i '^from: anon' > /dev/null if [[ $? = 0 ]]; then - anoncfg="${TMPDIR}/${smtp}.anon.$RANDOM" + + _tmp_create + anoncfg="$JAROTMP" + cat <<EOF > "$anoncfg" REMAIL n POOLSIZE 0 @@ -389,8 +361,6 @@ BEGIN { head=1 } func "mixmaster returns $res" done - ${=rm} $anoncfg - else # normal send with msmtp act "Sending out email" @@ -485,23 +455,26 @@ peek() { error "Error retrieving password for $login on $imap" unset password all; return 1 } - tmp="$TMPDIR/$imap.peek.$RANDOM" - newlock "$tmp" + + _tmp_create + tmp="$JAROTMP" cat <<EOF >> "$tmp" set imap_pass = "${password}" # set imap_peek = yes EOF unset password - print "source '$tmp'" > "$TMPDIR/muttpass" # when peeking don't mark unread messages as Old # and sort date received with no threading (latest up) - cat <<EOF >> "$TMPDIR/muttpass" + touch "$MAILDIRS/tmp/muttpass" + cat <<EOF >> "$MAILDIRS/tmp/muttpass" +source '$tmp' unset mark_old set sort=reverse-date-received EOF + # fork a process to delete the pass files after use (sleep 1; - cp /dev/null "$TMPDIR/muttpass" - unlink "$tmp" # secure delete in ram + cp /dev/null "$MAILDIRS/tmp/muttpass" + cp /dev/null "$tmp" # zero the tmp passfile ) & ${=mutt} -F $MUTTDIR/rc -f ${iproto}://${ilogin}@${imap}:${imap_port}/${folder} diff --git a/src/zlibs/filters b/src/zlibs/filters @@ -444,7 +444,7 @@ source '$WORKDIR/.mutt/formats' source '$WORKDIR/.mutt/keybindings' source '$WORKDIR/.mutt/colors' source '$MAILDIRS/Identity.txt' -source '$TMPDIR/muttpass' +source '$MAILDIRS/tmp/muttpass' EOF @@ -496,16 +496,16 @@ EOF eval `print $t | awk ' { print "_type=" $1 "; _app=" $2 ";" }'` cat <<EOF >> $MUTTDIR/mailcap -${_type}; a="${TMPDIR}" && f=\`basename %s\` && rm -f "\$a"/"\$f" && cp %s "\$a"/"\$f" && ${_app} "\$a"/"\$f" +${_type}; a="${MAILDIRS}/tmp" && f=\`basename %s\` && rm -f "\$a"/"\$f" && cp %s "\$a"/"\$f" && ${_app} "\$a"/"\$f" EOF done cat <<EOF >> $MUTTDIR/mailcap -application/*; a="${TMPDIR}" && f=\`basename %s\` && rm -f "\$a"/"\$f" && cp %s "\$a"/"\$f" && jaro preview "\$a"/"\$f" +application/*; a="${MAILDIRS}/tmp" && f=\`basename %s\` && rm -f "\$a"/"\$f" && cp %s "\$a"/"\$f" && jaro preview "\$a"/"\$f" EOF } # Applications.txt # this one is empty and sources files in temp when necessary - touch $TMPDIR/muttpass + touch $MAILDIRS/tmp/muttpass # just the header, will be completed later rm -f $MUTTDIR/mboxes @@ -514,15 +514,17 @@ EOF for f in `cat "$MAILDIRS/Filters.txt" | awk ' /^#/ {next} /^./ { print $4 }'`; do - # MUTT (generate mailboxes priority this parser) - print " \\" >> $MUTTDIR/mboxes - print -n " +${f} " >> $MUTTDIR/mboxes + # MUTT (generate mailboxes priority this parser) + print " \\" >> $MUTTDIR/mboxes + print -n " +${f} " >> $MUTTDIR/mboxes done print " \\" >> $MUTTDIR/mboxes print " +unsorted.ml +unsorted" >> $MUTTDIR/mboxes - - uniq $MUTTDIR/mboxes > $TMPDIR/mboxes - mv $TMPDIR/mboxes $MUTTDIR/mboxes + + _tmp_create + ttmp="$JAROTMP" + uniq $MUTTDIR/mboxes > $ttmp + mv $ttmp $MUTTDIR/mboxes } diff --git a/src/zlibs/helpers b/src/zlibs/helpers @@ -183,7 +183,7 @@ BEGIN { head=1 } # of course we have a preference for AutOrg, the editor from our suite # however the default is nano if nothing else is choosen. jarovim() { - vim -c 'set fo=tcrq' -c 'set tw=72' \ + vim -c 'set fo=tcrq' -c 'set tw=72' -c 'set paste' \ -c 'map <C-j> {gq}' -c 'imap <C-j> <esc>{gq}i' \ "${@}" return $? diff --git a/src/zlibs/maildirs b/src/zlibs/maildirs @@ -92,57 +92,55 @@ rmdupes() { ## special argument lastlog { test "$1" = "lastlog" } && { - lastdirs=(`maildirs_lastlog`) - act "Pruning duplicates across ${#lastdirs} destination maildirs:" - act "${lastdirs}" - # recursion here - rmdupes ${=lastdirs} - notice "Done pruning" - # all the prioritization above is so that duplicates are spotted - # across different maildirs and deleted from the filtered source - return 0 + lastdirs=(`maildirs_lastlog`) + act "Pruning duplicates across ${#lastdirs} destination maildirs:" + act "${lastdirs}" + # recursion here + rmdupes ${=lastdirs} + notice "Done pruning" + # all the prioritization above is so that duplicates are spotted + # across different maildirs and deleted from the filtered source + return 0 } ############### - + tot=0 typeset -al msgs - - formail_cache="$TMPDIR/filter.rmdupes.$datestamp.$RANDOM" - newlock "$formail_cache" - + + _tmp_create + formail_cache="$JAROTMP" + for folder in ${=@}; do - { test -r "$folder" } || { folder="$MAILDIRS/$folder" } - { test -r "$folder" } || { - error "Directory not found: $folder" - continue } - - { maildircheck "${folder}" } || { - error "Not a maildir folder: $folder" - continue } - - c=0 - notice "Checking for duplicates in $folder" - msgs=`${=find} "${folder}" -maxdepth 2 -type f` - act "Please wait, this can take a while..." - - - - for m in ${(f)msgs}; do - func "formail < $m" - # 128MB should be enough ehre? - formail -D 128000000 "$formail_cache" <"$m" \ - && rm "$m" && c=$(( $c + 1 )) - done - act "$c duplicates found and deleted" - tot=$(( $tot + $c )) + { test -r "$folder" } || { folder="$MAILDIRS/$folder" } + { test -r "$folder" } || { + error "Directory not found: $folder" + continue } + + { maildircheck "${folder}" } || { + error "Not a maildir folder: $folder" + continue } + + c=0 + notice "Checking for duplicates in $folder" + msgs=`${=find} "${folder}" -maxdepth 2 -type f` + act "Please wait, this can take a while..." + + + + for m in ${(f)msgs}; do + func "formail < $m" + # 128MB should be enough ehre? + formail -D 128000000 "$formail_cache" <"$m" \ + && rm "$m" && c=$(( $c + 1 )) + done + act "$c duplicates found and deleted" + tot=$(( $tot + $c )) done - - unlink "$formail_cache" - + if [ "$tot" = "0" ]; then - act "No duplicates found at all" + act "No duplicates found at all" else - act "$tot total duplicates found and deleted" + act "$tot total duplicates found and deleted" fi } diff --git a/src/zlibs/publish b/src/zlibs/publish @@ -159,51 +159,52 @@ pubdb_extract_body() { _path="$1" { test -r "$_path" } || { error "mail not found for body extraction: $_path"; return 1 } - pushd ${TMPDIR}/pubdb + mkdir -p ${MAILDIRS}/pubdb + pushd ${MAILDIRS}/pubdb # check if it has already html _html=`mu extract "$_path" | awk '/text\/html/ {print $1; exit}'` - { test "$_html" = "" } || { + [[ "$_html" = "" ]] || { mu extract --overwrite --parts="$_html" "$_path" - # check if there is an html header to weed out - grep '<body>' "$_html".part > /dev/null - if [ $? = 0 ]; then - awk ' + # check if there is an html header to weed out + grep '<body>' "$_html".part > /dev/null + if [ $? = 0 ]; then + awk ' BEGIN { body=0 } /<body/ { body=1; next } /<\/body/ { body=0; next } { if(body==1) print $0 }' "$_html".part | iconv -c - else - cat "$_html".part | iconv -c - fi - rm "$_html".part - return 0 } - + else + cat "$_html".part | iconv -c + fi + rm "$_html".part + return 0 } + # use the first text/plain _text=`mu extract "$_path" | awk '/text\/plain/ {print $1; exit}'` { test "$_text" = "" } || { - mu extract --overwrite --parts="$_text" "$_path" - # here we tweak the origin to avoid headers in markdown - # preferring to interpret # as inline preformat - cat "$_text".part | sed ' + mu extract --overwrite --parts="$_text" "$_path" + # here we tweak the origin to avoid headers in markdown + # preferring to interpret # as inline preformat + cat "$_text".part | sed ' s/^#/ /g ' | iconv -c | maruku --html-frag 2>/dev/null | sed ' s|http://[^ |<]*|<a href="&">&</a>|g s|https://[^ |<]*|<a href="&">&</a>|g' -# s|www\.[^ |<]*|<a href="http://&">&</a>|g' - rm "$_text".part - return 0 - } + # s|www\.[^ |<]*|<a href="http://&">&</a>|g' +rm "$_text".part +return 0 +} - # check if its an html only mail - # _html=`mu extract "$_path" | awk '/text\/html/ {print $1; exit}'` - # { test "$_html" = "" } || { - # mu extract --overwrite --parts="$_html" "$_path" - # elinks -dump "$_html".part - # rm "$_html".part - # return 0 } +# check if its an html only mail +# _html=`mu extract "$_path" | awk '/text\/html/ {print $1; exit}'` +# { test "$_html" = "" } || { +# mu extract --overwrite --parts="$_html" "$_path" +# elinks -dump "$_html".part +# rm "$_html".part +# return 0 } - return 0 +return 0 }