jaromail

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

roundomo (6133B)


      1 #!/usr/bin/env zsh
      2 #
      3 # Copyright (C) 2014-2015 Denis Roio <jaromil@dyne.org>
      4 #
      5 # This source  code is free  software; you can redistribute  it and/or
      6 # modify it under the terms of  the GNU Public License as published by
      7 # the Free  Software Foundation; either  version 3 of the  License, or
      8 # (at your option) any later version.
      9 #
     10 # This source code is distributed in  the hope that it will be useful,
     11 # but  WITHOUT ANY  WARRANTY;  without even  the  implied warranty  of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     13 # Please refer to the GNU Public License for more details.
     14 #
     15 # You should have received a copy of the GNU Public License along with
     16 # this source code; if not, write to:
     17 # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18 
     19 REVISION="Feb/2015"
     20 
     21 QUIET=0
     22 DEBUG=0
     23 
     24 SCRIPT=$0
     25 
     26 TEST=0
     27 
     28 # export ROUNDOMO to env with path to the installation
     29 # else start it from inside the dir with ./dowse
     30 DIR=${ROUNDOMO:-`pwd`}
     31 
     32 # For gettext
     33 TEXTDOMAIN=roundomo
     34 
     35 
     36 source $DIR/zlibs/messaging.sh
     37 
     38 
     39 # system configuration
     40 [[ -r $DIR/roundomo.conf ]] || {
     41     error "configuration not found: roundomo.conf"; return 1 }
     42 
     43 source $DIR/roundomo.conf
     44 
     45 myquery() {
     46     mysql -B -N -L -u $roundcube_db_user -p$roundcube_db_pass $roundcube_db }
     47 
     48 # quick command to login into the database to debug schemas
     49 [[ "$1" == "login" ]] && {
     50     mysql -u $roundcube_db_user -p$roundcube_db_pass $roundcube_db; return 0 }
     51 
     52 
     53 whitelist() {
     54     # check args
     55     [[ "$1" == "" ]] && { error "no user specified to whitelist()"; return 1 }    
     56     user="$1"
     57 
     58     # retrieve userid in db
     59     check_user="select user_id from users where username='$user';"
     60     user_id=`print $check_user | myquery`
     61     
     62     [[ "$user_id" == "" ]] && { error "user not found: $user"; return 1 }
     63     
     64     notice "Roundomo taking care of: $user (id: $user_id)"
     65 
     66     # retrieve contacts in db
     67     list_contacts="select email from contacts where user_id='$user_id';"
     68     contacts=(`print $list_contacts | myquery`)
     69     contacts_count=${#contacts}
     70     act "contacts found in addressbook: ${contacts_count}"
     71     [[ $contacts_count == 0 ]] && return 1
     72     
     73     # hash contacts to avoid duplicate computation
     74     contacts_hash="`print ${=contacts}|sha1sum|awk '{print $1}'`"
     75 
     76     # if --force is used then force refresh of whitelist even is hash
     77     # is the same (meaning there are no new contacts)
     78     option_is_set -f || { 
     79         [[ -r ".$user" ]] && {
     80 	        contacts_hash_prev=`cat ".$user"`
     81 	        [[ "$contacts_hash_prev" == "$contacts_hash" ]] && {
     82 	            act "no new contacts detected."; return 0 }
     83         }
     84     }
     85 
     86     act "refreshing whitelist with new contacts"
     87     
     88     cat <<EOF > ".$user.sieve"
     89 require ["fileinto","mailbox","variables"];
     90 EOF
     91 
     92     # whitelist only if contacts are present: 
     93     # makes sure priv is activated even with empty addressbook
     94     [[ $contacts_count = 0 ]] || {
     95         cat <<EOF >> ".$user.sieve"
     96 if header :contains "From" [
     97 EOF
     98         for i in $contacts; do
     99 	        contacts_count=$(( $contacts_count - 1 ))
    100             # a single entry may contain more comma separated addresses
    101             ii=(${(s:,:)i}) 
    102             for c in $ii; do
    103 	            print -n "\"$c\"" >> ".$user.sieve"
    104 	            [[ $contacts_count -gt 0 ]] && {
    105 	                print -n "," >> ".$user.sieve" }
    106             done
    107 	        print >> ".$user.sieve"
    108         done
    109         cat <<EOF >> ".$user.sieve"
    110 ]
    111 { fileinto "INBOX"; stop; }
    112 EOF
    113     }
    114     
    115     # static rules: spam, priv, lists and social networks
    116     cat <<EOF >> ".$user.sieve"
    117 if header :is "X-Spam-Flag" "YES" {
    118     fileinto :create "zz.spam"; stop;
    119 }
    120 
    121 if header :contains [ "To","Cc" ] "$user" {
    122     fileinto :create "priv"; stop;
    123 }
    124 
    125 # lists auto filters
    126 if header :matches "List-Id" "*<*>" {
    127   fileinto :create "lists.\${2}"; stop; }
    128 elsif header :matches "X-BeenThere" "*@*" {
    129  fileinto :create "lists.\${1}.\${2}"; stop; }
    130 elsif header :matches "List-Post" "<mailto:*@*" {
    131  fileinto :create "lists.\${1}.\${2}"; stop; }
    132 
    133 if anyof (header :contains "X-Mailman-Version" ".",
    134           header :contains "Mailing-List" ".") {
    135   fileinto :create "lists.unsorted"; stop; }
    136 
    137 # social networks
    138 if header :contains "From" "basecamp" { fileinto :create "zz.social"; stop; }
    139 if header :contains "From" "linkedin.com" { fileinto :create "zz.social"; stop; }
    140 if header :contains "From" "github.com" { fileinto :create "zz.social"; stop; }
    141 if header :contains "From" "facebook" { fileinto :create "zz.social"; stop; }
    142 if header :contains "From" "FriendFeed" { fileinto :create "zz.social"; stop; }
    143 if header :contains "From" "twitter.com" { fileinto :create "zz.social"; stop; }
    144 if header :contains "From" "identi.ca" { fileinto :create "zz.social"; stop; }
    145 if header :contains "From" "wmt-noreply@google" { fileinto :create "zz.social"; stop; }
    146 if header :contains "From" "plus.google.com" { fileinto :create "zz.social"; stop; }
    147 if header :contains "From" "launchpad" { fileinto :create "zz.social"; stop; }
    148 if header :contains "From" "statusnet" { fileinto :create "zz.social"; stop; }
    149 if header :contains "From" "googlealerts" { fileinto :create "zz.social"; stop; }
    150 if header :contains "From" "youtube.com" { fileinto :create "zz.social"; stop; }
    151 
    152 fileinto :create "unsorted";
    153 EOF
    154     
    155     act "sieve filter generated"
    156     sievec .$user.sieve
    157     [[ $? = 0 ]] || {
    158         error "error: filters don't compile."; return 1 }
    159 
    160     # save the latest hash to avoid duplicate effort
    161     print "$contacts_hash" > ".$user"
    162     
    163     func "cp .$user.sieve $sieve_path/$user"
    164     [[ $TEST = 1 ]] || { 
    165         cp ".$user.sieve" $sieve_path/$user
    166     }
    167 }
    168 
    169 # main()
    170 
    171 typeset -A opts
    172 typeset -A subcommands_opts
    173 
    174 main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -no-color)
    175 subcommands_opts[__default]=""
    176 subcommands_opts[test]=""  
    177 
    178 init_commandline ${=argv}
    179 
    180 [[ -r $DIR/roundomo.users ]] || {
    181     error "roundomo.users is not configured with a list of users."; return 1 }
    182 
    183 case "$subcommand" in
    184     test) TEST=1 ; DEBUG=1 ;;
    185 esac
    186 
    187 roundomo_users=`awk '/^#/ {next} {print $0}' $DIR/roundomo.users`
    188 for u in ${(f)roundomo_users}; do
    189     whitelist "$u"
    190 done
    191 
    192