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 124f55125e99db9b961921d6920774bb87f71e4d
parent 54aea8c2acdefe8e3cea2f48a0ef6abc92514b98
Author: Jaromil <jaromil@dyne.org>
Date:   Fri,  1 Jun 2012 16:43:48 +0200

better handling of multiple accounts

Diffstat:
Msrc/jaro | 291+++++++++++++++++++++++++++++++++++++------------------------------------------
1 file changed, 138 insertions(+), 153 deletions(-)

diff --git a/src/jaro b/src/jaro @@ -46,6 +46,10 @@ PARAM=() typeset -A global_opts typeset -A opts +# global variable for account selection +typeset -h account +account=default + # global variables for accounts typeset -h name login host protocol port password auth folders accountopt @@ -77,10 +81,10 @@ if [ "$1" != "-q" ]; then # honor quiet flag case $(uname) in Linux) OS=GNU notice "Jaro Mail v$VERSION running on GNU/Linux" ;; - + Darwin) OS=MAC notice "Jaro Mail v$VERSION running on Mac/OSX" ;; - + *) OS=GNU # default error "Running on an unknown operating system, assuming GNU" ;; esac @@ -217,7 +221,7 @@ ask_password() { act "New password set for $1 @ $2" security add-internet-password -a $1 -s $2 -w "${password}" else - act "Using saved password for $1 @ $2" + act "Using saved password for $1 @ $2" password=`security find-internet-password -a $1 -s $2 -g \ 2>&1| awk '/^password:/ { print $2 }' | sed -e 's/"//g'` fi @@ -248,7 +252,7 @@ set from = "$name <$login>" EOF else error "No identity found, left blank." - touch $MUTTDIR/identity + touch $MUTTDIR/identity fi } @@ -261,11 +265,11 @@ option_is_set() { [[ -n ${(k)opts[$1]} ]]; r=$? if [[ $2 == out ]]; then - if [[ $r == 0 ]]; then - echo 'set' - else - echo 'unset' - fi + if [[ $r == 0 ]]; then + echo 'set' + else + echo 'unset' + fi fi return $r; } @@ -290,21 +294,31 @@ maildirmake() { ${=mkdir} ${1}/tmp } -# arg 1 : account name, es: imap.gmail +# arg 1 : account type, es: imap or smtp +# -a defines which account name other than 'default' read_account() { - adir=$WORKDIR/Accounts - acct=$1 - type="`echo $1|cut -d. -f1`" + typeset -al all + + if [ -z $1 ]; then type="*" + + else type=$1 # smtp, imap, pop ... file prefixes" + fi + # find the file - if [ -r $adir/$acct.txt ]; then - acct=$adir/$acct.txt - elif [ -r $adir/$acct ]; then # try without .txt - acct=$adir/$acct - else - error "account $acct not found in $adir" + for a in `find $WORKDIR/Accounts -name "$type.$account*" | grep -v smtp`; do all+=($a); done + if [ ${#all} = 0 ]; then + error "No $type account found: $account" + return 1 + elif [ ${#all} != 1 ]; then + error "Too many $type accounts named $account:" + for i in ${all}; do act -n "${all[i]}"; done + echo; error "Refine your account keyword" return 1 fi + acct=${all[1]} + type=`basename $acct | cut -d. -f1` + case $type in imap|smtp) @@ -341,7 +355,7 @@ read_account() { func "type: $type" func "name: $name" - func "email: $email" + func "email: $email" func "host: $host" func "login: $login" func "trans: $transport" @@ -358,43 +372,6 @@ read_account() { esac return 0 } -# read a default account -# arg: "in" or "out" -# meaning an account to receive or send emails -read_default_account() { - adir=$WORKDIR/Accounts - typeset -al all - if [ -z $1 ]; then - error "Error: read_default_account called without argument" - return 1 - fi - case $1 in - "in") - 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 - read_account `basename ${all[1]}` # take the first found - return $? - else - error "No accounts configured, look in $WORKDIR/Accounts" - fi - fi - ;; - "out") - 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 $? - fi - ;; - esac - return 1 -} queue() { local base; @@ -409,7 +386,7 @@ queue() { # Create new unique filenames of the form # MAILFILE: ccyy-mm-dd-hh.mm.ss[-x].mail # MSMTPFILE: ccyy-mm-dd-hh.mm.ss[-x].msmtp - # where x is a consecutive number only appended if you send more than one + # where x is a consecutive number only appended if you send more than one # mail per second. base="`date +%Y-%m-%d-%H.%M.%S`" func "[$base] queue called with params: ${PARAM[@]}" @@ -449,7 +426,7 @@ cert() { gmail) cc=Equifax_Secure_Certificate_Authority if ! [ -r $WORKDIR/certs/${cc}.pem ]; then - + curl -o $WORKDIR/certs/${cc}.pem \ "https://www.geotrust.com/resources/root_certificates/certificates/${cc}.cer" openssl x509 -in \ @@ -499,16 +476,32 @@ cert() { ########### # FETCHMAIL +fetchall() { + notice "Fetching all accounts in $MAILDIRS" + res=0 + for i in `find $WORKDIR/Accounts -type f | grep -v README`; do + a=`basename $i` + type=`echo $a | cut -d. -f1` + account=`echo $a | cut -d. -f2` + func "$account type $type: $a" + fetch $type + if [ $? != 0 ]; then res=1; fi + # returns an error if just one of the accounts did + done + return $res +} fetch() { adir=$WORKDIR/Accounts - acct=$1 + typeset -al all - if [ -z $acct ]; then # fetch all accounts - 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 + # recurdion here + if [ "$1" = "all" ]; then fetchall; return $?; fi + + read_account "$1" + if [ $? != 0 ]; then + error "Account configuration not found, or broken. Aborting operation." + return 1 fi if ! [ -r $PROCMAILDIR/rc ]; then @@ -516,87 +509,81 @@ fetch() { update fi - for a in ${all}; do - read_account `basename $a` - if [ $? != 0 ]; then - error "Account configuration not found, or broken. Aborting operation." - return 1 - fi - notice "Fetching mails from $name" - ask_password $login $host - if [ $? != 0 ]; then - error "Error retrieving password for $login on $host" - unset password all - return 1 - fi + notice "Fetching mails for $name" + ask_password $login $host + if [ $? != 0 ]; then + error "Error retrieving password for $login on $host" + unset password all + return 1 + fi - newlock $WORKDIR/tmp/$host.fetch - cat <<EOF > $WORKDIR/tmp/$host.fetch + newlock $WORKDIR/tmp/$host.fetch + cat <<EOF > $WORKDIR/tmp/$host.fetch poll $host with proto IMAP user "$login" there with password "$password" EOF - unset password + unset password - if ! [ -z $accountopt ]; then # add option configuration - echo "${accountopt}" >> $WORKDIR/tmp/$host.fetch; fi + if ! [ -z $accountopt ]; then # add option configuration + echo "${accountopt}" >> $WORKDIR/tmp/$host.fetch; fi - if ! [ -z $folders ]; then # add folder configuration - echo "folder ${folders}" >> $WORKDIR/tmp/$host.fetch; fi + if ! [ -z $folders ]; then # add folder configuration + echo "folder ${folders}" >> $WORKDIR/tmp/$host.fetch; fi - cat <<EOF >> $WORKDIR/tmp/$host.fetch + cat <<EOF >> $WORKDIR/tmp/$host.fetch ssl warnings 3600 and wants mda "procmail -m $PROCMAILDIR/rc" EOF - if [ "$cert" = "check" ]; then - cat <<EOF >> $WORKDIR/tmp/$host.fetch + if [ "$cert" = "check" ]; then + cat <<EOF >> $WORKDIR/tmp/$host.fetch sslcertck sslcertpath '$WORKDIR/certs' EOF - fi - cat <<EOF >> $WORKDIR/tmp/$host.fetch + fi + cat <<EOF >> $WORKDIR/tmp/$host.fetch antispam 571 550 501 554 EOF # try login without doing anything - fetchmail -c -f $WORKDIR/tmp/$host.fetch + fetchmail -c -f $WORKDIR/tmp/$host.fetch # examine result - case $? in - 1) - notice "No mails for $name" - unlock $WORKDIR/tmp/$host.fetch - return 1 - ;; - 2) - error "Invalid or unknown certificate for $host" - unlock $WORKDIR/tmp/$host.fetch - return 1 - ;; - 3) - error "Invalid password for user $login at $host" - unlock $WORKDIR/tmp/$host.fetch - return 1 + case $? in + 1) + notice "No mails for $name" + unlock $WORKDIR/tmp/$host.fetch + return 1 ;; - *) ;; - esac + 2) + error "Invalid or unknown certificate for $host" + unlock $WORKDIR/tmp/$host.fetch + return 1 + ;; + 3) + error "Invalid password for user $login at $host" + unlock $WORKDIR/tmp/$host.fetch + return 1 + ;; + *) ;; + esac # archive old procmail log - if [ -r $WORKDIR/log/procmail.log ]; then - 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..." + if [ -r $WORKDIR/log/procmail.log ]; then + 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 - + 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 + 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" + echo "${briefing}" - 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" - echo "${briefing}" - done return 0 } @@ -612,19 +599,14 @@ send() { mailnum=`ls ${MAILDIRS}/outbox | grep 'mail$' | wc -l` mailnum=${mailnum// /} # trim whitespace if [ "$mailnum" = "0" ]; then - act "Outbox is empty, no mails to send." - return 0 + act "Outbox is empty, no mails to send." + return 0 fi - if [ -z $1 ]; then # no particular account specified - # use the default account - read_default_account "out" - else - read_account smtp.${1} - fi + read_account smtp notice "Sending out ${mailnum} mails via $name" - + # defaults [ -z $auth ] && auth=plain [ -z $port ] && port=25 @@ -655,7 +637,7 @@ EOF act "To: ${recipients}" msmtp -C $WORKDIR/tmp/$host.send -- ${=recipients} < "${mail}" if [ $? != 0 ]; then - error "Error sending mail, skipped" + error "Error sending mail, skipped" unlock ${smtp} else act "Mail sent succesfully" @@ -673,12 +655,12 @@ EOF # PEEK # this function will open the MTA to the imap server without fetching mails locally peek() { - # TODO account selection with -a - read_default_account in + read_account imap + notice "Peek into remote imap account $name" folder="" - if ! [ -z ${1} ]; then + if ! [ -z ${1} ]; then folder="/${1}" act "opening folder ${folder}" fi @@ -710,7 +692,7 @@ update() { # generate muttrc # backup what's too old in the maildirs # ... - + # debug configuration func "MAILDIRS: $MAILDIRS" func "WORKDIR: $WORKDIR" @@ -726,7 +708,7 @@ update() { maildirmake $MAILDIRS/unsorted maildirmake $MAILDIRS/ml.unsorted ${=mkdir} $MAILDIRS/outbox - + ###### # MUTT ${=mkdir} $MUTTDIR @@ -775,13 +757,14 @@ source $WORKDIR/Mutt.txt EOF - + # just the header, will be completed later in procmail loop rm -f $MUTTDIR/mboxes echo -n "mailboxes +priv" > $MUTTDIR/mboxes # update identity with default - read_default_account "in" + read_account imap + switch_identity ########## @@ -816,7 +799,7 @@ EOF ####### echo "# filters generated from Filters.txt" >> $PROCMAILDIR/rc - + for f in `cat $WORKDIR/Filters.txt | awk '/^#/ {next} /^./ { print $1 ";" $2 ";" $3 ";" $4 }'`; do header="${f[(ws:;:)1]}" address="${f[(ws:;:)2]}" @@ -846,7 +829,7 @@ EOF ####### echo "# filters generated from Accounts" >> $PROCMAILDIR/rc - + typeset -al accts for f in `cat $WORKDIR/Accounts/* | awk '/^email/ { print $2 }'`; do echo "ADDR=${f}\tDEST=priv/\tINCLUDERC=\$PMSRC/pf-chkto.rc" >> $PROCMAILDIR/rc @@ -1014,6 +997,7 @@ Main commands: Options: + -a use a particular account instead of default (keyword) -h print this help -v version information for this tool -q run quietly without printing informations @@ -1052,14 +1036,14 @@ main() # I. usability; user expect that "-s" is "size # II. Option parsing WILL EXPLODE if you do this kind of bad things # (it will say "option defined more than once, and he's right) - main_opts=(q -quiet=q D -debug=D h -help=h v -version=v n -dry-run=n) + main_opts=(a: -account=a q -quiet=q D -debug=D h -help=h v -version=v n -dry-run=n) subcommands_opts[__default]="" subcommands_opts[queue]="" subcommands_opts[fetch]="" subcommands_opts[send]="" subcommands_opts[read]="" subcommands_opts[compose]="" - subcommands_opts[peek]="" + subcommands_opts[peek]="" subcommands_opts[update]="" subcommands_opts[query]="" subcommands_opts[learn]="" @@ -1074,9 +1058,9 @@ main() ### Detect subcommand local -aU every_opts #every_opts behave like a set; that is, an array with unique elements for optspec in $subcommands_opts$main_opts; do - for opt in ${=optspec}; do - every_opts+=${opt} - done + for opt in ${=optspec}; do + every_opts+=${opt} + done done local -a oldstar oldstar=($argv) @@ -1084,11 +1068,11 @@ main() unset discardme subcommand=$1 if [[ -z $subcommand ]]; then - subcommand="__default" + subcommand="__default" fi if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand - error "Subcommand '$subcommand' doesn't exist" - exitcode=1 + error "Subcommand '$subcommand' doesn't exist" + exitcode=1 return 1 fi argv=(${oldstar}) @@ -1098,10 +1082,10 @@ main() # zsh magic: ${=string} will split to multiple arguments when spaces occur set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]} if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing - zparseopts -M -E -D -Aopts ${cmd_opts} + zparseopts -M -E -D -Aopts ${cmd_opts} if [[ $? != 0 ]]; then error "Some error occurred during option processing." - exitcode=1 + exitcode=1 return 1 fi fi @@ -1127,10 +1111,11 @@ main() fi ### End parsing command-specific options - if option_is_set -v; then + if option_is_set -v; then cat $JAROMAILEXEC | awk '/^#/ {print $0 } !/^#/ {exit}' echo fi + if option_is_set -a; then account=`option_value -a`; fi if option_is_set -h; then usage; fi if option_is_set -q; then QUIET=1; fi if option_is_set -D; then func "Debug messages ON"; DEBUG=1; fi