diff --git a/spam/acl_security.pl b/spam/acl_security.pl index 1df871e46..6b8ed1b4b 100644 --- a/spam/acl_security.pl +++ b/spam/acl_security.pl @@ -5,19 +5,34 @@ require 'spam-lib.pl'; # Output HTML for editing security options for the spam module sub acl_security_form { +# Allowed features print " $text{'acl_avail'}\n"; print " \n"; +# Config file to edit print " $text{'acl_file'}\n"; print "",&ui_opt_textbox("file", $_[0]->{'file'}, 40, $text{'acl_filedef'}), " \n"; + +# Allowed auto-whitelist users +print " $text{'acl_awl'}\n"; +print "",&ui_radio("awl_mode", $_[0]->{'awl_groups'} ? 2 : + $_[0]->{'awl_users'} ? 1 : 0, + [ [ 0, $text{'acl_awl0'}."
\n" ], + [ 1, &text('acl_awl1', + &ui_textbox("awl_users", $_[0]->{'awl_users'}, 40). + &user_chooser_button("awl_users", 1)."
\n" ], + [ 2, &text('acl_awl2', + &ui_textbox("awl_groups", $_[0]->{'awl_groups'}, 40). + &group_chooser_button("awl_users", 1)."
\n" ], + ])," \n"; } # acl_security_save(&options) @@ -26,5 +41,13 @@ sub acl_security_save { $_[0]->{'avail'} = join(",", split(/\0/, $in{'avail'})); $_[0]->{'file'} = $in{'file_def'} ? undef : $in{'file'}; +delete($_[0]->{'awl_users'}); +delete($_[0]->{'awl_groups'}); +if ($in{'awl_mode'} == 1) { + $_[0]->{'awl_users'} = $in{'awl_users'}; + } +elsif ($in{'awl_mode'} == 2) { + $_[0]->{'awl_groups'} = $in{'awl_groups'}; + } } diff --git a/spam/delete_awl.cgi b/spam/delete_awl.cgi index 5752ea87b..b07a51630 100644 --- a/spam/delete_awl.cgi +++ b/spam/delete_awl.cgi @@ -7,7 +7,7 @@ require './spam-lib.pl'; &ReadParse(); # Check stuff -&open_auto_whitelist_dbm() || &error($text{'dawl_eopen'}); +&open_auto_whitelist_dbm($in{'user'}) || &error($text{'dawl_eopen'}); @d = split(/\0/, $in{'d'}); @d || &error($text{'dawl_enone'}); @@ -18,5 +18,6 @@ foreach $d (@d) { } &close_auto_whitelist_dbm(); -&redirect("edit_awl.cgi?search=".&urlize($in{'search'})); +&redirect("edit_awl.cgi?search=".&urlize($in{'search'}). + "&user=".&urlize($in{'user'})); diff --git a/spam/edit_awl.cgi b/spam/edit_awl.cgi index e5e2dd40d..15ffc853f 100644 --- a/spam/edit_awl.cgi +++ b/spam/edit_awl.cgi @@ -1,19 +1,40 @@ #!/usr/local/bin/perl # Display entries in the auto-whitelist +# XXX delete all +# XXX access control require './spam-lib.pl'; &can_use_check("awl"); &ui_print_header(undef, $text{'awl_title'}, ""); &ReadParse(); +$formno = 0; + +# Check if we need a username +if (&supports_auto_whitelist() == 2) { + print &ui_form_start("edit_awl.cgi"); + print "$text{'awl_user'}\n"; + print &ui_user_textbox("user", $in{'user'}),"\n", + &ui_submit($text{'awl_uok'}); + print &ui_form_end(); + + if (!$in{'user'}) { + # Can't do any more + &ui_print_footer("", $text{'index_return'}); + return; + } + } # Open the DBM, or give up -$ok = &open_auto_whitelist_dbm(); -if ($ok == 0) { - &ui_print_endpage(&text('awl_cannot', - &get_auto_whitelist_file())); +$awf = &get_auto_whitelist_file($in{'user'}); +$ok = &open_auto_whitelist_dbm($in{'user'}); +if (!$awf) { + &ui_print_endpage("".&text('awl_nofile').""); + } +elsif ($ok == 0) { + &ui_print_endpage("".&text('awl_cannot', $awf).""); } elsif ($ok < 0) { - &ui_print_endpage(&text('awl_empty', &get_auto_whitelist_file())); + &ui_print_endpage("".&text('awl_empty', $awf).""); } # Show search form @@ -22,7 +43,9 @@ print &ui_form_start("edit_awl.cgi"); print "$text{'awl_search'}\n"; print &ui_textbox("search", $in{'search'}, 30),"\n", &ui_submit($text{'awl_ok'}); +print &ui_hidden("user", $in{'user'}); print &ui_form_end(); +$formno++; if ($in{'search'}) { @keys = grep { /\Q$in{'search'}\E/i } @keys; print &text('awl_searching', @@ -32,7 +55,8 @@ if ($in{'search'}) { # Show table print &ui_form_start("delete_awl.cgi", "post"); print &ui_hidden("search", $in{'search'}); -@links = ( &select_all_link("d", 1), &select_invert_link("d", 1) ); +print &ui_hidden("user", $in{'user'}); +@links = ( &select_all_link("d", $formno), &select_invert_link("d", $formno) ); @tds = ( "width=5" ); print &ui_links_row(\@links); print &ui_columns_start([ "", diff --git a/spam/index.cgi b/spam/index.cgi index b55ec466c..d2d61ac4e 100755 --- a/spam/index.cgi +++ b/spam/index.cgi @@ -130,7 +130,7 @@ else { push(@pages, 'setup') if ($spam_enabled == 0); push(@pages, 'procmail') if ($delivery_enabled == 1); push(@pages, 'db') if (!$module_info{'usermin'}); - push(@pages, 'awl') if (&get_auto_whitelist_file()); + push(@pages, 'awl') if (&supports_auto_whitelist()); @pages = grep { &can_use_page($_) } @pages; $sfolder = $module_info{'usermin'} ? &spam_file_folder() : undef; diff --git a/spam/lang/en b/spam/lang/en index 9d99df8ef..0df69b209 100644 --- a/spam/lang/en +++ b/spam/lang/en @@ -254,6 +254,10 @@ apply_none=No SpamAssassin daemon processes found! acl_avail=Icons available to user acl_file=SpamAssassin configuration file to edit acl_filedef=Global config file +acl_awl=Allowed users for auto-whitelist editing +acl_awl0=All users +acl_awl1=Only listed users $1 +acl_awl2=Only members of groups $1 search_escore=Missing or invalid spam score search_results5=$1 mail messages with spam score at or above $2 .. @@ -351,6 +355,9 @@ awl_unknown=Unknown awl_delete=Remove Selected Entries awl_cannot=Usermin could not open your auto-whitelist file $1, perhaps because it is in an un-supported format. awl_empty=Your auto-whitelist file $1 does not contain any entries. It will be populated by SpamAsssassin as mail is processed by the system. +awl_user=Show auto-whitelist for user: +awl_uok=Show +awl_nofile=This user does not have an auto-whitelist file. dawl_err=Failed to delete from auto-whitelist dawl_eopen=Could not open whitelist diff --git a/spam/spam-lib.pl b/spam/spam-lib.pl index f93564a53..ee1f5921b 100644 --- a/spam/spam-lib.pl +++ b/spam/spam-lib.pl @@ -933,22 +933,25 @@ $get_ldap_user_cache{$user} = $uinfo; return $uinfo; } -# get_auto_whitelist_file() +# get_auto_whitelist_file([user]) # Returns the base path to the auto whitelist DBM, if any. sub get_auto_whitelist_file { +local ($user) = @_; local $conf = &get_config(); local $awp = &find("auto_whitelist_path", $conf); if (!$awp) { - $awp = &find_default("auto_whitelist_path", - "~/.spamassassin/auto-whitelist"); + $awp = &find_default("auto_whitelist_path"); } +$awp ||= "~/.spamassassin/auto-whitelist"; if ($awp !~ /^\//) { # Make absolute - return undef if (!$module_info{'usermin'}); - $awp =~ s/^(\~|\$HOME)\//$remote_user_info[7]\//; + local @uinfo = $module_info{'usermin'} ? @remote_user_info : + $user ? getpwnam($user) : ( ); + return undef if (scalar(@uinfo) == 0); + $awp =~ s/^(\~|\$HOME)\//$uinfo[7]\//; if ($awp !~ /^\//) { - $awp = "$remote_user_info[7]/$awp"; + $awp = "$uinfo[7]/$awp"; } } # Does it exist? @@ -959,12 +962,13 @@ if (!-r $awp) { return $awp; } -# open_auto_whitelist_dbm() +# open_auto_whitelist_dbm([user]) # Ties the %awl hash to the autowhitelist DBM file. Returns 1 if successful, or # 0 if it could not be opened, or -1 if empty. sub open_auto_whitelist_dbm { -local $awp = &get_auto_whitelist_file(); +local ($user) = @_; +local $awp = &get_auto_whitelist_file($user); return 0 if (!$awp); local $anyok; foreach my $cls ('DB_File', 'GDBM_File', 'SDBM_File') { @@ -987,5 +991,45 @@ sub close_auto_whitelist_dbm untie(%awl); } +# supports_auto_whitelist() +# Returns 1 if SpamAssassin is doing auto-whitelisting for the current user, +# 2 if for multiple users. +sub supports_auto_whitelist +{ +if ($module_info{'usermin'}) { + return &get_auto_whitelist_file() ? 1 : 0; + } +else { + return 2; + } +} + +sub can_edit_awl +{ +local ($user) = @_; +return 1 if ($module_info{'usermin'}); # Only one user anyway +if ($_[0]->{'awl_users'}) { + # Check if on user list + return &indexof($user, split(/\s+/, $_[0]->{'awl_users'})) >= 0; + } +elsif ($_[0]->{'awl_groups'}) { + # Check if the user is a member of any of the allowed groups + local %ugroups; + local @uinfo = getpwnam($user); + return 0 if (!defined(@uinfo)); + local @ginfo = getgrgid($uinfo[3]); + $ugroups{$ginfo[0]}++ if (defined(@ginfo)); + foreach my $o (&other_groups($user)) { + $ugroups{$o}++; + } + local @can = grep { $ugroups{$_} } split(/\s+/, $_[0]->{'awl_groups'}); + return @can ? 1 : 0; + } +else { + # No restrictions + return 1; + } +} + 1;