Re: sam + ldap patch

Neil Sequeira <[email protected]> Thu, 18 Sep 2003 14:24:12 -0400
Newsgroups gmane.comp.horde.sam
Organization NCS Consulting Inc.
Message-ID <[email protected]>
Jan Schneider wrote:

> Zitat von Neil Sequeira <[email protected]>:
> 
>> Here's a start at an LDAP backend for sam.  It needs the > 
> Great! But please try to follow the coding standards.
> 
> Jan.
> 

An updated patch is attached.  It should follow the coding conventions 
and it fixes a bug displaying the blacklist.

BTW, this patch does a horrible job of handling errors (it silently 
ignores them!) because i'm not familiar with Horde (PEAR?) error 
reporting functions yet.  I hope to have it updated with better error 
reporting down the road (time permitting).


	-neil


-- 
sam mailing list
Frequently Asked Questions: http://horde.org/faq/
To unsubscribe, mail: [email protected]
sam.ldap.newpatch (text/x-diff, 9.5 KB)
diff -ru -N sam/config/backends.php.dist sam.new/config/backends.php.dist
--- sam/config/backends.php.dist	2003-09-06 13:53:24.000000000 -0400
+++ sam.new/config/backends.php.dist	2003-09-17 22:37:48.000000000 -0400
@@ -121,3 +121,49 @@
         ),
     ),
 );
