diff --git a/ql-web/trunk/lang/de.hdf b/ql-web/trunk/lang/de.hdf index 13236cd..4d77c69 100644 --- a/ql-web/trunk/lang/de.hdf +++ b/ql-web/trunk/lang/de.hdf @@ -38,33 +38,46 @@ Lang { OldPassword = Altes Passwort NewPassword = Neues Passwort NewPasswordAgain = Neues Passwort wiederholen + PasswordType = Welches Passwort soll geaendert werden + AllPasswords = alle FooterText = eine Web-Oberflaeche fuer - Filter_None = keine Spam-Pruefung - Filter_Mark = markiere Spam - Filter_Move = verschiebe Spam in das Spam-Verzeichnis - StatusSpamNone = eingehende Nachrichten werden nicht auf Spam geprueft - StatusSpamMark = Spam wird markiert - StatusSpamMove = Spam wird in das Spam-Verzeichnis verschoben Vacation = Abwesenheitsbenachrichtigung versenden VacationText = Inhalt der automatischen Abwesenheitsnachricht ForwardCount = Anzahl der Mail-Weiterleitungen NoForward = Keine Weiterleitungen VacationEnabled = Abwesenheitsnachrichten werden versandt - VacationDisabled = Keine Abwesenhitsnachrichten + VacationDisabled = Keine Abwesenheitsnachrichten + SpamAction { + None = keine Pruefung auf Spam + Mark = Spam wird markiert + Move = Spam wird in das Spam-Verzeichnis verschoben + } + SpamOptions { + Score = Spam-Schwellwert (5.0 ist ueblich) + AttachText = Anhaenge spam-verdaechtiger Mails werden als reiner Text dargestellt + Subject = Betreff-Text fuer spam-verdaechtige Mails + Bayes = Stichwort-basierte Bewertung (bayesianische Filterung) verwenden + WhiteList = Adressen vertrauter Sender (* ist ein Platzhalter) + } } ErrorMessage { UnknownAction = Diese Aktion ist undefiniert! ParameterMissing = Diese Aktion benoetigt weitere Parameter! + UnknownPasswordHash = Dieser Passwort-Hash-Typ wird (derzeit) nicht unterstuetzt. } WarningMessage { LdapConnect = Fehler beim Zugriff auf den LDAP-Server! - FilterConfig = Die Filterungseinstellungen konnten nicht gespeichert werden! + UpdateFilterAction = Die Spam-Filter-Aktion konnte nicht gespeichert werden! + UpdateFilterOptions = Die Spam-Filter-Einstellungen konnten nicht gespeichert werden! AddForward = Die Weiterleitung konnte nicht aktiviert werden! DelForward = Mindestens eine Weiterleitung konnte nicht entfernt werden! ExistingForward = Diese Weiterleitung existiert bereits! + EmptyOldPassword = Das bisherige Passwort muss angegeben werden! + EmptyNewPassword = Das neue Passwort darf nicht leer sein! + DifferentNewPasswords = Das neue Passwort wurde nicht korrekt wiederholt. WrongPassword = Das angegebene Passwort war nicht korrekt! InvalidAddress = Die angegebene Mailadresse ist ungueltig! EmptyAddress = Es wurde keine Mailadresse angegeben! @@ -79,7 +92,8 @@ Lang { UpdatePassword = Das Passwort wurde erfolgreich geaendert. AddForward = Die Weiterleitung wurde hinzugefuegt. DelForward = Die Weiterleitung wurde entfernt. - UpdateFilter = Die Spam-Filter-Einstellungen wurden gespeichert. + UpdateFilterAction = Die gewuenschte Spam-Filter-Aktion wurde aktiviert. + UpdateFilterOptions = Die Spam-Filter-Einstellungen wurden gespeichert. UpdateVacation = Die Abwesenheits-Einstellungen wurden gespeichert. } @@ -95,7 +109,8 @@ Lang { Password = Passwort aendern DelForward = Weiterleitung(en) entfernen AddForward = Weiterleitungen hinzufuegen - Filter = Spam-Filterung einrichten + FilterAction = Wie soll Spam behandelt werden? + FilterOptions = Individuelle Spamfilter-Konfiguration Vacation = Abwesenheitsbenachrichtigung einrichten } } diff --git a/ql-web/trunk/ql-web.conf b/ql-web/trunk/ql-web.conf index bda9f34..529b7a3 100644 --- a/ql-web/trunk/ql-web.conf +++ b/ql-web/trunk/ql-web.conf @@ -13,7 +13,17 @@ $LDAP_HOST = 'ldap.sao'; # the string '_USERNAME_' will be replaced by the real username $LDAP_USER_DN = "cn=_USERNAME_,sc=mailAccount,ou=People,o=neofaxe,dc=systemausfall,dc=org"; -#$LDAP_SPAM_MOVE = "/usr/local/bin/ifspamh spam-_USERNAME_\@systemausfall.org"; +# DNs for passwords (more than one is allowed) +# _USERNAME_ will be substituted (see LDAP_USER_DN) +# do not use the reserved name 'all'! +# every entry must contain the following elements: 'dn'. 'attr' and 'hash' +%LDAP_PASSWD = ( + "Mail" => { dn => $LDAP_USER_DN, + attr => 'userPassword', + hash => 'MD5' }, + "Web" => { dn => "cn=_USERNAME_,sc=webAccount,ou=People,o=neofaxe,dc=systemausfall,dc=org", + attr => 'userPassword', + hash => 'MD5' } ); # spam filtering disables forwarding and local delivery (for spam) # dot-qmail files will be used, if there @@ -23,3 +33,12 @@ $LDAP_SPAM_MOVE = [ "deliveryProgramPath" => "/data/scripts/spam_moving.sh" ]; $LDAP_SPAM_MARK = [ "deliveryProgramPath" => "/data/scripts/spam_tagging.sh", "deliveryMode" => "nolocal" ]; +# some spamassassin options +# syntax: "OPTION" => "DEFAULT" +# (look into /etc/spamassassin/local.cf for examples) +# you have to change the code of ql-web.pl to use more options +$LDAP_SPAM_OPTIONS = [ "required_score" => "5.0", + "report_safe" => 2, + "rewrite_header Subject" => "*** SPAM-Verdacht ***", + "use_bayes" => 1, + "whitelist_from" => "" ]; diff --git a/ql-web/trunk/ql-web.pl b/ql-web/trunk/ql-web.pl index 5f77760..224344c 100755 --- a/ql-web/trunk/ql-web.pl +++ b/ql-web/trunk/ql-web.pl @@ -12,6 +12,7 @@ use Mail::Address; use CGI; use IO::File; use Net::LDAP; +use Digest::MD5; # Net::LDAP will care about base64 encoding for multiline ldap entries #use MIME::Base64; @@ -31,6 +32,7 @@ use vars qw[$HOME_DIR]; $HOME_DIR=$tmp[7]; use vars qw[$HTML_TITLE]; use vars qw[$CSS_URL $TEMPLATE_DIR $LANGUAGE_DIR $HTML_LANGUAGE]; use vars qw[$LDAP_HOST $LDAP_USER_DN $LDAP_SPAM_MOVE $LDAP_SPAM_MARK]; +use vars qw[$LDAP_SPAM_OPTIONS %LDAP_PASSWD]; # set default TEXT_ENCODE use vars qw[$TEXT_ENCODE]; $TEXT_ENCODE='us-ascii'; @@ -60,22 +62,33 @@ $TEMPLATE_DIR = 'template' unless defined($TEMPLATE_DIR); # Untaint form input ... &untaint; -$mail_user = $ENV{'REMOTE_USER'}; +$mail_user = defined($ENV{'REMOTE_USER'})? $ENV{'REMOTE_USER'} : ''; +&fatal_error("undefined REMOTE_USER variable - you have to use http authentication for ql-web!") if ($mail_user eq ''); my $pagedata = load_hdf(); -my $action = $q->param('action'); +my $action = defined($q->param('action'))? $q->param('action') : ''; # This is where we decide what to do, depending on the form state and the # users chosen course of action ... # TODO: unify all these "is list param set?" checks ... -if ($action eq '' || $action eq 'overview') { +if (($action eq '') || ($action eq 'overview')) { # Default action - display the current mail account configuration $pagename = 'overview'; } elsif ($action eq 'password_form') { # display password change dialog $pagename = 'password_form'; } elsif ($action eq 'password_update') { - $success = 'UpdatePassword' if (&update_password()); + if (defined($q->param('pw_type'))) { + if (&update_password()) { + $success = 'UpdatePassword'; + $pagename = 'overview'; + } else { + $pagename = 'password_form'; + } + } else { + $error = 'ParameterMissing'; + $pagename = 'password_form'; + } } elsif ($action eq 'forward_form') { $pagename = 'forward_form'; } elsif ($action eq 'forward_add') { @@ -99,9 +112,13 @@ if ($action eq '' || $action eq 'overview') { } } elsif ($action eq 'filter_form') { $pagename = 'filter_form'; -} elsif ($action eq 'filter_update') { +} elsif ($action eq 'filter_action_update') { # update filtering setting - $success = 'UpdateFilter' if (&update_filter()); + $success = 'UpdateFilterAction' if (&update_filter_action()); + $pagename = 'filter_form'; +} elsif ($action eq 'filter_options_update') { + # update filtering setting + $success = 'UpdateFilterOptions' if (&update_filter_options()); $pagename = 'filter_form'; } elsif ($action eq 'vacation_form') { $pagename = 'vacation_form'; @@ -125,24 +142,47 @@ exit; # ========================================================================= sub set_pagedata { + # filtering actions $pagedata->setValue('Data.isSpamMove', &is_spam_move()? 1 : 0); $pagedata->setValue('Data.isSpamMark', &is_spam_mark()? 1 : 0); $pagedata->setValue('Data.UserName', $mail_user); - # retrieve frowarding addresses - my $one_forward; - my $i = 0; - foreach $one_forward (&get_ldap_values('mailForwardingAddress')) { - $pagedata->setValue("Data.ForwardAddresses.$i", $one_forward); - $i++; + # filtering options + $pagedata->setValue('Data.Filter.Score', &get_spamassassin_option('required_score')); + $pagedata->setValue('Data.Filter.AttachText', (&get_spamassassin_option('report_safe') eq 2)? 1 : 0); + $pagedata->setValue('Data.Filter.Subject', &get_spamassassin_option('rewrite_header Subject')); + $pagedata->setValue('Data.Filter.Bayes', &get_spamassassin_option('use_bayes')); + $pagedata->setValue('Data.Filter.WhiteList', &get_spamassassin_option('whitelist_from')); + + { + # retrieve frowarding addresses + my $one_forward; + my $i = 0; + foreach $one_forward (&get_ldap_values('mailForwardingAddress')) { + $pagedata->setValue("Data.ForwardAddresses.$i", $one_forward); + $i++; + } } - my $vacation_text = &get_ldap_values('mailReplyText'); - # encoding is not necessary -> automatically done by Net::LDAP - #$vacation_text = MIME::Base64::decode_base64($vacation_text); - my $vacation_state = &compare_ldap_attr('deliveryMode', 'reply'); - $pagedata->setValue('Data.VacationText', $vacation_text); - $pagedata->setValue('Data.isVacation', $vacation_state? 1 : 0); + # vacation reply + { + my $vacation_text = &get_ldap_values('mailReplyText'); + # encoding is not necessary -> automatically done by Net::LDAP + #$vacation_text = MIME::Base64::decode_base64($vacation_text); + my $vacation_state = &compare_ldap_attr('deliveryMode', 'reply'); + $pagedata->setValue('Data.VacationText', $vacation_text); + $pagedata->setValue('Data.isVacation', $vacation_state? 1 : 0); + } + + { + # password information + my $pw_type; + my $i = 0; + foreach $pw_type (keys %LDAP_PASSWD) { + $pagedata->setValue("Data.Password.Types.$i", $pw_type); + $i++; + } + } } # --------------------------------------------------------------------------- @@ -290,7 +330,91 @@ sub update_vacation { # --------------------------------------------------------------------------- -sub update_filter { +sub update_filter_options { + my $password = $q->param('pw'); + my $user_dn = $LDAP_USER_DN; + $user_dn =~ s/_USERNAME_/$mail_user/g; + + my $ldap = Net::LDAP->new($LDAP_HOST); + my $result = $ldap->bind($user_dn, password => $password); + if ($result->is_error) { + $warning = 'WrongPassword'; + return (0==1); + } + + $result = $ldap->modify($user_dn, delete => ['spamassassin'] ); + if ($result->is_error) { + warn $result->error_text; + $warning = 'UpdateFilterOptions'; + $ldap->unbind; + return (1==0); + } + + { + my $spam_score = $q->param('spam_score'); + $spam_score =~ s/[^0-9\.]//g; + if (($spam_score ne '') && + ($spam_score ne &get_spamassassin_option_default('required_score'))) { + $result = $ldap->modify($user_dn, + add => { 'spamassassin' => "required_score $spam_score" }); + $warning = 'UpdateFilterOptions' && warn $result->error_text + if ($result->is_error); + } + } + + { + my $spam_attach_text = defined($q->param('spam_attach_text'))? 2 : 1; + if ($spam_attach_text ne &get_spamassassin_option_default('report_safe')) { + $result = $ldap->modify($user_dn, + add => { 'spamassassin' => "report_safe $spam_attach_text" }); + $warning = 'UpdateFilterOptions' && warn $result->error_text + if ($result->is_error); + } + } + + { + my $spam_subject = $q->param('spam_subject'); + $spam_subject =~ s/\n//mg; + chomp $spam_subject; + if ($spam_subject ne &get_spamassassin_option_default('rewrite_header Subject')) { + $result = $ldap->modify($user_dn, + add => { 'spamassassin' => "rewrite_header Subject $spam_subject" }); + $warning = 'UpdateFilterOptions' && warn $result->error_text + if ($result->is_error); + } + } + + { + my $spam_bayes = defined($q->param('spam_bayes'))? 1 : 0; + if ($spam_bayes ne &get_spamassassin_option_default('use_bayes')) { + $result = $ldap->modify($user_dn, + add => { 'spamassassin' => "use_bayes $spam_bayes" }); + $warning = 'UpdateFilterOptions' && warn $result->error_text + if ($result->is_error); + } + } + + { + my $spam_whitelist = $q->param('spam_whitelist'); + $spam_whitelist =~ s/[^\w_\-\@\.\n]//g; + my $one_white; + foreach $one_white (split /\n/, $spam_whitelist) { + if ($one_white ne '') { + $result = $ldap->modify($user_dn, + add => { 'spamassassin' => "whitelist_from $one_white" }); + $warning = 'UpdateFilterOptions' && warn $result->error_text + if ($result->is_error); + } + } + } + + $ldap->unbind; + return ($warning ne 'UpdateFilterOptions'); +} + +# --------------------------------------------------------------------------- + +sub update_filter_action { my $ldif_move; my $ldif_mark; my $password = $q->param('pw'); @@ -331,7 +455,7 @@ sub update_filter { $ldap->unbind; if ($result->is_error) { - $warning = 'FilterConfig'; + $warning = 'UpdateFilterAction'; warn $result->error_text; return (0==1); } else { @@ -342,7 +466,75 @@ sub update_filter { # --------------------------------------------------------------------------- sub update_password { - # TODO + my $pw_type = $q->param('pw_type'); + my $old_pw = $q->param('oldpassword'); + my $new_pw = $q->param('newpassword'); + my $new_pw2 = $q->param('newpassword2'); + + $warning = ''; + + $warning = 'EmptyOldPassword' if ($old_pw eq ''); + $warning = 'EmptyNewPassword' if ($new_pw eq ''); + $warning = 'DifferentNewPasswords' if ($new_pw ne $new_pw2); + + # the previous checks are critical + return (0==1) if ($warning ne ''); + + my $password = $q->param('oldpassword'); + + my $current_pw_type; + # first: check for right password before changing anything + foreach $current_pw_type (keys %LDAP_PASSWD) { + if (($current_pw_type eq $pw_type) || ($pw_type eq 'all')) { + my $ldap = Net::LDAP->new($LDAP_HOST); + my $current_pw_info = $LDAP_PASSWD{$current_pw_type}; + my $user_dn = $current_pw_info->{'dn'}; + $user_dn =~ s/_USERNAME_/$mail_user/g; + my $result = $ldap->bind($user_dn, password => $password); + if ($result->is_error) { + $warning = 'WrongPassword'; + return (0==1); + } + $ldap->unbind; + } + } + + # ok - passwords are good - now change the passwords + foreach $current_pw_type (keys %LDAP_PASSWD) { + if (($current_pw_type eq $pw_type) || ($pw_type eq 'all')) { + my $ldap = Net::LDAP->new($LDAP_HOST); + my $current_pw_info = $LDAP_PASSWD{$current_pw_type}; + my $user_dn = $current_pw_info->{'dn'}; + $user_dn =~ s/_USERNAME_/$mail_user/g; + my $result = $ldap->bind($user_dn, password => $password); + if ($result->is_error) { + # this should only happen in race conditions + $warning = 'WrongPassword'; + return (0==1); + } + + # TODO: for now only md5 is supported + my $new_pw_hash; + if ($current_pw_info->{'hash'} =~ /^md5/i ) { + $new_pw_hash = '{MD5}' . Digest::MD5::md5_base64($new_pw) . '=='; + } else { + warn "[ql-web]: this hash type is not supported yet (only md5 is available). Please take a look at ql-web.pl - it is very easy to add support for other hash types."; + $error = 'UnknownPasswordHash'; + return (0==1); + } + + $result = $ldap->modify($user_dn, + replace => [ $current_pw_info->{'attr'} => $new_pw_hash ]); + if ($result->is_error) { + $warning = 'UpdatePassword'; + warn $result->error_text; + } + + $ldap->unbind; + } + } + + return (0==0); } # --------------------------------------------------------------------------- @@ -405,6 +597,41 @@ sub is_spam_mark { # --------------------------------------------------------------------------- +sub get_spamassassin_option { + my $option = shift; + my @sa_options = &get_ldap_values('spamassassin'); + my $result = ''; + my $entry; + foreach $entry (@sa_options) { + $result .= "$1\n" if ($entry =~ /^$option\s(.*)$/); + } + if ($result eq '') { + $result = &get_spamassassin_option_default($option); + chomp $result; + return $result; + } else { + chomp $result; + return $result; + } +} + +# --------------------------------------------------------------------------- + +sub get_spamassassin_option_default { + my $option = shift; + my $current; + my $found = 0; + foreach $current (@$LDAP_SPAM_OPTIONS) { + return $current if ($found eq 1); + $found = 1 if ($current eq $option); + } + warn "[ql-web]: default value of spamassassin option '$option' not found"; + return ''; + +} + +# --------------------------------------------------------------------------- + # values will get substituted (e.g. _USERNAME_ ...) sub compare_ldap_attr { my ($attr, $value) = @_; @@ -458,7 +685,8 @@ sub get_ldap_values { if (wantarray) { return @values; } else { - return $values[0]; + return $values[0] if (@values); + return ''; } } @@ -474,7 +702,8 @@ sub load_hdf { $hdf->setValue("Config.TemplateDir", "$TEMPLATE_DIR/"); &fatal_error("Language data dir ($LANGUAGE_DIR) not found!") unless (-e $LANGUAGE_DIR); $hdf->setValue("Config.LanguageDir", "$LANGUAGE_DIR/"); - $hdf->setValue("Config.ScriptName", $ENV{'SCRIPT_NAME'}); + my $scriptname = defined($ENV{'SCRIPT_NAME'})? $ENV{'SCRIPT_NAME'} : ''; + $hdf->setValue("Config.ScriptName", $scriptname); $hdf->setValue("Config.Stylesheet", "$CSS_URL"); $hdf->setValue("Config.PageTitle", "$HTML_TITLE"); diff --git a/ql-web/trunk/template/filter_form.cs b/ql-web/trunk/template/filter_form.cs index b6286f3..935c4c6 100644 --- a/ql-web/trunk/template/filter_form.cs +++ b/ql-web/trunk/template/filter_form.cs @@ -3,28 +3,75 @@
- +
+ + +
+
+ +
+ + +
+ - - + +
diff --git a/ql-web/trunk/template/overview.cs b/ql-web/trunk/template/overview.cs index b64fa59..36f9a67 100644 --- a/ql-web/trunk/template/overview.cs +++ b/ql-web/trunk/template/overview.cs @@ -15,9 +15,9 @@
-

+

diff --git a/ql-web/trunk/template/password_form.cs b/ql-web/trunk/template/password_form.cs index dd57ac2..9be49da 100644 --- a/ql-web/trunk/template/password_form.cs +++ b/ql-web/trunk/template/password_form.cs @@ -7,13 +7,32 @@