jaromail

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

helpers (9217B)


      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 # Copyleft (C) 2010-2015 Denis Roio <jaromil@dyne.org>
      8 #
      9 # This source  code is free  software; you can redistribute  it and/or
     10 # modify it under the terms of  the GNU Public License as published by
     11 # the Free  Software Foundation; either  version 3 of the  License, or
     12 # (at your option) any later version.
     13 #
     14 # This source code is distributed in  the hope that it will be useful,
     15 # but  WITHOUT ANY  WARRANTY;  without even  the  implied warranty  of
     16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     17 # Please refer to the GNU Public License for more details.
     18 #
     19 # You should have received a copy of the GNU Public License along with
     20 # this source code; if not, write to:
     21 # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     22 
     23 # start without options: auto
     24 # read or compose depending if there is an argument that is an email
     25 # or a folder to start with
     26 
     27 # which mutt binary to use
     28 mutt="mutt"
     29 
     30 datestamp() { date '+%d%b%y' }
     31 
     32 # remote leading and trailing spaces in a string taken from stdin
     33 trim() {
     34     sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g'
     35 }
     36 
     37 # zmodload zsh/mapfile
     38 printfile() {
     39     print ${mapfile[$1]}
     40 }
     41 
     42 # extract all emails found in a text from stdin
     43 # outputs them one per line
     44 extract_emails() {
     45 awk '{ for (i=1;i<=NF;i++)
     46      if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) {
     47        gsub(/<|>|,/ , "" , $i); print $i } }'
     48 }
     49 
     50 # zmodload zsh/regex
     51 isemail() {
     52 	print "$1" | grep -q -E '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}' && return 0
     53     return 1
     54 }
     55 
     56 ismailinglist() {
     57     _head=`awk '{ print $0 }
     58 /^$/ { exit }'`
     59     [[ "$_head" =~ "List-Id" ]] \
     60         || [[ "$_head" =~ "X-BeenThere" ]] \
     61         || [[ "$_head" =~ "List-Post" ]] \
     62         || [[ "$_head" =~ "X-Mailman-Version" ]] \
     63         || [[ "$_head" =~ "Mailing-List" ]] \
     64         && return 0
     65     return 1
     66 }
     67 
     68 # parses stdin and converts some characters to html
     69 escape_html() {
     70     sed -e '
     71 s/\&/\&amp;/g
     72 s/>/\&gt;/g
     73 s/</\&lt;/g
     74 s/"/\&quot;/g
     75 '
     76 }
     77 
     78 e_parse() {
     79     _arg=""
     80     # optional second argument limits parsing to header fields
     81     [[ "$1" = "" ]] || [[ "$1" = "all" ]] || _arg="-x $1"
     82 
     83     # use RFC822 parser in fetchaddr
     84     e_parsed=`${WORKDIR}/bin/fetchaddr ${=_arg} -a`
     85 
     86     for _p in ${(f)e_parsed}; do
     87         _e="${(Q)_p[(ws:,:)1]:l}"
     88         # check if an email address was found
     89         isemail "$_e" || continue
     90         # avoid duplicates
     91         [[ "${(v)e_addr[$_e]}" = "" ]] || continue
     92 
     93         # extract also the name using comma separator
     94         _n="${(Q)_p[(ws:,:)2]}"
     95 
     96         e_addr+=("$_e" "$_n")
     97         func "parsed: $_n <$_e>"
     98     done
     99 
    100     # no emails found
    101     [[ ${#e_addr} = 0 ]] && return 1
    102 
    103     return 0
    104 }
    105 
    106 
    107 # short utility to print only mail headers
    108 hdr() {
    109    [[ -r "$1" ]] || {
    110         error "hdr() called on non existing file: $1"
    111         return 1 }
    112     awk '{ print $0 }
    113 /^$/ { exit }' "$1"
    114 }
    115 
    116 # short utility to print only mail body
    117 body() {
    118     { test -r "$1" } || {
    119     error "body() called on non existing file: $1"
    120     return 1 }
    121     awk '
    122 BEGIN { head=1 }
    123 /^$/ { head=0 }
    124 { if(head==0)
    125     print $0
    126   else
    127     next }' "$1"
    128 }
    129 
    130 save_replay() {
    131     fn save_replay
    132     _cmd="$1"
    133     req=(_cmd)
    134     ckreq || return 1
    135 
    136     tee $MAILDIRS/cache/replay.$_cmd
    137     [[ $? = 0 ]] && \
    138         ln -sf $MAILDIRS/cache/replay.$_cmd $MAILDIRS/cache/replay.last
    139     return 0
    140 }
    141 
    142 replay() {
    143     fn "replay $*"
    144 
    145     arg=$1
    146 
    147     if [[ "$arg" = "" ]]; then
    148 
    149         if [[ -r $MAILDIRS/cache/replay.last ]]; then
    150             notice "Replay last stdout from `stat -c %z $MAILDIRS/cache/replay.last`"
    151             cat $MAILDIRS/cache/replay.last
    152             return $?
    153         else
    154             # never run a command?
    155             error "There is nothing to replay"
    156             return 1
    157         fi
    158 
    159     elif [[ -r $MAILDIRS/cache/replay.$arg ]]; then
    160         notice "Replay stdout of command '$arg' from `stat -c %z $MAILDIRS/cache/replay.last`"
    161         cat $MAILDIRS/cache/replay.$arg
    162         return $?
    163 
    164     elif [[ "$arg" = "list" ]]; then
    165         notice "Listing available replays:"
    166         ls -l $MAILDIRS/cache/replay.*
    167         return $?
    168     else
    169         error "Nothing to replay for command: $arg"
    170         return 1
    171     fi
    172     return 1
    173 }
    174 
    175 #########
    176 ## Editor
    177 # this part guesses what is the best editor already present on the system
    178 # of course we have a preference for AutOrg, the editor from our suite
    179 # however the default is nano if nothing else is choosen.
    180 jarovim() {
    181     fn jarovim
    182     vim -c 'set fo=tcrq' -c 'set tw=72' \
    183         -c 'map <C-j> {gq}' -c 'imap <C-j> <esc>{gq}i' \
    184         "${@}"
    185     return $?
    186 }
    187 
    188 edit_file() {
    189     fn edit_file $*
    190 	_editor=${EDITOR:-vim}
    191 	_editor=${JARO_EDITOR:-$_editor}
    192     req=(_editor)
    193     ckreq || return 1
    194 
    195     func "selected editor: $_editor"
    196     case $_editor in
    197         # refine settings for email
    198         vi|vim) jarovim "$*"; return $? ;;
    199         emacs) emacsclient "$*"; return $? ;;
    200         *) ${=_editor} "$*"; return $? ;;
    201     esac
    202 
    203     # if we are here we need to guess
    204     case $OS in
    205         MAC) open -t $* ;;
    206         GNU)
    207             ps ax|grep '[e]macs' > /dev/null
    208             if [ $? = 0 ]; then
    209                 emacsclient -a $*
    210             elif command -v vim > /dev/null; then
    211                 jarovim $*
    212             elif command -v nano > /dev/null; then
    213                 nano -m -S -Q ">" -I -E -D -T 4 -U -W -c -i -k -r 72 $*
    214             else
    215                 error "No editor found, please configure the JARO_EDITOR environment variable."
    216             fi
    217             ;;
    218     esac
    219     return $?
    220 }
    221 
    222 ##############
    223 ## Open a File
    224 preview_file() {
    225     case $OS in
    226         GNU)
    227             xdg-open $*
    228             ;;
    229         MAC)
    230             open -g $*
    231             ;;
    232     esac
    233 }
    234 
    235 
    236 #########################
    237 ## check if we are online
    238 is_online() {
    239     func "Test if we are online"
    240     { test "$FORCE" = "1" } && {
    241     act "Internet check skipped (--force in use)"
    242     return 0
    243     }
    244     _res=1
    245     _host=${1:-8.8.8.8}
    246     _port=${2:-NONE}
    247 
    248     _mode=inet # or host
    249     { test "$_port" = "NONE" } || { _mode=host }
    250 
    251     case $_mode in
    252     inet)
    253         func "trying to ping ${_host}"
    254         ping -c1 -n ${_host} 2>&1 > /dev/null
    255         { test $? = 0 } || {
    256         error "Internet seems unreachable"
    257         act "Network connection is checked with a ping to 8.8.8.8"
    258         act "if your network doesn't allows it to pass, use -f to force."
    259         error "Operation aborted."
    260         exit 1
    261         }
    262         act "Internet seems to be reachable"
    263         ;;
    264     host)
    265         func "trying to connect ${_host} port ${_port}"
    266         #busybox nc -w 16 -z ${_host} ${_port} > /dev/null
    267         busybox nc -w 16 -z ${_host} ${_port} > /dev/null
    268         { test $? = 0 } || {
    269         error "Host unreachable: $_host"
    270         act "Network connection is checked with 16s timeout"
    271         act "if you want to bypass this check, use -f to force."
    272         error "Operation aborted."
    273         return 1
    274         }
    275         act "Host $_host responds on port $_port"
    276         ;;
    277     esac
    278     return 0
    279 }
    280 
    281 
    282 human_size() {
    283     [[ $1 -gt 0 ]] || {
    284         error "human_size() called with invalid argument"
    285         return 1
    286     }
    287 
    288     # we use the binary operation for speed
    289     # shift right 10 is divide by 1024
    290 
    291     # gigabytes
    292     [[ $1 -gt 1073741824 ]] && {
    293         print -n "$(( $1 >> 30 )) GB"
    294         return 0
    295     }
    296 
    297     # megabytes
    298     [[ $1 -gt 1048576 ]] && {
    299         print -n "$(( $1 >> 20 )) MB"
    300         return 0
    301     }
    302     # kilobytes
    303     [[ $1 -gt 1024 ]] && {
    304         print -n "$(( $1 >> 10 )) KB"
    305         return 0
    306     }
    307     # bytes
    308     print -n "$1 Bytes"
    309     return 0
    310 }
    311 
    312 ######
    313 # CERT
    314 # downloads and/or installs certificates
    315 
    316 
    317 
    318 cert() {
    319 
    320     act "Downloading all known certificates (needs Internet connectivity)"
    321 
    322 #	gmail)
    323     cc=Equifax_Secure_Certificate_Authority
    324     if ! [ -r $MAILDIRS/certs/${cc}.pem ]; then
    325 
    326     curl -o $MAILDIRS/certs/${cc}.pem \
    327         "https://www.geotrust.com/resources/root_certificates/certificates/${cc}.cer"
    328     openssl x509 -in \
    329         $MAILDIRS/certs/${cc}.pem -fingerprint \
    330         -subject -issuer -serial -hash -noout
    331     fi
    332     notice "Google CA succesfully installed"
    333 
    334 #	dyne|autistici|freaknet)
    335     cc=Autistici_Certificate_Authority
    336     if ! [ -r $MAILDIRS/certs/${cc}.pem ]; then
    337     curl -o $MAILDIRS/certs/${cc}.pem \
    338         "http://ca.autistici.org/ca.pem"
    339     openssl x509 -in \
    340         $MAILDIRS/certs/${cc}.pem \
    341         -fingerprint -subject -issuer -serial -hash -noout
    342     fi
    343     notice "Aut/Inv CA succesfully installed"
    344 
    345 #	riseup)
    346     cc=RiseupCA
    347     if ! [ -r $MAILDIRS/certs/${cc}.pem ]; then
    348     curl -o $MAILDIRS/certs/${cc}.pem "https://help.riseup.net/assets/43052/RiseupCA.pem"
    349     openssl x509 -in \
    350         $MAILDIRS/certs/${cc}.pem \
    351         -fingerprint -subject -issuer -serial -hash -noout
    352     fi
    353     notice "Riseup CA succesfully installed"
    354 
    355     act "refreshing certificates"
    356     c_rehash $MAILDIRS/certs > /dev/null
    357     if [ $? != 0 ]; then
    358     error "Error refreshing certificates in $MAILDIRS/certs"
    359     c_rehash $MAILDIRS/certs
    360     fi
    361     notice "Done importing most common certificates."
    362     return 0
    363 }