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 54aea8c2acdefe8e3cea2f48a0ef6abc92514b98
parent 8e6160a3d0404f538713327f409855a9ecf47f43
Author: Jaromil <jaromil@dyne.org>
Date:   Fri,  1 Jun 2012 13:28:19 +0200

safe locking mechanism, now sending multiple mails works

Diffstat:
MTODO | 6++++++
Minstall.sh | 1+
Msrc/jaro | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
3 files changed, 97 insertions(+), 35 deletions(-)

diff --git a/TODO b/TODO @@ -16,6 +16,12 @@ * Stats + Have some fancy statistics +* Speedmail or Quickmail + write down a mail from commandline and send it right away (if online) + doesn't uses Mutt to generate the mail body + but might read Mutt.txt configuration for headers and such + + * DONE Maildirs + Remove duplicates from maildirs (garbage collection) + Backup system with expiration date diff --git a/install.sh b/install.sh @@ -247,6 +247,7 @@ cp src/lbdb/fetchaddr $WORKDIR/.lbdb/ chmod +x $WORKDIR/.lbdb/* ln -sf $WORKDIR/.lbdb/lbdb-fetchaddr $WORKDIR/bin/ ln -sf $WORKDIR/.lbdb/lbdbq $WORKDIR/bin/ +ln -sf $WORKDIR/.lbdb/dotlock $WORKDIR/bin/ # OS specific lbdb rules case $OS in diff --git a/src/jaro b/src/jaro @@ -30,9 +30,9 @@ for arg in ${argv}; do OLDARGS+=($arg); done # declare global variables QUIET=0 -DEBUG=0 +DEBUG=1 DRYRUN=0 - +CLEANEXIT=1 # which command to use when creating dirs mkdir="`which mkdir` -m 700 -p" @@ -115,8 +115,9 @@ PROCMAILDIR=$WORKDIR/.procmail MUTTDIR=$WORKDIR/.mutt cleanexit() { - for f in `ls $WORKDIR/tmp/`; do - ${=rm} $WORKDIR/tmp/$f + func "Clean exit procedures" + for f in `ls $WORKDIR/tmp/ | grep -v '.lock$'`; do + unlink $WORKDIR/tmp/$f done unset typeset -h name login host protocol port password auth folders accountopt } @@ -131,6 +132,48 @@ TRAPINT() { fi } +lock() { + func "lock: $@" + $WORKDIR/.lbdb/dotlock ${=@} + case $? in + 1) error "Cannot lock non existing file: $@" + return 1 ;; + 3) error "Locked file in use: $@" + return 3 ;; + 5) error "Impossible to lock: $@" + return 5 ;; + # success + 0) return 0 ;; + esac +} +newlock() { # create locked + func "creating locked file: $1" + touch $1 + chmod 600 $1 + lock $1 +} +unlock() { + func "unlock: $@" + $WORKDIR/.lbdb/dotlock -u ${=@} + if [ $? != 0 ]; then + error "Unable to unlock: $@" + return 1 + fi + return 0 +} +locked_unlink() { # delete a file that we are locking + # use with care! this can permanently erase currently locked files + ${=rm} ${1} + touch ${1} + $WORKDIR/.lbdb/dotlock -d -f ${1} +} +unlink() { # lock and delete + func "unlink: $1" + lock ${1} + locked_unlink ${1} & +} + + # we use pinentry # comes from gpg project and is secure # it also conveniently uses the right toolkit @@ -261,9 +304,11 @@ read_account() { error "account $acct not found in $adir" return 1 fi - atmp=$WORKDIR/tmp/$1 + case $type in imap|smtp) + + lock $acct ttmp=`cat $acct | awk ' /^#/ { next } /^name/ { printf "name=\""; for(i=2;i<=NF;i++) printf "%s ", $i; printf "\";" } @@ -278,6 +323,8 @@ read_account() { /^options/ { printf "accountopt=\""; for(i=2;i<=NF;i++) printf "%s ", $i; printf "\";" } /^folders/ { printf "folders=\""; for(i=2;i<=NF;i++) printf "%s ", $i; printf "\";" } '` + unlock $acct + eval "$ttmp" # check required fields { test -z $host } && { error "Field missing in account $acct: host"; return 1 } @@ -323,10 +370,9 @@ read_default_account() { fi case $1 in "in") - if [ -r $adir/imap.default ]; then - read_account imap.default - return $? - else + read_account imap.default + if [ $? = 0 ]; then return $? # configured default found + else # pick first one for a in `find $adir -name "imap*"`; do all+=($a); done for a in `find $adir -name "pop*"`; do all+=($a); done if [ ${#all} != 0 ]; then @@ -338,10 +384,9 @@ read_default_account() { fi ;; "out") - if [ -r $adir/smtp.default ]; then - read_account smtp.default - return $? - else + read_account smtp.default + if [ $? = 0 ]; then return $? # configured default found + else # pick first one for a in `find $adir -name "smtp*"`; do all+=($a); done read_account `basename ${all[1]}` # take the first found return $? @@ -381,8 +426,10 @@ queue() { msmtpfile="$base.msmtp" # Write command line to $MSMTPFILE echo "$@" > "$msmtpfile" + lock $msmtpfile # Write the mail to $MAILFILE cat > "$mailfile" + unlock $msmtpfile cd - return 0 } @@ -456,17 +503,19 @@ fetch() { adir=$WORKDIR/Accounts acct=$1 typeset -al all + if [ -z $acct ]; then # fetch all accounts - for a in `find $adir -name "imap*"`; do all+=($a); done - for a in `find $adir -name "pop*"`; do all+=($a); done - else - # fetch only one account - for a in `find $adir -name "$acct*"`; do all+=($a); done + for a in `find $adir -type f -name "imap*"`; do all+=($a); done + for a in `find $adir -type f -name "pop*"`; do all+=($a); done + else # fetch only one account + for a in `find $adir -type f -name "$acct*"`; do all+=($a); done fi + if ! [ -r $PROCMAILDIR/rc ]; then act "updating procmail configuration" update fi + for a in ${all}; do read_account `basename $a` if [ $? != 0 ]; then @@ -474,8 +523,6 @@ fetch() { return 1 fi notice "Fetching mails from $name" - touch $WORKDIR/tmp/$host.fetch - chmod 600 $WORKDIR/tmp/$host.fetch ask_password $login $host if [ $? != 0 ]; then error "Error retrieving password for $login on $host" @@ -483,6 +530,7 @@ fetch() { return 1 fi + newlock $WORKDIR/tmp/$host.fetch cat <<EOF > $WORKDIR/tmp/$host.fetch poll $host with proto IMAP user "$login" there with password "$password" EOF @@ -512,17 +560,17 @@ EOF case $? in 1) notice "No mails for $name" - ${=rm} $WORKDIR/tmp/$host.fetch + unlock $WORKDIR/tmp/$host.fetch return 1 ;; 2) error "Invalid or unknown certificate for $host" - ${=rm} $WORKDIR/tmp/$host.fetch + unlock $WORKDIR/tmp/$host.fetch return 1 ;; 3) error "Invalid password for user $login at $host" - ${=rm} $WORKDIR/tmp/$host.fetch + unlock $WORKDIR/tmp/$host.fetch return 1 ;; *) ;; @@ -530,18 +578,20 @@ EOF # archive old procmail log if [ -r $WORKDIR/log/procmail.log ]; then - touch $WORKDIR/log/procmail-${datestamp}.log - chmod 700 $WORKDIR/log/procmail-${datestamp}.log + newlock $WORKDIR/log/procmail-${datestamp}.log cat $WORKDIR/log/procmail.log \ >> $WORKDIR/log/procmail-${datestamp}.log rm -f $WORKDIR/log/procmail.log + unlock $WORKDIR/log/procmail-${datestamp}.log fi act "please wait while downloading mails..." cat $WORKDIR/tmp/$host.fetch | fetchmail -f - # TODO: substitute this with cat conf | fetchmail -f - # to avoid writing the password in clear on filesystem - + + unlock $WORKDIR/tmp/$host.fetch + total=`mailstat -k $WORKDIR/log/procmail.log | tail -n1 | awk '{print $2}'` briefing=`mailstat -kt $WORKDIR/log/procmail.log |awk '!/procmail/ { print " " $2 "\t" $3 }'|sort -nr` notice "$total emails fetched" @@ -578,13 +628,14 @@ send() { # defaults [ -z $auth ] && auth=plain [ -z $port ] && port=25 - - touch $WORKDIR/tmp/$host.send - chmod 600 $WORKDIR/tmp/$host.send + + ask_password $login $host + + newlock $WORKDIR/tmp/$host.send cat <<EOF > $WORKDIR/tmp/$host.send account default -from ${name} +from ${email} user ${login} host ${host} port ${port} @@ -599,18 +650,22 @@ EOF for mail in `find $MAILDIRS/outbox -name "*.mail"`; do smtp=`echo ${mail} | sed -e 's/mail/msmtp/'` + lock ${smtp} recipients="`cat ${smtp}`" act "To: ${recipients}" msmtp -C $WORKDIR/tmp/$host.send -- ${=recipients} < "${mail}" if [ $? != 0 ]; then error "Error sending mail, skipped" + unlock ${smtp} else act "Mail sent succesfully" # whitelist those to whom we send mails - cat ${mail} | $WORKDIR/bin/jaro -q learn - ${=rm} ${mail} ${smtp} + cat ${mail} | $WORKDIR/bin/jaro -q learn + ${=rm} ${mail} & + locked_unlink ${smtp} & fi done + unlock $WORKDIR/tmp/$host.send return 0 } @@ -1092,8 +1147,8 @@ main() update) update ;; - query) query ${PARAM} ;; - learn) learn ${PARAM} ;; + query) CLEANEXIT=0; query ${PARAM} ;; + learn) CLEANEXIT=0; learn ${PARAM} ;; backup) backup ${PARAM} ;; rmdupes) rmdupes ${PARAM} ;; @@ -1111,5 +1166,5 @@ main() check_bin main $@ -cleanexit +if [ $CLEANEXIT = 1 ]; then cleanexit; fi return $exitcode