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