#! /usr/local/bin/perl -wT # display and edit a DCC whitelist file # Copyright (c) 2006 by Rhyolite Software, LLC # # This agreement is not applicable to any entity which sells anti-spam # solutions to others or provides an anti-spam solution as part of a # security solution sold to other entities, or to a private network # which employs the DCC or uses data provided by operation of the DCC # but does not provide corresponding data to other users. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # Parties not eligible to receive a license under this agreement can # obtain a commercial license to use DCC and permission to use # U.S. Patent 6,330,590 by contacting Commtouch at http://www.commtouch.com/ # or by email to nospam@commtouch.com. # # A commercial license would be for Distributed Checksum and Reputation # Clearinghouse software. That software includes additional features. This # free license for Distributed ChecksumClearinghouse Software does not in any # way grant permision to use Distributed Checksum and Reputation Clearinghouse # software # # THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC # BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES # OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS # SOFTWARE. # Rhyolite Software DCC 1.3.50-1.45 $Revision$ # Generated automatically from edit-whiteclnt.in by configure. # This file must protected with an equivalent to httpd.conf lines # in the README file. use strict 'subs'; my($main_whiteclnt); # path to the main whiteclnt file my(@file); # list representation of the file my(%dict); # dictionary of checksums and options my(%def_options); # option settings from main whiteclnt file my($cur_key, $cur_entry, $msg); # get DCC parameters local($whiteclnt, # path to the per-user whitelist file $thold_cks, # checksums that can have thresholds $user, %query, $edit_url, $form_hidden, $list_msg_link, $sub_white); do('/usr/local/dcc/cgi-bin/common') || die("could not get DCC configuration: $!\n"); # display the file literally if ($query{literal}) { my($buf); open(WHITECLNT, "< $whiteclnt") or html_whine("open($whiteclnt): $!"); print "Content-type: text/plain\n"; print "Expires: Thu, 01 Dec 1994 16:00:00 GMT\n"; print "pragma: no-cache\n\n"; print $buf while (read(WHITECLNT, $buf, 4*1024)); print "\n"; close(WHITECLNT); exit; } # lock, read and parse the whiteclnt file local($whiteclnt_version, $whiteclnt_notify_pat, $whiteclnt_notify, $whiteclnt_lock, $whiteclnt_change_log); read_whiteclnt(\@file, \%dict); # get option defaults from the main whiteclnt file read_whitedefs(\%def_options); $cur_key = $query{key}; if (!defined($cur_key)) { $cur_entry = undef; } else { $cur_entry = $dict{$cur_key}; } $cur_msg = $query{msg}; $cur_msg = "" if (!defined($cur_msg)); html_head("Whitelist for $user"); common_buttons(); print "Message $list_msg_link${url_ques}msg=$query{msg}\">$query{msg}\n" if ($query{msg}); print <$edit_link${url_ques}literal=yes" TARGET="DCC literal whiteclnt">Literal contents of whitelist. EOF # add new entry if ($query{Add}) { my @new_entry = ck_new_white_entry($query{comment}, $query{count}, $query{type}, $query{val}); give_up($new_entry[0]) if (!defined($new_entry[1])); $new_key = $new_entry[0]; give_up("checksum already present") if ($dict{$new_key}); # send the entry to the disk with the rest of the file $msg = chg_white_entry(\@file, \%dict, $new_key, \@new_entry); give_up($msg) if ($msg); $cur_key = $new_key; $cur_entry = \@new_entry; finish("checksum added"); } # change current whitelist entry if ($query{Change}) { give_up("no checksum selected to change") if (! $cur_key); give_up("checksum [$cur_key] has disappeared") if (!$cur_entry || !$$cur_entry[0]); my @new_entry = ck_new_white_entry($query{comment}, $query{count}, $query{type}, $query{val}); give_up($new_entry[0]) if (!defined($new_entry[1])); give_up("no changes requested") if ($$cur_entry[1] eq $new_entry[1] && $$cur_entry[2] eq $new_entry[2]); # send the change to the disk with the rest of the file $msg = chg_white_entry(\@file, \%dict, $cur_key, \@new_entry); give_up($msg) if ($msg); $cur_key = $new_entry[0]; finish("checksum changed"); } # delete current entry if ($query{Delete}) { give_up("no checksum selected to delete") if (! $cur_key); give_up("checksum [$cur_key] has disappeared") if (! $cur_entry || ! $$cur_entry[0]); # write everything to the new file except the deleted entry $msg = chg_white_entry(\@file, \%dict, $cur_key); give_up($msg) if ($msg); undef_cur(); finish("checksum deleted"); } # undo the last change if ($query{Undo}) { $msg = undo_whiteclnt(); give_up($msg) if ($msg); undef_cur(); read_whiteclnt(\@file, \%dict); finish("undone"); } # change new log file mail notifcations if ($query{notify}) { my $old_notify = $whiteclnt_notify; if ($query{notify} =~ /Disable/i) { $whiteclnt_notify =~ s/$whiteclnt_notify_pat$/${1}off$3$4/i; } elsif ($query{notify} =~ /Enable/i) { $whiteclnt_notify =~ s/$whiteclnt_notify_pat$/${1}on$3$4/i; } if ($query{notifybox}) { my $new_box = $query{notifybox}; $new_box =~ s/^\s+(.*)\s*$/$1/; $whiteclnt_notify =~ s/$whiteclnt_notify_pat$/$1$2$3$new_box/i; } give_up('The notification mailbox is limited to -, _, letters, and digits') if ($whiteclnt_notify !~ /^$whiteclnt_notify_pat$/); if ($whiteclnt_notify ne $old_notify) { $msg = write_whiteclnt(@file); give_up($msg) if ($msg); } } # process requests to change options form_option("dccenable", "On", "dcc-on", "Off", "dcc-off"); form_option("greyfilter", "On", "greylist-on", "Off", "greylist-off");; form_option("greylog", "On", "greylist-log-on", "Off", "greylist-log-off"); form_option("mtafirst", "MTA-first", "first", "MTA-last", "last"); form_option("reps", "On", "DCC-reps-on", "Off", "DCC-reps-off"); form_option("dnsbl", "On", "dnsbl-on", "Off", "dnsbl-off"); form_option("logall", "On", "log-all", "Off", "log-normal"); form_option("logsubdir", "day", "log-subdirectory-day", "hour", "log-subdirectory-hour", "minute", "log-subdirectory-minute"); form_option("discardok", "forced-discard-ok", "discard spam", "forced-discard-nok", "delay mail"); # process requests from the HTTP client to change the threshold foreach my $ck (split(/,/, $thold_cks)) { my $nm = "thold-$ck"; foreach my $val ($query{$nm}, $query{"text-$nm"}) { next if (!$val); if ($val =~ /^Default/) { form_option_sub($nm); } elsif (!parse_thold_value($ck, $val)) { give_up("invalid setting '$nm' '$val'"); } else { form_option_sub($nm, "option threshold $ck,$val\n"); } last; } } # nothing to do? give_up("checksum [$cur_key] has disappeared") if ($cur_key && (! $cur_entry || ! $$cur_entry[0])); basic_form($query{result} ? "