+
+/* SpamAssassin LDAP storage example. */
+$backends['spamd_ldap'] = array(
+    'preferred' => '',
+    'hordeauth' => true,
+    'driver' => 'spamd_ldap',
+    'params' => array(
+        'ldapserver' => 'localhost',
+        'basedn' => 'ou=users,dc=example,dc=com',
+	'attribute' => 'spamassassinConfig',
+	'uid' => 'uid',
+        'option_map' => array(
+            'hit_level' => 'required_hits',
+            'subject_tag' => 'subject_tag',
+            'rewrite_sub' => 'rewrite_subject',
+            'level_stars' => 'spam_level_stars',
+            'report_header' => 'report_header',
+            'terse_report' => 'use_terse_report',
+            'defang_mime' => 'defang_mime',
+            'skip_rbl' => 'skip_rbl_checks',
+        ),
+	'defaults' => array(
+	    'hit_level' => '5',
+	    'subject_tag' => '***SPAM***',
+	    'rewrite_sub' => 1,
+	    'level_stars' => 1,
+	    'report_header' => 0,
+	    'terse_report' => 1,
+	    'defang_mime' => 1,
+	    'skip_rbl' => 1,
+	),
+	'capabilities' => array(
+		'hit_level',
+		'report_header',
+		'rewrite_sub',
+		'subject_tag',
+		'level_stars',
+		'defang_mime',
+		'whitelist_to',
+		'whitelist_from',
+		'blacklist_to',
+		'blacklist_from',
+		'skip_rbl'
+	),
+    ),
+);
diff -ru -N sam/lib/Driver/spamd_ldap.php sam.new/lib/Driver/spamd_ldap.php
--- sam/lib/Driver/spamd_ldap.php	1969-12-31 19:00:00.000000000 -0500
+++ sam.new/lib/Driver/spamd_ldap.php	2003-09-18 14:09:43.000000000 -0400
@@ -0,0 +1,246 @@
+<?php
+/**
+ * $Horde: sam/lib/Driver/spamd_ldap.php,v 1.32 2003/09/18 17:32:44 max Exp $
+ * SAM storage implementation for LDAP backend - based on spamd_sql.php.
+ * Requires SpamAssassin patch found at: 
+ *                    http://bugzilla.spamassassin.org/show_bug.cgi?id=2205
+ *
+ * Required parameters:
+ * ====================
+ *   'ldapserver'   --  The hostname of the ldap server.
+ *   'basedn'       --  The password associated with 'username'.
+ *   'attribute'    --  The database type (ie. 'pgsql', 'mysql, etc.).
+ *   'uid'          --  The communication protocol ('tcp', 'unix', etc.).
+ *
+ * @author  Chris Bowlby <[email protected]>
+ * @author  Max Kalika <[email protected]>
+ * @author  Neil Sequeira <[email protected]> 
+ * @version $Revision: 1.32 $
+ * @since   SAM 0.0.1
+ * @package sam
+ */
+
+class SAM_Driver_spamd_ldap extends SAM_Driver {
+
+    /**
+     * Handle for the current LDAP connection.
+     *
+     * @var $_linkid
+     */
+    var $_linkid;
+
+    /**
+     * Boolean indicating whether or not we're connected to the LDAP server.
+     *
+     * @var boolean $_connected
+     */
+    var $_connected = false;
+
+    /**
+     * List of the capabilities supported by this driver.
+     * This has been moved into the backend configuration
+     *  to make disabling capabilities easier.
+     *
+     * @var array $_capabilities
+     */
+/*
+    var $_capabilities = array('hit_level',
+                               'report_header',
+                               'rewrite_sub',
+                               'subject_tag',
+                               'level_stars',
+                               'defang_mime',
+                               'whitelist_to',
+                               'whitelist_from',
+                               'blacklist_to',
+                               'blacklist_from',
+                               'skip_rbl');
+*/
+    /**
+     * Constructs a new LDAP storage object.
+     *
+     * @access public
+     *
+     * @param string $user               The user who owns these SPAM options.
+     * @param optional array $params     A hash containing connection
+     *                                   parameters.
+     */
+    function SAM_Driver_spamd_ldap($user, $params = array())
+    {
+        $this->_user = $user;
+        $this->_params = $params;
+        $this->_capabilities = $params['capabilities'];
+    }
+
+    /**
+     * Converts a SAM attribute to an option that SpamAssassin will use.
+     *
+     * @access private
+     *
+     * @param string $attribute          The SAM attribute to convert.
+     *
+     * @return string   The converted SpamAssassin option or the original
+     *                  attribute if no match is found.
+     */
+    function _mapAttributeToOption($attribute)
+    {
+        return isset($this->_params['option_map'][$attribute])
+               ? $this->_params['option_map'][$attribute] : $attribute;
+    }
+
+    /**
+     * Converts a SpamAssassin option to a SAM attribute.
+     *
+     * @access private
+     *
+     * @param string $option             The SpamAssassin option to convert.
+     *
+     * @return string   The converted SAM attribute or the original option if
+     *                  no match is found.
+     */
+    function _mapOptionToAttribute($option)
+    {
+        $attribute_map = array();
+        if (isset($this->_params['option_map'])) {
+            $attribute_map = array_flip($this->_params['option_map']);
+        }
+
+        return isset($attribute_map[$option])
+               ? $attribute_map[$option] : $option;
+    }
+
+    /**
+     * Retrieve an option set from the storage backend.
+     *
+     * @access private
+     *
+     */
+    function retrieve()
+    {
+        /* Make sure we have a valid database connection. */
+        $this->_connect();
+        /* set default values */
+        $this->_setDefaults();
+        $eres = false;
+        $user = $this->_user;
+        $attrib = strtolower($this->_params['attribute']);
+        $filter = "(".$this->_params['uid']."=$user)";
+        $res = ldap_search($this->_linkid, $this->_params['basedn'], 
+                                                $filter, array($attrib));
+        if ($res) $eres = ldap_get_entries($this->_linkid, $res);
+        if ($eres && isset($eres[0][$attrib])) {
+            for ($i = 0; $i < $eres[0][$attrib]['count']; $i++) {
+                list($a,$v) = explode(" ", $eres[0][$attrib][$i]);
+                $ra = $this->_mapOptionToAttribute($a);
+                if (is_numeric($v)) {
+                    if (ereg('\.',$v)) {
+                        $newoptions[$ra][] = (float) $v;
+                    } else {
+                        $newoptions[$ra][] = (int) $v;
+                    }
+                }
+                else {
+                    $newoptions[$ra][] = $v;
+                }
+            }
+            /* go through new options and pull single values */
+            /* out of their arrays */
+            foreach($newoptions as $k => $v) {
+                if (count($v) > 1) {
+                    $this->_options[$k] = $v;
+                }
+                else {
+                    $this->_options[$k] = $v[0];
+                }
+            }
+        }
+    }
+
+    /**
+     * Set default values
+     *
+     * @access private
+     *
+     */
+    function _setDefaults()
+    {
+        $defaults = $this->_params['defaults'];
+        foreach($defaults as $a => $v) {
+            $this->_options[$a] = $v;
+        }
+    }
+    
+    /**
+     * Store an option set in the storage backend.
+     *
+     * @access private
+     *
+     * @return boolean    True on success or false on failure.
+     */
+    function store()
+    {
+        /* Make sure we have a valid database connection. */
+        if (!$this->_connect()) { return false; }
+        $user = $this->_user;
+        $attrib = $this->_params['attribute'];
+        $userdn = $this->_params['uid']."=$user,".$this->_params['basedn'];       
+        $store = $this->_options;
+        foreach($store as $a => $v) {
+            $sa = $this->_mapAttributeToOption($a);
+            if (is_array($v)) {
+                foreach($v as $av)
+                    $entry[$attrib][] = "$sa $av";
+            } else {
+                $entry[$attrib][] = "$sa $v";
+            }
+        }
+        return (ldap_modify($this->_linkid, $userdn, $entry));
+    }
+    
+    /**
+     * Attempts to open a connection to the LDAP server.
+     *
+     * @access private
+     *
+     * @return boolean    True on success or false on failure.
+     */
+    function _connect()
+    {
+        if ($this->_connected) return true;
+        $bindpass = Auth::getCredential('password');
+        $user = $this->_user;
+        $binddn = $this->_params['uid']."=$user,".$this->_params['basedn'];
+        Horde::assertDriverConfig($this->_params, 'spamd_ldap',
+            array('ldapserver', 'basedn', 'attribute', 'uid'),
+            'SAM backend', 'backends.php', '$backends');
+        for ($tries = 3; $tries > 0; $tries--) {   /* try three times */
+            $lc = ldap_connect($this->_params['ldapserver']);
+            if ($lc) {
+                $lb = ldap_bind($lc, $binddn, $bindpass);
+                if ($lb) {
+                    $this->_linkid = $lc;
+                    $this->connected = true;
+                    return true;
+                } else  {
+                    ldap_unbind($lc);
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Disconnect from the LDAP server and clean up the connection.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    function _disconnect()
+    {
+        if ($this->_connected) {
+            $this->_connected = false;
+            return ldap_unbind($this->_linkid);
+        }
+        return true;
+    }
+
+}