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 4a12cc222848994d4d797743f9a580c0b903c78d
parent a92bfd3efbf6f766699c8b62b6d46c5ec9f18760
Author: Jaromil <jaromil@dyne.org>
Date:   Sun,  4 May 2014 20:44:27 +0200

new mixmaster support to send anonymous emails

Diffstat:
MTODO.md | 38+++++++++++++++++++++++---------------
Msrc/zlibs/email | 161++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
2 files changed, 121 insertions(+), 78 deletions(-)

diff --git a/TODO.md b/TODO.md @@ -5,6 +5,29 @@ https://dyne.org/donate +## Vacation trigger +sieve script example + +``` + +require ["fileinto", "vacation", "variables"]; + +if header :is "X-Spam-Flag" "YES" { + fileinto "Spam"; +} + +if header :matches "Subject" "*" { + set "subjwas" ": ${1}"; +} + +vacation + :days 1 + :subject "Out of office reply${subjwas}" +"I'm out of office, please contact Joan Doe instead. +Best regards +John Doe"; +``` + ## Peek to open imap folders with reverse Date ordering when open, imap folders should list emails without threading this is really intended for a peek @@ -37,14 +60,6 @@ PLAN: use isync and open local maildirs -## Mixmaster - basically is an almost finished implementation, only thing - missing is to clear out the header handling between jaromail and mutt - -## Multiple outboxes - handle different send programs - for example anonymous/ for the mixmaster queue - ## Substitute procmail with a simple C filter we just filter From, To:, CC: and mailman headers @@ -57,13 +72,6 @@ ## TBT time based text, all included in html mails -## Install - * Full integration with the Tomb process creation - * integrate install/setup command in jaro - -## Mouse enable MUA - mutt or mu in debian? - * Find out how to make mouse selection work ## Speedmail or Quickmail write down a mail from commandline and send it right away (if online) diff --git a/src/zlibs/email b/src/zlibs/email @@ -23,8 +23,8 @@ # extract all emails found in stdin, one per line extract_emails() { awk '{ for (i=1;i<=NF;i++) - if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) { - gsub(/<|>|,/ , "" , $i); print $i } }' + if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) { + gsub(/<|>|,/ , "" , $i); print $i } }' } compose() { @@ -48,9 +48,12 @@ queue() { queue_to=($@) # set it ready for saving in outbux queue_body="$base" - cat > $TMPDIR/$queue_body.mail - + # pre-processing of the email headers + cat | awk ' +/User-Agent:/ { print "User-Agent: Jaro Mail <http://jaromail.dyne.org>"; next } +{ print $0 } +' > "$TMPDIR/${queue_body}.mail" maildirmake $MAILDIRS/outbox { test $? = 0 } || { @@ -76,18 +79,17 @@ queue() { # bodies: the list of mail bodies to send # corresponding to files with same name and extension .msmtp bodies=(`find $tmppp -type f -name '*.mail'`) - rcpt=(`find $tmppp -type f -name '*.msmtp'`) for i in ${bodies}; do mv $i $MAILDIRS/outbox/new/; done - for i in ${rcpt}; do mv $i $MAILDIRS/outbox/send/${i%%.msmtp}.rcpt; done rmdir $tmppp fi } - mkdir -p $MAILDIRS/outbox/send + ${=mkdir} $MAILDIRS/outbox/send lock $MAILDIRS/outbox + # check if recipients are a Group if [[ "${=queue_to}" =~ '@jaromail.group' ]]; then - # recipients are in a group + groupfile="`print ${=queue_to}|cut -d@ -f1`" act "email recipients are in group ${groupfile}" @@ -104,7 +106,8 @@ queue() { 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 @@ -112,12 +115,9 @@ queue() { individual) for i in ${(f)recipients}; do ig=${base}-${RANDOM} - cat ${TMPDIR}/${queue_body}.mail | \ + cat "${TMPDIR}/${queue_body}.mail" | \ awk '/^To:/ { print "'"To: $i"'"; next } { print $0 }' \ > ${MAILDIRS}/outbox/new/${ig}.mail - # TODO: check email validation and <stripping> - print "$i" | sed 's/.*<//;s/>$//' \ - > ${MAILDIRS}/outbox/send/${ig}.rcpt done ;; @@ -130,28 +130,16 @@ queue() { else cc+=", $i"; fi done ig=${base}-${RANDOM} - cat ${TMPDIR}/${queue_body}.mail | \ + cat "${TMPDIR}/${queue_body}.mail" | \ awk '/^To:/ { print "'"To: $cc"'"; print "'"Reply-To: $cc"'"; next } - { print $0 }' \ - > ${MAILDIRS}/outbox/new/${ig}.mail - # extract emails one per line - rcpt=`print $cc | extract_emails` - func "cc: $cc" - func "rcpt: $rcpt" - print ${=rcpt} > ${MAILDIRS}/outbox/send/${ig}.rcpt + { print $0 }' \ + > ${MAILDIRS}/outbox/new/${ig}.mail ;; esac else # recipients are set in the email envelope - act "email recipients: $queue_to" - # file into the outbox filtering headers - cat $TMPDIR/$queue_body.mail | awk ' -/User-Agent:/ { print "User-Agent: Jaro Mail <http://jaromail.dyne.org>"; next } -{ print $0 } -' > $MAILDIRS/outbox/new/$queue_body.mail - rm $TMPDIR/$queue_body.mail - print "${=queue_to}" > $MAILDIRS/outbox/send/$queue_body.rcpt + mv $TMPDIR/$queue_body.mail $MAILDIRS/outbox/new/$queue_body.mail fi unlock $MAILDIRS/outbox @@ -211,8 +199,8 @@ fetch() { # return here if the imap folders are all empty # { test ${imap_info[${#imap_info}]} = 0 } && { - # act "Mailbox is empty, nothing to fetch." - # return 0 } + # act "Mailbox is empty, nothing to fetch." + # return 0 } # notice "Total occupation is `human_size ${imap_info[${#imap_info}]}`" @@ -303,8 +291,8 @@ send() { # list mails to send - queue_rcpt=(`${=find} ${MAILDIRS}/outbox/send -type f -name '*.rcpt'`) - { test ${#queue_rcpt} = 0 } && { + queue_outbox=(`${=find} ${MAILDIRS}/outbox -type f -name '*.mail'`) + { test ${#queue_outbox} = 0 } && { act "Outbox is empty, no mails to send." return 0 } @@ -321,7 +309,7 @@ send() { is_online ${host} ${port} { test $? = 0 } || { return 1 } - notice "Sending out ${#queue_rcpt} mails via ${type}.${account}" + notice "Sending out ${#queue_outbox} mails via ${type}.${account}" { test $DRYRUN = 1 } && { return 0 } @@ -351,41 +339,88 @@ logfile ${MAILDIRS}/logs/msmtp.log auth ${auth} password ${password} EOF - unset password - for q in ${queue_rcpt}; do - qr=`basename ${q%%.rcpt}` - func "looking for a mail body to send labeled $qr" - qbody=(`${=find} $MAILDIRS/outbox -type f -name "${qr}.mail*"`) - func "found ${#qbody} hits: ${=qbody}" - recipients=(`cat $q`) - func "recipients: ${=recipients}" - - { test ${#qbody} = 0 } && { - # body for rcpt not found, mail must have been deleted from outbox - act "canceled delivery for a deleted mail to: `cat $q`" - ${=rm} $q - continue } - - { test ${#qbody} -gt 1 } && { - error "too many mail bodies found for a message to: `cat $q`" - error "this is quite confusing, check your outbox, we'll skip for now." - continue } - - act "To: ${=recipients}" - msmtp -C $tmp -- ${=recipients} < "${qbody}" - if [ $? != 0 ]; then + for qbody in ${queue_outbox}; do + + # check if this is an anonymous mail + head -n 20 "$qbody" | grep -i '^from: anon' > /dev/null + if [ $? = 0 ]; then + anoncfg="${TMPDIR}/${host}.anon.$RANDOM" + cat <<EOF > "$anoncfg" +REMAIL n +POOLSIZE 0 +SENDPOOLTIME 0m +RATE 100 + +PGPREMPUBASC /var/lib/mixmaster/used-stats/pubring.asc +PUBRING /var/lib/mixmaster/used-stats/pubring.mix +TYPE1LIST /var/lib/mixmaster/used-stats/rlist.txt +TYPE2REL /var/lib/mixmaster/used-stats/mlist.txt +TYPE2LIST /var/lib/mixmaster/used-stats/type2.list + +SENDMAIL=msmtp -C $tmp -t +ERRLOG=${MAILDIRS}/logs/mixmaster.log +VERBOSE=2 + +EOF + + act "Sending out anonymous email via mixmaster" + act "`head -n 20 $qbody | grep '^Subject:'`" + recipients=(`cat $qbody | fetchaddr -a -x to | cut -d, -f1`) + recipients+=(`cat $qbody | fetchaddr -a -x cc | cut -d, -f1`) + for r in ${recipients}; do + act "Sending to: ${r}" + # strip headers and send via mixmaster + cat "$qbody" | awk ' +BEGIN { head=1 } +/^To: / { print $0; next } +/^Cc: / { print $0; next } +/^Bcc: / { print $0; next } +/^Subject: / { print $0; next } +/^In-Reply-To: / { print $0; next } +/^References: / { print $0; next } +/^Mime-Version: / { print $0; next } +/^Content-Type: / { print $0; next } +/^Content-Disposition: / { print $0; next } +/^$/ { head=0 } +{ if(head==0) print $0 } +' | mixmaster --config=$anoncfg -m --to="$r" + res=$? + func "mixmaster returns $res" + done + + ${=rm} $anoncfg + + else # normal send with msmtp + + act "Sending out email" + head -n 10 "$qbody" | awk ' +/^From:/ { print " . " $0 } +/^To:/ { print " . " $0 } +/^Cc:/ { print " . " $0 } +/^Subject:/ { print " . " $0 } +' + msmtp -C $tmp -t < "${qbody}" + res=$? + + fi + + # evaluate results + if [ "$res" != "0" ]; then error "Error sending mail, skipped" else - act "Mail sent succesfully" - # whitelist those to whom we send mails + notice "Mail sent succesfully" + # whitelist those to whom we send mails cat "$qbody" | $WORKDIR/bin/jaro -q learn recipient - ${=rm} "$q" "$qbody" + mv "$qbody" $MAILDIRS/sent/new/ fi + done + unset password unlock $MAILDIRS/outbox - unlink $tmp + unlock $tmp + ${=rm} $tmp return 0 } @@ -441,8 +476,8 @@ 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 + # and sort date received with no threading (latest up) + cat <<EOF >> $TMPDIR/muttpass unset mark_old set sort=reverse-date-received EOF