jaromail

a commandline tool to easily and privately handle your e-mail
git clone git://parazyd.org/jaromail.git
Log | Files | Refs | Submodules | README

jaro (22894B)


      1 #!/usr/bin/env zsh
      2 #
      3 # Jaro Mail, your humble and faithful electronic postman
      4 #
      5 # a tool to easily and privately handle your e-mail communication
      6 #
      7 # Copyright (C) 2010-2015 Dyne.org Foundation
      8 #
      9 # JaroMail is designed, written and maintained by Denis Roio <jaromil@dyne.org>
     10 #
     11 # This source  code is free  software; you can redistribute  it and/or
     12 # modify it under the terms of  the GNU Public License as published by
     13 # the Free  Software Foundation; either  version 3 of the  License, or
     14 # (at your option) any later version.
     15 #
     16 # This source code is distributed in  the hope that it will be useful,
     17 # but  WITHOUT ANY  WARRANTY;  without even  the  implied warranty  of
     18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     19 # Please refer to the GNU Public License for more details.
     20 #
     21 # You should have received a copy of the GNU Public License along with
     22 # this source code; if not, write to:
     23 # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     24 
     25 PROGRAM=jaro-mail
     26 VERSION=4.3-dev
     27 DATE=Mar/2017
     28 JAROMAILEXEC=$0
     29 
     30 # default permission on files
     31 umask 066
     32 
     33 # honor quiet and debug flags as early as possible
     34 if [[ ${@} == *-q* ]]; then QUIET=1; fi
     35 if [[ ${@} == *-D* ]]; then DEBUG=1; fi
     36 
     37 
     38 # check if we are inside the directory
     39 if [ -r jaro/bin/jaro ]; then
     40     MAILDIRS=`pwd`
     41 # check if we are on OSX
     42 elif [ -r /Applications/JaroMail.app ]; then
     43     MAILDIRS="$HOME/Library/Application Support/JaroMail"
     44 # else use GNU/Linux default
     45 else
     46     MAILDIRS=$HOME/Mail
     47 fi
     48 
     49 # end override
     50 MAILDIRS=${JAROMAILDIR:-$MAILDIRS}
     51 
     52 
     53 # check if we are testing from source
     54 if [[ -r ../src/jaro ]]; then
     55     WORKDIR="../src"
     56 # check if we are on OSX
     57 elif [[ -r /Applications/JaroMail.app/Contents/Resources/jaro ]]; then
     58     WORKDIR="/Applications/JaroMail.app/Contents/Resources/jaro"
     59 elif [[ -r /usr/share/jaromail/bin/jaro ]]; then
     60     # use GNU/Linux default in usr
     61     WORKDIR="/usr/share/jaromail"
     62 else
     63     # use GNU/Linux default in local
     64     WORKDIR="/usr/local/share/jaromail"
     65 fi
     66 
     67 # env override
     68 WORKDIR=${JAROWORKDIR:-$WORKDIR}
     69 
     70 # load our zuper extension
     71 # different load paths from inside source or installed
     72 if [ "$WORKDIR" = "../src" ]; then
     73 	source $WORKDIR/zuper/zuper
     74 else
     75 	source $WORKDIR/zlibs/zuper
     76 fi
     77 
     78 
     79 # what operating system are we in? use os_detect()
     80 # simplifying modes of operation: GNU or MAC
     81 case $(uname -o) in
     82     GNU/Linux) OS=GNU
     83     notice "Jaro Mail v${VERSION} running on GNU/Linux"	;;
     84 
     85     Darwin) OS=MAC
     86     notice "Jaro Mail v${VERSION} running on Mac/OSX"	;;
     87 
     88     Cygwin) OS=WIN
     89         notice "Jaro Mail v${VERSION} runing on MS/Win" ;;
     90 
     91     *) OS=GNU # default
     92     error "Running on an unknown operating system, assuming GNU" ;;
     93 esac
     94 
     95 
     96 
     97 # global variables
     98 vars+=(DEBUG QUIET DRYRUN CALLMUTT MAILDIRS WORKDIR)
     99 QUIET=${QUIET:-0}
    100 DEBUG=${DEBUG:-0}
    101 DRYRUN=${DRYRUN:-0}
    102 CALLMUTT=${CALLMUTT:-1}
    103 
    104 # use gnome-keyring for passwords on GNU systems
    105 vars+=(PASS GNOMEKEY SECRET_TOOL PASSWORD_STORE_DIR)
    106 PASS=${PASS:-0}
    107 GNOMEKEY=${GNOMEKEY:-0}
    108 SECRET_TOOL=${SECRET_TOOL:-0}
    109 PASSWORD_STORE_DIR=${JARO_PASSWORD_STORE_DIR:-$MAILDIRS/.password-store}
    110 
    111 # global variables for binaries called
    112 vars+=(rm mkdir mutt SQL OS)
    113 
    114 # load zsh modules
    115 zmodload zsh/regex
    116 zmodload zsh/mapfile
    117 zmodload zsh/system
    118 zmodload -F zsh/stat b:zstat
    119 zmodload zsh/sched
    120 
    121 ##########################
    122 
    123 # SQL command
    124 SQL=sqlite3
    125 
    126 vars+=(PROGRAM global_quit)
    127 global_quit=0
    128 
    129 # global variable for account selection
    130 vars+=(account account_type)
    131 account=default
    132 vars+=(list)
    133 list=whitelist
    134 
    135 # global variables for accounts
    136 vars+=(name login imap imap_port smtp smtp_port protocol password auth accountopt)
    137 arrs+=(folders exclude)
    138 vars+=(host port type)
    139 vars+=(my_hdr)
    140 
    141 # global for server fingerprints
    142 vars+=(fingerprint)
    143 
    144 # global variables for addressbook
    145 vars+=(hostname addressbook addressbook_tmp)
    146 
    147 # global variables for email parsers
    148 typeset -A e_addr
    149 vars+=(e_parsed)
    150 
    151 # global array for maildirs (filled by list_maildirs)
    152 arrs+=(maildirs)
    153 vars+=(last_deliver)
    154 
    155 # stdin when read becomes globally accessible
    156 vars+=(stdin bytesread)
    157 stdin=""
    158 bytesread=0
    159 
    160 # global arrays for search results and mailpaths
    161 # are all arrays of absolute paths
    162 arrs+=(search_results mailpaths)
    163 search_results=()
    164 
    165 # global variable for mutt binary
    166 vars+=(mutt pgpewrap dotlock)
    167 
    168 # global variable for exit code
    169 vars+=(exitcode)
    170 # exitcode=0
    171 
    172 # global variable for infos on imap folder
    173 # format: name;num_of_messages;size_in_bytes
    174 # last entry is grand_total_in_bytes ${imap_info[${#imap_info}]}
    175 typeset -alU imap_info
    176 
    177 # global variable for mutt options
    178 vars+=(muttflags)
    179 
    180 autoload colors; colors
    181 
    182 # default addressbook
    183 ADDRESSBOOK="$MAILDIRS/whitelist.abook"
    184 
    185 # which command to use when creating dirs
    186 mkdir="`command -v mkdir` -m 700 -p"
    187 
    188 
    189 act "System in $WORKDIR"
    190 
    191 PATH="$WORKDIR/bin:$PATH"
    192 
    193 # load our ZLibs
    194 if [ -d $WORKDIR/zlibs ]; then
    195     # if testing from source load uncompiled libs
    196     if [ "$WORKDIR" = "../src" ]; then
    197 		for z in `find $WORKDIR/zlibs -type f`; do
    198 			[[ "$z" =~ "zuper" ]] && continue
    199 			func "Loading zlib: ${z}"
    200 			source ${z}
    201 		done
    202     else
    203 		fpath+=($WORKDIR/zlibs)
    204         for z in `ls $WORKDIR/zlibs`; do
    205             [[ "$z" =~ "zuper" ]] && continue
    206 			[[ "$z" =~ ".zwc"  ]] && continue
    207             func "Loading zlib: ${z}"
    208             source $WORKDIR/zlibs/${z}
    209         done
    210     fi
    211 
    212     func "full set of auxiliary functions loaded"
    213 elif [[ $1 = source ]]; then
    214 
    215     act "limited set of auxiliary functions sourced"
    216 
    217 else
    218     error "No ZLibs found in $WORKDIR/zlibs"
    219     error "This installation of Jaro Mail is broken."
    220     exit 1
    221 fi
    222 
    223 
    224 
    225 ##########
    226 # complete
    227 if [ "$WORKDIR" = "../src" ]; then
    228 	source $WORKDIR/zuper/zuper.init
    229 else
    230 	source $WORKDIR/zlibs/zuper.init
    231 fi
    232 
    233 
    234 ACCOUNTS="$MAILDIRS/Accounts"
    235 KEYRING="$MAILDIRS/Keyring"
    236 
    237 case $OS in
    238     GNU)
    239         # backward compatibility tests for old paths in JaroMail <1.3
    240         { test -d $WORKDIR/Accounts } && { test ! -d $ACCOUNTS } && {
    241             act "Updating accounts location: $ACCOUNTS"
    242             cp -ra $WORKDIR/Accounts $ACCOUNTS
    243         }
    244 
    245         { test -r "$WORKDIR/keyring" } && { test ! -r "$KEYRING" } && {
    246             act "Updating keyring location: $KEYRING"
    247             cp $WORKDIR/keyring "$KEYRING"
    248         }
    249     ;;
    250     MAC)
    251     ;;
    252 esac
    253 
    254 hostname=$(hostname) # gather the current hostname
    255 
    256 [[ "$1" = "source" ]] || { # skip checks if just sourcing
    257 
    258     # make sure we have a directory for account configurations
    259     { test -d "$ACCOUNTS" } || { ${=mkdir} "$ACCOUNTS" }
    260 
    261     # make sure we have a local keyring in case system-wide not found
    262     # { test -r "$KEYRING" } || { create_keyring "$KEYRING" }
    263 
    264     # make sure we have an addressbook
    265     [[ -r "$ADDRESSBOOK" ]] || { create_addressbook "$ADDRESSBOOK" }
    266 
    267     ${=mkdir} "$MAILDIRS"
    268 
    269     # make sure the permissions are private
    270     chmod 700 "$MAILDIRS"
    271 
    272     ${=mkdir} "$MAILDIRS/logs"
    273     # ${=mkdir} "$MAILDIRS/certs"
    274 
    275     MUTTDIR="$MAILDIRS/.mutt"
    276     { test -d "$MUTTDIR" } || { ${=mkdir} "$MUTTDIR" }
    277 
    278     # make sure we have Filters.txt Applications.txt Mutt.txt
    279     { test -r "$MAILDIRS/Filters.txt" } || {
    280         cp "$WORKDIR/Filters.txt" "$MAILDIRS/Filters.txt"
    281         notice "Default filters created" }
    282 
    283     { test -r "$MAILDIRS/Applications.txt" } || {
    284         cp "$WORKDIR/Applications.txt" "$MAILDIRS/Applications.txt"
    285         notice "Default helper applications settings created" }
    286 
    287     { test -r "$MAILDIRS/Mutt.txt" } || {
    288         cp "$WORKDIR/Mutt.txt" "$MAILDIRS/Mutt.txt"
    289         notice "Default Mutt configuration template created" }
    290 
    291 } # if not sourcing
    292 
    293 # binary programs recognition
    294 check_bin() {
    295 
    296     # required programs
    297     for req in pinentry fetchmail gpg mutt msmtp; do
    298         isfound $req
    299         { test $? != 0 } && {
    300             error "Cannot find $req. Please install it."
    301             exit 1
    302         }
    303     done
    304 
    305     # make sure a gnupg dir exists
    306     { test -r $HOME/.gnupg/pubring.gpg } || {
    307         ${=mkdir} $HOME/.gnupg
    308         touch $HOME/.gnupg/pubring.gpg
    309         touch $HOME/.gnupg/secring.gpg
    310     }
    311 
    312     # which find command to use
    313     case $OS in
    314         GNU) find="find -O3" ;;
    315         MAC) find="gfind -O3" ;;
    316         *) find="find"
    317     esac
    318 
    319     # which wipe command to use
    320     if  isfound wipe; then
    321         rm="wipe -f -s -q -R /dev/urandom"
    322     elif isfound srm; then
    323         rm="srm -m"
    324     else
    325         rm="rm -f"
    326     fi
    327     func "Rm binary: $rm"
    328 
    329     # which mutt binary to use
    330     if isfound mutt; then
    331         # system-wide
    332         # TODO: check if this is also the location on Fedora
    333         pgpewrap="${WORKDIR}/bin/gpgewrap"
    334         dotlock="${WORKDIR}/bin/dotlock"
    335     elif isfound mutt-jaro; then
    336         # in-house compiled
    337         mutt=mutt-jaro
    338         pgpewrap=pgpewrap
    339         dotlock=dotlock
    340     else
    341         error "Cannot find Mutt. Please install it."
    342         exit 1
    343     fi
    344     func "Mutt binary: `command -v mutt`"
    345     func "Notmuch binary: `command -v notmuch`"
    346 	func "Keyring set: $JARO_KEYRING"
    347 	if [[ "$JARO_KEYRING" == "" ]]; then
    348 		# check for pass, else fallback
    349 		if isfound pass; then
    350 			act "keyring in use: pass (auto-detected)"
    351 			[[ -d $PASSWORD_STORE_DIR ]] ||
    352 				warning "the 'pass' keyring is found but not initialised"
    353 
    354 			PASS=1
    355 
    356 			# check if secret-tool is present else fallback to gnome-keyring
    357 		elif isfound secret-tool; then
    358 			act "keyring in use: secret-tool (auto-detected)"
    359 			SECRET_TOOL=1
    360 		else
    361 			ps ax | grep '[g]nome-keyring-daemon' > /dev/null
    362 			[[ $? = 0 ]] && {
    363 				act "keyring in use: gnome-keyring (auto-detected)"
    364 				GNOMEKEY=1
    365 			}
    366 		fi
    367 
    368 	else # JARO_KEYRING defined
    369 
    370 		case $JARO_KEYRING in
    371 			pass)
    372 				act "keyring in use: pass (set in JARO_KEYRING)"
    373 				export PASSWORD_STORE_DIR=${PASSWORD_STORE_DIR:-$HOME/.password-store}
    374 				[[ -d $PASSWORD_STORE_DIR ]] || {
    375 					error "the 'pass' keyring is found but not initialised"
    376 					error "run 'pass init your-gpg-id'"
    377 				}
    378 				PASS=1 ;;
    379 			secret-tool)
    380 				act "keyring in use: secret-tool (set in JARO_KEYRING)"
    381 				SECRET_TOOL=1 ;;
    382 			gnome-keyring)
    383 				act "keyring in use: gnome-keyring (set in JARO_KEYRING)"
    384 				GNOMEKEY=1  ;;
    385 		esac
    386 
    387 	fi
    388 
    389 
    390     return 0
    391 }
    392 
    393 
    394 usage() {
    395     cat <<EOF | more
    396 Jaro Mail $VERSION - your humble and faithful electronic postman
    397 
    398 Synopsis: jaro [options] [command] [command-options]
    399 
    400 Main commands:
    401 
    402  fetch  download unread emails from [account]
    403  send   send all mails queued in the outbox
    404  peek   look into the [account] mailbox without downloading
    405  index  build or update the search index for all maildirs
    406  search search maildirs or addressbook for expressions
    407 
    408 Options:
    409 
    410  -a     use a particular [account] instead of default
    411  -l     set [abook] in use, 'whitelist' is default
    412  -n     dry run, show operations without executing them
    413  -q     run quietly without printing information
    414  -D     print debugging information at runtime
    415  -v     version information for this tool
    416  -h     print this help
    417 
    418 == Addressbook commands (use -l to indicate which addressbook)
    419 a pipe | in front indicate they take an email body from stdin
    420 
    421  abook    edit the [abook] using the console editor
    422 
    423  extract  list addresses of all recipients or senders found in:
    424           maildir, search expr, vcard, gnupg keyring or pubkey
    425 
    426 |import   read address list from stdin into addressbook or group
    427           (the output of extract commands can be piped to import)
    428 
    429 
    430 == Operational commands (use -a to indicate [account])
    431 
    432  fetch    downloads emails locally from [account]
    433 
    434  send     send all outbox/ queue via the [account]'s smtp
    435 
    436  peek     interactive connection to an [account]'s imap folder
    437           (takes folder names as argument, default is INBOX)
    438 
    439  passwd   set [account]'s passwords in the OS native keyring
    440 
    441 |queue    read a mail from stdin and queue it in outbox/ for sending
    442 
    443 |smtp     read a mail from stdin and send it via [accounts]'s smtp
    444 
    445 
    446 == Storage commands
    447 
    448  open     open a maildir folder (can use -R for read-only)
    449 
    450  backup   move search results from a maildir to another one
    451           (string or date range matches, can use -n for dry-run)
    452 
    453  merge    merge a source maildir into destination, then delete source
    454 
    455  update   updates all filter engine according to Filters.txt
    456           (also generates Sieve format rules ready for server use)
    457 
    458  filter   process a maildir distributing emails according to Filters.txt
    459           (if none specified, processes incoming/ - called by fetch)
    460 
    461 For a complete introductory documentation, see the User Manual (PDF)
    462 Website on <http://dyne.org/software/jaro-mail>
    463 Report bugs to <https://github.com/dyne/JaroMail/issues>
    464 EOF
    465 }
    466 
    467 typeset -A subcommands_opts
    468 
    469 main() {
    470 	# zuper option parser
    471 
    472     option_main=(a: -account=a l: -list=l q -quiet=q D -debug=D h -help=h v -version=v n -dry-run=n f -force=f)
    473 
    474     option_subcommands[__empty]="R -readonly=R"
    475     option_subcommands[compose]=""
    476     option_subcommands[queue]=""
    477     option_subcommands[fetch]=""
    478     option_subcommands[send]=""
    479     option_subcommands[peek]="R -readonly=R"
    480     option_subcommands[open]="R -readonly=R"
    481 
    482     option_subcommands[help]=""
    483 
    484     option_subcommands[update]=""
    485 
    486     option_subcommands[stat]=""
    487 
    488     option_subcommands[index]=""
    489     option_subcommands[search]=""
    490     option_subcommands[alot]=""
    491     option_subcommands[notmuch]=""
    492 
    493     option_subcommands[addr]=""
    494     option_subcommands[learn]=""
    495     option_subcommands[forget]=""
    496 
    497     option_subcommands[complete]=""
    498     option_subcommands[isknown]=""
    499 
    500     option_subcommands[list]=""
    501     option_subcommands[extract]=""
    502     option_subcommands[parse]=""
    503     option_subcommands[header]=""
    504     option_subcommands[headers]=""
    505 
    506     option_subcommands[import]=""
    507     option_subcommands[export]=""
    508     option_subcommands[vcard]=""
    509     option_subcommands[abook]=""
    510 	option_subcommands[new]=""
    511 
    512     option_subcommands[vim]=""
    513     option_subcommands[edit]=""
    514     option_subcommands[preview]=""
    515 
    516     option_subcommands[replay]=""
    517 
    518     option_subcommands[remember]=""
    519 
    520     option_subcommands[backup]=""
    521     option_subcommands[rmdupes]=""
    522     option_subcommands[merge]=""
    523     option_subcommands[filter]=""
    524     option_subcommands[deliver]=""
    525 
    526     option_subcommands[passwd]=""
    527     option_subcommands[askpass]=""
    528 
    529     option_subcommands[source]=""
    530 
    531     option_subcommands[isonline]=""
    532 
    533     option_subcommands[init]=""
    534 
    535     option_subcommands[imap]=""
    536 
    537     option_subcommands[smtp]=""
    538 
    539     option_subcommands[crypt]=""
    540     option_subcommands[cryptsign]=""
    541 
    542     option_subcommands[isml]=""
    543     option_subcommands[ismd]=""
    544 
    545 	option.parse $* || return 1
    546 	func "subcommand: $subcommand"
    547 
    548 	# print out version
    549     if option.is_set -v; then
    550 		cat $JAROMAILEXEC | awk '/^#/ {print $0 } !/^#/ {exit}'
    551 		echo
    552     fi
    553 
    554     { option.is_set -a } && { account=`option.value -a` }
    555     { option.is_set -l } && {
    556         if [[ "`option.value -l`" =~ "black" ]]; then
    557             list=blacklist
    558         elif [[ "`option.value -l`" =~ "white" ]]; then
    559             list=whitelist
    560         else
    561             list=`option.value -l`
    562         fi
    563         ADDRESSBOOK="$MAILDIRS"/$list.abook
    564     }
    565     { option.is_set -h } && { usage; return 0 }
    566     { option.is_set -v } && {
    567         cat $JAROMAILEXEC | awk 'BEGIN { v=1 } !/^#/ { exit }'
    568         return 0
    569     }
    570     { option.is_set -q } && { QUIET=1 }
    571     { option.is_set -D } && { DEBUG=1; QUIET=0
    572     func "All debug messages ON" }
    573     { option.is_set -n } && { DRYRUN=1
    574     act "Dry run, show operations without executing them." }
    575     { option.is_set -R } && { muttflags+=" -R " }
    576     { option.is_set -f } && { FORCE=1 }
    577 
    578     # clean up options from param
    579     # PARAM=(${PARAM// -? /})
    580 
    581     case "$subcommand" in
    582 
    583     compose) stdin_compose ${option_params}
    584             exitcode=$?
    585             ;;
    586 
    587     queue)  stdin_queue ${option_params}
    588             exitcode=$?
    589             ;;
    590 
    591     fetch)  fetch ${option_params} && \
    592                  update_filters && \
    593                  filter_maildir incoming && \
    594                  update_notmuch
    595 			;;
    596 
    597     send)   send ${option_params}
    598             exitcode=$?
    599             ;;
    600 
    601     peek)   peek ${option_params}
    602             exitcode=$?
    603             ;;
    604 
    605     remember)
    606         deliver remember
    607         exitcode=$?
    608         ;;
    609 
    610     replay)
    611         replay ${option_params}
    612         exitcode=$?
    613         ;;
    614 
    615     update|init)
    616         [[ "$option_params" = "" ]] || {
    617             for p in $option_params; do
    618                 [[ "$p" = "." ]] && p=$PWD
    619                 [[ -d $p ]] && MAILDIRS=$p
    620             done
    621         }
    622         init_inbox
    623         update_filters
    624         update_sieve
    625         update_notmuch
    626         notice "Initialization completed in $MAILDIRS"
    627         act "configure accounts in $MAILDIRS/Accounts"
    628         ;;
    629 
    630     help) usage ;;
    631 
    632     index) nm_index
    633            exitcode=$?
    634            ;;
    635 
    636     # notmuch search with file output
    637     search) search ${option_params}
    638             [[ $DRYRUN = 1 ]] || {
    639                 for i in ${search_results=}; do
    640                     print - "$i"
    641                 done
    642             } | save_replay $subcommand
    643             ;;
    644 
    645     alot) alot_search ${option_params} ;;
    646 
    647     notmuch)
    648             notice "Command: notmuch ${option_params}"
    649             nm ${option_params} | save_replay $subcommand
    650             exitcode=$?
    651             ;;
    652 
    653     addr|list) search_addressbook ${option_params}
    654                ;;
    655 
    656     complete) complete ${option_params}
    657               exitcode=$?
    658               ;;
    659 
    660     isknown) sender_isknown ${option_params}
    661              exitcode=$?
    662              ;;
    663 
    664     learn)  learn ${option_params}
    665             exitcode=$?
    666             ;;
    667 
    668     import) import ${option_params}
    669             exitcode=$?
    670             ;;
    671 
    672     "export")
    673             export_vcard ${option_params}
    674             ;;
    675 
    676     abook)  edit_abook ${option_params}
    677             exitcode=$?
    678             ;;
    679 
    680 	new)
    681 		new_account ${option_params}
    682 		exitcode=$?
    683 		;;
    684 
    685     stat*)  stats ${option_params} | sort -n
    686             exitcode=$?
    687             ;;
    688 
    689     edit|vim)   edit_file ${option_params}    ;;
    690     open)       x_mutt -f ${option_params}  ;;
    691     preview)    preview_file ${option_params} ;;
    692 
    693     mkdir)
    694         DEBUG=1 maildirmake ${option_params}
    695         exitcode=$?
    696         ;;
    697 
    698     copy)
    699         maildir_shift ${option_params} cp
    700         exitcode=$?
    701         ;;
    702 
    703     move)
    704         maildir_shift ${option_params} mv
    705         exitcode=$?
    706         ;;
    707 
    708     link)
    709         maildir_shift ${option_params} ln
    710         exitcode=$?
    711         ;;
    712 
    713     rmdupes) rmdupes ${option_params} ;;
    714     merge)   merge ${option_params}   ;;
    715 
    716     filter)
    717         update_filters ${option_params}
    718         [[ $? = 0 ]] || {
    719             error "error updating filters, operation aborted."
    720             break
    721         }
    722         filter_maildir ${option_params} | save_replay $subcommand
    723         exitcode=$?
    724         ;;
    725 
    726     deliver)
    727             deliver ${option_params}
    728             exitcode=$?
    729             ;;
    730 
    731     passwd) read_account $account
    732             host=${option_params:-$imap}
    733             new_password ;;
    734 
    735     # cert)    cert ${option_params} ;; # was checking is_online
    736 
    737     isonline) is_online ${option_params}; exitcode=$? ;;
    738 
    739 
    740     'source')   return 0 ;;
    741 
    742     askpass)
    743         read_account $account
    744         ask_password
    745         # shell escape special chars
    746         print - "$password" | sed '
    747 s/\&/\\&/g
    748 s/\$/\\$/g
    749 s/ /\\ /g
    750 s/;/\\;/g
    751 s/\?/\\?/g
    752 s/\*/\\*/g
    753 ' 
    754         # TODO: verify if this works with all passwords
    755         exitcode=$?
    756         ;;
    757 
    758     imap)
    759         imapcmd="${option_params[(w)1]}"
    760         case $imapcmd in
    761             console)
    762                 read_account $account
    763                 host=${imap:-$host}
    764                 ask_password
    765                 func "$login $password"
    766                 cat <<EOF | run_imap_query
    767 B00000 CAPABILITY
    768 B00001 LOGIN "${login}" "${password}"
    769 EOF
    770                 ;;
    771             getsize)
    772                 read_account $account
    773                 host=${imap:-$host}
    774                 ask_password
    775                 bytes_total=`imap_get_size "$2"`
    776                 exitcode=$?
    777                 notice "Size of account $login on $imap"
    778                 act "$bytes_total bytes"
    779                 mib_total=$(( $bytes_total / 1048576 ))
    780                 act "$mib_total MB (MiB)"
    781                 ;;
    782             listfolders)
    783                 read_account $account
    784                 host=${imap:-$host}
    785                 ask_password
    786                 folders=(`imap_list_folders`)
    787                 exitcode=$?
    788                 notice "List of folders for $login on $imap"
    789                 for f in $folders; do print - "$f"; done \
    790                     | save_replay $subcommand | column
    791 
    792                 ;;
    793             # interactive)
    794             #     read_account
    795             #     ask_password
    796             #     imap_interactive_shell
    797             #     exitcode=$?
    798             #     ;;
    799             *)
    800                 error "imap needs a subcommand: getsize or listfolders"
    801                 ;;
    802         esac
    803         ;;
    804 
    805     # list)
    806     #     list_abook ${option_params}
    807     #     exitcode=$?
    808     #     ;;
    809 
    810     extract|parse)
    811         extract_addresses ${option_params} \
    812             | save_replay $subcommand
    813         exitcode=$?
    814         ;;
    815 
    816     header|headers)
    817         extract_headers ${option_params}
    818         exitcode=$?
    819         ;;
    820 
    821     smtp)
    822         smtp_send ${option_params}
    823         exitcode=$?
    824         ;;
    825 
    826     isml)
    827         ismailinglist
    828         [[ $? = 0 ]] && \
    829             notice "Email read from stdin is a mailinglist"
    830         exitcode=$?
    831         ;;
    832 
    833     ismd)
    834         maildircheck ${option_params}
    835         [[ $? = 0 ]] && \
    836             notice "Directory is a maildir: ${option_params}"
    837         exitcode=$?
    838         ;;
    839 
    840     __empty) # unknown command, pass it to autostart
    841         func "no command, autostart"
    842         func "subcommand: $subcommand"
    843 		read_account "$account"
    844         if [[ "$subcommand" = "__empty" ]]; then
    845 			# open folder
    846 			if [[ "$option_params" = "" ]]; then
    847 		        x_mutt -Z
    848 		    else
    849 		        x_mutt -f "$option_params"
    850 		    fi
    851 		fi
    852         exitcode=$?
    853 		;;
    854 
    855 	__unknown:*)
    856         # argument passed: determine if an email
    857 		subcommand=${subcommand[(ws@:@)2]}
    858 		func "subcommand: $subcommand"
    859 		read_account "$account"
    860         if isemail "$subcommand"; then
    861             notice "Composing message to: $*"
    862             # its an email, TODO see if we have it in our addressbook
    863             x_mutt $*
    864             exitcode=0
    865             # or a directory of file
    866         elif maildircheck "$subcommand"; then
    867             # is it a maildir? then open
    868             x_mutt -f "$subcommand"
    869             exitcode=$?
    870         elif maildircheck "$MAILDIRS/$subcommand"; then
    871             x_mutt -f "$MAILDIRS/$subcommand"
    872             exitcode=$?
    873         elif [[ -f "$subcommand" ]]; then
    874             # is it a regular file? then ;attach it
    875             x_mutt -a $*
    876             exitcode=0
    877         else
    878             # just open; mutt on first unread folder
    879             x_mutt
    880             exitcode=$?
    881 
    882         fi
    883         ;;
    884     esac
    885 
    886     return $exitcode
    887 }
    888 
    889 
    890 check_bin
    891 main $@
    892 # endgame NOERRORS
    893 # return $exitcode