$query{result}\n" : ""); ############################################################################# # display the basic editing form as well as the entire file sub basic_form { my($result) = @_; # '' or some kind of error message my($entry, $new_val, $comment, $locked, $form, $undo_ok, $change_ok); close(WHITECLNT); $locked = ($whiteclnt_lock =~ /\blocked/) ? " disabled" : ""; # generate an non-error message and comment if this is the first time if (! $result) { if (! $query{auto}) { $result = "

 \n" } elsif (! $cur_entry) { my $str = "\n# "; $str .= "added from logged message $query{msg} " if ($query{msg}); $str .= strftime("%x", localtime); $query{comment} = html_str_encode($str); $query{count} = "OK"; $result = "

select <Add> to add this checksum to your whitelist\n"; } } $undo_ok = newest_whiteclnt_bak() ? $locked : " disabled"; # we will prime the form with the currently selected whiteclnt entry, if any if ($cur_entry) { $comment = $$cur_entry[1]; $query{comment} = html_str_encode($comment); my $value = $$cur_entry[2]; $value =~ s/(\S+)\s+//; $query{count} = $1; ($query{type}, $query{val}) = parse_type_value($value); $change_ok = $locked; } else { # "disabled" does not work with Netscape 4.*, but we have to handle # changes without a valid key, so don't worry about it $change_ok = " disabled"; } $comment = $query{comment}; if (! $comment) { $comment = ""; } else { $comment =~ s/\s+$//mg; # need a blank on a leading blank line to preserve it in Mozilla form $comment =~ s/^\n/ \n/; } # generate common start of forms $form = "

$form_hidden"; $form .= "" if ($query{msg}); if ($cur_key) { $form .= ""; } # emit any error message from the previous action and the UNDO button print < last change $form EOF # emit the start of the "Add/Change/Delete Whitelist Entry" form print_button("
 ", 'Add', $locked, 'Add'); print_button("\t", 'Change', $change_ok, 'Change'); print_button("\t", 'Delete', $change_ok, 'Delete'); print <Whitelist Entry
