Branch 'pykolab-0.5' - 14 commits - configure.ac conf/kolab.conf cyruslib.py kolabd/process.py pykolab/auth pykolab/setup pykolab/utils.py saslauthd/__init__.py share/Makefile.am share/templates wallace/__init__.py wallace/modules.py

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Wed May 8 12:15:55 CEST 2013


 conf/kolab.conf                         |    7 +
 configure.ac                            |    2 
 cyruslib.py                             |  150 +++++++++++++++++-----
 kolabd/process.py                       |    8 +
 pykolab/auth/__init__.py                |    2 
 pykolab/auth/ldap/__init__.py           |   58 +++++---
 pykolab/auth/ldap/cache.py              |   24 ++-
 pykolab/setup/setup_freebusy.py         |   99 +++-----------
 pykolab/utils.py                        |   25 +++
 saslauthd/__init__.py                   |    1 
 share/Makefile.am                       |   22 ---
 share/templates/freebusy/config.php.tpl |  212 --------------------------------
 share/templates/zpush/config.php.tpl    |  106 ----------------
 wallace/__init__.py                     |   27 +++-
 wallace/modules.py                      |   23 +++
 15 files changed, 277 insertions(+), 489 deletions(-)

New commits:
commit 4e8e861f36982644397f67e0f08f62cb3e63623d
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon May 6 14:52:04 2013 +0100

    Release 0.5.12

diff --git a/configure.ac b/configure.ac
index 85374e6..eba2348 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([pykolab], 0.5.11)
+AC_INIT([pykolab], 0.5.12)
 AC_SUBST([RELEASE], 1)
 
 AC_CONFIG_SRCDIR(pykolab/constants.py.in)


commit 256c6a4f69b6799a94c10721e12927b4a7e9334b
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon May 6 14:51:50 2013 +0100

    Remove templates we are not using anymore

