6 commits - cyruslib.py pykolab/auth pykolab/cli pykolab/wap_client saslauthd/__init__.py wallace/__init__.py wallace/modules.py

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Fri May 3 18:00:34 CEST 2013


 cyruslib.py                      |  153 +++++++++++++++++++++++++++++----------
 pykolab/auth/__init__.py         |    2 
 pykolab/cli/cmd_add_domain.py    |   33 +-------
 pykolab/cli/cmd_delete_domain.py |   58 ++++++++++++++
 pykolab/cli/cmd_find_domain.py   |   58 ++++++++++++++
 pykolab/cli/commands.py          |    2 
 pykolab/wap_client/__init__.py   |   52 ++++++-------
 saslauthd/__init__.py            |    1 
 wallace/__init__.py              |   15 +++
 wallace/modules.py               |   19 +++-
 10 files changed, 292 insertions(+), 101 deletions(-)

New commits:
commit b1d4465c6eaf6848d67c1af4bcdfe158511ca244
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 17:59:00 2013 +0200

    Some initial domain operations for the command-line interface

diff --git a/pykolab/cli/cmd_add_domain.py b/pykolab/cli/cmd_add_domain.py
index 16c3da8..92aab7c 100644
--- a/pykolab/cli/cmd_add_domain.py
+++ b/pykolab/cli/cmd_add_domain.py
@@ -35,16 +35,16 @@ def __init__():
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
-            '--alias-for',
-            dest    = "parent_domain",
-            action  = "store",
-            default = None,
-            help    = _("Add domain as alias for DOMAIN"),
+            '--alias',
+            dest    = "domains",
+            action  = "append",
+            default = [],
+            help    = _("Add alias domain."),
             metavar = "DOMAIN",
         )
 
 def description():
