commit 9501d4cb5628adc99c770d09a57fdc0f8b460db0
parent 79ecdf9e5222d47c6699f999f3dace46027ad0d6
Author: Jaromil <jaromil@dyne.org>
Date: Mon, 14 May 2012 16:20:18 +0200
new account system for multiple accounts
fixes to procmail filters
and several other improvements
Diffstat:
7 files changed, 855 insertions(+), 660 deletions(-)
diff --git a/install.sh b/install.sh
@@ -1,85 +1,59 @@
#!/usr/bin/env zsh
-# Postino install script
-
-if ! [ -r src/postino ]; then
- echo "Error: this script should be run from inside a postino software distribution"
+# jaromail install script
+#
+# Copyleft (C) 2010-2012 Denis Roio <jaromil@dyne.org>
+#
+# This source code is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This source code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# Please refer to the GNU Public License for more details.
+#
+# You should have received a copy of the GNU Public License along with
+# this source code; if not, write to:
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+if ! [ -r src/jaro ]; then
+ echo "Error: this script should be run from inside a jaromail software distribution"
exit 1
fi
+if [ $1 ]; then
+ MAILDIRS=$1;
+else
+ MAILDIRS=$HOME/Mail
+fi
-WORKDIR=$HOME/.postino
-MAILDIRS=$HOME/Mail
+WORKDIR=$MAILDIRS/jaro
+PROCMAILDIR=$WORKDIR/.procmail
+MUTTDIR=$WORKDIR/.mutt
umask 007 # James Bond ;^)
-if [ $1 ]; then WORKDIR=$1; fi
-# make sure the directory is private
-mkdir -p $WORKDIR
-mkdir -p $MAILDIRS
-source src/postino
+source src/jaro
-notice "Installing Postino in $WORKDIR"
+# make sure the directory is private
+${=mkdir} $MAILDIRS
+${=mkdir} $WORKDIR
-if [ $? != 0 ]; then
- error "Postino directory $WORKDIR is not private, set its permissions to 700"
- error "Refusing to proceed."
- exit 1
-fi
+notice "Installing Jaromail in $WORKDIR"
-# install the main postino script
-mkdir -p ${WORKDIR}/bin
-cp src/postino ${WORKDIR}/bin
+# install the main jaromail script
+${=mkdir} ${WORKDIR}/bin
+cp src/jaro ${WORKDIR}/bin
# make sure we have a temp and cache dir
-mkdir -p $WORKDIR/tmp $WORKDIR/cache
+${=mkdir} $WORKDIR/tmp $WORKDIR/cache
-if ! [ -r $MAILDIRS/Configuration.txt ]; then
- cat <<EOF > $MAILDIRS/Configuration.txt
-# Name appearing in From: field
-FULLNAME="Anonymous"
-
-# MAIL USER (the left and right parts of an email)
-USER=username
-# @
-DOMAIN=gmail.com
-
-# IMAP (RECEIVE)
-IMAP_ADDRESS=imap.gmail.com
-IMAP_LOGIN=\${USER}@\${DOMAIN}
-
-# SMTP (SEND)
-SMTP_ADDRESS=smtp.gmail.com
-SMTP_LOGIN=\${USER}@\${DOMAIN}
-SMTP_PORT=465
-
-# SMTP_CERTIFICATE=gmail.pem
-# LOCAL FILES
-# to change the location of this directory,
-# export POSTINO_DIR as env var
-# the defaults below should be ok, they place
-# mutt, procmail, mstmp and other confs in ~/.postino
-
-MAILDIRS=$MAILDIRS
-MUTTDIR=$WORKDIR/.mutt
-PROCMAILDIR=$WORKDIR/.procmail
-CERTIFICATES=$HOME/.ssl/certs
-
-# directory of the sieve filter
-# REMOTE_FILTER=/var/mail/...
-EOF
- act "Default configuration created"
-else
- error "Existing $MAILDIRS/Configuration.txt skipped"
-fi
-
-# source the default configuration
-source $MAILDIRS/Configuration.txt
-
-if ! [ -r $MAILDIRS/Filters.txt ]; then
- cat <<EOF > $MAILDIRS/Filters.txt
-# Example filter configuration for Postino
+if ! [ -r $WORKDIR/Filters.txt ]; then
+ cat <<EOF > $WORKDIR/Filters.txt
+# Example filter configuration for Jaromail
# accepted email addresses
to jaromil@dyne.org save priv
@@ -112,36 +86,88 @@ from academia.edu save web.academia
EOF
act "Default filters created"
else
- error "Existing configuration $MAILDIRS/Filters.txt skipped"
+ error "Existing configuration $WORKDIR/Filters.txt skipped"
fi
-source $MAILDIRS/Configuration.txt
+if ! [ -r $WORKDIR/Accounts ]; then
+ ${=mkdir} $WORKDIR/Accounts
+ cat <<EOF > $WORKDIR/Accounts/README.txt
+Directory containing account information
+
+Each file contains a different account: imap, pop or gmail
+each account contains all information needed to connect it
+
+For example a file named imap.gmail.txt should contain:
+
+----8<----8<----8<----8<----8<----8<----8<----8<----8<----
+# Name and values are separated by spaces or tabs
+
+# Name appearing in From: field
+name Anonymous
+
+host imap.gmail.com
+
+login USERNAME@gmail.com
+
+auth plain # or kerberos, etc
+
+transport ssl
+
+port 993
+
+cert /path/to/cert
+
+# the password field will be filled in automatically
+
+----8<----8<----8<----8<----8<----8<----8<----8<----8<----
+
+Or a file named smtp.gmail.txt should contain:
+
+----8<----8<----8<----8<----8<----8<----8<----8<----8<----
+# Name and values are separated by spaces or tabs
+
+host smtp.gmail.com
+
+login USERNAME@gmail.com
+
+transport ssl # or "tls" or "plain"
+
+port 465
+
+----8<----8<----8<----8<----8<----8<----8<----8<----8<----
+
+EOF
+ act "Default accounts directory created"
+else
+ error "Existing configuration $WORKDIR/Accounts skipped"
+fi
# procmail is entirely generated
# so overwriting it won't hurt
act "Installing procmail scripts"
-mkdir -p $PROCMAILDIR
+${=mkdir} $PROCMAILDIR
cp -a share/procmail/* $PROCMAILDIR
# also mutt is safe to override
-mkdir -p $MUTTDIR
+${=mkdir} $MUTTDIR
cp -a share/mutt/* $MUTTDIR
act "Installing little brother database"
# safe to override
-mkdir -p $WORKDIR/.lbdb
+${=mkdir} $WORKDIR/.lbdb
for aw in munge.awk.in munge-keeporder.awk.in tac.awk.in; do
dst=`echo $aw | sed -e 's/.awk.in$//'`
cat share/lbdb/$aw \
- | sed -e "s&@AWK@&/usr/bin/env awk&g" \
+ | sed -e "s&@AWK@&`which awk`&g" \
> $WORKDIR/.lbdb/$dst
done
for sh in lbdb-fetchaddr.sh.in lbdb-munge.sh.in lbdb_lib.sh.in lbdbq.sh.in; do
dst=`echo $sh | sed -e 's/.sh.in$//'`
cat share/lbdb/$sh \
| sed -e "s&@SH@&/usr/bin/env zsh&g" \
- | sed -e "s&@DOTLOCK@&${WORKDIR}/tmp/.lbdb.lock&g" \
- | sed -e "s&@LBDB_VERSION@&0.38-postino&g" \
+ | sed -e "s&@DOTLOCK@&mutt_dotlock&g" \
+ | sed -e "s&@LBDB_FILE&${WORKDIR}/.lbdb/m_inmail.list&g" \
+ | sed -e "s&@LBDB_VERSION@&0.38-jaromail&g" \
| sed -e "s&@prefix@&${WORKDIR}/.lbdb&g" \
| sed -e "s&@exec_prefix@&${WORKDIR}/.lbdb&g" \
| sed -e "s&@libdir@&${WORKDIR}/.lbdb&g" \
@@ -154,7 +180,7 @@ lbdb_modules=(m_finger m_gpg m_inmail m_muttalias m_osx_addressbook m_vcf)
for mod in ${lbdb_modules}; do
cat share/lbdb/${mod}.sh.in \
| sed -e "s&@SH@&/usr/bin/env zsh&g" \
- | sed -e "s&@LBDB_VERSION@&0.38-postino&g" \
+ | sed -e "s&@LBDB_VERSION@&0.38-jaromail&g" \
| sed -e "s&@prefix@&${WORKDIR}/.lbdb&g" \
| sed -e "s&@exec_prefix@&${WORKDIR}/.lbdb&g" \
| sed -e "s&@libdir@&${WORKDIR}/.lbdb&g" \
@@ -178,7 +204,7 @@ ln -sf $WORKDIR/.lbdb $HOME/
# generate initial configuration
-src/postino update
+MAILDIRS=$MAILDIRS WORKDIR=$WORKDIR src/jaro update
case $OS in
@@ -187,10 +213,10 @@ case $OS in
cp -a build/osx/* $WORKDIR/bin
fi
touch $HOME/.profile
- cat $HOME/.profile | grep '^# Postino' > /dev/null
+ cat $HOME/.profile | grep '^# Jaromail' > /dev/null
if [ $? != 0 ]; then
cat <<EOF >> $HOME/.profile
-# Postino Installer addition on `date`
+# Jaromail Installer addition on `date`
export PATH=$WORKDIR/bin:\$PATH
# Finished adapting your PATH
EOF
@@ -201,14 +227,13 @@ EOF
;;
esac
-notice "Installation completed, now edit your personal settings:"
-act "$MAILDIRS/Configuration.txt"
+notice "Installation completed" #, now edit your personal settings:"
case $OS in
GNU)
;;
MAC)
- open /Applications/TextEdit.app $MAILDIRS/Configuration.txt
;;
+# open /Applications/TextEdit.app $WORKDIR/Configuration.txt
*)
;;
esac
diff --git a/share/lbdb/lbdb-fetchaddr.sh.in b/share/lbdb/lbdb-fetchaddr.sh.in
@@ -28,7 +28,7 @@ prefix=@prefix@
exec_prefix=@exec_prefix@
dotlock=@DOTLOCK@
fetchaddr=@libdir@/fetchaddr
-db=$HOME/.lbdb/m_inmail.list
+db=@LBDB_FILE@
datefmt='%Y-%m-%d %H:%M'
additional_param=""
diff --git a/share/lbdb/m_inmail.sh.in b/share/lbdb/m_inmail.sh.in
@@ -23,7 +23,7 @@ prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
-m_inmail_db=$HOME/.lbdb/m_inmail.list
+m_inmail_db=@libdir@/m_inmail.list
m_inmail_query()
{
diff --git a/share/mutt/general b/share/mutt/general
@@ -1,13 +1,6 @@
# Postino's generic config settings for Mutt
# originally used by Jaromil
-# specific configuration files
-# hardcoded path for now (TODO)
-source ~/.postino/.mutt/crypto
-source ~/.postino/.mutt/colors
-source ~/.postino/.mutt/formats
-source ~/.postino/.mutt/keybindings
-
###############
## SMTP options
unset use_8bitmime
diff --git a/share/procmail/pf-save.rc b/share/procmail/pf-save.rc
@@ -53,7 +53,7 @@
:0 c
* PF_DEST ?? [^ ]
* $! DEST ?? $PF_DEST
- |procmail -pm PF_RECURSE=yes pf-save.rc
+ |procmail -pm PF_RECURSE=yes $PMSRC/pf-save.rc
# If we were recursing, don't file multiple copies into DEFAULT
:0
diff --git a/src/jaro b/src/jaro
@@ -0,0 +1,746 @@
+#!/usr/bin/env zsh
+#
+# Jaro Mail, your humble and faithful electronic postman
+#
+# a tool to easily and privately handle your e-mail communication
+#
+# Copyleft (C) 2010-2012 Denis Roio <jaromil@dyne.org>
+#
+# This source code is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This source code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# Please refer to the GNU Public License for more details.
+#
+# You should have received a copy of the GNU Public License along with
+# this source code; if not, write to:
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+VERSION=0.6
+DATE=March/2012
+JAROMAILEXEC=$0
+typeset -a OLDARGS
+for arg in ${argv}; do OLDARGS+=($arg); done
+
+##########################
+# declare global variables
+
+QUIET=0
+DEBUG=1
+
+
+
+# which command to use when creating dirs
+mkdir="`which mkdir` -m 700 -p"
+
+
+##########################
+
+
+PARAM=()
+typeset -A global_opts
+typeset -A opts
+
+typeset -h name login host protocol port password auth cert
+
+autoload colors; colors
+
+# 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[white] $1" >&2; fi }
+error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[white] $1" >&2; fi }
+func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[white] $1" >&2; fi }
+act() {
+ if [[ $QUIET == 0 ]]; then
+ if [ "$1" = "-n" ]; then
+ print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2;
+ else
+ print "$fg_bold[white] . $fg_no_bold[white] $1" >&2;
+ fi
+ fi
+}
+
+# what operating system are we in? use os_detect()
+# simplifying modes of operation: GNU or MAC
+case $(uname) in
+ Linux) OS=GNU
+ notice "Jaro Mail v$VERSION running on GNU/Linux" ;;
+
+ Apple) OS=MAC
+ notice "Jaro Mail v$VERSION running on Mac/OSX" ;;
+
+ *) OS=GNU # default
+ error "Running on an unknown operating system, assuming GNU" ;;
+esac
+
+
+if [ -z $MAILDIRS ]; then
+ MAILDIRS=$HOME/Mail
+fi
+
+
+# default working dir
+WORKDIR=$MAILDIRS/jaro
+
+${=mkdir} $MAILDIRS
+${=mkdir} $WORKDIR
+
+
+if [ -r $WORKDIR/Configuration.txt ]; then
+ source $WORKDIR/Configuration.txt
+fi
+
+# make sure the directory is private
+chmod 700 $WORKDIR
+
+# make sure we have a temp and cache dir
+${=mkdir} "$WORKDIR/tmp"
+${=mkdir} "$WORKDIR/cache"
+${=mkdir} "$WORKDIR/log"
+
+PROCMAILDIR=$WORKDIR/.procmail
+MUTTDIR=$WORKDIR/.mutt
+
+# make sure tmp is wiped from sensitive data in case of sigINT
+TRAPINT() {
+ error "Caught signal, aborting operations."
+ for f in `ls $WORKDIR/tmp/`; do
+ ${=rm} $f
+ done
+
+ if [ "$DEBUG" = "1" ]; then
+ return 1
+ else
+ exit 1
+ fi
+}
+
+# we use pinentry
+# comes from gpg project and is secure
+# it also conveniently uses the right toolkit
+pin_entry() {
+ cat <<EOF | pinentry 2>/dev/null | awk '/^D / { sub(/^D /, ""); print }'
+OPTION ttyname=$TTY
+OPTION lc-ctype=$LANG
+SETTITLE Type your password
+SETDESC Type the password for $1 @ $2
+SETPROMPT Password:
+GETPIN
+EOF
+}
+
+
+check_bin() {
+ # check for required programs
+ for req in pinentry fetchmail procmail; do
+ which $req >/dev/null || die "Cannot find $req. Please install it." 1
+ done
+
+ # which wipe command to use
+ which wipe > /dev/null && rm="wipe -f -s" \
+ || which srm > /dev/null && rm="srm -fm" \
+ || rm="rm -f"
+}
+
+# retrieve a password for user @ domain
+# put it in variable password
+# up to the caller to unset it after use
+ask_password() {
+ case $OS in
+ MAC)
+ func "Looking for password in keyring: $1 @ $2"
+ security find-internet-password -a $1 -s $2 > /dev/null
+ if [ $? != 0 ]; then # its a new password
+ password=`pin_entry $1 @ $2`
+ 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"
+ password=`security find-internet-password -a $1 -s $2 -g \
+ 2>&1| awk '/^password:/ { print $2 }' | sed -e 's/"//g'`
+ fi
+ return 0
+ ;;
+ GNU)
+ password=`pin_entry $login $host`
+ return 0
+ ;;
+ *)
+ error "Unknown system, can't figure out how to handle passwords"
+ return 1
+ esac
+}
+
+switch_identity() {
+ act "switch to identity: $name <$login>"
+ rm -f $MUTTDIR/identity
+ cat <<EOF > $MUTTDIR/identity
+set hostname = $host
+set realname = "$name"
+set from = "$name <$login>"
+EOF
+}
+
+
+option_is_set() {
+ #First argument, the option (something like "-s")
+ #Second (optional) argument: if it's "out", command will print it out 'set'/'unset'
+ # This is useful for if conditions
+ #Return 0 if is set, 1 otherwise
+ [[ -n ${(k)opts[$1]} ]];
+ r=$?
+ if [[ $2 == out ]]; then
+ if [[ $r == 0 ]]; then
+ echo 'set'
+ else
+ echo 'unset'
+ fi
+ fi
+ return $r;
+}
+option_value() {
+ #First argument, the option (something like "-s")
+ <<< ${opts[$1]}
+}
+maildirmake() {
+
+ if [ -z $1 ]; then
+ error "internal error: missing argument for maildirmake"
+ return
+ fi
+
+ if [ -r $1 ]; then
+ func "maildir exists: $1"
+ return
+ fi
+
+ ${=mkdir} ${1}/cur
+ ${=mkdir} ${1}/new
+ ${=mkdir} ${1}/tmp
+
+}
+# arg 1 : account name, es: imap.gmail
+read_account() {
+ adir=$WORKDIR/Accounts
+ acct=$1
+ type="`echo $1|cut -d. -f1`"
+ # 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"
+ return 1
+ fi
+ atmp=$WORKDIR/tmp/$1
+ case $type in
+ imap|smtp)
+ ttmp=`cat $acct | awk '
+ /^#/ { next }
+ /^name/ { for(i=2;i<=NF;i++) printf "%s ", $i; printf ";" }
+ /^host/ { printf "%s;", $2 }
+ /^login/ { printf "%s;", $2 }
+ /^transport/ { printf "%s;", $2 }
+ /^port/ { printf "%s;", $2 }
+ /^password/ {printf "%s;", $2 }
+ /^auth/ { printf "%s;", $2 }
+ /^cert/ { printf "%s;", $2 }
+ '`
+ name=${ttmp[(ws:;:)1]}
+ host=${ttmp[(ws:;:)2]}
+ login=${ttmp[(ws:;:)3]}
+ transport=${ttmp[(ws:;:)4]}
+ port=${ttmp[(ws:;:)5]}
+ password=${ttmp[(ws:;:)6]}
+ auth=${ttmp[(ws:;:)7]}
+ cert=${ttmp[(ws:;:)8]}
+
+ # if not present the field is equal to the preceeding one
+ [ "$cert" = "$auth" ] && unset cert
+ [ "$auth" = "$password" ] && unset auth
+ [ "$password" = "$port" ] && unset password
+
+ func "name: $name"
+ func "host: $host"
+ func "login: $login"
+ func "trans: $transport"
+ func "port: $port"
+ func "cert: $cert"
+ func "auth: $auth"
+ ;;
+ *)
+ error "Account type \"$type\" not recognized."
+ return 1
+ ;;
+ esac
+ return 0
+}
+
+queue() {
+ local base;
+ local tmp;
+ local mailfile;
+ local msmtpfile;
+
+ # add mails to the sendout queue
+ umask 007
+ ${=mkdir} $MAILDIRS/outbox
+ cd $MAILDIRS/outbox || return 1
+ notice "Adding mail to the outbox 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
+ # mail per second.
+ base="`date +%Y-%m-%d-%H.%M.%S`"
+ func "[$base] queue called with params: ${PARAM[@]}"
+
+ if [ -f "$base.mail" -o -f "$base.msmtp" ]; then
+ tmp="$base"
+ i=1
+ while [ -f "$tmp-$i.mail" -o -f "$tmp-$i.msmtp" ]; do
+ i=`expr $i + 1`
+ done
+ base="$base-$i"
+ fi
+ mailfile="$base.mail"
+ msmtpfile="$base.msmtp"
+ # Write command line to $MSMTPFILE
+ echo "$@" > "$msmtpfile"
+ # Write the mail to $MAILFILE
+ cat > "$mailfile"
+ cd -
+ return 0
+}
+
+
+###########
+# FETCHMAIL
+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
+ fi
+ if ! [ -r $PROCMAILDIR/rc ]; then
+ act "updating procmail configuration"
+ update
+ fi
+ for a in ${all}; do
+ read_account `basename $a`
+ 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"
+ unset password all
+ return 1
+ fi
+
+ cat <<EOF > $WORKDIR/tmp/$host.fetch
+poll $host with proto IMAP user "$login" there with password "$password"
+keep fetchall and ssl warnings 3600 and wants mda "procmail -m $PROCMAILDIR/rc"
+antispam 571 550 501 554
+EOF
+ unset password
+ # this function generates a fetchmail configuration and downloads emails
+ act "launching fetchmail"
+ ( sleep 2; act "deleting temporary files"
+ ${=rm} $WORKDIR/tmp/$host.fetch ) &
+ fetchmail -f $WORKDIR/tmp/$host.fetch
+ done
+ return 0
+}
+
+######
+# SMTP
+send() {
+ # this function should send all mails in queue
+ notice "Sending out all mails in outbox queue"
+
+ local -aU smtp_set #behave like a set; that is, an array with unique elements
+ smtp_set="${SMTP_LOGIN};${SMTP_ADDRESS};${SMTP_PORT}"
+ for s in ${(f)smtp_set}; do
+ slogin="${s[(ws:;:)1]}"
+ shost="${s[(ws:;:)2]}"
+ sport="${s[(ws:;:)3]}"
+ func "SMTP: $slogin $shost:$sport"
+ done
+ cd ${MAILDIRS}/outbox
+ mailnum=`ls | grep 'mail$' | wc -l`
+ mailnum=${mailnum// /} # trim whitespace
+ if [ "$mailnum" = "0" ]; then
+ act "Outbox is empty, no mails to send."
+ return
+ fi
+ ask_password ${slogin} ${shost}
+ cat <<EOF > $WORKDIR/tmp/send
+account default
+from ${USER}@${DOMAIN}
+tls on
+host ${SMTP_ADDRESS}
+port ${SMTP_PORT}
+tls_starttls on
+tls_certcheck off
+logfile ${WORKDIR}/log/msmtp.log
+auth plain
+user ${SMTP_LOGIN}
+password ${password}
+EOF
+ unset password
+ chmod 600 $WORKDIR/tmp/send
+ mailaddr=`cat *.mail | grep '^To:'`
+ notice "${mailnum} mails to send:"
+ for mail in *.mail; do
+ act "`cat ${mail} | grep '^To:'`"
+ smtp=`echo ${mail} | sed -e 's/mail/msmtp/'`
+ msmtp -C $WORKDIR/tmp/send "`cat ${smtp}`" < "${mail}"
+ if [ $? != 0 ]; then
+ error "Error sending mail, skipped"
+ else
+ act "Mail sent succesfully"
+ rm -f ${mail} ${smtp}
+ fi
+ done
+ cd -
+ ${=rm} ${WORKDIR}/tmp/send
+ return 0
+}
+
+######
+# PEEK
+# this function will open the MTA to the imap server without fetching mails locally
+peek() {
+ adir=$WORKDIR/Accounts
+ if [ -z $1 ]; then
+ error "Peek needs a specific imap account to login."
+ act "List of accounts you have configured:"
+ for i in `find $adir -name "imap*"`; do act "`basename $i`"; done
+ return 1
+ fi
+ acct=$1
+ typeset -al all
+ for a in `find $adir -name "imap.$acct*"`; do all+=($a); done
+ read_account `basename ${all[1]}`
+ notice "Peek into remote imap account $name"
+ switch_identity
+ case $transport in
+ ssl) iproto="imaps" ;;
+ plain) iproto="imap" ;;
+ esac
+ # escape at sign in login
+ ilogin=`echo $login | sed 's/@/\\@/'`
+ mutt -F $MUTTDIR/rc -f ${iproto}://${ilogin}@${host}
+ # TODO automatic input of password in mutt
+ return $?
+}
+
+update() {
+ notice "Updating all configurations and filters"
+ # this function should:
+ # parse all filters
+ # generate procmailrc
+ # generate muttrc
+ # backup what's too old in the maildirs
+ # ...
+
+ # debug configuration
+ func "MAILDIRS: $MAILDIRS"
+ func "WORKDIR: $WORKDIR"
+ func "MUTTDIR: $MUTTDIR"
+ func "PROCMAILDIR: $PROCMAILDIR"
+
+ # make sure maildirs where to put mails exist
+ ${=mkdir} $MAILDIRS
+ maildirmake $MAILDIRS/known
+ maildirmake $MAILDIRS/sent
+ maildirmake $MAILDIRS/priv
+ maildirmake $MAILDIRS/postponed
+ maildirmake $MAILDIRS/unsorted
+ ${=mkdir} $MAILDIRS/outbox
+
+ ######
+ # MUTT
+ ${=mkdir} $MUTTDIR
+ rm -f $MUTTDIR/rc
+ cat<<EOF > $MUTTDIR/rc
+# mutt config generated by Jaro Mail
+unset use_domain
+set folder = $MAILDIRS
+set spoolfile = $MAILDIRS/known/
+set record = $MAILDIRS/sent/
+set postponed= $MAILDIRS/postponed/
+set tmpdir = $WORKDIR/tmp
+set query_command = "jaro query '%s'"
+set sendmail = "jaro queue"
+set header_cache= $WORKDIR/cache
+set maildir_header_cache_verify=no
+set editor = "$EDITOR"
+set mailcap_path = "$WORKDIR/mailcap"
+EOF
+
+ if [ -r $MUTTDIR/general ]; then
+ echo "# user tweaked configuration" >> $MUTTDIR/rc
+ echo "source ${MUTTDIR}/general" >> $MUTTDIR/rc
+ fi
+
+cat <<EOF >> $MUTTDIR/rc
+# mailboxes in order of priority
+source $MUTTDIR/mboxes
+
+# specific configuration files
+# hardcoded path for now (TODO)
+source $MUTTDIR/crypto
+source $MUTTDIR/colors
+source $MUTTDIR/formats
+source $MUTTDIR/keybindings
+source $MUTTDIR/identity
+## end of Jaro Mail generated muttrc
+####################################
+
+EOF
+
+
+ # just the header, will be completed later in procmail loop
+ rm -f $MUTTDIR/mboxes
+ echo -n "mailboxes +priv" > $MUTTDIR/mboxes
+
+ ##########
+ # PROCMAIL
+ ${=mkdir} $PROCMAILDIR
+ rm -f $PROCMAILDIR/rc
+ touch $PROCMAILDIR/rc
+ cat<<EOF >> $PROCMAILDIR/rc
+# procmail configuration file generated by Jaro Mail
+MAILDIR=$MAILDIRS
+DEFAULT=unsorted/
+VERBOSE=off
+LOGFILE=$WORKDIR/log/procmail.log
+SHELL = /bin/sh # VERY IMPORTANT
+UMASK = 007 # James Bond :-)
+LINEBUF = 8192 # avoid procmail choke
+
+# Using Procmail Module Library http://sf.net/projects/pm-lib
+PMSRC = $PROCMAILDIR
+# Load the central initial startup code.
+INCLUDERC = \$PMSRC/pm-javar.rc
+PF_DEST = "" # clear these vars
+PF_FROM = ""
+# don't save multiple copies
+PF_RECURSE = yes
+:0
+* ? test \$PMSRC/pf-chkto.rc
+{
+EOF
+
+ #######
+ echo "# filters generated from Accounts" >> $PROCMAILDIR/rc
+ typeset -al accts
+ for f in `cat $WORKDIR/Accounts/* | awk '/^login/ { print $2 }'`; do
+ echo "ADDR=${f}\tDEST=priv\tINCLUDERC=\$PMSRC/pf-chkto.rc" >> $PROCMAILDIR/rc
+ done
+
+ #######
+ 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]}"
+ action="${f[(ws:;:)3]}"
+ destination="${f[(ws:;:)4]}"
+ case $header in
+ to)
+ print "ADDR=${address}\tDEST=${destination}\tINCLUDERC=\$PMSRC/pf-chkto.rc" \
+ >> $PROCMAILDIR/rc
+ ;;
+ from)
+ print "ADDR=${address}\tDEST=${destination}\tINCLUDERC=\$PMSRC/pf-check.rc" \
+ >> $PROCMAILDIR/rc
+ ;;
+ *)
+ error "unsupported filter: $header (skipped)"
+ ;;
+ esac
+ # MUTT (generate mailboxes priority this parser)
+ echo " \\" >> $MUTTDIR/mboxes
+ echo -n " +${destination} " >> $MUTTDIR/mboxes
+ done
+
+ echo " \\" >> $MUTTDIR/mboxes
+ echo " +unsorted" >> $MUTTDIR/mboxes
+ uniq $MUTTDIR/mboxes > $WORKDIR/tmp/mboxes
+ mv $WORKDIR/tmp/mboxes $MUTTDIR/mboxes
+ rm -f $WORKDIR/tmp/mboxes
+
+ cat <<EOF >> $PROCMAILDIR/rc
+}
+
+# save the mails
+:0
+* PF_DEST ?? .
+* ? test \$PMSRC/pf-save.rc
+{ INCLUDERC=\$PMSRC/pf-save.rc }
+
+# if the sender is known (ldbd recognizes it) then put mail in high priority 'known'
+:0 w:
+* ? formail -x"From:" | head -n1 | tr 'A-Z' 'a-z' | sed 's/.*\W\([0-9a-z_.-]\+@[0-9a-z_.-]\+\).*/\1/' | xargs jaro query
+known/
+
+# if got here, go to unsorted
+
+# save the mails
+:0
+* PF_DEST ?? .
+* ? test \$PMSRC/pf-save.rc
+{ INCLUDERC=\$PMSRC/pf-save.rc }
+
+EOF
+ return 0
+}
+
+usage() {
+ cat <<EOF
+Jaro Mail $VERSION - your humble and faithful electronic postman
+
+ Copyright (C) 2010-2012 Dyne.org Foundation, License GNU GPL v3+
+ This is free software: you are free to change and redistribute it
+ The latest Tomb sourcecode is published on <http://tomb.dyne.org>
+
+Syntact: jaro command [options] [account]
+
+Commands:
+
+ queue
+ send
+ peek
+ fetch
+ sync
+
+Options:
+
+ -h print this help
+ -v version information for this tool
+ -q run quietly without printing informations
+ -D print debugging information at runtime
+
+
+For more informations on Jaro Mail read the manual: man jaro
+Please report bugs on <http://bugs.dyne.org>.
+EOF
+}
+
+
+main()
+ {
+ local -A subcommands_opts
+ ### Options configuration
+ #Hi, dear developer! Are you trying to add a new subcommand, or to add some options?
+ #Well, keep in mind that:
+ # 1. An option CAN'T have differente meanings/behaviour in different subcommands.
+ # For example, "-s" means "size" and accept an argument. If you are tempted to add
+ # an option "-s" (that means, for example "silent", and doesn't accept an argument)
+ # DON'T DO IT!
+ # There are two reasons for that:
+ # 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)
+ subcommands_opts[__default]=""
+ subcommands_opts[queue]=""
+ subcommands_opts[fetch]=""
+ subcommands_opts[send]=""
+ subcommands_opts[peek]=""
+ subcommands_opts[update]=""
+ subcommands_opts[query]=""
+ subcommands_opts[source]=""
+# subcommands_opts[mount]=${subcommands_opts[open]}
+# subcommands_opts[create]="s: -size=s -ignore-swap k: -key=k"
+ ### 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
+ done
+ local -a oldstar
+ oldstar=($argv)
+ zparseopts -M -E -D -Adiscardme ${every_opts}
+ unset discardme
+ subcommand=$1
+ if [[ -z $subcommand ]]; then
+ subcommand="__default"
+ fi
+ if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand
+ error "Subcommand '$subcommand' doesn't exist"
+ exit 127
+ fi
+ argv=(${oldstar})
+ unset oldstar
+
+ ### Parsing global + command-specific options
+ # 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}
+ if [[ $? != 0 ]]; then
+ error "Some error occurred during option processing."
+ exit 127
+ fi
+ fi
+ #build PARAM (array of arguments) and check if there are unrecognized options
+ local ok=0
+# PARAM=()
+ for arg in $*; do
+ if [[ $arg == '--' || $arg == '-' ]]; then
+ ok=1
+ continue #it shouldnt be appended to PARAM
+ elif [[ $arg[1] == '-' ]]; then
+ if [[ $ok == 0 ]]; then
+ error "unrecognized option $arg"
+ exit 127
+ fi
+ fi
+ PARAM+=$arg
+ done
+ #first parameter actually is the subcommand: delete it and shift
+ if [[ $subcommand != '__default' ]]; then
+ PARAM[1]=()
+ shift
+ fi
+ ### End parsing command-specific options
+
+ if option_is_set -v; then act "Jaro Mail - $VERSION"; 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
+
+ case "$subcommand" in
+ queue) queue ${PARAM} ;;
+ fetch) fetch ${PARAM} ;;
+ send) send ${PARAM} ;;
+ peek) peek ${PARAM} ;;
+ update) update ;;
+ query) ${WORKDIR}/.lbdb/lbdbq ${PARAM} ;;
+ 'source') return 0 ;;
+ __default) ;;
+ *) error "command \"$subcommand\" not recognized"
+ act "try -h for help"
+ return 1
+ ;;
+ esac
+ return 0
+}
+
+check_bin
+main $@
diff --git a/src/postino b/src/postino
@@ -1,569 +0,0 @@
-#!/usr/bin/env zsh
-#
-# Postino, your humble and faithful electronic postman
-#
-# a tool to easily and privately handle your e-mail communication
-#
-# Copyleft (C) 2010-2012 Denis Roio <jaromil@dyne.org>
-#
-# This source code is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This source code is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# Please refer to the GNU Public License for more details.
-#
-# You should have received a copy of the GNU Public License along with
-# this source code; if not, write to:
-# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-VERSION=0.6
-DATE=March/2012
-POSTINOEXEC=$0
-typeset -a OLDARGS
-for arg in ${argv}; do OLDARGS+=($arg); done
-
-##########################
-# declare global variables
-
-QUIET=0
-DEBUG=1
-
-
-
-# which command to use when creating dirs
-mkdir="`which mkdir` -m 700 -p"
-
-
-##########################
-
-
-PARAM=()
-typeset -A global_opts
-typeset -A opts
-
-autoload colors; colors
-
-# 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[white] $1" >&2; fi }
-error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[white] $1" >&2; fi }
-func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[white] $1" >&2; fi }
-act() {
- if [[ $QUIET == 0 ]]; then
- if [ "$1" = "-n" ]; then
- print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2;
- else
- print "$fg_bold[white] . $fg_no_bold[white] $1" >&2;
- fi
- fi
-}
-
-# what operating system are we in? use os_detect()
-# simplifying modes of operation: GNU or MAC
-case $(uname) in
- Linux) OS=GNU
- act "Running on GNU/Linux" ;;
-
- Apple) OS=MAC
- act "Running on Mac/OSX" ;;
-
- *) OS=GNU # default
- error "Running on an unknown operating system, assuming GNU" ;;
-esac
-
-
-if ! [ -r $HOME/Mail/Configuration.txt ]; then return; fi
-
-source $HOME/Mail/Configuration.txt
-
-# default working dir
-if [ -z $POSTINO_DIR ]; then
- export WORKDIR=$HOME/.postino
-else
- export WORKDIR=$POSTINO_DIR
-fi
-
-
-# make sure the directory is private
-chmod 700 $WORKDIR
-if [ $? != 0 ]; then
- error "Postino directory $WORKDIR is not private, set permissions to 700"
- error "Refusing to proceed."
- exit 1
-fi
-
-# make sure we have a temp and cache dir
-${=mkdir} "$WORKDIR/tmp"
-${=mkdir} "$WORKDIR/cache"
-
-
-# make sure tmp is wiped from sensitive data in case of sigINT
-TRAPINT() {
- error "Caught signal, aborting operations."
- for f in `ls $WORKDIR/tmp/`; do
- srm -fm $f
- done
- exit 1
-}
-
-# we use pinentry
-# comes from gpg project and is secure
-# it also conveniently uses the right toolkit
-pin_entry() {
- cat <<EOF | pinentry 2>/dev/null | awk '/^D / { sub(/^D /, ""); print }'
-OPTION ttyname=$TTY
-OPTION lc-ctype=$LANG
-SETTITLE Type your password
-SETDESC Type the password for $1 @ $2
-SETPROMPT Password:
-GETPIN
-EOF
-}
-
-# retrieve a password for user @ domain
-# put it in variable password
-# up to the caller to unset it after use
-ask_password() {
- func "Looking for password in keyring: $1 @ $2"
- security find-internet-password -a $1 -s $2 > /dev/null
- if [ $? != 0 ]; then # its a new password
- export password=`pin_entry $1 @ $2`
- 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"
- export password=`security find-internet-password -a $1 -s $2 -g \
- 2>&1| awk '/^password:/ { print $2 }' | sed -e 's/"//g'`
- fi
-}
-
-option_is_set() {
- #First argument, the option (something like "-s")
- #Second (optional) argument: if it's "out", command will print it out 'set'/'unset'
- # This is useful for if conditions
- #Return 0 if is set, 1 otherwise
- [[ -n ${(k)opts[$1]} ]];
- r=$?
- if [[ $2 == out ]]; then
- if [[ $r == 0 ]]; then
- echo 'set'
- else
- echo 'unset'
- fi
- fi
- return $r;
-}
-option_value() {
- #First argument, the option (something like "-s")
- <<< ${opts[$1]}
-}
-maildirmake() {
-
- if [ -z $1 ]; then
- error "internal error: missing argument for maildirmake"
- return
- fi
-
- if [ -r $1 ]; then
- func "maildir exists: $1"
- return
- fi
-
- ${=mkdir} ${1}/cur
- ${=mkdir} ${1}/new
- ${=mkdir} ${1}/tmp
-
-}
-
-queue() {
- local base;
- local tmp;
- local mailfile;
- local msmtpfile;
-
- # add mails to the sendout queue
- umask 007
- ${=mkdir} $MAILDIRS/outbox
- cd $MAILDIRS/outbox || return 1
- notice "Adding mail to the outbox 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
- # mail per second.
- base="`date +%Y-%m-%d-%H.%M.%S`"
- func "[$base] queue called with params: ${PARAM[@]}"
-
- if [ -f "$base.mail" -o -f "$base.msmtp" ]; then
- tmp="$base"
- i=1
- while [ -f "$tmp-$i.mail" -o -f "$tmp-$i.msmtp" ]; do
- i=`expr $i + 1`
- done
- base="$base-$i"
- fi
- mailfile="$base.mail"
- msmtpfile="$base.msmtp"
- # Write command line to $MSMTPFILE
- echo "$@" > "$msmtpfile"
- # Write the mail to $MAILFILE
- cat > "$mailfile"
- cd -
- return 0
-}
-
-
-###########
-# FETCHMAIL
-fetch() {
- notice "Fetching mails from server"
- # this function generates a fetchmail configuration and downloads emails
- local -aU imap_set # behave like a set
- imap_set="${IMAP_LOGIN};${IMAP_ADDRESS}"
- for i in ${(f)imap_set}; do
- ilogin="${i[(ws:;:)1]}"
- ihost="${i[(ws:;:)2]}"
- func "IMAP: $ilogin $ihost"
- done
- if ! [ -r $PROCMAILDIR/rc ]; then
- act "Syncing configuration"
- sync
- fi
- ask_password $ilogin $ihost
- touch $WORKDIR/tmp/fetch; chmod 600 $WORKDIR/tmp/fetch
- cat <<EOF > $WORKDIR/tmp/fetch
-poll $ihost with proto IMAP user "$ilogin" there with password "$password"
-keep and ssl warnings 3600 and wants mda "procmail -m $PROCMAILDIR/rc"
-antispam 571 550 501 554
-EOF
- unset password
-
- act "Launching fetchmail"
- fetchmail -f $WORKDIR/tmp/fetch
- srm -fm $WORKDIR/tmp/fetch
- return 0
-}
-
-######
-# SMTP
-send() {
- # this function should send all mails in queue
- notice "Sending out all mails in outbox queue"
-
- local -aU smtp_set #behave like a set; that is, an array with unique elements
- smtp_set="${SMTP_LOGIN};${SMTP_ADDRESS};${SMTP_PORT}"
- for s in ${(f)smtp_set}; do
- slogin="${s[(ws:;:)1]}"
- shost="${s[(ws:;:)2]}"
- sport="${s[(ws:;:)3]}"
- func "SMTP: $slogin $shost:$sport"
- done
- cd ${MAILDIRS}/outbox
- mailnum=`ls | grep 'mail$' | wc -l`
- mailnum=${mailnum// /} # trim whitespace
- if [ "$mailnum" = "0" ]; then
- act "Outbox is empty, no mails to send."
- return
- fi
- ask_password ${slogin} ${shost}
- cat <<EOF > $WORKDIR/tmp/send
-account default
-from ${USER}@${DOMAIN}
-tls on
-host ${SMTP_ADDRESS}
-port ${SMTP_PORT}
-tls_starttls on
-tls_certcheck off
-logfile ${WORKDIR}/msmtp.log
-auth plain
-user ${SMTP_LOGIN}
-password ${password}
-EOF
- unset password
- chmod 600 $WORKDIR/tmp/send
- mailaddr=`cat *.mail | grep '^To:'`
- notice "${mailnum} mails to send:"
- for mail in *.mail; do
- act "`cat ${mail} | grep '^To:'`"
- smtp=`echo ${mail} | sed -e 's/mail/msmtp/'`
- msmtp -C $WORKDIR/tmp/send "`cat ${smtp}`" < "${mail}"
- if [ $? != 0 ]; then
- error "Error sending mail, skipped"
- else
- act "Mail sent succesfully"
- rm -f ${mail} ${smtp}
- fi
- done
- cd -
- srm -fm ${WORKDIR}/tmp/send
- return 0
-}
-
-######
-# IMAP
-peek() {
- notice "Peeking into remote mailbox"
- # this function will open the MTA to the imap server without fetching mails locally
- local -aU imap_set #behave like a set; that is, an array with unique elements
- imap_set="${IMAP_LOGIN};${IMAP_ADDRESS}"
- for i in ${(f)imap_set}; do
- ilogin="${i[(ws:;:)1]}"
- ihost="${i[(ws:;:)2]}"
- func "IMAP: $ilogin $ihost"
- done
- # escape at sign in login
- mutt -f imaps://`echo $ilogin | sed 's/@/\\@/'`@${ihost}
-}
-
-update() {
- notice "Updating all configurations and filters"
- # this function should:
- # parse all filters
- # generate procmailrc
- # generate muttrc
- # backup what's too old in the maildirs
- # ...
-
- # debug configuration
- func "WORKDIR: $WORKDIR"
- func "MAILDIRS: $MAILDIRS"
-
- # make sure maildirs where to put mails exist
- ${=mkdir} $MAILDIRS
- maildirmake $MAILDIRS/known
- maildirmake $MAILDIRS/sent
- maildirmake $MAILDIRS/priv
- maildirmake $MAILDIRS/postponed
- maildirmake $MAILDIRS/unsorted
- ${=mkdir} $MAILDIRS/outbox
-
- ######
- # MUTT
- if ! [ -z $MUTTDIR ]; then
- ${=mkdir} $MUTTDIR
- rm -f $MUTTDIR/rc
- cat<<EOF > $MUTTDIR/rc
-# mutt config generated by postino
-unset use_domain
-set hostname = $DOMAIN
-set realname = $FULLNAME
-set from = "$FULLNAME <${USER}@${DOMAIN}>"
-set folder = $MAILDIRS
-set spoolfile = $MAILDIRS/known/
-set record = $MAILDIRS/sent/
-set postponed= $MAILDIRS/postponed/
-set tmpdir = $WORKDIR/tmp
-set query_command = "postino query '%s'"
-set sendmail = "postino queue"
-set header_cache= $MUTTDIR/cache
-set maildir_header_cache_verify=no
-set editor = "$EDITOR"
-set mailcap_path = "$WORKDIR/mailcap"
-
-# mailboxes in order of priority
-source $MUTTDIR/mboxes
-EOF
-
- if [ -r $MUTTDIR/general ]; then
- echo "# user tweaked configuration" >> $MUTTDIR/rc
- echo "source ${MUTTDIR}/general" >> $MUTTDIR/rc
- fi
- echo "## end of generated muttrc" >> $MUTTDIR/rc
- echo "##########################" >> $MUTTDIR/rc
- echo >> $MUTTDIR/rc
-
- # just the header, will be completed later in procmail loop
- rm -f $WORKDIR/mutt/mboxes
- echo -n "mailboxes +priv" > $MUTTDIR/mboxes
- fi
-
- ##########
- # PROCMAIL
- ${=mkdir} $PROCMAILDIR
- rm -f $PROCMAILDIR/rc
- touch $PROCMAILDIR/rc
- cat<<EOF >> $PROCMAILDIR/rc
-# procmail configuration file generated by postino
-MAILDIR=$MAILDIRS
-DEFAULT=unsorted/
-VERBOSE=off
-LOGFILE=$PROCMAILDIR/log
-SHELL = /bin/sh # VERY IMPORTANT
-UMASK = 007 # James Bond :-)
-LINEBUF = 8192 # avoid procmail choke
-src/postino update
-# Using Procmail Module Library http://sf.net/projects/pm-lib
-PMSRC = $PROCMAILDIR
-# Load the central initial startup code.
-INCLUDERC = $PMSRC/pm-javar.rc
-PF_DEST = "" # clear these vars
-PF_FROM = ""
-# don't save multiple copies
-PF_RECURSE = yes
-:0
-* ? test pf-chkto.rc
-{
-# filters generated from postino Filters.txt
-EOF
- for f in `cat $MAILDIRS/Filters.txt | awk '/^#/ {next} /^./ { print $1 ";" $2 ";" $3 ";" $4 }'`; do
- header="${f[(ws:;:)1]}"
- address="${f[(ws:;:)2]}"
- action="${f[(ws:;:)3]}"
- destination="${f[(ws:;:)4]}"
- case $header in
- to)
- print "ADDR=${address}\tDEST=${destination}\tINCLUDERC=$PMSRC/pf-chkto.rc" \
- >> $PROCMAILDIR/rc
- ;;
- from)
- print "ADDR=${address}\tDEST=${destination}\tINCLUDERC=$PMSRC/pf-check.rc" \
- >> $PROCMAILDIR/rc
- ;;
- *)
- error "unsupported filter: $header (skipped)"
- ;;
- esac
- # MUTT (generate mailboxes priority this parser)
- echo " \\" >> $MUTTDIR/mboxes
- echo -n " +${destination} " >> $MUTTDIR/mboxes
- done
-
- echo " \\" >> $MUTTDIR/mboxes
- echo " +unsorted" >> $MUTTDIR/mboxes
- uniq $MUTTDIR/mboxes > $WORKDIR/tmp/mboxes
- mv $WORKDIR/tmp/mboxes $MUTTDIR/mboxes
- rm -f $WORKDIR/tmp/mboxes
-
- cat <<EOF >> $PROCMAILDIR/rc
-}
-
-# save the mails
-:0
-* PF_DEST ?? .
-* ? test $PMSRC/pf-save.rc
-{ INCLUDERC=$PMSRC/pf-save.rc }
-
-# if the sender is known (ldbd recognizes it) then put mail in high priority 'known'
-:0 w:
-* ? formail -x"From:" | head -n1 | tr 'A-Z' 'a-z' | sed 's/.*\W\([0-9a-z_.-]\+@[0-9a-z_.-]\+\).*/\1/' | xargs lbdbq
-known/
-
-# if got here, go to unsorted
-
-# save the mails
-:0
-* PF_DEST ?? .
-* ? test $PMSRC/pf-save.rc
-{ INCLUDERC=$PMSRC/pf-save.rc }
-
-EOF
- return 0
-}
-
-
-main()
- {
- local -A subcommands_opts
- ### Options configuration
- #Hi, dear developer! Are you trying to add a new subcommand, or to add some options?
- #Well, keep in mind that:
- # 1. An option CAN'T have differente meanings/behaviour in different subcommands.
- # For example, "-s" means "size" and accept an argument. If you are tempted to add
- # an option "-s" (that means, for example "silent", and doesn't accept an argument)
- # DON'T DO IT!
- # There are two reasons for that:
- # 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)
- subcommands_opts[__default]=""
- subcommands_opts[queue]=""
- subcommands_opts[fetch]=""
- subcommands_opts[send]=""
- subcommands_opts[peek]=""
- subcommands_opts[update]=""
- subcommands_opts[query]=""
- subcommands_opts[source]=""
-# subcommands_opts[mount]=${subcommands_opts[open]}
-# subcommands_opts[create]="s: -size=s -ignore-swap k: -key=k"
- ### 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
- done
- local -a oldstar
- oldstar=($argv)
- zparseopts -M -E -D -Adiscardme ${every_opts}
- unset discardme
- subcommand=$1
- if [[ -z $subcommand ]]; then
- subcommand="__default"
- fi
- if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand
- error "Subcommand '$subcommand' doesn't exist"
- exit 127
- fi
- argv=(${oldstar})
- unset oldstar
-
- ### Parsing global + command-specific options
- # 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}
- if [[ $? != 0 ]]; then
- error "Some error occurred during option processing."
- exit 127
- fi
- fi
- #build PARAM (array of arguments) and check if there are unrecognized options
- local ok=0
-# PARAM=()
- for arg in $*; do
- if [[ $arg == '--' || $arg == '-' ]]; then
- ok=1
- continue #it shouldnt be appended to PARAM
- elif [[ $arg[1] == '-' ]]; then
- if [[ $ok == 0 ]]; then
- error "unrecognized option $arg"
- exit 127
- fi
- fi
- PARAM+=$arg
- done
- #first parameter actually is the subcommand: delete it and shift
- if [[ $subcommand != '__default' ]]; then
- PARAM[1]=()
- shift
- fi
- ### End parsing command-specific options
-
- if option_is_set -v; then act "Postino - $VERSION"; fi
- if option_is_set -h; then error "TODO usage here"; fi
- if option_is_set -q; then QUIET=1; fi
- if option_is_set -D; then func "Debug messages ON"; DEBUG=1; fi
-
- case "$subcommand" in
- queue) queue ${PARAM} ;;
- fetch) fetch ;;
- send) send ;;
- peek) peek ;;
- update) update ;;
- query) lbdbq $@ ;;
- 'source') return 0 ;;
- __default) ;;
- *) error "command \"$subcommand\" not recognized"
- act "try -h for help"
- return 1
- ;;
- esac
- return 0
-}
-
-main $@