diff --git a/share/Makefile.am b/share/Makefile.am
index 4bfff10..b3174e1 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -2,34 +2,16 @@ templatedir = $(datadir)/kolab/templates
 template_DATA = \
 	$(wildcard templates/*.tpl)
 
-fbtemplatedir = $(datadir)/kolab/templates/freebusy
-fbtemplate_DATA = \
-	$(wildcard templates/freebusy/*.tpl)
-
-hordetemplatedir = $(datadir)/kolab/templates/horde/conf.d/
-hordetemplate_DATA = \
-	$(wildcard templates/horde/conf.d/*.tpl)
-
 rctemplatedir = $(datadir)/kolab/templates/roundcubemail
 rctemplate_DATA = \
 	$(wildcard templates/roundcubemail/*.tpl)
 
-zpushtemplatedir = $(datadir)/kolab/templates/zpush
-zpushtemplate_DATA = \
-	$(wildcard templates/zpush/*.tpl)
-
 EXTRA_DIST = \
 	$(template_DATA) \
-	$(fbtemplate_DATA) \
-	$(hordetemplate_DATA) \
-	$(rctemplate_DATA) \
-	$(zpushtemplate_DATA)
+	$(rctemplate_DATA)
 
 install-exec-local:
 	mkdir -p \
 		$(DESTDIR)/$(sysconfdir)/kolab/templates \
-		$(DESTDIR)/$(sysconfdir)/kolab/templates/freebusy \
-		$(DESTDIR)/$(sysconfdir)/kolab/templates/horde/conf.d \
-		$(DESTDIR)/$(sysconfdir)/kolab/templates/roundcubemail \
-		$(DESTDIR)/$(sysconfdir)/kolab/templates/zpush
+		$(DESTDIR)/$(sysconfdir)/kolab/templates/roundcubemail
 


commit 1ae33d76b8eaea3509d04fda08b05c4c7c757588
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Tue Apr 30 16:42:44 2013 +0100

    These files can go too

diff --git a/share/templates/freebusy/config.php.tpl b/share/templates/freebusy/config.php.tpl
deleted file mode 100644
index f41195a..0000000
--- a/share/templates/freebusy/config.php.tpl
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-/**
- * This file provides configuration settings for both the freebusy.php
- * and the pfb.php scripts.
- *
- * \$Horde: framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/config.php,v 1.4.2.2 2010/07/22 13:55:30 wrobel Exp \$
- *
- * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
- *
- * @author  Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
- * @author  Gunnar Wrobel <p at rdus.de>
- * @author  Thomas Arendsen Hein <thomas at intevation.de>
- * @package Kolab_FreeBusy
- */
-
-\$conf = array();
-
-/* Horde::Log configuration */
-\$conf['log']['enabled']          = true;
-\$conf['log']['priority']         = PEAR_LOG_DEBUG;
-\$conf['log']['type']             = 'file';
-\$conf['log']['name']             = '/var/log/kolab/freebusy/freebusy.log';
-\$conf['log']['ident']            = 'Kolab Free/Busy';
-\$conf['log']['params']['append'] = true;
-
-/* PHP error logging */
-ini_set('error_log', '/var/log/kolab/freebusy/php.log');
-
-/* Horde::Kolab::LDAP configuration */
-\$conf['kolab']['ldap']['server'] = 'localhost';
-\$conf['kolab']['ldap']['basedn'] = '$ldap_base_dn';
-\$conf['kolab']['ldap']['phpdn']  = '$ldap_service_bind_dn';
-\$conf['kolab']['ldap']['phppw']  = '$ldap_service_bind_pw';
-
-/* Horde::Kolab::IMAP configuration */
-\$conf['kolab']['imap']['server']   = 'localhost';
-\$conf['kolab']['imap']['port']     = 143;
-\$conf['kolab']['imap']['protocol'] = 'imap/tls/novalidate-cert/readonly';
-\$conf['kolab']['imap']['namespaces'] = array(
-    array('type' => 'personal', 'name' => '', 'delimiter' => '/'),
-    array('type' => 'other', 'name' => 'Other Users', 'delimiter' => '/'),
-    array('type' => 'shared', 'name' => 'Shared Folders', 'delimiter' => '/'),
-);
-
-/* Horde::Auth configuration */
-\$conf['auth']['params']['login_block'] = 0;
-\$conf['auth']['checkbrowser']          = false;
-\$conf['auth']['checkip']               = false;
-\$conf['umask'] = false;
-
-\$conf['auth']['driver'] = 'imap';
-\$conf['auth']['params']['hostspec'] = 'localhost';
-\$conf['auth']['params']['protocol'] = 'imap/tls/novalidate-cert';
-
-/* Allow special users to log into the system */
-\$conf['kolab']['imap']['allow_special_users'] = true;
-
-/* Do not record login attempts */
-\$conf['auth']['params']['login_block'] = false;
-
-/* Kolab::Freebusy configuration */
-
-/* Should we redirect using a Location header, if the user is not local? If this
- * is false we silently download the file ourselves and output it so that it
- * looks as though the free/busy information is coming from us.
- */
-\$conf['fb']['redirect']     = false;
-
-/* What is the address of the current server where the calendar data is stored?
- * This is also used as the LDAP server address where user objects reside.
- */
-\$conf['kolab']['freebusy']['server']  = 'http://' . \$_SERVER["HTTP_HOST"] . '/freebusy';
-
-/* What is our default mail domain? This is used if any users do not have
- * '@domain' specified after their username as part of their email address.
- */
-\$conf['fb']['email_domain'] = '$primary_domain';
-
-/* Location of the cache files */
-\$conf['fb']['cache_dir']    = '/tmp';
-\$conf['fb']['cache']['driver'] = 'sql';
-\$conf['fb']['cache']['params']['phptype'] = 'mysql';
-\$conf['fb']['cache']['params']['username'] = 'roundcube';
-\$conf['fb']['cache']['params']['password'] = '$mysql_roundcube_password';
-\$conf['fb']['cache']['params']['hostspec'] = 'localhost';
-\$conf['fb']['cache']['params']['database'] = 'roundcube';
-\$conf['fb']['cache']['params']['charset'] = 'utf-8';
-
-/* What db type to use for the freebusy caches */
-\$conf['fb']['dbformat']     = 'db4';
-
-/* Should we send a Content-Type header, indicating what the mime type of the
- * resulting VFB file is?
- */
-\$conf['fb']['send_content_type'] = false;
-
-/* Should we send a Content-Length header, indicating how large the resulting
- * VFB file is?
- */
-\$conf['fb']['send_content_length'] = false;
-
-/* Should we send a Content-Disposition header, indicating what the name of the
- * resulting VFB file should be?
- */
-\$conf['fb']['send_content_disposition'] = false;
-
-/* Should we use ACLs or does everybody get full rights? DO NOT set
- * this to false if you don't know what you are doing. Your free/busy
- * service should not be visible to any outside networks when
- * disabling the use of ACL settings.
- */
-\$conf['fb']['use_acls'] = true;
-
-/* How many days in advance should the free/busy information be calculated? This
- * is the default value that can be overwritten by the kolabFreeBusyFuture
- * attribute of the users LDAP account.
- */
-\$conf['fb']['future_days'] = 180;
-
-/* The resulting vCalendar file is being cached. The following setting
- * determines how many seconds it will be delivered without checking if
- * the contents of the file might have changed. A negative setting disables
- * caching (which is currently required for the resource management to work).
- */
-\$conf['fb']['vcal_cache']['min_age'] = -1;
-
-/* The resulting vCalendar file is being cached. The following setting
- * determines after how many seconds it will be considered too old for
- * delivery and a refresh of its contents will be enforced.
- */
-\$conf['fb']['vcal_cache']['max_age'] = 259200;
-
-/* The IMAP namespaces on the server. @TODO: Should obviously be
- * auto-detected.
- */
-\$conf['fb']['namespace']['personal'] = '';
-\$conf['fb']['namespace']['other'] = 'Other Users';
-
-/* In most cases you can rely on the standard event status to free/busy status
- * mapping. For the default kolab server this will mean that only the event
- * status "free" will be mapped to the free/busy status "FREE". All other event
- * status ("tentative", "busy", "outofoffice") will be mapped to "BUSY".
- *
- * If this mapping should be modified you can define it like this:
- *
- * \$conf['fb']['status_map'] = array(
- *    Horde_Kolab_FreeBusy_Object_Event::STATUS_TENTATIVE =>
- *    Horde_Kolab_FreeBusy_Helper_Freebusy_StatusMap::STATUS_BUSY_TENTATIVE,
- *    Horde_Kolab_FreeBusy_Object_Event::STATUS_OUTOFOFFICE =>
- *    'X-OUT-OF-OFFICE',
- * );
- */
-require_once 'Horde/Kolab/FreeBusy/Object/Event.php';
-require_once 'Horde/Kolab/FreeBusy/Helper/Freebusy/StatusMap.php';
-\$conf['fb']['status_map'] = array(
-    Horde_Kolab_FreeBusy_Object_Event::STATUS_TENTATIVE =>
-    Horde_Kolab_FreeBusy_Helper_Freebusy_StatusMap::STATUS_BUSY_TENTATIVE,
-    Horde_Kolab_FreeBusy_Object_Event::STATUS_OUTOFOFFICE =>
-    'X-OUT-OF-OFFICE',
-);
-
-/* Are there remote servers on which users have additional (shared)
- * folders? In that case free/busy information should also be fetched
- * from these servers.
- *
- * Add them like this:
- *
- * array('remote1.example.com', 'remote2.example.com')
- */
-\$conf['fb']['remote_servers'] = array();
-
-/* Is there an exchange server that you want to relay through this free/busy
- * application?
- *
- * Configure it like this:
- *
- * \$conf['fb']['exchange_server'] = array(
- *    'url' => 'https://example.com',
- *    'interval' => 30,
- * );
- */
-#\$conf['fb']['exchange_server'] = array(
-#    'url' => 'http://test90-9.test90.kolabsys.com',
-#    'interval' => 30,
-#    'username' => 'kolabservice',
-#    'password' => 'SomePass',
-#);
-
-/**
- * Ensure we use the Kolab group driver when handling groups.
- */
-\$conf['group']['driver'] = 'kolab';
-\$conf['group']['cache'] = false;
-
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-//
-// If you modify this file, please do not forget to also modify the
-// template in kolabd!
-//
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-// DEBUGGING
-// =========
-//
-// Activate this to see the log messages on the screen
-// \$conf['log']['type'] = 'display';
-//
-// Activate this to see the php messages on the screen
-// ini_set('display_errors', 1);
-//
-// Both settings will disrupt header delivery (which should not cause a
-// problem).
diff --git a/share/templates/zpush/config.php.tpl b/share/templates/zpush/config.php.tpl
deleted file mode 100644
index 9480ae4..0000000
--- a/share/templates/zpush/config.php.tpl
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-    /***********************************************
-    * File      :   config.php
-    * Project   :   Z-Push
-    * Descr     :   Main configuration file
-    *
-    */
-
-    define('KOLAB_SERVER', "$ldap_ldap_uri");
-    define('KOLAB_LDAP_BASE',"$ldap_base_dn");
-    define('KOLAB_BIND_DN',"$ldap_service_bind_dn");
-    define('KOLAB_BIND_PW',"$ldap_service_bind_pw");
-    define("KOLAB_LDAP_ACL","");
-    define('KOLAB_IMAP_SERVER', "$imap_server");
-
-    // Defines the default time zone
-    if (function_exists("date_default_timezone_set")){
-        date_default_timezone_set(date_default_timezone_get());
-    }
-
-    // Defines the base path on the server, terminated by a slash
-    define('BASE_PATH', dirname(\$_SERVER['SCRIPT_FILENAME']) . "/");
-
-    // Define the include paths
-    ini_set(
-            'include_path',
-            BASE_PATH . "include/" . PATH_SEPARATOR .
-            BASE_PATH . PATH_SEPARATOR .
-            ini_get('include_path') . PATH_SEPARATOR .
-            "/usr/share/php/" . PATH_SEPARATOR .
-            "/usr/share/php5/" . PATH_SEPARATOR .
-            "/usr/share/pear/"
-    );
-
-    define('STATE_DIR', 'state');
-
-    // Try to set unlimited timeout
-    define('SCRIPT_TIMEOUT', 0);
-
-    //Max size of attachments to display inline. Default is 1MB
-    define('MAX_EMBEDDED_SIZE', 1048576);
-
-    // Device Provisioning
-    define('PROVISIONING', true);
-
-    // This option allows the 'loose enforcement' of the provisioning policies for older
-    // devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution
-    // false (default) - Enforce provisioning for all devices
-    // true - allow older devices, but enforce policies on devices which support it
-    define('LOOSE_PROVISIONING', false);
-    // Default conflict preference
-    // Some devices allow to set if the server or PIM (mobile)
-    // should win in case of a synchronization conflict
-    //   SYNC_CONFLICT_OVERWRITE_SERVER - Server is overwritten, PIM wins
-    //   SYNC_CONFLICT_OVERWRITE_PIM    - PIM is overwritten, Server wins (default)
-    define('SYNC_CONFLICT_DEFAULT', SYNC_CONFLICT_OVERWRITE_PIM);
-
-    // The data providers that we are using (see configuration below
-    \$BACKEND_PROVIDER = "BackendKolab";
-
-    define("KOLAB_LDAP_ACL","");
-    define('KOLAB_IMAP_NAMESPACES', Array(
-                'personal' => "",
-                'shared' => "Shared Folders",
-                'users' => "Other Users"
-            )
-        );
-
-    define('KOLAB_IMAP_PORT', 143);
-    define('KOLAB_IMAP_OPTIONS', "/tls/novalidate-cert");
-
-    define('KOLAB_INDEX',"/var/cache/kolab/z-push/kolabindex");
-
-    //KolabMode
-    //  0 = FlatMode
-    //  1 = FolderMode
-    //  2 = try to determine the mode
-    define("KOLAB_MODE",2);
-    // define which mobile support foldermode
-    // this list is checked if KOLAB_MODE is set to 2
-    define("KOLAB_MOBILES_FOLDERMODE","iphone:ipod:ipad");
-    // folders by default if annotation is not found
-    // possiblename1:possiblename2: ......
-    // if no folders found the last found will be the default
-    define('KOLAB_DEFAULTFOLDER_DIARY',"calendar:kalender:calendrier:agenda");
-    define('KOLAB_DEFAULTFOLDER_CONTACT',"contacts:kontact");
-    define('KOLAB_DEFAULTFOLDER_TASK',"task:taske");
-    // If 1: shared folders will be read-only, even if the user have rights on it
-    define('KOLAB_SHAREDFOLDERS_RO',"1");
-
-    // Logfile
-    define('KOLAB_LOGFILE',"/var/log/z-push/access.log");
-     //For Gal
-    define('SYNC_GAL_DISPLAYNAME','cn');
-    define('SYNC_GAL_PHONE','telephonenumber');
-    define('SYNC_GAL_OFFICE', '');
-    define('SYNC_GAL_TITLE','title');
-    define('SYNC_GAL_COMPANY','o');
-    define('SYNC_GAL_ALIAS','uid');
-    define('SYNC_GAL_FIRSTNAME','givenname');
-    define('SYNC_GAL_LASTNAME','sn');
-    define('SYNC_GAL_HOMEPHONE','homephone');
-    define('SYNC_GAL_MOBILEPHONE','mobile');
-    define('SYNC_GAL_EMAILADDRESS','mail');
-
-?>


commit f804fb79958caea1c1bc13585b76608e0cca398d
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Tue Apr 30 16:41:35 2013 +0100

    Supply setup-kolab routines for the new kolab-freebusy service

diff --git a/pykolab/setup/setup_freebusy.py b/pykolab/setup/setup_freebusy.py
index 9d99ca9..32b3310 100644
--- a/pykolab/setup/setup_freebusy.py
+++ b/pykolab/setup/setup_freebusy.py
@@ -17,9 +17,8 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #
 
-from Cheetah.Template import Template
+from ConfigParser import RawConfigParser
 import os
-import subprocess
 import sys
 import time
 
@@ -39,92 +38,44 @@ def __init__():
             'freebusy',
             execute,
             description=description(),
-            after=['mysql','ldap', 'roundcube']
+            after=['ldap']
         )
 
 def description():
     return _("Setup Free/Busy.")
 
 def execute(*args, **kw):
