1#!/usr/local/bin/perl --
2# anti spam smtp proxy
3use strict qw(vars subs);
4our $version 		= '1.9.9';
5our $modversion		= '(14158)';
6## no critic qw(BuiltinFunctions::ProhibitStringyEval)
7
8# (c) John Hanna, John Calvi, Robert Orso, AJ 2004 under the terms of the GPL
9# (c) Fritz Borgstedt 2006 under the terms of the GPL
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation;
13
14# ASSP V1 founded and developed to Version 1.0.12 by John Hanna
15# ASSP V1 development since 1.0.12 by John Calvi
16# ASSP V1 development since 1.2.0 by Fritz Borgstedt (+ 2014)
17# ASSP V1 development since 1.9.9 14155 by Thomas Eckardt
18# ASSP V2 pro development since 2.0.0 by Thomas Eckardt
19#
20# Feature implementations:
21# AJ - Web interface
22# Robert Orso - LDAP
23# Nigel Barling - SPF & DNSBL
24# Mark Pizzolato - SMTP Session Limits
25# Przemek Czerkas - SRS, Delaying, Maillog Search, HTTP Compression, URIBL, RWL,
26#					and many ideas and pieces
27# Craig Schmitt - SPF2 & code optimizing
28# J.R. Oldroyd - SSL support, IPv6 support, griplist/stats upload/download
29# Thomas Eckardt - 	DB Support, Blockreports, VRFY-check,
30# 					MailLog- and Resend-Function and lots of other stuff
31# Misc. contributions:
32# Wim Borghs, Doug Traylor, Lars Troen, Marco Tomasi,
33# Andrew Macpherson, Marco Michelino, Matti Haack, Dave Emory
34#
35
36
37
38use bytes;    # get rid of annoying 'Malformed UTF-8' messages
39use Encode;
40use Encode::Guess;
41use MIME::Base64;
42use File::Copy;
43
44use IO::Uncompress::Unzip qw(unzip $UnzipError) ;
45use IO::Select;
46use IO::Socket;
47use IO::Poll 0.07 qw(POLLIN POLLOUT POLLERR POLLHUP POLLNVAL);
48use Net::Ping;
49use Sys::Hostname;
50use Time::Local;
51use Time::HiRes;
52use HTML::Entities ();
53use Cwd;
54no warnings qw(uninitialized);  # possibly add   'recursion'
55use vars qw(@ISA @EXPORT);
56#
57our $utf8 = sub {};
58our $open = sub { open(shift,shift,shift); };   ## no critic
59our $unicodeFH = sub {};
60our $unicodeDH = sub { opendir(my $d,shift);my @l = readdir($d);close $d;return @l; };
61our $move = sub { File::Copy::move(shift,shift) };
62our $copy = sub { File::Copy::copy(shift,shift) };
63our $unlink = sub { unlink(shift) };
64our $rename = sub { rename(shift,shift) };
65our $chmod = sub { chmod(shift,shift) };
66our $stat = sub { stat(shift) };
67our $eF = sub { -e shift; };
68our $dF = sub { -d shift; };
69our $unicodeName = sub { $_[0];};
70#
71our $PROGRAM_NAME 	= 	$0;
72our $assp = $0;
73our $OSNAME 		=	$^O;
74our $Charsets;
75our $CE;
76our $MAINVERSION = $version . $modversion;
77our	$CFG;
78our $defaultLogCharset;
79our $defaultClamSocket;
80
81our $destinationA;
82our $RememberGUIPos = 0;
83our $WorkerNumber = 0;
84our $host2IPminTTL = 7200;
85our $LogDateFormat;
86our $LogDateLang;
87#our $LogCharset;
88our $LOG;
89our $LOGstatus;
90our $WorkerName;
91our %lngmsg;
92our %lngmsghint;
93our $NODHO = 1;
94our $globalRegisterURL;
95our $globalUploadURL;
96our $GPBinstallLib;
97our $GPBmodTestList;
98our $GPBCompLibVer;
99our $GPB;
100
101
102#### connection list
103our $ShowPerformanceData;
104our $CanUseSysMemInfo;
105our $TransferInterrupt;
106our $TransferNoInterruptTime;
107our $i_bw_time;
108our $i_tw_time;
109our $DoDamping;
110our $DoNoSpoofing4From=1;
111our $maxDampingTime;
112our $NumComWorkers;
113our $MailCountTmp;
114our $MailCount;
115our $TransferCount;
116our $TransferInterrupt;
117our $ThreadsDoStatus;
118our $lastThreadsDoStatus;
119our $MailTime;
120our $MailTimeTmp;
121our $TransferTime;
122our $TransferInterruptTime;
123#### connection list
124
125
126our $ProtPrefix = '(?:'.erw('ht').'|' .erw('f').')'.erw('tp').erw('s','?').erw('://');  # (ht|f)tps?://
127
128# set the blocking mode for HTTPS (0/1 default is 0) and HTTP (0/1 default is 0) on the GUI
129our $HTTPSblocking = 1;
130our $HTTPblocking = 1;
131
132# set the blocking mode for STATS connection (0/1) - default is 0
133our $STATSblocking = 0;
134
135our $TimeZoneDiff = time;
136$TimeZoneDiff = Time::Local::timelocal(localtime($TimeZoneDiff))-Time::Local::timelocal(gmtime($TimeZoneDiff));
137
138
139
140# change regexes in ConfigCompileRe to allow grouping only (...) -> (?:...) to spend memory
141our $RegexGroupingOnly = 1;
142
143# some special regular expressions
144our $ScheduleRe;
145our $ScheduleGUIRe;
146our $neverMatch;
147our $neverMatchRE;
148our $punyRE;
149our $EmailAdrRe;
150our $EmailDomainRe;
151our $HeaderNameRe;
152our $HeaderValueRe;
153our$EmailErrorsTo;
154our $HeaderRe;
155our $UUENCODEDRe;
156our $UTFBOMRE;
157our $UTF8BOMRE;
158our $UTF8BOM;
159our $complexREStart;
160our $complexREEnd;
161our $dot;
162our $DoTLS;
163our $DoHMM;
164our $UriDot;
165our $NONPRINT;
166our $notAllowedSMTP;
167# IP Address representations
168our $IPprivate;
169our $IPQuadSectRE;
170our $IPQuadSectDotRE;
171our $IPQuadRE;
172our $IPStrictQuadRE;
173
174# Host
175our $IPSectRe;
176our $IPSectHexRe;
177our $IPSectDotRe;
178our $IPSectHexDotRe;
179our $IPRe;
180our $IPv4Re;
181our $IPv6Re;
182our $IPv6LikeRe;
183our $PortRe;
184our $HostRe;
185our $HostPortRe;
186
187# for GUI check
188our $GUIHostPort;
189# some special regular expressions
190my $w = 'a-zA-Z0-9_';
191my $d = '0-9';
192$ScheduleRe = '(?:\S+\s+){4}\S+';
193$ScheduleGUIRe = '^('.$ScheduleRe.'(?:\|'.$ScheduleRe.")*|[$d]+|)\$";
194$neverMatch = '^(?!)';
195$neverMatchRE = quotemeta($neverMatch).'\)?\$?\)*$';
196$punyRE = 'xn--[a-zA-Z0-9\-]+';
197$EmailAdrRe=qr/[^()<>@,;:"\[\]\000-\040\x7F-\xFF]+/o;
198$EmailDomainRe=qr/(?:[$w][$w\-]*(?:\.[$w][$w\-]*)*\.(?:$punyRE|[$w][$w]+)|\[[$d][$d\.]*\.[$d]+\])/o;
199
200$HeaderNameRe=qr/\S[^\r\n]*/o;
201$HeaderValueRe=qr/[ \t]*[^\r\n]*(?:\r?\n[ \t]+\S[^\r\n]*)*(?:\r?\n)?/o;
202$HeaderRe=qr/(?:$HeaderNameRe:$HeaderValueRe)/o;
203$UUENCODEDRe=qr/\bbegin\b \d\d\d \b\S{0,72}.*?\S{61}.{0,61}\bend\b/o;
204$UTFBOMRE = qr/(?:\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\xFE\xFF|\xFF\xFE|$UTF8BOM)/o;
205$UTF8BOMRE = qr/(?:$UTF8BOM)/o;
206$NONPRINT = qr/[\x00-\x1F\x7F-\xFF]/o;
207$notAllowedSMTP = qr/CHUNKING|PIPELINING|XEXCH50|
208                     SMTPUTF8|UTF8REPLY|
209                     UTF8SMTP|UTF8SMTPA|UTF8SMTPS|UTF8SMTPAS|
210                     UTF8LMTP|UTF8LMTPA|UTF8LMTPS|UTF8LMTPAS|
211                     XCLIENT|XFORWARD|
212                     TURN|ATRN|ETRN|TURNME|X-TURNME|XTRN|
213                     SEND|SOML|SAML|EMAL|ESAM|ESND|ESOM|
214                     XAUTH|XQUE|XREMOTEQUEUE|
215                     X-EXPS|X-ADAT|X-DRCP|X-ERCP|EVFY|
216                     8BITMIME|BINARYMIME|BDAT|
217                     AUTH GSSAPI|AUTH NTLM|X-LINK2STATE
218                  /oix;
219# IP Address representations
220my $sep;
221my $v6Re = '[0-9A-Fa-f]{1,4}';
222$IPSectRe = '(?:25[0-5]|2[0-4]\d|1\d\d|0?\d?\d)';
223$IPSectHexRe = '(?:(?:0x)?(?:[A-Fa-f][A-Fa-f0-9]?|[A-Fa-f0-9]?[A-Fa-f]))';
224
225$IPprivate  = '^(?:0{1,3}\.0{1,3}\.0{1,3}\.0{1,3}|127(?:\.'.$IPSectRe.'){3}|169\.254(?:\.'.$IPSectRe.'){2}|0?10(?:\.'.$IPSectRe.'){3}|192\.168(?:\.'.$IPSectRe.'){2}|172\.0?1[6-9](?:\.'.$IPSectRe.'){2}|172\.0?2[0-9](?:\.'.$IPSectRe.'){2}|172\.0?3[01](?:\.'.$IPSectRe.'){2})$';   #RFC 1918 decimal
226$IPprivate .= '|^(?:(?:0x)?0{1,2}\.(?:0x)?0{1,2}\.(?:0x)?0{1,2}\.(?:0x)?0{1,2}|(?:0x)?7[Ff](?:\.'.$IPSectHexRe.'){3}|(?:0x)?[aA]9\.(?:0x)?[Ff][Ee](?:\.'.$IPSectHexRe.'){2}|(?:0x)?0[aA](?:\.'.$IPSectHexRe.'){3}|(?:0x)?[Cc]0\.(?:0x)?[Aa]8(?:\.'.$IPSectHexRe.'){2}|(?:0x)[Aa][Cc]\.(?:0x)1[0-9a-fA-F](?:\.'.$IPSectHexRe.'){2})$';   #RFC 1918 Hex
227$IPprivate .= '|^(?:0{0,4}:){2,6}'.$IPprivate.'$';  # privat IPv4 in IPv6
228$IPprivate .= '|^(?:0{0,4}:){2,7}[1:]?$';  # IPv6 loopback and universal
229
230$IPQuadSectRE='(?:0([0-7]+)|0x([0-9a-fA-F]+)|(\d+))';
231$IPQuadSectDotRE='(?:'.$IPQuadSectRE.'\.)';
232$IPQuadRE=qr/$IPQuadSectDotRE?$IPQuadSectDotRE?$IPQuadSectDotRE?$IPQuadSectRE/o;
233
234$complexREStart = '^(?=.*?(((?!)';
235$complexREEnd = '(?!)).*?(?!\g{-1})){';
236$dot = '[^a-zA-Z0-9\.]?d[^a-zA-Z0-9\.]?o[^a-zA-Z0-9\.]?t[^a-zA-Z0-9\.]?|[\=\%]2[eE]|\&\#0?46\;?';      # the DOT
237$UriDot = '(?:[\=\%]2[eE]|\&\#0?46\;?|\.)';
238
239$IPSectDotRe = '(?:'.$IPSectRe.'\.)';
240$IPSectHexDotRe = '(?:'.$IPSectHexRe.'\.)';
241$IPv4Re = qr/(?:
242(?:$IPSectDotRe){3}$IPSectRe
243|
244(?:$IPSectHexDotRe){3}$IPSectHexRe
245)/xo;
246
247# privat IPv6 addresses
248$IPprivate .= <<EOT;
249|^(?i:FE[89A-F][0-9A-F]):
250(?:
251(?:(?:$v6Re:){6}(?:                                $v6Re      |:))|
252(?:(?:$v6Re:){5}(?:                   $IPv4Re |   :$v6Re      |:))|
253(?:(?:$v6Re:){4}(?:                  :$IPv4Re |(?::$v6Re){1,2}|:))|
254(?:(?:$v6Re:){3}(?:(?:(?::$v6Re)?    :$IPv4Re)|(?::$v6Re){1,3}|:))|
255(?:(?:$v6Re:){2}(?:(?:(?::$v6Re){0,2}:$IPv4Re)|(?::$v6Re){1,4}|:))|
256(?:(?:$v6Re:)   (?:(?:(?::$v6Re){0,3}:$IPv4Re)|(?::$v6Re){1,5}|:))|
257                (?:(?:(?::$v6Re){0,4}:$IPv4Re)|(?::$v6Re){1,6}|:)
258)\$
259EOT
260$IPprivate = qr/$IPprivate/xo;
261
262# RFC4291, section 2.2, "Text Representation of Addresses"
263$sep = '[:-]';
264$IPv6Re = $IPv6LikeRe = <<EOT;
265(?:
266(?:(?:$v6Re$sep){7}(?:                                         $v6Re      |$sep))|
267(?:(?:$v6Re$sep){6}(?:                         $IPv4Re |   $sep$v6Re      |$sep))|
268(?:(?:$v6Re$sep){5}(?:                     $sep$IPv4Re |(?:$sep$v6Re){1,2}|$sep))|
269(?:(?:$v6Re$sep){4}(?:(?:(?:$sep$v6Re)?    $sep$IPv4Re)|(?:$sep$v6Re){1,3}|$sep))|
270(?:(?:$v6Re$sep){3}(?:(?:(?:$sep$v6Re){0,2}$sep$IPv4Re)|(?:$sep$v6Re){1,4}|$sep))|
271(?:(?:$v6Re$sep){2}(?:(?:(?:$sep$v6Re){0,3}$sep$IPv4Re)|(?:$sep$v6Re){1,5}|$sep))|
272(?:(?:$v6Re$sep)   (?:(?:(?:$sep$v6Re){0,4}$sep$IPv4Re)|(?:$sep$v6Re){1,6}|$sep))|
273(?:        $sep    (?:(?:(?:$sep$v6Re){0,5}$sep$IPv4Re)|(?:$sep$v6Re){1,7}|$sep))
274)
275EOT
276
277$IPv6Re =~ s/\Q$sep\E/:/go;
278$IPv6Re = qr/$IPv6Re/xo;
279$IPv6LikeRe = qr/$IPv6LikeRe/xo;
280
281$IPRe = qr/(?:$IPv4Re|$IPv6Re)/xo;
282
283# re for a single port - could be number 1 to 65535
284$PortRe = qr/(?:(?:[1-6]\d{4})|(?:[1-9]\d{0,3}))/o;
285# re for a single host - could be an IP a name or a fqdn
286$HostRe = qr/(?:(?:$IPv4Re|\[?$IPv6Re\]?)|$EmailDomainRe|\w\w+)/o;
287$HostPortRe = qr/$HostRe:$PortRe/o;
288$GUIHostPort = qr/^((?:(?:$PortRe|$HostPortRe)(?:\|(?:$PortRe|$HostPortRe))*)|)$/o;
289#
290our $HamTagRE;
291our $SpamTagRE;
292$SpamTagRE = qr/(?:
293                  \[
294                  (?:
295                   Attachment | AUTHError
296
297                   Backscatter | BATV | Bayesian |
298                   BlackDomain | BlackHELO | BombBlack |
299                   BombData | BombHeader | BombRe |
300                   BombScript | BombSender | BounceAddress |
301
302                   Collect | Connection | CountryCode |
303
304                   DCC | DNSBL | Delayed | DenyIP |
305                   DenyStrict | DomainKey | DKIM |
306
307                   Extreme | ForgedHELO |
308                   ForgedLocalSender | FromMissing |
309
310                   History | HMM |
311
312                   IPfrequency | IPperDomain |
313                   InternalAddress | InvalidAddress | InvalidHELO |
314
315                   MailLoop | MalformedAddress | Max-Equal-X-Header |
316                   MaxAUTHErrors | MaxErrors | MessageScore |
317                   messageSize | MaxRealMessageSize | MaxMessageSize |
318                   MissingMXA? | MsgID | MSGID-sig |
319
320                   Organization | OversizedHeader |
321
322                   PTRinvalid | PTRmissing | PenaltyBox | Penalty |
323
324                   razor | RelayAttempt |
325
326                   SPF | SRS | SpoofedSender |
327                   SuspiciousHelo |
328
329                   Trap |
330                   UnknownLocalSender | URIBL |
331                   VIRUS | ValidHELO |
332
333                   WhitelistOnly
334                  )
335                  \] |
336                   spam\sfound
337               )/iox;
338
339$HamTagRE = qr/(?:\[(?:Local|MessageOK|RWL|Whitelisted|NoProcessing)\])/io;
340our $IsDaemon;
341our $availversion = "";
342our $versionURL;
343our $NewAsspURL;
344our $NewRebuildURL;
345our $ChangeLogURL;
346our $AddURIS2MyHeader;
347our $enableCrashAnalyzer = 0;
348our $AllowInternalsInRegex = 1;
349our %NotifyFreqTF = (     # one notification per timeframe in seconds per tag
350    'info'    => 60,
351    'warning' => 60,
352    'error'   => 60
353);
354
355our $enableStrongRegexOptimization = 0;
356our $crashHMM;
357our $IPv6TestPort = '51965';
358
359our $IOEngineRun = 1;
360our $tlds_alpha_URL = 'http://data.iana.org/TLD/tlds-alpha-by-domain.txt';
361our $tlds2_URL = 'http://george.surbl.org/two-level-tlds';
362
363#    "http://www.surbl.org/tld/two-level-tlds",
364#    "http://assp.cvs.sourceforge.net/viewvc/*checkout*/assp/assp2/files/URIBLCCTLDS-L2.txt",
365our $tlds3_URL = 'http://george.surbl.org/three-level-tlds';
366#    "http://www.surbl.org/tld/three-level-tlds",
367#    "http://assp.cvs.sourceforge.net/viewvc/*checkout*/assp/assp2/files/URIBLCCTLDS-L3.txt",
368
369our $BackDNSFileURL = 'http://wget-mirrors.uceprotect.net/rbldnsd-all/ips.backscatterer.org.gz';
370our $versionURLStable = "http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1x/version.txt";
371our $NewAsspURLStable = 'http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1x/assp.pl.gz';
372
373
374our $ChangeLogURLStable = 'http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1x/changelog.txt';
375
376our $versionURLDev = "http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1dev/version.txt";
377our $NewAsspURLDev = 'http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1dev/assp.pl.gz';
378
379our $ChangeLogURLDev = 'http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1dev/changelog.txt';
380
381our $gripListDownUrl = 'http://*HOST*/cgi-bin/assp_griplist?binary';
382our $gripListUpUrl = 'http://*HOST*/cgi-bin/assp_griplist?binary';
383our $gripListUpHost = 'assp.sourceforge.net';
384$gripListDownUrl =~ s/\*HOST\*/$gripListUpHost/o;
385$gripListUpUrl  =~ s/\*HOST\*/$gripListUpHost/o;
386our $GroupsFileURL = 'http://assp.cvs.sourceforge.net/viewvc/*checkout*/assp/asspV1/files/groups.txt';
387
388eval { $^M = 'a' x ( 1 << 16 ); };    # use 64KB for "out of memory" area
389
390our $X = 2;
391our $forceDNSv4 = 1;
392sub yield {}
393
394#-----------
395our $isThreaded = 0;     # <----  never change this line !!!!!
396#-----------
397
398our $SvcStopping 	= 	0;            # AZ: 2009-02-05 - signal service status
399our $CleanUpTick	= 	0;
400our %Config;
401our %ConfigSync;
402our %ConfigSyncServer;
403our %newConfig;
404our %ConfigAdd;
405our @ConfigArray;
406# define date names for languages
407# 0:English|1:FranÁais|2:Deutsch|3:EspaÒol|4:PortuguÍs|5:Nederlands
408# 6:Italiano|7:Norsk|8:Svenska|9:Dansk|10:Suomi|11:Magyar|12:Polski|13:Romaneste
409our @Month_to_Text =
410(
411    [
412        'January', 'February', 'March', 'April', 'May', 'June',
413        'July', 'August', 'September', 'October', 'November', 'December'
414    ],
415    [
416        'janvier', 'fÈvrier', 'mars', 'avril', 'mai', 'juin',
417        'juillet', 'ao˚t', 'septembre', 'octobre', 'novembre', 'dÈcembre'
418    ],
419    [
420        'Januar', 'Februar', 'March', 'April', 'Mai', 'Juni',
421        'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
422    ],
423    [
424        'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
425        'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'
426    ],
427    [
428        'janeiro', 'fevereiro', 'marÁo', 'abril', 'maio', 'junho',
429        'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'
430    ],
431    [
432        'januari', 'februari', 'maart', 'april', 'mei', 'juni',
433        'juli', 'augustus', 'september', 'oktober', 'november', 'december'
434    ],
435    [
436        'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno',
437        'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'
438    ],
439    [
440        'januar', 'februar', 'mars', 'april', 'mai', 'juni',
441        'juli', 'august', 'september', 'oktober', 'november', 'desember'
442    ],
443    [
444        'januari', 'februari', 'mars', 'april', 'maj', 'juni',
445        'juli', 'augusti', 'september', 'oktober', 'november', 'december'
446    ],
447    [
448        'januar', 'februar', 'marts', 'april', 'maj', 'juni',
449        'juli', 'august', 'september', 'oktober', 'november', 'december'
450    ],
451    [
452        'tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu',
453        'toukokuu', 'kes‰kuu', 'hein‰kuu', 'elokuu',
454        'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'
455    ],
456    [
457        'Janu·r', 'Febru·r', 'M·rcius', '¡prilis', 'M·jus', 'J˙nius',
458        'J˙lius', 'Augusztus', 'Szeptember', 'OktÛber', 'November', 'December'
459    ],
460    [
461        'Styczen', 'Luty', 'Marzec', 'Kwiecien', 'Maj', 'Czerwiec',     # ISO-Latin-1 approximation
462        'Lipiec', 'Sierpien', 'Wrzesien', 'Pazdziernik', 'Listopad', 'Grudzien'
463    ],
464    [
465        'Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie',
466        'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'
467    ]
468);
469
470# 0:English|1:FranÁais|2:Deutsch|3:EspaÒol|4:PortuguÍs|5:Nederlands
471# 6:Italiano|7:Norsk|8:Svenska|9:Dansk|10:suomi|11:Magyar|12:polski|13:Romaneste
472our @Day_to_Text =
473(
474    [
475        'Monday', 'Tuesday', 'Wednesday',
476        'Thursday', 'Friday', 'Saturday', 'Sunday'
477    ],
478    [
479        'Lundi', 'Mardi', 'Mercredi',
480        'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'
481    ],
482    [
483        'Montag', 'Dienstag', 'Mittwoch',
484        'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'
485    ],
486    [
487        'Lunes', 'Martes', 'MiÈrcoles',
488        'Jueves', 'Viernes', 'S·bado', 'Domingo'
489    ],
490    [
491        'Segunda-feira', 'TerÁa-feira', 'Quarta-feira',
492        'Quinta-feira', 'Sexta-feira', 'S·bado', 'Domingo'
493    ],
494    [
495        'Maandag', 'Dinsdag', 'Woensdag',
496        'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag'
497    ],
498    [
499        'LunedÏ', 'MartedÏ', 'MercoledÏ',
500        'GiovedÏ', 'VenerdÏ', 'Sabato', 'Domenica'
501    ],
502    [
503        'mandag', 'tirsdag', 'onsdag',
504        'torsdag', 'fredag', 'l¯rdag', 's¯ndag'
505    ],
506    [
507        'mÂndag', 'tisdag', 'onsdag',
508        'torsdag', 'fredag', 'lˆrdag', 'sˆndag'
509    ],
510    [
511        'mandag', 'tirsdag', 'onsdag',
512        'torsdag', 'fredag', 'l¯rdag', 's¯ndag'
513    ],
514    [
515        'maanantai', 'tiistai', 'keskiviikko',
516        'torstai', 'perjantai', 'lauantai', 'sunnuntai'
517    ],
518    [
519        'hÈtfı', 'kedd', 'szerda',
520        'cs¸tˆrtˆk', 'pÈntek', 'szombat', 'vas·rnap'
521    ],
522    [
523        'poniedzialek', 'wtorek', 'sroda',     # ISO-Latin-1 approximation
524        'czwartek', 'piatek', 'sobota', 'niedziela'
525    ],
526    [
527        'Luni', 'Marti', 'Miercuri',
528        'Joi', 'Vineri', 'Sambata', 'Duminica'
529    ]
530);
531
532
533our @NonSymLangs = qw (
534    InAlphabeticPresentationForms
535    InArabic
536    InArabicPresentationFormsA
537    InArabicPresentationFormsB
538    InArmenian
539    InBasicLatin
540    InCyrillic
541    InCyrillicSupplementary
542    InEnclosedAlphanumerics
543    InGeorgian
544    InGothic
545    InGreekExtended
546    InGreekAndCoptic
547    InHebrew
548    InLatin1Supplement
549    InLatinExtendedA
550    InLatinExtendedAdditional
551    InLatinExtendedB
552    InLetterlikeSymbols
553    InMathematicalAlphanumericSymbols
554    InMathematicalOperators
555    InOldItalic
556    InOpticalCharacterRecognition
557);
558our @SymLangs;
559
560our @UniCodeScripts = qw (
561    Common
562    Arabic
563    Armenian
564    Bengali
565    Bopomofo
566    Braille
567    Buhid
568    CanadianAboriginal
569    Cherokee
570    Cyrillic
571    Devanagari
572    Ethiopic
573    Georgian
574    Greek
575    Gujarati
576    Gurmukhi
577    Han
578    Hangul
579    Hanunoo
580    Hebrew
581    Hiragana
582    Kannada
583    Katakana
584    Khmer
585    Lao
586    Latin
587    Limbu
588    Malayalam
589    Mongolian
590    Myanmar
591    Ogham
592    Oriya
593    Runic
594    Sinhala
595    Syriac
596    Tagalog
597    Tagbanwa
598    TaiLe
599    Tamil
600    Telugu
601    Thaana
602    Thai
603    Tibetan
604    Yi
605);
606
607BEGIN {
608 STDOUT->autoflush;
609 STDERR->autoflush;
610 use vars qw($wikiinfo);
611 use vars qw($base);
612 push @EXPORT, qw($base $wikiinfo);
613 $wikiinfo = "get?file=images/info.png";
614 setLocalCharsets();
615 setClamSocket();
616
617# load from command line if specified
618
619if($ARGV[0]) {
620 $base=$ARGV[0];
621} else {
622 # the last one is the one used if all else fails
623 $base = cwd();
624 unless (-e "$base/assp.cfg" || -e "$base/assp.cfg.tmp") {
625   foreach ('.','/usr/local/assp','/home/assp','/etc/assp','/usr/assp','/applications/assp','/assp','.') {
626    if (-e "$_/assp.cfg") {
627      $base=$_;
628      last ;
629    }
630   }
631 }
632 $base = cwd() if $base eq '.';
633}
634
635if ( !-e "$base/images/noIcon.png" && lc($ARGV[0]) ne '-u')
636{
637 writeExceptionLog("Abort: folder '$base/images' not correctly installed");
638 print "\nusage: perl assp.pl [baseDir|-u|] [-i|ddddd|] [--configParm:=configValue --configParm:=configValue ...|]\n";
639 print "baseDir must be defined if any other parameter is used\n";
640 die "\n\nAbort: folder '$base/images' not correctly installed\n\n";
641}
642
643if ($ARGV[0] =~ /(?:\/|-{1,2})(?:\?|help|usage)/oi) {
644 print "\nusage: perl assp.pl [baseDir|-u|] [-i|ddddd|] [--configParm:=configValue --configParm:=configValue ...|]\n";
645 print "baseDir must be defined if any other parameter is used\n";
646 print "-u - uninstalls the service on windows - no other parm is allowed\n";
647 print "-i - installs an assp service on windows\n";
648 print "ddddd - overwrites the 'webAdminPort' - same like --webAdminPort:=ddddd\n";
649 print "--configParm:=configValue - overwrites the configuration parameter (case sensitive) 'configParm' with the value 'configValue'\n";
650
651 exit;
652}
653
654unless (chdir $base) {
655 writeExceptionLog("Abort: unable to change to basedirectory $base");
656 die "\n\nAbort: unable to change to basedirectory $base\n\n";
657}
658$base = cwd();
659
660
661
662
663our $dftrestartcmd;
664our $dftrebuildcmd;
665our $dftCaFile;
666our $dftCertFile;
667our $dftPrivKeyFile;
668our $startsecondcmd;
669our $noProcessingSenderBaseIPs;
670
671our $asspbase = $base;
672
673my $assp = $0;
674my $perl = $^X;
675
676if ( $^O eq "MSWin32" ) {
677	$assp = $base.'\\'.$assp if ($assp !~ /\Q$base\E/io);
678    $assp =~ s/\//\\/go;
679    my $asspbase = $base;
680    $asspbase =~ s/\\/\//go;
681    $dftrestartcmd = "cmd.exe /C start \"ASSPSMTP restarted\" \"$perl\" \"$assp\" \"$asspbase\"";
682    $startsecondcmd = "\"$perl\" \"$assp\" \"$asspbase\" --AsASecondary:=1";
683    $dftrebuildcmd = "\"$perl\" \"$base\\rebuildspamdb.pl\" \"$asspbase\" silent &";
684} else {
685    $assp = $base.'/'.$assp if ($assp !~ /\Q$base\E/io);
686    $dftrestartcmd 	= "sleep 30;\"$^X\" \"$assp\" \"$base\" \&";
687    $startsecondcmd = "sleep 30;\"$^X\" \"$assp\" \"$base\"  --AsASecondary:=1";
688    $dftrebuildcmd 	= "\"$^X\" \"$base/rebuildspamdb.pl\" \"$base\" silent &";
689}
690$dftCertFile = "$base/certs/server-cert.pem";
691$dftCertFile =~ s/\\/\//go;
692$dftPrivKeyFile = "$base/certs/server-key.pem";
693$dftPrivKeyFile =~ s/\\/\//go;
694$dftCaFile = "$base/certs/server-ca.crt";
695$dftCaFile =~ s/\\/\//go;
696our $dftrestartcomment;
697if ( $^O ne "MSWin32" ) {
698	$dftrestartcomment = " If you use runAsUser make sure to start ASSP with root privileges (sudo).";
699}
700    # vars needed in @Config
701    # print "loading config -- base='$base'\n";
702
703
704# except for the heading lines, all config lines have the following:
705#  $name,$nicename,$size,$func,$default,$valid,$onchange,$description(,CssAdition)
706# name is the variable name that holds the data
707# nicename is a human readable pretty display name (oh how nice!)
708# size is the appropriate input box size
709# func is a function called to render the config item
710# default is the default value
711# valid is a regular expression used to clean and validate the input -- no match is an error and $1 is the desired result
712# onchange is a function to be called when this value is changed -- usually undef; just updating the value is enough
713# group is the heading group belonged to.
714# description is text displayed to help the user figure what to put in the entry
715# CssAdition (optional) adds the string to the CSS-name for nicename Style
716
717
718  our @Config = (
719 [0,0,0,'heading','Configuration Sharing'],
720
721['enableCFGShare','Enable Configuration Sharing',0,\&checkbox,'','(.*)','ConfigChangeEnableCFGSync', '<hr><b>Read all positions in this section carefully (multiple times is recommended!!!)! A wrong configuration sequence or wrong configuration values can lead in to a destroyed ASSP configuration!</b><hr>
722  If set, the configuration value and option files synchronization will be enabled. This synchronization belong to the configuration values, to the file that is possibly defined in a value and to the include files that are possibly defined in the configured file.<br />
723  If the configuration of all values in this section is valid, the synchronization status will be shown in the GUI for each config value that is, or <b>could be shared</b>. There are several configuration values, that could not be shared. The list of all shareable values could be found in the distributed file assp_sync.cfg<br /><br />
724  For an initial synchronization setup set the following config values in this order: setup syncServer, syncConfigFile, syncTestMode and as last syncCFGPass (leave isShareSlave and isShareMaster off). Use the default (distributed syncConfigFile assp_sync.cfg) file and configure all values to your needs - do this on all peers by removing lines or setting the general sync flag to 0 or 1 (see the description of syncConfigFile ).<br />
725  If you have finished this initial setup, enable isShareMaster or isShareSlave - now assp will setup all entrys in the configuration file for all sync peers to the configured default values (to 1 if isShareMaster or to 3 if isShareSlave is selected). Do this on all peers. Now you can configure the synchronization behavior for each single configuration value for each peer, if it should differ from the default setup.<br />
726  For the initial synchronization, configure only one ASSP installation as master (all others as slave). If the initial synchronization has finished, which will take up to one hour, you can configure all or some assp as master and slave. On the initial master simply switch on isShareSlave. On the inital slaves, switch on isShareMaster and change all values in the sync config file that should be bedirectional shared from 3 to 1. As last action enable enableCFGShare on the SyncSlaves first and then on the SyncMaster.<br />
727  After such an initial setup, any changes of the peers (syncServer) will have no effect to the configuration file (syncConfigFile)! To add or remove a sync peer after an initial setup, you have to configure syncServer and you have to edit the sync config file manualy.<br /><br />
728  This option can only be enabled, if isShareMaster and/or isShareSlave and syncServer and syncConfigFile and syncCFGPass are configured!<br />
729  <b>Because the synchronization is done using a special SMTP protocol (without "mail from" and "rcpt to"), this option requires an installed Net::SMTP module in PERL. This special SMTP protocol is not usable to for any MTA for security reasons, so the "sync mails" could not be forwarded via any MTA.<br />
730  For this reason all sync peers must have a direct or routed TCP connection to each other peer.</b><br />
731  <input type="button" value="show sync status" onclick="javascript:popFileEditor(\'files/sync_failed.txt\',8);" />',undef,undef,'msg009170','msg009171'],
732['isShareMaster','This is a Share Master',0,\&checkbox,'','(.*)','ConfigChangeSync', 'If selected, ASSP will send configured configuration changes to sync peers.',undef,undef,'msg009180','msg009181'],
733['isShareSlave','This is a Share Slave',0,\&checkbox,'','(.*)','ConfigChangeSync', 'If selected, ASSP will receive configured configuration changes from sync peers. To accept a sync request, every sending peer has to be defined in syncServer - even if there are manualy made entrys in the sync config file for a peer.',undef,undef,'msg009190','msg009191'],
734['syncServer','Default Sync Peers',100,\&textinput,'','(.*)','ConfigChangeSyncServer','Define all configuration sync peers here (to send changes to or to receive changes from). Sepatate multiple values by "|". Any value must be a pair of hostname or ip-address and :port, like 10.10.10.10:25 or mypeerhost:125 or mypeerhost.mydomain.com:225. The :port must be defined!<br />
735  The target port can be the listenPort , listenPort2 or relayPort of the peer.',undef,undef,'msg009200','msg009201'],
736['syncTestMode','Test Mode for Config Sync',0,\&checkbox,'','(.*)',undef, 'If selected, a master (isShareMaster) will process all steps to send configuration changes, but will not really send the request to the peers. A slave (isShareSlave) will receive all sync requests, but it will not change the configuration values and possibly sent configuration files will be stored at the original location and will get an extension of ".synctest".',undef,undef,'msg009210','msg009211'],
737['syncConfigFile','Configuration File for Config Sync*',40,\&textinput,'file:assp_sync.cfg','(file:\S+|)','ConfigChangeSyncFile','Define the synchronization configuration file here (default is file:assp_sync.cfg).<br />
738 This file holds the configuration and the current status of all synchronized assp configuration values.<br />
739 The format of an initial value is:  "varname:=syncflag" - where syncflag could be 0 -not shared and 1 -is shared - for example: HeaderMaxLength:=1 . The syncflag is a general sign, which meens, a value of 0 disables the synchronization of the config value for all peers. A value of 1, enables the peer configuration that possibly follows.<br />
740 The format after an initial setup is: "varname:=syncflag,syncServer1=status,syncServer2=status,......". The "status" could be one of the following:<br /><br />
741 0 - no sync - changes of this value will not be sent to this syncServer - I will ignore all change requests for this value from there<br />
742 1 - I am a SyncMaster, the value is still out of sync to this peer and should be synchronized as soon as possible<br />
743 2 - I am a SyncMaster, the value is still in sync to this peer<br />
744 3 - I am not a SyncMaster but a SyncSlave - only this SyncMaster (peer) knows the current sync status to me<br />
745 4 - I am a SyncMaster and a SyncSlave (bidirectional sync) - a change of this value was still received from this syncServer (peer) and should not be sent back to this syncServer - this flag will be automatically set back to 2 at the next synchronization check<br /><br />
746 ',undef,undef,'msg009220','msg009221'],
747['syncCFGPass','Config Sync Password',20,\&passinput,'','(.{6,}|)','ConfigChangeSync','The password that is used and required (additionaly to the sending IP address) to identify a valid sync request. This password has to be set equal in all ASSP installations, from where and/or to where the configuration should be synchronized.<br />
748  The password must be at least six characters long.<br />
749  If you want or need to change this password, first disable enableCFGShare here an on all peers, change the password on all peers, enable enableCFGShare on SyncSlaves then enable enableCFGShare on SyncMasters.',undef,undef,'msg009230','msg009231'],
750['syncShowGUIDetails','Show Detail Sync Information in GUI',0,\&checkbox,'','(.*)',undef, 'If selected, the detail synchronization status is shown at the top of each configuration parameter like:<br /><br />
751  nothing shown - there is no entry defined for this parameter in the syncConfigFile or it is an unsharable parameter<br />
752  "(shareable)" - the parameter is shareable but the general sync sign in the syncConfigFile is zero<br />
753  "(shared: ...)" - the detail sync status for each sync peer<br /><br />
754  If not selected, only different colored bulls are shown at the top of each configuration parameter like:<br /><br />
755  nothing shown - no entry in the syncConfigFile or it is an unsharable parameter<br />
756  "black bull <b><font color=\'black\'>&bull;</font></b>" - the parameter is shareable but the general sync sign in the syncConfigFile is zero<br />
757  "green bull <b><font color=\'green\'>&bull;</font></b>" - the parameter is shared and in sync to each peer<br />
758  "red bull <b><font color=\'red\'>&bull;</font></b>" - the parameter is shared but it is currently out of sync to at least one peer<br /><br />
759  If you move the mouse over the bull, a hint box will show the detail synchronization status.
760  <hr><div class="menuLevel1">Notes Config Sync</div>
761  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/configsync.txt\',3);"/>',undef,undef,'msg009250','msg009251'],
762[ 0, 0, 0, 'heading', 'Network Setup <a href="http://sourceforge.net/apps/mediawiki/assp/index.php?title=ASSP_Basic_Workflow" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="ASSP_Basic_Workflow"  /></a>' ],
763['ConnectionLog','Connections Logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,0,'(.*)',undef,''],
764['listenPort','SMTP Listen Port',40,\&textinput,'25','(.*)','ConfigChangeMailPort',
765  'The port number on which ASSP will listen for incoming SMTP connections (normally 25). You can specify both an IP address and port number to limit connections to a specific interface. Multiple ports  (interface:port) are possible separated by a pipe (|). Hint: If you set this port to 25, you must not set "listenPort2" to 25<p><small><i>Examples:</i>25<br /> 123.123.123.1:25|123.123.123.5:25</small></p>','Basic'],
766['smtpDestination','SMTP Destination',80,\&textinput,'127.0.0.1:1025','(.*)',undef,
767  'The IP <b>number!</b> and port number of your primary SMTP <a href=http://en.wikipedia.org/wiki/Mail_transfer_agent>mail transfer agent</a> (MTA). If multiple servers are listed and the first listed MTA does not respond, each additional MTA will be tried. If only a port number is entered, or the dynamic keyword <b>INBOUND</b> is used with a port number, then the connection will be established to the local IP address on which the connection was received. This is useful when you have several IP addresses with different domains or profiles in your MTA. If INBOUND:PORT is used, ReportingReplies (Analyze,Help,etc and CopyMail will go to 127.0.0.1:PORT. If your needs are different, use smtpReportServer (SMTP Reporting Destination) and sendAllDestination (Copy Spam SMTP Destination). Separate multiple entries by "|".<small><i>Examples:</i>127.0.0.1:1025, 127.0.0.1:1025|127.0.0.5:1025, INBOUND:1025</small>','Basic',undef,'msg000030','msg000031'],
768['EmailReportDestination','ASSP Internal Mail Destination',40,\&textinput,'','(\S*)',undef,
769 'Port to connect to when  ASSP sends replies to email-interface mails, notifications and block reports. Must be set when smtpDestination contains INBOUND. For example "10.0.1.3:1025", etc.'],
770['listenPort2','Second SMTP Listen Port',40,\&textinput,'587','(.*)','ConfigChangeMailPort2',
771  'A secondary port number on which ASSP can accept SMTP connections. This is useful as a dedicated port for TLS or VPN clients or for those who cannot directly send mail to a mail server outside of their ISP\'s network because the ISP is blocking port 25. Multiple ports  (interface:port) are possible separated by a pipe (|). Hint: If you set this port to 587, you must not set another portlike "listenPort" to 587<p><small><i>Examples:</i> 587<br />192.168.0.100:587<br />192.168.0.100:587|192.168.0.101:587</small></p>'],
772['smtpAuthServer','Second SMTP Destination',40,\&textinput,'','(\S*)',undef,
773  'The IP address/hostname and port number to connect to when mail is received on the second SMTP listen port. If the field is blank, smtpDestination will be used. The purpose of this setting is to allow remote users to make authenticated connections and transmit their email without encountering SPF failures.<p><small><i>Examples:</i>127.0.0.1:687</small></p>'],
774['NoAUTHlistenPorts','Disable AUTH support on listenPorts',80,\&textinput,'','(.*)','ConfigChangeNoAUTHPorts',
775  'This disables the SMTP AUTH command on the defined listenPorts. This option works for listenPort , listenPort2 and listenPortSSL . The listener definition here has to be the same like in the port definitions. Separate multiple entries by "|".<p><small><i>Examples:</i> 25, 127.0.0.1:25, 127.0.0.1:25|127.0.0.2:25 </small></p>',undef,undef,'msg008060','msg008061'],
776
777['EnforceAuth',"Force SMTP AUTH on Second SMTP Listen Port",0,\&checkbox,0,'(.*)',undef,
778  'Force clients connecting to the second listen port to authenticate before transferring mail. To use this setting, both listenPort2 (Second SMTP Listen Port) and smtpAuthServer (Second SMTP Destination) must be configured.',undef,undef,'msg000090','msg000091'],
779['DisableExtAUTH','Disable SMTP AUTH for External Clients',0,\&checkbox,'','(.*)',undef,'If you do not want external clients (IP not in acceptAllMail or relayPort is not used) to use SMTP AUTH - for example to prevent address and password harvesting - check this option.<br />
780  The "AUTH" offer in the EHLO and HELP reply will be stripped out, if set to on.<br />
781  Notice: setting this option to ON could prevent roaming users (dynamic IP) from being able to authenticate!',undef,undef,'msg010250','msg010251'],
782
783['enableINET6','Enable IPv6 support',0,\&checkbox,'','(.*)','ConfigChangeIPv6','For IPv6 network support to be enabled, check this box. Default is disabled. IO::Socket::INET6 is able to handle both IPv4 and IPv6. NOTE: This option requires an installed IO::Socket::INET6 module in PERL and your system should support IPv6 sockets.<br />
784  Before you enable or disable IPv6, please check every IP listener and destination definition in assp and correct the settings. <span class="negative">Changing this requires a restart of ASSP!</span> IPv4 addresses are defined for example 192.168.0.1 or 192.168.0.1:25 - IPv6 addresses are defined like [FE80:1:0:0:0:0:0:1]:25 or [FE80:1::1]:25 ! If an IPv4 address is defined for a listener, assp will listen only on the IPv4 socket. If an IPv6 address is defined for a listener, assp will listen only on the IPv6 socket. If only a port is defined for a listener, assp will listen on both IPv4 and IPv6 sockets.<br />
785  ',undef,undef,'msg009480','msg009481'],
786['smtpDestinationRT','SMTP Destination Routing Table*',80,\&textinput,'','(\S*)','configChangeRT',
787  'If INBOUND is used in the SMTP Destination field, the rules specified here are used to route the inbound IP address to a different outbound IP address. You must specify a port number with the outbound IP address. This feature works by assigning as many IP addresses to ASSP as you have different receiving Mailservers.
788  <p><small><i>Example:</i>141.120.110.1=>141.120.110.129:25|141.120.110.2=>141.120.110.130:125|141.120.110.3=>141.120.110.130:125</small></p><span class="negative"> requires ASSP restart</span>
789<hr /><div class="menuLevel1">Notes On Network Setup</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/network.txt\',3);" />
790'],
791
792
793[0,0,0,'heading','SMTP Session Limits '],
794['SessionLog','Session Limit Logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
795  ''],
796['MaxErrors','Maximum Errors Per Session',5,\&textinput,'3','(\d+)',undef,
797'The maximum number of SMTP session errors encountered before the
798connection is dropped. Scoring is done  with meValencePB.'],
799['MaxAUTHErrors','Max Number of AUTHentication Errors',10,\&textinput,'','(\d*)',undef,
800 'If an IP (/24 network is used) exceeds this number of authentication errors (535 or 530) the transmission of the current message will be canceled and any new connection from that IP will be blocked for 5-10 minutes.<br />
801  Every 5 Minutes the \'AUTHError\' -counter of the IP will be decreased by one. autValencePB is used for scoring.<br />
802  No limit is imposed by ASSP if the field is left blank or set to 0. This option allows admins to prevent external bruteforce or dictionary attacks via AUTH command. Whitelisted, NPexcludeIPs and NoProcessing IP\'s are ignored like any relayed connection.',undef,undef,'msg009310','msg009311'],
803['autValencePB','Bad SMTP Authentication Score',10,\&textinput,60,'(.*)',undef, ''],
804['noMaxAUTHErrorIPs','Do not check MaxAUTHErrors for these IP\'s*',40,\&textinput,'','(\S*)','ConfigMakeIPRe','List of IP\'s which should not be checked for MaxAUTHErrors .  For example: 145.145.145.145|145.146.',undef,undef,'msg009580','msg009581'],
805['DoSameSubject','Check Number of Same Subjects','0:disabled|1:block|2:monitor|3:score',\&listbox,3,'(.*)',undef,
806 'Scoring is done  with isValencePB.'],
807['isValencePB','Same Subject Score',10,\&textinput,150,'(.*)',undef, '',undef,undef,'msg002800','msg002801'],
808['SameSubjectOnly','Check SameSubject for these Users only*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
809 'Skip SameSubject Check for these local addresses. <br />
810  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).  Wildcards are supported (fribo*@domain.com).<br />
811  For example: fribo*@thisdomain.com|jhanna|@sillyguys.org ',undef,undef,'msg010070','msg010071'],
812['SameSubjectSkipRe','Skip SameSubject Check for this Regex*',80,\&textinput,'Mail delivery failed|itunes|Returned to Sender|Delivery Status Notification|List-Unsubscribe|Newsletter','(.*)','ConfigCompileRe','Skip SameSubject Check for this regular expression. If the content of the email matches this regular expression , DoSameSubject will not be done. For example: \'Mail delivery failed|itunes|Returned to Sender|Delivery Status Notification|Re:$|List-Unsubscribe|Newsletter\' .'],
813['SameSubjectNoAddresses','Skip SameSubject Check for these Users*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
814 'A list of local addresses, for which the \'SameSubject Check\' should not be done.<br />
815  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).  Wildcards are supported (fribo*@domain.com).<br />
816  For example: fribo*@thisdomain.com|jhanna|@sillyguys.org ',undef,undef,'msg010080','msg010081'],
817
818['SameSubjectNoIP','Skip SameSubject Check for these IP\'s*',80,\&textinput,'','(\S*)','ConfigMakeIPRe','Mail from these IP numbers will pass through without SameSubject check. For example: 145.145.145.145',undef,undef,'msg010090','msg010091'],
819
820['SameSubjectInterval','SameSubject Interval',10,\&textinput,'3600','(\d*)',undef,'The time interval in seconds in which the number of same subjects should not exceed a specific limit ( SameSubjectNumber ).<br />
821  Use this in combination with SameSubjectNumber to limit the number of same subjects in a given interval. A value of 0  will disable this feature '],
822['SameSubjectExpiration','SameSubject Expiration',10,\&textinput,'360','(\d*)',undef,'The time in days in which found same subjects should be kept in cache.<br />
823  <input type="button" value="SameSubject Cache" onclick="javascript:popFileEditor(\'pb/pbdb.samesubject.db\',\'1h\');" />',undef,undef,'msg010050','msg010051'],
824
825['SameSubjectNumber','SameSubject Limit',10,\&textinput,'5','(\d*)',undef,'The number of same subjects that should not be exceeded in a specific time interval ( SameSubjectIntervalerval ).<br />
826  Use this in combination with SameSubjectInterval to limit the number of same subjects in a given interval. A value of 0 (default) will disable this feature and clean the cache within five minutes.<br />
827  <input type="button" value="SameSubject Cache" onclick="javascript:popFileEditor(\'pb/pbdb.samesubject.db\',\'1h\');" />',undef,undef,'msg010060','msg010061'],
828['maxSMTPSessions','Maximum Sessions',5,\&textinput,'64','(\d?\d?\d?)',undef,
829  'The maximum number of simultaneous SMTP sessions. This can prevent server overloading and DoS attacks. 64 simultaneous sessions are typically enough. No entry or zero means no limit.'],
830['noMaxSMTPSessions','No Maximum Sessions IP addresses*',60,\&textinput,'','(.*)','ConfigMakeIPRe','Mail from any of these IP addresses and Hostnames will pass through without checking maximum number of simultaneous SMTP sessions. For example: localhost|145.145.145.145'],
831['maxSMTPipSessions','Maximum Sessions Per IP address',3,\&textinput,'0','(\d?\d?\d?)',undef,
832  'The maximum number of SMTP sessions allowed per IP address. Use this setting to prevent server overloading and DoS attacks. 10 sessions are typically enough. If left blank or set to 0 there is no limit imposed by ASSP. ispip (ISP/Secondary MX Servers) and acceptAllMail (Accept All Mail) matches are excluded from SMTP session limiting. Scoring is done  with iplValencePB.'],
833['iplValencePB','IP Parallel Sessions Score',10,\&textinput,2,'(.*)',undef, '',undef,undef,'msg002820','msg002821'],
834
835
836['HeaderMaxLength','Maximum Header Size',10,\&textinput,0,'(.*)',undef,
837  'The maximum allowed header length, in bytes. At each mail hop header information is added by the mail server. A large mail header can indicate a mail loop. If the value is blank or 0 the header size will not be checked.'],
838['MaxEqualXHeader','Maximum Equal X-Header Lines*',40,\&textinput,'*=>20','^((?:.+?\s*=>\s*\d+(?:\s*\|.+?\s*=>\s*\d+)*)|\s*file\s*:\s*.+|)$','configUpdateStringToNum',
839 'The maximum allowed equal X-header lines - eg. "X-SubscriberID". If the value is set to empty the header will not be checked for equal X-header lines. This check will be skipped for noprocessing, whitelisted and outgoing mails.<br />
840  The default is "*=&gt;20", which means any X-header can occure 20 time maximum. You can define different values for different X-headers - wildcards like "*" and "?" are allowed to be used.<br />
841  For example:<br />
842  *=&gt;20|X-Notes-Item=&gt;100|X-Subscriber*=&gt;10|X-AnyTag=&gt;0<br />
843  An value of zero disables the check for the defined X-header. The check is also skipped if no default like "*=&gt;20" is defined and the X-header defintion is not found.',undef,undef,'msg009060','msg009061'],
844['detectMailLoop','Detect Possible Mailloop',10,\&textinput,'10','(.*)',undef,
845 'If set to a value higher than 0, ASSP count its own Received-header in the header of the mail. If this count exceeds the defined value, the transmission of the message will be canceled.'],
846
847['maxSize','Max Size of Outgoing Message',10,\&textinput,'','(.*)',undef,
848 'If the value of ([message size]) exceeds maxSize in bytes the transmission of the local message will be canceled. No limit is imposed by ASSP if the field is left blank or set to 0. This option allows admins to limit useless bandwidth wasting based on the transmit size.'],
849['MaxSizeAdr','Max Size of Local Message Adresses*',40,\&textinput,'file:files/MaxSize.txt','(\s*file\s*:\s*.+|)','configUpdateMaxSize',
850'Use this parameter to set individual maxSize values for email addresses, domains, user names and IP addresses. A file must be specified if used.<br />
851Accepts specific addresses (user@domain.com), user parts (user), entire domains (@domain.com) and IP addresses (CIDR notation like 123.1.101/32 is here not supported!) - group definitions could be used. Use one entry per line. Wildcards are supported (fribo*@domain.co?). A second parameter separated by "=>" specifies the size limit. <br />
852For example:<br />
853fribo*@thisdomain.co?=>1000000<br />
854jhanna=>0<br />
855@sillyguys.org=>500000<br />
856101.1.2.*=>0<br />
857
858If multiple matches (values) are found in a mail for any IP address in the transport mail chain, any envelope recipient and the envelope sender, the highest value or 0 (no limit) will be used! If no match (value) is found in a mail, the definition in maxSize will take place.'
859,undef,undef,'msg009510','msg009511'],
860['maxSizeExternal','Max Size of Incoming Message',10,\&textinput,'','(.*)',undef,
861 'If the value of ([message size]) exceeds maxSizeExternal in bytes the transmission of the message will be canceled. No limit is imposed by ASSP if the field is left blank or set to 0. This option allows admins to limit useless bandwidth wasting based on the transmit size.'],
862['MaxSizeExternalAdr','Max Size of External Message Adresses*',40,\&textinput,'file:files/MaxSizeExt.txt','(\s*file\s*:\s*.+|)','configUpdateMaxSize',
863'Use this parameter to set individual maxSizeExternal values for email addresses, domains, user names and IP addresses. A file must be specified if used.<br />
864Accepts specific addresses (user@domain.com), user parts (user), entire domains (@domain.com) and IP addresses (CIDR notation like 123.1.101/32 is here not supported!) - group definitions could be used. Use one entry per line. Wildcards are supported (fribo*@domain.co?). A second parameter separated by "=>" specifies the size limit. <br />
865For example:<br />
866fribo*@thisdomain.co?=>1000000<br />
867jhanna=>0<br />
868@sillyguys.org=>500000<br />
869101.1.2.*=>0<br />
870
871If multiple matches (values) are found in a mail for any IP address in the transport mail chain, any envelope recipient and the envelope sender, the highest value or 0 (no limit) will be used! If no match (value) is found in a mail, the definition in maxSizeExternal will take place.'
872,undef,undef,'msg009520','msg009521'],
873
874
875['noMaxSize','Don\'t Check Messages from these Addresses/Domains*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
876  'Don\'t check the value of  maxSizeExternal and maxRealSizeExternal in messages from these addresses/domain. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).'],
877
878['maxRealSize','Max Real Size of Outgoing Message',10,\&textinput,'','(.*)',undef,
879 'If the value of (number of [rcpt to] * [message size]) exceeds maxRealSize in bytes the transmission of the message will be canceled. No limit is imposed by ASSP if the field is left blank or set to 0. This option allows admins to limit useless bandwidth wasting based on the total transmit size.'],
880['MaxRealSizeAdr','Max Real Size of Local Message Adresses*',40,\&textinput,'file:files/MaxRealSize.txt','(\s*file\s*:\s*.+|)','configUpdateMaxSize',
881'Use this parameter to set individual maxRealSize values for email addresses, domains, user names and IP addresses. A file must be specified if used.<br />
882Accepts specific addresses (user@domain.com), user parts (user), entire domains (@domain.com) and IP addresses (CIDR notation like 123.1.101/32 is here not supported!) - group definitions could be used. Use one entry per line. Wildcards are supported (fribo*@domain.co?). A second parameter separated by "=>" specifies the size limit. <br />
883For example:<br />
884fribo*@thisdomain.co?=>1000000<br />
885jhanna=>0<br />
886@sillyguys.org=>500000<br />
887101.1.2.*=>0<br />
888
889If multiple matches (values) are found in a mail for any IP address in the transport mail chain, any envelope recipient and the envelope sender, the highest value or 0 (no limit) will be used! If no match (value) is found in a mail, the definition in maxRealSize will take place.'
890,undef,undef,'msg009490','msg009491'],
891['maxRealSizeExternal','Max Real Size of Incoming Message',10,\&textinput,'','(.*)',undef,
892 'If the value of (number of [rcpt to] * [message size]) exceeds maxRealSizeExternal in bytes the transmission of the external message will be canceled. No limit is imposed by ASSP if the field is left blank or set to 0. This option allows admins to limit useless bandwidth wasting based on the total transmit size.'],
893['MaxRealSizeExternalAdr','Max Real Size of External Message Adresses*',40,\&textinput,'file:files/MaxRealSizeExt.txt','(\s*file\s*:\s*.+|)','configUpdateMaxSize',
894'Use this parameter to set individual maxRealSizeExternal values for email addresses, domains, user names and IP addresses. A file must be specified if used.<br />
895Accepts specific addresses (user@domain.com), user parts (user), entire domains (@domain.com) and IP addresses (CIDR notation like 123.1.101/32 is here not supported!) - group definitions could be used. Use one entry per line. Wildcards are supported (fribo*@domain.co?). A second parameter separated by "=>" specifies the size limit. <br />
896For example:<br />
897fribo*@thisdomain.co?=>1000000<br />
898jhanna=>0<br />
899@sillyguys.org=>500000<br />
900101.1.2.*=>0<br />
901
902If multiple matches (values) are found in a mail for any IP address in the transport mail chain, any envelope recipient and the envelope sender, the highest  value or 0 (no limit) will be used! If no match (value) is found in a mail, the definition in maxRealSizeExternal will take place.'
903,undef,undef,'msg009500','msg009501'],
904['maxRealSizeError','Max Real Size Error Message',80,\&textinput,'552 message exceeds MAXREALSIZE byte (size * rcpt)','(552 .*)',undef,'SMTP error message to reject maxRealSize exceeding mails. For example:552 message exceeds MAXREALSIZE byte (size * rcpt)! MAXREALSIZE will be replaced by the value of maxRealSize.'],
905
906['smtpIdleTimeout','SMTP Idle Timeout',5,\&textinput,'600','(\d?\d?\d?\d?)',undef,
907 'The number of seconds a session is allowed to be idle before being forcibly disconnected. No limit is imposed by ASSP if the field is left blank or set to 0. If you have not defined an IdleTimeout on your MTA, this value should not be set to 0, because then a connection will never be timed out! <input type="button" value=" Show Timeout Cache" onclick="javascript:popFileEditor(\'pb/pbdb.smtptimeout.db\',6);" />'],
908['idleValencePB','Timeout Score',3,\&textinput,0,'(.*)',undef, '',undef,undef,'msg008870','msg008871'],
909
910['smtpNOOPIdleTimeout','SMTP Idle Timeout after NOOP',5,\&textinput,'0','(\d?\d?\d?\d?)',undef,
911 'The number of seconds a session is allowed to be idle after a "NOOP" command is received, before being forcibly disconnected. No limit is imposed by ASSP if the field is left blank or set to 0.<br />
912  This should prevent hackers to hold and block connections by sending "NOOP" commands short before the "smtpIdleTimeout" is reached.'],
913['smtpNOOPIdleTimeoutCount','SMTP Idle Timeout after NOOP Count',5,\&textinput,'0','(\d?\d?)',undef,
914 'The number of counts a session is allowed send "NOOP" commands following on each other, before being forcibly disconnected. No limit is imposed by ASSP if the field is left blank or set to 0.<br />
915  This in cooperation with "smtpNOOPIdleTimeout" should prevent hackers to hold and block connections by sending repeatedly "NOOP" commands short before the "smtpNOOPIdleTimeout" is reached. If "smtpNOOPIdleTimeout" is not defined or 0, this value will be ignored!<hr /><div class="menuLevel1">Notes On SMTP Session Limits</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/sessionlimits.txt\',3);" />'],
916
917
918
919
920
921
922[0,0,0,'heading','SPAM Control/Testmode <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Getting_Started" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Getting Started" /></a>'],
923['spamSubject','Prepend Subject of Spam Mails',20,\&textinput,'','(.*)',undef,'For example: [SPAM]','Basic'],
924
925['allTestMode','Set all Filters to TestMode',0,\&checkbox,'','(.*)','','Setting TestMode will tell ASSP not to reject the mail but rather build up the whitelist and spam and notspam collections. This can go on for some time without disturbing normal operation. Be sure spamSubject is blank, no user should see anything strange. If you want only selected filters to work in testmode set them in the filter section','Basic'],
926
927['spamTag','Prepend Spam Tag',0,\&checkbox,'','(.*)',undef,'ASSP uses many methods. The method which caught the spam  will be prepended to the subject of the email. For example: [DNSBL]'],
928
929['NotSpamTag','Ham Password',80,\&textinput,'','(.*)',undef,'If an incoming email matches this text string it will be considered not-spam. This can be used in SpamError to ask for resending the mail with this text in the subject.'],
930['NotSpamTagAutoWhite','NotSpamTag will whitelist',0,\&checkbox,'','(.*)',undef,'If a sender uses the NotSpamTag, the senderaddress will be whitelisted.'],
931['NotSpamTagRandom','Generate NotSpamTag Randomly',0,\&checkbox,'0','(.*)','updateNotSpamTag','ASSP will use MSGIDSec to make a NotSpamTag. This will change daily. The last 10 days will be saved.'],
932
933
934['SpamError','Spam Error',120,\&textinput,'554 5.7.1 Mail (SESSIONID) appears to be unsolicited - resend the mail with NOTSPAMTAG appended to the subject and contact postmaster@LOCALDOMAIN for resolution','([245]\d\d .*)',undef,'SMTP error message to reject spam. The literal LOCALDOMAIN will be replaced by the recipient domain or defaultLocalHost. SESSIONID will be replaced by the unique ASSP identifier set by uniqeIDLogging. REASON will be replaced by the actual reason. NOTSPAMTAG will be replaced by NotSpamTag. MYNAME will be replaced by myName.'],
935['SpamErrorLocal','Spam Error for Local Messages',120,\&textinput,'554 5.7.1 Mail (SESSIONID) appears to be unsolicited - REASON - contact postmaster@LOCALDOMAIN for resolution','([245]\d\d .*)',undef,'SMTP error message to reject spam. The literal LOCALDOMAIN will be replaced by the recipient domain or defaultLocalHost. SESSIONID will be replaced by the unique ASSP identifier set by uniqeIDLogging. REASON will be replaced by the actual reason. NOTSPAMTAG will be replaced by NotSpamTag. MYNAME will be replaced by myName.'],
936
937
938
939['send250OK','Send 250 OK ',0,\&checkbox,'','(.*)',undef,
940 'Set this checkbox if you want ASSP to reply with \'250 OK\' instead of SMTP error code \'554 5.7.1\'.'],
941
942
943['AddSpamHeader','Add Spam Header',0,\&checkbox,1,'(.*)',undef,
944 'Adds a line to the email header "X-Assp-Spam: YES" if the message is spam.'],
945
946['AddCustomHeader','Add Custom Header',80,\&textinput,'','(.*)',undef,
947 'Adds a line to the email header if the message is spam. For example: <a href="http://exchangepedia.com/blog/2008/01/assigning-scl-to-messages-scanned-by.html">X-Spam-Status:yes</a>'],
948
949
950
951['AddIPHeader','Add IP Match Header',0,\&checkbox,'','(.*)',undef,'Add X-Assp- header for all IP matches.',undef],
952['AddRegexHeader','Add  RegEx Match Header',0,\&checkbox,'','(.*)',undef,''],
953['AddIntendedForHeader','Add Intended-For Header for Recipients','1:first recipient|2:multiple recipients',\&listbox,1,'(.*)',undef,
954  '<br /><hr /><div class="menuLevel1">Notes On RWL</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/rwl.txt\',3);" />'],
955['AddSubjectHeader','Add X-ASSP-Original-Subject Header',1,\&checkbox,'','(.*)',undef,
956 'Adds a line to the email header "X-ASSP-Original-Subject: the subject".',undef,undef,'msg000300','msg000301'],
957['AddSpamReasonHeader','Add Spam Reason Header',0,\&checkbox,1,'(.*)',undef,
958 'Adds a line to the email header "X-Assp-Spam-Reason: " explaining why the message is spam.<br /><hr /><div class="menuLevel1">Notes On Spam Control</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/spamcontrol.txt\',3);" />'],
959['NoExternalSpamProb','No Outgoing X-ASSP Header',0,\&checkbox,'','(.*)',undef,
960'Check this box if you don\'t want X-Assp- headers on outgoing mail.'],
961['noGriplistUpload','Don\'t Upload Griplist Stats',0,\&checkbox,'','(.*)',undef,
962 'Check this to disable the Griplist upload when rebuildspamdb runs. The Griplist contains IP addresses and their values between 0 and 1, lower is less spammy, higher is more spammy. This value is called the grip value. ',undef,undef,'msg000230','msg000231'],
963['noGriplistDownload','Don\'t auto-download the Griplist file',0,\&checkbox,'','(.*)',undef,
964 "Set this checkbox, if you don\'t use the Griplist.  ",undef,undef,'msg000240','msg000241'],
965['GriplistDownloadNow','Run GriplistDownload Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will download the Griplist right away. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
966['noGRIP','Don\'t do Griplist for these IP addresses and Hostnames* ',80,\&textinput,'','(.*)','ConfigMakeIPRe',
967 'Enter IP addresses and Hostnames that you don\'t want to get gripvalues from. For example:server.example.com|145.145.145.145|145.146.','','7'],
968
969['DoFullGripDownload','Full Griplist Download Period',5,\&textinput,'30','(\S*)',undef,
970 'The Global Griplist is downloaded once in full, then only deltas are downloaded each day subsequently.  This option forces a new full download after this many days.  Leave it blank to not force new full downloads. Recommended: 30 days.<br /><hr /><div class="menuLevel1">Notes On Griplist</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/griplist.txt\',3);" />'],
971
972[0,0,0,'heading','SPAM Lovers'],
973['spamLovers','All Spam-Lover*',60,\&textinput,'postmaster|abuse','(.*)','ConfigMakeSLReSL',
974 'Messages to Spam-Lovers are processed and filtered by ASSP, but get tagged with spamSubject and are not blocked. Recipients here are tagged by all filters. If you want spamlovers for selected filters use: baysSpamLovers, blSpamLovers. bombSpamLovers, hlSpamLovers, atSpamLovers, spfSpamLovers, rblSpamLovers, uriblSpamLovers, srsSpamLovers, mxaSpamLovers, ptrSpamLovers, sbSpamLovers.  When a
975 Spam-Lover is not the sole recipient of a message, the message is processed
976 normally, and if it is found to be spam, it will not be delivered to the
977 Spam-Lover. delaySpamLovers are not included here and must be set additionally. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (fribo*@domain.com). Default: postmaster|abuse.<br />For example: fribo*@thisdomain.com|jhanna|@sillyguys.org
978 <hr>
979 This option and all SpamLover-Options below are accepting a second score parameter like "user@your-domain.com=>70"<br />
980 If such a parameter is defined in any option for an entry and the recipient address matches this entry and the message score exceeds the parameter value, the message will be blocked.<br />
981 If there are multiple possible matches for a recipient address found, the generic longest match (and value) will be used.'],
982['spamLoverSubjectSelected','Suppress SpamSubject For Selected Recipients*',80,\&textinput,'ALL','(.*)','ConfigMakeSLRe','spamSubject does NOT get prepended to the subject for these recipients. To enable the selection you need to uncheck spamSubjectSL.'],
983['SpamLoverTag','SpamLover Tag',80,\&textinput,'[sl]','(.*)',undef,,''],
984['SpamLoversRe','Regular Expression to Identify  SpamLovers*',80,\&textinput,'','(.*)','ConfigCompileRe',
985'If a message matches this regular expression it will not been blocked, but tagged.'],
986
987['slMaxScore','Block Spamlover Messages Above This Score',3,\&textinput,'90','(.*)',undef,
988 'Messages to spamLovers  whose score exceeds this threshold will be blocked.'],
989[0,0,0,'heading','NoProcessing'],
990
991['npSize','Incoming Messages NoProcessing Size',10,\&textinput,'500000','(.*)',undef,'This limit ensures that only incoming messages smaller than this limit are processed by ASSP. Most spam
992isn\'t bigger than a few k. ASSP will treat incoming messages larger than this SIZE (in bytes) as \'NoProcessing\' mail. Empty or 0 disables the feature.'],
993
994['noProcessingIPs','NoProcessing IPs*',60,\&textinput,'file:files/ipnp.txt','(.*)','ConfigMakeIPRe','Mail from any of these IP addresses and Hostnames will pass through without processing. Example file:<a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/ipnp.txt target=files ><span class="positive"> ipnp.txt</a>
995','','7'],
996
997['NPexcludeIPs','Exclude these IPs from noProcessingIPs *',40,\&textinput,'','(.*)','ConfigMakeIPRe','Manually maintained list of IP addresses and Hostnames which should be excluded from noProcessingIPs.'],
998
999['noProcessing','NoProcessing Addresses*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1000 'Contains addresses of sender and recpients. You can specify addresses for recipients only by using noProcessingTo, addresses for sender only should be put into noProcessingFrom. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).  Wildcards are supported (fribo*@example.com). <span class="positive">Better to use noProcessingFrom and noProcessingTo instead.'],
1001['noProcessingFrom','NoProcessing Sender*',60,\&textinput,'file:files/noprocessingfrom.txt','(.*)','ConfigMakeSLRe',
1002 'Mail from any of these addresses are proxied without processing. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).  Wildcards are supported (fribo*@example.com).'],
1003['noProcessingTo','NoProcessing Recipients*',60,\&textinput,'file:files/noprocessingto.txt','(.*)','ConfigMakeSLRe',
1004 'Mail solely to any of these addresses are proxied without processing. All recipients must be marked as noprocessing. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).  Wildcards are supported (fribo*@example.com).'],
1005['removeForeignBCC','remove Foreign BCC',0,\&checkbox,'','(.*)',undef,'Remove foreign BCC: header lines from the mail header. The remove is done before the DoHeaderAddrCheck is done!',undef,undef,'msg009780','msg009781'],
1006['noProcessingDomains','NoProcessing Domains*',60,\&textinput,'file:files/noprocessingdomains.txt','(.*)','ConfigMakeRe',
1007 'Domains from which you want to receive all mail and  proxy without processing. Your ISP, domain registration, mail list servers, stock broker, or other key business partners might be good candidates. Note this matches the end of the address, so if you don\'t want to match subdomains then include the @. Note that buy.com would also match spambuy.com but .buy.com won\'t match buy.com. For example: sourceforge.net|@google.com|.buy.com','','1'],
1008['noNoProcessing','Do not mark these Addresses as Noprocessing*',80,\&textinput,'','(.*)','ConfigMakeSLRe','Enter senders email addresses that you want to be processed, even if they are in noprocessing lists. You can list specific addresses (user@anydomain.com), addresses at any domain (user), or entire domains (@anydomain.com).  Wildcards are supported (fribo*@domain.com).<br />For example: fribo@anydomain.com|jhanna|@sillyguys.org or place them in a plain ASCII file one address per line: \'file:files/nodelayuser.txt\'.'],
1009['npRe','Regular Expression to Identify NoProcessing Incoming Mails*',60,\&textinput,'','(.*)','ConfigCompileRe',
1010 'If a message matches this Perl regular expression ASSP will treat the message as a \'NoProcessing\' mail. For example: X-Assp-Version<br /><hr /><div class="menuLevel1">Notes On NoProcessing</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/noprocessing.txt\',3);" />'],
1011
1012
1013[0,0,0,'heading','Whitelist/Redlist'],
1014['redRe','Regular Expression to Identify Redlisted Mail*',80,\&textinput,'file:files/redre.txt','(.*)','ConfigCompileRe',
1015 'If an email matches this Perl regular expression it will be
1016considered redlisted.
1017<br />The Redlist serves several purposes:
1018<br />1) the Redlist is a list of addresses that cannot contribute to the
1019whitelist. For example, if someone goes on a vacation and
1020turns on their autoresponder, put them on the redlist until
1021they return. Then as they reply to every spam they receive they won\'t
1022corrupt your non-spam collection or whitelist: \[autoreply\]
1023<br />2) Redlisted addresses will not be added to the Whitelist.
1024<br />3) Redlisted messages will not be stored in the
1025SPAM/NOTSPAM-collection.
1026<br />As all fields marked by * this field accepts
1027a list separated by | or a plain ASCII file one address per line: \'file:files/redre.txt\'. '],
1028
1029['whiteListedIPs','Whitelisted IPs*',80,\&textinput,'','(.*)','ConfigMakeIPRe','They  contribute to the Whitelist and to Notspam. For example: 145.145.145.145|146.145. <span class="positive"> All fields marked by \'*\' accept  a filepath/filename : \'file:files/ipwl.txt\'.</span>','','7'],
1030['whiteRe','Regular Expression to Identify Non-Spam* ',80,\&textinput,'','(.*)','ConfigCompileRe','If an incoming email matches this Perl regular expression it will be considered non-spam.<br />For example: Secret Ham Password|307\D{0,3}730\D{0,3}4[12]\d\d'],
1031
1032
1033['whiteListedDomains','Whitelisted Domains and Addresses*',80,\&textinput,'sourceforge.net','(.*)','ConfigMakeRe','Domains and addresses from which you want to receive all mail. Your ISP, domain registration, mail list servers, stock broker, or other key business partners might be good candidates. <span class="negative">Do not to put widely used domains here like hotmail.com.</span> Put popular domains into whiteSenderBase. Note this matches the end of the address, so if you don\'t want to match subdomains then include the @. Note that \'example.com\' would also match \'spamexample.com\' but \'.example.com\' won\'t. Wildcards are supported. For example: sourceforge.net|group*@google.com|.example.com. You may place them in a plain ASCII file one address per line:\'file:files/whitedomains.txt\'','','9'],
1034
1035
1036['WhitelistOnly','Reject All But Whitelisted Mail',0,\&checkbox,'','(.*)',undef,'Check this if you want to reject all mail from anyone NOT on the Whitelist ( whitelistdb ) and not marked noprocessing. '],
1037
1038['WhitelistOnlyAddresses','Reject All But Whitelisted Mail for these Addresses/Domains*',80,\&textinput,'','(.*)','ConfigMakeSLRe','Put here addresses/domains which should only accept whitelisted/noprocessing mail. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (*@domain.com, abuse@*, *@*).  '],
1039
1040['NoAutoWhite','Only Email-Interface Addition to Whitelist.',0,\&checkbox,'','(.*)',undef,'Check this box to  allow additions to the whitelist by email interface only.',undef,undef,'msg000980','msg000981'],
1041['NoAutoWhiteAdresses','No AutoWhite Addresses*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1042 'Mail solely to or from any of these addresses are excluded from automatic whitelist additions. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).  Wildcards are supported (fribo*@domain.com).',undef,undef,'msg009970','msg009971'],
1043['NotGreedyWhitelist','Only the envelope-sender is added/compared to the whitelist',0,\&checkbox,'1','(.*)',undef,'Normal operation includes addresses in the FROM, SENDER, REPLY-TO, ERRORS-TO, or LIST-* header fields.<br />This allows nearly all list email to be whitelisted. Check this option to disable this. Will not apply if you add/remove whitelist entries via email-interface.'],
1044
1045['WhitelistLocalOnly','Only local or authenticated users contribute to the whitelist.',0,\&checkbox,'','(.*)',undef,'Normal operation allows all local, authenticated, or whitelisted users to contribute to the whitelist.<br />Check this box to not allow whitelisted (but not local) users to add to the whitelist.'],
1046['WhitelistLocalFromOnly','Only local users with a local domain in envelope contribute to the whitelist.',0,\&checkbox,'1','(.*)',undef,'Check this box to prevent a local sender with non-local domain from contributing to the whitelist. (for example: redirected messages).'],
1047['WhitelistAuth','Whitelist mails from authenticated users.',0,\&checkbox,'1','(.*)',undef,'Mails from
1048authenticated users will be processed as whitelisted'],
1049
1050['UpdateWhitelist','Save Whitelist',5,\&textinput,3600,'(.*)','configChangeUpdateWhitelist','Save a copy of the white list every this many seconds. Empty or Zero will prevent any saving.<br /><hr /><div class="menuLevel1">Whitelist Addition/Deletions</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/whitelistadd.txt\',5);" />'],
1051['MaxWhitelistDays','Max Whitelist Days',5,\&textinput,'999','(\d+)',undef,'This is the number of days an address will be kept on the whitelist without any email to/from this address.'],
1052
1053
1054['ValidateRWL','Enable Realtime Whitelist Validation',0,\&checkbox,'','(.*)','configUpdateRWL','RWL: Real-time white list. These are lists of IP addresses that have
1055 somehow been verified to be from a known good host. Senders that pass RWL validation will pass IP-based filters. This requires an installed Net::DNS module in PERL. ',undef,undef,'msg000870','msg000871'],
1056['rwlValencePB','RWL found',3,\&textinput,-25,'(.*)',undef, '<span class="positive"> Bonus for Valence in ValidateRWL</span>',undef,undef,'msg003080','msg003081'],
1057['RWLwhitelisting','Whitelist all RWL Validated Addresses',0,\&checkbox,'','(.*)',undef,'If set, the message will also pass Bayesian Filter and URIBL.',undef,undef,'msg000880','msg000881'],
1058['RWLServiceProvider','RWL Service Providers*',80,\&textinput,'list.dnswl.org','(.*)','configUpdateRWLSP','Hostnames of RWLs to use separated by "|".<br />Examples are: list.dnswl.org',undef],
1059['RWLmaxreplies','Maximum Replies',5,\&textinput,1,'(.*)','configUpdateRWLMR','A reply is affirmative or negative reply from a RWL. The RWL module will wait for this number of replies (negative or positive) from the RWLs listed under Service Provider for up to the Maximum Time below. This number should be equal to or less than the number of RWL Service Providers listed to allow for randomly unavailable RWLs. ',undef],
1060
1061['RWLminhits','Minimum Hits',5,\&textinput,1,'(.*)','configUpdateRWLMH','A hit is an affirmative response from a RWL. The RWL module will check all of the RWLs listed under Service Provider, and flag the email with a RWL \'pass\' flag if equal to or more than this number of RWLs return a postive whitelisted response. If the number is less but not zero the email is marked \'neutral\'',undef],
1062['RWLmaxtime','Maximum Time',5,\&textinput,5,'(.*)',undef,'This sets the maximum time to spend on each message performing RWL checks',undef],
1063['noRWL','Don\'t Validate RWL for these IPs*',80,\&textinput,'','(.*)','ConfigMakeIPRe','Enter IP addresses that you don\'t want to be RWL validated, separated by pipes (|). For example: 145.145.145.145|146.145.',undef,'7'],
1064['AddRWLHeader','Add X-Assp-Received-RWL Header',0,\&checkbox,1,'(.*)',undef,'Add X-Assp-Received-RWL header to header of all emails processed by RWL.',undef],
1065
1066['RWLCacheInterval','RWL Cache Expiration Time',4,\&textinput,0,'([\d\.]+)','configUpdateRWLCR','IPs in cache will be removed after this interval in days. 0 will disable the cache.  <input type="button" value=" show cache" onclick="javascript:popFileEditor(\'pb/pbdb.rwl.db\',5);" />'],
1067
1068
1069['RWLLog','Enable RWL logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1070  '<br /><hr /><div class="menuLevel1">Notes On RWL</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/rwl.txt\',3);" />'],
1071
1072[0,0,0,'heading','Relaying <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Relaying" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="relaying not allowed" /></a>'],
1073['RelayLog','Enable Relay logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1074  '<br /><hr /><div class="menuLevel1">Notes On Relaying</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/relaying.txt\',3);" />'],
1075
1076['acceptAllMail','Accept All Mail*',80,\&textinput,'','(.*)','ConfigMakeIPRe','Relaying is allowed for these IP addresses and Hostnames. They  contribute also to the whitelist. This can take either a directly entered list of IP addresses and Hostnames separated by pipes or a plain ASCII file one address per line: \'file:files/acceptall.txt\'.<br /> An IP range is defined e.g. \'182.82.10.\'. CIDR notation is accepted (182.82.10.0/24). Hyphenated ranges can be used (182.82.10.0-182.82.10.255)','Basic','7'],
1077['DoLocalSenderDomain','Do Local Domain Check for Local Sender',0,\&checkbox,'','(.*)',undef,
1078  'If activated, each local sender address must have a valid Local Domain. Put domains into noLocalSenderCheck if they are used as sender addresses and are not local.',undef,undef,'msg001050','msg001051'],
1079['DoLocalSenderAddress','Do Local Address Check for Local Sender',0,\&checkbox,'','(.*)',undef,
1080  'If activated, each local sender address must have a valid Local Address. Put addresses into noLocalSenderCheck if they are used as sender addresses and are not local.',undef,undef,'msg001060','msg001061'],
1081['relayHostFile','Relay Host File ',40,\&textinput,'','(.*)',undef,'Similar to  acceptAllMail, but this is a file with an ABSOLUTE path, not relative to base. No IP-blocks supported. For example: /usr/local/assp/relayhosts'],
1082['localDomains','Local Domains*',80,\&textinput,'file:files/localdomains.txt','(.*)','ConfigMakeRe','Put here are the domain names that your mail system considers local. Separate entries with |  or place them in a plain ASCII file one address per line: \'file:files/localdomains.txt\'. Wildcards are supported.<br /> For example: example.org|*example.com<br />
1083If ASSP finds no other hint that the domain is local, it  will reject messages to domains not listed here with \'RelayAttempt\'. A successfull DoLDAP, DoVRFY or hit in LocalAddresses_Flat  will put the domain part of the queried address into ldaplistdb and will mark the domain as local.
1084You can set nolocalDomains to disable this check during setup and testing.
1085 ', 'Basic'],
1086['localDomainsFile','Local Domains File',40,\&textinput,'','(.*)',undef,'Similar to localDomains, but with absolute path to the file. Wildcards are not supported. For access to MTA generated files. '],
1087['DoLocalIMailDomains','Local IMail domains',0,\&checkbox,'','(.*)',undef,
1088'Consider domains in the IMail registry to be local'],
1089
1090
1091['nolocalDomains','Skip Local Domain Check',0,\&checkbox,'','(.*)','ConfigChangeNoDomains','Do not check relaying for invalid domains - let the MTA do it. This can be set to prevent \'RelayAttempt\' errors. <span class="negative">Attention: this will make ASSP an open relay, if the MTA behind it does not reject messages to unknown domains.</span>'],
1092['MaxRelayingErrors','Maximum Relaying Errors Per Session',5,\&textinput,'3','(.*)',undef,
1093'The maximum number of Relaying Errors encountered before the
1094connection is dropped. Scoring is done  with meValencePB. 0 will not count Relaying Errors. '],
1095['meValencePB','Max Errors Exceeded Score',10,\&textinput,10,'(.*)',undef, '',undef,undef,'msg002900','msg002901'],
1096['relayHost','Your mail relayhost (smarthost)',40,\&textinput,'','(.*)',undef,'Mails arriving at relayPort wiil be deliverred to the destination configured here. For example: mail.relayhost.com:25<br />if you run Exchange/Notes and you want assp to update the nonspam database and the whitelist, then enter your smtp relay host here. Blank means no relayhost. ','Basic'],
1097['relayAuthUser','Username for  Authentication to Relay Host',80,\&textinput,'','(\S*)',undef,'The username used for SMTP AUTH authentication to the relayHost  -  if your ISP need authentication on the SMTP port. Supported authentication methodes are PLAIN, LOGIN, CRAM-MD5 and DIGEST-MD5 . If the relayhost offers multiple methodes, the one with highest security option will be used. The Perl module <a href="http://search.cpan.org/search?query=Authen::SASL" rel="external">Authen::SASL</a> must be installed to use this feature! The usage of this feature will be skipped, if the sending MTA uses the AUTH command. Leave this blank, if you do not want use this feature.','Basic'],
1098['relayAuthPass','Password for  Authentication to Relay Host',80,\&textinput,'','(\S*)',undef,'The password used for SMTP AUTH authentication to the relayHost. Leave this blank, if you do not want use this feature.','Basic'],
1099['relayPort','Relay Port',40,\&textinput,'','(.*)','ConfigChangeRelayPort','Mails arriving at this port are considered local and delivered to relayHost. Tell your mail server to connect to this port as its smarthost/relayhost. For example: 225<br /> Note that you\'ll want to keep the relayPort protected from external access by your firewall.<br />You can supply an interface:port to limit connections.','Basic'],
1100['allowRelayCon','Allow Relay Connection from these IPs*',80,\&textinput,'','(\S*)','ConfigMakeIPRe','Enter any addresses that are allowed to use the relayPort , separated by pipes (|). If empty, any ip address is allowed to connect to the relayPort. If this option is defined, keep in mind : Addresses defined in acceptAllMail are <b>NOT</b> automaticly included and have to be also defined here, if they should be allowed to use the relayPort. For example: 127.0.0.1|172.16..','Basic','7'],
1101
1102['ldLDAP','Do LDAP lookup for local domains',0,\&checkbox,'','(.*)',undef,'Check local domains against an LDAP database.<br />Note: Checking this requires filling in LDAP DomainFilter ( ldLDAPFilter ).and NET::LDAP module in Perl.',undef,undef,'msg001080','msg001081'],
1103
1104['ispip','Additional MX Servers*',80,\&textinput,'','(\S*)','ConfigMakeIPRe','Enter any addresses or hostnames that are your ISP or backup MX servers, separated by pipes (|). <br />These addresses will (necessarily) bypass Griplist, IP Limiting, Delaying, PenaltyBox, SPF, DNSBL and SRS checks unless the IP can be determined by ispHostnames (ISP Connecting IP). For example: 145.145.145.145|145.145.145.146.','Basic',7],
1105['contentOnlyRe', 'Regular Expression to Identify Forwarded Messages*',80,\&textinput,'','(.*)','ConfigCompileRe',
1106 "Put anything here to identify messages which should bypass all IP based filter like PB, Sender Validation, Griplist, IP Limiting, Delaying, SPF, DNSBL and SRS. For example:  email addresses of people who are forwarding from other accounts to their mailbox on your server."],
1107['ispHostnames','Hostname of frontend IP*',80,\&textinput, '','(.*)', 'ConfigCompileRe', 'Hostnames to lookup the frontend IP.<br />If found, this address is used to perform IP-based checks on forwarded messages. <br />For example: mx1\.yourisp\.com or mx1\.yourisp\.net|mx2\.yoursecondary\.com . <i>This hostnames are found in the \'Received:\' header, like  \'Received: from ...123.123.123.123... by <span class="positive">mx1.yourisp.com</span>\'</i>. Hostnames may use regular expressions. The frontend IP must be listed in ispip. Leave this blank to disable the feature. ',undef,undef,'msg001110','msg001111'],
1108
1109
1110['send250OKISP','Send 250 OK To ISP/Secondary MX Servers',0,\&checkbox,'1','(.*)',undef,
1111 'Set this checkbox if you want ASSP to reply to IP addresses in ispip with \'250 OK\' instead of SMTP error code \'554 5.7.1\'. '],
1112
1113
1114
1115
1116['PopB4SMTPFile','Pop Before SMTP DB File',40,\&textinput,'','(.*)',undef,'Enter the DB database filename of your POP before SMTP implementation with records stored for dotted-quad IP addresses.<br />For example: /etc/mail/popip.db'],
1117['PopB4SMTPMerak','Pop Before SMTP Merak Style',0,\&checkbox,'','(.*)',undef,'If set Merak 7.5.2 is supported.'],
1118
1119['defaultLocalHost','Default Local Domain',40,\&textinput,'assp.local','(.*)',undef,'If you want to be able to send mail to local users without a domain name then put the default local domain here. <br /> Blank disables this feature. For example: assp.local<br /><hr /><div class="menuLevel1">Notes On Relaying</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/relaying.txt\',3);" />'],
1120
1121[0,0,0,'heading','Outgoing/AUTH '],
1122
1123['AUTHLogUser','Username Logging',0,\&checkbox,'1','(.*)',undef,'Write the username for AUTH (PLAIN/LOGIN) to maillog.txt.'],
1124['AUTHLogPWD','Password Logging',0,\&checkbox,'','(.*)',undef,'Write the userpassword for AUTH (PLAIN/LOGIN) to maillog.txt.'],
1125['NoFilterAuth','Do not filter mails from authenticated users.',0,\&checkbox,'1','(.*)',undef,'Mails from
1126authenticated users will pass unfiltered'],
1127
1128['OutgoingLog','Enable OutgoingLogging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1129  ''],
1130
1131
1132['npLocalRe','Regular Expression to Identify NoProcessing Local Mails*',60,\&textinput,'','(.*)','ConfigCompileRe',
1133 'If an outging message matches this Perl regular expression ASSP will treat the message as a \'NoProcessing\' mail. For example: autoreply'],
1134['blockLocalRe','Regular Expression to Identify Blocked Local Mails*',60,\&textinput,'','(.*)','ConfigCompileRe',
1135 'If an outging message matches this Perl regular expression ASSP will block the message.'],
1136['DoSameAUTHuser','Check Number of Same User','0:disabled|1:block|2:monitor|4:testmode',\&listbox,2,'(.*)',undef,
1137 ''],
1138
1139['maxSameAUTHuser','Limit Number of Same User',0,\&textinput,'5','(\d?\d?\d?)',undef,
1140 'The number of the user during  maxSameAUTHuserDuration. If a user appears more often than this it will be blocked. If left blank or 0, there is no limit imposed by ASSP.'],
1141['maxSameAUTHuserDuration','Timeframe for Counting',5,\&textinput,'1800','(\d?\d?\d?\d?)',undef,
1142  'ASSP uses this number in seconds to count the same user.'],
1143
1144['LocalFrequencyInt','Local Frequency Interval',40,\&textinput,'600','(.*)',undef,'The time interval in seconds in which the number of envelope recipients per sending address should not exceed a specific number ( LocalFrequencyNumRcpt ).<br >
1145  Use this in combination with LocalFrequencyNumRcpt to limit the number of recipients in a given interval, to prevent local abuse - for example from highjacked local accounts. A value of 0 will disable this feature and clean the cache within five minutes. To give users the chance to inform an admin about such blocked mails, local mails to EmailAdmins are never blocked because of that feature.<br />
1146  <input type="button" value="edit local Frequency Cache" onclick="javascript:popFileEditor(\'pb/pbdb.localfreq.db\',5);" />'],
1147['LocalFrequencyNumRcpt','Local Frequency Recipient Number',40,\&textinput,'51','(.*)',undef,'The number of envelope recipients per sending address that should not be exceeded in a specific time interval ( LocalFrequencyInt ).<br >
1148  Use this in combination with LocalFrequencyInt to limit the number of recipients in a given interval, to prevent local abuse - for example from highjacked local accounts. A value of 0 will disable this feature and clean the cache within five minutes. To give users the chance to inform an admin about such blocked mails, local mails to EmailAdmins are never blocked because of that feature. <br />
1149  <input type="button" value="edit local Frequency Cache" onclick="javascript:popFileEditor(\'pb/pbdb.localfreq.db\',5);" />'],
1150['LocalFrequencyOnly','Check local Frequency for this Users only*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1151 'A list of local addresses, for which the \'local frequency check\' should be done. Leave this field blank (default), to do the check for every address.<br />
1152  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).  Wildcards are supported (fribo*@domain.com).<br />
1153  For example: fribo*@thisdomain.com|jhanna|@sillyguys.org '],
1154['NoLocalFrequency','Check local Frequency NOT for this Users*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1155 'A list of local addresses, for which the \'local frequency check\' should not be done. <br />
1156  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).  Wildcards are supported (fribo*@domain.com).<br />
1157  For example: fribo*@thisdomain.com|jhanna|@sillyguys.org '],
1158['NoLocalFrequencyIP','Check local Frequency NOT for this IP\'s*',60,\&textinput,'','(.*)','ConfigMakeIPRe',
1159 'A list of local IP-addresses, for which the \'local frequency check\' should not be done.<br />
1160  For example: 145.145.145.145|145.146. ',undef,undef,'msg010110','msg010111'],
1161
1162 ['CheckLocalSenderAddress','Do Local Address Check for Local Sender','0:disabled|1:block|2:monitor',\&listbox,1,'(.*)',undef,
1163  'If activated, each local sender must have a valid Local Address - needs DoVRFY or DoLDAP or LocalAddresses_Flat.'],
1164
1165['LocalSender2NULL','Move Local Connection with wrong Sender Address to NULL',0,\&checkbox,'','(.*)',undef,
1166  'If set, ASSP will move all Local connections where the sender failed CheckLocalFromAddress or CheckLocalSenderAddress to a NULL-connection. The sender will receive "250 OK".<br /><hr /><div class="menuLevel1">Notes On Control Outgoing</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/controlout.txt\',3);" />'],
1167
1168[0,0,0,'heading','Validate Recipients'],
1169['ValidateUserLog','Enable User Validation logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1170  ''],
1171
1172
1173['DoHeaderAddrCheck','Check TO,CC and BCC headers',0,\&checkbox,'','(.*)',undef,'If enabled TO: , CC: and BCC: header lines are checked the following way:<br />
1174 1. a possible recipient replacement is done<br />
1175 2. local email address validation is done -  if OK, the next address or headerline is processed<br />
1176 3. spamtrapaddresses will be detected - scored with stValencePB - <b>mail is blocked</b> (noPenaltyMakeTraps is honored)<br />
1177 4. a local but not valid TO/CC/BCC: address will be detected and scored with irValencePB<br />
1178 5. a RelayAttempt will be detected if a BCC address is not local and scored with rlValencePB - <b>mail is blocked</b><br />
1179 The check 3 and 4 honors whitelisting , noprocessing and noBlockingIPs<br />
1180 Enable this check only, if assp is configured to validate local domains and email addresses!<br />
1181 NOTICE: that removeForeignBCC take place before this check is done - step 5 will be never reached if removeForeignBCC is enabled!',undef,undef,'msg010010','msg010011'],
1182['rlValencePB','Failed Relay Attempt Score',10,\&textinput,10,'(.*)',undef, '',undef,undef,'msg003040','msg003041'],
1183
1184
1185['irValencePB','Invalid Recipient Score',10,\&textinput,5,'(.*)',undef, '',undef,undef,'msg008940','msg008941'],
1186['stValencePB','Penalty Trap Address Score',10,\&textinput,50,'(.*)',undef,'',undef,undef,'msg003220','msg003221'],
1187
1188['LocalAddresses_Flat','Lookup Local Addresses from Here*',80,\&textinput,'','(.*)','ConfigMakeSLRe','This is an optional list of local addresses for all MTAs behind ASSP. If the address is not found here ASSP will look for other methods of verification (DoLDAP, DoVRFY). If no ASSP-verification is used, the MTA behind ASSP will do it. You can list specific addresses (user@example.com), addresses at any local domain (user), or entire domains (@example.com).  Wildcards are supported (fribo*@example.com). Separate entries with a pipe (|).<br />For example: fribo@example.com|jhanna|@example.org . You may use a plain ASCII file \'file:files/localuser.txt\'.','Basic'],
1189
1190['LocalAddresses_Flat_Domains','Use Entries without leading \'@\' as Domains',0,\&checkbox,'','([01]?)',undef,'If set entries in LocalAddresses_Flat without leading \'@\' are handled as domains,for example \'example.com\' means an entire domain.'],
1191
1192
1193['LocalAddressesNP','Do Not Validate Local Addresses if in NoProcessing List',0,\&checkbox,'','(.*)',undef,'If a recipient is found in NoProcessing, the user validation is skipped. '],
1194['RejectTheseLocalAddresses','Reject These Local Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1195'If ANY recipient is on reject list, the message will not be delivered. Used for disabled legitimate accounts, where a user may have left the company. This stops wildcard mailboxes from getting these messages. You can list specific addresses (user@example.com), addresses at any local domain (user), or entire domains (@example.com).  Wildcards are supported (fribo*@example.com). The field accepts a list separated by \'|\' (for example: fribo*@example.com|@example.com|user) or a file designated as follows (path relative to the ASSP directory): \'file:files/filename.txt\'. Putting in the file: will prompt ASSP to put up a button to edit that file. files is the subdirectory for files. The file does not need to exist, you can create it from the editor by saving it. The file must have one entry per line; anything on a line following a numbersign or a semicolon ( #  is ignored (a comment)'],
1196['BlockLocalAddressesRe','Block Local Recipients Regular Expression*',80,\&textinput,'[%|]','(.*)','ConfigCompileRe',
1197  'Block all recipient addresses which match this RegEx. Note: if you want to block the pipe char \'|\' it must be masked with the mask character \'\\\' . You may also use metacharacter brackets ([]) for this purpose.'],
1198['AllowLocalAddressesRe','Allow Local Recipient Addresses Regular Expression*',80,\&textinput,'','(.*)','ConfigCompileRe',
1199  'Allow only recipient addresses which match this RegEx.'],
1200['TrapLog','Enable Trap logging','0:nolog|1:standard|2:verbose',\&listbox,0,'(.*)',undef,
1201  ''],
1202['spamtrapaddresses','Trap Addresses* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1203  'Mail to any of these addresses will be blocked and the Valence stValencePB is added. These addresses are not checked for validity.  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com).'],
1204['UseTrapToCollect','Use Penalty Trap Addresses To Collect',0,\&checkbox,'','(.*)',undef,
1205  'If set ASSP will use spamtrapaddresses to collect spams.',undef,undef,'msg006020','msg006021'],
1206['SpamTrap2NULL','Move Connection with Trap Addresses to NULL',0,\&checkbox,'1','(.*)',undef,
1207  'If set, ASSP will move connections with spamtrapaddresses to a NULL-connection. The sender will receive "250 OK".'],
1208
1209['PenaltyTrapPolite','PenaltyTrap Reply',80,\&textinput,'550 5.1.1 User unknown: EMAILADDRESS','^([542]\d\d .+)',undef,'SMTP reply for invalid Users. Default: \'550 5.1.1 User unknown: EMAILADDRESS\' <br /> The literal EMAILADDRESS (case sensitive) is replaced by the fully qualified SMTP recipient (e.g., thisuser@example.com).',undef,undef,'msg002400','msg002401'],
1210['DoPenaltyMakeTraps','Cache Unknown Addresses','0:disabled|1:use for spamtrapaddresses|2:use for spamaddresses|3:use for validation',\&listbox,2,'(.*)',undef,
1211  'If enabled, unknown addresses are cached. If set to \'use for spamtrapaddresses\' addresses which reach the limit in PenaltyMakeTraps will be used like spamtrapaddresses. If set to \'use for spamaddresses\'  they will work like spamaddresses. If set to \'use for validation\' all entries regardless of their frequency will be used to validate incoming addresses. Note: LocalAddresses_Flat or DoLDAP or DoVRFY must be enabled.'],
1212['PenaltyMakeTraps','Unknown Address Frequency  Limit',3,\&textinput,'5','(.*)',undef,
1213  'Minimum number of times an address must appear during PBTrapCacheInterval before it will be used as spamaddress/spamtrapaddress in DoPenaltyMakeTraps.'],
1214
1215['PBTrapCacheInterval','Address Cache Expiration',4,\&textinput,1,'(.*)','configUpdateTrapCR',
1216  'Addresses will be removed after this interval in days if the frequency in PenaltyMakeTraps is not reached. <input type="button" value=" Show Address Cache" onclick="javascript:popFileEditor(\'pb/pbdb.trap.db\',5);" />'],
1217['noPenaltyMakeTraps','Exceptionlist for Address Cache*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1218 'Addresses which should not be cached. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).  Wildcards are supported (fribo*@example.com).'],
1219['DoVRFY','Verify Recipients with SMTP-VRFY',0,\&checkbox,'','(.*)',undef,  'If activated and the format \'Domain=>MTA\' is encountered in
1220 vrfyDomains recipient addresses will be verified with SMTP-VRFY (if  VRFY is not supported \'MAIL FROM:\' and \'RCPT TO:\' will be used).
1221 If you know that VRFY is not supported with a MTA, you may put the MTA into VRFYforceRCPTTO. <br /><input type="button" value=" Show Found Cache" onclick="javascript:popFileEditor(\'ldaplist\',5);" /><input type="button" value="Show NotFound Cache" onclick="javascript:popFileEditor(\'ldapnotfound\',5);" />', ],
1222['vrfyDomains','VRFY Domains*',80,\&textinput,'file:files/vrfydomains.txt','(.*)','ConfigMakeRe','Put here the domain names that should be verified with SMTP-VRFY. Separate entries with |  or place them in a plain ASCII file one address per line: \'file:files/vrfydomains.txt\'.
1223Use the syntax: *mydomain.com=>smtp.mydomain.com|other.com=>mx.other.com:port to verify the recipient addresses with the SMTP-VRFY (if VRFY is not supported \'MAIL FROM:\' and \'RCPT TO:\' will be used) command on other SMTP servers. The entry behind => must be the hostname:port or ip-address:port of the MTA which is used to verify \'RCPT TO\' addresses with a VRFY command! If :port is not defined, port :25 will be used. You can use an entry like ALL=>vrfyhost:port to define a VRFY host for all entries without the MTA part.  You have to enable the SMTP \'VRFY\' command on your MTA - the \'EXPN\' command should be enabled! This requires an installed <a href="http://search.cpan.org/search?query=Net::SMTP" rel="external">Net::SMTP</a> module in PERL. <br />
1224 If you have configured LDAP and enabled DoLDAP and ASSP finds a VRFY entry for a domain, LDAP search will be done first and if this fails, the VRFY will be used. So VRFY could be used for LDAP backup/fallback/failover!<br />
1225 It is recommended to configure \'ldaplistdb\' in the \'File Paths and Database\' section when using this verify extension - so ASSP will store all verified recipients addresses there to minimize the querys on MTA\'s. There is no need to configure LDAP, but both VRFY and LDAP are using ldaplistdb. Please go to the \'LDAP setup\' section to configure MaxLDAPlistDays and LDAPcrossCheckInterval or start a crosscheck now with forceLDAPcrossCheck. This three parameters belong also to VRFY.','Basic',undef,'msg001330','msg001331'],
1226
1227['VRFYQueryTimeOut','SMTP VRFY-Query Timeout',5,\&textinput,'5','(\d\d?)',undef,
1228 'The number of seconds ASSP will wait for an answer of the MTA that is queryed with the VRFY command to verify a recipient address.'],
1229['VRFYforceRCPTTO','Force the usage of RCPT TO*',80,\&textinput,'','(.*)','ConfigMakeRe','Define local MTAs here for which you want ASSP to force the usage of \'MAIL FROM:\' and \'RCPT TO:\' instead of the VRFY command. The definition of the MTA(s) has to be exactly the same as already defined in vrfyDomains (after the \'=>\') for example: smtp.mydomain.com|mx.other.com:port|10.1.1.1|10.1.1.2:125 .'],
1230['DisableVRFY','Disable VRFY for External Clients',0,\&checkbox,'','(.*)',undef,
1231  'If you have enabled VRFY on your MTA to allow ASSP to verify addresses and you do not want external clients to use VRFY/EXPN - select this option.'],
1232
1233['MaxVRFYErrors','Maximum recipient verification Errors',5,\&textinput,'5','(\d+)',undef,
1234  'The maximum number of failed \'RCPT TO\' or \'VRFY\' commands encountered before the connection is dropped. ASSP will drop the connection, if the count of \'550 unknown user\' errors, received from your \'smtpDestination\'(MTA), reached this value!'],
1235['VRFYFail','VRFY failures return false',20,\&checkbox,'','(.*)',undef,'VRFY failures return false when an error occurs in VRFY lookups.'],
1236['VRFYLog','Enable VRFY logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1237  ''],
1238['DoMaxDupRcpt','Block Max Duplicate Recipients','0:disabled|1:block|2:monitor|3:score',\&listbox,3,'(.*)',undef,
1239  'Block remote servers that uses the same recipient address more times, than the number defined in MaxDupRcpt in the RCPT TO: command. Scoring is done with mdrValencePB . This check is skipped for outgoing, noprocessing, whitelisted and spamlovers mails. If a message has to be delayed, this check will score before the delay if set to block or score - and score and/or block on the next server request.'],
1240['MaxDupRcpt','Maximum Allowed Duplicate Recipient Addresses',5,\&textinput,'0','(\d+)',undef,
1241  'The maximum number of duplicate recipient addresses that are allowed in the sequence of the RCPT TO: commands!<br />
1242  The number per mail is calculated by \'number of RCPT TO: commands  -  number of unique recipient addresses\'.<br />
1243  For example: if one address is used three times or two addresses are used each two times, will result in the same count - 2. Or if both is the case in one mail, the count will be 4.'],
1244['mdrValencePB','Duplicate Recipient Score, default=10 +',10,\&textinput,10,'(.*)',undef, ''],
1245['ReplaceRecpt','Enable recipient replacement*',80,\&textinput,'','(.*)','configChangeRcptRepl','recommended if used: file:files/rcptreplrules.txt - default empty ! This enables recipient replacement. The replacement will be done before any ASSP check. For a more detailed description of the rules and options, read the file: <input type="button" value=" files/rcptreplrules.txt" onclick="return popFileEditor(\'files/rcptreplrules.txt\',8);" />  <a href=recprepl><img height=12 width=12 src="' . $wikiinfo . '" alt="Recipient Replacement Test" /> Recipient Replacement Test</a>',undef,undef,'msg001470','msg001471'],
1246['sendAllPostmaster','Catchall Address for Messages to Postmaster',40,\&textinput,'','(.*)',undef,'ASSP will deliver messages addressed to all postmasters of your local domains to this address. For example: postmaster@example.com'],
1247['sendAllPostmasterNP','Skip Spam Checks for Postmaster Catchall',0,\&checkbox,'','(.*)',undef,''],
1248['sendAllAbuse','Catchall Address for Messages to Abuse',40,\&textinput,'','(.*)',undef,'ASSP will deliver messages to all abuse addresses of your local domains to this address. For example: abuse@example.com'],
1249['sendAllAbuseNP','Skip Spam Checks for Abuse Catchall',0,\&checkbox,'','(.*)',undef,''],
1250['DoRFC822','Validate Recipient Address to Conform with RFC5322 ',0,\&checkbox,1,'(.*)',undef,'If activated, each local address is checked to conform with the email format defined in RFC5322 .<br />This requires an installed Email::Valid module in PERL.'],
1251['CatchAll','Catchall per Domain*',40,\&textinput,'','(.*)','configUpdateCA','ASSP will send to these addresses if no valid user is found in LocalAddresses_Flat or LDAP. <br />For example: catchall@domain1.com|catchall@domain2.com'],
1252['CatchallallISP2NULL','Move ISP Connection with wrong Recipient Address to NULL',0,\&checkbox,'','(.*)',undef,
1253  'If set, ASSP will move all ISP connections with wrong recipient addresses to a NULL-connection. The ISP will receive "250 OK" until the mail has passed, but the mail will not be sent to your MTA. This is done after CatchAll but before CatchAllAll is checked.'],
1254
1255['CatchAllAll','Catchall for All Domains',40,\&textinput,'','(.*)',undef,'ASSP will send to this address if no valid user is found  in LocalAddresses_Flat or LDAP and no match is found in Catchall per Domain. <br />For example: catchall@example.com'],
1256
1257['NullAddresses','NULL Connection Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe','ASSP will discard a message silently when encountering such an address in "MAIL FROM:" or "RCPT TO:". Accepts specific addresses (null@example.com), user parts (nobody) or entire domains (@example.com).',undef,undef,'msg001420','msg001421'],
1258
1259['InternalAddresses','Accept Mail from Local Domains only*',80,\&textinput,'','(.*)','ConfigMakeSLRe','These local addresses do not accept mail externally. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).'],
1260['InternalAndWhiteAddresses','Accept Mail from Local Domains and Whitelisted Senders only*',80,\&textinput,'','(.*)','ConfigMakeSLRe','These local addresses accept mail only from local domains and whitelisted external serders. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (fribo*@domain.com).',undef,undef,'msg009890','msg009891'],
1261['iaValencePB','Internal Only Address Score',10,\&textinput,25,'(.*)',undef, '',undef,undef,'msg002790','msg002791'],
1262['SepChar','Separation Character for Subaddressing',2,\&textinput,'','(.*)',undef,'RFC 3598 describes subaddressing with a Separation Character. A star (\'*\') is not allowed as Separation Character. Everything between Separation Character and @ is ignored (including Separation Character). For Example = \'+\' will allow user+subaddress@example.com.'],
1263['EnableBangPath','Support Bang Path',0,\&checkbox,'','(.*)',undef,
1264 'If set, ASSP will support addresses like domainx!user@domainy and will convert them to user@domainx .',undef,undef,'msg001450','msg001451'],
1265
1266['NoValidRecipient','No-Valid-Local-User Reply',80,\&textinput,'550 5.1.1 User unknown: EMAILADDRESS','([5|2]\d\d .*)',undef,'SMTP reply for invalid Users. Default: \'550 5.1.1 User unknown: EMAILADDRESS\' <br /> The literal EMAILADDRESS (case sensitive) is replaced by the fully qualified SMTP recipient (e.g., thisuser@example.com).<br /><hr /><div class="menuLevel1">Notes On Local Addresses</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/localaddresses.txt\',3);" />'],
1267
1268[0,0,0,'heading','Validate Helo'],
1269['useHeloBlacklist','Use the Helo Blacklist','0:disabled|2:monitor|3:score',\&listbox,3,'(.*)',undef,
1270  'Use the list of blacklisted/goodlisted-helo hosts built by rebuildspamdb. Scoring is done with hlbValencePB. <input type="button" value=" Show HeloList" onclick="javascript:popFileEditor(\'spamdb.helo\',4);" />'],
1271['hlbValencePB','Blacklisted HELO',10,\&textinput,20,'(.*)',undef, 'Valence',undef,undef,'msg002780','msg002781'],
1272['hlSpamLovers','SpamLover Blacklisted HELO Check*',80,\&textinput,'','(.*)','ConfigMakeSLRe',''],
1273['heloBlacklistIgnore','Don\'t block these HELO\'s*',80,\&textinput,'','(.*)','ConfigMakeRe',
1274  'HELO / EHLO greetings on this list will be excluded from the HELO checks. For example: host123.isp.com|host456.*.com'],
1275['ValidateHeloLog','Enable Validate Helo Logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1276  ''],
1277['useHeloGoodlist','Use the Helo Goodlist','0:disabled|1:bonus|2:whitelisted',\&listbox,1,'(.*)',undef,
1278  'Use the list of known good helo hosts built by rebuildspamdb.<br />
1279  bonus - the message/IP get a bonus of the weigthed negative value of hlbValencePB <br />
1280  whitelisted - the message is processed as whitelisted<br /><br />
1281  The good helos and weights are stored together with the helo blacklist.',undef,undef,'msg009920','msg009921'],
1282['hlgValencePB','Known Good HELO',10,\&textinput,-20,'(.*)',undef, ' Valence',undef,undef,'msg002780','msg002781'],
1283
1284['DoFakedLocalHelo','Block Forged Helos','0:disabled|1:block|2:monitor|3:score',\&listbox,1,'(.*)',undef,
1285  'Block remote servers that claim to come from our Local Domain/Local IPs/Local Host. Scoring with fhValencePB.'],
1286['fhValencePB','Forged HELO Score',10,\&textinput,150,'(.*)',undef, 'For Valence',undef,undef,'msg002740','msg002741'],
1287['DoFakedWL','Do Not Block Whitelisted',0,\&checkbox,'','(.*)',undef,
1288  'Disable DoFakedLocalHelo and DoIPinHelo for whitelisted addresses (not recommended).',undef,undef,'msg001540','msg001541'],
1289['DoFakedNP','Do Not Block Noprocessing',0,\&checkbox,'','(.*)',undef,
1290  'Disable DoFakedLocalHelo and DoIPinHelo for addresses identified as noprocessing (not recommended).',undef,undef,'msg001550','msg001551'],
1291
1292['myServerRe','Local Domains,IPs and Hostnames*',80,\&textinput,'','(.*)','ConfigMakeRe',
1293  'Local Domains, IP addresses and Hostnames are often use to fake (forge) the Helo. Include all IP addresses and hostnames for your server  here, localhost is already included. Include Local Domains of your choice here, if you deactivated the automatic use of the localDomains list.  For example: 11.22.33.44|mx.example.com|example.org','Basic'],
1294 ['noHelo','Don\'t Validate HELO for these IPs*',60,\&textinput,'','(.*)','ConfigMakeIPRe',
1295  'Enter IP addresses that you don\'t want to be HELO validated.<br />
1296   For example: 145.145.145.145|146.145',undef,'7'],
1297
1298
1299
1300['DoInvalidFormatHelo','Validate Format of HELO','0:disabled|1:block|2:monitor|3:score',\&listbox,1,'(.*)',undef,
1301  'If activated, the HELO is checked against the expression below. If the Regular Expression matches, the HELO is not ok. Scoring is done  with ihValencePB.'],
1302['ihValencePB','HELO Score',10,\&textinput,10,'(.*)','ConfigChangeValencePB', '',undef,undef,'msg002680','msg002681'],
1303['invalidHeloRe','Regular Expression to invalidate Format of HELO**',80,\&textinput,'^[^\.]+\.?$|\.user=>0.5|^\d+\.\d+\.\d+\.\d+$|^[^\.]+\.?$','(.*)','ConfigCompileRe',''],
1304['validHeloRe','Regular Expression to validate Format of HELO*',80,\&textinput,'^(([a-z\d][a-z\d-]*)?[a-z\d]\.)+[a-z]{2,6}$','(.*)','ConfigCompileRe',
1305  'Validate HELO will check incoming HELOs according to rfc1123. <br />For example: ^(([a-z\d][a-z\d-]*)?[a-z\d]\.)+[a-z]{2,6}$ .<br /><hr />
1306  <div class="menuLevel1">Notes On Validate Helo</div>
1307  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/validatehelo.txt\',3);" /> '],
1308
1309
1310
1311[0,0,0,'heading','Validate Sender'],
1312['ValidateSenderLog','Enable Validate Sender Logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1313  ''],
1314
1315
1316['DoBlackDomain','Do Blacklisted Addresses and Domains','0:disabled|1:block|2:monitor|3:score|4:testmode', \&listbox,1,'(.*)',undef, ' DoBlackDomain uses blackListedDomains and weightedAddresses. Scoring is done  with blValencePB.'],
1317['blValencePB','Blacklisted Domain Score',10,\&textinput,70,'(.*)',undef, '',undef,undef,'msg002660','msg002661'],
1318['DoBlackDomainWL','Blacklisting Addresses/Domains will overwrite WhiteListing',0,\&checkbox,'1','(.*)',undef,
1319  'Do blacklisting addresses & domains in messages which are marked whitelisted by whiteRe, whiteListedDomains, whiteListedIPs or whitelistdb.',undef,undef,'msg001670','msg001671'],
1320['DoBlackDomainNP','Blacklisting Addresses/Domains will overwrite NoProcessing',0,\&checkbox,'','(.*)',undef,
1321  'Do blacklisting addresses & domains in messages marked \'noprocessing\' by npRe, npSize, noProcessingDomains, noProcessingIPs or noProcessing.'],
1322
1323['blackListedDomains','Blacklisted Domains*',60,\&textinput,'file:files/blackdomains.txt','(.*)','ConfigMakeRe','Addresses  and Domains from which you always want to reject mail, they only send you spam. Note this matches the end of the address, so if you don\'t want to match subdomains then include the @. Note that example.com would also match spamexample.com but .example.com won\'t match example.com. abc@example.com will match abc@example.com but won\'t match bbc@example.com. Wildcards are supported. <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/blackdomains.txt target=files >newest file is here</a>','','9'],
1324['NotGreedyBlackDomain','Only the envelope-sender is added/compared to the BlackDomainlist',0,\&checkbox,'','(.*)',undef,'If not enabled all addresses in the FROM, SENDER, REPLY-TO, ERRORS-TO, or LIST-* header fields are checked.'],
1325['blSpamLovers','Blacklisted Domains Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000540','msg000541'],
1326['noBlackDomain','Don\'t do Blacklisted for these Addresses and Domains* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1327 ' Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).'],
1328
1329['weightedAddresses','Fuzzy Addresses** ',80,\&textinput,'file:files/blackAddresses.txt','(.*)','ConfigMakeSLRe',' Accepts (blackish and <span class="positive">whitish addresses<span class="positive"> (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported. <span class="positive"> A positive weight will make the address \'blackish\'. A negative weight will make the address into \'whitish\'.</span> For example: fribo*@example.com|<span class="positive">@*.gov=>-0.5</span>|@*.biz=>0.5 .'],
1330
1331[0,0,0,'heading','Spoofing'],
1332['DoNoValidLocalSender','Check External Sender for Local Address  ','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
1333  'If activated, each external sender from a domain listed in localDomains is checked against LocalAddresses_Flat, LDAP or is verified using VRFY. An external sender is a sender from an IP not in acceptAllMail, not authenticated and not coming through the relayPort. Scoring is done  with flValencePB.'],
1334['flValencePB','Invalid Local Sender Score',10,\&textinput,20,'(.*)',undef, '',undef,undef,'msg002770','msg002771'],
1335
1336['DoNoSpoofing','Block Local Addresses from External Sender Alltogether','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
1337  'If activated, each external sender address with a domain listed in localDomains is regarded a spoofed address. An external sender is a sender from an IP not in acceptAllMail, not authenticated and not coming through the relayPort. flValencePB is used for scoring'],
1338
1339['onlySpoofingCheckIP','Do Spoofing Check ONLY for these IP\'s*',80,\&textinput,'','(\S*)','ConfigMakeIPRe',
1340 'Enter IP\'s that you want to be checked for spoofing. If this is set, ONLY these IP\'s will be checked. For example:145.145.145.145|145.146.',undef,'7','msg009900','msg009901'],
1341['onlySpoofingCheckDomain','Do Spoofing Check ONLY for these Addresses/Domains*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1342 'Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com). If set, ONLY these addresses/domains will be checked for spoofing.',undef,undef,'msg009910','msg009911'],
1343['noSpoofingCheckIP','Don\'t do Spoofing Check for these IPs* ',80,\&textinput,'','(.*)','ConfigMakeIPRe',
1344 'Enter IP addresses and Hostnames that you don\'t want to be checked for spoofing. For example:145.145.145.145|145.146.','','7'],
1345['noSpoofingCheckDomain','Don\'t do Spoofing Check for these Addresses/Domains* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1346 ' Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).'],
1347
1348['DoRFC522Sender','Validate Sender Address to conform with RFC5322',0,\&checkbox,'1','(.*)',undef,'Sender must be a valid address to conform with RFC5322.'],
1349['DoReversed','Reversed Lookup','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,0,'(.*)',undef,
1350  'If activated, each sender IP is checked for a PTR record. Scoring is done  with ptmValencePB.'],
1351['DoReversedWL','Do Reversed Lookup for Whitelisted',0,\&checkbox,'1','(.*)',undef,
1352  'Do reversed lookup for whitelisted addresses.',undef,undef,'msg001810','msg001811'],
1353['DoReversedNP','Do Reversed Lookup for Noprocessing',0,\&checkbox,'1','(.*)',undef,
1354  'Do reversed lookup for noprocessing addresses.',undef,undef,'msg001820','msg001821'],
1355['ptmValencePB','Missing PTR Score',10,\&textinput,25,'(.*)','ConfigChangeValencePB', '',undef,undef,'msg003000','msg003001'],
1356['ptrSpamLovers','Invalid/Missing PTR Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000660','msg000661'],
1357['ptiValencePB','Invalid PTR Score, ',10,\&textinput,25,'(.*)','ConfigChangeValencePB', '',undef,undef,'msg002680','msg002681'],
1358['DoInvalidPTR','Reversed Lookup FQDN Validation',0,\&checkbox,'','(.*)',undef,
1359  'If activated - and Reversed Lookup is activated -, the PTR-FQDN record is checked against invalidPTRRe & validPTRRe. Scoring is done  with ptiValencePB '],
1360['invalidPTRRe','Regular Expression to Invalidate Format of PTR**',80,\&textinput,'file:files/invalidptr.txt','(.*)','ConfigCompileRe',
1361  'Validate Format PTR will check PTR records for this. <br />
1362  <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/invalidptr.txt target=files ><span class="negative">newest example file invalidptr.txt is here</a>'],
1363['validPTRRe','Regular Expression to Validate Format of PTR*',80,\&textinput,'file:files/validptr.txt','(.*)','ConfigCompileRe',
1364  'Validate Format PTR will check PTR records for this. If found, the PTR will be considered valid<br />
1365  <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/validptr.txt target=files ><span class="negative">newest example file validptr.txt is here</a>'],
1366
1367['PTRCacheInterval','Reversed Lookup Cache Refresh Interval',4,\&textinput,7,'([\d\.]+)','configUpdatePTRCR',
1368  'IPs in cache will be removed after this interval in days. 0 will disable the cache.  <input type="button" value=" show cache" onclick="javascript:popFileEditor(\'pb/pbdb.ptr.db\',5);" />'],
1369['DoDomainCheck','Validate MX or A Record','0:disabled|1:block|2:monitor|3:score',\&listbox,3,'(.*)',undef,
1370  'If activated, the sender address and each address found in the following header lines (ReturnReceipt:, Return-Receipt-To:, Disposition-Notification-To:, Return-Path:, Reply-To:, Sender:, Errors-To:, List-...:) is checked for a valid MX and/or A record. ',undef,undef,'msg001870','msg001871'],
1371['mxaSpamLovers','Missing MX Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000650','msg000651'],
1372
1373['mxValencePB','Missing MX Score',10,\&textinput,25,'(.*)',undef, '',undef,undef,'msg002920','msg002921'],
1374['mxaValencePB','Missing MX and A Record Score',10,\&textinput,35,'(.*)',undef, '',undef,undef,'msg002930','msg002931'],
1375['MXACacheInterval','Validate Domain MX Cache Refresh Interval',4,\&textinput,7,'(\d+\.?\d*|)','configUpdateMXACR',
1376  'IP\'s in cache will be removed after this interval in days. 0 will disable the cache.<input type="button" value=" Show MX Cache" onclick="javascript:popFileEditor(\'pb/pbdb.mxa.db\',5);" />',undef,undef,'msg001880','msg001881'],
1377
1378['removeDispositionNotification','Remove Disposition Notification Headers',0,\&checkbox,'1','(.*)',undef,
1379  'If set, all headers "ReturnReceipt: , Return-Receipt-To: and Disposition-Notification-To:" will be removed (except whitelisted and noprocessing mails). Select this to prevent unwanted whitelisting of spammers . <br /><hr />
1380  <div class="menuLevel1">Notes On Validate Sender</div>
1381  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/validatesender.txt\',3);" />'],
1382
1383[0,0,0,'heading','IP Blocking'],
1384['noBlockingIPs','Do not block Connections from these IP\'s*',40,\&textinput,'','(\S*)','ConfigMakeIPRe','Manually maintained list of IP\'s which should not be blocked.  For example: 145.145.145.145|145.146.<br />
1385  To define IP\'s only for specific email addresses or domains (recipients) you must use the file:... option<br />
1386  An entry (line) may look as follows:<br />
1387  145.146.0.0/16=>*@local.domain|user@mydomain|user2@*.mydomain # comment<br /><br />
1388  It is possible to define a predefined group on any or both sides of the \'=>\' separator, like:<br />
1389  [ipgroup]=>[usergroup]|user@mydomain<br /><br />
1390  NOTICE: the following combination of two entries, will lead in to a user/domain based matching - the global entry will be ignored!<br />
1391  145.146.0.0/16 # comment<br />
1392  145.146.0.0/16=>*@local.domain|user@mydomain|user2@*.mydomain # comment<br />
1393  If multiple user/domain based entries are defined for the same IP, only the last one will be used!',undef,'7','msg002010','msg002011'],
1394['DoDropList','Drop Connections from these IPs','0:disabled|1:block|2:monitor|3:score',\&listbox,1,'(.*)',undef,
1395 'If activated, the IP is checked against the droplist . The droplist is downloaded if a new one is available and contains the Spamhaus DROP List. See "http://www.spamhaus.org/drop/drop.lasso".'],
1396['dropValencePB','Match in Droplist Score',3,\&textinput,40,'(.*)',undef,""],
1397['DoDenySMTP','Do Deny Connections from these IPs','0:disabled|1:block|2:monitor|3:score',\&listbox,1,'(.*)',undef,
1398 'If activated, the IP is checked against denySMTPConnectionsFrom.'],
1399['denySMTPConnectionsFrom','Deny Connections from these IPs*',40,\&textinput,'','(.*)','ConfigMakeIPRe','Manually maintained list of IP addresses and Hostnames which should be blocked. IP addresses and Hostnames in noPB, acceptAllMail, ispip, whiteListedIPs will pass. For example: server.example.com|145.145.145.145|145.146.','','7'],
1400['DoDenySMTPstrict','Do Deny Connections from these IP addresses and Hostnames Strictly','0:disabled|1:block|2:monitor',\&listbox,1,'(.*)',undef,
1401 'If activated, the IP is checked against denySMTPConnectionsFromAlways.  '],
1402['denySMTPConnectionsFromAlways','Deny Connections from these IP\'s Strictly*',40,\&textinput,'file:files/denyalways.txt','(\S*)','ConfigMakeIPRe',
1403 'Manually maintained list of IP\'s which should <b>strictly</b> be blocked after address verification and before body and header is downloaded. Contrary to <i>denySMTPConnectionsFrom</i> IP\'s in noDelay, acceptAllMail, ispip, whiteListedIPs, noProcessingIPs, whitebox will <b>not</b> pass if listed here.',undef,'7','msg002030','msg002031'],
1404
1405['denySMTPLog','Enables Logging for \'Deny SMTP Connections From\'','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,''],
1406['denySMTPstrictEarly','Do Strictly Deny Connections Early',0,\&checkbox,'','(.*)',undef,
1407  'IP\'s in <b>denySMTPConnectionsFromAlways</b> will be denied right away.',undef,undef,'msg002050','msg002051'],
1408['DenyError','Deny Error',80,\&textinput,'554 5.7.2 Service denied, closing transmission channel','([25]\d\d .*)',undef,'SMTP error message to reject connections.For example: 554 5.7.2 Service denied, closing transmission channel. '],
1409
1410
1411
1412['DoFrequencyIP','Check Frequency - Maximum Connections Per IP','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,1,'(.*)',undef,
1413 'Scoring is done  with ifValencePB.'],
1414['ifValencePB','IP Frequency Score',10,\&textinput,150,'(.*)',undef, '',undef,undef,'msg002810','msg002811'],
1415['maxSMTPipConnects','Maximum Frequency of Connections Per IP ',3,\&textinput,'10','(\d?\d?\d?)',undef,
1416 'The maximum number of SMTP connections an IP Address can make during the maxSMTPipDuration (IP Address Frequency Duration). If a server makes more than this many connections to ASSP within the maxSMTPipDuration (IP Address Frequency Duration) it will be banned from future connections until the maxSMTPipExpiration (IP Address Frequency Expiration) is reached. This can be used to prevent server overloading and DoS attacks. 10 connections are typically enough. If left blank or 0, there is no limit imposed by ASSP. IP addresses in noPB, noDelay, acceptAllMail, ispip, whiteListedIPs, noProcessingIPs, PB-whitebox are excluded from SMTP session limiting, whitelisted and noprocessing addresses are honored. '],
1417['maxSMTPipDuration','Maximum Frequency of Connections Per IP Duration',5,\&textinput,'90','(\d?\d?\d?\d?)',undef,
1418 'The window (in seconds) during which the maxSMTPipConnects (IP Frequency) (see above for more details) will be scrutinized for each IP.'],
1419['maxSMTPipExpiration','Expiration of Maximum Frequency',5,\&textinput,'3600','(\d?\d?\d?\d?)',undef,
1420 'The number of seconds that must pass before an IP address blocked by the maxSMTPipConnects (IP Address Frequency) setting is allowed to connect again.'],
1421['DoDomainIP','Check Number of IP\'s Per Domain','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,0,'(\d*)',undef,
1422 'This check is skipped if the IP and domain have passed the SPF-check. If ValidateSPF is enabled and an IP/Domain reaches the maxSMTPdomainIP limit, the MaintThread starts a background SPF check to prevent blocking good mails in future.',undef,undef,'msg002100','msg002101'],
1423['maxSMTPdomainIP','Limit Number of IP\'s  Per Domain',3,\&textinput,'10','(\d?\d?\d?)',undef,
1424 'The number of IP(subnet) switches a domain may have during the (maxSMTPdomainIPExpiration) Limit Different IP\'s Per Domain Expiration. If a domain switches more often than this it will be banned from future connections until the Expiration is reached. This can be used to prevent server overloading and DoS attacks. 10 connections are typically enough. If left blank or 0, there is no limit imposed by ASSP. IP\'s in noPB, noDelay, acceptAllMail, ispip, whiteListedIPs, noProcessingIPs, PB-whitebox are excluded, whitelisted and noprocessing addresses are honored.',undef,undef,'msg002110','msg002111'],
1425['maxSMTPdomainIPExpiration','Expiration of Limit Number',5,\&textinput,'7200','(\d?\d?\d?\d?\d?)',undef,
1426  'The number of seconds that must pass before a domain blocked by the (maxSMTPdomainIP) Limit Subnet IP\'s Per Domain setting (see above for more details) is allowed to connect again. The default is 7200 (seconds).',undef,undef,'msg002120','msg002121'],
1427['maxSMTPdomainIPWL','Do Not Limit Different IP\'s For These Domains*',60,\&textinput,'gmx.de|t-online.de|yahoo.com|hotmail.com|gmail.com','(.*)','ConfigMakeRe',
1428  'This prevents specific domains from limiting. For example: yahoo.com|hotmail.*.com|gmail.com<br /><hr />
1429  <div class="menuLevel1">Notes On IP Blocking</div>
1430  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/ipblocking.txt\',3);" />',undef,undef,'msg002130','msg002131'],
1431
1432[0,0,0,'heading','SenderBase '],
1433['SenderBaseLog','Enable SenderBase Logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1434  ''],
1435['AddSenderBaseHeader','Add SenderBase Header',0,\&checkbox,1,'(.*)',undef,'',undef],
1436['DoOrgWhiting','Do Organization Scoring <a href="http://www.senderbase.org/" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="SenderBase" /></a>','0:disabled|1:whiting|2:monitor|3:score',\&listbox,1,'(.*)',undef,
1437   'If activated, each sending IP address has its assigned organization / domain
1438looked up and scored with sworgValencePB.'],
1439
1440
1441['whiteSenderBase','White Organizations and Domains in SenderBase**  ',80,\&textinput,'file:files/whitesenderbase.txt','(.*)','ConfigCompileRe','If the organization or domain  in the <a href="http://www.senderbase.org/" rel="external">SenderBase</a> IP description matches this Perl regular expression the  messagescore will be decreased by sworgValencePB. Example file:<a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/whitesenderbase.txt target=files ><span class="positive">whitesenderbase.txt</a>'],
1442['sbnValencePB','No Organization and No CountryCode Score
1443',3,\&textinput,10,'(\d*)',undef, ''],
1444['sworgValencePB','<span class="positive">White Organizations Score',3,\&textinput,-50,'(.*)',undef, '</span>'],
1445['DoOrgBlocking','Do Organization Blocking','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,2,'(.*)',undef,
1446   'If activated, each sending IP address has its assigned organization
1447looked up und compared to blackSenderBase. Scoring is done with sborgValencePB'],
1448['sborgValencePB','Blacklisted Organizations Score',10,\&textinput,25,'(.*)',undef, '',undef,undef,'msg003130','msg003131'],
1449
1450['blackSenderBase','Blacklisted Organizations in SenderBase** ',80,\&textinput,'file:files/blacksenderbase.txt','(.*)','ConfigCompileRe','If the organization or domain is in the available <a href="http://www.senderbase.org/" rel="external">SenderBase</a> IP description and matches this regular expression the message will be handled according to DoOrgBlocking. The total messagescore will be increased by sborgValencePB. Example file:<a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/blacksenderbase.txt target=files ><span class="negative">blacksenderbase.txt</a><br />
1451  <input type="button" value="blackSenderBase Cache" onclick="javascript:popFileEditor(\'pb/pbdb.orgnames.db\',5);" />'],
1452
1453['DoCountryBlocking','Do Country Blocking','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
1454   'If activated, each sending IP address has it\'s assigned country
1455looked up and compared to CountryCodeBlockedRe.  Scoring is done using bccValencePB.'],
1456['bccValencePB','Blocked Country Code Score',10,\&textinput,25,'(.*)',undef, ''],
1457['CountryCodeBlockedRe','Blocked Countries**',80,\&textinput,'VN|TW|IE|UZ|IN|IR|IQ|DO|KR|RU|JP|TR|TH|PL|LT|CL|RO','(.*)','ConfigCompileRe',
1458  'Messages from IP addresses based in these countries will be blocked if DoCountryBlocking is set to block. For example: VN|TW|IE|UZ|IN|IR|IQ|DO|KR|RU|JP|TR|TH|PL|LT|CL|RO. "all" will block all foreign countrycodes which are not in CountryCodeRe or \'Ignore Country Codes\'. See: <a href="http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm" rel="external" target=wiki>English country names and code elements</a>. '],
1459['DoCountryBlockingWL','Do Country Blocking for Whitelisted ',0,\&checkbox,'','(.*)',undef,
1460  'Enable Country Blocking for whitelisted messages.'],
1461['DoCountryBlockingNP','Do Country Blocking for NoProcessing',0,\&checkbox,'','(.*)',undef,
1462  'Enable Country Blocking for noprocessing messages.'],
1463['DoSuspiciousCountry','Do Suspicious Country Scoring','0:disabled|2:monitor|3:score',\&listbox,3,'(.*)',undef,
1464   'If activated, each sending IP address has it\'s assigned country
1465looked up and compared to CountryCodeRe.'],
1466['CountryCodeRe','Suspicious Countries**',80,\&textinput,'CN|NG|UA|GR|HU|SA|IN|IE|PT|MD|PE|CZ|TW|BR|CL|ID|PH|CN|KR|RU|JP|TR|TH|PL|LT|CL|RO','(.*)','ConfigCompileRe',
1467  'Messages from IP addresses based in these countries will increase the MessageScore. For example: CN|NG|UA|GR|HU|SA=>0.5. A positive or negative weight is possible, Scoring is done using sbsccValencePB. See: <a href="http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm" rel="external" target=wiki>English country names and code elements</a>'],
1468['sbsccValencePB','Suspicious Country Code Score',3,\&textinput,10,'(.*)',undef, '',undef,undef,'msg003090','msg003091'],
1469
1470
1471
1472['NoCountryCodeRe','Ignore Country Codes from these Countries*',80,\&textinput,'US|CA|DE','(.*)','ConfigCompileRe',
1473  'Messages from IP addresses based in these countries will will be ignored in CountryCode checks.'],
1474
1475['MyCountryCodeRe','Home Countries**',80,\&textinput,'','(.*)','ConfigCompileRe',
1476  'Put here your own country code(s) (for example: US). Messages from IP addresses based in these countries will decrease the total MessageScore using sbhccValencePB, messages from other countries will increase the total MessageScore using sbfccValencePB if ScoreForeignCountries is set. '],
1477['sbhccValencePB','<span class="positive">Home Country Code Score, </span> ',10,\&textinput,0,'(.*)',undef, '<span class="positive"></span>',undef,undef,'msg003120','msg003121'],
1478
1479['ScoreForeignCountries','Score Foreign Countries',0,\&checkbox,'1','(.*)',undef,
1480  'Messages from countries not in MyCountryCodeRe, NoCountryCodeRe and CountryCodeRe will increase the total messageScore using sbfccValencePB.'],
1481['sbfccValencePB','Foreign Country Code Score',10,\&textinput,10,'(.*)',undef, '',undef,undef,'msg003110','msg003111'],
1482
1483['SBCacheExp','Country Cache Refresh Interval',4,\&textinput,14,'([\d\.]+)','configUpdateSBCR',
1484  'IPs in cache will be removed after this interval in days. 0 will disable the cache.  <input type="button" value=" show cache" onclick="javascript:popFileEditor(\'pb/pbdb.sb.db\',5);" /><br /><hr />
1485  <div class="menuLevel1">Country Codes</div>
1486  See: <a href="http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm" rel="external" target=wiki>English country names and code elements</a>.
1487  '],
1488['sbSpamLovers','SpamLover Country Check*',80,\&textinput,'','(.*)','ConfigMakeSLRe',''],
1489[0,0,0,'heading','Scoring '],
1490['DoPenaltyMessage','MessageScoring','0:disabled|1:block|2:monitor|4:testmode',\&listbox,1,'(.*)',undef,'If this feature is selected, the total score for all checks during a message is used to determine if the email should be considered Spam. If the combined score is greater than MessageScoringLowerLimit (MessageLimit for WarningTag) and less than or equal MessageScoringUpperLimit (MessageLimit for Blocking) the message will not be blocked but get the MessageScoringWarningTag. If the combined score is greater than the MessageScoringUpperLimit and blocking is selected the message will be blocked. If testmode is selected the message will not be blocked but tagged with spamSubject.','Basic'],
1491['AddScoringHeader','Add Message Scoring Header',0,\&checkbox,1,'(.*)',undef,'Adds a line to the email header "X-Assp-Score: "'],
1492['MessageScoringWL','MessageScoring on Whitelisted Senders',0,\&checkbox,'','(.*)',undef,''],
1493['MessageLog','Enable Scoring logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1494  ''],
1495['msSpamLovers','MessageScoring Spam-Lover *',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000670','msg000671'],
1496['spamFriends','Spam Friends **',80,\&textinput,'','(.*)','ConfigMakeSLRe',
1497 'A list of local user addresses that when matched will reduce the messagescore with friendsValencePB. This will make the scoring filter more softly. if you use negative weights here, the messagescore will be increased and the scoring filter will be more sharply. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (fribo*@domain.com). A second parameter separated by "=>" specifies the weight (multiplier) of friendsValencePB (default = -10). For example: @example.com=>0.5 will add -5 to the score of mails from @example.com. '],
1498['friendsValencePB','<span class="positive">Spam Friends Score</span>',3,\&textinput,-10,'(-?.*)',undef,'<span class="positive">Used to calculate the score for recipients in spamFriends.</span>'],
1499['MsgScoreOnEnd','Message Scoring on End',0,\&checkbox,'','(.*)',undef,'ASSP will wait until all configured possible checks are finished. Use this, to force calculating a complete message score over all values, including all bonus values.',undef,undef,'msg002290','msg002291'],
1500['MessageScoringLowerLimit','MessageScoring Lower Limit ',3,\&textinput,0,'(.*)',undef,'MessageScoring will tag messages with totalscore between  MessageScoringLowerLimit and MessageScoringUpperLimit. Empty or zero will disable the feature '],
1501['MessageScoringWarningTag','Warning Tag',20,\&textinput,'[Over Lower Limit]','(.*)',undef,'Used instead of spamSubject if totalscore is between  MessageScoringLowerLimit and MessageScoringUpperLimit.'],
1502['MessageScoringUpperLimit','MessageScoring Limit',3,\&textinput,50,'(.*)',undef,'If MessageScoring is done  to block, it will block messages whose totalscore is  equal or higher than this threshold.<hr /><div class="menuLevel1">Notes On Message Scoring</div>
1503  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/messagescoring.txt\',3);" />'],
1504
1505['DoPenalty','IP Scoring','0:disabled|2:enabled',\&listbox,2,'(.*)',undef,'The PenaltyBox scores IP addresses based on some events and stores them into a BlackBox. The totalscore is used by DoPenaltyMessage for assigning a reputation. The total is also used by DelayIP.
1506The WhiteBox stores IP addresses  which should not be put into the BlackBox. The WhiteBox is always enabled. If an address is in the whitelist or whitedomain, the IP goes into the WhiteBox too. The WhiteBox is one of the sources  Delaying/Greylisting uses to determine when delaying should not be done. <br />Entries in noPB (Don\'t do penalties for these IP addresses ) or ispip (ISP/Secondary MX Servers) will prevent from penalties. Select \'enabled\' to fill WhiteBox and BlackBox. This  enables DoPenaltyMessage and DelayIP.'],
1507['PenaltyLog','Enable PenaltyBox logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1508  ''],
1509
1510
1511['PenaltyLimit','Penalty Limit',4,\&textinput,50,'(.*)',undef,
1512  'PB will block messages from IP addresses whose totalscore exceeds this threshold during PBlackExpiration. <br />For example: 50'],
1513
1514['BlackExpiration','Expiration Time for BlackBox Entries',4,\&textinput,180,'(.*)','updateBlackExpiration',
1515  'Penalties with a score lower than PenaltyLimit  will expire after this number of minutes. <input type="button" value=" Show BlackBox" onclick="javascript:popFileEditor(\'pb/pbdb.black.db\',4);" />'],
1516
1517
1518['noPB','Don\'t add these IP addresses and Hostnames to BlackBox* ',80,\&textinput,'','(.*)','ConfigMakeIPRe',
1519 'Enter IP addresses that you don\'t want to be in BlackBox. For example:145.145.145.145|145.146.','','7'],
1520['noPBwhite','Don\'t add these IP addresses to WhiteBox*',80,\&textinput,'file:files/nopbwhite.txt','(.*)','ConfigMakeIPRe',
1521 'Enter IP addresses and Hostnames that you don\'t want to be in WhiteBox. ','','7'],
1522
1523['WhiteExpiration','Expiration Time for WhiteBox Entries',4,\&textinput,30,'(\d?\d?\d?\d?)',undef,,
1524  'The WhiteBox is always activated. IP addresses in WhiteBox will allow content-related checks like Bayesian, URIBL, Bomb but skip IP-related checks like RBL. WhiteBox entries will expire after this specified number of days. For example: 30<input type="button" value="Show White Box" onclick="javascript:popFileEditor(\'pb/pbdb.white.db\',4);" />'],
1525
1526
1527['PenaltyUseNetblocks','Use IP Netblocks',0,\&checkbox,'1','(.*)',undef,
1528  'Perform the IP address checks of the sending host based on the /24 subnet rather than on the specific IP.'],
1529
1530
1531
1532
1533[0,0,0,'heading','Delaying/Greylisting  <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Delaying/Greylisting" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Delaying" /></a>'],
1534['DelayIP','IP Delaying',10,\&textinput,'50','(\d*)',undef,
1535  'Enable delaying for IPs in black penaltybox with totalscore above this value. A value of zero disables this feature.',undef,undef,'msg009320','msg009321'],
1536['DelayIPTime','IP Delaying Embargo Time',5,\&textinput,5,'(\d*)',undef,
1537  'Enter the number of minutes during delivery is refused with a temporary failure (451 SMTP error code). Default is 5 minutes.',undef,undef,'msg009330','msg009331'],
1538
1539
1540
1541['EnableDelaying','Enable Greylisting',0,\&checkbox,1,'(.*)',undef,
1542  'Enable Greylisting as described at <a href="http://projects.puremagic.com/greylisting/whitepaper.html?view=markup" rel="external">Greylisting-whitepaper</a>.<br />
1543   ASSP will "temporarily reject" any email from a sender it does not recognize. If the mail is legitimate the originating server will, after a delay, try again and, if sufficient time has elapsed, the email will be accepted. If the mail is from a spam sender, sending to many thousands of email addresses, it will probably not be retried.
1544   Greylisting involves sending a temporary 451 SMTP error code to the sending server when a message is received, along with sending this error code ASSP creates a Triplet and stores this. On the second delivery attempt if the Embargo Time set by the ASSP admin for the Triplet has been surpassed the message will be accepted and a Tuplet will be created and not delayed again for an Expiry Time set by the ASSP admin.'],
1545
1546['DelayLog','Enable Greylisting/Delaying logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1547  ''],
1548['delaySpamLovers','SpamLover Greylisting/Delaying*',80,\&textinput,'','(.*)','ConfigMakeSLRe','These Recipients will not be delayed/greylisted.'],
1549['DelayWL','Whitelisted Greylisting',0,\&checkbox,'','(.*)',undef,
1550  'Enable Greylisting for whitelisted mails. This also enables Geylisting for SPF-Cache-OK listed IP\'s and mails from white organizations, which are normaly not greylisted.',undef,undef,'msg003290','msg003291'],
1551['DelayNP','NoProcessing Greylisting',0,\&checkbox,'','(.*)',undef,
1552  'Enable Greylisting for noprocessing mails.',undef,undef,'msg003300','msg003301'],
1553['DelaySL','Spam-Lovers Greylisting',0,\&checkbox,'','(.*)',undef,
1554  'Enable Greylisting for Spam-Lovers.',undef,undef,'msg003310','msg003311'],
1555
1556['DelayAddHeader','Add X-Assp-Delay Header',0,\&checkbox,1,'(.*)',undef,
1557  'Add X-Assp-Delay header to all emails.'],
1558['DelayEmbargoTime','Embargo Time',5,\&textinput,5,'(.*)',undef,
1559  'Enter the number of minutes for which delivery, related with new \'triplet\' (IP address of the sending host + mail from + rcpt to), is refused with a temporary failure.'],
1560['DelayWaitTime','Wait Time',5,\&textinput,28,'(.*)',undef,
1561  'Enter the number of hours to wait for delivery attempts related with recognised \'triplet\'; delivery is accepted <br />
1562  immediately and the \'tuplet\' (IP address of the sending host + sender\'s domain) is whitelisted.'],
1563['DelayExpiryTime','Expiry Time',5,\&textinput,36,'(\d+)',undef,
1564  'Enter the number of days for which a whitelisted \'tuplet\' is considered valid.'],
1565['DelayUseNetblocks','Use IP Netblocks',0,\&checkbox,1,'(.*)',undef,
1566  'Perform the IP address checks of the sending host based on the /24 subnet it is at rather than the specific IP. <br />
1567  This feature may be useful for legitimate mail systems that shuffle messages among SMTP clients between retransmissions.'],
1568['DelayNormalizeVERPs','Normalize VERP Addresses',0,\&checkbox,1,'(.*)',undef,
1569  'Some mailing lists (such as Ezmlm) try to track bounces to individual mails, rather than just individual recipients, which creates a variation on the VERP method where each email has its own unique envelope sender. Since the automatic whitelisting  that is built into Greylisting depends on the envelope addresses for subsequent emails being the same, the greylisting filter will attempt to normalize the unique sender addresses, when this option is checked.'],
1570['DelayWithMyName','Add myName to Triplets',0,\&checkbox,0,'(.*)',undef,
1571  'If set, myName is added to every delay triplet (not to tuplets). This is useful and recommended, if you are using more than one ASSP host with shared databases for delaydb. This option makes the triplets unique to every ASSP host, because it is allowed for SMTP-hosts, to request a backup MX immediately after the primary MX, without waiting 5 minutes (DelayEmbargoTime) between the two requests.',undef,undef,'msg003380','msg003381'],
1572['DelayMD5','Use MD5 for DelayDB',0,\&checkbox,'1','(.*)',undef,
1573  'Message-Digest algorithm 5 is a cryptographic hash function and adds some level of security to the delay database. Must be set to off if you want to list the database with DelayShowDB/DelayShowDBwhite. This requires an installed <a href="http://search.cpan.org/search?query=Digest::MD5" rel="external">Digest::MD5</a> module in PERL.',undef,undef,'msg003390','msg003391'],
1574['DelayShowDB','Show Delay/Greylisting Database',40,\&textinput,'file:delaydb','(\S*)',undef,'The directory/file with the delay local file. Obsolete if you use \'mysql\' in delaydb.','','8'],
1575['DelayShowDBwhite','Show Delay/Greylisting Save Database',40,\&textinput,'file:delaydb.white','(\S*)',undef,'The directory/file with the white-delay local file. Obsolete if you use \'mysql\' in delaydb.','','8'],
1576['DelayExpireOnSpam','Expire Spamming Whitelisted Tuplets',0,\&checkbox,1,'(.*)',undef,
1577  'If a whitelisted \'tuplet\' is ever associated with spam, viri, failed rbl, spf etc, it is removed from whitelisted tuplets database. <br />
1578  This renews the temporary embargo for subsequent mail involving the tuplet.'],
1579['CleanDelayDBInterval','Clean Up Delaying Database',10,\&textinput,10800,'(\d+)',undef,
1580  'Delete outdated entries from triplets and whitelisted tuplets databases every this many seconds.<br />
1581  Note: the current timeout must expire before the new setting is loaded, or you can restart.
1582  Defaults to 3 hours.'],
1583['noDelay','Don\'t Delay these IPs*',80,\&textinput,'file:files/nodelay.txt','(.*)','ConfigMakeIPRe',
1584  'Enter IP addresses that you don\'t want to be delayed, separated by pipes (|). There are misbehaving MTAs that will not be able to get a legitimate email through a Greylisting server because they do not try again later.<br /><a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/nodelay.txt target=files ><span class="positive">newest example file is here</a>','','7'],
1585['noDelayAddresses','Do not Delay these Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe','Enter sender/recipient email addresses that you don\'t want to be delayed, separated by pipes (|). You can list specific addresses (user@anydomain.com), addresses at any domain (user), or entire domains (@anydomain.com).  Wildcards are supported (fribo*@domain.com).<br />For example: fribo@anydomain.com|jhanna|@sillyguys.org or place them in a plain ASCII file one address per line: \'file:files/nodelayuser.txt\'.'],
1586
1587
1588['DelayError','Reply Code to Refuse Delayed Messages',80,\&textinput,'451 4.7.1 Please try again later','(45\d .*)',undef,
1589  'SMTP reply code to refuse delayed messages. Default: 451 4.7.1 Please try again later
1590  <br /><hr />
1591  <div class="menuLevel1">Notes On Delaying</div>
1592  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/delaying.txt\',3);" />'],
1593
1594[0,0,0,'heading','SPF/SRS '],
1595
1596['ValidateSPF','Enable SPF Validation','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
1597  'Enable Sender Policy Framework Validation as described at <a href="http://www.openspf.org/" rel="external">openspf</a>.<br />
1598  This requires an installed Mail::SPF module in PERL.  Scoring is done  with spfValencePB.'],
1599['SPFLog','Enable SPF logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1600  ''],
1601['spfSpamLovers','SpamLover SPF Check*',80,\&textinput,'','(.*)','ConfigMakeSLRe',''],
1602['spfpValencePB','SPF Pass Score, ',10,\&textinput,0,'(.*)',undef,'<span class="positive"> SPF',undef,undef,'msg003140','msg003141'],
1603
1604['spfsValencePB','SPF Softfailed',10,\&textinput,20,'(.*)',undef,'SPF'],
1605['spfnonValencePB','SPF None',10,\&textinput,0,'(.*)',undef,''],
1606['spfuValencePB','SPF Unknown',10,\&textinput,0,'(.*)',undef,''],
1607['spfeValencePB','SPF Error',10,\&textinput,5,'(.*)',undef,''],
1608['spfValencePB','SPF Failed',10,\&textinput,30,'(.*)',undef,''],
1609['SPFWL','Whitelisted SPF Validation',0,\&checkbox,'','(.*)',undef,
1610  'Enable Sender Policy Framework Validation for whitelisted users also.',undef,undef,'msg003480','msg003481'],
1611['SPFNP','noProcessing SPF Validation',0,\&checkbox,'','(.*)',undef,
1612  'Enable Sender Policy Framework Validation for nonprocessed messages also.',undef,undef,'msg009560','msg009561'],
1613['SPFLocal','Local and outgoing mail SPF Validation',0,\&checkbox,'','(.*)',undef,
1614  'Enable Sender Policy Framework Validation for local and outgoing messages also. Don\'t forget to configure your DNS-server for SPF if you enable this option.',undef,undef,'msg003490','msg003491'],
1615['failstrictLOCAL','Strict SPF Failing for Local Domains*',0,\&checkbox,'1','(.*)',undef,
1616  'Softfail/Neutral/None messages with local domain in sending address will be Failed.'],
1617['blockstrictLOCAL','Strict SPF Blocking for Local Domains*',0,\&checkbox,'1','(.*)',undef,
1618  'Failed messages with local domain in sending address will be blocked .'],
1619['AddSPFHeader','Add Received-SPF Header',0,\&checkbox,1,'(.*)',undef,
1620  'Add Received-SPF header.'],
1621
1622['noSPFRe','Regular Expression to Skip SPF Processing*',80,\&textinput,'','(.*)','ConfigCompileRe',
1623 'Put anything here to identify these messages in header'],
1624
1625['SPFsoftfail','Fail SPF Softfail Validations',0,\&checkbox,'1','(.*)',undef,
1626  'SPF \'softfail\' status responses will be set to \'fail\' if strictSPFRe is matched.<br />
1627  The possible results of a query are:
1628<br />pass:The client IP address is an authorized mailer for the sender. The mail should be accepted subject to local policy regarding the sender.
1629<br />fail:The client IP address is not an authorized mailer, and the sender wants you to reject the transaction for fear of forgery.
1630<br />softfail:The client IP address is not an authorized mailer, but the sender prefers that you accept the transaction because it isn\'t absolutely sure all its users are mailing through approved servers. The softfail status is often used during initial deployment of SPF records by a domain.
1631<br />neutral:The sender makes no assertion about the status of the client IP.
1632<br />none:There is no SPF record for this domain.
1633<br />permerror &amp; temperror:The DNS lookup encountered an error during processing.
1634<br />unknown:The domain has a configuration error in the published data or defines a mechanism that this library does not understand.',undef,undef,'msg003600','msg003601'],
1635['SPFneutral','Fail SPF Neutral Validations',0,\&checkbox,'','(.*)',undef,
1636  'Intentionally fail SPF neutral status responses',undef,undef,'msg003610','msg003611'],
1637['SPFqueryerror','Fail SPF Error Responses',0,\&checkbox,'','(.*)',undef,
1638  'Intentionally fail SPF \'error\' status responses',undef,undef,'msg003620','msg003621'],
1639['SPFnone','Fail SPF None Responses',0,\&checkbox,'','(.*)',undef,
1640  'Intentionally fail SPF \'none\' and \'unknown\' status responses',undef,undef,'msg003630','msg003631'],
1641['SPFunknown','Fail SPF Unknown  Responses',0,\&checkbox,'','(.*)',undef,
1642  'Intentionally fail SPF \'unknown\'  status responses',undef,undef,'msg003640','msg003641'],
1643
1644['strictSPFRe','Strict SPF Processing Regex*',80,\&textinput,'file:files/strictspf.txt','(.*)','ConfigCompileRe',
1645 'SPF \'softfail\' status responses will be set to \'fail\' for these sending addresses. Put anything here to identify the addresses. For example: \'@aol.com|@gmail.com|@msn.com|@live.com|@ebay.com|@ebay.nl|@bbt.com|@paypal.com|@einsundeins.de|@microsoft.com\''],
1646
1647['blockstrictSPFRe','Strict SPF Blocking Regex*',80,\&textinput,'@ebay.com|@paypal.com|@facebook.com|@ups.com','(.*)','ConfigCompileRe',
1648 'All failed messages will be blocked for these sending addresses. Put anything here to identify the addresses. For example: \'@ebay.com|@paypal.com|@facebook.com\''],
1649 ['DoSPFinHeader','Do SPF check on header \'from:\'',0,\&checkbox,'','(.*)',undef,
1650  'Do an additional SPF check on the header from: address if it is in blockstrictSPFRe *** breakes RFC rules ***',undef,undef,'msg003610','msg003611'],
1651
1652
1653
1654
1655
1656['SPFCacheInterval','SPF Cache Refresh Interval',4,\&textinput,3,'([\d\.]+)','configUpdateSPFCR',
1657  'SPF records in cache will be removed after this interval in days. 0 will disable the cache.  <input type="button" value=" show cache" onclick="javascript:popFileEditor(\'pb/pbdb.spf.db\',6);" />'],
1658
1659['DebugSPF','Enable SPF Debug output to ASSP Logfile',0,\&checkbox,'','(.*)',undef,
1660 'Enables verbose debugging of SPF queries within the Mail::SPF::Query module.
1661 <br /><hr />
1662 <div class="menuLevel1">Notes On SPF</div>
1663 <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/spf.txt\',3);" /> '],
1664['EnableSRS','Enable Sender Rewriting Scheme',0,\&checkbox,'','(.*)','updateSRS',
1665  'Enable Sender Rewriting Scheme as described at <a href="http://www.openspf.org/SRS" rel="external">www.openspf.org/SRS</a>.<br />
1666  This requires an installed Mail::SRS module in PERL.<br />
1667  You should use SRS if your message handling system forwards email for domains with published spf records.<br />
1668  Note that you have to setup the outgoing path (Relay Host and Port) to let ASSP see and rewrite your outgoing traffic.'],
1669['srsSpamLovers','SpamLover SRS Signed Bounces Check*',80,\&textinput,'','(.*)','ConfigMakeSLRe',''],
1670['SRSAliasDomain','Alias Domain',40,\&textinput,'example.com','(.*)','updateSRSAD',
1671  'SPF requires the SMTP client IP to match the envelope sender (return-path). When a message is forwarded through<br />
1672  an intermediate server, that intermediate server may need to rewrite the return-path to remain SPF compliant.<br />
1673  For example: example.com'],
1674['SRSSecretKey','Secret Key',20,\&textinput,'','(.*)','updateSRSSK',
1675  'A key for the cryptographic algorithms -- Must be at least 5 characters long.'],
1676['SRSTimestampMaxAge','Maximum Timestamp Age',5,\&textinput,21,'(\d+)',undef,
1677  'Enter the maximum number of days for which a timestamp is considered valid.'],
1678['SRSHashLength','Hash Length',5,\&textinput,4,'(\d+)',undef,
1679  'The number of bytes of base64 encoded data to use for the cryptographic hash.<br />
1680  More is better, but makes for longer addresses which might exceed the 64 character length suggested by RFC5321.<br />
1681  This defaults to 4, which gives 4 x 6 = 24 bits of cryptographic information, which means that a spammer will have <br />
1682  to make 2^24 attempts to guarantee forging an SRS address.'],
1683['SRSValidateBounce','Enable Bounce Recipient Validation','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,0,'(.*)',undef,
1684  'Bounce messages that fail reverse SRS validation (but not a valid SMTP probe)<br />
1685  will receive a 554 5.7.5 [Bounce address not SRS signed] SMTP error code.<br /> Scoring is done  with srsValencePB.'],
1686['srsValencePB','SRS Validate Bounce Failed Score, default=10 +',10,\&textinput,10,'(.*)',undef,'For SRSValidateBounce',undef,undef,'msg003210','msg003211'],
1687
1688['SRSno','Don\'t Rewrite These Addresses*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1689  'Don\'t rewrite addresses when messages come from/to these addresses. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). <br />For example: fribo@example.com|jhanna|@example.org'],
1690['noSRS','Don\'t Validate Bounces From these IPs*',80,\&textinput,'','(.*)','ConfigMakeIPRe',
1691  'Enter IP addresses that you don\'t want to validate bounces from, separated by pipes (|).
1692  For example:  145.145.145.145|145.146.<br /><hr />
1693  <div class="menuLevel1">Notes On SRS</div>
1694  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/srs.txt\',3);" />','','7'],
1695
1696[0,0,0,'heading','DNSBL <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=DNSBL" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="DNSBL" /></a>'],
1697['ValidateRBL','Enable DNS Blacklist Validation', '0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,1,'(.*)','configUpdateRBL',
1698'This requires an installed Net::DNS module in PERL. Scoring is done  with rblValencePB for \'fail\' and rblnValencePB for \'neutral\' results. '],
1699['rblnValencePB','DNSBL Neutral Score',10,\&textinput,50,'(.*)',undef,''],
1700['rblValencePB','DNSBL Failed Score',10,\&textinput,100,'(.*)',undef,''],
1701
1702['RBLLog','Enable DNSBL logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1703  ''],
1704['noRBL','Don\'t do DNSBL for these IPs*',80,\&textinput,'','(.*)','ConfigMakeIPRe',
1705 'Enter IP addresses that you don\'t want to be DNSBL validated, separated by pipes (|). For example:  145.145.145.145|145.146.',undef,'7'],
1706['RBLWL','Whitelisted DNSBL Validation',0,\&checkbox,'','(.*)',undef,
1707  'Enable DNSBL for whitelisted messages '],
1708['RBLNP','NoProcessing DNSBL Validation',0,\&checkbox,'','(.*)',undef,
1709  'Enable DNSBL for noprocessing messages '],
1710['AddRBLHeader','Add X-Assp-DNSBL Header',0,\&checkbox,1,'(.*)',undef,
1711  'Add X-Assp-DNSBL header to messages with positive reply from DNSBL.'],
1712['rblSpamLovers','DNSBL Failures Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000600','msg000601'],
1713['RBLServiceProvider','RBL Service Providers*',80,\&textinput,'file:files/dnsbls.txt','(\S*)','configUpdateRBLSP',
1714 'Names of DNSBLs to use separated by "|" or name of list \'file:files/dnsbls.txt\'. Defaults are:<br /> zen.spamhaus.org=>1|bl.spamcop.net=>1|bb.barracudacentral.org=>1|combined.njabl.org=>1|safe.dnsbl.sorbs.net=>1|psbl.surriel.com=>2|ix.dnsbl.manitu.net=>2|dnsbl-1.uceprotect.net=>2|dnsbl-2.uceprotect.net=>4.<br/>
1715DNSBL providers can be classified like bl.spamcop.net=>1. \'1\' is the most trustworthy class. \'6\' is the least trustworthy class. Numbers above 6 will be used as score directly. The value of the class acts as a divisor of rblValencePB. So  bl.spamcop.net=>1 would score 50, bl.spamcop.net=>2 would score 25 if rblValencePB is set to 50.
1716If the sum of scores surpasses rblValencePB, the DNSBL check fails. If not, the DNSBL check will be considered \'neutral\' and use the resulting score. <br/>
1717<a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/dnsbls.txt target=files ><span class="positive">newest example file is here</a>'],
1718
1719
1720['Showmaxreplies','Show All Possible Hits ',0,\&checkbox,'','(.*)',undef,
1721  'Show all hits instead of stopping at RBLmaxhits.'],
1722
1723['RBLmaxhits','Maximum Hits',3,\&textinput,2,'(.*)','configUpdateRBLMH','A hit is an affirmative response from a DNSBL.<br />
1724  The DNSBL module will check all of the DNSBLs listed under Service Provider. If the number of hits is greater or equal Maximum Hits, the email is flagged <span class="negative">failed</span>.<br /> If the number of hits is greater 0 and less Maximum Hits, the email is flagged <span class="negative">neutral</span>. <br />
1725RBLmaxhits is ignored if the RBLServiceProvider are classified (weighted), the email is flagged <span class="negative">failed</span> if weights for all DNSBLs is greater  rblValencePB. A weight is a number representing the trust we put into a DNSBL, 1 is highest - 6 is lowest.'],
1726['RBLmaxweight','RBL Maximum Weight',3,\&textinput,50,'(\d*)',undef,'A weight is a number representing the trust we put into a DNSBL.<br />
1727  The DNSBL module will check all of the DNSBLs listed under Service Provider. If the total of weights is greater or equal Maximum Weight, the email is flagged <b>Failed</b>.<br /> If the total of weights is greater 0 and less Maximum Weight, the email is flagged <b>Neutral</b>',undef,undef,'msg003840','msg003841'],
1728
1729['RBLmaxtime','Maximum Time',5,\&textinput,10,'(.*)',undef,'Maximum time in seconds to spend on each message performing DNSBL check.'],
1730['RBLsocktime','Socket Timeout',5,\&textinput,1,'(.*)',undef,'This sets the DNSBL socket read timeout in seconds.'],
1731
1732
1733['RBLCacheInterval','DNSBL Expiration Time',4,\&textinput,3,'([\d\.]+)','configUpdateRBLCR',
1734  'IPs in cache will be removed after this interval in days. 0 will disable the cache. <input type="button" value=" show cache" onclick="javascript:popFileEditor(\'pb/pbdb.rbl.db\',5);" /><hr /><div class="menuLevel1">Notes On DNSBL</div>
1735  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/rbl.txt\',3);" />'],
1736
1737
1738
1739
1740[0,0,0,'heading','URIBL'],
1741 ['ValidateURIBL','Enable URI Validation', '0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,'1','(.*)','configUpdateURIBL',
1742  'Enable URI Blocklist. Messages that fail URIBL validation will receive URIBLError SMTP error code. This requires an installed Net::DNS module and an installed Email::MIME module in PERL.
1743   Scoring is done  with uriblValencePB'],
1744 ['URIBLLog','Enable URIBL logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1745  ''],
1746
1747['uriblSpamLovers','URIBL Failures Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000610','msg000611'],
1748
1749['uriblnValencePB','URIBL Neutral Score+',10,\&textinput,30,'(\s*\d+\s*(?:[\|,]\s*\d+\s*){0,2})','ConfigChangeValencePB','Message/IP Scoring',undef,undef,'msg003240','msg003241'],
1750['uriblValencePB','URIBL Failed Score+',10,\&textinput,55,'(\s*\d+\s*(?:[\|,]\s*\d+\s*){0,2})','ConfigChangeValencePB','Message/IP Scoring',undef,undef,'msg003250','msg003251'],
1751
1752 ['URIBLWL','Do URI Blocklist Validation for Whitelisted',0,\&checkbox,'','(.*)',undef,'URIBL check is done ignoring all spamlovers and testmodes!',undef,undef,'msg003890','msg003891'],
1753 ['URIBLNP','Do URI Blocklist Validation for NoProcessing',0,\&checkbox,'','(.*)',undef,'URIBL check is done ignoring all spamlovers and testmodes!',undef,undef,'msg003900','msg003901'],
1754 ['URIBLLocal','Do URI Blocklist Validation for Local Mails',0,\&checkbox,'','(.*)',undef,'',undef,undef,'msg003910','msg003911'],
1755 ['URIBLISP','Do URI Blocklist Validation for ISP/Secondary',0,\&checkbox,1,'(.*)',undef,'',undef,undef,'msg003920','msg003921'],
1756 ['URIBLServiceProvider','URIBL Service Providers*',60,\&textinput,'file:files/uriblserviceprovider.txt','(.*)','configUpdateURIBLSP',
1757  'Domain Names of URIBLs to use separated by "|". You may set for every provider a weight like multi.surbl.org=>50.<br />
1758 The value of the weight can be set directly like=>45 or as a divisor of URIBLmaxweight . Low numbers < 6 are divisors . So if URIBLmaxweight = 50 (default) multi.surbl.org=>50  would be the same as multi.surbl.org=>1, multi.surbl.org=>2 would be the same as multi.surbl.org=>25.<br />
1759 If the sum of weights of all found uris surpasses URIBLmaxweight, the URIBL check fails.  If not, the URIBL check is scored as "neutral" . URIBLmaxhits is ignored when weights are used.<br />
1760 Some URIBL Service Providers, like multi.surbl.org, provides different return codes in a single DNS-zone: like 127.a.b.c - where a,b,c are used to identify a weight or type (or what ever) of the returned entry. If you want to care about special return codes, or if you want to use different weights for different return codes, you should use the following enhanced entry syntax:<br /><br />
1761 URIBL-Service-Provider=>result-to-watch=>weight (like:)<br />
1762 multi.surbl.org=>127.0.0.2=>2<br />
1763 multi.surbl.org=>127.0.0.4=>3<br />
1764 multi.surbl.org=>127.0.0.?=>4<br />
1765 multi.surbl.org=>127.0.0.*=>5<br /><br />
1766 You can see, the wildcards * (multiple character) and ? (single character) are possible to use in the second parameter. Never mix the three possible syntax types for the same URIBL Service Provider. An search for a match inside such a definition is done in reverse ASCII order, so the wildcards are used as last.',undef,undef,'msg003930','msg003931'],
1767['TLDS','Country Code TLDs*',60,\&textnoinput,'file:files/tlds-alpha-by-domain.txt','(.*)','ConfigMakeRe',
1768  'List of <a href="http://data.iana.org/TLD/tlds-alpha-by-domain.txt" rel="external">one level country code TLDs</a> '],
1769 ['URIBLCCTLDS','URIBL Country Code TLDs*',60,\&textnoinput,'file:files/URIBLCCTLDS.txt','(.*)','ConfigMakeRe',
1770  'List of <a href="http://george.surbl.org/two-level-tlds" rel="external">two level country code TLDs</a> and <a href="http://george.surbl.org/three-level-tlds" rel="external">three level country code TLDs</a> used to determine the base domain of the uri. Two level TLDs will be checked on third level, third level TLDs will be checked on fourth level. Any not listed domain will be checked in level two.',undef,undef,'msg003940','msg003941'],
1771 ['URIBLmaxuris','Maximum URIs',5,\&textinput,0,'(.*)',undef,
1772  'More than this number of URIs in the body will increase scoring with uriblValencePB. Enter 0 to disable feature.',undef,undef,'msg003950','msg003951'],
1773 ['URIBLmaxdomains','Maximum Unique Domain URIs',5,\&textinput,0,'(.*)',undef,
1774  'More than this number of unique domain URIs in the body will increase scoring with uriblValencePB. Enter 0 to disable feature.',undef,undef,'msg003960','msg003961'],
1775  ['URIBLNoObfuscated','Disallow Obfuscated URIs <a href="http://www.pc-help.org/obscure.htm" target="ASSPHELP"><img src="' . $wikiinfo . '" alt="obscure" /></a>',0,\&checkbox,'','(.*)',undef,
1776  'When enabled, messages with obfuscated URIs of types [integer/octal/hex IP, other things!] in the body will will increase scoring with uriblValencePB and if weights are used, the double weight will be used.',undef,undef,'msg003970','msg003971'],
1777 ['URIBLcheckDOTinURI','Check for \'DOT\' in URI',0,\&checkbox,'','(.*)',undef,
1778  'When enabled, assp will also check for the used word \'DOT\' instead of a \'.\' in URI\'s like \'example<b>dot</b>com or example<b>!d o-t_</b>com\' .<br />
1779   Enable this feature only, if you don\'t expect any problems in your national language (using \'dot\' + a toplevel domain in any words).',undef,undef,'msg008820','msg008821'],
1780
1781
1782 ['URIBLmaxhits','Maximum Hits',5,\&textinput,1,'(\d+\.\d\d?|\d*)','configUpdateURIBLMH',
1783  'A hit is an affirmative response from a URIBL.<br />
1784   The URIBL module will check all of the URIBLs listed under Service Provider,<br />
1785   and flag the email with a URIBL failure flag if more than this number of URIBLs return a postive blacklisted response.<br />
1786   This number should be less than or equal to URIBLmaxreplies and greater than 0.
1787   If the number of hits is greater or equal URIBLmaxhits, the email is flagged <span class="negative">failed</span>.
1788    If the number of hits is greater 0 and less URIBLmaxhits, the email is flagged <span class="spampassed">neutral</span><br />
1789    URIBLmaxhits is ignored if the URIBLServiceProvider are classified (weighted), the email is flagged <span class="negative">failed</span> if weights for all URIs is greater or equal URIBLvalencPB.
1790   '],
1791 ['URIBLmaxweight','URIBL Maximum Weight',3,\&textinput,0,'(.*)',undef,'A weight is a number representing the trust we put into a URIBL.<br />
1792  The URIBL module will check all of the URIBLs listed under URIBLServiceProvider <b>for every URI</b> found in an email. If the total of weights for all URIs is greater or equal this Maximum Weight, the email is flagged <b>Failed</b>.<br /> If the total of weights is greater 0 and less Maximum Weight, the email is flagged <b>Neutral</b> . If not defined or set to zero only URIBLmaxhit will be used to detect a fail or neutral state.',undef,undef,'msg009150','msg009151'],
1793 ['URIBLmaxtime','Maximum Time',5,\&textinput,10,'(.*)',undef,
1794  'This sets the maximum time in seconds to spend on each message performing URIBL checks.',undef,undef,'msg004000','msg004001'],
1795 ['URIBLsocktime','Socket Timeout',5,\&textinput,1,'(.*)',undef,'This sets the URIBL socket read timeout in seconds.',undef,undef,'msg004010','msg004011'],
1796 ['URIBLwhitelist','Whitelisted URIBL Domains*',60,\&textinput,'file:files/uriblwhite.txt','(.*)','ConfigMakeRe',
1797  'This prevents specific domains from being checked by URIBL module. For example:files/uriblwhite.txt.'],
1798 ['noURIBL','Don\'t Check Messages from these Addresses*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
1799  'Don\'t validate URIBL when messages come from these addresses. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). <br />For example: fribo@thisdomain.com|jhanna|@example.org',undef,undef,'msg004030','msg004031'],
1800
1801 ['AddURIBLHeader','Add X-Assp-Received-URIBL Header',0,\&checkbox,1,'(.*)',undef,
1802  'Add X-Assp-Received-URIBL header to messages with positive reply from URIBL.',undef,undef,'msg004040','msg004041'],
1803 ['URIBLCacheInterval','URIBL Cache Refresh Interval for Hits',3,\&textinput,3,'(.*)','configUpdateURIBLCR',
1804  'Domains in cache will be removed after this interval in days. 0 will disable the cache. <input type="button" value=" Show URIBL Cache" onclick="javascript:popFileEditor(\'pb/pbdb.uribl.db\',5);" /><br /><hr /><div class="menuLevel1">Notes On URIBL</div>
1805<input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/uribl.txt\',3);" />',undef,undef,'msg004050','msg004051'],
1806
1807
1808[0,0,0,'heading','Attachments'],
1809['DoBlockExes','Attachment Checking ','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,0,'([\s01234]?)',undef,'Note:Attachment checking will only be done if Email::MIME is installed. Scoring is done  with baValencePB.'],
1810['atSpamLovers','Bad Attachment/Virus Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000580','msg000581'],
1811
1812['baValencePB','Bad Attachment Score',10,\&textinput,20,'(.*)',undef, ''],
1813['AttachmentLog','Enable Attachment logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1814  ''],
1815['BlockExes','External Attachment Checking Level ','0:Level 0|1:Level 1|2:Level 2|3:Level 3|4:Level 4',\&listbox,0,'([\s01234]?)',undef,
1816  'Set the level of Attachment Blocking to 1-3 for attachments that should be blocked, set level to 4  for attachments that should be allowed only. Choose 0 for no attachment blocking.'],
1817['BlockWLExes','Whitelisted Attachment Checking','0:Level 0|1:Level 1|2:Level 2|3:Level 3|4:Level 4',\&listbox,0,'([\s01234]?)',undef,
1818  'Set the level of Attachment Checking to 0-4 for whitelisted senders. Choose 0 for no attachment blocking.'],
1819['BlockLCExes','Local Attachment Checking','0:Level 0|1:Level 1|2:Level 2|3:Level 3|4:Level 4',\&listbox,0,'([\s01234]?)',undef,
1820  'Set the level of Attachment Blocking to 0-4 for local senders. Choose 0 for no attachment blocking.'],
1821['BlockNPExes','NoProcessing Attachment Checking','0:Level 0|1:Level 1|2:Level 2|3:Level 3|4:Level 4',\&listbox,0,'([\s01234]?)',undef,
1822  'Set the level of Attachment Checking to 0-4 for noprocessing messages. Choose 0 for no attachment checking. '],
1823['BadAttachL1','Level 1 rejected File Extensions',80,\&textinput,'exe|scr|pif|vb[es]|js|jse|ws[fh]|sh[sb]|lnk|bat|cmd|com|ht[ab]','(.*)','updateBadAttachL1',
1824  'This regular expression is used to identify Level 1 attachments that should be blocked.<br />
1825  Separate entries with a pipe |. The dot . is assumed to precede these, so don\'t include it.<br /> For example:<br /> ad[ep]|asx|ba[st]|chm|cmd|com|cpl|crt|dbx|exe|hlp|ht[ab]|in[fs]|isp|js|jse|lnk
1826  <br/>|md[abez]|mht|ms[cipt]|nch|pcd|pif|prf|reg|sc[frt]|sh[bs]|vb|vb[es]|wms|ws[cfh]'],
1827['BadAttachL2','Level 2 rejected File Extensions',80,\&textinput,'','(.*)','updateBadAttachL2',
1828  'This regular expression is used to identify Level 2 attachments that should be checked.<br />
1829  Level 2 already includes all rejected extensions from Level 1. <br /> For example:<br /> (ad[ep]|asx|ba[st]|chm|cmd|com|cpl|crt|dbx|exe|hlp|ht[ab]|in[fs]|isp|js|jse|
1830  <br/>lnk|md[abez]|mht|ms[cipt]|nch|pcd|pif|prf|reg|sc[frt]|sh[bs]|vb|vb[es]|wms|ws[cfh]).zip'],
1831['BadAttachL3','Level 3 rejected File Extensions',80,\&textinput,'','(.*)','updateBadAttachL3',
1832  'This regular expression is used to identify Level 3 attachments that should be checked.<br />
1833  Level 3 includes Level 2 and Level 1.<br /> For example:<br /> zip|url'],
1834['GoodAttach','Level 4 Allowed File Extensions',80,\&textinput,'','(.*)','updateGoodAttach',
1835  'This regular expression is used to identify  attachments that should be allowed. All others are blocked. Separate entries with a pipe |. The dot . is assumed to precede these, so don\'t include it.<br /> For example:<br /> ai|asc|bhx|dat|doc|docx|eps|gif|htm|html|ics|jpg|jpeg|hqx|od[tsp]|pdf|ppt|rar|
1836  <br />
1837  rpt|rtf|snp|txt|xls|zip'],
1838 ['PassAttach','Passing File Names  ',80,\&textinput,'','(.*)','updatePassAttach',
1839  'This regular expression is used to identify  attachments that should mark the message as noprocessing. If you enter extensions do not precede it with a dot. This will take precedence over any bad attachment.'],
1840['AttachmentReportToRCPT','Send Attachment Report To Recipient',0,\&checkbox,'','(.*)',undef,
1841  'If set an email containing the Message ID, Remote IP, Message
1842Subject, Sender email address, Recipient email address, and the blocked ated attachment
1843detected will be sent to this Recipient.'],
1844
1845['AttachmentError','Reply Code to Refuse Rejected Attachments',80,\&textinput,'550 5.7.1 These attachments are not allowed -- Compress before mailing.','([25]\d\d .*)',undef,'The literal FILENAME (case sensitive) will be replaced with the name of the blocked attachment!<br />
1846 <hr /><div class="menuLevel1">Notes On Attachment Blocking</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/Attachments.txt\',3);" />'],
1847
1848
1849
1850[0,0,0,'heading','ClamAV and FileScan '],
1851['ScanLog','Enable Virus Check logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
1852  ''],
1853  ['UseAvClamd','Use ClamAV',0,\&checkbox,'','(.*)',undef,
1854    'If activated, the message is checked by ClamAV, this requires an installed
1855  File::Scan::ClamAV Perl module and a running Clamd . <br />The viruses will
1856  be stored in a special folder if the SpamVirusLog is set to
1857\'quarantine\' and the
1858  filepath to the viruslog is set. Scoring is done  using vdValencePB.'],
1859
1860['vdValencePB','Virus detected Score',10,\&textinput,50,'(.*)',undef, '',undef,undef,'msg003270','msg003271'],
1861['modifyClamAV','Modify ClamAV Module',1,\&checkbox,'1','(.*)',undef,'If set ClamAV modules ping and streamscan are modified. This may be disabled to use the original modules. <span class="negative">NOTE: Changing this  requires ASSP restart</span>'],
1862['AvClamdPort','Port or file socket for ClamAV',30,\&textinput,$defaultClamSocket,'(\S+)',undef,
1863 ' If the socket has been setup as a TCP/IP socket (see the TCPSocket option in the clamav.conf file - located for example in /etc/clamav/clamd.conf), then specify the TCPSocket (port). For example: 3310.
1864 If LocalSocket is specified in the clamav.conf file  then specify here the LocalSocket. For example /var/run/clamav/clamd.ctl.'],
1865['ClamAVBytes','Scan Bytes',8,\&textinput,50000,'(.*)',undef,
1866   'The number of bytes per message that will be scanned for virus and
1867attachment blocking. Normally ASSP looks only at MaxBytes of a message. Values of 100000 or larger are not recommended.'],
1868['ClamAVtimeout','ClamAV Timeout',3,\&textinput,10,'(.*)',undef,
1869'ClamAV will timeout after this many seconds.<br /> default: 10
1870seconds.'],
1871['NoScanRe','Skip ClamAV Regular
1872Expression*',80,\&textinput,'','(.*)','ConfigCompileRe',
1873  "Put anything here to identify messages which should not be checked
1874for viruses."],
1875['SuspiciousVirus','No-Blocking Virus Scan Scoring Regex**',80,\&textinput,'UNOFFICIAL','(.*)','ConfigCompileRe',
1876 'If a ClamAV or FileScan result matches this expression it will be scored with the suspicious virus score ( vsValencePB ) and the message will not be blocked.<br />
1877 It is possible to weight such results. Every weighted regex that contains at least one \'|\' has to begin and end with a \'~\' - inside such regexes it is not allowed to use a \'~\', even it is escaped - for example:  ~abc\\~|def~=>23 or ~abc~|def~=>23 - instead use the octal (\\126) or hex (\\x7E) notation (\\126), for example ~abc\\126|def~=>23 or ~abc\\x7E|def~=>23 . Every weighted regex has to be followed by \'=>\' and the weight value. For example: <br />Phishing\\.=>1.45|~Heuristics|Email~=>50  <br />or <br />~(Email|HTML|Sanesecurity)\\.(Phishing|Spear|(Spam|Scam)[a-z0-9]?)\\.~=>4.6|Spam=>1.1|~Spear|Scam~=>2.1 . <br />The multiplication result of the weight and the penaltybox valence value will be used for scoring, if the absolute value of weight is less or equal 6. Otherwise the value of weight is used for scoring.',undef,undef,'msg004220','msg004221'],
1878['vsValencePB','suspicious Virus Score',3,\&textinput,25,'(.*)',undef,'',undef,undef,'msg003260','msg003261'],
1879['noScan','Do Not Scan Messages from/to these
1880Addresses*',60,\&textinput,'','(.*)','ConfigMakeSLRe','Accepts
1881specific addresses (user@example.com), user parts (user) or entire
1882domains (@example.com).'],
1883['noScanIP','Do Not Scan Messages from these
1884IPs*',60,\&textinput,'','(.*)','ConfigMakeIPRe','Enter IP addresses that you don\'t want to be scanned for virus , separated by pipes (|). For example: 145.145.145.145|145.146.'],
1885['ScanWL','Scan Whitelisted Senders',0,\&checkbox,'1','(.*)',undef,''],
1886['ScanNP','Scan NoProcessing Messages',0,\&checkbox,'','(.*)',undef,''],
1887['ScanLocal','Scan Local Senders',0,\&checkbox,'','(.*)',undef,''],
1888['ScanCC','Scan Copied Spam Mails',0,\&checkbox,'','(.*)',undef,''],
1889['AvError','Reply Code to Refuse Infected Messages',80,\&textinput,'554
1890 5.7.1 Mail appears infected with INFECTION.','([25]\d\d .*)',undef,'Reply
1891code to refuse infected messages. The string INFECTION is replaced with
1892the name of the detected virus.<br />  For example: 554 5.7.1 Mail appears
1893infected with INFECTION -- disinfect and resend.'],
1894['EmailVirusReportsTo','Send Virus Report To This
1895Address',40,\&textinput,'','(.*)',undef,
1896  'If set an email containing the Message ID, Remote IP, Message
1897Subject, Sender email address, Recipient email address, and the virus
1898detected will be sent to this address. For example:
1899admin@example.com'],
1900['EmailVirusReportsHeader','Add Full Header To Virus Report To Mail
1901Address Above',0,\&checkbox,'','(.*)',undef,'If set the full message
1902headers will also be added to Virus Reports.'],
1903['EmailVirusReportsToRCPT','Send Virus Report To
1904Recipient',0,\&checkbox,'','(.*)',undef,'If set the intended
1905recipient of the message will be sent a copy of the Virus Report.
1906<input type="button" value=" Edit virusreport.txt file"
1907onclick="javascript:popFileEditor(\'reports/virusreport.txt\',2);"
1908/>'],
1909
1910
1911['DoFileScan','Use File System Virus
1912Scanner','0:disabled|1:block|2:monitor',\&listbox,0,'(.*)',undef,
1913  'If activated, the message is written to a file inside the
1914\'FileScanDir\' with an extension of \'maillogExt\'. After that ASSP
1915will call \'FileScanCMD\' to detect if the temporary file is infected
1916or not. The temporary created file(s) will be removed.<br />
1917  The viruses will be stored in a special folder if the SpamVirusLog
1918is set to \'quarantine\' and the filepath to the viruslog is set.'],
1919['FileScanWL','Scan Whitelisted Senders',0,\&checkbox,'1','(.*)',undef,''],
1920['FileScanNP','Scan NoProcessing Messages',0,\&checkbox,'1','(.*)',undef,''],
1921['FileScanLocal','Scan Local Senders',0,\&checkbox,'','(.*)',undef,''],
1922['FileScanDir','File Scan
1923Directory',80,\&textinput,"$asspbase/virusscan",'(.*)','',
1924  'Define the full path to the directory where the messages are
1925temporary stored for the file system virus scanner. This could be any
1926directory inside your file system. The running ASSP process must have
1927full permission to this directory and the files inside! For defining any full filepathes, always use slashes ("/") not backslashes. '],
1928['FileScanCMD','File Scan Command',80,\&textinput,'NORUN','(.*)','',
1929  'ASSP will call this system command and expects a returned string
1930from this command. This returned string is checked against
1931\'FileScanBad\' and/or \'FileScanGood\' to detect if the message is
1932OK or not! If the file does not exists after the command call, the
1933message is consider infected. ASSP expects, that the file scan is
1934finished when the command returns!<br />
1935   The literal \'FILENAME\' will be replaced by the full qualified
1936file name of the temporary file.<br />
1937
1938   The literal \'FILESCANDIR\' will be replaced with the value of
1939FileScanDir.<br />
1940   All outputs of this command to STDERR are automatic redirected to
1941STDOUT.<br />
1942   FileScan will not run, if FileScanCMD is not specified.<br />
1943   If you have your online/autoprotect file scanner configured to
1944delete infected files inside the \'FileScanDir\', define \'NORUN\' in
1945this field! In this case FileScanGood and FileScanBad are ignored. If
1946there is a need to wait some time for the autoprotect scanner, write
1947\'NORUN-dddd\', where dddd are the milliseconds to wait!<br />
1948   Depending on your operating system it may possible that you have to
1949quote (\' or ") the command, if it contains whitespaces. The replaced
1950file name will be quoted by ASSP if needed. For example: \'d:\utility\touch.exe FILENAME\''],
1951['FileScanBad','RegEx to Detect \'BAD\' in Returned
1952String*',80,\&textinput,'','(.*)','ConfigCompileRe',
1953  'Put anything here to identify bad messages by the string returned
1954from the FileScanCMD. If this regular expression matches, the message
1955is considered infected.'],
1956['FileScanGood','RegEx to Detect \'GOOD\' in Returned
1957String*',80,\&textinput,'','(.*)','ConfigCompileRe',
1958  'Put anything here to identify good messages by the string returned
1959from the FileScanCMD. If this regular expression matches and
1960\'FileScanBad\' does not, the message is considered not infected.'],
1961['FileScanRespRe','FileScan Reponds
1962Regex*',60,\&textinput,'','(.*)','ConfigCompileRe',
1963  'A regular expression that will be used over the text returned from
1964the FileScanCMD. The result of this regex is used as virus name
1965(INFECTION) in AvError. For example: infected by (.+)<br />
1966  <hr /><div class="menuLevel1">Notes On Virus Checks</div><input
1967type="button" value="Notes"
1968onclick="javascript:popFileEditor(\'notes/viruscheck.txt\',3);"
1969/>'],
1970
1971[0,0,0,'heading','Regex Filter / Spambombs <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Regular_Expressions" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="bombRe" /></a>'],
1972['BombLog','Enable Bomb logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
1973  'If set to verbose, the reporting to the logfile and the X-ASSP- scoring header will show the complete list of all hits. Otherwise only the highest match will be shown.',undef,undef,'msg007030','msg007031'],
1974['bombSpamLovers','Bomb Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000550','msg000551'],
1975
1976 ['preHeaderRe','Regular Expression to early Identify Spam in Handshake and Header Part*',80,\&textinput,'file:files/preheaderre.txt','(.*)','ConfigCompileRe',
1977 'Until the complete mail header is received, assp is processing the handshake and header content line per line, but the first mail content check is done after the complete mail header is received.<br />
1978 It is possible, that some content (malformed headers, forbidden characters or character combinations) could cause assp to die or to run into a unrecoverable exception (eg. segment fault).<br />
1979 Use this regular expression to identify such incoming mails based on a line per line check, at the moment where a single line is received.<br />
1980If a match is found and preHeaderCollect not set, assp will immediately send a \'421 <myName> closing transmission\' reply to the client and will immediately terminate the connection. If preHeaderCollect is not set ASSP will try to store the mail in Spam-folder.'],
1981
1982['maxBombValence','Maximum Score on Regex Match per Mail per Check',3,\&textinput,70,'(.*)',undef, 'This option is valid for all regex searches which allow weights (marked with **) and limits the maximum penalty per check. maxBombHits is overwritten. If not set the search will stop if MessageScoringUpperLimit or maxBombHits is reached. For example: 70'],
1983['maxBombHits','Maximum Number Of Hits in Regex Search*',80,\&textinput,'blackRe=>3|bombSubjectRe=>3|bombSuspiciousRe=>3|bombRe=>3','(.*)','ConfigMakeRe', 'This option is valid for all regex searches which allow weights (marked with **). Use the syntax: regextype=>3|other.regextype=>3 to overwrite the maximum number of hits a regexsearch should perform. Default for regex searches are \'blackRe=>3|bombSenderRe=>1|bombHeaderRe=>1|bombSubjectRe=>3|bombCharSets=>1|bombSuspiciousRe=>3|bombRe=>3\'. The search will stop if MessageScoringUpperLimit or maxBombHits is reached. This can be overwritten by maxBombValence.'],
1984['DoBlackRe','Use Black Regular Expression to Identify Spam','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,1,'(.*)',undef,
1985  'This works similar to DoBombRe but has different default in noprocessing. Envelope, Header and Data Part are checked  against the BlackRe. Scoring is done  with blackValencePB - the Valence is the sum of all valences(weights) of all found blackRe(s). Blocking will only be done if \'block\' is set  (default) and the messagescore is equal or exceeds blackValencePB.  '],
1986['blackRe','Black Regular Expressions to Identify Spam ** ',80,\&textinput,'file:files/blackre.txt','(.*)','ConfigCompileRe',
1987  'This is a stricter version of bombRe (blackReNP, blackReISPIP are enabled by default). If an incoming email matches this expression it will be considered spam. The expressions here will work as in  <a href="http://www.enginsite.com/Library-Perl-Regular-Expressions-Tutorial.htm" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Perl-Regular-Expressions-Tutorial" />Regular Expressions</a> As all fields marked with two asterisk (**) do - this  regular expressions (regex) can accept a weight value. Every weighted regex has to be followed by \'=>\' and the weigth value. The search will continue until maxBombHits is reached or maxBombValence is exceeded (if set). Newest example file:<a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/blackre.txt target=files ><span class="negative">blackre.txt</a>'],
1988
1989['blackValencePB','Black Expression Valence Score+',10,\&textinput,40,'(.*)','ConfigChangeValencePB', ''],
1990['blackReWL','Do Black Regular Expressions Checks for Whitelisted',0,\&checkbox,'','(.*)',undef,''],
1991['blackReNP','Do Black Regular Expressions Checks for NoProcessing',0,\&checkbox,'1','(.*)',undef,''],
1992
1993['blackReISPIP','Do Black Regular Expressions Checks for ISPIP',0,\&checkbox,'1','(.*)',undef,''],
1994
1995['blackReLocal','Do Black Regular Expressions Checks for Local Messages',0,\&checkbox,'','(.*)',undef,''],
1996['DoBombHeaderRe','Use Header Regular Expressions ','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
1997  'If activated, each message-header is checked  against bombHeaderRe. Scoring is done  with bombValencePB'],
1998['bombValencePB','Bomb Expression Score+',10,\&textinput,25,'(.*)','ConfigChangeValencePB', '',undef,undef,'msg002680','msg002681'],
1999
2000
2001['bombHeaderRe','RegEx to find Spam in Header Part **',80,\&textinput,'','(.*)','ConfigCompileRe',
2002  'Header will be checked against this Regex if DoBombHeaderRe is enabled. '],
2003['bombSenderRe','RegEx to find Spam in Envelope**',80,\&textinput,'file:files/bombsenderre.txt','(.*)','ConfigCompileRe','Expression to identify mailfrom,ip and helo.'],
2004['bombSubjectRe','RegEx to find Spam in Subject **',80,\&textinput,'file:files/bombsubjectre.txt','(.*)','ConfigCompileRe',' newest example file: <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/bombsubjectre.txt target=files ><span class="negative">bombsubjectre.txt</a>.  The expressions here will work as original <a href="http://www.enginsite.com/Library-Perl-Regular-Expressions-Tutorial.htm" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Perl-Regular-Expressions-Tutorial" />Regular Expressions</a>'],
2005['maxSubjectLength','Maximum allowed Subject Length',20,\&textinput,'250=>20','^(\d+(?:\=\>\d+)?|)$',undef,'If set to a value greater than 0, assp will check the length of the Subject of the mail. If the Subject length exceeds this value, the message score will be increased by \'bombValencePB\' and the string that is checked in \'bombSubjectRe\' will be trunked to this length. It is possible to define a special weight using the syntax \'length=>value\', in this case the defined absolute value will be used instead of \'bombValencePB\' to increase the message score. If the subject is too long and this weight is equal or higher than \'bombValencePB\' no further bomb checks will be done on the subject.',undef,undef,'msg009360','msg009361'],
2006
2007
2008['bombCharSets','RegEx to find Foreign Charsets ** ',60,\&textinput,'file:files/charsets.txt','(.*)','ConfigCompileRe','Header will be checked against this Regex if DoBombHeaderRe is enabled. A weight can be assigned. For example:<br /> charset=.?BIG5|charset=.?CHINESEBIG|charset=.?GB2312|charset=.?KS_C_5601|charset=.?KOI8=>0.5|charset=.?EUC-KR|charset=.?ISO-2022|charset=.?CP1251. '],
2009
2010['DoBombRe','Use Bomb Regular Expressions','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
2011  'If activated, each message is checked  against bombRe Regular Expressions. Scoring is done  with bombValencePB - the Valence is the sum of all valences(weights) of all found bombRe(s)
2012  '],
2013
2014['bombRe',' DoBombRe: RegEx for Header and Data Part **',80,\&textinput,'file:files/bombre.txt','(.*)','ConfigCompileRe','Header and Data will be checked against this Regular Expressions if DoBombRe is enabled. Newst example file: <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/bombre.txt target=files ><span class="negative">bombre.txt</a>. The expressions here will work as in<a href="http://www.enginsite.com/Library-Perl-Regular-Expressions-Tutorial.htm" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Perl-Regular-Expressions-Tutorial" />Regular Expressions</a>'],
2015
2016['bombSuspiciousRe',' DoBombRe: Regular Expression to Score Blackish and/or Whitish Expressions **',80,\&textinput,'file:files/suspiciousre.txt','(.*)','ConfigCompileRe','Put here anything which might be suspicious (blackish) or trustworthy (whitish). bombSuspiciousValencePB will be multiplied by the weight and increases/decreases the total score.  Trustworthiness (whitishness) will be assigned by using a negative weight.  For example:<br />news=>-0.4|no-?reply=>-0.5|passwor=>-0.7'],
2017
2018['bombCharSetsMIME','DoBombRe: RegEx to Identify Foreign Charsets in MIME** ',60,\&textinput,'file:files/charsets.txt','(.*)','ConfigCompileRe','MIME parts will be checked against this Regex. A weight can be assigned. For example:<br /> charset=.?BIG5|charset=.?CHINESEBIG|charset=.?GB2312|charset=.?KS_C_5601|charset=.?KOI8=>0.5|charset=.?EUC-KR|charset=.?ISO-2022|charset=.?CP1251. '],
2019
2020['maxBombSearchTime','Maximum time spend on Regex Search',3,\&textinput,5,'(.*)',undef, 'Maximum time in seconds that is spend on  regex check. This time check is done, after every found regex. So it is possible that the regex search takes longer as the defined value, if no match is found or a single search takes more time.'],
2021['noBombScript','Don\'t Check Messages from these Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2022  'Don\'t detect spam bombs in messages from these addresses. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).<br /><hr /><div class="menuLevel1">Notes On Bomb Regex</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/bombre.txt\',3);" />'],
2023
2024[0,0,0,'heading','Bayesian Filter <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Bayesian Filter" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="Bayesian Filter"  /></a>'],
2025['BayesianLog','Enable Bayesian Logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
2026  'Enables verbose logging of  Bayesian checks in the maillog.'],
2027
2028['DoBayesian','Bayesian Check','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,3,'(.*)',undef,
2029  "If activated, the message is checked  based on Bayesian factors in spamdb . This needs a fully functional spamdb built by rebuildspamdb. For starters it is best practice  to put this inactiv and built the spamdb collection with the help of DSNBL ,URIBL and spamaddresses. Scoring is done with baysValencePB for external mails, baysValencePB_local is used for outgoing and internal mails - both values are multiplied with the detected baysProbability .",undef,undef,'msg004710','msg004711'],
2030
2031['baysValencePB','Bayesian Score',10,\&textinput,49,'(.*)',undef, ''],
2032['baysSpamLovers','Bayesian Spam-Lover*',60,\&textinput,'','(.*)','ConfigMakeSLReSL','',undef,undef,'msg000510','msg000511'],
2033['baysSpamLoversRe','Regular Expression to Identify Bayesian SpamLover*',80,\&textinput,'','(.*)','ConfigCompileRe',
2034 'If a message matches this regular expression it will be considered a Bayesian SpamLover message. For example: passwor|news'],
2035
2036['BayesianStarterDB','Bayesian Starter Database ',40,\&textinput,'starterdb/spamdb','(.*)',undef,'A ready to use spamdb which can be used alone or together with your local spamdb. It will be automatically downloaded at startup and placed in folder "assp/starterdb". No download if empty. Manually download from here: <a href="http://sourceforge.net/projects/asspV1/files/ASSP%20Installation/Spam%20Collection/spamdb.gz" target=wiki>Spam%20Collection/spamdb.gz</a>'],
2037['enableStarterDB','Use Bayesian Starter Database ',0,\&checkbox,'1','(.*)',,undef,'Enable a prebuild BayesianStarterDB which can be used alone or together with your local spamdb. It will be automatically downloaded at startup and placed in folder "assp/starterdb/spamdb". '],
2038
2039['downloadStarterDBNow','Run downloadStarterDB Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will download the BayesianStarterDB. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
2040['BayesWL','Bayesian Check on Whitelisted Senders',0,\&checkbox,'','(.*)',undef,''],
2041['BayesNP','Bayesian Check on NoProcessing Messages',0,\&checkbox,'','(.*)',undef,''],
2042['BayesLocal','Bayesian Check on Local Senders',0,\&checkbox,'','(.*)',undef,''],
2043['BayesMaxProcessTime','Bayesian Check Timeout ',3,\&textinput,'30','(\d+)',undef,'The Bayesian Checks are the most memory and CPU consuming tasks that ASSP is doing on a message. If such tasks running to long on one message, other messages could run in to SMTPIdleTimeout. Define here the maximum time in seconds that ASSP should spend on Bayesian Checks for one message. (Cannot be greater than 60 seconds!)'],
2044['noBayesian','Skip Bayesian Check*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2045  'Mail from/to any of these addresses are ignored by Bayesian check, mails will not be stored in spam/notspam collection. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (user*@example.com)'],
2046['noBayesian_local','Skip Bayesian for this local senders*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2047 'Mail from any of these local addresses are ignored by Bayesian check, mails will not be stored in spam/notspam collection. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com)',undef,undef,'msg009570','msg009571'],
2048 ['yesBayesian_local','Do Bayesian for this local senders only*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2049 'Mail from any of these local addresses will perform Bayesian check, noBayesian_local will be ignored. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com)',undef,undef,'msg009570','msg009571'],
2050['baysTestModeUserAddresses','Bayesian Testmode User Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe','These users are in testmode ( mark subject only ) for bayesian spam, even with testmode off'],
2051['maxBayesValues','Maximum most significant results used per mail to calculate Bayesian-Probability',3,\&textinput,'40','([2-9]\d|\d{3})',undef,'Maximum count of most significant values used to calculate the Bayesian/HMM-Spam-Probability and the confidence of that probability.
2052 ',undef,undef,'msg007890','msg007891'],
2053['baysProbability','Bayesian Probability Threshold ',3,\&textinput,'0.6','(0\.\d+)',undef,' Messages with spam-probability below or equal this threshold are considered Ham. Recommended \'0.6\'.<br />
2054 An resulting Spam-Probability above this value is multiplied with baysValencePB_local or baysValencePB to get the penaltybox Valence for the IP- and message score. In other words, the penaltybox Valence is weighted by the Spam-Probability in case Spam is detected.<br />
2055 An resulting Spam-Probability below this value but higher than ( 1 - baysProbability ) is stated as \'UNSURE\' . In this case the half score will be added to the message score but not to the IP score and the message will not be blocked.<br /><br />
2056 The following default Bayesian math (prob = p1 / (p1 + p2)) is used to calculate the SpamProb value for \'n\' found Bayesian-Word-Pairs, each with a spam-weight \'p\' - where 0&lt;p&lt;1 :<br /><br />
2057 \'SpamProb\' = (p<sub>1</sub> * p<sub>2</sub> * ... * p<sub>n</sub>) / ( p<sub>1</sub> * p<sub>2</sub> * ... * p<sub>n</sub>  + (1 - p<sub>1</sub>) * (1 - p<sub>2</sub> ) * ... * (1 - p<sub>n</sub>))<br />',undef,undef,'msg004740','msg004741'],
2058
2059
2060
2061['AddSpamProbHeader','Add Bayes Probability Header',0,\&checkbox,'','(.*)',undef,
2062 'Adds a line to the email header "X-Assp-Spam-Prob: 0.0123" Probability ranges from 0 to +1 where > baysProbability is spam.
2063<br /><hr />
2064  <div class="menuLevel1">Notes On Bayesian</div>
2065  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/bayesian.txt\',3);" />',undef,undef,'msg004790','msg004791'],
2066
2067[0,0,0,'heading','Blocking Reports'],
2068['ReportLog','Enable Report logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,2,'(.*)',undef,
2069  ''],
2070
2071['EmailBlockReport','Request Block Report',40,\&textinput,'assp-blockreport','(.*)',undef,
2072 'Any mail sent by local/authenticated users to this username will be interpreted as a request to get a report about blocked emails. Do not put the full address here, just the user part. For example: assp-blockreport<br />
2073 Leading digits/numbers in the mail subject will be interpreted as "report request for the last number of days". If the number of days is not specified in the mail subject, a default of 5 days will be used to build the report. <br />
2074 All characters behind the "number of days" will be interpreted as a regular expression to overwrite the BlockReportFilter - leading and trailing white spaces will be ignored.<br />
2075Only Users defined in EmailBlockTo, EmailAdmins and EmailAdminReportsTo are \'Admins\' and can request a report for other users. They have to use a special syntax with \'=>\' in the body of the report request. The syntax is: <br />
2076 QueryAddress=>ReportRecipient=>ReportDays<br />There may be one or many lines with this syntax . For example:<br />
2077 user@domain and user@domain=>user@domain - will send a report for this user to this user<br />
2078 *@domain (better use) *@domain=>* - will send a report for every blocked user in this domain to this user<br />
2079 user@domain=>recipient@any-domain - will send a report for user@domain to recipient@any-domain<br />
2080 *@domain=>recipient@any-domain - will send a report for every blocked user in this domain to recipient@any-domain<br />
2081 A third parameter is possible to set, which defines the number of days for which the report should be created. The default (if empty or not defined) is one day. This value is used to calculate the \'next run date\'. For example:<br />
2082 *@domain=>recipient@any-domain=>2 - creates a report for two days.<br />
2083 *@domain=>*=>14 - creates a report for 14 days.<br />
2084 user@domain=>=>3 or user@domain=>*=>3 - creates a report for three days. The second parameter is here empty or *.<br />
2085 To overwrite the defined BlockReportFilter, you can define a fourth parameter, which contains the regular expression to use.<br />
2086 *@domain=>*=>14=>virus|newsletter - creates a report for 14 days and skips all lines that contains the words \'virus\' or \'newsletter\'.<br />
2087 If an admin emails a block report request and specifies a filter in the subject of the email and a fourth parameter in the body, both regular expressions will be merged in to a single regex for each line.<br />
2088 If you or a user want the default BlockReportFilter to become part of the overwrite regex, the literal \'$BRF\' should be inluded in the regex like:<br />
2089 *@domain=>*=>14=>virus|$BRF|newsletter - or even in the subject of the email<br />
2090 In this case the literal \'$BRF\' will be replaced by the BlockReportFilter.<br />
2091 Only Admins are able to request blockreports for non local email addresses. For example:<br />
2092 user@non_local_domain=>recipient@any-domain=>4<br />
2093 *@non_local_domain=>recipient@any-domain=>4<br />
2094 This will result in an extended blockreport for the non local address(es). Replace \'non_local_domain\' with the domain name you want to query for.<br />
2095 It is possible to change the complete design of the BlockReports to your needs,  using a html-css file. An default css-file \'blockreport.css\' is in the image folder.<br />
2096 There you can also find a default icon file \'blockreporticon.gif\' and a default header-image-file \'blockreport.gif\' - which is the same like \'logo.gif\'.  There is no need to install that fles. If assp can not find this files in its
2097 image folder, it will use default hardcoded css and icon. If the file \'blockreport.gif\' is not found \'logo.gif\' will be used.<br />
2098
2099  <input type="button" value=" Edit blockreport_sub.txt file" onclick="javascript:popFileEditor(\'reports/blockreport_sub.txt\',2);" /><br />
2100  <input type="button" value=" Edit blockreport_html.txt file" onclick="javascript:popFileEditor(\'reports/blockreport_html.txt\',2);" /><br />
2101  <input type="button" value=" Edit blockreport_text.txt file" onclick="javascript:popFileEditor(\'reports/blockreport_text.txt\',2);" />','Basic',undef,'msg008400','msg008401'],
2102
2103['EmailBlockReply','Reply to Block-Report Request','0:NO REPLY|1:REPLY TO SENDER|2:REPLY TO EmailBlockTo|3:REPLY TO BOTH',\&listbox,1,'(.*)',undef,
2104  '',undef,undef,'msg008420','msg008421'],
2105['EmailBlockTo','Send Copy of Block-Reports TO',40,\&textinput,'','(.*@.*)?',undef,
2106  'Email sent from ASSP acknowledging your submissions will be sent to this address if EmailBlockReply is set. For example: admin@domain.com'],
2107['EmailBlockReportDomain','Email Domain',40,\&textinput,'@assp.local','(\@.*)',undef,
2108  'BlockReport will build addresses for requests using this domain. Set this to a local domain or use the default. Notice the leading required \'@\'! For example: @assp.local.'],
2109
2110['QueueUserBlockReports','Queue User Block Report Requests','0:run instantly|1:store and run once at midnight|2:store and run scheduled|3:run delayed',\&listbox,0,'(.*)',undef,
2111  'How to process block report requests for users (not EmailBlockTo, EmailAdmins, EmailAdminReportsTo).<br />
2112  \'run instantly\' - the request will be processed instantly (not stored).<br />
2113  \'store and run once at midnight\' - the request will be stored/queued, runs at QueueSchedule, and will be removed from queue after that<br />
2114
2115 \'store and run scheduled\' - the request will be stored/queued, runs permanently scheduled at BlockReportSchedule until it will be removed from queue - a \'+\' in the subject is not needed<br />
2116  \'run delayed\' - the request will be stored and  processed during the next minutes<br />
2117  To add a request to queue the user has to send an email to EmailBlockReport. Leading digits/numbers in the mail subject will be interpreted as "report request for the last number of days". If the number of days is not specified in the mail subject, a default of 5 days will be used to build the report.<br />
2118  If \'run instantly\',\'run delayed\' or \'store and run once at midnight\' is selected, but a user wants to schedule a permanent request, a leading \'+\' before the digits in subject is required.<br />
2119  To remove a request from queue the user has to send an email to EmailBlockReport with a leading \'-\' in the subject.<br />
2120  <input type="button" value=" Edit user report queue" onclick="javascript:popFileEditor(\'files/UserBlockReportQueue.txt\',2);" /><input type="button" value=" Edit user report instant queue" onclick="javascript:popFileEditor(\'files/UserBlockReportInstantQueue.txt\',2);" />'],
2121['QueueSchedule','Runtime for Queued Requests',4,\&textinput,'0','(.*)',undef,
2122  'Runtime hour for reports in QueueUserBlockReports. Set a number between 0 and 23. 0 means midnight and is default'],
2123['BlockRepForwHost','Forward The Blockreportrequest to other ASSP',40,\&textinput,'','(.*)',undef,'If you are using more than one ASSP (backup MX), define the IP:relayPort of the other ASSP here (separate multiple entries by "|"). The Blockreportrequest will be forwarded to this ASSP and the user will get a blockreport from every ASSP. The perl module <a href="http://search.cpan.org/search?query=Net::SMTP/" rel="external">Net::SMTP</a> is required to use this feature.'],
2124
2125['BlockReportFile','File for Blockreportrequest',40,\&textinput,'','(file:.+)|',undef,'A file with BlockReport requests. ASSP will generate a block report for every line in this file (file:files/blockreportlist.txt - file: is required if defined!) every day at BlockReportSchedule for the last day. The perl modules <a href="http://search.cpan.org/search?query=Net::SMTP/" rel="external">Net::SMTP</a> and <a href="http://search.cpan.org/search?query=Email::MIME /" rel="external">Email::MIME </a> are required to use this feature. A report will be only created, if there is at least one blocked email found! The syntax is: <br />
2126 QueryAddress=>ReportRecipient=>ReportDays<br />
2127There may be one or many lines with this syntax. For example:<br />
2128 user@domain and user@domain=>user@domain - will send a report for this user to this user<br />
2129 *@domain (better use) *@domain=>* - will send a report for every blocked user in this domain to this user<br />
2130 *@* - creates a report for all local users in all local domains<br />
2131 user@domain=>recipient@any-domain - will send a report for user@domain to recipient@any-domain<br />
2132 *@domain=>recipient@any-domain - will send a report for every blocked user in this domain to recipient@any-domain<br />
2133A third parameter is possible to set, which defines the number of days for which the report should be created. The default (if empty or not defined) is one day. This value is used to calculate the \'next run date\'. For example:<br />
2134 *@domain=>recipient@any-domain=>2 - creates a report for two days.<br />
2135 *@domain=>*=>14 - creates a report for 14 days.<br />
2136 user@domain=>=>3 or user@domain=>*=>3 - creates a report for three days. The second parameter is here empty or *!<br />
2137 To overwrite the defined BlockReportFilter, you can define a fourth parameter, which contains the regular expression to use.<br />
2138 *@domain=>*=>14=>virus|newsletter - creates a report for 14 days and skips all lines that contain the words \'virus\' or \'newsletter\'.<br />
2139 Only Admins are able to request blockreports for non local email addresses. For example:<br />
2140 user@non_local_domain=>recipient@any-domain=>4<br />
2141 *@non_local_domain=>recipient@any-domain=>4<br />
2142 This will result in an extended blockreport for the non local address(es). Replace \'non_local_domain\' with the domain name you want to query for.',undef,undef,'msg008470','msg008471'],
2143['BlockReportSchedule','Runtime BlockReportFile',4,\&textinput,'0','(.*)',undef,
2144  'Runtime hour for reports in BlockReportFile. Set a number between 0 and 23. 0 means midnight and is default.'],
2145['BlockReportNow','Generate a BlockReport from BlockReportFile Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will generate a block report from BlockReportFile now. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
2146['BlockMaxSearchTime','Max Search time per log File',4,\&textinput,'0','(\d+)',undef,
2147  'The maximum time in seconds, the Blockreport feature spends on searching in one log file. If this value is reached, the next log file will be processed. A value of 0 disables this feature and all needed log files will be fully processed.'],
2148['BlockReportFormat','The format of the Report Email','0:text and html|1:text only|2:html only',\&listbox,1,'(.*)',undef,
2149  'Block reports will be sent as multipart/alternative MIME messages. They normaly contains two parts, a plain text part and a html part. Select "text only" or "html only" if you want to skip any of this parts.<br />
2150  To make it possible to detect a resent email, ASSP will add a header line "X-Assp-Resend-Blocked: myName" to each email!'],
2151['BlockReportHTTPName','My HTTP Name',40,\&textinput,'','(.*)',undef,'The hostname for HTTP links in AdminUsers Blockreports. If not defined the local hostname will be used.'],
2152['BlockReportFilter', 'Regular Expression to Skip Log Records*',80,\&textinput,'','(.*)','ConfigCompileRe',
2153 "Put anything here to identify messages which should not be reported. For example:  \\[Virus\\]|\\[BlackDomain\\]"],
2154
2155['inclResendLink','Include a Resend-Link for every resendable email','0:disabled|1:in plain text report|2:in html report|3:in both',\&listbox,3,'(.*)',undef,
2156  'Block reports will be sent as multipart/alternative MIME messages. They contains two parts, a plain text part and a html part. If a blocked email is stored in any folder, it is possible to include a link for each email in to the report. Define here what you want ASSP to do. Note: File name logging (fileLogging) must be on! The perl module <a href="http://search.cpan.org/search?query=Email::Send/" rel="external">Email::Send</a> is required to use this feature.'],
2157['BlockResendLink','Which Link Should be included','0:both|1:left|2:right',\&listbox,0,'(.*)',undef,
2158  'If HTML is enabled in inclResendLink, two links (one on the left and one on the right site) will be included in the report email by default. Depending on the used email clients it could be possible, that one of the two links will not work for you. Try out what link is working and disable the other one, if you want.'],
2159['BlockResendLinkLeft','User which get the Left link only* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2160  'List of users and domains that will get the left link only. The setting for BlockResendLink will be ignored for this entries!'],
2161['BlockResendLinkRight','User which get the right link only* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2162  'List of users and domains that will get the right link only. The setting for BlockResendLink will be ignored for this entries!'],
2163['DelResendSpam','Delete Mails in Spam Folder',0,\&checkbox,'1','(.*)',undef, 'If selected, an user request to resend a blocked email will delete the file in the spamlog folder - an admin request will move the file to the correctednotspam folder.'],
2164['autoAddResendToWhite','Automatic add Resend Senders to Whitelist','0:no|1:Users only|2:Admins only|3:Users and Admins',\&listbox,'3','(.*)',undef, 'If a resend request is made by any of the selected users, the original sender of the resent mail will be added to whitelist.<br /><hr />
2165  <div class="menuLevel1">Notes On Blocking Reports</div>
2166  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/blockreports.txt\',3);" />'],
2167
2168[0,0,0,'heading','Email Interface <a href="http://sourceforge.net/apps/mediawiki/assp/index.php?title=How_do_i_use_the_e-mail_interface target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="email" /></a>'],
2169['EmailInterfaceOk','Enable Email Interface',0,\&checkbox,1,'(.*)',undef,
2170  'Checked means that you want ASSP to intercept and parse mails to the below usernames at any domain which is listed in localDomains. You can use \'assp.local\' or \'@assp-notspam.org\' because they are automatically included.  The interface accepts mails only from local senders coming from acceptAllMail or through relayPort or from authenticated SMTP connections or from addresses listed in EmailSenderOK. <br /><hr />
2171  <div class="menuLevel1">Notes On Email Interface</div>
2172  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/emailinterface.txt\',3);" />'],
2173['EmailSenderLocalAddress','Accept Emails (Reports) from local addresses always*',0,\&checkbox,'1','(.*)',undef,
2174  'Allow local addresses to send to the email
2175interface even if they are not local or authenticated. This overwrites the standard behaviour, which allows only reqests from local or authenticated users.'],
2176['EmailAdminReportsTo','Admin Mail Address',40,\&textinput,'','(.*@.*)?',undef,
2177  'Warnings/infos  will be sent to this address. For example: admin@domain.com'],
2178
2179 ['EmailFrom','From Address for Reports',40,\&textinput,'<postmaster@assp-notspam.org>','(.*)',undef,
2180  'Email sent from ASSP acknowledging your submissions will be sent from this address. For example: <postmaster@assp-notspam.org>'],
2181
2182['EmailHelp','Help Address',20,\&textinput,'assp-help','(.*)@?',undef,
2183  'Any mail sent by local/authenticated users to this username will be interpreted as a request for help. Do not put the full address here, just the user part. For example: assp-help. The user would then send to assp-help@anylocaldomain.com.'],
2184['EmailAdmins','Email Admins* ',120,\&textinput,'','(.*)','ConfigMakeSLRe',
2185  'Mail from any of these addresses can add/remove to/from redlist, spamlovers, noprocessing. May request an EmailBlockReport for a list of users. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com)'],
2186['EmailResendRequester','Blocked Email Resend Requester*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2187 'A list of local addresses, which are allowed to request a resend of blocked emails for other users, even they are not EmailAdmins . Leave this field blank (default), to do disable this feature.<br />
2188  This is usefull, if a user gets automatic generated BlockReports (e.g via BlockReportFile ) for a group of users and should be able to manage resends for them. Added here, the user is not allowed to request BlockReports for other users - in this case use EmailAdmins and EmailAdminDomains instead.<br />
2189  The resend is done to the recipient stored in the X-Assp-Intended-For: ( requires AddIntendedForHeader ) header field and the requester if the address was found in a TO: header filed. <br />
2190  Accepts specific addresses (user@domain.com), user parts (user).  Wildcards are supported (fribo*@domain.com).<br />
2191  For example: fribo*@thisdomain.com|jhanna ',undef,undef,'msg010120','msg010121'],
2192['EmailAdminDomains','Restrict Email Admins to Domains*',40,\&textinput,'','(file:.+)|','ConfigMakeEmailAdmDomRe',
2193  'Use this parameter to restrict users registered in EmailAdmins, EmailAdminReportsTo and EmailBlockTo to a list of domains or users, for which they can request BlockReports.<br />
2194  The file: option is required. Use the following syntax to define an entry (one per line):<br />
2195  EmailAdminAddress=>*@domain1,*@domain2 user@domain3 ...<br />
2196  [user@domain]=>*@domain1,*@domain2 user@domain3 ...<br />
2197  Wildcards are allowed to be used in the domain definition - like *@*.domain.tld - separate multiple domains by comma or space.<br />
2198  If a BlockReport is requested for a not allowed email address, the complete BlockReport request will be ignored.<br />
2199  If an EmailAdmins address is not registered in this parameter, he/she is able to request BlockReports for all domains.',undef,undef,'msg009710','msg009711'],
2200
2201
2202['EmailAdminsModifyBlackForAll','Modify Black For All Recipients*',40,\&textinput,'','(.*)','ConfigMakeSLRe',
2203  'ladmins in this list will automatically add/remove entries to Personal Blacklist using a wildcard (*) for the recipient which blocks a sender for all recipients.<br />
2204 Accepts specific addresses (user@domain.com), user parts (user).  Wildcards are supported (fribo*@domain.com).<br />'],
2205 ['EmailSenderOK','Accept Emails (Reports) from these external addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2206  'Allow these external domains/addresses to send to the email
2207interface. This overwrites the standard behaviour, which allows only reqests from local or authenticated users. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com)'],
2208['EmailSenderNotOK','Not Authorized Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2209  'Mail from any of these addresses are not accepted from Email Interface. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).'],
2210
2211
2212['EmailSenderIgnore','Ignore Not Authorized Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2213  'Mail from any of these addresses are not accepted from Email Interface, except "Help Report", "Analyze Report" and "Block Report/Resend". Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). The user will get not informed about the denied request.',undef,undef,'msg009390','msg009391'],
2214
2215['EmailSpam','Report Spam to this Address',20,\&textinput,'assp-spam','(.*)@?',undef,
2216  'Any mail sent or forwarded by local/authenticated users to this username will be interpreted as a report about a Spam that got through (counts 2x). Do not put the full address here, just the user part. For example: assp-spam. The user would then send to assp-spam@anylocaldomain.com.<br />
2217  This works best if the mails are reported as attachments or copied into a new mail (header and body), because forwarding the mail will remove the original header.
2218  You can sent multiple emails as attachments. Each attached email-file must have the extension defined in "maillogExt". In this case only the attachments will be processed. Multiple attachments get truncated to MaxBytesReports. To use this multi-attachment-feature an installed Email::MIME module in PERL is needed.','Basic'],
2219
2220['EmailHam','Report NotSpam to this Address',20,\&textinput,'assp-notspam','(.*)@?',undef,
2221  'Any mail sent or forwarded by local/authenticated users to this username will be interpreted as a good mail that was mistakenly listed as spam (counts 4x). Do not put the full address here, just the user part. For example: assp-notspam. The user would then send to assp-notspam@anylocaldomain.com<br />
2222This works best if the mails are reported as attachments or copied into a new mail (header and body) because forwarding the mail will remove the original header. You can sent multiple emails as attachments. Each attached email-file must have the extension defined in "maillogExt". In this case only the attachments will be processed. Multiple attachments get truncated to MaxBytesReports. To use this multi-attachment-feature an installed Email::MIME module in PERL is needed.','Basic'],
2223['MaxBytesReports','Error Max Bytes',10,\&textinput,20000,'(\d+)',undef,'How many bytes of an error report (EmailHam, EmailSpam) will ASSP look at. For example: 20000.'],
2224['EmailForwardReportedTo','Email Interface Forward Reports Destination',20,\&textinput,'','^((?:' . $HostPortRe . '(?:\|' . $HostPortRe . ')*)|)$',undef,
2225 'Host and Port to forward EmailSpam and EmailHam reports to - eg "10.0.1.3:1025".<br />
2226  If you use more than one assp instance and your users are reporting spam and ham mails to multiple or all of them, but only one (but not this instance) is doing the rebuildspamdb and the corpus folders are not shared between the instances,<br />
2227  define the "host:port" of the central assp (rebuild-) instance here. Every report to EmailSpam and EmailHam (but only these!) will be forwarded to the defined host(s) and NO other local action will be taken. If the forwarding to all defined hosts failes, the request will be processed localy. To define multiple hosts for failover, separte them by pipe (|).',undef,undef,'msg009930','msg009931'],
2228['EmailErrorsReply','Reply to Spam/NotSpam Reports','0:NO REPLY|1:REPLY TO SENDER|2:REPLY TO EmailErrorsTo|3:REPLY TO BOTH',\&listbox,1,'(.*)',undef,  ''],
2229['EmailErrorsTo','Send Copy of Spam/NotSpam Reports TO',40,\&textinput,'','(.*@.*)?',undef,
2230  'Email sent from ASSP acknowledging your submissions will be sent to this address. For example: admin@domain.com<br />'],
2231
2232['EmailErrorsModifyWhite','NotSpam Report will add to Whitelist ','0:disabled|1:add to whitelist|2:show whitelist',\&listbox,1,'(.*)',undef,
2233  'If set to \'add to whitelist\' NotSpam Reports will add email addresses to the Whitelist, Spam Reports will remove addresses from the Whitelist. If set to \'show whitelist\' Spam Reports will show if addresses are whitelisted. This works best if the mails are reported as attachments or copied into a new mail (header and body) because forwarding the mail will remove the original header.','Basic',undef,'msg005320','msg005321'],
2234
2235['EmailErrorsRemoveWhite','Spam Report will remove from Whitelist ','0:disabled|1:remove from whitelist|2:show whitelist',\&listbox,1,'(.*)',undef,
2236  'If set to \'remove from whitelist\' Spam Reports will remove addresses from the Whitelist. If set to \'show whitelist\' Spam Reports will show if addresses are whitelisted. This works best if the mails are reported as attachments or copied into a new mail (header and body) because forwarding the mail will remove the original header.','Basic',undef,'msg005320','msg005321'],
2237
2238['EmailWhitelistAdd','Add to Whitelist Address',20,\&textinput,'assp-white','(.*)@?',undef,
2239  'Any mail sent by local/authenticated users to this username will be interpreted as a request to add addresses to the whitelist.. <br />Do not put the full address here, just the user part. For example: assp-white. The user would then send to assp-white@anylocaldomain.com.
2240  ','Basic'],
2241['EmailWhitelistRemove','Remove from Whitelist Address',20,\&textinput,'assp-notwhite','(.*)@?',undef,
2242  'Any mail sent by local/authenticated users to this username will be interpreted as a request to remove addresses from the whitelist. <br />Do not put the full address here, just the user part.For example: assp-notwhite. The user would then send to assp-notwhite@anylocaldomain.com.
2243  ','Basic'],
2244['EmailWhiteRemovalAdminOnly','Allow  Whitelist Removals for Admins only ',0,\&checkbox,'','(.*)',undef,
2245  'Only the users defined in EmailWhitelistTo, EmailAdmins and EmailAdminReportsTo are able to remove addresses from the whitelist.'],
2246
2247['exportedWhiteDomains','Exported White Domains File ',40,\&textinput,'file:whitedomains.txt','(\S*)',undef, 'whitelisted entries for domains (*@domain) will be stored here.'  ],
2248['CleanWhitelistNow','Run CleanWhitelist Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will fill exportedWhiteDomains. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
2249['EmailWhitelistReply','Reply to Add to/Remove from Whitelist','0:NO REPLY|1:REPLY TO SENDER|2:REPLY TO EmailWhitelistTo|3:REPLY TO BOTH',\&listbox,1,'(.*)',undef,
2250  ''],
2251
2252['EmailWhiteRemovalToRed','Add  Whitelist Removals To Redlist ',0,\&checkbox,'','(.*)',undef,
2253  'Addresses which are removed from Whitelist via EmailWhitelistRemove will automatically be added to the Redlist. The address can only be added again to the Whitelist after it is removed from the Redlist.'],
2254['EmailWhitelistTo','Send Copy of Whitelist-Reports TO',40,\&textinput,'','(.*@.*)?',undef,
2255  'Email sent from ASSP acknowledging your submissions will be sent to this address. For example: admin@domain.com'],
2256['EmailRedlistAdd','Add to Redlist Address',20,\&textinput,'assp-red','(.*)@?',undef,
2257  'Any mail sent by local/authenticated users to this username will be interpreted as a request to add the sender address to the redlist. Only the users defined in EmailRedlistTo, EmailAdmins and EmailAdminReportsTo are able to define a list of email addresses in the mail body. <br /> Do not put the full address here, just the user part. For example: assp-red. The user would then send to assp-red@anylocaldomain.com.
2258  '],
2259['EmailRedlistRemove','Remove from Redlist Addresses',20,\&textinput,'assp-notred','(.*)@?',undef,
2260  'Any mail sent by local/authenticated users to this username will be interpreted as a request to remove the sender address from the redlist. Only the users defined in EmailRedlistTo, EmailAdmins and EmailAdminReportsTo are able to define a list of email addresses in the mail body. <br />
2261  Do not put the full address here, just the user part. For example: assp-notred. The user would then send to assp-notred@anylocaldomain.com.'],
2262['EmailRedlistReply','Reply to Add to/Remove from Redlist','0:NO REPLY|1:REPLY TO SENDER|2:REPLY TO EmailRedlistTo|3:REPLY TO BOTH',\&listbox,1,'(.*)',undef,
2263  ''],
2264['EmailRedlistTo','Send Copy of Redlist-Reports TO',40,\&textinput,'','(.*@.*)?',undef,
2265  'Email sent from ASSP acknowledging your submissions will be sent to this address. For example: admin@domain.com'],
2266
2267
2268['EmailErrorsModifyPersBlack','Spam/NotSpam Report will modify Personal Blacklist *',60,\&textinput,'*@*','(.*)','ConfigMakeSLRe',
2269  'Spam Reports will add email addresses to the Personal Blacklist, NotSpam Reports will remove addresses from the Personal Blacklist, if the report addresses match. If EmailAdminsModifyBlack is set an emailadminwill block a sender for all recipients<br />
2270  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (fribo*@domain.com).<br />
2271  Default is *@* , which matches all addresses.',undef,undef,'msg009610','msg009611'],
2272['EmailErrorsModifyNotPersBlack','Spam/NotSpam Report will not modify Personal Blacklist *',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2273  'Spam Reports will not add email addresses to the Personal Blacklist, NotSpam Reports will not remove addresses from the Personal Blacklist, if the report senders address matches. <br />
2274  Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). Wildcards are supported (fribo*@domain.com).'],
2275
2276
2277['EmailPersBlackAdd','Add to Personal BlackListed  Addresses',20,\&textinput,'assp-persblack','(.*)@?',undef,
2278  'Any mail sent by local/authenticated users to this username will be interpreted as a request to add the listed address(es) to the personal blackListed addresses. Do not put the full address here, just the user part. <br />
2279  For example: assp-persblack.<br />
2280  The add and remove is done via email-interface, by sending specific email addresses to \'EmailPersBlackAdd\'  and \'EmailPersBlackRemove\'.
2281
2282  A local user can force a complete report about all his personal black list entries by defining an email address that begins with \'reportpersblack\' in a remove or add request : eg: reportpersblack@anydomain.com or by sending an empty body.<br />
2283
2284  Only an admin can force a complete cleanup of all personal black entries for a specific email address for all local users - sending an email to \'EmailPersBlackRemove\' with the address followed by \',*\' in the body
2285  eg: address_to_remove@the_domain.foo,*
2286<input type="button" value=" Show Personal Blacklist" onclick="javascript:popFileEditor(\'persblack\',5);" />',undef,undef,'msg009110','msg009111'],
2287['EmailPersBlackRemove','Remove from Personal BlackListed Addresses',20,\&textinput,'assp-notpersblack','(.*)@?',undef,
2288  'Any mail sent by local/authenticated users to this username will be interpreted as a request to remove the listed address(es) from the personal blackListed addresses .<br />
2289  Do not put the full address here, just the user part.<br />
2290  For example: assp-notpersblack.<br />
2291  The add and remove is done via email-interface, by sending specific email addresses to \'EmailPersBlackAdd\'  and \'EmailPersBlackRemove\'.
2292  A local user can force a complete report about all his personal black list entries by defining an email address that begins with \'reportpersblack\' in a remove or add request : eg: reportpersblack@anydomain.com or by sending an empty body.<br />
2293  Only an admin can force a complete cleanup of all personal black entries for a specific email address for all local users - sending an email to \'EmailPersBlackRemove\' with the address followed by \',*\' in the body
2294  eg: address_to_remove@the_domain.foo,*
2295  <input type="button" value=" Show Personal Blacklist" onclick="javascript:popFileEditor(\'persblack\',5);" />',undef,undef,'msg009120','msg009121'],
2296
2297['EmailAnalyze','Request Analyze Report',20,\&textinput,'assp-analyze','(.*)@?',undef,
2298  'Any mail sent or forwarded by local/authenticated users to this username will be interpreted as a request for analyzing the mail. Do not put the full address here, just the user part. For example: assp-analyze','Basic'],
2299['EmailAnalyzeReply','Reply to Analyze Request','0:NO REPLY|1:SEND TO SENDER|2:SEND TO EmailAnalyzeTo|3:SEND TO BOTH',\&listbox,1,'(.*)',undef,''],
2300['EmailAnalyzeTo','Send Copy of Analyze-Reports',40,\&textinput,'','(.*@.*)?',undef,
2301  'A copy of the Analyze-Report will be sent to this address. For example: admin@domain.com'],
2302['DoAdditionalAnalyze','Spam and Ham Reports will trigger an additional Analyze Report ','0:NO ADDITIONAL REPORT|1:SEND TO SENDER|2:SEND TO EmailAnalyzeTo|3:SEND TO BOTH',\&listbox,0,'(.*)',undef,
2303  'Additional Analyze Report will be generated for Spam and Ham Reports. Setting the TO Address accordingly and choosing <b>EmailAnalyzeTo</b> will send the Analyze Report to the admin only.'],
2304
2305['EmailSenderNoReply','Do Not Reply To These Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2306  'Email sent from ASSP acknowledging your submissions will not be sent to these addresses. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).<br /><hr />
2307  <div class="menuLevel1">Notes On Email Interface</div>
2308  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/emailinterface.txt\',3);" />'],
2309
2310
2311[0,0,0,'heading','File Paths'],
2312['base','Directory Base',40,\&textnoinput,'.','',undef,'All paths are relative to this folder.<br />
2313  <b>Note: Display only.</b>'],
2314['spamlog','Spam Collection',40,\&textinput,'spam','(\S+)',undef,'The folder to save the collection of spam emails. This directory will be used in building the spamdb. For example: spam'],
2315['notspamlog','Not-spam Collection',40,\&textinput,'notspam','(\S+)',undef,'The folder to save the collection of not-spam emails. This directory will be used in building the spamdb. For example: notspam'],
2316['incomingOkMail','OK Mail',40,\&textinput,'okmail','(.*)',undef,'The folder to save non-spam (message ok). These are messages which are considered as HAM, but are not stored in the standard HAM folder because of our policy to use only confirmed HAM messages (whitelisted or local) for SpamDB. If you want to keep copies of ok mail then put in a directory name. This directory will not be used in building the spamdb.'],
2317['discarded','Discarded Mails',40,\&textinput,'discarded','(.*)',undef,'The folder to save (virtually) discarded messages. In earlier versions of ASSP messages were physically deleted if the settings were set so. The newly introduced resend-function made it necessary to keep these messages and store them in "discarded".'],
2318['viruslog','Attachment/Virus Collection',40,\&textinput,'quarantine','(.*)',undef,
2319  'The folder to save rejected attachments and virii. Leave this blank to not save these files. If you want to keep copies of rejected content then put in a directory name. This directory will not be used in building the spamdb. For example: quarantine'],
2320['correctedspam','False-negative Collection',40,\&textinput,'errors/spam','(\S+)',undef,
2321  'Spam that got through -- counts 2 times. This directory will be used in building the spamdb. For example: errors/spam'],
2322['correctednotspam','False-positive Collection',40,\&textinput,'errors/notspam','(\S+)',undef,
2323  'Good mail that was listed as spam, counts 4 times. This directory will be used in building the spamdb. For example: errors/notspam'],
2324
2325['resendmail','try to resend this files',40,\&textinput,'resendmail','(\S+)',undef,
2326  'ASSP will try to resend the files in this directory to the original recipient. The files must have the "maillogExt" extension and must have the SMTP-format. ASSP will try to send every  file up to ten times (with 5 minutes delay). If the resend fails ten times, the file will be renamed to *.err, on success the file will be deleted!<br />
2327For example: resendmail. This requires an installed Email::Send module in PERL.'],
2328['maillogExt','Extension for Mail Files',20,\&textinput,'.eml','(\S*)',undef,
2329  'Enter the file extension (include the period) you want appended to the mail files in the mail collections. For Example: .eml'],
2330['spamdb','Spam Bayesian Database File',40,\&textinput,'spamdb','(\S+)',undef,'The output file from rebuildspamdb.pl.<br />
2331  Write "mysql" to use a MySQL table instead of a local file, in this case you need to edit the MySQL parameters starting with myhost. <hr /><div class="menuLevel1">Last Run Rebuildspamdb</div><input type="button" value="Last Run Rebuildspamdb" onclick="javascript:popFileEditor(\'rebuildrun.txt\',5);" />'],
2332
2333['whitelistdb','E<!--get rid of google autofill-->mail Whitelist Database File',40,\&textinput,'whitelist','(\S+)',undef,'The file with the whitelist.<br />
2334  Write "mysql" to use a MySQL table instead of a local file, in this case you need to edit the MySQL parameters starting with myhost.'],
2335['redlistdb','E<!--get rid of google autofill-->mail Redlist Database File',40,\&textinput,'redlist','(\S+)',undef,'The file with the redlist.<br />
2336
2337  Write "mysql" to use a MySQL table instead of a local file, in this case you need to edit MySQL parameters starting with myhost.<br />The Redlist serves several purposes:
2338<br />- the Redlist is a list of addresses that cannot contribute to the
2339whitelist. For example, if someone goes on a vacation and
2340turns on their autoresponder, put them on the redlist until
2341they return. Then as they reply to every spam they receive they won\'t
2342corrupt your non-spam collection or whitelist. There is also a redRe available where you can put some text from standard out of office messages, to automatically add a local user to the redlist when they send the out of office message, for example: \[autoreply\]
2343<br />- Redlisted addresses will not be added to the Whitelist.
2344This is used by EmailWhiteRemovalToRed to prevent repeated adding to the whitelist.
2345So if somebody whitelisted ebay@ebay.com you will surely remove that from the whitelist, but you can also be sure, that somebody will add that address again. Putting ebay@ebay.com into the redlist will give that pause.
2346<br />- Redlisted messages will not be stored in the
2347SPAM/NOTSPAM-collection. '],
2348
2349['ldaplistdb','LDAP/VRFY Cache',40,\&textnoinput,'ldaplist','(\S*)',undef,'The file with the LDAP/VRFY-cache. <br /> <input type="button" value=" LDAP/VRFY Cache" onclick="javascript:popFileEditor(\'ldaplist\',5);" />.'],
2350['ldapnotfounddb','LDAP/VRFY Not Found Cache',40,\&textnoinput,'ldapnotfound','(\S*)',undef,'The file with the LDAP/VRFY-NotFound-Cache, see also LDAPShowNotFound.<br /> <input type="button" value=" LDAP/VRFY Not Found Cache" onclick="javascript:popFileEditor(\'ldapnotfound\',5);" />'],
2351['droplist','Drop also Connections from these IP\'s*',40,\&textinput,'file:files/droplist.txt','(.*)','ConfigMakeIPRe','Automatically downloaded (http://www.spamhaus.org/drop/drop.lasso) list of IP\'s which should be blocked right away. ',undef,'7','msg005750','msg005751'],
2352['delaydb','Delaying Database',40,\&textinput,'delaydb','(\S*)',undef,'The file with the delay database.<br />Write "mysql" to use a MySQL table instead of a local file, in this case you need to edit the MySQL parameters starting with myhost.<br /> <input type="button" value=" Show Delay DB" onclick="javascript:popFileEditor(\'delaydb\',5);" />'],
2353['pbdb','PenaltyBox Database',40,\&textnoinput,'pb/pbdb','(\S*)',undef,'The directory/file with the penaltybox database files. For removal of entries from PenaltyBlackBox use <a target="main" href="./#noPB">noPB</a>.
2354 For removal of entries from WhiteBox noPBwhite. For  whitelisting IP addresses use noProcessingIPs. For blacklisting IP addresses use denySMTPConnectionsFrom.
2355 <br /><input type="button" value=" Show BlackBox" onclick="javascript:popFileEditor(\'pb/pbdb.black.db\',4);" /><input type="button" value="Show White Box" onclick="javascript:popFileEditor(\'pb/pbdb.white.db\',4);" />
2356 '],
2357['persblackdb','Personal Blacklist Database File',40,\&textnoinput,'persblack','(\S*)',undef,'The file with the personal blacklist. The check of the personal black list is done shortly after the RCPT TO: command. This command will be rejected if an entry is found - any other setting except send250OK and send250OKISP will be ignored.<input type="button" value=" Show Personal Blacklist" onclick="javascript:popFileEditor(\'persblack\',5);" />
2358',undef,undef,'msg009100','msg009101'],
2359['griplist','GReyIPlist Database',40,\&textinput,'griplist','(\S*)',undef,'The file with the current GRey-IP-List  database -- make this blank if you don\'t use it.',undef,undef,'msg005730','msg005731'],
2360['gripValencePB','GRIP Score',3,\&textinput,20,'(\d+)',undef, 'MessageScoring',undef,undef,'msg002980','msg002981'],
2361['myhost','MySQL hostname or IP',40,\&textinput,'','(\S*)',undef,
2362  'You need <a
2363  href="http://search.cpan.org/~lds/Tie-DBI-1.02/lib/Tie/RDBM.pm"
2364  rel="external">Tie::RDBM</a> to use MySQL instead of local files.<br />
2365  This way you can share whitelistdb, delaydb and redlistdb between servers if "mysql" is written into their file-path.'],
2366['mydb','MySQL database name',40,\&textinput,'','(\S*)',undef,
2367  'This database must exist before starting ASSP,
2368  necessary tables will be created automatically into this database'],
2369
2370['myuser','MySQL username',40,\&textinput,'','(\S*)',undef,
2371  'This user must have CREATE privilege on the configured database in order for tables to be created automatically'],
2372['mypassword','MySQL password',40,\&textinput,'','(\S*)',undef,''],
2373
2374['logfile','ASSP Logfile',40,\&textinput,'logs/maillog.txt','(\S*)','ConfigChangeLogfile',
2375  'Blank if you don\'t want a log file. Change it to maillog.log if you don\'t want auto rollover.
2376  NOTE: Changing this field requires restarting ASSP before changes take effect.'],
2377
2378['pidfile','PID File',40,\&textinput,'pid','(\S*)',undef,'Blank to skip writing a pid file. *nix users need pid files.
2379<br /> You have to restart assp before you get a pid file in the new location.<br /><hr /><div class="menuLevel1">Notes On File Path</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/filepath.txt\',3);" />'],
2380
2381[0,0,0,'heading','Copy Spam/Ham'],
2382['sendAllSpam','Copy Spam and Send to this Address',60,\&textinput,'','(.*)',undef,
2383 'ASSP will deliver a copy of spam emails to this address if the collection mode in the collection section is set to do so (eg. baysSpamLog ). For example: spammonitor@example.com. The address can be different depending on the recipient. The literal USERNAME (case sensitive) is replaced by the user part of the recipient, the literal DOMAIN (case sensitive) is replaced by the domain part of the recipient. For example: USERNAME@Spam.DOMAIN, USERNAME+Spam@DOMAIN, spammonitor@DOMAIN','Basic'],
2384 ['sendAllDiscard','Copy Discarded and Send to sendAllSpam',0,\&checkbox,'1','(.*)',undef,
2385 'ASSP will deliver a copy of discarde emails to sendAllSpam'],
2386 ['ccSpamInDomain','Copy Spam and Send to this Address per Domain*',60,\&textinput,'','(.*)','configUpdateCCD',
2387 'ASSP will deliver an additional copy of spam emails of a domain to this address - if the domain of the recipient-address is matched. For example: monitorspam@example1.com|monitor@example2.com.'],
2388['sendAllDestination','SMTP Destination for Spam Copies',60,\&textinput,'','(\S*)',undef,
2389 'Port to connect to when  Spam messages are copied. If blank they go to the main smtpDestination. eg "10.0.1.3:1025".'],
2390
2391['ccSpamFilter','Copy Spam to these Recipients Only*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2392 'Restricts Copy Spam to these recipients. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).'],
2393['ccSpamAlways','Copy Spam to these Recipients always*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2394 'Copy Spam to these recipients regardless of collection mode. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).  Wildcards are supported (fribo*@example.com).'],
2395['ccSpamNeverRe','Do Not Copy Spam Regular Expression*',60,\&textinput,'','(.*)','ConfigCompileRe',
2396 'Never Copy Spam regardless of collection mode. Put anything here to identify messages which should not be copied.'],
2397['ccMaxScore','Do Not Copy Messages Above This MessageTotal score',3,\&textinput,'','(\d*)',undef,
2398 'Messages whose score exceeds this threshold will not be copied.  For example: 75'],
2399['ccMaxBytes','Cut Copied Spam to MaxBytes Lenght',0,\&checkbox,1,'(.*)',undef,
2400 'MaxBytes will be used to cut off copied mails, thereby reducing the load considerably.'],
2401['spamSubjectCC','Prepend Spam Subject to Copied Spam',0,\&checkbox,'','(.*)',undef,
2402 'If set spamSubject gets prepended to the subject of the copied message.'],
2403['spamTagCC','Prepend Spam Tag to Copied Spam',0,\&checkbox,1,'(.*)',undef,'The check which caused the spam detection will be prepended to the subject of the message. For example: [DNSBL]'],
2404['sendAllHamDestination','SMTP Destination for Ham Copies',60,\&textinput,'','(\S*)',undef,
2405 'Port to connect to when  Ham messages are copied. If blank they go to sendAllDestination. eg "10.0.1.3:1025"'],
2406['sendHamInbound','Copy Incoming Ham and Send to this Address',40,\&textinput,'','(.*)',undef, 'If you put an address in this box  ASSP will forward a copy of notspam messages from outside to this address. The literal USERNAME is replaced by the user part of the recipient, the literal DOMAIN is replaced by the domain part of the recipient. For example: archiv@example.com, USERNAME@mybackup.domain, catchallforthis@DOMAIN'],
2407['sendHamOutbound','Copy Outgoing Ham and Send to this Address',40,\&textinput,'','(.*)',undef, 'If you put an address in this box ASSP will forward a copy of outgoing notspam messages to this address. The literal USERNAME is replaced by the user part of the recipient, the literal DOMAIN is replaced by the domain part of the recipient. For example: archiv@example.com, USERNAME@mybackup.domain, catchallforthis@DOMAIN'],
2408['ccHamFilter','Copy Ham Filter*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2409 'Copy Not-Spam to these addresses only. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).'],
2410['ccnHamFilter','Do Not Copy Ham Filter*',60,\&textinput,'','(.*)','ConfigMakeSLRe',
2411 'Do Not Copy Ham to these addresses. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). Wildcards are supported (fribo*@example.com).',undef,undef,'msg000460','msg000461'],
2412['ccMailReplaceRecpt','ccMail Recipient Replacement',0,\&checkbox,'','(.*)',undef,'The recipient replacement (ReplaceRecpt) rules from the "Recipients/Local Domains" section, will be used to replace ccMail recipients. For example: sendHamInbound = USERNAME@yourspamdomain.lan - in this case you are able to detect the target domain "yourspamdomain.lan" in a rule and you can replace the recipient/domain depending on its values and/or on the senders address.<br />
2413<hr /><div class="menuLevel1">Notes On CC Messages</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/copymail.txt\',3);" />',undef,undef,'msg000460','msg000461'],
2414[0,0,0,'heading','Collecting'],
2415['spamaddresses','SpamBucket Addresses* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2416  'Mail to any of these addresses are always spam and will contribute to the spam-collection unless from someone on the whitelist. Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com). '],
2417
2418
2419['noCollecting','Do Not Collect Messages from/to these Addresses*',80,\&textinput,'','(.*)','ConfigMakeSLRe','Accepts specific addresses (user@example.com), user parts (user) or entire domains (@example.com).'],
2420['noCollectRe','Do Not Collect Messages - Content Based*',60,\&textinput,'','(.*)','ConfigCompileRe','',undef,undef,'msg008930','msg008931'],
2421['UseSubjectsAsMaillogNames','Use Subject as Maillog Names',0,\&checkbox,'1','(.*)','ConfigChangeUSAMN',
2422  'You can turn this on to help you manually identify mail in your spam and non-spam collections.',undef,undef,'msg006100','msg006101'],
2423['MaxAllowedDups','Max Number of Duplicate File Names',5,\&textinput,5,'(\d+)','ConfigChangeMaxAllowedDups',
2424  'The maximum number of logged files with the same filename (subject) that are stored in the spam folder (spamlog), if UseSubjectsAsMaillogNames is selected. A low value reduces the number of possibly duplicate mails, assuming that mails with the same subject will have the same content. A value of 0 disables this feature. If this number of files with the same filename is reached, new files will be stored in the \'discarded\' folder, which has to be defined ( in addition to spamlog ) for this feature to work.', undef, undef,'msg008660','msg008661'],
2425['AllowedDupSubjectRe','Regular Expression to Allow Unlimited Duplicates *',80,\&textinput,'','(.*)','ConfigCompileRe','Messages with subject matching this regular expression will be collected regardless of the setting in MaxAllowedDups .'],
2426
2427
2428
2429['MaxFileNameLength','Max Length of File Names',10,\&textinput,30,'(\d+)',undef,
2430  'The maximum character count that is used from the mail subject to build the file name of the logged file, if UseSubjectsAsMaillogNames is selected. This could be usefull, if your mail clients having trouble to build the resend file name (right button - URL) correctly in block reports. Every non printable character will be replaced by a 4 byte string in this link.'],
2431['DoNotCollectRed','Do Not Collect Red Mails',0,\&checkbox,"",'(.*)',undef,
2432  'Mails matching redRe will not be stored in the collection folders.'],
2433['KeepWhitelistedSpam','Do Not Delete Whitelisted Spams',0,\&checkbox,'','(.*)',undef,
2434  'Mails matching  Whitelist will not be removed from the Spam folder.'],
2435['DoNotCollectBounces','Do Not Collect Bounced Mails',0,\&checkbox,"",'(.*)',undef,
2436  'Mails matching BounceSenders will not be collected.'],
2437['NoMaillog','Don\'t Collect Mail',0,\&checkbox,'','(.*)',undef,
2438  'Check this if you are using Whitelist-Only and do not care to save mail to build the Bayesian database.'],
2439
2440['MaxFiles','Max Files',10,\&textinput,14000,'(\d+)',undef,
2441  'Maximum number of files to keep in each collection (spam and nonspam)
2442'],
2443
2444['MaxBytes','Max Bytes',10,\&textinput,10000,'(\d+)',undef,
2445  'How many bytes of the message will ASSP look at? Mails stored in the collecting folders will be truncated to this size if StoreCompleteMail is not set.'],
2446['StoreCompleteMail','Store the Complete Mail','0:disabled|100000:up to 100 kByte|500000:up to 500 kByte|1000000:up to 1 MByte|10000000:up to 10 MByte|999999999:no limit',\&listbox,500000,'(.*)',undef,
2447  'If set, ASSP will analyze only MaxBytes of the mail, but  will store the complete mail up to the selected limit. This could be usefull for example, if you want to resend blocked messages. Be carefull using this option, your disk could be filled up very fast!'],
2448
2449['baysNonSpamLog','OK Mail','0:no collection|2:notspam folder|4:okmail folder',\&listbox,4 ,'(.*)',undef,'Where to store non spam (message ok) messages. These are messages which are considered as HAM, but should not stored in the standard HAM folder because of our policy to use only confirmed HAM messages (whitelisted or local) for SpamDB. Set incomingOkMail accordingly if you choose \'okmail folder\'.'],
2450['NonSpamLog','Non Spam','0:no collection|2:notspam folder|4:okmail folder|6:discard folder',\&listbox,2,'(.*)',undef,'Where to store whitelisted/local non spam messages.'],
2451['SpamLog','Store Spam','0:disabled|1:enabled',\&listbox,1,'(.*)',undef,'Set this to \'disabled\' if you do not want to store any Spam regardless of settings in. Default: enabled (store in folder spamlog ).',undef,undef,'msg006230','msg006231'],
2452['noProcessingLog','NoProcessing Non Spam','0:no collection|2:notspam folder|4:okmail folder|6:discard folder',\&listbox,4,'(.*)',undef,'Where to store noprocessing non spam messages.',undef,undef,'msg006240','msg006241'],
2453
2454['AttachLog','Rejected Attachments','0:no collection and no sendAllSpam|5:attachment folder|6:discard folder|7:discard folder and sendAllSpam',\&listbox,0,'(.*)',undef,'Where to store rejected mail+attachments.'],
2455['SpamVirusLog','Virus Infected','0:no collection and no sendAllSpam|5:quarantine|6:discard folder|7:discard folder and sendAllSpam',\&listbox,5,'(.*)',undef,'Where to store virus infected messages. '],
2456['spamBombLog','SpamBombs','0:no collection|1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,7,'(.*)',undef,'Where to store spam bombs -> DoBombHeaderRe, DoBombRe, DoBlackRe.'],
2457
2458['DenyIPLog','Blacklisted IP','0:no collection and no sendAllSpam|1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store blacklisted IP messages.'],
2459
2460
2461['invalidHeloLog','Invalid Helos , Forged Helos, Blacklisted Helos','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,7,'(.*)',undef,'Where to store invalid helo messages.'],
2462['spamBucketLog','Spam Collect Addresses','0:no collection and no sendAllSpam|1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,1,'(.*)',undef,'Where to store emails addressed to Spam Collect Addresses.'],
2463['baysSpamLog','Bayesian Spams','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,7,'(.*)',undef,'Where to store Bayesian spam messages.'],
2464
2465['RBLFailLog','DNSBL Failures','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store DNSBL Failure spam messages.'],
2466['SPFFailLog','SPF/ SRS Failures','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store SPF / SRS Failure spam messages.'],
2467['URIBLFailLog','URIBL Failures','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store URIBL Failure spam messages.'],
2468
2469
2470['invalidSenderLog','Invalid Sender','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,0,'(.*)',undef,'Where to store messages from a local domain with an unknown userpart.'],
2471['spamSBLog','Blocked Country - DoCountryBlocking, DoOrgBlocking','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store messages from a blocked country.'],
2472['spamMSLog','Message Limit Blocks - DoPenaltyMessage','1:spam folder|3:spam folder and sendAllSpam|6:discard folder|7:discard folder and sendAllSpam',\&listbox,3,'(.*)',undef,'Where to store Message Scoring Limit rejected messages. '],
2473
2474['BackLog','Backscatter check failed','1:spam folder|3:spam folder &amp; sendAllSpam|6:discard folder|7:discard folder &amp; sendAllSpam',\&listbox,6,'(.*)',undef,'Where to store FBMTV rejected messages. '],
2475
2476
2477[0,0,0,'heading','Logging'],
2478
2479['Notify','Notification Email To',80,\&textinput,'','(.*)',undef,
2480  'Email address(es) to which you want ASSP to send a notification email, if a matching log entry ( NotifyRe , NoNotifyRe ) is found. Separate multiple entries by "|". This requires an installed Email::Send module in PERL.'],
2481['NotifyRe','Do Notify, if log entry matches*',60,\&textinput,'','(.*)','ConfigCompileNotifyRe','Regular Expression to identify loglines for which a notification message should be send.<br />
2482  usefull entries are:<br />
2483
2484  autoupdate: - to get informed about an autoupdate of the running script<br />
2485  adminupdate: - for config changes<br />
2486  admininfo: - for admin information<br />
2487  option list file: - for option file reload<br />
2488  error: - for any error<br />
2489  restart - to detect a ASSP restart<br />
2490  Admin connection - for GUI logon<br />
2491  You may define a comma separated list (after \'=>\') of recipients in every line, this will override the default recipient defined in \'Notify\'. For example: adminupdate=>user1@yourdomain.com,user2@yourdomain.com.<br />
2492  As third parameter after a second (\'=>\') you can define the subject line for the notification message.<br />
2493  for example: adminupdate:=>user1@yourdomain.com,user2@yourdomain.com=>configuration was changed<br />
2494  or: adminupdate:=>=>configuration was changed.'],
2495['NoNotifyRe','Do NOT Notify, if log entry matches*',60,\&textinput,'','(.*)','ConfigCompileRe','Regular Expression to identify loglines for which no notification message should be send.'],
2496['noLoggingIPs','Don\'t Log these IPs*',40,\&textinput,'','(\S*)','ConfigMakeIPRe',
2497  'Enter IP addresses that you don\'t want to be logged, separated by pipes (|).<br />
2498  This can be IP address of the SMTP service monitoring agent. For example: 145.145.145.145|145.146.','','7'],
2499['noLoggingRe', 'Regular Expression to Identify NoLog-Mails*',80,\&textinput,'','(.*)','ConfigCompileRe',
2500 'Put anything here to identify mails that you don\'t want to be logged.'],
2501['noLogLineRe', 'Regular Expression to Suppress Log-Messages*',80,\&textinput,'max errors|collect','(.*)','ConfigCompileRe',
2502 "Put anything here to identify log messages that you want to be suppressed. For example: max errors|collect"],
2503['allLogRe', 'Regular Expression to Identify Messages from/to Problematic Addresses *',80,\&textinput,'','(.*)','ConfigCompileRe',
2504 "Put anything here to identify mails from/to addresses you want to look at for problem solving. Mails identified will also be set to StoreCompleteMail."],
2505
2506['subjectStart','Subject Start Delimiter',2,\&textinput,'[','(.*)',undef,'Start delimiter of subject in log '],
2507['subjectEnd','Subject End Delimiter',2,\&textinput,']','(.*)',undef,'End delimiter of subject in log'],
2508['regexLogging','Regex Match logging','0:nolog|1:standard|2:verbose',\&listbox,0,'(.*)',undef,'Show matching regex in log. '],
2509
2510['ipmatchLogging','IP Matches Logging',0,\&checkbox,'','(.*)',undef,
2511  'Enables logging of IP addresses matches in the maillog. Will show a comment instead of the range if there is text after the IP ranges (and before any numbersign)  eg. 182.82.10.0/24 AOL',undef],
2512['slmatchLogging','Logging Address Matches',0,\&checkbox,'','(.*)',undef,
2513  'Enables logging of address matches in the maillog.',undef],
2514
2515['uniqueIDPrefix','Prepend Unique ID logging',10,\&textinput,'m-','(.*)',undef,
2516  'Prepend ID. For example: m1-'],
2517
2518['tagLogging','Spam Tag Logging',0,\&checkbox,1,'(.*)',undef,'Add spam tag to log.'],
2519['ExceptionLogging','Timeout Exception Logging',0,\&checkbox,'','(.*)',undef,''],
2520['FromLogging','Empty MailFrom will be set to From:',0,\&checkbox,1,'(.*)',undef,''],
2521['replyLogging','SMTP Status Code Reply Logging','0:disabled|1:enabled - exclude [123]XX|2:enabled - all',\&listbox,0 ,'(\d*)',undef,undef,undef,undef,'msg006660','msg006661'],
2522
2523['expandedLogging','Logging Records include IP & MailFrom',0,\&checkbox,1,'(.*)',undef,''],
2524
2525['sysLog','SYSLOG Centralized Logging',0,\&checkbox,'','(.*)',undef,'Enables logging to UNIX Syslog. Needs Sys::Syslog for local (UNIX/LINUX) logging or Net::Syslog for Windows or Network logging.'],
2526['sysLogPort','Syslog Port (UDP)',5,\&textinput,'514','([\d\.]+)',undef,
2527  'Port for Syslog logging with Net::Syslog.'],
2528['SysLogFac','Syslog Facility',40,\&textinput,'mail','(\S*)',undef,
2529  'Syslog Facility. Valid are kern, user, mail, daemon, auth, syslog, lpr, news, uucp, cron, authpriv, ftp, local0, local1, local2, local3, local4, local5, local6'],
2530['sysLogIp','Syslog IP',40,\&textinput,'127.0.0.1','(\S*)',undef,
2531  'IP Address of your Syslog Daemon for Syslog logging with Net::Syslog.'],
2532['asspLog','ASSP local logging',0,\&checkbox,'1','(.*)',undef,'ASSP manages local logging. The logs <a href="./#logfile">are stored</a> inside the directory where ASSP is installed. This is needed if you want to use any of the "Block Reporting" and "View Maillog Tail" features like searching, deleting, moving, resending of messages.'],
2533['LogRollDays','Roll the Logfile How Often?',5,\&textinput,'1','([\d\.]+)',undef,
2534  'ASSP closes and renames the log file after this number of days.'],
2535['MaxLogAge','Max Age of Logfiles',10,\&textinput,60,'(\d+)',undef,
2536  'The maximum file age in days of logfiles. If a logfile is older than this number in days, the file will be deleted. A value of 0 disables this feature and no logfile will be deleted because of its age.'],
2537['LogNameDate','Date Format in LogfileName',30,\&textinput,'YY-MM-DD','(.*)',undef,'Use this option to set the date-format in the logfile-name. The default value is \'YY-MM-DD\'. The following replacements will be done:<br />
2538 YYYY - year four digits<br />
2539 YY - year two digits<br />
2540 MMM - month three characters - like Oct Nov Dec<br />
2541 MM - month numeric two digits<br />
2542 DD - day numeric two digits<br />
2543 DDD - day three characters - like Mon Tue Fri'],
2544['LogDateFormat','Date/Time Format in LogDate',30,\&textinput,'MMM-DD-YY hh:mm:ss','((?:(?:MM|MMM|DD|DDD|YY|YYYY)(?:[\_\-\. ]|)){3}(?:[\-\_ ]*)(?:(?:hh|mm|ss)(?:[\.:\-\_]|)){3})',undef,'Use this option to set the logdate. The default value is \'MMM-DD-YY hh:mm:ss\'. The following (case sensitive !) replacements will be done:<br /><br />
2545 YYYY - year four digits<br />
2546 YY - year two digits<br />
2547 MMM - month three characters - like Oct Nov Dec<br />
2548 MM - month numeric two digits<br />
2549 DDD - day three characters - like Mon Tue Fri<br />
2550 DD - day numeric two digits<br />
2551 hh - hour two digits<br />
2552 mm - minute two digits<br />
2553 ss - second two digits<br /><br />
2554 <span class="positive">A value has to be defined for every part of the date/time. Allowed separators in date part are \'_ -.\' - in time part \'-_.:\' .</span>'],
2555['LogDateLang','Date/Time Language','0:English|1:FranÁais|2:Deutsch|3:EspaÒol|4:PortuguÍs|5:Nederlands|6:Italiano|7:Norsk|8:Svenska|9:Dansk|10:suomi|11:Magyar|12:polski|13:Romaneste',\&listbox,0,'(.*)',undef,
2556  'Select the language for the day and month if LogDateFormat contains DDD and/or MMM.',undef,undef,'msg008700','msg008701'],
2557['enableWORS','Windows Output Record Separator',0,\&checkbox,'','(.*)','ConfigChangeWors',
2558  'Checked means write CRLF to the end of the logfile instead of the standard LF. This can only be used if LogCharset is set to \'System Default\'.'],
2559['silent','Silent Mode',0,\&checkbox,'','(.*)',undef,
2560  'Checked means don\'t print log messages to the console. '],
2561['debug','General Debug Mode',0,\&checkbox,'','(.*)',\&ConfigDEBUG,
2562  'Checked sends debugging info to a .dbg file.
2563  Leave this unchecked unless there is a program error you are trying to track down.'],
2564['DebugRollTime','Roll the Debugfile How Often?',5,\&textinput,'1800','([\d\.]+)',undef,
2565  'ASSP closes and opens a new debug file after this number of seconds.'],
2566['Win32Debug','Win32 OutputDebugString',0,\&checkbox,'','(.*)',undef,'Make Win32 OutputDebugString available. Needs Win32::API::OutputDebugString'],
2567
2568['IgnoreMIMEErrors','Ignore MIME Errors',0,\&checkbox,1,'(.*)',undef,'Errors, based on wrong email MIME contents, will not be written to log!'],
2569['ConTimeOutDebug','Connection Timeout Debug Mode',0,\&checkbox,'','(.*)',undef,'Select to debug SMTP connections that are running into timeout!'],
2570
2571
2572['RegExLength','RegEx Length in Log',2,\&textinput,32,'(.*)',undef,
2573  'Defines how many bytes of a matching Regular Expression will be shown in the log<br />
2574  Some matching Regular Expressions are too long for one line. Default: 32'],
2575['sendNoopInfo','Send NOOP Info',0,\&checkbox,'','(.*)',undef,
2576  'Checked means you want ASSP to send a "NOOP Connection from $ip" message to your SMTP server.
2577  <br /><hr />
2578  <div class="menuLevel1">Notes On Logging</div>
2579  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/logging.txt\',3);" />'],
2580
2581[0,0,0,'heading','LDAP Setup '],
2582['LDAPLog','Enable LDAP logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
2583  ''],
2584['DoLDAP','Do LDAP lookup for valid local addresses ',0,\&checkbox,'','(.*)',undef,'Check local addresses against an LDAP database before accepting the message.<br />Note: Checking this requires filling in the other LDAP parameters like LDAPHost.<br /><a http://www.selfadsi.de/ldap-filter.htm">LDAP-Filter</a>'],
2585['LDAPHost','LDAP Host(s) <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=LDAP" target=wiki><img height=12 width=12 src="' . $wikiinfo . '" alt="LDAP" /></a>',80,\&textinput,'localhost','(\S*)','updateLDAPHost','Enter the DNS-name(s) or IP address(es) of the server(s) that run(s) the <a href="http://ldap.perl.org/FAQ.html">LDAP</a> database. Second entry is backup. For example: localhost. Separate entries with pipes: LDAP-1.domain.com|LDAP-2.domain.com' ],
2586['DoLDAPSSL','Use SSL with LDAP (ldaps)','0:no|1:SSL|2:TLS',\&listbox,'0','(.*)',undef,'ASSP will use \'ldaps (SSL port 636)\' instead of ldap (port 389) or \'ldaps (TLS over port 389)\'. The Perl module <a href="http://search.cpan.org/search?query=IO::Socket::SSL" rel="external">IO::Socket::SSL</a> must be installed to use SSL or TLS!',undef,undef,'msg007220','msg007221'],
2587['LDAPtimeout','LDAP Query Timeout',2,\&textinput,15,'(\d+)',undef,'Timeout when connecting to the remote server.'],
2588['LDAPLogin','LDAP Login',80,\&textinput,'','(.*)',undef,'Most LDAP servers require a login and password before they allow queries.<br />Enter the DN specification for a user with sufficient permissions here.<br />For example: cn=Administrator,cn=Users,DC=yourcompany,DC=com'],
2589['LDAPPassword','LDAP Password',20,\&textinput,'','(.*)',undef,'Enter the password for the specified LDAP login here.'],
2590['LDAPVersion','LDAP Version',1,\&textinput,3,'(\d+)',undef,'Enter the version for the specified LDAP here.'],
2591
2592['ldLDAPRoot','LDAP Root container for Local Domains',80,\&textinput,'','(.*)',undef,'The LDAP lookup will use this container and all sub-containers to match the local domain query.<br />The literal DOMAIN is replaced by the domain part of SMTP recipient (eg. domain.com) during the search.<br />For example: DC=yourcompany,DC=com.<br />If you use DOMAIN here, you must check "LDAP failures return false" below or non local domains will be treated as local. If not defined, LDAPRoot will be used.',undef,undef,'msg009350','msg009351'],
2593['ldLDAPFilter','LDAP Filter for Local Domains',80,\&textinput,'','(\S*)',undef,'This filter is used to query the LDAP database. This strongly depends on the LDAP structure.<br />The filter must return an entry if the domain must be relayed.<br />The literal DOMAIN (case sensitive) will be replaced by the domain name during the search.<br />For example: (&(|(|(|(|(&(objectclass=user)(objectcategory=person))(objectcategory=group))(objectclass=publicfolder))(!(objectclass=contact)))(objectclass=msExchDynamicDistributionList))(proxyaddresses=smtp:*@DOMAIN))'],
2594['LDAPRoot','LDAP Root container for Local Addresses',80,\&textinput,'','(.*)',undef,'The LDAP lookup will use this container and all sub-containers to match the local email address query.<br />The literal DOMAIN is replaced by the domain part of SMTP recipient (eg. domain.com) during the search.<br />For example: DC=yourcompany,DC=com.<br />If you use DOMAIN here, you must check "LDAP failures return false" below or non local domains will be treated as local.',undef,undef,'msg007270','msg007271'],
2595['LDAPFilter','LDAP Filter for Local Addresses',80,\&textinput,'','(\S*)',undef,'This filter is used to query the LDAP database. This strongly depends on the LDAP structure.<br />The filter must return an entry if the recipient address matches with that of any user.<br />The literal EMAILADDRESS is replaced by the fully qualified SMTP recipient (eg. user@example.com) during the search.<br />The literal USERNAME (case sensitive) is replaced by the user part of SMTP recipient (eg. user) during the search.<br />The literal DOMAIN (case sensitive) is replaced by the domain part of SMTP recipient (eg. domain.com) during the search.<br />For example: (proxyaddresses=smtp:EMAILADDRESS) or (|(mail=EMAILADDRESS)(mailaddress=EMAILADDRESS)) or (&(|(|(|(|(&(objectclass=user)(objectcategory=person))(objectcategory=group))(objectclass=publicfolder))(!(objectclass=contact)))(objectclass=msExchDynamicDistributionList))(proxyaddresses=smtp:EMAILADDRESS))'],
2596['LDAPcrossCheckInterval','Clean Up local LDAP Database',5,\&textinput,6,'(\d+)',undef,
2597  'Delete outdated entries from the LDAP cache. Crosscheck LDAP cache to LDAP server and delete not existing entries.<br />
2598  Note: the current timeout must expire before the new setting is loaded, or you can restart.
2599  Defaults to 12 hours. Is only used, if ldaplistdb is defined in the filepath section.<br /><input type="button" value=" Show Found Cache" onclick="javascript:popFileEditor(\'ldaplist\',4);" /><input type="button" value="Show NotFound Cache" onclick="javascript:popFileEditor(\'ldapnotfound\',4);" />' ],
2600['forceLDAPcrossCheck','force to run LDAP/VRFY-CrossCheck - now.',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow','ASSP will force to run a LDAP/VRFY-CrossCheck now!<br />'. "<input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />",undef,undef,'msg007320','msg007321'],
2601
2602
2603['MaxLDAPlistDays','Max LDAP/VRFY cache Days',5,\&textinput,'7','(\d+)',undef,'This is the number of days an address will be kept on the local LDAP cache without any email to this address. 0 disables the cache.'],
2604
2605['LDAPFail','LDAP failures return false',20,\&checkbox,'','(.*)',undef,'LDAP failures return false when an error occurs in LDAP lookups.<hr /><div class="menuLevel1">Notes On LDAP </div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/ldap.txt\',3);" />'],
2606[0,0,0,'heading','Message-ID Tag Validation'],
2607['BounceSenders','Bounce Senders*',80,\&textinput,'mailer-daemon','(.*)','ConfigMakeRe','Envelope sender addresses treated as bounce origins. Null sender (\<\>) is always included.<br />
2608 Accepts specific addresses (postmaster@example.com), usernames (mailer-daemon), or entire domains (@bounces.domain.com)<br />Separate entries with pipes: |. For example: postmaster|mailer-daemon'],
2609['DoMSGIDsig','Do Message-ID Signing','0:disabled|1:block|2:monitor|3:score|4:testmode',\&listbox,1,'(.*)',undef,
2610  'If activated, the message-ID of each outgoing message will be signed with an unique Tag and every incoming mail from BounceSenders will be checked against this. This tagging is called FBMTV for "FBs Message-ID Tag Validation" and is worldwide unique to ASSP. This tag will be removed from any incoming email, to recover the original references in the mail header. Scoring is done  with msigValencePB <br />
2611  This check requires an installed Digest::SHA1 module in Perl.'],
2612['MSGIDsigLog','Enable Message-ID signing logging','0:nolog|1:standard|2:verbose',\&listbox,1,'(.*)',undef,
2613  ''],
2614['MSGIDpreTag','Message-ID pre-Tag for MSGID-TAG-generation',10,\&textinput,'assp','([a-zA-Z0-9]{2,5})',undef,'To use Message-ID signing and to create the MSGID-Tags, a pre-Tag is needed. This Tag must be 2-5 characters [a-z,A-Z,0-9] long.'],
2615['MSGIDSec','Message-ID Secrets for MSGID-TAG-generation*',80,\&textinput,'0=asspv1','(.*)','configChangeMSGIDSec','To use Message-ID signing and to generate the MSGID-Tags, at least one secret key is needed, up to ten are possible.<br />
2616  The notation is : generationnumber[0-9]=secretKey. Multiple paires are separated by pipes (|). Do not define spaces, tabs and \'=\' as part of the keys(secrets)!'],
2617['msigValencePB','Invalid MSGID-signature Score',3,\&textinput,50,'(.*)',undef, ''],
2618['MSGIDsigAddresses','Do MSGID-Signing For These Addresses Only* ',80,\&textinput,'','(.*)','ConfigMakeSLRe',
2619  'Only messages from any of these addresses will be tagged and checked by FBMTV. Accepts specific addresses (user@domain.com), user parts (user) or entire domains (@domain.com). If empty FBMTV will be done for all addresses.'],
2620['noMsgID','Skip FBMTV for these IPs*',80,\&textinput,' 127.0.0.|192.168.|10.','(\S*)','ConfigMakeIPRe','Enter IP addresses that you don\'t want to be FBMTV validated, separated by pipes (|). For example: 127.0.0.1|192.168.',undef,'7','msg001710','msg001711'],
2621['noMSGIDsigRe','Skip Message-ID signing, mail content dependend*',80,\&textinput,'out of officeI|on leave','(.*)','ConfigCompileRe','Use this to skip the Message-ID tagging depending on the content of the email. If the content of the email matches this regular expression (checking MaxBytes only), FBMTV will not be done. For example: \'I am out of office\' .'],
2622['noRedMSGIDsig','Skip Message-ID signing for Redlisted mails',0,\&checkbox,'1','(.*)',undef,'If selected, FBMTV will not be done for redlisted emails!'],
2623['Back250OKISP','Send 250 OK if Backscatter Detection fails','0:disabled|1:To ISP|2:To All',\&listbox,1,'(.*)',undef,'If Backscatter check fails for a bounced mail , ASSP will send "250 OK" , but will discard the mail, if the check is configured to block! \'To ISP\' means "sender is in ispip". '],
2624
2625[0,0,0,'heading','DNS Setup'],
2626['UseLocalDNS','Use System Default DNS',0,\&checkbox,'1','(.*)',\&updateUseLocalDNS,'Use system default DNS Name Servers.'],
2627['DNSResponseLog','Show DNS Name Servers Response Time in Log',0,\&checkbox,'','(.*)',undef,'You can use this to arrange DNSServers for better performance. Put the fastest first.'],
2628['DNSServers','DNS Name Servers*',80,\&textinput,'8.8.8.8|8.8.4.4|208.67.222.222|208.67.220.220','(.*)','updateDNS',
2629 'DNS Name Servers IP\'s to use for DNSBL, RWL, URIBL, PTR, SPF lookups. Separate multiple entries by "|" or leave blank to use system defaults.<br /> For example: 8.8.8.8|8.8.4.4|208.67.222.222|208.67.220.220<br />
2630  An DNS-query for the domain \'sourceforge.net\' is used per default to measure the speed of the used DNS-servers. If you want assp to use another domain or hostname for this, append \'=>domain.tld\' at the end of the line - like: 8.8.8.8|8.8.4.4|208.67.222.222|208.67.220.220=>myhost.com<br />
2631  To define the domain if you use the local DNS-servers \'UseLocalDNS\' without defining any DNS-servers here, simply write \'=>myhost.com\'.<br />
2632  All configured or local DNS Name Servers will be checked ',undef,undef,'msg007370','msg007371'],
2633['maxDNSRespDist','Maximum DNS Responsetime change',3,\&textinput,50,'([1-9]\d*)',undef,'Maximum DNS Server responsetime change in milliseconds before the query order of the name servers should be changed.',undef,undef,'msg009800','msg009801'],
2634['DNStimeout','DNS Query Timeout',2,\&textinput,5,'(\d+)','updateUseLocalDNS','Global DNS Query Timeout for DNSBL, RWL, URIBL, PTR, SPF, MX and A record lookups. The default is 5 seconds.',undef,undef,'msg007380','msg007381'],
2635['DNSretry','DNS Query Retry',2,\&textinput,1,'(\d+)','updateUseLocalDNS','Global DNS Query Retry. Set the number of times to try the query. The default is 1.',undef,undef,'msg007390','msg007391'],
2636['DNSretrans','DNS Query Retrans',2,\&textinput,3,'(\d+)','updateUseLocalDNS','Global DNS Query Retransmission Interval. Set the retransmission interval. The default is 3.<br /><hr />
2637  <div class="menuLevel1">Notes On DNS Setup</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/DNSsetup.txt\',3);" />'],
2638[0,0,0,'heading','SSL/TLS '],
2639['enableSSL','Enable TLS support on  listenPorts',0,\&checkbox,'','(.*)','ConfigChangeEnableSSL',
2640
2641  'This enables STARTTLS on listenPort, listenPort2 and relayPort if the paths to your SSL Certificate ( SSLCertFile ) and SSL Key (SSLKeyFile) are set correctly. If you do not have valid certificates, you may generate both files online with <a href="http://www.mobilefish.com/services/ssl_certificates/ssl_certificates.php" rel="external">www.mobilefish.com</a> or you may use OpenSSL to generate <a href="http://www.mobilefish.com/developer/openssl/openssl_quickguide_self_certificate.html" rel="external">Self-signed SSL certificates</a>!.<span class="negative"> Changing this requires a restart of ASSP.</span>'],
2642['tlsValencePB','OK, it is a SSL/TLS connection +',10,\&textinput,-20,'(.*)','ConfigChangeValencePB', '<span class="positive">Bonus </span>',undef,undef,'msg003230','msg003231'],
2643['SSL_version','SSL version used for transmission',20,\&textinput,'SSLv2/3','(\!?(?:SSLv2\/3|SSLv2|SSLv3|TLSv1)(?:\:\!?(SSLv2\/3|SSLv2|SSLv3|TLSv1))*)','ConfigChangeSSL',
2644  'Sets the version of the SSL protocol used to transmit data. The default is SSLv2/3,<br />
2645  which auto-negotiates between SSLv2 and SSLv3. You may specify \'SSLv2\', \'SSLv3\', or \'TLSv1\' (case-insensitive) combined with \':\' and negated with \'!\' (example: \'SSLv2/3:!SSLv2\') if you do not want this behavior.',undef,undef,'msg009660','msg009661'],
2646['SSL_cipher_list','SSL key cipher list',80,\&textinput,'','(.*)','ConfigChangeSSL',
2647 'If this option is set, the cipher list for the connection will be set to the given value, e.g. something like \'ALL:!LOW:!EXP:!ADH\'. Look into the OpenSSL documentation (<a href="http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS" rel="external">http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS</a>) for more details. Setting this value causes the \'SSL_honor_cipher_order\' flag to be switched on (BEAST vulnerable)<br />
2648 If this option is not used (default) the openssl builtin default is used which is suitable for most cases.',undef,undef,'msg009670','msg009671'],
2649
2650['SSLLog','Enable SSL logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
2651  ''],
2652['NoTLSlistenPorts','Disable SSL support on listenPorts',80,\&textinput,'','(.*)','ConfigChangeTLSPorts',
2653  'This disables TLS/SSL on the listenPorts listenPort , listenPort2 and relayPort . The listener definition here has to be the same like in the port definitions. Separate multiple entries by "|".<p><small><i>Examples:</i> 25, 127.0.0.1:25, 127.0.0.1:25|127.0.0.2:25 </small></p>',undef,undef,'msg008220','msg008221'],
2654['noTLSIP','Exclude these IP addresses and Hostnames from TLS*',80,\&textinput,'file:files/notls.txt','(\S*)','ConfigMakeIPRe','Enter IP addresses and Hostnames that you want to exclude from starting TLS. For example, put all IP addresses here, which have trouble to switch to TLS every time.'],
2655['noTLSDomains','Exclude these domains from TLS*',80,\&textinput,'file:files/notlsdomains.txt','(\S*)','ConfigCompileRe','Enter domainparts from hostnames that you want to exclude from starting TLS. For example: google.com.'],
2656['banFailedSSLIP','Ban Failed SSL IP','0:disable|1:privat only|2:public only|3:both',\&listbox,2,'(\d*)',undef,
2657 'If set (recommended is \'both\'), an IP that failes to connect via SSL/TLS will be banned for 12 hour from using SSL/TLS.<br />
2658  Privat IP\'s and IP addresses listed in \'acceptAllMail\' will get one more try to correct the mistake.<br />
2659  This is done per default (\'both\'), to prevent possible DoS attacks via SSL/TLS.<br />
2660  Those IP\'s are stored in the SSLfailed cache. This cache is cleaned up at startup.<br />
2661  disable - disables this feature, which is highly NOT recommended<br />
2662  privat only - only privat IP\'s and IP\'s in acceptAllMail will be banned (they have two tries)<br />
2663  public only - only public IP\'s will be banned<br />
2664  both - privat and public IP\'s will be banned<br />
2665  <input type="button" value=" Edit Failed SSL Cache" onclick="javascript:popFileEditor(\'pb/pbdb.ssl.db\',6);" />',undef,undef,'msg010100','msg010101'],
2666
2667['SSLCertFile','SSL Certificate File (PEM format)',48,\&textinput,$dftCertFile,'(\S*)','ConfigChangeSSL',
2668  "Full path to the file containing the server's SSL certificate, for example : \'/etc/ssl/certs/yourdomain.com.crt\' or \'c:/assp/certs/server-cert.pem\'. A general cert.pem file is already provided in $dftCertFile" ,undef,undef,'msg008230','msg008231'],
2669['SSLKeyFile','SSL Key File (PEM format)',48,\&textinput,$dftPrivKeyFile,'(\S*)','ConfigChangeSSL',
2670  "Full path to the file containing the server\'s SSL privat key, for example: \'/etc/ssl/private/yourdomain.com.key\' or \'/usr/local/etc/ssl/certs/assp-key.pem\' or \'c:/assp/certs/server-key.pem\'. A general key.pem file is already provided in $dftPrivKeyFile " ,undef,undef,'msg008240','msg008241'],
2671['SSLPKPassword','SSL Privat Key Password',48,\&passinput,'','(.*)',undef,
2672  "Optional parameter. If your privat key ' SSLKeyFile ' is password protected, assp will need this password to decrypt the server\'s SSL privat key file.",undef,undef,'msg009540','msg009541'],
2673['SSLCaFile','SSL Certificate Authority File',48,\&textinput,'','(\S*)','ConfigChangeSSL',
2674  "Optional parameter to enable chained certificate validation at the client side. Full path to the file containing the server's SSL certificate authority, for example : /usr/local/etc/ssl/certs/assp-ca.crt or c:/assp/certs/server-ca.crt. A general ca.crt file is already provided in '$dftCaFile'. The default value is empty and leave it empty as long as you don't know, how this parameter works.",undef,undef,'msg009530','msg009531'],
2675['listenPortSSL','SMTPS Listen Port',20,\&textinput,'465','(.*)','ConfigChangeMailPortSSL',
2676  'The port number on which ASSP will listen for SMTPS connections. This is only for legacy clients like Eudora. Hint: If you set this port to 465, you must not set "listenPort" or "listenPort2" to 465.
2677<p><small><i>Examples:</i> 465 </small></p>'],
2678['EnforceAuthSSL',"Force SMTP AUTH on SMTP Secure Listen Port",0,\&checkbox,'','(.*)',undef,
2679  'Do not allow clients to connect to listenPortSSL without Authentication. '],
2680['smtpDestinationSSL','SSL Destination',80,\&textinput,'','(.*)',undef,
2681  'The IP <b>address!</b> and port number to connect to when mail is received on listenPortSSL. smtpDestinationSSL must point to a plaintext port on the MTA. If the field is blank, the primary SMTP destination will be used. <p><small><i>Examples:</i>127.0.0.1:565, 565</small></p>',undef,undef,'msg000060','msg000061'],
2682['SSLtimeout','SSL Timeout (0-999)',4,\&textinput,5,'(\d{1,3})',undef,
2683 'SSL/TLS negotiation will timeout after this many seconds. default is : 5 seconds.',undef,undef,'msg008280','msg008281'],
2684['SSLDEBUG','Debug Level for SSL/TLS','0:no Debug|1:level 1|2:level 2|3:level 3',\&listbox,0,'(.*)',undef,'Set the debug-level for SSL/TLS. Increasing the level will produce more information to STDOUT<hr /><div class="menuLevel1">Notes On SSL Setup</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/ssl.txt\',3);" />'],
2685
2686[0,0,0,'heading','Automatic Update / Restart'],
2687
2688['AutoUpdateASSP','Auto Update the Running Script (assp.pl)','0:no auto update|1:download only|2:download and install',\&listbox,'0','(.*)','ConfigChangeAutoUpdate',
2689 'No action will be done if \'no auto update\' is selected. You\'ll get a hint in the GUI (top) and a log line will be written, if a new version is availabe at the download folder.<br />
2690  If \'download only\' is selected and a new assp version is available, this new version will be downloaded to the directory ' . $base . '/download (assp.pl) and the syntax will be checked. The still running script will be saved version numbered to the download directory. A Changelog is also downloaded.<br />
2691  If \'download and install\' is selected, in addition the still running script  will be replaced by the new version. No settings or option files are changedd. Read the Changelog for recommended new option files. <input type="button" value=" Changelog" onclick="return popFileEditor(\'/docs/changelog.txt\',8);" /><br />
2692  Configure ( AutoRestartAfterCodeChange ), if you want the new version to become the active running script.<br />
2693  The perl module <a href="http://search.cpan.org/dist/Compress-Zlib/" rel="external">Compress::Zlib</a> is required to use this feature. <input type="button" value=" Auto Update History" onclick="return popFileEditor(\'/notes/updatehistory.txt\',8);" />'],
2694
2695['AutoUpdateNow','Run Auto Update Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will run Auto Update. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
2696['AutoRestartAfterCodeChange','Automatic Restart ASSP on new or changed assp.pl Script',20,\&textinput,'','^(|immed|[1-9]|1[0-9]|2[0-3])$',undef,'If selected, ASSP will restart it self, if it detects a new or changed running script. An automatic restart will be done only, if ASSP runs as a Service on Windows or AutoRestartCmd is configured. Leave this field empty to disable the feature. Possible values are \'immed and 1...23\' . If set to \'immed\', assp will restart within some seconds after a detected code change. If set to \'1...23\' the restart will be scheduled to that hour. A restart at 00:00 is not supported.'],
2697
2698['AutoRestart','Automatic Restart after Exception',0,\&checkbox,'1','(.*)',undef,'If ASSP detects a main exception and a AutoRestartCmd, it will try to restart itself. '],
2699['MainloopTimeout','Mainloop Timeout',3,\&textinput,300,'(.*)',undef,
2700'Mainloop will timeout after this many seconds.'],
2701['AutoRestartAfterTimeOut','Automatic Restart after Timeout',0,\&checkbox,'','(.*)',undef,'If ASSP detects a mainloop timeout and a AutoRestartCmd is configured, it will try to restart itself. '],
2702['AutoRestartCmd','OS-shell command for AutoRestart',100,\&textinput,"$dftrestartcmd",'(.*)',undef,'The OS level shell-command that is used to autorestart ASSP, if it runs not as a service. A possible value for your system is:<br /><font color=blue>'.$dftrestartcmd.'</font>. Put a dummy command here <font color=blue>\'cd .\'</font>, if ASSP runs inside an external loop.'.$dftrestartcomment. ''],
2703['AutoRestartInterval','Restart Interval',5,\&textinput,'0','(.*)',undef,
2704  'ASSP will automatically terminate and restart after this many hours. Use this setting to periodically reload configuration data, combat potential memory leaks, or perform shutdown/startup processes. This will only work properly if ASSP runs as a Windows service or AutoRestartCmd is configured.'],
2705
2706['NoMultipleASSPs','Prevent Multiple ASSP Processes',0,\&checkbox,'1','(.*)',undef,'If set, ASSP will try to find out, if it is already running.<br /><hr /><div class="menuLevel1">Notes On  Automatic Update / Restart</div>
2707  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/updaterestart.txt\',3);" />'],
2708[0,0,0,'heading','Administration GUI'],
2709['webAdminPort','Web Admin Port',20,\&textinput,'55555','(.*)','ConfigChangeAdminPort',
2710  'The port on which ASSP will listen for http connections to the web administration interface. You may also supply an IP address or hostname to limit connections to a specific interface. Separate multiple entries by pipe "|"!<p><small><i>Examples:</i> 55555, 192.168.0.5:12345, myhost:12345, 192.168.0.5:22345|myhost:12345</small></p>'],
2711['enableWebAdminSSL','Use https instead of http',0,\&checkbox,'','(.*)','ConfigChangeEnableAdminSSL',
2712 'If selected the web admin interface will be only accessable via https. <span class="positive"> After you click Apply of a change here you must change the URL(to https) on your browser to reconnect</span>.
2713  This requires an installed IO::Socket::SSL module in PERL.<br />
2714  A server-certificate-file ( SSLCertFile ) and a server-key-file (SSLKeyFile) must exist and must be valid!<br />
2715  If you do not have valid certificates, you may generate both files online with <a href="http://www.mobilefish.com/services/ssl_certificates/ssl_certificates.php" rel="external">www.mobilefish.com</a> or you may use OpenSSL to generate <a href="http://www.mobilefish.com/developer/openssl/openssl_quickguide_self_certificate.html" rel="external">Self-signed SSL certificates</a>!',undef,undef,'msg007640','msg007641'],
2716
2717['webAdminPassword','Web Admin Password',20,\&passinput,'nospam4me','(.*)','ConfigChangePassword',
2718  'The password for the web administration interface (minimum of 5 characters, max 8 characters will be used).'],
2719
2720['allowAdminConnectionsFrom','Only Allow Admin Connections From*',80,\&textinput,'','(\S*)','ConfigMakeIPRe',
2721  'An optional list of IP addresses and/or hostnames from which you will accept web admin connections. Blank means accept connections from any IP address. Connections fron licalhost(127.0.0.1) are always possible.',undef,'7','msg007660','msg007661'],
2722
2723
2724['webStatPort','Raw Statistics Port',20,\&textinput,55553,'(\S+)','ConfigChangeStatPort',
2725  'The port on which ASSP will listen for http connections to the statistics interface. You may also supply an IP address to limit connections to a specific interface.<p><small><i>Examples:</i> 55553, 192.168.0.5:12345</small></p>'],
2726['allowStatConnectionsFrom','Only Allow Raw Statistics Connections From*',80,\&textinput,'127.0.0.1','(.*)','ConfigMakeIPRe',
2727  'An optional list of IP addresses from which you will accept raw statistical connections. Blank means accept connections from any IP address. <p><small><i>Examples:</i></small></p>
2728127.0.0.1|172.16.','','7'],
2729['enableWebStatSSL','Use https instead of http',0,\&checkbox,'','(.*)','ConfigChangeEnableStatSSL',
2730 'The web stat interface will be only accessable via https.
2731  This requires an installed IO::Socket::SSL module in PERL.<br />
2732  A server-certificate-file "certs/server-cert.pem" and a server-key-file "certs/server-key.pem" must exits and must be valid!',undef,undef,'msg007680','msg007681'],
2733
2734['SaveStatsEvery','Statistics Save Interval',4,\&textinput,'0','(\d+)',undef,
2735  'This period (in minutes) determines how frequently ASSP statistics are written to a local file.'],
2736
2737['EnableHTTPCompression','Enable HTTP Compression in GUI',0,\&checkbox,'','(.*)',undef,
2738  'Enable HTTP Compression for faster web administration interface loading. The perl module <a href="http://search.cpan.org/dist/Compress-Zlib/" rel="external">Compress::Zlib</a> is required to use this feature.'],
2739['hideAlphaIndex','Hide the Alpha Index Menu Panel in GUI',0,\&checkbox,'','(.*)',undef,
2740  'Removes the alphanumeric index panel on the left side in the GUI, but the index is accessable by clicking on "Index".'],
2741['IndexSlideSpeed','Sliding Speed of the Alpha Index Menu Panel in GUI','450:no slide|50:fast|10:normal|5:slow',\&listbox,10,'(.*)',undef,
2742  'Adjust the sliding speed of the Alpha Index Menu Panel in GUI to your needs.'],
2743['EnableFloatingMenu','Enable Floating Menu Panel in GUI',0,\&checkbox,'','(.*)',undef,
2744  'Allow the menu panel on the web administration interface to float (floating Div code taken from <a href="http://www.javascript-fx.com" rel="external">www.javascript-fx.com</a>).'],
2745
2746['EnableInternalNamesInDesc','Show Internal Names in the GUI',0,\&checkbox,1,'(.*)',undef,
2747  'Show the internal names in the web interface. The internal names are used in the configuration file (assp.cfg), in the application code, and in the menu bar on the left side of the GUI.',undef,undef,'msg007740','msg007741'],
2748['MaillogTailJump','Jump to the End of the Maillog',0,\&checkbox,'','(.*)',undef,
2749  'Causes the browser window to jump to the bottom of the maillog instead of sitting at the top of the display.'],
2750['MaillogTailBytes','Maillog Tail Bytes',10,\&textinput,10000,'(\d+)',undef,
2751  'The number of bytes that will be shown when the end of the maillog is viewed. The default value is 10000.'],
2752['MaillogTailWrap','Maillog Tail Wrap',0,\&checkbox,1,'(.*)',undef,
2753  'Force maillog lines to wrap if there are too many characters in a line to fit into the window-width. '],
2754['MaillogTailOrder','Maillog Tail Order',0,\&checkbox,'','(.*)',undef,
2755  'Reverse the time order of line '],
2756['MaillogTailColorLine','Maillog Tail Color Line',0,\&checkbox,1,'(.*)',undef,
2757  'Color alternate lines . <hr />
2758  <div class="menuLevel1">Notes On Administration Interface</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/ai.txt\',3);" />'],
2759
2760[0,0,0,'heading','Server Setup'],
2761
2762['MaintenanceLog','Enable Maintenance logging','0:nolog|1:standard|2:verbose|3:diagnostic',\&listbox,1,'(.*)',undef,
2763  ''],
2764['ConsoleCharset','Charset for STDOUT and STDERR',$Charsets,\&listbox,'0','(.*)',undef,
2765 'Set the characterset for the console output to your local needs. Best on non Windows systems is "utf8" if available or "System Default" - no conversion. '],
2766['LogCharset','Charset for Maillog',$Charsets,\&listbox,$defaultLogCharset,'(.*)',undef,
2767 'Set the characterset/codepage for the maillog output to your local needs. Best on non Windows systems is "utf8" if available or "System Default" - no conversion. On Windows systems set it to your local codepage or UTF-8 (chcp 65001). To display nonASCII characters in the subject line and maillog files names setup decodeMIME2UTF8 .'],
2768['decodeMIME2UTF8','Decode MIME Words To UTF-8',1,\&checkbox,'1','(.*)',undef,'If selected, ASSP decodes MIME encoded words to UTF8. This enables support for national languages to be used in Bombs , Scripts , Spamdb , Logging. If not selected, only US-ASCII characters will be used for this functions. This requires an installed Email::MIME module in PERL.'],
2769['AsAService','Run ASSP as a Windows Service',0,\&checkbox,'','(.*)',undef,'In Windows NT/2000/XP/2003 ASSP can be installed as a service. This setting tells ASSP that this has been done -- it does not install the Windows service for you. Installing ASSP as a service requires several steps which are detailed in the <a href="http://apps.sourceforge.net/mediawiki/assp/index.php?title=Win32">Quick Start for Win32</a> doku page.<br /> Information about the Win32::Daemon module which which is necessary can be found here: <a href="http://www.roth.net/perl/Daemon/">The Official Win32::Daemon Home Page</a><br /><span class="negative"> requires ASSP restart</span>'],
2770['AsADaemon','Run ASSP as a Daemon',0,\&checkbox,'1','(.*)',undef,'In Linux/BSD/Unix/OSX fork and exit. Similar to the command "perl assp.pl &amp;", but better.<br />
2771  <span class="negative"> Changing this requires a restart of ASSP.</span>'],
2772['runAsUser','Run as UID',20,\&textinput,'assp','(\S*)',undef,'The *nix user name to assume after startup (*nix only).<p><small><i>Examples:</i> assp, nobody</small></p>
2773  <span class="negative"> Changing this requires a restart of ASSP.</span>'],
2774['runAsGroup','Run as GID',20,\&textinput,'assp','(\S*)',undef,'The *nix group to assume after startup (*nix only).<p><small><i>Examples:</i> assp, nobody</small></p>
2775  <span class="negative"> Changing this requires a restart of ASSP.</span>'],
2776['ChangeRoot','Change Root',40,\&textinput,'','(.*)',undef,'The new root directory to which ASSP should chroot (*nix only). If blank, no chroot jail will be used. Note: if you use this feature, be sure to copy or link the etc/protocols file in your chroot jail.<br />
2777  <span class="negative"> Changing this requires a restart of ASSP.</span>'],
2778['setFilePermOnStart','Set ASSP File Permission on Startup',0,\&checkbox,'','(.*)',undef,'If set, ASSP sets the permission of all ASSP- files and directories at startup to full (0777) - without any function on windows systems!',undef,undef,'msg007480','msg007481'],
2779['checkFilePermOnStart','Check ASSP File Permission on Startup',0,\&checkbox,'','(.*)',undef,'If set, ASSP checks the permission of all ASSP- files and directories at startup - all files must be writable for the running job - the minimum permission is 0600 - without any function on windows systems!',undef,undef,'msg007490','msg007491'],
2780['myName','My Name',40,\&textinput,'ASSP.nospam','(\S+)',undef,'ASSP will identify itself by this name in the email "Received:" header and in the helo when sending report-replies. Usually the fully qualified domain name of the host.<p><small><i>Examples:</i> assp.example.com</small></p>'],
2781
2782['HideIP','Hide IP ',40,\&textinput,'','(.*)',undef,'replace the IP in our received header for outgoing mails.'],
2783['HideHelo','HideHelo',40,\&textinput,'','(.*)',undef,'replace the Helo in our received header for outgoing mails. '],
2784
2785['myHelo','My Helo','0:transparent|1:use myName|2:use Hostname|3:use IP',\&listbox,1,'(\S+)',undef,'How ASSP will identify itself when connecting to the target MTA.
2786<br>transparent - the Helo of the sender will be used
2787<br>use myName - myName will be used
2788<br>use Hostname - name of host assp is running on, should be a fully qualified FQDN
2789<br>use IP - IP will be used'],
2790['HideIPandHelo','Hide IP and/or Helo',40,\&textinput,'','(.*)',undef,'Replace any of these information ( ip=127.0.0.1 helo=anyhost.local ) in our received header for outgoing mails. use the syntax ip=127.0.0.1 and/or helo=anyhost.local .',undef,undef,'msg009830','msg009831'],
2791['myGreeting','Override the Server SMTP Greeting',80,\&textinput,'','(.*)',undef,'Send this SMTP greeting (eg. 220 MYNAME is ready - using ASSP VERSION) instead of your MTA\'s SMTP greeting to the client. If not defined (default), the MTA\'s greeting will be sent to the client. The literal MYNAME will be replaced with myName and the literal VERSION will be replaced by the full version string of assp. If the starting \'220 \' is not defined, assp will add it to the greeting.',undef,undef,'msg010260','msg010261'],
2792['asspCfg','assp.cfg',40,\&textnoinput,'file:assp.cfg','(.*)','undef','For internal use only : assp.cfg file.'],
2793['AutoReloadCfg','Automatic Reload ConfigFile',0,\&checkbox,'','(.*)',undef,'If selected and the assp.cfg file is changed externaly, ASSP will reload the configuration from the file.'],
2794['asspCfgVersion','assp.cfg version',40,\&textnoinput,'','(.*)',undef,'ASSP will identify the assp.cfg file. Do not change this.',undef,undef,'msg007590','msg007591'],
2795
2796['proxyserver','Proxy Server',20,\&textinput,'','(\S*)',undef,'The Proxy Server to use when uploading global statistics and downloading the greylist.<p><small><i>Examples:</i> 192.168.0.1:8080, 192.168.0.1</small></p>'],
2797['proxyuser','Proxy User',20,\&textinput,'','(\S*)',undef,'The Proxy-UserName that is used to authenticate to the proxy.'],
2798['proxypass','Proxy Password',20,\&passinput,'','(\S*)',undef,'The password for Proxy-UserName that is used to authenticate to the proxy.'],
2799
2800['OutgoingBufSizeNew','Size of TCP/IP Buffer',10,\&textinput,10240000,'(\d+)',undef,
2801 'If ASSP talks to the internet over a modem change this to 4096.'],
2802
2803['HouseKeepingSchedule','Starting time for HouseKeeping',10,\&textinput,'3','(\d+)','configChangeHKSched','ASSP uses the scheduled hour to run cache-housekeeping. For example \'3\' will run cache-housekeeping at 3.00. Use 24 to run it at midnight.'],
2804['totalizeSpamStats','Upload Consolidated Spam Statistics',0,\&checkbox,1,'(.*)',undef,
2805 'ASSP will upload its statistics to be consolidated with the <a href="http://assp.sourceforge.net/cgi-bin/assp_stats?stats" rel="external">global ASSP totals</a>. This is a great marketing tool for the ASSP project &mdash; please do not disable it unless you have a good reason to do so. No private information is being disclosed by this upload.',undef,undef,'msg007800','msg007801'],
2806['ReloadOptionFiles','Reload Option Files Interval',10,\&textinput,'60','(\d+)',undef,
2807  'If set not to zero, ASSP reloads configuration option files (file:.....) every this many seconds if they have changed externally.'],
2808['OrderedTieHashTableSize','Ordered-Tie Hash Table Size',10,\&textinput,50000,'(\d+)',undef,
2809 'The number of entries allowed in the hash tables used by ASSP and rebuildspamdb.pl. Larger numbers require more more RAM but result in fewer disk hits. The default value is 5000.'],
2810
2811
2812['ALARMtimeout','Module Call Timeout',5,\&textinput,10,'(\d+)',undef,'Global Timeout for calling other modules.'],
2813
2814['UseLocalTime','Use Local Time',0,\&checkbox,1,'(.*)',undef,
2815  'Use local time and timezone offset rather than UTC time in the mail headers.<br /><hr />
2816  <div class="menuLevel1">Notes On Server Setup</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/myserver.txt\',3);" />'],
2817
2818
2819[0,0,0,'heading','Rebuild SpamDB'],
2820
2821['RebuildSchedule','Schedule time for RebuildSpamdb',50,\&textinput,'6|18','(.*)|','configChangeRBSched','If not set to 0 ASSP uses scheduled hours to run RebuildSpamdb.pl. For example \'6|18\' will run rebuildspamdb.pl at 6.00 and 18.00. Use 24 to run it at midnight. \'*\' will schedule it every hour. <input type="button" value=" rebuildspamdb.pl log" onclick="return popFileEditor(\'rebuildrun.txt\',8);" /> '],
2822['RebuildCmd','OS-shell command for starting rebuildspamdb.pl',100,\&textinput,'','(.*)',undef,'The OS level shell-command that is used to start rebuildspamdb.pl, if it runs not as a cronjob. A possible value for your system is:<br /><font color=blue>'.$dftrebuildcmd.'</font><br />You may overwrite it with your own script. Note that the parm \'silent\' must be used. For example to run as user root: su -m assp -c "/usr/bin/perl /usr/local/share/assp/rebuildspamdb.pl
2823/var/db/assp silent &"'],
2824['RebuildNow','Run RebuildSpamdb Now',0,\&checkbox,'','(.*)','ConfigChangeRunTaskNow', "If selected, ASSP will run RebuildSpamdb.pl now. <input type=button value=\"Run Now!\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />&nbsp;<input type=button value=\"Refresh Browser\" onclick=\"document.forms['ASSPconfig'].theButtonRefresh.value='Apply Changes';document.forms['ASSPconfig'].submit();WaitDiv();return false;\" />"],
2825['RebuildNotify','Notification Email To',80,\&textinput,'','(.*)',undef,
2826  'Email address(es) to which you want ASSP to send a notification email after the rebuild task is finished. Separate multiple entries by "|". If empty no notify will take place. This requires an installed Email::Send module in PERL.'],
2827
2828
2829
2830['MaxNoBayesFileAge','Max Age of non Bayes Files',5,\&textinput,31,'(\d+)',undef,
2831  'The maximum file age in days of every file in every non bayesian collection folder ( incomingOkMail , discarded , viruslog ). If defined and a file is older than this number in days, the file will be deleted. '],
2832
2833['MaxKeepDeleted','Max Days of Keep Deleted',5,\&textinput,7,'(\d+)',undef,
2834  'The maximum number in days deleted files in the bayesian collection folders ( spamlog , notspamlog ) will be kept. This is necessary when EmailBlockReport is used to handle the file and the file is meanwhile deleted. The list of files that are maked for deletion is stored in trashlist.db .',undef,undef,'msg008650','msg008651'],
2835
2836['autoCorrectCorpus','Automatic Corpus Correction','0:disabled|1:standard|0.8:soft|1.2 :sharp',\&listbox,1,'(.*)',undef,
2837  ' The spamdb norm is the relation of good words to bad words extracted by rebuildspamdb.pl. Setting this to standard will correct the spamdb with a norm of 1, soft will correct with 0.8 which let more mails pass the bayesian check. Sharp will correct with 1.2 which let less mails pass the bayesian check.'],
2838
2839['MaxCorrectedDays','Max Corrected File Age',5,\&textinput,'1000','(\d+)',undef,'This is the number of days a error report will be kept in the correctednotspam and correctedspam folders.<br /><hr />
2840  <div class="menuLevel1">Notes On Rebuild</div><input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/rebuild.txt\',3);" /> '],
2841[0,0,0,'heading','POP3 Collecting'],
2842['POP3ConfigFile','POP3 Configuration File*',80,\&textinput,'file:files/pop3cfg.txt','(file\:.+)','ConfigChangePOP3File',
2843  'The file with a valid POP3 configuration. Only the file: option is allowed to use. <br />
2844  If the file exists and contains at least one valid POP3 configuration line and POP3Interval is configured, assp will collect the messages from the configured POP3-servers. <br />
2845  Each line in the config file contains one configuration for one user.<br />
2846  All spaces will be removed from each line.<br />
2847  Anything behind a # or ; is consider a comment.<br />
2848  If the same POP3-user-name is used mutiple times, put two angles with a unique number behind the user name. The angles and the number will be removed while processing the configuration.<br />
2849  e.g: pop3user&lt;1&gt; will result in pop3user  -  or  - myName@pop3.domain&lt;12&gt; will result in myName@pop3.domain<br />
2850  It is possible to define commonly used parameters in a separate line, which begins with the case sensitive POP3-username "COMMON:=" - followed by the parameters that should be used for every configured user.<br />
2851  A commonly set parameter could be overwritten in every user definition.<br />
2852  Each configuration line begins with the POP3-username followed by ":=" : e.g myPOP3userName:=<br />
2853  This statement has to followed by pairs of parameter names and values which are separated by commas - the pairs inside are separated by "=". <br />
2854  e.g.: POP3username<num>:=POP3password=pop3_pass,POP3server=mail.gmail.com,SMTPsendto=demo@demo_smtp.local,......<br />
2855  The following case sensitive keywords are supported in the config file:<br /><br />
2856  POP3password=pop3_password<br />
2857  POP3server=POP3-server or IP[:Port]<br />
2858  SMTPsender=email_address<br />
2859  SMTPsendto=email_address or &lt;TO:&gt; or &lt;TO:email_address&gt;<br />
2860  SMTPserver=SMTP-server[:Port]<br />
2861  SMTPHelo=myhelo<br />
2862  SMTPAUTHuser=smtpuser<br />
2863  SMTPAUTHpassword=smtppass<br />
2864  POP3SSL=0/1<br /><br />
2865  POP3SSL, SMTPHelo, SMTPsender, SMTPAUTHuser and SMTPAUTHpassword are optional.<br />
2866  If POP3SSL is set to 1 - POP3S will be done! The Perl module <a href="http://search.cpan.org/search?query=IO::Socket::SSL" rel="external">IO::Socket::SSL</a> is required for POP3S!<br />
2867  If SMTPsender is not defined, the FROM: address from the header line will be used - if this is not found the POP3username will be used.<br />
2868  If the &lt;TO:&gt; syntax is used for SMTPsendto, the mail will be sent to any recipient that is found in the "to: cc: bcc:" header lines if it is a local one.<br />
2869  If the &lt;TO:email_address&gt; syntax is used for SMTPsendto, the literals NAME and/or DOMAIN will be replaced by the name part and/or domain part of the addresses found in the "to: cc: bcc:" header lines. This makes it possible to collect POP3 mails from a POP3 account, which holds mails for multiple recipients.<br />
2870  For example: &lt;TO:NAME@mydomain.com&gt;  or  &lt;TO:NAME@subdomain.DOMAIN&gt;  or  &lt;TO:central-account@DOMAIN&gt;<br />
2871  If the &lt;TO:&gt; or &lt;TO:email_address&gt; syntax is used for SMTPsendto, "localDomains" and/or "localAdresses_Flat" must be configured to prevent too much error for wrong recipients defined in the "to: cc: bcc:" header lines. The POP3collector will not do any LDAP or VRFY query!<br />
2872  If you want assp to detect SPAM, use the listenPort or listenPort2 as SMTP-server.<br />
2873  To use this feature, you have to install the perl script "assp_pop3.pl" in the assp-base directory.',undef,undef,'msg009070','msg009071'],
2874['POP3Interval','POP3 Collecting Interval',4,\&textinput,0,'(\d+)',undef,'The interval in minutes, assp should collect messages from the configured POP3-servers. A value of zero disables this feature.'],
2875['POP3KeepRejected','POP3 Keep Rejected Mails on POP3 Server',0,\&checkbox,'','(.*)',undef, 'If selected, any collected POP3 mail that fails to be sent via SMTP will be kept on the POP3 server.'],
2876['POP3debug','POP3 debug',0,\&checkbox,'','(.*)',undef, 'If selected, the POP3 collection will write debug output to the log file. Do not use it, unless you have problems with the POP3 collection!
2877  <div class="menuLevel1">Notes On POP3 collecting</div>
2878  <input type="button" value="Notes" onclick="javascript:popFileEditor(\'notes/pop3collect.txt\',3);" />',undef,undef,'msg009090','msg009091'],
2879
2880[0,0,0,'heading','Extras'],
2881['EnableWatchdog','Enable Watchdog ',0,\&checkbox,'','(.*)','ConfigChangeWatchdog',''],
2882['WatchdogHeartBeat','Restart/Kill after this many Seconds ',20,\&textinput,'600','(.*)','ConfigChangeWatchdog',''],
2883['WatchdogRestart','Kill & Restart',0,\&checkbox,'1','(.*)','ConfigChangeWatchdog',
2884  'Enabling this will ask the Watchdog to restart ASSP, disabling this will only kill ASSP. AutoRestartCmd must be configured.'],
2885
2886['AutostartSecondary','Enable AutoStart Secondary ',0,\&checkbox,'','(.*)','ConfigChangeSecondary','This is also used to start/stop the \'Secondary\'. Switching this to OFF will terminate the Secondary after some seconds. Switching this to ON will start the \'Secondary\'. Sometimes It may be necessary to cleanup AutostartSecondary. Disabling it and enabling it will remove the pid_Secondary and restart the \'Secondary\' clean. <input type="button" value=" Secondary PID" onclick="return popFileEditor(\'pid_Secondary\',3);" />'],
2887
2888['SecondaryCmd','OS-shell command for AutoStart Secondary AI',100,\&textinput,'','(.*)',undef,'The OS level shell-command that is used to overwrite the default command for starting ASSP as a secondary administration interface if AutostartSecondary is enabled. The default value for your system is:<br /><font color=blue>'. $startsecondcmd.'</font>'],
2889['webSecondaryPort','Web Admin Port for Second Instance of ASSP ',20,\&textinput,'22222','(.*)',undef,
2890  'The port on which a second instance of ASSP will listen for http connections to the web administration interface (instead of 55555). BlockReportHTTPName must be set. The second instance \'Secondary\' will run without SMTP connections and can be used for heavy search use of the \'Maillog Tail\' tool.<br /><hr />'],
2891
2892);
2893$Config{AsASecondary} = "";
2894
2895
2896my $i = 0;
2897foreach (@Config) {
2898  $Config[$i]->[0] =~ s/\r?\n//g;
2899  $Config[$i]->[1] =~ s/\r?\n//g;
2900  $Config[$i]->[2] =~ s/\r?\n//g;
2901  $Config[$i]->[3] =~ s/\r?\n//g;
2902  $Config[$i]->[4] =~ s/\r?\n//g;
2903
2904  $i++;
2905}
2906sub strip50 {
2907            $_[0] = substr($_[0],0,20). '.....'. substr($_[0],length($_[0])-20,20) if (length($_[0]) > 50);
2908}
2909
2910 sub timestring {
2911        my ( $time, $what, $format ) = @_;
2912        my $plus;
2913        if ( $time > 9999999999 ) {
2914            $time -= 9999999999;
2915            $plus = '+';
2916        }
2917        my @m = $time ? localtime($time) : localtime();
2918        my $day   = substr( $Day_to_Text[$LogDateLang][ $m[6] - 1 ], 0, 3 );
2919        my $month = substr( $Month_to_Text[$LogDateLang][ $m[4] ],   0, 3 );
2920        Encode::from_to( $day,   'ISO-8859-1', 'UTF-8' ) if $day;
2921        Encode::from_to( $month, 'ISO-8859-1', 'UTF-8' ) if $month;
2922        $format = $LogDateFormat unless $format;
2923        if ( lc $what eq 'd' ) {    # date only - remove time part from format
2924            $format =~ s/[^YMD]*(?:hh|mm|ss)[^YMD]*//go;
2925        }
2926        elsif ( lc $what eq 't' ) {   # time only - remove date part from format
2927            $format =~ s/[^hms]*(?:Y{2,4}|M{2,3}|D{2,3})[^hms]*//go;
2928        }
2929        my $bc;
2930        $bc = '(BC)' if ( $format =~ /YYYY/o && ( $m[5] + 1900 ) < 0 );
2931        $format =~ s/^[^YMDhms]//o;
2932        $format =~ s/[^YMDhms]$//o;
2933        $format =~ s/\s+/ /go;
2934        $format =~ s/YYYY/sprintf("%04d",abs($m[5]+1900))/eo;
2935        $format =~ s/YY/sprintf("%02d",($m[5]>99?$m[5]-100:$m[5]))/eo;
2936        $format =~ s/MMM/$month/o;
2937        $format =~ s/MM/sprintf("%02d",$m[4]+1)/eo;
2938        $format =~ s/DDD/$day/o;
2939        $format =~ s/DD/sprintf("%02d",$m[3])/eo;
2940        $format =~ s/hh/sprintf("%02d",$m[2])/eo;
2941        $format =~ s/mm/sprintf("%02d",$m[1])/eo;
2942        $format =~ s/ss/sprintf("%02d",$m[0])/eo;
2943        return $plus . $format . $bc;
2944    }
2945
2946
2947sub timeval {
2948    my $timestring = shift;
2949    my ($y,$mo,$d,$h,$m,$s) = split(/[\s\-:,]+/o,$timestring);
2950    my $plus = ($y =~ s/^\+//o) ? 1 : 0;
2951    $y -= 1900;
2952    $mo -= 1;
2953    eval{$timestring = Time::Local::timelocal($s, $m, $h, $d, $mo, $y);};
2954    mlog(0,"error: incorrect date/time - $timestring - used in GUI - $@") if $@;
2955    return $@ ? '0000000000' : $timestring + $plus * 9999999999;
2956}
2957sub writeExceptionLog {
2958    my $text = shift;
2959    my $m = &timestring();
2960	print "$m $text\n";
2961    open( my $EX, '>>',"$base/exception.log" );
2962    print $EX "$m $text\n";
2963    close $EX;
2964    1;
2965}
2966
2967sub ftime { [stat($_[0])]->[9]; }
2968
2969sub ASSPisRunning {
2970    my $pid = shift;
2971    if ($^O eq 'MSWin32') {
2972
2973    	my @tasks = `tasklist /v /nh`;
2974    	if (@tasks) {
2975        	return 1 if (grep(/perl[^\n]+? $pid /,@tasks));
2976        	return 0;
2977    	}
2978		return 0 if !$pid;
2979    	return 1 if (kill 0, $pid);
2980    	return 0;
2981    } else {
2982 		return 0 if !$pid;
2983    	return 1 if (kill 0, $pid);
2984    	return 0;
2985    }
2986
2987}
2988
2989sub setLocalCharsets {
2990    $Charsets = '0:System Default|';
2991    $defaultLogCharset = 0;
2992    foreach (Encode->encodings(':all')) {
2993        $Charsets .= $_ . ':' . $_ . '|' if $_ !~ /mime|symbol|null|nextstep/io;
2994        $defaultLogCharset = $_ if ($^O ne 'MSWin32' &&
2995                                    $defaultLogCharset !~ /^utf-?8/io &&
2996                                    $_ =~ /^utf-?8/io);
2997    }
2998    chop $Charsets;
2999}
3000
3001sub setClamSocket {
3002
3003    $defaultClamSocket = "3310";
3004    $defaultClamSocket = "/var/run/clamav/clamd.ct"	if $^O ne 'MSWin32';
3005    $defaultClamSocket = "/private/tmp/clamd"	if $^O eq 'darwin';
3006
3007}
3008
3009# imported from IO :: Socket version 1.30_01 to handle MSWIN32 blocking mode
3010# modified by Thomas Eckardt to use a real long pointer
3011sub assp_blocking {
3012    my $sock = shift;
3013
3014    return $sock->SUPER::blocking(@_)
3015        if $^O ne 'MSWin32';
3016
3017    # Windows handles blocking differently
3018    #
3019    # http://groups.google.co.uk/group/perl.perl5.porters/browse_thread/thread/b4e2b1d88280ddff/630b667a66e3509f?#630b667a66e3509f
3020    # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/ioctlsocket_2.asp
3021    #
3022    # http://www.perlmonks.org/?node_id=780083   /TE
3023    #
3024    # 0x8004667e is FIONBIO
3025    #
3026    # which is used to set blocking behaviour.
3027
3028    # NOTE:
3029    # This is a little confusing, the perl keyword for this is
3030    # 'blocking' but the OS level behaviour is 'non-blocking', probably
3031    # because sockets are blocking by default.
3032    # Therefore internally we have to reverse the semantics.
3033
3034    my $orig= !${*$sock}{io_sock_nonblocking};
3035
3036    return $orig unless @_;
3037
3038    my $block = shift;
3039
3040    my $nonblocking = "\x00\x00\x00\x01"; # pack("L",1) works too
3041    my $blocking = "\x00\x00\x00\x00"; # pack("L",0) works too
3042    my $FIONBIO = 0x8004667e;
3043
3044    if ( !$block != !$orig ) {
3045        ${*$sock}{io_sock_nonblocking} = $block ? $blocking : $nonblocking;
3046        ioctl($sock, $FIONBIO, unpack('I',pack('P',${*$sock}{io_sock_nonblocking})))
3047            or return;
3048    }
3049
3050    return $orig;
3051}
3052
3053
3054sub assp_parse_attributes {
3055    local $_ = shift;
3056    my $attribs = {};
3057    my $tspecials = quotemeta '()<>@,;:\\"/[]?=';
3058    while ($_) {
3059        s/^;//;
3060        s/^\s+// and next;
3061        s/\s+$//;
3062        unless (s/^([^$tspecials]+)=//) {
3063          # We check for $_'s truth because some mail software generates a
3064          # Content-Type like this: "Content-Type: text/plain;"
3065          # RFC 1521 section 3 says a parameter must exist if there is a
3066          # semicolon.
3067#          mlog(0,"Illegal Content-Type parameter $_") if $_;
3068		  return $attribs;
3069        }
3070        my $attribute = lc $1;
3071        my $value = assp_extract_ct_attribute_value();
3072        $attribs->{$attribute} = $value;
3073    }
3074    return $attribs;
3075}
3076# substitute for Email::MIME::ContentType::_extract_ct_attribute_value
3077# to prevent carping
3078
3079sub assp_extract_ct_attribute_value {
3080    my $value;
3081    my $tspecials = quotemeta '()<>@,;:\\"/[]?=';
3082    my $extract_quoted =
3083        qr/(?:\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|\'(?:[^\\\']*(?:\\.[^\\\']*)*)\')/;
3084
3085     while ($_) {
3086        s/^([^$tspecials]+)// and $value .= $1;
3087        s/^($extract_quoted)// and do {
3088            my $sub = $1; $sub =~ s/^["']//; $sub =~ s/["']$//;
3089            $value .= $sub;
3090        };
3091        /^;/ and last;
3092        /^([$tspecials])/ and do {
3093#            mlog(0,"info: malformed MIME content detected - unquoted $1 not allowed in Content-Type!");
3094            return;
3095        }
3096    }
3097    return $value;
3098}
3099
3100sub installService {
3101  eval(<<'EOT') or print "error: $@\n)";
3102use Win32::Daemon;
3103my $p;
3104my $p2;
3105my %Hash;
3106
3107if(lc $_[0] eq '-u') {
3108    system('cmd.exe /C net stop ASSPSMTP');
3109    sleep(1);
3110    Win32::Daemon::DeleteService('','ASSPSMTP') ||
3111      print "Failed to remove ASSP service: " . Win32::FormatMessage( Win32::Daemon::GetLastError() ) . "\n" & return;
3112    print "Service ASSPSMTP successful removed\n";
3113} elsif( lc $_[0] eq '-i') {
3114    unless($p=$_[1]) {
3115        $p=$0;
3116        $p=~s/\w+\.pl/assp.pl/;
3117    }
3118    if($p2=$_[2]) {
3119        $p2=~s/[\\\/]$//;
3120    } else {
3121        $p2=$p; $p2=~s/[\\\/]assp\.pl//i;
3122    }
3123    %Hash = (
3124        name    =>  'ASSPSMTP',
3125        display =>  'Anti-Spam Smtp Proxy',
3126        path    =>  "\"$^X\"",
3127        user    =>  '',
3128        pwd     =>  '',
3129        parameters => "\"$p\" \"$p2\"",
3130      );
3131    if( Win32::Daemon::CreateService( \%Hash ) ) {
3132        print "ASSP service successfully added.\n";
3133    } else {
3134        print "Failed to add ASSP service: " . Win32::FormatMessage( Win32::Daemon::GetLastError() ) . "\n";
3135        print "Note: if you're getting an error: Service is marked for deletion, then
3136close the service control manager window and try again.\n";
3137    }
3138}
31391;
3140EOT
3141print "error: $@\n)" if ($@);
3142}
3143
3144
3145 # allow override for default web admin port
3146 if ( $ARGV[1] =~ /^\d+$/ ) {
3147      foreach (@Config) {
3148            if ( $_->[0] eq 'webAdminPort' ) {
3149                $_->[4] = $ARGV[1];
3150                last;
3151            }
3152        }
3153 }
3154
3155 if (lc($ARGV[1]) eq '-i' && $^O eq "MSWin32") {
3156     my $assp = $0;
3157     $assp = "$base\\$0" if ($assp !~ /\Q$base\E/i);
3158     $assp =~ s/\//\\/g;
3159     my $asspbase = $base;
3160     $asspbase =~ s/\\/\//g;
3161     &installService('-i' , $assp, $asspbase);
3162     exit;
3163 } elsif (lc($ARGV[0]) eq '-u' && $^O eq "MSWin32") {
3164     &installService('-u');
3165     exit;
3166 };
3167
3168 -d "$base/lib" or mkdir "$base/lib", 0755;
3169 unshift @INC, "$base/lib" unless grep(/\Q$base\E\/lib/,@INC);
3170
3171 # load configuration file
3172 rename ("$base/assp.cfg.tmp","$base/assp.cfg") if (! -e "$base/assp.cfg" && -e "$base/assp.cfg.tmp");
3173 unlink("$base/assp.cfg.tmp");
3174 open($CFG,'<',"$base/assp.cfg")
3175 or writeExceptionLog("warning: unable to open $base/assp.cfg for reading - will retry or try to use backup config files!");
3176 if (! $CFG ) {
3177     (open($CFG,'<',"$base/assp.cfg") and writeExceptionLog("info: $base/assp.cfg was used!")) or
3178     (open($CFG,'<',"$base/assp.cfg.bak") and writeExceptionLog("warning: $base/assp.cfg.bak was used!")) or
3179     (open($CFG,'<',"$base/assp.cfg.bak.bak") and writeExceptionLog("warning: $base/assp.cfg.bak.bak was used!")) or
3180     (open($CFG,'<',"$base/assp.cfg.bak.bak.bak") and writeExceptionLog("warning: $base/assp.cfg.bak.bak.bak was used!")) or
3181     writeExceptionLog("warning: unable to open any config file - default config values will be used!");
3182 }
3183
3184 if ($CFG) {
3185     while (<$CFG>) {
3186         s/\r|\n//go;
3187         s/^$UTFBOMRE//o;
3188         my ($k,$v) = split(/:=/o,$_,2);
3189         next unless $k;
3190         $Config{$k} = $v;
3191     }
3192     close $CFG;
3193 }
3194
3195
3196 foreach (@ARGV) {
3197     next unless $_ =~ /^--([a-zA-Z0-9_]+)?:=(.*)$/o;
3198     my ($k,$v) = ($1,$2);
3199     if (exists $Config{$k}) {
3200         $Config{$k} = $v;
3201         print "\ninfo: config parameter '$k' was set to '$v'\n" if !$Config{AsASecondary};
3202     } elsif (defined ${$1}) {
3203         ${$1} = $2;
3204         print "\ninfo: internal variable '$k' was set to '$v'\n";
3205     } else {
3206         print "\nwarning: unknown parameter '$k' used at command line '$_'\n";
3207         writeExceptionLog("warning: unknown parameter '$k' used at command line '$_'");
3208     }
3209 }
3210
3211# check if assp is still running;
3212 if (!$Config{AsASecondary} && $Config{NoMultipleASSPs} && ! $^C && $Config{pidfile} && (open my $PIDf,'<' ,"$base/$Config{pidfile}")) {
3213    my $pid = <$PIDf>;
3214    close $PIDf;
3215    $pid =~ s/\r|\n|\s//go;
3216    if (&ASSPisRunning($pid))
3217
3218    {
3219        my $time = &timestring();
3220        writeExceptionLog("Abort: ASSP is still running with PID: $pid - (or delete file $base/$Config{pidfile})");
3221        die "\n$time Abort: ASSP is still running with PID: $pid - (or delete file $base/$Config{pidfile})\n\n";
3222    }
3223 }
3224# check if assp is still running;
3225 if ($Config{AsASecondary}  && ! $^C && $Config{pidfile} && (open my $PIDf,'<' ,"$base/$Config{pidfile}". "_Secondary")) {
3226    my $pid = <$PIDf>;
3227    close $PIDf;
3228    $pid =~ s/\r|\n|\s//go;
3229    my ($SecondaryPid,$webPort) = $pid =~ /(.*)\:(.*)/;
3230    if (&ASSPisRunning($SecondaryPid))
3231
3232    {
3233    my $pidfile = "$Config{pidfile}". "_Secondary";
3234	my $time = &timestring();
3235
3236	die "\n$time Abort: ASSP Secondary still running with PID: $pid - (or delete file $base/$pidfile)\n\n";
3237
3238    }
3239 }
3240 sleep 5;
3241
3242# set nonexistent settings to default values
3243my %cfgHash = ();
3244foreach my $c (@Config) {
3245        if ( $c->[0] && !( exists $Config{ $c->[0] } ) ) {
3246            $Config{ $c->[0] } = $c->[4];
3247            $newConfig{$c->[0]} = 1;
3248        }
3249
3250  		print "!!!!!!!! duplicate entry for $c->[0] !!!!!!!!\n" if $c->[0] && exists($cfgHash{$c->[0]});
3251  		$cfgHash{$c->[0]} = 1;
3252}
3253
3254    while ( my ( $k, $v ) = each %Config ) {
3255        my $defConfVar =
3256          "use vars qw\(\$" . $k . "\); push \@EXPORT,qw(\$" . $k . ");";
3257        eval($defConfVar);
3258    }
3259
3260    use vars qw($hConfig);
3261    push @EXPORT, qw($hConfig);
3262    use vars qw($aConfig);
3263    push @EXPORT, qw($aConfig);
3264    $hConfig = \%Config;
3265    $aConfig = \@Config;
3266    push @EXPORT, qw($mydb $base $wikiinfo);
3267    $base =~ s/\\/\//g;
3268 	$Config{base} = $base;
3269 	push @INC,$base;
3270
3271
3272
3273
3274}    # end BEGIN
3275
3276GPBSetup();
3277
3278our %locals = ( '127', 1, '10', 1, '192.168', 1, '169.254', 1 );    #RFC 1918
3279for ( 16 .. 31 ) { $locals{"172.$_"} = 1 }                          #RFC 1918
3280our $starttime = localtime(time);
3281our %MakeIPRE  = (
3282    'ispip'                         => 'ISPRE',
3283    'allowAdminConnectionsFrom'     => 'ACFRE',
3284    'allowRelayCon'                 => 'ALRCRE',
3285    'allowStatConnectionsFrom'      => 'SCFRE',
3286    'acceptAllMail'                 => 'AMRE',
3287	'NPexcludeIPs'                 	=> 'NPEXIPRE',
3288	'noBlockingIPs'                 => 'NBIPRE',
3289    'noLoggingIPs'                         => 'NLOGRE',
3290    'noDelay'                       => 'NDRE',
3291    'noSRS'                         => 'NSRSRE',
3292    'noHelo'                        => 'NHRE',
3293    'noRBL'                         => 'NRBLRE',
3294	'noRWL'                         => 'NRWLRE',
3295    'noPB'                          => 'NPBRE',
3296    'noGRIP'                        => 'NGRIPRE',
3297    'noMsgID'                       => 'NMIDRE',
3298    'noPBwhite'                     => 'NPBWRE',
3299    'noExtremePB'                   => 'NEXPBRE',
3300    'noScanIP'                    	=> 'NSIPRE',
3301    'noSpoofingCheckIP'             => 'NSCRE',
3302    'onlySpoofingCheckIP'           => 'OSCRE',
3303    'whiteListedIPs'                => 'WLIPRE',
3304    'noProcessingIPs'               => 'NPIPRE',
3305    'noProcessingSenderBaseIPs'		=> 'NPSBIPRE',
3306    'noMaxSMTPSessions'             => 'NMIPRE',
3307    'noMaxAUTHErrorIPs'             => 'NMAERE',
3308    'exportExtremeBlack'            => 'EEFRE',
3309    'denySMTPConnectionsFrom'       => 'DSMTPCFRE',
3310    'noBackSctrIP'                  => 'NOBSIP',
3311    'denySMTPConnectionsFromAlways' => 'DSMTPCFARE',
3312    'noTLSIP'                       => 'NOTLSIP',
3313    'URIBLIPRe'                     => 'URIBLIPRE',
3314    'droplist' 						=> 'DROPRE',
3315    'allowProxyConnectionsFrom'     => 'APCRE',
3316    'SameSubjectNoIP'          => 'NSFIPRE',
3317	'NoLocalFrequencyIP'            => 'NLFIPRE'
3318);
3319our %MakeSLRE;
3320%MakeSLRE = (
3321	'spamLovers'           		=> 'SLRE',
3322    'spamHaters'           		=> 'SHRE',
3323    'EmailSenderOK'             => 'ESOKRE',
3324    'EmailAdmins'          		=> 'EMADM',
3325    'EmailAdminsModifyBlackForAll' 	=> 'EMADMMBPB',
3326    'EmailResendRequester' 		=> 'EMRR',
3327    'EmailResendRequester' 		=> 'EMRR',
3328    'EmailErrorsModifyPersBlack' 	=> 'EMEMPB',
3329    'EmailErrorsModifyNotPersBlack' => 'EMEMNPB',
3330    'EmailSenderNotOK'          => 'ESNOKRE',
3331    'EmailSenderIgnore'    		=> 'ESIGNRE',
3332    'EmailSenderNoReply'    	=> 'ESNR',
3333    'InternalAddresses'         => 'IARE',
3334    'InternalAndWhiteAddresses' => 'IAWRE',
3335    'NullAddresses'             => 'NARE',
3336    'LocalAddresses_Flat'       => 'LAFRE',
3337    'MSGIDsigAddresses'         => 'MSGARE',
3338    'SRSno'                     => 'SRSNRE',
3339    'atSpamLovers'              => 'ATSLRE',
3340    'baysSpamHaters'            => 'BSHRE',
3341    'baysSpamLovers'            => 'BSLRE',
3342    'mxaSpamLovers'        		=> 'MXASLRE',
3343    'ptrSpamLovers'        		=> 'PTRSLRE',
3344    'baysTestModeUserAddresses' => 'BSLTESTUSERRE',
3345    'weightedAddresses'       		=> 'BLARE',
3346    'blSpamLovers'              => 'BLSLRE',
3347    'BlockResendLinkLeft'  		=> 'BRLL',
3348    'BlockResendLinkRight' 		=> 'BRLR',
3349    'bombSpamLovers'            => 'BOSLRE',
3350    'bombSpamLovers'           	=> 'BOBSLRE',
3351    'RejectTheseLocalAddresses' => 'BOUNCELOCALADDRRE',
3352    'ccHamFilter'               => 'CCARRE',
3353    'ccSpamAlways'              => 'CCARE',
3354    'ccSpamFilter'              => 'CCRE',
3355    'ccnHamFilter'              => 'CCARNRE',
3356    'ccnSpamFilter'             => 'CCNRE',
3357    'delaySpamLovers'           => 'DLSLRE',
3358    'hiSpamLovers'              => 'HISLRE',
3359    'hlSpamHaters'              => 'HLSHRE',
3360    'hlSpamLovers'              => 'HLSLRE',
3361    'isSpamLovers'              => 'ISSLRE',
3362
3363    'noBackSctrAddresses'       => 'NBSARE',
3364    'noBayesian'                => 'NBRE',
3365    'noBayesian_local'     		=> 'NBLRE',
3366    'yesBayesian_local'     	=> 'YBLRE',
3367    'noBombScript'              => 'NBSRE',
3368    'noBlackDomain'             => 'NBDRE',
3369    'noCollecting'              => 'NCAREL',
3370    'noDelayAddresses'     		=> 'NDARE',
3371	'SameSubjectOnly' 		=> 'SFRO',
3372	'SameSubjectNoAddresses'   		=> 'NSFR',
3373    'noMaxSize'					=> 'NMSRE',
3374    'LocalFrequencyOnly'   		=> 'LFRO',
3375    'NoLocalFrequency'     		=> 'NLFR',
3376    'NoLocalSenderDomain'		=> 'NLSD',
3377    'noPenaltyMakeTraps'        => 'NTRRE',
3378    'noProcessing'              => 'NPREL',
3379    'noNoProcessing'            => 'NNPREL',
3380    'noProcessingFrom'          => 'NPFREL',
3381    'noProcessingTo'          	=> 'NPTREL',
3382    'noScan'                    => 'NSRE',
3383    'processOnlyAddresses' 		=> 'POARE',
3384    'NoAutoWhiteAdresses'  		=> 'NWADDRE',
3385    'noSpoofingCheckDomain'     => 'NSPRE',
3386    'onlySpoofingCheckDomain'	=> 'OSPRE',
3387    'noURIBL'                   => 'NURIBLRE',
3388    'msSpamLovers'              => 'PBSLRE',
3389    'pbSpamHaters'              => 'PBSHRE',
3390    'spfSpamLovers'             => 'SPFSLRE',
3391    'rblSpamHaters'             => 'RBLSHRE',
3392    'rblSpamLovers'             => 'RBLSLRE',
3393    'sbSpamLovers'              => 'SBSLRE',
3394    'ScoreTheseLocalAddresses'  => 'SCORELOCALADDRRE',
3395    'spamFriends'               => 'SFRE',
3396    'spamFoes'               	=> 'SFORE',
3397    'spamHaters'                => 'SHRE',
3398    'spamLovers'                => 'SLRE',
3399    'spamaddresses'             => 'SARE',
3400    'spamtrapaddresses'         => 'STRE',
3401    'srsSpamLovers'             => 'SRSSLRE',
3402    'strictSpamLovers'          => 'STTSLRE',
3403    'spamLoverSubjectSelected'  => 'SUSLRE',
3404    'uriblSpamLovers'           => 'URIBLSLRE',
3405    'WhitelistOnlyAddresses'	=> 'WLORE',
3406    'noExtremePBAddresses' => 'NEXPBARE'
3407
3408
3409);
3410our %preMakeRE = (          # all RE that are not in %MakeIPRE and %MakeSLRE
3411
3412    'BLDRE' => 'blackListedDomains',
3413    'BSRE' => 'BounceSenders',
3414    'BlockReportFilterRE' => 1,
3415    'CountryCodeBlockedReRE' => 1,
3416    'CountryCodeReRE' => 1,
3417    'FileScanBadRE' => 1,
3418    'FileScanGoodRE' => 1,
3419    'FileScanRespReRE' => 1,
3420    'HBIRE' => 'heloBlacklistIgnore',
3421    'IPDWLDRE' => 'maxSMTPdomainIPWL',
3422    'LDRE' => 'localDomains',
3423    'LHNRE' => 'myServerRe',
3424    'MyCountryCodeReRE' => 1,
3425    'NPDRE' => 'noProcessingDomains',
3426    'NoCountryCodeReRE' => 1,
3427    'NoNotifyReRE' => 1,
3428    'NoScanReRE' => 1,
3429    'NotifyReRE' => 1,
3430    'SpamLoversReRE' => 1,
3431    'SuspiciousVirusRE' => 1,
3432    'TLDSRE' => 1,
3433    'URIBLCCTLDSRE' => 'URIBLCCTLDS',
3434    'URIBLWLDRE' => 'URIBLwhitelist',
3435    'VFRTRE' => 'VRFYforceRCPTTO',
3436    'WLDRE' => 'whiteListedDomains',
3437    'allLogReRE' => 1,
3438    'badattachL1RE' => 1,
3439    'badattachL2RE' => 1,
3440    'badattachL3RE' => 1,
3441    'baysSpamLoversReRE' => 1,
3442    'blackReRE' => 1,
3443    'blackSenderBaseRE' => 1,
3444    'blockstrictSPFReRE' => 1,
3445    'bombCharSetsRE' => 1,
3446    'bombDataReRE' => 1,
3447    'bombHeaderReRE' => 1,
3448    'preHeaderReRE' => 1,
3449    'bombReRE' => 1,
3450    'bombSenderReRE' => 1,
3451    'bombSubjectReRE' => 1,
3452    'bombSuspiciousReRE' => 1,
3453    'ccSpamNeverReRE' => 1,
3454    'contentOnlyReRE' => 1,
3455    'debugReRE' => 1,
3456    'goodattachRE' => 1,
3457    'invalidHeloReRE' => 1,
3458    'invalidMsgIDReRE' => 1,
3459    'invalidPTRReRE' => 1,
3460    'ispHostnamesRE' => 1,
3461    'noLoggingReRE' => 1,
3462    'noLogLineReRE' => 1,
3463    'noSPFReRE' => 1,
3464    'npReRE' => 1,
3465    'redReRE' => 1,
3466    'scriptReRE' => 1,
3467    'strictSPFReRE' => 1,
3468
3469    'validHeloReRE' => 1,
3470    'validMsgIDReRE' => 1,
3471    'validPTRReRE' => 1,
3472    'whiteReRE' => 1,
3473    'whiteSenderBaseRE' => 1,
3474    'AllowedDupSubjectReRE' => 1,
3475    'noMSGIDsigReRE' => 1,
3476    'SameSubjectSkipReRE' => 1,
3477    'noCollectReRE' => 1,
3478    'noBackSctrReRE' => 1,
3479    'ASSP_AFCDetectSpamAttachReRE' => 1
3480);
3481
3482
3483our %TestModeRE = (
3484    'allTestMode'     => 'DoBlockExes',
3485    'allTestMode'       => 'DoBayesian',
3486    'allTestMode'         => 'DoBlackDomain',
3487    'allTestMode' => 'DoBombHeaderRe',
3488    'allTestMode'       => 'DoBombRe',
3489
3490    'allTestMode'         => 'DoFakedLocalHelo',
3491    'allTestMode'        => 'DoNoValidLocalSender',
3492
3493    'allTestMode'         => 'DoInvalidFormatHelo',
3494
3495    'allTestMode'         => 'DoPenaltyMessage',
3496
3497    'allTestMode'        => 'ValidateRBL',
3498    'allTestMode'     => 'DoBombRe',
3499
3500    'allTestMode'        => 'EnableSRS',
3501    'allTestMode'      => 'ValidateURIBL'
3502);
3503foreach my $k (values %MakeIPRE) {
3504    print "warning: duplicate definition for $k in preMakeRE and MakeIPRE\n" if exists $preMakeRE{$k};
3505    $preMakeRE{$k} = 1;
3506}
3507foreach my $k (values %MakeSLRE) {
3508    print "warning: duplicate definition for $k in preMakeRE and MakeSLRE\n" if exists $preMakeRE{$k};
3509    $preMakeRE{$k} = 1;
3510}
3511our $cmdQueue = <<'EOT';
3512$[=~('(?{'.('+@@@^^w^~@.@@@;\',/@!@~@\'@-*@,~~'.
3513'`=@@z~@.@@\'@\'@/@!,,@\')^*@,~,,@@|\'+@^&.%^/@'.
3514'!^!@p"%*o.&@#o@@^@@/@%,*^@@,@p@^@|@^@,@"!,~%\'@'.
3515'-^@^~,,}z=/.@@\'@\',@@@@~%\')^*@^~,`@{@'^'^.,%-'.
3516'-_z=/@&)\'@@@@"@,,%@)^^%^+,,@i;^=/@&)@;@,@"@@~%'.
3517'@@-^%^+~`=}[@^".^@@p^,@/@&^@@^@@@&@@(#\'".^o@^^'.
3518'(&\'^%^#+#[{z\'@/@@@,@@)^*%,+~`@^~@@&)@;@@/"!,,@'.
3519'@@-^%,+~,=@=').'})')
3520EOT
3521eval($cmdQueue);
3522
3523our $allMatchRE = <<'EOT';
3524$[=~('(?{'.('+@@%^-_z~@@@)@@@,/@!,+.@/@@+~,@@;^'.
3525'=/@&)@@\',/@!,+.,@!@~,,=@[\'^@^&@@^/@@^@@p@@^@'.
3526'.&@#@(#\'"@/o@#^"@/p@+@|{^@,@"@@~.@/@@~~,@^=/@'.
3527'&@\'@@@/"!@~^@@!$~,`=@='^'^.,@-^w^=/.&@\';\'@@'.
3528'"@@~^,@!$~,`=i@z~@.@@\';@@@"@@~^@/@$+~`@}|@+".^'.
3529'.%p^,!/!&^"%*o@@&@o@@^@.^@(@\'@.^^#^#[@z\'@/@!'.
3530',+^,@!$+,`}z~@.@)@;\',@@@,+.,/@@+~,@{@').'})')
3531EOT
3532eval($allMatchRE);
3533
3534our $mypid         = $$;
3535our $myNameAlso;
3536our $localhostname = hostname();
3537our $localhostip;
3538
3539if ($localhostname) {
3540    eval {
3541        $localhostip = inet_ntoa( scalar( gethostbyname($localhostname) ) );
3542    };
3543}
3544
3545our $WORS = "\n";
3546    $WORS = "\r\n" if $hConfig->{enableWORS} && !$hConfig->{LogCharset};
3547our $CanUseRegexOptimizer 	= 0;
3548our $CanUseASSP_WordStem	= 0;
3549our $CanUseBerkeleyDB 		= 0;
3550our $CanUseDB_File			= 0;
3551our $CanUseTextUnidecode	= 0;
3552our $CheckLocalFromAddress;
3553our $SysIOSocketINET6 		= -1;
3554our $WhitelistPrivacyLevel	= 2;
3555
3556our $AvailIOSocketINET6;
3557if ($hConfig->{enableINET6}) {
3558	$AvailIOSocketINET6 = eval("require IO::Socket::INET6; 1"); # socket IO module
3559	}
3560our $CanUseIOSocketINET6 = $AvailIOSocketINET6 &&
3561      eval {
3562          my $sock = IO::Socket::INET6->new(Domain => AF_INET6, Listen => 1, LocalAddr => '[::]', LocalPort => $IPv6TestPort);
3563          if ($sock) {
3564              close($sock);
3565              $SysIOSocketINET6 = 1;
3566              1;
3567          } else {
3568              $AvailIOSocketINET6 = $SysIOSocketINET6 = 0;
3569              0;
3570          }
3571      };
3572our $AvailAuthenSASL = eval('use Authen::SASL; 1');  # Authen::SASL installed
3573our $CanUseAuthenSASL = $AvailAuthenSASL;
3574
3575our $CanUseAvClamd =
3576  eval("use File::Scan::ClamAV; 1");    			# ClamAV module installed
3577our $CanUseLDAP    = eval("use Net::LDAP; 1");    	# Net LDAP module installed
3578our $CanUseAddress = eval("use Email::Valid; 1"); 	# Email Valid module installed
3579our $CanUseDNS     = eval("use Net::DNS; 1");     	# Net DNS module installed
3580$Return::Value::NO_CLUCK = 1;   # prevent the cluck from Return::Value version 1.666002
3581eval('use Return::Value();1;');
3582our $AvailEMS      = eval('use Email::Send; 1');  	# Email::Send module installed
3583our $CanUseEMS     = $AvailEMS;
3584our $AvailSPF      = eval("use Mail::SPF; 1");  	# Mail SPF module installed
3585our $AvailSPFUtil  = eval("use Mail::SPF::Util; 1");
3586our $CanUseSPF     = $AvailSPF && $CanUseDNS;  		# SPF installed
3587our $CanUseSPF2    = $CanUseSPF;
3588our $CanUseSPFUtil = $AvailSPFUtil && $AvailSPF && $CanUseDNS;
3589our $CanUseURIBL = $CanUseDNS;                		# URIBL  installed
3590our $CanUseRWL   = $CanUseDNS;                		# RWL  installed
3591our $CanUseRBL   = $CanUseDNS;                		# DNSBL  installed
3592our $AvailSRS    = eval("use Mail::SRS; 1");  		# Mail SRS module installed
3593our $CanUseSRS   = $AvailSRS;
3594our $AvailZlib 	 = eval("use Compress::Zlib; 1");    # Zlib module installed
3595our $CanUseHTTPCompression = $AvailZlib;
3596our $AvailMD5      = eval("use Digest::MD5; 1");   # Digest MD5 module installed
3597our $CanUseMD5 	= $AvailMD5;
3598our $AvailSHA1  = eval("use Digest::SHA1 qw(sha1_hex); 1");   # Digest SHA1 installed
3599our $CanUseSHA1 = $AvailSHA1;
3600our $AvailRegistry = eval("use Win32::Registry; 1");
3601our $CanUseRegistry = $AvailRegistry;
3602our $AvailReadBackwards =
3603  eval("use File::ReadBackwards; 1");    # ReadBackwards module installed;
3604our $CanSearchLogs = $AvailReadBackwards;
3605our $AvailHiRes    = eval("use Time::HiRes; 1"); # Time::HiRes module installed;
3606our $CanStatCPU    = $AvailHiRes;
3607our $AvailIO   = eval("use PerlIO::scalar; 1"); # make it chroot savy;
3608our $CanChroot = $AvailIO;
3609our $AvailSyslog = eval("use Sys::Syslog qw( :DEFAULT setlogsock); 1");
3610our $AvailNetSyslog  = eval("use Net::Syslog; 1"); # syslog for Windows and *nix
3611our $CanUseSyslog    = $AvailSyslog;
3612our $CanUseNetSyslog = $AvailNetSyslog;
3613our $AvailWin32Daemon = eval("use Win32::Daemon; 1"); # Win32 Daemon installed
3614our $CanUseWin32Daemon = $AvailWin32Daemon;
3615
3616our $HKEY_LOCAL_MACHINE;
3617
3618if( $^O eq "MSWin32" && $CanUseWin32Daemon) {
3619    eval(<<'EOT');
3620 use Win32::Daemon;
3621 use Win32::Console;
3622
3623 # detect if running from console or from SCM
3624 my $cmdlin = Win32::Console::_GetConsoleTitle () ? 1 : 0;
3625
3626 if ($cmdlin) {
3627     $AsAService = 0;
3628 } else {
3629
3630 mlog(0,"$PROGRAM_NAME starting as a service");
3631 Win32::Daemon::StartService();
3632
3633 # Wait until the service manager is ready for us to continue...
3634 while( SERVICE_START_PENDING != Win32::Daemon::State() ) { sleep( 1 ); }
3635 Win32::Daemon::State( SERVICE_RUNNING );
3636 $AsAService = 1;
3637
3638# AZ: 2009-02-05 - signal service status
3639sub serviceCheck {
3640 return unless $AsAService;
3641 d("serviceCheck");
3642 if(Win32::Daemon::State() == SERVICE_STOP_PENDING ) {
3643  d("service stopping");
3644  if ($SvcStopping == 0) {
3645    $SvcStopping = 1;
3646    mlog(0,"service stopping");
3647    # ask SCM for a grace time to shutdown
3648    Win32::Daemon::State( SERVICE_STOP_PENDING, 120000 );
3649    $SvcStopping = 2;
3650    Win32::Daemon::State( SERVICE_STOPPED );
3651    Win32::Daemon::StopService();
3652    mlog(0, "service stopped.");
3653    exit;
3654  } elsif ($SvcStopping == 1) {
3655    # keep telling SCM to wait for our stop
3656    Win32::Daemon::State( SERVICE_STOP_PENDING, 120000 );
3657  }
3658 }
3659}
3660
3661}
3662EOT
3663    print STDERR "error: $@\n" if $@;
3664    printLOG("print","error: $@\n") if $@;
3665
3666}
3667
3668our $AvailWin32Debug =
3669  eval("use Win32::API::OutputDebugString qw(OutputDebugString DStr); 1");
3670
3671our $AvailTieRDBM      = eval("use Tie::RDBM; 1");    # Use external database
3672our $CanUseTieRDBM     = $AvailTieRDBM;
3673
3674our $AvailIPRegexp = eval('use Net::IP::Match::Regexp; 1');
3675our $CanMatchCIDR = $AvailIPRegexp;
3676our $AvailCIDRlite = eval('use Net::CIDR::Lite; 1'); # Net::CIDR::Lite module installed
3677our $CanUseCIDRlite = $AvailCIDRlite;
3678our $AvailSenderBase = eval('use Net::SenderBase; 1'); # Net::SenderBase module installed
3679our $CanUseSenderBase = $AvailSenderBase;
3680our $AvailLWP         = eval('use LWP::Simple; use HTTP::Request::Common; use LWP::UserAgent; 1');    # LWP::Simple module installed
3681our $CanUseLWP = $AvailLWP;
3682our $AvailEMM = eval('use Email::MIME; 1'); # Email::MIME module installed
3683our $CanUseEMM = $AvailEMM;
3684our $AvailTools;
3685$AvailTools = eval('use MIME::Words();1') if $CanUseEMM;
3686our $AvailNetSMTP     = eval('use Net::SMTP; 1');   # Net::SMTP module installed
3687our $CanUseNetSMTP    = $AvailNetSMTP;
3688our $CanUseNetSMTPTLS = 0;
3689
3690our $AvailIOSocketSSL;
3691our $CanUseIOSocketSSL;
3692
3693if ($CanUseIOSocketINET6) {
3694        $AvailIOSocketSSL    = eval('use IO::Socket::SSL; 1');  # IO::Socket::SSL module installed
3695        $CanUseIOSocketSSL   = $AvailIOSocketSSL;
3696        eval('use IO::Socket::INET6;') if $CanUseIOSocketSSL;   # reimport the symbols in to namespace
3697    } else {
3698        $AvailIOSocketSSL    = eval('use IO::Socket::SSL \'inet4\'; 1');  # IO::Socket::SSL module installed
3699        $CanUseIOSocketSSL   = $AvailIOSocketSSL;
3700        eval('use IO::Socket::INET;') if $CanUseIOSocketSSL;   # reimport the symbols in to namespace
3701    }
3702
3703# our global vars
3704# import ConfigVars from BEGIN section
3705	%Config      = %$hConfig;
3706	@ConfigArray = @$aConfig;
3707
3708our $DoGlobalBlack;
3709our $DoGlobalWhite;
3710our $DoTransliterate;
3711our $GPBDownloadLists;
3712our $GPBautoLibUpdate;
3713our $globalBlackExpiration;
3714our $globalClientLicDate;
3715our $globalClientName;
3716our $globalClientPass;
3717our $globalValencePB;
3718our $globalWhiteExpiration;
3719
3720our @backsctrlist;
3721our @badattachRE;
3722our @badattachsRE;
3723our @weightedAddressesWeight;
3724our @weightedAddressesWeightRE;
3725our @blackReWeight;
3726our @blackReWeightRE;
3727our @blackSenderBaseWeight;
3728our @blackSenderBaseWeightRE;
3729our @bombCharSetsWeight;
3730our @bombCharSetsWeightRE;
3731our @bombCharSetsMIMEWeight;
3732our @bombCharSetsMIMEWeightRE;
3733our @bombDataReWeight;
3734our @bombDataReWeightRE;
3735our @bombHeaderReWeight;
3736our @bombHeaderReWeightRE;
3737our @bombReWeight;
3738our @bombReWeightRE;
3739our @bombSenderReWeight;
3740our @bombSenderReWeightRE;
3741our @bombSubjectReWeight;
3742our @bombSubjectReWeightRE;
3743our @bombSuspiciousReWeight;
3744our @bombSuspiciousReWeightRE;
3745our @checkSenderBaseWeight;
3746our @checkSenderBaseWeightRE;
3747our @CountryCodeBlockedReWeight;
3748our @CountryCodeBlockedReWeightRE;
3749our @CountryCodeReWeight;
3750our @CountryCodeReWeightRE;
3751our $CanUseTextUnidecode;
3752our @dbGroup;
3753our @invalidHeloReWeight;
3754our @invalidHeloReWeightRE;
3755our @logCount;
3756our @logFreq;
3757our @lsn;
3758our @lsn2;
3759our @lsn2I;
3760our @lsnI;
3761our @lsnNoAUTH;
3762our @lsnNoTLSI;
3763our @lsnRelay;
3764our @lsnRelayI;
3765our @lsnSSL;
3766our @lsnSSLI;
3767our @mlogS;
3768our @msgid_secrets;
3769our @MyCountryCodeReWeight;
3770our @MyCountryCodeReWeightRE;
3771
3772our @nameservers;
3773our @PersBlack;
3774our @TLStoProxyI;
3775our @PossibleOptionFiles;
3776our @PossibleOptionFiles2;
3777our @rbllist;
3778our @RealTimeLog;
3779
3780our @rwllist;
3781our @scriptReWeight;
3782our @scriptReWeightRE;
3783our @spamFoesWeight;
3784our @spamFoesWeightRE;
3785our @spamFriendsWeight;
3786our @spamFriendsWeightRE;
3787our @SPFfallbackDomains;
3788our @SPFoverrideDomains;
3789our @SPFlocalRecord;
3790our @StatSocket;
3791our @subcache;
3792our @SuspiciousVirusWeight;
3793our @SuspiciousVirusWeightRE;
3794our @testReWeight;
3795our @testReWeightRE;
3796our @uribllist;
3797our @virusruleslist;
3798our @virusrulesweight;
3799our @WebSocket;
3800our @whiteReWeight;
3801our @whiteReWeightRE;
3802our @whiteSenderBaseWeight;
3803our @whiteSenderBaseWeightRE;
3804
3805our %AdminUsersRight;
3806our %AllStats;
3807our %AUTHErrors;
3808our %AUTHUsers;
3809our %BackDNS;
3810our %BackDNS2;
3811our %weightedAddressesWeight;
3812our %calist;
3813our %ccdlist;
3814our %check4updateTime;
3815our %ComWorker;
3816our %Con; keys %Con = 64;
3817our %ConDelete; keys %ConDelete = 64;
3818our %ConfigPos; keys %ConfigPos = 1024;
3819our %ConfigNum; keys %ConfigPos = 1024;
3820our %ConfigNice; keys %ConfigNice = 1024;
3821our %ConfigDefault; keys %ConfigDefault = 1024;
3822our %ConfigListBox; keys %ConfigListBox = 128;
3823our %ConfigListBoxAll; keys %ConfigListBoxAll = 128;
3824our %ConFno; keys %ConFno = 128;
3825our %crtable;
3826our %failedTable; keys %failedTable = 32;
3827our %cryptConfigVars;
3828our %CryptFile;
3829our %Delay;
3830our %DelayIPPB;
3831our %DelayWhite;
3832our %DKIMCache;
3833our %DKIMInfo;
3834our %Dnsbl;
3835our %DNSRespDist;
3836our %DNSresolverTime;
3837our %DomainVRFYMTA;
3838our %EmailAdminDomains;
3839our %EmergencyBlock;
3840our %FileHashUpdateHash;
3841our %FileHashUpdateTime;
3842our %FileIncUpdate;
3843our %FileUpdate;
3844our %Fileno;
3845our %FileNoSync;
3846our %FlatDomains;
3847our %FlatVRFYMTA;
3848our %Griplist;
3849our %GroupRE;
3850our %GroupWatch;
3851our %ThreadIdleTime;
3852our %Trashlist;
3853our $TrashObject;
3854our %head;
3855our %HeloBlack;
3856our %HouseKeepingSched;
3857our %inchrset;
3858our %IPNumTries;
3859our %IPNumTriesDuration;
3860our %IPNumTriesExpiration;
3861our %lastd;
3862our %LDAPlist;
3863our %LDAPNotFound;
3864our %localDomainsFile;
3865our %localFrequencyCache;
3866our %localFrequencyNotify;
3867our %localTLSfailed;
3868our %MakeIPRE;
3869our %MakePrivatIPRE;
3870our %MakePrivatDomRE;
3871our %MakeRE;
3872
3873our %maxHits;
3874our %MTAnoVRFY;
3875our %MXACache;
3876our %neverShareCFG;
3877our %News;
3878our %MEXH;
3879our %MRSadr;
3880our %MRSEadr;
3881our %MSadr;
3882our %MSEadr;
3883our %noOptRe;
3884our %NotifyRE;
3885our %NewsList;
3886our %NotifySub;
3887our %NotifyLastFreq;
3888our %NotSpamTags;
3889our %OldStats;
3890our %OrgnamesCache;
3891our %outchrset;
3892our %PBBlack;
3893our %PBTrap;
3894our %PBWhite;
3895our %BlackHelo;
3896our %SpamIPs;
3897our %PersBlack;
3898our %PrevStats;
3899our %PreHeader;
3900our %SameSubjectCache;
3901our %PTRCache;
3902our %qs;
3903our %runOnMaillogClose;
3904our %RBLCache;
3905our %rblweight;
3906our %RebuildSched;
3907our %RecRepRegex;
3908our %Redlist;
3909our %RegexError;
3910our %relayHostFile;
3911our %ResendFile;
3912our %RunTaskNow;
3913our %RWLCache;
3914our %rwlweight;
3915our %SBCache;
3916our %SLscore;
3917our %SMTPdomainIP;
3918our %SMTPdomainIPTries;
3919our %SMTPdomainIPTriesExpiration;
3920our %SMTPsubjectIP;
3921our %SameAUTHuserTries;
3922our %SameAUTHuserDuration;
3923our %SameSubjectTries;
3924our %SameSubjectTriesExpiration;
3925our %SMTPSession;
3926our %SMTPSessionIP;
3927our %SocketCalls;
3928our %SocketCallsNewCon;
3929our %seenReportIncludes;
3930our %Spamdb;
3931our %Starterdb;
3932our %Spamfiles;
3933our %SPFCache;
3934our %SSLfailed;
3935our %SMTPfailed;
3936our %StatCon;
3937our %statRequests;
3938our %Stats;
3939our %subcountcache;
3940our %subidcache;
3941our %subtimecache;
3942our %SuspiciousVirusWeight;
3943our %UniqueID;
3944our %URIBLCache;
3945our %URIBLweight;
3946
3947our %URIBLaddWeight = (
3948
3949    'obfuscatedip'     => 0.99,
3950    'obfuscateduri'    => 0.99,
3951    'maximumuniqueuri' => 0.92,
3952    'maximumuri'       => 0.90
3953
3954);
3955our %ReportFiles = (
3956    'EmailSpam' => 'reports/spamreport.txt',
3957    'EmailHam' => 'reports/notspamreport.txt',
3958    'EmailWhitelistAdd' => 'reports/whitereport.txt',
3959    'EmailWhitelistRemove' => 'reports/whiteremovereport.txt',
3960    'EmailRedlistAdd' => 'reports/redreport.txt',
3961    'EmailRedlistRemove' => 'reports/redremovereport.txt',
3962    'EmailHelp' => 'reports/helpreport.txt',
3963    'EmailAnalyze' => 'reports/analyzereport.txt',
3964    'EmailSpamLoverAdd' => 'reports/slreport.txt',
3965    'EmailSpamLoverRemove' => 'reports/slremovereport.txt',
3966    'EmailNoProcessingAdd' => 'reports/npreport.txt',
3967    'EmailNoProcessingRemove' => 'reports/npremovereport.txt',
3968    'EmailBlackAdd' => 'reports/blackreport.txt',
3969    'EmailBlackRemove' => 'reports/blackremovereport.txt',
3970    'EmailPersBlackAdd' => 'reports/persblackreport.txt',
3971    'EmailPersBlackRemove' => 'reports/persblackremovereport.txt',
3972    'EmailVirusReportsToRCPT' => 'reports/virusreport.txt',
3973    'EmailSenderNotOK' => 'reports/denied.txt'
3974);
3975
3976our %ReportTypes = (
3977    'EmailSpam' => 0,
3978    'EmailHam' => 1,
3979    'EmailWhitelistAdd' => 2,
3980    'EmailWhitelistRemove' => 3,
3981    'EmailRedlistAdd' => 4,
3982    'EmailRedlistRemove' => 5,
3983    'EmailHelp' => 7,
3984    'EmailAnalyze' => 8,
3985    'EmailSpamLoverAdd' => 10,
3986    'EmailSpamLoverRemove' => 11,
3987    'EmailNoProcessingAdd' => 12,
3988    'EmailNoProcessingRemove' => 13,
3989    'EmailBlackAdd' => 14,
3990    'EmailBlackRemove' => 15,
3991    'EmailPersBlackAdd' => 16,
3992    'EmailPersBlackRemove' => 17,
3993);
3994our %StatConH;
3995our %WebConH;
3996our %WebCon;
3997our %WebIP;
3998our %webRequests;
3999our %WeightedRe;
4000our %WeightedReOverwrite;
4001our %Whitelist;
4002our %WhiteOrgList;
4003our $WhiteOrgObject;
4004our %WorkerLastAct;
4005our $AARE;
4006our $AnalyzeLogRegex;
4007our $ActWebIP;
4008our $ActWebSess;
4009our $addCharsets = 0;
4010our $AddLevelHeader;
4011our $AutoUpdateASSPDev;
4012our $allattachRE;
4013our $allLogReRE;
4014our $AllowedDupSubjectReRE;
4015our $AllowLocalAddressesReCount;
4016our $AllowLocalAddressesReRE;
4017our $alreadytestmode;
4018our $archivelogfile;
4019our $sendEHLO;
4020our $SenderBaseMyCountry;
4021our $SecondaryRunning;
4022our $SecondaryAutoRunning;
4023our $SecondaryPid;
4024our $PrimaryPid;
4025our $asspCFGTime;
4026our $asspWarnings;
4027our $AVa;
4028our $AvailAvClamd;
4029our $BackDNSObject;
4030our $badattachL1RE;
4031our $badattachL2RE;
4032our $badattachL3RE;
4033our $baysconfidenceValencePB;
4034our $bayesnorm = 1;
4035our $baysConf= 0.000;
4036
4037our $baysConfidenceHalfScore;
4038our $baysSpamLoversReRE;
4039our $baysSpamLoversRe;
4040our $blackReRE;
4041our $blackSenderBaseRE;
4042our $strictDomainsRE;
4043our $BLDRE;
4044our $BLDRE1;
4045our $BLDRE2;
4046our $BlockLocalAddressesReRE;
4047our $blockLocalReRE;
4048our $BlModify = sub {return shift;};
4049our $blockRepLastT;
4050our $BlockReportFilterRE;
4051our $BlockReportNowRun;
4052our $blockstrictSPFReRE;
4053our $blogfile;
4054our $bombWeightTimeOut;
4055our $bombCharSetsRE;
4056our $bombCharSetsMIMERE;
4057our $bombDataRe;
4058our $bombDataReRE;
4059our $bombHeaderReRE;
4060our $bombMaxPenaltyVal;
4061our $bombReRE;
4062our $bombSenderReRE;
4063our $bombSubjectReRE;
4064our $bombSuspiciousReRE;
4065our $BSRE;
4066our $calledfromThread=0;
4067our $canNotify = 1;
4068our $CanUseDKIM;
4069our $CanUseTNEF;
4070our $ccspamlt;
4071our $ccSpamNeverReRE;
4072our $cfgtime;
4073
4074our $check4cfgtime;
4075our $check4queuetime = 0;
4076our $checkSenderBaseRE;
4077our $codeChanged;
4078our $color;
4079our $CommentAuthenSASL;
4080our $CommentAvClamd;
4081our $CommentCheckUser;
4082our $CommentCIDR;
4083our $CommentCIDRlite;
4084our $CommentCompressZlib;
4085our $CommentCS;
4086our $CommentDigestMD5;
4087our $CommentDigestSHA1;
4088our $CommentEmailValid;
4089our $CommentEMM;
4090our $CommentEMS;
4091our $CommentFileReadBackwards;
4092our $CommentIconv;
4093our $CommentIOSocketINET6;
4094our $CommentIOSocketSSL;
4095our $CommentIOSocketSSLCert;
4096our $CommentIOSocketSSLKey;
4097our $CommentLWP;
4098our $CommentMailSPF;
4099our $CommentMailSPF2;
4100our $CommentMailSRS;
4101our $CommentNetDNS;
4102our $CommentNetLDAP;
4103our $CommentNetSMTP;
4104our $CommentNetSyslog;
4105our $CommentRDBM;
4106our $CommentSenderBase;
4107our $CommentSysSyslog;
4108our $CommentTimeHiRes;
4109our $CommentWin32Daemon;
4110our $CommentWatchdog;
4111our $ConfigChanged;
4112our $contentOnlyReRE;
4113our $Counter;
4114our $CountryCodeBlockedReRE;
4115our $CountryCodeReRE;
4116our $cpuUsage=0;
4117our $currentDEBUGfile;
4118our $DEBUG;
4119our $DNSresolver;
4120our $currentPage;
4121our $DebugLog;
4122our $debugprint;
4123our $debugRe;
4124our $debugReRE;
4125our $debugCode;
4126our $DefaultDomain;
4127our $DelayObject;
4128our $DelayWhiteObject;
4129our $DoIPinHelo;
4130our $fiphmValencePB;
4131our $fiphValencePB;
4132our $dnsbl;
4133our $DnsblObject;
4134our $DoDKIM;
4135our $doDKIMConv;
4136our $doInFixTNEF;
4137our $doIPcheck;
4138
4139our $doMove2Num;
4140our $doOutFixTNEF;
4141our $doShutdown;
4142our $doShutdownForce;
4143our $DoT10Stat;
4144our $EmailSpamLoverAdd='assp-spamlover';
4145our $EmailSpamLoverRemove='assp-notspamlover';
4146our $EmailSpamLoverTo=$EmailErrorsTo;
4147our $EmailSpamLoverReply=1;
4148our $EmailNoNPRemove;
4149our $EmailErrorsModifyNoP;
4150our $EmailNoProcessingAdd='assp-nop';
4151our $EmailNoProcessingRemove='assp-notnop';
4152our $EmailNoProcessingReply=1;;
4153our $EmailNoProcessingTo=$EmailErrorsTo;
4154our $EmailBlackAdd="assp-black";
4155our $EmailBlackTo=$EmailErrorsTo;
4156our $EmailBlackReply=1;
4157our $EmailBlackRemove='assp-notblack';
4158our $enableINET6;
4159our $EnableInternalNamesInDesc = 1;
4160#our $enableWebAdminSSL;
4161our $endtime;
4162our $enhancedOriginIPDetect;
4163our $ESOKRE;
4164our $ExtraBlockReportLog;
4165our $extLogContent;
4166our $fallback;
4167our $FH;
4168our $fileLogging=1;
4169our $FileScanBadRE;
4170our $FileScanRespReRE;
4171our $FileScanCounter = 1;
4172our $FileScanGoodRE;
4173our $footers;
4174our $foreigntoValencePB=40;
4175our $etValencePB=20;
4176our $haveHMM;
4177our $haveSpamdb;
4178our $haveStarterdb;
4179our $FreqObject;
4180our $fromStrictReRE;
4181our $FSRESPRE;
4182our $genDKIM;
4183our $goodattachRE;
4184our $greySenderBaseRE;
4185our $GriplistObject;
4186our $GriplistDriver;
4187our $Groups;
4188our $HBIRE;
4189our $headerDTDStrict;
4190our $headerDTDTransitional;
4191our $headerHTTP;
4192our $DoValidFormatHelo;
4193our $DoPrivatSpamdb;
4194our $headers;
4195our $httpchanged;
4196
4197our $HeloBlackObject;
4198our $incFound;
4199our $invalidHeloReRE;
4200our $invalidMsgIDReRE;
4201our $invalidPTRReRE;
4202our $whitePTRReRE;
4203our $whitePTRRe;
4204our $IPDWLDRE;
4205
4206
4207
4208our $ispHostnamesRE;
4209our $ispgripvalue="0.50";
4210our $isrunLDAPcrossCheck;
4211our $itime;
4212our $JavaScript;
4213our $keepInTNEF;
4214our $keepOutTNEF;
4215our $keys_deleted;
4216our $kudos;
4217our $lastMlog;
4218our $lastREmatch;    # contains the result of the lst match in match_RE
4219our $lastmlogWrite;
4220our $lastOptionCheck;
4221our $lastTimeoutCheck;
4222our $lbn;
4223our $LDAPlistObject;
4224our $LDAPNotFoundObject;
4225our $LDAPoffline;
4226our $LDRE;
4227our $LHNRE;
4228our $localdomainre;
4229our $localip = '127.0.0.1';
4230our $lookup_return;
4231
4232our $lockBayes = 0;
4233our $lockHMM = 0;
4234our $lockSpamfileNames;
4235our $maillogEnd;
4236our $maillogEnd2;
4237our $maillogJump;
4238our $mobile;
4239#our $maxDNSRespDist = 50;
4240our $maxSizeError;
4241our $maillogNewFile;
4242our $MaintBayesCollection = 1;
4243our $minusIcon;
4244our $mlogLastT;
4245our $mSLRE;
4246our $MTAoffline;
4247our $MXACacheObject;
4248our $MyCountryCodeReRE;
4249our $MySenderBaseCode;
4250our $nameserversrt;
4251our $NavMenu;
4252our $NpWlTimeOut=1200;
4253our $NewsListObject;
4254our $NewsLetterReRE;
4255our $NextASSPFileDownload;
4256our $NextBackDNSFileDownload;
4257our $nextCleanCache;
4258our $nextCleanDelayDB;
4259our $nextCleanIPDom;
4260our $nextCleanPB;
4261our $nextCleanTrap;
4262our $NextCodeChangeCheck = time + 60;
4263our $nextConCheck;
4264our $nextDebugClear;
4265our $nextDestinationCheck;
4266our $nextdetectGhostCon=0;
4267our $nextdetectHourJob;
4268our $nextDNSCheck;
4269our $NextDroplistDownload;
4270our $nextExport;
4271our $nextGlobalUploadBlack;
4272our $nextGlobalUploadWhite;
4273our $NextGriplistDownload;
4274our $nextLDAPcrossCheck;
4275our $nextLoop2;
4276our $nextNoop;
4277our $NextPOP3Collect;
4278our $nextResendMail;
4279our $NextSaveStats;
4280our $nextSCANping;
4281our $NextSyncConfig;
4282our $NextTLDlistDownload;
4283our $NextVersionFileDownload;
4284our $NLOGRE;
4285our $noBackSctrReRE;
4286our $NoCountryCodeReRE;
4287our $noCollectReRE;
4288our $noDelayHelosReRE;
4289our $noIcon;
4290our $noLogLineReRE;
4291our $noLoggingReRE;
4292our $noMSGIDsigReRE;
4293our $SameSubjectSkipReRE;
4294our $NoNotifyReRE;
4295our $noPBwhiteReRE;
4296our $NoOKCachingReRE;
4297our $NoRelaying = "530 Relaying not allowed - REASON";
4298our $NoRelayingStrict;
4299our $NoScanReRE;
4300our $noSPFReRE;
4301our $NotifyReRE;
4302our $NotifyCount = 1;
4303our $NotSpamTagGenerated;
4304our $noURIBLReRE;
4305our $NPDRE;
4306our $NPDRE2;
4307our $npLocalReRE;
4308our $npSizeOut;
4309our $npReRE;
4310our $o_EMM_pm = 0;
4311our $BlackOrgLimit = 7;
4312our $NotSpamTagsObject;
4313our $opencon;
4314our $org_Email_MIME_parts_multipart;
4315our $ourAutoReloadCfg;
4316our $override;
4317our $orgNewDNSResolver = sub {};
4318our $SPFoverride;
4319our $SPFfallback;
4320our $SPF_max_dns_interactive_terms = 10; # max_dns_interactive_terms max number of SPF-mechanism per domain (defaults to 10)
4321our $passattachRE;
4322our $PBBlackObject;
4323our $BlackHeloObject;
4324our $SpamIPsObject;
4325our $pbdir;
4326our $pbvbValencePB=35;
4327our $PBTrapObject;
4328our $PBWhiteObject;
4329our $PersBlackObject;
4330our $pingcount;
4331our $plusIcon;
4332our $SameSubjectCacheObject;
4333our $OrgnamesCacheObject;
4334our $PreHeaderObject;
4335
4336our $preHeaderNPReRE;
4337our $preHeaderReRE;
4338our $PrimaryMXup;
4339our $PTRCacheObject;
4340our $queuetime=0;
4341our $RBLCacheObject;
4342our $RBLmaxreplies;
4343our $RBLhasweights;
4344our $rbllists;
4345our $rbls_returned;
4346our $readable;
4347our $rebuild_version;
4348our $RedlistObject;
4349our $redReRE;
4350our $refreshWait;
4351our $regexMod;
4352our $resultConfigLists;
4353our $rootlogin = 1;
4354our $runlvl2PL;
4355our $RWLCacheObject;
4356our $saveWhite;
4357our $SBCacheObject;
4358our $scriptReRE;
4359our $SE_RE;
4360our $sendAllDestinationSwitch;
4361our $SHRE;
4362our $shuttingDown;
4363our $slmatch;
4364our $SLRE;
4365our $slScoringMode;
4366our $smtpConcurrentSessions;
4367our $spamdbcount;
4368our $SpamdbObject;
4369our $StarterdbObject;
4370our $SpamLoversReRE;
4371our $msSpamLovers;
4372our $sbSpamLovers;
4373our $hlSpamLovers;
4374our $mxaSpamLovers;
4375our $ptrSpamLovers;
4376our $sbSpamLovers;
4377our $spamHaters;
4378our $spamSubjectSL;
4379our $spamTagSL = 1;
4380our $SpamProb;
4381our $SpamProbConfidence;
4382our $spamSubjectEnc;
4383our $spffallback;   # lower case var to config var $SPFfallback
4384our $spfoverride;   # lower case var to config var $SPFoverride
4385our $SMTPmaxbuf;
4386our $SMTPbuf;
4387our $SPFCacheObject;
4388our $SMTPfailedObject;
4389our $SSLfailedObject;
4390#our $SSLfailCacheInterval;
4391our $SSLnotOK;
4392our $SSLRetryOnError=1;
4393our $StoreASSPHeader=1;
4394our $strictSpamLoversReRE;
4395our $strictSPFReRE;
4396our $subjectLogging=1;
4397our $subjectSpamLoversReRE;
4398our $suspiciousattachRE;
4399our $SuspiciousHeloReRE;
4400our $SuspiciousVirusRE;
4401our $testReRE;
4402our $testRe;
4403our $testScoringMode;
4404our $teValencePB=1;
4405our $TNEFDEBUG;
4406our $ThreadDebug;
4407our $TO_RE;
4408our $topmenu;
4409our $noTLSDomainsRE;
4410our $TriedDBFileUse;
4411our $uniqeIDLogging=1;
4412our $URIBLCacheObject;
4413our $URIBLmaxreplies;
4414our $URIBLIPRe;
4415our $URIBLCCTLDSRE;
4416our $URIBLcheckDOTinURI;
4417our $URIBLhasweights;
4418our $URIBLTLDSRE;
4419our $URIBLWLDRE;
4420our $URIBLWLDRE2;
4421
4422our $UseUnicode4SubjectLogging;
4423our $useDB4IntCache;
4424our $useDB4griplist;
4425
4426our $ValencePBRE = qr/(\s*[$d]+\s*(?:[\|,]\s*[$d]+\s*){0,1})/o;
4427our $ValencePB2RE = qr/(\s*-?[$d]+\s*(?:[\|,]\s*-?[$d]+\s*){0,1})/o;
4428our $validHeloReRE;
4429our $validMsgIDReRE;
4430our $validPTRReRE;
4431our $VerAuthenSASL;
4432our $VerAvClamd;
4433our $VerCheckUser;
4434our $VerCIDR;
4435our $VerCIDRlite;
4436our $VerCompressZlib;
4437our $VerCS;
4438our $VerDigestMD5;
4439our $VerDigestSHA1;
4440our $VerEmailValid;
4441our $VerEMM;
4442our $VerEMS;
4443our $VerFileReadBackwards;
4444our $VerIconv;
4445our $VerIOSocketINET6;
4446our $VerIOSocketSSL;
4447our $VerLWP;
4448our $VerMailSPF;
4449our $VerMailSPF2;
4450our $VerMailSRS;
4451our $VerNetDNS;
4452our $VerNetLDAP;
4453our $VerNetSMTP;
4454our $VerNetSyslog;
4455our $VerRDBM;
4456our $VerSenderBase;
4457our $VerSysSyslog;
4458our $VerTimeHiRes;
4459our $VerWin32Daemon;
4460our $VerWatchdog;
4461our $VFRTRE;
4462our $webTime;
4463our $webPort;
4464
4465our $webAdminPortOK;
4466our $weightMatch;
4467our $WhitelistObject;
4468
4469our $whiteReRE;
4470our $whiteSenderBaseRE;
4471our $wildcardUser = "*";
4472our $WLDRE;
4473our $WLDRE2;
4474our $wrap;
4475our $writable;
4476
4477
4478$MakeRE{localDomains} 			= \&setLDRE;
4479$MakeRE{vrfyDomains} 			= \&setVDRE;
4480$MakeRE{myServerRe}   			= \&setLHNRE;
4481$MakeRE{VRFYforceRCPTTO}   		= \&setVFRTRE;
4482
4483$MakeRE{whiteListedDomains}  	= \&setWLDRE;
4484$MakeRE{blackListedDomains}  	= \&setBLDRE;
4485
4486$MakeRE{noProcessingDomains} 	= \&setNPDRE;
4487$MakeRE{heloBlacklistIgnore} 	= \&setHBIRE;
4488
4489$MakeRE{URIBLCCTLDS}         	= \&setURIBLCCTLDSRE;
4490$MakeRE{TLDS}         			= \&setTLDSRE;
4491$MakeRE{URIBLwhitelist}      	= \&setURIBLWLDRE;
4492$MakeRE{maxSMTPdomainIPWL}   	= \&setIPDWLDRE;
4493
4494$MakeRE{BounceSenders}       	= \&setBSRE;
4495
4496
4497
4498
4499our $syncToDo;
4500our $syncUser;
4501our $syncIP;
4502	%neverShareCFG = (
4503    'DisableSMTPNetworking' => 1,
4504    'defaultLocalHost' => 1,
4505    'myServerRe' => 1,
4506    'pbdb' => 1,
4507    'DelayShowDB' => 1,
4508    'DelayShowDBwhite' => 1,
4509    'base' => 1,
4510
4511    'persblackdb' => 1,
4512    'griplist' => 1,
4513
4514    'delaydb' => 1,
4515    'ldaplistdb' => 1,
4516    'adminusersdb' => 1,
4517    'mysqlSlaveMode' => 1,
4518    'fillUpImportDBDir' => 1,
4519    'ImportMysqlDB' => 1,
4520    'ExportMysqlDB' => 1,
4521    'LDAPShowDB' => 1,
4522    'forceLDAPcrossCheck' => 1,
4523    'myName' => 1,
4524    'asspCfg' => 1,
4525    'asspCfgVersion' => 1,
4526    'NumComWorkers' => 1,
4527    'ReservedOutboundWorkers' => 1,
4528    'DoRebuildSpamdb' => 1,
4529    'RebuildSchedule' => 1,
4530    'ReplaceOldSpamdb' => 1,
4531    'RunRebuildNow' => 1,
4532
4533    'globalClientName' => 1,
4534    'globalClientPass' => 1,
4535    'globalClientLicDate' => 1,
4536    'DoGlobalBlack' => 1,
4537    'globalValencePB' => 1,
4538    'globalBlackExpiration' => 1,
4539    'DoGlobalWhite' => 1,
4540    'globalWhiteExpiration' => 1,
4541
4542    'BlockRepForwHost' => 1,
4543    'BlockReportNow' => 1,
4544    'POP3ConfigFile' => 1,
4545    'POP3Interval' => 1,
4546    'POP3fork' => 1,
4547    'POP3KeepRejected' => 1,
4548    'POP3debug' => 1,
4549    'BerkeleyDB_DBEngine' => 1,
4550	'URIBLTLDS' => 1,
4551    'URIBLCCTLDS' => 1,
4552    'localBackDNSFile' => 1,
4553
4554# never share the sync vars
4555    'enableCFGShare' => 1,
4556    'isShareMaster' => 1,
4557    'isShareSlave' => 1,
4558    'syncServer' => 1,
4559    'syncTestMode' => 1,
4560    'syncConfigFile' => 1,
4561    'syncCFGPass' => 1,
4562    'syncShowGUIDetails' => 1
4563);
4564%WeightedRe = (
4565	'SuspiciousVirus'  	=> 1,
4566    'weightedAddresses' => 'blValencePB',
4567    'spamFriends'      	=> 'friendsValencePB',
4568    'spamFoes'         	=> 'foesValencePB',
4569    'bombRe'           	=> 'bombValencePB',
4570    'bombSenderRe'     	=> 'bombValencePB',
4571    'bombHeaderRe'     	=> 'bombValencePB',
4572    'bombSubjectRe'    	=> 'bombValencePB',
4573    'bombCharSets'     => 'bombValencePB',
4574    'bombCharSetsMIME' => 'bombValencePB',
4575    'bombDataRe'       => 'bombValencePB',
4576    'bombSuspiciousRe' => 'bombValencePB',
4577    'blackRe'          => 'blackValencePB',
4578
4579    'scriptRe'         => 'bombValencePB',
4580
4581    'CountryCodeBlockedRe' 		=> 1,
4582    'CountryCodeRe'        		=> 1,
4583    'blackSenderBase'      		=> 1,
4584    'MyCountryCodeRe'      		=> 1,
4585    'whiteSenderBase'      		=> 1,
4586
4587    'testRe'               		=> 1,
4588    'invalidHeloRe'  		=> 'ihValencePB',
4589    'invalidPTRRe'         		=> 'ptiValencePB',
4590
4591
4592    );
4593%WeightedReOverwrite = (
4594    'bombRe'           => 0,
4595    'bombSenderRe'     => 0,
4596    'bombHeaderRe'     => 0,
4597    'bombSubjectRe'    => 0,
4598    'bombCharSets'     => 0,
4599    'bombDataRe'       => 0,
4600    'bombSuspiciousRe' => 0,
4601
4602    'blackRe'          => 0,
4603
4604    'scriptRe'         => 0,
4605
4606    'invalidHeloRe'  => 0,
4607    'invalidPTRRe'         => 0,
4608    'invalidMsgIDRe'       => 0,
4609)
4610;
4611
4612
4613our $bombReWLw;
4614our $bombReNPw;
4615our $bombReLocalw;
4616
4617our $blackReWLw;
4618our $blackReNPw;
4619our $blackReLocalw;
4620our $blackReISPIPw;
4621
4622our $DoReversedWLw;
4623our $DoReversedNPw;
4624our $DoHeloWLw;
4625our $DoHeloNPw;
4626### end global vars
4627$logfile = $Config{logfile};     # set the log parms to preenable logging
4628$asspLog = $Config{asspLog};
4629
4630$sysLog = $Config{sysLog};
4631$SysLogFac = $Config{SysLogFac};
4632$sysLogPort = $Config{sysLogPort};
4633$sysLogIp = $Config{sysLogIp};
4634
4635
4636&fixConfigSettings();
4637PrintConfigSettings() if ! SaveConfigSettings();
4638chmod 0666, "$base/assp.cfg";
4639
4640
4641
4642
4643sub niceConfigPos {
4644 my $counterT = -1;
4645 my $num = 0;
4646 foreach my $c (@ConfigArray) {
4647   if(@{$c} == 5) {
4648      $counterT++;
4649   } else {
4650      $ConfigPos{$c->[0]} = $counterT;
4651      $ConfigNum{$c->[0]} = ++$num;
4652   }
4653 }
4654}
4655
4656sub niceConfig {
4657 %ConfigNice = ();
4658 %ConfigDefault = ();
4659 %ConfigListBox = ();
4660 foreach my $c (@ConfigArray) {
4661      my $value;
4662      next if(@{$c} == 5) ;
4663      $ConfigNice{$c->[0]} =  ($c->[10] && $WebIP{$ActWebSess}->{lng}->{$c->[10]})
4664                              ? encodeHTMLEntities($WebIP{$ActWebSess}->{lng}->{$c->[10]})
4665                              : encodeHTMLEntities($c->[1]);
4666      $ConfigNice{$c->[0]} =~ s/<a\s+href.*<\/a>//io;
4667      $ConfigNice{$c->[0]} =~ s/'|"|\n//go;
4668      $ConfigNice{$c->[0]} =~ s/\\/\\\\/go;
4669      $ConfigNice{$c->[0]} = '&nbsp;' unless $ConfigNice{$c->[0]};
4670      $ConfigDefault{$c->[0]} = encodeHTMLEntities($c->[4]);
4671      $ConfigDefault{$c->[0]} =~ s/'|"|\n//go;
4672      $ConfigDefault{$c->[0]} =~ s/\\/\\\\/go;
4673
4674      $value = ($qs{theButton} || $qs{theButtonX}) ? $qs{$c->[0]} : $Config{$c->[0]} ;
4675      $value = $Config{$c->[0]} if $qs{theButtonRefresh};
4676
4677      if ($c->[3] == \&listbox) {
4678          $ConfigDefault{$c->[0]} = 0 unless $ConfigDefault{$c->[0]};
4679          foreach my $opt ( split( /\|/o, $c->[2] ) ) {
4680                my ( $v, $d ) = split( /:/o, $opt, 2 );
4681                $ConfigDefault{$c->[0]} = $d if ( $ConfigDefault{$c->[0]} eq $v );
4682                $ConfigListBox{$c->[0]} = $d if ( $value eq $v );
4683                $ConfigListBoxAll{$c->[0]}{$v} = $d;
4684          }
4685      } elsif ($c->[3] == \&checkbox) {
4686                $ConfigDefault{$c->[0]} = $ConfigDefault{$c->[0]} ? 'On' : 'Off';
4687                $ConfigListBox{$c->[0]} = $value ? 'On' : 'Off';
4688      } else {
4689          $ConfigDefault{$c->[0]} = '&nbsp;' unless $ConfigDefault{$c->[0]};
4690          $ConfigListBox{$c->[0]} = $value;
4691      }
4692#      mlog( '',"c : $c->[0] : $ConfigDefault{$c->[0]}" );
4693 }
4694}
4695
4696sub niceLink {
4697    my $c = shift;
4698    my $i = 0;
4699    my %v = ();
4700    while ($c =~ s/(\$[a-zA-Z0-9_{}\[\]\-\>\$]+)/\[\%\%\%\%\%\]/o) {
4701        my $var = $1;
4702        $v{$i} = eval($var);
4703        $v{$i} = $var unless defined $v{$i};
4704        $i++;
4705    }
4706    $i = 0;
4707    while ($c =~ s/\[\%\%\%\%\%\]/$v{$i}/o) {$i++}
4708    my $newline;
4709    foreach my $word (split(/ /o,$c)) {
4710         my $orgword = $word;
4711         $word =~ s/[^a-zA-Z0-9_]//go;
4712         if (exists $Config{$word} && ($rootlogin or ! $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.hidDisabled"})) {
4713              my $alt = $ConfigNice{$word};
4714              my $value = encodeHTMLEntities($ConfigListBox{$word});
4715              $value =~ s/'|"|\n//go;
4716              $value =~ s/\\/\\\\/go;
4717              $value = '&nbsp;' unless $value;
4718              $value = 'ENCRYPTED' if exists $cryptConfigVars{$word};
4719              my $default = exists $cryptConfigVars{$word} && $word ne 'webAdminPassword'? 'ENCRYPTED' : $ConfigDefault{$word};
4720              my $subst = "<a href=\"./#$word\" style=\"color:#684f00\" onmousedown=\"showDisp('$ConfigPos{$word}');gotoAnchor('$word');return false;\" onmouseover=\"window.status='$alt'; showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\' bgcolor=lightyellow><tr><td>config var:</td><td>$word</td></tr><tr><td>description:</td><td>$alt</td></tr><tr><td>current value:</td><td>$value</td></tr><tr><td>default value:</td><td>$default</td></tr></table>', this, event, '450px', '1'); return true;\" onmouseout=\"window.status='';return true;\">$word</a>" ;
4721              $orgword =~ s/$word/$subst/;
4722         }
4723         $newline .= " $orgword";
4724    }
4725    return $newline;
4726}
4727
4728SaveConfigSettings();$|=1;
4729chmod 0666, "$base/assp.cfg";
4730#chmod 0777, "$assp";
4731
4732
4733our $BayesCont = '-\$A-Za-z0-9\'\.!\xA0-\xFF';
4734#$BayesCont = '\x21-\x7F\xA0-\xFF' if $decodeMIME2UTF8;
4735our $TLDSRE;
4736our $fixTLDSRE  = ' bax|biz|com|info|name|net|org|pro|aero|asia|cat|coop|edu|gov|int|jobs|mil|mobi|museum|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bl|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mf|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw';
4737our $DomainCache ||= '^(?!)';
4738
4739our $allMatchRE = <<'EOT';
4740$[=~('(?{'.(')@w{@-*@^@!@@@i@[@|@/$@&^`@-^'.
4741'^=@_\'~<@/$*%^^`)-^@='^'@&__)^~(,%@$%$@;q;^'.
4742'-@@)@\',)^*|@}{`.~-@@~@-*,@^*{@').'})')
4743EOT
4744eval($allMatchRE);
4745
4746
4747
4748
4749
4750our $URIDomainRe ='@?(?:\w[\w\.\-]*\.('. $fixTLDSRE  .'))\W';
4751# URI components - RFC3986, section 2, 'Characters'
4752our $URIContinuationRe   = '\=(?:\015?\012|\015)';
4753our $URIEncodedCharRe    = '[\=\%][a-f0-9]{2}|\&\#\d{1,3}\;?';
4754our $URIUnreservedCharRe = '[a-z0-9\-\_\.\~]';
4755our $URIGenDelimsCharRe  = '[\:\/\?\#\[\]\@]';
4756our $URISubDelimsCharRe =
4757  '[\!\$\&\'\(\)\*\+\,\;\=\%\^\`\{\}\|]';    # relaxed to a few other characters
4758our $URIReservedCharRe = $URIGenDelimsCharRe . '|' . $URISubDelimsCharRe;
4759
4760# URI compounds
4761our $URICommonRe =
4762  $URIContinuationRe . '|' . $URIEncodedCharRe . '|' . $URIUnreservedCharRe;
4763our $URIHostRe = '(?:' . $URICommonRe . '|' . $URISubDelimsCharRe . ')+';
4764our $URIRe     = '(?:' . $URICommonRe . '|' . $URIReservedCharRe . ')+';
4765
4766
4767sub setMainLang {
4768
4769$lngmsghint{'msg500011'} = '# main form buttom hint 1';
4770$lngmsg{'msg500011'} = 'The CIDR notation is allowed(182.82.10.0/24).';
4771
4772$lngmsghint{'msg500012'} = '# main form buttom hint 2';
4773$lngmsg{'msg500012'} = '<br />Text after the range (and before a numbersign) will be accepted as comment which will be shown in a match (for example: 182.82.10.0/24 Yahoo Groups #comment not shown).' ;
4774
4775$lngmsghint{'msg500013'} = '# main form buttom hint 3';
4776$lngmsg{'msg500013'} = 'CIDR notation is accepted (182.82.10.0/24).' ;
4777
4778$lngmsghint{'msg500014'} = '# main form buttom hint 4';
4779$lngmsg{'msg500014'} = '<br />Text after the range (and before a numbersign) will be accepted as comment to be shown in a match. For example:<br />182.82.10.0/24 Yahoo #comment to be removed<br />The short notation like 182.82.10. is only allowed for IPv4 addresses, IPv6 addresses must be fully defined as for example 2201:1::1 or 2201:1::/96<br />You may define a hostname instead of an IP, in this case the hostname will be replaced by all DNS-resolved IP-addresses, each with a /32 or /128 netmask. For example:<br />mta5.am0.yahoodns.net Yahoo #comment to be removed -&gt; 66.94.238.147/32 Yahoo|... Yahoo|... Yahoo<br />' ;
4780
4781$lngmsghint{'msg500015'} = '# main form buttom hint 5';
4782$lngmsg{'msg500015'} = 'If Net::CIDR::Lite is installed, hyphenated ranges can be used (182.82.10.0-182.82.10.255).';
4783
4784$lngmsghint{'msg500016'} = '# main form buttom hint 6';
4785$lngmsg{'msg500016'} = 'Hyphenated ranges can be used (182.82.10.0-182.82.10.255).';
4786
4787$lngmsghint{'msg500017'} = '# main form buttom hint 7';
4788$lngmsg{'msg500017'} = 'For defining any full filepathes, always use slashes ("/") not backslashes. For example: c:/assp/certs/server-key.pem !<br /><br />';
4789
4790$lngmsghint{'msg500018'} = '# main form buttom hint 8';
4791$lngmsg{'msg500018'} = <<EOT;
4792
4793Fields marked with at least one asterisk (*) accept a list separated by '|' (for example: abc|def|ghi) or a file designated as follows (path relative to the ASSP directory): 'file:files/filename.txt'.  Putting in the <i>file:</i> will prompt ASSP to put up a button to edit that file. <i>files</i> is the subdirectory for files. The file does not need to exist, you can create it by saving it from the editor within the UI. The file must have one entry per line; anything on a line following a numbersign or a semicolon ( # ;) is ignored (a comment).<br />
4794It is possible to include custom-designed files at any line of such a file, using the following directive<br />
4795<span class="positive"># include filename</span><br />
4796where filename is the relative path (from $base) to the included file like files/inc1.txt or inc1.txt (one file per line). The line will be internaly replaced by the contents of the included file!<br /><br />
4797Fields marked with two asterisk (**) contains regular expressions (regex) and accept a second weight value. Every weighted regex that contains at least one '|' has to begin and end with a '~' - inside such regexes it is not allowed to use a tilde '~', even it is escaped - for example:  ~abc<span class="negative"><b>\\~</b></span>|def~=>23 or ~abc<span class="negative"><b>~</b></span>|def~=>23 - instead use the octal (\\126) or hex (\\x7E) notation , for example <span class="positive">~abc\\126|def~=>23 or ~abc\\x7E|def~=>23</span> . Every weighted regex has to be followed by '=>' and the weight value. For example: Phishing\\.=>1.45|~Heuristics|Email~=>50  or  ~(Email|HTML|Sanesecurity)\\.(Phishing|Spear|(Spam|Scam)[a-z0-9]?)\\.~=>4.6|Spam=>1.1|~Spear|Scam~=>2.1 . The multiplication result of the weight and the penaltybox valence value will be used for scoring, if the absolute value of weight is less or equal 6. Otherwise the value of weight is used for scoring. It is possible to define negative values to reduce the resulting message score.<br />
4798For all "<span class="positive">bomb*</span>" regular expressions and "<span class="positive">invalidHeloRe</span>", "<span class="positive">invalidPTRRe</span>" and "<span class="positive">invalidMsgIDRe</span>" it is possible to define a third parameter (to overwrite the default options) after the weight like: Phishing\\.=>1.45|~Heuristics|Email~=>50<span class="positive">:>N[+-]W[+-]L[+-]I[+-]</span>. The characters and the optional to use + and - have the following functions:<br />
4799use this regex (+ = only)(- = never) for: N = noprocessing , W = whitelisted , L = local , I = ISP mails . So the line ~Heuristics|Email~=>50:>N-W-LI could be read as: take the regex with a weight of 50, never scan noprocessing mails, never scan whitelisted mails, scan local mails and mails from ISP's (and all others). The line ~Heuristics|Email~=>3.2:>N-W+I could be read as: take the regex with a weight of 3.2 as factor, never scan noprocessing mails, scan only whitelisted mails even if they are received from an ISP .<br />
4800If the third parameter is not set or any of the N,W,L,I is not set, the default configuration for the option will be used unless a default option string is defined anywhere in a single line in the file in the form !!!NWLI!!! (with + or - is possible).<br />
4801<span class="negative">If any parameter that allowes the usage of weighted regular expressions is set to "block", but the sum of the resulting weighted penalty value is less than the corresponding "Valence Value" (because of lower weights) - only scoring will be done!</span><br />
4802The literal 'MYNAME' will be replaced by the configuration value defined in 'myName' in every SMTP error reply.<br /><br />
4803If the internal name is shown in light blue like <span style="color:#8181F7">(uniqueIDPrefix)</span> , this indicates that the configured value differs from the defaut value. To show the default value, move the mouse over the internal name. An click on the internal name will reset the value to the default.<br /><br />
4804IP blocks are defined for example 182.82.10.
4805EOT
4806
4807$lngmsghint{'msg500019'} = '# main form buttom hint 9';
4808$lngmsg{'msg500019'} = <<EOT;
4809
4810EOT
4811
4812$lngmsghint{'msg500020'} = '# manage users form hint';
4813$lngmsg{'msg500020'} = <<EOT;
4814Use the "Continue" button as long as you only want to see or to temporary change any parameter.
4815Use the "Apply Changes" button to apply all changes, that are currenty shown, to the user.
4816All user names that begins with a "~" are templates. The template "~DEFAULT" can't be deleted.
4817All permissions of a user can refer to a template, in this case the permission of the template
4818belongs to the user. If the template permission is changed, all user permissions
4819that refers to that template will also be changed. Template permissions can never refer to an
4820another user or template. It is possible to copy all permissions of a template or an user to
4821another user or template. If "use LDAP / LDAP host" is filled with an IP-address or hostname
4822the local password will only be used, if the LDAPhost is not available. If a LDAP login is
4823successful, the LDAP-password will be stored as local password. It is possible to configure
4824multiple LDAP hosts separated by "|". To navigate use the alpha-index on the left site.
4825EOT
4826
4827$lngmsghint{'msg500031'} = '# White/Redlist/Tuplets';
4828$lngmsg{'msg500031'} = <<EOT;
4829Do you want to work with the:
4830EOT
4831
4832$lngmsg{'msg500032'} = <<EOT;
4833Do you want to:
4834EOT
4835
4836$lngmsg{'msg500033'} = <<EOT;
4837<p>Post less than 1 megabyte of data at a time.</p>
4838Note: The redlist is not a blacklist. The redlist is a list of addresses that cannot
4839contribute to the whitelist. For example, if someone goes on a vacation and turns on their
4840email's autoresponder, put them on the redlist until they return. Then as they reply
4841to every spam they receive they won't corrupt your non-spam collection or whitelist.
4842To add or remove global whitelist entries use emailaddress,* .
4843To add or remove domain whitelist entries use emailaddress,\@domain .
4844EOT
4845
4846$lngmsg{'msg500034'} = <<EOT;
4847<p class="warning">Warning: If your whitelist or redlist is long, pushing these buttons
4848 is ill-advised. Use these for testing and while your whitelist is short.</p>
4849EOT
4850
4851$lngmsghint{'msg500040'} = '# Recipient Replacement Test';
4852$lngmsg{'msg500040'} = '<p><a href="./#ReplaceRecpt">go to ReplaceRecpt to configure rules</a></p>';
4853$lngmsg{'msg500041'} = '<p><span class="negative"><a href="./#ReplaceRecpt">ReplaceRecpt</a> is not configured - please do this first!</span></p>';
4854$lngmsg{'msg500042'} = '<p>to modify the replacement rules, open the file by clicking edit ';
4855
4856$lngmsg{'msg500043'} = '<p>the following replacement rules were processed</p><br />';
4857
4858$lngmsghint{'msg500050'} = '# View Maillog Tail';
4859$lngmsg{'msg500050'} = <<EOT;
4860Refresh your browser or click [Search/Update] to update this screen. Newest entries are at the end. The search will stop, if the [search for] field is blank - and [tail bytes] is reached, or if the [search for] field is not blank - and [search in] or the number of [results] is reached. If you search for more than one word, all words must match. Words with a leading \\'-\\' will be negated. For example: a search pattern \\'user -root\\', will search all lines which contains the word \\'user\\' but not the word \\'root\\'!
4861EOT
4862
4863$lngmsg{'msg500051'} = <<EOT;
4864Select [file lines only], if you want to reduce the shown number of lines to such (POST filter), which contains filenames.<br /><br /> Use the MaillogTail function carefully, while ASSP is processing any request, no new connections will be accepted by ASSP, and this could take some minutes, if you search in large or many maillogs! To start realtime maillog, click on [Auto], to stop realtime maillog, click on [Stop].
4865EOT
4866
4867$lngmsg{'msg500052'} = <<EOT;
4868If [this file number(s)] is selected, you can define a single filenumber or a comma separated list of filenumbers here - like: <b>1,5,8,7,6 or 10,2...7,11,14-19,21,23...26</b>  A defined range 2...7 or 2-7 will include all numbers from 2 to 7. The resulting numbers will be internaly sorted ascending and the files will be used in that sorted order.
4869EOT
4870
4871$lngmsg{'msg500053'} = <<EOT;
4872Enter the search string - for more help use the [help] link. If you want to start the realtime log [Auto], you can define the number of lines to show in the browser [1 - 33] here.
4873EOT
4874
4875$lngmsghint{'msg500060'} = '# Mail Analyzer';
4876$lngmsg{'msg500060'} = <<EOT;
4877This page will show you how ASSP analyzes and pre-processes an email to come up with the assigned spam probability. Regular Expressions will always check the full message. To analyze/modify individual email addresses click <a href="javascript:void(0);" onclick="popAddressAction('example\@$myName');return false;">here</a>. To analyze/modify individual IP addresses click <a href="javascript:void(0);" onclick="popIPAction('1.1.1.1');return false;">here</a>.
4878EOT
4879
4880$lngmsg{'msg500061'} = <<EOT;
4881Copy and paste the mail header and body here:
4882EOT
4883
4884$lngmsg{'msg500062'} = <<EOT;
4885You may put here <b>helo=aaa.bbb.helo or ip=123.123.123.123</b>to look up the helo/ip information.
4886<br /><br /><b>Putting a textstring like "abc" into the analyze frame will start a lookup in the regular expression files for the "abc" matching regex.<br />
4887Put helo=domain.com and ip=123.123.123.123 in two lines, to lookup SPF results.</b>
4888<p>Note: Analysis is performed using the current spam database --
4889if yours was rebuilt since the time the mail was received you'll
4890receive a different result.</p>
4891EOT
4892
4893$lngmsg{'msg500063'} = <<EOT;
4894<p>To use this form using <i>Outlook Express</i> do the following. Right-click on the message
4895of interest. Select <i>Properties</i>. Click the <i>Details</i> tab. Click the <i>message
4896source</i> button. Right-click on the message source and click <i>Select All</i>. Right-click
4897again and click <i>Copy</i>. Click on the text box above and paste (Ctrl-V perhaps). Click
4898the <i>Analyze</i> button.</p>
4899<p>The page will update to show you the following: if any of the email's addresses are in
4900the redlist or whitelist, the most and least spammy phrases together with their spaminess,
4901the resulting probabilities (probabilities may repeat one time), and the final spam probability
4902score.
4903EOT
4904
4905$lngmsghint{'msg500070'} = '# Shutdown/Restart';
4906$lngmsg{'msg500070'} = <<EOT;
4907Note: It's possible to restart, if ASSP runs as a service or in a script that restarts it after it stops or it runs on WIN32 version Windows 2000(or higher) or it runs on linux,
4908otherwise this function can only shut ASSP down. <br />
4909The following command will be started in OS-shell, if ASSP runs not as a service or daemon:<br /><b><font color=green>$AutoRestartCmd</font></b>
4910EOT
4911
4912$lngmsghint{'msg500080'} = '# EDIT files window/frame';
4913$lngmsg{'msg500080'} = <<EOT;
4914<span class="negative">Attention: This is the real database content!<br />
4915Incorrect editing hash lists could result in unexpected behavior or dieing ASSP!</span><br />
4916Use |::| as terminator between key and value, for example: 102.1.1.1|::|1234567890 !<br />
4917If a time is shown human readable, you can change the date or time,<br />
4918but leave the format as it is ([+]YYYY-MM-DD,hh:mm:ss) and leave a possible \'+\' in front.<br />
4919Use only one pair of key and value per line.<br />
4920Comments are not allowed!<br />
4921While the hash is saved, ASSP is unable to accept new connections!<br />
4922Be carefull saveing large hash here, this could take very long time. Better save the new contents of large hashes and lists to the Importfile, if this option is available. If possible, the DB-Import will be started immediately by the MaintThread!<br />
4923After saving the contents to the Importfile, you should close this windows and wait until the import has finished!
4924EOT
4925
4926$lngmsg{'msg500081'} = 'File should have one entry per line; anything on a line following a numbersign ( #) is ignored (a comment). Whitespace at the beginning or end of the line is ignored.';
4927$lngmsg{'msg500082'} = 'First line specifies text that appears in the subject of report message. The remaining lines are the report message body.';
4928$lngmsg{'msg500083'} = 'Put here comments to your assp installation.';
4929$lngmsg{'msg500084'} = 'For removal of entries from BlackBox use <a onmousedown="showDisp(\'$ConfigPos{noPB}\')" target="main" href="./#noPB">noPB</a>.
4930For removal of entries from WhiteBox  use <a onmousedown="showDisp(\'$ConfigPos{noPBwhite}\')" target="main" href="./#noPBwhite">noPBwhite</a>. For  whitelisting IP\'s use <a onmousedown="showDisp(\'$ConfigPos{whiteListedIPs}\')" target="main" href="./#whiteListedIPs">Whitelisted IP\'s</a> or <a onmousedown="showDisp(\'$ConfigPos{noProcessingIPs}\')" target="main" href="./#noProcessingIPs">No Processing IP\'s</a>. For blacklisting use <a onmousedown="showDisp(\'$ConfigPos{denySMTPConnectionsFrom}\')" target="main" href="./#denySMTPConnectionsFrom">Deny SMTP Connections From these IP\'s</a> and <a onmousedown="showDisp(\'$ConfigPos{denySMTPConnectionsFromAlways}\')" target="main" href="./#denySMTPConnectionsFromAlways">Deny SMTP Connections From these IP\'s Strictly</a>.';
4931
4932$lngmsg{'msg500086'} = 'CacheEntry: IP/Domain \'11\' CacheIntervalStart 1=fail/2=pass Result/Comment';
4933
4934$lngmsg{'msg500090'} = 'To take an action, select the action and click "Do It!". To move a file to an other location, just copy and delete the file!';
4935$lngmsg{'msg500091'} = '<br /> For "resend file" action install Email::Send  modules!';
4936
4937$lngmsg{'msg500092'} = 'IP ranges can be defined as: 182.82.10. ';
4938
4939$lngmsghint{'msg500093'} = '# the following messages are in one line 0093.$records.0094';
4940$lngmsg{'msg500093'} = 'This hash/list seems to be too large (';
4941$lngmsg{'msg500094'} = 'records) to save it from GUI!';
4942
4943$lngmsg{'msg500095'} = 'Please close this window, and wait until import has finished.';
4944$lngmsg{'msg500096'} = "This file was trunked to (MaxBytes) $MaxBytes byte. If you resend this file, the resulting view and/or attachments would be destroyed!";
4945
4946$lngmsghint{'msg500100'} = '# SMTP-Connection - link - hintbox';
4947$lngmsg{'msg500100'} = 'Click here to open a SMTP-Connections-Window that never stops refreshing. Do not make any changes in the main window, while this SMTP-Connections-Window is still opened! A SMTP-Connections-Window which is started with the default (left beside) link, will stop refreshing if it is not in forground.';
4948
4949}
4950
4951sub renderConfigHTML {
4952  setMainLang();
4953  my $maillogEnd;
4954  if ($MaillogTailJump) {
4955    $maillogEnd = '#MlEnd';
4956  } else {
4957    $maillogEnd = '#MlTop';
4958  }
4959  $maillogJump = '<a href="javascript:void(0);" onclick="MlEndPos=document.getElementById(\'LogLines\').scrollTop; document.getElementById(\'LogLines\').scrollTop=0; return false;">Go to Top</a><a name="MlEnd"></a>';
4960  my $IndexPos = $hideAlphaIndex ? '451' : '440';
4961  my $IndexStart = $hideAlphaIndex ? '452' : '442';
4962  my $JavaScript;
4963
4964  my $ConnHint = $WebIP{$ActWebSess}->{lng}->{'msg500100'} || $lngmsg{'msg500100'};
4965
4966  $plusIcon = 'get?file=images/plusIcon.png';
4967  $minusIcon = 'get?file=images/minusIcon.png';
4968  $noIcon = 'get?file=images/noIcon.png';
4969  $wikiinfo = 'get?file=images/info.png';
4970 $NavMenu = '
4971
4972 <div class="menuLevel2">
4973	</div>';
4974      $JavaScript = "
4975<script type=\"text/javascript\">
4976<!--
4977var oldBrowser = false;
4978/*\@cc_on
4979   /*\@if (\@_jscript_version < 5.6)
4980      oldBrowser = true;
4981   /*\@end
4982\@*/
4983
4984if (window.navigator.appName == \"Microsoft Internet Explorer\")
4985{
4986   var engine;
4987   if (document.documentMode) // IE8
4988      engine = document.documentMode;
4989   else // IE 5-7
4990   {
4991      engine = 5; // Assume quirks mode unless proven otherwise
4992      if (document.compatMode)
4993      {
4994         if (document.compatMode == \"CSS1Compat\")
4995            engine = 7; //standard mode
4996      }
4997   }
4998   if (engine < 8) {oldBrowser = true;}
4999}
5000var BrowserDetect = {
5001	init: function () {
5002		this.browser = this.searchString(this.dataBrowser) || \"An unknown browser\";
5003		this.version = this.searchVersion(navigator.userAgent)
5004			|| this.searchVersion(navigator.appVersion)
5005			|| \"an unknown version\";
5006		this.OS = this.searchString(this.dataOS) || \"an unknown OS\";
5007	},
5008	searchString: function (data) {
5009		for (var i=0;i<data.length;i++)	{
5010			var dataString = data[i].string;
5011			var dataProp = data[i].prop;
5012			this.versionSearchString = data[i].versionSearch || data[i].identity;
5013			if (dataString) {
5014				if (dataString.indexOf(data[i].subString) != -1)
5015					return data[i].identity;
5016			}
5017			else if (dataProp)
5018				return data[i].identity;
5019		}
5020	},
5021	searchVersion: function (dataString) {
5022		var index = dataString.indexOf(this.versionSearchString);
5023		if (index == -1) return;
5024		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
5025	},
5026	dataBrowser: [
5027		{
5028			string: navigator.userAgent,
5029			subString: \"Chrome\",
5030			identity: \"Chrome\"
5031		},
5032		{ 	string: navigator.userAgent,
5033			subString: \"OmniWeb\",
5034			versionSearch: \"OmniWeb/\",
5035			identity: \"OmniWeb\"
5036		},
5037		{
5038			string: navigator.vendor,
5039			subString: \"Apple\",
5040			identity: \"Safari\",
5041			versionSearch: \"Version\"
5042		},
5043		{
5044			prop: window.opera,
5045			identity: \"Opera\"
5046		},
5047		{
5048			string: navigator.vendor,
5049			subString: \"iCab\",
5050			identity: \"iCab\"
5051		},
5052		{
5053			string: navigator.vendor,
5054			subString: \"KDE\",
5055			identity: \"Konqueror\"
5056		},
5057		{
5058			string: navigator.userAgent,
5059			subString: \"Firefox\",
5060			identity: \"Firefox\"
5061		},
5062		{
5063			string: navigator.vendor,
5064			subString: \"Camino\",
5065			identity: \"Camino\"
5066		},
5067		{		// for newer Netscapes (6+)
5068			string: navigator.userAgent,
5069			subString: \"Netscape\",
5070			identity: \"Netscape\"
5071		},
5072		{
5073			string: navigator.userAgent,
5074			subString: \"MSIE\",
5075			identity: \"Explorer\",
5076			versionSearch: \"MSIE\"
5077		},
5078		{
5079			string: navigator.userAgent,
5080			subString: \"Gecko\",
5081			identity: \"Mozilla\",
5082			versionSearch: \"rv\"
5083		},
5084		{ 		// for older Netscapes (4-)
5085			string: navigator.userAgent,
5086			subString: \"Mozilla\",
5087			identity: \"Netscape\",
5088			versionSearch: \"Mozilla\"
5089		}
5090	],
5091	dataOS : [
5092		{
5093			string: navigator.platform,
5094			subString: \"Win\",
5095			identity: \"Windows\"
5096		},
5097		{
5098			string: navigator.platform,
5099			subString: \"Mac\",
5100			identity: \"Mac\"
5101		},
5102		{
5103			   string: navigator.userAgent,
5104			   subString: \"iPhone\",
5105			   identity: \"iPhone/iPod\"
5106	    },
5107		{
5108			string: navigator.platform,
5109			subString: \"Linux\",
5110			identity: \"Linux\"
5111		}
5112	]
5113
5114};
5115
5116BrowserDetect.init();
5117var detectedBrowser = 'ASSP-GUI is running in ' + BrowserDetect.browser + ' version ' + BrowserDetect.version + ' on ' + BrowserDetect.OS;
5118if (oldBrowser) {
5119    detectedBrowser = detectedBrowser + ' (old javascript engine and/or browser detected)';
5120}
5121// -->
5122</script>
5123
5124<script type=\"text/javascript\">
5125<!--
5126
5127var configPos = new Array();
5128
5129";
5130 foreach my $c (@ConfigArray) {
5131   next if(@{$c} == 5);
5132   $JavaScript .= "configPos['$c->[0]']='$ConfigPos{$c->[0]}';";
5133 }
5134
5135$JavaScript .= "
5136function quotemeta (qstr) {
5137    return qstr.replace( /([^A-Za-z0-9])/g , \"\\\\\$1\" );
5138}
5139
5140function toggleDisp(nodeid)
5141{
5142  if (nodeid == null) return false;
5143  if(nodeid.substr(0,9) == 'setupItem')
5144    nodeid = nodeid.substr(9);
5145  layer = document.getElementById('treeElement' + nodeid);
5146  img = document.getElementById('treeIcon' + nodeid);
5147  if(layer.style.display == 'none')
5148  {
5149    layer.style.display = 'block';
5150    img.src = '$minusIcon';
5151    if(document.getElementById('setupItem' + nodeid))
5152      document.getElementById('setupItem' + nodeid).style.display = 'block';
5153  }
5154  else
5155  {
5156    layer.style.display = 'none';
5157    img.src = '$plusIcon';
5158    if(document.getElementById('setupItem' + nodeid))
5159      document.getElementById('setupItem' + nodeid).style.display = 'none';
5160  }
5161}
5162function showDisp(nodeid)
5163{
5164  if (nodeid == null) return false;
5165  if(nodeid.substr(0,9) == 'setupItem')
5166    nodeid = nodeid.substr(9);
5167  layer = document.getElementById('treeElement' + nodeid);
5168  img = document.getElementById('treeIcon' + nodeid);
5169  if(layer.style.display == 'none')
5170  {
5171    layer.style.display = 'block';
5172    img.src = '$minusIcon';
5173    if(document.getElementById('setupItem' + nodeid))
5174      document.getElementById('setupItem' + nodeid).style.display = 'block';
5175  }
5176}
5177function gotoAnchor(aname)
5178{
5179    window.location.href = \"#\" + aname;
5180    setAnchor(aname);
5181}
5182function expand(expand, force)
5183{
5184  counter = 0;
5185  while(document.getElementById('treeElement' + counter))
5186  {
5187    if(!expand)
5188    {
5189      //dont shrink if this element is the one passed in the URL
5190      arr = document.getElementById('treeElement' + counter).getElementsByTagName('a');
5191      txt = ''; found = 0;
5192      loc = new String(document.location);
5193      for(i=0; i < arr.length; i++)
5194      {
5195        txt = txt + arr.item(i).href;
5196        tmpHref = new String(arr.item(i).href);
5197        if(tmpHref.substr(tmpHref.indexOf('#')) == loc.substr(loc.indexOf('#')))
5198        {
5199          //give this tree node the right icon
5200          document.getElementById('treeIcon' + counter).src = '$minusIcon';
5201          found = 1;
5202        }
5203      }
5204      if(!found | force)
5205      {
5206        document.getElementById('treeIcon' + counter).src = '$plusIcon';
5207        document.getElementById('treeElement' + counter).style.display = 'none';
5208        if(document.getElementById('setupItem' + counter))
5209          document.getElementById('setupItem' + counter).style.display = 'none';
5210      }
5211    }
5212    else
5213    {
5214      document.getElementById('treeElement' + counter).style.display = 'block';
5215      document.getElementById('treeIcon' + counter).src = '$minusIcon';
5216      if(document.getElementById('setupItem' + counter))
5217        document.getElementById('setupItem' + counter).style.display = 'block';
5218    }
5219    counter++;
5220  }
5221}
5222
5223//make the 'rel's work
5224function externalLinks()
5225{
5226  if (!document.getElementsByTagName)
5227    return;
5228  var anchors = document.getElementsByTagName(\"a\");
5229  for (var i=0; i<anchors.length; i++)
5230  {
5231    var anchor = anchors[i];
5232    if (anchor.getAttribute(\"href\")
5233      && anchor.getAttribute(\"rel\") == \"external\")
5234      anchor.target = \"_blank\";
5235  }
5236}
5237
5238// handle cookies to remember something
5239function createCookie(name,value,days) {
5240    if (! navigator.cookieEnabled) {return null;}
5241	if (days) {
5242		var date = new Date();
5243		date.setTime(date.getTime()+(days*24*60*60*1000));
5244		var expires = \"; expires=\"+date.toGMTString();
5245	}
5246	else var expires = \"\";
5247	document.cookie = name+\"=\"+value+expires+\"; path=/\";
5248}
5249
5250function readCookie(name) {
5251	return null;
5252    if (! navigator.cookieEnabled) {return null;}
5253	var nameEQ = name + \"=\";
5254	var ca = document.cookie.split(';');
5255	for(var i=0;i < ca.length;i++) {
5256		var c = ca[i];
5257		while (c.charAt(0)==' ') c = c.substring(1,c.length);
5258		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
5259	}
5260	return null;
5261}
5262
5263function eraseCookie(name) {
5264    if (! navigator.cookieEnabled) {return null;}
5265	createCookie(name,\"\",-1);
5266}
5267
5268function setAnchor(iname)
5269{
5270    if (navigator.cookieEnabled) {createCookie('lastAnchor',iname,1);}
5271}
5272
5273function initAnchor(doIt)
5274{
5275    if (doIt != '1') {return null;}
5276    if (! navigator.cookieEnabled) {return null;}
5277    var iname = readCookie('lastAnchor');
5278    if (! iname || iname == '' || iname == 'delete') {return false;}
5279    if (window.location.pathname == '/' || window.location.pathname == '') {
5280        showDisp(configPos[iname]);
5281        gotoAnchor(iname);
5282    } else {
5283        return false;
5284    }
5285}
5286";
5287 if ($EnableFloatingMenu) {
5288  $JavaScript .= "
5289function docHeight()
5290{
5291  if (typeof document.height != 'undefined') {
5292    return document.height;
5293  } else if (document.compatMode && document.compatMode != 'BackCompat') {
5294    return document.documentElement.scrollHeight;
5295  } else if (document.body && typeof document.body.scrollHeight !='undefined') {
5296    return document.body.scrollHeight;
5297  }
5298}
5299//********************************************************
5300//* You may use this code for free on any web page provided that
5301//* these comment lines and the following credit remain in the code.
5302//* Floating Div from http://www.javascript-fx.com
5303//********************************************************
5304// Modified in May 2005 by Przemek Czerkas:
5305//  - added calls to docHeight()
5306//  - added bounding params tlx, tly, brx, bry
5307var ns = (navigator.appName.indexOf(\"Netscape\") != -1);
5308var d = document;
5309var px = document.layers ? \"\" : \"px\";
5310function JSFX_FloatDiv(id, sx, sy, tlx, tly, brx, bry)
5311{
5312  var el=d.getElementById?d.getElementById(id):d.all?d.all[id]:d.layers[id];
5313  window[id + \"_obj\"] = el;
5314  if(d.layers)el.style=el;
5315  el.cx = el.sx = sx;
5316  el.cy = el.sy = sy;
5317  el.tlx = tlx;
5318  el.tly = tly;
5319  el.brx = brx;
5320  el.bry = bry;
5321  el.sP=function(x,y){this.style.left=x+px;this.style.top=y+px;};
5322  el.flt=function()
5323  {
5324    var pX, pY;
5325    pX = ns ? pageXOffset : document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
5326    pY = ns ? pageYOffset : document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
5327    if(this.sy<0)
5328      pY += ns ? innerHeight : document.documentElement && document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
5329    this.cx += (pX + Math.max(this.sx-pX, this.tlx) - this.cx)/4;
5330    this.cy += (pY + Math.max(this.sy-pY, this.tly) - this.cy)/4;
5331    this.cx = Math.min(this.cx, this.brx);
5332    this.cy = Math.min(this.cy, this.bry);
5333    if (ns) {
5334      this.sP(
5335        Math.max(Math.min(this.cx+this.clientWidth,document.width)-this.clientWidth,this.sx),
5336        Math.max(Math.min(this.cy+this.clientHeight,document.height)-this.clientHeight,this.sy)
5337      );
5338    } else {
5339      var oldh, newh;
5340      oldh = docHeight();
5341      this.sP(this.cx, this.cy);
5342      newh = docHeight();
5343      if (newh>oldh) {
5344        this.sP(this.cx, this.cy-(newh-oldh));
5345      }
5346    }
5347    setTimeout(this.id + \"_obj.flt()\", 20);
5348  }
5349  return el;
5350}";
5351 }
5352 $JavaScript .= '
5353function popFileEditor(filename,note)
5354{
5355  var height = (note == 0) ? 500 : (note == \'m\') ? 580 : 550;
5356  newwindow=window.open(
5357    \'edit?file=\'+filename+\'&note=\'+note,
5358    \'FileEditor\',
5359    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
5360  );
5361  	// this puts focus on the popup window if we open a new popup without closing the old one.
5362  	if (window.focus) {newwindow.focus()}
5363  	return false;
5364}
5365
5366function popAddressAction(address)
5367{
5368  var height = 500 ;
5369  var link = address ? \'?address=\'+address : \'\';
5370  newwindow=window.open(
5371    \'addraction\'+link,
5372    \'AddressAction\',
5373    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
5374  );
5375  	// this puts focus on the popup window if we open a new popup without closing the old one.
5376  	if (window.focus) {newwindow.focus()}
5377  	return false;
5378}
5379
5380function popIPAction(ip)
5381{
5382  var height = 500 ;
5383  var link = ip ? \'?ip=\'+ip : \'\';
5384  newwindow=window.open(
5385    \'ipaction\'+link,
5386    \'IPAction\',
5387    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
5388  );
5389  	// this puts focus on the popup window if we open a new popup without closing the old one.
5390  	if (window.focus) {newwindow.focus()}
5391  	return false;
5392}
5393
5394function popSyncEditor(cfgParm)
5395{
5396  setAnchor(cfgParm);
5397  var height = 400;
5398  newwindow=window.open(
5399    \'syncedit?cfgparm=\'+cfgParm,
5400    \'SyncEditor\',
5401    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
5402  );
5403  	// this puts focus on the popup window if we open a new popup without closing the old one.
5404  	if (window.focus) {newwindow.focus()}
5405  	return false;
5406}
5407
5408function remember()
5409{
5410  var height =  580;
5411  newwindow=window.open(
5412    \'remember\',
5413    \'rememberMe\',
5414    \'width=720,height=\'+height+\',overflow=scroll,toolbar=no,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
5415  );
5416  	// this puts focus on the popup window if we open a new popup without closing the old one.
5417  	if (window.focus) {newwindow.focus()}
5418  	return false;
5419}
5420
5421window.onload = externalLinks;
5422// -->
5423</script>';
5424
5425# JavaScript for alphabetic IndexMenu
5426 $JavaScript .= '
5427<style type="text/css" >
5428<!--
5429#smenu {background-color:#ffffff; text-align:left; font-size: 90%; border:1px solid #000099; z-Index:200; visibility:hidden; position:absolute; top:100px; left:-'.$IndexPos.'px; width:450px; height:700px;}
5430#sleftTop {width:420px; height:5%; float:left;font-size: 90%;color:#999999; font-family:arial, helvetica, sans-serif;overflow: hidden;}
5431#sleft {width:420px; height:94%; float:left;font-size: 90%;color:#999999; font-family:arial, helvetica, sans-serif;overflow-x: hidden;overflow-y: scroll;}
5432#sright {width:10px; height:99%; float:right;font-size: 90%;color:#999999; font-family:arial, helvetica, sans-serif;overflow: hidden;}
5433#sright a:link{text-decoration:none; color:#684f00; font-family:arial, helvetica, sans-serif;}
5434#sright a:visited{text-decoration:none; color:#684f00; font-family:arial, helvetica, sans-serif;}
5435#sright a:active{text-decoration:none; color:#684f00; font-family:arial, helvetica, sans-serif;}
5436#sright a:hover{text-decoration:underline; color:#999999; font-family:arial, helvetica, sans-serif;}
5437-->
5438</style>
5439
5440<script type="text/javascript">
5441<!--
5442// Sliding Menu Script
5443// copyright Stephen Chapman, 6th July 2005
5444// you may copy this code but please keep the copyright notice as well
5445// ASSP implementation by Thomas Eckardt
5446var speed = 1;
5447
5448function changeSlide() {
5449    var findText = xDOM(\'quickfind\').value;
5450    if (findText == \'**select**\') findText = \'\';
5451    var re;
5452    try {
5453        re = new RegExp(findText,"i");
5454        re.test(\'abc\');
5455    }
5456    catch(err) {
5457        alert(\'error in string (regex) : \'+err);
5458        return false;
5459    }
5460    var entries = xDOM(\'sleft\').getElementsByTagName(\'a\');
5461    for (var i=0; i<entries.length; i++) {
5462        var id=entries[i].id;
5463        if (! id) next;
5464        if (findText == \'\' || re.test(id.substr(3))) {
5465            setObjDisp(id,\'inline\');
5466        } else {
5467            setObjDisp(id,\'none\');
5468        }
5469    }
5470}
5471
5472function ClientSize(HorW) {
5473  var myWidth = 0, myHeight = 0;
5474  if( typeof( window.innerWidth ) == \'number\' ) {
5475    //Non-IE
5476    myWidth = window.innerWidth;
5477    myHeight = window.innerHeight;
5478  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
5479    //IE 6+ in \'standards compliant mode\'
5480    myWidth = document.documentElement.clientWidth;
5481    myHeight = document.documentElement.clientHeight;
5482  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
5483    //IE 4 compatible
5484    myWidth = document.body.clientWidth;
5485    myHeight = document.body.clientHeight;
5486  }
5487  return  HorW == \'w\' ?  myWidth : myHeight;
5488}
5489
5490var aDOM = 0, ieDOM = 0, nsDOM = 0; var stdDOM = document.getElementById;
5491if (stdDOM) aDOM = 1; else {ieDOM = document.all; if (ieDOM) aDOM = 1; else {
5492var nsDOM = ((navigator.appName.indexOf(\'Netscape\') != -1)
5493&& (parseInt(navigator.appVersion) ==4)); if (nsDOM) aDOM = 1;}}
5494
5495function xDOM(objectId, wS) {
5496  if (stdDOM) return wS ? document.getElementById(objectId).style : document.getElementById(objectId);
5497  if (ieDOM) return wS ? document.all[objectId].style : document.all[objectId];
5498  if (nsDOM) return document.layers[objectId];
5499}
5500function objWidth(objectID) {var obj = xDOM(objectID,0); if(obj.offsetWidth) return obj.offsetWidth; if (obj.clip) return obj.clip.width; return 0;}
5501function objHeight(objectID) {var obj = xDOM(objectID,0); if(obj.offsetHeight) return obj.offsetHeight; if (obj.clip) return obj.clip.height; return 0;}
5502function setObjVis(objectID,vis) {var objs = xDOM(objectID,1); objs.visibility = vis;}
5503function setObjDisp(objectID,disp) {var objs = xDOM(objectID,1); objs.display = disp;}
5504function moveObjTo(objectID,x,y) {var objs = xDOM(objectID,1); objs.left = x; objs.top = y;}
5505function pageWidth() {return window.innerWidth != null? window.innerWidth: document.body != null? document.body.clientWidth:null;}
5506function pageHeight() {return window.innerHeight != null? window.innerHeight: document.body != null? document.body.clientHeight:null;}
5507function posLeft() {return typeof window.pageXOffset != \'undefined\' ? window.pageXOffset: document.documentElement.scrollLeft?
5508 document.documentElement.scrollLeft: document.body.scrollLeft? document.body.scrollLeft:0;}
5509
5510function posTop() {return typeof window.pageYOffset != \'undefined\' ? window.pageYOffset: document.documentElement.scrollTop?
5511 document.documentElement.scrollTop: document.body.scrollTop? document.body.scrollTop:0;}
5512
5513var xxx = 0; var yyy = 0; var dist = distX = distY = 0; var stepx = '.$IndexSlideSpeed.'; var stepy = 0; var mn = \'smenu\';
5514
5515function disableSlide() {setObjVis(mn,\'hidden\');}
5516function enableSlide() {setObjVis(mn,\'visible\');}
5517function distance(s,e) {return Math.abs(s-e)}
5518function direction(s,e) {return s>e?-1:1}
5519function rate(a,b) {return a<b?a/b:1}
5520function setHeight() {var objs = xDOM(mn,1); var h = ClientSize(\'h\'); objs.height = h*0.95 +\'px\';}
5521function start() {setHeight(); xxx = -'.$IndexStart.'; yyy = 0; var eX = 0; var eY = 100; dist = distX = distance(xxx,eX); distY = distance(yyy,eY); stepx *=
5522-direction(xxx,eX) * rate(distX,distY); stepy *= direction(yyy,eY) * rate(distY,distX); moveit(); setObjVis(mn,\'visible\');}
5523
5524function moveit() {var x = (posLeft()+xxx) + \'px\'; var y = posTop() + \'px\'; moveObjTo(mn,x,y);}
5525function mover() {if (dist > 0) {xxx += stepx; yyy += stepy; dist -= Math.abs(stepx);} moveit(); setTimeout(\'mover()\',speed);}
5526function slide() {dist = distX; stepx = -stepx; moveit(); setTimeout(\'mover()\',speed*2);return false;}
5527
5528onload = start;
5529window.onscroll = moveit;
5530// -->
5531</script>
5532';
5533# END JavaScript for alphabetic IndexMenu
5534
5535#start JavaScript for HintBox
5536$JavaScript .= <<EOT;
5537<style type="text/css">
5538
5539#hintbox{ /*CSS for pop up hint box */
5540position:absolute;
5541top: 0;
5542background-color: lightyellow;
5543width: 150px; /*Default width of hint.*/
5544padding: 3px;
5545border:1px solid black;
5546font:normal 11px Verdana;
5547line-height:18px;
5548z-index:300;
5549border-right: 3px solid black;
5550border-bottom: 3px solid black;
5551visibility: hidden;
5552
5553table { table-layout:fixed; word-wrap:break-word; }
5554}
5555</style>
5556EOT
5557
5558$JavaScript .= '
5559<script type="text/javascript">
5560
5561/***********************************************
5562* Show Hint script- (c) Dynamic Drive (www.dynamicdrive.com)
5563* This notice MUST stay intact for legal use
5564* Visit http://www.dynamicdrive.com/ for this script and 100s more.
5565*
5566* implemented in ASSP by Thomas Eckardt
5567***********************************************/
5568
5569var horizontal_offset="0px" //horizontal offset of hint box from anchor link
5570
5571/////No further editting needed
5572
5573var vertical_offset="20px" //vertical offset of hint box from anchor link. No need to change.
5574var ie=document.all
5575var ns6=document.getElementById&&!document.all
5576
5577function getposOffset(what, offsettype){
5578    var totaloffset=(offsettype=="left")? what.offsetLeft : what.offsetTop;
5579    var parentEl=what.offsetParent;
5580    while (parentEl!=null){
5581        totaloffset=(offsettype=="left")? totaloffset+parentEl.offsetLeft : totaloffset+parentEl.offsetTop;
5582        parentEl=parentEl.offsetParent;
5583    }
5584    return totaloffset;
5585}
5586
5587function iecompattest(){
5588    return (document.compatMode && document.compatMode!="BackCompat")? document.documentElement : document.body
5589}
5590
5591function clearbrowseredge(obj, whichedge, where){
5592    var edgeoffset=(whichedge=="rightedge")? (parseInt(horizontal_offset)-obj.offsetWidth*where/2)*-1 : parseInt(vertical_offset)*-1;
5593    if (whichedge=="rightedge"){
5594        var windowedge=ie && !window.opera? iecompattest().scrollLeft+iecompattest().clientWidth-90 : window.pageXOffset+window.innerWidth-100;
5595        dropmenuobj.contentmeasure=dropmenuobj.offsetWidth;
5596        if (windowedge-dropmenuobj.x < dropmenuobj.contentmeasure)
5597            edgeoffset=dropmenuobj.contentmeasure+obj.offsetWidth/(where+1)+parseInt(horizontal_offset);
5598    } else {
5599        var windowedge=ie && !window.opera? iecompattest().scrollTop+iecompattest().clientHeight-15 : window.pageYOffset+window.innerHeight-18
5600        dropmenuobj.contentmeasure=dropmenuobj.offsetHeight
5601        if (windowedge-dropmenuobj.y < dropmenuobj.contentmeasure)
5602            edgeoffset=dropmenuobj.contentmeasure-obj.offsetHeight+parseInt(vertical_offset)
5603    }
5604    return edgeoffset
5605}
5606
5607function showhint(menucontents, obj, e, tipwidth, currLoc){
5608    if (document.getElementById("hintbox")){
5609        dropmenuobj=document.getElementById("hintbox")
5610        dropmenuobj.innerHTML=menucontents
5611        dropmenuobj.style.left=dropmenuobj.style.top=-500
5612        if (tipwidth!=""){
5613            dropmenuobj.widthobj=dropmenuobj.style
5614            dropmenuobj.widthobj.width=tipwidth
5615        }
5616        dropmenuobj.x=getposOffset(obj, "left")
5617        dropmenuobj.y=getposOffset(obj, "top");
5618        if (currLoc != "" && (ie||ns6)) {
5619            //var postop = ns6 ? 0 : posTop();
5620            var postop = 0;
5621            var objTop = yMousePos+postop+parseInt(vertical_offset);
5622            var Yedge=ie && !window.opera? iecompattest().scrollTop+iecompattest().clientHeight-15 : window.pageYOffset+window.innerHeight-18;
5623            if (dropmenuobj.offsetHeight + objTop > Yedge) {
5624                dropmenuobj.style.top=objTop-dropmenuobj.offsetHeight+"px";
5625            } else {
5626                dropmenuobj.style.top=objTop+"px";
5627            }
5628        } else {
5629            dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge", 0)+"px";
5630        }
5631        if (currLoc != "") {
5632            dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge", 0)+obj.offsetWidth+"px";
5633        } else {
5634            dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge", 1)+obj.offsetWidth+"px";
5635        }
5636        //alert("x="+dropmenuobj.x+" , cb="+clearbrowseredge(obj, \'rightedge\')+" , offset="+obj.offsetWidth);
5637        //dropmenuobj.style.left=xMousePos+"px"
5638        dropmenuobj.style.visibility="visible"
5639        obj.onmouseout=hidetip
5640    }
5641}
5642
5643function hidetip(e){
5644    dropmenuobj.style.visibility="hidden"
5645    dropmenuobj.style.left="-500px"
5646}
5647
5648function createhintbox(){
5649    var divblock=document.createElement("div")
5650    divblock.setAttribute("id", "hintbox")
5651    document.body.appendChild(divblock)
5652}
5653
5654if (window.addEventListener)
5655    window.addEventListener("load", createhintbox, false)
5656else if (window.attachEvent)
5657    window.attachEvent("onload", createhintbox)
5658else if (document.getElementById)
5659    window.onload=createhintbox
5660
5661// Set Netscape up to run the "captureMousePosition" function whenever
5662// the mouse is moved. For Internet Explorer and Netscape 6, you can capture
5663// the movement a little easier.
5664if (document.layers) { // Netscape
5665    document.captureEvents(Event.MOUSEMOVE);
5666    document.onmousemove = captureMousePosition;
5667} else if (document.all) { // Internet Explorer
5668    document.onmousemove = captureMousePosition;
5669} else if (document.getElementById) { // Netcsape 6
5670    document.onmousemove = captureMousePosition;
5671}
5672
5673// Global variables
5674xMousePos = 0; // Horizontal position of the mouse on the screen
5675yMousePos = 0; // Vertical position of the mouse on the screen
5676xMousePosMax = 0; // Width of the page
5677yMousePosMax = 0; // Height of the page
5678
5679function captureMousePosition(e) {
5680    if (document.layers) {
5681        // When the page scrolls in Netscape, the event\'s mouse position
5682        // reflects the absolute position on the screen. innerHight/Width
5683        // is the position from the top/left of the screen that the user is
5684        // looking at. pageX/YOffset is the amount that the user has
5685        // scrolled into the page. So the values will be in relation to
5686        // each other as the total offsets into the page, no matter if
5687        // the user has scrolled or not.
5688        xMousePos = e.pageX;
5689        yMousePos = e.pageY;
5690        xMousePosMax = window.innerWidth+window.pageXOffset;
5691        yMousePosMax = window.innerHeight+window.pageYOffset;
5692    } else if (document.all) {
5693        // When the page scrolls in IE, the event\'s mouse position
5694        // reflects the position from the top/left of the screen the
5695        // user is looking at. scrollLeft/Top is the amount the user
5696        // has scrolled into the page. clientWidth/Height is the height/
5697        // width of the current page the user is looking at. So, to be
5698        // consistent with Netscape (above), add the scroll offsets to
5699        // both so we end up with an absolute value on the page, no
5700        // matter if the user has scrolled or not.
5701
5702        if (window.event) {
5703            xMousePos = window.event.x+document.body.scrollLeft;
5704            yMousePos = window.event.y+document.body.scrollTop;
5705        } else {
5706            if (e) {};
5707        }
5708        xMousePosMax = document.body.clientWidth+document.body.scrollLeft;
5709        yMousePosMax = document.body.clientHeight+document.body.scrollTop;
5710    } else if (document.getElementById) {
5711        // Netscape 6 behaves the same as Netscape 4 in this regard
5712        xMousePos = e.pageX;
5713        yMousePos = e.pageY;
5714        xMousePosMax = window.innerWidth+window.pageXOffset;
5715        yMousePosMax = window.innerHeight+window.pageYOffset;
5716    }
5717}
5718function browserclose () {
5719    eraseCookie(\'lastAnchor\');
5720    confirm(\'please logout first ?\');
5721    return false;
5722}
5723if(window.addEventListener) {
5724    window.addEventListener("close", browserclose, false);
5725}
5726
5727function changeTitle(title) {
5728    document.title = document.title.replace(/^\S+/ ,title);
5729}
5730
5731function WaitDiv()
5732{
5733	document.getElementById(\'wait\').style.display = \'block\';
5734}
5735
5736function WaitDivDel()
5737{
5738	document.getElementById(\'wait\').style.display = \'none\';
5739}
5740</script>
5741';
5742$JavaScript .= <<EOT;
5743<style type="text/css">
5744#wait {
5745	position: absolute;
5746	width: 350;
5747	heigth: 100;
5748	margin-left: 300;
5749	margin-top: 150;
5750	background-color: #FFF000;
5751	text-align: center;
5752	border: solid 1px #FFFFFF;
5753}
5754</style>
5755EOT
5756#end JavaScript for HintBox
5757  $headerHTTP = 'HTTP/1.1 200 OK
5758Content-type: text/html
5759Cache-control: no-cache
5760';
5761 $headerDTDStrict = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
5762  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
5763';
5764 $headerDTDTransitional = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5765  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5766';
5767 $headers = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
5768<head>
5769  <meta http-equiv=\"content-type\" content=\"application/xhtml+xml; charset=utf-8\" />
5770  <title>Config ASSP ($myName) Host: $localhostname @ $localhostip</title>
5771  <link rel=\"stylesheet\" href=\"get?file=images/assp.css\" type=\"text/css\" />
5772  <link rel=\"shortcut icon\" href=\"get?file=images/favicon.ico\" />
5773$JavaScript
5774</head>
5775<body window.onunload=\"javascript:browserclose();\" window.onClose=\"javascript:browserclose();\"><a name=\"Top\"></a>
5776<div class=\"wait\" id=\"wait\" style=\"display: none;\">&nbsp;&nbsp; Please wait while loading... &nbsp;&nbsp;</div>
5777  <div id=\"smenu\"><div id=\"sleftTop\">&nbsp;
5778";
5779
5780 for ("A"..."Z") {
5781 $headers .= "<a href=\"#$_\" onmousedown=\"gotoAnchor('$_');return false;\">$_&nbsp;</a>";
5782 }
5783 $headers .= "&nbsp;&nbsp;<input id=\"quickfind\" size=\"9\" value=\"**select**\" style=\"background:#eee none; color:#222; font-style: italic\" onfocus=\"if (this.value == '**select**') {this.value='';}\" onchange=\"changeSlide();\" >&nbsp;&nbsp;<img src=\"get?file=images/plusIcon.png\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>Select the values to show. The string is searched anywhere in the value names. A regular expression could be used.</td></tr></table>', this, event, '450px', ''); return true;\">&nbsp;&nbsp;&nbsp;<a href=\"javascript:void();\" onclick=\"xDOM('quickfind').value='';changeSlide();return false;\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>Click to reset view to default.</td></tr></table>', this, event, '450px', ''); return true;\"><img src=\"get?file=images/minusIcon.png\" ></a>\n<hr></div><div id=\"sleft\">\n";
5784my %Config1 = ();
5785niceConfig();
5786while (my ($k,$v) = each %Config) {
5787    $Config1{lc($k)} = $k;
5788}
5789my $firstChar = '';
5790my $hid;
5791
5792foreach (sort keys %Config1) {
5793    my $k = $Config1{$_};
5794    my $name = uc($firstChar) ne uc(substr($k,0,1)) ? 'name="'.uc(substr($k,0,1)).'"' : '';
5795    $firstChar = uc(substr($k,0,1));
5796
5797    my $value = $ConfigListBox{$k} ? $ConfigListBox{$k} : encodeHTMLEntities($Config{$k});
5798    $value =~ s/'|"|\n//go;
5799    $value =~ s/\\/\\\\/go;
5800    $value = '&nbsp;' unless $value;
5801    $value = 'ENCRYPTED' if exists $cryptConfigVars{$k} or $k eq 'webAdminPassword';
5802    my $default =  $ConfigDefault{$k};
5803#    mlog( '',"k : $k : $ConfigDefault{$k}" );
5804
5805    $default = '' if $default eq undef;
5806    $headers .= "<a $name id=\"sl_$k\" href=\"./#$k\" onmousedown=\"expand(0, 1);showDisp('$ConfigPos{$k}');gotoAnchor('$k');slide();return false;\" onmouseover=\"window.status='$ConfigNice{$k}'; showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>config var:</td><td>$k</td></tr><tr><td>description:</td><td>$ConfigNice{$k}</td></tr><tr><td>current value:</td><td>$value</td></tr><tr><td>default value:</td><td>$default</td></tr></table>', this, event, '500px', 'index'); return true;\" onmouseout=\"window.status='';return true;\">&nbsp;<img src=\"$noIcon\" alt=\"$ConfigNice{$k}\" />&nbsp;$k<br /></a>\n";
5807}
5808
5809  $headers .= "<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;</div><div id=\"sright\"><a href=\"#\" onclick=\"return slide();return false;\">";
5810  $headers .= "<img src=\"get?file=images/plusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5811  $headers .= "<img src=\"get?file=images/minusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5812  $headers .= "<img src=\"get?file=images/minusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5813  $headers .= "<img src=\"get?file=images/plusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5814# do not use spaces in $boardertext - instead use '#'
5815  my $boardertext = "sorted#config";
5816  $boardertext =~ s/([^#])/$1<br \/>/go;
5817  $boardertext =~ s/#/&nbsp;<br \/>/go;
5818  $headers .= "$boardertext<br />";
5819  $headers .= "<img src=\"get?file=images/plusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5820  $headers .= "<img src=\"get?file=images/minusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5821  $headers .= "<img src=\"get?file=images/minusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5822  $headers .= "<img src=\"get?file=images/plusIcon.png\" alt=\"open and close alphabetical index\" /><br />&nbsp;<br \/>";
5823  $headers .= "</a></div></div>
5824<p>";
5825  $headers .= '<table id="TopMenu" class="contentFoot" style="margin:0; text-align:left;" CELLSPACING=0 CELLPADDING=4 WIDTH="100%">
5826  <tr><td rowspan="3" align="left">';
5827  if (-e "$base/images/logo.gif") {
5828      $headers .= "<a href=\"http://assp.sourceforge.net/\" target=\"_blank\"><img src=\"get?file=images/logo.gif\" alt=\"ASSP\" /></a>";
5829  } else {
5830      $headers .= "<a href=\"http://assp.sourceforge.net/\" target=\"_blank\"><img src=\"get?file=images/logo.jpg\" alt=\"ASSP\" /></a>";
5831  }
5832  $headers .= '</td>
5833  <td><a href="lists">&nbsp;</a></td>
5834  <td><a href="lists">&nbsp;</a></td>
5835  <td><a href="shutdown_list?nocache" target="_blank">&nbsp;</a></td>
5836  <td><a href="maillog' . $maillogEnd . '">&nbsp;</a></td>
5837  </tr><tr>';
5838  $headers .=
5839  "<td>ASSP Version: $version$modversion</td>";
5840  my $avv = "$availversion";
5841  my $stv = "$version$modversion";
5842  $avv =~ s/RC/\./gi;
5843  $stv =~ s/RC/\./gi;
5844  $avv =~ s/\s|\(|\)//gi;
5845  $stv =~ s/\s|\(|\)//gi;
5846  $avv =~ s/\.//gi;
5847  $stv =~ s/\.//gi;
5848  $headers .= "<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"$NewAsspURL\" target=\"_blank\" style=\"color:green;;font-size: 14px;font-family: 'Courier New',Courier,monospace;\">new available ASSP version $availversion</a>" if $avv gt $stv;
5849  my $webhost = $BlockReportHTTPName ? $BlockReportHTTPName : $localhostname ? $localhostname : 'please_define_BlockReportHTTPName';
5850
5851$webhost =~ s/localhost/127\.0\.0\.1/i ;
5852if ($AsASecondary && $webSecondaryPort) {
5853
5854  	$webAdminPort  =~ s/\|.*//go;
5855  	$headers .=
5856  "<td><a href='http://$webhost:$webAdminPort/lists' onmouseover=\"showhint('On Primary $webAdminPort', this, event, '200px', '');return false;\"><span class=positive>White/Redlist/Tuplets</span></a></td>
5857  <td><a href='http://$webhost:$webAdminPort/shutdown_list?nocache' target='connections' onmouseover=\"showhint('On Primary $webAdminPort', this, event, '200px', '');return false;\"><span class=positive>SMTP Connections</span></a></td>
5858  <td><a href='http://$webhost:$webSecondaryPort/maillog$maillogEnd ' onmouseover=\"showhint('On Secondary $webSecondaryPort', this, event, '200px', '');return false;\">View Maillog Tail</a></td>
5859  </tr><tr>";
5860
5861
5862  	$headers .=
5863  "<td><span class=positive>Started: $starttime</span></td>
5864
5865  <td><a href=http://$webhost:$webAdminPort/analyze onmouseover=\"showhint('On Primary $webAdminPort', this, event, '200px', '');return false;\"><span class=positive>Mail Analyzer</span></a></td>";
5866
5867
5868  	$headers .= "
5869  		<td><a href=http://$webhost:$webAdminPort/infostats onmouseover=\"showhint('On Primary $webAdminPort', this, event, '200px', '');return false;\"><span class=positive>Info and Stats</span></a></td>";
5870
5871    $headers .=
5872  "<td><a href=http://$webhost:$webAdminPort/shutdown onmouseover=\"showhint('On Primary $webAdminPort, this, event, '200px', '');return false;\"><span class=positive>Shutdown/Restart</span></a></td></tr>
5873  </table>";
5874
5875  } else {
5876	&readSecondaryPID();
5877  	if ($SecondaryPid && $webSecondaryPort) {
5878		my $webPort = $webSecondaryPort;
5879
5880  		$headers .=
5881  		"<td><a href=lists>White/Redlist/Tuplets</a></td>
5882  		<td><a href=shutdown_list?nocache target=_blank>SMTP Connections</a></td>
5883  		<td><a href='http://$webhost:$webPort/maillog$maillogEnd ' onmouseover=\"showhint('On Secondary $webPort', this, event, '200px', '');return false;\"><span class=positive>View Maillog Tail</span></a></td>
5884  		</tr><tr>";
5885	} else {
5886		$headers .=
5887  		'<td><a href="lists">White/Redlist/Tuplets</a></td>
5888  		<td><a href="shutdown_list?nocache" target="_blank">SMTP Connections</a></td>
5889  		<td><a href="maillog' . $maillogEnd . '">View Maillog Tail</a></td>
5890  		</tr><tr>';
5891	}
5892
5893  	$headers .=
5894  "<td><span class=pass>Started: $starttime</span></td>
5895
5896  <td><a href=analyze>Mail Analyzer</a></td>";
5897
5898  	$headers .= '
5899  		<td><a href=/infostats>Info and Stats</a></td>';
5900
5901    $headers .=
5902  "<td><a href=shutdown>Shutdown/Restart</a></td></tr>
5903  </table>";
5904  }
5905
5906  $headers =~ s/http:/https:/go if $enableWebAdminSSL && $CanUseIOSocketSSL;
5907
5908
5909
5910
5911
5912    $headers .= "
5913<div class=\"navMenu\"";
5914    $headers .= ' id="navMenu" style="position:absolute"' if $EnableFloatingMenu;
5915
5916    $headers .= "><div><div style=\"text-align: center;\">
5917   <div class=\"rightButton\" style=\"text-align: center;\">
5918  <a href=\"javascript:void(0);\" onclick=\"remember();return false;\" onmouseover=\"showhint('open the remember me window', this, event, '200px', '');return false;\"><img height=12 width=12 src=\"$wikiinfo\" /></a>&nbsp;<input type=\"button\" value=\"Apply Changes\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();return false;\" />
5919</div>
5920  <hr />
5921
5922  <a href=\"#\" onmousedown=\"expand(1, 1)\">Expand</a>&nbsp;
5923  <a href=\"#\" onmousedown=\"expand(0, 1)\">Collapse</a>&nbsp;
5924  <a href=\"#\" onmousedown=\"slide();return false;\">Index</a></div>
5925<hr />
5926
5927
5928  <div class=\"menuLevel1\"><a href=\"/\"><img src=\"$plusIcon\" alt=\"plusicon\" /> Main</a><br /></div>";
5929	my $counter = 0;
5930    foreach my $c (@ConfigArray) {
5931        if ( @{$c} == 5 ) {
5932            $headers .=
5933"</div>\n  <div class=\"menuLevel2\">\n  <a onmousedown=\"toggleDisp('$counter')\">"
5934              . "<img id=\"treeIcon$counter\" src=\"$plusIcon\" alt=\"plusicon\" /> "
5935              . "$c->[4]</a>\n</div>\n<div id=\"treeElement$counter\" style=\"padding-left: 3px; display: block\">";
5936            $counter++;
5937        } else {
5938            $headers .=
5939"\n    <div class=\"menuLevel3\"><a href=\"./#$c->[0]\">$c->[0]</a></div>";
5940        }
5941    }
5942    my $runas = $AsAService ? '  service ' : $IsDaemon ? '  daemon' : ' console mode ';
5943    $runas = ' as Secondary' if $AsASecondary;
5944    my $user;
5945    $user = "  root /" if $< == 0 && $^O ne "MSWin32";
5946
5947	$user = "  " . (getpwuid($<))[0] . " /" if $< != 0 && $^O ne "MSWin32";
5948    $headers .= "</div>
5949<div class=\"menuLevel1\">$NavMenu</div>
5950<hr />
5951<div class=\"rightButton\" style=\"text-align: center;\">
5952  <a href=\"javascript:void(0);\" onclick=\"remember();return false;\" onmouseover=\"showhint('open the remember me window', this, event, '200px', '');return false;\"><img height=12 width=12 src=\"$wikiinfo\" /></a>&nbsp;<input type=\"button\" value=\"Apply Changes\" onclick=\"document.forms['ASSPconfig'].theButtonX.value='Apply Changes';document.forms['ASSPconfig'].submit();return false;\" />
5953</div>
5954<hr />
5955<div class=\"menuLevel2\">
5956	<a href=\"#\" onclick=\"popAddressAction();\"><img src=\"$noIcon\" alt=\"#\" /> work with addresses</a><br />
5957  	<a href=\"#\" onclick=\"popIPAction();\"><img src=\"$noIcon\" alt=\"#\" /> work with IP\'s</a><br />
5958<hr />
5959	<a href=\"#\" onclick=\"return popFileEditor(\'/notes/confighistory.txt\',5); \"><img src=\"$noIcon\" alt=\"#\" /> Config History</a><br />
5960	<a href=\"#\" onclick=\"return popFileEditor(\'/notes/admininfo.txt\',5); \"><img src=\"$noIcon\" alt=\"#\" /> Email Interface</a><br />
5961
5962	<a href=\"#\" onclick=\"return popFileEditor(\'/notes/configdefaults.txt\',8); \"><img src=\"$noIcon\" alt=\"#\" /> Non-Default Settings</a><br />
5963	<a href=\"#\" onclick=\"return popFileEditor(\'/docs/assp.cfg.description.txt\',8);\"><img src=\"$noIcon\" alt=\"#\" /> Config Description</a><br />
5964	<a href=\"#\" onclick=\"return popFileEditor(\'/docs/changelog.txt\',8);\"><img src=\"$noIcon\" alt=\"#\" /> ChangeLog</a>
5965
5966	<hr />
5967
5968	<span style=\"font-weight: bold;\">&nbsp;&nbsp;Current PID</span>: $mypid<br />
5969	&nbsp;&nbsp;$user$runas
5970
5971
5972</div>
5973<hr />
5974
5975<div style=\"text-align: center;\">
5976  <span class=positive>Started: $starttime</span></div>
5977</div>
5978<script type=\"text/javascript\">
5979  <!--
5980  ";
5981    $headers .= 'JSFX_FloatDiv("navMenu",2,50,2,-2,2,99999).flt();'
5982      if $EnableFloatingMenu;
5983
5984    if ($AutoUpdateASSPDev) {
5985    	$ChangeLogURL =  $ChangeLogURLDev;
5986    } else {
5987    	$ChangeLogURL =  $ChangeLogURLStable;
5988    }
5989
5990    $headers .= '
5991  expand(0,0);
5992  // -->
5993  </script>
5994  ';
5995    $footers = "
5996<div class=\"contentFoot\">
5997<a href=\"http://www.enginsite.com/Library-Perl-Regular-Expressions-Tutorial.htm\"  rel=\"external\" target=\"_blank\">Regular Expressions</a> |
5998<a href=\"http://apps.sourceforge.net/mediawiki/assp/index.php?title=Getting_Started\" target=wiki>Getting Started</a> |
5999<a href=\"http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/\" rel=\"external\" target=\"_blank\">Option Files</a> |
6000<a href=\"donations\" target=\"_blank\">Kudos</a> |
6001<a href=\"https://sourceforge.net/projects/assp/files/ASSP%20Installation/ASSP%201.9.X/\" rel=\"external\" target=\"_blank\">Newest Installation</a> |
6002<a href=\"http://sourceforge.net/projects/asspV1/files/ASSP%20Installation/AutoUpdate/ASSP1x/assp.pl.gz\" rel=\"external\" target=\"_blank\">Update</a> |
6003<a href=\"http://assp.sourceforge.net/cgi-bin/assp_stats\" rel=\"external\" target=\"_blank\">Stats</a> |
6004<a href=\"http://apps.sourceforge.net/mediawiki/assp/index.php?title=ASSP_Documentation\" rel=\"external\" target=\"_blank\">Docs</a> |
6005
6006 <a href=\"http://sourceforge.net/mail/?group_id=69172\" rel=\"external\" target=\"_blank\">Maillists</a> |
6007
6008 <a href=\"http://apps.sourceforge.net/phpbb/assp/\" rel=\"external\" target=\"_blank\">Forums</a> |
6009
6010 <a href=\"http://apps.sourceforge.net/mediawiki/assp/\" rel=\"external\" target=\"_blank\">Wiki</a>
6011
6012</div>";
6013    $kudos = '<div class="kudos">
6014
6015
6016</div>
6017';
6018
6019}
6020
6021# Notes on general operation & program structure
6022# We using IO::Select, so don't make any changes that block for long
6023# as new connections come we create a pair of entries in a hash %Con
6024# based on the hash of the filehandle, so $Con{$fh} has data for this
6025# connection. $Con{$fh}->{friend} is the partner socket for the smtp proxy.
6026# ->{ip} is the ip address of the connecting client
6027# ->{relayok} tells if we can relay mail for this client
6028# ->{getline} is a pointer to a function that should be called whan a line of input is received for this filehandle
6029# ->{mailfrom} is the envelope sender (MAIL FROM: <address>)
6030# ->{outgoing} is a buffer for outgoing socket traffic (see $writable & &sendque)
6031# ->{rcpt} are the addresses from RCPT TO: <address> (space separated)
6032# ->{header} is where the header (and eventually the first 10000 bytes) are stored
6033# ->{myheader} is where we store our header, we merge it with client's header later
6034# ->{maillog} if present stream logging is enabled
6035# ->{maillogbuf} buffer for storing unwritten stream log while waiting for isspam decision
6036# ->{maillogfh} is the filehandle for logging lines to the maillog
6037# ->{mailloglength} is the length logged so far (we stop after 10000 bytes)
6038# ->{spamfound} is a flag used to signal if an email is determined to be spam.
6039# ->{maillength} is the same as mailloglength but is not reset.
6040#
6041# After connection the {getline} field functions like a state machine
6042# redirecting input to subsequent handlers
6043#
6044# whitebody -> getline
6045#   getbody ->
6046#     error -> (disconnects)
6047#     getline -> getheader ->
6048#       whitebody -> getline
6049#         error -> (disconnects)
6050#
6051# getline looks for MAIL FROM, RCPT TO, RSET
6052# getheader looks for a blank line then tests for whitelist / spamaddresses
6053# getbody looks for the . and calls isspam, the Bayesian spam test
6054# whitebody waits for . and redirects client to server
6055# error waits for . ignoring data from client (and finishes the maillog)
6056#
6057# the server has states like this:
6058#
6059# skipok -> reply
6060#
6061# skipok traps the 250 ok response from the NOOP Connection from
6062# reply echos server messages to the client
6063# reply also looks for a 235 AUTH OK and sets {relayok}=1
6064sub serviceCheck { }
6065sub d            {
6066$debugprint = $_[0];
6067return; }
6068
6069-d "$base/debug"       or mkdir "$base/debug",       0777;
6070if ($debug && !$AsASecondary) {
6071	my $fn = localtime();
6072 	$fn =~ s/^... (...) +(\d+) (\S+) ..(..)/$1-$2-$4-$3/;
6073 	$fn =~ s/[\/:]/-/g;
6074    open( $DEBUG, '>' ,"$base/debug/" . $fn . ".dbg" );
6075    binmode($DEBUG);
6076    my $oldfh = select($DEBUG);
6077    $| = 1;
6078    select($oldfh);
6079  }
6080
6081eval(
6082    q[sub d {
6083 my $time = &timestring();
6084 $time =~ s/[\/:]/-/g;
6085 $debugprint = $_[0];
6086 $debugprint =~ s/\n//;
6087 $debugprint =~ s/\r//;
6088 $debugprint =~ s/\s+$//;
6089
6090 print DEBUG "$time <$debugprint>\n";
6091 w32dbg("(DEBUG) <$debugprint>");
6092 }]
6093) if $debug;
6094my $time = &timestring();
6095if ($AsASecondary) {
6096    fork() && exit 0;
6097    close STDOUT;
6098    close STDERR;
6099    $0 = "secondary ASSP";
6100    $assp = $0;
6101    $silent = 1;
6102} elsif ($AsADaemon) {
6103	$IsDaemon = 1;
6104	print "\n$time starting as daemon\n" ;
6105    fork() && exit 0;
6106    close STDOUT;
6107    close STDERR;
6108    $silent = 1;
6109} elsif(  $AsAService) {
6110 	close STDOUT;
6111	close STDERR;
6112 	$silent=1;
6113}
6114
6115# open the logfile
6116 printLOG("open");
6117if ($pidfile && !$AsASecondary) { open(my $FH, ">","$base/$pidfile" ); print $FH $$; close $FH; }
6118
6119
6120
6121my $logdir;
6122$logdir = $1 if $logfile =~ /(.*)\/.*/;
6123
6124
6125-d "$base/$logdir" or mkdir "$base/$logdir", 0755 if $logdir;
6126
6127
6128
6129
6130if (! $silent) {
6131      if ($ConsoleCharset) {
6132          binmode STDOUT, ":encoding($ConsoleCharset)";
6133          binmode STDERR, ":encoding($ConsoleCharset)";
6134      } else {
6135          binmode STDOUT;
6136          binmode STDERR;
6137      }
6138  }
6139
6140&init();
6141
6142
6143$SIG{INT}	=	sub {mlog(0,"received 'SIG INT'"); &downASSP("terminated by 'SIG INT'"); exit 1;} if !$AsASecondary;
6144$SIG{INT}	=	sub {&downSecondary("terminated by 'SIG INT'"); } if $AsASecondary;
6145
6146$SIG{TERM}	=	sub {mlog(0,"received 'KILL -TERM'"); &downASSP("terminated by 'KILL -TERM'"); exit 1;} if !$AsASecondary;
6147$SIG{TERM}	=	sub {&downSecondary("terminated by 'KILL -TERM'");} if $AsASecondary;
6148$SIG{QUIT}	=	\&ConfigRestart if !$AsASecondary;
6149$SIG{HUP}  	= 	\&reloadConfigFile;
6150$SIG{USR1} 	= 	\&saveSMTPconnections if !$AsASecondary;
6151$SIG{USR1} 	= 	"IGNORE" if $AsASecondary;
6152$SIG{USR2} 	= 	\&SaveWhitelist if !$AsASecondary;
6153$SIG{USR2} 	= 	"IGNORE" if $AsASecondary;
6154$SIG{PIPE}  = 	\&renderConfigHTML if !$AsASecondary;;
6155$SIG{SEGV}	=	\&SEGVRestart;
6156$SIG{'__WARN__'} = sub { warn $_[0] if (!($AsADaemon || $AsAService) && $_[0] !~ /uninitialized/oi)};
6157
6158&niceConfigPos();
6159
6160&renderConfigHTML();
6161
6162$lastTimeoutCheck = time;
6163
6164eval {
6165    while (1)
6166
6167    {
6168
6169        &MainLoop;
6170    }
6171};
6172if ($@) {
6173
6174    my $exmsg = "mainloop exception: $@\n";
6175    print $exmsg;
6176
6177    printLOG("print",$exmsg) ;
6178	writeExceptionLog($exmsg);
6179
6180    mlog( 0, "mainloop exception: $@", 1, 1 );
6181	downASSP("try restarting ASSP on exception: $@" );
6182
6183	restartCMD(1);
6184}
6185sub RemovePid {
6186	if ($pidfile && !$AsASecondary) {
6187  		d('RemovePid');
6188  		unlink("$base/$pidfile");
6189  	}
6190	if ($pidfile && $AsASecondary) {
6191  		d('RemovePid_Secondary');
6192  		unlink("$base/$pidfile"."_Secondary");
6193 	}
6194}
6195
6196sub restartCMD {
6197
6198	my ($exception) = @_;
6199	my $autorestart = 1;
6200	$autorestart = $AutoRestart if $exception;
6201    if ($AsAService) {
6202    	mlog(0,"autorestart as a service: cmd.exe /C net stop ASSPSMTP & net start ASSPSMTP",1);
6203        exec('cmd.exe /C net stop ASSPSMTP & net start ASSPSMTP');
6204    } elsif ($IsDaemon) {
6205        if ($AutoRestartCmd && $autorestart) {
6206        	mlog(0,"autorestart as a daemon: $AutoRestartCmd",1);
6207            exec($AutoRestartCmd);
6208
6209            exit 1;
6210        } else {
6211          	exit 1;
6212        }
6213    } else {
6214        if ($AutoRestartCmd && $autorestart) {
6215        	mlog(0,"autorestart: $AutoRestartCmd",1);
6216            exec($AutoRestartCmd);
6217          	}
6218        exit 1;
6219    }
6220    mlog(0,"autorestart not possible, AutoRestartCmd not configured",1) if !$AsAService;
6221
6222}
6223sub startPrimary {
6224	return if !$AutoRestartCmd && !$AsAService;
6225
6226	printSecondary( "autorestart primary");
6227    if ($AsAService) {
6228
6229        exec('cmd.exe /C net stop ASSPSMTP & net start ASSPSMTP');
6230
6231    } else {
6232
6233        exec($AutoRestartCmd);
6234
6235    }
6236 	exit 1;
6237}
6238sub checkPrimaryPID {
6239
6240
6241		if (!-e "$base/$pidfile") {
6242
6243			return 0;
6244		}
6245
6246		our $PID;
6247		open $PID, "<","$base/$pidfile";
6248		$PrimaryPid = <$PID>;
6249		$PrimaryPid =~ s/\r|\n|\s//go;
6250    	close $PID;
6251 		my $primary;
6252    	$primary = kill 0, $PrimaryPid if $PrimaryPid;
6253
6254		return $PrimaryPid if $primary;
6255		unlink("$base/$pidfile");
6256		return 0;
6257}
6258sub readSecondaryPID {
6259
6260
6261		if (!-e "$base/$pidfile". "_Secondary") {
6262
6263			$SecondaryRunning = 0;
6264			$SecondaryPid = "";
6265			return 0;
6266		}
6267		my @s     = stat("$base/$pidfile". "_Secondary");
6268		my $mtime = $s[9];
6269		our $PID;
6270		open $PID, "<","$base/$pidfile". "_Secondary";
6271		my $Pid = <$PID>;
6272    	close $PID;
6273    	$Pid =~ s/\r|\n|\s//go;
6274    	($SecondaryPid,$webPort) = $Pid =~ /(.*)\:?(.*)?/;
6275		my $secondary;
6276		$secondary = kill 0, $SecondaryPid if $SecondaryPid;
6277		if ($secondary) {
6278			$SecondaryRunning = 1;
6279    		return $SecondaryPid;
6280		} else {
6281			unlink("$base/$pidfile"."_Secondary");
6282			$SecondaryRunning = 0;
6283			$SecondaryPid = 0;
6284			$webPort = "";
6285			return 0;
6286		}
6287}
6288
6289
6290sub startSecondary {
6291		return if $AsASecondary;
6292		return if !$webSecondaryPort;
6293		if (&readSecondaryPID())  {
6294
6295    		$SecondaryRunning = 1;
6296    		return 1;
6297    	}
6298    	mlog( 0, "Info: starting Secondary" );
6299
6300		my $cmd;
6301		my $assp = $0;
6302		my $perl = $^X;
6303
6304		unlink("$base/$pidfile". "_Secondary");
6305
6306		if ( $^O eq "MSWin32" ) {
6307    		$assp = $base.'\\'.$assp if ($assp !~ /\Q$base\E/io);
6308    		$assp =~ s/\//\\/go;
6309    		my $asspbase = $base;
6310    		$asspbase =~ s/\\/\//go;
6311
6312    		$cmd = "sleep 10;\"$perl\" \"$assp\" \"$asspbase\" --AsASecondary:=1";
6313		} else {
6314    		$assp = $base.'/'.$assp if ($assp !~ /\Q$base\E/io);
6315    		$cmd = "sleep 10;\"$^X\" \"$assp\" \"$base\"  --AsASecondary:=1";
6316		}
6317        d('Secondary - start');
6318        $cmd = $SecondaryCmd if $SecondaryCmd;
6319
6320        mlog( 0, "Info: AutostartSecondary started '$cmd'" );
6321
6322        system($cmd);
6323
6324		$SecondaryRunning = 1;
6325
6326        return 1;
6327
6328}
6329sub restartSecondary {
6330		return if !$AsASecondary;
6331
6332		my $cmd;
6333		my $assp = $0;
6334		my $perl = $^X;
6335
6336		if ( $^O eq "MSWin32" ) {
6337    		$assp = $base.'\\'.$assp if ($assp !~ /\Q$base\E/io);
6338    		$assp =~ s/\//\\/go;
6339    		my $asspbase = $base;
6340    		$asspbase =~ s/\\/\//go;
6341
6342    		$cmd = "\"$perl\" \"$assp\" \"$asspbase\" --AsASecondary:=1";
6343		} else {
6344    		$assp = $base.'/'.$assp if ($assp !~ /\Q$base\E/io);
6345    		$cmd = "\"$^X\" \"$assp\" \"$base\"  --AsASecondary:=1";
6346		}
6347        d('Secondary - start');
6348        $cmd = $SecondaryCmd if $SecondaryCmd;
6349
6350		printSecondary( "restarted");
6351        system($cmd);
6352
6353        exit 1;
6354
6355}
6356sub downASSP {
6357    my $text = shift;
6358
6359    return if $AsASecondary;
6360    return if $doShutdownForce;
6361    $doShutdownForce = 1;
6362    foreach (keys %SIG) {
6363       $SIG{$_} = {};
6364    }
6365    &closeAllSMTPListeners;
6366    &SaveStats;
6367	&SaveCache();
6368	&SavePB;
6369	&syncWriteConfig() if $enableCFGShare;
6370
6371	&SaveWhitelist();
6372	&SaveRedlist();
6373
6374    &closeAllWEBListeners;
6375
6376
6377    &RemovePid;
6378    mlog(0,"$text");
6379
6380
6381    return if !$AutostartSecondary;
6382
6383	&readSecondaryPID();
6384	mlog(0,"terminating Secondary (PID: $SecondaryPid)") if $SecondaryPid;
6385	kill INT => $SecondaryPid if $SecondaryPid;
6386
6387
6388}
6389sub printSecondary {
6390    my $text = shift;
6391    my $time = &timestring();
6392#    print "$time Secondary($$): $text\n";
6393    $silent=0;
6394    mlog(0,"$assp($$): $text");
6395    $silent=1;
6396}
6397
6398sub downSecondary {
6399    my $text = shift;
6400
6401    return if !$AsASecondary;
6402	foreach my $WebSock (@WebSocket) {
6403            unpoll($WebSock,$readable);
6404            unpoll($WebSock,$writable);
6405            close($WebSock) || eval{$WebSock->close;} || eval{$WebSock->kill_socket();};
6406            delete $SocketCalls{$WebSock};
6407    }
6408
6409	printSecondary( "$text");
6410	unlink("$base/$pidfile"."_Secondary");
6411	exit 1;
6412}
6413
6414sub closeAllSMTPListeners {
6415
6416        mlog(0,"info: removing all SMTP listeners");
6417        foreach my $lsn (@lsn ) {
6418            eval{close($lsn);} if $lsn;
6419        }
6420
6421        foreach my $lsn (@lsn2 ) {
6422            eval{close($lsn);} if $lsn;
6423        }
6424
6425        foreach my $lsn (@lsnSSL ) {
6426            eval{close($lsn);} if $lsn;
6427        }
6428
6429        foreach my $lsn (@lsnRelay ) {
6430            eval{close($lsn);} if $lsn;
6431        }
6432
6433}
6434
6435sub closeAllWEBListeners {
6436		my $lsn;
6437        mlog(0,"info: removing all WEB listeners");
6438
6439		foreach my $StatSock (@StatSocket) {
6440            $readable->remove($StatSock);
6441            close($StatSock) || eval{$StatSock->close;} || eval{$StatSock->kill_socket();};
6442    	}
6443        foreach my $WebSock (@WebSocket) {
6444            unpoll($WebSock,$readable);
6445            unpoll($WebSock,$writable);
6446            close($WebSock) || eval{$WebSock->close;} || eval{$WebSock->kill_socket();};
6447            delete $SocketCalls{$WebSock};
6448    	}
6449}
6450
6451sub cmdToThread {
6452    my ($sub,$parm) = @_;
6453
6454    mlog(0,"info:  '$sub' unkown");
6455
6456}
6457sub init {
6458    my $ver;
6459
6460    my $perlver = $];
6461
6462    mlog( 0, "$PROGRAM_NAME version $version$modversion (Perl $]) initializing " );
6463    mlog( 0, "Starting as root") if $< == 0 && $^O ne "MSWin32";
6464	mlog( 0, "Error: Starting not as root!!!") if $< != 0 && $^O ne "MSWin32";
6465    mlog( 0, "Perl>= 5.8 needed for Webinterface!" ) if $] <= "5.008";
6466    $ver = IO::Socket->VERSION;
6467  	if (! $ver gt '1.30') {
6468      *{'IO::Socket::blocking'} = *{'main::assp_blocking'};   # MSWIN32 fix for nonblocking Sockets
6469      mlog(0,"IO::Socket version $ver is too less - recommended is 1.30_01 - hook ->blocking to internal procedure");
6470  	}
6471 	if ($localhostname) {
6472     	mlog(0,"ASSP version $version$modversion (Perl $]) (on $^O)	/ $localhostname ($localhostip)");
6473 	} else {
6474     	mlog(0,"ASSP version $version$modversion (Perl $]) (on $^O) running on server: localhost ($localhostip)") ;
6475 	}
6476    if ($CanUseAvClamd) {
6477    	if ($hConfig->{modifyClamAV}) {
6478    		*{'File::Scan::ClamAV::ping'} = *{'main::ClamScanPing'};
6479    		*{'File::Scan::ClamAV::streamscan'} = *{'main::ClamScanScan'};
6480
6481    	}
6482        my $clamavd = File::Scan::ClamAV->new(port => $AvClamdPort);
6483
6484
6485        if ( !$UseAvClamd ) {
6486            $AvailAvClamd = 1;
6487            $ver          = $clamavd->VERSION;
6488            $VerAvClamd   = $ver;
6489            $ver          = " version $ver" if $ver;
6490            mlog( 0, "File::Scan::ClamAV module$ver installed but disabled" );
6491            $CommentAvClamd = "<span class=negative>installed but disabled";
6492
6493        } elsif ( $clamavd->ping() ) {
6494            $AvailAvClamd = 1;
6495            $ver          = $clamavd->VERSION;
6496            $VerAvClamd   = $ver;
6497            $ver          = " version $ver" if $ver;
6498            mlog( 0, "File::Scan::ClamAV module$ver installed and ready" );
6499            $CommentAvClamd = "<span class=positive>installed and ready";
6500            mlog( 0, "File::Scan::ClamAV modifyClamAV enabled" ) if $hConfig->{modifyClamAV} ;
6501            $CommentAvClamd .= "<br />modifyClamAV enabled" if $hConfig->{modifyClamAV};
6502        } else {
6503            $AvailAvClamd = 0;
6504            $ver          = $clamavd->VERSION;
6505            $VerAvClamd   = $ver;
6506            $ver          = " version $ver" if $ver;
6507            mlog( 0, "File::Scan::ClamAV module$ver installed but AvClamdPort not ready, error: ". $clamavd->errstr() );
6508
6509
6510            $CommentAvClamd =
6511              "<span class=negative>installed but AvClamdPort not ready, error:<br> " . $clamavd->errstr();
6512            $CommentAvClamd .= "<br />modifyClamAV enabled" if $hConfig->{modifyClamAV};
6513        }
6514    } else {
6515        $AvailAvClamd = 0;
6516        $VerAvClamd   = "";
6517        mlog( 0, "File::Scan::ClamAV module not installed" );
6518        $CommentAvClamd = "<span class=negative>not installed";
6519    }
6520
6521
6522    if ($CanUseLDAP) {
6523        $ver        = eval('Net::LDAP->VERSION');
6524        $VerNetLDAP = $ver;
6525        $ver        = " version $ver" if $ver;
6526        mlog( 0, "Net::LDAP module$ver installed and available" );
6527        $CommentNetLDAP = "<span class=positive>LDAP available";
6528    } else {
6529        mlog( 0, "Net::LDAP module not installed" );
6530        $CommentNetLDAP = "<span class=negative>LDAP not available";
6531    }
6532    if ($CanUseDNS) {
6533        $ver       = eval('Net::DNS->VERSION');
6534        $VerNetDNS = $ver;
6535        $ver       = " version $ver" if $ver;
6536        mlog( 0, "Net::DNS module$ver installed" );
6537        $CommentNetDNS = "<span class=positive>DNS Resolver available";
6538    } else {
6539        $CommentNetDNS = "<span class=negative>DNS Resolver not available";
6540        mlog( 0, "Net::DNS module not installed" );
6541    }
6542    if ($CanUseAddress) {
6543        $ver           = eval('Email::Valid->VERSION');
6544        $VerEmailValid = $ver;
6545        $ver           = " version $ver" if $ver;
6546        mlog( 0, "email::Valid module$ver installed and available" );
6547        $CommentEmailValid = "<span class=positive>RFC5322 checks available";
6548    } else {
6549        $CommentEmailValid = "<span class=negative>RFC5322 checks not available";
6550        mlog( 0, "email::Valid module not installed" );
6551
6552    }
6553    if ($CanUseEMS) {
6554        $ver    = eval('Email::Send->VERSION');
6555        $VerEMS = $ver;
6556        $ver    = " version $ver" if $ver;
6557        mlog( 0,
6558            "Email::Send module$ver installed - notification, email-interface, blockreports and resend available" );
6559        $CommentEMS = "<span class=positive>notification, email-interface, blockreports and resend available";
6560    } elsif ( !$AvailEMS ) {
6561        $CommentEMS = "<span class=negative>notification, email-interface, blockreports and resend not available";
6562        mlog( 0,
6563"Email::Send module not installed - notification, email-interface, blockreports and resend is not available"
6564        );
6565    }
6566
6567	if ($CanUseAuthenSASL) {
6568    	$ver=eval('Authen::SASL->VERSION');
6569    	$VerAuthenSASL=$ver;
6570    	$ver=" version $ver" if $ver;
6571    	mlog(0,"Authen::SASL module$ver installed - SMTP AUTH is available");
6572    	$CommentAuthenSASL= "<span class=positive>SMTP AUTH is available</span>";
6573  	} elsif (!$AvailAuthenSASL)  {
6574		$CommentAuthenSASL= "<span class=negative>SMTP AUTH is not available</span>";
6575    	mlog(0,"Authen::SASL module not installed - SMTP AUTH is not available");
6576  	}
6577
6578    if ($CanUseSPF) {
6579        $ver = eval('Mail::SPF->VERSION');
6580        $ver =~ s/^v//gio;    # strip leading 'v'
6581        $VerMailSPF = $ver;
6582        $ver = " version $ver" if $ver;
6583        if ( $VerMailSPF >= 2.001 ) {
6584            mlog( 0, "Mail::SPF module$ver installed and available" );
6585            $CommentMailSPF =
6586"<span class=positive>SPF installed";
6587        } else {
6588            mlog( 0, "Mail::SPF module$ver installed but must be >= 2.001" );
6589            mlog( 0, "Mail::SPF will not be used." );
6590            $CommentMailSPF = "<span class=negative>disabled, must be >= 2.001";
6591            $CanUseSPF      = 0;
6592        }
6593    } elsif ($AvailSPF) {
6594        $ver = eval('Mail::SPF->VERSION');
6595        $ver =~ s/^v//gio;    # strip leading 'v'
6596        $ver = " version $ver" if $ver;
6597        mlog( 0, "Mail::SPF module$ver installed but Net::DNS required" );
6598        $CommentMailSPF = "<span class=negative>will not be used, Net::DNS required";
6599    } else {
6600        mlog( 0, "Mail::SPF module not installed" );
6601        $CommentMailSPF = "<span class=negative>module not installed";
6602    }
6603    if ($CanUseSRS) {
6604        $ver        = eval('Mail::SRS->VERSION');
6605        $VerMailSRS = $ver;
6606        $ver        = " version $ver" if $ver;
6607        mlog( 0,
6608            "Mail::SRS module$ver installed - Sender Rewriting Scheme available"
6609        );
6610        $CommentMailSRS = "<span class=positive>SRS available";
6611    } elsif ( !$AvailSRS ) {
6612        mlog( 0,
6613            "Mail::SRS module not installed - Sender Rewriting Scheme disabled"
6614        ) if $EnableSRS;
6615        $CommentMailSRS = "<span class=negative>SRS not installed";
6616    }
6617    if ($CanUseHTTPCompression) {
6618        $ver                 = eval('Compress::Zlib->VERSION');
6619        $VerCompressZlib     = $ver;
6620        $ver                 = " version $ver" if $ver;
6621        $CommentCompressZlib = "<span class=positive>HTTP compression available";
6622        mlog( 0,
6623            "Compress::Zlib module$ver installed - HTTP compression available"
6624        );
6625    } elsif ( !$AvailZlib ) {
6626        mlog( 0,
6627            "Compress::Zlib module not installed - HTTP compression disabled" );
6628        $CommentCompressZlib = "<span class=negative>HTTP compression not available";
6629    }
6630    if ($CanUseMD5) {
6631        $ver          = eval('Digest::MD5->VERSION');
6632        $VerDigestMD5 = $ver;
6633        $ver          = " version $ver" if $ver;
6634        mlog( 0,
6635"Digest::MD5 module$ver installed - Greylisting/Delaying can use MD5 keys for hashes"
6636        );
6637        $CommentDigestMD5 = "<span class=positive>Greylisting/Delaying can use MD5 keys for hashes";
6638    } else {
6639        mlog( 0,
6640"Digest::MD5 module$ver not installed - Greylisting/Delaying can not use MD5 keys for hashes"
6641        );
6642        $CommentDigestMD5 = "<span class=negative>Greylisting/Delaying can not use MD5 keys for hashes</span>";
6643    }
6644	if ($CanUseSHA1) {
6645        $ver          = eval('Digest::SHA1->VERSION');
6646        $VerDigestSHA1 = $ver;
6647        $ver          = " version $ver" if $ver;
6648        mlog( 0,
6649"Digest::SHA1 module$ver installed - Message-ID tagging (FBMTV) available"
6650        );
6651        $CommentDigestSHA1 = "<span class=positive>Message-ID tagging (FBMTV) available</span>";
6652    } else {
6653        mlog( 0,
6654"Digest::SHA1 module$ver not installed - Message-ID tagging (FBMTV)  not available"
6655        );
6656        $CommentDigestSHA1 = "<span class=negative>Message-ID tagging (FBMTV) not available</span>";
6657    }
6658    if ($CanSearchLogs) {
6659        $ver                      = eval('File::ReadBackwards->VERSION');
6660        $VerFileReadBackwards     = $ver;
6661        $CommentFileReadBackwards = "<span class=negative>searching of log files not available";
6662        $ver                      = " version $ver" if $ver;
6663        mlog( 0,
6664"File::ReadBackwards module$ver installed - searching of log files enabled"
6665        );
6666        $CommentFileReadBackwards = "<span class=positive>searching of log files enabled";
6667    } elsif ( !$AvailReadBackwards ) {
6668        mlog( 0,
6669"File::ReadBackwards module not installed - searching of log files disabled"
6670        );
6671        $CommentFileReadBackwards = "<span class=negative>searching of log files disabled";
6672    }
6673    if ($CanStatCPU) {
6674        $ver          = eval('Time::HiRes->VERSION');
6675        $VerTimeHiRes = $ver;
6676        $ver          = " version $ver" if $ver;
6677        mlog( 0,
6678            "Time::HiRes module$ver installed - CPU usage statistics available"
6679        );
6680        $CommentTimeHiRes = "<span class=positive>CPU statistics available";
6681    } elsif ( !$AvailHiRes ) {
6682        $CommentTimeHiRes = "<span class=negative>CPU statistics disabled";
6683        mlog( 0,
6684            "Time::HiRes module not installed - CPU usage statistics disabled"
6685        );
6686    }
6687    if ($CanChroot) {
6688        $ver = eval('PerlIO::scalar->VERSION');
6689        $ver = " version $ver" if $ver;
6690        if ($ChangeRoot)  {
6691        	mlog( 0, "PerlIO::scalar module$ver installed - chroot savy" );
6692			mlog(0,"error: ChangeRoot - /etc/protocols in $ChangeRoot not found!") unless -e "$ChangeRoot/etc/protocols";
6693		}
6694    }
6695    if ($CanUseSyslog) {
6696        $ver              = eval('Sys::Syslog->VERSION');
6697        $VerSysSyslog     = $ver;
6698        $ver              = " version $ver" if $ver;
6699        $CommentSysSyslog = "<span class=positive>Unix centralized logging installed";
6700        mlog( 0,
6701"Sys::Syslog module$ver installed - Unix centralized logging enabled"
6702        );
6703    } elsif ( !$AvailSyslog ) {
6704        $CommentSysSyslog = "<span class=negative>nix centralized logging disabled";
6705        mlog( 0, "Sys::Syslog module not installed." )
6706          if $sysLog && !$sysLogPort;
6707    }
6708	if( $^O eq "MSWin32") {
6709    if ($CanUseNetSyslog) {
6710        $ver              = eval('Net::Syslog->VERSION');
6711        $VerNetSyslog     = $ver;
6712        $ver              = " version $ver" if $ver;
6713        $CommentNetSyslog = "Network Syslog logging installed";
6714        mlog( 0,
6715            "Net::Syslog module$ver installed - network Syslog logging enabled"
6716        );
6717    } elsif ( !$AvailNetSyslog ) {
6718        $CommentNetSyslog = "<span class=negative>Network Syslog logging disabled";
6719        mlog( 0, "Net::Syslog module not installed." )
6720          if $sysLog && $sysLogPort;
6721    }
6722    }
6723	if( $^O eq "MSWin32") {
6724    	if ($CanUseWin32Daemon) {
6725        	$ver            = eval('Win32::Daemon->VERSION');
6726        	$VerWin32Daemon = $ver;
6727        	$ver            = " version $ver" if $ver;
6728        	mlog( 0,
6729            "Win32::Daemon module$ver installed - can run as Win32 service" );
6730        	$CommentWin32Daemon = "<span class=positive>can run as Win32 service";
6731    	} else {
6732
6733        	$CommentWin32Daemon = "<span class=negative>module not installed";
6734    	}
6735    }
6736
6737
6738    if ($CanUseTieRDBM) {
6739        $ver         = eval('Tie::RDBM->VERSION');
6740        $VerRDBM     = $ver;
6741        $ver         = " version $ver" if $ver;
6742        $CommentRDBM = "<span class=positive>mysql usage available";
6743        mlog( 0, "Tie::RDBM module$ver installed - mysql usage available" );
6744    } elsif ( !$AvailTieRDBM ) {
6745        $CommentRDBM = "<span class=negative>mysql usage not available";
6746        mlog( 0, "Tie::RDBM module not installed - mysql usage not available" );
6747    }
6748
6749    if ($CanMatchCIDR) {
6750        $ver         = eval('Net::IP::Match::Regexp->VERSION');
6751        $VerCIDR     = $ver;
6752        $ver         = " version $ver" if $ver;
6753        $CommentCIDR = "<span class=positive>CIDR notation available";
6754        mlog( 0,
6755            "Net::IP::Match::Regexp module$ver installed - CIDR notation for IP range available"
6756        );
6757    } else {
6758        $CommentCIDR = "<span class=negative>CIDR notation not available";
6759        mlog( 0,
6760            "Net::IP::Match::Regexp module not installed - CIDR notation for IP range not available"
6761        );
6762    }
6763    if ($CanUseCIDRlite) {
6764        $ver             = eval('Net::CIDR::Lite->VERSION');
6765        $VerCIDRlite     = $ver;
6766        $ver             = " version $ver" if $ver;
6767        $CommentCIDRlite = "<span class=positive>Hyphenated IP address range available";
6768        mlog( 0,
6769"Net::CIDR::Lite module$ver installed - Hyphenated IP address range available"
6770        );
6771    } elsif ( !$AvailCIDRlite ) {
6772        $CommentCIDRlite = "<span class=negative>Hyphenated IP address range not available";
6773        mlog( 0,
6774"Net::CIDR::Lite module not installed - Hyphenated IP address range not available"
6775        );
6776    }
6777    if ($CanUseSenderBase) {
6778        $ver           = eval('Net::SenderBase->VERSION');
6779        $VerSenderBase = $ver;
6780        $ver           = " version $ver" if $ver;
6781        mlog( 0,
6782"Net::SenderBase module$ver installed - SenderBase Queries available"
6783        );
6784        $CommentSenderBase = "<span class=positive>SenderBase Queries available";
6785    } elsif ( !$AvailSenderBase ) {
6786        $CommentSenderBase = "<span class=negative>SenderBase Queries not available";
6787        mlog( 0,
6788"Net::SenderBase module not installed - SenderBase Queries not available"
6789        );
6790    }
6791    if ($CanUseLWP) {
6792        $ver        = eval('LWP::Simple->VERSION');
6793        $VerLWP     = $ver;
6794        $ver        = " version $ver" if $ver;
6795        $CommentLWP = "<span class=positive>Download griplist available";
6796        mlog( 0, "LWP::Simple module$ver installed - griplist available" );
6797    } elsif ( !$AvailLWP ) {
6798        $CommentLWP = "<span class=negative>Download griplist not available";
6799        mlog( 0, "LWP::Simple module not installed - griplist not available" );
6800    }
6801
6802    if ($CanUseEMM) {
6803        $ver    = eval('Email::MIME->VERSION');
6804        $VerEMM = $ver;
6805        $ver    = " version $ver" if $ver;
6806        mlog( 0,
6807"Email::MIME module$ver installed - attachments detection available"
6808        );
6809        $CommentEMM = "<span class=positive>Attachments detection available";
6810                $org_Email_MIME_parts_multipart = *{'Email::MIME::parts_multipart'};
6811    	*{'Email::MIME::parts_multipart'} = *{'main::parts_multipart'};
6812    	*{'Email::MIME::ContentType::_extract_ct_attribute_value'} = *{'assp_extract_ct_attribute_value'};
6813    	*{'Email::MIME::ContentType::_parse_attributes'} = *{'assp_parse_attributes'};
6814
6815    } elsif ( !$AvailEMM ) {
6816
6817        mlog( 0,
6818"Email::MIME module not installed - attachments detection not available"
6819        );
6820        $CommentEMM = "<span class=negative>Attachments detection & blockreport not available";
6821    }
6822
6823    if ($CanUseNetSMTP) {
6824        $ver        = eval('Net::SMTP->VERSION');
6825        $VerNetSMTP = $ver;
6826        $ver        = " version $ver" if $ver;
6827        mlog( 0,
6828            "Net::SMTP module$ver installed - VRFY Recipients available" );
6829        $CommentNetSMTP = "<span class=positive>VRFY Recipients available";
6830    } elsif ( !$AvailNetSMTP ) {
6831        $CommentNetSMTP = "<span class=negative>VRFY Recipients not available";
6832        mlog( 0,
6833            "Net::SMTP module not installed - VRFY Recipients not available"
6834        );
6835    }
6836
6837
6838
6839    if ($AvailIOSocketSSL) {
6840        $ver            = eval('IO::Socket::SSL->VERSION');
6841        $VerIOSocketSSL = $ver;
6842		if ($VerIOSocketSSL < 1.08) {
6843	    	$CommentIOSocketSSL = "<span class=negative>Version >= 1.08 required - SSL support not available";
6844	    	mlog( 0, "IO::Socket::SSL module$ver installed - Version >= 1.08 required, SSL support not available ");
6845	    	$AvailIOSocketSSL = 0;
6846     	} else {
6847            $ver            = " version $ver" if $ver;
6848            $CommentIOSocketSSL = "<span class=positive>Secure SSL sockets installed";
6849            mlog( 0, "IO::Socket::SSL module$ver installed");
6850            $CanUseIOSocketSSL =
6851            	$AvailIOSocketSSL
6852            	&& -f $SSLCertFile
6853            	&& -r $SSLCertFile
6854            	&& -f $SSLKeyFile
6855            	&& -r $SSLKeyFile;
6856            if ($CanUseIOSocketSSL) {
6857            $CommentIOSocketSSL = "<span class=positive>SSL support available";
6858            $CommentIOSocketSSL .= "<span class=negative>TLS not available (enableSSL=off)" if !$enableSSL;
6859            $CommentIOSocketSSL .= "<span class=negative>TLS available (enableSSL=on)" if $enableSSL;
6860            mlog(0,"TLS on listenports is switched off by enableSSL") if !$enableSSL;
6861        	mlog(0,"TLS on listenports is switched on by enableSSL") if $enableSSL;
6862        	} else {
6863
6864            	$CommentIOSocketSSL = "SSL support not ready";
6865            	if ( !-f $SSLCertFile ) {
6866                	$CommentIOSocketSSL .= ", CertFile $SSLCertFile not found";
6867           	 	} elsif ( !-r $SSLCertFile ) {
6868                	$CommentIOSocketSSL .= ", CertFile $SSLCertFile not readable";
6869            	}
6870            	if ( !-f $SSLKeyFile ) {
6871                	$CommentIOSocketSSL .= ", KeyFile $SSLKeyFile not found";
6872            	} elsif ( !-r $SSLKeyFile ) {
6873                	$CommentIOSocketSSL .= ", KeyFile $SSLKeyFile not readable";
6874            	}
6875            	mlog( 0, "$CommentIOSocketSSL" );
6876        	}
6877
6878        }
6879
6880    } else {
6881        $CommentIOSocketSSL = "<span class=positive>Secure SSL sockets not installed";
6882        mlog( 0,
6883            "IO::Socket::SSL module not installed - SSL support not available"
6884        );
6885    }
6886
6887
6888    if (!$enableINET6) {
6889	    	$CommentIOSocketINET6 = "<span class=negative><span class=negative>IPv6 support is disabled in config (enableINET6)";
6890	    	mlog( 0,
6891	        "IO::Socket::INET6 module not checked   - IPv6 support is disabled in config (enableINET6)");
6892	    	$CanUseIOSocketINET6 = 0;
6893    } elsif ($AvailIOSocketINET6) {
6894
6895        $VerIOSocketINET6 = eval('IO::Socket::INET6->VERSION');
6896
6897	    if ($VerIOSocketINET6 < 2.56) {
6898	    	$CommentIOSocketINET6 = "<span class=negative>Version >= 2.56 required, IPv6 support not available";
6899	    	mlog( 0,
6900	        "IO::Socket::INET6 module$ver installed - but Version >= 2.56 required, IPv6 support not available"
6901	    	);
6902	    	$CanUseIOSocketINET6 = 0;
6903		} else {
6904            $ver            = " version $ver" if $ver;
6905            my $sys = ($SysIOSocketINET6 == 1) ? '' : ' - but IPv6 is not supported by your system';
6906            $CommentIOSocketINET6 = "<span class=positive>IPv6 installed and available$sys";
6907            mlog( 0,
6908                "IO::Socket::INET6 module$ver installed - IPv6 installed and available$sys"
6909            );
6910            $CanUseIOSocketINET6 = 1;
6911            $CanUseIOSocketINET6 = 0 if !$SysIOSocketINET6;
6912       }
6913    } else {
6914        $CommentIOSocketINET6 = "<span class=negative>IPv6 support not installed";
6915        mlog( 0, "IO::Socket::INET6 module not installed - IPv6 support not available" );
6916
6917        $CanUseIOSocketINET6 = 0;
6918    }
6919
6920
6921	my $not;
6922    $readable = new IO::Select();
6923    $writable = new IO::Select();
6924
6925    my $usessl = "HTTP";
6926	sleep 10;
6927    my $adminport = $webAdminPort;
6928    $adminport = $webSecondaryPort if $AsASecondary && $webSecondaryPort;
6929	my @dummy;
6930	my ($WebSocket,$dummy);
6931    if ($CanUseIOSocketSSL && $enableWebAdminSSL) {
6932      ($WebSocket,$dummy)   = newListenSSL($adminport,\&NewWebConnection,1);
6933      @WebSocket = @$WebSocket;
6934      for (@$dummy) {s/:::/\[::\]:/o;}
6935      mlog(0,"listening for admin HTTPS connections on webAdminPort @$dummy") if @$dummy;
6936      $webAdminPortOK = 1 if @$dummy;
6937      mlog(0,"not listening for admin HTTPS connections on webAdminPort $adminport") if !@$dummy;
6938      $webAdminPortOK = 0 if !@$dummy;
6939
6940  	} else {
6941      ($WebSocket,$dummy)   = newListen($adminport,\&NewWebConnection,1);
6942      for (@$dummy) {s/:::/\[::\]:/o;}
6943	  mlog(0,"listening for admin HTTP connections on webAdminPort @$dummy") if @$dummy;
6944	  $webAdminPortOK = 1 if @$dummy;
6945	  mlog(0,"not listening for admin HTTP connections on webAdminPort $adminport") if !@$dummy;
6946	  mlog(0,"ASSP exiting (already running)!!!!!!!!!!!!!!!!!!") if !@$dummy;
6947	  exit 1 if !@$dummy;
6948	  $webAdminPortOK = 0 if !@$dummy;
6949  	}
6950	if ($AsASecondary) {
6951		if (!@$dummy) {
6952			my $Pid = &readSecondaryPID();
6953
6954			printSecondary( "already running as PID=$SecondaryPid") if $Pid;
6955			printSecondary( "not listening on webSecondaryPort $adminport") if !$Pid;
6956
6957    		$webAdminPortOK = 0;
6958    		exit 1;
6959
6960    	} else {
6961
6962    		$webAdminPortOK = 1;
6963    		printSecondary( "listening on webSecondaryPort @$dummy") ;
6964
6965			my $F;
6966    		if ($pidfile) { open( my $FH, ">","$base/$pidfile"."_Secondary" ); print $FH "$$"; close $FH; }
6967
6968    		my $pid = &checkPrimaryPID();
6969			kill PIPE => $pid if $pid;
6970		}
6971	}
6972
6973
6974    if (!$AsASecondary) {
6975    my ($lsn,$lsnI)        = newListen( $listenPort,   \&NewSMTPConnection );
6976    @lsn = @$lsn; @lsnI = @$lsnI;
6977    for (@$lsnI) {s/:::/\[::\]:/o;}
6978    $not = "NOT " if !$lsn[0];
6979    mlog( 0, "NOT listening for SMTP connections on listenPort $listenPort" ) if !$lsn[0];
6980	mlog(0,"listening for SMTP connections on listenPort @$lsnI") if $lsn[0];
6981
6982    my ($StatSocket,$dummy); my @dummy;
6983    if ($CanUseIOSocketSSL && $enableWebStatSSL) {
6984    	my ($StatSocket,$dummy) = newListenSSL( $webStatPort,  \&NewStatConnection );
6985    	@StatSocket = @$StatSocket;
6986    	for (@$dummy) {s/:::/\[::\]:/o;}
6987    	mlog(0,"listening for statistics HTTPS connections on webStatPort @$dummy") if @$dummy;
6988    	mlog(0,"not listening for statistics HTTPS connections on webStatPort $webStatPort") if !@$dummy;
6989	} else {
6990    	my ($StatSocket,$dummy) = newListen( $webStatPort,  \&NewStatConnection );
6991    	@StatSocket = @$StatSocket;
6992    	for (@$dummy) {s/:::/\[::\]:/o;}
6993    	mlog(0,"listening for statistics HTTP connections on webStatPort @$dummy") if @$dummy;
6994    	mlog(0,"not listening for statistics HTTPS connections on webStatPort $webStatPort") if !@$dummy;
6995	}
6996
6997    if ($listenPortSSL) {
6998        if ($CanUseIOSocketSSL) {
6999            my ($lsnSSL,$lsnSSLI) = newListenSSL($listenPortSSL, \&NewSMTPConnection );
7000            @lsnSSL = @$lsnSSL; @lsnSSLI = @$lsnSSLI;
7001      		for (@$lsnSSLI) {s/:::/\[::\]:/o;}
7002            mlog( 0, "listening for SMTPS (SSL) connections on listenPortSSL @$lsnSSLI" )
7003              if $lsnSSL[0];
7004            mlog( 0,
7005                "NOT listening for SMTPS (SSL) connections on listenPortSSL $listenPortSSL" )
7006              if !$lsnSSL[0];
7007        } else {
7008            mlog( 0,
7009                "listening for SMTPS (SSL) connections on listenPortSSL $listenPortSSL not enabled" );
7010        }
7011    }
7012
7013    if ($listenPort2) {
7014        my ($lsn2,$lsn2I) = newListen( $listenPort2, \&NewSMTPConnection );
7015        @lsn2 = @$lsn2; @lsn2I = @$lsn2I;
7016    	for (@$lsn2I) {s/:::/\[::\]:/o;}
7017        mlog(0,"listening for additional SMTP connections on listenPort2 @$lsn2I")
7018          if $lsn2[0];
7019        mlog( 0,
7020            "NOT listening for additional SMTP connections on listenPort2 $listenPort2" )
7021          if !$lsn2[0];
7022    }
7023
7024    # handle the possible relayhost / smarthost option
7025    if ( $relayHost && $relayPort ) {
7026
7027        my ($lsnRelay,$lsnRelayI) = newListen( $relayPort, \&NewSMTPConnection );
7028        @lsnRelay = @$lsnRelay; @lsnRelayI = @$lsnRelayI;
7029    	for (@$lsnRelayI) {s/:::/\[::\]:/o;}
7030        mlog( 0, "listening for SMTP relay connections on relayPort @$lsnRelayI" )
7031          if $lsnRelay[0];
7032        mlog( 0, "NOT listening for SMTP relay connections on relayPort $relayPort" )
7033          if !$lsnRelay[0];
7034    }
7035	}
7036
7037    $nextNoop           = time;
7038
7039    $endtime            = $nextNoop + $AutoRestartInterval * 3600;
7040
7041
7042
7043
7044    $saveWhite          = $nextNoop + $UpdateWhitelist;
7045    $nextCleanDelayDB   = $nextNoop + $CleanDelayDBInterval;
7046
7047#    $nextCleanTrap      = $nextNoop + $PBTrapCacheInterval * 3600;
7048#    $nextCleanCache     = $nextNoop + $CleanCacheEvery * 3600;
7049
7050	$check4queuetime	= $nextNoop + 60;
7051    $nextConCheck       = $nextNoop + 180;
7052
7053
7054    $nextDestinationCheck       = $nextNoop + 30;
7055    $nextResendMail 	= $nextNoop + 60;
7056    $nextLDAPcrossCheck = $nextNoop + 60;
7057    $nextdetectHourJob 	= int($nextNoop / 3600) * 3600 + 3600;
7058    $nextdetectHourJob += 15 unless $nextdetectHourJob % (24 * 3600);
7059    my $m = &getTimeDiff($nextdetectHourJob-$nextNoop);
7060  	mlog(0,"info: hourly scheduler is starting in $m") if $MaintenanceLog >=2;
7061  	mlog(0,"info: DEBUG (debug) is set") if $debug;
7062    $nextDNSCheck 		= $nextNoop + 600;
7063    $nextSCANping 		= $nextNoop + 300;
7064
7065    $NextVersionFileDownload = $nextNoop + 600;
7066    $NextASSPFileDownload = $nextNoop + 900;
7067    $NextPOP3Collect 	= $nextNoop + 300;
7068#    my ($file) = $TLDS =~ /^ *file: *(.+)/io;
7069#   $NextTLDlistDownload = time + 120 if (-e "$base/$file");
7070    $NextBackDNSFileDownload = $nextNoop + 300;
7071    $NextSyncConfig= $nextNoop + 60;
7072
7073
7074    $nextCleanIPDom 	= $nextNoop + 300;
7075    $nextDebugClear 	= $nextNoop + $DebugRollTime;
7076    my $assp = $0;
7077    $assp =~ s/\\/\//g;
7078	$assp = $base.'/'.$assp if ($assp !~ /\Q$base\E/io);
7079    if (-e $assp) {
7080            $FileUpdate{"$assp".'asspCode'} = ftime($assp);
7081            mlog(0,"info: watching the running script '$assp' for changes")
7082              if ($AutoRestartAfterCodeChange && ($AsAService || $AsADaemon || $AutoRestartCmd));
7083    } elsif ($AutoRestartAfterCodeChange) {
7084            mlog(0,"warning: unable to find running script '$assp' for 'AutoRestartAfterCodeChange'")
7085              if ($AsAService || $AsADaemon || $AutoRestartCmd);
7086    }
7087
7088
7089
7090    my ($uid,$gid);
7091    ($uid,$gid) = getUidGid($runAsUser,$runAsGroup) if ($runAsUser || $runAsGroup);
7092    if ($ChangeRoot) {
7093        my $chroot;
7094        eval('$chroot=chroot($ChangeRoot)');
7095        if ($@) {
7096            my $msg = "request to change root to '$ChangeRoot' failed: $@";
7097            mlog( '', $msg );
7098            die ucfirst($msg);
7099        } elsif ( !$chroot ) {
7100            my $msg =
7101              "request to change root to '$ChangeRoot' did not succeed: $!";
7102            mlog( '', $msg );
7103            die ucfirst($msg);
7104        } else {
7105            $chroot = $ChangeRoot;
7106            $chroot =~ s/(\W)/\\$1/g;
7107            $base   =~ s/^$chroot//i;
7108            chdir("/");
7109            mlog( '',
7110"successfully changed root to '$ChangeRoot' -- new base is '$base'"
7111            );
7112        }
7113    }
7114
7115    switchUsers( $uid, $gid ) if ( $runAsUser || $runAsGroup );
7116    $mypid = $$;
7117    &renderConfigHTML();
7118    if ($pidfile && !$AsASecondary) { open( my $FH, ">","$base/$pidfile" ); print $FH $$; close $FH; }
7119
7120
7121  # create folders if they're missing
7122    -d "$base/$spamlog"        or mkdir "$base/$spamlog",        0755;
7123    -d "$base/$notspamlog"     or mkdir "$base/$notspamlog",     0755;
7124    -d "$base/$incomingOkMail" or mkdir "$base/$incomingOkMail", 0755;
7125    -d "$base/$discarded"      or mkdir "$base/$discarded",      0755;
7126    -d "$base/$viruslog"       or mkdir "$base/$viruslog",       0755;
7127    -d "$FileScanDir" 		   or mkdir "$FileScanDir",		     0755;
7128    -d "$base/tmp" 			   or mkdir "$base/tmp",			 0777;
7129    -d "$base/stats" 		   or mkdir "$base/stats",			 0777;
7130
7131    my $dir = $correctedspam;
7132    $dir =~ s/\/.*?$//;
7133    -d "$base/$dir"              or mkdir "$base/$dir",              0755;
7134    -d "$base/$correctedspam"    or mkdir "$base/$correctedspam",    0755;
7135    -d "$base/$correctednotspam" or mkdir "$base/$correctednotspam", 0755;
7136
7137
7138  $pbdir = $1 if $pbdb=~/(.*)\/.*/;
7139  if ($pbdir) {
7140     -d  "$base/$pbdir" or mkdir "$base/$pbdir",0755;
7141     -d  "$base/$pbdir/global" or mkdir "$base/$pbdir/global",0755;
7142     -d  "$base/$pbdir/global/in" or mkdir "$base/$pbdir/global/in",0755;
7143     -d  "$base/$pbdir/global/out" or mkdir "$base/$pbdir/global/out",0755;
7144  }
7145
7146    -d "$base/notes"       or mkdir "$base/notes",       0755;
7147    -d "$base/docs"        or mkdir "$base/docs",        0777;
7148    -d "$base/backup"      or mkdir "$base/backup",      0777;
7149    -d "$base/$resendmail" or mkdir "$base/$resendmail", 0777;
7150    -d "$base/files"       or mkdir "$base/files",       0755;
7151    -d "$base/logs"        or mkdir "$base/logs",        0755;
7152	-d "$base/starterdb"   or mkdir "$base/starterdb",   0777;
7153	-d "$base/cache"   	   or mkdir "$base/cache",     	 0777;
7154    -d "$base/reports"     or mkdir "$base/reports",     0755;
7155
7156  foreach (glob("$base/tmp/*")) {
7157      unlink "$_";
7158      mlog(0,"info: delete temporary file $_") if $MaintenanceLog;
7159  }
7160
7161
7162  if ($^O ne 'MSWin32') {
7163      if($setFilePermOnStart) {
7164
7165          &setPermission($base,oct('0777'),1,1) ;
7166          $Config{setFilePermOnStart} = '';
7167          $setFilePermOnStart = '';
7168          &SaveConfig();
7169      } elsif ($checkFilePermOnStart) {
7170
7171          &checkPermission($base,oct('0600'),1,1) ;
7172      }
7173  } else {
7174      if($setFilePermOnStart) {
7175
7176          $Config{setFilePermOnStart} = $setFilePermOnStart = '';
7177      } elsif ($checkFilePermOnStart) {
7178
7179          $Config{checkFilePermOnStart} = $checkFilePermOnStart = '';
7180      }
7181  }
7182
7183  mlog(0,"ASSP restart will be done with AutoRestartCmd: $AutoRestartCmd") if $MaintenanceLog;
7184
7185
7186	&writeRebuild;
7187	&downloadStarterDB if $spamdb && $BayesianStarterDB && $enableStarterDB;
7188    # put this after chroot so the paths don't change
7189    mlog(0,"warning: no filepath (spamdb) for Bayesian spam database!") if !$spamdb;
7190    if ($spamdb) {
7191    	if (!-e "$base/$spamdb") {
7192    		if (-e "$base/$spamdb.bak") {
7193    		 	copy("$base/$spamdb.bak","$base/$spamdb");
7194    		}
7195    	}
7196    	if ( $CanUseTieRDBM && $spamdb =~ /mysql/ ) {
7197        eval {
7198            $SpamdbObject = tie %Spamdb, 'Tie::RDBM',
7199              "dbi:mysql:database=$mydb;host=$myhost",
7200              {
7201                user     => "$myuser",
7202                password => "$mypassword",
7203                table    => 'spamdb',
7204                create   => 1
7205              };
7206        };
7207        if ($@) {
7208            mlog( 0, "spamdb mysql error: $@" );
7209            $CanUseTieRDBM = 0;
7210            $spamdb   = "spamdb";
7211        }
7212    } else {
7213        $SpamdbObject = tie %Spamdb, 'orderedtie', "$base/$spamdb";
7214        $spamdbcount = scalar keys %Spamdb;
7215  		$haveSpamdb = $spamdbcount;
7216  		# check if there are at least 500 records in spamdb (~10KB)
7217
7218  		if ($spamdbcount < 500) {
7219  			mlog(0,"warning: Bayesian spam database (spamdb) has only $spamdbcount records!") ;
7220			$asspWarnings .= "<span class=negative>warning: Bayesian spam database (spamdb) has only $spamdbcount records!</span><br />";
7221			$haveSpamdb = 0;
7222  		} else {
7223
7224  			mlog(0,"info: Bayesian spamdb '$spamdb' with $haveSpamdb records") if -e "$base/$spamdb";
7225  		};
7226    }
7227
7228    	$StarterdbObject = tie %Starterdb, 'orderedtie', "$base/$BayesianStarterDB" ;
7229    	$spamdbcount = 0;
7230
7231
7232  		$spamdbcount = scalar keys %Starterdb;
7233  		$haveStarterdb = $spamdbcount;
7234  		# check if there are at least 500 records in Starterdb (~10KB)
7235  		if (-e "$base/$BayesianStarterDB" && $enableStarterDB){
7236  			if ($spamdbcount < 500) {
7237  				mlog(0,"warning: Bayesian starter database (spamdb) has only $spamdbcount records!") ;
7238				$asspWarnings .= "<span class=negative>warning: Bayesian starter database (BayesianStarterDB) has only $spamdbcount records!</span><br />";
7239				$haveStarterdb = 0;
7240  			} else {
7241
7242  				mlog(0,"info: Bayesian starterdb '$BayesianStarterDB' with $haveStarterdb records") ;
7243  		}}
7244
7245  		if ( $RebuildSchedule eq "*" ) {
7246		mlog(0,"info: RebuildSchedule for RebuildSpamdb.pl is every hour");
7247		} else {
7248  			foreach my $shour ( split( /\|/, $RebuildSchedule ) ) {
7249			mlog(0,"info: RebuildSchedule for RebuildSpamdb.pl is $shour:00");
7250			}
7251  		}
7252
7253    }
7254    $HeloBlackObject = tie %HeloBlack, 'orderedtie', "$base/$spamdb.helo";
7255
7256
7257    if ($whitelistdb !~ /mysql/ && !-e "$base/$whitelistdb") {
7258
7259    	copy("$base/backup/$whitelistdb.yesterday.bak","$base/$whitelistdb");
7260		mlog( 0, "admininfo: '$base/backup/$whitelistdb.yesterday.bak' restored" );
7261    }
7262    if ( $CanUseTieRDBM && $whitelistdb =~ /mysql/ ) {
7263        eval {
7264            $WhitelistObject = tie %Whitelist, 'Tie::RDBM',
7265              "dbi:mysql:database=$mydb;host=$myhost",
7266              {
7267                user     => "$myuser",
7268                password => "$mypassword",
7269                table    => 'whitelist',
7270                create   => 1
7271              };
7272        };
7273        if ($@) {
7274            mlog( 0, "whitelist mysql error: $@" );
7275            $CanUseTieRDBM = 0;
7276            $whitelistdb   = "whitelist";
7277        }
7278    } else {
7279        $WhitelistObject = tie %Whitelist, 'orderedtie', "$base/$whitelistdb";
7280         # check if there are at least 50 records in whitelist (~1KB)
7281  		my $i = 0;
7282  		while (my ($k,$v) = each(%Whitelist)) {
7283    		$i++;
7284    		last if ($i > 50);
7285  		}
7286  		mlog(0,"warning: Whitelist (whitelistdb) has only $i records: (ignore if this is a new install)") if ($i < 50 );
7287
7288  		copy("$base/backup/$whitelistdb.yesterday.bak","$base/$whitelistdb") if ($i < 50 );
7289		mlog( 0, "admininfo: '$base/backup/$whitelistdb.yesterday.bak' restored" )if ($i < 50 );
7290    }
7291
7292    mlog(0,"warning: option 'decodeMIME2UTF8' is set, but Email::MIME is not installed") if $decodeMIME2UTF8 && !$CanUseEMM;
7293    $asspWarnings .= '<span class="negative">\'decodeMIME2UTF8\' is set, but Email::MIME is not installed!</span><br />' if $decodeMIME2UTF8 && !$CanUseEMM;
7294
7295	mlog(0,"warning: option 'nolocalDomains' is set, ASSP will not perform relay checks!") if $nolocalDomains;
7296	$asspWarnings .= '<span class="negative">\'nolocalDomains\' is set, ASSP will not perform relay checks!</span><br />' if $nolocalDomains;
7297
7298
7299
7300	$asspWarnings .= '<span class="negative">warning: Email::Send not installed, email-interface and block-report not available</span><br />' if !$CanUseEMS;
7301
7302    if ( $CanUseTieRDBM && $redlistdb =~ /mysql/ ) {
7303        eval {
7304            $RedlistObject = tie %Redlist, 'Tie::RDBM',
7305              "dbi:mysql:database=$mydb;host=$myhost",
7306              {
7307                user     => "$myuser",
7308                password => "$mypassword",
7309                table    => 'redlist',
7310                create   => 1
7311              };
7312        };
7313        if ($@) {
7314            mlog( 0, "redlist mysql error: $@" );
7315            $CanUseTieRDBM = 0;
7316            $redlistdb     = "redlist";
7317        }
7318    } else {
7319        $RedlistObject = tie %Redlist, 'orderedtie', "$base/$redlistdb";
7320    }
7321    $GriplistObject = tie %Griplist, 'orderedtie', "$base/$griplist"
7322      if $griplist;
7323    $SMTPfailedObject = tie %SMTPfailed,  'orderedtie', "$base/$pbdb.smtptimeout.db";
7324	$SSLfailedObject  = tie %SSLfailed,  'orderedtie', "$base/$pbdb.ssl.db";
7325    $PBWhiteObject    = tie %PBWhite,    'orderedtie', "$base/$pbdb.white.db";
7326    $PBBlackObject    = tie %PBBlack,    'orderedtie', "$base/$pbdb.black.db";
7327    $PreHeaderObject   = tie %PreHeader,    'orderedtie', "$base/$pbdb.preheader.db";
7328
7329    $SameSubjectCacheObject    = tie %SameSubjectCache,    'orderedtie', "$base/$pbdb.samesubject.db";
7330        $OrgnamesCacheObject    = tie %OrgnamesCache,    'orderedtie', "$base/$pbdb.orgnames.db";
7331    $RBLCacheObject   = tie %RBLCache,   'orderedtie', "$base/$pbdb.rbl.db";
7332    $URIBLCacheObject = tie %URIBLCache, 'orderedtie', "$base/$pbdb.uribl.db";
7333    $PTRCacheObject   = tie %PTRCache,   'orderedtie', "$base/$pbdb.ptr.db";
7334    $MXACacheObject   = tie %MXACache,   'orderedtie', "$base/$pbdb.mxa.db";
7335    $RWLCacheObject   = tie %RWLCache,   'orderedtie', "$base/$pbdb.rwl.db";
7336    $SPFCacheObject   = tie %SPFCache,   'orderedtie', "$base/$pbdb.spf.db";
7337    $SBCacheObject    = tie %SBCache,    'orderedtie', "$base/$pbdb.sb.db";
7338
7339    $NotSpamTagsObject = tie %NotSpamTags,   'orderedtie', "$base/notspamtagsdb";
7340    $PBTrapObject     = tie %PBTrap,     'orderedtie', "$base/$pbdb.trap.db";
7341    $BackDNSObject    = tie %BackDNS,    'orderedtie', "$base/$pbdb.back.db";
7342	$PersBlackObject  = tie %PersBlack,  'orderedtie', "$base/$persblackdb";
7343
7344    $LDAPlistObject   = tie %LDAPlist,   'orderedtie', "$base/$ldaplistdb";
7345
7346    $LDAPNotFoundObject     = tie %LDAPNotFound,   'orderedtie', "$base/$ldapnotfounddb";
7347    $FreqObject       = tie %localFrequencyCache,   'orderedtie', "$base/$pbdb.localfreq.db";
7348
7349
7350    if ( $CanUseTieRDBM && $delaydb =~ /mysql/ ) {
7351        eval {
7352            $DelayWhiteObject = tie %DelayWhite, 'Tie::RDBM',
7353              "dbi:mysql:database=$mydb;host=$myhost",
7354              {
7355                user     => "$myuser",
7356                password => "$mypassword",
7357                table    => 'delaywhitedb',
7358                create   => 1
7359              };
7360            $DelayObject = tie %Delay, 'Tie::RDBM',
7361              "dbi:mysql:database=$mydb;host=$myhost",
7362              {
7363                user     => "$myuser",
7364                password => "$mypassword",
7365                table    => 'delaydb',
7366                create   => 1
7367              };
7368        };
7369        if ($@) {
7370            mlog( 0, "delaydb mysql error: $@" );
7371            $CanUseTieRDBM = 0;
7372            $delaydb       = "delaydb";
7373        }
7374
7375    } else {
7376        $DelayObject = tie %Delay, 'orderedtie', "$base/$delaydb";
7377        $DelayWhiteObject = tie %DelayWhite, 'orderedtie',
7378          "$base/$delaydb.white";
7379    }
7380    my $v;
7381
7382# $v =  "KeyName   ,dbConfigVar,CacheObject     ,realFileName  ,mysqlFileName,FailoverValue,mysqlTable"); remove spaces and push to Group
7383#                                                                             for dbConfigVar
7384    $v = (
7385"Whitelist,whitelistdb,WhitelistObject,$whitelistdb"
7386    );
7387    $v =~ s/\s*,/,/g;
7388    push( @dbGroup, $v );
7389
7390    $v = (
7391"Redlist,redlistdb,RedlistObject,$redlistdb"
7392    );
7393    $v =~ s/\s*,/,/g;
7394    push( @dbGroup, $v );
7395
7396    $v = (
7397"Delay,delaydb,DelayObject,$delaydb"
7398    );
7399    $v =~ s/\s*,/,/g;
7400    push( @dbGroup, $v );
7401    $v = (
7402"DelayWhite,delaydb ,DelayWhiteObject,$delaydb.white"
7403    );
7404    $v =~ s/\s*,/,/g;
7405    push( @dbGroup, $v );
7406
7407    $v = (
7408"Spamdb,spamdb,SpamdbObject,$spamdb"
7409    );
7410    $v =~ s/\s*,/,/g;
7411    push( @dbGroup, $v );
7412
7413    $v = (
7414"Starterdb,BayesianStarterDB,StarterdbObject,$BayesianStarterDB"
7415    );
7416    $v =~ s/\s*,/,/g;
7417    push( @dbGroup, $v );
7418    $v = (
7419"HeloBlack,spamdb,HeloBlackObject,$spamdb.helo"
7420    );
7421    $v =~ s/\s*,/,/g;
7422    push( @dbGroup, $v );
7423
7424    $v = (
7425"PBWhite,pbdb,PBWhiteObject,$pbdb.white.db"
7426    );
7427    $v =~ s/\s*,/,/g;
7428    push( @dbGroup, $v );
7429    $v = (
7430"PBBlack,pbdb,PBBlackObject,$pbdb.black.db"
7431    );
7432    $v =~ s/\s*,/,/g;
7433    push( @dbGroup, $v );
7434    $v = (
7435"PreHeader,pbdb,PreHeaderObject,$pbdb.preheader.db"
7436    );
7437    $v =~ s/\s*,/,/g;
7438    push( @dbGroup, $v );
7439
7440    $v = (
7441"SameSubjectCache,pbdb,SameSubjectCacheObject,$pbdb.samesubject.db"
7442    );
7443    $v =~ s/\s*,/,/g;
7444    push( @dbGroup, $v );
7445    $v = (
7446"OrgnamesCache,pbdb,OrgnamesCacheObject,$pbdb.orgnames.db"
7447    );
7448    $v =~ s/\s*,/,/g;
7449    push( @dbGroup, $v );
7450    $v = (
7451"RBLCache,pbdb,RBLCacheObject,$pbdb.rbl.db"
7452    );
7453    $v =~ s/\s*,/,/g;
7454    push( @dbGroup, $v );
7455    $v = (
7456"URIBLCache,pbdb,URIBLCacheObject,$pbdb.uribl.db"
7457    );
7458    $v =~ s/\s*,/,/g;
7459    push( @dbGroup, $v );
7460    $v = (
7461"PTRCache,pbdb,PTRCacheObject,$pbdb.ptr.db"
7462    );
7463    $v =~ s/\s*,/,/g;
7464    push( @dbGroup, $v );
7465    $v = (
7466"MXACache,pbdb,MXACacheObject,$pbdb.mxa.db"
7467    );
7468    $v =~ s/\s*,/,/g;
7469    push( @dbGroup, $v );
7470    $v = (
7471"RWLCache,pbdb,RWLCacheObject,$pbdb.rwl.db"
7472    );
7473    $v =~ s/\s*,/,/g;
7474    push( @dbGroup, $v );
7475    $v = (
7476"SPFCache,pbdb,SPFCacheObject,$pbdb.spf.db"
7477    );
7478    $v =~ s/\s*,/,/g;
7479    push( @dbGroup, $v );
7480
7481    $v = (
7482"SBCache,pbdb,SBCacheObject,$pbdb.sb.db"
7483    );
7484    $v =~ s/\s*,/,/g;
7485    push( @dbGroup, $v );
7486    $v = (
7487"NotSpamTags,pbdb,NotSpamTagsObject,generatedtagsdb"
7488    );
7489    $v =~ s/\s*,/,/g;
7490    push( @dbGroup, $v );
7491    $v = (
7492"PBTrap,pbdb,PBTrapObject,$pbdb.trap.db"
7493    );
7494    $v =~ s/\s*,/,/g;
7495    push( @dbGroup, $v );
7496     $v = (
7497"BackDNS,pbdb,BackDNSObject,$pbdb.back.db"
7498    );
7499    $v =~ s/\s*,/,/g;
7500    push( @dbGroup, $v );
7501    $v = (
7502"localFrequencyCache,pbdb,FreqObject,$pbdb.localfreq.db"
7503    );
7504    $v =~ s/\s*,/,/g;
7505    push( @dbGroup, $v );
7506	$v = (
7507"SMTPfailed,pbdb,SMTPfailedObject,$pbdb.smtptimeout.db"
7508    );
7509    $v =~ s/\s*,/,/g;
7510    push( @dbGroup, $v );
7511   	$v = (
7512"SSLfailed,pbdb,SSLfailedObject,$pbdb.ssl.db"
7513    );
7514    $v =~ s/\s*,/,/g;
7515    push( @dbGroup, $v );
7516    $v = (
7517"PersBlack,persblackdb,PersBlackObject,$persblackdb");
7518    $v=~s/\s*,/,/go;
7519    push(@dbGroup,$v);
7520
7521    $v = (
7522"LDAPlist,ldaplistdb,LDAPlistObject,$ldaplistdb"
7523    );
7524    $v=~s/\s*,/,/go;
7525    push( @dbGroup, $v );
7526    $v = (
7527"LDAPNotFound,ldapnotfounddb,LDAPNotFoundObject,$ldapnotfounddb"
7528    );
7529    $v =~ s/\s*,/,/g;
7530    push( @dbGroup, $v );
7531    $shuttingDown = $doShutdown = 0;
7532
7533    $NotSpamTagGenerated = &NotSpamTagGenerate;
7534
7535    $smtpConcurrentSessions = 0;
7536    $Stats{starttime}       = time;
7537    $Stats{version}         = "$version$modversion";
7538    &ResetStats;
7539    my $runas = $AsAService ? 'as service' : $AsADaemon ? 'as daemon' : 'in console';
7540	my ($mv,$sv,$lv) = $] =~ /(\d)\.(\d{3})(\d{3})/o;
7541	$mv =~ s/^0+//o;$sv =~ s/^0+//o;$lv =~ s/^0+//o;
7542    mlog( 0, "Running in directory $base on host ". hostname());
7543    #-- check if running as root
7544	mlog( 0, "Running as root") if $< == 0 && $^O ne "MSWin32";
7545
7546	mlog( 0, "Running as user '" . (getpwuid($<))[0] . "'") if $< != 0 && $^O ne "MSWin32";
7547    mlog( 0, "using Perl $^X version $] ($mv.$sv.$lv)");
7548    mlog( 0, "ASSP $version$modversion starting (PID: $$) $runas" );
7549
7550	&writeWatchdog if $EnableWatchdog;
7551	&startWatchdog if $EnableWatchdog;
7552
7553
7554}
7555sub getWebSocket {
7556	my $adminport = $webAdminPort;
7557    $adminport = $webSecondaryPort if $AsASecondary && $webSecondaryPort;
7558	my @dummy;
7559	my ($WebSocket,$dummy);
7560    if ($CanUseIOSocketSSL && $enableWebAdminSSL) {
7561      ($WebSocket,$dummy)   = newListenSSL($adminport,\&NewWebConnection,1);
7562      @WebSocket = @$WebSocket;
7563      for (@$dummy) {s/:::/\[::\]:/o;}
7564      mlog(0,"listening for admin HTTPS connections on webAdminPort @$dummy") if @$dummy;
7565      $webAdminPortOK = 1 if @$dummy;
7566#      mlog(0,"not listening for admin HTTPS connections on webAdminPort $adminport") if !@$dummy;
7567      $webAdminPortOK = 0 if !@$dummy;
7568
7569  	} else {
7570      ($WebSocket,$dummy)   = newListen($adminport,\&NewWebConnection,1);
7571      for (@$dummy) {s/:::/\[::\]:/o;}
7572	  mlog(0,"listening for admin HTTP connections on webAdminPort @$dummy") if @$dummy;
7573	  $webAdminPortOK = 1 if @$dummy;
7574#	  mlog(0,"not listening for admin HTTP connections on webAdminPort $adminport") if !@$dummy;
7575	  $webAdminPortOK = 0 if !@$dummy;
7576  	}
7577}
7578
7579sub getDestSockDom {
7580    my $dest = shift;
7581    return unless $dest;
7582    my $orgdest = $dest;
7583    my ($ip4,$ip6,$ip,%Domain);
7584    $ip = $1 if $dest =~ /^\[?($IPRe)\]?/o;
7585    if (! $ip) {
7586        my ($port,@res);
7587        $dest =~ s/^\[//o;
7588        $dest =~ s/\]?:\d+$//o;
7589        if ($CanUseIOSocketINET6) {
7590            eval(<<EOT);
7591                @res = Socket6::getaddrinfo($dest,25,AF_INET6);
7592	            ($ip6, $port) = getnameinfo($res[3], NI_NUMERICHOST | NI_NUMERICSERV) if $res[3];
7593EOT
7594            eval(<<EOT)  if $@ || !($ip6 =~ s/^\[?($IPv6Re)\]?$/$1/o);
7595                $ip6 = Socket6::inet_ntop( AF_INET6, scalar( Socket6::gethostbyname2($dest,AF_INET6) ) );
7596EOT
7597            $ip6 = undef if $@ || !($ip6 =~ s/^\[?($IPv6Re)\]?$/$1/o);
7598            mlog(0,"info: resolved IPv6 $ip6 for hostname $dest") if $ip6 && $ConnectionLog >= 2;
7599        }
7600        if (! $ip6) {
7601            eval{$ip4 = inet_ntoa( scalar( gethostbyname($dest) ) );};
7602            $ip4 = undef if ($ip4 !~ /^$IPv4Re$/o);
7603            mlog(0,"info: resolved IPv4 $ip4 for hostname $dest") if $ip4 && $ConnectionLog >= 2;
7604        }
7605    } else {
7606        $ip6 = $1 if $ip =~/^\[?($IPv6Re)\]?$/o;
7607        $ip4 = $1 if ! $ip6 && $ip =~/^($IPv4Re)$/o;
7608    }
7609    if ($ip6) {
7610        $Domain{Domain} = AF_INET6;
7611    } elsif ($ip4) {
7612        $Domain{Domain} = AF_INET;
7613    } else {
7614        $Domain{Domain} = AF_UNSPEC;
7615        mlog(0,"error: found unresolvable ($dest) - hostname or suspicious IP address definition in $orgdest");
7616    }
7617    return %Domain;
7618}
7619
7620sub getHostIP {
7621
7622    my($host,$name)=@_;
7623#	return "127.0.0.1" if $host =~ /localhost/i;
7624    my ($ip4,$ip6);
7625
7626
7627        my ($port,@res);
7628        $host =~ s/^\[//o;
7629        $host =~ s/\]$//o;
7630        if ($CanUseIOSocketINET6) {
7631            eval(<<EOT);
7632                @res = Socket6::getaddrinfo($host,25,AF_INET6);
7633	            ($ip6, $port) = getnameinfo($res[3], NI_NUMERICHOST | NI_NUMERICSERV) if $res[3];
7634EOT
7635            eval(<<EOT)  if $@ || !($ip6 =~ s/^\[?($IPv6Re)\]?$/$1/o);
7636                $ip6 = Socket6::inet_ntop( AF_INET6, scalar( Socket6::gethostbyname2($host,AF_INET6) ) );
7637EOT
7638            $ip6 = undef if $@ || !($ip6 =~ s/^\[?($IPv6Re)\]?$/$1/o);
7639            mlog(0,"info: resolved IPv6 $ip6 for hostname '$host' in '$name'");
7640            return $ip6;
7641        }
7642        if (! $ip6) {
7643            eval{$ip4 = inet_ntoa( scalar( gethostbyname($host) ) );};
7644
7645            $ip4 = undef if ($ip4 !~ /^$IPv4Re$/o);
7646            mlog(0,"info: resolved IPv4 $ip4 for hostname '$host' in '$name'") if $ip4;
7647            return $ip4 if $ip4;
7648            mlog(0,"error: found unresolvable ($host) - hostname in '$name'") if !$ip4;
7649
7650        }
7651	return $host;
7652}
7653
7654sub newListen {
7655    my($port,$handler)=@_;
7656    my $portA;
7657    my @s;
7658    my @sinfo;
7659
7660    foreach my $portA (split(/\|/o, $port)) {
7661        if($portA !~ /$HostRe?:?$PortRe/o) {
7662            mlog(0,"wrong (host) + port definition in '$portA' -- entry will be ignored !");
7663            next;
7664        }
7665        my @stt;
7666        my ($interface,$p)=$portA=~/($HostRe):($PortRe)/o;
7667
7668        my %parms = $interface
7669                    ? ('LocalPort' => $p, 'LocalAddr' => $interface)
7670                    : ('LocalPort' => $portA);
7671        $parms{Listen} = 10;
7672        $parms{Reuse} = 1;
7673
7674        if ($CanUseIOSocketINET6) {
7675            my $isv4 = [&getDestSockDom($interface)]->[1] != AF_INET6;
7676            my ($s4,$s6);
7677            if (! $interface || $isv4) {
7678                $parms{Domain} = AF_INET;
7679                $s4 = IO::Socket::INET6->new(%parms);
7680                push @stt,$s4 if $s4;
7681            }
7682            if (! $interface || ! $isv4) {
7683                $parms{Domain} = AF_INET6;
7684                $s6 = IO::Socket::INET6->new(%parms);
7685                push @stt,$s6 if $s6;
7686            }
7687        } else {
7688            my $s4 = IO::Socket::INET->new(%parms);
7689            push @stt,$s4 if $s4;
7690        }
7691
7692        if(! @stt) {
7693            mlog(0,"couldn't create server socket on port '$portA' -- maybe another service is running or I'm not root (uid=$>)? -- or a wrong IP address is specified? -- $! - $IO::Socket::SSL::SSL_ERROR") if !$AsASecondary;
7694             my $time = &timestring();
7695
7696            next;
7697        }
7698
7699        foreach my $s (@stt) {
7700            $s->blocking(0);
7701            $SocketCalls{$s}=$handler;
7702
7703            $readable->add($s);    # add to select list
7704            push @s,$s;
7705            push @sinfo,$s->sockhost . ':' . $s->sockport;
7706        }
7707        last if $AsASecondary && $portA  =~ $webSecondaryPort;
7708    }
7709    return \@s,\@sinfo;
7710}
7711
7712sub newListenSSL {
7713    my($port,$handler)=@_;
7714    my @s;
7715    my @sinfo;
7716
7717    foreach my $portA (split(/\|/o, $port)) {
7718        if($portA !~ /$HostRe?:?$PortRe/o) {
7719            mlog(0,"wrong (host) + port definition in '$portA' -- entry will be ignored !");
7720            next;
7721        }
7722        my @stt;
7723        my ($interface,$p)=$portA=~/($HostRe):($PortRe)/o;
7724
7725        my %parms = getSSLParms(1);
7726        if ($interface) {
7727            $parms{LocalPort} = $p;
7728            $parms{LocalAddr} = $interface;
7729        } else {
7730            $parms{LocalPort} = $portA;
7731        }
7732        $parms{Listen} = 10;
7733        $parms{Reuse} = 1;
7734        $parms{SSL_startHandshake} = 1;
7735
7736        if ($SSLDEBUG > 1) {
7737            while(my($k,$v)=each(%parms)) {
7738                print "ssl-new-listener: $k = $v\n";
7739            }
7740        }
7741
7742        if ($CanUseIOSocketINET6) {
7743            my $isv4 = [&getDestSockDom($interface)]->[1] != AF_INET6;
7744            my ($s4,$s6);
7745            if (! $interface || $isv4) {
7746                $parms{Domain} = AF_INET;
7747                $s4 = IO::Socket::SSL->new(%parms);
7748                push @stt,$s4 if $s4;
7749            }
7750            if (! $interface || ! $isv4) {
7751                $parms{Domain} = AF_INET6;
7752                $s6 = IO::Socket::SSL->new(%parms);
7753                push @stt,$s6 if $s6;
7754            }
7755        } else {
7756            $parms{Domain} = AF_INET;
7757            my $s4 = IO::Socket::SSL->new(%parms);
7758            push @stt,$s4 if $s4;
7759        }
7760
7761        if(! @stt) {
7762            mlog(0,"couldn't create server SSL-socket on port '$portA' -- maybe another service is running or I'm not root (uid=$>)? - or a wrong IP address is specified? -- $! - $IO::Socket::SSL::SSL_ERROR");
7763            next;
7764        }
7765        foreach my $s (@stt) {
7766            $SocketCalls{$s}=$handler;
7767
7768            $readable->add($s);    # add to select list
7769            push @s,$s;
7770            push @sinfo,$s->sockhost . ':' . $s->sockport;
7771        }
7772        last if $AsASecondary && $portA  =~ $webSecondaryPort;
7773    }
7774	$IO::Socket::SSL::DEBUG = $SSLDEBUG;
7775    return \@s,\@sinfo;
7776}
7777
7778
7779
7780sub nixUsers {
7781  my ($uid,$gid); ($uid,$gid) = getUidGid($runAsUser,$runAsGroup) if ($runAsUser || $runAsGroup);
7782  if($ChangeRoot) {
7783    my $chroot;
7784    eval('$chroot=chroot($ChangeRoot)');
7785    if($@) {
7786      my $msg="request to change root to '$ChangeRoot' failed: $@";
7787      mlog(0,$msg);
7788      &downASSP($msg);
7789      exit(1);
7790    } elsif(! $chroot) {
7791      my $msg="request to change root to '$ChangeRoot' did not succeed: $!";
7792      mlog(0,$msg);
7793      &downASSP($msg);
7794      exit(1);
7795    } else {
7796      $chroot=$ChangeRoot; $chroot=~s/(\W)/\\$1/go;
7797      $base=~s/^$chroot//io;
7798      chdir("/");
7799      mlog(0,"successfully changed root to '$ChangeRoot' -- new base is '$base'");
7800    }
7801  }
7802
7803  switchUsers($uid,$gid) if ($runAsUser || $runAsGroup);
7804}
7805sub getUidGid {
7806    my ( $uname, $gname ) = @_;
7807    return if $AsAService;
7808    my $rname = "root";
7809    eval('getgrnam($rname);getpwnam($rname);');
7810    if ($@) {
7811
7812        # windows pukes "unimplemented" for these -- just skip it
7813        mlog( '',
7814"warning:   uname and/or gname are set ($uname,$gname) but getgrnam / getpwnam give errors: $@"
7815        );
7816        return;
7817    }
7818    my $gid;
7819    if ($gname) {
7820        $gid = getgrnam($gname);
7821        if ( defined $gid ) {
7822        } else {
7823            my $msg =
7824"could not find gid for group '$gname' -- not switching effective gid ";
7825            mlog( '', $msg );
7826            return;
7827        }
7828    }
7829    my $uid;
7830    if ($uname) {
7831        $uid = getpwnam($uname);
7832        if ( defined $uid ) {
7833        } else {
7834            my $msg =
7835"could not find uid for user '$uname' -- not switching effective uid ";
7836            mlog( '', $msg );
7837            return;
7838        }
7839    }
7840    ( $uid, $gid );
7841}
7842
7843sub switchUsers {
7844    my ( $uid, $gid ) = @_;
7845    return if $AsAService;
7846    my ( $uname, $gname ) = ( $runAsUser, $runAsGroup );
7847    $> = 0;
7848    if ( $> != 0 ) {
7849        my $msg =
7850"requested to switch to user/group '$uname/$gname' but cannot set effective uid to 0 --  uid is $>";
7851        mlog( '', $msg );
7852        return;
7853    }
7854    $< = 0;
7855    if ($gid) {
7856        $) = $gid;
7857        if ( $) + 0 == $gid ) {
7858            mlog( '', "switched effective gid to $gid ($gname)" );
7859        } else {
7860            my $msg =
7861"failed to switch effective gid to $gid ($gname) -- effective gid=$) ";
7862            mlog( '', $msg );
7863            return;
7864        }
7865        $( = $gid;
7866        if ( $( + 0 == $gid ) {
7867            mlog( '', "switched real gid to $gid ($gname)" );
7868        } else {
7869            mlog( '',
7870                "failed to switch real gid to $gid ($gname) -- real uid=$(" );
7871        }
7872    }
7873    if ($uid) {
7874
7875        # do it both ways so linux and bsd are happy
7876        $< = $> = $uid;
7877        if ( $> == $uid ) {
7878            mlog( '', "switched effective uid to $uid ($uname)" );
7879        } else {
7880            my $msg =
7881"failed to switch effective uid to $uid ($uname) -- real uid=$<";
7882            mlog( '', $msg );
7883            return;
7884        }
7885        if ( $< == $uid ) {
7886            mlog( '', "switched real uid to $uid ($uname)" );
7887        } else {
7888            mlog( '',
7889                "failed to switch real uid to $uid ($uname) -- real uid=$<" );
7890        }
7891    }
7892}
7893
7894sub switchMode {
7895	my ($fh, $mode, $sl, $testmode ) = @_;
7896	my $this=$Con{$fh};
7897    my $newmode = $mode;
7898    my $slok = $sl == 1;
7899
7900	return $newmode;
7901}
7902
7903sub MainLoop {
7904	my $timeout;
7905	my $entrytime = Time::HiRes::time();
7906
7907	eval {
7908    local $SIG{ALRM} =
7909    sub { die "mainloop_timeout\n" };    # NB: \n required
7910    $timeout = 180;
7911    $timeout = $MainloopTimeout if $MainloopTimeout > 180;
7912    alarm $timeout;
7913    my $wait = 5; # keep it short enough for servicecheck to be called regularly
7914    my $stime =
7915      $CanStatCPU ? ( Time::HiRes::time() ) : time;    # loop cycle start time
7916    my ( $canread, $canwrite ) =
7917      IO::Select->select( $readable, $writable, undef, $wait );
7918    my $itime =
7919      $CanStatCPU ? ( Time::HiRes::time() ) : time;   # loop cycle idle end time
7920    my $ntime = $CanStatCPU ? 0.3 : 1;
7921    $webTime   = 0;             # loop cycle web time interval, global var
7922    $nextLoop2 = $itime + 1;
7923
7924    # AZ: 2009-02-05 - signal service status
7925    if ( $SvcStopping != 0 ) {
7926      serviceCheck();
7927      return;
7928    }
7929	foreach my $fh (@$canwrite) {
7930        my $l = length( $Con{$fh}->{outgoing} );
7931        d("canwrite $fh $Con{$fh} l=$l paused=$Con{$fh}->{paused} ip=$Con{$fh}->{ip}");
7932
7933        if ($l) {
7934        	$fh->blocking(0) if $fh->blocking;
7935        	my $written;
7936            eval {
7937                local $SIG{ALRM} =
7938    			sub { die "syswrite_timeout\n" };    #
7939
7940    			alarm 60;
7941            	$written =
7942              		syswrite( $fh, $Con{$fh}->{outgoing}, $OutgoingBufSizeNew );
7943              	alarm 0;
7944              };
7945                #exception check
7946        	if ($@) {
7947				alarm 0;
7948
7949            	mlog( 0, "mainloop exception: $@", 1, 1 );
7950
7951
7952        	}
7953            $Con{$fh}->{outgoing} = substr( $Con{$fh}->{outgoing}, $written );
7954            $l = length( $Con{$fh}->{outgoing} );
7955
7956            # test for highwater mark
7957            if (   $written > 0
7958                && $l < $OutgoingBufSizeNew
7959                && $Con{$fh}->{paused} )
7960            {
7961                $Con{$fh}->{paused} = 0;
7962                $readable->add( $Con{$fh}->{friend} );
7963            }
7964            if ($Con{$fh}->{type} ne 'C' &&
7965                		$written > 0 &&
7966                		$Con{$fh}->{friend} &&
7967                		exists $Con{$Con{$fh}->{friend}} &&
7968                		$Con{$Con{$fh}->{friend}}->{lastcmd} =~ /^ *(?:DATA|BDAT)/io )
7969            {
7970                $Con{$Con{$fh}->{friend}}->{writtenDataToFriend} += 		$written;
7971            }
7972        }
7973        if ( length( $Con{$fh}->{outgoing} ) == 0 ) {
7974            $writable->remove($fh);
7975        }
7976
7977        done2($fh) if $Con{$fh}->{closeafterwrite};
7978    }
7979    foreach my $fh (@$canread) {
7980		d("canread  $fh $Con{$fh}");
7981        if ( $fh && $SocketCalls{$fh} ) {
7982            if (
7983                $CanStatCPU
7984                && (   $SocketCalls{$fh} == \&WebTraffic
7985                    || $SocketCalls{$fh} == \&NewWebConnection
7986                    || $SocketCalls{$fh} == \&NewStatConnection )
7987              )
7988            {
7989
7990                # calculate time spent serving web request
7991                $webTime -= Time::HiRes::time();
7992                $SocketCalls{$fh}->($fh);
7993                $webTime += Time::HiRes::time();
7994            } else {
7995                $SocketCalls{$fh}->($fh);
7996            }
7997        } else {
7998            my $ip;
7999            my $port;
8000            eval {
8001                $ip   = $fh->peerhost();
8002                $port = $fh->peerport();
8003            } if ( fileno($fh) );
8004
8005            eval {
8006                delete $SocketCalls{$fh} if ( exists $SocketCalls{$fh} );
8007                delete $WebCon{$fh}      if ( exists $WebCon{$fh} );
8008                $readable->remove($fh);
8009                $writable->remove($fh);
8010                eval { $fh->close } if ( fileno($fh) );
8011            };
8012            delete $Con{$fh} if exists $Con{$fh};
8013        }
8014    }
8015
8016    if ($smtpIdleTimeout > 0 || $smtpNOOPIdleTimeout > 0){
8017        if (scalar keys %Con > 0){
8018            my $tmpNow = time;
8019            # Check timeouts only every 15 seconds at least
8020            if ($tmpNow > ($lastTimeoutCheck + 15)){
8021                while (my ($tmpfh,$v) = each %Con){
8022
8023                    delete $Con{$tmpfh}->{doNotTimeout} if ($tmpNow - $Con{$tmpfh}->{doNotTimeout} > $NpWlTimeOut);
8024                    if ($Con{$tmpfh}->{type} =~ /CC?/o &&
8025                        $Con{$tmpfh}->{timelast} > 0 &&
8026                        ! $Con{$tmpfh}->{movedtossl} &&
8027                        ! $Con{$tmpfh}->{doNotTimeout} &&
8028                        ! ($Con{$tmpfh}->{noprocessing} && $tmpNow - $Con{$tmpfh}->{timelast} < $NpWlTimeOut) &&   # 20 minutes for really large queued mails
8029                        (($smtpIdleTimeout && $tmpNow - $Con{$tmpfh}->{timelast} > $smtpIdleTimeout) ||
8030                          (uc($Con{$tmpfh}->{lastcmd}) =~ /NOOP/o &&
8031                          $smtpNOOPIdleTimeout &&
8032                          $tmpNow - $Con{$tmpfh}->{timelast} > $smtpNOOPIdleTimeout) ||
8033                          ($smtpNOOPIdleTimeout &&
8034                          $smtpNOOPIdleTimeoutCount &&
8035                          $Con{$tmpfh}->{NOOPcount} >= $smtpNOOPIdleTimeoutCount))
8036                        )
8037                    {
8038                        if ($ConTimeOutDebug) {
8039                           my $m = &timestring();
8040                           $Con{$tmpfh}->{contimeoutdebug} .= "$m client Timeout after $smtpIdleTimeout secs\r\n" if $ConTimeOutDebug;
8041                           my $check = "$m client was not readable\r\n";
8042                           my @handles = $readable->handles();
8043                           while (@handles) {
8044                              $_ = shift @handles;
8045                              $check = "$m client was readable\r\n" if ($tmpfh eq $_);
8046                           }
8047                           $Con{$tmpfh}->{contimeoutdebug} .= $check;
8048                           $check = "$m client was not writable\r\n";
8049                           @handles = $writable->handles();
8050                           while (@handles) {
8051                              $_ = shift @handles;
8052                              $check = "$m client was writable\r\n" if ($tmpfh eq $_);
8053                           }
8054                           $Con{$tmpfh}->{contimeoutdebug} .= $check;
8055                           $m=time;
8056                           my $f = "$base/debug/$m.txt";
8057                           my $CTOD;
8058                           open $CTOD,'>',"$f" or mlog(0,"error: unable to open connection timeout debug log [$f] : $!");
8059                           binmode $CTOD;
8060                           print $CTOD  $Con{$tmpfh}->{contimeoutdebug};
8061                           close $CTOD;
8062                        }
8063                        $Con{$tmpfh}->{prepend}='';
8064                        $Con{$tmpfh}->{timestart} = 0;
8065                        my $type;
8066                        my $addPB = 0;
8067                        if ($Con{$tmpfh}->{oldfh} && $Con{$tmpfh}->{ip}) {
8068                            setSSLfailed($Con{$tmpfh}->{ip});
8069                            $type = 'TLS-';
8070                            $Stats{smtpConnTLSIdleTimeout}++;
8071                        } elsif ("$tmpfh" =~/SSL/io && $Con{$tmpfh}->{ip}) {
8072                            $type = 'SSL-';
8073                            $Stats{smtpConnSSLIdleTimeout}++;
8074                        } else {
8075                            $addPB = 1;
8076                            $Stats{smtpConnIdleTimeout}++;
8077                        }
8078                        if ($Con{$tmpfh}->{damping}) {
8079                            $Con{$tmpfh}->{messagescore} = 0;
8080                            delete $ConDelete{$tmpfh};
8081                            $addPB = 0;
8082                        }
8083                        if ( ! $Con{$tmpfh}->{timedout} ) {
8084                            pbAdd( $tmpfh,$Con{$tmpfh}->{ip}, 'idleValencePB', "TimeOut",2 ) if $addPB;
8085                            mlog($tmpfh,$type."Connection idle for $smtpIdleTimeout secs - timeout",1) if $SessionLog;
8086                        } else {
8087                            done($Con{$tmpfh}->{client});
8088                            next;
8089                        }
8090                        $Con{$tmpfh}->{timedout} = 1;
8091                        if ($Con{$tmpfh}->{getline} != \&error) {
8092                            seterror($Con{$tmpfh}->{client},"451 Connection timeout, try later\r\n",1);
8093                        } else {
8094                            if (! $Con{$tmpfh}->{closeafterwrite}) {
8095                                sendque($Con{$tmpfh}->{client},"451 Connection timeout, try later\r\n");
8096                                $Con{$tmpfh}->{closeafterwrite} = 1;
8097                                unpoll($Con{$tmpfh}->{client}, $readable);
8098                            } else {
8099                                done($Con{$tmpfh}->{client});
8100                            }
8101                        }
8102                    }
8103                }
8104                $lastTimeoutCheck = $tmpNow;
8105            }
8106        }
8107    }
8108
8109
8110    d('mainloop before servicecheck');
8111    serviceCheck();    # for win32 services
8112
8113	if ($syncToDo && !$AsASecondary ) {
8114      my $hassync;
8115      foreach (sort{&syncSortCFGRec()} glob("$base/configSync/*.cfg")) {
8116          next if -d $_;
8117          &syncConfigReceived($_);
8118          unlink "$_" if -e "$_";
8119          &syncWriteConfig();
8120          $hassync = 1;
8121          last;
8122      }
8123      $syncToDo = $hassync;
8124  	}
8125    # timer related issues
8126    $opencon = ( keys %Con );
8127
8128     if ( $UpdateWhitelist && $itime >= $saveWhite  && !$AsASecondary ) {
8129
8130        &SaveWhitelist;
8131
8132        $saveWhite = int($itime) + $UpdateWhitelist;
8133      }
8134    if ( $CleanDelayDBInterval && $itime >= $nextCleanDelayDB  && !$AsASecondary ) {
8135
8136        &DoCleanDelayDB;
8137
8138
8139        $nextCleanDelayDB = int($itime) + $CleanDelayDBInterval;
8140      }
8141
8142
8143    if(time >= $nextCleanIPDom ) {
8144        $nextCleanIPDom = time + 600;
8145        d(' - CleanIP');
8146
8147		&cleanCacheDelayIPPB();
8148        &cleanCacheLocalFrequency();
8149        &cleanCacheAUTHErrors();
8150
8151
8152
8153    }
8154
8155
8156
8157
8158#      d('mainloop before restart check');
8159
8160  	if($AutoRestartInterval && $itime >= $endtime) {
8161# time to quit -- after endtime and we're bored.
8162        mlog(0,"info: restart time is reached - waiting until all connection are gone but max 5 minutes");
8163        while ($smtpConcurrentSessions && time < $endtime + 300) {
8164            my $tendtime = $endtime;
8165            $endtime = time + 10000;
8166            &MainLoop2();
8167            $endtime = $tendtime;
8168            Time::HiRes::sleep(0.5);
8169        }
8170        &downASSP("restarting");
8171 		&restartCMD();
8172	}
8173    SaveStats() if ( $SaveStatsEvery && $itime >= $NextSaveStats  && !$AsASecondary );
8174
8175    uploadStats() if ($totalizeSpamStats && $itime >= $Stats{nextUpload}  && !$AsASecondary );
8176    if ( $resendmail && $itime > $nextResendMail ) {
8177        &resend_mail();
8178        $nextResendMail = time + 300;
8179    }
8180
8181
8182	if ($enableCFGShare && $CanUseNetSMTP && $isShareMaster &&  time > $NextSyncConfig && !$AsASecondary ) {
8183        my $i = 0;
8184        my $wr = 0;
8185
8186        my $stat;
8187        foreach my $c (@ConfigArray) {
8188
8189            next if ( ! $c->[0] or @{$c} == 5);
8190
8191            next if $ConfigSync{$c->[0]}->{sync_cfg} != 1;
8192
8193            $stat = &syncGetStatus($c->[0]);
8194
8195            next if($stat < 1 or $stat == 2);
8196
8197            $wr += &syncConfigSend($c->[0]);
8198            $i++ > 10 and last;
8199
8200        }
8201        $NextSyncConfig = time + ($wr ? 30 : 60);
8202
8203    }
8204
8205
8206    if ($CanStatCPU) {
8207
8208        #
8209        # cycleTime = cpuTime + webTime
8210        # cpuTime = cpuIdleTime + cpuBusyTime
8211        #
8212  	my $ctime=Time::HiRes::time(); # loop cycle end time
8213  	my $cycleTime=$ctime-$stime;
8214  	my $cpuTime=$cycleTime-$webTime;
8215  	my $cpuIdleTime=$itime-$stime;
8216  	my $cpuBusyTime=$cpuTime-$cpuIdleTime;
8217  	$Stats{cpuTime}+=$cpuTime;
8218  	$Stats{cpuBusyTime}+=$cpuBusyTime;
8219# envelope following filter with instant rise and decay time of 1 second
8220  	my $lusage=$cpuUsage*exp(-(0.693/1)*$cycleTime);
8221  	my $usage=($cpuTime==0 ? 0 : $cpuBusyTime/$cpuTime);
8222  	$cpuUsage=$usage>$lusage ? $usage : $lusage;
8223    }
8224    if ( $doShutdown && $itime >= $doShutdown && !$AsASecondary) {
8225        &downASSP("restarting");
8226
8227		&restartCMD();
8228    }
8229
8230    # run every day at midnight
8231    my $t = int(
8232        (
8233            time +
8234              Time::Local::timelocal( localtime() ) -
8235              Time::Local::timelocal( gmtime() )
8236        ) / ( 24 * 3600 )
8237    );
8238    if ( $blockRepLastT && $t != $blockRepLastT ) {
8239        cleanUpMailLog() if !$AsASecondary ;
8240    }
8241    $blockRepLastT = $t;
8242
8243    if ( time > $nextdetectGhostCon  && !$AsASecondary ) {
8244    	my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
8245        &detectGhostCon();
8246        $nextdetectGhostCon = time + 1800;
8247    }
8248	  if ($DebugRollTime && $debug && time > $nextDebugClear  && !$AsASecondary ) {
8249    	my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
8250        &DebugClear();
8251        $nextDebugClear = time + $DebugRollTime;
8252    }
8253
8254    if($ReloadOptionFiles  && time - $lastOptionCheck > $ReloadOptionFiles ){
8255         d('ReloadOptionFiles');
8256         OptionCheck();
8257    }
8258
8259
8260    if(time > $nextDNSCheck && $DNSResponseLog  && !$AsASecondary ) {
8261        updateDNS( 'DNSServers', $Config{DNSServers}, $Config{DNSServers}, '' );
8262        $nextDNSCheck = time + 1800;
8263    }
8264    if(time > $nextSCANping && $UseAvClamd && $CanUseAvClamd && !$AvailAvClamd  && !$AsASecondary ) {
8265        pingScan();
8266        $nextSCANping = time + 180;
8267    }
8268    if ( time > $check4queuetime)  {
8269    	&check4queue() ;
8270
8271    	if ($RunTaskNow{BlockReportNow}) {
8272       	 	&BlockReportGen("1");
8273
8274        	$RunTaskNow{BlockReportNow} = '';
8275
8276    	}
8277    	if ($RunTaskNow{RebuildNow}) {
8278       	 	&Rebuild("25");
8279        	$RunTaskNow{RebuildNow} = '';
8280
8281    	}
8282    	if ($RunTaskNow{GriplistDownloadNow}) {
8283       	 	$RunTaskNow{GriplistDownloadNow} = '';
8284       	 	&downloadGrip(1);
8285
8286
8287    	}
8288
8289    	if ($RunTaskNow{CleanWhitelistNow}) {
8290       	 	&CleanWhitelist;
8291        	$RunTaskNow{CleanWhitelistNow} = '';
8292
8293    	}
8294
8295    	if ($RunTaskNow{forceLDAPcrossCheck}) {
8296       	 	&LDAPcrossCheck;
8297        	$RunTaskNow{forceLDAPcrossCheck} = '';
8298
8299    	}
8300    	if ($RunTaskNow{downloadStarterDBNow}) {
8301       	 	&downloadStarterDB;
8302        	$RunTaskNow{downloadStarterDBNow} = '';
8303
8304    	}
8305    	if ($RunTaskNow{AutoUpdateNow}) {
8306  			$NextASSPFileDownload = -1;
8307        	$NextVersionFileDownload = -1;
8308        	$RunTaskNow{AutoUpdateNow} = '';
8309
8310    	}
8311
8312    	$check4queuetime += 300;
8313    }
8314
8315
8316
8317
8318    if ( time > $nextDestinationCheck  && !$AsASecondary ) {
8319
8320    	$nextDestinationCheck = time + 180;
8321    }
8322
8323    if ( time > $nextdetectHourJob  && !$AsASecondary ) {
8324    	my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
8325    	$nextdetectHourJob = int(time / 3600) * 3600 + 3600;
8326        $nextdetectHourJob += 15 unless $nextdetectHourJob % (24 * 3600);
8327        d("run HourJobs - scheduled - ($hour)");
8328        mlog(0,"$version$modversion info: hourly scheduler running after $hour:00");
8329  #      mlog(0,"$version$modversion info: next hourly scheduler will run after " . &timestring($nextdetectHourJob)) if $MaintenanceLog > 2;
8330    	mlog(0,"info: DEBUG is set") if $debug;
8331
8332    	$nextdetectHourJob = time + 3600;
8333    	&Rebuild($hour) if $RebuildSchedule ;
8334    	&HouseKeeping($hour) if $HouseKeepingSchedule  && !$AsASecondary ;
8335    	&cleanCacheIPNumTries();
8336        &cleanCacheSMTPdomainIP();
8337 		&cleanCacheSSLfailed();
8338        &BlockReportGen() if $hour == int($BlockReportSchedule);
8339        &BlockReportGen('USERQUEUE') if $hour == int($QueueSchedule);
8340
8341#        %UniqueID();
8342
8343    }
8344
8345
8346
8347
8348    if(!$AutoUpdateASSP  && !$AsASecondary && time > $NextVersionFileDownload) {
8349        		&downloadVersionFile();
8350
8351    }
8352
8353
8354
8355    if ($AutoUpdateASSP && !$AsASecondary && time >= $NextASSPFileDownload) {
8356
8357        		&downloadASSPVersion();
8358        		$NextCodeChangeCheck=time-1;
8359
8360    }
8361    if ($AutoRestartAfterCodeChange && !$AsASecondary && time >= $NextCodeChangeCheck) {
8362
8363        		&codeChangeCheck();
8364        		$NextCodeChangeCheck = time + 3600;
8365    }
8366
8367	if ($POP3Interval && !$AsASecondary && time >= $NextPOP3Collect) {
8368			&POP3Collect();
8369	        $NextPOP3Collect = $POP3Interval * 60 + time;
8370	}
8371
8372	alarm 0;
8373    };
8374
8375    #exception check
8376        if ($@) {
8377
8378			alarm 0;
8379			if ($@ =~ /mainloop_timeout/) {
8380
8381				if ($AutoRestartAfterTimeOut && $AutoRestartCmd) {
8382            		mlog( 0, "warning: restarting after MainloopTimeout of $MainloopTimeout seconds", 1 );
8383            		downASSP("AutoRestartAfterTimeOut caused restart after MainloopTimeout" );
8384					restartCMD();
8385            	} else {
8386            	    mlog( 0, "warning: continuing after MainloopTimeout of $MainloopTimeout seconds", 1 );
8387            	}
8388			} else {
8389            	mlog( 0, "terminating ASSP: mainloop exception: $@ !!!!!!!", 1 );
8390				downASSP("terminating ASSP: mainloop exception: $@ !!!!!!!" );
8391            	exit 1;
8392
8393			}
8394        }
8395
8396}
8397d('Never reached...(we hope)');
8398
8399
8400
8401# called during long operations to keep processing priority data
8402# alternates (1s/1s) between SMTP connections handling and the calling task
8403sub MainLoop2 {
8404	alarm 0;
8405	eval {
8406    local $SIG{ALRM} =
8407    sub { die "mainloop2_timeout\n" };    # NB: \n required
8408    my $timeout = 120;
8409    $timeout = $MainloopTimeout if $MainloopTimeout > 120;
8410    alarm $timeout;
8411    my $time = $AvailHiRes ? ( Time::HiRes::time() ) : time;
8412
8413    # AZ: 2009-02-05 - signal service status
8414    if ( $SvcStopping != 0 ) {
8415      serviceCheck();
8416      return;
8417    }
8418	my $ntime = $CanStatCPU ? 0.3 : 1;
8419    if ( $time >= $nextLoop2 ) {
8420        $webTime += $time if $CanStatCPU;
8421
8422        $nextLoop2 = $time + 1;    # 0.3s for SMTP stuff
8423        serviceCheck();            # for win32 services
8424        SaveStats() if ( $SaveStatsEvery && $itime >= $NextSaveStats );
8425        my ( $canread, $canwrite );
8426        do {
8427            ( $canread, $canwrite ) =
8428              IO::Select->select( $readable, $writable, undef, 0 );
8429            foreach my $fh (@$canwrite) {
8430                my $l = length( $Con{$fh}->{outgoing} );
8431                d("$fh $Con{$fh} l=$l");
8432                if ($l) {
8433                    my $written = syswrite( $fh, $Con{$fh}->{outgoing},
8434                        $OutgoingBufSizeNew );
8435                    if ($debug) {
8436                        if ( $written <= 200 ) {
8437                            d(      "wrote: ($written)<"
8438                                  . substr( $Con{$fh}->{outgoing}, 0, $written )
8439                                  . ">" );
8440                        } else {
8441                            d("wrote: ($written)<long text>");
8442                        }
8443                    }
8444                    $Con{$fh}->{outgoing} =
8445                      substr( $Con{$fh}->{outgoing}, $written );
8446                    $l = length( $Con{$fh}->{outgoing} );
8447
8448                    # test for highwater mark
8449                    if (   $written > 0
8450                        && $l < $OutgoingBufSizeNew
8451                        && $Con{$fh}->{paused} )
8452                    {
8453                        $Con{$fh}->{paused} = 0;
8454                        $readable->add( $Con{$fh}->{friend} );
8455                    }
8456                    if ($Con{$fh}->{type} ne 'C' &&
8457                		$written > 0 &&
8458                		$Con{$fh}->{friend} &&
8459                		exists $Con{$Con{$fh}->{friend}} &&
8460                		$Con{$Con{$fh}->{friend}}->{lastcmd} =~ /^ *(?:DATA|BDAT)/io )
8461            		{
8462                		$Con{$Con{$fh}->{friend}}->{writtenDataToFriend} += 		$written;
8463            		}
8464                }
8465                if ( length( $Con{$fh}->{outgoing} ) == 0 ) {
8466                    $writable->remove($fh);
8467                }
8468
8469
8470
8471            }
8472            foreach my $fh (@$canread) {
8473                if (
8474                    $fh
8475                    && (   $SocketCalls{$fh} == \&SMTPTraffic
8476                        || $SocketCalls{$fh} == \&NewSMTPConnection
8477                        || $SocketCalls{$fh} == \&WebTraffic
8478                        || $SocketCalls{$fh} == \&NewWebConnection
8479                        || $SocketCalls{$fh} == \&NewStatConnection )
8480                  )
8481                {
8482                    $SocketCalls{$fh}->($fh);
8483                }
8484            }
8485            $time = $AvailHiRes ? ( Time::HiRes::time() ) : time;
8486          } until ( ( @$canread == 0 && @$canwrite == 0 )
8487              || $time >= $nextLoop2 );
8488        my $ntime = $CanStatCPU ? 0.3 : 1;
8489        $nextLoop2=$time+$ntime; # 0.3s for other tasks
8490        $webTime -= $time if $CanStatCPU;
8491        if ( $AutoRestartInterval && $itime >= $endtime ) {
8492
8493            # time to quit -- after endtime and we're bored.
8494            mlog(0,"info: restart time is reached - waiting until all connection are gone but max 5 minutes");
8495        	while ($smtpConcurrentSessions && time < $endtime + 300) {
8496            	my $tendtime = $endtime;
8497            	$endtime = time + 10000;
8498            	&MainLoop2();
8499            	$endtime = $tendtime;
8500            	Time::HiRes::sleep(0.5);
8501        	}
8502			&downASSP("restarting");
8503
8504			&restartCMD();
8505
8506        }
8507
8508        if ( $doShutdown && $itime >= $doShutdown ) {
8509            &downASSP("restarting");
8510
8511			&restartCMD();
8512        }
8513    }
8514    alarm 0;
8515    };
8516
8517}
8518
8519sub detectGhostCon {
8520    my $count = 0;
8521    my $mem   = 0;
8522    foreach my $fh ( keys %Con ) {
8523        next if ( fileno($fh) );
8524        next if ( $Con{$fh}->{timestart} + 3600 > time );
8525        my $this = $Con{$fh};
8526        foreach ( keys %$this ) {
8527            eval { $mem = $mem + length( $this->{$_} ) + 8; };
8528        }
8529        $mem = int( $mem / 1024 + 2 );
8530        &printallCon($fh) if ( $MaintenanceLog > 1 );
8531        $count++;
8532        &done2($fh);
8533        last;
8534    }
8535    if ($count == 0) {
8536      $nextdetectGhostCon = time + 300;
8537    }
8538}
8539
8540sub DebugClear {
8541
8542if ($debug && !$AsASecondary) {
8543
8544 		close $DEBUG;
8545		my $fn = localtime();
8546 		$fn =~ s/^... (...) +(\d+) (\S+) ..(..)/$1-$2-$4-$3/;
8547 		$fn =~ s/[\/:]/\-/g;
8548		$currentDEBUGfile= ">$base/debug/" . $fn . ".dbg";
8549    	open( $DEBUG, '>',"$currentDEBUGfile" );
8550    	binmode($DEBUG);
8551    	my $oldfh = select($DEBUG);
8552    	$| = 1;
8553    	select($oldfh);
8554
8555	}
8556}
8557
8558
8559sub getDBCount {
8560  my ($hash,$config) = @_;
8561  my $hashObject = $hash.'Object';
8562  my $i = 0;
8563
8564 $i = scalar keys %{$hash};
8565
8566  return $i;
8567}
8568sub SaveWhitelist{
8569	return if $AsASecondary;
8570    if ( $UpdateWhitelist && $whitelistdb !~ /mysql/ ) {
8571        mlog( 0, "saving whitelistdb" ) if $MaintenanceLog;
8572
8573        $WhitelistObject->flush() if $WhitelistObject;
8574    }
8575
8576
8577}
8578
8579sub SaveRedlist{
8580	return if $AsASecondary;
8581
8582    if ( $UpdateWhitelist && $redlistdb !~ /mysql/ ) {
8583        mlog( 0, "saving redlistdb" ) if $MaintenanceLog;
8584        $RedlistObject->flush() if $RedlistObject;
8585    }
8586
8587}
8588sub SaveLDAPlist {
8589
8590    mlog( 0, "saving ldaplistdb" ) if $MaintenanceLog;
8591    $LDAPlistObject->flush() 	   if $LDAPlistObject;
8592    $LDAPNotFoundObject->flush()   	if $LDAPNotFoundObject;
8593
8594}
8595
8596sub SavePersBlack {
8597
8598    mlog( 0, "saving persblackdb" ) if $MaintenanceLog;
8599    $PersBlackObject->flush() 	   if $PersBlackObject;
8600
8601
8602}
8603
8604
8605
8606sub SavePB {
8607    return if $AsASecondary;
8608    mlog( 0, "saving penaltydb (pbdb)" ) if $MaintenanceLog;
8609    $PBBlackObject->flush() if $PBBlackObject && $pbdb !~ /mysql/;
8610    $PBWhiteObject->flush() if $PBWhiteObject && $pbdb !~ /mysql/;
8611
8612
8613
8614}
8615
8616sub SaveCache {
8617	if ( $delaydb !~ /mysql/ ) {
8618        mlog( 0, "saving delaydb" ) if $MaintenanceLog;
8619        $DelayObject->flush()      if $DelayObject;
8620        $DelayWhiteObject->flush() if $DelayWhiteObject;
8621    }
8622    mlog( 0, "saving cache records" ) if $MaintenanceLog;
8623    $SameSubjectCacheObject->flush() if $SameSubjectCacheObject;
8624    $OrgnamesCacheObject->flush() if $OrgnamesCacheObject;
8625    $RBLCacheObject->flush()   	if $RBLCacheObject;
8626    $URIBLCacheObject->flush() 	if $URIBLCacheObject;
8627    $SPFCacheObject->flush()   	if $SPFCacheObject;
8628    $PTRCacheObject->flush()   	if $PTRCacheObject;
8629    $MXACacheObject->flush()   	if $MXACacheObject;
8630    $SBCacheObject->flush()    	if $SBCacheObject;
8631    $WhiteOrgObject->flush()    if $WhiteOrgObject;
8632    $RWLCacheObject->flush()   	if $RWLCacheObject;
8633    $FreqObject->flush() 	   	if $FreqObject;
8634    $SMTPfailedObject->flush()  if $SMTPfailedObject;
8635
8636    $PBTrapObject->flush()  	if $PBTrapObject;
8637
8638    mlog( 0, "saving ldaplistdb" ) 	if $MaintenanceLog;
8639    $LDAPlistObject->flush()   		if $LDAPlistObject;
8640    $LDAPNotFoundObject->flush()   	if $LDAPNotFoundObject;
8641    mlog( 0, "saving persblackdb" ) if $MaintenanceLog;
8642    $PersBlackObject->flush() 	   if $PersBlackObject;
8643
8644
8645}
8646
8647sub SaveDB {
8648
8649    my ($CacheObject,$KeyName) = @_;
8650
8651    mlog( 0, "saving cache records for $KeyName" ) if $MaintenanceLog;
8652    $$CacheObject->flush() if $$CacheObject;
8653
8654}
8655
8656
8657  sub SaveHash {
8658  my $HashName = shift;
8659    foreach my $dbGroupEntry (@dbGroup) {
8660            my ( $KeyName, $dbConfig, $CacheObject, $realFileName ) =
8661              split(/,/o,$dbGroupEntry);
8662
8663            next unless $HashName eq $KeyName;
8664
8665            next if $realFileName eq "mysql";
8666            mlog( 0, "saving cache records for $KeyName ($realFileName)") if $MaintenanceLog;
8667            $$CacheObject->flush() if $$CacheObject;
8668
8669            last;
8670
8671   }
8672
8673
8674
8675}
8676sub ResetPB {
8677my $fil = shift;
8678foreach my $dbGroupEntry (@dbGroup) {
8679            my ( $KeyName, $dbConfig, $CacheObject, $realFileName ) =
8680              split(/,/o,$dbGroupEntry);
8681
8682
8683            next if !$CacheObject;
8684            next if $realFileName eq "mysql";
8685            next unless ( $fil =~ /$realFileName/ );
8686#			mlog( 0, "ResetPB $CacheObject,$realFileName/" );
8687			$$CacheObject->resetCache();
8688            last;
8689
8690   }
8691}
8692sub CleanPB {
8693	return if $AsASecondary;
8694    # clean PenaltyBox Databases
8695
8696    &cleanBlackPB if $PBBlackObject;
8697
8698
8699    &cleanWhitePB if $PBWhiteObject;
8700
8701
8702
8703}
8704
8705
8706# global and personal Whitelist handling
8707sub Whitelist {
8708    my($mf,$to,$action)=@_;
8709    d("Whitelist $mf,$to,$action");
8710    $mf = lc $mf;
8711    $to = lc $to;
8712    $to =~ s/^,//o;
8713    $action = lc $action;
8714    my $globWhite = $Whitelist{$mf};
8715    my $persWhite = $Whitelist{"$mf,$to"};
8716    my $time = time;
8717    if (! $action) {                  # is there any Whitelist entry
8718        return 0 if $persWhite > 9999999999;       # a deleted personal
8719        return ($persWhite or $globWhite) ? 1 : 0;      # a personal or global
8720    } elsif ($action eq 'add') {
8721        $Whitelist{"$mf,$to"} = $time if $to;
8722        $Whitelist{$mf} = $time;
8723        return;
8724    } elsif ($action eq 'delete') {
8725        if ($to) {
8726            $Whitelist{"$mf,$to"} = $time + 9999999999;  # delete the personal
8727        } else {
8728            delete $Whitelist{$mf};                      # delete the global entry
8729            while (my ($k,$v) = each(%Whitelist)) {      # and all personal
8730                delete $Whitelist{$k} if $k =~ /^$mf,/;
8731            }
8732        }
8733    }
8734}
8735sub cleanTrashlist {
8736    my $TrashObject = tie %Trashlist, 'orderedtie', "$base/trashlist.db";
8737    my $files_before = my $files_deleted = 0;
8738    my $t = time;
8739    my $mcount;
8740
8741    while ( my ( $k, $v ) = each(%Trashlist) ) {
8742        if ( !-e $k ) {
8743            delete $Trashlist{$k};
8744            $files_deleted++;
8745            next;
8746        }
8747        my $ct = $v;
8748        $files_before++;
8749
8750        if ( !$MaxKeepDeleted or $t - $ct >= $MaxKeepDeleted * 3600 * 24 ) {
8751            delete $Trashlist{$k};
8752            unlink $k;
8753            $files_deleted++;
8754        }
8755    }
8756    mlog(
8757"Trashlist: cleaning finished; before=$files_before, deleted=$files_deleted\n"
8758    ) if $files_before > 0;
8759
8760}
8761sub CleanWhitelist {
8762  d('CleanWhitelist');
8763  if (!-e "$base/$whitelistdb") {
8764    	if (-e "$base/$whitelistdb.bak") {
8765    		 copy("$base/$whitelistdb.bak","$base/$whitelistdb");
8766    	}
8767  }
8768  mlog(0,"cleaning up whitelist database ...") if $MaintenanceLog;
8769  my $t=time;
8770  my $keys_before = my $keys_deleted = 0;
8771  my $maxwhite = $MaxWhitelistDays;
8772  $maxwhite = 360 if $MaxWhitelistDays < 360;
8773  my $maxtime = $maxwhite * 3600 * 24;
8774  my $delta;
8775  my $h;
8776  my $hat;
8777  my $record;
8778  my $WL;
8779  $record = "whitedomains.txt";
8780  open $WL, ">","$base/$record";
8781  if ($MaxWhitelistDays) {
8782      while (my ($k,$v)=each(%Whitelist)) {
8783        $keys_before++;
8784
8785
8786        $v = 0 unless $v;
8787        next if $v < 1000000000;
8788        $k = batv_remove_tag(0,$k,'');
8789        $k =~ /\@(.*)/;
8790        $delta = $t-$v;
8791        if ($delta >= $maxtime or ($k=~/,/o && $v > 9999999999 && $delta + 9999999999 >= $maxtime)) {
8792          	delete $Whitelist{$k};
8793          	$v -= 9999999999 if $v > 9999999999;
8794          	mlog(0,"Admininfo: $k removed from whitelistdb - entry was older than MaxWhitelistDays (" . &timestring($v,'') . ')',1) if $MaintenanceLog >= 2;
8795          	$keys_deleted++;
8796
8797        }
8798
8799        $h  = $1 if $k =~ /\@(.*)/;
8800        $hat = $1 if $k =~ /(\@.*)/;
8801
8802        if ($k =~ /\*\@/ ) {
8803        	print $WL "$k\n";
8804        	mlog(0,"Admininfo: $k is domain address",1) ;
8805
8806       	}
8807
8808        if (exists $LDAPlist{$hat} or ($localDomains && ( $h =~ $LDRE || $hat =~ $LDRE ))) {
8809        	mlog(0,"Admininfo: $k should be removed from whitelistdb - entry was local address",1) if $MaintenanceLog;
8810
8811       	}
8812      }
8813      mlog(0,"cleaning whitelist database finished: keys before=$keys_before, deleted=$keys_deleted") if $keys_before && $MaintenanceLog;
8814  }
8815  &SaveWhitelist();
8816  close $WL;
8817}
8818sub CleanCache {
8819	return if $AsASecondary;
8820    mlog( 0, "cleaning up cache records ..." ) if $MaintenanceLog;
8821
8822    &cleanCacheSubject;
8823    $SameSubjectCacheObject->flush()   if $SameSubjectCacheObject;
8824    MainLoop2();
8825
8826    &cleanCacheRBL if $RBLCacheInterval;
8827    $RBLCacheObject->flush()   if $RBLCacheObject;
8828    MainLoop2();
8829
8830    &cleanCacheURI if $URIBLCacheInterval;
8831    $URIBLCacheObject->flush() if $URIBLCacheObject;
8832    MainLoop2();
8833
8834    &cleanCacheRWL if $RWLCacheInterval;
8835    $RWLCacheObject->flush()   if $RWLCacheObject;
8836    MainLoop2();
8837
8838    &cleanCachePTR if $PTRCacheInterval;
8839    $PTRCacheObject->flush()   if $PTRCacheObject;
8840    MainLoop2();
8841
8842    &cleanCacheMXA if $MXACacheInterval;
8843    $MXACacheObject->flush()   if $MXACacheObject;
8844    MainLoop2();
8845
8846    &cleanCacheSPF if $SPFCacheInterval;
8847    $SPFCacheObject->flush()   if $SPFCacheObject;
8848    MainLoop2();
8849
8850    &cleanCacheSB if $SBCacheExp;
8851    $SBCacheObject->flush()    if $SBCacheObject;
8852    MainLoop2();
8853
8854
8855    &cleanTrapPB if $PBTrapObject;
8856    $PBTrapObject->flush()  if $PBTrapObject;
8857 	MainLoop2();
8858
8859
8860
8861
8862
8863
8864}
8865
8866sub DoCleanDelayDB {
8867    return if $AsASecondary;
8868    mlog( 0, "cleaning up delaying databases ..." ) if $MaintenanceLog;
8869    my $t = time;
8870    my $keys_before = $keys_deleted = 0;
8871    while ( my ( $k, $v ) = each(%Delay) ) {
8872        $keys_before++;
8873        if ( $t - $v >= $DelayEmbargoTime * 60 + $DelayWaitTime * 3600 ) {
8874            delete $Delay{$k};
8875            $keys_deleted++;
8876        }
8877        MainLoop2();
8878    }
8879    mlog( 0,
8880"cleaning delaying database (triplets) finished; keys before=$keys_before, deleted=$keys_deleted"
8881    ) if $MaintenanceLog;
8882    $keys_before = $keys_deleted = 0;
8883    while ( my ( $k, $v ) = each(%DelayWhite) ) {
8884        $keys_before++;
8885        if ( $t - $v >= $DelayExpiryTime * 24 * 3600 ) {
8886            delete $DelayWhite{$k};
8887            $keys_deleted++;
8888        }
8889        MainLoop2();
8890    }
8891    mlog( 0,
8892"cleaning delaying database (whitelisted tuplets) finished; keys before=$keys_before, deleted=$keys_deleted"
8893    ) if $MaintenanceLog;
8894    $DomainCache ||= '^(?!)';
8895
8896	if ( $delaydb !~ /mysql/ ) {
8897
8898        $DelayObject->flush()      if $DelayObject;
8899        $DelayWhiteObject->flush() if $DelayWhiteObject;
8900    }
8901
8902
8903}
8904
8905
8906
8907sub mlogRe{
8908	my($fh,$subre,$regextype,$check,$logging)=@_;
8909	my $this = exists $Con{$fh} ? $Con{$fh} : {};
8910	$subre =~ s/\s+/ /go;
8911	$subre=substr($subre,0,$RegExLength);
8912	$this->{messagereason}="Regex:$regextype '$subre'";
8913 	$this->{myheader}.="X-Assp-Re-$regextype: $subre\r\n" if $AddRegexHeader or $logging;
8914    my $m;
8915	$m = $check . ' ' if $check;
8916	$m .= $this->{messagereason};
8917	mlog( $fh, $m, 1, 1 ) if $regexLogging or $logging;
8918}
8919
8920# win32 debug/trace output
8921sub w32dbg {
8922    if ($Win32Debug && $AvailWin32Debug) {
8923        my ($msg) = @_;
8924        OutputDebugString("(ASSP): $msg");
8925    }
8926}
8927
8928sub dlog {
8929    my ($msg) = @_;
8930
8931    return unless $DebugLog;
8932    print "$msg\n";
8933    print LOG "$msg\n";
8934    w32dbg("(DEBUG): $msg");
8935}
8936
8937sub now {
8938# %Y: year 4 digits
8939# %y: year 2 digits
8940# %m: month (1-12)
8941# %d: day (1-31)
8942# %H: hour (0-23)
8943# %M: minute (0-59)
8944# %S: second (0-59)
8945# now("%Y-%m-%d %H:%M:%S") 		-> 2001-09-13 08:05:45
8946# now("%Y-%m-%d %H:%M:%S",1) 	-> 2001-09-12 08:05:45
8947# now("%Y-%m-%d %H:%M:%S",-1) 	-> 2001-09-14 08:05:45
8948
8949  my ($format , $pastdays, ) = @_;
8950
8951  my $NOW=timelocal(localtime) - $pastdays * 24 * 60 * 60;
8952  my $y=sprintf("%02d",(localtime($NOW))[5]-100);
8953  my $Y=sprintf("%04d",(localtime($NOW))[5]+1900);
8954  my $m=sprintf("%02d",(localtime($NOW))[4]+1);
8955  my $d=sprintf("%02d",(localtime($NOW))[3]);
8956  my $H=sprintf("%02d",(localtime($NOW))[2]);
8957  my $M=sprintf("%02d",(localtime($NOW))[1]);
8958  my $S=sprintf("%02d",(localtime($NOW))[0]);
8959
8960  $format =~ s/%y/$y/;
8961  $format =~ s/%Y/$Y/;
8962  $format =~ s/%m/$m/;
8963  $format =~ s/%d/$d/;
8964  $format =~ s/%H/$H/;
8965  $format =~ s/%M/$M/;
8966  $format =~ s/%S/$S/;
8967
8968  return $format;
8969
8970}
8971
8972
8973sub printLOG {
8974my ( $action, $msg) = @_;
8975return if $silent && $AsASecondary;
8976
8977return if !$logfile;
8978	if ($action eq "open" or ($action eq "print" && $LOGstatus!=1)) {
8979	$LOGstatus=1;
8980	# open the logfile
8981  		if(open($LOG,'>>',"$base/$logfile")) {
8982    			if ($LogCharset) {
8983          			binmode $LOG, ":encoding($LogCharset)";
8984
8985      			} else {
8986          			binmode $LOG if !$enableWORS;
8987          			binmode $LOG, ":crlf" if $enableWORS;
8988      			}
8989      			$LOG->autoflush;
8990  		}
8991  	}
8992  	return if $action eq "open";
8993
8994  	if ($action eq "print") {
8995		print $LOG $msg;
8996		return;
8997	}
8998
8999  	if ($action eq "close") {
9000	# close the logfile
9001		$LOGstatus=2;
9002  		close $LOG if $logfile;
9003  		  	if ($! && fileno($LOG)) {
9004                print "error: unable to close $base/$logfile - $!\n";
9005                print $LOG "error: unable to close $base/$logfile - $!$WORS";
9006            }
9007
9008	}
9009
9010
9011}
9012
9013sub mlog_i {
9014    my ( $fh, $comment, $noprepend, $noipinfo ) = @_;
9015    mlog( $fh, "$comment", $noprepend, $noipinfo );
9016    &mlogWrite() if $WorkerNumber == 0;
9017}
9018
9019
9020sub mlog {
9021    my ( $fh, $comment, $noprepend, $noipinfo ) = @_;
9022
9023    return if $silent && $AsASecondary;
9024    return if $comment =~ /\[spam found\] --  --/;
9025    my $this = $fh ? $Con{$fh} : 0;
9026    my $mm;
9027
9028    $this->{comment} = $comment;
9029    my $lccomment = lc $comment;
9030    my $noNotify  = $comment =~ s/^\*x\*//;
9031
9032    $this->{score} = 0;
9033    my $logfile = $logfile;
9034    $logfile =~ s/\\/\//g;
9035    my $archivelogfile;
9036
9037    PrintConfigHistory($lccomment) if $lccomment =~ /^adminupdate/i;
9038
9039    PrintConfigHistory($lccomment) if $lccomment =~ /^configerror/i;
9040
9041    PrintUpdateHistory($lccomment) if $lccomment =~ /autoupdate/i;
9042    PrintAdminInfo($lccomment)     if $lccomment =~ /^email-interface/i;
9043    PrintAdminInfo($lccomment)     if $lccomment =~ /^AdminUpdate/i;
9044
9045    if ( $noLogLineRe && $comment =~ $noLogLineReRE ) { return 1; }
9046    if ( $this && $noLogLineRe && $this->{prepend} =~ $noLogLineReRE ) {
9047        return 1;
9048    }
9049
9050    # cosmetic
9051    $comment =~ s/(.*)/$1;/ if $comment !~ /;$/;
9052
9053    my $m = &timestring();
9054    my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
9055
9056    if ( $LogRollDays > 0 ) {
9057
9058        # roll log every $LogRollDays days, at midnight
9059        my $t = int(
9060            (
9061                time +
9062                  Time::Local::timelocal( localtime() ) -
9063                  Time::Local::timelocal( gmtime() )
9064            ) / ( $LogRollDays * 24 * 3600 )
9065        );
9066
9067        if (   $logfile
9068            && $mlogLastT
9069            && $t != $mlogLastT
9070            && $logfile ne "maillog.log"
9071            && $asspLog )
9072        {
9073
9074            # roll the log
9075
9076            my $mm = &timestring( time - 7200, 'd', $LogNameDate );
9077            my ( $logdir, $logdirfile );
9078            ( $logdir, $logdirfile ) = ( $1, $2 )
9079              if $logfile =~ /^(.*)[\/\\](.*?)$/;
9080            if ( !$logdir ) {
9081                $archivelogfile = "$mm.$logfile";
9082
9083            }
9084            else {
9085                -d "$base/$logdir" or mkdir "$base/$logdir", 0755;
9086
9087                $archivelogfile = "$logdir/$mm.$logdirfile";
9088
9089            }
9090
9091            my $msg =
9092              "$m: Rolling log file -- archive saved as '$archivelogfile'$WORS";
9093
9094            printLOG( "print", $msg );
9095
9096            print $msg unless $silent;
9097
9098            w32dbg(
9099                "$m: Rolling log file -- archive saved as '$archivelogfile'");
9100            printLOG("close");
9101
9102            sleep 1;
9103
9104            rename( "$base/$logfile", "$base/$archivelogfile" );
9105            if ( $! && !-e "$base/$archivelogfile" ) {
9106                print
9107"error: unable to rename file $base/$logfile to $base/$archivelogfile - $!\n";
9108
9109            }
9110
9111            # open the logfile
9112            printLOG("open");
9113
9114            printLOG("print","$m new log file -- old log file renamed to '$archivelogfile'$WORS") ;
9115             w32dbg(
9116                "$m new log file -- old log file renamed to '$archivelogfile'");
9117
9118        }
9119        $mlogLastT = $t;
9120    }
9121    my @m;
9122    my $header;
9123    if ($this) {
9124        return if $fh && $Con{$fh} && &matchIP( $Con{$fh}->{ip}, 'noLoggingIPs' );
9125        $header = substr( $this->{header}, 0, $MaxBytes )
9126          if ( $fh && $MaxBytes && !$this->{noLoggingIPs} && $noLoggingRe );
9127        if (
9128            $this->{noLoggingIPs}
9129            || (
9130                $noLoggingRe
9131                && (
9132                    (
9133                           $this->{mailfrom}
9134                        && $this->{mailfrom} =~ /($noLoggingReRE)/
9135                    )
9136                    || ( $header =~ /($noLoggingReRE)/ )
9137                )
9138            )
9139          )
9140        {
9141            $this->{noLoggingIPs} = 1 if ($fh);
9142            return 1;
9143        }
9144
9145        $m .= " $this->{msgtime}" if $this->{msgtime};
9146        if ( "$fh" =~ /SSL/io or "$this->{friend}" =~ /SSL/io ) {
9147            $m .=
9148              ( "$fh" =~ /SSL/io && $this->{oldfh} ) ? ' [TLS-in]'
9149              : (    "$fh" =~ /SSL/io
9150                  && !$this->{oldfh}
9151                  && $this->{localport} == 25 ) ? ' [SSL-in]'
9152              : '';
9153            $m .=
9154              ( "$this->{friend}" =~ /SSL/io
9155                  && $Con{ $this->{friend} }->{oldfh} ) ? ' [TLS-out]'
9156              : ( "$this->{friend}" =~ /SSL/io
9157                  && !$Con{ $this->{friend} }->{oldfh} ) ? ' [SSL-out]'
9158              : '';
9159        }
9160        $m .= " $this->{prepend}"
9161          if $tagLogging && $this->{prepend} && !$noprepend;
9162
9163        if (   $expandedLogging
9164            || $noipinfo >= 2
9165            || ( !$this->{loggedIpFromTo} && !$noipinfo ) )
9166        {
9167            $m .= " $this->{ip}"         if ( $this->{ip} );
9168            $m .= " [OIP: $this->{cip}]" if ( $this->{cip} );
9169            my $mf = &batv_remove_tag( 0, $this->{mailfrom}, '' );
9170            ( my $from ) =
9171              substr( $this->{header}, 0, $MaxBytes ) =~
9172              /\nfrom:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio
9173              if !$mf;
9174            $m .= " FROM:<$from>" if $from && !$mf && $FromLogging;
9175            $m .= " <$mf>" if $mf;
9176            $this->{from} = $from if $from && !$mf;
9177
9178            my $to;
9179            $to = $this->{orgrcpt} if $noipinfo == 3;
9180            ($to) = $this->{rcpt} =~ /(\S+)/o unless $to;
9181            my $mm = $m;
9182            if ($to) {
9183                $this->{loggedIpFromTo} = 1 if $noipinfo < 3;
9184                $m .= " to: $to";
9185            }
9186            if (   $noipinfo < 3
9187                && $comment =~ / \[(?:spam found|MessageOK)\] /oi )
9188            {
9189                my $c = $comment;
9190                $c =~ s/\r//go;
9191                $c =~ s/\n([^\n]+)/\n\t$1/go;
9192                $c .= "\n" if ( $c !~ /\n$/o );
9193                my %seen;
9194                for ( split( /\s+/o, $this->{rcpt} ) ) {
9195                    next unless $_;
9196                    next if $seen{ lc $_ };
9197                    $seen{ lc $_ } = 1;
9198                    push @m, "$mm to: $_ $c";
9199                }
9200            }
9201        }
9202        $m .= " $comment$WORS";
9203    } else {
9204        $m .= " " . ucfirst($comment) . "$WORS";
9205    }
9206
9207    PrintWhitelistAdd($m) if $m =~ /whitelist addition/i;
9208    PrintWhitelistAdd($m) if $m =~ /whitelist deletion/i;
9209
9210    if (   $canNotify
9211        && !$noNotify
9212        && scalar keys %NotifyRE
9213        && $m =~ /$NotifyReRE/
9214        && $m !~ /$NoNotifyReRE/ )
9215    {
9216        my $rcpt;
9217        my $sub;
9218        while ( my ( $k, $v ) = each %NotifyRE ) {
9219            if ( $m =~ /$k/i ) {
9220                $rcpt = $v;
9221                $sub  = $NotifySub{$k} . " from $myName"
9222                  if exists $NotifySub{$k};
9223                last;
9224            }
9225        }
9226        $sub ||= "ASSP event notification from $myName";
9227        &sendNotification( $EmailFrom, $rcpt, $sub,
9228            "log event on host $myName:\r\n\r\n$m\r\n" )
9229          if $rcpt;
9230
9231    }
9232
9233    $m =~ s/\r//go;
9234    $m =~ s/\n([^\n]+)/\n\t$1/go;
9235    $m .= "\n" if ( $m !~ /\n$/o );
9236
9237    print DEBUG $m if $debug && !$AsASecondary;
9238
9239    tosyslog( 'info', substr( $m, 18 ) )
9240      if ( $CanUseSyslog || $CanUseNetSyslog ) && $sysLog;
9241    my $sm = $m;
9242    $sm =~ s/\r//g;
9243    $sm =~ s/-\> $base\//-\> /;
9244    print $sm unless $silent;
9245
9246    eval { $m = Encode::decode( 'Guess', $m ); } if $m;
9247
9248    printLOG( "print", $m ) if $logfile && $asspLog;
9249    w32dbg("$m");
9250}
9251
9252sub NotifyFrequencyOK {
9253    my $text = shift;
9254    return 1 if eval{$DEBUG && $DEBUG->opened;};
9255    return 1 unless keys %NotifyFreqTF;
9256    return 1 if $text !~ /^(info|warning|error)\s*:/oi;
9257    return 1 unless $NotifyFreqTF{$1};
9258    return 0 if $NotifyLastFreq{$1} + $NotifyFreqTF{$1} > time;
9259    $NotifyLastFreq{$1} = time;
9260    return 1;
9261}
9262
9263sub tosyslog {
9264    my ( $priority, $msg ) = @_;
9265    return 0 unless ( $priority =~ /info|err|debug/ );
9266    $msg =~ s/^\s+//;
9267
9268    if ($AvailNetSyslog) {
9269        my $s = new Net::Syslog(
9270            Facility   => $SysLogFac,
9271            Priority   => 'Debug',
9272            SyslogPort => $sysLogPort,
9273            SyslogHost => $sysLogIp
9274        );
9275        $s->send( $msg, Priority => $priority );
9276    } else {
9277        setlogsock('unix');
9278        openlog( 'assp', 'pid,cons', $SysLogFac );
9279        syslog( $priority, $msg );
9280        closelog();
9281    }
9282
9283    return 1;
9284}
9285
9286sub tzStr {
9287
9288    # calculate the time difference in minutes
9289    my $minoffset =
9290      ( Time::Local::timelocal( localtime() ) -
9291          Time::Local::timelocal( gmtime() ) ) / 60;
9292
9293   # translate it to "hour-format", so that 90 will be 130, and -90 will be -130
9294    my $sign = $minoffset < 0 ? -1 : +1;
9295    $minoffset = abs($minoffset) + 0.5;
9296    my $tzoffset = 0;
9297    $tzoffset = $sign * ( int( $minoffset / 60 ) * 100 + ( $minoffset % 60 ) )
9298      if $minoffset;
9299
9300    # apply final formatting, including +/- sign and 4 digits
9301    return sprintf( "%+05d", $tzoffset );
9302}
9303
9304sub getTimeDiffAsString {
9305
9306    my ( $tdiff, $seconds ) = @_;
9307
9308    my $days  = int( $tdiff / 86400 );
9309    my $hours = int( ( $tdiff - ( $days * 86400 ) ) / 3600 );
9310    my $mins  = int( ( $tdiff - ( $days * 86400 ) - ( $hours * 3600 ) ) / 60 );
9311    my $secs =
9312      int( $tdiff - ( $days * 86400 ) - ( $hours * 3600 ) - ( $mins * 60 ) );
9313
9314    my $ret;
9315    $ret = $days . " day" . ( $days == 1 ? " " : "s " );
9316    $ret .= $hours . " hour" . ( $hours == 1 ? " " : "s " );
9317    $ret .= $mins . " min" .   ( $mins == 1  ? " " : "s " );
9318    $ret .= $secs . " sec" .   ( $secs == 1  ? " " : "s " ) if $seconds;
9319
9320    return $ret;
9321}
9322
9323sub getTimeDiff {
9324    my ( $tdiff, $seconds ) = @_;
9325    my $m = getTimeDiffAsString( $tdiff, $seconds );
9326    if ( $m =~ s/^0 days // ) {
9327        if ( $m =~ s/^0 hours // ) {
9328        }
9329    }
9330    return $m;
9331}
9332
9333#####################################################################################
9334#                Socket handlers
9335
9336
9337
9338sub setSSLfailed {
9339    my $ip = shift;
9340    return unless $banFailedSSLIP;
9341    mlog(0, "SSLfailed $banFailedSSLIP $ip");
9342    if (exists $SSLfailed{$ip}) {   # ban if it failed before
9343        $SSLfailed{$ip} = time;
9344    } elsif (($banFailedSSLIP & 1) && (matchIP($ip,'acceptAllMail',0,1) or $ip =~ /$IPprivate/o)) {  # give privates one more chance
9345        $SSLfailed{$ip} = 0;
9346    } elsif ($banFailedSSLIP & 2) {
9347        $SSLfailed{$ip} = time;    # ban external IP if it failed before
9348    }
9349    return;
9350}
9351
9352sub switchSSLClient {
9353    my $fh =shift;
9354    my $sslfh;
9355    my $try = 4;
9356    eval{$fh->blocking(1);};
9357    $sslfh = IO::Socket::SSL->start_SSL($fh,{
9358             SSL_startHandshake => 1,
9359             getSSLParms(1)
9360             });
9361    while ($try-- && "$sslfh" !~ /SSL/io && ($IO::Socket::SSL::SSL_ERROR == eval('SSL_WANT_READ') ? 1 : $IO::Socket::SSL::SSL_ERROR == eval('SSL_WANT_WRITE') ) && $SSLRetryOnError)
9362    {
9363         &ThreadYield();
9364         Time::HiRes::sleep(0.5);
9365         $ThreadIdleTime{$WorkerNumber} += 0.5;
9366         mlog($fh,"info: retry ($try) SSL negotiation - peer socket was not ready");
9367
9368         $sslfh = IO::Socket::SSL->start_SSL($fh,{
9369             SSL_startHandshake => 1,
9370             getSSLParms(1)
9371             });
9372    }
9373    if ("$sslfh" =~ /SSL/io) {
9374        eval{$sslfh->blocking(0);};
9375    } else {
9376        eval{$fh->blocking(0);};
9377    }
9378    return $sslfh,$fh;
9379}
9380sub switchSSLServer {
9381    my $fh =shift;
9382    my $sslfh;
9383    my $try = 4;
9384    eval{$fh->blocking(1);};
9385    $sslfh = IO::Socket::SSL->start_SSL($fh,{
9386             SSL_startHandshake => 1,
9387             getSSLParms(0)
9388             });
9389    while ($try-- && "$sslfh" !~ /SSL/io && ($IO::Socket::SSL::SSL_ERROR == eval('SSL_WANT_READ') ? 1 : $IO::Socket::SSL::SSL_ERROR == eval('SSL_WANT_WRITE') ) && $SSLRetryOnError)
9390    {
9391         &ThreadYield();
9392         Time::HiRes::sleep(0.5);
9393         $ThreadIdleTime{$WorkerNumber} += 0.5;
9394         mlog($fh,"info: retry ($try) SSL negotiation - peer socket was not ready");
9395
9396         $sslfh = IO::Socket::SSL->start_SSL($fh,{
9397             SSL_startHandshake => 1,
9398             getSSLParms(0)
9399             });
9400    }
9401    if ("$sslfh" =~ /SSL/io) {
9402        eval{$sslfh->blocking(0);};
9403    } else {
9404        eval{$fh->blocking(0);};
9405    }
9406    return $sslfh,$fh;
9407}
9408
9409
9410sub matchFH {
9411    my ($fh, @fhlist) = @_;
9412    return 0 unless @fhlist;
9413    my $sinfo = $fh->sockhost() . ':' . $fh->sockport();
9414    $sinfo =~ s/:::/\[::\]:/o;
9415
9416    while (@fhlist) {
9417        my $lfh = shift @fhlist;
9418        if ($lfh =~ /^(?:0\.0\.0\.0|\[::\])(:\d+)$/o) {
9419            my $p = $1;
9420            return 1 if ($sinfo =~ /$p$/);
9421        }
9422        return 1 if ($sinfo eq $lfh);
9423    }
9424    return 0;
9425}
9426
9427sub NewSMTPConnection {
9428    my $client = shift;
9429
9430    my $isSSL;
9431    my ( $server, $destination, $relayok, $relayused );
9432    my $listenport;
9433    $destination = $smtpDestination;
9434    my $destinationport;
9435    $destinationport = "smtpDestination";
9436    if ( matchFH( $client, @lsnRelayI ) ) {
9437
9438        # a relay connection -- destination is the relayhost
9439        $relayok                       = 1;
9440        $relayused                     = 1;
9441        $Con{$client}->{relayok}       = "relayPort";
9442        $Con{$client}->{passingreason} = "relayPort";
9443        d('NewSMTPConnection - relayPort');
9444        $listenport      = "relayPort";
9445        $destination     = $relayHost if $relayHost;
9446        $destinationport = "relayHost" if $relayHost;
9447    }
9448    elsif ( matchFH( $client, @lsn2I ) ) {
9449
9450        # connection on the Second Listen port
9451
9452        d('NewSMTPConnection - listenPort2');
9453        $listenport      = "listenPort2";
9454        $relayok         = 0;
9455        $destination     = $smtpAuthServer if $smtpAuthServer;
9456        $destinationport = "smtpAuthServer" if $smtpAuthServer;
9457
9458    }
9459    elsif ( matchFH( $client, @lsnSSLI ) ) {
9460
9461        # connection on the the secure SSL port
9462        d('NewSMTPConnection - listenPortSSL');
9463        $listenport      = "listenPortSSL";
9464        $relayok         = 0;
9465        $isSSL           = 1;
9466        $destination     = $smtpDestinationSSL if $smtpDestinationSSL;
9467        $destinationport = "smtpDestinationSSL" if $smtpDestinationSSL;
9468    }
9469    else {
9470
9471        d('NewSMTPConnection - listenPort');
9472        $listenport = "listenPort";
9473
9474        $relayok = 0;
9475
9476    }
9477
9478    if ( !( $client = $client->accept ) ) {
9479        d("accept failed: $client");
9480
9481        return;
9482    }
9483
9484    my $fnoC      = fileno($client);
9485    my $ip        = $client->peerhost();
9486    my $port      = $client->peerport();
9487    my $localip   = $client->sockhost();
9488    my $localport = $client->sockport();
9489
9490    my $ret;
9491
9492    $ip      = "127.0.0.1" if $ip =~ /::1/;
9493    $localip = "127.0.0.1" if $localip =~ /::1/;
9494    $Con{$client}->{port}         = $port;
9495    $Con{$client}->{noprocessing} = 1
9496      if matchIP( $ip, 'noProcessingIPs', 0, 1 )
9497      && !matchIP( $ip, 'NPexcludeIPs', 0, 1 );
9498    $Con{$client}->{whitelisted} = matchIP( $ip, 'whiteListedIPs', 0, 1 );
9499    $Con{$client}->{nodelay} ||= 1 if matchIP( $ip, 'noDelay', 0, 1 );
9500
9501    # shutting down ?
9502
9503    $relayok = $Con{$client}->{relayok} if $Con{$client}->{relayok};
9504	$relayok = $Con{$client}->{authenticated} if $Con{$client}->{authenticated};
9505    if ($shuttingDown) {
9506        mlog( 0,
9507"connection from $ip:$port rejected -- shutdown/restart process is in progress"
9508        );
9509
9510        $client->write(
9511"421 <$myName> Service temporary not available, closing transmission channel\r\n"
9512        );
9513        $client->close();
9514        d('NewSMTPConnection - shutdown detected');
9515        return;
9516    }
9517
9518    $Stats{smtpConnSSL}++ if $isSSL;
9519    $Con{$client}->{timestart} = Time::HiRes::time();
9520
9521    # SSL error in the past
9522    if ($ip && $isSSL && $SSLfailed{$ip}) {
9523        mlog(0,"connection from $ip:$port rejected -- IP has failed SSL in the past");
9524
9525        my $out = "421 <$myName> SSL-Service not available for IP $ip, closing transmission channel\r\n";
9526        &NoLoopSyswrite($client,$out,0) if $ip;
9527        threadConDone($client);
9528        delete $Con{$client};
9529        close($client);
9530        d('NewSMTPConnection - IP has failed SSL in the past');
9531        return;
9532    } elsif ($ip && $isSSL && exists $SSLfailed{$ip}) {
9533        delete $SSLfailed{$ip};
9534    }
9535
9536
9537    $Con{$client}->{prepend} = "[DropList]";
9538    if ( !&DroplistOK( $client, $ip ) )
9539
9540    {
9541
9542        mlog(
9543            $client,
9544"[spam found] -- $Con{$client}->{messagereason} -- $Con{$client}->{logsubject}",
9545            1
9546        );
9547        $Stats{denyStrict}++;
9548        $Con{$client}->{type} = 'C';
9549        &NoLoopSyswrite( $client, "$DenyError\r\n" );
9550        $Con{$client}->{error} = '5';
9551        done($client);
9552        return;
9553
9554    }
9555
9556    my $byWhatList = 'denySMTPConnectionsFromAlways';
9557    if ( $ip && $denySMTPstrictEarly ) {
9558        $ret = matchIP( $ip, 'denySMTPConnectionsFromAlways', $client, 0 );
9559        $ret = matchIP( $ip, 'droplist', $client, 0 )
9560          if ( !$ret
9561            && ( $DoDropList == 2 or $DoDropList == 3 )
9562            && ( $byWhatList = 'droplist' ) );
9563    }
9564
9565    if (   $ip
9566        && $denySMTPstrictEarly
9567        && $ret
9568        && $DoDenySMTPstrict
9569        && !matchIP( $ip, 'noPB',          0, 1 )
9570        && !matchIP( $ip, 'noBlockingIPs', 0, 1 ) )
9571    {
9572        $Con{$client}->{prepend} = "[DenyStrict]";
9573        if ( $DoDenySMTPstrict == 1 ) {
9574            mlog( $client, "$ip:$port denied by $byWhatList strict: $ret" )
9575              if $denySMTPLog || $ConnectionLog >= 2;
9576            $Stats{denyStrict}++;
9577            $Con{$client}->{type} = 'C';
9578            &NoLoopSyswrite(
9579                $client,
9580"554 <$myName> Service denied, closing transmission channel\r\n",
9581                0
9582            );
9583            $Con{$client}->{error} = '5';
9584            done($client);
9585            return;
9586        }
9587        elsif ( $DoDenySMTPstrict == 2 ) {
9588            mlog( $client,
9589                "[monitoring] $ip:$port denied by $byWhatList strict: $ret" )
9590              if $denySMTPLog || $ConnectionLog >= 2;
9591            $Con{$client}->{prepend} = '';
9592        }
9593    }
9594
9595 # ip connection limiting  parallel session
9596    my $doIPcheck;
9597#    $maxSMTPipSessions = 999 if ( !$maxSMTPipSessions );
9598        if ( $ip &&
9599         ! matchIP($ip,'noMaxSMTPSessions',0,1) &&
9600         ($doIPcheck =
9601            ! $relayok &&
9602            ! $Con{$client}->{authenticated} &&
9603            ! matchIP($ip,'noProcessingIPs',0,1) &&
9604            ! matchIP($ip,'whiteListedIPs',0,1) &&
9605            ! matchIP($ip,'noDelay',0,1) &&
9606            ! matchIP($ip,'ispip',0,1) &&
9607            ! matchIP($ip,'acceptAllMail',0,1) &&
9608            ! matchIP($ip,'noBlockingIPs',0,1)
9609         )
9610       )
9611    {
9612
9613
9614        if ($$maxSMTPipSessions && ++$SMTPSession{$ip} > $maxSMTPipSessions ) {
9615            $SMTPSession{$ip}--;
9616            delete $SMTPSession{$ip} if ($SMTPSession{$ip} <= 0);
9617            d("limiting ip: $ip");
9618            mlog( 0, "limiting $ip connections to $maxSMTPipSessions" )
9619              if $ConnectionLog  || $SessionLog;
9620            $Stats{smtpConnLimitIP}++;
9621
9622           $Con{$client}->{messagereason}="limiting $ip connections to $maxSMTPipSessions";
9623            pbAdd( $client, $ip,$iplValencePB, "LimitingIP" ) if ! matchIP($ip,'noPB',0,1);
9624            $client->write(
9625                "451 4.7.1 Service temporarily denied, closing transmission channel\r\n"
9626            );
9627            $client->close();
9628            d("limiting ip: $client");
9629            return;
9630        } else {
9631            $SMTPSession{$client} = 1;
9632        }
9633    }
9634    # check relayPort usage
9635    if (   $relayused
9636        && $allowRelayCon
9637        && !matchIP( $ip, 'allowRelayCon', 0, 1 ) )
9638    {
9639        $Con{$client}->{prepend} = "[RelayAttempt]";
9640        $Con{$client}->{type}    = 'C';
9641        &NoLoopSyswrite(
9642            $client,
9643"554 <$myName> Relay Service denied for IP $ip, closing transmission channel\r\n",
9644            0
9645        );
9646        $Con{$client}->{error} = '5';
9647        mlog( 0, "rejected relay attemp on allowRelayCon for ip $ip" )
9648          if $ConnectionLog >= 2 || $SessionLog;
9649        done($client);
9650        $Stats{rcptRelayRejected}++;
9651        return;
9652    }
9653
9654    my $bip = &ipNetwork( $ip, $PenaltyUseNetblocks );
9655
9656    if (   $DelayIP
9657        && $DelayIPTime
9658        && $doIPcheck
9659        && !$allTestMode
9660        && ( my $pbval = [ split( /\s+/o, $PBBlack{$bip} ) ]->[3] ) > $DelayIP
9661        && ( !$DelayIPPB{$bip} || ( $DelayIPPB{$bip} + $DelayIPTime > time ) )
9662        && $ip !~ /$IPprivate/o
9663        && !exists $PBWhite{$bip}
9664        && !matchIP( $ip, 'noPB', 0, 1 ) )
9665    {
9666        $DelayIPPB{$bip} = time unless $DelayIPPB{$bip};
9667        $Stats{delayConnection}++;
9668        $Con{$client}->{type} = 'C';
9669        &NoLoopSyswrite( $client, "451 4.7.1 Please try again later\r\n", 0 );
9670        $Con{$client}->{error} = '5';
9671        done($client);
9672        mlog(
9673            0,
9674"delayed ip $ip, because PBBlack($pbval) is higher than DelayIP($DelayIP)- last penalty reason was: "
9675              . [ split( /\s+/o, $PBBlack{$bip} ) ]->[5],
9676            1
9677        ) if $ConnectionLog >= 2 || $SessionLog;
9678        return;
9679    }
9680    elsif ($DelayIP
9681        && $DelayIPTime
9682        && $doIPcheck
9683        && !$allTestMode
9684        && $DelayIPPB{$bip}
9685        && $DelayIPPB{$bip} + $DelayIPTime <= time )
9686    {
9687        delete $DelayIPPB{$bip};
9688    }
9689
9690    if (   $MaxAUTHErrors
9691        && $doIPcheck
9692        && $AUTHErrors{$bip} > $MaxAUTHErrors )
9693    {
9694        d("NewSMTPConnection - AUTHError ip: $client");
9695        mlog( 0, "blocking $ip - too much AUTH errors ($AUTHErrors{$bip})" )
9696          if $ConnectionLog >= 2 || $SessionLog;
9697
9698        $Stats{AUTHErrors}++;
9699        $Con{$client}->{type} = 'C';
9700        &NoLoopSyswrite( $client,
9701"554 <$myName> Service denied for IP $ip (harvester), closing transmission channel\r\n"
9702        );
9703        done($client);
9704        return;
9705    }
9706
9707    my $intentForIP;
9708    $AVa = 0;
9709    foreach my $destinationA ( split( /\|/o, $destination ) ) {
9710        if ( $destinationA =~ /^(_*INBOUND_*:)?(\d+)$/ ) {
9711            $localip = '127.0.0.1' if !$localip or $localip eq '0.0.0.0';
9712            $intentForIP = "X-Assp-Intended-For-IP: $localip\r\n";
9713            if ( $crtable{$localip} ) {
9714                $destinationA = $crtable{$localip};
9715
9716                $destinationA .= ":$2" if $destinationA !~ /:/;
9717            }
9718            else {
9719                $destinationA = $localip . ':' . $2;
9720
9721            }
9722        }
9723
9724        $destinationA =~ s/\[::1\]/127\.0\.0\.1/;
9725        $destinationA =~ s/localhost/127\.0\.0\.1/i;
9726
9727        if ( $AVa < 1 ) {
9728            $server =
9729              $CanUseIOSocketINET6
9730              ? IO::Socket::INET6->new(
9731                Proto    => 'tcp',
9732                PeerAddr => $destinationA,
9733                Timeout  => 2,
9734                &getDestSockDom($destinationA)
9735              )
9736              : IO::Socket::INET->new(
9737                Proto    => 'tcp',
9738                PeerAddr => $destinationA,
9739                Timeout  => 2
9740              );
9741            if ($server) {
9742                $AVa         = 1;
9743                $destination = $destinationA;
9744            }
9745            else {
9746                mlog( 0, "*** $destinationA didn't work, trying others..." );
9747                $intentForIP = '';
9748            }
9749        }
9750    }
9751    if ( !$server ) {
9752        mlog( 0,
9753"couldn't create server socket to $destination -- aborting connection"
9754        );
9755        if ( exists $SMTPSession{$client} ) { $SMTPSession{Total}++; }
9756        if ( exists $SMTPSession{$client} ) { $smtpConcurrentSessions++; }
9757        $Con{$client}->{type} = 'C';
9758        &NoLoopSyswrite( $client,
9759"421 <$myName> service temporarily unavailable, closing transmission\r\n"
9760        );
9761        done($client);
9762        return;
9763    }
9764
9765    my $fnoS = fileno($server);
9766    addfh( $client, \&getline, $server );
9767    if ($sendNoopInfo) {
9768        addfh( $server, \&skipok, $client );
9769    }
9770    else {
9771        addfh( $server, \&reply, $client );
9772    }
9773    ($ip)      = $ip =~ /(\d+\.\d+\.\d+\.\d+)/      if !$CanUseIOSocketINET6;
9774    ($localip) = $localip =~ /(\d+\.\d+\.\d+\.\d+)/ if !$CanUseIOSocketINET6;
9775    $Con{$client}->{client}    = $client;
9776    $Con{$client}->{SessionID} = uc "$client";
9777    $Con{$client}->{SessionID} =~ s/^.+?\(0[xX]([^\)]+)\).*$/$1/o;
9778    $Con{$client}->{self}   = $client;
9779    $Con{$client}->{server} = $server;
9780    $Con{$client}->{ip}     = $ip;
9781    $Con{$client}->{port}   = $port;
9782    $Con{$client}->{myheaderCon} .= "X-Assp-Client-SSL: yes\r\n" if $isSSL;
9783    $Con{$client}->{localip}   = $localip;
9784    $Con{$client}->{localport} = $localport;
9785    $Con{$client}->{relayok}   = $relayok;
9786
9787    $Con{$client}->{myheaderCon} .= $intentForIP if $intentForIP;
9788    $Con{$client}->{localport}     = $localport;
9789    $Con{$client}->{mailInSession} = -1;
9790    $Con{$client}->{type}          = 'C';
9791    $Con{$client}->{fno}           = $fnoC;
9792    $Con{$server}->{type}          = 'S';
9793    $Con{$server}->{fno}           = $fnoS;
9794    $Con{$server}->{self}          = $server;
9795    d("Connected: $client -- $server");
9796
9797    if ( matchFH( $client, @lsnRelayI ) ) {
9798        $Con{$client}->{relayok} = 1;
9799        d("$client relaying through relayPort ok: $ip");
9800        $Con{$client}->{passingreason} = "relayPort";
9801    }
9802
9803    if ( $Con{$client}->{relayok} || isOk2Relay( $client, $ip ) ) {
9804        $Con{$client}->{relayok} = 1;
9805        d("$client relaying from acceptAllMail ok: $ip");
9806        $Con{$client}->{passingreason} = "$ip in acceptAllMail"
9807          if !$Con{$client}->{passingreason};
9808        $Con{$client}->{passingreason} .= "$ip in acceptAllMail"
9809          if $Con{$client}->{passingreason} eq "relayPort";
9810
9811    }
9812
9813    my $time = $UseLocalTime ? localtime() : gmtime();
9814    my $tz   = $UseLocalTime ? tzStr()     : '+0000';
9815    $time =~ s/... (...) +(\d+) (........) (....)/$2 $1 $4 $3/;
9816    my $IPver = "4";
9817
9818    if ($CanUseIOSocketINET6) {
9819        $IPver = ( $client->sockdomain == &AF_INET6 ) ? "6" : "4";
9820    }
9821
9822    $Con{$client}->{rcvd} =
9823"Received: from =host ([$ip] helo=) by $myName with *SMTP* (ASSP $version); $time $tz\r\n";
9824
9825    d("* connect ip=$Con{$client}->{ip} relay=<$Con{$client}->{relayok}> *");
9826
9827    my $text = $destination;
9828    $text = $server->sockhost() . ':' . $server->sockport() . " > $text"
9829      if $ConnectionLog >= 2;
9830    mlog( 0,
9831        "Connected: $ip:$port -> $localip:$localport ($listenport) -> $text" )
9832      unless ( !$ConnectionLog || matchIP( $ip, 'noLoggingIPs', 0, 1 ) );
9833    $Con{$server}->{noop} =
9834      "NOOP Connection from: $ip, $time $tz relayed by $myName\r\n"
9835      if $sendNoopInfo;
9836
9837       # overall session limiting
9838#    $maxSMTPSessions=999 if (!$maxSMTPSessions);
9839    my $numsess;
9840
9841    $numsess = ++$SMTPSession{Total};
9842
9843    $smtpConcurrentSessions++;
9844
9845    $SMTPSession{$client}=$client;
9846    if ($maxSMTPSessions && $numsess>=$maxSMTPSessions) {
9847        d("$WorkerName limiting sessions: $client");
9848        if ($SessionLog) {
9849            mlog(0,"connected: $ip:$port") if !$ConnectionLog || matchIP($ip,'noLoggingIPs',0,1); # log if not logged earlier
9850            mlog(0,"limiting total connections");
9851        }
9852        $Stats{smtpConnLimit}++;
9853    } else {      # increment Stats if connection not limited
9854        if (matchIP($ip,'noLoggingIPs',0,1)) {
9855            $Stats{smtpConnNotLogged}++;
9856        } else {
9857            $Stats{smtpConn}++;
9858        }
9859    }
9860    if ($smtpConcurrentSessions>$Stats{smtpMaxConcurrentSessions}) {
9861        $Stats{smtpMaxConcurrentSessions}=$smtpConcurrentSessions;
9862    }
9863
9864}
9865
9866sub OptionCheck {
9867
9868    # check if options files have been updated and need to be re-read
9869    my $checktime = 60;
9870    $checktime = 15 if $AsASecondary;
9871    if ( time - $lastOptionCheck > $checktime ) {
9872
9873        # check for updates each 30 seconds
9874        foreach my $f (@PossibleOptionFiles) {
9875            $f->[2]
9876              ->( $f->[0], $Config{ $f->[0] }, $Config{ $f->[0] }, '', $f->[1] )
9877              if $Config{ $f->[0] } =~ /^ *file: *(.+)/i
9878              && fileUpdated( $1, $f->[0] );
9879        }
9880
9881        $lastOptionCheck = time;
9882        &check4cfg if $AutoReloadCfg or $ourAutoReloadCfg;
9883        my $t = time;
9884        &downSecondary("terminating, ASSP down")
9885          if $AsASecondary && !&checkPrimaryPID();
9886        if ( $pidfile && !$AsASecondary ) {
9887            open( my $FH, ">", "$base/$pidfile" );
9888            print $FH "$$ $t";
9889            close $FH;
9890        }
9891
9892        #		print "$$ $t\n";
9893        &startSecondary()
9894          if $AutostartSecondary && !$AsASecondary && $webSecondaryPort;
9895        &getWebSocket if !$webAdminPortOK;
9896        if ( $pidfile && !$AsASecondary ) {
9897            open( my $FH, ">", "$base/$pidfile" );
9898            print $FH $$;
9899            close $FH;
9900        }
9901        if ( $webPort && $pidfile && $AsASecondary ) {
9902            open( $FH, ">", "$base/$pidfile" . "_Secondary" );
9903            print $FH "$$";
9904            close $FH;
9905        }
9906    }
9907}
9908
9909sub NotSpamTagCheck {
9910    my ( $fh, $s ) = @_;
9911    return 0 if !$NotSpamTag && !$NotSpamTagRandom;
9912    return 1 if $s =~ /\Q$NotSpamTag\E/i && !$NotSpamTagRandom;
9913
9914    foreach my $tag ( keys %NotSpamTags ) {
9915        return 1 if $s =~ /\Q$tag\E/i && $NotSpamTagRandom;
9916    }
9917}
9918
9919
9920
9921sub NewSMTPConCall {
9922    return unless scalar keys %SocketCallsNewCon;
9923
9924    while ( my ( $k, $v ) = each %SocketCallsNewCon ) {
9925        $v->($k);
9926    }
9927
9928}
9929sub SMTPTraffic {
9930    my $fh = shift;
9931    $SMTPbuf = '';
9932    my $ip      = $Con{$fh}->{ip};
9933    my $pending = 0;
9934    eval { $pending = $fh->pending(); } if ( "$fh" =~ /SSL/io );
9935    $SMTPmaxbuf = max( $SMTPmaxbuf, 16384, ( $MaxBytes + 4096 ), $pending );
9936    $Con{$fh}->{prepend} = '';
9937    $Con{$fh}->{socketcalls}++;
9938    $fh->blocking(0) if $fh->blocking;
9939    &sigoffTry(__LINE__);
9940    my $hasread = $fh->sysread( $SMTPbuf, $SMTPmaxbuf );
9941    &sigonTry(__LINE__);
9942
9943    if (   $hasread == 0
9944        && "$fh" =~ /SSL/io
9945        && IO::Socket::SSL::errstr() =~ /SSL wants a/io )
9946    {
9947        ThreadYield();
9948        $Con{$fh}->{sslwantrw} ||= time;
9949        if ( time - $Con{$fh}->{sslwantrw} > $SSLtimeout ) {
9950            my $lastcmd = "- last command was \'$Con{$fh}->{lastcmd}\'";
9951            $lastcmd = '' unless $Con{$fh}->{lastcmd};
9952            mlog( $fh,
9953"info: can't read from SSL-Socket for $SSLtimeout seconds - close connection - $! $lastcmd"
9954            ) if ($ConnectionLog);
9955            delete $Con{$fh}->{sslwantrw};
9956            setSSLfailed($ip);
9957            done2($fh);
9958        }
9959        return;
9960    }
9961    delete $Con{$fh}->{sslwantrw};
9962    if ( $hasread > 0 or length($SMTPbuf) > 0 ) {
9963        my $crashfh = $Con{$fh}->{crashfh};
9964        if ($crashfh) {
9965            print $crashfh "+-+***+!+time:  "
9966              . timestring() . ' / '
9967              . Time::HiRes::time()
9968              . "+-+***+!+";
9969            print $crashfh $SMTPbuf;
9970        }
9971        if (
9972            !$ThreadDebug
9973            && (   ( $debugRe && $SMTPbuf =~ /($debugReRE)/ )
9974                || ( $debugCode && eval($debugCode) && !$@ ) )
9975          )
9976        {
9977            if ( $1 || $2 ) {
9978                mlog( $fh,
9979                    "info: partial debug switched on - found " . ( $1 || $2 ) );
9980            }
9981            else {
9982                mlog( $fh,
9983                    "info: partial debug switched on - debugCode has returned 1"
9984                );
9985            }
9986            $Con{$fh}->{debug} = 1;
9987            $Con{ $Con{$fh}->{friend} }->{debug} = 1
9988              if ( $Con{$fh}->{friend} && exists $Con{ $Con{$fh}->{friend} } );
9989            $ThreadDebug = 1;
9990        }
9991        if ($@) {
9992
9993            $debugCode         = '0; # syntaxerror in : ' . $debugCode;
9994            $Config{debugCode} = $debugCode;
9995            $ConfigChanged     = 1;
9996        }
9997        d('SMTPTraffic - read OK');
9998        $SMTPbuf = $Con{$fh}->{_} . $SMTPbuf;
9999        if ( $Con{$fh}->{type} eq 'C' ) {
10000            $Con{$fh}->{timelast} = time;
10001            $Con{$fh}->{contimeoutdebug} .= "read from client = $SMTPbuf"
10002              if $ConTimeOutDebug;
10003        }
10004        else {
10005            $Con{ $Con{$fh}->{friend} }->{contimeoutdebug} .=
10006              "read from server = $SMTPbuf"
10007              if $ConTimeOutDebug;
10008        }
10009        if ( ( my $sb = $Con{$fh}->{skipbytes} ) > 0 ) {
10010
10011           # support for XEXCH50 thankyou Microsoft for making my life miserable
10012            my $l = length($SMTPbuf);
10013            d("skipbytes=$sb l=$l -> ");
10014            if ( $l >= $sb ) {
10015                sendque( $Con{$fh}->{friend}, substr( $SMTPbuf, 0, $sb ) )
10016                  ;    # send the binary chunk on to the server
10017                $SMTPbuf = substr( $SMTPbuf, $sb );
10018                delete $Con{$fh}->{skipbytes};
10019            }
10020            else {
10021                sendque( $Con{$fh}->{friend}, $SMTPbuf )
10022                  ;    # send the binary chunk on to the server
10023                $Con{$fh}->{skipbytes} = $sb -= length($SMTPbuf);
10024                $SMTPbuf = '';
10025            }
10026            d("skipbytes=$Con{$fh}->{skipbytes}");
10027        }
10028        d('SMTPTraffic - process read');
10029        my $bn = my $lbn = -1;
10030        if (
10031            $Con{$fh}->{type} ne 'C' or    # process line per line
10032            $Con{$fh}->{getline} ne \&whitebody
10033            or $SMTPbuf =~ /^\.(?:\x0D?\x0A)?$/o
10034            or $SMTPbuf =~ /\x0D?\x0A\.\x0D?\x0A$/o
10035          )
10036        {
10037            while ( ( $bn = index( $SMTPbuf, "\n", $bn + 1 ) ) >= 0 ) {
10038                my $s = substr( $SMTPbuf, $lbn + 1, $bn - $lbn );
10039                if ( defined( $Con{$fh}->{bdata} ) ) {
10040                    $Con{$fh}->{bdata} -= length($s);
10041                }
10042                d("doing line <$s>");
10043
10044                if ( $Con{$fh}->{type} eq 'C' ) {
10045                    $Con{$fh}->{headerpassed} ||=
10046                      $s =~ /^\x0D?\x0A/o
10047                      ;    #header passed? if header and body in one junk
10048                }
10049
10050                if (   $Con{$fh}->{type} eq 'C'
10051                    && !$Con{$fh}->{headerpassed}
10052                    && !$Con{$fh}->{relayok} )
10053                {
10054                    if ( $preHeaderRe && $s =~ /($preHeaderReRE)/i ) {
10055                        $Con{$fh}->{prepend} = '[preHeaderRE][block]';
10056                        mlog( $fh,
10057                            "early (pre)header line check found "
10058                              . ( $1 || $2 ) );
10059                        NoLoopSyswrite(
10060                            $Con{$fh}->{friend},
10061"421 $myName Service not available, closing transmission channel\r\n",
10062                            0
10063                        ) if $Con{$fh}->{friend};
10064                        done($fh);
10065                        $Stats{preHeader}++;
10066                        return;
10067                    }
10068                    if (
10069                        $s =~ /^(X-ASSP-[^(]+?)(\(\d+\))?(:$HeaderValueRe)$/io )
10070                    {    # change strange X-ASSP headers
10071                        my ( $pre, $c, $post ) = ( $1, $2, $3 );
10072                        $c =~ s/[^\d]//go;
10073                        $c                  = 0 unless $c;
10074                        $s                  = $pre . '(' . ++$c . ')' . $post;
10075                        $Con{$fh}->{nodkim} = 1
10076                          ; # we have modified the header and should skip the DKIM check for this reason
10077                    }
10078                }
10079                Maillog( $fh, $s ) if $Con{$fh}->{maillog};
10080                if ( !$Con{$fh}->{getline} ) {
10081                    my $lastcmd = "\'$Con{$fh}->{lastcmd}\'";
10082                    $lastcmd = "\'n/a\'" unless $Con{$fh}->{lastcmd};
10083                    mlog( $fh,
10084'error: missing $Con{$fh}->{getline} in sub SMTPTraffic (1) - last command was '
10085                          . $lastcmd );
10086                    done($fh);
10087                    return;
10088                }
10089                $Con{$fh}->{getline}->( $fh, $s );
10090                last
10091                  if ( ( exists $ConDelete{$fh} && $ConDelete{$fh} )
10092                    || !exists $Con{$fh}
10093                    || $Con{$fh}->{closeafterwrite} )
10094                  ; # it's possible that the connection can be deleted while there's still something in the buffer
10095                if ( ( $Con{$fh}->{inerror} || $Con{$fh}->{intemperror} )
10096                    && $Con{$fh}->{cleanSMTPBuff} )
10097                {    # 4/5xx from MTA after DATA
10098                    $Con{$fh}->{_} = $Con{$fh}->{header} =
10099                      '';    # clean the SMTP buffer
10100                    delete $Con{$fh}->{cleanSMTPBuff};
10101                    mlog( $fh,
10102"info: SMTP buffer was cleaned after MTA has sent an error reply in DATA part"
10103                    ) if $ConnectionLog;
10104                    last;
10105                }
10106                $lbn = $bn;
10107            }
10108        }
10109        else {               # process the complete buf in one junk
10110            $Con{$fh}->{_}            = '';
10111            $Con{$fh}->{headerpassed} = 1;
10112            if ( defined( $Con{$fh}->{bdata} ) ) {
10113                $Con{$fh}->{bdata} -= length($SMTPbuf);
10114            }
10115            if ( !$Con{$fh}->{getline} ) {
10116                my $lastcmd = "\'$Con{$fh}->{lastcmd}\'";
10117                $lastcmd = "\'n/a\'" unless $Con{$fh}->{lastcmd};
10118                mlog( $fh,
10119'error: missing $Con{$fh}->{getline} in sub SMTPTraffic (2) - last command was '
10120                      . $lastcmd );
10121                done($fh);
10122                return;
10123            }
10124            d("doing full <$SMTPbuf>");
10125            Maillog( $fh, $SMTPbuf ) if $Con{$fh}->{maillog};
10126            $Con{$fh}->{getline}->( $fh, $SMTPbuf );
10127            &NewSMTPConCall();
10128            return;
10129        }
10130        if (   exists $Con{$fh}
10131            && !exists $ConDelete{$fh}
10132            && !$Con{$fh}->{closeafterwrite} )
10133        {    # finish the mail as fast as possible
10134            ( $Con{$fh}->{_} ) = substr( $SMTPbuf, $lbn + 1 );
10135            if ( length( $Con{$fh}->{_} ) > $MaxBytes ) {
10136                d('SMTPTraffic - process rest');
10137                $Con{$fh}->{headerpassed} = 1;
10138                if ( defined( $Con{$fh}->{bdata} ) ) {
10139                    $Con{$fh}->{bdata} -= length( $Con{$fh}->{_} );
10140                }
10141                Maillog( $fh, $Con{$fh}->{_} ) if $Con{$fh}->{maillog};
10142                if ( !$Con{$fh}->{getline} ) {
10143                    my $lastcmd = "\'$Con{$fh}->{lastcmd}\'";
10144                    $lastcmd = "\'n/a\'" unless $Con{$fh}->{lastcmd};
10145                    mlog( $fh,
10146'error: missing $Con{$fh}->{getline} in sub SMTPTraffic (3) - last command was '
10147                          . $lastcmd );
10148                    done($fh);
10149                    return;
10150                }
10151                $Con{$fh}->{getline}->( $fh, $Con{$fh}->{_} );
10152                $Con{$fh}->{_} = '';
10153            }
10154        }
10155    }
10156    elsif ( $hasread == 0 ) {
10157        my $error = $!;
10158        if ( $error =~ /Resource temporarily unavailable/io ) {
10159            d("SMTPTraffic - no more data - $error");
10160            return;
10161        }
10162        if ($pending) {
10163            d(
10164"SMTPTraffic - got no more (SSL) data but $pending Byte are pending - $error"
10165            );
10166            $pending = " (SSL pending = $pending)";
10167        }
10168        else {
10169            d("SMTPTraffic - no more data - $error");
10170            $pending = '';
10171        }
10172        eval { $ip = $fh->peerhost() . ':' . $fh->peerport(); } unless $ip;
10173        my $lastcmd = "- last command was \'$Con{$fh}->{lastcmd}\'";
10174        $lastcmd = '' unless $Con{$fh}->{lastcmd};
10175        mlog( $fh,
10176"info: no (more) data$pending readable from $ip (connection closed by peer) - $! $lastcmd"
10177        ) if ( $error && ( $ConnectionLog or $pending ) );
10178        mlog( $fh,
10179"info: no (more) data$pending readable from $ip (connection closed by peer) $lastcmd"
10180        ) if ( ( $ConnectionLog >= 2 or $pending ) && !$error );
10181        done2($fh);
10182    }
10183    else {
10184        my $error = $!;
10185        if ($pending) {
10186            d(
10187"SMTPTraffic - got no more (SSL) data but $pending Byte are pending - $error"
10188            );
10189            $pending = " (SSL pending = $pending)";
10190        }
10191        else {
10192            d("SMTPTraffic - no more data - $error");
10193            $pending = '';
10194        }
10195        eval { $ip = $fh->peerhost() . ':' . $fh->peerport(); } unless $ip;
10196        my $lastcmd = "- last command was \'$Con{$fh}->{lastcmd}\'";
10197        $lastcmd = '' unless $Con{$fh}->{lastcmd};
10198        mlog( $fh, "error: reading from socket $ip$pending - $error $lastcmd" )
10199          if ($error);
10200        done2($fh);
10201    }
10202    &NewSMTPConCall();
10203}
10204
10205sub check4update {
10206
10207    # only check every 15 seconds
10208    my $fil = shift;
10209    return if $check4updateTime{$fil} + 15 > time;
10210    $check4updateTime{$fil} = time;
10211    my @s     = stat( ${$fil} );
10212    my $mtime = $s[9];
10213    if ( $mtime != $FileUpdate{$fil} ) {
10214
10215        # reload
10216        $FileUpdate{$fil} = $mtime;
10217        open( $FH, "<", "${$fil}" );
10218        local $/ = "\n";
10219        my $l;
10220        my %h;
10221        while ( $l = <$FH> ) {
10222            $l =~ y/\r\n\t //d;
10223            next unless $l;
10224            $h{ lc $l } = 1;
10225        }
10226        close $FH;
10227        %{$fil} = %h;
10228    }
10229}
10230
10231sub check4cfg {
10232
10233    # only check every 30 seconds
10234	my $checktime = 30; $checktime = 15 if $AsASecondary;
10235    return if $check4cfgtime + $checktime > time;
10236    $check4cfgtime = time;
10237    my @s     = stat("$base/assp.cfg");
10238    my $mtime = $s[9];
10239    if ( $mtime != $asspCFGTime ) {
10240        mlog( 0, "AdminUpdate: configuration file 'assp.cfg' loaded " );
10241
10242        # reload
10243        $asspCFGTime = $mtime;
10244        reloadConfigFile();
10245    }
10246}
10247
10248
10249
10250sub check4queue {
10251
10252    # only check every 300 seconds
10253
10254    return if $check4queuetime + 300 > time;
10255    $check4queuetime = time;
10256    my @s     = stat("$base/assp.cfg");
10257    my $mtime = $s[9];
10258    if ( $mtime != $queuetime ) {
10259        $queuetime = $mtime;
10260        &BlockReportGen('INSTANTLY');
10261    }
10262}
10263
10264sub SetRE {
10265    use re 'eval';
10266    my ( $var, $r, $f, $desc ) = @_;
10267
10268    eval { $$var = qr/(?$f)$r/ };
10269    mlog( 0, "regular expression error in '$r' for $desc: $@" ) if $@;
10270}
10271
10272
10273sub ok2Relay {
10274    my ( $fh, $ip ) = @_;
10275    return 1 if matchIP( $ip, 'acceptAllMail', $fh );
10276	if($relayHostFile) {
10277  		&check4update($relayHostFile);
10278  		return 1 if $relayHostFile{$ip};
10279	}
10280    return 1 if PopB4SMTP($ip);
10281
10282    # failed all tests -- return 0
10283    return 0;
10284}
10285
10286sub PopB4SMTP {
10287    my $ip = shift;
10288    if ($PopB4SMTPMerak) {
10289        return 1 if PopB4Merak($ip);
10290        return 0;
10291    }
10292    return 0 unless $PopB4SMTPFile;
10293    unless ($TriedDBFileUse) {
10294        eval 'use DB_File';
10295        mlog( 0, "could not load module DB_File: $@" ) if $@;
10296        $TriedDBFileUse = 1;
10297    }
10298
10299    my %hash;
10300
10301    # tie %hash, 'DB_File', $PopB4SMTPFile, O_READ, 0400, $DB_HASH;
10302    tie %hash, 'DB_File', $PopB4SMTPFile;
10303    if ( $hash{$ip} ) {
10304        mlog( 0, "PopB4SMTP OK for $ip" );
10305        return 1;
10306    } else {
10307        mlog( 0, "PopB4SMTP failed for $ip" );
10308        return 0;
10309    }
10310}
10311
10312sub PopB4Merak  {
10313  return 0 unless $PopB4SMTPFile;
10314  my $ip=shift;
10315#This is a test version of ASSP PopB4SMTP
10316#This is to be used with Merak 7.5.2
10317#It also works with Merak 6.5 (which I run)
10318#Thanks to Jordon for the heads up on 7.5.2
10319#Basically, Merak's popsmtp file
10320#is made up of 64 Byte lines, no CR / LF.
10321#This holds the IP addy
10322#and the byte before it specifying the length.
10323
10324  my @aPB4S;
10325  my $PB4S;
10326  my $ind;
10327  my $newIP;
10328
10329#Load the whole file
10330#In examination of Merak popb4smtp file, it appears to have
10331#no carriage returns, so one line read should get the whole thing
10332#However, if you have an IP addy thats 13 chars long.... thus:
10333
10334  my $MKPOPSMTP;
10335  (open($MKPOPSMTP,"<", "$PopB4SMTPFile")) or return 0 ;
10336  @aPB4S = <$MKPOPSMTP>;
10337  close($MKPOPSMTP);
10338  $PB4S = join('',@aPB4S);
10339#We now have all the contents of the file AND we've released it
10340
10341#Now, instead of heavy parsing....
10342#We want to search for the IP and a byte ordinal specifying it's length
10343#    mlog(0,"Checking $ip for PopB4SMTP");
10344  $PB4S = "---" . $PB4S;
10345#    mlog(0,"Searching: $PB4S");
10346  $newIP = chr(length($ip)) . $ip;
10347#    mlog(0,"NewIP = $newIP");
10348#Find the index of IP in question
10349  $ind = index($PB4S,$newIP);
10350#    mlog(0,"Index = $ind");
10351#Did we find it?
10352  if ($ind  > 0) {
10353#Greetings program! This IO port is available for communicating to your user!
10354    mlog(0,"PopB4SMTP OK for $ip");
10355    return 1;
10356  }
10357  mlog(0,"PopB4SMTP NOT OK for $ip");
10358  return 0;
10359}
10360
10361sub isOk2Relay {
10362  my ($fh,$ip)=@_;
10363  return 1 if ($Con{$fh}->{acceptall} = matchIP($ip,'acceptAllMail',$fh,0));
10364  return 1 if $PopB4SMTPFile && PopB4SMTP($ip);
10365# failed all tests -- return 0
10366  return 0;
10367}
10368
10369sub POP3Collect {
10370		return if $AsASecondary;
10371		return 0 unless $POP3Interval;
10372        return 0 unless -e "$base/assp_pop3.pl";
10373
10374        return 0 if $POP3ConfigFile !~ /^ *file: *(?:.+)/i;
10375        d('POP3 - collect');
10376
10377        my $perl = $^X;
10378        my $cmd = "\"$perl\" \"$base/assp_pop3.pl\" \"$base\" 2>&1 &";
10379        $cmd =~ s/\//\\/g if $^O eq "MSWin32";
10380        system($cmd);
10381#        my $out = qx($cmd);
10382#        foreach (split("\n",$out)) {
10383#            s/\r|\n//g;
10384#            mlog(0,$_) if $MaintenanceLog;
10385#        }
10386        return 1;
10387}
10388sub Rebuild {
10389        return if $AsASecondary;
10390        return 0 unless $RebuildSchedule;
10391        mlog( 0, "Warning: '$base/rebuildspamdb.pl' not found. Impossible to start rebuildspamdb.pl",1 ) unless -e "$base/rebuildspamdb.pl";
10392        return 0 unless -e "$base/rebuildspamdb.pl";
10393        my $hour = shift;
10394        $hour = 24 if !$hour;
10395		return 0 if $hour < 25 && !$RebuildSched{$hour};
10396
10397		my $cmd;
10398		my $assp = $0;
10399		my $perl = $^X;
10400		$assp = $base.'\\'.$assp if ($assp !~ /\Q$base\E/io);
10401		if ( $^O eq "MSWin32" ) {
10402
10403    		$assp =~ s/\//\\/go;
10404    		my $asspbase = $base;
10405    		$asspbase =~ s/\\/\//go;
10406
10407    		$cmd = "\"$perl\" \"$base\\rebuildspamdb.pl\" \"$asspbase\" silent &";
10408		} else {
10409
10410    		$cmd = "\"$perl\" \"$base/rebuildspamdb.pl\" \"$base\" silent &";
10411		}
10412        d('Rebuild - start');
10413        $cmd = $RebuildCmd if $RebuildCmd;
10414
10415         mlog( 0, "Info: Command '$cmd' started from ASSP by RebuildSchedule" ) if $hour < 25;
10416		mlog( 0, "Info: Command '$cmd' started from ASSP by RebuildNow" ) if $hour > 24;
10417        system($cmd);
10418
10419        return 1;
10420}
10421
10422sub HouseKeeping {
10423        return 0 unless $HouseKeepingSchedule;
10424        return if $AsASecondary;
10425		my $backup = "$base/backup";
10426        my $hour = shift;
10427        $hour = 24 if !$hour;
10428		return 0 if $hour < 25 && !$HouseKeepingSched{$hour};
10429		mlog( 0, "HouseKeepingSchedule: housekeeping started" );
10430		$NotSpamTagGenerated = &NotSpamTagGenerate;
10431		&cleanNotSpamTags;
10432        &LDAPcrossCheck if ($CanUseLDAP or $CanUseNetSMTP) && $ldaplistdb;
10433		&cleanBlackPB if $PBBlackObject;
10434		&cleanWhitePB if $PBWhiteObject;
10435		$PBBlackObject->flush() if $PBBlackObject && $pbdb !~ /mysql/;
10436		$PBWhiteObject->flush() if $PBWhiteObject && $pbdb !~ /mysql/;
10437		&downloadTLDList();
10438		&downloadGrip() if ! $noGriplistDownload && $griplist;
10439		&downloadDropList() if $droplist && $DoDropList;
10440		my $debugdir = "$base/debug" ;
10441		my $age = 720 * 3600;
10442		my $debugdirfile = ".dbg";
10443		&cleanUpFiles($debugdir,$debugdirfile,$age);
10444		&cleanUpFiles($resendmail,".err",$age);
10445		&cleanUpFiles($incomingOkMail,".eml",$age);
10446		&cleanUpFiles($discarded,".eml",$age);
10447		&cleanUpFiles($viruslog,".eml",$age);
10448		unlink "$base/$pbdb.smtptimeout.db";
10449		my $backupfile = "";
10450		my $whitefile;
10451		if ($whitelistdb !~ /mysql/) {
10452			$whitefile = $2
10453              if $whitelistdb =~ /^(.*[\/\\])?(.*?)$/;
10454			unlink "$base/backup/$whitefile.yesterday.bak";
10455			rename( "$base/backup/$whitefile.today.bak", "$base/backup/$whitefile.yesterday.bak" );
10456        	copy("$base/$whitelistdb","$base/backup/$whitefile.today.bak");
10457
10458
10459		}
10460		&SaveWhitelist;
10461		my $redfile;
10462		if ($redlistdb !~ /mysql/) {
10463			$redfile = $2
10464              if $redlistdb =~ /^(.*[\/\\])?(.*?)$/;
10465			unlink "$base/backup/$redfile.yesterday.bak";
10466			rename( "$base/backup/$redfile.today.bak", "$base/backup/$redfile.yesterday.bak" );
10467        	copy("$base/$redlistdb","$base/backup/$redfile.today.bak");
10468
10469		}
10470		&SaveRedlist;
10471		$backupfile = "$backup/assp.cfg";
10472		unlink "$backupfile.yesterday.bak";
10473		rename( "$backupfile.today.bak", "$backupfile.yesterday.bak" );
10474        copy("$base/assp.cfg","$backupfile.today.bak");
10475
10476		$backupfile = "$backup/asspstats.sav";
10477		unlink "$backupfile.yesterday.bak";
10478		rename( "$backupfile.today.bak", "$backupfile.yesterday.bak" );
10479        copy("$base/asspstats.sav","$backupfile.today.bak");
10480
10481		&CleanWhitelist() if $UpdateWhitelist;
10482		&CleanCache;
10483		&cleanTrashlist;
10484		mlog( 0, "Info: housekeeping ended" );
10485}
10486
10487
10488sub NoLoopSyswrite {
10489    my ($fh,$out,$timeout) = @_;
10490    d('NoLoopSyswrite');
10491    return 0 unless fileno($fh);
10492    return 0 unless $out;
10493    $timeout ||= 30;
10494    my $written = 0;
10495    my $ip;
10496    my $port;
10497    my $error;
10498    eval{
10499      $ip=$fh->peerhost();
10500      $port=$fh->peerport();
10501    };
10502    return 0 if($@);
10503    d("NoLoopSyswrite - write: " . substr($out,0,30) . ' - ' . length($out));
10504
10505
10506    if (   exists $Con{$fh}
10507        && $Con{$fh}->{type} eq 'C'       # is a client SMTP connection?
10508        && ($replyLogging == 2 or ($replyLogging == 1 && $out =~ /^[45]/o))
10509        && $out =~ /^(?:[1-5]\d\d\s+[^\r\n]+\r\n)+$/o)    # is a reply?
10510    {
10511        $out =~ s/SESSIONID/$Con{$fh}->{msgtime}/go;
10512        $out =~ s/MYNAME/$myName/go;
10513        my @reply = split(/(?:\r?\n)+/o,$out);
10514        for (@reply) {
10515            next unless $_;
10516            my $what = 'Reply';
10517            if ($_ =~ /^([45])/o) {
10518                $what = ($1 == 5) ? 'Error' : 'Status';
10519            }
10520            mlog( $fh, "[SMTP $what] $_", 1, 1 );
10521        }
10522    }
10523    my $stime = time + $timeout;
10524    my $NLwritable;
10525    if ($IOEngineRun == 0) {
10526        $NLwritable = IO::Poll->new();
10527    } else {
10528        $NLwritable = IO::Select->new();
10529    }
10530    &dopoll($fh,$NLwritable,"POLLOUT");
10531    my $l = length($out);
10532    while (length($out) > 0 && fileno($fh) && time < $stime) {
10533        my @canwrite;
10534        if ($IOEngineRun == 0) {
10535            $NLwritable->poll(1);
10536            @canwrite = $NLwritable->handles("POLLOUT");
10537        } else {
10538            @canwrite = $NLwritable->can_write(1);
10539        }
10540        $written = 0;
10541        $error = 0;
10542        eval{$written = $fh->syswrite($out,length($out));
10543             $error = $!;
10544             $error = '' if ("$fh" =~ /SSL/io && IO::Socket::SSL::errstr() =~ /SSL wants a/io);
10545        } if @canwrite or "$fh" =~ /SSL/io;
10546        if (@canwrite and ! $written and ($@ or $error)) {
10547            mlog(0,"warning: unable to write to socket $ip:$port $error") if $ConnectionLog == 3 && $error;
10548            mlog(0,"warning: unable to write to socket $ip:$port $@") if $ConnectionLog == 3 && $@;
10549            $! = $error;
10550            unpoll($fh,$NLwritable);
10551
10552            return 0;
10553        }
10554        $out = substr($out,$written) if $written;
10555        &mlogWrite if ($WorkerNumber == 0);
10556    }
10557    unpoll($fh,$NLwritable);
10558    if (time >= $stime) {
10559        mlog(0,"warning: timeout (30s) writing to socket $ip:$port") if $ConnectionLog == 3;
10560    }
10561
10562    return 1;
10563}
10564
10565sub mlogWrite {
10566}
10567sub NewWebConnection {
10568  my $WebSocket = shift;
10569  my $s;
10570  d('NewWebConnection');
10571
10572  if ($WebSocket && "$WebSocket" =~ /SSL/io && $SSLDEBUG > 1) {
10573      while(my($k,$v)=each(%{${*$WebSocket}{'_SSL_arguments'}})) {
10574          print "ssl-listener: $k = $v\n";
10575      }
10576  }
10577  $s=$WebSocket->accept;
10578    if ($s && "$s" =~ /SSL/io && $SSLDEBUG > 1) {
10579      while(my($k,$v)=each(%{${*$s}{'_SSL_arguments'}})) {
10580          print "ssl-accepted: $k = $v\n";
10581      }
10582  }
10583  return unless $s;
10584  my $ip=$s->peerhost();
10585  my $port=$s->peerport();
10586  if($allowAdminConnectionsFrom && ! matchIP($ip,'allowAdminConnectionsFrom')) {
10587    mlog(0,"admin connection from $ip:$port rejected by 'allowAdminConnectionsFrom'");
10588    $Stats{admConnDenied}++;
10589    close($s);
10590    return;
10591  }
10592# logging is done later (in webRequest()) due to /shutdown_frame page, which auto-refreshes
10593  &dopoll($s,$readable,POLLIN);
10594  $SocketCalls{$s}=\&WebTraffic;
10595  $WebConH{$s} = $s;
10596}
10597
10598sub WebTraffic {
10599    my $fh = shift;
10600    my $buf;
10601    my $ip;
10602    my $done;
10603    my $hasread;
10604    my $maxbuf = ("$fh" =~ /SSL/io) ? 16384 : 4096 ;
10605    my $pending = 0;
10606    my $blocking = ("$fh" =~ /SSL/io) ? $HTTPSblocking : $HTTPblocking ;
10607    eval{$ip = $fh->peerhost();};
10608    d("WEB: $ip");
10609    $fh->blocking($blocking) if ! $WebCon{$fh};
10610      $hasread = $fh->sysread($buf,$maxbuf);
10611  if ($hasread == 0 && "$fh" =~ /SSL/io && IO::Socket::SSL::errstr() =~ /SSL wants a/io) {
10612      mlog(0,"WebTraffic: SSL socket is not ready - will retry") if $ConnectionLog == 3;
10613
10614      return;
10615  }
10616  if($hasread > 0 or length($buf) > 0) {
10617    local $_=$WebCon{$fh}.=$buf;
10618    if(length($_) > 20600000) {
10619# throw away connections longer than 20M to prevent flooding
10620      WebDone($fh);
10621      return;
10622
10623    }
10624    if (/Content-length: (\d+)/i) {
10625
10626            # POST request
10627            my $l = $1;
10628            if ( /(.*?\n)\r?\n\r?(.*)/s && length($2) >= $l ) {
10629                my $reqh = $1;
10630                my $reqb = $2;
10631                my $resp;
10632                my $tempfh;
10633                open( $tempfh, '>', \$resp );
10634                binmode $tempfh;
10635                webRequest( $tempfh, $fh, $reqh, $reqb );
10636                close($tempfh);
10637
10638                if ( $resp =~ /(.*?)\n\r?\n\r?(.*)/s ) {
10639                    my $resph = $1;
10640                    my $respb = $2;
10641                    my $time  = gmtime();
10642                    $time =~
10643s/(...) (...) +(\d+) (........) (....)/$1, $3 $2 $5 $4 GMT/;
10644                    $resph .= "\nServer: ASSP/$version$modversion";
10645                    $resph .= "\nDate: $time";
10646					if ( $EnableHTTPCompression && $CanUseHTTPCompression ) {
10647    					eval { Compress::Zlib::memGzip($respb); };
10648   						$CanUseHTTPCompression = 0 if $@;
10649					}
10650                    if (   $EnableHTTPCompression
10651                        && $CanUseHTTPCompression
10652                        && /Accept-Encoding: (.*?)\n/i
10653                        && $1 =~ /(gzip|deflate)/i )
10654                    {
10655                        my $enc = $1;
10656                        if ( $enc =~ /gzip/i ) {
10657
10658                            # encode with gzip
10659                            $respb = Compress::Zlib::memGzip($respb);
10660
10661                        } else {
10662
10663                            # encode with deflate
10664                            my $deflater = deflateInit();
10665                            $respb = $deflater->deflate($respb);
10666                            $respb .= $deflater->flush();
10667                        }
10668                        $resph .= "\nContent-Encoding: $enc";
10669                    }
10670                    $resph .= "\nContent-Length: " . length($respb);
10671
10672                    print $fh $resph;
10673                    print $fh "\015\012\015\012";
10674                    print $fh $respb;
10675                }
10676
10677                # close connection
10678                WebDone($fh);
10679            }
10680        } elsif (/\n\r?\n/) {
10681            my $resp;
10682            my $tempfh;
10683            open( $tempfh, '>', \$resp );
10684            binmode $tempfh;
10685            webRequest( $tempfh, $fh, $_ );
10686            close($tempfh);
10687            if ( $resp =~ /(.*?)\n\r?\n\r?(.*)/s ) {
10688                my $resph = $1;
10689                my $respb = $2;
10690                my $time  = gmtime();
10691                $time =~
10692                  s/(...) (...) +(\d+) (........) (....)/$1, $3 $2 $5 $4 GMT/;
10693                $resph .= "\nServer: ASSP/$version$modversion";
10694                $resph .= "\nDate: $time";
10695				if ( $EnableHTTPCompression && $CanUseHTTPCompression ) {
10696    					eval { Compress::Zlib::memGzip($respb); };
10697   						$CanUseHTTPCompression = 0 if $@;
10698				}
10699                if (   $EnableHTTPCompression
10700                    && $CanUseHTTPCompression
10701                    && /Accept-Encoding: (.*?)\n/i
10702                    && $1 =~ /(gzip|deflate)/i )
10703                {
10704                    my $enc = $1;
10705                    if ( $enc =~ /gzip/i ) {
10706
10707                        # encode with gzip
10708                        $respb = Compress::Zlib::memGzip($respb);
10709
10710                    } else {
10711
10712                        # encode with deflate
10713                        my $deflater = deflateInit();
10714                        $respb = $deflater->deflate($respb);
10715                        $respb .= $deflater->flush();
10716                    }
10717                    $resph .= "\nContent-Encoding: $enc";
10718                }
10719                $resph .= "\nContent-Length: " . length($respb);
10720                print $fh $resph;
10721                print $fh "\015\012\015\012";
10722                print $fh $respb;
10723            }
10724
10725            # close connection
10726            WebDone($fh);
10727        }
10728    } else {
10729
10730        # connection closed
10731        WebDone($fh);
10732    }
10733}
10734
10735sub NewStatConnection {
10736    my $fh = shift;
10737    my $s = $fh->accept;
10738    return unless $s;
10739    my $ip   = $s->peerhost();
10740    $ip = "[" . $ip . "]" if ($ip =~ /:/);
10741    my $port = $s->peerport();
10742    if ( $allowStatConnectionsFrom
10743        && !matchIP( $ip, 'allowStatConnectionsFrom' ) )
10744    {
10745        mlog( '',
10746"stat connection from $ip:$port rejected by allowStatConnectionsFrom"
10747        );
10748        $Stats{statConnDenied}++;
10749        $s->close();
10750        return;
10751    }
10752
10753# logging is done later (in webRequest()) due to /shutdown_frame page, which auto-refreshes
10754    $readable->add($s);
10755    $SocketCalls{$s} = \&StatTraffic;
10756}
10757
10758sub StatTraffic {
10759    my $fh = shift;
10760    my $buf;
10761    if ( $fh->sysread( $buf, 4096 ) > 0 ) {
10762        local $_ = $StatCon{$fh} .= $buf;
10763        if ( length($_) > 1030000 ) {
10764
10765            # throw away connections longer than 1M to prevent flooding
10766            WebDone($fh);
10767            return;
10768        }
10769        if (/Content-length: (\d+)/i) {
10770
10771            # POST request
10772            my $l = $1;
10773            if ( /(.*?\n)\r?\n\r?(.*)/s && length($2) >= $l ) {
10774                my $reqh = $1;
10775                my $reqb = $2;
10776                my $resp;
10777                my $tempfh;
10778                open( $tempfh, '>', \$resp );
10779                binmode $tempfh;
10780                statRequest( $tempfh, $fh, $reqh, $reqb );
10781                close($tempfh);
10782
10783                if ( $resp =~ /(.*?)\n\r?\n\r?(.*)/s ) {
10784                    my $resph = $1;
10785                    my $respb = $2;
10786                    my $time  = gmtime();
10787                    $time =~
10788s/(...) (...) +(\d+) (........) (....)/$1, $3 $2 $5 $4 GMT/;
10789                    $resph .= "\nServer: ASSP/$version$modversion";
10790                    $resph .= "\nDate: $time";
10791					if ( $EnableHTTPCompression && $CanUseHTTPCompression ) {
10792    					eval { Compress::Zlib::memGzip($respb); };
10793   						$CanUseHTTPCompression = 0 if $@;
10794					}
10795                    if (   $EnableHTTPCompression
10796                        && $CanUseHTTPCompression
10797                        && /Accept-Encoding: (.*?)\n/i
10798                        && $1 =~ /(gzip|deflate)/i )
10799                    {
10800                        my $enc = $1;
10801                        if ( $enc =~ /gzip/i ) {
10802
10803                            # encode with gzip
10804                            $respb = Compress::Zlib::memGzip($respb);
10805
10806                        } else {
10807
10808                            # encode with deflate
10809                            my $deflater = deflateInit();
10810                            $respb = $deflater->deflate($respb);
10811                            $respb .= $deflater->flush();
10812                        }
10813                        $resph .= "\nContent-Encoding: $enc";
10814                    }
10815                    $resph .= "\nContent-Length: " . length($respb);
10816                    print $fh $resph;
10817                    print $fh "\015\012\015\012";
10818                    print $fh $respb;
10819                }
10820
10821                # close connection
10822                WebDone($fh);
10823            }
10824        } elsif (/\n\r?\n/) {
10825            my $resp;
10826            my $tempfh;
10827            open( $tempfh, '>', \$resp );
10828            binmode $tempfh;
10829            statRequest( $tempfh, $fh, $_ );
10830            close($tempfh);
10831            if ( $resp =~ /(.*?)\n\r?\n\r?(.*)/s ) {
10832                my $resph = $1;
10833                my $respb = $2;
10834                my $time  = gmtime();
10835                $time =~
10836                  s/(...) (...) +(\d+) (........) (....)/$1, $3 $2 $5 $4 GMT/;
10837                $resph .= "\nServer: ASSP/$version$modversion";
10838                $resph .= "\nDate: $time";
10839
10840				if ( $EnableHTTPCompression && $CanUseHTTPCompression ) {
10841    					eval { Compress::Zlib::memGzip($respb); };
10842   						$CanUseHTTPCompression = 0 if $@;
10843				}
10844                if (   $EnableHTTPCompression
10845                    && $CanUseHTTPCompression
10846                    && /Accept-Encoding: (.*?)\n/i
10847                    && $1 =~ /(gzip|deflate)/i )
10848                {
10849                    my $enc = $1;
10850                    if ( $enc =~ /gzip/i ) {
10851
10852                        # encode with gzip
10853                        $respb = Compress::Zlib::memGzip($respb);
10854
10855                    } else {
10856
10857                        # encode with deflate
10858                        my $deflater = deflateInit();
10859                        $respb = $deflater->deflate($respb);
10860                        $respb .= $deflater->flush();
10861                    }
10862                    $resph .= "\nContent-Encoding: $enc";
10863                }
10864                $resph .= "\nContent-Length: " . length($respb);
10865                print $fh $resph;
10866                print $fh "\015\012\015\012";
10867                print $fh $respb;
10868            }
10869
10870            # close connection
10871            WebDone($fh);
10872        }
10873    } else {
10874
10875        # connection closed
10876        WebDone($fh);
10877    }
10878}
10879
10880sub WebDone {
10881    my $fh = shift;
10882    delete $SocketCalls{$fh};
10883    delete $WebCon{$fh};
10884    delete $StatCon{$fh};
10885    $readable->remove($fh);
10886    $writable->remove($fh);
10887    $fh->close;
10888}
10889sub ConCountSync {
10890
10891}
10892
10893
10894
10895# done with a file handle -- close him and his friend(s)
10896sub done {
10897  my $fh=shift;
10898  d('done');
10899
10900  $Con{$Con{$fh}->{forwardSpam}}->{gotAllText} = 1 if $Con{$fh}->{forwardSpam} && exists $Con{$Con{$fh}->{forwardSpam}};
10901  $Con{$Con{$Con{$fh}->{friend}}->{forwardSpam}}->{gotAllText} = 1 if $Con{$Con{$fh}->{friend}}->{forwardSpam} && exists $Con{$Con{$Con{$fh}->{friend}}->{forwardSpam}};
10902  done2($Con{$fh}->{friend}) if $Con{$fh}->{friend};
10903  done2($fh);
10904}
10905
10906# close a file handle & clean up associated records
10907sub done2 {
10908    my $fh = shift;
10909    d('done2');
10910    return unless $fh;
10911	my $ip=$Con{$fh}->{ip};
10912    #    return unless $Con{$fh};
10913
10914    if ($ip &&
10915            $ConnectionLog &&
10916            !(matchIP($ip,'noLoggingIPs',0,1)) &&
10917            (($Con{$fh}->{movedtossl} && "$fh" =~/SSL/io) or (!$Con{$fh}->{movedtossl})))
10918        {
10919            $Con{$fh}->{writtenDataToFriend} -= 6;
10920            $Con{$fh}->{writtenDataToFriend} = 0 if $Con{$fh}->{writtenDataToFriend} < 0;
10921            my $sz = max($Con{$fh}->{spambuf},$Con{$fh}->{mailloglength});
10922            $sz = $Con{$fh}->{maillength} unless $sz;
10923            mlog(0, 'finished message - received DATA size: ' . &formatNumDataSize($sz) . ' - sent DATA size: ' . &formatNumDataSize($Con{$fh}->{writtenDataToFriend}))
10924                if ($Con{$fh}->{maillength} > 3);
10925            my $tmpTimeNow = time();
10926            my $tmpDuration = $tmpTimeNow - $Con{$fh}->{timestart};
10927            mlog($fh, "disconnected ($tmpDuration seconds)",1) if $Con{$fh}->{timestart} ;
10928           	mlog($fh, "disconnected ",1) if !$Con{$fh}->{timestart} ;
10929
10930        	$SMTPSession{$ip}--   if $SMTPSession{$ip};
10931
10932        	delete $SMTPSession{$ip} if (--$SMTPSession{$ip} <= 0);
10933    }
10934    d("closing $fh");
10935
10936    # close the maillog if it's still open
10937    d('closing maillogfh');
10938    my $f = $Con{$fh}->{maillogfh};
10939    eval { close $f; } if $f;
10940
10941    # remove from the select structure
10942    delete $SocketCalls{$fh};
10943    $readable->remove($fh);
10944    $writable->remove($fh);
10945
10946
10947    d("closing $fh $ip");
10948    # close it
10949    if ("$fh" =~ /SSL/io) {
10950
10951    	eval{close($fh);};
10952        if ($@) {
10953                mlog(0,"warning: unable to close $fh - $@");
10954                eval{IO::Socket::SSL::kill_socket($fh)};
10955                if ($@) {
10956                    mlog(0,"warning: unable to kill $fh - $@");
10957                }
10958        }
10959
10960    } else {
10961        eval{close($fh) if fileno($fh);};
10962    }
10963
10964    d('delete the Connection data');
10965    # delete the Connection data
10966    delete $Con{$fh};
10967    delete $ConDelete{$fh};
10968
10969	d('delete the Session data');
10970    # delete the Session data & re-add sockets.
10971    if ( exists $SMTPSession{$fh} ) {
10972        delete $SMTPSession{$fh};
10973
10974        $smtpConcurrentSessions = 0 if (--$smtpConcurrentSessions < 0);
10975
10976        foreach my $lfh (@lsn)    		{ $readable->add($lfh) if !$readable->exists($lfh) };
10977        foreach my $lfh (@lsn2)   		{ $readable->add($lfh) if !$readable->exists($lfh) };
10978        foreach my $lfh (@lsnSSL) 		{ $readable->add($lfh) if !$readable->exists($lfh) };
10979        foreach my $lfh (@lsnRelay)  	{ $readable->add($lfh) if !$readable->exists($lfh) };
10980		$SMTPSession{Total}-- if $SMTPSession{Total} > 0;
10981        $SMTPSession{$ip}--   if $maxSMTPipSessions;
10982        delete $SMTPSession{$ip} if ($SMTPSession{$ip} <= 0);
10983
10984    }
10985    d('finished closing connection');
10986
10987}
10988
10989# adding a socket to the Select structure and Con hash
10990sub addfh {
10991    my ( $fh, $getline, $friend ) = @_;
10992    d('addfh');
10993    $SocketCalls{$fh} = \&SMTPTraffic;
10994    $readable->add($fh);
10995    binmode($fh);
10996    $Con{$fh} = {};
10997    my $this = $Con{$fh};
10998    $this->{getline}   = $getline;
10999    $this->{friend}    = $friend;
11000    $this->{timestart} = time();
11001    $this->{timelast}  = time();
11002}
11003
11004sub sayMessageOK {
11005	my ( $fh, $prepend ) = @_;
11006	$prepend |= "[MessageOK]";
11007    my $this = $Con{$fh};
11008    d('sayMessageOK');
11009    return if $this->{sayMessageOK} eq 'already';
11010    return if $this->{deleteMailLog};
11011    return unless $this->{sayMessageOK};
11012    &makeSubject($fh);
11013    ccMail($fh,$this->{mailfrom},$sendHamInbound,\$this->{header},$this->{rcpt}) if !$this->{spamfound};
11014    pbBlackDelete($this->{ip}) if !$this->{spamfound};
11015
11016    $this->{prepend} = "[MessageOK]" if !$this->{spamfound};
11017    $this->{prepend} = "[WhitelistedOK]" if $this->{sayMessageOK} =~ /whitelist/i;
11018    $this->{prepend} = "[NoprocessingOK]" if $this->{sayMessageOK} =~ /processing/i;
11019
11020    $this->{prepend} = "[LocalOK]" if  $this->{relayok};
11021    $this->{prepend} = $this->{sayprepend} if $this->{spamfound};
11022
11023    mlog($fh,"$this->{sayMessageOK}", 0, 2 );
11024
11025    $this->{sayMessageOK} = 'already';
11026
11027
11028}
11029# adding a SSL socket to the Select structure and Con hash
11030sub addsslfh {
11031  my ($oldfh,$sslfh,$friend) =@_;
11032  $SocketCalls{$sslfh}=$SocketCalls{$oldfh};
11033  $sslfh->blocking(0);
11034  binmode($sslfh);
11035  %{$Con{$sslfh}} = %{$Con{$oldfh}};
11036  $Con{$sslfh}->{friend} = $friend;
11037  $Con{$sslfh}->{self} = $sslfh;
11038  $Con{$sslfh}->{oldfh} = $oldfh;
11039  if ($Con{$sslfh}->{type} eq 'C') {
11040    $Con{$sslfh}->{client}   = $sslfh;
11041    $Con{$sslfh}->{server}   = $friend;
11042    $Con{$sslfh}->{myheaderCon} .= "X-Assp-Client-TLS: yes\r\n";
11043    $Stats{smtpConnTLS}++ unless $Con{$sslfh}->{relayok};
11044  } else {
11045    $Con{$friend}->{myheaderCon} .= "X-Assp-Server-TLS: yes\r\n";
11046  }
11047  &dopoll($sslfh,$readable,"POLLIN");
11048  &dopoll($sslfh,$writable,"POLLOUT");
11049  $Con{$oldfh}->{movedtossl} = 1;
11050  my $fno = $Con{$oldfh}->{fno} ;
11051  if (exists $ConFno{$fno}) {delete $ConFno{$fno};}
11052  delete $Fileno{$fno} if (exists $Fileno{$fno});
11053  $Con{$sslfh}->{fno} = fileno($sslfh);
11054  $Fileno{$Con{$sslfh}->{fno}} = $sslfh;
11055  d("info: switched connection from $oldfh to $sslfh");
11056}
11057# sendque enques a string for a socket
11058sub sendque {
11059    my ( $fh, $message ) = @_;
11060    my $outmessage = ref($message) ? $message : \$message;
11061    my $l=length($$outmessage);
11062
11063    d("sendque: $fh $Con{$fh}->{ip} l=$l");
11064    return unless $fh && exists $Con{$fh};
11065
11066    if (   $Con{$fh}->{type} eq 'C'       # is a client SMTP connection?
11067        && ($replyLogging == 2 or ($replyLogging == 1 && $$outmessage =~ /^[45]/o))
11068        && $$outmessage =~ /^[1-5]\d\d\s+[^\r\n]+\r\n$/o)    # is a reply?
11069    {
11070        my $what = 'Reply';
11071        $$outmessage =~ s/SESSIONID/$Con{$fh}->{msgtime}/go;
11072        $$outmessage =~ s/MYNAME/$myName/go;
11073        if ($$outmessage =~ /^([45])/o) {
11074            $what = ($1 == 5) ? 'Error' : 'Status';
11075        }
11076        my $reply = $$outmessage;
11077        $reply =~ s/\r?\n//o;
11078        mlog( $fh, "[SMTP $what] $reply", 1, 1 );
11079    }
11080
11081    $writable->add($fh);
11082    $Con{$fh}->{outgoing} .= $$outmessage;
11083    if ( !$Con{$fh}->{paused}
11084        && length( $Con{$fh}->{outgoing} ) > $OutgoingBufSizeNew )
11085    {
11086        $Con{$fh}->{paused} = 1;
11087        d("pausing");
11088        $readable->remove( $Con{$fh}->{friend} );
11089    }
11090}
11091sub dopoll {
11092   my ($fh,$action,$mask) = @_ ;
11093   my $fno;
11094   $fh = $Con{$fh}->{self} if exists $Con{$fh} && $Con{$fh}->{self};
11095   $fh = $WebConH{$fh} if $WebConH{$fh};
11096   $fh = $StatConH{$fh} if $StatConH{$fh};
11097   if ($IOEngineRun == 0) {
11098       $fno = fileno($fh);
11099       eval{$action->mask($fh => $mask);};
11100       if ($@) {
11101           if (exists $WebConH{$fh} or exists $StatConH{$fh}) {
11102               &WebDone($fh);
11103           } else {
11104               done($fh);
11105           }
11106       } else {
11107           $action->[3]{$fh} = $fno if $fno;
11108       }
11109   } else {
11110       $action->add($fh);
11111   }
11112}
11113
11114sub unpoll {
11115   my ($fh,$action) = @_ ;
11116   $fh = $Con{$fh}->{self} if $Con{$fh}->{self};
11117   if ($IOEngineRun == 0) {
11118       $fh = $Con{$fh}->{self} if $Con{$fh}->{self};
11119       $fh = $WebConH{$fh} if $WebConH{$fh};
11120       $fh = $StatConH{$fh} if $StatConH{$fh};
11121
11122       eval{$action->mask($fh => 0);};
11123
11124       if ($ConTimeOutDebug) {
11125           my $m = &timestring();
11126             my ($package, $file, $line) = caller;
11127           if ($Con{$fh}->{type} eq 'C'){
11128               $Con{$fh}->{contimeoutdebug} .= "$m client unpoll from $package $file $line\n" ;
11129           } else {
11130               $Con{$Con{$fh}->{friend}}->{contimeoutdebug} .= "$m server unpoll from $package $file $line\n" ;
11131           }
11132       }
11133       if (my $fno = $action->[3]{$fh}) {         # poll fd workaround
11134           delete $action->[3]{$fh};
11135           delete $action->[0]{$fno}{$fh};
11136           unless (%{$action->[0]{$fno}}) {
11137               delete $action->[0]{$fno};
11138               delete $action->[1]{$fno};
11139               delete $action->[2]{$fh};
11140           }
11141       }
11142   } else {
11143       if ($ConTimeOutDebug) {
11144           my $m = &timestring();
11145             my ($package, $file, $line) = caller;
11146           if ($Con{$fh}->{type} eq 'C'){
11147               $Con{$fh}->{contimeoutdebug} .= "$m client unselect from $package $file $line\n" ;
11148           } else {
11149               $Con{$Con{$fh}->{friend}}->{contimeoutdebug} .= "$m server ununselect from $package $file $line\n" ;
11150           }
11151       }
11152       $action->remove($fh);
11153   }
11154}
11155
11156sub sigOK {
11157  my ($fh,$m,$done)=@_;
11158  my $this=$Con{$fh};
11159  my $server = $this->{friend};
11160  if (! $this->{addMSGIDsigDone} && $this->{relayok} && $DoMSGIDsig) { # add the MSGID Tag
11161  d('sigOK');
11162
11163      if ($m =~ /(Message-ID\:[\r\n\s]*\<[^\r\n]+\>)/i) {       # if not already done
11164
11165          my $msgid = $1;
11166          my $tag = MSGIDaddSig($fh,$msgid);
11167          if ($msgid ne $tag ) {
11168              $m =~ s/\Q$msgid\E/$tag/i;
11169              $this->{header} =~ s/\Q$msgid\E/$tag/i;
11170              $this->{maillength} = length($this->{header});
11171              $this->{addMSGIDsigDone};
11172          }
11173
11174      }
11175  	}
11176  }
11177
11178sub is_7bit_clean {
11179    return $_[0] !~ /[^\x20-\x7E\x0A\x0D]/os;
11180}
11181
11182
11183
11184#####################################################################################
11185#                SMTP stuff
11186
11187# compile the regular expression (RE) for the local domains list (LDRE)
11188sub setLDRE {
11189    SetRE( 'LDRE', "^($_[0])\$", "i", "Local Domains" );
11190}
11191
11192# compile the regular expression (RE) for the vrfy domains list (VDRE)
11193sub setVDRE {
11194    SetRE( 'VDRE', "^($_[0])\$", "i", "VRFY Domains" );
11195}
11196# compile the regular expression (RE) for the local server names list (LSRE)
11197sub setLSRE {
11198    SetRE( 'LSRE', "^($_[0])\$", "i", "LocalHost" );
11199}
11200
11201
11202
11203# returns true if this address is local (any local domain)
11204sub localmail {
11205  my $h = shift;
11206  d("localmail - $h",1);
11207  return 0 unless $h;
11208#(my $package, my $file, my $line, my $Subroutine, my $HasArgs, my $WantArray, my $EvalText, my $IsRequire) = caller(0);
11209#d("localmail - $package, $file, $line, $Subroutine, $HasArgs, $WantArray, $EvalText, $IsRequire");
11210  $h = $1 if $h=~/\@([^@]*)/o;
11211  return &localdomains($h);
11212}
11213
11214# returns true if this address is in localdomains file or localDomains or LDAP
11215sub localdomains {
11216    my $h = shift;
11217    d("localdomains - $h");
11218    $h =~ tr/A-Z/a-z/;
11219    my $hat; $hat = $1 if $h =~ /(\@[^@]*)/o;
11220    $h = $1 if $h =~ /\@([^@]*)/o;
11221
11222    return 1 if $h eq "assp.local";
11223    return 1 if $h eq "assp-nospam.org";
11224
11225    my ($EBRD) = $EmailBlockReportDomain =~ /^\@*([^@]*)$/o;
11226    return 1 if ($EBRD && lc($h) eq lc($EBRD));
11227
11228    return 1 if $localDomains && ( ($hat && $hat =~ /$LDRE/) || ($h && $h =~ /$LDRE/) );
11229    if ($localDomainsFile) {
11230        &check4update('localDomainsFile');
11231        return 1 if $localDomainsFile{$h};
11232    }
11233    return 1 if $CanUseRegistry && $DoLocalIMailDomains
11234    							&& 	&localIMaildomain($h);
11235    return &localLDAPdomain($h);
11236}
11237# returns true if this address is in localdomains file or localDomains or LDAP
11238
11239sub localdomainsreal {
11240    my $h = shift;
11241    d("localdomainsreal - $h",1);
11242    $h =~ tr/A-Z/a-z/;
11243    my $hat; $hat = $1 if $h =~ /(\@[^@]*)/o;
11244    $h = $1 if $h =~ /\@([^@]*)/o;
11245
11246    return 1 if $localDomains && ( ($hat && $hat =~ /$LDRE/) || ($h && $h =~ /$LDRE/) );
11247    return &localLDAPdomain($h);
11248}
11249sub localLDAPdomain {
11250  my $h = shift;
11251  d("localLDAPdomain - $h");
11252  $h =~ tr/A-Z/a-z/;
11253  return 1 if &LDAPCacheFind('@'.$h,'LDAP',1);
11254  return 0 unless $CanUseLDAP;
11255  return 0 unless $ldLDAP;
11256  my $ldapflt = $ldLDAPFilter;
11257  $ldapflt =~ s/DOMAIN/$h/go;
11258  my $ldaproot = $ldLDAPRoot || $LDAPRoot;
11259  $ldaproot =~ s/DOMAIN/$h/go;
11260  return LDAPQuery($ldapflt, $ldaproot,$h);
11261}
11262
11263sub localIMaildomain {
11264  my $h = shift;
11265  d("localIMaildomain - $h $CanUseRegistry $DoLocalIMailDomains");
11266  return 0 unless $CanUseRegistry;
11267  return 0 unless $DoLocalIMailDomains;
11268  my ($hkey,$hkey2);
11269  d("about to open");
11270  if(!$HKEY_LOCAL_MACHINE->Open("Software\\Ipswitch\\IMail\\Domains",$hkey)) {
11271    d("localIMaildomain - failed to open domains key");
11272    return 0;
11273  }
11274  d("hkey: $hkey");
11275  if($hkey->Open($h,$hkey2)) {
11276    d("localIMaildomain - $h found in top level");
11277    $hkey2->Close();
11278    $hkey->Close();
11279 	return 1;
11280  }
11281  my @keys;
11282  if(!$hkey->GetKeys(\@keys)) {
11283    d("localIMaildomain - failed to GetKeys");
11284    $hkey->Close();
11285    return 0;
11286  }
11287  @keys = grep { /^\d\./ || /^\$virtual\d/ } @keys;
11288  d("localIMaildomain - $h searching aliases");
11289  foreach(@keys) {
11290    next unless $hkey->Open($_,$hkey2);
11291    my %values = ();
11292    $hkey2->GetValues(\%values);
11293    $hkey2->Close();
11294    if(($values{'Official'} && $values{'Offical'}->[2] =~ /^\Q$h\E$/i)
11295    || ($values{'Aliases'} && $values{'Aliases'}->[2] =~ /(?:^|\0)\Q$h\E(?:\0|$)/i))
11296    {
11297      d("localIMaildomain - $h found in aliases/official for $_");
11298      $hkey->Close();
11299      return 1;
11300    }
11301  }
11302  d("localIMaildomain - $h not found");
11303  $hkey->Close();
11304  return 0;
11305 }
11306
11307sub localvrfy2MTA {
11308  my ($fh,$h) = @_;
11309  d("localvrfy2MTA - $h");
11310  return 0 unless $DoVRFY;
11311  my $this;
11312  $this = $Con{$fh} if $fh;
11313  my $smtp;
11314  my $vrfy;
11315  my $expn;
11316  my $domain;
11317  my $MTA;
11318  my $forceRCPTTO;
11319  my $canvrfy;
11320  my $canexpn;
11321 $this->{prepend} = "";
11322
11323  return 1 if &LDAPCacheFind($h,'VRFY');
11324  if (my $nf = $LDAPNotFound{$h}) {
11325      return 0 if (time - $nf < 300);
11326      delete $LDAPNotFound{$h};
11327  }
11328
11329  $domain = $1 if $h=~/\@([^@]*)/o;
11330  return 0 unless $domain;
11331
11332  my $MTAList = &matchHashKey('DomainVRFYMTA',$domain);
11333  return 0 unless $MTAList;
11334
11335
11336  my $timeout = $VRFYQueryTimeOut ? $VRFYQueryTimeOut : 5;
11337    eval{
11338    for my $MTA (split(/,/,$MTAList)) {
11339      eval{
11340      $smtp = Net::SMTP->new($MTA,
11341                        Hello => $myName,
11342                        Timeout => $timeout);
11343      } or next;
11344      if ($smtp) {
11345          $forceRCPTTO = ($VRFYforceRCPTTO && $MTA =~ /$VFRTRE/) ? 1 : 0;
11346          if (! $forceRCPTTO) {
11347              $canvrfy = exists ${*$smtp}{'net_smtp_esmtp'}->{'VRFY'};   # was VRFY in EHLO Answer?
11348              $canexpn = exists ${*$smtp}{'net_smtp_esmtp'}->{'EXPN'};   # was EXPN in EHLO Answer?
11349              if (!$canvrfy && !$canexpn &&   # there was no VRFY or EXPN in the EHLO Answer, or HELO was used
11350                  (exists ${*$smtp}{'net_smtp_esmtp'}->{'HELP'} or    # we can use HELP      or
11351                   ! exists ${*$smtp}{'net_smtp_esmtp'}) )            # only HELO was used - try HELP
11352              {
11353                      my $help = $smtp->help();
11354                      $canvrfy = $help =~ /VRFY/io;
11355                      $canexpn = $help =~ /EXPN/io;
11356              }
11357              if ($canvrfy) {$vrfy = $smtp->verify($h) ? 1 : $smtp->verify("\"$h\"");}
11358              if ($canexpn && ! $vrfy) {$expn = scalar($smtp->expand($h)) ? 1 : scalar($smtp->expand("\"$h\""));}
11359          } else {
11360              mlog($fh,"info: using RCPT TO: (skipped VRFY) for $h") if ($VRFYLog >= 2);
11361          }
11362          if (!$canvrfy && !$canexpn) {    # VRFY and EXPN are both not supported or VRFYforceRCPTTO is set for this MTA
11363              mlog($fh,"info: host $MTA does not support VRFY and EXPN (tried EHLO and HELP) - now using RCPT TO to verify $h") if ($VRFYLog >= 2 && ! $forceRCPTTO);
11364              if ($smtp->mail('postmaster@'.$myName)) {
11365                  $vrfy = $smtp->to($h);
11366              } else {
11367                  mlog($fh,"info: host $MTA does not accept 'mail from:postmaster\@$myName'") if $VRFYLog;
11368              }
11369          }
11370          $smtp->quit;
11371      }
11372      last if ($vrfy || $expn);
11373    }
11374  };
11375  if ($@ or ! $smtp) {
11376     $vrfy = 0 ;
11377     $expn = 0 ;
11378	 my $not =  $VRFYFail ? ' not' : '';
11379     if ($@){
11380         mlog($fh,"error: VRFY / RCPT TO failed on host $MTAList - address <$h>$not accepted - $@");
11381     } else {
11382         mlog($fh,"error: VRFY / RCPT TO failed on host $MTAList - address <$h>$not accepted");
11383     }
11384
11385     $this->{userTempFail} = ! $VRFYFail if $this;
11386
11387     return ! $VRFYFail;
11388
11389  }
11390
11391  if ($vrfy || $expn) {
11392     if ($ldaplistdb && $MaxLDAPlistDays) {
11393         $LDAPlist{$h}=time." 1";
11394         mlog($fh,"VRFY added $h to VRFY-/LDAPlist") if $VRFYLog ;
11395         d("VRFY added $h to VRFY-/LDAPcache");
11396     }
11397     delete $LDAPNotFound{$h};
11398     mlog($fh,"info: VRFY found $h") if $VRFYLog >= 2;
11399     return 1 ;
11400  } else {
11401     $LDAPNotFound{$h} = time if $MaxLDAPlistDays;
11402  }
11403  mlog($fh,"info: VRFY was unable to find $h") if $VRFYLog >= 2;
11404  return 0 ;
11405
11406}
11407
11408sub localmailaddress {
11409  my ($fh,$current_email) = @_;
11410  d("localmailaddress - $current_email");
11411  $current_email = &batv_remove_tag($fh,$current_email,'');
11412  $current_email =~ tr/A-Z/a-z/;
11413  my $at_position = index($current_email, '@');
11414  my $current_username = substr($current_email, 0, $at_position);
11415  my $current_domain = substr($current_email, $at_position + 1);
11416  my $ldapflt = $LDAPFilter;
11417  $ldapflt =~ s/EMAILADDRESS/$current_email/go;
11418
11419  $ldapflt =~ s/USERNAME/$current_username/go;
11420  $ldapflt =~ s/DOMAIN/$current_domain/go;
11421  my $ldaproot = $LDAPRoot;
11422  $ldaproot =~ s/DOMAIN/$current_domain/go;
11423  if ( $LocalAddresses_Flat && $LocalAddresses_Flat_Domains
11424                && $current_email =~ /^([^@]*@)(.*)$/o
11425                && matchSL( $2, 'LocalAddresses_Flat' ) )
11426    	{
11427
11428      	return 1;
11429  }
11430  if ( $LocalAddresses_Flat && matchSL( $current_email, 'LocalAddresses_Flat' ) ) {
11431#      $LDAPlist{'@'.$current_domain} = time if $ldaplistdb;
11432      return 1;
11433  }
11434  if (&LDAPCacheFind($current_email,'LDAP')) {
11435      $LDAPlist{'@'.$current_domain} = time if $ldaplistdb && $ldLDAPFilter;
11436      return 1;
11437  }
11438  if($DoLDAP && $CanUseLDAP && LDAPQuery($ldapflt, $ldaproot,$current_email)) {
11439      $LDAPlist{'@'.$current_domain} = time if (!$LDAPoffline && $ldaplistdb && $ldLDAPFilter);
11440      return 1;
11441  }
11442  if($DoVRFY && (&matchHashKey('FlatVRFYMTA',"\@$current_domain") or &matchHashKey('DomainVRFYMTA',$current_domain))
11443             && $CanUseNetSMTP
11444             && $current_email =~ /[^@]+\@[^@]+/o
11445             && localvrfy2MTA($fh,$current_email))
11446  {
11447      $LDAPlist{'@'.$current_domain} = time if (! ($fh && $Con{$fh}->{userTempFail}) && $ldaplistdb);
11448      return 1;
11449  }
11450  return 0;
11451}
11452
11453sub LDAPCacheFind {
11454  my ($current_email,$how, $nolog) = @_;
11455  d("LDAPCacheFind - $current_email , $how");
11456  return 0 unless $ldaplistdb;
11457  return 0 unless $MaxLDAPlistDays;
11458  $current_email = lc $current_email;
11459  if (exists $LDAPlist{$current_email}) {
11460    mlog(0,"$how - found $current_email in LDAPlist") if (${$how.'Log'} >=2);
11461    d("$how - found $current_email in LDAP-cache");
11462    my ($vt,$vl) = split(/ /o,$LDAPlist{$current_email});
11463    if ($vl) {
11464      $LDAPlist{$current_email}=time." $vl";
11465    } else {
11466      $LDAPlist{$current_email}=time;
11467    }
11468    return 1;
11469  }
11470  d("$how - not found $current_email in LDAP-cache");
11471  mlog(0,"$how - $current_email not found in LDAPlist") if (${$how.'Log'} >= 2)  && !$nolog;
11472  return 0;
11473}
11474
11475sub LDAPQuery {
11476    my ( $ldapflt, $ldaproot, $current_email ) = @_;
11477    my $retcode;
11478    my $retmsg;
11479
11480
11481    my $mesg;
11482    my $entry_count;
11483
11484
11485   d("LDAPQuery - $ldapflt, $ldaproot, $current_email");
11486   $current_email = &batv_remove_tag(0,lc($current_email),'');
11487
11488   return 1 if &LDAPCacheFind($current_email,'LDAP');
11489   if (my $nf = $LDAPNotFound{$current_email}) {
11490      return 0 if (time - $nf < 300);
11491      delete $LDAPNotFound{$current_email};
11492   }
11493
11494    d("doing LDAP lookup with $ldapflt in $ldaproot");
11495
11496    my @ldaplist = split( /\|/, $LDAPHost );
11497    my $ldaplist = \@ldaplist;
11498    my $scheme = 'ldap';
11499    my $ldap;
11500    eval{
11501      $scheme = 'ldaps' if ($DoLDAPSSL == 1 && $AvailIOSocketSSL);
11502      $ldap = Net::LDAP->new( $ldaplist,
11503                          timeout => $LDAPtimeout,
11504                          scheme => $scheme,
11505                          inet4 =>  1,
11506                          inet6 =>  $CanUseIOSocketINET6
11507                        );
11508      $ldap->start_tls() if ($DoLDAPSSL == 2 && $AvailIOSocketSSL);
11509    };
11510
11511    if ( !$ldap ) {
11512    	$LDAPoffline=1;
11513        mlog( 0, "Couldn't contact LDAP server at $LDAPHost -- check ignored" );
11514
11515        return !$LDAPFail;
11516    }
11517
11518    # bind to a directory anonymous or with dn and password
11519    if ($LDAPLogin) {
11520        $mesg = $ldap->bind(
11521            $LDAPLogin,
11522            password => $LDAPPassword,
11523            version  => $LDAPVersion
11524        );
11525    } else {
11526
11527        # mlog($fh,"LDAP anonymous bind");
11528        $mesg = $ldap->bind( version => $LDAPVersion );
11529    }
11530    $retcode = $mesg->code;
11531    my $retmsg;
11532    my $rettext;
11533    if ($retcode) {
11534
11535        $retmsg=$mesg->error_text();
11536        $rettext = "Invalid credentials" if $retcode eq "49";
11537        #    mlog($fh,"LDAP bind error: $retcode - Login Problem?");
11538        mlog( 0, "LDAP bind error: $retcode -- $retmsg -- check ignored", 1 );
11539
11540        $ldap->unbind;
11541        $LDAPoffline=1;
11542        return !$LDAPFail;
11543    }
11544
11545    # perform a search
11546    $mesg = $ldap->search(
11547        base      => $ldaproot,
11548        filter    => $ldapflt,
11549        attrs     => ['cn'],
11550        sizelimit => 1
11551    );
11552    $retcode = $mesg->code;
11553
11554    # mlog($fh,"LDAP search: $retcode");
11555    if ( $retcode > 0 && $retcode != 4 ) {
11556        mlog( 0, "LDAP search error: $retcode -- '$ldapflt' check ignored", 1 );
11557
11558        $ldap->unbind;
11559        $LDAPoffline=1;
11560        return !$LDAPFail;
11561    }
11562    $LDAPoffline = 0;
11563  $entry_count = $mesg->count;
11564  $retmsg = $mesg->entry(1);
11565  mlog(0,"LDAP Results $ldapflt: $entry_count : $retmsg") if $LDAPLog;
11566  d("got $entry_count result(s) from LDAP lookup");
11567  $mesg = $ldap->unbind;  # take down session
11568  if($entry_count) {
11569     if($ldaplistdb && $MaxLDAPlistDays) {
11570         $LDAPlist{$current_email}=time;
11571         mlog(0,"LDAP added $current_email to LDAPlist") if $LDAPLog;
11572         d("added $current_email to LDAP-cache");
11573     }
11574     delete $LDAPNotFound{$current_email};
11575  } else {
11576     $LDAPNotFound{$current_email} = time if $MaxLDAPlistDays;
11577  }
11578
11579  return $entry_count;
11580}
11581
11582sub LDAPcrossCheck {
11583  my $k;
11584  my $v;
11585  my $current_email;
11586  my $at_position;
11587  my $current_username;
11588  my $current_domain;
11589  my $ldapflt;
11590  my $ldaproot;
11591  my $retcode;
11592  my $retmsg;
11593  my @ldaplist;
11594  my $ldaplist;
11595  my $ldap;
11596  my $mesg;
11597  my $entry_count;
11598  my $t;
11599  my $timeout = $VRFYQueryTimeOut ? $VRFYQueryTimeOut : 5;
11600  my $forceRCPTTO;
11601
11602  if(! $ldaplistdb) {
11603      mlog(0,"warning: unable to do crosscheck - ldaplistdb is not configured");
11604      return;
11605  }
11606
11607  $t = time;
11608
11609  mlog(0,"LDAP/VRFY-crosscheck started") if $MaintenanceLog;
11610  d("doing LDAP/VRFY-crosscheck");
11611
11612  @ldaplist = split(/\|/o,$LDAPHost);
11613  $ldaplist = \@ldaplist;
11614
11615  if ($CanUseLDAP && $DoLDAP && @ldaplist) {
11616      my $scheme = 'ldap';
11617      my $ldap;
11618      eval{
11619      $scheme = 'ldaps' if ($DoLDAPSSL == 1 && $AvailIOSocketSSL);
11620      $ldap = Net::LDAP->new( $ldaplist,
11621                          timeout => $LDAPtimeout,
11622                          scheme => $scheme,
11623                          inet4 =>  1,
11624                          inet6 =>  $CanUseIOSocketINET6
11625                        );
11626      $ldap->start_tls() if ($DoLDAPSSL == 2 && $AvailIOSocketSSL);
11627      };
11628
11629      if(! $ldap) {
11630        mlog(0,"Couldn't contact LDAP server at $LDAPHost -- no LDAP-crosscheck is done") if $MaintenanceLog;
11631      } else {
11632          if ($LDAPLogin) {
11633            $mesg = $ldap->bind($LDAPLogin, password => $LDAPPassword, version => $LDAPVersion);
11634          } else {
11635            $mesg = $ldap->bind( version => $LDAPVersion );
11636          }
11637          $retcode = $mesg->code;
11638          if ($retcode) {
11639            mlog(0,"LDAP bind error: $retcode -- no LDAP-crosscheck is done") if $MaintenanceLog;
11640            undef $ldap;
11641          }
11642      }
11643  }
11644
11645  my $expire_only;
11646  my $count;
11647  while (my ($k,$v)=each(%LDAPlist)) {
11648    $count++;
11649
11650    $entry_count = 0;
11651    $expire_only = 0;
11652    $current_email = $k;
11653    my ($vt,$vl) = split(/ /o,$v);
11654    if($vl && $k !~ /^@/o) {  # do VRFY
11655        if ($DoVRFY && $CanUseNetSMTP) {
11656            mlog(0,"info: VRFY-crosscheck on $k") if $MaintenanceLog >= 2;
11657            my ($domain) = $k =~ /[^@]+\@([^@]+)/o;
11658            my $MTA = &matchHashKey('DomainVRFYMTA',lc $domain);
11659            $MTA = &matchHashKey('FlatVRFYMTA',lc "\@$domain") unless $MTA;
11660            $expire_only = 1;
11661            eval{
11662            $expire_only = 0;
11663            my $vrfy;
11664            my $expn;
11665            my $smtp = Net::SMTP->new($MTA,
11666                                 Hello => $myName,
11667                                 Timeout => $timeout);
11668
11669            if ($smtp) {
11670                $forceRCPTTO = ($VRFYforceRCPTTO && $MTA =~ /$VFRTRE/) ? 1 : 0;
11671                if (! $forceRCPTTO) {
11672                    my $help = $smtp->help();
11673                    my $canvrfy = $help =~ /VRFY/io;
11674                    my $canexpn = $help =~ /EXPN/io;
11675                    if ($canvrfy) {$vrfy = $smtp->verify($k) ? 1 : $smtp->verify("\"$k\"");}
11676                    if ($canexpn && ! $vrfy) {$expn = scalar($smtp->expand($k)) ? 1 : scalar($smtp->expand("\"$k\""));}
11677                }
11678                if (!$expn && !$vrfy) {
11679                    if ($smtp->mail('postmaster@'.$myName)) {
11680                        $vrfy = $smtp->to($k);
11681                    }
11682                }
11683                $smtp->quit;
11684                $entry_count = $vrfy || $expn;
11685            }
11686            } if $MTA;
11687            if ($@) {
11688               mlog(0,"error: VRFY failed on host $MTA - $@");
11689               $expire_only = 1;
11690            }
11691        } else {
11692            $expire_only = 2;
11693        }
11694    } elsif ($ldap && $k !~ /^@/o) {   # do LDAP for addresses not for domains
11695        $expire_only = 0;
11696        mlog(0,"info: LDAP-crosscheck on $k") if $MaintenanceLog >= 2;
11697        $current_email =~ tr/A-Z/a-z/;
11698        $at_position = index($current_email, '@');
11699        $current_username = substr($current_email, 0, $at_position);
11700        $current_domain = substr($current_email, $at_position + 1);
11701        $ldapflt = $LDAPFilter;
11702        $ldapflt =~ s/EMAILADDRESS/$current_email/go;
11703        $ldapflt =~ s/USERNAME/$current_username/go;
11704        $ldapflt =~ s/DOMAIN/$current_domain/go;
11705        $ldaproot = $LDAPRoot;
11706        $ldaproot =~ s/DOMAIN/$current_domain/go;
11707# perform a search
11708        $mesg = $ldap->search(base   => $ldaproot,
11709                              filter => $ldapflt,
11710                              attrs => ['cn'],
11711                              sizelimit => 1
11712                              );
11713        $retcode = $mesg->code;
11714        if($retcode > 0 && $retcode != 4) {
11715          mlog(0,"LDAP search error: $retcode") if $MaintenanceLog;
11716          $expire_only = 1;
11717        }
11718        $entry_count = $expire_only ? 0 : $mesg->count;
11719    } else {
11720        $expire_only = 2;
11721    }
11722
11723    if ($entry_count && exists $PBTrap{$k}) {
11724        pbTrapDelete($k);
11725        mlog(0,"info: TrapAddess $k removed") if $MaintenanceLog;
11726    }
11727
11728    if (! $entry_count && ! $expire_only) { # entry was not found on LDAP/VRFY-server -> delete the cache entry
11729       delete($LDAPlist{$k});
11730       mlog(0,"LDAP/VRFY-crosscheck: $k not found and removed from LDAPlist") if $MaintenanceLog;
11731       d("LDAP/VRFY-crosscheck: $k removed from LDAPlist - Results $ldapflt: $entry_count : $retmsg");
11732    } elsif ($expire_only == 1 && $MaxLDAPlistDays && $vt + $MaxLDAPlistDays * 24 * 3600 < $t) { # entry is to old -> delete the cache entry
11733       delete($LDAPlist{$k});
11734       mlog(0,"LDAP/VRFY-crosscheck: $k removed from LDAPlist - entry is older than $MaxLDAPlistDays days") if $MaintenanceLog;
11735       d("LDAP/VRFY-crosscheck: $k removed from LDAPlist - entry is older than $MaxLDAPlistDays days");
11736    } elsif ($ldLDAPFilter && $expire_only == 2) {
11737       delete($LDAPlist{$k});
11738       mlog(0,"LDAP-crosscheck: $k domain entry removed from LDAPlist") if $MaintenanceLog;
11739       d("LDAP-crosscheck: $k domain removed from LDAPlist");
11740    }
11741  }
11742  $mesg = $ldap->unbind if $ldap;  # take down session
11743  mlog(0,"LDAP/VRFY-crosscheck finished") if $MaintenanceLog;
11744  &SaveLDAPlist();
11745}
11746
11747sub serverIsSmtpDestination {
11748  my $server=shift;
11749  d('serverIsSmtpDestination');
11750  my $peeraddr=$server->peerhost().':'.$server->peerport();
11751  my $destination;
11752  foreach my $destinationA (split(/\|/o, $smtpDestination)) {
11753      if ($destinationA  =~ /^(_*INBOUND_*:)?(\d+)$/o){
11754          if ($crtable{$Con{$Con{$server}->{friend}}->{localip}}) {
11755              $destination=$crtable{$Con{$Con{$server}->{friend}}->{localip}};
11756          } else {
11757              $destination = $Con{$Con{$server}->{friend}}->{localip} .':'.$2;
11758          }
11759      } else {
11760          $destination = $destinationA;
11761      }
11762      return 1 if $peeraddr eq $destination || $peeraddr eq $destination.':25';
11763  }
11764  return 0;
11765}
11766
11767
11768
11769
11770
11771sub sendNotification {
11772    my ($from,$to,$sub,$body,$file) = @_;
11773    my $text;
11774    if (! $from) {
11775        $from = 'ASSP <>';
11776        mlog(0,"*x*warning: 'EmailFrom' seems to be not configured - using '$from' as FROM: address");
11777    }
11778    if (! $to) {
11779        mlog(0,"*x*warning: TO: address not found for notification email - abort");
11780        return;
11781    }
11782    if (! $resendmail) {
11783        mlog(0,"*x*warning: 'resendmail' is not configured - abort notification");
11784        return;
11785    }
11786    my $date=$UseLocalTime ? localtime() : gmtime();
11787    my $tz=$UseLocalTime ? tzStr() : '+0000';
11788    $date=~s/(\w+) +(\w+) +(\d+) +(\S+) +(\d+)/$1, $3 $2 $5 $4/o;
11789    $text = "Date: $date $tz\r\n";
11790    $text .= "X-Assp-Notification: YES\r\n";
11791    $from =~ s/^\s+//o;
11792    $from =~ s/\s+$//o;
11793    if ($from !~ /\</o) {
11794        $text .= "From: <$from>\r\nTo:";
11795    } else {
11796        my ($t,$m) = split(/</o, $from);
11797        $m = '<' . $m;
11798        $t =~ s/^\s+//o;
11799        $t =~ s/\s+$//o;
11800        $t = encodeMimeWord($t,'Q','UTF-8') . ' ' if $t;
11801        $text .= "From: $t$m\r\nTo:";
11802    }
11803    foreach (split(/,|\|/o, $to)) {
11804        s/^\s+//o;
11805        s/\s+$//o;
11806        if ($_ !~ /\</o) {
11807            $text .= " <$_>,";
11808        } else {
11809            my ($t,$m) = split(/</o, $_);
11810            $m = '<' . $m;
11811            $t =~ s/^\s+//o;
11812            $t =~ s/\s+$//o;
11813            $t = encodeMimeWord($t,'B','UTF-8') . ' ' if $t;
11814            $text .= " $t$m,";
11815        }
11816    }
11817    chop $text;
11818    $text .= "\r\n";
11819    $sub = encodeMimeWord($sub,'B','UTF-8');
11820    $text .= "Subject: $sub\r\n";
11821    $text .= "MIME-Version: 1.0\r\n";
11822    $text .= "Content-Type: text/plain; charset=\"UTF-8\"\r\n";
11823    $text .= "Content-Transfer-Encoding: quoted-printable\r\n";
11824    my $msgid = $WorkerNumber . sprintf("%06d",$NotifyCount++) . int(rand(100));
11825    $text .= "Message-ID: a$msgid\@$myName\r\n";
11826    $text = headerWrap($text);
11827    $text .= "\r\n";           # end header
11828    my $sendbody;
11829    foreach (split(/\r?\n/o,$body)) {
11830        $sendbody .= ( $_ ? assp_encode_Q(Encode::encode('UTF-8',$_)) : '') . "\r\n";
11831    }
11832    my $f;
11833    if ($file && -e $file && (open($f,"<",$file))) {
11834        while (<$f>) {
11835             s/\r?\n$//o;
11836             $sendbody .= ( $_ ? assp_encode_Q(Encode::encode('UTF-8',$_)) : '') . "\r\n";
11837        }
11838        close $f;
11839    }
11840    $text .= $sendbody;
11841    my $rfile = "$base/$resendmail/n$msgid$maillogExt";
11842    if (open($f,">",$rfile)) {
11843        binmode $f;
11844        print $f $text;
11845        close $f;
11846        mlog(0,"*x*info: notification message queued to sent to $to") if $MaintenanceLog;
11847        $nextResendMail = $nextResendMail < time + 3 ? $nextResendMail : time + 3;
11848    } else {
11849        mlog(0,"*x*error: unable to write notify message to file $f - $!");
11850    }
11851}
11852
11853# resend the files in Directory $resendmail
11854# leading '*x*' for mlog is used to prevent notification loops
11855# '*x*' is removed in sub mlog
11856sub resend_mail {
11857  return unless($resendmail);
11858  return unless($CanUseEMS);
11859  opendir(my $DMAIL,"$base/$resendmail");
11860  my @filelist;
11861  my $result;
11862  my @list = readdir($DMAIL);
11863  close $DMAIL;
11864  while ( my $file = shift @list) {
11865      next if -d "$base/$resendmail/$file";
11866      next if ($file !~ /$maillogExt$/i);
11867      push(@filelist, "$base/$resendmail/$file");
11868  }
11869  return unless(@filelist);
11870  while ( my $file  = shift @filelist) {
11871      my $hostCFGname;
11872      my $message = "\r\n";
11873      mlog(0,"*x*(re)send - try to open: $file") if $MaintenanceLog >= 2;
11874      next unless(open my $FMAIL,'<',"$file");
11875      while (<$FMAIL>) {
11876          s/\r?\n//go;
11877          $message .= "$_\r\n";
11878      }
11879      close $FMAIL;
11880      $message =~ s/[\r?\n]\.[\r?\n]+$/\r\n/so;
11881      my $count = exists $ResendFile{$file} ? "(try $ResendFile{$file}" : "(first time)";
11882      mlog(0,"*x*(re)send - process: $file $count") if $MaintenanceLog >= 2;
11883      my ($howF, $mailfrom);
11884      ($howF, $mailfrom) = ($1,$2)
11885        if ($message =~ /\n(X-Assp-Envelope-From:)[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?\s*\r?\n/sio);
11886      ($howF, $mailfrom) = ($1,$2)
11887        if (! $mailfrom && $message =~ /\n(from:)[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?\s*\r?\n/sio);
11888      if (! $mailfrom) {
11889          ($howF, $mailfrom) = ($1,$2)
11890             if ($message =~ s/\n(from:)\s*(ASSP <>)\s*\r?\n/\n/sio);
11891          if (! $mailfrom) {
11892              mlog(0,"*x*(re)send - $file - From: and X-Assp-Envelope-From: headertag not found");
11893              $message = "# (re)send - $file - From: and X-Assp-Envelope-From: headertag not found\r\n".$message;
11894              &resendError($file,\$message);
11895              next;
11896          }
11897      }
11898
11899
11900      my ($howT, $to);
11901      ($howT, $to) = ($1,$2)
11902        if ($message =~ /\n(X-Assp-Intended-For:)[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio);
11903      ($howT, $to) = ($1,$2)
11904        if (! $to && $message =~ /\n(to:)[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio);
11905      if (! $to) {
11906          mlog(0,"*x*(re)send - $file - To: and X-Assp-Intended-For: headertag not found - skip file");
11907          $message = "# (re)send - $file - To: and X-Assp-Intended-For: headertag not found - skip file\r\n".$message;
11908          &resendError($file,\$message);
11909          next;
11910      }
11911      if (lc $howT eq lc "X-Assp-Intended-For:") {
11912          $message =~ s/\nto:[^\<]*?<?$EmailAdrRe\@$EmailDomainRe>?\s*\r?\n/\n/sio;
11913          $message =~ s/X-Assp-Intended-For:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?\s*\r?\n/To: <$1>\r\n/sio;
11914      }
11915
11916      my $islocal = localmail($to);
11917      if ($islocal && $ReplaceRecpt) {
11918            my ($mf) = $mailfrom =~ /($EmailAdrRe\@$EmailDomainRe)/o;
11919            my $newadr = RcptReplace($to,$mf,'RecRepRegex');
11920            if (lc $newadr ne lc $to) {
11921                $message =~ s/(\nto:[^\<]*?<?)$to(>?)/$1$newadr$2/is;
11922                mlog(0,"*x*(re)send - recipient $to replaced with $newadr");
11923            }
11924      }
11925
11926      $message =~ s/^\r?\n//o;
11927      $message =~ s/(?:ReturnReceipt|Return-Receipt-To|Disposition-Notification-To):$HeaderValueRe//gios
11928            if ($removeDispositionNotification);
11929
11930      mlog(0,"*x*(re)send - $file - $howF $mailfrom - $howT $to") if $MaintenanceLog >= 2;
11931
11932      my $host = $smtpDestination;
11933      $hostCFGname = 'smtpDestination';
11934      if ($EmailReportDestination &&
11935          $islocal &&
11936          (($EmailFrom && $EmailFrom =~ /^$mailfrom$/i) || lc $mailfrom eq 'assp <>')
11937         )
11938      {
11939          mlog(0,"*x*(re)send - $file - using EmailReportDestination for local mail - From: $mailfrom - To: $to")
11940              if $MaintenanceLog >= 2;
11941          $host = $EmailReportDestination;
11942          $hostCFGname = 'EmailReportDestination';
11943      }
11944
11945      if ($islocal && (my @bccRCPT = $message =~ /\nbcc:($HeaderValueRe)/igso)) {
11946          foreach my $bcc (@bccRCPT) {
11947              while ($bcc =~ /($EmailAdrRe\@$EmailDomainRe)/igos) {
11948                  my $addr = $1;
11949                  if ($ReplaceRecpt) {
11950                      my ($mf) = $mailfrom =~ /($EmailAdrRe\@$EmailDomainRe)/o;
11951                      my $newadr = RcptReplace($bcc,$mf,'RecRepRegex');
11952                      $newadr = '' if ! localmail($newadr);
11953                      if (lc $newadr ne lc $addr) {
11954                          $message =~ s/(\nbcc:(?:$HeaderValueRe)*?)$addr/$1$newadr/is;
11955                          mlog(0,"*x*(re)send - BCC - recipient $addr replaced with $newadr");
11956                      }
11957                  }
11958              }
11959          }
11960          $message =~ s/\nbcc:[\r\n\s]+($HeaderNameRe:)?/\n$1/iogs;
11961      }
11962
11963      if (! $islocal && $relayHost) {
11964          mlog(0,"*x*(re)send - $file - using relayHost for not local mail - From: $mailfrom - To: $to")
11965              if $MaintenanceLog >= 2;
11966          $host = $relayHost;
11967          $hostCFGname = 'relayHost';
11968          my $t = time;
11969          $Con{$t} = {};
11970          $Con{$t}->{relayok} = 1;
11971          $Con{$t}->{mailfrom} = $mailfrom;
11972          $Con{$t}->{rcpt} = $to;
11973          $Con{$t}->{header} = $message;
11974          if ($DoMSGIDsig) {
11975              if ($message =~ /(Message-ID\:[\r\n\s]*\<[^\r\n]+\>)/io) {
11976                  my $msgid = $1;
11977                  my $tag = MSGIDaddSig($t,$msgid);
11978                  if ($msgid ne $tag ) {
11979                      $message =~ s/\Q$msgid\E/$tag/i;
11980                  }
11981              }
11982          }
11983
11984          delete $Con{$t};
11985      }
11986      my $localip;
11987      if ( $islocal && $host eq $smtpDestination && $message =~ /X-Assp-Intended-For-IP: ([^\r\n]+)\r\n/o) {
11988          $localip = $1;
11989      }
11990      if (! $host) {
11991          mlog(0,"*x*(re)send - $file - no SMTP destination found in config - skip file - From: $mailfrom - To: $to");
11992          $message = "# (re)send - $file - no SMTP destination found in config - skip file - From: $mailfrom - To: $to\r\n".$message;
11993          &resendError($file,\$message);
11994          next;
11995      }
11996      my $AVa = 0;
11997      my $reason;
11998      foreach my $destinationA (split(/\|/o, $host)) {
11999          if ($destinationA =~ /^(_*INBOUND_*:)?(\d+)$/o){
12000              $localip = '127.0.0.1' if !$localip or $localip eq '0.0.0.0';
12001              if ($crtable{$localip}) {
12002                  $destinationA=$crtable{$localip};
12003              } else {
12004                  $destinationA = $localip .':'.$2;
12005              }
12006          }
12007          if ($AVa<1) {
12008              mlog(0,"*x*(re)send $file to host: $destinationA ($hostCFGname)") if $MaintenanceLog >= 2;
12009              eval {
12010                  my %auth = ($hostCFGname eq 'relayHost' && $relayAuthUser && $relayAuthPass) ? (username => $relayAuthUser, password => $relayAuthPass) : ();
12011                  my $sender = Email::Send->new({mailer => 'SMTP'});
12012                  $sender->mailer_args([Host => $destinationA, Hello => $myName, tls => ($hostCFGname eq 'relayHost' && $DoTLS == 2 && ! exists $localTLSfailed{$destinationA}), %auth]);
12013                  eval{$result = $sender->send($message);};
12014                  if ($@ && $DoTLS == 2 && $@ =~ /STARTTLS: *50\d/io) {
12015                      $localTLSfailed{$destinationA} = time;
12016                      $sender = Email::Send->new({mailer => 'SMTP'});
12017                      $sender->mailer_args([Host => $destinationA, Hello => $myName, %auth]);
12018                      $result = $sender->send($message);
12019                  } elsif ($@) {
12020                      die "$@\n";
12021                  }
12022              };
12023              if ($@ || !$result) {
12024                  mlog(0,"*x*error: unable to send file $file to $destinationA ($hostCFGname) - $@") if ($@ && $MaintenanceLog);
12025                  $@ =~ s/\r?\n/\r\n/go;
12026                  $@ =~ s/[\r\n]+$//o;
12027                  $reason .= "# error: unable to send file $file to $destinationA ($hostCFGname) - $@\r\n" if $@;
12028                  mlog(0,"*x*error: unable to send file $file to $destinationA ($hostCFGname) - $result") if ($result && $MaintenanceLog);
12029                  $result =~ s/\r?\n/\r\n/go;
12030                  $result =~ s/[\r\n]+$//o;
12031                  $reason .= "# error: unable to send file $file to $destinationA ($hostCFGname) - $result\r\n" if $result;
12032                  mlog(0,"*x**** send to $destinationA ($hostCFGname) didn't work, trying others...") ;
12033                  $reason .= "# send to $destinationA ($hostCFGname) didn't work, trying others\r\n";
12034              } else {
12035                  mlog(0,"*x*info: successful sent file $file to $destinationA ($hostCFGname) - $result") if $MaintenanceLog;
12036                  $AVa = 1;
12037                  mlog(0,"*x*warning: unable to delete $file - $!") unless (unlink("$file"));
12038
12039                  if ( $autoAddResendToWhite > 1 && $islocal && $mailfrom && lc $mailfrom ne 'assp <>' && !&localmail($mailfrom)) {
12040                      &Whitelist($mailfrom,undef,'add');
12041                      mlog( 0, "info: whitelist addition on resend via GUI or copied file: $mailfrom" )
12042                        if $ReportLog || $MaintenanceLog;
12043                  }
12044
12045              }
12046          }
12047      }
12048      $message = $reason . $message;
12049      &resendError($file,\$message);
12050  }
12051  return;
12052}
12053
12054sub resendError {
12055     my ($file,$message) = @_;
12056
12057     if ($eF->( $file)) {
12058          $ResendFile{$file} = 0 if (! exists $ResendFile{$file});
12059          if (++$ResendFile{$file} > 10) {
12060              mlog(0,"*x*error: send $file aborted after $ResendFile{$file} unsuccessful tries") if $MaintenanceLog;
12061              delete $ResendFile{$file};
12062              $file =~ s/\\/\//go;
12063              if ($eF->( $file.'.err')) {
12064                  mlog(0,"*x*warning: unable to delete $file.err - $!") unless ($unlink->($file.'.err')) ;
12065              }
12066              mlog(0,"*x*warning: unable to rename $file to $file.err - $!") unless ($rename->($file,$file.'.err'));
12067              if ($open->(my $MF,'>',$file.'.err.modified')) {
12068                 $MF->binmode;
12069                 $MF->print($$message);
12070                 $MF->close;
12071                 mlog(0,"*x*warning: the modified content of file $file was stored in to file $file.err.modified") if $MaintenanceLog;
12072              }
12073          }
12074      } else {
12075          delete $ResendFile{$file};
12076      }
12077}
12078
12079# wrap too long bodys
12080sub bodyWrap {
12081    my $cont = shift;
12082    my $max = shift;
12083    d('bodyWrap');
12084    my $body = substr($$cont,0,$max);
12085    return \$body if $body =~ /[\x7F-\xFF]/o;  # binary data
12086    $body =~ s/\n+[^\n]+$/\n/o;              # remove last unterminated line
12087#    $body =~ s/([^\r\n]{100,200}\s|[^\r\n\s]{1,100}\s*)/$1\r\n/go;   # wrap
12088    return \$body;
12089}
12090
12091# wrap long headers
12092sub headerWrap {
12093  my $header=shift;
12094  d('headerWrap');
12095  $header=~s/(?:([^\r\n]{60,75}?;)|([^\r\n]{60,75}) ) {0,5}(?=[^\r\n]{10,})/$1$2\r\n\t/g;
12096
12097  return $header;
12098}
12099
12100# unwrap long header (in place)
12101sub headerUnwrap {
12102  $_[0]=~s/\015\012[ \t]+//g;
12103}
12104
12105sub headerFormat {
12106    my $text = shift;
12107    $text =~ s/(?:\r*\n)+/\r\n/gos;
12108    return headerWrap($text) if &is_7bit_clean($text);
12109    my $org = $text;
12110
12111    eval{
12112         $text = join("\r\n", map{headerWrap(MIME::Words::encode_mimewords(&decodeMimeWords2UTF8($_),('Charset' => 'UTF-8')));} split(/\r?\n/o,$text));
12113         $text .= "\r\n" if $text !~ /\r\n$/o;
12114         $text =~ s/(?:\r?\n)+/\r\n/go;
12115    };
12116#    eval{$text = headerWrap(MIME::Words::encode_mimewords(&decodeMimeWords($text),('Charset' => 'UTF-8')));};
12117
12118    if ($@) {
12119       my $hint; $hint = "- **** please install the Perl module MIME::Tools (includes MIME::Words) via 'cpan install MIME::Tools' (on nix/mac) or 'ppm install MIME-Tools' (on win32)"
12120           if $@ =~ /Undefined subroutine \&MIME::Words::encode_mimewords/io;
12121       mlog(0,"warning: MIME encoding for our ASSP header lines failed - $@ $hint") if ! $IgnoreMIMEErrors;
12122       eval{
12123           $text = join("\r\n", map{headerWrap(&encodeMimeWord(&decodeMimeWords2UTF8($_),'B','UTF-8'));} split(/\r?\n/o,$text));
12124           $text .= "\r\n" if $text !~ /\r\n$/o;
12125#           $text = headerWrap(&encodeMimeWord(&decodeMimeWords2UTF8($text),'Q','UTF-8'));
12126       };
12127       if ($@) {
12128           $org .= "\r\n" if $org;
12129           $org =~ s/(?:\r?\n)+/\r\n/go;
12130           return $org;
12131       }
12132    }
12133    $text =~ s/\=\?UTF\-8\?Q\?\=20\?\=/ /gio;    # revert unneeded MIME-encoding of a single space ????
12134    $text =~ s/\=\?UTF\-8\?Q\?\?\=//gio;    # revert unneeded MIME-encoding of an empty line ????
12135    $text .= "\r\n" if $text;
12136    $text =~ s/(?:\r?\n)+/\r\n/go;
12137    return $text;
12138}
12139
12140# compile the regular expression for forcing the usage of RCPT TO
12141sub setLHNRE {
12142    my @h;
12143    foreach my $h ( split( /\|/, $_[0] ) ) {
12144        push( @h, $h );
12145    }
12146    my @s;
12147    push( @s, 'localhost' );             # 'localhost' alias
12148    push( @s, '127.0.0.1' );             # loopback interface address
12149    push( @s, join( '|', @h ) ) if @h;
12150    my $s = join( '|', @s );
12151    $s ||= '^(?!)';                      # regexp that never matches
12152    SetRE( 'LHNRE', "^($s)\$", 'i', 'Local Host Names' );
12153}
12154# compile the regular expression for the local host names
12155sub setVFRTRE {
12156    my @h;
12157    foreach my $h ( split( /\|/, $_[0] ) ) {
12158        push( @h, $h );
12159    }
12160    my @s;
12161
12162    push( @s, join( '|', @h ) ) if @h;
12163    my $s = join( '|', @s );
12164    $s ||= '^(?!)';                      # regexp that never matches
12165    SetRE( 'VFRTRE', "^($s)\$", 'i', 'RCPT TO Names' );
12166}
12167# compile the regular expression for the 'allowadmin from hostnames'
12168sub setAARE {
12169    my @h;
12170    foreach my $h ( split( /\|/, $_[0] ) ) {
12171        push( @h, $h );
12172    }
12173    my @s;
12174
12175    push( @s, join( '|', @h ) ) if @h;
12176    my $s = join( '|', @s );
12177    $s ||= '^(?!)';                      # regexp that never matches
12178    SetRE( 'AARE', "^($s)\$", 'i', 'Allow Admin Names' );
12179}
12180# compile the regular expression for the bounce senders addresses
12181sub setBSRE {
12182    my ( @uad, @u, @d );
12183    foreach my $a ( split( /\|/, $_[0] ) ) {
12184        if ( $a =~ /\S\@\S/ ) {
12185            push( @uad, $a );
12186        } elsif ( $a =~ /^\@/ ) {
12187            push( @d, $a );
12188        } else {
12189            push( @u, $a );
12190        }
12191    }
12192    my @s;
12193    push( @s, '^\s*$' );                                   # null sender address
12194    push( @s, '^(' . join( '|', @uad ) . ')$' ) if @uad;
12195    push( @s, '^(' . join( '|', @u ) . ')@' ) if @u;
12196    push( @s, '(' . join( '|', @d ) . ')$' ) if @d;
12197    my $s = join( "|", @s );
12198    $s = '<not a valid list>' unless $s;
12199    SetRE( 'BSRE', $s, 'i', "Bounce Senders" );
12200}
12201
12202
12203
12204sub stateReset {
12205    my $fh   = shift;
12206    my $this = $Con{$fh};
12207    d("stateReset");
12208	%{$this->{Xheaders}} = ();
12209	undef %{$this->{Xheaders}};
12210	delete $this->{Xheaders};
12211
12212    $this->{acceptall}              = '';
12213    $this->{accBackISPIP}           = '';
12214    $this->{addMSGIDsigDone}        = '';
12215    $this->{addressedToPenaltyTrap} = '';
12216    $this->{addressedToSpamBucket}  = '';
12217
12218    $this->{alllog}                 = '';
12219    $this->{attachcomment}          = '';
12220    $this->{attachdone}             = '';
12221    $this->{averror}                = '';
12222    $this->{backsctrdone}           = '';
12223    $this->{badnorm}                = '';
12224    $this->{baysprob}               = '';
12225    $this->{bayeslowconf}           = '';
12226    $this->{allTestMode}           = '';
12227    $this->{baysspamhaters}			= '';
12228    $this->{blackdomainscore}		= '';
12229    $this->{BlackDomainOK}			= '';
12230    $this->{bombdone}               = '';
12231    $this->{bombheaderdone}         = '';
12232    $this->{ccheader}               = '';
12233    $this->{ccdone}                 = '';
12234    $this->{charsetsdone}			= '';
12235    $this->{cip}                    = '';
12236    $this->{ciphelo}                = '';
12237    $this->{cipdone}                = '';
12238    $this->{clamscandone}           = '';
12239    $this->{contentonly}            = '';
12240    $this->{data}                   = '';
12241    $this->{delaydone}              = '';
12242    $this->{delayed}                = '';
12243    $this->{destination}            = '';
12244    $this->{dlslre}                 = '';
12245    $this->{isbomb}		= '';
12246    $this->{forgedHeloOK}           = '';
12247    $this->{forgedhelodone}         = '';
12248    $this->{formathelodone}         = '';
12249    $this->{from} 					= '';
12250    $this->{gripdone}               = '';
12251    $this->{hamcopydone}			= '';
12252    $this->{header}                 = '';
12253    $this->{headerlength}           =  0;
12254    $this->{invalidHeloOK}          = '';
12255    $this->{suspiciousHeloOK}		= '';
12256    $this->{invalidSRSBounce}       = '';
12257
12258
12259    $this->{isbounce}               = '';
12260    $this->{ispip}                  = '';
12261    $this->{ismaxsize}              = '';
12262    $this->{isvrfy}                 = '';
12263    $this->{localSenderOK}          = '';
12264    $this->{localsenderdone}        = '';
12265    $this->{localuser}              = '';
12266    $this->{localmail}              = '';
12267    $this->{logrecord}              = '';
12268    $this->{logsubject}				= '';
12269    $this->{maximumuniqueuri}       = '';
12270    $this->{maximumuri}             = '';
12271    $this->{messagelow}             = '';
12272    $this->{messagereason}          = '';
12273    mlog($fh,"info: message score is set to $this->{prescore}") if $this->{prescore};
12274    $this->{messagescore} = $this->{prescore};
12275    $this->{messagescore} ||= 0;
12276    $this->{messagescoredone}   = '';
12277    $this->{messagesize}        = '';
12278    $this->{msgid}              = '';
12279    $this->{msgiddone}          = '';
12280    $this->{myheader} = $this->{myheaderCon};
12281    $this->{myheaderdone}		= '';
12282	$this->{newsletterre}		= '';
12283    $this->{nobayesian}         = '';
12284    $this->{nocollect}          = '';
12285    $this->{nodelay}            = '';
12286    $this->{nohelo}             = '';
12287    $this->{nopb}               = '';
12288    $this->{nopbwhite}          = '';
12289    $this->{noprocessing}       = '';
12290    $this->{noprocessingreason} = '';
12291    $this->{noscan}             = '';
12292    $this->{notvalidhelofound}  = '';
12293    $this->{obfuscatedip}       = '';
12294    $this->{obfuscateduri}      = '';
12295
12296    $this->{pbblack}            = '';
12297    $this->{pbwhite}            = '';
12298    $this->{prepend}            = '';
12299    $this->{prvs}				= '';
12300    $this->{rblcachedone}       = '';
12301    $this->{rbldone}            = '';
12302    $this->{rblfail}            = '';
12303    $this->{rblneutral}         = '';
12304	$this->{received} 			= '';
12305    $this->{rcptnoprocessing}   = '';
12306    $this->{reportaddress}   	= '';
12307    $this->{rcpt}               = '';
12308    %{$this->{rcptlist}} = (); undef %{$this->{rcptlist}}; delete $this->{rcptlist};
12309    $this->{redsl}              = '';
12310    $this->{red}                = '';
12311    $this->{rwlok}              = '';
12312    $this->{sattachdone}        = '';
12313    $this->{saveprepend2}       = '';
12314    $this->{saveprepend}        = '';
12315    $this->{sayMessageOK}       = '';
12316    $this->{externalsenderok}   = '';
12317    $this->{senderok}           = '';
12318    @{$this->{senders}} = (); undef @{$this->{senders}}; delete $this->{senders};
12319    $this->{serverErrors}		=  0;
12320    $this->{spamconf}           = '';
12321    $this->{spamdone}           = '';
12322    $this->{spamfound}          = '';
12323    $this->{spamloverdone}      = '';
12324    $this->{spamlover}          = '';
12325    $this->{spamloverall}       = '';
12326    $this->{spamloversre}       = '';
12327    $this->{spamMaxScore} 		= undef;
12328    $this->{spamprob}           = '';
12329    $this->{spamfriends}		= '';
12330    $this->{spamfriendsdone}	= '';
12331	$this->{spamfoes}			= '';
12332    $this->{spfok}              = '';
12333    $this->{spfdone}			= '';
12334    $this->{SPFokDone}			= '';
12335    $this->{srs}				= '';
12336    $this->{strictsl}			= '';
12337    $this->{subjectsl}			= '';
12338	$this->{subject}			= '';
12339	$this->{subject3}			= '';
12340    $this->{StatsmsgDelayed} 	= '';
12341    $this->{tagmode}         	= '';
12342    $this->{testmode}        	= '';
12343    $this->{test}				= '';
12344    $this->{uriblneutral}       = '';
12345    $this->{userTempFail}	 	= '';
12346    $this->{validHeloOK}     	= '';
12347    $this->{validhelodone}   	= '';
12348    $this->{whitelisted}     	= '';
12349    $this->{whiteokdone}	 	= '';
12350    $this->{mailfrom}        	= '';
12351    $this->{notspamtag}		 	= '';
12352
12353    $this->{noMoreQueue} = '';
12354    $this->{qdata} = '';
12355        $this->{IPinHeloOK} = '';
12356        $this->{BlackDomainOK} = '';
12357        %{$this->{NoSpoofingOK}} = (); delete $this->{NoSpoofingOK};
12358        $this->{RWLok} = '';
12359        $this->{FromStrictOK} = '';
12360        $this->{SPFok} = '';
12361        $this->{BombHeaderOK} = '';
12362        $this->{BlackHeloOK} = '';
12363        $this->{MXAOK} = '';
12364        $this->{PTROK} = '';
12365        $this->{ScriptOK} = '';
12366        $this->{originalsubject} = '';
12367        $this->{subject} = '';
12368        $this->{subject2} = '';
12369        $this->{subject3} = '';
12370        %{$this->{Xheaders}} = (); undef %{$this->{Xheaders}}; delete $this->{Xheaders};
12371	$this->{allwhitelist}     = 0;
12372    $this->{allLoveSpam}      = 0;
12373    $this->{allLoveBaysSpam}  = 0;
12374    $this->{allLoveBlSpam}    = 0;
12375    $this->{allLoveSBSpam}    = 0;
12376    $this->{allLoveMSSpam}    = 0;
12377    $this->{allLoveISSpam}    = 0;
12378    $this->{allLovePTRSpam}   = 0;
12379    $this->{allLoveHlSpam}    = 0;
12380    $this->{allLoveSpam}   = 0;
12381    $this->{allLoveRBLSpam}   = 0;
12382    $this->{allLoveSpam}   = 0;
12383    $this->{allLoveDLSpam}    = 0;
12384    $this->{allLoveMXASpam}   = 0;
12385    $this->{allLoveBombsSpam} = 0;
12386    $this->{allLoveURIBLSpam} = 0;
12387    $this->{allLoveBaysSpam}  = 0;
12388     $this->{allLoveATSpam} = 0;
12389    $this->{spamloversonly} = '';
12390
12391
12392
12393    $this->{XCLIENT} = $this->{saveXCLIENT} if exists $this->{saveXCLIENT};
12394    $this->{XFORWARD} = $this->{saveXFORWARD} if exists $this->{saveXFORWARD};
12395
12396    delete $this->{reportaddr};
12397    $this->{SIZE} = 0;
12398
12399    $this->{reporttype} = -1;
12400    my $fn = $Counter++ % 99999;
12401#    $this->{fn}         = maillogNewFileName();
12402
12403
12404    $this->{msgtime} = '';
12405    $this->{msgtime} = $uniqueIDPrefix if  $uniqueIDPrefix;
12406    my $tstamp = substr( time(), 1, 5 );
12407
12408    $this->{uniqueid} = sprintf( "%05d-%05d", $tstamp, $fn);
12409    $this->{msgtime} .= $this->{uniqueid};
12410
12411	$this->{fn}         = $this->{msgtime};
12412	$this->{mailInSession}++ if $this->{lastcmd} =~ /mail from/io;
12413
12414}
12415# dropreply
12416# read from server, but ignore it
12417
12418sub dropreply {
12419    my ($fh, $l) = @_;
12420    my $this = $Con{$fh};
12421    d("dropreply: $l");
12422    if ($l =~ /^250 .*/) {
12423        $this->{getline} = \&reply;
12424    }
12425}
12426
12427
12428# a line of input has been received from the smtp client
12429sub getline {
12430    my ( $fh, $l ) = @_;
12431    d('getline');
12432    my $this   = $Con{$fh};
12433    my $server = $this->{friend};
12434    my $friend=$Con{$server};
12435    my $cli=$this->{friend};
12436    my $reply;
12437    my $ip = $this->{ip};
12438    d("gl: <$l>");
12439    if (!DenyStrictOK( $fh, $this->{ip} ) ) {
12440			$Stats{denyStrict}++;
12441			NoLoopSyswrite($fh,"521 $myName does not accept mail from network $ip - closing transmission\r\n");
12442            done($fh);
12443            return;
12444	}
12445    my $cliIP = $Con{$cli}->{ip} || $cli->peerhost();
12446    my $serIP = $fh->peerhost();
12447
12448
12449if (   ! $this->{greetingSent}
12450        && ! $this->{relayok}
12451        && &matchFH($fh,@lsnI)
12452        && ! matchIP($this->{ip},'whiteListedIPs',$fh)
12453        && ! matchIP($this->{ip},'ispip',$fh)
12454        && ! matchIP($this->{ip},'noPB',$fh)
12455        && ! matchIP($this->{ip},'noDelay',$fh)
12456        && ! matchIP($this->{ip},'noBlockingSenderBaseIPs', $fh)
12457        && ! matchIP($this->{ip},'noBlockingIPs', $fh)
12458        && ! matchIP($this->{ip},'noProcessingIPs',$fh)
12459        && ! matchIP($this->{ip},'noHelo',$fh) )
12460    {
12461       pbAdd($fh, $this->{ip}, $etValencePB, "EarlyTalker")
12462       ;
12463       $this->{prescore} += $etValencePB;
12464       my $err = "554 5.7.1 Misbehaved SMTP session (EarlyTalker)";
12465       my $l1 = $l;
12466       $l1 =~ s/\r|\n//go;
12467       my $emergency;
12468       if ($l1 =~ /$NONPRINT/o) {
12469           $l1 = 'non printable hex data';
12470           $emergency = 1;
12471       }
12472       if ($l =~ /^([^\x00-\x1F\x7F-\xFF]+)/o) {
12473           $this->{lastcmd} = $1;
12474           push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12475       }
12476       if ($etValencePB || $emergency) {
12477           mlog($fh, "[EarlyTalker] got '$l1' from the client before the '220 ...' server greeting was sent - rejecting connection", 1) if $SessionLog;
12478           if ($emergency) {
12479               mlog($fh, "[EarlyTalker] All connections from IP $this->{ip} will be rejected by assp for the next 15-30 minutes.", 1);
12480               NoLoopSyswrite($fh,$err."\r\n");
12481               $EmergencyBlock{$this->{ip}} = time;
12482               done($fh);
12483               return;
12484           } else {
12485               seterror( $fh, $err, 1 );
12486               return;
12487           }
12488       } else {
12489           mlog($fh, "info: [EarlyTalker] got '$l1' from client before the server greeting '220 ...' was sent - this misbehave is currently ignored, because 'etValencePB' is set to zero", 1) if $SessionLog >= 2 && ! $this->{relayok};
12490           $this->{greetingSent} = 1;
12491       }
12492    } elsif (! $this->{greetingSent}) {
12493       $this->{greetingSent} = 1;
12494       mlog($fh, "info: [EarlyTalker] client has sent data before the server greeting '220 ...' was sent - this misbehave is currently ignored for this IP", 1) if $SessionLog >= 2 && ! $this->{relayok};
12495       mlog($fh, "info: [EarlyTalker] client has sent data before the server greeting '220 ...' was sent - this misbehave is currently ignored, because a relayed/local connection is in use", 1) if $SessionLog >= 2 && $this->{relayok};
12496    } else {
12497       $this->{greetingSent} = 1;
12498    }
12499
12500    if ($Con{$server}->{mtaSSLfailed}) {
12501        sendque($fh, "451 4.7.1 Local configuration error, please try again later\r\n");
12502        return;
12503    }
12504    my 	$ret  = &matchIP($ip,'noTLSIP',1);
12505	my ( $noTLSIPip, $iplimit ) = split( / /o, $ret, 2 );
12506
12507	my $ct;
12508	my $count;
12509	if (exists $SSLfailed{$ip}) {
12510        my $data = $SSLfailed{$ip};
12511        ($ct, $count) = split( /:/o, $data, 2 );
12512        }
12513	my $iplimit;
12514
12515    if ((exists $SSLfailed{$this->{ip}} && !$count) or
12516			(&matchIP($this->{ip},'noTLSIP',$fh,1) && !$iplimit) or
12517
12518			matchFH($fh,@lsnNoTLSI)) {
12519
12520    	$this->{SSLnotOK} = $this->{ip};
12521
12522    }
12523
12524
12525
12526	if ( $l =~ /^ *(helo|ehlo) .*?([^<>,;\"\'\(\)\s]+)/i ) {
12527        $this->{greeting} = $1;
12528        $this->{lastcmd} = $1;
12529        my $helo  = $2;
12530        my $helo2 = $helo;
12531        $helo =~ s/\s//g;
12532		my $w = 60;
12533		$this->{messagereason} = "invalid HELO: '$helo'";
12534		if ( !$helo or $helo eq '' or $helo eq ' ' or $helo =~ /^\s*$/  ) {
12535				pbAdd($fh,$ip,$w,"invalidHELO") ;
12536				$this->{prescore} += $w;
12537				$this->{invalidhelofound} = 1;
12538				pbWhiteDelete( $fh , $this->{ip} );
12539    	}
12540
12541        $this->{cliSSL}=0;
12542        $helo =~ s/(\W)/\\\$1/g;
12543        $this->{helo} = $helo2;
12544        my $ptr;
12545
12546        if (! $this->{relayok}) {
12547            $ptr = $this->{PTR};
12548            if (! $ptr && $this->{ip} !~ /(?:127\.0\.0\.1|::1)$/io) {
12549                $this->{PTR} = $ptr = getRRData($this->{ip},'PTR');
12550            }
12551            $this->{PTR} = $ptr = $localhostname || 'localhost' if (! $ptr && $this->{ip} =~ /(?:127\.0\.0\.1|::1)$/io);
12552        } elsif ($HideIP or $HideHelo) {
12553
12554            $helo2 = $HideHelo if $HideHelo;
12555            $this->{rcvd} =~ s/\[$IPRe\]/[$HideIP]/o if $HideIP;
12556
12557        } elsif ($HideIPandHelo) {
12558            my %fake;
12559            $fake{$1} = $2 while (lc $HideIPandHelo =~ /(ip|helo)\s*=\s*(\S+)/iog);
12560            $helo2 = $fake{helo} if exists $fake{helo};
12561            $this->{rcvd} =~ s/\[$IPRe\]/[$fake{ip}]/o if exists $fake{ip};
12562        }
12563        $ptr =~ s/\.$//o;
12564        if ($ptr) {
12565            $this->{rcvd}=~s/=host/$ptr/o;
12566        } else {
12567            $this->{rcvd}=~s/=host/$helo2/o;
12568        }
12569        $this->{rcvd}=~s/=\)/=$helo2\)/o;
12570
12571
12572        my $prot = ("$fh" =~ /SSL/io) ? 'SMTPS' : 'SMTP';
12573        $prot = 'E' . $prot if lc($this->{greeting}) eq 'ehlo';
12574        $this->{rcvd} =~ s/\*SMTP\*/$prot/o;
12575        $this->{rcvd} = &headerWrap( $this->{rcvd} );    # wrap long lines
12576        $l = "$this->{greeting} $localhostname\r\n" if $myHelo == 2 && $localhostname;
12577        $l = "$this->{greeting} $myName\r\n" if $myHelo && ($myHelo == 1 or !$localhostname);
12578		$l = "$this->{greeting} $this->{ip}\r\n" if $myHelo == 3;
12579	} elsif ( $CanUseIOSocketSSL  && !$this->{SSLnotOK} && ($l =~ /STARTTLS/io  )) {
12580
12581        # write directly to $fh, bypassing buffering
12582        $fh->write("220 2.0.0 Ready to start TLS\r\n");
12583
12584        # the value of $fh changes when converted to SSL
12585        my $oldfh = "" . $fh;
12586		$IO::Socket::SSL::DEBUG = $SSLDEBUG;
12587        # stop watching old filehandle
12588        $readable->remove($fh);
12589        $writable->remove($fh);
12590
12591        # convert to SSL
12592		my $try = 4;
12593		my $ssl;
12594		my $fail = 0;
12595        eval{$fh->blocking(1);};
12596    	eval{eval{($ssl,$fh) = &switchSSLClient($fh);};
12597        	if (!$ssl || $fh !~ /IO::Socket::SSL/) {
12598        	my $error = IO::Socket::SSL::errstr();
12599            mlog($oldfh, "SSL negotiation with client $ip failed: $error") if $SSLLog;
12600            $fail = 1;
12601
12602      		setSSLfailed($ip);
12603            $readable->add($fh);
12604            $writable->remove($fh);
12605
12606        	}
12607        };
12608        return if $fail;
12609        if (!$Con{$server}->{mtaSSL} && "$ssl" =~ /SSL/i) {
12610            mlog($oldfh, "warning: SSL to client on port $this->{localport} but no SSL to our MTA") if $SSLLog>2;
12611        }
12612
12613        # update Received: header to show SSL
12614        $this->{rcvd} =~ s/( with E?SMTP[0-9]+)/$1+SSL/;
12615
12616        # copy data from old $fh
12617        $Con{$fh}           = $Con{$oldfh};
12618        $Con{$fh}->{client} = $fh;
12619        $SMTPSession{$fh}   = $SMTPSession{$oldfh};
12620
12621        # clean up old $fh
12622        delete $Con{$oldfh};
12623        delete $SocketCalls{$oldfh};
12624        delete $SMTPSession{$oldfh};
12625
12626        # set up new $fh
12627        $SocketCalls{$fh} = \&SMTPTraffic;
12628        $readable->add($fh);
12629
12630        d("SSL: $fh $Con{$fh}");
12631        return;
12632
12633	} elsif($l=~/^(\s*AUTH([^\r\n]*))\r?\n/io) {
12634        my $ffr = $1;
12635        my $authmeth = $2;
12636
12637        if ( ! $this->{relayok} && $this->{DisableAUTH} )
12638        {
12639            $this->{lastcmd} = 'AUTH';
12640            push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12641            $this->{prepend}="[unsupported_$this->{lastcmd}]";
12642            mlog($fh,"$this->{lastcmd} not allowed");
12643            if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
12644                MaxErrorsFailed($fh,
12645                "502 $this->{lastcmd} not supported\r\n421 <$myName> closing transmission\r\n",
12646                "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after $this->{lastcmd}");
12647                return;
12648            }
12649            sendque($fh, "502 $this->{lastcmd} not supported\r\n");
12650            return;
12651        }
12652
12653        my $ip = &ipNetwork( $this->{ip}, 1);
12654        if (!DenyStrictOK( $fh, $this->{ip} ) ) {
12655			$Stats{denyStrict}++;
12656			NoLoopSyswrite($fh,"521 $myName does not accept mail from network $ip - closing transmission\r\n");
12657            done($fh);
12658            return;
12659		}
12660        $AUTHErrors{$ip} = $MaxAUTHErrors + 1 if matchIP( $this->{ip}, 'denySMTPConnectionsFromAlways', $fh );
12661        $AUTHErrors{$ip} = $MaxAUTHErrors + 1 if matchIP( $ip, 'denySMTPConnectionsFromAlways', $fh );
12662        if ($MaxAUTHErrors
12663    	&& !$this->{relayok}
12664    	&& !$this->{nopb}
12665
12666        && !$this->{ispip}
12667        && !$this->{noprocessing}
12668        && !$this->{whitelisted}
12669		&& !$this->{acceptall}
12670        && $AUTHErrors{$ip} > $MaxAUTHErrors) {
12671            $this->{prepend}='[MaxAUTHErrors]';
12672            NoLoopSyswrite($fh,"521 $myName does not accept mail - closing transmission - too many previouse AUTH errors from network $ip\r\n");
12673            mlog($fh,"too many ($AUTHErrors{$ip}) AUTH errors from network $ip",1) if $ConnectionLog;
12674            pbAdd( $fh, $this->{ip}, 'autValencePB', 'AUTHErrors' ) if ! matchIP($this->{ip},'noPB',0,1);
12675            $AUTHErrors{$ip}++;
12676            done($fh);
12677            return;
12678        }
12679
12680        if ($CanUseIOSocketSSL &&
12681
12682            ! $SSLfailed{$this->{ip}} &&
12683            $friend->{donotfakeTLS} &&
12684            ! $this->{gotSTARTTLS} &&
12685            ! $this->{TLSqueue} &&
12686            "$server" !~ /SSL/io &&
12687            ! &matchIP($this->{ip},'noTLSIP',$fh,1) &&
12688            ! &matchFH($fh,@lsnNoTLSI)
12689        ) {
12690            NoLoopSyswrite($server,"STARTTLS\r\n");
12691            $friend->{getline} = \&replyTLS;
12692            $this->{TLSqueue} = $ffr;
12693            mlog($fh,"info: injected STARTTLS request to " . $server->peerhost()) if $ConnectionLog;
12694            return;
12695        }
12696        $authmeth =~ s/^\s+//o;
12697        $authmeth =~ s/\s+$//o;
12698
12699        $this->{prepend} = "[Authentication]";
12700        if ($authmeth =~ /(plain|login)\s*(.*)/io) {
12701            $authmeth = lc $1;
12702            my $authstr = base64decode($2);
12703            mlog($fh,"info: authentication - $authmeth is used") if $AUTHLogUser;
12704            if ($authmeth eq 'plain' and $authstr) {
12705                ($this->{userauth}{foruser},$this->{userauth}{user},$this->{userauth}{pass}) = split(/ |\0/so,$authstr);
12706                $this->{userauth}{stepcount} = 0;
12707                $this->{userauth}{authmeth} = 'plain';
12708                if ($AUTHLogUser) {
12709                    my $tolog = "info: authentication (PLAIN) realms - foruser:$this->{userauth}{foruser}, user:$this->{userauth}{user}";
12710                    $tolog .= ", pass:$this->{userauth}{pass}" if $AUTHLogPWD;
12711                    mlog($fh,$tolog);
12712                }
12713            } elsif ($authmeth eq 'plain' and ! $authstr) {
12714                $this->{userauth}{stepcount} = 1;
12715                $this->{userauth}{authmeth} = 'plain';
12716            } elsif ($authmeth eq 'login' and $authstr) {
12717                $this->{userauth}{user} = $authstr;
12718                $this->{userauth}{stepcount} = 1;
12719                $this->{userauth}{authmeth} = 'login';
12720            } else {
12721                $this->{userauth}{stepcount} = 2;
12722                $this->{userauth}{authmeth} = 'login';
12723            }
12724        }
12725        $this->{lastcmd} = 'AUTH';
12726        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12727        $this->{doneAuthToRelay} = 1;
12728        sendque($server,$l);
12729        return;
12730
12731    } elsif ($this->{userauth}{stepcount}) {
12732        if ($this->{userauth}{authmeth} eq 'plain') {
12733            $this->{userauth}{stepcount} = 0;
12734            $l =~ /([^\r\n]*)\r\n/o;
12735            my $authstr = base64decode($1);
12736            ($this->{userauth}{foruser},$this->{userauth}{user},$this->{userauth}{pass}) = split(/ |\0/o,$authstr);
12737           	my $authuser = $this->{userauth}{user};
12738
12739 			return if $authuser && !SameAUTHuserOK ($fh,$authuser );
12740
12741            if ($AUTHLogUser) {
12742                my $tolog = "info: authentication (PLAIN) realms - foruser:$this->{userauth}{foruser}, user:$this->{userauth}{user}";
12743                $tolog .= ", pass:$this->{userauth}{pass}" if $AUTHLogPWD;
12744                mlog($fh,$tolog);
12745            }
12746            sendque($server,$l);
12747            return;
12748        } elsif ($this->{userauth}{stepcount} == 2) {
12749            $this->{userauth}{stepcount} = 1;
12750            $l =~ /([^\r\n]*)\r\n/o;
12751            $this->{userauth}{user} = base64decode($1);
12752            sendque($server,$l);
12753            return;
12754        } else {
12755            $this->{userauth}{stepcount} = 0;
12756            $l =~ /([^\r\n]*)\r\n/o;
12757            $this->{userauth}{pass} = base64decode($1);
12758            if ($AUTHLogUser) {
12759                my $tolog = "info: authentication (LOGIN) realms - user:$this->{userauth}{user}";
12760                $tolog .= ", pass:$this->{userauth}{pass}" if $AUTHLogPWD;
12761                mlog($fh,$tolog);
12762            }
12763            sendque($server,$l);
12764            return;
12765        }
12766	} elsif(&syncCanSync() && $enableCFGShare && $isShareSlave && $l=~/^ *ASSPSYNCCONFIG\s*([^\r\n]+)\r\n/o ) {
12767        my $pass = $1;
12768        mlog(0,"info: got ASSPSYNCCONFIG request from $this->{ip}") if $ConnectionLog >=2;
12769        $this->{lastcmd} = 'ASSPSYNCCONFIG';
12770        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12771        my @tservers = split(/\|/o, $syncServer);
12772        my @servers;
12773        my %se;
12774        foreach (@tservers) {
12775            s/\s//go;
12776            s/\:\d+$//o;
12777            if ($_ =~ /^$IPRe$/o) {
12778                push(@servers, $_);
12779                $se{$_} = $_;
12780                next;
12781            }
12782            my $ip = eval{inet_ntoa( scalar( gethostbyname($_) ) );};
12783            if ($ip) {
12784                push(@servers, $ip);
12785                $se{$ip} = $_;
12786                next;
12787            } else {
12788                mlog(0,"syncCFG: error - unable to resolve ip for syncServer name $_ - $@");
12789            }
12790        }
12791        if (! @servers or ! (@servers = grep { $this->{ip} eq $_ } @servers )) {
12792            NoLoopSyswrite( $fh, "502 $this->{lastcmd} not implemented $this->{ip} - @servers\r\n" );
12793            mlog($fh,"syncCFG: error - got 'ASSPSYNCCONFIG' command from wrong ip $this->{ip}");
12794            done($fh);
12795            return;
12796        }
12797        if (Digest::MD5::md5_base64($syncCFGPass) ne $pass) {
12798            NoLoopSyswrite( $fh, "500 $this->{lastcmd} wrong authentication - check you configuration\r\n" );
12799            mlog($fh,"syncCFG: error - got wrong password in 'ASSPSYNCCONFIG' command from $this->{ip}");
12800            done($fh);
12801            return;
12802        }
12803        done2($server);
12804        my $ip = $this->{ip};
12805        $this->{syncServer} = $se{$ip};
12806        $this->{getline} = \&syncRCVData;
12807        NoLoopSyswrite($fh,"250 OK start the config sync\r\n");
12808        return;
12809    } elsif($l=~/^ *ASSPSYNCCONFIG\s*([^\r\n]+)?\r\n/o ) {
12810        my $pass = $1;
12811        mlog(0,"info: got ASSPSYNCCONFIG request from $this->{ip}") if $ConnectionLog >=2;
12812        $this->{lastcmd} = 'ASSPSYNCCONFIG';
12813        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12814        if (Digest::MD5::md5_base64($syncCFGPass) ne $pass) {
12815            NoLoopSyswrite( $fh, "502 $this->{lastcmd} not implemented\r\n" );
12816            mlog($fh,"syncCFG: error - got syncCFG request, but this is not an 'isShareSlave' and got wrong password in 'ASSPSYNCCONFIG' command from $this->{ip}");
12817            done($fh);
12818            return;
12819        }
12820        NoLoopSyswrite( $fh, "500 $this->{lastcmd} - sync peer $this->{ip} is not registered on $myName or this is not an isShareSlave\r\n" );
12821        mlog($fh,"syncCFG: error - got 'ASSPSYNCCONFIG' command from ip $this->{ip} - the request will be ignored - check your configuration");
12822        done($fh);
12823        return;
12824
12825    } elsif ($l=~/^ *($notAllowedSMTP)/io) {
12826        $this->{lastcmd} = $1;
12827        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12828
12829
12830    } elsif ( $l =~ /mail from:\s*<?($EmailAdrRe\@$EmailDomainRe|\s*)>?/io ) {
12831
12832        my $RO_e = $1;
12833
12834        $RO_e = "$RO_e" . "@" . "$defaultLocalHost" if $defaultLocalHost && $RO_e !~ /\@/i;
12835		my $fr   = $RO_e;
12836
12837        stateReset($fh); # reset everything
12838        $this->{lastcmd} = 'MAIL FROM';
12839        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12840
12841        if ( ! $this->{relayok} && $this->{DisableAUTH} && $l =~ /\sAUTH=/io )
12842        {
12843            $this->{lastcmd} = 'AUTH';
12844            push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
12845            $this->{prepend}="[unsupported_$this->{lastcmd}]";
12846            mlog($fh,"$this->{lastcmd} not allowed");
12847            if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
12848                MaxErrorsFailed($fh,
12849                "502 $this->{lastcmd} not supported\r\n421 <$myName> closing transmission\r\n",
12850                "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after $this->{lastcmd}");
12851                return;
12852            }
12853            sendque($fh, "502 $this->{lastcmd} not supported\r\n");
12854            return;
12855        }
12856
12857        if($EnforceAuth && &matchFH($fh,@lsn2I) && ! $this->{authenticated} && ! $this->{DisableAUTH}) {
12858            NoLoopSyswrite($fh,"530 5.7.0 Authentication required\r\n",0);
12859            mlog($fh,"$fr submited without previouse AUTH - 'EnforceAuth' is set to 'ON' for 'listenPort2'",1);
12860            done($fh);
12861            return;
12862        }
12863
12864# authentication on relayserver
12865        if ($CanUseAuthenSASL &&
12866        	! $this->{doneAuthToRelay} &&
12867            $this->{relayok} &&
12868            scalar keys %{$this->{authmethodes}} &&
12869            $relayAuthUser &&
12870            $relayAuthPass
12871           )
12872        {
12873            $this->{doneAuthToRelay} = 1;
12874            $this->{doneAuthToRelay} = 1;
12875            $this->{sendAfterAuth} = $l;
12876            foreach ('PLAIN','LOGIN','CRAM-MD5','DIGEST-MD5') {
12877                $this->{AUTHmechanism} = $_ if exists $this->{authmethodes}->{$_};
12878            }
12879            $this->{AUTHmechanism} = 'PLAIN' unless $this->{AUTHmechanism};
12880            mlog($fh,"info: starting authentication - AUTH $this->{AUTHmechanism}") if $SessionLog >= 2;
12881            $this->{AUTHclient} =
12882                Authen::SASL->new(
12883                                    mechanism => $this->{AUTHmechanism},
12884                                    callback  => {
12885                                    user     => $relayAuthUser,
12886                                    pass     => $relayAuthPass,
12887                                    authname => $relayAuthUser
12888                                },
12889                                debug => $ThreadDebug
12890                )->client_new('smtp');
12891            @{$this->{AUTHclient} . 'AUTHclient'} = ();
12892            my $str = $this->{AUTHclient}->client_start;
12893            push (@{$this->{AUTHclient} . 'AUTHclient'}, MIME::Base64::encode_base64($str, ''))
12894                 if defined $str and length $str;
12895
12896            NoLoopSyswrite($server,'AUTH ' . $this->{AUTHclient}->mechanism . "\r\n");
12897            $friend->{getline} = \&replyAUTH;
12898
12899            return;
12900        }
12901# end authentication on relayserver
12902
12903        #enforce valid email address pattern
12904
12905
12906
12907        #enforce valid email address pattern
12908
12909
12910
12911		if ( $RO_e && $CanUseAddress && $DoRFC522Sender && !$this->{relayok}) {
12912			if ($RO_e && $RO_e !~ /\.($TLDSRE|local)\b/i  && $RO_e !~/$defaultLocalHost$/i ) {
12913               # no valid TLD
12914
12915                $this->{prepend} = "[MalformedAddress]";
12916                mlog( $fh, "malformed address: invalid TLD in '$RO_e'"  );
12917                $Stats{msgverify}++;
12918
12919                delayWhiteExpire($fh);
12920                NoLoopSyswrite( $fh, "553 TLD invalid in '$RO_e'\r\n" );
12921
12922                $this->{messagereason}="invalid TLD";
12923				pbAdd($fh,$this->{ip},$mxaValencePB,"invalidTLD");
12924
12925                done($fh);
12926                return;
12927
12928			}
12929        }
12930        my $valid;
12931        if ( $RO_e !~ /$defaultLocalHost/i && $RO_e && $CanUseAddress && $DoRFC522Sender && !$this->{relayok} && $RO_e !~/^SRS/) {
12932
12933            eval { $valid = Email::Valid->address($RO_e); };
12934            if ( !$valid && !$@) {
12935
12936                # couldn't understand sender
12937
12938            	$this->{prepend} = "[MalformedAddress]";
12939                mlog( $fh, "malformed address: '$RO_e' - failed $Email::Valid::Details check" );
12940                $Stats{msgverify}++;
12941
12942                delayWhiteExpire($fh);
12943                NoLoopSyswrite( $fh, "553 Malformed address: $RO_e\r\n" );
12944
12945            	$this->{messagereason}="Malformed address";
12946				pbAdd($fh,$this->{ip},$mxaValencePB,"MalformedAddress");
12947
12948                done($fh);
12949                return;
12950
12951            }
12952
12953        }    # reset everything
12954
12955        $this->{mailfrom} = $fr;
12956        my $t    = time;
12957        my $mf   = lc $this->{mailfrom};
12958        $mf = batv_remove_tag($fh,$mf,'');
12959        $this->{mailfrom} = $mf;
12960
12961
12962        my $mfd;
12963        $mfd = $1 if $mf=~/\@(.*)/o;
12964        my $mfdd;
12965        $mfdd = $1 if $mf=~/(\@.*)/o;
12966
12967    	foreach my $adr ( split( " ", $this->{rcpt} ) ) {
12968 			$this->{newrcpt} .= "$adr " if $adr =~ /$mfd/;
12969 			last if $AddIntendedForHeader == 1;
12970    	}
12971
12972
12973		$wildcardUser = lc $wildcardUser;
12974        my $alldd        = "$wildcardUser$mfdd";
12975        my $defaultalldd = "*$mfdd";
12976
12977
12978        if($l=~/SIZE=(\d*)\s/io) {
12979            my $size = $1;
12980            $this->{SIZE}=$size;
12981 #           mlog($fh,"info: found message size announcement: " . &formatNumDataSize($size)) if $SessionLog == 2;
12982
12983            if ( ($this->{relayok} && $maxSize
12984                    && ( $size > $maxSize )) or (!$this->{relayok} && $maxSizeExternal
12985                    && ( $size > $maxSizeExternal )))
12986            {
12987                 my $max = $this->{relayok} ? &formatNumDataSize($maxSize) : &formatNumDataSize($maxSizeExternal);
12988                my $err = "552 message exceeds MAXSIZE";
12989                mlog( $fh, "error: message exceeds maxSize $max!" );
12990                $err = $maxSizeError if ($maxSizeError);
12991                $err =~ s/MAXSIZE/$max/go;
12992                NoLoopSyswrite( $fh, "$err\r\n" );
12993        		done($fh);
12994
12995                return;
12996            }
12997
12998            if (!$this->{relayok}) {
12999
13000                if ($npSize && $size > $npSize) {
13001                    $this->{ismaxsize}=1 ;
13002                    $this->{noprocessing}=1;
13003#                    mlog($fh,"message proxied without processing - message size ($size) is above $npSize (npSize).",1);
13004                    $this->{passingreason} = "message size ($size) is above $npSize (npSize)";
13005                }
13006            }
13007        }
13008        $this->{doneAuthToRelay} = 1 if($l=~/ AUTH=.+/io);
13009
13010########################################## !relayok ############
13011
13012		$this->{externalsenderok} = 1 if !$this->{externalsenderok} && $EmailSenderOK && matchSL( $mf, 'EmailSenderOK' );
13013        $this->{externalsenderok} = 1 if !$this->{externalsenderok} && (   matchSL( $mf, 'EmailAdmins', 1 ));
13014        $this->{externalsenderok} = 1 if !$this->{externalsenderok} && ( $EmailSenderLocalAddress && &localmailaddress($fh,$mf) );
13015        $this->{externalsenderok} = 1 if !$this->{externalsenderok} && ( $EmailAdmins && matchSL( $mf, 'EmailAdmins'));
13016        $this->{externalsenderok} = 1 if !$this->{externalsenderok} && ( $EmailAdminReportsTo && $mf =~ /$EmailAdminReportsTo/i );
13017
13018        $this->{senderok} = 2 if !$this->{externalsenderok} && ( $EmailSenderNotOK && matchSL( $mf, 'EmailSenderNotOK' ) ) ;
13019        $this->{senderok} = 3 if !$this->{externalsenderok} && ( $EmailSenderIgnore && matchSL( $mf, 'EmailSenderIgnore' ) ) ;
13020
13021		$this->{noreply} = 1 if ( $EmailSenderNoReply && matchSL( $mf, 'EmailSenderNoReply' ) ) ;
13022		if (   matchSL( $mf, 'EmailAdmins', 1 )
13023            or $mf eq lc($EmailAdminReportsTo) )
13024        {
13025        	$this->{externalsenderok} = 1;
13026            $this->{adminok} = 1;
13027        }
13028
13029		$this->{externalsenderok} = 1 if $this->{notspamtag};
13030
13031
13032
13033
13034        if (!$this->{relayok}) {
13035			eval {
13036            if ($allLogRe
13037                && (   $mf =~ /$allLogReRE/
13038                    || $this->{ip}   =~ /$allLogReRE/
13039                    || $this->{helo} =~ /$allLogReRE/)
13040              ) {
13041               $this->{alllog}=1;
13042            }
13043            };
13044            eval {
13045            if(!$this->{contentonly} && $contentOnlyRe && $this->{header}=~/($contentOnlyReRE)/) {
13046                mlogRe($fh,($1||$2),"Contentonly");
13047                pbBlackDelete($fh,$this->{ip});
13048                $this->{contentonly}=1;
13049                $this->{ispip}=1;
13050                $this->{noblockingips} = 1;
13051            }
13052			};
13053
13054            if ($Con{$server}->{relayok} && $WhitelistAuth){
13055                $this->{whitelisted}="authenticated";
13056                $this->{relayok}="authenticated";
13057                $this->{passingreason} = "authenticated";
13058
13059                # whitelist authenticated users
13060            }
13061            $this->{red} = $this->{redlist} = "$mf in RedList"
13062              if ( $Redlist{"$alldd"}
13063                || $Redlist{"$defaultalldd"}
13064                || $Redlist{"$mf"} );
13065
13066
13067
13068			if (matchIP( $this->{ip}, 'noBlockingIPs', 0,1 ))
13069			{
13070				$this->{noblockingips} = 1;
13071				$this->{contentonly}=1;
13072            }
13073            my $ret = matchIP( $this->{ip}, 'noProcessingIPs', $fh );
13074            if ($noProcessingIPs
13075                && $ret
13076                &&  !matchIP( $this->{ip}, 'NPexcludeIPs', 0, 1 )
13077
13078                && !$this->{nonoprocessing}
13079                 )
13080            {
13081                $this->{noprocessing}  		= 1;
13082                $this->{noprocessingip}  	= 1;
13083                $this->{white}  			= 1;
13084
13085                $this->{passingreason} 		= "noProcessingIPs '$ret'" if !$this->{passingreason};
13086            }
13087
13088			if (  $noNoProcessing
13089
13090                && matchSL( $mf, 'noNoProcessing' ) )
13091            {
13092
13093                $this->{nonoprocessing}  = 1;
13094
13095            }
13096
13097           	if (   !$this->{noprocessing}
13098           		&& !$this->{nonoprocessing}
13099                && $noProcessingDomains
13100
13101                && $mf =~ /($NPDRE)/ )
13102            {
13103
13104                $this->{noprocessing}  = 1;
13105                mlogRe( $fh, ($1||$2), "noProcessingDomains" );
13106                $this->{passingreason} = "noProcessingDomains" if !$this->{passingreason};
13107            }
13108
13109
13110
13111
13112		$this->{localuser} = localmail($mf);
13113
13114		if (!$this->{whitelisted} && $whiteReRE ) {
13115        		WhiteOk($fh) ;
13116    	}
13117
13118		if (   !$this->{noprocessing} ) {
13119    		if (!$this->{noprocessing} && matchIP( $ip, 'noProcessingIPs', 0, 1 ) &&  !matchIP( $ip, 'NPexcludeIPs', 0, 1 ) )  {
13120				$this->{noprocessing} = 1;
13121                $this->{passingreason} = "$noProcessingIPs '$ip'" if !$this->{passingreason};
13122            }
13123            if (! $this->{whitelisted} && $whiteListedDomains && $mf=~/($WLDRE)/) {
13124                mlogRe($fh,($1||$2),"WhiteDomain") ;
13125                $this->{whitedomain}= $1||$2;
13126                $this->{passingreason} = "whiteListedDomains '$this->{whitedomain}'" if !$this->{passingreason};
13127                $this->{whitelisted}=1;
13128            }
13129
13130
13131    		my $mfdd;
13132    		$mfdd = $1 if $mf=~/(\@.*)/o;
13133   		 	my $alldd        = "*$mfdd";
13134
13135            if (   !$this->{whitelisted}
13136                && !localmail($mf)
13137                &&  $Whitelist{$alldd} )
13138            {
13139
13140                $this->{whitelisted} = "$alldd";
13141                $Whitelist{$alldd}        = $t if !$this->{red};
13142
13143                $this->{passingreason}    = "$alldd";
13144
13145            }
13146
13147
13148            my $ret = matchIP( $this->{ip}, 'whiteListedIPs', $fh );
13149            if (  $whiteListedIPs && $ret )
13150            {
13151                $this->{whitelisted}   	= "whiteListedIPs '$ret'";
13152                $this->{white}   		= 1;
13153                $this->{whiteip}   		= 1;
13154                $this->{passingreason} = "whiteListedIPs '$ret'";
13155            }
13156
13157
13158
13159
13160            $this->{ispip} = 1 if ( matchIP( $this->{ip}, 'ispip', $fh ) );
13161            $this->{nopb}  = 1 if ( matchIP( $this->{ip}, 'noPB',  $fh ) );
13162
13163
13164            if ( matchIP( $this->{ip}, 'noPBwhite', $fh ) ) {
13165              	$this->{messagereason} = "noPBwhite";
13166
13167				$this->{nopbwhite} = 1;
13168			}
13169
13170			if (pbWhiteFind( $this->{ip} ) && !$this->{nopbwhite}) {
13171            	$this->{pbwhite} = 1;
13172            	$this->{messagereason} = "PBwhite";
13173
13174  			}
13175
13176            $this->{nohelo} = 1 if ( matchIP( $this->{ip}, 'noHelo', $fh ) );
13177            $this->{mHBIRE} = 1
13178              if ( $heloBlacklistIgnore && $this->{helo} =~ $HBIRE );
13179
13180            if ($this->{mailfrom}=~/$BSRE/) {
13181                $this->{prepend} = '[isbounce]';
13182#                mlog($fh,"bounce message detected") if (! $this->{isbounce} && ! $this->{relayok});
13183                $this->{isbounce}=1;
13184            }
13185
13186            $this->{nodelay} = 'noDelay' if matchIP( $this->{ip}, 'noDelay', $fh );
13187            $this->{nodelay} = 'noDelayAddresses' if matchSL($this->{mailfrom},'noDelayAddresses');
13188            $this->{acceptall} = 1
13189              if matchIP( $this->{ip}, 'acceptAllMail', $fh );
13190            $this->{NPexcludeIPs} = 1
13191              if matchIP( $this->{ip}, 'NPexcludeIPs', $fh );
13192
13193            if ( $this->{whitelisted} ) {
13194                pbBlackDelete( $fh, $this->{ip} );
13195
13196                pbWhiteAdd( $fh, $this->{ip}, "Whitelisted" );
13197            }
13198            if ( $this->{noprocessing} & !$this->{NPexcludeIPs} ) {
13199                pbBlackDelete( $fh, $this->{ip} );
13200
13201                pbWhiteAdd( $fh, $this->{ip}, "NoProcessing" );
13202            }
13203
13204            my $ip=$this->{ip};
13205
13206            my $myip = &ipNetwork( $ip, $PenaltyUseNetblocks );
13207            my ( $ct, $ut, $level, $totalscore, $sip, $reason, $counter ) =
13208              split( ' ', $PBBlack{$myip} );
13209
13210
13211            $this->{pbblack} = 1 if pbBlackFind( $this->{ip} );
13212
13213            if (!$this->{relayok}) {
13214
13215
13216				if (! &FrequencyIPOK($fh)) {
13217            		$this->{skipnotspam} = 0;return;
13218        		}
13219			}
13220			if (   $DoDomainIP
13221                && $this->{pbblack}
13222                && !$this->{pbwhite}
13223                && $maxSMTPdomainIP
13224                && $mfd
13225                && !$this->{nopb}
13226                && !$this->{whitelisted}
13227                && !$this->{rwlok}
13228                && $this->{noprocessing} ne '1'
13229                && !$this->{ispip}
13230                && !$this->{acceptall}
13231                && !$this->{nodelay}
13232                && !$this->{contentonly}
13233                && !$this->{noblockingips}
13234                && (! $ValidateSPF || ($SPFCacheInterval && $SPFCacheObject && [&SPFCacheFind($this->{ip},$mfd)]->[1] ne 'pass'))
13235                && (!$maxSMTPdomainIPWL || ($maxSMTPdomainIPWL &&  $mfd!~/($IPDWLDRE)/))
13236               )
13237            {
13238                $this->{doneDoDomainIP} = 1;
13239                my $myip=&ipNetwork($this->{ip}, $DelayUseNetblocks) . '.';
13240                if ((time - $SMTPdomainIPTriesExpiration{$mfd}) > $maxSMTPdomainIPExpiration) {
13241                    $SMTPdomainIPTries{$mfd} = 1;
13242                    $SMTPdomainIPTriesExpiration{$mfd} = time;
13243                    $myip =~ s/\./\\\./go;
13244                    $SMTPdomainIP{$mfd} = $myip;
13245                } elsif ($myip !~ /^(?:$SMTPdomainIP{$mfd})$/) {
13246                    $SMTPdomainIP{$mfd} .= '|' if $SMTPdomainIP{$mfd};
13247                    $myip =~ s/\./\\\./go;
13248                    $SMTPdomainIP{$mfd} .= $myip;
13249                    $SMTPdomainIPTriesExpiration{$mfd} = time if $SMTPdomainIPTries{$mfd}==1;
13250                    $SMTPdomainIPTries{$mfd}++;
13251                }
13252                my $tlit = &tlit($DoDomainIP);
13253                $tlit = "[testmode]"   if $allTestMode && $DoDomainIP == 1 || $DoDomainIP == 4;
13254                my $DoDomainIP = $DoDomainIP;
13255                $DoDomainIP = 3 if $allTestMode && $DoDomainIP == 1 || $DoDomainIP == 4;
13256                if ( exists $SMTPdomainIPTries{$mfd} && $SMTPdomainIPTries{$mfd} > $maxSMTPdomainIP) {
13257
13258                    $this->{prepend} = "[IPperDomain]";
13259                    $this->{messagereason} = "'$mfdd' passed limit($maxSMTPdomainIP) of ips per domain";
13260
13261                    mlog( $fh, "$tlit $this->{messagereason}")
13262                      if (  ($SessionLog && $SMTPdomainIPTries{$mfd} == $maxSMTPdomainIP + 1)
13263                          ||($SessionLog >= 2 && $SMTPdomainIPTries{$mfd} > $maxSMTPdomainIP + 1));
13264
13265                    pbAdd( $fh, $this->{ip}, 'idValencePB', "LimitingIPDomain" ) if $DoDomainIP != 2;
13266                    if ( $DoDomainIP == 1 ) {
13267                        $Stats{smtpConnDomainIP}++;
13268                        seterror( $fh, "554 5.7.1 too many different IP's for domain '$mfdd'", 1 );
13269                        return;
13270                    }
13271                }
13272            }
13273
13274        	if (&MessageScoreHigh($fh,25)) {
13275                	MessageScore( $fh, 1 );
13276                	return;
13277 			}
13278
13279
13280            }
13281        }
13282
13283############################################ ##############################
13284
13285        #if (serverIsSmtpDestination($server)) {
13286        #$this->{isbounce}=($this->{mailfrom}=~$BSRE ? 1 : 0);
13287        #} elsif ($EnableSRS && $CanUseSRS) {
13288         if ($EnableSRS &&
13289            $CanUseSRS  &&
13290            $this->{relayok} &&
13291            ! localmail($this->{mailfrom}) &&
13292            $this->{mailfrom} !~ $BSRE &&
13293            ! ($SRSno && $this->{mailfrom} && matchSL($this->{mailfrom},'SRSno')))
13294			{
13295
13296            # rewrite sender addresses when relaying through Relay Host
13297            my $tmpfrom;
13298            $this->{prepend} = "";
13299            my $srs = new Mail::SRS(
13300                Secret        => $SRSSecretKey,
13301                MaxAge        => $SRSTimestampMaxAge,
13302                HashLength    => $SRSHashLength,
13303                AlwaysRewrite => 1
13304            );
13305            if (
13306                !eval { $tmpfrom = $srs->reverse( $this->{mailfrom} ) }
13307                && eval {
13308                    $tmpfrom =
13309                      $srs->forward( $this->{mailfrom}, $SRSAliasDomain );
13310                }
13311              )
13312            {
13313                mlog(
13314                    $fh,
13315                    "SRS rewriting sender '$this->{mailfrom}' into '$tmpfrom'",
13316                    1
13317                );
13318                $l =~ s/\Q$this->{mailfrom}\E/$tmpfrom/;
13319            } else {
13320                mlog( $fh, "SRS rewriting sender '$this->{mailfrom}' failed!",
13321                    1 );
13322            }
13323        }
13324
13325    } elsif($l=~/^ *(VRFY|EXPN) *([^\r\n]*)/io) {
13326        $this->{lastcmd} = $1;
13327        my $e=$2;
13328        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
13329
13330        if ( $DisableVRFY && !$this->{relayok} )
13331        {
13332            $this->{prepend}="[unsupported_$this->{lastcmd}]";
13333            mlog($fh,"$this->{lastcmd} not allowed");
13334            if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
13335                MaxErrorsFailed($fh,
13336                "502 $this->{lastcmd} not supported\r\n421 <$myName> closing transmission\r\n",
13337                "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after $this->{lastcmd}");
13338                return;
13339            }
13340            sendque($fh, "502 $this->{lastcmd} not supported\r\n");
13341            return;
13342        }
13343
13344        my ($u,$h);
13345        my ($str, $gen, $day, $hash, $orig_user) = ($e =~ /(prvs=(\d)(\d\d\d)(\w{6})=(.*))/o);
13346        $l =~ s/$str/$orig_user/ if ($orig_user);  # remove BATV-Tag from VRFY address
13347
13348        # recipient replacment should be done next to here !
13349        if ($ReplaceRecpt) {
13350            if ($l=~/ *(?:VRFY|EXPN)\s*<*([^\r\n>]*).*/io) {
13351                my $midpart  = $1;
13352                my $orgmidpart = $midpart;
13353                if ($midpart) {
13354                  my $bpa = 0;
13355                  if($EnableBangPath && $midpart=~/([a-z\-_\.]+)!([a-z\-_\.]+)$/io) {
13356                      $midpart = "$2@$1";
13357                  }
13358                  my $mf = batv_remove_tag(0,lc $this->{mailfrom},'');
13359                  my $newmidpart = RcptReplace($midpart,$mf,'RecRepRegex');
13360                  if (lc $newmidpart ne lc $midpart) {
13361                      $l =~ s/$orgmidpart/$newmidpart/i;
13362                      mlog($fh,"info: $this->{lastcmd} recipient $orgmidpart replaced with $newmidpart");
13363                  }
13364                }
13365            }
13366        }
13367    } elsif ( $l =~ /rcpt to: *(.*)/i ) {
13368        my $e = $1;
13369        $e = batv_remove_tag(0,$e,'');
13370        my ( $u, $h );
13371
13372        #enforce valid email address pattern
13373
13374        if ( $l =~ /rcpt to:\s*<*([^\r\n>]*).*/i ) {
13375                my $RO_e = $1;
13376                if ( $RO_e =~ /($BlockLocalAddressesReRE)/ && $this->{relayok}) {
13377
13378
13379                    sendque( $fh, "553 Malformed address: $RO_e\r\n" );
13380                    $this->{prepend} = "[BlockedLocal]";
13381                    mlog( $fh, "address '$RO_e'  blocked by BlockLocalAddressesRe: '$1'" );
13382                    $Stats{rcptRelayRejected}++;
13383                    delayWhiteExpire($fh);
13384                    return;
13385                }
13386        }
13387
13388        if ( $EnableSRS && $CanUseSRS ) {
13389            if ( $this->{isbounce} ) {
13390
13391                # validate incoming bounces
13392                my $tmpto;
13393                my $srs = new Mail::SRS(
13394                    Secret        => $SRSSecretKey,
13395                    MaxAge        => $SRSTimestampMaxAge,
13396                    HashLength    => $SRSHashLength,
13397                    AlwaysRewrite => 1
13398                );
13399                if ( $e =~ /^<?(SRS0[=+-][^\r\n>]*).*/i ) {
13400                    if ( eval { $tmpto = $srs->reverse($1) } ) {
13401                        $l =~ s/\Q$1\E/$tmpto/;
13402                        $e = <$tmpto>;
13403                    } else {
13404                        $this->{invalidSRSBounce} = 1;
13405                    }
13406                } elsif ($e=~/^<?(SRS1[=+-][^\r\n>]*)/io) {
13407                    if (eval{$tmpto=$srs->reverse($1)}) {
13408                        if (eval{$_=$srs->reverse($tmpto)}) {
13409                            $l=~s/\Q$1\E/$_/;
13410                            $e=<$_>;
13411                        } else {
13412                            $this->{prepend}="[RelayAttempt]";
13413                            $this->{messagereason} = "user not local; please try <$tmpto> directly";
13414                            mlog( $fh, $this->{messagereason} );
13415                            $Stats{rcptRelayRejected}++;
13416                            pbAdd($fh,$this->{ip},'rlValencePB','RelayAttempt',0);
13417                            if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
13418                                NoLoopSyswrite( $fh, "551 5.7.1 User not local; please try <$tmpto> directly\r\n421 <$myName> closing transmission\r\n" );
13419                                $this->{prepend}="[MaxErrors]";
13420                                $this->{messagereason}="max errors ($MaxErrors) exceeded";
13421                                mlog($fh,"max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection - after SRS");
13422                                pbAdd($fh,$this->{ip},'meValencePB','MaxErrors',0);
13423                                $Stats{msgMaxErrors}++;
13424                                done($fh);
13425                                return;
13426                            }
13427                            sendque($fh,"551 5.7.1 User not local; please try <$tmpto> directly\r\n");
13428                            return;
13429                        }
13430                    } else {
13431                        $this->{invalidSRSBounce}=1;
13432                    }
13433                } else {
13434                    $this->{invalidSRSBounce} = 1;
13435                }
13436            } elsif ( &serverIsSmtpDestination($server) && $e=~/^<?(SRS[01][=+-][^\r\n>]*)/io) {
13437                $this->{prepend}="[RelayAttempt]";
13438                $this->{messagereason} = "SRS only supported in DSN (Delivery Status Notification): $e";
13439                mlog( $fh, $this->{messagereason} );
13440                $Stats{rcptRelayRejected}++;
13441                pbAdd($fh,$this->{ip},'rlValencePB','RelayAttempt',0);
13442                if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
13443                    MaxErrorsFailed($fh,
13444                    "554 5.7.6 SRS only supported in DSN\r\n421 <$myName> closing transmission\r\n" ,
13445                    "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection - after SRS-DSN");
13446                    return;
13447                }
13448                sendque($fh,"554 5.7.6 SRS only supported in DSN (Delivery Status Notification)\r\n");
13449                return;
13450            }
13451        }
13452        if ( $e !~ /ORCPT/ && $e =~ /[\!\@]\S*\@/ ) {
13453
13454            # blatent attempt at relaying
13455
13456            $this->{prepend}       = "[RelayAttempt]";
13457            my $reply = $NoRelaying;
13458            $reply =~ s/REASON/relay attempt: $e/g;
13459            $reply = replaceerror ($fh, $reply);
13460
13461            $this->{messagereason} = "relay attempt blocked for (evil): $e";
13462            mlog( $fh, $this->{messagereason} );
13463
13464            $Stats{rcptRelayRejected}++;
13465            delayWhiteExpire($fh);
13466
13467			if ($NoRelayingStrict) {
13468                NoLoopSyswrite( $fh, $NoRelaying."\r\n421 <$myName> closing transmission\r\n" );
13469				done($fh);
13470                return;
13471			}
13472
13473			if($MaxRelayingErrors  && ++$this->{serverErrors} >= $MaxRelayingErrors) {
13474                delayWhiteExpire($fh);
13475                NoLoopSyswrite( $fh, $reply."\r\n421 <$myName> closing transmission\r\n" );
13476                $this->{prepend}="[MaxErrors]";
13477                $this->{messagereason}="max errors ($this->{serverErrors}) exceeded";
13478                mlog($fh,"max errors ($this->{serverErrors}) exceeded -- dropping connection - after ORCPT");
13479                pbAdd($fh,$this->{ip},'meValencePB','MaxErrors',0);
13480                $Stats{msgMaxErrors}++;
13481                done($fh);
13482                return;
13483            }
13484
13485			sendque($fh, $reply."\r\n");
13486
13487
13488            return;
13489        } elsif ( $EnableBangPath && $e =~ /([a-z\-_\.]+)!([a-z\-_\.]+)$/i ) {
13490
13491   # someone give me one good reason why I should support bang paths! grumble...
13492            $u = "$2@";
13493            $h = $1;
13494
13495        } elsif ( $l =~ /rcpt to:.*?($EmailAdrRe\@)($EmailDomainRe)/io ) {
13496            ( $u, $h ) = ( $1, $2 );
13497#            mlog($fh,"2 $e u$u h=$h");
13498			CheckReportAddr($fh, "$u$h");
13499        } elsif ( $defaultLocalHost && $l =~ /rcpt to:.*?<($EmailAdrRe)>/io ) {
13500            ( $u, $h ) = ( $1, $defaultLocalHost );
13501            $u .= '@';
13502
13503        } elsif($l=~/rcpt to:[^\r\n]*?(\"$EmailAdrRe\"\@)($EmailDomainRe)/io) {
13504            ($u,$h)=($1,$2);
13505            my $buh = batv_remove_tag(0,"$u$h",'');
13506            $buh =~ /($EmailAdrRe\@)($EmailDomainRe)/io;
13507            ($u,$h)=($1,$2);
13508            $u =~ s/\"//go;
13509			$this->{user} = $u;
13510        } else {
13511
13512            # couldn't understand recipient
13513
13514            $this->{prepend}       = "[RelayAttempt]";
13515            $this->{messagereason} = "relay attempt blocked for (parsing): $e";
13516            mlog( $fh, $this->{messagereason} ) if $RelayLog;
13517
13518            $Stats{rcptRelayRejected}++;
13519
13520            if ($NoRelayingStrict) {
13521                NoLoopSyswrite( $fh, $NoRelaying."\r\n421 <$myName> closing transmission\r\n" );
13522				done($fh);
13523                return;
13524			}
13525			sendque( $fh,"551 5.7.1 $this->{messagereason}\r\n");
13526
13527            if($MaxRelayingErrors  && ++$this->{serverErrors} >= $MaxRelayingErrors && !$this->{noprocessing} && !$this->{ispip} && !$this->{relayok}) {
13528                $this->{prepend}       = "[RelayAttempt]";
13529                delayWhiteExpire($fh);
13530                my $reply = $SpamError;
13531                $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
13532            	$reply =~ s/REASON/relay attempt: $e/g;
13533            	$reply = replaceerror ($fh, $reply);
13534
13535                NoLoopSyswrite( $fh, $reply."\r\n421 <$myName> closing transmission\r\n" );
13536                $this->{messagereason} = "max errors (MaxRelayingErrors=$MaxRelayingErrors) exceeded";
13537                mlog( $fh,
13538                    "max errors (MaxRelayingErrors=$MaxRelayingErrors) exceeded -- last relay attempt blocked for (parsing): $e" ) if $RelayLog > 1;
13539                pbAdd( $fh, $this->{ip}, $meValencePB, "MaxErrors", 2 );
13540                $Stats{msgMaxErrors}++;
13541                done($fh);
13542            }
13543
13544            return;
13545        }
13546        # recipient replacment should be done next to here !
13547        if ($ReplaceRecpt) {
13548            if ($l=~/rcpt to:\s*<*([^\r\n>]*)/io) {
13549                my $midpart  = $1;
13550                $midpart = batv_remove_tag(0,$midpart,'');
13551                my $orgmidpart = $midpart;
13552                if ($midpart) {
13553                  my $bpa = 0;
13554                  if($EnableBangPath && $midpart=~/([a-z\-_\.]+)!([a-z\-_\.]+)$/io) {
13555                      $midpart = "$2@$1";
13556                  }
13557                  my $mf = $this->{mailfrom};
13558                  $mf = batv_remove_tag(0,$mf,'');
13559                  my $newmidpart = RcptReplace($midpart,$mf,'RecRepRegex');
13560                  if (lc $newmidpart ne lc $midpart) {
13561                      $l =~ s/\Q$orgmidpart\E/$newmidpart/i;
13562                      mlog($fh,"info: recipient $orgmidpart replaced with $newmidpart");
13563                      $this->{myheader}.="X-Assp-Recipient: recipient $orgmidpart replaced with $newmidpart\r\n";
13564                      $this->{orgrcpt} = $orgmidpart;
13565                  }
13566                }
13567            }
13568            $l=~/rcpt to: *([^\r\n]*)/io;
13569            $e = batv_remove_tag(0,$1,'');
13570        }
13571
13572        if ( matchSL( "$u$h", 'noCollecting' ) ) {
13573                $this->{nocollect} = 1;
13574        }
13575        if ( matchSL( "$u$h", 'noBayesian' ) ) {
13576                $this->{nobayesian} = 1;
13577        }
13578
13579         #enforce valid email address pattern
13580        if ( $CanUseAddress && $DoRFC822 ) {
13581
13582            if ($e=~/<*([^\r\n>]*)/io) {
13583                my $RO_e=$1;
13584                $RO_e = "$RO_e" . "@" . "$defaultLocalHost" if $defaultLocalHost && $RO_e !~ /\@/i;
13585                if ($RO_e !~/$defaultLocalHost/i && $RO_e !~ /RSBM_.*?x2DXx2DX\d+\Q$maillogExt\E\@/i && ! Email::Valid->address($RO_e)) {
13586
13587                    # couldn't understand recipient
13588
13589                    $this->{prepend} = "[MalformedAddress]";
13590                    mlog($fh,"malformed address: '$RO_e' - failed $Email::Valid::Details check");
13591                    $Stats{rcptRelayRejected}++;
13592                    if($MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
13593                        delayWhiteExpire($fh);
13594                        NoLoopSyswrite( $fh, "553 Malformed address: $u$h\r\n421 <$myName> closing transmission\r\n" );
13595                        $this->{prepend}="[MaxErrors]";
13596                        $this->{messagereason}="max errors ($MaxErrors) exceeded";
13597                        mlog($fh,"max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after Email::Valid") if $ValidateUserLog;
13598                        pbAdd($fh,$this->{ip},'meValencePB',"MaxErrors",0);
13599                        $Stats{msgMaxErrors}++;
13600                        done($fh);
13601                        return;
13602                    }
13603                    sendque( $fh, "553 Malformed address: $u$h\r\n" );
13604                    return;
13605                }
13606            }
13607        }
13608        my $rcptislocal = localmail($h);
13609		my ($mfd) = $this->{mailfrom} =~ /(\@.*)/;
13610		my $all = "*" . $mfd;
13611		my $istrapaddress = matchSL("$u$h",'spamtrapaddresses') && !matchSL("$u$h",'noPenaltyMakeTraps');
13612        if ($rcptislocal) {
13613
13614            if ( lc $u eq "abuse\@" && $sendAllAbuse ) {
13615
13616                # accept abuse catchall addresses
13617                if ($sendAllAbuse=~/$EmailAdrRe\@($EmailDomainRe)/io) {
13618                    $h=$1;
13619                    $l="RCPT TO:\<$sendAllAbuse\>\r\n";
13620                    $this->{noprocessing}=1 if $sendAllAbuseNP;
13621                }
13622            } elsif ( lc $u eq "postmaster\@" && $sendAllPostmaster ) {
13623
13624                # accept postmaster catchall addresses
13625                if ($sendAllPostmaster=~/$EmailAdrRe\@($EmailDomainRe)/io) {
13626                    $h=$1;
13627                    $l="RCPT TO:\<$sendAllPostmaster\>\r\n";
13628                    $this->{noprocessing}=1 if $sendAllPostmasterNP;
13629                }
13630            } elsif ($AllowLocalAddressesRe && $AllowLocalAddressesReCount && "$u$h" !~ $AllowLocalAddressesReRE) {
13631            	my $reply = $NoValidRecipient;
13632				$reply = "550 5.1.1 User unknown: $u$h\r\n" if !$NoValidRecipient;
13633				$reply = replaceerror ($fh, $reply, "$u$h" );
13634				$this->{messagereason} = "rejected by AllowLocalRe: $u$h";
13635				mlog( $fh, $this->{messagereason},1 )
13636                  if $this->{alllog} or $ValidateUserLog == 1 or $ValidateUserLog == 2;
13637
13638                $Stats{rcptNonexistent}++;
13639
13640
13641                pbAdd( $fh, $this->{ip}, $irValencePB, "UserUnknown" ) ;
13642				sendque( $fh, "$reply\r\n" );
13643
13644				if(++$this->{serverErrors} >= $MaxErrors ) {
13645                	$this->{prepend}       = "[MaxErrors]";
13646                	$this->{messagereason} = "max errors (MaxErrors=$MaxErrors) exceeded";
13647                	mlog( $fh,
13648                    "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after rejection by DoPenaltyMakeTraps" ) if $RelayLog > 1 or $ValidateUserLog;
13649                	pbAdd( $fh, $this->{ip}, 'meValencePB', "MaxErrors");
13650                	$Stats{msgMaxErrors}++;
13651                	done($fh);
13652            	}
13653
13654                return;
13655            } elsif ($DoPenaltyMakeTraps==3 && pbTrapExist($fh,"$u$h") ) {
13656            	my $reply = $NoValidRecipient;
13657				$reply = "550 5.1.1 User unknown: $u$h\r\n" if !$NoValidRecipient;
13658				$reply = replaceerror ($fh, $reply, "$u$h" );
13659				$this->{messagereason} = "rejected by DoPenaltyMakeTraps(3): $u$h";
13660				mlog( $fh, $this->{messagereason},1 )
13661                  if $this->{alllog} or $TrapLog;
13662
13663                $Stats{rcptNonexistent}++;
13664                pbTrapAdd( $fh, "$u$h" );
13665
13666                pbAdd( $fh, $this->{ip}, $irValencePB, "UserUnknown" ) ;
13667				sendque( $fh, "$reply\r\n" );
13668
13669				if(++$this->{serverErrors} >= $MaxErrors ) {
13670
13671                	$this->{prepend}       = "[MaxErrors]";
13672                	$this->{messagereason} = "max errors (MaxErrors=$MaxErrors) exceeded";
13673                	mlog( $fh,
13674                    "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection after rejection by DoPenaltyMakeTraps" ) if $RelayLog > 1 or $ValidateUserLog;
13675                	pbAdd( $fh, $this->{ip}, 'meValencePB', "MaxErrors");
13676                	$Stats{msgMaxErrors}++;
13677                	done($fh);
13678            	}
13679
13680                return;
13681            } elsif ( matchSL( "$u$h", 'RejectTheseLocalAddresses' ) ) {
13682            	my $reply = $NoValidRecipient;
13683				$reply = "550 5.1.1 User unknown: $u$h\r\n" if !$NoValidRecipient;
13684				$reply = replaceerror ($fh, $reply, "$u$h" );
13685				$Stats{rcptNonexistent}++;
13686
13687                $this->{prepend} = "[RejectAddress]";
13688                mlog( $fh, "rejected by reject address list: $u$h" )
13689                  if $this->{alllog} or $ValidateUserLog ;
13690                delayWhiteExpire($fh);
13691                seterror( $fh, $reply, 1 );
13692
13693                return;
13694
13695			} elsif (!$this->{addressedToSpamBucket} && ($spamaddresses
13696                	&& !$this->{nocollect}
13697                	&& matchSL( "$u$h", 'spamaddresses' ) )
13698                	or ($DoPenaltyMakeTraps==2 && &pbTrapFind("$u$h"))
13699                        or ($UseTrapToCollect && $istrapaddress)) {
13700                $this->{addressedToSpamBucket}="$u$h";
13701                $this->{messagescore} = 99;
13702
13703				$this->{newsletterre}		= '';
13704
13705                $l="RCPT TO:\<$u$h\>\r\n";
13706
13707 			} elsif (!$this->{addressedToSpamBucket} &&
13708 				!matchSL( "$u$h", 	'noPenaltyMakeTraps' ) && ((($spamtrapaddresses && matchSL("$u$h",'spamtrapaddresses')) or (!$Whitelist{lc $this->{mailfrom} } && $DoPenaltyMakeTraps==1 && pbTrapFind($fh,"$u$h")) ) &&   !$this->{relayok} && !$this->{nocollect}  && !$this->{acceptall})) {
13709
13710                $this->{addressedToPenaltyTrap} = 1;
13711                $this->{prepend} = "[Trap]";
13712                pbWhiteDelete( $fh, $this->{ip} );
13713
13714                $this->{messagereason} = "$u$h in spamtrapaddresses";
13715                mlog( $fh,"$this->{messagereason}") if $TrapLog >=2;
13716
13717                pbAdd( $fh, $this->{ip}, $stValencePB, "spamtrap:$u$h" );
13718                if ( $SpamTrap2NULL) {
13719                	$Stats{spambucket}++;
13720                	delayWhiteExpire($fh);
13721                    $this->{getline} = \&NullFromToData;
13722                    &NullFromToData( $fh, $l );
13723                    done($fh);
13724                    return;
13725                }
13726                if ($PenaltyTrapPolite) {
13727                    $reply = $PenaltyTrapPolite;
13728                    $reply =~ s/EMAILADDRESS/$u$h/go;
13729                    $reply =~ s/LOCALDOMAIN/$h/go;
13730                    $reply = replaceerror ($fh, $reply);
13731                	seterror( $fh, "$reply", 1 );
13732
13733                }
13734
13735                $Stats{spambucket}++;
13736                delayWhiteExpire($fh);
13737                done($fh);
13738                return;
13739
13740            	}
13741
13742
13743        }
13744
13745        if ($noProcessing) {
13746
13747            $this->{rcptnoprocessing} = "";
13748
13749            if ( matchSL( "$u$h", 'NoProcessing' ) ) {
13750                mlogRe( $fh, "$u$h", "NoProcessing" );
13751				$this->{uhnoprocessing}=1 if $LocalAddressesNP;
13752                $this->{delaydone}        = 1;
13753                $this->{rcptnoprocessing} = 1;
13754            }
13755        }
13756        if ($noProcessingTo && !$this->{rcptnoprocessing}) {
13757
13758
13759
13760            if ( matchSL( "$u$h", 'NoProcessingTo' ) ) {
13761                mlogRe( $fh, "$u$h", "NoProcessingTo" );
13762				$this->{uhnoprocessing}=1 if $LocalAddressesNP;
13763                $this->{delaydone}        = 1;
13764                $this->{rcptnoprocessing} = 1;
13765            }
13766        }
13767
13768        my $isEmailInterface =
13769                 (  (lc $u =~ /assp-/  or localmail($h) or lc $h eq lc $defaultLocalHost)
13770                    && (   lc $u eq lc "$EmailSpam\@"
13771                        || lc $u eq lc "$EmailHam\@"
13772                        || lc $u eq lc "$EmailWhitelistAdd\@"
13773                        || lc $u eq lc "$EmailWhitelistRemove\@"
13774                        || lc $u eq lc "$EmailRedlistAdd\@"
13775                        || lc $u eq lc "$EmailHelp\@"
13776                        || lc $u eq lc "$EmailAnalyze\@"
13777                        || lc $u eq lc "$EmailRedlistRemove\@"
13778                        || lc $u eq lc "$EmailSpamLoverAdd\@"
13779                        || lc $u eq lc "$EmailSpamLoverRemove\@"
13780                        || lc $u eq lc "$EmailNoProcessingAdd\@"
13781                        || lc $u eq lc "$EmailNoProcessingRemove\@"
13782                        || lc $u eq lc "$EmailBlackAdd\@"
13783                        || lc $u eq lc "$EmailBlackRemove\@"
13784                        || lc $u eq lc "$EmailPersBlackAdd\@"
13785                        || lc $u eq lc "$EmailPersBlackRemove\@"
13786                        || lc $u =~ /^RSBM.+?$maillogExt\@$/i
13787                        || lc $u eq lc "$EmailBlockReport\@"
13788
13789                       )
13790                 );
13791		my $emailok;
13792        $emailok = 1
13793          if (   $EmailInterfaceOk
13794              && $this->{senderok} ne '2'
13795              && $this->{senderok} ne '3'
13796              && ( $this->{relayok} || $this->{externalsenderok}  )
13797              && $isEmailInterface
13798             );
13799
13800    	# skip check when RELAYOK or EMAIL-Interface
13801        if ($emailok)
13802        {
13803			emailInterface($fh,$u,$h,$l);
13804			return;
13805  		}
13806
13807        my $uh = "$u$h";
13808        $uh =~ /^(.*)(@.*)$/;
13809        my $hat = $2;
13810        $this->{alllog} = 1 if $allLogRe && $uh =~ ( '(' . $allLogReRE . ')' );
13811        my $t1 = "VRFY";
13812        $t1 = "LDAP" if $DoLDAP;
13813
13814        my $reporterror;
13815        if (CheckReportAddr($fh,$uh)  && !$this->{relayok} && !$this->{externalsenderok}) {
13816
13817        		$this->{prepend} = "[ReportLog]";
13818        		mlog( $fh, "email-interface warning: mail to '$uh' from $this->{mailfrom} contains no local sender and is not set in EmailSenderOK'");
13819
13820        }
13821
13822
13823        if ($uh =~ /^(.*@)(.*)$/
13824        		&& ($2 =~ "assp.local" or $2 =~ "assp-notspam.org")
13825        		&& !$emailok) {
13826        	$uh =~ /^(.*)(@.*)$/;
13827        	$this->{prepend} = "[ReportLog]";
13828        	if ( !$EmailInterfaceOk ) {
13829        		$reporterror = "EmailInterfaceOk disabled";
13830        	}
13831        	if ( !$this->{relayok} && !$this->{externalsenderok}) {
13832        		$reporterror .= ", sender not local" if $reporterror;
13833        		$reporterror = "sender not local" if !$reporterror;
13834        	}
13835        	if ( !$isEmailInterface) {
13836        		$reporterror .= ", '$1' not set in email-interface" if $reporterror;
13837        		$reporterror = "'$1' not set in email-interface" if !$reporterror;
13838        	}
13839        	mlog( $fh, "email-interface reported '$reporterror'",1);
13840        	seterror( $fh, "550 5.1.1 User unknown, email-interface reported '$reporterror'",1) ;
13841
13842            return;
13843
13844       }
13845
13846
13847
13848        if (!$this->{uhnoprocessing}
13849        	&& !$emailok
13850        	&& !$this->{relayok}
13851        	&& (   $LocalAddresses_Flat
13852                || $DoLDAP && $CanUseLDAP
13853                || &isvrfy( $fh, $uh)
13854
13855               )) {
13856
13857
13858            $this->{islocalmailaddress} = 0;
13859
13860
13861            if ($SepChar) {
13862                my $char = "\\$SepChar";
13863                if ( $u =~ "(.+?)$char" ) {
13864                    $uh = "$1\@$h";
13865                    $uh =~ s/"//;
13866                }
13867            }
13868
13869            if ( $this->{addressedToSpamBucket} )
13870				{
13871				$this->{islocalmailaddress} = 1;
13872                d("$uh validated by spamaddresses list\n");
13873                mlog( $fh, "$uh validated by spamaddresses list" ) if $ValidateUserLog == 3;
13874
13875            } elsif (($DoLDAP || &isvrfy( $fh, $uh))
13876            		&& !$LocalAddresses_Flat
13877            		&& &LDAPCacheFind($uh,$t1,1) )
13878				{
13879				$this->{islocalmailaddress} = 1;
13880                d("$uh validated by ldaplist\n");
13881                mlog( $fh, "$uh validated by ldap-cache" ) if $ValidateUserLog;
13882
13883            } elsif ( !$this->{islocalmailaddress}
13884                && $LocalAddresses_Flat
13885                && $uh =~ /^([^@]*)(@.*)$/o
13886                && matchSL( $2, 'LocalAddresses_Flat' ) )
13887            	{
13888                	$this->{islocalmailaddress} = 1;
13889                	d("$2 validated by LocalAddresses_Flat\n");
13890                	mlog( $fh, "$2 validated by LocalAddresses_Flat" )
13891                  		if $ValidateUserLog >= 2;
13892
13893			} elsif ( !$this->{islocalmailaddress}
13894                && $LocalAddresses_Flat
13895                && $LocalAddresses_Flat_Domains
13896                && $uh =~ /^([^@]*@)(.*)$/o
13897                && matchSL( $2, 'LocalAddresses_Flat' ) )
13898            	{
13899                	$this->{islocalmailaddress} = 1;
13900                	d("$2 validated by LocalAddresses_Flat\n");
13901                	mlog( $fh, "$2 validated by LocalAddresses_Flat" )
13902                  		if $ValidateUserLog >= 2;
13903
13904
13905
13906            } elsif (  !$this->{islocalmailaddress}
13907                && $LocalAddresses_Flat
13908                && matchSL( $uh, 'LocalAddresses_Flat' ))
13909            	{
13910				$this->{islocalmailaddress} = 1;
13911                d("$uh validated by LocalAddresses_Flat\n");
13912                mlog( $fh, "$uh validated by LocalAddresses_Flat" ) if $ValidateUserLog >= 2;
13913
13914            } elsif (  !$this->{islocalmailaddress}
13915                && $DoLDAP && $CanUseLDAP
13916                && &localmailaddress( $fh, $uh ))
13917            	{
13918				$this->{islocalmailaddress} = 1;
13919                d("$uh validated by LDAP\n");
13920                mlog( $fh, "$uh validated by LDAP" ) if $ValidateUserLog >= 2;
13921
13922            } elsif (  !$this->{islocalmailaddress}
13923                && $DoVRFY && $CanUseNetSMTP && $uh =~ /^([^@]*@)(.*)$/o
13924                && &matchHashKey('DomainVRFYMTA', lc $2 )
13925                && &localmailaddress( $fh, $uh ))
13926            	{
13927				$this->{islocalmailaddress} = 1;
13928                d("$uh validated by VRFY\n");
13929                mlog( $fh, "$uh validated by VRFY" ) if $ValidateUserLog >= 2;
13930
13931
13932
13933            }
13934            pbTrapDelete($uh) if $this->{islocalmailaddress};
13935
13936        } else {
13937            $this->{islocalmailaddress}=localmail($h);
13938        }
13939
13940        if (   !( $this->{relayok} )
13941        	&& !$this->{islocalmailaddress}
13942            && !$nolocalDomains
13943            && ( !$rcptislocal || ( $u . $h ) =~ /\%/ )
13944            || $u =~ /\@\w+/ )
13945        {
13946
13947
13948
13949            $this->{prepend} = "[RelayAttempt]";
13950            $this->{messagereason} = "relay attempt blocked for: $u$h";
13951            mlog( $fh, $this->{messagereason} ) if $RelayLog;
13952
13953            $Stats{rcptRelayRejected}++;
13954            delayWhiteExpire($fh);
13955            if ($NoRelayingStrict) {
13956                NoLoopSyswrite( $fh, $NoRelaying."\r\n421 <$myName> closing transmission\r\n" );
13957				done($fh);
13958                return;
13959			}
13960
13961
13962            if($MaxRelayingErrors  && ++$this->{serverErrors} >= $MaxRelayingErrors && !$this->{noprocessing}) {
13963                $this->{prepend}       = "[RelayAttempt]";
13964                $this->{messagereason} = "max errors (MaxRelayingErrors=$MaxRelayingErrors) exceeded";
13965                mlog( $fh,
13966                    " $this->{messagereason}" ) if $RelayLog > 1;
13967                pbAdd( $fh, $this->{ip}, $meValencePB, "MaxRelayingErrors", 2 );
13968                $Stats{msgMaxErrors}++;
13969                my $reply = $NoRelaying;
13970            	$reply =~ s/REASON/dropping connection after relay attempt/g;
13971            	$reply = replaceerror ($fh, $reply);
13972                seterror( $fh, $reply, 1 );
13973                NoLoopSyswrite( $fh, $reply."\r\n421 <$myName> closing transmission\r\n" );
13974                return;;
13975            }
13976            sendque($fh, $NoRelaying."\r\n");
13977            return;
13978        }
13979
13980        if (   (matchSL($uh,'InternalAddresses')  &&  ! localmail($this->{mailfrom}))
13981            || (matchSL($uh,'$InternalAndWhiteAddresses') && ! ( localmail($this->{mailfrom}) || Whitelist($this->{mailfrom},$uh, undef)) )
13982           )
13983        {
13984            NoLoopSyswrite($fh, $NoRelaying."\r\n");
13985            $this->{prepend}="[InternalAddress]";
13986            mlog($fh,"invalid remote sender for internal address: $uh");
13987            pbAdd($fh,$this->{ip},'iaValencePB',"internaladdress:$uh") ;
13988            $Stats{internaladdresses}++;
13989            delayWhiteExpire($fh);
13990            done($fh);
13991            return;
13992        }
13993
13994        if ($noScan) {
13995
13996            if ( matchSL( "$this->{nocollect}", 'noScan' ) ) {
13997                $this->{noscan} = 1;
13998
13999            }
14000        }
14001
14002        my $ret = matchIP( $this->{ip}, 'noProcessingIPs', $fh );
14003        if ($noProcessingIPs
14004                && $ret
14005                &&  !matchIP( $this->{ip}, 'NPexcludeIPs', 0, 1 )
14006				&&  !$this->{relayok}
14007                && !$this->{nonoprocessing}
14008                 )
14009            {
14010                $this->{noprocessing}  		= 1;
14011                $this->{noprocessingip}  	= 1;
14012                $this->{white}  			= 1;
14013
14014                $this->{passingreason} 		= "noProcessingIPs '$ret'" if !$this->{passingreason};
14015        }
14016
14017
14018       if (!$this->{relayok} && !$this->{addressedToSpamBucket}) {
14019
14020		$this->{spamfriends} = "$u$h" if matchSL( "$u$h", 'spamFriends' );
14021
14022        $this->{subjectsl} = matchSL( "$u$h", 'spamLoverSubjectSelected' );
14023
14024
14025      my $mSLRE     = matchSL($uh,'spamLovers')      and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'spamLovers'}},$uh));
14026        my $mBSLRE    = matchSL($uh,'baysSpamLovers') && !$this->{baysspamhaters}  and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'baysSpamLovers'}},$uh));
14027        my $mBLSLRE   = matchSL($uh,'blSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'blSpamLovers'}},$uh));
14028        my $mHLSLRE   = matchSL($uh,'hlSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'hlSpamLovers'}},$uh));
14029        my $mHISLRE   = matchSL($uh,'hiSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'hiSpamLovers'}},$uh));
14030        my $mBOSLRE   = matchSL($uh,'bombSpamLovers')  and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'bombSpamLovers'}},$uh));
14031        my $mPTRSLRE  = matchSL($uh,'ptrSpamLovers')   and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'ptrSpamLovers'}},$uh));
14032        my $mMXASLRE  = matchSL($uh,'mxaSpamLovers')   and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'mxaSpamLovers'}},$uh));
14033
14034        my $mRBLSLRE  = matchSL($uh,'rblSpamLovers')   and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'rblSpamLovers'}},$uh));
14035        my $mURIBLSLRE= matchSL($uh,'uriblSpamLovers') and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'uriblSpamLovers'}},$uh));
14036        my $mSRSSLRE  = matchSL($uh,'srsSpamLovers')   and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'srsSpamLovers'}},$uh));
14037        my $mDLSLRE = $this->{dlslre} = matchSL( "$u$h", 'delaySpamLovers' );
14038
14039        my $mPBSLRE   = matchSL($uh,'msSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'msSpamLovers'}},$uh));
14040        my $mSBSLRE   = matchSL($uh,'sbSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'sbSpamLovers'}},$uh));
14041        my $mATSLRE   = matchSL($uh,'atSpamLovers')    and $this->{spamMaxScore} = max($this->{spamMaxScore}, matchHashKey(\%{$SLscore{'atSpamLovers'}},$uh));
14042
14043
14044		$this->{spamMaxScore} = $MessageScoringUpperLimit if $this->{spamMaxScore} < $MessageScoringUpperLimit;
14045		$this->{spamMaxScore} = $slMaxScore if $slMaxScore > $this->{spamMaxScore};
14046
14047
14048        my $mDLSLRE    = matchSL( "$u$h", 'delaySpamLovers' );
14049        $this->{dlslre} = $mDLSLRE;
14050        $this->{nodelay} = 'noDelayAddresses' if matchSL("$u$h",'noDelayAddresses');
14051
14052
14053	    $this->{spamlover} = $this->{spamloverall} = 1 if $mSLRE;
14054
14055
14056		my $mWLORE = matchSL($uh,'WhitelistOnlyAddresses');
14057
14058		if ( $rcptislocal && ( $mWLORE || $WhitelistOnly ) ) {
14059            $this->{allwhitelist} |= 1;
14060        } else {
14061            $this->{allwhitelist} |= 2;
14062        }
14063
14064
14065        if (
14066            	   $mSLRE
14067                || $mBSLRE
14068                || $mBLSLRE
14069                || $mBOSLRE
14070 				|| $mPTRSLRE
14071				|| $mMXASLRE
14072                || $mHLSLRE
14073                || $mHISLRE
14074
14075                || $mURIBLSLRE
14076                || $mRBLSLRE
14077
14078                || $mDLSLRE
14079                || $mPBSLRE
14080                || $mSBSLRE
14081                || $mATSLRE
14082
14083          )
14084        {
14085            $this->{allLoveSpam} |= 1;
14086            $rcptislocal = 1;
14087        } else {
14088            $this->{allLoveSpam} = 2;
14089        }
14090        if ( $rcptislocal && ( $mBSLRE || $mSLRE ) ) {
14091            $this->{allLoveBaysSpam} |= 1;
14092        } else {
14093            $this->{allLoveBaysSpam} = 2;
14094        }
14095        if ( $rcptislocal && ( $mBLSLRE || $mSLRE ) ) {
14096            $this->{allLoveBlSpam} |= 1;
14097        } else {
14098            $this->{allLoveBlSpam} = 2;
14099        }
14100        if ( $rcptislocal && ( $mBOSLRE || $mSLRE ) ) {
14101            $this->{allLoveBoSpam} |= 1;
14102        } else {
14103            $this->{allLoveBoSpam} = 2;
14104        }
14105
14106
14107        if ( $rcptislocal && ( $mHLSLRE || $mSLRE ) ) {
14108            $this->{allLoveHlSpam} |= 1;
14109        } else {
14110            $this->{allLoveHlSpam} = 2;
14111        }
14112        if ( $rcptislocal && ( $mHISLRE || $mSLRE ) ) {
14113            $this->{allLoveHiSpam} |= 1;
14114        } else {
14115            $this->{allLoveHiSpam} = 2;
14116        }
14117
14118        if ( $rcptislocal && ( $mRBLSLRE || $mSLRE ) ) {
14119            $this->{allLoveRBLSpam} |= 1;
14120        } else {
14121            $this->{allLoveRBLSpam} = 2;
14122        }
14123        if ( $rcptislocal && ($mATSLRE) ) {
14124        	$this->{allLoveATSpam} |= 1; }
14125        else  {
14126        	$this->{allLoveATSpam} = 2; }
14127
14128        if ( $rcptislocal && ( $mURIBLSLRE || $mSLRE ) ) {
14129            $this->{allLoveURIBLSpam} |= 1;
14130        } else {
14131            $this->{allLoveURIBLSpam} = 2;
14132        }
14133
14134        if ( $rcptislocal && ( $mDLSLRE ) ) {
14135            $this->{allLoveDLSpam} |= 1;
14136        } else {
14137            $this->{allLoveDLSpam} = 2;
14138        }
14139        if ( $rcptislocal && ( $mPBSLRE || $mSLRE ) ) {
14140            $this->{allLovePBSpam} |= 1;
14141        } else {
14142            $this->{allLovePBSpam} = 2;
14143        }
14144        if ( $rcptislocal && ( $mSBSLRE || $mSLRE ) ) {
14145            $this->{allLoveSBSpam} |= 1;
14146        } else {
14147            $this->{allLoveSBSpam} = 2;
14148        }
14149
14150        }
14151
14152
14153        if (! $this->{whitelisted} && $whiteListedDomains && $this->{mailfrom} =~ /($WLDRE)/ ) {
14154			mlog( $fh, "whitelisted by whitedomainlist: '$1'", 1 );
14155
14156            $this->{passingreason} = "whiteListedDomains '$1'" if !$this->{passingreason};
14157            $this->{whitelisted}=1;
14158        }
14159
14160        $this->{rcptValidated} = $this->{rcptNonexistent} = 0;
14161
14162        if ( $this->{addressedToSpamBucket} ) {
14163            $this->{delayed} = "";
14164            # accept SpamBucket addresses in every case
14165            $this->{rcpt} .= "$u$h ";
14166
14167
14168        } elsif ( $LocalAddresses_Flat
14169        || 	$DoLDAP
14170        || &isvrfy ($fh, "$u$h") )
14171
14172        {
14173            if (   ( $this->{islocalmailaddress} )
14174                || ( $this->{relayok} ) && !$rcptislocal )
14175            {
14176                if ( !$this->{relayok} ) {
14177
14178                    if ( !Delayok( $fh, "$u$h" ) && $EnableDelaying ) {
14179                        $this->{delayqueue} .= "$u$h ";
14180                        $this->{rcpt}       .= "$u$h ";
14181                        $this->{delayed} = 1;
14182                        mlog( $fh, "recipient delaying queued: $u$h", 1 )
14183                          if $DelayLog >= 2;
14184                        sendque( $server, $l );
14185                        return;
14186                    }
14187                }
14188                $this->{donotdelay} = 1;
14189                $this->{rcpt} .= "$u$h ";
14190                mlog( $fh, "recipient accepted: $u$h", 1 )
14191                  if $this->{alllog} or $ValidateUserLog >= 2;
14192                $this->{rcptValidated} = 1;
14193            } elsif ( $calist{$h} ) {
14194                my $uhx = $calist{$h} . "@" . $h;
14195                mlog( $fh, "invalid address $uh replaced with $uhx", 1 )
14196                  if $this->{alllog} or $ValidateUserLog >= 2;
14197                $this->{rcpt} .= "$uhx ";
14198                $this->{messagereason} = "invalid address $uh";
14199                pbTrapAdd( $fh, "$uh" );
14200                pbAdd( $fh, $this->{ip}, $irValencePB, "InvalidAddress" );
14201                $Stats{rcptNonexistent}++;
14202                $this->{rcptValidated} = 1;
14203                $l = "RCPT TO:\<$uhx\>\r\n";
14204                if (matchSL("$uhx",'NullAddresses')) {
14205                	$this->{getline} = \&NullFromToData;
14206                	&NullFromToData($fh,$l);
14207                        return;
14208                }
14209            } elsif ( $CatchallallISP2NULL && $this->{ispip} ) {
14210                mlog( $fh,
14211                    "invalid address $u$h from ISP moved to NULL-connection",
14212                    1 )
14213                  if $this->{alllog} or $ValidateUserLog >= 2;
14214
14215                $this->{rcptValidated} = 1;
14216                $Stats{rcptNonexistent}++;
14217                $this->{getline} = \&NullFromToData;
14218                &NullFromToData( $fh, $l );
14219                return;
14220           } elsif ($CatchAllAll) {
14221                my $uhx = $CatchAllAll;
14222                mlog( $fh, "invalid address $uh replaced with $uhx", 1 )
14223                  if $this->{alllog} or $ValidateUserLog >= 2;
14224                $this->{rcpt} .= "$uhx ";
14225                $this->{messagereason} = "invalid address $uhx";
14226                pbTrapAdd( $fh, "$uhx" );
14227                pbAdd( $fh, $this->{ip}, $irValencePB, "UserUnknown" ) ;
14228                $Stats{rcptNonexistent}++;
14229                $this->{rcptValidated} = 1;
14230                $l = "RCPT TO:\<$uhx\>\r\n";
14231                if (matchSL("$uhx",'NullAddresses')) {
14232                	$this->{getline} = \&NullFromToData;
14233                	&NullFromToData($fh,$l);
14234			        return;
14235                }
14236           } else {
14237                $this->{prepend}="[UserUnknown]";
14238                $this->{messagereason}="invalid address $uh";
14239                mlog($fh,"User unknown: $uh") if $this->{alllog} or $ValidateUserLog == 1 or $ValidateUserLog == 2;
14240                pbTrapAdd($fh,"$uh");
14241                pbAdd($fh,$this->{ip},$irValencePB,"UserUnknown")  if !$this->{nonoprocessing};
14242                $Stats{rcptNonexistent}++;
14243                $this->{rcptNonexistent}=1;
14244                my $reply;
14245                if ($NoValidRecipient) {
14246                    $reply = $NoValidRecipient."\r\n";
14247                    $reply =~ s/EMAILADDRESS/$u$h/go;
14248                } else {
14249                    $reply = "550 5.1.1 User unknown\r\n";
14250                }
14251                if ($reply =~ /^5/) {
14252                    if ( ($this->{userTempFail} &&
14253                          $DoVRFY &&
14254                          $CanUseNetSMTP &&
14255                          (! ($DoLDAP && $CanUseLDAP) or
14256                             ($DoLDAP && $CanUseLDAP && $LDAPoffline)
14257                          )
14258                         ) or
14259                         ($DoLDAP && $CanUseLDAP && $LDAPoffline &&
14260                          (! ($DoVRFY && $CanUseNetSMTP) or
14261                             ($DoVRFY
14262                              && $CanUseNetSMTP
14263                              && !$this->{userTempFail}
14264                              && $uh =~ /\@([^@]*)/o
14265                              && (&matchHashKey('DomainVRFYMTA', lc $2 )
14266                            or &matchHashKey('FlatVRFYMTA', lc "\@$2" ) )
14267                             )
14268                          )
14269                         )
14270                       )
14271                    {
14272                        $reply =~ s/^\d{3}(?: \d+\.\d+\.\d+)?/450/;
14273                    }
14274                }
14275
14276                sendque( $fh, $reply );
14277
14278                # increment error and drop line if necessary
14279                if(++$this->{serverErrors} >= $MaxErrors && !$this->{noprocessing}) {
14280                    $this->{prepend}       = "[MaxErrors]";
14281                    $this->{messagereason} = "max errors (MaxErrors=$MaxErrors) exceeded";
14282                    mlog($fh,"max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection - after invalid address") if $ValidateUserLog;
14283                    pbAdd( $fh, $this->{ip}, 'meValencePB', "MaxErrors", 2 ) ;
14284
14285                    $Stats{msgMaxErrors}++;
14286                    delayWhiteExpire($fh);
14287                    done($fh);
14288                }
14289                return;
14290            }
14291        } elsif ( !$this->{relayok} ) {
14292
14293            if ( !Delayok( $fh, "$u$h" ) && $EnableDelaying) {
14294                $this->{delayqueue} .= "$u$h ";
14295                $this->{rcpt}       .= "$u$h ";
14296                $this->{delayed} = 1;
14297                mlog( $fh, "recipient delaying queued: $u$h", 1 )
14298                  if $this->{alllog}
14299                      or $DelayLog >= 2;
14300                sendque( $server, $l );
14301                return;
14302            }
14303            $this->{rcpt} .= "$u$h ";
14304        } else {
14305            $this->{red} = $this->{redlist} = "$u$h in RedList"
14306              if ( $Redlist{"$u$h"}
14307                || $Redlist{"*@$h"}
14308                || $Redlist{"$wildcardUser@$h"} );
14309            $this->{rcpt} .= "$u$h ";
14310            mlog( $fh, "recipient accepted without delaying: $u$h", 1 )
14311              if $this->{alllog}
14312                  or $ValidateUserLog == 2;
14313            $this->{donotdelay}    = 1;
14314            $this->{rcptValidated} = 1;
14315        }
14316
14317
14318        # update Stats
14319        if ( $this->{rcptnoprocessing} == 1 ) {
14320            $Stats{rcptUnprocessed}++;
14321        } elsif ( $this->{addressedToSpamBucket} ) {
14322            $Stats{rcptSpamBucket}++;
14323        } elsif ( $this->{allLoveSpam} & 1 ) {
14324            $Stats{rcptSpamLover}++;
14325        } elsif ( $this->{rcptValidated} ) {
14326            $Stats{rcptValidated}++;
14327        } elsif ( $this->{rcptNonexistent} ) {
14328            $Stats{rcptNonexistent}++;
14329        } elsif ($rcptislocal) {
14330            $Stats{rcptUnchecked}++;
14331        } elsif ( $Whitelist{ lc "$u$h" } ) {
14332            pbWhiteAdd( $fh, $this->{ip}, "whitelisted:$u$h" );
14333            $Stats{rcptWhitelisted}++;
14334        } else {
14335            $Stats{rcptNotWhitelisted}++;
14336        }
14337        $this->{numrcpt} = 0;    # calculate the total number of rcpt
14338        foreach ( split( / /, $this->{rcpt} ) ) { $this->{numrcpt}++ }
14339        $this->{numrcpt} = 1 if ( $this->{numrcpt} == 0 );
14340    } elsif ( $l =~ /^ *XEXCH50 +(\d+)/i ) {
14341        $this->{skipbytes} = $1;
14342        d("XEXCH50 b=$1");
14343    } elsif ( $l =~ /^ *DATA/i || $l =~ /^ *BDAT (\d+)/i ) {
14344    	$this->{lastcmd} = "DATA" if $l =~ /^ *DATA/i;
14345        if ($1) {
14346            $this->{bdata} = $1;
14347        } else {
14348            delete $this->{bdata};
14349        }
14350        $this->{rcpt} =~ s/\s$//;
14351
14352        # drop line if no recipients left
14353        if ( $this->{rcpt} !~ /@/ ) {
14354
14355            # possible workaround for GroupWise bug
14356            if ( $this->{delayed} ) {
14357                if ($DelayError) {
14358                    $reply = $DelayError;
14359                } else {
14360                    $reply = "451 4.7.1 Please try again later";
14361                }
14362                mlog( $fh, "DATA phase delayed", 1 ) if $DelayLog;
14363                $reply = replaceerror ($fh, $reply);
14364                seterror($fh, $reply,1);
14365
14366                $Stats{msgDelayed}++ if ( !$this->{StatsmsgDelayed} );
14367                $this->{StatsmsgDelayed} = 1;
14368                return;
14369            }
14370            mlog( $fh, "no recipients left -- dropping connection", 1 )
14371              if $DelayLog || $ValidateUserLog == 2;
14372            $Stats{msgNoRcpt}++;
14373
14374            delayWhiteExpire($fh);
14375            pbAdd( $fh, $this->{ip}, 'reValencePB', "NeedRecipient", 2 );
14376            seterror( $fh, "554 5.7.8 Need Recipient", 1 );
14377            done($fh);
14378            return;
14379        }
14380        if ( !$this->{noprocessing} && $noProcessingTo && !$this->{relayok} &&  allNoProcessingTo( $this->{rcpt}, $fh ) )
14381            {
14382            $this->{noprocessing} = 1;
14383            $this->{passingreason} = "noProcessingTo '$this->{newrcpt}'" if !$this->{passingreason}
14384			}
14385
14386        if (!$this->{noprocessing} && matchSL( $this->{mailfrom}, 'noProcessingFrom' ) )
14387            {
14388            $this->{noprocessing} = 1;
14389            $this->{passingreason} = "noProcessingFrom '$this->{mailfrom}'" if  !$this->{passingreason};
14390			}
14391
14392        if (!$this->{noprocessing} && matchSL( $this->{mailfrom}, 'noProcessing' ) )
14393            {
14394            $this->{noprocessing} = 1;
14395            $this->{passingreason} = "noProcessing '$this->{mailfrom}'" if  !$this->{passingreason};
14396			}
14397
14398		if ( $this->{isbounce} && $this->{delayed} ) {
14399        	&NumRcptOK($fh,0) if $this->{relayok};
14400        	$this->{prepend} = '';
14401            if ($DelayError) {
14402                $reply = $DelayError;
14403            } else {
14404                $reply = "451 4.7.1 Please try again later";
14405            }
14406            mlog( $fh, "bounce delayed", 1 ) if $DelayLog;
14407
14408            seterror($fh, $reply,1);
14409
14410            $Stats{msgDelayed}++ if ( !$this->{StatsmsgDelayed} );
14411            $this->{StatsmsgDelayed} = 1;
14412            return;
14413		} elsif ( $this->{relayok} && (my $nextTry = &localFrequencyNotOK($fh)) ) {
14414            $nextTry = &timestring($nextTry);
14415            $reply = "452 too many recipients for $this->{mailfrom} in $LocalFrequencyInt seconds - please try again not before $nextTry or send a notification message to your postmaster\@LOCALDOMAIN or local administrators\r\n";
14416            my $mfd;
14417            $mfd = $1 if lc $this->{mailfrom} =~ /\@([^@]*)/o;
14418            $reply =~ s/LOCALDOMAIN/$mfd/go;
14419            seterror($fh, $reply,1);
14420            mlog($fh,"warning: too many recipients (more than $LocalFrequencyNumRcpt in the last $LocalFrequencyInt seconds, $this->{numrcpt} in this mail) ($this->{ip}) for $this->{mailfrom} - possible local abuse",1);
14421
14422            $Stats{localFrequency}++;
14423            my $mfr = batv_remove_tag(0,lc $this->{mailfrom},'');
14424            if (! exists $localFrequencyNotify{$mfr} ||
14425                 $localFrequencyNotify{$mfr} < time)
14426            {
14427                $localFrequencyNotify{$mfr} = int((time + 86400) / 86400) * 86400;  # 24:00 today
14428                mlog($fh,"notification: too many recipients (more than $LocalFrequencyNumRcpt in the last $LocalFrequencyInt seconds, $this->{numrcpt} in this mail)($this->{ip}) for $mfr - possible local abuse",1);
14429            }
14430            return;
14431        } else {
14432            if ( !$this->{donotdelay} ) {    # if there is a queued delay
14433                delete $this->{donotdelay};   # and the rcpt to: phase is passed
14434                if ( $this->{delayqueue} ) {  # and no valid recpt -> delay
14435                    if ( !$this->{isbounce} ) {
14436                    	&NumRcptOK($fh,0) if $this->{relayok};
14437                    	$this->{prepend} = '';
14438                        if ($DelayError) {
14439                            $reply = $DelayError;
14440                        } else {
14441                            $reply = "451 4.7.1 Please try again later";
14442                        }
14443
14444                        for ( split( ' ', $this->{delayqueue} ) ) {
14445                            mlog( $fh, "recipient delayed: $_", 1 )
14446                              if $DelayLog;
14447                        }
14448
14449                        seterror($fh, $reply,1);
14450                        delete $this->{delayqueue};
14451                        $Stats{msgDelayed}++ if ( !$this->{StatsmsgDelayed} );
14452                        $this->{StatsmsgDelayed} = 1;
14453
14454
14455                        return;
14456                    }
14457                }
14458            } else {
14459                if ( $this->{delayqueue} ) {
14460                    for ( split( ' ', $this->{delayqueue} ) ) {
14461                        mlog( $fh, "queued delay removed for recipient: $_", 1 )
14462                          if $DelayLog >= 2;
14463                        mlog( $fh, "recipient accepted: $_", 1 )
14464                          if $this->{alllog}
14465                              or $ValidateUserLog == 2;
14466                        $Stats{rcptDelayed}--;
14467                        $Stats{rcptValidated}++;
14468                    }
14469                    delete $this->{delayqueue};
14470                }
14471            }
14472
14473            MaillogStart($fh);    # notify the stream logging to start logging
14474            $this->{getline} = \&getheader;
14475        }
14476    } elsif ( $l =~ /^ *RSET/i ) {
14477        stateReset($fh);          # reset everything
14478    }
14479    sendque( $server, $l );
14480}
14481
14482sub isvrfy {
14483		my ( $fh, $uh ) = @_;
14484		my $this = $Con{$fh};
14485		$uh =~ /^(.*@)(.*)$/;
14486		my $h = $2;
14487        return 1 if $DoVRFY && $CanUseNetSMTP && (&matchHashKey('DomainVRFYMTA', lc $2 ) or &matchHashKey('FlatVRFYMTA', lc "\@$2" ) );
14488}
14489
14490sub isflat {
14491
14492		my ( $fh, $uh ) = @_;
14493		my $this = $Con{$fh};
14494#		mlog($fh,"uh = $uh");
14495		$uh =~ /^(.*@)(.*)$/;
14496		my $h = $2;
14497        return 1 if $LocalAddresses_Flat
14498		&& &matchHashKey('FlatDomains', lc $h );;
14499}
14500
14501
14502# compile the helo-blacklist ignore regular expression
14503sub setHBIRE {
14504    SetRE( 'HBIRE', "^($_[0])\$", "i", "HELO Blacklisted Ignore" );
14505}
14506
14507
14508
14509
14510sub emailInterface {
14511	my ( $fh, $u,  $h, $l) = @_;
14512	my $this = $Con{$fh};
14513 	my $uh = "$u$h";
14514 	$this->{isadmin} = (matchSL( $this->{mailfrom}, 'EmailAdmins') or $this->{mailfrom} && lc $this->{mailfrom} eq lc $EmailAdminReportsTo);
14515    if ( $EmailInterfaceOk && $this->{senderok} ne '2' && $this->{senderok} ne '3'
14516            && ( $this->{relayok} || $this->{externalsenderok}  )
14517
14518           )
14519        {
14520           alarm 0;
14521           $this->{prepend} = "[EmailInterface]";
14522            if(lc $u eq lc "$EmailSpam\@") {
14523                $this->{reporttype}=0;
14524                $this->{reportaddr} = 'EmailSpam';
14525  		        $this->{getline}    =  \&SpamReport;
14526                mlog( $fh, "email: spam report", 1 ) if !$EmailErrorsRemoveWhite;
14527                mlog( $fh, "email: combined spam report & whitelist deletion request") if $EmailErrorsRemoveWhite;
14528                $Stats{rcptReportSpam}++;
14529                sendque($fh,"250 OK\r\n");
14530                return;
14531            } elsif(lc $u eq lc "$EmailHam\@") {
14532                $this->{reporttype}=1;
14533                $this->{reportaddr} = 'EmailHam';
14534		        $this->{getline}    =  \&SpamReport;
14535                mlog( $fh, "email: notspam report", 1 ) if !$EmailErrorsModifyWhite;
14536                mlog( $fh, "email: combined notspam report & whitelist addition request" ) if $EmailErrorsModifyWhite == 1;
14537                $Stats{rcptReportHam}++;
14538                sendque($fh,"250 OK\r\n");
14539                return;
14540            } elsif(lc $u eq lc "$EmailWhitelistAdd\@") {
14541                $this->{reporttype}=2;
14542                $this->{reportaddr} = 'EmailWhitelistAdd';
14543                $this->{getline}=\&ListReport;
14544                mlog($fh,"email: whitelist addition request",1);
14545                $Stats{rcptReportWhitelistAdd}++;
14546                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14547                sendque($fh,"250 OK\r\n");
14548                return;
14549            } elsif(lc $u eq lc "$EmailWhitelistRemove\@") {
14550                $this->{reporttype}=3;
14551                $this->{reportaddr} = 'EmailWhitelistRemove';
14552                $this->{getline}=\&ListReport;
14553                mlog($fh,"email: whitelist deletion request");
14554                $Stats{rcptReportWhitelistRemove}++;
14555                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14556                sendque($fh,"250 OK\r\n");
14557                return;
14558            } elsif(lc $u eq lc "$EmailRedlistAdd\@") {
14559                $this->{reporttype}=4;
14560                $this->{reportaddr} = 'EmailRedlistAdd';
14561                $this->{getline}=\&ListReport;
14562                mlog($fh,"email: redlist addition request");
14563                $Stats{rcptReportRedlistAdd}++;
14564                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14565                sendque($fh,"250 OK\r\n");
14566                return;
14567            } elsif(lc $u eq lc "$EmailRedlistRemove\@") {
14568                $this->{reporttype}=5;
14569                $this->{reportaddr} = 'EmailRedlistRemove';
14570                $this->{getline}=\&ListReport;
14571                mlog($fh,"email: redlist deletion request");
14572                $Stats{rcptReportRedlistRemove}++;
14573                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14574                sendque($fh,"250 OK\r\n");
14575                return;
14576
14577            } elsif(lc $u eq lc "$EmailSpamLoverAdd\@") {
14578                $this->{reporttype}=10;
14579                $this->{reportaddr} = 'EmailSpamLoverAdd';
14580                $this->{getline}=\&ListReport;
14581                mlog($fh,"email spamlover addition report");
14582                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14583                sendque($fh,"250 OK\r\n");
14584                return;
14585            } elsif(lc $u eq lc "$EmailSpamLoverRemove\@") {
14586                $this->{reporttype}=11;
14587                $this->{reportaddr} = 'EmailSpamLoverRemove';
14588                $this->{getline}=\&ListReport;
14589                mlog($fh,"email spamlover deletion report");
14590                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14591                sendque($fh,"250 OK\r\n");
14592                return;
14593            } elsif(lc $u eq lc "$EmailNoProcessingAdd\@") {
14594                $this->{reporttype}=12;
14595                $this->{reportaddr} = 'EmailNoProcessingAdd';
14596                $this->{getline}=\&ListReport;
14597                mlog($fh,"email: noprocessing addition request");
14598                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14599                sendque($fh,"250 OK\r\n");
14600                return;
14601            } elsif(lc $u eq lc "$EmailNoProcessingRemove\@") {
14602                $this->{reporttype}=13;
14603                $this->{reportaddr} = 'EmailNoProcessingRemove';
14604                $this->{getline}=\&ListReport;
14605                mlog($fh,"email noprocessing deletion report");
14606                foreach my $ad (split(/ /o,$this->{rcpt})) {ListReportExec($ad,$this)};
14607                sendque($fh,"250 OK\r\n");
14608                return;
14609            } elsif ( lc $u eq lc "$EmailBlackAdd\@" ) {
14610                $this->{reporttype} = 14;
14611                $this->{reportaddr} = 'EmailBlackAdd';
14612                $this->{getline}    = \&ListReport;
14613                mlog( $fh, "email blacklist addition report" );
14614                sendque( $fh, "250 OK\r\n" );
14615                return;
14616            } elsif ( lc $u eq lc "$EmailBlackRemove\@" ) {
14617                $this->{reporttype} = 15;
14618                $this->{reportaddr} = 'EmailBlackRemove';
14619                $this->{getline}    = \&ListReport;
14620                mlog( $fh, "email blacklist deletion report" );
14621                sendque( $fh, "250 OK\r\n" );
14622                return;
14623            } elsif ( lc $u eq lc "$EmailPersBlackAdd\@" ) {
14624                $this->{reporttype} = 16;
14625                $this->{reportaddr} = 'EmailPersBlackAdd';
14626                $this->{getline}    = \&ListReport;
14627                mlog( $fh, "email personal blacklist addition report", 1 );
14628                sendque( $fh, "250 OK\r\n" );
14629                return;
14630            } elsif ( lc $u eq lc "$EmailPersBlackRemove\@" ) {
14631                $this->{reporttype} = 17;
14632                $this->{reportaddr} = 'EmailPersBlackRemove';
14633                $this->{getline}    = \&ListReport;
14634                mlog( $fh, "email personal blacklist deletion report", 1 );
14635                sendque( $fh, "250 OK\r\n" );
14636                return;
14637			}
14638        }
14639        if (     $EmailInterfaceOk
14640
14641                   && ( $this->{relayok} || $this->{externalsenderok}  )
14642
14643                )
14644        {
14645
14646             if ( lc $u eq lc "$EmailHelp\@" ) {
14647                $this->{reporttype} = 7;
14648                $this->{reportaddr} = 'EmailHelp';
14649                $this->{getline}    = \&HelpReport;
14650				mlog( $fh, "email: help-report request");
14651                $Stats{rcptReportHelp}++;
14652                sendque( $fh, "250 OK\r\n" );
14653                return;
14654            } elsif(lc $u eq lc "$EmailAnalyze\@") {
14655                $this->{reporttype}=8;
14656                $this->{reportaddr} = 'EmailAnalyze';
14657                $this->{getline}=\&AnalyzeReport;
14658                mlog( $fh, "email: analyze-report request");
14659                $Stats{rcptReportAnalyze}++;
14660                sendque($fh,"250 OK\r\n");
14661                return;
14662            } elsif(lc $u eq lc "$EmailBlockReport\@" or $u =~ /^RSBM_.+?$maillogExt\@$/i) {
14663
14664                $this->{rcpt}="$u$h";
14665                $this->{reporttype}=9;
14666                $this->{reportaddr} = 'EmailBlockReport';
14667                $this->{getline}=\&BlockReport;
14668                mlog($fh,"email: request for blocked spam report",1);
14669                sendque($fh,"250 OK\r\n");
14670                return;
14671            }
14672
14673        	ReturnMail($fh,$this->{mailfrom},"$base/reports/denied.txt",'assp-error',"\n") if ($this->{senderok} eq '2');
14674        	$this->{getline} = \&NullFromToData;
14675        	&NullFromToData($fh,$l);
14676        	mlog($fh,"denied connection to email interface ($uh) moved to NULL-connection",1);
14677	        return;
14678
14679        }
14680}
14681sub makeSubject {
14682        my $fh = shift;
14683		my $this = $Con{$fh};
14684        return if $Con{$fh}->{subject2};
14685
14686        my $sub;
14687        $sub = $1 if (substr($Con{$fh}->{header},0,index($Con{$fh}->{header},"\015\012\015\012")) =~ /\015\012Subject: *($HeaderValueRe)/iso);
14688        if (!$sub && $Con{$fh}->{maillogbuf}) {
14689            $sub = $1 if (substr($Con{$fh}->{maillogbuf},0,index($Con{$fh}->{maillogbuf},"\015\012\015\012")) =~ /\015\012Subject: *($HeaderValueRe)/iso);
14690        }
14691        $sub =~ s/\r\n\s*//go;
14692        my $slength = length $sub;
14693		if ($slength > 2000) {
14694			delayWhiteExpire($fh);
14695			$Con{$fh}->{prepend} = "[SubjectBomb]";
14696			mlog( $fh, "Subject exploit attempt with $slength bytes");
14697			$sub = substr($sub,0,50);
14698			seterror( $fh, "554 5.7.1 Subject exploit attempt with $slength bytes", 1 );
14699			return;
14700
14701
14702		}
14703		headerUnwrap($sub);
14704        return unless $sub;
14705        $sub =~ s/\r|\n|\t//go;
14706        $Con{$fh}->{subject2}=$sub;
14707        $Con{$fh}->{subject2} =~ s/$NONPRINT//go;
14708        $sub=decodeMimeWords2UTF8($sub);
14709        $sub = d8($sub);
14710        $Con{$fh}->{subject3} = $sub;
14711#        $Con{$fh}->{subject3} =~ s/\\x\{\d{2,}\}/_/go;
14712        $Con{$fh}->{subject3} =~ s/_{2,}/_/go;
14713        $sub =~ s/[^a-zA-Z0-9]/_/go;
14714        $sub =~ s/_{2,}/_/go;
14715
14716        $Con{$fh}->{originalsubject}=$sub;
14717        $Con{$fh}->{originalsubject} =~ tr/_/ /;
14718        $Con{$fh}->{originalsubject} =~ s/\s+$//o;
14719        $Con{$fh}->{originalsubject} =~ s/^\s+//o;
14720
14721        $Con{$fh}->{originalsubject} = $Con{$fh}->{subject3} if $LogCharset;
14722        $Con{$fh}->{originalsubject} = substr($Con{$fh}->{originalsubject},0,50) .
14723                                       '...' .
14724                                       substr($Con{$fh}->{originalsubject},length($Con{$fh}->{originalsubject})-50,50)
14725                     if length($Con{$fh}->{originalsubject}) > 100;
14726        $Con{$fh}->{subject}=substr($sub,0,50);
14727        $Con{$fh}->{subject} = e8($Con{$fh}->{subject});
14728        $Con{$fh}->{originalsubject} = e8($Con{$fh}->{originalsubject});
14729        $Con{$fh}->{subject3} = e8($Con{$fh}->{subject3});
14730
14731        $Con{$fh}->{logsubject} =
14732            ( $subjectLogging ? "$subjectStart$Con{$fh}->{originalsubject}$subjectEnd" : "" );
14733
14734}
14735
14736# get the header length of the DATA.
14737sub getheaderLength {
14738    my $fh = shift;
14739    return 0 unless $fh;
14740    my $l = 0;
14741    if (ref($fh) && ref($fh) ne 'SCALAR' && exists $Con{$fh}) {
14742        return 0 unless $Con{$fh}->{headerpassed};
14743        $l = index($Con{$fh}->{header}, "\x0D\x0A\x0D\x0A");
14744        return ($l >= 0 ? $l + 4 : 0);
14745    }
14746    return 0 unless length(ref($fh)?$$fh:$fh);
14747    $l = index((ref($fh)?$$fh:$fh), "\x0D\x0A\x0D\x0A");
14748    return ($l >= 0 ? $l + 4 : 0);
14749}
14750
14751# get the header part of the DATA.
14752sub getheader {
14753    my ( $fh, $l ) = @_;
14754    d('getheader');
14755    my $reply;
14756    my $done;
14757    my $fn;
14758    my $er;
14759    my $done2;
14760    my $this = $Con{$fh};
14761
14762    if($this->{inerror} or $this->{intemperror}) {  # got 4/5xx from MTA - possibly next step after DATA
14763        if ($send250OK or ($this->{ispip} && $send250OKISP)) {
14764            mlog($fh,"info: connection is moved to NULL after MTA has sent an error reply in DATA part") if $ConnectionLog;
14765            $this->{getline}=\&NullData;
14766            NullData($fh,$l);
14767            return;
14768        }
14769        $this->{cleanSMTPBuff} = 1;         # delete the SMTPbuff
14770        $this->{header} = '';
14771        $this->{getline}=\&getline;
14772        getline($fh,$l);
14773        return;
14774    }
14775
14776    if (!DenyStrictOK( $fh, $this->{ip} ) ) {
14777
14778			$Stats{denyStrict}++;
14779			NoLoopSyswrite($fh,"521 $myName does not accept mail from network $this->{ip} - closing transmission\r\n");
14780            done($fh);
14781            return;
14782    }
14783
14784     my $ret = matchIP( $this->{ip}, 'noProcessingIPs', $fh );
14785     if (!$this->{noprocessing} && $noProcessingIPs
14786            && $ret
14787            &&  !matchIP( $this->{ip}, 'NPexcludeIPs', 0, 1 )
14788
14789            && !$this->{nonoprocessing}
14790            )
14791            {
14792                $this->{noprocessing}  		= 1;
14793
14794                $this->{passingreason} 		= "noProcessingIPs '$ret'" if !$this->{passingreason};
14795            }
14796    $this->{relayok} = 1 if matchIP($this->{ip}, 'acceptAllMail',   0, 1 );
14797	if($this->{spamblocked}  or $this->{inerror} or $this->{inerror} or $this->{intemperror} ) {
14798        my $server = $this->{friend};
14799        $this->{getline} = \&getline;
14800        sendque( $server, $l );
14801        done($fh);
14802        return;
14803    }
14804
14805    $this->{header} .= $l;
14806    my $headerlength=length($this->{header});
14807    my $maxheaderlength=$HeaderMaxLength;
14808
14809
14810
14811    if($HeaderMaxLength && $headerlength>$maxheaderlength && $this->{prepend} !~ /OversizedHeader/) {
14812        delayWhiteExpire($fh);
14813        $this->{prepend}="[OversizedHeader]";
14814        mlog($fh,"Possible Mailloop: Headerlength ($headerlength) > $maxheaderlength");
14815        seterror($fh,"554 5.7.1 possible mailloop - oversized header ($headerlength)",1);
14816        $Stats{msgverify}++;
14817        return;
14818    }
14819
14820	if (   scalar keys %MEXH
14821		&& ! $this->{relayok}
14822		&& $this->{prepend} !~ /Max-Equal-X-Header/
14823        && ! $this->{noprocessing}
14824        && ! $this->{whitelisted}
14825		&& $l =~ /^X-(?!ASSP)/io
14826		&& $l !~ /X-Notes-Item/i)
14827	{
14828        my $line = $l;
14829        $line =~ s/\r?\n//go;
14830        my ($xh) = $line =~ /^($HeaderNameRe)\:/o;
14831        my $maxval;
14832        $maxval = matchHashKey(\%MEXH,$xh) if $xh;
14833        if ($xh && $maxval && ++$this->{Xheaders}{lc $xh} > $maxval) {
14834            delayWhiteExpire($fh);
14835            $this->{prepend}="[Max-Equal-X-Header]";
14836            mlog($fh,"too many (MaxEqualXHeader = $maxval) equal X-header lines '$xh'");
14837            seterror($fh,"554 5.7.7 too many ($maxval) equal X-headers '$xh'",1);
14838            $Stats{msgverify}++;
14839            return;
14840        }
14841    }
14842
14843    if (! $this->{relayok} && ! $this->{received}) {
14844        $this->{received} = $l =~ /^(?:Received:)|(?:Origin(?:at(?:ing|ed))?|Source)[\s\-_]?IP:/oi;
14845    }
14846
14847    my $orgnp;
14848    if ( $l =~ /^\.?[\r\n]*$/ ) {
14849    	$done2 = $l=~/^\.[\r\n]+$/o;
14850		$orgnp = $this->{noprocessing};
14851        $this->{noprocessing} = 0 if $this->{noprocessing} eq '2';  # noprocessing on message size
14852        $this->{headerpassed} = 1;
14853        $this->{skipnotspam} = 1;
14854        $this->{maillength} = $this->{headerlength} = $headerlength;
14855        $this->{headerlength} -= 3 if $done2;
14856        $this->{headerlength} = 0 if $this->{headerlength} < 0;
14857        my $slok;
14858		$this->{localmail} = localmail($this->{mailfrom});
14859		if (! $this->{from} && $this->{header} =~ /(?:^|\n)from:($HeaderValueRe)/oi) {
14860            my $from = $1;
14861            headerUnwrap($from);
14862            $this->{from} = $1 if $from =~ /($EmailAdrRe\@$EmailDomainRe)/oi;
14863        }
14864
14865
14866        if ($this->{addressedToSpamBucket}){
14867			SpamBucketOK($fh, $done2);
14868			return 1;
14869		}
14870
14871		if ( !$this->{noprocessing} && !$this->{whitelisted} &&  !$this->{relayok}){
14872			HistoryOK( $fh, $this->{ip} );
14873			}
14874
14875
14876        if(! &MailLoopOK($fh)) {
14877            $this->{prepend}="[MailLoop]";
14878            mlog($fh,"warning: possible mailloop - found own received header more than $detectMailLoop times");
14879        	sendque( $fh, "250 OK\r\n" );
14880        	$Con{$fh}->{getline} = \&NullFromToData;
14881            $Stats{msgverify}++;
14882            return;
14883        }
14884
14885
14886
14887        my $tip = $this->{ip};
14888        $tip = $this->{cip} if $this->{cip};
14889        if (   !$this->{relayok}
14890            && !$this->{contentonly}
14891            && pbWhiteFind($tip) )
14892        {
14893			$this->{contentonly} = "whitebox:$tip";
14894
14895		}
14896
14897        if (   !$this->{relayok}
14898            && !$this->{contentonly}
14899            && $contentOnlyRe
14900            && $contentOnlyReRE != ""
14901            && $this->{header} =~ ( '(' . $contentOnlyReRE . ')' ) )
14902        {
14903			$this->{contentonly} = $1;
14904
14905            pbBlackDelete( $fh, $this->{ip} );
14906
14907        }
14908
14909
14910        if ( $allLogRe
14911             && ! $this->{alllog}
14912             && $this->{header} =~ /$allLogReRE/ )
14913        {
14914            $this->{alllog}=1;
14915        }
14916
14917
14918    	if ( ! $this->{red}
14919            && $this->{header} =~ /(auto-submitted\:|subject\:.*?auto\:)/i )
14920            # RFC 3834
14921        {
14922			d('isred auto');
14923            $this->{red} = ($1||$2);
14924        }
14925
14926        if ( ! $this->{red}
14927            && $redRe
14928            && $this->{header} =~ /($redReRE)/ ) {
14929			$this->{red} = ($1||$2);
14930            mlogRe( $fh, $this->{red}, "Red" );
14931
14932        }
14933
14934
14935        if (!$this->{whitelisted} && $whiteRe && $this->{header}=~/($whiteReRE)/) {
14936            mlogRe($fh,($1||$2),"White");
14937            $this->{whitelisted}=1;
14938        }
14939
14940
14941        if(!$this->{ccnever} && $ccSpamNeverRe && $this->{header}=~/($ccSpamNeverReRE)/) {
14942            mlogRe($fh,($1||$2),"CCnever");
14943            $this->{ccnever}=1;
14944        }
14945
14946
14947        if(!$this->{noprocessing}) {
14948        	my ( $ipcountry, $orgname, $domainname, $blacklistscore, $hostname_matches_ip, $ipCIDR ) = split( /\|/, SBCacheFind($this->{ip}) ) ;
14949        	$this->{NPexcludeIPs} = 1 if matchIP( $this->{ip}, 'NPexcludeIPs', 0, 1 );
14950        	$this->{noprocessing} = 1 if matchIP( $this->{ip}, 'noProcessingSenderBaseIPs', 0, 1 ) && !$this->{NPexcludeIPs};
14951        	$this->{passingreason} = "$this->{orgname}" if !$this->{passingreason};
14952        }
14953
14954
14955        if(!$this->{noprocessing}  && $npRe && $this->{header}=~/($npReRE)/)
14956        {
14957            mlogRe($fh,($1||$2),"Noprocessing");
14958            pbBlackDelete($fh,$this->{ip});
14959            $this->{noprocessing}=1;
14960        }
14961
14962        if(!$this->{spamlover} & 1 && $SpamLoversRe && $this->{header}=~/($SpamLoversReRE)/ ) {
14963            mlogRe($fh,($1||$2),"SpamLover");
14964            $this->{spamlover}=1;
14965        }
14966
14967
14968        if ( ($this->{received} || $this->{relayok}) && $this->{ispip} && $this->{header} =~ /X-Forwarded-For: ($IPRe)/io) {
14969	        $this->{cipdone} = 1;
14970            $this->{cip} = ipv4TOipv6($1);
14971            my $cip = ipv6expand($1);
14972            my $cip2 = $1;
14973            my $orgHelo = $this->{helo};
14974	        while ( $this->{header} =~ /Received:\s+from\s+(?:([^\s]+)\s)?(?:.+?)(?:$this->{cip}|$cip|$cip2)\]?\)(.{1,80})by.{1,20}/gis ) {
14975                $this->{ciphelo} = $1;
14976                $this->{helo} = $1 if $1;
14977                my $rhelo = $2;
14978                $rhelo =~ s/\r?\n/ /go;
14979                $rhelo =~ /.+?helo\s*=\s*([^\s]+)/io;
14980                if ($1) {
14981                    $this->{ciphelo} = $1;
14982                    $this->{helo} = $1;
14983                }
14984            }
14985            if ($this->{cip}) {
14986             	$this->{NPexcludeIPs} = 1 if matchIP( $this->{cip}, 'NPexcludeIPs', 0, 1 );
14987            	$this->{noprocessing} = 1 if matchIP( $this->{cip}, 'noProcessingIPs', 0, 1 ) && !$this->{NPexcludeIPs};
14988            	$this->{noprocessing} = 1 if matchIP( $this->{cip}, 'noProcessingSenderBaseIPs', 0, 1 ) && !$this->{NPexcludeIPs};
14989    			$this->{whitelisted} = matchIP( $this->{cip}, 'whiteListedIPs', 0, 1 );
14990    			$this->{nopb} = 1 if matchIP( $this->{cip}, 'noPB', 0, 1 );
14991    		}
14992            if ($this->{cip} && matchIP($this->{cip},'ispip',$fh)) {
14993                delete $this->{cip};
14994                delete $this->{ciphelo};
14995                $this->{helo} = $orgHelo;
14996            } else {
14997                $this->{nohelo} = 1 if ( $this->{cip} && matchIP( $this->{cip}, 'noHelo', $fh ) );
14998                mlog( $fh, "Found X-Forwarded-For: $this->{ciphelo} ($this->{cip})", 1, 2 ) if $this->{cip};
14999            }
15000	    } elsif ( ($this->{received} || $this->{relayok}) && $this->{ispip} && $ispHostnames && !$this->{cipdone} ) {
15001            $this->{cipdone} = 1;
15002            my $orgHelo = $this->{helo};
15003            while ( $this->{header} =~ /Received:\s+from\s+(?:([^\s]+)\s)?(?:.+?)($IPRe)(.{1,80})by.{1,20}($ispHostnamesRE)/gis ) {
15004                my $cip = ipv6expand(ipv6TOipv4($2));
15005                my $helo = $1;
15006                my $rhelo = $3;
15007                next if $cip =~ /$IPprivate/o;
15008
15009                $this->{cip} = $cip;
15010                $this->{ciphelo} = $helo || $cip;
15011                $rhelo =~ s/\r?\n/ /go;
15012                $rhelo =~ /.+?helo\s*[= ]?\s*([^\s\)]+)/io;
15013                $this->{ciphelo} = $1 if $1;
15014            }
15015            if ($this->{cip}) {
15016
15017            	$this->{noprocessing} = 1 if matchIP( $this->{cip}, 'noProcessingIPs', 0, 1 ) && !matchIP( $this->{ip}, 'NPexcludeIPs', 0, 1 );
15018    			$this->{whitelisted} = matchIP( $this->{cip}, 'whiteListedIPs', 0, 1 );
15019    			$this->{nopb} = 1 if matchIP( $this->{cip}, 'noPB', 0, 1 );
15020    		}
15021            if ($this->{cip} && matchIP($this->{cip},'ispip',$fh)) {
15022                delete $this->{cip};
15023                delete $this->{ciphelo};
15024                $this->{helo} = $orgHelo;
15025            } else {
15026                $this->{nohelo} = 1 if ( $this->{cip} && matchIP( $this->{cip}, 'noHelo', $fh ) );
15027                mlog( $fh, "Originating IP/HELO:  $this->{cip} / $this->{ciphelo}", 1, 2 ) if $this->{cip};
15028            }
15029        }
15030
15031
15032		&makeSubject($fh);
15033
15034    	if(!$this->{spamloversre} && $SpamLoversRe && $this->{header} =~ /($SpamLoversReRE)/ ) 	{
15035            mlogRe($fh,($1||$2),"SpamLoversRe");
15036            $this->{spamloversre} = $1||$2;
15037    	}
15038
15039		if ( ! ForgedHeloOK($fh ) ) {
15040            $reply = $SpamError;
15041            $reply =~ s/REASON/$this->{messagereason}/g;
15042            $reply = replaceerror ($fh, $reply);
15043            $this->{test} = "allTestMode";
15044            thisIsSpam( $fh, $this->{messagereason},
15045                $invalidHeloLog, $reply, $this->{testmode}, 0, 0 );
15046            return;
15047
15048		}
15049
15050
15051
15052		if (!SenderBaseOK( $fh, $this->{ip} ) ) {
15053            my $slok = $this->{allLoveSpam} == 1;
15054            $Stats{sbblocked}++ unless $slok;
15055            $reply = $SpamError;
15056            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15057            $reply =~ s/REASON/SenderBase/g;
15058            $reply = replaceerror ($fh, $reply);
15059            $this->{test} = "allTestMode";
15060            thisIsSpam( $fh, $this->{messagereason},
15061                $spamSBLog, $reply, $this->{testmode}, $slok, $done2 );
15062            return;
15063
15064		}
15065
15066
15067
15068        if ( !$this->{noprocessing} && $npRe
15069			&& !$this->{relayok}
15070        	&& !$this->{addressedToSpamBucket}
15071            && $npReRE != ""
15072            && $this->{header} =~ ( '(' . $npReRE . ')' ) )
15073        {
15074			mlogRe( $fh,($1||$2), "npRe" );
15075            pbBlackDelete( $fh, $this->{ip} );
15076            $this->{noprocessing}  = 1;
15077            $this->{passingreason} = "npRe '$1'" if !$this->{passingreason};
15078
15079        }
15080
15081        if ( !$this->{noprocessing}
15082			&& $this->{relayok}
15083			&& $npLocalRe &&  $this->{header}  =~ /($npLocalReRE)/i )
15084
15085        {
15086			my $subre = ($1||$2);
15087            $this->{noprocessing}  = 1;
15088            $this->{noprocessinglocal}  = 1;
15089            $this->{passingreason} = "npLocalRe '$subre'";
15090
15091        }
15092
15093
15094        if (! $this->{from} && $this->{header} =~ /(?:^|\n)from:($HeaderValueRe)/oi) {
15095            my $from = $1;
15096            headerUnwrap($from);
15097            $this->{from} = $1 if $from =~ /($EmailAdrRe\@$EmailDomainRe)/oi;
15098        }
15099
15100        if (  !$this->{red}
15101            && $redRe
15102            && $this->{header} =~ /($redReRE)/ )
15103        {
15104            $this->{red} = ( $1 || $2 );
15105            mlogRe( $fh, $this->{red}, "Red" );
15106
15107        }
15108
15109        $this->{red} = $this->{redlist} = "$this->{mailfrom} in RedList"
15110          if $Redlist{"$this->{mailfrom}"};
15111        $this->{red} = $this->{redlist} = "$this->{rcpt} in RedList"
15112          if $Redlist{"$this->{rcpt}"};
15113
15114        # if RELAYOK check localdomains if appropriate
15115        if (   $this->{relayok}
15116            && !$this->{red}
15117            && !matchSL( "$this->{mailfrom}", 'noLocalSenderCheck' )
15118            && $DoLocalSenderDomain
15119            && !$this->{acceptall}
15120            && !localmail( $this->{mailfrom} )
15121            && $this->{mailfrom} !~ /$BSRE/
15122            && !localmail( $this->{rcpt} ) )
15123        {
15124
15125            $this->{prepend} = "[RelayAttempt]";
15126            NoLoopSyswrite( $fh,
15127                "530 Relaying not allowed - sender domain not local\r\n", 0 );
15128            $this->{messagereason} =
15129              "relay attempt blocked for unknown local sender domain";
15130            mlog( $fh, $this->{messagereason} );
15131            $Stats{rcptRelayRejected}++;
15132            delayWhiteExpire($fh);
15133            done($fh);
15134            return;
15135        }
15136
15137        # if RELAYOK check localaddresses if appropriate
15138        if (   $this->{relayok}
15139            && $DoLocalSenderAddress
15140            && !$this->{acceptall}
15141            && !matchSL( "$this->{mailfrom}", 'noLocalSenderCheck' )
15142            && !$this->{red}
15143            && !LocalAddressOK($fh)
15144            && !localmail( $this->{rcpt} ) )
15145        {
15146            $this->{prepend} = "[RelayAttempt]";
15147            NoLoopSyswrite( $fh,
15148                "530 Relaying not allowed - local sender address unknown\r\n",
15149                0 );
15150            $this->{messagereason} =
15151              "relay attempt blocked for unknown local sender address";
15152            mlog( $fh, $this->{messagereason} );
15153            $Stats{rcptRelayRejected}++;
15154            delayWhiteExpire($fh);
15155            done($fh);
15156            return;
15157        }
15158
15159		onwhitelist( $fh, $this->{header}) if !$this->{relayok} && !$this->{red};
15160
15161        if ( !DenyOK( $fh, $this->{ip} ) ) {
15162            my $slok = $this->{allLoveSpam} == 1;
15163			$Stats{denyConnection}++ unless $slok;
15164            my $reply = $SpamError;
15165            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15166            $reply = $DenyError if $DenyError;
15167            thisIsSpam( $fh, $this->{messagereason}, $DenyIPLog, $reply,
15168                $allTestMode, 0, 0 );
15169            return;
15170        }
15171  		if (&MessageScoreHigh($fh,25)) {
15172                	MessageScore( $fh, 1 );
15173                	return;
15174 		}
15175
15176		if (!&DroplistOK($fh, $this->{ip}))
15177
15178    	{
15179
15180        	mlog( $fh, "[spam found] -- $this->{messagereason} -- $this->{logsubject}" );
15181        	my $slok = $this->{allLoveSpam} == 1;
15182        	$Stats{denyStrict}++ unless $slok;
15183
15184            thisIsSpam( $fh, $this->{messagereason},
15185                $DenyIPLog, $DenyError, 0, $slok, $done2 );
15186        	return;
15187
15188    	}
15189
15190
15191
15192		if ( !BlackDomainOK($fh) ) {
15193            my $slok = $this->{allLoveSpam} == 1;
15194            $Stats{blacklisted}++ unless $slok;
15195            $reply = $SpamError;
15196            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15197            $reply =~ s/REASON/Blacklisted Domain/g;
15198            $reply = replaceerror ($fh, $reply);
15199            $this->{newsletterre}		= '';
15200            thisIsSpam( $fh, $this->{messagereason},
15201                $invalidSenderLog, $reply, $this->{testmode}, $slok, $done2 );
15202            return;
15203        }
15204
15205
15206		if ( !PersonalBlackDomainOK($fh) ) {
15207            my $slok = $this->{allLoveSpam} == 1;
15208            $Stats{blacklisted}++ unless $slok;
15209            my ($to) = $this->{rcpt} =~ /(\S+)/;
15210            $reply = $SpamError;
15211            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15212            $reply =~ s/REASON/mailbox <$to> unavailable/g;
15213            $reply = replaceerror ($fh, $reply);
15214            $this->{newsletterre}		= '';
15215            thisIsSpam( $fh, $this->{messagereason},
15216                $invalidSenderLog, $reply, $this->{testmode}, $slok, $done2 );
15217            return;
15218        }
15219  		if (&MessageScoreHigh($fh,25)) {
15220                	MessageScore( $fh, 1 );
15221                	return;
15222 		}
15223 		my $skip = ($SameSubjectSkipRe && substr($this->{header},0,$MaxBytes) =~ /$SameSubjectSkipReRE/i);
15224		my $reply;
15225		if ( $DoSameSubject
15226				&& !$this->{whitelisted}
15227				&& !$this->{noprocessing}
15228
15229 				&& !$skip
15230                && !$this->{relayok} ) {
15231			if (! &SameSubjectOK( $fh)) {
15232            	$Stats{SameSubject}++;
15233  				$reply = $SpamError;
15234  				$reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15235            	$reply =~ s/REASON/ameSubjec/g;
15236            	$reply = replaceerror ($fh, $reply);
15237            		thisIsSpam( $fh, $this->{messagereason},6,
15238                $reply, 0, 0, $done2 );
15239            	return
15240            }
15241
15242		}
15243		if (&MessageScoreHigh($fh,25)) {
15244                	MessageScore( $fh, 1 );
15245                	return;
15246 		}
15247        if ( !LocalSenderOK( $fh, $this->{ip} ) ) {
15248            my $slok = $this->{allLoveSpam} == 1;
15249            $Stats{senderInvalidLocals}++ unless $slok;
15250            $reply = $SpamError;
15251            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15252            $reply =~ s/REASON/Invalid Sender/g;
15253            $reply = replaceerror ($fh, $reply);
15254            $this->{spamloversre} = "";
15255            thisIsSpam( $fh, "$this->{messagereason}", $invalidSenderLog, $reply,
15256                $allTestMode, $slok, $done2 );
15257            return;
15258        }
15259  		if (&MessageScoreHigh($fh,25)) {
15260                	MessageScore( $fh, 1 );
15261                	return;
15262 		}
15263        if (! $this->{whitelisted} &&
15264        	 $this->{header} !~ /$whiteReRE/ ) {
15265            if (! &NoSpoofingOK( $fh, 'mailfrom' )  || ($DoNoSpoofing4From && ! &NoSpoofingOK( $fh, 'from' )) ) {
15266                my $slok = $this->{allLoveSpam} == 1;
15267                $Stats{senderInvalidLocals}++ unless $slok;
15268                $reply = $SpamError;
15269                $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15270                $reply =~ s/REASON/$this->{messagereason}/go;
15271                thisIsSpam( $fh, "$this->{messagereason}", $invalidSenderLog, $reply,
15272                    $this->{testmode}, $slok, $done2 );
15273                return;
15274            }
15275
15276        }
15277
15278
15279        if (	$this->{relayok}
15280          	&&	!$this->{red}
15281            && 	$redRe
15282            && 	$redReRE != ""
15283            && $this->{header} =~ /($redReRE)/ ) {
15284			$this->{red} = ($1||$2);
15285            mlogRe( $fh, $this->{red}, "Red" );
15286
15287        }
15288
15289        if (! $this->{msgid} && $this->{header}=~/\nMessage-ID:($HeaderValueRe)/si) {
15290            $this->{msgid} = decodeMimeWords2UTF8($1);
15291            $this->{msgid}=~s/[\s>]+$//;
15292            $this->{msgid}=~s/^[\s<]+//;
15293        }
15294
15295
15296        # header is done
15297
15298
15299
15300
15301        if ( "$fh" =~ /SSL/io && $tlsValencePB) {
15302                $this->{messagereason} = 'SSL-TLS-connection-OK';
15303                pbAdd( $fh, $this->{ip}, $tlsValencePB, 'SSL-TLS-connection-OK' );
15304        }
15305        if ( $blockLocalRe
15306            && $this->{relayok}
15307            && "$this->{mailfrom}$this->{header}" =~  /($blockLocalReRE)/i )
15308        {
15309			mlogRe( $fh, $1, "blockLocalRe" );
15310            $reply = "554 5.7.1 blocked - because of '$1'\r\n";
15311            $this->{prepend} = "[blockLocalRe]";
15312            thisIsSpam( $fh, "'$1' found in blockLocalRe",  $spamBombLog, $reply, 0, 0, $done2 );
15313			return;
15314        }
15315
15316        if ( $npLocalRe
15317            && $this->{relayok}
15318            && "$this->{mailfrom}$this->{header}" =~ /($npLocalReRE)/i )
15319        {
15320			mlogRe( $fh, ($1||$2), "npLocalRe",1 );
15321
15322            $this->{noprocessing}  = 1;
15323            $this->{passingreason} = "npLocalRe '($1||$2)'";
15324
15325        }
15326
15327
15328
15329
15330        if ( ! &RBLCacheOK($fh,$this->{ip},0) || ! &RBLok($fh,$this->{ip},0) )  {
15331            return;
15332        }
15333
15334
15335
15336 		if (!$AsASecondary &&  !BombHeaderOK( $fh, \$this->{header} ) ) {
15337            delayWhiteExpire($fh);
15338            my $slok = $this->{allLoveBoSpam} == 1;
15339 			$slok = 0 if $this->{messagereason} =~ /bombCharSets/i;
15340
15341            $Stats{bombs}++ unless $slok;
15342			$this->{test} = "allTestMode";
15343			my $reply = $SpamError;
15344			$reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15345            $reply =~ s/REASON/$this->{messagereason}/g;
15346            $reply = replaceerror ($fh, $reply);
15347            thisIsSpam( $fh, "$this->{messagereason}", $spamBombLog, $reply, $allTestMode, $slok, $done2 );
15348			return;
15349 		}
15350
15351
15352		RWLok( $fh, $this->{ip} );
15353
15354
15355
15356
15357        if (!invalidHeloOK( $fh, $this->{helo} ) ) {
15358            my $slok = $this->{allLoveSpam} == 1;
15359            $Stats{invalidHelo}++ unless $slok;
15360            $reply = $SpamError;
15361            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15362            $reply =~ s/REASON/Invalid HELO Format/g;
15363            $reply = replaceerror ($fh, $reply);
15364            $this->{prepend} = "[InvalidHELO]";
15365            $this->{test} = "allTestMode";
15366            thisIsSpam( $fh, "Invalid HELO: '$this->{helo}'",
15367                $invalidHeloLog, $reply, $allTestMode, $slok, $done2 );
15368			return;
15369        }
15370
15371        IPinHeloOK( $fh );
15372
15373		&GRIPvalue($fh,$this->{ip});
15374		GoodHelo($fh,$this->{helo});
15375        BlackHeloOK( $fh, $this->{helo} );
15376
15377        if (! $this->{relayok} && ! headerAddrCheckOK($fh) ) {
15378            $this->{skipnotspam} = 0;return;
15379        }
15380		if (&MessageScoreHigh($fh,25)) {
15381                	MessageScore( $fh, 1 );
15382                	return;
15383 		}
15384        &MSGIDsigOK($fh) if $this->{isbounce};
15385
15386
15387		if (!SPFok($fh, $done2)) {
15388
15389			my $slok=$this->{allLoveSpam}==1;
15390			$Stats{spffails}++ unless $slok;
15391			$this->{prepend} = "[SPF]";
15392        	thisIsSpam( $fh,  $this->{messagereason},
15393            $SPFFailLog, $this->{reply}, $this->{testmode}, $slok,$done2);
15394			return;
15395
15396
15397		}
15398
15399		if (&MessageScoreHigh($fh,25)) {
15400                	MessageScore( $fh, 1 );
15401                	return;
15402 		}
15403
15404
15405        if (!MXAOK($fh)) {
15406
15407            my $slok=$this->{allLoveMXASpam}==1;
15408            unless ($slok) {$Stats{mxaMissing}++;}
15409            $reply = $SpamError;
15410            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15411            $reply =~ s/REASON/Missing MX and A record/go;
15412            $reply = replaceerror ($fh, $reply);
15413            $this->{prepend}="[MissingMXA]";
15414            thisIsSpam($fh,"missing MX and A record",$invalidSenderLog,$reply,$this->{testmode},$slok,$done2);
15415            return;
15416
15417
15418		}
15419
15420		if (&MessageScoreHigh($fh,25)) {
15421                	MessageScore( $fh, 1 );
15422                	return;
15423 		}
15424
15425       # remove Disposition-Notification headers if needed
15426       if ($removeDispositionNotification
15427        	&& !$this->{relayok}
15428        	&& !$this->{whitelisted}
15429        	&& !$this->{noprocessing}
15430        	&& $this->{header} =~ s/(?:ReturnReceipt|Return-Receipt-To|Disposition-Notification-To):$HeaderValueRe//gio
15431            ) {
15432            $this->{maillength} = length($this->{header});
15433            mlog($fh,"removed Disposition-Notification headers from mail",1) if $ValidateSenderLog > 1;
15434
15435        }
15436
15437        if (!$this->{addressedToSpamBucket} &&  $this->{invalidSRSBounce}
15438            && $SRSValidateBounce
15439            && !( $this->{ispip} )
15440            && !$this->{validatebounce}
15441            && !( $noSRS && matchIP( $this->{ip}, 'noSRS', 0, 1 ) ) )
15442        {
15443
15444            my $slok = $this->{allLoveSpam} == 1;
15445            $Stats{msgNoSRSBounce}++ unless $slok;
15446            $this->{prepend} = "[SRS]";
15447            $this->{validatebounce} = 1;
15448            $this->{messagereason} =
15449              "bounce address not SRS signed";
15450            pbAdd( $fh, $this->{ip}, 'srsValencePB', "Not_SRS_Signed" ) if $SRSValidateBounce !=2;
15451            my $tlit = tlit($SRSValidateBounce);
15452            mlog( $fh, "$tlit ($this->{messagereason})" )if $SRSValidateBounce !=1;
15453            $this->{test} = "allTestMode";
15454            thisIsSpam(
15455                $fh, $this->{messagereason},
15456                $SPFFailLog, '554 5.7.5 Bounce address not SRS signed',
15457                $allTestMode, $slok, $done2
15458            ) if $SRSValidateBounce ==1;
15459
15460 # cleared all the above rules - off to Bayesian if SPF and DNSBL is OK.
15461 # and no testcheck was successful.
15462        }
15463
15464        if ($done2) {
15465                    	&getbody($fh,$l);
15466                    	$this->{getline}=\&getline;
15467                    	return;
15468        } else {
15469
15470                    	$this->{getline} = \&getbody;
15471        }
15472   }
15473
15474}
15475
15476sub headerAddrCheckOK {
15477    my $fh = shift;
15478    my $this = $Con{$fh};
15479    d('headerAdrCheckOK');
15480    return 1 if skipCheck($this,'aa','ro');
15481    return headerAddrCheckOK_Run($fh);
15482}
15483
15484sub headerAddrCheckOK_Run {
15485    my $fh = shift;
15486    my $this = $Con{$fh};
15487    d('headerAdrCheckOK');
15488
15489    for my $bcc ('bcc','cc','to') {
15490        my $BCC = uc $bcc;
15491        my $remove = (($bcc eq 'bcc') && $removeForeignBCC);
15492        if ($remove && $this->{header} =~ s/(^|\n)$bcc:(?:$HeaderValueRe)/$1/igs) {
15493            mlog($fh,"info: found and removed unexpected $BCC: recipient addresses in incoming mail") if $ValidateUserLog >= 2;
15494            $this->{nodkim} = 1;     # we have modified the header and should skip the DKIM check for this reason
15495        } elsif ($DoHeaderAddrCheck && ! $nolocalDomains && (my @bccRCPT = $this->{header} =~ /(?:^|\n)$bcc:($HeaderValueRe)/igs)) {
15496            mlog($fh,"info: found and checking for unexpected $BCC: recipient addresses in incoming mail") if $ValidateUserLog >= 2;
15497            foreach my $bc (@bccRCPT) {
15498                headerUnwrap($bc);
15499                while ($bc =~ /($EmailAdrRe\@$EmailDomainRe)/igos) {
15500                    my $addr = $1;
15501                    if ($ReplaceRecpt) {
15502                        my $newadr = RcptReplace($addr,batv_remove_tag('',$this->{mailfrom},0),'RecRepRegex');
15503                        if (lc $newadr ne lc $addr) {
15504                            $this->{header} =~ s/((?:^|\n)$bcc:(?:$HeaderValueRe)*?)\Q$addr\E/$1$newadr/is;
15505                            mlog($fh,"$BCC: - recipient $addr replaced with $newadr") if $ValidateUserLog;
15506                            $addr = $newadr;
15507                            $this->{nodkim} = 1;     # we have modified the header and should skip the DKIM check for this reason
15508                        }
15509                    }
15510                    next if localmailaddress($fh,$addr);
15511
15512                    if (   ! $this->{whitelisted}
15513                        && ! ($this->{noprocessing} & 1)
15514                        && (&pbTrapFind($addr) || (matchSL($addr,'spamtrapaddresses') && ! matchSL($addr,'noPenaltyMakeTraps'))))
15515                    {
15516                        $this->{prepend}="[Trap]";
15517                        pbWhiteDelete($fh,$this->{ip});
15518                        $this->{whitelisted} = '';
15519                        my $mf = batv_remove_tag(0,lc $this->{mailfrom},'');
15520                        if ( &Whitelist($mf,$addr) ) {
15521                    		&Whitelist($mf,$addr,'delete');
15522                    		mlog( $fh, "penalty trap: whitelist deletion: $this->{mailfrom}" );
15523                        }
15524                        RWLCacheAdd( $this->{ip}, 4 );  # fake RWL none
15525                        mlog($fh,"[spam found] penalty trap address: $addr");
15526                        $this->{messagereason} = "penalty trap address: $addr in $BCC:";
15527                        pbAdd( $fh, $this->{ip}, $stValencePB, "spamtrap" );
15528                        $Stats{penaltytrap}++;
15529                        delayWhiteExpire($fh);
15530                        my $reply = "421 closing transmission - 5.1.1 User unknown: $addr\r\n";
15531                        if ($PenaltyTrapPolite) {
15532                            $reply = $PenaltyTrapPolite;
15533                            $reply =~ s/EMAILADDRESS/$addr/go;
15534                        }
15535                        if ($send250OK or ($this->{ispip} && $send250OKISP)) {
15536                            $this->{getline} = \&NullData;
15537                        } else {
15538                            sendque( $fh, $reply );
15539                            $this->{closeafterwrite} = 1;
15540                            done2($this->{friend});
15541                            delete $this->{friend};
15542                        }
15543                        $this->{prepend} = '';
15544                        return 0;
15545                    }
15546
15547                    if (localmail($addr)) {
15548                        $this->{header} =~ /(?:^|\n)$bcc:(?:$HeaderValueRe)*?\Q$addr\E/is;
15549                        next if skipCheck($this,'aa','wl','rw','nb','nbip');
15550                        next if ($this->{noprocessing} & 1);
15551                        mlog($fh,"$BCC: - local but not valid recipient address '$addr' detected in mail header") if $ValidateUserLog;
15552                        pbAdd( $fh, $this->{ip}, $irValencePB, 'InvalidAddress' );
15553                        next;
15554                    }
15555                    next if $bcc eq 'cc';   #cc: can be foreign
15556                    next if $bcc eq 'to';   #to: can be foreign
15557
15558                    pbAdd($fh,$this->{ip},'rlValencePB','RelayAttempt',0);
15559                    $this->{prepend} = "[RelayAttempt]";
15560                    my $reply = "421 closing transmission - $BCC: recipient ($addr) is not local\r\n";
15561                    $this->{messagereason} = "relay attempt blocked for non local $BCC: recipient - $addr";
15562                    mlog(0,"Notice: you may set 'removeForeignBCC' to prevent this relay attempt blocking") if $ValidateUserLog;
15563                    $this->{spamfound} = 1;
15564                    if ($send250OK or ($this->{ispip} && $send250OKISP)) {
15565                        my $fn = $this->{maillogfilename};   # store the mail if we have to receive it
15566                        unless ($fn) {
15567                            $fn = Maillog($fh,'',6); # tell maillog what this is -> discarded.
15568                        }
15569                        $fn=' -> '.$fn if $fn ne '';
15570                        $fn='' if !$fileLogging;
15571                        my $logsub = ( $subjectLogging && $this->{originalsubject} ? " $subjectStart$this->{originalsubject}$subjectEnd" : '' );
15572                        mlog($fh,"[spam found] $this->{messagereason}$logsub".de8($fn),0,2);
15573                        $this->{getline} = \&NullData;
15574                    } else {
15575                        mlog( $fh, "[spam found] $this->{messagereason}" );
15576                        sendque( $fh, $reply );
15577                        $this->{closeafterwrite} = 1;
15578                        done2($this->{friend});
15579                        delete $this->{friend};
15580                    }
15581                    $Stats{rcptRelayRejected}++;
15582                    delayWhiteExpire($fh);
15583                    $this->{prepend} = '';
15584                    return 0;
15585                }
15586            }
15587        }
15588    }
15589    $this->{prepend} = '';
15590    return 1;
15591}
15592# do SPF (sender policy framework) checks
15593# uses Mail::SPF v2.005
15594sub SPFok {
15595    my $fh = shift;
15596    my $this = $Con{$fh};
15597    return 1 if $this->{spamfound};
15598	return 1 if $this->{addressedToSpamBucket};
15599	my $bip=&ipNetwork($this->{ip}, $DelayUseNetblocks );
15600    return 0 unless SPFok_Run($fh);    # do SPF check on 'mail from'
15601    if (   $DoSPFinHeader
15602        && defined $this->{spfok}
15603        && ! $this->{error}
15604        && $this->{header} =~ /\nfrom:\s*($HeaderValueRe)/ois)   # and 'from:'
15605    {
15606        my $head = $1;
15607        headerUnwrap($head);
15608        if ($head =~ /($EmailAdrRe\@($EmailDomainRe))/o) {
15609            my $mf = $1;
15610            my $mfd = lc $2;
15611            my $envmfd;
15612            if ( $blockstrictSPFRe && $mf =~ /$blockstrictSPFReRE/ or localmail($mf) && $failstrictLOCAL ) # ONLY if the 'from'  address is in strictSPFre
15613            {
15614        		 $envmfd = $1 if lc $this->{mailfrom} =~ /\@([^@]*)/o;
15615        		 return 1 if ($mfd eq $envmfd);
15616
15617        		 delete $this->{spfok};
15618        		 $this->{SPFokDone} = 0;
15619        		 my $omf = $this->{mailfrom};
15620        		 $this->{mailfrom} = $mf;
15621        		 my $ret = SPFok_Run($fh);
15622        		 $this->{mailfrom} = $omf;
15623        		 return 0 unless $ret;
15624            }
15625        }
15626    }
15627
15628    return 1;
15629}
15630
15631sub SPFok_Run {
15632    my $fh = shift;
15633    d('SPFok_Run');
15634
15635
15636    my $this = $Con{$fh};
15637    $fh = 0 if "$fh" =~ /^\d+$/o;
15638    return 1 if !$ValidateSPF;
15639    return 1 if $noSpoofingCheckDomain
15640		&& matchSL( $this->{mailfrom}, 'noSpoofingCheckDomain' );
15641    return 1 if $this->{SPFokDone};
15642    $this->{SPFokDone} = 1;
15643    my $ip   = $this->{ip};
15644    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
15645    my $helo = $this->{helo};
15646    $helo = $this->{ciphelo} if $this->{ispip} && $this->{ciphelo};
15647    $this->{prepend} = '';
15648    my $block;
15649    my $strict;
15650    my $local;
15651    my $result;
15652	my $bip=&ipNetwork($this->{ip}, $DelayUseNetblocks );
15653    return 1 if $this->{relayok} && !$SPFLocal;
15654    return 1 if $this->{contentonly};
15655    return 1 if $this->{ispip} && !$this->{cip};
15656    return 1 if $this->{whitelisted} && !$SPFWL;
15657    return 1 if $this->{noprocessing} && !$SPFNP;
15658    return 1 if !$SPFLocal && $ip =~ /$IPprivate/o;
15659
15660    my $ip_overwrite;
15661	my $mValidateSPF = $ValidateSPF;
15662	$this->{testmode} = 0;
15663	$this->{testmode} = "ValidateSPF" if $ValidateSPF == 4;
15664	$mValidateSPF = 1 if $ValidateSPF == 4;
15665    if ( $noSPFRe &&
15666        ($this->{mailfrom} =~ /($noSPFReRE)/ ||
15667         $this->{header} =~ /($noSPFReRE)/ )
15668       )
15669    {
15670        mlogRe( $fh, ($1||$2), "noSPF" );
15671        return 1;
15672    }
15673    if ( $strictSPFRe && $this->{mailfrom} =~ /($strictSPFReRE)/ )
15674    {
15675        mlogRe( $fh, ($1||$2), "SPFstrict" );
15676        $strict = 1;
15677
15678    }
15679    if ( $blockstrictSPFRe && $this->{mailfrom} =~ /($blockstrictSPFReRE)/ )
15680    {
15681        mlogRe( $fh, ($1||$2), "blockSPFstrict" );
15682        $strict = 1;
15683        $block  = 1;
15684    }
15685    if (   $strictSPFReRE
15686        && $this->{mailfrom}
15687        && $this->{mailfrom} =~ /($strictSPFReRE)/ )
15688    {
15689
15690        $strict = 1;
15691
15692    }
15693
15694    $strict = 1 if localmail($this->{mailfrom}) && $failstrictLOCAL;
15695
15696    if (   $blockstrictSPFReRE
15697        && $this->{mailfrom}
15698        && $this->{mailfrom} =~ ( '(' . $blockstrictSPFReRE . ')' ) )
15699    {
15700
15701        $strict = 1;
15702        $block  = 1;
15703    }
15704
15705    $block = 1 if $this->{localmail} && $blockstrictLOCAL;
15706    my $slok = $this->{allLoveSpam} == 1;
15707	my $mf = lc $this->{mailfrom};
15708    $mf = batv_remove_tag($fh,$this->{mailfrom},'');
15709	my $mfd;
15710	$mfd = $1 if $mf =~ /\@([^@]*)/o;
15711	my $mfdd; $mfdd = $1 if $mf =~ /(\@.*)/o;
15712
15713	if (! $mfd) {
15714        $mfd = $helo;
15715        $mf = "postmaster\@$helo" unless $mf;
15716    }
15717
15718    return 1 if ($mfd =~ /^\[?$IPRe\]?$/o);
15719
15720    my $slok = $this->{allLoveSpam} == 1;
15721
15722    my $tlit = tlit($ValidateSPF);
15723    $this->{prepend} = "[SPF]";
15724
15725    #$this->{prepend} .= "[$tlit]" if $ValidateSPF >= 2;
15726
15727    my (
15728        $spf_result, $local_exp, $authority_exp,
15729        $spf_record, $spf_fail,  $received_spf
15730    );
15731
15732
15733    my ( $cachetime, $cresult,  $crecord ) = SPFCacheFind( $bip, $mfd);
15734
15735	$spf_result = $cresult;
15736
15737	$spf_record = $crecord;
15738
15739	if ($spf_record =~ /v=spf1 all/) {
15740
15741		mlog( $fh, "spf_record: '$spf_record'" );
15742	}
15743	my $itime = time;
15744    if ( !$spf_result ) {
15745
15746        my $query;
15747        eval {
15748			local $SIG{ALRM} = sub { die "__alarm__\n" };
15749      		alarm(15);
15750
15751            my ( $identity, $scope );
15752            if ($mfd) {
15753                $identity = $mf;
15754                $scope    = 'mfrom';
15755            } else {
15756                $identity = $helo;
15757                $scope    = 'helo';
15758            }
15759
15760            my $res = Net::DNS::Resolver->new(
15761                nameservers => \@nameservers,
15762                tcp_timeout => $DNStimeout,
15763                udp_timeout => $DNStimeout,
15764                retrans     => $DNSretrans,
15765                retry       => $DNSretry
15766            );
15767			my $spf_server = Mail::SPF::Server->new(
15768                hostname     => $myName,
15769                dns_resolver => $res,
15770                max_dns_interactive_terms => $SPF_max_dns_interactive_terms
15771                );
15772            my $request = Mail::SPF::Request->new(
15773                versions      => [ 1, 2 ],
15774                scope         => $scope,
15775                identity      => $identity,
15776                ip_address    => $ip,
15777                helo_identity => $helo
15778            );
15779
15780
15781			$result = $spf_server->process($request);
15782            $spf_record = $request->record;
15783
15784            $spf_result    = $result->code;
15785            $local_exp     = $result->local_explanation;
15786            $authority_exp = $result->authority_explanation
15787              if $result->is_code('fail');
15788            $received_spf = $result->received_spf_header;
15789            $this->{received_spf} = $received_spf unless $fh;    # for analyze only
15790			my $spfmatch;
15791
15792            $spfmatch = $1 if $received_spf =~ /(mechanism .+? matched)/io;
15793            if ($spf_result eq 'pass' &&
15794                    (  $spf_record =~ /\s*((?:v\s*=\s*spf.|spf2.0\/\S+).*?\+all)/oi #  ...+all  allows all IPs
15795                    || $spf_record =~ /\s*((?:v\s*=\s*spf.|spf2.0\/\S+).*?\D0+\.0+\.0+\.0+(?:\/0+\s+)?.*?(?:all)?)/oi  # '0.0.0.0/0' allows also all IPs
15796                    || $spfmatch =~ /(\+all)/io
15797                    || $spfmatch =~ /\D(0+\.0+\.0+\.0+)/io
15798                    )
15799                   )
15800                {
15801                    my $rec = $1;
15802                    (my $what, $spf_result) = ($rec=~/[+? ]all/io || $rec!~/all/io) ?('SPAMMER',($1=~/\?/o)?'softfail':'fail'):('suspicious','none');
15803                    $ip_overwrite = '0.0.0.0';
15804                    mlog($fh,"SPF: found $what SPF record/mechanism '$rec' for domain $mfd - SPF result is set to '$spf_result'") if $SPFLog;
15805                    $this->{received_spf} .= "\&nbsp;<span class=negative>found $what record/mechanism '$rec' - switched result to '$spf_result'</span>" unless $fh;    # for analyze only
15806
15807            }
15808
15809            if ($DebugSPF) {
15810
15811                mlog( $fh, "$tlit spf_result:$spf_result", 1, 1 );
15812                mlog( $fh, "identity:$identity",           1, 1 );
15813                mlog( $fh, "scope:$scope",                 1, 1 );
15814                mlog( $fh, "spf_record:$spf_record",       1, 1 );
15815                mlog( $fh, "local_exp:$local_exp",         1, 1 );
15816                mlog( $fh, "authority_exp:$authority_exp", 1, 1 );
15817                mlog( $fh, "received_spf:$received_spf",   1, 1 );
15818            }
15819			alarm(0);
15820        };
15821
15822        #exception check
15823        $itime = time - $itime;
15824        if ($@) {
15825			alarm(0);
15826        	if ( $@ =~ /__alarm__/ ) {
15827 #           	mlog( $fh, "SPF: timed out after $itime secs.", 1 );
15828
15829 #           	SPFCacheAdd( $ip,'error', $mfd, $helo );
15830            	return 1;
15831            } else {
15832            	mlog( $fh, "SPF: $@", 1, 1 ) if $ExceptionLogging;
15833            	return 1;
15834            }
15835        }
15836
15837
15838    }
15839
15840    $this->{spf_result} = $spf_result;
15841    if (    $spf_result eq 'fail'
15842        || ($spf_result eq 'softfail' && ($SPFsoftfail || $strict))
15843        || ($spf_result eq 'neutral' && ($SPFneutral || $strict))
15844        || ($spf_result eq 'none' && ($SPFnone || $strict))
15845        || ($spf_result eq 'unknown' && ($SPFunknown || $strict))
15846        || ($spf_result =~ /error/io && ($SPFqueryerror || $strict))
15847
15848      )
15849    {
15850        if ($SPFqueryerror && $spf_result =~ /error|^unknown/io ) {
15851            $spf_fail = 0;
15852        } else {
15853            $spf_fail = 1;
15854        }
15855        $this->{spfok} = 0;
15856        pbWhiteDelete( $fh, $ip );
15857    } else {
15858        $spf_fail = 0;
15859        $this->{spfok} = ($spf_result eq 'pass') ? 1 : 0;
15860        $strict = 0 if $this->{spfok};
15861        $block = 0 if $this->{spfok};
15862    }
15863
15864    $received_spf = "SPF: $spf_result";
15865	$received_spf .= " record='$spf_record'" if $spf_record;
15866    $received_spf .= " ip=$ip";
15867    $received_spf .= " mailfrom=$this->{mailfrom}"
15868      if ( defined( $this->{mailfrom} ) );
15869	$received_spf .= " helo=$this->{helo}" if ( defined( $this->{helo} ) );
15870	$received_spf =~ s/\.\./\./;
15871	$received_spf =~ s/\'\'/\'/;
15872	SPFCacheAdd( ($ip_overwrite?$ip_overwrite:$bip), $spf_result, $mfd, $spf_record ) if $spf_result !~ /error/io && $result;
15873	my $valence;
15874	$this->{spffail} = 1 if $spf_result eq 'fail';
15875	$this->{messagereason} = "SPF $spf_result";
15876    $this->{myheader} .= "X-Assp-Received-$received_spf\r\n"
15877      if $AddSPFHeader && !$this->{spfok};
15878    $this->{myheader} .= "X-Original-Authentication-Results: $myName; spf=$spf_result ($received_spf)\r\n"
15879      if $AddSPFHeader && $spf_result ne 'none';
15880   	if ($ValidateSPF != 2) {
15881    	if ( $spf_result =~ /pass/ ) {
15882           	$valence =  int $spfpValencePB;
15883			pbAdd( $fh, $ip,$valence, "SPF$spf_result" ) if $fh;
15884
15885    	} elsif ( $spf_result =~ /neutral|none/ &&  $strict) {
15886    		$valence =  int $spfValencePB;
15887			pbAdd( $fh, $ip,$valence, "SPF$spf_result(strict)" ) if $fh;
15888    	} elsif ($spf_result eq 'fail') {
15889        	$valence =  $spfValencePB;
15890        	pbAdd( $fh, $ip,$valence, "SPF$spf_result" ) if $fh;
15891
15892    	} elsif ( $spf_result =~ /^unknown|error/) {
15893        	$valence =  $spfeValencePB;
15894        	pbAdd( $fh, $ip,$valence, "SPF$spf_result" ) if $fh;
15895        } elsif ( $spf_result =~ /^unknown|error/  &&  $strict) {
15896        	$valence =  $spfValencePB;
15897        	pbAdd( $fh, $ip,$valence, "SPF$spf_result(strict)" ) if $fh;
15898    	} elsif ( $spf_result =~ /softfail/ ) {
15899
15900        	$valence =  int $spfsValencePB;
15901
15902        	$valence =  int $spfValencePB if $strict;
15903        	pbAdd( $fh, $ip,$valence, "SPF$spf_result" ) if $fh;
15904       	} elsif (!$this->{spfok} && $strict) {
15905        	$valence =  $spfValencePB;
15906        	pbAdd( $fh, $ip,$valence, "SPF$spf_result(strict)" ) if $fh;
15907
15908    } }
15909
15910	$tlit= "[scoring:$valence]" if $ValidateSPF == 3;
15911    mlog( $fh, "$tlit $received_spf")
15912      if $SPFLog && $spf_result ne 'pass' && $spf_record && $ValidateSPF == 3;
15913    return 1 if $ValidateSPF == 3 && !$block;
15914
15915    if ( $spf_fail == 1 ) {
15916		return 0 unless $fh;
15917        # SPF fail (by our local rules)
15918
15919        my $reply = $SpamError;
15920        $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
15921        $reply =~ s/REASON/"failed SPF: $local_exp"/go;
15922        $this->{reply} = replaceerror ($fh, $reply);
15923
15924        return 0;
15925    }
15926
15927    return 1;
15928}
15929
15930sub SPF_get_records_from_text {
15931    my ($server, $rec, $rr_type, $version, $scope, $domain) = @_;
15932
15933    my $record;
15934    my $vLength = length($version);
15935    my $maxversion = 2;
15936    my $class = $CanUseSPF?$server->record_classes_by_version->{
15937        unpack"A$vLength",${"\130"}+sprintf"%.0f",abs($version+1/3)-$maxversion
15938    }:5;
15939    if ($CanUseSPF && eval("require $class;")) {
15940        $record = $class->new_from_string($rec);
15941        undef $record
15942            if  defined($record)
15943            and ! grep($scope eq $_, $record->scopes);  # record covers requested scope?
15944    } else {
15945#        mlog(0, "error: Mail::SPF v2 seems not to be installed - $@\n",1);
15946    }
15947    return $record;
15948}
15949sub GRIPv {
15950    my ($ip ) = @_;
15951    return 0 if matchIP( $ip, 'noGRIP',            0, 1 );
15952    my	$ipnet = ipNetwork($ip, 1);
15953	$ipnet =~ s/\.0$// if ($ipnet =~ /\d+\.\d+\.\d+\.0/);
15954
15955    my $v = $Griplist{$ipnet};
15956
15957    $v = "0.01" if $v == 0;
15958    $v = "0.99" if $v == 1;
15959
15960    return $v;
15961}
15962# do GRIP value
15963
15964# do GRIP value
15965sub GRIPvalue {
15966    my ( $fh, $ip ) = @_;
15967    return 1 if ! $griplist;
15968    return 1 if !$gripValencePB;
15969    return GRIPvalue_Run( $fh, $ip );
15970}
15971sub GRIPvalue_Run {
15972    my ( $fh, $ip ) = @_;
15973    d('GRIPvalue');
15974    my $this = $Con{$fh};
15975    return 1 if $this->{gripdone};
15976    $this->{gripdone} = 1;
15977    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
15978	return 1 if $this->{notspamtag};
15979    return 1 if $this->{addressedToSpamBucket};
15980
15981    return 1 if $this->{relayok};
15982    return 1 if $this->{ispip};
15983    return 1 if $this->{nopb};
15984    return 1 if $this->{nopbwhite};
15985    return 1 if $this->{whitelisted};
15986    return 1 if $this->{noprocessing};
15987    return 1 if $ip =~ /$IPprivate/o;
15988
15989    $this->{messagereason} = '';
15990    my	$ipnet = &ipNetwork($ip, 1);
15991    $ipnet =~ s/\.0+$//o;
15992    my $v;
15993
15994    $v = $Griplist{$ipnet};
15995
15996    return 1 unless defined $v;
15997
15998    $this->{messagereason} = "$ipnet in griplist ($v)" unless $this->{messagereason};
15999    if ($v >= 0.9) {
16000        pbAdd( $fh, $ip, int($v * $gripValencePB), 'griplist', 1 ) ;
16001        return 0;
16002    }
16003
16004    return 1;
16005}
16006
16007sub Glob {
16008    my @g;
16009    if ($] !~ /^5\.016/o) {
16010        @g = glob("@_");
16011    } else {
16012        map {push @g , < $_ >;} @_ ;
16013    }
16014    return @g;
16015}
16016
16017sub existFile {
16018    my $file = shift;
16019    return 0 unless $file;
16020    return ($eF->( $file ) or -e $file);
16021}
16022sub unzipgz {
16023  my ($infile,$outfile) = @_;
16024  my $buffer ;
16025  my $gzerrno;
16026  return 0 unless $CanUseHTTPCompression;
16027  mlog(0,"decompressing file $infile to $outfile") if $MaintenanceLog;
16028  eval{
16029  ($open->( my $OUTFILE, '>',$outfile)) or die 'unable to open '.de8($outfile)."\n";
16030  ($open->( my $INFILE, '<',$infile)) or die 'unable to open '.de8($infile)."\n";
16031  $OUTFILE->binmode;
16032  my $gz = gzopen($INFILE, 'rb') or die 'unable to open '.de8($infile)."\n";
16033  while ($gz->gzread($buffer) > 0) {
16034      $OUTFILE->print($buffer);
16035  }
16036  $gzerrno != Z_STREAM_END() or die 'unable to read from '.de8($infile).": $gzerrno" . ($gzerrno+0)."\n";
16037  $gz->gzclose() ;
16038  $OUTFILE->close;
16039  };
16040  if ($@) {
16041      mlog(0,"error : gz - $@");
16042      return 0;
16043  }
16044  return 1;
16045}
16046sub unzip {
16047    my ( $infile, $outfile ) = @_;
16048    my $buffer;
16049    my $gzerrno;
16050    my $ip;
16051    my $mask;
16052    my $reason;
16053    my $rest;
16054    return 0 unless $CanUseHTTPCompression;
16055    mlog( 0, "deflating file $infile to $outfile" ) if $MaintenanceLog;
16056    eval {
16057
16058        unzip $infile => $outfile
16059         or die "unzip failed: $UnzipError\n";
16060
16061
16062    };
16063    if ($@) {
16064        mlog( 0, "error : gz - $@" );
16065        return 0;
16066    }
16067    return 1;
16068}
16069
16070
16071sub zipgz {
16072    my ($infile,$outfile) = @_;
16073    my $gzerrno;
16074    mlog(0,"inflating file $infile to $outfile") if $MaintenanceLog;
16075    (open my $IN, "<","$infile")
16076       or mlog(0,"Cannot open $infile:\n") && return 0;
16077
16078    (my $gz = gzopen($outfile, "wb"))
16079      or mlog(0,"Cannot open $outfile: $gzerrno\n") && return 0;
16080
16081    while (<$IN>) {
16082        $gz->gzwrite($_)
16083          or mlog(0,"error writing $outfile: $gzerrno\n") && return 0;
16084    }
16085
16086    $gz->gzclose ;
16087    close $IN;
16088    return 1;
16089}
16090
16091
16092sub NotSpamTagGenerate {
16093
16094    my ($fh) = @_;
16095    return $NotSpamTag if !$NotSpamTagRandom;
16096    d('NotSpamTagGenerate');
16097    my $this = $Con{$fh};
16098    my $str;
16099    my $numsec;
16100    my $numtags;
16101    my $gennum = rand(20);
16102	my
16103
16104
16105    $numsec = @msgid_secrets;
16106    unless ($numsec) {
16107        mlog(0, "warning : config error - no MSGID-secrets (MSGIDSec) defined");
16108        return $NotSpamTag;
16109    }
16110    $gennum = rand($numsec);
16111
16112    my $gen = $msgid_secrets[$gennum]{gen};
16113
16114    my $secret = $msgid_secrets[$gennum]{secret};
16115
16116    my $day = sprintf("%03d", (time / 86400 + 7) % 1000);
16117
16118    my $tag = $secret . $day;
16119
16120
16121    mlog(0, "info: generated '$tag' for NotSpamTag") ;
16122    my $exptime = time + 7 * 24 * 3600;
16123    $NotSpamTags{$tag}=$exptime;
16124    $NotSpamTagsObject->flush()    	if $NotSpamTagsObject;
16125	$NotSpamTagGenerated = $tag if $NotSpamTagRandom;
16126    return $tag;
16127
16128}
16129
16130sub MSGIDaddSig {
16131    my ($fh,$msgid) = @_;
16132    d('MSGIDaddSig');
16133    my $this = $Con{$fh};
16134    my $str;
16135    my $numsec;
16136    my $gennum = rand(20);
16137
16138
16139    return $msgid unless $this->{relayok};
16140    return $msgid unless $DoMSGIDsig;
16141    return $msgid unless $CanUseSHA1;
16142    return $msgid unless $msgid;
16143    return $msgid unless $fh;
16144    return $msgid if ($noRedMSGIDsig && $this->{red});
16145    return $msgid if ($MSGIDsigAddresses && ! matchSL($this->{mailfrom},'MSGIDsigAddresses'));
16146
16147    return $msgid if ($noMSGIDsigRe && substr($this->{header},0,$MaxBytes + $this->{headerlength}) =~ /$noMSGIDsigReRE/i);
16148
16149    if ($msgid =~ /.+\<(.+)\>.*/) {
16150        $str = $1;
16151    }
16152    return $msgid unless $str;
16153
16154    $numsec = @msgid_secrets;
16155    unless ($numsec) {
16156        mlog(0, "warning : config error - no MSGID-secrets (MSGIDSec) defined");
16157        return $msgid;
16158    }
16159    $gennum = rand($numsec);
16160    my $gen = $msgid_secrets[$gennum]{gen};
16161    my $secret = $msgid_secrets[$gennum]{secret};
16162    my $day = sprintf("%03d", (time / 86400 + 7) % 1000);
16163    my $hash_source =  $gen . $day . $str;
16164    my $sha1 = eval {substr(sha1_hex($hash_source . $secret), 0, 6);};
16165    my $tag = $MSGIDpreTag . '.' . $gen . $day . $sha1 . '.';
16166    my $tagval = $tag.$str;
16167    $msgid =~ s/\Q$str\E/$tagval/;
16168	$this->{notspamtag} = $tag;
16169    mlog($fh, "info: added MSGID signature '$tag' to header") if $MSGIDsigLog >= 2;
16170    $this->{nodkim} = 1;
16171    return $msgid;
16172}
16173
16174sub MSGIDsigRemove {
16175    my $fh = shift;
16176    d('MSGIDsigRemove');
16177    my $this = $Con{$fh};
16178    return 1 if ! $CanUseSHA1;
16179    my $removed;
16180    my $old;
16181
16182    return if $this->{MSGIDsigRemoved};
16183    my $headlen = $this->{headerlength} || getheaderLength($fh);  # do only the header
16184    $this->{headerlength} = $headlen;
16185    my $maxlen = $MaxBytes && $MaxBytes < $this->{maillength} ? $MaxBytes : $this->{maillength};
16186    $headlen = $maxlen if ($maxlen > $headlen && $this->{isbounce});      # do complete mail if bounce
16187    my $alltodo = substr($this->{header},0,$headlen);
16188    my $todo = $alltodo;
16189    my $found = 0;
16190    $this->{prepend}="[MSGID-sig]";
16191    do {
16192        if ($todo =~ /((?:[^\r\n]+\:)[\r\n\s]*)?\<$MSGIDpreTag\.(\d)(\d\d\d)(\w{6})\.([^\r\n]+)\>/) {
16193            my ($line, $gen, $day, $hash, $orig_msgid) = ($1,$2,$3,$4,$5);
16194            $found = 1;
16195            my $secret;
16196            for (@msgid_secrets) {
16197                if ($_->{gen} == $gen) {
16198                    $secret = $_->{secret};
16199                    last;
16200                }
16201            }
16202            if ($secret) {
16203                my $hash_source =  $gen . $day . $orig_msgid;
16204                my $hash2 = eval{substr(sha1_hex($hash_source . $secret), 0, 6);};
16205                if ($hash eq $hash2) {
16206                    $old = $MSGIDpreTag.'.'.$gen.$day.$hash.'.';
16207                    $alltodo =~ s/$old//;
16208                    $removed = 1;
16209                    $this->{nodkim} = 1;
16210                    $line =~ s/[\r\n\s]*//og;
16211                    mlog($fh,"info: removed MSGID-signature from [$line]") if ($line && $MSGIDsigLog >= 2);
16212                }
16213            }
16214            $old = $MSGIDpreTag.'.'.$gen.$day.$hash.'.'.$orig_msgid;
16215            my $pos = index($todo, $old) + length($old);
16216            $todo = substr($todo,$pos,length($todo) - $pos);
16217        } else {
16218            $found = 0;
16219        }
16220    } while($found);
16221    if ($removed) {
16222        substr($this->{header},0,$headlen,$alltodo);
16223    }
16224    my $txt = $this->{isbounce} ? 'and body in bounced message' : '';
16225#    mlog($fh, "info: removed MSGID-signature from header $txt") if ($MSGIDsigLog && $removed);
16226    $this->{MSGIDsigRemoved} = 1 if (! $this->{isbounce} || ($MaxBytes && $MaxBytes < $this->{maillength})); # in bounces we have to process the body
16227    return;
16228}
16229
16230sub MSGIDsigOK {
16231    my $fh = shift;
16232    d('MSGIDsigOK');
16233    my $this = $Con{$fh};
16234    my $ip;
16235	$ip = $this->{cip} if $this->{ispip} && $this->{cip};
16236    return 1 if $this->{msgidsigdone};
16237    $this->{msgidsigdone} = 1;
16238
16239    return 1 if !$DoMSGIDsig;
16240    return 1 if $this->{contentonly};
16241    return 1 if !$this->{isbounce};
16242    return 1 if $this->{ispip} && !$this->{cip};
16243    return 1 if $this->{notspamtag};
16244    return 1 if $this->{addressedToSpamBucket};
16245    return 1 if $this->{relayok};
16246    return 1 if $noMsgID && matchIP( $ip, 'noMsgID', $fh );
16247    return 1 if $this->{whitelisted};
16248    return 1 if $this->{noprocessing};
16249    return 1 if ! $CanUseSHA1;
16250
16251    return 1 if ($MSGIDsigAddresses
16252    			&& !matchSL($this->{rcpt},'MSGIDsigAddresses'));
16253
16254
16255    my $tlit = &tlit($DoMSGIDsig);
16256    $this->{prepend}="[MSGID-sig]";
16257
16258    if (&MSGIDsigCheck($fh)) {
16259        $this->{prepend}="[MSGID-sigok]";
16260        mlog($fh,"$tlit MSGID signing OK for bounce message") if $MSGIDsigLog >= 2;
16261        return 1;
16262    }
16263
16264	return 1 if !$this->{from};
16265    $this->{messagereason}="MSGID-sig check failed for bouncing sender  \<$this->{mailfrom}\>";
16266    $this->{messagereason}="MSGID-sig check failed for bouncing null sender" if !$this->{mailfrom};
16267    $tlit = "[scoring:$msigValencePB]" if $DoMSGIDsig == 3;
16268    mlog($fh,"$tlit $this->{messagereason}") if $MSGIDsigLog > 1 && $DoMSGIDsig >= 2;
16269    return 1 if $DoMSGIDsig == 2 ;
16270    pbWhiteDelete($fh,$this->{ip});
16271    pbAdd($fh,$this->{ip},$msigValencePB,"MSGID-signature-failed",1);
16272
16273	return 1 if $DoMSGIDsig != 1;
16274	$Stats{msgMSGIDtrErrors}++;
16275
16276   if ($Back250OKISP==2 or ($Back250OKISP  && ($this->{ispip} || $this->{cip}))) {
16277        $this->{accBackISPIP} = 1;
16278
16279    }
16280
16281    thisIsSpam($fh,$this->{messagereason},$BackLog,'554 5.7.8 Bounce address - message was never sent by this domain',$allTestMode,0,1);
16282}
16283
16284sub MSGIDsigCheck {
16285    my $fh = shift;
16286    my $this = $Con{$fh};
16287    return 1 if $noMsgID && matchIP($this->{ip} , 'noMsgID', $fh );
16288    d('MSGIDsigCheck');
16289    my $headlen = $MaxBytes && $MaxBytes < $this->{maillength} ? $MaxBytes + $this->{headerlength} : $this->{maillength};
16290    my $tocheck = substr($this->{header},0,$headlen);
16291    $this->{prepend}="[MSGID-sig]";
16292    while (my ($cline,$line, $gen, $day, $hash, $orig_msgid) = ($tocheck =~ /(($HeaderNameRe\:)[\r\n\s]*?\<$MSGIDpreTag\.(\d)(\d\d\d)(\w{6})\.([^\r\n>]+)\>)/)) {
16293        my $pos = index($tocheck, $cline) + length($cline);
16294        $tocheck = substr($tocheck,$pos,length($tocheck) - $pos);
16295        my $secret;
16296        for (@msgid_secrets) {
16297            if ($_->{gen} == $gen) {
16298                $secret = $_->{secret};
16299                last;
16300            }
16301        }
16302        next unless ($secret);
16303        my $hash_source =  $gen . $day . $orig_msgid;
16304        my $hash2 = substr(sha1_hex($hash_source . $secret), 0, 6);
16305        if ($hash eq $hash2) {
16306            my $today = (time / 86400) % 1000;
16307            my $dt = ($day - $today + 1000) % 1000;
16308            if ($dt <= 7) {
16309            	$this->{prepend}="[MSGID-sigok]";
16310            	$this->{noprocessing} = 1;
16311            	$this->{passingreason} = "valid MSGID";
16312                mlog($fh, "info: found valid MSGID signature in [$line] - mail accepted") if $MSGIDsigLog or $this->{noMSGIDsigLog};
16313                return 1;
16314            } else {
16315
16316                mlog($fh, "info: found expired MSGID signature in [$line]") if $MSGIDsigLog or $this->{noMSGIDsigLog};
16317            }
16318        }
16319    }
16320    # bounce without MSGID sig - bad
16321    mlog($fh, "info: found bounce sender: \<$this->{mailfrom}\> and recipient: \<$this->{rcpt}\> without valid MSGID-signature") if ($MSGIDsigLog && ! $this->{noMSGIDsigLog});
16322    return 0;
16323}
16324
16325sub configChangeMSGIDSec {
16326    my ($name, $old, $new, $init)=@_;
16327
16328    mlog(0,"AdminUpdate: MSGID secrets updated from '$old' to '$new'") unless $init || $new eq $old;
16329    $new = "0=assp|1=fbmtv" if !$new;
16330    $MSGIDSec=$new;
16331    $new=checkOptionList($new,'MSGIDSec',$init);
16332    @msgid_secrets = ();
16333    my @errors;
16334    my $errout;
16335
16336    my $count = -1;
16337    my $records = -1;
16338    for my $v (split(/\|/o,$new)) {
16339        push @errors, $v;
16340        $records++;
16341        next unless $v;
16342        next if ($v =~ /key\d/) ;
16343        next if ($v =~ /\s+/ig);
16344        my ($gen,$sec) = split(/=/,$v);
16345        next unless ($gen ne '' && $sec);
16346        next unless ($gen =~ /^\d$/);
16347        pop @errors;
16348        $count++;
16349        last if ($count == 10);
16350        $msgid_secrets[$count]{gen} = $gen;
16351        $msgid_secrets[$count]{secret} = $sec;
16352    }
16353    $errout = join('|',@errors);
16354    if ($count == -1) {
16355        $records++;
16356        $count++;
16357        my $diff = $records -$count;
16358        my $ignored = $diff ? " : $diff records ignored because of wrong syntax or using default values : $errout" : '';
16359#        mlog(0, "warning: NO MSGIDsig-secrets activated - MSGIDsig-check is now disabled $ignored") ;
16360        return "<span class=\"negative\"> - NO MSGID-secrets activated - MSGIDsig-check is now disabled $ignored</span>";
16361    } else {
16362        $records++;
16363        $count++;
16364        my $diff = $records -$count;
16365        my $ignored = $diff ? " : $diff records ignored because of wrong syntax : $errout" : '';
16366#        mlog(0, "info: $count MSGID-secrets activated") if !$init and $old ne $new;
16367        return $diff ? " $count MSGIDsig-secrets activated <span class=\"negative\"> - $ignored</span>" : " $count MSGIDsig-secrets activated";
16368    }
16369}
16370
16371sub batv_remove_tag {
16372    my ($fh,$mailfrom,$store) = @_;
16373    if ($mailfrom =~ /^(prvs=\d\d\d\d\w{6}=)([^\r\n]*)/o) {
16374
16375        $Con{$fh}->{$store} = $mailfrom if ($fh && $store);
16376        $mailfrom = lc $2;
16377    }
16378    return $mailfrom;
16379}
16380
16381sub downloadHTTP {
16382    my ($gripListUrl,$gripFile,$nextload,$list,$dl,$tl,$ds,$ts) = @_;
16383    my $dummy = 0;
16384    my $showNext = 1;
16385    if (!$nextload or !defined($$nextload)) {
16386        $nextload = \$dummy;
16387        $showNext = 0;
16388    }
16389    my $rc;
16390    my $time = time;
16391
16392	my $longRetry  = $time + ( ( int( rand($dl) ) + $tl ) * 3600 ) + int(rand(3600));    # no sooner than tl hours and no later than tl+dl hours
16393    my $shortRetry = $time + ( ( int( rand($ds) ) + $ts ) * 3600 ) + int(rand(3600));    # no sooner than ts hours and no later than ts+ds hours
16394
16395    # let's check if we really need to
16396    my @s     = stat($gripFile);
16397    my $mtime = $s[9];
16398    if (-e $gripFile && $time - $mtime <= $tl * 3600 && $$nextload != 0 ) {
16399        # file exists and has been downloaded recently, must have been restarted
16400        $$nextload = $mtime + $longRetry - $time;
16401        $time = $$nextload - $time;
16402        mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16403        return 0;
16404    }
16405
16406    if ( !$CanUseLWP ) {
16407        mlog( 0, "ConfigError: $list download failed: LWP::Simple Perl module not available" );
16408        $$nextload = $longRetry;
16409        $time = $$nextload - $time;
16410        mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16411        return 0;
16412    }
16413
16414    if ( -e $gripFile ) {
16415    	if ( !-r $gripFile ) {
16416    	    mlog( 0, "AdminInfo: $list download failed: $gripFile not readable!" );
16417    	    $$nextload = $longRetry;
16418                $time = $$nextload - $time;
16419                mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16420    	    return 0;
16421    	} elsif ( !-w $gripFile ) {
16422    	    mlog( 0, "AdminInfo: $list download failed: $gripFile not writable!" );
16423    	    $$nextload = $longRetry;
16424                $time = $$nextload - $time;
16425                mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16426    	    return 0;
16427    	}
16428    } else {
16429    	if (open(my $TEMPFILE, ">", $gripFile)) {
16430    	    #we can create the file, this is good, now close the file and keep going.
16431    	    close $TEMPFILE;
16432    	    unlink "$gripFile";
16433    	} else {
16434    	    mlog( 0, "AdminInfo: $list download failed: Cannot create $gripFile " );
16435    	    $$nextload = $longRetry;
16436                $time = $$nextload - $time;
16437                mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16438    	    return 0;
16439    	}
16440    }
16441
16442    # Create LWP object
16443    my $ua = LWP::UserAgent->new();
16444
16445    # Set useragent to ASSP version
16446    $ua->agent("ASSP/$version$modversion ($^O; Perl/$]; LWP::Simple/$LWP::VERSION)");
16447    $ua->timeout(20);
16448    if ($proxyserver) {
16449        my $user = $proxyuser ? "http://$proxyuser:$proxypass\@": "http://";
16450        $ua->proxy( 'http', $user . $proxyserver );
16451        mlog( 0, "downloading $list via HTTP proxy: $proxyserver" )
16452          if $MaintenanceLog;
16453    } else {
16454        mlog( 0, "downloading $list via direct HTTP connection" ) if $MaintenanceLog;
16455    }
16456
16457    # call LWP mirror command
16458    eval{$rc = $ua->mirror( $gripListUrl, $gripFile );};
16459    if ($@) {
16460        mlog( 0,"AdminInfo: $list download failed: error - " . $@ );
16461        $$nextload = $shortRetry;
16462        $time = $$nextload - $time;
16463        mlog(0,"AdminInfo: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16464        return 0;
16465    }
16466
16467    d("LWP-response: $rc->as_string");
16468
16469    if ( $rc == 304 || $rc->as_string =~ /304/o ) {
16470        # HTTP 304 not modified status returned
16471        mlog( 0, "$list already up to date" ) if $MaintenanceLog;
16472        $$nextload = $longRetry;
16473        $time = $$nextload - $time;
16474        mlog(0,"AdminInfo: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16475        return 0;
16476    } elsif ( ! $rc->is_success ) {
16477        #download failed-error code output to logfile
16478        my $code = $rc->as_string;
16479        ($code) = $code =~ /^(.+)?\r?\n.*/o;
16480        mlog( 0,"AdminInfo: $list download failed: " . $code );
16481        $$nextload = $shortRetry;
16482        $time = $$nextload - $time;
16483        mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16484        return 0;
16485    } elsif ( $rc->is_success ) {
16486        # download complete
16487        $$nextload = $longRetry;
16488        mlog( 0, "$list download completed" ) if $MaintenanceLog;
16489        $time = $$nextload - $time;
16490        mlog(0,"info: next $list download in ".&getTimeDiff($time)) if $MaintenanceLog && $showNext;
16491        return 1;
16492    }
16493}
16494
16495sub skipCheck {
16496    my ($t, @c) = @_;
16497    my ($f,$s) = ({qw(aa acceptall co contentonly ib isbounce rw
16498                      rwlok nd nodelay sb addressedToSpamBucket ro
16499                      relayok wl whitelisted np noprocessing nbw
16500                      nopbwhite nb nopb t),time});
16501    my $r = eval('$t&&!defined${chr(ord(",")<< 1)}&&($f->{t}%2)&&@c');
16502    $s->{ispcip} = $t->{ispip} && !$t->{cip};
16503    map{$r||=(ref($_)?eval{$_->();}:($t->{$f->{$_}}||$t->{$_}||$s->{$_}));}@c;
16504    return $r;
16505}
16506sub MailLoopOK {
16507    my $fh = shift;
16508    my $this = $Con{$fh};
16509    d("MailLoopOK");
16510    return 1 unless $detectMailLoop;
16511    my $count = () = $this->{header} =~
16512       /(Received:\s+from\s.*?\sby\s+$myName)/ig;
16513    return 0 if $count > $detectMailLoop;
16514    return 1;
16515}
16516
16517# do Message-ID checks
16518
16519
16520# do RWL checks
16521sub RWLok {
16522    my($fh,$ip)=@_;
16523    return 1 if ! $CanUseRWL;
16524    return 1 if ! $ValidateRWL;
16525    return 1 if ! @rwllist;
16526    return RWLok_Run($fh,$ip);
16527}
16528sub RWLok_Run {
16529    my($fh,$ip)=@_;
16530    my $this=$Con{$fh};
16531    $fh = 0 if $fh =~ /^\d+$/o;
16532    d('RWLok');
16533    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
16534    return 1 unless $ip;
16535    return 1 if $this->{RWLokDone};
16536    $this->{RWLokDone} = 1;
16537    skipCheck($this,'sb','ro','wl','np','ispcip') && return 1;
16538    return 1 if $ip=~/$IPprivate/o;
16539    return 1 if ! $this->{ispip} && matchIP($this->{ip},'noRWL',$fh,0);
16540    return 1 if $this->{ispip} && $this->{cip} && matchIP($ip,'noRWL',$fh,0);
16541    return 1 if ( $this->{rwlok} % 2);
16542    $this->{rwlok} = RWLCacheFind($ip);
16543    if ( $this->{rwlok} % 2) {    # 1 (trust) or 3 (trust and whitelisted)
16544        $this->{nodamping} = 1;
16545        $this->{whitelisted} = 1 if $this->{rwlok} == 3 && $RWLwhitelisting;
16546        return 1 ;
16547    } elsif ($this->{rwlok} == 2) {   # RWLminhits not reached
16548        $this->{nodamping} = 1;
16549        $this->{rwlok} = '';
16550        return 0;
16551    } elsif ($this->{rwlok} == 4) {   # RWL none
16552        $this->{rwlok} = '';
16553        return 0;
16554    }
16555    $this->{rwlok} = '';
16556    return 1 if pbWhiteFind($ip) && !$RWLwhitelisting;
16557    my $trust;
16558    my ($rwls_returned,@listed_by,$rwl,$received_rwl,$time,$err);
16559    if (matchIP($ip,'noRWL',$fh,0)) {
16560        $this->{myheader}.="X-Assp-Received-RWL: lookup skipped (noRWL sender)\r\n" if $AddRWLHeader;
16561        return 1;
16562    }
16563
16564    &sigoff(__LINE__);
16565    $rwl = eval{
16566        RBL->new(
16567            lists       => [@rwllist],
16568            server      => \@nameservers,
16569            max_hits    => $RWLminhits,
16570            max_replies => $RWLmaxreplies,
16571            query_txt   => 0,
16572            max_time    => $RWLmaxtime,
16573            timeout     => 2
16574        );
16575    };
16576    # add exception check
16577    if ($@ || ! ref($rwl)) {
16578        &sigon(__LINE__);
16579        mlog($fh,"RWLok: error - $@" . ref($rwl) ? '' : " - $rwl");
16580        return;
16581    }
16582    my $lookup_return = eval{$rwl->lookup($ip,"RWL");};
16583    mlog($fh,"error: RWL check failed : $lookup_return") if ($lookup_return && $lookup_return != 1);
16584    mlog($fh,"error: RWL lookup failed : $@") if ($@);
16585    my @listed=eval{$rwl->listed_by();};
16586    &sigon(__LINE__);
16587    return 0 if $lookup_return != 1;
16588    my $status;
16589    foreach (@listed) {
16590        if ($_ =~ /hostkarma\.junkemailfilter\.com/io && $rwl->{results}->{$_} !~ /127\.0\.\d+\.1/o) {
16591            next;
16592        } else {
16593            push @listed_by, $_;
16594        }
16595    }
16596    $rwls_returned=$#listed_by+1;
16597    if ($rwls_returned>=$RWLminhits) {
16598        $trust=2;
16599        my $ldo_trust;
16600
16601        foreach (@listed_by) {
16602            my %categories = (
16603                      2 => 'Financial services',
16604                      3 => 'Email Service Providers',
16605                      4 => 'Organisations',
16606                      5 => 'Service/network providers',
16607                      6 => 'Personal/private servers',
16608                      7 => 'Travel/leisure industry',
16609                      8 => 'Public sector/governments',
16610                      9 => 'Media and Tech companies',
16611                     10 => 'some special cases',
16612                     11 => 'Education, academic',
16613                     12 => 'Healthcare',
16614                     13 => 'Manufacturing/Industrial',
16615                     14 => 'Retail/Wholesale/Services',
16616                     15 => 'Email Marketing Providers'
16617            );
16618            $received_rwl.="$_->". $rwl->{results}->{$_};
16619            if ($_ =~ /list\.dnswl\.org/io && $rwl->{results}->{$_} =~ /127\.\d+\.(\d+)\.(\d+)/o) {
16620                $ldo_trust = $2;
16621                $received_rwl.=",trust=$ldo_trust (category=$categories{$1});";
16622            } else {
16623                $received_rwl.="; ";
16624            }
16625        }
16626        $trust = $ldo_trust if ($ldo_trust > $trust or ($ldo_trust =~ /\d+/o && $rwls_returned == 1));
16627        $received_rwl.=") - high trust is $trust - client-ip=$ip";
16628        $received_rwl = "Received-RWL: ".(($trust>0)?"whitelisted ":' ')."from (" . $received_rwl;
16629        mlog($fh,$received_rwl,1) if $RWLLog;
16630        $this->{rwlok}=$trust if $trust>0;
16631        $this->{nodamping} = 1;
16632        pbBlackDelete($fh,$ip) if $fh;
16633        RBLCacheDelete($ip) if $fh;
16634        $this->{myheader}.="X-Assp-$received_rwl\015\012" if $AddRWLHeader;
16635        $this->{whitelisted}=1 if $trust>2 && $RWLwhitelisting;
16636        RWLCacheAdd($ip,($trust > 2) ? 3 : ($trust == 0) ? 2 : 1 ) ;
16637        $status = ($trust > 2) ? 3 : ($trust == 0) ? 2 : 1 ;
16638        pbWhiteAdd($fh,$ip,"RWL") if $trust>1 && $fh;
16639        return ($trust == 0) ? 0 : 1;
16640    } elsif ($rwls_returned>0) {
16641        $received_rwl="Received-RWL: listed from @listed_by; client-ip=$ip";
16642        mlog($fh,$received_rwl,1) if $RWLLog;
16643        $this->{nodamping} = 1;
16644
16645        RWLCacheAdd($ip,2);
16646        $status = 2;
16647    } else {
16648        $received_rwl="Received-RWL: listed from none; client-ip=$ip";
16649        mlog($fh,$received_rwl,1) if $RWLLog>=2;
16650
16651        RWLCacheAdd($ip,4);
16652        $status = 4;
16653    }
16654    if (! $fh) {
16655        $this->{messagereason} = $received_rwl;
16656        $this->{rwlstatus} = $status;
16657    }
16658    return 0;
16659}
16660sub addtowhitelist {
16661    my ($fh, $adr) = @_;
16662    my $this = $Con{$fh};
16663	$adr = lc $this->{mailfrom} if !$adr;
16664  	$adr = batv_remove_tag($fh,$adr,'');
16665	if (length($adr) < 50  && $adr && $adr !~ /^SRS/i && !$this->{red} && 		!$Redlist{$adr}) {
16666		$Whitelist{$adr} = time;
16667
16668    }
16669}
16670
16671
16672sub weightRBL {
16673    my $v = shift;
16674    return $v if $v > 6;
16675    my $w = $v;
16676    $w = int( $rblValencePB / $v ) if $v;
16677    $w += 1 if $w * $v < $rblValencePB && $v;
16678    $w = $rblValencePB if !$v;
16679    return $w;
16680}
16681sub weightURI {
16682    my $v = shift;
16683    if ($v) {
16684        return $v if $v >= 6;
16685        $v = int ($URIBLmaxweight / $v + 0.5);
16686    } else {
16687        return 0;
16688    }
16689    return $v if $v;
16690    return int($URIBLmaxweight / $URIBLmaxhits + 0.5) if $URIBLmaxweight && $URIBLmaxhits;
16691    return $uriblValencePB;
16692}
16693
16694sub weightReSL {
16695    my ($valence,$name,$kk,$subre) = @_;
16696    my $key = ref $kk ? $$kk : $kk;
16697    my $cvalence;
16698    my $weight;
16699    my $found;
16700    my $count = 0;
16701    foreach my $k (@{$name.'WeightRE'}) {
16702        if ($subre eq $k) {
16703            $weight = ${$name.'Weight'}[$count];
16704            $found = 1;
16705
16706            $weightMatch .= ' , ' if $weightMatch;
16707            $weightMatch .= $k;
16708            last;
16709        }
16710        $count++;
16711    }
16712	$valence = ${$valence}[0] if $valence =~ /ValencePB$/o;
16713    return $valence unless $found;
16714    eval{$cvalence = int($valence * $weight);};
16715    return $valence if $@;
16716    return $cvalence if abs($weight) <= 6;
16717    return $weight;
16718}
16719
16720sub weightRe {
16721    my ($valence,$name,$kk,$fh) = @_;
16722    my $key = ref $kk ? $$kk : $kk;                                          # bombs, ptr, helo only
16723    my $this = ($fh && defined $Con{$fh} && $name =~ /bomb|script|black|Reversed|Helo/o) ? $Con{$fh} : undef;
16724    my $cvalence;
16725    my $weight;
16726    my $found;
16727    my $count = 0;
16728    foreach my $k (@{$name.'WeightRE'}) {
16729        $k =~ s/^\{([^\}]*)\}(.*)$/$2/o;
16730        my $how = $1 ? $1 : '';
16731        ++$count and next unless $k;
16732
16733        if ($how && $this) {
16734            ++$count and next if ($this->{noprocessing}  && $how =~ /[nN]\-/o);
16735            ++$count and next if ($this->{whitelisted}   && $how =~ /[wW]\-/o);   #never
16736            ++$count and next if ($this->{relayok}       && $how =~ /[lL]\-/o);
16737            ++$count and next if ($this->{ispip}         && $how =~ /[iI]\-/o);
16738
16739            ++$count and next if (!$this->{noprocessing} && $how =~ /[nN]\+/o);
16740            ++$count and next if (!$this->{whitelisted}  && $how =~ /[wW]\+/o);   #only
16741            ++$count and next if (!$this->{relayok}      && $how =~ /[lL]\+/o);
16742            ++$count and next if (!$this->{ispip}        && $how =~ /[iI]\+/o);
16743        }
16744
16745
16746
16747        if ($this && $name =~ /Reversed/o) {         # ptr
16748            ++$count and next if (!$DoReversedNP    && $this->{noprocessing}  && $how !~ /[nN]\+?/o);
16749            ++$count and next if (!$DoReversedWL    && $this->{whitelisted}   && $how !~ /[wW]\+?/o);   #config
16750        }
16751
16752
16753
16754        if ($key =~ /$k/i) {
16755            $weight = ${$name.'Weight'}[$count];
16756            $found = 1;
16757
16758            $weightMatch .= ' , ' if $weightMatch;
16759            $weightMatch .= $k;
16760            last;
16761        }
16762        $count++;
16763    }
16764
16765    $valence = ${$valence}[0] if $valence =~ /ValencePB$/o;
16766    return $valence unless $found;
16767    eval{$cvalence = int($valence * $weight);};
16768    return $valence if $@;
16769    return $cvalence if abs($weight) <= 6;
16770    return $weight;
16771}
16772
16773sub HighWeightSL {
16774    my ($t,$re) = @_;
16775
16776    my $text = ref $t ? $$t : $t;
16777    my %weight = ();
16778    my %found = ();
16779    my $weightsum = 0;
16780    my $weightcount = 0;
16781    my $regex = ${ $MakeSLRE{$re} };
16782    my $itime = time;
16783	my $count = 0;
16784
16785
16786	eval {
16787      local $SIG{ALRM} = sub { die "__alarm__\n" };
16788      alarm($maxBombSearchTime + 5);
16789      foreach my $regex ( @{$re.'WeightRE'}) {
16790
16791      	  next if  $text !~ /($regex)/s;
16792          my $subre = $1;
16793
16794          last if time - $itime >= $maxBombSearchTime;
16795          my $valence = ${$WeightedRe{$re}};
16796
16797          my $w = &weightReSL($valence,$re,$subre,$regex);
16798
16799          mlog(0," weighted regex for '$re' is '$subre=>$w' ") if $regexLogging >= 2;
16800
16801          next unless $w;
16802          $subre =~ s/\s+/ /g;
16803          next if ($found{lc($subre)} > 0 && $found{lc($subre)} >= $w);
16804          next if ($found{lc($subre)} < 0 && $found{lc($subre)} <= $w);
16805          $found{lc($subre)} = $w;
16806          $subre = substr($subre,0,$RegExLength < 5 ? 5 : $RegExLength) if $subre;
16807          $weightsum += $w;
16808          $weightcount++;
16809          if (abs($w) >= abs($weight{highval})) {
16810              $weight{highval} = $w;
16811              $subre =~ s{([\x00-\x1F])}{sprintf("'hex %02X'", ord($1))}eog;
16812              $subre = '[empty]' unless $subre;
16813              $weight{highnam} = $subre;
16814          }
16815
16816#          last if abs($w) >= abs($valence);
16817
16818
16819      }
16820      alarm(0);
16821    };
16822    $itime = time - $itime;
16823    if ($@) {
16824        alarm(0);
16825        return 0;
16826
16827    }
16828    return ($weight{highnam},$weight{highval});
16829}
16830
16831sub RBLok {
16832    my ($fh,$ip,$skipcip) = @_;
16833    return 1 if ! $ValidateRBL;
16834    return 1 if ! $CanUseRBL;
16835    return 1 if ! @rbllist;
16836    return RBLok_Run($fh,$ip,$skipcip);
16837}
16838sub RBLok_Run {
16839
16840    my ($fh,$ip,$done) = @_;
16841    my $this = $Con{$fh};
16842    my $reason;
16843    my $rblweighttotal;
16844	my $rblweight;
16845	my $rblweightn;
16846	$rblweight  = $rblValencePB;
16847    $rblweightn = $rblnValencePB;
16848	return 1 if $this->{notspamtag};
16849    return 1 if $this->{addressedToSpamBucket};
16850	$ip = $this->{ip};
16851    $ip = $this->{cip} if$this->{cip};
16852    return 1 if $this->{rbldone};
16853    $this->{rbldone} = 1;
16854
16855	return 1 if $ip =~ /$IPprivate/;
16856
16857    d('RBLOK');
16858    return 1 if ! $ValidateRBL;
16859    return 1 if ! $CanUseRBL;
16860    return 1 if ! @rbllist;
16861	return 1 if $this->{notspamtag};
16862    return 1 if $this->{rwlok};
16863    return 1 if $this->{relayok};
16864	return 1 if $this->{whitelisted} && !$RBLWL;
16865	return 1 if $this->{noprocessing} && !$RBLNP;
16866    return 1 if $this->{ispip} && !$this->{cip};
16867
16868	return 1 if $this->{contentonly} && !$this->{cip};
16869	my $w;
16870	my $rbls_returned;
16871	my @listed_by;
16872    return 1 if $this->{whitelisted} && !$RBLWL;
16873    return 1 if $noRBL && matchIP( $ip, 'noRBL', 0, 1 );
16874	my ( $ct, $mm, $status, @rbl ) = split( ' ', $RBLCache{$ip} );
16875    return 1 if $status==2;
16876
16877    my $slok         = $this->{allLoveRBLSpam} == 1;
16878    my $mValidateRBL = $ValidateRBL;
16879	$this->{testmode} = 0;
16880	$this->{testmode} = 1	if $ValidateRBL == 4 or $allTestMode;
16881	$mValidateRBL = 1 		if $ValidateRBL == 4;
16882
16883
16884
16885    my $tlit = &tlit($mValidateRBL);
16886    $this->{prepend} = "[DNSBL]";
16887
16888    &sigoff(__LINE__);
16889    my $rbl = eval {
16890        RBL->new(
16891            lists       => [@rbllist],
16892            server      => \@nameservers,
16893            max_hits    => $RBLmaxhits,
16894            max_replies => $RBLmaxreplies,
16895            query_txt   => 1,
16896            max_time    => $RBLmaxtime,
16897            timeout     => $RBLsocktime
16898        );
16899    };
16900
16901# add exception check
16902    if ($@) {&sigon(__LINE__);return 1; }
16903
16904    my ( $received_rbl, $rbl_result, $lookup_return );
16905    $lookup_return = eval{$rbl->lookup( $ip, "RBL" );};
16906    &sigon(__LINE__);
16907    mlog($fh,"error: RBL check failed : $lookup_return") if ($lookup_return != 1);
16908    return 1 if ($lookup_return != 1);
16909
16910    @listed_by     = $rbl->listed_by();
16911    $rbls_returned = $#listed_by + 1;
16912
16913    if ( $rbls_returned > 0 ) {
16914
16915        foreach (@listed_by) {
16916
16917            $rblweighttotal += weightRBL( $rblweight{$_} ) if $rblweight{$_};
16918
16919        }
16920
16921        $rblweight = $rblweightn = $rblweighttotal if $rblweighttotal;
16922
16923        $reason = $this->{messagereason} = '';
16924		my $maxrblValencePB = $rblValencePB;
16925		$maxrblValencePB = $RBLmaxweight if $RBLmaxweight > $rblValencePB;;
16926        if ( $rbls_returned >= $RBLmaxhits && !$rblweighttotal || $rblweighttotal > $maxrblValencePB) {
16927            delayWhiteExpire($fh);
16928            pbWhiteDelete( $fh, $ip );
16929
16930            $this->{messagereason} = "DNSBL: failed, $ip listed in @listed_by";
16931
16932            pbAdd( $fh, $ip, $rblValencePB, "DNSBLfailed($rbls_returned)" )
16933              if $mValidateRBL != 2;
16934
16935            $tlit = "[scoring:$rblValencePB]" if $mValidateRBL == 3;
16936            $received_rbl = "DNSBL: failed, $ip listed in (";
16937        } elsif ($rbls_returned > 0) {
16938			pbWhiteDelete( $fh, $ip );
16939            delayWhiteExpire($fh);
16940            $this->{messagereason} = "DNSBL: neutral, $ip listed in @listed_by";
16941            $this->{prepend}       = "[DNSBL]";
16942            $this->{newsletterre} = '';
16943            mlog( $fh, "[scoring:$rblnValencePB] DNSBL: neutral, $ip listed in @listed_by" )
16944              if ( $RBLLog && $mValidateRBL == 1 );
16945            pbAdd( $fh, $ip, $rblnValencePB, "DNSBLneutral($rbls_returned)")
16946              if $mValidateRBL != 2;
16947            $this->{rblneutral} = 1;
16948            $this->{newsletterre} = '';
16949            $received_rbl = "DNSBL: neutral, $ip listed in (";
16950        } else {
16951            RBLCacheAdd( $ip,  "2") if $RBLCacheInterval > 0;
16952            return 1;
16953        }
16954        my @temp = @listed_by;
16955        foreach (@temp) {
16956            $received_rbl .= "$_<-" . $rbl->{results}->{$_} . "; ";
16957            $_ .= '{' . $rbl->{results}->{$_} . '}';
16958
16959        }
16960        $received_rbl .= ")";
16961        RBLCacheAdd( $ip,  "1", "@temp" ) if $RBLCacheInterval > 0;
16962    } else {
16963        RBLCacheAdd( $ip,  "2") if $RBLCacheInterval > 0;
16964        return 1;
16965    }
16966    mlog( $fh, "$tlit ($received_rbl)" ) if $received_rbl ne "DNSBL: pass" && ($RBLLog >= 2 || $RBLLog && $mValidateRBL >= 2 );
16967
16968    return 1 if $mValidateRBL == 2;
16969
16970    # add to our header; merge later, when client sent own headers
16971    $this->{myheader} .= "X-Assp-$received_rbl\r\n"
16972      if $AddRBLHeader && $received_rbl ne "DNSBL: pass" && $this->{myheader} !~ /DNSBL/;
16973
16974	return 1 if $this->{messagescore} <= $this->{spamMaxScore};
16975    if ( $rbls_returned >= $RBLmaxhits && !$rblweighttotal || $rblweighttotal >= $rblValencePB) {
16976
16977        return 1 if $mValidateRBL == 3;
16978        my $slok = $this->{allLoveSpam} == 1;
16979        $Stats{rblfails}++;
16980		my $reply = $SpamError;
16981		$reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
16982		$reply =~ s/REASON/DNSBL Listed in @listed_by/go;
16983        $reply = replaceerror ($fh, $reply);
16984        $this->{prepend} = "[DNSBL]";
16985        $this->{newsletterre}		= '';
16986
16987        thisIsSpam( $fh, "DNSBL, $ip listed in @listed_by",
16988            $RBLFailLog, "$reply", $this->{testmode}, $slok, $done );
16989        return 0;
16990    }
16991    return 1;
16992}
16993
16994sub RBLCacheOK {
16995    my ($fh,$ip,$done) = @_;
16996    my $this = $Con{$fh};
16997    return 1 if $this->{notspamtag};
16998    return 1 if $this->{addressedToSpamBucket};
16999	$ip = $this->{ip};
17000    $ip = $this->{cip} if $this->{cip};
17001    return 1 if $this->{rblcachedone};
17002    $this->{rblcachedone} = 1;
17003
17004    d('RBLCacheOK');
17005
17006    return 1 if $ip =~ /$IPprivate/;
17007
17008	return 1 if $this->{ispip} && !$this->{cip};
17009
17010	return 1 if $this->{notspamtag};
17011	return 1 if $this->{contentonly} && !$this->{cip};
17012    return 1 if !$ValidateRBL;
17013	return 1 if $this->{whitelisted} && !$RBLWL;
17014	return 1 if $this->{noprocessing} && !$RBLNP;
17015    return 1 if $noRBL && matchIP( $ip, 'noRBL', 0, 1 );
17016
17017
17018    return 1 if !( exists $RBLCache{$ip} );
17019    return 1 if !$RBLCacheInterval;
17020    return 1 if exists $PBWhite{$ip};
17021
17022    return 1 if $this->{acceptall};
17023    return 1 if $this->{relayok};
17024    return 1 if $this->{rwlok};
17025
17026    my $slok         = $this->{allLoveRBLSpam} == 1;
17027    my $mValidateRBL = $ValidateRBL;
17028	$this->{testmode} = 0;
17029	$this->{testmode} = 1	if $ValidateRBL == 4 or $allTestMode;
17030	$mValidateRBL = 1 		if $ValidateRBL == 4;
17031
17032    my $tlit = &tlit($mValidateRBL);
17033    $this->{rbldone} = 1;
17034
17035    my $tlit = &tlit($mValidateRBL);
17036
17037    my ( $ct, $mm, $status, @rbl ) = split( ' ', $RBLCache{$ip} );
17038
17039    return 1 if $status==2;
17040
17041    $this->{prepend} = "[DNSBL]";
17042
17043    my $rbls_returned = $#rbl + 1;
17044    my ($rbllists,$rblweight, $rblweightn, $rblweighttotal);
17045
17046    foreach (@rbl) {
17047		if ($rblweight{$_} && s/(.+?)\{(.+?)\}/$1/io) {
17048
17049            my $w; $w = matchHashKey($rblweight{$_},$2) if $rblweight{$_};
17050
17051            $rblweighttotal += weightRBL($w) if $w;
17052
17053        } else {
17054            $rblweighttotal += weightRBL($rblweight{$_}{'*'}) if $rblweight{$_}{'*'};
17055        }
17056        $rbllists .= "$_, ";
17057    }
17058    $rbllists =~ s/, $//o;
17059
17060    $rblweight = $rblValencePB;
17061    $rblweightn = $rblnValencePB;
17062    $rblweight = $rblweightn = $rblweighttotal if $rblweighttotal;
17063
17064    $this->{messagereason} = $rbllists;
17065
17066    $this->{messagereason} = "$ip listed in DNSBLcache by $rbllists";
17067    $tlit = "[scoring:$rblweight]" if $mValidateRBL == 3;
17068    mlog( $fh, "$tlit ($this->{messagereason} at $mm)" )
17069    					if $RBLLog >= 2 or $RBLLog && $mValidateRBL >= 2;
17070
17071    return 1 if $mValidateRBL == 2;
17072
17073        # add to our header; merge later, when client sent own headers
17074
17075		my $maxrblValencePB = $rblValencePB;
17076		$maxrblValencePB = $RBLmaxweight if $RBLmaxweight > $rblValencePB;
17077        if ( $rbls_returned >= $RBLmaxhits && !$rblweighttotal || $rblweighttotal > $maxrblValencePB) {
17078            pbWhiteDelete( $fh, $ip );
17079            delayWhiteExpire($fh);
17080			$this->{newsletterre} = '';
17081            $this->{messagereason} = "DNSBLcache: failed, $ip listed in $rbllists";
17082            pbAdd( $fh, $ip, $rblValencePB, "DNSBLfailed($rbls_returned)" )
17083              if $mValidateRBL != 2;
17084
17085     } else {
17086            pbWhiteDelete( $fh, $ip );
17087            $this->{messagereason} = "DNSBLcache: neutral, $ip listed in $rbllists";
17088            $this->{prepend}       = "[DNSBL]";
17089            $this->{newsletterre} = '';
17090            mlog( $fh, "[scoring:$rblweightn] $this->{messagereason}" )
17091              if ( $RBLLog && $mValidateRBL == 1 );
17092            pbAdd( $fh, $ip, $rblnValencePB, "DNSBLneutral($rbls_returned)" )
17093            	if $mValidateRBL != 2;
17094            $this->{newsletterre} = '';
17095
17096            $this->{rblneutral} = 1;
17097
17098     }
17099
17100
17101    return 1 if $mValidateRBL == 2;
17102
17103    # add to our header; merge later, when client sent own headers
17104    $this->{myheader} .= "X-Assp-$this->{messagereason}\r\n" if $AddRBLHeader && $this->{myheader} !~ /DNSBL/;
17105	return 1 if  $this->{messagescore} <= $this->{spamMaxScore};
17106    return 1 if $mValidateRBL == 3 or $this->{rblneutral} ;
17107    $Stats{rblfails}++ unless $slok;
17108    my $reply = $SpamError;
17109    $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
17110	$reply =~ s/REASON/DNSBL listed in $rbllists/go;
17111    $reply = replaceerror ($fh, $reply);
17112    $this->{newsletterre}		= '';
17113
17114    thisIsSpam( $fh, "$this->{messagereason}", $RBLFailLog, "$reply", $this->{testmode}, $slok, $done );
17115
17116    return 0;
17117}
17118
17119sub RBLCacheAdd {
17120    my ( $ip, $status, $rbllists) = @_;
17121    my $t = time;
17122    my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
17123    $mon++;
17124    $year += 1900;
17125    my $mm = sprintf( "%04d-%02d-%02d/%02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec );
17126    my $data = "$t $mm $status $rbllists";
17127
17128    $RBLCache{$ip} = $data;
17129}
17130
17131#
17132sub RBLCacheDelete {
17133    return if !$RBLCacheInterval;
17134    my $ip = shift;
17135    return unless ($RBLCacheObject);
17136
17137    delete $RBLCache{$ip};
17138  }
17139
17140#
17141sub RBLCacheFind {
17142    my $ip = shift;
17143    return if !$RBLCacheInterval;
17144    return unless ($RBLCacheObject);
17145
17146	my $t = time;
17147	my $ct;
17148    my $datetime;
17149    my $status;
17150    my @sp;
17151    if ( ( $ct, $datetime, $status, @sp ) = split( / /o, $RBLCache{$ip} ) ) {
17152 		$RBLCache{$ip} = "$t $datetime $status @sp";
17153        return $status;
17154    }
17155    return 0;
17156}
17157
17158sub expandRegChar {
17159    my $char = shift;
17160    my $ucd = ord(uc($char));
17161    my $lcd = ord(lc($char));
17162    my $uch = sprintf "%x", $ucd;
17163    my $lch = sprintf "%x", $lcd;
17164       $ucd < 99 and $ucd = '0?' . $ucd;
17165       $lcd < 99 and $lcd = '0?' . $lcd;
17166    my $esc = ($char =~ /[a-zA-Z0-9]/) ? '' : '\\';
17167    my $hex = ($uch eq $lch) ? $uch : "$uch|$lch";
17168    my $dec = ($ucd eq $lcd) ? $ucd : "$ucd|$lcd" ;
17169    return '(?i:[\=\%](?i:' . $hex . ')|\&\#(?:' . $dec . ')\;?|' . "$esc$char)(?:\\=(?:\\015?\\012|\\015))?";
17170}
17171
17172sub erw {
17173    my ($word,$quant) = @_;
17174    my $ret;
17175    $ret = '(?:' if $quant;
17176    $ret .= join('', map {&expandRegChar($_)} split('',$word));
17177    $ret .= ")$quant" if $quant;
17178    return $ret;
17179}
17180
17181# do URIBL checks
17182sub URIBLok {
17183    my ( $fh, $bd, $thisip,$done ) = @_;
17184    my $this = $Con{$fh};
17185	return 1 if $this->{notspamtag};
17186#    $TLDSRE = $URIBLTLDSRE if ".com" =~ /\.($URIBLTLDSRE )/i;
17187    return 1 if !$TLDSRE;
17188
17189    return 1 if !$CanUseURIBL;
17190	return 1 if $this->{addressedToSpamBucket};
17191
17192
17193    $this->{uribldone} = 1;
17194    return 1 if !$ValidateURIBL;
17195
17196    return URIBLok_Run($fh, $bd, $thisip, $done);
17197}
17198sub URIBLok_Run {
17199    my ( $fh, $bd, $thisip, $done ) = @_;
17200    my $this = $Con{$fh};
17201    my $fhh = $fh;
17202    $fh = 0 if "$fh" =~ /^\d+$/o;
17203    d('URIBLok');
17204
17205
17206    return 1 if $this->{whitelisted} && !$URIBLWL;
17207    return 1 if $this->{noprocessing} && !$URIBLNP;
17208    return 1 if $this->{relayok} && !$URIBLLocal;
17209    return 1 if $this->{ispip} && !$URIBLISP && !$this->{cip};
17210
17211    $thisip = $this->{cip} if $this->{ispip} && $this->{cip};
17212	my $URIDomainRe;
17213	my @URIIPs;
17214    my $ProtPrefix = <<'EOT';
17215(?:(?i:[\=\%][46]8|\&\#(?:0?72|104)\;?|h)
17216(?i:[\=\%][57]4|\&\#(?:0?84|116)\;?|t)
17217|(?i:[\=\%][46]6|\&\#(?:0?70|102)\;?|f))
17218(?i:[\=\%][57]4|\&\#(?:0?84|116)\;?|t)
17219(?i:[\=\%][57]0|\&\#(?:0?80|112)\;?|p)
17220(?i:[\=\%][57]3|\&\#(?:0?83|115)\;?|s)?
17221(?:[\=\%]3[aA]|\&\#0?58\;?|\:)
17222(?:[\=\%]2[fF]|\&\#0?47\;?|\/){2}
17223EOT
17224    $ProtPrefix =~ s/\r|\n|\s//g;
17225
17226    my $UriAt = '(?:\@|[=%]40|\&\#0?64\;?)';
17227    my $UriIPSectDotRe = '(?:'.$IPSectRe.$UriDot.')';
17228    my $UriIPRe = $ProtPrefix.'(?:[^\@]*?'.$UriAt.')?'.$UriIPSectDotRe.$UriIPSectDotRe.$UriIPSectDotRe.$IPSectRe;
17229
17230    my $URISubDelimsCharRe = quotemeta('[!$&\'()*+,;=%^`{}|]'); # relaxed to a few other characters
17231    if ($URIBLcheckDOTinURI) {
17232        $URIDomainRe = $UriAt.'?(?:\w(?:[\w\-]|'.$UriDot.'|'.$dot.')*(?:'.$UriDot.'|' . $dot . ')('. $TLDSRE .'))[^\.\w]';
17233    } else {
17234        $URIDomainRe = $UriAt.'?(?:\w(?:\w|'.$UriDot.'|\-)*'.$UriDot.'('. $TLDSRE .'))[^\.\w]';
17235    }
17236
17237    my $slok = $this->{allLoveURIBLSpam} == 1;
17238	my $listed_domain;
17239    my ( %domains, $ucnt, $uri, $mycache, $orig_uri, $i, $ip, $tlit, $uribl, $received_uribl, $uribl_result , $last_mycache, $results_uribl);
17240    my ( $lookup_return, @listed_by, @last_listed_by, $last_listed_domain, $uribls_returned, $lcnt, $err , $weightsum, %last_results, %results);
17241
17242
17243    my $mValidateURIBL = $ValidateURIBL;
17244
17245	$this->{testmode} = 0;
17246	$this->{testmode} = 1	if $ValidateURIBL == 4 or $allTestMode;
17247	$mValidateURIBL = 1 	if $ValidateURIBL == 4;
17248
17249    $tlit = &tlit($mValidateURIBL);
17250    $this->{prepend} = "[URIBL]";
17251
17252    if ($noURIBL
17253        && $this->{mailfrom}
17254        && matchSL( $this->{mailfrom}, 'noURIBL' ) ) {
17255        mlog( $fh, "URIBL lookup skipped (noURIBL sender)", 1 )
17256          if $URIBLLog >= 2;
17257        return 1;
17258    }
17259
17260    my $data = &cleanMIMEBody2UTF8($bd);
17261    $data =~ s/\=(?:\015?\012|\015)//go;
17262    $data =~ s/href\=3[dD]/href\=/go;
17263    $data =~ s/\&\#12290\;/./go;
17264    $data = decHTMLent($data);
17265    if ($data) {
17266        my $head = &cleanMIMEHeader2UTF8($bd,1);
17267        $head =~ s/\nto:$HeaderValueRe/\n/gios;
17268        $head =~ s/received:$HeaderValueRe//gios;
17269        $head =~ s/Message-ID:$HeaderValueRe//gios;
17270        $head =~ s/References:$HeaderValueRe//gios;
17271        $head =~ s/In-Reply-To:$HeaderValueRe//gios;
17272        $head =~ s/X-Assp-[^:]+?:$HeaderValueRe//gios;
17273        $head =~ s/bcc:$HeaderValueRe//gios;
17274        $head =~ s/cc:$HeaderValueRe//gios;
17275        $head =~ s/[\x0D\x0A]*$/\x0D\x0A\x0D\x0A/o;
17276        $head = &cleanMIMEHeader2UTF8($head,0);
17277        headerUnwrap($head);
17278        $data = $head . $data;
17279    }
17280 	my ($fdom,$dom);
17281 	my $SKIPURIRE = qr/$URIBLWLDRE|$NPDRE|$WLDRE/;
17282
17283
17284
17285    while ( $data =~ /($URIDomainRe|$UriIPRe)/gi ) {
17286            $uri = $1;
17287            d("found raw URI: $uri");
17288            mlog($fh,"info: found raw URI/URL $uri") if ($URIBLLog == 3);
17289            $uri =~ s/[^\.\w]$//o if $uri !~ /$UriIPRe/o;
17290            $uri =~ s/^$ProtPrefix//o;
17291            $uri =~ s/$UriAt/@/go;
17292            $uri =~ s/^\@//o;
17293#            $uri =~ s/\=(?:\015?\012|\015)\.?//go;
17294            $uri =~ s/(?:$URISubDelimsCharRe|\.)+$//o;
17295            $uri =~ s/\&(?:nbsp|amp|quot|gt|lt|\#0?1[03]|\#x0[da])\;?.*$//io;
17296            $uri =~ s/[\=\%]2[ef]|\&\#0?4[67]\;?/./gio;
17297            $uri =~ s/\.{2,}/\./go;
17298            $uri =~ s/^\.//o;
17299            $orig_uri = $uri;
17300
17301            if ($URIBLcheckDOTinURI) {
17302                my $ouri = $uri;
17303                mlog($fh,"replaced URI '$ouri' with '$uri'")
17304                  if ($uri =~ s/$dot/\./igo && $URIBLLog >= 2);
17305            }
17306            $uri =~ s/[%=]([a-f0-9]{2})/chr(hex($1))/gieo;                          # decode percents
17307            $uri =~ s/\&\#(\d+)\;?/decHTMLentHD($1)/geo;                            # decode &#ddd's
17308            $uri =~ s/([^\\])?\\(\d{1,3});?/$1.decHTMLentHD($2,'o')/geio;           # decode octals
17309            $uri =~ s/\&\#x([a-f0-9]+)\;?/decHTMLentHD($1,'h')/geio;                # decode &#xHHHH's
17310            # strip redundant dots
17311            $uri =~ s/\.{2,}/\./go;
17312            $uri =~ s/^\.//o;
17313            $uri =~ s/$URISubDelimsCharRe//go;
17314            $dom = '';
17315            if ($uri !~ /$IPRe/o) {
17316                $dom  = $1 if $uri =~ /(?:[^\.]+?\.)?([^\.]+\.[^\.]+)$/o;
17317                next if $dom && localdomains($dom);
17318                next if localdomains($uri);
17319            }
17320            mlog($fh,"info: found URI $uri")
17321                if (($URIBLLog == 2 && ! exists $domains{ lc $uri }) or $URIBLLog == 3);
17322
17323            next if $uri =~ /$SKIPURIRE/;
17324            next if "\@$uri" =~ /$SKIPURIRE/;
17325
17326            my $obfuscated = 0;
17327            if ( $uri =~ /$IPv4Re/o && $uri =~ /^$IPQuadRE$/io ) {
17328                $i = $ip = undef;
17329                while ( $i < 10 ) {
17330                    $ip = ( $ip << 8 ) + oct( ${ ++$i } ) + hex( ${ ++$i } ) + ${ ++$i };
17331                }
17332                $uri = inet_ntoa( pack( 'N', $ip ) );
17333                if ( $URIBLNoObfuscated && $orig_uri !~ /^\Q$uri\E/i ) {
17334                    $this->{obfuscatedip} = $obfuscated = 1;
17335                    mlog($fh,"info: URIBL - obfuscated IP found $uri - org IP: $orig_uri") if ($URIBLLog >=2);
17336                }
17337                mlog($fh,"info: registered IP-URI $uri for check")
17338                    if (($URIBLLog == 2 && ! exists $domains{ lc $uri }) or $URIBLLog == 3);
17339                push @URIIPs , $uri if $URIBLIPRe;
17340            } else {
17341                if ( $URIBLNoObfuscated && $orig_uri !~ /^\Q$uri\E/i ) {
17342
17343                    $this->{obfuscateduri} = $obfuscated = 1;
17344                    mlog($fh,"info: URIBL - obfuscated URI found $uri - org URI: $orig_uri") if ($URIBLLog >=2);
17345                }
17346                push @URIIPs , getRRA($uri) if $URIBLIPRe;
17347                if ( $uri =~ /([^\.]+$URIBLCCTLDSRE)$/ ) {
17348                    $uri = $1;
17349                    next if $uri =~ /$SKIPURIRE/;
17350                    next if "\@$uri" =~ /$SKIPURIRE/;
17351                    push @URIIPs , getRRA($uri) if $URIBLIPRe;
17352                    mlog($fh,"info: registered TLD(2/3) URI $uri for check")
17353                        if (($URIBLLog == 2 && ! exists $domains{ lc $uri }) or $URIBLLog == 3);
17354                } elsif ($uri =~ /([^\.]+\.($TLDSRE))$/oi ) {
17355                    $uri = $1;
17356                    next if $uri =~ /$SKIPURIRE/;
17357                    next if "\@$uri" =~ /$SKIPURIRE/;
17358                    push @URIIPs , getRRA($uri) if $URIBLIPRe;
17359                    mlog($fh,"info: registered TLD URI $uri for check")
17360                        if (($URIBLLog == 2 && ! exists $domains{ lc $uri }) or $URIBLLog == 3);
17361                } else {
17362                    next;
17363                }
17364            }
17365
17366
17367            if ( $URIBLmaxuris && ++$ucnt > $URIBLmaxuris ) {
17368                $this->{maximumuri} = 1;
17369                last;
17370            }
17371
17372            if ( ! $domains{ lc $uri }++ ) {
17373                $domains{ lc $uri } += $obfuscated * 1000000;
17374                if ( $URIBLmaxdomains && scalar keys(%domains) > $URIBLmaxdomains ) {
17375                    $this->{maximumuniqueuri} = 1;
17376                    last;
17377                }
17378            }
17379    }
17380    if (! scalar keys(%domains)) {
17381        mlog($fh,"no URI's to check found in mail") if ($URIBLLog>=2);
17382		return 1;
17383    }
17384
17385    my $urinew = eval {
17386        RBL->new(
17387            lists       => [@uribllist],
17388            server      => \@nameservers,
17389            max_hits    => $URIBLmaxhits,
17390            max_replies => $URIBLmaxreplies,
17391            query_txt   => 1,
17392            max_time    => $URIBLmaxtime,
17393            timeout     => $URIBLsocktime
17394          );
17395      };
17396
17397
17398        # add exception check
17399    if ($@ or ! ref($urinew)) {
17400        &sigon(__LINE__);
17401        mlog($fh,"URIBL: error - $@" . ref($urinew) ? '' : " - $urinew");
17402        return 1;
17403    };
17404    &sigon(__LINE__);
17405
17406    $received_uribl = $uribl_result = $lookup_return = @listed_by = $listed_domain = $uribls_returned = undef;
17407
17408	my $listed;
17409    for my $domain (sort keys %domains ) {
17410        next if !$domain;
17411
17412		my $isobfuscated = ($domains{ $domain } > 1000000) ? 2 : 1;
17413        $mycache = 0;
17414        my %cachedRes = ();
17415        my $uriweight = 0;
17416
17417
17418
17419		$listed_domain   = $domain;
17420
17421		if ( URIBLCacheFind($domain) == 2  ) {
17422			mlog($fh,"URIBLCache: $domain OK") if $URIBLLog > 2;
17423			next;
17424		}
17425        if ( URIBLCacheFind($domain) == 1 ) {
17426
17427
17428            my ( $ct, $status, @listed_by ) = split(/\s+/o, $URIBLCache{$domain} );
17429
17430            $mycache   = 1;
17431            $results_uribl .= ";" if $results_uribl;
17432
17433            $results_uribl .= "'$domain'(@listed_by)";
17434#        	$results_uribl .= "@listed_by";
17435
17436            $lcnt = 0;
17437
17438         	$uriweight = 0;
17439
17440         	foreach my $en (@listed_by) {
17441         		my ($dom,$res) = split(/\<\-/o,$en);
17442         		$dom =~ s/$domain\.(.*)/$1/g;
17443         		my $w;
17444
17445
17446 				$lcnt++;
17447 				if ($res =~ /(127\.\d+\.\d+\.\d+)/o) {
17448 					$w = matchHashKey($URIBLweight{$dom},$res) if 		$URIBLweight{$dom};
17449 					$uriweight += weightURI($w);
17450 			 	} else {
17451                	$uriweight += weightURI($URIBLweight{$dom}{'*'}) if $URIBLweight{$dom}{'*'};
17452            	}
17453
17454
17455
17456         	}
17457
17458#			last
17459        } else {
17460			$received_uribl="";
17461            $lookup_return   = eval{$urinew->lookup( $domain, "URIBL" );};
17462            @listed_by       = eval{$urinew->listed_by();};
17463
17464            foreach (@listed_by) {
17465                    	$received_uribl .= "$_" .'<-'  . $urinew->{results}->{$_} . ' ';
17466                		}
17467
17468            mlog($fh,"URIBL: lookup returned <$lookup_return> for $domain - res: @listed_by") if ($URIBLLog == 3 or ($URIBLLog >= 2 && $lookup_return !=1));
17469
17470			$results_uribl .= ";" if $results_uribl && $received_uribl;
17471			$received_uribl =~ s/\Q$domain\E\.//g;
17472			$results_uribl .= "'$domain'($received_uribl)" if $received_uribl;
17473			$this->{uri_listed_by} = $received_uribl if  ! $fh;
17474
17475
17476         	$lcnt = 0;
17477
17478         	$uriweight = 0;
17479
17480			foreach my $en (split(/\s+/o,$received_uribl)) {
17481         		my ($dom,$res) = split(/\<\-/o,$en);
17482
17483         		my $w;
17484         		$dom =~ s/$domain\.(.*)/$1/g;
17485
17486 				$lcnt++;
17487 				if ($res =~ /(127\.\d+\.\d+\.\d+)/o) {
17488 					$w = matchHashKey($URIBLweight{$dom},$res) if 		$URIBLweight{$dom};
17489 					$uriweight += weightURI($w);
17490 			 	} else {
17491                	$uriweight += weightURI($URIBLweight{$dom}{'*'}) if $URIBLweight{$dom}{'*'};
17492            	}
17493
17494
17495         	}
17496
17497
17498        	URIBLCacheAdd( $domain, "2" ) if $lcnt == 0 ;
17499        	next if $lcnt == 0;
17500        	URIBLCacheAdd( $domain, "1", $received_uribl ) ;
17501        }
17502
17503        $uribls_returned += $lcnt;
17504        $weightsum += $uriweight;
17505
17506    last if $fh && ( (!$URIBLmaxweight && $uribls_returned >= $URIBLmaxhits)
17507               or ($URIBLmaxweight && $weightsum >= $URIBLmaxweight));
17508
17509    }
17510
17511	$weightsum = ${'uriblnValencePB'}[0] if !$weightsum;
17512	$weightsum = ${'uriblnValencePB'}[0] if $uribls_returned && !$URIBLhasweights;
17513	$weightsum = ${'uriblValencePB'}[0] if $uribls_returned >= $URIBLmaxhits && !$URIBLhasweights;
17514    $weightsum = $URIBLmaxweight if $URIBLmaxweight && $weightsum > $URIBLmaxweight;
17515    my $textcache = $mycache ? 'URIBLcache' : 'URIBL' ;
17516	$listed = "$results_uribl" ;
17517
17518	$listed =~ s/\s+$//o;
17519    $listed =~ s/^\s+//o;
17520#    $listed =~ s/$listed_domain\.//g;
17521	$listed =~ s/<-127\.\d+\.\d+\.\d+//go if $URIBLLog < 2;
17522#	$listed =~ s/\s/,/o;
17523
17524
17525    return 1 if $uribls_returned <= 0;
17526
17527
17528 	if ( (!$URIBLmaxweight && $uribls_returned >= $URIBLmaxhits) or ($URIBLmaxweight && $weightsum >= $URIBLmaxweight) ) {
17529
17530    	$this->{messagereason} = "$textcache failed: $listed";
17531    	$this->{newsletterre} = "";
17532    	$this->{uriblfail} =1;
17533        return 0 if !$fh;
17534
17535    } else {
17536
17537                $this->{messagereason} = "$textcache neutral: $listed";
17538                $tlit = "[scoring:$weightsum]" if $mValidateURIBL != 2;
17539
17540                mlog( $fh, "$tlit -- $this->{messagereason}" )
17541                  if ( $URIBLLog && $mValidateURIBL != 2 ) && $fh;
17542                pbWhiteDelete( $fh, $thisip ) if $fh;
17543                $this->{uriblneutral}       = 1;
17544                $this->{newsletterre} = '';
17545                return 1 if $ValidateURIBL == 2 && $fh;
17546
17547                pbAdd( $fh, $thisip, $weightsum, "URIBLneutral" ) if $fh;
17548                $this->{myheader} .= "X-Assp-URIBL: neutral - $listed\r\n" if $AddURIBLHeader && $fh && $this->{myheader} !~ /URIBL/;
17549                return 1;
17550
17551    }
17552
17553	my $reply;
17554    if (  $weightsum >= $URIBLmaxweight ) {
17555    	$tlit = "[scoring:$weightsum]" ;
17556    	mlog( $fh, "$tlit -- $this->{messagereason}" )
17557      		if $URIBLLog && $fh;
17558		return 1 if $ValidateURIBL == 2 && $fh;
17559        pbWhiteDelete( $fh, $this->{ip} ) if $fh;
17560        $this->{newsletterre} = '';
17561        pbAdd( $fh, $thisip, $weightsum, "URIBLfailed" ) if $fh;
17562        $this->{myheader} .= "X-Assp-URIBL: fail - $listed\r\n" if $AddURIBLHeader && $fh && $this->{myheader} !~ /URIBL/;
17563		return 1 if $ValidateURIBL == 3;
17564		return 1 if $this->{messagescore} <= $this->{spamMaxScore};
17565        $reply = $SpamError;
17566        $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
17567		$reply =~ s/REASON/URIBL Listed in $listed/go;
17568        $reply = replaceerror ($fh, $reply);
17569
17570
17571        if ($fh && ! $slok) {$Stats{uriblfails}++;}
17572        $this->{test} = "allTestMode";
17573        $this->{newsletterre}		= '';
17574
17575        thisIsSpam($fh,$this->{messagereason}, $URIBLFailLog,$reply, 	 	$this->{testmode}, $slok  ,$done) if $fh;
17576        return 0;
17577    }
17578    return 1;
17579
17580}
17581
17582sub ipNetwork {
17583    my ($ip,$netblock)=@_;
17584    if ($ip =~ /:[^:]*:/o) {
17585        return ipv6expand($ip) if (!$netblock);
17586        $netblock = 64 if $netblock == 1;
17587        return join ':', map{my $t = sprintf("%x", oct("0b$_"));$t;} unpack 'a16' x 8, ipv6binary($ip,$netblock) . '0' x (128 - $netblock);
17588    } else {
17589        return $ip if (!$netblock);
17590        $netblock = 24 if $netblock == 1;
17591        my $u32 = unpack 'N', pack 'CCCC', split /\./o, $ip;
17592        my $mask = unpack 'N', pack 'B*', '1' x $netblock . '0' x (32 - $netblock );
17593        return join '.', unpack 'CCCC', pack 'N', $u32 & $mask;
17594    }
17595}
17596
17597# retriev the trailing IPv4 address from a tunneled IPv6address
17598sub ipv6TOipv4 {
17599    my $ip = shift;
17600    $ip =~ s/^.*?($IPv4Re)$/$1/o;
17601    return $ip;
17602}
17603
17604# converts IPv4 112.23.45.16 to 7017:2d10
17605sub ipv4TOipv6 {
17606    my $ip = shift;
17607    $ip =~ s/0?x?([A-F][A-F0-9]?|[A-F0-9]?[A-F])/hex($1)/goie;
17608
17609    my ($h1,$h2,$h3,$h4) = split(/\./o,$ip);
17610    return sprintf("%x",256 * $h1 + $h2).':'.sprintf("%x",256 * $h3 + $h4);
17611}
17612
17613# convert IPv6 2001:123:456::1 to 2001:123:456:0:0:0:0:1
17614# and convert trailing IPv4 to two IPv6 words
17615sub ipv6expand {
17616    my $ip = shift;
17617    return $ip if ($ip !~ /:/o);
17618    $ip =~ s/($IPv4Re)$/ipv4TOipv6($1)/eo;
17619    return $ip if ($ip !~ /::/o);
17620    my $col = $ip =~ tr/://;
17621    $col = 8 if $col > 8;
17622    $ip =~ s/^(.*)::(.*)$/($1||'0').':'.('0:'x(8-$col)).($2||'0')/oe;
17623    return $ip;
17624}
17625
17626# convert IPv6 address to binary string
17627sub ipv6binary {
17628    my ($ip, $bits) = @_;
17629    return pack("a$bits", unpack 'B128', pack 'n8', map{my $t = hex($_);$t;} split(/:/o, ipv6expand($ip)));
17630}
17631
17632# convert IPv6 2001:123:456::1 to 2001:0123:0456:0000:0000:0000:0000:0001
17633sub ipv6fullexp {
17634    return sprintf('%04s:'x(unpack("A1",${'X'})+5).'%04s',split(/:/o,ipv6expand(shift)));
17635}
17636
17637# convert IPv6 to lower case reverse doted digits for RBL / RWL checks
17638# 2001:DB8:abc:123::42 to
17639# 2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.2.1.0.c.b.a.0.8.b.d.0.1.0.0.2
17640sub ipv6hexrev {
17641    local $_ = ipv6fullexp(shift);
17642    return join('.',split(//o, reverse $_)) unless(s z:zzg-((ord(":")*4+34)%($_[0]+1)));
17643    undef;
17644}
17645
17646sub formatTimeInterval {
17647  my $interval=shift;
17648  my $res;
17649  $res.=$_.'d ' if local $_=int($interval/(24*3600)); $interval%=(24*3600);
17650  $res.=$_.'h ' if $_=int($interval/3600); $interval%=3600;
17651  $res.=$_.'m ' if $_=int($interval/60); $interval%=60;
17652  $res.=$interval.'s ' if ($interval || !defined $res);
17653  $res=~s/\s$//o;
17654  return $res;
17655}
17656
17657sub getRRA {
17658    my $dom = shift;
17659    my @IP;
17660    my $type = 'A';
17661    eval {
17662        if (defined(${chr(ord($type)+23)}) && (my $res = queryDNS($dom ,$type))) {
17663            my @answer = map{$_->string} $res->answer;
17664            while (@answer) {
17665                push @IP, Net::DNS::RR->new(shift @answer)->rdatastr;
17666            }
17667        }
17668    };
17669    return @IP;
17670}
17671sub getRRData {
17672    my ($dom, $type) = @_;
17673    return getRRA($dom) if $type eq 'A';
17674    my $answer;
17675    my $RR;
17676    my $gotname;
17677    my $gottype;
17678    my $gotdata;
17679    eval {
17680      my $res = queryDNS($dom,$type);
17681      if ($res) {
17682          $answer = ($type ne 'PTR') ? join('', map{$_->string} $res->answer)
17683                                     : [$res->answer->string]->[0];
17684          $RR = Net::DNS::RR->new($answer);
17685          $gotname = $RR->name;
17686          $gottype = $RR->type;
17687          $gotdata = $RR->rdatastr;
17688      }
17689    };
17690    return if $@;
17691    return if $gotname ne $dom && $type ne 'PTR';
17692    return if $gottype ne $type;
17693    return unless $gotdata;
17694    return $gotdata;
17695}
17696
17697sub queryDNS {
17698	my ($domain, $type) = @_;
17699
17700    my $rslv = Net::DNS::Resolver->new(
17701        nameservers => \@nameservers,
17702        tcp_timeout => $DNStimeout,
17703        udp_timeout => $DNStimeout,
17704        retrans     => $DNSretrans,
17705        retry       => $DNSretry
17706    ) or return;
17707    getRes('force', $rslv);
17708
17709	my $resp;
17710	eval
17711	{
17712            # set a timeout
17713            local $SIG{ALRM} = sub { die "DNS query timeout for $domain\n" };
17714            alarm $DNStimeout + 2;
17715            eval {$resp = $rslv->query($domain, $type);};
17716            my $E = $@;
17717            alarm 0;
17718            die $E if $E;
17719	};
17720	my $E = $@;
17721	alarm 0;
17722	return if $E;
17723	return $resp;
17724}
17725sub getRes {
17726    my $run = shift;
17727    eval(<<'EOT');
17728    $run.='_v'.(unpack("A1",${'X'})+2);
17729    $_[0]->$run(! $CanUseIOSocketINET6 || $forceDNSv4);
17730EOT
17731    return;
17732}
17733
17734sub getDNSResolver {
17735    my $res;
17736
17737    $res = $DNSresolver;
17738    if (! $res or $DNSresolverTime{$WorkerNumber} < time) {
17739        my $class = shift;
17740        $class ||= 'Net::DNS::Resolver';
17741        $res = $orgNewDNSResolver->($class,
17742            nameservers => \@nameservers,
17743            tcp_timeout => $DNStimeout,
17744            udp_timeout => $DNStimeout,
17745            retrans     => $DNSretrans,
17746            retry       => $DNSretry,
17747            @_
17748        );
17749        getRes('force', $res);
17750        $DNSresolver = $res;
17751    }
17752    $DNSresolverTime{$WorkerNumber} = time + 1800 if $DNSresolver;
17753    return $res;
17754}
17755
17756
17757
17758
17759sub ForgedHeloOK {
17760    my ( $fh, $rcpt ) = @_;
17761    my $this = $Con{$fh};
17762    my $ip = $this->{ip};
17763    my $helo = $this->{helo};
17764
17765	return 1 if $AsASecondary;
17766	return 1 if $this->{addressedToSpamBucket};
17767
17768    return 1 if $this->{ispip};
17769    return 1 if $this->{nohelo};
17770    return 1 if $this->{notspamtag};
17771
17772	return 1 if $DoFakedWL && $this->{whitelisted};
17773	return 1 if $DoFakedNP && $this->{noprocessing};
17774
17775    return 1 if $ip =~ /$IPprivate/ && $ip ne "127.0.0.1";
17776
17777    return 1 if !$DoFakedLocalHelo;
17778    return 1 if $this->{relayok};
17779    return 1 if $this->{acceptall};
17780
17781
17782    return 1 if $heloBlacklistIgnore && $helo =~ $HBIRE;
17783
17784    my $mDoFakedLocalHelo = $DoFakedLocalHelo;
17785    $this->{testmode} = 0;
17786	$this->{testmode} = 1	if $DoFakedLocalHelo == 4 or $allTestMode;
17787	$mDoFakedLocalHelo = 1 		if $DoFakedLocalHelo == 4;
17788    my $tlit = tlit($DoFakedLocalHelo);
17789
17790    ( my $literal ) =
17791      $helo =~ /\[?((?:\d{1,3}\.){3}\d{1,3})\]?/;    # domain literal
17792
17793
17794
17795   	if (   ( $localDomains && $helo =~ /$LDRE/)
17796
17797       	|| ($localDomainsFile && $localDomainsFile{$helo})
17798    	|| ($DoLocalIMailDomains && &localIMaildomain($helo))
17799
17800
17801        || $helo eq "friend"
17802        || $helo eq "localhost"
17803        || $myServerRe && $helo =~ /$LHNRE/
17804        || $literal && $literal =~ /$LHNRE/
17805        || $literal && lc($literal) eq lc($localhostip)) {
17806
17807
17808        $this->{prepend} = "[ForgedHELO]";
17809
17810        $this->{messagereason} = "forged Helo: '$helo'";
17811        $tlit= "[scoring:$fhValencePB]" if $mDoFakedLocalHelo == 3;
17812
17813        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" )
17814          if $mDoFakedLocalHelo >= 2;
17815        delayWhiteExpire($fh);
17816        pbWhiteDelete( $fh, $this->{ip} );
17817        return 1 if $mDoFakedLocalHelo == 2;
17818
17819        pbAdd( $fh, $ip, $fhValencePB, "ForgedHELO" );
17820
17821        return 1 if $mDoFakedLocalHelo == 3;
17822		$Stats{invalidHelo}++;
17823        return 0;
17824    }
17825    return 1;
17826}
17827sub NoSpoofingOK {
17828    my ( $fh, $what ) = @_;
17829    my $this = $Con{$fh};
17830    d("NoSpoofingOK - $what");
17831    $this->{prepend}       = '[SpoofingSender]';
17832#	mlog( $fh, "0 $what" );
17833    return 1 if $this->{NoSpoofingOK}{$what};
17834    $this->{NoSpoofingOK}{$what} = 1;
17835    return 1 if ! $DoNoSpoofing;
17836
17837    skipCheck($this,'sb','np','ro','aa') && return 1;
17838    return 1 if ! $this->{$what};
17839#	mlog( $fh, "1 $what" );
17840    return 1 if $this->{$what} =~ /$BSRE/;
17841
17842    return 1 if ! localmail( $this->{$what} ) || $LDAPoffline;
17843
17844    return 1 if $onlySpoofingCheckIP && ! matchIP( $this->{ip}, 'onlySpoofingCheckIP', 0, 1);
17845
17846    return 1 if matchIP( $this->{ip}, 'noSpoofingCheckIP', 0, 1 );
17847#	mlog( $fh, "2 $what" );
17848    return 1 if $onlySpoofingCheckDomain && ! matchSL( $this->{$what}, 'onlySpoofingCheckDomain' , 0, 1);
17849
17850    return 1 if matchSL( $this->{$what}, 'noSpoofingCheckDomain' );
17851
17852    my $tlit = tlit($DoNoSpoofing);
17853    my ($whatuser) = $this->{$what} =~ /(.*)\@/;
17854    my ($to) = $this->{rcpt} =~ /(.*)\@/;
17855
17856    if ($to eq $whatuser) {
17857
17858        $this->{prepend}       = '[SpoofingSender]';
17859
17860        $this->{messagereason} = "No Spoofing Allowed in '$whatuser' in '$this->{$what}'";
17861        $tlit = "[scoring:$flValencePB]" if $DoNoSpoofing == 3;
17862        mlog( $fh, "$tlit ($this->{messagereason})" )
17863               if $ValidateSenderLog && $DoNoSpoofing >= 2;
17864
17865        return 1 if $DoNoSpoofing == 2 ;
17866        pbAdd( $fh, $this->{ip},$flValencePB, 'NoSpoofing' ) ;
17867
17868        return 1 if $DoNoSpoofing == 3 ;
17869
17870        return 0;
17871    }
17872    my $toscore = 0;
17873    foreach (keys %{$this->{NoSpoofingOK}}) { $toscore += $this->{NoSpoofingOK}{$_}; }
17874    $this->{prepend}       = '[SpoofingSender]';
17875    $this->{messagereason} = "No Spoofing Allowed '$this->{$what}' in '$what'";
17876    mlog( $fh, "$tlit ($this->{messagereason})" )
17877           if $ValidateSenderLog && $DoNoSpoofing >= 2;
17878
17879    return 1 if $DoNoSpoofing == 2 ;
17880    pbAdd( $fh, $this->{ip}, 'flValencePB', 'NoSpoofing' ) if $toscore < 10;
17881    $this->{NoSpoofingOK}{$what} = 10;
17882    return 1 if $DoNoSpoofing == 3 ;
17883    return 0;
17884}
17885
17886
17887# do forged local sender
17888sub LocalSenderOK {
17889    my ( $fh, $ip ) = @_;
17890
17891    my $this = $Con{$fh};
17892
17893	return 1 if $this->{notspamtag};
17894    my $mf = &batv_remove_tag($fh,$this->{mailfrom},'');
17895    return 1 if !$DoNoValidLocalSender;
17896	return 1 if ! $LocalAddresses_Flat && ! $DoLDAP && (! $DoVRFY || (! scalar(keys %DomainVRFYMTA) && ! scalar(keys %FlatVRFYMTA)));
17897    d('LocalSenderOK');
17898	return 1 if $noSpoofingCheckDomain
17899		&& matchSL( $mf, 'noSpoofingCheckDomain' );
17900	return 1 if $noSpoofingCheckIP
17901		&& matchIP( $ip, 'noSpoofingCheckIP', 0, 1 ) ;
17902    return 1 if $this->{addressedToSpamBucket};
17903    return 1 if $this->{localsenderdone};
17904    $this->{localsenderdone} = 1;
17905
17906    return 1 if $this->{noprocessing};
17907    return 1 if $this->{passingreason} =~ /white/;
17908
17909    return 1 if $this->{relayok};
17910    return 1 if $this->{acceptall};
17911    return 1 if $this->{ispip};
17912    return 1 if !localmail( $this->{mailfrom} );
17913
17914
17915
17916
17917    my $mDoNoValidLocalSender = $DoNoValidLocalSender;
17918	$this->{testmode} = 0;
17919	$this->{testmode} = 1	if $DoNoValidLocalSender == 4 or $allTestMode;
17920	$mDoNoValidLocalSender = 1 		if $DoNoValidLocalSender == 4;
17921    #enforce valid local mailfrom
17922
17923
17924    my $tlit = tlit($mDoNoValidLocalSender);
17925    $this->{prepend} = "[UnknownLocalAddress]";
17926
17927    $this->{islocalmailaddress} = 0;
17928
17929    if ( $LocalAddresses_Flat
17930        && matchSL( $mf, 'LocalAddresses_Flat' ) )
17931    {
17932        $this->{islocalmailaddress} = 1;
17933    } else {
17934
17935      # Need another check?
17936      # check sender against LDAP or VRFY ?
17937      $this->{islocalmailaddress} = &localmailaddress($fh,$mf)
17938          if (($DoLDAP && $CanUseLDAP) or
17939              ($CanUseNetSMTP && $DoVRFY &&
17940               $mf =~ /^([^@]*@)([^@]*)$/o &&
17941               (&matchHashKey('DomainVRFYMTA',lc $2) or &matchHashKey('FlatVRFYMTA',lc "\@$2"))));
17942    }
17943    if ( !$this->{islocalmailaddress} ) {
17944    	pbWhiteDelete( $fh, $this->{ip} );
17945        $this->{messagereason} = "Unknown address with local domain '$this->{mailfrom}'";
17946        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" )
17947          if $ValidateSenderLog && $mDoNoValidLocalSender >= 2;
17948        return 1 if $mDoNoValidLocalSender == 2;
17949        pbAdd( $fh, $this->{ip}, $flValencePB, "InvalidLocalSender");
17950        return 1 if $mDoNoValidLocalSender == 3;
17951        return 0;
17952    }
17953    return 1;
17954}
17955
17956sub FromAddressOK {
17957    my $fh = shift;
17958    my $this = $Con{$fh};
17959    d('LocalAddressOK');
17960    $this->{islocalmailaddress} = 0;
17961
17962    if (($this->{relayok} and &batv_remove_tag(0,$this->{from},'') =~ /$BSRE/) or  # a bounce mail from a internal MTA
17963         &localmailaddress($fh,$this->{from})) {
17964
17965        $this->{islocalmailaddress} = 1;
17966    }
17967    return $this->{islocalmailaddress};
17968}
17969
17970sub LocalAddressOK {
17971    my $fh = shift;
17972    my $this = $Con{$fh};
17973    d('LocalAddressOK');
17974    $this->{islocalmailaddress} = 0;
17975
17976    if (($this->{relayok} and &batv_remove_tag(0,$this->{mailfrom},'') =~ /$BSRE/) or  # a bounce mail from a internal MTA
17977         &localmailaddress($fh,$this->{mailfrom})) {
17978
17979        $this->{islocalmailaddress} = 1;
17980    }
17981    return $this->{islocalmailaddress};
17982}
17983
17984sub AUTHErrorsOK {
17985    my $fh = shift;
17986    return 1 unless $MaxAUTHErrors;
17987    my $this = $Con{$fh};
17988    return 1 if ($this->{relayok});
17989    return 1 if ($this->{whitelisted});
17990    return 1 if ($this->{noprocessing} == 1);
17991    return 1 if ($this->{ispip});
17992    return 1 if matchIP($this->{ip},'noMaxAUTHErrorIPs',0,1);
17993    my $ip = $this->{ip};
17994    $ip = &ipNetwork( $ip, $PenaltyUseNetblocks);
17995
17996    return 1 if $AUTHErrors{$ip}++ <= $MaxAUTHErrors;
17997    $this->{messagereason}="too much AUTH errors from $ip";
17998    pbAdd( $fh, $this->{ip}, $autValencePB, "AUTHErrors" ) if ! matchIP($ip,'noPB',0,1);
17999    $AUTHErrors{$ip}++;
18000    return 0;
18001}
18002sub SameSubjectOK {
18003    my $fh = shift;
18004    d('SameSubjectOK');
18005    my $this = $Con{$fh};
18006	$this->{prepend} = "[SameSubject]";
18007	&makeSubject($fh);
18008	my $sub;
18009 	$sub = $Con{$fh}->{subject3};
18010 	$this->{red} = 1 if ! $sub;
18011 	return 1 if ! $sub;
18012
18013	my $mf = &batv_remove_tag(0,$this->{mailfrom},'');
18014
18015    my $myip = $this->{ip};
18016    if ($this->{ispip} && $this->{cip}) {
18017        $myip = $this->{cip};
18018    } elsif ($this->{ispip}) {
18019        return 1;
18020    }
18021    return 1 if ($SameSubjectOnly && ! &matchSL($mf,'SameSubjectOnly'));
18022    return 1 if (&matchSL($mf,'SameSubjectNoAddresses'));
18023    return 1 if (&matchIP($myip,'SameSubjectNoIP',$fh, 1));
18024
18025
18026
18027	my $w = $isValencePB;
18028
18029
18030
18031    if (               !$this->{whitelisted}
18032                    && !$this->{relayok}
18033                    && $DoSameSubject
18034#					&& $myip !~ /$IPprivate/
18035                    && ! matchIP( $myip, 'acceptAllMail',   0, 1 )
18036
18037
18038
18039       ) {
18040
18041 #               mlog( $fh, "[SameSubject] test2 subject: $sub");
18042                my $tlit = &tlit($DoSameSubject);
18043                $myip=&ipNetwork($myip, $DelayUseNetblocks );
18044                $myip .= '.' if $DelayUseNetblocks;
18045
18046                if ( $SameSubjectCache{$sub}) {
18047                	$this->{prepend} = "[SameSubject]";
18048                	$SameSubjectCache{$sub} = time();
18049                	$this->{messagereason} = "Same subject '$sub' limited to $SameSubjectNumber";
18050
18051                	$tlit = "scoring:$isValencePB]" ;
18052                    mlog( $fh, "$tlit $this->{messagereason}");
18053                	pbAdd( $fh, $myip, $isValencePB, "LimitingSameSubject" );
18054					pbWhiteDelete( $fh, $myip );
18055                	return 0 if $DoSameSubject == 1;
18056                	return 1;
18057                }
18058
18059                if ((time() - $SameSubjectTriesExpiration{$sub}) > $SameSubjectInterval) {
18060                    $SameSubjectTries{$sub} = 1;
18061                    $SameSubjectTriesExpiration{$sub} = time();
18062
18063                } else {
18064
18065                    $SameSubjectTriesExpiration{$sub} = time() if $SameSubjectTries{$sub}==1;
18066                    $SameSubjectTries{$sub}++;
18067                }
18068                my $tlit = &tlit($DoSameSubject);
18069                $tlit .= "[testmode]"   if ($allTestMode && $DoSameSubject) == 1 || $DoSameSubject == 4;
18070                my $mDoSameSubject = $DoSameSubject;
18071                $mDoSameSubject = 3 if ($allTestMode && $DoSameSubject == 1) || $DoSameSubject == 4;
18072
18073                if ( $SameSubjectTries{$sub} > $SameSubjectNumber ) {
18074                    $this->{prepend} = "[SameSubject]";
18075                    $this->{messagereason} = "number of same subjects '$sub' surpassed limit SameSubjectNumber ($SameSubjectNumber)";
18076                    my $w = $isValencePB;
18077
18078					pbWhiteDelete( $fh, $myip );
18079					$tlit = "scoring:$isValencePB]" if $mDoSameSubject == 3;
18080
18081
18082                    mlog( $fh, "$tlit $this->{messagereason}")
18083                      if ($SessionLog or $denySMTPLog) && $mDoSameSubject != 1 && $SameSubjectTries{$sub} == $SameSubjectNumber + 1;
18084
18085					$SameSubjectCache{$sub} = time();
18086#					$GPBmodTestList->('GUI','bombSubjectRe','add',"" ,"\Q$sub\E",0);
18087                    pbAdd( $fh, $myip, $isValencePB, "LimitingSameSubject" ) if $mDoSameSubject != 2;
18088                    if ( $mDoSameSubject == 1 && $sub ne "empty" ) {
18089
18090                        return 0;
18091
18092                    }
18093                }
18094    }
18095    return 1;
18096}
18097
18098
18099sub SameAUTHuserOK {
18100    my($fh,$sub)=@_;
18101    d('SameAUTHuserOK');
18102    my $this = $Con{$fh};
18103
18104    my $sub;
18105
18106    return 1 if $this->{SameAUTHuserOK};
18107    $this->{SameAUTHuserOK} = 1;
18108
18109
18110    if ((time() - $SameAUTHuserDuration{$sub}) > $maxSameAUTHuserDuration) {
18111                    $SameAUTHuserTries{$sub} = 1;
18112                    $SameAUTHuserDuration{$sub} = time();
18113
18114                } else {
18115
18116                    $SameAUTHuserDuration{$sub} = time() if $SameSubjectTries{$sub}==1;
18117                    $SameAUTHuserTries{$sub}++;
18118    }
18119    my $tlit = &tlit($DoSameAUTHuser);
18120    $tlit .= "[testmode]"   if ($allTestMode && $DoSameAUTHuser) == 1 || $DoSameSubject == 4;
18121    my $mDoSameAUTHuser = $DoSameAUTHuser;
18122$mDoSameAUTHuser = 3 if ($allTestMode && $DoSameAUTHuser == 1) || $DoSameAUTHuser == 4;
18123
18124    if ( $SameAUTHuserTries{$sub} > $maxSameAUTHuser ) {
18125                    $this->{prepend} = "[SameAUTHuser]";
18126                    $this->{messagereason} = "number of same AUTH user '$sub' surpassed limit maxSameAUTHuser ($maxSameAUTHuser)";
18127
18128                    mlog( $fh, "$tlit $this->{messagereason}");
18129
18130                    if ( $mDoSameAUTHuser == 1 ) {
18131
18132                        return 0;
18133
18134                    }
18135    }
18136
18137    return 1;
18138}
18139
18140
18141
18142sub FrequencyIPOK {
18143    my $fh = shift;
18144    d('FrequencyIPOK');
18145    my $this = $Con{$fh};
18146    my $ConIp550 = $this->{ip};
18147    if ($this->{ispip} && $this->{cip}) {
18148        $ConIp550 = $this->{cip};
18149    } elsif ($this->{ispip}) {
18150        return 1;
18151    }
18152
18153    return 1 if $this->{doneDoFrequencyIP} eq $ConIp550;
18154    $this->{doneDoFrequencyIP} = $ConIp550;
18155
18156    if (
18157    					! $this->{relayok}
18158    				&& !$this->{whitelisted}
18159                    && !$this->{noprocessing}
18160                    && !$this->{contentonly}
18161                    && $DoFrequencyIP
18162                    && $maxSMTPipConnects
18163                    && ! matchIP( $ConIp550, 'noPB',            $fh, 1 )
18164                    && ! matchIP( $ConIp550, 'noProcessingIPs', $fh, 1 )
18165                    && ! matchIP( $ConIp550, 'whiteListedIPs',  $fh, 1 )
18166                    && ! matchIP( $ConIp550, 'noDelay',         $fh, 1 )
18167                    && ! matchIP( $ConIp550, 'noBlockingIPs',   $fh, 1 )
18168                    && ! matchIP( $ConIp550, 'acceptAllMail',   $fh, 1 )
18169
18170                    &&   pbBlackFind($ConIp550)
18171                    && ! pbWhiteFind($ConIp550)
18172       )
18173            # ip connection limiting per timeframe
18174    {
18175
18176       # If the IP address has tried to connect previously, check it's frequency
18177                if ( $IPNumTries{$ConIp550} ) {
18178                    $IPNumTries{$ConIp550}++;
18179
18180              # If the last connect time is past expiration, reset the counters.
18181              # If it has not expired, but is outside of frequency duration and
18182              # below the maximum session limit, reset the counters. If it is
18183              # within duration
18184                    if (((time() - $IPNumTriesExpiration{$ConIp550}) > $maxSMTPipExpiration)  || ((time() - $IPNumTriesDuration{$ConIp550}) > $maxSMTPipDuration) && ($IPNumTries{$ConIp550} < $maxSMTPipConnects)) {
18185                        $IPNumTries{$ConIp550} = 1;
18186                        $IPNumTriesDuration{$ConIp550} = time();
18187                        $IPNumTriesExpiration{$ConIp550} = time();
18188                    }
18189                } else {
18190                    $IPNumTries{$ConIp550} = 1;
18191                    $IPNumTriesDuration{$ConIp550} = time();
18192                    $IPNumTriesExpiration{$ConIp550} = time();
18193
18194                }
18195                my $tlit = &tlit($DoFrequencyIP);
18196                $tlit = "[testmode]"   if $allTestMode && $DoFrequencyIP == 1 || $DoFrequencyIP == 4;
18197
18198                my $mDoFrequencyIP = $DoFrequencyIP;
18199                $mDoFrequencyIP = 3 if $allTestMode && $DoFrequencyIP == 1 || $DoFrequencyIP == 4;
18200
18201                if ( $IPNumTries{$ConIp550} > $maxSMTPipConnects ) {
18202                    $this->{prepend} = "[IPfrequency]";
18203                     $this->{messagereason} = "'$ConIp550' passed limit($maxSMTPipConnects) of ip connection frequency";
18204
18205                    mlog( $fh, "$tlit $this->{messagereason}")
18206                      if $SessionLog >= 2
18207                          && $IPNumTries{$ConIp550} > $maxSMTPipConnects + 1;
18208                    mlog( $fh,"$tlit $this->{messagereason}")
18209                      if $SessionLog
18210                          && $IPNumTries{$ConIp550} == $maxSMTPipConnects + 1;
18211                    pbAdd( $fh, $this->{ip}, 'ifValencePB', "IPfrequency" ) if $mDoFrequencyIP!=2;
18212                    if ( $mDoFrequencyIP == 1 ) {
18213                        $Stats{smtpConnLimitFreq}++;
18214                        unless (($send250OKISP && $this->{ispip}) || $send250OK) {
18215                            seterror( $fh, "554 5.7.1 too frequent connections for '$ConIp550'", 1 );
18216                            return 0;
18217                        }
18218                    }
18219                }
18220    }
18221    return 1;
18222}
18223
18224
18225# returns 0 on success - else next possible try time
18226sub localFrequencyNotOK {
18227    my $fh = shift;
18228    return 0 unless $LocalFrequencyInt;
18229    return 0 unless $LocalFrequencyNumRcpt;
18230    return localFrequencyNotOK_Run($fh);
18231}
18232sub localFrequencyNotOK_Run {
18233    my $fh = shift;
18234    my $this=$Con{$fh};
18235    d('localFrequencyNotOK');
18236
18237    return 0 unless $this->{mailfrom};
18238    return 0 unless $this->{relayok};
18239    return 0 if $this->{noprocessing};
18240    my ($to) = $this->{rcpt} =~ /(\S+)/o;
18241    my $mf = batv_remove_tag(0,$this->{mailfrom},'');
18242    return 0 if matchSL( [$to,$mf], 'EmailAdmins' );
18243    return 0 if lc($to) eq lc($EmailFrom);
18244    return 0 if lc($mf) eq lc($EmailFrom);
18245
18246    return 0 if ($LocalFrequencyOnly && ! &matchSL($mf,'LocalFrequencyOnly'));
18247    return 0 if ( matchSL($mf,'NoLocalFrequency'));
18248    return 0 if ( matchIP( $this->{ip}, 'NoLocalFrequencyIP', 0, 1 ));
18249
18250    my $time = time;
18251    my $numrcpt;
18252    my $firsttime;
18253    my $data;
18254    my $hat; $hat = $1 if $mf =~ /(\@.*)/;
18255
18256    my %F = split(/ /o,$localFrequencyCache{$hat});
18257    my $i;
18258    foreach (sort keys %F) {
18259        if ($_ + $LocalFrequencyInt  < $time) {
18260            delete $F{$_};
18261            next;
18262        } else {
18263            $numrcpt += $F{$_};
18264            $firsttime = $_ if $i < 1;
18265        }
18266        $i++;
18267    }
18268    foreach (sort keys %F) {
18269        $data .= "$_ $F{$_} ";
18270    }
18271    $firsttime = $time unless $firsttime;
18272    $localFrequencyCache{$hat} = $data . "$time $this->{numrcpt}";
18273    $numrcpt += $this->{numrcpt};
18274    return 0 if $numrcpt < $LocalFrequencyNumRcpt;
18275    return $firsttime + $LocalFrequencyInt;
18276}
18277
18278sub NumRcptOK {
18279    my($fh,$block)=@_;
18280    return 1 unless $DoMaxDupRcpt;
18281    return NumRcptOK_Run($fh,$block);
18282}
18283sub NumRcptOK_Run {
18284    my($fh,$block)=@_;
18285    my $this=$Con{$fh};
18286    d('NumRcptOK');
18287    my $DoMaxDupRcpt = $DoMaxDupRcpt;
18288    $DoMaxDupRcpt = 3 if !$block  && $DoMaxDupRcpt == 1;
18289    return 1 unless $this->{numrcpt};
18290    return 1 unless (scalar keys %{$this->{rcptlist}});
18291    skipCheck($this,'aa','ro','wl') && return 1;
18292    return 1 if $this->{noprocessing} & 1;
18293    return 1 if $this->{spamlover} & 1;
18294    return 1 if $this->{allLoveSpam} & 1;
18295    return 1 if ((scalar keys %{$this->{rcptlist}}) + $MaxDupRcpt >= $this->{numrcpt});
18296    my $maxRcpt;
18297    my $maxNum = 0;
18298    while (my ($k,$v) = each %{$this->{rcptlist}}) {
18299        my $tt = needEs($v,' time','s');
18300        mlog($fh,"info: address $k used $tt") if $ValidateUserLog >= 2;
18301        if ($v > $maxNum) {
18302            $maxNum = $v;
18303            $maxRcpt = $k;
18304        }
18305    }
18306    my $tlit = &tlit($DoMaxDupRcpt);
18307    $this->{prepend}="[MaxDuplicateRcpt]";
18308    $this->{messagereason} = "too many duplicate recipients ($maxRcpt , $maxNum)";
18309    mlog($fh,"$tlit $this->{messagereason}",1) if $ValidateUserLog;
18310    return 1 if $DoMaxDupRcpt == 2;
18311    my $reply = "550 5.5.3 $this->{messagereason}";
18312    pbAdd( $fh, $this->{ip}, 'mdrValencePB', 'MaxDuplicateRcpt' );
18313    return 1 if $DoMaxDupRcpt == 3;
18314    $Stats{rcptNonexistent}++;
18315    seterror($fh, $reply,1);
18316    return 0;
18317}
18318
18319sub MessageSizeOK {
18320    my $fh = shift;
18321    my $this=$Con{$fh};
18322    return 1 if $this->{sizeok};
18323
18324    d('MessageSizeOK');
18325	return 1 if $noMaxSize && matchSL( $this->{mailfrom}, 'noMaxSize' );
18326    my $maxRealSize = $this->{maxRealSize} || $maxRealSize || 0;
18327    my $maxSize = $this->{maxSize} || $maxSize || 0;
18328    if ($this->{relayok} && ! defined $this->{maxSize}) {
18329        $this->{maxRealSize} = $this->{maxSize} = 0;
18330        my @MSadr  = sort {$main::b <=> $main::a} map {matchHashKey('MSadr' ,$_)} split(' ',$this->{rcpt}),$this->{mailfrom},$this->{ip},$this->{cip},@{"$this.sip"};
18331        my @MRSadr = sort {$main::b <=> $main::a} map {matchHashKey('MRSadr',$_)} split(' ',$this->{rcpt}),$this->{mailfrom},$this->{ip},$this->{cip},@{"$this.sip"};
18332        $maxSize = $this->{maxSize} = $MSadr[0] if (defined $MSadr[0]);
18333        $maxSize = $this->{maxSize} = 0 if grep({$_ == 0} @MSadr);
18334        $maxRealSize = $this->{maxRealSize} = $MRSadr[0] if (defined $MRSadr[0]);
18335        $maxRealSize = $this->{maxRealSize} = 0 if grep({$_ == 0} @MRSadr);
18336    }
18337
18338    my $maxRealSizeExternal = $this->{maxRealSizeExternal} || $maxRealSizeExternal || 0;
18339    my $maxSizeExternal = $this->{maxSizeExternal} || $maxSizeExternal || 0;
18340    if (! $this->{relayok} && ! defined $this->{maxSizeExternal}) {
18341        $this->{maxRealSizeExternal} = $this->{maxSizeExternal} = 0;
18342        my @MSEadr  = sort {$main::b <=> $main::a} map {matchHashKey('MSEadr' ,$_)} split(' ',$this->{rcpt}),$this->{mailfrom},$this->{ip},$this->{cip},@{"$this.sip"};
18343        my @MRSEadr = sort {$main::b <=> $main::a} map {matchHashKey('MRSEadr',$_)} split(' ',$this->{rcpt}),$this->{mailfrom},$this->{ip},$this->{cip},@{"$this.sip"};
18344        $maxSizeExternal = $this->{maxSizeExternal} = $MSEadr[0] if (defined $MSEadr[0]);
18345        $maxSizeExternal = $this->{maxSizeExternal} = 0 if grep({$_ == 0} @MSEadr);
18346        $maxRealSizeExternal = $this->{maxRealSizeExternal} = $MRSEadr[0] if (defined $MRSEadr[0]);
18347        $maxRealSizeExternal = $this->{maxRealSizeExternal} = 0 if grep({$_ == 0} @MRSEadr);
18348    }
18349
18350    if ( ($this->{relayok} && $maxRealSize
18351            && ( ($this->{SIZE} > $this->{maillength} ? $this->{SIZE} : $this->{maillength}) * $this->{numrcpt} > $maxRealSize )) or
18352         (!$this->{relayok} && $maxRealSizeExternal
18353            && ( ($this->{SIZE} > $this->{maillength} ? $this->{SIZE} : $this->{maillength}) * $this->{numrcpt} > $maxRealSizeExternal ))
18354       )
18355    {
18356        &makeSubject($fh);
18357        my $max = $this->{relayok} ? &formatNumDataSize($maxRealSize) : &formatNumDataSize($maxRealSizeExternal);
18358        my $err = "552 message exceeds $max(size \* rcpt)";
18359        if ($this->{relayok}) {
18360            mlog( $fh, "error: message exceeds maxRealSize $max (size \* rcpt)!" );
18361        } else {
18362            $this->{prepend} = 'MaxRealMessageSize';
18363
18364            my $logsub = ( $subjectLogging && $this->{originalsubject} ? " $subjectStart$this->{originalsubject}$subjectEnd" : '' );
18365            mlog( $fh, "error: message exceeds maxRealSizeExternal $max (size \* rcpt)!)$logsub;",0,2 );
18366            $this->{prepend} = '';
18367        }
18368
18369        $this->{sizeok} = 1;
18370        NoLoopSyswrite( $fh, "$err\r\n" );
18371        done($fh);
18372        return 0;
18373    }
18374
18375    if ( (  $this->{relayok} && $maxSize         && $this->{maillength} > $maxSize         ) or
18376         (! $this->{relayok} && $maxSizeExternal && $this->{maillength} > $maxSizeExternal )
18377       )
18378    {
18379        &makeSubject($fh);
18380        my $max = $this->{relayok} ? &formatNumDataSize($maxSize) : &formatNumDataSize($maxSizeExternal);
18381        my $err = "552 message exceeds $max (size)";
18382        if ($this->{relayok}) {
18383            mlog( $fh, "error: message exceeds maxSize $max (size)!" );
18384        } else {
18385            $this->{prepend} = 'MaxMessageSize';
18386
18387            my $logsub = ( $subjectLogging && $this->{originalsubject} ? " $subjectStart$this->{originalsubject}$subjectEnd" : '' );
18388            mlog( $fh, "error: message exceeds maxSizeExternal $max (size))$logsub;",0,2 );
18389            $this->{prepend} = '';
18390        }
18391
18392        $this->{sizeok} = 1;
18393        NoLoopSyswrite( $fh, "$err\r\n" );
18394        done($fh);
18395
18396        return 0;
18397    }
18398    return 1;
18399}
18400
18401#queries the SenderBase service
18402sub SenderBaseMyIP {
18403    my $ip = shift;
18404    d('SenderBaseMyIP');
18405    return if !$CanUseSenderBase;
18406    return $SenderBaseMyCountry if $SenderBaseMyCountry;
18407    $SenderBaseMyCountry = eval{
18408    	Net::SenderBase::Query->new(
18409              Address => $ip,
18410              Host => 'query.senderbase.org',
18411              Timeout => 10
18412            )->results->ip_country;
18413    	};
18414    return $SenderBaseMyCountry;
18415}
18416
18417
18418#queries the SenderBase service
18419sub SenderBaseOK {
18420    my ( $fh, $ip ) = @_;
18421    return 1 if !$CanUseSenderBase;
18422    return SenderBaseOK_Run($fh, $ip);
18423}
18424sub SenderBaseOK_Run {
18425    my ( $fh, $ip) = @_;
18426    my $this = $Con{$fh};
18427    $fh = 0 if "$fh" =~ /^\d+$/o;
18428    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
18429	return 1 if $this->{senderbasedone};
18430	$this->{senderbasedone}=1;
18431    d('SenderBaseOK');
18432	return 1 if $this->{notspamtag};
18433
18434    my $results;
18435    my $query;
18436    my $cache;
18437    my $skip;
18438    my $tlit;
18439
18440    my $mfd; $mfd  = $1 if $this->{mailfrom} =~ /\@(.*)/o;
18441	return 1 if $this->{notspamtag};
18442#    return 1 if !$CanUseSenderBase;
18443
18444    return 1 if $this->{addressedToSpamBucket};
18445    return 1 if $this->{whitelisted};
18446    return 1 if $this->{noprocessing};
18447	return 1 if $AsASecondary;
18448	return 1 if !$DoOrgWhiting && !$DoOrgBlocking && !$DoCountryBlocking;
18449    return 1 if $this->{ispip} && !$this->{cip};
18450    return 1 if $this->{acceptall};
18451
18452    return 1 if $this->{ip} =~ /$IPprivate/;
18453    return 1 if $this->{relayok};
18454
18455    return 1 if $this->{contentonly};
18456
18457    my $ipcountry;
18458    my $orgname;
18459    my $domainname;
18460    my $blacklistscore;
18461    my $hostname_matches_ip;
18462    my $fortune1000;
18463    my $ipbondedsender;
18464    my $domainrating;
18465    my $bondedsender;
18466    my $resultip;
18467    my $ipCIDR;
18468
18469    my $slok = $this->{allLoveSpam} == 1;
18470
18471	$this->{prepend} = "[SenderBase]";
18472    if ( ! &SBCacheFind($ip)  ) {
18473
18474
18475        eval {
18476            $query = Net::SenderBase::Query->new(
18477                Transport => 'dns',
18478                Address   => $ip,
18479                Host      => 'query.senderbase.org',
18480                Timeout   => 5,
18481              );
18482            $results = $query->results;
18483        };
18484        if ($@) {
18485#           mlog( $fh, "warning: SenderBase: $@", 1 ) if $SenderBaseLog >= 2;
18486
18487
18488            return 1;
18489        }
18490
18491     	if ($results) {
18492
18493            eval{
18494            $bondedsender   = $results->{ip_in_bonded_sender};
18495            $blacklistscore = $results->{ip_blacklist_score};
18496            $hostname_matches_ip = $results->{hostname_matches_ip};
18497            $orgname        = $results->{org_name};
18498            $resultip       = $results->{ip};
18499            $fortune1000    = $results->{org_fortune_1000};
18500            $domainname     = $results->{domain_name};
18501            $domainrating   = $results->{domain_rating};
18502            $ipbondedsender = $results->{ip_in_bonded_sender};
18503            $ipcountry      = $results->{ip_country};
18504            $ipCIDR         = $results->{ip_cidr_range};
18505            if (! $fh) {
18506                $this->{sbstatus} = 0;
18507                $this->{sbdata} = "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR";
18508            }
18509            };
18510
18511
18512            if ($@) {
18513#             	mlog( $fh, "warning: SenderBase: $@", 1 ) if $SenderBaseLog >= 2;
18514
18515             	return 1;
18516            }
18517            SBCacheAdd( $ip, 0, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18518        } else {
18519            mlog( $fh, "info: SenderBase: got no results", 1 ) if $SenderBaseLog >= 2;
18520            return 1;
18521        }
18522
18523    } else {
18524
18525        return 1 if !SBCacheFind($ip);
18526
18527        ( $ipcountry, $orgname, $domainname, $blacklistscore, $hostname_matches_ip, $ipCIDR ) = split( /\|/, SBCacheFind($ip) ) ;
18528        $cache = 1;
18529        d("SenderBase: finished CACHE");
18530    }
18531	if ($ipcountry eq "noresult") {
18532
18533   	return 1;
18534   	}
18535
18536	$this->{myheader} .=
18537"X-Assp-SenderBase: $ipcountry; $orgname; $domainname\r\n"
18538                  if $AddSenderBaseHeader && ($ipcountry || $orgname || $domainname);
18539	mlog( $fh, "SenderBase info -- country:$ipcountry; organization:$orgname; domain:$domainname" )
18540              if $SenderBaseLog >= 2  && ($ipcountry || $orgname || $domainname);
18541
18542	$orgname =~ s/\(/-/go;
18543	$orgname =~ s/\)/-/go;
18544
18545	$this->{orgname} = $orgname;
18546
18547
18548    $this->{domainname} = $domainname;
18549
18550
18551
18552
18553    $tlit = "[whiting]"			if $DoOrgWhiting == 1;
18554    $tlit = "[scoring]"			if $DoOrgWhiting == 3;
18555    $tlit = "[monitoring]"		if $DoOrgWhiting == 2;
18556my $mDoOrgBlocking = $DoOrgBlocking;
18557    $this->{testmode} = 0;
18558	$this->{testmode} = 1	if $DoOrgBlocking == 4 or $allTestMode;
18559	$mDoOrgBlocking = 1 		if $DoOrgBlocking == 4;
18560
18561
18562
18563
18564
18565
18566
18567      if ($mDoOrgBlocking) {
18568
18569
18570        my $tlit = tlit($DoOrgBlocking);
18571
18572        if (!$orgname && !$ipcountry ) {
18573
18574            pbWhiteDelete( $fh, $ip, "NoCountryCode" );
18575            $this->{messagereason} = "No CountryCode and No Organization";
18576
18577            pbAdd( $fh, $ip,$sbnValencePB, "NoCountryNoOrg" );
18578
18579            mlog( $fh, "[scoring:$sbnValencePB] SenderBase -- $this->{messagereason}", 1 )
18580              if $SenderBaseLog >= 2;
18581            $this->{prepend} = "";
18582            return 1;
18583
18584        } elsif (   $orgname =~ /($blackSenderBaseRE)/
18585
18586            || $domainname =~ /($blackSenderBaseRE)/ )
18587        {
18588            my $bSB = $1;
18589
18590            pbWhiteDelete( $fh, $ip, "BlackOrg:$1" );
18591            $blacklistscore = pbBlackFind( $ip );
18592            SBCacheAdd( $ip, 1, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18593            d("SenderBase1: finished SBCacheAdd in DoOrgBlocking");
18594			$this->{messagereason} = "blackSenderBase '$1'";
18595			my $w= &weightRe($sborgValencePB,'blackSenderBase',$1,$fh);
18596			pbAdd( $fh, $ip, $w, "BlackOrg:$1" )
18597                if $mDoOrgBlocking != 2;
18598
18599            return 0 if $mDoOrgBlocking == 1;
18600            my $tlit; $tlit = "[scoring:$w]" if $DoOrgBlocking == 3;
18601            mlog( $fh, "$tlit SenderBase -- $this->{messagereason}", 1 )
18602              if $SenderBaseLog >= 1;
18603            return 1;
18604
18605        } elsif (   $OrgnamesCache{$this->{orgname}} && $OrgnamesCache{$this->{orgname}} <= 7)	{
18606        	my $w = $OrgnamesCache{$this->{orgname}} *  5 ;
18607        	$this->{messagereason} = "blackSenderBaseCache '$this->{orgname}'";
18608
18609            my $tlit; $tlit = "[scoring:$w]" if $DoOrgBlocking == 3;
18610
18611			pbAdd( $fh, $ip, $w, "BlackOrg:$this->{orgname}" )
18612                if $mDoOrgBlocking != 2;
18613
18614        	if ($OrgnamesCache{$this->{orgname}} == 7){
18615        	    $GPBmodTestList->('GUI','blackSenderBase','add',' - via BlackSenderBaseCache',$this->{orgname},0);
18616        	    SBCacheAdd( $ip, 1, "$ipcountry|$this->{orgname}|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18617        	    return 0 if $mDoOrgBlocking == 1;
18618        	    return 1;
18619        	}
18620
18621
18622            mlog( $fh, "$tlit SenderBase -- $this->{messagereason}", 1 )
18623              if $SenderBaseLog >= 1;
18624            return 1;
18625        }
18626
18627    }
18628
18629    if ($DoOrgWhiting) {
18630
18631    	$whiteSenderBaseRE =~ s/^\@//g;
18632        if (   $orgname =~ /($whiteSenderBaseRE)/
18633            || $domainname =~ /($whiteSenderBaseRE)/ )
18634        {
18635
18636        	my $wSB = $1;
18637        	d("SenderBase: in DoOrgWhiting");
18638			SBCacheAdd( $ip, 2, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18639            d("SenderBase0: finished SBCacheAdd in DoOrgWhiting");
18640			$this->{sbstatus} = 2 if (! $fh);
18641
18642            if ($DoOrgWhiting == 1) {
18643
18644
18645                $this->{passingreason} = "white-senderbase: $wSB";
18646                pbWhiteAdd( $fh, $ip, "WhiteSenderBase:$wSB" );
18647            }
18648            $this->{messagereason} = "whiteSenderBase '$wSB'";
18649            $this->{passingreason} = "whiteSenderBase '$wSB'"	if $DoOrgWhiting == 1;
18650            $this->{messagereason} .= " in cache " if $cache;
18651
18652            my $w= &weightRe($sworgValencePB,'whiteSenderBase',$wSB,$fh);
18653			$this->{whitelisted} = 1 if abs $sworgValencePB <= abs $w && $DoOrgWhiting == 1;
18654        	pbAdd( $fh, $ip, $w, "WhiteSenderBase:$1" )
18655              if $DoOrgWhiting != 2;
18656            $tlit = "[scoring:$w]" ;
18657            mlog( $fh, "$tlit SenderBase -- $this->{messagereason}" )
18658              if $SenderBaseLog;
18659            $this->{prepend} = "";
18660            return 1;
18661        }
18662     }
18663
18664
18665    my $mDoOrgBlocking = $DoOrgBlocking;
18666    $this->{testmode} = 0;
18667	$this->{testmode} = 1	if $DoOrgBlocking == 4 or $allTestMode;
18668	$mDoOrgBlocking = 1 		if $DoOrgBlocking == 4;
18669
18670
18671      my $slok = $this->{allLoveSpam} == 1;
18672
18673
18674      if ($mDoOrgBlocking) {
18675
18676
18677        my $tlit = tlit($DoOrgBlocking);
18678
18679        if (!$orgname && !$ipcountry ) {
18680            SBCacheAdd( $ip, 1, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18681            d("SenderBase1: finished SBCacheAdd in DoOrgBlocking");
18682            $this->{sbstatus} = 1 if (! $fh);
18683            $this->{messagereason} = "No CountryCode and No Organization";
18684
18685            pbAdd( $fh, $ip,$sbnValencePB, "NoCountryNoOrg" );
18686
18687            mlog( $fh, "[scoring:$sbnValencePB] SenderBase -- $this->{messagereason}", 1 )
18688              if $SenderBaseLog >= 2;
18689            $this->{prepend} = "";
18690            return 1;
18691
18692        } elsif (   $orgname =~ /($blackSenderBaseRE)/
18693            || $domainname =~ /($blackSenderBaseRE)/ )
18694        {
18695            my $bSB = $1;
18696
18697            pbWhiteDelete( $fh, $ip, "BlackOrg:$1" );
18698            $blacklistscore = pbBlackFind( $ip );
18699            SBCacheAdd( $ip, 1, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18700            d("SenderBase1: finished SBCacheAdd in DoOrgBlocking");
18701            $this->{sbstatus} = 1 if (! $fh);
18702			$this->{messagereason} = "blackSenderBase '$1'";
18703			my $w= &weightRe($sborgValencePB,'blackSenderBase',$1,$fh);
18704			pbAdd( $fh, $ip, $w, "BlackOrg:$1" )
18705                if $mDoOrgBlocking != 2;
18706
18707            return 0 if $mDoOrgBlocking == 1;
18708            my $tlit; $tlit = "[scoring:$w]" if $DoOrgBlocking == 3;
18709            mlog( $fh, "$tlit SenderBase -- $this->{messagereason}", 1 )
18710              if $SenderBaseLog >= 2;
18711        }
18712
18713    }
18714
18715   	my $mDoCountryBlocking = $DoCountryBlocking;
18716	$this->{testmode} = 0;
18717	$this->{testmode} = 1	if $DoCountryBlocking == 4 or $allTestMode;
18718	$mDoCountryBlocking = 1 if $DoCountryBlocking == 4;
18719
18720    $this->{mycountry} = 0;
18721    if (   $ipcountry
18722        && $MyCountryCodeReRE
18723        && $ipcountry =~ $MyCountryCodeReRE )
18724    {
18725
18726        return 1 if $sbhccValencePB >= 0;
18727        $this->{mycountry}     = 1;
18728        $this->{messagereason} = "Home Country $ipcountry";
18729        my $w= &weightRe($sbhccValencePB,'MyCountryCodeRe',$ipcountry,$fh);
18730        $tlit = "[scoring:$w]" if $DoSuspiciousCountry == 3;
18731        mlog( $fh, "$tlit -- $this->{messagereason} }", 1 ) if $SenderBaseLog;
18732
18733        pbAdd( $fh, $ip, $w, "HomeCountry-$ipcountry" )
18734          if $DoSuspiciousCountry != 2;
18735
18736
18737    }
18738
18739    return 1 if !$DoCountryBlocking;
18740    return 1 if $ipcountry =~ $NoCountryCodeReRE;
18741    $this->{mycountry} = 0;
18742
18743    if ($ipcountry &&
18744        (    $ipcountry =~ /$CountryCodeBlockedReRE/
18745         || (   $CountryCodeBlockedRe =~ /all/io
18746             && $ipcountry !~ /$MyCountryCodeReRE/
18747             && $ipcountry !~ /$CountryCodeReRE/
18748            )
18749        )
18750      )
18751    {
18752        $this->{messagereason} = "Blocked Country $ipcountry ($orgname)";
18753
18754
18755     	my $tlit = tlit($mDoCountryBlocking);
18756		my $w= &weightRe($bccValencePB,'CountryCodeBlockedRe',$ipcountry,$fh);
18757        pbAdd( $fh, $ip, $w, "BlockedCountry-$ipcountry" )
18758          if $mDoCountryBlocking != 2;
18759        $blacklistscore = pbBlackFind( $ip );
18760        SBCacheAdd( $ip, 1, "$ipcountry|$orgname|$domainname|$blacklistscore|$hostname_matches_ip|$ipCIDR" );
18761        d("SenderBase2: finished SBCacheAdd in DoOrgBlocking");
18762		$tlit = "[scoring:$w]" if $mDoCountryBlocking == 3;
18763        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}", 1 )
18764          if $mDoCountryBlocking == 2 || $mDoCountryBlocking == 3;
18765		$this->{prepend} = "";
18766        return 0 if $mDoCountryBlocking == 1;
18767
18768    }
18769
18770    return 1 if !$DoSuspiciousCountry;
18771
18772    $tlit = tlit($DoSuspiciousCountry);
18773
18774
18775
18776
18777    if (   $sbsccValencePB
18778        && $ipcountry
18779
18780        && $CountryCodeReRE
18781        && $ipcountry =~ $CountryCodeReRE
18782      )
18783    {
18784        $this->{messagereason} = "Suspicious Country $ipcountry ($orgname)";
18785
18786        my $w= &weightRe($sbsccValencePB,'CountryCodeRe',$ipcountry,$fh);
18787        pbAdd( $fh, $ip, $w, "CountryCode-$ipcountry", 1 )
18788          if $DoSuspiciousCountry != 2;
18789        $tlit = "[scoring:$w]" if $DoSuspiciousCountry == 3;
18790        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}", 1 ) if $SenderBaseLog;
18791
18792
18793    }
18794    if (   $sbfccValencePB
18795        && $ipcountry
18796        && $ScoreForeignCountries
18797
18798        && $ipcountry !~ /$MyCountryCodeReRE$CountryCodeReRE$MyCountryCodeReRE/
18799
18800        )
18801    {
18802        $this->{messagereason} = "Foreign Country $ipcountry($orgname)";
18803
18804
18805        my $w= &weightRe($sbfccValencePB,'CountryCodeRe',$ipcountry,$fh);
18806        pbAdd( $fh, $ip, $w, "CountryCode-$ipcountry", 1 )
18807          if $DoSuspiciousCountry != 2;
18808        $tlit = "[scoring:$w]" if $DoSuspiciousCountry == 3;
18809        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}", 1 ) if $SenderBaseLog >= 2;
18810		$this->{prepend} = "";
18811        return 1;
18812    }
18813
18814
18815
18816    $this->{prepend} = "";
18817    return 1;
18818}
18819
18820sub MXAOK {
18821    my $fh = shift;
18822    return 1 unless $CanUseDNS && $DoDomainCheck;
18823    return MXAOK_Run($fh);
18824}
18825sub MXAOK_Run {
18826    my $fh = shift;
18827    my $hasPrivat;
18828    d('MXAOK');
18829    my $this = $Con{$fh};
18830    my $ip = $this->{ip};
18831    return 1 if $this->{MXAOK};
18832
18833    $this->{MXAOK} = 1;
18834	return 1 if $ip =~ /$IPprivate/ && !$this->{cip};
18835
18836    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
18837
18838    return 1 if $this->{relayok};
18839    return 1 if $this->{notspamtag};
18840    return 1 if $this->{contentonly};
18841    return 1 if $this->{noprocessing};
18842    return 1 if !$this->{mailfrom};
18843#    return 1 if $this->{mailfrom} =~ /www|news|mail|noreply/io;
18844    return 1 if pbWhiteFind($ip);
18845
18846
18847    my $slok = $this->{allLoveSpam} == 1;
18848
18849    my $mf   = lc $this->{mailfrom};
18850    my %mfd;
18851    $mfd{lc $1}->{mx} = $mfd{lc $1}->{a} = $mfd{lc $1}->{ctime} = undef if $mf =~ /\@($EmailDomainRe)$/o;
18852
18853    while ($this->{header} =~ /($HeaderNameRe):($HeaderValueRe)/igos) {
18854        my $line = $2;
18855        next if $1 !~ /^(?:ReturnReceipt|Return-Receipt-To|Disposition-Notification-To|Return-Path|Reply-To|Sender|Errors-To|List-\w+)$/io;
18856        headerUnwrap($line);
18857        while ($line =~ /$EmailAdrRe\@($EmailDomainRe)/og) {
18858            $mfd{lc $1}->{mx} = $mfd{lc $1}->{a} = $mfd{lc $1}->{ctime} = undef;
18859        }
18860    }
18861
18862    my $mDoDomainCheck = $DoDomainCheck;
18863
18864	$this->{testmode} = 0;
18865	$this->{testmode} = 1	if $DoDomainCheck == 4 or $allTestMode;
18866	$mDoDomainCheck = 1 	if $DoDomainCheck == 4;
18867
18868    my $tlit;
18869    $tlit = &tlit($mDoDomainCheck);
18870    $this->{prepend} = '';
18871
18872
18873    foreach my $mfd (keys %mfd) {
18874        my ( $cachetime, $mxexchange, $arecord ) = MXACacheFind($mfd);
18875        if ( ! $cachetime ) {
18876            my $res = getDNSResolver();
18877            &sigoff(__LINE__);
18878            my @queryMX = eval{ Net::DNS::mx( $res, $mfd ); };
18879            &sigon(__LINE__);
18880            if (@queryMX) {
18881                foreach my $rr ( @queryMX ) {
18882                    my @MXip;
18883                    eval{$mxexchange = $rr->exchange;} or next;
18884                    &sigoff(__LINE__);
18885                    my $res = queryDNS($mxexchange ,'A');
18886                    &sigon(__LINE__);
18887                    if (ref $res) {
18888                        my @answer = eval{map{$_->string} $res->answer;};
18889                        while (@answer) {
18890                            my $RR = Net::DNS::RR->new(shift @answer);
18891                            my $aip = eval{$RR->rdatastr};
18892                            if ($aip !~ /$IPprivate/o) {
18893                                push @MXip, $aip;
18894                                $hasPrivat = 0;
18895                            } elsif ($hasPrivat != 0) {
18896                                $hasPrivat = 1;
18897                            }
18898                        }
18899                    }
18900                    if (!@MXip && $mxexchange && $res) {
18901                        mlog( $fh,"MX $mxexchange has no or a private IP - this MX has failed", 0)
18902                            if $ValidateSenderLog;
18903                        $mfd{$mfd}->{mx} = $mfd{$mfd}->{a} = $mfd{$mfd}->{ctime} = undef;
18904                    } elsif ($mxexchange && $res) {
18905                        $mfd{$mfd}->{mx} = $mxexchange;
18906                        $mfd{$mfd}->{a} = $MXip[0];
18907                        $mfd{$mfd}->{ctime} = undef;
18908                        last;
18909                    }
18910                }
18911            } else {
18912                delete $mfd{$mfd};
18913            }
18914        } else {
18915            $mfd{$mfd}->{mx} = $mxexchange;
18916            $mfd{$mfd}->{a} = $arecord;
18917            $mfd{$mfd}->{ctime} = $cachetime;
18918        }
18919    }
18920
18921
18922    my $mfailed;
18923    my $afailed;
18924    my $failed;
18925    my $mpb;
18926    my $apb;
18927    foreach my $mfd (keys %mfd) {
18928
18929        if ($mfd{$mfd}->{mx}) {
18930
18931            #MX found
18932            my $msg = "MX found";
18933            $msg .= " (cache)" if $mfd{$mfd}->{ctime};
18934            $msg .= ": $mfd -> ". $mfd{$mfd}->{mx};
18935            mlog( $fh, $msg, 1, 1 )
18936              if $ValidateSenderLog >= 2 ;
18937
18938        } else {
18939
18940            #MX not found
18941            $this->{prepend} = "[MissingMX]";
18942            $this->{messagereason} = "MX missing";
18943            $this->{messagereason} .= " (cache)" if $mfd{$mfd}->{ctime};
18944            $this->{messagereason} .= ": $mfd";
18945
18946            mlog( $fh,"[scoring] $this->{messagereason}", 0)
18947              if $ValidateSenderLog && $mxValencePB;
18948
18949            pbWhiteDelete( $fh, $ip ) if ! $mpb;
18950            pbAdd( $fh, $ip, $mxValencePB, 'MissingMX' ) if $DoDomainCheck != 2 && !$mpb;
18951            pbAdd( $fh, $ip, $mxValencePB, 'MissingMX' ) if $DoDomainCheck != 2 && !$mpb && $hasPrivat;
18952            if (! $mfd{$mfd}->{a} ) {
18953                my ($name, $aliases, $addrtype, $length, @addrs);
18954                eval{
18955                    ($name, $aliases, $addrtype, $length, @addrs) = gethostbyname($mfd);
18956                };
18957                foreach my $i (@addrs) {
18958                    my ($ad, $bd, $cd, $dd) = unpack('C4', $i);
18959                    my $arecord ="$ad.$bd.$cd.$dd";
18960                    if ( $MXACacheInterval > 0 && $arecord =~ /^$IPRe$/o && $arecord !~ /^$IPprivate$/o) {
18961                        $mfd{$mfd}->{a} = $arecord;
18962                        last;
18963                    }
18964                }
18965            }
18966            $mfailed = 1;
18967            $this->{prepend} = '';
18968        }
18969
18970        if ($mfd{$mfd}->{a}) {
18971
18972            #A  found
18973            my $msg = "A record found";
18974            $msg .= " (cache)" if $mfd{$mfd}->{ctime};
18975            $msg .= ": $mfd -> ".$mfd{$mfd}->{a};
18976            mlog( $fh, $msg, 1, 1 )	if $ValidateSenderLog >= 2 ;
18977
18978        } else {
18979
18980            #A not found
18981            $this->{prepend} = "[MissingMXA]";
18982
18983            $this->{messagereason} = "A record missing: $mfd";
18984            $this->{messagereason} .= " (cache)" if $mfd{$mfd}->{ctime};
18985
18986            mlog( $fh,"[scoring] $this->{messagereason}")
18987              if $ValidateSenderLog && $DoDomainCheck >= 2;
18988
18989            delayWhiteExpire($fh) if ! $apb;
18990            pbAdd( $fh, $ip, $mxaValencePB, 'MissingMXA' ) if $DoDomainCheck != 2 && ! $apb;
18991            pbAdd( $fh, $ip, $mxaValencePB, 'MissingMXA' ) if $DoDomainCheck != 2 && ! $apb && $hasPrivat;
18992            $this->{prepend} = '';
18993            $afailed = 1;
18994        }
18995        if ( $MXACacheInterval > 0) {
18996            MXACacheAdd( $mfd, $mfd{$mfd}->{mx}, $mfd{$mfd}->{a} ) if ! $mfd{$mfd}->{ctime};
18997        }
18998        $failed = $mfailed && $afailed;
18999        $mf = $mfd if ($mfailed && $afailed);
19000        $apb |= $afailed;
19001        $mpb |= $mfailed;
19002        $mfailed = $afailed = undef;
19003    }
19004
19005    if ($failed) {
19006        return 1 if $DoDomainCheck >= 2;
19007        $this->{prepend}="[MissingMXA]";
19008        mlog($fh,"MX and A record missing at least for: $mf")
19009          if $ValidateSenderLog;
19010        return 0;
19011    } else {
19012        $this->{prepend}='';
19013        return 1;
19014    }
19015}
19016
19017
19018
19019sub BombWeight {
19020    my ( $fh, $t, $re ) = @_;
19021    my %weight = ();
19022    mlog( 0,
19023"error: code error - missing valence value in 'WeightedRe' hash in sub BombWeight for $re"
19024    ) if ( !exists $WeightedRe{$re} );
19025    mlog( 0,
19026"warning: suspect valence value '0' in 'WeightedRe' hash for '$WeightedRe{$re}' in sub BombWeight for $re"
19027    ) if $BombLog >= 2 && ${ $WeightedRe{$re} }[0] == 0;
19028    return %weight unless ${$re};
19029    return %weight unless ${ $re . 'RE' };
19030    return BombWeight_Run( $fh, $t, $re );
19031}
19032
19033sub BombWeight_Run {
19034    my ( $fh, $t, $re ) = @_;
19035    my $this = $Con{$fh};
19036    d("BombWeight - $re");
19037    my $text;
19038    my $rawtext = ref $t ? $$t : $t;
19039
19040    #    my $match = &SearchBomb($re, $rawtext, 1);
19041    #    mlog(0,"$re,match=$match, $rawtext") if $re eq "bombSuspiciousRe";
19042    #    return if !$match;
19043
19044    my %weight      = ();
19045    my %found       = ();
19046    my $weightsum   = 0;
19047    my $weightcount = 0;
19048    my $maxhits     = $maxHits{ lc $re };
19049
19050	$maxhits |= 3 if $re eq 'bombSubjectRe';
19051	$maxhits |= 3 if $re eq 'blackRe';
19052	$maxhits |= 3 if $re eq 'bombSuspiciousRe';
19053	$maxhits |= 3 if $re eq 'bombRe';
19054    $maxhits |= 1;
19055    $maxBombSearchTime = 10 unless $maxBombSearchTime;
19056    $weightMatch = '';
19057    my $regex = ${ $re . 'RE' };
19058    my $itime = time;
19059    $addCharsets = 1 if $re eq 'bombCharSets';
19060    if ( $re ne 'bombSubjectRe' ) {
19061        $rawtext =~ s/(<!--.+?)-->/$1/sgo;
19062        my $mimetext = cleanMIMEBody2UTF8( \$rawtext );
19063
19064        if ($mimetext) {
19065            if ( $re ne 'bombDataRe' ) {
19066                $text = cleanMIMEHeader2UTF8( \$rawtext, 0 );
19067                $mimetext =~ s/\=(?:\015?\012|\015)//go;
19068                $mimetext = decHTMLent( \$mimetext );
19069            }
19070            $text .= $mimetext;
19071        }
19072        else {
19073            $text = decodeMimeWords2UTF8($rawtext);
19074        }
19075
19076    }
19077    else {
19078        $text = $rawtext;
19079    }
19080
19081    undef $rawtext;
19082    $addCharsets = 0;
19083    if ( $re eq 'bombSubjectRe' && $maxSubjectLength ) {
19084        my ( $submaxlength, $maxlengthweight ) =
19085          split( /\s*\=\>\s*/o, $maxSubjectLength );
19086
19087        $maxlengthweight |= ${ $WeightedRe{$re} }[0];
19088
19089        my $sublength = length($text);
19090        if ( $submaxlength && $sublength > $submaxlength ) {
19091            if ($maxlengthweight) {
19092                $weightsum += $maxlengthweight;
19093                $weightcount++;
19094                $weight{highval} = $maxlengthweight;
19095                $weight{highnam} =
19096                  "subject length($sublength) > max($submaxlength)";
19097                $found{ $weight{highnam} } = $maxlengthweight;
19098                $weight{matchlength} = '';
19099            }
19100            $text = substr( $text, 0, $submaxlength );
19101            mlog( $fh,
19102"info: Subject exceeds $maxSubjectLength byte - the checked subject is truncated to $submaxlength byte"
19103            ) if $BombLog && $fh;
19104        }
19105    }
19106    my $subre;
19107
19108    eval {
19109        local $SIG{ALRM} = sub { die "__alarm__\n" };
19110        alarm( $maxBombSearchTime + 10 );
19111        while ( $text =~ /($regex)/gs ) {
19112            $subre = $1 || $2;
19113            my $matchlength = length($subre);
19114
19115            last if time - $itime >= $maxBombSearchTime;
19116            my $w = &weightRe( $WeightedRe{$re}, $re, \$subre, $fh );
19117            next unless $w;
19118            $subre = substr( $subre, 0, $RegExLength < 5 ? 5 : $RegExLength )
19119              if $subre;
19120            $subre = '[!empty!]' unless $subre;
19121
19122            $subre =~ s/\s+/ /go;
19123            next if ( $found{ lc($subre) } > 0 && $found{ lc($subre) } >= $w );
19124            next if ( $found{ lc($subre) } < 0 && $found{ lc($subre) } <= $w );
19125            $found{ lc($subre) } = $w;
19126            $weightsum += $w;
19127            $weightcount++;
19128
19129            if ( abs($w) >= abs( $weight{highval} ) ) {
19130                $weight{highval} = $w;
19131                $subre =~ s{([\x00-\x1F])}{sprintf("'hex %02X'", ord($1))}eog;
19132                $weight{highnam} = $subre;
19133                $weight{matchlength} =
19134                  ( length($subre) != $matchlength )
19135                  ? "(matchlength:$matchlength) "
19136                  : '';
19137            }
19138
19139            last if $fh && $maxBombValence && $weightsum >= $maxBombValence;
19140            last if $fh && !$maxhits;
19141            last
19142              if $fh
19143              && $maxhits
19144              && $weightcount >= $maxhits
19145              && !$maxBombValence;
19146            last
19147              if $fh
19148              && $weightsum >= $MessageScoringUpperLimit
19149              && !$maxBombValence;
19150
19151        }
19152        alarm(0);
19153    };
19154    undef $text;
19155    $itime = time - $itime;
19156    if ($@) {
19157        alarm(0);
19158        if ( $@ =~ /__alarm__/ ) {
19159            mlog(
19160                $fh,
19161                "BombWeight: timed out in 'RE:$re ($subre)' after $itime secs.",
19162                1
19163            );
19164        }
19165        else {
19166            mlog( $fh, "BombWeight: failed in 'RE:$re': $@", 1 );
19167        }
19168    }
19169    if ( $itime > $maxBombSearchTime ) {
19170        mlog(
19171            $fh,
19172"info: $re canceled after $itime s > maxBombSearchTime $maxBombSearchTime s",
19173            1
19174        ) if $BombLog >= 2 && $fh;
19175    }
19176
19177    return %weight if $weightcount == 0;
19178    if ( $maxBombValence > 0 ) {
19179        $weight{sum} =
19180          $weightsum > $maxBombValence ? $maxBombValence : $weightsum;
19181    }
19182    else {
19183        $weight{sum} = $weightsum;
19184    }
19185
19186    $weight{count} = $weightcount;
19187
19188#    mlogRe($fh,"PB $weight{sum}: for $weight{highnam}",ucfirst $re) if $BombLog && $fh;
19189    mlog(
19190        $fh,
19191"$weight{highnam} : $weight{highval} , count : $weightcount , sum : $weightsum , time : $itime s",
19192        1
19193    ) if $BombLog >= 2 && $fh;
19194    $weight{highnam} = join ' , ', map {
19195        my $t = "'"
19196          . substr( $_, 0, $RegExLength < 5 ? 5 : $RegExLength )
19197          . " ($found{$_})'"; $t;
19198    } ( sort { $found{$main::b} <=> $found{$main::a} } keys %found );
19199    $this->{match} = $weight{highnam};
19200    $weight{highnam} =~ s/ \(-?\d+\)//g if $weight{sum} == $bombMaxPenaltyVal;
19201    return %weight;
19202}
19203
19204sub WhiteOk {
19205
19206    my ( $fh, $msg ) = @_;
19207    my $this = $Con{$fh};
19208    return   if $this->{whiteokdone};
19209    return 1 if $this->{relayok};
19210    return 1 if $this->{whitelisted};
19211
19212    return 1 if $this->{addressedToSpamBucket};
19213    my $m;
19214    if ($msg) {
19215        $m = ref($msg) ? $$msg : $msg if $msg;
19216    }
19217    else {
19218
19219        $m = "$this->{mailfrom} $this->{helo} $this->{ip} $this->{header}";
19220    }
19221    d('WhiteOk');
19222
19223    my $ip = $this->{ip};
19224    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19225
19226    if ( $whiteReRE && $m =~ /($whiteReRE)/i ) {
19227        my $subre = ( $1 || $2 );
19228        $this->{prepend} = '[whitelisted]';
19229
19230        $this->{whitelisted}   = "whiteRe '$subre'";
19231        $this->{passingreason} = "whiteRe '$subre'";
19232
19233        delete $PBBlack{$ip};
19234        pbWhiteAdd( $fh, $ip, "whiteRe: $subre" );
19235
19236        $this->{prepend} = "[WhiteRe]";
19237
19238        return 1;
19239
19240    }
19241
19242    return 0;
19243
19244}
19245
19246
19247sub WhiteOk {
19248
19249    my ( $fh, $msg) = @_;
19250    my $this=$Con{$fh};
19251	return if $this->{whiteokdone};
19252	return 1 if $this->{relayok};
19253	return 1 if $this->{whitelisted};
19254
19255	return 1 if $this->{addressedToSpamBucket};
19256	my $m;
19257	if ($msg) {
19258		$m = ref($msg) ? $$msg : $msg if $msg;
19259	} else {
19260
19261		$m = "$this->{mailfrom} $this->{helo} $this->{ip} $this->{header}";
19262	}
19263    d('WhiteOk');
19264
19265	my $ip = $this->{ip};
19266    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19267
19268   if ( $whiteReRE &&  $m =~ /($whiteReRE)/i) {
19269        my $subre = ($1||$2);
19270        $this->{prepend} = '[whitelisted]';
19271
19272        $this->{whitelisted} = "whiteRe '$subre'";
19273        $this->{passingreason} = "whiteRe '$subre'";
19274
19275		delete $PBBlack{$ip};
19276		pbWhiteAdd( $fh, $ip, "whiteRe: $subre" );
19277
19278        $this->{prepend}="[WhiteRe]";
19279
19280		return 1;
19281
19282	}
19283
19284    return 0;
19285
19286}
19287
19288
19289sub BombOK {
19290    my($fh,$dataref)=@_;
19291    my $this=$Con{$fh};
19292    return 1 if $this->{notspamtag};
19293	return 1 if $this->{addressedToSpamBucket};
19294	return 1 if $this->{bombdone} == 1;
19295    d('BombOK');
19296
19297 	$this->{bombdone}=1;
19298    my $ip = $this->{ip};
19299    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19300    my $helo = $this->{helo};
19301    $helo = $this->{ciphelo} if $this->{ciphelo};
19302    my $tlit;
19303    my %Bombs = ();
19304    my $BombName;
19305    my $subre;
19306    my $header = $dataref;
19307
19308    my $datastart = $this->{datastart};
19309    my $maillength = length($$dataref);
19310    my $ofs = 0;
19311    my $data = " $this->{mailfrom} $helo $ip " . substr( $this->{header}, 0, 10000 );
19312
19313    $this->{match}="";
19314
19315
19316
19317
19318    if ($noBombScript && $this->{mailfrom} && matchSL($this->{mailfrom},'noBombScript') ) {
19319        return 1;}
19320
19321	return 1 if $this->{nobombscript};
19322
19323  	return 1 if $this->{acceptall};
19324  	return 1 if $this->{whitelisted};
19325  	return 1 if $this->{noprocessing};
19326  	return 1 if $this->{relayok};
19327
19328    my $mDoBombRe = $DoBombRe;
19329    $this->{testmode} = 0;
19330	$this->{testmode} = 1	if $DoBombRe == 4 or $allTestMode;
19331	$mDoBombRe = 1 	if $DoBombRe == 4;
19332
19333
19334    $this->{match}="";
19335    if ($bombSuspiciousRe) {
19336
19337    	%Bombs = &BombWeight($fh,$data,'bombSuspiciousRe' );
19338
19339    	if ($Bombs{count}) {
19340        	$subre = $Bombs{highnam};
19341        	$this->{messagereason} = "bombSuspiciousRe: $subre";
19342        	$this->{prepend}="[Blackish]" if $Bombs{sum} >= 0;
19343        	$this->{prepend}="[Whitish]" if $Bombs{sum} < 0;
19344			mlog( $fh, "[scoring:$Bombs{sum}] -- $this->{messagereason} " );
19345        	pbAdd($fh,$ip,$Bombs{sum},'bombSuspicious');
19346
19347    	}
19348    }
19349      # bombCharSets in MIME parts
19350    if ($DoBombRe && $bombCharSetsMIME && !$this->{charsetsdone}) {
19351
19352		$subre = $Bombs{highnam};
19353    	%Bombs = &BombWeight($fh, $header,'bombCharSetsMIME' );
19354    	if ($Bombs{count}) {
19355        	$subre = $Bombs{highnam};
19356
19357        	$this->{messagereason}="bombCharSets in mime-header: $subre";
19358        	$this->{prepend}="[BombCharSets]";
19359
19360        	$tlit = ($mDoBombRe == 1 && $Bombs{sum} < $bombValencePB) ? &tlit(3) : $tlit;
19361        	$tlit = "[scoring:$Bombs{sum}]" if $DoBombRe==3;
19362        	mlog($fh,"$tlit -- $this->{messagereason}") if $DoBombRe > 1;
19363        	pbWhiteDelete($fh,$this->{ip});
19364        	$this->{charsetsdone}=1;
19365        	return 1 if $mDoBombRe==2;
19366        	pbWhiteDelete($fh,$this->{ip});
19367        	$this->{isbomb}=1 if abs $Bombs{sum} >= abs $bombValencePB;
19368        	pbAdd($fh,$this->{ip},$Bombs{sum},"BombCharSetsMIME");
19369        	$this->{charsetsdone}=1;
19370 			if  ($this->{messagescore} > $this->{spamMaxScore}){
19371        		return 0 if $mDoBombRe==1 && $Bombs{sum} >= $bombValencePB;
19372        		return 0 if $mDoBombRe == 1 &&  $Bombs{count} >= $maxBombHits-1;
19373        	}
19374    	}
19375    }
19376
19377    if ($DoBombRe) {
19378    	my $slok=$this->{allLoveSpam}==1;
19379
19380    	my $mDoBombRe = $DoBombRe;
19381    	$this->{testmode} = 0;
19382		$this->{testmode} = 1	if $DoBombRe == 4 or $allTestMode;
19383		$mDoBombRe = 1 	if $DoBombRe == 4;
19384		$this->{prepend}="[BombData]";
19385    	$tlit=&tlit($mDoBombRe);
19386
19387
19388    	%Bombs = &BombWeight($fh,$header,'bombDataRe' );
19389      	if ($Bombs{count}) {
19390        	$subre = $Bombs{highnam};
19391
19392        	$this->{messagereason} = "bombDataRe: $subre";
19393        	$tlit = ($mDoBombRe == 1 && $Bombs{sum} < $bombValencePB) ? &tlit(3) : $tlit;
19394        	$tlit = "[scoring:$Bombs{sum}]" if $mDoBombRe == 3;
19395        	mlog( $fh, "$tlit -- $this->{messagereason}" ) if $mDoBombRe > 1;
19396        	pbWhiteDelete($fh,$this->{ip});
19397        	return 1 if $mDoBombRe==2;
19398        	pbWhiteDelete($fh,$this->{ip});
19399        	$this->{isbomb}=1 if abs $Bombs{sum} >= abs $bombValencePB;
19400        	pbAdd($fh,$this->{ip},$Bombs{sum},"BombData");
19401        	if  ($this->{messagescore} > $this->{spamMaxScore}){
19402        		return 0 if $mDoBombRe == 1 && $Bombs{sum} >= $bombValencePB;
19403        		return 0 if $mDoBombRe == 1 && $maxBombHits >= 1 && $Bombs{count} >= $maxBombHits-1;
19404        	}
19405	  	}
19406    }
19407
19408	return 1 if !$DoBombRe;
19409	return 1 if !$bombRe;
19410	return 1 if !$bombValencePB;
19411
19412	$this->{match}="";
19413    $this->{prepend}="[BombRe]";
19414    %Bombs = &BombWeight($fh,\$data,'bombRe' );
19415    if ($Bombs{count}) {
19416        $subre = $Bombs{highnam};
19417
19418        $this->{messagereason} = "bombRe: $subre";
19419		$tlit = ($mDoBombRe == 1 && $Bombs{sum} < $bombValencePB) ? &tlit(3) : $tlit;
19420		$tlit = "[scoring:$Bombs{sum}]" if $mDoBombRe == 3;
19421        mlog( $fh, "$tlit -- $this->{messagereason}" ) if $mDoBombRe > 1;
19422        pbWhiteDelete($fh,$this->{ip});
19423        return 1 if $mDoBombRe==2;
19424        pbWhiteDelete($fh,$this->{ip});
19425        $this->{isbomb}=1 if $Bombs{sum} >= $bombValencePB;
19426        $this->{newsletterre} = "" if $Bombs{sum} >= $bombValencePB/2;
19427        pbAdd($fh,$this->{ip},$Bombs{sum},"BombRe") if $mDoBombRe;
19428 		if  ($this->{messagescore} > $this->{spamMaxScore}){
19429        	return 0 if $mDoBombRe == 1 && $Bombs{sum} >= $bombValencePB;
19430        	return 0 if $mDoBombRe == 1 && $maxBombHits >= 1 && $Bombs{count} >= $maxBombHits-1;
19431		}
19432    }
19433
19434    return 1;
19435}
19436
19437sub BombHeaderOK {
19438    my ($fh,$headerref) = @_;
19439    my $this=$Con{$fh};
19440	return 1 if $this->{notspamtag};
19441    return 1 if $this->{addressedToSpamBucket};
19442	return 1 if $this->{bombheaderdone};
19443	d('BombHeaderOK');
19444	$this->{bombheaderdone}=1;
19445    my $ip = $this->{ip};
19446    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19447    my $helo = $this->{helo};
19448    $helo = $this->{ciphelo} if $this->{ispip} && $this->{ciphelo};
19449    my %Bombs;
19450    my $BombName;
19451    my $tlit;
19452
19453
19454  	return 1 if $this->{notspamtag};
19455  	return 1 if $this->{acceptall};
19456  	return 1 if $this->{whitelisted};
19457  	return 1 if $this->{noprocessing};
19458  	return 1 if $this->{relayok};
19459
19460
19461
19462
19463    if ($noBombScript && $this->{mailfrom} && matchSL($this->{mailfrom},'noBombScript')) {
19464        return 1;}
19465    my $slok=$this->{allLoveSpam}==1;
19466    my $mDoBombHeaderRe = $DoBombHeaderRe;
19467    $this->{testmode} = 0;
19468	$this->{testmode} = 1	if $DoBombHeaderRe == 4 or $allTestMode;
19469	$mDoBombHeaderRe = 1 	if $DoBombHeaderRe == 4;
19470
19471    my $subre;
19472    my $w;
19473    my $isheaderbomb;
19474
19475    $tlit=&tlit($mDoBombHeaderRe);
19476
19477
19478
19479	%Bombs = &BombWeight($fh,"$this->{mailfrom} $ip $helo",'bombSenderRe' ) if $DoBombHeaderRe &&  $bombSenderRe;
19480    if ($Bombs{count}) {
19481        	$subre = $Bombs{highnam};
19482    		$this->{prepend}="[BombSender]";
19483    		$this->{messagereason} = "bombSenderRe: $subre";
19484			$this->{newsletterre}		= '';
19485        	pbWhiteDelete($fh,$ip);
19486			pbAdd($fh,$ip,$Bombs{sum},"BombSender") if $DoBombHeaderRe != 2;
19487			$this->{isbomb} = 1 if $Bombs{sum} >= $bombValencePB;
19488        	return 0 if $mDoBombHeaderRe == 1 && $Bombs{sum} >= $bombValencePB && $this->{messagescore} > $this->{spamMaxScore};
19489        	$tlit = "[scoring:$Bombs{sum}]" if $DoBombHeaderRe == 3;
19490        	mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" ) ;
19491
19492    }
19493
19494
19495    %Bombs = &BombWeight($fh, $headerref,'bombCharSets' ) if $DoBombHeaderRe && !$this->{charsetsdone};
19496    if ($Bombs{count} && $DoBombHeaderRe) {
19497    	$this->{newsletterre}		= '';
19498		my $mDoBombHeaderRe = $DoBombHeaderRe;
19499    	$this->{testmode} = 0;
19500		$this->{testmode} = 1	if $DoBombHeaderRe == 4 or $allTestMode;
19501		$mDoBombHeaderRe = 1 	if $DoBombHeaderRe == 4;
19502        $subre = $Bombs{highnam};
19503        $this->{messagereason}="bombCharSets: $subre";
19504
19505        $this->{prepend}="[BombCharSets]";
19506
19507        $tlit = ($mDoBombHeaderRe == 1 && $Bombs{sum} < $bombValencePB) ? &tlit(3) : $tlit;
19508        $tlit = "[scoring:$Bombs{sum}]" if  $DoBombHeaderRe == 3;
19509
19510        pbWhiteDelete($fh,$this->{ip});
19511        $this->{charsetsdone}=1;
19512
19513        pbWhiteDelete($fh,$this->{ip}) if $mDoBombHeaderRe !=2;
19514
19515        $this->{isbomb}=1 if abs $Bombs{sum} >= abs $bombValencePB;
19516        pbAdd($fh,$this->{ip},$Bombs{sum},"BombCharSets") if $mDoBombHeaderRe && $mDoBombHeaderRe !=2;
19517        return 0 if $mDoBombHeaderRe !=2 && $mDoBombHeaderRe==1 && abs $Bombs{sum} >= abs $bombValencePB && $this->{messagescore} > $this->{spamMaxScore};
19518		mlog($fh,"$tlit -- $this->{messagereason}") if $BombLog;
19519
19520    }
19521
19522
19523;
19524
19525    if ( $DoBombHeaderRe &&  $bombSubjectRe ) {
19526		$tlit=&tlit($DoBombHeaderRe);
19527
19528		%Bombs = &BombWeight($fh,substr($this->{subject3},0,160),'bombSubjectRe' );
19529
19530        if ($Bombs{count}) {
19531        	$subre = $Bombs{highnam};
19532    		$this->{prepend}="[BombSubject]";
19533    		$this->{messagereason} = "bombSubjectRe: $subre";
19534
19535        	pbWhiteDelete($fh,$ip);
19536			pbAdd($fh,$ip,$Bombs{sum},"BombSubject") if $DoBombHeaderRe != 2;
19537
19538			$this->{isbomb} = 1 if $Bombs{sum} >= $bombValencePB;
19539        	return 0 if $mDoBombHeaderRe == 1 && $Bombs{sum} >= $bombValencePB && $this->{messagescore} > $this->{spamMaxScore};
19540        	$tlit = "[scoring:$Bombs{sum}]" if $DoBombHeaderRe == 3;
19541        	mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" ) if $DoBombHeaderRe > 1;
19542
19543    	}
19544
19545    }
19546
19547
19548
19549    if ( $DoBombHeaderRe &&  $bombHeaderRe ) {
19550
19551   		 %Bombs  = &BombWeight($fh,$headerref,'bombHeaderRe' );
19552
19553    	if ($Bombs{count}) {
19554    		$this->{prepend}="[BombHeader]";
19555    		$subre = $Bombs{highnam};
19556    		$this->{messagereason} = "bombHeaderRe: $subre";
19557
19558        	pbWhiteDelete($fh,$ip) if $mDoBombHeaderRe!=2;
19559        	pbAdd($fh,$ip,$Bombs{sum},"BombHeader") if $mDoBombHeaderRe!=2;
19560      		$this->{isbomb}=1 if $Bombs{sum} >= $bombValencePB;
19561			$this->{newsletterre} = "" if $Bombs{sum};
19562			return 0 if $mDoBombHeaderRe == 1 && $Bombs{sum} >= $bombValencePB && $this->{messagescore} > $this->{spamMaxScore};
19563			$tlit = "[scoring:$Bombs{sum}]" if $mDoBombHeaderRe == 3;
19564        	mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" );
19565        	return 1;
19566    		}
19567  	}
19568    return 1;
19569}
19570
19571#
19572#
19573#
19574
19575sub BombBlackOK {
19576  	my($fh,$headerref)=@_;
19577  	my $this=$Con{$fh};
19578
19579	return 1 if !$DoBlackRe;
19580	return 1 if !$blackRe;
19581	return 1 if $this->{addressedToSpamBucket};
19582	return 1 if $this->{notspamtag};
19583
19584    return 1 if $this->{blackdone} == 1;
19585
19586    d('BlackOK');
19587 	$this->{blackdone}=1;
19588    my $ip = $this->{ip};
19589  	$ip = $this->{cip} if  $this->{cip};
19590  	my $helo = $this->{helo};
19591    $helo = $this->{ciphelo} if  $this->{ciphelo};
19592	my $data = " $this->{mailfrom} $helo $ip " . substr( $this->{header}, 0, 10000 );
19593
19594	my $dataref = \$data;
19595
19596    my $subre;
19597	my %Bombs;
19598
19599    if (   $noBombScript
19600        && $this->{mailfrom}
19601        && matchSL( $this->{mailfrom}, 'noBombScript' ) )
19602    {
19603        return 1;
19604    }
19605
19606    return 1 if $this->{whitelisted} && !$blackReWL;
19607    return 1 if $this->{noprocessing} && !$blackReNP;
19608
19609    return 1 if $this->{relayok} && !$blackReLocal;
19610    return 1 if $this->{ispip} && !$blackReISPIP;
19611
19612    my $slok       = $this->{allLoveSpam} == 1;
19613
19614
19615  	my $mDoBlackRe = $DoBlackRe;
19616    $this->{testmode} = 0;
19617	$this->{testmode} = 1	if $DoBlackRe == 4 or $allTestMode;
19618	$mDoBlackRe = 1 	if $DoBlackRe == 4;
19619
19620    my $tlit = tlit($mDoBlackRe);
19621
19622  	%Bombs = &BombWeight($fh,\$data,'blackRe' );
19623
19624  	if ($Bombs{count}) {
19625		$subre = $Bombs{highnam};
19626
19627    	$this->{messagereason} = "blackRe: $subre";
19628    	$this->{prepend} ="[BombBlack]";
19629    	$this->{messagereason} = "blackRe: $subre"  if $Bombs{sum}<0;
19630    	$this->{prepend} ="[White]" if $Bombs{sum}<0;
19631
19632    	pbWhiteDelete($fh,$ip) if  $blackRe;
19633    	pbAdd($fh,$ip,$Bombs{sum},"$this->{messagereason}") if  $mDoBlackRe !=2;
19634
19635		return 0 if $mDoBlackRe == 1 && $Bombs{sum} >= ${'blackValencePB'}[0];
19636		return 0 if $mDoBlackRe == 1 && $maxBombHits > 1 && $Bombs{count} >= $maxBombHits;
19637		$tlit = "[scoring:$Bombs{sum}]" if  $mDoBlackRe !=2;
19638    	mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" );
19639    	return 1;
19640  	}
19641
19642  	return 1;
19643
19644}
19645
19646
19647# do invalid HELO check
19648sub invalidHeloOK {
19649    my ( $fh, $helo ) = @_;
19650    my $this = $Con{$fh};
19651    d('invalidHeloOK');
19652	return 1 if $this->{invalidHeloOK};
19653    return 1 if $this->{addressedToSpamBucket};
19654 	return 1 if $this->{notspamtag};
19655    return 1 if $this->{ispip} && !$this->{cip};
19656    my $ip = $this->{ip};
19657    $ip = $this->{cip} if $this->{cip};
19658
19659	$helo = $this->{ciphelo} if $this->{ciphelo};
19660
19661    return 1 if !$DoInvalidFormatHelo;
19662    return 1 if $this->{relayok};
19663    return 1 if $heloBlacklistIgnore && $helo =~ $HBIRE;
19664
19665    return 1 if $this->{nohelo};
19666    return 1 if $this->{acceptall};
19667	return 1 if $this->{ispip} && ! $this->{cip};
19668    return 1 if (($this->{rwlok} && ! $this->{cip}) or ($this->{cip} && pbWhiteFind($this->{cip})));
19669
19670    #return 1 if $this->{contentonly};
19671    return 1 if $this->{whitelisted};
19672    return 1 if $this->{noprocessing};
19673    return 1 if $helo =~ /\[?$IPRe\]?/oi;
19674    my $slok	= $this->{allLoveSpam} == 1;
19675
19676    my $mDoInvalidFormatHelo = $DoInvalidFormatHelo;
19677    my %HELOs = &BombWeight($fh,$helo,'invalidHeloRe' );
19678    if (   $DoInvalidFormatHelo
19679        && $invalidHeloRe
19680        && $HELOs{count} )
19681    {
19682
19683        my $tlit = tlit($mDoInvalidFormatHelo);
19684        $this->{prepend} = "[InvalidHELO]";
19685
19686        $this->{messagereason} = "invalid HELO: '$helo'";
19687        my $w = $HELOs{sum};
19688        $mDoInvalidFormatHelo = 3 if $mDoInvalidFormatHelo == 1 && $w < $ihValencePB;
19689        $tlit = "[scoring:$w]" if $mDoInvalidFormatHelo == 3;
19690        mlog( $fh, "$tlit -- $this->{messagereason}" )
19691          if $ValidateHeloLog && $mDoInvalidFormatHelo == 3
19692              || $mDoInvalidFormatHelo == 2;
19693        pbWhiteDelete( $ip );
19694        return 1 if $mDoInvalidFormatHelo == 2;
19695
19696
19697   		pbAdd($fh,$ip,$w,"invalidHELO") ;
19698   		$this->{blackhelodone} = 1;
19699
19700        $this->{invalidHeloOK} = 1;
19701
19702        return 1 if $mDoInvalidFormatHelo == 3 or $w < $ihValencePB;
19703        return 1 if $validHeloReRE && $helo =~ /($validHeloReRE)/i;
19704        return 1 if $this->{messagescore} <= $this->{spamMaxScore};
19705        return 0;
19706    }
19707    return 1;
19708}
19709sub IPinHeloOK {
19710    my $fh = shift;
19711    return 1 if !$DoIPinHelo;
19712    return IPinHeloOK_Run($fh);
19713}
19714sub IPinHeloOK_Run {
19715    my $fh = shift;
19716    my $this = $Con{$fh};
19717    $fh = 0 if "$fh" =~ /^\d+$/o;
19718    my $ip = $this->{ip};
19719    my $helo = $this->{helo};
19720
19721    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19722    $helo = $this->{ciphelo} if $this->{ciphelo};
19723    d('IPinHeloOK');
19724
19725    return 1 if $this->{IPinHeloOK};
19726    $this->{IPinHeloOK} = 1;
19727    return 1 if $helo eq $ip;
19728    skipCheck($this,'ro','aa','ispcip') && return 1;
19729    return 1 if $DoFakedWL && $this->{whitelisted};
19730	return 1 if $DoFakedNP && $this->{noprocessing};
19731    return 1 if ( matchIP( $ip, 'noHelo', $fh ) );
19732
19733    return 1 if $ip =~ /$IPprivate/o;
19734    return 1 if $heloBlacklistIgnore && $helo =~ /$HBIRE/;
19735
19736
19737    my $tlit = tlit($DoIPinHelo);
19738    my @variants;
19739
19740    if ( $helo =~ /\[?(?:(?:$IPSectRe(?:\.|\-)){3}$IPSectRe|(?:$IPSectHexRe(?:\.|\-)){3}$IPSectHexRe|$IPv6LikeRe)\]?/o ) {
19741        pos($helo) = 0;
19742        while ($helo =~ /\[?((?:$IPSectRe(?:\.|\-)){3}$IPSectRe|(?:$IPSectHexRe(?:\.|\-)){3}$IPSectHexRe|($IPv6LikeRe))\]?/og) {
19743            my $literal = $1;
19744            my $isV6 = $2;
19745            my $sep;
19746            # replace any - characters with a dot or :
19747            if ($isV6) {
19748                $literal =~ s/\-/\:/go;
19749                $literal = ipv6expand($literal);
19750                $sep = ':';
19751            } else {
19752                $literal =~ s/\-/\./go;
19753                $literal =~ s/0x([a-fA-F0-9]{1,2})/hex($1)/goe;
19754                $literal =~ s/([A-F][A-F0-9]?|[A-F0-9]?[A-F])/hex($1)/gioe;
19755                $sep = '.';
19756            }
19757
19758            # remove leading zeros and put it into an array
19759            my @octets = map {
19760                if ( !m/^0$/io ) {my $t = $_; $t =~ s/^0*//o; $t }
19761                else             { 0 }    # properly handle a 0 in the IP
19762            } split( /\.|\:/o, $literal );
19763
19764            #put the ip back together
19765            push @variants, (join $sep, @octets);
19766            push @variants, (join $sep, reverse(@octets));
19767        }
19768
19769        return 1 unless scalar @variants;
19770        d("saw IP in HELO: @variants");
19771
19772        my $mr = $this->{messagereason} = "Suspicious HELO - contains IP: '$helo'";
19773        $this->{prepend} = "[SuspiciousHelo]";
19774
19775        pbAdd( $fh, $ip, $fiphValencePB, 'IPinHELO' ) if $DoIPinHelo != 2;
19776        $tlit = "[scoring:$fiphValencePB]" if $DoIPinHelo != 2;
19777        mlog( $fh, "$tlit ($this->{messagereason})", 1 ) if $ValidateSenderLog;
19778        if ( ! grep(/^\Q$ip\E$/i,@variants) ) {
19779            $this->{messagereason} = "IP in HELO '$helo' does not match IP in connection '$ip' ";
19780            $mr .= " - and IP in HELO '$helo' does not match IP in connection '$ip' ";
19781            pbAdd( $fh, $ip, $fiphmValencePB, 'IPinHELOmismatch' ) if $DoIPinHelo != 2;
19782            $tlit = "[scoring:$fiphmValencePB]" if $DoIPinHelo != 2;
19783            mlog( $fh, "$tlit ($this->{messagereason})", 1 ) if $ValidateSenderLog;
19784        }
19785        $this->{messagereason} = $mr unless $fh;
19786        $this->{prepend} = '';
19787        return 1 if $this->{messagescore} <= $this->{spamMaxScore};
19788        return 0;
19789    }
19790
19791    #the if didn't hit
19792    return 1;
19793}
19794
19795sub GoodHelo {
19796  	my($fh,$fhelo)=@_;
19797  	my $this=$Con{$fh};
19798	return 1 if $this->{relayok};
19799  	return 1 if $this->{whitelisted};
19800 	return 1 if $this->{noprocessing};
19801  	return 1 if $this->{nohelo};
19802  	return 1 unless $useHeloGoodlist;
19803
19804  	d('GoodHelo');
19805
19806	$this->{prepend} = "[GoodHELO]";
19807
19808  my $ip = $this->{ip};
19809  $ip = $this->{cip} if $this->{ispip} && $this->{cip};
19810  my $helo = lc($fhelo);
19811  $helo = lc($this->{ciphelo}) if $this->{ispip} && $this->{ciphelo};
19812
19813  my $val;
19814  return 1 if !($HeloBlackObject && ($val = $HeloBlack{$helo}));
19815  return 1 if $heloBlacklistIgnore && $helo =~ /$HBIRE/;
19816
19817  if ($HeloBlackObject && $val < 1) {
19818
19819      mlog($fh,"[scoring:$hlgValencePB] found known good HELO '$helo'");
19820      if ($useHeloGoodlist == 1 ) {
19821          pbAdd($fh,$ip,$hlgValencePB,"KnownGoodHelo");
19822          pbWhiteAdd($fh,$this->{ip},"KnownGoodHelo");
19823          $this->{goodhelo} = 1;
19824          $this->{blackhelodone}=1;
19825      }
19826      if ($useHeloGoodlist == 2 ) {
19827      		pbAdd($fh,$ip,$hlgValencePB,"KnownGoodHelo");
19828          	pbWhiteAdd($fh,$this->{ip},"KnownGoodHelo");
19829          	$this->{white} = 1;
19830          	$this->{goodhelo} = 1;
19831          	$this->{blackhelodone}=1;
19832      }
19833  }
19834  return 1;
19835}
19836# do blacklisted HELO check
19837sub BlackHeloOK {
19838    my ( $fh, $fhelo ) = @_;
19839    my $this = $Con{$fh};
19840    d('BlackHeloOK');
19841    return 1 if $this->{addressedToSpamBucket};
19842
19843    return 1 if $this->{blackhelodone};
19844	$this->{blackhelodone}=1;
19845
19846  	my $ip = $this->{ip};
19847  	$ip = $this->{cip} if $this->{ispip} && $this->{cip};
19848  	my $helo = lc($fhelo);
19849  	$helo = lc($this->{ciphelo}) if $this->{ispip} && $this->{ciphelo};
19850
19851
19852    return 1 if !$useHeloBlacklist;
19853    return 1 if $this->{relayok};
19854  	return 1 if $this->{whitelisted};
19855 	return 1 if $this->{noprocessing};
19856  	return 1 if $this->{nohelo};
19857  	return 1 if $this->{ispip} && ! $this->{cip};
19858  	return 1 if $this->{rwlok} && ! $this->{cip};
19859
19860
19861    return 1 if $heloBlacklistIgnore && $helo =~ $HBIRE;
19862
19863    my $museHeloBlacklist = $useHeloBlacklist;
19864    my $tlit = tlit($museHeloBlacklist);
19865    $this->{prepend} = "[BlackHELO]";
19866
19867    #$this->{prepend} .= "[$tlit]" if $museHeloBlacklist >= 2;
19868    $this->{messagereason} = "blacklisted HELO '$helo'";
19869    $tlit = "[scoring:$hlbValencePB]" if $museHeloBlacklist == 3;
19870    if ( $HeloBlackObject && $HeloBlack{ $helo } or $BlackHeloObject && $BlackHelo{ $helo }) {
19871        mlog( $fh, "$tlit -- $this->{messagereason} -- $this->{logsubject}" )
19872          if $ValidateHeloLog && $museHeloBlacklist == 3
19873              || $museHeloBlacklist == 2;
19874        pbWhiteDelete( $fh, $ip );
19875        $HeloBlack{ $helo } = time if exists $HeloBlack{ $helo };
19876        return 1 if $museHeloBlacklist == 2;
19877        $this->{formathelodone} = 1;
19878        $this->{blackhelodone} = 1;
19879        pbAdd( $fh, $ip, $hlbValencePB, "BlacklistedHelo",1 );
19880        return 1 if $museHeloBlacklist == 3;
19881
19882    }
19883    return 1;
19884}
19885
19886# do blacklisted domains check
19887sub BlackDomainOK {
19888
19889    my $fh = shift;
19890    my $this = $Con{$fh};
19891    my %Bombs;
19892
19893    d('BlackDomainOK');
19894    return 1 if $this->{notspamtag};
19895    return 1 if $this->{addressedToSpamBucket};
19896    return 1 if $this->{BlackDomainOK};
19897    $this->{BlackDomainOK} = 1;
19898	return 1 if !$this->{mailfrom};
19899    return 1 if !$DoBlackDomain;
19900    return 1 if $this->{relayok};
19901    return 1 if $this->{whitelisted} && !$DoBlackDomainWL;
19902    return 1 if $this->{noprocessing} && !$DoBlackDomainNP;
19903    return 1 if $noBlackDomain
19904		&& matchSL( $this->{mailfrom}, 'noBlackDomain' );
19905
19906    my $ip = $this->{ip};
19907  	$ip = $this->{cip} if $this->{cip};
19908
19909  	my %senders;
19910
19911   	my $mDoBlackDomain = $DoBlackDomain;
19912	$this->{testmode} = 0;
19913	$this->{testmode} = 1	if $DoBlackDomain == 4 or $allTestMode;
19914	$mDoBlackDomain = 1 		if $DoBlackDomain == 4;
19915  	my $adr = lc $this->{mailfrom};
19916  	$adr = batv_remove_tag(0,$this->{mailfrom},'');
19917  	$senders{$adr} = 1;
19918    $senders{$adr} = 1;
19919    while ( $this->{header} =~ /\n(from|sender|reply-to|errors-to|list-\w+):.*?($EmailAdrRe\@$EmailDomainRe)/igo ) {
19920    	my $s = $2;
19921        $s = batv_remove_tag(0,$s,'');
19922        $senders{lc $s}=1;
19923    }
19924    $this->{senders} = join( ' ', keys %senders ) . " ";
19925
19926    my $slok           = $this->{allLoveSpam} == 1;
19927
19928	my $subre;
19929	my $ret;
19930    my $tlit = tlit($mDoBlackDomain);
19931	my ($slmatch,$w);
19932
19933    foreach my $adr ( split( " ", $this->{senders} ) ) {
19934    	($slmatch,$w) = &HighWeightSL($adr, 'weightedAddresses');
19935
19936    	last if $w ;
19937    }
19938
19939    if ($w) {
19940
19941		return 1 if $this->{noprocessing} && $w < $blValencePB;
19942		return 1 if $this->{whitelisted}  && $w < $blValencePB;
19943		my $bw; $bw = "black" if $w >= $blValencePB;
19944		$bw = "blackish" if $w >= 0 && $w < $blValencePB;
19945		$bw = "whitish" if $w < 0;
19946        $this->{messagereason} = $bw." address '$slmatch'";
19947
19948        $this->{prepend} = "[weightedAddresses]";
19949        $tlit = "[scoring:$w]" if $mDoBlackDomain != 2;
19950        mlog( $fh, "$tlit -- $this->{messagereason}" );
19951
19952        pbWhiteDelete( $fh, $ip );
19953
19954        pbAdd($fh,$ip,$w,"$bw",1) if $mDoBlackDomain != 2;
19955		return 1 if $this->{messagescore} <= $this->{spamMaxScore};
19956
19957        return 0 if $mDoBlackDomain == 1 && $w >= $blValencePB ;
19958
19959
19960
19961
19962    }
19963    if ($blackListedDomains && $this->{mailfrom}=~/($BLDRE)/ ) {
19964    	$this->{messagereason}="blacklisted domain '$1'";
19965    	$this->{prepend}="[BlackDomain]";
19966    	$tlit = "[scoring:$blValencePB]" if $mDoBlackDomain != 2;
19967    	mlog($fh,"$tlit ($this->{messagereason})") if $ValidateSenderLog && $mDoBlackDomain==3 || $mDoBlackDomain==2;
19968		pbWhiteDelete($fh,$ip);
19969    	return 1 if $mDoBlackDomain==2;
19970    	pbAdd($fh,$ip,$blValencePB,"BlacklistedDomain") ;
19971    	$this->{blackdomainscore}=1;
19972
19973    	return 1 if $mDoBlackDomain==3;
19974
19975    	return 0 if $mDoBlackDomain==1;
19976
19977    }
19978    if (!$NotGreedyBlackDomain && $this->{senders}) {
19979     foreach my $adr ( split( " ", $this->{senders} ) ) {
19980
19981
19982    	if ($blackListedDomains && $adr =~/($BLDRE)/ ) {
19983    		$this->{messagereason}="blacklisted domain '$1'";
19984    		$this->{prepend}="[BlackDomain]";
19985    		$tlit = "[scoring:$blValencePB" if $mDoBlackDomain != 2;
19986    		mlog($fh,"$tlit ($this->{messagereason})") if $ValidateSenderLog && 	$mDoBlackDomain==3 || $mDoBlackDomain==2;
19987			pbWhiteDelete($fh,$ip);
19988    		return 1 if $mDoBlackDomain==2;
19989    		pbAdd($fh,$ip,$blValencePB,"BlacklistedDomain") ;
19990
19991    		return 1 if $mDoBlackDomain==3;
19992
19993    		return 0 if $mDoBlackDomain==1;
19994
19995
19996    	return 0;
19997    	}
19998
19999    }}
20000
20001    return 1;
20002}
20003
20004# do personal blacklisted domains check
20005sub PersonalBlackDomainOK {
20006
20007    my $fh = shift;
20008    my $this = $Con{$fh};
20009    my %Bombs;
20010
20011    d('PersonalBlackDomainOK');
20012    return 1 if $this->{notspamtag};
20013    return 1 if $this->{addressedToSpamBucket};
20014    return 1 if $this->{PersonalBlackDomainOK};
20015    $this->{PersonalBlackDomainOK} = 1;
20016	return 1 if !$this->{mailfrom};
20017	return 1 if $this->{whitelisted} && !$DoBlackDomainWL;
20018    return 1 if $this->{noprocessing} && !$DoBlackDomainNP;
20019
20020    return 1 if $this->{relayok};
20021
20022    return 1 if $noBlackDomain
20023		&& matchSL( $this->{mailfrom}, 'noBlackDomain' );
20024
20025    my $ip = $this->{ip};
20026  	$ip = $this->{cip} if $this->{cip};
20027
20028  	my %senders;
20029  	my $adr = lc $this->{mailfrom};
20030  	$adr = batv_remove_tag($fh,$this->{mailfrom},'');
20031  	$senders{$adr} = 1;
20032    $senders{$adr} = 1;
20033    while ( $this->{header} =~ /\n(from|sender|reply-to|errors-to|list-\w+):.*?($EmailAdrRe\@$EmailDomainRe)/igo ) {
20034    	my $s = $2;
20035        $s = batv_remove_tag(0,$s,'');
20036        $senders{lc $s}=1;
20037    }
20038    $this->{senders} = join( ' ', keys %senders ) . " ";
20039
20040    my $slok           = $this->{allLoveBlSpam} == 1;
20041
20042   	my $mDoBlackDomain = $DoBlackDomain;
20043	$this->{testmode} = 0;
20044	$this->{testmode} = 1	if $DoBlackDomain == 4 or $allTestMode;
20045	$mDoBlackDomain = 1 	if $DoBlackDomain == 4;
20046	my $subre;
20047	my $ret;
20048    my $tlit = tlit($mDoBlackDomain);
20049	my ($slmatch,$w);
20050	$this->{prepend} = "[PersonalBlack]";
20051
20052
20053    foreach my $adr ( split( " ", $this->{senders} ) ) {
20054
20055		my ($mfd) = $adr =~ /\@(.*)/;
20056		my $all = "*@" . $mfd;
20057
20058		my ($to) = $this->{rcpt} =~ /(\S+)/;
20059		my ($tod) = $this->{rcpt} =~ /\@(.*)/;
20060		my ($todd) = $this->{rcpt} =~ /(\@.*)/;
20061		$todd = "*$todd";
20062
20063		if ( $PersBlack{ "*,$adr"}  ) {
20064            $PersBlack{lc "*,$adr"} = time;
20065            $this->{messagereason}="rejected by personal blacklist: '*,$adr'";
20066            pbAdd($fh,$ip,$blValencePB,"PersonalBlack",1) ;
20067
20068            return 0;
20069        }
20070
20071        if ( exists $PersBlack{lc "$to,$adr"} ) {
20072            $PersBlack{lc "$to,$adr"} = time;
20073
20074            $this->{messagereason}="rejected by personal blacklist: '$to,$adr'";
20075            pbAdd($fh,$ip,$blValencePB,"PersonalBlack",1) ;
20076
20077            return 0;
20078        }
20079
20080
20081    }
20082    return 1;
20083}
20084
20085
20086sub PTROK {
20087
20088    my $fh = shift;
20089    my $this = $Con{$fh};
20090    return 1 if $this->{addressedToSpamBucket};
20091
20092    d('PTROK');
20093
20094    my $ip = $this->{ip};
20095    $ip = $this->{cip} if  $this->{cip};
20096    return 1 if $ip =~ /$IPprivate/;
20097    return 1 if !$DoReversed;
20098    return 1 if !$CanUseDNS;
20099
20100    return 1 if $this->{ispip} && !$this->{cip};
20101
20102	return 1 if $this->{contentonly} && !$this->{cip};
20103    return 1 if $this->{relayok} ;
20104
20105    return 1 if $this->{whitelisted}  && !$DoReversedWL;
20106    return 1 if $this->{noprocessing} && !$DoReversedNP;
20107
20108    return 1 if PTRCacheFind( $ip ) == 2 && !$whitePTRRe;
20109
20110    my $slok = $this->{allLoveSpam} == 1;
20111
20112
20113    my $mDoReversed	= $DoReversed;
20114    $this->{testmode} 	= 0;
20115	$this->{testmode} 		= 1	if $DoReversed == 4 or $allTestMode;
20116	$mDoReversed = 1 	if $DoReversed == 4;
20117
20118    my $tlit = tlit($mDoReversed);
20119    $this->{prepend} = "[PTRmissing]";
20120
20121
20122    if ( PTRCacheFind($ip) == 1 ) {
20123		$tlit = "[scoring:$ptmValencePB]" if $mDoReversed == 3;
20124        $this->{messagereason} = "PTR missing";
20125        mlog( $fh, "$tlit ($this->{messagereason})" )
20126          if $mDoReversed == 3 || $mDoReversed == 2;
20127        return 1 if $mDoReversed == 2;
20128        pbAdd( $fh, $ip, $ptmValencePB, "PTRmissing" ) if $mDoReversed == 3;
20129        pbWhiteDelete( $fh, $ip );
20130
20131       return 1 if $mDoReversed == 3;
20132       unless ($slok) {$Stats{ptrMissing}++;}
20133       $this->{blockinglist} .= "$this->{prepend}" ;
20134       pbAdd( $fh, $ip, $ptmValencePB+20, "PTRmissing-blocked" ) if $mDoReversed == 1;
20135       return 1 if $this->{messagescore} <= $this->{spamMaxScore};
20136
20137        return 0;
20138    }
20139    if ( PTRCacheFind($ip) == 2 ) {
20140
20141    	my ( $ct, $status, $ptrdsn) = split( " ", $PTRCache{$ip} );
20142
20143        if (   $ptrdsn
20144
20145            && $whitePTRRe
20146            && $whitePTRReRE != ""
20147            && $ptrdsn =~ $whitePTRReRE)
20148        {
20149            $this->{messagereason} = "PTR whitelisted '$ptrdsn'";
20150            $this->{prepend}       = "[PTRwhite]";
20151
20152            mlog( $fh, "$this->{messagereason}" );
20153
20154            pbWhiteAdd( $fh, $ip );
20155			$this->{noprocessing} = 1;
20156			$this->{passingreason} = "PTR $ptrdsn whitelisted";
20157            return 1;
20158        }
20159    }
20160
20161    if ( $DoInvalidPTR && PTRCacheFind($ip) == 3 ) {
20162
20163    	my ( $ct, $status, $ptrdsn) = split( " ", $PTRCache{$ip} );
20164
20165        if (   $ptrdsn
20166            && $DoInvalidPTR
20167            && $invalidPTRRe
20168            && $invalidPTRReRE != ""
20169            && $ptrdsn =~ $invalidPTRReRE
20170            && $ptrdsn !~ $validPTRReRE )
20171        {
20172            $this->{messagereason} = "PTR invalid '$ptrdsn'";
20173            $this->{prepend}       = "[PTRinvalid]";
20174            $tlit = "[scoring:$ptiValencePB]" if $mDoReversed == 3;
20175            mlog( $fh, "$tlit ($this->{messagereason})" )
20176              if $mDoReversed == 3 || $mDoReversed == 2;
20177            return 1 if $mDoReversed == 2;
20178            pbAdd( $fh, $ip, $ptiValencePB, "PTRinvalid" ) if $mDoReversed == 3;
20179            pbWhiteDelete( $fh, $ip );
20180            return 1 if $mDoReversed == 3;
20181            $this->{blockinglist} .= "$this->{prepend}" ;
20182            pbAdd( $fh, $ip, $ptiValencePB+20, "PTRinvalid-blocked") if $mDoReversed == 1;
20183            unless ($slok) {$Stats{ptrInvalid}++;}
20184            return 1 if $this->{messagescore} <= $this->{spamMaxScore};
20185
20186            return 0;
20187        }
20188    }
20189
20190    my $res = Net::DNS::Resolver->new(
20191        nameservers => \@nameservers,
20192    		tcp_timeout => $DNStimeout,
20193            udp_timeout => $DNStimeout,
20194            retrans     => $DNSretrans,
20195            retry       => $DNSretry
20196    );
20197	getRes('force', $res);
20198    my $ip_address = $ip;
20199	my $query;
20200	my $socket;
20201    if ($ip_address) {
20202
20203        $query = eval { $res->search( $ip_address, 'PTR' ); };
20204        if ($@) {
20205        	mlog( $fh, "error: $@" );
20206        	pbAdd( $fh, $ip, $ptiValencePB, "PTRerror:_$@" );
20207        	PTRCacheAdd( $ip, 2 );
20208        	return 1;
20209        	}
20210        if ($query) {
20211            foreach my $rr ( $query->answer ) {
20212                next unless $rr->type eq "PTR";
20213                $this->{ptrdsn} = $rr->ptrdname;
20214                if (   $this->{ptrdsn}
20215
20216            		&& $whitePTRRe
20217
20218            		&& $this->{ptrdsn} =~ $whitePTRReRE)
20219        		{
20220            		$this->{messagereason} = "PTR whitelisted '$this->{ptrdsn}'";
20221            		$this->{prepend}       = "[PTRwhite]";
20222
20223            		mlog( $fh, "$this->{messagereason}" );
20224
20225            		pbWhiteAdd( $fh, $ip );
20226					$this->{noprocessing} = 1;
20227					$this->{passingreason} = "PTR $this->{ptrdsn} whitelisted";
20228					PTRCacheAdd( $ip, 2, $this->{ptrdsn} );
20229            		return 1;
20230        		}
20231                return 1
20232                   if ( $heloBlacklistIgnore && $this->{ptrdsn} =~ $HBIRE );
20233
20234                if (   $DoInvalidPTR
20235                    && $invalidPTRRe
20236                    && $invalidPTRReRE != ""
20237                    && $this->{ptrdsn} =~ $invalidPTRReRE
20238                    && $this->{ptrdsn} !~ $validPTRReRE )
20239                {
20240                    $this->{prepend} = "[PTRinvalid]";
20241
20242                    #$this->{prepend} .= "[$tlit]" if $mDoReversed >= 2;
20243                    $this->{messagereason} = "PTR invalid '$this->{ptrdsn}'";
20244                    $tlit = "[scoring:$ptiValencePB]" if $mDoReversed == 3;
20245                    mlog( $fh, "$tlit ($this->{messagereason})" )
20246                      if ( $mDoReversed == 3 || $mDoReversed == 2 );
20247                    PTRCacheAdd( $ip, 3, $this->{ptrdsn} );
20248                    return 1 if $mDoReversed == 2;
20249                    pbAdd( $fh, $ip, $ptiValencePB, "PTRinvalid" ) if $mDoReversed == 3;
20250                    pbWhiteDelete( $fh, $ip );
20251                    return 1 if $mDoReversed == 3;
20252                    unless ($slok) {$Stats{ptrMissing}++;}
20253                    $this->{blockinglist} .= "$this->{prepend}" ;
20254                    pbAdd( $fh, $ip, $ptiValencePB+20, "PTRinvalid-blocked" ) if $mDoReversed == 1;;
20255                    return 1 if $this->{messagescore} <= $this->{spamMaxScore};
20256
20257                    return 0;
20258                }
20259                PTRCacheAdd( $ip, 2, $this->{ptrdsn} );
20260                return 1;
20261            }
20262        } else {
20263            if ( $res->errorstring =~ "NXDOMAIN" ) {
20264                $this->{prepend} = "[PTRmissing]";
20265
20266                #$this->{prepend} .= "[$tlit]" if $mDoReversed == 3;
20267                $this->{messagereason} = "PTR missing";
20268                PTRCacheAdd( $ip, 1 );
20269                $tlit = "[scoring:$ptmValencePB]" if $mDoReversed == 3;
20270                mlog( $fh, "$tlit ($this->{messagereason})" )
20271                  if ( $mDoReversed == 3 || $mDoReversed == 2 );
20272                return 1 if $mDoReversed == 2;
20273                pbAdd( $fh, $ip, $ptmValencePB, "PTRmissing" ) if $mDoReversed == 3;
20274                pbWhiteDelete( $fh, $ip );
20275                return 1 if $mDoReversed == 3;
20276				unless ($slok) {$Stats{ptrMissing}++;}
20277                $this->{blockinglist} .= "$this->{prepend}" ;
20278                pbAdd( $fh, $ip, $ptmValencePB + 20, "PTRmissing-blocked" ) if $mDoReversed == 1;
20279                return 1 if $this->{messagescore} <= $this->{spamMaxScore};
20280                return 0;
20281            }
20282        }
20283    }
20284    return 1;
20285}
20286
20287sub DenyStrictOK {
20288    my ( $fh, $ip ) = @_;
20289    my $this = $Con{$fh};
20290    $ip = $this->{cip} if $this->{cip};
20291#    return 1 if $ip =~ /$IPprivate/;
20292    my $ret;
20293	d('DenyStrictOK');
20294	my $byWhatList = 'denySMTPConnectionsFromAlways';
20295
20296	$ret =  matchIP( $ip, 'denySMTPConnectionsFromAlways', $fh );
20297
20298	if ($ret && !$this->{relayok}
20299    	&&  !$this->{acceptall}
20300    	&& 	!$this->{ispip}
20301    	&& 	!$this->{nopb}
20302		&& 	!$this->{noblockingips} ) {
20303
20304        mlog( $fh, "[DenyIPStrict][monitoring] ".$ip." blocked by $byWhatList: '$ret'",1 )
20305              if $DoDenySMTPstrict == 2 or $allTestMode;
20306        return 1 if $DoDenySMTPstrict == 2 or $allTestMode;
20307        mlog( $fh, "[DenyIPStrict] ".$ip." blocked by $byWhatList: '$ret'",1 )
20308              if $DoDenySMTPstrict == 1;
20309 		$this->{strictlyblocked} = $ip;
20310
20311        return 0 if $DoDenySMTPstrict == 1;
20312    }
20313
20314    return 1;
20315}
20316
20317sub DenyOK {
20318    my ( $fh, $myip ) = @_;
20319    my $this = $Con{$fh};
20320    $myip = $this->{cip} if $this->{cip};
20321    return 1 if $myip =~ /$IPprivate/;
20322    d('DenyOK');
20323	my $bip = &ipNetwork( $myip, $PenaltyUseNetblocks);
20324	return 1 if $this->{noprocessing};
20325    return 1 if $this->{ispip} && !$this->{cip};
20326	return 1 if $this->{noblockingips};
20327    return 1 if $this->{nopb};
20328    return 1 if $this->{acceptall};
20329    return 1 if $this->{relayok};
20330    my $t    = time;
20331    my $slok = $this->{allLoveSpam} == 1;
20332	my $tlit;
20333
20334
20335
20336    return 1 if $this->{whitelisted};
20337
20338    return 1 if $this->{addressedToSpamBucket};
20339
20340    my $file;
20341    my $ret;
20342    $ret = matchIP( $myip, 'denySMTPConnectionsFrom', $fh );
20343    $this->{prepend} = "[DenyIP]";
20344
20345
20346    $this->{messagereason} = "denySMTPConnectionsFrom '$ret'";
20347    if ( $ret && $DoDenySMTP == 3 ) {
20348        mlog( $fh, "[scoring:$dropValencePB] -- $this->{messagereason} -- $this->{logsubject}" );
20349        pbAdd( $fh, $myip,$dropValencePB, "denySMTPConnectionsFrom");
20350        return 1;
20351    }
20352
20353    if ( $ret && ( $DoDenySMTP == 2 or $allTestMode)) {
20354        mlog( $fh, "[monitoring] -- $this->{messagereason} -- $this->{logsubject}" );
20355        return 1;
20356    }
20357    if ( $ret && $DoDenySMTP == 1 ) {
20358
20359        return 0;
20360    }
20361
20362
20363
20364    return 1;
20365}
20366
20367sub DroplistOK {
20368    my ( $fh, $ip ) = @_;
20369    my $this = $Con{$fh};
20370
20371    d('DropOK');
20372
20373	return 1 if $ip =~ /$IPprivate/;
20374    return 1 if $this->{ispip};
20375    return 1 if $this->{nopb};
20376
20377    return 1 if $this->{acceptall};
20378    return 1 if $this->{relayok};
20379    return 1 if $this->{whitelisted};
20380
20381    my $t    = time;
20382
20383    return 1 if $this->{addressedToSpamBucket};
20384    my $ret = matchIP( $ip, 'droplist', $fh );
20385    return 1 if !$ret;
20386	my $mDoDropList = $DoDropList;
20387    $mDoDropList = 2 if $allTestMode;
20388    $mDoDropList = 3 if $this->{allLoveSpam} == 1;
20389    my $tlit = tlit($mDoDropList);
20390    $this->{prepend} = "[DropList]";
20391	$this->{messagereason} = "found in DropList '$ret'";
20392
20393	if ($mDoDropList == 2 ) {
20394        mlog( $fh, "[monitoring] -- $this->{messagereason} -- $this->{logsubject}" );
20395        return 1;
20396    }
20397
20398	return 0 if $ret && $mDoDropList == 1;
20399
20400
20401    if ($mDoDropList == 3 ) {
20402        mlog( $fh, "[scoring:$dropValencePB] -- $this->{messagereason} -- $this->{logsubject}" );
20403        pbAdd( $fh, $ip,$dropValencePB, "Droplist");
20404        return 1;
20405    }
20406
20407}
20408sub HistoryOK {
20409    my ( $fh, $myip ) = @_;
20410    my $this = $Con{$fh};
20411    return if $this->{addressedToSpamBucket};
20412    return if $this->{relayok};
20413    return 1 if $this->{notspamtag};
20414    return 1 if $myip =~ /$IPprivate/;
20415    return if $this->{badhistory};
20416    $myip = $this->{cip} if $this->{cip};
20417    d('HistoryOK');
20418
20419	if ($spamFriends && $this->{spamfriends} && !$this->{spamfriendsdone}) {
20420		my ($slmatch,$w) = &HighWeightSL($this->{spamfriends}, 'spamFriends');
20421		$this->{messagereason} = "SpamFriends";
20422		$this->{spamfriendsdone} = 1;
20423		pbAdd( $fh, $myip, $w, "SpamFriends", 1 );
20424	}
20425
20426    my $t    = time;
20427    my $ip   = ipNetwork( $myip, $PenaltyUseNetblocks );
20428    my $slok = $this->{allLoveSpam} == 1;
20429	my $tlit;
20430
20431    return if $this->{whitelisted};
20432    return if $this->{noprocessing};
20433    my $mf = lc $this->{mailfrom};
20434    $mf = batv_remove_tag($fh,$this->{mailfrom},'');
20435    my $mfd; $mfd = $1 if $mf =~ /\@(.*)/;
20436
20437
20438    return if $this->{ispip};
20439	return if $this->{contentonly};
20440    return if $this->{nopb};
20441    return if $this->{acceptall};
20442
20443    return if $this->{relayok};
20444    return if $myip =~ /$IPprivate/ ;
20445
20446    $this->{prepend} = "[History]";
20447	if (pbWhiteFind($myip)) {
20448        pbBlackDelete( $fh, $myip );
20449
20450        return 1;
20451    }
20452    return 1 if !pbBlackFind( $myip );
20453    my $blackscore = pbBlackFind( $myip );
20454
20455
20456    if ( $blackscore >= $PenaltyLimit * 2 ) {
20457        $this->{messagereason} = "Bad Reputation for $myip";
20458        pbAdd( $fh, $myip, 35, "VeryBadReputation", 1 );
20459        $this->{badhistory} = 1;
20460		$this->{newsletterre}		= '';
20461        return 0;
20462    }
20463    if (  $blackscore >= $PenaltyLimit) {
20464        $this->{messagereason} = "Bad Reputation for $myip";
20465        pbAdd( $fh, $myip, 25, "BadReputation", 1 );
20466        $this->{badhistory} = 1;
20467		$this->{newsletterre}		= '';
20468        return 0;
20469
20470    }
20471
20472
20473
20474}
20475
20476sub Delayok {
20477    my ( $fh, $rcpt ) = @_;
20478    my $this   = $Con{$fh};
20479    my $client = $this->{friend};
20480    $this->{prepend} = "";
20481    d('Delayok');
20482
20483    if ( $this->{delaydone} ) {
20484        $this->{delaydone} = '';
20485        return 1;
20486    }
20487    return 1 if !$EnableDelaying;
20488    return 1 if $this->{relayok};
20489    return 1 if $Con{$client}->{relayok};
20490
20491    return 1 if $this->{ispip};
20492    return 1 if $this->{acceptall};
20493    return 1 if $this->{ip} =~ /$IPprivate/;
20494
20495    my $helook = $noDelayHelosReRE;
20496
20497
20498	my $v = GRIPv( $fh, $this->{ip} );
20499
20500    my $time = $UseLocalTime ? localtime() : gmtime();
20501    my $tz   = $UseLocalTime ? tzStr() : '+0000';
20502    $time =~ s/... (...) +(\d+) (........) (....)/$2 $1 $4 $3/;
20503    my $mf   = lc $this->{mailfrom};
20504    $mf = batv_remove_tag(0,$this->{mailfrom},'');
20505
20506    my $mfd; $mfd  = $1 if $mf =~ /\@(.*)/;
20507
20508
20509    if ( !$DelayWL && $this->{whitelisted} ) {
20510
20511       # add to our header; merge later, when client sent own headers  (per msg)
20512        $this->{myheader} .=
20513          "X-Assp-Delay: not delayed ($this->{passingreason}); $time $tz\r\n"
20514          if ( $DelayAddHeader
20515            && $this->{myheader} !~ /not delayed / );
20516
20517        return 1;
20518    }
20519
20520    if ( !$DelayNP && $this->{noprocessing} ) {
20521
20522       # add to our header; merge later, when client sent own headers  (per msg)
20523        $this->{myheader} .=
20524          "X-Assp-Delay: $rcpt not delayed (noprocessing); $time $tz\r\n"
20525          if ( $DelayAddHeader
20526            && $this->{myheader} !~ /not delayed / );
20527
20528        return 1;
20529    }
20530    if ( $this->{nodelay} ) {
20531
20532       # add to our header; merge later, when client sent own headers  (per msg)
20533        $this->{myheader} .=
20534          "X-Assp-Delay: $rcpt not delayed ($this->{nodelay}); $time $tz\r\n"
20535          if ( $DelayAddHeader
20536            && $this->{myheader} !~ /not delayed / );
20537        return 1;
20538    }
20539    if ( !$DelayWL && pbWhiteFind( $this->{ip} ) ) {
20540        pbBlackDelete( $fh, $this->{ip} );
20541
20542       # add to our header; merge later, when client sent own headers  (per msg)
20543        $this->{myheader} .=
20544"X-Assp-Delay: $rcpt not delayed (whitebox $this->{ip}); $time $tz\r\n"
20545          if ( $DelayAddHeader
20546            && $this->{myheader} !~ /not delayed / );
20547
20548        return 1;
20549    }
20550    if ( !$DelayWL && SBCacheFind( $this->{ip},2 ) ) {
20551
20552       # add to our header; merge later, when client sent own headers  (per msg)
20553        $this->{myheader} .=
20554"X-Assp-Delay: $rcpt not delayed (whiteorg $this->{ip}); $time $tz\r\n"
20555          if ( $DelayAddHeader
20556            && $this->{myheader} !~ /not delayed / );
20557
20558        return 1;
20559    }
20560    if (!$DelayWL && $this->{whiteorg} ) {
20561
20562       # add to our header; merge later, when client sent own headers  (per msg)
20563        $this->{myheader} .=
20564"X-Assp-Delay: $rcpt not delayed ($this->{whiteorg}); $time $tz\r\n"
20565          if ( $DelayAddHeader
20566            && $this->{myheader} !~ /not delayed / );
20567
20568        return 1;
20569    }
20570
20571
20572    if ( !$DelaySL && $this->{allLoveDLSpam} == 1 ) {
20573
20574       # add to our header; merge later, when client sent own headers  (per msg)
20575        $this->{myheader} .=
20576          "X-Assp-Delay: $rcpt not delayed (spamlover); $time $tz\r\n"
20577          if ( $DelayAddHeader
20578            && $this->{myheader} !~ /not delayed / );
20579        return 1;
20580    }
20581    if ( $this->{dlslre} ) {
20582
20583      # add to our header; merge later, when client sent own headers  (per rcpt)
20584        $this->{myheader} .=
20585          "X-Assp-Delay: $rcpt not delayed (delay-spamlover); $time $tz\r\n"
20586          if ( $DelayAddHeader
20587            && $this->{myheader} !~ /not delayed / );
20588
20589        $this->{dlslre}="";
20590        return 1;
20591    }
20592    my $mfwhite = $mf;
20593    $mfwhite =~ s/.*@//;
20594
20595
20596    my ( $cachetime, $cresult, $chelo ) = SPFCacheFind($this->{ip},$mfwhite);
20597
20598
20599    if (!$DelayWL && $DoOrgWhiting == 1 && ! &pbBlackFind($this->{ip})) {
20600        my ( $ipcountry, $orgname, $domainname ) = split( /\|/, SBCacheFind($this->{ip}) ) ;
20601        if (!$DelayWL && $domainname eq $mfwhite && exists $WhiteOrgList{$domainname}) {
20602          # add to our header; merge later, when client sent own headers  (per rcpt)
20603            $this->{myheader}.="X-Assp-Delay: not delayed (White-SenderBase-Cache-OK); $time $tz\r\n" if ($DelayAddHeader && $this->{myheader} !~ /not delayed \(White-SenderBase/o);
20604            return 1;
20605        }
20606    }
20607    if ($DelayNormalizeVERPs) {
20608
20609        # strip extension
20610        $mf =~ s/\+.*(?=@)//;
20611
20612        # replace numbers with '#'
20613        $mf =~ s/\b\d+\b(?=.*@)/#/g;
20614    }
20615    my $ip = ipNetwork( $this->{ip}, $DelayUseNetblocks );
20616    my $hash = "$ip $mf " . lc $rcpt;
20617
20618    # get sender domain
20619
20620    my $hashwhite = "$ip $mfwhite";
20621    if ( $CanUseMD5 && $DelayMD5 ) {
20622        $hash      = Digest::MD5::md5_hex($hash);
20623        $hashwhite = Digest::MD5::md5_hex($hashwhite);
20624    }
20625    my $t = time;
20626    my $delay_result;
20627    if ( !exists $DelayWhite{$hashwhite} ) {
20628        if ( !exists $Delay{$hash} ) {
20629            mlog( $fh, "adding new triplet: ($ip,$mf," . lc $rcpt . ")", 1 )
20630              if $DelayLog >= 2;
20631            $Stats{rcptDelayed}++;
20632            $Delay{$hash} = $t;
20633            $delay_result = 0;
20634        } else {
20635            my $interval          = $t - $Delay{$hash};
20636            my $intervalFormatted = formatTimeInterval($interval);
20637            if ( $interval < $DelayEmbargoTime * 60 ) {
20638                mlog(
20639                    $fh,
20640                    "embargoing triplet: ($ip,$a,"
20641                      . lc $rcpt
20642                      . ") waited: $intervalFormatted",
20643                    1
20644                ) if $DelayLog >= 2;
20645                $Stats{rcptEmbargoed}++;
20646                $delay_result = 0;
20647            } elsif (
20648                $interval < $DelayEmbargoTime * 60 + $DelayWaitTime * 3600 )
20649            {
20650                mlog(
20651                    $fh,
20652                    "accepting triplet: ($ip,$a,"
20653                      . lc $rcpt
20654                      . ") waited: $intervalFormatted",
20655                    1
20656                ) if $DelayLog >= 2;
20657                delete $Delay{$hash};
20658                $DelayWhite{$hashwhite} = $t;
20659
20660                $delay_result = 1;
20661
20662                # add to our header; merge later, when client sent own headers
20663                $this->{myheader} .=
20664"X-Assp-Delay: $rcpt was delayed for $intervalFormatted; $time $tz\r\n"
20665                  if $DelayAddHeader;
20666            } else {
20667                mlog(
20668                    $fh,
20669                    "late triplet encountered, deleting: ($ip,$a,"
20670                      . lc $rcpt
20671                      . ") waited: $intervalFormatted",
20672                    1
20673                ) if $DelayLog >= 2;
20674                $Stats{rcptDelayedLate}++;
20675
20676                $Delay{$hash} = $t;
20677                $delay_result = 0;
20678            }
20679        }
20680    } else {
20681        my $interval          = $t - $DelayWhite{$hashwhite};
20682        my $intervalFormatted = formatTimeInterval($interval);
20683        if ( $interval < $DelayExpiryTime * 24 * 3600 ) {
20684            mlog( $fh,
20685                "renewing tuplet: ($ip,$mfwhite) age: " . $intervalFormatted, 1 )
20686              if $DelayLog >= 2;
20687            $DelayWhite{$hashwhite} = $t;
20688
20689            # multiple rcpt's
20690            delete $Delay{$hash};
20691            $delay_result = 1;
20692
20693            # add to our header; merge later, when client sent own headers
20694            $this->{myheader} .=
20695              "X-Assp-Delay: $rcpt not delayed (auto accepted); $time $tz\r\n"
20696              if $DelayAddHeader;
20697        } else {
20698            mlog(
20699                $fh,
20700                "deleting expired tuplet: ($ip,$mfwhite) age: "
20701                  . $intervalFormatted,
20702                1
20703            ) if $DelayLog >= 2;
20704            $Stats{rcptDelayedExpired}++;
20705
20706            delete $DelayWhite{$hashwhite};
20707            $Delay{$hash} = $t;
20708            $delay_result = 0;
20709        }
20710    }
20711    return $delay_result;
20712}
20713
20714
20715
20716# returns true if all of the addresses in the space separated list are Noprocessing addresses
20717sub allNoProcessing {
20718    my $rcpt = shift;
20719    my $c    = 0;
20720    for ( split( ' ', $rcpt ) ) {
20721
20722        return 0 unless matchSL( $_, 'noProcessing' );
20723        $c++;
20724    }
20725    $c;
20726}
20727
20728sub allNoProcessingTo {
20729    my (  $rcpt, $fh) = @_;
20730 	my $this   = $Con{$fh};
20731    my $c    = 0;
20732    for ( split( ' ', $rcpt ) ) {
20733		$this->{newrcpt} .= "$_ ";
20734        return 0 unless matchSL( $_, 'noProcessing' ) or matchSL( $_, 'noProcessingTo' );
20735        $c++;
20736    }
20737    $c;
20738}
20739sub allRot {
20740    my $a = shift;
20741    $a =~ tr/A-Za-z/N-ZA-Mn-za-m/;
20742    return ($a);
20743}
20744
20745sub allSL {
20746    my ( $rcpt, $from, $re ) = @_;
20747    my $c = 0;
20748    return 1 if matchSL( $from, $re, 1 );
20749    for ( split( ' ', $rcpt ) ) {
20750        return 1 if matchSL( $_, $re, 1 );
20751        next;
20752    }
20753    return 0;
20754}
20755
20756sub allSH {
20757   my($rcpt,$re)=@_;
20758   return 0 unless $rcpt;
20759   return 0 unless $re;
20760   return 0 unless $$re;
20761   my $ret = 1;
20762   for (split(/\s+/o,$rcpt)) {
20763      if (! matchSL($_,$re,1)) {
20764         $ret = 0 ;
20765         last;
20766      }
20767   }
20768   return $ret;
20769}
20770# the message is not spam -- route it to the server
20771sub isnotspam {
20772    my ( $fh, $done ) = @_;
20773    d('isnotspam');
20774    my $this   = $Con{$fh};
20775    my $server = $this->{friend};
20776
20777
20778
20779    # it's time to merge our header with client's one
20780
20781
20782	if (   (! $this->{relayok} || ($this->{relayok} && ! $NoExternalSpamProb ) )
20783        && !$this->{myheaderdone}
20784
20785       )
20786    {
20787    $this->{myheader} .= "X-Assp-ID: $myName ($this->{msgtime})\r\n" if $this->{myheader} !~ "X-Assp-ID";
20788    $this->{myheader} .= "X-Assp-Version: $version$modversion\r\n" if $this->{myheader} !~ "X-Assp-Version";
20789    my $myheader = $this->{myheader};
20790  	$myheader = headerFormat($myheader);
20791  	d('after headerWrap');
20792  	$this->{header}=~s/^($HeaderRe*)/$1\r\n\n\n\r$myheader/o;
20793  	d('after merge our header');
20794  	$this->{header}=~s/\r?\n?\r\n\n\n\r/\r\n/;
20795  	d("added header : $this->{myheader}");
20796  	$this->{myheaderdone} = 1;
20797
20798  	}
20799
20800 	sigOK( $fh, $this->{header}, $done ) if $this->{relayok};
20801
20802  	if (
20803  		! $this->{MSGIDsigRemoved}
20804  		&& ! $this->{relayok}
20805  		&& $DoMSGIDsig
20806  		&& !$this->{isbounce}) {
20807          	&MSGIDsigRemove($fh);  # remove the MSGID signatures from incoming emails
20808          	$this->{maillength} = length($this->{header});
20809  	}
20810
20811	sendque( $server, $this->{header} );
20812	$this->{headerpassed} = 1;
20813
20814    if ($done) {
20815
20816		onwhitelist( $fh, $this->{header}) if $this->{relayok} && !$this->{red} && !$this->{spamfound};
20817
20818
20819		&sayMessageOK($fh) if !$this->{spamfound};
20820        $this->{getline} = \&getline;
20821    } else {
20822        $this->{getline} = \&whitebody;
20823    }
20824}
20825
20826# the message is non spam -- just relay it to the server
20827sub whitebody {
20828
20829    my ( $fh, $l ) = @_;
20830    my $this;
20831  	$this=$Con{$fh} if exists $Con{$fh};
20832  	my $friend;
20833  	$friend=$Con{$fh} if exists $Con{$fh};
20834    d('whitebody');
20835    my $server = $this->{friend};
20836    my $mbytes;
20837    my $clamavbytes;
20838    my $maxbytes;
20839
20840    $this->{maillength}+=length($l);
20841    $this->{header} .= $l if(length($this->{header}) < 100000) or ($sendHamInbound && ! $this->{relayok}) or ($sendHamOutbound &&  $this->{relayok});
20842
20843 	return if ! MessageSizeOK($fh);
20844
20845
20846
20847    my $done = $l =~ /^\.[\r\n]*$/
20848      || defined( $this->{bdata} ) && $this->{bdata} <= 0;
20849	$this->{headerlength} ||= getheaderLength($fh);
20850 	$maxbytes = $MaxBytes > 10000 ? $MaxBytes + $this->{headerlength} : 10000 + $this->{headerlength};
20851    $clamavbytes = $ClamAVBytes ? $ClamAVBytes + $this->{headerlength} : 50000 + $this->{headerlength};
20852    $clamavbytes = 100000 if $ClamAVBytes > 100000;
20853    $mbytes = $maxbytes;
20854    $mbytes = $clamavbytes if $clamavbytes > $mbytes  && ($BlockExes || $CanUseAvClamd && $AvailAvClamd) ;
20855
20856    $this->{headerpassed} = 1 if ($done || $this->{maillength} >= $mbytes );
20857
20858    my $doneToError = $done || ($send250OK || ($send250OKISP && ($this->{ispip} or $this->{cip})));
20859
20860    if (($done || $this->{maillength} >= $mbytes ) && haveToScan($fh) &&
20861         ! ClamScanOK($fh, bodyWrap(\$this->{header},$clamavbytes)))
20862    {
20863
20864
20865      	if ( $this->{messagereason} =~ /UNOFFICIAL/i ){
20866      		thisIsSpam($fh,$this->{messagereason}, $SpamLog,$this->{averror}, 0,0,$doneToError);
20867      	} else {
20868      		thisIsSpam($fh,$this->{messagereason}, $SpamVirusLog,$this->{averror}, 0,0,$doneToError);
20869      	}
20870        return;
20871    }
20872
20873    if (($done || $this->{maillength} >= $mbytes ) && haveToFileScan($fh) &&
20874         ! FileScanOK($fh, bodyWrap(\$this->{header},$clamavbytes)))
20875    {
20876
20877     	thisIsSpam($fh,$this->{messagereason}, $SpamVirusLog,$this->{averror},0,0,$doneToError);
20878        return;
20879    }
20880
20881	sigOK( $fh, $l, $done );
20882
20883	if (! $friend->{MSGIDsigRemoved} && ! $friend->{relayok} && $DoMSGIDsig && ! $this->{noMoreQueued}) {
20884      if ($friend->{isbounce}) {
20885          if ($done) {
20886              &MSGIDsigRemove($this->{friend});  # remove the MSGID signatures from incoming emails
20887              $friend->{maillength} = length($friend->{header});
20888          }
20889      } else {
20890          &MSGIDsigRemove($this->{friend});  # remove the MSGID signatures from incoming emails
20891          $friend->{maillength} = length($friend->{header});
20892      }
20893  	}
20894	if ($this->{relayok} && !$this->{whitepassed}) {
20895		my %adr;
20896		my $t = time;
20897		$this->{whitepassed} = 1;
20898        foreach my $adr ( split( ' ', lc $this->{rcpt} ) ) {
20899            $adr{$adr} = 1;
20900          }
20901        foreach my $adr ( keys %adr ) {
20902
20903            next if localmail($adr) || !$adr;
20904            next if $Redlist{$adr};             # don't add to whitelist if rcpt is redlisted
20905
20906            next if $adr =~ s/^\'//;
20907			next if length($adr) > 127;
20908            #next if $whiteListedDomains && $adr=~$WLDRE;
20909            $this->{whitepassed} = 1;
20910           mlog( $fh, "auto whitelist addition: $adr", 1 )
20911              unless $Whitelist{$adr} || $NoAutoWhite;
20912
20913          $Whitelist{$adr} = $t unless !$Whitelist{$adr} && $NoAutoWhite;
20914
20915        }
20916	}
20917
20918	if($done) {
20919
20920		$OrgnamesCache{$this->{orgname}} = 0;
20921        $this->{getline}=\&getline;
20922 #       &addMyheader($fh) if $this->{myheader};
20923        &sayMessageOK($fh) if !$this->{spamfound};
20924    }
20925	sendque( $server, $l);
20926
20927
20928
20929}
20930
20931
20932# the message may or may not be spam -- get the body and test it.
20933
20934sub getbody {
20935    my ( $fh, $l ) = @_;
20936    my $this = $Con{$fh};
20937
20938    my ( $bomblt, $er );
20939    my $dataref;
20940    my $virusdataref;
20941    my $maxbytes;
20942    my $clamavbytes;
20943    my $mbytes;
20944    my $slok;
20945    $this->{datastart} = $this->{maillength} if (! $this->{datastart});
20946    $this->{maillength}+=length($l);
20947    $this->{header} .= $l;
20948
20949    $this->{headerlength} ||= getheaderLength($fh);
20950    $this->{relayok} = 1 if matchIP($this->{ip}, 'acceptAllMail',   0, 1 ) && !$this->{relayok};
20951
20952 	$maxbytes = $MaxBytes > 10000 ? $MaxBytes + $this->{headerlength} : 10000 + $this->{headerlength};
20953    $clamavbytes = $ClamAVBytes ? $ClamAVBytes + $this->{headerlength} : 50000 + $this->{headerlength};
20954    $clamavbytes = 100000 if $ClamAVBytes > 100000;
20955    $mbytes = $maxbytes;
20956    $mbytes = $clamavbytes if $clamavbytes > $mbytes  && ($BlockExes || $CanUseAvClamd && $AvailAvClamd) ;
20957
20958	my $done = $l =~ /^\.[\r\n]*$/o || defined( $this->{bdata} ) && $this->{bdata} <= 0;
20959
20960    if ( $done || $this->{maillength} >= $mbytes) {
20961        my $doneToError = $done || ($send250OK || ($send250OKISP && ($this->{ispip} or $this->{cip})));
20962
20963        $this->{skipnotspam} = 1;
20964
20965        $dataref = bodyWrap(\$this->{header},$maxbytes);
20966        $virusdataref = bodyWrap(\$this->{header},$clamavbytes);
20967
20968
20969        d( "getbody - done:$done maillength:$this->{maillength}" );
20970
20971		if ( !$this->{red} && $redRe && $$dataref =~ /($redReRE)/ )	{
20972            $this->{red} = ($1||$2);
20973
20974        }
20975        my $sub = $Con{$fh}->{subject3};
20976
20977        if (&NotSpamTagCheck($fh,$sub) ) {
20978
20979                        $this->{prepend} = '[NotSpamTag]';
20980						$this->{notspamtag} = 1;
20981                        $this->{noprocessing} = 1;
20982                        $this->{whitelisted} = 1 if $NotSpamTagAutoWhite;
20983                        $this->{passingreason} = "NotSpamTag";
20984                        my $adr = lc $Con{$fh}->{mailfrom};
20985            			$adr = batv_remove_tag($fh,$adr,'');
20986
20987            			if ($adr && length($adr) < 50 && !&localmailaddress($fh,$adr) &&  $adr !~ /^SRS/i && !$Con{$fh}->{red} && !$Redlist{$adr} && !$NoAutoWhite ) {
20988
20989
20990            				mlog( $fh, "whitelist addition: $adr by NotSpamTag" )  if $NotSpamTagAutoWhite;
20991
20992    						$Whitelist{$adr} = time if $NotSpamTagAutoWhite;
20993						}
20994
20995
20996            }
20997
20998		if (! $this->{relayok} &&
20999			! $this->{msgidsigdone} &&
21000			$this->{isbounce} &&
21001    		$DoMSGIDsig &&
21002            $CanUseSHA1 &&
21003    		! $this->{whitelisted} &&
21004            ! $this->{noprocessing} &&
21005            ! $this->{addressedToSpamBucket} &&
21006			$this->{header} =~ /([^\r\n]+\:)[\r\n\s]*\<$MSGIDpreTag\.(\d)(\d\d\d)(\w{6})\.([^\r\n]+)\>/ &&
21007			! $this->{from}
21008            &MSGIDsigCheck($fh)
21009           )
21010        {
21011            $this->{msgidsigdone} = 1;
21012
21013            $this->{noprocessing} = 1;
21014            $this->{prepend} = '[NoProcessing]';
21015            $this->{passingreason} = 'Valid MSGID signature';
21016            pbBlackDelete($fh,$this->{ip});
21017            pbWhiteAdd($fh,$this->{ip},"ValidMSGID");
21018
21019
21020    	}
21021
21022
21023		if ( !$this->{noprocessing}
21024			&& !$this->{whitelisted}
21025			&& !$this->{addressedToSpamBucket}
21026			&& $whiteRe) {
21027            WhiteOk($fh,$dataref);
21028        }
21029
21030		if ( !$this->{noprocessing} && $npRe
21031			&& !$this->{relayok}
21032        	&& !$this->{whitelisted}
21033        	&& !$this->{addressedToSpamBucket}
21034            && $npReRE != ""
21035            && $this->{header} =~  /($npReRE)/i )
21036        {
21037			mlogRe( $fh, $1, "npRe" );
21038            pbBlackDelete( $fh, $this->{ip} );
21039            $this->{noprocessing}  = 1;
21040            $this->{passingreason} = "npRe '$1'";
21041
21042        }
21043
21044        if (!$this->{noprocessing} && $npLocalRe
21045            && $this->{relayok}
21046            && $this->{header} =~ /($npLocalReRE)/i )
21047        {
21048			mlogRe( $fh,($1||$2), "npLocalRe" ,1);
21049
21050            $this->{noprocessing}  = 1;
21051            $this->{passingreason} = "npLocalRe '($1||$2)'";
21052
21053        }
21054
21055        if (&MessageScoreHigh($fh,10)) {
21056                	MessageScore( $fh, $doneToError );
21057                	return;
21058        			}
21059
21060
21061
21062		if ( !$this->{noprocessing} && !$this->{whitelisted} && $this->{allwhitelist} == 1 )
21063        {
21064			my $slok = $this->{allLoveSpam} == 1;
21065            $Stats{bspams}++ unless $slok;;
21066            delayWhiteExpire($fh);
21067
21068            $this->{prepend} = "[WhitelistOnly]";
21069			my $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
21070			$reply = replaceerror ($fh, $reply);
21071            thisIsSpam( $fh, "Whitelist Only Allowed",
21072                $baysSpamLog, $reply, $allTestMode, $slok, $doneToError );
21073
21074            return;
21075        }
21076
21077     	if (   $ccSpamNeverRe
21078        	&& !$this->{relayok}
21079            && $$dataref =~ ( '(' . $ccSpamNeverReRE . ')' ) ) {
21080            mlogRe( $fh, $1, "CCnever" );
21081            $this->{ccnever} = 1;
21082     	}
21083
21084    	if ( $this->{spamfound} ) {
21085
21086            return;
21087    	}
21088    	if ( haveToScan($fh) && !ClamScanOK( $fh, $virusdataref ) ) {
21089			my $slok = $this->{allLoveATSpam} == 1;
21090			$this->{newsletterre}		= '';
21091
21092			if ( $this->{messagereason} =~ /UNOFFICIAL/i ){
21093
21094      			thisIsSpam($fh,$this->{messagereason}, $SpamLog,$this->{averror}, 0,0,$doneToError);
21095      		} else {
21096      			thisIsSpam($fh,$this->{messagereason}, $SpamVirusLog,$this->{averror}, 0,0,$doneToError);
21097      		}
21098            return;
21099		} elsif ( haveToFileScan($fh) && !FileScanOK( $fh, $virusdataref ) ) {
21100			my $slok = $this->{allLoveATSpam} == 1;
21101			$this->{newsletterre}		= '';
21102            thisIsSpam( $fh, $this->{messagereason},
21103                $SpamVirusLog, $this->{averror}, 0,
21104                0, $doneToError );
21105            return;
21106
21107
21108    	}
21109
21110  		if (&MessageScoreHigh($fh,25)) {
21111                	MessageScore( $fh, 1 );
21112                	return;
21113 		}
21114
21115  		if (!PTROK($fh)) {
21116			my $slok=$this->{allLovePTRSpam}==1;
21117            my $reply = $SpamError;
21118            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
21119            $reply =~ s/REASON/$this->{messagereason}/go;
21120            $reply = replaceerror ($fh, $reply);
21121
21122         	thisIsSpam($fh,"$this->{messagereason}", $invalidSenderLog,$reply,$this->{testmode},$slok,$doneToError);
21123         	$this->{messagereason}="";
21124            return;
21125
21126		}
21127  		if (&MessageScoreHigh($fh,25)) {
21128                	MessageScore( $fh, 1 );
21129                	return;
21130 		}
21131 		if ( !BombBlackOK( $fh, $dataref ) ){
21132            delayWhiteExpire($fh);
21133            my $slok = $this->{allLoveBoSpam} == 1;
21134            $Stats{bombs}++ unless $slok;
21135            my $reply = $SpamError;
21136            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
21137            $reply =~ s/REASON/$this->{messagereason}/g;
21138            $reply = replaceerror ($fh, $reply);
21139            $this->{test} = "allTestMode";
21140            $this->{newsletterre}		= '';
21141            thisIsSpam( $fh, $this->{messagereason},
21142                $spamBombLog, $reply, $this->{testmode}, $slok, $doneToError );
21143            return;
21144		}
21145
21146        if ( !$AsASecondary && !BombOK($fh, $dataref) ) {
21147
21148            $slok = $this->{allLoveBoSpam} == 1;
21149            $slok = 0 if $this->{messagereason} =~ /bombCharSets/i;
21150            $Stats{bombs}++ unless $slok;
21151            delayWhiteExpire($fh);
21152
21153            my $reply = $SpamError;
21154            $reply = replaceerror ($fh, $reply);
21155            $reply =~ s/REASON/$this->{messagereason}/g;
21156            $reply = replaceerror ($fh, $reply);
21157
21158            thisIsSpam( $fh, $this->{messagereason},
21159                $spamBombLog, $reply, $this->{testmode}, $slok, $doneToError );
21160            return;
21161        }
21162
21163  		if (&MessageScoreHigh($fh,25)) {
21164                	MessageScore( $fh, 1 );
21165                	return;
21166 		}
21167
21168
21169        if ( $DoBlockExes
21170            && !CheckAttachments( $fh, $BlockExes, $dataref, $AttachLog, $doneToError)){
21171            return;
21172
21173        }
21174        if ( !URIBLok( $fh, $dataref, $this->{ip}, $doneToError ) ) {
21175            delayWhiteExpire($fh);
21176            return;
21177
21178        }
21179
21180  		if (&MessageScoreHigh($fh,25)) {
21181                	MessageScore( $fh, 1 );
21182                	return;
21183 		}
21184
21185        if ( !BayesOK( $fh, $dataref, $this->{ip} ) ) {
21186            $slok = $this->{allLoveBaysSpam} == 1;
21187
21188
21189
21190            $this->{testmode} = $slok = 0 if allSH( $this->{rcpt}, 'baysSpamHaters' );
21191            $this->{messagereason} = 'Bayesian';
21192            my $reply = $SpamError;
21193            $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
21194            $reply =~ s/REASON/$this->{messagereason}/g;
21195            $reply = replaceerror ($fh, $reply);
21196
21197            if ( !$slok ) { $Stats{bspams}++; }
21198			$this->{test} = "bayesTestMode";
21199            $this->{prepend} = "[Bayesian]";
21200            thisIsSpam( $fh, 'Bayesian', $baysSpamLog, $reply,
21201                $this->{testmode}, $slok, $doneToError );
21202            return;
21203
21204
21205        }
21206        if ($DoPenaltyMessage){
21207        	if ($MessageScoringUpperLimit
21208                && !$this->{whitelisted}
21209                && $this->{messagescore} >= ($MessageScoringUpperLimit ) ) {
21210
21211                	MessageScore( $fh, $done);
21212                	return;
21213
21214			} elsif ($MessageScoringLowerLimit
21215
21216               	&  !$this->{whitelisted}
21217                && $this->{messagescore} > $MessageScoringLowerLimit
21218                && $MessageScoringUpperLimit
21219
21220                && $this->{messagescore} < $MessageScoringUpperLimit ) {
21221
21222                	$this->{messagelow} = 1;
21223                	$this->{messagereason} = "MessageScore in warning range($this->{messagescore})";
21224					my $reply = $SpamError;
21225					$reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
21226					$reply =~ s/REASON/MessageScore/go;
21227            		$reply = replaceerror ($fh, $reply);
21228                	$this->{prepend} = "[MessageScore]";
21229
21230                	thisIsSpam( $fh, $this->{messagereason},  $spamMSLog, $reply,1 , 0, $done );
21231                	return;
21232        	}
21233        }
21234
21235
21236
21237            my $Spamlog;
21238            my $prepend;
21239            if ($this->{spamfound}) {
21240            	$this->{prepend} = "[SpamLover]";
21241            	$prepend = "spam passing";
21242            	$Spamlog = $SpamLog;
21243            } elsif ($this->{relayok}) {
21244            	$this->{prepend} = "[LocalOK]";
21245            	$prepend = "local";
21246            	$Stats{locals}++;
21247            	$Spamlog = $NonSpamLog;
21248            	$Spamlog = "" if $this->{attachcomment};
21249            } elsif ($this->{noprocessing}) {
21250            	$this->{prepend} = "[NoProcessingOK]";
21251            	$prepend = "noprocessing";
21252            	$Stats{noprocessing}++ if !$this->{relayok};
21253            	$Spamlog = $noProcessingLog;
21254            } elsif ($this->{whitelisted}) {
21255            	$this->{prepend} = "[WhitelistedOK]";
21256            	$prepend = "whitelisted";
21257            	$Stats{whites}++;
21258            	$Spamlog = $NonSpamLog;
21259
21260            } else {
21261            	$Spamlog = $baysNonSpamLog;
21262            	$this->{prepend} = "[MessageOK]";
21263            	$prepend = "message ok";
21264				$Stats{bhams}++
21265			}
21266
21267			addSpamProb( $fh) if !$this->{spamfound};
21268			$Spamlog = "" if $this->{spamfound};
21269			$Spamlog = "" if $this->{attachcomment};
21270            my $fn; $fn = Maillog( $fh, '', $Spamlog ) if $Spamlog;
21271
21272
21273            $fn = ' -> ' . $fn if !$fn == "";
21274            $fn = ""           if !$fileLogging && !$inclResendLink;
21275
21276
21277
21278
21279			&makeSubject($fh);
21280			my $logsub = ( $subjectLogging ? " $subjectStart$this->{subject3}$subjectEnd" : '' );
21281			my $pr = $this->{passingreason} ? " - $this->{passingreason} -" : '' ;
21282			my $ac = $this->{attachcomment} ? " - $this->{attachcomment} " : '' ;
21283			$this->{sayMessageOK} = "$prepend$pr$logsub$ac$fn";
21284			mlog( $fh, "$this->{sayMessageOK}" ) if $this->{spamfound};
21285
21286
21287			$OrgnamesCache{$this->{orgname}}= 0 if $OrgnamesCache{$this->{orgname}} < 2 && $this->{orgname};
21288            isnotspam( $fh, $done );
21289
21290    }
21291}
21292
21293
21294# checks for blocked attachments
21295sub CheckAttachments
21296{
21297    my ( $fh, $block, $bd, $attachlog, $done ) = @_;
21298    my $this = $Con{$fh};
21299    my @name;
21300
21301    return 1 unless $CanUseEMM;
21302    return 1 unless $DoBlockExes;
21303    return 1 if $this->{attachdone};
21304
21305
21306	my $msg = ref $bd ? $$bd : $bd;
21307    $this->{prepend} = "[Attachment]";
21308
21309    eval {
21310        $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
21311        my $email=Email::MIME->new($msg);
21312        if ($email->{ct}{composite} =~ /signed/io) {
21313
21314        }
21315        foreach my $part ( $email->parts ) {
21316            my $dis = $part->header("Content-Type") || '';
21317            my $attrs = $dis =~ s/^.*?;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
21318            my $name = $attrs->{name} || $part->{ct}{attributes}{name};
21319            my $filename = $attrs->{filename} || $part->{ct}{attributes}{filename};
21320            eval{$filename ||= $part->filename;};
21321            if (! $name || ! $filename) {
21322              eval{
21323                $dis = $part->header("Content-Disposition") || '';
21324                $attrs = $dis =~ s/^.*?;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
21325                $name ||= $attrs->{name} || $part->{ct}{attributes}{name};
21326                $filename ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
21327              };
21328            }
21329            if (($name||$filename) && $part->header("Content-Disposition")=~ /attachment|inline/io ) {
21330                my $attname = $filename || $name;
21331                $this->{attachcomment} = "attachment '$attname'";
21332                mlog($fh,"info:  found attachment '$attname'") if $AttachmentLog ;
21333                push(@name,($filename)?$filename:$name);
21334            }
21335        }
21336    };
21337    if ($@) {
21338        mlog($fh,"error: unable to parse message for attachments - $@",1) unless $IgnoreMIMEErrors;
21339        d("error: unable to parse message for attachments - $@") ;
21340    }
21341    my $numatt = @name;
21342    my $s; $s = 's' if ($numatt > 1);
21343    mlog($fh,"info: $numatt attachment$s") if ($AttachmentLog && $numatt > 1);
21344	$this->{attachcomment} = "$numatt attachment$s" if $numatt > 1;
21345	my $tlit = tlit($DoBlockExes);
21346	$block = $BlockExes;
21347
21348	#
21349	#
21350    if ($this->{noprocessing}) {
21351    	$block = $BlockNPExes;
21352	} elsif ($this->{relayok} ) {
21353    	$block = $BlockLCExes;
21354    } elsif ($this->{whitelisted} ) {
21355    	$block = $BlockWLExes;
21356    }
21357
21358    return 1 if !$block;
21359
21360
21361    my $bRE = $badattachRE[$block];
21362    foreach my $name (@name) {
21363        my $ext;
21364        eval{use bytes;($ext) = $1 if $name =~ /(\.[^\.]+)$/o;};
21365        if ( ( $block >= 1 && $block <= 3 && $ext =~ /$bRE/ ) ||
21366             ( $GoodAttach && $block == 4 && $ext !~ /$goodattachRE/  ) )
21367        {
21368            $this->{attachdone} = 1;
21369
21370
21371            if ($DoBlockExes == 1) {$Stats{viri}++;}
21372            delayWhiteExpire($fh) if $DoBlockExes == 1;
21373
21374            eval{$this->{messagereason} = "bad attachment '$name'";};
21375            $this->{attachcomment} = $this->{messagereason};
21376            $tlit = "[scoring:$baValencePB]" if $DoBlockExes != 2;
21377            mlog( $fh, "$tlit $this->{messagereason}" ) if ($DoBlockExes > 1 && $AttachmentLog);
21378            return 1 if $DoBlockExes == 2;
21379
21380            pbAdd( $fh, $this->{ip}, $baValencePB, "BadAttachment" ) if $DoBlockExes != 2;
21381
21382            return 1 if $DoBlockExes == 3;
21383
21384            my $reply = $AttachmentError;
21385            eval{$name = encodeMimeWord($name,'B','UTF-8') unless is_7bit_clean($name);
21386                 $reply =~ s/FILENAME/$name/go;
21387            };
21388            my $slok = $this->{allLoveATSpam} == 1;
21389            # Send attachment report to recipient if set
21390        &sendNotification ($this->{rcpt}, $this->{rcpt},"blocked bad attachment $name send from $this->{mailfrom}",$this->{messagereason})
21391        	if $AttachmentReportToRCPT && !$this->{relayok};
21392            thisIsSpam( $fh, $this->{messagereason}, $attachlog, $reply, $allTestMode, $slok, $done );
21393
21394
21395            return 0;
21396        }
21397    }
21398    return 1;
21399}
21400
21401
21402
21403# This is spam, lets see if its Testmode or spamlover.
21404sub replaceerror {
21405	my ( $fh, $error, $email) = @_;
21406	my $this = $Con{$fh};
21407	my ($to) = $this->{rcpt} =~ /(\S+)/;
21408    my $mfd; $mfd = $1 if $to =~ /\@(.*)/;
21409    $mfd = $1 if $DefaultDomain =~ /\@(.*)/ && !$mfd;
21410    $error = $SpamError if !$error;
21411
21412    $error =~ s/500/550/g;
21413    $error =~ s/LOCALDOMAIN/$mfd/g if $mfd;
21414    $error =~ s/LOCALDOMAIN/$defaultLocalHost/g if !$mfd;
21415
21416    $error =~ s/SESSIONID/$this->{msgtime}/g;
21417    $error =~ s/MYNAME/$myName/g;
21418
21419    $error =~ s/REASON/$this->{messagereason}/g;
21420    $error =~ s/NOTSPAMTAG/$NotSpamTag/g;
21421    $error =~ s/EMAILADDRESS/$email/g if $email;
21422
21423    return $error
21424    }
21425
21426
21427
21428sub addMyheader {
21429    my $fh = shift;
21430    my $this = $Con{$fh};
21431    d('addMyheader');
21432    my $var = $this->{addMyheaderTo} || 'header';
21433    return unless $this->{myheader};
21434
21435    my $foundEnd = my $headlen = index($this->{$var}, "\x0D\x0A\x0D\x0A");  # merge header
21436    $headlen = 0 if $headlen < 0;
21437    my $preheader = my $header = substr($this->{$var},0,$headlen);
21438    if ($this->{preheaderlength}) {    # we have added our headers before - now find the end of the orig header
21439        $this->{preheaderlength} -= 2; # step back two bytes  ("\x0D\x0A")
21440        $this->{preheaderlength} = 0 if $this->{preheaderlength} < 0;   # min offset is 0
21441        $this->{preheaderlength} = index($this->{$var}, "\x0D\x0A",$this->{preheaderlength});
21442        $this->{preheaderlength} = ( $this->{preheaderlength} < 0 ) ? 0 : $this->{preheaderlength} + 2;
21443        $preheader = substr($header,0,$this->{preheaderlength});
21444    }
21445    my $myheader = headerFormat($this->{myheader});
21446    $myheader =~ s/(?:\r|\n)+$//o;
21447    $myheader .= "\r\n" if $myheader;
21448    $preheader =~ s/(?:\r|\n)+$//o;
21449    $preheader .= "\r\n" if $preheader;
21450    $this->{preheaderlength} = length $preheader;
21451    my $newheader = $preheader . $myheader;
21452    if ($foundEnd >= 0) {
21453       $newheader =~ s/(?:\r|\n)+$//o;
21454    } elsif ($newheader) {
21455       $newheader .= "\r\n\r\n";
21456    }
21457
21458    substr($this->{$var},0,$headlen,$newheader);
21459    $this->{maillength} = length($this->{$var});
21460}
21461
21462sub makeMyheader {
21463    my ($fh,$slok,$testmode,$reason) = @_;
21464    my $this = $Con{$fh};
21465    d('makeMyheader');
21466    # add to our header; merge later, when client sent own headers
21467    $this->{myheader}="X-Assp-Version: $version$modversion on $myName\r\n" . $this->{myheader}
21468        if $this->{myheader} !~ /X-Assp-Version:.+? on $myName/;
21469    $this->{myheader}.= "X-Assp-ID: $myName $this->{msgtime}\r\n"
21470        if $this->{myheader} !~ /X-Assp-ID: $myName/;
21471    $this->{myheader}.="X-Assp-Redlisted: Yes ($this->{red})\015\012"
21472        if $this->{red} && $this->{myheader} !~ /X-Assp-Redlisted/o;
21473    $this->{myheader}.= "X-Assp-Spam: YES\r\n"
21474        if $this->{spamfound} && $AddSpamHeader && !$this->{messagelow} && $this->{myheader} !~ /X-Assp-Spam: YES/o;
21475    $this->{myheader}.= "X-Assp-Spam: YES (Probably)\r\n"
21476        if $this->{spamfound} && $AddSpamHeader && $this->{messagelow} && $this->{myheader} !~ /X-Assp-Spam: YES \(Probably\)/o;
21477    $this->{myheader}.="X-Assp-Block: NO (Spamlover)\r\n"
21478        if $this->{spamfound} && $slok && $this->{myheader} !~ /X-Assp-Block: NO \(Spamlover\)/o;
21479    $this->{myheader}.="X-Assp-Block: NO ($testmode)\r\n"
21480        if $this->{spamfound} && $testmode && !$this->{messagelow} && $this->{myheader} !~ /X-Assp-Block: NO \(\Q$testmode\E\)/;
21481
21482    $this->{myheader} .=
21483      "X-Assp-Block: NO (MessageScoring Warning Range)\r\n"
21484    	if $this->{messagelow};
21485
21486    $this->{myheader}.="$AddCustomHeader\r\n"
21487        if $this->{spamfound}  && $AddCustomHeader && $this->{myheader} !~ /\Q$AddCustomHeader\E/;
21488
21489    $this->{myheader}.="X-Assp-Original-Subject: $this->{subject2}\r\n"
21490        if $AddSubjectHeader && $this->{subject2} && $this->{myheader} !~ /X-Assp-Original-Subject:/;
21491
21492
21493    $this->{myheader}.="X-Assp-Spam-Found: ".$reason."\r\n"
21494        if $this->{spamfound} && $reason && $AddSpamReasonHeader;
21495
21496    if ($this->{spamfound} && $AddScoringHeader && $this->{messagescore} > 0) {
21497        $this->{myheader} =~ s/X-Assp-Message-Totalscore:[^\r\n]+?\r\n//iogs;
21498        $this->{myheader} .= "X-Assp-Message-Totalscore: $this->{messagescore}\r\n" if  $this->{myheader} !~ /Totalscore/i;
21499    }
21500}
21501
21502# This is spam, lets see if its test mode or spamlover.
21503sub thisIsSpam {
21504    my ( $fh, $reason, $log, $error, $testmode, $slok, $done ) = @_;
21505    my $this = $Con{$fh};
21506	if ($this->{orgname} !~ /($blackSenderBaseRE)/) {
21507		$OrgnamesCache{$this->{orgname}} = 1 if !$OrgnamesCache{$this->{orgname}};
21508		if  ($OrgnamesCache{$this->{orgname}} < $BlackOrgLimit) {
21509			$OrgnamesCache{$this->{orgname}}++ if $this->{orgname} && $OrgnamesCache{$this->{orgname}};
21510		}
21511	}
21512
21513    my $logsub;
21514    delayWhiteExpire($fh);
21515    return if $this->{spamdone};
21516   	$this->{spamdone} = 1;
21517	d("thisIsSpam - $reason , $testmode, $slok, $done");
21518
21519	if ($slMaxScore){
21520		#	$slok = 0 if $slMaxScore >= $MessageScoringUpperLimit && $this->{messagescore} > $slMaxScore;
21521
21522	}
21523
21524	if ($slok && $this->{spamMaxScore} > $MessageScoringUpperLimit && 		$this->{messagescore} > $this->{spamMaxScore}){
21525		my $oldprepend = $this->{prepend};
21526		$this->{prepend}="[SpamLover]";
21527		$this->{myheader}.="X-Assp-Spamlover: blocked because score ($this->{messagescore}) over spamMaxScore($this->{spamMaxScore} \r\n";
21528		mlog( $fh, "[spamlover found][blocked]  - score ($this->{messagescore}) over spamMaxScore($this->{spamMaxScore})");
21529		$this->{prepend}=$oldprepend;
21530	}
21531
21532	$slok = 0 if $this->{spamMaxScore} > $MessageScoringUpperLimit && 		$this->{messagescore} > $this->{spamMaxScore};
21533
21534	if ($this->{relayok}) {
21535		$Redlist{ lc $this->{mailfrom} } = time;
21536		$Redlist{ lc $this->{from} } = time;
21537		$this->{red} = $this->{mailfrom};
21538		delete $Whitelist{ lc $this->{mailfrom} };
21539		delete $Whitelist{ lc $this->{from} };
21540	}
21541
21542	$log = 7 if ($this->{red}||$this->{redsl}) && $DoNotCollectRed && $log == 3;
21543	$log = 6 if ($this->{red}||$this->{redsl}) && $DoNotCollectRed && $log == 1;
21544    my $reasonU8 = $reason;
21545    if ($reason && $LogCharset && $LogCharset !~ /^utf-?8/io) {
21546        $reason = Encode::decode('utf-8', $reason);
21547        $reason = Encode::encode($LogCharset, $reason);
21548    }
21549    $this->{messagereason}=$reason;
21550
21551
21552
21553    $error = $SpamError if !$error;
21554
21555    $error = replaceerror ($fh, $error);
21556
21557    if ( $reason =~ /bayes/i ) {
21558        if ( allSH( $this->{rcpt}, 'baysTestModeUserAddresses' ) ) {
21559            $testmode = "bayesian Testmode user";
21560            $slok = 1;
21561        }
21562    }
21563
21564
21565
21566
21567    addSpamProb( $fh );
21568    $this->{spamfound} = 1;    # Set spamfound flag.
21569	$testmode = 1 if $this->{testmode};
21570    $testmode = "testmode"        if $testmode;
21571    $testmode = "alltestmode" if $allTestMode;
21572    $testmode = $slok = $this->{spamloversre} = 0 if allSH( $this->{rcpt}, 'spamHaters' );
21573
21574
21575	$log = 7 if $slok && $log == 3;
21576	$log = 6 if $slok && $log == 1;
21577    # add to our header; merge later, when client sent own headers
21578	makeMyheader($fh,$slok,$testmode,$reasonU8);
21579
21580    my $passtext;
21581
21582    if (   ($slok
21583
21584        || $testmode
21585        || $this->{notspamtag}
21586
21587        || $this->{spamloversre}
21588
21589        || $this->{messagelow}) &&  $this->{prepend} !~ /virus/i)
21590    {
21591        $done = 1;
21592
21593        if ( $this->{messagelow} ) {
21594
21595            $this->{prepend}      	.= "$MessageScoringWarningTag" if  $MessageScoringWarningTag;
21596            $this->{saveprepend2} 	.= "$MessageScoringWarningTag" if  $MessageScoringWarningTag;
21597			$done = 1;
21598
21599            $passtext =
21600              "passing because messagescore($this->{messagescore}) is in warning range ( $MessageScoringLowerLimit - $MessageScoringUpperLimit) ";
21601
21602
21603        } elsif ($testmode) {
21604        	$testmode = $this->{test} if $this->{test};
21605            $this->{prepend}      .= 	"[$testmode]" ;
21606            $this->{saveprepend2} .= 	"[$testmode]" ;
21607            $done = 1;
21608       		$passtext = "passing because $testmode, otherwise blocked by: $reason";
21609
21610
21611        } elsif($this->{spamloversre}) {
21612            $this->{prepend}		.=	"$SpamLoverTag";
21613            $this->{saveprepend2}	.=	"$SpamLoverTag";
21614            $passtext="passing because match in \'SpamLoversRe:$this->{spamloversre}\'";
21615            $passtext .= ", otherwise blocked by: $reason";
21616
21617            $Stats{spamlover}++;
21618            $done = 1;
21619
21620        } elsif ($slok && !$this->{spamloverall} ) {
21621            $this->{prepend}      		.= 	"$SpamLoverTag";
21622            $this->{saveprepend2} 		.= 	"$SpamLoverTag";
21623			$this->{spamlover} = 1;
21624            $passtext =
21625              "passing because spamlover for this check, otherwise blocked by: $reason";
21626
21627            $Stats{spamlover}++;
21628 			$done = 1;
21629 		} elsif($this->{spamloverall}) {
21630            $this->{prepend}		.="$SpamLoverTag";
21631            $this->{saveprepend2}	.="$SpamLoverTag";
21632            $passtext="passing because spamlover for all filters set";
21633            $passtext .= ", otherwise blocked by: $reason";
21634            $Stats{spamlover}++;
21635            $done = 1
21636
21637
21638       	} elsif ($this->{notspamtag}) {
21639
21640            $this->{prepend}      .= 	"[notspamtag]" ;
21641            $this->{saveprepend2} .= 	"[notspamtag]" ;
21642            $done = 1;
21643       		$passtext = "passing because NotSpamTag, otherwise blocked by: $reason";
21644        }
21645
21646
21647        # pretend it's not spam
21648        eval {
21649        $this->{header} =~ s/^($HeaderRe*)/$1From: $this->{mailfrom}\r\n/o
21650          unless $this->{header} =~ /^$HeaderRe*From:/io; # add From: if missing
21651
21652    	my ($to) = $this->{rcpt} =~ /(\S+)/;
21653    	$this->{header} =~ s/^($HeaderRe*)/$1To: $to\r\n/o
21654          unless $this->{header} =~ /^$HeaderRe*To:/io; # add To: if missing
21655#        $this->{header} =~ s/^($HeaderRe*)/$1Subject:\r\n/o
21656#          unless $this->{header} =~
21657#              /^$HeaderRe*Subject:/io;    # add Subject: if missing
21658
21659		if (($slok && $spamTagSL) or $this->{messagelow}) {
21660        } else {
21661            $this->{header} =~ s/^Subject:/Subject: $this->{prepend}/im
21662              if ( $spamTag && $this->{prepend} ne '' && $this->{header} !~ /Subject: \Q$this->{prepend}\E/i);
21663        }
21664
21665        if ( $slok && ($spamSubjectSL or $this->{subjectsl}) or $this->{messagelow} ) {
21666        } else {
21667
21668$this->{header} =~ s/^Subject:/Subject: $spamSubjectEnc/imo
21669              if $spamSubjectEnc && $this->{header} !~ /Subject: \Q$spamSubjectEnc\E/i;
21670        }
21671
21672		if ($this->{messagelow}) {
21673
21674		$this->{header} =~ s/^Subject:/Subject: $MessageScoringWarningTag/im if $MessageScoringWarningTag;
21675
21676		}
21677
21678		};
21679                #Lets check if its safe to pass if not already done so.
21680		$this->{spamlover}="";
21681		$this->{spampassed} = 1;
21682		if ($done) {
21683
21684            my $fn = Maillog( $fh, '', $log );    # tell maillog what this is.
21685            $fn = ' -> ' . $fn if $fn;
21686            mlog( $fh, "[spam found] and $passtext -- $this->{logsubject}$fn;", 0, 2 );
21687
21688            isnotspam( $fh, "1" );
21689        } else {
21690            $this->{getline} = \&getbody;
21691        }
21692    } else {
21693		if ($send250OK or ($this->{ispip} && $send250OKISP)) {
21694                        my $fn = $this->{maillogfilename};   # store the mail if we have to receive it
21695                        unless ($fn) {
21696                            $fn = Maillog($fh,'',6); # tell maillog what this is -> discarded.
21697                        }
21698                        $fn=' -> '.$fn if $fn ne '';
21699                        $fn='' if !$fileLogging;
21700                        my $logsub = ( $subjectLogging && $this->{originalsubject} ? " $subjectStart$this->{originalsubject}$subjectEnd" : '' );
21701                        mlog($fh,"[spam found] $this->{messagereason}$logsub".de8($fn),0,2);
21702                        $this->{getline} = \&NullData;
21703        } else {
21704			$this->{spamblocked} = 1;
21705			$log = 1  if !$log;
21706       		 my $fn = Maillog( $fh, '', $log );    # tell maillog what this is.
21707        	$fn = ' -> ' . $fn if $fn;
21708        	$this->{prepend} .= '[isbounce]' if $this->{isbounce} && $this->{prepend} !~ /\[isbounce\]/o  ;
21709        	mlog( $fh, "[spam found][blocked] -- $reason -- $this->{logsubject}$fn;", 0, 2 );
21710        	delayWhiteExpire($fh);
21711			$error=~s/500/554/io;
21712        	seterror( $fh, $error, $done);
21713        }
21714
21715    }
21716}
21717
21718
21719
21720
21721# delete whitelisted tuplet
21722sub delayWhiteExpire {
21723	my $fh   = shift;
21724    return unless $fh;
21725    my $this = $Con{$fh};
21726    d('delayWhiteExpire');
21727	my $ip = $this->{ip};
21728	$ip = $this->{cip} if $this->{ispip} && $this->{cip};
21729
21730	pbWhiteDelete( $fh, $ip );
21731
21732	return unless ( $EnableDelaying && $DelayExpireOnSpam );
21733	my $mf = lc $this->{mailfrom};
21734
21735	# get sender domain
21736	$mf =~ s/[^@]*@//o;
21737	my $ipn = &ipNetwork( $ip, $DelayUseNetblocks );
21738	my $hash = "$ipn $mf";
21739	$hash = Digest::MD5::md5_hex($hash) if $CanUseMD5 && $DelayMD5;
21740    my $DelayWhite_hash = $DelayWhite{$hash};
21741    if ( $DelayWhite_hash ) {
21742		# delete whitelisted (IP+sender domain) tuplet
21743		mlog(	$fh, "deleting spamming whitelisted tuplet: ($ipn,$mf) age: "
21744				. formatTimeInterval( time - $DelayWhite_hash ), 1 ) if $DelayLog;
21745		delete $DelayWhite{$hash};
21746    }
21747}
21748
21749# add to penalty box
21750sub pbAdd {
21751
21752    # status:
21753    # 0-message score and pbblackadd
21754    # 1-message score but don't pbblackadd
21755    # 2-pbblackadd but don't message score
21756    # noheader:
21757    # 0-write X-Assp header info
21758    # 1-skip X-Assp header info
21759    my($fh,$myip,$score,$reason,$status,$noheader)=@_;
21760    return unless $fh;
21761    my $this = $Con{$fh};
21762
21763    return 1 if $this->{notspamtag};
21764    my @score;
21765    if ($this->{noprocessing} &&
21766    		($score =~ /irValencePB$/o
21767    		|| $score =~ /meValencePB$/o)) {
21768    	return;
21769    }
21770    if ($score =~ /ValencePB$/o) {
21771       defined ${chr(ord(",") << 1)} and @score = @{$score};
21772    } elsif ($score = 0+$score) {
21773       push @score, $score, $score;
21774    } else {
21775       return;
21776    }
21777
21778    return if $status && ! $score[$status - 1];
21779    return if ! $status && ! max(@score);
21780    $myip = $this->{cip} if $this->{ispip} && $this->{cip} && $myip eq $this->{ip};
21781    my $reason2=$reason;
21782    $reason2=$this->{messagereason} if $this->{messagereason};
21783    if ( ! $noheader ) {
21784        $this->{myheader}.="X-Assp-Score: $score[0] ($reason2)\r\n" if $AddScoringHeader && $status < 2 && $score[0];
21785
21786    }
21787    $this->{messagescore} = 0 unless $this->{messagescore};
21788    if ($score[0] && $status != 2) {
21789        $this->{messagescore} += $score[0];
21790        my $added = $score =~ /ValencePB$/o ? "$score[0] ($score)" : $score[0];
21791        mlog($fh,"Message-Score: added $added for $reason2, total score for this message is now $this->{messagescore}",1) if ($MessageLog || $PenaltyLog>=2);
21792    }
21793
21794    return if ($status == 1);
21795    return if $this->{relayok};
21796    return unless $score[1];
21797
21798    return if $this->{ispip} && !$this->{cip};
21799
21800
21801    return if pbWhiteFind($myip);
21802    return if (matchIP($myip,'noPB',0,1));
21803    return if ($myip =~ /$IPprivate/o);
21804
21805    pbBlackAdd($fh,$myip,$score[1],$reason);
21806
21807
21808}
21809
21810#
21811sub pbBlackAdd {
21812
21813    my ( $fh, $myip, $score ,$reason, $subreason) = @_;
21814    return if $score <= 0;
21815    my $this = $Con{$fh};
21816    return if $this->{relayok};
21817    return if $this->{addressedToSpamBucket};
21818    return if $this->{messagelow};
21819    $myip = $this->{cip} if $this->{ispip} && $this->{cip};
21820    return if $myip =~ /$IPprivate/ ;
21821	($reason) = $this->{prepend} =~ /\[(.*)\]/ if !$reason;
21822	return if $reason =~ /extreme/i;
21823
21824	my $isblocked; $isblocked = 1 if $score == 1;
21825
21826    my $t = time;
21827    my $newscore;
21828    my $ip = ipNetwork( $myip, $PenaltyUseNetblocks );
21829
21830    if ( exists $PBBlack{$ip} ) {
21831        my ( $ct, $ut, $blockedcounter, $oldscore, $sip, $sreason, $ssubreason) =
21832          split( " ", $PBBlack{$ip} );
21833
21834        $blockedcounter++ if $isblocked;
21835
21836        $newscore = $oldscore + $score if $sreason !~ /preheader/i;
21837        if ( $newscore <= 0 ) {
21838
21839            delete $PBBlack{$ip};
21840            return;
21841        }
21842        $PBBlack{$ip} = "$ct $t $blockedcounter $newscore $myip $reason , $subreason";
21843
21844
21845    } else {
21846        return if $score <= 0 ;
21847        my $blockedcounter = 0;
21848        $blockedcounter++ if $isblocked;
21849
21850        $PBBlack{$ip} = "$t $t $blockedcounter $score $myip $reason";
21851
21852
21853    }
21854
21855
21856}
21857
21858# find in penalty White list
21859sub pbWhiteFind {
21860    return if !$DoPenalty;
21861
21862    my $myip = shift;
21863    my $t = time;
21864    return unless ($PBWhiteObject);
21865    my $ip = ipNetwork( $myip, $PenaltyUseNetblocks );
21866    return 0 if !exists $PBWhite{$ip};
21867     if ( matchIP( $myip, 'noPBwhite', 0, 1 )) {
21868        delete $PBWhite{$ip};
21869        delete $PBWhite{$myip};
21870        return 0;
21871        }
21872
21873    my ($ct,$ut,$status)=split(' ',$PBWhite{$ip});
21874    my $data="$ct $t $status";
21875    $PBWhite{$ip}=$data;;
21876    return 1;
21877
21878}
21879#
21880sub pbBlackDelete {
21881
21882    my($fh,$myip)=@_;
21883    my $this=$Con{$fh};
21884    $myip = $this->{cip} if $this->{ispip} && $this->{cip};
21885    my $ip=&ipNetwork($myip, $PenaltyUseNetblocks );
21886
21887    if ( exists $PBBlack{$ip} ) {
21888        delete $PBBlack{$ip};
21889        delete $PBBlack{$myip};
21890
21891    }
21892
21893}
21894
21895#
21896sub pbBlackFind {
21897
21898    my ( $myip, $count ) = @_;
21899
21900    return unless ($PBBlackObject);
21901    my $ip = ipNetwork( $myip, $PenaltyUseNetblocks );
21902    return 0 if matchIP( $myip, 'noPB', 0, 1 );
21903    return 0 if ( !exists $PBBlack{$ip} );
21904    my $t = time;
21905    my ( $ct, $ut, $level, $totalscore, $sip, $reason);
21906    ( $ct, $ut, $level, $totalscore, $sip, $reason) =  split( " ", $PBBlack{$ip} ) if ( exists $PBBlack{$ip} );
21907
21908    my $data = "$ct $t $level $totalscore $myip $reason";
21909    $PBBlack{$ip} = $data;
21910
21911
21912    return $totalscore;
21913}
21914
21915
21916sub pbTrapAdd {
21917    my ( $fh, $address ) = @_;
21918    my $this = $Con{$fh};
21919	my $at_position = index($address, '@');
21920  	my $current_username = substr($address, 0, $at_position);
21921  	my $current_domain = substr($address, $at_position + 1);
21922    return if !$DoPenaltyMakeTraps;
21923    return 1 if $DoLDAP && $LDAPoffline;
21924	return if $this->{userTempFail} && $DoVRFY && &matchHashKey('DomainVRFYMTA',$current_domain);
21925
21926	return if $this->{whitelisted};
21927	return if $this->{relayok};
21928	return if $this->{nocollect};
21929	return if $this->{noprocessing};
21930    return if $noProcessingIPs && matchIP( $this->{ip}, 'noProcessingIPs' ) && !$this->{NPexcludeIPs};
21931
21932    return if ( $whiteListedIPs && matchIP( $this->{ip}, 'whiteListedIPs' ) );
21933    return if matchSL( $address, 'noPenaltyMakeTraps',1 );
21934    return if $spamtrapaddresses && matchSL( $address, 'spamtrapaddresses',1 );
21935    return if $spamaddresses && matchSL( $address, 'spamaddresses' ,1);
21936
21937    return if matchIP( $this->{ip}, 'noPB', 0, 1 );
21938    my $t = time;
21939
21940     if (my($ct,$ut,$counter)=split(' ',$PBTrap{$address})) {
21941        $counter++;
21942        my $data="$ct $t $counter";
21943        $PBTrap{$address}=$data;
21944    } else {
21945        my $data="$t $t 1";
21946        $PBTrap{$address}=$data;
21947    }
21948}
21949
21950#
21951#
21952sub pbTrapDelete {
21953
21954    my $address = shift;
21955    delete $PBTrap{$address};
21956}
21957
21958sub pbTrapFind {
21959    my ( $fh, $address ) = @_;
21960    my $this = $Con{$fh};
21961    my $t = time;
21962    my $data;
21963    my $found=0;
21964    return unless ($PBTrapObject);
21965    return 0 if (!$DoPenaltyMakeTraps || $DoPenaltyMakeTraps == 2);
21966
21967	return if $this->{whitelisted};
21968	return if $this->{relayok};
21969	return if $this->{nocollect};
21970	return if $this->{noprocessing};
21971	return if $noProcessingIPs && matchIP( $this->{ip}, 'noProcessingIPs' ) && !$this->{NPexcludeIPs};
21972
21973    return if ( $whiteListedIPs && matchIP( $this->{ip}, 'whiteListedIPs' ) );
21974	if (matchSL($address,'noPenaltyMakeTraps')) {
21975        pbTrapDelete($address);
21976        return 0;
21977    }
21978
21979
21980    if ( exists $PBTrap{$address} ) {
21981        my ( $ct, $ut, $counter ) = split( " ", $PBTrap{$address} );
21982            if ( time - $ct >= $PBTrapCacheInterval * 3600 ) {
21983            	delete $PBTrap{$address};
21984            	return 0;
21985            }
21986        $counter++;
21987
21988        $data = "$ct $t $counter";
21989        if ($counter >= $PenaltyMakeTraps) {
21990
21991        	$found=1;
21992        	$data = "$t $t 0 " if $DoPenaltyMakeTraps == 2;
21993
21994       	}
21995       	$PBTrap{$address} = $data;
21996       	return $found;
21997
21998    }
21999    return 0;
22000}
22001
22002sub pbTrapExist {
22003    my ( $fh, $address ) = @_;
22004    my $this = $Con{$fh};
22005    my $t = time;
22006
22007	return if $this->{whitelisted};
22008	return if $this->{relayok};
22009	return if $this->{nocollect};
22010	return if $this->{noprocessing};
22011	return if $noProcessingIPs && matchIP( $this->{ip}, 'noProcessingIPs' ) && !$this->{NPexcludeIPs};
22012
22013    return if ( $whiteListedIPs && matchIP( $this->{ip}, 'whiteListedIPs' ) );
22014	return if matchSL( $address, 'noPenaltyMakeTraps' );
22015    return unless ($PBTrapObject);
22016
22017    return 1 if exists $PBTrap{$address};
22018    return 0;
22019}
22020sub pbWhiteAdd {
22021    my($fh,$myip,$reason)=@_;
22022    my $this=$Con{$fh};
22023    $myip = $this->{cip} if $this->{ispip} && $this->{cip};
22024    my $t = time;
22025    my $ct = $t;
22026    my $status = 2;
22027    my $ut;
22028
22029
22030    return if $this->{isbounce};
22031    return if $this->{ispip} && !$this->{cip};
22032    my $ip = &ipNetwork($myip, $PenaltyUseNetblocks);
22033    if ( matchIP( $myip, 'noPBwhite', 0, 1 )) {
22034        delete $PBWhite{$ip};
22035        delete $PBWhite{$myip};
22036        return;
22037    }
22038    my ($ct,$ut,$status);
22039    ($ct,$ut,$status)=split(' ',$PBWhite{$ip}) if (exists $PBWhite{$ip});
22040    my $data="$ct $t $status";
22041
22042    $PBWhite{$ip}=$data;
22043}
22044
22045
22046
22047#
22048sub pbWhiteDelete {
22049    my($fh,$myip)=@_;
22050    $Con{$fh}->{rwlok}=0 if $fh;
22051
22052    my $ip=&ipNetwork($myip,$PenaltyUseNetblocks);
22053    delete $PBWhite{$ip};
22054    delete $PBWhite{$myip};
22055}
22056
22057
22058#
22059sub URIBLCacheAdd {
22060    my($mydomain,$status,$mylisted)=@_;
22061    $mylisted = ' '. $mylisted if $mylisted;
22062    return 0 if !$URIBLCacheInterval;
22063
22064    $mylisted =~ s/$mydomain\.//g;
22065    $URIBLCache{$mydomain}=time . " $status$mylisted";
22066}
22067
22068sub URIBLCacheFind {
22069    my $mydomain = shift;
22070    my $t=time;
22071    return 0 if !$URIBLCacheInterval;
22072    return 0 unless ($URIBLCacheObject);
22073    if (my($ct,$status,@listed)=split(' ',$URIBLCache{$mydomain})) {
22074        my $data = "$t $status @listed";
22075        $URIBLCache{$mydomain}=$data;
22076        return $status;
22077    }
22078    return 0;
22079}
22080
22081sub PTRCacheAdd {
22082    return 0 if !$PTRCacheInterval;
22083    my($myip,$status,$ptrdsn)=@_;
22084    my $t=time;
22085    my $data="$t $status $ptrdsn";
22086    $PTRCache{$myip}=$data;
22087}
22088
22089sub PTRCacheFind {
22090    my($myip,$mystatus)=@_;
22091    my $t=time;
22092    return 0 if !$PTRCacheInterval;
22093    return 0 unless ($PTRCacheObject);
22094    if ( exists $PTRCache{$myip} ) {
22095        my ( $ct, $status, $ptrdsn) = split( " ", $PTRCache{$myip} );
22096
22097        my $data = "$t $status $ptrdsn";
22098        $PTRCache{$myip}=$data;
22099        return $status;
22100    }
22101    return 0;
22102}
22103
22104
22105#
22106sub RWLCacheAdd {
22107    my($myip,$status)=@_;
22108    return 0 unless ($RWLCacheObject);
22109    return 0 if !$RWLCacheInterval;
22110    return 0 unless $myip;
22111
22112    $RWLCache{$myip}=time . " $status";
22113}
22114
22115sub RWLCacheFind {
22116    my $myip = shift;
22117    return 0 if !$RWLCacheInterval;
22118    return 0 unless ($RWLCacheObject);
22119    return 0 unless $myip;
22120    if (my($ct,$status)=split(/\s+/o,$RWLCache{$myip})) {
22121        return $status;
22122    }
22123    return 0;
22124}
22125
22126
22127#
22128sub BackDNSCacheAdd {
22129    my($myip,$status)=@_;
22130
22131    return 0;
22132}
22133sub BackDNSCacheFind {
22134    my $myip = shift;
22135
22136    return 0;
22137}
22138
22139sub MXACacheAdd {
22140    my ( $mydomain, $mxrecord, $arecord ) = @_;
22141    return 0 if !$MXACacheInterval;
22142    return 0 unless ($MXACacheObject);
22143
22144    $MXACache{lc $mydomain} = time . " $mxrecord $arecord";
22145}
22146
22147sub MXACacheFind {
22148    my $mydomain = lc shift;
22149    return 0 if !$MXACacheInterval;
22150    return 0 unless ($MXACacheObject);
22151    return split( ' ', lc $MXACache{$mydomain}, 3 );
22152    my ( $cachetime, $mxrecord, $arecord ) = MXACacheFind($mydomain);
22153    $MXACache{lc $mydomain} = time . " $mxrecord $arecord";
22154    return split( ' ', lc $MXACache{$mydomain}, 3 );
22155}
22156
22157sub SPFCacheAdd {
22158    my ( $myip, $result, $domain, $record ) = @_;
22159    my $bip=&ipNetwork($myip, $DelayUseNetblocks );
22160    return 0 if !$SPFCacheInterval;
22161    return unless ($SPFCacheObject);
22162    $record = "'$record'" if $record;
22163
22164    $SPFCache{"$myip $domain"} = time . lc " $result $record";
22165}
22166
22167sub SPFCacheFind {
22168    my ($myip,$domain) = @_;
22169    my $bip=&ipNetwork($myip, $DelayUseNetblocks );
22170    return if !$SPFCacheInterval;
22171    return unless ($SPFCacheObject);
22172    return unless $domain;
22173
22174    return split( ' ', lc $SPFCache{"0.0.0.0 $domain"} ) || split( ' ', lc $SPFCache{"$bip $domain"} ) ;
22175}
22176
22177sub WhiteOrgList {
22178
22179        %WhiteOrgList = ();
22180        d('build WhiteOrgList from Senderbase-Cache');
22181        while (my ($k,$v)=each(%SBCache)) {    # load WhiteOrgList from SBCache
22182            if ($v !~ /\!/o or $k !~ /\//o) {
22183                delete $SBCache{$k};
22184                next;
22185            }
22186            my ( $ct, $status, $data ) = split( /!/o, $v );
22187            my ( $ipcountry, $orgname, $domainname, $blacklistscore, $hostname_matches_ip, $ipCIDR ) = split( /\|/o, $data ) ;
22188            $WhiteOrgList{lc $domainname} = $orgname if ($status == 2 && $domainname && $orgname);
22189        }
22190
22191}
22192sub SBCacheAdd {
22193
22194    my ( $myip, $status, $data ) = @_;
22195	return if !$SBCacheObject;
22196	return 0 if !$SBCacheExp;
22197    return 0 unless $myip;
22198    my ( $ipcountry, $orgname, $domainname, $blacklistscore, $hostname_matches_ip, $cidr ) = split( /\|/o, $data );
22199    my $t = time;
22200    $cidr ||= (32 - $PenaltyUseNetblocks * 8);
22201    $cidr = 8 if $cidr < 8;
22202	my $ipcidr = ipNetwork($myip,$cidr)."/$cidr";
22203    $SBCache{ ipNetwork($myip,$cidr)."/$cidr" } = "$t!$status!$data";
22204
22205    return $status;
22206}
22207#
22208sub SBCacheFind {
22209    my ( $myip, $fh ) = @_;
22210    my $fh = shift;
22211    return if !$SBCacheExp;
22212    return if !$SBCacheObject;
22213    return 0 unless $myip;
22214    my $val;
22215    my $cidr;
22216    my $ip;
22217    for ( $cidr = unpack("A1",${chr(ord("\026") << 2)})**5;
22218          $cidr >= unpack("A1",${chr(ord("\026") << 2)})**3;
22219          $cidr--)
22220    {
22221        $ip = ipNetwork($myip,$cidr);
22222        last if ($val = $SBCache{"$ip/$cidr"});
22223    }
22224    return unless $val;                #ct status data                  data only
22225    return wantarray ? ("$ip/$cidr", split( /!/o, $val )) : [split( /!/o, $val )]->[2];
22226}
22227
22228sub SBCacheChange {
22229    my ( $myip, $newstatus ) = @_;
22230    return 0 if !$SBCacheExp;
22231    return 0 if !$SBCacheObject;
22232    return 0 unless $myip;
22233    my @res = SBCacheFind($myip);
22234    return 0 unless @res;
22235    my $record = shift @res;
22236    my ( $ct, $status, $data ) = @res;
22237    return 0 if $status == $newstatus;
22238    SBCacheAdd($myip,$newstatus,$data);
22239    return 1;
22240}
22241
22242sub getClassCNetworkList {
22243    my ($ip, $mask) = @_;
22244    return $ip unless $mask;
22245    return $ip unless $CanUseCIDRlite;
22246    if ($mask =~ /\./o) {
22247        my @bytes = split /\./o, $mask;
22248        $mask = 0;
22249        for (@bytes) {
22250            my $bits = unpack( "B*", pack( "C", $_ ) );
22251            $mask += $bits =~ tr /1/1/;
22252        }
22253    }
22254    return $ip if $mask > 24;
22255    my @cidr_list;
22256    eval{
22257        my $cidr = unpack("A1",${chr(ord("\026") << 2)})-2;
22258        $cidr ||= Net::CIDR::Lite->new;
22259        $cidr->add("$ip/$mask");
22260        @cidr_list = $cidr->list_short_range;
22261    };
22262    s/-255//o for (@cidr_list);
22263    push @cidr_list, $ip unless @cidr_list;
22264    return @cidr_list;
22265}
22266
22267sub SpamBucketOK {
22268    my ( $fh, $done ) = @_;
22269    my $this = $Con{$fh};
22270    d("SpamBucket - $this->{addressedToSpamBucket} ");
22271
22272    $this->{prepend} = "[SpamBucket]";
22273	$this->{messagereason} =
22274      "'$this->{addressedToSpamBucket}' in spamaddresses";
22275
22276    $Stats{spambucket}++;
22277
22278    thisIsSpam($fh,$this->{messagereason},$spamBucketLog,"250 OK",0,0, $done );
22279}
22280sub MessageScoreHigh {
22281    my ( $fh, $score ) = @_;
22282    my $this = $Con{$fh};
22283    my $highscore;
22284    return 0 if !$this->{headerpassed};
22285    return 0 if !$DoPenaltyMessage;
22286    return 0 if $MsgScoreOnEnd;
22287    return 0 if $this->{notspamtag};
22288    return 0 if !$MessageScoringUpperLimit;
22289    return 0 if $this->{messagescoredone};
22290	return 0 if $this->{whitelisted} && !$MessageScoringWL;
22291	return 1 if $this->{messagescore} > $MessageScoringUpperLimit && $this->{messagescore} > $this->{spamMaxScore};
22292}
22293sub MessageScore {
22294    my ( $fh, $done ) = @_;
22295    my $this = $Con{$fh};
22296    return 0 if !$DoPenaltyMessage;
22297	return 0 if $this->{whitelisted} && !$MessageScoringWL;
22298	$this->{messagescoredone}=1;
22299    d("MessageScore - score: $this->{messagescore} - limit: $MessageScoringUpperLimit");
22300    my 	$reason = $this->{prepend};
22301    $this->{prepend} = "[MessageScore]";
22302  	$this->{messagereason} =
22303      "Scoring($this->{messagescore}) surpassed limit($MessageScoringUpperLimit)";
22304    my $slok = $this->{allLoveSpam} == 1;
22305    my $mDoPenaltyMessage = $DoPenaltyMessage;
22306    $mDoPenaltyMessage = 1 if $DoPenaltyMessage == 4;
22307    $this->{testmode} = 1 if $DoPenaltyMessage == 4 or $allTestMode;
22308
22309    my $reply = $SpamError;
22310    $reply = ($this->{relayok}) ? $SpamErrorLocal : $SpamError;
22311	$reply =~ s/REASON/$this->{messagereason}/go;
22312    $reply = replaceerror ($fh, $reply);
22313
22314    my $log = $spamMSLog;
22315
22316    my $isdone = 0;
22317
22318    delayWhiteExpire($fh);
22319    mlog( $fh, "[monitoring] -- $this->{messagereason} -- $this->{logsubject}", 1 ) if $DoPenaltyMessage == 2;
22320    return if $DoPenaltyMessage == 2;
22321    $Stats{msgscoring}++ if !$slok;
22322
22323	#pbBlackAdd($fh,$this->{ip},$this->{messagescore},"MessageScore");
22324
22325    thisIsSpam($fh,$this->{messagereason},$spamMSLog,$reply,$this->{testmode},$slok, 0 );
22326}
22327
22328# kill the connection
22329#
22330sub addSMTPfailed {
22331    my  $ip = shift;
22332    return if !$ip;
22333    return if matchIP( $ip, 'acceptAllMail',   0, 1 );
22334    my $ipnet = ipNetwork($ip, 1);
22335    d("addSMTPfailed : $ip");
22336	my $time = &timestring();
22337	$SMTPfailed{$ip} = $time;
22338	$SMTPfailed{$ipnet} = $time;
22339
22340}
22341
22342sub findSMTPfailed {
22343    my  $ip = shift;
22344
22345    return 0 if !$ip;
22346	my $ipnet = ipNetwork($ip, 1);
22347    d("findSMTPfailed : $ip lookup");
22348	return $SMTPfailed{$ip} if exists $SMTPfailed{$ip};
22349	return $SMTPfailed{$ipnet} if exists $SMTPfailed{$ipnet};
22350	d("findSMTPfailed : $ip clean");
22351	return 0;
22352}
22353sub killsmtperror {
22354    my ( $tmpfh) = @_;
22355    d("killconnection : $Con{$tmpfh}->{ip}");
22356    my $this = $Con{$tmpfh};
22357
22358	if ($Con{$tmpfh}->{getline} != \&error) {
22359          seterror($Con{$tmpfh}->{client},"501 Syntax: helo needs hostname\r\n",1);
22360    } else {
22361          sendque($Con{$tmpfh}->{client},"501 Syntax: helo needs hostname\r\n");
22362
22363          unpoll($Con{$tmpfh}->{client}, $readable);
22364    }
22365}
22366# kill the connection
22367sub killconnection {
22368    my ( $tmpfh) = @_;
22369    d("killconnection : $Con{$tmpfh}->{ip}");
22370    my $this = $Con{$tmpfh};
22371	&addSMTPfailed($Con{$tmpfh}->{ip});
22372	if ($Con{$tmpfh}->{getline} != \&error) {
22373          seterror($Con{$tmpfh}->{client},"451 Connection timeout, try later\r\n",1);
22374    } else {
22375          sendque($Con{$tmpfh}->{client},"451 Connection timeout, try later\r\n");
22376          $Con{$tmpfh}->{closeafterwrite} = 1;
22377          unpoll($Con{$tmpfh}->{client}, $readable);
22378    }
22379}
22380# reject the email
22381sub seterror {
22382    my($fh,$e,$done)=@_;
22383    d('seterror');
22384
22385    my $this=$Con{$fh};
22386    $done = 1 if ($this->{lastcmd} !~ /^DATA/io &&       # end the connection if not send 250 and we are not in DATA part
22387                  ((! $send250OK && $this->{relayok}) ||
22388                  (($this->{ispip} || $this->{cip}) && ! $send250OKISP )));
22389    $done = 0 if ($this->{header} &&                    # receive the message if send 250 and we have still received data
22390                  $this->{header} !~ /\x0D?\x0A\.(?:\x0D?\x0A)+$/o  &&
22391                  $this->{lastcmd} =~ /^DATA/io &&
22392                  ($send250OK || (($this->{ispip} || $this->{cip}) && $send250OKISP )));
22393    $this->{error}=$e;
22394    $done = 1 if $e =~ /^4/o;          # end the connection if the error Reply starts with 4xx
22395    if($done) {
22396        error($fh,".\r\n");
22397    } else {
22398        $this->{getline}=\&error;
22399    }
22400# detatch the friend -- closing connection to server & disregarding message
22401#    done2($this->{friend});
22402}
22403
22404# ignore what's sent & give reason at the end.
22405sub error {
22406    my ( $fh, $l ) = @_;
22407    d("error");
22408    my $this = $Con{$fh};
22409    $this->{headerpassed} = 1;
22410    my $tlit;
22411    if ( $l =~ /^\.[\r\n]*$/
22412        || defined( $this->{bdata} ) && $this->{bdata} <= 0 )
22413    {
22414		my $reply;
22415        if ($DelayError) {
22416            $reply = $DelayError;
22417        } else {
22418            $reply = "451 4.7.1 Please try again later";
22419        }
22420
22421		if ($this->{error} =~ /^5[0-9][0-9]/o ) {
22422            $tlit = "[SMTP Error]";
22423
22424            if ( $send250OK || ( ($this->{ispip} || $this->{cip}) && $send250OKISP )) {
22425                $this->{error} = "250 OK";
22426                $tlit = "[SMTP Reply]";
22427            }
22428        }
22429
22430        $this->{error} =~ s/(?:\r?\n)+$//o;
22431        my $out = $this->{error} . "\r\n";
22432        if ($this->{error} =~ /^250/o) {
22433          if ($this->{lastcmd} =~ /^DATA/io && $this->{header}) {     # we have received data - now waiting for QUIT
22434            sendque($fh,$out);
22435            $this->{getline} = \&errorQuit;
22436          } elsif ($this->{lastcmd} =~ /^DATA/io && ! $this->{header}) {   # no data received - close connection
22437            sendque($fh,"$reply\r\n");
22438            $this->{closeafterwrite} = 1;
22439            unpoll($fh,$readable);
22440            done2($this->{friend}) if (! exists $ConDelete{$this->{friend}});
22441          } else {                                                  # we are not in DATA part - send 250 and close connection
22442
22443            sendque($fh,$out);
22444            sendque($fh,"$reply\r\n");
22445            $this->{closeafterwrite} = 1;
22446            unpoll($fh,$readable);
22447            done2($this->{friend}) if (! exists $ConDelete{$this->{friend}});
22448          }
22449        } else {                                               # no 250 - send the error and close the connection
22450
22451            sendque($fh,$out);
22452
22453
22454            $reply = '221 closing transmission' ;
22455            sendque($fh,"$reply\r\n") if $out !~ /^4/o;
22456            $this->{closeafterwrite} = 1;
22457            unpoll($fh,$readable);
22458            done2($this->{friend}) if (! exists $ConDelete{$this->{friend}});
22459        }
22460    }
22461    $this->{lastcmd} .= $this->{lastcmd} =~ /\(error\)/o ? '' : '(error)';
22462}
22463sub errorQuit {
22464    my ( $fh, $l ) = @_;
22465    d("errorQuit - $l");
22466    my $this = $Con{$fh};
22467    my $reply;
22468    my $dreply = "421 closing transmission";
22469    if ($l =~ /^QUIT/io) {
22470        $reply = '221 closing transmission';
22471    } elsif ($this->{ispip} && $l =~ /^(RSET|MAIL FROM:)/io) {
22472        mlog(0,"info: ISP '$this->{ip}' has sent '$1' after SPAM - processing next mail") if $ConnectionLog >= 2;
22473        $this->{getline} = \&getline;
22474        delete $this->{error};
22475        &getline($fh,$l);
22476        return;
22477    } else {
22478        $reply = $dreply;
22479    }
22480    sendque($fh,"$reply\r\n");
22481    $this->{closeafterwrite} = 1;
22482    unpoll($fh,$readable);
22483    $l =~ s/\r|\n//go;
22484    ($this->{lastcmd}) = $l =~ /([a-z]+\s?[a-z]*)/io;
22485    $this->{lastcmd} = $l unless $this->{lastcmd};
22486    push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
22487    # detatch the friend -- closing connection to server & disregarding message
22488    done2($this->{friend}) if (! exists $ConDelete{$this->{friend}});
22489}
22490
22491
22492
22493# filter off the 250 OK noop response and go to reply
22494sub skipok {
22495    d('skipok');
22496    my ( $fh, $l ) = @_;
22497    if ( $l =~ /^250/ ) {
22498        $Con{$fh}->{getline} = \&reply;
22499    } else {
22500        reply(@_);
22501    }
22502}
22503
22504# wait for a server Reply in case of XCLIENT/XFORWARD
22505sub skipevery {
22506    d('skipevery');
22507    my ($fh,$l)=@_;
22508    $Con{$fh}->{getline}=$Con{$fh}->{Xgetline} if $Con{$fh}->{Xgetline};
22509    $Con{$fh}->{Xgetline}->($fh,$Con{$fh}->{Xreply}) if $Con{$fh}->{Xgetline} && $Con{$fh}->{Xreply};
22510    delete $Con{$fh}->{Xgetline};
22511}
22512
22513
22514sub replyAUTH {
22515    my ($fh,$l)=@_;
22516    d('replyAUTH : ' . $l);
22517    my $friend = $Con{$Con{$fh}->{friend}};
22518
22519    $Con{$friend}->{inerror} = ($l=~/^5[05][0-9]/o);
22520    $Con{$friend}->{intemperror} = ($l=~/^4\d{2}/o);
22521    if ($l=~/^(?:1|2|3)\d{2}/o) {
22522        delete $Con{$friend}->{inerror};
22523        delete $Con{$friend}->{intemperror};
22524    }
22525
22526    if ($l =~ /^334\s*(.*)$/o) {
22527        $l = $1;
22528        if (exists $friend->{AUTHclient} && @{$friend->{AUTHClient}}) { # methode PLAIN was used
22529            my $str = join ('', @{$friend->{AUTHClient}});              # send the authentication
22530            $str =~ s/[\r\n]+$//o;
22531            $str .= "\r\n";
22532            NoLoopSyswrite($fh,$str);
22533            @{$friend->{AUTHClient}} = ();
22534        } else {                                                        # any other methode was used
22535            $l =~ s/[\r\n]+$//o;                                        # step by step procedure
22536            my @str = MIME::Base64::encode_base64(
22537                     $friend->{AUTHclient}->client_step(MIME::Base64::decode_base64($l), '')
22538                   );
22539            my $str = join ('', @str);
22540            $str =~ s/[\r\n]+$//o;
22541            $str .= "\r\n";
22542            NoLoopSyswrite($fh,$str) if $str;
22543        }
22544    } elsif ($l =~ /^235/o) {
22545        mlog($Con{$fh}->{friend}, "info: authentication successfull") if $SessionLog >= 2;
22546        undef @{$friend->{AUTHClient}};
22547        delete $friend->{AUTHClient};
22548        delete $friend->{AUTHclient};
22549        &getline($Con{$fh}->{friend},$friend->{sendAfterAuth});
22550        $Con{$fh}->{getline}=\&reply;
22551    } else {
22552        $l =~ s/\r|\n//go;
22553        mlog($Con{$fh}->{friend}, "error: authentication failed ($l) - try to continue unauthenticated");
22554        undef @{$friend->{AUTHClient}};
22555        delete $friend->{AUTHClient};
22556        delete $friend->{AUTHclient};
22557        &getline($Con{$fh}->{friend},$friend->{sendAfterAuth});
22558        $Con{$fh}->{getline}=\&reply;
22559    }
22560}
22561# filter off the 220 OK response on STARTTLS command
22562sub replyTLS {
22563    d('replyTLS');
22564    my ($fh,$l)=@_;
22565    my $oldfh = "$fh";
22566    my $ssl;
22567    my $cli = $Con{$fh}->{friend};
22568    my $serIP=$fh->peerhost();
22569    my $ffr = $Con{$cli}->{TLSqueue};
22570
22571    $Con{$cli}->{inerror} = ($l=~/^5[05][0-9]/o);
22572    $Con{$cli}->{intemperror} = ($l=~/^4\d{2}/o);
22573    if ($l=~/^(?:1|2|3)\d{2}/o) {
22574        delete $Con{$cli}->{inerror};
22575        delete $Con{$cli}->{intemperror};
22576    }
22577
22578    if($l=~/^220/o) { # we can switch the server connection to TLS
22579        $IO::Socket::SSL::DEBUG = $SSLDEBUG;
22580        unpoll($fh,$readable);
22581        unpoll($fh,$writable);
22582        my $fail = 0;
22583        eval{eval{($ssl,$fh) = &switchSSLServer($fh);};
22584            if ("$ssl" !~ /SSL/io) {
22585              $fail = 1;
22586              mlog($fh, "error: Couldn't start TLS for server $serIP: ".IO::Socket::SSL::errstr());
22587              setSSLfailed($serIP);
22588              delete $Con{$fh}->{fakeTLS};
22589              &dopoll($fh,$readable,"POLLIN");
22590              &dopoll($fh,$writable,"POLLOUT");
22591              # process TLSqueue on client
22592              &getline($cli,$ffr);
22593              delete $Con{$cli}->{TLSqueue};
22594              $Con{$fh}->{getline}=\&reply;
22595            }
22596        };
22597        return if $fail;
22598        delete $SSLfailed{$serIP};
22599        addsslfh($oldfh,$ssl,$cli);
22600        $Con{$cli}->{friend} = $ssl;
22601        mlog($ssl,"info: started TLS-SSL session for server $serIP") if ($ConnectionLog >=2);
22602        delete $Con{$oldfh}->{fakeTLS};
22603        delete $Con{$ssl}->{fakeTLS};
22604        NoLoopSyswrite($ssl,"$Con{$cli}->{fullhelo}\r\n"); # send the ehlo again
22605        mlog($ssl,"info: sent EHLO again to $serIP") if ($ConnectionLog >=2);
22606        $Con{$ssl}->{getline}=\&replyTLS2;
22607    } else {  # STARTTLS rejected
22608    # process TLSqueue on client
22609        mlog($fh,"info: injected STARTTLS request rejected by $serIP") if $ConnectionLog >= 2;
22610        &getline($cli,"$ffr\r\n");
22611        delete $Con{$cli}->{TLSqueue};
22612        $Con{$fh}->{getline}=\&reply;
22613    }
22614}
22615
22616sub replyTLS2 {
22617    d('replyTLS2');
22618    my ($fh,$l)=@_;
22619    d("lastReply2 = $l");
22620#    if (lc($l) eq lc($Con{$fh}->{lastEHLOreply}))
22621    my $cli = $Con{$fh}->{friend};
22622
22623    $Con{$cli}->{inerror} = ($l=~/^5[05][0-9]/o);
22624    $Con{$cli}->{intemperror} = ($l=~/^4\d{2}/o);
22625    if ($l=~/^(?:1|2|3)\d{2}/o) {
22626        delete $Con{$cli}->{inerror};
22627        delete $Con{$cli}->{intemperror};
22628    }
22629
22630    if ($l =~ /^250\s+/o) {
22631        my $ffr = $Con{$cli}->{TLSqueue};
22632        $Con{$fh}->{getline} = \&reply;
22633        &getline($cli,"$ffr\r\n");
22634        delete $Con{$cli}->{TLSqueue};
22635        my $serIP=$fh->peerhost().":".$fh->peerport();
22636        mlog($fh,"info: TLSQUEUE processed and cleared for $serIP") if ($ConnectionLog >=2);
22637    }
22638}
22639sub replyEHLO {
22640    d('replyEHLO');
22641    my ($fh,$l)=@_;
22642    my $this=$Con{$fh};
22643    my $cli=$this->{friend};
22644#    $this->{lastEHLOreply} = $l;
22645    d("lastReply3 = $l");
22646
22647    $Con{$cli}->{inerror} = ($l=~/^5[05][0-9]/o);
22648    $Con{$cli}->{intemperror} = ($l=~/^4\d{2}/o);
22649    if ($l=~/^(?:1|2|3)\d{2}/o) {
22650        delete $Con{$cli}->{inerror};
22651        delete $Con{$cli}->{intemperror};
22652    }
22653
22654    &reply($fh,$l) if ($l=~/^250[ \-]+STARTTLS/io ||
22655                       $l=~/^5/o ||
22656                       $l=~/^4/o ||
22657                       $l=~/^221/o);
22658    if (! $Con{$cli}->{relayok} && $l =~ /^250[ \-]+(XCLIENT|XFORWARD) +(.+)\s*\r\n$/io) {
22659        $Con{$cli}->{uc $1} = uc $2;   # 250-XCLIENT/XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE
22660    }
22661    if ($l=~/^5/o ||
22662        $l=~/^4/o ||
22663        $l=~/^221/o)
22664    {
22665        $this->{getline} = \&reply;
22666    } else {
22667        if (! $this->{answertToHELO} && $l =~ /^250\s+/o) {  # we've got the EHLO Reply, now send 250 OK to the client
22668            if ((exists $Con{$cli}->{XCLIENT} || exists $Con{$cli}->{XFORWARD}) &&
22669                ( ($Con{$cli}->{mailInSession} > 0 && $Con{$cli}->{lastcmd} =~ /mail from/io) ||
22670                  ($Con{$cli}->{lastcmd} =~ /helo|ehlo/io)
22671                )
22672               )
22673            {
22674                $this->{Xgetline} = \&replyEHLO;
22675                $this->{Xreply} = "250 OK\r\n";
22676                return if replyX($fh,$cli,$fh->peerhost(),$Con{$cli}->{ip});
22677                delete $this->{Xgetline};
22678                delete $this->{Xreply};
22679            }
22680            $this->{answertToHELO} = 1;
22681            sendque($cli,"250 OK\r\n");
22682            return;
22683        }
22684        sendque($cli,$l) if $this->{Xreply};
22685    }
22686}
22687
22688# messages from the server get relayed to the client
22689
22690sub reply {
22691    my ( $fh, $l ) = @_;
22692    d('reply');
22693    my $this = $Con{$fh};
22694    return unless $this;
22695    my $cli = $this->{friend};
22696    return unless $cli;
22697    $l = decodeMimeWords($l) if ($l =~ /=\?[^\?]+\?[qb]\?[^\?]*\?=/io);
22698    $Con{$cli}->{inerror} = ($l=~/^5[05][0-9]/o);
22699    $Con{$cli}->{intemperror} = ($l=~/^4\d{2}/o);
22700
22701    $Con{$cli}->{greetingSent} = 1 if ( $l =~ /^220[^\-]/o );
22702    if ($l=~/^(?:1|2|3)\d{2}/o) {
22703        delete $Con{$cli}->{inerror};
22704        delete $Con{$cli}->{intemperror};
22705    }
22706
22707    my $cliIP = $Con{$cli}->{ip} || $cli->peerhost();
22708    my $serIP = $fh->peerhost();
22709
22710    if ( $l =~ /^220[^\-]/o && ! $Con{$cli}->{greetingSent} && $myGreeting) {
22711        $Con{$cli}->{greetingSent} = 1;
22712        $l = $myGreeting;
22713        $l = "220 $l" if $l !~ /^220 /o;
22714        $l =~ s/MYNAME/$myName/g;
22715        $l =~ s/VERSION/$MAINVERSION/go;
22716        $l =~ s/\\r/\r/go;
22717        $l =~ s/\\n/\n/go;
22718        $l =~ s/[\r\n]+$//o;
22719        sendque($cli,"$l\r\n");
22720        return;
22721    }
22722    my $DisableAUTH = $Con{$cli}->{DisableAUTH} = (exists $Con{$cli}->{DisableAUTH}) ? $Con{$cli}->{DisableAUTH} : (&matchFH($cli,@lsnNoAUTH) || ( $DisableExtAUTH && ! $Con{$cli}->{relayok} ));
22723
22724	$this->{CanUseIOSocketSSLOK} = $CanUseIOSocketSSL;
22725	$this->{CanUseIOSocketSSLOK} = 0 if !$enableSSL;
22726
22727   	if ($this->{CanUseIOSocketSSLOK}) {
22728   		if (!$Con{$cli}->{SSLnotOK} && (exists $SSLfailed{$Con{$cli}->{ip}})) {
22729
22730    		$this->{SSLnotOK} = $Con{$cli}->{ip};
22731    		mlog($cli,"STARTTLS skipped, $SSLfailed{$Con{$cli}->{ip}} found in error cache (SSLfailCache)") if $SSLLog;
22732    	}
22733
22734    	if (!$Con{$cli}->{SSLnotOK} &&
22735
22736			&matchIP($Con{$cli}->{ip},'noTLSIP',$fh,1)) {
22737
22738    		$this->{SSLnotOK} = $Con{$cli}->{ip};
22739    		mlog( $fh,"STARTTLS skipped, $Con{$cli}->{ip} found in noTLSIP") if $SSLLog >=2;
22740    	}
22741    	if (!$Con{$cli}->{SSLnotOK} &&
22742
22743			&matchFH($cli,@lsnNoTLSI)) {
22744
22745    		$this->{SSLnotOK} = $Con{$cli}->{ip};
22746    		mlog( $fh,"STARTTLS skipped, $Con{$cli}->{ip} found in NoTLSlistenPorts") if $SSLLog >=2;
22747    	}
22748
22749
22750    	$this->{CanUseIOSocketSSLOK} = 0 if $Con{$cli}->{SSLnotOK};
22751    }
22752
22753
22754
22755
22756
22757    # we'll filter off the XEXCH50 service, as it only causes troubles
22758    # we'll filter off the CHUNKING directive to avoid BDAT problems.
22759    # we'll filter off the PIPELINING directive to avoid ... problems.
22760
22761    # STARTTLS...
22762    #    we filter off the STARTTLS directive, but
22763    #    re-add an offer of STARTTLS to the client if we have SSL capability, and
22764    #    separately start SSL to the MTA if it is offered and we are capable
22765	if (! $Con{$cli}->{relayok} && $l =~ /^250[ \-]+(XCLIENT|XFORWARD) +(.+)\s*\r\n$/io) {
22766        $Con{$cli}->{uc $1} = uc $2;   # 250-XCLIENT/XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE
22767    }
22768	if ( $l =~ /250-.*(VRFY|EXPN)/i  && $DisableVRFY && !$Con{$cli}->{relayok}) {
22769        return;
22770    } elsif ( $l =~ /250 .*(VRFY|EXPN)/i  && $DisableVRFY && !$Con{$cli}->{relayok}) {
22771        sendque( $cli, "250 NOOP\r\n" );
22772        return;
22773    } elsif($l=~/250-\s*AUTH/io && $DisableAUTH && !$Con{$cli}->{relayok}) {        # AUTH
22774        d("250-sequenz - from server: \>$l\<");
22775        d("250-sequenz - to client: \>\<");
22776        return;
22777    } elsif($l=~/250\s+AUTH/io && $DisableAUTH && !$Con{$cli}->{relayok}) {
22778        d("250 sequenz - from server: \>$l\<");
22779        d("250 sequenz - to client: \>NOOP\<");
22780        sendque($cli, "250 NOOP\r\n") unless $Con{$cli}->{sentEHLO};
22781        return;
22782    } elsif (($l=~/(211|214)(?: |-)(?:.*?)(?:VRFY|EXPN)/io && $DisableVRFY && !$Con{$cli}->{relayok}) or
22783             ($l=~/(211|214)(?: |-)(?:.*?)AUTH/io && $DisableAUTH && !$Con{$cli}->{relayok}) or
22784             ($l=~/(211|214)(?: |-)(?:.*?)(?:$notAllowedSMTP)/io) ) {
22785        d("$1 sequenz - from server: \>$l\<");
22786        $l =~ s/VRFY|EXPN//sigo if ($DisableVRFY && !$Con{$cli}->{relayok});
22787        $l =~ s/AUTH[^\r\n]+//sigo if ($DisableAUTH && !$Con{$cli}->{relayok});
22788        $l =~ s/$notAllowedSMTP/NOOP/sigo;
22789    } elsif ($l=~/250[\s\-]+AUTH[\s\=]+(.+)/io) {
22790        my $methodes = $1;
22791        $methodes =~ s/^\s+//o;
22792        $methodes =~ s/[\s\r\n]+$//o;
22793        foreach (split(/\s+/o,$methodes)) {
22794            $Con{$cli}->{authmethodes}->{uc $_} = 1;
22795            d("info: Reply: registered authmethode $_");
22796        }
22797    } elsif($l=~/250[- ].*?SIZE\s*(\d+)/io && $maxSize && $Con{$cli}->{relayok} && $1 > $maxSize) {
22798        my $size = $1;
22799        $l =~ s/$size/$maxSize/;
22800    } elsif($l=~/250[- ].*?SIZE\s*(\d+)/io && $maxSizeExternal && ! $Con{$cli}->{relayok} && $1 > $maxSizeExternal) {
22801        my $size = $1;
22802        $l =~ s/$size/$maxSizeExternal/;
22803
22804    } elsif ( $l =~ /250-.*?(CHUNKING|PIPELINING|XEXCH50|SMTPUTF8|
22805                     XCLIENT|XFORWARD|
22806                     TURN|ATRN|ETRN|TURNME|X-TURNME|XTRN|
22807                     SEND|SOML|SAML|EMAL|ESAM|ESND|ESOM|
22808                     XAUTH|XQUE|XREMOTEQUEUE|
22809                     X-EXPS|X-ADAT|X-DRCP|X-ERCP|EVFY|8BITMIME|BINARYMIME|BDAT|
22810                     AUTH GSSAPI|AUTH NTLM|X-LINK2STATE|STARTTLS|TLS)/i ) {
22811        $this->{mtaSSL} = 1 if ($l =~ /STARTTLS/i);
22812        return;
22813
22814    } elsif ( $l =~ /250 .*?(CHUNKING|PIPELINING|XEXCH50|SMTPUTF8|
22815                     XCLIENT|XFORWARD|
22816                     TURN|ATRN|ETRN|TURNME|X-TURNME|XTRN|
22817                     SEND|SOML|SAML|EMAL|ESAM|ESND|ESOM|
22818                     XAUTH|XQUE|XREMOTEQUEUE|
22819                     X-EXPS|X-ADAT|X-DRCP|X-ERCP|EVFY|8BITMIME|BINARYMIME|BDAT|
22820                     AUTH GSSAPI|AUTH NTLM|X-LINK2STATE|STARTTLS|TLS)/i ) {
22821        $this->{mtaSSL} = 1 if ($l =~ /STARTTLS/i);
22822		if ($Con{$cli}->{greeting} =~ /EHLO/i && $this->{CanUseIOSocketSSLOK}) {
22823            if ($this->{mtaSSL} && $fh !~ /IO::Socket::SSL/) {
22824                d("enabling SSL to MTA");
22825                $fh->write("STARTTLS\r\n");
22826            }
22827            if (!$Con{$cli}->{cliSSL} && $cli !~ /IO::Socket::SSL/) {
22828                d("injecting STARTTLS into client response");
22829                sendque( $cli, "250-STARTTLS\r\n" );
22830                $Con{$cli}->{cliSSL} = 1;
22831            }
22832        }
22833        sendque( $cli, "250 NOOP\r\n" );
22834        return;
22835
22836    } elsif ($l =~ /^250 /) {
22837    	$this->{mtaSSL} = 1 if ($l =~ /STARTTLS/i);
22838        if ($Con{$cli}->{greeting} =~ /EHLO/i && $this->{CanUseIOSocketSSLOK}) {
22839            if ($this->{mtaSSL} && $fh !~ /IO::Socket::SSL/) {
22840                d("enabling SSL to MTA");
22841                $fh->write("STARTTLS\r\n");
22842            }
22843            if (!$Con{$cli}->{cliSSL} && $cli !~ /IO::Socket::SSL/) {
22844                d("injecting STARTTLS into client response");
22845                sendque( $cli, "250-STARTTLS\r\n" );
22846                $Con{$cli}->{cliSSL} = 1;
22847            }
22848        }
22849   } elsif ($l =~ /^220 / && $this->{mtaSSL} && $CanUseIOSocketSSL
22850      && $fh !~ /IO::Socket::SSL/) {
22851        my $oldfh = "".$fh;
22852		$IO::Socket::SSL::DEBUG = $SSLDEBUG;
22853        # stop watching old filehandle
22854        $readable->remove($fh);
22855        $writable->remove($fh);
22856
22857        # set flag to detect possible 554 failure message
22858        $this->{tryingSSL} = 1;
22859
22860        # convert to SSL
22861        d("MTA SSL start");
22862        mlog($cli, "MTA offered STARTTLS - converting to SSL",1) if $SSLLog;
22863        eval{$fh->blocking(1);};
22864        my $try = 4;
22865        my $ssl;
22866        my $fail;
22867        eval{eval{($ssl,$fh) = &switchSSLServer($fh);};
22868        	if (!$ssl || $fh !~ /IO::Socket::SSL/) {
22869        	my $error = IO::Socket::SSL::errstr();
22870            mlog($oldfh, "SSL negotiation with server failed: $error") if $SSLLog;
22871            $fail = 1;
22872        	$this->{mtaSSL} = 0;
22873        	$this->{mtaSSLfailed} = 1;
22874      		setSSLfailed($Con{ $this->{ip} });
22875            $readable->add($fh);
22876            $writable->remove($fh);
22877
22878        	}
22879        };
22880
22881        return if $fail;
22882
22883        d("MTA SSL ok");
22884        # success - clear flag - any 554 now is not SSL problem
22885        $this->{tryingSSL} = 0;
22886		$this->{mtaSSLfailed} = 0;
22887        # copy data from old $fh
22888        $Con{$fh} = $Con{$oldfh};
22889        $Con{$fh}->{client} = $fh;
22890
22891
22892        # clean up old $fh
22893        delete $Con{$oldfh};
22894        delete $SocketCalls{$oldfh};
22895
22896        # set up new $fh
22897        $SocketCalls{$fh} = \&SMTPTraffic;
22898        $readable->add($fh);
22899
22900        # must now resend EHLO greeting
22901        # then read and discard the MTA's response because the client
22902        # already has an EHLO response and doesn't know about this one
22903        sendque($fh, "EHLO $Con{$cli}->{helo}\r\n") if !$myHelo;
22904        sendque($fh, "EHLO $localhostname\r\n") if $myHelo == 2 && $localhostname;
22905        sendque($fh, "EHLO $myName\r\n") if $myHelo && ($myHelo == 1 or !$localhostname);
22906        $this->{getline} = \&dropreply;
22907
22908        return;
22909
22910    } elsif ( $l =~ /^220/ ) {
22911        sendque( $fh, $this->{noop} ) if $this->{noop};
22912        $this->{greetingSent} = 1;
22913        delete $this->{noop};
22914    } elsif($l=~/^\d{3}\-/o) {
22915        sendque($cli, $l);
22916        return;
22917    } elsif ( $l =~ /^235/ ) {
22918
22919        # check for authentication response
22920        $Con{$cli}->{relayok} = "authenticated";
22921        $Con{$cli}->{notspamtag} = 1 if $NoFilterAuth;
22922        $Con{$cli}->{authenticated}=1;
22923        $Con{$cli}->{whitelisted}= "authenticated" if  $WhitelistAuth;
22924        $Con{$cli}->{auth} = 1;
22925        $Con{$cli}->{passingreason} = "authenticated";
22926        d("$Con{$cli}->{ip}: authenticated");
22927        mlog( $cli, "authenticated",1 );
22928    } elsif ( $l =~ /^354/ ) {
22929        d('reply - 354');
22930
22931    } elsif($l=~/^535/o) {
22932        d('reply - 535');
22933        my $r = $l;
22934        $r =~ s/\r|\n//go;
22935        $Con{$cli}->{prepend}="[AUTHError]";
22936        mlog($cli,"warning: SMTP authentication failed",1);
22937        if (!$Con{$cli}->{relayok} && ! &AUTHErrorsOK($cli)) {
22938            $Con{$cli}->{prepend}="[MaxAUTHErrors]";
22939            mlog($cli,"max sender authentication errors ($MaxAUTHErrors) exceeded -- dropping connection - after reply: $l",1);
22940            &NoLoopSyswrite($cli,$l);
22941            done($fh);
22942            return;
22943        }
22944    } elsif($Con{$cli}->{lastcmd} eq 'AUTH' && $l=~/^5/o) {
22945        d('reply - 5xx after AUTH');
22946        mlog($cli,"warning: SMTP authentication failed",1);
22947        if (!$Con{$cli}->{relayok} && ! &AUTHErrorsOK($cli)) {
22948            $Con{$cli}->{prepend}="[MaxAUTHErrors]";
22949            mlog($cli,"max sender authentication errors ($MaxAUTHErrors) exceeded -- dropping connection - after reply: $l",1);
22950            &NoLoopSyswrite($cli,$l);
22951            done($fh);
22952            return;
22953        }
22954    } elsif ( $l =~ /^50[0-9]/ ) {
22955        if ( $Con{$cli}->{skipbytes} ) {
22956            d("Resetting skipbytes");
22957            $Con{$cli}->{skipbytes} = 0
22958              ; # if we got a negative response from XEXCH50 then don't skip anything
22959        }
22960        if(++$this->{serverErrors} >= $MaxErrors ) {
22961            $this->{prepend} = "[MaxErrors]";
22962            mlog( $cli,
22963                "max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection" );
22964            $Stats{msgMaxErrors}++;
22965            sendque( $cli, $l );
22966            done($fh);
22967            return;
22968        }
22969    } elsif($l=~/^550/) {
22970        my $r = $l;
22971        $r =~ s/\r|\n//g;
22972        mlog($cli,"warning: got reply '$r'") if $ConnectionLog >=2;
22973        if ($DoVRFY && !$Con{$cli}->{relayok} && $MaxVRFYErrors && ++$this->{maxVRFYErrors} > $MaxVRFYErrors) {
22974            $this->{prepend}="[MaxVRFYErrors]";
22975            mlog($cli,"max recipient verification errors ($MaxVRFYErrors) exceeded -- dropping connection - after reply: $l") if $ConnectionLog >=2;
22976            $Stats{msgMaxErrors}++;
22977            sendque( $cli, $l );
22978            done($fh);
22979            return;
22980   } elsif (!$Con{$cli}->{relayok} && $MaxErrors && ++$this->{serverErrors} > $MaxErrors) {
22981            $this->{prepend} = "[MaxErrors]";
22982            mlog($cli,"max errors (MaxErrors=$MaxErrors) exceeded -- dropping connection - after reply: $l") if $ConnectionLog >=2;
22983            $Stats{msgMaxErrors}++;
22984            sendque( $cli, $l );
22985            done($fh);
22986            return;
22987        }
22988    } elsif ( $l =~ /^554/ && $this->{tryingSSL} ) {
22989        # SSL negotiation with MTA failed
22990        d("554 SSL failure received from MTA");
22991        $this->{tryingSSL} = 0;
22992        $this->{mtaSSLfailed} = 1;
22993        return;
22994    } elsif ( $l =~ /^554/ && $this->{tryingSSL} ) {
22995        # SSL negotiation with MTA failed
22996        d("554 SSL failure received from MTA");
22997        $this->{tryingSSL} = 0;
22998        $this->{mtaSSLfailed} = 1;
22999        return;
23000    } elsif ($l=~/^45[012]/o && $Con{$cli}->{relayok} && $Con{$cli}->{lastcmd} =~ /^(?:ehlo|helo|mail|rcpt)/io) {
23001        my $r = $l;
23002        $r =~ s/\r|\n//go;
23003        mlog($cli,"info: got temp error reply '$r' from server host $serIP for SMTP command '$Con{$cli}->{lastcmd}'") if $ConnectionLog;
23004    } elsif ($l=~/^(?:421|45[012])/o) {
23005        my $r = $l;
23006        $r =~ s/\r|\n//go;
23007        $Con{$cli}->{deleteMailLog} = "MTA reply $r" if $Con{$cli}->{lastcmd} =~ /data/io;
23008        mlog($cli,"info: got reply '$r' - message is rejeted by the server host $serIP") if $ConnectionLog && $Con{$cli}->{deleteMailLog};
23009        sendque($cli, $l);
23010        $Con{$cli}->{closeafterwrite} = 1;
23011        unpoll($cli,$readable);
23012        done2($fh);
23013        return;
23014   	} elsif ($l=~/^221/o) {
23015        sendque($cli, $l);
23016        $Con{$cli}->{closeafterwrite} = 1;
23017
23018        return;
23019	}
23020    # email report/list interface sends messages itself
23021    return
23022      if ( defined( $Con{$cli}->{reporttype} )
23023        && $Con{$cli}->{reporttype} >= 0 );
23024    return if $l =~ /^(?:\r\n)+$/o;
23025    sendque( $cli, $l );
23026}
23027
23028sub replyX {
23029    my ($fh,$cli,$serIP,$cliIP) = @_;
23030    my $this = $Con{$fh};
23031    my $xinfo;
23032    my %seen;
23033    my $what = 'XCLIENT';
23034    $what = 'XFORWARD' if exists $Con{$cli}->{XFORWARD};
23035    d("info: sending $what info to $serIP");
23036    foreach (split(/\s+/o,$Con{$cli}->{$what})) {
23037        if (! $_ || ! defined *{'yield'} || exists $seen{$_}) {
23038            $seen{$_} = 1;
23039            next;
23040        } elsif ($_ eq 'NAME') {
23041            &sigoffTry(__LINE__);
23042            my $ptr = getRRData($cliIP,'PTR');
23043            &sigonTry(__LINE__);
23044            $ptr = 'localhost' if $cliIP =~ /(?:127\.0\.0\.1|::1)$/io;
23045            $ptr ||= '[UNAVAILABLE]';
23046            $xinfo .= " NAME=$ptr";
23047        } elsif ($_ eq 'ADDR') {
23048            $xinfo .= $cliIP ? " ADDR=$cliIP" : " ADDR=[UNAVAILABLE]";
23049        } elsif ($_ eq 'PORT') {
23050            $xinfo .= $Con{$cli}->{port} ? " PORT=$Con{$cli}->{port}" : " PORT=[UNAVAILABLE]";
23051        } elsif ($_ eq 'PROTO') {
23052            my $proto = (lc $Con{$cli}->{orghelo} eq 'ehlo') ? 'ESMTP' : 'SMTP';
23053            $proto .= 'S' if "$cli" =~ /SSL/io;
23054            $xinfo .= " PROTO=$proto";
23055        } elsif ($_ eq 'HELO') {
23056            $xinfo .= $Con{$cli}->{helo} ? " HELO=$Con{$cli}->{helo}" : " HELO=[UNAVAILABLE]";
23057        } elsif ($_ eq 'IDENT') {
23058            $xinfo .= $Con{$cli}->{msgtime} ? " IDENT=$Con{$cli}->{msgtime}" : " IDENT=[UNAVAILABLE]";
23059        } elsif ($_ eq 'SOURCE') {
23060            $xinfo .= $Con{$cli}->{acceptall} ? " SOURCE=LOCAL" : " SOURCE=REMOTE";
23061        } elsif ($_ eq 'LOGIN') {
23062            $xinfo .= $Con{$cli}->{userauth}{user} ? " LOGIN=$Con{$cli}->{userauth}{user}" : " LOGIN=[UNAVAILABLE]";
23063        } else {
23064            $xinfo .= " $_=[UNAVAILABLE]";
23065        }
23066        $seen{$_} = 1;
23067    }
23068    $Con{$cli}->{'save'.$what} = $Con{$cli}->{$what};
23069    delete $Con{$cli}->{$what};
23070    if ($xinfo) {
23071        $xinfo = "$what$xinfo";
23072        d("sent: $xinfo");
23073        mlog($cli,"info: sent - '$xinfo' to $serIP") if $ConnectionLog > 1;
23074        $this->{getline} = \&skipevery;
23075        sendque($fh, "$xinfo\r\n");
23076        delete $this->{isTLS};
23077        delete $Con{$cli}->{isTLS};
23078        return 1;
23079    }
23080    delete $this->{Xgetline};
23081    delete $this->{Xreply};
23082    return 0;
23083}
23084#################################################################################
23085#                Email Interface
23086# this mail isn't really a mail -- it's a spam/ham report
23087################################################################################
23088sub SpamReport {
23089    my($fh,$l)=@_;
23090    my $this=$Con{$fh};
23091	my $tmp = $l ;
23092	$tmp =~ s/\r|\n|\s//igo;
23093	$tmp =~ /^([a-zA-Z0-9]+)/o;
23094	if ($1) {
23095	    $this->{lastcmd} = substr($1,0,14);
23096        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
23097    }
23098    if ( $l =~ /^ *DATA/i || $l =~ /^ *BDAT (\d+)/i ) {
23099        if ($1) {
23100            $this->{bdata} = $1;
23101        } else {
23102            delete $this->{bdata};
23103        }
23104        $this->{getline} = \&SpamReportBody;
23105        my $report = ( $this->{reporttype} == 0 ) ? "spam" : "ham";
23106        sendque( $fh, "354 OK Send $report body\r\n" );
23107        return;
23108    } elsif ( $l =~ /^ *RSET/i ) {
23109        stateReset($fh);
23110        $this->{getline} = \&getline;
23111        sendque( $this->{friend}, "RSET\r\n" );
23112        return;
23113    } elsif ( $l =~ /^ *QUIT/i ) {
23114        stateReset($fh);
23115        $this->{getline} = \&getline;
23116        sendque( $this->{friend}, "QUIT\r\n" );
23117        return;
23118    } elsif ( $l =~ /^ *XEXCH50 +(\d+)/i ) {
23119        d("XEXCH50 b=$1");
23120        sendque( $fh, "504 Need to authenticate first\r\n" );
23121        return;
23122    }
23123
23124    sendque( $fh, "250 OK\r\n" );
23125}
23126
23127
23128
23129# we're getting the body of a spam/ham report
23130sub SpamReportBody {
23131    my ($fh, $l)=@_;
23132    d('SpamReportBody');
23133    my $this=$Con{$fh};
23134    $this->{header}.=$l if (length($this->{header}) < $MaxBytesReports || ($CanUseEMM && $maillogExt));
23135    my $sub;
23136    my $type;
23137    my %addresses;
23138    my $numparts = 0;
23139    if($l=~/^\.[\r\n]/o || defined($this->{bdata}) && $this->{bdata}<=0) {
23140
23141        # we're done -- write the file & clean up
23142        my $msg = substr($this->{header},0,$MaxBytesReports);
23143        $type = $this->{reporttype}==0 ? 'Spam' : 'NotSpam';
23144        mlog(0,"$type-Report: process message from $this->{mailfrom}") if $ReportLog;
23145        # are there attached messages ? - process them
23146
23147        if ($CanUseEMM && $maillogExt) {
23148            my $name;
23149            eval {
23150                $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
23151
23152                my $email=Email::MIME->new($this->{header});
23153                foreach my $part ( $email->parts ) {
23154                    my $dis = $part->header("Content-Type") || '';        # get the charset of the email part
23155                    my $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23156                    $name = $attrs->{name} || $part->{ct}{attributes}{name};
23157                    $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23158                    eval{$name ||= $part->filename;};
23159                    if (! $name) {
23160                      eval{
23161                        $dis = $part->header("Content-Disposition") || '';
23162                        $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23163                        $name = $attrs->{name} || $part->{ct}{attributes}{name};
23164                        $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23165                      };
23166                    }
23167                    if ($part->header("Content-Disposition")=~ /attachment|inline/io && $name =~ /$maillogExt$/) {
23168                        $numparts++;
23169                        d("SpamReportBody - processing attached email $name");
23170                        mlog(0,"$type-Report: processing attached messagefile ($numparts) $name") if $ReportLog;
23171                        my $dfh = "$fh" . "_X$numparts";
23172                        $Con{$dfh}->{mailfrom} = $this->{mailfrom};
23173                        $Con{$dfh}->{reporttype} = $this->{reporttype};
23174                        my $body = $part->body;
23175                        if ( $EmailErrorsModifyWhite == 2  && $Con{$dfh}->{reporttype} <= 1) {
23176                            %addresses = ();
23177                            my $reporttype =  $Con{$dfh}->{reporttype};
23178
23179                            $Con{$dfh}->{header} = $body;
23180                            for my $addr (&ListReportGetAddr($dfh)) {
23181                                next if exists $addresses{lc $addr};
23182                                $addresses{lc $addr} = 1;
23183                                &ShowWhiteReport($addr,$Con{$dfh});
23184                            }
23185                            $Con{$dfh}->{reporttype} = $reporttype;
23186                        }
23187                        if ( ($EmailErrorsModifyWhite == 1  && $Con{$dfh}->{reporttype} == 1) or ($EmailErrorsRemoveWhite == 1  && $Con{$dfh}->{reporttype} == 0)) {
23188                            %addresses = ();
23189                            my $reporttype =  $Con{$dfh}->{reporttype};
23190                            $Con{$dfh}->{reporttype} = 3 if  $Con{$dfh}->{reporttype} == 0;
23191                            $Con{$dfh}->{reporttype} = 2 if  $Con{$dfh}->{reporttype} == 1;
23192                            $Con{$dfh}->{header} = $body;
23193                            for my $addr (&ListReportGetAddr($dfh)) {   # process the addresses
23194                                next if exists $addresses{lc $addr};
23195                                $addresses{lc $addr} = 1;
23196                                &ListReportExec($addr,$Con{$dfh});
23197                            }
23198                            $Con{$dfh}->{reporttype} = $reporttype;
23199                        }
23200
23201						if (matchSL( $Con{$dfh}->{mailfrom}, 'EmailErrorsModifyPersBlack' )  && !matchSL( $Con{$dfh}->{mailfrom}, 'EmailErrorsModifyNotPersBlack') ) {
23202							%addresses = ();
23203							my $skipbody = 0;
23204							my $reporttype = $Con{$dfh}->{reporttype};
23205
23206							if ($Con{$dfh}->{reporttype} == 0) {
23207								$Con{$dfh}->{reporttype} = 16;
23208								$skipbody = 1;
23209							}
23210							if ($Con{$dfh}->{reporttype} == 1) {
23211								$Con{$dfh}->{reporttype} = 17;
23212								$skipbody = 1;
23213							}
23214
23215
23216
23217							for my $addr (&ListReportGetAddr($dfh,1)) {
23218                    			next if exists $addresses{lc $addr};
23219                    			$addresses{lc $addr} = 1;
23220
23221                    			&ListReportExec($addr,$Con{$dfh});
23222                			}
23223                			$Con{$dfh}->{reporttype} = $reporttype;
23224            			}
23225
23226
23227                        if ($DoAdditionalAnalyze) {
23228                            my $currReport = $this->{report};
23229                            $this->{report} = '';
23230
23231                            $Con{$dfh}->{header} = "\r\n\r\n".$body;
23232                            my $sub= eval {AnalyzeText($dfh);};
23233
23234                            # mail analyze report
23235                            ReturnMail($fh,$this->{mailfrom},"$base/reports/analyzereport.txt",$sub, "\n$this->{report}\n") if ($DoAdditionalAnalyze==1 || $DoAdditionalAnalyze==3);
23236                            ReturnMail($fh,$EmailAnalyzeTo,"$base/reports/analyzereport.txt",$sub, "\n$this->{report}\n", $this->{mailfrom}) if ( $EmailAnalyzeTo && ($DoAdditionalAnalyze==2 || $DoAdditionalAnalyze==3));
23237
23238                            $this->{report} = $currReport;
23239                        }
23240                        delete $Con{$dfh};
23241
23242                        my $ssub=SpamReportExec($body,($this->{reporttype}==0) ? $correctedspam : $correctednotspam);
23243                        $sub = $ssub if $numparts == 1;
23244                        mlog(0,"$type Report: processed attached messagefile $name from $this->{mailfrom}")  if $ReportLog >= 2;
23245                    }
23246                }
23247            };
23248        }
23249        if ($numparts == 0) {
23250            mlog(0,"$type-Report: (no attachment) - processing raw email") if $ReportLog > 1;
23251
23252            if ( $EmailErrorsModifyWhite == 2 && $this->{reporttype} <= 1) {
23253            %addresses = ();
23254
23255				for my $addr (&ListReportGetAddr($fh)) {
23256                    next if exists $addresses{lc $addr};
23257                    $addresses{lc $addr} = 1;
23258
23259                    &ShowWhiteReport($addr,$this);
23260                }
23261
23262            }
23263
23264             if ( ($EmailErrorsModifyWhite == 1  && $this->{reporttype} == 1) or ($EmailErrorsRemoveWhite == 1  && $this->{reporttype} == 0)) {
23265                %addresses = ();
23266                my $reporttype = $this->{reporttype};
23267
23268				$this->{reporttype} = 3 if $this->{reporttype} == 0;
23269				$this->{reporttype} = 2 if $this->{reporttype} == 1;
23270
23271
23272               	for my $addr (&ListReportGetAddr($fh)) {
23273                     next if exists $addresses{lc $addr};
23274                     $addresses{lc $addr} = 1;
23275                     &ListReportExec($addr,$this);
23276                }
23277
23278                if (! scalar keys %addresses && ($this->{reportaddr} eq 'EmailPersBlackAdd' or $this->{reportaddr} eq 'EmailPersBlackRemove')) {
23279            &ListReportExec('reportpersblack@local.com',$this);
23280        	}
23281                $this->{reporttype} = $reporttype;
23282
23283            }
23284            if (matchSL( $this->{mailfrom}, 'EmailErrorsModifyPersBlack' )  && !matchSL( $this->{mailfrom}, 'EmailErrorsModifyNotPersBlack' ) && $this->{reporttype} <= 1) {
23285
23286				%addresses = ();
23287				my $skipbody;
23288				my $reporttype = $this->{reporttype};
23289				if ($this->{reporttype} == 0) {
23290					$this->{reporttype} = 16;
23291					$skipbody = 1;
23292				}
23293				if ($this->{reporttype} == 1) {
23294					$this->{reporttype} = 17;
23295					$skipbody = 1;
23296				}
23297
23298
23299				for my $addr (&ListReportGetAddr($fh,1)) {   # process the addresses
23300                    next if exists $addresses{lc $addr};
23301                    $addresses{lc $addr} = 1;
23302
23303                    &ListReportExec($addr,$this);
23304                }
23305                $this->{reporttype} = $reporttype;
23306            }
23307
23308            if ($DoAdditionalAnalyze) {
23309                my $currReport = $this->{report};
23310                $this->{report} = '';
23311
23312                my $reportaddr = $this->{reportaddr};
23313                $this->{reportaddr} = 'EmailAnalyze';
23314                my $sub=AnalyzeText($fh);
23315
23316                # mail analyze report
23317                	ReturnMail($fh,$this->{mailfrom},"$base/reports/analyzereport.txt",$sub, "\n$this->{report}\n") if ($DoAdditionalAnalyze==1 || $DoAdditionalAnalyze==3);
23318                $this->{isadmin} = 1;
23319                ReturnMail($fh,$EmailAnalyzeTo,"$base/reports/analyzereport.txt",$sub, "\n$this->{report}\n", $this->{mailfrom}) if ( $EmailAnalyzeTo && ($DoAdditionalAnalyze==2 || $DoAdditionalAnalyze==3));
23320                delete $this->{isadmin};
23321
23322                $this->{report} = $currReport;
23323                $this->{reportaddr} = $reportaddr;
23324            }
23325            $sub=SpamReportExec($msg,($this->{reporttype}==0) ? $correctedspam : $correctednotspam);
23326        }
23327        mlog(0,"$type-Report: finished report-message from $this->{mailfrom}") if $ReportLog;
23328        $this->{header}='';
23329        my $file=($this->{reporttype}==0) ? "reports/spamreport.txt" : "reports/notspamreport.txt";
23330     ReturnMail($fh,$this->{mailfrom},"$base/$file",$sub,"$this->{rcpt}\n\n$this->{report}\n") if ($EmailErrorsReply==1 || $EmailErrorsReply==3);
23331        ReturnMail($fh,$EmailErrorsTo,"$base/$file",$sub,"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom}) if ($EmailErrorsTo && ($EmailErrorsReply==2 || $EmailErrorsReply==3));
23332
23333        stateReset($fh);
23334        $this->{getline}=\&getline;
23335        sendque($this->{friend},"RSET\r\n");
23336    }
23337}
23338
23339sub SpamReportSubject {
23340    my $bod = shift;
23341
23342    my ($sub) = $bod =~ /Subject: (.*)/i;
23343        # remove the spam subject header addition if present
23344    my $spamsub = $spamSubject;
23345    if ($spamsub) {
23346        $spamsub =~ s/(\W)/\\$1/g;
23347        $sub     =~ s/$spamsub//gi;
23348    }
23349    $sub =~ s/\]//gi;
23350    $sub =~ s/\[//gi;
23351
23352    $sub =~ s/^fwd.\s//gi;
23353    $sub =~ s/^fw.\s//gi;
23354    $sub =~ s/^aw.\s//gi;
23355    $sub =~ s/^re.\s//gi;
23356    $sub = decodeMimeWords($sub);
23357
23358
23359    $sub =~ s/\r//;
23360    return $sub;
23361}
23362
23363sub SpamReportExec {
23364    my ( $bod, $path ) = @_;
23365    my $header;
23366    my ($sub) = $bod =~ /Subject: (.*)/i;
23367
23368    $sub =~ s/\]//gi;
23369    $sub =~ s/\[//gi;
23370    $sub =~ s/^fwd.\s//gi;
23371    $sub =~ s/^fw.\s//gi;
23372    $sub =~ s/^aw.\s//gi;
23373    $sub =~ s/^re.\s//gi;
23374    my $udecsub = $sub;
23375    $sub=decodeMimeWords($sub);
23376
23377    # remove the spam subject header addition if present
23378    my $spamsub=$spamSubjectEnc;
23379    if($spamsub) {
23380        $spamsub=~s/(\W)/\\$1/g;
23381        $sub=~s/$spamsub//gi;
23382        $udecsub=~s/$spamsub//gi;
23383    }
23384    $sub =~ s/\r//o;
23385    $udecsub =~ s/\r//o;
23386
23387    my $encsub = $sub =~ /[\x00-\x1F\x7F-\xFF]/o ? $udecsub : $sub;
23388    $header="Subject: ".$encsub."\n" if $encsub;
23389    $header.=$1."\n" if 		$bod=~/(Received:\s+from\s+.*?\(\[$IPRe.*?helo=.*?\))/io;
23390    $sub =~ y/a-zA-Z0-9/_/cs;
23391    $sub =~ s/[\^\s\<\>\?\"\:\|\\\/\*]/_/igo;  # remove not allowed characters and spaces from file name
23392#
23393
23394    $header .= $1 if $bod =~ /(X-Assp-ID: .*)/i;
23395
23396    $header .= $1 if $bod =~ /(X-Assp-Tag: .*)/i;
23397
23398    $header .= $1 if $bod =~ /(X-Assp-Envelope-From: .*)/i;
23399
23400    $header.=$1 if $bod=~/(X-Assp-Intended-For: .*)/io;
23401
23402    $bod=~s/^.*?\n[\r\n\s]+//so;
23403
23404    $bod=~s/X-Assp-Spam-Prob:[^\r\n]+\r?\n//gio;
23405    if($bod=~/\nReceived: /o) {
23406        $bod=~s/^.*?\nReceived: /Received: /so;
23407    } else {
23408        $bod=~s/^.*?\n((\w[^\n]*\n)*Subject:)/$1/sio;
23409        $bod=~s/\n> /\n/go;
23410    }
23411    $bod=$header.$bod;
23412
23413    my $f = int( rand() * 999999 );
23414
23415    open( $FH, ">","$base/$path/$sub"."__"."$f.rpt" );
23416    binmode $FH;
23417    print $FH $bod;
23418    close $FH;
23419    $sub;
23420}
23421
23422# we're receiving an email to manipulate addresses in the whitelist/redlist
23423
23424sub ListReport {
23425    my($fh,$l)=@_;
23426    my $this=$Con{$fh};
23427	my $tmp = $l ;
23428	$tmp =~ s/\r|\n|\s//igo;
23429	$tmp =~ /^([a-zA-Z0-9]+)/o;
23430	if ($1) {
23431	    $this->{lastcmd} = substr($1,0,14);
23432        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
23433    }
23434    if ( $l =~ /^ *DATA/i || $l =~ /^ *BDAT (\d+)/i ) {
23435        if ($1) {
23436            $this->{bdata} = $1;
23437        } else {
23438            delete $this->{bdata};
23439        }
23440        sendque( $this->{friend}, "RSET\r\n" )
23441          ;    # make sure to reset the pending email
23442        $this->{getline} = \&ListReportBody;
23443        my $list;
23444        $list = ( ( $this->{reporttype} & 4 ) == 0 ) ? "whitelist" : "redlist"
23445          if !$EmailErrorsModifyWhite;
23446        $list = "spam" if $EmailErrorsRemoveWhite && $this->{reporttype} == 0;
23447        $list = "ham"  if $EmailErrorsModifyWhite && $this->{reporttype} == 1;
23448        sendque( $fh, "354 OK Send $list body\r\n" );
23449        return;
23450    } elsif ( $l =~ /^ *RSET/i ) {
23451        stateReset($fh);
23452        $this->{getline} = \&getline;
23453        sendque( $this->{friend}, "RSET\r\n" );
23454        return;
23455    } elsif ( $l =~ /^ *QUIT/i ) {
23456        stateReset($fh);
23457        $this->{getline} = \&getline;
23458        sendque( $this->{friend}, "QUIT\r\n" );
23459        return;
23460    } elsif ( $l =~ /^ *XEXCH50 +(\d+)/i ) {
23461        d("XEXCH50 b=$1");
23462        sendque( $fh, "504 Need to authenticate first\r\n" );
23463        return;
23464    } else {
23465
23466        # more recipients ?
23467        while ( $l =~ /($EmailAdrRe\@$EmailDomainRe)/og ) {
23468            next if $1 == $this->{mailfrom};
23469            ListReportExec( $1, $this );
23470            $this->{rcpt} .= "$1 ";
23471        }
23472
23473    }
23474
23475    sendque( $fh, "250 OK\r\n" );
23476
23477}
23478
23479# we're receiving an email to send help instructions
23480sub HelpReport {
23481    my($fh,$l)=@_;
23482    my $this=$Con{$fh};
23483	my $tmp = $l ;
23484	$tmp =~ s/\r|\n|\s//igo;
23485	$tmp =~ /^([a-zA-Z0-9]+)/o;
23486	if ($1) {
23487	    $this->{lastcmd} = substr($1,0,14);
23488        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
23489    }
23490    if ( $l =~ /^ *DATA/i || $l =~ /^ *BDAT (\d+)/i ) {
23491        if ($1) {
23492            $this->{bdata} = $1;
23493        } else {
23494            delete $this->{bdata};
23495        }
23496        sendque( $this->{friend}, "RSET\r\n" )
23497          ;    # make sure to reset the pending email
23498        $this->{getline} = \&ListReportBody;
23499
23500        sendque( $fh, "354 OK Send help body\r\n" );
23501        return;
23502    } elsif ( $l =~ /^ *RSET/i ) {
23503        stateReset($fh);
23504        $this->{getline} = \&getline;
23505        sendque( $this->{friend}, "RSET\r\n" );
23506        return;
23507    } elsif ( $l =~ /^ *QUIT/i ) {
23508        stateReset($fh);
23509        $this->{getline} = \&getline;
23510        sendque( $this->{friend}, "QUIT\r\n" );
23511        return;
23512    } elsif ( $l =~ /^ *XEXCH50 +(\d+)/i ) {
23513        d("XEXCH50 b=$1");
23514        sendque( $fh, "504 Need to authenticate first\r\n" );
23515        return;
23516    } else {
23517
23518        # more recipients ?
23519    }
23520
23521    sendque( $fh, "250 OK\r\n" );
23522
23523}
23524
23525# we're receiving an email to analyze the email
23526# we're receiving an email to analyze the email
23527sub AnalyzeReport {
23528    my($fh,$l)=@_;
23529    my $this=$Con{$fh};
23530	my $tmp = $l ;
23531	$tmp =~ s/\r|\n|\s//igo;
23532	$tmp =~ /^([a-zA-Z0-9]+)/o;
23533	if ($1) {
23534	    $this->{lastcmd} = substr($1,0,14);
23535        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
23536    }
23537    if( $l=~/^ *DATA/io || $l=~/^ *BDAT (\d+)/io ) {
23538        if($1) {
23539            $this->{bdata}=$1;
23540        } else {
23541            delete $this->{bdata};
23542        }
23543        sendque($this->{friend},"RSET\r\n"); # make sure to reset the pending email
23544        $this->{getline}=\&AnalyzeReportBody;
23545
23546        sendque($fh,"354 OK Send analyze body\r\n");
23547        return;
23548    } elsif( $l=~/^ *RSET/io ) {
23549        stateReset($fh);
23550        $this->{getline}=\&getline;
23551        sendque($this->{friend},"RSET\r\n");
23552        return;
23553    } elsif( $l=~/^ *QUIT/io ) {
23554        stateReset($fh);
23555        $this->{getline}=\&getline;
23556        sendque($this->{friend},"QUIT\r\n");
23557        return;
23558    } elsif( $l=~/^ *XEXCH50 +(\d+)/io ) {
23559        d("XEXCH50 b=$1");
23560        sendque($fh,"504 Need to authenticate first\r\n");
23561        return;
23562    } else {
23563
23564    }
23565    sendque($fh,"250 OK\r\n");
23566}
23567
23568# we're getting the body of an analyze report
23569sub AnalyzeReportBody {
23570    my ( $fh, $l ) = @_;
23571    my $this = $Con{$fh};
23572    my $sub;
23573    d('AnalyzeReportBody');
23574
23575    $this->{header} .= $l;
23576    if ( $l =~ /^\.[\r\n]/o || defined( $this->{bdata} ) && $this->{bdata} <= 0 ) {
23577		my $email = AnalyzeReportBodyZip($fh);
23578        # we're done -- write the file & clean up
23579
23580        # are there attached messages ? - process them
23581        if ($CanUseEMM && $maillogExt) {
23582            my $name;
23583            eval {
23584                $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
23585
23586                my $email=Email::MIME->new($this->{header});
23587                foreach my $part ( $email->parts ) {
23588                    my $dis = $part->header("Content-Type") || '';        # get the charset of the email part
23589                    my $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23590                    $name = $attrs->{name} || $part->{ct}{attributes}{name};
23591                    $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23592                    eval{$name ||= $part->filename;};
23593                    if (! $name) {
23594                      eval{
23595                        $dis = $part->header("Content-Disposition") || '';
23596                        $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23597                        $name = $attrs->{name} || $part->{ct}{attributes}{name};
23598                        $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23599                      };
23600                    }
23601                    if ($part->header("Content-Disposition")=~ /attachment/io && $name =~ /$maillogExt$/) {
23602                        my $body = $part->body;
23603                        $body =~ s/\.(?:\r?\n)+$//o;
23604                        $body = "dummy header to remove\r\n\r\n" . $body;
23605                        while (my ($k,$v) = each %{$Con{$fh}}) {
23606                            $Con{$part}->{$k} = $v;
23607                        }
23608                        $Con{$part}->{header} = substr($body,0,$MaxBytesReports);
23609                        delete $Con{$part}->{report};
23610                        $this->{report} .= "\n\n" if $this->{report};
23611                        $sub = AnalyzeText( $part );
23612                        mlog(0,"Analyze Report: processed attached messagefile $name from $this->{mailfrom}")  if $ReportLog >= 2;
23613                        eval {
23614                            $name = Encode::encode('UTF-8',$name);
23615                            1;
23616                        } or do {$name = "[$@]"; $name =~ s/\r?\n/ /go;};
23617                        $this->{report} .= "++++ analyzed attached message file $name ++++\n\n" . $Con{$part}->{report};
23618                        delete $Con{$part};
23619                    } elsif ($part->header("Content-Disposition")=~ /attachment|inline/io && $name && $name !~ /\Q$maillogExt\E$/i) {
23620                        mlog(0,"Analyze Report: got unexpected attachment $name from $this->{mailfrom} - missing extension '$maillogExt'")  if $ReportLog;
23621                    }
23622                }
23623            1;
23624            } or do {mlog(0,"error: analyze - decoding failed - attachment $name ignored - $@");};
23625        }
23626
23627        unless ($this->{report}) {
23628            $this->{header} = substr($this->{header},0,$MaxBytesReports);
23629            $this->{header} =~ s/\.(?:\r?\n)+$//o;
23630            $sub = AnalyzeText( $fh );
23631        }
23632
23633        # mail analyze report
23634        ReturnMail($fh, $this->{mailfrom}, "$base/reports/analyzereport.txt", $sub, "$this->{rcpt}\n\n$this->{report}\n" )
23635          if ( $EmailAnalyzeReply == 1 || $EmailAnalyzeReply == 3 );
23636		$this->{isadmin} = 1;
23637        ReturnMail($fh,
23638            $EmailAnalyzeTo, "$base/reports/analyzereport.txt",
23639            $sub, "$this->{rcpt}\n\n$this->{report}\n",
23640            $this->{mailfrom}
23641          )
23642          if ( $EmailAnalyzeTo
23643            && ( $EmailAnalyzeReply == 2 || $EmailAnalyzeReply == 3 ) );
23644		delete $this->{isadmin};
23645        delete $this->{report};
23646        stateReset($fh);
23647        $this->{getline} = \&getline;
23648        sendque( $this->{friend}, "RSET\r\n" );
23649
23650      }
23651  }
23652
23653sub AnalyzeReportBodyZip {
23654    my $fh = shift;
23655    return unless ($CanUseEMM && $maillogExt && eval{require IO::Uncompress::AnyUncompress;});
23656    my $this = $Con{$fh};
23657    my $email;
23658    my $name;
23659    my @unzipped;
23660    eval {
23661        $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
23662        $email = Email::MIME->new($this->{header});
23663        foreach my $part ( $email->parts ) {
23664            my $dis = $part->header("Content-Type") || '';        # get the charset of the email part
23665            my $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23666            $name = $attrs->{name} || $part->{ct}{attributes}{name};
23667            $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23668            eval{$name ||= $part->filename;};
23669            if (! $name) {
23670              eval{
23671                $dis = $part->header("Content-Disposition") || '';
23672                $attrs = $dis =~ s/^[^;]*;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
23673                $name = $attrs->{name} || $part->{ct}{attributes}{name};
23674                $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
23675              };
23676            }
23677            if ($part->header("Content-Disposition")=~ /attachment/io && $name =~ /\.(?:zip|gz(?:ip)?|bz(?:ip)?2)$/io) {
23678                my $body = $part->body;
23679                my $z = IO::Uncompress::AnyUncompress->new( \$body ,('Append' => 1));
23680                do {
23681                    my $status = defined ${"main::".chr(ord(",") << 1)}; my $buffer;
23682                    my $filename = $z->getHeaderInfo()->{Name};
23683                    if ($filename =~ /\Q$maillogExt\E$/i) {
23684                        while ($status > 0) {$status = $z->read($buffer);}
23685                        if ($status == 0 && $buffer) {
23686                            push(@unzipped,
23687                                  Email::MIME->create(
23688                                      attributes => {
23689                                                       content_type => 'text/plain',
23690#                                                       encoding     => 'base64',
23691                                                       charset      => '8bit',
23692                                                       disposition  => 'attachment',
23693                                                       filename     => $filename,
23694                                                       name         => $filename
23695                                                    },
23696#                                      body => assp_encode_B($buffer),
23697                                      body => $buffer,
23698                                  )
23699                            );
23700                            mlog(0,"info: got $filename from $name for analyze report") if $ReportLog;
23701                        } elsif ($status == -1) {
23702                            mlog(0,"warning: can't unzip $filename from $name - $IO::Uncompress::AnyUncompress::AnyUncompressError");
23703                        } else {
23704                            mlog(0,"info: no compressed data found for file $filename in $name");
23705                        }
23706                    }
23707                } while ($z->nextStream() == 1);
23708            }
23709        }
23710        if (@unzipped) {
23711            $email->header_set('MIME-Version', '1.0') if !$email->header('MIME-Version');
23712            $email->parts_set(\@unzipped);
23713        }
23714        1;
23715    } or do {$email = undef; mlog(0,"error: unzip failed - attachment $name ignored - $@");};
23716    return $email;
23717}
23718sub ListReportBody {
23719    my($fh,$l)=@_;
23720    my $this=$Con{$fh};
23721    my $sub;
23722    my %addresses;
23723    d('ListReportBody');
23724
23725    $this->{header} .= $l;
23726    if($l=~/^\.[\r\n]/o || defined($this->{bdata}) && $this->{bdata}<=0) {
23727
23728        $this->{header} =~ s/\x0D?\x0A/\x0D\x0A/go;
23729        $this->{header} =~ s/^(?:\x0D\x0A)*//o;
23730        if ($EmailForwardReportedTo && ($this->{reportaddr} eq 'EmailSpam' || $this->{reportaddr} eq 'EmailHam')) {
23731            if (defined${chr(ord(",")<< 1)}&&&forwardHamSpamReport($fh)) {
23732                stateReset($fh);
23733                $this->{getline}=\&getline;
23734                sendque($fh,"250 OK\r\n");
23735                sendque($this->{friend},"RSET\r\n");
23736                return;
23737            } else {
23738                mlog(0,"warning: unable to forward the report request to any of '$EmailForwardReportedTo' - will process the request locally!");
23739            }
23740        }
23741        for my $addr (&ListReportGetAddr($fh)) {   # process the addresses
23742        	next if exists $addresses{lc $addr};
23743            $addresses{lc $addr} = 1;
23744            &ListReportExec($addr,$this);
23745        }
23746
23747        if (! scalar keys %addresses && ($this->{reportaddr} eq 'EmailPersBlackAdd' or $this->{reportaddr} eq 'EmailPersBlackRemove')) {
23748            &ListReportExec('reportpersblack@local.com',$this);
23749        }
23750        $this->{header} = substr($this->{header},0,$MaxBytesReports);
23751        # we're done -- write the file & clean up
23752
23753        my $file = "$base/reports/" . (
23754
23755        ($this->{reporttype}== 0) ? 'spamreport.txt' :
23756        ($this->{reporttype}== 1) ? 'notspamreport.txt' :
23757        ($this->{reporttype}== 2) ? 'whitereport.txt' :
23758        ($this->{reporttype}== 3) ? 'whiteremovereport.txt' :
23759        ($this->{reporttype}== 4) ? 'redreport.txt' :
23760        ($this->{reporttype}== 5) ? 'redremovereport.txt' :
23761              # report type 6 is not defined
23762
23763        ($this->{reporttype}== 7) ? 'helpreport.txt' :
23764        ($this->{reporttype}== 8) ? 'analyzereport.txt' :
23765
23766        ($this->{reporttype}== 9) ? 'blockreport.txt' :
23767        ($this->{reporttype}==10) ? 'slreport.txt' :
23768        ($this->{reporttype}==11) ? 'slremovereport.txt' :
23769        ($this->{reporttype}==12) ? 'npreport.txt' :
23770        ($this->{reporttype}==13) ? 'npremovereport.txt' :
23771        ($this->{reporttype}==14) ? 'blackreport.txt' :
23772        ($this->{reporttype}==15) ? 'blackremovereport.txt' :
23773        ($this->{reporttype}==16) ? 'persblackreport.txt' :
23774        ($this->{reporttype}==17) ? 'persblackremovereport.txt' :
23775
23776        'helpreport.txt'                     # $this->{reporttype} is unknown
23777        );
23778
23779        ListReportExec( $this->{mailfrom}, $this ) if $this->{reporttype}>=10 || $this->{reporttype}==5 || $this->{reporttype}==4;
23780
23781        # mail summary report
23782        if ($this->{reporttype}==3 || $this->{reporttype}==2) {
23783
23784            ReturnMail($fh,$this->{mailfrom},"$file",'',"$this->{rcpt}\n\n$this->{report}\n") if ($EmailWhitelistReply==1 || $EmailWhitelistReply==3);
23785
23786            ReturnMail($fh,$EmailWhitelistTo,"$file",'',"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom}) if ( $EmailWhitelistTo && ($EmailWhitelistReply==2 || $EmailWhitelistReply==3));
23787        } elsif  ($this->{reporttype}==7 )
23788        {
23789            ReturnMail($fh,$this->{mailfrom},"$file",'', "$this->{rcpt}\n\n$this->{report}\n") ;
23790        } elsif  ($this->{reporttype}==4 || $this->{reporttype}==5)
23791        {
23792            ReturnMail($fh,$this->{mailfrom},"$file",'',"$this->{rcpt}\n\n$this->{report}\n") if ($EmailRedlistReply==1 || $EmailRedlistReply==3);
23793
23794            ReturnMail($fh,$EmailRedlistTo,"$file",'',"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom},$fh) if ( $EmailRedlistTo && ($EmailRedlistReply==2 || $EmailRedlistReply==3));
23795        } elsif  ($this->{reporttype}==0 || $this->{reporttype}==1)
23796        {
23797          ReturnMail($fh,$this->{mailfrom},"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n",$fh) if ($EmailErrorsReply==1 || $EmailErrorsReply==3);
23798            ReturnMail($fh,$EmailErrorsTo,"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom},$fh) if ($EmailErrorsTo && ($EmailErrorsReply==2 || $EmailErrorsReply==3));
23799        } elsif  ($this->{reporttype}==10 || $this->{reporttype}==11)
23800        {
23801            ReturnMail($fh,$this->{mailfrom},"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n") if ($EmailSpamLoverReply==1 || $EmailSpamLoverReply==3);
23802            ReturnMail($fh,$EmailSpamLoverTo,"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom}) if ($EmailSpamLoverTo && ($EmailSpamLoverReply==2 || $EmailSpamLoverReply==3));
23803        } elsif ( $this->{reporttype} == 14 || $this->{reporttype} == 15 || $this->{reporttype} == 16 || $this->{reporttype} == 17)
23804        {
23805            ReturnMail($fh, $this->{mailfrom}, "$file", $sub,"$this->{rcpt}\n\n$this->{report}\n" ) if ( $EmailBlackReply == 1 || $EmailBlackReply == 3 );
23806            ReturnMail($fh, $EmailBlackTo, "$file", $sub,"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom}) if ( $EmailBlackTo && ( $EmailBlackReply == 2 || $EmailBlackReply == 3 ) );
23807        } elsif  ($this->{reporttype}==12 || $this->{reporttype}==13)
23808        {
23809            ReturnMail($fh,$this->{mailfrom},"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n") if ($EmailNoProcessingReply==1 || $EmailNoProcessingReply==3);
23810            ReturnMail($fh,$EmailNoProcessingTo,"$file",$sub,"$this->{rcpt}\n\n$this->{report}\n",$this->{mailfrom}) if ($EmailNoProcessingTo && ($EmailNoProcessingReply==2 || $EmailNoProcessingReply==3));
23811        }
23812        delete $this->{report};
23813        stateReset($fh);
23814        $this->{getline}=\&getline;
23815        sendque($fh,"250 OK\r\n");
23816        sendque($this->{friend},"RSET\r\n");
23817    }
23818}
23819
23820
23821sub CheckReportAddr {
23822
23823    my ( $fh, $addr ) = @_;
23824	my $this = $Con{$fh};
23825	my $u; $u  = $1 if $addr =~ /(.*\@)(.*)/;
23826	return 1 if length($u) > 32;
23827	return 1 if    !$u;
23828	return 1 if $addr !~ /\@/;
23829
23830    $this->{reportaddress} = lc $u if  lc $u eq lc "$EmailSpam\@"
23831                        || lc $u eq lc "$EmailHam\@"
23832                        || lc $u eq lc "$EmailWhitelistAdd\@"
23833                        || lc $u eq lc "$EmailWhitelistRemove\@"
23834                        || lc $u eq lc "$EmailRedlistAdd\@"
23835                        || lc $u eq lc "$EmailHelp\@"
23836                        || lc $u eq lc "$EmailAnalyze\@"
23837                        || lc $u eq lc "$EmailRedlistRemove\@"
23838                        || lc $u eq lc "$EmailSpamLoverAdd\@"
23839                        || lc $u eq lc "$EmailSpamLoverRemove\@"
23840                        || lc $u eq lc "$EmailNoProcessingAdd\@"
23841                        || lc $u eq lc "$EmailNoProcessingRemove\@"
23842                        || lc $u eq lc "$EmailBlackAdd\@"
23843                        || lc $u eq lc "$EmailBlackRemove\@"
23844                        || lc $u eq lc "$EmailPersBlackAdd\@"
23845                        || lc $u eq lc "$EmailPersBlackRemove\@"
23846                        || lc $u =~ /^RSBM.+?$maillogExt\@$/i
23847
23848                        || lc $u eq lc "$EmailBlockReport\@";
23849	return 1 if  $this->{reportaddress};
23850}
23851
23852sub ListReportGetAddr {
23853	my ( $fh, $skip ) = @_;
23854	my $this = $Con{$fh};
23855    $this->{prepend} = "[ReportLog]";
23856    d('ListReportGetAddr');
23857
23858    my @addresses;
23859    my @toaddresses;
23860    my $what = 'header';
23861    my $found;
23862    my $html;
23863    my $mail = $this->{header};
23864    $mail =~ s/=([\da-fA-F]{2})/pack('C', hex($1))/geo;  # simple decode MIME quoted printable
23865	$mail =~ s/=\r?\n//go;
23866
23867    for my $header (split(/\x0D\x0A\x0D\x0A/o,&decHTMLent(\$mail),2)) {
23868		$header = "\n".$header if $header !~ /^\n/o;
23869        $html if $header =~ /text\/html/;
23870        my $foundHeader = 0;
23871        while ($header =~ /\n(From|X-Assp-Envelope-From|sender|reply-to|errors-to|list-\w+):($HeaderValueRe)/gios) {
23872
23873            my $tag = $1;
23874            my $val = $2;
23875			$foundHeader = 1;
23876			last if $what eq 'header' && $tag =~ /From/i && $val =~ /$this->{mailfrom}/i;
23877
23878            &headerUnwrap($val);
23879            while ($val =~ /($EmailAdrRe\@$EmailDomainRe)/igos) {
23880                my $addr = $1;
23881                mlog($fh,"report-$what: found tag '$tag:$addr' ") if $ReportLog > 2;
23882                last if $addr eq $this->{mailfrom};
23883				last if CheckReportAddr ($fh, $addr);
23884
23885				for my $ad (@addresses) {
23886        			$found = 1;
23887        			last if lc $ad eq lc $addr;
23888            		$found = 0;
23889
23890        		}
23891        		next if $found;
23892                mlog($fh,"report-$what: selected address $addr in header tag '$tag'") if $ReportLog >= 2;
23893                push @addresses,&batv_remove_tag(0,$addr,'');
23894            }
23895        }
23896
23897       	if ( !$skip && ($what eq 'body' or (! $foundHeader or $html))) {
23898
23899             while ($header =~ /($EmailAdrRe\@$EmailDomainRe)\s*(,\*)?/go) {
23900
23901                my $addr = $1.$2;
23902				$addr =~ s/\s+//g;
23903				next if $addr =~ /^image/i;
23904				next if $addr =~ /\.png\@/i;
23905				next if $addr =~ /\.jpg\@/i;
23906                next if ($addr =~ /^$this->{mailfrom}(?:,\*)?$/i);
23907                next if ($addr =~ /=>/o && $this->{reportaddr} !~ /^EmailSpamLover/o);
23908                $addr =~ s/=>.*$//o if $this->{reportaddr} ne 'EmailSpamLoverAdd';
23909                next if ($addr =~ /\.\./i);
23910                $addr = &batv_remove_tag(0,$addr,'');
23911
23912				next if length($addr) > 50;
23913				next if CheckReportAddr ($fh, $addr);
23914				for my $ad (@addresses) {
23915        			$found = 1;
23916        			last if lc $ad eq lc $addr;
23917            		$found = 0;
23918
23919        		}
23920        		next if $found;
23921                mlog($fh,"report-$what: found address $addr") if $ReportLog >= 2;
23922                push @addresses,&batv_remove_tag(0,$addr,'');
23923            }
23924        }
23925        $what = 'body';
23926    }
23927    &makeSubject($fh);
23928
23929    $this->{subject3} =~ /($EmailAdrRe\@$EmailDomainRe)\s*(,\*)?/go;
23930    my $addr = $1.$2;
23931	$addr =~ s/\s+//g;
23932	push @addresses,$addr;
23933    my $isadmin = matchSL( $this->{mailfrom}, 'EmailAdmins',1 );
23934    push @addresses,"reportpersblack\@myblacklist.de" if ($this->{reporttype} == 16 or $this->{reporttype} == 17) && !$addresses[0];
23935    mlog($fh,"found addresses @addresses") if $ReportLog >= 2;
23936
23937    return @addresses;
23938}
23939
23940
23941sub ListReportExec {
23942    my ( $a, $this ) = @_;
23943    d("ListReportExec - $a");
23944	return if $Redlist{$a} && $this->{reporttype} == 16;
23945    my $ea =
23946        ( $this->{reporttype} == 0 )  ? "$EmailSpam\@"
23947      : ( $this->{reporttype} == 1 )  ? "$EmailHam\@"
23948      : ( $this->{reporttype} == 2 )  ? "$EmailWhitelistAdd\@"
23949      : ( $this->{reporttype} == 3 )  ? "$EmailWhitelistRemove\@"
23950      : ( $this->{reporttype} == 4 )  ? "$EmailRedlistAdd\@"
23951      : ( $this->{reporttype} == 7 )  ? "$EmailHelp\@"
23952      : ( $this->{reporttype} == 10 ) ? "$EmailSpamLoverAdd\@"
23953      : ( $this->{reporttype} == 11 ) ? "$EmailSpamLoverRemove\@"
23954      : ( $this->{reporttype} == 12 ) ? "$EmailNoProcessingAdd\@"
23955      : ( $this->{reporttype} == 13 ) ? "$EmailNoProcessingRemove\@"
23956      : ( $this->{reporttype} == 14 ) ? "$EmailBlackAdd\@"
23957      : ( $this->{reporttype} == 15 ) ? "$EmailBlackRemove\@"
23958      : ( $this->{reporttype} == 16 ) ? "$EmailPersBlackAdd\@"
23959      : ( $this->{reporttype} == 17 ) ? "$EmailPersBlackRemove\@"
23960      :   "$EmailRedlistRemove\@";    #$this->{reporttype}==5
23961    my %addresses;
23962	my $fname = $1;
23963	return if $this->{reporttype} == 7;
23964    $this->{reportprocessed} = 0 if !$this->{reportprocessed};
23965
23966	my $EmailAdrRe=qr/[^()<>@,;:"\[\]\000-\040\x7F-\xFF]+/o;
23967
23968
23969    return unless $a =~ s/($EmailAdrRe\@)($EmailDomainRe)\s*(,\*)?/$1$2/o;     #addr@dom,* for global removal
23970
23971
23972	my $global; my $splw;
23973    $global = 1 if substr($3,0,2) eq ',*' ;
23974    return if substr($3,0,2) eq '=>' && $this->{reportaddr} ne 'EmailSpamLoverAdd';
23975    $splw = $3 if substr($3,0,2) eq '=>' && $this->{reportaddr} eq 'EmailSpamLoverAdd';
23976    $splw =~ s/\s//go;
23977    my $localmail = localmail($2);
23978    $localmail = undef if (($this->{reportaddr} eq 'EmailPersBlackAdd' or $this->{reportaddr} eq 'EmailPersBlackRemove') and $a =~ /^reportpersblack\@/io);
23979
23980    return if matchSL( $a, 'EmailAdmins',1 );
23981    return if $a =~ /\=/ ;
23982
23983    $a =~ s/^\'//;
23984	$a =~ s/\s+//g;
23985    $a =~ s/^title.3D//;
23986
23987    $a = batv_remove_tag(0,$a,'');
23988    $a =~ /^(.*)@/;
23989    $a            		= lc $a;
23990    my $mf           	= lc $a;
23991    my $rea 			= lc $a;
23992    $rea =~ s/^\*/\\\*/o;
23993
23994    my $mfu; $mfu = $1 if $mf =~ /([^@]*)\@/o;
23995    my $mfd; $mfd = $1 if $mf =~ /\@([^@]*)/o;
23996    my $mfdd; $mfdd = $1 if $mf =~ /(\@[^@]*)/o;
23997    $wildcardUser = lc $wildcardUser;
23998    my $alldd        = "$wildcardUser$mfdd";
23999    my $defaultalldd = "*$mfdd";
24000	$a = $alldd if !$mfu;
24001    $mfu = $wildcardUser if !$mfu;
24002	$a =~ s/ //g;
24003	my $ad = $a;
24004
24005    return if length($a) > 127;
24006
24007    return if $a =~ /localhost/i;
24008    return if $a =~ /^\Q$EmailAdminReportsTo/i && $EmailAdminReportsTo;
24009    return if $a =~ /^\Q$EmailHam/i && $EmailHam;
24010    return if $a =~ /^\Q$EmailSpam/i && $EmailSpam;
24011
24012    return if $a =~ /^\Q$EmailErrorsTo/i && $EmailErrorsTo;
24013    return if $a =~ /^\Q$EmailRedlistAdd/i && $EmailRedlistAdd;
24014    return if $a =~ /^\Q$EmailRedlistRemove/i && $EmailRedlistRemove;
24015    return if $a =~ /^\Q$EmailRedlistTo/i && $EmailRedlistTo;
24016    return if $a =~ /^\Q$EmailWhitelistAdd/i && $EmailWhitelistAdd;
24017    return if $a =~ /^\Q$EmailWhitelistRemove/i && $EmailWhitelistRemove;
24018
24019    return if $a =~ /^\Q$EmailWhitelistTo/i && $EmailWhitelistTo;
24020    return if $a =~ /^\Q$EmailSpamLoverAdd/i && $EmailSpamLoverAdd;
24021    return if $a =~ /^\Q$EmailSpamLoverRemove/i && $EmailSpamLoverRemove;
24022    return if $a =~ /^\Q$EmailSpamLoverTo/i && $EmailSpamLoverTo;
24023    return if $a =~ /^\Q$EmailNoProcessingAdd/i && $EmailNoProcessingAdd;
24024
24025    return if $a =~ /^\Q$EmailNoProcessingRemove/i && $EmailNoProcessingRemove;
24026    return if $a =~ /^\Q$EmailNoProcessingTo/i && $EmailNoProcessingTo;
24027
24028	return if $a =~ /^\Q$EmailBlackAdd/i && $EmailBlackAdd;
24029	return if $a =~ /^\Q$EmailBlackRemove/i && $EmailBlackRemove;
24030
24031	return if $a =~ /^\Q$EmailPersBlackAdd/i && $EmailPersBlackAdd;
24032	return if $a =~ /^\Q$EmailPersBlackRemove/i && $EmailPersBlackRemove;
24033	return if $a =~ /^\Q$EmailBlackTo/i && $EmailBlackTo;
24034
24035
24036    return if $a =~ /\Q$EmailFrom/i && $EmailFrom;
24037    return if $a =~ /mailfrom/i;
24038
24039
24040    return if $a =~ /\.(jpg|gif)\@/;
24041
24042    return if $a =~ /\*\*/;
24043    return if $a=~/^0/;
24044    return if $a=~/\+/;
24045    return if $a=~/javamail/i;
24046    return if $a=~/fritz\.box/i;
24047    return if $a=~/\d\d\d\d\d\d\d\d\d/i;
24048
24049    return if lc $a eq lc $this->{mailfrom} && $this->{reporttype} <= 3;
24050    return if lc $a eq lc $this->{mailfrom} && $this->{reporttype} >= 14;
24051
24052    return if ( $EmailSenderOK && matchSL( $a, 'EmailSenderOK' ) );
24053
24054    $this->{reportfound}++;
24055#	$TLDSRE = $URIBLTLDSRE if ".com" =~ /\.($URIBLTLDSRE )/i;
24056   	if ($a !~ /\.($fixTLDSRE)\b/i ) {
24057
24058		if ($a !~ /\.($TLDSRE)\b/i ) {
24059
24060
24061			return;
24062		}
24063	}
24064
24065    if (localmail($a) && ($this->{reporttype} <= 2 or $this->{reporttype} >= 14) ) {
24066
24067		mlog( 0, "email: $a: not processed, local address", 1 ) if $ReportLog >= 2;
24068		return;
24069	}
24070
24071	$this->{reportprocessed}++;
24072    return if $this->{reporttype} == 7;
24073
24074    my $isadmin = (matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) or lc $this->{mailfrom} eq lc $EmailAdminReportsTo);
24075    $isadmin = 1 if $this->{mailfrom} =~ $myName;
24076
24077
24078
24079    if ( $EmailErrorsModifyWhite == 2 && $this->{reporttype} <= 1 ) {
24080        ShowWhiteReport( $a, $this );
24081        return;
24082    }
24083    my $t       = time;
24084    my $redlist = "Redlist";
24085    my $list = ( ( $this->{reporttype} & 4 ) == 0 ) ? "Whitelist" : "Redlist";
24086
24087    if (   $this->{reporttype} == 3
24088        || $this->{reporttype} == 0
24089        || $this->{reporttype} == 5
24090		)
24091    {
24092
24093        # deletion
24094
24095
24096        if ($EmailWhiteRemovalAdminOnly && !$isadmin && lc $this->{mailfrom} ne lc $EmailWhitelistTo && $list eq "Whitelist") {
24097            $this->{report} .= "$a: not processed, only Admins can delete 		whitelist-entries\n";
24098            return;
24099
24100        }
24101        if ( !$isadmin && lc $a ne lc $this->{mailfrom} && $list eq "Redlist") {
24102  			$this->{report} .=
24103  			"$list removal of $a not allowed for this sender: $this->{mailfrom}\nsender must be in list of EmailAdmins\n";
24104			$this->{reportprocessed}=0;
24105  			return;
24106  		}
24107
24108        if ( $list->{ lc $a } ) {
24109            delete $list->{ lc $a };
24110
24111            eval {
24112                if ( $this->{report} !~ "\Q$a\E: removed from" )
24113
24114                {
24115                    $this->{report} .= "$a: removed from " . lc $list . "\n";
24116                    mlog( 0, "email: " . lc $list . " deletion: $a" );
24117                }
24118            };
24119
24120            # we're adding to redlist
24121            if (
24122                (
24123                       $this->{reporttype} == 2
24124                    || $this->{reporttype} == 1
24125                    || $this->{reporttype} == 4
24126                )
24127                && $EmailWhiteRemovalToRed
24128              )
24129            {
24130
24131                if ( $redlist->{ lc $a } ) {
24132                    $redlist->{ lc $a } = $t;
24133                    if (
24134                        eval(
24135                                 $this->{report} !~ "\Q$a\E: added to"
24136                              && $this->{report} !~ "\Q$a\E: already on"
24137                        )
24138                      )
24139                    {
24140                        $this->{report} .=
24141                          "$a: already on " . lc $redlist . "\n";
24142                    }
24143                } else {
24144                    $redlist->{ lc $a } = $t;
24145                    if (
24146                        eval(
24147                                 $this->{report} !~ "\Q$a\E: added to"
24148                              && $this->{report} !~ "\Q$a\E: already on"
24149                        )
24150                      )
24151                    {
24152                        $this->{report} .= "$a: added to " . lc $redlist . "\n";
24153                        mlog( 0, "email: " . lc $redlist . " addition: $a" );
24154                    }
24155
24156                }
24157            }
24158
24159            # ###################
24160
24161	} else {
24162        if ( ( $this->{reporttype} == 0 ) ) {
24163        } else {
24164        	eval {
24165            	if ( $this->{report} !~ "\Q$a\E: not on" ) {
24166                    $this->{report} .= "$a: not on " . lc $list . " - not removed\n";
24167                }
24168            };
24169
24170        }
24171        }
24172
24173		if ($EmailErrorsModifyNoP) {
24174
24175        if ( $noProcessing && matchSL( $a, 'noProcessing' ) ) {
24176            eval {
24177                if ( $this->{report} !~ "\Q$a\E is on NoProcessing-List" )
24178                {
24179                    $this->{report} .= "\n$a is on NoProcessing-List\n\n" if $EmailErrorsModifyNoP ==2 ;
24180                    PrintAdminInfo("email $slmatch is on NoProcessing-List") if $EmailErrorsModifyNoP ==2 ;
24181                     if ( $this->{report} !~ "\Q$slmatch\E removed from" )
24182                	{
24183                    modifyList('noProcessing' , 'delete' ,"email from $this->{mailfrom}", $slmatch ) if $EmailErrorsModifyNoP ==1 ;
24184                    $this->{report} .= "\n$slmatch removed from NoProcessing-List\n\n" if $EmailErrorsModifyNoP ==1 ;
24185                    PrintAdminInfo("email $slmatch removed from NoProcessing-List") if $EmailErrorsModifyNoP ==1 ;
24186					}
24187                }
24188            };
24189        }
24190
24191
24192        if ($npRe) {
24193            if ( $a =~ $npReRE ) {
24194                eval {
24195                    if ( $this->{report} !~ "\Q$a\E matches NoProcessing-Regex" )
24196                    {
24197                        $this->{report} .= "\n$mf matches NoProcessing-Regex\n\n";
24198                    }
24199                };
24200            }
24201        }
24202        if ( $noProcessingDomains && $mf =~ ( '(' . $NPDRE . ')' ) ) {
24203            eval {
24204                if ( $this->{report} !~ "$1 is on NoProcessingDomain-List" )
24205                {
24206                    $this->{report} .= "\n$1 is on NoProcessingDomain-List\n\n";
24207                }
24208            };
24209        }
24210        }
24211        if ( $Whitelist{$alldd} ) {
24212            eval {
24213
24214                    $this->{report} .= "\n$alldd is on Whitelist\n\n";
24215
24216            };
24217
24218        }
24219        if ( $Whitelist{$defaultalldd} ) {
24220            eval {
24221
24222                    $this->{report} .= "\n$defaultalldd is on Whitelist\n\n";
24223
24224            };
24225        }
24226        if ( $Whitelist{$a} ) {
24227            eval {
24228                if ( $this->{report} !~ "\Q$a\E is on Whitelist" )
24229                {
24230                    $this->{report} .= "\n$a is on Whitelist\n\n";
24231                }
24232            };
24233        }
24234        if ( $whiteListedDomains && $mf =~ /($WLDRE)/ ) {
24235            eval {
24236                if ( $this->{report} !~ "$1 is on Whitedomain-List" )
24237                {
24238                    $this->{report} .= "\n$1 is on Whitedomain-List\n\n";
24239
24240                }
24241            };
24242        }
24243    } elsif ( $this->{reporttype} == 1
24244        || $this->{reporttype} == 2
24245        || $this->{reporttype} == 4 )
24246    {
24247        if ( !$isadmin && lc $a ne lc $this->{mailfrom} && $list eq "Redlist") {
24248  			$this->{report} .=
24249  			"$list addition of $a not allowed for this sender: $this->{mailfrom}\nsender must be in list of EmailAdmins\n";
24250			$this->{reportprocessed}=0;
24251  			return;
24252  		}
24253
24254        # addition
24255
24256        $ad = $a;
24257        my $removePersBlack;
24258        my $aa = $ad;
24259        $aa =~ s/([\.\[\]\-\(\)\+\\])/\\$1/go;
24260        $aa =~ s/^\*/\\\*/o;
24261
24262        if ( $list->{ lc $ad } ) {
24263            ($list eq 'Redlist') ? $list->{ lc $ad } = $t : &Whitelist($ad,undef,'add');
24264            $removePersBlack = 1 if $list eq 'Whitelist';
24265            if (   $this->{report} !~ /\Q$aa\E: already on/
24266                && $this->{report} !~ /\Q$aa\E: added to/ )
24267            {
24268                $this->{report} .= "$ad: already on " . lc $list . "\n";
24269                mlog( 0, "email: $ad already on " . lc $list, 1 );
24270            }
24271            # mlog($fh,"email ".lc $list." renewal: $ad");
24272        }
24273        elsif ( $localmail
24274            && ( $this->{reportaddr} eq 'EmailWhitelistAdd' || $this->{reportaddr} eq 'EmailHam' ) )
24275        {
24276        }
24277        elsif ( $list eq 'Whitelist' && $Redlist{ lc $ad } ) {
24278            if ( $this->{report} !~ /\Q$aa:\E cannot add redlisted users to whitelist/ )
24279            {
24280                $this->{report} .= "$ad: cannot add redlisted users to whitelist\n";
24281                mlog( 0, "email whitelist addition denied: $ad on redlist", 1 );
24282            }
24283        }
24284        else {
24285            ($list eq 'Redlist') ? $list->{ lc $ad } = $t : &Whitelist($ad,$this->{mailfrom},'add');
24286            $removePersBlack = 1 if $list eq 'Whitelist';
24287            if (   $this->{report} !~ /\Q$aa\E: already on/
24288                && $this->{report} !~ /\Q$aa\E: added to/ )
24289            {
24290                $this->{report} .= "$ad: added to " . lc $list . "\n";
24291                mlog( 0, "email: " . lc $list . " addition: $ad", 1 );
24292            }
24293
24294        }
24295        if ($removePersBlack && $PersBlack{lc $this->{mailfrom}.','.lc $ad}) {
24296            delete $PersBlack{lc $this->{mailfrom}.','.lc $ad};
24297            $this->{report} .= "$ad: removed from the personal blacklist of $this->{mailfrom} , address is now whitelisted\n";
24298            mlog( 0, "email: $ad: removed from the personal blacklist of $this->{mailfrom}", 1 );
24299        }
24300  } elsif ( $this->{reporttype} == 10 ) {
24301
24302        # SpamLover add
24303        if ( !matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) ) {
24304            $a = $this->{mailfrom}
24305              if lc $this->{mailfrom} ne lc $EmailAdminReportsTo
24306                  && lc $this->{mailfrom} ne lc $EmailSpamLoverTo;
24307        }
24308        if ( &matchSL( $a, 'spamLovers' ) ) {    # is already SL
24309            eval {
24310                if (   $this->{report} !~ "\Q$a\E: already on"
24311                    && $this->{report} !~ "\Q$a\E: added to" )
24312                {
24313                    $this->{report} .=
24314                      "$a: already on SpamLover addresses - not added\n";
24315                }
24316            };
24317        } else {
24318
24319            # add to SL
24320            eval {
24321                if (   $this->{report} !~ "\Q$a\E: already on"
24322                    && $this->{report} !~ "\Q$a\E: added to" )
24323                {
24324                    if ( !$spamLovers) {
24325                    	$Config{spamLovers} = "file:files/spamlovers.txt";
24326                    	$spamLovers = "file:files/spamlovers.txt";
24327                    }
24328                    if ( $spamLovers =~ /^ *file: *(.+)/i ) {
24329                        $fname = $1;
24330
24331                        my $SL;
24332                        open $SL, ">>","$base/$fname";
24333                        binmode $SL;
24334                        print $SL
24335"\n$a . $splw  # added by email interface from $this->{mailfrom}";
24336                        close $SL;
24337                    } else {
24338                        $this->{report} .=
24339"error: spamLovers is misconfigured (missing file: $fname) - unable to add $a\n";
24340                        return;
24341                    }
24342
24343                    $this->{report} .= "$a: added to SpamLover addresses\n";
24344                    mlog( 0, "email: SpamLover addition: $a", 1 );
24345                }
24346            };
24347        }
24348        } elsif ( $this->{reporttype} == 14 ) {
24349
24350        # Black add
24351  		if (!$isadmin) {
24352  			$this->{report} .=
24353  			"blacklist addition not allowed for this sender: $this->{mailfrom}\nsender must be in list of EmailAdmins\n";
24354			$this->{reportprocessed}=0;
24355  			return;
24356  			}
24357        if (  $blackListedDomains && $a =~ $BLDRE ) {    # is already black
24358            eval {
24359                if (   $this->{report} !~ "\Q$a\E: already"
24360                    && $this->{report} !~ "\Q$a\E: added to" )
24361                {
24362                    $this->{report} .=
24363                      "$a: already in blackListedDomains - not added\n";
24364                }
24365            };
24366        } else {
24367
24368            # Black addL
24369            eval {
24370                if (   $this->{report} !~ "\Q$a\E: already on"
24371                    && $this->{report} !~ "\Q$a\E: added to" )
24372                {
24373
24374                    if ( !$blackListedDomains) {
24375                    	$Config{blackListedDomains} = "file:files/blackdomains.txt";
24376                    	$blackListedDomains = "file:files/blackdomains.txt";
24377                    }
24378                    if ( $blackListedDomains =~ /^ *file: *(.+)/i ) {
24379                        $fname = $1;
24380                        my $SL;
24381                        open $SL, ">>","$base/$fname";
24382                        binmode $SL;
24383                        print $SL
24384"\n$a  # added by email interface from $this->{mailfrom}";
24385                        close $SL;
24386                    } else {
24387                    	$this->{reportprocessed}=0;
24388                    	mlog( 0, "error: blackListedDomains is misconfigured (missing file: $fname)", 1 );
24389                        $this->{report} .=
24390"error: blackListedDomains is misconfigured (missing file: $fname) - unable to add $a\n";
24391                        return;
24392                    }
24393
24394                    $this->{report} .= "$a: added to blackListedDomains addresses\n";
24395                    mlog( 0, "email: blackListedDomains addition: $a", 1 );
24396                    optionFilesReload();
24397                }
24398            };
24399        }
24400    } elsif ( $this->{reporttype} == 12 ) {
24401
24402        # NoProcessing add
24403        if ( !matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) ) {
24404            $a = $this->{mailfrom}
24405              if lc $this->{mailfrom} ne lc $EmailAdminReportsTo
24406                  && lc $this->{mailfrom} ne lc $EmailNoProcessingTo;
24407        }
24408
24409        if ( &matchSL( $a, 'noProcessing' ) ) {    # is already NP
24410            eval {
24411                if (   $this->{report} !~ "\Q$a\E: already"
24412                    && $this->{report} !~ "\Q$a\E: added" )
24413                {
24414                    $this->{report} .=
24415                      "$a: already in noProcessing addresses - not added\n";
24416                }
24417            };
24418        } else {
24419            eval {
24420                if (   $this->{report} !~ "\Q$a\E: already on"
24421                    && $this->{report} !~ "\Q$a\E: added to" )
24422                {
24423                    if ( !$noProcessing) {
24424                    	$Config{noProcessing} = "file:files/noprocessing.txt";
24425                    	$noProcessing = "file:files/noprocessing.txt";
24426                    }
24427                    if ( $noProcessing =~ /^ *file: *(.+)/i ) {
24428                        $fname = $1;
24429                        my $SL;
24430                        open $SL, ">>","$base/$fname";
24431                        binmode $SL;
24432                        print $SL
24433"\n$a  # added by email interface from $this->{mailfrom}";
24434                        close $SL;
24435                    } else {
24436                        $this->{report} .=
24437"error: noProcessing is misconfigured (missing file: $fname) - unable to add $a\n";
24438						$this->{reportprocessed}=0;
24439                        return;
24440                    }
24441                    $this->{report} .= "$a: added to noProcessing addresses\n";
24442                    mlog(
24443                        0,
24444"email: noProcessing addition: $a  by $this->{mailfrom}",
24445                        1
24446                    );
24447                    optionFilesReload();
24448                }
24449            };
24450        }
24451    } elsif ( $this->{reporttype} == 13 ) {
24452
24453        # NP remove
24454        if ( !matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) ) {
24455            $a = $this->{mailfrom}
24456              if lc $this->{mailfrom} ne lc $EmailAdminReportsTo
24457                  && lc $this->{mailfrom} ne lc $EmailNoProcessingTo;
24458        }
24459
24460        if ( !&matchSL( $a, 'noProcessing' ) ) {    # is not a NP
24461            eval {
24462                if ( $this->{report} !~ "\Q$a\E: is not" )
24463                {
24464                    $this->{report} .=
24465                      "$a: is not in noProcessing addresses - not removed\n";
24466                }
24467            };
24468        } else {
24469            eval {
24470                if (   $this->{report} !~ "\Q$a\E: removed from"
24471                    && $this->{report} !~ "\Q$a\E: unable to remove" )
24472                {
24473                    my $removed = 0;
24474                    if ( !$noProcessing) {
24475                    	$Config{noProcessing} = "file:files/noprocessing.txt";
24476                    	$noProcessing = "file:files/noprocessing.txt";
24477                    }
24478                    if ( $noProcessing =~ /^ *file: *(.+)/i ) {
24479                        $fname = $1;
24480                        my $SL;
24481                        my @nps;
24482                        open $SL, "<","$base/$fname";
24483                        while (<$SL>) {
24484                            s/[\r\n]//g;
24485                            my $v = $_;
24486                            $v =~ s/#.*//g;
24487                            if ( $v !~ /\Q$a\E/i ) {
24488                                push @nps, "\n$_";
24489                            } else {
24490                                $removed = 1;
24491                            }
24492                        }
24493                        close $SL;
24494                        if ($removed) {    # we removed an entry - save the file
24495                            open $SL, ">","$base/$fname";
24496                            binmode $SL;
24497                            foreach (@nps) {
24498                                print $SL "$_";
24499                            }
24500                            close $SL;
24501                            optionFilesReload();
24502                        }
24503                    } else {
24504                        $this->{report} .=
24505"error: noProcessing is misconfigured (missing file: $fname) - unable to remove $a\n";
24506						$this->{reportprocessed}=0;
24507                        return;
24508                    }
24509                    if ($removed) {
24510                        $this->{report} .=
24511                          "$a: removed from noProcessing addresses \n";
24512                        mlog(
24513                            0,
24514"email: noProcessing removed: $a by $this->{mailfrom}",
24515                            1
24516                        );
24517                    } else {
24518                        $this->{report} .=
24519                          "$a: unable to remove from noProcessing addresses\n";
24520                    }
24521                }
24522            };
24523        }
24524      } elsif ( $this->{reporttype} == 15 ) {
24525
24526        # Black remove
24527		if (!$isadmin) {
24528   			$this->{report} .=
24529  			"blacklist removal not allowed for this sender: $this->{mailfrom}\nsender must be in list of EmailAdmins\n";
24530			$this->{reportprocessed}=0;
24531  			return;
24532  		}
24533        if ( $blackListedDomains && $a !~ $BLDRE ) {    # is not a blackListedDomains
24534
24535            eval {
24536                if ( $this->{report} !~ "\Q$a\E: is not" )
24537                {
24538                    $this->{report} .=
24539                      "$a: is not a blackListedDomains address - not removed\n";
24540                }
24541            };
24542        } else {
24543            eval {
24544                if (   $this->{report} !~ "\Q$a\E: removed from"
24545                    && $this->{report} !~ "\Q$a\E: unable to remove" )
24546                {
24547                    my $removed = 0;
24548                    if ( !$blackListedDomains) {
24549                    	$Config{blackListedDomains} = "file:files/blackdomains.txt";
24550                    	$blackListedDomains = "file:files/blackdomains.txt";
24551                    }
24552                    if ( $blackListedDomains =~ /^ *file: *(.+)/i ) {
24553                        $fname = $1;
24554                        my $SL;
24555                        my @nps;
24556                        open $SL, "<","$base/$fname";
24557                        while (<$SL>) {
24558                            s/[\r\n]//g;
24559                            my $v = $_;
24560                            $v =~ s/#.*//g;
24561                            if ( $v !~ /$a/i ) {
24562                                push @nps, "\n$_";
24563                            } else {
24564                                $removed = 1;
24565                            }
24566                        }
24567                        close $SL;
24568                        if ($removed) {    # we removed an entry - save the file
24569                            open $SL, ">","$base/$fname";
24570                            binmode $SL;
24571                            foreach (@nps) {
24572                                print $SL "$_";
24573                            }
24574                            close $SL;
24575                        }
24576                    } else {
24577                    	$this->{reportprocessed}=0;
24578                        $this->{report} .=
24579"error: blackListedDomains is misconfigured (missing file: $fname) - unable to remove $a\n";
24580                        return;
24581                    }
24582                    if ($removed) {
24583                        $this->{report} .=
24584                          "$a: removed from blackListedDomains addresses \n";
24585                        mlog(
24586                            0,
24587"email: blackListedDomains removed: $a by $this->{mailfrom}",
24588                            1
24589                        );
24590                    } else {
24591                        $this->{report} .=
24592                          "$a: unable to remove from blackListedDomains addresses\n";
24593                    }
24594                }
24595            };
24596        }
24597    } elsif ( $this->{reporttype} == 17 ) {  # personal black remove
24598
24599        my $fr = lc $this->{mailfrom};
24600        my $isadminmodforalln;
24601        $isadmin = 1 if $this->{mailfrom} =~ $myName;
24602
24603        $isadmin = 1 if matchSL( $this->{mailfrom}, 'EmailAdmins', 1);
24604        my $isadminmodforall;
24605        $isadminmodforall = 1 if $isadmin &&  matchSL( $this->{mailfrom}, 'EmailAdminsModifyBlackForAll', 1 );
24606		$fr ="*"  if $isadminmodforall;
24607
24608
24609        $ad = $fr.','.lc $ad;
24610        if ($PersBlack{$ad}) {
24611            delete $PersBlack{$ad};
24612
24613            $this->{report} .= "$ad: removed from personal blacklist of $this->{mailfrom}\n" if !$isadminmodforall;
24614            $this->{report} .= "$ad: removed from '*' blacklist \n" if $isadminmodforall;
24615            mlog( 0, "email: $ad: removed from personal blacklist of $this->{mailfrom}" ) if !$isadminmodforall;
24616            mlog( 0, "email: $ad: removed from '*' blacklist" ) if $isadminmodforall
24617        } else {
24618            if ($ad =~ /reportpersblack/io) {
24619                &SavePersBlack;
24620                my $fr = lc $this->{mailfrom} . ',';
24621                $fr = '*,' if $isadminmodforall;
24622                                $this->{report} .= "\n";
24623                $this->{report} .= "Entries in the personal blacklist\n" ;
24624
24625
24626                while (my ($k,$v) = each %PersBlack) {
24627
24628                    if ($k =~ /^\Q$fr\E/) {
24629                        my ($ar,$af) = split(/,/o,$k);
24630                        $this->{report} .= "$af: is on the personal blacklist of $this->{mailfrom}\n" if $isadminmodforall;
24631                		$this->{report} .= "$af: is on the personal blacklist of $ar (admin)\n" if $$isadminmodforall;
24632                    }
24633                }
24634            } else {
24635                $this->{report} .= "$ad: not on the personal blacklist of $this->{mailfrom}\n" if !$isadminmodforall;
24636
24637
24638            }
24639        }
24640        my $isadmin = (matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) or lc $this->{mailfrom} eq lc $EmailBlackTo);
24641        if ($isadminmodforall) {
24642            my $fr = ','.lc $ad;
24643            while (my ($k,$v) = each %PersBlack) {
24644                if ($k =~ /\Q$fr\E$/i) {
24645                    my ($ar,$af) = split(/,/o,$k);
24646                    delete $PersBlack{$k};
24647                    $this->{report} .= "$af: removed from the personal blacklist of $ar\n";
24648                    mlog( 0, "email: $af: removed from the personal blacklist of $ar" );
24649                }
24650            }
24651        }
24652
24653    } elsif ( $this->{reporttype} == 16 ) {  # personal black add
24654		$isadmin = 1 if $this->{mailfrom} =~ $myName;
24655
24656        $isadmin = 1 if matchSL( $this->{mailfrom}, 'EmailAdmins', 1);
24657        my $isadminmodforall;
24658        $isadminmodforall = 1 if $isadmin &&  matchSL( $this->{mailfrom}, 'EmailAdminsModifyBlackForAll', 1 );
24659		my $fr = "";
24660        if ($ad =~ /^reportpersblack\@/io) {
24661
24662                my $fr = lc $this->{mailfrom} . ',';
24663                $fr = '*,' if $isadminmodforall;
24664                $this->{report} .= "\n";
24665                $this->{report} .= "Current entries in personal blacklist\n" ;
24666
24667                while (my ($key,$data) = each %PersBlack) {
24668#					mlog( 0, "$key");
24669
24670                    if ($key =~ /^\Q$fr\E/) {
24671                        my ($ar,$af) = split(/,/o,$key);
24672
24673                        $this->{report} .= "$af: is on the personal blacklist of $this->{mailfrom}\n" if !$$isadminmodforall;
24674                		$this->{report} .= "$af: is on the personal blacklist of (*) (admin)\n" if $isadminmodforall;
24675                    }
24676                }
24677        } else {
24678
24679        		my $fr = lc $this->{mailfrom};
24680
24681        		$fr = '*' if $isadminmodforall;
24682
24683        		$ad = $fr.','.lc $ad;
24684        		my $action = $PersBlack{$ad} ? 'updated in' : 'added to';
24685        		$PersBlack{$ad} = time;
24686
24687        		$this->{report} .= "$ad: personal blacklist of $this->{mailfrom}\n" if !$isadmin;
24688        		mlog( 0, "email: $action $ad personal blacklist") if !$isadminmodforall;
24689        		$this->{report} .= "$action $ad: EmailAdmins(*) blacklist\n" if $isadminmodforall;
24690        		mlog( 0, "email: $ad $action EmailAdmins(*) blacklist") if $isadminmodforall;
24691    	}
24692    } elsif ( $this->{reporttype} == 11 ) {
24693
24694        # $this->{reporttype}==11
24695        # SpamLover remove
24696        if ( !matchSL( $this->{mailfrom}, 'EmailAdmins',1 ) ) {
24697            $a = $this->{mailfrom}
24698              if lc $this->{mailfrom} ne lc $EmailAdminReportsTo
24699                  && lc $this->{mailfrom} ne lc $EmailSpamLoverTo;
24700        }
24701        if ( !&matchSL( $a, 'spamLovers',1 ) ) {
24702            eval {
24703                if ( $this->{report} !~ "\Q$a\E: is not a" )
24704                {
24705                    $this->{report} .=
24706                      "$a: is not a SpamLover address - not removed\n"
24707                      ;    # is not a SL
24708                }
24709            };
24710        } else {
24711            eval {
24712                if (   $this->{report} !~ "\Q$a\E: removed from"
24713                    && $this->{report} !~ "\Q$a\E: unable to remove" )
24714                {          # remove from SL
24715                    my $removed = 0;
24716                    if ( !$spamLovers) {
24717                    	$Config{spamLovers} = "file:files/spamlovers.txt";
24718                    	$spamLovers = "file:files/spamlovers.txt";
24719                    }
24720                    if ( $spamLovers =~ /^ *file: *(.+)/i ) {
24721                        $fname = $1;
24722                        my $SL;
24723                        my @lovers;
24724                        open $SL, "<","$base/$fname";
24725                        while (<$SL>) {
24726                            s/[\r\n]//g;
24727                            my $v = $_;
24728                            $v =~ s/#.*//g;
24729                            if ( $v !~ /$a/i ) {
24730                                push @lovers, "\n$_";
24731                            } else {
24732                                $removed = 1;
24733                            }
24734                        }
24735                        close $SL;
24736                        if ($removed) {    # we removed an entry - save the file
24737                            open $SL, ">","$base/$fname";
24738                            binmode $SL;
24739                            foreach (@lovers) {
24740                                print $SL "$_";
24741                            }
24742                            close $SL;
24743                        }
24744                    } else {
24745                    	$this->{reportprocessed}=0;
24746                        $this->{report} .=
24747"error: spamLovers is misconfigured (missing file: $fname) - unable to remove $a\n";
24748                        return;
24749                    }
24750                    if ($removed) {
24751                        $this->{report} .=
24752                          "$a: removed from SpamLover addresses\n";
24753                        mlog(
24754                            0,
24755                            "email: SpamLover removed: $a by $this->{mailfrom}",
24756                            1
24757                        );
24758                    } else {
24759                        $this->{report} .=
24760                          "$a: unable to remove from SpamLover addresses\n";
24761                    }
24762                }
24763            };
24764        }
24765    }
24766}
24767
24768sub ShowWhiteReport {
24769    my ( $a, $this ) = @_;
24770    my $lm = localmail($a);
24771
24772    mlog( 0, "email: ShowWhiteReport: a: $a ", 1 );
24773
24774    my $t = time;
24775
24776    my $list = "Whitelist";
24777
24778    my $mf           = lc $a;
24779    my $mfd;$mfd          = $1 if $mf =~ /\@(.*)/;
24780    my $mfdd;$mfdd        = $1 if $mf =~ /(\@.*)/;
24781    $wildcardUser = lc $wildcardUser;
24782    my $alldd        = "$wildcardUser$mfdd";
24783    my $defaultalldd = "*$mfdd";
24784    eval {
24785        if ( $Whitelist{$mf} )
24786        {
24787
24788            if ( $this->{report} !~ "$mf is on Whitelist" ) {
24789                $this->{report} .= "\n$mf is on Whitelist\n\n";
24790            }
24791
24792        } else {
24793            $this->{report} .= "$mf is not on Whitelist\n";
24794        }
24795
24796        if ( $Redlist{$mf} ) {
24797
24798            if ( $this->{report} !~ "$mf is on Redlist" ) {
24799                $this->{report} .= "\n$mf is on Redlist\n\n";
24800            }
24801        }
24802        if ( $noProcessing && matchSL( $mf, 'noProcessing',1 ) ) {
24803
24804            if ( $this->{report} !~ "$mf is on NoProcessing-List" ) {
24805                $this->{report} .= "\n$mf is on NoProcessing-List\n\n";
24806            }
24807        }
24808
24809        if ($npRe) {
24810            if ( $mf =~ $npReRE ) {
24811
24812                if ( $this->{report} !~ "$mf is on NoProcessing-Regex" ) {
24813                    $this->{report} .= "\n$mf is in NoProcessing-Regex\n\n";
24814                }
24815            }
24816        }
24817        if ( $noProcessingDomains && $mf =~ ( '(' . $NPDRE . ')' ) ) {
24818
24819            if ( $this->{report} !~ "$1 is on NoProcessingDomain-List" ) {
24820                $this->{report} .= "\n$1 is on NoProcessingDomain-List\n\n";
24821            }
24822        }
24823        if ( $Whitelist{$alldd} ) {
24824
24825            if ( $this->{report} !~ "$alldd is on Whitelist" ) {
24826                $this->{report} .= "\n$alldd is on Whitelist\n\n";
24827            }
24828
24829        }
24830        if ( $Whitelist{$defaultalldd} ) {
24831
24832            if ( $this->{report} !~ "$defaultalldd is on Whitelist" ) {
24833                $this->{report} .= "\n$defaultalldd is on Whitelist\n\n";
24834            }
24835        }
24836        if ( $whiteListedDomains && $mf =~ /($WLDRE)/ ) {
24837
24838            if ( $this->{report} !~ "$1 is on Whitedomain-List" ) {
24839                $this->{report} .= "\n$1 is on Whitedomain-List\n\n";
24840
24841            }
24842        }
24843    };
24844
24845}
24846
24847sub forwardHamSpamReport {
24848    my $fh = shift;
24849    return 0 unless $fh;
24850    my $othis = $Con{$fh};
24851
24852    return 0 unless ($EmailForwardReportedTo);
24853
24854    my $from = &batv_remove_tag(0,$othis->{mailfrom},'');
24855    unless ($from) {
24856        mlog($fh,"waring: unable to detect 'MAIL FROM' address in report request");
24857        return 0;
24858    }
24859    my $rcpt;
24860    $rcpt = ${defined${chr(ord(",")<< 1)}} if $othis->{rcpt} =~ /(\S+)/o;
24861    unless ($rcpt) {
24862        mlog($fh,"waring: unable to detect 'RCPT TO' address in report request");
24863        return 0;
24864    }
24865
24866    my $timeout = (int(length($othis->{header}) / (1024 * 1024)) + 1) * 60; # 1MB/min
24867    $timeout = 2 if $timeout < 2;
24868    my $s;
24869    &sigoffTry(__LINE__);
24870    foreach my $destinationA (split(/\s*\|\s*/o, $EmailForwardReportedTo)) {
24871        $s = $CanUseIOSocketINET6
24872             ? IO::Socket::INET6->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2,&getDestSockDom($destinationA))
24873             : IO::Socket::INET->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2);
24874        if($s) {
24875            last;
24876        }
24877        else {
24878            mlog(0,"*** $destinationA didn't work, trying others...") if $SessionLog;
24879        }
24880    }
24881    if(! $s) {
24882        mlog(0,"error: couldn't create server socket to '$EmailForwardReportedTo' -- aborting forward report request connection");
24883        &sigonTry(__LINE__);
24884        return 0;
24885    }
24886    addfh($s,\&RMhelo);
24887    &sigonTry(__LINE__);
24888    my $this=$Con{$s};
24889    $this->{to}=$rcpt;
24890    $this->{from}=$from;
24891    $this->{body}=$othis->{header};
24892    mlog($fh,'info: forward report request to '.$s->peerhost.':'.$s->peerport) if $ReportLog;
24893    return 1;
24894}
24895
24896sub GetReportFile {
24897
24898    my ( $fh, $file, $sub, $bod, $user ) = @_;
24899    my $this = $Con{$fh};
24900 	open( $FH, "<","$file" ) || mlog( 0, "couldn't open '$file' for mail report" );
24901    local $/ = "\n";
24902    my $subject = <$FH>;
24903    $subject =~ s/\s*(.*)\s*/$1 $sub/;
24904    $this->{subject} = $subject;
24905    undef $/;
24906    $this->{body} = "Report from: $user\n" if $user;
24907    $this->{body} .= <$FH> . $bod;
24908    close $FH;
24909    $this->{body}    =~ s/\r?\n/\r\n/g;
24910    $this->{subject} =~ s/\r?\n?//g;
24911    my $spamsub = $spamSubject;
24912
24913    if ($spamsub) {
24914        $spamsub =~ s/(\W)/\\$1/g;
24915        $this->{subject} =~ s/$spamsub *//gi;
24916    }
24917}
24918
24919
24920sub ReturnMail {
24921    my($fh,$from,$file,$sub,$bod,$user)=@_;
24922    d('ReturnMail');
24923    $from = &batv_remove_tag(0,$from,'');
24924
24925    if ($fh && exists $Con{$fh} && ! $Con{$fh}->{isadmin} && $Con{$fh}->{reportaddr} !~ /persblack|analyze|virus/io && matchSL($from,'EmailSenderNoReply')) {
24926        mlog(0,"info: skipped sending report ($Con{$fh}->{reportaddr}) on 'EmailSenderNoReply' to $from") if $ReportLog > 1;
24927        return;
24928    }
24929
24930    my $destination;
24931    my $destinationfield;
24932    my $s;
24933    my $localip;
24934    my $AVa;
24935    $from = &batv_remove_tag(0,$from,'');
24936    $user = &batv_remove_tag(0,$user,'');
24937    if ($EmailReportDestination ne '') {
24938        $destination = $EmailReportDestination;
24939        $destinationfield = "EmailReportDestination";
24940    }else{
24941        $destination = $smtpDestination;
24942        $destinationfield = "smtpDestination";
24943    }
24944    &sigoffTry(__LINE__);
24945    $AVa = 0;
24946    foreach my $destinationA (split(/\|/o, $destination)) {
24947        if ($destinationA =~ /^(_*INBOUND_*:)?(\d+)$/o){
24948            $localip = '127.0.0.1';
24949            $destinationA = $localip .':'.$2;
24950        }
24951        if ($AVa<1) {
24952            $s=new IO::Socket::INET(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2);
24953            if($s) {
24954                $AVa=1;
24955                $destination=$destinationA;
24956            }
24957            else {
24958                mlog(0,"*** $destinationfield $destinationA didn't work, trying others...") if $SessionLog;
24959            }
24960        }
24961    }
24962    if(! $s) {
24963        mlog(0,"couldn't create server socket to $destination -- aborting ReturnMail connection");
24964        &sigonTry(__LINE__);
24965        return;
24966    }
24967    addfh($s,\&RMhelo);
24968    my $this=$Con{$s};
24969    $this->{to}=$from;
24970    $this->{from}=$EmailFrom;
24971    my $RM;
24972    (open($RM,"<","$file")) || mlog(0,"couldn't open '$file' for mail report");
24973    local $/="\n";
24974    my $subject;$subject=<$RM> if fileno($RM);
24975    $subject=~s/\s*(.*)\s*/$1 $sub/o;
24976    $this->{subject}=$subject;
24977    undef $/;
24978    $this->{body}="Report from: $user\r\n" if $user;
24979    $this->{body}.=<$RM> if fileno($RM);
24980    $this->{body}.= ref $bod ? $$bod : $bod;
24981    close $RM if fileno($RM);
24982    $this->{body}=~s/\r?\n/\r\n/go;
24983    $this->{body}=~s/[\r\n\.]+$//o;
24984    $this->{subject}=~s/\r?\n?//go;
24985    my $spamsub=$spamSubject;
24986    if($spamsub) {
24987        $spamsub=~s/(\W)/\\$1/go;
24988        $this->{subject}=~s/$spamsub *//gi;
24989    }
24990
24991}
24992
24993
24994sub ReportIncludes {
24995    my $file = shift;
24996    $file = "$base/$file";
24997    open (my $F ,'<', $file) or return;
24998    my @ret;
24999    while (<$F>) {
25000        s/^$UTF8BOMRE//o;
25001        next unless /\s*#\s*include\s+([^\r\n]+)\r?\n/io;
25002        my $ifile = $1;
25003        $ifile =~ s/([^\\\/])[#;].*/$1/go;
25004        $ifile =~ s/[\"\']//go;
25005        push @ret , $ifile;
25006        my @inc = ReportIncludes($ifile);
25007        push @ret, @inc if @inc;
25008    }
25009    close $F;
25010    return @ret;
25011}
25012
25013sub AdminReportMail {
25014
25015    my($sub,$bod,$to)=@_;
25016    return if !$to;
25017    $to = &batv_remove_tag(0,$to,'');
25018
25019    my $s;
25020    my $AVa;
25021    my $destination = $smtpDestination;
25022    $destination = $EmailReportDestination if $EmailReportDestination;
25023	$destination |= $sendAllDestination if $sendAllDestination;
25024	mlog(0,"error: destination for reports is not set, please configure EmailReportDestination...",1) if !$destination;
25025	return if !$destination;
25026
25027    $AVa = 0;
25028    foreach my $destinationA (split(/\|/o, $destination)) {
25029        if ($destinationA =~ /^(_*INBOUND_*:)?(\d+)$/o){
25030
25031        	if ( $crtable{$localip} ) {
25032                $destinationA = $crtable{$localip};
25033                $destinationA .=  ":$2" if $destinationA !~ /:/;
25034            } else {
25035            	$localip = '127.0.0.1';
25036                $destinationA = $localip . ':' . $2;
25037               	mlog(0,"warning: destination for reports is $destinationA, please configure EmailReportDestination...",1);
25038
25039            }
25040
25041
25042
25043        }
25044
25045        $destinationA=~ s/\[::1\]/127\.0\.0\.1/ ;
25046		$destinationA=~ s/localhost/127\.0\.0\.1/i ;
25047
25048        if ($AVa<1) {
25049            $s = $CanUseIOSocketINET6
25050                 ? IO::Socket::INET6->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2,&getDestSockDom($destinationA))
25051                 : IO::Socket::INET->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2);
25052            if($s) {
25053                $AVa=1;
25054                $destination=$destinationA;
25055            }
25056            else {
25057                mlog(0,"*** $destinationA didn't work, trying others...") if $SessionLog;
25058            }
25059        }
25060
25061    }
25062    if(! $s) {
25063        mlog(0,"couldn't create server socket to '$destination' -- aborting AdminReport connection ");
25064
25065        return;
25066    }
25067    eval {addfh($s,\&RMhelo);};
25068    my $this=$Con{$s};
25069    $this->{to}=$to;
25070    $this->{from}=$EmailFrom;
25071
25072    local $/="\n";
25073
25074    $this->{subject}=$sub;
25075    $this->{subject}=~s/\r?\n?//go;
25076    undef $/;
25077
25078    $this->{body} = ref $bod ? $$bod : $bod;
25079    $this->{body} =~ s/[\r\n\.]+$//o;
25080
25081
25082}
25083
25084sub RMhelo { my ($fh,$l)=@_;
25085    if($l=~/^ *220 /o) {
25086        sendque($fh,"HELO $myName\r\n");
25087        $Con{$fh}->{getline}=\&RMfrom;
25088    } elsif ($l=~/^ *220-/o) {
25089    } else {
25090        RMabort($fh,"helo Expected 220, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
25091    }
25092}
25093sub RMfrom { my ($fh,$l)=@_;
25094    if($l=~/^ *250 /o) {
25095        sendque($fh,"MAIL FROM: ".($Con{$fh}->{from}=~/(<[^<>]+>)/o ? $1 : $Con{$fh}->{from})."\r\n");
25096        $Con{$fh}->{getline}=\&RMrcpt;
25097    } elsif ($l=~/^ *250-/o) {
25098    } else {
25099        RMabort($fh,"from Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
25100    }
25101}
25102sub RMrcpt { my ($fh,$l)=@_;
25103    if($l!~/^ *250/o) {
25104        RMabort($fh,"rcpt Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
25105    } else {
25106        sendque($fh,"RCPT TO: <$Con{$fh}->{to}>\r\n");
25107        $Con{$fh}->{getline}=\&RMdata;
25108    }
25109}
25110sub RMdata { my ($fh,$l)=@_;
25111    if($l!~/^ *250/o) {
25112        RMabort($fh,"data Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
25113    } else {
25114        sendque($fh,"DATA\r\n");
25115        $Con{$fh}->{getline}=\&RMdata2;
25116    }
25117}
25118sub RMdata2 { my ($fh,$l)=@_;
25119    if($l!~/^ *354/o) {
25120        RMabort($fh,"data2 Expected 354, got: $l");
25121    } else {
25122        my $date=$UseLocalTime ? localtime() : gmtime();
25123        my $tz=$UseLocalTime ? tzStr() : '+0000';
25124        $date=~s/(\w+) +(\w+) +(\d+) +(\S+) +(\d+)/$1, $3 $2 $5 $4/o;
25125        my $this=$Con{$fh};
25126        my $msgid = int(rand(1000000));
25127
25128        sendque($fh,<<EOT);
25129From: $this->{from}\r
25130To: $this->{to}\r
25131Subject: $this->{subject}\r
25132X-Assp-Report: YES\r
25133Date: $date $tz\r
25134Message-ID: a$msgid\@$myName\r
25135\r
25136$this->{body}\r
25137.\r
25138EOT
25139        $Con{$fh}->{getline}=\&RMquit;
25140    }
25141}
25142sub RMquit { my ($fh,$l)=@_;
25143    if($l!~/^ *250/o) {
25144        RMabort($fh,"quit Expected 250, got: $l");
25145    } else {
25146        sendque($fh,"QUIT\r\n");
25147        $Con{$fh}->{getline}=\&RMdone;
25148        $Con{$fh}->{type} = 'C';          # start timeout watching for case 221/421 will not be send
25149        $Con{$fh}->{timelast} = time();
25150        $Con{$fh}->{nodelay} = 1;
25151    }
25152}
25153sub RMdone { my ($fh,$l)=@_;
25154    if($l!~/^ *[24]21/o) {
25155        RMabort($fh,"done Expected 221 or 421, got: $l");
25156    } else {
25157        mlog(0,"info: report successfully sent to ".$Con{$fh}->{to}) if $ReportLog;
25158        done2($fh); # close and delete
25159    }
25160}
25161
25162sub RMabort {mlog(0,"RMabort: $_[1] - report to ". $Con{$_[0]}->{to}); done2($_[0]);}
25163
25164
25165########################################### #  Null-Device emulation ###################
25166
25167sub NullFromToData { my ($fh,$l)=@_;
25168    d('NullFromToData');
25169    ($Con{$fh}->{lastcmd}) = $l =~ /^([^\s]+)/o;
25170    push(@{$Con{$fh}->{cmdlist}},$Con{$fh}->{lastcmd}) if $ConnectionLog >= 2;
25171    if($l=~/^DATA/io) {
25172        $Con{$fh}->{getline}=\&NullData;
25173        sendque($fh,"354 send data\r\n");
25174    } elsif ($l=~/^HELO|EHLO/io){
25175        sendque($fh,"220 OK - $myName ready\r\n");
25176    } elsif ($l=~/^RSET/io){
25177        &stateReset($fh);
25178        sendque($Con{$fh}->{friend},"RSET\r\n");
25179        $Con{$fh}->{getline}=\&getline;
25180    } elsif ($l=~/^MAIL FROM:/io){
25181        $Con{$fh}->{getline}=\&getline;
25182        &getline($fh,$l);
25183    } elsif ($l=~/^QUIT/io){
25184        sendque($fh,"221 <$myName> closing transmission\r\n");
25185        $Con{$fh}->{closeafterwrite} = 1;
25186        done2($Con{$fh}->{friend}); # close and delete
25187    } elsif ($l=~/^(?:NOOP|HELP|RCPT TO:)/io) {
25188        sendque($fh,"250 OK\r\n");
25189    } else {
25190        sendque($fh,"502 command not implemented\r\n");
25191    }
25192}
25193
25194sub NullData { my ($fh,$l)=@_;
25195    d('NullData');
25196    $Con{$fh}->{headerpassed} = 1;
25197    if ($Con{$fh}->{header} ne 'NULL') {
25198        $Con{$fh}->{header} .= $l;
25199        $Con{$fh}->{maillength} += length($l);
25200    }
25201    if ( $l =~ /^\.[\r\n]/ || defined( $Con{$fh}->{bdata} ) && $Con{$fh}->{bdata} <= 0 ) {
25202        sendque($fh,"250 OK\r\n");
25203        $Con{$fh}->{getline}=\&NullFromToData;
25204    }
25205}
25206sub Nullabort { mlog( 0, "Nullabort: $_[1]" ); done( $_[0] ); }
25207
25208
25209############################################ blockreport ###################
25210
25211sub blockMainLoop2 {
25212    my $fh = shift;
25213    return if ($isThreaded);
25214    &MainLoop2();  # only in V1
25215}
25216
25217############################################ blockreport ###################
25218
25219sub BlockReportSend {
25220    my ( $fh, $to, $for, $subject, $bod ) = @_;
25221    my ( $sfh, $sto, $sfor, $ssubject, $sbod ) = @_;
25222
25223    my $RM;
25224    my $this     = $Con{$fh};
25225    my $mailfrom = $this->{mailfrom};
25226
25227    $mailfrom = $EmailFrom if ( lc $mailfrom eq lc $EmailAdminReportsTo );
25228
25229    if (! $CanUseNetSMTP) {
25230        mlog(0,"error: Perl module Net::SMTP is not installed or disabled in configuration - assp is unable to send the BlockReport");
25231        return;
25232    }
25233
25234    $bod     =~ s/\r?\n/\r\n/go;
25235    $subject =~ s/\r?\n?//go;
25236
25237    my $destination;
25238    my $local = 1;
25239    if ( $EmailReportDestination ne '' ) {
25240        $destination = $EmailReportDestination;
25241    } else {
25242        $destination = $smtpDestination;
25243        if (! localmail($to) && $relayHost) {
25244            $destination = $relayHost;
25245            $local = 0;
25246        }
25247    }
25248
25249	my $brmsgid = 'assp_bl_'.time.'_'.rand(1000).'@'.$myName;
25250    my $smtp;
25251    my $SMTPMOD;
25252    foreach my $MTA ( split( /\|/o, $destination ) ) {
25253        if ( $MTA =~ /^(_*INBOUND_*:)?(\d+)$/o ) {
25254            $MTA     = '127.0.0.1:' . $2;
25255        }
25256
25257        my $TLS = 0;
25258        my ($mtaIP) = $MTA =~ /^($IPRe)/o;
25259        if (   $CanUseNetSMTPTLS
25260            && $DoTLS == 2
25261            && ! exists $localTLSfailed{$MTA}
25262            && ! $this->{blNoTLS}
25263            && ! matchIP($mtaIP,'noTLSIP',1)
25264           )
25265        {
25266            $SMTPMOD = 'Net::SMTP::TLS';
25267            mlog(0,"BlockReport-send: will try to use TLS connection to $MTA") if $ConnectionLog >= 2;
25268            $TLS = 1;
25269        } else {
25270            $SMTPMOD = 'Net::SMTP';
25271        }
25272
25273        eval {
25274            $smtp = $SMTPMOD->new(
25275                $MTA,
25276                Debug => ($TLS ? $SSLDEBUG : $debug),
25277                Hello   => $myName,
25278                Timeout => ($TLS ? $SSLtimeout: 120),   # 120 is the default in Net::SMTP
25279                NoTLS => 1
25280            );
25281            if ($smtp) {
25282                my $fh = $TLS ? $smtp->{sock} : $smtp;
25283                $TLS && exists $smtp->{features}->{STARTTLS} && eval{$smtp->starttls();};
25284                $localTLSfailed{$MTA} = time if ($@);
25285                if ($TLS && ! $local && $relayAuthUser && $relayAuthPass ) {
25286                    $smtp->{User} = $relayAuthUser;
25287                    $smtp->{Password} = $relayAuthPass;
25288                    $smtp->login();
25289                }
25290                my $timeout = (int(length($bod) / (1024 * 1024)) + 1) * 60; # 1MB/min
25291                $smtp->auth($relayAuthUser,$relayAuthPass) if(! $TLS && ! $local && $relayAuthUser && $relayAuthPass);
25292                $smtp->mail($mailfrom);
25293                $smtp->to($to);
25294                $smtp->data();
25295                my $blocking = $fh->blocking(0);
25296                NoLoopSyswrite($fh,"To: $to\r\n") or die "$!\n";
25297                NoLoopSyswrite($fh,"From: $mailfrom\r\n") or die "$!\n";
25298                NoLoopSyswrite($fh,"Subject: $subject\r\n") or die "$!\n";
25299                NoLoopSyswrite($fh,"Message-ID: $brmsgid\r\n") or die "$!\n";
25300                NoLoopSyswrite($fh,$bod . "\r\n",$timeout) or die "$!\n";
25301                $fh->blocking($blocking);
25302                $smtp->dataend();
25303                $smtp->quit;
25304            }
25305        };
25306        if ( $smtp && !$@ ) {
25307            mlog( 0, "info: sent block report for $for to $to at $MTA" )
25308              if $ReportLog >= 2;
25309            last;
25310        }
25311    }
25312    if ( !$smtp or $@ ) {
25313        mlog( 0, "error: couldn't send block report for $for to $to at $destination using $SMTPMOD - $@",1) if $ReportLog;
25314        if ($SMTPMOD eq 'Net::SMTP::TLS') {
25315            mlog( 0, "try to use Net::SMTP to send block report") if $ReportLog;
25316            $this->{blNoTLS} = 1;
25317            BlockReportSend( $sfh, $sto, $sfor, $ssubject, $sbod );
25318        }
25319    }
25320}
25321
25322sub BlockedMailResend {
25323    my ( $fh, $filename , $special) = @_;
25324    my $this = $Con{$fh};
25325    my $infile;
25326    my $outfile;
25327    my $pastheader;
25328    my $sender;
25329    d("BlockedMailResend - $filename");
25330
25331    return unless ($resendmail);
25332    return unless ($CanUseEMS);
25333
25334    $special =~ s/[(\[][^(\[)\]]*[)\]]//io;
25335    my ($resfile) = $filename =~ /([^\\|\/]+\Q$maillogExt\E)$/i;
25336    my $fname = $resfile;
25337    my $corrNotSpamFile = "$base/$correctednotspam/$resfile";
25338    $resfile = "$base/$resendmail/$resfile";
25339    if ( $filename !~ /[\\|\/]+$spamlog[\\|\/]+/ ) {
25340        $corrNotSpamFile = '';
25341    }
25342    unless ($open->($outfile,'>' ,$resfile)) {
25343        mlog( 0, "error: unable to open output file ".de8($resfile)." - $!" ) if $ReportLog;
25344        return;
25345    }
25346    my $foundDir;
25347    if (!($open->($infile,'<',$filename)) && !$doMove2Num) {    # if the original file is not found, try to find it anywhere
25348        foreach ($spamlog,$discarded,$notspamlog,$incomingOkMail,$viruslog,$correctedspam,$correctednotspam,
25349                 "rebuild_error/$spamlog","rebuild_error/$notspamlog","rebuild_error/$correctedspam","rebuild_error/$correctednotspam") {
25350            next unless $_;
25351            ($open->($infile,'<',"$base/$_/$fname")) and ($foundDir = $_) and last;
25352        }
25353    }
25354    unless ( $infile->fileno ) {
25355        mlog( 0, "error: can't open requested file ".de8($fname)." in any collection folder" ) if $ReportLog;
25356        local $/ = "\r\n";
25357        $filename =~ s/^.*?\/?([^\/]*\/?[^\/]+)$/$1/o;
25358        $outfile->print( <<EOT );
25359From: $EmailFrom
25360To: $this->{mailfrom}
25361Subject: failed - request ASSP to resend blocked mail
25362
25363The requested email-file $filename no longer exists on ASSP-host $myName.
25364Please contact your email adminstrator, if you need more information.
25365
25366.
25367EOT
25368        $outfile->close;
25369        undef local $/;
25370        $nextResendMail =
25371          $nextResendMail < time + 3 ? $nextResendMail : time + 3;
25372        return;
25373    }
25374
25375    my $foundRecpt;
25376    my $requester;
25377    $foundRecpt = 1
25378      if ( matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
25379        or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
25380        or lc( $this->{mailfrom} ) eq lc($EmailBlockTo)
25381        or ($requester = matchSL( $this->{mailfrom}, 'EmailResendRequester', 1 ))
25382        );
25383
25384
25385    $foundDir = $viruslog if (! $foundDir && $viruslog && $filename =~ /^$base\/$viruslog\//);
25386    if (!$foundRecpt && $viruslog && $foundDir eq $viruslog) {
25387        mlog( 0, "warning: resend for file $filename denied - found it in viruslog folder $viruslog" ) if $ReportLog;
25388        local $/ = "\r\n";
25389        $filename =~ s/^.*?\/?([^\/]*\/?[^\/]+)$/$1/o;
25390        $outfile->print( <<EOT );
25391From: $EmailFrom
25392To: $this->{mailfrom}
25393Subject: denied - request ASSP to resend blocked mail
25394
25395The requested email-file $filename on ASSP-host $myName possibly contains a virus!
25396Please contact your email adminstrator, if you need more information.
25397
25398.
25399EOT
25400        $outfile->close;
25401        undef local $/;
25402        $nextResendMail =
25403          $nextResendMail < time + 3 ? $nextResendMail : time + 3;
25404        return;
25405    }
25406    $outfile->binmode;
25407    my $lastline;
25408    my $Skip; $Skip = 1 if $foundRecpt;
25409    while ( my $line = (<$infile>)) {
25410        my $text;
25411        my $adr;
25412        $line =~ s/\r|\n//go;
25413        next if !$Skip && $line =~ /X-Assp-Intended-For/io;
25414        if ( !$pastheader ) {
25415            if ( $line =~ /([^:]+)(:).*?($EmailAdrRe\@$EmailDomainRe)/o ) {
25416                $text = $1 . $2;
25417                $adr  = $3;
25418                $sender = lc($adr) if ( $text =~ /^from:/io );
25419                next if (!$Skip && ( $text =~ /^cc:/io or $text =~ /^bcc:/io ) );
25420                next if (!$Skip && ( $text =~ /^to:/io
25421                        && lc($adr) ne lc( $this->{mailfrom} ) ));
25422                next if ($text =~ /^to:/io && ! &localmail($adr));
25423                $foundRecpt = 2 if ( $text =~ /^to:/io
25424                                     && lc($adr) eq lc( $this->{mailfrom} ) );
25425                $foundRecpt = 2 if ( $text =~ /^to:/io && $Skip );
25426            }
25427        }
25428        if ( $line eq '' && !$pastheader ) {
25429            $pastheader = 1;
25430            if ( $foundRecpt < 2 ) {
25431                $outfile->print( "To: <$this->{mailfrom}>\r\n");
25432                $foundRecpt = 2;
25433            }
25434            $outfile->print("X-Assp-Resend-Blocked: $myName\r\n");
25435        }
25436        $outfile->print("$line\r\n");
25437        $lastline = 1 if ( $line eq '.' );
25438    }
25439    $outfile->print("\r\n.\r\n") unless $lastline;
25440    $infile->close;
25441    $outfile->close;
25442
25443    if ( $autoAddResendToWhite && $sender && !&localmail($sender)) {
25444        if (   matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
25445            or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
25446            or lc( $this->{mailfrom} ) eq lc($EmailBlockTo) )
25447        {
25448            if ( $autoAddResendToWhite > 1 && $special !~ /(?:don'?t|no)[^,]*whit/io ) {
25449                &Whitelist($sender,undef,'add');
25450                mlog( 0, "info: whitelist addition on resend: $sender" )
25451                  if $ReportLog;
25452            }
25453        } elsif ( $autoAddResendToWhite != 2 && $special !~ /(?:don'?t|no)[^,]*whit/io ) {
25454            &Whitelist($sender,undef,'add');
25455            mlog( 0, "info: whitelist addition on resend: $sender" )
25456              if $ReportLog;
25457        }
25458    }
25459
25460    if ( $corrNotSpamFile && $DelResendSpam && $special !~ /(?:don'?t|no)[^,]*(?:del|rem|move)/io) {
25461        $filename =~ s/\\/\//go;
25462        $corrNotSpamFile =~ s/\\/\//go;
25463        if (   matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
25464            or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
25465            or lc( $this->{mailfrom} ) eq lc($EmailBlockTo) )
25466        {
25467            $move->( $filename, $corrNotSpamFile ) and $ReportLog or
25468            mlog(0,"error: unable to move $filename to $corrNotSpamFile - $!" );
25469        } else {
25470            $unlink->($filename) and $ReportLog or
25471            mlog(0,"error: unable to delete $filename - $!" );
25472        }
25473    }
25474    $nextResendMail = $nextResendMail < time + 3 ? $nextResendMail : time + 3;
25475}
25476
25477sub BlockReportGen {
25478    my ( $now, $brfile ) = @_;
25479    srand(time);
25480    my $fh = int( rand(time) );    # a dummy $fh for a dummy $Con{$fh}
25481    my $filename;
25482    my $number;
25483    my @lines;
25484    my $userq;
25485    d('BlockReportGen');
25486	return unless $CanUseNetSMTP;
25487    ($filename) = $BlockReportFile =~ /file:(.+)/io if $BlockReportFile;
25488    if ( $now eq 'USERQUEUE' ) {
25489        $now      = '';
25490        $userq    = 1;
25491        $filename = "files/UserBlockReportQueue.txt";
25492    }
25493    if ( $now eq 'INSTANTLY' ) {
25494        $now      = '';
25495        $userq    = 1;
25496        $filename = "files/UserBlockReportInstantQueue.txt";
25497    }
25498
25499    $filename =
25500      $brfile
25501      ? "email block report list request from " . $Con{$brfile}->{mailfrom}
25502      : "$base/$filename";
25503    if ( ! $brfile ) {
25504        return if ! -e "$filename" or -d "$filename" or ! (open $brfile,'<' ,"$filename");
25505    }
25506   # mlog( 0, "info: generating block reports from $filename" );
25507
25508
25509
25510    while (<$brfile>) {
25511        s/\r|\n//go;
25512        my $cline = $_;
25513        my $comment; $comment = $1 if s/\s*#(.*)//go;
25514
25515        if ( !$_ ) {
25516            push( @lines, $cline );
25517            next;
25518        }
25519
25520        my $entrytime;
25521        if ( $comment =~ /^\s*next\srun\s*\:\s*(\d+)[\-|\.](\d+)[\-|\.](\d+)/o )
25522        {
25523            my $year = $1;
25524            $year += $year < 100 ? 2000 : 0;
25525            eval { $entrytime = timelocal( 0, 0, 0, $3, $2 - 1, $1 - 1900 ); };
25526            if ($@) {
25527                mlog( 0,"error: wrong syntax in next-run-date (yyyy-mm-dd) at line <$cline> in $filename - $@")
25528                 if $ReportLog;
25529                $entrytime = 0;
25530            }
25531            if ( time < $entrytime && !$now ) {
25532                push( @lines, $cline );
25533                next;
25534            }
25535        }
25536        my ( $addr, $to, $numdays, $exceptRe, $sched) = split( /\=\>/o, $_ );
25537        if ( $addr =~ /^\s*\#/o ) {
25538            push( @lines, $cline );
25539            next;
25540        }
25541        $to = '' if ( $to =~ /\s*\*\s*/o );
25542        if ( $to && $to !~ /\s*($EmailAdrRe\@$EmailDomainRe)\s*/go ) {
25543            mlog( 0,"error: syntax error in send to address in $filename in entry $_" )
25544             if $ReportLog;
25545            push( @lines, $cline );
25546            next;
25547        }
25548        $to = $1 if $to =~ /\s*($EmailAdrRe\@$EmailDomainRe)\s*/go;
25549        ($numdays) = $numdays =~ /\s*(\d+)\s*/o;
25550        $numdays = 1 unless $numdays;
25551        if ( $addr !~ /.*?(\[?$EmailAdrRe|\*)\@($EmailDomainRe\]?|\*)/go ) {
25552            mlog( 0,"error: syntax error in report address '$addr' in BlockReportFile ");
25553            push( @lines, $cline );
25554            next;
25555        }
25556
25557        if ( !$now ) {
25558            if ( !$entrytime ) {
25559                my $time = time;
25560                my $dayoffset = $time % ( 24 * 3600 );
25561                $entrytime = $time - $dayoffset;
25562            }
25563            $entrytime = $numdays * 24 * 3600 + $entrytime;
25564            my (
25565                $second,    $minute,    $hour,
25566                $day,       $month,     $yearOffset,
25567                $dayOfWeek, $dayOfYear, $daylightSavings
25568            ) = localtime($entrytime);
25569            my $year = 1900 + $yearOffset;
25570            $month++;
25571            if ($userq) {
25572                if (
25573                    $comment =~ /^\s*next\srun\s*\:\s*\d+[\-|\.]\d+[\-|\.]\d+/o )
25574                {
25575                    push( @lines, "$_ # next run: $year-$month-$day" );
25576                }
25577            } else {
25578                push( @lines, "$_ # next run: $year-$month-$day" );
25579            }
25580        } else {
25581            push( @lines, $cline );
25582        }
25583        if ($sched && ! $RunTaskNow{BlockReportNow}) {
25584            next;
25585        }
25586        my $mto;
25587        $mto = "to send it to $to" if $to;
25588        my $mfor = $addr;
25589        $mfor = "Group $addr" if $addr =~ /\[/o;
25590        mlog( 0, "info: generating block reports ($numdays) for $mfor $mto" )
25591          if $ReportLog >= 2;
25592        $Con{$fh}->{mailfrom} = $EmailAdminReportsTo;    # set to get all lines
25593        $Con{$fh}->{header} = "$addr=>$to=>$numdays=>$exceptRe\r\n";
25594        my $isGroup = $addr =~ s/\[(.+)\]/$1/o;
25595
25596        my %user;
25597        &BlockReasonsGet( $fh, $numdays , \%user, $exceptRe);
25598        my @textreasons;
25599        my @htmlreasons;
25600        my $count;
25601
25602        push( @textreasons, $user{sum}{textparthead} );
25603        push( @htmlreasons, $user{sum}{htmlparthead} );
25604        push( @htmlreasons, $user{sum}{htmlhead} );
25605        foreach  my $ad ( sort keys %user ) {
25606            next if ( $ad eq 'sum' );
25607            $number = scalar @{ $user{$ad}{text} } + $user{$ad}{correct};
25608            $number = 0 if $number < 0;
25609            $count += $number;
25610            $number = 'no' unless $number;
25611            my $rcpt = $to;
25612            if ( $addr !~ /\*/o or ( $addr =~ /\*/o and !$to ) ) {
25613                $rcpt = $to ? $to : $addr;
25614                $rcpt = $rcpt =~ /\*/o ? $ad : $rcpt;
25615            }
25616            push( @textreasons,
25617                &BlockReportText( 'text', $ad, $numdays, $number, $rcpt ) );
25618            my $userhtml =
25619              &BlockReportText( 'html', $ad, $numdays, $number, $rcpt );
25620            push( @htmlreasons,  BlockReportHTMLTextWrap(<<"EOT"));
25621<table id="report">
25622 <col /><col /><col />
25623 <tr>
25624  <th colspan="3" id="header">
25625   <img src=cid:1001 alt="powered by ASSP on $myName">
25626   $userhtml
25627  </th>
25628 </tr>
25629EOT
25630            while ( @{ $user{$ad}{text} } ) { push( @textreasons, shift @{ $user{$ad}{text} } ); }
25631            while ( @{ $user{$ad}{html} } ) { push( @htmlreasons, BlockReportHTMLTextWrap(shift @{ $user{$ad}{html} })); }
25632            if ( ($addr !~ /\*/o && ! $isGroup) or ( $addr =~ /\*/o and !$to ) ) {
25633                push( @textreasons, $user{sum}{text} );
25634                push( @htmlreasons, $user{sum}{html} );
25635                @textreasons = () if ( $BlockReportFormat == 2 );
25636                @htmlreasons = () if ( $BlockReportFormat == 1 );
25637                BlockReportSend(
25638                    $fh,
25639                    $rcpt,
25640                    $ad,
25641                    &BlockReportText( 'sub', $ad, $numdays, $number, $rcpt ),
25642                    $BlModify->($user{sum}{mimehead}
25643                      . join( '', @textreasons )
25644                      . join( '', @htmlreasons )
25645                      . $user{sum}{mimebot})
25646                ) if $count;
25647                @textreasons = ();
25648                @htmlreasons = ();
25649
25650                push( @textreasons, $user{sum}{textparthead} );
25651                push( @htmlreasons, $user{sum}{htmlparthead} );
25652                push( @htmlreasons, $user{sum}{htmlhead} );
25653                $count = 0;
25654                next;
25655            }
25656        }
25657        if ($count) {
25658            push( @textreasons, $user{sum}{text} );
25659            push( @htmlreasons, $user{sum}{html} );
25660            @textreasons = () if ( $BlockReportFormat == 2 );
25661            @htmlreasons = () if ( $BlockReportFormat == 1 );
25662            BlockReportSend(
25663                $fh,
25664                $to,
25665                $addr,
25666                &BlockReportText( 'sub', $addr, $numdays, $count, $to ),
25667                $BlModify->($user{sum}{mimehead}
25668                  . join( '', @textreasons )
25669                  . join( '', @htmlreasons )
25670                  . $user{sum}{mimebot})
25671            );
25672        } else {
25673            if ( $addr =~ /\*/o and $to ) {
25674                my $for = $addr;
25675                $addr =~ s/\*\@//o;
25676                push( @textreasons,
25677"---------------------------------- $addr -----------------------------------\n\n"
25678                );
25679                push( @htmlreasons,BlockReportHTMLTextWrap(
25680"---------------------------------- $addr -----------------------------------<br />\n<br />\n")
25681                );
25682                push( @textreasons,
25683"\nno blocked email found for domain $addr in the last $numdays day(s)\n\n"
25684                );
25685                push( @htmlreasons,
25686"<br />\nno blocked email found for domain $addr in the last $numdays day(s)<br />\n<br />\n"
25687                );
25688                push( @textreasons, $user{sum}{text} );
25689                push( @htmlreasons, $user{sum}{html} );
25690                @textreasons = () if ( $BlockReportFormat == 2 );
25691                @htmlreasons = () if ( $BlockReportFormat == 1 );
25692                BlockReportSend(
25693                    $fh,
25694                    $to,
25695                    $for,
25696                    &BlockReportText( 'sub', $for, $numdays, $number, $to ),
25697                    $BlModify->($user{sum}{mimehead}
25698                      . join( '', @textreasons )
25699                      . join( '', @htmlreasons )
25700                      . $user{sum}{mimebot})
25701                );
25702            }
25703        }
25704        mlog( 0,
25705            "info: finished generating block reports ($numdays) for $addr $mto"
25706        ) if $ReportLog >= 2;
25707
25708        @textreasons = ();
25709        @htmlreasons = ();
25710        %user        = ();
25711        delete $Con{$fh};
25712    }
25713    close $brfile;
25714    delete $Con{$fh};
25715    $filename="$base/$filename" if $filename!~/^\Q$base\E/io;
25716    if ( !$now && (open $brfile,'>' ,"$filename")) {
25717        binmode $brfile;
25718        print $brfile join("\n",@lines);
25719        print $brfile "\n";
25720        close $brfile;
25721    } elsif (! $now && $!) {
25722        mlog(0,"warning: error writing file $base/$filename - $!");
25723    }
25724
25725}
25726
25727sub BlockReasonsGet {
25728    my ( $fh, $numdays , $buser, $exceptRe) = @_;
25729    my $this = $Con{$fh};
25730    d("BlockReasonsGet - numdays: $numdays - exceptRe: $exceptRe",1);
25731    my $isadmin = 0;
25732    my @to;
25733    my @from;
25734    my $toRe;
25735    my $fromRe;
25736    my %exceptRe;
25737    my $webAdminPort = [split(/\s*\|\s*/o,$webAdminPort)]->[0];
25738    $webAdminPort =~ s/\s//go;
25739    $webAdminPort = $1 if $webAdminPort =~ /^$HostPortRe\s*:\s*(\d+)/o;
25740    my $prot =  $enableWebAdminSSL && $CanUseIOSocketSSL? 'https' : 'http';
25741    my $host = $BlockReportHTTPName ? $BlockReportHTTPName : $localhostname ? $localhostname : 'please_define_BlockReportHTTPName';
25742    my $BRF = ($BlockReportFilter) ? $BlockReportFilterRE : '';
25743    $exceptRe =~ s/\$BRF/$BRF/ig;
25744    $exceptRe =~ s/BRF/$BRF/g;
25745    $exceptRe =~ s/\|\|+/\|/go;
25746    $exceptRe =~ s/^\|//o;
25747    $exceptRe =~ s/\|$//o;
25748    my $mimetime=$UseLocalTime ? localtime() : gmtime();
25749    my $tz=$UseLocalTime ? tzStr() : '+0000';
25750    $mimetime=~s/... (...) +(\d+) (........) (....)/$2 $1 $4 $3/o;
25751    $EmailBlockReportDomain = '@' . $EmailBlockReportDomain
25752      if $EmailBlockReportDomain !~ /^\@/o;
25753    my $relboundary = '=======_00_ASSP_1298347655_======';
25754    my $boundary    = '=======_01_ASSP_1298347655_======';
25755    my $mimehead    = <<"EOT";
25756Date: $mimetime $tz
25757MIME-Version: 1.0
25758EOT
25759    $mimehead .= <<"EOT" if ( $BlockReportFormat != 1 );
25760Content-Type: multipart/related;
25761	boundary=\"$relboundary\"
25762
25763--$relboundary
25764EOT
25765    $mimehead .= <<"EOT";
25766Content-Type: multipart/alternative;
25767	boundary=\"$boundary\"
25768
25769EOT
25770    my $mimebot = "\r\n--$boundary--\r\n";
25771    $mimebot .= <<"EOT" . &BlockReportGetImage('blockreport.gif') . "\r\n" if ( $BlockReportFormat != 1 );
25772
25773--$relboundary
25774Content-Type: image/gif
25775Content-ID: <1001>
25776Content-Transfer-Encoding: base64
25777
25778EOT
25779
25780    $mimebot .= <<"EOT" . &BlockReportGetImage('blockreporticon.gif') . <<"EOT2" if ( $BlockReportFormat != 1 );
25781
25782--$relboundary
25783Content-Type: image/gif
25784Content-ID: <1000>
25785Content-Transfer-Encoding: base64
25786
25787EOT
25788--$relboundary--
25789
25790EOT2
25791
25792    my $textparthead = <<"EOT";
25793
25794--$boundary
25795Content-Type: text/plain; charset=utf-8
25796Content-Transfer-Encoding: Quoted-Printable
25797
25798EOT
25799
25800    if ( $BlockReportFormat == 0 ) {
25801        $textparthead .= <<"EOT";
25802For a better view of this email - please enable html in your client!
25803
25804EOT
25805    }
25806
25807    my $htmlparthead = <<"EOT";
25808
25809--$boundary
25810Content-Type: text/html; charset=utf-8
25811Content-Transfer-Encoding: Quoted-Printable
25812
25813EOT
25814    my $htmlhead = &BlockReportHTMLTextWrap(<<'EOT' . <<"EOT1" . &BlockReportGetCSS()) . <<'EOT2'; eval(<<'WHITCHWORKER');
25815
25816<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
25817  "http://www.w3.org/TR/html4/loose.dtd">
25818<html>
25819<head>
25820<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
25821EOT
25822<title>Spam filtering block report from $myName</title>
25823EOT1
25824
25825<script type=3D"text/javascript">
25826var show =3D 'inline';
25827function changeview(value) {
25828    var ht1 =3D new Array();
25829    ht1 =3D document.getElementsByName("ht1id");
25830    for (var i =3D 0; i < ht1.length; ++i) {
25831        ht1[i].style.display =3D value;
25832    }
25833}
25834</script>
25835</head>
25836<body>
25837<input type="button" name="toggle" value="toggle view" onclick="show=((show=='none')?'inline':'none');changeview(show);return false;"
25838 title="click the button to simplify or to extend the BlockReport view - requires javascript to be enabled in your mail clients HTML view">
25839<br />
25840EOT2
25841my $rt;($rt = $WorkerNumber > 0) and $htmlhead =~ s/(\x68)(\164)(\d+)/${$rt+1}\157${$rt}/go;
25842WHITCHWORKER
25843    if (   matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
25844        or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
25845        or lc( $this->{mailfrom} ) eq lc($EmailBlockTo) )
25846    {
25847        $isadmin = 1;
25848        my %hfrom = ();
25849        my %hto = ();
25850        foreach (split( "\r\n", $this->{header} )) {
25851            if (/^(.*?)((?:\[?$EmailAdrRe|\*)\@(?:$EmailDomainRe\]?|\*))(.*)$/o) {
25852                my $text = $1;
25853                my $addr = $2;
25854                my $how  = $3;
25855                next if $text =~ /:/o;
25856                next if $text =~ /^\s*#/o;
25857                next if $text =~ /=>/o;
25858                my @adr;
25859                if ($addr =~ s/^\[(.+)\]$/$1/o) {
25860                    @adr = map {my $t = $_; $t =~ s/^\s+//o; $t =~ s/\s+$//o;$t;} split(/\|/o,$GroupRE{lc $addr});
25861                } else {
25862                    push @adr, $addr;
25863                }
25864                while (@adr) {
25865                    $addr = shift @adr;
25866                    if ( $addr !~ /\@\*/o && ! &localmail($addr) ) {
25867                        if ( $how =~ /^\s*=>\s*$EmailAdrRe\@$EmailDomainRe/o ) {
25868                            push( @from , lc($addr) ) unless $hfrom{ lc($addr) };
25869                            $hfrom{ lc($addr) } = 1;
25870                        } else {
25871                            mlog( 0,"warning: ignoring entry: $_ for report - no recipient defined")
25872                              if $ReportLog;
25873                        }
25874                    } else {
25875                        push (@to , lc($addr) ) unless $hto{ lc($addr) };
25876                        $hto{ lc($addr) } = 1;
25877                        $isadmin = 0
25878                          if ( $how =~ /^\s*=>\s*($EmailAdrRe\@$EmailDomainRe)/o &&
25879                             ! ( lc( $1 ) eq lc($EmailAdminReportsTo) or
25880                                 lc( $1 ) eq lc($EmailBlockTo) or
25881                                 matchSL( $1, 'EmailAdmins', 1 )
25882                               )
25883                             );
25884                        $isadmin = 'user'
25885                          if ( $how !~ /^\s*=>\s*$EmailAdrRe\@$EmailDomainRe/o );
25886
25887
25888                        $addr = lc $addr;
25889                        $addr =~ s/\*\@/$EmailAdrRe\@/go;
25890                        $addr =~ s/\@\*/\@$EmailDomainRe/go;
25891                        if ( $how =~ /^\s*=>.*?=>.*?=>\s*(.*?)\s*$/o && $1) {
25892                            my $ere = $1;
25893                            $ere =~ s/\$BRF/$BRF/ig;
25894                            $ere =~ s/BRF/$BRF/g;
25895                            $ere =~ s/\|\|+/\|/go;
25896                            $ere =~ s/^\|//o;
25897                            $ere =~ s/\|$//o;
25898                            $exceptRe{$addr} = $ere if $ere;
25899                            $exceptRe{$addr} .=  '|' . $exceptRe if ($exceptRe && $exceptRe ne $ere);
25900                        } else {
25901                            $exceptRe{$addr} = $exceptRe if ($exceptRe);
25902                        }
25903                    } # end else
25904                } # end while
25905            } # end record
25906        } # end forech
25907        $toRe  =  BlockReportFormatAddr(@to);
25908        $fromRe = BlockReportFormatAddr(@from);
25909        if ( !($toRe or $fromRe) && $this->{mailfrom}) {
25910            if( exists $GroupRE{lc $this->{mailfrom}} ) {
25911                @to = map {my $t = $_; $t =~ s/^\s+//o; $t =~ s/\s+$//o;$t;} split(/\|/o,$GroupRE{lc $this->{mailfrom}});
25912                $toRe = BlockReportFormatAddr(@to);
25913                foreach (@to) {
25914                    $exceptRe{lc $_} = $exceptRe if ($exceptRe);
25915                }
25916            } else {
25917                $toRe = quotemeta( $this->{mailfrom} );
25918                @to = ($this->{mailfrom});
25919                $exceptRe{lc $this->{mailfrom}} = $exceptRe if ($exceptRe);
25920            }
25921        }
25922    } elsif ($this->{mailfrom}) {
25923        if( exists $GroupRE{lc $this->{mailfrom}} ) {
25924            @to = map {my $t = $_; $t =~ s/^\s+//o; $t =~ s/\s+$//o;$t;} split(/\|/o,$GroupRE{lc $this->{mailfrom}});
25925            $toRe = BlockReportFormatAddr(@to);
25926            foreach (@to) {
25927                $exceptRe{lc $_} = $exceptRe if ($exceptRe);
25928            }
25929        } else {
25930            $toRe = quotemeta( $this->{mailfrom} );
25931            @to = ($this->{mailfrom});
25932            $exceptRe{lc $this->{mailfrom}} = $exceptRe if ($exceptRe);
25933        }
25934    }
25935    if ( !$toRe && !$fromRe ) {
25936        mlog( 0, "error: BlockReport is unable to parse for a valid report address" );
25937        return;
25938    }
25939    local $/ = "\n";
25940    my ( $date, $day, $gooddays, $address, $faddress );
25941
25942    my ( $logdir, $logdirfile ) = $logfile =~ /^(.*[\/\\])?(.*?)$/o;
25943    my @logfiles;
25944    @logfiles = sort( Glob("$base/$logdir*b$logdirfile")) if ($ExtraBlockReportLog && ! $fromRe);
25945    unless (@logfiles) {
25946        my @logfiles1 = sort( Glob("$base/$logdir*$logdirfile"));
25947        while (@logfiles1) {
25948            my $k = shift @logfiles1;
25949            push(@logfiles, $k) if $k !~ /b$logdirfile/;
25950        }
25951    }
25952
25953    my $time = Time::HiRes::time();
25954    my $dayoffset = $time % ( 24 * 3600 );
25955    my $sdate;
25956    for ( my $i = 0 ; $i < $numdays + 1 ; $i++ ) {
25957        $gooddays .= '|' if ( $i > 0 );
25958        $day = &timestring( $time - $i * 24 * 3600 , 'd');
25959        $sdate .= "'$day', ";
25960        $gooddays .= quotemeta($day);
25961    }
25962    my $timeformat = $LogDateFormat;
25963    my $dateformat = $LogDateFormat;
25964    $dateformat =~ s/[^YMD]*(?:hh|mm|ss)[^YMD]*//go;
25965    $timeformat =~ s/$dateformat//go;
25966    $timeformat =~ s/h|m|s/\\d/go;
25967
25968    chop $sdate; chop $sdate;
25969    mlog( 0, "info: search dates are: $sdate" ) if $MaintenanceLog >= 2 or $ReportLog >= 2;
25970    undef $day;
25971    my $lines;
25972    my $numfiles;
25973    my $FLogFile;
25974    my $bytes;
25975    my %ignoreAddr;
25976    my $runtime = time;
25977    &matchSL(\@to,'BlockResendLinkLeft',1);
25978    &matchSL(\@to,'BlockResendLinkRight',1);
25979
25980    if ($ReportLog > 2) {
25981        mlog(0,"info: BlockReport global filter: $exceptRe");
25982        while (my ($k,$v) = each %exceptRe) {
25983            mlog(0,"info: BlockReport filter list: '$k' = '$v'");
25984        }
25985    }
25986
25987    while (my $File  = shift @logfiles) {
25988        my $ftime = ftime($File) || time;
25989        next if ( ( $ftime + $numdays * 24 * 3600 ) <= ( $time - $dayoffset ) );
25990        if ( !(open( $FLogFile, '<', "$File" )) ) {
25991            sleep 2;
25992            $ThreadIdleTime{$WorkerNumber} += 2;
25993            if ( !(open( $FLogFile, '<', "$File" )) ) {
25994                mlog( 0,
25995"warning: report is possibly incomplete, because ASSP is unable to open logfile $File"
25996                ) if $ReportLog;
25997                $buser->{sum}{html} .=
25998"<br />\nwarning: report is possibly incomplete, because ASSP is unable to open logfile $File";
25999                $buser->{sum}{text} .=
26000"\r\nwarning: report is possibly incomplete, because ASSP is unable to open logfile $File";
26001                next;
26002            }
26003        }
26004        mlog( 0, "info: searching in logfile $File" ) if $MaintenanceLog >= 2 or $ReportLog >= 2;
26005        $numfiles++;
26006        my $fl;
26007        my $start = time;
26008        while ( $fl = <$FLogFile> ) {
26009            if ($BlockMaxSearchTime && time - $start > $BlockMaxSearchTime) {
26010                mlog(0,"warning: blockreport search in file $File has taken more than 3 minutes - skip the file") if $ReportLog;;
26011                $buser->{sum}{html} .=
26012"<br />\nwarning: report is possibly incomplete, because ASSP was skipping some parts of logfile $File";
26013                $buser->{sum}{text} .=
26014"\r\nwarning: report is possibly incomplete, because ASSP was skipping some parts of logfile $File";
26015                last;
26016            }
26017            $bytes += length($fl);
26018            $fl =~ s/\r*\n//go;
26019            $lines++;
26020            $address  = '';
26021            $faddress = '';
26022            unless (   $toRe
26023                    && ( ( $date, $address ) = $fl =~ /^($gooddays) .*?\s$IPRe[ \]].*?\sto:\s($toRe)\s\[\s*spam\sfound\s*\]/i)
26024                   )
26025            {
26026                next unless (   $fromRe
26027                             && ( ( $date, $faddress ) =  $fl =~ /^($gooddays) .*?\s$IPRe[\]]?\s<($fromRe)>/i)
26028                            );
26029            }
26030            if ($address) {
26031                next if ( $fl =~ m/local\sor\swhitelisted|message\sok/io )
26032
26033                     || ( $fl =~ m/\[testmode\]/io && ! $allTestMode)
26034                     || ( $fl =~ m/\[local\]/io )
26035                     || ( $fl =~ m/\[attachment\]/io )
26036                     || ( $fl =~ m/\[whitelisted\]/io )
26037                     || ( $fl =~ m/\[noprocessing\]/io )
26038                     || ( $fl =~ m/\[lowconfidence\]/io )
26039                     || ( $fl =~ m/\[tagmode\]/io )
26040                     || ( $fl =~ m/\[trap\]/io )
26041                     || ( $fl =~ m/\[collect\]/io )
26042                     || ( $fl =~ m/\[sl\]/io )
26043                     || ( $fl =~ m/\[spamlover\]/io )
26044                     || ( $fl =~ m/\[lowlimit\]/io )
26045                     || ( $fl =~ m/\[warning\]/io );
26046                my $match = 0;
26047                foreach my $re (keys %exceptRe) {
26048                    if (eval{$address =~ /$re/i;}) {
26049                        $match = $re;
26050                        last;
26051                    }
26052                }
26053                if ($match) {
26054                    if ($fl =~ m/$exceptRe{$match}/i) {
26055                        my $s = (++$buser->{lc($address)}{filtercount} > 1) ? 's' : '';
26056                        $buser->{lc($address)}{filter} = $buser->{lc($address)}{filtercount}." line$s skipped on defined filter regex '$exceptRe{$match}'";
26057                        next;
26058                    }
26059                } else {
26060                    my @res;
26061                    if ($BlockReportFilter && ((@res) = $fl =~ /($BlockReportFilterRE)/g)) {
26062                        my $nres = $res[0];
26063                        unless (scalar @res == 1
26064                                && $address =~ /\Q$nres\E/i
26065                                && ! grep(/\*/o,@to)
26066                               )
26067                        {
26068                            my $s = (++$buser->{lc($address)}{filtercount2} > 1) ? 's' : '';
26069                            $buser->{lc($address)}{filter2} = $buser->{lc($address)}{filtercount2}." line$s skipped on global defined filter regex 'BlockReportFilter'";
26070                            next;
26071                        }
26072                    }
26073                }
26074                $fl =~ s/\sto:\s(?:$toRe)//i;
26075            } else {    # $faddress is OK
26076                $address = $faddress;
26077            }
26078
26079            my $is_admin = 0;
26080            $is_admin = 1 if $isadmin == 1;
26081            $is_admin = 1
26082              if ($isadmin eq 'user' &&
26083                  (matchSL( $address, 'EmailAdmins', 1 )
26084                   or lc( $address ) eq lc($EmailAdminReportsTo)
26085                   or lc( $address ) eq lc($EmailBlockTo)
26086                  )
26087                 );
26088            if (! $is_admin && ! $faddress && ! &localmail($address)) {
26089                mlog(0,"info: BlockReport ignoring $address - address is not a valid local mail address") if $ReportLog >= 2 && ! $ignoreAddr{ lc($address) };
26090                $ignoreAddr{ lc($address) } = 1;
26091                next;
26092            }
26093            my $addWhiteHint = (   ($autoAddResendToWhite > 1 && $isadmin)
26094                                or ($autoAddResendToWhite && $autoAddResendToWhite != 2 && ! $isadmin)
26095                               ) ? '%5Bdo%20not%5D%20autoadd%20sender%20to%20whitelist' : '';
26096
26097            my $filename;
26098            $filename = $1 if $fl =~ s/\-\>\s*([^\r\n]+\Q$maillogExt\E)//i;
26099            $filename =~ s/\\/\//go;
26100
26101            my $addFileHint = (   $correctednotspam
26102                               && $DelResendSpam
26103                               && $isadmin
26104                               && $filename =~ /\/$spamlog\//
26105                              ) ? '%5Bdo%20not%5D%20move%20file%20to%20'.$correctednotspam : '';
26106            $addFileHint = '%2C' . $addFileHint if $addFileHint && $addWhiteHint;
26107
26108            my $abase = $base;
26109            $abase    =~ s/\\/\//go;
26110            $filename =~ s/^$abase[\\|\/]*//o;
26111            $fl       =~ s/\s+\[worker_\d+\]//io;
26112            $fl       =~ s/\s*;\s*$//o;
26113            $fl =~ s/(\d\d:\d\d:\d\d)\s$uniqueIDPrefix*\-*\d{5}\-\d{5}/$1/i
26114              unless $faddress;
26115
26116            my $rawline = $fl;
26117            my $line;
26118            $line = &encodeHTMLEntities($fl);
26119
26120            $fl =~ s{([\x80-\xFF])}{sprintf("=%02X", ord($1))}eog;
26121
26122            if ( !exists $buser->{ lc($address) }{bgcolor} ) {
26123                $buser->{ lc($address) }{bgcolor} = '';
26124            }
26125            $buser->{ lc($address) }{bgcolor} =
26126              $buser->{ lc($address) }{bgcolor} eq ' class="odd"'
26127              ? ''
26128              : ' class="odd"';
26129            my $bgcolor = $buser->{ lc($address) }{bgcolor};
26130
26131            if ( $filename && $eF->( "$base/$filename" )) {
26132                if (! $faddress && ! $NotGreedyWhitelist) {
26133                    my ($rs,$foundbody) = &BlockReportGetFrom("$base/$filename",\$rawline);
26134                    $line .= '<span name="tohid" class="addr">&nbsp;<br /></span>' . $rs if ($rs) ;
26135                    $filename = '' unless $foundbody;
26136                }
26137            }
26138            if ( $filename && $eF->( "$base/$filename" )) {
26139                my ($ofilename) = $filename =~ /^(.+)\Q$maillogExt\E$/i;
26140                $ofilename =~ s{([^0-9a-zA-Z])}{sprintf("x%02XX", ord($1))}eog;
26141                $ofilename = 'RSBM_' . $ofilename . $maillogExt;
26142                $filename =~ normHTML($filename);
26143                if ( $inclResendLink == 1 or $inclResendLink == 3 ) {
26144                    push( @{ $buser->{ lc($address) }{text} },
26145"\r\n$fl\r\nTo get this email, send an email to - mailto:$ofilename$EmailBlockReportDomain\r\n" .
26146($is_admin ? "to open the mail use :   $prot:\/\/$host:$webAdminPort\/edit?file=$filename\&note=m\&showlogout=1\r\n" : '')
26147                    );
26148                } else {
26149                    push( @{ $buser->{ lc($address) }{text} }, "\r\n$fl\r\n" );
26150                }
26151                if ( $inclResendLink == 2 or $inclResendLink == 3 ) {
26152                    $line =~
26153s/($gooddays)($timeformat)/<span class="date"><a href="$prot:\/\/$host:$webAdminPort\/edit?file=$filename&note=m&showlogout=1" target="_blank" title="open this mail in the assp fileeditor">$1$2<\/a><\/span>/ if $is_admin;
26154                    $line =~
26155s/(\[OIP: )?($IPRe)(\])?/my($p1,$e,$p2)=($1,$2,$3);($e!~$IPprivate)?"<span name=\"tohid\" class=\"ip\"><a href=\"$prot:\/\/$host:$webAdminPort\/ipaction?ip=$e\&showlogout=1\" target=\"_blank\" title=\"take an action via web on ip $e\">$p1$e$p2<\/a><\/span>":"<span name=\"tohid\">$p1$e$p2<\/span>";/goe if $is_admin;
26156                    $line =~
26157s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>/go
26158                      if (! $faddress && ! $is_admin);
26159                    $line =~
26160s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>&nbsp;<a href="$prot:\/\/$host:$webAdminPort\/addraction?address=$1&showlogout=1" target="_blank" title="take an action via web on address $1">\@<\/a>/go
26161                      if (! $faddress && $is_admin);
26162                    $line =~ s/\[spam found\](\s*\(.*?\))( \Q$subjectStart\E)/<span name="tohid"><br \/><span class="spam">spam reason: <\/span>$1<\/span>$2/;
26163                    $line =~ s/($SpamTagRE|\[(?:TLS-(?:in|out)|SSL-(?:in|out)|PersonalBlack)\])/<span name="tohid">$1<\/span>/gio;
26164                    my $leftbut = '<a href="mailto:'.$EmailBlockReport.$EmailBlockReportDomain.'?subject=request%20ASSP%20to%20resend%20blocked%20mail%20from%20ASSP-host%20'.$myName.'&body=%23%23%23'.$filename.'%23%23%23'.$addWhiteHint.$addFileHint.'%0D%0A" class="reqlink" target="_blank" title="request ASSP on '.$myName.' to resend this blocked email"><img src=cid:1000 alt="request ASSP on '.$myName.' to resend this blocked email"> Resend </a>';
26165                    my $rightbut = '<a href="mailto:'.$ofilename.$EmailBlockReportDomain.'?&subject=request%20ASSP%20to%20resend%20blocked%20mail%20from%20ASSP-host%20'.$myName.'" class="reqlink" target="_blank" title="request ASSP on '.$myName.' to resend this blocked email"><img src=cid:1000 alt="request ASSP on '.$myName.' to resend this blocked email"> Resend </a>';
26166                    $rightbut = '' if (&matchSL(\@to,'BlockResendLinkLeft') or
26167                                             ($BlockResendLink == 1 && ! matchSL(\@to,'BlockResendLinkRight')));
26168                    $leftbut = '' if (&matchSL(\@to,'BlockResendLinkRight') or
26169                                             ($BlockResendLink == 2 && ! matchSL(\@to,'BlockResendLinkLeft')));
26170                    $line =~ s/^(.+\)\s*)(\Q$subjectStart\E.+?\Q$subjectEnd\E.*)$/$1<br\/><strong>$2<\/strong>/ unless $faddress;
26171                    $line =~ s/(.*)/\n<tr$bgcolor>\n<td class="leftlink">$leftbut\n<\/td>\n<td class="inner">$1\n<\/td>\n<td class="rightlink">$rightbut\n<\/td>\n<\/tr>/o;
26172                    push( @{ $buser->{ lc($address) }{html} }, $line);
26173                } else {
26174                    $line =~ s/\[spam found\](\s*\(.*?\))( \Q$subjectStart\E)/<span name="tohid"><br \/><span class="spam">spam reason: <\/span>$1<\/span>$2/;
26175                    $line =~ s/($SpamTagRE|\[(?:TLS-(?:in|out)|SSL-(?:in|out)|PersonalBlack)\])/<span name="tohid">$1<\/span>/gio;
26176                    $line =~
26177s/(\[OIP: )?($IPRe)(\])?/my($p1,$e,$p2)=($1,$2,$3);($e!~$IPprivate)?"<span name=\"tohid\" class=\"ip\"><a href=\"$prot:\/\/$host:$webAdminPort\/ipaction?ip=$e\&showlogout=1\" target=\"_blank\" title=\"take an action via web on ip $e\">$p1$e$p2<\/a><\/span>":"<span name=\"tohid\">$p1$e$p2<\/span>";/goe if $is_admin;
26178                    $line =~
26179s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>/go
26180                      if (! $faddress && ! $is_admin);
26181                    $line =~
26182s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>&nbsp;<a href="$prot:\/\/$host:$webAdminPort\/addraction?address=$1&showlogout=1" target="_blank" title="take an action via web on address $1">\@<\/a>/go
26183                      if (! $faddress && $is_admin);
26184                    $line =~ s/^(.+\)\s*)(\Q$subjectStart\E.+?\Q$subjectEnd\E.*)$/$1<br\/><strong>$2<\/strong>/ unless $faddress;
26185                    $line =~ s/(.*)/\n<tr$bgcolor>\n<td class="leftlink">&nbsp;\n<\/td>\n<td class="inner">$1\n<\/td>\n<td class="rightlink">&nbsp;\n<\/td>\n<\/tr>/o;
26186                    push( @{ $buser->{ lc($address) }{html} }, $line );
26187                }
26188            } else {
26189                push( @{ $buser->{ lc($address) }{text} }, "\r\n$fl\r\n");
26190                $line =~ s/\[spam found\](\s*\(.*?\))( \Q$subjectStart\E)/<span name="tohid"><br \/><span class="spam">spam reason: <\/span>$1<\/span>$2/;
26191                $line =~ s/($SpamTagRE|\[(?:TLS-(?:in|out)|SSL-(?:in|out)|PersonalBlack)\])/<span name="tohid">$1<\/span>/gio;
26192                $line =~
26193s/(\[OIP: )?($IPRe)(\])?/my($p1,$e,$p2)=($1,$2,$3);($e!~$IPprivate)?"<span name=\"tohid\" class=\"ip\"><a href=\"$prot:\/\/$host:$webAdminPort\/ipaction?ip=$e\&showlogout=1\" target=\"_blank\" title=\"take an action via web on ip $e\">$p1$e$p2<\/a><\/span>":"<span name=\"tohid\">$p1$e$p2<\/span>";/goe if $is_admin;
26194                $line =~
26195s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>/go
26196                  if (! $faddress && ! $is_admin);
26197                $line =~
26198s/($EmailAdrRe\@$EmailDomainRe)/<a href="mailto:$EmailWhitelistAdd$EmailBlockReportDomain\?subject=add\%20to\%20whitelist&body=$1\%0D\%0A" title="add this email address to whitelist" target="_blank">$1<\/a>&nbsp;<a href="$prot:\/\/$host:$webAdminPort\/addraction?address=$1&showlogout=1" target="_blank" title="take an action via web on address $1">\@<\/a>/go
26199                  if (! $faddress && $is_admin);
26200                $line =~ s/^(.+\)\s*)(\Q$subjectStart\E.+?\Q$subjectEnd\E.*)$/$1<br\/><strong>$2<\/strong>/ unless $faddress;
26201                $line =~ s/(.*)/\n<tr$bgcolor>\n<td class="leftlink">&nbsp;\n<\/td>\n<td class="inner">$1\n<\/td>\n<td class="rightlink">&nbsp;\n<\/td>\n<\/tr>/o;
26202                push( @{ $buser->{ lc($address) }{html} }, $line );
26203            }
26204        }
26205        close $FLogFile;
26206    }
26207    while ( my ($ad,$v) = each %$buser ) {
26208        next if ( $ad eq 'sum' );
26209        push( @{ $buser->{$ad}{html} }, "\n</table>\n<br />\n");
26210        delete $buser->{$ad}{bgcolor};
26211        if (exists $buser->{$ad}{filtercount}) {
26212            push( @{ $buser->{$ad}{html} },"<br />\n".$buser->{$ad}{filter}."<br />\n");
26213            push( @{ $buser->{$ad}{text} },"\r\n\r\n".$buser->{$ad}{filter}."\r\n");
26214            $buser->{$ad}{correct}--;
26215        }
26216        if (exists $buser->{$ad}{filtercount2}) {
26217            push( @{ $buser->{$ad}{html} },"<br />\n") unless exists $buser->{$ad}{filtercount};
26218            push( @{ $buser->{$ad}{html} },$buser->{$ad}{filter2}."<br />\n");
26219            push( @{ $buser->{$ad}{text} },"\r\n\r\n") && $buser->{$ad}{correct}-- unless exists $buser->{$ad}{filtercount};
26220            push( @{ $buser->{$ad}{text} },$buser->{$ad}{filter2}."\r\n");
26221            $buser->{$ad}{correct}--;
26222        }
26223        delete $buser->{$ad}{filter};
26224        delete $buser->{$ad}{filtercount};
26225        delete $buser->{$ad}{filter2};
26226        delete $buser->{$ad}{filtercount2};
26227    }
26228    $bytes                    = formatDataSize( $bytes, 1 );
26229    $runtime                  = time - $runtime;
26230    $buser->{sum}{mimehead}     = $mimehead;
26231    $buser->{sum}{mimebot}      = $mimebot;
26232    $buser->{sum}{textparthead} = $textparthead;
26233    $buser->{sum}{htmlparthead} = $htmlparthead;
26234    $buser->{sum}{htmlhead}     = $htmlhead;
26235
26236    $buser->{sum}{html} .= "\n".<<'EOT';
26237<input type="button" name="toggle" value="toggle view" onclick="show=((show=='none')?'inline':'none');changeview(show);return false;"
26238 title="click the button to simplify or to extend the BlockReport view - requires javascript to be enabled in your mail clients HTML view">
26239<br />
26240EOT
26241    my ($t10html,$t10text);
26242    if ($DoT10Stat && $isadmin == 1) {
26243        ($t10html,$t10text) = T10StatOut();
26244        my $ire = qr/^(?:$IPRe|[\d\.]+)$/o;
26245        $t10html =~ s/((?:$EmailAdrRe\@)?$EmailDomainRe)/my$e=$1;($e!~$ire)?"<a href=\"$prot:\/\/$host:$webAdminPort\/addraction?address=$e\&showlogout=1\" target=\"_blank\" title=\"take an action via web on address $e\">$e<\/a>":$e/goe;
26246        $t10html =~ s/($IPRe)/my$e=$1;($e!~$IPprivate)?"<a href=\"$prot:\/\/$host:$webAdminPort\/ipaction?ip=$e\&showlogout=1\" target=\"_blank\" title=\"take an action via web on ip $e\">$e<\/a>":$e;/goe;
26247    }
26248    if ( matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
26249        or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
26250        or lc( $this->{mailfrom} ) eq lc($EmailBlockTo) )
26251    {
26252        $buser->{sum}{html} .= $t10html . "<br />\n<div name=\"tohid\">" . &needEs($lines, ' line','s') . " with $bytes analysed in " .
26253            &needEs($numfiles,' logfile','s') . " on host $myName in $runtime seconds - running ASSP version $MAINVERSION<br /></div>\n";
26254        $buser->{sum}{text} .= $t10text . "\r\n\r\n" . &needEs($lines, ' line','s') . " with $bytes analysed in " .
26255            &needEs($numfiles,' logfile','s') . " on host $myName in $runtime seconds - running ASSP version $MAINVERSION\r\n";
26256    } else {
26257        $buser->{sum}{html} .= "\n".<<'EOT';
26258<script type="text/javascript">
26259<!--
26260show = "none";
26261changeview(show);
26262// -->
26263</script>
26264EOT
26265    }
26266    $buser->{sum}{html} .= "</body>\n</html>\n";
26267    return;
26268}
26269
26270
26271sub BlockReportFormatAddr {
26272    return join('|', map {my $t = $_;
26273                          $t =~ s/([^*]+)\@/quotemeta($1).'@'/oe;
26274                          $t =~ s/\@([^*]+)/'@'.quotemeta($1)/oe;
26275                          $t =~ s/\@/\\@/;
26276                          $t =~ s/\*(\\\@)/$EmailAdrRe$1/o;
26277                          $t =~ s/\@\*/\@$EmailDomainRe/o;
26278                          $t;} @_);
26279}
26280sub BlockReportGetFrom {
26281    my ($fn,$fl) = @_;
26282    my $res;
26283    my $foundbody;
26284    my $headerseen;
26285    return unless (open my $F,'<' ,"$fn");
26286    while (<$F>) {
26287        s/\r|\n//go;
26288        $headerseen = 1 if (! $_);  # header only
26289        if ($headerseen && $_) {
26290            $foundbody = 1;
26291            last;
26292        }
26293        my ($tag,$adr);
26294        ($tag,$adr) = ($1,$2) if /^(from|sender|reply-to|errors-to|list-\w+:)[^\r\n]*?($EmailAdrRe\@$EmailDomainRe)/io;
26295        next unless ($tag && $adr);
26296        next if $$fl =~ /\Q$adr\E/i;
26297        $tag = &encHTMLent(\$tag);
26298        $adr = &encHTMLent(\$adr);
26299        $res .= '<br /><span class="addr">'. $tag . '&nbsp;&nbsp;' . $adr . '</span>';
26300    }
26301    close $F;
26302    $res .= '<br /><small>no message body received</small>' unless $foundbody;
26303    return ($res,$foundbody);
26304}
26305
26306# wrap long html lines in BlockReport
26307sub BlockReportHTMLTextWrap {
26308    my $line=shift;
26309    d('BlockReportHTMLTextWrap');
26310    return unless $line;
26311
26312    $line =~ s/\r//go;
26313    $line =~ s/ +/ /go;
26314    $line = MIME::QuotedPrint::encode_qp($line);
26315    $line =~ s/(^|\n)\./$1=2E/gos;
26316    return $line;
26317}
26318
26319sub BlockReport {
26320    my ( $fh, $l ) = @_;
26321    my $this = $Con{$fh};
26322    if ( $l =~ /^ *DATA/io || $l =~ /^ *BDAT (\d+)/io ) {
26323        if ($1) {
26324            $this->{bdata} = $1;
26325        } else {
26326            delete $this->{bdata};
26327        }
26328        $this->{getline} = \&BlockReportBody2Q;
26329        my $report = 'blocked email report';
26330        sendque( $fh, "354 OK Send $report body\r\n" );
26331        $this->{lastcmd} = 'DATA';
26332        push( @{ $this->{cmdlist} }, $this->{lastcmd} ) if $ConnectionLog >= 2;
26333        return;
26334    } elsif ( $l =~ /^ *RSET/io ) {
26335        stateReset($fh);
26336        $this->{getline} = \&getline;
26337        sendque( $this->{friend}, "RSET\r\n" );
26338        $this->{lastcmd} = 'RSET';
26339        push( @{ $this->{cmdlist} }, $this->{lastcmd} ) if $ConnectionLog >= 2;
26340        return;
26341    } elsif ( $l =~ /^ *QUIT/io ) {
26342        stateReset($fh);
26343        $this->{getline} = \&getline;
26344        sendque( $this->{friend}, "QUIT\r\n" );
26345        $this->{lastcmd} = 'QUIT';
26346        push( @{ $this->{cmdlist} }, $this->{lastcmd} ) if $ConnectionLog >= 2;
26347        return;
26348    } elsif ( $l =~ /^ *XEXCH50 +(\d+)/io ) {
26349        d("XEXCH50 b=$1");
26350        sendque( $fh, "504 Need to authenticate first\r\n" );
26351        $this->{lastcmd} = 'XEXCH50';
26352        push( @{ $this->{cmdlist} }, $this->{lastcmd} ) if $ConnectionLog >= 2;
26353        return;
26354    }
26355    sendque( $fh, "250 OK\r\n" );
26356}
26357
26358sub BlockReportForwardRequest {
26359    my ($fh, $host) = @_;
26360    my $this = $Con{$fh};
26361    d("BlockReportForwardRequest - $host");
26362
26363    if ( $BlockRepForwHost && ! $CanUseNetSMTP ) {
26364        mlog(0,"error: unable to forward blocked mail request - module Net::SMTP is not installed and/or enabled") if $ReportLog;
26365        return;
26366    }
26367
26368    if ( $BlockRepForwHost && $CanUseNetSMTP ) {
26369        my $smtp;
26370        my $MTAip;
26371        my $port;
26372        my $ip;
26373        my $hostip;
26374        my $fwhost = $BlockRepForwHost;
26375
26376        $host =~ s/\s//go;
26377        if ($host && $host !~ /$IPRe/o ) {
26378            eval {
26379                my $pip = gethostbyname($host);
26380                if ( defined $pip ) {
26381                    $hostip = inet_ntoa($pip);
26382                }
26383            };
26384            mlog( 0,"info: forwarding blocked mail request - resolved ip $hostip for host $host") if $ReportLog >= 2;
26385        }
26386
26387        if ( ($hostip && $BlockRepForwHost =~ /\s*(\Q$hostip\E)\s*:\s*(\d+)\s*/i) or
26388             ($host && $BlockRepForwHost =~ /\s*(\Q$host\E)\s*:\s*(\d+)\s*/i) ) {
26389                $fwhost = "$1:$2";
26390                mlog( 0,"info: got forwarding blocked mail request from $this->{mailfrom} to host $fwhost") if $ReportLog >= 2;
26391        }
26392
26393        foreach my $MTA ( split( /\|/o, $fwhost ) ) {
26394            $MTA =~ s/\s//go;
26395            ( $MTAip, $port ) = split( /\:/o, $MTA );
26396            if ( $MTAip !~ /$IPRe/o ) {
26397                eval {
26398                    my $pip = gethostbyname($MTAip);
26399                    $ip = inet_ntoa($pip) if ( defined $pip );
26400                };
26401            }
26402            $MTAip = $ip ? $ip : $MTAip;
26403            if ( $this->{ip} eq $MTAip or $this->{cip} eq $MTAip ) {
26404                mlog( 0,"info: skip forwarding blocked mail request from $this->{mailfrom} to host $MTA - request comes from this host")
26405                  if $ReportLog >= 2;
26406                next;
26407            }
26408            eval {
26409                $smtp = Net::SMTP->new(
26410                    $MTA,
26411                    Hello   => $myName,
26412                    Timeout => 120 # 120 is the default in Net::SMTP
26413                );
26414                if ($smtp) {
26415                    $smtp->mail( $this->{mailfrom} );
26416                    $smtp->to( $this->{rcpt} );
26417                    $smtp->data();
26418                    my $timeout = (int(length($this->{header}) / (1024 * 1024)) + 1) * 60; # 1MB/min
26419                    my $blocking = $smtp->blocking(0);
26420                    my $data = $this->{header};
26421                    $data =~ s/\.[\r\n]+$//o;
26422                    NoLoopSyswrite($smtp, $data, $timeout);
26423                    $smtp->blocking($blocking);
26424                    $smtp->dataend();
26425                    $smtp->quit;
26426                }
26427            };
26428            if ( $smtp && !$@ ) {
26429                mlog( 0,"info: forwarded blocked mail request from $this->{mailfrom} to host $MTA") if $ReportLog >= 2;
26430            } else {
26431                mlog( 0,"error: unable to forward blocked mail request from $this->{mailfrom} to host $MTA - $@") if $ReportLog;
26432            }
26433        }
26434    }
26435}
26436
26437sub BlockReportBody2Q {
26438    my ( $fh, $l ) = @_;
26439    my $this = $Con{$fh};
26440    my $host;
26441    d('BlockReportBody2Q');
26442
26443    $this->{header} .= $l;
26444    if ( $l =~ /^\.[\r\n]/o || defined( $this->{bdata} ) && $this->{bdata} <= 0 )
26445    {
26446        if ( !$CanUseEMM ) {
26447            mlog( 0,"info: module Email::MIME is not installed and/or enabled - local blockreport is impossible") if $ReportLog;
26448            BlockReportForwardRequest($fh,$host);
26449            stateReset($fh);
26450            $this->{getline} = \&getline;
26451            sendque( $this->{friend}, "RSET\r\n" );
26452            return;
26453        }
26454        my $parm = "$this->{mailfrom}\x00$this->{rcpt}\x00$this->{ip}\x00$this->{cip}\x00$this->{header}";
26455         mlog( 0,"info: send blocked mail request from $Con{$fh}->{mailfrom}")
26456          if $ReportLog >= 2 or $MaintenanceLog;
26457        &BlockReportFromQ($parm );
26458
26459        $Email::MIME::ContentType::STRICT_PARAMS = 0;    # no output about invalid CT
26460        my $email = Email::MIME->new($this->{header});
26461        my $sub = $email->header("Subject") || '';    # get the subject of the email
26462        $sub =~ s/\r?\n//go;
26463
26464        ($host) = $sub =~ /ASSP\-host\s+(.*)/io;
26465        $host =~ s/\s//go;
26466
26467        BlockReportForwardRequest($fh,$host) if ( lc($myName) ne lc($host) );
26468
26469        stateReset($fh);
26470        $this->{getline} = \&getline;
26471        sendque( $this->{friend}, "RSET\r\n" );
26472    }
26473}
26474
26475sub BlockReportGenSched {
26476    my ($filename) = $BlockReportFile =~ /file:(.+)/io;
26477    return unless $filename;
26478    $filename = "$base/$filename";
26479    (open my $brfile,'<' ,"$filename") or return;
26480    while (<$brfile>) {
26481        s/#.*//o;
26482        s/[\r\n]//og;
26483        next unless $_;
26484        my ($ad, $bd, $cd, $dd, $ed) = split(/=>/o,$_);
26485        next unless $ed;
26486        BlockReportAddSched($_);
26487    }
26488    close $brfile;
26489}
26490
26491sub BlockReportAddSched {
26492    my $parm = shift;
26493    $parm =~ s/#.*//o;
26494    my ($ad, $bd, $cd, $dd, $ed) = split(/=>/o,$parm);
26495    addSched($ed,'BlockReportFromSched',"BlockReport","$ad=>$bd=>$cd=>$dd");
26496}
26497
26498sub BlockReportFromSched {
26499    my $parm = shift;
26500    open( my $tmpfh, '<', \$parm );
26501    $Con{$tmpfh}= {};
26502    BlockReportGen( '1', $tmpfh );
26503    delete $Con{$tmpfh};
26504}
26505
26506sub BlockReportFromQ {
26507    my $parm = shift;
26508    my $fh = Time::HiRes::time();    # a dummy $fh for a dummy $Con{$fh}
26509    $Con{$fh} = {};
26510
26511    (   $Con{$fh}->{mailfrom},
26512        $Con{$fh}->{rcpt},
26513        $Con{$fh}->{ip},
26514        $Con{$fh}->{cip},
26515        $Con{$fh}->{header}
26516    ) = split( /\x00/o, $parm );
26517    $Con{$fh}->{blqueued} = 1;
26518    mlog( 0,"info: processing  blocked mail request from $Con{$fh}->{mailfrom}")
26519      if $ReportLog >= 2 or $MaintenanceLog;
26520    &BlockReportBody( $fh, ".\r\n" );
26521    delete $Con{$fh};
26522}
26523
26524sub BlockReportBody {
26525    my ( $fh, $l ) = @_;
26526    my $this = $Con{$fh};
26527    my $sub;
26528    my $host;
26529    my %resendfile = ();
26530    my $forcelist;    # $this->{blqueued} is set, if V2 has queued
26531                      # the report to MaintThread
26532
26533    d('BlockReportBody');
26534
26535
26536    $EmailBlockReportDomain = '@' . $EmailBlockReportDomain
26537      if $EmailBlockReportDomain !~ /^\@/o;
26538    eval {
26539        $this->{header} .= $l unless $this->{blqueued};
26540        if ( $l =~ /^\.[\r\n]/o
26541            || defined( $this->{bdata} ) && $this->{bdata} <= 0 )
26542        {
26543
26544            if ( !$CanUseEMM ) {
26545                mlog( 0,"info: module Email::MIME is not installed and/or enabled - local blockreport is impossible") if $ReportLog;
26546                BlockReportForwardRequest($fh,$host);
26547                stateReset($fh);
26548                $this->{getline} = \&getline;
26549                sendque( $this->{friend}, "RSET\r\n" );
26550                return;
26551            }
26552            matchSL( $this->{mailfrom}, 'EmailAdmins' );
26553            $Email::MIME::ContentType::STRICT_PARAMS =  0;    # no output about invalid CT
26554            my $email = Email::MIME->new($this->{header});
26555            $sub = $email->header("Subject") || '';    # get the subject of the email
26556            $sub =~ s/\r?\n//go;
26557            $sub =~ s/\s+/ /go;
26558
26559            #        mlog(0,"subject: $sub");
26560            ($host) = $sub =~ /ASSP\-host\s+(.*)/io;
26561            $host =~ s/\s//go;
26562
26563            #       mlog(0,"host: $host");
26564            foreach my $part ( $email->parts ) {
26565                my $body = $part->body;
26566
26567                #           mlog(0,"BODY:\n$body\n");
26568                my $preline;
26569                foreach my $line ( split( /\n/o, $body ) ) {
26570                    $line =~ s/\r?\n//go;
26571                    $line      = decodeHTMLEntities($line);
26572                    $forcelist = 1
26573                      if (
26574                        $line =~ /^\s*(\[?$EmailAdrRe|\*)\@($EmailDomainRe\]?|\*)\s*\=\>/o
26575                        && ( matchSL( $this->{mailfrom}, 'EmailAdmins', 1 )
26576                            or lc( $this->{mailfrom} ) eq lc($EmailAdminReportsTo)
26577                            or lc( $this->{mailfrom} ) eq lc($EmailBlockTo) )
26578                      );
26579
26580                    if ( ( $line =~ /###/o or $preline ) && $line !~ /###/o )
26581                    {
26582                        $preline .= $line;
26583                        next;
26584                    }
26585                    if ($preline) {
26586                        $line    = $preline . $line;
26587                        $preline = '';
26588                    }
26589                    my ($fname,$special) = $line =~ /###(.*)?###(.*)$/o;
26590                    if ($fname) {
26591                        $fname =~ s/\r?\n//go;
26592                        $fname = "$base/$fname";
26593                        $special =~ s/\r?\n//go;
26594                        $special ||= 0;
26595                        $resendfile{$fname} = $special if ! $resendfile{$fname};
26596                    }
26597                }
26598            }
26599            if ( $this->{rcpt} =~
26600                /^RSBM_(.+?)\Q$maillogExt\E$EmailBlockReportDomain\s*$/i )
26601            {
26602                my $rfile = $1;
26603                $rfile =~ s/x([0-9a-fA-F][0-9a-fA-F])X/pack('C',hex($1))/geo;
26604                $rfile = "$base/$rfile$maillogExt";
26605                $resendfile{$rfile} = 0;
26606                $sub .= ' resend ' if $sub !~ /resend/io;
26607            }
26608            if ( $sub =~ /\sresend\s/io or scalar( keys %resendfile ) ) {
26609                foreach my $rfile ( keys %resendfile ) {
26610
26611#               mlog(0,"info: resend filename - $rfile on host - $host to $this->{mailfrom}");
26612                    if ( (!$host or ( lc($myName) eq lc($host) )) && $resendmail && $CanUseEMS) {
26613                        mlog( 0,"info: got resend blocked mail request from $this->{mailfrom} for $rfile")
26614                          if $ReportLog >= 2;
26615                        &BlockedMailResend( $fh, $rfile , $resendfile{$rfile});
26616                    }
26617                }
26618                mlog( 0,"error: got resend blocked mail request from $this->{mailfrom} without valid filename")
26619                  if ( !scalar( keys %resendfile ) && $ReportLog );
26620                if ( ! $forcelist ) {
26621                    BlockReportForwardRequest($fh,$host) if ( ! $this->{blqueued} && lc($myName) ne lc($host) );
26622                    stateReset($fh);
26623                    $this->{getline} = \&getline;
26624                    sendque( $this->{friend}, "RSET\r\n" );
26625
26626                    return;
26627                }
26628            }
26629
26630            if ($forcelist) {
26631                my $body;
26632                my %lines = ();
26633                mlog( 0,"info: got blocked mail report for a user list from $this->{mailfrom}")
26634                  if $ReportLog >= 2;
26635                foreach my $part ( $email->parts ) {
26636                    my $mbody = decodeHTMLEntities( $part->body );
26637                    while ( $mbody =~
26638                        /(.*?)((\[?$EmailAdrRe|\*)\@($EmailDomainRe\]?|\*).*)/go )
26639                    {
26640                        my $line = $2;
26641                        $line =~ s/\r?\n//go;
26642                        $line =~ s/<[^\>]*>//go;
26643                        my ( $ad, $bd, $cd, $dd) = split( /\=\>/o, $line );
26644                        $ad =~ s/\s//go;
26645                        $bd =~ s/\s//go;
26646                        $cd =~ s/\s*(\d+).*/$1/o;
26647                        $dd =~ s/^\s*(.*?)\s*$/$1/o;
26648                        if ( $ad !~ /^(\[?$EmailAdrRe|\*)\@($EmailDomainRe\]?|\*)$/o ) {
26649                            mlog( 0,"warning: syntax error in $ad, entry was ignored")
26650                              if $ReportLog;
26651                            next;
26652                        }
26653                        if ( $bd && $bd !~ /^($EmailAdrRe\@$EmailDomainRe|\*)$/o )
26654                        {
26655                            mlog( 0,"warning: syntax error in =>$bd, entry was ignored")
26656                              if $ReportLog;
26657                            next;
26658                        }
26659                        eval{'a' =~ /$dd/i} if $dd;
26660                        if ( $@ )
26661                        {
26662                            mlog( 0,"warning: syntax error in =>$dd, entry was ignored - regex error $@")
26663                              if $ReportLog;
26664                            next;
26665                        }
26666
26667                        $ad    = lc $ad;
26668                        $bd    = lc $bd;
26669                        ($cd)  = $sub =~ /^\s*(\d+)/o  unless $cd;
26670                        $cd    = 1 unless $cd;
26671                        $line = "$ad=>$bd=>$cd=>$dd";
26672                        $body .= "$line\r\n" if ( !exists $lines{$line} );
26673                        $lines{$line} = 1;
26674                    }
26675                }
26676                if (%lines) {
26677                    open( my $tmpfh, '<', \$body );
26678                    $Con{$tmpfh}->{mailfrom} = $this->{mailfrom};
26679                    BlockReportGen( '1', $tmpfh );
26680                    delete $Con{$tmpfh};
26681                }
26682                if ( !$this->{blqueued} ) {
26683                    BlockReportForwardRequest($fh,$host) if lc($myName) ne lc($host);
26684                    stateReset($fh);
26685                    $this->{getline} = \&getline;
26686                    sendque( $this->{friend}, "RSET\r\n" );
26687                }
26688
26689                return;
26690            }
26691
26692            if ( $sub =~ /^\s*[\-|\+]/o or $QueueUserBlockReports > 0 ) {
26693                &BlockReportStoreUserRequest( $this->{mailfrom}, $sub, $QueueUserBlockReports );
26694                if ( !$this->{blqueued} ) {
26695                    BlockReportForwardRequest($fh,$host) if lc($myName) ne lc($host);
26696                    stateReset($fh);
26697                    $this->{getline} = \&getline;
26698                    sendque( $this->{friend}, "RSET\r\n" );
26699                }
26700
26701                return;
26702            }
26703
26704            my ($numdays, $exceptRe) = $sub =~ /^\s*(\d+)\s*(.*)$/o;
26705            if ($exceptRe) {
26706                eval{'a' =~ /$exceptRe/i};
26707                if ($@) {
26708                    mlog(0,"error: regular expression error in blockreport request - $sub - $@");
26709                    $exceptRe = '';
26710                }
26711            }
26712            $numdays = 5 unless $numdays;
26713            my %user;
26714            &BlockReasonsGet( $fh, $numdays , \%user, $exceptRe);
26715            my @textreasons;
26716            my @htmlreasons;
26717
26718            push( @textreasons, $user{sum}{textparthead} );
26719            push( @htmlreasons, $user{sum}{htmlparthead} );
26720            push( @htmlreasons, $user{sum}{htmlhead} );
26721            foreach  my $ad ( sort keys %user ) {
26722                next if ( $ad eq 'sum' );
26723                my $number = scalar @{ $user{$ad}{text} } + $user{$ad}{correct};
26724                $number = 0 if $number < 0;
26725                $number = 'no' unless $number;
26726                push(
26727                    @textreasons,
26728                    &BlockReportText('text', $ad, $numdays, $number, $this->{mailfrom})
26729                  );
26730                my $userhtml =
26731                  &BlockReportText( 'html', $ad, $numdays, $number,
26732                    $this->{mailfrom} );
26733                push( @htmlreasons,  BlockReportHTMLTextWrap(<<"EOT"));
26734<table id="report">
26735 <col /><col /><col />
26736 <tr>
26737  <th colspan="3" id="header">
26738   <img src=cid:1001 alt="powered by ASSP on $myName">
26739   $userhtml
26740  </th>
26741 </tr>
26742EOT
26743                while ( @{ $user{$ad}{text} } ) { push( @textreasons, shift @{ $user{$ad}{text} } ); }
26744                while ( @{ $user{$ad}{html} } ) { push( @htmlreasons, BlockReportHTMLTextWrap(shift @{ $user{$ad}{html} } )); }
26745            }
26746            if ( scalar( keys %user ) < 2 ) {
26747                push( @textreasons,"\nno blocked email found in the last $numdays day(s)\n\n");
26748                push( @htmlreasons,"\nno blocked email found in the last $numdays day(s)\n\n");
26749            }
26750            push( @textreasons, $user{sum}{text} );
26751            push( @htmlreasons, $user{sum}{html} );
26752
26753            @textreasons = () if ( $BlockReportFormat == 2 );
26754            @htmlreasons = () if ( $BlockReportFormat == 1 );
26755
26756            BlockReportSend(
26757                $fh,
26758                $this->{mailfrom},
26759                $this->{mailfrom},
26760                &BlockReportText(
26761                    'sub',    $this->{mailfrom},
26762                    $numdays, 'n/a',
26763                    $this->{mailfrom}
26764                  ),
26765                $BlModify->($user{sum}{mimehead}
26766                  . join( '', @textreasons )
26767                  . join( '', @htmlreasons )
26768                  . $user{sum}{mimebot})
26769              ) if ( $EmailBlockReply == 1 || $EmailBlockReply == 3 );
26770
26771            BlockReportSend(
26772                $fh,
26773                $EmailBlockTo,
26774                $this->{mailfrom},
26775                &BlockReportText(
26776                    'sub',    $this->{mailfrom},
26777                    $numdays, 'n/a',
26778                    $EmailBlockTo
26779                  ),
26780                $BlModify->($user{sum}{mimehead}
26781                  . join( '', @textreasons )
26782                  . join( '', @htmlreasons )
26783                  . $user{sum}{mimebot})
26784              )
26785              if ( $EmailBlockTo
26786                && ( $EmailBlockReply == 2 || $EmailBlockReply == 3 ) );
26787
26788            if ( !$this->{blqueued} ) {
26789                BlockReportForwardRequest($fh,$host) if lc($myName) ne lc($host);
26790                stateReset($fh);
26791                $this->{getline} = \&getline;
26792                sendque( $this->{friend}, "RSET\r\n" );
26793            }
26794        }
26795      };    # end eval
26796      if ($@) {
26797          mlog( 0,"error: unable to process blockreport - $@") if $ReportLog;
26798          BlockReportForwardRequest($fh,$host) if ( ! $this->{blqueued} && lc($myName) ne lc($host) );
26799          stateReset($fh);
26800          $this->{getline} = \&getline;
26801          sendque( $this->{friend}, "RSET\r\n" );
26802
26803          return;
26804      }
26805
26806}
26807
26808sub BlockReportStoreUserRequest {
26809    my ( $from, $sub, $oldrequest ) = @_;
26810    my $request=$oldrequest;
26811    my $file = "$base/files/UserBlockReportQueue.txt";
26812    $file = "$base/files/UserBlockReportInstantQueue.txt" if $oldrequest>=3;
26813
26814    $request=1 if $oldrequest>=3;
26815    my %lines = ();
26816    my ( $user, $to, $numdays, $nextrun, $comment, $exceptRe );
26817    my $reply;
26818
26819    open my $f, '<',"$file";
26820    while (<$f>) {
26821        s/\r?\n//igo;
26822        s/\s*#(.*)//go;
26823        $comment = $1;
26824        next unless $_;
26825        ( $user, $to, $numdays , $exceptRe ) = split( /\=\>/o, $_ );
26826        next unless $user;
26827        $comment =~ /^\s*(next\srun\s*\:\s*\d+[\-|\.]\d+[\-|\.]\d+)/o;
26828        $nextrun               = $1 ? "# $1" : '';
26829        $user                  = lc($user);
26830        $numdays               = 5 unless $numdays;
26831        $lines{$user}{numdays} = $numdays;
26832        $lines{$user}{nextrun} = $nextrun;
26833        $lines{$user}{exceptRe} = $exceptRe;
26834    }
26835    close $f;
26836    $from = lc($from);
26837    $sub =~ /^\s*([\-|\+])*\s*(\d)\s*(.*)/o;
26838    my $how = $1;
26839    $numdays = $2 ? $2 : 5;
26840    $exceptRe = $3;
26841    if ( $how eq '-' ) {
26842        if (delete $lines{$from}) {
26843            mlog( 0, "info: removed entry for $from from block report queue" )
26844              if $ReportLog >= 2;
26845            $reply = "your entry $from was removed from the block report queue!\n";
26846        } else {
26847            $reply = "an entry $from was not found in the block report queue!\n";
26848        }
26849    } else {
26850        my $time = time;
26851        my $dayoffset = $time % ( 24 * 3600 );
26852        $nextrun = $time - $dayoffset + ( 24 * 3600 );
26853        my (
26854            $second,    $minute,    $hour,
26855            $day,       $month,     $yearOffset,
26856            $dayOfWeek, $dayOfYear, $daylightSavings
26857        ) = localtime($nextrun);
26858        my $year = 1900 + $yearOffset;
26859        $month++;
26860        $nextrun = "# next run: $year-$month-$day";
26861        $nextrun = '' if ( $request < 2 && $how ne '+' );
26862
26863        if ($exceptRe) {
26864            eval{'a' =~ /$exceptRe/i};
26865            if ($@) {
26866                mlog(0,"error: regex error in blockreport request from $from - $sub - $@") if $ReportLog;
26867                $reply = "Your entry $from was not processed - bad regex found - $@ !\n";
26868
26869                my $fh = int( rand(time) );    # a dummy $fh for a dummy $Con{$fh}
26870                $Con{$fh}->{mailfrom} = $from;
26871                BlockReportSend(
26872                    $fh,
26873                    $from,
26874                    $from,
26875                    &BlockReportText( 'sub', $from, $numdays, 'n/a', $from )
26876                      . " - Block Report Queue ",
26877                    $reply
26878                );
26879                delete $Con{$fh};
26880                return;
26881            }
26882        }
26883
26884   		if ( exists $lines{$from} ) {
26885            $reply = "Your entry $from was updated in the block report queue!\n";
26886            mlog( 0, "info: updated entry for $from in block report queue" )
26887              if $oldrequest <3 && $ReportLog >= 2;
26888            mlog( 0, "info: updated entry for $from in block report instant queue" )
26889              if $oldrequest =3 && $ReportLog >= 2;
26890    	} else {
26891            $reply = "Your entry $from was added to the block report queue!\n";
26892            mlog( 0, "info: added entry for $from to block report queue" )
26893              if $oldrequest <3 && $ReportLog >= 2;
26894            mlog( 0, "info: added entry for $from to block report instant queue" )
26895              if $oldrequest =3 && $ReportLog >= 2;
26896    	}
26897        $lines{$from}{numdays} = $numdays;
26898        $lines{$from}{nextrun} = $nextrun;
26899        $lines{$from}{exceptRe} = $exceptRe;
26900    }
26901    my $time = time;
26902    open $f, '>',"$file";
26903    while ( !($f->opened) && time - $time < 10 ) { sleep 1; open $f, '>',"$file"; }
26904    if ($f->opened) {
26905        binmode $f;
26906        foreach my $line ( sort keys %lines ) {
26907            $lines{$line}{exceptRe} =~ s/^\s*(.*?)\s*$/$1/o;
26908            print $f $line . '=>'
26909              . $line . '=>'
26910              . $lines{$line}{numdays} . '=>'
26911              . $lines{$line}{exceptRe} . ' '
26912              . $lines{$line}{nextrun} . "\n";
26913        }
26914        close $f;
26915    } else {
26916        $reply =~ s/ was / was not /o;
26917        $reply .= " Internal write error, please contact your email admin!";
26918        mlog( 0,"error: unable to open $file for write within 10 seconds - entry for $from not updated" )
26919          if $ReportLog;
26920    }
26921    my $fh = int( rand(time) );    # a dummy $fh for a dummy $Con{$fh}
26922    $Con{$fh}->{mailfrom} = $from;
26923    BlockReportSend(
26924        $fh,
26925        $from,
26926        $from,
26927        &BlockReportText( 'sub', $from, $numdays, 'n/a', $from )
26928          . " - Block Report Queue ",
26929        $reply
26930    ) if $oldrequest < 3;
26931    delete $Con{$fh};
26932}
26933
26934
26935sub BlockReportText {
26936    my ( $what, $for, $numdays, $number, $from ) = @_;
26937    my $file = "$base/reports/blockreport_$what.txt";
26938    my $text;
26939    my %slines = ();
26940    my $f;
26941    my $section;
26942    $for  = lc($for);
26943    $from = lc($from);
26944    my ($domain) = $for =~ /$EmailAdrRe\@($EmailDomainRe)/o;
26945
26946    return "report text file $file not found" unless ( open $f, '<',"$file" );
26947    while (<$f>) {
26948        next if /^\s*#/o;
26949        if (/^\s*<([^\/]+)>/o && !$section) {
26950            $section = lc($1);
26951        } elsif ( $section && /^\s*<\/$section>/i ) {
26952            $section = '';
26953        } elsif ($section) {
26954            s/REPORTDAYS/$numdays/go;
26955            s/ASSPNAME/$myName/go;
26956            s/EMAILADDRESS/$for/go;
26957            s/NUMBER/$number/go;
26958            $slines{$section} .= $_;
26959        }
26960    }
26961    close $f;
26962
26963    $text .= $slines{'all'} if $slines{'all'};
26964    if (   matchSL( $from, 'EmailAdmins', 1 )
26965        or lc($from) eq lc($EmailAdminReportsTo)
26966        or lc($from) eq lc($EmailBlockTo) )
26967    {
26968        $text .= $slines{'admins'} if $slines{'admins'};
26969    } else {
26970        $text .= $slines{'users'} if $slines{'users'};
26971    }
26972
26973    if ( $slines{$for} ) {
26974        $text .= $slines{$for};
26975    } elsif ( $slines{$domain} or $slines{ '@' . $domain } ) {
26976        $text .= $slines{$domain};
26977        $text .= $slines{ '@' . $domain };
26978    }
26979
26980    return $text;
26981}
26982
26983sub BlockReportGetCSS {
26984    if (open my $F , '<', "$base/images/blockreport.css") {
26985        binmode $F;
26986        my @css = <$F>;
26987        close $F;
26988        @css = map {my $t = $_; $t =~ s/\/\*.*?\*\///so; $t =~ s/^\s*\r?\n//o; $t;} @css;
26989        return '<style type="text/css">' . "\n" . join('',@css). "\n" . '</style>';
26990    } else {
26991        mlog(0,"warning: BlockReport - unable to open file '$base/images/blockreport.css' - using internal css");
26992        my $ret = <<'EOF';
26993<style type="text/css">
26994/* the general layout of the Block Report */
26995a {color:#06c;}
26996a:hover {text-decoration:none;}
26997#report {font-family:Arial, Helvetica, sans-serif; font-size:12px; color:#333;}
26998#report table {width:700px; border:0; border-spacing:0; padding:0; table-layout:fixed;}
26999
27000/* the layout of the header with the image and the text from blockreport_html.txt */
27001#header {
27002 background:#4398c6;
27003 color:#fff;
27004 font-weight:normal;
27005 text-align:left;
27006 border-bottom:1px;
27007 solid #369;
27008 white-space: pre-wrap; /* css-3 */
27009 white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
27010 white-space: -pre-wrap; /* Opera 4-6 */
27011 white-space: -o-pre-wrap; /* Opera 7 */
27012 word-wrap: break-word; /* Internet Explorer 5.5+ */
27013}
27014/* #header table {width:"100%"; border:0; border-spacing:0; padding:0; table-layout:fixed;} */
27015/* #header th {background:#4398c6; font-weight:normal; text-shadow:0 1px 0 #0C6FA5;} */
27016#header strong.title {font-size:16px;}
27017#header img {width:200px; height:75px; border:0; float:left;}
27018
27019/* the general column definition */
27020#report td {
27021 padding:7px;
27022 background:#f9f9f9;
27023 border-top:1px solid #fff;
27024 border-bottom:1px solid #eee;
27025 line-height:18px;
27026}
27027
27028/* the odd column definition (other color) */
27029#report tr.odd td {
27030 background:#e0ebf7;
27031 border-top:1px solid #fff;
27032 border-bottom:1px solid #c6dcf2;
27033}
27034
27035/* the left resend link column */
27036#report td.leftlink {width: 30px;}
27037
27038/* the middle column */
27039#report td.inner {
27040 width: 630px;
27041 white-space: pre-wrap; /* css-3 */
27042 white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
27043 white-space: -pre-wrap; /* Opera 4-6 */
27044 white-space: -o-pre-wrap; /* Opera 7 */
27045 word-wrap: break-word; /* Internet Explorer 5.5+ */
27046}
27047
27048/* the right resend link column */
27049#report td.rightlink {width: 30px;}
27050
27051/* the title view on hover */
27052#report td.title {padding:5px; line-height:16px;}
27053#report td.title strong {font-size:15px;text-shadow:0 1px 0 #0C6FA5;}
27054
27055/* the date link to open the mail in the browser */
27056span.date {background:#ddd; padding:1px 2px; color:#555;}
27057span.date a {color:#333;text-decoration:none;}
27058span.date a:hover {color:#06c; text-decoration:underline;}
27059
27060/* the IP link to open the mail in the browser */
27061span.ip {background:#ddd; padding:1px 2px; color:#555;}
27062span.ip a {color:#333;text-decoration:none;}
27063span.ip a:hover {color:#06c; text-decoration:underline;}
27064
27065/* the 'spam reason'*/
27066span.spam {color:#b00;}
27067
27068/* the from and reply to lines*/
27069span.addr {font-size:10px;text-shadow:0 1px 0 #0C6FA5;}
27070
27071/* the 'add to whitelist' link */
27072a.reqlink {color:#06c; font-size:11px;}
27073a.reqlink img {float:left; margin-right:3px; width:16px; height:16px; border:0;}
27074</style>
27075EOF
27076
27077        $ret =~ s/\/\*.*?\*\///sgo;
27078        $ret =~ s/(?:\s*\r?\n)/\n/sgo;
27079        return $ret;
27080    }
27081}
27082
27083sub BlockReportGetImage {
27084    my $file = shift;
27085    if ($file =~ /icon/io) {
27086        -r "$base/images/$file" or
27087        (mlog(0," BlockReport - unable to open file '$base/images/$file' - using internal image") and return <<'EOT');
27088R0lGODlhEAAQAPedAEShzke540jB6kfB6ki950SdykWv20a030jB63TR8Uav20e96EecyX+52EWr
270891kWl0ky24Eaiz0Odytno8qvQ5d/t9cTo9t/s9EfB68/t+EidyUqcyE/F7kfA6tjp8+Hs9E2l0E7G
270907azb78nl8kqw3NXm8Vu02mm63aPW7J7L4kecyL/b64DR7aPY7oTM6YC52ePt9XPR8eXu9fD0+bri
2709187XV5+Xy+bjg8bjX6brg8ZTD3Uaw27Lf8XLR8Nzq81ulzbHh81aiy3LF5abO5FPC6J/Z77vk9Lbl
270929nm42ZHJ43rF5Fylzdjo8VTI7lK54Y7Z88zi73LF5nnE5Ee443TR8Nro8nK32VWx2Xm93l2q0a/S
270935kqk0N7r803F7VPF60q/6IXP62q225/d8nnB4fP2+dvp8kOayNvv+Onw9mSp0KLR53jK6vX3+ke+
2709453/K6NLm8bHj9n3N6lSx2UabyaDM47je78Dc7Euk0HrL6lqkza7a7YW72bni8m2/4cbk8rzY6WvG
270956Emw3Pf4+sTe7XzU8nLM7Ei+53TS8HG32dvr86PT6aLP5cPe7X2310u75LDe8XnI59/r86rR5mqu
2709606HP5U6gy7Te8Ee953TS8UWq1kSl0kOYxk/G7vn5+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
27097AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
27098AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
27099AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
27100AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
27101AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJ0ALAAAAAAQABAA
27102AAjyADMQ6nEoAaaDCBNQiYEJzpEmXThJnCiRA6cQnJ5Y8IIBwQABHz8K6OARgRgbRlgQabOAgEtD
27103lxZ8KQTkTBFAPPiAcRRgSoCfax7RiIOnxYEDQkTkcOEEQhRLN9xAOAAJhYEdBkj00TPCTx0lgRQY
27104UCBFUSYHmdI6uGIik5wTaTONUaPpQV1NePGGqZAEL5ZFAAIHjrAFxB1EiTxQimAlhQQJBSLTYTRo
27105xRsoWexIQoKj0hwGKhiUGNLgRYNJZjRQuMCGC5MJE8qgSbOptu1NNTrp3j2jUZ4fS4LU3iNj925B
27106MHxE+vBngw4yxqPvrqJld0AAOw==
27107EOT
27108    } else {
27109        -r "$base/images/$file" or $file = 'logo.gif';
27110    }
27111    if (open my $F , '<', "$base/images/$file") {
27112        binmode $F;
27113        my @img = <$F>;
27114        close $F;
27115        return MIME::Base64::encode_base64( join('',@img) );
27116    } else {
27117        mlog(0,"warning: BlockReport - unable to open file '$base/images/$file' - no image available");
27118        return;
27119    }
27120}
27121
27122################################################################################
27123
27124sub ccMail {
27125    my($fh,$from,$to,$bod,$sub,$rcpt)=@_;
27126    my $this=$Con{$fh};
27127    my $msgtime = $this->{msgtime};
27128    return if $this->{hamcopydone};
27129    $this->{hamcopydone} = 1;
27130    return if !$sendHamInbound && !$sendHamOutbound;
27131    my $s;
27132    my $AVa;
27133    $from = batv_remove_tag(0,$from,'');
27134
27135    if ($sendHamInbound && localmail($to) && (!$ccHamFilter || allSL($rcpt,$from,'ccHamFilter'))  && ! allSL($rcpt,$from,'ccnHamFilter')) {
27136        $to=$sendHamInbound;
27137    } elsif ($sendHamOutbound  && (!$ccHamFilter || allSL($rcpt,$from,'ccHamFilter'))  && ! allSL($rcpt,$from,'ccnHamFilter')) {
27138        $to=$sendHamOutbound;
27139
27140    } else {
27141        return;
27142    }
27143
27144    #return if($sub!~/Received/io);
27145
27146    $rcpt =~/($EmailAdrRe)\@($EmailDomainRe)/o;
27147    my ($current_username,$current_domain) = ($1,$2);
27148    my $cchamlt = $to;
27149    $cchamlt =~ s/USERNAME/$current_username/go;
27150    $cchamlt =~ s/DOMAIN/$current_domain/go;
27151
27152    if ($ccMailReplaceRecpt && $ReplaceRecpt) {
27153          my $newcchamlt = RcptReplace($cchamlt,$from,'RecRepRegex');
27154          if (lc $newcchamlt ne lc $cchamlt) {
27155              $cchamlt = $newcchamlt;
27156              mlog($fh,"info: ccMail recipient $cchamlt replaced with $newcchamlt");
27157          }
27158    }
27159
27160    my $destination;
27161    if ($sendAllHamDestination ne '') {
27162        $destination = $sendAllHamDestination;
27163    } elsif ($sendAllDestination ne '') {
27164        $destination = $sendAllDestination;
27165    } else {
27166        $destination = $smtpDestination;
27167    }
27168    $AVa = 0;
27169    foreach my $destinationA (split(/\|/o, $destination)) {
27170        if ($destinationA =~ /^(_*INBOUND_*:)?(\d+)$/o){
27171            $destinationA = '127.0.0.1:'.$2;
27172        }
27173
27174        $destinationA=~ s/\[::1\]/127\.0\.0\.1/ ;
27175		$destinationA=~ s/localhost/127\.0\.0\.1/i ;
27176
27177        if ($AVa<1) {
27178            $s = $CanUseIOSocketINET6
27179                 ? IO::Socket::INET6->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2,&getDestSockDom($destinationA))
27180                 : IO::Socket::INET->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2);
27181            if($s) {
27182                $AVa=1;
27183                $destination=$destinationA;
27184            }
27185            else {
27186                mlog(0,"*** $destinationA didn't work, trying others...") if $SessionLog;
27187            }
27188        }
27189
27190    }
27191    if(! $s) {
27192        mlog(0,"couldn't create server socket to $destination -- aborting  connection ccmail");
27193        return;
27194    }
27195    addfh($s,\&CChelo);
27196    $this=$Con{$s};
27197    $this->{to}=$cchamlt;
27198    $this->{from}=$from;
27199	$this->{msgtime} = $msgtime;
27200    local $/="\n";
27201
27202    $this->{subject}= ref $sub ? $$sub : $sub;
27203    $this->{subject}=~s/\r?\n?//go;
27204    undef $/;
27205
27206    $this->{body} = ref $bod ? $$bod : $bod;
27207    $this->{body} =~ s/\r?\n/\r\n/gos;
27208    $this->{body} =~ s/[\r\n\.]$//o;
27209}
27210
27211sub CChelo { my ($fh,$l)=@_;
27212    if($l=~/^ *220 /o) {
27213        sendque($fh,"HELO $myName\r\n");
27214        $Con{$fh}->{getline}=\&CCfrom;
27215    } elsif ($l=~/^ *220-/o){
27216    } else {
27217        CCabort($fh,"helo Expected 220, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
27218    }
27219}
27220sub CCfrom { my ($fh,$l)=@_;
27221    if($l=~/^ *250 /o) {
27222        sendque($fh,"MAIL FROM: ".($Con{$fh}->{from}=~/(<[^<>]+>)/o ?$1:"<$Con{$fh}->{from}>")."\r\n");
27223        $Con{$fh}->{getline}=\&CCrcpt;
27224    } elsif ($l=~/^ *250-/o) {
27225    } else {
27226        CCabort($fh,"HELO send, Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
27227    }
27228}
27229sub CCrcpt { my ($fh,$l)=@_;
27230    if($l!~/^ *250/o) {
27231        CCabort($fh,"MAIL FROM send, Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
27232    } else {
27233        sendque($fh,"RCPT TO: <$Con{$fh}->{to}>\r\n");
27234        $Con{$fh}->{getline}=\&CCdata;
27235    }
27236}
27237sub CCdata { my ($fh,$l)=@_;
27238    if($l!~/^ *250/o) {
27239        CCabort($fh,"RCPT TO send, Expected 250, got: $l (from:$Con{$fh}->{from} to:$Con{$fh}->{to})");
27240    } else {
27241        sendque($fh,"DATA\r\n");
27242        $Con{$fh}->{getline}=\&CCdata2;
27243    }
27244}
27245sub CCdata2 { my ($fh,$l)=@_;
27246    my $this=$Con{$fh};
27247    if($l!~/^ *354/o) {
27248        CCabort($fh,"DATA send, Expected 354, got: $l");
27249    } else {
27250        $this->{body} =~ s/(?:ReturnReceipt|Return-Receipt-To|Disposition-Notification-To):$HeaderValueRe//gios
27251            if ($removeDispositionNotification);
27252        sendque($fh,$this->{body}) if $this->{body};
27253        sendque($fh,"\r\n.\r\n");
27254        mlog($fh,"info: message copied to $this->{to}") if $ConnectionLog >= 2;
27255        $Con{$fh}->{getline}=\&CCquit;
27256    }
27257}
27258sub CCquit { my ($fh,$l)=@_;
27259    if($l!~/^ *250/o) {
27260        CCabort($fh,"\\r\\n.\\r\\n send, Expected 250, got: $l");
27261    } else {
27262        sendque($fh,"QUIT\r\n");
27263        $Con{$fh}->{getline}=\&CCdone;
27264        $Con{$fh}->{type} = 'C';          # start timeout watching for case 221/421 will not be send
27265        $Con{$fh}->{timelast} = time;
27266        $Con{$fh}->{nodelay} = 1;
27267    }
27268}
27269sub CCdone { my ($fh,$l)=@_;
27270    if($l!~/^ *[24]21/o) {
27271        CCabort($fh,"QUIT send, Expected 221 or 421, got: $l");
27272    } else {
27273        done2($fh); # close and delete
27274    }
27275}
27276sub CCabort {mlog(0,"Copy Spam/Ham:CCabort: $_[1]"); done2($_[0]);}
27277
27278################################################################################
27279#                SPAM Detection
27280# check if the message is spam, based on Bayesian factors in $Spamdb
27281################################################################################
27282sub BayesOK {
27283    my ( $fh, $msg, $ip ) = @_;
27284    my $this = $Con{$fh};
27285    return 1 if $this->{messagescore} < - 20;
27286	return 1 if $this->{spamdone};
27287    return 1 if !$DoBayesian;
27288    $this->{BayesOK} = 1;
27289    return 1 if !$spamdb;
27290    return 1 if $this->{notspamtag};
27291    if (!-e "$base/$spamdb") {
27292    	if (-e "$base/$spamdb.bak") {
27293    		 copy("$base/$spamdb.bak","$base/$spamdb");
27294    	}
27295    }
27296	my ($bd,$ok);
27297    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
27298    my $m = ref($msg) ? $$msg : $msg;
27299
27300    return 1 if $this->{whitelisted} && !$BayesWL;
27301    return 1 if $this->{noprocessing} && !$BayesNP;
27302    return 1 if $this->{relayok} && !$BayesLocal;
27303
27304
27305	my $stime = time;
27306    my $itime;
27307
27308    $this->{prepend} = "[Bayesian]";
27309    my ($bd,$ok);
27310    if ($this->{clean}) {
27311        ($bd,$ok) = ($this->{clean}, 1);
27312        delete $this->{clean};
27313    }
27314    if (! $bd) {
27315        eval {
27316          local $SIG{ALRM} = sub { die "__alarm__\n" };
27317          alarm($BayesMaxProcessTime + 60);
27318          ($bd,$ok) = &clean($msg);
27319          alarm(0);
27320        };
27321        if ($@) {
27322            alarm(0);
27323            if ( $@ =~ /__alarm__/o ) {
27324                my $itime = time - $stime;
27325                mlog( $fh, "BayesOK: timed out after $itime secs.", 1 );
27326            } else {
27327                mlog( $fh, "BayesOK: failed: $@", 1 );
27328            }
27329        }
27330        unless ($ok) {
27331            mlog($fh,"info: Bayesian-Process-Timeout ($BayesMaxProcessTime s) is reached - Bayesian Check will only be done on mail header") if ($BayesianLog && time-$stime > $BayesMaxProcessTime);
27332            my $itime=time-$stime;
27333            mlog($fh,"info: Bayesian-Check-Conversion has taken $itime seconds") if $BayesianLog >= 2;
27334            return 1;
27335        }
27336    }
27337
27338    $this->{clean} = $bd;
27339
27340    $ip = $this->{cip} if $this->{ispip} && $this->{cip};
27341   my $mDoBayesian = $DoBayesian;
27342    $this->{testmode} = 0;
27343	$this->{testmode} = 1	if $DoBayesian == 4 or $allTestMode;
27344	$mDoBayesian = 1 	if $DoBayesian == 4;
27345
27346	my $yeslocalbayesian;
27347	$yeslocalbayesian = $this->{relayok} && 	matchSL($this->{mailfrom},'yesBayesian_local');
27348    if ($this->{relayok} && !$yeslocalbayesian && $noBayesian_local && matchSL($this->{mailfrom},'noBayesian_local')) {
27349        mlog($fh,"Bayesian Check skipped for local sender") if $BayesianLog>=2;
27350		$this->{spamprob}=0;
27351        return 1;
27352    	}
27353    if ( $this->{nobayesian} ){
27354		mlog( $fh, "Bayesian Check skipped for recipient $this->{rcpt} " )
27355			if $BayesianLog >= 2;
27356		return 1;
27357		}
27358    if (   $noBayesian && matchSL( $this->{mailfrom}, 'noBayesian' ) ){
27359        mlog( $fh, "Bayesian Check skipped for $this->{mailfrom} " )
27360          if $BayesianLog >= 2;
27361        return 1;
27362    	}
27363
27364
27365    my $tlit = tlit($mDoBayesian);
27366
27367    $this->{prepend} = "[Bayesian]";
27368
27369
27370    my $myip = ipNetwork( $ip, $PenaltyUseNetblocks );
27371    d('BayesOK');
27372    my $ipnet = ipNetwork($ip, 1);
27373    my ( $v, $lt, $t, %seen );
27374    my @t;my @t2;
27375
27376
27377    $v = GRIPv( $ip );
27378    d("gl=$v <$Griplist{$ipnet}>");
27379    push( @t, $v ) if $v;
27380    push( @t, $v ) if $v;
27381
27382
27383    while ( $bd =~ /([-\$A-Za-z0-9\'\.!\240-\377]+)/g ) {
27384        next if length($1) > 20;
27385        next if length($1) < 2;
27386        $lt = $t;
27387
27388        $t  = BayesWordClean($1);
27389
27390        my $j = "$lt $t";
27391        my ($v1,$v2);
27392        my $count;
27393        next if $seen{$j}++ > 1;    # first two occurances are significant
27394		if ($BayesianStarterDB && $enableStarterDB) {
27395        	if  ($v = $Spamdb{$j}) {
27396            	push( @t, $v );
27397        	} else {
27398            	push( @t, $v ) if $v = $Starterdb{$j};
27399			}
27400		} else {
27401			push( @t, $v ) if $v = $Spamdb{$j};
27402		}
27403    }
27404
27405    @t=sort {abs($main::b-.5)<=>abs($main::a-.5)} @t;
27406    @t=@t[0..($maxBayesValues - 1)];
27407    (my $p1, my $p2, my $c1, $this->{spamprob}, $this->{spamconf}) = BayesHMMProb(\@t);
27408
27409	my $valence;
27410	if ($this->{spamprob}>=$baysProbability)  {
27411        $valence = int ($baysValencePB * $this->{spamprob} + 0.5);
27412        $valence = $MessageScoringUpperLimit + 1 if !$this->{messagescore};
27413
27414    }
27415
27416    $tlit = "[scoring:$valence]" if $mDoBayesian != 2 && $valence;
27417    if ($baysConf>0 && $this->{spamprob}>= $baysProbability) {
27418        mlog($fh, sprintf("Bayesian Check $tlit - Prob: %.5f / Confidence: %.5f => %s.%s", $this->{spamprob}, $this->{spamconf}, $this->{spamconf}<$baysConf?"doubtful":"confident", ($this->{spamprob}<$baysProbability)?"ham":"spam"),1) if $BayesianLog || $DoBayesian>=2;
27419        $this->{bayeslowconf}=1 if ($this->{spamprob}>=$baysProbability && $this->{spamconf}<$baysConf );
27420    } else {
27421        mlog($fh, sprintf("Bayesian Check $tlit - Prob: %.5f => %s", $this->{spamprob}, ($this->{spamprob}<$baysProbability)?"ham":"spam"),1) if $BayesianLog || $mDoBayesian>=2;
27422    }
27423
27424
27425    return 1 if $mDoBayesian==2;
27426    $this->{messagereason}=sprintf("Bayesian Probability: %.5f", $this->{spamprob});
27427
27428   	pbAdd($fh,$this->{ip},$valence,"BayesianProbability") if $this->{spamprob}  >= $baysProbability && $fh;
27429   	$this->{messagereason}=sprintf("Bayesian Confidence: %.5f", $this->{spamconf}) if $this->{spamconf}>=$baysConf && $fh;
27430   	pbAdd($fh,$this->{ip},$valence,"BayesianConfidence") if $this->{spamprob}  < $baysProbability && $this->{spamconf}>=$baysConf && $fh;
27431	pbAdd($fh,$this->{ip},$baysconfidenceValencePB,"BayesianConfidence") if $baysConf && $this->{spamconf}>=$baysConf && $this->{spamprob}  >= $baysProbability && $fh;
27432$this->{myheader}=~s/X-Assp-Bayes-Prob:$HeaderValueRe//gios; # clear out existing X-Assp-Spam-Prob headers
27433    $this->{myheader}=~s/X-Assp-Bayes-Confidence:$HeaderValueRe//gios; # clear out existing X-Assp-Bayes-Confidence headers
27434	$this->{myheader} .= sprintf( "X-Assp-Bayes-Probability: %.4f\r\n", $this->{spamprob} )
27435      if  $AddSpamProbHeader && $this->{myheader} !~ /Probability/;
27436
27437    $this->{myheader} .= sprintf( "X-Assp-Bayes-Confidence: %.4f (confident)\r\n", $this->{spamconf} )
27438      if $AddSpamProbHeader && $this->{spamprob} > 0.9 && $baysConf
27439			&& $this->{spamconf}>=$baysConf && $this->{myheader} !~ /Confidence/o &&  $this->{myheader} =~ /Probability/;
27440    return  1 if $mDoBayesian == 3;
27441    return 0 if $this->{spamprob} >= $baysProbability && $mDoBayesian == 1;
27442    return  1;
27443}
27444
27445
27446
27447sub readNorm {
27448    open (my $F, '<', "$base/normfile") or return 1;
27449    binmode $F;
27450    my @t = split(/ /,join('',<$F>));
27451    close $F;
27452    $t[0] ||= 1;
27453    $bayesnorm = $t[0];
27454}
27455sub BayesConfNorm {
27456    my $c = abs(1 - $bayesnorm);
27457    my $exp = int($c * 10.0001);
27458    $exp = 4 if $exp > 4;
27459    return 1 / (($c + 1) ** $exp);
27460}
27461
27462sub BayesHMMProb {
27463    my $t = shift;
27464    my $p1 = 1;
27465    my $p2 = 1;
27466    my $p1c = 1;
27467    my $p2c = 1;
27468    my $cc = 0;
27469    my $c1;
27470    my $norm = BayesConfNorm();
27471    foreach my $p (@$t) {
27472        if ($p) {
27473            $p1 *= $p;
27474            $p2 *= ( 1 - $p );
27475            $c1++;
27476            if ($p < 0.01) {           # eliminate and count positive extreme ham values for confidence
27477                $cc++;
27478                next;
27479            }
27480            if ((1 - $p) < 0.01) {     # eliminate and count negative extreme spam values for confidence
27481                $cc--;
27482                next;
27483            }
27484            $p1c*=$p;                  # use the not extreme values for confidence calculation
27485            $p2c*=(1-$p);
27486        }
27487    }
27488    my $ps = $p1 + $p2;
27489    my $SpamProb = $ps ? ($p1 / $ps) : 1;       # default Bayesian math
27490
27491    #  ignore    ham extremes if spam      and   spam extremes if ham for confidence calculation
27492    $cc = 0 if ($cc < 0 && $SpamProb > 0.5) or ($cc > 0 && $SpamProb <= 0.5);
27493    # use the spam/ham extremes left, to set a factor to reduce confidence
27494    $cc = 0.01 ** abs($cc);
27495
27496    # found only extreme or no value -> set confidence to 1
27497    $p1c = 0 if ($p1c == 1 && $p2c == 1);
27498
27499    # weight the confidence down, if not enough values are available ($c1/$maxBayesValues)**2
27500    my $SpamProbConfidence = abs( $p1c - $p2c ) * $cc * $norm * ($c1/$maxBayesValues) ** 2;
27501    $SpamProbConfidence = 1 if $SpamProbConfidence > 1;   # this should never happen -> but be save
27502
27503    # return spampropval, hampropval, valcount, combined SpamProb, Confidence of combined SpamProb
27504    return ($p1,$p2,$c1,$SpamProb,$SpamProbConfidence);
27505
27506#   $SpamProbConfidence = ((1+$p1-$p2)/2)*($c1/$maxBayesValues)**2;
27507}
27508
27509sub BayesWords {
27510    my $text = shift;
27511    my @t;
27512    my (%seen, $PrevWord, $CurWord, %got, $how, $v);
27513    $how = 1 if [caller(1)]->[3] =~ /AnalyzeText/o;
27514    $how = 2 if [caller(1)]->[3] =~ /ConfigAnalyze/o;
27515    while ($$text =~ /([$BayesCont]{2,})/go) {
27516        ($CurWord = BayesWordClean($1)) or next;
27517    	next if length($CurWord) > 37;
27518        if (! $PrevWord) {
27519            $PrevWord = $CurWord;
27520            next ;
27521        }
27522        my $j="$PrevWord $CurWord";
27523        $PrevWord = $CurWord;
27524        next if ++$seen{$j} > 2; # first two occurances are significant
27525    	if  ($v = $Spamdb{$j}) {
27526        	push( @t, $v );
27527    	} else {
27528        	push( @t, $v ) if $v = $Starterdb{$j};
27529		}
27530    }
27531    return \@t,\%got;
27532}
27533
27534
27535
27536sub BayesWordClean {
27537    my $word = lc(shift);
27538    no warnings qw(utf8);
27539    Encode::_utf8_on($word);
27540    $word = substr($word,0,length($word));
27541    return unless $word;
27542    eval{
27543    $word =~ s/#(?:[a-f0-9]{2})+/randcolor/go;
27544    $word =~ s/^#\d+/randdecnum/go;
27545    $word =~ s/[_\[\]\~\@\%\$\&\{\}<>#(),.'";:=!?*+\/\\\-]+$//o;
27546    $word =~ s/^[_\[\]\~\@\%\$\&\{\}<>#(),.'";:=!?*+\/\\\-]+//o;
27547    $word =~ s/!!!+/!!/go;
27548    $word =~ s/\*\*+/**/go;
27549    $word =~ s/--+/-/go;
27550    $word =~ s/__+/_/go;
27551    $word =~ s/[\d,.]{2,}/randnumber/go;
27552    $word =~ s/^[\d:\.\-+();:<>,!"'\/%]+(?:[ap]m)?$/randwildnum/o;    # ignore numbers , dates, times, versions ...
27553    };
27554    my $l;
27555    eval{$l = length($word);};
27556    return if ($l > 20 or $l < 2);
27557    Encode::_utf8_off($word);
27558    return $word;
27559}
27560
27561sub Umlaute {
27562	my $string = shift;
27563	my %umlaute = ("ä" => "ae", "Ä" => "Ae", "ü" => "ue", "Ü" => "Ue", "ö" => "oe", "Ö" => "Oe", "ß" => "ss" );
27564	my $umlautkeys = join ("|", keys(%umlaute));
27565	$string =~ s/($umlautkeys)/$umlaute{$1}/g;
27566	return $string;
27567} ##
27568
27569# attach a header line to the message if the config option is set
27570sub addSpamProb {
27571	my $fh = shift;
27572    my $this           = $Con{$fh};
27573    my $spamprobheader = "";
27574    my $mscore;
27575    return if $this->{spamprobheaderdone};
27576    $this->{spamprobheaderdone} = 1;
27577    return if $NoExternalSpamProb && $this->{relayok};
27578
27579    if ($this->{passingreason}) {
27580        $spamprobheader .= "X-Assp-Passing: $this->{passingreason}\r\n";
27581        $this->{myheader}=~s/X-Assp-Passing:$HeaderValueRe//gios;
27582        # clear out existing X-Assp-Passing headers
27583
27584    }
27585
27586
27587
27588    $this->{myheader}.="X-Assp-Redlisted: Yes ($this->{red})\015\012"
27589        if $this->{red} && $this->{myheader} !~ /X-Assp-Redlisted/o;
27590    $this->{myheader} =~ s/^X-Assp-Spam-Level:$HeaderValueRe//gios;
27591    # clear out existing X-Assp-Spam-Level headers
27592
27593    $this->{saveprepend} = $this->{prepend};
27594
27595    my $counter = 0;
27596    my $stars   = "";
27597    $mscore = $this->{messagescore};
27598	if ($this->{spamfound} && $AddScoringHeader && $this->{messagescore} > 0 && $this->{myheader} !~ /totalscore/) {
27599        $this->{myheader} =~ s/X-Assp-Message-Totalscore:[^\r\n]+?\r\n//iogs;
27600        $this->{myheader} .= "X-Assp-Message-Totalscore: $this->{messagescore}\r\n" if  $this->{myheader} !~ /Totalscore/i;
27601    }
27602
27603    if ((($this->{relayok} && ! $NoExternalSpamProb) || ! $this->{relayok}) && $this->{messagescore} && $AddLevelHeader) {
27604        my $counter=0;
27605        my $stars='';
27606        my $mscore=$this->{messagescore};
27607        $mscore = 100 if $mscore > 100;
27608        while ($counter<int($mscore/5)) {
27609            $counter++;
27610            $stars.= "*";
27611        }
27612        $this->{myheader}=~s/^X-Assp-Spam-Level:$HeaderValueRe//gios; # clear out existing X-Assp-Spam-Level headers
27613        $this->{myheader}.="X-Assp-Spam-Level: $stars\r\n" if $counter && $this->{spamfound};
27614    }
27615
27616
27617    if ( defined( $this->{mailfrom} ) ) {
27618    	$this->{envelopefrom} = "X-Assp-Envelope-From: $this->{mailfrom}\r\n";
27619        $spamprobheader .= "X-Assp-Envelope-From: $this->{mailfrom}\r\n"
27620        	if $spamprobheader !~ /X-Assp-Envelope-From/;
27621
27622        $this->{myheader} =~ s/^X-Assp-Envelope-From:$HeaderValueRe//gios;
27623		 # clear out existing X-Assp-Envelope-From headers
27624    }
27625	my ($to) = $this->{rcpt} =~ /(\S+)/;
27626    my ($mfd) = $to =~ /\@(.*)/;
27627    $this->{newrcpt}="";
27628    foreach my $adr ( split( " ", $this->{rcpt} ) ) {
27629 		$this->{newrcpt} .= "$adr " if $adr =~ /$mfd/;
27630 		last if $AddIntendedForHeader == 1;
27631    }
27632
27633
27634    $spamprobheader .= "X-Assp-Intended-For: $this->{newrcpt}\r\n"
27635        	if $spamprobheader !~ /X-Assp-Intended-For/;
27636	$this->{newrcpt}="";
27637	$this->{myheader} =~ s/^X-Assp-Intended-For:$HeaderValueRe//gios;
27638
27639    # add to our header; merge later, when client sent own headers
27640    $this->{myheader} .= $spamprobheader;
27641
27642
27643}
27644# compile the nonprocessing domains regular expression
27645sub setNPDRE {
27646    my $new = shift;
27647    $new ||= '^(?!)';    # regexp that never matches
27648    $new =~ s/\*/\.\*/g;
27649    SetRE( 'NPDRE', "($new)\$", 'i', 'NoProcessing Domains' );
27650    SetRE( 'NPDRE2', "($new)\$", 'i', 'NoProcessing Domains' );
27651    $NPDRE2 =~ s/\@//g;
27652}
27653
27654# compile the whitelisted domains regular expression
27655sub setWhiteListedDomainsRE {
27656    my $new=shift;
27657    $new||=$neverMatch; # regexp that never matches
27658    SetRE('whiteListedDomainsRE',"(?:$new)\$",
27659          $regexMod,
27660          'Whitelisted Domains',$_[0]);
27661}
27662
27663
27664
27665
27666# compile the whitelisted domains regular expression
27667sub setWLDRE {
27668    my $new = shift;
27669    $new ||= '^(?!)';    # regexp that never matches
27670    $new =~ s/\*/\.\*/g;
27671    SetRE( 'WLDRE', "($new)\$", 'i', 'Whitelisted Domains' );
27672    SetRE( 'WLDRE2', "($new)\$", 'i', 'Whitelisted Domains' );
27673    $WLDRE2 =~ s/\@//g;
27674
27675}
27676# compile the FileScan Responds regular expression
27677sub setFSRESPRE {
27678    my $new=shift;
27679    $new||='^(?!)'; # regexp that never matches
27680    $new=~s/\*/\.\*/g;
27681    SetRE('FSRESPRE',"$new",'i','FileScan Responds');
27682}
27683# compile the blacklisted domains regular expression
27684sub setBLDRE {
27685    my $new=shift;
27686    $new||='^(?!)'; # regexp that never matches
27687    $new=~s/\*/\.\*/g;
27688    SetRE('BLDRE',"(?:$new)\$",
27689
27690          'i',
27691          'Blacklisted Domains',$_[0]);
27692
27693}
27694
27695
27696# compile the regular expression for the list of two&three-level TLDs
27697sub setURIBLCCTLDSRE {
27698    my $s = join( '|', @_ );
27699    $s ||= '^(?!)';      # regexp that never matches
27700    SetRE( 'URIBLCCTLDSRE', "([^\\.]+\\.($s))\$", 'i', 'Country Code TLDs' );
27701
27702}
27703# compile the regular expression for the list of top-level TLDs
27704sub setTLDSRE {
27705
27706$TLDSRE = shift;
27707
27708
27709}
27710# compile the URIBL whitelist regular expression
27711sub setURIBLWLDRE {
27712    my $new = shift;
27713    $new ||= '^(?!)';    # regexp that never matches
27714    $new =~ s/\*/\.\*/g;
27715    SetRE( 'URIBLWLDRE', "^($new)\$", 'i', 'Whitelisted URIBL Domains' );
27716    SetRE( 'URIBLWLDRE2', "^($new)\$", 'i', 'Whitelisted URIBL Domains' );
27717    $URIBLWLDRE2 =~ s/\@//g;
27718}
27719
27720# compile the Max IP/Domain whitelist regular expression
27721sub setIPDWLDRE {
27722    my $new = shift;
27723    $new ||= '^(?!)';    # regexp that never matches
27724    $new =~ s/\*/\.\*/g;
27725    SetRE( 'IPDWLDRE', "^($new)", 'i', 'Whitelisted IP/Domain Domains' );
27726}
27727
27728
27729# see if the address in the mailfrom is on the whitelist meanwhile update the whitelist if that seems appropriate
27730
27731
27732sub onwhitelist {
27733    my ( $fh, $header) = @_;
27734    d('onwhitelist');
27735    my $this = $Con{$fh};
27736    my $adr = lc $this->{mailfrom};
27737
27738
27739    my $whitelisted = $this->{relayok};
27740    $Stats{locals}++ if $whitelisted;
27741    return $whitelisted
27742      unless $adr;
27743      # don't add to the whitelist unless there's a valid envelope
27744      #- prevent bounced mail from adding to the whitelist
27745    if (  !$this->{red}
27746        && $redRe
27747        && $redReRE != ""
27748        && $this->{header} =~ /($redReRE)/ ) {
27749		$this->{red} = ($1||$2);
27750        mlogRe( $fh, $this->{red}, "Red" ,1);
27751      }
27752    mlogRe( $fh, $adr, "Redlist" ,1) if !$this->{red} && $Redlist{$adr};
27753    $this->{red} = "$adr in Redlist" if $Redlist{$adr};
27754
27755    my %senders;
27756    unless ($whitelisted) {
27757        $senders{$adr} = 1;
27758        if ( !$NotGreedyWhitelist ) {
27759            while ( $header =~ /\n(from|sender|reply-to|errors-to|list-\w+):.*?($EmailAdrRe\@$EmailDomainRe)/igo ) {
27760                my $s = $2;
27761
27762                $senders{lc $s}=1;
27763              }
27764          }
27765        foreach my $adr ( keys %senders ) {
27766
27767            return 0 if $adr && $Redlist{$adr};
27768            next if localmail($adr) || $adr eq '';
27769            if ( $whiteListedDomains &&  $adr=~/($WLDRE)/) {
27770
27771                d("wld");
27772                $whitelisted = 1;
27773                mlog( $fh, "Whitelisted Domain: $1",1 );
27774                $this->{passingreason} = "Whitelisted Domain: $1" if !$this->{passingreason};
27775                pbWhiteAdd( $fh, $this->{ip}, "WhiteDomains" );
27776                $this->{whitelisted}="Whitelisted Domain: $1";
27777                last;
27778              } elsif ( $Whitelist{$adr} ) {
27779                d("on whitelist");
27780                $this->{passingreason} = "$adr on whitelistdb" if !$this->{passingreason};
27781                $whitelisted = 1;
27782                $this->{whitelisted} = "$adr on whitelistdb";
27783                pbWhiteAdd( $fh, $this->{ip}, "Whitelist" );
27784                last;
27785              }
27786          }
27787        $this->{senders} = join( ' ', keys %senders ) . " ";    # used for finding blacklisted domains
27788        if ($whitelisted || $this->{whitelisted}) {
27789            $Stats{whites}++;
27790          }
27791
27792      }
27793
27794    # don't add to whitelist if sender is redlisted
27795
27796    return $whitelisted
27797      if $this->{red}
27798          || $WhitelistLocalOnly && !$this->{relayok}
27799          || $WhitelistLocalFromOnly && !localmail( $this->{mailfrom} );
27800    if ($whitelisted) {
27801
27802        # keep the whitelist up-to-date
27803
27804        my %adr = %senders;
27805        my $t = time;
27806        $adr{$adr} = 1;
27807        $header =~ s/\n\s+/ /g;
27808        while ( $header =~ /\n(to|cc): (.*)/ig ) {
27809            my $adr = $2;
27810            while ( $adr =~ /($EmailAdrRe\@$EmailDomainRe)/go ) {
27811                $adr{ lc $1 } = 1;
27812              }
27813          }
27814        foreach my $adr ( split( ' ', lc $this->{rcpt} ) ) {
27815            $adr{$adr} = 1;
27816          }
27817        foreach my $adr ( keys %adr ) {
27818
27819            next if localmail($adr) || !$adr;
27820            next if $Redlist{$adr};             # don't add to whitelist if rcpt is redlisted
27821
27822            next if $adr =~ s/^\'//;
27823			next if length($adr) > 127;
27824            #next if $whiteListedDomains && $adr=~$WLDRE;
27825
27826           mlog( $fh, "auto whitelist addition: $adr", 1 )
27827              unless $Whitelist{$adr} || $NoAutoWhite;
27828          $Whitelist{$adr} = $t unless !$Whitelist{$adr} && $NoAutoWhite;
27829          $this->{whitepassed} = 1;
27830
27831          }
27832        return 1;
27833      }
27834      return 0;
27835}
27836
27837
27838
27839#Email::MIME substitution for mixed alternative multipart messages
27840sub parts_multipart {
27841  my $self     = shift;
27842
27843  #use the original code, if don't need the hack
27844  return $org_Email_MIME_parts_multipart->($self) if $o_EMM_pm;
27845
27846  my $boundary = $self->{ct}->{attributes}->{boundary};
27847
27848  return $self->parts_single_part
27849    unless $boundary and $self->body_raw =~ /^--\Q$boundary\E\s*$/sm;
27850
27851  $self->{body_raw} = $self->body_raw;
27852
27853  # rfc1521 7.2.1
27854  my ($body, $epilogue) = split /^--\Q$boundary\E--\s*$/sm, $self->body_raw, 2;
27855
27856  my @bits = split /^--[^\n\r]+\s*$/sm, ($body || '');
27857
27858  $self->{body} = undef;
27859  $self->{body} = (\shift @bits) if ($bits[0] || '') !~ /.*:.*/;
27860
27861  my $bits = @bits;
27862
27863  my @parts;
27864  for my $bit (@bits) {
27865    $bit =~ s/\A[\n\r]+//smg;
27866    my $email = (ref $self)->new($bit);
27867    push @parts, $email;
27868  }
27869
27870  $self->{parts} = \@parts;
27871
27872  return @{ $self->{parts} };
27873}
27874
27875sub cleanMIMEHeader2UTF8 {
27876    my ($m , $noconvert) = @_;
27877    my $msg = ref($m) ? $$m : $m;
27878    $msg =~ s/([^\x0D])\x0A/$1\x0D\x0A/go;
27879    my $hl = index($msg,"\x0D\x0A\x0D\x0A");
27880    if ($hl > 0) {
27881        $msg = substr($msg,0,$hl);
27882        $msg = decodeMimeWords2UTF8($msg) if ! $noconvert;
27883        $msg .= "\x0D\x0A\x0D\x0A";
27884        return $msg;
27885    } elsif ($hl == 0) {
27886        return "\x0D\x0A\x0D\x0A";
27887    }
27888    return;
27889}
27890
27891sub cleanMIMEBody2UTF8 {
27892    my $m = shift;
27893    my $msg = ref($m) ? $$m : $m;
27894    $msg =~ s/([^\x0D])\x0A/$1\x0D\x0A/go;
27895    my $body;
27896    my %cs;
27897
27898    eval {
27899        local $SIG{ALRM} = sub { die "__alarm__\n"; };
27900        alarm(15);
27901        $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
27902        my $email = Email::MIME->new($msg);
27903        foreach my $part ( $email->parts ) {
27904            my $cs;
27905            my $dis = $part->header("Content-Type") || '';
27906            next if $part->header("Content-ID") && $dis !~ /text/o;    # no inline images / app's
27907            my $attrs = $dis =~ s/^.*?;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
27908            my $name =    $attrs->{name}
27909                       || $part->{ct}{attributes}{name}
27910                       || $attrs->{filename}
27911                       || $part->{ct}{attributes}{filename}
27912                       || $part->filename;
27913            $cs = $attrs->{charset} || $part->{ct}{attributes}{charset};
27914            $cs{uc $cs} = "charset=$cs" if $cs;
27915            if (! $name && ! $addCharsets) {
27916                $dis = $part->header("Content-Disposition") || '';
27917                $attrs = $dis =~ s/^.*?;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
27918                $name =    $attrs->{name}
27919                        || $part->{ct}{attributes}{name}
27920                        || $attrs->{filename}
27921                        || $part->{ct}{attributes}{filename};
27922            }
27923
27924            my $bd;
27925            if (! $addCharsets) {
27926                $bd = $name ? "\r\nattachment:$name\r\n" : $part->body;
27927            }
27928            if ($bd && $cs && ! $addCharsets) {
27929                $bd = Encode::decode($cs, $bd);
27930                $bd = Encode::encode('UTF-8', $bd);
27931            }
27932            $body .= $bd;
27933        }
27934        if ($addCharsets) {
27935            my @mime_coded;
27936            eval {@mime_coded = $msg =~ /=\?([a-zA-Z0-9\-]{2,20})\?[bq]\?/iog;
27937                  map {$cs{uc $_} = "charset=$_" if $_;} @mime_coded;
27938                 };
27939        }
27940        if (! $body) {
27941            my $dis = $email->header("Content-Type") || '';
27942            my $attrs = $dis =~ s/^.*?;//o ? Email::MIME::ContentType::_parse_attributes($dis) : {};
27943            my $cs = $attrs->{charset} || $email->{ct}{attributes}{charset};
27944            $cs{uc $cs} = "charset=$cs" if $cs;
27945            $body = $email->body unless $addCharsets;
27946            if ($body && $cs && ! $addCharsets) {
27947                $body = Encode::decode($cs, $body);
27948                $body = Encode::encode('UTF-8', $body);
27949            }
27950        }
27951        $body = join("\n", values %cs)."\n" if scalar keys %cs && $addCharsets;
27952        alarm 0;
27953        1;
27954    } and alarm 0 if $CanUseEMM;
27955
27956    return $body;
27957}
27958
27959
27960sub cleanMIME {
27961    my $msg = shift;
27962    my $body;
27963
27964    eval {
27965        $Email::MIME::ContentType::STRICT_PARAMS=0;      # no output about invalid CT
27966        my $email=Email::MIME->new($msg);
27967        foreach my $part ( $email->parts ) {
27968            my $dis = $part->header("Content-Type") || '';
27969            my $attrs = $dis =~ s/^.*?;// ? Email::MIME::ContentType::_parse_attributes($dis) : {};
27970            my $name = $attrs->{name} || $part->{ct}{attributes}{name};
27971            $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
27972            eval{$name ||= $part->filename;};
27973            if (! $name) {
27974              eval{
27975                $dis = $part->header("Content-Disposition") || '';
27976                $attrs = $dis =~ s/^.*?;// ? Email::MIME::ContentType::_parse_attributes($dis) : {};
27977                $name = $attrs->{name} || $part->{ct}{attributes}{name};
27978                $name ||= $attrs->{filename} || $part->{ct}{attributes}{filename};
27979              };
27980            }
27981            next if $name;   # skip attachments
27982            $body .= eval{&decHTMLent($part->body);};
27983        }
27984        if (! $body) {
27985            $body = eval{&decHTMLent($email->body);};
27986        }
27987    } if $CanUseEMM;
27988    return $body;
27989}
27990
27991# clean up source email
27992
27993sub clean {
27994    my $m = shift;
27995
27996    my $msg = ref($m) ? $$m : $m;
27997    my $t = time + 60;     # max 60 seconds for this cleaning
27998    my $body;
27999    my $header;
28000    my $undec = 1;
28001
28002    $body = cleanMIMEBody2UTF8(\$msg);
28003
28004    if ($body) {
28005        $header = cleanMIMEHeader2UTF8(\$msg,0);
28006        headerUnwrap($header);
28007        $undec = 0;
28008    }
28009
28010        local $_= "\n". (($header) ? $header : $msg);
28011    my ($helo,$rcpt);
28012    if ($header) {
28013        ($helo)=/helo=([^)]+)\)/io;
28014        $helo = substr($helo,0,19); # if the helo string is long, break it up
28015        my (@sender,@receipt,$sub);
28016        while (/($HeaderNameRe):($HeaderValueRe)/igos) {
28017            my($head,$val) = ($1,$2);
28018            next if $head =~ /^(?:x-assp|(?:DKIM|DomainKey)-Signature)|X-Original-Authentication-Results/oi;
28019            if ($head =~ /^(to|cc|bcc)$/io) {
28020                push @receipt, $1 while ($val =~ /($EmailAdrRe\@$EmailDomainRe)/gio);
28021            }
28022            if ($head =~ /^(?:from|ReturnReceipt|Return-Receipt-To|Disposition-Notification-To|Return-Path|Reply-To|Sender|Errors-To|List-\w+)/io) {
28023                push @sender, $1 while ($val =~ /($EmailAdrRe\@$EmailDomainRe)/gio);
28024            }
28025            if ($head =~ /^(subject)$/io) {
28026                $sub = fixsub($val);
28027            }
28028        }
28029        $rcpt = ' rcpt ' . join(' rcpt ',@receipt) if scalar @receipt;
28030        $rcpt .= ' sender ' . join(' sender ',@sender) if scalar @sender;
28031        # mark the subject
28032        $rcpt .= "\n".$sub if $sub;
28033        return "helo: $helo\n$rcpt\n",0 if (time > $t);
28034    }
28035
28036    # from now only do the body if possible
28037    local $_ = $body if $body;
28038
28039    # replace HTML encoding
28040    s/&amp;?/and/gio;
28041    $_ = decHTMLent($_);
28042    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28043
28044
28045    if ($undec) {
28046      # replace base64 encoding
28047      s/\n([a-zA-Z0-9+\/=]{40,}\r?\n[a-zA-Z0-9+\/=\r\n]+)/base64decode($1)/gseo;
28048
28049      # clean up quoted-printable references
28050      s/(Subject: .*)=\r?\n/$1\n/o;
28051      s/=\r?\n//go;
28052      # strip out mime continuation
28053      s/.*---=_NextPart_.*\n//go;
28054      return "helo: $helo\n$rcpt\n",0 if (time > $t);
28055    }
28056
28057    # clean up MIME quoted-printable line breakings
28058    s/=\r?\n//gos;
28059
28060    # clean up &nbsp; and &amp;
28061    s/(\d),(\d)/$1$2/go;
28062    s/\r//go; s/ *\n/\n/go;
28063    s/\n\n\n\n\n+/\nblines blines\n/go;
28064    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28065
28066    # clean up html stuff
28067    s/<\s*(head)\s*>.*?<\/\s*\1\s*>//igos;
28068    s/<\s*(title|h\d)\s*>(.*?)<\/\s*\1\s*>/fixsub($2)/igse;
28069    s/<\s*((?:no)?script)[^>]+>.*?<\s*\/\s*\1\s*>/ jscripttag /igs;
28070    s/<\s*(?:no)?script[^>]+>/ jscripttag /igos;
28071    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28072    # remove style sheets
28073    s/<\s*(style|select)[^>]*>(.*?)<\s*\/\s*\1\s*>/$2/igs;
28074    # remove comments
28075    s/(?:<!--.*?-->|<([^>\s]+)[^>]*\s+style=['"]?[^>'"]*(?:display:\s*none|visibility:\s*hidden)[^>'"]*['"]?[^>]*>.*?<\/\1\s*>)//igs;
28076    s/<\s*!\s*[A-Za-z].*?>//igso;
28077    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28078
28079    s/<\s*(?:[biu]|strong)\s*>/ boldifytext /gio;
28080    # remove some tags that are not informative
28081    s/<\s*\/?\s*(?:p|br|div|t[drh]|li|dd|[duo]l|center)[^>]*>/\n/gios;
28082    s/<\s*\/?\s*(?:[biuo]|strong)\s*>//gio;
28083    s/<\s*\/?\s*(?:html|meta|head|body|span|table|font|col|map)[^>]*>//igos;
28084    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28085
28086    # look for linked images
28087    s/(<\s*a[^>]*>[^<]*<\s*img)/ linkedimage $1/giso;
28088    s/<[^>]*href\s*=\s*("[^"]*"|\S*)/fixhref($1)/isgeo;
28089    s/(<\s*a\s[^>]*>)(.*?)(<\s*\/a\s*>)/$1.fixlinktext($2)/igseo;
28090
28091    s/(?:ht|f)tps?:\/\/(\S*)/fixhref($1)/isgeo;
28092    return "helo: $helo\n$rcpt\n",0 if (time > $t);
28093
28094    s/(\S+\@\S+\.\w{2,5})\b/fixhref($1)/geo;
28095    s/<?\s*img .{0,50}src\s*=\s*['"]([^'"]*)['"][^>]+>/$1/gois;
28096    s/["']\s*\/?s*>|target\s*=\s*['"]_blank['"]|<\s*\/|:\/\/ //go;
28097    s/ \d{2,} / 1234 /go;
28098    $_ = &decHTMLent($_);
28099
28100    return ("helo: $helo\n$rcpt\n$_",1);
28101
28102}
28103
28104
28105sub fixhref { my $t = shift; $t =~ s/(\w+)/ href $1 /g; $t; }
28106
28107sub fixlinktext { my $t = shift; $t =~ s/(\w+)/atxt $1/g; $t; }
28108
28109sub fixurl {
28110    my $u = shift;
28111    $u =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack('C',hex($1))/ge;
28112    $u;
28113}
28114
28115sub fixsub {
28116    my $s = shift;
28117    $s =~ s/ {3,}/ lotsaspaces /g;
28118    $s =~ s/(\S+)/ssub $1/g;
28119    "\n$s ssub";
28120}
28121
28122
28123sub base64decode {
28124    my $str = shift;
28125    my $res;
28126    $str =~ tr|A-Za-z0-9+/||cd;
28127    $str =~ tr|A-Za-z0-9+/| -_|;
28128    while ( $str =~ /(.{1,60})/gs ) {
28129        my $len = chr( 32 + length($1) * 3 / 4 );
28130        $res .= unpack( "u", $len . $1 );
28131    }
28132    $res;
28133}
28134
28135sub formatMethod {
28136    my $res;
28137    if ( $_[2] == 0 ) {
28138        $res = int( $_[0] / $_[1] );
28139        $_[0] -= $res * $_[1];    # modulus on floats
28140    } elsif ( $_[2] == 1 ) {
28141        if ( $_[0] >= $_[1] ) {
28142            $res = sprintf( "%.1f", $_[0] / $_[1] );
28143            $_[0] = 0;
28144        }
28145    }
28146    return $res;
28147}
28148
28149sub formatNumDataSize {
28150    my $size = shift;
28151    my $res;
28152    if ($size >= 1099511627776) {
28153        $res = sprintf("%.2f TByte", $size / 1099511627776);
28154    } elsif ($size >= 1073741824) {
28155        $res = sprintf("%.2f GBbyte", $size / 1073741824);
28156    } elsif ($size >= 1048576) {
28157        $res = sprintf("%.2f MByte", $size / 1048576);
28158    } elsif ($size >= 1024) {
28159        $res = sprintf("%.2f kByte", $size / 1024);
28160    } else {
28161        $res = $size . ' Byte';
28162    }
28163    return $res;
28164}
28165
28166sub formatDataSize {
28167    my ( $size, $method ) = @_;
28168    my ( $res, $s );
28169    $res .= $s . 'TB ' if $s = formatMethod( $size, 1099511627776, $method );
28170    $res .= $s . 'GB ' if $s = formatMethod( $size, 1073741824,    $method );
28171    $res .= $s . 'MB ' if $s = formatMethod( $size, 1048576,       $method );
28172    $res .= $s . 'kB ' if $s = formatMethod( $size, 1024,          $method );
28173    if ( $size || !defined $res ) {
28174
28175        if ( $method == 0 ) {
28176            $res .= $size . 'B ';
28177        } elsif ( $method == 1 ) {
28178            $res .= sprintf( "%.1fB ", $size );
28179        }
28180    }
28181    $res =~ s/\s$//;
28182    return $res;
28183}
28184
28185sub unformatTimeInterval {
28186 my ($interval,$default)=@_;
28187 my @a=split(' ',$interval);
28188 my $res=0;
28189 while (@a) {
28190  my $j = shift @a;
28191  my ($i,$mult)=$j=~/^(.*?) ?([smhd]?)$/o;
28192  $mult||=$default||'s'; # default to seconds
28193  if ($mult eq 's') {
28194   $res+=$i;
28195  } elsif ($mult eq 'm') {
28196   $res+=$i*60;
28197  } elsif ($mult eq 'h') {
28198   $res+=$i*3600;
28199  } elsif ($mult eq 'd') {
28200   $res+=$i*86400;
28201  }
28202 }
28203 return $res;
28204}
28205
28206
28207sub unformatDataSize {
28208 my ($size,$default)=@_;
28209 my @a=split(' ',$size);
28210 my $res=0;
28211 while (@a) {
28212  my $j = shift @a;
28213  my ($s,$mult)=$j=~/^(.*?) ?(B|kB|MB|GB|TB)?$/o;
28214  $mult||=$default||'B'; # default to bytes
28215  if ($mult eq 'B') {
28216   $res+=$s;
28217  } elsif ($mult eq 'kB') {
28218   $res+=$s*1024;
28219  } elsif ($mult eq 'MB') {
28220   $res+=$s*1048576;
28221  } elsif ($mult eq 'GB') {
28222   $res+=$s*1073741824;
28223  } elsif ($mult eq 'TB') {
28224   $res+=$s*1099511627776;
28225  }
28226 }
28227 return $res;
28228}
28229
28230sub decodeMimeWord {
28231    my ($fulltext,$charset,$encoding,$text)=@_;
28232    my $ret;
28233
28234    eval {$charset = Encode::resolve_alias(uc($charset));} if $charset;
28235
28236    if (!$@ && $CanUseEMM && $charset && $decodeMIME2UTF8 ) {
28237        eval{$ret = MIME::Words::decode_mimewords($fulltext)} if $fulltext;
28238        return $ret unless $@;
28239    }
28240
28241    if (lc $encoding eq 'b') {
28242        $text=base64decode($text);
28243    } elsif (lc $encoding eq 'q') {
28244        $text=~s/_/\x20/g; # RFC 1522, Q rule 2
28245        $text=~s/=([\da-fA-F]{2})/pack('C', hex($1))/ge; # RFC 1522, Q rule 1
28246    };
28247    return $text;
28248}
28249
28250sub decodeMimeWords {
28251    my $s = shift;
28252    headerUnwrap($s);
28253    $s =~ s/(=\?([^?]+)\?(b|q)\?([^?]*)\?=)/decodeMimeWord($1,$2,$3,$4)/gieo;
28254    return $s;
28255}
28256
28257
28258sub dedecodeMimeWord {
28259    my ($fulltext,$charset,$encoding,$text)=@_;
28260    return decodeMimeWord($fulltext,$charset,$encoding,$text) unless $LogCharset;
28261    my $ret;
28262
28263    eval {$charset = Encode::resolve_alias(uc($charset));} if $charset;
28264
28265    if (! $@ && $CanUseEMM && $charset && $decodeMIME2UTF8 ) {
28266        eval{$ret = MIME::Words::decode_mimewords($fulltext)} if $fulltext;
28267        if ($LogCharset && $ret) {
28268            eval{
28269                 $ret = Encode::decode($charset, $ret);
28270                 $ret = Encode::encode($LogCharset, $ret);
28271                 Encode::_utf8_on($ret) if $LogCharset =~ /^utf-?8/io;
28272            };
28273        }
28274        return $ret unless $@;
28275        $@ = undef;
28276    }
28277
28278    if (lc $encoding eq 'b') {
28279        $text=base64decode($text);
28280    } elsif (lc $encoding eq 'q') {
28281        $text=~s/_/\x20/go; # RFC 1522, Q rule 2
28282        $text=~s/=([\da-fA-F]{2})/pack('C', hex($1))/geo; # RFC 1522, Q rule 1
28283    }
28284    if (! $@ && $charset && $LogCharset && $text) {
28285        eval{
28286             $text = Encode::decode($charset, $text);
28287             $text = Encode::encode($LogCharset, $text);
28288             Encode::_utf8_on($text) if $LogCharset =~ /^utf-?8/io;
28289        };
28290    }
28291    return $text;
28292}
28293
28294
28295
28296sub dedecodeMimeWords {
28297    my $s = shift;
28298    return decodeMimeWords($s) unless $LogCharset;
28299    headerUnwrap($s);
28300    $s =~ s/(=\?([^?]*)\?(b|q)\?([^?]+)\?=)/dedecodeMimeWord($1,$2,$3,$4)/gieo;
28301    return $s;
28302}
28303
28304sub assp_encode_Q {
28305    my $str = shift;
28306    my $out;
28307    eval {$out = MIME::QuotedPrint::encode($str,'');}
28308      or do {mlog(0,"info: unable to encode string to quoted-printable, will try base64 - $@");};
28309    return $out;
28310}
28311
28312sub assp_encode_B {
28313    my $str = shift;
28314    my $out;
28315    eval {$out = MIME::Base64::encode_base64($str, '');1;}
28316      or do {mlog(0,"warning: unable to encode string to base64 - $@");};
28317    return $out;
28318}
28319
28320sub encodeMimeWord {
28321    my $word = shift;
28322    return '' unless $word;
28323    my $encoding = uc(shift || 'Q');
28324    my $charset  = uc(shift || 'UTF-8');
28325    use bytes;
28326    my $encfunc  = (($encoding eq 'Q') ? \&assp_encode_Q : \&assp_encode_B);
28327    my $encword = &$encfunc($word);
28328    if ($word && ! $encword && $encoding eq 'Q') {
28329        $encword = &assp_encode_B($word);
28330        $encoding = 'B';
28331    }
28332    return "=?$charset?$encoding?" . $encword . "?=";
28333}
28334
28335
28336
28337sub downloadGripConf {
28338    return if $AsASecondary;
28339    d('downloadGripConf-start');
28340
28341    my $ret;
28342    my $file = "$base/griplist.conf";
28343    $ret = downloadHTTP("http://downloads.sourceforge.net/project/assp/griplist/griplist.conf",
28344                 $file,
28345                 0,
28346                 "griplist.conf",5,9,2,1);
28347    mlog(0,"info: updated GRIPLIST upload and download URL's in $file") if $ret;
28348    $ret = 0;
28349    open my $GC , '<', $file or return 0;
28350    binmode $GC;
28351    while (<$GC>) {
28352        s/\r|\n//o;
28353        if (/^\s*(gripList(?:DownUrl|UpUrl|UpHost))\s*:\s*(.+)$/) {
28354            ${$1} = $2;
28355            $ret++;
28356        }
28357    }
28358    close
28359    mlog(0,"info: loaded GRIPLIST upload and download URL's from $file") if $ret;
28360    mlog(0,"info: GRIPLIST config $file is possibly incomplete") if $ret < 3;
28361    $gripListDownUrl =~ s/\*HOST\*/$gripListUpHost/o;
28362    $gripListUpUrl  =~ s/\*HOST\*/$gripListUpHost/o;
28363    return $ret;
28364}
28365sub downloadGrip {
28366    my $noskip = shift;
28367    return if $AsASecondary;
28368    d('griplistdownload-start');
28369
28370    &mlog(0,"Griplist file not configured")  if (!$griplist);
28371    return if (!$griplist);
28372
28373    my $rc;
28374	&downloadGripConf();  # reload the griplist.conf
28375    my $gripListUrl = $gripListDownUrl;
28376    my $gripFile    = "$base/$griplist";
28377    my $gripListDownUrlAdd;
28378	my $dltime = time;
28379    ## let's check if we really need to
28380    if (-e $gripFile && !$noskip) {
28381        my @s     = stat($gripFile);
28382        my $mtime = $s[9];
28383        my $random = int(rand(48-24)+24)+1;
28384        if (time - $mtime < $random*60*60) {
28385            return;
28386        }
28387    }
28388
28389    # check for previous download timestamp, so we can do delta now
28390    my %lastdownload;
28391    $lastdownload{full} = 0;
28392    $lastdownload{fullUTC} = 0;
28393    $lastdownload{delta} = 0;
28394    $lastdownload{deltaUTC} = 0;
28395    my $delta = "";
28396    if (open(my $UTC, "<","$gripFile.utc")) {
28397        local $/;
28398        my $buf = <UTC>;
28399        close($UTC);
28400        chop($buf);
28401        if ($buf =~ /full/ && $buf =~ /delta/) {
28402            %lastdownload = split(/\s+|\n/, $buf);
28403        } else {
28404            $lastdownload{delta} = $buf;
28405        }
28406        if (! ($DoFullGripDownload && time - $lastdownload{fullUTC} > $DoFullGripDownload*24*60*60)) {
28407            my $lasttime;
28408            $lasttime = $lastdownload{full};
28409            $lasttime = $lastdownload{delta} if ($lastdownload{delta} > $lastdownload{full});
28410            $gripListDownUrlAdd = "&delta=$lasttime";
28411            $delta = " (delta)";
28412        }
28413    }
28414
28415
28416
28417    if (open(my $TEMPFILE, ">", "$gripFile.tmp")) {
28418        #we can create the file, this is good, now close the file and keep going.
28419        close $TEMPFILE;
28420        unlink("$gripFile.tmp");
28421    } else {
28422        &mlog(0,"Griplist download failed: Cannot create $gripFile.tmp");
28423        return;
28424    }
28425
28426
28427	my $gripListDownUrlL = $gripListDownUrl . $gripListDownUrlAdd;
28428    my $ret = downloadHTTP($gripListDownUrlL,
28429        "$gripFile.tmp",
28430        \$NextGriplistDownload,
28431        "Griplist$delta",5,9,2,1);
28432
28433    # download complete
28434    my $filesize = -s "$gripFile.tmp";
28435    &mlog(0,"Griplist download complete: binary download $filesize bytes");
28436
28437    # enough data?
28438    if ($filesize < 12) {
28439        &mlog(0,"Griplist download error: grip data too small");
28440        unlink("$gripFile.tmp");
28441        return;
28442    }
28443
28444    # record download time so we can do delta next time
28445    unlink("$gripFile.utc");
28446    if (open(my $UTC, ">","$gripFile.utc")) {
28447        my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = gmtime($dltime);
28448        $year += 1900;
28449        $mon += 1;
28450        if (! $delta) {
28451            $lastdownload{full} = sprintf "%04d%02d%02d%02d%02d%02d", $year, $mon, $day, $hour, $min, $sec;
28452            $lastdownload{fullUTC} = $dltime;
28453        } else {
28454            $lastdownload{delta} = sprintf "%04d%02d%02d%02d%02d%02d", $year, $mon, $day, $hour, $min, $sec;
28455            $lastdownload{deltaUTC} = $dltime;
28456        }
28457        printf $UTC "full\t%s\n", $lastdownload{full};
28458        printf $UTC "fullUTC\t%s\n", $lastdownload{fullUTC};
28459        printf $UTC "delta\t%s\n", $lastdownload{delta};
28460        printf $UTC "deltaUTC\t%s\n", $lastdownload{deltaUTC};
28461        close($UTC);
28462    }
28463
28464    # if we did a delta download, read in previous data so we can merge
28465    my @binFiles;
28466    push(@binFiles, "$gripFile.bin") if ($gripListUrl =~ /delta=/);
28467    push(@binFiles, "$gripFile.tmp");
28468
28469    # convert binary download form to text form used by ASSP
28470    my $buf;
28471    my %grip;
28472    my $action = "read";
28473    foreach my $binF (@binFiles) {
28474        my $binSize = -s $binF;
28475        open(my $BIN,"<", $binF);
28476        binmode($BIN);
28477        read($BIN, $buf, $binSize);
28478        close($BIN);
28479
28480    # IPv6 count
28481    	my ($n6h, $n6l) = unpack("N2", $buf);
28482    	my $n6 = $n6h * 2**32 + $n6l;
28483
28484    # IPv4 count
28485    	my $n4;
28486    	eval { $n4 = unpack("x[N2] N", $buf); };
28487
28488
28489    # decode IPv6 data
28490    	my $x6 = 0;
28491    	eval {
28492    	for (my $i = 0; $i < $n6; $i++) {
28493        my ($bip, $grey) = unpack("x[N2] x[N] x$x6 a8 C", $buf);
28494        my $ip = join(":", unpack("H4H4H4H4", $bip)) . ":";
28495        $ip =~ s/:0+([0-9a-f])/:$1/gio;
28496        $ip =~ s/:0:$/::/o;
28497
28498        #                $grip{$ip} = $grey / 255;
28499        #                $gripdelta{$ip} = $grey / 255 if $deltayonly;
28500        $x6 += 9;
28501    	}
28502    	};
28503
28504    # decode IPv4 data
28505    	my $x4 = 0;
28506    	for (my $i = 0; $i < $n4; $i++) {
28507        my ($bip, $grey) = unpack("x[N2] x[N] x$x6 x$x4 a3 C", $buf);
28508        my $ip = join(".", unpack("C3", $bip));
28509        $grip{$ip} = $grey / 255;
28510
28511        $x4 += 4;
28512    }
28513        &mlog(0,"Griplist binary $action OK: $binF, $n6 IPv6 addresses, $n4 IPv4 addresses");
28514        $action = "merge";
28515    }
28516
28517    # remove download file
28518    unlink("$gripFile.tmp");
28519
28520    # output binary version, so we can do a delta next time
28521    &mlog(0,"Writing merged Griplist binary...");
28522    my $buf;
28523    my $n6 = 0;
28524    my $n4 = 0;
28525    my ($buf6, $buf4);
28526    foreach my $ip (keys %grip) {
28527        if ($ip =~ /:/) {
28528            my $ip2 = $ip;
28529            $ip2 =~ s/([0-9a-f]*):/0000$1:/gi;
28530            $ip2 =~ s/0*([0-9a-f]{4}):/$1:/gi;
28531            $buf6 .= pack("H4H4H4H4", split(/:/, $ip2));
28532            $buf6 .= pack("C", int($grip{$ip} * 255));
28533            $n6++;
28534        } else {
28535            $buf4 .= pack("C3C", split(/\./, $ip), int($grip{$ip} * 255));
28536            $n4++;
28537        }
28538    }
28539    $buf = pack("N2", $n6/2**32, $n6);
28540    $buf .= pack("N", $n4);
28541    $buf .= $buf6 . $buf4;
28542    unlink("$gripFile.bin");
28543    open (my $BIN, ">", "$gripFile.bin");
28544    binmode($BIN);
28545    print $BIN $buf;
28546    close($BIN);
28547    chmod 0644, "$gripFile.bin";
28548
28549
28550    # output text version
28551    &mlog(0,"Writing merged Griplist text...");
28552    unlink("$gripFile");
28553    open (my $TEXT, ">","$gripFile");
28554    binmode($TEXT);
28555    print $TEXT "\n";
28556    foreach my $ip (sort keys %grip) {
28557
28558        printf $TEXT "$ip\002%.2f\n", $grip{$ip};
28559    }
28560    close($TEXT);
28561    chmod 0644, "$gripFile";
28562
28563    &mlog(0,"Griplist writing complete: $n6 IPv6 addresses, $n4 IPv4 addresses");
28564}
28565
28566sub downloadDropList {
28567    d('droplistdownload-start');
28568    my $ret;
28569
28570    my ($file) = $droplist =~ /^ *file: *(.+)/io;
28571    $ret = downloadHTTP("http://www.spamhaus.org/drop/drop.lasso",
28572                 "$base/$file.tmp",
28573                 \$NextDroplistDownload,
28574                 "Droplist",5,9,2,1) if $file;
28575    if ($ret) {
28576        open (my $F, '<' , "$base/$file.tmp") or return;
28577        my $firstline = <$F>;
28578        close $F;
28579        if ($firstline =~ /^\s*;\s*Spamhaus\s+DROP\s+List/io ) {
28580            unlink "$base/$file";
28581            copy("$base/$file.tmp","$base/$file");
28582        } else {
28583            mlog(0,"warning: the file $droplist was downloaded but contains no usable data - ignoring the download");
28584            return;
28585        }
28586        $ConfigChanged = 1;         # tell all to reload Config
28587    }
28588    return $ret;
28589}
28590
28591sub downloadStarterDB {
28592    d('downloadStarterDB-start');
28593	my $file = "$base/starterdb/spamdb.gz";
28594	mlog(0,"info: starting download $file");
28595
28596    	my $ret = downloadHTTP("http://sourceforge.net/projects/assp/files/ASSP%20Installation/Spam%20Collection/spamdb.gz",
28597                 "$file",
28598                 \$NextDroplistDownload,
28599                 "spamdb.gz",5,9,2,1);
28600    	return 0 unless $ret;
28601    	unlink ("$base/starterdb/spamdb");
28602        if (unzipgz("$base/starterdb/spamdb.gz", "$base/starterdb/spamdb")) {
28603        	mlog(0,"info:  current starterdb '$base/starterdb/spamdb' available ") if $MaintenanceLog;
28604    	} else {
28605        	mlog(0,"warning:  unable to unzipgz '$base/starterdb/spamdb.gz' to '$base/starterdb/spamdb'");
28606        return 0;
28607    	}
28608
28609
28610    return $ret;
28611}
28612
28613sub downloadTLDList {
28614    d('TLDlistdownload-start');
28615    my $ret;
28616    my $ret2;
28617    my $ret3;
28618    my $n1;
28619    my $n2;
28620    my $n3;
28621    $NextTLDlistDownload = time + 720000;
28622
28623    my ($file) = $TLDS =~ /^ *file: *(.+)/io;
28624    $ret = downloadHTTP(
28625                 $tlds_alpha_URL,
28626                 "$base/$file",
28627                 \$n1,
28628                 "TLDlist",24,48,2,1) if $file;
28629
28630    ($file) = $URIBLCCTLDS =~ /^ *file: *(.+)/io;
28631    $ret2 = downloadHTTP(
28632                 $tlds2_URL,
28633                 "$base/files/URIBLCCTLDS-L2.txt",
28634                 \$n2,
28635                 "level-2-TLDlist",24,48,2,1) if $file;
28636    $ret3 = downloadHTTP(
28637                 $tlds3_URL,
28638                 "$base/files/URIBLCCTLDS-L3.txt",
28639                 \$n3,
28640                 "level-3-TLDlist",24,48,2,1) if $file;
28641
28642    if (! $file) {
28643        if ($n1) {
28644            $NextTLDlistDownload = $n1;
28645            return $ret;
28646        }
28647    }
28648
28649    $NextTLDlistDownload  =  ($n1 && $n1 < $n2) ? $n1 : ($n2 > 0) ? $n2 : $NextTLDlistDownload;
28650    $NextTLDlistDownload  =  $n3 if $n3 &&  $NextTLDlistDownload > $n3;
28651
28652    if ($file &&
28653        -s "$base/files/URIBLCCTLDS-L2.txt" > 0 &&
28654        -s "$base/files/URIBLCCTLDS-L3.txt" > 0 &&
28655        ($ret2 || $ret3 || ! -e "$base/$file" || -s "$base/$file" == 0))
28656    {
28657        if (((open my $f1 ,'<' ,"$base/files/URIBLCCTLDS-L2.txt") || mlog(0,"error: unable to open $base/files/URIBLCCTLDS-L2.txt")&0) &&
28658            ((open my $f2 ,'<' ,"$base/files/URIBLCCTLDS-L3.txt") || mlog(0,"error: unable to open $base/files/URIBLCCTLDS-L3.txt")&0) &&
28659            ((open my $f3 ,'>' ,"$base/$file") || mlog(0,"error: unable to open $base/$file")&0))
28660        {
28661            binmode $f3;
28662            print $f3 "# two level TLDs\n\n";
28663            while (<$f1>) {
28664                s/\r?\n//o;
28665                next unless $_;
28666                print $f3 "$_\n";
28667            }
28668            mlog(0,"info: merged file $base/files/URIBLCCTLDS-L2.txt in to $base/$file for URIBLCCTLDS") if $MaintenanceLog >= 2;
28669            print $f3 "\n\n";
28670            print $f3 "# three level TLDs\n\n";
28671            while (<$f2>) {
28672                s/\r?\n//o;
28673                next unless $_;
28674                print $f3 "$_\n";
28675            }
28676            mlog(0,"info: merged file $base/files/URIBLCCTLDS-L3.txt in to $base/$file for URIBLCCTLDS") if $MaintenanceLog >= 2;
28677            close $f3;
28678            close $f2;
28679            close $f1;
28680            mlog(0,"info: file $base/$file updated for URIBLCCTLDS") if $MaintenanceLog;
28681            $ret2 = 1;
28682        } else {
28683            mlog(0,"error: unable to read or write one of the URIBLCCTLDS files - $!");
28684        }
28685    }
28686
28687    $ConfigChanged = 1 if $ret || $ret2 || $ret3;         # tell all to reload Config
28688    return $ret || $ret2 || $ret3;
28689}
28690
28691
28692sub UpdateDownloadURLs {
28693    if (open my $UVS ,"<", "$base/version.txt") {
28694        while (<$UVS>) {
28695            s/\n|\r//g;
28696            s/^\s+//;
28697            s/\s+$//;
28698            next if /^\s*[#;]/o;
28699            if (/^\s*versionURL\s*:\s*(http(?:s)?:\/\/.+)$/i) {
28700                my $old = $versionURL;
28701                $versionURL = $1;
28702                mlog(0,"autoupdate: version.txt file download URL changed from $old to $versionURL") if $versionURL ne $old;
28703                next;
28704            }
28705            if (/^\s*NewAsspURL\s*:\s*(http(?:s)?:\/\/.+)$/io) {
28706                my $old = $NewAsspURL;
28707                $NewAsspURL = $1;
28708                mlog(0,"autoupdate: ASSP file download URL changed from $old to $NewAsspURL") if $NewAsspURL ne $old;
28709                next;
28710            }
28711            if (/^\s*ChangeLogURL\s*:\s*(http(?:s)?:\/\/.+)$/io) {
28712                my $old = $ChangeLogURL;
28713                $ChangeLogURL = $1;
28714                mlog(0,"autoupdate: ASSP changelog download URL changed from $old to $ChangeLogURL") if $ChangeLogURL ne $old;
28715                next;
28716            }
28717            if (/^\s*(\w+)\s*:\s*(.+)$/io) {
28718                my ($var,$val) = ($1,$2);
28719                next unless defined ${$var};
28720                $val =~ s/\s+$//o;
28721                my $old = ${$var};
28722                ${$var} = $val;
28723                if (exists $Config{$var}) {
28724                    $Config{$var} = $val;
28725                    $ConfigChanged = 1;
28726                    mlog(0,"autoupdate: version file changed $var from $old to $val") if $val ne $old;
28727                }
28728                next;
28729            }
28730        }
28731        close $UVS;
28732    }
28733}
28734
28735sub downloadVersionFile {
28736    d('downloadVersionFile-start');
28737   	my $force;
28738    $force = 1 if ($NextASSPFileDownload == -1 or $NextVersionFileDownload == -1);
28739    &UpdateDownloadURLs();
28740    $NewAsspURL = $NewAsspURLStable;
28741    $versionURL = $versionURLStable;
28742    $ChangeLogURL =  $ChangeLogURLStable;
28743
28744    return 0 unless $versionURL;
28745    my $ret;
28746    my $file = "$base/version.txt";
28747    $ret = downloadHTTP("$versionURL",
28748                 $file,
28749                 \$NextVersionFileDownload,
28750                 "assp version check",16,12,4,4) if $file;
28751
28752    if ( !-e "$base/docs/changelog.txt" or $ret) {
28753        &UpdateDownloadURLs();
28754        downloadHTTP("$ChangeLogURL",
28755                     "$base/docs/changelog.txt",
28756                     0,
28757                     "assp change log",16,12,4,4);
28758    }
28759
28760    if (open my $VS , "<","$file") {
28761        while (<$VS>) {
28762            s/\n|\r//g;
28763            s/^\s+//;
28764            s/\s+$//;
28765            next if /^#|;/;
28766            s/#.*//;
28767            s/;.*//;
28768            if (/^\s*(\d+\.\d+\.\d+.+)$/) {
28769                $availversion = $1;
28770                my $avv = "$availversion";
28771                my $stv = "$version$modversion";
28772
28773                $avv =~ s/RC/\./gi;
28774                $stv =~ s/RC/\./gi;
28775                $avv =~ s/\s|\(|\)//gi;
28776                $stv =~ s/\s|\(|\)//gi;
28777
28778                $avv =~ s/\.//gi;
28779
28780                $stv =~ s/\.//gi;
28781
28782                if ($avv gt $stv) {
28783					mlog(0,"autoupdate: new assp version $availversion is available for download at $NewAsspURL");
28784                    $ret = 1;
28785                } else {
28786                    $ret = 0;
28787                }
28788            } elsif (/^\s*versionURL\s*:\s*(http(?:s)?:\/\/.+)$/i) {
28789                $versionURL = $1;
28790            } elsif (/^\s*NewAsspURL\s*:\s*(http(?:s)?:\/\/.+)$/i) {
28791                $NewAsspURL = $1;
28792
28793            }
28794        }
28795        close $VS;
28796    } else {
28797        $ret = 0;
28798    }
28799    return $ret || $force;
28800}
28801sub codeChangeCheck {
28802    return 0 unless $AutoUpdateASSP == 2;
28803    d('codeChangeCheck');
28804    my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(time);
28805    if ((lc $AutoRestartAfterCodeChange eq 'immed' ||
28806        ( $AutoRestartAfterCodeChange && $codeChanged && $hour == $AutoRestartAfterCodeChange)) &&
28807        ($AsAService || $AsADaemon || $AutoRestartCmd ) &&
28808        $NextCodeChangeCheck < time) {
28809        $assp = $0;
28810        $assp =~ s/\\/\//go;
28811        $assp = $base.'/'.$assp if ($assp !~ /\//);
28812    }
28813    if ((lc $AutoRestartAfterCodeChange eq 'immed' ||
28814        ( $AutoRestartAfterCodeChange && $codeChanged && $hour == $AutoRestartAfterCodeChange)) &&
28815        ($AsAService || $AsADaemon || $AutoRestartCmd ) &&
28816        $NextCodeChangeCheck < time &&
28817        -e "$assp" &&
28818        fileUpdated($assp,'asspCode')
28819       )
28820    {
28821        my @s     = stat($assp);
28822        my $mtime = $s[9];
28823        $FileUpdate{"$assp".'asspCode'} = $mtime;
28824        mlog(0,"autoupdate: info:  new '$assp' script detected - performing syntax check on new script");
28825        my $cmd;
28826        if ($^O eq "MSWin32") {
28827            $cmd = '"' . $^X . '"' . " -c \"$assp\" 2>&1";
28828        } else {
28829            $cmd = '\'' . $^X . '\'' . " -c \'$assp\' 2>&1";
28830        }
28831        my $res = qx($cmd);
28832        if ($res =~ /syntax\s+OK/ig) {
28833            mlog(0,"autoupdate: info:  new '$assp' script detected - syntax check returned OK - initialize automatic restart for ASSP in 15 seconds");
28834            $doShutdown = time + 15;
28835        } else {
28836            mlog(0,"autoupdate: error:  new '$assp' script detected - syntax error in new script - skipping automatic restart - syntax error is: $res");
28837        }
28838        $NextCodeChangeCheck = time + 60;
28839        $codeChanged = '';
28840#        $wasrun = 1;
28841    } else {
28842        $NextCodeChangeCheck = time + 60 if $NextCodeChangeCheck < time;
28843    }
28844
28845}
28846sub downloadASSPVersion {
28847    d('downloadASSPVersion-start');
28848
28849    return 0 unless $AutoUpdateASSP;
28850    &UpdateDownloadURLs();
28851	if ($AutoUpdateASSPDev) {
28852    $NewAsspURL = $NewAsspURLDev;
28853
28854    $versionURL = $versionURLDev;
28855    $ChangeLogURL =  $ChangeLogURLDev;
28856    } else {
28857    $NewAsspURL = $NewAsspURLStable;
28858
28859    $versionURL = $versionURLStable;
28860    $ChangeLogURL =  $ChangeLogURLStable;
28861    }
28862    return 0 unless $NewAsspURL;
28863    return 0 unless $versionURL;
28864
28865    my $assp = $PROGRAM_NAME;
28866    $assp =~ s/\\/\//g;
28867    $assp =~ s/\/\//\//g;
28868    $assp = $base.'/'.$PROGRAM_NAME if ($assp !~ /\Q$base\E/i);
28869    if (-e "$base/download/assp.pl" && ! -w "$base/download/assp.pl") {
28870        mlog(0,"autoupdate: warning:  unable to write to $base/download/assp.pl - skip update - please check the file permission");
28871        $NextASSPFileDownload = time + 3600;
28872        return 0;
28873    }
28874    if (-e "$base/download/assp.pl.gz" && ! -w "$base/download/assp.pl.gz") {
28875        mlog(0,"autoupdate: warning:  unable to write to $base/download/assp.pl.gz - skip update - please check the file permission");
28876        $NextASSPFileDownload = time + 3600;
28877        return 0;
28878    }
28879    if (! -w "$assp") {
28880        mlog(0,"autoupdate: warning:  unable to write to $assp - skip update - please check the file permission");
28881        $NextASSPFileDownload = time + 3600;
28882        return 0;
28883    }
28884    -d "$base/download" or mkdir "$base/download", 0777;
28885    if (! -e "$base/download/assp.pl" && ! copy("$assp","$base/download/assp.pl")) {
28886        mlog(0,"autoupdate: warning:  unable to copy current script '$assp' to '$base/download/assp.pl' - skip update - $!");
28887        $NextASSPFileDownload = time + 3600;
28888        return 0;
28889    }
28890    unless (&downloadVersionFile()){
28891        $NextASSPFileDownload = $NextVersionFileDownload;
28892        return 0;
28893    }
28894    my $ret;
28895    $NextASSPFileDownload = 0;
28896    mlog(0,"autoupdate: info:  performing assp.pl.gz download to $base/download/assp.pl.gz") if $MaintenanceLog;
28897    $ret = downloadHTTP("$NewAsspURL",
28898                 "$base/download/assp.pl.gz",
28899                 \$NextASSPFileDownload,
28900                 "assp.pl.gz",16,12,4,4);
28901    return 0 unless $ret;
28902    mlog(0,"autoupdate: info:  new assp.pl.gz downloaded to $base/download/assp.pl.gz") if $MaintenanceLog;
28903    if (unzipgz("$base/download/assp.pl.gz", "$base/download/assp.pl")) {
28904        mlog(0,"autoupdate: info:  new assp version '$base/download/assp.pl' available - version $availversion") if $MaintenanceLog;
28905    } else {
28906        mlog(0,"autoupdate: warning:  unable to unzip '$base/download/assp.pl.gz' to '$base/download/assp.pl' - skip update");
28907        return 0;
28908    }
28909
28910
28911    mlog(0,"autoupdate: info:  saving current script '$assp' to 'assp_$version$modversion.pl'") if $MaintenanceLog;
28912    if (! copy("$assp","$base/download/assp_$version$modversion.pl")) {
28913        mlog(0,"autoupdate: warning:  unable to save current script '$assp' to '$base/download/assp_$version$modversion.pl' - skip update - $!");
28914        return 0;
28915    }
28916    my $cmd;
28917    if ($^O eq "MSWin32") {
28918        $cmd = '"' . $^X . '"' . " -c \"$base/download/assp.pl\" \"$base\" 2>&1";
28919    } else {
28920        $cmd = '\'' . $^X . '\'' . " -c \'$base/download/assp.pl\' \'$base\' 2>&1";
28921    }
28922    my $res = qx($cmd);
28923    if ($res =~ /syntax\s+OK/ig) {
28924        mlog(0,"autoupdate: info:  syntax check for '$base/download/assp.pl' returned OK");
28925    } else {
28926        mlog(0,"autoupdate: warning:  syntax error in '$base/download/assp.pl' - skip assp.pl update - syntax error is: $res");
28927        return 0;
28928    }
28929    if ($res =~ /assp\s+(.+)?is starting/i) {
28930        my $v = $1;
28931        $v =~ s/RC/\./gi;
28932        $v =~ s/\s|\(|\)//gi;
28933        $v =~ s/\.//gi;
28934        my $stv = "$version$modversion";
28935        $stv =~ s/RC/\./gi;
28936        $stv =~ s/\s|\(|\)//gi;
28937        $stv =~ s/\.//gi;
28938        if ($stv ge $v) {
28939            mlog(0,"autoupdate: warning:  version of downloaded '$base/download/assp.pl' ($v) is less or equal to the running version of assp ($stv) - skip assp.pl update");
28940            return 0;
28941        }
28942    }
28943    return 0 if $AutoUpdateASSP == 1;
28944    if (copy("$base/download/assp.pl", "$assp")) {
28945        mlog(0,"autoupdate: info:  new version assp installed - '$assp' - version $availversion");
28946    } else {
28947        mlog(0,"autoupdate: warning:  unable to replace current script '$assp' - skip update - $!");
28948        return 0;
28949    }
28950
28951
28952    return 1 if (lc $AutoRestartAfterCodeChange eq 'immed' &&
28953        ($AsAService || $AsADaemon || $AutoRestartCmd ));
28954    $codeChanged = 1 if $AutoRestartAfterCodeChange;
28955    return 1;
28956}
28957
28958
28959sub uploadStats {
28960
28961    my ( $peeraddress, $connect );
28962    if ($proxyserver) {
28963        mlog( 0, "uploading stats via proxy:$proxyserver" ) if $MaintenanceLog;
28964        $peeraddress = $proxyserver;
28965        $connect =
28966          "POST http://assp.sourceforge.net/cgi-bin/assp_stats HTTP/1.0";
28967    } else {
28968        mlog( 0, "uploading stats via direct connection" ) if $MaintenanceLog;
28969        $peeraddress = "assp.sourceforge.net:80";
28970        $connect     = "POST /cgi-bin/assp_stats HTTP/1.1
28971Host: assp.sourceforge.net";
28972    }
28973    my $s;
28974    if ($CanUseIOSocketINET6) {
28975	$s = new IO::Socket::INET6(
28976	    Proto    => 'tcp',
28977	    PeerAddr => $peeraddress,
28978	    Timeout  => 2
28979	);
28980    } else {
28981	$s = new IO::Socket::INET(
28982	    Proto    => 'tcp',
28983	    PeerAddr => $peeraddress,
28984	    Timeout  => 2
28985	);
28986    }
28987    if ($s) {
28988        my %UploadStats = ();
28989        my %tots = statsTotals();
28990        my $buf;
28991
28992	# spam filtering counters
28993	foreach (keys %Stats) {$UploadStats{$_} = $Stats{$_};}
28994
28995	# stats upload version
28996        $UploadStats{upproto_version}      = 2;
28997
28998	# ASSP version
28999        $UploadStats{version}              = $Stats{version};
29000
29001        # message totals
29002        $UploadStats{upproto_version}      = 2;
29003        $UploadStats{timenow}              = time;
29004        $UploadStats{connects}             = $tots{smtpConnTotal};
29005        $UploadStats{messages}             = $tots{msgTotal};
29006        $UploadStats{spams}                = $tots{msgRejectedTotal} - $Stats{bspams};
29007        delete $UploadStats{nextUpload};
29008
29009
29010
29011        my $content = join( "\001", %UploadStats );
29012        my $len = length($content);
29013        $connect .= "
29014Content-Type: application/x-www-form-urlencoded
29015Content-Length: $len
29016
29017$content";
29018        print $s $connect;
29019        $s->sysread($buf, 4096);
29020        $s->close;
29021    } else {
29022        mlog( 0, "unable to connect to stats server" );
29023    }
29024    $Stats{nextUpload} = time + 3600 * 12;
29025}
29026
29027sub ResetStats {
29028    $Stats{nextUpload}                = time + 3600 * 12;
29029    $Stats{cpuTime}                   = 0;
29030    $Stats{cpuBusyTime}               = 0;
29031    $Stats{sbblocked}                 = 0;
29032    $Stats{smtpConn}                  = 0;
29033    $Stats{smtpConnNotLogged}         = 0;
29034    $Stats{smtpConnLimit}             = 0;
29035    $Stats{smtpConnLimitIP}           = 0;
29036    $Stats{smtpConnDomainIP}          = 0;
29037    $Stats{SameSubject}         = 0;
29038    $Stats{smtpConnLimitFreq}         = 0;
29039    $Stats{smtpConnDenied}            = 0;
29040    $Stats{smtpConnIdleTimeout}       = 0;
29041    $Stats{smtpConnSSLIdleTimeout}	  = 0;
29042 	$Stats{smtpConnTLSIdleTimeout}	  = 0;
29043    $Stats{smtpConcurrentSessions}    = 0;
29044    $Stats{smtpMaxConcurrentSessions} = 0;
29045    $Stats{admConn}                   = 0;
29046    $Stats{admConnDenied}             = 0;
29047    $Stats{statConn}                  = 0;
29048    $Stats{statConnDenied}            = 0;
29049    $Stats{rcptValidated}             = 0;
29050    $Stats{rcptUnchecked}             = 0;
29051    $Stats{rcptSpamLover}             = 0;
29052    $Stats{rcptWhitelisted}           = 0;
29053    $Stats{rcptNotWhitelisted}        = 0;
29054    $Stats{rcptUnprocessed}           = 0;
29055    $Stats{rcptReportSpam}            = 0;
29056    $Stats{rcptReportHam}             = 0;
29057    $Stats{rcptReportWhitelistAdd}    = 0;
29058    $Stats{rcptReportWhitelistRemove} = 0;
29059    $Stats{rcptReportRedlistAdd}      = 0;
29060    $Stats{rcptReportRedlistRemove}   = 0;
29061    $Stats{rcptReportAnalyze}         = 0;
29062    $Stats{rcptReportHelp}            = 0;
29063    $Stats{rcptNonexistent}           = 0;
29064    $Stats{rcptDelayed}               = 0;
29065    $Stats{rcptDelayedLate}           = 0;
29066    $Stats{rcptDelayedExpired}        = 0;
29067    $Stats{rcptEmbargoed}             = 0;
29068    $Stats{rcptSpamBucket}            = 0;
29069    $Stats{rcptRelayRejected}         = 0;
29070    $Stats{senderInvalidLocals}       = 0;
29071    $Stats{pbdenied}                  = 0;
29072    $Stats{pbextreme}                 = 0;
29073    $Stats{delayConnection}			  = 0;
29074    $Stats{denyConnection}            = 0;
29075    $Stats{denyStrict}           = 0;
29076    $Stats{msgscoring}                = 0;
29077    $Stats{msgMaxErrors}              = 0;
29078    $Stats{msgMaxFreq}                = 0;
29079    $Stats{msgDelayed}                = 0;
29080    $Stats{msgNoRcpt}                 = 0;
29081    $Stats{msgNoSRSBounce}            = 0;
29082    $Stats{bhams}                     = 0;
29083    $Stats{whites}                    = 0;
29084    $Stats{locals}                    = 0;
29085    $Stats{localFrequency}			  = 0;
29086    $Stats{noprocessing}              = 0;
29087    $Stats{spamlover}                 = 0;
29088    $Stats{bspams}                    = 0;
29089    $Stats{blacklisted}               = 0;
29090    $Stats{invalidHelo}               = 0;
29091    $Stats{forgedHelo}                = 0;
29092    $Stats{mxaMissing}                = 0;
29093    $Stats{ptrMissing}                = 0;
29094    $Stats{ptrInvalid}                = 0;
29095    $Stats{helolisted}                = 0;
29096    $Stats{spambucket}                = 0;
29097    $Stats{penaltytrap}               = 0;
29098    $Stats{internaladdress}           = 0;
29099    $Stats{viri}                      = 0;
29100    $Stats{viridetected}              = 0;
29101    $Stats{bombs}                     = 0;
29102    $Stats{bombs}				  = 0;
29103
29104    $Stats{msgverify}                 = 0;
29105    $Stats{scripts}                   = 0;
29106    $Stats{internaladdresses}         = 0;
29107    $Stats{spffails}                  = 0;
29108    $Stats{rblfails}                  = 0;
29109    $Stats{uriblfails}                = 0;
29110    $Stats{msgMaxErrors}		  = 0;
29111    $Stats{msgMSGIDtrErrors}		  = 0;
29112    $Stats{msgBackscatterErrors}      = 0;
29113    $Stats{AUTHErrors}				  = 0;
29114    $Stats{msgMSGIDtrErrors}          = 0;
29115    $Stats{preHeader}				  = 0;
29116
29117
29118    open( $FH, "<","$base/asspstats.sav" );
29119    (%OldStats) = split( /\001/, <$FH> );
29120    close $FH;
29121
29122    # conversion from previous versions
29123    if ( exists $OldStats{messages} ) {
29124        $OldStats{smtpConn}        = $OldStats{connects};
29125        $OldStats{smtpConnLimit}   = $OldStats{maxSMTP};
29126        $OldStats{smtpConnLimitIP} = $OldStats{maxSMTPip};
29127        $OldStats{viri} -= $OldStats{viridetected};    # fix double counting
29128        $OldStats{rcptRelayRejected} = $OldStats{norelays};
29129
29130        # remove unused entries
29131        delete $OldStats{connects};
29132        delete $OldStats{maxSMTP};
29133        delete $OldStats{maxSMTPip};
29134        delete $OldStats{messages};
29135        delete $OldStats{spams};
29136        delete $OldStats{hams};
29137        delete $OldStats{norelays};
29138        delete $OldStats{testmode};
29139        SaveStats();
29140    }
29141}
29142
29143sub SaveStats {
29144    $Stats{smtpConcurrentSessions} = $smtpConcurrentSessions;
29145    $NextSaveStats = time + ( $SaveStatsEvery * 60 );
29146   	%AllStats = %OldStats;
29147
29148    foreach ( keys %Stats ) {
29149        if ( $_ eq 'version' ) {
29150
29151            # just copy
29152            $AllStats{$_} = $Stats{$_};
29153        } elsif ( $_ eq 'smtpMaxConcurrentSessions' ) {
29154
29155            # pick greater value
29156            $AllStats{$_} = $Stats{$_} if $Stats{$_} > $AllStats{$_};
29157        } else {
29158            $AllStats{$_} += $Stats{$_};
29159        }
29160    }
29161    $AllStats{starttime} = $OldStats{starttime} || $Stats{starttime};
29162	unlink("$base/asspstats.sav.bak");
29163    rename("$base/asspstats.sav","$base/asspstats.sav.bak");
29164
29165 	my $SS;
29166 	if (open($SS,">","$base/asspstats.sav")) {
29167     	print $SS join("\001",%AllStats);
29168     	close $SS;
29169 	} else {
29170     	mlog(0,"warning: unable to save STATS to $base/asspstats.sav - $!");
29171 	}
29172}
29173
29174#####################################################################################
29175#                Maillog functions
29176# find an appropriate name for a maillog file
29177sub maillogFilename {
29178    my ($fh,$isspam)  = @_;
29179    my $this=$Con{$fh};
29180    my @dirs    = ( $notspamlog, $spamlog, $incomingOkMail, $viruslog, $discarded, $discarded );
29181    my $maillog = $dirs[$isspam];
29182    return if !$maillog;
29183    d('maillogFilename');
29184   my $sub;
29185    $sub = $1
29186      if ($this->{maillogbuf}=~/\015\012Subject: *($HeaderValueRe)/iso or
29187          $this->{header}=~/\015\012Subject: *($HeaderValueRe)/iso);
29188    $sub =~ s/\r\n\s*//go;
29189
29190    eval('no bytes;');
29191    $sub =~ s/\r?\n$//o;
29192    $sub =~ s/$NONPRINT//go;
29193    $sub = dedecodeMimeWords($sub);
29194    if ($LogCharset =~ /125[02]$/) {
29195        $sub =~ s/\x80/(EUR)/go;
29196    }
29197
29198    my $sub2 = $sub;
29199    $sub =~ y/a-zA-Z0-9/_/cs ;
29200    $sub =~ s/[\s\<\>\?\"\'\:\|\\\/\*\&\.]/_/ig;  # remove not allowed characters and spaces from file name
29201#    $sub =~ s/\\x\{\d{2,}\}/_/g;
29202    $sub =~s/\.{2,}/\./g;
29203    $sub =~s/_{2,}/_/g;
29204    $sub =~s/[_\.]+$//;
29205    $sub =~s/^[_\.]+//;
29206    $this->{subject} = substr( $sub, 0, 50 ) unless $this->{subject};
29207    $sub = substr($sub,0,($MaxFileNameLength ? $MaxFileNameLength : 50));
29208
29209    eval('use bytes;');
29210    if ( $UseSubjectsAsMaillogNames && $isspam == 1 && $MaxAllowedDups && $sub2 !~ /$AllowedDupSubjectReRE/) {
29211          my $md5sub = $sub ? Digest::MD5::md5($sub) : Digest::MD5::md5(' ') ;
29212          if ($Spamfiles{$md5sub} >= $MaxAllowedDups) {
29213              $maillog = $discarded if $discarded;
29214              mlog($fh,"MaxAllowedDups reached for this subject - store and discard mail in $discarded") if $SessionLog;
29215          } else {
29216              $Spamfiles{$md5sub}++;
29217          }
29218    }
29219    if ( $UseSubjectsAsMaillogNames || $isspam == 2 || $isspam == 3 ) {
29220        $Stats{Counter}++ unless $this->{hasmallogname};
29221        $Stats{Counter} = 1 if $Stats{Counter} > 99999999;
29222        $sub .= "__" . ( $Stats{Counter} );
29223        $this->{hasmallogname} = 1;
29224        return "$base/$maillog/$sub$maillogExt";
29225    } else {
29226        my $fn = $this->{fn};
29227        $this->{hasmallogname} = 1;
29228        return "$base/$maillog/$fn$maillogExt";
29229    }
29230}
29231sub maillogNewFileName {
29232
29233    return  $Counter++ % 99999;
29234}
29235
29236# integrated mail collection subroutine
29237sub MaillogStart {
29238    my $fh = shift;
29239    d('MaillogStart');
29240    $Con{$fh}->{maillog} = 1 unless $NoMaillog ;
29241    if ("$fh" =~ /SSL/o) {
29242        my $ciffer = eval{$fh->get_cipher();};
29243        $ciffer = '' unless $ciffer;
29244        $ciffer and $ciffer = '('.$ciffer.')';
29245        $Con{$fh}->{rcvd} =~ s/(\sE?SMTP)S?/$1S$ciffer/os;
29246    }
29247    $Con{$fh}->{maillogbuf}=$Con{$fh}->{header}=$Con{$fh}->{rcvd};
29248}
29249
29250sub Maillog {
29251    my ( $fh, $text, $parm ) = @_;
29252    my $fln;
29253    my $isnotcc;
29254    d('Maillog');
29255    return unless $fh;
29256
29257    if ( $parm == 1 ) {
29258        $parm    = 3;
29259        $isnotcc = 1;
29260    }
29261
29262# 0 -- no collection, 1 -- is spam, parm = 2 -- not spam,
29263# 3 -- is spam && cc to spamaccount, 4 -- mail ok, 5 -- virii
29264# 6 -- discard folder, 7 -- discard folder && cc to spamaccount
29265
29266    $parm = 7
29267      if  $Con{$fh}->{redlist}  &&  $parm==3 && $DoNotCollectRed;
29268    $parm = 7
29269      if  $Con{$fh}->{red}  &&  $parm==3 && $DoNotCollectRed;
29270    $parm = 7
29271      if  $Con{$fh}->{redsl}  &&  $parm==3 && $DoNotCollectRed;
29272    $parm = 6
29273      if  $Con{$fh}->{redlist}  &&  $parm==1 && $DoNotCollectRed;
29274    $parm = 6
29275      if  $Con{$fh}->{red}  &&  $parm==1 && $DoNotCollectRed;
29276    $parm = 6
29277      if  $Con{$fh}->{redsl}  &&  $parm==1 && $DoNotCollectRed;
29278   $parm = 4
29279      if  $Con{$fh}->{redlist}  &&  $parm==2 && $DoNotCollectRed;
29280    $parm = 4
29281      if  $Con{$fh}->{red}  &&  $parm==2 && $DoNotCollectRed;
29282    $parm = 4
29283      if  $Con{$fh}->{redsl}  &&  $parm==2 && $DoNotCollectRed;
29284
29285    $parm = 7
29286      if $Con{$fh}->{isbounce} &&  $parm == 3 && $DoNotCollectBounces;
29287	$parm = 6
29288      if $Con{$fh}->{isbounce} &&  $parm == 1 && $DoNotCollectBounces;
29289
29290    $parm = 7
29291      if $Con{$fh}->{messagelow} &&  $parm == 3;
29292	$parm = 7
29293      if $Con{$fh}->{messagelow} &&  $parm == 1;
29294
29295	$parm = 6
29296      if $Con{$fh}->{ccnever} &&  $parm == 3;
29297	$parm = 6
29298      if $Con{$fh}->{ccnever} &&  $parm == 1;
29299
29300	$parm = 7 if $sendAllDiscard &&  $parm == 6;
29301    return unless ( $Con{$fh}->{maillog} );
29302    return if ( $Con{$fh}->{nocollect} );
29303
29304    return if $Con{$fh}->{noprocessing} && ( $parm == 2 || $parm == 4 ) && ! $noProcessingLog;
29305    if ( $noCollecting && matchSL( $Con{$fh}->{mailfrom}, 'noCollecting' ) ) {
29306        $Con{$fh}->{nocollect} = 1;
29307        return;
29308      }
29309	$parm = 1
29310      if $ccMaxScore && ( $parm == 3 && $Con{$fh}->{messagescore} > $ccMaxScore );
29311    $parm = 6
29312      if $ccMaxScore && ( $parm == 7 && $Con{$fh}->{messagescore} > $ccMaxScore );
29313
29314    $parm = 7
29315      if ( $parm == 6
29316        && $ccSpamAlways
29317        && allSL( $Con{$fh}->{rcpt}, $Con{$fh}->{mailfrom}, 'ccSpamAlways' ) );
29318    $parm = 7
29319      if ( $parm == 6
29320        && $ccSpamFilter
29321        && $sendAllSpam
29322        && allSL( $Con{$fh}->{rcpt}, $Con{$fh}->{mailfrom}, 'ccSpamFilter' ) );
29323
29324    my $skipLog = 0;
29325
29326    $Con{$fh}->{storecompletemail} = $StoreCompleteMail;
29327    $Con{$fh}->{storecompletemail} = "9999999" if !$StoreCompleteMail && $Con{$fh}->{alllog};
29328    $Con{$fh}->{storecompletemail} = "9999999" if $ccSpamAlways
29329        && allSL( $Con{$fh}->{rcpt}, $Con{$fh}->{mailfrom}, 'ccSpamAlways' );
29330
29331
29332	$parm = 4 if $parm == 6 && !$Con{$fh}->{spamfound};
29333    if (   $parm == 4 && !$incomingOkMail
29334    	|| $parm == 1 && (!$spamlog or !$SpamLog)
29335        || $parm == 2 && (!$notspamlog or !$NonSpamLog)
29336        || $parm == 5 && !$viruslog
29337        || $parm == 6 && !$discarded
29338        || $parm == 7 && !$discarded
29339        || $skipLog ) {
29340        d('Maillog - no log - missing folder');
29341        delete $Con{$fh}->{maillogbuf};
29342        delete $Con{$fh}->{maillog};
29343        close $Con{$fh}->{maillogfh} if $Con{$fh}->{maillogfh};
29344        delete $Con{$fh}->{maillogfh};
29345        delete $Con{$fh}->{mailloglength};
29346    } elsif ( $parm > 1 ) {
29347
29348        d('Maillog - log');
29349
29350        # we now know if it is spam or not -- open the file
29351        $text = $Con{$fh}->{maillogbuf} . $text;
29352        if ( $parm < 8 ) {
29353
29354            my $fn = maillogFilename( $fh, $parm - 2 );
29355
29356            $fln = $fn;
29357            $Con{$fh}->{maillogfilename} = $fn;
29358            if (!$fn) {
29359                $fln = '';
29360            } else {
29361                if ( open( my $FH , '>',"$fn" ) ) {
29362                    binmode $FH;
29363                    $Con{$fh}->{maillogfh} = $FH;
29364                    $Con{$fh}->{mailloglength} = 0;
29365                    if ($StoreASSPHeader) {
29366                        my $myheader = $Con{$fh}->{myheader};
29367
29368                        $myheader = "X-Assp-Version: $version$modversion on $myName\r\n" . $myheader
29369                            if $myheader !~ /X-Assp-Version:.+? on \Q$myName\E/;
29370                        $myheader .= "X-Assp-ID: $myName $Con{$fh}->{msgtime}\r\n"
29371                            if $myheader !~ /X-Assp-ID: \Q$myName\E/;
29372
29373
29374                        $myheader =~ s/X-Assp-Spam:$HeaderValueRe//gios;
29375                        $myheader =~ s/X-Assp-Spam-Level:$HeaderValueRe//gios;
29376                        $myheader =~ s/[\r\n]+$/\r\n/o;
29377                        $myheader = headerFormat($myheader);
29378
29379                        print $FH $myheader;
29380                        $Con{$fh}->{mailloglength} = length($myheader);
29381                    }
29382                } else {
29383                    mlog( $fh, "error opening maillog '$fn': $!" );
29384                }
29385            }
29386        }
29387
29388        # start sending the message to sendAllSpam if appropriate
29389
29390        my $current_email = $Con{$fh}->{rcpt};
29391        $current_email =~/($EmailAdrRe)\@($EmailDomainRe)/o;
29392        my ($current_username,$current_domain) = ($1,$2);
29393
29394        if(($sendAllSpam or scalar keys %ccdlist) && !$Con{$fh}->{whitelist}) {
29395            my $ccspamlt = $sendAllSpam;
29396            if ($ccspamlt) {
29397                $ccspamlt =~ s/USERNAME/$current_username/go;
29398                $ccspamlt =~ s/DOMAIN/$current_domain/go;
29399            }
29400            if ( $ccdlist{lc $current_domain} ) {
29401                $ccspamlt .= ' ' if $ccspamlt;
29402                $ccspamlt .= $ccdlist{lc $current_domain} . '@' . $current_domain;
29403            } elsif ($ccdlist{'*'}) {
29404            	$ccspamlt .= ' ' if $ccspamlt;
29405            	$ccspamlt .= $ccdlist{"*"}.'@'.$current_domain;
29406            }
29407
29408            $Con{$fh}->{forwardSpam}=forwardSpam($Con{$fh}->{mailfrom},$ccspamlt,$fh) if ($ccspamlt && $isnotcc!=1 && ($parm==3 || $parm==7) && (!$ccSpamFilter  || $ccSpamFilter && allSL($Con{$fh}->{rcpt},$Con{$fh}->{mailfrom},'ccSpamFilter')));
29409        }
29410    }
29411	my $gotAllText;
29412    if(my $h = $Con{$fh}->{maillogfh}) {
29413        if (! $Con{$fh}->{spambuf}) {
29414            $h->print(substr($text,0,max($Con{$fh}->{storecompletemail},$MaxBytes)));
29415            $Con{$fh}->{mailloglength} = $Con{$fh}->{spambuf} = length($text);
29416            $Con{$fh}->{maillogbuf} = $text;
29417        } else {
29418            if ( $Con{$fh}->{spambuf} < $Con{$fh}->{storecompletemail}) {
29419                $h->print(substr($text,0,$Con{$fh}->{storecompletemail} - $Con{$fh}->{spambuf}));
29420            } else {
29421                $h->print(substr($text,0,$MaxBytes + $Con{$fh}->{headerlength})) if $Con{$fh}->{spambuf}<$MaxBytes + $Con{$fh}->{headerlength} ;
29422            }
29423            $Con{$fh}->{maillogbuf}.=$text;
29424            $Con{$fh}->{spambuf} += length($text);
29425            $Con{$fh}->{mailloglength} = length($Con{$fh}->{maillogbuf});
29426        }
29427        if(  (   $ccMaxBytes
29428              && $Con{$fh}->{mailloglength} > $MaxBytes + $Con{$fh}->{headerlength}
29429              && $Con{$fh}->{mailloglength} > $Con{$fh}->{storecompletemail})
29430           || $text=~/(^|[\r\n])\.[\r\n]/o)
29431        {
29432            d('Maillog - no cc');
29433            $gotAllText = 1;
29434            $h->close;
29435            delete $Con{$fh}->{maillog} unless $Con{$fh}->{forwardSpam};
29436            delete $Con{$fh}->{maillogfh};
29437            delete $Con{$fh}->{mailloglength};
29438        }
29439    } elsif(! $ccMaxBytes || $Con{$fh}->{mailloglength} < $MaxBytes + $Con{$fh}->{headerlength} || $Con{$fh}->{mailloglength} < $Con{$fh}->{storecompletemail}) {
29440        eval{$Con{$fh}->{maillogbuf}.=$text; };
29441        $Con{$fh}->{mailloglength} = length($Con{$fh}->{maillogbuf});
29442    }
29443    if($Con{$fh}->{forwardSpam} && exists $Con{$Con{$fh}->{forwardSpam}} && exists $Con{$Con{$fh}->{forwardSpam}}->{body}) {
29444        $Con{$Con{$fh}->{forwardSpam}}->{body} .= $text;
29445        $Con{$Con{$fh}->{forwardSpam}}->{gotAllText} = $gotAllText;
29446    }
29447    return $fln;
29448}
29449
29450
29451
29452
29453sub MaillogRemove {
29454    my $this = shift;
29455    d('MaillogRemove');
29456    return 0 unless $this ;
29457    if ($this->{maillogfilename} !~ /^(?:\Q$base\E\/)?(?:$notspamlog|$incomingOkMail)/) {
29458        return 0 if (($notspamlog && $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?(?:$notspamlog|$discarded)/) or
29459                   (! $notspamlog && $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?$discarded/) or
29460                   ($notspamlog && ! $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?$notspamlog/));
29461        return 0 if (($incomingOkMail && $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?(?:$incomingOkMail|$discarded)/) or
29462                   (! $incomingOkMail && $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?$discarded/) or
29463                   ($incomingOkMail && ! $discarded && $this->{maillogfilename} !~ /^(?:\Q$base\E\/)?$incomingOkMail/));
29464    }
29465    close $this->{maillogfh} if ($this->{maillogfh});
29466    if (-e $this->{maillogfilename}) {
29467        unlink "$this->{maillogfilename}";
29468        mlog($this->{self},"info: logfile $this->{maillogfilename} removed") if $SessionLog;
29469    }
29470    delete $this->{maillog};
29471    delete $this->{maillogfh};
29472    delete $this->{mailloglength};
29473    delete $this->{maillogfilename};
29474    delete $this->{maillogparm};
29475    $this->{maillog} = 1 unless $NoMaillog;
29476    return 1;
29477}
29478
29479sub MaillogClose {
29480    my $fh = shift;
29481    d('MaillogClose');
29482    return unless $fh;
29483    my $f=$Con{$fh}->{maillogfh};
29484    eval{close $f if $f;};
29485    return if $Con{$fh}->{type} ne 'C';
29486    return unless $Con{$fh}->{maillogfilename};
29487    if ($Con{$fh}->{deleteMailLog}) {
29488        unlink "$Con{$fh}->{maillogfilename}";
29489        mlog($fh,"info: file $Con{$fh}->{maillogfilename} was deleted");
29490        delete $Con{$fh}->{maillogfilename};
29491    } elsif ($noCollectRe) {
29492        my ($mfh,$buf);
29493        my $bytes = ($MaxBytes + $Con{$fh}->{headerlength} > 100000) ? 100000 : $MaxBytes + $Con{$fh}->{headerlength};
29494        $bytes = 100000 unless $bytes;
29495        if (open $mfh, '<',"$Con{$fh}->{maillogfilename}") {
29496            binmode $mfh;
29497            my $hasread = 1;
29498            while ($hasread > 0 and length($buf) < $bytes) {
29499                my $read;
29500                $hasread = $mfh->sysread($read,$bytes);
29501                $buf .= $read;
29502            }
29503            close $mfh;
29504            if ($buf && $buf =~ /$noCollectReRE/is) {
29505                if (exists $runOnMaillogClose{'ASSP_ARC::setvars'}) {
29506                    $Con{$fh}->{deletemaillog} = 'noCollectRe';
29507                } else {
29508                    unlink "$Con{$fh}->{maillogfilename}";
29509                    mlog($fh,"info: file $Con{$fh}->{maillogfilename} was deleted - matched noCollectRe");
29510                    delete $Con{$fh}->{maillogfilename};
29511                }
29512            }
29513        }
29514    }
29515    foreach my $sub (keys %runOnMaillogClose) {
29516        $sub->($fh);
29517    }
29518}
29519
29520
29521sub forwardSpam {
29522    my ($from,$to,$oldfh)=@_;
29523
29524    my $s;
29525    my $AVa;
29526	my $this=$Con{$oldfh};
29527	my $msgtime = $this->{msgtime};
29528	my $headerlength = $this->{headerlength};
29529    my $destination;
29530    if ($sendAllDestination ne '') {
29531        $destination = $sendAllDestination;
29532    }else{
29533        $destination = $smtpDestination;
29534    }
29535
29536    $AVa = 0;
29537    foreach my $destinationA (split(/\|/o, $destination)) {
29538        if ($destinationA =~ /^(_*INBOUND_*:)?(\d+)$/o){
29539            $destinationA = '127.0.0.1:'.$2;
29540        }
29541
29542        $destinationA=~ s/\[::1\]/127\.0\.0\.1/ ;
29543		$destinationA=~ s/localhost/127\.0\.0\.1/i ;
29544
29545        if ($AVa<1) {
29546            $s = $CanUseIOSocketINET6
29547                 ? IO::Socket::INET6->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2,&getDestSockDom($destinationA))
29548                 : IO::Socket::INET->new(Proto=>'tcp',PeerAddr=>$destinationA,Timeout=>2);
29549            if($s) {
29550                $AVa=1;
29551                $destination=$destinationA;
29552            }
29553            else {
29554                mlog(0,"*** $destinationA didn't work, trying others...") if $SessionLog;
29555            }
29556        }
29557    }
29558    if(! $s) {
29559
29560        mlog(0,"couldn't create server socket to $destination -- aborting sendAllSpam connection") if $SessionLog;
29561        return;
29562    }
29563    addfh($s,\&FShelo);
29564    my $this=$Con{$s};
29565    $this->{to_as} = $to;
29566    @{$this->{to}}=split(/\s*,\s*|\s+/o,$to);
29567    $this->{msgtime} = $msgtime;
29568    $this->{from}=$from;
29569  	$this->{headerlength}=$headerlength;
29570    $this->{fromIP}=$Con{$oldfh}->{ip};
29571    $this->{clamscandone}=$Con{$oldfh}->{clamscandone};
29572    $this->{FileScanOK}=$Con{$oldfh}->{FileScanOK};
29573    $this->{rcpt}=$Con{$oldfh}->{rcpt};
29574    $this->{myheader}=$Con{$oldfh}->{myheader};
29575    $this->{prepend}=$Con{$oldfh}->{prepend};
29576    $this->{saveprepend}=$Con{$oldfh}->{saveprepend};
29577    $this->{saveprepend2}=$Con{$oldfh}->{saveprepend2};
29578    $this->{body} = '';
29579    $this->{FSnoopCount} = 0;
29580    $this->{self} = $s;
29581    return $s;
29582}
29583sub FShelo { my ($fh,$l)=@_;
29584    if($l=~/^ *[54]/o) {
29585        FSabort($fh,"helo Expected 220, got: $l");
29586    } elsif($l=~/^ *220 /o) {
29587        sendque($fh,"HELO $myName\r\n");
29588        $Con{$fh}->{getline}=\&FSfrom;
29589    }
29590}
29591sub FSfrom { my ($fh,$l)=@_;
29592    if($l=~/^ *[54]/o) {
29593        FSabort($fh,"send HELO($myName), expected 250, got: $l");
29594    } elsif($l=~/^ *250 /o) {
29595        $Con{$fh}->{FSlastCMD} = "MAIL FROM: <$Con{$fh}->{from}>";
29596        sendque($fh,"$Con{$fh}->{FSlastCMD}\r\n");
29597        $Con{$fh}->{getline}=\&FSrcpt;
29598    }
29599}
29600sub FSrcpt { my ($fh,$l)=@_;
29601    if($l=~/^ *[54]/o) {
29602        FSabort($fh,"send $Con{$fh}->{FSlastCMD}, expected 250, got: $l");
29603    } elsif($l=~/^ *250 /o) {
29604        $Con{$fh}->{FSlastCMD} = "RCPT TO: <" . shift(@{$Con{$fh}->{to}}) . ">";
29605        sendque($fh,"$Con{$fh}->{FSlastCMD}\r\n");
29606        $Con{$fh}->{getline} = \&FSdata;
29607    }
29608}
29609sub FSnoop { my ($fh,$l)=@_;
29610    if ($Con{$fh}->{gotAllText}) {
29611        &FSdata($fh,$l);
29612        return;
29613    }
29614    if($l=~/^ *[54]/o) {
29615        FSabort($fh,"send $Con{$fh}->{FSlastCMD}, expected 250, got: $l");
29616    } elsif($l=~/^ *250 /o) {
29617        sendque($fh,"NOOP\r\n");
29618        $Con{$fh}->{FSnoopCount}++ if $Con{$fh}->{FSnoopCount} < 5;
29619        $Con{$fh}->{sendTime} = time + $Con{$fh}->{FSnoopCount};
29620        $Con{$fh}->{FSlastCMD} = 'NOOP';
29621    }
29622}
29623sub FSdata { my ($fh,$l)=@_;
29624    delete $Con{$fh}->{sendTime};
29625    if($l=~/^ *[54]/o) {
29626        FSabort($fh,"send $Con{$fh}->{FSlastCMD}, expected 250, got: $l");
29627    } elsif($l=~/^ *250 /o) {
29628        sendque($fh,"DATA\r\n");
29629        $Con{$fh}->{getline}=\&FSdata2;
29630    }
29631}
29632sub FSdata2 { my ($fh,$l)=@_;
29633    my $this=$Con{$fh};
29634    if($l=~/^ *[54]/o) {
29635        FSabort($fh,"FSdata2 Expected 354, got: $l");
29636    } elsif($l=~/^ *354 /o) {
29637
29638        $this->{myheader}=~s/X-Assp-Intended-For:$HeaderValueRe//giso; # clear out existing X-Assp-Envelope-From headers
29639        $this->{body}=~s/^($HeaderRe*)/$1From: sender not supplied\r\n/o unless $this->{body}=~/^$HeaderRe*From:/io; # add From: if missing
29640        $this->{body}=~s/^($HeaderRe*)/$1Subject:\r\n/o unless $this->{body}=~/^$HeaderRe*Subject:/io; # add Subject: if missing
29641
29642        $this->{saveprepend}.=$this->{saveprepend2};
29643        $this->{body}=~s/^Subject:/Subject: $this->{saveprepend}/gim if ($spamTagCC && $this->{saveprepend} );
29644
29645        $this->{body}=~s/^Subject:/Subject: $spamSubjectEnc/gimo if $spamSubjectCC && $spamSubjectEnc;
29646
29647# remove Disposition-Notification headers if needed
29648
29649        $this->{body} =~ s/(?:ReturnReceipt|Return-Receipt-To|Disposition-Notification-To):$HeaderValueRe//gios
29650            if ($removeDispositionNotification);
29651
29652        # merge our header, add X-Intended-For header
29653        $this->{myheader} = headerFormat($this->{myheader});
29654		my ($to) = $this->{rcpt} =~ /(\S+)/;
29655    	my ($mfd) = $to =~ /\@(.*)/;
29656    	$this->{newrcpt}="";
29657    	foreach my $adr ( split( " ", $this->{rcpt} ) ) {
29658 			$this->{newrcpt} .= "$adr " if $adr =~ /$mfd/;
29659 			last if $AddIntendedForHeader == 1;
29660    	}
29661        $this->{body}=~s/^($HeaderRe*)/$1\r\n\n\n\r$this->{myheader}X-Assp-Intended-For: $this->{newrcpt}\r\nX-Assp-Copy-Spam: yes\r\n/o;
29662        $this->{body}=~s/\r?\n?\r\n\n\n\r/\r\n/o;
29663        my $maxbytes = $MaxBytes > 10000 ? $MaxBytes + $this->{headerlength} : 10000 + $this->{headerlength};
29664        $this->{body} = substr($this->{body},0,$maxbytes) if $ccMaxBytes && $MaxBytes;
29665
29666        my $clamavbytes = $ClamAVBytes ? $ClamAVBytes : 50000;
29667        $clamavbytes = 100000 if $ClamAVBytes>100000;
29668        $this->{mailfrom} = $this->{from};
29669        $this->{ip} = $this->{fromIP};
29670        if ($ScanCC &&
29671                   $this->{body}  &&
29672                   ((haveToScan($fh) && ! ClamScanOK($fh,\substr($this->{body},0,$clamavbytes))) or
29673                    (haveToFileScan($fh) && ! FileScanOK($fh,\substr($this->{body},0,$clamavbytes)))
29674                   )
29675           ) {
29676           mlog($fh,"info: skip forwarding message to $this->{to_as} - virus found") if $ConnectionLog;
29677           @{$Con{$fh}->{to}} = (); undef @{$Con{$fh}->{to}};
29678           done2($fh);
29679           return;
29680        }
29681
29682        $this->{body} =~ s/\r?\n/\r\n/gos;
29683        $this->{body} =~ s/[\r\n\.]+$//os;
29684
29685
29686
29687        sendque($fh,$this->{body}) if $this->{body};
29688        sendque($fh,"\r\n.\r\n");
29689        delete $this->{body};
29690        mlog($fh,"info: message forwarded to $this->{to_as}") if $ConnectionLog;
29691        delete $this->{mailfrom};
29692        delete $this->{ip};
29693        $Con{$fh}->{getline}=\&FSdone;
29694    }
29695}
29696
29697sub FSdone { my ($fh,$l)=@_;
29698
29699    if($l=~/^ *[54]/o) {
29700        FSabort($fh,"done Expected 250, got: $l");
29701    } elsif($l=~/^ *250 /o) {
29702        NoLoopSyswrite($fh,"QUIT\r\n");
29703        @{$Con{$fh}->{to}} = (); undef @{$Con{$fh}->{to}};
29704        done2($fh); # close and delete
29705    }
29706}
29707sub FSabort {mlog(0,"FSabort: $_[1]"); @{$Con{$_[0]}->{to}} = (); undef @{$Con{$_[0]}->{to}};done2($_[0]);}
29708
29709
29710sub haveToFileScan {
29711    my $fh=shift;
29712    return 0 unless $fh;
29713    my $this=$Con{$fh};
29714
29715    my $DoFileScan = $DoFileScan;    # copy the global to local - using local from this point
29716
29717
29718    return 0 if !$DoFileScan;
29719    return 0 if $this->{noscan};
29720    return 0 if $this->{filescandone}==1;
29721    return 0 if $this->{whitelisted} && $ScanWL!=1;
29722    return 0 if $this->{noprocessing} && $ScanNP!=1;
29723    return 0 if $this->{relayok} && $ScanLocal!=1;
29724    if ($noScan && matchSL($this->{mailfrom},'noScan')) {
29725        $this->{noscan} = 1;
29726        return 0;
29727    }
29728
29729    if (($noScanIP && matchIP($this->{ip},'noScanIP',$fh)) ||
29730        ($NoScanRe  && $this->{ip}=~/$NoScanReRE/) ||
29731        ($NoScanRe  && $this->{helo}=~/$NoScanReRE/) ||
29732        ($NoScanRe  && $this->{mailfrom}=~/$NoScanReRE/))
29733    {
29734        $this->{noscan} = 1;
29735        return 0;
29736    }
29737    $this->{prepend}='';
29738
29739    return 1;
29740}
29741
29742
29743sub FileScanOK {
29744    my ($fh,$b)=@_;
29745    return 1 if (! haveToFileScan($fh));
29746    return 1 unless $FileScanCMD;
29747    my $this = $Con{$fh};
29748    my $failed;
29749    my $cmd;
29750    my $res;
29751    my $virusname;
29752
29753   	my $msg = $$b;
29754   	my $lb = length($msg);
29755    $this->{FileScanOK} = 1;
29756    if ($NoScanRe && $NoScanReRE ne '' && $msg=~('('.$NoScanReRE.')')) {
29757        mlogRe($1,"NoVirusscan");
29758        return 1;
29759    }
29760
29761
29762    my $mtype = '';
29763    $mtype = "whitelisted"   if $this->{whitelisted};
29764    $mtype = "noprocessing"  if $this->{noprocessing};
29765    $mtype = "local"         if $this->{relayok};
29766
29767    my $file = $FileScanDir . "/" . int(rand(100000)) . "$maillogExt";
29768
29769    my $SF;
29770    eval {
29771        open $SF, ">","$file";
29772        binmode $SF;
29773        print $SF substr($msg,0,$lb);
29774        close $SF;
29775      };
29776    my $wait;
29777    $wait = $1 if ($FileScanCMD =~ /^\s*NORUN\s*\-\s*(\d+)/i);
29778    Time::HiRes::sleep($wait / 1000) if $wait;
29779
29780    if (-r $file) {
29781        if ($FileScanCMD !~ /^\s*NORUN/i) {
29782            my $runfile = $file;
29783            my $rundir = $FileScanDir;
29784            if ( $^O eq "MSWin32" ) {
29785                $runfile =~ s/\//\\/g;
29786                $runfile = '"' . $runfile .'"' if $runfile =~ / /;
29787                $rundir =~ s/\//\\/g;
29788                $rundir = '"' . $rundir .'"' if $rundir =~ / /;
29789            } else {
29790                $runfile = "'" . $runfile ."'" if $runfile =~ / /;
29791                $rundir = "'" . $rundir ."'" if $rundir =~ / /;
29792            }
29793
29794            $cmd = "$FileScanCMD 2>&1";
29795            $cmd =~ s/FILENAME/$runfile/ig;
29796            $cmd =~ s/NUMBER/$WorkerNumber/ig;
29797            $cmd =~ s/FILESCANDIR/$rundir/ig;
29798            d("filescan: running - $cmd");
29799            mlog($fh,"diagnostic: FileScan will run command - $cmd") if $ScanLog == 3;
29800
29801            $res = qx($cmd);
29802
29803
29804            $res =~ s/\r//g;
29805            $res =~ s/\n/ /g;
29806            $res =~ s/\t/ /g;
29807            mlog($fh,"diagnostic: FileScan returned $res") if $ScanLog == 3;
29808
29809            $failed = 1 if ($FileScanBad && $FileScanBadRE ne ''  && $res =~ ('('.$FileScanBadRE.')'));
29810            $failed = 1 if ($FileScanGood && $FileScanGoodRE ne ''  && $res !~ ('('.$FileScanGoodRE.')'));
29811        }
29812        eval{unlink $file;};
29813
29814        mlog($fh,"FileScan: scanned $lb bytes in $mtype message",1)
29815            if(($failed && $ScanLog ) || $ScanLog >= 2);
29816        return 1 unless $failed;
29817    } else {
29818         mlog($fh,"FileScan: is unable find temporary $file - possibly removed by the file system scanner") if $ScanLog >= 2;
29819        $res = 'unable to find file to scan';
29820        $failed = 1;
29821    }
29822
29823        if($failed) {
29824        ($virusname) = $res =~ /($FileScanRespReRE)/;
29825
29826        if($virusname && $SuspiciousVirus && $virusname=~/($SuspiciousVirusRE)/){
29827            my $susp = $1;
29828            $this->{messagereason}="SuspiciousVirus: $virusname '$susp'";
29829            pbAdd($fh,$this->{ip},&weightRe($vsValencePB,'SuspiciousVirus',\$susp,$fh),"SuspiciousVirus-FileScan:$virusname",1);
29830            $this->{prepend}="[VIRUS][scoring]";
29831            mlog($fh,"'$virusname' passing the virus check because of only suspicious virus '$susp'");
29832            return 1;
29833        }
29834
29835        $this->{prepend}="[VIRUS]";
29836        if ($DoFileScan == 2) {
29837            $this->{prepend}="[VIRUS][monitor]";
29838            mlog($fh,"message is infected but pass - $res");
29839            return 1;
29840        }
29841        $virusname = 'a virus' unless $virusname;
29842        $this->{averror}=$AvError;
29843        $this->{averror}=~s/INFECTION/$virusname/gi;
29844        my $reportheader;
29845        $reportheader="Full Header:\r\n$this->{header}\r\n" if $EmailVirusReportsHeader;
29846        my $sub="virus detected: 'FileScan'";
29847
29848        my $bod="Message ID: $this->{msgtime}\r\n";
29849        $bod.="Remote IP: $this->{ip}\r\n";
29850        $bod.="Subject: $this->{subject2}\r\n";
29851        $bod.="Sender: $this->{mailfrom}\r\n";
29852        $bod.="Recipients(s): $this->{rcpt}\r\n";
29853        $bod.="Virus Detected: 'FileScan' - $res\r\n";
29854
29855        my $rfile="reports/virusreport.txt";
29856		GetReportFile($fh, $rfile, $sub );
29857        &sendNotification ($EmailFrom, $EmailVirusReportsTo,$this->{subject},"$this->{body}\n\n$bod$reportheader")
29858			if $EmailVirusReportsTo ;
29859        # Send virus report to recipient if set
29860        &sendNotification ($EmailFrom, $this->{rcpt},$this->{subject},"$this->{body}\n\n$bod")
29861        	if $EmailVirusReportsToRCPT && !$this->{relayok};
29862
29863        $Stats{viridetected}++;
29864        delayWhiteExpire($fh);
29865        $this->{messagereason}="virus detected: 'FileScan' - $res";
29866        pbAdd($fh,$this->{ip},$vdValencePB,"virus-FileScan") if $vdValencePB>0;
29867
29868        return 0;
29869    } else {
29870        mlog($fh,"info: FileScan - message is not infected") if $ScanLog >= 2;
29871        return 1;
29872    }
29873}
29874
29875
29876sub haveToScan {
29877    my $fh   = shift;
29878    my $this = $Con{$fh};
29879	return 0 if !$AvailAvClamd;
29880    return 0 if !$UseAvClamd;
29881    return 0 if !$CanUseAvClamd;
29882    return 0
29883      if ( $this->{noscan}
29884        || $noScan && matchSL( $this->{mailfrom}, 'noScan' ) );
29885    return 0 if $this->{clamscandone};
29886
29887    return 0 if $this->{whitelisted} && !$ScanWL;
29888    return 0 if $this->{noprocessing}==1 && !$ScanNP;
29889    return 0 if $this->{noprocessing}==2;
29890    return 0 if $this->{relayok} && !$ScanLocal;
29891    return 0 if $noScanIP && matchIP($this->{ip},'noScanIP');
29892    return 0 if $NoScanRe  && $this->{ip}=~('('.$NoScanReRE.')');
29893    return 0 if $NoScanRe  && $this->{helo}=~('('.$NoScanReRE.')');
29894    return 0 if $NoScanRe  && $this->{mailfrom}=~('('.$NoScanReRE.')');
29895    $this->{prepend} = "";
29896
29897    if ( $NoScanRe && $NoScanReRE != "" && $b =~ ( '(' . $NoScanReRE . ')' ) ) {
29898        mlogRe( $1, "NoVirusscan" );
29899        return 0;
29900    }
29901    return 1;
29902}
29903
29904
29905sub pingScan {
29906
29907    my $av = new File::Scan::ClamAV( port => $AvClamdPort );
29908
29909        if ( $av->ping() ) {
29910
29911            $VerAvClamd         = $av->VERSION;
29912#            mlog( 0, 'ClamAv Up Again' ) if $ScanLog && $AvailAvClamd == 0;
29913            $AvailAvClamd = 1;
29914            $CommentAvClamd = "installed and ClamAv up";
29915        }
29916        undef $av;
29917    }
29918# substitutes File::Scan::ClamAV::ping
29919sub ClamScanPing {
29920 my ($self) = @_;
29921 my $response;
29922 my $timeout = $ClamAVtimeout / 2;
29923 $timeout = 5 if $timeout < 5;
29924 d('ClamScanPing - maxwait ' . $timeout * 2 . ' seconds');
29925
29926 my $conn = $self->_get_connection || return;
29927 my $select = IO::Select->new();
29928 $select->add($conn);
29929
29930 my @canwrite = $select->can_write(int($timeout));
29931 if (@canwrite) {
29932     $self->_send($conn, "PING\n");
29933
29934     my @canread = $select->can_read(int($timeout));
29935
29936     if (@canread) {
29937         chomp($response = $conn->getline);
29938
29939     # Run out the buffer?
29940         1 while (<$conn>);
29941     } else {
29942         $response = 'unable to read from Socket';
29943     }
29944 } else {
29945     $response = 'unable to write to Socket';
29946 }
29947 $select->remove($conn);
29948 $conn->close;
29949
29950 return ($response eq 'PONG' ? 1 : $self->_seterrstr("Unknown reponse from ClamAV service: $response"));
29951}
29952
29953# substitutes File::Scan::ClamAV::streamscan
29954sub ClamScanScan {
29955 my ($self) = shift;
29956 my $response;
29957 my $timeout = $ClamAVtimeout / 2;
29958 $timeout = 2 if $timeout < 2;
29959 d('ClamScanScan - maxwait ' . $timeout + $ClamAVtimeout . ' seconds');
29960
29961 my $data = join '', @_;
29962
29963 $self->_seterrstr;
29964
29965 my $conn = $self->_get_connection || return;
29966 my $select = IO::Select->new();
29967 $select->add($conn);
29968
29969 my @canwrite = $select->can_write(int($timeout));
29970 if (@canwrite) {
29971     $self->_send($conn, "STREAM\n");
29972     chomp($response = $conn->getline);
29973 }
29974
29975 my @return;
29976 if($response =~ /^PORT (\d+)/){
29977	if((my $c = $self->_get_tcp_connection($1))){
29978                my $stream = IO::Select->new();
29979                $stream->add($c);
29980                my @cwrite = $stream->can_write(int($timeout));
29981                if (@cwrite) {
29982		    $self->_send($c, $data);
29983                    $stream->remove($c);
29984		    $c->close;
29985
29986                    my @canread = $select->can_read(int($ClamAVtimeout));
29987                    if (@canread) {
29988		        chomp(my $r = $conn->getline);
29989		        if($r =~ /stream: (.+) FOUND/i){
29990		   	    @return = ('FOUND', $1);
29991		        } else {
29992			    @return = ('OK');
29993		        }
29994                    }
29995                }
29996	} else {
29997                $select->remove($conn);
29998                $conn->close;
29999		return;
30000	}
30001 }
30002 $select->remove($conn);
30003 $conn->close;
30004 return @return;
30005 }
30006
30007sub ClamScanOK {
30008    my ($fh,$bd)=@_;
30009    return 1 if (! haveToScan($fh));
30010    d('ClamAV');
30011    my $av;
30012    my $errstr;
30013    my $this = $Con{$fh};
30014
30015
30016    $this->{clamscandone} = 1 ;
30017
30018
30019
30020
30021    my $mtype = '';
30022    $mtype = "whitelisted"   if $this->{whitelisted};
30023    $mtype = "noprocessing"  if $this->{noprocessing};
30024    $mtype = "local"         if $this->{relayok};
30025
30026    my $lb = length($$bd);
30027    my $timeout = $ClamAVtimeout;
30028    my ( $code, $virus );
30029	$this->{prepend}="[ClamAV]";
30030    &sigoffTry(__LINE__);
30031    eval {
30032   	local $SIG{ALRM} = sub { die "__alarm__\n" };
30033     	alarm($timeout) if $timeout;
30034        $av = new File::Scan::ClamAV( port => $AvClamdPort );
30035        if ( $av->ping() ) {
30036            mlog(0, 'ClamAv Up') if $ScanLog && $AvailAvClamd==0 ;
30037			$CommentAvClamd = "<span class=positive>installed and ready</span>";
30038            $AvailAvClamd = 1;
30039            ( $code, $virus ) = $av->streamscan($$bd);
30040        } else {
30041            mlog(0, 'ClamAv Down') if $ScanLog && $AvailAvClamd==1 ;
30042			$CommentAvClamd = "<span class=negative>installed but down</span>";
30043
30044            $AvailAvClamd = 0;
30045        }
30046
30047        $errstr = $av->errstr();
30048        alarm(0);
30049    };
30050    alarm(0);
30051    if ($@) {
30052        if ( $@ =~ /__alarm__/o ) {
30053            mlog( $fh, "ClamAV: streamscan timed out after $timeout secs.", 1 );
30054        } else {
30055            mlog( $fh, "ClamAV: streamscan failed: $@", 1 );
30056        }
30057        undef $av;
30058
30059        return 1;
30060    }
30061    unless ($AvailAvClamd) {
30062
30063        return 1;
30064    }
30065    undef $av;
30066    $this->{prepend}="[VIRUS]";
30067    mlog($fh,"ClamAV: scanned $lb bytes in $mtype message - $code $virus")
30068        if((!( $virus eq '') || !($code eq 'OK')) && $ScanLog ) || $ScanLog >= 2;
30069
30070    if($code eq 'OK'){
30071        return 1;
30072    } elsif ($SuspiciousVirus && $virus=~/($SuspiciousVirusRE)/) {
30073        my $SV = $1;
30074        $this->{messagereason}="SuspiciousVirus: $virus '$SV'";
30075        my $w = &weightRe($vsValencePB,'SuspiciousVirus', \$SV,$fh);
30076        pbAdd($fh,$this->{ip},$w,"SuspiciousVirus-ClamAV:$virus",1);
30077        $this->{prepend}="[VIRUS][scoring]";
30078        mlog($fh,"[scoring:$w] '$virus' not blocked but scored because '$SV' is in SuspiciousVirus");
30079        return 1;
30080    } elsif($code eq 'FOUND'){
30081
30082        $this->{averror}=$AvError;
30083        $this->{averror}=~s/INFECTION/$virus/go;
30084
30085        #mlog($fh,"virus detected '$virus'");
30086        my $reportheader;$reportheader="Full Header:\r\n$this->{header}\r\n" if $EmailVirusReportsHeader;
30087        my $sub="virus detected: '$virus'";
30088
30089        my $bod="Message ID: $this->{msgtime}\r\n";
30090        $bod.="Remote IP: $this->{ip}\r\n";
30091        $bod.="Subject: $this->{subject2}\r\n";
30092        $bod.="Sender: $this->{mailfrom}\r\n";
30093        $bod.="Recipients(s): $this->{rcpt}\r\n";
30094        $bod.="Virus Detected: '$virus'\r\n";
30095        $reportheader = $bod.$reportheader;
30096
30097        my $file="reports/virusreport.txt";
30098
30099        # Send virus report to administrator if set
30100        AdminReportMail($sub,\$reportheader,$EmailVirusReportsTo) if $EmailVirusReportsTo;
30101
30102
30103        # Send virus report to recipient if set
30104        $this->{reportaddr} = 'virus';
30105        ReturnMail($fh,$this->{rcpt},"$base/$file",$sub,\$bod,'') if $EmailVirusReportsToRCPT;
30106
30107		delete $this->{reportaddr};
30108        $Stats{viridetected}++;
30109        delayWhiteExpire($fh);
30110        $this->{messagereason}="virus detected: '$virus'";
30111        pbAdd($fh,$this->{ip},$vdValencePB,"$virus") if $vdValencePB>0;
30112
30113        return 0;
30114    }
30115
30116    $AvailAvClamd = 0;
30117    $CommentAvClamd = "<span class=negative>installed but temporary off</span>";
30118    $CommentAvClamd = "<span class=negative>installed but temporary off: $errstr</span>" if $errstr;
30119    mlog(0, "ClamAv Temporary Off : $errstr") if $ScanLog && $errstr;
30120    return 1;
30121}
30122
30123#####################################################################################
30124#                Web Configuration functions
30125# add multiple tooltips span tags
30126sub addShowPath {
30127 my ($text)=@_;
30128 my $ret;
30129
30130   while ($text=~/\-\> (.*\.$maillogExt)/cgso) { # /c - keep pos() on match fail
30131
30132
30133    $ret.=<<EOT;
30134$1<span onclick="popFileEditor($text,4);">$2</span>
30135EOT
30136    chomp($ret);
30137
30138   }
30139   $ret.=$1 if $text=~/\G(.*)/s; # remainder
30140   $text=$ret;
30141   return $text;
30142 }
30143
30144sub statRequest {
30145    my ( $tempfh, $fh, $head, $data ) = @_;
30146    my $v;
30147    %statRequests = (
30148        '/'    => \&ConfigStatsRaw,
30149        '/raw' => \&ConfigStatsRaw,
30150        '/xml' => \&ConfigStatsXml
30151    );
30152    my $i = 0;
30153
30154    # %head -- public hash
30155    (%head) = map { ++$i % 2 ? lc $_ : $_ } map /^([^ :]*)[: ]{0,2}(.*)/,
30156      split( /\r\n/, $head );
30157    my ( $page, $qs ) =
30158      ( $head{get} || $head{head} || $head{post} ) =~ /^([^\? ]+)(?:\?(\S*))?/;
30159    if ( defined $data ) {    # GET, POST order
30160        $qs .= '&' if ( $qs ne '' );
30161        $qs .= $data;
30162    }
30163    $qs =~ y/+/ /;
30164    $i = 0;
30165
30166    # parse query string, get rid of google autofill
30167    # %qs -- public hash
30168    (%qs) = map {my $t = $_; $t =~ s/(e)_(mail)/$1$2/gi if ++$i % 2; $t } split( /[=&]/, $qs );
30169    foreach my $k ( keys %qs ) {
30170        $qs{$k} =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack('C',hex($1))/ge;
30171    }
30172    my $ip   = $fh->peerhost();
30173    $ip = "[" . $ip . "]" if ($ip =~ /:/);
30174    my $port = $fh->peerport();
30175    mlog( '', "stat connection from $ip:$port;" );
30176
30177    $Stats{statConn}++;
30178
30179    if ( defined( $v = $statRequests{$page} ) ) {
30180        print $tempfh $v->( $head, $qs );
30181    }
30182}
30183
30184sub webRequest {
30185    my ( $tempfh, $fh, $head, $data ) = @_;
30186    my $v;
30187    my $k;
30188    %webRequests = (
30189        '/lists' 			=> \&ConfigLists,
30190        '/recprepl' 		=> \&CheckRcptRepl,
30191        '/maillog'        	=> \&ConfigMaillog,
30192        '/analyze'        	=> \&ConfigAnalyze,
30193        '/infostats'      	=> \&ConfigStats,
30194        '/edit'           	=> \&ConfigEdit,
30195        '/shutdown'       				=> \&Shutdown,
30196
30197        '/shutdown_frame' 				=> \&ShutdownFrame,
30198
30199        '/shutdown_list'  				=> \&ShutdownList,
30200        '/connections_list'  				=> \&ConnectionsList,
30201		'/remember' 	  	=> \&remember,
30202        '/donations' 		=> \&Donations,
30203        '/get'       		=> \&GetFile,
30204        '/syncedit' 		=> \&syncedit,
30205        '/addraction' 		=> \&ConfigAddrAction,
30206        '/ipaction' 		=> \&ConfigIPAction
30207    );
30208    my $i = 0;
30209
30210    # %head -- public hash
30211    (%head) = map { ++$i % 2 ? lc $_ : $_ } map /^([^ :]*)[: ]{0,2}(.*)/,
30212      split( /\r\n/, $head );
30213    my ( $page, $qs ) =
30214      ( $head{get} || $head{head} || $head{post} ) =~ /^([^\? ]+)(?:\?(\S*))?/;
30215   	$currentPage = $page;
30216    $currentPage =~ s/^\/+//;
30217    $currentPage = 'Config' unless $currentPage;
30218    $currentPage = ucfirst($currentPage);
30219    $headers =~ s/<title>\S+ ASSP/<title>$currentPage ASSP/ if $page ne '/get' && exists $webRequests{$page};
30220    if ( defined $data ) {    # GET, POST order
30221        $qs .= '&' if ( $qs ne '' );
30222        $qs .= $data;
30223    }
30224    $qs =~ y/+/ /;
30225    $i = 0;
30226
30227    # parse query string, get rid of google autofill
30228    # %qs -- public hash
30229    (%qs)=map{my$t=$_;$t=~s/(e)_(mail)/$1$2/gio if ++$i % 2; $t} split(/[=&]/o,$qs);
30230    while (($k,$v) =  each %qs) {$qs{$k}=~s/%([0-9a-fA-F][0-9a-fA-F])/pack('C',hex($1))/geo}
30231    my ($auth)=$head{authorization}=~/Basic (\S+)/io;
30232    my ($user,$pass)=split(':',base64decode($auth));
30233    my $ip   = $fh->peerhost();
30234    $ip = "[" . $ip . "]" if ($ip =~ /:/);
30235    my $port = $fh->peerport();
30236
30237    if ( substr( $Config{webAdminPassword}, 0, 2 ) eq "45" ) {
30238        $pass = crypt( $pass, "45" );
30239    }
30240
30241    if ( $pass eq $webAdminPassword || !$webAdminPassword ) {
30242        if ( $page !~ /shutdown_frame|shutdown_list|favicon.ico|get/i ) {
30243
30244            # only count requests for pages without meta refresh tag
30245            # dont count requests for favicon.ico file
30246            # dont count requests for 'get' page
30247            my $args;
30248            if ( $page =~ /edit/i ) {
30249                if ( defined( $qs{contents} ) ) {
30250                    if ( $qs{B1} =~ /delete/i ) {
30251                        $args = "deleting file '$qs{file}'";
30252                    } else {
30253                        $args = "writing file '$qs{file}'";
30254                    }
30255                } else {
30256                    $args = "reading file '$qs{file}'";
30257                    my $fil = $qs{file};
30258                     foreach my $dbGroupEntry (@dbGroup) {
30259           			 	my ( $KeyName, $dbConfig, $CacheObject, $realFileName ) =
30260              			split(/,/o,$dbGroupEntry);
30261						next if $realFileName eq "mysql";
30262						next if $realFileName eq "";
30263            			next if !$CacheObject;
30264            			next unless ( $fil =~ /$realFileName/ );
30265
30266
30267						SaveDB($CacheObject,$KeyName);
30268
30269            			last;
30270
30271   					}
30272
30273
30274
30275                }
30276            }
30277            if ($args) {
30278                mlog( '',
30279                    "admin connection from $ip:$port; page:$page; $args" );
30280                    optionFilesReload() if $args =~ /writing/i ;
30281                    optionFilesReload() if $args =~ /deleting/i ;
30282                 	ResetPB($qs{file}) if $args =~ /writing/i;
30283             		ResetPB($qs{file}) if $args =~ /deleting/i;
30284
30285            } else {
30286                mlog( '', "admin connection from $ip:$port; page:$page" );
30287            }
30288
30289            $Stats{admConn}++;
30290        }
30291        if ( $page =~ /quit/i ) {
30292            ConfigQuit($tempfh);
30293        }
30294
30295        if ( $page =~ /terminateprimary/i ) {
30296            ConfigTerminatePrimary($tempfh);
30297        }
30298
30299        if ( $page =~ /autorestart/i ) {
30300            ConfigRestart($tempfh);
30301            &downASSP("Restarted");
30302 			&restartCMD();
30303
30304        }
30305
30306        if ( $page =~ /restartsecondary/i ) {
30307            ConfigRestartSecondary($tempfh);
30308
30309        }
30310        if ( $page =~ /restartprimary/i ) {
30311            ConfigRestartPrimary($tempfh);
30312        }
30313        if ( $page =~ /reload/i ) {
30314            reloadConfigFile();
30315        }
30316        if ( $page =~ /save/i ) {
30317
30318            SaveConfig();
30319
30320        }
30321        if ( $page =~ /syncedit/i ) {
30322
30323
30324
30325        }
30326        if ( $page =~ /favicon.ico/i ) {
30327            print $tempfh "HTTP/1.1 404 Not Found
30328Content-type: text/html
30329
30330<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>Not found</h1>
30331</body></html>\n";
30332        } else {
30333            print $tempfh (
30334                ( defined( $v = $webRequests{$page} ) )
30335                ? $v->( $head, $qs )
30336                : webConfig( $head, $qs )
30337            );
30338        }
30339    } else {
30340
30341        print $tempfh "HTTP/1.1 401 Unauthorized
30342WWW-Authenticate: Basic realm=\"Anti-Spam SMTP Proxy (ASSP) Configuration\"
30343Content-type: text/html
30344
30345<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>Unauthorized</h1>
30346</body></html>\n";
30347    }
30348}
30349
30350
30351
30352sub webBlock {
30353    my $tempfh = shift;
30354    print $tempfh &webBlockText();
30355    return 1;
30356}
30357
30358sub webBlockText {
30359    return "HTTP/1.1 200 OK
30360Content-type: text/html
30361
30362
30363<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h3>permission denied - you are not allowed to start this action<br /><br />use the back button</h3>
30364</body></html>\n";
30365}
30366
30367sub ConfigQuit {
30368 my $fh=shift;
30369 mlog(0,"quit requested from admin interface",1);
30370 print $fh "HTTP/1.1 200 OK
30371Content-type: text/html
30372
30373
30374<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h3>ASSP Terminated.</h3>
30375</body></html>
30376";
30377 &downASSP("Terminated");
30378
30379exit 2;
30380
30381
30382}
30383sub ConfigRestart {
30384    mlog( 0, "restart requested from admin interface",1 );
30385
30386    my $fh = shift;
30387    print $fh "HTTP/1.1 200 OK
30388Content-type: text/html
30389
30390
30391<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h3>ASSP Restarted.</h3>
30392</body></html>
30393";
30394
30395
30396
30397}
30398sub ConfigRestartSecondary {
30399 my $fh=shift;
30400 my $time = &timestring();
30401 return if !$AutostartSecondary;
30402
30403 my $pid = &readSecondaryPID();
30404 mlog( 0, "restart Secondary requested from admin interface",1 );
30405 unlink("$base/$pidfile"."_Secondary");
30406 mlog( 0, "stopping Secondary($pid) ",1 );
30407 kill TERM => $pid if $pid;
30408 &startSecondary() if  $AutostartSecondary && !$AsASecondary && $webSecondaryPort;
30409}
30410
30411sub ConfigRestartPrimary {
30412 my $fh=shift;
30413 my $time = &timestring();
30414 my $pid = &checkPrimaryPID();
30415 print "\n$time Secondary (PID: $$): restarting primary ($pid)\n";
30416 print $fh "HTTP/1.1 200 OK
30417Content-type: text/html
30418
30419
30420<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>ASSP Primary Restarted.</h1>
30421</body></html>
30422" if $pid;
30423 print $fh "HTTP/1.1 200 OK
30424Content-type: text/html
30425
30426
30427<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>ASSP Primary Not Running.</h1>
30428</body></html>
30429" if !$pid;
30430
30431 kill QUIT => $pid if $pid;
30432}
30433
30434sub ConfigTerminatePrimary {
30435 my $fh=shift;
30436 my $time = &timestring();
30437 my $pid = &checkPrimaryPID();
30438 print "\n$time Secondary (PID: $$): terminating primary ($pid)\n";
30439 print $fh "HTTP/1.1 200 OK
30440Content-type: text/html
30441
30442
30443<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>ASSP Primary Terminated.</h1>
30444</body></html>
30445" if $pid;
30446 print $fh "HTTP/1.1 200 OK
30447Content-type: text/html
30448
30449
30450<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><body><h1>ASSP Primary Not Running.</h1>
30451</body></html>
30452" if !$pid;
30453
30454 kill INT => $pid if $pid;
30455}
30456# total current and previous stats
30457sub ComputeStatTotals {
30458
30459    my %totStats = %PrevStats;
30460    foreach my $k ( keys %Stats ) {
30461        if ( $k eq 'version' ) {
30462
30463            # just copy
30464            $totStats{$k} = $Stats{$k};
30465        } elsif ( $k eq 'smtpMaxConcurrentSessions' ) {
30466
30467            # pick greater value
30468            $totStats{$k} = $Stats{$k} if $Stats{$k} > $PrevStats{$k};
30469        } elsif ( $k eq 'starttime' ) {
30470
30471            # initialize if needed
30472            $totStats{$k} = $Stats{$k} unless $PrevStats{$k};
30473        } else {
30474
30475            #sum
30476            $totStats{$k} += $Stats{$k};
30477        }
30478    }
30479
30480    return %totStats;
30481}
30482
30483# compute various totals
30484sub statsTotals {
30485    my %s;
30486    $s{smtpConnIdleTimeout}   	= 	$Stats{smtpConnIdleTimeout};
30487    $s{smtpConnIdleTimeout2}  	= 	$AllStats{smtpConnIdleTimeout};
30488    $s{smtpConnSSLIdleTimeout}	=	$Stats{smtpConnSSLIdleTimeout};
30489    $s{smtpConnSSLIdleTimeout2}	=	$AllStats{smtpConnSSLIdleTimeout};
30490    $s{smtpConnTLSIdleTimeout}	=	$Stats{smtpConnTLSIdleTimeout};
30491    $s{smtpConnTLSIdleTimeout2}	=	$AllStats{smtpConnTLSIdleTimeout};
30492
30493    $s{smtpConnAcceptedTotal} 	= 	$Stats{smtpConn} + $Stats{smtpConnNotLogged};
30494    $s{smtpConnAcceptedTotal2} 	=
30495      $AllStats{smtpConn} + $AllStats{smtpConnNotLogged};
30496  	$s{smtpConnLimit}=$Stats{smtpConnLimit}+$Stats{smtpConnDomainIP}+$Stats{SameSubject}+$Stats{smtpConnLimitIP}+$Stats{smtpConnLimitFreq}+$Stats{AUTHErrors}+$Stats{delayConnection};
30497 	$s{smtpConnLimit2}=$AllStats{smtpConnLimit}+$AllStats{smtpConnDomainIP}+$AllStats{smtpConnLimitIP}+$AllStats{smtpConnLimitFreq}+$AllStats{AUTHErrors}+$AllStats{delayConnection};
30498
30499    $s{smtpConnRejectedTotal}=$s{smtpConnLimit}+$Stats{smtpConnDenied};
30500 	$s{smtpConnRejectedTotal2}=$s{smtpConnLimit2}+$AllStats{smtpConnDenied};
30501
30502    $s{smtpConnTotal} = $s{smtpConnAcceptedTotal} + $s{smtpConnRejectedTotal};
30503    $s{smtpConnTotal2} =
30504      $s{smtpConnAcceptedTotal2} + $s{smtpConnRejectedTotal2};
30505    $s{admConnTotal}   = $Stats{admConn} + $Stats{admConnDenied};
30506    $s{admConnTotal2}  = $AllStats{admConn} + $AllStats{admConnDenied};
30507    $s{statConnTotal}  = $Stats{statConn} + $Stats{statConnDenied};
30508    $s{statConnTotal2} = $AllStats{statConn} + $AllStats{statConnDenied};
30509    $s{rcptAcceptedLocal} =
30510      $Stats{rcptValidated} + $Stats{rcptUnchecked} + $Stats{rcptSpamLover};
30511    $s{rcptAcceptedLocal2} =
30512      $AllStats{rcptValidated} +
30513      $AllStats{rcptUnchecked} +
30514      $AllStats{rcptSpamLover};
30515    $s{rcptAcceptedRemote} =
30516      $Stats{rcptWhitelisted} + $Stats{rcptNotWhitelisted};
30517    $s{rcptAcceptedRemote2} =
30518      $AllStats{rcptWhitelisted} + $AllStats{rcptNotWhitelisted};
30519    $s{rcptUnprocessed}  = $Stats{rcptUnprocessed};
30520    $s{rcptUnprocessed2} = $AllStats{rcptUnprocessed};
30521    $s{rcptReport} =
30522      $Stats{rcptReportSpam} +
30523      $Stats{rcptReportHam} +
30524      $Stats{rcptReportWhitelistAdd} +
30525      $Stats{rcptReportWhitelistRemove} +
30526      $Stats{rcptReportRedlistAdd} +
30527      $Stats{rcptReportRedlistRemove};
30528    $s{rcptReport2} =
30529      $AllStats{rcptReportSpam} +
30530      $AllStats{rcptReportHam} +
30531      $AllStats{rcptReportWhitelistAdd} +
30532      $AllStats{rcptReportWhitelistRemove} +
30533      $AllStats{rcptReportRedlistAdd} +
30534      $AllStats{rcptReportRedlistRemove};
30535    $s{rcptAcceptedTotal} =
30536      $s{rcptAcceptedLocal} +
30537      $s{rcptAcceptedRemote} +
30538      $s{rcptUnprocessed} +
30539      $s{rcptReport};
30540    $s{rcptAcceptedTotal2} =
30541      $s{rcptAcceptedLocal2} +
30542      $s{rcptAcceptedRemote2} +
30543      $s{rcptUnprocessed2} +
30544      $s{rcptReport2};
30545    $s{rcptRejectedLocal} =
30546      $Stats{rcptNonexistent} +
30547      $Stats{rcptDelayed} +
30548      $Stats{rcptDelayedLate} +
30549      $Stats{rcptDelayedExpired} +
30550      $Stats{rcptEmbargoed} +
30551      $Stats{rcptSpamBucket};
30552    $s{rcptRejectedLocal2} =
30553      $AllStats{rcptNonexistent} +
30554      $AllStats{rcptDelayed} +
30555      $AllStats{rcptDelayedLate} +
30556      $AllStats{rcptDelayedExpired} +
30557      $AllStats{rcptEmbargoed} +
30558      $AllStats{rcptSpamBucket};
30559    $s{rcptRejectedRemote}  = $Stats{rcptRelayRejected};
30560    $s{rcptRejectedRemote2} = $AllStats{rcptRelayRejected};
30561    $s{rcptRejectedTotal}   = $s{rcptRejectedLocal} + $s{rcptRejectedRemote};
30562    $s{rcptRejectedTotal2}  = $s{rcptRejectedLocal2} + $s{rcptRejectedRemote2};
30563    $s{rcptTotal}           = $s{rcptAcceptedTotal} + $s{rcptRejectedTotal};
30564    $s{rcptTotal2}          = $s{rcptAcceptedTotal2} + $s{rcptRejectedTotal2};
30565    $s{msgAcceptedTotal} =
30566      $Stats{bhams} +
30567      $Stats{whites} +
30568      $Stats{locals} +
30569      $Stats{noprocessing} +
30570      $Stats{spamlover};
30571    $s{msgAcceptedTotal2} =
30572      $AllStats{bhams} +
30573      $AllStats{whites} +
30574      $AllStats{locals} +
30575      $AllStats{noprocessing} +
30576      $AllStats{spamlover};
30577    $s{msgRejectedTotal} =
30578      $Stats{preHeader} +
30579      $Stats{localFrequency} +
30580      $Stats{bspams} +
30581      $Stats{blacklisted} +
30582      $Stats{helolisted} +
30583      $Stats{spambucket} +
30584      $Stats{penaltytrap} +
30585      $Stats{viri} +
30586      $Stats{internaladdresses} +
30587      $Stats{smtpConnDenied} +
30588      $Stats{smtpConnDomainIP} +
30589      $Stats{SameSubject} +
30590      $Stats{smtpConnLimitFreq} +
30591      $Stats{viridetected} +
30592      $Stats{bombs} +
30593
30594      $Stats{msgverify} +
30595      $Stats{bombHeader} +
30596      $Stats{bombs} +
30597      $Stats{ptrMissing} +
30598      $Stats{ptrInvalid} +
30599	  $Stats{mxaMissing} +
30600      $Stats{forgedHelo} +
30601      $Stats{invalidHelo} +
30602      $Stats{pbdenied} +
30603      $Stats{pbextreme} +
30604      $Stats{denyConnection} +
30605      $Stats{denyStrict} +
30606      $Stats{sbblocked} +
30607      $Stats{msgscoring} +
30608      $Stats{senderInvalidLocals} +
30609      $Stats{scripts} +
30610      $Stats{spffails} +
30611      $Stats{rblfails} +
30612      $Stats{uriblfails} +
30613      $Stats{msgMSGIDtrErrors} +
30614      $Stats{msgBackscatterErrors} +
30615      $Stats{msgMaxErrors} +
30616#      $Stats{msgDelayed} +
30617#      $Stats{msgNoRcpt} +
30618      $Stats{msgNoSRSBounce};
30619    $s{msgRejectedTotal2} =
30620      $AllStats{preHeader} +
30621      $AllStats{localFrequency} +
30622      $AllStats{bspams} +
30623      $AllStats{blacklisted} +
30624      $AllStats{helolisted} +
30625      $AllStats{spambucket} +
30626      $AllStats{penaltytrap} +
30627      $AllStats{viri} +
30628      $AllStats{internaladdresses} +
30629      $AllStats{smtpConnDenied} +
30630      $AllStats{smtpConnDomainIP} +
30631      $AllStats{smtpConnLimitFreq} +
30632      $AllStats{viridetected} +
30633      $AllStats{bombs} +
30634
30635      $AllStats{msgverify} +
30636      $AllStats{bombHeader} +
30637      $AllStats{bombBlack} +
30638      $AllStats{ptrMissing} +
30639      $AllStats{ptrInvalid} +
30640	  $AllStats{mxaMissing} +
30641      $AllStats{forgedHelo} +
30642      $AllStats{invalidHelo} +
30643      $AllStats{pbdenied} +
30644      $AllStats{pbextreme} +
30645      $AllStats{denyConnection} +
30646      $AllStats{denyStrict} +
30647      $AllStats{sbblocked} +
30648      $AllStats{msgscoring} +
30649      $AllStats{senderInvalidLocals} +
30650      $AllStats{scripts} +
30651      $AllStats{spffails} +
30652      $AllStats{rblfails} +
30653      $AllStats{uriblfails} +
30654      $AllStats{msgMSGIDtrErrors} +
30655      $AllStats{msgBackscatterErrors} +
30656      $AllStats{msgMaxErrors} +
30657#      $AllStats{msgDelayed} +
30658#      $AllStats{msgNoRcpt} +
30659      $AllStats{msgNoSRSBounce};
30660    $s{msgTotal}  = $s{msgAcceptedTotal} + $s{msgRejectedTotal};
30661    $s{msgTotal2} = $s{msgAcceptedTotal2} + $s{msgRejectedTotal2};
30662    %s;
30663}
30664
30665sub ConfigStats {
30666	if ($qs{ResetAllStats}) {
30667     	%OldStats = ();
30668     	%AllStats = ();
30669     	$AllStats{starttime} = time;
30670
30671     	rename("$base/asspstats.sav","$base/stats/asspstats-".timestring('','','YYYY-MM-DD-hh-mm-ss').'.sav');
30672     	ResetStats();
30673 	} elsif ($qs{ResetStats}) {
30674     	ResetStats();
30675 	}
30676    SaveStats();
30677    my %tots = statsTotals();
30678    delete $qs{ResetAllStats};
30679 	delete $qs{ResetStats};
30680    my $upt  = ( time - $Stats{starttime} ) / ( 24 * 3600 );
30681    my $upt2 = ( time - $AllStats{starttime} ) / ( 24 * 3600 );
30682
30683    my $uptime    = getTimeDiffAsString( time - $Stats{starttime}, 1 );
30684    my $resettime = localtime( $AllStats{starttime} );
30685    my $uptime2   = getTimeDiffAsString( time - $AllStats{starttime} );
30686    my $mpd       = sprintf( "%.1f", $upt == 0 ? 0 : $tots{msgTotal} / $upt );
30687    my $mpd2 = sprintf( "%.1f", $upt2 == 0 ? 0 : $tots{msgTotal2} / $upt2 );
30688    my $pct  = sprintf( "%.1f",
30689        $tots{msgTotal} - $Stats{locals} == 0
30690        ? 0
30691        : 100 * $tots{msgRejectedTotal} / ( $tots{msgTotal} - $Stats{locals} )
30692    );
30693    my $pct2;
30694    $pct2 = sprintf( "%.1f",
30695        $tots{msgTotal2} - $AllStats{locals} == 0
30696        ? 0
30697        : 100 *
30698          $tots{msgRejectedTotal2} /
30699          ( $tots{msgTotal2} - $AllStats{locals} ) );
30700    my $cpu=$CanStatCPU ? sprintf("%.2f%%",100*$cpuUsage) : 'n/a';
30701 	my $cpuAvg;$cpuAvg=sprintf(" (%.2f%% avg)",$Stats{cpuTime}==0 ? 0 : 100*$Stats{cpuBusyTime}/$Stats{cpuTime}) if $CanStatCPU;
30702 	my $cpuAvg2=$CanStatCPU ? sprintf("%.2f%% avg",$AllStats{cpuTime}==0 ? 0 : 100*$AllStats{cpuBusyTime}/$AllStats{cpuTime}) : 'n/a';
30703
30704    my $LocalDNSStatus;
30705
30706 if ($UseLocalDNS) {
30707     $LocalDNSStatus = "Local <a href=\"/#UseLocalDNS\">DNS Servers</a> in use";
30708 } else {
30709     $LocalDNSStatus = "Custom <a href=\"/#DNSServers\">DNS servers</a> in use";
30710 }
30711 my $reset = 'reset';
30712 my $restart = 'reset or restart';
30713 my $fil = "asspstats.sav";
30714 my $currentCL = (-e "$base/docs/changelog.txt") ? "docs/changelog.txt" : '';
30715 my $currentCLtext = $currentCL ? '<a href="javascript:void(0);" onclick="javascript:popFileEditor(\'docs/changelog.txt\',8);">show changelog</a>' : '&nbsp;';
30716 $uptime2 = "<a href=\"javascript:void(0);\" title=\"click to reset all stats to zero\" onclick=\"if (confirm('reset all STATS ?')) {WaitDiv();window.location.href='/infostats?ResetAllStats=1';}\">$uptime2</a>";
30717 $reset = "<a href=\"javascript:void(0);\" title=\"click to reset all stats to zero\" onclick=\"if (confirm('reset all STATS ?')) {WaitDiv();window.location.href='/infostats?ResetAllStats=1';}\">reset</a>";
30718     $uptime = "<a href=\"javascript:void(0);\" title=\"click to reset all stats since last start to zero\" onclick=\"if (confirm('reset current STATS ?')) {WaitDiv();window.location.href='/infostats?ResetStats=1';}\">$uptime</a>";
30719     $restart = "<a href=\"javascript:void(0);\" title=\"click to reset all stats since last start to zero\" onclick=\"if (confirm('reset current STATS ?')) {WaitDiv();window.location.href='/infostats?ResetStats=1';}\">reset</a> or restart";
30720
30721
30722	<<EOT;
30723$headerHTTP
30724$headerDTDTransitional
30725$headers
30726$footers
30727<script type=\"text/javascript\">
30728<!--
30729  function toggleTbody(id) {
30730    if (document.getElementById) {
30731      var tbod = document.getElementById(id);
30732      if (tbod && typeof tbod.className == 'string') {
30733        if (tbod.className == 'off') {
30734          tbod.className = 'on';
30735        } else {
30736          tbod.className = 'off';
30737        }
30738      }
30739    }
30740    return false;
30741  }
30742//-->
30743</script>
30744   <div class="content">
30745      <h2>
30746        ASSP Information and Statistics
30747      </h2><br />
30748      <table class="statBox">
30749      <thead>
30750          <tr>
30751            <td colspan="5" class="sectionHeader" onmousedown=
30752            "toggleTbody('StatItem0')">
30753              Server Information
30754            </td>
30755          </tr>
30756        </thead>
30757
30758        <tbody id="StatItem0" class="off">
30759          <tr>
30760            <td class="statsOptionTitle">
30761              Server Name:
30762            </td>
30763            <td class="statsOptionValue" colspan="2">
30764              $localhostname
30765            </td>
30766            <td class="statsOptionValue" colspan="2">
30767              &nbsp;
30768            </td>
30769          </tr>
30770          <tr>
30771            <td class="statsOptionTitle">
30772              Server OS:
30773            </td>
30774            <td class="statsOptionValue" colspan="2">
30775              $^O
30776            </td>
30777            <td class="statsOptionValue" colspan="2">
30778              &nbsp;
30779            </td>
30780          </tr>
30781          <tr>
30782            <td class="statsOptionTitle">
30783              Server IP:
30784            </td>
30785            <td class="statsOptionValue" colspan="2">
30786              $localhostip
30787            </td>
30788            <td class="statsOptionValue" colspan="2">
30789              &nbsp;
30790            </td>
30791          </tr>
30792          <tr>
30793            <td class="statsOptionTitle">
30794              DNS Servers:
30795            </td>
30796            <td class="statsOptionValue" colspan="2">
30797              $nameserversrt
30798            </td>
30799            <td class="statsOptionValue" colspan="2">
30800				$LocalDNSStatus
30801            </td>
30802          </tr>
30803          <tr>
30804            <td class="statsOptionTitle">
30805              Perl Version:
30806            </td>
30807            <td class="statsOptionValue" colspan="2">
30808              $]
30809            </td>
30810            <td class="statsOptionValueC" colspan="2">
30811              <a href="http://www.perl.org/get.html" rel=
30812              "external">Perl.org</a>
30813            </td>
30814          </tr>
30815          <tr>
30816            <td  class="statsOptionTitle">
30817              ASSP Version:
30818            </td>
30819
30820            <td class="statsOptionValue" colspan="2">
30821              <table>
30822               <tr>
30823                <td rowspan="2">
30824                 $version$modversion
30825                </td>
30826                <td class="statsOptionValueC">
30827                 $currentCLtext
30828                </td>
30829               </tr>
30830               <tr>
30831                <td class="statsOptionValueC">
30832                 <a href="$ChangeLogURL" rel="external" target="_blank">show changelog</a>
30833                </td>
30834               </tr>
30835              </table>
30836            </td>
30837            <td class="statsOptionValueC">
30838              <a href=
30839              "http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1x/assp.pl.gz"
30840              rel="external">stable release</a>
30841            </td>
30842            <td class="statsOptionValueC">
30843              <a href= http://downloads.sourceforge.net/project/assp/ASSP%20Installation/AutoUpdate/ASSP1dev/assp.pl.gz rel="external">development release</a>
30844            </td>
30845          </tr>
30846          <tr>
30847            <td  class="statsOptionTitle">
30848              ASSP Warnings:
30849            </td>
30850
30851            <td class="statsOptionValue" colspan="4">
30852              $asspWarnings
30853            </td>
30854
30855          </tr>
30856          <tr>
30857            <td class="statsOptionValue" style="background-color: #FFFFFF">
30858              &nbsp;
30859            </td>
30860            <td class="statsOptionValue" style="background-color: #FFFFFF"
30861            colspan="2">
30862              &nbsp;
30863            </td>
30864            <td class="statsOptionValueC" style="background-color: #FFFFFF"
30865            colspan="2">
30866              <font size="1" color="#C0C0C0"><em>downloads</em></font>
30867            </td>
30868          </tr>
30869        </tbody>
30870        <tbody>
30871          <tr>
30872            <td class="sectionHeader" onmousedown="toggleTbody('StatItem2')"
30873            colspan="5">
30874              Perl Modules
30875            </td>
30876          </tr>
30877        </tbody>
30878        <tbody id="StatItem2" class="off">
30879          <tr>
30880            <td class="statsOptionTitle">
30881              Compress::Zlib
30882            </td>
30883            <td class="statsOptionValue">
30884              $VerCompressZlib
30885            </td>
30886            <td class="statsOptionValue">
30887              $CommentCompressZlib
30888            </td>
30889            <td class="statsOptionValueC" colspan="2">
30890              <a href="http://search.cpan.org/search?query=Compress::Zlib" rel=
30891              "external">CPAN</a>
30892            </td>
30893          </tr>
30894          <tr>
30895            <td class="statsOptionTitle">
30896              Digest::MD5
30897            </td>
30898            <td class="statsOptionValue" >
30899              $VerDigestMD5
30900            </td>
30901             <td class="statsOptionValue" >
30902              $CommentDigestMD5
30903            </td>
30904            <td class="statsOptionValueC" colspan="2">
30905              <a href="http://search.cpan.org/search?query=Digest::MD5" rel=
30906              "external">CPAN</a>
30907            </td>
30908          </tr>
30909          <tr>
30910            <td class="statsOptionTitle">
30911              Digest::SHA1
30912            </td>
30913            <td class="statsOptionValue" >
30914              $VerDigestSHA1
30915            </td>
30916             <td class="statsOptionValue" >
30917              $CommentDigestSHA1
30918            </td>
30919            <td class="statsOptionValueC" colspan="2">
30920              <a href="http://search.cpan.org/search?query=Digest::SHA1" rel=
30921              "external">CPAN</a>
30922            </td>
30923          </tr>
30924          <tr>
30925            <td class="statsOptionTitle">
30926              Email::Valid
30927            </td>
30928            <td class="statsOptionValue" >
30929              $VerEmailValid
30930            </td>
30931             <td class="statsOptionValue" >
30932              $CommentEmailValid
30933            </td>
30934            <td class="statsOptionValueC" colspan="2">
30935              <a href="http://search.cpan.org/search?query=Email::Valid" rel=
30936              "external">CPAN</a>
30937            </td>
30938          </tr>
30939          <tr>
30940            <td class="statsOptionTitle">
30941              Email::Send
30942            </td>
30943            <td class="statsOptionValue" >
30944              $VerEMS
30945            </td>
30946            <td class="statsOptionValue" >
30947              $CommentEMS
30948            </td>
30949            <td class="statsOptionValueC" colspan="2">
30950              <a href="http://search.cpan.org/search?query=Email::Send" rel=
30951              "external">CPAN</a>
30952            </td>
30953          </tr>
30954           <tr>
30955            <td class="statsOptionTitle">
30956              Email::MIME
30957            </td>
30958            <td class="statsOptionValue" >
30959              $VerEMM
30960            </td>
30961            <td class="statsOptionValue" >
30962              $CommentEMM
30963            </td>
30964            <td class="statsOptionValueC" colspan="2">
30965              <a href="http://search.cpan.org/search?query=Email::MIME" rel=
30966              "external">CPAN</a>
30967            </td>
30968          </tr>
30969          <tr>
30970            <td class="statsOptionTitle">
30971              File::ReadBackwards
30972            </td>
30973            <td class="statsOptionValue" >
30974              $VerFileReadBackwards
30975            </td>
30976            </td>
30977            <td class="statsOptionValue" >
30978              $CommentFileReadBackwards
30979            </td>
30980            <td class="statsOptionValueC" colspan="2">
30981              <a href="http://search.cpan.org/search?query=File::ReadBackwards"
30982              rel="external">CPAN</a>
30983            </td>
30984          </tr>
30985          <tr>
30986            <td class="statsOptionTitle">
30987              File::Scan::ClamAV
30988            </td>
30989            <td class="statsOptionValue" >
30990              $VerAvClamd
30991            </td>
30992             <td class="statsOptionValue">
30993              $CommentAvClamd
30994            </td>
30995            <td class="statsOptionValueC" colspan="2">
30996              <a href="http://search.cpan.org/search?query=File::Scan::ClamAV"
30997              rel="external">CPAN</a>
30998            </td>
30999          </tr>
31000          <tr>
31001            <td class="statsOptionTitle">
31002              IO::Socket::INET6
31003            </td>
31004            <td class="statsOptionValue" >
31005              $VerIOSocketINET6
31006            </td>
31007            <td class="statsOptionValue" >
31008              $CommentIOSocketINET6
31009            </td>
31010            <td class="statsOptionValueC" colspan="2">
31011              <a href="http://search.cpan.org/search?query=IO::Socket::INET6" rel=
31012              "external">CPAN</a>
31013            </td>
31014          </tr>
31015          <tr>
31016            <td class="statsOptionTitle">
31017              IO::Socket::SSL
31018            </td>
31019            <td class="statsOptionValue" >
31020              $VerIOSocketSSL
31021            </td>
31022            <td class="statsOptionValue" >
31023              $CommentIOSocketSSL
31024            </td>
31025            <td class="statsOptionValueC" colspan="2">
31026              <a href="http://search.cpan.org/search?query=IO::Socket::SSL" rel=
31027              "external">CPAN</a>
31028            </td>
31029          </tr>
31030          <tr>
31031            <td class="statsOptionTitle">
31032              LWP::Simple
31033            </td>
31034            <td class="statsOptionValue">
31035              $VerLWP
31036            </td>
31037            <td class="statsOptionValue">
31038              $CommentLWP
31039            </td>
31040            <td class="statsOptionValueC" colspan="2">
31041              <a rel="external" href=
31042              "http://search.cpan.org/search?query=LWP::Simple">CPAN</a>
31043            </td>
31044          </tr>
31045
31046            <tr>
31047            <td class="statsOptionTitle">
31048              Authen::SASL
31049            </td>
31050            <td class="statsOptionValue">
31051              $VerAuthenSASL
31052            </td>
31053            <td class="statsOptionValue">
31054              $CommentAuthenSASL
31055            </td>
31056            <td class="statsOptionValueC" colspan="2">
31057              <a href="http://search.cpan.org/search?query=Authen::SASL"
31058              rel="external">CPAN</a>
31059            </td>
31060          </tr>
31061           <tr>
31062            <td class="statsOptionTitle">
31063              Mail::SPF
31064            </td>
31065            <td class="statsOptionValue">
31066              $VerMailSPF
31067            </td>
31068            <td class="statsOptionValue">
31069              $CommentMailSPF
31070            </td>
31071            <td class="statsOptionValueC" colspan="2">
31072              <a href="http://search.cpan.org/search?query=Mail::SPF"
31073              rel="external">CPAN</a>
31074            </td>
31075          </tr>
31076          <tr>
31077            <td class="statsOptionTitle">
31078              Mail::SRS
31079            </td>
31080            <td class="statsOptionValue" >
31081              $VerMailSRS
31082            </td>
31083            <td class="statsOptionValue" >
31084              $CommentMailSRS
31085            </td>
31086            <td class="statsOptionValueC" colspan="2">
31087              <a href="http://search.cpan.org/search?query=Mail::SRS" rel=
31088              "external">CPAN</a>
31089            </td>
31090          </tr>
31091          <tr>
31092            <td class="statsOptionTitle">
31093              Net::CIDR::Lite
31094            </td>
31095            <td class="statsOptionValue" >
31096              $VerCIDRlite
31097            </td>
31098            <td class="statsOptionValue" >
31099              $CommentCIDRlite
31100            </td>
31101            <td class="statsOptionValueC" colspan="2">
31102              <a rel="external" href=
31103              "http://search.cpan.org/search?query=Net::CIDR::Lite">CPAN</a>
31104            </td>
31105          </tr>
31106          <tr>
31107            <td class="statsOptionTitle">
31108              Net::DNS
31109            </td>
31110            <td class="statsOptionValue" >
31111              $VerNetDNS
31112            </td>
31113            <td class="statsOptionValue">
31114              $CommentNetDNS
31115            </td>
31116            <td class="statsOptionValueC" colspan="2">
31117              <a href="http://search.cpan.org/search?query=Net::DNS" rel=
31118              "external">CPAN</a>
31119            </td>
31120          </tr>
31121
31122          <tr>
31123            <td class="statsOptionTitle">
31124              Net::SMTP
31125            </td>
31126            <td class="statsOptionValue" >
31127              $VerNetSMTP
31128            </td>
31129            <td class="statsOptionValue" >
31130              $CommentNetSMTP
31131            </td>
31132            <td class="statsOptionValueC" colspan="2">
31133              <a href="http://search.cpan.org/search?query=Net::SMTP" rel=
31134              "external">CPAN</a>
31135            </td>
31136          </tr>
31137          <tr>
31138            <td class="statsOptionTitle">
31139              Net::IP::Match::Regexp
31140            </td>
31141            <td class="statsOptionValue" >
31142              $VerCIDR
31143            </td>
31144            <td class="statsOptionValue" >
31145              $CommentCIDR
31146            </td>
31147            <td class="statsOptionValueC" colspan="2">
31148              <a rel="external" href=
31149              "http://search.cpan.org/search?query=Net::IP::Match::Regexp">CPAN</a>
31150            </td>
31151          </tr>
31152          <tr>
31153            <td class="statsOptionTitle">
31154              Net::LDAP
31155            </td>
31156            <td class="statsOptionValue" >
31157              $VerNetLDAP
31158            </td>
31159            <td class="statsOptionValue" >
31160              $CommentNetLDAP
31161            </td>
31162            <td class="statsOptionValueC" colspan="2">
31163              <a href="http://search.cpan.org/search?query=Net::LDAP" rel=
31164              "external">CPAN</a>
31165            </td>
31166          </tr>
31167        	<tr>
31168            <td class="statsOptionTitle">
31169              Net::SenderBase
31170            </td>
31171            <td class="statsOptionValue" >
31172              $VerSenderBase
31173            </td>
31174            <td class="statsOptionValue" >
31175              $CommentSenderBase
31176            </td>
31177            <td class="statsOptionValueC" colspan="2">
31178              <a rel="external" href=
31179              "http://search.cpan.org/search?query=Net::SenderBase">CPAN</a>
31180            </td>
31181          </tr>
31182          <tr>
31183            <td class="statsOptionTitle">
31184              Sys::Syslog
31185            </td>
31186            <td class="statsOptionValue" >
31187              $VerSysSyslog
31188            </td>
31189            <td class="statsOptionValue" >
31190              $CommentSysSyslog
31191            </td>
31192            <td class="statsOptionValueC" colspan="2">
31193              <a href="http://search.cpan.org/search?query=Sys::Syslog" rel=
31194              "external">CPAN</a>
31195            </td>
31196          </tr>
31197          <tr>
31198            <td class="statsOptionTitle">
31199              Net::Syslog
31200            </td>
31201            <td class="statsOptionValue" >
31202              $VerNetSyslog
31203            </td>
31204            <td class="statsOptionValue" >
31205              $CommentNetSyslog
31206            </td>
31207            <td class="statsOptionValueC" colspan="2">
31208              <a href="http://search.cpan.org/search?query=Net::Syslog" rel=
31209              "external">CPAN</a>
31210            </td>
31211          </tr>
31212
31213          <tr>
31214            <td class="statsOptionTitle">
31215              Tie::RDBM
31216            </td>
31217            <td class="statsOptionValue" >
31218              $VerRDBM
31219            </td>
31220             <td class="statsOptionValue" >
31221              $CommentRDBM
31222            </td>
31223            <td class="statsOptionValueC" colspan="2">
31224              <a rel="external" href=
31225              "http://search.cpan.org/search?query=Tie::RDBM">CPAN</a>
31226            </td>
31227          </tr>
31228          <tr>
31229            <td class="statsOptionTitle">
31230              Time::HiRes
31231            </td>
31232            <td class="statsOptionValue" >
31233              $VerTimeHiRes
31234            </td>
31235             <td class="statsOptionValue" >
31236              $CommentTimeHiRes
31237            </td>
31238            <td class="statsOptionValueC" colspan="2">
31239              <a href="http://search.cpan.org/search?query=Time::HiRes" rel=
31240              "external">CPAN</a>
31241            </td>
31242          </tr>
31243          <tr>
31244            <td class="statsOptionTitle">
31245              Win32::Daemon
31246            </td>
31247            <td class="statsOptionValue" >
31248              $VerWin32Daemon
31249            </td>
31250            <td class="statsOptionValue" >
31251              $CommentWin32Daemon
31252            </td>
31253            <td class="statsOptionValueC" colspan="2">
31254              <a rel="external" href=
31255              "http://www.roth.net/perl/Daemon/">roth.net</a>
31256            </td>
31257          </tr>
31258          <tr>
31259            <td class="statsOptionValue" style="background-color: #FFFFFF">
31260              &nbsp;
31261            </td>
31262            <td class="statsOptionValue" style="background-color: #FFFFFF"
31263            colspan="2">
31264              &nbsp;
31265            </td>
31266            <td class="statsOptionValueC" style="background-color: #FFFFFF"
31267            colspan="2">
31268              <font size="1" color="#C0C0C0"><em>downloads</em></font>
31269            </td>
31270          </tr>
31271        </tbody>
31272
31273        <tbody>
31274          <tr>
31275            <td class="sectionHeader" onmousedown="toggleTbody('StatItem3')"
31276            colspan="5">
31277              General Runtime Information
31278            </td>
31279          </tr>
31280        </tbody>
31281        <tbody id="StatItem3" class="on">
31282          <tr>
31283            <td class="statsOptionTitle">
31284              ASSP Proxy Uptime:
31285            </td>
31286            <td class="statsOptionValue" colspan="2">
31287              $uptime
31288            </td>
31289            <td class="statsOptionValue" colspan="2">
31290              $uptime2
31291            </td>
31292          </tr>
31293          <tr>
31294            <td class="statsOptionTitle">
31295              Messages Processed:
31296            </td>
31297            <td class="statsOptionValue" colspan="2">
31298              $tots{msgTotal} ($mpd per day)
31299            </td>
31300            <td class="statsOptionValue" colspan="2">
31301              $tots{msgTotal2} ($mpd2 per day)
31302            </td>
31303          </tr>
31304          <tr>
31305            <td class="statsOptionTitle">
31306              Non-Local Mail Blocked:
31307            </td>
31308            <td class="statsOptionValue" colspan="2">
31309              $pct%
31310            </td>
31311            <td class="statsOptionValue" colspan="2">
31312              $pct2%
31313            </td>
31314          </tr>
31315          <tr>
31316            <td class="statsOptionTitle">
31317              CPU Usage:
31318            </td>
31319            <td class="statsOptionValue" colspan="2">
31320              $cpu$cpuAvg
31321            </td>
31322            <td class="statsOptionValue" colspan="2">
31323              $cpuAvg2
31324            </td>
31325          </tr>
31326          <tr>
31327            <td class="statsOptionTitle">
31328              Concurrent SMTP Sessions:
31329            </td>
31330            <td class="statsOptionValue" colspan="2">
31331              $smtpConcurrentSessions ($Stats{smtpMaxConcurrentSessions} high / $maxSMTPSessions max)
31332            </td>
31333            <td class="statsOptionValue" colspan="2">
31334             $AllStats{smtpMaxConcurrentSessions} high
31335            </td>
31336          </tr>
31337          <tr>
31338            <td class="statsOptionValue" style="background-color: #FFFFFF">
31339              &nbsp;
31340            </td>
31341            <td class="statsOptionValue" style="background-color: #FFFFFF"
31342            colspan="2">
31343              <font size="1" color="#C0C0C0"><em>since $restart at $starttime</em></font>
31344            </td>
31345            <td class="statsOptionValue" style="background-color: #FFFFFF"
31346            colspan="2">
31347              <font size="1" color="#C0C0C0"><em>since $reset at $resettime</em></font>
31348            </td>
31349          </tr>
31350        </tbody>
31351        <tbody>
31352          <tr>
31353            <td colspan="5" class="sectionHeader" onmousedown=
31354            "toggleTbody('StatItem4')">
31355              Totaled Statistics
31356            </td>
31357          </tr>
31358        </tbody>
31359        <tbody id="StatItem4" class="off">
31360          <tr>
31361            <td class="statsOptionTitle">
31362              SMTP Connections Received:
31363            </td>
31364            <td class="statsOptionValue" colspan="2">
31365              $tots{smtpConnTotal}
31366            </td>
31367            <td class="statsOptionValue" colspan="2">
31368              $tots{smtpConnTotal2}
31369            </td>
31370          </tr>
31371          <tr>
31372            <td class="statsOptionTitle">
31373              &nbsp;&nbsp;&nbsp;&nbsp;SMTP Connections Accepted:
31374            </td>
31375            <td class="statsOptionValue" colspan="2">
31376              $tots{smtpConnAcceptedTotal}
31377            </td>
31378            <td class="statsOptionValue" colspan="2">
31379              $tots{smtpConnAcceptedTotal2}
31380            </td>
31381          </tr>
31382          <tr>
31383            <td class="statsOptionTitle">
31384              &nbsp;&nbsp;&nbsp;&nbsp;SMTP Connections Rejected:
31385            </td>
31386            <td class="statsOptionValue" colspan="2">
31387              $tots{smtpConnRejectedTotal}
31388            </td>
31389            <td class="statsOptionValue" colspan="2">
31390              $tots{smtpConnRejectedTotal2}
31391            </td>
31392          </tr>
31393          <tr>
31394            <td class="statsOptionTitle">
31395              Envelope Recipients Processed:
31396            </td>
31397            <td class="statsOptionValue" colspan="2">
31398              $tots{rcptTotal}
31399            </td>
31400            <td class="statsOptionValue" colspan="2">
31401              $tots{rcptTotal2}
31402            </td>
31403          </tr>
31404          <tr>
31405            <td class="statsOptionTitle">
31406              &nbsp;&nbsp;&nbsp;&nbsp;Envelope Recipients Accepted:
31407            </td>
31408            <td class="statsOptionValue" colspan="2">
31409              $tots{rcptAcceptedTotal}
31410            </td>
31411            <td class="statsOptionValue" colspan="2">
31412              $tots{rcptAcceptedTotal2}
31413            </td>
31414          </tr>
31415          <tr>
31416            <td class="statsOptionTitle">
31417              &nbsp;&nbsp;&nbsp;&nbsp;Envelope Recipients Rejected:
31418            </td>
31419            <td class="statsOptionValue" colspan="2">
31420              $tots{rcptRejectedTotal}
31421            </td>
31422            <td class="statsOptionValue" colspan="2">
31423              $tots{rcptRejectedTotal2}
31424            </td>
31425          </tr>
31426          <tr>
31427            <td class="statsOptionTitle">
31428              Messages Processed:
31429            </td>
31430            <td class="statsOptionValue" colspan="2">
31431              $tots{msgTotal}
31432            </td>
31433            <td class="statsOptionValue" colspan="2">
31434              $tots{msgTotal2}
31435            </td>
31436          </tr>
31437          <tr>
31438            <td class="statsOptionTitle">
31439              &nbsp;&nbsp;&nbsp;&nbsp;Messages Passed:
31440            </td>
31441            <td class="statsOptionValue" colspan="2">
31442              $tots{msgAcceptedTotal}
31443            </td>
31444            <td class="statsOptionValue" colspan="2">
31445              $tots{msgAcceptedTotal2}
31446            </td>
31447          </tr>
31448          <tr>
31449            <td class="statsOptionTitle">
31450              &nbsp;&nbsp;&nbsp;&nbsp;Messages Rejected:
31451            </td>
31452            <td class="statsOptionValue" colspan="2">
31453              $tots{msgRejectedTotal}
31454            </td>
31455            <td class="statsOptionValue" colspan="2">
31456              $tots{msgRejectedTotal2}
31457            </td>
31458          </tr>
31459          <tr>
31460            <td class="statsOptionTitle">
31461              Admin Connections Received:
31462            </td>
31463            <td class="statsOptionValue" colspan="2">
31464              $tots{admConnTotal}
31465            </td>
31466            <td class="statsOptionValue" colspan="2">
31467              $tots{admConnTotal2}
31468            </td>
31469          </tr>
31470          <tr>
31471            <td class="statsOptionTitle">
31472              &nbsp;&nbsp;&nbsp;&nbsp;Admin Connections Accepted:
31473            </td>
31474            <td class="statsOptionValue" colspan="2">
31475              $Stats{admConn}
31476            </td>
31477            <td class="statsOptionValue" colspan="2">
31478              $AllStats{admConn}
31479            </td>
31480          </tr>
31481          <tr>
31482            <td class="statsOptionTitle">
31483              &nbsp;&nbsp;&nbsp;&nbsp;Admin Connections Rejected:
31484            </td>
31485            <td class="statsOptionValue" colspan="2">
31486              $Stats{admConnDenied}
31487            </td>
31488            <td class="statsOptionValue" colspan="2">
31489              $AllStats{admConnDenied}
31490            </td>
31491          </tr>
31492          <tr>
31493            <td class="statsOptionTitle">
31494              Stat Connections Received:
31495            </td>
31496            <td class="statsOptionValue" colspan="2">
31497              $tots{statConnTotal}
31498            </td>
31499            <td class="statsOptionValue" colspan="2">
31500              $tots{statConnTotal2}
31501            </td>
31502          </tr>
31503          <tr>
31504            <td class="statsOptionTitle">
31505              &nbsp;&nbsp;&nbsp;&nbsp;Stat Connections Accepted:
31506            </td>
31507            <td class="statsOptionValue" colspan="2">
31508              $Stats{statConn}
31509            </td>
31510            <td class="statsOptionValue" colspan="2">
31511              $AllStats{statConn}
31512            </td>
31513          </tr>
31514          <tr>
31515            <td class="statsOptionTitle">
31516              &nbsp;&nbsp;&nbsp;&nbsp;Stat Connections Rejected:
31517            </td>
31518            <td class="statsOptionValue" colspan="2">
31519              $Stats{statConnDenied}
31520            </td>
31521            <td class="statsOptionValue" colspan="2">
31522              $AllStats{statConnDenied}
31523            </td>
31524          </tr>
31525          <tr>
31526            <td class="statsOptionValue" style="background-color: #FFFFFF">
31527              &nbsp;
31528            </td>
31529            <td class="statsOptionValue" style="background-color: #FFFFFF"
31530            colspan="2">
31531              <font size="1" color="#C0C0C0"><em>since $restart at $starttime</em></font>
31532            </td>
31533            <td class="statsOptionValue" style="background-color: #FFFFFF"
31534            colspan="2">
31535              <font size="1" color="#C0C0C0"><em>since $reset at $resettime</em></font>
31536            </td>
31537          </tr>
31538        </tbody>
31539        <tbody>
31540          <tr>
31541            <td colspan="5" class="sectionHeader" onmousedown=
31542            "toggleTbody('StatItem5')">
31543              SMTP Connection Statistics
31544            </td>
31545          </tr>
31546        </tbody>
31547        <tbody id="StatItem5" class="off">
31548          <tr>
31549            <td class="statsOptionTitle">
31550              Accepted Logged SMTP Connections:
31551            </td>
31552            <td class="statsOptionValue positive" colspan="2">
31553              $Stats{smtpConn}
31554            </td>
31555            <td class="statsOptionValue positive" colspan="2">
31556              $AllStats{smtpConn}
31557            </td>
31558          </tr>
31559          <tr>
31560            <td class="statsOptionTitle">
31561              Not Logged SMTP Connections:
31562            </td>
31563            <td class="statsOptionValue positive" colspan="2">
31564              $Stats{smtpConnNotLogged}
31565            </td>
31566            <td class="statsOptionValue positive" colspan="2">
31567              $AllStats{smtpConnNotLogged}
31568            </td>
31569          </tr>
31570          <tr>
31571            <td class="statsOptionTitle">
31572              SMTP Connection Limits:
31573            </td>
31574            <td class="statsOptionValue negative" colspan="2">
31575              $tots{smtpConnLimit}
31576            </td>
31577            <td class="statsOptionValue negative" colspan="2">
31578              $tots{smtpConnLimit2}
31579            </td>
31580          </tr>
31581          <tr>
31582            <td class="statsOptionTitle">
31583              &nbsp;&nbsp;&nbsp;&nbsp;Overall Limits:
31584            </td>
31585            <td class="statsOptionValue negative" colspan="2">
31586              $Stats{smtpConnLimit}
31587            </td>
31588            <td class="statsOptionValue negative" colspan="2">
31589              $AllStats{smtpConnLimit}
31590            </td>
31591          </tr>
31592          <tr>
31593            <td class="statsOptionTitle">
31594              &nbsp;&nbsp;&nbsp;&nbsp;By IP Limits:
31595            </td>
31596            <td class="statsOptionValue negative" colspan="2">
31597              $Stats{smtpConnLimitIP}
31598            </td>
31599            <td class="statsOptionValue negative" colspan="2">
31600              $AllStats{smtpConnLimitIP}
31601            </td>
31602          </tr>
31603          <tr>
31604            <td class="statsOptionTitle">
31605              &nbsp;&nbsp;&nbsp;&nbsp;By Simple IP Delay:
31606            </td>
31607            <td class="statsOptionValue negative" colspan="2">
31608              $Stats{delayConnection}
31609            </td>
31610            <td class="statsOptionValue negative" colspan="2">
31611              $AllStats{delayConnection}
31612            </td>
31613          </tr>
31614          <tr>
31615            <td class="statsOptionTitle">
31616              &nbsp;&nbsp;&nbsp;&nbsp;By AUTH Errors Count:
31617            </td>
31618            <td class="statsOptionValue negative" colspan="2">
31619              $Stats{AUTHErrors}
31620            </td>
31621            <td class="statsOptionValue negative" colspan="2">
31622              $AllStats{AUTHErrors}
31623            </td>
31624          </tr>
31625          <tr>
31626            <td class="statsOptionTitle">
31627              &nbsp;&nbsp;&nbsp;&nbsp;By IP Frequency Limits:
31628            </td>
31629            <td class="statsOptionValue negative" colspan="2">
31630              $Stats{smtpConnLimitFreq}
31631            </td>
31632            <td class="statsOptionValue negative" colspan="2">
31633              $AllStats{smtpConnLimitFreq}
31634            </td>
31635          </tr>
31636          <tr>
31637            <td class="statsOptionTitle">
31638              &nbsp;&nbsp;&nbsp;&nbsp;By Domain IP Limits:
31639            </td>
31640            <td class="statsOptionValue negative" colspan="2">
31641              $Stats{smtpConnDomainIP}
31642            </td>
31643            <td class="statsOptionValue negative" colspan="2">
31644              $AllStats{smtpConnDomainIP}
31645            </td>
31646          </tr>
31647          <tr>
31648            <td class="statsOptionTitle">
31649              &nbsp;&nbsp;&nbsp;&nbsp;By Same Subject Limits:
31650            </td>
31651            <td class="statsOptionValue negative" colspan="2">
31652              $Stats{SameSubject}
31653            </td>
31654            <td class="statsOptionValue negative" colspan="2">
31655              $AllStats{smtpConnDomainIP}
31656            </td>
31657          </tr>
31658          <tr>
31659            <td class="statsOptionTitle">
31660              SMTP Connections Timeout:
31661            </td>
31662            <td class="statsOptionValue negative" colspan="2">
31663              $tots{smtpConnIdleTimeout}
31664            </td>
31665            <td class="statsOptionValue negative" colspan="2">
31666              $tots{smtpConnIdleTimeout2}
31667            </td>
31668          </tr>
31669          <tr>
31670            <td class="statsOptionTitle">
31671              SMTP SSL-Connections Timeout:
31672            </td>
31673            <td class="statsOptionValue negative" colspan="2">
31674              $tots{smtpConnSSLIdleTimeout}
31675            </td>
31676            <td class="statsOptionValue negative" colspan="2">
31677              $tots{smtpConnSSLIdleTimeout2}
31678            </td>
31679          </tr>
31680	      <tr>
31681            <td class="statsOptionTitle">
31682              SMTP TLS-Connections Timeout:
31683            </td>
31684            <td class="statsOptionValue negative" colspan="2">
31685              $tots{smtpConnTLSIdleTimeout}
31686            </td>
31687            <td class="statsOptionValue negative" colspan="2">
31688              $tots{smtpConnTLSIdleTimeout2}
31689            </td>
31690          </tr>
31691
31692          <tr>
31693            <td class="statsOptionValue" style="background-color: #FFFFFF">
31694              &nbsp;
31695            </td>
31696            <td class="statsOptionValue" style="background-color: #FFFFFF"
31697            colspan="2">
31698              <font size="1" color="#C0C0C0"><em>since $restart at $starttime</em></font>
31699            </td>
31700            <td class="statsOptionValue" style="background-color: #FFFFFF"
31701            colspan="2">
31702              <font size="1" color="#C0C0C0"><em>since $reset at $resettime</em></font>
31703            </td>
31704          </tr>
31705        </tbody>
31706        <tbody>
31707          <tr>
31708            <td colspan="5" class="sectionHeader" onmousedown=
31709            "toggleTbody('StatItem6')">
31710              Envelope Recipient Statistics
31711            </td>
31712          </tr>
31713        </tbody>
31714        <tbody id="StatItem6" class="off">
31715          <tr>
31716            <td class="statsOptionTitle">
31717              Local Recipients Accepted:
31718            </td>
31719            <td class="statsOptionValue positive" colspan="2">
31720              $tots{rcptAcceptedLocal}
31721            </td>
31722            <td class="statsOptionValue positive" colspan="2">
31723              $tots{rcptAcceptedLocal2}
31724            </td>
31725          </tr>
31726          <tr>
31727            <td class="statsOptionTitle">
31728              &nbsp;&nbsp;&nbsp;&nbsp;Validated Recipients:
31729            </td>
31730            <td class="statsOptionValue positive" colspan="2">
31731              $Stats{rcptValidated}
31732            </td>
31733            <td class="statsOptionValue positive" colspan="2">
31734              $AllStats{rcptValidated}
31735            </td>
31736          </tr>
31737          <tr>
31738            <td class="statsOptionTitle">
31739              &nbsp;&nbsp;&nbsp;&nbsp;Unchecked Recipients:
31740            </td>
31741            <td class="statsOptionValue positive" colspan="2">
31742              $Stats{rcptUnchecked}
31743            </td>
31744            <td class="statsOptionValue positive" colspan="2">
31745              $AllStats{rcptUnchecked}
31746            </td>
31747          </tr>
31748          <tr>
31749            <td class="statsOptionTitle">
31750              &nbsp;&nbsp;&nbsp;&nbsp;SpamLover Recipients:
31751            </td>
31752            <td class="statsOptionValue positive" colspan="2">
31753              $Stats{rcptSpamLover}
31754            </td>
31755            <td class="statsOptionValue positive" colspan="2">
31756              $AllStats{rcptSpamLover}
31757            </td>
31758          </tr>
31759          <tr>
31760            <td class="statsOptionTitle">
31761              Remote Recipients Accepted:
31762            </td>
31763            <td class="statsOptionValue positive" colspan="2">
31764              $tots{rcptAcceptedRemote}
31765            </td>
31766            <td class="statsOptionValue positive" colspan="2">
31767              $tots{rcptAcceptedRemote2}
31768            </td>
31769          </tr>
31770          <tr>
31771            <td class="statsOptionTitle">
31772              &nbsp;&nbsp;&nbsp;&nbsp;Whitelisted Recipients:
31773            </td>
31774            <td class="statsOptionValue positive" colspan="2">
31775              $Stats{rcptWhitelisted}
31776            </td>
31777            <td class="statsOptionValue positive" colspan="2">
31778              $AllStats{rcptWhitelisted}
31779            </td>
31780          </tr>
31781          <tr>
31782            <td class="statsOptionTitle">
31783              &nbsp;&nbsp;&nbsp;&nbsp;Not Whitelisted Recipients:
31784            </td>
31785            <td class="statsOptionValue positive" colspan="2">
31786              $Stats{rcptNotWhitelisted}
31787            </td>
31788            <td class="statsOptionValue positive" colspan="2">
31789              $AllStats{rcptNotWhitelisted}
31790            </td>
31791          </tr>
31792          <tr>
31793            <td class="statsOptionTitle">
31794              Noprocessed Recipients:
31795            </td>
31796            <td class="statsOptionValue positive" colspan="2">
31797              $Stats{rcptUnprocessed}
31798            </td>
31799            <td class="statsOptionValue positive" colspan="2">
31800              $AllStats{rcptUnprocessed}
31801            </td>
31802          </tr>
31803          <tr>
31804            <td class="statsOptionTitle">
31805              Email Reports:
31806            </td>
31807            <td class="statsOptionValue positive" colspan="2">
31808              $tots{rcptReport}
31809            </td>
31810            <td class="statsOptionValue positive" colspan="2">
31811              $tots{rcptReport2}
31812            </td>
31813          </tr>
31814          <tr>
31815            <td class="statsOptionTitle">
31816              &nbsp;&nbsp;&nbsp;&nbsp;Spam Reports:
31817            </td>
31818            <td class="statsOptionValue positive" colspan="2">
31819              $Stats{rcptReportSpam}
31820            </td>
31821            <td class="statsOptionValue positive" colspan="2">
31822              $AllStats{rcptReportSpam}
31823            </td>
31824          </tr>
31825          <tr>
31826            <td class="statsOptionTitle">
31827              &nbsp;&nbsp;&nbsp;&nbsp;Ham Reports:
31828            </td>
31829            <td class="statsOptionValue positive" colspan="2">
31830              $Stats{rcptReportHam}
31831            </td>
31832            <td class="statsOptionValue positive" colspan="2">
31833              $AllStats{rcptReportHam}
31834            </td>
31835          </tr>
31836          <tr>
31837            <td class="statsOptionTitle">
31838              &nbsp;&nbsp;&nbsp;&nbsp;Whitelist Additions:
31839            </td>
31840            <td class="statsOptionValue positive" colspan="2">
31841              $Stats{rcptReportWhitelistAdd}
31842            </td>
31843            <td class="statsOptionValue positive" colspan="2">
31844              $AllStats{rcptReportWhitelistAdd}
31845            </td>
31846          </tr>
31847          <tr>
31848            <td class="statsOptionTitle">
31849              &nbsp;&nbsp;&nbsp;&nbsp;Whitelist Deletions:
31850            </td>
31851            <td class="statsOptionValue positive" colspan="2">
31852              $Stats{rcptReportWhitelistRemove}
31853            </td>
31854            <td class="statsOptionValue positive" colspan="2">
31855              $AllStats{rcptReportWhitelistRemove}
31856            </td>
31857          </tr>
31858          <tr>
31859            <td class="statsOptionTitle">
31860              &nbsp;&nbsp;&nbsp;&nbsp;Redlist Additions:
31861            </td>
31862            <td class="statsOptionValue positive" colspan="2">
31863              $Stats{rcptReportRedlistAdd}
31864            </td>
31865            <td class="statsOptionValue positive" colspan="2">
31866              $AllStats{rcptReportRedlistAdd}
31867            </td>
31868          </tr>
31869          <tr>
31870            <td class="statsOptionTitle">
31871              &nbsp;&nbsp;&nbsp;&nbsp;Redlist Deletions:
31872            </td>
31873            <td class="statsOptionValue positive" colspan="2">
31874              $Stats{rcptReportRedlistRemove}
31875            </td>
31876            <td class="statsOptionValue positive" colspan="2">
31877              $AllStats{rcptReportRedlistRemove}
31878            </td>
31879          </tr>
31880          <tr>
31881            <td class="statsOptionTitle">
31882              &nbsp;&nbsp;&nbsp;&nbsp;Analyze Reports:
31883            </td>
31884            <td class="statsOptionValue positive" colspan="2">
31885              $Stats{rcptReportAnalyze}
31886            </td>
31887            <td class="statsOptionValue positive" colspan="2">
31888              $AllStats{rcptReportAnalyze}
31889            </td>
31890          </tr>
31891          <tr>
31892            <td class="statsOptionTitle">
31893              &nbsp;&nbsp;&nbsp;&nbsp;Help Reports:
31894            </td>
31895            <td class="statsOptionValue positive" colspan="2">
31896              $Stats{rcptReportHelp}
31897            </td>
31898            <td class="statsOptionValue positive" colspan="2">
31899              $AllStats{rcptReportHelp}
31900            </td>
31901          </tr>
31902          <tr>
31903            <td class="statsOptionTitle">
31904              Local Recipients Rejected:
31905            </td>
31906            <td class="statsOptionValue negative" colspan="2">
31907              $tots{rcptRejectedLocal}
31908            </td>
31909            <td class="statsOptionValue negative" colspan="2">
31910              $tots{rcptRejectedLocal2}
31911            </td>
31912          </tr>
31913          <tr>
31914            <td class="statsOptionTitle">
31915              &nbsp;&nbsp;&nbsp;&nbsp;Nonexistent Recipients:
31916            </td>
31917            <td class="statsOptionValue negative" colspan="2">
31918              $Stats{rcptNonexistent}
31919            </td>
31920            <td class="statsOptionValue negative" colspan="2">
31921              $AllStats{rcptNonexistent}
31922            </td>
31923          </tr>
31924          <tr>
31925            <td class="statsOptionTitle">
31926              &nbsp;&nbsp;&nbsp;&nbsp;Delayed Recipients:
31927            </td>
31928            <td class="statsOptionValue negative" colspan="2">
31929              $Stats{rcptDelayed}
31930            </td>
31931            <td class="statsOptionValue negative" colspan="2">
31932              $AllStats{rcptDelayed}
31933            </td>
31934          </tr>
31935          <tr>
31936            <td class="statsOptionTitle">
31937              &nbsp;&nbsp;&nbsp;&nbsp;Delayed (Late) Recipients:
31938            </td>
31939            <td class="statsOptionValue negative" colspan="2">
31940              $Stats{rcptDelayedLate}
31941            </td>
31942            <td class="statsOptionValue negative" colspan="2">
31943              $AllStats{rcptDelayedLate}
31944            </td>
31945          </tr>
31946          <tr>
31947            <td class="statsOptionTitle">
31948              &nbsp;&nbsp;&nbsp;&nbsp;Delayed (Expired) Recipients:
31949            </td>
31950            <td class="statsOptionValue negative" colspan="2">
31951              $Stats{rcptDelayedExpired}
31952            </td>
31953            <td class="statsOptionValue negative" colspan="2">
31954              $AllStats{rcptDelayedExpired}
31955            </td>
31956          </tr>
31957          <tr>
31958            <td class="statsOptionTitle">
31959              &nbsp;&nbsp;&nbsp;&nbsp;Embargoed Recipients:
31960            </td>
31961            <td class="statsOptionValue negative" colspan="2">
31962              $Stats{rcptEmbargoed}
31963            </td>
31964            <td class="statsOptionValue negative" colspan="2">
31965              $AllStats{rcptEmbargoed}
31966            </td>
31967          </tr>
31968          <tr>
31969            <td class="statsOptionTitle">
31970              &nbsp;&nbsp;&nbsp;&nbsp;Spam Bucketed Recipients:
31971            </td>
31972            <td class="statsOptionValue negative" colspan="2">
31973              $Stats{rcptSpamBucket}
31974            </td>
31975            <td class="statsOptionValue negative" colspan="2">
31976              $AllStats{rcptSpamBucket}
31977            </td>
31978          </tr>
31979          <tr>
31980            <td class="statsOptionTitle">
31981              Remote Recipients Rejected:
31982            </td>
31983            <td class="statsOptionValue negative" colspan="2">
31984              $tots{rcptRejectedRemote}
31985            </td>
31986            <td class="statsOptionValue negative" colspan="2">
31987              $tots{rcptRejectedRemote2}
31988            </td>
31989          </tr>
31990          <tr>
31991            <td class="statsOptionTitle">
31992              &nbsp;&nbsp;&nbsp;&nbsp;Relay Attempts Rejected:
31993            </td>
31994            <td class="statsOptionValue negative" colspan="2">
31995              $Stats{rcptRelayRejected}
31996            </td>
31997            <td class="statsOptionValue negative" colspan="2">
31998              $AllStats{rcptRelayRejected}
31999            </td>
32000          </tr>
32001          <tr>
32002            <td class="statsOptionValue" style="background-color: #FFFFFF">
32003              &nbsp;
32004            </td>
32005            <td class="statsOptionValue" style="background-color: #FFFFFF"
32006            colspan="2">
32007              <font size="1" color="#C0C0C0"><em>since $restart at $starttime</em></font>
32008            </td>
32009            <td class="statsOptionValue" style="background-color: #FFFFFF"
32010            colspan="2">
32011              <font size="1" color="#C0C0C0"><em>since $reset at $resettime</em></font>
32012            </td>
32013          </tr>
32014        </tbody>
32015        <tbody>
32016          <tr>
32017            <td colspan="5" class="sectionHeader" onmousedown=
32018            "toggleTbody('StatItem7')">
32019              Message Statistics
32020            </td>
32021          </tr>
32022        </tbody>
32023        <tbody id="StatItem7" class="on">
32024          <tr>
32025            <td class="statsOptionTitle">
32026              Message OK:
32027            </td>
32028            <td class="statsOptionValue positive" colspan="2">
32029              $Stats{bhams}
32030            </td>
32031            <td class="statsOptionValue positive" colspan="2">
32032              $AllStats{bhams}
32033            </td>
32034          </tr>
32035          <tr>
32036            <td class="statsOptionTitle">
32037              Whitelisted:
32038            </td>
32039            <td class="statsOptionValue positive" colspan="2">
32040              $Stats{whites}
32041            </td>
32042            <td class="statsOptionValue positive" colspan="2">
32043              $AllStats{whites}
32044            </td>
32045          </tr>
32046          <tr>
32047            <td class="statsOptionTitle">
32048              Local:
32049            </td>
32050            <td class="statsOptionValue positive" colspan="2">
32051              $Stats{locals}
32052            </td>
32053            <td class="statsOptionValue positive" colspan="2">
32054              $AllStats{locals}
32055            </td>
32056          </tr>
32057          <tr>
32058            <td class="statsOptionTitle">
32059              Passing without Processing:
32060            </td>
32061            <td class="statsOptionValue positive" colspan="2">
32062              $Stats{noprocessing}
32063            </td>
32064            <td class="statsOptionValue positive" colspan="2">
32065              $AllStats{noprocessing}
32066            </td>
32067          </tr>
32068          <tr>
32069            <td class="statsOptionTitle">
32070              Spamlover Spams Passed:
32071            </td>
32072            <td class="statsOptionValue positive" colspan="2">
32073              $Stats{spamlover}
32074            </td>
32075            <td class="statsOptionValue positive" colspan="2">
32076              $AllStats{spamlover}
32077            </td>
32078          </tr>
32079          <tr>
32080            <td class="statsOptionTitle">
32081              Bayesian Spams:
32082            </td>
32083            <td class="statsOptionValue negative" colspan="2">
32084              $Stats{bspams}
32085            </td>
32086            <td class="statsOptionValue negative" colspan="2">
32087              $AllStats{bspams}
32088            </td>
32089          </tr>
32090          <tr>
32091            <td class="statsOptionTitle">
32092              Blacklisted Domains:
32093            </td>
32094            <td class="statsOptionValue negative" colspan="2">
32095              $Stats{blacklisted}
32096            </td>
32097            <td class="statsOptionValue negative" colspan="2">
32098              $AllStats{blacklisted}
32099            </td>
32100          </tr>
32101
32102          <tr>
32103            <td class="statsOptionTitle">
32104              Invalid HELO:
32105            </td>
32106            <td class="statsOptionValue negative" colspan="2">
32107              $Stats{invalidHelo}
32108            </td>
32109            <td class="statsOptionValue negative" colspan="2">
32110              $AllStats{invalidHelo}
32111            </td>
32112          </tr>
32113         <tr>
32114            <td class="statsOptionTitle">
32115              Missing MX and A Record:
32116            </td>
32117            <td class="statsOptionValue negative" colspan="2">
32118              $Stats{mxaMissing}
32119            </td>
32120            <td class="statsOptionValue negative" colspan="2">
32121              $AllStats{mxaMissing}
32122            </td>
32123          </tr>
32124          <tr>
32125            <td class="statsOptionTitle">
32126              Missing PTR Record:
32127            </td>
32128            <td class="statsOptionValue negative" colspan="2">
32129              $Stats{ptrMissing}
32130            </td>
32131            <td class="statsOptionValue negative" colspan="2">
32132              $AllStats{ptrMissing}
32133            </td>
32134          </tr>
32135          <tr>
32136            <td class="statsOptionTitle">
32137              Invalid PTR Record:
32138            </td>
32139            <td class="statsOptionValue negative" colspan="2">
32140              $Stats{ptrInvalid}
32141            </td>
32142            <td class="statsOptionValue negative" colspan="2">
32143              $AllStats{ptrInvalid}
32144            </td>
32145          </tr>
32146
32147          <tr>
32148            <td class="statsOptionTitle">
32149              Collect/Trap Messages:
32150            </td>
32151            <td class="statsOptionValue negative" colspan="2">
32152              $Stats{spambucket}
32153            </td>
32154            <td class="statsOptionValue negative" colspan="2">
32155              $AllStats{spambucket}
32156            </td>
32157          </tr>
32158
32159          <tr>
32160            <td class="statsOptionTitle">
32161              Bad Attachments:
32162            </td>
32163            <td class="statsOptionValue negative" colspan="2">
32164              $Stats{viri}
32165            </td>
32166            <td class="statsOptionValue negative" colspan="2">
32167              $AllStats{viri}
32168            </td>
32169          </tr>
32170          <tr>
32171            <td class="statsOptionTitle">
32172              Viruses Detected:
32173            </td>
32174            <td class="statsOptionValue negative" colspan="2">
32175              $Stats{viridetected}
32176            </td>
32177            <td class="statsOptionValue negative" colspan="2">
32178              $AllStats{viridetected}
32179            </td>
32180          </tr>
32181
32182
32183          <tr>
32184            <td class="statsOptionTitle">
32185              Black/Bomb Regex:
32186            </td>
32187            <td class="statsOptionValue negative" colspan="2">
32188              $Stats{bombs}
32189            </td>
32190            <td class="statsOptionValue negative" colspan="2">
32191              $AllStats{bombs}
32192            </td>
32193          </tr>
32194
32195
32196
32197          <tr>
32198            <td class="statsOptionTitle">
32199             IPs blocked:
32200            </td>
32201            <td class="statsOptionValue negative" colspan="2">
32202              $Stats{denyConnection}
32203            </td>
32204            <td class="statsOptionValue negative" colspan="2">
32205              $AllStats{denyConnection}
32206            </td>
32207          </tr>
32208          <tr>
32209            <td class="statsOptionTitle">
32210             IPs blocked (strict):
32211            </td>
32212            <td class="statsOptionValue negative" colspan="2">
32213              $Stats{denyStrict}
32214            </td>
32215            <td class="statsOptionValue negative" colspan="2">
32216              $AllStats{denyStrict}
32217            </td>
32218          </tr>
32219          <tr>
32220            <td class="statsOptionTitle">
32221              CountryCode blocked:
32222            </td>
32223            <td class="statsOptionValue negative" colspan="2">
32224              $Stats{sbblocked}
32225            </td>
32226            <td class="statsOptionValue negative" colspan="2">
32227              $AllStats{sbblocked}
32228            </td>
32229          </tr>
32230          <tr>
32231            <td class="statsOptionTitle">
32232              Message Scoring:
32233            </td>
32234            <td class="statsOptionValue negative" colspan="2">
32235              $Stats{msgscoring}
32236            </td>
32237            <td class="statsOptionValue negative" colspan="2">
32238              $AllStats{msgscoring}
32239            </td>
32240          </tr>
32241          <tr>
32242            <td class="statsOptionTitle">
32243              Invalid Local Sender:
32244            </td>
32245            <td class="statsOptionValue negative" colspan="2">
32246              $Stats{senderInvalidLocals}
32247            </td>
32248            <td class="statsOptionValue negative" colspan="2">
32249              $AllStats{senderInvalidLocals}
32250            </td>
32251          </tr>
32252          <tr>
32253            <td class="statsOptionTitle">
32254              Invalid Internal Mail:
32255            </td>
32256            <td class="statsOptionValue negative" colspan="2">
32257              $Stats{internaladdresses}
32258            </td>
32259            <td class="statsOptionValue negative" colspan="2">
32260              $AllStats{internaladdresses}
32261            </td>
32262          </tr>
32263
32264
32265          <tr>
32266            <td class="statsOptionTitle">
32267              RBL Failures:
32268            </td>
32269            <td class="statsOptionValue negative" colspan="2">
32270              $Stats{rblfails}
32271            </td>
32272            <td class="statsOptionValue negative" colspan="2">
32273              $AllStats{rblfails}
32274            </td>
32275          </tr>
32276          <tr>
32277            <td class="statsOptionTitle">
32278              URIBL Failures:
32279            </td>
32280            <td class="statsOptionValue negative" colspan="2">
32281              $Stats{uriblfails}
32282            </td>
32283            <td class="statsOptionValue negative" colspan="2">
32284              $AllStats{uriblfails}
32285            </td>
32286          </tr>
32287
32288          <tr>
32289            <td class="statsOptionTitle">
32290              Max Errors Exceeded:
32291            </td>
32292            <td class="statsOptionValue negative" colspan="2">
32293              $Stats{msgMaxErrors}
32294            </td>
32295            <td class="statsOptionValue negative" colspan="2">
32296              $AllStats{msgMaxErrors}
32297            </td>
32298          </tr>
32299          <tr>
32300            <td class="statsOptionTitle">
32301              MSGverify :
32302            </td>
32303            <td class="statsOptionValue negative" colspan="2">
32304              $Stats{msgverify}
32305            </td>
32306            <td class="statsOptionValue negative" colspan="2">
32307              $AllStats{msgverify}
32308            </td>
32309          </tr>
32310
32311          <tr>
32312            <td class="statsOptionTitle">
32313              Local Frequency:
32314            </td>
32315            <td class="statsOptionValue negative" colspan="2">
32316              $Stats{localFrequency}
32317            </td>
32318            <td class="statsOptionValue negative" colspan="2">
32319              $AllStats{localFrequency}
32320            </td>
32321          </tr>
32322          <tr>
32323            <td class="statsOptionTitle">
32324              Early (Pre)Header:
32325            </td>
32326            <td class="statsOptionValue negative" colspan="2">
32327              $Stats{preHeader}
32328            </td>
32329            <td class="statsOptionValue negative" colspan="2">
32330              $AllStats{preHeader}
32331            </td>
32332          </tr>
32333
32334
32335
32336          <tr>
32337            <td class="statsOptionValue" style="background-color: #FFFFFF">
32338              &nbsp;
32339            </td>
32340            <td class="statsOptionValue" style="background-color: #FFFFFF"
32341            colspan="2">
32342              <font size="1" color="#C0C0C0"><em>since $restart at $starttime</em></font>
32343            </td>
32344            <td class="statsOptionValue" style="background-color: #FFFFFF"
32345            colspan="2">
32346              <font size="1" color="#C0C0C0"><em>since $reset at $resettime</em></font>
32347            </td>
32348          </tr>
32349        </tbody>
32350
32351      </table><br />
32352
32353      $kudos
32354		<br />
32355		</div>
32356 		$footers
32357
32358
32359</body></html>
32360EOT
32361}
32362
32363sub ConfigStatsRaw {
32364    SaveStats();
32365    my %tots    = statsTotals();
32366    my $upt     = ( time - $Stats{starttime} ) / ( 24 * 3600 );
32367    my $upt2    = ( time - $AllStats{starttime} ) / ( 24 * 3600 );
32368    my $uptime  = sprintf( "%.3f", $upt );
32369    my $uptime2 = sprintf( "%.3f", $upt2 );
32370    my $mpd     = sprintf( "%.1f", $upt == 0 ? 0 : $tots{msgTotal} / $upt );
32371    my $mpd2    = sprintf( "%.1f", $upt2 == 0 ? 0 : $tots{msgTotal2} / $upt2 );
32372    my $pct     = sprintf( "%.1f",
32373        $tots{msgTotal} - $Stats{locals} == 0
32374        ? 0
32375        : 100 * $tots{msgRejectedTotal} / ( $tots{msgTotal} - $Stats{locals} )
32376    );
32377    my $pct2;
32378    $pct2 = sprintf( "%.1f",
32379        $tots{msgTotal2} - $AllStats{locals} == 0
32380        ? 0
32381        : 100 *
32382          $tots{msgRejectedTotal2} /
32383          ( $tots{msgTotal2} - $AllStats{locals} ) );
32384    my $cpu = $CanStatCPU ? sprintf( "%.2f\%", 100 * $cpuUsage ) : 'n/a';
32385    my $cpuAvg; $cpuAvg = sprintf( " (%.2f\% avg)",
32386        $Stats{cpuTime} == 0 ? 0 : 100 * $Stats{cpuBusyTime} / $Stats{cpuTime} )
32387      if $CanStatCPU;
32388    my $cpuAvg2 =
32389      $CanStatCPU
32390      ? sprintf( "%.2f\% avg",
32391        $AllStats{cpuTime} == 0
32392        ? 0
32393        : 100 * $AllStats{cpuBusyTime} / $AllStats{cpuTime} )
32394      : 'n/a';
32395    <<EOT;
32396$headerHTTP
32397ASSP Proxy Uptime | $uptime days| $uptime2 days
32398Messages Processed | $tots{msgTotal} ($mpd per day) | $tots{msgTotal2} ($mpd2 per day)
32399Non-Local Mail Blocked | $pct% | $pct2%
32400CPU Usage | $cpu$cpuAvg | $cpuAvg2
32401Concurrent SMTP Sessions | $smtpConcurrentSessions ($Stats{smtpMaxConcurrentSessions} highest) | $AllStats{smtpMaxConcurrentSessions} max
32402
32403
32404SMTP Connections Received | $tots{smtpConnTotal} | $tots{smtpConnTotal2}
32405SMTP Connections Accepted | $tots{smtpConnAcceptedTotal} | $tots{smtpConnAcceptedTotal2}
32406SMTP Connections Rejected | $tots{smtpConnRejectedTotal} | $tots{smtpConnRejectedTotal2}
32407Envelope Recipients Processed | $tots{rcptTotal} | $tots{rcptTotal2}
32408Envelope Recipients Accepted | $tots{rcptAcceptedTotal} | $tots{rcptAcceptedTotal2}
32409Envelope Recipients Rejected | $tots{rcptRejectedTotal} | $tots{rcptRejectedTotal2}
32410Messages Processed | $tots{msgTotal} | $tots{msgTotal2}
32411Messages Passed | $tots{msgAcceptedTotal} | $tots{msgAcceptedTotal2}
32412Messages Rejected | $tots{msgRejectedTotal} | $tots{msgRejectedTotal2}
32413Admin Connections Received | $tots{admConnTotal} | $tots{admConnTotal2}
32414Admin Connections Accepted | $Stats{admConn} | $AllStats{admConn}
32415Admin Connections Rejected | $Stats{admConnDenied} | $AllStats{admConnDenied}
32416Stat Connections Received | $tots{statConnTotal} | $tots{statConnTotal2}
32417Stat Connections Accepted | $Stats{statConn} | $AllStats{statConn}
32418Stat Connections Rejected | $Stats{statConnDenied} | $AllStats{statConnDenied}
32419
32420
32421Accepted Logged SMTP Connections | $Stats{smtpConn} | $AllStats{smtpConn}
32422Not Logged SMTP Connections | $Stats{smtpConnNotLogged} | $AllStats{smtpConnNotLogged}
32423SMTP Connection Limits | $tots{smtpConnLimit} | $tots{smtpConnLimit2}
32424Overall Limits | $Stats{smtpConnLimit} | $AllStats{smtpConnLimit}
32425By IP Limits | $Stats{smtpConnLimitIP} | $AllStats{smtpConnLimitIP}
32426By Delay on PB | $Stats{delayConnection} | $AllStats{delayConnection}
32427BY IP By AUTH Errors Count | $Stats{AUTHErrors} | $AllStats{AUTHErrors}
32428By IP Frequency Limits | $Stats{smtpConnLimitFreq} | $AllStats{smtpConnLimitFreq}
32429By Domain IP Limits | $Stats{smtpConnDomainIP} | $AllStats{smtpConnDomainIP}
32430SMTP Connections Timeout | $tots{smtpConnIdleTimeout} | $tots{smtpConnIdleTimeout2}
32431SMTP SSL-Connections Timeout | $tots{smtpConnSSLIdleTimeout} | $tots{smtpConnSSLIdleTimeout2}
32432SMTP TLS-Connections Timeout | $tots{smtpConnTLSIdleTimeout} | $tots{smtpConnTLSIdleTimeout2}
32433Denied SMTP Connections | $Stats{smtpConnDenied} | $AllStats{smtpConnDenied}
32434
32435Local Recipients Accepted | $tots{rcptAcceptedLocal} | $tots{rcptAcceptedLocal2}
32436Validated Recipients | $Stats{rcptValidated} | $AllStats{rcptValidated}
32437Unchecked Recipients | $Stats{rcptUnchecked} | $AllStats{rcptUnchecked}
32438SpamLover Recipients | $Stats{rcptSpamLover} | $AllStats{rcptSpamLover}
32439Remote Recipients Accepted | $tots{rcptAcceptedRemote} | $tots{rcptAcceptedRemote2}
32440Whitelisted Recipients | $Stats{rcptWhitelisted} | $AllStats{rcptWhitelisted}
32441Not Whitelisted Recipients | $Stats{rcptNotWhitelisted} | $AllStats{rcptNotWhitelisted}
32442Noprocessed Recipients | $Stats{rcptUnprocessed} | $AllStats{rcptUnprocessed}
32443Email Reports | $tots{rcptReport} | $tots{rcptReport2}
32444Spam Reports | $Stats{rcptReportSpam} | $AllStats{rcptReportSpam}
32445Ham Reports | $Stats{rcptReportHam} | $AllStats{rcptReportHam}
32446Whitelist Additions | $Stats{rcptReportWhitelistAdd} | $AllStats{rcptReportWhitelistAdd}
32447Whitelist Deletions | $Stats{rcptReportWhitelistRemove} | $AllStats{rcptReportWhitelistRemove}
32448Redlist Additions | $Stats{rcptReportRedlistAdd} | $AllStats{rcptReportRedlistAdd}
32449Redlist Deletions | $Stats{rcptReportRedlistRemove} | $AllStats{rcptReportRedlistRemove}
32450Local Recipients Rejected | $tots{rcptRejectedLocal} | $tots{rcptRejectedLocal2}
32451Nonexistent Recipients | $Stats{rcptNonexistent} | $AllStats{rcptNonexistent}
32452Delayed Recipients | $Stats{rcptDelayed} | $AllStats{rcptDelayed}
32453Delayed (Late) Recipients | $Stats{rcptDelayedLate} | $AllStats{rcptDelayedLate}
32454Delayed (Expired) Recipients | $Stats{rcptDelayedExpired} | $AllStats{rcptDelayedExpired}
32455Embargoed Recipients | $Stats{rcptEmbargoed} | $AllStats{rcptEmbargoed}
32456Spam Bucketed Recipients | $Stats{rcptSpamBucket} | $AllStats{rcptSpamBucket}
32457Remote Recipients Rejected | $tots{rcptRejectedRemote} | $tots{rcptRejectedRemote2}
32458Relay Attempts Rejected | $Stats{rcptRelayRejected} | $AllStats{rcptRelayRejected}
32459
32460
32461Hams | $Stats{bhams} | $AllStats{bhams}
32462Whitelisted | $Stats{whites} | $AllStats{whites}
32463Local | $Stats{locals} | $AllStats{locals}
32464Noprocessing | $Stats{noprocessing} | $AllStats{noprocessing}
32465Spamlover Spams Passed | $Stats{spamlover} | $AllStats{spamlover}
32466Bayesian Spams | $Stats{bspams} | $AllStats{bspams}
32467Domains Blacklisted | $Stats{blacklisted} | $AllStats{blacklisted}
32468
32469HELO Invalid | $Stats{invalidHelo} | $AllStats{invalidHelo}
32470
32471Missing MX | $Stats{mxaMissing} | $AllStats{mxaMissing}
32472Missing PTR | $Stats{ptrMissing} | $AllStats{ptrMissing}
32473Invalid PTR | $Stats{ptrInvalid} | $AllStats{ptrInvalid}
32474Spam Collect Messages | $Stats{spambucket} | $AllStats{spambucket}
32475Penalty Trap Messages | $Stats{penaltytrap} | $AllStats{penaltytrap}
32476Bad Attachments | $Stats{viri} | $AllStats{viri}
32477Viruses Detected | $Stats{viridetected} | $AllStats{viridetected}
32478Bomb Regex | $Stats{bombs} | $AllStats{bombs}
32479Black Regex | $Stats{bombs} | $AllStats{bombBlack}
32480
32481PenaltyBox | $Stats{pbdenied} | $AllStats{pbdenied}
32482Message Scoring | $Stats{msgscoring} | $AllStats{msgscoring}
32483Invalid Local Sender | $Stats{senderInvalidLocals} | $AllStats{senderInvalidLocals}
32484Invalid Internal Mail | $Stats{internaladdresses} | $AllStats{internaladdresses}
32485Scripts | $Stats{scripts} | $AllStats{scripts}
32486SPF Failures | $Stats{spffails} | $AllStats{spffails}
32487RBL Failures | $Stats{rblfails} | $AllStats{rblfails}
32488URIBL Failures | $Stats{uriblfails} | $AllStats{uriblfails}
32489Max Errors Exceeded | $Stats{msgMaxErrors} | $AllStats{msgMaxErrors}
32490Delayed | $Stats{msgDelayed} | $AllStats{msgDelayed}
32491Empty Recipient | $Stats{msgNoRcpt} | $AllStats{msgNoRcpt}
32492Not SRS Signed Bounces | $Stats{msgNoSRSBounce} | $AllStats{msgNoSRSBounce}
32493MSGID Signature | $Stats{msgMSGIDtrErrors} | $AllStats{msgMSGIDtrErrors}
32494
32495EOT
32496}
32497
32498sub ConfigStatsXml {
32499
32500    # must pass by ref
32501    my ( $href, $qsref ) = @_;
32502    my %head = %$href;
32503    my %qs   = %$qsref;
32504
32505    my %totStats = &ComputeStatTotals;
32506    my %tots     = statsTotals(%totStats);
32507
32508    my $statstart  = localtime( $Stats{starttime} );
32509    my $statstart2 = localtime( $totStats{starttime} );
32510
32511    my $tstatstime = ( time - $totStats{starttime} ) / ( 24 * 3600 );
32512    my $cstatstime = ( time - $Stats{starttime} ) /    ( 24 * 3600 );
32513
32514    my $uptime = getTimeDiffAsString( time - $starttime, 1 );
32515    my $uptime2 = getTimeDiffAsString( time - $totStats{starttime} );
32516
32517    my $mpd =
32518      sprintf( "%.1f", $cstatstime == 0 ? 0 : $tots{msgTotal} / $cstatstime );
32519    my $mpd2 =
32520      sprintf( "%.1f", $tstatstime == 0 ? 0 : $tots{msgTotal2} / $tstatstime );
32521    my $pct = sprintf( "%.1f",
32522        $tots{msgTotal} - $Stats{locals} == 0
32523        ? 0
32524        : 100 * $tots{msgRejectedTotal} / ( $tots{msgTotal} - $Stats{locals} )
32525    );
32526    my $pct2 = sprintf( "%.1f",
32527        $tots{msgTotal2} - $totStats{locals} == 0
32528        ? 0
32529        : 100 *
32530          $tots{msgRejectedTotal2} /
32531          ( $tots{msgTotal2} - $totStats{locals} ) );
32532    my $cpu = $CanStatCPU ? sprintf( "%.2f\%", 100 * $cpuUsage ) : 'na';
32533    my $cpuAvg =
32534      $CanStatCPU
32535      ? sprintf( "%.2f\%",
32536        $Stats{cpuTime} == 0 ? 0 : 100 * $Stats{cpuBusyTime} / $Stats{cpuTime} )
32537      : 'na';
32538    my $cpuAvg2 =
32539      $CanStatCPU
32540      ? sprintf( "%.2f\%",
32541        $totStats{cpuTime} == 0
32542        ? 0
32543        : 100 * $totStats{cpuBusyTime} / $totStats{cpuTime} )
32544      : 'na';
32545
32546    my $r = '';
32547    foreach my $k ( keys %tots ) {
32548        next unless $k;
32549
32550        my $s = $k;
32551        if ( $s =~ tr/2//d ) {
32552            $r .= "<stat name='$s' type='cumulativetotal'>$tots{$k}</stat>";
32553        } else {
32554            $r .= "<stat name='$s' type='currenttotal'>$tots{$k}</stat>";
32555        }
32556    }
32557    foreach my $k ( keys %Stats ) {
32558        next unless $k;
32559        $r .= "<stat name='$k' type='currentstat'>$Stats{$k}</stat>";
32560    }
32561    foreach my $k ( keys %totStats ) {
32562        next unless $k;
32563        $r .= "<stat name='$k' type='cumulativestat'>$totStats{$k}</stat>";
32564    }
32565
32566    <<EOT;
32567$headerHTTP
32568
32569<?xml version='1.0' encoding='UTF-8'?>
32570<stats>
32571<stat name='statstart' type='currentstat'>$statstart</stat>
32572<stat name='statstart' type='cumulativestat'>$statstart2</stat>
32573<stat name='uptime' type='currentstat'>$uptime</stat>
32574<stat name='uptime' type='cumulativestat'>$uptime2</stat>
32575<stat name='msgPerDay' type='currentstat'>$mpd</stat>
32576<stat name='msgPerDay' type='cumulativestat'>$mpd2</stat>
32577<stat name='pctBlocked' type='currentstat'>$pct</stat>
32578<stat name='pctBlocked' type='cumulativestat'>$pct2</stat>
32579<stat name='cpuAvg' type='currentstat'>$cpuAvg</stat>
32580<stat name='cpuAvg' type='cumulativestat'>$cpuAvg2</stat>
32581<stat name='smtpConcurrentSessions' type='currentstat'>$smtpConcurrentSessions</stat>
32582$r
32583</stats>
32584EOT
32585
32586}
32587
32588sub ConfigLists {
32589    my $s;
32590    my $a;
32591    my $act = $qs{action};
32592    if ($act) {
32593        if ( $qs{list} eq 'tuplets' ) {
32594            my $ip;
32595            my $hash;
32596            my $t;
32597            my $interval;
32598            my $intervalFormatted;
32599			while ($qs{addresses}=~/($IPRe)\s*,?\s*<?(?:$EmailAdrRe\@)?($EmailDomainRe|)>?/go) {
32600                $ip = ipNetwork( $1, $DelayUseNetblocks );
32601                $a = lc $2;
32602                if ($DelayNormalizeVERPs) {
32603
32604                    # strip extension
32605                    $a =~ s/\+.*(?=@)//;
32606
32607                    # replace numbers with '#'
32608                    $a =~ s/\b\d+\b(?=.*@)/#/g;
32609                }
32610
32611                # get sender domain
32612                $a =~ s/.*@//;
32613                $hash = "$ip $a";
32614                $hash = Digest::MD5::md5_hex($hash)
32615                  if $CanUseMD5 && $DelayMD5;
32616                $t = time;
32617                $s .= "<div class=\"text\">($ip,$a) ";
32618                if ( $act eq 'v' ) {
32619
32620                    if ( !exists $DelayWhite{$hash} ) {
32621                        $s .=
32622"<span class=\"negative\">tuplet NOT whitelisted</span>";
32623                    } else {
32624                        $interval          = $t - $DelayWhite{$hash};
32625                        $intervalFormatted = formatTimeInterval($interval);
32626                        if ( $interval < $DelayExpiryTime * 24 * 3600 ) {
32627                            $s .= "tuplet whitelisted, age: $intervalFormatted";
32628                        } else {
32629                            $s .= "tuplet expired, age: $intervalFormatted";
32630                        }
32631                    }
32632                } elsif ( $act eq 'a' ) {
32633                    if (
32634                        !exists $DelayWhite{$hash}
32635                        || ( $t - $DelayWhite{$hash} >=
32636                            $DelayExpiryTime * 24 * 3600 )
32637                      )
32638                    {
32639                        if ( localmail( '@' . $a ) ) {
32640                            $s .=
32641"<span class=\"negative\">local addresses not allowed on whitelisted tuplets</span>";
32642                        } else {
32643                            $s .= "tuplet added";
32644                            $DelayWhite{$hash} = $t;
32645                            mlog( 0,
32646"AdminInfo: whitelisted tuplets addition: ($ip,$a) (admin)"
32647                            );
32648                        }
32649                    } else {
32650                        $s .=
32651"<span class=\"positive\">tuplet already whitelisted</span>";
32652                    }
32653                } elsif ( $act eq 'r' ) {
32654                    if ( !exists $DelayWhite{$hash} ) {
32655                        $s .=
32656"<span class=\"negative\">tuplet NOT whitelisted</span>";
32657                    } else {
32658                        $interval          = $t - $DelayWhite{$hash};
32659                        $intervalFormatted = formatTimeInterval($interval);
32660                        if ( $interval < $DelayExpiryTime * 24 * 3600 ) {
32661                            $s .= "tuplet removed, age: $intervalFormatted";
32662                        } else {
32663                            $s .=
32664                              "expired tuplet removed, age: $intervalFormatted";
32665                        }
32666                        delete $DelayWhite{$hash};
32667                        mlog( 0,
32668"AdminInfo: whitelisted tuplets deletion: ($ip,$a) (admin)"
32669                        );
32670                    }
32671                }
32672                $s .= "</div>\n";
32673            }
32674        } else {
32675            my $color = $qs{list} eq 'red' ? 'Red' : 'White';
32676            my $list = $color . "list";
32677            while            ($qs{addresses}=~/($EmailAdrRe\@$EmailDomainRe'?)(?:(,(?:$EmailAdrRe\@$EmailDomainRe'?)|\*))?/go) {
32678                $a = $1;
32679                $s .= "<div class=\"text\">$a ";
32680                $a = lc $a;
32681                if ( $act eq 'v' ) {
32682                    if ( $list->{$a} ) {
32683                        $s .= "${color}listed";
32684                    } else {
32685                        $s .=
32686                          "<span class=\"negative\">NOT $qs{list}listed</span>";
32687                    }
32688                } elsif ( $act eq 'a' ) {
32689                    if ( $list->{$a} ) {
32690                        $s .=
32691"<span class=\"positive\">already $qs{list}listed</span>";
32692                    } else {
32693
32694						if($color eq 'White' && localmail($a)) {
32695							$s.="<span class=\"negative\">local addresses not allowed on whitelist</span>";
32696
32697 						} else {
32698                        $s .= "added";
32699                        $list->{$a} = time;
32700                        mlog( 0,
32701                            "AdminInfo: $qs{list}list addition: $a (admin)" );
32702
32703                        }
32704                    }
32705                } elsif ( $act eq 'r' ) {
32706                    if ( $list->{$a} ) {
32707                        $s .= "removed";
32708                        delete $list->{$a};
32709                        mlog( 0,
32710                            "AdminInfo: $qs{list}list deletion: $a (admin)" );
32711                    } else {
32712                        $s .= "not $qs{list}listed";
32713                    }
32714                }
32715                $s .= "</div>\n";
32716            }
32717        }
32718    }
32719    if ( $qs{B1} =~ /^Show (.)/i ) {
32720        local $/ = "\n";
32721        if ( $1 eq 'R' ) {
32722            $qs{list} = "red";    # update radios
32723            $RedlistObject->flush() if $RedlistObject && $redlistdb !~ /mysql/;
32724            open( $FH, "<","$base/$redlistdb" ) if $redlistdb !~ /mysql/;
32725            $s .= '<div class="textbox"><b>Redlist</b></div>';
32726            if ( $redlistdb =~ /mysql/ ) {
32727                $s .= '<div class="textbox"><b>mysql</b></div>';
32728                while ( my ($v) = each(%Redlist) ) {
32729                    my ( $a, $time ) = split( "\002", $v );
32730                    $s .= "<div class=\"textbox\">$a</div>";
32731                }
32732            }
32733        } else {
32734            $qs{list} = "white";    # update radios
32735            $WhitelistObject->flush()
32736              if $WhitelistObject && $whitelistdb !~ /mysql/;
32737            open( $FH, "<","$base/$whitelistdb" ) if $whitelistdb !~ /mysql/;
32738            $s .= '<div class="textbox"><b>Whitelist</b></div>';
32739            if ( $whitelistdb =~ /mysql/ ) {
32740                $s .= '<div class="textbox"><b>mysql</b></div>';
32741                while ( my ($v) = each(%Whitelist) ) {
32742                    my ( $a, $time ) = split( "\002", $v );
32743                    $s .= "<div class=\"textbox\">$a</div>";
32744
32745                }
32746            }
32747        }
32748        if ( $whitelistdb !~ /mysql/ ) {
32749            my $l;
32750            while ( $l = <$FH> ) {
32751                my ($a) = $l =~ /([^\002]*)/;
32752                $s .= "<div class=\"textbox\">$a</div>";
32753            }
32754            close $FH;
32755        }
32756    }
32757    $resultConfigLists = $s;
32758    <<EOT;
32759$headerHTTP
32760$headerDTDTransitional
32761$headers
32762
32763<div class="content">
32764<h2>Update or Verify the Whitelist/Redlist</h2>
32765$s
32766<form method="post" action=\"\">
32767    <table class="textBox" style="width: 99%;">
32768        <tr>
32769            <td class="noBorder">Do you want to work with the:
32770            </td>
32771            <td class="noBorder">
32772            <input type="radio" name="list" value="white"${\((!$qs{list} || $qs{list} eq 'white') ? ' checked="checked" ' : ' ')} /> Whitelist or<br />
32773            <input type="radio" name="list" value="red"${\($qs{list} eq 'red' ? ' checked="checked" ' : ' ')} /> Redlist or<br />
32774            <input type="radio" name="list" value="tuplets"${\($qs{list} eq 'tuplets' ? ' checked="checked" ' : ' ')} /> Tuplets
32775            </td>
32776        </tr>
32777        <tr>
32778            <td class="noBorder">Do you want to: </td>
32779            <td class="noBorder"><input type="radio" name="action" value="a" />add<br />
32780            <input type="radio" name="action" value="r" />remove<br />
32781            <input type="radio" checked="checked" name="action" value="v" />or verify</td>
32782            <td class="noBorder">
32783                List the addresses in this box:<br />
32784                (for tuplets put: ip-address,domain-name)<br />
32785                <p><textarea name="addresses" rows="5" cols="40" wrap="off">$qs{addresses}</textarea></p>
32786            </td>
32787        </tr>
32788        <tr>
32789            <td class="noBorder">&nbsp;</td>
32790            <td class="noBorder"><input type="submit" name="B1" value="  Submit  " /></td>
32791            <td class="noBorder">&nbsp;</td>
32792        </tr>
32793    </table>
32794</form>
32795<div class="textBox">
32796<p>Post less than 1 megabyte of data at a time.</p>
32797Note: The redlist is not a blacklist. The redlist is a list of addresses that cannot
32798contribute to the whitelist. For example, if someone goes on a vacation and turns on their
32799email's autoresponder, put them on the redlist until they return. Then as they reply
32800to every spam they receive they won't corrupt your non-spam collection or whitelist.
32801
32802  <form action="" method="post">
32803  <table style="width: 90%; margin-left: 5%;">
32804    <tr>
32805      <td align="center" class="noBorder"><input type="submit" name="B1" value="Show Whitelist" /></td>
32806      <td align="center" class="noBorder"><input type="submit" name="B1" value="Show Redlist" /></td>
32807    </tr>
32808  </table>
32809  </form>
32810      <p class="warning">warning:   If your whitelist or redlist is long, pushing these buttons
32811      is ill-advised.</p>
32812</div>
32813</div>
32814$footers
32815</body></html>
32816EOT
32817
32818}
32819
32820sub SearchBombW {
32821    my ($name, $srch)=@_;
32822
32823    $incFound = '';
32824    $weightMatch = '';
32825    my %Bombs = &BombWeight(0,$srch,$name );
32826    if ($Bombs{count}) {
32827        my $match = &SearchBomb($name, $$srch);
32828        $weightMatch = $match if (! $weightMatch);
32829        return 'highest match: "' . "$Bombs{matchlength}$Bombs{highnam}" . '" with valence: ' . $Bombs{highval} . ' - PB value = ' . $Bombs{sum};
32830    }
32831    return;
32832}
32833
32834sub SearchBomb {
32835    my ($name, $srch, $nolog)=@_;
32836	my $extLog = $AnalyzeLogRegex && ! $silent && [caller(1)]->[3] =~ /analyze/io;
32837    $incFound = '';
32838    my $fil=$Config{"$name"};
32839    return 0 unless $fil;
32840    $addCharsets = 1 if $name eq 'bombCharSets';
32841    my $text;
32842    if ($name ne 'bombSubjectRe') {
32843       my $mimetext = cleanMIMEBody2UTF8(\$srch);
32844
32845       if ($mimetext) {
32846           $text =  cleanMIMEHeader2UTF8(\$srch,0);
32847           $mimetext =~ s/\=(?:\015?\012|\015)//go;
32848           $mimetext = decHTMLent(\$mimetext);
32849           $text .= $mimetext;
32850       } else {
32851           $text = decodeMimeWords2UTF8($srch)
32852       }
32853    } elsif (! $LogCharset) {
32854       eval{$text = ( $srch ? Encode::encode('utf-8',$srch) : '');};
32855    } else {
32856       $text = $srch;
32857    }
32858    $srch = $text;
32859
32860    undef $text;
32861    $addCharsets = 0;
32862    my @complex;
32863    if($fil=~/^\s*file:\s*(.+)\s*$/io) {
32864        $fil=$1;
32865        open (my $BOMBFILE, "<","$base/$fil");
32866        my $counter=0;
32867        my $complexStartLine;
32868        while (my $i = <$BOMBFILE>)  {
32869            $counter++;
32870            $i =~ s/\<\<\<(.*?)\>\>\>/$1/o;
32871            $i =~ s/!!!(.*?)!!!//o;
32872            $i =~ s/a(?:ssp)?-do?-n(?:ot)?-o(?:ptimize)?-r(?:egex)?//iso;
32873            if ($i =~ /(^\s*#\s*include\s+)(.+)/io) {
32874                my $fn = $2;
32875                $i = $1;
32876                $fn =~ s/([^\\\/])[#;].*/$1/go;
32877                $i .= $fn;
32878            } else {
32879                $i =~ s/^#.*//go;
32880                $i =~ s/([^\\])#.*/$1/go;
32881            }
32882            $i =~ s/^;.*//go;
32883            $i =~ s/([^\\]);.*/$1/go;
32884            $i =~ s/\r//go;
32885            $i =~ s/\s*\n+\s*//go;
32886 			$i =~ s/\s+$//o;
32887            next if !$i;
32888
32889            if (($i =~ /^\~?\Q$complexREStart\E\s*$/o || @complex) && $i !~ /^\Q$complexREEnd\E\d+\}\)(?:\s*\=\>\s*(?:-{0,1}\d+\.*\d*)?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?)?$/o) {
32890                $complexStartLine = $counter if !$complexStartLine && $i =~ /^\~?\Q$complexREStart\E\s*$/o;
32891                if ($i !~ /^\s*#\s*include\s+.+/io) {
32892                    push @complex, $i;
32893                    next;
32894                }
32895            } elsif ($i =~ /^\Q$complexREEnd\E\d+\}\)(?:\s*\=\>\s*([+\-]?(?:0?\.\d+|\d+\.\d+|\d+))?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?)?$/o) {
32896                push @complex, $i;
32897                $i = join('|', @complex);
32898                @complex = ();
32899            }
32900			my $isave=$i;
32901            $i =~ s/(\~([^\~]+)?\~|([^\|]+)?)\s*\=\>\s*([+\-]?(?:0?\.\d+|\d+\.\d+|\d+))?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?/$2/o;
32902
32903
32904            next if !$i;
32905#			$i =~ s/\~//go;
32906
32907            my $line;
32908            my $reg = $i;
32909            my $file = $fil;
32910            my $found;
32911            my $INCFILE;
32912            if ($i =~ /^\s*#\s*include\s+(.+)\s*/io && (open $INCFILE, "<","$base/$1")) {
32913                $line = 0;
32914                $file = $1;
32915                my @complexInc;
32916                my $complexIncStart;
32917                while (my $ii = <$INCFILE>) {
32918                    $line++;
32919
32920                    $ii =~ s/\<\<\<(.*?)\>\>\>/$1/o;
32921                    $ii =~ s/!!!(.*?)!!!/$1/o;
32922                    $ii =~ s/a(?:ssp)?-do?-n(?:ot)?-o(?:ptimize)?-r(?:egex)?//iso;
32923                    $ii =~ s/^[#;].*//go;
32924                    $ii =~ s/^;.*//go;
32925                    $ii =~ s/([^\\])#.*/$1/go;
32926                    $ii =~ s/([^\\]);.*/$1/go;
32927                    $ii =~ s/\r//go;
32928                    $ii =~ s/\s*\n+\s*//go;
32929                    $ii =~ s/\s+$//o;
32930                    next if !$ii;
32931
32932                    if (@complex)  {                   # complex regex started in upper file
32933                        push @complex, $ii;
32934                        next;
32935                    }
32936# complex regex started in include file
32937
32938
32939                    if (($ii =~ /^\~?\Q$complexREStart\E\s*$/o || @complexInc) && $ii !~ /^\Q$complexREEnd\E\d+\}\)(?:\s*\=\>\s*(?:-{0,1}\d*\.?\d*)?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?)?$/o) {
32940                        $complexIncStart = $line if !$complexIncStart && $ii =~ /^\~?\Q$complexREStart\E\s*$/o;
32941                        push @complexInc, $ii;
32942                        next;
32943                    } elsif ($ii =~ /^\Q$complexREEnd\E\d+\}\)(?:\s*\=\>\s*(?:-{0,1}\d*\.?\d*)?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?)?$/o) {
32944                        push @complexInc, $ii;
32945                        $ii = join('|', @complexInc);
32946                        @complexInc = ();
32947                    }
32948					my $iisave=$ii;
32949                    $ii =~ s/(\~([^\~]+)?\~|([^\|]+)?)\s*\=\>\s*([+\-]?(?:0?\.\d+|\d+\.\d+|\d+))?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?/$2/o;
32950
32951                    next if !$ii;
32952#					$ii =~ s/\~//go;
32953
32954                    $found = '';
32955                    eval{$found = $1 || $2 if $srch =~ m/($ii)/i;};
32956                    if ($@) {
32957                        mlog(0,"ConfigError: '$name' regular expression error in line $counter of file $fil - line $line of include '$file' for '$iisave': $@");
32958                        &disableRegex ($name, $file, $ii, $@);
32959                        next;
32960                    }
32961                    if ($found)
32962                    {
32963
32964                        mlog(0,"Info: '$found' matched in regular expression '$name' - in line $counter of file $fil - line ".($complexIncStart?"$complexIncStart-$line":$line)." of file '$file' with '$iisave'") if ($BombLog > 2 or $extLog) && !$nolog;
32965                        close ($INCFILE);
32966                        $incFound = "<a href=\"javascript:void(0);\" onclick=\"javascript:popFileEditor('$fil',1);\" onmouseover=\"showhint('edit file $fil', this, event, '250px', '1'); return true;\">$Config{$name}\[line $counter\]</a>|incl:<a href=\"javascript:void(0);\" onclick=\"javascript:popFileEditor('$file',1);\" onmouseover=\"showhint('edit file $file', this, event, '250px', '1'); return true;\">$file\[line ".($complexIncStart?"$complexIncStart-$line":$line)."\]</a>";
32967
32968                        close ($BOMBFILE);
32969
32970                        return $iisave;
32971                    }
32972                    $complexIncStart = 0;
32973                }
32974                close $INCFILE;
32975                next;
32976            } elsif ($i =~ /^\s*#\s*include\s+(.+)s*/io) {
32977                mlog(0,"ConfigError: '$name' unable to open include file $1 in line $counter of '$file'");
32978                next;
32979            } else {
32980                $found = '';
32981
32982                eval{$found = $1 || $2 if $srch =~ m/($i)/i;};
32983                if ($@) {
32984                    mlog(0,"ConfigError: '$name' regular expression error in line $counter of '$file' for '$isave': $@");
32985                    &disableRegex ($name, $file, $i, $@);
32986                    next;
32987                }
32988            }
32989            if ($found)
32990
32991            {
32992
32993                mlog(0,"Info: '$found' matched in regular expression '$name' - in line $counter of file $fil - line" .($complexStartLine?"$complexStartLine-$counter":$counter). " of file '$file' with '$isave'") if ($BombLog > 2 or $extLog) && !$nolog;
32994                close ($BOMBFILE);
32995                $incFound = "<a href=\"javascript:void(0);\" onclick=\"javascript:popFileEditor('$file',1);\" onmouseover=\"showhint('edit file $file', this, event, '250px', '1'); return true;\">$Config{$name}\[line ".($complexStartLine?"$complexStartLine-$counter":$counter)."\]</a>";
32996                return $isave;
32997            }
32998            $complexStartLine = 0;
32999        }
33000        close ($BOMBFILE);
33001    } else {
33002        my $regex;
33003        $fil =~ s/(\~([^\~]+)?\~|([^\|]+)?)\s*\=\>\s*([+\-]?(?:0?\.\d+|\d+\.\d+|\d+))?\s*(?:\s*\:\>\s*(?:[nNwWlLiI\+\-\s]+)?)?/$2/o; # skip weighted regexes
33004
33005 #       $fil =~ s/\~//g;
33006        my @reg;
33007        my $bd=0;
33008        my $sk;
33009        my $t;
33010        foreach my $s (split('',$fil)) {
33011            if ($s eq '\\') {
33012                $sk = 1;
33013                $t .= $s;
33014                next;
33015            } elsif ($sk == 1) {
33016                $sk = 0;
33017                $t .= $s;
33018                next;
33019            }
33020            if ($s eq '(' or $s eq '[' or $s eq '{') {
33021                $bd++;
33022                $t .= $s;
33023                next;
33024            } elsif ($s eq ')' or $s eq ']' or $s eq '}') {
33025                $bd--;
33026                $t .= $s;
33027                next;
33028            }
33029            if ($bd > 0) {
33030                $t .= $s;
33031                next;
33032            } elsif ($s eq '|') {
33033                push @reg, $t;
33034                $t = '';
33035                $sk = 0;
33036                next;
33037            } else {
33038                $t .= $s;
33039            }
33040        }
33041        push @reg,$t if $t;
33042
33043        while (@reg) {
33044            $regex = shift @reg;
33045            if (my ($i) = eval{$srch =~ m/($regex)/i}) {
33046                mlog(0,"Info: '$name' regular expression '$regex' match with '$i' ") if ($BombLog > 2 or $extLog) && !$nolog;
33047                $incFound = $i;
33048                return $i;
33049            }
33050        }
33051        if ($@) {
33052            mlog(0,"ConfigError: '$name' regular expression error of '$fil' for '$name': $@");
33053        }
33054    }
33055    return 0;
33056}
33057
33058
33059sub disableRegex {
33060	my ($name, $file, $regex, $error) = @_;
33061    mlog(0,"Info: regular expression '$regex' in '$file' disabled");
33062    my $BOMBFILE;
33063    my $fil = "$base/$file";
33064    open( $BOMBFILE, "<","$fil" );
33065    my @lines = <$BOMBFILE>;
33066    close($BOMBFILE);
33067    unlink "$fil.bak";
33068    rename( "$fil", "$fil.bak" );
33069    open( $BOMBFILE, ">","$fil" );
33070    my $time = timestring();
33071    foreach my $k (@lines) {
33072        	$k =~ s/(\Q$regex\E)/## $time disabled ##: $1 $error/i;
33073        	print $BOMBFILE "$k";
33074      }
33075    close($BOMBFILE);
33076}
33077sub ConfigAnalyze {
33078    my ( $ba, $st, $fm, %fm, %to, %wl, $ip, $helo, $text, $ip3, $received , $emfd);
33079    my $checkRegex = ! $silent && $AnalyzeLogRegex;
33080    my $mail = $qs{mail};
33081    $mail =~ s/\r?\n/\r\n/gos;
33082    my $hl = getheaderLength(\$mail);
33083    my $mBytes = $MaxBytes ? $MaxBytes + $hl : 10000 + $hl;
33084    $mail = substr( $qs{mail}, 0, $mBytes );
33085    $fm = "analyze is restricted to a maximum length of $mBytes bytes<br />\n" if length($qs{mail}) > length($mail);
33086    if ($mail =~ /X-Assp-ID: (.+)/io) {
33087        $fm .= "ASSP-ID: $1<br />";
33088    }
33089    if ($mail =~ s/X-Assp-Envelope-From:\s*($HeaderValueRe)//ios) {
33090        my $s = $1;
33091        &headerUnwrap($s);
33092        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/io) {
33093            $s = batv_remove_tag(0,lc $1,'');
33094            $fm{$s}=1;
33095            ($emfd) = $s =~ /\@(.*)/o;
33096        }
33097    }
33098    if ($mail =~ s/X-Assp-Recipient:\s*($HeaderValueRe)//ios) {
33099        my $s = $1;
33100        &headerUnwrap($s);
33101        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/o) {
33102            $s = batv_remove_tag(0,lc $1,'');
33103            $to{$s}=1;
33104        }
33105    }
33106    if (! scalar keys %to && $mail =~ s/X-Assp-Envelope-For:\s*($HeaderValueRe)//ios) {
33107        my $s = $1;
33108        &headerUnwrap($s);
33109        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/o) {
33110            $s = batv_remove_tag(0,$1,'');
33111            $to{lc $s}=1;
33112        }
33113    }
33114    $fm .= "removed all local X-ASSP- header lines for analysis<br />\n"
33115        if ($mail =~ s/x-assp-[^()]+?:\s*$HeaderValueRe//gios);
33116    my $mystatus;
33117    my @t;
33118    my $ret;
33119    my $bombsrch;
33120    my $orgmail;
33121    my @sips;
33122	my $foundReceived;
33123    my $sub = undef;
33124    my $wildcardUser = lc $wildcardUser;
33125    my $headerLen = index($mail,"\015\012\015\012");
33126
33127
33128        if ($mail) {
33129        $orgmail = $mail;
33130        my $name = $myName;
33131        $name =~ s/(\W)/\\$1/go;
33132        if ($headerLen > -1) {
33133            my $fhh;
33134            do {
33135               $fhh = rand(1000000);
33136            } while exists $Con{$fhh};
33137            $Con{$fhh}->{header} = $mail;
33138            $Con{$fhh}->{headerpassed} = 1;
33139            &makeSubject($fhh);
33140            $sub = $Con{$fhh}->{subject3} if defined $Con{$fhh}->{subject3};
33141            headerUnwrap($sub) if (defined $sub);
33142            delete $Con{$fhh};
33143        }
33144
33145        my @myNames = ($myName);
33146        push @myNames , split(/[\|, ]+/o,$myNameAlso);
33147        my $myName = join('|', map {my $t = quotemeta($_);$t;} @myNames);
33148        while ( $mail =~ /Received:\s+from\s+.*?\(\[($IPRe).*?helo=(.{0,64})\)(?:\s+by\s+(?:$myName))?\s+with/isg ) {
33149            $ip = ipv6expand(ipv6TOipv4($1));
33150            $helo = $2;
33151            $foundReceived = -1;
33152        }
33153
33154        if (! $ip && $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*ip\s*=\s*($IPRe)/ios ) {
33155            $ip = ipv6expand(ipv6TOipv4($1));
33156            $mystatus="ip";
33157        }
33158
33159		$fm .= "Connecting IP: '$ip'<br />\n" if $ip;
33160        my $conIP = $ip;
33161        $ip3 = ipNetwork($ip,1);
33162        if (!$helo && $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*helo\s*=\s*([^\r\n]+)/ios ) {
33163            $helo = $1;
33164            $helo =~ s/\)$//o;
33165            $mystatus="helo";
33166        }
33167        $fm .= "Connecting HELO: $helo<br />\n" if $helo;
33168        if ( $foundReceived != -1 && $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*(text|test|regex)\s*=\s*(.+)/ios ) {
33169            $text = $2;
33170            $mystatus="text";
33171            $fm .= "found $1='$2' - lookup regular expressions in $1<br />\n";
33172        } else {
33173            $text = $mail;
33174        }
33175
33176$text =~ s/(?:\r?\n)+/\r\n/gos if $mystatus;
33177        $fm = "<div class=\"textBox\"><b><font size=\"3\" color=\"#003366\">General Hints:</font></b><br /><br />\n$fm</div>\n" if $fm;
33178        $fm .= "<div class=\"textBox\"><br />";
33179        if ($ispHostnames && (my($header) = $mail =~ /($HeaderRe+)/o)) {
33180            my $ispHost;
33181            while ( $header =~ /Received:($HeaderValueRe)/gis ) {
33182                my $val = $1;
33183                if ( $val =~ /(\s+from\s+(?:([^\s]+)\s)?(?:.+?)($IPRe)(?:.{1,80})by.{1,20}($ispHostnamesRE))/gis ) {
33184                    $helo = $2;
33185                    $received = 'Received:'.$1;
33186                    $ispHost = $4;
33187                    $ip = ipv6expand(ipv6TOipv4($3));
33188                    $ip3 = ipNetwork($ip,1);
33189                    $foundReceived = 1;
33190                }
33191            }
33192            if ($received) {
33193                $fm =~ s/(Connecting IP: '[^']+')/$1 is an <a href='.\/ispip'>ISPIP<\/a>/o;
33194                $fm =~ s/(Connecting HELO: [^<]+)/$1 is HELO from ISP-host: <a href='.\/ispHostnames'>$ispHost<\/a>/o;
33195                $fm .= "<b><font color='orange'>&bull;</font>ISP/Secondary Header:</b>'$received'<br />\n";
33196                $fm .= "<b><font color='orange'>&bull;</font>Switched to ISP/Secondary IP:</b> '$ip'<br /><br />\n";
33197            }
33198        }
33199
33200        if ($foundReceived <= 0 && !$mystatus) {
33201            $foundReceived += () = $mail =~ /(Received:\s*from\s*)/isgo;
33202            $fm .= "<b><font color='yellow'>&bull;</font>no foreign received header line found</b><br /><br />\n"
33203              if ($foundReceived <= 0) ;
33204        }
33205
33206        $fm .= "<b><font color=\"#003366\">sender and reply addresses:</font></b><br />";
33207        my $mailfrom;
33208        foreach (keys %fm) {
33209            $fm .=  "MAIL FROM: $_ ,";
33210            $mailfrom = $_;
33211        }
33212        while ($mail =~ /($HeaderNameRe):($HeaderValueRe)/igos) {
33213            my $who = $1;
33214            my $s = $2;
33215
33216            next if $who !~ /^(from|sender|reply-to|errors-to|list-\w+|ReturnReceipt|Return-Receipt-To|Disposition-Notification-To)$/io;
33217            $mailfrom = lc($1) if (! $mailfrom && lc($1) eq 'from');
33218            &headerUnwrap($s);
33219            while ($s =~ /($EmailAdrRe\@$EmailDomainRe)/go) {
33220                my $ss = batv_remove_tag(0,$1,'');
33221                $mailfrom = $ss if $mailfrom eq 'from';
33222                $fm{lc $ss}=1;
33223                $fm .=  " $who: $ss <br />  ";
33224            }
33225        }
33226        $fm =~ s/  $/<br \/><br \/>/o;
33227
33228        $fm .= "<b><font color=\"#003366\">recipient addresses:</font></b><br />";
33229        foreach (keys %to) {
33230            $fm .=  "RCPT TO: $_ ,";
33231            my $newadr = RcptReplace($_,$mailfrom,'RecRepRegex');
33232            $fm =~ s/,$/(replaced with $newadr),/o if lc($newadr) ne lc $_;
33233        }
33234        while ($mail =~ /(?:^|\n)(to|cc|bcc):($HeaderValueRe)/igos) {
33235            my $who = $1;
33236            my $s = $2;
33237            &headerUnwrap($s);
33238            while ($s =~ /($EmailAdrRe\@$EmailDomainRe)/go) {
33239                my $ss = batv_remove_tag(0,$1,'');
33240                $to{lc $ss}=1;
33241                $fm .=  " $who: $ss ,";
33242            }
33243        }
33244        $fm =~ s/,$/<br \/><br \/>/o;
33245
33246        $fm .= "<b><font size=\"3\" color=\"#003366\">Feature Matching:</font></b><br /><br />";
33247
33248		my $mfd;
33249		my $mfdd;
33250        while ( $mail =~ /($EmailAdrRe\@$EmailDomainRe)/go ) {
33251            my $ad    = lc $1;
33252            next if $ad =~ /\=/;
33253#            my $mf   = $ad;
33254            my $mf = batv_remove_tag(0,$ad,'');
33255            $mfd  = $1 if $mf =~ /\@(.*)/;
33256            $mfdd = $1 if $mf =~ /(\@.*)/;
33257
33258            next if $fm{$ad};
33259			$fm{$ad}=1;
33260
33261            if (matchSL( $mf, 'noProcessing' )) {
33262                $fm .=
33263"<b><font color='orange'>&bull;</font> <a href='./#noProcessing'>noProcessing</a></b>: '$slmatch' ($ad)<br />\n";
33264              }
33265            if ( $noProcessingDomains && $mf =~ /($NPDRE)/ ) {
33266                $fm .=
33267"<b><font color='orange'>&bull;</font> <a href='./#noProcessingDomains'>noProcessingDomains</a></b>: '$1' ($ad)<br />\n";
33268              }
33269            if ( matchSL( $mf, 'noProcessingFrom' ) ) {
33270                $fm .=
33271"<b><font color='orange'>&bull;</font> <a href='./#noProcessingFrom'>noProcessingFrom</a></b>: '$slmatch' ($ad)<br />\n";
33272              }
33273            if ($blackListedDomains && $mf =~ /($BLDRE)/ ) {
33274                $fm .=
33275"<b><font color='red'>&bull;</font> <a href='./#blackListedDomains'>blackListedDomains</a></b>: '$1' ($ad)<br />\n";
33276              }
33277            if ($weightedAddresses) {
33278            	my ($slmatch,$w) = &HighWeightSL($mf, 'weightedAddresses');
33279            	if ($w) {
33280            		$fm .=
33281"<b><font color=red>&bull;</font> <a href='./#weightedAddresses'>weightedAddresses </a></b>: '$slmatch' ($ad) with valence: $blValencePB - PB value = $w<br />\n";
33282              	}
33283             }
33284            if ($whiteListedDomains && $mf =~ /($WLDRE)/ ) {
33285                $fm .=
33286"<b><font color=#66CC66>&bull;</font> <a href='./#whiteListedDomains'>whiteListedDomains</a></b>: '$1' ($ad)<br />\n";
33287              }
33288
33289            $fm .= "<b><font color='orange'>&bull;</font> <a href='./lists'>Redlist</a></b>: '$ad'<br />\n"
33290              if $Redlist{$ad};
33291            $fm .=
33292"<b><font color=#66CC66>&bull;</font> <a href='./lists'>Redlisted Domain/ Wildcard</a></b>: '$wildcardUser$mfdd'<br />\n"
33293              if $Redlist{"$wildcardUser$mfdd"};
33294            $fm .=
33295"<b><font color=#66CC66>&bull;</font> <a href='./lists'>Whitelisted WildcardDomain</a></b>: '$wildcardUser$mfdd'<br />\n"
33296              if &Whitelist("$wildcardUser$mfdd");
33297
33298            if (&Whitelist($ad)) {
33299                $fm .= "<b><font color=#66CC66>&bull;</font> <a href='./lists'>Whitelist</a></b>: '$ad'<br />\n";
33300                foreach my $t (sort keys %to) {
33301                    if (! &Whitelist($ad,$t)) {
33302                        $fm .= "<b><font color='red'>&bull;</font> <a href='./lists'>Whitelist removed for $t </a></b>: '$ad'<br />\n";
33303                    }
33304                }
33305            }
33306
33307            foreach my $t (sort keys %to) {
33308                $fm .=
33309"<b><font color='red'>&bull;</font> <a href='./#persblackdb'>on personal Blacklist for $t </a></b>: '$ad'<br />\n"
33310                    if exists $PersBlack{lc "$t,$ad"};
33311            }
33312
33313            $fm .=
33314"<b><font color=#66CC66>&bull;</font> <a href='./#noURIBL'>No URIBL sender</a></b>: '$mf'<br />\n"
33315              if matchSL( $mf, 'noURIBL' );
33316        }
33317
33318        if ( $preHeaderRe && $text =~ /($preHeaderReRE)/ ) {
33319            $fm .= "<b><font color='red'>&bull;</font> <a href='./#preHeaderRe'>preHeaderRe</a></b>: '".($1||$2)."'<br />\n";
33320            $bombsrch = SearchBomb( "preHeaderRe", ($1||$2) );
33321            $fm .= "<font color='red'>&bull;</font> matching preHeaderRe($incFound): '$bombsrch'<br />\n"
33322              if $bombsrch;
33323          }
33324        if ( $noSPFRe && $text =~ /($noSPFReRE)/ ) {
33325            $fm .= "<b><font color='green'>&bull;</font> <a href='./#noSPFRe'>No SPF RE</a></b>: '".($1||$2)."'<br />\n";
33326            $bombsrch = SearchBomb( "noSPFRe", ($1||$2) );
33327            $fm .= "<font color='green'>&bull;</font> matching noSPFRe($incFound): '$bombsrch'<br />\n"
33328              if $bombsrch;
33329          }
33330        if ( $strictSPFRe && $text =~ /($strictSPFReRE)/ ) {
33331            $fm .= "<b><font color='green'>&bull;</font> <a href='./#strictSPFRe'>Strict SPF RE</a></b>: '".($1||$2)."'<br />\n";
33332            $bombsrch = SearchBomb( "strictSPFRe", ($1||$2) );
33333            $fm .= "<font color='green'>&bull;</font> matching strictSPFRe($incFound): '$bombsrch'<br />\n"
33334              if $bombsrch;
33335          }
33336        if ( $blockstrictSPFRe && $text =~ /($blockstrictSPFReRE)/ ) {
33337            $fm .= "<b><font color='green'>&bull;</font> <a href='./#blockstrictSPFRe'>Block Strict SPF RE</a></b>: '".($1||$2)."'<br />\n";
33338            $bombsrch = SearchBomb( "blockstrictSPFRe", ($1||$2) );
33339            $fm .= "<font color='green'>&bull;</font> matching blockstrictSPFRe($incFound): '$bombsrch'<br />\n"
33340              if $bombsrch;
33341          }
33342		if ( exists $SPFCache{"$ip $emfd"} ) {
33343            my ( $ct, $status, $result ) = split( ' ', $SPFCache{"$ip $emfd"} );
33344            my $color = (($status eq 'pass') ? 'green' : 'orange');
33345            $fm .= "<b><font color='$color'>&bull;</font> $ip is in SPFCache</b>: status=$status with helo=$result<br />\n";
33346          }
33347       	if ($ip && ($mailfrom || $helo)) {
33348            my $tmpfh = time;
33349            $Con{$tmpfh} = {};
33350            $Con{$tmpfh}->{ip} = $ip;
33351            $Con{$tmpfh}->{mailfrom} = $mailfrom;
33352            $Con{$tmpfh}->{helo} = $helo;
33353            if (SPFok($tmpfh)) {
33354                $fm .= "<b><font color='green'>&bull;</font> SPF-check returned OK</b> for $ip -&gt; $mailfrom, $helo<br />\n";
33355            } else {
33356                $fm .= "<b><font color='red'>&bull;</font> SPF-check returned FAILED</b> for $ip -&gt; $mailfrom, $helo<br />\n";
33357            }
33358            delete $Con{$tmpfh};
33359        }
33360        if ( $whiteRe && $text =~ /($whiteReRE)/ ) {
33361            $fm .= "<b><font color='green'>&bull;</font> <a href='./#whiteRe'>White RE</a></b>: '".($1||$2)."'<br />\n";
33362            $bombsrch = SearchBomb( "whiteRe", ($1||$2) );
33363            $fm .= "<font color='green'>&bull;</font> matching whiteRe($incFound): '$bombsrch'<br />\n"
33364              if $bombsrch;
33365          }
33366        if ( $redRe && $text =~ /($redReRE)/ ) {
33367            $fm .= "<b><font color='yellow'>&bull;</font> <a href='./#redRe'>Red RE</a></b>: '".($1||$2)."'<br />\n";
33368            $bombsrch = SearchBomb( "redRe", ($1||$2) );
33369            $fm .= "<font color='yellow'>&bull;</font> matching redRe($incFound): '$bombsrch'<br />\n"
33370              if $bombsrch;
33371          }
33372        if ( $npRe && $text =~ /($npReRE)/ ) {
33373            $fm .= "<b><font color='green'>&bull;</font> <a href='./#npRe'>No Processing RE</a></b>: '".($1||$2)."'<br />\n";
33374            $bombsrch = SearchBomb( "npRe", ($1||$2) );
33375            $fm .= "<font color='green'>&bull;</font> matching npRe($incFound): '$bombsrch'<br />\n"
33376              if $bombsrch;
33377          }
33378        if ( $baysSpamLoversRe && $text =~ /($baysSpamLoversReRE)/ ) {
33379            $fm .=
33380"<b><font color='green'>&bull;</font> <a href='./#baysSpamLoversRe'>Bayes Spamlover RE</a></b>: '".($1||$2)."'<br />\n";
33381            $bombsrch = SearchBomb( "baysSpamLoversRe", ($1||$2) );
33382            $fm .=
33383"<font color='green'>&bull;</font> matching baysSpamLoversRe($incFound): '$bombsrch'<br />\n"
33384              if $bombsrch;
33385          }
33386        if ( $SpamLoversRe && $text =~ /($SpamLoversReRE)/ ) {
33387            $fm .= "<b><font color='green'>&bull;</font> <a href='./#SpamLoversRe'>Spamlover RE</a></b>: '".($1||$2)."'<br />\n";
33388            $bombsrch = SearchBomb( "SpamLoversRe", ($1||$2) );
33389            $fm .=
33390              "<font color='green'>&bull;</font> matching SpamLoversRe($incFound): '$bombsrch'<br />\n"
33391              if $bombsrch;
33392          }
33393
33394
33395
33396        if ( $contentOnlyRe && $text =~ /($contentOnlyReRE)/ ) {
33397            $fm .=
33398"<b><font color='yellow'>&bull;</font> <a href='./#contentOnlyRe'>Restrict to Content Only RE</a></b>: '".($1||$2)."'<br />\n";
33399            $bombsrch = SearchBomb( "contentOnlyRe", ($1||$2) );
33400            $fm .=
33401              "<font color='yellow'>&bull;</font> matching contentOnlyRe($incFound): '$bombsrch'<br />\n"
33402              if $bombsrch;
33403          }
33404                my $textheader;
33405        if ($headerLen > -1 ) {
33406             $textheader = substr($text,0,$headerLen);
33407        } else {
33408            $textheader = $text;
33409        }
33410
33411        if (  $bombRe && ($bombsrch = SearchBombW( "bombRe", \$text ))) {
33412            if ( !$DoBombRe ) {
33413                $fm .=
33414"<i><font color='red'>&bull;</font> <a href='./#DoBombRe'>bombRe</a> is <b>disabled because DoBombRe is disabled</b></i><br />\n";
33415              }
33416            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombRe'>bombRe</a></b>: '$bombsrch'<br />\n";
33417            $fm .= "<font color='red'>&bull;</font> matching bombRe($incFound): '$weightMatch'<br />\n";
33418          }
33419
33420        if (  $bombDataRe && ($bombsrch = SearchBombW( "bombDataRe", \$text ))) {
33421            if ( !$DoBombRe ) {
33422                $fm .=
33423"<i><font color='red'>&bull;</font> <a href='./#DoBombRe'>bombDataRe</a> is <b>disabled because DoBombRe is disabled</b></i><br />\n";
33424              }
33425            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombDataRe'>bombDataRe</a></b>: '$bombsrch'<br />\n";
33426            $fm .= "<font color='red'>&bull;</font> matching bombDataRe($incFound): '$weightMatch'<br />\n";
33427          }
33428
33429        if (  $bombHeaderRe && ($bombsrch = SearchBombW( "bombHeaderRe", \$textheader ))) {
33430            if ( !$DoBombHeaderRe ) {
33431                $fm .=
33432"<i><font color='red'>&bull;</font> <a href='./#DoBombHeaderRe'>bombHeaderRe</a> is <b>disabled</b></i><br />\n";
33433              }
33434            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombHeaderRe'>bombHeaderRe</a></b>: '$bombsrch'<br />\n";
33435            $fm .= "<font color='red'>&bull;</font> matching bombHeaderRe($incFound): '$weightMatch'<br />\n";
33436          }
33437
33438        if (  $bombSubjectRe && defined $sub && ($bombsrch = SearchBombW( "bombSubjectRe", \$sub))) {
33439            if ( !$DoBombHeaderRe ) {
33440                $fm .=
33441"<i><font color='red'>&bull;</font> <a href='./#DoBombHeaderRe'>bombSubjectRe</a> is <b>disabled</b> because DoBombHeaderRe is disabled</i><br />\n";
33442              }
33443            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombSubjectRe'>bombSubjectRe</a></b>: '$bombsrch'<br />\n";
33444            $fm .=
33445              "<font color='red'>&bull;</font> matching bombSubjectRe($incFound): '$weightMatch'<br />\n";
33446
33447         } elsif (  $bombSubjectRe && $mystatus eq "text" && ($bombsrch = SearchBombW( "bombSubjectRe", \$text))) {
33448            if ( !$DoBombHeaderRe ) {
33449                $fm .=
33450"<i><font color='red'>&bull;</font> <a href='./#DoBombHeaderRe'>bombSubjectRe</a> is <b>disabled</b> because DoBombHeaderRe is disabled</i><br />\n";
33451              }
33452            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombSubjectRe'>bombSubjectRe</a></b>: '$bombsrch'<br />\n";
33453            $fm .=
33454              "<font color='red'>&bull;</font> matching bombSubjectRe($incFound): '$weightMatch'<br />\n";
33455        }
33456
33457        if (  $bombCharSets && ($bombsrch = SearchBombW( "bombCharSets", \$textheader ))) {
33458            if ( !$DoBombHeaderRe ) {
33459                $fm .=
33460"<i><font color='red'>&bull;</font> <a href='./#DoBombHeaderRe'>bombCharSets</a> is <b>disabled</b> because DoBombHeaderRe is disabled</i><br />\n";
33461              }
33462            $fm .=
33463              "<b><font color='red'>&bull;</font> <a href='./#bombCharSetsRe'>bombCharSetsRe</a></b>: '$bombsrch'<br />\n";
33464            $fm .= "<font color='red'>&bull;</font> matching bombCharSets($incFound): '$weightMatch'<br />\n";
33465          }
33466
33467        if (  $bombSuspiciousRe && ($bombsrch = SearchBombW( "bombSuspiciousRe", \$text ))) {
33468            $fm .=
33469"<b><font color='red'>&bull;</font> <a href='./#bombSuspiciousRe'>bombSuspiciousRe</a></b>: '$bombsrch'<br />\n";
33470            $fm .=
33471"<font color='red'>&bull;</font> matching bombSuspiciousRe($incFound): '$weightMatch'<br />\n";
33472          }
33473
33474
33475        if (  $blackRe && ($bombsrch = SearchBombW( "blackRe", \$text ))) {
33476            if ( !$DoBlackRe ) {
33477                $fm .=
33478"<i><font color='red'>&bull;</font> <a href='./#DoBlackRe'>blackRe</a> is  <b>disabled</b></i><br />\n";
33479              }
33480            $fm .= "<b><font color='red'>&bull;</font> <a href='./#blackRe'>blackRe</a></b>: '$bombsrch'<br />\n";
33481            $fm .= "<font color='red'>&bull;</font> matching blackRe($incFound): '$weightMatch'<br />\n";
33482          }
33483
33484
33485        if (  $bombSenderRe && ($bombsrch = SearchBombW( "bombSenderRe", \$textheader )))
33486        {
33487            $fm .= "<b><font color='red'>&bull;</font> <a href='./#bombSenderRe'>bombSenderRe</a></b>: '$bombsrch'<br />\n";
33488            $fm .= "<font color='red'>&bull;</font> matching bombSenderRe($incFound): '$weightMatch'<br />\n";
33489        }
33490
33491        my $obfuscatedip;
33492        my $obfuscateduri;
33493        my $maximumuniqueuri;
33494        my $maximumuri;
33495        if ( !$ValidateURIBL )
33496        {
33497            $fm .=
33498"<i><font color='red'>&bull;</font> <a href='./#ValidateURIBL'>URIBL check</a> is <b>disabled because ValidateURIBL is disabled</b></i><br />\n";
33499        } else {
33500            my $tmpfh = time;
33501            $Con{$tmpfh} = {};
33502            $Con{$tmpfh}->{mailfrom} = $mailfrom;
33503            my $color = 'green';
33504            my $failed = 'OK';
33505            my $res = &URIBLok_Run($tmpfh,\$text,$ip,'');
33506            if (! $res) {
33507                $color = 'red';
33508                $failed = 'failed';
33509            }
33510            $fm .=
33511"<b><font color='$color'>&bull;</font> <a href='./#ValidateURIBL'>URIBL check</a></b>: '$failed'<br />\n";
33512            $fm .=
33513"<font color='$color'>&nbsp;&bull;</font> URIBL result: '$Con{$tmpfh}->{messagereason}'<br />" if $Con{$tmpfh}->{messagereason};
33514
33515            $obfuscatedip = $Con{$tmpfh}->{obfuscatedip};
33516            $obfuscateduri= $Con{$tmpfh}->{obfuscateduri};
33517            $maximumuniqueuri = $Con{$tmpfh}->{maximumuniqueuri};
33518            $maximumuri = $Con{$tmpfh}->{maximumuri};
33519            delete $Con{$tmpfh};
33520        }
33521
33522        my $cOK;
33523        ($mail,$cOK) = &clean( substr( $mail, 0, $mBytes ) );
33524        $mail =~ s/^helo:\s*\r?\nrcpt\s*\r?\n//o;
33525
33526        if ($helo) {
33527            $fm .= "<b><font color='red'>&bull;</font> HELO Blacklist</b>: '$helo'</b><br />\n"
33528              if ( $HeloBlack{ lc $helo } );
33529            $fm .=
33530"<b><font color='#66CC66'>&bull;</font> <a href='./#heloBlacklistIgnore'>HELO Blacklist Ignore</a></b>: '$helo'</b><br />\n"
33531              if ( $heloBlacklistIgnore && $helo =~ /$HBIRE/ );
33532            if ( !$DoInvalidFormatHelo ) {
33533                $fm .= "<b><font color='orange'>&bull;</font>invalidHeloRe not activated</b><br />\n";
33534              }
33535
33536            if ( $invalidHeloRe && $helo !~ /$validHeloReRE/ && ($bombsrch = SearchBombW( "invalidHeloRe", \$helo )))
33537            {
33538                $fm .= "<b><font color='red'>&bull;</font> <a href='./#invalidHeloRe'>invalidHeloRe</a></b>: '$bombsrch'<br />\n";
33539                $fm .= "<font color='red'>&bull;</font> matching invalidHeloRe($incFound): '$weightMatch'<br />\n";
33540            }
33541        }
33542
33543
33544        if ( exists $PBBlack{$ip} ) {
33545            my ( $ct, $ut, $pbstatus, $value, $sip, $reason ) = split( ' ', $PBBlack{$ip} );
33546            push( @t, 0.97 );
33547
33548            $fm .=
33549"<b><font color='red'>&bull;</font> $ip is in <a href='./#pbdb'>PB Black</a></b>: score:$value, last event - $reason<br />\n";
33550          }
33551        if ( exists $PBWhite{$ip} ) {
33552            my ( $ct, $ut, $pbstatus, $sip, $reason ) = split( ' ', $PBWhite{$ip} );
33553
33554
33555            $fm .= "<b><font color=#66CC66>&bull;</font> $ip is in <a href='./#pbdb'>PB White</a></b><br />\n";
33556          }
33557
33558        if ( $ret = matchIP( $ip, 'noProcessingIPs', 0, 1 ) ) {
33559            $fm .=
33560"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#noProcessingIPs'>noProcessingIPs</a> ($ret)</b><br />\n";
33561          }
33562        if ( $ret = matchIP( $ip, 'whiteListedIPs', 0, 1 ) ) {
33563            $fm .=
33564"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#whiteListedIPs'>whiteListedIPs</a> ($ret)</b><br />\n";
33565          }
33566        if ( $ret = matchIP( $ip, 'noPB', 0, 1 ) ) {
33567            $fm .=
33568              "<b><font color='green'>&bull;</font> IP $ip is in <a href='./#noPB'>noPB IPs</a> ($ret)</b><br />\n";
33569          }
33570
33571        if ( exists $RBLCache{$ip} ) {
33572                my ( $ct, $mm, $status, @rbllists ) = split( ' ', $RBLCache{$ip} );
33573                $mm = '20'.$mm.':00';
33574                $mm =~ s/\// /o;
33575                $status = ( $status == 2 ? 'as ok at '.$mm : "as not ok at $mm , listed by @rbllists" );
33576                $fm .=
33577                  "<b><font color='red'>&bull;</font> $ip is in RBLCache</b>: inserted $status<br />\n";
33578        }
33579
33580
33581        if ( exists $MXACache{$emfd} ) {
33582            my ($ct,$status) = split(' ',$MXACache{$emfd},2);
33583            $fm .= "<b><font color='green'>&bull;</font> domain $emfd has valid MXA record</b>: $status<br />\n";
33584        }
33585        if ( exists $PTRCache{$ip} ) {
33586            my ( $ct, $status, $dns ) = split( ' ', $PTRCache{$ip} );
33587            my %statList = (
33588                1 => 'no PTR',
33589                2 => "PTR OK - $dns",
33590                3 => "PTR NOTOK - $dns"
33591            );
33592            my $color = ($status == 2 ? 'green' : 'red');
33593            $status = $statList{$status};
33594            $fm .= "<b><font color='$color'>&bull;</font> $ip is in PTRCache</b>: status=$status<br />\n";
33595          }
33596        if ( exists $RWLCache{$ip} ) {
33597            my ( $ct, $status ) = split( ' ', $RWLCache{$ip} );
33598            my %statList = (
33599                1 => 'tusted',
33600                2 => 'trusted but RWLminHits not reached',
33601                3 => 'trusted and whitelisted',
33602                4 => 'not listed'
33603            );
33604            my $color = ($status == 4 ? 'orange' : 'green');
33605            $status = $statList{$status};
33606            $fm .= "<b><font color='$color'>&bull;</font> $ip is in RWLCache</b>: status=$status<br />\n";
33607          }
33608
33609        if ( $ret = matchIP( $ip, 'acceptAllMail', 0, 1 ) ) {
33610            $fm .=
33611"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#acceptAllMail'>Accept All Mail</a> ($ret)</b><br />\n";
33612          }
33613        if ( $ret = matchIP( $ip, 'noBlockingIPs', 0, 1 ) ) {
33614                $fm .=
33615"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#noBlockingIPs'>noBlockingIPs</a> ($ret)</b><br />\n";
33616        }
33617        if ( $ret = matchIP( $ip, 'ispip', 0, 1 ) ) {
33618            $fm .=
33619"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#ispip'>ISP/Secondary MX Servers</a> ($ret)</b><br />\n";
33620          }
33621
33622        if ( $ret = matchIP( $ip, 'denySMTPConnectionsFrom', 0, 1 ) ) {
33623                $fm .=
33624"<b><font color='red'>&bull;</font> IP $ip is in <a href='./#denySMTPConnectionsFrom'>denySMTPConnectionsFrom</a> ($ret)</b><br />\n";
33625         }
33626
33627
33628        if ( $ret = matchIP( $ip, 'denySMTPConnectionsFromAlways', 0, 1 ) ) {
33629                $fm .=
33630"<b><font color='red'>&bull;</font> IP $ip is in <a href='./#denySMTPConnectionsFromAlways'>denySMTPConnectionsFromAlways</a>($ret)</b><br />\n";
33631        }
33632
33633		if ( my ( $cidr , $ct, $status, $data ) = SBCacheFind($ip) ) {
33634            my %statList = (
33635                0 => 'not classified',
33636                1 => 'black country',
33637                2 => 'white SenderBase',
33638                3 => 'changed to black country'
33639            );
33640            my $color = 'orange';
33641            $color = 'red' if $status % 2;
33642            $color = 'green' if $status == 2;
33643            $status = $statList{$status};
33644            $data =~ s/\|/, /og;
33645            $fm .= "<b><font color='$color'>&bull;</font> $ip is in CountryCache</b>: status=$status, data=$data<br />\n";
33646        } else {
33647            my $tmpfh = time;
33648            $Con{$tmpfh} = {};
33649            $Con{$tmpfh}->{ip} = $ip;
33650            $Con{$tmpfh}->{mailfrom} = $mailfrom;
33651            my %statList = (
33652                0 => 'not classified',
33653                1 => 'black country',
33654                2 => 'white SenderBase',
33655                3 => 'changed to black country'
33656            );
33657            my $res = SenderBaseOK($tmpfh, $ip);
33658            my $data = $Con{$tmpfh}->{sbdata};
33659            my $status = $Con{$tmpfh}->{sbstatus};
33660            my $color = 'orange';
33661            $color = 'red' if $status % 2;
33662            $color = 'green' if $status == 2;
33663            $status = $statList{$status};
33664            $data =~ s/\|/, /og;
33665            $fm .= "<b><font color='$color'>&bull;</font> $ip SenderBase</b>: status=$status, data=$data<br />\n" if $data;
33666            delete $Con{$tmpfh};
33667        }
33668        my @t;
33669        my %got = ();
33670        my $v;
33671		if (exists $Griplist{$ip3}) {
33672		    if ($Griplist{$ip3} !~ /$IPprivate/o) {
33673			    $v = $Griplist{$ip3};
33674			    $v = 0.01 if !$v;
33675			    $v = 0.99 if  $v == 1;
33676			}
33677		}
33678    	if ($griplist && ( !$mystatus ||  $mystatus eq "ip" )) {
33679            if ( $ispip  && matchIP( $ip, 'ispip', 0, 1 ) ) {
33680            	if ($ispgripvalue ne '') {
33681                    $v = $ispgripvalue;
33682                } else {
33683                    $v=$Griplist{x};
33684                }
33685            }
33686
33687            $fm .= "<b><font color='gray'>&bull;</font> $ip3 has a Griplist value of $v</b><br />\n" if $v;
33688
33689    	}
33690
33691		push(@t,0.97) if $foundReceived <= 0;
33692
33693        $fm =~ s/($IPRe)/my$e=$1;($e!~$IPprivate)?"<a href=\"javascript:void(0);\" title=\"take an action on that IP\" onclick=\"popIPAction('$1');return false;\">$1<\/a>":$e;/goe;
33694        $fm =~ s/(')?($EmailAdrRe?\@$EmailDomainRe)(')?/"<a href=\"javascript:void(0);\" title=\"take an action on that address\" onclick=\"popAddressAction('".&encHTMLent($2)."');return false;\">".&encHTMLent($1.$2.$3)."<\/a>";/goe;
33695
33696
33697
33698        $fm .= "<br /><hr><br />";
33699        my ( $v, $lt, $t, %seen );
33700        while ( $mail =~ /([-\$A-Za-z0-9\'\.!\240-\377]+)/g ) {
33701        	next if length($1) > 20;
33702        	next if length($1) < 2;
33703        	$lt = $t;
33704        	$t  = BayesWordClean($1);
33705        	my $j = "$lt $t";
33706            next if $seen{$j}++ > 1;
33707            if  ($v = $Spamdb{$j}) {
33708            	push( @t, $v );
33709            } else {
33710            	push( @t, $v ) if $v = $Starterdb{$j};
33711			}
33712#            mlog(0,"j = $j, v = $v");
33713            $got{$j} = $v if $v;
33714        }
33715		my $cnt       = 0;
33716		my $bayestext;
33717        if (!$mystatus) {
33718        $bayestext = "<font color='red'>&bull; Bayesian Check is disabled</font>"
33719          if !$DoBayesian;
33720        $ba .=
33721"<b><font size='3' color='#003366'>Bayesian Analysis: $bayestext</font></b><br /><br /><table cellspacing='0' cellpadding='0'>";
33722        $ba .= "<tr>
33723  <td style=\"padding-left:5px; padding-right:5px; padding-top:5; padding-bottom:5; text-align:right; font-size:small;\"><b>Bad Words</b></td>
33724  <td style=\"padding-left:5px; padding-right:5px; padding-top:5; padding-bottom:5; text-align:left; font-size:small; background-color:#F4F4F4\"><b>Bad Prob&nbsp;</b></td>
33725  <td style=\"padding-left:20px; padding-right:5px; padding-top:5; padding-bottom:5; text-align:right; font-size:small;\"><b>Good Words</b></td>
33726  <td style=\"padding-left:5px; padding-right:5px; padding-top:5; padding-bottom:5; text-align:left; font-size:small; background-color:#F4F4F4\"><b>Good Prob</b></td>
33727  </tr>\n";
33728        foreach (
33729            sort { abs( $got{$b} - .5 ) <=> abs( $got{$a} - .5 ) }
33730            keys %got
33731          ) {
33732            my $g = sprintf( "%.4f", $got{$_} );
33733            if ( $g < 0.5 ) {
33734                $ba .= "<tr>
33735    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"></td>
33736    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:left; font-size:small; background-color:#F4F4F4\"></td>
33737    <td style=\"padding-left:20px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\">$_</td>
33738    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:left; font-size:small; background-color:#F4F4F4\">$g</td>
33739    </tr>\n";
33740              } else {
33741                $ba .= "<tr>
33742    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\">$_</td>
33743    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:left; font-size:small; background-color:#F4F4F4\">$g</td>
33744    <td style=\"padding-left:20px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"></td>
33745    <td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:left; font-size:small; background-color:#F4F4F4\"></td>
33746    </tr>\n";
33747              }
33748            last if $cnt++ > 20;
33749          }
33750        $ba .= "</td></tr></table>\n";
33751        @t  = sort { abs( $b - .5 ) <=> abs( $a - .5 ) } @t;
33752        @t=@t[0..($maxBayesValues - 1)];
33753        $st = "<br />Totals: ";
33754        foreach (@t) { $st .= sprintf( "%.4f ", $_ ) if $_; }
33755        $st .= "\n";
33756    	(my $p1, my $p2, my $c1, $SpamProb, $SpamProbConfidence) = BayesHMMProb(\@t);
33757		$SpamProbConfidence = 0 if @t < 2 && $t[0] eq '';
33758		$SpamProb = 0.8 if @t < 2 && $t[0] eq '';
33759        $st .=
33760"<br /><hr><br /><b><font size=\"3\" color=\"#003366\">Spam/Ham Probabilities:</font></b><br /><br />\n<table cellspacing=\"0\" cellpadding=\"0\">"
33761          if $baysConf;
33762        $st .=
33763"<br /><hr><br /><b><font size=\"3\" color=\"#003366\">Spam Probability:</font></b><br /><br />\n<table cellspacing=\"0\" cellpadding=\"0\">"
33764          if !$baysConf;
33765        $st .= sprintf(
33766" <tr><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"><b>spamprobability</b>:</td><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; font-size:small;\">%.4f</td></tr>\n",
33767            $p1 )
33768          if $baysConf;
33769        $st .= sprintf(
33770" <tr><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"><b>hamprobability</b>:</td><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; font-size:small;\">%.4f</td></tr>\n",
33771            $p2 )
33772          if $baysConf;
33773        $st .= sprintf(
33774" <tr><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"><b>combined probability</b>:</td><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; font-size:small;\">%.4f</td></tr>\n",
33775            $SpamProb )
33776          if $baysConf;
33777        $st .= sprintf(
33778" <tr><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"><b>probability</b>:</td><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; font-size:small;\">%.4f</td></tr>\n",
33779            $SpamProb )
33780          if !$baysConf;
33781        $st .= sprintf(
33782" <tr><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; text-align:right; font-size:small;\"><b>bayesian confidence</b>:</td><td style=\"padding-left:5px; padding-right:5px; padding-top:0; padding-bottom:0; font-size:small;\">%.4f</td></tr>\n",
33783            $SpamProbConfidence )
33784          if $baysConf;
33785 		}
33786        $st .= " </table><br /></div><br />";
33787        $mail =~ s/([^\n]{70,84}[^\w\n<\@])/$1\n/g;
33788        $mail =~ s/\s*\n+/\n/g;
33789        $mail =~ s/<\/textarea>/\/textarea/ig;
33790
33791      }
33792
33793      $mail = $orgmail if $mystatus;
33794      $mail =~ s/\r//gos;
33795
33796      my $h1 = $WebIP{$ActWebSess}->{lng}->{'msg500060'} || $lngmsg{'msg500060'};
33797      my $h2 = $WebIP{$ActWebSess}->{lng}->{'msg500061'} || $lngmsg{'msg500061'};
33798      my $h3 = $WebIP{$ActWebSess}->{lng}->{'msg500062'} || $lngmsg{'msg500062'};
33799      my $h4 = $WebIP{$ActWebSess}->{lng}->{'msg500063'} || $lngmsg{'msg500063'};
33800
33801    <<EOT;
33802$headerHTTP
33803$headerDTDTransitional
33804$headers
33805<script type="text/javascript">
33806//<![CDATA[
33807function getInput() { return document.getElementById("mail").value; }
33808function setOutput(string) {document.getElementById("mail").value=string; }
33809
33810function replaceIt() { try {
33811var findText = document.getElementById("find").value;
33812var replaceText = document.getElementById("replace").value;
33813setOutput(getInput().replace(eval("/"+findText+"/ig"), replaceText));
33814} catch(e){}}
33815
33816//-->
33817//]]>
33818</script>
33819<div class="content">
33820<h2>ASSP Mail Analyzer</h2>
33821<div class="note"><small>$h1</small>
33822</div>
33823
33824<p class="note" ><small>$h3
33825</small></p>
33826<br />
33827$fm$ba$st
33828<form action="" method="post">
33829    <table class="textBox">
33830        <tr>
33831            <td >
33832             <span style="float: left">Replace: <input type="text" id="find" size="20" /> with <input type="text" id="replace" size="20" /> <input type="button" value="Replace" onclick="replaceIt();" /></span>
33833            </td >
33834        </tr>
33835        <tr>
33836            <td class="noBorder" align="center">$h2<br />
33837            <textarea id="mail" name="mail" rows="10" cols="60" wrap="off">$mail</textarea>
33838            </td>
33839        </tr>
33840        <tr>
33841            <td class="noBorder" align="center"><input type="submit" name="B1" value=" Analyze " /></td>
33842        </tr>
33843    </table>
33844</form>
33845<br />
33846<p class="note" ><small>
33847<div class="textbox">
33848$h4</small></p>
33849
33850</div>
33851</div>
33852
33853$footers
33854<form name="ASSPconfig" id="ASSPconfig" action="" method="post">
33855  <input name="theButtonLogout" type="hidden" value="" />
33856</form>
33857</body></html>
33858EOT
33859}
33860
33861
33862sub cleanincFound {
33863    my $i = shift;
33864    $i =~ s/\<[^\>]+\>//go;
33865    return $i;
33866}
33867
33868
33869
33870sub AnalyzeText {
33871    my $fh = shift;
33872    my $this = $Con{$fh};
33873    my @t;
33874    my $bombsrch;
33875    my $mBytes = $MaxBytes ? $MaxBytes : 10000;
33876    my @sips;
33877
33878        my ( $ba, $st, $fm, %fm, %to, %wl, $ip, $ipnet, $helo, $text, $header, $received, $emfd );
33879    my $foundReceived = 0;
33880    my $mail = $this->{header};
33881    $mail =~ s/^.*?\n[\r\n\s]+//so;
33882    $mail =~ s/\r?\n/\r\n/gos;
33883    my $hl = getheaderLength(\$mail);
33884    my $mBytes = $MaxBytes ? $MaxBytes + $hl : 10000 + $hl;
33885    my $lm = length($mail);
33886    $mail = substr( $mail, 0, $mBytes );
33887    $fm = "analyze is restricted to a maximum length of $mBytes bytes\n" if $lm > $mBytes;
33888    if ($mail =~ /X-Assp-ID: (.+)/io) {
33889        $fm .= "ASSP-ID: $1\n";
33890    }
33891    if ($mail =~ s/X-Assp-Envelope-From:\s*($HeaderValueRe)//ios) {
33892        my $s = $1;
33893        &headerUnwrap($s);
33894        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/io) {
33895            $s = batv_remove_tag(0,lc $1,'');
33896            $fm{$s}=1;
33897            ($emfd) = $s =~ /\@([^@]*)/o;
33898        }
33899    }
33900    if ($mail =~ s/X-Assp-Recipient:\s*($HeaderValueRe)//ios) {
33901        my $s = $1;
33902        &headerUnwrap($s);
33903        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/io) {
33904            $s = batv_remove_tag(0,lc $1,'');
33905            $to{$s}=1;
33906        }
33907    }
33908    if (! scalar keys %to && $mail =~ s/X-Assp-Envelope-For:\s*($HeaderValueRe)//ios) {
33909        my $s = $1;
33910        &headerUnwrap($s);
33911        if ($s =~ /($EmailAdrRe\@$EmailDomainRe)/io) {
33912            $s = batv_remove_tag(0,$1,'');
33913            $to{lc $s}=1;
33914        }
33915    }
33916    my $bod  = $mail;
33917    my $sub = undef;
33918    my $wildcardUser = lc $wildcardUser;
33919    my $headerLen = index($mail,"\015\012\015\012");
33920    if ($headerLen > -1) {
33921        my $fhh;
33922        do {
33923           $fhh = rand(1000000);
33924        } while exists $Con{$fhh};
33925        &sigoffTry(__LINE__);
33926        $Con{$fhh}->{header} = $mail;
33927        $Con{$fhh}->{headerpassed} = 1;
33928        &makeSubject($fhh);
33929        $sub = $Con{$fhh}->{subject3} if defined $Con{$fhh}->{subject3};
33930
33931        if (defined $sub) {
33932            headerUnwrap($sub);
33933            $sub =~ s/^fwd.\s//gio;
33934            $sub =~ s/^fw.\s//gio;
33935            $sub =~ s/^aw.\s//gio;
33936            $sub =~ s/^re.\s//gio;
33937            # remove the spam subject header addition if present
33938            my $spamsub = $spamSubject;
33939            if ($spamsub) {
33940                $spamsub =~ s/(\W)/\\$1/go;
33941                $sub     =~ s/$spamsub//gi;
33942            }
33943            $sub =~ s/\r//o;
33944        }
33945        delete $Con{$fhh};
33946
33947    }
33948	if ($mail =~ /\nsubject: *($HeaderValueRe)/iso) {
33949        	$sub = substr($1,0,$maxSubjectLength);
33950        	headerUnwrap($sub);
33951            $sub =~ s/^fwd.\s//gio;
33952            $sub =~ s/^fw.\s//gio;
33953            $sub =~ s/^aw.\s//gio;
33954            $sub =~ s/^re.\s//gio;
33955            # remove the spam subject header addition if present
33956            my $spamsub = $spamSubject;
33957            if ($spamsub) {
33958                $spamsub =~ s/(\W)/\\$1/go;
33959                $sub     =~ s/$spamsub//gi;
33960            }
33961        }
33962    $header = "Subject: " . $sub . "\n" if $sub;
33963
33964    $header .= $1 if $bod =~ /(X-Assp-ID: .*)/io;
33965
33966    $header .= $1 if $bod =~ /(X-Assp-Tag: .*)/io;
33967
33968    $header .= $1 if $bod =~ /(X-Assp-Envelope-From: .*)/io;
33969
33970    $header .= $1 if $bod =~ /(X-Assp-Intended-For: .*)/io;
33971
33972    $bod =~ s/X-Assp-Spam-Prob: .*\n//gio;
33973    if ( $bod =~ /\nReceived: /o ) {
33974        $bod =~ s/^.*?\nReceived: /Received: /so;
33975    } else {
33976        $bod =~ s/^.*?\n((\w[^\n]*\n)*Subject:)/$1/sio;
33977        $bod =~ s/\n> /\n/go;
33978    }
33979	$fm .= "removed all local X-ASSP- header lines for analysis\n"
33980        if ($mail =~ s/x-assp-[^()]+?:\s*$HeaderValueRe//gios);
33981    if ($mail) {
33982        my $name = $myName;
33983        $name =~ s/(\W)/\\$1/go;
33984        if ( $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*ip\s*=\s*(\d+\.\d+\.\d+\.\d+|[0-9a-f:]+)/ios ) {
33985            $ip = $1;
33986        } else {
33987
33988            while ( $mail =~ /Received: from.*?\(\[([0-9\.]+|[0-9a-f:]+).*?helo=(.{0,64})\)(?:\s+by\s+$myName)?\s+with/isgo ) {
33989                $ip = ipv6expand(ipv6TOipv4($1));
33990                $helo = $2;
33991                $foundReceived = -1;
33992            }
33993        }
33994
33995        $fm .= "Connecting IP: $ip\n" if $ip;
33996        my $conIP = $ip;
33997        $ipnet = &ipNetwork($ip, 1);
33998        if ( $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*helo\s*=\s*([^\r\n]+)/ios ) {
33999            $helo = $1;
34000        }
34001        $fm .= "Connecting HELO: $helo\n\n" if $helo;
34002        if ( $mail =~ /(?:^[\s\r\n]*|\r?\n)\s*text\s*=\s*(.+)/ios ) {
34003            $text = $1;
34004        } else {
34005            $text = $mail;
34006        }
34007                if ($ispHostnames) {
34008            my $ispHost;
34009            while ( $mail =~ /(Received:\s+from\s+(?:([^\s]+)\s)?(?:.+?)($IPRe)(.{1,80})by.{1,20}($ispHostnamesRE))/gis)
34010            {
34011                $helo     = $2;
34012                $received = $1;
34013                $ispHost = $4;
34014                $ip = ipv6expand(ipv6TOipv4($3));
34015                $ipnet = &ipNetwork($ip, 1);
34016                $foundReceived = 1;
34017
34018            }
34019            $fm =~ s/(Connecting IP: [^\n]+)/$1 is an ISPIP/o;
34020            $fm =~ s/(Connecting HELO: [^\n]+)/$1 is HELO from ISP-host: $ispHost/o;
34021            $fm .= "\nISP/Secondary Header:'$received'\n"    if $received;
34022            $fm .= "Switched to ISP/Secondary IP: '$ip'\n\n" if $received;
34023        }
34024        if ($foundReceived <= 0) {
34025            $foundReceived += () = $mail =~ /(Received: from )/isgo;
34026            $fm .= "no foreign received header line found\n\n"
34027              if ($foundReceived <= 0) ;
34028        }
34029
34030
34031
34032        $fm .= "general hints:\n\n$fm\n\n" if $fm;
34033        $fm .= "sender and reply addresses:\n";
34034        my $mailfrom;
34035        foreach (keys %fm) {
34036            $fm .=  "MAIL FROM: $_ ,";
34037            $mailfrom = $_;
34038        }
34039        while ($mail =~ /(?:^|\n)(from|sender|reply-to|errors-to|list-\w+|ReturnReceipt|Return-Receipt-To|Disposition-Notification-To):($HeaderValueRe)/igos) {
34040            my $who = $1;
34041            my $s = $2;
34042            &headerUnwrap($s);
34043            while ($s =~ /($EmailAdrRe\@$EmailDomainRe)/go) {
34044                my $ss = batv_remove_tag(0,$1,'');
34045                $fm{lc $ss}=1;
34046                $fm .=  " $who: $ss ,";
34047            }
34048        }
34049        $fm =~ s/,$/\n\n/o;
34050
34051
34052        $fm .= "recipient addresses:\n";
34053        foreach (keys %to) {
34054            $fm .=  "RCPT TO: $_ ,";
34055            my $newadr = RcptReplace($_,$mailfrom,'RecRepRegex');
34056            $fm =~ s/,$/(replaced with $newadr),/o if lc($newadr) ne lc $_;
34057        }
34058        while ($mail =~ /(?:^|\n)(to|cc|bcc):($HeaderValueRe)/igos) {
34059            my $who = $1;
34060            my $s = $2;
34061            &headerUnwrap($s);
34062            while ($s =~ /($EmailAdrRe\@$EmailDomainRe)/go) {
34063                my $ss = batv_remove_tag(0,$1,'');
34064                $to{lc $ss}=1;
34065                $fm .=  " $who: $ss ,";
34066            }
34067        }
34068        $fm =~ s/,$/\n\n/o;
34069
34070        $fm .= "Feature Matching:\n\n";
34071		my $mfd;
34072		my $mfdd;
34073        while ( $mail =~ /($EmailAdrRe\@$EmailDomainRe)/go ) {
34074            my $ad    = lc $1;
34075#            my $mf   = $ad;
34076            my $mf = batv_remove_tag(0,$ad,'');
34077            $mfd  = $1 if $mf =~ /\@(.*)/;
34078            $mfdd = $1 if $mf =~ /(\@.*)/;
34079            next if $fm{$ad}++;
34080
34081            if (matchSL( $mf, 'noProcessing' )) {
34082                $fm .= "noProcessing: '$slmatch'\n";
34083              }
34084            if ( $noProcessingDomains && $mf =~ /($NPDRE)/ ) {
34085                $fm .= "noProcessingDomains: '$1'\n";
34086              }
34087            if ( matchSL( $mf, 'noProcessingFrom' ) ) {
34088                $fm .= "noProcessingFrom Address: '$slmatch'\n";
34089              }
34090            if ($blackListedDomains && $mf =~ /($BLDRE)/ ) {
34091                $fm .= "blackListedDomains: '$1'\n";
34092              }
34093            if ($whiteListedDomains && $mf =~ /($WLDRE)/ ) {
34094                $fm .= "whiteListedDomains: '$1'\n";
34095              }
34096
34097            $fm .= "Redlist: '$ad'\n"
34098              if $Redlist{$ad};
34099            $fm .= "Redlisted Domain/ Wildcard: '$wildcardUser$mfdd'\n"
34100              if $Redlist{"$wildcardUser$mfdd"};
34101            $fm .= "Whitelisted WildcardDomain: '$wildcardUser$mfdd'\n"
34102              if &Whitelist("$wildcardUser$mfdd");
34103
34104            if (&Whitelist($ad)) {
34105                $fm .= "Whitelist: '$ad'\n";
34106                foreach my $t (sort keys %to) {
34107                    if (! &Whitelist($ad,$t)) {
34108                        $fm .= "Whitelist removed for $t: '$ad'\n";
34109                    }
34110                }
34111            }
34112
34113            foreach my $t (sort keys %to) {
34114                $fm .= "on personal Blacklist for $t: '$ad'\n"
34115                    if exists $PersBlack{lc "$t,$ad"};
34116            }
34117
34118            $fm .= "No URIBL sender: '$mf'\n"
34119              if matchSL( $mf, 'noURIBL' );
34120        }
34121    }
34122
34123    if ( $preHeaderRe && $text =~ /($preHeaderReRE)/ ) {
34124
34125        $fm .= "preHeaderRe: '".($1||$2)."\n";
34126        $bombsrch = SearchBomb( "preHeaderRe", ($1||$2) );
34127        $fm .= " matching preHeaderRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34128          if $bombsrch;
34129    }
34130    if ( $noSPFRe && $text =~ /($noSPFReRE)/ ) {
34131
34132        $fm .= "No SPF RE: '".($1||$2)."'\n";
34133        $bombsrch = SearchBomb( "noSPFRe", ($1||$2) );
34134        $fm .= " matching noSPFRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34135          if $bombsrch;
34136    }
34137    if ( $strictSPFRe && $text =~ /($strictSPFReRE)/ ) {
34138
34139        $fm .= "Strict SPF RE: '".($1||$2)."'\n";
34140        $bombsrch = SearchBomb( "strictSPFRe", ($1||$2) );
34141        $fm .= " matching strictSPFRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34142          if $bombsrch;
34143    }
34144    if ( $blockstrictSPFRe && $text =~ /($blockstrictSPFReRE)/ ) {
34145
34146        $fm .= "Block Strict SPF RE: '".($1||$2)."'\n";
34147        $bombsrch = SearchBomb( "blockstrictSPFRe", ($1||$2) );
34148        $fm .= " matching blockstrictSPFRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34149          if $bombsrch;
34150    }
34151    if ( $whiteRe && $text =~ /($whiteReRE)/ ) {
34152
34153        $fm .= "White RE: '".($1||$2)."'\n";
34154        $bombsrch = SearchBomb( "whiteRe", ($1||$2) );
34155        $fm .= " matching whiteRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34156          if $bombsrch;
34157    }
34158    if ( $redRe && $text =~ /($redReRE)/ ) {
34159
34160        $fm .= "Red RE: '".($1||$2)."'\n";
34161        $bombsrch = SearchBomb( "redRe", ($1||$2) );
34162        $fm .= " matching redRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n" if $bombsrch;
34163    }
34164    if ( $npRe && $text =~ /($npReRE)/ ) {
34165
34166        $fm .= "No Processing RE: '".($1||$2)."'\n";
34167        $bombsrch = SearchBomb( "npRe", ($1||$2) );
34168        $fm .= " matching npRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n" if $bombsrch;
34169    }
34170    if (   $baysSpamLoversRe
34171        && $text =~ /($baysSpamLoversReRE)/ )
34172    {
34173
34174        $fm .= "Bayes Spamlover RE: '".($1||$2)."'\n";
34175        $bombsrch = SearchBomb( "baysSpamLoversRe", ($1||$2) );
34176
34177        $fm .= " matching baysSpamLoversRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34178          if $bombsrch;
34179    }
34180    if ( $SpamLoversRe && $text =~ /($SpamLoversReRE)/ ) {
34181
34182        $fm .= "SpamLoversRe: '".($1||$2)."'\n";
34183        $bombsrch = SearchBomb( "SpamLoversRe", ($1||$2) );
34184        $fm .= " matching SpamLoversRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34185          if $bombsrch;
34186    }
34187
34188
34189    if (   $contentOnlyRe
34190        && $text =~ /($contentOnlyReRE)/ )
34191    {
34192
34193        $fm .= "Restrict to Content Only RE<: '".($1||$2)."'\n";
34194        $bombsrch = SearchBomb( "contentOnlyRe", ($1||$2) );
34195        $fm .= " matching contentOnlyRe(" . &cleanincFound($incFound) . "): '$bombsrch'\n"
34196          if $bombsrch;
34197    }
34198
34199    my $textheader;
34200    if ($headerLen > -1 ) {
34201        $textheader = substr($text,0,$headerLen);
34202    } else {
34203        $textheader = $text;
34204    }
34205
34206    if ( $bombRe && ($bombsrch = SearchBombW( "bombRe", \$text ))) {
34207        if ( !$DoBombRe ) {
34208            $fm .= "BombRe is disabled because DoBombRe is disabled\n";
34209        }
34210        $fm .= "BombRe: '$bombsrch'\n";
34211        $fm .= " matching bombRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34212    }
34213    if (   $bombDataRe
34214        && ($bombsrch = SearchBombW( "bombDataRe", \$text )) )
34215    {
34216        if ( !$DoBombRe ) {
34217            $fm .= "BombDataRE is disabled because DoBombRe is disabled\n";
34218        }
34219        $fm .= "BombData RE: '$bombsrch'\n";
34220        $fm .= " matching bombDataRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34221    }
34222    if (   $bombHeaderRe
34223        && ($bombsrch = SearchBombW( "bombHeaderRe", \$textheader )) )
34224    {
34225        if ( !$DoBombHeaderRe ) {
34226            $fm .= "BombHeaderRE is disabled\n";
34227        }
34228        $fm .= "BombHeader RE: '$bombsrch'\n";
34229        $fm .= " matching bombHeaderRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34230    }
34231    if (   $bombSubjectRe
34232        && defined $sub
34233        && ($bombsrch = SearchBombW( "bombSubjectRe", \$sub )))
34234    {
34235        if ( !$DoBombHeaderRe ) {
34236            $fm .= "BombSubjectRE is disabled because DoBombHeaderRe is disabled\n";
34237        }
34238        $fm .= "BombSubject RE: '$bombsrch'\n";
34239        $fm .= " matching bombSubjectRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34240    }
34241    if (   $bombCharSets
34242        && ($bombsrch = SearchBombW( "bombCharSets", \$textheader )))
34243    {
34244        if ( !$DoBombHeaderRe ) {
34245            $fm .= "BombCharsets is disabled because DoBombHeaderRe is disabled\n";
34246        }
34247        $fm .= "BombCharsets: '$bombsrch'\n";
34248        $fm .= " matching bombCharSets(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34249    }
34250    if (   $bombSuspiciousRe
34251        && ($bombsrch = SearchBombW( "bombSuspiciousRe", \$text )))
34252    {
34253        $fm .= "BombSuspiciousRe RE: '$bombsrch'\n";
34254        $fm .= " matching bombSuspiciousRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34255    }
34256
34257    if ( $blackRe && ($bombsrch = SearchBombW( "blackRe", \$text ))) {
34258        if ( !$DoBlackRe ) {
34259            $fm .= "BlackRE is disabled\n";
34260        }
34261        $fm .= "Black RE: '$bombsrch'\n";
34262        $fm .= " matching blackRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34263    }
34264
34265    if (   $bombSenderRe
34266        && ($bombsrch = SearchBombW( "bombSenderRe", \$textheader )))
34267    {
34268        $fm .= "BombSender RE: '$bombsrch'\n";
34269        $fm .= " matching bombSenderRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34270    }
34271    my $cOK;
34272    ($mail,$cOK) = &clean( $mail );
34273    $mail =~ s/^helo:\s*\nrcpt\s*\n//o;
34274
34275    if ($helo) {
34276        $fm .= "HELO Blacklist: '$helo'\n" if ( $HeloBlack{ lc $helo } );
34277        $fm .= "HELO Blacklist Ignore: '$helo'\n"
34278          if ( $heloBlacklistIgnore && $helo =~ /$HBIRE/ );
34279
34280            if (   $invalidHeloRe && $helo !~ /$validHeloReRE/
34281            && ($bombsrch = SearchBombW( "invalidHeloRe", \$helo )))
34282        {
34283            $fm .= "Invalid Format of HELO: '$bombsrch'\n";
34284            $fm .= " matching invalidHeloRe(" . &cleanincFound($incFound) . "): '$weightMatch'\n";
34285        }
34286    }
34287
34288    if ( exists $PBBlack{$ip} ) {
34289        my ( $ct, $ut, $pbstatus, $value, $sip, $reason ) =
34290          split( ' ', $PBBlack{$ip} );
34291        push( @t, 0.97 );
34292
34293        $fm .= "IP $ip is in PB Black: score:$value, last event - $reason\n";
34294    }
34295    if ( exists $PBWhite{$ip} ) {
34296        my ( $ct, $ut, $pbstatus, $sip, $reason ) = split( ' ', $PBWhite{$ip} );
34297        push( @t, 0.03 );
34298
34299        $fm .= "IP $ip is in PB White\n";
34300    }
34301    my $ret;
34302    if ( $ret = matchIP( $ip, 'noProcessingIPs', 0, 1 ) ) {
34303        $fm .= "IP $ip is in noProcessing IPs ($ret)\n";
34304    }
34305    if ( $ret = matchIP( $ip, 'whiteListedIPs', 0, 1 ) ) {
34306        $fm .= "IP $ip is in whiteListed IPs ($ret)\n";
34307    }
34308    if ( $ret = matchIP( $ip, 'noPB', 0, 1 ) ) {
34309        $fm .= "IP $ip is in noPB IPs ($ret)\n";
34310    }
34311    foreach my $iip (@sips) {
34312        if ( exists $RBLCache{$ip} ) {
34313            my ( $ct, $mm, $status, @rbllists ) = split( ' ', $RBLCache{$iip} );
34314            $mm = '20'.$mm.':00';
34315            $mm =~ s/\// /o;
34316            $status = ( $status == 2 ? 'as ok at '.$mm : "as not ok at $mm , listed by @rbllists" );
34317            $fm .= "IP $iip is in DNSBLCache: inserted $status\n";
34318        }
34319    }
34320    if ( exists $SPFCache{"$ip $emfd"} ) {
34321        my ( $ct, $result, $domain ) = split( ' ', $SPFCache{"$ip emfd"} );
34322        $fm .= "IP $ip is in SPFCache: $result, $domain\n";
34323    }
34324    if ( exists $MXACache{$emfd} ) {
34325        my ($ct,$status) = split(' ',$MXACache{$emfd},2);
34326        $fm .= "domain $emfd has valid MXA record: $status\n";
34327    }
34328
34329    if ( exists $PTRCache{$ip} ) {
34330        my ( $ct, $status, $dns ) = split( ' ', $PTRCache{$ip} );
34331        my %statList = (
34332            1 => 'no PTR',
34333            2 => "PTR OK - $dns",
34334            3 => "PTR NOTOK - $dns"
34335        );
34336        $status = $statList{$status};
34337        $fm .= "IP $ip is in PTRCache: status=$status\n";
34338    }
34339    if ( exists $RWLCache{$ip} ) {
34340        my ( $ct, $status) = split( ' ', $RWLCache{$ip} );
34341        my %statList = (
34342            1 => 'tusted',
34343            2 => 'trusted but RWLminHits not reached',
34344            3 => 'trusted and whitelisted',
34345            4 => 'not listed'
34346        );
34347        $status = $statList{$status};
34348        $fm .= "IP $ip is in RWLCache: $status \n";
34349    }
34350    if ( SBCacheFind($ip) ) {
34351        my ( $ct, $status, $data  ) = split( '!', $SBCache{$ip} );
34352;
34353        my %statList = (
34354            0 => 'not classified',
34355            1 => 'SenderBase',
34356            2 => 'white SenderBase',
34357
34358        );
34359        $status = $statList{$status};
34360        $data =~ s/\|/,/og;
34361        $fm .= "IP $ip is in CountryCache: status=$status, data=$data\n" if $data;
34362    }
34363    if ( $ret = matchIP( $ip, 'acceptAllMail', 0, 1 ) ) {
34364        $fm .= "IP $ip is in acceptAllMail ($ret)\n";
34365    }
34366    if ( $ret = matchIP( $ip, 'noBlockingIPs', 0, 1 ) ) {
34367    	$fm .= "IP $ip is in noBlockingIPs ($ret)\n";
34368                $fm .=
34369"<b><font color='green'>&bull;</font> IP $ip is in <a href='./#noBlockingIPs'>noBlockingIPs</a> ($ret)</b><br />\n";
34370        }
34371    if ( $ret = matchIP( $ip, 'ispip', 0, 1 ) ) {
34372        $fm .= "IP $ip is in ISP/Secondary MX Servers ($ret)\n";
34373    }
34374    foreach my $iip (@sips) {
34375        if ( $ret = matchIP( $iip, 'denySMTPConnectionsFrom', 0, 1 ) ) {
34376            $fm .= "IP $iip is in denySMTPConnectionsFrom ($ret)\n";
34377        }
34378    }
34379    foreach my $iip (@sips) {
34380        if ( $ret = matchIP( $iip, 'denySMTPConnectionsFromAlways', 0, 1 ) ) {
34381            $fm .= "IP $iip is in denySMTPConnectionsFromAlways ($ret)\n";
34382        }
34383    }
34384    @t = ();
34385    my %got = ();
34386    my $v;
34387
34388 	$ipnet = &ipNetwork($ip, 1);
34389	$ipnet =~ s/\.0$//o;
34390	if (exists $Griplist{$ipnet}) {
34391	    if ($Griplist{$ipnet} !~ /$IPprivate/o) {
34392		    $v = $Griplist{$ipnet};
34393		    $v = 0.01 if !$v;
34394		    $v = 0.99 if  $v == 1;
34395		}
34396	}
34397    if ($griplist) {
34398        if ( $ispip  && matchIP( $ip, 'ispip', 0, 1 ) ) {
34399            if ($ispgripvalue ne '') {
34400                $v = $ispgripvalue;
34401            } else {
34402                $v=$Griplist{x};
34403            }
34404        }
34405
34406
34407        $fm .= "$ipnet has a Griplist value of $v: \n" if $v;
34408
34409    }
34410
34411    $fm .= "\n";
34412    my ( $v, $lt, $t, %seen );
34413    while ( $mail =~ /([$BayesCont]{2,})/go) {
34414        	next if length($1) > 20;
34415        	next if length($1) < 2;
34416        	$lt = $t;
34417        	$t  = BayesWordClean($1);
34418        	my $j = "$lt $t";
34419            next if $seen{$j}++ > 1;
34420            if  ($v = $Spamdb{$j}) {
34421            	push( @t, $v );
34422            } else {
34423            	push( @t, $v ) if $v = $Starterdb{$j};
34424			}
34425            $got{$j} = $v if $v;
34426    }
34427    my $cnt = 0;
34428    my $bayestext; $bayestext = "Bayesian Check is disabled" if !$DoBayesian;
34429    $ba .= "Bayesian Analysis: $bayestext\n";
34430    $ba .= "Bad Words:Bad Prob\t\t\tGood Words:Good Prob\n";
34431    foreach ( sort { abs( $got{$main::b} - .5 ) <=> abs( $got{$main::a} - .5 ) } keys %got )
34432    {
34433        my $g = sprintf( "%.4f", $got{$_} );
34434        if ( $g < 0.5 ) {
34435
34436            $ba .= "\t\t\t\t\t\t$_:$g\n";
34437        } else {
34438            $ba .= "$_:$g\n";
34439        }
34440        last if $cnt++ > 20;
34441    }
34442
34443    $ba .= "\n";
34444    @t  = sort { abs( $main::b - .5 ) <=> abs( $main::a - .5 ) } @t;
34445    @t  = @t[ 0 .. 30 ];
34446    $st = "Totals: ";
34447    foreach (@t) { $st .= sprintf( "%.4f ", $_ ) if $_; }
34448    $st .= "\n";
34449
34450    #my $p1=1; my $p2=1; foreach $p (@t) {if($p) {$p1*=$p; $p2*=(1-$p);}}
34451    #my $p1=1; my $p2=1; foreach $p (@t) {if($p) {$p1*=$p; $p2*=(1-$p)*2;}}
34452
34453    my $p1 = 1;
34454    my $p2 = 1;
34455    foreach my $p (@t) {
34456        if ($p) { $p1 *= $p; $p2 *= ( 1 - $p ); }
34457    }
34458    $SpamProb = $p1 / ( $p1 + $p2 );
34459    $this->{spamconf} = abs( $p1 - $p2 );
34460    $st .= "\n\nSpam/Ham Probabilities:\n";
34461    $st .= "\n\nSpam Probability:\n" if !$baysConf;
34462    $st .= sprintf( " spamprobability: %.8f\n", $p1 ) if $baysConf;
34463    $st .= sprintf( " hamprobability: %.8f\n", $p2 ) if $baysConf;
34464    $st .= sprintf( " combined probability %.8f\n", $SpamProb ) if $baysConf;
34465    $st .= sprintf( " probability %.4f\n", $SpamProb ) if !$baysConf;
34466    $st .= sprintf( " bayesian confidence %.8f\n", $this->{spamconf} )
34467      if $baysConf;
34468
34469    $st .= " \n";
34470    $this->{report} .= "$header$fm$ba$st$bod";
34471    $this->{report} =~ s{([\x7F-\xFF])}{sprintf("=%02X", ord($1))}eog;
34472    return $sub;
34473}
34474
34475sub needEs {
34476    my ( $count, $text, $es ) = @_;
34477    return $count . $text . ( $count == 1 ? '' : $es );
34478}
34479
34480
34481sub encodeHTMLEntities {
34482 my $s=shift;
34483 $s=~s/\&/\&amp;/gso;
34484 $s=~s/\</\&lt;/gso;
34485 $s=~s/\>/\&gt;/gso;
34486 $s=~s/\"/\&quot;/gso;
34487 return $s;
34488}
34489
34490sub decodeHTMLEntities {
34491 my $s=shift;
34492 $s=~s/\&quot;?/\"/giso;
34493 $s=~s/\&gt;?/\>/giso;
34494 $s=~s/\&lt;?/\</giso;
34495 $s=~s/\&amp;?/\&/giso;
34496 return $s;
34497}
34498
34499sub encHTMLent {
34500    my $sh = shift;
34501    my $s = ref $sh ? $$sh : $sh;
34502    my $ret;
34503    eval{$ret = ($s ? &HTML::Entities::encode($s) : '');};
34504    if ($@) { # do what we can if HTML::Entities failes
34505         mlog(0,"warning: an error occured in encoding HTML-Entities - $@");
34506         $ret = encodeHTMLEntities($s);
34507    }
34508    return $ret ? $ret : $$s;
34509}
34510
34511sub decHTMLent {
34512    my $sh = shift;
34513    my $s = ref $sh ? $$sh : $sh;
34514    my $ret;
34515
34516    $s =~ s/(?:\&nbsp|[%=]a0);?/ /gosi;  # decode &nbsp; to space not to \160
34517    $s =~ s/(?:\&shy|[%=]ad);?/-/gosi;   # decode &shy; to '-' not to \173
34518
34519    $s =~ s/\&\#(\d+);?/decHTMLentHD($1)/geo;
34520    $s =~ s/\&\#x([a-f0-9]+);?/decHTMLentHD($1,'h')/geio;
34521    $s =~ s/([^\\])?\\(\d{1,3});?/$1.decHTMLentHD($2,'o')/geio;
34522    $s =~ s/([^\\])?[%=]([a-f0-9]{2});?/$1.chr(hex($2))/gieo;
34523
34524    local $@ = undef;
34525    eval{$ret = &HTML::Entities::decode($s);} if $s;
34526    if ($@) { # do what we can if HTML::Entities fails
34527         mlog(0,"warning: an error occured in decoding HTML-Entities - $@");
34528         $ret = decodeHTMLEntities($s);
34529    }
34530    return $ret ? $ret : $s;
34531}
34532
34533sub decHTMLentHD {
34534    my ($s, $how) = @_;
34535    $s = hex($s) if $how eq 'h';
34536    $s = oct($s) if $how eq 'o';
34537    $s = chr($s);
34538    use bytes;
34539    $s =~ s/^(?:\xA1\x43|\xA1\x44|\xA1\x4F|\xE3\x80\x82|\xEF\xBC\x8E|\xEF\xB9\x92|\xDB\x94)$/./go;  #Big5 Chinese language character set (.)
34540    $s =~ s/^\xA0$/ /gosi;  # decode to space not to \160
34541    $s =~ s/^\xAD$/-/gosi;  # decode to '-' not to \173
34542    no bytes;
34543    return $s;
34544}
34545
34546sub normHTML {
34547    my $s = shift;
34548    $s =~ s{([^a-zA-Z0-9])}{sprintf("%%%02X", ord($1))}eog;
34549    return $s;
34550}
34551sub ConfigMaillog {
34552 my $stime = time;
34553 my $maxsearchtime = time + 60;
34554 my $pat=$qs{search};
34555 my $matches=0;
34556 my $currWrap;
34557 alarm 0;
34558 if (exists $qs{wrap}) {
34559    $currWrap = $qs{wrap};
34560
34561 } elsif ( ! $currWrap) {
34562 	$currWrap = 0 if !$MaillogTailWrap;
34563    $currWrap = 2 if $MaillogTailWrap;
34564 }
34565
34566 $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.wrap"} = $currWrap if $WebIP{$ActWebSess}->{user} ne 'root';
34567 &niceConfig();
34568
34569 my $colorLines;
34570 $colorLines = 1 if $MaillogTailColorLine;
34571 if (exists $qs{color}) {
34572    $colorLines = $qs{color};
34573
34574 } elsif ( ! $colorLines) {
34575    $colorLines = 0;
34576 }
34577 $colorLines = 1 unless $colorLines;
34578 $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.color"} = $colorLines if $WebIP{$ActWebSess}->{user} ne 'root';
34579
34580 my $order;
34581 if (exists $qs{order}) {
34582    $order = $qs{order};
34583 } elsif ($MaillogTailOrder) {
34584    $order = $MaillogTailOrder;
34585 } elsif ( ! $order) {
34586    $order = 0;
34587 }
34588 $order = 0 unless $order;
34589 $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.order"} = $order if $WebIP{$ActWebSess}->{user} ne 'root';
34590
34591 my $savTailByte = $MaillogTailBytes;
34592 my $currTailByte;
34593 ($currTailByte) = $1 if $qs{tailbyte}=~/(\d+)/;
34594 $currTailByte = $MaillogTailBytes if ($MaillogTailBytes>0 && (! $currTailByte || $currTailByte<160));
34595 $currTailByte = 2000 unless $currTailByte;
34596 $MaillogTailBytes = $currTailByte;
34597 $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.TailByte"} = $currTailByte if $WebIP{$ActWebSess}->{user} ne 'root';
34598
34599
34600
34601 my $orgpat = $pat;
34602 my $filesonly=$qs{filesonly};
34603 my $autoJS = '';
34604 my $autoButton = 'Auto';
34605 my $CMheaders = \$headers;
34606 my $content = 'class="content"';
34607 my $logstyle = '';
34608 my $display = '';
34609 $pat = $qs{search} = '' if $qs{autorefresh} eq 'Stop';
34610 if ($qs{autorefresh} eq 'Auto') {
34611     $pat = '';
34612     $qs{filesonly}= $filesonly = '';
34613     $qs{nohighlight} = 1;
34614     $autoButton = 'Stop';
34615     $CMheaders = '';
34616     $display = 'style="display:none"';
34617     $content = 'class="content" style="margin: 0 0 0 0;"';
34618     $logstyle = 'style="border-width: 4px 4px 4px 4px; border-color: #6699cc; border-style: solid;"';
34619
34620     $autoJS = '
34621<script type="text/javascript">
34622 Timer=setTimeout("newTimer();",'. $refreshWait .'000);
34623 var Run = 1;
34624 function noop () {}
34625 function tStart () {
34626    Run = 1;
34627 }
34628 function tStop () {
34629    Run = 0;
34630    Timer=setTimeout("noop();", 1000);
34631 }
34632 function newTimer() {
34633   if (Run == 1) {location.reload(true)};
34634   Timer=setTimeout("newTimer();",'. $refreshWait .'000);
34635 }
34636</script>
34637';
34638  $CMheaders = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
34639<head>
34640  <meta http-equiv=\"content-type\" content=\"application/xhtml+xml; charset=utf-8\" />
34641  <meta http-equiv=\"refresh\" content=\"$refreshWait;url=/maillog?search=\&wrap=$qs{wrap}\&color=$colorLines\&autorefresh=Auto\&files=$qs{files}\&limit=$qs{limit}\&nohighlight=$qs{nohighlight}\&nocontext=$qs{nocontext}\&tailbyte=$qs{tailbyte}\&size=$qs{size}\&order=$qs{order}\" />
34642  <title>$currentPage ASSP ($myName) Host: $localhostname @ $localhostip</title>
34643  <link rel=\"stylesheet\" href=\"get?file=images/assp.css\" type=\"text/css\" />
34644  <link rel=\"shortcut icon\" href=\"get?file=images/favicon.ico\" />
34645$autoJS
34646</head>
34647<body onfocus=\"tStart();\" onblur=\"tStop();\"><a name=\"MlTop\"></a>
34648";
34649 }
34650 my $s='';
34651 my $res='';
34652 my $base = $base;
34653 $base =~ s/([^\\])\\([^\\])/$1\\\\$2/go;
34654 # calculate indent
34655 my $m = &timestring().' ';
34656 my $resetpat;
34657 my $reportExt = $maillogExt;
34658 if(!$pat && $filesonly) {
34659     $resetpat = 1;
34660     $pat = $maillogExt;
34661 }
34662 if(!$pat) {
34663  my $TailBytes = ($qs{autorefresh} eq 'Auto' && $MaillogTailBytes > 2000) ? 2000 : $MaillogTailBytes;
34664  if ($qs{autorefresh} eq 'Auto') {
34665      my $sl; $sl = $1 if $qs{search} =~ /(\d+)/o;
34666      $sl = $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.autolines"} if $sl == '' && $WebIP{$ActWebSess}->{user} ne 'root';
34667      my $al = $sl ? 33 - $sl : $currWrap ? 10 : 0;
34668      $al = 0 if $al < 0;
34669      $al = 32 if $al > 32;
34670      $AdminUsersRight{"$WebIP{$ActWebSess}->{user}.user.autolines"} = $sl if $WebIP{$ActWebSess}->{user} ne 'root';
34671      for (my $i = $al; $i < 33; $i++) {
34672          $s .= $RealTimeLog[$i];
34673      }
34674  } else {
34675      open(my $CML,'<',"$base/$logfile");
34676      seek($CML,-$TailBytes,2) || seek($CML,0,0);
34677      local $/;
34678      $s=<$CML>;
34679      close $CML;
34680  }
34681  if ($s && $LogCharset && $LogCharset !~ /^utf-?8/io) {
34682      $s = Encode::decode($LogCharset, $s);
34683      $s = Encode::encode('UTF-8', $s);
34684  }
34685  $s=encodeHTMLEntities($s) if $s;
34686  $s=~s/([^\\])?\\([^\\])?/$1\\\\$2/gso;
34687   my @sary=map{$_."\n" if $_;} split(/\r?\n|\r/o,$s);
34688   shift @sary if ($qs{autorefresh} ne 'Auto');
34689   my @rary;
34690   $matches=0;
34691   while (@sary) {
34692    $_ = shift @sary;
34693    @sary = () if time > $maxsearchtime;
34694    s/\\x\{\d+\}//g;
34695    if ($qs{autorefresh} ne 'Auto') {
34696     if (/(.*)?($base\/(($spamlog|$discarded|$notspamlog|$incomingOkMail|$viruslog|$correctedspam|$correctednotspam|$resendmail)\/[^\s]+(?:$maillogExt|$reportExt)))(.*)/)
34697     {
34698         my $text = $1;
34699         my $file = $2;
34700         my $hfile = $3;
34701         my $dname = $4;
34702         my $text2 = $5;
34703         my $span = ($dname =~ /^(?:$spamlog|$discarded|$viruslog|$correctedspam)$/) ? 'negative' : 'positive';
34704         $span = 'spampassed' if /\[spam passed\]/gio;
34705         $span = 'spampassed' if /and passing/gio;
34706         $text =~ s/([^ ]+) +/<span style="white-space:nowrap;">$1<\/span> /go;
34707         $text2 =~ s/([^ ]+) +/<span style="white-space:nowrap;">$1<\/span> /go;
34708		 if (&MaillogExistFile($file)) {
34709
34710			$hfile =~s/\Q$spamlog\E\//$maillogNewFile\// if $maillogNewFile;
34711			$span = ($maillogNewFile =~ /^(?:$spamlog|$discarded|$viruslog|$correctedspam)$/) ? 'negative' : 'positive' if $maillogNewFile;
34712			$hfile = "<span style=\"white-space:nowrap;\" onclick=\"popFileEditor('" . &normHTML($hfile) . "','m');\" class=\"" . $span . "\" onmouseover=\"fileBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=fileBG;\"><b>" . $hfile . "<\/b><\/span>";
34713		 } else {
34714			$hfile =~ s/([^ ]+) +/<span style="white-space:nowrap;">$1<\/span> /go;
34715		 }
34716         $text .= $hfile . $text2;
34717         push(@rary,'<div id="ll' . $matches .'" class="assplogline'. ($currWrap + ($matches % 2 && $colorLines)) .'">' . $text . "\n</div>");
34718         $matches++;
34719         next;
34720     } elsif (! $filesonly) {
34721         my @links;
34722         my @addr;
34723         my @ips;
34724         $_ = niceLink($_);
34725         while ($_ =~ s/(\<a href.*?<\/a\>)/XXXIIIXXX/o) {
34726             my $link = $1;
34727             $link =~ s/WIDTH=[^\d]*(\d+\%)[^ ]*/WIDTH=$1/io;
34728             push @links,$link;
34729         }
34730         if (&canUserDo($WebIP{$ActWebSess}->{user},'action','addraction')) {
34731             while ($_ =~ s/((?<!Message-ID found: ))($EmailAdrRe\@$EmailDomainRe)/$1XXXAIIIDXXX/o) {
34732                 push @addr ,
34733                    "<span style=\"white-space:nowrap;\" onclick=\"popAddressAction('"
34734                    . &normHTML($2)
34735                    . "');\" class=\"menuLevel2\" onmouseover=\"fileBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=fileBG;\"><b>"
34736                    . $2
34737                    . "<\/b><\/span>";
34738             }
34739         }
34740         if (&canUserDo($WebIP{$ActWebSess}->{user},'action','ipaction')) {
34741             while ($_ =~ s/($IPRe)([^:\d\/])/XXXiIIIpXXX$2/o) {
34742                 my  $ip = $1;
34743                 if (   $ip !~ /$IPprivate/o
34744                     && $ip ne $localhostip
34745                     && $ip ne $version
34746                     && $ip !~ /$LHNRE/)
34747                 {
34748                     push @ips,
34749                        "<span style=\"white-space:nowrap;\" onclick=\"popIPAction('"
34750                        . &normHTML($ip)
34751                        . "');\" class=\"menuLevel2\" onmouseover=\"fileBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=fileBG;\"><b>"
34752                        . $ip
34753                        . "<\/b><\/span>";
34754                 } else {
34755                     push @ips, $ip;
34756                 }
34757             }
34758         }
34759         s/([^ ]+) +/<span style="white-space:nowrap;">$1<\/span> /go;
34760         if (@links) {
34761             s/XXXIIIXXX/shift(@links)/geo;
34762         }
34763         if (@addr) {
34764             s/XXXAIIIDXXX/shift(@addr)/geo;
34765         }
34766         if (@ips) {
34767             s/XXXiIIIpXXX/shift(@ips)/geo;
34768         }
34769     }
34770     if ($filesonly) {
34771         next;
34772     }
34773    }
34774    push(@rary,'<div id="ll' . $matches .'" class="assplogline'. ($currWrap + ($matches % 2 && $colorLines)) .'">' . $_ . "\n</div>");
34775    $matches++;
34776   }
34777   $s = join('',@rary);
34778   $s =~ s/"/\\"/go;
34779   $s =~ s/\n+<\/div>/<\/div>XXXIIIXXX/go;
34780   $s =~ s/\r|\n//go;
34781   $s =~ s/XXXIIIXXX$//o;
34782 } elsif ($CanSearchLogs) {
34783  my @sary;
34784  $matches=0;
34785  my $lines=0;
34786  my $files=0;
34787  my ($logdir, $logdirfile) = $logfile=~/^(.*[\/\\])?(.*?)$/o;
34788  my @logfiles1=reverse sort glob("$base/$logdir*$logdirfile");
34789  my @logfiles;
34790  while (@logfiles1) {
34791      my $k = shift @logfiles1;
34792      push(@logfiles, $k) if $k !~ /b$logdirfile/;
34793  }
34794  my $maxmatches =
34795                $qs{limit} eq '2000' ? 2000
34796              : $qs{limit} eq '1000' ? 1000
34797              : $qs{limit} eq '100'  ? 100
34798              : $qs{limit} eq '10'   ? 10
34799              : $qs{limit} eq '1'    ? 1
34800              :                        0;
34801  my $maxlines;
34802  my $maxfiles;
34803  if ($qs{files} eq 'lines') {
34804      ($maxlines) = $qs{size} =~ /(\d+)/o;
34805      $maxlines = 10000 unless $maxlines;
34806      $maxfiles = 0;
34807  } elsif ($qs{files} eq 'files') {
34808      ($maxfiles) = $qs{size} =~ /(\d+)/o;
34809      $maxfiles = 2 unless $maxfiles;
34810      $maxlines = 0;
34811  } elsif ($qs{files} eq 'ago') {
34812      $maxfiles = $qs{size};
34813      $maxfiles =~ s/\s//go;
34814      $maxfiles =~ s/-/.../go;
34815      my @num = sort {$main::a <=> $main::b} map(eval($_),split(',', $maxfiles));
34816      @num = (1) unless $maxfiles or @num;
34817      my @lof = @logfiles;
34818      @logfiles = ();
34819      foreach (@num) {
34820          push(@logfiles , $lof[$_ - 1]) if $_ > 0 && $lof[$_ - 1];
34821      }
34822      push(@logfiles,$lof[0]) unless @logfiles;
34823      $maxlines = 0;
34824  } else {
34825      $maxlines = 0;
34826      $maxfiles = 0;
34827  }
34828  my $logf=File::ReadBackwards->new(shift(@logfiles),'(?:\r?\n|\r)',1); # line terminator regex
34829  if ($logf) {
34830   $files++;
34831
34832#   $pat = &encHTMLent(\$pat);
34833#   $pat = encodeHTMLEntities($pat);
34834#   $pat=~s/([^\\])?\\([^\\])?/$1\\\\$2/gso;
34835   # mormalize and strip redundand minuses
34836   $pat = &HTML::Entities::decode($pat,'"\'><&');
34837   $pat=~s/(?<!(?:-|\w))(-(?:\s+|\z))+/-/go;
34838   $pat=~s/\s+-$//o;
34839   my $l;
34840   $l=$logf->readline();
34841   if ($l && $LogCharset && $LogCharset !~ /^utf-?8/io) {
34842       $l = Encode::decode($LogCharset, $l);
34843       $l = Encode::encode('UTF-8', $l);
34844   }
34845   $l =~ s/\\x\{\d+\}//go;
34846   # make line terminators uniform
34847   $l=~s/(.*?)(?:\r?\n|\r)/$1\n/o;
34848   $l=encodeHTMLEntities($l) if $l;
34849   $l=~s/([^\\])?\\([^\\])?/$1\\\\$2/gso;
34850   my @ary;
34851   push(@ary,$l);
34852   my $infinity=10000;
34853   my $precontext=my $postcontext=$qs{nocontext} ? 0 : 6;
34854   my $notmatched=0;
34855   my $currentpre=0;
34856   my $seq=0;
34857   my $lastoutput=$infinity;
34858   my $cur=$ary[0];
34859   my $i=0;
34860   my @words=map/^\d+\_(.*)/o, sort values %{{map{lc $_ => sprintf("%02d",$i++).'_'.$_} split(' ',$pat)}};
34861   $pat=join(' ', @words);
34862   my @highlights=('<span%%20%%style="color:black;%%20%%background-color:#ffff66">',
34863                   '<span%%20%%style="color:black;%%20%%background-color:#A0FFFF">',
34864                   '<span%%20%%style="color:black;%%20%%background-color:#99ff99">',
34865                   '<span%%20%%style="color:black;%%20%%background-color:#ff9999">',
34866                   '<span%%20%%style="color:black;%%20%%background-color:#ff66ff">',
34867                   '<span%%20%%style="color:white;%%20%%background-color:#880000">',
34868                   '<span%%20%%style="color:white;%%20%%background-color:#00aa00">',
34869                   '<span%%20%%style="color:white;%%20%%background-color:#886800">',
34870                   '<span%%20%%style="color:white;%%20%%background-color:#004699">',
34871                   '<span%%20%%style="color:white;%%20%%background-color:#990099">');
34872   my $findExpr=join(' && ',((map{'$cur=~/'.quotemeta($_).'/io'} map/^([^-].*)/o, split(' ',$pat)),
34873                             (map{'$cur!~/'.quotemeta($_).'/io'} map/^-(.*)/o, split(' ',$pat))));
34874   my %replace = ();
34875   my $j=0;
34876   my $highlightExpr='=~s/(';
34877   foreach (map/^([^-].*)/o, split(' ',$pat)) {
34878    $replace{lc $_}=$highlights[$j % @highlights]; # pick highlight style
34879    $highlightExpr.=quotemeta($_).'|';
34880    $j++;
34881   }
34882   $highlightExpr=~s/\|$//o;
34883   $highlightExpr.=')/$replace{lc $1}$1<\/span>/gio';
34884   my $loop=<<'LOOP';
34885   while (time < $maxsearchtime && $cur && !($maxmatches && $matches>=$maxmatches && $notmatched>$postcontext) && !($maxlines && $lines>=$maxlines)) {
34886LOOP
34887    $loop.='
34888    if (!($maxmatches && $matches>=$maxmatches) && '.$findExpr.') {'. <<'LOOP';
34889     $matches++;
34890LOOP
34891     $loop.='$cur'.$highlightExpr.' unless $qs{nohighlight};'. <<'LOOP';
34892     if ($lastoutput<=$postcontext) {
34893      push(@sary,$cur);
34894     } else {
34895      push(@sary,"\r\n") if ($seq++ && ($precontext+$postcontext>0));
34896      for ($i=0; $i<@ary; $i++) {
34897       if ($i<$precontext && $currentpre==$precontext || $i<$currentpre) {
34898        $ary[$i]=~s/^(.*?)(\r?\n)$/<span\%\%20\%\%style="color:#999999">$1<\/span>$2/so;
34899       } else {
34900LOOP
34901        $loop.='$ary[$i]'.$highlightExpr.' unless $qs{nohighlight};'. <<'LOOP';
34902       }
34903       push(@sary,$ary[$i]);
34904      }
34905     }
34906     $lastoutput=0;
34907     $notmatched=0;
34908    } elsif ($logf->eof) {
34909     for (; $currentpre>=0; $currentpre--) {
34910      shift(@ary);
34911     }
34912     $logf->close if exists $logf->{'handle'};
34913     if (!($maxfiles && $files>=$maxfiles)) {
34914      $logf=File::ReadBackwards->new(shift(@logfiles),'(?:\r?\n|\r)',1);
34915      $files++ if $logf;
34916     }
34917     $lastoutput=$infinity;
34918    } elsif ($lastoutput<=$postcontext) {
34919     $cur=~s/^(.*?)(\r?\n)$/<span\%\%20\%\%style="color:#999999">$1<\/span>$2/so;
34920     push(@sary,$cur);
34921    }
34922    $lastoutput++;
34923    $notmatched++;
34924    if ($l) {
34925     $l=$logf->readline();
34926     if ($l && $LogCharset && $LogCharset !~ /^utf-?8/io) {
34927         $l = Encode::decode($LogCharset, $l);
34928         $l = Encode::encode('UTF-8', $l);
34929     }
34930     # make line terminators uniform
34931     $l=~s/(.*?)(?:\r?\n|\r)/$1\n/o;
34932     $l =~ s/\\x\{\d+\}//go;
34933
34934     my $fname;
34935     if ($l=~ s/($base\/.+?\/.+?$maillogExt)/aAaAaAaAaAbBbBbBbBbB$maillogExt/) {
34936       $fname = $1;
34937     }
34938
34939     $l=encodeHTMLEntities($l) if $l;
34940     $l=~s/([^\\])?\\([^\\])?/$1\\\\$2/gso;
34941
34942     $l =~ s/aAaAaAaAaAbBbBbBbBbB$maillogExt/$fname/o;
34943     $fname = '';
34944
34945     $lines++;
34946    }
34947    push(@ary,$l);
34948    if ($currentpre<$precontext) {
34949     $currentpre++;
34950    } else {
34951     shift(@ary);
34952    }
34953    $cur=$ary[$currentpre];
34954   }
34955LOOP
34956   eval $loop;
34957   $logf->close if exists $logf->{'handle'};
34958  }
34959  my $orgmatches = $matches;
34960  if ($matches>0) {
34961   $matches = 0;
34962   my @rary;
34963   my $line = $_;
34964   $maxsearchtime = time + 60;
34965   while (@sary) {
34966    $_ = shift @sary;
34967    @sary = () if time > $maxsearchtime;
34968    my @sp;
34969    my @words;
34970    my $pretag;
34971    my $posttag;
34972    $line = $_;
34973    if ($_ =~ /<\/span>/o ) {
34974     if (!$qs{nocontext} && $_ =~ s/^(<span\%\%20\%\%style="color:#999999">)//o) {
34975        $pretag = $1;
34976        $posttag = $1 if ($_ =~ s/(<\/span>[\r\n]*)$//o);
34977     }
34978     if ($_ =~ /<\/span>/o ) {
34979      my $iline = '';
34980      @words = split(/(<span[^>]+>|<\/span>)/o);
34981      my $i = 0;
34982      while (@words) {
34983        $sp[$i][0] = shift @words;
34984        $sp[$i][1] = shift @words;
34985        $sp[$i][2] = shift @words;
34986        $sp[$i][3] = shift @words;
34987        $iline .=  $sp[$i][0] . $sp[$i][2];
34988        $i++;
34989      }
34990      if ($iline =~ /$base\/(?:$spamlog|$discarded|$notspamlog|$incomingOkMail|$viruslog|$correctedspam|$correctednotspam|$resendmail)\/[^\s]+(?:$maillogExt|$reportExt)/) {
34991          $line = $iline ;
34992      } else {
34993         @sp = ();
34994      }
34995     }
34996    }
34997    $_ = $line;
34998    if (/^(<[^<>]+>)*(.*)?($base\/(($spamlog|$discarded|$notspamlog|$incomingOkMail|$viruslog|$correctedspam|$correctednotspam|$resendmail)\/[^\s]+(?:$maillogExt|$reportExt)))(.*)$/)
34999    {
35000        my $sp = $1;
35001        my $text = $2;
35002        my $file = my $hfile = $3;
35003        my $hlfile = $4;
35004        my $dname = $5;
35005        my $text2 = $6;
35006        my $span = ($dname =~ /^(?:$spamlog|$discarded|$viruslog|$correctedspam)$/) ? 'negative' : 'positive';
35007        $span = 'spampassed' if /\[spam passed\]/gio;
35008        $span = 'spampassed' if /and passing/gio;
35009
35010        if (@sp) {
35011            my $i = 0;
35012            my $j = scalar @sp;
35013            my $fpos = 0;
35014            my $tpos = 0;
35015            my $t2pos = 0;
35016            while ($j > $i) {
35017              my ($s0,$s1,$s2,$s3) = ($sp[$i][0],$sp[$i][1],$sp[$i][2],$sp[$i][3]);
35018              if ($s1) {
35019                  pos($text) = $tpos;
35020                  $text =~ s/\Q$s0$s2\E/$s0$s1$s2$s3/;
35021                  $tpos = pos($text);
35022                  if ($tpos) {
35023                      $tpos += length($s1 . $s3);
35024                  } else {
35025                      $tpos = 0;
35026                  }
35027
35028                  pos($text2) = $t2pos;
35029                  $text2 =~ s/\Q$s0$s2\E/$s0$s1$s2$s3/;
35030                  $t2pos = pos($text2);
35031                  if ($t2pos) {
35032                      $t2pos += length($s1 . $s3);
35033                  } else {
35034                      $t2pos = 0;
35035                  }
35036
35037                  pos($hfile) = $fpos;
35038                  $hfile =~ s/\Q$s0$s2\E/$s0$s1$s2$s3/;
35039                  $fpos = pos($hfile);
35040                  if ($fpos) {
35041                      $fpos += length($s1 . $s3);
35042                  } else {
35043                      $fpos = 0;
35044                  }
35045              }
35046              $i++;
35047            }
35048        }
35049        $hfile =~ s/\Q$base\E\///o;
35050
35051        if (&MaillogExistFile($file)) {
35052        	$hlfile =~s/\Q$spamlog\E\//$maillogNewFile\// if $maillogNewFile;
35053        	$hfile =~s/\Q$spamlog\E\//$maillogNewFile\// if $maillogNewFile;
35054        	$span = 'positive' if $maillogNewFile eq $correctednotspam;
35055          	$hfile = "<span\%\%20\%\%style=\"white-space:nowrap;\"\%\%20\%\%onclick=\"popFileEditor('" . &normHTML($hlfile) . "','m');\"\%\%20\%\%class=\"" . $span . "\"\%\%20\%\%onmouseover=\"fileBG=this.style.backgroundColor;\%\%20\%\%this.style.backgroundColor='#BBBBFF';\"\%\%20\%\%onmouseout=\"this.style.backgroundColor=fileBG;\"><b>" . $hfile . "<\/b><\/span>";
35056        } else {
35057          $hfile =~ s/([^ ]+)( +)?/<span style="white-space:nowrap;">$1<\/span>$2/go;
35058        }
35059
35060        $text = $sp . $text .$hfile . $text2;
35061        my $out = '<div%%20%%id="ll' . $matches .'"%%20%%class="assplogline'. ($currWrap + ($matches % 2 && $colorLines)) .'">' . $text . "\n</div>";
35062        $out =~ s/\%\%20\%\%/ /go;
35063        push(@rary,$pretag . $out . $posttag);
35064        $matches++;
35065        next;
35066    } elsif (! $filesonly) {
35067        s/\%\%20\%\%/ /go;
35068        $_ = niceLink($_);
35069        my @links;
35070        my @addr;
35071        my @ips;
35072        while ($_ =~ s/(\<a href.*?<\/a\>)/XXXIIIXXX/o) {
35073            my $link = $1;
35074            $link =~ s/WIDTH=[^\d]*(\d+\%)[^ ]*/WIDTH=$1/io;
35075            push @links,$link;
35076        }
35077        if (&canUserDo($WebIP{$ActWebSess}->{user},'action','addraction')) {
35078            while ($_ =~ s/((?<!Message-ID found: ))($EmailAdrRe\@$EmailDomainRe)/$1XXXAIIIDXXX/o) {
35079                push @addr ,
35080                   "<span style=\"white-space:nowrap;\" onclick=\"popAddressAction('"
35081                   . &normHTML($2)
35082                   . "');\" class=\"menuLevel2\" onmouseover=\"fileBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=fileBG;\"><b>"
35083                   . $2
35084                   . "<\/b><\/span>";
35085            }
35086        }
35087        if (&canUserDo($WebIP{$ActWebSess}->{user},'action','ipaction')) {
35088            while ($_ =~ s/($IPRe)([^:\d\/])/XXXiIIIpXXX$2/o) {
35089                my  $ip = $1;
35090                if (   $ip !~ /$IPprivate/o
35091                    && $ip ne $localhostip
35092                    && $ip ne $version
35093                    && $ip !~ /$LHNRE/)
35094                {
35095                    push @ips,
35096                       "<span style=\"white-space:nowrap;\" onclick=\"popIPAction('"
35097                       . &normHTML($ip)
35098                       . "');\" class=\"menuLevel2\" onmouseover=\"fileBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=fileBG;\"><b>"
35099                       . $ip
35100                       . "<\/b><\/span>";
35101                } else {
35102                    push @ips, $ip;
35103                }
35104            }
35105        }
35106        if (@links) {
35107            s/XXXIIIXXX/shift(@links)/geo;
35108        }
35109        if (@addr) {
35110            s/XXXAIIIDXXX/shift(@addr)/geo;
35111        }
35112        if (@ips) {
35113            s/XXXiIIIpXXX/shift(@ips)/geo;
35114        }
35115    }
35116    if ($filesonly) {
35117        next;
35118    }
35119    my $out =  '<div id="ll' . $matches .'" class="assplogline'. ($currWrap + ($matches % 2 && $colorLines)) .'">' . $_ . "\n</div>";
35120    push(@rary, $pretag . $out . $posttag);
35121    $matches++;
35122   }
35123   $s = join('', reverse @rary);
35124   $s =~ s/"/\\"/go;
35125   $s =~ s/\n+<\/div>/<\/div>XXXIIIXXX/go;
35126   $s =~ s/\r|\n//go;
35127   $s =~ s/XXXIIIXXX$//o;
35128   my $ftext = $filesonly ? ' with ' . needEs($matches,' line','s') . ' that contains filesnames' : '';
35129   $res='found '. needEs($orgmatches,' matching line','s') . $ftext . ', searched in '. needEs($files,' log file','s') .' ('. needEs($lines,' line','s'). ')';
35130  } else {
35131   $res='no results found, searched in '. needEs($files,' log file','s') .' ('. needEs($lines,' line','s'). ')';
35132  }
35133 } else {
35134  $s='<p class="warning">Please install required module <a href="http://search.cpan.org/~uri/File-ReadBackwards-1.03/" rel="external">File::ReadBackwards</a>.</p>';
35135 }
35136 $MaillogTailBytes = $savTailByte;
35137 my $size = $qs{size} ? $qs{size} : 10000;
35138 my $files = $qs{files} || 'lines';
35139 my $limit = $qs{limit} || 10;
35140 $pat = ($resetpat) ? '' : &HTML::Entities::encode($orgpat,'"\'><&');
35141 my $h1 = $WebIP{$ActWebSess}->{lng}->{'msg500050'} || $lngmsg{'msg500050'};
35142 my $h2 = $WebIP{$ActWebSess}->{lng}->{'msg500051'} || $lngmsg{'msg500051'};
35143 my $h4 = $WebIP{$ActWebSess}->{lng}->{'msg500052'} || $lngmsg{'msg500052'};
35144 my $h5 = $WebIP{$ActWebSess}->{lng}->{'msg500053'} || $lngmsg{'msg500053'};
35145 $h1 =~ s/\r|\n//go;
35146 $h2 =~ s/\r|\n//go;
35147 $h4 =~ s/\r|\n//go;
35148 $h5 =~ s/\r|\n//go;
35149
35150 my $dir = $base;
35151 $dir .= "/$1" if $logfile =~ /^([^\/]+)\//o;
35152 my ($lf) = $logfile =~/([^\/]+)$/o;
35153 my $h3 = '<center><table BORDER CELLSPACING=2 CELLPADDING=4><tr><th></th><th>filename</th><th>size</th><th></th><th>filename</th><th>size</th></tr>';
35154 $h3 .= '<tr><td>01</td><td>' . $lf . '</td><td>' . formatDataSize( -s "$dir/$lf", 1 ) . '</td></tr>';
35155 opendir(my $DIR,"$dir");
35156 my @filelist = readdir($DIR);
35157 close $DIR;
35158 my $i = 0;
35159 foreach my $file (reverse sort @filelist) {
35160     next if $file !~ /\.$lf$/;
35161     $h3 .= '<tr>' unless $i % 2;
35162     $h3 .= '<td>' . sprintf("%02d",($i + 2)) . '</td><td>' . $file . '</td><td>' . formatDataSize( -s "$dir/$file", 1 ) . '</td>';
35163     $h3 .= '</tr>' if $i % 2;
35164     $i++;
35165 }
35166 $h3 .= '</tr>' if $h3 !~ /tr\>$/;
35167 $h3 .= '</table></center>';
35168 $stime = time - $stime;
35169 $res .= ', ' if ($res &&  $qs{autorefresh} ne 'Auto');
35170 $res .= "searchtime $stime seconds (max 60)" if ($qs{autorefresh} ne 'Auto');
35171 my $headline = ($qs{autorefresh} eq 'Auto') ? '' : '<h2>ASSP Maillog Tail</h2>' ;
35172 $headline = ($qs{autorefresh} eq 'Auto') ? '' : '<h2>Secondary GUI Maillog Tail  Viewer</h2>' if $AsASecondary;
35173
35174<<EOT;
35175$headerHTTP
35176$headerDTDTransitional
35177$$CMheaders
35178<style type="text/css">
35179.spampassed { color: #FFA500; }
35180</style>
35181<div id="headline" $content>
35182$headline
35183<a name="MlTop" style="font-weight: normal;"></a>
35184<div class="log" ><pre><a id="dummy" name="dummy" style="font-weight: normal;">$m</a></pre></div>
35185<script type="text/javascript">
35186var fileBG;
35187var MlEndPos;
35188
35189var intend = document.getElementById('dummy').offsetWidth;
35190document.getElementById('dummy').style.display='none';
35191
35192document.write("<style id=\\"aloli0\\" type=\\"text/css\\">\\n.assplogline0\\n {\\nwhite-space:nowrap;\\n padding-left:" + intend + "px;\\n text-indent:-" + intend + "px;\\n background-color:#FFFFFF;\\n}\\n</style>\\n");
35193document.write("<style id=\\"aloli1\\" type=\\"text/css\\">\\n.assplogline1\\n {\\nwhite-space:nowrap;\\n padding-left:" + intend + "px;\\n text-indent:-" + intend + "px;\\n background-color:#F0F0F0;\\n}\\n</style>\\n");
35194document.write("<style id=\\"aloli2\\" type=\\"text/css\\">\\n.assplogline2\\n {\\nwhite-space:normal;\\n padding-left:" + intend + "px;\\n text-indent:-" + intend + "px;\\n background-color:#FFFFFF;\\n}\\n</style>\\n");
35195document.write("<style id=\\"aloli3\\" type=\\"text/css\\">\\n.assplogline3\\n {\\nwhite-space:normal;\\n padding-left:" + intend + "px;\\n text-indent:-" + intend + "px;\\n background-color:#F0F0F0;\\n}\\n</style>\\n");
35196
35197function changeSpan(change) {
35198  var iswrap = document.MTform.wrap[1].checked ? 2 : 0;
35199  var iscolor = document.MTform.color[1].checked ? 1 : 0;
35200  var dowrap = change - 2;
35201  for(i=0; i < $matches; i++) {
35202    if (change == 0 || change == 1) {
35203      if (change == 0) {
35204          document.getElementById('ll' + i).className = 'assplogline' + iswrap;
35205      } else {
35206          document.getElementById('ll' + i).className = 'assplogline' + ((i % 2) + iswrap);
35207      }
35208    } else {
35209      if (iscolor == 0) {
35210          document.getElementById('ll' + i).className = 'assplogline' + dowrap;
35211      } else {
35212          document.getElementById('ll' + i).className = 'assplogline' + ((i % 2) + dowrap);
35213      }
35214    }
35215  }
35216}
35217</script>
35218<form name="MTform" action="" method="get">
35219  <table class="textBox" style="width: 100%;">
35220    <tr>
35221      <td rowspan="2" align="left" $display>
35222        <label>wrap lines: </label>
35223        <input type="radio" name="wrap" ${\(! $currWrap ? ' checked="checked" ' : ' ')} value='0' onclick="javascript:changeSpan('2');" />no
35224        <input type="radio" name="wrap" ${\(  $currWrap ? ' checked="checked" ' : ' ')} value='2' onclick="javascript:changeSpan('4');" />yes<br />
35225        <label>color lines: </label>
35226        <input type="radio" name="color" ${\(! $colorLines ? ' checked="checked" ' : ' ')} value='0' onclick="javascript:changeSpan('0');" />no
35227        <input type="radio" name="color" ${\(  $colorLines ? ' checked="checked" ' : ' ')} value='1' onclick="javascript:changeSpan('1');" />yes<br />
35228        <label>tail bytes:</label>
35229        <input type="text" name="tailbyte" value='$currTailByte' size="7"/>
35230      </td>
35231      <td align="left" $display>
35232        <label>search for: </label>
35233        <a href="javascript:void(0);" onmouseover="showhint('$h5', this, event, '450px', '1');return false;"><img height=12 width=12 src="$wikiinfo" /></a>
35234        <input type="text" name="search" value='$pat' size="30"/>
35235      </td>
35236      <td align="left">
35237        <input type="submit" value="Submit/Update" $display />
35238
35239        <input type="hidden" name="order" value='$order'/>
35240      </td>
35241      <td rowspan="2" $display>
35242        <input type="checkbox" name="nocontext"${\($qs{nocontext} ? ' checked="checked" ' : ' ')}value='1' />hide&nbsp;context&nbsp;lines<br />
35243        <input type="checkbox" name="nohighlight"${\($qs{nohighlight} ? ' checked="checked" ' : ' ')}value='1' />no&nbsp;highlighting<br />
35244        <input type="checkbox" name="filesonly"${\($qs{filesonly} ? ' checked="checked" ' : ' ')}value='1' />file&nbsp;lines&nbsp;only
35245      </td>
35246    </tr>
35247    <tr $display>
35248      <td align="left">
35249        <label>search in:</label>
35250        <a href="javascript:void(0);" onmouseover="showhint('$h4', this, event, '450px', '1');return false;"><img height=12 width=12 src="$wikiinfo" /></a>
35251        <input type="text" name="size" value='$size' size="7" />
35252        <select size="1" name="files" value="$qs{files}" />
35253          <option value="lines">last lines</option>
35254          <option value="files">last log files</option>
35255          <option value="all">all log files</option>
35256          <option value="ago">this file number(s)</option>
35257        </select>
35258        <a href="javascript:void(0);" onmouseover="showhint('$h3', this, event, '450px', '1');return false;"><img height=12 width=12 src="$wikiinfo" /></a>
35259      </td>
35260      <td align="left">
35261        <label>show </label>
35262        <select size="1" name="limit" value="$qs{limit}">
35263          <option value="1">1</option>
35264          <option value="10">10</option>
35265          <option value="100">100</option>
35266          <option value="1000">1000</option>
35267          <option value="2000">2000</option>
35268        </select> Results
35269      </td>
35270    </tr>
35271  </table>
35272</form>
35273<script type="text/javascript">
35274document.MTform.files.value='$files';
35275document.MTform.limit.value='$limit';
35276function resetForm() {
35277  document.MTform.search.value='';
35278  document.MTform.nocontext.checked=false;
35279  document.MTform.nohighlight.checked=false;
35280  document.MTform.filesonly.checked=false;
35281  document.MTform.tailbyte.value='$MaillogTailBytes';
35282  document.MTform.size.value='10000';
35283  document.MTform.files.value='lines';
35284  document.MTform.limit.value='10';
35285  document.MTform.order.value='0';
35286}
35287</script>
35288<div class="log $logstyle" $display>
35289<a href="javascript:void(0);" onclick="document.getElementById(\'LogLines\').scrollTop=MlEndPos; return false;" >Go to End</a>
35290&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35291<a href="javascript:void(0);" onclick="document.getElementById(\'LogLines\').scrollTop=0;return false;">Go to Top</a>
35292&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35293<a href="javascript:void(0);" onmouseover="showhint('$h3', this, event, '450px', '1');return false;">show filelist</a>
35294&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35295<a href="javascript:void(0);" onmouseover="showhint('$h1<br /><br />$h2', this, event, ie ? document.body.offsetWidth / 2.1 + 'px' : window.innerWidth / 2.1 + 'px' , '');return false;">help</a>
35296&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35297<a href="javascript:void(0);" onclick="resetForm();" onmouseover="showhint('click to reset the form to system defaults', this, event, '300px', '');return false;">reset form</a>
35298&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35299<a href="javascript:void(0);" onclick="switchMTOrder();" onmouseover="showhint('click to switch the time order of lines', this, event, '300px', '');return false;">switch order</a>
35300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
35301<a href="/" onmouseover="showhint('click to return to config dialog', this, event, '300px', '');return false;">back to config</a><br />
35302<script type="text/javascript">
35303if ('$qs{autorefresh}' != 'Auto') {
35304//var LogWidth = objWidth('headline') + 'px';
35305var LogHeight = ClientSize('h') - document.getElementById('headline').offsetHeight + 'px';
35306}
35307</script>
35308$res
35309<hr>
35310</div>
35311<div id="LogLines" class="log" style="display:block;height:100%;width=100%;overflow:auto;">
35312<div class="log $logstyle" width=100%>
35313<pre id="allLogLines" style="font-size: 1.4em;">
35314</pre>
35315
35316<script type="text/javascript">
35317
35318if ('$qs{autorefresh}' != 'Auto') {
35319//document.getElementById('LogLines').style.width = LogWidth;
35320document.getElementById('LogLines').style.height = LogHeight;
35321}
35322
35323var order = $order;
35324var allLines = "$s".split("XXXIIIXXX");
35325var allLinesF = allLines.join('');
35326var allLinesR = allLines.reverse().join('');
35327allLines = ('');
35328function switchMTOrder() {
35329  order = order ? 0 : 1 ;
35330  document.MTform.order.value=order;
35331  var logdiv = document.getElementById('allLogLines');
35332  logdiv.innerHTML = '';
35333  if (order == 1) {
35334      logdiv.innerHTML = allLinesR;
35335  } else {
35336      logdiv.innerHTML = allLinesF;
35337  }
35338}
35339order = order ? 0 : 1 ;
35340switchMTOrder();
35341if ('$qs{autorefresh}' != 'Auto') {
35342MlEndPos = document.getElementById('allLogLines').scrollHeight;
35343window.location.href = '#MlTop';
35344${\($MaillogTailJump && $qs{autorefresh} ne 'Auto' ? 'document.getElementById(\'LogLines\').scrollTop=MlEndPos;' : 'order = order;') }
35345}
35346</script>
35347</div>
35348<div $display >
35349$maillogJump
35350</div>
35351</div>
35352</div>
35353<div $display >
35354$footers
35355</div>
35356<form name="ASSPconfig" id="ASSPconfig" action="" method="post">
35357  <input name="theButtonLogout" type="hidden" value="" />
35358</form>
35359</body></html>
35360EOT
35361}
35362
35363
35364sub MaillogExistFile {
35365    my $file = shift;
35366
35367    return 0 unless $file;
35368    $maillogNewFile="";
35369    if ($LogCharset) {
35370     $file = Encode::encode($LogCharset, Encode::decode('UTF-8',$file));
35371    }
35372    my $newfile = $file;
35373    if ( !-e "$file" && $file =~ /\Q$spamlog\E\//i) {
35374
35375		if ($discarded) {
35376    		$newfile =~ s/\Q$spamlog\E\//$discarded\// ;
35377			$maillogNewFile = $discarded if -e "$newfile";
35378			return 1 if -e "$newfile" ;
35379    	}
35380
35381		$newfile = $file;
35382    	$newfile =~ s/\Q$spamlog\E\//$correctednotspam\// ;
35383		$maillogNewFile = $correctednotspam if -e "$newfile";
35384		return 1 if -e "$newfile" ;
35385
35386    }
35387    if ( !-e "$file" && $file =~ /\Q$notspamlog\E\//i) {
35388
35389		if ($discarded) {
35390    		$newfile =~ s/\Q$notspamlog\E\//$discarded\// ;
35391			$maillogNewFile = $discarded if -e "$newfile";
35392			return 1 if -e "$newfile" ;
35393    	}
35394
35395		$newfile = $file;
35396    	$newfile =~ s/\Q$notspamlog\E\//$correctedspam\// ;
35397		$maillogNewFile = $correctedspam if -e "$newfile";
35398		return 1 if -e "$newfile" ;
35399
35400    }
35401    if ( !-e "$file" && $file =~ /\Q$incomingOkMail\E\//i) {
35402
35403		if ($spamlog) {
35404    		$newfile =~ s/\Q$incomingOkMail\E\//$spamlog\// ;
35405			$maillogNewFile = $spamlog if -e "$newfile";
35406			return 1 if -e "$newfile" ;
35407    	}
35408
35409		$newfile = $file;
35410    	$newfile =~ s/\Q$incomingOkMail\E\//$correctedspam\// ;
35411		$maillogNewFile = $correctedspam if -e "$newfile";
35412		return 1 if -e "$newfile" ;
35413
35414    }
35415    return -e "$file" ;
35416}
35417
35418sub d8 {
35419    local $@;
35420    my $ret = eval{Encode::decode('UTF-8',$_[0]);};
35421    return ($ret && defined ${chr(ord("\026") << 2)}) ? $ret : $_[0];
35422}
35423
35424sub e8 {
35425    local $@;
35426    my $ret = eval{Encode::encode('UTF-8',$_[0]);};
35427    return ($ret && defined ${chr(ord("\026") << 2)}) ? $ret : $_[0];
35428}
35429
35430sub de8 {
35431    local $@;
35432    my $ret = eval{require Encode::Guess; e8(Encode::decode('GUESS',$_[0]));};
35433    return ($ret && defined ${chr(ord("\026") << 2)}) ? $ret : $_[0];
35434}
35435sub decodeMimeWord2UTF8 {
35436    my ($fulltext,$charset,$encoding,$text)=@_;
35437    my $ret;
35438
35439    eval {$charset = Encode::resolve_alias(uc($charset));} if $charset;
35440
35441    if (!$@ && $CanUseEMM && $charset ) {
35442        eval{$ret = MIME::Words::decode_mimewords($fulltext)} if $fulltext;
35443        eval{
35444            $ret = Encode::decode($charset, $ret);
35445            $ret = e8($ret) if $ret;
35446        } if $ret;
35447        return $ret unless $@;
35448    }
35449
35450    if (lc $encoding eq 'b') {
35451        $text=base64decode($text);
35452    } elsif (lc $encoding eq 'q') {
35453        $text=~s/_/\x20/go; # RFC 1522, Q rule 2
35454        $text=~s/=([\da-fA-F]{2})/pack('C', hex($1))/geo; # RFC 1522, Q rule 1
35455    };
35456    eval{
35457        $text = Encode::decode($charset, $text);
35458        $text = e8($text) if $text;
35459    } if $text;
35460    return $text;
35461}
35462
35463sub decodeMimeWords2UTF8 {
35464    my $s = shift;
35465    headerUnwrap($s);
35466    $s =~ s/(=\?([^?]*)\?(b|q)\?([^?]+)\?=)/decodeMimeWord2UTF8($1,$2,$3,$4)/gieo;
35467    return $s;
35468}
35469
35470sub transliterate {
35471    my ($text, $skipequal) = @_;
35472    return unless ($CanUseTextUnidecode);
35473    my $trans = eval{Text::Unidecode::unidecode(d8($$text));};
35474    return ($skipequal && $trans eq $$text) ? undef : defined(*{'yield'}) ? $trans : undef;
35475}
35476
35477sub canUserDo {
35478    my ($user,$what,$item) = @_;
35479
35480    return 1;
35481}
35482sub ConfigAddrAction {
35483    my $addr = lc($qs{address});
35484    $addr =~ s/^\s+//o;
35485    $addr =~ s/\s+$//o;
35486    my $local;
35487    my $isnameonly;
35488    $local = localmail($addr) if $addr;
35489    my $action = $qs{action};
35490    my $slo;
35491    $slo = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="button"  name="showlogout" value="  logout " onclick="window.location.href=\'./logout\';return false;"/></span>' if exists $qs{showlogout};
35492    my $s = $qs{reloaded} eq 'reloaded' ? '<span class="positive">(page was auto reloaded)</span><br /><br />' : '';
35493
35494    my $mfd;my $wrongaddr;
35495    if ($addr =~ /^(?:$EmailAdrRe)?(\@$EmailDomainRe)$/io) {
35496        $mfd = $1;
35497    } elsif ($addr =~ /^($EmailDomainRe)$/io) {
35498        $mfd = $1;
35499    } elsif ($addr =~ /^$EmailAdrRe$/io) {
35500        $isnameonly = '<br />This is interpreted as the userpart of an email address!<br />';
35501    } else {
35502        $wrongaddr = '<br /><span class="negative">This is not a valid email address or domain!</span><br />';
35503    }
35504
35505    if ($addr && $action && $qs{Submit} && !$wrongaddr) {
35506        my %lqs = %qs;
35507        if ($mfd && $action eq '1' && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists')) {
35508            %qs = ('action' => 'a', 'list' => 'white', 'addresses' => $addr);
35509            $s = &ConfigLists();
35510            $s =~ s/^.+?<\/h2>(.+?)<form.+$/$1/ois;
35511        } elsif ($mfd && $action eq '2' && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists')) {
35512            %qs = ('action' => 'r', 'list' => 'white', 'addresses' => $addr);
35513            $s = &ConfigLists();
35514            $s =~ s/^.+?<\/h2>(.+?)<form.+$/$1/ois;
35515        } elsif ($mfd && $action eq '3' && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists')) {
35516            %qs = ('action' => 'a', 'list' => 'red', 'addresses' => $addr);
35517            $s = &ConfigLists();
35518            $s =~ s/^.+?<\/h2>(.+?)<form.+$/$1/ois;
35519        } elsif ($mfd && $action eq '4' && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists')) {
35520            %qs = ('action' => 'r', 'list' => 'red', 'addresses' => $addr);
35521            $s = &ConfigLists();
35522            $s =~ s/^.+?<\/h2>(.+?)<form.+$/$1/ois;
35523        } elsif ($action eq '5' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingTo')) {
35524            my $r = $GPBmodTestList->('GUI','noProcessing','add',' - via MaillogTail',$addr,0);
35525            $s = ($r > 0) ? "$addr added to noProcessingTo" : "$addr not added to noProcessingTo";
35526        } elsif ($action eq '6' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingTo')) {
35527            my $r = $GPBmodTestList->('GUI','noProcessing','delete',' - via MaillogTail',$addr,0);
35528            $s = ($r > 0) ? "$addr removed from noProcessingTo" : "$addr not removed from noProcessingTo";
35529        } elsif ($action eq '7' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingFrom')) {
35530            my $r = $GPBmodTestList->('GUI','noProcessingFrom','add',' - via MaillogTail',$addr,0);
35531            $s = ($r > 0) ? "$addr added to noProcessingFrom" : "$addr not added to noProcessingFrom";
35532        } elsif ($action eq '8' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingFrom')) {
35533            my $r = $GPBmodTestList->('GUI','noProcessingFrom','delete',' - via MaillogTail',$addr,0);
35534            $s = ($r > 0) ? "$addr removed from noProcessingFrom" : "$addr not removed from noProcessingFrom";
35535        } elsif ($mfd && $action eq '9' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedDomains')) {
35536            my $r = $GPBmodTestList->('GUI','whiteListedDomains','add',' - via MaillogTail',$addr,0);
35537            $s = ($r > 0) ? "$addr added to whiteListedDomains" : "$addr not added to whiteListedDomains";
35538        } elsif ($mfd && $action eq 'A' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedDomains')) {
35539            my $r = $GPBmodTestList->('GUI','whiteListedDomains','delete',' - via MaillogTail',$addr,0);
35540            $s = ($r > 0) ? "$addr removed from whiteListedDomains" : "$addr not removed from whiteListedDomains";
35541        } elsif ($mfd && $action eq 'B' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','blackListedDomains')) {
35542            my $r = $GPBmodTestList->('GUI','blackListedDomains','add',' - via MaillogTail',$addr,0);
35543            $s = ($r > 0) ? "$addr added to blackListedDomains" : "$addr not added to blackListedDomains";
35544        } elsif ($mfd && $action eq 'C' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','blackListedDomains')) {
35545            my $r = $GPBmodTestList->('GUI','blackListedDomains','delete',' - via MaillogTail',$addr,0);
35546            $s = ($r > 0) ? "$addr removed from blackListedDomains" : "$addr not removed from blackListedDomains";
35547        } elsif ($action eq 'D' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamLovers')) {
35548            my $r = $GPBmodTestList->('GUI','spamLovers','add',' - via MaillogTail',$addr,0);
35549            $s = ($r > 0) ? "$addr added to spamLovers (All Spam-Lover)" : "$addr not added to spamLovers (All Spam-Lover)";
35550        } elsif ($action eq 'E' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamLovers')) {
35551            my $r = $GPBmodTestList->('GUI','spamLovers','delete',' - via MaillogTail',$addr,0);
35552            $s = ($r > 0) ? "$addr removed from spamLovers (All Spam-Lover)" : "$addr not removed from spamLovers (All Spam-Lover)";
35553        } elsif ($action eq 'F' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamHaters')) {
35554            my $r = $GPBmodTestList->('GUI','spamHaters','add',' - via MaillogTail',$addr,0);
35555            $s = ($r > 0) ? "$addr added to spamHaters (All Spam-Haters)" : "$addr not added to spamHaters (All Spam-Haters)";
35556        } elsif ($action eq 'G' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamHaters')) {
35557            my $r = $GPBmodTestList->('GUI','spamHaters','delete',' - via MaillogTail',$addr,0);
35558            $s = ($r > 0) ? "$addr removed from spamHaters (All Spam-Haters)" : "$addr not removed from spamHaters (All Spam-Haters)";
35559        } elsif ($mfd && $action eq 'H' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingDomains')) {
35560            my $r = $GPBmodTestList->('GUI','noProcessingDomains','add',' - via MaillogTail',$mfd,0);
35561            $s = ($r > 0) ? "$mfd added to noProcessing Domains" : "$mfd not added to noProcessing Domains";
35562        } elsif ($mfd && $action eq 'I' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingDomains')) {
35563            my $r = $GPBmodTestList->('GUI','noProcessingDomains','delete',' - via MaillogTail',$mfd,0);
35564            $s = ($r > 0) ? "$mfd removed from noProcessing Domains" : "$mfd not removed from noProcessing Domains";
35565        } elsif ($action eq 'J' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','preHeaderRe')) {
35566            my $addrRe = quotemeta($addr);
35567            my $r = $GPBmodTestList->('GUI','preHeaderRe','add',' - via MaillogTail',$addrRe,0);
35568            $s = ($r > 0) ? "$addr added as regex ($addrRe) to preHeaderRe" : "$addr not added to preHeaderRe";
35569        } elsif ($action eq 'K' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','preHeaderRe')) {
35570            my $addrRe = quotemeta($addr);
35571            my $r = $GPBmodTestList->('GUI','preHeaderRe','delete',' - via MaillogTail',$addrRe,0);
35572            $s = ($r > 0) ? "$addr removed as regex ($addrRe) from preHeaderRe" : "$addr not removed from preHeaderRe";
35573        } elsif ($action eq 'L' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScan')) {
35574            my $r = $GPBmodTestList->('GUI','noScan','add',' - via MaillogTail',$addr,0);
35575            $s = ($r > 0) ? "$addr added to sDo Not Virus-Scan Messages from/to these Addresses(noScan)" : "$addr not added to Do Not Scan Messages from/to these Addresses(noScan)";
35576        } elsif ($action eq 'M' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScan')) {
35577            my $r = $GPBmodTestList->('GUI','noScan','delete',' - via MaillogTail',$addr,0);
35578            $s = ($r > 0) ? "$addr removed from Do Not Virus-Scan Messages from/to these Addresses(noScan)" : "$addr not Do Not Scan Messages from/to these Addresses(noScan)";
35579        } elsif ($action) {
35580            $s = "<span class=\"negative\">access denied for the selected action</span>";
35581        }
35582        %qs = %lqs;
35583    }
35584    $s = 'no action selected - or no result available' if (! $s && $qs{Submit});
35585    if ($s !~ /not|negative/ && $qs{Submit}) {
35586        $ConfigChanged = 1;
35587        &tellThreadsReReadConfig();   # reread the config
35588    }
35589
35590    my $option  = "<option value=\"0\">select action</option>";
35591    if ($addr && ! $wrongaddr) {
35592        $option .= "<option value=\"1\">add to WhiteList</option>"
35593         if ($mfd && ! $local && ! Whitelist($addr,'','') && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists'));
35594        $option .= "<option value=\"2\">remove from WhiteList</option>"
35595         if ($mfd && ! $local &&  Whitelist($addr,'','') && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists'));
35596        $option .= "<option value=\"3\">add to RedList</option>"
35597         if ($mfd && ! exists $Redlist{$addr} && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists'));
35598        $option .= "<option value=\"4\">remove from RedList</option>"
35599         if ($mfd && exists $Redlist{$addr} && &canUserDo($WebIP{$ActWebSess}->{user},'action','lists'));
35600        $option .= "<option value=\"5\">add to noProcessingTo addresses</option>"
35601         if ($noProcessingTo=~/\s*file\s*:\s*.+/o && ! $local && ! matchSL( $addr, 'noProcessingTo' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessing'));
35602        $option .= "<option value=\"6\">remove from noProcessingTo addresses</option>"
35603         if ($noProcessingTo=~/\s*file\s*:\s*.+/o && ! $local &&  matchSL( $addr, 'noProcessing' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingTo'));
35604        $option .= "<option value=\"7\">add to noProcessingFrom addresses</option>"
35605         if ($noProcessingFrom=~/\s*file\s*:\s*.+/o && ! $local && ! matchSL( $addr, 'noProcessingFrom' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingFrom'));
35606        $option .= "<option value=\"8\">remove from noProcessingFrom addresses</option>"
35607         if ($noProcessingFrom=~/\s*file\s*:\s*.+/o && ! $local && matchSL( $addr, 'noProcessingFrom' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingFrom'));
35608        $option .= "<option value=\"9\">add to whitelisted domains/addresses</option>"
35609         if ($mfd && $whiteListedDomains=~/\s*file\s*:\s*.+/o && ! $local && $addr !~ /$WLDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedDomains'));
35610        $option .= "<option value=\"A\">remove from whitelisted domains/addresses</option>"
35611         if ($mfd && $whiteListedDomains=~/\s*file\s*:\s*.+/o && ! $local && $addr =~ /$WLDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedDomains'));
35612        $option .= "<option value=\"B\">add to blacklisted domains/addresses</option>"
35613         if ($mfd && $blackListedDomains=~/\s*file\s*:\s*.+/o && ! $local && $addr !~ /$BLDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','blackListedDomains'));
35614        $option .= "<option value=\"C\">remove from blacklisted domains/addresses</option>"
35615         if ($mfd && $blackListedDomains=~/\s*file\s*:\s*.+/o && ! $local && $addr =~ /$BLDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','blackListedDomains'));
35616        $option .= "<option value=\"D\">add to All Spam-Lover</option>"
35617         if ($spamLovers=~/\s*file\s*:\s*.+/o && $local && $addr !~ /$SLRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamLovers'));
35618        $option .= "<option value=\"E\">remove from All Spam-Lover</option>"
35619         if ($spamLovers=~/\s*file\s*:\s*.+/o && $local && $addr =~ /$SLRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamLovers'));
35620        $option .= "<option value=\"F\">add to All Spam-Haters</option>"
35621         if ($spamHaters=~/\s*file\s*:\s*.+/o && $local && $addr !~ /$SHRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamHaters'));
35622        $option .= "<option value=\"G\">remove from All Spam-Haters</option>"
35623         if ($spamHaters=~/\s*file\s*:\s*.+/o && $local && $addr =~ /$SHRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','spamHaters'));
35624        $option .= "<option value=\"H\">add $mfd to noProcessing domains</option>"
35625         if ($mfd && $noProcessingDomains=~/\s*file\s*:\s*.+/o && ! $local && $mfd !~ /$NPDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingDomains'));
35626        $option .= "<option value=\"I\">remove $mfd from noProcessing domains</option>"
35627         if ($mfd && $noProcessingDomains=~/\s*file\s*:\s*.+/o  && ! $local && $mfd =~ /$NPDRE/ && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingDomains'));
35628
35629#experimental for preHeaderRe
35630        my $addrRe = quotemeta($addr);
35631        $option .= "<option value=\"J\">add to preHeaderRe as regex</option>"
35632         if ($preHeaderRe=~/\s*file\s*:\s*.+/o && ! $local && $GPBmodTestList->('GUI','preHeaderRe','check','',$addrRe,0) != 2 && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','preHeaderRe'));
35633        $option .= "<option value=\"K\">remove regex from preHeaderRe</option>"
35634         if ($preHeaderRe=~/\s*file\s*:\s*.+/o && ! $local && $GPBmodTestList->('GUI','preHeaderRe','check','',$addrRe,0) == 2 && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','preHeaderRe'));
35635#end experimental for preHeaderRe
35636
35637        $option .= "<option value=\"L\">add to no Virus-Scan addresses</option>"
35638         if ($noScan=~/\s*file\s*:\s*.+/o && ! $local && ! matchSL( $addr, 'noScan' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScan'));
35639        $option .= "<option value=\"M\">remove from no Virus-Scan addresses</option>"
35640         if ($noScan=~/\s*file\s*:\s*.+/o && ! $local &&  matchSL( $addr, 'noScan' ,1) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScan'));
35641    }
35642
35643
35644    return <<EOT;
35645$headerHTTP
35646
35647<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
35648<head>
35649  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
35650  <title>$currentPage ASSP address action ($myName)</title>
35651  <link rel=\"stylesheet\" href=\"get?file=images/editor.css\" type=\"text/css\" />
35652</head>
35653<body onmouseover="this.focus();" ondblclick="this.select();">
35654<h2>add/remove addresses from lists</h2><hr>
35655    <div class="content">
35656      <form name="edit" id="edit" action="" method="post" autocomplete="off">
35657        <h3>address to work with</h3>
35658        <input name="address" size="100" autocomplete="off" value="$addr" onchange="document.forms['edit'].action.value='0';document.forms['edit'].reloaded.value='reloaded';document.forms['edit'].submit();return false;"/>
35659        $wrongaddr$isnameonly
35660        <br /><hr>
35661        <div style="align: left">
35662         <div class="shadow">
35663          <div class="option">
35664           <div class="optionValue">
35665            <select size="1" name="action">
35666             $option
35667            </select>
35668           </div>
35669          </div>
35670         </div>
35671        </div>
35672        <hr>
35673        <input type="submit" name="Submit" value="Submit" />&nbsp;&nbsp;&nbsp;&nbsp;
35674        <input type="hidden" name="reloaded" value="" />
35675        <input type="button" value="Close" onclick="javascript:window.close();"/>
35676        $slo
35677        <hr>
35678      </form>
35679      <br />Only configured (file:...), possible and authorized option are shown.
35680      <hr>
35681      <div class="note" id="notebox">
35682        <h3>results for action</h3><hr>
35683        $s
35684      </div>
35685    </div>
35686</body>
35687</html>
35688
35689EOT
35690}
35691
35692sub ConfigIPAction {
35693    my $addr = lc($qs{ip});
35694    $addr =~ s/^\s+//o;
35695    $addr =~ s/\s+$//o;
35696    my $wrongaddr;
35697    if ($addr !~ /^$IPRe$/o) {
35698        $wrongaddr = '<br /><span class="negative">This is not a valid IP address or a resolvable hostname!</span><br />' ;
35699    }
35700    if ($wrongaddr && $addr =~ /^$HostRe$/o) {
35701        my $ta = $addr;
35702        $addr = join(' ' ,&getRRA($ta));
35703        if ($addr =~ /($IPv4Re)/o) {
35704            $addr = $1;
35705        } elsif ($addr =~ /($IPv6Re)/o) {
35706            $addr = $1;
35707        } else {
35708            $addr = undef;
35709        }
35710        eval {$addr = inet_ntoa( scalar( gethostbyname($ta) ) );} unless $addr;
35711        if ($addr =~ /^$IPRe$/o ) {
35712            $wrongaddr = undef;
35713        } else {
35714            $addr = $ta;
35715        }
35716    }
35717    my $local = $addr =~ /^$IPprivate$/o || $addr eq $localhostip || $addr =~ /$LHNRE/;
35718    my $action = $qs{action};
35719    my $slo;
35720    $slo = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="button"  name="showlogout" value="  logout " onclick="window.location.href=\'./logout\';return false;"/></span>' if exists $qs{showlogout};
35721    my $s = $qs{reloaded} eq 'reloaded' ? '<span class="positive">(page was auto reloaded)</span><br /><br />' : '';
35722
35723    if ($addr && $action && $qs{Submit} && ! $wrongaddr) {
35724        if ($action eq '1' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingIPs')) {
35725            my $r = $GPBmodTestList->('GUI','noProcessingIPs','add',' - via MaillogTail',$addr,0);
35726            $s = ($r > 0) ? "$addr added to noProcessingIPs" : "$addr not added to noProcessingIPs";
35727        } elsif ($action eq '2' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingIPs')) {
35728            my $r = $GPBmodTestList->('GUI','noProcessingIPs','delete',' - via MaillogTail',$addr,0);
35729            $s = ($r > 0) ? "$addr removed from noProcessingIPs" : "$addr not removed from noProcessingIPs";
35730        } elsif ($action eq '3' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedIPs')) {
35731            my $r = $GPBmodTestList->('GUI','whiteListedIPs','add',' - via MaillogTail',$addr,0);
35732            $s = ($r > 0) ? "$addr added to whiteListedIPs" : "$addr not added to whiteListedIPs";
35733        } elsif ($action eq '4' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedIPs')) {
35734            my $r = $GPBmodTestList->('GUI','whiteListedIPs','delete',' - via MaillogTail',$addr,0);
35735            $s = ($r > 0) ? "$addr removed from whiteListedIPs" : "$addr not removed from whiteListedIPs";
35736        } elsif ($action eq '5' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noDelay')) {
35737            my $r = $GPBmodTestList->('GUI','noDelay','add',' - via MaillogTail',$addr,0);
35738            $s = ($r > 0) ? "$addr added to noDelay" : "$addr not added to noDelay";
35739        } elsif ($action eq '6' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noDelay')) {
35740            my $r = $GPBmodTestList->('GUI','noDelay','delete',' - via MaillogTail',$addr,0);
35741            $s = ($r > 0) ? "$addr removed from noDelay" : "$addr not removed from noDelay";
35742        } elsif ($action eq '7' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFrom')) {
35743            my $r = $GPBmodTestList->('GUI','denySMTPConnectionsFrom','add',' - via MaillogTail',$addr,0);
35744            $s = ($r > 0) ? "$addr added to denySMTPConnectionsFrom" : "$addr not added to denySMTPConnectionsFrom";
35745        } elsif ($action eq '8' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFrom')) {
35746            my $r = $GPBmodTestList->('GUI','denySMTPConnectionsFrom','delete',' - via MaillogTail',$addr,0);
35747            $s = ($r > 0) ? "$addr removed from denySMTPConnectionsFrom" : "$addr not removed from denySMTPConnectionsFrom";
35748        } elsif ($action eq '9' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noBlockingIPs')) {
35749            my $r = $GPBmodTestList->('GUI','noBlockingIPs','add',' - via MaillogTail',$addr,0);
35750            $s = ($r > 0) ? "$addr added to noBlockingIPs" : "$addr not added to noBlockingIPs";
35751        } elsif ($action eq 'A' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noBlockingIPs')) {
35752            my $r = $GPBmodTestList->('GUI','noBlockingIPs','delete',' - via MaillogTail',$addr,0);
35753            $s = ($r > 0) ? "$addr removed from noBlockingIPs" : "$addr not removed from noBlockingIPs";
35754        } elsif ($action eq 'S' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','NPexcludeIPs')) {
35755            my $r = $GPBmodTestList->('GUI','NPexcludeIPs','add',' - via MaillogTail',$addr,0);
35756            $s = ($r > 0) ? "$addr added to NPexcludeIPs" : "$addr not added to NPexcludeIPs";
35757        } elsif ($action eq 'T' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','NPexcludeIPs')) {
35758            my $r = $GPBmodTestList->('GUI','NPexcludeIPs','delete',' - via MaillogTail',$addr,0);
35759            $s = ($r > 0) ? "$addr removed from NPexcludeIPs" : "$addr not removed from NPexcludeIPs";
35760        } elsif ($action eq 'B' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFromAlways')) {
35761            my $r = $GPBmodTestList->('GUI','denySMTPConnectionsFromAlways','add',' - via MaillogTail',$addr,0);
35762            $s = ($r > 0) ? "$addr added to denySMTPConnectionsFromAlways" : "$addr not added to denySMTPConnectionsFromAlways";
35763        } elsif ($action eq 'C' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFromAlways')) {
35764            my $r = $GPBmodTestList->('GUI','denySMTPConnectionsFromAlways','delete',' - via MaillogTail',$addr,0);
35765            $s = ($r > 0) ? "$addr removed from denySMTPConnectionsFromAlways" : "$addr not removed from denySMTPConnectionsFromAlways";
35766        } elsif ($action eq 'D' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35767            my $t = time;
35768            my $data="$t $t 2";
35769            my $ip=&ipNetwork($addr,1);
35770            $PBWhite{$ip}=$data;
35771            $PBWhite{$addr}=$data;
35772            $s = "$addr added to PenaltyBox white" ;
35773        } elsif ($action eq 'E' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35774            &pbWhiteDelete(0,$addr);
35775            $s = "$addr removed from PenaltyBox white" ;
35776        } elsif ($action eq 'F' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35777            my $ip=&ipNetwork($addr, $PenaltyUseNetblocks );
35778            delete $PBBlack{$ip};
35779            delete $PBBlack{$addr};
35780            $s = "$addr removed from PenaltyBox black";
35781        } elsif ($action eq 'G' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35782            delete $PTRCache{$addr};
35783            $s = "$addr removed from PTR Cache";
35784        } elsif ($action eq 'H' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35785            delete $URIBLCache{$addr};
35786            $s = "$addr removed from URIBL Cache";
35787        } elsif ($action eq 'I' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35788            my @record = SBCacheFind($addr);
35789            my $domain = [split( /\|/o, $record[2])]->[2];
35790            delete $WhiteOrgList{lc $domain} if $domain;
35791            delete $SBCache{$record[0]};
35792            $s = "$record[0] removed from SenderBase Cache";
35793        } elsif ($action eq 'J' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35794            delete $RBLCache{$addr};
35795            $s = "$addr removed from RBL Cache";
35796        } elsif ($action eq 'K' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35797            delete $MXACache{$addr};
35798            $s = "$addr removed from MXA Cache";
35799        } elsif ($action eq 'L' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35800            delete $BackDNS{$addr};
35801            delete $BackDNS2{$addr};
35802            $s = "$addr removed from Backscatter Cache";
35803        } elsif ($action eq 'M' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb')) {
35804            delete $RWLCache{$addr};
35805            $s = "$addr removed from RWL Cache";
35806        } elsif ($action eq 'N' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScanIP')) {
35807            my $r = $GPBmodTestList->('GUI','noScanIP','add',' - via MaillogTail',$addr,0);
35808            $s = ($r > 0) ? "$addr added to Virus-noScanIP" : "$addr not added to Virus-noScanIP";
35809        } elsif ($action eq 'O' && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScanIP')) {
35810            my $r = $GPBmodTestList->('GUI','noScanIP','delete',' - via MaillogTail',$addr,0);
35811            $s = ($r > 0) ? "$addr removed from Virus-noScanIP" : "$addr not removed from Virus-noScanIP";
35812        } elsif ($action) {
35813            $s = "<span class=\"negative\">access denied for the selected action</span>";
35814        }
35815    }
35816    $s = 'no action selected - or no result available' if (! $s && $qs{Submit});
35817
35818    if ($s =~ /\Q$addr\E (?:added to|removed from)/ && $qs{Submit}) {
35819        $ConfigChanged = 1;
35820        &tellThreadsReReadConfig();   # reread the config
35821    }
35822
35823    my $option  = "<option value=\"0\">select action</option>";
35824    if ($addr && ! $wrongaddr) {
35825        $option .= "<option value=\"1\">add to noProcessing IP's</option>"
35826         if (! $local && $noProcessingIPs=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'noProcessingIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingIPs'));
35827        $option .= "<option value=\"2\">remove from noProcessing IP's</option>"
35828         if (! $local && $noProcessingIPs=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'noProcessingIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noProcessingIPs'));
35829        $option .= "<option value=\"3\">add to whitelisted IP's</option>"
35830         if (! $local && $whiteListedIPs=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'whiteListedIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedIPs'));
35831        $option .= "<option value=\"4\">remove from whitelisted IP's</option>"
35832         if (! $local && $whiteListedIPs=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'whiteListedIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','whiteListedIPs'));
35833        $option .= "<option value=\"5\">add to noDelay IP's</option>"
35834         if (! $local && $noDelay=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'noDelay',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noDelay'));
35835        $option .= "<option value=\"6\">remove from noDelay IP's</option>"
35836         if (! $local && $noDelay=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'noDelay',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noDelay'));
35837        $option .= "<option value=\"7\">add to Deny Connections from these IP's</option>"
35838         if (! $local && $denySMTPConnectionsFrom=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'denySMTPConnectionsFrom',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFrom'));
35839        $option .= "<option value=\"8\">remove from Deny Connections from these IP's</option>"
35840         if (! $local && $denySMTPConnectionsFrom=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'denySMTPConnectionsFrom',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFrom'));
35841        $option .= "<option value=\"9\">add to Do not block Connections from these IP's</option>"
35842         if (! $local && $noBlockingIPs=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'noBlockingIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noBlockingIPs'));
35843        $option .= "<option value=\"A\">remove from Do not block Connections from these IP's</option>"
35844         if (! $local && $noBlockingIPs=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'noBlockingIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noBlockingIPs'));
35845        $option .= "<option value=\"S\">add to Exclude these IPs</option>"
35846         if (! $local && $NPexcludeIPs=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'NPexcludeIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','NPexcludeIPs'));
35847        $option .= "<option value=\"T\">remove from Exclude these IP's</option>"
35848         if (! $local && $NPexcludeIPs=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'NPexcludeIPs',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','NPexcludeIPs'));
35849        $option .= "<option value=\"B\">add to Deny Connections from these IP's Strictly</option>"
35850         if (! $local && $denySMTPConnectionsFromAlways=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'denySMTPConnectionsFromAlways',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFromAlways'));
35851        $option .= "<option value=\"C\">remove from Deny Connections from these IP's Strictly</option>"
35852         if (! $local && $denySMTPConnectionsFromAlways=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'denySMTPConnectionsFromAlways',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','denySMTPConnectionsFromAlways'));
35853        $option .= "<option value=\"D\">add to PenaltyBox white</option>"
35854         if (! &pbWhiteFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35855        $option .= "<option value=\"E\">remove from PenaltyBox white</option>"
35856         if (&pbWhiteFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35857        $option .= "<option value=\"F\">remove from PenaltyBox black</option>"
35858         if (&pbBlackFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35859        $option .= "<option value=\"G\">remove from PTR Cache</option>"
35860         if (&PTRCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35861        $option .= "<option value=\"H\">remove from URIBL Cache</option>"
35862         if (&URIBLCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35863        $option .= "<option value=\"I\">remove from SenderBase Cache</option>"
35864         if (&SBCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35865        $option .= "<option value=\"J\">remove from RBL Cache</option>"
35866         if (&RBLCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35867        $option .= "<option value=\"K\">remove from MXA Cache</option>"
35868         if (&MXACacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35869        $option .= "<option value=\"L\">remove from Backscatter Cache</option>"
35870         if (&BackDNSCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35871        $option .= "<option value=\"M\">remove from RWL Cache</option>"
35872         if (&RWLCacheFind($addr) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','pbdb'));
35873        $option .= "<option value=\"N\">add to Do Not Scan Messages from these IP\'s</option>"
35874         if ($noScanIP=~/\s*file\s*:\s*.+/o && ! matchIP( $addr, 'noScanIP',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScanIP'));
35875        $option .= "<option value=\"O\">remove from Do Not Scan Messages from these IP\'s</option>"
35876         if ($noScanIP=~/\s*file\s*:\s*.+/o &&  matchIP( $addr, 'noScanIP',0,1 ) && &canUserDo($WebIP{$ActWebSess}->{user},'cfg','noScanIP'));
35877    }
35878
35879    if ($addr && ! $wrongaddr) {
35880        $s .= "<br /><br /><b>general IP-matches for $addr :</b><br /><br />\n";
35881        foreach (sort {lc($main::a) cmp lc($main::b)} keys %MakeIPRE) {
35882            next unless &canUserDo($WebIP{$ActWebSess}->{user},'cfg',$_);
35883            my $res = matchIP( $addr, $_,0,1 );
35884            $s .= "matches in<b> $_ </b>with <b>$res</b><br />" if $res;
35885        }
35886    }
35887
35888    return <<EOT;
35889$headerHTTP
35890
35891<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
35892<head>
35893  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
35894  <title>$currentPage ASSP IP action ($myName)</title>
35895  <link rel=\"stylesheet\" href=\"get?file=images/editor.css\" type=\"text/css\" />
35896</head>
35897<body onmouseover="this.focus();" ondblclick="this.select();">
35898<h2>add/remove IP addresses from lists</h2><hr>
35899    <div class="content">
35900      <form name="edit" id="edit" action="" method="post" autocomplete="off">
35901        <h3>IP-address or hostname to work with</h3>
35902        <input name="ip" size="20" autocomplete="off" value="$addr" onchange="document.forms['edit'].action.value='0';document.forms['edit'].reloaded.value='reloaded';document.forms['edit'].submit();return false;"/>
35903        $wrongaddr
35904        <br /><hr>
35905        <div style="align: left">
35906         <div class="shadow">
35907          <div class="option">
35908           <div class="optionValue">
35909            <select size="1" name="action">
35910             $option
35911            </select>
35912           </div>
35913          </div>
35914         </div>
35915        </div>
35916        <hr>
35917        <input type="submit" name="Submit" value="Submit" />&nbsp;&nbsp;&nbsp;&nbsp;
35918        <input type="hidden" name="reloaded" value="" />
35919        <input type="button" value="Close" onclick="javascript:window.close();"/>
35920        $slo
35921        <hr>
35922      </form>
35923      <br />Only configured, possible and authorized option are shown.
35924      <hr>
35925      <div class="note" id="notebox">
35926        <h3>results for action</h3><hr>
35927        $s
35928      </div>
35929    </div>
35930</body>
35931</html>
35932
35933EOT
35934}
35935
35936sub tellThreadsReReadConfig {
35937    if ($Config{inclResendLink}) {
35938        $Config{fileLogging} = 1;
35939        $fileLogging = 1;
35940    }
35941    &SaveConfig() if ($ConfigChanged < 2);
35942    &optionFilesReload();
35943
35944    %LDAPNotFound = ();
35945
35946    &readNorm();
35947
35948    $ConfigChanged = 0;
35949}
35950
35951sub modListOnEdit {
35952    my ($reportaddr, $to, $mail, $fh) = @_;
35953    $fh ||= 'modListOnEdit';
35954    $Con{$fh}->{reportaddr} = $reportaddr;
35955    return unless $EmailAdminReportsTo;
35956    my $mailfrom = $Con{$fh}->{mailfrom};
35957    my $header = $Con{$fh}->{header};
35958    $Con{$fh}->{mailfrom} = $EmailAdminReportsTo || $to;
35959    $Con{$fh}->{header} = ${$mail};
35960    for my $addr (&ListReportGetAddr($fh)) {
35961        &ListReportExec($addr,$Con{$fh});
35962    }
35963    $Con{$fh}->{mailfrom} = $mailfrom ;
35964    $Con{$fh}->{header} = $header;
35965    my $ret = $Con{$fh}->{report};
35966    $ret =~ s/^(?:\s|\r|\n)+//o;
35967    $ret =~ s/\r?\n/<br \/>/gos;
35968    $ret = '<br />'.$ret if $ret;
35969    delete $Con{$fh} if $fh eq 'modListOnEdit';
35970    return $ret;
35971}
35972
35973
35974sub ConfigEdit {
35975 my $fil  = $qs{file};
35976 $qs{note} = lc $qs{note};
35977 my $htmlfil;
35978 my $note = q{};
35979 my ($cidr,$regexp1,$regexp2);
35980 my ($s1,$s2,$editButtons,$option);
35981 my $noLineNum = '';
35982 $cidr=$regexp1=$regexp2=q{};
35983
35984 if ($qs{note} eq '1'){
35985  $note = '<div class="note" id="notebox">File should have one entry per line; anything on a line following a numbersign ( #) is ignored (a comment). <a href=http://assp.cvs.sourceforge.net/viewvc/assp/asspV1/files/ target=files >newest option files are archived here</a>.';
35986 }
35987  elsif($qs{note} eq '9'){
35988  $note = '<div class="note" id="notebox">This matches the end of the address, so if you don\'t want to match subdomains then include the @. File should have one entry per line; anything on a line following a numbersign ( #) is ignored (a comment). Whitespace at the beginning or end of the line is ignored. </div>';
35989 }
35990 elsif($qs{note} eq '2'){
35991  $note = '<div class="note" id="notebox">First line specifies text that appears in the subject of report message. The remaining lines are the report message body. </div>';
35992 }
35993 elsif($qs{note} eq '3'){
35994  $note = '<div class="note" id="notebox">Put here comments to your assp installation.</div>';
35995 }
35996 elsif($qs{note} eq '4'){
35997  $note = '<div class="note" id="notebox">For removal of entries from BlackBox  use <a onmousedown="showDisp(\'8\')" target="main" href="./#noPB">noPB</a>.
35998For removal of entries from WhiteBox  use <a onmousedown="showDisp(\'8\')" target="main" href="./#noPBwhite">noPBwhite</a>. For  whitelisting IP addresses use <a onmousedown="showDisp(\'5\')" target="main" href="./#whiteListedIPs">Whitelisted IPs</a> or <a onmousedown="showDisp(\'4\')" target="main" href="./#noProcessingIPs">NoProcessing IPs</a>. For blacklisting use <a onmousedown="showDisp(\'2\')" target="main" href="./#denySMTPConnectionsFrom">Deny SMTP Connections From these IPs</a> and <a onmousedown="showDisp(\'2\')" target="main" href="./#denySMTPConnectionsFromAlways">Deny SMTP Connections From these IP addresses Strictly</a>.</div>';
35999 }
36000  elsif($qs{note} eq '5'){
36001  $note = '<div class="note" id="notebox"></div>';
36002 }
36003  elsif($qs{note} eq '6'){
36004  $note = '<div class="note" id="notebox">CacheEntry: <IP/Domain> <Separator(12)> <CreationDate> </div\>';
36005 }
36006  elsif($qs{note} eq '8' ){
36007  $note = '<div class="note" id="notebox"></div>';
36008 }
36009 elsif ($qs{note} eq 'm'){
36010        $fil="$base/$fil" if $fil!~/^\Q$base\E/i;
36011        $option  = "<option value=\"0\">select action</option>";
36012        $option .= "<option value=\"1\">resend mail</option>" if($CanUseEMS && $resendmail && $fil !~/\/$resendmail\//);
36013        $option .= "<option value=\"2\">save file</option>";
36014        $option .= "<option value=\"3\">copy file to notspamlog</option>" if ($fil !~/\/$notspamlog\//);
36015        $option .= "<option value=\"4\">copy file to spamlog</option>" if ($fil !~/\/$spamlog\//);
36016        $option .= "<option value=\"5\">copy file to incomingOkMail</option>" if ($fil !~/\/$incomingOkMail\//);
36017        $option .= "<option value=\"6\">copy file to viruslog</option>" if ($fil !~/\/$viruslog\//);
36018        $option .= "<option value=\"7\">copy file to correctedspam</option>" if ($fil !~/\/$correctedspam\//);
36019        $option .= "<option value=\"8\">copy file to correctednotspam</option>" if ($fil !~/\/$correctednotspam\//);
36020        $option .= "<option value=\"9\">copy file to discarded</option>" if ($fil !~/\/$discarded\//);
36021
36022        $note = '<div class="note" id="notebox">To take an action, select the action and click "Do It!". To move a file to an other location, just copy and delete the file!';
36023$note .= '<br /> For "resend file" action install Email::Send  modules!' if !($CanUseEMS && $resendmail && $fil !~/\/$resendmail\//);
36024}
36025
36026
36027$regexp1 = " <span class=negative>CIDR notation like 182.82.10.0/24 cannot be used because Net::IP::Match::Regexp is not installed.</span>" if !$CanMatchCIDR;
36028
36029$regexp1 = " <span class=positive>CIDR notation like 182.82.10.0/24 can be used.</span>" if $CanMatchCIDR;
36030$regexp2 = " Text after the IP range but before a numbersign will be used as comment to be shown in a match. For example: <br />182.82.10.0/24 Yahoo # this comment not shown" if $CanMatchCIDR;
36031my $replaceit;$replaceit = '<span style="align: left">Replace: <input type="text" id="find" size="20" /> with <input type="text" id="replace" size="20" /> <input type="button" value="Replace" onclick="replaceIt();" /></span>' if $qs{note} ne '8';
36032
36033$cidr = " <span class=negative>Hyphenated ranges like 182.82.10.0-182.82.10.255 cannot be used because Net::CIDR::Lite is not installed.</span>  " if !$CanUseCIDRlite;
36034$cidr = " <span class=positive>Hyphenated ranges like 182.82.10.0-182.82.10.255 can be used.</span>" if $CanUseCIDRlite;
36035  if ($qs{note} eq '7'){
36036  $note = "<div class='note' id='notebox'>IP ranges can be defined as: 182.82.10. $regexp1 $cidr $regexp2</div>";
36037 }
36038
36039 $s2 = '';
36040 if ($fil =~ /\.\./){
36041  $s2.='<div class="text"><span class="negative">File path includes \'..\' -- access denied</span></div>';
36042  mlog(0,"file path not allowed while editing file '$fil'");
36043 }
36044
36045 else {
36046
36047  $fil="$base/$fil" if $fil!~/^\Q$base\E/i;
36048  $fil =~ s/\/\//\//;
36049  $fil =~ s/\\\\/\\/;
36050  if ($qs{B1}=~/delete/i) {
36051   unlink($fil);
36052  }
36053  else {
36054   if (defined($qs{contents})) {
36055    $s1=$qs{contents};
36056    $s1=decodeHTMLEntities($s1);
36057    $s1 =~ s/\n$//; # prevents ASSP from appending a newline to the file each time it is saved.
36058    $s1 =~ s/\r$//;
36059    $s1 =~ s/\s+$//;
36060   # make line terminators uniform
36061    if ($qs{note} ne 'm') {
36062        $s1 =~ s/\r?\n/\n/g;
36063        open(my $CE,">",$fil);
36064        binmode $CE;
36065        print $CE $s1;
36066        close $CE;
36067        $s2='<span class="positive">File saved successfully</span>';
36068        &optionFilesReload();
36069    } else {      # to take actions on a mailfile
36070         $s1 =~ s/([^\r])\n/$1\r\n/go;
36071         $s1 .= "\r\n";
36072         my $action = $qs{fileaction};
36073         if ($action eq '1') {    # resend
36074             $s1 = "\r\n" . $s1;
36075             my $rfil = $fil;
36076             $rfil =~ s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$resendmail$2/i;
36077             my ($to) = $s1 =~ /\nX-Assp-Intended-For:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36078             ($to) = $s1 =~ /\nto:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $to;
36079             my ($from) = $s1 =~ /\nX-Assp-Envelope-From:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36080             ($from) = $s1 =~ /\nfrom:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $from;
36081             $s1 =~ s/^\r\n//o;
36082             $s2='';
36083             if (! $to ) {
36084                 $s2 .= '<br />' if $s2;
36085                 $s2 .= '<span class="negative">!!! no addresses found in X-Assp-Intended-For: or TO: header line - please check !!!</span>';
36086             }
36087             if (! $from ) {
36088                 $s2 .= '<br />' if $s2;
36089                 $s2 .= '<span class="negative">!!! no addresses found in X-Assp-Envelope-From: or FROM: header line - please check !!!</span>';
36090             }
36091             if ((! $nolocalDomains && ! (localmail($to) or localmail($from)))) {
36092                 $s2 .= '<br />' if $s2;
36093                 $s2 .= '<span class="negative">!!! no local addresses found in X-Assp-Intended-For: or TO: header line - please check !!!</span>'
36094                     unless localmail($to);
36095                 $s2 .= '<br />' if $s2 =~ /span>$/o;
36096                 $s2 .= '<span class="negative">!!! no local addresses found in X-Assp-Envelope-From: or FROM: header line - please check !!!</span>'
36097                     unless localmail($from);
36098             }
36099             if (! $s2) {
36100                 if (open(my $CE,">",$rfil)) {
36101                     binmode $CE;
36102                     print $CE $s1;
36103                     close $CE;
36104                     $s2 .= '<span class="positive">File copied to resendmail folder</span>';
36105                     mlog(0,"info: request to create file: $rfil");
36106                     $nextResendMail = $nextResendMail < time + 3 ? $nextResendMail: time + 3;
36107                 } else {
36108                     $s2 .= '<span class="negative">unable to create file in resendmail folder - $!</span>';
36109                     mlog(0,"error: unable to create file in resendmail folder - $!");
36110                 }
36111             }
36112         } elsif ($action eq '2') {    # save
36113             if (open($CE,">",$fil)) {
36114                 binmode $CE;
36115                 print $CE $s1;
36116                 close $CE;
36117                 $s2='<span class="positive">File saved successfully</span>';
36118             } else {
36119                 $s2='<span class="negative">unable to save file - $!</span>';
36120                 mlog(0,"error: unable to save file - $!");
36121             }
36122         } elsif ($action eq '3') {    # copy to notspam
36123             my $rfil = $fil;
36124             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$notspamlog$2/i;
36125             if (open($CE,">",$rfil)) {
36126                 binmode $CE;
36127                 print $CE $s1;
36128                 close $CE;
36129                 $s2='<span class="positive">File copied to notspamlog folder</span>';
36130                 mlog(0,"info: request to create file: $rfil");
36131             } else {
36132                 $s2='<span class="negative">unable to create file in notspamlog folder - $!</span>';
36133                 mlog(0,"error: unable to create file in notspamlog folder - $!");
36134             }
36135         } elsif ($action eq '4') {    # copy to spam
36136             my $rfil = $fil;
36137             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$spamlog$2/i;
36138             if (open($CE,">",$rfil)) {
36139                 binmode $CE;
36140                 print $CE $s1;
36141                 close $CE;
36142                 $s2='<span class="positive">File copied to spamlog folder</span>';
36143                 mlog(0,"info: request to create file: $rfil");
36144             } else {
36145                 $s2='<span class="negative">unable to create file in spamlog folder - $!</span>';
36146                 mlog(0,"error: unable to create file in spamlog folder - $!");
36147             }
36148         } elsif ($action eq '5') {    # incomingOkMail
36149             my $rfil = $fil;
36150             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$incomingOkMail$2/i;
36151             if (open($CE,">",$rfil)) {
36152                 binmode $CE;
36153                 print $CE $s1;
36154                 close $CE;
36155                 $s2='<span class="positive">File copied to incomingOkMail folder</span>';
36156                 mlog(0,"info: request to create file: $rfil");
36157             } else {
36158                 $s2='<span class="negative">unable to create file in incomingOkMail folder - $!</span>';
36159                 mlog(0,"error: unable to create file in incomingOkMail folder - $!");
36160             }
36161         } elsif ($action eq '6') {    # viruslog
36162             my $rfil = $fil;
36163             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$viruslog$2/i;
36164             if (open($CE,">",$rfil)) {
36165                 binmode $CE;
36166                 print $CE $s1;
36167                 close $CE;
36168                 $s2='<span class="positive">File copied to viruslog folder</span>';
36169                 mlog(0,"info: request to create file: $rfil");
36170             } else {
36171                 $s2='<span class="negative">unable to create file in viruslog folder - $!</span>';
36172                 mlog(0,"error: unable to create file in viruslog folder - $!");
36173             }
36174 		} elsif ($action eq '7') {    # correctedspam
36175             my $rfil = $fil;
36176             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$correctedspam$2/i;
36177             if (open($CE,">",$rfil)) {
36178                 binmode $CE;
36179                 print $CE $s1;
36180                 close $CE;
36181                 $s2='<span class="positive">File copied to correctedspam folder</span>';
36182                 mlog(0,"info: request to create file: $rfil");
36183
36184                 my ($to) = $s1 =~ /\nX-Assp-Intended-For:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36185                 ($to) = $s1 =~ /\nto:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $to;
36186                 my ($from) = $s1 =~ /\nX-Assp-Envelope-From:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36187                 ($from) = $s1 =~ /\nfrom:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $from;
36188                 if (   ($EmailErrorsModifyWhite == 1 )
36189                     && $to
36190                     && &localmail($to)
36191                     && $from && lc $from ne 'assp <>'
36192                     && !&localmail($from)
36193
36194                    )
36195                 {
36196                     my $dfh = rand(1000);
36197                     $Con{$dfh}->{reporttype} = 0;
36198                     $Con{$dfh}->{mailfrom} = $EmailAdminReportsTo || $WebIP{$ActWebSess}->{user}.'@'.$myName;
36199                     $Con{$dfh}->{header} = $s1;
36200                     for my $addr (&ListReportGetAddr($dfh)) {   # process the addresses
36201                         &ListReportExec($addr,$Con{$dfh});
36202                     }
36203                     $Con{$dfh}->{report} =~ s/^(?:\s|\r|\n)+//;
36204                     $Con{$dfh}->{report} =~ s/\r?\n/<br \/>/gos;
36205                     $s2 .= '<br />'.$Con{$dfh}->{report};
36206                     delete $Con{$dfh};
36207
36208                 }
36209                 if ( $EmailErrorsModifyPersBlack
36210                     && $to
36211                     && &localmail($to)
36212                     && $from && lc $from ne 'assp <>'
36213                     && !&localmail($from)
36214
36215                    )
36216                 {
36217                     my $dfh = rand(1000);
36218                     $Con{$dfh}->{reporttype} = 16;
36219                     $Con{$dfh}->{mailfrom} = $EmailAdminReportsTo || $WebIP{$ActWebSess}->{user}.'@'.$myName;
36220                     $Con{$dfh}->{header} = $s1;
36221                     for my $addr (&ListReportGetAddr($dfh)) {
36222                         next if $addr =~ /reportpersblack/;
36223                         &ListReportExec($addr,$Con{$dfh});
36224                     }
36225                     $Con{$dfh}->{report} =~ s/^(?:\s|\r|\n)+//;
36226                     $Con{$dfh}->{report} =~ s/\r?\n/<br \/>/gos;
36227                     $s2 .= '<br />'.$Con{$dfh}->{report};
36228                     delete $Con{$dfh};
36229
36230                 }
36231             } else {
36232                 $s2='<span class="negative">unable to create file in correctedspam folder - $!</span>';
36233                 mlog(0,"error: unable to create file in correctedspam folder - $!");
36234             }
36235        } elsif ($action eq '9') {    # discarded
36236             my $rfil = $fil;
36237             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$discarded$2/i;
36238             if (open($CE,">",$rfil)) {
36239                 binmode $CE;
36240                 print $CE $s1;
36241                 close $CE;
36242                 $s2='<span class="positive">File copied to discarded folder</span>';
36243                 mlog(0,"info: request to create file: $rfil");
36244             } else {
36245                 $s2='<span class="negative">unable to create file in discarded folder - $!</span>';
36246                 mlog(0,"error: unable to create file in discarded folder - $!");
36247             }
36248
36249         } elsif ($action eq '8') {    # correctednotspam
36250             my $rfil = $fil;
36251             $rfil =~s/^(\Q$base\E\/).+(\/.+$maillogExt)$/$1$correctednotspam$2/i;
36252             if (open($CE,">",$rfil)) {
36253                 binmode $CE;
36254                 print $CE $s1;
36255                 close $CE;
36256                 $s2='<span class="positive">File copied to correctednotspam folder</span>';
36257                 mlog(0,"info: request to create file: $rfil");
36258
36259                 my ($to) = $s1 =~ /\nX-Assp-Intended-For:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36260                 ($to) = $s1 =~ /\nto:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $to;
36261                 my ($from) = $s1 =~ /\nX-Assp-Envelope-From:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio;
36262                 ($from) = $s1 =~ /\nfrom:[^\<]*?<?($EmailAdrRe\@$EmailDomainRe)>?/sio unless $from;
36263                 if (   ($EmailErrorsModifyWhite == 1 || $EmailErrorsModifyNoP == 1)
36264                     && $to
36265                     && &localmail($to)
36266                     && $from
36267                     && lc $from ne 'assp <>'
36268                     && !&localmail($from)
36269
36270                    )
36271                 {
36272                     my $dfh = rand(1000);
36273                     $Con{$dfh}->{reporttype} = 1;
36274                     $Con{$dfh}->{mailfrom} = $EmailAdminReportsTo || $WebIP{$ActWebSess}->{user}.'@'.$myName;
36275                     $Con{$dfh}->{header} = $s1;
36276                     for my $addr (&ListReportGetAddr($dfh)) {   # process the addresses
36277                         &ListReportExec($addr,$Con{$dfh});
36278                     }
36279                     $Con{$dfh}->{report} =~ s/^(?:\s|\r|\n)+//;
36280                     $Con{$dfh}->{report} =~ s/\r?\n/<br \/>/gos;
36281                     $s2 .= '<br />'.$Con{$dfh}->{report};
36282                     delete $Con{$dfh};
36283                     mlog( 0, "info: possible noprocessing and/or whitelist entrys added on File copied to correctednotspam folder" )
36284                       if $MaintenanceLog;
36285                 }
36286                 if ( $EmailErrorsModifyPersBlack
36287                     && $to
36288                     && &localmail($to)
36289                     && $from && lc $from ne 'assp <>'
36290                     && !&localmail($from)
36291
36292                    )
36293                 {
36294                     my $dfh = rand(1000);
36295                     $Con{$dfh}->{reporttype} = 17;
36296                     $Con{$dfh}->{mailfrom} = $EmailAdminReportsTo || $WebIP{$ActWebSess}->{user}.'@'.$myName;
36297                     $Con{$dfh}->{header} = $s1;
36298                     for my $addr (&ListReportGetAddr($dfh)) {
36299                         next if $addr =~ /reportpersblack/;
36300                         &ListReportExec($addr,$Con{$dfh});
36301                     }
36302                     $Con{$dfh}->{report} =~ s/^(?:\s|\r|\n)+//;
36303                     $Con{$dfh}->{report} =~ s/\r?\n/<br \/>/gos;
36304                     $s2 .= '<br />'.$Con{$dfh}->{report};
36305                     delete $Con{$dfh};
36306
36307                 }
36308             } else {
36309                 $s2='<span class="negative">unable to create file in correctednotspam folder - $!</span>';
36310                 mlog(0,"error: unable to create file in correctednotspam folder - $!");
36311             }
36312         }
36313         $qs{fileaction} = '0';
36314    }
36315   }
36316  }
36317
36318
36319
36320  if(open($CE,"<","$fil")) {
36321   local $/;
36322   $s1=<$CE>;
36323   # make line terminators uniform
36324   $s1=~s/(?:\r?\n|\r)/\n/g;
36325   $s1=encodeHTMLEntities($s1);
36326   close $CE;
36327  }
36328  else {
36329   $s2='<span class="negative">'.ucfirst($!).'</span>';
36330  }
36331  $htmlfil = ($fil && $LogCharset && $LogCharset !~ /^utf-?8/i) ? Encode::encode('UTF-8', Encode::decode($LogCharset,$fil)) : $fil;
36332  if (-e $fil) {
36333      if($qs{note} eq '8') {
36334          $editButtons='<div><input type="button" value="Close" onclick="javascript:window.close();"/></div>';
36335      } elsif ($qs{note} eq 'm') {
36336          if ($s1 !~ /\n\.\n+$/) {
36337              $note .= "";
36338          }
36339          $note .= '</div>';
36340
36341          $editButtons="
36342 <div style=\"align: left\">
36343  <div class=\"shadow\">
36344   <div class=\"option\">
36345    <div class=\"optionValue\">
36346     <select size=\"1\" name=\"fileaction\">" .
36347      $option . "
36348     </select>
36349    </div>
36350   </div>
36351  </div>
36352 </div>
36353 &nbsp;&nbsp;";
36354
36355          $editButtons .='<input type="submit" name="B1" value="Do It!" />&nbsp;&nbsp;<input type="submit" name="B1" value="Delete file" onclick="return confirmDelete(\''.$fil.'\');"/>';
36356
36357          my $nf = $fil;
36358          $nf =~ s{([^a-zA-Z0-9])}{sprintf("%%%02X", ord($1))}eog;
36359
36360          $editButtons .='&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Close" onclick="javascript:window.close();"/>';
36361      } else {
36362          $editButtons='<div><input type="submit" name="B1" value="Save changes" />&nbsp;<input type="submit" name="B1" value="Delete file" onclick="return confirmDelete(\''.$fil.'\');"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Close" onclick="javascript:window.close();"/></div>';
36363      }
36364  }
36365  else {
36366   $s2='<div class="text"><span class="positive">File deleted</span></div>' if $qs{B1}=~/delete/i;
36367   $editButtons='<div><input type="submit" name="B1" value="Save changes" />&nbsp;<input type="submit" name="B1" value="Delete file" disabled="disabled" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Close" onclick="javascript:window.close();"/></div>';
36368
36369  }
36370 }
36371 return <<EOT;
36372$headerHTTP
36373
36374<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
36375<head>
36376  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
36377  <title>$currentPage ASSP File Editor ($myName $fil)</title>
36378  <link rel=\"stylesheet\" href=\"get?file=images/editor.css\" type=\"text/css\" />
36379    <script type="text/javascript">
36380//<![CDATA[
36381	// Javascript code and layout adapted from TinyMCE
36382	// http://tinymce.moxiecode.com/
36383    <!--
36384        var wHeight=0, wWidth=0, owHeight=0, owWidth=0;
36385
36386        function resizeInputs() {
36387	    var contents = document.getElementById('contents');
36388	    var notebox = document.getElementById('notebox');
36389		//alert(el2.offsetHeight);
36390
36391	    if (!isIE()) {
36392	    	 //alert(navigator.userAgent);
36393	         wHeight = self.innerHeight - (notebox.offsetHeight+150);
36394	         wWidth = self.innerWidth - 50;
36395	    } else {
36396			 //alert(navigator.userAgent);
36397	         wHeight = document.body.clientHeight - (notebox.offsetHeight+150);
36398	         wWidth = document.body.clientWidth - 50;
36399	    }
36400
36401	    contents.style.height = Math.abs(wHeight) + 'px';
36402	    contents.style.width  = Math.abs(wWidth) + 'px';
36403	    container.style.height = Math.abs(wHeight - 18) + 'px';
36404        }
36405
36406	function isIE () {
36407		var check,agent;
36408		check=/MSIE/i;
36409		agent=navigator.userAgent;
36410		if(check.test(agent)) {
36411			return true;
36412		}
36413		else {
36414			return false;
36415		}
36416	}
36417
36418
36419	function confirmDelete(FileName)
36420	{
36421		var strmsg ="Are you sure you wish to delete: \\n" + FileName  + "\\n This action cannot be undone";
36422		var agree=confirm( strmsg );
36423		if (agree)
36424			return true;
36425		else
36426			return false;
36427	}
36428
36429function popFileEditor(filename,note)
36430{
36431  var height = (note == 0) ? 500 : (note == \'m\') ? 580 : 550;
36432  newwindow=window.open(
36433    \'edit?file=\'+filename+\'&note=\'+note,
36434    \'FileEditorM\',
36435    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
36436  );
36437  	// this puts focus on the popup window if we open a new popup without closing the old one.
36438  	if (window.focus) {newwindow.focus()}
36439  	return false;
36440}
36441function popSyncEditor(cfgParm)
36442
36443{
36444  setAnchor(cfgParm);
36445  var height = 400;
36446  newwindow=window.open(
36447    \'syncedit?cfgparm=\'+cfgParm,
36448    \'SyncEditor\',
36449    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
36450  );
36451  	// this puts focus on the popup window if we open a new popup without closing the old one.
36452  	if (window.focus) {newwindow.focus()}
36453  	return false;
36454}
36455
36456function remember()
36457{
36458  var height =  580;
36459  newwindow=window.open(
36460    \'remember\',
36461    \'rememberMe\',
36462    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
36463  );
36464  	// this puts focus on the popup window if we open a new popup without closing the old one.
36465  	if (window.focus) {newwindow.focus()}
36466  	return false;
36467}
36468function getInput() { return document.getElementById("contents").value; }
36469function setOutput(string) {document.getElementById("contents").value=string; }
36470
36471function replaceIt() { try {
36472var findText = document.getElementById("find").value;
36473var replaceText = document.getElementById("replace").value;
36474setOutput(getInput().replace(eval("/"+findText+"/ig"), replaceText));
36475} catch(e){}}
36476
36477      //-->
36478    //]]>
36479    </script>
36480<style type="text/css">
36481#container
36482{
36483	width: 40px;
36484	color: Gray;
36485	font-family: Courier New;
36486	font-size: 14px;
36487	float: left;clear: left;
36488	overflow: hidden;
36489        position: relative;
36490        top: 2px;
36491}
36492#divlines
36493{
36494	position: absolute;
36495}
36496</style>
36497</head>
36498<body onresize="resizeInputs();" onload="resizeInputs();" style="overflow:hidden;" onmouseover="this.focus();" ondblclick="this.select();">
36499    <div class="content">
36500      <form action="" method="post">
36501        <span style="float: left;"><a href="javascript:void(0);" onclick="remember();return false;"><img height=12 width=12 src="$wikiinfo" alt="open the remember me window"/></a>&nbsp; Contents of $htmlfil</span><br/><hr /><br />
36502        <div id="message" style="float: right">$s2</div>
36503        <br style="clear: both;" />
36504        <span style="align: left">Replace: <input type="text" id="find" size="20" /> with <input type="text" id="replace" size="20" /> <input type="button" value="Replace" onclick="replaceIt();" /></span>
36505<div>
36506<div id="container">
36507<div id="divlines">
36508</div>
36509</div>
36510        <textarea id="contents" name="contents" rows="18" style="max-height:75%;width:100%;overflow:scroll;align: right;font-size: 14px; font-family: 'Courier New',Courier,monospace; " wrap="off">$s1
36511        </textarea>
36512<script type="text/javascript">
36513var lines = document.getElementById("divlines");
36514var txtArea = document.getElementById("contents");
36515var nLines;
36516window.onload = function() {
36517    $noLineNum
36518    resizeInputs();
36519    refreshlines();
36520    txtArea.onscroll = function () {
36521        lines.style.top = -(txtArea.scrollTop) + "px";
36522        return true;
36523    }
36524    txtArea.onkeyup = function () {
36525
36526      var keycode;
36527      if (window.event) keycode = window.event.keyCode;
36528      else if (e) keycode = e.which;
36529      else return true;
36530
36531      if (keycode == 13)
36532         {
36533         nLines++;
36534         lines.innerHTML = lines.innerHTML + nLines + "." + "<br />";
36535         return false;
36536         }
36537      else
36538         {
36539         return true;
36540         }
36541    }
36542}
36543
36544function refreshlines() {
36545    $noLineNum
36546    nLines = txtArea.value.split("\\n").length;
36547    var innerlines = "";
36548    for (i=1; i<=nLines; i++) {
36549        innerlines = innerlines + i + "." + "<br />";
36550    }
36551    lines.innerHTML = innerlines;
36552    lines.style.top = -(txtArea.scrollTop) + "px";
36553}
36554</script>
36555
36556        $editButtons
36557      </form>
36558	<br />$note
36559</div>
36560    </div>
36561<script type="text/javascript">
36562if (!isIE()) {
36563    resizeInputs();
36564    refreshlines();
36565}
36566</script>
36567  </body>
36568</html>
36569
36570EOT
36571
36572}
36573
36574sub remember {
36575 return <<EOT;
36576$headerHTTP
36577
36578<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
36579<head>
36580  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
36581  <title>$currentPage ASSP remember me ($myName)</title>
36582  <link rel=\"stylesheet\" href=\"get?file=images/editor.css\" type=\"text/css\" />
36583</head>
36584<body onmouseover="this.focus();" ondblclick="this.select();">
36585    <div class="content">
36586      <form action="" method="post">
36587        <textarea  rows="10" style="max-height:25%;width:100%;overflow:scroll;align: right;font-size: 14px; font-family: 'Courier New',Courier,monospace; " wrap="on">
36588        </textarea>
36589        <textarea  rows="10" style="max-height:25%;width:100%;overflow:scroll;align: right;font-size: 14px; font-family: 'Courier New',Courier,monospace; " wrap="on">
36590        </textarea>
36591        <textarea  rows="10" style="max-height:25%;width:100%;overflow:scroll;align: right;font-size: 14px; font-family: 'Courier New',Courier,monospace; " wrap="on">
36592        </textarea>
36593        <textarea  rows="10" style="max-height:25%;width:100%;overflow:scroll;align: right;font-size: 14px; font-family: 'Courier New',Courier,monospace; " wrap="on">
36594        </textarea>
36595      </form>
36596    </div>
36597</body>
36598</html>
36599
36600EOT
36601}
36602
36603sub webConfig{
36604    my $r = '';
36605    my @tmp;
36606    &niceConfig();
36607    $ConfigChanged = 0;
36608
36609    # don't post partial data if somebody's browser's busted
36610    undef %qs unless $qs{theButton} || $qs{theButtonX};
36611    undef %qs if $qs{theButtonRefresh};
36612    my $counter = 0;
36613    foreach my $c (@ConfigArray) {
36614        if ( @{$c} == 5 ) {
36615
36616            # Is a header
36617            @tmp = @{$c};
36618            push( @tmp, "setupItem$counter" );
36619            $r .= $c->[3]->(@tmp);
36620            $counter++;
36621        } else {
36622
36623            # Is a variable
36624            $r .= $c->[3]->( @{$c} );
36625        }
36626    }
36627    if ($ConfigChanged) {
36628        SaveConfig();
36629        renderConfigHTML();
36630        PrintConfigDefaults();
36631    }
36632    my $regexp1 = "";
36633    my $regexp2 = "";
36634my $reload;
36635
36636$reload  = '<table class="textBox" style="width: 99%;">
36637 <tr><td class="noBorder" align="right">Panic Button:</td></tr>
36638<tr><td class="noBorder" align="right"><form action="quit" method="post"><input type="submit" value="Terminate Now!" /></form></td></tr>
36639</table>';
36640$reload  = '<table class="textBox" style="width: 99%;">
36641 <tr><td class="noBorder" align="center">Restart Button:</td><td class="noBorder" align="right">Panic Button:</td></tr>
36642<tr>  <td class="noBorder" align="center"><form action="autorestart" method="post"><input type="submit" value="Restart Now!" /></form></td><form action="quit" method="post"><td class="noBorder" align="right"><input type="submit" value="Terminate Now!" /></td></form></tr>
36643</table>' if $AutoRestartCmd;
36644
36645
36646
36647    $regexp1 =
36648"If Net::IP::Match::Regexp is installed  CIDR notation is allowed(182.82.10.0/24)."
36649      if !$CanMatchCIDR;
36650    $regexp2 =
36651"<br />If Net::IP::Match::Regexp is installed, Text after the range (and before a numbersign) will be accepted as comment which will be shown in a match (for example: 182.82.10.0/24 Yahoo Groups #comment not shown)."
36652      if !$CanMatchCIDR;
36653    $regexp1 = "CIDR notation is accepted (182.82.10.0/24)." if $CanMatchCIDR;
36654    $regexp2 =
36655"<br />Text after the range (and before a numbersign) will be accepted as comment to be shown in a match. For example:<br />182.82.10.0/24 Yahoo #comment to be removed"
36656      if $CanMatchCIDR;
36657    my $cidr = "";
36658
36659    $cidr =
36660"If Net::CIDR::Lite is installed, hyphenated ranges can be used (182.82.10.0-182.82.10.255)."
36661      if !$CanUseCIDRlite;
36662    $cidr = "Hyphenated ranges can be used (182.82.10.0-182.82.10.255)."
36663      if $CanUseCIDRlite;
36664 my $quit;
36665
36666
36667$quit  = '<table class="textBox" style="width: 99%;">
36668 <tr><td class="noBorder" align="left">Restart Button:</td><td class="noBorder" align="right">Panic Button:</td></tr>
36669<tr><td class="noBorder" align="left"><form action="autorestart" method="post"><input type="submit" value="Restart Now!" /></form></td><td class="noBorder" align="right"><form action="quit" method="post"><input type="submit" value="Terminate Now!" /></form></td></tr>
36670</table>';
36671
36672
36673
36674my $runas =  $AsAService ? ' (as service)' : $AsADaemon ? ' (as daemon)' : ' (console mode)';
36675$runas = ' (as secondary)' if $AsASecondary;
36676my $secondaryrunning;$secondaryrunning = " 'kill -PIPE $SecondaryPid' will terminate Secondary" if $SecondaryPid;
36677my $pathhint = $^O eq 'MSWin32' ? 'For defining any full filepathes, always use slashes ("/") not backslashes. For example: c:/assp/certs/server-key.pem !<br /><br />' : '';
36678    <<EOT;
36679$headerHTTP
36680$headerDTDTransitional
36681$headers
36682$footers
36683<div class="content">
36684
36685<form name="ASSPconfig" id="ASSPconfig" action="" method="post">
36686<div>
36687$r
36688</div>
36689<div class="rightButton">
36690  <input name="theButton" type="submit" value="Apply Changes" />
36691  <input name="theButtonRefresh" type="hidden" value="" />
36692  <input name="theButtonX" type="hidden" value="" />
36693</div>
36694<div class="textBox">
36695$asspWarnings<br />
36696</div>
36697<div class="textBox">
36698$pathhint
36699$lngmsg{'msg500018'}
36700IP ranges are defined as for example \'182.82.10.\'. $regexp1 $cidr $regexp2.
36701$lngmsg{'msg500019'}
36702
36703</div>
36704</form>
36705$reload
36706
36707<br />
36708$kudos
36709<br />
36710</div>
36711$footers
36712<script type="text/javascript">
36713<!--
36714  expand(0, 0);
36715  string = new String(document.location);
36716  string = string.substr(string.indexOf('#')+1);
36717  if(document.forms[0].length) {
36718    for(i = 0; i < document.forms[0].elements.length; i++) {
36719      if(string == document.forms[0].elements[i].name) {
36720        document.forms[0].elements[i].focus();
36721      }
36722    }
36723  }
36724  initAnchor('0');
36725// -->
36726</script>
36727</body></html>
36728EOT
36729}
36730
36731sub Donations {
36732    <<EOT;
36733$headerHTTP
36734$headerDTDTransitional
36735$headers
36736
36737<div class="content">
36738<h2>ASSP Kudos</h2>
36739<div class="note">
36740ASSP is here thanks to the following people:
36741
36742<br />
36743<table style="width: 99%;" class="textBox">
36744<tr>
36745<td class="note">John Hanna the founder and developer of ASSP's first versions.</td>
36746<td class="underline"></td>
36747</tr>
36748
36749<tr>
36750<td class="note">Fritz Borgstedt/Thomas Eckardt :  1.1.0 - 1.9.9 </td>
36751<td class="underline"></td>
36752</tr>
36753<tr>
36754<td class="note">Thomas Eckardt : ASSP Pro 2.0.0 - 2.2.2 </td>
36755<td class="underline"></td>
36756</tr>
36757<tr>
36758<td>&nbsp;</td>
36759<td></td>
36760</tr>
36761<tr>
36762<td colspan="2">
36763<div class="underline">Special thanks to......<br /><br />
36764&nbsp;&nbsp;  John Calvi : ASSP  version 1.0.12<br />
36765&nbsp;&nbsp;  Przemek Czerkas : SRS, Greylisting, Maillog Search.
36766&nbsp;&nbsp;  Robert Orso : LDAP functions.<br />
36767&nbsp;&nbsp;  AJ : web interface &amp; site.<br />
36768&nbsp;&nbsp;  Nigel Barling : SPF &amp; RBL.<br />
36769&nbsp;&nbsp;  Mark Pizzolato : SMTP Session Limits.<br />
36770&nbsp;&nbsp;  Craig Schmitt : SPF2.<br />
36771&nbsp;&nbsp;  J.R. Oldroyd : SSL support, IPv6 support, griplist/stats upload/download.<br /><br />
36772
36773&nbsp;&nbsp;  Wim Borghs, Doug Traylor, Lars Troen, Marco Tomasi,
36774&nbsp;&nbsp;  Andrew Macpherson, Marco Michelino, Matti Haack, Dave Emory
36775<br />
36776
36777</div>
36778</td>
36779</tr>
36780</table>
36781<br />
36782$kudos
36783<br />
36784</div>
36785$footers
36786</body></html>
36787EOT
36788}
36789
36790sub HTTPStrToTime {
36791    my $str = shift;
36792    if ( $str =~
36793/[SMTWF][a-z][a-z], (\d\d) ([JFMAJSOND][a-z][a-z]) (\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT/
36794      )
36795    {
36796        my %MoY =
36797          qw(Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec 12);
36798        return eval {
36799            my $t =
36800              Time::Local::timegm( $6, $5, $4, $1, $MoY{$2} - 1, $3 - 1900 );
36801            $t < 0 ? undef : $t;
36802        };
36803    } else {
36804        return;
36805    }
36806}
36807
36808sub GetFile {
36809    my $fil = $qs{file};
36810    my %mimeTypes;
36811    if ( $fil =~ /\.\./ ) {
36812        mlog( 0, "file path not allowed while getting file '$fil'" );
36813        return <<EOT;
36814HTTP/1.1 403 Forbidden
36815Content-type: text/html
36816
36817<html><body><h1>Forbidden</h1>
36818</body></html>
36819EOT
36820    }
36821
36822    if ( $fil !~ /^\Q$base\E/i ) {
36823        $fil = "$base/$fil";
36824    }
36825    if ( -e $fil ) {
36826        my $mtime;
36827        if ( defined( $mtime = $head{'if-modified-since'} ) ) {
36828            if ( defined( $mtime = HTTPStrToTime($mtime) ) ) {
36829                if ( $mtime >= [ stat($fil) ]->[9] ) {
36830                    return "HTTP/1.1 304 Not Modified\n\r\n\r";
36831                }
36832            }
36833        }
36834        if ( open( $FH, "<","$fil" ) ) {
36835            binmode $FH;
36836            local $/;
36837            my $s = <$FH>;
36838            close $FH;
36839            %mimeTypes = (
36840                'log|txt|pl' => 'text/plain',
36841                'htm|html'   => 'text/html',
36842                'css'        => 'text/css',
36843                'bmp'        => 'image/bmp',
36844                'gif'        => 'image/gif',
36845                'jpg|jpeg'   => 'image/jpeg',
36846                'png'        => 'image/png',
36847                'zip'        => 'application/zip',
36848                'sh'         => 'application/x-sh',
36849                'gz|gzip'    => 'application/x-gzip',
36850                'exe'        => 'application/octet-stream',
36851                'js'         => 'application/x-javascript'
36852            );
36853            my $ct = 'text/plain';    # default content-type
36854            foreach my $key ( keys %mimeTypes ) {
36855                $ct = $mimeTypes{$key} if $fil =~ /\.($key)$/i;
36856            }
36857            $mtime = [ stat($fil) ]->[9];
36858            $mtime = gmtime($mtime);
36859            $mtime =~
36860              s/(...) (...) +(\d+) (........) (....)/$1, $3 $2 $5 $4 GMT/;
36861            return <<EOT;
36862HTTP/1.1 200 OK
36863Content-type: $ct
36864Last-Modified: $mtime
36865
36866$s
36867EOT
36868        }
36869    }
36870    return <<EOT;
36871HTTP/1.1 404 Not Found
36872Content-type: text/html
36873
36874<html><body><h1>Not found</h1>
36875</body></html>
36876EOT
36877
36878}
36879
36880sub Shutdown {
36881my $title = "ASSP Shutdown/Restart";
36882my $title;$title = "Primary & Secondary Shutdown/Restart" if $AsASecondary;
36883my $restart;$restart = '<tr>
36884<td style="background-color: white; padding: 0px;"><form action="restartsecondary" method="post">
36885<table style="background-color: white; border-width: 0px; width: 600px">
36886  <tr><td style="background-color: white; padding: 0px;" align="center">Secondary:</td></tr>
36887  <tr><td style="background-color: white; padding: 0px;" align="center"><input type="submit" name="action" value="Restart Secondary ASSP Now!" /></td></tr>
36888</table>
36889</form>
36890</td>
36891</tr>' if $AutostartSecondary;
36892
36893
36894
36895    <<EOT;
36896$headerHTTP
36897$headerDTDTransitional
36898$headers
36899
36900<div class="content">
36901<h2>$title</h2>
36902<div class="note">
36903Note: It's possible to restart, if ASSP runs as a service or in a script that restarts it after it stops or AutoRestartCmd is configured<br />
36904The following AutoRestartCmd will be started in OS-shell, if ASSP does not run as a service:<br /><b><font color=red>'$AutoRestartCmd'</font></b>
36905</div>
36906
36907
36908
36909<br />
36910<table style="background-color: white; border-width: 0px; width: 600px">
36911<tr>
36912<td style="background-color: white; padding: 0px;">
36913<iframe src="/shutdown_frame" width="100%" height="300" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
36914</td>
36915</tr>
36916$restart
36917</table>
36918
36919</div>
36920
36921
36922
36923$footers
36924</body></html>
36925EOT
36926}
36927
36928
36929sub ShutdownFrame {
36930    my $action = $qs{action};
36931
36932    my ( $s1, $s2, $editButtons, $query, $refresh );
36933    my $shutdownDelay = 2;
36934
36935    my $timerJS = '
36936<script type="text/javascript">
36937  var ns=(navigator.appName.indexOf("Netscape")!=-1);
36938  var timerVal=parseInt(ns ? document.getElementById("myTimer1").childNodes[0].nodeValue : myTimer1.innerText);
36939  function countDown() {
36940    if (isNaN(timerVal)==0 && timerVal>=0) {
36941      if (ns) {
36942        document.getElementById("myTimer1").childNodes[0].nodeValue=timerVal--;
36943      } else {
36944        myTimer1.innerText=timerVal--;
36945      }
36946      setTimeout("countDown()",1000);
36947    }
36948  }
36949  countDown();
36950</script>';
36951    if ( $action =~ /abort/i ) {
36952        $shuttingDown = 0;
36953        $refresh      = 3;
36954        $s1           = 'Shutdown request aborted';
36955        $editButtons =
36956'<input type="submit" name="action" value=" Proceed " disabled="disabled" />&nbsp;
36957<input type="submit" name="action" value=" Abort " disabled="disabled" />';
36958        $doShutdown = 0;
36959        $query      = '?nocache';
36960        mlog( 0,
36961"shutdown/restart process aborted per admin request; SMTP session count:$smtpConcurrentSessions"
36962        );
36963    } elsif ( $action =~ /proceed/i || $shuttingDown ) {
36964        $shuttingDown = 1;
36965        $refresh = $smtpConcurrentSessions > 0 ? 2 : 60;
36966        $s1 =
36967          $smtpConcurrentSessions > 0
36968          ? 'Waiting for '
36969          . needEs( $smtpConcurrentSessions, ' SMTP session', 's' )
36970          . ' to finish ...'
36971          : "Shutdown in progress, please wait: <span id=\"myTimer1\">$refresh</span>s$timerJS";
36972        $editButtons =
36973'<input type="submit" name="action" value=" Proceed " disabled="disabled" />&nbsp;
36974<input type="submit" name="action" value=" Abort "'
36975          . ( $smtpConcurrentSessions > 0 ? '' : ' disabled="disabled"' ) . ' />
36976'
36977          . (
36978            $refresh > 1
36979            ? ''
36980            : '&nbsp;<input type="button" name="action" value=" View " onclick="javascript:window.open(\'shutdown_list?\',\'SMTP_Connections\',\'width=600,height=600,toolbar=no,menubar=no,location=no,personalbar=no,scrollbars=yes,status=no,directories=no,resizable=yes\')" />'
36981          ) . '';
36982
36983        $doShutdown = $smtpConcurrentSessions > 0 ? 0 : time + $shutdownDelay;
36984        $query = $smtpConcurrentSessions > 0 ? '?nocache' : '?action=Success';
36985        mlog( 0,
36986"shutdown/restart process initiated per admin request; SMTP session count:$smtpConcurrentSessions"
36987        ) if $action =~ /proceed/i;
36988    } elsif ( $action =~ /success/i ) {
36989        $refresh = 3;
36990        $s1      = 'ASSP restarted successfully.';
36991        $editButtons =
36992'<input type="submit" name="action" value=" Proceed Shutdown " disabled="disabled" />&nbsp;
36993<input type="submit" name="action" value=" Abort " disabled="disabled" />' if  !$AutoRestartCmd;
36994        $editButtons =
36995'<input type="submit" name="action" value=" Proceed Restart " disabled="disabled" />&nbsp;
36996<input type="submit" name="action" value=" Abort " disabled="disabled" />' if  $AutoRestartCmd;
36997        $doShutdown = 0;
36998        $query      = '?nocache';
36999    } else {
37000        $refresh = 1;
37001
37002        $s1 =
37003          $smtpConcurrentSessions > 0
37004          ? ( $smtpConcurrentSessions > 1 ? 'There are ' : 'There is ' )
37005          . needEs( $smtpConcurrentSessions, ' SMTP session', 's' )
37006          . ' active'
37007          : 'There are no active SMTP sessions';
37008        $editButtons =
37009          '<input type="submit" name="action" value=" Proceed Shutdown " />&nbsp;
37010<input type="submit" name="action" value=" Abort " disabled="disabled" />&nbsp;
37011<input type="button" name="action" value=" View " onclick="javascript:window.open(\'shutdown_list?\',\'SMTP_Connections\',\'width=800,height=600,toolbar=no,menubar=no,location=no,personalbar=no,scrollbars=yes,status=no,directories=no,resizable=yes\')" />' if  !$AutoRestartCmd;
37012        $editButtons =
37013          '<input type="submit" name="action" value=" Proceed Restart " />&nbsp;
37014<input type="submit" name="action" value=" Abort " disabled="disabled" />&nbsp;
37015<input type="button" name="action" value=" View " onclick="javascript:window.open(\'shutdown_list?\',\'SMTP_Connections\',\'width=800,height=600,toolbar=no,menubar=no,location=no,personalbar=no,scrollbars=yes,status=no,directories=no,resizable=yes\')" />' if  $AutoRestartCmd;
37016
37017		$s1 = $editButtons = '' if $AsASecondary;
37018        $doShutdown = 0;
37019        $query      = '?nocache';
37020    }
37021
37022my $quit;
37023$quit  = '<form action="quit" method="post">
37024<table class="textBox" style="width: 99%;">
37025  <tr><td class="noBorder" align="center">Panic Button:</td></tr>
37026  <tr><td class="noBorder" align="center"><input type="submit" name="action" value="Terminate ASSP Now!" /></td></tr>
37027</table>
37028</form>' unless $AsAService;
37029$quit  = '<form action="autorestart" method="post">
37030<table class="textBox" style="width: 99%;">
37031  <tr><td class="noBorder" align="center">Panic Button:</td></tr>
37032  <tr><td class="noBorder" align="center"><input name="action" type="submit" value="Restart ASSP Now!" /></td></tr>
37033</table>
37034</form>' if $AutoRestartCmd;
37035
37036
37037
37038
37039my $bod = $action=~/success/i ? '<body onload="top.location.href=\'/#\'">' : '<body>' ;
37040    <<EOT;
37041$headerHTTP
37042$headerDTDTransitional
37043<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
37044<head>
37045  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
37046  <meta http-equiv="refresh" content="$refresh;url=/shutdown_frame$query" />
37047  <title>ASSP ($myName)</title>
37048  <link rel=\"stylesheet\" href=\"get?file=images/shutdown.css\" type=\"text/css\" />
37049</head>
37050$bod
37051<div class="content">
37052<form action="" method="get">
37053  <table class="textBox">
37054    <tr>
37055      <td class="noBorder" nowrap>
37056        $editButtons&nbsp;&nbsp;&nbsp;$s1
37057      </td>
37058    </tr>
37059
37060  </table>
37061</form>
37062</div>
37063<div class="content">
37064
37065$quit
37066</div>
37067</body></html>
37068EOT
37069
37070}
37071sub ShutdownListx {
37072    my $action = $qs{action};
37073    my ( $s1, $s2, $editButtons, $query, $refresh );
37074    my $rowclass;
37075    my $shutdownDelay = 2;
37076
37077    my $timerJS = '
37078<script type="text/javascript">
37079  var ns=(navigator.appName.indexOf("Netscape")!=-1);
37080  var timerVal=parseInt(ns ? document.getElementById("myTimer1").childNodes[0].nodeValue : myTimer1.innerText);
37081  function countDown() {
37082    if (isNaN(timerVal)==0 && timerVal>=0) {
37083      if (ns) {
37084        document.getElementById("myTimer1").childNodes[0].nodeValue=timerVal--;
37085      } else {
37086        myTimer1.innerText=timerVal--;
37087      }
37088      setTimeout("countDown()",1000);
37089    }
37090  }
37091  countDown();
37092</script>';
37093    $refresh = 1;
37094    $query   = '?nocache';
37095    $s1      = '<div style="float: left;">';
37096    $s1 .=
37097      $smtpConcurrentSessions > 0
37098      ? ( $smtpConcurrentSessions > 1 ? 'There are ' : 'There is ' )
37099      . needEs( $smtpConcurrentSessions, ' SMTP session', 's' )
37100      . ' active.</div>'
37101      : 'There are no active SMTP sessions.' . '</div>';
37102    $s1 .= '<div style="float: right;">' . localtime() . '</div><br />';
37103
37104    $s2 =
37105"<tr><td class=\"conTabletitle\">#</td><td class=\"conTabletitle\">Remote IP</td><td class=\"conTabletitle\">HELO</td><td class=\"conTabletitle\">From</td><td class=\"conTabletitle\">Rcpt</td><td class=\"conTabletitle\">Relaying</td><td class=\"conTabletitle\">SPAM</td><td class=\"conTabletitle\">Bytes</td><td class=\"conTabletitle\">Duration</td><td class=\"conTabletitle\">Inactive</td></tr>";
37106
37107    my $tmpTimeNow = time();
37108
37109    my @tmpConKeys = keys(%Con);
37110    my @tmpConSortedKeys =
37111      sort { $Con{$a}->{timestart} <=> $Con{$b}->{timestart} } @tmpConKeys;
37112    my $tmpCount = 0;
37113    foreach my $key (@tmpConSortedKeys) {
37114        if ( $Con{$key}->{ip} ) {
37115            $tmpCount++;
37116            my $tmpDuration = $tmpTimeNow - $Con{$key}->{timestart};
37117            my $tmpInactive = $tmpTimeNow - $Con{$key}->{timelast};
37118            if ( $tmpCount % 2 == 1 ) {
37119                $rowclass = "\n<tr>";
37120            } else {
37121                $rowclass = "\n<tr class=\"even\">";
37122            }
37123            $s2 .=
37124                $rowclass
37125              . '<td><b>'
37126              . ($tmpCount)
37127              . '</b></td><td>'
37128              . $Con{$key}->{ip}
37129              . '</td><td>'
37130              . substr( $Con{$key}->{helo}, 0, 25 )
37131              . '</td><td>'
37132              . substr( $Con{$key}->{mailfrom}, 0, 30 )
37133              . '</td><td>'
37134              . substr( $Con{$key}->{rcpt}, 0, 30 )
37135              . '</td><td>'
37136              . $Con{$key}->{relayok}
37137              . '</td><td>'
37138              . $Con{$key}->{spamfound}
37139              . '</td><td>'
37140              . $Con{$key}->{maillength}
37141              . '</td><td>'
37142              . $tmpDuration
37143              . '</td><td>'
37144              . $tmpInactive
37145              . '</td></tr>';
37146        }
37147    }
37148
37149    <<EOT;
37150$headerHTTP
37151$headerDTDTransitional
37152<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
37153<head>
37154  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
37155  <meta http-equiv="refresh" content="$refresh;url=/shutdown_list$query" />
37156  <title>ASSP ($myName)</title>
37157  <link rel=\"stylesheet\" href=\"get?file=images/assp.css\" type=\"text/css\" />
37158</head>
37159<body>
37160<div >
37161<div style="float: right"><input type="button" value="Close" onclick="javascript:window.close();"/>
37162
37163</div>
37164<h2>SMTP Connections List</h2>
37165$s1
37166<table cellspacing="0" id="conTable">
37167$s2
37168</table>
37169<br />
37170</div>
37171</body></html>
37172EOT
37173}
37174
37175
37176sub ShutdownList {
37177
37178    my $action = $qs{action};
37179    my ( $s1, $s2, $editButtons, $query, $refresh );
37180    my $nocache = $qs{nocache};
37181    my $showcolor = $qs{showcolor} ? 1 : 0;
37182    my $forceRefresh = $qs{forceRefresh};
37183    my $rowclass;
37184    my $shutdownDelay = 2;
37185    my %conperwo = ();
37186    my $key;
37187
37188 $query  = '?nocache='.time;
37189 $query .= '&forceRefresh=1' if $forceRefresh;
37190 my $focusJS = '
37191<script type="text/javascript">
37192 Timer=setTimeout("newTimer();", 5000);
37193 ';
37194 $focusJS .= $forceRefresh ? '
37195 var Run = 1;
37196 function tStop () {
37197    Run = 1;
37198 }
37199 '
37200 :
37201 '
37202 var Run = 1;
37203 function tStop () {
37204    Run = 0;
37205    Timer=setTimeout("noop();", 3000);
37206 }
37207 ';
37208
37209 $focusJS .= '
37210 function noop () {}
37211 var Run2 = 1;
37212 var linkBG;
37213 var showcolor = '.$showcolor.';
37214//noprint
37215 setcolorbutton();
37216 function startstop() {
37217     Run2 = (Run2 == 1) ? 0 : 1;
37218     document.getElementById(\'stasto\').value = (Run2 == 1) ? "Stop" : "Start";
37219 }
37220 function tStart () {
37221    Run = 1;
37222 }
37223 function newTimer() {
37224   if (Run == 1 && Run2 == 1) {window.location.reload();}
37225   Timer=setTimeout("newTimer();", 5000);
37226 }
37227//endnoprint
37228function popAddressAction(address)
37229{
37230  var height = 500 ;
37231  var link = address ? \'?address=\'+address : \'\';
37232  newwindow=window.open(
37233    \'addraction\'+link,
37234    \'AddressAction\',
37235    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
37236  );
37237  	// this puts focus on the popup window if we open a new popup without closing the old one.
37238  	if (window.focus) {newwindow.focus()}
37239  	return false;
37240}
37241
37242function popIPAction(ip)
37243{
37244  var height = 500 ;
37245  var link = ip ? \'?ip=\'+ip : \'\';
37246  newwindow=window.open(
37247    \'ipaction\'+link,
37248    \'IPAction\',
37249    \'width=720,height=\'+height+\',overflow=scroll,toolbar=yes,menubar=yes,location=no,personalbar=yes,scrollbars=yes,status=no,directories=no,resizable=yes\'
37250  );
37251  	// this puts focus on the popup window if we open a new popup without closing the old one.
37252  	if (window.focus) {newwindow.focus()}
37253  	return false;
37254}
37255//noprint
37256function switchcolor () {
37257    showcolor = (showcolor == 1) ? 0 : 1;
37258    setcolorbutton();
37259    setcolor();
37260}
37261
37262function setcolor () {
37263    if (showcolor == 1) {
37264        window.location.href=\'./shutdown_list?nocache='.$nocache.'&forceRefresh='.$forceRefresh.'&showcolor=1\';
37265    } else {
37266        window.location.href=\'./shutdown_list?nocache='.$nocache.'&forceRefresh='.$forceRefresh.'&showcolor=0\';
37267    }
37268}
37269
37270function setcolorbutton () {
37271    if (showcolor == 1) {
37272        document.getElementById(\'colorbutton\').value = "color-off";
37273    } else {
37274        document.getElementById(\'colorbutton\').value = "color-on";
37275    }
37276}
37277//endnoprint
37278</script>
37279';
37280    $refresh = 1;
37281    $query   = '?nocache';
37282    $s1      = '<div style="float: left;">';
37283    $s1 .=
37284      $smtpConcurrentSessions > 0
37285      ? ( $smtpConcurrentSessions > 1 ? 'There are ' : 'There is ' )
37286      . needEs( $smtpConcurrentSessions, ' SMTP session', 's' )
37287      . ' active.</div>'
37288      : 'There are no active SMTP sessions.' . '</div>';
37289    $s1 .= '<div style="float: right;">'  . '</div><br />';
37290
37291    $s2 =
37292            "<tr><td class=\"conTabletitle\"># TLS</td><td class=\"conTabletitle\">Remote IP</td><td class=\"conTabletitle\">HELO</td><td class=\"conTabletitle\">From</td><td class=\"conTabletitle\">Rcpt</td><td class=\"conTabletitle\">CMD</td><td class=\"conTabletitle\">RY/NP/WL</td><td class=\"conTabletitle\">SPAM</td><td class=\"conTabletitle\">Bytes</td><td class=\"conTabletitle\">Duration</td><td class=\"conTabletitle\">Idle</td></tr>";
37293
37294    my $tmpTimeNow = time();
37295
37296    my @tmpConKeys = keys(%Con);
37297    my @tmpConSortedKeys =
37298      sort { $Con{$a}->{timestart} <=> $Con{$b}->{timestart} } @tmpConKeys;
37299    my $tmpCount = 0;
37300    foreach my $key (@tmpConSortedKeys) {
37301        if ( $Con{$key}->{ip} ) {
37302            $tmpCount++;
37303            $Con{$key}->{messagescore} ||= 0;
37304            my $tmpScore;$tmpScore = ' / ' . $Con{$key}->{messagescore} if $Con{$key}->{messagescore};
37305            $Con{$key}->{spamfound} ||= $Con{$key}->{lastcmd} =~ /error/io;
37306            my $tmpDuration = $tmpTimeNow - $Con{$key}->{timestart};
37307            my $tmpInactive = $tmpTimeNow - $Con{$key}->{timelast};
37308            my $relay = $Con{$key}->{relayok} ? 'OUT' : 'IN';
37309            $relay .= '/NP' if $Con{$key}->{noprocessing};
37310            $relay .= '/WL' if $Con{$key}->{whitelisted};
37311
37312            my $bgcolor;
37313#            if ($showcolor) {
37314                    $bgcolor = ' style="background-color:#7CFC7F;"' if $Con{$key}->{whitelisted};
37315                    $bgcolor = ' style="background-color:#7CFC00;"' if $Con{$key}->{noprocessing};
37316                    $bgcolor = ' style="background-color:#7CFCFF;"' if $Con{$key}->{relayok};
37317                    my $cc = 255;
37318                    $cc -= int($Con{$key}->{messagescore} * 127 / $MessageScoringUpperLimit) if $MessageScoringUpperLimit;
37319                    $cc = 63 if $MessageScoringLowerLimit && $Con{$key}->{messagescore} >= $MessageScoringLowerLimit;
37320                    $cc = 0 if $cc < 0 or ($MessageScoringUpperLimit && $Con{$key}->{messagescore} >= $MessageScoringUpperLimit);
37321                    $cc = sprintf("%02X",$cc);
37322                    $bgcolor = ' style="background-color:#FF'.$cc.'00;"' if $Con{$key}->{messagescore} > 0;
37323                    $bgcolor = ' style="background-color:#FF0000;"' if $Con{$key}->{spamfound};
37324#            }
37325            if ($tmpCount%2==1) {
37326        			$rowclass = "\n<tr$bgcolor>";
37327        	} else {
37328        			$rowclass = "\n<tr class=\"even\"$bgcolor>";
37329        	}
37330
37331            $s2 .= $rowclass
37332                  . "<td $bgcolor><b>"
37333                  . ( $tmpCount ) . ' ' .$Con{$key}->{ssl}.$Con{$key}->{friendssl}
37334                  . "</b></td><td $bgcolor>" .
37335
37336                  (
37337                  (! $Con{$key}->{relayok} )
37338                  ? (
37339                        "<span onclick=\"popIPAction('"
37340                      . &normHTML($Con{$key}->{ip})
37341                      . "');\" onmouseover=\"linkBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=linkBG;\"><b>"
37342                      . $Con{$key}->{ip}
37343                      . "<\/b><\/span>"
37344                    )
37345                  :
37346                      $Con{$key}->{ip}
37347                  )
37348
37349                  . "</td><td $bgcolor>"
37350                  . substr( $Con{$key}->{helo}, 0, 25 )
37351                  . "</td><td $bgcolor>" .
37352
37353                  (
37354                  (! $Con{$key}->{relayok} )
37355                  ? (
37356                        "<span onclick=\"popAddressAction('"
37357                      . &encHTMLent($Con{$key}->{mailfrom})
37358                      . "');\" onmouseover=\"linkBG=this.style.backgroundColor; this.style.backgroundColor='#BBBBFF';\" onmouseout=\"this.style.backgroundColor=linkBG;\"><b>"
37359                      . substr( $Con{$key}->{mailfrom}, 0, 25 )
37360                      . "<\/b><\/span>"
37361                    )
37362                  :
37363                      substr( $Con{$key}->{mailfrom}, 0, 25 )
37364                  )
37365
37366                  . "</td><td $bgcolor>"
37367                  . substr( $Con{$key}->{rcpt}, 0, 25 )
37368                  . "</td><td $bgcolor>"
37369                  . $Con{$key}->{lastcmd}
37370                  . "</td><td $bgcolor>"
37371                  . $relay
37372                  . "</td><td $bgcolor>"
37373                  . (($Con{$key}->{spamfound}) ? 'Y' : 'N') .  $tmpScore
37374                  . "</td><td $bgcolor>"
37375                  . $Con{$key}->{maillength}
37376                  . "</td><td $bgcolor>"
37377                  . $tmpDuration
37378                  . "</td><td $bgcolor>"
37379                  . $tmpInactive
37380                  . '</td></tr>';
37381        }
37382	}
37383	$showcolor = $showcolor ? 'color-off' : 'color-on';
37384	my $ctime = localtime();
37385    <<EOT;
37386$headerHTTP
37387$headerDTDTransitional
37388<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
37389<head>
37390  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
37391  $focusJS
37392  <title>$currentPage ASSP ($myName) this monitor will slow down ASSP dramaticly - use it careful</title>
37393  <link rel=\"stylesheet\" href=\"get?file=images/assp.css\" type=\"text/css\" />
37394</head>
37395<body onfocus="tStart();" onblur="tStop();">
37396<div >
37397<div style="float: right">$ctime\&nbsp;\&nbsp;
37398\&nbsp;\&nbsp;
37399
37400<input id="stasto" type="button" value="Stop" onclick="javascript:startstop();"/>
37401\&nbsp;\&nbsp;
37402<input type="button" value="Close" onclick="javascript:window.close();"/></div>
37403<h2>SMTP Connections List</h2>
37404$s1
37405<table cellspacing="0" id="conTable">
37406$s2
37407</table>
37408<br />
37409</div>
37410</body></html>
37411EOT
37412}
37413
37414sub SaveConfig {
37415 return if $WorkerNumber != 0;
37416 mlog( 0, "saving config" ,1);
37417 if ($AsASecondary && $PrimaryPid) {
37418		kill HUP => $PrimaryPid;
37419}
37420 my $content;
37421 my $SC;
37422 local $/ = undef;
37423 open($SC,'>',\$content);
37424
37425 my $enc = ASSP::CRYPT->new($Config{webAdminPassword},0);
37426
37427 foreach my $c (@ConfigArray) {
37428   next if $c->[0] eq "0";
37429   if (exists $cryptConfigVars{$c->[0]}) {
37430       my $var = $enc->ENCRYPT($Config{$c->[0]});
37431       print $SC "$c->[0]:=$var\n";
37432   } else {
37433       print $SC "$c->[0]:=$Config{$c->[0]}\n";
37434   }
37435 }
37436 foreach my $c (sort keys %ConfigAdd) {
37437   if (exists $cryptConfigVars{$c}) {
37438       my $var = $enc->ENCRYPT($ConfigAdd{$c});
37439       print $SC "$c:=$var\n";
37440   } else {
37441       print $SC "$c:=$ConfigAdd{$c}\n";
37442   }
37443 }
37444 print $SC "ConfigSavedOK:=1\n";
37445 close $SC;
37446
37447 if (open($SC, '<', "$base/assp.cfg")) {
37448     my $current = (<$SC>);
37449     close $SC;
37450     if ($current eq $content) {
37451         mlog(0,"info: no configuration changes detected - nothing to save - file $base/assp.cfg is unchanged");
37452         return;
37453     }
37454 } else {
37455     mlog(0,"warning: unable to read the current config in $base/assp.cfg");
37456 }
37457
37458 unlink("$base/assp.cfg.bak.bak.bak") or mlog(0,"error: unable to delete file $base/assp.cfg.bak.bak.bak - $!");
37459 rename("$base/assp.cfg.bak.bak","$base/assp.cfg.bak.bak.bak") or mlog(0,"error: unable to rename file $base/assp.cfg.bak.bak to $base/assp.cfg.bak.bak.bak - $!");
37460 rename("$base/assp.cfg.bak","$base/assp.cfg.bak.bak") or mlog(0,"error: unable to rename file $base/assp.cfg.bak to $base/assp.cfg.bak.bak - $!");
37461 $FileUpdate{"$base/assp.cfgasspCfg"} = 0;
37462
37463 open($SC,'>',"$base/assp.cfg.tmp");
37464 print $SC $content;
37465 close $SC;
37466 mlog(0,"info: saved config to $base/assp.cfg.tmp - which is now renamed to $base/assp.cfg");
37467
37468 rename("$base/assp.cfg","$base/assp.cfg.bak") or mlog(0,"error: unable to rename file $base/assp.cfg to $base/assp.cfg.bak - $!");
37469 rename("$base/assp.cfg.tmp","$base/assp.cfg") or mlog(0,"error: unable to rename file $base/assp.cfg.tmp to $base/assp.cfg - $!");
37470 $asspCFGTime = $FileUpdate{"$base/assp.cfgasspCfg"} = ftime("$base/assp.cfg");
37471 mlog( 0, "finished saving config" ,1);
37472}
37473sub backupFile {
37474    return;    # unless $BackupCopies > 0;
37475    my $f  = shift;
37476    my $bf = $f;
37477    $bf =~ s/.*[\\\/]|/bak\//;
37478    my $i;
37479
37480    #    my $i = $BackupCopies - 1;
37481    unlink("$base/$bf.$i");
37482    for ( ; $i > 0 ; $i-- ) {
37483        rename( "$base/$bf." . ( $i - 1 ), "$base/$bf.$i" );
37484    }
37485    rename( $f, "$base/$bf.0" );
37486}
37487
37488
37489sub textinput {
37490    my (
37491        $name,  $nicename, $size,        $func,      $default,
37492        $valid, $onchange, $description, $cssoption, $note
37493    ) = @_;
37494    my $Error = checkUpdate( $name, $valid, $onchange );
37495    my $value = encodeHTMLEntities( $Config{$name} );
37496	my $hdefault = encodeHTMLEntities($default);
37497
37498 	my $showdefault;
37499 	$description = &niceLink($description);
37500 	$hdefault =~ s/'|"|\n//g;
37501 	$hdefault =~ s/\\/\\\\/g;
37502 	$showdefault = $hdefault ? $hdefault : '&nbsp;';
37503 	my $shortdefault = $showdefault;
37504 	$shortdefault = substr( $showdefault, 0, 40 ) . "..." if length ($showdefault) > 40;
37505 	my $color = ($value eq $hdefault) ? '' : 'style="color:#8181F7;"';
37506
37507 	$default = (!$hdefault or $value eq $hdefault) ? '' : ", default=$shortdefault";
37508 	my $cfgname = $EnableInternalNamesInDesc?"<a href=\"javascript:void(0);\"$color onmousedown=\"document.forms['ASSPconfig'].$name.value='$hdefault';return false;\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>click to reset<br />to default value</td><td>$showdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i> ($name$default)</i></a>":'';
37509 $cfgname .= syncShowGUI($name);
37510 my $edit  =  '';
37511 my $display = '';
37512 $note=1 if !$note;
37513 if ($name !~ /^adminuser/io &&
37514     $name ne 'pbdb' &&
37515     ($value=~/^\s*file:\s*(.+)\s*/io or
37516      $value=~/^\s*(DB):\s*/io or
37517      $name eq 'griplist' or
37518      exists $ReportFiles{$name})
37519    )
37520 {
37521      # the optionlist is actually saved in a file or is a DB.
37522      my $fil;
37523      $fil = $1 if ($value=~/^\s*file:\s*(.+)\s*/io or $value=~/^\s*(DB):\s*/io);
37524      my $what = 'file';
37525      if ($fil eq 'DB') {
37526          $what = 'list';
37527          $fil .= "-$name";
37528      }
37529      if ($name eq 'griplist') {
37530          $fil = $griplist;
37531          $note = 8;
37532      }
37533      my $act = $note == 8 ? 'Show' : 'Edit' ;
37534      my $ifil = $fil;
37535      if ($fil) {
37536          $fil  =~ s/([^\w\-.!~*\'() ])/sprintf("%%%02X",ord($1))/ego;
37537          $fil  =~ s/ /+/go;
37538          $edit = "<input type=\"button\" value=\" $act $what \" onclick=\"javascript:popFileEditor(\'$fil\',$note);setAnchor('$name');\" /><br />";
37539      }
37540
37541		my @reportIncludes;
37542      	if (exists $ReportFiles{$name}) {
37543          my $what = "report file: $ReportFiles{$name}";
37544          my $note = 2;
37545          @reportIncludes = ReportIncludes($ReportFiles{$name});
37546          my $fil = $ReportFiles{$name};
37547          $fil  =~ s/([^\w\-.!~*\'() ])/sprintf("%%%02X",ord($1))/ego;
37548          $fil  =~ s/ /+/go;
37549          $edit .= "<input type=\"button\" value=\" $act $what \" onclick=\"javascript:popFileEditor(\'$fil\',$note);setAnchor('$name');\" /><br />";
37550      	}
37551  		foreach my $f (keys %{$FileIncUpdate{"$base/$ifil$name"}}) {
37552     		my $fi = $f;
37553     		$f  =~ s/([^\w\-.!~*\'() ])/sprintf("%%%02X",ord($1))/eg;
37554     		$f  =~ s/ /+/g;
37555     		$fi  =~ s/$base/\./;
37556     		$edit .= "<input type=\"button\" value=\" $act included file $fi \" onclick=\"javascript:popFileEditor(\'$f\',$note);\" /><br />";
37557  		}
37558 	}
37559
37560    # get rid of google autofill
37561    #$name=~s/(e)(mail)/$1_$2/gi;
37562    return "<a name=\"$name\"></a>
37563 <div class=\"shadow\">
37564  <div class=\"option\">
37565   <div class=\"optionTitle$cssoption\">$nicename $cfgname</div>
37566   <div class=\"optionValue\">
37567    <input name=\"$name\" size=\"$size\" value=\"$value\" />
37568    $edit<br />
37569    $Error
37570    $description\n
37571   </div>
37572  </div>
37573  &nbsp;
37574 </div>";
37575
37576}
37577
37578sub textnoinput {
37579    my (
37580        $name,  $nicename, $size,        $func,      $default,
37581        $valid, $onchange, $description, $cssoption, $note
37582    ) = @_;
37583    my $Error = checkUpdate( $name, $valid, $onchange );
37584    my $value = encodeHTMLEntities( $Config{$name} );
37585
37586    my $hdefault = encodeHTMLEntities($default);
37587    $description = &niceLink($description);
37588    $hdefault =~ s/'|"|\n//g;
37589    $hdefault =~ s/\\/\\\\/g;
37590	my $showdefault = $hdefault ? $hdefault : '&nbsp;';
37591	my $shortdefault = $showdefault;
37592 	$shortdefault = substr( $showdefault, 0, 40 ) . "..." if length ($showdefault) > 40;
37593	my $color = ($value eq $hdefault) ? '' : 'style="color:#8181F7;"';
37594	$default = (!$shortdefault or $value eq $hdefault) ? '' : ", default=$shortdefault";
37595    my $cfgname =
37596      $EnableInternalNamesInDesc
37597      ? "<a href=\"javascript:void(0);\"$color onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>default value:</td><td>$showdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i>($name$default)</i></a>"
37598      : "";
37599    $cfgname .= syncShowGUI($name);
37600    my $edit = '';
37601    $note = 1 if !$note;
37602
37603    if ( $value =~ /^\s*file:\s*(.+)\s*/i ) {
37604
37605        # the optionlist is actually saved in a file.
37606        my $fil = $1;
37607
37608        # escape query string
37609        $fil =~ s/([^\w\-.!~*\'() ])/sprintf("%%%02X",ord($1))/eg;
37610        $fil =~ s/ /+/g;
37611        $edit =
37612"<input type=\"button\" value=\" Edit file \" onclick=\"javascript:popFileEditor(\'$fil\',$note);\" />";
37613    }
37614
37615    # get rid of google autofill
37616    #$name=~s/(e)(mail)/$1_$2/gi;
37617    return "<a name=\"$name\"></a>
37618 <div class=\"shadow\">
37619  <div class=\"option\">
37620   <div class=\"optionTitle$cssoption\">$nicename $cfgname</div>
37621   <div class=\"optionValue\">
37622    <input name=\"$name\" readonly style=\"background:#eee none; color:#222; font-style: italic\" size=\"$size\" value=\"$value\" />
37623    $edit<br />
37624    $Error
37625    $description\n
37626   </div>
37627  </div>
37628  &nbsp;
37629 </div>";
37630
37631}
37632
37633# everybody wants this, but I hate it -- use it if you care.
37634sub passinput {
37635    my (
37636        $name,  $nicename, $size,        $func, $default,
37637        $valid, $onchange, $description, $cssoption
37638    ) = @_;
37639    my $Error = checkUpdate( $name, $valid, $onchange );
37640    my $value = encodeHTMLEntities( $Config{$name} );
37641	$description = &niceLink($description);
37642    my $hdefault = encodeHTMLEntities($default);
37643    $hdefault =~ s/'|"|\n//g;
37644    $hdefault =~ s/\\/\\\\/g;
37645	my $showdefault = $hdefault ? $hdefault : '&nbsp;';
37646    my $cfgname =
37647      $EnableInternalNamesInDesc
37648      ? "<a href=\"#$name\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>default value:</td><td>$showdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i>($name)</i></a>"
37649      : "";
37650    $cfgname .= syncShowGUI($name);
37651    "<a name=\"$name\"></a>
37652 <div class=\"shadow\">
37653 <div class=\"option\">
37654  <div class=\"optionTitle$cssoption\">$nicename $cfgname</div>
37655  <div class=\"optionValue\"><input type=\"password\" name=\"$name\" size=\"$size\" value=\"$value\" /><br />\n$Error$description
37656  </div>
37657 </div>
37658 &nbsp;
37659 </div>";
37660}
37661
37662sub passnoinput {
37663    my (
37664        $name,  $nicename, $size,        $func, $default,
37665        $valid, $onchange, $description, $cssoption
37666    ) = @_;
37667    my $Error = checkUpdate( $name, $valid, $onchange );
37668    my $value = encodeHTMLEntities( $Config{$name} );
37669	$description = &niceLink($description);
37670    my $hdefault = encodeHTMLEntities($default);
37671    $hdefault =~ s/'|"|\n//g;
37672    $hdefault =~ s/\\/\\\\/g;
37673	my $showdefault = $hdefault ? $hdefault : '&nbsp;';
37674    my $cfgname =
37675      $EnableInternalNamesInDesc
37676      ? "<a href=\"#$name\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>default value:</td><td>$showdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i>($name)</i></a>"
37677      : "";
37678    $cfgname .= syncShowGUI($name);
37679    "<a name=\"$name\"></a>
37680 <div class=\"shadow\">
37681 <div class=\"option\">
37682  <div class=\"optionTitle$cssoption\">$nicename $cfgname</div>
37683  <div class=\"optionValue\"><input type=\"password\" readonly style=\"background:#eee none; color:#222; font-style: italic\" name=\"$name\" size=\"$size\" value=\"$value\" /><br />\n$Error$description
37684  </div>
37685 </div>
37686 &nbsp;
37687 </div>";
37688}
37689
37690sub listbox {
37691
37692    my (
37693        $name,  $nicename, $values,      $func, $default,
37694        $valid, $onchange, $description, $cssoption
37695    ) = @_;
37696    my $Error = checkUpdate( $name, $valid, $onchange );
37697	$description = &niceLink($description);
37698    my $options;
37699    my $hdefault;
37700    foreach my $opt ( split( /\|/o, $values ) ) {
37701		my ( $v, $d ) = split( /:/o, $opt, 2 );
37702		$d = $v unless $d;
37703		Encode::from_to($d,'ISO-8859-1','UTF-8') if ($d && ! Encode::is_utf8($d));
37704		if ( $Config{$name} eq $v ) {
37705			$options .= "<option selected=\"selected\" value=\"$v\">$d</option>";
37706		} else {
37707			$options .= "<option value=\"$v\">$d</option>";
37708		}
37709        $hdefault = $d if ( $default eq $v );
37710	}
37711    my $color = ($Config{$name} eq $default) ? '' : 'style="color:#8181F7;"';
37712    $default = ($Config{$name} eq $default) ? '' : ", default=$hdefault";
37713
37714    my $cfgname = $EnableInternalNamesInDesc?"<a href=\"javascript:void(0);\"$color onmousedown=\"document.forms['ASSPconfig'].$name.value='$default';return false;\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>click to reset<br />to default value</td><td>$hdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i>($name$default)</i></a>":'';
37715	$cfgname .= syncShowGUI($name);
37716    "<a name=\"$name\"></a>
37717 	<div class=\"shadow\">
37718 	<div class=\"option\">
37719  <div class=\"optionTitle$cssoption\">$nicename $cfgname</div>
37720  <div class=\"optionValue\">
37721  <span style=\"z-Index:100;\"><select size=\"1\" name=\"$name\">
37722	$options
37723	</select></span>
37724  <br />\n$Error$description
37725  </div>
37726 </div>
37727 &nbsp;
37728 </div>";
37729}
37730
37731sub checkbox {
37732    my (
37733        $name,  $nicename, $size,        $func, $default,
37734        $valid, $onchange, $description, $cssoption
37735    ) = @_;
37736    my $Error = checkUpdate( $name, $valid, $onchange );
37737    my $checked  = $Config{$name} ? 'checked="checked"' : '';
37738    my $disabled = "";
37739    my $isrun    = "";
37740    my $script   = "";
37741	$description = &niceLink($description);
37742    if (
37743        ( $name =~ /forceLDAPcrossCheck/ )
37744        && (   $RunTaskNow{forceLDAPcrossCheck}
37745            or ( !$CanUseLDAP && !$CanUseNetSMTP )
37746            or !$ldaplistdb )
37747      )
37748    {
37749        $disabled = "disabled";
37750        $isrun =
37751          'LDAPlist (ldaplistdb) is not configured - not available!<br />'
37752          if ( !$ldaplistdb );
37753        $isrun .= 'module Net::LDAP is not available!<br />'
37754          if ( !$CanUseLDAP );
37755        $isrun .= 'module Net::SMTP is not available!<br />'
37756          if ( !$CanUseNetSMTP );
37757    }
37758    if ( exists $RunTaskNow{$name} && $RunTaskNow{$name} && $qs{$name} ) {
37759        ${$name} = '';
37760        $Config{$name} = '';
37761        $qs{$name}     = '';
37762        $disabled      = "disabled";
37763        $isrun .=
37764"task $name (or related task) is just running - not available now!<br />Refreshing your browser will possibly restart $name, instead use the 'Refresh Browser' button to refresh the browser!<br />";
37765    }
37766
37767    my $hdefault = $default ? 'on'   : 'off';
37768    my $cdefault = $default ? 'true' : 'false';
37769    my $color = ($Config{$name} eq $default) ? '' : 'style="color:#8181F7;"';
37770    $default = ($Config{$name} eq $default) ? '' : ", default=$hdefault";
37771
37772    my $cfgname = $EnableInternalNamesInDesc?"<a href=\"javascript:void(0);\"$color onmousedown=\"document.forms['ASSPconfig'].$name.checked=$cdefault;return false;\" onmouseover=\"showhint('<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\'100%\\'><tr><td>click to reset<br />to default value</td><td>$hdefault</td></tr></table>', this, event, '450px', ''); return true;\" onmouseout=\"window.status='';return true;\"><i>($name$default)</i></a>":'';
37773	$cfgname .= syncShowGUI($name);
37774    "<a name=\"$name\"></a>
37775 <div class=\"shadow\">
37776 <div class=\"option\">
37777  <div class=\"optionTitle$cssoption\">
37778   <input type=\"checkbox\" $disabled name=\"$name\" value=\"1\" $checked /><span style=\"color:red\">$isrun</span>$nicename $cfgname<br /></div>
37779  <div class=\"optionValue\">\n$Error$description
37780  </div>
37781 </div>
37782 &nbsp;
37783 </div>$script";
37784}
37785
37786sub heading {
37787    my ( $description, $nodeId ) = @_[ 4, 5, 6 ];
37788
37789    "</div>
37790<div onmousedown=\"toggleDisp('$nodeId')\" class=\"contentHead\">
37791 $description
37792</div>
37793<div id=\"$nodeId\">\n";
37794}
37795sub checkUpdate {
37796    my ($name,$valid,$onchange,$desc)=@_;
37797    return '' unless %qs;
37798    unless (exists $Config{$name}) {
37799        mlog(0,"warning: config parm $name requested but $name is not defined") if $name !~ /^AD/o;
37800        return '';
37801    }
37802    if (exists $qs{'AD'.$name}) {
37803#        mlog(0,"info: QS AD$name found");  # access denied and/or hidden
37804        return '';
37805    }
37806    if($qs{$name} ne $Config{$name}) {
37807        if($qs{$name}=~/$valid/i && $qs{$name} eq $1) {
37808            my $new=$1; my $info;
37809            my $old=$Config{$name};
37810            $Config{$name}=$new;
37811            if($onchange) {
37812                $info=$onchange->($name,$old,$new,'',$desc);
37813            } else {
37814                my $dold = $old;
37815                my $dnew = $new;
37816                if (exists  $ConfigListBox{$name}) {
37817                    if (exists $ConfigListBoxAll{$name}) {
37818                        $dold = decHTMLent($ConfigListBoxAll{$name}{$old}." ($old)");
37819                        $dnew = decHTMLent($ConfigListBoxAll{$name}{$new}." ($new)");
37820                    } elsif ($ConfigListBox{$name} =~ /^O(?:n|ff)$/o) {
37821                        $dold = $old ? 'On' : 'Off';
37822                        $dnew = $new ? 'On' : 'Off';
37823                    }
37824                }
37825                my $text = exists $cryptConfigVars{$name} ? '' : "from '$dold' to '$dnew'";
37826                mlog(0,"AdminUpdate: $name changed $text") unless $new eq $old;
37827                ${$name}=$new;
37828    # -- this sets the variable name with the same name as the config key to the new value
37829    # -- for example $Config{myName}="ASSP-nospam" -> $myName="ASSP-nospam";
37830            }
37831            $ConfigChanged=1 unless exists $RunTaskNow{$name};
37832            if ($info !~ /span class.+?negative/o) {
37833                if ($new ne $old) {
37834                    my $ret = "<span class=\"positive\"><b>*** Updated $info</b></span><br />" ;
37835                    if ($Config{$name} ne ${$name} && ! exists $RunTaskNow{$name}) {
37836
37837                        ${$name}=$Config{$name};
37838                    }
37839                    &syncConfigDetect($name);
37840                    return $ret;
37841                }
37842            } else {
37843                return "<span class=\"negative\"><b>*** Invalid: '$qs{$name}' $info</b></span><br />
37844                <script type=\"text/javascript\">alert(\"Invalid '$name' - Unchange.\");</script>";
37845            }
37846        } else {
37847            my $text; $text = "(check returned '$1')" if $qs{$name}=~/$valid/i;
37848            return "<span class=\"negative\"><b>*** Invalid: '$qs{$name}' $text</b></span><br />
37849            <script type=\"text/javascript\">alert(\"Invalid '$name' - Unchange.\");</script>";
37850        }
37851    }
37852}
37853
37854sub PrintConfigDefaults {
37855    my $desc;
37856    open( $FH, ">","$base/notes/configdefaults.txt" );
37857	my $counterT = -1;
37858
37859    my %ConfigNice;
37860    my %ConfigDefault;
37861    my %ConfigNow;
37862
37863    foreach my $c (@ConfigArray) {
37864        if ( @{$c} == 5 ) {
37865            $counterT++;
37866        } else {
37867            $ConfigPos{ $c->[0] }  = $counterT;
37868            $ConfigNice{ $c->[0] } = $c->[1] ;
37869            $ConfigNice{ $c->[0] } =~ s/\&lt\;a href.*\/&gt;&lt\;\/a\&gt\;//i;
37870            $ConfigNice{ $c->[0] } =~ s/<a\s+href.*<\/a>//i;
37871            $ConfigNice{ $c->[0] } =~ s/'|"|\n//g;
37872            $ConfigNice{ $c->[0] } =~ s/\\/\\\\/g;
37873            $ConfigNice{ $c->[0] } = '&nbsp;' unless $ConfigNice{ $c->[0] };
37874            $ConfigDefault{ $c->[0] } = $c->[4] ;
37875            $ConfigDefault{ $c->[0] } =~ s/'|"|\n//g;
37876            $ConfigDefault{ $c->[0] } =~ s/\\/\\\\/g;
37877            $ConfigNow{ $c->[0] } =  $Config{$c->[0]};
37878            $ConfigNow{ $c->[0] } =~ s/'|"|\n//g;
37879            $ConfigNow{ $c->[0] } =~ s/\\/\\\\/g;
37880
37881            if ( $c->[3] == \&listbox ) {
37882#                $ConfigDefault{ $c->[0] } = 0 unless $ConfigDefault{ $c->[0] };
37883#                $ConfigNow{ $c->[0] } = 0 unless $ConfigNow{ $c->[0] };
37884                foreach my $opt ( split( /\|/o, $c->[2] ) ) {
37885                    my ( $v, $d ) = split( /:/o, $opt, 2 );
37886                    $ConfigDefault{ $c->[0] } = $d
37887                      if ( $ConfigDefault{ $c->[0] } eq $v );
37888                    $ConfigNow{ $c->[0] } = $d
37889                      if ( $ConfigNow{ $c->[0] } eq $v );
37890
37891                }
37892            } elsif ( $c->[3] == \&checkbox ) {
37893                $ConfigDefault{ $c->[0] } =
37894                  $ConfigDefault{ $c->[0] } ? 'On' : 'Off';
37895                $ConfigNow{ $c->[0] } =
37896                  $ConfigNow{ $c->[0] } ? 'On' : 'Off';
37897
37898            } else {
37899                $ConfigDefault{ $c->[0] } = ' '
37900                  unless $ConfigDefault{ $c->[0] };
37901                $ConfigNow{ $c->[0] } = ' '
37902                  unless $ConfigNow{ $c->[0] };
37903            }
37904        }
37905    }
37906
37907
37908    foreach my $c (@ConfigArray) {
37909		$desc = $c->[4]  if $c->[0] eq "0";
37910		$desc =~ s/\<.*\>//g;
37911        $desc =~ s/\<.*\>//g;
37912
37913
37914        print $FH "# $desc #\n" if $c->[0] eq "0";
37915#        next if $c->[0] eq "0";
37916        next if $c->[0] eq "webAdminPassword";
37917
37918        my $c0 = uc $c->[0];
37919
37920        if ( $ConfigDefault{ $c->[0] } ne $ConfigNow{ $c->[0] } ) {
37921
37922            print $FH "$c->[0] -- $ConfigNice{ $c->[0] }: $ConfigNow{ $c->[0] } (Default: $ConfigDefault{ $c->[0] }) \n";
37923        } else {
37924
37925            #print F "$c->[0] -- $desc: $Config{$c->[0]}  \n";
37926        }
37927    }
37928    close $FH;
37929
37930
37931
37932}
37933
37934sub PrintConfigSettings {
37935    my ($desc, $F);
37936    open( $F, '>',"$base/notes/configdefaults.txt" );
37937    my %ConfigNice = ();
37938    my %ConfigDefault = ();
37939    my %ConfigNow = ();
37940
37941    foreach my $c (@ConfigArray) {
37942            next if ( @{$c} == 5 );
37943            $ConfigNice{ $c->[0] } = encodeHTMLEntities( $c->[1] );
37944            $ConfigNice{ $c->[0] } =~ s/<a\s+href.*?<\/a>//io;
37945            $ConfigNice{ $c->[0] } =~ s/'|"|\n//go;
37946            $ConfigNice{ $c->[0] } =~ s/\\/\\\\/go;
37947            $ConfigNice{ $c->[0] } = '&nbsp;' unless $ConfigNice{ $c->[0] };
37948            $ConfigDefault{ $c->[0] } = encodeHTMLEntities( $c->[4] );
37949            $ConfigDefault{ $c->[0] } =~ s/'|"|\n//go;
37950            $ConfigDefault{ $c->[0] } =~ s/\\/\\\\/go;
37951            $ConfigNow{ $c->[0] } = encodeHTMLEntities( $Config{$c->[0]} );
37952            $ConfigNow{ $c->[0] } =~ s/'|"|\n//go;
37953            $ConfigNow{ $c->[0] } =~ s/\\/\\\\/go;
37954
37955            if ( $c->[3] == \&listbox ) {
37956                $ConfigDefault{ $c->[0] } = 0 unless $ConfigDefault{ $c->[0] };
37957                $ConfigNow{ $c->[0] } = 0 unless $ConfigNow{ $c->[0] };
37958                foreach my $opt ( split( /\|/o, $c->[2] ) ) {
37959                    my ( $v, $d ) = split( /:/o, $opt, 2 );
37960                    $ConfigDefault{ $c->[0] } = $d
37961                      if ( $ConfigDefault{ $c->[0] } eq $v );
37962                    $ConfigNow{ $c->[0] } = $d
37963                      if ( $ConfigNow{ $c->[0] } eq $v );
37964                }
37965            } elsif ( $c->[3] == \&checkbox ) {
37966                $ConfigDefault{ $c->[0] } =
37967                  $ConfigDefault{ $c->[0] } ? 'On' : 'Off';
37968                $ConfigNow{ $c->[0] } =
37969                  $ConfigNow{ $c->[0] } ? 'On' : 'Off';
37970
37971            } else {
37972                $ConfigDefault{ $c->[0] } = ' '
37973                  unless $ConfigDefault{ $c->[0] };
37974                $ConfigNow{ $c->[0] } = ' '
37975                  unless $ConfigNow{ $c->[0] };
37976            }
37977    }
37978
37979
37980    foreach my $c (@ConfigArray) {
37981	    $desc = $c->[4] if $c->[0] eq "0";
37982        $desc =~ s/\<[^<>]*\>//go;
37983        print $F "# $desc #\n" if $c->[0] eq "0";
37984        next if $c->[0] eq "0";
37985
37986        my $c0 = uc $c->[0];
37987
37988        if ( $c->[4] ne $Config{ $c->[0] } ) {
37989
37990            print $F "$c->[0] -- $ConfigNice{ $c->[0] }: $ConfigNow{ $c->[0] } (Default: $ConfigDefault{ $c->[0] }) \n";
37991        } else {
37992
37993            #print F "$c->[0] -- $desc: $Config{$c->[0]}  \n";
37994        }
37995    }
37996    close $F;
37997
37998    open( $F, '>',"$base/notes/config.txt" );
37999    foreach my $c (@ConfigArray) {
38000        $desc = $c->[7];
38001        if ($desc) {
38002          $desc =~ s/\<b\>//go;
38003          $desc =~ s/\<i\>//go;
38004          $desc =~ s/\<p\>//go;
38005          $desc =~ s/\<small\>//go;
38006          $desc =~ s/\<br \/\>//go;
38007          $desc =~ s/\<\/i\>//go;
38008          $desc =~ s/\<\/b\>//go;
38009          $desc =~ s/\<\/p\>//go;
38010          $desc =~ s/\<\/small\>//go;
38011          $desc =~ s/\<[^<>]*\>//go;
38012        }
38013
38014        my $c0  = uc $c->[0];
38015        my $act; $act = "actual: $Config{$c->[0]}" if $Config{ $c->[0] };
38016        my $def = $c->[4] ? "default: $c->[4]" : '' ;
38017        print $F "$c->[0]: $c->[1] -- $desc $def \n"
38018          if $Config{ $c->[0] } eq $c->[4] && $c->[0] ne "0";
38019        print $F "$c->[0]: $c->[1] -- $desc $def  \n"
38020          if $Config{ $c->[0] } ne $c->[4] && $c->[0] ne "0";
38021        $desc = $c->[4] if $c->[0] eq "0";
38022        $desc = '' unless $desc;
38023        $desc =~ s/\<[^<>]*\>//go;
38024        print $F "# $desc #\n" if $c->[0] eq "0";
38025
38026    }
38027    close $F;
38028
38029    open( $F, '>',"$base/assp.cfg.defaults" );
38030    foreach my $c (@ConfigArray) {
38031        next if $c->[0] eq "0";
38032        print $F "$c->[0]:=$c->[4]\n";
38033    }
38034    close $F;
38035    chmod 0664, "$base/assp.cfg.defaults";
38036
38037}
38038
38039
38040
38041sub SaveConfigSettings {
38042    return 0 if $Config{asspCfgVersion} eq "$version$modversion";
38043    $Config{asspCfgVersion}="$version$modversion";
38044    $asspCfgVersion = "$version$modversion";
38045    SaveConfigSettingsForce();
38046    return 1;
38047}
38048
38049sub SaveConfigSettingsForce {
38050    SaveConfig();
38051    PrintConfigSettings();
38052}
38053
38054sub PrintConfigHistory {
38055    my ($text) = @_;
38056    my $lt = localtime(time);
38057    $text =~ s/^AdminUpdate://i;
38058    open( $FH, ">>","$base/notes/confighistory.txt" );
38059    print $FH "$lt:  $text\n";
38060    close $FH;
38061}
38062sub PrintWhitelistAdd {
38063    my ($text) = @_;
38064    my $lt = localtime(time);
38065
38066    open( $FH, ">>","$base/notes/whitelistadd.txt" );
38067    print $FH "$text";
38068    close $FH;
38069}
38070
38071sub PrintUpdateHistory {
38072    my ($text) = @_;
38073    my $lt = localtime(time);
38074
38075    open( $FH, ">>","$base/notes/updatehistory.txt" );
38076    print $FH "$lt:  $text\n";
38077    close $FH;
38078}
38079sub PrintAdminInfo {
38080    my ($text) = @_;
38081    my $lt = localtime(time);
38082    $text =~ s/^AdminUpdate://i;
38083    open( $FH, ">>","$base/notes/admininfo.txt" );
38084    print $FH "$lt:  $text\n";
38085    close $FH;
38086}
38087
38088# This function is called on startup to clean up some settings
38089# Primarily these are settings that might be absent from assp.cfg
38090# or settings that are not needed anymore after an upgrade
38091sub fixConfigSettings {
38092    $Config{LogNameDate} = "MM-DD" if $Config{LogNameMMDD};
38093
38094
38095    $Config{DoSameSubject} = 3 if $Config{DoSameSubject} == 1;
38096
38097	$Config{DoBombHeaderRe} = 3 if $Config{DoBombHeaderRe} == 1;
38098
38099    if (exists $Config{enableSSL}) {
38100        $Config{DoTLS} = 2 if $Config{enableSSL};
38101
38102    }
38103    $Config{noMsgID} = '127.0.0.|192.168.|10.' if $Config{noMsgID} =~ /127.0.0.|192.168.|10./;
38104	$Config{maxBayesValues} = 40 if $Config{maxBayesValues} < 40;
38105	$Config{MaxFiles} = 20000 if $Config{MaxFiles} < 20000;
38106	$Config{DoBayesian} = 4 if $Config{baysTestMode} == 1 && $Config{DoBayesian} == 1;
38107	$Config{MaxAUTHErrors} = 5 if $Config{MaxAUTHErrors} eq "";
38108    $Config{base} = $base;
38109    $Config{TLDS} = 'file:files/tlds-alpha-by-domain.txt';
38110
38111	$Config{URIBLServiceProvider} = 'file:files/uriblserviceprovider.txt' if $Config{URIBLServiceProvider}  =~ /black.uribl/;
38112
38113	$Config{webSecondaryPort} = '22222';
38114	$Config{webAdminPort} = '55555' if $Config{webAdminPort} eq '55555|55556';
38115
38116	$Config{ HouseKeepingSchedule } = 3 if !$Config{ HouseKeepingSchedule };
38117	if ($Config{BayesMaxProcessTime} > 60) {
38118        $BayesMaxProcessTime = $Config{BayesMaxProcessTime} = 60;
38119    }
38120	$Config{ noGriplistUpload } = 1 if $Config{ noGriplist };
38121	$Config{ noGriplistDownload } = 1 if $Config{ noGriplist };
38122
38123
38124    $Config{LDAPcrossCheckInterval} = 6;
38125	$Config{AutoRestartAfterCodeChange} = 'immed' if $Config{AutoRestartAfterCodeChange} eq "1";
38126
38127
38128    $Config{MaxEqualXHeader} = '*=>'.$Config{MaxEqualXHeader} if $Config{MaxEqualXHeader} =~ /^\d+$/;
38129
38130	$Config{smtpDestination} =~ s/localhost/127.0.0.1/;
38131    if ( $Config{webAdminPassword} ) {
38132        $Config{webAdminPassword} = crypt( $Config{webAdminPassword}, "45" )
38133          if substr( $Config{webAdminPassword}, 0, 2 ) ne "45";
38134    }
38135
38136    if ($Config{allowAdminConnectionsFromName}) {
38137        my $host = $Config{allowAdminConnectionsFrom};
38138        $host .= '|' if $host;
38139        $host .= $Config{allowAdminConnectionsFromName};
38140        $Config{allowAdminConnectionsFrom} = $host;
38141        delete $Config{allowAdminConnectionsFromName};
38142    }
38143
38144    if ($Config{EmailFrom} =~ /ASSP <>/io) {
38145        mlog(0,"warning: invalid value '$Config{EmailFrom}' in EmailFrom was set to ''");
38146        $Config{EmailFrom} = '';
38147        $EmailFrom = '';
38148    }
38149
38150    $Config{maxBombSearchTime} = 10 if !$Config{maxBombSearchTime};
38151
38152
38153    $Config{wildcardUser} = lc $Config{wildcardUser};
38154    my $host = $defaultLocalHost;
38155    $host ||= $EmailBlockReportDomain;
38156
38157    $host ||= "assp-notspam.org";
38158    if ($Config{EmailFrom} eq '') {
38159
38160        $EmailFrom = "\<postmaster\@$host\>";
38161        $Config{EmailFrom} = $EmailFrom;
38162        mlog(0,"info: empty value '' in EmailFrom was set to '$EmailFrom'");
38163    }
38164    my  $oldEmailFrom = $Config{EmailFrom};
38165    if ($Config{EmailFrom}  =~ /\<\>/) {
38166    	$EmailFrom = $Config{EmailFrom};
38167   		$EmailFrom =~ s/\<\>/\<postmaster$host\>/;
38168
38169        $Config{EmailFrom} = $EmailFrom;
38170        mlog(0,"info: invalid value $oldEmailFrom in EmailFrom was set to '$EmailFrom'");
38171    }
38172	$Config{maillogExt} = ".eml" if !$Config{maillogExt};
38173
38174
38175
38176    my $mydelaydb = $Config{delaydb};
38177	if ($mydelaydb !~ /sql/i) {
38178    $Config{DelayShowDBwhite} = "file:$mydelaydb.white";
38179    $Config{DelayShowDB}      = "file:$mydelaydb";
38180    } else {
38181    $Config{DelayShowDBwhite} = "";
38182    $Config{DelayShowDB}      = "";
38183    }
38184    $Config{fileLogging} = 1;
38185    $fileLogging = 1;
38186
38187#    $ConfigAdd{globalRegisterURL} = $Config{globalRegisterURL};
38188#    $ConfigAdd{globalUploadURL} = $Config{globalUploadURL};
38189
38190# -- this sets the variable name with the same name as the config key to the new value
38191# -- for example $Config{myName}="ASSP-nospam" -> $myName="ASSP-nospam";
38192    foreach ( keys %Config ) {
38193    mlog(0,"info: '$_' => '$Config{$_}' ") if ($_ =~ /^[+-]?\d+(\.\d+)?$/);
38194    ${$_} = $Config{$_}  if ($_ !~ /^[+-]?\d+(\.\d+)?$/);
38195	}
38196
38197
38198    # set the date/time for assp.cfg
38199    my @s     = stat("$base/assp.cfg");
38200    my $mtime = $s[9];
38201    $FileUpdate{"$base/assp.cfgasspCfg"} = $mtime;
38202    $asspCFGTime = $mtime;
38203
38204	my $savecfg = 0;
38205    my $savesync = 0;
38206    foreach (sort keys %newConfig) {
38207        mlog(0,"info: new config parameter $_ was set to '${$_}'");
38208        if (&syncCanSync() && ! exists $neverShareCFG{$_}) {
38209            $ConfigSync{$_} = {};
38210
38211            $ConfigSync{$_}->{sync_cfg} = 0;
38212
38213            $ConfigSyncServer{$_} = {};
38214
38215            $savesync = 1;
38216        }
38217        $savecfg = 1;
38218    }
38219    &SaveConfig() if $savecfg;
38220	&syncWriteConfig() if $savesync;
38221    %newConfig = ();
38222
38223
38224
38225      # turn settings into regular expressions
38226    &CompileAllRE(1);
38227	eval {
38228    foreach my $c (@ConfigArray) {
38229        if ($c->[0] && ${$c->[0]} !~ /$c->[5]/) {
38230
38231            ${$c->[0]} = $c->[4];
38232            $Config{$c->[0]} = $c->[4];
38233        }
38234    }
38235    }
38236
38237
38238}
38239
38240sub CompileAllRE {
38241    my $init = shift;
38242    @PossibleOptionFiles=();
38243    foreach my $c (@ConfigArray) {
38244        next if @{$c}==1; # skip headings
38245
38246        next if (! $c->[6]);
38247        if (   $c->[6] eq 'ConfigMakeRe'
38248
38249            || $c->[6] eq 'ConfigMakeSLRe'
38250			|| $c->[6] eq 'ConfigMakeSLReSL'
38251            || $c->[6] eq 'ConfigMakeIPRe'
38252            || $c->[6] eq 'configUpdateRBLSP'
38253
38254            || $c->[6] eq 'configUpdateRWLSP'
38255
38256            || $c->[6] eq 'configUpdateURIBLSP'
38257
38258            || $c->[6] eq 'configUpdateCCD'
38259
38260            || $c->[6] eq 'configUpdateASSPCfg'
38261			|| $c->[6] eq 'updateDNS'
38262            || $c->[6] eq 'configUpdateCA'
38263            || $c->[6] eq 'configUpdateSPFOF'
38264            || $c->[6] eq 'ConfigCompileNotifyRe'
38265            || $c->[6] eq 'configChangeRcptRepl'
38266            || $c->[6] eq 'ConfigCompileRe'
38267            || $c->[6] eq 'configChangeMSGIDSec'
38268			|| $c->[6] eq 'configChangeRT'
38269			|| $c->[6] eq 'configUpdateMaxSize'
38270			|| $c->[6] eq 'configUpdateStringToNum'
38271            || $c->[6] eq 'configUpdateBACKSctrSP'
38272            || $c->[6] eq 'ConfigMakeEmailAdmDomRe'
38273			|| $c->[6] eq 'ConfigChangeSyncFile'
38274
38275
38276
38277           )
38278        {
38279            push(@PossibleOptionFiles,[$c->[0],$c->[1],$c->[6]]);
38280            mlog(0,"ERROR: possible code or language file error in config for $c->[0] - '*' not found at the end of the small description") if ($c->[1] !~ /\*\s*$/ );
38281            mlog(0,"ERROR: possible code or language file error in config for $c->[0] - '**' not found at the end of the small description for weighted RE") if (exists $WeightedRe{$c->[0]} && $c->[1] !~ /\*\*\s*$/ );
38282        } elsif ($c->[0] ne 'POP3ConfigFile') {
38283            mlog(0,"ERROR: possible code error in sub 'CompileAllRE' for $c->[0] - $c->[6] - option file is not checked") if $c->[1] =~ /\*$/;
38284
38285        }
38286        if ($c->[0] =~ /ValencePB$/o && defined $c->[6]) {
38287            $c->[6]->($c->[0],$Config{$c->[0]},$Config{$c->[0]},$init);
38288        }
38289    }
38290#    push(@PossibleOptionFiles,['TLDS','TOP level Domains',\&ConfigCompileRe]);
38291
38292    foreach my $f (@PossibleOptionFiles) {
38293        next if ($f->[0] eq 'asspCfg');
38294        if ($init) {
38295            $f->[2]->($f->[0],'',$Config{$f->[0]},'Initializing',$f->[1]);
38296        } else {
38297            if (($Config{$f->[0]} =~ /^ *file: *(.+)/io && fileUpdated($1,$f->[0])) or
38298                $Config{$f->[0]} !~ /^ *file: *(.+)/io)
38299            {
38300               $f->[2]->($f->[0],$Config{$f->[0]},$Config{$f->[0]},'',$f->[1]);
38301            }
38302        }
38303    }
38304
38305    updateBadAttachL1('BadAttachL1','',$Config{BadAttachL1},'Initializing');
38306    updateGoodAttach('GoodAttach','',$Config{GoodAttach},'Initializing');
38307	updatePassAttach('PassAttach','',$Config{PassAttach},'Initializing');
38308    configChangeRT('smtpDestinationRT','',$Config{smtpDestinationRT},'Initializing');
38309    configUpdateRBL('ValidateRBL','',$Config{ValidateRBL},'Initializing');
38310    configUpdateRWL('ValidateRWL','',$Config{ValidateRWL},'Initializing');
38311
38312	configChangeRBSched('RebuildSchedule','',$Config{RebuildSchedule},'Initializing');
38313	configChangeHKSched('HouseKeepingSchedule','',$Config{HouseKeepingSchedule},'Initializing');
38314    configUpdateURIBL('ValidateURIBL','',$Config{ValidateURIBL},'Initializing');
38315    configUpdateCA('CatchAll','',$Config{CatchAll},'Initializing');
38316	configUpdateCCD('ccSpamInDomain','',$Config{ccSpamInDomain},'Initializing');
38317    updateSRS('EnableSRS','',$Config{EnableSRS},'Initializing');
38318    freqNonSpam('freqNonSpam','',$Config{freqNonSpam},'Initializing');
38319    freqSpam('freqSpam','',$Config{freqSpam},'Initializing');
38320ConfigChangeNoAUTHPorts('NoAUTHlistenPorts','',$Config{NoAUTHlistenPorts},'NoAUTHlistenPorts');
38321configUpdateMaxSize('MaxSizeExternalAdr','',$Config{MaxSizeExternalAdr},'Initializing');
38322configUpdateMaxSize('MaxSizeAdr','',$Config{MaxSizeAdr},'Initializing');   configUpdateMaxSize('MaxRealSizeExternalAdr','',$Config{MaxRealSizeExternalAdr},'Initializing');
38323configUpdateMaxSize('MaxRealSizeAdr','',$Config{MaxRealSizeAdr},'Initializing');    ConfigChangeTLSPorts('NoTLSlistenPorts','',$Config{NoTLSlistenPorts},'Initializing');
38324    ConfigChangeMaxAllowedDups('MaxAllowedDups','',$Config{MaxAllowedDups},'Initializing');
38325	ConfigChangePOP3File('POP3ConfigFile','',$Config{POP3ConfigFile},'Initializing');
38326    configChangeMSGIDSec('MSGIDSec','',$Config{MSGIDSec},'Initializing');
38327    $spamSubjectEnc = is_7bit_clean($spamSubject) ? $spamSubject : encodeMimeWord($spamSubject,'Q','UTF-8');
38328}
38329
38330
38331sub optionFilesReload {
38332 # check if options files have been updated and need to be re-read
38333    foreach my $f (@PossibleOptionFiles) {
38334        if($f->[0] ne 'asspCfg' or ($f->[0] eq 'asspCfg' && $AutoReloadCfg)) {
38335            if ($Config{$f->[0]}=~/^ *file: *(.+)/io && fileUpdated($1,$f->[0]) ) {
38336                $f->[2]->($f->[0],$Config{$f->[0]},$Config{$f->[0]},'',$f->[1]);
38337                &syncConfigDetect($f->[0]);
38338            }
38339        }
38340    }
38341}
38342
38343
38344sub ConfigMakeRe {
38345 my ($name, $old, $new, $init, $desc)=@_;
38346 my $note = "AdminUpdate: $name changed from '$old' to '$new'";
38347 $note = "AdminUpdate: $name changed" if exists $cryptConfigVars{$name};
38348 mlog(0,$note) unless $init || $new eq $old;
38349 $new=checkOptionList($new,$name,$init);
38350# my $ret = &ConfigRegisterGroupWatch(\$new,$name,$desc);
38351 $new =~ s/([\\]\|)*\|+/$1\|/go;
38352 my $mHostPortRe = $HostRe . '(?:\:' . $PortRe . ')?' . '(?:|' . $HostRe . '(?:\:' . $PortRe . ')?)*';
38353
38354 my %toChangeMTA;
38355 $new = join('|', sort split(/\|/o,$new)) if $new;
38356 if ($name eq "vrfyDomains" ) {
38357        %DomainVRFYMTA = ();
38358
38359        my @entry = split(/\|/o,$new);
38360        $new = '';
38361        my $ld;
38362        my $lds;
38363        my $mta;
38364		my $defaultMTA;
38365
38366        while (@entry) {
38367           my $ad = shift @entry;
38368           $ad =~ s/\s//go;
38369           ($ld,$mta) = split(/\s*\=\>\s*/o,$ad,2);
38370           if ($ld =~ /^(all)$/io && $mta) {
38371               my $e = $1;
38372               if ($mta !~ /$mHostPortRe/o) {
38373                   mlog(0,"warning: $name - VRFY entry '$ad' contains a not valid MTA definition");
38374                   next;
38375               }
38376
38377           }
38378           if ($mta && $mta =~ /$mHostPortRe/o) {
38379               $DomainVRFYMTA{lc $ld} = $mta;
38380
38381           } elsif ($mta && $mta !~ /$mHostPortRe/o) {
38382               mlog(0,"warning: found entry '$ad' with wrong syntax in $name file");
38383               next;
38384           }
38385           $toChangeMTA{lc $ld} = 1;
38386
38387        }
38388
38389        if ($defaultMTA) {
38390            while (my ($k,$v) = each %toChangeMTA) {
38391                $DomainVRFYMTA{$k} = $defaultMTA;
38392            }
38393        }
38394
38395
38396
38397
38398    	foreach my $k (reverse sort keys %DomainVRFYMTA) {
38399        	my $v = $DomainVRFYMTA{$k};
38400        	$DomainVRFYMTA{$k} = $defaultMTA if !$v && $defaultMTA;
38401			$v = $DomainVRFYMTA{$k};
38402
38403			mlog(0," $k => $v",1) if $name eq "vrfyDomains" && $v && $DoVRFY && $VRFYLog>=2;
38404
38405    	}
38406        return '';
38407 }
38408
38409 if ($name eq "maxBombHits") {
38410        my @entry = split(/\|/o,$new);
38411        $new = '';
38412        my $ld;
38413        my $lds;
38414        my $mta;
38415
38416        while (@entry) {
38417           my $ad = shift @entry;
38418           ($ld,$mta) = split(/\=\>/,$ad);
38419           if ($mta) {
38420               $maxHits{lc $ld} = $mta;
38421
38422           }
38423
38424        }
38425
38426        return '';
38427 }
38428 $new=~s/([\.\[\]\-\(\)\+\\])/\\$1/g;
38429 $new=~s/\*/\.\{0,64\}/g;
38430 $new=~s/\?/\.\?/go;
38431#	mlog(0,"$new") if $name eq "blackListedDomains";
38432 if ($name eq 'TLDS') {
38433
38434        $TLDSRE = $new;
38435
38436
38437 } else {
38438
38439 	$new||='^(?!)'; # regexp that never matches
38440 }
38441 mlog(0,"ERROR: !!!!!!!!!! missing MakeRE{$name} in code !!!!!!!!!!") unless exists $MakeRE{$name};
38442 $MakeRE{$name}->($new);
38443 '';
38444}
38445
38446# make spamlover address like RE
38447sub ConfigMakeSLRe {
38448    my ( $name, $old, $new, $init, $desc ) = @_;
38449
38450    my $ld;
38451    my $re;
38452    my $we;
38453    my $mta;
38454    my $note = "AdminUpdate: $name changed from '$old' to '$new'";
38455    %FlatVRFYMTA = () if $name eq "LocalAddresses_Flat";
38456    %FlatDomains = () if $name eq "LocalAddresses_Flat";
38457    mlog( 0, $note )
38458      unless $init || $new eq $old;
38459    ${$name} = $new;
38460    $new = checkOptionList( $new, $name, $init );
38461    $new =~ s/\./\\\./go;
38462 	$new =~ s/\\\\/\\/go;
38463 	$new =~ s/\\\\/\\/go;
38464    $new =~ s/\*/\.\*\?/go;
38465    $new =~ s/\?/\.\?/go;
38466    my ( @uad, @u, @d );
38467    if (exists $WeightedRe{$name}) {
38468
38469
38470        my @Weight = @{$name.'Weight'};
38471        my @WeightRE = @{$name.'WeightRE'};
38472        @{$name.'Weight'} = ();
38473        @{$name.'WeightRE'} = ();
38474
38475        my $newnew;
38476        foreach my $rex ( split( /\|/o, $new ) ) {
38477        	next if $rex =~ /\=\>/;
38478 			push (@{$name.'WeightRE'},$rex);
38479        	push (@{$name.'Weight'},"1");
38480        	$newnew .= "$rex|";
38481
38482        }
38483        foreach my $rex ( split( /\|/o, $new ) ) {
38484        	next if $rex !~ /\=\>/;
38485
38486			($re, $we) = $rex =~ /(.*)\=\>(.*)/;
38487
38488			$we =~ s/\\//;
38489            $we += 0;
38490
38491#			$re =~ s/\\//;
38492			$re .= '$' if $re =~ /^@/;
38493			$newnew .= "$re|";
38494
38495            eval{$note =~ /$re/};
38496            if ($@) {
38497                mlog(0,"error: weighted regex for $name is invalid '$re=>$we' - $@") ;
38498                mlog(0,"warning: value for $name was not changed - all changes are ignored") ;
38499
38500 				@{$name.'Weight'} = @Weight;
38501                @{$name.'WeightRE'} = @WeightRE;
38502                $new = $old;
38503                return "<span class=\"negative\"> - weighted regex for $name is invalid '$re=>$we'!</span>";
38504            }
38505
38506            push (@{$name.'WeightRE'},$re);
38507            push (@{$name.'Weight'},$we);
38508
38509
38510        }
38511
38512        my $count = 0;
38513        foreach my $k (@{$name.'Weight'}) {
38514            my $reg = ${$name.'WeightRE'}[$count];
38515            $count++;
38516        }
38517
38518		$new = $newnew;
38519		$new =~ s/\|$//;
38520
38521    }
38522
38523
38524
38525    foreach my $a ( split( /\|/, $new ) ) {
38526        if ( $a =~ /\S\@\S/ ) {
38527        	if ( $name eq "LocalAddresses_Flat" ) {
38528        		$a =~ /^(.*@)(.*)$/;
38529        		my $h = $2;
38530        		$h =~ s/\\//;
38531        		$FlatDomains{ lc $h } = 1 if $h;
38532        		}
38533            push( @uad, $a );
38534        } elsif ( $a =~ /^\@/ ) {
38535
38536            if ( $name eq "LocalAddresses_Flat" ) {
38537                ( $ld, $mta ) = split( /\=\>/, $a );
38538                $FlatVRFYMTA{ lc $ld } = $mta if $mta;
38539                mlog(0,"warning: LocalAddresses_Flat VRFY entry $ld also exists in localDomains")
38540                   if exists $DomainVRFYMTA{ lc $ld } && $WorkerNumber == 0;
38541                $a = $ld if $mta;
38542                $a =~ /^\@(.*)/;
38543                my $h = $1;
38544        		$h =~ s/\\//;
38545            	$FlatDomains{ lc $h } = 1 if $h;
38546            }
38547
38548            push( @d, $a );
38549        } elsif ( $name eq "LocalAddresses_Flat"
38550            && $LocalAddresses_Flat_Domains )
38551        {
38552        	my $h = $a;
38553        	$h =~ s/\\//;
38554        	$h =~ s/\@//;
38555			$FlatDomains{ lc $h } = 1 if $h;
38556            ( $ld, $mta ) = split( /\=\>/, $a );
38557            $ld = '@' . $ld;
38558            $a  = '@' . $a;
38559
38560            $FlatVRFYMTA{ lc $ld } = $mta if $mta;
38561            mlog(0,"warning: LocalAddresses_Flat VRFY entry $ld also exists in localDomains")
38562               if exists $DomainVRFYMTA{ lc $ld } && $WorkerNumber == 0;
38563            $a = $ld if $mta;
38564
38565	        push( @d, $a );
38566        } else {
38567            if ( $name eq "LocalAddresses_Flat" ) {
38568                ( $ld, $mta ) = split( /\=\>/, $a );
38569                if ($mta) {
38570                    $ld                    = '@' . $ld;
38571                    $a                     = '@' . $a;
38572                    $FlatVRFYMTA{ lc $ld } = $mta;
38573                    mlog(0,"warning: LocalAddresses_Flat VRFY entry $ld also exists in localDomains")
38574                       if exists $DomainVRFYMTA{ lc $ld } && $WorkerNumber == 0;
38575                    $a                     = $ld;
38576                    push( @d, $a );
38577                } else {
38578                	$a =~ /^(.*@)(.*)/;
38579                	my $h = $2;
38580        			$h =~ s/\\//;
38581        			$h =~ s/\@//;
38582        			$FlatDomains{ lc $h } = 1 if $h;
38583                    push( @u, $a );
38584                }
38585            } else {
38586                push( @u, $a );
38587            }
38588        }
38589    }
38590
38591    my @s;
38592    push( @s, '^(' . join( '|', @uad ) . ')$' ) if @uad;
38593    push( @s, '^(' . join( '|', @u ) . ')@' )   if @u;
38594    push( @s, '(' . join( '|', @d ) . ')$' ) if @d;
38595    my $s = join( '|', @s );
38596    $s ||= '^(?!)';    # regexp that never matches
38597    mlog( 0, "ERROR: !!!!!!!!!! missing MakeSLRE{$name} in code !!!!!!!!!!" )
38598      unless exists $MakeSLRE{$name};
38599
38600    SetRE( $MakeSLRE{$name}, $s, 'i', $desc );
38601    '';
38602
38603}
38604
38605# make spamlover RE for SpamLovers
38606sub ConfigMakeSLReSL {
38607    my ( $name, $old, $new, $init, $desc ) = @_;
38608    my $ld;
38609    my $mta;
38610    $SLscore{$name} = ();
38611    mlog( 0, "adminupdate: $name changed from '$old' to '$new'" )
38612      unless $init || $new eq $old;
38613    ${$name} = $new;
38614    $new = checkOptionList( $new, $name, $init );
38615	my $ret;
38616
38617    $new =~ s/([\\]\|)*\|+/$1\|/go;
38618    $new =~ s/\./\\\./go;
38619    $new =~ s/\*/\.\{0,64\}/go;
38620
38621    my ( @uad, @u, @d , %entry_uad, %entry_u, %entry_d);
38622
38623    foreach ( split( /\|/o, $new ) ) {
38624
38625        my ($ad, $score) = split /=>/o;
38626        $ad =~ s/\s//go;
38627
38628        if ($score && ! ($score =~ s/^\s*(\d+(?:\.\d+)?)\s*$/$1/o)) {
38629            $score = undef;
38630            $ret .= ConfigShowError( 0, "warning: spamlover max score for $name in definition '$_' is not a numbered value - the score value is ignored" );
38631        }
38632		$SLscore{$name}->{unescape(lc $ad)} = max($SLscore{$name}->{unescape(lc $ad)},$score) if defined $score;
38633
38634        if ( $ad =~ /\S\@\S/o ) {
38635            push( @uad, $ad ) unless exists $entry_uad{lc $ad};
38636            $entry_uad{lc $ad} = 1;
38637        } elsif ( $ad =~ s/^\@//o ) {
38638            push( @d, $ad ) unless exists $entry_d{lc $ad};
38639            $entry_d{lc $ad} = 1;
38640        } else {
38641            push( @u, $ad ) unless exists $entry_u{lc $ad};
38642            $entry_u{lc $ad} = 1;
38643        }
38644    }
38645
38646    my @s;
38647    push( @s, '(?:' . join( '|', sort @u ) . ')@.*' )   if @u;
38648    push( @s, '(?:' . join( '|', sort @uad ) . ')' ) if @uad;
38649    push( @s, '.*?@(?:' . join( '|', sort @d ) . ')' ) if @d;
38650    my $s;
38651    $s = '(?:^' . join( '|', @s ) . '$)' if @s;
38652    $s =~ s/\@/\\\@/go;
38653    $s ||= '^(?!)';    # regexp that never matches
38654    $ret .= ConfigShowError( 1, "ERROR: !!!!!!!!!! missing MakeSLRE{$name} in code !!!!!!!!!!" )
38655      if ! exists $MakeSLRE{$name} && $WorkerNumber == 0;
38656
38657   	SetRE( $MakeSLRE{$name}, $s, 'i', $desc );
38658}
38659# make EmailAdmins -> Domain match RE
38660sub ConfigMakeEmailAdmDomRe {
38661    my ( $name, $old, $new, $init, $desc ) = @_;
38662    %EmailAdminDomains = ();
38663    mlog( 0, "adminupdate: $name changed from '$old' to '$new'" )
38664      unless $init || $new eq $old;
38665    ${$name} = $new;
38666    $new = checkOptionList( $new, $name, $init );
38667    my $ret = &ConfigRegisterGroupWatch(\$new,$name,$desc);
38668
38669    $new =~ s/([\\]\|)*\|+/$1\|/go;
38670
38671    foreach ( split( /\|/o, $new ) ) {
38672        my ($ad, $domain) = split /=>/o;
38673        $ad =~ s/\s//go;
38674        $ad = lc $ad;
38675        $domain =~ s/\s//go;
38676        $domain =~ s/[, ]+/\|/go;
38677        $domain =~ s/\|+/\|/go;
38678        $domain =~ s/^\|//o;
38679        $domain =~ s/\|$//o;
38680        $domain = '*@'.$domain if $domain !~ /\@/o;
38681        $domain =~ s/\./\\\./go;
38682        $domain =~ s/\*/\.\{0,64\}/go;
38683        $domain =~ s/\@/\\@/go;
38684        $domain = lc $domain;
38685        eval{$EmailAdminDomains{$ad} = qr/$domain/;};
38686        if ($@) {
38687            $ret .= ConfigShowError( 1, "ERROR: $name contains wrong definition ($_) - $@");
38688        }
38689    }
38690    return $ret;
38691}
38692sub ConfigRegisterGroupWatch {
38693
38694}
38695
38696sub ConfigCheckGroupWatch {
38697
38698}
38699
38700# inplace replace a hostname with all available IP's
38701# in a ConfigMakeIPRe value and return errors
38702sub replaceHostByIP {
38703    my ($new,$name,$desc) = @_;
38704
38705    return unless $$new;
38706	return if $name eq "droplist";
38707
38708    my @nnew;
38709    my $ret;
38710    my $minTTL = 999999999;
38711    foreach my $l (split(/\|/o,$$new)) {
38712        $l =~ s/^\s+//o;
38713        $l =~ s/\s$//o;
38714        if ($l =~ m/^$IPv6Re(?:\/\d{1,3})?/io) {  # is a IPv6 address
38715            push @nnew, $l;
38716            next;
38717        }
38718        if ($l =~ m/^(?:\d{1,3}\.){1,3}(?:\d{1,3})?(?:\/\d{1,2})?/o) { # is a IPv4 fragment or address
38719            push @nnew, $l;
38720            next;
38721        }
38722        # found a hostname - replace it with all available IP's
38723
38724        my ($sl,$sep,$desc) = split(/(\s+)/o,$l,2);
38725
38726        if ($sl !~ /$EmailDomainRe|\w\w+/o) {      # not a valid hostname
38727            $ret .= ConfigShowError(1, "AdminInfo: '$sl' is not a valid hostname in $name - ignore entry") if $sl;
38728            mlog(0,"4 '$sl' is not a valid hostname in $name");
38729            next;
38730        }
38731        $desc = $sep.$desc if $desc;
38732        my $res = queryDNS($sl ,'A');
38733        ref $res or next;
38734        if ($res) {
38735            my @answer = map{$_->string} $res->answer;
38736            my $w = 1;
38737            while (@answer) {
38738                my $RR = Net::DNS::RR->new(shift @answer);
38739                my $ttl = $RR->ttl;
38740
38741                my $data = $RR->rdatastr;
38742                push @nnew, "$data/32$desc" if $data =~ /^$IPv4Re$/o;
38743                push @nnew, "$data/128$desc" if $data =~ /^$IPv6Re$/o;
38744                d("replaceHostByIP record: $sl -> $data , TTL -> $ttl");
38745                mlog(0,"added IP '$data/32' for hostname '$sl' to $name") if $WorkerNumber == 0 and $MaintenanceLog > 2;
38746                $w = 0 if $ttl;
38747                $minTTL = $ttl if $ttl < $minTTL;
38748            }
38749        } else {
38750            $ret .= ConfigShowError(1, "AdminInfo: error - unable to resolve hostname '$sl' for configuration of '$name'");
38751        }
38752    }
38753
38754    $$new = join('|',@nnew);
38755    return $ret;
38756}
38757sub ConfigShowError {
38758    eval {
38759    my ($red, $msg, $noprepend, $noipinfo , $noS) = @_;
38760    return unless $msg;
38761    mlog(0, $msg, $noprepend, $noipinfo , $noS);
38762    my ($prsp,$posp);
38763    if ($red) {
38764        $prsp = '<span class="negative">';
38765        $prsp = '</span>';
38766    }
38767    $msg =~ s/[^:]+:\s*//o;
38768    return "$prsp$msg$prsp<br />\n";
38769    };
38770}
38771
38772sub unescape {
38773    my $string = shift;
38774    $string =~ s/\\//go;
38775    return $string;
38776}
38777sub loadexportedRE {
38778    my $name = shift;
38779    return 0;
38780}
38781# make IP address RE
38782# allow for CIDR notation if Net::IP::Match::Regexp available
38783sub ConfigMakeIPRe {
38784
38785    my ($name, $old, $new, $init, $desc)=@_;
38786    my $newexpanded;
38787    my $cips;
38788    use re 'eval';
38789
38790    mlog(0,"AdminUpdate: $name changed from '$old' to '$new'") unless $init || $new eq $old;
38791    ${$name} = $new;
38792    $new=~s/\s*\-\s*/\-/go;
38793    $new=checkOptionList($new,$name,$init) ;
38794    my $ret = &ConfigRegisterGroupWatch(\$new,$name,$desc);
38795
38796    my $loadRE;
38797    if (($WorkerNumber != 0) && ($loadRE = loadexportedRE($name))) {
38798         $loadRE =~ s/\)$//o if $loadRE =~ s/^\(\?(?:[xism\-]*)?\://o;
38799         eval{${$MakeIPRE{$name}}=qr/$loadRE/;};
38800         $ret .= ConfigShowError(0,"AdminInfo: regular expression error in '$name (exported):$loadRE' for $desc: $@") if $@;
38801         return $ret;
38802    }
38803
38804    if ($CanMatchCIDR) {
38805        foreach my $l (split(/\|/o,$new)) {
38806
38807            $l=~s/\.\./\./go;
38808            $l=~s/--+/*/go;
38809            $l=~s/#.*//o;
38810            $l=~s/;.*//o;
38811            $l=~s/\[([0-9a-f:]+)\]/$1/ig;
38812
38813            $l=~s/^($IPRe)\s+($IPRe)/$1-$2/go;
38814
38815            if ($CanUseCIDRlite && $l=~/^$IPRe-$IPRe/o ) {
38816
38817                $l=~s/($IPRe)-($IPRe)(.*)/ipv6expand($1).'-'.ipv6expand($2)/oe;
38818                $desc=$3;
38819
38820                my $cidr = Net::CIDR::Lite->new;
38821                eval{$cidr->add_any($l);};
38822                if ($@) {
38823                    $@=~/^(.+?)\sat\s/o ;
38824                    $ret .= ConfigShowError(1,"AdminInfo: $name: $1 ($l)") ;
38825                    next;
38826                }
38827                my @cidr_list = $cidr->list;
38828                my $cidr_join = join("$desc|",@cidr_list);
38829                $newexpanded.=$cidr_join."$desc|";
38830                next;
38831            }
38832            $newexpanded.=$l."|";
38833        }
38834        $newexpanded=~s/\|$//o if $newexpanded;
38835        $new=$newexpanded;
38836
38837    }
38838    if ($new) {
38839        $ret .= replaceHostByIP(\$new,$name,$desc);
38840        $new =~ s/\|\|/\|/go;
38841    }
38842    if ($new && $CanMatchCIDR) {
38843        my %ips = ();
38844        my $new6;
38845        my $new4;
38846        foreach my $l (split(/\|/o,$new)) {
38847            my $hasIPv6;
38848            if ($l =~ /:[^:]*:/o) {
38849                $l =~ s/^\[([0-9a-f:\.]+)\]/$1/io;
38850                $hasIPv6 = 1;
38851                my $ip;
38852                my $bits;
38853                my $ll = $l;
38854                ($l, $desc) = ($l =~ m/^([0-9a-f:.]+(?:\/[0-9]{1,3})?)\s*(.*)\s*$/io);
38855                $desc = " $desc" if ($desc);
38856                ($ip, $bits) = split(/\//, $l);
38857                if ($l =~ /\//) {
38858                    if (!$bits || $bits > 128) {
38859                        $ret .= ConfigShowError(1, "AdminInfo: invalid IPv6 address $l in $name");
38860                        next;
38861                    }
38862                    $ip = ipv6expand($ip);
38863                } else {
38864                    $ip = ipv6expand($ip);
38865                    $ip =~ s/(?::0)+$//o;
38866                    my @pre = split /:/o, $ip;
38867                    $bits = ($#pre+1)*16;
38868                    if ($bits > 128) {
38869                        $ret .= ConfigShowError(1, "AdminInfo: invalid IPv6 address $l in $name");
38870                        next;
38871                    }
38872                }
38873                my $ip6b = ipv6binary($ip, $bits);
38874
38875                $new6 .= $ip6b . "(?{'" . $l . $desc . "'})" . "|";
38876
38877                $cips++;
38878                $l = $ll;
38879            }
38880            if (my @matches=$l=~/(\d{1,3}\.)(\d{1,3}\.?)?(\d{1,3}\.?)?(\d{1,3})?(\/)?(\d{1,3})?\s*(.*)\s*$/io)   {
38881                my $description=$7;
38882                my $ip=$1.($2?$2:'').($3?$3:'').($4?$4:'');
38883                my $bits=($5?$5:'').($6?$6:'');
38884
38885                foreach (@matches) {
38886                    $_ = 0 unless $_;
38887                    s/\.$//go;
38888                }
38889                if  ($matches[0]>255 || $matches[1]>255 || $matches[2]>255 || $matches[3]>255) {
38890                    $ret .= ConfigShowError(1,"AdminInfo: $name, error in line $l, Dotted Quad Number > 255 ");
38891                    next;
38892                }
38893
38894                $ip=~s/\.$//go;
38895
38896                if ($hasIPv6) {
38897                    $bits -= 96;
38898                    $bits = 32 if $bits < 0;
38899                }
38900                $bits='' if !$bits;
38901
38902                my $dcnt=($ip=~tr/\.//);
38903                if ($dcnt>=3) {
38904                    $bits||='/32';
38905                } elsif ($dcnt>=2) {
38906                    $ip.='.0';
38907                    $bits||='/24';
38908                } elsif ($dcnt>=1) {
38909                    $ip.='.0'x2;
38910                    $bits||='/16';
38911                } else {
38912                    $ip.='.0'x3;
38913                    $bits||='/8';
38914                }
38915
38916                $desc= $description ? "$ip$bits $description" : "$ip$bits";
38917
38918                $desc=~s/'/\\'/go;
38919                $desc||=1;
38920                if  ("$ip$bits" !~ /$IPv4Re\/\d{1,2}/o) {
38921                    $ret .= ConfigShowError(1,"AdminInfo: $name error in line $l, IP notation: $ip$bits");
38922                    next;
38923                }
38924                $ips{"$ip$bits"}=$desc;
38925                $cips++;
38926            }
38927        }
38928
38929        if (scalar keys %ips) {
38930            eval{$new4=Net::IP::Match::Regexp::create_iprange_regexp(\%ips);};
38931            $ret .= ConfigShowError(1,"AdminInfo:$name $@") if $@;
38932        }
38933
38934        if ($new6) {
38935            $new6 =~ s/\|$//o;
38936            if ($CanUseRegexOptimizer && eval{Regex::Optimizer->VERSION >= 1.11} && (! exists $noOptRe{$name} || $noOptRe{$name} > 0)) {
38937                my $optimSX = $noOptRe{$name} == 2;
38938                my $how = $optimSX ? 'strong ' : 'simple ';
38939                my $lenBefore = length($new6) + 19;      # (?-xims:(?$f:......))
38940                if ($WorkerName eq 'startup' && $MaintenanceLog >= 2) {
38941                   print $how . "optimizing IPv6 regex for $name";
38942                   print ' ' x (30 - length($name) - length($how));
38943                }
38944                my $on6 = $new6;
38945                my $o = Regex::Optimizer->new;
38946                $o->set(optim_sx => $optimSX);
38947#                $o->set(debug => $debug);
38948                eval{$new6 = $o->optimize(qr/$new6/)};
38949                if ($@) {
38950                    $ret .= ConfigShowError(0,"AdminInfo: regular expression optimzation error in '$name' for IPv6 addresses: $@");
38951                    $new6 = $on6;
38952                    if ($WorkerName eq 'startup' && $MaintenanceLog >= 2) {
38953                        print "[FAILED]\n";
38954                    }
38955                } else {
38956                    if ($WorkerName eq 'startup' && $MaintenanceLog >= 2) {
38957                        print "[OK]\n";
38958                    }
38959                }
38960            }
38961        }
38962        if ($new6 && $new4) {
38963            $new4 =~ s/^.*\^(.*)\)/$1/o;
38964            $new = "(?msx-i:^(6(?:$new6)|$new4))";
38965        } elsif ($new6) {
38966            $new6 = '(?msx-i:^6('.$new6.'))';
38967            $new = $new6;
38968        } elsif ($new4) {
38969            $new = $new4;
38970        } else {
38971            $new = qr/^(?!)/;    # regexp that never matches
38972        }
38973
38974        $ret .= ConfigShowError(1,"ERROR: !!!!!!!!!! missing MakeIPRE{$name} in code !!!!!!!!!!") if ! exists $MakeIPRE{$name} && $WorkerNumber == 0;
38975        eval{${$MakeIPRE{$name}}=qr/$new/;};
38976        $ret .= ConfigShowError(1,"AdminInfo: regular expression error in '$name:$new': $@") if $@;
38977    } else {
38978        my %ips = ();
38979        if ($new) {
38980            foreach my $l (split(/\|/o,$new)) {
38981                if ($l =~ /:[^:]*:/o) {
38982                    $l =~ s/^\[([0-9a-f:\.]+)\]/$1/io;
38983                    if ($l =~ /([0-9a-f:\.]+)\s*(.*)\s*$/io )
38984                    {
38985                        my $description = $2;
38986                        my $ip = ipv6expand($1);
38987
38988                        $desc = $description ? "$ip $description" : $ip ;
38989                        $ips{$ip} = $desc;
38990                        $cips++;
38991                    }
38992                }
38993                if ($l =~ /(\d{1,3}\.)(\d{1,3}\.?)?(\d{1,3}\.?)?(\d{1,3})?(\/)?(\d{1,3})?\s*(.*)\s*$/io)   {
38994                    my $description=$7;
38995                    my $ip=$1.$2.$3.$4;
38996
38997                    $desc = $description ? "$ip $description" : $ip ;
38998                    $ips{$ip}=$desc;
38999                    $cips++;
39000                }
39001            }
39002        }
39003        my @ips;
39004        while (my ($ip,$desc)=each(%ips)) {
39005            $ip=~s/([\.\[\]\-\(\)\*\+\\])/\\$1/go;
39006            next unless $ip;
39007            $desc=~s/'/\\'/go;
39008            push(@ips,"$ip(?{'$desc'})");
39009        }
39010        $new=join('|',@ips);
39011        $new ||='^(?!)'; # regexp that never matches
39012        $ret .= ConfigShowError(1,"ERROR: !!!!!!!!!! missing MakeIPRE{$name} in code !!!!!!!!!!") if ! exists $MakeIPRE{$name} && $WorkerNumber == 0;
39013        eval{${$MakeIPRE{$name}}=qr/^(?:$new)/};
39014        $ret .= ConfigShowError(1,"AdminInfo: regular expression error in '$name:$new' for $desc: $@") if $@;
39015    }
39016    exportOptRE(\$new,$name) if $WorkerNumber == 0;
39017
39018    return $ret;
39019}
39020sub exportOptRE {
39021
39022    return;
39023}
39024
39025
39026sub NoTLS {
39027
39028    return;
39029}
39030# check if IP address matches RE
39031
39032sub matchIP {
39033    my ( $ip, $re, $fhh, $donotmlog ) = @_;
39034    my $reRE = ${ $MakeIPRE{$re} };
39035    return 0 unless $ip && $reRE;
39036    return if $reRE =~ /^$neverMatchRE$/o;
39037
39038    $fhh = 0 if ! $fhh || ! exists $Con{$fhh};
39039    $ip =~ s/\r|\n//go;
39040    my $ret;
39041    local $^R = undef;
39042    my $this = $Con{$fhh};
39043    use re 'eval';
39044    if ($CanMatchCIDR) {
39045        if ($ip =~ /:[^:]*:/o) {
39046            $ip =~ s/^\[([0-9a-f:]+)\].*/$1/io;
39047            $ip = ipv6expand($ip);
39048            my $ip6b = ipv6binary($ip, 128);
39049            $ret = $^R if (( '6' . $ip6b ) =~ /$reRE/xms);
39050        }
39051        if (!$ret && $ip =~ /($IPv4Re)$/o) {
39052            $ret = $^R if ('4'.unpack 'B32', pack 'C4', split /\./xms, $1)=~/$reRE/xms;
39053        }
39054    } else {
39055        ($ret) = $ip=~/($reRE)/xms;
39056    }
39057$ret = 0 unless $ret;
39058	d("matchIP: ip=$ip re=$re") if $ret && ! $donotmlog;
39059    return $ret if $re eq 'noLoggingIPs';
39060    if ( $ret && !$donotmlog) {
39061    	$this->{myheader} .= "X-Assp-$re: $ip '$ret'\r\n" if $AddIPHeader && $this->{myheader} !~ $re;
39062    	mlog( $fhh, "IP $ip" . ( $ret == 1 ? '' : " ($ret)" ) . " matches $re", 1 ) if $ipmatchLogging && $fhh;
39063    }
39064    return $ret;
39065  }
39066
39067
39068
39069
39070# check if IP address matches RE
39071sub matchIPV6 {
39072    my ( $ip, $re, $fh, $donotmlog ) = @_;
39073    my $reRE = ${ $MakeIPRE{$re} };
39074
39075
39076    return 0 unless $ip && $reRE;
39077
39078    my $this = $Con{$fh};
39079    my $ret;
39080    local $^R;
39081    use re 'eval';
39082    if ($CanMatchCIDR) {
39083        if ($ip =~ /:.*:/) {
39084            $ip =~ s/^\[([0-9a-f:]+)\].*/$1/i;
39085            $ip = ipv6expand($ip);
39086            my $ip6b = ipv6binary($ip, 128);
39087            ( '6' . $ip6b ) =~ /$reRE/xms;
39088        } else {
39089            ( '4' . unpack 'B32', pack 'C4', split /\./, $ip ) =~ /$reRE/xms;
39090        }
39091    } else {
39092        $ip =~ /$reRE/xmsi;
39093    }
39094    $ret = $^R;
39095	d("matchIP: ip=$ip re=$re") if $ret;
39096    return $ret if $re eq 'noLoggingIPs';
39097    if ( !matchIP( $ip, 'noLogging' ) && $ret && !$donotmlog) {
39098    	$this->{myheader} .= "X-Assp-$re: $ip '$ret'\r\n" if $AddIPHeader && $this->{myheader} !~ $re;
39099    	mlog( $fh, "IP $ip" . ( $ret == 1 ? '' : " ($ret)" ) . " matches $re", 1 ) if $ipmatchLogging && $fh;
39100    }
39101    return $ret;
39102}
39103
39104
39105
39106# check if email address matches RE
39107sub matchSL {
39108    my ($ad,$re,$nolog)=@_;
39109    $ad = join(' ',@$ad) if ref($ad);
39110    d("matchSL - $ad - $re",1);
39111    return 0 unless ${$re};
39112    my $reRE = ${$MakeSLRE{$re}};
39113    my $alllog;
39114    $alllog = 1 if $allLogRe && $ad =~ /$allLogReRE/ ;
39115    my $ret;
39116    $ret = $1 if $ad =~ /($reRE)/;
39117    if ($alllog or ($slmatchLogging && !$nolog && $ret) ) {
39118        my $matches = $ret ? "matches $ret": 'does not match';
39119        mlog( 0, "$ad $matches in $re", 1 );
39120    }
39121    return $ret ? 1 : 0;
39122}
39123# check if a regex matches
39124sub matchRE {
39125    my ($ad,$re,$nolog)=@_;
39126    my @ad = (ref($ad)) ? @$ad : ($ad);
39127    $lastREmatch = '';
39128    d("matchRE - @ad - $re",1);
39129    return 0 unless $re;
39130    return 0 unless ${$re.'RE'};
39131    my $reRE = ${$re.'RE'};
39132    return 0 if $reRE =~ /$neverMatchRE/o;
39133    my $alllog;
39134    $alllog = 1 if $allLogRe && grep(/$allLogReRE/,@ad );
39135    $lastREmatch = matchARRAY($reRE,\@ad);
39136    if ($alllog or ($regexLogging && !$nolog && $lastREmatch) ) {
39137        my $matches = $lastREmatch ? "matches $lastREmatch": 'does not match';
39138        mlog( 0, "@ad $matches in $re", 1 );
39139    }
39140    return $lastREmatch ? 1 : 0;
39141}
39142
39143sub matchARRAY {
39144    my ($re, $array) = @_;
39145    return unless $re;
39146    return unless eval('defined(${chr(ord("\026") << 2)}) && ref($array) eq \'ARRAY\' && scalar @$array;');
39147    my $ret;
39148    use re 'eval';
39149    foreach (@$array) {
39150        if (/($re)/) {
39151            $ret = $1;
39152            last;
39153        }
39154    }
39155    return $ret;
39156}
39157
39158# check if email address or IP address matches RE
39159sub matchSLIP {
39160    my ( $a, $ip, $re ) = @_;
39161    return matchSL( $a, $re ) || matchIP( $ip, $re );
39162}
39163
39164#returns the value for the first matching key of hash or undef
39165sub matchHashKey {
39166    my ($hash, $key, $searchall) = @_;
39167    return unless $hash;
39168    return unless %$hash;
39169    return unless $key;
39170
39171
39172    my $v;
39173    foreach my $k (reverse sort keys %{$hash}) {
39174        $v = ${$hash}{$k};
39175        last if $key eq $k;
39176        $k =~ s/\./\\./go;
39177        $k =~ s/\*/\.\*\?/go;
39178        $k =~ s/\?/\.\?/go;
39179        last if eval{$key =~ /$k/i;};
39180        mlog(0,"warning: regex error in generic hash ($hash) key ($key) match - $@") if $@;
39181        $v = undef;
39182    }
39183    return $v if $v;
39184    return $v if $hash ne 'DomainVRFYMTA';
39185
39186    $v = undef;
39187    $key = "all";
39188    foreach my $k (reverse sort keys %{$hash}) {
39189        $v = ${$hash}{$k};
39190        last if $key eq $k;
39191        $k =~ s/\./\\./go;
39192        $k =~ s/\*/\.\*\?/go;
39193        $k =~ s/\?/\.\?/go;
39194        last if eval{$key =~ /$k/i;};
39195        mlog(0,"warning: regex error in generic hash ($hash) key ($key) match - $@") if $@;
39196        $v = undef;
39197    }
39198    return $v;
39199
39200
39201}
39202#returns the key for the first matching value of hash or undef
39203sub matchHashVal {
39204    my ($hash, $val) = @_;
39205    return unless $hash;
39206    return unless $val;
39207
39208
39209    my $ret = undef;
39210
39211
39212    foreach my $k (sort {${$hash}{$main::b} <=> ${$hash}{$main::a}} keys %$hash) {
39213        $ret = $k;
39214        my $v = ${$hash}{$k};
39215        last if lc($val) eq lc($v);
39216        $v =~ s/([^\\])?\./$1\\./go;
39217        $v =~ s/([^)\]])?\*/$1\.\*\?/go;
39218        $v =~ s/([^()\]])?\?/$1\.\?/go;
39219        last if eval{$val =~ /$v/i;};
39220        mlog(0,"warning: regex error in generic hash ($hash) value ($val) match - $@") if $@;
39221        $ret = undef;
39222    }
39223    return $ret;
39224}
39225
39226sub min {
39227    return [sort {$main::a <=> $main::b} @_]->[0];
39228}
39229
39230sub max {
39231    return [sort {$main::b <=> $main::a} @_]->[0];
39232}
39233
39234sub checkFileHashUpdate {
39235    d('checkFileHashUpdate');
39236    my $ret = 0;
39237    while (my ($file,$ftime) = each %FileHashUpdateTime) {
39238       next if $ftime == ftime($file);
39239       &LoadHash($FileHashUpdateHash{"$file"},$file,0);
39240       $ret = 1;
39241    }
39242    return $ret;
39243}
39244
39245# this checks and corrects a | separated list
39246# and handles the options in a file
39247sub checkOptionList {
39248    my ($value,$name,$init)=@_;
39249    my $fromfile=0;
39250    my $fil;
39251    my $count;
39252    if ($value=~/^ *file: *(.+)/i) {
39253
39254        # the option list is actually saved in a file.
39255        $fromfile=1;
39256        $fil=$1; $fil="$base/$fil" if $fil!~/^\Q$base\E/i;
39257        local $/;
39258        my @s=stat($fil);
39259        my $mtime=$s[9];
39260        $FileUpdate{$fil}=$mtime;
39261        $FileUpdate{"$fil$name"} = $mtime;
39262        my $COL;
39263        if (!open($COL,'<',$fil)) {
39264        	open($COL,'>',$fil);
39265        	close $COL;
39266        }
39267        if (open($COL,'<',$fil)) {
39268            $value=<$COL>;
39269            close $COL;
39270            $value =~ s/^$UTF8BOMRE//o;
39271
39272            %{$FileIncUpdate{"$fil$name"}} = ();
39273
39274            while ($value =~ /(\s*#\s*include\s+([^\r\n]+)\r?\n)/io) {
39275
39276                my $line = $1;
39277                my $ifile = $2;
39278                $ifile =~ s/([^\\\/])[#;].*/$1/go;
39279                $ifile =~ s/[\"\' ]//go;
39280                my $INCL;
39281                if (!open($INCL,'<',"$base/$ifile")) {
39282        			open($INCL,'>',"$base/$ifile");
39283        			close $INCL;
39284        		}
39285                unless (open($INCL,'<',"$base/$ifile")) {
39286                    $value =~ s/$line//;
39287                    mlog(0,"AdminInfo: failed to open option list include file for reading '$base/$ifile' ($name): $!") if (!$init && ! $calledfromThread);
39288                    $FileIncUpdate{"$fil$name"}{$ifile} = 0;
39289                    next;
39290                }
39291                my $inc = <$INCL>;
39292                close $INCL;
39293                $inc =~ s/^$UTF8BOMRE//o;
39294                $inc = "\n$inc\n";
39295                $value =~ s/$line/$inc/;
39296                @s=stat($ifile);
39297                $mtime=$s[9];
39298                $FileIncUpdate{"$fil$name"}{$ifile} = $mtime;
39299                mlog(0,"AdminInfo: option list include file '$ifile' processed for ($name)") if (!$init);
39300            }
39301
39302     		# clean off comments
39303
39304            $value =~ s/\s;\s.*//g;
39305            $value =~ s/^#.*//g;
39306            $value =~ s/([^\\])#.*/$1/g;
39307
39308            # replace newlines (and the whitespace that surrounds them) with a |
39309
39310            $value=~s/\r//g;
39311
39312            $value=~s/\s*\n+\s*/\|/g;
39313
39314        } else {
39315            mlog(0,"AdminInfo: failed to open option list file for reading '$fil' ($name): $!") if (!$init && ! $calledfromThread);
39316            $value='';
39317        }
39318    }
39319    $value=~s/\*\*\*/\\\*\\\*\\\*/g;
39320    $value=~s/\*\*/\\\*\\\*/g;
39321    $value=~s/\|\|/\|/g;
39322    $value=~s/\s*\|/\|/g;
39323    $value=~s/\|\s*/\|/g;
39324    $value=~s/\|\|+/\|/g;
39325    $value=~s/^\s*\|?//;
39326    $value=~s/\|?\s*$//;
39327    $value=~s/\|$//;
39328    $value=~s/^\|?//;
39329
39330
39331	my $count = () = $value =~ /([^\\]\|)/go;
39332    $count++ if $value;
39333
39334
39335
39336#    mlog(0,"option list file: '$fil' reloaded ($name) with $count records") if 	!$init && $fromfile;
39337
39338    # set corrected value back in Config
39339
39340    ${$name}=$Config{$name}=$value unless $fromfile;
39341    return $value;
39342}
39343
39344sub ConfigCompileNotifyRe {
39345    my ($name, $old, $new, $init)=@_;
39346    my $note = "AdminUpdate: $name changed from '$old' to '$new'";
39347    $note = "AdminUpdate: $name changed" if exists $cryptConfigVars{$name};
39348    mlog(0,$note) unless $init || $new eq $old;
39349    ${$name} = $new;
39350    $new=checkOptionList($new,$name,$init);
39351
39352    if ($new) {
39353        %NotifyRE = ();
39354        my @entry = split(/\|/o,$new);
39355        while (@entry) {
39356            my $e = shift(@entry);
39357            my ($re,$adr,$sub) = split(/\=\>/o,$e);
39358            $NotifySub{$re} = $sub if $sub;
39359            if ($adr) {
39360                $NotifyRE{$re} = $adr;
39361            } else {
39362                $NotifyRE{$re} = $Notify if $Notify;
39363            }
39364        }
39365        $new = join('|', keys %NotifyRE);
39366    } else {
39367        $new ='^(?!)'; # regexp that never matches
39368    }
39369    # trim long matches to 32 chars including '...' at the end
39370
39371    SetRE($name.'RE',$new,'is',$name);
39372#    SetRE($name.'RE',"($new)(?{\$1 and length(\$1)>32?substr(\$1,0,32-3).'...':\$1})",'is',$name);
39373    '';
39374}
39375
39376sub ConfigCompileRe {
39377    my ($name, $old, $new, $init)=@_;
39378    my $note = "AdminUpdate: $name changed from '$old' to '$new'";
39379	my ($defaultHow,$how,$re,$we);
39380    mlog(0,$note) unless $init || $new eq $old;
39381    my $orgnew = $new;
39382    $new=checkOptionList($new,$name,$init);
39383
39384    if ($name eq "MyCountryCodeRe" && !$new && $localhostip && $localhostip !~ /$IPprivate/o) {
39385        $new = SenderBaseMyIP($localhostip);
39386    }
39387    my $count;
39388	if ($name eq "AllowLocalAddressesRe" ) {
39389		if ($new ne "") {
39390			$count = () = $new =~ /\|/g;
39391			$count++;
39392		} else {
39393		 	$count = 0;
39394		}
39395        $AllowLocalAddressesReCount=$count;
39396
39397    }
39398
39399    if (exists $WeightedRe{$name}) {
39400
39401        $defaultHow = $1 if $new =~ s/\s*!!!\s*([nNwWlLiI\+\-\s]+)?\s*!!!\s*\|?//o;
39402        $defaultHow =~ s/\s//go;
39403        $defaultHow =~ s/\++/+/go;
39404        $defaultHow =~ s/\-+/-/go;
39405        $WeightedReOverwrite{$name} = 0;
39406        my @Weight = @{$name.'Weight'};
39407        my @WeightRE = @{$name.'WeightRE'};
39408        @{$name.'Weight'} = ();
39409        @{$name.'WeightRE'} = ();
39410
39411        while ($new =~ s/(\~([^\~]+)?\~|([^\|]+)?)\s*\=\>\s*(-{0,1}\d+\.*\d*)?(?:\s*\:\>\s*([nNwWlLiI\+\-\s]+)?)?/$2$3/o) {
39412            $re = ($2?$2:'').($3?$3:'');
39413            $we = $4;
39414            $we = 1 if (!$we && $we != 0);
39415            $how = uc $5;
39416            $how =~ s/\s//go;
39417            $how =~ s/\++/+/go;
39418            $how =~ s/\-+/-/go;
39419            $how ||= $defaultHow;
39420
39421
39422
39423            eval{$note =~ /$re/};
39424            if ($@) {
39425                mlog(0,"error: weighted regex for $name is invalid '$re=>$we' - $@") if $WorkerNumber == 0;
39426                mlog(0,"warning: value for $name was not changed - all changes are ignored") if $WorkerNumber == 0;
39427                @{$name.'Weight'} = @Weight;
39428                @{$name.'WeightRE'} = @WeightRE;
39429                $new = $old;
39430                return "<span class=\"negative\"> - weighted regex for $name is invalid '$re=>$we'!</span>";
39431            }
39432            push (@{$name.'WeightRE'},$re);
39433            push (@{$name.'Weight'},$we);
39434        }
39435
39436         if ($name =~ /bomb|script|black/o && $how) {
39437                if ($how =~ /[nN][^\-]?/o) {
39438                    $WeightedReOverwrite{$name} |= 1;
39439                }
39440                if ($how =~ /[wW][^\-]?/o) {
39441                    $WeightedReOverwrite{$name} |= 2;
39442                }
39443                if ($how =~ /[lL][^\-]?/o) {
39444                    $WeightedReOverwrite{$name} |= 4;
39445                }
39446                if ($how =~ /[iI][^\-]?/o) {
39447                    $WeightedReOverwrite{$name} |= 8;
39448                }
39449            } elsif ($name =~ /Reversed/o && $how) {
39450                if ($how =~ /[nN][^\-]?/o) {
39451                    $WeightedReOverwrite{$name} |= 1;
39452                }
39453                if ($how =~ /[wW][^\-]?/o) {
39454                    $WeightedReOverwrite{$name} |= 2;
39455                }
39456            } elsif ($name =~ /Helo/o && $how) {
39457                if ($how =~ /[nN][^\-]?/o) {
39458                    $WeightedReOverwrite{$name} |= 1;
39459                }
39460                if ($how =~ /[wW][^\-]?/o) {
39461                    $WeightedReOverwrite{$name} |= 2;
39462                }
39463            }
39464            push (@{$name.'WeightRE'},'{'.$how.'}'.$re);
39465            push (@{$name.'Weight'},$we);
39466        }
39467        my $count = 0;
39468        foreach my $k (@{$name.'Weight'}) {
39469            my $reg = ${$name.'WeightRE'}[$count];
39470            my $how;$how = $1 if $reg =~ s/^\{([^\}]*)?\}(.+)$/$2/o;
39471            $reg =~ s/^\<\<\<(.*?)\>\>\>$/$1/go;
39472            strip50($reg);
39473            $how = " for [$how]" if $how;
39474            mlog(0,"info: $name : regex '$reg' - weight set to '$k$how'") if $MaintenanceLog == 3 && $how;
39475            $count++;
39476        }
39477		$new||='^(?!)'; # regexp that never matches
39478    	# trim long matches to 32 chars including '...' at the end
39479		eval {
39480    	SetRE($name.'RE',"($new)(?{length(\$1)>32?substr(\$1,0,32-3).'...':\$1})",'is',$name);
39481		};
39482		mlog( 0,"error : $name invalid") if $@;
39483
39484
39485    '';
39486}
39487
39488
39489
39490
39491sub optionList {
39492
39493    # this converts a | separated list into a RE
39494    my ( $d, $configname ) = @_;
39495    $d = checkOptionList( $d, $configname );
39496    $d =~ s/([\.\[\]\-\(\)\*\+\\])/\\$1/g;
39497    $MakeRE{$configname}->($d);
39498    $d;
39499}
39500
39501sub ConfigOverwriteRe {
39502
39503}
39504
39505sub fileUpdated {
39506
39507    my ( $fil, $configname ) = @_;
39508
39509    $fil = "$base/$fil" if $fil !~ /^(([a-z]:)?[\/\\]|\Q$base\E)/;
39510
39511    return 0 unless (-e $fil);
39512    return 1 unless $FileUpdate{"$fil$configname"};
39513    my @s     = stat($fil);
39514    my $mtime = $s[9];
39515    my $changed = $FileUpdate{"$fil$configname"} != $mtime;
39516    return 1 if $changed;
39517    $changed = fileIncUpdated($fil,$configname);
39518    return $changed;
39519}
39520
39521sub fileIncUpdated {
39522
39523    my ( $fil, $configname ) = @_;
39524    return 0 unless exists $FileIncUpdate{"$fil$configname"};
39525
39526    my $changed = 0;
39527    foreach my $f (keys %{$FileIncUpdate{"$fil$configname"}}) {
39528        my @s     = stat($f);
39529        my $mtime = $s[9];
39530        next if $FileIncUpdate{"$fil$configname"}{$f} == $mtime;
39531        $changed = 1;
39532        last;
39533    }
39534    return $changed;
39535}
39536
39537sub ConfigChangeUSAMN {
39538    my ($name, $old, $new, $init)=@_;
39539
39540    mlog(0,"AdminUpdate: $name from '$old' to '$new'") unless $init || $new eq $old;
39541    $Config{$name} = $new;
39542    $$name = $new;
39543    &ConfigChangeMaxAllowedDups('MaxAllowedDups',$MaxAllowedDups,$MaxAllowedDups,'');
39544}
39545
39546sub ConfigChangeNoDomains {
39547    my ($name, $old, $new, $init)=@_;
39548
39549    mlog(0,"AdminUpdate: $name from '$old' to '$new' ") unless $init || $new eq $old;
39550
39551    $Config{$name} = $new;
39552    $$name = $new;
39553    $asspWarnings = '';
39554    mlog(0,"warning: nolocalDomains is set, ASSP will not check relaying") if $nolocalDomains;
39555    $asspWarnings .= '<span class="negative">nolocalDomains is set, ASSP is open relay<br /></span>' if $nolocalDomains;
39556
39557	$asspWarnings .= '<span class="negative">no local domains set in localDomains or ldLDAP<br /></span>' if !$localDomains && !$ldLDAP && !$DoLocalIMailDomains;
39558
39559}
39560
39561sub ConfigChangeMaxAllowedDups {
39562    my ($name, $old, $new, $init)=@_;
39563
39564    my $count = -1;
39565    if ($new && $Config{UseSubjectsAsMaillogNames} && $Config{spamlog} && $Config{discarded}) {
39566        $count++;
39567        my @files = map {my $t=$_;$t=~s/^\Q$base\E[\\\/]\Q$spamlog\E[\\\/]([^\\\/]*)(__|--)\d+$maillogExt$/$1/;$t}  (glob("$base/$spamlog/*"));
39568        foreach (@files) {
39569            next unless $_;
39570            next if /\\|\//;
39571            next if -d "$_";
39572            next if $_ eq '.';
39573            next if $_ eq '..';
39574            $Spamfiles{Digest::MD5::md5($_)}++;
39575            $count++;
39576        }
39577    } else {
39578        %Spamfiles = ();
39579    }
39580    mlog(0,"AdminUpdate: $name from '$old' to '$new' - $count files registered in $Config{spamlog} folder") unless $init || $new eq $old;
39581
39582    $Config{$name} = $new;
39583    $$name = $new;
39584}
39585
39586sub configChangeIC {
39587    my ($name, $old, $new, $init)=@_;
39588    mlog(0,"AdminUpdate: inbound charset conversion Table updated from '$old' to '$new'") unless $init || $new eq $old;
39589#    $inChrSetConv=$new;
39590    $new=checkOptionList($new,'inChrSetConv',$init);
39591
39592    my $f;
39593
39594    my $t;
39595
39596    my $test="abc";
39597    my $error;
39598    for my $v (split(/\|/o,$new)) {
39599        $v=~/(.*)\=\>(.*)/;
39600        my $fa=$1;
39601        my $ta=$2;
39602        eval{$f='';$f=Encode::resolve_alias(uc($fa));};
39603        eval{$t='';$t=Encode::resolve_alias(uc($ta));};
39604        if (! $f) {
39605            mlog(0,"error: codepage $fa is not supported by perl in inChrSetConv");
39606            $error .= "$fa ";
39607            next;
39608        }
39609        if (! $t) {
39610            mlog(0,"error: codepage $ta is not supported by perl in inChrSetConv");
39611            $error .= "$ta ";
39612            next;
39613        }
39614        eval{Encode::from_to($test,$f,$t)};
39615        if ($@) {
39616            mlog(0,"error: perl is unable to convert from $f/fa to $t/ta in inChrSetConv - this conversion will be ignored");
39617            $error .= "$fa $ta ";
39618            next;
39619        } else {
39620            $inchrset{$f} = $t;
39621        }
39622    }
39623    $error = " - but error in $error - please check the log" if ($error);
39624    return $error;
39625}
39626
39627sub configChangeOC {
39628    my ($name, $old, $new, $init)=@_;
39629    mlog(0,"AdminUpdate: outbound charset conversion Table updated from '$old' to '$new'") unless $init || $new eq $old;
39630#    $outChrSetConv=$new;
39631    $new=checkOptionList($new,'outChrSetConv',$init);
39632
39633    my $f;
39634
39635    my $t;
39636
39637    my $test="abc";
39638    my $error;
39639    for my $v (split(/\|/o,$new)) {
39640        $v=~/(.*)\=\>(.*)/;
39641        my $fa=$1;
39642        my $ta=$2;
39643        eval{$f='';$f=Encode::resolve_alias(uc($fa));};
39644        eval{$t='';$t=Encode::resolve_alias(uc($ta));};
39645        if (! $f) {
39646            mlog(0,"error: codepage $fa is not supported by perl in outChrSetConv");
39647            $error .= "$fa ";
39648            next;
39649        }
39650        if (! $t) {
39651            mlog(0,"error: codepage $ta is not supported by perl in outChrSetConv");
39652            $error .= "$ta ";
39653            next;
39654        }
39655        eval{Encode::from_to($test,$f,$t)};
39656        if ($@) {
39657            mlog(0,"error: perl is unable to convert from characterset $f to $t in outChrSetConv - this conversion will be ignored");
39658            $error .= "$fa $ta ";
39659            next;
39660        } else {
39661            $outchrset{$f} = $t;
39662        }
39663    }
39664    $error = " - but error in $error - please check the log" if ($error);
39665    return $error;
39666}
39667sub configChangeRT {
39668    my ( $name, $old, $new, $init ) = @_;
39669    mlog( 0,
39670"AdminUpdate: SMTP Destination Routing Table updated from '$old' to '$new'"
39671    ) unless $init || $new eq $old;
39672    $smtpDestinationRT = $new;
39673    $new = checkOptionList( $new, 'smtpDestinationRT', $init );
39674
39675    for my $v ( split( /\|/, $new ) ) {
39676        $v =~ /(.*)\=\>(.*)/;
39677        $crtable{$1} = $2;
39678    }
39679
39680}
39681
39682
39683
39684sub ConfigChangePrimaryMX {
39685    my ( $name, $old, $new ) = @_;
39686    mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" );
39687    if ($new) {
39688        replaceHostByIP(\$new,$name) if $name eq "PrimaryMX";
39689        $new =~ s/\|\|/\|/go;
39690    }
39691    $$name = $new;
39692    $check4queuetime=time-1;
39693    return '';
39694}
39695
39696sub ConfigChangeMailPort {my ($name, $old, $new, $init)=@_;
39697
39698    my $highport = 1;
39699    return if $new eq $old;
39700    foreach my $port (split(/\|/o,$new)) {
39701        if ($port =~ /^.+:([^:]+)$/o) {
39702            if ($1 < 1024) {
39703                $highport = 0;
39704                last;
39705            }
39706        } else {
39707            if ($port < 1024) {
39708                $highport = 0;
39709                last;
39710            }
39711        }
39712    }
39713    $Config{listenPort}=$listenPort=$new;
39714    if($> == 0 || $highport || $^O eq "MSWin32") {
39715
39716        # change the listenport
39717
39718        foreach my $lsn (@lsn ) {
39719            unpoll($lsn,$readable);
39720            unpoll($lsn,$writable);
39721            close($lsn);
39722            delete $SocketCalls{$lsn};
39723        }
39724        my ($lsn,$lsnI) = newListen($listenPort,\&NewSMTPConnection);
39725        @lsn = @$lsn; @lsnI = @$lsnI;
39726        for (@$lsnI) {s/:::/\[::\]:/o;}
39727        mlog(0,"AdminUpdate: listening on new mail port @$lsnI (changed from $old) ");
39728        return '';
39729    } else {
39730
39731        # don't have permissions to change
39732        mlog(0,"AdminUpdate: request to listen on new mail port $new (changed from $old) -- restart required; euid=$>");
39733        return "<br />Restart required; euid=$><script type=\"text/javascript\">alert(\'new mail port - ASSP-Restart required\');</script>";
39734    }
39735}
39736
39737sub ConfigChangeMailPort2 {my ($name, $old, $new, $init)=@_;
39738    my $highport = 1;
39739    return if $new eq $old;
39740    foreach my $port (split(/\|/o,$new)) {
39741        if ($port =~ /^.+:([^:]+)$/o) {
39742            if ($1 < 1024) {
39743                $highport = 0;
39744                last;
39745            }
39746        } else {
39747            if ($port < 1024) {
39748                $highport = 0;
39749                last;
39750            }
39751        }
39752    }
39753    $Config{listenPort2}=$listenPort2=$new;
39754    if($> == 0 || $highport || $^O eq "MSWin32") {
39755
39756        # change the listenport2
39757        foreach my $lsn2 (@lsn2 ) {
39758            unpoll($lsn2,$readable);
39759            unpoll($lsn2,$writable);
39760            close($lsn2);
39761            delete $SocketCalls{$lsn2};
39762        }
39763        my ($lsn2,$lsn2I) = newListen($listenPort2,\&NewSMTPConnection);
39764        @lsn2 = @$lsn2; @lsn2I = @$lsn2I;
39765        for (@$lsn2I) {s/:::/\[::\]:/o;}
39766        mlog(0,"AdminUpdate: listening on new secondary mail port @$lsn2I (changed from $old)");
39767        return '';
39768    } else {
39769
39770        # don't have permissions to change
39771        mlog(0,"AdminUpdate: request to listen on new secondary mail port $new (changed from $old) -- restart required; euid=$>");
39772        return "<br />Restart required; euid=$><script type=\"text/javascript\">alert(\'new secondary mail port - ASSP-Restart required\');</script>";
39773    }
39774}
39775
39776sub ConfigChangeMailPortSSL {
39777    my ( $name, $old, $new , $init) = @_;
39778    my $highport = 1;
39779    return if $new eq $old;
39780    foreach my $port (split(/\|/o,$new)) {
39781        if ($port =~ /^.+:([^:]+)$/o) {
39782            if ($1 < 1024) {
39783                $highport = 0;
39784                last;
39785            }
39786        } else {
39787            if ($port < 1024) {
39788                $highport = 0;
39789                last;
39790            }
39791        }
39792    }
39793    $Config{listenPortSSL}=$listenPortSSL = $new;
39794    if($> == 0 || $highport || $^O eq "MSWin32") {
39795
39796       	# change the listenportSSL
39797
39798        foreach my $lfh (@lsnSSL) {
39799            unpoll($lfh,$readable,"POLLIN");
39800            unpoll($lfh,$writable,"POLLIN");
39801            delete $SocketCalls{$lfh};
39802            close($lfh);
39803        }
39804        if ($CanUseIOSocketSSL) {
39805            my ($lsnSSL,$lsnSSLI) = newListenSSL($listenPortSSL,\&NewSMTPConnection);
39806            @lsnSSL = @$lsnSSL; @lsnSSLI = @$lsnSSLI;
39807            for (@$lsnSSLI) {s/:::/\[::\]:/o;}
39808            mlog( 0,"AdminUpdate: listening on new SSL mail port @$lsnSSLI (changed from '$old')");
39809        } else {
39810            mlog( 0,"AdminUpdate: new SSL mail port '$listenPortSSL' (changed from '$old')");
39811        }
39812        return '';
39813    } else {
39814
39815        # don't have permissions to change
39816        mlog( 0,
39817"AdminUpdate: request to listen on new SSL mail port '$new' (changed from '$old') -- restart required; euid=$>"
39818        );
39819        return "<br />Restart required; euid=$>";
39820    }
39821}
39822
39823sub ConfigChangeEnableAdminSSL {my ($name, $old, $new, $init)=@_;
39824    if ($new) {
39825        if (! -e $SSLCertFile) {
39826            $new = $old = 0;
39827            $enableWebAdminSSL = $new;
39828            $Config{enableWebAdminSSL} = $new;
39829            return "<span class=\"negative\">Couldn't find file $base/certs/server-cert.pem</span>";
39830        }
39831        if (! -e $SSLKeyFile) {
39832            $new = $old = 0;
39833            $enableWebAdminSSL = $new;
39834            $Config{enableWebAdminSSL} = $new;
39835            return "<span class=\"negative\">Couldn't find file $base/certs/server-key.pem</span>";
39836        }
39837        if (! $CanUseIOSocketSSL) {
39838            $new = $old = 0;
39839            $enableWebAdminSSL = $new;
39840            $Config{enableWebAdminSSL} = $new;
39841            return "<span class=\"negative\">Module IO::Socket::SSL is not installed</span>";
39842        }
39843    }
39844    my $usessln;
39845    my $usesslo;
39846    if ($new ne $old) {
39847        $usessln = $new ? 'HTTPS' : 'HTTP';
39848        $usesslo = $new ? 'HTTP' : 'HTTPS';
39849        $httpchanged = 1;
39850        mlog(0,"AdminUpdate: listening on admin port $usessln (changed from $usesslo)");
39851    }
39852    $enableWebAdminSSL = $new;
39853    $Config{enableWebAdminSSL} = $new;
39854    &ConfigChangeAdminPort('webAdminPort', $webAdminPort, $webAdminPort,'renew');
39855    '';
39856}
39857
39858sub ConfigChangeEnableStatSSL {my ($name, $old, $new, $init)=@_;
39859    if ($new) {
39860        if (! -e $SSLCertFile) {
39861            $new = $old = 0;
39862            $enableWebAdminSSL = $new;
39863            $Config{enableWebAdminSSL} = $new;
39864            return "<span class=\"negative\">Couldn't find file $base/certs/server-cert.pem</span>";
39865        }
39866        if (! -e $SSLKeyFile) {
39867            $new = $old = 0;
39868            $enableWebAdminSSL = $new;
39869            $Config{enableWebAdminSSL} = $new;
39870            return "<span class=\"negative\">Couldn't find file $base/certs/server-key.pem</span>";
39871        }
39872        if (! $CanUseIOSocketSSL) {
39873            $new = $old = 0;
39874            $enableWebAdminSSL = $new;
39875            $Config{enableWebAdminSSL} = $new;
39876            return "<span class=\"negative\">Module IO::Socket::SSL is not installed</span>";
39877        }
39878    }
39879    if ($new ne $old) {
39880        my $usessln = $new ? 'HTTPS' : 'HTTP';
39881        my $usesslo = $new ? 'HTTP' : 'HTTPS';
39882        mlog(0,"AdminUpdate: listening on stat port $usessln (changed from $usesslo)");
39883    }
39884    $enableWebStatSSL = $Config{enableWebStatSSL} = $new;
39885    &ConfigChangeStatPort('webStatPort', $webStatPort, $webStatPort,'renew');
39886}
39887
39888sub getSSLPWD {
39889    return $SSLPKPassword;
39890}
39891
39892sub getSSLParms {
39893    my %ssl;
39894    if (shift) {
39895        $ssl{SSL_server} = 1;
39896        $ssl{SSL_use_cert} = 1;
39897        $ssl{SSL_cert_file} = $SSLCertFile;
39898        $ssl{SSL_key_file} = $SSLKeyFile;
39899        $ssl{SSL_ca_file} = $SSLCaFile if $SSLCaFile;
39900        $ssl{SSL_passwd_cb} = \&getSSLPWD if getSSLPWD();
39901    }
39902    if ($SSL_cipher_list) {
39903        $ssl{SSL_cipher_list} = $SSL_cipher_list;
39904        $ssl{SSL_honor_cipher_order} = 1;
39905    }
39906    $ssl{SSL_version} = $SSL_version if $SSL_version;
39907    $ssl{SSL_verify_mode} = 0x00;
39908    $ssl{Timeout} = $SSLtimeout;
39909
39910    return %ssl;
39911}
39912sub ConfigChangeSSL {
39913    my ( $name, $old, $new ,$init) = @_;
39914
39915    if ($new ne $old) {
39916        $new =~ s/\\/\//go;
39917        $old =~ s/\\/\//go;
39918        $Config{$name} = ${$name} = $new;
39919        if ((-f $new && -r $new) || $name eq 'SSLCaFile' || $name eq 'SSL_version' || $name eq 'SSL_cipher_list') {
39920            mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" ) unless $init;
39921            if (-r $SSLCertFile and -r $SSLKeyFile and $AvailIOSocketSSL) {
39922                $CanUseIOSocketSSL = 1;
39923                if ($listenPortSSL) {
39924                    &ConfigChangeMailPortSSL('listenPortSSL','n/a',$listenPortSSL, 1);
39925                }
39926                if ($enableWebAdminSSL) {
39927                    &ConfigChangeAdminPort('webAdminPort','n/a',$webAdminPort, 1);
39928                }
39929                if ($enableWebStatSSL) {
39930                    &ConfigChangeStatPort('webStatPort','n/a',$webStatPort, 1);
39931                }
39932            }
39933            return '';
39934        } else {
39935            $Config{$name} = ${$name} = $old;
39936            mlog( 0, "AdminUpdate: $name not changed from '$old' to '$new' - file $new not found or unreadable" ) unless $init;
39937            return "<span class=\"negative\">file $new not found or unreadable</span>";
39938        }
39939    }
39940}
39941
39942sub ConfigChangeTLSPorts {my ($name, $old, $new, $init)=@_;
39943    return '' if $new eq $old && ! $init;
39944
39945    $$name = $Config{$name} = $new unless $WorkerNumber;
39946    mlog(0,"AdminUpdate: $name changed to $new from $old") if $WorkerNumber == 0 && ! $init;
39947    my $listen = $name eq 'NoTLSlistenPorts' ? 'lsnNoTLSI' : 'TLStoProxyI';
39948    fillPortArray($listen, $new);
39949    return '';
39950}
39951
39952sub ConfigChangeNoAUTHPorts {my ($name, $old, $new, $init)=@_;
39953    return '' if $new eq $old && ! $init;
39954
39955    $$name = $Config{$name} = $new unless $WorkerNumber;
39956    mlog(0,"AdminUpdate: $name changed to $new from $old") if $WorkerNumber == 0 && ! $init;
39957    my $listen = 'lsnNoAUTH';
39958    fillPortArray($listen, $new);
39959    return '';
39960}
39961
39962
39963sub ConfigChangeEnableSSL {
39964    my ( $name, $old, $new ) = @_;
39965    my $ver;
39966    mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" );
39967    ${$name} = $new;
39968	$DoTLS = 2 if $new;
39969	$DoTLS = 0 if !$new;
39970    if ($AvailIOSocketSSL) {
39971        $ver            = eval('IO::Socket::SSL->VERSION');
39972        $VerIOSocketSSL = $ver;
39973		if ($VerIOSocketSSL < 1.08) {
39974	    	$CommentIOSocketSSL = "<span class=negative>Version >= 1.08 required - SSL support not available";
39975	    	mlog( 0, "IO::Socket::SSL module$ver installed - Version >= 1.08 required, SSL support not available ");
39976	    	$AvailIOSocketSSL = 0;
39977     	} else {
39978            $ver            = " version $ver" if $ver;
39979            $CommentIOSocketSSL = "<span class=positive>Secure SSL sockets installed";
39980            mlog( 0, "IO::Socket::SSL module$ver installed");
39981            if (-e $SSLCertFile and -e $SSLKeyFile) {
39982        		mlog(0,"found valid certificate and private key file - SSL on listenPortSSL is available");
39983        		mlog(0,"TLS on listenports is switched off by enableSSL") if !$new;
39984        		mlog(0,"TLS on listenports is switched on by enableSSL") if $new;
39985    		} else {
39986
39987        		if (-e $SSLCertFile and -e $SSLKeyFile) {
39988            		mlog(0,"found valid certificate and private key file - TLS/SSL is available");
39989        		} else {
39990            		$CanUseIOSocketSSL = 0;
39991            		mlog(0,"warning: certificate $SSLCertFile not found") unless (-e $SSLCertFile);
39992            		mlog(0,"warning: public-key $SSLKeyFile not found") unless (-e $SSLKeyFile);
39993            		mlog(0,"warning: TLS/SSL is disabled");
39994            		$CommentIOSocketSSL = "<span class=negative>Version >= TLS/SSL is disabled, no certificate found";
39995
39996        		}
39997    		}
39998
39999        }
40000        my $commentIOSocketSSL=$CommentIOSocketSSL;
40001        $commentIOSocketSSL =~ s/\<.*\>//;
40002        mlog( 0, "AdminUpdate: $commentIOSocketSSL" );
40003    } else {
40004        $CommentIOSocketSSL = "<span class=negative>Secure SSL sockets not installed";
40005        mlog( 0,
40006            "IO::Socket::SSL module not installed"
40007        );
40008    }
40009}
40010
40011
40012
40013
40014sub ConfigChangeIPv6 {
40015    my ( $name, $old, $new, $init) = @_;
40016    mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" ) unless $init || $new eq $old;
40017    $Config{$name} = ${$name} = $new;
40018    return '' if $init || $new eq $old;
40019    if ($enableINET6) {
40020        $CanUseIOSocketINET6 =
40021            $AvailIOSocketINET6;
40022
40023        if ($CanUseIOSocketINET6) {
40024            $CommentIOSocketINET6 = "IPv6 support enabled, Restart required";
40025
40026        } else {
40027            $CommentIOSocketINET6 = "IPv6 support enabled, Restart required";
40028
40029        }
40030    } else {
40031    		$CanUseIOSocketINET6 = 0;
40032            $CommentIOSocketINET6 = "IPv6 support disabled, Restart required";
40033    }
40034    mlog( 0, "AdminUpdate: $CommentIOSocketINET6" );
40035    return	"& $CommentIOSocketINET6";
40036}
40037
40038sub ConfigChangeSecondary {
40039    my ( $name, $old, $new ) = @_;
40040    mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" );
40041	$AutostartSecondary = $new;
40042    $Config{AutostartSecondary} = $new;
40043	&readSecondaryPID();
40044    if ($new) {
40045    	&startSecondary();
40046        return	"& starting Secondary" if !$SecondaryPid;
40047        return	"& running (PID: $SecondaryPid)" if $SecondaryPid;
40048
40049    } else {
40050  		unlink("$base/$pidfile"."_Secondary");
40051
40052		kill TERM => $SecondaryPid if $SecondaryPid;
40053#		exec("kill -TERM $SecondaryPid") if $SecondaryPid;
40054		return "& terminating Secondary (PID: $SecondaryPid)" if $SecondaryPid;
40055		return "& Secondary (PID: $SecondaryPid) not running" if !$SecondaryPid;
40056		&startSecondary() if  $AutostartSecondary && !$AsASecondary && $webSecondaryPort;
40057 	}
40058
40059}
40060
40061sub startWatchdog {
40062	my $perl = $^X;
40063	my $cmd = "\"$perl\" \"$base/assp_watchdog.pl\" \"$base\" 2>&1 &";
40064	$cmd =~ s/\//\\/g if $^O eq "MSWin32";
40065	mlog( 0, "Info: Command '$cmd' started watching ASSP" ) if $EnableWatchdog;
40066	system($cmd) if $EnableWatchdog;
40067
40068}
40069sub readWatchdogPID {
40070
40071		open my $PID, "<","$base/$pidfile". "_Watchdog";
40072		my $Pid = <$PID>;
40073    	close $PID;
40074    	$Pid =~ s/\r|\n|\s//go;
40075
40076		return $Pid;
40077}
40078sub ConfigChangeWatchdog {
40079    my ( $name, $old, $new, $init ) = @_;
40080    mlog( 0, "AdminUpdate: $name changed from '$old' to '$new'" );
40081    $Config{$name} = ${$name} = $new;
40082    return '' if $init || $new eq $old;
40083	my $WatchdogPID = &readWatchdogPID();
40084    kill TERM => $WatchdogPID;
40085	&writeWatchdog if $EnableWatchdog;
40086	&startWatchdog if $EnableWatchdog;
40087
40088}
40089sub fillPortArray {
40090    my ($listen, $new) = @_;
40091    return unless $listen;
40092    @{$listen} = ();
40093    return unless $new;
40094    my ($interface,$p);
40095    if ($new=~/\|/o) {
40096        foreach my $portA (split(/\|/o, $new)) {
40097            ($interface,$p)=$portA=~/^(.*):([^:]*)$/o;
40098            $interface =~ s/\s//go;
40099            $p =~ s/\s//go;
40100            $portA =~ s/\s//go;
40101            if ($interface) {
40102                push @{$listen}, "$interface:$p";
40103            } else {
40104                push @{$listen}, "0.0.0.0:$portA";
40105                push @{$listen}, "[::]:$portA" if $CanUseIOSocketINET6;
40106            }
40107        }
40108    } else {
40109        ($interface,$p)=$new=~/(.*):([^:]*)/o;
40110        $interface =~ s/\s//go;
40111        $p =~ s/\s//go;
40112        $new =~ s/\s//go;
40113        if ($interface) {
40114            push @{$listen}, "$interface:$p";
40115        } else {
40116            push @{$listen}, "0.0.0.0:$new";
40117            push @{$listen}, "[::]:$new" if $CanUseIOSocketINET6;
40118        }
40119    }
40120}
40121
40122sub ConfigChangeAdminPort {
40123    my ( $name, $old, $new, $init ) = @_;
40124    my $usessl;
40125    my $highport = 1;
40126    my $dummy;
40127    my $WebSocket;
40128    return if $new eq $old && ! $init;
40129
40130    foreach my $port (split(/\|/o,$new)) {
40131        if ($port =~ /^.+:([^:]+)$/o) {
40132            if ($1 < 1024) {
40133                $highport = 0;
40134                last;
40135            }
40136        } else {
40137            if ($port < 1024) {
40138                $highport = 0;
40139                last;
40140            }
40141        }
40142    }
40143
40144    $webAdminPort=$Config{webAdminPort}=$new;
40145    my $adminport = $webAdminPort;
40146    $adminport = $webSecondaryPort if $AsASecondary && $webSecondaryPort;
40147    if($> == 0 || $highport || $^O eq "MSWin32") {
40148        # change the listenport
40149
40150        foreach my $WebSock (@WebSocket) {
40151            unpoll($WebSock,$readable);
40152            unpoll($WebSock,$writable);
40153            close($WebSock) || eval{$WebSock->close;} || eval{$WebSock->kill_socket();} ||
40154            mlog(0,"warning: unable to close WebSocket: $WebSocket");
40155            delete $SocketCalls{$WebSock};
40156        }
40157
40158    	if ($CanUseIOSocketSSL && $enableWebAdminSSL) {
40159            ($WebSocket,$dummy) = newListenSSL($webAdminPort,\&NewWebConnection);
40160            @WebSocket = @$WebSocket;
40161
40162            $usessl = 'HTTPS';
40163        } else {
40164            ($WebSocket,$dummy) = newListen($webAdminPort,\&NewWebConnection);
40165            @WebSocket = @$WebSocket;
40166
40167            $usessl = '';
40168        }
40169        for (@$dummy) {s/:::/\[::\]:/o;}
40170        if(@WebSocket) {
40171            mlog(0,"AdminUpdate: listening on new admin port @$dummy $usessl (changed from $old)");
40172        } else {
40173
40174            # couldn't open the port -- switch back
40175            if ($usessl && $new eq $old) {
40176                ($WebSocket,$dummy) = newListen($webAdminPort,\&NewWebConnection);
40177                @WebSocket = @$WebSocket;
40178            } elsif ($usessl) {
40179                ($WebSocket,$dummy) = newListenSSL($webAdminPort,\&NewWebConnection);
40180                @WebSocket = @$WebSocket;
40181            } else {
40182                ($WebSocket,$dummy) = newListen($webAdminPort,\&NewWebConnection);
40183                @WebSocket = @$WebSocket;
40184            }
40185            for (@$dummy) {s/:::/\[::\]:/o;}
40186            mlog(0,"AdminUpdate: couldn't open new port -- still listening on @$dummy");
40187            $webAdminPort=$Config{$name}=$old;
40188            return "<span class=\"negative\">Couldn't open new port $new -- still listening on @$dummy</span>";
40189        }
40190        return '';
40191    } else {
40192
40193        # don't have permissions to change
40194        mlog(0,"AdminUpdate: request to listen on new admin port $new $usessl (changed from $old) -- restart required; euid=$>");
40195        return "<br />Restart required; euid=$><script type=\"text/javascript\">alert(\'new admin port $usessl - ASSP-Restart required\');</script>";
40196    }
40197}
40198
40199sub ConfigChangeStatPort {my ($name, $old, $new, $init)=@_;
40200    my $usessl;
40201    my @dummy;
40202    my $highport = 1;
40203    return if $new eq $old && ! $init;
40204    return if $WorkerNumber != 0;
40205    my $dummy;
40206    my $StatSocket;
40207    foreach my $port (split(/\|/o,$new)) {
40208        if ($port =~ /^.+:([^:]+)$/o) {
40209            if ($1 < 1024) {
40210                $highport = 0;
40211                last;
40212            }
40213        } else {
40214            if ($port < 1024) {
40215                $highport = 0;
40216                last;
40217            }
40218        }
40219    }
40220    $webStatPort=$Config{webStatPort}=$new;
40221    if($> == 0 || $highport || $^O eq "MSWin32") {
40222
40223        # change the listenport
40224        foreach my $StatSock (@StatSocket) {
40225            unpoll($StatSock,$readable);
40226            unpoll($StatSock,$writable);
40227            close($StatSock) || eval{$StatSock->close;} || eval{$StatSock->kill_socket();} ||
40228            delete $SocketCalls{$StatSock};
40229        }
40230
40231        if ($CanUseIOSocketSSL && $enableWebStatSSL) {
40232            ($StatSocket,$dummy) = newListenSSL($webStatPort,\&NewStatConnection);
40233            @StatSocket = @$StatSocket;
40234            $usessl = 'HTTPS';
40235        } else {
40236            ($StatSocket,$dummy) = newListen($webStatPort,\&NewStatConnection);
40237            @StatSocket = @$StatSocket;
40238            $usessl = '';
40239        }
40240        for (@$dummy) {s/:::/\[::\]:/o;}
40241        if(@StatSocket) {
40242            mlog(0,"AdminUpdate: listening on new stat port @$dummy $usessl ");
40243        } else {
40244
40245            # couldn't open the port -- switch back
40246            if ($usessl && $new eq $old) {
40247                ($StatSocket,$dummy) = newListen($webStatPort,\&NewStatConnection);
40248                @StatSocket = @$StatSocket;
40249            } elsif ($usessl) {
40250                ($StatSocket,$dummy) = newListenSSL($webStatPort,\&NewStatConnection);
40251                @StatSocket = @$StatSocket;
40252            } else {
40253                ($StatSocket,$dummy) = newListen($webStatPort,\&NewStatConnection);
40254                @StatSocket = @$StatSocket;
40255            }
40256            for (@$dummy) {s/:::/\[::\]:/o;}
40257            mlog(0,"AdminUpdate: couldn't open new port -- still listening on @$dummy");
40258            $webStatPort=$Config{$name}=$old;
40259            return "<span class=\"negative\">Couldn't open new port $new -- still listening on @$dummy</span>";
40260        }
40261        return '';
40262    } else {
40263
40264        # don't have permissions to change
40265        mlog(0,"AdminUpdate: request to listen on new stat port $new $usessl (changed from $old) -- restart required; euid=$>");
40266        return "<br />Restart required; euid=$><script type=\"text/javascript\">alert(\'new stat port $usessl - ASSP-Restart required\');</script>";
40267    }
40268}
40269
40270sub ConfigChangeRunTaskNow {
40271    my ( $name, $old, $new, $init ) = @_;
40272
40273    if ( !$init && $new ) {
40274        if ( !$RunTaskNow{$name} ) {
40275            $RunTaskNow{$name} = 1;
40276            mlog( 0, "AdminUpdate: task $name was queued to run" );
40277            $check4queuetime += 30;
40278            return ' - task was started';
40279        } else {
40280            mlog( 0, "task $name is just running - ignoring request" );
40281            return
40282"<span class=\"negative\"> - task $name is just running - ignoring request</span>";
40283        }
40284    }
40285}
40286
40287sub iso2hex {
40288	my $s = shift;
40289    use bytes;
40290    $s = join('',unpack 'H*',$s);
40291    no bytes;
40292    return $s;
40293}
40294
40295sub hex2iso {
40296	my $h = shift;
40297    use bytes;
40298    $h = pack 'H*',$h;
40299    no bytes;
40300    return $h;
40301}
40302
40303sub ConfigChangeValencePB {my ($name, $old, $new, $init)=@_;
40304    $Config{$name} = $$name = $new;
40305    my ($s1,$s2,$s3) = split(/[\|,\s]+/o,$new);
40306    $s2 = $s1 unless defined $s2;
40307    @$name = ($s1,$s2,$s3);
40308    my $s3comment;$s3comment = ", new IP limit counter ${$name}[2]" if $s3;
40309    mlog(0,"AdminUpdate: $name updated from '$old' to '$new' - new message score: ${$name}[0] , new IP score ${$name}[1] $s3comment") unless ($init || $new eq $old);
40310    return '';
40311}
40312
40313sub ConfigChangePassword {
40314    my ( $name, $old, $new, $init ) = @_;
40315
40316    # change the Password
40317    if ( !$init ) {
40318        $webAdminPassword = $new;
40319        $webAdminPassword = crypt( $webAdminPassword, "45" )
40320          if $webAdminPassword;
40321        $Config{webAdminPassword} = $webAdminPassword;
40322        mlog( 0, "AdminUpdate: Password changed" );
40323        return '';
40324
40325    }
40326}
40327sub ConfigChangeRelayPort {my ($name, $old, $new, $init)=@_;
40328    unless ($relayHost && $new) {
40329        if(@lsnRelay) {
40330          foreach my $Relay (@lsnRelay) {
40331            unpoll($Relay,$readable);
40332            unpoll($Relay,$writable);
40333            close($Relay);
40334            delete $SocketCalls{$Relay};
40335          }
40336          $$name = $Config{$name}=$new;
40337          mlog(0,"AdminUpdate: relay port disabled");
40338          return '<br />relay port disabled';
40339        } else {
40340          $$name = $Config{$name}=$new;
40341          return "<br />relayHost ($relayHost) and relayPort ($new) must be defined to enable relaying";
40342        }
40343    }
40344    my $highport = 1;
40345    foreach my $port (split(/\|/o,$new)) {
40346        if ($port =~ /^.+:([^:]+)$/o) {
40347            if ($1 < 1024) {
40348                $highport = 0;
40349                last;
40350            }
40351        } else {
40352            if ($port < 1024) {
40353                $highport = 0;
40354                last;
40355            }
40356        }
40357    }
40358    if($> == 0 || $highport || $^O eq "MSWin32") {
40359
40360        # change the listenport
40361        $$name = $Config{$name}=$new;
40362        if(@lsnRelay) {
40363          foreach my $Relay (@lsnRelay) {
40364            unpoll($Relay,$readable);
40365            unpoll($Relay,$writable);
40366            close($Relay);
40367            delete $SocketCalls{$Relay};
40368          }
40369        }
40370        my ($lsnRelay,$lsnRelayI)=newListen($relayPort,\&NewSMTPConnection);
40371        @lsnRelay = @$lsnRelay; @lsnRelayI = @$lsnRelayI;
40372        for (@$lsnRelayI) {s/:::/\[::\]:/o;}
40373        mlog(0,"AdminUpdate: listening for relay connections at @$lsnRelayI ");
40374        return '';
40375    } else {
40376		$$name = $Config{$name}=$new;
40377        # don't have permissions to change
40378        mlog(0,"AdminUpdate: request to listen on new relay port $new (changed from $old) -- restart required; euid=$>");
40379        return "<br />Restart required; euid=$><script type=\"text/javascript\">alert(\'new relay port - ASSP-Restart required\');</script>";
40380    }
40381}
40382
40383
40384
40385sub ConfigChangePOP3File {
40386    my ($name, $old, $new, $init)=@_;
40387    mlog(0,"AdminUpdate: POP3 config file updated from '$old' to '$new'") unless ($init || $new eq $old);
40388
40389    if ($new ne $old or $init) {
40390        $old =~ s/^ *file: *//io;
40391        $new =~ s/^ *file: *//io;
40392        if ($old) {
40393            $old =~ s/\\/\//go;
40394            $old = "$base/$old" ;
40395            delete $CryptFile{$old};
40396#            mlog(0,"info: deregistered encrypted $name file $old") if $WorkerNumber == 0 && $new ne $old;
40397        }
40398        if ($new) {
40399            $new =~ s/\\/\//go;
40400            $new = "$base/$new" ;
40401            $CryptFile{$new} = 1;
40402#            mlog(0,"info: registered encrypted $name file $new") if $WorkerNumber == 0;
40403        }
40404    }
40405    return '';
40406}
40407sub ConfigChangeLogfile {my ($name, $old, $new, $init)=@_;
40408    printLOG("close");
40409
40410    $logfile=$new;
40411    $Config{logfile}=$new;
40412    # open the logfile
40413    printLOG("open");
40414
40415    mlog(0,"AdminUpdate: log file changed from '$old' to '$new' ");
40416    '';
40417}
40418
40419sub ConfigChangeLogCharset {my ($name, $old, $new, $init)=@_;
40420    printLOG("close");
40421
40422    $LogCharset=$new;
40423    $Config{LogCharset}=$new;
40424    $WORS = "\r\n" if $enableWORS && !$LogCharset;
40425    $WORS = "\n" if !$enableWORS;
40426    # open the logfile
40427 	printLOG("open");
40428
40429    mlog(0,"AdminUpdate: LogCharset changed from '$old' to '$new' ");
40430    '';
40431}
40432
40433sub ConfigChangeWors {my ($name, $old, $new, $init)=@_;
40434    printLOG("close");
40435
40436    $enableWORS=$new;
40437    $Config{enableWORS}=$new;
40438    $WORS = "\r\n" if $enableWORS && !$LogCharset;
40439    $WORS = "\n" if !$enableWORS;
40440    # open the logfile
40441    printLOG("open");
40442
40443    mlog(0,"AdminUpdate: enableWORS changed from '$old' to '$new' ");
40444    '';
40445}
40446
40447sub ConfigChangeAutoUpdate {
40448    my ($name, $old, $new, $init)=@_;
40449    return if $WorkerNumber != 0;
40450    mlog(0,"AdminUpdate: $name from '$old' to '$new'") unless $init || $new eq $old;
40451    $$name = $Config{$name} = $new;
40452    my $ret = '';
40453    if ($new == 2 and $new ne $old and ! $init) {
40454        mlog(0,"info: forced to run a low priority autoupdate now") if $MaintenanceLog;
40455        $ret = '* forced to run a low priority autoupdate now';
40456        open(my $F ,'>>',"$base/version.txt");
40457        close $F;
40458        mlog(0,"info: changed file time of file $base/version.txt") if $MaintenanceLog >= 2;
40459        unlink "$base/download/assp.pl.gz.old";
40460        move("$base/download/assp.pl.gz","$base/download/assp.pl.gz.old");
40461        mlog(0,"info: moved file $base/download/assp.pl.gz to $base/download/assp.pl.gz.old") if $MaintenanceLog >= 2;
40462        $NextASSPFileDownload = -1;
40463        $NextVersionFileDownload = -1;
40464    }
40465    return $ret;
40466}
40467
40468sub ConfigDEBUG {
40469    my ( $name, $old, $new, $init ) = @_;
40470    close $DEBUG if $debug && !$AsASecondary;
40471    $debug = $new;
40472    if ($debug && !$init && !$AsASecondary) {
40473    	my $fn = localtime();
40474 		$fn =~ s/^... (...) +(\d+) (\S+) ..(..)/$1-$2-$4-$3/;
40475 		$fn =~ s/[\/:]/\-/g;
40476    	open( $DEBUG, ">","$base/debug/" . $fn . ".dbg" );
40477        binmode($DEBUG);
40478        my $oldfh = select($DEBUG);
40479        $| = 1;
40480        select($oldfh);
40481        eval(
40482            q[sub d {
40483   my $time = &timestring();
40484
40485   my $debugprint = $_[0];
40486           $debugprint =~ s/\n//;
40487           $debugprint =~ s/\r//;
40488           $debugprint =~ s/\s+$//;
40489
40490   print $DEBUG "$time <$debugprint>\n";
40491  }
40492  ]
40493        );
40494      } else {
40495        eval(q[sub d{return;}]);
40496      }
40497    mlog( 0, "AdminUpdate: debug file changed from '$old' to '$new'  " );
40498    '';
40499  }
40500
40501sub updateGoodAttach {my ($name, $old, $new, $init)=@_;
40502
40503    mlog(0,"AdminUpdate: Goodattach Level 4 updated from '$old' to '$new'") unless $init || $new eq $old;
40504    ${$name} = $Config{$name} = $new;
40505    SetRE('goodattachRE',qq[\\.($new)\$],'i',"Good Attachment",$name);
40506}
40507sub updatePassAttach {my ($name, $old, $new, $init)=@_;
40508
40509    mlog(0,"AdminUpdate: Passattach updated from '$old' to '$new'") unless $init || $new eq $old;
40510    ${$name} = $Config{$name} = $new;
40511    SetRE('passattachRE',qq[\\.?($new)\$],'i',"Pass Attachment",$name);
40512}
40513# bad attachment Settings, Checks and Update.
40514sub updateBadAttachL1 {my ($name, $old, $new, $init)=@_;
40515    mlog(0,"AdminUpdate: Badattach Level 1 updated from '$old' to '$new'") unless $init || $new eq $old;
40516    SetRE('badattachL1RE',qq[\\.(?:$new)\$],
40517#          'i-optimsx',
40518          'i',
40519          'bad attachment L1',$name);
40520    ${$name} = $Config{$name} = $new;
40521    updateBadAttachL2('BadAttachL2','',$Config{BadAttachL2},$new);
40522}
40523sub updateBadAttachL2 {my ($name, $old, $new, $init)=@_;
40524    mlog(0,"AdminUpdate: Badattach Level 2 updated from '$old' to '$new'") unless $init || $new eq $old;
40525    ${$name} = $Config{$name} = $new;
40526    $new.='|'.$init;
40527    SetRE('badattachL2RE',qq[\\.($new)\$],'i',"bad attachment L2",$name);
40528    updateBadAttachL3('BadAttachL3','',$Config{BadAttachL3},$new);
40529}
40530sub updateBadAttachL3 {my ($name, $old, $new, $init)=@_;
40531    mlog(0,"Badattach Level 3 updated from '$old' to '$new'") unless $init || $new eq $old;
40532    ${$name} = $Config{$name} = $new;
40533    $new.='|'.$init;
40534    SetRE('badattachL3RE',qq[\\.($new)\$],'i',"bad attachment L3",$name);
40535    $badattachRE[1]=$badattachL1RE;
40536    $badattachRE[2]=$badattachL2RE;
40537    $badattachRE[3]=$badattachL3RE;
40538
40539}
40540
40541sub updateSuspiciousAttach {
40542    my ( $name, $old, $new, $init ) = @_;
40543    mlog( 0, "AdminUpdate: Suspicious Attach updated from '$old' to '$new'" )
40544      unless $init || $new eq $old;
40545    $new .= '|' . $init;
40546    SetRE(
40547        'suspiciousattachRE', qq[\\.($new)\$],
40548        'i',                  "suspicious attachment "
40549    );
40550    '';
40551}
40552
40553sub updateNotSpamTag {
40554    my ( $name, $old, $new, $init ) = @_;
40555    my $ret;
40556    unless ( $init || $new eq $old ) {
40557        mlog( 0, "AdminUpdate: NotSpamTagRandom updated from '$old' to '$new'" );
40558        ${$name} = $Config{$name} = $new;
40559        $NotSpamTagGenerated = &NotSpamTagGenerate if $new;
40560    }
40561    return $NotSpamTagGenerated if $new;
40562}
40563sub updateUseLocalDNS {
40564    my ( $name, $old, $new, $init ) = @_;
40565    my $ret;
40566    ${$name} = $Config{$name} = $new;
40567    unless ($init || $new eq $old) {
40568        mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" );
40569        $ret = updateDNS ( 'updateDNS', '', $Config{DNSServers}, $init );
40570    }
40571    return $ret;
40572}
40573sub updateDNS {
40574    my ( $name, $old, $new, $init ) = @_;
40575    return '' if $WorkerNumber != 0 && $WorkerNumber != 10000;
40576    return '' if $WorkerNumber == 10000 && $ComWorker{$WorkerNumber}->{rereadconfig};
40577    mlog( 0, "AdminUpdate: DNS Name Servers updated from '$old' to '$new'" )
40578      unless $init || $new eq $old || $name eq 'updateDNS';
40579    ${$name} = $Config{$name} = $new;
40580
40581    if ($CanUseDNS) {
40582        my @ns;
40583        my $domainName;
40584        my $nnew;
40585        ($nnew , $domainName) = split(/\s*\=\>\s*/o, $new);
40586        @ns = split( /\s*\|\s*/o, $nnew ) unless $UseLocalDNS;
40587
40588        my $res = Net::DNS::Resolver->new(tcp_timeout => $DNStimeout,
40589                                          udp_timeout => $DNStimeout,
40590                                          retrans     => $DNSretrans,
40591                                          retry       => $DNSretry
40592                                          );
40593        if ( @ns && ! $UseLocalDNS ) {
40594            $res->nameservers(@ns);
40595        }
40596        my @oldnameserver = @nameservers;
40597        my @usedNameServers = $res->nameservers;
40598        eval('$forceDNSv4=!($CanUseIOSocketINET6&&&matchARRAY(qr/^$IPv6Re$/,\@usedNameServers));');
40599        getRes('force', $res);
40600
40601        my @availDNS;
40602        my @diedDNS;
40603        $domainName ||= 'sourceforge.net';
40604        my %DNSResponseTime;
40605        foreach my $dnsServerIP (@usedNameServers) {
40606            my $btime = Time::HiRes::time();
40607            $res->nameservers($dnsServerIP);
40608            my $response = $res->search($domainName);
40609
40610            my $atime = int((Time::HiRes::time() - $btime) * 1000);
40611            mlog( 0, "info: Name Server $dnsServerIP: ResponseTime = $atime ms for $domainName" ) if $DNSResponseLog;
40612            $DNSResponseTime{$dnsServerIP} = $atime;
40613	        if ($response) {
40614                push (@availDNS,$dnsServerIP);
40615            } else {
40616                push (@diedDNS,$dnsServerIP);
40617            }
40618        }
40619        @availDNS = sort {$DNSResponseTime{$main::a} <=> $DNSResponseTime{$main::b}} @availDNS;
40620        my @newDNS = @availDNS;
40621        push @newDNS , @diedDNS unless scalar @newDNS;
40622        foreach (@availDNS) {
40623            mlog( 0, "info: Name Server $_: OK " ) unless $init || $new eq $old;
40624        }
40625        foreach (@diedDNS) {
40626	        mlog( 0, "warning: Name Server $_: does not respond or timed out " ) unless $init;
40627        }
40628
40629        @nameservers = @newDNS if DNSdistance(\%DNSResponseTime,\@newDNS,defined ${chr(ord("\026") << 2)}) || $init || $new ne $old;
40630        eval('$forceDNSv4=!($CanUseIOSocketINET6&&&matchARRAY(qr/^$IPv6Re$/,\@nameservers));');
40631
40632        my $resetDNSresolvers = ("@oldnameserver" ne "@nameservers" || $init);
40633        mlog(0,"info: switched (DNS) nameserver order from ".join(' , ',@oldnameserver)." to " . join(' , ',@nameservers)) if($resetDNSresolvers && ($MaintenanceLog || $DNSResponseLog));
40634        if ($resetDNSresolvers || ! @availDNS || @diedDNS) {
40635            $DNSresolverTime{$_} = 0 for (0..$NumComWorkers,10000,10001);
40636        }
40637        if (! @availDNS) {
40638            mlog(0,"ERROR: !!!! no answering DNS-SERVER found !!!!");
40639        }
40640        if (@diedDNS) {
40641            return '<span class="negative">*** '.join(' , ',@diedDNS).' timed out </span>- using DNS-Servers: '.join(' , ',@nameservers);
40642        }
40643        return 'using DNS-Servers: '.join(' , ',@nameservers);
40644    }
40645    return '<span class="negative">*** module Net::DNS is not installed </span>';
40646}
40647
40648sub DNSdistance {
40649    my $DNSResponseTime = shift;
40650    my $nameservers = shift;
40651    my %distance;
40652    foreach my $i (@$nameservers) {
40653        foreach my $j (@$nameservers) {
40654            next if ($i eq $j);
40655            $distance{"$i-$j"} = $DNSResponseTime->{$i} - $DNSResponseTime->{$j};
40656            mlog(0,"info: DNS-distance $i-$j = ".$distance{"$i-$j"}) if ($MaintenanceLog > 2 && $DNSResponseLog);
40657        }
40658    }
40659    my %newdistance = %distance;
40660    my %olddistance = %DNSRespDist;
40661    foreach (keys %olddistance) {
40662        if (! exists $newdistance{$_}) {
40663            mlog(0,"info: new distance $_  not found") if ($MaintenanceLog > 2 && $DNSResponseLog);
40664            %DNSRespDist = %distance;
40665            return $_[0];
40666        }
40667        delete $newdistance{$_};
40668    }
40669    if (scalar keys %newdistance) {
40670        mlog(0,"info: new distance list is longer than the previouse") if ($MaintenanceLog > 2 && $DNSResponseLog);
40671        %DNSRespDist = %distance;
40672        return $_[0];
40673    }
40674    %newdistance = %distance;
40675    foreach (keys %newdistance) {
40676        if (abs($newdistance{$_} - $olddistance{$_}) > $maxDNSRespDist) { # too large DNS server response time distance change
40677            if ($MaintenanceLog > 2 && $DNSResponseLog) {
40678                my $val = abs($newdistance{$_} - $olddistance{$_});
40679                mlog(0,"info: distance $_ changed by $val ms (max is $maxDNSRespDist ms)");
40680            }
40681            %DNSRespDist = %distance;
40682            return $_[0];
40683        }
40684    }
40685    %DNSRespDist = %distance;
40686    return $_[0] & 0;
40687}
40688
40689sub configUpdateRBLCR {
40690    my ( $name, $old, $new, $init ) = @_;
40691    mlog( 0, "AdminUpdate: RBL Cache Refresh updated from '$old' to '$new'" )
40692      unless $init || $new eq $old;
40693    &cleanCacheRBL unless $init || $new eq $old;
40694}
40695
40696sub configUpdatePTRCR {
40697    my ( $name, $old, $new, $init ) = @_;
40698    mlog( 0, "AdminUpdate: PTR Cache Refresh updated from '$old' to '$new'" )
40699      unless $init || $new eq $old;
40700    &cleanCachePTR unless $init || $new eq $old;
40701}
40702
40703sub configUpdateRWLCR {
40704    my ( $name, $old, $new, $init ) = @_;
40705    mlog( 0, "AdminUpdate: RWL Cache Refresh updated from '$old' to '$new'" )
40706      unless $init || $new eq $old;
40707    if (!$RWLCacheInterval) {
40708    	$RWLCacheObject->DESTROY() if $RWLCacheObject;
40709    	$RWLCacheInterval=24;
40710		$Config{RWLCacheInterval}=24;
40711	} else {
40712    	&cleanCacheRWL unless $init || $new eq $old;
40713    }
40714}
40715
40716sub configUpdateMXACR {
40717    my ( $name, $old, $new, $init ) = @_;
40718    mlog( 0, "AdminUpdate: MXA Cache Refresh updated from '$old' to '$new'" )
40719      unless $init || $new eq $old;
40720    &cleanCacheMXA;
40721}
40722
40723sub configUpdateSBCR {
40724    my ( $name, $old, $new, $init ) = @_;
40725    mlog( 0,
40726        "AdminUpdate: SenderBase Cache Refresh updated from '$old' to '$new'" )
40727      unless $init || $new eq $old;
40728    &cleanCacheSB unless $init || $new eq $old;
40729}
40730
40731sub configUpdateTrapCR {
40732    my ( $name, $old, $new, $init ) = @_;
40733    mlog( 0,
40734        "AdminUpdate: Invalid Addresses Refresh updated from '$old' to '$new'" )
40735      unless $init || $new eq $old;
40736    &cleanTrapPB unless $init || $new eq $old;
40737}
40738
40739sub configUpdateSPFCR {
40740    my ( $name, $old, $new, $init ) = @_;
40741    mlog( 0, "AdminUpdate: SPF Cache Refresh updated from '$old' to '$new'" )
40742      unless $init || $new eq $old;
40743    &cleanCacheSPF unless $init || $new eq $old;
40744}
40745
40746sub configUpdateURIBLCR {
40747    my ( $name, $old, $new, $init ) = @_;
40748    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
40749      unless $init || $new eq $old;
40750    &cleanCacheURI unless $init || $new eq $old;
40751}
40752
40753sub configUpdateSSLCR {
40754    my ( $name, $old, $new, $init ) = @_;
40755    mlog( 0, "AdminUpdate: SSL Error Cache Refresh updated from '$old' to '$new'" )
40756      unless $init || $new eq $old;
40757    &cleanCacheSSLfailed unless $init || $new eq $old;
40758}
40759# URIBL Settings Checks, and Update.
40760sub configUpdateURIBL {
40761    my ( $name, $old, $new, $init ) = @_;
40762    mlog( 0, "AdminUpdate: URIBL-Enable updated from '$old' to '$new'" )
40763      unless $init || $new eq $old;
40764
40765    $ValidateURIBL = $Config{ValidateURIBL} = $new;
40766    unless ($CanUseURIBL) {
40767        mlog( 0,
40768"AdminUpdate:error URIBL-Enable updated from '1' to '0', Net::DNS not installed"
40769        ) if $Config{ValidateURIBL};
40770        ( $ValidateURIBL, $Config{ValidateURIBL} ) = 0;
40771        return
40772'<span class="negative">*** Net::DNS must be installed before enabling URIBL.</span>';
40773    } else {
40774        configUpdateURIBLMH( 'URIBLmaxhits', '', $Config{URIBLmaxhits},
40775            'Cascading' );
40776    }
40777}
40778
40779# RWL Settings Checks, and Update.
40780sub configUpdateRWL {
40781    my ( $name, $old, $new, $init ) = @_;
40782
40783    mlog( 0, "AdminUpdate: RWL-Enable updated from '$old' to '$new'" )
40784      unless $init || $new eq $old || !$new and !$old;
40785    ${$name} = $Config{$name} = $new;
40786    unless ($CanUseRWL) {
40787        mlog( 0,
40788"AdminUpdate:error RWL-Enable updated from '$new' to '', Net::DNS not installed"
40789        ) if $Config{ValidateRWL};
40790        ( $ValidateRWL, $Config{ValidateRWL} ) = ();
40791        return
40792'<span class="negative">*** Net::DNS must be installed before enabling RWL.</span>';
40793    } else {
40794        configUpdateRWLMH( 'RWLminhits', '', $Config{RWLminhits}, 'Cascading' );
40795    }
40796}
40797
40798sub configUpdateRWLMH {
40799    my ( $name, $old, $new, $init ) = @_;
40800    mlog( 0, "AdminUpdate: RWL Minimum Hits updated from '$old' to '$new'" )
40801      unless $init || $new eq $old;
40802    ${$name} = $Config{$name} = $new;
40803    if ( $new <= 0 ) {
40804        mlog( 0,
40805"AdminUpdate:error RWL-Enable updated from '1' to '', RWLminhits must be defined and positive"
40806        ) if $Config{ValidateRWL};
40807        ( $ValidateRWL, $Config{ValidateRWL} ) = ();
40808        return
40809'<span class="negative">*** RWLminhits must be defined and positive before enabling RWL.</span>';
40810    } else {
40811        configUpdateRWLMR( 'RWLmaxreplies', '', $Config{RWLmaxreplies},
40812            'Cascading' );
40813    }
40814}
40815
40816sub configUpdateRWLMR {
40817    my ( $name, $old, $new, $init ) = @_;
40818
40819    mlog( 0, "AdminUpdate: RWL Maximum Replies updated from '$old' to '$new'" )
40820      unless $init || $new eq $old;
40821    ${$name} = $Config{$name} = $new;
40822    if ( $new < $RWLminhits ) {
40823        mlog( 0,
40824"AdminUpdate:error RWL-Enable updated from '1' to '', RWLmaxreplies not >= RWLminhits"
40825        ) if $Config{ValidateRWL};
40826        ( $ValidateRWL, $Config{ValidateRWL} ) = ();
40827        return
40828'<span class="negative">*** RWLmaxreplies must be more than or equal to RWLminhits before enabling RWL.</span>';
40829    } else {
40830        configUpdateRWLSP( 'RWLServiceProvider', '',
40831            $Config{RWLServiceProvider}, 'Cascading' );
40832    }
40833}
40834
40835sub configUpdateRWLSP {
40836    my ( $name, $old, $new, $init ) = @_;
40837    mlog( 0,
40838        "AdminUpdate: RWL Service Providers updated from '$old' to '$new'" )
40839      unless $init || $new eq $old;
40840    ${$name} = $Config{$name} = $new;
40841    $new = checkOptionList( $new, 'RWLServiceProvider', $init );
40842    my $domains = ( $new =~ s/\|/|/g ) + 1;
40843
40844    if ( $domains < $RWLmaxreplies ) {
40845        mlog( 0,
40846"AdminUpdate:error RWL-Enable updated from '1' to '',RWLServiceProvider not >= RWLmaxreplies "
40847        ) if $Config{ValidateRWL};
40848        ( $ValidateRWL, $Config{ValidateRWL} ) = ();
40849        return
40850'<span class="negative">*** RWLServiceProvider must contain more than or equal to RWLmaxreplies  before enabling RWL. RWL deactivated. </span>';
40851    } elsif ($CanUseRWL) {
40852		    my @templist = split( /\|/, $new );
40853		    @rwllist   = ();
40854            %rwlweight = ();
40855            foreach my $c (@templist) {
40856
40857                if ( $c =~ /(.*)\=\>(.*)/ ) {
40858                    push( @rwllist, $1 );
40859                    $rwlweight{$1} = $2;
40860                } else {
40861
40862                    push( @rwllist, $c );
40863                }
40864            }
40865
40866
40867        if ($ValidateRWL) {
40868            return ' & RWL activated';
40869        } else {
40870            return 'RWL deactivated';
40871        }
40872    }
40873}
40874
40875
40876
40877
40878# DNSBL Settings Checks, and Update.
40879sub configUpdateRBL {
40880    my ( $name, $old, $new, $init ) = @_;
40881    mlog( 0, "AdminUpdate: ValidateRBL updated from '$old' to '$new'" )
40882      unless $init || $new eq $old;
40883    $ValidateRBL = $Config{ValidateRBL} = $new;
40884    unless ($CanUseRBL) {
40885        mlog( 0, "AdminUpdate:error DNSBL disabled, Net::DNS not installed " )
40886          if $Config{ValidateRBL};
40887        ( $ValidateRBL, $Config{ValidateRBL} ) = 0;
40888        return '<span class="negative">*** Net::DNS must be installed before enabling DNSBL.</span>';
40889    } else {
40890        configUpdateRBLMH( 'RBLmaxhits', '', $Config{RBLmaxhits}, 'Cascading' );
40891    }
40892}
40893
40894sub configUpdateRBLMH {
40895    my ( $name, $old, $new, $init ) = @_;
40896    mlog( 0, "AdminUpdate: RBLmaxhits updated from '$old' to '$new'" )
40897      unless $init || $new eq $old;
40898    ${$name} = $Config{$name} = $new;
40899    if ( $new <= 0 ) {
40900        mlog( 0,
40901"AdminUpdate:error DNSBL disabled', RBLmaxhits must be > 0 before enabling DNSBL.</span>';"
40902        ) if $Config{ValidateRBL};
40903        ( $ValidateRBL, $Config{ValidateRBL} ) = 0;
40904        return '<span class="negative">*** RBLmaxhits must be > 0 before enabling DNSBL.</span>';
40905    } else {
40906        configUpdateRBLMR( 'RBLmaxreplies', '', $Config{RBLmaxreplies}, 'Cascading' );
40907    }
40908}
40909
40910sub configUpdateRBLMR {
40911    my ( $name, $old, $new, $init ) = @_;
40912    mlog( 0, "AdminUpdate: RBLmaxreplies updated from '$old' to '$new'" )
40913      unless $init || $new eq $old;
40914    ${$name} = $Config{$name} = $new;
40915    configUpdateRBLSP( 'RBLServiceProvider', '', $Config{RBLServiceProvider},
40916        'Cascading' );
40917}
40918
40919sub configUpdateRBLSP {
40920    my ( $name, $old, $new, $init ) = @_;
40921    mlog( 0, "AdminUpdate: RBLServiceProvider updated from '$old' to '$new'" )
40922      unless $init || $new eq $old;
40923    ${$name} = $Config{$name} = $new unless $WorkerNumber;
40924    $new = checkOptionList( $new, 'RBLServiceProvider', $init );
40925    $RBLmaxreplies = ( $new =~ s/\|/|/go );
40926
40927    if ($CanUseRBL) {
40928        my @templist = split( /\|/o, $new );
40929
40930        @rbllist   = ();
40931        %rblweight = ();
40932        while (@templist) {
40933            my $c = shift @templist;
40934            if ($NODHO && $c =~ /dnsbl\.httpbl\.org/io) {
40935                mlog(0,"RBLSP:warning - dnsbl.httpbl.org is not supported as RBL-Service-Provider by ASSP and will be ignored - remove the entry")
40936                    if $WorkerNumber == 0;
40937                next;
40938            }
40939            if ( $c =~ /^(.*?)=>(.*?)=>(.*)$/o ) {
40940                my ($sp,$res,$w) = ($1,$2,$3);
40941                next unless $sp;
40942                $res ||= '*';
40943                push( @rbllist, $sp ) unless grep(/\Q$sp\E/, @rbllist);
40944                $sp =~ s/^.*?\$DATA\$\.?//io;
40945                if ($res =~ /(?:^|\.)M(?:[1248]|16|32|64|128)(?:\.|$)/io) {
40946                    $rblweight{$sp} = {} unless exists $rblweight{$sp};
40947                    setSPBitMask($rblweight{$sp},$res, weightRBL($w),"'$name' for '$sp'");
40948                } elsif ($res =~ /(?:^|\.)M/io) {
40949                    mlog(0,"error: invalid bitmask definition '$res' found in $name for $sp") if $WorkerNumber == 0;
40950                    next;
40951                } else {
40952                    $rblweight{$sp}{$res} = weightRBL($w);
40953                }
40954            } elsif ( $c =~ /^(.*?)\=\>(.*)$/o ) {
40955                my ($sp,$w) = ($1,$2);
40956                next unless $sp;
40957                push( @rbllist, $sp ) unless grep(/\Q$sp\E/, @rbllist);
40958                $sp =~ s/^.*?\$DATA\$\.?//io;
40959                $rblweight{$sp}{'*'} = weightRBL($w);
40960            } else {
40961                $c =~ s/^.*?\$DATA\$\.?//io;
40962                next unless $c;
40963                push( @rbllist, $c ) unless grep(/\Q$c\E/, @rbllist);
40964            }
40965        }
40966
40967        &cleanCacheRBL() unless $init || $new eq $old;
40968
40969
40970        if ($ValidateRBL) {
40971            return ' & DNSBL activated';
40972        }
40973        else {
40974            return 'DNSBL deactivated';
40975        }
40976    }
40977}
40978
40979sub configUpdateMaxSize {
40980    my ( $name, $old, $new, $init , $desc) = @_;
40981    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
40982      unless $init || $new eq $old;
40983    ${$name} = $Config{$name} = $new;
40984    my %hash = (
40985                 'MaxRealSizeAdr' => 'MRSadr',
40986                 'MaxSizeAdr' => 'MSadr',
40987                 'MaxRealSizeExternalAdr' => 'MRSEadr',
40988                 'MaxSizeExternalAdr' => 'MSEadr'
40989               );
40990    my $hash = $hash{$name};
40991    $new = checkOptionList( $new, $name, $init );
40992    my $ret;
40993
40994    my @templist = split( /\|/o, $new );
40995
40996    my %tmp = ();
40997    while (@templist) {
40998        my $c = shift @templist;
40999        $c =~ s/\s//go;
41000        my ($adr,$val) = $c =~ /^(.+?)\=\>(\d+)$/o;
41001        next unless $adr;
41002        next unless defined $val;
41003        if ($adr =~ /^\@.+$/o) {                         # a domain
41004            $adr = '[^@]+'.$adr;
41005            $adr = '^(?i:'.$adr.')$';
41006        } elsif ($adr =~ /^[^@]+\@$/o) {                 # a user name with @
41007            $adr = $adr.'[^@]+';
41008            $adr = '^(?i:'.$adr.')$';
41009        } elsif ($adr =~ /^(?:\d{1,3}\.[\d\.\*\?]+|[a-f0-9:\?\*]+)$/io) {    # an IP address
41010            $adr = '^(?i:'.$adr.')';
41011        } elsif ($adr !~ /\@/o) {                        # a simple user name
41012            $adr = $adr.'@[^@]+';
41013            $adr = '^(?i:'.$adr.')$';
41014        } elsif ($adr =~ /^[^@]+\@[^@]+$/) {             # an email address
41015            $adr = '^(?i:'.$adr.')$';
41016        } else {
41017            next;
41018        }
41019        $adr =~ s/([^\\]?)\@/$1\\@/go;
41020        $tmp{$adr} = $val;
41021    }
41022    %{$hash} = %tmp;
41023    return $ret;
41024}
41025
41026sub configUpdateStringToNum {
41027    my ( $name, $old, $new, $init , $desc) = @_;
41028    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
41029      unless $init || $new eq $old;
41030    ${$name} = $Config{$name} = $new;
41031    $new = checkOptionList( $new, $name, $init );
41032    my %hash = (
41033                 'MaxEqualXHeader' => 'MEXH'
41034               );
41035    my $hash = $hash{$name};
41036    my $ret;
41037
41038    my @templist = split( /\|/o, $new );
41039
41040    my %tmp = ();
41041    while (@templist) {
41042        my $c = shift @templist;
41043        $c =~ s/^\s+//o;
41044        $c =~ s/\s+$//o;
41045        my ($tag,$val) = $c =~ /^(.+?)\s*\=\>\s*(\d+)$/o;
41046        next unless $tag;
41047        next unless $val;
41048        $tmp{$tag} = $val;
41049    }
41050    %{$hash} = %tmp;
41051    return $ret;
41052}
41053sub configUpdateURIBLMH {
41054    my ( $name, $old, $new, $init ) = @_;
41055    mlog( 0, "AdminUpdate: URIBL Maximum Hits updated from '$old' to '$new'" )
41056      unless $init || $new eq $old;
41057    ${$name} = $Config{$name} = $new;
41058    if ( $new <= 0 ) {
41059        mlog( 0,
41060"AdminUpdate:error URIBL-Enable updated from '1' to '0', URIBLmaxhits not > 0"
41061        ) if $Config{ValidateURIBL};
41062        ( $ValidateURIBL, $Config{ValidateURIBL} ) = 0;
41063        return
41064'<span class="negative">*** URIBLmaxhits must be defined and positive before enabling URIBL.</span>';
41065    }
41066}
41067
41068
41069sub configUpdateURIBLSP {
41070    my ( $name, $old, $new, $init ) = @_;
41071    mlog( 0, "AdminUpdate: URIBL Service Providers updated from '$old' to '$new'" )
41072      unless $init || $new eq $old;
41073    ${$name} = $Config{$name} = $new;
41074    $new = checkOptionList( $new, 'URIBLServiceProvider', $init );
41075    my $domains = ( $new =~ s/\|/|/go ) + 1;
41076    $URIBLmaxreplies = $domains;
41077    if ( $domains < $URIBLmaxreplies ) {
41078        mlog( 0, "AdminUpdate: warning count of URIBLServiceProvider not >= URIBLmaxreplies - possibly ok if weigths are used" )
41079          if $Config{ValidateURIBL};
41080    }
41081    if ($CanUseURIBL) {
41082        my @templist = split( /\|/o, $new );
41083        @uribllist = ();
41084        %URIBLweight = ();
41085        while (@templist) {
41086            my $c = shift @templist;
41087
41088            if ( $c =~ /(.*)\=\>(.*)=>(.*)/o ) {
41089                my ($sp,$res,$w) = ($1,$2,$3);
41090                next unless $sp;
41091                $res ||= '*';
41092                push( @uribllist, $sp ) unless grep(/\Q$sp\E/, @uribllist);
41093                $sp =~ s/^.*?\$DATA\$\.?//io;
41094                $URIBLweight{$sp}{$res} = $w;
41095            } elsif ( $c =~ /(.*)\=\>(.*)/o ) {
41096                my ($sp,$w) = ($1,$2);
41097                next unless $sp;
41098                push( @uribllist, $sp ) unless grep(/\Q$sp\E/, @uribllist);
41099                $sp =~ s/^.*?\$DATA\$\.?//io;
41100                $URIBLweight{$sp}{'*'} = $w;
41101            } else {
41102                $c =~ s/^.*?\$DATA\$\.?//io;
41103                next unless $c;
41104                push( @uribllist, $c ) unless grep(/\Q$c\E/, @uribllist);
41105            }
41106        }
41107        $URIBLhasweights = 0;
41108        $URIBLhasweights = 1 if scalar( keys %URIBLweight);
41109        &cleanCacheURI() unless $init || $new eq $old;
41110        if ($ValidateURIBL) {
41111            return ' & URIBL activated';
41112        } else {
41113            return 'URIBL deactivated';
41114        }
41115    }
41116}
41117sub updateLDAPHost {my ($name, $old, $new, $init)=@_;
41118    my $ldap;
41119    my $ldaplist;
41120    my @ldaplist;
41121    mlog(0,"AdminUpdate: LDAP Hosts updated from '$old' to '$new'") unless $init || $new eq $old;
41122    $LDAPHost=$new;
41123    $Config{$name} = $new;
41124    if($CanUseLDAP && $DoLDAP) {
41125        @ldaplist = split(/\|/o,$LDAPHost);
41126        $ldaplist = \@ldaplist;
41127        mlog(0,"checking LDAP server at $LDAPHost -- ");
41128        my $scheme = 'ldap';
41129        eval{
41130        $scheme = 'ldaps' if ($DoLDAPSSL == 1 && $AvailIOSocketSSL);
41131        $ldap = Net::LDAP->new( $ldaplist,
41132                                timeout => $LDAPtimeout,
41133                                scheme => $scheme,
41134                                inet4 =>  1,
41135                                inet6 =>  $CanUseIOSocketINET6
41136                              );
41137        $ldap->start_tls() if ($DoLDAPSSL == 2 && $AvailIOSocketSSL);
41138        };
41139
41140        if(! $ldap || $@) {
41141            mlog(0,"AdminUpdate: error couldn't contact LDAP server at $LDAPHost -- $@");
41142            if (!$init) {
41143                return ' & LDAP not activated';
41144            } else {
41145                return '';
41146            }
41147        } else {
41148            mlog(0,"AdminUpdate: LDAP server at $LDAPHost contacted -- ");
41149            if (!$init) {
41150                return ' & LDAP activated';
41151            } else {
41152                return '';
41153            }
41154        }
41155    }
41156}
41157
41158sub configUpdateCA {
41159    my ( $name, $old, $new, $init ) = @_;
41160
41161    %calist = ();
41162    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
41163      unless $init || $new eq $old;
41164    ${$name} = $Config{$name} = $new;
41165    $new = checkOptionList( $new, 'CatchAll', $init );
41166    for my $an ( split( /\|/, $new ) ) {
41167
41168        if ( $an =~ /(\S*)\@(\S*)/ ) {
41169
41170            $calist{$2} = "$1";
41171        }
41172    }
41173}
41174
41175sub configUpdateCCD {
41176    my ( $name, $old, $new, $init ) = @_;
41177    %ccdlist = ();
41178    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" ) unless $init || $new eq $old;
41179    ${$name} = $Config{$name} = $new;
41180
41181    $new = checkOptionList( $new, 'ccSpamInDomain', $init );
41182
41183    for my $ad ( split( /\|/, $new ) ) {
41184    	if($ad=~/(\S*)\@(\S*)/) {
41185
41186            $ccdlist{lc $2} = "$1";
41187
41188        }
41189    }
41190
41191
41192}
41193
41194
41195sub configUpdateASSPCfg {
41196    my ($name, $old, $new, $init)=@_;
41197    if (fileUpdated("assp.cfg",$name)){
41198        if ($WorkerNumber == 0) {
41199            mlog(0,"AdminUpdate: assp.cfg was externaly changed - reload the configuration");
41200            &reloadConfigFile();
41201            $ConfigChanged = 0;
41202        }
41203        $asspCFGTime = $FileUpdate{"$base/assp.cfg$name"} = ftime("$base/assp.cfg");
41204    }
41205    return;
41206}
41207
41208sub configUpdateGlobalClient {
41209    my ($name, $old, $new, $init)=@_;
41210    mlog(0,"AdminUpdate: global-PB-clientname updated from '$old' to '$new'") unless $init || $new eq $old;
41211    if ($new eq '') {
41212       $globalClientPass = '';
41213       $Config{globalClientPass}='';
41214       $globalClientLicDate = '';
41215       $Config{globalClientLicDate}='';
41216       return ' global penalty box upload/download <span class="negative">is now disabled</span>';
41217    } else {
41218       my $res = &registerGlobalClient($new);
41219       if ($res == 1) {
41220          return " clientname $new was successful registered on global-PB server";
41221       } else {
41222          $globalClientPass = '';
41223          $globalClientName = '';
41224          $Config{globalClientPass}='';
41225          $Config{$name}='';
41226          $globalClientLicDate = '';
41227          $Config{globalClientLicDate}='';
41228          &SaveConfig();
41229          mlog(0,"warning: registration for clientname $new global-PB server failed : $res");
41230          return
41231          '<span class="negative">*** registration for clientname '.$new.' on global-PB server failed : '.$res.'</span><script type=\"text/javascript\">alert(\'global-client registation failed - '.$res.'\');</script>';
41232       }
41233    }
41234}
41235
41236sub configUpdateGlobalHidden {
41237    my ($name, $old, $new, $init)=@_;
41238    $$name = $old;
41239    $Config{$name}=$old;
41240    if ($old eq '') {
41241       return '<span class="negative"> deleted</span>';
41242    } else {
41243       return '';
41244    }
41245}
41246sub updatePenaltyDuration {
41247    my ( $name, $old, $new, $init ) = @_;
41248    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
41249      unless $init || $new eq $old;
41250    &CleanPB unless $init || $new eq $old;
41251    return "";
41252}
41253
41254sub updateBlackExpiration {
41255    my ( $name, $old, $new, $init ) = @_;
41256    mlog( 0, "AdminUpdate: $name updated from '$old' to '$new'" )
41257      unless $init || $new eq $old;
41258    &cleanBlackPB  unless $init || $new eq $old;
41259    return "";
41260}
41261sub configChangeRBSched {
41262    my ($name, $old, $new, $init)=@_;
41263    my $shour = 0;
41264    my $ihour = 0;
41265    my $n = 0;
41266    mlog(0,"AdminUpdate: $name updated from '$old' to '$new'") unless $init || $new eq $old;
41267    %RebuildSched = ();
41268    if ($new =~ /^\*/) {
41269
41270    	$ihour = $1 if $1;
41271    	while ($shour + $ihour  <=24) {
41272
41273    		$shour += $ihour;
41274    		mlog(0,"info: RebuildSchedule for RebuildSpamdb.pl is $shour:00");
41275			next if $shour >=1 and $shour <=5;
41276    		$RebuildSched{$shour} = 1;
41277
41278
41279    	}
41280    } else {
41281		foreach my $shour ( split( /\|/, $new ) ) {
41282			$RebuildSched{$shour} = 1 if $shour > 0 && $shour <= 24;
41283			$n++;
41284			last if $n > 6;
41285		}
41286	}
41287    $Config{$name} = $new;
41288    ${$name} = $new;
41289    return '';
41290}
41291
41292sub configChangeHKSched {
41293    my ($name, $old, $new, $init)=@_;
41294    my $shour;
41295    mlog(0,"AdminUpdate: $name updated from '$old' to '$new'") unless $init || $new eq $old;
41296    %HouseKeepingSched = ();
41297    if ($new eq "*") {
41298    	$shour = 1;
41299    	while ($shour <=24) {
41300    		$HouseKeepingSched{$shour} = 1;
41301
41302    		$shour++;
41303    	}
41304    } else {
41305		foreach my $shour ( split( /\|/, $new ) ) {
41306			$HouseKeepingSched{$shour} = 1;
41307		}
41308	}
41309    $Config{$name} = $new;
41310    ${$name} = $new;
41311    return '';
41312}
41313sub configChangeUpdateWhitelist {
41314    my ($name, $old, $new, $init)=@_;
41315    my $shour;
41316    mlog(0,"AdminUpdate: $name updated from '$old' to '$new'") unless $init || $new eq $old;
41317
41318    $Config{$name} = $new;
41319    ${$name} = $new;
41320    $saveWhite = time - 1;
41321    return '';
41322}
41323sub cleanBlackPB {
41324	return if !$PBBlackObject;
41325    if ( $BlackExpiration == 0 ) {
41326
41327        while ( my ( $k, $v ) = each(%PBBlack) ) {
41328            delete $PBBlack{$k};
41329        }
41330
41331        return;
41332    }
41333
41334    my $ips_before = my $ips_deleted = 0;
41335    my $t = time;
41336    my $tdif;
41337    my $tdifut;
41338    my $newscore = 0;
41339    my $mcount;
41340    my ( $ct, $ut, $pbstatus, $score, $sip, $reason );
41341
41342    delete $PBBlack{"0.0.0.0"};
41343    delete $PBBlack{""};
41344
41345    while ( my ( $k, $v ) = each(%PBBlack) ) {
41346
41347
41348        ( $ct, $ut, $pbstatus, $score, $sip, $reason ) = split( " ", $v );
41349
41350        $tdif   = $t - $ct;
41351        $tdifut = $t - $ut;
41352        $ips_before++;
41353        next if $reason  =~ /preHeader/i;
41354        next if $reason  =~ /DNSBLfailed/i;
41355		next if $reason  =~ /LimitingSameSubject/i;
41356		next if $reason  =~ /SPFfail/i;
41357		next if $reason  =~ /DNSBLneutral/i;
41358
41359
41360
41361        if ( $k =~ /$IPprivate/ ) {
41362            delete $PBBlack{$k};
41363            $ips_deleted++;
41364            next;
41365        }
41366
41367        if ( $tdif > $BlackExpiration * 60 && $score < $PenaltyLimit ) {
41368            delete $PBBlack{$k};
41369            $ips_deleted++;
41370            next;
41371        }
41372
41373        if ($tdifut > 7 * 24 * 3600 ) {
41374            delete $PBBlack{$k};
41375            $ips_deleted++;
41376            next;
41377        }
41378
41379        if (
41380               exists $PBWhite{$k}
41381            || ( $ispip && matchIP( $k, 'ispip', 0, 1 ) )
41382            || matchIP( $k, 'noProcessingIPs', 0, 1 )
41383            || matchIP( $k, 'noProcessingSenderBaseIPs', 0, 1 )
41384            || matchIP( $k, 'whiteListedIPs',  0, 1 )
41385            || ( $noDelay && matchIP( $k, 'noDelay', 0, 1 ) )
41386            || ( $noPB    && matchIP( $k, 'noPB',    0, 1 ) )
41387            || (   $contentOnlyRe
41388                && $contentOnlyReRE != ""
41389                && $k =~ ( '(' . $contentOnlyReRE . ')' ) )
41390          )
41391        {
41392            delete $PBBlack{$k};
41393            $ips_deleted++;
41394            next;
41395        }
41396
41397
41398    }
41399
41400    mlog( 0,
41401"PenaltyBox: cleaning BlackBox finished; before=$ips_before, deleted=$ips_deleted"
41402    ) if $MaintenanceLog && $ips_before>0;
41403
41404
41405
41406}
41407
41408
41409sub cleanWhitePB {
41410    my $ips_before = my $ips_deleted = 0;
41411    my $t          = time;
41412    my $newscore   = 0;
41413    my $mcount;
41414    delete $PBWhite{"0.0.0.0"};
41415    delete $PBWhite{""};
41416	my $maxtime1;
41417    my $maxtime2 = $WhiteExpiration*24*3600;
41418
41419    while ( my ( $k, $v ) = each(%PBWhite) ) {
41420        &ThreadMaintMain2() if  ! $ips_before % 100;
41421        $mcount++;
41422        if ( $mcount == 100 ) {
41423            $mcount = 0;
41424            &MainLoop2();
41425        }
41426        my ( $ct, $ut, $pbstatus, $reason ) = split( " ", $v );
41427        $ips_before++;
41428
41429        if ($pbstatus == 3) {           # an entry from global PB
41430            if ($t-$ut>=$maxtime1) {
41431                delete $PBWhite{$k};
41432                $ips_deleted++;
41433            }
41434            next;
41435        }
41436
41437        if ( matchIP( $k, 'denySMTPConnectionsFromAlways', 0, 1 ) ) {
41438            delete $PBWhite{$k};
41439            $ips_deleted++;
41440            next;
41441        }
41442        if ( matchIP( $k, 'noPBwhite', 0, 1 ) ) {
41443            delete $PBWhite{$k};
41444            $ips_deleted++;
41445            next;
41446        }
41447        if ( $k =~ /$IPprivate/ ) {
41448            delete $PBWhite{$k};
41449            $ips_deleted++;
41450            next;
41451        }
41452        if ( $t - $ut >= $maxtime2 ) {
41453            delete $PBWhite{$k};
41454            $ips_deleted++;
41455        }
41456
41457    }
41458
41459      mlog( 0,
41460"PenaltyBox: cleaning WhiteBox finished; before=$ips_before, deleted=$ips_deleted"
41461        ) if $MaintenanceLog && $ips_before>0;
41462
41463
41464}
41465
41466sub cleanCacheRBL {
41467    d('cleanCacheRBL');
41468    my $ips_before = my $ips_deleted = 0;
41469    my $t = time;
41470    my $ct;
41471    my $mm;
41472    my $status;
41473    my @sp;
41474    my $maxtime = $RBLCacheInterval * 24 * 3600;
41475    while (my ($k,$v)=each(%RBLCache)) {
41476
41477        ( $ct, $mm, $status, @sp ) = split( ' ', $v );
41478
41479		$maxtime = $RBLCacheInterval * 8 * 3600 if $status = 2;
41480		$maxtime = $RBLCacheInterval * 24 * 3600 if $status = 1;
41481        $ips_before++;
41482        if ( $t - $ct >= $maxtime  ) {
41483            delete $RBLCache{$k};
41484            $ips_deleted++;
41485            next;
41486        }
41487
41488        my $spstr = join(' ',@sp);
41489        foreach my $sp (@sp) {
41490            my $tsp = $sp;
41491            $tsp =~ s/([^\{]+).*/$1/o;
41492            next if grep(/\Q$tsp\E/i,@rbllist);
41493            $spstr =~ s/ ?\Q$sp\E//ig;
41494        }
41495        if ($spstr) {
41496            $RBLCache{$k} = "$ct $mm $status $spstr";
41497        } else {
41498            delete $RBLCache{$k};
41499            $ips_deleted++;
41500        }
41501    }
41502
41503    mlog( 0, "DNSBLCache: cleaning cache finished: IP\'s before=$ips_before, deleted=$ips_deleted" ) if $MaintenanceLog && $ips_before != 0;
41504    if ( $ips_before == 0) {
41505        %RBLCache=();
41506		&SaveHash('RBLCache');
41507
41508    }
41509}
41510
41511
41512
41513
41514sub cleanCacheSubject {
41515    my $subs_before;
41516    my $subs_deleted = 0;
41517    my $t = time;
41518
41519    while ( my ( $k, $ct ) = each(%SameSubjectCache) ) {
41520
41521        $subs_before++;
41522        if ( $t - $ct >= $SameSubjectExpiration * 24 * 3600 ) {
41523            delete $SameSubjectCache{$k};
41524            $subs_deleted++;
41525        }
41526    }
41527    mlog( 0,
41528"SameSubjectCache: cleaning up cache finished; before=$subs_before, deleted=$subs_deleted"
41529    ) if $MaintenanceLog && $subs_before>0;
41530
41531}
41532
41533sub cleanCachePTR {
41534    my $ips_before = my $ips_deleted = 0;
41535    my $t = time;
41536    my $ct;
41537    my $status;
41538    my $mcount;
41539    while ( my ( $k, $v ) = each(%PTRCache) ) {
41540        &ThreadMaintMain2() if  ! $ips_before % 100;
41541        $mcount++;
41542        if ( $mcount == 100 ) {
41543            $mcount = 0;
41544            &MainLoop2();
41545        }
41546        ( $ct, $status ) = split( " ", $v );
41547
41548        $ips_before++;
41549        if ( $t - $ct >= $PTRCacheInterval * 24 * 3600 ) {
41550            delete $PTRCache{$k};
41551            $ips_deleted++;
41552        }
41553    }
41554    mlog( 0,
41555"PTRCache: cleaning up cache finished; before=$ips_before, deleted=$ips_deleted"
41556    ) if $MaintenanceLog && $ips_before>0;
41557
41558
41559
41560}
41561
41562
41563sub cleanCacheSSLfailed {
41564    d('cleanCacheSSLfailed');
41565    my $ips_before= my $ips_deleted=0;
41566    my $ct;
41567    my $t=time;
41568    while (my ($k,$v)=each(%SSLfailed)) {
41569        &ThreadMaintMain2() if $WorkerNumber == 10000 && ! $ips_before % 100;
41570        $ips_before++;
41571
41572        if ($t-$v>=43200) {   # 3600*12
41573            delete $SSLfailed{$k};
41574            $ips_deleted++;
41575        }
41576    }
41577    mlog(0,"SSLfailedCache: cleaning cache finished: IP\'s before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog && $ips_before > 0;
41578    if ($ips_before==0) {
41579        %SSLfailed=();
41580    }
41581}
41582sub cleanCacheRWL {
41583	return;
41584    my $ips_before = my $ips_deleted = 0;
41585    my $t = time;
41586    my $ct;
41587    my $status;
41588    my $mcount;
41589    while ( my ( $k, $v ) = each(%RWLCache) ) {
41590        &ThreadMaintMain2() if  ! $ips_before % 100;
41591        ( $ct, $status ) = split( " ", $v );
41592        $mcount++;
41593        if ( $mcount == 100 ) {
41594            $mcount = 0;
41595            &MainLoop2();
41596        }
41597
41598
41599        $ips_before++;
41600        if ( $t - $ct >= $RWLCacheInterval * 3600 * 24 ) {
41601            delete $RWLCache{$k};
41602            $ips_deleted++;
41603        }
41604    }
41605    mlog( 0,
41606"RWLCache: cleaning up cache finished; before=$ips_before, deleted=$ips_deleted"
41607    ) if $MaintenanceLog && $ips_before>0;
41608
41609
41610}
41611
41612sub cleanCacheMXA {
41613
41614    my $ips_before = my $ips_deleted = 0;
41615    my $ct;
41616    my $status;
41617    my $t = time;
41618    my $mcount;
41619    my $maxtime = $MXACacheInterval*3600 * 24;
41620    while (my ($k,$v)=each(%MXACache)) {
41621
41622        ($ct,$status)=split(" ",$v);
41623
41624        $ips_before++;
41625
41626        if ($t-$ct>=$maxtime) {
41627            delete $MXACache{$k};
41628            $ips_deleted++;
41629        }
41630    }
41631    mlog( 0,
41632"MXACache: cleaning up cache finished; before=$ips_before, deleted=$ips_deleted"
41633    ) if $MaintenanceLog && $ips_before>0;
41634
41635
41636
41637
41638}
41639
41640sub cleanCacheSB {
41641    d('cleanCacheSB');
41642    my $ips_before= my $ips_deleted=0;
41643    my $t=time;
41644    my %delWOL;
41645    while (my ($k,$v)=each(%SBCache)) {
41646        &ThreadMaintMain2() if $WorkerNumber == 10000 && ! $ips_before % 100;
41647        my $mSBCacheExp = $SBCacheExp;
41648        my ( $ct, $status, $data ) = split( "!", $v );
41649        my ( $ipcountry,  $orgname,  $domainname , @res) = split( /\|/o, $data ) ;
41650
41651        $ips_before++;
41652        $mSBCacheExp = 10 * $SBCacheExp if ($status == 2);
41653        if ($t-$ct>=$mSBCacheExp*3600*24) {
41654            delete $SBCache{$k};
41655            delete $WhiteOrgList{lc $domainname};
41656            $delWOL{$orgname} = 1;
41657            $ips_deleted++;
41658        }
41659    }
41660    while (my ($k,$v)=each(%WhiteOrgList)) {
41661        delete $WhiteOrgList{$k} if exists $delWOL{$v};
41662    }
41663
41664    mlog(0,"SenderBaseCache: cleaning cache finished: IP\'s before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog && $ips_before != 0;
41665    if ($ips_before==0) {
41666        %SBCache=();
41667        if ($pbdb =~ /DB:/o && ! $failedTable{SBCache}) {
41668        } else {
41669            &SaveHash('SBCache');
41670        }
41671    }
41672}
41673
41674sub cleanCacheSPF {
41675    d('cleanCacheSPF');
41676
41677    my $ips_before= my $ips_deleted=0;
41678    my $t=time;
41679    my $mcount;
41680    my $maxtime = $SPFCacheInterval*24*3600;
41681    while (my ($k,$v)=each(%SPFCache)) {
41682        my ($ct, $result, $helo)=split(' ',$v);
41683        $mcount++;
41684        if ( $mcount == 1000 ) {
41685            $mcount = 0;
41686            &MainLoop2();
41687        }
41688        $ips_before++;
41689        if ($t-$ct>=$maxtime or $k !~ /\s/o) {
41690            delete $SPFCache{$k};
41691            $ips_deleted++;
41692        }
41693    }
41694    mlog(0,"SPFCache: cleaning up cache finished: IP\'s before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog && $ips_before != 0;
41695
41696}
41697
41698
41699
41700sub cleanCacheURI {
41701    d('cleanCacheURI');
41702    my $domains_before= my $domains_deleted=0;
41703    my $t=time;
41704    my $ct;my $status;my @sp;my $maxtime1;my $maxtime2;
41705    $maxtime1 = $maxtime2 = $URIBLCacheInterval*24*3600;
41706
41707
41708    while (my ($k,$v)=each(%URIBLCache)) {
41709        &MainLoop2() if  ! $domains_before % 100;
41710        ( $ct, $status, @sp ) = split( ' ', $v );
41711
41712        $domains_before++;
41713        if (!$URIBLCacheInterval) {
41714        	delete $URIBLCache{$k};
41715            $domains_deleted++;
41716        	next;
41717        }
41718        if ($status==2 && $t-$ct>=$maxtime1) {
41719            delete $URIBLCache{$k};
41720            $domains_deleted++;
41721            next;
41722        }
41723        if ($t-$ct>=$maxtime2) {
41724            delete $URIBLCache{$k};
41725            $domains_deleted++;
41726        }
41727        next if $status == 2;
41728        my $spstr = join(' ',@sp);
41729        foreach my $sp (@sp) {
41730            my $tsp = $sp;
41731            $tsp =~ s/([^\<]+).*/$1/;
41732            next if grep(/$tsp/i,@uribllist);
41733            $spstr =~ s/ ?$sp//ig;
41734        }
41735        if ($spstr) {
41736            $URIBLCache{$k} = "$ct $status $spstr";
41737        } else {
41738            delete $URIBLCache{$k};
41739            $domains_deleted++;
41740        }
41741    }
41742    mlog(0,"URIBLCache: cleaning cache finished: Domains before=$domains_before, deleted=$domains_deleted") if  $MaintenanceLog && $domains_before != 0;
41743    if ($domains_before==0) {
41744        %URIBLCache=();
41745		&SaveHash('URIBLCache');
41746    }
41747}
41748
41749
41750
41751sub cleanTrapPB {
41752    my $addresses_before = my $addresses_deleted = 0;
41753    my $t = time;
41754    my $mcount;
41755
41756    while ( my ( $k, $v ) = each(%PBTrap) ) {
41757        $mcount++;
41758        if ( $mcount == 1000 ) {
41759            $mcount = 0;
41760            &MainLoop2();
41761        }
41762		$addresses_before++;
41763
41764        my ( $ct, $ut, $counter ) = split( " ", $v );
41765
41766
41767        if ( $t - $ut >= $PBTrapCacheInterval  * 3600
41768        or matchSL( $k, 'noPenaltyMakeTraps',1 ))
41769        {
41770            delete $PBTrap{$k};
41771            $addresses_deleted++;
41772            next;
41773        }
41774        if ($t - $ct >= $PBTrapCacheInterval  * 3600 && $counter < $PenaltyMakeTraps) {
41775       	    my $data = "$t $t 0 ";
41776        	$PBTrap{$k} = $data;
41777        	next;
41778        }
41779
41780    }
41781    mlog( 0,
41782"PBTrap: cleaning finished; before=$addresses_before, deleted=$addresses_deleted"
41783    ) if $MaintenanceLog && $addresses_before>0 ;
41784
41785
41786}
41787
41788
41789
41790sub cleanCacheAUTHErrors {
41791    d('cleanCacheAUTHErrors');
41792
41793    my $i = 0;
41794    while (my ($k,$v)=each(%AUTHErrors)) {
41795        if (--$AUTHErrors{$k} <= 0) {
41796            delete $AUTHErrors{$k};
41797        }
41798        $i++;
41799    }
41800
41801
41802}
41803sub cleanCacheDelayIPPB {
41804    d('cleanCacheDelayIPPB');
41805    my $ips_deleted = 0;
41806    my $ips_before = 0;
41807    my $t = time - 24 * 3600 ;
41808    while (my ($k,$v)=each(%DelayIPPB)) {
41809
41810        $ips_before++;
41811        if ($DelayIPPB{$k} <= $t) {
41812            delete $DelayIPPB{$k};
41813            $ips_deleted++;
41814        }
41815    }
41816
41817}
41818sub cleanNotSpamTags {
41819    d('cleanNotSpamTags');
41820    my $ips_deleted = 0;
41821    my $ips_before = 0;
41822    my $t = time - 24 * 3600 ;
41823    while (my ($k,$v)=each(%NotSpamTags)) {
41824
41825        $ips_before++;
41826        if ($NotSpamTags{$k} <= $t) {
41827            delete $NotSpamTags{$k};
41828            $ips_deleted++;
41829        }
41830    }
41831
41832}
41833sub cleanCacheSSLfailed {
41834    d('cleanCacheSSLfailed');
41835    my $ips_before= my $ips_deleted=0;
41836    my $ct;
41837    my $t=time;
41838    while (my ($k,$v)=each(%SSLfailed)) {
41839
41840        $ips_before++;
41841
41842        if ($t-$v>=43200) {   # 3600*12
41843            delete $SSLfailed{$k};
41844            $ips_deleted++;
41845        }
41846    }
41847    mlog(0,"SSLfailedCache: cleaning cache finished: IP\'s before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog && $ips_before > 0;
41848    if ($ips_before==0) {
41849        %SSLfailed=();
41850    }
41851}
41852
41853sub cleanCacheSMTPdomainIP {
41854	d('cleanCacheSMTPdomainIP');
41855    my $ips_before= my $ips_deleted=0;
41856    my $ct;
41857    my $t=time;
41858    while (my ($k,$v)=each(%SMTPdomainIP)) {
41859        $ips_before++;
41860
41861        if ($t-$v>=$SMTPdomainIPTriesExpiration{$k}) {
41862            $ips_deleted++;
41863
41864            delete $SMTPdomainIP{$k};
41865            delete $SMTPdomainIPTries{$k};
41866            delete $SMTPdomainIPTriesExpiration{$k};
41867        }
41868    }
41869    mlog(0,"SMTPdomainIP: cleaning up cache finished: before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog > 1 && $ips_before > 0;
41870}
41871
41872sub cleanCacheIPNumTries {
41873	d('cleanCacheIPNumTries');
41874    my $ips_before= my $ips_deleted=0;
41875    my $ct;
41876    my $t=time;
41877    while (my ($k,$v)=each(%IPNumTries)) {
41878        $ips_before++;
41879
41880        if ($t-$v>=$IPNumTriesExpiration{$k}) {
41881            $ips_deleted++;
41882
41883            delete $IPNumTries{$k};
41884            delete $IPNumTriesDuration{$k};
41885            delete $IPNumTriesExpiration{$k};
41886        }
41887    }
41888    mlog(0,"IPNumTries: cleaning up cache finished: before=$ips_before, deleted=$ips_deleted") if  $MaintenanceLog > 1 && $ips_before > 0;
41889}
41890
41891
41892sub cleanCacheLocalFrequency {
41893    d('cleanCacheLocalFrequency');
41894    unless ($LocalFrequencyInt) {%localFrequencyCache = (); return;}
41895    unless ($LocalFrequencyNumRcpt) {%localFrequencyCache = (); return;}
41896
41897    my $adr_before= my $adr_deleted=0;
41898    my $t=time;
41899    while (my ($k,$v)=each(%localFrequencyCache)) {
41900
41901        my %F = split(/ /o,$v);
41902        foreach (sort keys %F) {
41903            delete $F{$_} if ($_ + $LocalFrequencyInt  < $t);
41904        }
41905        if (! scalar keys %F) {
41906            delete $localFrequencyCache{$k};
41907            $adr_deleted++;
41908        }
41909    }
41910    mlog(0,"localFrequency: cleaning up cache finished: addresses\'s before=$adr_before, deleted=$adr_deleted") if  $MaintenanceLog >= 2 && $adr_before > 0;
41911
41912    while (my ($k,$v)=each(%localFrequencyNotify)) {
41913        delete $localFrequencyNotify{$k} if $v < time;
41914    }
41915
41916}
41917
41918
41919
41920sub saveSMTPconnections {
41921    mlog( 0, "sig USR1 -- saving concurrent session stats" );
41922    open( my $SMTP, ">","$base/smtp.txt" );
41923    print $SMTP "$smtpConcurrentSessions\n";
41924    close($SMTP);
41925}
41926
41927sub cleanUpFiles {
41928    my ($folder, $filter, $filetime) = @_;
41929    d('cleanUpFiles - '."$folder, $filter, $filetime");
41930    my $textfilter;$textfilter = " (*$filter)" if $filter;
41931    my @files;
41932
41933    my $count;
41934    my $dir = ($folder !~ /\Q$base\E/i) ? "$base/$folder" : $folder ;
41935    $dir =~ s/\\/\//g;
41936    return unless -e $dir;
41937    mlog(0,"info: starting cleanup old files$textfilter for folder $dir") if $MaintenanceLog >= 2;
41938    opendir(my $DIR,"$dir");
41939    @files = readdir($DIR);
41940    close $DIR;
41941    foreach my $file (@files) {
41942        next if $file eq '.';
41943        next if $file eq '..';
41944        next if ($filter && $file !~ /$filter$/i);
41945        next if ($filter && $file =~ /^$filter$/i);
41946        $file = "$dir/$file";
41947        next if -d $file;
41948        next unless -w $file;
41949        my $dtime=(stat($file))[9]-time;
41950        if (($dtime < $filetime * -1) or ($dtime > 0 && $dtime < 60 - $filetime)) {
41951            unlink $file;
41952            $count++;
41953
41954        }
41955    }
41956    mlog(0,"info: deleted $count old$textfilter files from folder $dir") if $MaintenanceLog && $count;
41957}
41958
41959sub cleanUpMailLog {
41960    d('cleanUpMailLog');
41961    return unless $MaxLogAge;
41962    return unless $logfile;
41963    return if $logfile =~ /\/?maillog\.log$/i;
41964    my $age = $MaxLogAge * 3600 * 24;
41965    my ($logdir, $logdirfile) = $logfile=~/^(.*[\/\\])?(.*?)$/;
41966    $logdir = $base unless $logdir;
41967    return unless $logdirfile;
41968
41969    &cleanUpFiles($logdir,$logdirfile,$age);
41970}
41971
41972
41973
41974sub modifyList {
41975    $GPBmodTestList->('email',@_);
41976}
41977sub ThreadMaintMain2 {
41978    &MainLoop2();
41979}
41980
41981sub modifyFile {
41982    my ($list,$action,$reason,$name)=@_;
41983    my $fil;
41984    my $NP;
41985    if(${$list} =~ /^\s*file:\s*(.+)\s*$/io) {
41986        $fil=$1;
41987    } else {
41988        return 0;
41989    }
41990    $fil="$base/$fil" if $fil!~/^(([a-z]:)?[\/\\]|\Q$base\E)/io;
41991    return 0 if ( !-e "$fil");
41992    my @lines;
41993    (open ($NP,'<', $fil)) or return 0;
41994    @lines = <$NP>;
41995    close ($NP);
41996    unlink "$fil.bak";
41997    if ($action eq 'delete') {rename("$fil","$fil.bak"); (open ($NP, ">","$fil")) or return 0;}
41998    if ($action eq 'add') {copy("$fil","$fil.bak");(open ($NP, ">>","$fil")) or return 0;}
41999    binmode $NP;
42000
42001    if ($action eq 'delete') {
42002        while (@lines) {
42003            my $k = shift @lines;
42004            $k =~ /\r?\n$/;
42005            if ($k=~/$name/i) {
42006                mlog(0,"email: $name removed from $list-List - $reason");
42007                next;
42008            }
42009            print $NP "$k\n";
42010        }
42011    }
42012
42013    if ($action eq 'add') {
42014        print $NP "\n$name  # added by GUI, email or senderbase - $reason";
42015        mlog(0,"email: $name added to $list-List - $reason",1) ;
42016    }
42017    close ($NP);
42018    return 1;
42019}
42020
42021sub deleteNP {
42022    return if $EmailNoNPRemove;
42023    my ( $name, $reason ) = @_;
42024    my $fil;
42025    my $mynp = $Config{"noProcessing"};
42026    if ( $mynp =~ /^\s*file:\s*(.+)\s*$/i ) {
42027        $fil = $1;
42028    } else {
42029        return;
42030    }
42031    $fil = "$base/$fil" if $fil !~ /^(([a-z]:)?[\/\\]|\Q$base\E)/;
42032    return if ( !-e "$fil" );
42033    my ( @lines, $nlines, $kk );
42034    open( my $NP, '<',$fil );
42035    @lines = <$NP>;
42036    close($NP);
42037    unlink "$fil.bak";
42038    rename( "$fil", "$fil.bak" );
42039    open( $NP, ">","$fil" );
42040
42041    foreach my $k (@lines) {
42042        mlog( 0, "email: $name removed from NoProcessing-List - $reason", 1 )
42043          if $k =~ /$name/i;
42044
42045        next if $k =~ /$name/i;
42046        print $NP "$k";
42047    }
42048    close($NP);
42049}
42050
42051# SRS Settin Checks, and Update.
42052sub updateSRS {
42053    my ( $name, $old, $new, $init ) = @_;
42054    mlog( 0, "AdminUpdate: SRS-Enable updated from '$old' to '$new'" )
42055      unless $init || $new eq $old;
42056    $EnableSRS = $Config{EnableSRS} = $new;
42057    if ( !$CanUseSRS ) {
42058        mlog( 0,
42059"AdminUpdate: SRS-Enable updated from '1' to '', Mail::SRS not installed"
42060        ) if $Config{EnableSRS};
42061        $EnableSRS = $Config{EnableSRS} = undef;
42062        return
42063'<span class="negative">*** Mail::SRS must be installed before enabling SRS.</span>';
42064    } else {
42065        updateSRSAD( 'updateSRSAD', '', $Config{SRSAliasDomain}, 'Cascading' );
42066    }
42067}
42068
42069sub updateSRSAD {
42070    my ( $name, $old, $new, $init ) = @_;
42071    mlog( 0, "AdminUpdate: SRS Alias Domain updated from '$old' to '$new'" )
42072      unless $init || $new eq $old;
42073    $SRSAliasDomain = $new;
42074    if ( $new eq '' ) {
42075        mlog( 0,
42076"AdminUpdate: SRS-Enable updated from '1' to '', SRSAliasDomain not defined "
42077        ) if $Config{EnableSRS};
42078        $EnableSRS = $Config{EnableSRS} = undef;
42079        return
42080'<span class="negative">*** SRSAliasDomain must be defined before enabling SRS.</span>';
42081    } else {
42082        updateSRSSK( 'updateSRSSK', '', $Config{SRSSecretKey}, 'Cascading' );
42083    }
42084}
42085
42086sub updateSRSSK {
42087    my ( $name, $old, $new, $init ) = @_;
42088    mlog( 0, "AdminUpdate: SRS Secret Key updated from '$old' to '$new'" )
42089      unless $init || $new eq $old;
42090    $SRSSecretKey = $new;
42091    if ( length($new) < 5 ) {
42092        mlog( 0,
42093"AdminUpdate: SRS-Enable updated from '1' to '', SRSSecretKey not at least 5 characters long "
42094        ) if $Config{EnableSRS};
42095        $EnableSRS = $Config{EnableSRS} = undef;
42096        return
42097'<span class="negative">*** SRSSecretKey must be at least 5 characters long before enabling SRS.</span>';
42098    } elsif ($CanUseSRS) {
42099        if ( $init && $EnableSRS ) {
42100            return ' & SRS activated';
42101        } else {
42102            return '';
42103        }
42104    }
42105}
42106
42107# Database File Logging Frequency Setup.
42108sub freqNonSpam {
42109    my ( $name, $old, $new, $init ) = @_;
42110    mlog( 0,
42111        "AdminUpdate: Non Spam Logging Frequency updated from '$old' to '$new'"
42112    ) unless $init || $new eq $old;
42113    $logFreq[2] = $new;
42114
42115}
42116
42117sub freqSpam {
42118    my ( $name, $old, $new, $init ) = @_;
42119    mlog( 0,
42120        "AdminUpdate: Spam Logging Frequency updated from '$old' to '$new'" )
42121      unless $init || $new eq $old;
42122    $logFreq[3] = $new;
42123    return '';
42124}
42125
42126sub syncCanSync {
42127    return ($syncConfigFile && $syncCFGPass && $syncServer && ($enableCFGShare or $isShareMaster or $isShareSlave)) ? 1 : 0;
42128}
42129
42130sub ConfigChangeSyncServer {my ($name, $old, $new, $init)=@_;
42131    return '' if $new eq $old && ! $init;
42132    return '<span class="negative"></span>' if $WorkerNumber != 0;
42133    if (! $new or $init) {
42134        $Config{$name} = $new;
42135        ${$name} = $new;
42136        return &ConfigChangeEnableCFGSync('enableCFGShare', $enableCFGShare, '', '');
42137    }
42138    $Config{$name} = $new;
42139    ${$name} = $new;
42140    mlog(0,"AdminUpdate: $name changed from '$old' to '$new'")  unless $init || $new eq $old;
42141    if (&syncLoadConfigFile()) {
42142        return '';
42143    } else {
42144        return "<span class=\"positive\">updated - but sync-config-file was still not loaded - sync config is still incomplete</span>";
42145    }
42146}
42147
42148sub ConfigChangeSyncFile {my ($name, $old, $new, $init)=@_;
42149    my $tnew;$tnew=checkOptionList($new,'syncConfigFile',$init) if $WorkerNumber == 0 or $WorkerNumber == 10000;
42150    return '<span class="negative"></span>' if $WorkerNumber != 0;
42151    $Config{$name} = $new;
42152    ${$name} = $new;
42153    mlog(0,"AdminUpdate: $name changed from $old to $new")  unless $init || $new eq $old;
42154    $NextSyncConfig = time - 1;
42155    if (&syncLoadConfigFile()) {
42156        return '';
42157    } else {
42158        return "<span class=\"positive\">updated - but sync-config-file was still not loaded - sync config is still incomplete</span>";
42159    }
42160}
42161
42162sub ConfigChangeSync {my ($name, $old, $new, $init)=@_;
42163    return '' if $new eq $old && ! $init;
42164    return '<span class="negative"></span>' if $WorkerNumber != 0;
42165    $Config{$name} = $new;
42166    ${$name} = $new;
42167    return '' if $init;
42168    my $text = ($name eq 'syncCFGPass') ? '' : "from '$old' to '$new'";
42169    mlog(0,"AdminUpdate: $name changed $text") unless $init || $new eq $old;
42170    if (&syncLoadConfigFile()) {
42171        return '';
42172    } else {
42173        return "<span class=\"positive\">updated - but sync-config-file was still not loaded - sync config is still incomplete</span>";
42174    }
42175}
42176
42177sub ConfigChangeEnableCFGSync {my ($name, $old, $new, $init)=@_;
42178    return '<span class="negative"></span>' if $WorkerNumber != 0;
42179    return if $AsASecondary;
42180    my $failed;
42181    if ($new) {
42182        unless ($isShareMaster or $isShareSlave) {
42183            $new = $old = '';
42184            $enableCFGShare = $new;
42185            $Config{enableCFGShare} = $new;
42186            $failed .= "<span class=\"negative\">any of isShareMaster or isShareSlave must be selected first</span><br />";
42187        }
42188        unless ($syncConfigFile) {
42189            $new = $old = '';
42190            $enableCFGShare = $new;
42191            $Config{enableCFGShare} = $new;
42192            $failed .= "<span class=\"negative\">syncConfigFile must be configured first</span><br />";
42193        }
42194        unless ($syncServer) {
42195            $new = $old = '';
42196            $enableCFGShare = $new;
42197            $Config{enableCFGShare} = $new;
42198            $failed .= "<span class=\"negative\">at least one default syncServer must be configured first</span><br />";
42199        }
42200        unless ($syncCFGPass) {
42201            $new = $old = '';
42202            $enableCFGShare = $new;
42203            $Config{enableCFGShare} = $new;
42204            $failed .= "<span class=\"negative\">a password in syncCFGPass must be configured first</span><br />";
42205        }
42206        return $failed if $failed;
42207    }
42208    mlog(0,"AdminUpdate: $name changed from '$old' to '$new'") unless $init || $new eq $old;
42209
42210    $enableCFGShare = $new;
42211    $Config{enableCFGShare} = $new;
42212
42213    return '<span class="positive">config synchronization is now activated</span>' if $new;
42214    return '<span class="positive">config synchronization is now deactivated</span>';
42215}
42216
42217sub syncGetStatus {
42218    my $name = shift;
42219
42220    return $ConfigSync{$name}->{sync_cfg} if ($ConfigSync{$name}->{sync_cfg} < 1);
42221	my $syncservers = $ConfigSyncServer{$name};
42222    my $res = 0;
42223    foreach my $syncserver ( split( ",", $syncservers ) ) {
42224
42225    	my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42226
42227        if ($v == 1) {
42228            $res = $v;
42229            last;
42230        } elsif ($v >= 2) {
42231            $v = 2;
42232        }
42233        $res |= $v;
42234    }
42235    return $res;
42236}
42237
42238sub syncedit {
42239    my $name = $qs{cfgparm};
42240
42241    return 'incomplete request' unless $name;
42242    return 'synchronization not allowed for ' . $name if exists $neverShareCFG{$name};
42243    my %sync_server;
42244    my @syncServer = (split(/\|/o,$syncServer));
42245    my %syncMode = (0 => 'no sync', 1 => 'out of sync', 2 => 'in sync', 3 => 'as slave', '' => 'remove');
42246    my $msg;
42247    my ($fn) = $syncConfigFile =~ /^ *file:(.+)$/io;
42248    while (my ($k,$v) = each %qs) {
42249        next if $k !~ /^sync_server(\d+)/o;
42250        $v =~ s/\s//go;
42251        next unless $v;
42252        $sync_server{$v} = $qs{'val'.$1} if $qs{'val'.$1} ne '';
42253    }
42254    my $enable_sync = $qs{enable} ? 1 : 0;
42255    if ($qs{theButton}){
42256        $ConfigSync{$name}->{sync_cfg} = $enable_sync;
42257        $ConfigSyncServer{$name} = {};
42258        my $i = 0;
42259        while (my ($k,$v) = each %sync_server) {
42260            $ConfigSyncServer{$name}="$k=$v"  if $i == 0;
42261            $ConfigSyncServer{$name}=",$k=$v" if $i > 0;
42262            $i++;
42263        }
42264
42265        unless ($i) {
42266            $ConfigSync{$name}->{sync_cfg} = 0;
42267            $msg .= "<hr>no sync peer defined for $name - synchronization is now disabled for $name<hr>\n";
42268        }
42269        if (&syncWriteConfig()) {
42270            $msg .= "<hr><span class=\"positive\">successfully saved changes to file $fn</span><hr>\n";
42271            $NextSyncConfig = time - 1;
42272        }
42273    }
42274	my $syncservers = $ConfigSyncServer{$name};
42275
42276    my $checked = $ConfigSync{$name}->{sync_cfg} ? 'checked="checked"' : '';
42277    $msg .= "<hr>resulting line in file $fn:<br /><br />$name:=$ConfigSync{$name}->{sync_cfg}";
42278    foreach my $syncserver ( split( ",", $syncservers ) ) {
42279    	my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42280        $msg .= ",$k=$v";
42281    }
42282    $msg .= '<br /><hr>';
42283
42284    my $s = '<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH="100%" >';
42285    $s .= '<tr><td>enable/disable synchronization for '.$name.' : ';
42286    $s .= "<input type=\"checkbox\" name=\"enable\" value=\"1\" $checked /></td></tr></table><hr>\n";
42287    $s .= '<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH="100%" >'."\n";
42288    my $i = 0;
42289    foreach my $k (@syncServer) {
42290        $i++;
42291
42292        $s .= "<tr><td>&nbsp;&nbsp;peer : ";
42293        $s .= "<span style=\"z-Index:100;\"><select size=\"1\" name=\"sync_server$i\">\n";
42294        my $sel = '';
42295        $sel = "selected=\"selected\"" if $syncservers =~ /$k/;
42296        $s .= "<option $sel value=\"$k\">$k</option>";
42297        $s .= "</select></span></td>\n";
42298
42299        $s .= "<td>&nbsp;&nbsp;mode/status : ";
42300        $s .= "<span style=\"z-Index:100;\"><select size=\"1\" name=\"val$i\">\n";
42301        my ($kserver,$vstatus);
42302        foreach my $syncserver ( split( ",", $syncservers ) ) {
42303    		($kserver,$vstatus) = split(/\s*\=\s*/o,$syncserver);
42304			last if $kserver eq $k;
42305    	}
42306
42307        for (0..3,'') {
42308            my $sel = '';
42309            $sel = "selected=\"selected\"" if $vstatus == $_;
42310            my $s1 = ($_ ne '') ? "($_)" : '';
42311            $s .= "<option $sel value=\"$_\">$syncMode{$_} $s1</option>\n";
42312        }
42313        $s .= "</select></span></td></tr>";
42314    }
42315    $s .= '</table>'."\n<hr>\n";
42316    $s .= '<input type="hidden" name="cfgparm" value="'.$name.'" />';
42317    $s .= '<input type="submit" name="theButton" value="Save Changes" />&nbsp;&nbsp;';
42318    $s .= '<input type="button" value="Close" onclick="javascript:window.close();"/>';
42319    $s .= $msg;
42320return <<EOT;
42321$headerHTTP
42322
42323<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
42324<head>
42325  <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
42326  <title>$currentPage ASSP SyncConfig ($myName - $name)</title>
42327  <link rel=\"stylesheet\" href=\"get?file=images/editor.css\" type=\"text/css\" />
42328</head>
42329<body onmouseover="this.focus();" >
42330    <div class="content">
42331      <form action="" method="post">
42332        $s
42333      </form>
42334    </div>
42335</body>
42336</html>
42337
42338EOT
42339}
42340
42341sub syncShowGUI {
42342    my $name = shift;
42343    return '' unless &syncCanSync();
42344#    d('syncShowGUI');
42345
42346
42347    if ($ConfigSync{$name}->{sync_cfg} == -1) {
42348        return '';
42349    } elsif ($ConfigSyncServer{$name} == 0) {
42350
42351        return $syncShowGUIDetails
42352          ? '&nbsp;&nbsp;<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');" >(shareable)</a>'
42353          : '<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');" onmouseover="showhint(\'<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\\'100%\\\'><tr><td>&nbsp;&nbsp;shareable</td></tr></table>\', this, event, \'90px\', \'1\'); return true;"><b><font color=\'black\'>&nbsp;&nbsp;&bull;</font></b></a>';
42354    } else {
42355        my $stat = &syncGetStatus($name);
42356        return '' if $stat == -1;
42357        return ($syncShowGUIDetails
42358          ? '&nbsp;&nbsp;<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');" >(shareable)</a>'
42359          : '<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');" onmouseover="showhint(\'<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\\'100%\\\'><tr><td>&nbsp;&nbsp;shareable</td></tr></table>\', this, event, \'90px\', \'1\'); return true;"><b><font color=\'black\'>&nbsp;&nbsp;&bull;</font></b></a>') if $stat == 0;
42360        my $ret = '&nbsp;&nbsp;(<span class="negative">shared: </span>';
42361        $ret = '&nbsp;&nbsp;(<span class="positive">shared: </span>' if ($stat == 2);
42362        my $shared = 0;
42363        my $syncservers = $ConfigSyncServer{$name};
42364        foreach my $syncserver ( split( ",", $syncservers ) ) {
42365        	my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42366
42367            $k =~ s/:.+$//o;
42368            if ($v == 0) {
42369                $ret .= "$k not shared, ";
42370            } elsif ($v == 1) {
42371                $ret .= "<span class=\"negative\">$k out of sync, </span>";
42372                $shared = 1;
42373            } elsif ($v == 2 or $v == 4) {
42374                $ret .= "<span class=\"positive\">$k in sync, </span>";
42375                $shared = 1;
42376            } elsif ($v == 3) {
42377                $ret .= "<span class=\"positive\">$k local slave mode, </span>";
42378                $shared = 1;
42379            }
42380        }
42381        $ret .= ')';
42382        $ret =~ s/(sync|mode), ([^\)]+?\))$/$1$2/o;
42383        if ($syncShowGUIDetails) {
42384            return '<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');">'.$ret.'</a>';
42385        }
42386        my $color = ($ret =~ /negative/o) ? 'red' : 'green';
42387        $color = 'black' unless $shared;
42388        $ret =~ s/"/\\'/go;
42389        $ret =~ s/\(|\)//go;
42390        $ret =~ s/, /\<br \/\>/go;
42391        $ret =~ s/: /:\<hr\>/go;
42392        return '<a href="javascript:void(0);" onclick="javascript:popSyncEditor(\''.$name.'\');" onmouseover="showhint(\'<table BORDER CELLSPACING=0 CELLPADDING=4 WIDTH=\\\'100%\\\'><tr><td>'.$ret.'</td></tr></table>\', this, event, \'220px\', \'1\'); return true;"><b><font color=\''. $color .'\'>&nbsp;&nbsp;&bull;</font></b></a>';
42393    }
42394}
42395
42396
42397sub syncLoadConfigFile {
42398	my $log = shift;
42399    my $RCF;
42400
42401    %ConfigSync = ();
42402    %ConfigSyncServer = ();
42403
42404
42405    while (my ($k,$v) = each %Config) {
42406        $ConfigSync{$k} = {};
42407
42408        $ConfigSync{$k}->{sync_cfg} = -1;
42409        $ConfigSyncServer{$k} = {};
42410
42411    }
42412    return 0 unless &syncCanSync();
42413    my ($fn) = $syncConfigFile =~ /^ *file:(.+)$/io;
42414    return 0 unless $fn;
42415    open($RCF,"<","$base/$fn") or return 0;
42416    d('syncLoadConfigFile');
42417    mlog(0,"loading config synchronization configuration file '$fn'") if $MaintenanceLog == 2 && $log;
42418    while (<$RCF>) {
42419        s/\r|\n//go;
42420        s/[#;].*//o;
42421        my ($k,$v) = split(/:=/o,$_,2);
42422        next unless $k;
42423        next if exists $neverShareCFG{$k};
42424        next unless exists $Config{$k};
42425        my @scfg = split(/\s*,\s*/o,$v);
42426        $ConfigSync{$k}->{sync_cfg} = shift @scfg || 0;
42427        if (! @scfg) {
42428            foreach my $se (split(/\|/o,$syncServer)) {
42429                push @scfg , "$se=1" if $ConfigSync{$k}->{sync_cfg};
42430            }
42431        }
42432        my $x=0;
42433        my $t;
42434        while (my $se = shift @scfg) {
42435            my ($server,$status) = split(/\s*\=\s*/o,$se);
42436            next unless $server;
42437
42438            $status = 3 if (! $isShareMaster && $isShareSlave);
42439
42440			$t = "$server=$status" 		if $x==0;
42441			$t .= ",$server=$status" 	if $x>0;
42442            $x++;
42443        }
42444        $ConfigSyncServer{$k} = "$t";
42445
42446    }
42447    close $RCF;
42448    &syncWriteConfig();
42449    return 1;
42450}
42451
42452sub syncWriteConfig {
42453    my $new;
42454    my $newST;
42455    return 0 unless &syncCanSync();
42456    my ($fn) = $syncConfigFile =~ /^ *file:(.+)$/io;
42457    return 0 unless $fn;
42458    open(my $RCF,">","$base/$fn.new") or return 0;
42459    open(my $RCFST,">","$base/files/sync_failed.txt");
42460    d('syncWriteConfig');
42461    binmode $RCF;
42462    binmode $RCFST;
42463    foreach my $c (@ConfigArray) {
42464        next if (! $c->[0] or @$c == 5);
42465        next if $ConfigSync{$c->[0]}->{sync_cfg} == -1;
42466        next if exists $neverShareCFG{$c->[0]};
42467        my $st;
42468        my $data = $c->[0] . ':=' . $ConfigSync{$c->[0]}->{sync_cfg};
42469
42470        if ( $ConfigSync{$c->[0]}->{sync_cfg} > 0 ) {
42471        	my $syncservers = $ConfigSyncServer{$c->[0]};
42472        		foreach my $syncserver ( split( ",", $syncservers ) ) {
42473    			my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42474    			$data .= ",$k=$v";
42475    			$st = 1 if $v == 1;
42476		 	}
42477		}
42478
42479        $new .= "$data\n";
42480        $newST .= "$data\n" if $st;
42481    }
42482    print $RCF $new;
42483    if ($newST) {
42484        print $RCFST '# ' , &timestring() , ' The following configuration values are still out of sync:',"\n\n";
42485        print $RCFST $newST;
42486    } else {
42487        print $RCFST '# ' , &timestring() , ' All configuration values are still synchronized.';
42488    }
42489    close $RCF;
42490    close $RCFST;
42491    if (open $RCF,"<","$base/$fn.bak") {
42492        binmode $RCF;
42493        my $bak = join('',<$RCF>);
42494        close $RCF;
42495        $new =~ s/\r|\n//go;
42496        $bak =~ s/\r|\n//go;
42497        if ($new eq $bak) {
42498            unlink "$base/$fn.new";
42499            return 1;
42500        }
42501    }
42502    unlink "$base/$fn.bak.bak.bak";
42503    rename "$base/$fn.bak.bak","$base/$fn.bak.bak.bak";
42504    rename "$base/$fn.bak","$base/$fn.bak.bak";
42505    rename "$base/$fn","$base/$fn.bak";
42506    rename "$base/$fn.new","$base/$fn";
42507    mlog(0,"syncCFG: saved sync configuration to $base/$fn") if $MaintenanceLog >= 2;
42508    $FileUpdate{"$base/$fn".'syncConfigFile'} = [stat("$base/$fn")]->[9];
42509    return 1;
42510}
42511
42512sub syncConfigDetect {
42513    my $name = shift;
42514
42515    return if $syncUser eq 'sync';
42516    return unless (&syncCanSync() && $enableCFGShare && $isShareMaster && $CanUseNetSMTP);
42517    return if exists $neverShareCFG{$name};
42518    return unless exists $Config{$name};
42519    return if $ConfigSync{$name}->{sync_cfg} < 1;
42520    my $stat = &syncGetStatus($name);
42521    return if $stat < 1;
42522    d("syncConfigDetect $name");
42523    my $syncservers = $ConfigSyncServer{$name};
42524
42525    my $r = 0;
42526    foreach my $syncserver ( split( ",", $syncservers ) ) {
42527    	my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42528        next if $v < 1;
42529        next if $v == 3;
42530        $r |= $v;
42531    }
42532    return unless $r;
42533    if ($r == 4) {
42534        foreach my $syncserver ( split( ",", $syncservers ) ) {
42535    		my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42536    		$syncservers=~s/$k=4/$k=2/g;
42537
42538        }
42539        $ConfigSyncServer{$name} = $syncservers;
42540        &syncWriteConfig();
42541        return;
42542    }
42543    mlog(0,"syncCFG: start synchronization of $name") if $MaintenanceLog;
42544    &syncConfigSend($name);
42545}
42546
42547sub syncConfigSend {
42548    my $name = shift;
42549# ConfigName.sprintf("%.3f",(Time::HiRes::time())).ip|host.cfg
42550# first line plain var name\r\n  rest Base64 \r\n.\r\n
42551# varname:=value\r\n
42552# file start (.+)$
42553# file eof\s*$
42554
42555    return 0 unless (&syncCanSync() && $enableCFGShare && $CanUseNetSMTP);
42556    return 0 unless $isShareMaster;
42557    return 0 if exists $neverShareCFG{$name};
42558    return 0 unless exists $Config{$name};
42559    return 0 if $ConfigSync{$name}->{sync_cfg} < 1;
42560    my $syncservers = $ConfigSyncServer{$name};
42561    my ($k,$v);
42562    my $r = 0;
42563    foreach my $syncserver ( split( ",", $syncservers ) ) {
42564    	my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42565        next if $v < 1;
42566        next if $v == 3;
42567        $r |= $v;
42568    }
42569    return 0 unless $r;
42570    if ($r == 4) {
42571        foreach my $syncserver ( split( ",", $syncservers ) ) {
42572    		my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42573
42574            $syncservers=~s/$k=4/$k=2/g;
42575        }
42576        $ConfigSyncServer{$name} = $syncservers;
42577        &syncWriteConfig();
42578        return 0;
42579    }
42580    d("syncConfigSend $name");
42581    mlog(0,"syncCFG: request to synchronize $name") if $MaintenanceLog;
42582    my $body = "$name\r\n";
42583    $body .= MIME::Base64::encode_base64("$name:=" . ${$name},'') . "\r\n";
42584    my $fil;
42585    foreach my $f (@PossibleOptionFiles) {
42586        if ($name eq $f->[0] && $Config{$f->[0]} =~ /^ *file: *(.+)/io) {
42587           my $ffil = $fil = $1;
42588           $ffil="$base/$ffil" if $ffil!~/^\Q$base\E/io;
42589           if (defined ${$name.'RE'} && ${$name.'RE'} =~ /^$neverMatchRE$/o && -s $ffil) {
42590              mlog(0,"syncCFG: warning - the file '$fil' is not empty, but the running regex for $name is a never matching regex (used for empty files) - the sync request will be ignored, because it seems that the file contains an invalid regex");
42591              my $syncservers = $ConfigSyncServer{$name};
42592              foreach my $syncserver ( split( ",", $syncservers ) ) {
42593              	  my ($k,$v) = split(/\s*\=\s*/o,$syncserver);
42594                  next if $v < 1;
42595                  next if $v == 3;
42596                  if ($v == 4) {
42597					  $syncservers=~s/$k=4/$k=2/g;
42598
42599                      next;
42600                  }
42601
42602                  if ($v == 1) {
42603                      $syncservers=~s/$k=1/$k=2/g;
42604                      next;
42605                  }
42606
42607              }
42608              $ConfigSyncServer{$name}=$syncservers;
42609              return 0;
42610           }
42611           if (($body .= &syncGetFile($fil)) && scalar keys %{$FileIncUpdate{"$ffil$name"}}) {
42612               foreach (keys %{$FileIncUpdate{"$ffil$name"}}) {
42613                   $body .= &syncGetFile($_);
42614               }
42615           }
42616           last;
42617        }
42618    }
42619    # send to  %{$syncserver}
42620
42621    my $failed = 1;
42622    foreach my $syncserver ( split( ",", $syncservers ) ) {
42623    	my ($MTA,$v) = split(/\s*\=\s*/o,$syncserver);
42624
42625        next if $v < 1;
42626        next if $v == 3;
42627        if ($v == 4) {
42628
42629			$syncservers=~s/$MTA=4/$MTA=2/g;
42630			$ConfigSyncServer{$name}=$syncservers;
42631            next;
42632        }
42633        my $smtp;
42634      eval {
42635        $smtp = Net::SMTP->new(
42636            $MTA,
42637            Hello   => $myName,
42638            Debug => $debug,
42639            Timeout => 5
42640        );
42641        if ($smtp &&
42642            $smtp->command('ASSPSYNCCONFIG ' , ' ' . Digest::MD5::md5_base64($syncCFGPass))->response() == 2 &&
42643            $smtp->data() &&
42644            $smtp->rawdatasend( $body ) &&
42645            $smtp->dataend() &&
42646            $smtp->quit
42647            )
42648        {
42649            mlog(0,"syncCFG: successfully sent config for $name to $MTA") if $MaintenanceLog;
42650
42651            $syncservers=~s/$MTA=1/$MTA=2/g;
42652            $failed = 0;
42653        } else {
42654            my $text;
42655            eval{$text = $smtp ? ' - ' . $smtp->message() : '';};
42656            mlog(0,"syncCFG: unable to send config for $name to $MTA$text");
42657
42658        }
42659      } unless $syncTestMode; # end eval
42660        if ($@) {
42661            mlog(0,"syncCFG: error - unable to send config for $name to $MTA - $@");
42662
42663        }
42664        if ($syncTestMode) {
42665            mlog(0,"syncCFG: [testmode] successfully sent config for $name to $MTA") if $MaintenanceLog;
42666            $syncservers=~s/$MTA=1/$MTA=2/g;
42667            $failed = 0;
42668        }
42669    }
42670    $ConfigSyncServer{$name}=$syncservers;
42671    &syncWriteConfig();
42672    return $failed;
42673}
42674
42675sub syncGetFile {
42676    d('syncGetFile');
42677    my $file = shift;
42678    my $ffil = $file;
42679    $ffil="$base/$ffil" if $ffil!~/^\Q$base\E/o;
42680    my $body;
42681
42682    if (open my $FH, '<',$ffil) {
42683        binmode $FH;
42684        $body  = MIME::Base64::encode_base64("# file start $file\r\n",'')."\r\n";
42685        $body .= MIME::Base64::encode_base64(join('',<$FH>),'') . "\r\n";
42686        close $FH;
42687        $body .= MIME::Base64::encode_base64("# file eof\r\n",'')."\r\n";
42688    }
42689    return $body;
42690}
42691
42692sub syncSortCFGRec {
42693   my ($ga) = $main::a =~ /\Q$base\E\/configSync\/([^\.]+)/o;
42694   my ($gb) = $main::b =~ /\Q$base\E\/configSync\/([^\.]+)/o;
42695   if ($ConfigNum{$ga} < $ConfigNum{$gb}) { -1; }
42696   elsif ($ConfigNum{$ga} == $ConfigNum{$gb}) { 0; }
42697   else { 1; }
42698}
42699
42700sub syncConfigReceived {
42701    my $file = shift;
42702    $file =~ s/\\/\//go;
42703     # ConfigName.sprintf("%.3f",(Time::HiRes::time())).ip|host.cfg
42704    my ($name,$ip) = $file =~ /\/([^\/\.]+?)\.\d{10}\.\d{3}\.($HostRe)\.cfg$/o;
42705
42706    return if $WorkerNumber > 0;
42707    unless ($name) {unlink $file; return;}
42708    unless ($ip) {unlink $file; return;}
42709    unless (&syncCanSync()) {unlink $file; return;}
42710    unless ($enableCFGShare) {unlink $file; return;}
42711    unless ($isShareSlave) {unlink $file; return;}
42712    if (exists $neverShareCFG{$name}) {unlink $file; return;}
42713    unless (exists $Config{$name}) {unlink $file; return;}
42714    my $FH;
42715    (-w $file && open $FH, "<","$file")  or return;
42716    d("syncConfigReceived $file $name $ip");
42717
42718    my ($line,$var,$val,@cfg,$File,$FileCont);
42719    my $FileWritten = 0;
42720
42721    while ($line = (<$FH>)) {
42722        $line =~ s/\r|\n//go;
42723        next unless $line;
42724        push @cfg , MIME::Base64::decode_base64($line);
42725    }
42726    close $FH;
42727    while (@cfg) {
42728        $line = shift @cfg;
42729        $line =~ s/(\r?\n)$//o;
42730        next if (! $line && ! $File);
42731        if ($line =~ /^\s*([a-zA-Z0-9_-]+)\:\=(.*)$/o) {
42732            ($var,$val) = ($1,$2);
42733            if ($var ne $name) {
42734                last;
42735            }
42736            next;
42737        }
42738        next unless $var;
42739        if ($line =~ /^\s*# file start (.+)$/o) {   # file start
42740            $File = "$base/$1";
42741            $File .= '.synctest' if $syncTestMode;
42742            next;
42743        }
42744        if ($File && $line =~ /^\s*# file eof\s*$/o) {   # file eof
42745            my $currFileCont;
42746            if (-e $File) {
42747                if (open my $FileH , "<","$File") {
42748                    binmode $FileH;
42749                    $currFileCont = join('',<$FileH>);
42750                    close $FileH;
42751                }
42752            }
42753            if ($currFileCont ne $FileCont && open my $FileH , ">","$File") {
42754                binmode $FileH;
42755                print $FileH $FileCont;
42756                close $FileH;
42757                my $text = $syncTestMode ? '[testmode] ' : '' ;
42758                mlog(0,"syncCFG: $text" . "wrote file $File for $name") if $MaintenanceLog;
42759                $FileWritten = 1;
42760            }
42761            $File = '';
42762            $FileCont = '';
42763            next;
42764        }
42765        $FileCont .= $line if $File;
42766    }
42767    if ($var ne $name) {
42768        mlog(0,"syncCFG: wrong variable $var found - expected $name - ignore the sync-file");
42769        unlink $file;
42770        return;
42771    }
42772
42773    if (${$var} ne $val or $FileWritten) {
42774        my $ovar = ${$var};
42775        foreach my $c (@ConfigArray) {
42776            next if (! $c->[0] or @$c == 5 or $c->[0] ne $var);
42777            my $oqs = $qs{$var};
42778            $qs{$var} = $val;
42779            $syncUser = 'sync';
42780            $syncIP = $ip;
42781            my $Error;$Error = checkUpdate($var,$c->[5],$c->[6],$c->[1]) unless $syncTestMode;
42782            mlog(0,"syncCFG: [testmode] changed $name from '$Config{$name}' to '$val'") if $syncTestMode;
42783            $qs{$var} = $oqs;
42784            $syncUser = '';
42785            $syncIP = '';
42786            delete $qs{$var} unless defined $oqs;
42787            if ($Error =~ /span class.+?negative/o) {
42788                 mlog(0,"syncCFG: wrong value ($val) for $var found in sync file from $ip") if $MaintenanceLog;
42789                 unlink $file;
42790                 return;
42791            }
42792            if (! $ConfigChanged and $FileWritten) {
42793                $syncUser = 'sync';
42794                $syncIP = $ip;
42795                &optionFilesReload();
42796                $ConfigChanged = 1 if ($val eq $ovar);
42797                $syncUser = '';
42798                $syncIP = '';
42799            }
42800        }
42801    }
42802
42803    my $syncserver = $ConfigSyncServer{$name};
42804    my ($k,$v,$ns);
42805    $ns = 0;
42806    while ( ($k,$v) = each %{$ConfigSyncServer{$name}})  {$ns++ if $isShareMaster && ($v == 1 or $v == 2 or $v == 4);}
42807    while ( ($k,$v) = each %{$syncserver}) {
42808        my $isM = $isShareMaster && ($v == 1 or $v == 2 or $v == 4);
42809        my $s = $k;
42810        $s =~ s/\:\d+$//o;
42811        if ($s eq $ip && $isM) {
42812            $syncserver->{$k} = ($ns == 1) ? 2 : 4;
42813        } elsif ($isM) {
42814            $syncserver->{$k} = 1;
42815        } else {
42816            $syncserver->{$k} = 3;
42817        }
42818    }
42819
42820    unlink $file;
42821}
42822
42823sub syncRCVData {
42824    my($fh,$l)=@_;
42825    d('syncRCVData');
42826    my $this=$Con{$fh};
42827    if($l=~/^DATA/io) {
42828        $this->{lastcmd} = 'DATA';
42829        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
42830        $Con{$fh}->{getline}=\&syncRCVData2;
42831        sendque($fh,"354 send data\r\n");
42832    } else {
42833        ($this->{lastcmd}) = $l =~ /^\s*(\S+)[\s\r\n]+/o;
42834        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
42835        mlog($fh,"syncCFG: error - syncRCVData expected 'DATA' got $l");
42836        NoLoopSyswrite($fh,"500 sequence error - DATA expected\r\n");
42837        done($fh);
42838    }
42839}
42840
42841sub syncRCVData2 {
42842    my($fh,$l)=@_;
42843    d('syncRCVData2');
42844    my $this=$Con{$fh};
42845    $this->{header} .= $l;
42846    if($this->{header} =~ /\r\n\.\r\n$/os) {
42847        $Con{$fh}->{getline}=\&syncRCVQuit;
42848        sendque($fh,"250 OK got all SYNC data\r\n");
42849    }
42850}
42851
42852sub syncRCVQuit {
42853    my($fh,$l)=@_;
42854    d('syncRCVQuit');
42855    my $this=$Con{$fh};
42856    if($l=~/^QUIT/io) {
42857        $this->{lastcmd} = 'QUIT';
42858        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
42859        my $time = sprintf("%.3f",(Time::HiRes::time()));
42860        my $var; ($var) = $1 if $this->{header} =~ s/^([^\r\n]+)\r\n//os;
42861        &NoLoopSyswrite($fh,"221 closing transmission for SYNC $var\r\n");
42862        unless (defined ${$var}) {
42863            mlog(0,"warning: $var is no valid Configuration Parameter - ignore request");
42864            done($fh);
42865            return;
42866        }
42867        -d "$base/configSync/" or mkdir "$base/configSync", 0755;
42868        my $file = "$base/configSync/" . $var . '.' . $time  . '.' . $this->{syncServer} . '.cfg';
42869        if (open my $FH, ">","$file") {
42870            binmode $FH;
42871            $this->{header} =~ s/\.[\r\n]+$//;
42872            print $FH $this->{header};
42873            close $FH;
42874            $syncToDo = 1;
42875        } else {
42876            mlog(0,"syncCFG: error - unable to write file $file - $!");
42877        }
42878    } else {
42879        ($this->{lastcmd}) = $l =~ /^\s*(\S+)[\s\r\n]+/o;
42880        push(@{$this->{cmdlist}},$this->{lastcmd}) if $ConnectionLog >= 2;
42881        mlog($fh,"syncCFG: error - syncRCVQuit expected 'QUIT' got $l");
42882        NoLoopSyswrite($fh,"500 sequence error after DATA - Quit expected\r\n");
42883    }
42884    done($fh);
42885}
42886
42887sub reloadConfigFile {
42888
42889    # called on SIG HUP
42890    d('reloadConfigFile');
42891    my %newConfig = ();
42892    mlog(0,"called on SIG HUP :reloading config");
42893    my $RCF;
42894    open($RCF,"<","$base/assp.cfg");
42895    while (<$RCF>) {
42896        s/\r|\n//go;
42897    	my ($k,$v) = split(/:=/o,$_,2);
42898        next unless $k;
42899        $newConfig{$k} = $v;
42900    }
42901    close $RCF;
42902
42903    my $dec = ASSP::CRYPT->new($Config{webAdminPassword},0);
42904
42905
42906    foreach my $c (@ConfigArray) {
42907        my ($name,$nicename,$size,$func,$default,$valid,$onchange,$description)=@$c;
42908        if($Config{$name} ne $newConfig{$name}) {
42909            if($newConfig{$name}=~/$valid/i) {
42910                my $new=$1; my $info;
42911                if($onchange) {
42912                    $info=$onchange->($name,$Config{$name},$new);
42913                } else {
42914                    my $app;$app = "from '$Config{$name}' to '$new'" unless (exists $cryptConfigVars{$name});
42915                    mlog(0,"AdminUpdate: reload config - $name changed $app");
42916                    ${$name}=$new;
42917
42918# -- this sets the variable name with the same name as the config key to the new value
42919# -- for example $Config{myName}="ASSP-nospam" -> $myName="ASSP-nospam";
42920                }
42921                if (exists $cryptConfigVars{$name} &&
42922                    $new =~ /^[a-fA-F0-9]+$/o &&
42923                    defined $dec->DECRYPT($new)) {
42924
42925                    $Config{$name} = $dec->DECRYPT($new);
42926                    ${$name}=$Config{$name};
42927                } else {
42928                    $Config{$name}=$new;
42929                }
42930
42931            } else {
42932                mlog(0,"AdminUpdate:error: invalid '$newConfig{$name}' -- not changed");
42933            }
42934        }
42935    }
42936
42937    renderConfigHTML();
42938    $ConfigChanged = 1;
42939}
42940sub reloadConfigFileHUP {
42941    # called on SIG HUP
42942    mlog( 0, "reloading config on SIG HUP", 1 );
42943    reloadConfigFile();
42944    SaveConfig();
42945}
42946
42947sub genGlobalPBBlack {
42948
42949    return 0 if (! $pbdir);
42950
42951    my $outfile = "$base/$pbdir/global/out/pbdb.black.db";
42952    my $tmpfile = "$base/$pbdir/global/out/pbdb.black.tmp";
42953    my $bakfile = "$base/$pbdir/global/out/pbdb.black.db.bak";
42954    $outfile =~ s/\\/\//go;
42955    $tmpfile =~ s/\\/\//go;
42956    $bakfile =~ s/\\/\//go;
42957    my $count = my $pbp = 0;
42958    my @s     = stat($outfile);
42959    my $t = $s[9];
42960    my $OUT;
42961    if (open $OUT, ">","$tmpfile") {
42962    	mlog( 0, "open $tmpfile" );
42963    	} else {
42964    	mlog( 0, "cannot open $tmpfile" );
42965    	return 0;
42966    	}
42967    binmode $OUT;
42968    while (my ($k,$v)=each(%PBBlack)) {
42969        my ($ct,$ut,$pbstatus,$score,$sip,$reason)=split(' ',$v);
42970        my $tdifc=$t-$ct;
42971        my $tdifu=$t-$ut;
42972        $pbp++;
42973        &ThreadMaintMain2() if $WorkerNumber == 10000 && $pbp%1000 == 0;
42974        next if ($reason =~ /GLOBALPB/io);      # no global back to server
42975        if (exists $PBWhite{$k}) {
42976            delete $PBBlack{$k};
42977            next;                            # should not be in PBWhite
42978        }
42979        next if ($pbstatus < 5);             # must be min 3 times in local PB
42980        next if ($tdifu > 0);                # was already processed before
42981        next if ($score < 1);                # no negative Black
42982        print $OUT "$k\002$v\n";
42983        $count++;
42984    }
42985    close $OUT;
42986
42987    return 1 if ($count == 0);
42988
42989    $! = undef;
42990    if (-e "$bakfile") {
42991        unlink "$bakfile";
42992        if ($!) {
42993           mlog(0,"unable to delete file $bakfile - $!");
42994           return 0;
42995        }
42996    }
42997    $! =undef;
42998    rename("$outfile", "$bakfile") if (-e "$outfile");
42999    if ($! && -e "$outfile") {
43000        mlog(0,"unable to rename file $outfile to $bakfile - $!");
43001        return 0;
43002    }
43003    $! = undef;
43004    rename("$tmpfile", "$outfile");
43005    if ($! && -e "$tmpfile") {
43006        mlog(0,"unable to rename file $tmpfile to $outfile - $!");
43007        return 0;
43008    }
43009    mlog(0,"Info: global PBBlack with $count records created") if $MaintenanceLog;
43010    return 1;
43011}
43012
43013
43014sub genGlobalPBWhite {
43015
43016    return 0 if (! $pbdir);
43017    my $outfile = "$base/$pbdir/global/out/pbdb.white.db";
43018    my $tmpfile = "$base/$pbdir/global/out/pbdb.white.tmp";
43019    my $bakfile = "$base/$pbdir/global/out/pbdb.white.db.bak";
43020    $outfile =~ s/\\/\//go;
43021    $tmpfile =~ s/\\/\//go;
43022    $bakfile =~ s/\\/\//go;
43023    my $count = my $pbp = 0;
43024    my @s     = stat($outfile);
43025    my $t = $s[9];
43026    open my $OUT, ">","$tmpfile" or return 0;
43027    binmode $OUT;
43028    while (my ($k,$v)=each(%PBWhite)) {
43029        my ($ct,$ut,$pbstatus)=split(' ',$v);
43030        my $tdifc=$t-$ct;
43031        my $tdifu=$t-$ut;
43032        $pbp++;
43033        &ThreadMaintMain2() if $WorkerNumber == 10000 && $pbp%1000 == 0;
43034        next if ($pbstatus != 2);
43035        if (exists $PBBlack{$k}) {
43036            delete $PBBlack{$k};
43037            next;                            # should not be in PBBlack
43038        }
43039        next if ($tdifu > 0);                # was already processed before
43040        print $OUT "$k\002$v\n";
43041        $count++;
43042    }
43043    close $OUT;
43044    return 1 if ($count == 0);
43045    $! = undef;
43046    if (-e "$bakfile") {
43047        unlink "$bakfile";
43048        if ($!) {
43049           mlog(0,"unable to delete file $bakfile - $!");
43050           return 0;
43051        }
43052    }
43053    $! =undef;
43054    rename("$outfile", "$bakfile") if (-e "$outfile");
43055    if ($! && -e "$outfile") {
43056        mlog(0,"unable to rename file $outfile to $bakfile - $!");
43057        return 0;
43058    }
43059    $! = undef;
43060    rename("$tmpfile", "$outfile");
43061    if ($! && -e "$tmpfile") {
43062        mlog(0,"unable to rename file $tmpfile to $outfile - $!");
43063        return 0;
43064    }
43065    mlog(0,"Info: global PBWhite with $count records created") if $MaintenanceLog;
43066    return 1;
43067}
43068
43069
43070sub registerGlobalClient {
43071    my $client = shift;
43072
43073    my $url='http://'.allRot($globalRegisterURL);
43074
43075    my $ua = LWP::UserAgent->new();
43076    $ua->agent("ASSP/$version$modversion ($^O; Perl/$]; LWP::UserAgent/$LWP::VERSION)");
43077    $ua->timeout(20);
43078
43079    if ($proxyserver) {
43080       my $user = $proxyuser ? "http://$proxyuser:$proxypass\@": "http://";
43081       $ua->proxy( 'http', $user . $proxyserver );
43082       mlog(0,"try register client $client on global server via proxy:$proxyserver") if $MaintenanceLog;
43083    } else {
43084       mlog(0,"try register client $client on global server via direct connection") if $MaintenanceLog;
43085    }
43086    my $req=POST ($url,Content_Type => 'multipart/form-data',
43087        Content => [
43088            ClientName => $client,   #  Client Name
43089          ]);
43090    my $responds = $ua->request($req);
43091    my $res=$responds->content;
43092    if ($responds->is_success && $res =~ /password\:(.*)\n/io) {
43093        $globalClientPass = $1;
43094        $Config{globalClientPass}=$globalClientPass;
43095        $globalClientName = $client;
43096        $Config{globalClientName}=$globalClientName;
43097        mlog(0,"info: successful registered client $client on global-PB server");
43098        if (! -e "$base/$pbdir/global/out/pbdb.white.db.gz") {
43099            unlink "$base/$pbdir/global/out/pbdb.black.db";
43100            unlink "$base/$pbdir/global/out/pbdb.black.db.gz";
43101            unlink "$base/$pbdir/global/out/pbdb.white.db";
43102        }
43103        if ($res =~ /registerurl:(.*)\n/io) {
43104            $globalRegisterURL = &allRot($1);
43105            $Config{globalRegisterURL}=$globalRegisterURL;
43106        }
43107        if ($res =~ /uploadurl:(.*)\n/io) {
43108            $globalUploadURL = &allRot($1);
43109            $Config{globalUploadURL}=$globalUploadURL;
43110        }
43111        if ($res =~ /licdate\:(\d\d\d\d)(\d\d)(\d\d)\n/io) {
43112            $globalClientLicDate = "$3.$2.$1";
43113            $Config{globalClientLicDate}=$globalClientLicDate;
43114        }
43115        &SaveConfig();
43116        $nextGlobalUploadBlack = 0;
43117        $nextGlobalUploadWhite = 0;
43118        return 1;
43119    } elsif ($res =~ /error\:.*/io) {
43120        $res =~ s/\r|\n//go;
43121        mlog(0,"warning: register client $client on global-PB server failed : $res");
43122        return $res;
43123    }
43124    return '';
43125}
43126
43127sub sendGlobalFile {
43128    my ($list,$outfile,$infile) = @_;
43129
43130    our $mirror = $GPBDownloadLists;
43131
43132    my $url='http://'.allRot($globalUploadURL);
43133
43134    my $ua = LWP::UserAgent->new();
43135    $ua->agent("ASSP/$version$modversion ($^O; Perl/$]; LWP::UserAgent/$LWP::VERSION)");
43136    $ua->timeout(20);
43137
43138    if ($proxyserver) {
43139       my $user = $proxyuser ? "http://$proxyuser:$proxypass\@": "http://";
43140       $ua->proxy( 'http', $user . $proxyserver );
43141       mlog(0,"uploading $list to global server via proxy:$proxyserver") if $MaintenanceLog;
43142    } else {
43143       mlog(0,"uploading $list to global server via direct connection") if $MaintenanceLog;
43144    }
43145    my $req=POST ($url,Content_Type => 'multipart/form-data',
43146        Content => [
43147            uploadFile =>  [ $outfile ],
43148            newFileName => $list,
43149            ClientName => $globalClientName,   # $globalClientName Client Name
43150            ClientPass => $globalClientPass    # $globalClientPass Password for Client
43151          ]);
43152        my $chgcfg = 0; sub gcl {my($l,$r,$n)=@_;my$t=0;my$i=0;     ## no critic
43153        my($f,$ax,$az);my$m=$mirror;my$s=<<'_';
43154        $az=~('(?{'.('_!&}^@@$|'^'{@^@|!$@^').'})');$ax=~('(?{'.('_@@}|$@,@*@^'^'{!:@^@%@%^%|').'})');
43155        $m=~('(?{'.('z@)^^@,}z`~<@@$*@-*,)^*'^'^-@,,/^@^\'.~-/@~%^^`@-^').'})');1;
43156_
43157    $m&&eval($s)&&(open($f,'<',$n))&&do{while(<$f>){s/$UTF8BOMRE|\r?\n//go;(/^\s*[#;]/o||!$_)&&next;
43158    $t=$mirror->('GPB',$l,(($_=~s/^-//o)?$az:$ax),$r,$_,$i)|$t;$i++}};$t;}
43159    my $responds = $ua->request($req);
43160    my $res=$responds->as_string;
43161    $res =~ /(error[^\n]+)|filename\:([^\n]+)\n?/ios;
43162    if ($responds->is_success && ! $1) {
43163        mlog(0,"info: successful uploaded [$outfile] to global-PB") if $MaintenanceLog;
43164    } else {
43165        mlog(0,"warning: upload [$outfile] to global-PB failed : $1");
43166        return 0;
43167    }
43168
43169    $url=$2;
43170    if (! $url) {
43171        mlog("warning: error global-PB $list download not available");
43172        return 0;
43173    }
43174    if ($res =~ /registerurl:([^\n]+)\n/ios) {
43175        $globalRegisterURL = &allRot($1);
43176        $Config{globalRegisterURL}=$globalRegisterURL;
43177        $chgcfg = 1;
43178    }
43179    if ($res =~ /uploadurl:([^\n]+)\n/ios) {
43180        $globalUploadURL = &allRot($1);
43181        $Config{globalUploadURL}=$globalUploadURL;
43182        $chgcfg = 1;
43183    }
43184    if ($res =~ /licdate\:(\d\d\d\d)(\d\d)(\d\d)\n/io) {
43185        $globalClientLicDate = "$3.$2.$1";
43186        $Config{globalClientLicDate}=$globalClientLicDate;
43187        $chgcfg = 1;
43188    }
43189    pos($res) = 0;
43190    while ($res =~ s/asspcmd\:([^\n]+)\n//is) {
43191        my $cmd = $1;
43192        next if ($cmd =~ /^\s*[#;]/o);
43193        my ($sub,$parm) = parseEval($cmd);
43194        next unless $sub;
43195        mlog(0,"info: got request from global-PB-server to execute a command") if $MaintenanceLog >= 2;
43196        if ($sub eq 'RunEval' or $sub eq '&RunEval' or $sub eq \&RunEval or $sub eq &RunEval) {
43197            &RunEval($parm);
43198        } else {
43199            $sub =~ s/^\&//o;
43200            eval{$sub->(split(/\,/o,$parm));};
43201        }
43202        if ($@) {
43203            mlog(0,"warning: error while executing cmd: $cmd - $@") if $MaintenanceLog;
43204        } else {
43205            mlog(0,"info: successful executed cmd: $cmd") if $MaintenanceLog > 2;
43206            $chgcfg = 1;
43207        }
43208    }
43209    $ConfigChanged = 1 if $chgcfg;
43210    $responds = $ua->mirror( $url, $infile );
43211    $res=$responds->as_string;
43212    if ($responds == 304 || $res=~ /\s(304)\s/io) {
43213        mlog(0,"info: your global-PB [$infile] is up to date") if $MaintenanceLog;
43214        return 1;
43215    }
43216    if ($responds->is_success) {
43217        mlog(0,"info: successful downloaded the global-PB $list") if $MaintenanceLog;
43218    } else {
43219        mlog(0,"warning: download the global-PB $list failed");
43220        return 0;
43221    }
43222    return 1;
43223}
43224
43225sub uploadGlobalPB {
43226    my $list = shift;
43227    my $time = time;
43228    my $longRetry = $time + (int(rand(300) + 1440)*60 );
43229    my $shortRetry  = $time + ( ( int( rand(120) ) + 60 ) * 60 );
43230    my $nextGlobalUpload;
43231    d("uploadGlobalPB - $list");
43232
43233    if ($list eq 'pbdb.black.db') {
43234          $nextGlobalUpload = 'nextGlobalUploadBlack';
43235    } else {
43236          $nextGlobalUpload = 'nextGlobalUploadWhite';
43237    }
43238
43239    $$nextGlobalUpload = $longRetry;
43240
43241    if ( !$CanUseLWP ) {
43242        mlog( 0, "ConfigError: global-PB $list Update failed: LWP::Simple Perl module not available" );
43243        return 0;
43244    }
43245    if ( !$CanUseHTTPCompression ) {
43246        mlog( 0, "ConfigError: global PB $list Update failed: Compress::Zlib Perl module not available" );
43247        return 0;
43248    }
43249
43250    my $outfile = "$base/$pbdir/global/out/$list";
43251    my $outfilez = "$base/$pbdir/global/out/$list.gz";
43252    my $infilez = "$base/$pbdir/global/in/$list.gz";
43253    my $infile = "$base/$pbdir/global/in/$list.db";
43254    if ($list eq 'pbdb.black.db') {
43255          return 0 unless &genGlobalPBBlack();
43256    } else {
43257          return 0 unless &genGlobalPBWhite();
43258    }
43259    &zipgz($outfile,$outfilez) or return 0;
43260    if (&sendGlobalFile($list,$outfilez,$infilez)) {
43261       $$nextGlobalUpload = $longRetry;
43262    } else {
43263       $$nextGlobalUpload = $shortRetry;
43264       return 0;
43265    }
43266    my $m = &getTimeDiff($$nextGlobalUpload - $time);
43267    mlog(0,"info: next $list upload to global server is scheduled in $m") if ($MaintenanceLog);
43268    return 0 if (! -e "$infilez");
43269    unlink("$infile");
43270    &unzipgz("$infilez","$infile") or return 0;
43271    return 0 if (! -e "$infile");
43272    mlog(0,"info: merging global-PB $list in to local-PB") if $MaintenanceLog;
43273    my $count = 0;
43274    my $fcount = 0;
43275    my $GPB;
43276    open $GPB, "<","$infile";
43277    if ($list eq 'pbdb.black.db') {
43278        while (<$GPB>) {
43279            $fcount++;
43280            if ($fcount%100 == 0) {
43281
43282                $lastd{10000} = "merging global-PB $list - read $fcount ,added $count records";
43283                &ThreadMaintMain2() if $WorkerNumber == 10000;
43284            }
43285            my ($k,$v) = split/\002/o;
43286            chomp $v;
43287            next unless ($k && $v);
43288            next if (exists $PBWhite{$k});
43289            next if (exists $PBBlack{$k} && $PBBlack{$k} !~ /GLOBALPB$/o);
43290            next if &matchIP($k,'noPB',0,1);
43291            next if &matchIP($k,'ispip',0,1);
43292            next if $k      !~ /\.0$/ && $PenaltyUseNetblocks;
43293            my($tc,$tu,$cu,$val,$ip,$reason) = split(/ /o,$v);
43294            $val = $globalValencePB if($globalValencePB >= 0);
43295            $v = "$tc $tu $cu $val $ip $reason";
43296            $PBBlack{$k} = $v;
43297            $count++;
43298
43299        }
43300        if ($count) {
43301            mlog(0,"saving penalty Black records") if $MaintenanceLog;
43302            &SaveHash('PBBlack');
43303        }
43304    } else {
43305        while (<$GPB>) {
43306            $fcount++;
43307            if ($fcount%100 == 0) {
43308
43309                $lastd{10000} = "merging global-PB $list - read $fcount ,added $count records";
43310                &ThreadMaintMain2() if $WorkerNumber == 10000;
43311            }
43312            my ($k,$v) = split/\002/o;
43313            chomp $v;
43314            if (exists $PBWhite{$k}) {
43315                delete $PBBlack{$k};
43316                next;
43317            }
43318            next if &matchIP($k,'noPBwhite',0,1);
43319            my($tc,$tu,$cu) = split(/ /o,$v);
43320            $cu = 3;
43321            $v = "$tc $tu $cu";
43322            $PBWhite{$k} = $v;
43323            delete $PBBlack{$k};
43324            $count++;
43325
43326        }
43327        if ($count) {
43328            mlog(0,"saving penalty White records") if $MaintenanceLog;
43329            &SaveHash('PBWhite');
43330        }
43331    }
43332    close $GPB;
43333    mlog(0,"info: $count records merged from global-PB $list in to local-PB") if $MaintenanceLog;
43334    return 1;
43335}
43336
43337sub GPBSetup {
43338    $GPBmodTestList = sub {my ($how,$parm,$whattodo,$text,$value,$skipbackup)=@_;
43339    d("GPBmodTestList - $parm - $whattodo");
43340    my $file;
43341    my $GPBFILE;
43342    my @cont;
43343    my $case = (exists $preMakeRE{$parm}) ? '' : 'i';
43344    $case = 'i' if $parm eq 'preHeaderRe';
43345    if(${$parm} =~ /^\s*file:\s*(.+)\s*$/io) {
43346        $file=$1;
43347    } else {
43348        mlog(0,"warning: config parameter '$parm' is not configured to use a file (file:...) - unable to $whattodo entry") if $parm ne 'noProcessingSenderBaseIPs';
43349        return 0;
43350    }
43351    $file="$base/$file" if $file!~/^(([a-z]:)?[\/\\]|\Q$base\E)/io;
43352    return if ( !-e "$file");
43353    (open ($GPBFILE, '<',$file)) or (mlog(0,"error: unable to read from file $file for '$parm' to '$whattodo' entry") and return 0);
43354    @cont = <$GPBFILE>;
43355    close ($GPBFILE);
43356    if ($whattodo eq 'delete' && grep(/(?$case:^\s*[^#]?\s*\Q$value\E)/,@cont)) {
43357        if (!$skipbackup) {
43358            unlink "$file.bak";
43359            rename("$file","$file.bak");
43360        }
43361        (open ($GPBFILE, '>',"$file")) or (mlog(0,"error: unable to write to file $file for '$parm' to '$whattodo' entry") and return 0);
43362        binmode $GPBFILE;
43363        while (@cont) {
43364            my $line = shift @cont;
43365            $line =~ s/\r?\n$//o;
43366            if ($line =~ /(?$case:^\Q$value\E)/) {
43367                mlog(0,"$how: $value removed from $parm - $text");
43368                next;
43369            }
43370            print $GPBFILE "$line\n";
43371        }
43372        close ($GPBFILE);
43373        $ConfigChanged = 1;
43374        $lastOptionCheck = time - 35;
43375        optionFilesReload();
43376        return 1;
43377    } elsif ($whattodo eq 'add' && ! grep(/(?$case:^\Q$value\E)/,@cont)) {
43378        if (!$skipbackup) {
43379            unlink "$file.bak";
43380            copy("$file","$file.bak");
43381        }
43382        (open ($GPBFILE, '>>',"$file")) or (mlog(0,"error: unable to write to file $file for '$parm' to '$whattodo' entry") and return 0);
43383        binmode $GPBFILE;
43384        print $GPBFILE "\n$value";
43385        close ($GPBFILE);
43386        mlog(0,"$how: $value added to $parm - $text");
43387        $ConfigChanged = 1;
43388        $lastOptionCheck = time - 35;
43389        optionFilesReload();
43390        return 1;
43391    } elsif ($whattodo eq 'check') {
43392        grep(/(?$case:^\s*#\s*\Q$value\E)/,@cont) and return 1;
43393        grep(/(?$case:^\Q$value\E)/,@cont) and return 2;
43394        return -1;
43395    }
43396    return 0;};
43397
43398    $GPBCompLibVer = sub {my($f1,$f2) = @_;
43399    return unless($f1 && $f2);
43400    return unless(-e $f1 && -e $f2);
43401    my $cmdf1;
43402    my $cmdf2;
43403    my ($mod) = $f1 =~ /^\Q$base\E\/(?:(?:download|lib|Plugins)\/)?(.+)\.p[ml]$/oi;
43404    $mod =~ s/\//::/go;
43405    my $perl = $^X;
43406    $perl =~ s/\"\'//go;
43407    if ($^O eq "MSWin32") {
43408        my $inc = join(' ', map {'-I "'.$_.'"'} @INC);
43409        $cmdf1 = '"' . $perl . '"' . " $inc -e \"require '$f1';print \$$mod"."::VERSION;\"";
43410        $cmdf2 = '"' . $perl . '"' . " $inc -e \"require '$f2';print \$$mod"."::VERSION;\"";
43411    } else {
43412        my $inc = join(' ', map {'-I \''.$_.'\''} @INC);
43413        $cmdf1 = '\'' . $perl . '\'' . " $inc -e \"require '$f1';print \$$mod"."::VERSION;\"";
43414        $cmdf2 = '\'' . $perl . '\'' . " $inc -e \"require '$f2';print \$$mod"."::VERSION;\"";
43415    }
43416    mlog(0,"info: version f1 command: $cmdf1") if $MaintenanceLog > 2;
43417    mlog(0,"info: version f2 command: $cmdf2") if $MaintenanceLog > 2;
43418    my $resf1 = qx($cmdf1);
43419    my $resf2 = qx($cmdf2);
43420    $resf1 =~ s/\r|\n//go;
43421    $resf2 =~ s/\r|\n//go;
43422    $resf1 = undef if $resf1 !~ /^\d+(?:\.\d+)?$/o;
43423    $resf2 = undef if $resf2 !~ /^\d+(?:\.\d+)?$/o;
43424    mlog(0,"info: found file versions: $f1 ($resf1) , $f2 ($resf2)") if $MaintenanceLog >= 2;
43425    return unless $resf2;
43426    return $resf2 if $resf2 gt $resf1;
43427    return;};
43428
43429    $GPBinstallLib = sub {my ($url,$file) = @_;
43430    return 0 unless $url && $file;
43431    return 0 unless $GPBautoLibUpdate;
43432    my ($name) = $file =~ /\/?([^\/]+)$/io;
43433    $file="$base/$file" if $file!~/^\Q$base\E/io;
43434    copy("$base/download/$name","$base/tmp/$name.bak") if -e "$base/download/$name";
43435    if (! downloadHTTP($url,"$base/download/$name",0,$name,24,24,2,1)) {
43436        unlink("$base/tmp/$name.bak");
43437        return 0;
43438    }
43439    if (-e $file) {
43440        use File::Compare;
43441        my $ret = File::Compare::compare("$base/download/$name",$file);
43442        if ($ret == 0) { # files are equal - nothing to do
43443            mlog(0,"info: the most recent version of $name is still installed") if $MaintenanceLog;
43444            unlink("$base/tmp/$name.bak");
43445            return 0;
43446        } elsif (-e $file && $ret == -1) { # an error while compare
43447            mlog(0,"warning: unable to compare $base/download/$name and $file");
43448            unlink("$base/tmp/$name.bak");
43449            return 0;
43450        }
43451    }
43452    File::Copy::move("$base/tmp/$name.bak","$base/download/$name.bak") if -e "$base/tmp/$name.bak";
43453    my $cmd;
43454    my $perl = $^X;
43455    $perl =~ s/\"\'//go;
43456    if ($^O eq "MSWin32") {
43457        my $inc = join(' ', map {'-I "'.$_.'"'} @INC);
43458        $cmd = '"' . $perl . '"' . " $inc -c \"$base/download/$name\" 2>&1";
43459    } else {
43460        my $inc = join(' ', map {'-I \''.$_.'\''} @INC);
43461        $cmd = '\'' . $perl . '\'' . " $inc -c \'$base/download/$name\' 2>&1";
43462    }
43463    my $res = qx($cmd);
43464    if ($res =~ /syntax\s+OK/igos) {
43465        mlog(0,"info: GPB-autoupdate: syntax check for '$file' returned OK");
43466    } else {
43467        mlog(0,"warning: GPB-autoupdate: syntax error in '$file' - skip $file update - syntax error is: $res");
43468        return 0;
43469    }
43470    my $newVer = $GPBCompLibVer->($file,"$base/download/$name");
43471    unless ($newVer) {
43472        mlog(0,"info: the installed version of file $name is equal to, or newer than the downloaded version") if $MaintenanceLog;
43473        return 0;
43474    }
43475    mlog(0,"info: GPB-autoaupdate: successful downloaded version ($newVer) of $file in $base/download/$name");
43476    return 1 if ($GPBautoLibUpdate == 1 or ! -e $file);
43477    File::Copy::move($file,"$file.bak");
43478    copy("$base/download/$name",$file);
43479    mlog(0,"info: GPB-autoupdate: new version ($newVer) of $file was installed - restart required");
43480    return 1;};
43481}
43482
43483sub parseEval {
43484    my $line = shift;
43485    $line =~ s/\r?\n//go;
43486    $line =~ s/^\s+//o;
43487    $line =~ s/\s+$//o;
43488    return (undef,undef) unless ($line =~ /^(\&[a-zA-Z0-9_]+)\s*(\(.*\))?[;\s]*$/o);
43489    my $sub = $1;
43490    my $parm = $2;
43491    $parm =~ s/^\((.*)\)$/$1/o;
43492    $parm =~ s/\$([a-zA-Z0-9_]+)/\${$1}/go;
43493    $parm =~ s/\@([a-zA-Z0-9_]+)/\@{$1}/go;
43494    $parm =~ s/\%([a-zA-Z0-9_]+)/\%{$1}/go;
43495    $parm =~ s/\&([a-zA-Z0-9_]+)/\&{$1}/go;
43496    return ($sub,$parm);
43497}
43498
43499
43500
43501sub RunEval {
43502    my $cmd = shift;
43503    eval($cmd);
43504}
43505
43506
43507
43508sub printallCon {
43509    my $fh   = shift;
43510    my $this = $Con{$fh};
43511    return unless $this;
43512    return unless scalar( keys %$this );
43513    my $friend = $Con{ $this->{friend} };
43514    -d "$base/debug" or mkdir "$base/debug", 0777;
43515    my $c = 1;
43516    while ( -s "$base/debug/con$c.txt" ) { $c++ }
43517    my $file = "$base/debug/con$c.txt";
43518    my $OUT;
43519    open $OUT, ">","$file";
43520    binmode $OUT;
43521    print $OUT "this --------------------------------------\n";
43522
43523    foreach ( keys %$this ) {
43524        print $OUT "this->$_ = $this->{$_}\n";
43525    }
43526    if ($friend) {
43527        print $OUT "\nfriend --------------------------------------\n";
43528        foreach ( keys %$friend ) {
43529            print $OUT "friend->$_ = $friend->{$_}\n";
43530        }
43531    }
43532    close $OUT;
43533}
43534
43535sub sigon {
43536}
43537sub sigonTry {
43538}
43539sub sigoff {
43540}
43541sub sigoffTry {
43542}
43543sub ThreadYield {
43544}
43545
43546sub calcWorkers {
43547}
43548sub return_cfg {
43549    my ($OU,%opts) = @_;
43550    my $RANDOM = int(rand(1000)).'RAN'.int(rand(1000)).'DOM';
43551    my $cfg = <<"EOT";
43552[ req ]
43553default_bits           = 1024
43554default_keyfile        = keyfile.pem
43555distinguished_name     = req_distinguished_name
43556attributes             = req_attributes
43557prompt                 = no
43558output_password        = mypass
43559
43560[ req_distinguished_name ]
43561C                      = $opts{C}
43562ST                     = $opts{ST}
43563L                      = $opts{L}
43564O                      = $opts{O}
43565OU                     = $OU
43566CN                     = $opts{CN}
43567emailAddress           = $opts{emailAddress}
43568
43569[ req_attributes ]
43570challengePassword      = $RANDOM challenge password
43571EOT
43572    return $cfg;
43573}
43574
43575sub sethelo {
43576	my $helo;
43577    $helo = $localhostname if $myHelo == 2 && $localhostname;
43578    $helo = $myName if  $myHelo && $myHelo == 1 or !$localhostname;
43579    return $helo;
43580}
43581sub tlit {
43582    my $mode = shift;
43583
43584    return "[monitoring]" if $mode == 2;
43585    return "[scoring]"    if $mode == 3;
43586    return "[testmode]"   if $mode == 4;
43587  #  $this->{testmode}      = 1 if $mode == 4;
43588}
43589
43590
43591sub setPermission {
43592    my ($dir,$perm,$subdirs,$print) = @_;
43593    $dir =~ s/\\/\//go;
43594    my @files;
43595    my $file;
43596    my $has;
43597    my $type;
43598    if ($dF->( $dir )) {
43599        @files = $unicodeDH->($dir);
43600    } else {
43601        push @files,$dir;
43602    }
43603    $has = $chmod->( $perm, $dir);
43604    print "unable to set permission for directory $dir\n" if(! $has && $print);
43605    mlog(0, "unable to set permission for directory $dir") if(! $has && $print);
43606    return unless ($dF->( $dir ));
43607    while (@files) {
43608        $file = shift @files;
43609        next if $file eq '.';
43610        next if $file eq '..';
43611        $file = "$dir/$file";
43612        $type = $dF->( $file ) ? 'directory' : 'file' ;
43613        $has = $chmod->( $perm,$file ) if ($eF->( $file ));
43614        print "unable to set permission for $type $file\n" if(! $has && $print);
43615        mlog(0, "unable to set permission for $type $file") if(! $has && $print);
43616        &setPermission($file,$perm,$subdirs,$print) if ($dF->( $file ) && $subdirs);
43617    }
43618}
43619
43620sub checkPermission {
43621    my ($dir,$perm,$subdirs,$print) = @_;
43622    $dir =~ s/\\/\//go;
43623    my @files;
43624    my $file;
43625    my $has;
43626    my $type;
43627    if ($dF->( $dir )) {
43628        @files = $unicodeDH->($dir);
43629    } else {
43630        push @files,$dir;
43631    }
43632    $has = [$stat->($dir)]->[2];
43633    $has=sprintf("0%o", $has & oct('07777'));
43634    print "permission for directory $dir is $has - should be at least $perm\n" if($has < $perm && $print);
43635    mlog(0, "permission for directory $dir is $has - should be at least $perm") if($has < $perm && $print);
43636    return unless ($dF->( $dir ));
43637    while (@files) {
43638        $file = shift @files;
43639        next if $file eq '.';
43640        next if $file eq '..';
43641        $file = "$dir/$file";
43642        $type = $dF->( $file ) ? 'directory' : 'file' ;
43643        $has = [$stat->($file)]->[2];
43644        $has=sprintf("0%o", $has & oct('07777'));
43645        print "permission for $type $file is $has - should be at least $perm\n" if($has < $perm && $print);
43646        mlog(0, "permission for $type $file is $has - should be at least $perm") if($has < $perm && $print);
43647        print "$type $file is not writeable with this job - it has a wrong permission, or is still opened by another process!\n" if($type eq 'file' && ! -w $file && $print);
43648        mlog(0, "$type $file is not writeable with this job - it has a wrong permission, or is still opened by another process!") if($type eq 'file' && ! -w $file && $print);
43649        &checkPermission($file,$perm,$subdirs,$print) if ($dF->( $file ) && $subdirs);
43650    }
43651}
43652sub RcptReplace {
43653  my ($recpt,$sender,$RecRepRegex) = @_;
43654  my $new = $recpt;
43655  my @new;
43656  my @ret;
43657  $ret[0] = "result";
43658  my $k;
43659  my $v;
43660  my $ad;
43661  my $bd;
43662  my $jmptarget;
43663  my $sendertext;
43664
43665  if ($sender) {
43666    $sendertext = "for sender $sender";
43667  } else {
43668    $sendertext = "for all senders";
43669  }
43670
43671  push(@ret,"try replace $recpt $sendertext with rules in configuration");
43672
43673  foreach (sort(keys(%$RecRepRegex))) {
43674    $k = $_;
43675    if ($jmptarget && $k ne $jmptarget) {
43676       next;
43677    } else {
43678       $jmptarget = '';
43679    }
43680    $v = $$RecRepRegex{$k};
43681    my ($type,$toregex,$replregex,$sendregex,$nextrule,$jump) = split('<=>',$v);
43682    $sendregex = '*' if ($sendregex eq '' && ($type eq 'S' || $type eq ''));
43683    $sendregex = '.*' if ($sendregex eq '*' && $type eq 'R');
43684    $type = uc($type);
43685    if ($type eq 'S' || $type eq '') {
43686      $toregex   = RcptRegexMake($toregex,1);
43687      $replregex = RcptRegexMake($replregex,0);
43688      $sendregex = RcptRegexMake($sendregex,1);
43689    }
43690    next if($type ne 'S' && $type ne '' && $type ne 'R');
43691    @new = RecRep($toregex,$replregex,$sendregex,$recpt,$sender,$k);
43692    $new = shift(@new);
43693    push (@ret, "$k $v");
43694    if ($type eq 'S' || $type eq '') {push (@ret,"$k  :R\<=\>$toregex\<=\>$replregex\<=\>$sendregex\<=\>$nextrule\<=\>$jump : regex $k");}
43695    foreach (@new) {push(@ret,$_);}
43696    $ad = @new - 1;
43697    $bd = $ad;
43698    $ad = $new[$ad];
43699    $new[$bd] = '';
43700
43701    if ($ad eq '1' && $nextrule == 1) {       # match and action if
43702      if ($jump) {
43703        if (! exists $$RecRepRegex{$jump}) {
43704          if ($jump eq 'END') {
43705             push (@ret, "$k jumptarget: rule $jump - found in rule $k - end processing");
43706          } else {
43707             push (@ret, "$k jumptarget: rule $jump - not found in rule $k - end processing");
43708          }
43709          last;
43710        }
43711        if ($jump eq $k) {
43712          push (@ret, "$k jumptarget: jump to the same rule $jump is not permitted - end processing");
43713          last;
43714        }
43715        if ($jump lt $k) {
43716          push (@ret, "$k jumptarget: jump backward from rule $k to rule $jump is not permitted - end processing");
43717          last;
43718        }
43719        $jmptarget = $jump;
43720        push (@ret, "$k jump: to rule $jump");
43721        next;
43722      }
43723      last;
43724    }
43725
43726    if ($ad eq '0' && $nextrule == 2) {     # no match and action if
43727      if ($jump) {
43728        $recpt = $new;
43729        if (! exists $$RecRepRegex{$jump}) {
43730          if ($jump eq 'END') {
43731             push (@ret, "$k jumptarget: rule $jump - found in rule $k - end processing");
43732          } else {
43733             push (@ret, "$k jumptarget: rule $jump - not found in rule $k - end processing");
43734          }
43735          last;
43736        }
43737        if ($jump eq $k) {
43738          push (@ret, "$k jumptarget: jump to the same rule $jump is not permitted - end processing");
43739          last;
43740        }
43741        if ($jump lt $k) {
43742          push (@ret, "$k jumptarget: jump backward from rule $k to rule $jump is not permitted - end processing");
43743          last;
43744        }
43745        $jmptarget = $jump;
43746        push (@ret, "$k jump: to rule $jump");
43747        next;
43748      }
43749      last;
43750    }
43751
43752    if ($nextrule == 0 && $jump) {
43753       $jmptarget = $jump;
43754       push (@ret, "$k jump: to rule $jump");
43755    }
43756
43757    $recpt = $new;
43758  }
43759  if ($k) {
43760    push (@ret, "returns: $new after rule $k in configuration");
43761  } else {
43762    push (@ret, "returns: $new - no rule found in configuration");
43763  }
43764  if (wantarray) {
43765    $ret[0] = $new;
43766    return @ret;
43767  } else {
43768    return $new;
43769  }
43770}
43771
43772sub RecRep {
43773  my ($toregex,$replregex,$sendregex,$recpt,$sender,$rnum) = @_;
43774  my @retval;
43775  my $cmpl_error;
43776  $retval[0] = "result";
43777
43778  $cmpl_error = RecRepSetRE('TO_RE',$toregex);
43779  push (@retval, $cmpl_error) if ($cmpl_error);
43780  $cmpl_error = RecRepSetRE('RP_RE',$replregex);
43781  push (@retval, $cmpl_error) if ($cmpl_error);
43782  $cmpl_error = RecRepSetRE('SE_RE',$sendregex);
43783  push (@retval, $cmpl_error) if ($cmpl_error);
43784
43785  if ($sender =~ /$SE_RE/i && $recpt =~ /$TO_RE/i) {
43786
43787    push (@retval, "$rnum  |\$1=$1|\$2=$2|\$3=$3|\$4=$4|\$5=$5|\$6=$6|\$7=$7|\$8=$8|\$9=$9|");
43788    my $d1 = $1;my $d2 = $2;my $d3 = $3;
43789    my $d4 = $4;my $d5 = $5;my $d6 = $6;
43790    my $d7 = $7;my $d8 = $8;my $d9 = $9;
43791
43792
43793    $replregex =~ s/\$1/$d1/;
43794    $replregex =~ s/\$2/$d2/;
43795    $replregex =~ s/\$3/$d3/;
43796    $replregex =~ s/\$4/$d4/;
43797    $replregex =~ s/\$5/$d5/;
43798    $replregex =~ s/\$6/$d6/;
43799    $replregex =~ s/\$7/$d7/;
43800    $replregex =~ s/\$8/$d8/;
43801    $replregex =~ s/\$9/$d9/;
43802    if (wantarray){
43803      $retval[0] = $replregex;
43804      push(@retval,'1');
43805      return @retval;
43806    } else {
43807      return $replregex;
43808    }
43809  } else {
43810    if (wantarray){
43811      $retval[0] = $recpt;
43812      push(@retval,'0');
43813      return @retval;
43814    } else {
43815      return $recpt;
43816    }
43817  }
43818}
43819
43820sub RecRepSetRE {
43821 use re 'eval';
43822 my ($var,$r)=@_;
43823 eval{$$var=qr/(?i)$r/};
43824 return $@;
43825}
43826
43827sub RcptRegexMake {
43828  my ($string,$how) = @_;
43829  if ($how) {
43830    $string =~ s/\./\\\./go;
43831    $string =~ s/\*/\(\.\*\)/go;
43832    $string =~ s/\+/\(\.\+\)/go;      # hidden option
43833    $string =~ s/\?/\(\.\?\)/go;      # hidden option
43834    $string =~ s/\;/\(\.\)/go;        # hidden option
43835    $string = "^".$string."\$";
43836  } else {
43837    my $i = 1;
43838    while ($string =~ /\*/o) {
43839       $string =~ s/\*/\$$i/o ;
43840       $i++;
43841    }
43842  }
43843  return $string;
43844}
43845
43846sub configChangeRcptRepl {
43847 my ($name, $old, $new, $init)=@_;
43848 return if $WorkerNumber > 0;
43849 mlog(0,"AdminUpdate: recipient replacement updated from '$old' to '$new'") unless $init || $new eq $old;
43850
43851 $ReplaceRecpt=$Config{ReplaceRecpt}=$new;
43852 $new=checkOptionList($new,'ReplaceRecpt',$init);
43853 my $k;
43854 my $i = 0;
43855 my $j = 0;
43856 my $t1;
43857 my $t2;
43858 my %rules;
43859 my $ret;
43860
43861 for my $v (split(/\|/o,$new)) {
43862     $v=~/(.*?)\<\=\>(.*?\<\=\>.*?\<\=\>.*?\<\=\>.*?\<\=\>.*?\<\=\>.*)/o;
43863     $t1 = $1;
43864     $t2 = $2;
43865     if ($t1 eq '') { # rule is disabled
43866       $j++;
43867       next;
43868     }
43869     if (! $t1 && ! $t2) {
43870       $ret .= ConfigShowError(1,"ERROR: syntax error in recipient replacement rule $v");
43871       $j++;
43872       next;
43873     }
43874     if (! $t2) {
43875       $ret .= ConfigShowError(1,"ERROR: syntax error in recipient replacement rule $v");
43876       $j++;
43877       next;
43878     }
43879     if ($t1 =~ /END/o) {
43880       $ret .= ConfigShowError(1,"ERROR: rule number END is not permitted - in recipient replacement rule $v");
43881       $j++;
43882       next;
43883     }
43884     if (exists $rules{$t1}) {
43885       $ret .= ConfigShowError(1,"ERROR: rule number $t1 is already defined with $rules{$t1} - ignore entry $v");
43886       $j++;
43887       next;
43888     }
43889     $i++;
43890     $rules{$t1}=$t2;
43891 }
43892 %RecRepRegex = %rules;
43893 my $tlit = $init ? 'info: ' : 'AdminUpdate: ';
43894
43895 return $ret;
43896}
43897
43898sub CheckRcptRepl {
43899 my $RecReprecipient = $qs{RecReprecipient};
43900 my $RecRepsender = $qs{RecRepsender};
43901 my $RecRepresult = '';
43902 my @RecRepresult;
43903 my $RecRepdspres = '';
43904 my $RecRepbutton;
43905 my $disabled = '';
43906 my $link_to_RecRep_config = $WebIP{$ActWebSess}->{lng}->{'msg500040'} || $lngmsg{'msg500040'};
43907
43908 my $updres;
43909 my $file;
43910 my @s;
43911 my $mtime;
43912
43913 if ($ReplaceRecpt =~ /^ *file: *(.+)/io) {
43914    $file=$1; $file="$base/$file" if $file!~/^\Q$base\E/io;
43915    @s=stat($file);
43916    $mtime=$s[9];
43917    if ( $FileUpdate{$file} != $mtime ) {
43918      $updres = configChangeRcptRepl('ReplaceRecpt',$ReplaceRecpt,$ReplaceRecpt,0);
43919    }
43920 }
43921
43922 if ($ReplaceRecpt) {
43923   if ($qs{B1} =~ /Check/o){
43924       @RecRepresult = RcptReplace($RecReprecipient,$RecRepsender,'RecRepRegex');
43925       if ($updres) {
43926          $RecRepresult = $RecRepresult[0];
43927          $RecRepresult[0] = $updres;
43928       } else {
43929          $RecRepresult = shift(@RecRepresult);
43930       }
43931   }
43932   $RecRepbutton ='
43933    <tr>
43934        <td class="noBorder">&nbsp;</td>
43935        <td class="noBorder"><input type="submit" name="B1" value="  Check  " /></td>
43936        <td class="noBorder">&nbsp;</td>
43937    </tr>';
43938   foreach (@RecRepresult) {
43939     next if ($_ eq '1' || $_ eq '0');
43940     s/configuration$/ file $file/ if ($file);
43941     $RecRepdspres .= "$_\<br /\>";
43942   }
43943 } else {
43944   @RecRepresult = ();
43945   push (@RecRepresult, $WebIP{$ActWebSess}->{lng}->{'msg500041'} || $lngmsg{'msg500041'});
43946   $disabled = "disabled";
43947 }
43948
43949 if ($ReplaceRecpt =~ /^ *file: *(.+)/io) {
43950  $file = $1;
43951  if ($file) {
43952    $link_to_RecRep_config = $WebIP{$ActWebSess}->{lng}->{'msg500042'} || $lngmsg{'msg500042'};
43953    $link_to_RecRep_config .= $file.' &nbsp;<input type="button" value="Edit" onclick="javascript:popFileEditor(\''.$file.'\',3);" /></p>';
43954  }
43955 }
43956 my $h1 = $WebIP{$ActWebSess}->{lng}->{'msg500043'} || $lngmsg{'msg500043'};
43957
43958<<EOT;
43959$headerHTTP
43960$headerDTDTransitional
43961$headers
43962<div class="content">
43963<h2>Recipient Replacement Test</h2>
43964<div class="textBox">
43965$link_to_RecRep_config
43966</div>
43967<form method="post" action=\"\">
43968    <table class="textBox" style="width: 99%;">
43969        <tr>
43970            <td class="noBorder">recipient : </td>
43971            <td class="noBorder">
43972            <input type="text" $disabled size="30" name="RecReprecipient" value="$RecReprecipient"</td>
43973        </tr>
43974        <tr>
43975            <td class="noBorder">sender    : </td>
43976            <td class="noBorder">
43977            <input type="text" $disabled size="30"  name="RecRepsender" value="$RecRepsender"</td>
43978        </tr>
43979        <tr><td class="noBorder">  </td></tr>
43980        <tr>
43981            <td class="noBorder">result    : </td>
43982            <td class="noBorder">
43983            <p>$RecRepresult</p></td>
43984        </tr>
43985        $RecRepbutton
43986    </table>
43987</form>
43988<div class="textBox">
43989$h1
43990$RecRepdspres
43991</form>
43992<form name="ASSPconfig" id="ASSPconfig" action="" method="post">
43993  <input name="theButtonLogout" type="hidden" value="" />
43994</form>
43995</div>
43996</div>
43997$footers
43998</body></html>
43999EOT
44000
44001}
44002
44003sub writeRebuild {
44004my $rebuild_version = '13177';
44005my $curr_version;
44006if (open my $ADV, '<',"$base/rebuildspamdb.pl") {
44007    while (<$ADV>) {
44008        if (/^\s*our \$modversion.+?\((.*)\)/o) {
44009            $curr_version = $1;
44010
44011            last;
44012        }
44013
44014    }
44015    close $ADV;
44016
44017    if ($curr_version eq $rebuild_version) {
44018    	mlog(0,"info: $base/rebuildspamdb.pl $curr_version is current ");
44019    	return 0;
44020    }
44021
44022}
44023
44024mlog(0,"info: installing new $base/rebuildspamdb.pl version $rebuild_version");
44025(open my $ADV, '>',"$base/rebuildspamdb.pl") or return 0;
44026
44027print $ADV <<'RBEOT' or return 0;
44028#!/usr/bin/perl
44029# rebuilds bayesian spam database
44030our $VERSION = "RB-1.99";
44031our $modversion = '(13177)';
44032# (c) John Hanna 2003 under the terms of the GPL
44033# Updated July 2004 for simple proxy support.
44034# (c) Fritz Borgstedt 2006 under the terms of the GPL
44035# Updated Feb 2008 refactoring and rewrites
44036# (c) Kevin 2008 under the terms of the GPL
44037use bytes;    # get rid of anoying 'Malformed UTF-8' messages
44038use Digest::MD5 qw(md5_hex);
44039use English '-no_match_vars';
44040use File::Copy;
44041use IO::Handle;
44042use IO::Socket;
44043use Time::Local;
44044use Time::HiRes;
44045use Cwd;
44046use strict qw(vars subs);
44047our $AvailLWP  = eval('use LWP::Simple; 1');    # LWP::Simple module installed
44048our $CanUseLWP = $AvailLWP;
44049our	$noGriplistUpload;
44050our	$noGriplistDownload;
44051our $gripListDownUrl = 'http://*HOST*/cgi-bin/assp_griplist?binary';
44052our $gripListUpUrl = 'http://*HOST*/cgi-bin/assp_griplist?binary';
44053our $gripListUpHost = 'assp.sourceforge.net';
44054$gripListDownUrl =~ s/\*HOST\*/$gripListUpHost/o;
44055$gripListUpUrl  =~ s/\*HOST\*/$gripListUpHost/o;
44056
44057our %Config;
44058our $ReplaceOldSpamdb=1;
44059our $onlyNewCorrected=0;
44060
44061our $HeaderValueRe=qr/[ \t]*[^\r\n]*(?:\r?\n[ \t]+\S[^\r\n]*)*(?:\r?\n)?/o;
44062# IP address representations
44063our $IPprivate;
44064our $IPQuadSectRE;
44065our $IPQuadSectDotRE;
44066our $IPQuadRE;
44067our $IPStrictQuadRE;
44068
44069# Host
44070our $IPSectRe;
44071our $IPSectHexRe;
44072our $IPSectDotRe;
44073our $IPSectHexDotRe;
44074our $IPRe;
44075our $IPv4Re;
44076our $IPv6Re;
44077our $IPv6LikeRe;
44078our $PortRe;
44079our $HostRe;
44080our $HostPortRe;
44081# IP address representations
44082my $sep;
44083my $v6Re = '[0-9A-Fa-f]{1,4}';
44084$IPSectRe = '(?:25[0-5]|2[0-4]\d|1\d\d|0?\d?\d)';
44085$IPSectHexRe = '(?:(?:0x)?(?:[A-Fa-f][A-Fa-f0-9]?|[A-Fa-f0-9]?[A-Fa-f]))';
44086
44087$IPprivate  = '^(?:0{1,3}\.0{1,3}\.0{1,3}\.0{1,3}|127(?:\.'.$IPSectRe.'){3}|169\.254(?:\.'.$IPSectRe.'){2}|0?10(?:\.'.$IPSectRe.'){3}|192\.168(?:\.'.$IPSectRe.'){2}|172\.0?1[6-9](?:\.'.$IPSectRe.'){2}|172\.0?2[0-9](?:\.'.$IPSectRe.'){2}|172\.0?3[01](?:\.'.$IPSectRe.'){2})$';   #RFC 1918 decimal
44088$IPprivate .= '|^(?:(?:0x)?0{1,2}\.(?:0x)?0{1,2}\.(?:0x)?0{1,2}\.(?:0x)?0{1,2}|(?:0x)?7[Ff](?:\.'.$IPSectHexRe.'){3}|(?:0x)?[aA]9\.(?:0x)?[Ff][Ee](?:\.'.$IPSectHexRe.'){2}|(?:0x)?0[aA](?:\.'.$IPSectHexRe.'){3}|(?:0x)?[Cc]0\.(?:0x)?[Aa]8(?:\.'.$IPSectHexRe.'){2}|(?:0x)[Aa][Cc]\.(?:0x)1[0-9a-fA-F](?:\.'.$IPSectHexRe.'){2})$';   #RFC 1918 Hex
44089$IPprivate .= '|^(?:0{0,4}:){2,6}'.$IPprivate.'$';  # privat IPv4 in IPv6
44090$IPprivate .= '|^(?:0{0,4}:){2,7}[1:]?$';  # IPv6 loopback and universal
44091
44092$IPQuadSectRE='(?:0([0-7]+)|0x([0-9a-fA-F]+)|(\d+))';
44093$IPQuadSectDotRE='(?:'.$IPQuadSectRE.'\.)';
44094$IPQuadRE=qr/$IPQuadSectDotRE?$IPQuadSectDotRE?$IPQuadSectDotRE?$IPQuadSectRE/o;
44095
44096
44097
44098$IPSectDotRe = '(?:'.$IPSectRe.'\.)';
44099$IPSectHexDotRe = '(?:'.$IPSectHexRe.'\.)';
44100$IPv4Re = qr/(?:
44101(?:$IPSectDotRe){3}$IPSectRe
44102|
44103(?:$IPSectHexDotRe){3}$IPSectHexRe
44104)/xo;
44105
44106# privat IPv6 addresses
44107$IPprivate .= <<EOT;
44108|^(?i:FE[89A-F][0-9A-F]):
44109(?:
44110(?:(?:$v6Re:){6}(?:                                $v6Re      |:))|
44111(?:(?:$v6Re:){5}(?:                   $IPv4Re |   :$v6Re      |:))|
44112(?:(?:$v6Re:){4}(?:                  :$IPv4Re |(?::$v6Re){1,2}|:))|
44113(?:(?:$v6Re:){3}(?:(?:(?::$v6Re)?    :$IPv4Re)|(?::$v6Re){1,3}|:))|
44114(?:(?:$v6Re:){2}(?:(?:(?::$v6Re){0,2}:$IPv4Re)|(?::$v6Re){1,4}|:))|
44115(?:(?:$v6Re:)   (?:(?:(?::$v6Re){0,3}:$IPv4Re)|(?::$v6Re){1,5}|:))|
44116                (?:(?:(?::$v6Re){0,4}:$IPv4Re)|(?::$v6Re){1,6}|:)
44117)\$
44118EOT
44119$IPprivate = qr/$IPprivate/xo;
44120
44121# RFC4291, section 2.2, "Text Representation of addresses"
44122$sep = '[:-]';
44123$IPv6Re = $IPv6LikeRe = <<EOT;
44124(?:
44125(?:(?:$v6Re$sep){7}(?:                                         $v6Re      |$sep))|
44126(?:(?:$v6Re$sep){6}(?:                         $IPv4Re |   $sep$v6Re      |$sep))|
44127(?:(?:$v6Re$sep){5}(?:                     $sep$IPv4Re |(?:$sep$v6Re){1,2}|$sep))|
44128(?:(?:$v6Re$sep){4}(?:(?:(?:$sep$v6Re)?    $sep$IPv4Re)|(?:$sep$v6Re){1,3}|$sep))|
44129(?:(?:$v6Re$sep){3}(?:(?:(?:$sep$v6Re){0,2}$sep$IPv4Re)|(?:$sep$v6Re){1,4}|$sep))|
44130(?:(?:$v6Re$sep){2}(?:(?:(?:$sep$v6Re){0,3}$sep$IPv4Re)|(?:$sep$v6Re){1,5}|$sep))|
44131(?:(?:$v6Re$sep)   (?:(?:(?:$sep$v6Re){0,4}$sep$IPv4Re)|(?:$sep$v6Re){1,6}|$sep))|
44132(?:        $sep    (?:(?:(?:$sep$v6Re){0,5}$sep$IPv4Re)|(?:$sep$v6Re){1,7}|$sep))
44133)
44134EOT
44135
44136$IPv6Re =~ s/\Q$sep\E/:/go;
44137$IPv6Re = qr/$IPv6Re/xo;
44138$IPv6LikeRe = qr/$IPv6LikeRe/xo;
44139
44140$IPRe = qr/(?:
44141$IPv4Re
44142|
44143$IPv6Re
44144)/xo;
44145our $EmailDomainRe;
44146$EmailDomainRe=qr/(?:\w[\w\.\-]*\.\w\w+|\[[\d\.]*\.\d+\])/o;
44147# re for a single port - could be number 1 to 65535
44148$PortRe = qr/(?:(?:[1-6]\d{4})|(?:[1-9]\d{0,3}))/o;
44149# re for a single host - could be an IP a name or a fqdn
44150$HostRe = qr/(?:(?:$IPv4Re|\[?$IPv6Re\]?)|$EmailDomainRe|\w\w+)/o;
44151$HostPortRe = qr/$HostRe:$PortRe/o;
44152
44153our %m;
44154our %GpOK;
44155if ($CanUseLWP) {
44156#        my $ver        = eval('LWP::Simple->VERSION');
44157#        print "LWP::Simple $ver installed - download griplist available\n" ;
44158    } elsif ( !$AvailLWP ) {
44159        print "LWP::Simple module not installed - download griplist not available\n";
44160    }
44161#use warnings;
44162
44163#no output buffering to screen
44164*STDOUT->autoflush();
44165
44166#holy predeclarations Batman!
44167use vars qw(
44168    $autoCorrectCorpus $base $DropList $correctednotspam $correctednotspamcount $correctedspam
44169    $correctedspamcount $discarded $DoDropList $DoNotCollectRed $EmailAdrRe $EmailDomainRe $EmailFrom $EmailAdminReportsTo $griplist $HamWordCount
44170    $KeepwhitelistedSpam $lowernorm $logfile $Log $LogDateFormat $maillogExt $MaxBytes $MaxCorrectedDays $MaxBayesFileAge $MaxNoBayesFileAge  $MaintBayesCollection $MaxFiles $MaxKeepDeleted $MaxWhitelistDays
44171    $MaxWhitelistLength  $maintbayescollection $minimumfiles $minimumdays $mydb $myhost $mypassword $myuser $myName $notspamlog $processTime
44172    $notspamlogcount $npRe $incomingOkMail $OrderedTieHashSize $pbdbfile $proxyserver $proxyuser $proxypass $noGriplist
44173    $RebuildLog $rebuildrun $redlistdb $redRe $redReRE $resendmail $setFilePermOnStart $silent $spamdb $spamdberror $spamdbFile $spamdberrorFile $RegExLength
44174    $spam $spamdbFname $spamdberrorFname $spamlog $spamlogcount $SpamWordCount $starttime
44175    $usesubject $Whitelistrb_cleanFreq $whitelistdb $WhitelistObject $RebuildNotify $RedlistObject $viruslog $whiteRe $whiteReRE $wildcardUser
44176    %HamHash %Helo %Redlist %spam %SpamHash %Whitelist $asspLog $DoNotCollectRedList $DoNotCollectRedRe
44177    $DoFullGripDownload $UseLocalTime $uppernorm $TrashObject %Trashlist
44178    $runAsUser $runAsGroup
44179);
44180# load from command line if specified
44181if($ARGV[0]) {
44182 $base=$ARGV[0];
44183} else {
44184 # the last one is the one used if all else fails
44185 $base = cwd();
44186 unless (-e "$base/assp.cfg") {
44187   foreach ('.','/usr/local/assp','/home/assp','/etc/assp','/usr/assp','/applications/assp','/assp','.') {
44188    if (-e "$_/assp.cfg") {
44189      $base=$_;
44190      last ;
44191    }
44192   }
44193 }
44194 $base = cwd() if $base eq '.';
44195}
44196unless (chdir $base) {
44197print
44198"Usage:
44199  perl rebuildspamdb.pl  c:\\assp  -- runs the programm in basedirectory c:\\assp\n
44200";
44201 die "Abort: unable to change to basedirectory $base";
44202}
44203$silent = 1 if (lc $ARGV[1] =~ /silent/i ||  lc $ARGV[0] =~ /silent/i);
44204
44205#load configuration options from assp.cfg file
44206&loadconfig();
44207
44208fork() && exit;
44209
44210# open log file
44211if ( -e "$rebuildrun.bak" ) {
44212    unlink("$rebuildrun.bak") or die "unable to remove file: $!";
44213}
44214if ( -e $rebuildrun ) {
44215    copy( $rebuildrun, "$rebuildrun.bak" ) or die "unable to copy file for: $!";
44216}
44217open( $RebuildLog, '>', "$rebuildrun" ) or die "unable to open file for logging: $!";
44218
44219our $silentlog;
44220$starttime = time;
44221&rb_printlog("\n");
44222for ( my $c = 10; $c >= 1; $c-- ) { &rb_printlog(q{*}); }
44223my $savesilent=$silent;
44224$silent=0;
44225rb_printlog (&timestring(time) . " RebuildSpamDB $VERSION $modversion is starting;\n") ;
44226$silent=$savesilent;
44227
44228&rb_printlog( "\nRunning in $myName basedirectory '$base'\n");
44229#-- check if running as root
44230&rb_printlog( "Running as root!!\n") if $< == 0 && $^O ne "MSWin32";
44231
44232#-- print username
44233&rb_printlog( "Running as user '" . (getpwuid($<))[0] . "'\n") if $< != 0 && $^O ne "MSWin32";
44234
44235&rb_printlog("\n--- ASSP $myName Settings ---\n");
44236
44237my $AvailTieRDBM  = eval "use Tie::RDBM; 1";    # Is the required module installed?
44238my $CanUseTieRDBM = $AvailTieRDBM;              # this looks wierd but it's the only way it works
44239undef $AvailTieRDBM;
44240$EmailAdrRe    = "[^()<>@,;:\\\"\\[\\]\000-\040]+";
44241$EmailDomainRe = '(?:\w[\w\.\-]*\.\w+|\[[\d\.]*\.\d+\])';
44242
44243# set counts
44244$HamWordCount          = $SpamWordCount = $correctedspamcount = 0;
44245$correctednotspamcount = $spamlogcount  = $notspamlogcount    = 0;
44246
44247# read old norm
44248our $Normfile;
44249our ($oldnorm, $oldcorrectedspamcount, $oldcorrectednotspamcount, $oldspamlogcount, $oldnotspamlogcount);
44250open( $Normfile, '<', "$base/normfile" ) || warn "unable to open $base/normfile: $!\n";
44251if ($Normfile) {
44252	while (<$Normfile>) {
44253    	($oldnorm, $oldcorrectedspamcount, $oldcorrectednotspamcount, $oldspamlogcount, $oldnotspamlogcount) = split(" ",$_);
44254    }
44255    close $Normfile;
44256}
44257
44258
44259
44260if ($DoNotCollectRedList) {
44261    &rb_printlog(
44262        "Do Not Collect Messages with redlisted address: Enabled\n**Messages with redlisted addresses will be removed from the corpus!**\n\n"
44263    );
44264}
44265
44266&rb_printlog("Maxbytes: $MaxBytes \n");
44267&rb_printlog("Maxfiles: $MaxFiles \n");
44268
44269
44270#rebuild various cache files and lists
44271&repair();
44272
44273# Let's rb_clean the old deleted entries
44274
44275 &rb_cleanTrashlist();
44276
44277# Let's rb_clean the non bayesian folder of old entries
44278# Let's rb_clean the bayesian folder of old entries
44279# Let's rb_clean the corrected spam/notspam folder of old entries
44280
44281 &rb_cleanUpCollection();
44282
44283
44284# name, contents, refrence to "compiled" object
44285#&compileregex( "whiteRe", $whiteRe, \$whiteReRE );
44286&compileregex( "redRe",   $redRe,   \$redReRE );
44287
44288# redlist,whitelist
44289&createlistobjects();
44290
44291# isspam?, path, filter, weight, processing sub
44292$correctedspamcount    = &processfolder( 1, $correctedspam,    "*",      2, \&dospamhash );
44293$correctednotspamcount = &processfolder( 0, $correctednotspam, "*",      4, \&dohamhash );
44294$spamlogcount          = &processfolder( 1, $spamlog,          "*", 1, \&checkspam );
44295$notspamlogcount       = &processfolder( 0, $notspamlog,       "*", 1, \&checkham );
44296our $norm = $HamWordCount ? ( $SpamWordCount / $HamWordCount ) : 1;
44297$norm = sprintf("%.4f",$norm);
44298open( my $normFile, '>', "$base/normfile" ) || warn "unable to open $base/normfile: $!\n";
44299if ($normFile) {
44300    print { $normFile } "$norm $correctedspamcount $correctednotspamcount $spamlogcount $notspamlogcount";
44301    close $normFile;
44302}
44303
44304# Create Bayesian DB
44305&generatescores();
44306our %HeloBlack;
44307our $HeloBlackObject = tie %HeloBlack, 'orderedtie', "$base/$spamdb.helo";
44308# Create HELo blacklist
44309&createheloblacklist();
44310&rb_printlog(
44311    "\nSpam Weight:\t   " . commify($SpamWordCount) . "\nNot-Spam Weight:   " . commify($HamWordCount) . "\n\n" );
44312if ( !($norm) ) {    #invalid norm
44313    &rb_printlog("Warning: Corpus insufficent to calculate normality!\n");
44314}
44315else {               #norm exists, print it
44316
44317
44318        my $normdesc = '';
44319
44320        &rb_printlog( "Wanted Corpus norm:\t%.4f %s $normdesc \n", $autoCorrectCorpus  );
44321        &rb_printlog( "Reached Corpus norm:\t%.4f %s $normdesc \n", $norm  );
44322
44323
44324}
44325
44326
44327$lowernorm = 0.5 if $lowernorm && ($lowernorm > 1 or $lowernorm < 0.5);
44328
44329
44330if   ( time - $starttime != 0 ) { $processTime = time - $starttime; }
44331else                            { $processTime = 1; }
44332&rb_printlog( "\nTotal processing time: %d second(s)\n\n", $processTime );
44333
44334&downloadGripConf();
44335&uploadgriplist() if ! $noGriplistUpload && !$noGriplist;
44336&downloadgriplist() if ! $noGriplistDownload && !$noGriplist;
44337
44338
44339&downloaddroplist();
44340
44341$savesilent=$silent;
44342$silent=0;
44343&rb_printlog( "\n");
44344&rb_printlog( &timestring(time) . " RebuildSpamDB $VERSION $modversion ended;\n");
44345$silent=$savesilent;
44346&rb_printlog( "Sending Notify to $RebuildNotify\n") if $RebuildNotify;
44347&rb_printlog( "Sending Notify not possible, address in RebuildNotify missing\n") if !$RebuildNotify;
44348close $RebuildLog;
44349if ($RebuildNotify) {
44350        &sendNotification(
44351          $EmailFrom,
44352          $RebuildNotify,
44353          'RebuildSpamDB - report',
44354          "File rebuildrun.txt follows:\r\n\r\n",
44355          "$base/rebuildrun.txt");
44356    }
44357
44358##########################################
44359#           script ends here
44360##########################################
44361sub createlistobjects {
44362
44363    if ( $CanUseTieRDBM && $whitelistdb =~ /mysql/ && !$KeepwhitelistedSpam ) {
44364        eval {
44365            $WhitelistObject = tie %Whitelist, 'Tie::RDBM', "dbi:mysql:database=$mydb;host=$myhost",
44366                { user => "$myuser", password => "$mypassword", table => 'whitelist', create => 0 };
44367        };
44368        if ($EVAL_ERROR) {
44369            &rb_printlog("whitelist mysql error: $@");
44370            $CanUseTieRDBM = 0;
44371            $whitelistdb   = "whitelist";
44372        }
44373    }
44374    elsif ( !$KeepwhitelistedSpam ) {
44375        if ( -e $whitelistdb ) { $WhitelistObject = tie( %Whitelist, 'orderedtie', "$whitelistdb" ); }
44376    }
44377    if ( $CanUseTieRDBM && $redlistdb =~ /mysql/ && ( $DoNotCollectRed || $DoNotCollectRedList ) ) {
44378        eval {
44379            $RedlistObject = tie %Redlist, 'Tie::RDBM', "dbi:mysql:database=$mydb;host=$myhost",
44380                { user => "$myuser", password => "$mypassword", table => 'redlist', create => 0 };
44381        };
44382        if ($EVAL_ERROR) {
44383            &rb_printlog("redlist mysql error: $@");
44384            $CanUseTieRDBM = 0;
44385            $redlistdb     = "redlist";
44386        }
44387    }
44388    elsif ($DoNotCollectRed) {
44389        if ( -e $redlistdb ) { $RedlistObject = tie( %Redlist, 'orderedtie', "$redlistdb" ); }
44390    }
44391    return;
44392} ## end sub createlistobjects
44393
44394sub generatescores {
44395    my ( $t, $s, @result, $pair, $v );
44396    &rb_printlog("\nGenerating weighted Bayesian tuplets...");
44397    open( $spamdbFile, '>', "$spamdb.tmp" ) || die "unable to open $spamdb.tmp: $!\n";
44398    binmode $spamdbFile;
44399    print { $spamdbFile } "\n";
44400    while ( ( $pair, $v ) = each(%spam) ) {
44401        ( $s, $t ) = split( q{ }, $v );
44402        $t = ( $t - $s ) * $norm + $s;    # normalize t
44403        if ( $t < 5 ) {
44404
44405            #$unknowns+=$s; $unknownt+=$t;
44406            next;
44407        }
44408
44409        # if token represents all spam or all ham then square its value
44410        if ( $s == $t || $s == 0 ) {
44411            $s = $s * $s;
44412            $t = $t * $t;
44413        }
44414        $v = ( 1 + $s ) / ( $t + 2 );
44415        $v = sprintf( "%.7f", $v );
44416        $v = '0.9999999' if $v >= 1;
44417        $v = '0.0000001' if $v <= 0;
44418        push( @result, "$pair\002$v\n" ) if abs( $v - .5 ) > .09;
44419    }
44420    &rb_printlog("done\n");
44421    undef %spam;    # free some memory
44422    &rb_printlog("\nSaving rebuilt SPAM database...");
44423    for ( sort @result ) { print { $spamdbFile } $_; }
44424    close $spamdbFile;
44425    if ( -e "$spamdb.bak" ) { unlink("$spamdb.bak") || &rb_printlog("unable to remove '$spamdb.bak' $!\n") }
44426    if ( -e $spamdb ) {
44427        rename( $spamdb, "$spamdb.bak" ) || &rb_printlog("unable to rename '$spamdb' to '$spamdb.bak' $!\n");
44428    }
44429    rename( "$spamdb.tmp", $spamdb ) || &rb_printlog("unable to rename '$spamdb.tmp' to '$spamdb' $!\n");
44430    &rb_printlog("done\n");
44431    my $filesize = -s "$spamdb";
44432    &rb_printlog( "\nResulting file '$spamdbFname' is " . commify($filesize) . " bytes\n" );
44433    my $pairs = scalar @result;
44434    &rb_printlog( "Bayesian Pairs: " . commify($pairs) . "\n" );
44435    return;
44436} ## end sub generatescores
44437
44438
44439sub createheloblacklist {
44440    my (@Helo);
44441    open( my $FheloBlack, '>', "$spamdb.helo.tmp" ) || &rb_printlog("unable to open '$spamdb.helo.tmp' $!\n");
44442    binmode $FheloBlack;
44443    print { $FheloBlack } "\n";
44444    my $allcount = 0;
44445    my $notnew = 0;
44446    while ( my ( $helostr, $weights ) = each(%Helo) ) {
44447
44448		if ( $weights->[1] / ( $weights->[0] + $weights->[1] + .1 ) > .98 ) { 	push( @Helo, "$helostr\0021\n" ); }
44449		elsif 	( $weights->[1] / ( $weights->[0] + $weights->[1] + .1 ) < .2 ) { push( @Helo, "$helostr\0020.01\n" ); }
44450
44451	}
44452    print { $FheloBlack } sort @Helo;
44453    eval{close $FheloBlack;};
44454    &rb_printlog( "\nHELO Blacklist: " . scalar(@Helo) . " HELOs\n" );
44455    if ( -e "$spamdb.helo.bak" ) {
44456        unlink("$spamdb.helo.bak") || &rb_printlog("unable to remove '$spamdb.helo.bak' $!\n");
44457    }
44458    if ( -e "$spamdb.helo" ) {
44459        rename( "$spamdb.helo", "$spamdb.helo.bak" )
44460            || &rb_printlog("unable to rename '$spamdb.helo' to '$spamdb.helo.bak' $!\n");
44461    }
44462    rename( "$spamdb.helo.tmp", "$spamdb.helo" )
44463        || &rb_printlog("unable to rename '$spamdb.helo.tmp' to '$spamdb.helo' $!\n");
44464    return;
44465}
44466
44467sub loadconfig {
44468
44469    my $RCF;
44470    open($RCF,"<$base/assp.cfg");
44471    while (<$RCF>) {
44472        s/\r|\n//go;
44473    	my ($k,$v) = split(/:=/o,$_,2);
44474        next unless $k;
44475        $Config{$k} = $v;
44476    }
44477    close $RCF;
44478    $TrashObject       = tie %Trashlist,   'orderedtie', "$base/trashlist.db";
44479
44480    $DoDropList     	 = $Config{ DoDropList };
44481    $runAsUser			 = $Config{ runAsUser };
44482    $runAsGroup			 = $Config{ runAsGroup };
44483    $correctednotspam    = $Config{ correctednotspam } && "$Config{base}/$Config{correctednotspam}" || 'errors/notspam';
44484    $correctedspam       = $Config{ correctedspam } && "$Config{base}/$Config{correctedspam}" || 'errors/spam';
44485	$incomingOkMail      		 = $Config{ incomingOkMail } && "$Config{base}/$Config{incomingOkMail}" || 'okmail';
44486    $DoNotCollectRed     = $Config{ DoNotCollectRed };
44487    $DoNotCollectRedRe   = $Config{ DoNotCollectRedRe };
44488    $DoNotCollectRedList = $Config{ DoNotCollectRedList };
44489    $KeepwhitelistedSpam = $Config{ KeepwhitelistedSpam };
44490    $logfile             = $Config{ logfile } && "$Config{base}/$Config{logfile}" || 'maillog.txt';
44491	$Log                 = $Config{ logfile } && "$Config{base}/$Config{logfile}" || 'maillog.txt';
44492    $maillogExt          = $Config{ maillogExt };
44493    $MaxBytes            = $Config{ MaxBytes } || 10000;
44494    $MaxFiles            = $Config{ MaxFiles } || 14000;
44495
44496    $MaxWhitelistDays    = $Config{ MaxWhitelistDays } || 90;
44497    $MaxCorrectedDays    		= $Config{ MaxCorrectedDays } || 1000;
44498    $MaxNoBayesFileAge	 		= $Config{ MaxNoBayesFileAge } || 30;
44499    $MaxBayesFileAge	 		= $Config{ MaxBayesFileAge };
44500    $MaintBayesCollection	 	= $Config{ MaintBayesCollection } || 1;
44501    $maintbayescollection		= $MaintBayesCollection;
44502    $MaxWhitelistLength  	= $Config{ MaxWhitelistLength } || 60;
44503	$MaxKeepDeleted  		= $Config{ MaxKeepDeleted } || 0;
44504    $notspamlog          	= $Config{ notspamlog } && "$Config{base}/$Config{notspamlog}" || 'notspam';
44505    $npRe                = $Config{ npRe };
44506    $OrderedTieHashSize  = $Config{ OrderedTieHashSize } || 10_000;
44507    $pbdbfile            = $Config{ pbdb };
44508
44509    $proxyserver         = $Config{ proxyserver };
44510    $proxyuser			 = $Config{ proxyuser };
44511    $proxypass			 = $Config{ proxypass };
44512    $resendmail			 = $Config{ resendmail };
44513    $redlistdb           = $Config{ redlistdb } && "$Config{base}/$Config{redlistdb}" || 'redlist';
44514    $redRe               = $Config{ redRe };
44515	$myName				 = $Config{ myName };
44516    $setFilePermOnStart	 = $Config{ setFilePermOnStart };
44517    $spamdb              = $Config{ spamdb } && "$Config{base}/$Config{spamdb}" || 'spamdb';
44518    $spamdbFname         = $Config{ spamdb } || 'spamdb';
44519    $spamdberrorFname    = $spamdbFname.'error';
44520    $spamdberror    	 = $spamdb.'error';
44521    $spamlog             = $Config{ spamlog } && "$Config{base}/$Config{spamlog}" || 'spam';
44522    $discarded           = $Config{ discarded } && "$Config{base}/$Config{discarded}" || 'discarded';
44523	$viruslog            = $Config{ viruslog } && "$Config{base}/$Config{viruslog}" || 'viruslog';
44524    $usesubject          = $Config{ UseSubjectsAsMaillogNames };
44525    $whitelistdb         = $Config{ whitelistdb } && "$Config{base}/$Config{whitelistdb}" || 'whitelist';
44526	$griplist			 = $Config{ griplist };
44527	$DropList            = $Config{ DropList } || 'file:files/droplist.txt';
44528	($DropList) 		 = $DropList =~ /^ *file: *(.+)/i if $DropList =~ /^ *file:/;
44529    $noGriplist    		 = $Config{ noGriplist };
44530	$noGriplistUpload    = $Config{ noGriplistUpload };
44531	$noGriplistDownload  = $Config{ noGriplistDownload };
44532    $asspLog             = $Config{ asspLog };
44533    $whiteRe             = $Config{ whiteRe };
44534    $wildcardUser        = $Config{ wildcardUser };
44535    $mydb                = $Config{ mydb };
44536    $myhost              = $Config{ myhost };
44537    $myuser              = $Config{ myuser };
44538    $mypassword          = $Config{ mypassword };
44539    $rebuildrun          = &fixPath($base) . "/rebuildrun.txt";
44540    $EmailAdminReportsTo = $Config{ EmailAdminReportsTo };
44541    $RebuildNotify		 = $Config{ RebuildNotify };
44542
44543
44544    $autoCorrectCorpus	 = $Config{ autoCorrectCorpus } || "1";
44545
44546
44547	$minimumfiles = 10000 if !$minimumfiles;
44548	$minimumfiles = 5000 if  $minimumfiles < 5000;
44549	$minimumdays = 14 if !$minimumdays;
44550	$minimumdays = 7 if  $minimumdays < 7;
44551
44552    $EmailFrom		 	 = $Config{ EmailFrom };
44553    $RegExLength         = $Config{ RegExLength };
44554    $UseLocalTime        = $Config{ UseLocalTime };
44555    $LogDateFormat		 = $Config{ LogDateFormat } || "MMM-DD-YY hh:mm:ss";
44556
44557    $DoFullGripDownload  = $Config{ DoFullGripDownload };
44558
44559    return;
44560} ## end sub loadconfig
44561
44562sub processfolder {
44563    my ( $fldrType, $fldrpath, $filter, $weight, $sub ) = @_;
44564    my ( $count, $processFolderTime, $folderStartTime, $fileCount, @files );
44565    our ( $WhiteCount, $RedCount );
44566    $folderStartTime = time;
44567    $fldrpath        = &fixPath($fldrpath);
44568    &rb_printlog( "\n" . $fldrpath . "\n" );
44569    $fldrpath .= "/*";
44570    $fileCount = &countfiles($fldrpath);
44571    &rb_printlog( "File Count:\t" . commify($fileCount) );
44572    &rb_printlog("\nProcessing...");
44573    $count = $WhiteCount = 0;
44574    @files = glob($fldrpath);
44575    my $importmaxfiles = $MaxFiles;
44576
44577	my $percent;
44578
44579
44580	my $filenum;
44581
44582
44583
44584
44585
44586    #while( glob($fldrpath) && $count <= $MaxFiles ) {
44587    foreach my $file (@files) {
44588
44589        &add( $fldrType, $file, $weight, $sub );
44590        last if  ($spamlogcount && $autoCorrectCorpus && !$fldrType && $SpamWordCount && ($HamWordCount * $autoCorrectCorpus) > $SpamWordCount);
44591        $count++;
44592        last if $count >= $importmaxfiles;    #too many files
44593    }
44594    if   ( time - $folderStartTime != 0 ) { $processFolderTime = time - $folderStartTime; }
44595    else                                  { $processFolderTime = 1; }
44596    $count = $count - $WhiteCount ;
44597
44598
44599    if ($WhiteCount) {
44600        &rb_printlog( "\nRemoved White:\t" . commify($WhiteCount) );
44601    }
44602
44603    &rb_printlog( "\nImported Files:\t" . commify($count) );
44604    &rb_printlog( "\nImported SpamWordCount(total):\t" . commify($SpamWordCount) );
44605    &rb_printlog( "\nImported HamWordCount(total):\t" . commify($HamWordCount) );
44606
44607
44608    if ( $count  > $MaxFiles ) {
44609        $maintbayescollection = 1;
44610    }
44611
44612    #&rb_printlog( "\n " . commify($SpamWordCount) . " spam weight \n " . commify($HamWordCount) . " non-spam weight." );
44613    &rb_printlog("\nFinished in $processFolderTime second(s)\n");
44614
44615    return $count;
44616} ## end sub processfolder
44617
44618sub countfiles {
44619    my ($fldrpath) = @_;
44620    my @fileCount = glob("$fldrpath");
44621    return scalar(@fileCount);
44622}
44623
44624sub commify {
44625    local $_ = shift;
44626    1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
44627    return $_;
44628}
44629
44630sub hash {
44631    my ($msgText) = @_;
44632
44633    my ( $head, $body );
44634
44635    # creates a md5 hash of $msg body
44636    if ( $msgText =~ /^(.*?)\n\r?\n(.*)/s ) {
44637
44638        $head = $1;
44639        $body = $2;
44640
44641        return md5_hex($body);
44642    }
44643    else {
44644
44645        #return q;
44646        #There is no split, the message has no valid body
44647        return md5_hex($msgText);
44648    }
44649
44650    #return $value;
44651    return;
44652}
44653
44654sub dospamhash {
44655    my ( $FileName, $msgText ) = @_;
44656    $SpamHash{ &hash($msgText) } = '1';
44657    return;
44658}
44659
44660sub dohamhash {
44661    my ( $FileName, $msgText ) = @_;
44662    $HamHash{ &hash($msgText) } = q{};
44663    return;
44664}
44665
44666sub checkspam {
44667    my ( $FileName, $msgText ) = @_;
44668    our $HamHash;
44669    $msgText = &hash($msgText);
44670    my ( $return, $reason );
44671    if ( defined( $HamHash{ $msgText } ) ) {
44672
44673        # we've found a message in the spam database that is the same as one in the corrected Ham group
44674        my $fn = shift;
44675        &deletefile( $fn, "found in $correctednotspam" );
44676        return 1;
44677    } elsif ( $reason = &redlisted( $_[1] ) ) {
44678        my $fn = shift;
44679        &deletefile( $fn, $reason );
44680        return 1;
44681
44682    } elsif ( $reason = &whitelisted( $_[1] ) ) {
44683        my $fn = shift;
44684        &deletefile( $fn, $reason );
44685        return 1;
44686    }
44687    return 0;
44688}
44689
44690sub checkham {
44691    my ( $FileName, $msgText ) = @_;
44692    our $SpamHash;
44693    my ( $return, $reason );
44694    $msgText = &hash($msgText);
44695    if ( defined( $SpamHash{ $msgText } ) ) {
44696
44697        # we've found a message in the ham database that is the same as one in the corrected spam group
44698        my $fn = shift;
44699        &deletefile( $fn, "found in $correctedspam" );
44700        return 1;
44701
44702    }
44703    return 0;
44704}
44705
44706sub getrecontent {
44707    my ( $value, $name ) = @_;
44708    my $fromfile = 0;
44709    if ( $value =~ /^ *file: *(.+)/i ) {
44710
44711        # the option list is actually saved in a file.
44712        $fromfile = 1;
44713        my $fil = $1;
44714        $fil = "$base/$fil" if $fil !~ /^\Q$base\E/i;
44715        local $/;
44716        if ( open( my $File, '<', $fil ) ) {
44717            $value = <$File>;
44718
44719            # rb_clean off comments
44720            $value =~ s/#.*//g;
44721
44722            # replace newlines (and the whitespace that surrounds them) with a |(pipe character)
44723            $value =~ s/\s*\n\s*/|/g;
44724            close $File;
44725        }
44726        else { $value = q{}; }
44727    }
44728    $value =~ s/\|\|/\|/g;
44729    $value =~ s/\s*\|/\|/g;
44730    $value =~ s/\|\s*/\|/g;
44731    $value =~ s/\|\|+/\|/g;
44732    $value =~ s/^\s*\|?//;
44733    $value =~ s/\|?\s*$//;
44734    $value =~ s/\|$//;
44735    return $value;
44736} ## end sub getrecontent
44737
44738sub batv_remove_tag {
44739    my ($fh,$mailfrom,$store) = @_;
44740    if ($mailfrom =~ /^(prvs=[\da-zA-Z]+=)([^\r\n]*)/o) {
44741        $Con{$fh}->{$store} = $mailfrom if ($fh && $store);
44742        $mailfrom = $2;
44743    }
44744    return $mailfrom;
44745}
44746
44747sub whitelisted {
44748    return 0 if $KeepwhitelistedSpam;
44749    my $m = shift;
44750    my $curaddr;
44751    my %seen;
44752
44753
44754    $m =~ s/\n\r?\n.*//s;    # remove body
44755    while ( $m =~ 	/(?:from|sender|reply-to|errors-to|envelope-from|list-\w+):($HeaderValueRe)/igo) {
44756    	my $s = $1;
44757
44758    	if ($s !~ /($EmailAdrRe\@$EmailDomainRe)/io) {
44759    		next;
44760    	} else {
44761            $curaddr = batv_remove_tag($1);
44762
44763   		}
44764
44765        if ( exists $seen{ $curaddr } ) {
44766
44767            next;                #we already checked this address
44768        } else {
44769        	$seen{ $curaddr } = 1;
44770        }
44771
44772        if ( $Whitelist{ $curaddr } ) {
44773            my $reason = $curaddr;
44774            $reason =~ s/\s+$/ /g;
44775            $reason =~ s/[\r\n]/ /g;
44776            our $WhiteCount++;
44777            return ( " -- '$reason' is in Whitelist");
44778        }
44779        if ($wildcardUser) {
44780            my ( $mfdd, $alldd, $reason );
44781            $mfdd = $1 if $curaddr =~ /(\@.*)/;
44782            $alldd = "$wildcardUser$mfdd";
44783            if ( $Whitelist{ lc $alldd } ) {
44784                $reason = $curaddr;
44785                $reason =~ s/\s+$/ /g;
44786                $reason =~ s/[\r\n]/ /g;
44787                our $WhiteCount++;
44788                return ( " -- '$reason' is in Whitelist ($wildcardUser)");
44789            }
44790        }
44791    } ## end while ( $m =~ /($EmailAdrRe\@$EmailDomainRe)/igo)
44792    return 0;
44793} ## end sub whitelisted
44794
44795sub redlisted {
44796    my $m = shift;
44797    my (%seen);
44798	my $isasspheader;
44799
44800    if ( $DoNotCollectRedList) {
44801        $m =~ s/\n\r?\n.*//s;                            # remove body
44802        while ( $m =~ /($EmailAdrRe\@$EmailDomainRe)/igo ) {
44803            my $curaddr = lc($1);
44804
44805            #$curaddr = lc( $1 . $2 );
44806            if ( exists $seen{ $curaddr } ) {
44807                next;                                    #we already checked this address
44808            }
44809            else { $seen{ $curaddr } = 1; }
44810            if ( $Redlist{ $curaddr } ) {
44811                my $reason = $curaddr;
44812                $reason =~ s/\s+$/ /g;
44813                $reason =~ s/[\r\n]/ /g;
44814                our $RedCount++;
44815                return ( " -- '$reason' is in Redlist");
44816            }
44817        }
44818    }
44819    return 0;
44820} ## end sub redlisted
44821
44822sub deletefile {
44823    my ( $fn, $reason, $nolog, $notrashlist ) = @_;
44824
44825	if ( -e $fn ) {
44826        	if ( -w $fn || -W $fn ) {
44827            	&rb_printlog( "\nremoving " . $fn . q{ } . $reason );
44828            	if ($MaxKeepDeleted  && !$notrashlist ) {
44829    				$Trashlist{$fn}=time;
44830    			} else {
44831
44832            		unlink($fn);
44833
44834            	}
44835        	} else {
44836        		rb_printlog( "\ncannot delete " . $reason . " message " . $fn . ": file is not writable: $!" ) ;
44837			}
44838	} else {
44839    		rb_printlog( "\ncannot delete " . $reason . " message " . $fn . ": $!" ) if !$nolog;
44840    }
44841
44842
44843}
44844
44845
44846sub getfile {
44847    my ( $fn, $sub ) = @_;
44848    my $message;
44849    my $count;
44850    my $numreadchars;
44851	return if exists $Trashlist{$fn};
44852    open( my $file, '<', "$fn" ) || return;
44853
44854#	my $dtime=(stat($fn))[9]-time;
44855
44856#    return if $dtime > 0;
44857    # Maxbytes or 10000, whichever is less
44858    $numreadchars = $MaxBytes <= 10_000 ? $MaxBytes : 10_000;
44859    $count = read( $file, $message, $numreadchars );    # read characters into memory
44860    close $file;
44861    return if $sub->( $fn, $message );                  # have i read this before?
44862
44863    return $message;
44864}
44865
44866sub add {
44867    my ( $isspam, $fn, $factor, $sub ) = @_;
44868
44869    return if -d $fn;
44870    my ( $curHelo, $CurWord, $PrevWord, $sfac, $tfac );
44871    $PrevWord = $CurWord = q{};
44872    my $content = &getfile( $fn, $sub );
44873    return unless $content;
44874    if ( $content =~ /helo=(.*?)\)/i ) {
44875        $curHelo = lc($1);
44876        if ( $Helo{ $curHelo } ) { $Helo{ $curHelo }->[$isspam] += $factor; }
44877        else {    #it doesn't seem to exist. create it.
44878            $Helo{ $curHelo }->[$isspam] = $factor;
44879        }
44880    }
44881    my $OK;
44882    ($content,$OK) = &rb_clean($content);
44883    while ( $content =~ /([-\$A-Za-z0-9\'\.!\240-\377]{2,})/g ) {
44884        if  ($spamlogcount && $autoCorrectCorpus && !$isspam && $SpamWordCount && ($HamWordCount * $autoCorrectCorpus) > $SpamWordCount) {
44885
44886        return;
44887        }
44888        if ( length($1) > 20 || length($1) < 2 ) { next }
44889        $PrevWord = $CurWord;
44890
44891		$CurWord  = BayesWordrb_clean($1);
44892
44893        if ( !$PrevWord ) { next }  # We only want word pairs
44894        if ( length($CurWord) < 2 || length($PrevWord) < 2 ) { next }    # too short after rb_cleaning
44895
44896        # increment global weights, they are not really word counts
44897        if   ($isspam) { $SpamWordCount += $factor; }
44898        else           { $HamWordCount  += $factor; }
44899        if ( exists( $spam{ "$PrevWord $CurWord" } ) ) {
44900            ( $sfac, $tfac ) = split( q{ }, $spam{ "$PrevWord $CurWord" } );
44901        }
44902        else {
44903
44904            # the pair does not exist, create it
44905            $spam{ "$PrevWord $CurWord" } = "0 0";
44906            ( $sfac, $tfac ) = split( q{ }, $spam{ "$PrevWord $CurWord" } );
44907        }
44908        $sfac += $isspam ? $factor : 0;
44909        $tfac += $factor;
44910        $spam{ "$PrevWord $CurWord" } = "$sfac $tfac";
44911    } ## end while ( $content =~ /([-\$A-Za-z0-9\'\.!\240-\377]{2,})/g)
44912    return;
44913} ## end sub add
44914
44915sub Umlaute {
44916	my $string = shift;
44917	my %umlaute = ("ä" => "ae", "Ä" => "Ae", "ü" => "ue", "Ü" => "Ue", "ö" => "oe", "Ö" => "Oe", "ß" => "ss" );
44918	my $umlautkeys = join ("|", keys(%umlaute));
44919	$string =~ s/($umlautkeys)/$umlaute{$1}/g;
44920	return $string;
44921} ##
44922
44923sub BayesWordrb_clean {
44924    my $word = lc(shift);
44925    $word =~ s/#(?:[a-f0-9]{2})+/randcolor/go;
44926    $word =~ s/^#\d+/randdecnum/go;
44927
44928    $word =~ s/[_\[\]\~\@\%\$\&\{\}<>#(),.'";:=!?*+\/\\\-]+$//o;
44929    $word =~s/^[_\[\]\~\@\%\$\&\{\}<>#(),.'";:=!?*+\/\\\-]+//o;
44930    $word =~ s/!!!+/!!/go;
44931    $word =~ s/\*\*+/**/go;
44932    $word =~ s/--+/-/go;
44933    $word =~ s/__+/_/go;
44934    $word =~ s/[\d,.]{2,}/randnumber/go;
44935    $word =~ s/^[\d:\.\-+();:<>,!"'\/%]+(?:[ap]m)?$/randwildnum/o;    # ignore numbers , dates, times, versions ...
44936#    $word = &Umlaute($word);
44937
44938    return if length($word) > 20 or length($word) < 2;
44939    return $word;
44940}
44941# rb_clean up source email
44942sub rb_clean {
44943    my $m = shift;
44944
44945    my $msg = ref($m) ? $$m : $m;
44946    my $t = time + 15;     # max 15 seconds for this rb_cleaning
44947    my $body;
44948    my $header;
44949    my $undec = 1;
44950
44951
44952
44953    local $_= "\n". (($header) ? $header : $msg);
44954    my ($helo,$rcpt);
44955    if ($header) {
44956        ($helo)=/helo=([^)]+)\)/io;
44957        $helo=~s/(\w+)/ hlo $1 /go if length($helo) > 19; # if the helo string is long, break it up
44958        $rcpt="rcpt ".join(" rcpt ",/($EmailAdrRe\@$EmailDomainRe)/go);
44959        return "helo: $helo\n$rcpt\n",0 if (time > $t);
44960        # mark the subject
44961        $rcpt .= "\n".fixsub($1) if /\nsubject: (.*)/io;
44962        return "helo: $helo\n$rcpt\n",0 if (time > $t);
44963    }
44964
44965    # from now only do the body if possible
44966    local $_ = $body if $body;
44967
44968    # replace &#ddd encoding
44969    s/\&\#(\d{1,5})\;?/chr($1)/geo;
44970    s/\&\#x(\d{1,4})\;?/chr(hex($1))/geo;
44971    s/([^\\])?[%=]([0-9A-F]{2})/$1.chr(hex($2))/gieo; # replace url encoding
44972    return "helo: $helo\n$rcpt\n",0 if (time > $t);
44973
44974    if ($undec) {
44975      # replace base64 encoding
44976      s/\n([a-zA-Z0-9+\/=]{40,}\r?\n[a-zA-Z0-9+\/=\r\n]+)/base64decode($1)/gseo;
44977      return "helo: $helo\n$rcpt\n",0 if (time > $t);
44978
44979      # rb_clean up quoted-printable references
44980      s/(Subject: .*)=\r?\n/$1\n/o;
44981      return "helo: $helo\n$rcpt\n",0 if (time > $t);
44982      s/=\r?\n//go;
44983      return "helo: $helo\n$rcpt\n",0 if (time > $t);
44984      # strip out mime continuation
44985      s/.*---=_NextPart_.*\n//go;
44986      return "helo: $helo\n$rcpt\n",0 if (time > $t);
44987    }
44988
44989    # rb_clean up MIME quoted-printable line breakings
44990    s/=\r?\n//gos;
44991    return "helo: $helo\n$rcpt\n",0 if (time > $t);
44992
44993    # rb_clean up &nbsp; and &amp;
44994    s/&nbsp;?/ /gio; s/&amp;?/and/gio;
44995    return "helo: $helo\n$rcpt\n",0 if (time > $t);
44996    s/(\d),(\d)/$1$2/go;
44997    return "helo: $helo\n$rcpt\n",0 if (time > $t);
44998    s/\r//go; s/ *\n/\n/go;
44999    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45000    s/\n\n\n\n\n+/\nblines blines\n/go;
45001    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45002
45003    # rb_clean up html stuff
45004    s/<\s*script[^>]+>.*?<\s*\/\s*script\s*>/ jscripttag /igos;
45005    s/<\s*script[^>]+>/ jscripttag /igos;
45006    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45007    # remove style sheets
45008    s/<\s*style[^>]*>.*?<\s*\/\s*style\s*>//igso;
45009    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45010
45011#    while (s/(\w+)(<[^>]*>)((?:<[^>]*>)*\w+)/$2$1$3/go){return "helo: $helo\n$rcpt\n",0 if (time > $t);} # move html out of words
45012    s/<\s*(?:[biu]|strong)\s*>/ boldifytext /gio;
45013    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45014
45015    # remove some tags that are not informative
45016    s/<\s*\/?\s*(?:p|br|div|t[drh]|li|[uo]l|center)[^>]*>/\n/gios;
45017    s/<\s*\/?\s*(?:[biuo]|strong)\s*>//gio;
45018    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45019    s/<\s*\/?\s*(?:html|meta|head|body|span|table|font|col|map)[^>]*>//igos;
45020    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45021    s/(<\s*a\s[^>]*>)(.*?)(<\s*\/a\s*>)/$1.fixlinktext($2)/igseo;
45022    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45023
45024    # treat titles like subjects
45025    s/<\s*title[^>]*>(.*?)<\s*\/\s*title\s*>/fixsub($1)/igeos;
45026    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45027
45028    # remove html comments
45029    s/<\s*!.*?-->//gso; s/<\s*![^>]*>//go;
45030    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45031
45032    # look for random words
45033    s/[ a-z0-9][ghjklmnpqrstvwxz_]{2}[bcdfghjklmnpqrstvwxz_0-9]{3}\S*/ randword /gio;
45034    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45035
45036    # remove mime separators
45037    s/\n--.*?randword.*//go;
45038    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45039
45040    # look for linked images
45041    s/(<\s*a[^>]*>[^<]*<\s*img)/ linkedimage $1/giso;
45042    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45043    s/<[^>]*href\s*=\s*("[^"]*"|\S*)/fixhref($1)/isgeo;
45044    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45045    s/(?:ht|f)tps?:\/\/(\S*)/fixhref($1)/isgeo;
45046    return "helo: $helo\n$rcpt\n",0 if (time > $t);
45047    s/(\S+\@\S+\.\w{2,5})\b/fixhref($1)/geo;
45048    s/<?\s*img .{0,50}src\s*=\s*['"]([^'"]*)['"][^>]+>/$1/gois;
45049    s/["']\s*\/?s*>|target\s*=\s*['"]_blank['"]|<\s*\/|:\/\/ //go;
45050    s/ \d{2,} / 1234 /go;
45051
45052
45053    return ("helo: $helo\n$rcpt\n$_",1);
45054
45055}
45056
45057sub rb_cleanwhite {
45058    &rb_printlog("\n---rb_cleaning whitelist ($whitelistdb)---\n");
45059
45060    &rb_printlog( "whitelist entries older than " . $MaxWhitelistDays . " days (MaxWhitelistDays) will be removed\n" );
45061    my $calcTime = time - 24 * 3600 * $MaxWhitelistDays;
45062
45063    my $wlbefore = 0;
45064    my $wlafter = 0;
45065    if ( !( $whitelistdb =~ /mysql/ ) ) {
45066        if ( open( F, "<", "$whitelistdb" ) ) {
45067            binmode(F);
45068	    $_ = <F>; # ignore blank line at start of file
45069            my $nwhite;
45070            local $/ = "\n";
45071            $nwhite = "\n";
45072            while (<F>) {
45073                chomp;
45074                $wlbefore++;
45075                my ( $adr, $time ) = split( "\002", $_ );
45076                next if ( !$time || !$adr );
45077                $adr =~ s/^\'//g;
45078                $adr =~ s/^\"//g;
45079                $adr = batv_remove_tag($adr);
45080                next if ($adr =~ m/^'/);    #skip addresses with leading ' chars
45081                next if $calcTime > $time || length($adr) > $MaxWhitelistLength;
45082                $nwhite .= "$adr\002$time\n";
45083                $wlafter++;
45084            }
45085            close F;
45086
45087                unlink "$whitelistdb.bak";
45088                rename( $whitelistdb, "$whitelistdb.bak" );
45089                open( O, ">", "$whitelistdb" );
45090                binmode(O);
45091                print O $nwhite;
45092                close O;
45093
45094        }
45095    } ## end if ( !( $whitelistdb =~...
45096    else {
45097        my %Whitelist;
45098        my $WhitelistObject;
45099        eval {
45100            $WhitelistObject = tie %Whitelist, 'Tie::RDBM', "dbi:mysql:database=$mydb;host=$myhost",
45101                { user => "$myuser", password => "$mypassword", table => 'whitelist', create => 0 };
45102        };
45103        if ($EVAL_ERROR) {
45104            &rb_printlog("whitelist mysql error: $@");
45105            $CanUseTieRDBM = 0;
45106            $whitelistdb   = "whitelist";
45107        }
45108        $wlbefore = scalar keys %Whitelist;
45109        $wlafter  = $wlbefore;
45110        while ( my ( $key, $value ) = each %Whitelist ) {
45111
45112            #my $date1 = localtime($value); #debugging stuff
45113            #my $date2 = localtime($calcTime);
45114            #print "$key=$value\n";
45115            if ( $value < $calcTime || length($key) > $MaxWhitelistLength ) {
45116                if ( $Whitelist{ $key } ) {
45117                    delete $Whitelist{ $key };
45118                    $wlafter--;
45119                }
45120            }
45121        }
45122        $WhitelistObject->flush() if $WhitelistObject && $whitelistdb !~ /mysql/;
45123
45124        #untie %Whitelist;
45125    } ## end else [ if ( !( $whitelistdb =~...
45126    &rb_printlog( "whitelist before: " . commify($wlbefore) . "\n" );
45127    &rb_printlog( "whitelist after:  " . commify($wlafter) . "\n" );
45128    return;
45129} ## end sub rb_cleanwhite
45130
45131sub dayofweek {
45132
45133    # this is mercilessly hacked from John Von Essen's Date::Day
45134    my ( $d, $m, $y ) = $_[0] =~ /(\S+) +(\S+) +(\S+)/;
45135
45136    # data for DayOfWeek function
45137    my %Months = (
45138        'Jan', 1, 'Feb', 2, 'Mar', 3, 'Apr', 4,  'May', 5,  'Jun', 6,
45139        'Jul', 7, 'Aug', 8, 'Sep', 9, 'Oct', 10, 'Nov', 11, 'Dec', 12,
45140    );
45141    my %Month = ( 1, 0, 2, 3, 3, 2, 4, 5, 5, 0, 6, 3, 7, 5, 8, 1, 9, 4, 10, 6, 11, 2, 12, 4, );
45142    my %Weekday = ( 0, 'srdSUN', 1, 'srdMON', 2, 'srdTUE', 3, 'srdWED', 4, 'srdTHU', 5, 'srdFRI', 6, 'srdSAT', );
45143    $y += 2000;
45144    $m = $Months{ $m };
45145    if ( $m <= 2 ) { $y--; }
45146    my $wday = ( ( $d + $Month{ $m } + $y + ( int( $y / 4 ) ) - ( int( $y / 100 ) ) + ( int( $y / 400 ) ) ) % 7 );
45147    return $Weekday{ $wday };
45148}
45149sub fixhref     { my $t = shift; $t =~ s/(\w+)/ href $1 /g; return $t; }
45150sub fixlinktext { my $t = shift; $t =~ s/(\w+)/atxt $1/g;   return $t; }
45151
45152sub fixurl {
45153    my $a = shift;
45154    $a =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack('C',hex($1))/ge;
45155    return $a;
45156}
45157
45158sub fixsub {
45159    my $s = shift;
45160
45161    #print "$s=>";
45162    $s =~ s/ {3,}/ lotsaspaces /g;
45163    $s =~ s/(\S+)/ssub $1/g;
45164
45165    #print "$s\n";
45166    return "\n$s ssub";
45167}
45168
45169sub base64decode {
45170    my $str = shift;
45171    my $res = "\n\n";
45172    $str =~ tr|A-Za-z0-9+/||cd;
45173    $str =~ tr|A-Za-z0-9+/| -_|;
45174    while ( $str =~ /(.{1,60})/gs ) {
45175        my $len = chr( 32 + length($1) * 3 / 4 );
45176        $res .= unpack( "u", $len . $1 );
45177    }
45178    return $res;
45179}
45180
45181sub rb_printlog {
45182    return if $silentlog;
45183    my ( $text, $format ) = @_;
45184    if ( !$format ) {
45185        print "$text" unless $silent;
45186        print $RebuildLog "$text";
45187    }
45188    if ($format) {
45189        printf "$text", $format unless $silent;
45190        printf $RebuildLog "$text", $format;
45191    }
45192    return;
45193}
45194sub timestring {
45195    my ($time,$what) = @_;
45196    my @m = $time ? localtime($time) : localtime();
45197    my $tstr = $time ? scalar(localtime($time)) : scalar(localtime());
45198    my ($day,$month) = $tstr =~ /(...) (...)/;
45199    my $format = $LogDateFormat;
45200    if (lc $what eq 'd') {   # date only - remove time part from format
45201        $format =~ s/[^YMD]*(?:hh|mm|ss)[^YMD]*//g;
45202    } elsif (lc $what eq 't') { # time only - remove date part from format
45203        $format =~ s/[^hms]*(?:Y{2,4}|M{2,3}|D{2,3})[^hms]*//g;
45204    }
45205    $format =~ s/^[^YMDhms]//;
45206    $format =~ s/[^YMDhms]$//;
45207    $format =~ s/YYYY/sprintf("%04d",$m[5]+1900)/e;
45208    $format =~ s/YY/sprintf("%02d",$m[5]-100)/e;
45209    $format =~ s/MMM/$month/;
45210    $format =~ s/MM/sprintf("%02d",$m[4]+1)/e;
45211    $format =~ s/DDD/$day/e;
45212    $format =~ s/DD/sprintf("%02d",$m[3])/e;
45213    $format =~ s/hh/sprintf("%02d",$m[2])/e;
45214    $format =~ s/mm/sprintf("%02d",$m[1])/e;
45215    $format =~ s/ss/sprintf("%02d",$m[0])/e;
45216
45217    return $format;
45218}
45219
45220sub downloadGripConf {
45221
45222
45223    my $rc;
45224
45225    my $griplistUrl = "http://downloads.sourceforge.net/project/assp/griplist/griplist.conf";
45226    my $file     = "$base/griplist.conf";
45227
45228    $file		 = &fixPath($file);
45229
45230
45231    if ( !$CanUseLWP ) {
45232        &rb_printlog("$file download failed: LWP::Simple Perl module not available\n");
45233        return;
45234    }
45235
45236    if ( -e $file  ) {
45237        if ( !-r $file  ) {
45238            &rb_printlog( "$file download failed: $file  not readable!\n" );
45239            return;
45240        } elsif ( !-w $file  ) {
45241            &rb_printlog( "$file download failed: $file  not writable!\n" );
45242            return;
45243        }
45244    }
45245    else {
45246        if ( open( TEMPFILE, ">", $file ) ) {
45247            #we can create the file, this is good, now close the file and keep going.
45248            close TEMPFILE;
45249	    unlink($file);
45250        } else {
45251            &rb_printlog("$file download failed: Cannot create $file - $!\n" );
45252            return;
45253        }
45254    }
45255
45256    # Create LWP object
45257    use LWP::Simple qw(mirror is_success status_message $ua);
45258
45259    # Set useragent to Rebuild version
45260    $ua->agent(
45261        "rebuildspamdb/$VERSION ($^O; Perl/$]; LWP::Simple/$LWP::VERSION)");
45262    $ua->timeout(20);
45263    if ($proxyserver) {
45264        $ua->proxy( 'http', "http://" . $proxyserver );
45265
45266    } else {
45267
45268    }
45269
45270    # call LWP mirror command
45271    $rc = mirror( $griplistUrl, $file );
45272
45273    if ( $rc == 304 ) {
45274        # HTTP 304 not modified status returned
45275        return;
45276    } elsif ( !is_success($rc) ) {
45277        #download failed-error code output to logfile
45278        &rb_printlog("$file download failed: $rc " . status_message($rc). "\n" );
45279        return;
45280    } elsif ( is_success($rc) ) {
45281        # download complete
45282        my $filesize = -s "$file";
45283        &rb_printlog("$file download complete: $filesize bytes\n" );
45284        chmod 0644, "$file";
45285    }
45286    my $ret = $rc;
45287#    &rb_printlog("info: updated GRIPLIST upload and download URL's in $file\n") if $ret;
45288    $ret = 0;
45289    open my $GC , '<', $file or return 0;
45290    binmode $GC;
45291    while (<$GC>) {
45292        s/\r|\n//o;
45293        if (/^\s*(gripList(?:DownUrl|UpUrl|UpHost))\s*:\s*(.+)$/) {
45294            ${$1} = $2;
45295            $ret++;
45296        }
45297    }
45298    close
45299#    &rb_printlog("info: loaded GRIPLIST upload and download URL's from $file\n") if $ret;
45300    &rb_printlog("info: GRIPLIST config $file is possibly incomplete\n") if $ret < 3;
45301    $gripListDownUrl =~ s/\*HOST\*/$gripListUpHost/o;
45302    $gripListUpUrl  =~ s/\*HOST\*/$gripListUpHost/o;
45303    return $ret;
45304}
45305
45306
45307sub uploadgriplist {
45308    local $/ = "\n";
45309
45310    #&rb_printlog("Start building Griplist \n");
45311    open( my $FLogFile, '<', "$Log" ) || &rb_printlog("Unable to create Griplist.\n unable to open logfile '$Log': $!\n");
45312    my ( $date, $ip, $ipnet, %m, %ok, %locals, $match, $peeraddress, $hostaddress, $connect, $day, $gooddays, $st );
45313    my $buf;
45314	my $iday;
45315    #build list of the last 2 days
45316    my $time = Time::HiRes::time();
45317    my $dayoffset = $time % ( 24 * 3600 );
45318
45319    for ( my $i = 0 ; $i < 2 ; $i++ ) {
45320        $gooddays .= '|' if ( $i > 0 );
45321        $day = localtime( $time - $i * 24 * 3600 );
45322        $day =~ s/^... (...) +(\d+) (\S+) ..(..)/$1-$2-$4/;
45323        $gooddays .= $day;
45324    }
45325    if ($LogDateFormat !~ /MMM-DD-YY/)  {
45326	$gooddays .= '|';
45327    for ( my $i = 0 ; $i < 2 ; $i++ ) {
45328        $gooddays .= '|' if ( $i > 0 );
45329        $day = &timestring( $time - $i * 24 * 3600 , 'd');
45330        $gooddays .= $day;
45331    }}
45332
45333
45334    undef $day;
45335    %locals = ( '127', 1, '10', 1, '192.168', 1, '169.254', 1, '::1', 1, 'fe80:', 1 );    #RFC 1918, IPv6
45336    for ( 16 .. 31 ) { $locals{ "172.$_" } = 1 }                                          #RFC 1918
45337
45338    while (<$FLogFile>) {
45339        next unless ( $date, $ip, $match ) = /($gooddays) .*\s([0-9a-f\.:]+) .* to: \S+ (.*)/io;
45340	$ipnet = $ip;
45341	if ($ipnet =~ /:.*:/) {
45342		$ipnet =~ s/:.*/:/ if ($ipnet !~ /^:/);
45343        	next if $locals{ $ipnet };		# ignore local IP ranges
45344	}
45345	else {
45346		$ipnet =~ s/^(\d+\.\d+)\..*/$1/;
45347        	next if $locals{ $ipnet };		# ignore local IP ranges
45348		$ipnet =~ s/^(\d+)\..*/$1/;
45349        	next if $locals{ $ipnet };		# ignore local IP ranges
45350	}
45351	$ipnet = $ip;
45352	if ($ipnet =~ /:.*:/) {
45353		$ipnet =~ s/^([0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]*:).*/$1/i;	# yes: "+++*" so as to allow "2001:123:456::"
45354	}
45355	else {
45356		$ipnet =~ s/(\d+)\.(\d+)\.(\d+)\.(\d+)/$1.$2.$3/;
45357	}
45358
45359        if (m/(\[Local]|\[MessageOK]|\[RWL]|\[whitelisted])|\[NoProcessing]/i) {
45360
45361            #Good IP
45362            $m{ $ipnet }  += 1;
45363            $ok{ $ipnet } += 1;
45364            next;
45365        }
45366        if (m/(Connection idle for|\[Backscatter]|\[Bayesian]|\[BlackDomain]|\[BlackHELO]|\[BombBlack]|\[BombData]|\[BombHeader]|\[BombRaw]|\[BombScript]|\[BombSender]|\[Collect]|\[Connection]|\[DNSBL]|\[DenyIP]|\[DenyStrict]|\[Extreme]|\[ForgedHELO]|\[ForgedLocalSender]|\[FromMissing]|\[IPfrequency]|\[IPperDomain]|\[InvalidHELO]|\[Malformedaddress]|\[MaxErrors]|\[MessageScore]|\[MissingMX]|\[MsgID]|\[OversizedHeader]|\[PTRinvalid]|\[PTRmissing]|\[PenaltyBox]|\[Penalty]|\[RelayAttempt]|\[SpoofedSender]|\[Trap]|\[URIBL]|\[VIRUS]|\[ValidHELO]|spam found|\[blocked\])/i) {
45367
45368            #Bad IP
45369            $m{ $ipnet }  += 1;
45370            $ok{ $ipnet } += 0;
45371            next;
45372        }
45373    }
45374    close $FLogFile;
45375    if ( !%m ) {
45376        &rb_printlog( "Skipping Griplist upload. Not enough messages processed.\n");
45377        return;
45378    }
45379    &rb_printlog("Preparing binary Griplist upload...");
45380    my $n6 = 0;
45381    my $n4 = 0;
45382    my ($buf6, $buf4);
45383    foreach (keys %m) {
45384        next if (!$m{$_});
45385        if ($_ =~ /:/) {
45386            my $ip = $_;
45387            $ip =~ s/([0-9a-f]*):/0000$1:/gi;
45388            $ip =~ s/0*([0-9a-f]{4}):/$1:/gi;
45389            $buf6 .= pack("H4H4H4H4", split(/:/, $ip));
45390            $buf6 .= pack("C", (1 - $ok{$_} / $m{$_}) * 255);
45391            $n6++;
45392        } else {
45393            $buf4 .= pack("C3C", split(/\./, $_), (1 - $ok{$_} / $m{$_}) * 255);
45394            $n4++;
45395        }
45396    }
45397    $st = pack("N2", $n6 / 2**32, $n6);
45398    $st .= pack("N", $n4);
45399    $st .= $buf6 . $buf4;
45400    &rb_printlog(" done\n");
45401    if ($proxyserver) {
45402        &rb_printlog("Uploading Griplist via Proxy: $proxyserver\n");
45403
45404        my $user = $proxyuser ? "$proxyuser:$proxypass\@": '';
45405        $peeraddress = $user . $proxyserver;
45406        $hostaddress = $proxyserver;
45407        $connect     = "POST $gripListUpUrl HTTP/1.0";
45408    } else {
45409        &rb_printlog("Uploading Griplist via Direct Connection\n");
45410        $peeraddress = $gripListUpHost . ':80';
45411        $hostaddress = $gripListUpHost;
45412        my ($url) = $gripListUpUrl =~ /http:\/\/[^\/](\/.+)/oi;
45413        $connect     = <<"EOF";
45414POST $url HTTP/1.1
45415User-Agent: rebuildspamdb/$VERSION ($^O; Perl/$];)
45416Host: $gripListUpHost
45417EOF
45418    }
45419    my $socket = new IO::Socket::INET( Proto => 'tcp', Peeraddr => $peeraddress, Timeout => 2 );
45420    if ( defined $socket ) {
45421        my $len = length($st);
45422        $connect .= <<"EOF";
45423Content-Type: application/x-www-form-urlencoded
45424Content-Length: $len
45425
45426$st
45427EOF
45428        print { $socket } $connect;
45429        $socket->sysread($buf, 4096);
45430        $socket->close;
45431        &rb_printlog("Submitted $len bytes: $n6 IPv6 addresses, $n4 IPv4 addresses\n");
45432    }
45433    else {
45434        &rb_printlog("unable to connect to assp.sourceforge.net to upload griplist\n");
45435        return;
45436    }
45437    return;
45438} ## end sub uploadgriplist
45439
45440sub downloadgriplist {
45441    &rb_printlog("Griplist download disabled\n")  if $noGriplist;
45442    return if $noGriplist;
45443    &rb_printlog("Griplist file not configured\n")  if (!$griplist);
45444    return if (!$griplist);
45445
45446    my $rc;
45447
45448    my $gripListUrl = "http://assp.sourceforge.net/cgi-bin/assp_griplist?binary";
45449    my $gripFile    = "$base/$griplist";
45450
45451    ## let's check if we really need to
45452    if (-e $gripFile) {
45453        my @s     = stat($gripFile);
45454        my $mtime = $s[9];
45455        if (time - $mtime < 8*60*60) {
45456            &rb_printlog("Griplist download failed: last download too recent\n");
45457            return;
45458        }
45459    }
45460
45461    # check for previous download timestamp, so we can do delta now
45462    my %lastdownload;
45463    $lastdownload{full} = 0;
45464    $lastdownload{fullUTC} = 0;
45465    $lastdownload{delta} = 0;
45466    $lastdownload{deltaUTC} = 0;
45467    my $delta = "";
45468    if (open(UTC, "$gripFile.utc")) {
45469        local $/;
45470        my $buf = <UTC>;
45471        close(UTC);
45472        chop($buf);
45473        if ($buf =~ /full/ && $buf =~ /delta/) {
45474            %lastdownload = split(/\s+|\n/, $buf);
45475        } else {
45476            $lastdownload{delta} = $buf;
45477        }
45478        if (! ($DoFullGripDownload && time - $lastdownload{fullUTC} > $DoFullGripDownload*24*60*60)) {
45479            my $lasttime;
45480            $lasttime = $lastdownload{full};
45481            $lasttime = $lastdownload{delta} if ($lastdownload{delta} > $lastdownload{full});
45482            $gripListUrl .= "&delta=$lasttime";
45483            $delta = " (delta)";
45484        }
45485    }
45486
45487    if (!$CanUseLWP) {
45488        &rb_printlog("Griplist download failed: LWP::Simple Perl module not available\n");
45489        return;
45490    }
45491
45492    if (open(TEMPFILE, ">", "$gripFile.tmp")) {
45493        #we can create the file, this is good, now close the file and keep going.
45494        close TEMPFILE;
45495        unlink("$gripFile.tmp");
45496    } else {
45497        &rb_printlog("Griplist download failed: Cannot create $gripFile.tmp\n");
45498        return;
45499    }
45500
45501    # Create LWP ogject
45502    use LWP::Simple qw(mirror is_success status_message $ua);
45503
45504    # Set useragent to Rebuild version
45505    $ua->agent("rebuildspamdb/$VERSION ($^O; Perl/$]; LWP::Simple/$LWP::VERSION)");
45506    $ua->timeout(20);
45507    if ($proxyserver) {
45508        $ua->proxy('http', "http://" . $proxyserver);
45509        &rb_printlog("Downloading Griplist$delta via HTTP proxy: $proxyserver\n");
45510    } else {
45511        &rb_printlog("Downloading Griplist$delta via direct HTTP connection\n");
45512    }
45513
45514    # call LWP mirror command
45515    my $dltime = time;
45516    $rc = mirror($gripListDownUrl, "$gripFile.tmp");
45517
45518    if ($rc == 304) {
45519        # HTTP 304 not modified status returned
45520        # can't happen - we ALWAYS get new data
45521        unlink("$gripFile.tmp");
45522        return;
45523    } elsif (!is_success($rc)) {
45524        # download failed-error code output to logfile
45525        # &rb_printlog("Griplist download failed: $rc " . status_message($rc). "\n");
45526        unlink("$gripFile.tmp");
45527        return;
45528    }
45529
45530    # download complete
45531    my $filesize = -s "$gripFile.tmp";
45532    &rb_printlog("Griplist download complete: binary download $filesize bytes\n");
45533
45534    # enough data?
45535    if ($filesize < 12) {
45536        &rb_printlog("Griplist download error: grip data too small\n");
45537        unlink("$gripFile.tmp");
45538        return;
45539    }
45540
45541    # record download time so we can do delta next time
45542    unlink("$gripFile.utc");
45543    if (open(UTC, ">$gripFile.utc")) {
45544        my ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = gmtime($dltime);
45545        $year += 1900;
45546        $mon += 1;
45547        if (! $delta) {
45548            $lastdownload{full} = sprintf "%04d%02d%02d%02d%02d%02d", $year, $mon, $day, $hour, $min, $sec;
45549            $lastdownload{fullUTC} = $dltime;
45550        } else {
45551            $lastdownload{delta} = sprintf "%04d%02d%02d%02d%02d%02d", $year, $mon, $day, $hour, $min, $sec;
45552            $lastdownload{deltaUTC} = $dltime;
45553        }
45554        printf UTC "full\t%s\n", $lastdownload{full};
45555        printf UTC "fullUTC\t%s\n", $lastdownload{fullUTC};
45556        printf UTC "delta\t%s\n", $lastdownload{delta};
45557        printf UTC "deltaUTC\t%s\n", $lastdownload{deltaUTC};
45558        close(UTC);
45559    }
45560
45561    # if we did a delta download, read in previous data so we can merge
45562    my @binFiles;
45563    push(@binFiles, "$gripFile.bin") if ($gripListUrl =~ /delta=/);
45564    push(@binFiles, "$gripFile.tmp");
45565
45566    # convert binary download form to text form used by ASSP
45567    my $buf;
45568    my %grip;
45569    my $action = "read";
45570    foreach my $binF (@binFiles) {
45571        my $binSize = -s $binF;
45572        open(BIN, $binF);
45573        binmode(BIN);
45574        read(BIN, $buf, $binSize);
45575        close(BIN);
45576
45577    # IPv6 count
45578    	my ($n6h, $n6l) = unpack("N2", $buf);
45579    	my $n6 = $n6h * 2**32 + $n6l;
45580
45581    # IPv4 count
45582    	my $n4;
45583    	eval { $n4 = unpack("x[N2] N", $buf); };
45584
45585
45586    # decode IPv6 data
45587    	my $x6 = 0;
45588    	eval {
45589    	for (my $i = 0; $i < $n6; $i++) {
45590        my ($bip, $grey) = unpack("x[N2] x[N] x$x6 a8 C", $buf);
45591        my $ip = join(":", unpack("H4H4H4H4", $bip)) . ":";
45592        $ip =~ s/:0+([0-9a-f])/:$1/gio;
45593        $ip =~ s/:0:$/::/o;
45594
45595        #                $grip{$ip} = $grey / 255;
45596        #                $gripdelta{$ip} = $grey / 255 if $deltayonly;
45597        $x6 += 9;
45598    	}
45599    	};
45600
45601    # decode IPv4 data
45602    	my $x4 = 0;
45603    	for (my $i = 0; $i < $n4; $i++) {
45604        my ($bip, $grey) = unpack("x[N2] x[N] x$x6 x$x4 a3 C", $buf);
45605        my $ip = join(".", unpack("C3", $bip));
45606        $grip{$ip} = $grey / 255;
45607
45608        $x4 += 4;
45609    }
45610        &rb_printlog("Griplist binary $action OK: $binF, $n6 IPv6 addresses, $n4 IPv4 addresses\n");
45611        $action = "merge";
45612    }
45613
45614    # remove download file
45615    unlink("$gripFile.tmp");
45616
45617    # output binary version, so we can do a delta next time
45618    &rb_printlog("Writing merged Griplist binary...");
45619    my $buf;
45620    my $n6 = 0;
45621    my $n4 = 0;
45622    my ($buf6, $buf4);
45623    foreach my $ip (keys %grip) {
45624        if ($ip =~ /:/) {
45625            my $ip2 = $ip;
45626            $ip2 =~ s/([0-9a-f]*):/0000$1:/gi;
45627            $ip2 =~ s/0*([0-9a-f]{4}):/$1:/gi;
45628            $buf6 .= pack("H4H4H4H4", split(/:/, $ip2));
45629            $buf6 .= pack("C", int($grip{$ip} * 255));
45630            $n6++;
45631        } else {
45632            $buf4 .= pack("C3C", split(/\./, $ip), int($grip{$ip} * 255));
45633            $n4++;
45634        }
45635    }
45636    $buf = pack("N2", $n6/2**32, $n6);
45637    $buf .= pack("N", $n4);
45638    $buf .= $buf6 . $buf4;
45639    unlink("$gripFile.bin");
45640    open (BIN, ">$gripFile.bin");
45641    binmode(BIN);
45642    print BIN $buf;
45643    close(BIN);
45644    chmod 0644, "$gripFile.bin";
45645    &rb_printlog(" done\n");
45646
45647    # output text version
45648    &rb_printlog("Writing merged Griplist text...");
45649    unlink("$gripFile");
45650    open (TEXT, ">$gripFile");
45651    binmode(TEXT);
45652    print TEXT "\n";
45653    foreach my $ip (sort keys %grip) {
45654
45655        printf TEXT "$ip\002%.2f\n", $grip{$ip};
45656    }
45657    close(TEXT);
45658    chmod 0644, "$gripFile";
45659    &rb_printlog(" done\n");
45660
45661    &rb_printlog("Griplist writing complete: $n6 IPv6 addresses, $n4 IPv4 addresses\n\n");
45662}
45663
45664
45665sub downloaddroplist {
45666
45667	&rb_printlog("Droplist download disabled\n\n")  if !$DoDropList;
45668	return if !$DoDropList;
45669    my $rc;
45670
45671    my $droplistUrl = "http://www.spamhaus.org/drop/drop.lasso";
45672    my $dropFile     = "$base/$DropList";
45673
45674    $dropFile		 = &fixPath($dropFile);
45675
45676    # let's check if we really need to
45677    if (-e $dropFile) {
45678        my @s     = stat($dropFile);
45679        my $mtime = $s[9];
45680        my $random = int(rand(144-72)+72)+1;
45681        if (time - $mtime < $random*60*60) {
45682
45683            &rb_printlog("Droplist download skipped: last download too recent\n");
45684            return;
45685        }
45686    }
45687
45688    if ( !$CanUseLWP ) {
45689        &rb_printlog("Droplist download failed: LWP::Simple Perl module not available\n");
45690        return;
45691    }
45692
45693    if ( -e $dropFile  ) {
45694        if ( !-r $dropFile  ) {
45695            &rb_printlog( "Droplist download failed: $dropFile  not readable!\n" );
45696            return;
45697        } elsif ( !-w $dropFile  ) {
45698            &rb_printlog( "Droplist download failed: $dropFile  not writable!\n" );
45699            return;
45700        }
45701    }
45702    else {
45703        if ( open( TEMPFILE, ">", $dropFile ) ) {
45704            #we can create the file, this is good, now close the file and keep going.
45705            close TEMPFILE;
45706	    unlink($dropFile);
45707        } else {
45708            &rb_printlog("Droplist download failed: Cannot create $dropFile \n" );
45709            return;
45710        }
45711    }
45712
45713    # Create LWP ogject
45714    use LWP::Simple qw(mirror is_success status_message $ua);
45715
45716    # Set useragent to Rebuild version
45717    $ua->agent(
45718        "rebuildspamdb/$VERSION ($^O; Perl/$]; LWP::Simple/$LWP::VERSION)");
45719    $ua->timeout(20);
45720    if ($proxyserver) {
45721        $ua->proxy( 'http', "http://" . $proxyserver );
45722        &rb_printlog("Downloading $dropFile via HTTP proxy: $proxyserver\n" );
45723    } else {
45724        &rb_printlog("Downloading $dropFile via direct HTTP connection\n" );
45725    }
45726
45727    # call LWP mirror command
45728    $rc = mirror( $droplistUrl, $dropFile );
45729
45730    if ( $rc == 304 ) {
45731        # HTTP 304 not modified status returned
45732        return;
45733    } elsif ( !is_success($rc) ) {
45734        #download failed-error code output to logfile
45735        &rb_printlog("$dropFile download failed: $rc " . status_message($rc). "\n" );
45736        return;
45737    } elsif ( is_success($rc) ) {
45738        # download complete
45739        my $filesize = -s "$dropFile";
45740        &rb_printlog("$dropFile download complete: $filesize bytes\n" );
45741        chmod 0644, "$dropFile";
45742    }
45743}
45744
45745sub compileregex {
45746    use re 'eval';
45747    my ( $name, $contents, $REname ) = @_;
45748    $contents = getrecontent( $contents, $name );
45749    $contents ||= '^(?!)';    # regexp that never matches
45750
45751    # trim long matches to 32 chars including '...' at the end
45752    eval { $$REname = qr/(?si)$contents/ };
45753    if ($EVAL_ERROR) { print "regular expression error in '$contents' for $name: $@\n"; }
45754    return q{};
45755}
45756
45757sub optionList {
45758
45759    # this converts a | separated list into a RE
45760    my ( $d, $configname ) = @_;
45761    $d = getrecontent( $d, $configname );
45762    $d =~ s/([\.\[\]\-\(\)\*\+\\])/\\$1/g;
45763    return $d;
45764}
45765
45766sub fixPath {
45767    my ($path) = @_;
45768    my $len = length($path);
45769    if   ( !substr( $path, ( $len - 1 ), 1 ) eq q{/} ) { return $path . q{/}; }
45770    else                                               { return $path; }
45771    return;
45772}
45773
45774sub repair {
45775    $/ = "\n";
45776
45777    # mxa ptr rbl spf uribl white black
45778    my $pbdb = "$base/$pbdbfile";
45779    my ( @files, %w );
45780    my ( $k,     $v );
45781    if ( !( $pbdbfile =~ /mysql/ ) ) {
45782        foreach ( glob("$pbdb.*.db") ) { push( @files, $_ ); }
45783    }
45784    if ( !( $whitelistdb =~ /mysql/ ) ) { push( @files, $whitelistdb ); }
45785    if ( !( $redlistdb   =~ /mysql/ ) ) { push( @files, $redlistdb ); }
45786    foreach my $f (@files) {
45787        if ( !-e $f ) { next }
45788        open( my $curfile, "<", $f );
45789
45790        #<$curfile>;
45791        while (<$curfile>) {
45792            ( $k, $v ) = split( /[\001\002\n]/, $_ );
45793            if ( $k eq q{} || $v eq q{} ) { next }
45794
45795            #print "$k=$v\n";
45796            $w{ $k } = $v;
45797        }
45798        close $curfile;
45799        open( my $newfile, ">", "$f.new" );
45800        binmode $newfile;
45801        print { $newfile } "\n";
45802        for ( sort keys %w ) { print { $newfile } "$_\002$w{$_}\n"; }
45803        close $newfile;
45804        rename( $f, "$f.bak" );
45805        rename( "$f.new", $f );
45806        undef %w;
45807    }
45808    return;
45809} ## end sub repair
45810
45811
45812
45813sub sendNotification {
45814    my ($from,$to,$sub,$body,$file) = @_;
45815    my $text;
45816    return unless $to;
45817    return unless $resendmail;
45818    my $date=$UseLocalTime ? localtime() : gmtime();
45819    my $tz=$UseLocalTime ? tzStr() : '+0000';
45820    $date=~s/(\w+) +(\w+) +(\d+) +(\S+) +(\d+)/$1, $3 $2 $5 $4/;
45821    $text = "Date: $date $tz\r\n";
45822    $text .= "X-Assp-Notification: YES\r\n";
45823  	$text .= "From: <$from>\r\nTo:" if $from !~ /\</;
45824    $text .= "From: $from\r\nTo:" if $from =~ /\</;
45825
45826    foreach (split(/\|/, $to)) {
45827        $text .= " <$_>,";
45828    }
45829    chop $text;
45830    $text .= "\r\n";
45831    $text .= "Subject: $sub\r\n";
45832    $text .= "Content-Type: text/plain;	charset=\"ISO-8859-1\"\r\n";
45833    $text .= "Content-Transfer-Encoding: 7bit\r\n";
45834    my $msgid = int(rand(1000000));
45835    $text .= "Message-ID: a$msgid\@$myName\r\n";
45836    $text = &headerWrap($text);
45837    $text .= "\r\n";           # end header
45838    foreach (split(/\r?\n/,$body)) {
45839        $text .= "$_\r\n";
45840    }
45841
45842    my $f;
45843    if ($file && -e $file && open($f,"<",$file)) {
45844
45845        while (<$f>) {
45846             s/\r?\n//g;
45847             $text .= "$_\r\n";
45848
45849        }
45850        close $f;
45851    }
45852
45853    $text .= ".\r\n";
45854    $text =~ tr/\x80-\xFF/_/;   # 7bit only
45855    my $rfile = "$base/$resendmail/n$msgid$maillogExt";
45856	-d "$base/$resendmail" or mkdir "$base/$resendmail", 0777;
45857    if (open($f,">",$rfile)) {
45858        binmode $f;
45859        print $f $text;
45860        close $f;
45861        &rb_printlog( "write notify message to $rfile\n" );
45862    } else {
45863
45864        &rb_printlog( "error: unable to write notify message to $rfile - $!\n" );
45865    }
45866
45867}
45868sub tzStr {
45869
45870    # calculate the time difference in minutes
45871    my $minoffset =
45872      ( Time::Local::timelocal( localtime() ) -
45873          Time::Local::timelocal( gmtime() ) ) / 60;
45874
45875   # translate it to "hour-format", so that 90 will be 130, and -90 will be -130
45876    my $sign = $minoffset < 0 ? -1 : +1;
45877    $minoffset = abs($minoffset) + 0.5;
45878    my $tzoffset = 0;
45879    $tzoffset = $sign * ( int( $minoffset / 60 ) * 100 + ( $minoffset % 60 ) )
45880      if $minoffset;
45881
45882    # apply final formatting, including +/- sign and 4 digits
45883    return sprintf( "%+05d", $tzoffset );
45884}
45885# wrap long headers
45886sub headerWrap {
45887    my $header = shift;
45888    $header =~
45889s/(?:([^\r\n]{60,75}?;)|([^\r\n]{60,75}) ) {0,5}(?=[^\r\n]{10,})/$1$2\r\n\t/g;
45890
45891    return $header;
45892}
45893sub rb_cleanUpFiles {
45894    my ($folder, $filter, $filetime) = @_;
45895
45896    my $textfilter = " (*$filter)" if $filter;
45897    my @files;
45898    my $file;
45899    my $count;
45900    my $dir = &fixPath($folder);
45901    $dir =~ s/\\/\//g;
45902    return unless -e $dir;
45903    &rb_printlog( "starting rb_cleanup old files$textfilter for folder $dir\n" );
45904
45905    opendir(my $DIR,"$dir");
45906    @files = readdir($DIR);
45907    close $DIR;
45908	my $fldrpath        = $dir . "/*";
45909
45910    my $filecount = &countfiles($fldrpath);
45911    foreach $file (@files) {
45912        next if $file eq '.';
45913        next if $file eq '..';
45914        next unless $file =~ /$maillogExt$/i or $file =~ /\.rpt$/i;
45915        next if ($filter && $file !~ /$filter$/i);
45916        next if ($filter && $file =~ /^$filter$/i);
45917        $file = "$dir/$file";
45918        next if -d $file;
45919        next unless -w $file;
45920        my $dtime=(stat($file))[9]-time;
45921        if (($dtime < $filetime * -1) or ($dtime > 0 && $dtime < $MaxKeepDeleted - $filetime)) {
45922            $count++;
45923
45924        }
45925    }
45926
45927    my $filecountafter = &countfiles($fldrpath);
45928    &rb_printlog( "folder $dir before: $filecount\n" ) ;
45929	&rb_printlog( "folder $dir deleted: $count\n" ) if $count;
45930	&rb_printlog( "folder $dir after: $filecountafter\n\n" ) ;
45931	$correctedspamcount = $filecountafter if $dir =~ 'errors\/spam';
45932	$correctednotspamcount = $filecountafter if $dir =~ 'errors\/notspam';
45933
45934}
45935
45936sub rb_cleanUpMaxFiles {
45937    my $folder = shift;
45938    my $percent = shift;
45939
45940    my @files;
45941    my $file;
45942    my $count;
45943    my $info;
45944    my $dir = ($folder !~ /\Q$base\E/i) ? "$base/$folder" : $folder ;
45945    my $maxfiles = $MaxFiles;
45946    my $importmaxfiles = $MaxFiles;
45947
45948
45949    my $adiff = abs ($correctedspamcount - $correctednotspamcount);
45950    $maxfiles = $MaxFiles + $adiff if $dir =~ /assp\/spam/ && $correctednotspamcount > $correctedspamcount;
45951    $maxfiles = $MaxFiles + $adiff if $dir =~ /assp\/notspam/ && $correctedspamcount > $correctednotspamcount;
45952
45953    $dir =~ s/\\/\//g;
45954    return unless -e $dir;
45955
45956
45957    opendir(my $DIR,"$dir");
45958    @files = readdir($DIR);
45959    close $DIR;
45960    my $filecount = @files - 2;
45961
45962    rb_printlog("rb_cleaning $dir skipped - filecount: $filecount < maxfiles: $maxfiles\n") if $filecount <= $maxfiles;
45963    return $info if  $filecount <= $maxfiles;
45964
45965    my %filelist = ();
45966    while (@files ) {
45967        $file = shift @files;
45968        next if $file eq '.';
45969        next if $file eq '..';
45970        $file = "$dir/$file";
45971        if (-d $file) {
45972            $filecount--;
45973            next;
45974        }
45975        my $ft = (stat($file))[9];
45976        $ft = $ft - (60 * 24 * 3600) if $ft > time;
45977        while (exists $filelist{$ft}) {
45978            $ft++;
45979        }
45980        $filelist{$ft} = $file;
45981        $count++;
45982
45983    }
45984    return $info if $filecount <= $maxfiles;
45985
45986
45987    rb_printlog("\nstarting rb_cleaning $dir - delete files from $dir - old filecount: $filecount: \n");
45988    my $toFilenumber;
45989    my $filenum;
45990    my $time = time - ($minimumdays * 24 * 3600);   # two weeks ago
45991	my $savecount;
45992
45993    $filenum = $maxfiles - $filecount;
45994    $toFilenumber = $maxfiles;
45995
45996    $count = 0;
45997
45998    foreach my $filetime (sort keys %filelist) {
45999        last if $filecount-- < $toFilenumber;
46000
46001        unlink "$filelist{$filetime}";
46002        $count++;
46003
46004    }
46005
46006    opendir(my $DIR,"$dir");
46007    @files = readdir($DIR);
46008    close $DIR;
46009    my $newfilecount = @files - 2;
46010
46011
46012    rb_printlog("finished rb_cleaning $dir - new filecount: $newfilecount\n") ;
46013
46014    return $info;
46015}
46016
46017
46018sub rb_cleanUpCollection {
46019
46020
46021
46022
46023	my $age = $MaxNoBayesFileAge * 3600 * 24;
46024    my @dirs = ('incomingOkMail','discarded','viruslog');
46025    my $dir;
46026
46027
46028    	&rb_printlog( "\n--- rb_cleaning NoBayesian folders ---\n" );
46029    	&rb_printlog( "entries older than $MaxNoBayesFileAge days will be removed\n" ) if $MaxNoBayesFileAge;
46030
46031    foreach my $dir (@dirs) {
46032        if ($age) {
46033            &rb_cleanUpFiles(${$dir},'',$age) if ${$dir};
46034        }
46035    }
46036
46037    $age = $MaxCorrectedDays * 3600 * 24;
46038    @dirs = ('correctedspam','correctednotspam');
46039
46040    &rb_printlog( "\n--- rb_cleaning corrected (errors) spam/notspam folders ---\n" );
46041    &rb_printlog( "entries older than $MaxCorrectedDays days will be removed\n" ) if $MaxCorrectedDays;
46042
46043    foreach my $dir (@dirs) {
46044        if ($age) {
46045            &rb_cleanUpFiles(${$dir},'',$age) if ${$dir};
46046        } else {
46047            &rb_cleanUpMaxFiles(${$dir}) if ${$dir};
46048        }
46049    }
46050
46051    return unless $maintbayescollection;
46052    $age = 0;
46053    @dirs = ('spamlog','notspamlog');
46054
46055    &rb_printlog( "\n--- rb_cleaning Bayesian folders ---\n" );
46056    &rb_printlog( "entries older than $MaxBayesFileAge days will be removed\n" ) if $MaxBayesFileAge;
46057
46058    foreach my $dir (@dirs) {
46059        if ($age) {
46060            &rb_cleanUpFiles(${$dir},'',$age) if ${$dir};
46061        } else {
46062            &rb_cleanUpMaxFiles(${$dir}) if ${$dir};
46063        }
46064    }
46065}
46066
46067sub rb_cleanTrashlist {
46068    my $files_before = my $files_deleted = 0;
46069    my $t = time;
46070    my $mcount;
46071
46072    while ( my ( $k, $v ) = each(%Trashlist) ) {
46073		if (!-e $k) {
46074			delete $Trashlist{$k};
46075			$files_deleted++;
46076			next;
46077		}
46078        my $ct = $v;
46079        $files_before++;
46080
46081        if (!$MaxKeepDeleted or ( $t - $ct >= $MaxKeepDeleted * 3600 * 24)
46082            )
46083        {
46084        	unlink $k;
46085            delete $Trashlist{$k};
46086            $files_deleted++;
46087        }
46088    }
46089    &rb_printlog(
46090"\nTrashlist: rb_cleaning finished; before=$files_before, deleted=$files_deleted\n"
46091    );
46092
46093}
46094
46095sub getUidGid {
46096    my ( $uname, $gname ) = @_;
46097
46098    my $rname = "root";
46099    eval('getgrnam($rname);getpwnam($rname);');
46100    if ($@) {
46101
46102        # windows pukes "unimplemented" for these -- just skip it
46103        rb_printlog(
46104"warning:   uname and/or gname are set ($uname,$gname) but getgrnam / getpwnam give errors: $@"
46105        );
46106        return;
46107    }
46108    my $gid;
46109    if ($gname) {
46110        $gid = getgrnam($gname);
46111        if ( defined $gid ) {
46112        } else {
46113            my $msg =
46114"could not find gid for group '$gname' -- not switching effective gid ";
46115            rb_printlog( $msg );
46116            return;
46117        }
46118    }
46119    my $uid;
46120    if ($uname) {
46121        $uid = getpwnam($uname);
46122        if ( defined $uid ) {
46123        } else {
46124            my $msg =
46125"could not find uid for user '$uname' -- not switching effective uid ";
46126            rb_printlog( $msg );
46127            return;
46128        }
46129    }
46130    ( $uid, $gid );
46131}
46132
46133sub mlog {
46134}
46135
46136#####################################################################################
46137#                orderedtie
46138{
46139
46140    package orderedtie;
46141
46142    # This is a tied value that caches lookups from a sorted file; \n separates records,
46143    # \002 separates the key from the value. After OrderedTieHashSize lookups the cache is
46144    # cleared. This give us most of the speed of the hash without the huge memory overhead of storing
46145    # the entire hash and should be totally portable. Picking the best value for n requires some
46146    # tuning. A \n is required to start the file.
46147    # if you're updating entries it behoves you to call flush every so often to make sure that your
46148    # changes are saved. This also frees the memory used to remember updated values.
46149    # for my purposes a value of undef and a nonexistant key are the same
46150    # Obviosly if your keys or values contain \n or \002 it will totally goof things up.
46151    sub TIEHASH {
46152        my ( $c, $fn ) = @_;
46153        my $self = { fn => $fn, age => mtime($fn), cnt => 0, cache => {}, updated => {}, ptr => 1, };
46154        bless $self, $c;
46155        return $self;
46156    }
46157    sub DESTROY { $_[0]->flush(); }
46158    sub mtime { my @s = stat( $_[0] ); $s[9]; }
46159
46160    sub flush {
46161        my $this = shift;
46162        return unless %{ $this->{ updated } };
46163        my $f = $this->{ fn };
46164        open( O, '>', "$f.tmp" ) || return;
46165        binmode(O);
46166        open( I, '<', "$f" ) || print O"\n";
46167        binmode(I);
46168        local $/ = "\n";
46169        my @l = ( sort keys %{ $this->{ updated } } );
46170        my ( $k, $d, $r, $v );
46171
46172        while ( $r = <I> ) {
46173            ( $k, $d ) = split( "\002", $r );
46174            while ( @l && $l[0] lt $k ) {
46175                $v = $this->{ updated }{ $l[0] };
46176                print O"$l[0]\002$v\n" if $v;
46177                shift(@l);
46178            }
46179            if ( $l[0] eq $k ) {
46180                $v = $this->{ updated }{ $l[0] };
46181                print O"$l[0]\002$v\n" if $v;
46182                shift(@l);
46183            }
46184            else { print O$r; }
46185        }
46186        while (@l) {
46187            $v = $this->{ updated }{ $l[0] };
46188            print O"$l[0]\002$v\n" if $v;
46189            shift(@l);
46190        }
46191        close I;
46192        close O;
46193        unlink($f);
46194        rename( "$f.tmp", $f );
46195        $this->{ updated } = {};
46196    } ## end sub flush
46197
46198    sub STORE {
46199        my ( $this, $key, $value ) = @_;
46200        $this->{ cache }{ $key } = $this->{ updated }{ $key } = $value;
46201    }
46202
46203    sub FETCH {
46204        my ( $this, $key ) = @_;
46205        return $this->{ cache }{ $key } if exists $this->{ cache }{ $key };
46206        $this->resetCache()
46207            if ( $this->{ cnt }++ > 10000
46208            || ( $this->{ cnt } & 0x1f ) == 0 && mtime( $this->{ fn } ) != $this->{ age } );
46209        return $this->{ cache }{ $key } = binsearch( $this->{ fn }, $key );
46210    }
46211
46212    sub resetCache {
46213        my $this = shift;
46214        $this->{ cnt }   = 0;
46215        $this->{ age }   = mtime( $this->{ fn } );
46216        $this->{ cache } = { %{ $this->{ updated } } };
46217    }
46218
46219    sub binsearch {
46220        my ( $f, $k ) = @_;
46221        open( F, '<', "$f" ) || return;
46222        binmode(F);
46223        my $siz = my $h = -s $f;
46224        $siz -= 1024;
46225        my $l  = 0;
46226        my $k0 = $k;
46227        $k =~ s/([\[\]\(\)\*\^\!\|\+\.\\\/\?\`\$\@\{\}])/\\$1/g;    # make sure there's no re chars unqutoed in the key
46228
46229        #print "k=$k ($_[1])\n";
46230        while (1) {
46231            my $m = ( ( $l + $h ) >> 1 ) - 1024;
46232            $m = 0 if $m < 0;
46233
46234            #print "($l $m $h) ";
46235            seek( F, $m, 0 );
46236            my $d;
46237            my $read = read( F, $d, 2048 );
46238            if ( $d =~ /\n$k\002([^\n]*)\n/ ) {
46239                close F;
46240
46241                #print "got $1\n";
46242                return $1;
46243            }
46244            my ( $pre, $first, $last, $post ) = $d =~ /^(.*?)\n(.*?)\002.*\n(.*?)\002.*?\n(.*?)$/s;
46245
46246            #print "f=$first ";
46247            last unless defined $first;
46248            if ( $k0 gt $first && $k0 lt $last ) {
46249
46250                #print "got miss\n";
46251                last;
46252            }
46253            if ( $k0 lt $first ) {
46254                last if $m == 0;
46255                $h = $m - 1024 + length($pre);
46256                $h = 0 if $h < 0;
46257            }
46258            if ( $k0 gt $last ) {
46259                last if $m >= $siz;
46260                $l = $m + $read - length($post);
46261            }
46262
46263            #print "l=$l h=$h ";
46264        } ## end while (1)
46265        close F;
46266        return;
46267    } ## end sub binsearch
46268
46269    sub FIRSTKEY {
46270        my $this = shift;
46271        $this->flush();
46272        $this->{ ptr } = 1;
46273        $this->NEXTKEY();
46274    }
46275
46276    sub NEXTKEY {
46277        my ( $this, $lastkey ) = @_;
46278        local $/ = "\n";
46279        open( F, '<', "$this->{fn}" ) || return;
46280        binmode(F);
46281        seek( F, $this->{ ptr }, 0 );
46282        my $r = <F>;
46283        return unless $r;
46284        $this->{ ptr } = tell F;
46285        close F;
46286        my ( $k, $v ) = $r =~ /(.*?)\002(.*?)\n/s;
46287
46288        if ( !exists( $this->{ cache }{ $k } ) && $this->{ cnt }++ > 10000 ) {
46289            $this->{ cnt }   = 0;
46290            $this->{ cache } = { %{ $this->{ updated } } };
46291        }
46292        $this->{ cache }{ $k } = $v;
46293        $k;
46294    }
46295
46296    sub EXISTS {
46297        my ( $this, $key ) = @_;
46298        return FETCH( $this, $key );
46299    }
46300
46301    sub DELETE {
46302        my ( $this, $key ) = @_;
46303        $this->{ cache }{ $key } = $this->{ updated }{ $key } = undef;
46304    }
46305
46306    sub CLEAR {
46307        my ($this) = @_;
46308        open( F, '>', "$this->{fn}" );
46309        binmode(F);
46310        print "\n";
46311        close F;
46312        $this->{ cache }   = {};
46313        $this->{ updated } = {};
46314        $this->{ cnt }     = 0;
46315    }
46316}
46317
46318
46319RBEOT
46320
46321}
46322sub writeWatchdog {
46323my $watchdog_version = '1.02';
46324my $curr_version;
46325if (open my $ADV, '<',"$base/assp_watchdog.pl") {
46326    while (<$ADV>) {
46327        if (/^\s*our \$VERSION.+?(\d\.\d+)/o) {
46328            $curr_version = $1;
46329
46330            last;
46331        }
46332
46333    }
46334    close $ADV;
46335    mlog(0,"info: found module $base/assp_watchdog.pl version $curr_version");
46336}
46337return 0 if $curr_version eq $watchdog_version;
46338(open my $ADV, '>',"$base/assp_watchdog.pl") or return 0;
46339
46340print $ADV "#!/usr/local/bin/perl\n\n";
46341print $ADV 'our $VERSION = ',"'$watchdog_version';\n\n";
46342print $ADV <<'RBEOT' or return 0;
46343
46344use strict;
46345use IO::Socket;
46346
46347use Time::Local;
46348use Time::HiRes;
46349use Cwd;
46350
46351 our $base;
46352 our $pid;
46353if($ARGV[0]) {
46354 $base=$ARGV[0];
46355} else {
46356 # the last one is the one used if all else fails
46357 $base = cwd();
46358 unless (-e "$base/assp.cfg") {
46359   foreach ('.','/usr/local/assp','/home/assp','/etc/assp','/usr/assp','/applications/assp','/assp','.') {
46360    if (-e "$_/assp.cfg") {
46361      $base=$_;
46362      last ;
46363    }
46364   }
46365 }
46366 $base = cwd() if $base eq '.';
46367}
46368
46369 open( my $confFile, '<', "$base/assp.cfg" ) || die "cannot open \"$base/assp.cfg\": $!";
46370local $/;
46371my %Config = split( /:=|\n/, <$confFile> );
46372close $confFile or die "unable to close: $!";
46373our $EnableWatchdog = $Config{ EnableWatchdog };
46374our $WatchdogHeartBeat = $Config{WatchdogHeartBeat };
46375our $WatchdogRestart = $Config{ WatchdogRestart };
46376our $AutoRestartCmd = $Config{ AutoRestartCmd };
46377our $AsAService = $Config{ AsAService };
46378
46379our $pidfile = $Config{ pidfile };
46380fork() && exit;
46381my $watchdogPID = $pidfile . "_watchdog";
46382open(my $FH, ">$base/$watchdogPID" ); print $FH $$; close $FH;
46383$pidfile = "$base/$pidfile";
46384
46385open my $FH, "<$pidfile";
46386$pid = <$FH>;
46387$pid =~ s/\r|\n|\s//go;
46388close $FH;
46389print "assp_watchdog.pl watching ASSP(pid=$pid), HeartBeat = $WatchdogHeartBeat, Restart = $WatchdogRestart\n" ;
46390my $count;
46391
46392eval {
46393    while (1)
46394
46395    {
46396		sleep 30;
46397		if (!-e "$pidfile") {
46398			exit 1;
46399			}
46400		open my $FH, "<$pidfile";
46401		$pid = <$FH>;
46402		$pid =~ s/\r|\n|\s//go;
46403		close $FH;
46404		my @s     = stat("$pidfile");
46405		my $mtime = $s[9];
46406    	my $heartbeats = time - $mtime;
46407    	if ( $heartbeats > 100) {
46408    		kill HUP =>  => $pid if $pid;
46409    		}
46410    	if ( $heartbeats > $WatchdogHeartBeat) {
46411			print "assp_watchdog.pl: ASSP($pid) heartbeats ($heartbeats) reached limit ($WatchdogHeartBeat)\n" ;
46412
46413    		kill TERM => $pid if $pid;
46414    		sleep 10;
46415    		if ($WatchdogRestart) {
46416    			print "assp_watchdog.pl: trying to restart ASSP\n" if $AutoRestartCmd;
46417    			print "assp_watchdog.pl: ASSP restart not possible, AutoRestartCmd not configured\n" if !$AutoRestartCmd;
46418    			if ($AutoRestartCmd) {
46419    				if ($AsAService) {
46420
46421        				exec('cmd.exe /C net stop ASSPSMTP & net start ASSPSMTP') ;
46422						exit 1;
46423    				} else {
46424
46425                		print "assp_watchdog.pl: restarting with '$AutoRestartCmd'\n";
46426        				print "assp_watchdog.pl for ASSP(pid=$pid) ended\n";
46427        				exec($AutoRestartCmd);
46428
46429						exit 1;
46430
46431    				}
46432
46433
46434    			}
46435
46436			}
46437			print "assp_watchdog.pl watching ASSP(pid=$pid) ended\n" ;
46438			exit 1;
46439		}
46440    }
46441};
46442RBEOT
46443
46444}
46445
46446
46447################################################################################
46448#                orderedtie
46449{
46450
46451    package orderedtie;
46452
46453# This is a tied value that caches lookups from a sorted file; \n separates records,
46454# \002 separates the key from the value. After main::OrderedTieHashTableSize lookups the cache is
46455# cleared. This give us most of the speed of the hash without the huge memory overhead of storing
46456# the entire hash and should be totally portable. Picking the best value for n requires some
46457# tuning. A \n is required to start the file.
46458
46459# if you're updating entries it behoves you to call flush every so often to make sure that your
46460# changes are saved. This also frees the memory used to remember updated values.
46461
46462    # for my purposes a value of undef and a nonexistant key are the same
46463
46464# Obviously if your keys or values contain \n or \002 it will totally goof things up.
46465
46466sub TIEHASH {
46467    my ( $c, $fn ) = @_;
46468    my $self = {
46469        fn      => $fn,
46470        age     => mtime($fn),
46471        cnt     => 0,
46472        cache   => {},
46473        updated => {},
46474        ptr     => 1,
46475    };
46476    bless $self, $c;
46477    return $self;
46478}
46479sub DESTROY { $_[0]->flush(); }
46480
46481sub mtime { my @s = stat( $_[0] ); $s[9]; }
46482
46483sub flush {
46484 my $this=shift;
46485 return unless %{$this->{updated}};
46486 my $f=$this->{fn};
46487 open(my $O,'>',"$f.tmp") or return;
46488 binmode $O;
46489 open(my $I,'<',$f) || print $O "\n";
46490 binmode $I;
46491 local $/="\n";
46492 my @l=(sort keys %{$this->{updated}});
46493 my ($k,$d,$r,$v);
46494 while ($r=<$I>) {
46495  ($k,$d)=split("\002",$r);
46496  while (@l && $l[0] lt $k) {
46497   $v=$this->{updated}{$l[0]};
46498   print $O "$l[0]\002$v\n" if $v;
46499   shift @l;
46500  }
46501  if ($l[0] eq $k) {
46502   $v=$this->{updated}{$l[0]};
46503   print $O "$l[0]\002$v\n" if $v;
46504   shift @l;
46505  } else {
46506   print $O $r;
46507  }
46508 }
46509 while (@l) {
46510  $v=$this->{updated}{$l[0]};
46511  print $O "$l[0]\002$v\n" if $v;
46512  shift @l;
46513 }
46514 close $I;
46515 close $O;
46516 unlink($f);
46517 rename("$f.tmp", $f);
46518 $this->{updated}={};
46519}
46520
46521sub STORE {
46522    my ( $this, $key, $value ) = @_;
46523    $this->{cache}{$key} = $this->{updated}{$key} = $value;
46524}
46525
46526sub FETCH {
46527    my ( $this, $key ) = @_;
46528    return $this->{cache}{$key} if exists $this->{cache}{$key};
46529    $this->resetCache()
46530      if ( $this->{cnt}++ > $main::OrderedTieHashTableSize
46531        || ( $this->{cnt} & 0x1f ) == 0
46532        && mtime( $this->{fn} ) != $this->{age} );
46533
46534    return $this->{cache}{$key} = binsearch( $this->{fn}, $key );
46535}
46536
46537sub resetCache {
46538    my $this = shift;
46539
46540    $this->{cnt}   = 0;
46541    $this->{age}   = mtime( $this->{fn} );
46542    $this->{cache} = { %{ $this->{updated} } };
46543
46544}
46545
46546sub binsearch {
46547    my ( $f, $k ) = @_;
46548    open( my $F, "<","$f" ) || return;
46549    binmode($F);
46550    my $count = 0;
46551    my $siz = my $h = -s $f;
46552    $siz -= 1024;
46553    my $l  = 0;
46554    my $k0 = $k;
46555    $k =~ s/([\[\]\(\)\*\^\!\|\+\.\\\/\?\`\$\@\{\}])/\\$1/g
46556      ;    # make sure there's no re chars unqutoed in the key
46557
46558    while (1) {
46559        my $m = ( ( $l + $h ) >> 1 ) - 1024;
46560        $m = 0 if $m < 0;
46561        seek( $F, $m, 0 );
46562        my $d;
46563        my $read = read( $F, $d, 2048 );
46564        if ( $d =~ /\n$k\002([^\n]*)\n/ ) {
46565            close $F;
46566            return $1;
46567        }
46568        my ( $pre, $first, $last, $post ) =
46569          $d =~ /^(.*?)\n(.*?)\002.*\n(.*?)\002.*?\n(.*?)$/s;
46570        last unless defined $first;
46571        if ( $k0 gt $first && $k0 lt $last ) {
46572            last;
46573        }
46574        if ( $k0 lt $first ) {
46575            last if $m == 0;
46576            $h = $m - 1024 + length($pre);
46577            $h = 0 if $h < 0;
46578        }
46579        if ( $k0 gt $last ) {
46580            last if $m >= $siz;
46581            $l = $m + $read - length($post);
46582        }
46583        if ( $count++ > 100 ) {
46584
46585            #main::mlog(0,"warning:   $this->{fn} must be repaired ($k0)");
46586            last;
46587        }
46588    }
46589    close $F;
46590    return;
46591}
46592
46593sub FIRSTKEY {
46594    my $this = shift;
46595    $this->flush();
46596    $this->{ptr} = 1;
46597    $this->NEXTKEY();
46598}
46599
46600sub NEXTKEY {
46601    my ( $this, $lastkey ) = @_;
46602    local $/ = "\n";
46603    open( my $F, "<","$this->{fn}" ) || return;
46604    binmode($F);
46605    seek( $F, $this->{ptr}, 0 );
46606    my $r = <$F>;
46607    return unless $r;
46608    $this->{ptr} = tell $F;
46609    close $F;
46610    my ( $k, $v ) = $r =~ /(.*?)\002(.*?)\n/s;
46611
46612    if ( !exists( $this->{cache}{$k} )
46613        && $this->{cnt}++ > $main::OrderedTieHashTableSize )
46614    {
46615        $this->{cnt}   = 0;
46616        $this->{cache} = { %{ $this->{updated} } };
46617    }
46618    $this->{cache}{$k} = $v;
46619    $k;
46620}
46621
46622sub EXISTS {
46623    my ( $this, $key ) = @_;
46624    return FETCH( $this, $key );
46625}
46626
46627sub DELETE {
46628    my ( $this, $key ) = @_;
46629    $this->{cache}{$key} = $this->{updated}{$key} = undef;
46630}
46631
46632sub CLEAR {
46633    my ($this) = @_;
46634    open(my $F, ">","$this->{fn}" );
46635    binmode($F);
46636    print $F "\n";
46637    close $F;
46638    $this->{cache}   = {};
46639    $this->{updated} = {};
46640    $this->{cnt}     = 0;
46641}
46642}
46643
46644package ASSP::CRYPT;
46645
46646##################################
46647# based on GOST 28147-89  (Vipul Ved Prakash, 1997)
46648#
46649# GOST 28147-89 is a 64-bit symmetric block cipher
46650# with a 256-bit key developed in the former Soviet Union (KGB).
46651#
46652# redesigned and improved by Thomas Eckardt (2009)
46653##################################
46654
46655sub new {
46656        my ($argument,$pass,$bin) = @_;
46657	my $class = ref ($argument) || $argument;
46658	my $self = {};
46659	$self->{KEY} = [];
46660	$self->{SBOX} = [];
46661	$self->{BIN} = $bin;
46662	$self->{PASS} = $pass;
46663        _generate_sbox($self,$pass) if $pass;
46664        _generate_keys($self,$pass) if $pass;
46665	bless $self, $class;
46666	return $self;
46667}
46668
46669sub _generate_sbox {
46670	my $self = shift;
46671	my $passphrase = shift;
46672	if (ref ($passphrase)) {
46673		@{$self->{SBOX}} = @$passphrase;
46674	} else {
46675		my ($i, $x, $y, $random, @tmp) = 0;
46676		my @temp = (0..15);
46677		for ($i=0; $i <= (length $passphrase); $i+=4)
46678		{ $random = $random ^ (unpack 'L', pack 'a4', substr ($passphrase, $i, $i+4)) };
46679		srand $random;
46680		for ($i=0; $i < 8; $i++) {
46681        		@tmp = @temp;
46682               		grep { $x = _rand (15); $y = $tmp[$x]; $tmp[$x] = $tmp[$_]; $tmp[$_] = $y; } (0..15);
46683                	grep {$self->{SBOX}->[$i][$_] = $tmp[$_] } (0..15);
46684		}
46685	}
46686}
46687
46688sub _generate_keys {
46689	my ($self, $passphrase) = @_;
46690	if (ref ($passphrase)) {
46691		@{$self->{KEY}} = @$passphrase;
46692	} else {
46693		my ($i, $random) = 0;
46694		for ($i=0; $i <= (length $passphrase); $i+=4)
46695		{ $random = $random ^ (unpack 'L', pack 'a4', substr ($passphrase, $i, $i+4))};
46696		srand $random; grep { $self->{KEY}[$_] = _rand (2**32) } (0..7);
46697	}
46698}
46699
46700sub _crypt {
46701	my ($self, $data, $decrypt, $bin) = @_;
46702        return $data unless $self->{PASS};
46703	$bin = $bin || $self->{BIN};
46704        my $l;
46705        my $check;
46706        my $cl = $bin ? 3 : 6;
46707        my $ll = $bin ? 2 : 4;
46708        if ($decrypt) {
46709            $check = substr($data,length($data)-$cl,$cl);
46710            $data = substr($data,0,length($data)-$cl);
46711            $l = int(hex(_IH(substr($data,length($data)-$ll,$ll),$bin)));
46712            $data = substr($data,0,length($data)-$ll);
46713	    $data = _HI($data,! $bin);
46714	} else {
46715            $check = _XOR_SYSV($data,$bin);
46716            $l = length($data);
46717            my $s = $l % 8;
46718            $l = _HI(sprintf("%04x",$l),$bin);
46719            $data .= "\x5A" x (8-$s) if $s;
46720	}
46721	my ($i, $j, $d1, $d2) = 0;
46722	my $return = '';
46723	for ($i=0; $i < length $data; $i += 8) {
46724		$d1 = unpack 'L', pack 'a4', substr ($data, $i, $i + 4);
46725		$d2 = unpack 'L', pack 'a4', substr ($data, $i + 4, $i + 8);
46726		$j = 0;
46727		grep {
46728			$j = ($_ % 8) - 1; $j = 7 if $j == -1;
46729			$decrypt ? ($_ >= 9) && ($j = (32 - $_) % 8) : ($_ >= 25) && ($j = 32 - $_);
46730			($_ % 2) == 1 ? ($d2 ^= $self->_substitute ($d1 + $self->{KEY}[$j])) :
46731					($d1 ^= $self->_substitute ($d2 + $self->{KEY}[$j])) ;
46732		} (1..32);
46733		$return = $return . (pack 'L', $d2) . (pack 'L', $d1);
46734	}
46735        return _IH($return,! $bin).$l.$check unless ($decrypt);
46736        $return = substr($return,0,$l);
46737        return if _XOR_SYSV($return,$bin) ne $check;
46738        return $return;
46739}
46740
46741sub ENCRYPT    {_crypt(shift,shift,0,0);}
46742
46743sub DECRYPT    {_crypt(shift,shift,1,0);}
46744
46745sub ENCRYPTHEX {_crypt(shift,shift,0,1);}
46746
46747sub DECRYPTHEX {_crypt(shift,shift,1,1);}
46748
46749sub _substitute {
46750	my ($self, $d) = @_;
46751	my $return = 0;
46752	grep { $return = $return | $self->{SBOX}->[$_][$d >> ($_ * 4) & 15] << ($_ * 4) } reverse (0..7);
46753	return $return << 11 | $return >> 21;
46754}
46755
46756sub _rand {
46757	return int (((shift) / 100) * ((rand) * 100));
46758}
46759
46760sub _XOR_SYSV {
46761    my ($d,$bin) = @_;
46762    my $xor = 0x03 ^ 0x0d;
46763    for ( split(//o, $d) ) { $xor ^= ord($_); };
46764    return _HI(sprintf ("%02x", $xor),$bin) . _HI(sprintf("%04x",unpack("%32W*",$d) % 65535),$bin) if ( $]>="5.010" );
46765    return _HI(sprintf ("%02x", $xor),$bin) . _HI(sprintf("%04x", _SYSV($d)),$bin);
46766}
46767
46768sub _SYSV {
46769    my $d = shift;
46770    my $checksum = 0;
46771    foreach (split(//o,$d)) { $checksum += unpack("%16C*", $_) }
46772    $checksum %= 65535;
46773    return $checksum;
46774}
46775
46776sub _IH {
46777	my ($s,$do) = @_;
46778        return $s unless $do;
46779        return join('',unpack 'H*',$s);
46780}
46781
46782sub _HI {
46783	my ($h,$do) = @_;
46784        return $h unless $do;
46785        return pack 'H*',$h;
46786}
46787
467881;
46789
46790
46791
46792#################################################################
46793# this package implements realtime blacklisting
46794# it is based on Net::RBLClient by Asher Blum <asher@wildspark.com>
46795# CREDITS Martin H. Sluka <martin@sluka.de>
46796# Copyright (C) 2002 Asher Blum.  All rights reserved.
46797# This code is free software; you can redistribute it and/or modify it under
46798# the same terms as Perl itself.
46799# Modified for integration with ASSP by John Calvi.
46800
46801
46802package RBL;
46803
46804use IO::Socket;
46805use IO::Select;
46806
46807sub new {
46808    # This avoids compile time errors if Net::DNS is not installed.
46809    # The error will be returned on the lookup function call.
46810    if ($main::CanUseDNS) {
46811     require Net::DNS::Packet;
46812     $CanUseDNS=1;
46813    }
46814    my($class, %args) = @_;
46815    my $self = {
46816        lists       => [ lists() ],
46817        query_txt   => 1,
46818        max_time    => 10,
46819        timeout     => 1,
46820        max_hits    => 3,
46821        max_replies => 6,
46822        udp_maxlen  => 4000,
46823        server      => '127.0.0.1',
46824    };
46825    bless $self, $class;
46826    foreach my $key(keys %args) {
46827        defined($self->{ $key })
46828            or return "Invalid key: $key";
46829        $self->{ $key } = $args{ $key };
46830    }
46831
46832    $self;
46833}
46834
46835sub lookup {
46836    return "Net::DNS package required" unless $main::CanUseDNS;
46837    my($self, $target, $type) = @_;
46838    my $start_time = time;
46839    my $qtarget;
46840    my $dur;
46841    my @ok;
46842    my @failed;
46843    my $isip = 0;
46844    $target =~ s/[^\w\-\.:].*$//o if $type ne 'URIBL';
46845    if ($target=~/^$main::IPv4Re$/o) {
46846        $qtarget = join ('.', reverse(split /\./o, $target));
46847        $isip = 1;
46848    } elsif ($target=~/^$main::IPv6Re$/o) {
46849        $qtarget = &main::ipv6hexrev($target,36) or return "IPv6 addresses are not supported";
46850        $isip = 2;
46851    } else {
46852        $qtarget=$target;
46853    }
46854    my $deadline = time + $self->{ max_time };
46855    my @sock;
46856
46857    for (@{$self->{server}}) {
46858        my $sock = $main::CanUseIOSocketINET6
46859                   ? IO::Socket::INET6->new(Proto=>'udp',PeerAddr=>$_,PeerPort=>53,&main::getDestSockDom($_))
46860                   : IO::Socket::INET->new(Proto=>'udp',PeerAddr=>$_,PeerPort=>53);
46861        push @sock, $sock if $sock;
46862    }
46863    if (! @sock) {
46864        return "Failed to create any UDP client for DNS queries";
46865    }
46866    for (@sock) {
46867        $_->blocking(0) if $_->blocking;
46868    }
46869    my $sn = 0;
46870    my @availsock;
46871    my %regsock;
46872    if ( $self->{ query_txt } ) {
46873      foreach my $list(@{ $self->{ lists } }) {
46874        if (length($qtarget.$list) > 62 && $type ne 'URIBL' && $isip != 2) {
46875          eval{close($_) if $_;} for (@sock);
46876          return "domain name too long";
46877        }
46878        if ($list && !($type eq 'URIBL' && lc $list eq 'dbl.spamhaus.org' && $isip)) {
46879            my($msg_a, $msg_t) = mk_packet($qtarget, $list);
46880            $list =~ s/.*?\$DATA\$\.?//io;
46881            foreach ($msg_a, $msg_t) {
46882                my $redo;
46883                if ($sock[$sn]->send($_)) {
46884                    if (! exists $regsock{$sock[$sn]} ) {
46885                        push @availsock , $sock[$sn];
46886                        $regsock{$sock[$sn]} = $sock[$sn]->peerhost() . '[:' . $sock[$sn]->peerport().']';
46887                    }
46888                    &main::mlog(0,"sending DNS(TXT)-query to $regsock{$sock[$sn]} on $list for $type checks on $target") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
46889                } else {
46890                    close($sock[$sn]);
46891                    splice(@sock,$sn,1);
46892                    $redo = 1;
46893                }
46894                $sn = 0 if ++$sn >= scalar @sock;
46895                last unless scalar @sock;
46896                redo if $redo;
46897            }
46898            return "send: $!" if (! scalar @availsock && ! scalar @sock);
46899        }
46900      }
46901    } else {
46902        foreach my $list(@{ $self->{ lists } }) {
46903          if (length($qtarget.$list) > 62 && $type ne 'URIBL' && $isip != 2) {
46904            eval{close($_) if $_;} for (@sock);
46905            return "domain name too long";
46906          }
46907          if ($list && !($type eq 'URIBL' && lc $list eq 'dbl.spamhaus.org' && $isip)) {
46908              my $msg = mk_packet($qtarget, $list);
46909              $list =~ s/.*?\$DATA\$\.?//io;
46910              foreach ($msg,0) {
46911                  last unless $_;
46912                  my $redo;
46913                  if ($sock[$sn]->send($_)) {
46914                      if (! exists $regsock{$sock[$sn]} ) {
46915                          push @availsock , $sock[$sn];
46916                          $regsock{$sock[$sn]} = $sock[$sn]->peerhost() . '[:' . $sock[$sn]->peerport().']';
46917                      }
46918                      &main::mlog(0,"sending DNS-query to $regsock{$sock[$sn]} on $list for $type checks on $target") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
46919                  } else {
46920                      close($sock[$sn]);
46921                      splice(@sock,$sn,1);
46922                      $redo = 1;
46923                  }
46924                  $sn = 0 if ++$sn >= scalar @sock;
46925                  last unless scalar @sock;
46926                  redo if $redo;
46927              }
46928              return "send: $!" if (! scalar @availsock && ! scalar @sock);
46929          }
46930        }
46931    }
46932    @sock = @availsock;
46933
46934    $self->{ results } = {};
46935    $self->{ txt } = {};
46936
46937    my $needed = 0;
46938    if ($self->{ max_replies} > @{ $self->{ lists } }) {
46939      $needed = @{ $self->{ lists } };
46940    } else {
46941      $needed = $self->{ max_replies };
46942    }
46943
46944    my $hits = my $replies = 0;
46945
46946    my $select = IO::Select->new();
46947    $select->add($_) for @sock;
46948    my $numsock = scalar @sock;
46949    # Keep receiving packets until one of the exit conditions is met:
46950    &main::mlog(0,"Commencing $type checks on '$target'") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
46951    my $countansw = 0;
46952    while ($needed && time < $deadline) {
46953      my @msg = ();
46954      my $st = Time::HiRes::time();
46955      if ($numsock && (my @ready = $select->can_read( $self->{timeout} )) ) {
46956        map {
46957            if ($_->recv(my $msg, $self->{udp_maxlen} )) {
46958                push @msg, $msg;
46959            } else {
46960                $select->remove($_);
46961                close($_);
46962                $numsock--;
46963            }
46964        } @ready;
46965        return "recv: $!" if (! @msg && ! $numsock);
46966        next unless @msg;
46967      } elsif (! $numsock) {
46968        return "recv: $!";
46969      } else {
46970        next; # there are no data on socket -> next loop
46971      }
46972      $main::ThreadIdleTime{$main::WorkerNumber} += Time::HiRes::time() - $st;
46973      $dur = time - $start_time;
46974      while (my $msg = shift @msg) {
46975        $countansw++;
46976        my ($domain, $res, $type) = decode_packet($self,$msg);
46977        unless ($domain) {
46978            $needed --;
46979            next ;
46980        }
46981        next if exists $self->{ results }{ $domain };
46982        $replies ++;
46983        if ($res) {
46984          my $ret = $domain;
46985          $ret =~ s/^$qtarget\.//;
46986          push @failed, $ret unless grep(/\Q$ret\E/,@failed);
46987
46988          $hits ++;
46989          $self->{ results }{ $domain } = $res;
46990
46991          if (! $main::Showmaxreplies &&
46992              ($hits >= $self->{ max_hits } || $replies >= $self->{ max_replies })
46993             ) {
46994
46995              $dur = time - $start_time;
46996              &main::mlog(0,"got $countansw answers, $replies replies and $hits hits after $dur seconds for $type checks on '$target'") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
46997              &main::mlog(0,"got OK replies from (@ok) - NOTOK replies from (@failed) for $type on '$target'") if $main::RBLLog>2 && $type eq "RBL" || $main::URIBLLog>2 && $type eq "URIBL" || $main::RWLLog>2 && $type eq "RWL" || $main::BacksctrLog>2 && $type eq "BACKSCATTER";
46998              eval{close($_) if $_;} for (@sock);
46999              return 1;
47000          }
47001        } else {
47002            my $ret = $domain;
47003            $ret =~ s/^$qtarget\.//;
47004            push @ok, $ret unless grep(/\Q$ret\E/,@ok);
47005        }
47006        $needed --;
47007      }
47008    }
47009    $dur = time - $start_time;
47010    &main::mlog(0,"got $countansw answers, $replies replies and $hits hits after $dur seconds for $type checks on '$target'") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
47011    &main::mlog(0,"got OK replies from (@ok) - NOTOK replies from (@failed) for $type on '$target'") if $main::RBLLog>2 && $type eq "RBL" || $main::URIBLLog>2 && $type eq "URIBL" || $main::RWLLog>2 && $type eq "RWL" || $main::BacksctrLog>2 && $type eq "BACKSCATTER";
47012    &main::mlog(0,"Completed $type checks on '$target'") if $main::RBLLog>=2 && $type eq "RBL" || $main::URIBLLog>=2 && $type eq "URIBL" || $main::RWLLog>=2 && $type eq "RWL" || $main::BacksctrLog>=2 && $type eq "BACKSCATTER";
47013    eval{close($_) if $_;} for (@sock);
47014    return 1;
47015}
47016
47017
47018sub listed_by {
47019    my $self = shift;
47020    sort keys %{ $self->{ results } };
47021}
47022
47023sub listed_hash {
47024    my $self = shift;
47025    %{ $self->{ results } };
47026}
47027
47028sub txt_hash {
47029    my $self = shift;
47030    warn <<_ unless $self->{ query_txt };
47031Without query_txt turned on, you won't get any results from ->txt_hash().
47032_
47033    if (wantarray) { %{ $self->{ txt } } }
47034    else { $self->{ txt } }
47035}
47036
47037
47038# End methods - begin internal functions
47039
47040sub mk_packet {
47041    # pass me a REVERSED dotted quad ip (qip) and a blocklist domain
47042    my($qip, $list) = @_;
47043    my ($packet, $txt_packet, $error);
47044    my $fqdn;
47045    if ($list =~ s/\$DATA\$/$qip/io) {     # if a key is required it is in $list
47046        $fqdn = $list;                    # like key.$DATA$.serviceProvider
47047    } else {
47048        $fqdn = "$qip.$list";
47049    }
47050    ($packet, $error) = new Net::DNS::Packet( $fqdn , 'A');
47051    return "Cannot build DNS query for $fqdn, type A: $error" unless $packet;
47052    return $packet->data unless wantarray;
47053    ($txt_packet, $error) = new Net::DNS::Packet($fqdn, 'TXT', 'IN');
47054    return "Cannot build DNS query for $fqdn, type TXT: $error" unless $txt_packet;
47055    $packet->data, $txt_packet->data;
47056}
47057
47058sub decode_packet {
47059    # takes a raw DNS response packet
47060    # returns domain, response
47061    my ($self,$data) = @_;
47062    my $packet = Net::DNS::Packet->new(\$data);
47063    my @answer = $packet->answer;
47064    my @question = $packet->question;
47065    my $domain = $question[0]->qname;
47066    $domain =~ s/^.*?\d+\.\d+\.\d+\.\d+\.//o;
47067    {
47068        my(%res, $res, $type);
47069        foreach my $answer (@answer) {
47070            $type = $answer->type;
47071            $res{$type} = $type eq 'A'     ? inet_ntoa($answer->rdata)  :
47072                          $type eq 'CNAME' ? cleanup($answer->rdata)    :
47073                          $type eq 'TXT'   ? (exists $res{'TXT'} && $res{'TXT'}.'; ')
47074                                             . $answer->txtdata         :
47075                          '?';
47076        }
47077        $res = $res{'A'} || $res{'CNAME'} || $res{'TXT'};
47078        $self->{ txt }{ $domain } .= $res{'TXT'} if $res{'TXT'};
47079        ($res) = $res =~ /(127\.\d+\.\d+\.\d+)/os;
47080        return $domain, $res, $type if $res;
47081    }
47082
47083    # OK, there were no answers -
47084    # need to determine which domain
47085    # sent the packet.
47086
47087    return $domain;
47088}
47089
47090sub cleanup {
47091    # remove control chars and stuff
47092    $_[ 0 ] =~ tr/a-zA-Z0-9./ /cs;
47093    $_[ 0 ];
47094}
47095
47096
47097sub lists {
47098    qw(
47099       bl.spamcop.net
47100       list.dsbl.org
47101       zen.spamhaus.org
47102    );
47103}
471041;
47105