source: npl/mailserver/dspam/dspam-3.10.2/webui/cgi-bin/dspam.cgi @ c5c522c

gcc484ntopperl-5.22
Last change on this file since c5c522c was c5c522c, checked in by Edwin Eefting <edwin@datux.nl>, 8 years ago

initial commit, transferred from cleaned syn3 svn tree

  • Property mode set to 100755
File size: 54.3 KB
Line 
1#!/usr/bin/perl
2
3# $Id: dspam.cgi,v 1.57 2011/06/28 00:13:48 sbajic Exp $
4# DSPAM
5# COPYRIGHT (C) 2002-2012 DSPAM PROJECT
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20use strict;
21use POSIX qw(strftime ctime);
22use Time::Local;
23use vars qw { %CONFIG %DATA %FORM %LANG $MAILBOX $CURRENT_USER $USER $TMPFILE $USERSELECT };
24use vars qw { $CURRENT_STORE $LANGUAGE };
25
26#
27# Read configuration parameters common to all CGI scripts
28#
29if (!(-e "configure.pl") || !(-r "configure.pl")) {
30  &htmlheader;
31  print "<html><head><title>Error!</title></head><body bgcolor='white' text='black'><center><h1>";
32  print "Missing file configure.pl";
33  print "</h1></center></body></html>\n";
34  exit;
35}
36require "configure.pl";
37
38#
39# The current CGI script
40#
41
42$CONFIG{'ME'} = "dspam.cgi";
43
44$| = 1;
45
46#
47# Determine which extensions are available
48#
49
50if ($CONFIG{'AUTODETECT'} == 1 || $CONFIG{'AUTODETECT'} eq "") {
51  $CONFIG{'PREFERENCES_EXTENSION'} = 0;
52  $CONFIG{'LARGE_SCALE'} = 0;
53  $CONFIG{'DOMAIN_SCALE'} = 0;
54  do {
55    my $x = `$CONFIG{'DSPAM'} --version`;
56    if ($x =~ /--enable-preferences-extension/) {
57      $CONFIG{'PREFERENCES_EXTENSION'} = 1;
58    }
59    if ($x =~ /--enable-large-scale/) {
60      $CONFIG{'LARGE_SCALE'} = 1;
61    }
62    if ($x =~ /--enable-domain-scale/) {
63      $CONFIG{'DOMAIN_SCALE'} = 1;
64    }
65  }
66}
67
68#
69# Determine admin status
70#
71
72$CONFIG{'ADMIN'} = 0;
73if ($ENV{'REMOTE_USER'} ne "") {
74  open(FILE, "<./admins");
75  while(<FILE>) {
76    chomp;
77    if ($_ eq $ENV{'REMOTE_USER'}) {
78      $CONFIG{'ADMIN'} = 1;
79      last;
80    }
81  }
82  close(FILE);
83}
84$CONFIG{'SUBADMIN'} = 0;
85$CONFIG{'SUBADMIN_USERS'} = {};
86if ($ENV{'REMOTE_USER'} ne "" && $CONFIG{'ADMIN'} == 0) {
87  open(FILE, "<./subadmins");
88  while(<FILE>) {
89    chomp;
90    if ($_ !~ /^\s*#/) {
91      my ($subadmin, $users) = (split(/\s*:\s*/))[0,1];
92      if ($subadmin eq $ENV{'REMOTE_USER'}) {
93        $CONFIG{'SUBADMIN'} = 1;
94        $CONFIG{'SUBADMIN_USERS'}->{ $ENV{'REMOTE_USER'} } = 1;
95        for (split(/\s*,\s*/,$users)) {
96          my $user_clean = $_;
97          $user_clean =~ s/^\s+//;
98          $user_clean =~ s/\s+$//;
99          $CONFIG{'SUBADMIN_USERS'}->{ $user_clean } = 1 if $user_clean ne "";
100        }
101        last;
102      }
103    }
104  }
105  close(FILE);
106}
107
108#
109# Parse form
110#
111
112%FORM = &ReadParse;
113
114#
115# Configure languages
116#
117
118if ($FORM{'language'} ne "") {
119  $LANGUAGE = $FORM{'language'};
120} else {
121  $LANGUAGE = $CONFIG{'LANGUAGE_USED'};
122}
123if (! defined $CONFIG{'LANG'}->{$LANGUAGE}->{'NAME'}) {
124  $LANGUAGE = $CONFIG{'LANGUAGE_USED'};
125}
126my @dslanguages = ();
127for (split(/\n/,$CONFIG{'LANGUAGES'})) {
128  my $lang = $_;
129  $lang =~ s/\s*selected>/>/;
130  if ($lang =~ /value="([^"]+)"/) {
131    $lang =~ s/>/ selected>/ if ($1 eq $LANGUAGE);
132  }
133  push(@dslanguages, $lang);
134}
135$CONFIG{'LANGUAGES'} = join("\n", @dslanguages);
136$CONFIG{'TEMPLATES'} = $CONFIG{'LANG'}->{$LANGUAGE}->{'TEMPLATEDIR'};
137
138#
139# Configure Filesystem
140#
141
142$CURRENT_USER = $ENV{'REMOTE_USER'};
143
144if ($FORM{'user'} ne "") {
145  if ($CONFIG{'ADMIN'} == 1) {
146    $CURRENT_USER = $FORM{'user'};
147  } elsif ($CONFIG{'SUBADMIN'} == 1) {
148    my $form_user_domain = (split(/@/, $FORM{'user'}))[1];
149    if ($CONFIG{'SUBADMIN_USERS'}->{ $FORM{'user'} } == 1 || ($form_user_domain ne "" && $CONFIG{'SUBADMIN_USERS'}->{ "*@" . $form_user_domain } == 1)) {
150      $CURRENT_USER = $FORM{'user'};
151    } else {
152      $FORM{'user'} = $CURRENT_USER;
153    }
154  } else {
155   $FORM{'user'} = $CURRENT_USER;
156  }
157} else {
158  $FORM{'user'} = $CURRENT_USER;
159}
160
161$CONFIG{'DSPAM_ARGS'} =~ s/%CURRENT_USER%/$CURRENT_USER/g;
162
163# Current Store
164do {
165  my(%PREF) = GetPrefs($CURRENT_USER);
166  $CURRENT_STORE = $PREF{"localStore"};
167  if ($CURRENT_STORE eq "") { $CURRENT_STORE = $CURRENT_USER; }
168};
169
170$USER = GetPath($CURRENT_STORE);
171$MAILBOX = $USER . ".mbox";
172$TMPFILE = $USER . ".tmp";
173
174if ($CURRENT_USER eq "") {
175  &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_identity'}");
176}
177
178if ($FORM{'template'} eq "" || $FORM{'template'} !~ /^([A-Z0-9]*)$/i) {
179  $FORM{'template'} = "performance";
180}
181
182#
183# Create a list of known users
184#
185if ($CONFIG{'ADMIN'} == 1 || $CONFIG{'SUBADMIN'} == 1) {
186  my @dsusers = ();
187  push(@dsusers, qq!<select name="user">!);
188  open(IN, "$CONFIG{'DSPAM_STATS'}|");
189  while(<IN>) {
190    chomp;
191    my($username) = (split(/\s+/))[0,2,4,6,8,10,12];
192    if ($username ne "") {
193      if ($username eq $CURRENT_USER) {
194        # Add always current user to selection list
195        push(@dsusers, qq!<option value="$username" selected>&nbsp;$username</option>!);
196      } else {
197        if ($CONFIG{'ADMIN'} == 1) {
198          # Real administrators can see all users
199          push(@dsusers, qq!<option value="$username">&nbsp;$username</option>!);
200        } elsif ($CONFIG{'SUBADMIN'} == 1) {
201          # Sub-administrators can only see their users. Either full
202          # qualified email address or *@example.org
203          my $form_user_domain = (split(/@/, $username))[1];
204          if($CONFIG{'SUBADMIN_USERS'}->{ $username } == 1 || ($form_user_domain ne "" && $CONFIG{'SUBADMIN_USERS'}->{ "*@" . $form_user_domain } == 1)) {
205            $CONFIG{'SUBADMIN_USERS'}->{ $username } = 1; # Add full email to hash list so that we
206                                                          # can later check to ensure and secure
207                                                          # access/switch to target user by sub-
208                                                          # administrator.
209            push(@dsusers, qq!<option value="$username">&nbsp;$username</option>!);
210          }
211        }
212      }
213    }
214  }
215  push(@dsusers, qq!</select>!);
216  close(IN);
217  $USERSELECT = join("\n",@dsusers);
218  @dsusers=();
219}
220
221my($MYURL);
222$MYURL = qq!$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=$FORM{'template'}&amp;language=$LANGUAGE!;
223
224#
225# Set up initial display variables
226#
227&CheckQuarantine;
228$DATA{'REMOTE_USER'} = $CURRENT_USER;
229
230#
231# Display DSPAM Version
232#
233$DATA{'DSPAMVERSION'} = $CONFIG{'DSPAM_VERSION'};
234
235#
236# Process Commands
237#
238
239# Performance
240if ($FORM{'template'} eq "performance") {
241  if ($FORM{'command'} eq "resetStats") {
242    &ResetStats;
243    redirect($MYURL);
244  } elsif ($FORM{'command'} eq "tweak") {
245    &Tweak;
246    redirect($MYURL);
247  } else {
248    &DisplayIndex;
249  }
250
251# Quarantine
252} elsif ($FORM{'template'} eq "quarantine") {
253  if ($FORM{'command'} eq "viewMessage") {
254    &Quarantine_ViewMessage;
255  } else {
256    $MYURL .= "&amp;sortby=$FORM{'sortby'}" if ($FORM{'sortby'} ne "");
257    if ($FORM{'command'} eq "processQuarantine") {
258      &ProcessQuarantine;
259      redirect($MYURL);
260    } elsif ($FORM{'command'} eq "processFalsePositive") {
261      &ProcessFalsePositive;
262      redirect($MYURL);
263    } else {
264      &DisplayQuarantine;
265    }
266  }
267
268# Alerts
269} elsif ($FORM{'template'} eq "alerts") {
270  if ($FORM{'command'} eq "addAlert") {
271    &AddAlert;
272    redirect($MYURL);
273  } elsif ($FORM{'command'} eq "deleteAlert") {
274    &DeleteAlert;
275    redirect($MYURL);
276  } else {
277    &DisplayAlerts;
278  }
279
280# Preferences
281} elsif ($FORM{'template'} eq "preferences") {
282  &DisplayPreferences;
283
284# Analysis
285} elsif ($FORM{'template'} eq "analysis") {
286  &DisplayAnalysis;
287
288# History
289} elsif ($FORM{'template'} eq "history") {
290  &DisplayHistory;
291} elsif ($FORM{'template'} eq "fragment") {
292  &DisplayFragment;
293} else {
294  &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_invalid_command'} $FORM{'COMMAND'}");
295}
296
297#
298# History Functions
299#
300
301sub DisplayFragment {
302  $FORM{'signatureID'} =~ s/\///g;
303  $DATA{'FROM'} = $FORM{'from'};
304  $DATA{'SUBJECT'} = $FORM{'subject'};
305  $DATA{'INFO'} = $FORM{'info'};
306  $DATA{'TIME'} = $FORM{'time'};
307  open(FILE, "<$USER.frag/$FORM{'signatureID'}.frag") || &error($!);
308  while(<FILE>) {
309    s/</&lt\;/g;
310    s/>/&gt\;/g;
311    $DATA{'MESSAGE'} .= $_;
312  }
313  close(FILE);
314  &output(%DATA);
315}
316
317sub DisplayHistory {
318  my($all_lines , $begin, $history_pages, $rec, $history_page);
319  unless ($history_page = $FORM{'history_page'}) { $history_page = 1;}
320
321  my(@buffer, @history, $line, %rec);
322  my($rowclass) = "rowEven";
323
324  if ($CONFIG{'HISTORY_PER_PAGE'} == 0) {
325    $CONFIG{'HISTORY_PER_PAGE'} = 50;
326  }
327
328  my($show) = $FORM{'show'};
329  if ($show eq "") {
330    $show = "all";
331  }
332
333  if ($FORM{'command'} eq "retrainChecked") {
334    foreach my $i (0 .. $#{ $FORM{retrain_checked} }) {
335      my ($retrain, $signature) = split(/:/, $FORM{retrain_checked}[$i]);
336      if ($retrain eq "innocent") {
337        $FORM{'signatureID'} = $signature;
338        &ProcessFalsePositive();
339        undef $FORM{'signatureID'};
340      } elsif ($retrain eq "innocent" or $retrain eq "spam") {
341        system("$CONFIG{'DSPAM'} --source=error --class=" . quotemeta($retrain) . " --signature=" . quotemeta($signature) . " --user " . quotemeta("$CURRENT_USER"));
342      }
343    }
344    redirect("$MYURL&amp;show=$show&amp;history_page=$history_page");
345  } elsif ($FORM{'retrain'} ne "") {
346    if ($FORM{'retrain'} eq "innocent") {
347      &ProcessFalsePositive();
348    } else {
349      system("$CONFIG{'DSPAM'} --source=error --class=" . quotemeta($FORM{'retrain'}) . " --signature=" . quotemeta($FORM{'signatureID'}) . " --user " . quotemeta("$CURRENT_USER"));
350    }
351    redirect("$MYURL&amp;show=$show&amp;history_page=$history_page");
352  }
353
354  my($LOG) = "$USER.log";
355  if (! -e $LOG) {
356    &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_historic'}");
357  }
358
359  # Preseed retraining information and delivery errors
360 
361  open(LOG, "< $LOG") or die "Can't open log file $LOG";
362  while(<LOG>) {
363    my($time, $class, $from, $signature, $subject, $info, $messageid)
364      = split(/\t/, $_);
365    next if ($signature eq "");
366
367    # not good to check for messages to show here, we're skipping
368    # the retraining data so retrained messages won't show
369
370    if ($class eq "M" || $class eq "F" || $class eq "E" || $class eq "U") {
371      if ($class eq "E" || $class eq "U") {
372        $rec{$signature}->{'info'} = $info;
373      } elsif ($class eq "F" || $class eq "M") {
374        $rec{$signature}->{'class'} = $class;
375        $rec{$signature}->{'count'}++;
376        $rec{$signature}->{'info'} = $info
377          if ($rec{$signature}->{'info'} eq "");
378      }
379      # filter out resents if there are any. Since it's the same
380      # message we only allow retraining on the 1st occurence of it.
381    } elsif ($messageid == ''
382             || $rec{$signature}->{'messageid'} != $messageid
383             || $CONFIG{'HISTORY_DUPLICATES'} ne "no") {
384
385      # skip unwanted messages
386      next if ($class ne "S" && $show eq "spam");
387      next if ($class ne "I" && $show eq "innocent");
388      next if ($class ne "W" && $show eq "whitelisted");
389      next if ($class ne "V" && $show eq "virus");
390      next if ($class ne "A" && $show eq "blacklisted");
391      next if ($class ne "O" && $show eq "blocklisted");
392
393      $rec{$signature}->{'time'} = $time;
394      $rec{$signature}->{'class'} = $class;
395      $rec{$signature}->{'from'} = $from;
396      $rec{$signature}->{'signature'} = $signature;
397      $rec{$signature}->{'subject'} = $subject;
398      $rec{$signature}->{'info'} = $info;
399      $rec{$signature}->{'messageid'} = $messageid;
400
401      unshift(@buffer, $rec{$signature});
402    }
403  }
404  close(LOG);
405
406  if($CONFIG{'HISTORY_SIZE'} < ($#buffer+1)) {
407    $history_pages = int($CONFIG{'HISTORY_SIZE'} / $CONFIG{'HISTORY_PER_PAGE'});
408    $history_pages += 1 if($CONFIG{'HISTORY_SIZE'} % $CONFIG{'HISTORY_PER_PAGE'});
409  } else {
410    $history_pages = int( ($#buffer+1) / $CONFIG{'HISTORY_PER_PAGE'});
411    $history_pages += 1 if(($#buffer+1) % $CONFIG{'HISTORY_PER_PAGE'});
412  }
413  $begin = int(($history_page - 1) * $CONFIG{'HISTORY_PER_PAGE'}) ;
414
415  # Now lets just keep the information that we really need.
416  @buffer = splice(@buffer, $begin,$CONFIG{'HISTORY_PER_PAGE'});
417
418  my $retrain_checked_msg_no = 0;
419  my $counter = 0;
420  while ($rec = pop@buffer) {
421    $counter++;
422    my($time, $class, $from, $signature, $subject, $info, $messageid);
423
424    $time = $rec->{'time'};
425    $class = $rec->{'class'};
426    $from = $rec->{'from'};
427    $signature = $rec->{'signature'};
428    $subject = $rec->{'subject'};
429    $info = $rec->{'info'};
430    $messageid = $rec->{'messageid'};
431
432    next if ($signature eq "");
433    next if ($rec{$signature}->{'displayed'} ne "");
434    next if ($class eq "E");
435    $rec{$signature}->{'displayed'} = 1;
436
437    # Resends of retrained messages will need the original from/subject line
438    if ($messageid ne "") {
439      $from = $rec{$messageid}->{'from'}
440        if ($from eq "<None Specified>");
441      $subject = $rec{$messageid}->{'subject'}
442        if ($subject eq "<None Specified>");
443
444      $rec{$messageid}->{'from'} = $from
445        if ($rec{$messageid}->{'from'} eq "");
446      $rec{$messageid}->{'subject'} = $subject
447        if ($rec{$messageid}->{'subject'} eq "");
448    }
449
450    $from = "<None Specified>" if ($from eq "");
451    $subject = "<None Specified>" if ($subject eq "");
452
453    my $ctime;
454    if($CONFIG{"DATE_FORMAT"}) {
455      $ctime = strftime($CONFIG{"DATE_FORMAT"}, localtime($time));
456    } else {
457      $ctime = ctime($time);
458      my(@t) = split(/\:/, (split(/\s+/, $ctime))[3]);
459      my($x) = (split(/\s+/, $ctime))[0];
460      my($m) = "a";
461      if ($t[0]>=12) { $t[0] -= 12; $m = "p"; }
462      if ($t[0] == 0) { $t[0] = 12; }
463      $ctime = "$x $t[0]:$t[1]$m";
464    }
465
466    # Set the appropriate type and label for this message
467
468    my($cl, $cllabel);
469    $class = $rec{$signature}->{'class'} if ($rec{$signature}->{'class'} ne "");
470    if ($class eq "S") { $cl = "spam"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_spam'}"; }
471    elsif ($class eq "I") { $cl = "innocent"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_innocent'}"; }
472    elsif ($class eq "F") {
473      if ($rec{$signature}->{'count'} % 2 != 0) {
474        $cl = "false"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_miss'}";
475      } else {
476        $cl = "innocent"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_innocent'}";
477      }
478    }
479    elsif ($class eq "M") {
480      if ($rec{$signature}->{'count'} % 2 != 0) {
481          $cl = "missed"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_miss'}";
482      } else {
483          $cl = "spam"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_spam'}";
484      }
485    }
486    elsif ($class eq "W") { $cl = "whitelisted"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_whitelist'}"; }
487    elsif ($class eq "V") { $cl = "virus"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_virus'}"; }
488    elsif ($class eq "A") { $cl = "blacklisted"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_rbl'}"; }
489    elsif ($class eq "O") { $cl = "blocklisted"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_block'}"; }
490    elsif ($class eq "N") { $cl = "inoculation"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_spam'}"; }
491    elsif ($class eq "C") { $cl = "corpus"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_corpus'}"; }
492    elsif ($class eq "U") { $cl = "unknown"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_unknown'}"; }
493    elsif ($class eq "E") { $cl = "error"; $cllabel="$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_error'}"; }
494    if ($messageid ne "" && $messageid ne "1") {
495      if ($rec{$messageid}->{'resend'} ne "") {
496        $cl = "relay";
497        $cllabel = "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_label_resend'}";
498      }
499      $rec{$messageid}->{'resend'} = $signature;
500    }
501
502    $info = $rec{$signature}->{'info'} if ($rec{$signature}->{'info'} ne "");
503
504    $from = substr($from, 0, $CONFIG{'MAX_COL_LEN'} - 3) . "..." if (length($from)>$CONFIG{'MAX_COL_LEN'});
505    $subject = substr($subject, 0, $CONFIG{'MAX_COL_LEN'} - 3) . "..." if (length($subject)>$CONFIG{'MAX_COL_LEN'});
506
507    $from =~ s/&/&amp;/g;
508    $from =~ s/</&lt;/g;
509    $from =~ s/>/&gt;/g;
510    $from =~ s/"/&quot;/g;
511    $from =~ s/'/&#39;/g;       # MSIE doesn't know "&apos;"
512
513    $subject =~ s/&/&amp;/g;
514    $subject =~ s/</&lt;/g;
515    $subject =~ s/>/&gt;/g;
516    $subject =~ s/"/&quot;/g;
517    $subject =~ s/'/&#39;/g;    # MSIE doesn't know "&apos;"
518
519    my($rclass);
520    $rclass = "spam" if ($class eq "I" || $class eq "W" || $class eq "F");
521    $rclass = "innocent" if ($class eq "S" || $class eq "M" || $class eq "V" || $class eq "A" || $class eq "O");
522
523    my($retrain);
524    if ($rec{$signature}->{'class'} =~ /^(M|F)$/ && $rec{$signature}->{'count'} % 2 != 0) {
525      $retrain = "<b>$CONFIG{'LANG'}->{$LANGUAGE}->{'history_retrained'}</b>";
526    }
527
528    if ($retrain eq "") {
529      $retrain = qq!<A HREF="$MYURL&amp;show=$show&amp;history_page=$history_page&amp;retrain=$rclass&amp;signatureID=$signature">$CONFIG{'LANG'}->{$LANGUAGE}->{'history_retrain_as'}&nbsp;! . ucfirst($CONFIG{'LANG'}->{$LANGUAGE}->{'history_retrain_as_'.$rclass}) . "</A>";
530    } else {
531      $retrain .= qq! (<A HREF="$MYURL&amp;show=$show&amp;history_page=$history_page&amp;retrain=$rclass&amp;signatureID=$signature">$CONFIG{'LANG'}->{$LANGUAGE}->{'history_retrain_undo'}</A>)!;
532    }
533
534    my($path) = "$USER.frag/$signature.frag";
535    if (-e $path) {
536      my(%pairs);
537      $pairs{'template'} = "fragment";
538      $pairs{'signatureID'} = $signature;
539      my($sub) = $subject;
540      $sub =~ s/#//g;
541      $sub =~ s/(['])/\\$1/g;
542      $pairs{'subject'} = $sub;
543      $pairs{'from'} = $from;
544      $pairs{'info'} = $info;
545      $pairs{'time'} = $ctime;
546      $pairs{'user'} = $FORM{'user'};
547      my($url) = &SafeVars(%pairs);
548      $from = qq!<a href="javascript:openwin(580,400,1,'$CONFIG{'ME'}?$url')">$from</a>!;
549    }
550
551    my $retrain_action = "";
552    if ( $class eq "V" || $class eq "A" || $class eq "O" || $class eq "U" || $class eq "") {
553      $retrain_action = qq!<small>&nbsp;</small>!;
554    } else {
555      $retrain_action = qq!<input name="msgid$retrain_checked_msg_no" type="checkbox" value="$rclass:$signature" id="checkbox-$counter" onclick="checkboxclicked(this)"><small>$retrain</small>!;
556    }
557
558    # HTMLize special characters
559    if ($CONFIG{'HISTORY_HTMLIZE'} eq "yes") {
560      $from=htmlize($from);
561      $subject=htmlize($subject);
562    }
563
564    my($entry) = <<_END;
565<tr>
566 <td class="$cl $rowclass" nowrap="nowrap"><small>$cllabel</small></td>
567 <td class="$rowclass" nowrap="nowrap">$retrain_action</td>
568 <td class="$rowclass" nowrap="nowrap"><small>$ctime</small></td>
569 <td class="$rowclass" nowrap="nowrap"><small>$from</small></td>
570 <td class="$rowclass" nowrap="nowrap"><small>$subject</small></td>
571 <td class="$rowclass" nowrap="nowrap"><small>$info</small></td>
572</tr>
573_END
574
575    $retrain_checked_msg_no++;
576    push(@history, $entry);
577
578    if ($rowclass eq "rowEven") {
579      $rowclass = "rowOdd";
580    } else {
581      $rowclass = "rowEven";
582    }
583
584  }
585
586  while($line = pop(@history)) { $DATA{'HISTORY'} .= $line; }
587
588  $DATA{'HISTORYPAGES'} = qq!<input name="history_page" type="hidden" value="$history_page">!;
589
590  if ($CONFIG{'HISTORY_PER_PAGE'} > 0) {
591    $DATA{'HISTORYPAGES'} .= "<div class=\"historypages\">";
592    $DATA{'HISTORYPAGES'} .= "[" if ($history_pages > 0);
593    if (($history_pages > 1) && ($history_page > 1)) {
594      my $i = $history_page-1;
595      $DATA{'HISTORYPAGES'} .= "<a href=\"$MYURL&amp;show=$show&amp;history_page=$i\">&nbsp;&lt;&nbsp;</a>";
596    }
597    for(my $i = 1; $i <= $history_pages; $i++) {
598      $DATA{'HISTORYPAGES'} .= "<a href=\"$MYURL&amp;show=$show&amp;history_page=$i\">";
599      $DATA{'HISTORYPAGES'} .= "<big><strong>" if ($i == $history_page);
600      $DATA{'HISTORYPAGES'} .= "&nbsp;$i&nbsp;";
601      $DATA{'HISTORYPAGES'} .= "</strong></big>" if ($i == $history_page);
602      $DATA{'HISTORYPAGES'} .= "</a>";
603    }
604    if (($history_pages > 1) && ($history_page < $history_pages)) {
605      my $i = $history_page+1;
606      $DATA{'HISTORYPAGES'} .= "<a href=\"$MYURL&amp;show=$show&amp;history_page=$i\">&nbsp;&gt;&nbsp;</a>";
607    }
608    $DATA{'HISTORYPAGES'} .= "]" if ($history_pages > 0);
609    $DATA{'HISTORYPAGES'} .= "</div>";
610  }
611
612  $DATA{'SHOW'} = $show;
613  $DATA{'SHOW_SELECTOR'} .=  "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show'}: <a href=\"$MYURL&amp;show=all\">";
614  if ($show eq "all") {
615    $DATA{'SHOW_SELECTOR'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_all'}</strong>";
616  } else {
617    $DATA{'SHOW_SELECTOR'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_all'}";
618  }
619  $DATA{'SHOW_SELECTOR'} .=  "</a> | <a href=\"$MYURL&amp;show=spam\">";
620  if ($show eq "spam") {
621    $DATA{'SHOW_SELECTOR'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_spam'}</strong>";
622  } else {
623    $DATA{'SHOW_SELECTOR'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_spam'}";
624  }
625  $DATA{'SHOW_SELECTOR'} .=  "</a> | <a href=\"$MYURL&amp;show=innocent\">";
626  if ($show eq "innocent") {
627    $DATA{'SHOW_SELECTOR'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_innocent'}</strong>";
628  } else {
629    $DATA{'SHOW_SELECTOR'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_innocent'}";
630  }
631  $DATA{'SHOW_SELECTOR'} .=  "</a> | <a href=\"$MYURL&amp;show=whitelisted\">";
632  if ($show eq "whitelisted") {
633    $DATA{'SHOW_SELECTOR'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_whitelisted'}</strong>";
634  } else {
635    $DATA{'SHOW_SELECTOR'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'history_show_whitelisted'}";
636  }
637  $DATA{'SHOW_SELECTOR'} .=  "</a>";
638
639  &output(%DATA);
640}
641
642#
643# Analysis Functions
644#
645
646sub DisplayAnalysis {
647  my($LOG) = "$USER.log";
648
649  my %Stats=(
650    daily       => {},
651    weekly      => {},
652  );
653
654  my ($min, $hour, $mday, $mon, $year) = (localtime(time))[1,2,3,4,5];
655  my ($daystart) = timelocal(0, 0, 0, $mday, $mon, $year);
656  my ($periodstart) = $daystart - (3600*24*13); # 2 Weeks ago
657  my ($dailystart) = time - (3600*23);
658
659  if (! -e $LOG) {
660    &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_historic'}");
661  }
662
663  open(LOG, "<$LOG") || &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_cannot_open_log'}: $!");
664  while(<LOG>) {
665    my($t_log, $c_log) = split(/\t/);
666
667    # Only Parse Log Data in our Time Period
668    if ($t_log>=$periodstart) {
669      my($tmin, $thour, $tday, $tmon) = (localtime($t_log))[1,2,3,4];
670      $tmon++;
671
672      foreach my $period (qw( weekly daily )) {
673        my $idx;
674        if ($period eq "weekly") {
675          $idx="$tmon/$tday";
676        } else {
677          ($t_log>=$dailystart) || next;
678          $idx=To12Hour($thour);
679        }
680        if (!exists $Stats{$period}->{$idx}) {
681          $Stats{$period}->{$idx}={
682                nonspam =>      0,
683                spam    =>      0,
684                title   =>      $idx,
685                idx     =>      $t_log,
686          };
687        }
688        my $hr=$Stats{$period}->{$idx};
689        if ($c_log eq "S" || $c_log eq "V" || $c_log eq "A" || $c_log eq "O") {
690          $hr->{spam}++;
691        }
692        if ($c_log eq "I" || $c_log eq "W") {
693          $hr->{nonspam}++;
694        }
695        if ($c_log eq "F") {
696          $hr->{spam}--;
697          ($hr->{spam}<0) && ($hr->{spam}=0);
698          $hr->{nonspam}++;
699        }
700        if ($c_log eq "M") {
701          $hr->{nonspam}--;
702          ($hr->{nonspam}<0) && ($hr->{nonspam}=0);
703          $hr->{spam}++;
704        }
705      }
706    }
707  }
708  close(LOG);
709
710  foreach my $period (qw( daily weekly )) {
711    my $uc_period=uc($period);
712    my $hk="DATA_$uc_period";
713    my %lst=(spam => [], nonspam => [], title => []);
714    foreach my $hr (sort {$a->{idx}<=>$b->{idx}} (values %{$Stats{$period}})) {
715      foreach my $type (qw( spam nonspam title )) {
716        push(@{$lst{$type}},$hr->{$type});
717        my $totk="";
718        if ($type eq "spam") { $totk="S"; }
719        elsif ($type eq "nonspam") { $totk="I"; }
720        ($totk eq "") && next;
721        my $sk="T${totk}_$uc_period";
722        (exists $DATA{$sk}) || ($DATA{$sk}=0);
723        $DATA{$sk}+=$hr->{$type};
724      }
725    }
726    foreach my $type (qw( spam nonspam title )) {
727      (exists $lst{$type}) || ($lst{$type}=[]);
728      @{$lst{$type}} = (0) if (scalar(@{$lst{$type}}) == 0);
729    }
730    $DATA{$hk}=join("_",
731                join(",",@{$lst{spam}}),
732                join(",",@{$lst{nonspam}}),
733                join(",",@{$lst{title}}),
734        );
735  }
736
737  &output(%DATA);
738}
739
740#
741# Preferences Functions
742#
743
744sub DisplayPreferences {
745  my(%PREFS);
746  my($FILE) = "$USER.prefs";
747
748  my $username = $CURRENT_USER;
749
750  if ($FORM{'submit'} ne "") {
751
752    if ($FORM{'enableBNR'} ne "on") {
753      $FORM{'enableBNR'} = "off";
754    }
755
756    if ($FORM{'optIn'} ne "on") {
757      $FORM{'optIn'} = "off";
758    }
759
760    if ($FORM{'optOut'} ne "on") {
761      $FORM{'optOut'} = "off";
762    }
763
764    if ($FORM{'showFactors'} ne "on") {
765      $FORM{'showFactors'} = "off";
766    }
767                                                                               
768    if ($FORM{'enableWhitelist'} ne "on") {
769      $FORM{'enableWhitelist'} = "off";
770    }
771    if ($FORM{'dailyQuarantineSummary'} ne "on") {
772      $FORM{'dailyQuarantineSummary'} = "off";
773    }
774
775    if ($CONFIG{'PREFERENCES_EXTENSION'} == 1) {
776
777      if ($FORM{'spamSubject'} eq "") {
778        $FORM{'spamSubject'} = "''";
779      } else {
780        $FORM{'spamSubject'} = quotemeta($FORM{'spamSubject'});
781      }
782
783      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
784        " trainingMode " . quotemeta($FORM{'trainingMode'}) . " > /dev/null");
785      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
786        " spamAction " . quotemeta($FORM{'spamAction'}) . " > /dev/null");
787      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
788        " signatureLocation "
789        . quotemeta($FORM{'signatureLocation'}) . " > /dev/null");
790      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
791        " spamSubject " . $FORM{'spamSubject'} . " > /dev/null");
792      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
793        " statisticalSedation "
794        . quotemeta($FORM{'statisticalSedation'}) . " > /dev/null");
795      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
796        " enableBNR "
797        . quotemeta($FORM{'enableBNR'}) . "> /dev/null");
798      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
799        " optOut "
800        . quotemeta($FORM{'optOut'}) . ">/dev/null");
801      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
802        " optIn "
803        . quotemeta($FORM{'optIn'}) . ">/dev/null");
804      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
805        " showFactors "
806        . quotemeta($FORM{'showFactors'}) . "> /dev/null");
807      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
808        " enableWhitelist "
809        . quotemeta($FORM{'enableWhitelist'}) . "> /dev/null");
810      system("$CONFIG{'DSPAM_BIN'}/dspam_admin ch pref ".quotemeta($CURRENT_USER).
811        " dailyQuarantineSummary "
812        . quotemeta($FORM{'dailyQuarantineSummary'}) . "> /dev/null");
813
814
815    } else {
816      open(FILE, ">$FILE") || do { &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_cannot_write_prefs'}: $!"); };
817      print FILE <<_END;
818trainingMode=$FORM{'trainingMode'}
819spamAction=$FORM{'spamAction'}
820spamSubject=$FORM{'spamSubject'}
821statisticalSedation=$FORM{'statisticalSedation'}
822enableBNR=$FORM{'enableBNR'}
823optIn=$FORM{'optIn'}
824optOut=$FORM{'optOut'}
825showFactors=$FORM{'showFactors'}
826enableWhitelist=$FORM{'enableWhitelist'}
827signatureLocation=$FORM{'signatureLocation'}
828dailyQuarantineSummary=$FORM{'dailyQuarantineSummary'}
829_END
830      close(FILE);
831    }
832  redirect("$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=preferences&amp;language=$LANGUAGE");
833  }
834
835  %PREFS = GetPrefs();
836
837  $DATA{"SEDATION_$PREFS{'statisticalSedation'}"} = "CHECKED";
838  $DATA{"S_".$PREFS{'trainingMode'}} = "CHECKED";
839  $DATA{"S_ACTION_".uc($PREFS{'spamAction'})} = "CHECKED";
840  $DATA{"S_LOC_".uc($PREFS{'signatureLocation'})} = "CHECKED";
841  $DATA{"SPAM_SUBJECT"} = $PREFS{'spamSubject'};
842  if ($PREFS{'optIn'} eq "on") {
843    $DATA{'C_OPTIN'} = "CHECKED";
844  }
845  if ($PREFS{'optOut'} eq "on") {
846    $DATA{'C_OPTOUT'} = "CHECKED";
847  }
848  if ($PREFS{"enableBNR"} eq "on") {
849    $DATA{"C_BNR"} = "CHECKED";
850  }
851  if ($PREFS{"showFactors"} eq "on") {
852    $DATA{"C_FACTORS"} = "CHECKED";
853  }
854  if ($PREFS{"enableWhitelist"} eq "on") {
855    $DATA{"C_WHITELIST"} = "CHECKED";
856  }
857  if ($PREFS{"dailyQuarantineSummary"} eq "on") {
858    $DATA{"C_SUMMARY"} = "CHECKED";
859  }
860
861  if ($CONFIG{'OPTMODE'} eq "OUT") {
862    $DATA{"OPTION"} = "<INPUT TYPE=CHECKBOX NAME=optOut " . $DATA{'C_OPTOUT'} . ">$CONFIG{'LANG'}->{$LANGUAGE}->{'option_disable_filtering'}<br>";
863  } elsif ($CONFIG{'OPTMODE'} eq "IN") {
864    $DATA{"OPTION"} = "<INPUT TYPE=CHECKBOX NAME=optIn " . $DATA{'C_OPTIN'} . ">$CONFIG{'LANG'}->{$LANGUAGE}->{'option_enable_filtering'}<br>";
865  } else {
866    $DATA{"OPTION"} = "";
867  }
868
869  &output(%DATA);
870}
871
872#
873# Quarantine Functions
874#
875
876sub ProcessQuarantine {
877  if ($FORM{'manyNotSpam'} ne "") {
878    &Quarantine_ManyNotSpam;
879  } else {
880    &Quarantine_DeleteSpam;
881  }
882  &CheckQuarantine;
883  return;
884}
885
886sub ProcessFalsePositive {
887  my(@buffer, %head, $found);
888  if ($FORM{'signatureID'} eq "") {
889    &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_sigid'}");
890  }
891  open(FILE, "<$MAILBOX");
892  while(<FILE>) {
893    s/\r?\n$//;
894    push(@buffer, $_);
895  }
896  close(FILE);
897
898  while($#buffer>=0) {
899    my($buff, $mode, @temp);
900    $mode = 0;
901    @temp = ();
902    while(($buff !~ /^From /) && ($#buffer>=0)) {
903      $buff = $buffer[0];
904      if ($buff =~ /^From /) {
905        if ($mode == 0) { $mode = 1; }
906        else { next; }
907      }
908      $buff = shift(@buffer);
909      if ($buff !~ /^From /) {
910        push(@temp, $buff);
911      }
912      next;
913    }
914    foreach(@temp) {
915      last if ($_ eq "");
916      my($key, $val) = split(/\: ?/, $_, 2);
917      $head{$key} = $val;
918    }
919    if ($head{'X-DSPAM-Signature'} eq $FORM{'signatureID'}) {
920      $found = 1;
921      open(PIPE, "|$CONFIG{'DSPAM'} $CONFIG{'DSPAM_ARGS'} --rcpt-to $head{'X-DSPAM-Recipient'} >$TMPFILE 2>&1") || &error($!);
922      foreach(@temp) {
923        print PIPE "$_\n";
924      }
925      close(PIPE);
926    }
927  }
928
929  # Couldn't find the message, so just retrain on signature
930  if (!$found) {
931    system("$CONFIG{'DSPAM'} --source=error --class=innocent --signature=" . quotemeta($FORM{'signatureID'}) . " --user " . quotemeta("$CURRENT_USER"));
932  }
933
934  if ($?) {
935    my(@log);
936    open(LOG, "<$TMPFILE");
937    @log = <LOG>;
938    close(LOG);
939    unlink("$TMPFILE");
940    &error("<PRE>".join('', @log)."</PRE>");
941  }
942
943  unlink("$TMPFILE");
944  $FORM{$FORM{'signatureID'}} = "on";
945  &Quarantine_DeleteSpam();
946  return;
947}
948
949sub Quarantine_ManyNotSpam {
950  my(@buffer, @errors);
951
952  open(FILE, "<$MAILBOX");
953  while(<FILE>) {
954    s/\r?\n$//;
955    push(@buffer, $_);
956  }
957  close(FILE);
958
959  open(FILE, ">$MAILBOX") || &error($!);
960  open(RETRAIN, ">>$USER.retrain.log");
961
962  while($#buffer>=0) {
963    my($buff, $mode, @temp, %head, $delivered);
964    $mode = 0;
965    while(($buff !~ /^From /) && ($#buffer>=0)) {
966      $buff = $buffer[0];
967      if ($buff =~ /^From /) {
968        if ($mode == 0) {
969          $mode = 1;
970          $buff = shift(@buffer);
971          push(@temp, $buff);
972          $buff = "";
973          next;
974        } else {
975          next;
976        }
977      }
978      $buff = shift(@buffer);
979      push(@temp, $buff);
980      next;
981    }
982    foreach(@temp) {
983      last if ($_ eq "");
984      my($key, $val) = split(/\: ?/, $_, 2);
985      $head{$key} = $val;
986    }
987    $delivered = 0;
988    if ($FORM{$head{'X-DSPAM-Signature'}} ne "") {
989      my($err);
990      $err = &Deliver(@temp);
991      if ($err eq "") {
992        $delivered = 1;
993      } else {
994        push(@errors, $err);
995      }
996    }
997    if (!$delivered) {
998      foreach(@temp) {
999        print FILE "$_\n";
1000      }
1001    } else {
1002      print RETRAIN time . "\t$head{'X-DSPAM-Signature'}\tinnocent\n";
1003    }
1004  }
1005  close(RETRAIN);
1006  close(FILE);
1007  if (@errors > 0) {
1008    &error(join("<BR>", @errors));
1009  }
1010  return;
1011}
1012
1013sub Deliver {
1014  my(@temp) = @_;
1015  #determine original recipient
1016  my $recipient;
1017  foreach(@temp) {
1018        if ( /^X-DSPAM-Recipient: .*/ )
1019        {
1020                ($recipient) = /^X-DSPAM-Recipient: (.*)/;
1021        }
1022  }
1023 
1024  open(PIPE, "|$CONFIG{'DSPAM'} $CONFIG{'DSPAM_ARGS'} --rcpt-to $recipient") || return $!;
1025  foreach(@temp) {
1026    print PIPE "$_\n" || return $!;
1027  }
1028  close(PIPE) || return $!;
1029  return "";
1030}
1031
1032sub Quarantine_ViewMessage {
1033  my(@buffer);
1034
1035  if ($FORM{'signatureID'} eq "") {
1036    &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_sigid'}");
1037  }
1038
1039  $DATA{'MESSAGE_ID'} = $FORM{'signatureID'};
1040
1041  open(FILE, "<$MAILBOX");
1042  while(<FILE>) {
1043    s/\r?\n//;
1044    push(@buffer, $_);
1045  }
1046  close(FILE);
1047
1048  while($#buffer>=0) {
1049    my($buff, $mode, @temp, %head);
1050    $mode = 0;
1051    @temp = ();
1052    while(($buff !~ /^From /) && ($#buffer>=0)) {
1053      $buff = $buffer[0];
1054      if ($buff =~ /^From /) {
1055        if ($mode == 0) { $mode = 1; }
1056        else { next; }
1057      }
1058      $buff = shift(@buffer);
1059      if ($buff !~ /^From /) {
1060        push(@temp, $buff);
1061      }
1062      next;
1063    }
1064    foreach(@temp) {
1065      last if ($_ eq "");
1066      my($key, $val) = split(/\: ?/, $_, 2);
1067      $head{$key} = $val;
1068    }
1069    if ($head{'X-DSPAM-Signature'} eq $FORM{'signatureID'}) {
1070      foreach(@temp) {
1071        s/</\&lt\;/g;
1072        s/>/\&gt\;/g;
1073        $DATA{'MESSAGE'} .= "$_\n";
1074      }
1075    }
1076  }
1077  $FORM{'template'} = "viewmessage";
1078  &output(%DATA);
1079}
1080
1081sub Quarantine_DeleteSpam {
1082  my(@buffer);
1083  if ($FORM{'deleteAll'} ne "") {
1084    my($sz);
1085
1086    my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1087     $atime,$mtime,$ctime,$blksize,$blocks)
1088     = stat("$USER.mbox");
1089                                                                               
1090    open(FILE, "<$USER.mbox.size");
1091    $sz = <FILE>;
1092    close(FILE);
1093    chomp($sz);
1094
1095    if ($sz == $size) {
1096      open(FILE, ">$MAILBOX");
1097      close(FILE);
1098      unlink("$USER.mbox.size");
1099      unlink("$USER.mboxwarn");
1100    } else {
1101      return;
1102    }
1103
1104    $FORM{'template'} = "performance";
1105    &CheckQuarantine;
1106    redirect("$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=$FORM{'template'}&amp;language=$LANGUAGE");
1107    return;
1108  }
1109  open(FILE, "<$MAILBOX");
1110  while(<FILE>) {
1111    s/\r?\n//;
1112    push(@buffer, $_);
1113  }
1114  close(FILE);
1115 
1116
1117  open(FILE, ">$MAILBOX");
1118 
1119  while($#buffer>=0) {
1120    my($buff, $mode, @temp, %head);
1121    $mode = 0;
1122    while(($buff !~ /^From /) && ($#buffer>=0)) {
1123      $buff = $buffer[0];
1124      if ($buff =~ /^From /) {
1125        if ($mode == 0) {
1126          $mode = 1;
1127          $buff = shift(@buffer);
1128          push(@temp, $buff);
1129          $buff = "";
1130          next;
1131        } else {
1132          next;
1133        }
1134      }
1135      $buff = shift(@buffer);
1136      push(@temp, $buff);
1137      next;
1138    }
1139    foreach(@temp) {
1140      last if ($_ eq "");
1141      my($key, $val) = split(/\: ?/, $_, 2);
1142      $head{$key} = $val;
1143    }
1144    if ($FORM{$head{'X-DSPAM-Signature'}} eq "") {
1145      foreach(@temp) {
1146        print FILE "$_\n";
1147      }
1148    }
1149  }
1150  close(FILE);
1151  return;
1152}
1153
1154sub by_rating { $a->{'rating'} <=> $b->{'rating'} }
1155sub by_subject { lc($a->{'Subject'}) cmp lc($b->{'Subject'}) }
1156sub by_from { lc($a->{'From'}) cmp lc($b->{'From'}) }
1157
1158sub DisplayQuarantine {
1159  my(@alerts);
1160
1161  my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1162   $atime,$mtime,$ctime,$blksize,$blocks)
1163   = stat("$USER.mbox");
1164
1165  open(FILE, ">$USER.mbox.size");
1166  print(FILE "$size");
1167  close(FILE);
1168
1169  open(FILE, ">$MAILBOX.stamp");
1170  close(FILE);
1171  chmod 0660, "$MAILBOX.stamp";
1172
1173  open(FILE, "<$USER.alerts");
1174  while(<FILE>) {
1175    chomp;
1176    push(@alerts, $_);
1177  }
1178  close(FILE);
1179
1180  my($next, @buffer, $rowclass, $mode, $markclass, $marklabel, @headings);
1181  $rowclass="rowEven";
1182  open(FILE, "<$MAILBOX");
1183  while(<FILE>) {
1184    s/\r?\n//;
1185    if ($_ ne "") {
1186      if ($mode eq "") {
1187        if ($_ =~ /^From /) {
1188          $mode = 1;
1189        } else {
1190          next;
1191        }
1192      }
1193      push(@buffer, $_);
1194      next;
1195    }
1196    next if ($mode eq "");
1197
1198    my($new, $start, $alert);
1199    $alert = 0;
1200    $new = {};
1201    foreach(@buffer) {
1202      my($al);
1203      foreach $al (@alerts) {
1204        if (/$al/i) {
1205          $alert = 1;
1206        }
1207      }
1208      if ($_ =~ /^From /) {
1209        my(@a) = split(/ /, $_);
1210        my($x) = 2;
1211        for (0..$#a) {
1212          if (($a[$_] =~ /\@|>/) && ($_>$x)) {
1213            $x = $_ + 1;
1214          }
1215        }
1216        for(1..$x) { shift(@a); }
1217        $start = join(" ", @a);
1218      } else {
1219        my($key, $val) = split(/\: ?/, $_, 2);
1220#       print("found $key=$val<br>");
1221        $new->{$key} = $val;
1222      }
1223    }
1224    if ($rowclass eq "rowEven") {
1225      $rowclass = "rowOdd";
1226    } else {
1227      $rowclass = "rowEven";
1228    }
1229
1230    $new->{'alert'} = $alert;
1231
1232    if ($alert) { $rowclass="rowAlert"; }
1233
1234    # HTMLize special characters
1235    if ($CONFIG{'QUARANTINE_HTMLIZE'} eq "yes") {
1236      $new->{'Subject'} = htmlize($new->{'Subject'});
1237      $new->{'From'} = htmlize($new->{'From'});
1238    }
1239
1240    $new->{'Sub2'} = $new->{'X-DSPAM-Signature'};
1241    if (length($new->{'Subject'})>$CONFIG{'MAX_COL_LEN'}) {
1242      $new->{'Subject'} = substr($new->{'Subject'}, 0, $CONFIG{'MAX_COL_LEN'} - 3) . "...";
1243    }
1244 
1245    if (length($new->{'From'})>$CONFIG{'MAX_COL_LEN'}) {
1246      $new->{'From'} = substr($new->{'From'}, 0, $CONFIG{'MAX_COL_LEN'} - 3) . "...";
1247    }
1248
1249    if ($new->{'Subject'} eq "") {
1250      $new->{'Subject'} = "<None Specified>";
1251    }
1252
1253#   $new->{'rating'} = $new->{'X-DSPAM-Probability'} * $new->{'X-DSPAM-Confidence'};
1254    $new->{'rating'} = $new->{'X-DSPAM-Confidence'};
1255
1256    foreach(keys(%$new)) {
1257      next if ($_ eq "X-DSPAM-Signature");
1258      $new->{$_} =~ s/</\&lt\;/g;
1259      $new->{$_} =~ s/>/\&gt\;/g;
1260    }
1261
1262    push(@headings, $new);
1263
1264    @buffer = ();
1265    $mode = "";
1266    next;
1267  }
1268
1269  my($sortBy) = $FORM{'sortby'};
1270  if ($sortBy eq "") {
1271    $sortBy = $CONFIG{'SORT_DEFAULT'};
1272  }
1273  if ($sortBy eq "Rating") {
1274    @headings = sort by_rating @headings;
1275  }
1276  if ($sortBy eq "Subject") {
1277    @headings = sort by_subject @headings;
1278  }
1279  if ($sortBy eq "From") {
1280    @headings = sort by_from @headings;
1281  }
1282  if ($sortBy eq "Date") {
1283    @headings = reverse @headings;
1284  }
1285
1286  $DATA{'SORTBY'} = $sortBy;
1287  $DATA{'SORT_QUARANTINE'} .=  "<th><a href=\"$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=quarantine&amp;language=$LANGUAGE&amp;sortby=Rating&amp;user=$FORM{'user'}\">";
1288  if ($sortBy eq "Rating") {
1289    $DATA{'SORT_QUARANTINE'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_rating'}</strong>";
1290  } else {
1291    $DATA{'SORT_QUARANTINE'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_rating'}";
1292  }
1293  $DATA{'SORT_QUARANTINE'} .=  "</a></th>\n\t<th><a href=\"$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=quarantine&amp;language=$LANGUAGE&amp;sortby=Date&amp;user=$FORM{'user'}\">";
1294  if ($sortBy eq "Date") {
1295    $DATA{'SORT_QUARANTINE'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_date'}</strong>";
1296  } else {
1297    $DATA{'SORT_QUARANTINE'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_date'}";
1298  }
1299  $DATA{'SORT_QUARANTINE'} .=  "</a></th>\n\t<th><a href=\"$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=quarantine&amp;language=$LANGUAGE&amp;sortby=From&amp;user=$FORM{'user'}\">";
1300  if ($sortBy eq "From") {
1301    $DATA{'SORT_QUARANTINE'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_from'}</strong>";
1302  } else {
1303    $DATA{'SORT_QUARANTINE'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_from'}";
1304  }
1305  $DATA{'SORT_QUARANTINE'} .=  "</a></th>\n\t<th><a href=\"$CONFIG{'ME'}?user=$FORM{'user'}&amp;template=quarantine&amp;language=$LANGUAGE&amp;sortby=Subject&amp;user=$FORM{'user'}\">";
1306  if ($sortBy eq "Subject") {
1307    $DATA{'SORT_QUARANTINE'} .= "<strong>$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_subject'}</strong>";
1308  } else {
1309    $DATA{'SORT_QUARANTINE'} .= "$CONFIG{'LANG'}->{$LANGUAGE}->{'quarantine_subject'}";
1310  }
1311  $DATA{'SORT_QUARANTINE'} .=  "</a></th>\n\t<th>To</th>";
1312
1313
1314  my($row, $rowclass, $counter);
1315  $rowclass = "rowEven";
1316  $counter = 0;
1317  for $row (@headings) {
1318    $counter++;
1319    my($rating, $url, $markclass, $outclass);
1320    $rating = sprintf("%3.0f%%", $row->{'rating'} * 100.0);
1321    if ($row->{'rating'} > 0.8) {
1322        $markclass = "high";
1323    } else {
1324        if ($row->{'rating'} < 0.7) {
1325            $markclass = "low";
1326        } else {
1327            $markclass = "medium";
1328        }
1329    }
1330
1331    my(%PAIRS);
1332    $PAIRS{'signatureID'} = $row->{'X-DSPAM-Signature'};
1333    $PAIRS{'command'}  = "viewMessage";
1334    $PAIRS{'user'} = $CURRENT_USER;
1335    $PAIRS{'template'} = "quarantine";
1336    $url = &SafeVars(%PAIRS);
1337
1338    if ($row->{'alert'}) {
1339      $outclass = "rowAlert";
1340    } else {
1341      $outclass = $rowclass;
1342    }
1343
1344    my(@ptfields) = split(/\s+/, $row->{'X-DSPAM-Processed'});
1345    my(@times) = split(/\:/, $ptfields[3]);
1346    my($ptime);
1347    if($CONFIG{"DATE_FORMAT"}) {
1348      my($month);
1349      $month->{'Jan'}=0;
1350      $month->{'Feb'}=1;
1351      $month->{'Mar'}=2;
1352      $month->{'Apr'}=3;
1353      $month->{'May'}=4;
1354      $month->{'Jun'}=5;
1355      $month->{'Jul'}=6;
1356      $month->{'Aug'}=7;
1357      $month->{'Sep'}=8;
1358      $month->{'Oct'}=9;
1359      $month->{'Nov'}=10;
1360      $month->{'Dec'}=11;
1361      $ptime = strftime($CONFIG{"DATE_FORMAT"}, $times[2],$times[1],$times[0],$ptfields[2],$month->{$ptfields[1]},$ptfields[4]-1900);
1362    } else {
1363      my($mer) = "a";
1364      if ($times[0] > 12) { $times[0] -= 12; $mer = "p"; }
1365      if ($times[0] == 0) { $times[0] = "12"; }
1366      $ptime = "$ptfields[1] $ptfields[2] $times[0]:$times[1]$mer";
1367      }
1368
1369    $DATA{'QUARANTINE'} .= <<_END;
1370<tr>
1371 <td class="$outclass" nowrap="nowrap"><input type="checkbox" name="$row->{'X-DSPAM-Signature'}" id="checkbox-$counter" onclick="checkboxclicked(this)"></td>
1372 <td class="$outclass $markclass" nowrap="nowrap">$rating</td>
1373 <td class="$outclass" nowrap="nowrap">$ptime</td>
1374 <td class="$outclass" nowrap="nowrap">$row->{'From'}</td>
1375 <td class="$outclass" nowrap="nowrap"><a href="$CONFIG{'ME'}?$url">$row->{'Subject'}</a></td>
1376        <td class="$outclass" nowrap="true">$row->{'X-DSPAM-Recipient'}</td>
1377</tr>
1378_END
1379
1380    if ($rowclass eq "rowEven") {
1381      $rowclass = "rowOdd";
1382    } else {
1383      $rowclass = "rowEven";
1384    }
1385  }
1386
1387  &output(%DATA);
1388}
1389
1390#
1391# Performance Functions
1392#
1393
1394sub ResetStats {
1395  my($ts, $ti, $tm, $fp, $sc, $ic);
1396  my($group);
1397  open(FILE, "<$USER.stats");
1398  chomp($ts = <FILE>);
1399  chomp($group = <FILE>);
1400  close(FILE);
1401  ($ts, $ti, $tm, $fp, $sc, $ic) = split(/\,/, $ts);
1402 
1403  if ($group ne "") {
1404    my($GROUP) = GetPath($group) . ".stats";
1405    my($gts, $gti, $gtm, $gfp, $gsc, $gic);
1406    open(FILE, "<$GROUP");
1407    chomp($gts = <FILE>);
1408    close(FILE);
1409    ($gts, $gti, $gtm, $gfp, $gsc, $gic) = split(/\,/, $gts);
1410    $ts       -= $gts;
1411    $ti       -= $gti;
1412    $tm       -= $gtm;
1413    $fp       -= $gfp;
1414    $sc       -= $gsc;
1415    $ic       -= $gic;
1416  }
1417
1418  open(FILE, ">$USER.rstats");
1419  print FILE "$ts" . "," . "$ti" . "," . "$tm" . "," .
1420             "$fp" . "," . "$sc" . "," . "$ic\n";
1421  close(FILE);
1422}
1423
1424sub Tweak {
1425  my($ts, $ti, $tm, $fp, $sc, $ic);
1426  open(FILE, "<$USER.rstats");
1427  chomp($ts = <FILE>);
1428  close(FILE);
1429  ($ts, $ti, $tm, $fp, $sc, $ic) = split(/\,/, $ts);
1430  $tm++;
1431  open(FILE, ">$USER.rstats");
1432  print FILE "$ts,$ti,$tm,$fp,$sc,$ic\n";
1433  close(FILE);
1434}
1435
1436sub DisplayIndex {
1437  my($spam, $innocent, $ratio, $fp, $misses);
1438  my($rts, $rti, $rtm, $rfp, $sc, $ic, $overall, $fpratio, $monthly,
1439     $real_missed, $real_caught, $real_fp, $real_innocent);
1440  my($time) = ctime(time);
1441  my($group);
1442
1443  open(FILE, "<$USER.stats");
1444  chomp($spam = <FILE>);
1445  chomp($group = <FILE>);
1446  close(FILE);
1447  ($spam, $innocent, $misses, $fp, $sc, $ic) = split(/\,/, $spam);
1448
1449  if ($group ne "") {
1450    my($GROUP) = GetPath($group) . ".stats";
1451    my($gspam, $ginnocent, $gmisses, $gfp, $gsc, $gic);
1452    open(FILE, "<$GROUP");
1453    chomp($gspam = <FILE>);
1454    close(FILE);
1455    ($gspam, $ginnocent, $gmisses, $gfp, $gsc, $gic) = split(/\,/, $gspam);
1456    $spam     -= $gspam;
1457    $innocent -= $ginnocent;
1458    $misses   -= $gmisses;
1459    $fp       -= $gfp;
1460    $sc       -= $gsc;
1461    $ic       -= $gic;
1462  }
1463
1464  if ($spam+$innocent>0) {
1465    $ratio = sprintf("%2.3f",
1466      (($spam+$misses)/($spam+$misses+$fp+$innocent)*100));
1467  } else {
1468    $ratio = 0;
1469  }
1470
1471  if (open(FILE, "<$USER.rstats")) {
1472    my($rstats);
1473   
1474    chomp($rstats = <FILE>);
1475    ($rts, $rti, $rtm, $rfp) = split(/\,/, $rstats);
1476    close(FILE);
1477    $real_missed = $misses-$rtm;
1478    $real_caught = $spam-$rts;
1479    $real_fp = $fp-$rfp;
1480    if ($real_fp < 0) { $real_fp = 0; }
1481    $real_innocent = $innocent-$rti;
1482    if (($spam-$rts > 0) && ($spam-$rts + $misses-$rtm != 0) &&
1483        ($real_caught+$real_missed>0) && ($real_fp+$real_innocent>0)) {
1484      $monthly = sprintf("%2.3f",
1485        (100.0-(($real_missed)/($real_caught+$real_missed))*100.0));
1486      $overall = sprintf("%2.3f",
1487        (100.0-(($real_missed+$real_fp) /
1488        ($real_fp+$real_innocent+$real_caught+$real_missed))*100.0));
1489    } else {
1490      if ($real_caught == 0 && $real_missed > 0) {
1491        $monthly = 0;
1492        $overall = 0;
1493      } else {
1494        $monthly = 100;
1495        $overall = 100;
1496      }
1497    }
1498
1499    if ($real_fp+$real_innocent>0) {
1500      $fpratio = sprintf("%2.3f", ($real_fp/($real_fp+$real_innocent)*100));
1501    } else {
1502      $fpratio = 0;
1503    }
1504
1505  } else {
1506    $rts = $spam+$misses;
1507    $rti = $innocent;
1508    $rtm = $misses;
1509    $rfp = $fp;
1510    open(FILE, ">$USER.rstats");
1511    print FILE "$rts,$rti,$rtm,$rfp\n";
1512    close(FILE);
1513    $monthly = "N/A";
1514    $fpratio = "N/A";
1515    $overall = "N/A";
1516  }
1517
1518  $DATA{'TIME'} = $time;
1519  $DATA{'TOTAL_SPAM_SCANNED'} = $spam;
1520  $DATA{'TOTAL_SPAM_LEARNED'} = $misses;
1521  $DATA{'TOTAL_NONSPAM_SCANNED'} = $innocent;
1522  $DATA{'TOTAL_NONSPAM_LEARNED'} = $fp;
1523  $DATA{'SPAM_RATIO'} = $ratio;
1524  $DATA{'SPAM_ACCURACY'} = $monthly;
1525  $DATA{'NONSPAM_ERROR_RATE'} = $fpratio;
1526  $DATA{'OVERALL_ACCURACY'} = $overall;
1527  $DATA{'TOTAL_SPAM_CORPUSFED'} = $sc;
1528  $DATA{'TOTAL_NONSPAM_CORPUSFED'} = $ic;
1529  $DATA{'TOTAL_SPAM_MISSED'} = $real_missed;
1530  $DATA{'TOTAL_SPAM_CAUGHT'} = $real_caught;
1531  $DATA{'TOTAL_NONSPAM_MISSED'} = $real_fp;
1532  $DATA{'TOTAL_NONSPAM_CAUGHT'} = $real_innocent;
1533
1534  if ($CURRENT_USER !~ /\@/) {
1535    $DATA{'LOCAL_DOMAIN'} = "\@$CONFIG{'LOCAL_DOMAIN'}";
1536  } 
1537
1538  &output(%DATA);
1539}
1540
1541#
1542# Alert Functions
1543#
1544
1545sub AddAlert {
1546  if ($FORM{'ALERT'} eq "") {
1547    &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_alert_specified'}");
1548  }
1549  open(FILE, ">>$USER.alerts");
1550  print FILE "$FORM{'ALERT'}\n";
1551  close(FILE);
1552  return;
1553}
1554                                                                               
1555sub DeleteAlert {
1556  my($line, @alerts);
1557  $line = 0;
1558  if ($FORM{'line'} eq "") {
1559    &Error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_no_alert_specified'}");
1560  }
1561  open(FILE, "<$USER.alerts");
1562  while(<FILE>) {
1563    if ($line != $FORM{'line'}) {
1564      push(@alerts, $_);
1565    }
1566    $line++;
1567  }
1568  close(FILE);
1569                                                                               
1570  open(FILE, ">$USER.alerts");
1571  foreach(@alerts) { print FILE $_; }
1572  close(FILE);
1573  return;
1574}
1575
1576sub DisplayAlerts {
1577  my($supp);
1578 
1579  $DATA{'ALERTS'} = <<_end;
1580<table border="0" cellspacing="0" cellpadding="2">
1581        <tr>
1582                <th>$CONFIG{'LANG'}->{$LANGUAGE}->{'alert_name'}</th>
1583                <th>&nbsp;</th>
1584        </tr>
1585_end
1586
1587  my($rowclass);
1588  $rowclass = "rowEven";
1589
1590  do {
1591    my($line) = 0;
1592    open(FILE, "<$USER.alerts");
1593    while(<FILE>) {
1594      s/</&lt;/g;
1595      s/>/&gt;/g;
1596      $DATA{'ALERTS'} .= qq!<tr><td class="$rowclass">$_</td><td class="$rowclass">[<a href="$CONFIG{'ME'}?command=deleteAlert&amp;user=$FORM{'user'}&amp;template=alerts&amp;language=$LANGUAGE&amp;line=$line">$CONFIG{'LANG'}->{$LANGUAGE}->{'remove_alert'}</a>]</td></tr>\n!;
1597      $line++;
1598
1599      if ($rowclass eq "rowEven") {
1600        $rowclass = "rowOdd";
1601      } else {
1602        $rowclass = "rowEven";
1603      }
1604    }
1605  };
1606
1607$DATA{'ALERTS'} .= <<_end;
1608</table>
1609_end
1610
1611  &output(%DATA);
1612  exit;
1613}
1614
1615#
1616# Global Functions
1617#
1618
1619sub is_utf8 {
1620  my $s = "\x80" . $_[0];
1621  my $internal = unpack "p", pack "p", $s;
1622  return $s ne $internal;
1623}
1624
1625sub htmlize {
1626  #
1627  # Replace some characters
1628  # to be HTML characters
1629  #
1630  my($text) = @_;
1631
1632  my $has_encode = eval{require Encode;};
1633  my $has_html_entities = eval{require HTML::Entities;};
1634
1635  if ($text =~ /^(.*?)=\?([^?]+)\?([qb])\?([^?]*)\?=(.*)$/is) {
1636    if ($has_encode) {
1637      $text = Encode::decode($2, Encode::decode('MIME-Header', $text));
1638    }
1639  } elsif ($text =~ /([\xC2-\xDF][\x80-\xBF]
1640                     | \xE0[\xA0-\xBF][\x80-\xBF]
1641                     |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
1642                     | \xED[\x80-\x9F][\x80-\xBF]
1643                     | \xF0[\x90-\xBF][\x80-\xBF]{2}
1644                     |[\xF1-\xF3][\x80-\xBF]{3}
1645                     | \xF4[\x80-\x8F][\x80-\xBF]{2})/x) {
1646    if ($has_encode) {
1647      $text = Encode::decode("utf8", $text);
1648    }
1649  }
1650  if ($has_html_entities) {
1651    $text = HTML::Entities::encode_entities(HTML::Entities::decode_entities($text));
1652  }
1653  if ($text =~ /[\xC2-\xDF][\x80-\xBF]/) {
1654    if ((-e "htmlize.pl") && (-r "htmlize.pl")) {
1655      require "htmlize.pl";
1656      $text = htmlize_chars($text);
1657    }
1658  }
1659  return $text;
1660}
1661
1662sub redirect {
1663  my($loc) = @_;
1664  $loc =~ s/&amp\;/&/g;
1665  print "Expires: now\n";
1666  print "Pragma: no-cache\n";
1667  print "Cache-control: no-cache\n";
1668  print "Location: $loc\n\n";
1669  exit(0);
1670}
1671
1672sub htmlheader {
1673  print "Expires: now\n";
1674  print "Pragma: no-cache\n";
1675  print "Cache-control: no-cache\n";
1676  print "Content-type: text/html\n\n";
1677}
1678
1679sub output {
1680  if ($FORM{'template'} eq "" || $FORM{'template'} !~ /^([A-Z0-9]*)$/i) {
1681    $FORM{'template'} = "performance";
1682  }
1683  &htmlheader;
1684  my(%DATA) = @_;
1685  $DATA{'WEB_ROOT'} = $CONFIG{'WEB_ROOT'};
1686  $DATA{'LANG'} = $LANGUAGE;
1687
1688  # Check admin permissions
1689  do {
1690    if ($CONFIG{'ADMIN'} == 1) {
1691      $DATA{'NAV_ADMIN'} = qq!<li><a href="admin.cgi?language=$LANGUAGE">$CONFIG{'LANG'}->{$LANGUAGE}->{'admin_suite'}</a></li>!;
1692      $DATA{'FORM_USER'} = qq!<form action="$CONFIG{'ME'}"><input type="hidden" name="template" value="$FORM{'template'}">$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form'}&nbsp;$USERSELECT&nbsp;&nbsp;&nbsp;&nbsp;$CONFIG{'LANG'}->{$LANGUAGE}->{'lang_select'}$CONFIG{'LANGUAGES'}&nbsp;&nbsp;<input type="submit" value="$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form_submit'}"></form>!;
1693    } elsif ($CONFIG{'SUBADMIN'} == 1) {
1694      $DATA{'FORM_USER'} = qq!<form action="$CONFIG{'ME'}"><input type="hidden" name="template" value="$FORM{'template'}"><input type="hidden" name="language" value="$LANGUAGE">$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form'}&nbsp;$USERSELECT&nbsp;&nbsp;&nbsp;&nbsp;$CONFIG{'LANG'}->{$LANGUAGE}->{'lang_select'}$CONFIG{'LANGUAGES'}&nbsp;&nbsp;<input type="submit" value="$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form_submit'}"></form>!;
1695    } else {
1696      $DATA{'NAV_ADMIN'} = '';
1697      $DATA{'FORM_USER'} = qq!<form action="$CONFIG{'ME'}">$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form'}&nbsp;<strong>$CURRENT_USER</strong>&nbsp;&nbsp;&nbsp;&nbsp;$CONFIG{'LANG'}->{$LANGUAGE}->{'lang_select'}$CONFIG{'LANGUAGES'}&nbsp;&nbsp;<input type="submit" value="$CONFIG{'LANG'}->{$LANGUAGE}->{'user_form_submit'}"></form>!;
1698    }
1699  };
1700
1701  open(FILE, "<$CONFIG{'TEMPLATES'}/nav_$FORM{'template'}.html");
1702  while(<FILE>) {
1703    s/\$CGI\$/$CONFIG{'ME'}/g;
1704    if($FORM{'user'}) {
1705      if($CONFIG{'ADMIN'} == 1) {
1706        s/\$USER\$/user=$FORM{'user'}&amp;/g;
1707      } elsif ($CONFIG{'SUBADMIN'} == 1) {
1708        my $form_user_domain = (split(/@/, $FORM{'user'}))[1];
1709        if($CONFIG{'SUBADMIN_USERS'}->{ $FORM{'user'} } == 1 || ($form_user_domain ne "" && $CONFIG{'SUBADMIN_USERS'}->{ "*@" . $form_user_domain } == 1)) {
1710          s/\$USER\$/user=$FORM{'user'}&amp;/g;
1711        } else {
1712          s/\$USER\$//g;
1713        }
1714      } else {
1715        s/\$USER\$//g;
1716      }
1717    } else {
1718      s/\$USER\$//g;
1719    }
1720    s/\$([A-Z0-9_]*)\$/$DATA{$1}/g;
1721    print;
1722  }
1723  close(FILE);
1724  exit(0);
1725}
1726
1727sub SafeVars {
1728  my(%PAIRS) = @_;
1729  my($url, $key);
1730  foreach $key (keys(%PAIRS)) {
1731    my($value) = $PAIRS{$key};
1732    $value =~ s/([^A-Z0-9])/sprintf("%%%x", ord($1))/gie;
1733    $url .= "$key=$value&amp;";
1734  }
1735  $url =~ s/\&$//;
1736  return $url;
1737}
1738
1739sub error {
1740  my($error) = @_;
1741  $FORM{'template'} = "error";
1742  $DATA{'MESSAGE'} = <<_end;
1743$CONFIG{'LANG'}->{$LANGUAGE}->{'error_message_part1'}
1744<BR>
1745<B>$error</B><BR>
1746<BR>
1747$CONFIG{'LANG'}->{$LANGUAGE}->{'error_message_part2'}
1748_end
1749  &output(%DATA);
1750  exit;
1751}
1752
1753sub ReadParse {
1754  my(@pairs, %FORM);
1755  if ($ENV{'REQUEST_METHOD'} =~ /GET/i)
1756    { @pairs = split(/&/, $ENV{'QUERY_STRING'}); }
1757  else {
1758    my($buffer);
1759    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
1760    @pairs = split(/\&/, $buffer);
1761  }
1762  foreach(@pairs) {
1763    my($name, $value) = split(/\=/, $_);
1764
1765    $name =~ tr/+/ /;
1766    $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
1767    $value =~ tr/+/ /;
1768    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
1769    $value =~ s/<!--(.|\n)*-->//g;
1770
1771    if ($name =~ /^msgid[\d]+$/) {
1772        push(@{ $FORM{retrain_checked} }, $value);
1773    } else {
1774        $FORM{$name} = $value;
1775    }
1776  }
1777  return %FORM;
1778}
1779
1780sub CheckQuarantine {
1781  my($f)=0;
1782  open(FILE, "<$MAILBOX");
1783  while(<FILE>) {
1784    next unless (/^From /);
1785    $f++;
1786  }
1787  close(FILE);   
1788  if ($f == 0) {
1789    $f = "$CONFIG{'LANG'}->{$LANGUAGE}->{'empty'}";
1790  }
1791
1792  $DATA{'TOTAL_QUARANTINED_MESSAGES'} = $f;
1793}
1794
1795sub To12Hour {
1796  my($h) = @_;
1797  if ($h < 0) { $h += 24; }
1798  if ($h>11) { $h -= 12 if ($h>12) ; $h .= "p"; }
1799  else { $h = "12" if ($h == 0); $h .= "a"; }
1800  return $h;
1801}
1802
1803sub GetPath {
1804  my($USER) = @_;
1805  my($PATH);
1806
1807  # Domain-scale
1808  if ($CONFIG{'DOMAIN_SCALE'} == 1) {
1809    my($VPOPUSERNAME, $VPOPDOMAIN);
1810
1811    $VPOPUSERNAME = (split(/@/, $USER))[0];
1812    $VPOPDOMAIN = (split(/@/, $USER))[1];
1813    $VPOPDOMAIN = "local" if ($VPOPDOMAIN eq "");
1814    ($VPOPUSERNAME = $VPOPDOMAIN, $VPOPDOMAIN = "local") if ($VPOPUSERNAME eq "" && $VPOPDOMAIN ne "");
1815
1816    $PATH = "$CONFIG{'DSPAM_HOME'}/data/$VPOPDOMAIN/$VPOPUSERNAME/" .
1817            "$VPOPUSERNAME";
1818    return $PATH;
1819
1820  # Normal scale
1821  } elsif ($CONFIG{'LARGE_SCALE'} == 0) {
1822    $PATH = "$CONFIG{'DSPAM_HOME'}/data/$USER/$USER";
1823    return $PATH;
1824                                                                               
1825  # Large-scale
1826  } else {
1827    if (length($USER)>1) {
1828      $PATH = "$CONFIG{'DSPAM_HOME'}/data/" . substr($USER, 0, 1) .
1829        "/". substr($USER, 1, 1) . "/$USER/$USER";
1830    } else {
1831      $PATH = "$CONFIG{'DSPAM_HOME'}/data/$USER/$USER/$USER";
1832    }
1833    return $PATH;
1834  }
1835
1836  &error("Unable to determine filesystem scale");
1837}
1838
1839
1840sub GetPrefs {
1841  my(%PREFS);
1842
1843  my($FILE) = "$USER.prefs";
1844
1845  if ($CONFIG{'PREFERENCES_EXTENSION'} == 1) {
1846    my $PREF_USER = $CURRENT_USER;
1847    $PREF_USER = "default" if($CURRENT_USER eq "");
1848    open(PIPE, "$CONFIG{'DSPAM_BIN'}/dspam_admin agg pref " . quotemeta($PREF_USER) . "|");
1849    while(<PIPE>) {
1850      chomp;
1851      my($directive, $value) = split(/\=/);
1852      $PREFS{$directive} = $value;
1853    }
1854    close(PIPE);
1855  }
1856
1857  if (keys(%PREFS) eq "0" || $CONFIG{'PREFERENCES_EXTENSION'} != 1) {
1858
1859    if (! -e "./default.prefs") {
1860      &error("$CONFIG{'LANG'}->{$LANGUAGE}->{'error_load_default_prefs'}");
1861    }
1862    open(FILE, "<./default.prefs");
1863    while(<FILE>) {
1864      chomp;
1865      my($directive, $value) = split(/\=/);
1866      $PREFS{$directive} = $value;
1867    }
1868    close(FILE);
1869
1870    if( -e $FILE) {
1871      open(FILE, "<$FILE");
1872      while(<FILE>) {
1873        chomp;
1874        my($directive, $value) = split(/\=/);
1875        $PREFS{$directive} = $value;
1876      }
1877      close(FILE);
1878    }
1879  }
1880  return %PREFS
1881}
Note: See TracBrowser for help on using the repository browser.