-    if not os.path.isfile('/etc/kolab/freebusy/config.php'):
+    if not os.path.isfile('/etc/kolab-freebusy/config.ini') and not os.path.isfile('/etc/kolab-freebusy/config.ini.sample'):
         log.error(_("Free/Busy is not installed on this system"))
         return
 
-    if not hasattr(conf, 'mysql_roundcube_password'):
-        print >> sys.sdterr, utils.multiline_message(
-                _("""
-                        Please supply the MySQL password for the 'roundcube'
-                        user. You have supplied this password earlier, and it is
-                        available from the database URI setting in
-                        /etc/roundcubemail/db.inc.php.
-                    """)
-            )
-
-        conf.mysql_roundcube_password = utils.ask_question(
-                _("MySQL roundcube password"),
-                password=True,
-                confirm=True
-            )
+    if not os.path.isfile('/etc/kolab-freebusy/config.ini'):
+        os.rename('/etc/kolab-freebusy/config.ini.sample', '/etc/kolab-freebusy/config.ini')
 
     freebusy_settings = {
-            'ldap_base_dn': conf.get('ldap', 'base_dn'),
-            'ldap_ldap_uri': conf.get('ldap', 'ldap_uri'),
-            'ldap_service_bind_dn': conf.get('ldap', 'service_bind_dn'),
-            'ldap_service_bind_pw': conf.get('ldap', 'service_bind_pw'),
-            'primary_domain': conf.get('kolab', 'primary_domain'),
-            'mysql_roundcube_password': conf.mysql_roundcube_password
+            'directory "kolab-ldap"': {
+                    'host': conf.get('ldap', 'ldap_uri'),
+                    'base_dn': conf.get('ldap', 'base_dn'),
+                    'bind_dn': conf.get('ldap', 'service_bind_dn'),
+                    'bind_pw': conf.get('ldap', 'service_bind_pw'),
+                    'fbsource': 'file:/var/lib/kolab-freebusy/%mail.ifb',
+                },
+            'httpauth': {
+                }
         }
 