Comment $comment
  \n"; print "\t\n"; print "\t\n
\n\n"; # generate forms to control option lines print <
Options
$form mail notifications mesages to EOF $whiteclnt_notify =~ /$whiteclnt_notify_pat/; print "\t\n"; if ($2 eq "on") { $new_val = "Disable"; print "\ton\n"; } else { $new_val = "Enable"; print "\toff\n"; } print "\t\n $form\n"; print_button("\t", 'notify', $locked, $new_val); print "\t\n"; basic_form_line("dccenable", "DCC", $form, $locked, "dcc-off", "dcc-on"); if ($DCCM_ARGS =~ /-G/ || $DCCIFD_ARGS =~ /-G/ || (defined($GREY_CLIENT_ARGS) && $GREY_CLIENT_ARGS ne "")) { basic_form_line("greyfilter", "greylist filter", $form, $locked, "greylist-off", "greylist-on"); basic_form_line("greylog", "greylist log", $form, $locked, "greylist-log-off", "greylist-log-on"); } basic_form_line("mtafirst", "consult MTA blacklist", $form, $locked, "MTA-last", "MTA-first", "last", "first"); basic_form_line("dnsbl", "DNS blacklist checking", $form, $locked) if ($DCCM_ARGS =~ /-B/ || $DCCIFD_ARGS =~ /-B/ || (defined($DNSBL_ARGS) && $DNSBL_ARGS) =~ /-B/); basic_form_line("logall", "debug logging", $form, $locked, "log-normal", "log-all"); basic_form_line("discardok", " also addressed to others", $form, $locked, "forced-discard-nok", "forced-discard-ok", "delay mail", "discard spam"); print "\n"; # forms for checksum thresholds foreach my $ck (split(/,/, $thold_cks)) { my($cur_val, $sw_val, $nm, $def_label, $bydef, $dis_field, $dis_def, $dis_never); $nm = "thold-" . $ck; # construct label for the default button from default value $def_label = $def_options{$nm}; $def_label =~ s/.*([^<]+)<.*/Default ($1)/; if (defined($dict{$nm})) { $cur_val = $dict{$nm}[2]; $cur_val =~ s/.*,([-_a-z0-9%]+)\s+$/$1/i; $bydef = ''; $sw_val = $cur_val; } else { $cur_val = $def_options{$nm}; $cur_val =~ s@(.*)(.*)@$1@; $bydef = $2; $sw_val = 'Default'; } $dis_field = $locked; $dis_def = $sw_val eq 'Default' ? ' disabled' : $locked; $dis_never = $sw_val eq 'Never' ? ' disabled' : $locked; # changing reputation thresholds ought to affect tagging # even if reputation checking is turned off print <$form $ck threshold$bydef $form EOF print_button("\t", $nm, $dis_def, $def_label); print_button("\t", $nm, $dis_never, 'Never'); # "many" makes no sense for either reputation threshold print_button("\t", $nm, $sw_val eq 'many' ? ' disabled' : $locked, 'many') if ($ck !~ /^rep/i); print "\t\n"; } print "
\n

\n"; display_file(); } sub form_option_sub { my($key, $line) = @_; my($msg); # insert the new value, if any $file[1] = ["", "", $line] if ($line); # delete the old value if any $msg = chg_white_entry(\@file, \%dict, $key); give_up($msg) if ($msg); } # (try to) set an option for the file based on the form's results # The first arg is the name of the option. It is followed by # (form-value,file-value) pairs sub form_option { my($key, $new_formval, $formval, $fileval); $key = shift @_; $new_formval = $query{$key}; return if (!$new_formval); if ($new_formval =~ /^Default/) { form_option_sub("$key"); return; } while ($#_ > 0) { $formval = shift @_; $fileval = shift @_; if ($new_formval eq $formval) { form_option_sub("$key", "option $fileval\n"); return; } } give_up("invalid setting '$key'='$new_formval'"); } sub undef_cur { undef($cur_key); undef($cur_entry); delete $query{comment}; delete $query{count}; delete $query{type}; delete $query{val}; } sub finish { my($msg) = @_; $msg = html_str_encode($msg); basic_form("

$msg\n"); } sub give_up { my($msg) = @_; $msg = html_str_encode($msg); basic_form("

$msg\n"); } # You cannot use real HTML 4 buttons because Microsoft has exercised # its Freedom to Innovate in Internet Explorer and gets them all wrong. # Contrary to the standard, the idiots return all type=submit buttons. # They also return any text label instead of the value, thereby removing # most or all reason to use