Added a Module Config option for specifying a command other than spamassassin (such as spamc) to use in the procmailrc file. This defaults to an automatic mode, where spamc is used if spamd is running.
---- Changes since 1.300 ----
Added Module Config options for commands to run before and after saving SpamAssassin config changes.
+---- Changes since 1.360 ----
+Added the SQL and LDAP Databases page for configuring SpamAssassin to use a MySQL, PostgreSQL or LDAP database for user preferences.
print "<tr> <td valign=top><b>$text{'acl_avail'}</b></td>\n";
print "<td><select name=avail rows=6 multiple>\n";
local %avail = map { $_, 1 } split(/,/, $_[0]->{'avail'});
-foreach $a ('white', 'score', 'report', 'user', 'header', 'setup', 'procmail') {
+foreach $a ('white', 'score', 'report', 'user', 'header', 'setup', 'procmail',
+ 'db') {
printf "<option value=%s %s>%s\n",
$a, $avail{$a} ? "selected" : "", $text{$a."_title"};
}
-avail=white,score,report,user,header,priv,setup,procmail
+avail=white,score,report,user,header,priv,setup,procmail,db
--- /dev/null
+#!/usr/local/bin/perl
+# Show form for SpamAssassin DB options
+
+require './spam-lib.pl';
+&can_use_check("db");
+&ui_print_header(undef, $text{'db_title'}, "");
+$conf = &get_config();
+
+print "$text{'db_desc'}<p>\n";
+&start_form("save_db.cgi", $text{'db_header'});
+
+# Work out backend type
+$dsn = &find_value("user_scores_dsn", $conf);
+if ($dsn =~ /^DBI:([^:]+):([^:]+):([^:]+)(:(\d+))?$/) {
+ # To database
+ $mode = 1;
+ ($dbdriver, $dbdb, $dbhost, $dbport) = ($1, $2, $3, $5);
+ }
+elsif ($dsn =~ /^ldap:\/\/([^:]+)(:(\d+))?\/([^\?]+)\?([^\?]+)\?([^\?]+)\?([^=]+)=__USERNAME__/) {
+ # To LDAP
+ $mode = 3;
+ ($ldaphost, $ldapport, $ldapdn, $ldapattr, $ldapscope, $ldapuid) =
+ ($1, $3, $4, $5, $6, $7);
+ }
+elsif ($dsn) {
+ $mode = 4;
+ }
+else {
+ $mode = 0;
+ }
+
+# Generate input blocks for SQL and LDAP
+$dbtable = &ui_table_start(undef, undef, 2, [ "nowrap" ]);
+$dbtable .= &ui_table_row($text{'db_dbdriver'},
+ &ui_select("dbdriver", $dbdriver || "mysql",
+ [ [ "mysql", "MySQL" ], [ "Pg", "PostgreSQL" ] ],
+ 1, 0, 1));
+$dbtable .= &ui_table_row($text{'db_dbhost'},
+ &ui_textbox("dbhost", $dbhost, 40));
+$dbtable .= &ui_table_row($text{'db_dbdb'},
+ &ui_textbox("dbdb", $dbdb, 40));
+$dbtable .= &ui_table_row($text{'db_dbport'},
+ &ui_opt_textbox("dbport", $dbport, 5, $text{'default'}));
+$dbtable .= &ui_table_end();
+
+$ldaptable = &ui_table_start(undef, undef, 2, [ "nowrap" ]);
+$ldaptable .= &ui_table_row($text{'db_ldaphost'},
+ &ui_textbox("ldaphost", $ldaphost, 40));
+$ldaptable .= &ui_table_row($text{'db_ldapport'},
+ &ui_opt_textbox("ldapport", $ldapport, 5, $text{'default'}));
+$ldaptable .= &ui_table_row($text{'db_ldapdn'},
+ &ui_textbox("ldapdn", $ldapdn, 40));
+$ldaptable .= &ui_table_row($text{'db_ldapattr'},
+ &ui_textbox("ldapattr", $ldapattr, 20));
+$ldaptable .= &ui_table_row($text{'db_ldapscope'},
+ &ui_select("ldapscope", $ldapscope || "sub",
+ [ [ "sub", $text{'db_ldapsub'} ],
+ [ "one", $text{'db_ldapone'} ],
+ [ "base", $text{'db_ldapbase'} ] ], 1, 0, 1));
+$ldaptable .= &ui_table_row($text{'db_ldapuid'},
+ &ui_textbox("ldapuid", $ldapuid || "uid", 20));
+$ldaptable .= &ui_table_end();
+
+
+# Show backend type selector
+print "<tr> <td valign=top><b>$text{'db_dsn'}</b></td> <td nowrap>";
+print &ui_radio_table("mode", $mode,
+ [ [ 0, $text{'db_mode0'} ],
+ [ 1, $text{'db_mode1'}, $dbtable ],
+ [ 3, $text{'db_mode3'}, $ldaptable ],
+ [ 4, $text{'db_mode4'},
+ &ui_textbox("dsn", $dsn, 60) ] ]);
+print "</td> </tr>\n";
+
+print "<tr> <td colspan=2><hr></td> </tr>\n";
+
+# DB login
+print "<tr> <td><b>$text{'db_user'}</b></td> <td nowrap>";
+$user = &find("user_scores_sql_username", $conf);
+&opt_field("user_scores_sql_username", $user, 20, undef);
+print "</td> </tr>\n";
+
+# DB password
+print "<tr> <td><b>$text{'db_pass'}</b></td> <td nowrap>";
+$pass = &find("user_scores_sql_password", $conf);
+&opt_field("user_scores_sql_password", $pass, 20, undef);
+print "</td> </tr>\n";
+
+print "<tr> <td colspan=2><hr></td> </tr>\n";
+
+# LDAP login
+print "<tr> <td><b>$text{'db_luser'}</b></td> <td nowrap>";
+$user = &find("user_scores_ldap_username", $conf);
+&opt_field("user_scores_ldap_username", $user, 40, undef);
+print "</td> </tr>\n";
+
+# LDAP password
+print "<tr> <td><b>$text{'db_lpass'}</b></td> <td nowrap>";
+$pass = &find("user_scores_ldap_password", $conf);
+&opt_field("user_scores_ldap_password", $pass, 20, undef);
+print "</td> </tr>\n";
+
+
+
+&end_form(undef, $text{'save'});
+&ui_print_footer("", $text{'index_return'});
+
push(@pages, 'razor') if (!$razor && $module_info{'usermin'});
push(@pages, 'setup') if ($spam_enabled == 0);
push(@pages, 'procmail') if ($delivery_enabled == 1);
+ push(@pages, 'db') if (!$module_info{'usermin'});
@pages = grep { &can_use_page($_) } @pages;
$sfolder = $module_info{'usermin'} ? &spam_file_folder()
: undef;
connect_elogin=Failed to login to the database $1 : $2.
connect_equery=The database $1 does not contain the preferences table $2
+db_title=SQL and LDAP Databases
+db_header=Configuration storage database options
+db_dsn=Store user configurations in
+db_mode0=Configuration files
+db_mode1=SQL database
+db_mode3=LDAP database
+db_mode4=Other DSN
+db_user=SQL database username
+db_pass=SQL database password
+db_luser=LDAP server username
+db_lpass=LDAP server password
+db_dbdriver=Database type
+db_dbhost=Database server hostname
+db_dbdb=Database name
+db_dbport=Port number
+db_err=Failed to save databases
+db_edbhost=Missing or invalid SQL server hostname
+db_edbdb=Missing or invalid-looking database name
+db_edbport=Missing or invalid SQL server port number
+db_edsn=Missing other DSN
+db_eusername=Missing or invalid database username - no spaces are allowed
+db_ldaphost=LDAP server hostname
+db_ldapport=Port number
+db_ldapdn=Base DN for users
+db_ldapattr=Attribute for SpamAssassin preferences
+db_ldapscope=Search depth
+db_ldapsub=Entire subtree
+db_ldapone=One level
+db_ldapbase=Base only
+db_ldapuid=Attribute for username
+db_eldaphost=Missing or invalid LDAP server hostname
+db_eldapport=Missing or invalid LDAP server port number
+db_eldapdn=Missing or invalid base DN - no spaces are allowed
+db_eldapattr=Missing or invalid SpamAssassin attribute
+db_eldapuid=Missing or invalid username attribute
--- /dev/null
+#!/usr/local/bin/perl
+# Save LDAP and SQL database options
+
+require './spam-lib.pl';
+&error_setup($text{'db_err'});
+&can_use_check("db");
+&ReadParse();
+&execute_before("db");
+&lock_spam_files();
+$conf = &get_config();
+
+# Parse backend DSN
+if ($in{'mode'} == 0) {
+ # Files only
+ $dsn = undef;
+ }
+elsif ($in{'mode'} == 1) {
+ # Database of some type
+ gethostbyname($in{'dbhost'}) || &error($text{'db_edbhost'});
+ $in{'dbdb'} =~ /^[a-z0-9\.\-\_]+$/ || &error($text{'db_edbdb'});
+ $in{'dbport_def'} || $in{'dbport'} =~ /^\d+$/ ||
+ &error($text{'db_edbport'});
+ $dsn = join(":", "DBI", $in{'dbdriver'}, $in{'dbdb'}, $in{'dbhost'});
+ $dsn .= ":".$in{'dbport'} if (!$in{'dbport_def'});
+ }
+elsif ($in{'mode'} == 3) {
+ # LDAP
+ gethostbyname($in{'ldaphost'}) || &error($text{'db_eldaphost'});
+ $in{'ldapport_def'} || $in{'ldapport'} =~ /^\d+$/ ||
+ &error($text{'db_eldapport'});
+ $in{'ldapdn'} =~ /^\S+$/ || &error($text{'db_eldapdn'});
+ $in{'ldapattr'} =~ /^\S+$/ || &error($text{'db_eldapattr'});
+ $in{'ldapuid'} =~ /^\S+$/ || &error($text{'db_eldapuid'});
+ $dsn = "ldap://".$in{'ldaphost'}.
+ ($in{'ldapport_def'} ? "" : ":".$in{'ldapport'})."/".
+ $in{'ldapdn'}."?".$in{'ldapattr'}."?".$in{'ldapscope'}."?".
+ $in{'ldapuid'}."=__USERNAME__";
+ }
+else {
+ # Other DSN
+ $in{'dsn'} =~ /\S/ || &error($text{'db_edsn'});
+ $dsn = $in{'dsn'};
+ }
+&save_directives($conf, "user_scores_dsn", [ $dsn ], 1);
+
+# Parse username and password
+&parse_opt($conf, "user_scores_sql_username", \&username_check);
+&parse_opt($conf, "user_scores_sql_password");
+&parse_opt($conf, "user_scores_ldap_username", \&username_check);
+&parse_opt($conf, "user_scores_ldap_password");
+
+&flush_file_lines();
+&unlock_spam_files();
+&execute_after("db");
+&webmin_log("db");
+&redirect("");
+
+sub username_check
+{
+return $_[0] =~ /^\S+$/ ? undef : $text{'db_eusername'};
+}
+
}
}
$database_userpref_name = $remote_user;
- $include_config_files = 1; # XXX
+ $include_config_files = $config{'readfiles'};
$add_to_db = 1;
}
else {
$module_info{'usermin'} ? "$local_cf/user_prefs" :
"$local_cf/local.cf";
-# get_config([file])
+# get_config([file], [for-global])
# Return a structure containing the contents of the spamassassin config file
sub get_config
{
+local $forglobal = $_[1];
local @rv;
-if ($include_config_files) {
+if ($include_config_files || $forglobal) {
# Reading from file(s)
local $lnum = 0;
local $file = $_[0] || $local_cf;
local $dbh = &connect_spamassasin_db();
&error($dbh) if (!ref($dbh));
local $cmd = $dbh->prepare("select preference,value from userpref where username = ?");
- $cmd->execute($database_userpref_name);
+ $cmd->execute(!$forglobal ? $database_userpref_name :
+ $config{'dbglobal'} ? $config{'dbglobal'} : '@GLOBAL');
while(my ($name, $value) = $cmd->fetchrow()) {
local $dir = { 'name' => $name,
'value' => $value,
+ 'index' => scalar(@rv),
'mode' => $config{'mode'} };
$dir->{'words'} =
[ split(/\s+/, $dir->{'value'}) ];
elsif ($new[$i]) {
# Adding a directive
local $addmode = scalar(@old) ? $old[0]->{'mode'} :
+ $new[$i]->{'name'} =~ /^user_scores_/ ? 0 :
$add_to_db ? $config{'mode'} : 0;
if ($addmode == 0) {
# To a file
# To LDAP
# XXX
}
+ $new[$i]->{'mode'} = $addmode;
$new[$i]->{'index'} = @{$_[0]};
push(@{$_[0]}, $new[$i]);
}
sub find_default
{
if ($config{'global_cf'}) {
- local $gconf = &get_config($config{'global_cf'});
- local $v = &find_value($_[0], $gconf);
+ if (!defined($global_config_cache)) {
+ $global_config_cache = &get_config($config{'global_cf'}, 1);
+ }
+ local $v = &find_value($_[0], $global_config_cache);
return $v if (defined($v));
}
return $_[1];