-    want_files = [
-            'config.php',
-        ]
-
-    for want_file in want_files:
-        template_file = None
-        if os.path.isfile('/etc/kolab/templates/freebusy/%s.tpl' % (want_file)):
-            template_file = '/etc/kolab/templates/freebusy/%s.tpl' % (want_file)
-        elif os.path.isfile('/usr/share/kolab/templates/freebusy/%s.tpl' % (want_file)):
-            template_file = '/usr/share/kolab/templates/freebusy/%s.tpl' % (want_file)
-        elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'freebusy', '%s.tpl' % (want_file)))):
-            template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'freebusy', '%s.tpl' % (want_file)))
-
-        if not template_file == None:
-            log.debug(_("Using template file %r") % (template_file), level=8)
-            fp = open(template_file, 'r')
-            template_definition = fp.read()
-            fp.close()
-
-            t = Template(template_definition, searchList=[freebusy_settings])
-            log.debug(
-                    _("Successfully compiled template %r, writing out to %r") % (
-                            template_file,
-                            '/etc/kolab/freebusy/%s' % (want_file)
-                        ),
-                    level=8
-                )
-
-            fp = open('/etc/kolab/freebusy/%s' % (want_file), 'w')
-            fp.write(t.__str__())
-            fp.close()
+    cfg_parser = RawConfigParser()
+    cfg_parser.read('/etc/kolab-freebusy/config.ini')
 
-    time.sleep(2)
+    for section in freebusy_settings.keys():
+        if len(freebusy_settings[section].keys()) < 1:
+            cfg_parser.remove_section(section)
+            continue
 
