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