-    return _("Add a new domain or domain alias.")
+    return _("Add a new domain.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
@@ -58,31 +58,12 @@ def execute(*args, **kw):
         sys.exit(1)
 
     wap_client.authenticate(username=username)
-    domains = wap_client.domains_list()
 
     dna = conf.get('ldap', 'domain_name_attribute')
 
-    if not conf.parent_domain == None:
-        parent_found = False
-        if isinstance(domains['list'], dict):
-            for _domain in domains['list'].keys():
-                if parent_found:
-                    continue
-
-                if isinstance(domains['list'][_domain][dna], basestring):
-                    if conf.parent_domain == domains['list'][_domain][dna]:
-                        parent_found = True
-                elif isinstance(domains['list'][_domain], list):
-                    if conf.parent_domain in domains['list'][_domain][dna]:
-                        parent_found = True
-
-        if not parent_found:
-            log.error(_("Invalid parent domain"))
-            sys.exit(1)
-
     try:
         domain = conf.cli_args.pop(0)
     except IndexError, errmsg:
         domain = utils.ask_question(_("Domain name"))
 
-    wap_client.domain_add(domain, conf.parent_domain)
+    wap_client.domain_add(domain, conf.domains)
diff --git a/pykolab/cli/cmd_delete_domain.py b/pykolab/cli/cmd_delete_domain.py
new file mode 100644
index 0000000..6856aec
--- /dev/null
+++ b/pykolab/cli/cmd_delete_domain.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 or, at your option, any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+import sys
+
+import commands
+
+import pykolab
+
+from pykolab import utils
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+    commands.register('delete_domain', execute, description=description())
+
+def description():
+    return _("Delete a domain.")
+
+def execute(*args, **kw):
+    from pykolab import wap_client
+
+    # Use uber-administrative privileges
+    username = conf.get('ldap', 'bind_dn')
+    if username == None:
+        log.error(_("Could not find credentials with sufficient permissions" + \
+                "to add a domain name space."))
+
+        sys.exit(1)
+
+    wap_client.authenticate(username=username)
+
+    dna = conf.get('ldap', 'domain_name_attribute')
+
+    try:
+        domain = conf.cli_args.pop(0)
+    except IndexError, errmsg:
+        domain = utils.ask_question(_("Domain name"))
+
+    wap_client.domain_delete(domain)
diff --git a/pykolab/cli/cmd_find_domain.py b/pykolab/cli/cmd_find_domain.py
new file mode 100644
index 0000000..0486fee
--- /dev/null
+++ b/pykolab/cli/cmd_find_domain.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 or, at your option, any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+import sys
+
+import commands
+
+import pykolab
+
+from pykolab import utils
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+    commands.register('find_domain', execute, description=description())
+
+def description():
+    return _("Find a domain.")
+
+def execute(*args, **kw):
+    from pykolab import wap_client
+
+    # Use uber-administrative privileges
+    username = conf.get('ldap', 'bind_dn')
+    if username == None:
+        log.error(_("Could not find credentials with sufficient permissions" + \
+                "to add a domain name space."))
+
+        sys.exit(1)
+
+    wap_client.authenticate(username=username)
+
+    dna = conf.get('ldap', 'domain_name_attribute')
+
+    try:
+        domain = conf.cli_args.pop(0)
+    except IndexError, errmsg:
+        domain = utils.ask_question(_("Domain name"))
+
+    wap_client.domain_find(domain)
diff --git a/pykolab/cli/commands.py b/pykolab/cli/commands.py
index 56520fd..cb57160 100644
--- a/pykolab/cli/commands.py
+++ b/pykolab/cli/commands.py
@@ -62,8 +62,6 @@ def __init__():
     register('add_group', not_yet_implemented, description="Not yet implemented")
     register('delete_group', not_yet_implemented, description="Not yet implemented")
 
-    register('delete_domain', not_yet_implemented, description="Not yet implemented")
-
 def list_commands(*args, **kw):
     """
         List commands


commit 030c157841e2b8690938995f5ce6bd6c48a82ea5
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 17:58:31 2013 +0200

    Adjust the WAP client for domain operations

diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py
index 0d61ac3..5daa61c 100644
--- a/pykolab/wap_client/__init__.py
+++ b/pykolab/wap_client/__init__.py
@@ -76,40 +76,36 @@ def connect():
 
     return conn
 
-def domain_add(domain, parent=None):
-    params = {
-            'domain': domain,
-        }
-
+def domain_add(domain, aliases=[]):
     dna = conf.get('ldap', 'domain_name_attribute')
 
-    if not parent == None:
-        domains = domains_list()
-        parent_found = False
-        if isinstance(domains['list'], dict):
-            for _domain in domains['list'].keys():
-                if parent_found:
-                    continue
-
-                if isinstance(domains['list'][_domain][dna], basestring):
-                    if parent == domains['list'][_domain][dna]:
-                        parent_found = True
-                elif isinstance(domains['list'][_domain][dna], list):
-                    if parent in domains['list'][_domain][dna]:
-                        parent_found = True
-
-        if parent_found:
-            params['parent'] = parent
-        else:
-            log.error(_("Invalid parent domain"))
-            return
-
-    post = json.dumps(params)
+    post = json.dumps({
+            dna: [ domain ] + aliases
+        })
 
     return request('POST', 'domain.add', post=post)
 
+def domain_delete(domain):
+    domain_id, domain_attrs = domain_find(domain).popitem()
+
+    post = json.dumps({
+            'id': domain_id
+        })
+
+    return request('POST', 'domain.delete', post=post)
+
+def domain_find(domain):
+    dna = conf.get('ldap', 'domain_name_attribute')
+
+    get = { dna: domain }
+
+    return request('GET', 'domain.find', get=get)
+
 def domain_info(domain):
-    get = { 'domain': domain }
+    domain_id, domain_attrs = domain_find(domain)
+
+    get = { 'id': domain_id }
+
     return request('GET', 'domain.info', get=get)
 
 def domains_capabilities():


commit 4c89c7a579c9afbd860dacae1087cd1c489da645
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 17:57:41 2013 +0200

    Rewrite getannotation to be compatible with 2.5

diff --git a/cyruslib.py b/cyruslib.py
index 75b636f..460cf82 100644
--- a/cyruslib.py
+++ b/cyruslib.py
@@ -642,49 +642,124 @@ class CYRUS:
         """Get Annotation"""
         self.__prepare('GETANNOTATION')
         res, data = self.__docommand('getannotation', self.decode(mailbox), pattern)
+
         if (len(data) == 1) and data[0] is None:
             self.__verbose( '[GETANNOTATION %s] No results' % (mailbox) )
             return {}
+
         ann = {}
-        for annotation in data:
-            if isinstance(annotation, tuple):
-                annotation = annotation[0]
-            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 3c760fc298ca610e81119b0427b5b5ecb8fd116f
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 14:38:13 2013 +0200

    Replace the blob null bytes we're getting (#1627, #1631)

diff --git a/wallace/modules.py b/wallace/modules.py
index c96c924..d256bdb 100644
--- a/wallace/modules.py
+++ b/wallace/modules.py
@@ -251,8 +251,20 @@ 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'))
-    message = message_from_string(_message['data'])
+    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)
 
     sender = _message['from']
     recipients = _message['to']
@@ -271,8 +283,7 @@ def cb_action_ACCEPT(module, filepath):
                 #   come from (TODO)
                 # - Third, a character return is inserted somewhere. It
                 #   divides the body from the headers - and we don't like (TODO)
-                #unicode(message.as_string()).replace('\0', '').lstrip()
-                message.as_string().encode('utf-8').replace('\0','').lstrip()
+                message.as_string()
             )
 
     except smtplib.SMTPDataError, errmsg:


commit 9d8fd99811f9c70af7baa74a1b06968b2908c0dd
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri May 3 14:37:48 2013 +0200

    Throttle the number of connections

diff --git a/wallace/__init__.py b/wallace/__init__.py
index e018553..2db963e 100644
--- a/wallace/__init__.py
+++ b/wallace/__init__.py
@@ -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)
@@ -258,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):


commit a1fd9987e7f88dd71035396115c8ed132797046e
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 98c44f3..6eaa874 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 dd801c9..f04a232 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





More information about the commits mailing list