-    if os.path.isfile('/bin/systemctl'):
-        subprocess.call(['/bin/systemctl', 'restart', 'httpd.service'])
-    elif os.path.isfile('/sbin/service'):
-        subprocess.call(['/sbin/service', 'httpd', 'restart'])
-    elif os.path.isfile('/usr/sbin/service'):
-        subprocess.call(['/usr/sbin/service','apache2','restart'])
-    else:
-        log.error(_("Could not start the webserver server service."))
+        for key in freebusy_settings[section].keys():
+            cfg_parser.set(section, key, freebusy_settings[section][key])
 
-    if os.path.isfile('/bin/systemctl'):
-        subprocess.call(['/bin/systemctl', 'enable', 'httpd.service'])
-    elif os.path.isfile('/sbin/chkconfig'):
-        subprocess.call(['/sbin/chkconfig', 'httpd', 'on'])
-    elif os.path.isfile('/usr/sbin/update-rc.d'):
-        subprocess.call(['/usr/sbin/update-rc.d', 'apache2', 'defaults'])
-    else:
-        log.error(_("Could not configure to start on boot, the " + \
-                "webserver server service."))
+    fp = open('/etc/kolab-freebusy/config.ini', "w+")
+    cfg_parser.write(fp)
+    fp.close()
 


commit 0f2f1537e9721bf16e5f16248f7db343b22068c7
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 15:43:33 2013 +0200

    Add a note on the possible value of this Active Directory unique_attribute

diff --git a/conf/kolab.conf b/conf/kolab.conf
index f34445c..1f49494 100644
--- a/conf/kolab.conf
+++ b/conf/kolab.conf
@@ -122,6 +122,8 @@ quota_attribute = mailquota
 ;
 ; For OpenLDAP, use 'entrydn' - the 'entryUUID' can regrettably not be searched
 ; with.
+; 
+; For Active Directory, use 'objectsid'.
 unique_attribute = nsuniqueid
 
 ; Attribute names that hold valid, internal recipient addresses. Note the use


commit 507515dd994483b997a49dc0208680108f560510
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 15:30:39 2013 +0200

    Handle objectSid binary blobs to be a unique_attribute attribute name

diff --git a/pykolab/utils.py b/pykolab/utils.py
index 2ba5c89..57617a7 100644
--- a/pykolab/utils.py
+++ b/pykolab/utils.py
@@ -21,6 +21,7 @@ import getpass
 import grp
 import os
 import pwd
+import struct
 import sys
 
 import pykolab
@@ -271,16 +272,22 @@ def normalize(_object):
             if type(_object[key]) == list:
                 if _object[key] == None:
                     continue
+
                 if len(_object[key]) == 1:
                     result[key.lower()] = ''.join(_object[key])
                 else:
                     result[key.lower()] = _object[key]
+
             else:
                 if _object[key] == None:
                     continue
+
                 # What the heck?
                 result[key.lower()] = _object[key]
 
+        if result.has_key('objectsid') and not result['objectsid'][0] == "S":
+            result['objectsid'] = sid_to_string(result['objectsid'])
+
         if result.has_key('sn'):
             result['surname'] = result['sn'].replace(' ', '')
 
@@ -385,6 +392,24 @@ def pop_empty_from_list(_input_list):
         if not item == '':
             _output_list.append(item)
 
+def sid_to_string(sid):
+    srl = ord(sid[0])
+    number_sub_id = ord(sid[1])
+    iav = struct.unpack('!Q', '\x00\x00' + sid[2:8])[0]
+
+    sub_ids = []
+
+    for i in range(number_sub_id):
+        sub_ids.append(struct.unpack('<I',sid[8+4*i:12+4*i])[0])
+
+    result = 'S-%d-%d-%s' % (
+            srl,
+            iav,
+            '-'.join([str(s) for s in sub_ids]),
+        )
+
+    return result
+
 def standard_root_dn(domain):
     return 'dc=%s' % (',dc='.join(domain.split('.')))
 


commit 6a7a997f1984dca045113aed4995606ce1b128ec
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 13:59:44 2013 +0200

    Add the new setting [kolab] sync_interval to the default configuration

diff --git a/conf/kolab.conf b/conf/kolab.conf
index 339ec33..f34445c 100644
--- a/conf/kolab.conf
+++ b/conf/kolab.conf
@@ -17,6 +17,11 @@ imap_backend = cyrus-imap
 ; The default locale for this Kolab Groupware installation
 default_locale = en_US
 
+; Synchronization interval - describes the number of seconds to wait in
+; between non-persistent synchronization attempts. Relevant only for
+; deployments that lack persistent search and syncrepl ldap controls.
+sync_interval = 300
+
 [ldap]
 ; The URI to LDAP
 ldap_uri = ldap://localhost:389


commit f4915013da5d4846f76a81933e8a54f5e55a811a
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 13:49:41 2013 +0200

    If the entry_dn is None, we are chasing referrals - which we do not want.

diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 9ca37a9..b168262 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -361,7 +361,6 @@ class LDAP(pykolab.base.Base):
 
         _filter = "%s%s%s" % (__filter_prefix,_filter,__filter_suffix)
 
-
         log.debug(_("Finding recipient with filter %r") % (_filter), level=8)
 
         if len(_filter) <= 6:
@@ -379,7 +378,10 @@ class LDAP(pykolab.base.Base):
 
         for _result in _results:
             (_entry_id, _entry_attrs) = _result
-            _entry_dns.append(_entry_id)
+
+            # Prevent Active Directory referrals
+            if not _entry_id == None:
+                _entry_dns.append(_entry_id)
 
         return _entry_dns
 
