Added SQL and LDAP page
authorJamie Cameron <jcameron@webmin.com>
Mon, 8 Oct 2007 05:29:46 +0000 (05:29 +0000)
committerJamie Cameron <jcameron@webmin.com>
Mon, 8 Oct 2007 05:29:46 +0000 (05:29 +0000)
spam/CHANGELOG
spam/acl_security.pl
spam/defaultacl
spam/edit_db.cgi [new file with mode: 0755]
spam/images/db.gif [new file with mode: 0644]
spam/index.cgi
spam/lang/en
spam/save_db.cgi [new file with mode: 0755]
spam/spam-lib.pl

index bfdc31d..569a42e 100644 (file)
@@ -8,3 +8,5 @@ Updated the setup and procmail delivery pages to allow forwarding of spam to an
 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.
index 3bf6146..1df871e 100644 (file)
@@ -8,7 +8,8 @@ sub acl_security_form
 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"};
        }
index be978fc..e0a08dc 100644 (file)
@@ -1 +1 @@
-avail=white,score,report,user,header,priv,setup,procmail
+avail=white,score,report,user,header,priv,setup,procmail,db
diff --git a/spam/edit_db.cgi b/spam/edit_db.cgi
new file mode 100755 (executable)
index 0000000..3df68a3
--- /dev/null
@@ -0,0 +1,107 @@
+#!/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'});
+
diff --git a/spam/images/db.gif b/spam/images/db.gif
new file mode 100644 (file)
index 0000000..7de1ebb
Binary files /dev/null and b/spam/images/db.gif differ
index 575e6b1..3e61469 100755 (executable)
@@ -129,6 +129,7 @@ else {
                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;
index dcb8fb2..5fe0d73 100644 (file)
@@ -295,3 +295,38 @@ connect_emysql=Failed to load the database driver $1
 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
diff --git a/spam/save_db.cgi b/spam/save_db.cgi
new file mode 100755 (executable)
index 0000000..e132db8
--- /dev/null
@@ -0,0 +1,62 @@
+#!/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'};
+}
+
index 8a58be1..3621330 100644 (file)
@@ -20,7 +20,7 @@ if ($module_info{'usermin'}) {
                        }
                }
        $database_userpref_name = $remote_user;
-       $include_config_files = 1;      # XXX
+       $include_config_files = $config{'readfiles'};
        $add_to_db = 1;
        }
 else {
@@ -40,12 +40,13 @@ $add_cf = !-d $local_cf ? $local_cf :
          $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;
@@ -91,10 +92,12 @@ if ($config{'mode'} == 1 || $config{'mode'} == 2) {
        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'}) ];
@@ -210,6 +213,7 @@ for($i=0; $i<@old || $i<@new; $i++) {
        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
@@ -231,6 +235,7 @@ for($i=0; $i<@old || $i<@new; $i++) {
                        # To LDAP
                        # XXX
                        }
+               $new[$i]->{'mode'} = $addmode;
                $new[$i]->{'index'} = @{$_[0]};
                push(@{$_[0]}, $new[$i]);
                }
@@ -430,8 +435,10 @@ else {
 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];