1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
function imapFolder($cf, $username) { |
4
|
|
|
$return = array(); |
5
|
|
|
$open='{'.$cf['mailhost'].':143/imap/novalidate-cert/authuser='.$cf['authuser'].'}'; |
6
|
|
|
$m_mail = imap_open($open, $username, $cf['authpassword'], OP_READONLY) |
7
|
|
|
or syslog (LOG_EMERG, $cf['user'].': Error in IMAP connection to <'.$cf['mailhost'].'>: ' . imap_last_error()); |
8
|
|
|
if ( !$m_mail ) exit(254); |
|
|
|
|
9
|
|
|
|
10
|
|
|
|
11
|
|
|
syslog (LOG_INFO,$cf['user'].': Successfully connected to <'.$cf['mailhost'].'>; Listing folders of account <'.$username.'>...'); |
12
|
|
|
//get all folder |
13
|
|
|
$list = imap_list($m_mail, $open, "*"); |
14
|
|
|
imap_close($m_mail); |
15
|
|
|
if (is_array($list)) |
16
|
|
|
foreach ($list as $mbox) |
17
|
|
|
$return[] = explode($open,$mbox,2)[1]; |
18
|
|
|
else |
19
|
|
|
syslog (LOG_INFO,$cf['user'] . ': imap_list failed: ' . imap_last_error() ); |
20
|
|
|
return $return; |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
function imapFind ($cf, $username, $folder) { |
24
|
|
|
$head=array(); |
25
|
|
|
$m_mail = imap_open('{'.$cf['mailhost'].':143/imap/novalidate-cert/authuser='.$cf['authuser'].'}'.$folder, $username,$cf['authpassword'], OP_READONLY) |
26
|
|
|
or syslog (LOG_EMERG, $cf['user'].': Error in IMAP connection to <'.$cf['mailhost'].'>: ' . imap_last_error()); |
|
|
|
|
27
|
|
|
if ( !$m_mail ) exit(254); |
|
|
|
|
28
|
|
|
|
29
|
|
|
|
30
|
|
|
syslog (LOG_INFO,$cf['user'].': Successfully connected to <'.$cf['mailhost'].">; Reading <$folder> messages of last ".$cf['oldestday'].' days on account <'.$username.'>...'); |
31
|
|
|
//get all messages |
32
|
|
|
$dateTh = date ( "d-M-Y", strToTime ( '-'.$cf['oldestday'].' days' ) ); |
33
|
|
|
$m_search=imap_search ($m_mail, "SINCE \"$dateTh\" TEXT \"Authentication-Results: \"" ); |
34
|
|
|
|
35
|
|
|
|
36
|
|
|
// Order results starting from newest message |
37
|
|
|
if ( empty($m_search) ) { |
38
|
|
|
syslog (LOG_INFO,$cf['user'].": No suitable mail found in <$folder> folder."); |
39
|
|
View Code Duplication |
if ( $ierr = imap_errors() ) |
|
|
|
|
40
|
|
|
foreach ( $ierr as $thiserr ) |
41
|
|
|
syslog (LOG_ERR, $cf['user'].": IMAP Error: $thiserr"); |
42
|
|
View Code Duplication |
if ( $ierr = imap_alerts() ) |
|
|
|
|
43
|
|
|
foreach ( $ierr as $thiserr ) |
44
|
|
|
syslog (LOG_ALERT, $cf['user'].": IMAP Alert: $thiserr"); |
45
|
|
|
imap_close( $m_mail ); |
46
|
|
|
return FALSE; |
47
|
|
|
} |
48
|
|
|
$nmes = count ($m_search); |
49
|
|
|
syslog (LOG_INFO,$cf['user'].": Found $nmes mail in <$folder> folder."); |
50
|
|
|
if ($nmes>0) rsort($m_search); |
51
|
|
|
|
52
|
|
|
// loop for each message |
53
|
|
|
foreach ($m_search as $onem) |
54
|
|
|
$head[] = imap_fetchheader($m_mail, $onem ); |
55
|
|
|
imap_close($m_mail); |
56
|
|
|
return $head; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
function dspamLevel($prob, $conf) { |
60
|
|
|
/* Calculate DSPAM Level as the Spamassassin Plugin */ |
61
|
|
|
if (is_null($prob) or is_null($conf)) return '-'; |
|
|
|
|
62
|
|
|
$t_prob = abs((($prob - 0.5) * 2) * 100); |
63
|
|
|
return round(($t_prob + ($conf*100)) / 2); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
function dspamType($classSpam) { |
67
|
|
|
switch($classSpam) { |
68
|
|
|
case 'HAM': |
69
|
|
|
return 'Innocent'; |
70
|
|
|
case 'SPAM': |
71
|
|
|
return 'Spam'; |
72
|
|
|
default: |
73
|
|
|
/* this should never happens */ |
74
|
|
|
return $classSpam; |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
function imapInfo($user,$header,$ARhosts,$dpl=false, $learn=false) { |
79
|
|
|
/* Get relevant Info from header's mail */ |
80
|
|
|
/* Each line must end with /r/n */ |
81
|
|
|
|
82
|
|
|
$result = array( |
83
|
|
|
'date' => NULL, |
84
|
|
|
'from' => NULL, |
85
|
|
|
'messageid' => NULL, |
86
|
|
|
'dmarc' => array( |
87
|
|
|
'result' => NULL, |
88
|
|
|
'dom' => NULL |
89
|
|
|
), |
90
|
|
|
'spf' => array( |
91
|
|
|
'result' => NULL, |
92
|
|
|
'dom' => NULL |
93
|
|
|
), |
94
|
|
|
'dkim' => array( |
95
|
|
|
'result' => NULL, |
96
|
|
|
'dom' => NULL |
97
|
|
|
), |
98
|
|
|
'spam' => array( |
99
|
|
|
'status' => NULL, |
100
|
|
|
'score' => NULL, |
101
|
|
|
'th' => NULL, |
102
|
|
|
), |
103
|
|
|
'dspam' => array( |
104
|
|
|
'type' => NULL, |
105
|
|
|
'level' => NULL, |
106
|
|
|
'learn' => NULL |
107
|
|
|
), |
108
|
|
|
'warn' => NULL |
109
|
|
|
); |
110
|
|
|
|
111
|
|
|
|
112
|
|
View Code Duplication |
if ( preg_match_all ('/^Authentication\-Results:\s+(?<host>[\w\.]+);(?:\s+|\r\n\s+)dmarc=(?<dmarc>\w+)\s+\(p=\w+\s+dis=\w+\)\s+header\.from=(?<DMARCfrom>[\w\.]+)/m',$header,$received) ) { |
|
|
|
|
113
|
|
|
$k=0; |
114
|
|
|
for ($i = count($received[0])-1;$i>=0;$i--) { |
115
|
|
|
foreach ($ARhosts as $mx) { |
116
|
|
|
if ($mx == $received['host'][$i]) { |
117
|
|
|
/* This is a trusted AR result */ |
118
|
|
|
$result['dmarc']['result']=$received['dmarc'][$i]; |
119
|
|
|
$result['dmarc']['dom'] = $received['DMARCfrom'][$i]; |
120
|
|
|
$k++; |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
$received=NULL; |
126
|
|
|
if ($k>1) $result['warn'][] = 'The trusted DMARC AR Headers are present more than once. Something wrong.'; |
|
|
|
|
127
|
|
|
|
128
|
|
View Code Duplication |
if ( preg_match_all('/^Authentication\-Results:\s+(?<host>[\w\.]+);(?:\s+|\r\n\s+)spf=(?<spf>\w+)\s+smtp\.(?:mailfrom|helo)=(?<SPFfrom>[\w\.]+)/m',$header,$received) ) { |
|
|
|
|
129
|
|
|
$k=0; |
130
|
|
|
for ($i = count($received[0])-1;$i>=0;$i--) { |
131
|
|
|
foreach ($ARhosts as $mx) { |
132
|
|
|
if ($mx == $received['host'][$i]) { |
133
|
|
|
/* This is a trusted AR result */ |
134
|
|
|
$result['spf']['result']=$received['spf'][$i]; |
135
|
|
|
$result['spf']['dom'] = $received['SPFfrom'][$i]; |
136
|
|
|
$k++; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
$received=NULL; |
142
|
|
|
if ($k>1) $result['warn'][] = 'The trusted SPF AR Headers are present more than once. Something wrong.'; |
143
|
|
|
|
144
|
|
|
$k=0; |
145
|
|
View Code Duplication |
if ( preg_match_all('/^Authentication\-Results:\s+(?<host>[\w\.]+);(?:\s+|\r\n\s+)dkim=(?<dkim>\w+)\s+[\w\s\(\)\-]+header\.d=(?<DKIMdom>[\w\.]+)/m',$header,$received) ) { |
|
|
|
|
146
|
|
|
for ($i = count($received[0])-1;$i>=0;$i--) { |
147
|
|
|
foreach ($ARhosts as $mx) { |
148
|
|
|
if ($mx == $received['host'][$i]) { |
149
|
|
|
/* This is a trusted AR result */ |
150
|
|
|
$result['dkim']['result']=$received['dkim'][$i]; |
151
|
|
|
$result['dkim']['dom'] = $received['DKIMdom'][$i]; |
152
|
|
|
$k++; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
$received=NULL; |
158
|
|
|
if ($k>1) $result['warn'][] = 'The trusted DKIM AR Headers are present more than once. Something wrong.'; |
159
|
|
|
|
160
|
|
|
if ($dpl) { /* Use Spamassassin Plugin */ |
161
|
|
|
if ( preg_match_all('/^X\-Spam\-Status:\s(?P<spamstatus>\w+)\,(?:\s+|\r\n\s+)score=(?P<score>[\-\.\d]+)(?:\s+|\r\n\s+)tagged_above=\-{0,1}\d+(?:\s+|\r\n\s+)required=(?P<th>[\-\.\d]+)(?:\s+|\r\n\s+)tests=\[(?:.|\r\n\s+)*DSPAM_(?P<dtype>SPAM|HAM)_(?P<dlevel>\d\d)(?:.|\r\n\s+)*\]/m',$header,$received) ) { |
162
|
|
|
$result['spam']['status']=$received['spamstatus'][0]; |
163
|
|
|
$result['spam']['score'] = $received['score'][0]; |
164
|
|
|
$result['spam']['th'] = $received['th'][0]; |
165
|
|
|
$result['dspam']['type'] = dspamType($received['dtype'][0]); |
166
|
|
|
$result['dspam']['level'] =$received['dlevel'][0]; |
167
|
|
|
} |
168
|
|
|
if (count($received[0])>1) $result['warn'][] = 'The Spamassassin Headers are present more than once. I consider only the last one.'; |
169
|
|
|
} |
170
|
|
|
else { /* Parse apart all DSPAM Header and calculate a level */ |
171
|
|
|
if ( preg_match_all('/^X\-Spam\-Status:\s(?P<spamstatus>\w+)\,(?:\s+|\r\n\s+)score=(?P<score>[\-\.\d]+)(?:\s+|\r\n\s+)tagged_above=\-{0,1}\d+(?:\s+|\r\n\s+)required=(?P<th>[\-\.\d]+)(?:\s+|\r\n\s+)tests=\[(?:.|\r\n\s+)*\]/m',$header,$received) ) { |
172
|
|
|
$result['spam']['status']=$received['spamstatus'][0]; |
173
|
|
|
$result['spam']['score'] = $received['score'][0]; |
174
|
|
|
$result['spam']['th'] = $received['th'][0]; |
175
|
|
|
if (count($received[0])>1) |
176
|
|
|
$result['warn'][]= 'The Spamassassin Headers are present more than once. I consider only the last one.'; |
177
|
|
|
} |
178
|
|
View Code Duplication |
if ( preg_match ('/\r\nX\-DSPAM\-Result:\s(?P<result>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
179
|
|
|
$result['warn'] = 'DSPAM Result invalid, not present or present more than once.'; |
180
|
|
|
else |
181
|
|
|
$result['dspam']['type']=$received['result']; |
182
|
|
|
$prob = NULL; |
183
|
|
|
$conf = NULL; |
184
|
|
View Code Duplication |
if ( preg_match ('/\r\nX\-DSPAM\-Probability:\s(?P<prob>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
185
|
|
|
$result['warn'][] = 'DSPAM Probability invalid, not present or present more than once.'; |
186
|
|
|
else |
187
|
|
|
$prob = $received['prob']; |
188
|
|
View Code Duplication |
if ( preg_match ('/\r\nX\-DSPAM\-Confidence:\s(?P<conf>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
189
|
|
|
$result['warn'][] = 'DSPAM Confidence invalid, not present or present more than once.'; |
190
|
|
|
else |
191
|
|
|
$conf = $received['conf']; |
192
|
|
|
$result['dspam']['level'] = dspamLevel($prob,$conf); |
193
|
|
|
} |
194
|
|
|
$received=NULL; |
195
|
|
View Code Duplication |
if ( preg_match ('/\r\nFrom:\s(?P<from>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
196
|
|
|
$result['warn'][] = 'From header invalid or not present'; |
197
|
|
|
else |
198
|
|
|
$result['from'] = $received['from']; |
199
|
|
|
|
200
|
|
View Code Duplication |
if ( preg_match ('/\r\nDate:\s(?P<date>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
201
|
|
|
$result['warn'][] = 'Date header invalid or not present'; |
202
|
|
|
else |
203
|
|
|
$result['date'] = $received['date']; |
204
|
|
|
|
205
|
|
|
$received=NULL; |
206
|
|
View Code Duplication |
if ( preg_match ('/\r\nMessage\-I(?:D|d):\s(?P<mid>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
207
|
|
|
$result['warn'][] = 'Message-ID invalid, not present or present more than once.'; |
208
|
|
|
else |
209
|
|
|
$result['messageid']=$received['mid']; |
210
|
|
|
|
211
|
|
|
$received=NULL; |
212
|
|
|
|
213
|
|
|
switch ($learn) { |
214
|
|
|
case 'dspamc': |
215
|
|
View Code Duplication |
if ( preg_match ('/\r\nX\-DSPAM\-Signature:\s(?P<sig>.*)\r\n/',$header,$received) != 1) |
|
|
|
|
216
|
|
|
$result['warn'] = 'DSPAM Signature invalid, not present or present more than once.'; |
217
|
|
|
else |
218
|
|
|
$result['dspam']['learn']=$received['sig']; |
219
|
|
|
break; |
220
|
|
|
case false: |
221
|
|
|
break; |
222
|
|
|
default: |
223
|
|
|
syslog (LOG_INFO,$user.': Error in "learn" imap configuration value. Please, set "dspamc" or "false".'); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
return $result; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
|
230
|
|
|
|
231
|
|
|
|
232
|
|
|
function printTableHeader($title,$content,$footer=FALSE,$fcontent) { |
233
|
|
|
print <<<END |
234
|
|
|
<caption>$title</caption> |
235
|
|
|
<thead> |
236
|
|
|
<tr> |
237
|
|
|
END; |
238
|
|
|
$kcontent = array_keys($content); |
239
|
|
|
$cols = count($kcontent); |
240
|
|
|
for ($i=0; $i<$cols; $i++) { |
241
|
|
|
$key = $kcontent[$i]; |
242
|
|
|
printf ('<th colspan="%d" rowspan="%d">%s</th>', |
243
|
|
|
!is_array($content[$key]) ?: |
244
|
|
|
count(array_keys($content[$key])) ?: '1', |
245
|
|
|
!is_array($content[$key]) ?: |
246
|
|
|
empty(array_keys($content[$key])) ? '2' : '1', |
247
|
|
|
$kcontent[$i]); |
248
|
|
|
} |
249
|
|
|
print '</tr><tr>'; |
250
|
|
|
for ($i=0; $i<$cols; $i++) { |
251
|
|
|
$key = $kcontent[$i]; |
252
|
|
|
if (is_array($content[$key])&&($hs = array_keys($content[$key]))) { |
253
|
|
|
foreach ($hs as $h) |
254
|
|
|
printf('<th>%s</th>',$h); |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
print '</tr></thead>'; |
259
|
|
|
if ($footer) { |
260
|
|
|
print '<tfoot><tr>'; |
261
|
|
|
print "<th colspan=\"$cols\">".$fcontent.'</th>'; |
262
|
|
|
print '</tr></tfoot>'; |
263
|
|
|
} |
264
|
|
|
return TRUE; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
|
268
|
|
|
function formatVal($val, $learn) { |
269
|
|
|
foreach (array_keys($val) as $key) { |
270
|
|
|
if (is_array($val["$key"]) and ($key!='warn')) |
|
|
|
|
271
|
|
|
$val["$key"] = formatVal($val["$key"], $learn); |
272
|
|
|
else { |
273
|
|
|
switch ($key) { |
274
|
|
|
case 'warn': |
275
|
|
|
if (empty($val["$key"])) |
276
|
|
|
$val["$key"] = '-'; |
277
|
|
|
else |
278
|
|
|
$val["$key"] = sprintf('<div title="%s">Y</div>',implode($val["$key"],"\n")); |
279
|
|
|
break; |
280
|
|
|
case 'learn': |
281
|
|
|
$val["$key"] = formLearn($learn, $val); |
282
|
|
|
break; |
283
|
|
|
default: |
284
|
|
|
$val["$key"] = htmlentities($val["$key"]); |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
return $val; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
function formLearn($type, $par) { |
292
|
|
|
$return = NULL; |
293
|
|
|
switch ($type) { |
294
|
|
|
case 'dspamc': |
|
|
|
|
295
|
|
|
$classes = array('Spam', 'Innocent'); |
296
|
|
|
foreach ($classes as $class) { |
297
|
|
|
$par['class'] = $class; |
298
|
|
|
$val["$class"] = sprintf('dspamc --user dspam --deliver=summary --class=%s --source=error --signature=%s', |
|
|
|
|
299
|
|
|
strtolower($class), $par['learn']); |
300
|
|
|
if (($class != $par['type'])||($par['level']<99)) |
301
|
|
|
$return .= sprintf(file_get_contents('formLearnDSPAM.htm'), |
302
|
|
|
$class,$class,$val["$class"],base64_encode(json_encode($par)),$class); |
|
|
|
|
303
|
|
|
} |
304
|
|
|
default: |
305
|
|
|
return $return; |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
function printTableRow($row, $learn, $init=true) { |
310
|
|
|
$color = 'inherit'; |
311
|
|
|
if ($init) |
312
|
|
|
$row=formatVal($row,$learn); |
313
|
|
|
foreach( $row as $key => $val) { |
314
|
|
|
if (is_array($val)) |
315
|
|
|
printTableRow($val, $learn, false); |
316
|
|
|
else { |
317
|
|
|
/* DSPAM format */ |
318
|
|
|
if (isset($row['type'])) |
319
|
|
|
switch($row['type']) { |
320
|
|
|
case 'Innocent': |
321
|
|
|
case 'HAM': |
322
|
|
|
$color = 'rgba(0,255,0, %.1f)'; |
323
|
|
|
break; |
324
|
|
|
case 'Spam': |
325
|
|
|
case 'SPAM': |
326
|
|
|
$color = 'rgba(255,0,0,%.1f)'; |
327
|
|
|
} |
328
|
|
|
/* DMARC, DKIM, SPF format */ |
329
|
|
|
if (isset($row['result'])) |
330
|
|
|
switch($row['result']) { |
331
|
|
|
case 'pass': |
332
|
|
|
$color = 'rgba(0,255,0, %.1f)'; |
333
|
|
|
break; |
334
|
|
|
case 'fail': |
335
|
|
|
$color = 'rgba(255,0,0,%.1f)'; |
336
|
|
|
} |
337
|
|
|
/* Spamassassin format */ |
338
|
|
|
if (isset($row['status'])) |
339
|
|
|
switch($row['status']) { |
340
|
|
|
case 'No': |
341
|
|
|
$color = 'rgba(0,255,0, %.1f)'; |
342
|
|
|
break; |
343
|
|
|
case 'Yes': |
344
|
|
|
$color = 'rgba(255,0,0,%.1f)'; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
$alpha = (is_numeric($val)AND($key=='type')) ? round($val/100,1) : 1.0; |
|
|
|
|
348
|
|
|
$bg = sprintf(" style=\"background-color: $color\"", $alpha); |
349
|
|
|
printf ('<td%s>%s</td>',$bg, $val); |
350
|
|
|
} |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
?> |
|
|
|
|
354
|
|
|
|
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.