@@ -1761,6 +1763,10 @@ class LDAP(pykolab.base.Base):
         # Typical for Paged Results Control
         elif kw.has_key('entry') and isinstance(kw['entry'], list):
             for entry_dn,entry_attrs in kw['entry']:
+                # This is a referral
+                if entry_dn == None:
+                    continue
+
                 entry = { 'dn': entry_dn }
                 entry_attrs = utils.normalize(entry_attrs)
                 for attr in entry_attrs.keys():


commit fb7b5d11817333abffea4d484d58bad722c75396
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 13:48:44 2013 +0200

    Try using one supported control, and abort the entire synchronization if it succeeds. If the configured control does not succeed, try the next supported control

diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 75c11d5..9ca37a9 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -2140,27 +2140,33 @@ class LDAP(pykolab.base.Base):
             _use_ldap_controls = self.ldap.supported_controls
 
         for supported_control in _use_ldap_controls:
-            exec("""_results = self.%s(
-                    %r,
-                    scope=%r,
-                    filterstr=%r,
-                    attrlist=%r,
-                    attrsonly=%r,
-                    timeout=%r,
-                    callback=callback,
-                    primary_domain=%r,
-                    secondary_domains=%r
-                )""" % (
-                        supported_control,
-                        base_dn,
-                        scope,
-                        filterstr,
-                        attrlist,
-                        attrsonly,
-                        timeout,
-                        primary_domain,
-                        secondary_domains
+            try:
+                exec("""_results = self.%s(
+                        %r,
+                        scope=%r,
+                        filterstr=%r,
+                        attrlist=%r,
+                        attrsonly=%r,
+                        timeout=%r,
+                        callback=callback,
+                        primary_domain=%r,
+                        secondary_domains=%r
+                    )""" % (
+                            supported_control,
+                            base_dn,
+                            scope,
+                            filterstr,
+                            attrlist,
+                            attrsonly,
+                            timeout,
+                            primary_domain,
+                            secondary_domains
+                        )
                     )
-                )
+
+                break
+
+            except:
+                continue
 
         return _results


commit 8b001c078ce6aa241c69a2dae517885d9c635ff8
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 13:39:26 2013 +0200

    Add a timestamp format modifier, for the purpose of further integration with Active Directory, that uses a .0Z addition to the modifytimestamp.

diff --git a/pykolab/auth/ldap/cache.py b/pykolab/auth/ldap/cache.py
index 6688bf7..62dd594 100644
--- a/pykolab/auth/ldap/cache.py
+++ b/pykolab/auth/ldap/cache.py
@@ -62,9 +62,14 @@ class Entry(object):
     def __init__(self, uniqueid, result_attr, last_change):
         self.uniqueid = uniqueid
         self.result_attribute = result_attr
+
+        modifytimestamp_format = conf.get('ldap', 'modifytimestamp_format')
+        if modifytimestamp_format == None:
+            modifytimestamp_format = "%Y%m%d%H%M%SZ"
+
         self.last_change = datetime.datetime.strptime(
                 last_change,
-                "%Y%m%d%H%M%SZ"
+                modifytimestamp_format
             )
 
 ##
@@ -125,9 +130,13 @@ def get_entry(domain, entry, update=True):
         db.commit()
         _entry = db.query(Entry).filter_by(uniqueid=entry['id']).first()
     else:
-        if not _entry.last_change.strftime("%Y%m%d%H%M%SZ") == entry['modifytimestamp']:
+        modifytimestamp_format = conf.get('ldap', 'modifytimestamp_format')
+        if modifytimestamp_format == None:
+            modifytimestamp_format = "%Y%m%d%H%M%SZ"
+
+        if not _entry.last_change.strftime(modifytimestamp_format) == entry['modifytimestamp']:
             log.debug(_("Updating timestamp for cache entry %r") % (entry['id']), level=8)
-            last_change = datetime.datetime.strptime(entry['modifytimestamp'], "%Y%m%d%H%M%SZ")
+            last_change = datetime.datetime.strptime(entry['modifytimestamp'], modifytimestamp_format)
             _entry.last_change = last_change
             db.commit()
             _entry = db.query(Entry).filter_by(uniqueid=entry['id']).first()
@@ -163,7 +172,12 @@ def init_db(domain):
 def last_modify_timestamp(domain):
     db = init_db(domain)
     last_change = db.query(Entry).order_by(desc(Entry.last_change)).first()
+
+    modifytimestamp_format = conf.get('ldap', 'modifytimestamp_format')
+    if modifytimestamp_format == None:
+        modifytimestamp_format = "%Y%m%d%H%M%SZ"
+
     if not last_change == None:
-        return last_change.last_change.strftime("%Y%m%d%H%M%SZ")
+        return last_change.last_change.strftime(modifytimestamp_format)
 
-    return datetime.datetime(1900, 01, 01, 00, 00, 00).strftime("%Y%m%d%H%M%SZ")
+    return datetime.datetime(1900, 01, 01, 00, 00, 00).strftime(modifytimestamp_format)


commit f2879dafdd531c6182404b8b38bedf2ffd0d9025
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Mon Apr 29 13:34:17 2013 +0200

    Add a [kolab] section setting 'sync_interval', used as the interval between authn/authz synchronization calls.
    
    Synchronization would otherwise continue to occur with paged searches, or vlv searches, immediately after finishing a run.

diff --git a/kolabd/process.py b/kolabd/process.py
index 30b5065..4b8be0a 100644
--- a/kolabd/process.py
+++ b/kolabd/process.py
@@ -37,11 +37,19 @@ class KolabdProcess(multiprocessing.Process):
             )
 
     def synchronize(self, domain):
