minor alerts improvements
[distro-setup] / mailtest-check
1 #!/bin/bash
2
3 # Usage: mail-test-check [slow] [anything]
4 #
5 # slow: do slow checks, like spamassassin
6 #
7 # anything: consider non-interactive, dont print unless something went
8 # wrong
9
10
11 source /b/errhandle/err
12
13 [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
14
15 shopt -s nullglob
16
17 e() { $int || return 0; printf "mailtest-check: %s\n" "$*"; }
18
19
20 ## Minutes before we give error.
21 # We run this cronjob along with sending the test email every 5 minutes,
22 # so give it 1 minute to arrive, then if the latest email is older than
23 # 7 minutes, the last 2 haven't arrived in a reasonable amount of time.
24 min_limit=7
25
26
27 # spamassassin checking takes about 8 seconds. only do that every
28 # once in a while.
29 slow=false
30 if [[ $1 == slow ]]; then
31 slow=true
32 shift
33 fi
34
35 int=false
36 if [[ $SUDO_USER || $SSH_CONNECTION ]]; then
37 int=true
38 fi
39
40 if [[ $1 == int ]]; then
41 int=true
42 fi
43
44 if [[ $1 == nonint ]]; then
45 int=false
46 fi
47
48
49 if ! $int; then
50 sleep 60
51 fi
52
53 # avoid errors like this:
54 # Nov 8 08:16:05.439 [6080] warn: plugin: failed to parse plugin (from @INC): Can't locate Mail/SpamAssassin/Plugin/WLBLEval.pm: lib/Mail/SpamAssassin/Plugin/WLBLEval.pm: Permission denied at (eval 59) line 1.
55 #Nov 8 08:16:05.439 [6080] warn: plugin: failed to parse plugin (from @INC): Can't locate Mail/SpamAssassin/Plugin/VBounce.pm: lib/Mail/SpamAssassin/Plugin/VBounce.pm: Permission denied at (eval 60) line 1.
56 # i dont know why, i just found the solution online
57 cd /m/md
58
59 case $HOSTNAME in
60 bk)
61 folders=(/m/md/{expertpathologyreview.com,amnimal.ninja}/testignore)
62 froms=(ian@iankelling.org z@zroe.org testignore@je.b8.nz iank@gnu.org)
63 ;;
64 je)
65 froms=(ian@iankelling.org z@zroe.org testignore@expertpathologyreview.com testignore@amnimal.ninja)
66 folders=(/m/md/je.b8.nz/testignore)
67 ;;
68 *)
69 folders=(/m/md/l/testignore)
70 froms=(testignore@je.b8.nz testignore@expertpathologyreview.com testignore@amnimal.ninja ian@iankelling.org z@zroe.org iank@gnu.org)
71 if ! $int; then
72 timeout 120 rsync -e "ssh -oIdentitiesOnly=yes -F /dev/null -i /root/.ssh/jtuttle" -t --inplace -r 'jtuttle@fencepost.gnu.org:/home/j/jtuttle/Maildir/new/' /m/md/l/testignore/new
73 fi
74 ;;
75 esac
76
77 getspamdpid() {
78 if [[ ! $spamdpid || ! -d /proc/$spamdpid ]]; then
79 spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp' ||:)
80 fi
81 }
82 getspamdpid
83 e spamdpid: $spamdpid
84 if [[ ! $spamdpid ]]; then
85 echo $HOSTNAME mailtest spamd pid not found. systemctl status spamassassin:
86 systemctl status spamassassin
87 fi
88 tmpfile=$(mktemp)
89 for folder in ${folders[@]}; do
90 for from in ${froms[@]}; do
91 latest=
92 last_sec=0
93
94 if ! grep -rlFx "From: $from" $folder/{new,cur} >$tmpfile; then
95 e "no message found from: $from"
96 continue
97 fi
98 # webmail sends them to cur it seems
99 while read -r file; do
100 if [[ $file -nt $latest ]]; then
101 latest=$file
102 fi
103 done <$tmpfile
104
105 if [[ $latest ]]; then
106 to=$(awk '/^Envelope-to: / {print $2}' $latest)
107 last_sec=$(awk '/^Subject: / {print $4}' $latest)
108
109 if $slow; then
110 if ! $int; then
111 find $folder/new $folder/cur -type f -mmin +1080 -delete
112 fi
113 getspamdpid
114 if [[ $spamdpid ]]; then
115 if [[ $(readlink /proc/$$/ns/net) != "$(readlink /proc/$spamdpid/ns/net)" ]]; then
116 spamcpre="nsenter -t $spamdpid -n -m"
117 fi
118
119 declare -A results
120 # pyzor fails for our test message, so dont put useless load on their
121 # servers.
122 # example line that sed is parsing:
123 # (-0.1 / 5.0 requ) DKIM_SIGNED=0.1,DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,SPF_HELO_PASS=-0.001,SPF_PASS=-0.001,TVD_SPACE_RATIO=0.001 autolearn=_AUTOLEARN
124 for r in $($spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$latest" | tail -n2 | head -n1 | sed -r 's/^\([^)]*\) *//;s/=[^, ]*([, ]|$)/ /g'); do
125 case $r in
126 # got this in an update 2022-01. dun care
127 T_SCC_BODY_TEXT_LINE|SCC_BODY_SINGLE_WORD) : ;;
128 # we have a new domain, ignore this.
129 # it seems like some versions of spamassassin do BODY_SINGLE_WORD, others dont, we dun care.
130 # bayes_00 is a new one indicating ham, we dont care if its missing.
131 BAYES_00|BODY_SINGLE_WORD|FROM_FMBLA_NEWDOM*|autolearn) : ;;
132 SPF_HELO_NEUTRAL)
133 # some of my domains use neutral spf, treat them the same.
134 results[SPF_HELO_PASS]=t
135 ;;
136 *)
137 results[$r]=t
138 ;;
139 esac
140 done
141 # debugging
142 # e results = ${!results[@]}
143 missing=()
144
145 keys=(DKIM_SIGNED DKIM_VALID{,_AU,_EF} SPF_HELO_PASS SPF_PASS TVD_SPACE_RATIO)
146 if [[ $to == *@gnu.org && $from == *@gnu.org ]]; then
147 keys=(ALL_TRUSTED TVD_SPACE_RATIO)
148 elif [[ $to == *@gnu.org ]]; then
149 # eggs has RCVD_IN_DNSWL_MED
150 keys+=(RCVD_IN_DNSWL_MED)
151 elif [[ $from == *@gnu.org ]]; then
152 # eggs has these
153 keys+=(RCVD_IN_DNSWL_MED DKIMWL_WL_HIGH)
154 fi
155
156 for t in ${keys[@]}; do
157 if [[ ${results[$t]} ]]; then
158 unset "results[$t]"
159 elif [[ $t == DKIM_VALID_EF && $from == *@[^.]*.[^.]*.[^.]* ]]; then
160 :
161 # third level domains dont hit this. its because
162 # /usr/share/perl5/Mail/SpamAssassin/Plugin/DKIM.pm checks
163 # if its signed with the registryboundaries domain. afaik:
164 # we need the actual domain to sign it, this would result in
165 # a second signature. I only use second level domains for
166 # testing atm, fsf doesnt use them for anything but the
167 # forum and I dont expect that to have any deliverability
168 # problems. So, not bothering atm.
169 else
170 missing+=($t)
171 fi
172 done
173 if (( ${#results[@]} || ${#missing[@]} )); then
174 printf "$HOSTNAME spamtest %s/%s\n" "$latest"
175 if (( ${#results[@]} )); then
176 printf "unexpected %s" "${!results[*]} "
177 fi
178 if (( ${#missing[@]} )); then
179 printf "missing %s" "${missing[*]}"
180 fi
181 echo
182 echo mailtest-check: cat $latest:
183 cat $latest
184 echo mailtest-check: end of cat
185 printf "$(tput setaf 5 2>/dev/null ||:)█$(tput sgr0 2>/dev/null||:)%.0s" $(eval echo "{1..${COLUMNS:-60}}")
186 fi
187 fi # if spamdpid
188 fi # if $slow
189 fi # if [[ $latest ]]
190
191 now=$(date +%s)
192 limit=$(( now - 60 * min_limit ))
193 age_sec=$(( now - last_sec ))
194 e $((age_sec / 60)):$(( age_sec % 60 )) ago. to:$to from:$from $latest
195
196 if (( last_sec <= limit )); then
197 echo $HOSTNAME mailtest $folder $from $(date -d @$last_sec +'%a %m-%d %H:%M')
198 fi
199 done
200 done