+        sync_interval = conf.get('kolab', 'sync_interval')
+
+        if sync_interval == None or sync_interval == 0:
+            sync_interval = 300
+        else:
+            sync_interval = (int)(sync_interval)
+
         while True:
             try:
                 auth = Auth(domain)
                 auth.connect(domain)
                 auth.synchronize()
+                time.sleep(sync_interval)
             except KeyboardInterrupt:
                 break
             except Exception, errmsg:


commit f4f0f44e570b6e95a6c98d308c8e17773f1d811e
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat May 4 13:46:37 2013 +0100

    Fix metadata parsing

diff --git a/cyruslib.py b/cyruslib.py
index fe06b24..27ce418 100644
--- a/cyruslib.py
+++ b/cyruslib.py
@@ -646,43 +646,119 @@ class CYRUS:
             self.__verbose( '[GETANNOTATION %s] No results' % (mailbox) )
             return {}
         ann = {}
-        for annotation in data:
-            self.__verbose( '[GETANNOTATION] RAW %r (length %d)' % (annotation,len(annotation)))
-            annotation = annotation.split('"')
-            self.__verbose( '[GETANNOTATION] SPLIT %r (length %d)' % (annotation,len(annotation)))
-            if not len(annotation) in [ 9, 17, 21, 37 ]:
-                self.__verbose( '[GETANNOTATION] Invalid annotation entry' )
-                continue
-            mbx = self.encode(annotation[1])
-            _key = annotation[3]
-
-            if annotation[5] == "value.shared":
-                key = "/shared%s" % (_key)
-            elif annotation[5] == "value.priv":
-                key = "/private%s" % (_key)
-
-            value = annotation[7]
-
-            self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) )
-            if not ann.has_key(mbx):
-                ann[mbx] = {}
-            if not ann[mbx].has_key(key):
-                ann[mbx][key] = value
-
-            if len(annotation) > 21:
-                # There's another one hidden in here.
-                if annotation[21] == "value.shared":
-                    key = "/shared%s" % (_key)
-                elif annotation[21] == "value.priv":
-                    key = "/private%s" % (_key)
-
-                value = annotation[23]
-
-                self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) )
-                if not ann.has_key(mbx):
-                    ann[mbx] = {}
-                if not ann[mbx].has_key(key):
-                    ann[mbx][key] = value
+
+        annotations = []
+        empty_values = [ "NIL", '" "', None, '', ' ' ]
+
+        concat_items = []
+        for item in data:
+            if isinstance(item, tuple):
+                item = ' '.join([str(x) for x in item])
+
+            if len(concat_items) > 0:
+                concat_items.append(item)
+
+                if ''.join(concat_items).count('(') == ''.join(concat_items).count(')'):
+                    annotations.append(''.join(concat_items))
+                    concat_items = []
+                    continue
+            else:
+
+                if item.count('(') == item.count(')'):
+                    annotations.append(item)
+                    continue
+                else:
+                    concat_items.append(item)
+                    continue
+
+        for annotation in annotations:
+
+            folder = annotation.split()[0].replace('"','')
+
+            if not ann.has_key(folder):
+                ann[folder] = {}
+
+            key = annotation.split()[1].replace('"','').replace("'","")
+
+            _annot = annotation.split('(')[1].split(')')[0]
+
+            try:
+                value_priv = _annot[(_annot.index('"value.priv"')+len('"value.priv"')):_annot.index('"size.priv"')].strip()
+            except ValueError, errmsg:
+                value_priv = None
+
+            try:
+                size_priv = _annot[(_annot.index('"size.priv"')+len('"size.priv"')):].strip().split('"')[1].strip()
+                try:
+                    value_priv = value_priv[value_priv.index('{%s}' % (size_priv))+len('{%s}' % (size_priv)):].strip()
+                except Exception, errmsg:
+                    pass
+            except Exception, errmsg:
+                pass
+
+            if value_priv in empty_values:
+                value_priv = None
+            else:
+                try:
+                    value_priv = value_priv[:value_priv.index('"content-type.priv"')].strip()
+                except:
+                    pass
+
+                try:
+                    value_priv = value_priv[:value_priv.index('"modifiedsince.priv"')].strip()
+                except:
+                    pass
+
+                if value_priv.startswith('"'):
+                    value_priv = value_priv[1:]
+
+                if value_priv.endswith('"'):
+                    value_priv = value_priv[:-1]
+
+            if value_priv in empty_values:
+                value_priv = None
+
+            try:
+                value_shared = _annot[(_annot.index('"value.shared"')+len('"value.shared"')):_annot.index('"size.shared"')].strip()
+            except ValueError, errmsg:
+                value_shared = None
+
+            try:
+                size_shared = _annot[(_annot.index('"size.shared"')+len('"size.shared"')):].strip().split('"')[1].strip()
+                try:
+                    value_shared = value_shared[value_shared.index('{%s}' % (size_shared))+len('{%s}' % (size_shared)):].strip()
+                except Exception, errmsg:
+                    pass
+            except Exception, errmsg:
+                pass
+
+            if value_shared in empty_values:
+                value_shared = None
+            else:
+                try:
+                    value_shared = value_shared[:value_shared.index('"content-type.shared"')].strip()
+                except:
+                    pass
+
+                try:
+                    value_shared = value_shared[:value_shared.index('"modifiedsince.shared"')].strip()
+                except:
+                    pass
+
+                if value_shared.startswith('"'):
+                    value_shared = value_shared[1:]
+
+                if value_shared.endswith('"'):
+                    value_shared = value_shared[:-1]
+
+            if value_shared in empty_values:
+                value_shared = None
+
+            if not value_priv == None:
+                ann[folder]['/private' + key] = value_priv
+
+            if not value_shared == None:
+                ann[folder]['/shared' + key] = value_shared
 
         return ann
 


commit 9899cbee690d29e6ed8b9ffb9b0161625dd1e0e2
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 14:34:58 2013 +0200

    Disconnect from the authn/authz database after closing the client connection

diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index 359a0f6..dae8edc 100644
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -160,7 +160,7 @@ class Auth(pykolab.base.Base):
 
         self._auth.connect()
 
-    def disconnect(self):
+    def disconnect(self, domain=None):
         """
             Connect to the domain authentication backend using domain, or fall
             back to the primary domain specified by the configuration.
diff --git a/saslauthd/__init__.py b/saslauthd/__init__.py
index edbca94..933b81d 100644
--- a/saslauthd/__init__.py
+++ b/saslauthd/__init__.py
@@ -208,6 +208,7 @@ class SASLAuthDaemon(object):
                     pass
 
             clientsocket.close()
+            auth.disconnect()
 
     def reload_config(self, *args, **kw):
         pass


commit 4275f237a86d32880569c8cd65c15565dde6130f
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat May 4 13:36:34 2013 +0100

    Apply diff for #1627, #1631 (non-ascii characters cause wallace tracebacks)

diff --git a/wallace/__init__.py b/wallace/__init__.py
index b22430d..2db963e 100644
--- a/wallace/__init__.py
+++ b/wallace/__init__.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
+# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
@@ -63,6 +63,9 @@ def worker_process(*args, **kw):
 
 class WallaceDaemon(object):
     def __init__(self):
+        self.current_connections = 0
+        self.max_connections = 24
+
         daemon_group = conf.add_cli_parser_option_group(_("Daemon Options"))
 
         daemon_group.add_option(
@@ -215,21 +218,29 @@ class WallaceDaemon(object):
                     if stage.lower() == "defer":
                         continue
 
+                    self.current_connections += 1
                     self.pool.apply_async(pickup_message, (filepath, (self.modules), {'module': module, 'stage': stage}))
+                    self.current_connections -= 1
 
                     continue
 
+                self.current_connections += 1
                 self.pool.apply_async(pickup_message, (filepath, (self.modules)))
+                self.current_connections -= 1
 
         try:
             while 1:
+                while self.current_connections >= self.max_connections:
+                    time.sleep(0.5)
+
                 pair = s.accept()
                 log.info(_("Accepted connection"))
                 if not pair == None:
+                    self.current_connections += 1
                     connection, address = pair
-                    #print "Accepted connection from %r" % (address)
                     channel = SMTPChannel(self, connection, address)
                     asyncore.loop()
+
         except Exception, errmsg:
             traceback.print_exc()
             s.shutdown(1)
@@ -247,7 +258,9 @@ class WallaceDaemon(object):
                         'from': mailfrom,
                         'to': rcpttos,
                         'data': data
-                   }
+                   },
+                ensure_ascii=True,
+                indent=4
             )
 
         (fp, filename) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/")
@@ -256,6 +269,8 @@ class WallaceDaemon(object):
 
         self.pool.apply_async(pickup_message, (filename, (self.modules)))
 
+        self.current_connections -= 1
+
         return
 
     def reload_config(self, *args, **kw):
@@ -368,8 +383,7 @@ class WallaceDaemon(object):
         except AttributeError, e:
             exitcode = 1
             traceback.print_exc()
-            print >> sys.stderr, _("Traceback occurred, please report a " + \
-                "bug at http://bugzilla.kolabsys.com")
+            print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com")
 
         except TypeError, e:
             exitcode = 1
@@ -378,8 +392,7 @@ class WallaceDaemon(object):
         except:
             exitcode = 2
             traceback.print_exc()
-            print >> sys.stderr, _("Traceback occurred, please report a " + \
-                "bug at http://bugzilla.kolabsys.com")
+            print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com")
 
         sys.exit(exitcode)
 
diff --git a/wallace/modules.py b/wallace/modules.py
index a20d80e..d256bdb 100644
--- a/wallace/modules.py
+++ b/wallace/modules.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
+# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
@@ -251,9 +251,21 @@ X-Wallace-Result: REJECT
 
 def cb_action_ACCEPT(module, filepath):
     log.info(_("Accepting message in %s (by module %s)") % (filepath, module))
-    _message = json.load(open(filepath, 'r'))
+    try:
+        _message = json.load(open(filepath, 'r'))
+        log.debug(_(u"Message JSON loaded: %r") % (_message), level=9)
+    except Exception, errmsg:
+        log.error(_("Error loading message: %r") % (errmsg))
+        return
+
+    try:
+        message = message_from_string(str(_message['data']).replace('\x00',''))
+    except Exception, errmsg:
+        log.error(_("Error parsing message: %r") % (errmsg))
+        return
+
+    log.debug(_("Accepting message in: %r") %(filepath), level=8)
 
-    message = message_from_string("%s" %(str(_message['data'])))
     sender = _message['from']
     recipients = _message['to']
 
@@ -266,6 +278,11 @@ def cb_action_ACCEPT(module, filepath):
         smtp.sendmail(
                 sender,
                 recipients,
+                # - Make sure we do not send this as binary.
+                # - Second, strip NUL characters - I don't know where they
+                #   come from (TODO)
+                # - Third, a character return is inserted somewhere. It
+                #   divides the body from the headers - and we don't like (TODO)
                 message.as_string()
             )
 






More information about the commits mailing list