summaryrefslogtreecommitdiffstats
path: root/pykolab
diff options
context:
space:
mode:
authorJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2013-09-18 14:17:47 +0100
committerJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2013-09-18 14:17:47 +0100
commita4bbb85b2e377d0fe1bb76f127dc7d5f58d964a3 (patch)
tree612d13b095504f69080330c82f86c73f3b49d80d /pykolab
parent1dad011fd3005577481b359426cebe5fb41743fa (diff)
parent5f76c88df172452a1947819b1a1ad872543649b6 (diff)
downloadpykolab-a4bbb85b2e377d0fe1bb76f127dc7d5f58d964a3.tar.gz
Merge branch 'master' of ssh://git.kolabsys.com/git/pykolab
Diffstat (limited to 'pykolab')
-rw-r--r--pykolab/Makefile.am6
-rw-r--r--pykolab/__init__.py2
-rw-r--r--pykolab/auth/__init__.py25
-rw-r--r--pykolab/auth/ldap/__init__.py304
-rw-r--r--pykolab/auth/ldap/auth_cache.py138
-rw-r--r--pykolab/auth/ldap/cache.py26
-rw-r--r--pykolab/auth/ldap/syncrepl.py27
-rw-r--r--pykolab/base.py7
-rw-r--r--pykolab/cli/__init__.py2
-rw-r--r--pykolab/cli/cmd_acl_cleanup.py2
-rw-r--r--pykolab/cli/cmd_add_domain.py35
-rw-r--r--pykolab/cli/cmd_add_user.py38
-rw-r--r--pykolab/cli/cmd_add_user_subscription.py2
-rw-r--r--pykolab/cli/cmd_count_domain_mailboxes.py68
-rw-r--r--pykolab/cli/cmd_create_mailbox.py6
-rw-r--r--pykolab/cli/cmd_delete_domain.py58
-rw-r--r--pykolab/cli/cmd_delete_mailbox.py6
-rw-r--r--pykolab/cli/cmd_delete_mailbox_acl.py8
-rw-r--r--pykolab/cli/cmd_delete_message.py2
-rw-r--r--pykolab/cli/cmd_export_mailbox.py2
-rw-r--r--pykolab/cli/cmd_find_domain.py58
-rw-r--r--pykolab/cli/cmd_list_deleted_mailboxes.py44
-rw-r--r--pykolab/cli/cmd_list_domain_mailboxes.py83
-rw-r--r--pykolab/cli/cmd_list_domains.py2
-rw-r--r--pykolab/cli/cmd_list_mailbox_acls.py4
-rw-r--r--pykolab/cli/cmd_list_mailbox_metadata.py4
-rw-r--r--pykolab/cli/cmd_list_mailboxes.py23
-rw-r--r--pykolab/cli/cmd_list_messages.py9
-rw-r--r--pykolab/cli/cmd_list_quota.py29
-rw-r--r--pykolab/cli/cmd_list_user_subscriptions.py19
-rw-r--r--pykolab/cli/cmd_mailbox_cleanup.py2
-rw-r--r--pykolab/cli/cmd_remove_mailaddress.py2
-rw-r--r--pykolab/cli/cmd_remove_user_subscription.py2
-rw-r--r--pykolab/cli/cmd_rename_mailbox.py2
-rw-r--r--pykolab/cli/cmd_server_info.py58
-rw-r--r--pykolab/cli/cmd_set_language.py2
-rw-r--r--pykolab/cli/cmd_set_mail.py2
-rw-r--r--pykolab/cli/cmd_set_mailbox_acl.py4
-rw-r--r--pykolab/cli/cmd_set_mailbox_metadata.py2
-rw-r--r--pykolab/cli/cmd_sync.py2
-rw-r--r--pykolab/cli/cmd_sync_mailhost_attrs.py133
-rw-r--r--pykolab/cli/cmd_transfer_mailbox.py24
-rw-r--r--pykolab/cli/cmd_undelete_mailbox.py4
-rw-r--r--pykolab/cli/cmd_user_info.py48
-rw-r--r--pykolab/cli/commands.py5
-rw-r--r--pykolab/cli/sieve/cmd_list.py2
-rw-r--r--pykolab/cli/sieve/cmd_put.py2
-rw-r--r--pykolab/cli/sieve/cmd_refresh.py2
-rw-r--r--pykolab/cli/sieve/cmd_test.py2
-rw-r--r--pykolab/cli/telemetry/cmd_examine_command_issue.py2
-rw-r--r--pykolab/cli/telemetry/cmd_examine_session.py2
-rw-r--r--pykolab/cli/telemetry/cmd_expire_sessions.py2
-rw-r--r--pykolab/cli/telemetry/cmd_list_sessions.py2
-rw-r--r--pykolab/cli/wap/__init__.py0
-rw-r--r--pykolab/cli/wap/cmd_system_capabilities.py50
-rw-r--r--pykolab/cli/wap/cmd_user_types_list.py40
-rw-r--r--pykolab/conf/__init__.py12
-rw-r--r--pykolab/conf/defaults.py2
-rw-r--r--pykolab/conf/entitlement.py2
-rw-r--r--pykolab/errors.py2
-rw-r--r--pykolab/imap/__init__.py77
-rw-r--r--pykolab/imap/cyrus.py12
-rw-r--r--pykolab/imap_utf7.py91
-rw-r--r--pykolab/logger.py8
-rw-r--r--pykolab/plugins/__init__.py2
-rw-r--r--pykolab/plugins/defaultfolders/__init__.py2
-rw-r--r--pykolab/plugins/dynamicquota/__init__.py2
-rw-r--r--pykolab/plugins/recipientpolicy/__init__.py26
-rw-r--r--pykolab/plugins/sievemgmt/__init__.py2
-rw-r--r--pykolab/setup/__init__.py2
-rw-r--r--pykolab/setup/components.py2
-rw-r--r--pykolab/setup/setup_freebusy.py101
-rw-r--r--pykolab/setup/setup_imap.py2
-rw-r--r--pykolab/setup/setup_kolabd.py2
-rw-r--r--pykolab/setup/setup_ldap.py109
-rw-r--r--pykolab/setup/setup_mta.py67
-rw-r--r--pykolab/setup/setup_mysql.py2
-rw-r--r--pykolab/setup/setup_php.py2
-rw-r--r--pykolab/setup/setup_roundcube.py6
-rw-r--r--pykolab/setup/setup_syncroton.py2
-rw-r--r--pykolab/setup/setup_zpush.py109
-rw-r--r--pykolab/telemetry.py2
-rw-r--r--pykolab/translate.py2
-rw-r--r--pykolab/translit.py2
-rw-r--r--pykolab/utils.py69
-rw-r--r--pykolab/wap_client/__init__.py184
-rw-r--r--pykolab/xml/event.py77
87 files changed, 1910 insertions, 509 deletions
diff --git a/pykolab/Makefile.am b/pykolab/Makefile.am
index a3488c1..a23aa6e 100644
--- a/pykolab/Makefile.am
+++ b/pykolab/Makefile.am
@@ -8,6 +8,7 @@ pykolab_auth_PYTHON = \
pykolab_auth_ldapdir = $(pythondir)/$(PACKAGE)/auth/ldap
pykolab_auth_ldap_PYTHON = \
auth/ldap/__init__.py \
+ auth/ldap/auth_cache.py \
auth/ldap/cache.py \
auth/ldap/syncrepl.py
@@ -23,6 +24,10 @@ pykolab_clitelemetrydir = $(pythondir)/$(PACKAGE)/cli/telemetry
pykolab_clitelemetry_PYTHON = \
$(wildcard cli/telemetry/*.py)
+pykolab_cliwapdir = $(pythondir)/$(PACKAGE)/cli/wap
+pykolab_cliwap_PYTHON = \
+ $(wildcard cli/wap/*.py)
+
pykolab_confdir = $(pythondir)/$(PACKAGE)/conf
pykolab_conf_PYTHON = \
conf/defaults.py \
@@ -69,7 +74,6 @@ pykolab_setup_PYTHON = \
setup/setup_php.py \
setup/setup_roundcube.py \
setup/setup_syncroton.py \
- setup/setup_zpush.py \
setup/__init__.py
pykolab_wapclientdir = $(pythondir)/$(PACKAGE)/wap_client
diff --git a/pykolab/__init__.py b/pykolab/__init__.py
index 3f0b520..0666cd3 100644
--- a/pykolab/__init__.py
+++ b/pykolab/__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>
#
diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index 6b07549..fd02083 100644
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -1,4 +1,4 @@
-# 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>
#
@@ -37,15 +37,10 @@ class Auth(pykolab.base.Base):
"""
Initialize the authentication class.
"""
- pykolab.base.Base.__init__(self)
+ pykolab.base.Base.__init__(self, domain=domain)
self._auth = None
- if not domain == None:
- self.domain = domain
- else:
- self.domain = conf.get('kolab', 'primary_domain')
-
def authenticate(self, login):
"""
Verify login credentials supplied in login against the appropriate
@@ -97,8 +92,12 @@ class Auth(pykolab.base.Base):
return
if domain == None:
- section = 'kolab'
- domain = conf.get('kolab', 'primary_domain')
+ if not self.domain == None:
+ section = self.domain
+ domain = self.domain
+ else:
+ section = 'kolab'
+ domain = conf.get('kolab', 'primary_domain')
else:
self.list_domains()
section = domain
@@ -160,7 +159,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.
@@ -228,6 +227,8 @@ class Auth(pykolab.base.Base):
except:
if not self.domain == kolab_primary_domain:
return [(self.domain, [])]
+ else:
+ domains = []
# If no domains are found, the primary domain is used.
if len(domains) < 1:
@@ -263,8 +264,8 @@ class Auth(pykolab.base.Base):
def search_mail_address(self, domain, mail_address):
return self._auth._search_mail_address(domain, mail_address)
- def set_entry_attribute(self, domain, entry, attribute):
- return self._auth.set_entry_attribute(entry, attribute)
+ def set_entry_attribute(self, domain, entry, attribute, value):
+ return self._auth.set_entry_attribute(entry, attribute, value)
def set_entry_attributes(self, domain, entry, attributes):
return self._auth.set_entry_attributes(entry, attributes)
diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index e3ca936..cdb9ade 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -1,4 +1,4 @@
-# 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>
#
@@ -35,6 +35,7 @@ from pykolab.translate import _
log = pykolab.getLogger('pykolab.auth')
conf = pykolab.getConf()
+import auth_cache
import cache
# Catch python-ldap-2.4 changes
@@ -110,7 +111,7 @@ class LDAP(pykolab.base.Base):
Initialize the LDAP object for domain. If no domain is specified,
domain name space configured as 'kolab'.'primary_domain' is used.
"""
- pykolab.base.Base.__init__(self)
+ pykolab.base.Base.__init__(self, domain=domain)
self.ldap = None
self.bind = False
@@ -151,7 +152,26 @@ class LDAP(pykolab.base.Base):
self.connect()
self._bind()
- user_filter = self.config_get('user_filter')
+ # See if we know a base_dn for the domain
+ base_dn = None
+
+ try:
+ base_dn = auth_cache.get_entry(self.domain)
+ except:
+ pass
+
+ if base_dn == None:
+ config_base_dn = self.config_get('base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ base_dn = ldap_base_dn
+ else:
+ base_dn = config_base_dn
+
+ auth_cache.set_entry(self.domain, base_dn)
+
+ user_filter = self.config_get_raw('user_filter') % ({'base_dn':base_dn})
_filter = '(&(|'
@@ -163,41 +183,69 @@ class LDAP(pykolab.base.Base):
_filter += ')%s)' % (user_filter)
- _search = self.ldap.search_ext(
- self.config_get('base_dn'),
- ldap.SCOPE_SUBTREE,
- _filter,
- ['entrydn']
- )
+ entry_dn = None
- (
- _result_type,
- _result_data,
- _result_msgid,
- _result_controls
- ) = self.ldap.result3(_search)
+ try:
+ entry_dn = auth_cache.get_entry(_filter)
+ except:
+ pass
- if len(_result_data) >= 1:
- (entry_dn, entry_attrs) = _result_data[0]
+ if entry_dn == None:
+ _search = self.ldap.search_ext(
+ base_dn,
+ ldap.SCOPE_SUBTREE,
+ _filter,
+ ['entrydn']
+ )
- try:
- log.debug(_("Binding with user_dn %s and password %s")
- % (entry_dn, login[1]))
+ (
+ _result_type,
+ _result_data,
+ _result_msgid,
+ _result_controls
+ ) = self.ldap.result3(_search)
+
+ if len(_result_data) >= 1:
+ (entry_dn, entry_attrs) = _result_data[0]
- # Needs to be synchronous or succeeds and continues setting retval
- # to True!!
- self.ldap.simple_bind_s(entry_dn, login[1])
- retval = True
- except:
try:
- log.debug(
- _("Failed to authenticate as user %s") % (login[0]),
- level=8
- )
+ log.debug(_("Binding with user_dn %s and password %s")
+ % (entry_dn, login[1]))
+
+ # Needs to be synchronous or succeeds and continues setting retval
+ # to True!!
+ self.ldap.simple_bind_s(entry_dn, login[1])
+ retval = True
+ auth_cache.set_entry(_filter, entry_dn)
except:
- pass
+ try:
+ log.debug(
+ _("Failed to authenticate as user %s") % (login[0]),
+ level=8
+ )
+ except:
+ pass
+
+ retval = False
+ else:
+ try:
+ log.debug(_("Binding with user_dn %s and password %s")
+ % (entry_dn, login[1]))
+
+ # Needs to be synchronous or succeeds and continues setting retval
+ # to True!!
+ self.ldap.simple_bind_s(entry_dn, login[1])
+ retval = True
+ except:
+ try:
+ log.debug(
+ _("Failed to authenticate as user %s") % (login[0]),
+ level=8
+ )
+ except:
+ pass
- retval = False
+ retval = False
return retval
@@ -361,14 +409,21 @@ 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:
return None
+ config_base_dn = self.config_get('base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ base_dn = ldap_base_dn
+ else:
+ base_dn = config_base_dn
+
_results = self.ldap.search_s(
- self.config_get('base_dn'),
+ base_dn,
scope=ldap.SCOPE_SUBTREE,
filterstr=_filter,
attrlist=result_attributes,
@@ -379,7 +434,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
@@ -409,8 +467,6 @@ class LDAP(pykolab.base.Base):
__filter_prefix = "(&%s" % resource_filter
__filter_suffix = ")"
- resource_base_dn = self.config_get('resource_base_dn')
-
recipient_address_attrs = self.config_get_list("mail_attributes")
result_attributes = recipient_address_attrs
@@ -434,6 +490,14 @@ class LDAP(pykolab.base.Base):
if len(_filter) <= 6:
return None
+ config_base_dn = self.config_get('resource_base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ resource_base_dn = ldap_base_dn
+ else:
+ resource_base_dn = config_base_dn
+
_results = self.ldap.search_s(
resource_base_dn,
scope=ldap.SCOPE_SUBTREE,
@@ -564,6 +628,9 @@ class LDAP(pykolab.base.Base):
}
)
+ if primary_mail_address == None:
+ return entry_modifications
+
i = 1
_primary_mail = primary_mail_address
@@ -796,8 +863,16 @@ class LDAP(pykolab.base.Base):
_filter = "(%s=%s)" % (attr, value)
+ config_base_dn = self.config_get('base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ base_dn = ldap_base_dn
+ else:
+ base_dn = config_base_dn
+
return self._search(
- self.config_get('base_dn'),
+ base_dn,
filterstr=_filter,
attrlist=[
'*',
@@ -863,18 +938,34 @@ class LDAP(pykolab.base.Base):
else:
override_search = False
- self._search(
- self.config_get('base_dn'),
- filterstr=_filter,
- attrlist=[
- '*',
- self.config_get('unique_attribute'),
- conf.get('cyrus-sasl', 'result_attribute'),
- 'modifytimestamp'
- ],
- override_search=override_search,
- callback=self._synchronize_callback,
- )
+ config_base_dn = self.config_get('base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ base_dn = ldap_base_dn
+ else:
+ base_dn = config_base_dn
+
+ log.debug(_("Synchronization is searching against base DN: %s") % (base_dn), level=8)
+
+ try:
+ self._search(
+ base_dn,
+ filterstr=_filter,
+ attrlist=[
+ '*',
+ self.config_get('unique_attribute'),
+ conf.get('cyrus-sasl', 'result_attribute'),
+ 'modifytimestamp'
+ ],
+ override_search=override_search,
+ callback=self._synchronize_callback,
+ )
+ except Exception, errmsg:
+ log.error("Exception occurred: %r" % (errmsg))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
def user_quota(self, entry_id, folder):
default_quota = self.config_get('default_quota')
@@ -883,8 +974,9 @@ class LDAP(pykolab.base.Base):
if quota_attribute == None:
return
+ # The default quota may be None, but LDAP quota could still be set
if default_quota == None:
- return
+ default_quota = 0
self._bind()
@@ -919,6 +1011,11 @@ class LDAP(pykolab.base.Base):
}
)
+ try:
+ current_ldap_quota = (int)(current_ldap_quota)
+ except:
+ current_ldap_quota = None
+
if not current_ldap_quota == None:
if not new_quota == (int)(current_ldap_quota):
self.set_entry_attribute(
@@ -1180,6 +1277,9 @@ class LDAP(pykolab.base.Base):
if entry[result_attribute] == None:
return
+ if entry[result_attribute] == '':
+ return
+
cache.get_entry(self.domain, entry)
self.imap.connect(domain=self.domain)
@@ -1311,6 +1411,15 @@ class LDAP(pykolab.base.Base):
for key in entry_changes.keys():
entry[key] = entry_changes[key]
+ if not entry.has_key(result_attribute):
+ return
+
+ if entry[result_attribute] == None:
+ return
+
+ if entry[result_attribute] == '':
+ return
+
# Now look at entry_changes and old_canon_attr, and see if they're
# the same value.
if entry_changes.has_key(result_attribute):
@@ -1475,12 +1584,17 @@ class LDAP(pykolab.base.Base):
Expects the new entry.
"""
+ # Initialize old_canon_attr (#1701)
+ old_canon_attr = None
+
result_attribute = conf.get('cyrus-sasl','result_attribute')
_entry = cache.get_entry(self.domain, entry, update=False)
- if _entry.__dict__.has_key('result_attribute') and not _entry.result_attribute == '':
- old_canon_attr = _entry.result_attribute
+ # We do not necessarily have a synchronisation cache entry (#1701)
+ if not _entry == None:
+ if _entry.__dict__.has_key('result_attribute') and not _entry.result_attribute == '':
+ old_canon_attr = _entry.result_attribute
entry_changes = self.recipient_policy(entry)
@@ -1550,7 +1664,7 @@ class LDAP(pykolab.base.Base):
mailserver_attribute = self.config_get('mailserver_attribute')
if entry.has_key(mailserver_attribute):
- server = entry['mailserver_attribute']
+ server = entry[mailserver_attribute]
if not entry.has_key('kolabtargetfolder'):
entry['kolabtargetfolder'] = self.get_entry_attribute(
@@ -1725,7 +1839,13 @@ class LDAP(pykolab.base.Base):
entry_dn = self.entry_dn(entry_id)
- base_dn = self.config_get('base_dn')
+ config_base_dn = self.config_get('base_dn')
+ ldap_base_dn = self._kolab_domain_root_dn(self.domain)
+
+ if not ldap_base_dn == None and not ldap_base_dn == config_base_dn:
+ base_dn = ldap_base_dn
+ else:
+ base_dn = config_base_dn
for _type in ['user', 'group', 'sharedfolder']:
__filter = self.config_get('kolab_%s_filter' % (_type))
@@ -1844,6 +1964,9 @@ class LDAP(pykolab.base.Base):
_domain_attrs = utils.normalize(_domain_attrs)
if _domain_attrs.has_key(domain_rootdn_attribute):
return _domain_attrs[domain_rootdn_attribute]
+ else:
+ if isinstance(_domain_attrs[domain_name_attribute], list):
+ domain = _domain_attrs[domain_name_attribute][0]
else:
if conf.has_option('ldap', 'base_dn'):
@@ -1999,6 +2122,15 @@ class LDAP(pykolab.base.Base):
else:
change = change_dict['change_type']
+ # See if we can find the cache entry - this way we can get to
+ # the value of a (former, on a deleted entry) result_attribute
+ result_attribute = conf.get('cyrus-sasl', 'result_attribute')
+ if not entry.has_key(result_attribute):
+ cache_entry = cache.get_entry(self.domain, entry, update=False)
+
+ if hasattr(cache_entry, 'result_attribute') and change == 'delete':
+ entry[result_attribute] = cache_entry.result_attribute
+
eval(
"self._change_%s_%s(entry, change_dict)" % (
change,
@@ -2009,6 +2141,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():
@@ -2046,6 +2182,14 @@ class LDAP(pykolab.base.Base):
#
# server = self.imap.user_mailbox_server(folder)
+ log.debug(
+ _("Done with _synchronize_callback() for entry %r") % (
+ entry['id']
+ ),
+ level=9
+ )
+
+
def _unbind(self):
"""
Discard the current set of bind credentials.
@@ -2389,27 +2533,37 @@ 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 Exception, errmsg:
+ log.error(_("An error occured using %s: %r") % (supported_control, errmsg))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
+ continue
return _results
diff --git a/pykolab/auth/ldap/auth_cache.py b/pykolab/auth/ldap/auth_cache.py
new file mode 100644
index 0000000..12f362c
--- /dev/null
+++ b/pykolab/auth/ldap/auth_cache.py
@@ -0,0 +1,138 @@
+# 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 datetime
+
+import sqlalchemy
+
+from sqlalchemy import Column
+from sqlalchemy import DateTime
+from sqlalchemy import Integer
+from sqlalchemy import MetaData
+from sqlalchemy import String
+from sqlalchemy import Table
+
+from sqlalchemy import desc
+from sqlalchemy import create_engine
+from sqlalchemy.orm import mapper
+
+try:
+ from sqlalchemy.orm import relationship
+except:
+ from sqlalchemy.orm import relation as relationship
+
+try:
+ from sqlalchemy.orm import sessionmaker
+except:
+ from sqlalchemy.orm import create_session
+
+import pykolab
+
+from pykolab import utils
+from pykolab.constants import KOLAB_LIB_PATH
+from pykolab.translate import _
+
+conf = pykolab.getConf()
+log = pykolab.getLogger('pykolab.auth_cache')
+
+metadata = MetaData()
+
+db = None
+
+##
+## Classes
+##
+
+class Entry(object):
+ def __init__(self, key, value):
+ self.key = key
+ self.value = value
+
+##
+## Tables
+##
+
+entry_table = Table(
+ 'entries', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('domain', String(128), index=True, nullable=True),
+ Column('key', String(128), index=True, nullable=False),
+ Column('value', String(128), nullable=False),
+ Column('last_change', DateTime, nullable=False, default=datetime.datetime.now())
+ )
+
+##
+## Table <-> Class Mappers
+##
+
+mapper(Entry, entry_table)
+
+##
+## Functions
+##
+
+def get_entry(key):
+ db = init_db()
+ _entries = db.query(Entry).filter_by(key=key).all()
+
+ if len(_entries) == 0:
+ return None
+ if len(_entries) > 1:
+ return None
+
+ log.debug("Entry found: %r" % (_entries[0].__dict__))
+ log.debug("Returning: %r" % (_entries[0].value))
+
+ return _entries[0].value
+
+def set_entry(key, value):
+ db = init_db()
+ _entries = db.query(Entry).filter_by(key=key).all()
+
+ if len(_entries) == 0:
+ db.add(
+ Entry(
+ key,
+ value
+ )
+ )
+
+ db.commit()
+
+#def purge_entries():
+ #db = init_db()
+ #db.query(Entry).filter(Entry.last_change <= datetime.datetime.now()).delete()
+
+def init_db():
+ """
+ Returns a SQLAlchemy Session() instance.
+ """
+ global db
+
+ if not db == None:
+ return db
+
+ db_uri = 'sqlite:///%s/auth_cache.db' % (KOLAB_LIB_PATH)
+ echo = conf.debuglevel > 8
+ engine = create_engine(db_uri, echo=echo)
+ metadata.create_all(engine)
+
+ Session = sessionmaker(bind=engine)
+ db = Session()
+
+ return db
diff --git a/pykolab/auth/ldap/cache.py b/pykolab/auth/ldap/cache.py
index 6688bf7..55a47c3 100644
--- a/pykolab/auth/ldap/cache.py
+++ b/pykolab/auth/ldap/cache.py
@@ -1,4 +1,4 @@
-# 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>
#
@@ -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)
diff --git a/pykolab/auth/ldap/syncrepl.py b/pykolab/auth/ldap/syncrepl.py
index e02e086..03ab5ae 100644
--- a/pykolab/auth/ldap/syncrepl.py
+++ b/pykolab/auth/ldap/syncrepl.py
@@ -5,8 +5,13 @@ import ldap
import ldap.syncrepl
import ldapurl
+import pykolab
+
from pykolab import utils
+log = pykolab.getLogger('pykolab.syncrepl')
+conf = pykolab.getConf()
+
class DNSync(ldap.ldapobject.LDAPObject,ldap.syncrepl.SyncreplConsumer):
callback = None
@@ -28,16 +33,36 @@ class DNSync(ldap.ldapobject.LDAPObject,ldap.syncrepl.SyncreplConsumer):
return self.__db['cookie']
def syncrepl_delete(self, uuids):
+ log.debug("syncrepl_delete uuids: %r" % (uuids), level=8)
+
+ # Get the unique_attribute name to issue along with our
+ # callback (if any)
+ unique_attr = conf.get('ldap', 'unique_attribute')
+ if unique_attr == None:
+ unique_attr = 'entryuuid'
+
+ if unique_attr == 'nsuniqueid':
+ log.warning(
+ _("The name of the persistent, unique attribute " + \
+ "is very probably not compatible with the use of " + \
+ "syncrepl.")
+ )
+
+
for uuid in uuids:
dn = self.__db[uuid]
+ log.debug("syncrepl_delete dn: %r" % (dn), level=8)
+
if not self.callback == None:
self.callback(
change_type='delete',
previous_dn=None,
change_number=None,
dn=dn,
- entry={}
+ entry={
+ unique_attr: uuid
+ }
)
del self.__db[uuid]
diff --git a/pykolab/base.py b/pykolab/base.py
index ebc1a59..207783c 100644
--- a/pykolab/base.py
+++ b/pykolab/base.py
@@ -1,4 +1,4 @@
-# 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>
#
@@ -26,7 +26,10 @@ class Base(object):
Abstraction class for functions commonly shared between auth, imap, etc.
"""
def __init__(self, *args, **kw):
- self.domain = conf.get('kolab', 'primary_domain')
+ if kw.has_key('domain') and not kw['domain'] == None:
+ self.domain = kw['domain']
+ else:
+ self.domain = conf.get('kolab', 'primary_domain')
# Placeholder primary_domain => [secondary_domains]. Should be updated
# on auth backend _connect().
diff --git a/pykolab/cli/__init__.py b/pykolab/cli/__init__.py
index aa054c0..a2cf250 100644
--- a/pykolab/cli/__init__.py
+++ b/pykolab/cli/__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>
#
diff --git a/pykolab/cli/cmd_acl_cleanup.py b/pykolab/cli/cmd_acl_cleanup.py
index 80d5396..3880d68 100644
--- a/pykolab/cli/cmd_acl_cleanup.py
+++ b/pykolab/cli/cmd_acl_cleanup.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>
#
diff --git a/pykolab/cli/cmd_add_domain.py b/pykolab/cli/cmd_add_domain.py
index f3d5d97..92aab7c 100644
--- a/pykolab/cli/cmd_add_domain.py
+++ b/pykolab/cli/cmd_add_domain.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>
#
@@ -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_add_user.py b/pykolab/cli/cmd_add_user.py
new file mode 100644
index 0000000..873e1d8
--- /dev/null
+++ b/pykolab/cli/cmd_add_user.py
@@ -0,0 +1,38 @@
+# -*- 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 commands
+
+import pykolab
+
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('add_user', execute, description="Add a user.")
+
+def execute(*args, **kw):
+ from pykolab import wap_client
+ # Create the authentication object.
+ # TODO: Binds with superuser credentials!
+ wap_client.authenticate()
+ wap_client.user_add()
+
diff --git a/pykolab/cli/cmd_add_user_subscription.py b/pykolab/cli/cmd_add_user_subscription.py
index 4b3393a..666ef7e 100644
--- a/pykolab/cli/cmd_add_user_subscription.py
+++ b/pykolab/cli/cmd_add_user_subscription.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>
#
diff --git a/pykolab/cli/cmd_count_domain_mailboxes.py b/pykolab/cli/cmd_count_domain_mailboxes.py
new file mode 100644
index 0000000..6cc71ef
--- /dev/null
+++ b/pykolab/cli/cmd_count_domain_mailboxes.py
@@ -0,0 +1,68 @@
+# -*- 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 datetime
+
+import commands
+
+import pykolab
+
+from pykolab import imap_utf7
+from pykolab.auth import Auth
+from pykolab.imap import IMAP
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('count_domain_mailboxes', execute)
+
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
+def execute(*args, **kw):
+ """
+ List deleted mailboxes
+ """
+ imap = IMAP()
+ imap.connect()
+
+ auth = Auth()
+ auth.connect()
+
+ domains = auth.list_domains()
+
+ folders = []
+ for primary,secondaries in domains:
+ print "%s: %d" % (primary,len(imap.lm("user/%%@%s" % (primary))))
+ for secondary in secondaries:
+ print "%s: %d" % (secondary,len(imap.lm("user/%%@%s" % (secondary))))
+
+ null_realm = len(imap.lm("user/%%"))
+
+ if null_realm > 0:
+ print "null: %d" % (null_realm)
+
diff --git a/pykolab/cli/cmd_create_mailbox.py b/pykolab/cli/cmd_create_mailbox.py
index 5510e56..63ae0ad 100644
--- a/pykolab/cli/cmd_create_mailbox.py
+++ b/pykolab/cli/cmd_create_mailbox.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,9 +63,7 @@ def execute(*args, **kw):
imap = IMAP()
imap.connect()
- admin_login = conf.get('cyrus-imap', 'admin_login')
-
- imap.cm(mailbox)
+ imap.create_folder(mailbox)
if not conf.metadata == None:
imap.set_metadata(mailbox, conf.metadata.split('=')[0], conf.metadata.split('=')[1])
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_delete_mailbox.py b/pykolab/cli/cmd_delete_mailbox.py
index 0978b7e..61bc278 100644
--- a/pykolab/cli/cmd_delete_mailbox.py
+++ b/pykolab/cli/cmd_delete_mailbox.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>
#
@@ -49,7 +49,9 @@ def execute(*args, **kw):
imap = IMAP()
imap.connect()
- delete_folders = imap.lm(delete_folder)
+
+ delete_folders = imap.list_folders(delete_folder)
+
for delete_folder in delete_folders:
imap.delete_mailfolder(delete_folder)
diff --git a/pykolab/cli/cmd_delete_mailbox_acl.py b/pykolab/cli/cmd_delete_mailbox_acl.py
index 1bf2ad6..727b00d 100644
--- a/pykolab/cli/cmd_delete_mailbox_acl.py
+++ b/pykolab/cli/cmd_delete_mailbox_acl.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>
#
@@ -62,4 +62,8 @@ def execute(*args, **kw):
else:
folders = imap.lm(folder)
for folder in folders:
- imap.set_acl(folder, identifier, '') \ No newline at end of file
+ try:
+ imap.set_acl(folder, identifier, '')
+ except:
+ # Mailbox no longer exists?
+ pass \ No newline at end of file
diff --git a/pykolab/cli/cmd_delete_message.py b/pykolab/cli/cmd_delete_message.py
index 105cfd9..f90fdc0 100644
--- a/pykolab/cli/cmd_delete_message.py
+++ b/pykolab/cli/cmd_delete_message.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>
#
diff --git a/pykolab/cli/cmd_export_mailbox.py b/pykolab/cli/cmd_export_mailbox.py
index 72ea139..fde3fac 100644
--- a/pykolab/cli/cmd_export_mailbox.py
+++ b/pykolab/cli/cmd_export_mailbox.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>
#
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/cmd_list_deleted_mailboxes.py b/pykolab/cli/cmd_list_deleted_mailboxes.py
index 90bcddd..305a129 100644
--- a/pykolab/cli/cmd_list_deleted_mailboxes.py
+++ b/pykolab/cli/cmd_list_deleted_mailboxes.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>
#
@@ -17,10 +17,14 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
+import datetime
+
import commands
import pykolab
+from pykolab import imap_utf7
+from pykolab.auth import Auth
from pykolab.imap import IMAP
from pykolab.translate import _
@@ -30,14 +34,48 @@ conf = pykolab.getConf()
def __init__():
commands.register('list_deleted_mailboxes', execute)
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--raw',
+ dest = "raw",
+ action = "store_true",
+ default = False,
+ help = _("Display raw IMAP UTF-7 folder names"))
+
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
def execute(*args, **kw):
"""
List deleted mailboxes
"""
imap = IMAP()
imap.connect()
- folders = imap.lm("DELETED/*")
+
+ auth = Auth()
+ auth.connect()
+
+ domains = auth.list_domains()
+
+ folders = []
+ for primary,secondaries in domains:
+ folders.extend(imap.lm("DELETED/*@%s" % (primary)))
+ for secondary in secondaries:
+ folders.extend(imap.lm("DELETED/*@%s" % (secondary)))
+
+ folders.extend(imap.lm("DELETED/*"))
+
print "Deleted folders:"
+
for folder in folders:
- print folder
+ mbox_parts = imap.parse_mailfolder(folder)
+
+ if not conf.raw:
+ print "%s (Deleted at %s)" % (imap_utf7.decode(folder), datetime.datetime.fromtimestamp(int(mbox_parts['hex_timestamp'], 16)))
+ else:
+ print "%s (Deleted at %s)" % (folder, datetime.datetime.fromtimestamp(int(mbox_parts['hex_timestamp'], 16)))
diff --git a/pykolab/cli/cmd_list_domain_mailboxes.py b/pykolab/cli/cmd_list_domain_mailboxes.py
new file mode 100644
index 0000000..684f1c8
--- /dev/null
+++ b/pykolab/cli/cmd_list_domain_mailboxes.py
@@ -0,0 +1,83 @@
+# -*- 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 commands
+
+import pykolab
+
+from pykolab import imap_utf7
+from pykolab.auth import Auth
+from pykolab.imap import IMAP
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('list_domain_mailboxes', execute)
+
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--raw',
+ dest = "raw",
+ action = "store_true",
+ default = False,
+ help = _("Display raw IMAP UTF-7 folder names"))
+
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
+def execute(*args, **kw):
+ """
+ List deleted mailboxes
+ """
+
+ try:
+ domain = conf.cli_args.pop(0)
+ except:
+ domain = utils.ask_question(_("Domain"))
+
+ imap = IMAP()
+ imap.connect()
+
+ auth = Auth()
+ auth.connect()
+
+ domains = auth.list_domains()
+
+ folders = []
+ for primary,secondaries in domains:
+ if not domain == primary and not domain in secondaries:
+ continue
+
+ folders.extend(imap.lm("user/%%@%s" % (primary)))
+ for secondary in secondaries:
+ folders.extend(imap.lm("user/%%@%s" % (secondary)))
+
+ print "Deleted folders:"
+
+ for folder in folders:
+ if not conf.raw:
+ print imap_utf7.decode(folder)
+ else:
+ print folder
diff --git a/pykolab/cli/cmd_list_domains.py b/pykolab/cli/cmd_list_domains.py
index fd2c7da..c1b58d7 100644
--- a/pykolab/cli/cmd_list_domains.py
+++ b/pykolab/cli/cmd_list_domains.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>
#
diff --git a/pykolab/cli/cmd_list_mailbox_acls.py b/pykolab/cli/cmd_list_mailbox_acls.py
index 62bac4f..f69c09d 100644
--- a/pykolab/cli/cmd_list_mailbox_acls.py
+++ b/pykolab/cli/cmd_list_mailbox_acls.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>
#
@@ -55,7 +55,7 @@ def execute(*args, **kw):
else:
acls = []
- folders = imap.lm(folder)
+ folders = imap.list_folders(folder)
for folder in folders:
print "Folder", folder
acls = imap.list_acls(folder)
diff --git a/pykolab/cli/cmd_list_mailbox_metadata.py b/pykolab/cli/cmd_list_mailbox_metadata.py
index b430896..a07420d 100644
--- a/pykolab/cli/cmd_list_mailbox_metadata.py
+++ b/pykolab/cli/cmd_list_mailbox_metadata.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>
#
@@ -81,7 +81,7 @@ def execute(*args, **kw):
else:
metadata = []
- folders = imap.lm(folder)
+ folders = imap.list_folders(folder)
for folder in folders:
print "Folder", folder
diff --git a/pykolab/cli/cmd_list_mailboxes.py b/pykolab/cli/cmd_list_mailboxes.py
index 3451e7c..f9391eb 100644
--- a/pykolab/cli/cmd_list_mailboxes.py
+++ b/pykolab/cli/cmd_list_mailboxes.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>
#
@@ -21,6 +21,7 @@ import commands
import pykolab
+from pykolab import imap_utf7
from pykolab.imap import IMAP
from pykolab.translate import _
@@ -43,6 +44,13 @@ def cli_options():
default = False,
help = _("Display raw IMAP UTF-7 folder names"))
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
def execute(*args, **kw):
"""
List mailboxes
@@ -67,13 +75,20 @@ def execute(*args, **kw):
searches = [ '' ]
imap = IMAP()
- imap.connect()
+
+ if not conf.connect_server == None:
+ imap.connect(server=conf.connect_server)
+ else:
+ imap.connect()
folders = []
for search in searches:
log.debug(_("Appending folder search for %r") % (search), level=8)
- folders.extend(imap.lm(search))
+ folders.extend(imap.lm(imap_utf7.encode(search)))
for folder in folders:
- print folder
+ if not conf.raw:
+ print imap_utf7.decode(folder)
+ else:
+ print folder
diff --git a/pykolab/cli/cmd_list_messages.py b/pykolab/cli/cmd_list_messages.py
index 586e2ae..716cdd3 100644
--- a/pykolab/cli/cmd_list_messages.py
+++ b/pykolab/cli/cmd_list_messages.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>
#
@@ -23,6 +23,7 @@ import commands
import pykolab
+from pykolab import imap_utf7
from pykolab.imap import IMAP
from pykolab.translate import _
@@ -60,7 +61,7 @@ def execute(*args, **kw):
imap = IMAP()
imap.connect()
- _folder = imap.lm(folder)
+ _folder = imap.lm(imap_utf7.encode(folder))
if _folder == None or _folder == []:
log.error(_("No such folder"))
@@ -68,7 +69,7 @@ def execute(*args, **kw):
imap.set_acl(folder, 'cyrus-admin', 'lrs')
- imap.select(folder)
+ imap.select(imap_utf7.encode(folder))
if conf.list_deleted:
typ, data = imap.search(None, 'ALL')
@@ -94,3 +95,5 @@ def execute(*args, **kw):
print num
else:
print num
+
+ imap.set_acl(folder, 'cyrus-admin', '')
diff --git a/pykolab/cli/cmd_list_quota.py b/pykolab/cli/cmd_list_quota.py
index 6aba95b..9980ea3 100644
--- a/pykolab/cli/cmd_list_quota.py
+++ b/pykolab/cli/cmd_list_quota.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>
#
@@ -32,6 +32,15 @@ conf = pykolab.getConf()
def __init__():
commands.register('list_quota', execute, description=description(), aliases=['lq'])
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
def description():
return """List quota for a folder."""
@@ -46,11 +55,15 @@ def execute(*args, **kw):
quota_folder = '*'
imap = IMAP()
- imap.connect()
+
+ if not conf.connect_server == None:
+ imap.connect(server=conf.connect_server)
+ else:
+ imap.connect()
folders = []
- quota_folders = imap.lm(quota_folder)
+ quota_folders = imap.list_folders(quota_folder)
for quota_folder in quota_folders:
try:
(used, quota) = imap.get_quota(quota_folder)
@@ -63,7 +76,10 @@ def execute(*args, **kw):
percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
print "%d (Used: %d, Percentage: %d)" % (quota, used, percentage)
else:
- print "No quota"
+ if used == None:
+ print "%d (Used: %d, Percentage: %d)" % (quota, 0, 0)
+ else:
+ print "No quota"
except:
try:
(quota_root, used, quota) = imap.get_quota_root(quota_folder)
@@ -76,7 +92,10 @@ def execute(*args, **kw):
percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
print "%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, used, percentage)
else:
- print "No quota"
+ if used == None and not quota_root == None:
+ print "%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, 0, 0)
+ else:
+ print "No quota"
except:
print "Folder: %s" % (quota_folder)
print "No quota root"
diff --git a/pykolab/cli/cmd_list_user_subscriptions.py b/pykolab/cli/cmd_list_user_subscriptions.py
index a25e8d6..3360613 100644
--- a/pykolab/cli/cmd_list_user_subscriptions.py
+++ b/pykolab/cli/cmd_list_user_subscriptions.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>
#
@@ -21,6 +21,7 @@ import commands
import pykolab
+from pykolab import imap_utf7
from pykolab.imap import IMAP
from pykolab.translate import _
from pykolab import utils
@@ -33,6 +34,12 @@ def __init__():
def cli_options(*args, **kw):
my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--raw',
+ dest = "raw",
+ action = "store_true",
+ default = False,
+ help = _("Display raw IMAP UTF-7 folder names"))
+
my_option_group.add_option( '--unsubscribed',
dest = "unsubscribed",
action = "store_true",
@@ -83,9 +90,15 @@ def execute(*args, **kw):
unsubscribed_folders.append(folder)
if len(unsubscribed_folders) > 0:
- print "\n".join(unsubscribed_folders)
+ if not conf.raw:
+ print "\n".join([imap_utf7.decode(x) for x in unsubscribed_folders])
+ else:
+ print "\n".join(unsubscribed_folders)
else:
print _("No unsubscribed folders for user %s") % (user)
else:
- print "\n".join(subscribed_folders)
+ if not conf.raw:
+ print "\n".join([imap_utf7.decode(x) for x in subscribed_folders])
+ else:
+ print "\n".join(subscribed_folders)
diff --git a/pykolab/cli/cmd_mailbox_cleanup.py b/pykolab/cli/cmd_mailbox_cleanup.py
index c3af740..a214556 100644
--- a/pykolab/cli/cmd_mailbox_cleanup.py
+++ b/pykolab/cli/cmd_mailbox_cleanup.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>
#
diff --git a/pykolab/cli/cmd_remove_mailaddress.py b/pykolab/cli/cmd_remove_mailaddress.py
index 19c4756..fbb571a 100644
--- a/pykolab/cli/cmd_remove_mailaddress.py
+++ b/pykolab/cli/cmd_remove_mailaddress.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>
#
diff --git a/pykolab/cli/cmd_remove_user_subscription.py b/pykolab/cli/cmd_remove_user_subscription.py
index 79e9d09..17ff818 100644
--- a/pykolab/cli/cmd_remove_user_subscription.py
+++ b/pykolab/cli/cmd_remove_user_subscription.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>
#
diff --git a/pykolab/cli/cmd_rename_mailbox.py b/pykolab/cli/cmd_rename_mailbox.py
index 9917f6b..e596401 100644
--- a/pykolab/cli/cmd_rename_mailbox.py
+++ b/pykolab/cli/cmd_rename_mailbox.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>
#
diff --git a/pykolab/cli/cmd_server_info.py b/pykolab/cli/cmd_server_info.py
new file mode 100644
index 0000000..51ec40d
--- /dev/null
+++ b/pykolab/cli/cmd_server_info.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 commands
+
+import pykolab
+
+from pykolab.imap import IMAP
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('server_info', execute, description=description())
+
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
+
+def description():
+ return "Display server info.\n"
+
+def execute(*args, **kw):
+ """
+ List mailboxes
+ """
+
+ imap = IMAP()
+
+ if not conf.connect_server == None:
+ imap.connect(server=conf.connect_server)
+ else:
+ imap.connect()
+
+ print imap.get_metadata("")
diff --git a/pykolab/cli/cmd_set_language.py b/pykolab/cli/cmd_set_language.py
index 316d95a..4785fe3 100644
--- a/pykolab/cli/cmd_set_language.py
+++ b/pykolab/cli/cmd_set_language.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>
#
diff --git a/pykolab/cli/cmd_set_mail.py b/pykolab/cli/cmd_set_mail.py
index d32952f..1a54f46 100644
--- a/pykolab/cli/cmd_set_mail.py
+++ b/pykolab/cli/cmd_set_mail.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>
#
diff --git a/pykolab/cli/cmd_set_mailbox_acl.py b/pykolab/cli/cmd_set_mailbox_acl.py
index 8ba4567..e86c62d 100644
--- a/pykolab/cli/cmd_set_mailbox_acl.py
+++ b/pykolab/cli/cmd_set_mailbox_acl.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>
#
@@ -67,6 +67,6 @@ def execute(*args, **kw):
print >> sys.stderr, _("No such folder %r") % (folder)
else:
- folders = imap.lm(folder)
+ folders = imap.list_folders(folder)
for folder in folders:
imap.set_acl(folder, identifier, acl)
diff --git a/pykolab/cli/cmd_set_mailbox_metadata.py b/pykolab/cli/cmd_set_mailbox_metadata.py
index 2cade85..acff7c5 100644
--- a/pykolab/cli/cmd_set_mailbox_metadata.py
+++ b/pykolab/cli/cmd_set_mailbox_metadata.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>
#
diff --git a/pykolab/cli/cmd_sync.py b/pykolab/cli/cmd_sync.py
index 8beef3d..da75a71 100644
--- a/pykolab/cli/cmd_sync.py
+++ b/pykolab/cli/cmd_sync.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>
#
diff --git a/pykolab/cli/cmd_sync_mailhost_attrs.py b/pykolab/cli/cmd_sync_mailhost_attrs.py
new file mode 100644
index 0000000..0f2e4d9
--- /dev/null
+++ b/pykolab/cli/cmd_sync_mailhost_attrs.py
@@ -0,0 +1,133 @@
+# -*- 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 commands
+
+import pykolab
+
+from pykolab import imap_utf7
+from pykolab.auth import Auth
+from pykolab.imap import IMAP
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('sync_mailhost_attrs', execute, description=description())
+
+def description():
+ return "Synchronize mailHost attribute values with the actual mailserver in a Cyrus IMAP Murder.\n"
+
+def cli_options():
+ my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
+ my_option_group.add_option( '--delete',
+ dest = "delete",
+ action = "store_true",
+ default = False,
+ help = _("Delete mailboxes for recipients that do not appear to exist in LDAP."))
+
+ my_option_group.add_option( '--dry-run',
+ dest = "dry_run",
+ action = "store_true",
+ default = False,
+ help = _("Display changes, do not apply them."))
+
+ my_option_group.add_option( '--server',
+ dest = "connect_server",
+ action = "store",
+ default = None,
+ metavar = "SERVER",
+ help = _("List mailboxes on server SERVER only."))
+
+def execute(*args, **kw):
+ """
+ Synchronize or display changes
+ """
+
+ imap = IMAP()
+
+ if not conf.connect_server == None:
+ imap.connect(server=conf.connect_server)
+ else:
+ imap.connect()
+
+ auth = Auth()
+ auth.connect()
+
+ domains = auth.list_domains()
+
+ for primary,secondaries in domains:
+ folders = []
+
+ folders.extend(imap.lm('shared/%%@%s' % (primary)))
+ folders.extend(imap.lm('user/%%@%s' % (primary)))
+
+ for secondary in secondaries:
+ folders.extend(imap.lm('shared/%%@%s' % (secondary)))
+ folders.extend(imap.lm('user/%%@%s' % (secondary)))
+
+ auth = Auth(domain=primary)
+ auth.connect()
+
+ for folder in folders:
+ server = imap.user_mailbox_server(folder)
+ recipient = auth.find_recipient('/'.join(folder.split('/')[1:]))
+ if (isinstance(recipient, list)):
+ if len(recipient) > 1:
+ log.warning(_("Multiple recipients for '%s'!") % ('/'.join(folder.split('/')[1:])))
+ continue
+ elif len(recipient) == 0:
+ if conf.delete:
+ if conf.dry_run:
+ if not folder.split('/')[0] == 'shared':
+ log.warning(_("No recipients for '%s' (would have deleted the mailbox if not for --dry-run)!") % ('/'.join(folder.split('/')[1:])))
+ else:
+ continue
+ else:
+ if not '/'.join(folder.split('/')[0]) == 'shared':
+ log.info(_("Deleting mailbox '%s' because it has no recipients") % (folder))
+ imap.dm(folder)
+ else:
+ log.info(_("Not automatically deleting shared folder '%s'") % (folder))
+ else:
+ log.warning(_("No recipients for '%s' (use --delete to delete)!") % ('/'.join(folder.split('/')[1:])))
+
+ continue
+ else:
+ mailhost = auth.get_entry_attribute(primary, recipient, 'mailhost')
+
+ if not server == mailhost:
+ if conf.dry_run:
+ print folder, server, mailhost
+ else:
+ auth.set_entry_attribute(primary, recipient, 'mailhost', server)
+
+ folders = []
+ folders.extend(imap.lm("shared/%%"))
+ folders.extend(imap.lm("user/%%"))
+
+ auth = Auth()
+ auth.connect()
+
+ for folder in folders:
+ server = imap.user_mailbox_server(folder)
+ recipient = auth.find_recipient('/'.join(folder.split('/')[1:]))
+
+ print folder, server, recipient
diff --git a/pykolab/cli/cmd_transfer_mailbox.py b/pykolab/cli/cmd_transfer_mailbox.py
index 65843a9..56aab24 100644
--- a/pykolab/cli/cmd_transfer_mailbox.py
+++ b/pykolab/cli/cmd_transfer_mailbox.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>
#
@@ -21,6 +21,8 @@ import commands
import pykolab
+from pykolab.auth import Auth
+from pykolab.imap import IMAP
from pykolab.translate import _
log = pykolab.getLogger('pykolab.cli')
@@ -41,22 +43,26 @@ def execute(*args, **kw):
if len(conf.cli_args) > 0:
target_partition = conf.cli_args.pop(0)
- mbox_parts = imap.parse_mailfolder(mailfolder)
+ imap = IMAP()
+ imap.connect()
- print "Mailbox parts:", mbox_parts
+ mbox_parts = imap.parse_mailfolder(mailfolder)
if mbox_parts['domain'] == None:
+ domain = conf.get('kolab', 'primary_domain')
user_identifier = mbox_parts['path_parts'][1]
else:
+ domain = mbox_parts['domain']
user_identifier = "%s@%s" % (mbox_parts['path_parts'][1], mbox_parts['domain'])
- print "User Identifier:", user_identifier
+ auth = Auth(domain=domain)
+ auth.connect()
- user = auth.find_user("mail", user_identifier)
+ user = auth.find_recipient(user_identifier)
- print "User:", user
-
- imap.connect()
+ source_server = imap.user_mailbox_server(mailfolder)
+ imap.connect(server=source_server)
imap.imap.xfer(mailfolder, target_server)
- auth.set_user_attribute(mbox_parts['domain'], user, "mailHost", target_server)
+ if not user == None and not len(user) < 1:
+ auth.set_entry_attributes(domain, user, {'mailhost': target_server})
diff --git a/pykolab/cli/cmd_undelete_mailbox.py b/pykolab/cli/cmd_undelete_mailbox.py
index 5b7719c..46eec85 100644
--- a/pykolab/cli/cmd_undelete_mailbox.py
+++ b/pykolab/cli/cmd_undelete_mailbox.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>
#
@@ -21,6 +21,7 @@ import commands
import pykolab
+from pykolab.imap import IMAP
from pykolab.translate import _
log = pykolab.getLogger('pykolab.cli')
@@ -43,6 +44,7 @@ def execute(*args, **kw):
if len(conf.cli_args) > 0:
target_folder = conf.cli_args.pop(0)
+ imap = IMAP()
imap.connect()
imap.undelete_mailfolder(undelete_folder, target_folder)
diff --git a/pykolab/cli/cmd_user_info.py b/pykolab/cli/cmd_user_info.py
new file mode 100644
index 0000000..23fceb8
--- /dev/null
+++ b/pykolab/cli/cmd_user_info.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2012 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 commands
+
+import pykolab
+
+from pykolab import utils
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('user_info', execute, description="Display user information.")
+
+def execute(*args, **kw):
+ from pykolab import wap_client
+
+ try:
+ user = conf.cli_args.pop(0)
+ except IndexError, errmsg:
+ user = utils.ask_question(_("Email address"))
+
+ wap_client.authenticate(username=conf.get("ldap", "bind_dn"), password=conf.get("ldap", "bind_pw"))
+ if len(user.split('@')) > 1:
+ wap_client.system_select_domain(user.split('@')[1])
+
+ user_info = wap_client.user_find({'mail':user})
+
+ for (k,v) in user_info.iteritems():
+ print "%s: %r" % (k,v)
diff --git a/pykolab/cli/commands.py b/pykolab/cli/commands.py
index c559ac0..cb57160 100644
--- a/pykolab/cli/commands.py
+++ b/pykolab/cli/commands.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>
#
@@ -56,15 +56,12 @@ def __init__():
register('help', list_commands)
register('list_users', not_yet_implemented, description="Not yet implemented")
- register('add_user', not_yet_implemented, description="Not yet implemented")
register('delete_user', not_yet_implemented, description="Not yet implemented")
register('list_groups', not_yet_implemented, description="Not yet implemented")
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
diff --git a/pykolab/cli/sieve/cmd_list.py b/pykolab/cli/sieve/cmd_list.py
index e89a42e..2c2cb54 100644
--- a/pykolab/cli/sieve/cmd_list.py
+++ b/pykolab/cli/sieve/cmd_list.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>
#
diff --git a/pykolab/cli/sieve/cmd_put.py b/pykolab/cli/sieve/cmd_put.py
index b514653..1b9514c 100644
--- a/pykolab/cli/sieve/cmd_put.py
+++ b/pykolab/cli/sieve/cmd_put.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>
#
diff --git a/pykolab/cli/sieve/cmd_refresh.py b/pykolab/cli/sieve/cmd_refresh.py
index 87af982..496f31f 100644
--- a/pykolab/cli/sieve/cmd_refresh.py
+++ b/pykolab/cli/sieve/cmd_refresh.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>
#
diff --git a/pykolab/cli/sieve/cmd_test.py b/pykolab/cli/sieve/cmd_test.py
index d396aa2..3fa0b8e 100644
--- a/pykolab/cli/sieve/cmd_test.py
+++ b/pykolab/cli/sieve/cmd_test.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>
#
diff --git a/pykolab/cli/telemetry/cmd_examine_command_issue.py b/pykolab/cli/telemetry/cmd_examine_command_issue.py
index ec827a0..3e30d19 100644
--- a/pykolab/cli/telemetry/cmd_examine_command_issue.py
+++ b/pykolab/cli/telemetry/cmd_examine_command_issue.py
@@ -1,6 +1,6 @@
# -*- 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>
#
diff --git a/pykolab/cli/telemetry/cmd_examine_session.py b/pykolab/cli/telemetry/cmd_examine_session.py
index b9bbcad..f933dcb 100644
--- a/pykolab/cli/telemetry/cmd_examine_session.py
+++ b/pykolab/cli/telemetry/cmd_examine_session.py
@@ -1,6 +1,6 @@
# -*- 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>
#
diff --git a/pykolab/cli/telemetry/cmd_expire_sessions.py b/pykolab/cli/telemetry/cmd_expire_sessions.py
index a2c693a..92a1ab7 100644
--- a/pykolab/cli/telemetry/cmd_expire_sessions.py
+++ b/pykolab/cli/telemetry/cmd_expire_sessions.py
@@ -1,6 +1,6 @@
# -*- 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>
#
diff --git a/pykolab/cli/telemetry/cmd_list_sessions.py b/pykolab/cli/telemetry/cmd_list_sessions.py
index 4579ec5..f7b8c6e 100644
--- a/pykolab/cli/telemetry/cmd_list_sessions.py
+++ b/pykolab/cli/telemetry/cmd_list_sessions.py
@@ -1,6 +1,6 @@
# -*- 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>
#
diff --git a/pykolab/cli/wap/__init__.py b/pykolab/cli/wap/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pykolab/cli/wap/__init__.py
diff --git a/pykolab/cli/wap/cmd_system_capabilities.py b/pykolab/cli/wap/cmd_system_capabilities.py
new file mode 100644
index 0000000..f56c55d
--- /dev/null
+++ b/pykolab/cli/wap/cmd_system_capabilities.py
@@ -0,0 +1,50 @@
+# -*- 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 pykolab
+from pykolab.cli import commands
+
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('system_capabilities', execute, group='wap', description="Display the system capabilities.")
+
+def execute(*args, **kw):
+ from pykolab import wap_client
+ # Create the authentication object.
+ # TODO: Binds with superuser credentials!
+ wap_client.authenticate()
+ system_capabilities = wap_client.system_capabilities()
+
+ if system_capabilities['count'] < 1:
+ print "No system capabilities"
+ sys.exit(1)
+
+ for domain in system_capabilities['list'].keys():
+ print "Domain capabilities for %s" % (domain)
+
+ domain_capabilities = system_capabilities['list'][domain]
+
+ for service in domain_capabilities['actions'].keys():
+ print " %-15s - %r" % (service, domain_capabilities['actions'][service]['type'])
diff --git a/pykolab/cli/wap/cmd_user_types_list.py b/pykolab/cli/wap/cmd_user_types_list.py
new file mode 100644
index 0000000..eb8e2c4
--- /dev/null
+++ b/pykolab/cli/wap/cmd_user_types_list.py
@@ -0,0 +1,40 @@
+# -*- 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 pykolab
+from pykolab.cli import commands
+
+from pykolab.translate import _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('list_user_types', execute, group='wap', description="List WAP user types.")
+
+def execute(*args, **kw):
+ from pykolab import wap_client
+ # Create the authentication object.
+ # TODO: Binds with superuser credentials!
+ wap_client.authenticate()
+ user_types = wap_client.user_types_list()
+
+ for user_type in user_types['list']:
+ type = user_types['list'][user_type]
+ print "%-15s - %s" % (type['key'], type['description'])
diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py
index 29675ae..239c0dd 100644
--- a/pykolab/conf/__init__.py
+++ b/pykolab/conf/__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>
#
@@ -123,7 +123,7 @@ class Conf(object):
continue
for key in self.defaults.__dict__[section].keys():
- retval = False
+ retval = False
if not config.has_option(section, key):
continue
@@ -182,7 +182,7 @@ class Conf(object):
return
for key in config.options('testing'):
- retval = False
+ retval = False
if isinstance(self.defaults.__dict__['testing'][key], int):
value = config.getint('testing',key)
@@ -596,7 +596,7 @@ class Conf(object):
if value:
try:
from smtplib import SMTP
- self.use_mail = value
+ self.use_mail = value
return True
except ImportError:
log.error(_("No SMTP class found in the smtplib library."))
@@ -606,8 +606,8 @@ class Conf(object):
# Attempt to load the suite,
# Get the suite's options,
# Set them here.
- if not hasattr(self,'test_suites'):
- self.test_suites = []
+ if not hasattr(self,'test_suites'):
+ self.test_suites = []
if "zpush" in value:
selectively = False
diff --git a/pykolab/conf/defaults.py b/pykolab/conf/defaults.py
index e1e34ab..f85b594 100644
--- a/pykolab/conf/defaults.py
+++ b/pykolab/conf/defaults.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>
#
diff --git a/pykolab/conf/entitlement.py b/pykolab/conf/entitlement.py
index c726c1e..778781d 100644
--- a/pykolab/conf/entitlement.py
+++ b/pykolab/conf/entitlement.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>
#
diff --git a/pykolab/errors.py b/pykolab/errors.py
index d6547df..94efe0e 100644
--- a/pykolab/errors.py
+++ b/pykolab/errors.py
@@ -1,4 +1,4 @@
-# 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>
#
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index adf0006..52dfdc1 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__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>
#
@@ -111,6 +111,7 @@ class IMAP(object):
if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'):
uri = conf.get(domain, 'imap_uri')
+ scheme = None
hostname = None
port = None
@@ -131,6 +132,11 @@ class IMAP(object):
if port == None:
port = 993
+ if scheme == None or scheme == "":
+ scheme = 'imaps'
+
+ uri = '%s://%s:%s' % (scheme, hostname, port)
+
# Get the credentials
admin_login = conf.get(backend, 'admin_login')
admin_password = conf.get(backend, 'admin_password')
@@ -167,7 +173,7 @@ class IMAP(object):
self._imap[hostname].logged_in = True
else:
- if not login and self._imap[hostname].logged_in == True:
+ if not login:
self.disconnect(hostname)
self.connect(uri=uri,login=False)
elif login and not hasattr(self._imap[hostname],'logged_in'):
@@ -193,7 +199,8 @@ class IMAP(object):
def disconnect(self, server=None):
if server == None:
# No server specified, but make sure self.imap is None anyways
- del self.imap
+ if hasattr(self, 'imap'):
+ del self.imap
else:
if self._imap.has_key(server):
del self._imap[server]
@@ -201,6 +208,8 @@ class IMAP(object):
log.warning(_("Called imap.disconnect() on a server that we had no connection to."))
def create_folder(self, folder_path, server=None):
+ folder_path = self.folder_utf7(folder_path)
+
if not server == None:
if not self._imap.has_key(server):
self.connect(server=server)
@@ -236,12 +245,26 @@ class IMAP(object):
else:
raise AttributeError, _("%r has no attribute %s") % (self,name)
+ def folder_utf7(self, folder):
+ from pykolab import imap_utf7
+ return imap_utf7.encode(folder)
+
+ def folder_utf8(self, folder):
+ from pykolab import imap_utf7
+ return imap_utf7.decode(folder)
+
def get_metadata(self, folder):
"""
Obtain all metadata entries on a folder
"""
+ metadata = {}
+
+ _metadata = self.imap.getannotation(self.folder_utf7(folder), '*')
- return self.imap.getannotation(folder, '*')
+ for (k,v) in _metadata.items():
+ metadata[self.folder_utf8(k)] = v
+
+ return metadata
def get_separator(self):
if not hasattr(self, 'imap') or self.imap == None:
@@ -305,7 +328,7 @@ class IMAP(object):
if short_rights.has_key(acl):
acl = short_rights[acl]
- self.imap.sam(folder, identifier, acl)
+ self.imap.sam(self.folder_utf7(folder), identifier, acl)
def set_metadata(self, folder, metadata_path, metadata_value, shared=True):
"""
@@ -321,7 +344,7 @@ class IMAP(object):
shared = False
metadata_path = metadata_path.replace('/private/', '/')
- self.imap._setannotation(folder, metadata_path, metadata_value, shared)
+ self.imap._setannotation(self.folder_utf7(folder), metadata_path, metadata_value, shared)
def shared_folder_create(self, folder_path, server=None):
"""
@@ -445,11 +468,21 @@ class IMAP(object):
admin_login = conf.get(backend, 'admin_login')
admin_password = conf.get(backend, 'admin_password')
- self.connect(login=False)
-
- self.login_plain(admin_login, admin_password, folder)
+ success = False
+ while not success:
+ try:
- (personal, other, shared) = self.namespaces()
+ self.disconnect()
+ self.connect(login=False)
+ self.login_plain(admin_login, admin_password, folder)
+ (personal, other, shared) = self.namespaces()
+ success = True
+ except Exception, errmsg:
+ log.debug(_("Waiting for the Cyrus murder to settle... %r") % (errmsg))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
+ time.sleep(0.5)
for additional_folder in additional_folders.keys():
_add_folder = {}
@@ -461,9 +494,12 @@ class IMAP(object):
folder_name = "%s%s" % (personal, folder_name)
try:
- self.imap.cm(folder_name)
+ self.create_folder(folder_name)
except:
log.warning(_("Mailbox already exists: %s") % (folder_name))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
continue
if additional_folders[additional_folder].has_key("annotations"):
@@ -608,8 +644,8 @@ class IMAP(object):
"""
Check if the environment has a folder named folder.
"""
- folders = self.imap.lm(folder)
- log.debug(_("Looking for folder '%s', we found folders: %r") % (folder,folders), level=8)
+ folders = self.imap.lm(self.folder_utf7(folder))
+ log.debug(_("Looking for folder '%s', we found folders: %r") % (folder,[self.folder_utf8(x) for x in folders]), level=8)
# Greater then one, this folder may have subfolders.
if len(folders) > 0:
return True
@@ -636,7 +672,7 @@ class IMAP(object):
"%s") % (rights,subject,folder), level=8)
self.set_acl(
- folder,
+ self.folder_utf7(folder),
"%s" % (subject),
"%s" % (rights)
)
@@ -647,7 +683,7 @@ class IMAP(object):
"%s") % (rights,subject,folder), level=8)
self.set_acl(
- folder,
+ self.folder_utf7(folder),
"%s" % (subject),
""
)
@@ -872,22 +908,25 @@ class IMAP(object):
log.info(_("Deleting folder %s") % (mailfolder_path))
- self.imap.dm(mailfolder_path)
+ self.imap.dm(self.folder_utf7(mailfolder_path))
def get_quota(self, mailfolder_path):
try:
- return self.lq(mailfolder_path)
+ return self.lq(self.folder_utf7(mailfolder_path))
except:
return
def get_quota_root(self, mailfolder_path):
- return self.lqr(mailfolder_path)
+ return self.lqr(self.folder_utf7(mailfolder_path))
def list_acls(self, folder):
"""
List the ACL entries on a folder
"""
- return self.imap.lam(folder)
+ return self.imap.lam(self.folder_utf7(folder))
+
+ def list_folders(self, pattern):
+ return [self.folder_utf8(x) for x in self.lm(self.folder_utf7(pattern))]
def list_user_folders(self, primary_domain=None, secondary_domains=[]):
"""
diff --git a/pykolab/imap/cyrus.py b/pykolab/imap/cyrus.py
index 079c4dc..6881740 100644
--- a/pykolab/imap/cyrus.py
+++ b/pykolab/imap/cyrus.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>
#
@@ -193,6 +193,14 @@ class Cyrus(cyruslib.CYRUS):
return server
+ def folder_utf7(self, folder):
+ from pykolab import imap_utf7
+ return imap_utf7.encode(folder)
+
+ def folder_utf8(self, folder):
+ from pykolab import imap_utf7
+ return imap_utf7.decode(folder)
+
def _setquota(self, mailfolder, quota):
"""
Login to the actual backend server.
@@ -367,7 +375,7 @@ class Cyrus(cyruslib.CYRUS):
# but it still would not cover all cases.
#
- # If no folders where found... well... then there you go.
+ # If no folders were found... well... then there you go.
if len(folders) < 1:
return None
diff --git a/pykolab/imap_utf7.py b/pykolab/imap_utf7.py
new file mode 100644
index 0000000..038623b
--- /dev/null
+++ b/pykolab/imap_utf7.py
@@ -0,0 +1,91 @@
+# The contents of this file has been derived code from the Twisted project
+# (http://twistedmatrix.com/). The original author is Jp Calderone.
+
+# Twisted project license follows:
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+class FolderNameError(ValueError):
+ pass
+
+
+def encode(s):
+ if isinstance(s, str) and sum(n for n in (ord(c) for c in s) if n > 127):
+ try:
+ s = unicode(s, "UTF-8")
+ except Exception, errmsg:
+ raise FolderNameError("%r contains characters not valid in a str folder name. "
+ "Convert to unicode first?" % s)
+
+ r = []
+ _in = []
+ for c in s:
+ if ord(c) in (range(0x20, 0x26) + range(0x27, 0x7f)):
+ if _in:
+ r.extend(['&', modified_base64(''.join(_in)), '-'])
+ del _in[:]
+ r.append(str(c))
+ elif c == '&':
+ if _in:
+ r.extend(['&', modified_base64(''.join(_in)), '-'])
+ del _in[:]
+ r.append('&-')
+ else:
+ _in.append(c)
+ if _in:
+ r.extend(['&', modified_base64(''.join(_in)), '-'])
+
+ return ''.join(r)
+
+
+def decode(s):
+ r = []
+ decode = []
+ for c in s:
+ if c == '&' and not decode:
+ decode.append('&')
+ elif c == '-' and decode:
+ if len(decode) == 1:
+ r.append('&')
+ else:
+ r.append(modified_unbase64(''.join(decode[1:])))
+ decode = []
+ elif decode:
+ decode.append(c)
+ else:
+ r.append(c)
+ if decode:
+ r.append(modified_unbase64(''.join(decode[1:])))
+ out = ''.join(r)
+
+ if not isinstance(out, unicode):
+ out = unicode(out, 'latin-1')
+ return out
+
+
+def modified_base64(s):
+ s_utf7 = s.encode('utf-7')
+ return s_utf7[1:-1].replace('/', ',')
+
+
+def modified_unbase64(s):
+ s_utf7 = '+' + s.replace(',', '/') + '-'
+ return s_utf7.decode('utf-7')
diff --git a/pykolab/logger.py b/pykolab/logger.py
index c812261..6f82d5d 100644
--- a/pykolab/logger.py
+++ b/pykolab/logger.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>
#
@@ -41,7 +41,11 @@ class Logger(logging.Logger):
if hasattr(sys, 'argv'):
for arg in sys.argv:
if debuglevel == -1:
- debuglevel = int(arg)
+ try:
+ debuglevel = int(arg)
+ except ValueError, errmsg:
+ continue
+
loglevel = logging.DEBUG
break
diff --git a/pykolab/plugins/__init__.py b/pykolab/plugins/__init__.py
index d0ba88e..c85ab0e 100644
--- a/pykolab/plugins/__init__.py
+++ b/pykolab/plugins/__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>
#
diff --git a/pykolab/plugins/defaultfolders/__init__.py b/pykolab/plugins/defaultfolders/__init__.py
index cee1b02..45c63e8 100644
--- a/pykolab/plugins/defaultfolders/__init__.py
+++ b/pykolab/plugins/defaultfolders/__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>
#
diff --git a/pykolab/plugins/dynamicquota/__init__.py b/pykolab/plugins/dynamicquota/__init__.py
index 62823ff..af67421 100644
--- a/pykolab/plugins/dynamicquota/__init__.py
+++ b/pykolab/plugins/dynamicquota/__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>
#
diff --git a/pykolab/plugins/recipientpolicy/__init__.py b/pykolab/plugins/recipientpolicy/__init__.py
index e3d6b65..6ca70ef 100644
--- a/pykolab/plugins/recipientpolicy/__init__.py
+++ b/pykolab/plugins/recipientpolicy/__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>
#
@@ -77,9 +77,10 @@ class KolabRecipientpolicy(object):
return mail
except KeyError, e:
log.warning(_("Attribute substitution for 'mail' failed in Recipient Policy"))
- mail = utils.translate(user_attrs['mail'], user_attrs['preferredlanguage'])
- mail = mail.lower()
- return mail
+ if user_attrs.has_key('mail'):
+ return user_attrs['mail']
+ else:
+ return None
def set_secondary_mail(self, *args, **kw):
"""
@@ -120,7 +121,14 @@ class KolabRecipientpolicy(object):
_domains = [ kw['primary_domain'] ] + kw['secondary_domains']
for attr in [ 'givenname', 'sn', 'surname' ]:
- user_attrs[attr] = utils.translate(user_attrs[attr], user_attrs['preferredlanguage'])
+ try:
+ user_attrs[attr] = utils.translate(user_attrs[attr], user_attrs['preferredlanguage'])
+ except Exception, errmsg:
+ log.error(_("An error occurred in composing the secondary mail attribute for entry %r") % (user_attrs['id']))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
+ return []
for number in alternative_mail_routines.keys():
for routine in alternative_mail_routines[number].keys():
@@ -130,8 +138,12 @@ class KolabRecipientpolicy(object):
log.debug(_("Appending additional mail address: %s") % (retval), level=8)
alternative_mail.append(retval)
- except KeyError, e:
- log.warning(_("Attribute substitution for 'alternative_mail' failed in Recipient Policy"))
+ except Exception, errmsg:
+ log.error(_("Policy for secondary email address failed: %r") % (errmsg))
+ if conf.debuglevel > 8:
+ import traceback
+ traceback.print_exc()
+ return []
for _domain in kw['secondary_domains']:
user_attrs['domain'] = _domain
diff --git a/pykolab/plugins/sievemgmt/__init__.py b/pykolab/plugins/sievemgmt/__init__.py
index 6395f2f..32dbdf5 100644
--- a/pykolab/plugins/sievemgmt/__init__.py
+++ b/pykolab/plugins/sievemgmt/__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>
#
diff --git a/pykolab/setup/__init__.py b/pykolab/setup/__init__.py
index 2d9fafe..c238177 100644
--- a/pykolab/setup/__init__.py
+++ b/pykolab/setup/__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>
#
diff --git a/pykolab/setup/components.py b/pykolab/setup/components.py
index a51c416..b22a79a 100644
--- a/pykolab/setup/components.py
+++ b/pykolab/setup/components.py
@@ -1,6 +1,6 @@
# -*- 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>
#
diff --git a/pykolab/setup/setup_freebusy.py b/pykolab/setup/setup_freebusy.py
index 9d99ca9..7c0df1d 100644
--- a/pykolab/setup/setup_freebusy.py
+++ b/pykolab/setup/setup_freebusy.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>
#
@@ -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()
diff --git a/pykolab/setup/setup_imap.py b/pykolab/setup/setup_imap.py
index 4b7564f..292c891 100644
--- a/pykolab/setup/setup_imap.py
+++ b/pykolab/setup/setup_imap.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>
#
diff --git a/pykolab/setup/setup_kolabd.py b/pykolab/setup/setup_kolabd.py
index 47d1cae..4534502 100644
--- a/pykolab/setup/setup_kolabd.py
+++ b/pykolab/setup/setup_kolabd.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>
#
diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py
index a7cedba..f6576f1 100644
--- a/pykolab/setup/setup_ldap.py
+++ b/pykolab/setup/setup_ldap.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>
#
@@ -60,6 +60,30 @@ def cli_options():
help = _("Allow anonymous binds (default: no).")
)
+ ldap_group.add_option(
+ "--without-ldap",
+ dest = "without_ldap",
+ action = "store_true",
+ default = False,
+ help = _("Skip setting up the LDAP server.")
+ )
+
+ ldap_group.add_option(
+ "--with-openldap",
+ dest = "with_openldap",
+ action = "store_true",
+ default = False,
+ help = _("Setup configuration for OpenLDAP compatibility.")
+ )
+
+ ldap_group.add_option(
+ "--with-ad",
+ dest = "with_ad",
+ action = "store_true",
+ default = False,
+ help = _("Setup configuration for Active Directory compatibility.")
+ )
+
def description():
return _("Setup LDAP.")
@@ -69,6 +93,60 @@ def execute(*args, **kw):
if not conf.config_file == conf.defaults.config_file:
ask_questions = False
+ if conf.without_ldap:
+ print >> sys.stderr, _("Skipping setup of LDAP, as specified")
+ return
+
+ _input = {}
+
+ if conf.with_openldap and not conf.with_ad:
+
+ conf.command_set('ldap', 'unique_attribute', 'entryuuid')
+
+ fp = open(conf.defaults.config_file, "w+")
+ conf.cfg_parser.write(fp)
+ fp.close()
+
+ return
+
+ elif conf.with_ad and not conf.with_openldap:
+ conf.command_set('ldap', 'auth_attributes', 'samaccountname')
+ conf.command_set('ldap', 'modifytimestamp_format', '%%Y%%m%%d%%H%%M%%S.0Z')
+ conf.command_set('ldap', 'unique_attribute', 'userprincipalname')
+
+ # TODO: These attributes need to be checked
+ conf.command_set('ldap', 'mail_attributes', 'mail')
+ conf.command_set('ldap', 'mailserver_attributes', 'mailhost')
+ conf.command_set('ldap', 'quota_attribute', 'mailquota')
+
+ return
+
+ elif conf.with_ad and conf.with_openldap:
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ You can not configure Kolab to run against OpenLDAP
+ and Active Directory simultaneously.
+ """)
+ )
+
+ sys.exit(1)
+
+ # Pre-execution checks
+ for path, directories, files in os.walk('/etc/dirsrv/'):
+ for direct in directories:
+ if direct.startswith('slapd-'):
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ It seems 389 Directory Server has an existing
+ instance configured. This setup script does not
+ intend to destroy or overwrite your data. Please
+ make sure /etc/dirsrv/ and /var/lib/dirsrv/ are
+ clean so that this setup does not have to worry.
+ """)
+ )
+
+ sys.exit(1)
+
_input = {}
if ask_questions:
@@ -147,7 +225,6 @@ def execute(*args, **kw):
_input['fqdn'] = fqdn
_input['hostname'] = hostname.split('.')[0]
_input['domain'] = domainname
-
_input['nodotdomain'] = _input['domain'].replace('.','_')
_input['rootdn'] = utils.standard_root_dn(_input['domain'])
@@ -272,7 +349,24 @@ ServerAdminPwd = %(admin_pass)s
(stdoutdata, stderrdata) = setup_389.communicate()
- # TODO: Get the return code and display output if not successful.
+ if not setup_389.returncode == 0:
+ print >> sys.stderr, utils.multiline_message(
+ _("""
+ An error was detected in the setup procedure for 389
+ Directory Server. This setup will write out stderr and
+ stdout to /var/log/kolab/setup.error.log and
+ /var/log/kolab/setup.out.log respectively, before it
+ exits.
+ """)
+ )
+
+ fp = open('/var/log/kolab/setup.error.log', 'w')
+ fp.write(stderrdata)
+ fp.close()
+
+ fp = open('/var/log/kolab/setup.out.log', 'w')
+ fp.write(stderrdata)
+ fp.close()
log.debug(_("Setup DS stdout:"), level=8)
log.debug(stdoutdata, level=8)
@@ -280,9 +374,8 @@ ServerAdminPwd = %(admin_pass)s
log.debug(_("Setup DS stderr:"), level=8)
log.debug(stderrdata, level=8)
- # TODO: Fails when ran a second time.
-
- # TODO: When fail, fail gracefully.
+ if not setup_389.returncode == 0:
+ sys.exit(1)
# Find the kolab schema. It's installed as %doc in the kolab-schema package.
# TODO: Chown nobody, nobody, chmod 440
@@ -301,6 +394,7 @@ ServerAdminPwd = %(admin_pass)s
os.path.basename(schema_file)
)
)
+
schema_error = False
except:
log.error(_("Could not copy the LDAP extensions for Kolab"))
@@ -561,7 +655,7 @@ ServerAdminPwd = %(admin_pass)s
aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrators Group"; allow (all) groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot";)')
aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrator"; allow (all) userdn="ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot";)')
aci.append('(targetattr = "*")(version 3.0; acl "SIE Group"; allow (all) groupdn = "ldap:///cn=slapd-%(hostname)s,cn=389 Directory Server,cn=Server Group,cn=%(fqdn)s,ou=%(domain)s,o=NetscapeRoot";)' %(_input))
- aci.append('(targetattr = "*") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)')
+ aci.append('(targetattr != "userPassword") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)')
modlist = []
modlist.append((ldap.MOD_REPLACE, "aci", aci))
auth._auth.ldap.modify_s(dn, modlist)
@@ -575,3 +669,4 @@ ServerAdminPwd = %(admin_pass)s
else:
log.error(_("Could not start and configure to start on boot, the " + \
"directory server admin service."))
+
diff --git a/pykolab/setup/setup_mta.py b/pykolab/setup/setup_mta.py
index 35df4fb..9280d18 100644
--- a/pykolab/setup/setup_mta.py
+++ b/pykolab/setup/setup_mta.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>
#
@@ -52,6 +52,8 @@ def execute(*args, **kw):
resource_filter = conf.get('ldap', 'resource_filter')
+ sharedfolder_filter = conf.get('ldap', 'sharedfolder_filter')
+
server_host = utils.parse_ldap_uri(conf.get('ldap', 'ldap_uri'))[1]
files = {
@@ -67,7 +69,7 @@ domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
-query_filter = (&(|(mail=%%s)(alias=%%s))(|%(kolab_user_filter)s%(kolab_group_filter)s%(resource_filter)s))
+query_filter = (&(|(mail=%%s)(alias=%%s))(|%(kolab_user_filter)s%(kolab_group_filter)s%(resource_filter)s%(sharedfolder_filter)s))
result_attribute = mail
""" % {
"base_dn": conf.get('ldap', 'base_dn'),
@@ -77,6 +79,7 @@ result_attribute = mail
"kolab_user_filter": user_filter,
"kolab_group_filter": group_filter,
"resource_filter": resource_filter,
+ "sharedfolder_filter": sharedfolder_filter,
},
"/etc/postfix/ldap/mydestination.cf": """
server_host = %(server_host)s
@@ -111,7 +114,7 @@ bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
# This finds the mail enabled distribution group LDAP entry
-query_filter = (&(mail=%%s)(objectClass=kolabgroupofuniquenames)(objectclass=groupofuniquenames))
+query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectclass=groupofuniquenames)(!(objectclass=groupofurls)))
# From this type of group, get all uniqueMember DNs
special_result_attribute = uniqueMember
# Only from those DNs, get the mail
@@ -136,7 +139,7 @@ bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
# This finds the mail enabled dynamic distribution group LDAP entry
-query_filter = (&(mail=%%s)(objectClass=kolabgroupofuniquenames)(objectClass=groupOfURLs))
+query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectClass=groupOfURLs))
# From this type of group, get all memberURL searches/references
special_result_attribute = memberURL
# Only from those DNs, get the mail
@@ -189,6 +192,27 @@ result_attribute = mail
"service_bind_dn": conf.get('ldap', 'service_bind_dn'),
"service_bind_pw": conf.get('ldap', 'service_bind_pw'),
},
+ "/etc/postfix/ldap/virtual_alias_maps_sharedfolders.cf": """
+server_host = %(server_host)s
+server_port = 389
+version = 3
+search_base = %(base_dn)s
+scope = sub
+
+domain = ldap:/etc/postfix/ldap/mydestination.cf
+
+bind_dn = %(service_bind_dn)s
+bind_pw = %(service_bind_pw)s
+
+query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabsharedfolder))
+result_attribute = kolabtargetfolder
+result_format = shared+%%s
+""" % {
+ "base_dn": conf.get('ldap', 'base_dn'),
+ "server_host": server_host,
+ "service_bind_dn": conf.get('ldap', 'service_bind_dn'),
+ "service_bind_pw": conf.get('ldap', 'service_bind_pw'),
+ },
}
if not os.path.isdir('/etc/postfix/ldap'):
@@ -199,13 +223,22 @@ result_attribute = mail
fp.write(files[filename])
fp.close()
+ fp = open('/etc/postfix/transport', 'a')
+ fp.write("\n# Shared Folder Delivery for %(domain)s:\nshared@%(domain)s\t\tlmtp:unix:/var/lib/imap/socket/lmtp\n" % {'domain': conf.get('kolab', 'primary_domain')})
+ fp.close()
+
+ subprocess.call(["postmap", "/etc/postfix/transport"])
+
postfix_main_settings = {
"inet_interfaces": "all",
+ "recipient_delimiter": "+",
"local_recipient_maps": "ldap:/etc/postfix/ldap/local_recipient_maps.cf",
"mydestination": "ldap:/etc/postfix/ldap/mydestination.cf",
- "transport_maps": "ldap:/etc/postfix/ldap/transport_maps.cf",
- "virtual_alias_maps": "$alias_maps, ldap:/etc/postfix/ldap/virtual_alias_maps.cf, ldap:/etc/postfix/ldap/mailenabled_distgroups.cf, ldap:/etc/postfix/ldap/mailenabled_dynamic_distgroups.cf",
+ "transport_maps": "ldap:/etc/postfix/ldap/transport_maps.cf, hash:/etc/postfix/transport",
+ "virtual_alias_maps": "$alias_maps, ldap:/etc/postfix/ldap/virtual_alias_maps.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_sharedfolders.cf, ldap:/etc/postfix/ldap/mailenabled_distgroups.cf, ldap:/etc/postfix/ldap/mailenabled_dynamic_distgroups.cf",
"smtpd_tls_auth_only": "yes",
+ "smtpd_tls_security_level": "may",
+ "smtp_tls_security_level": "may",
"smtpd_sasl_auth_enable": "yes",
"smtpd_sender_login_maps": "$relay_recipient_maps",
"smtpd_sender_restrictions": "permit_mynetworks, reject_sender_login_mismatch",
@@ -232,6 +265,19 @@ result_attribute = mail
'/etc/postfix/main.cf'
)
+ # Copy header checks files
+ for hc_file in [ 'inbound', 'internal', 'submission' ]:
+ if not os.path.isfile("/etc/postfix/header_checks.%s" % (hc_file)):
+ if os.path.isfile('/etc/kolab/templates/header_checks.%s' % (hc_file)):
+ input_file = '/etc/kolab/templates/header_checks.%s' % (hc_file)
+ elif os.path.isfile('/usr/share/kolab/templates/header_checks.%s' % (hc_file)):
+ input_file = '/usr/share/kolab/templates/header_checks.%s' % (hc_file)
+ elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'header_checks.%s' % (hc_file)))):
+ input_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'header_checks.%s' % (hc_file)))
+
+ shutil.copy(input_file, "/etc/postfix/header_checks.%s" % (hc_file))
+ subprocess.call(["postmap", "/etc/postfix/header_checks.%s" % (hc_file)])
+
myaugeas = Augeas()
setting_base = '/files/etc/postfix/main.cf/'
@@ -241,9 +287,12 @@ result_attribute = mail
current_value = myaugeas.get(setting)
if current_value == None:
- insert_paths = myaugeas.match('/files/etc/postfix/main.cf/*')
- insert_path = insert_paths[(len(insert_paths)-1)]
- myaugeas.insert(insert_path, setting_key, False)
+ try:
+ myaugeas.set(setting, postfix_main_settings[setting_key])
+ except:
+ insert_paths = myaugeas.match('/files/etc/postfix/main.cf/*')
+ insert_path = insert_paths[(len(insert_paths)-1)]
+ myaugeas.insert(insert_path, setting_key, False)
log.debug(_("Setting key %r to %r") % (setting_key, postfix_main_settings[setting_key]), level=8)
myaugeas.set(setting, postfix_main_settings[setting_key])
diff --git a/pykolab/setup/setup_mysql.py b/pykolab/setup/setup_mysql.py
index 1c5fd0c..4b2e8c2 100644
--- a/pykolab/setup/setup_mysql.py
+++ b/pykolab/setup/setup_mysql.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>
#
diff --git a/pykolab/setup/setup_php.py b/pykolab/setup/setup_php.py
index 54915a5..34246f9 100644
--- a/pykolab/setup/setup_php.py
+++ b/pykolab/setup/setup_php.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>
#
diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py
index 1c1c60c..7536ce2 100644
--- a/pykolab/setup/setup_roundcube.py
+++ b/pykolab/setup/setup_roundcube.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>
#
@@ -90,11 +90,11 @@ def execute(*args, **kw):
want_files = [
'acl.inc.php',
'calendar.inc.php',
- 'db.inc.php',
+ 'config.inc.php',
'kolab_auth.inc.php',
+ 'kolab_files.inc.php',
'kolab_folders.inc.php',
'kolab.inc.php',
- 'main.inc.php',
'managesieve.inc.php',
'owncloud.inc.php',
'password.inc.php',
diff --git a/pykolab/setup/setup_syncroton.py b/pykolab/setup/setup_syncroton.py
index c14682e..f902f57 100644
--- a/pykolab/setup/setup_syncroton.py
+++ b/pykolab/setup/setup_syncroton.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>
#
diff --git a/pykolab/setup/setup_zpush.py b/pykolab/setup/setup_zpush.py
deleted file mode 100644
index 809d243..0000000
--- a/pykolab/setup/setup_zpush.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2010-2012 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.
-#
-
-from Cheetah.Template import Template
-import os
-import subprocess
-import sys
-import time
-
-import components
-
-import pykolab
-
-from pykolab import utils
-from pykolab.constants import *
-from pykolab.translate import _
-
-log = pykolab.getLogger('pykolab.setup')
-conf = pykolab.getConf()
-
-def __init__():
- components.register('zpush', execute, description=description(), after=['mysql','ldap'])
-
-def description():
- return _("Setup zpush.")
-
-def execute(*args, **kw):
- if not os.path.isfile('/etc/z-push/config.php'):
- log.error(_("Z-Push is not installed on this system"))
- return
-
- zpush_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'),
- 'imap_server': "localhost"
- }
-
-
- want_files = [
- 'config.php',
- ]
-
- for want_file in want_files:
- template_file = None
- if os.path.isfile('/etc/kolab/templates/zpush/%s.tpl' % (want_file)):
- template_file = '/etc/kolab/templates/zpush/%s.tpl' % (want_file)
- elif os.path.isfile('/usr/share/kolab/templates/zpush/%s.tpl' % (want_file)):
- template_file = '/usr/share/kolab/templates/zpush/%s.tpl' % (want_file)
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'zpush', '%s.tpl' % (want_file)))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'zpush', '%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=[zpush_settings])
- log.debug(
- _("Successfully compiled template %r, writing out to %r") % (
- template_file,
- '/etc/z-push/%s' % (want_file)
- ),
- level=8
- )
-
- fp = open('/etc/z-push/%s' % (want_file), 'w')
- fp.write(t.__str__())
- fp.close()
-
- time.sleep(2)
-
- 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."))
-
- 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."))
-
diff --git a/pykolab/telemetry.py b/pykolab/telemetry.py
index 66bd1ae..1a8eea4 100644
--- a/pykolab/telemetry.py
+++ b/pykolab/telemetry.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
#
-# 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>
#
diff --git a/pykolab/translate.py b/pykolab/translate.py
index 76f50b3..0a4bef2 100644
--- a/pykolab/translate.py
+++ b/pykolab/translate.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>
#
diff --git a/pykolab/translit.py b/pykolab/translit.py
index 119675f..855e249 100644
--- a/pykolab/translit.py
+++ b/pykolab/translit.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>
#
diff --git a/pykolab/utils.py b/pykolab/utils.py
index c10bbb2..5cba8f9 100644
--- a/pykolab/utils.py
+++ b/pykolab/utils.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>
#
@@ -21,6 +21,7 @@ import getpass
import grp
import os
import pwd
+import struct
import sys
import pykolab
@@ -126,11 +127,21 @@ def ask_confirmation(question, default="y", all_inclusive_no=True):
else:
return True
-def ask_menu(question, options={}):
- print question
+def ask_menu(question, options={}, default=''):
+ if not default == '':
+ print question + " [" + default + "]:"
+ else:
+ print question
+
answer_correct = False
max_key_length = 0
+ if isinstance(options, list):
+ _options = options
+ options = {}
+ for key in _options:
+ options[key] = key
+
keys = options.keys()
keys.sort()
@@ -142,10 +153,29 @@ def ask_menu(question, options={}):
str_format = "%%%ds" % max_key_length
- for key in keys:
- print " - " + eval("str_format % key") + ": " + options[key]
+ if default == '' or not default in options.keys():
+ for key in keys:
+ if options[key] == key:
+ print " - " + key
+ else:
+ print " - " + eval("str_format % key") + ": " + options[key]
+
+ answer = raw_input(_("Choice") + ": ")
- answer = raw_input(_("Choice") + ": ")
+ else:
+ answer = raw_input(_("Choice (type '?' for options)") + ": ")
+
+ if answer == '?':
+ for key in keys:
+ if options[key] == key:
+ print " - " + key
+ else:
+ print " - " + eval("str_format % key") + ": " + options[key]
+
+ continue
+
+ if answer == '' and default in options.keys():
+ answer = default
if answer in [str(x) for x in options.keys()]:
answer_correct = True
@@ -271,20 +301,29 @@ 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(' ', '')
if result.has_key('mail'):
+ if isinstance(result['mail'], list):
+ result['mail'] = result['mail'][0]
+
if len(result['mail']) > 0:
if len(result['mail'].split('@')) > 1:
result['domain'] = result['mail'].split('@')[1]
@@ -385,6 +424,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('.')))
diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py
index a38953a..1dd6aab 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():
@@ -164,10 +160,12 @@ def get_user_input():
user_types = user_types_list()
if user_types['count'] > 1:
+ print ""
for key in user_types['list'].keys():
if not key == "status":
print "%s) %s" % (key,user_types['list'][key]['name'])
+ print ""
user_type_id = utils.ask_question("Please select the user type")
elif user_types['count'] > 0:
@@ -185,15 +183,62 @@ def get_user_input():
sys.exit(1)
params = {
- 'user_type_id': user_type_id
+ 'object_type': 'user',
+ 'type_id': user_type_id
}
+ must_attrs = []
+ may_attrs = []
+
for attribute in user_type_info['form_fields'].keys():
- params[attribute] = utils.ask_question(attribute)
+ if isinstance(user_type_info['form_fields'][attribute], dict):
+ if user_type_info['form_fields'][attribute].has_key('optional') and user_type_info['form_fields'][attribute]['optional']:
+ may_attrs.append(attribute)
+ else:
+ must_attrs.append(attribute)
+ else:
+ must_attrs.append(attribute)
- for attribute in user_type_info['auto_form_fields'].keys():
- exec("retval = form_value_generate_%s(params)" % (attribute))
- params[attribute] = retval[attribute]
+ for attribute in must_attrs:
+ if isinstance(user_type_info['form_fields'][attribute], dict) and \
+ user_type_info['form_fields'][attribute].has_key('type'):
+
+ if user_type_info['form_fields'][attribute]['type'] == 'select':
+ if not user_type_info['form_fields'][attribute].has_key('values'):
+ attribute_values = form_value_select_options('user', user_type_id, attribute)
+
+ default = ''
+ if attribute_values[attribute].has_key('default'):
+ default = attribute_values[attribute]['default']
+
+ params[attribute] = utils.ask_menu(
+ "Choose the %s value" % (attribute),
+ attribute_values[attribute]['list'],
+ default=default
+ )
+
+ else:
+ default = ''
+ if user_type_info['form_fields'][attribute].has_key('default'):
+ default = user_type_info['form_fields'][attribute]['default']
+
+ params[attribute] = utils.ask_menu(
+ "Choose the %s value" % (attribute),
+ user_type_info['form_fields'][attribute]['values'],
+ default=default
+ )
+
+ else:
+ params[attribute] = utils.ask_question(attribute)
+
+ else:
+ params[attribute] = utils.ask_question(attribute)
+
+ for attribute in user_type_info['fields'].keys():
+ params[attribute] = user_type_info['fields'][attribute]
+
+ exec("retval = user_form_value_generate(params)")
+ print retval
return params
@@ -201,9 +246,19 @@ def group_add(params=None):
if params == None:
params = get_group_input()
- params = json.dumps(params)
+ post = json.dumps(params)
+
+ return request('POST', 'group.add', post=post)
+
+def group_delete(params=None):
+ if params == None:
+ params = {
+ 'id': utils.ask_question("Name of group to delete", "group")
+ }
- return request('POST', 'group.add', params)
+ post = json.dumps(params)
+
+ return request('POST', 'group.delete', post=post)
def group_form_value_generate_mail(params=None):
if params == None:
@@ -237,7 +292,8 @@ def request(method, api_uri, get=None, post=None, headers={}):
del response_data['status']
return response_data['result']
else:
- return response_data['result']
+ print "ERROR: %r" % (response_data['reason'])
+ return False
def request_raw(method, api_uri, get=None, post=None, headers={}):
global session_id
@@ -328,40 +384,60 @@ def user_edit(user = None, attributes={}):
return user_edit
-def user_form_value_generate_cn(params=None):
- if params == None:
- params = get_user_input()
-
- post = json.dumps(params)
+def user_find(attribs=None):
+ if attribs == None:
+ post = {
+ 'search': {
+ 'params': {
+ utils.ask_question("Attribute") : {
+ 'value': utils.ask_question("value"),
+ 'type': 'exact'
+ }
+ }
+ }
+ }
+ else:
+ post = { 'search': { 'params': {} } }
- return request('POST', 'user_form_value.generate_cn', post=post)
+ for (k,v) in attribs.iteritems():
+ post['search']['params'][k] = { 'value': v, 'type': 'exact' }
-def user_form_value_generate_displayname(params=None):
- if params == None:
- params = get_user_input()
+ post = json.dumps(post)
- post = json.dumps(params)
+ user = request('POST', 'user.find', post=post)
- return request('POST', 'user_form_value.generate_displayname', post=post)
+ return user
-def user_form_value_generate_mail(params=None):
+def user_form_value_generate(params=None):
if params == None:
params = get_user_input()
post = json.dumps(params)
- return request('POST', 'user_form_value.generate_mail', post=post)
+ return request('POST', 'form_value.generate', post=post)
def form_value_generate_password(*args, **kw):
return request('GET', 'form_value.generate_password')
-def form_value_list_options(attribute_name, *args, **kw):
- post = json.dumps({'attribute': attribute_name})
+def form_value_list_options(object_type, object_type_id, attribute):
+ post = json.dumps(
+ {
+ 'object_type': object_type,
+ 'type_id': object_type_id,
+ 'attribute': attribute
+ }
+ )
return request('POST', 'form_value.list_options', post=post)
-def form_value_select_options(attribute_name, *args, **kw):
- post = json.dumps({'attributes': [attribute_name]})
+def form_value_select_options(object_type, object_type_id, attribute):
+ post = json.dumps(
+ {
+ 'object_type': object_type,
+ 'type_id': object_type_id,
+ 'attributes': [ attribute ]
+ }
+ )
return request('POST', 'form_value.select_options', post=post)
@@ -423,7 +499,7 @@ def user_form_value_generate_uid(params=None):
params = json.dumps(params)
- return request('POST', 'user_form_value.generate_uid', params)
+ return request('POST', 'form_value.generate_uid', params)
def user_form_value_generate_userpassword(*args, **kw):
result = form_value_generate_password()
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 18eea3e..686039b 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -32,6 +32,7 @@ class Event(object):
def __init__(self, from_ical="", from_string=""):
self._attendees = []
+ self._categories = []
if from_ical == "":
if from_string == "":
@@ -48,6 +49,53 @@ class Event(object):
self._attendees.append(attendee)
self.event.setAttendees(self._attendees)
+ def add_category(self, category):
+ self._categories.append(category)
+ self.event.setCategories(self._categories)
+
+ def add_exception_date(self, _datetime):
+ valid_datetime = False
+ if isinstance(_datetime, datetime.date):
+ valid_datetime = True
+
+ if isinstance(_datetime, datetime.datetime):
+ # If no timezone information is passed on, make it UTC
+ if _datetime.tzinfo == None:
+ _datetime = _datetime.replace(tzinfo=pytz.utc)
+
+ valid_datetime = True
+
+ if not valid_datetime:
+ raise InvalidEventDateError, _("Event start needs datetime.date or datetime.datetime instance")
+
+ (
+ year,
+ month,
+ day,
+ ) = (
+ _datetime.year,
+ _datetime.month,
+ _datetime.day,
+ )
+ if hasattr(_datetime, 'hour'):
+ (
+ hour,
+ minute,
+ second
+ ) = (
+ _datetime.hour,
+ _datetime.minute,
+ _datetime.second
+ )
+ _cdatetime = kolabformat.cDateTime(year, month, day, hour, minute, second)
+ else:
+ _cdatetime = kolabformat.cDateTime(year, month, day)
+
+ if hasattr(_datetime, "tzinfo"):
+ _cdatetime.setTimezone(_datetime.tzinfo.__str__())
+
+ self.event.addExceptionDate(_cdatetime)
+
def as_string_itip(self, method="REQUEST"):
cal = icalendar.Calendar()
cal.add(
@@ -194,6 +242,12 @@ class Event(object):
def get_attendees(self):
return self._attendees
+ def get_categories(self):
+ return self.event.categories()
+
+ def get_classification(self):
+ return self.classification()
+
def get_created(self):
_datetime = self.event.created()
@@ -218,6 +272,9 @@ class Event(object):
except ValueError:
result = datetime.datetime.now()
+ def get_description(self):
+ return self.event.description()
+
def get_end(self):
_datetime = self.event.end()
@@ -256,6 +313,9 @@ class Event(object):
else:
return datetime.datetime(year, month, day, hour, minute, second, tzinfo=_timezone)
+ def get_exception_dates(self):
+ return self.event.exceptionDates()
+
def get_ical_attendee(self):
# TODO: Formatting, aye? See also the example snippet:
#
@@ -477,6 +537,9 @@ class Event(object):
attendee.set_participant_status(status)
self.event.setAttendees(self._attendees)
+ def set_classification(self, classification):
+ self.event.setClassification(classification)
+
def set_created(self, _datetime=None):
if _datetime == None:
_datetime = datetime.datetime.now()
@@ -501,6 +564,9 @@ class Event(object):
kolabformat.cDateTime(year, month, day, hour, minute, second)
)
+ def set_description(self, description):
+ self.event.setDescription(description)
+
def set_dtstamp(self, _datetime):
(
year,
@@ -565,6 +631,10 @@ class Event(object):
self.event.setEnd(_cdatetime)
+ def set_exception_dates(self, _datetimes):
+ for _datetime in _datetimes:
+ self.add_exception_date(_datetime)
+
def set_from_ical(self, attr, value):
if attr == "dtend":
self.set_ical_dtend(value.dt)
@@ -698,6 +768,9 @@ class Event(object):
self.event.setLastModified(kolabformat.cDateTime(year, month, day, hour, minute, second))
+ def set_location(self, location):
+ self.event.setLocation(location)
+
def set_organizer(self, email, name=None):
contactreference = ContactReference(email)
if not name == None:
@@ -708,6 +781,9 @@ class Event(object):
def set_priority(self, priority):
self.event.setPriority(priority)
+ def set_recurrence(self, recurrence):
+ self.event.setRecurrenceRule(recurrence)
+
def set_start(self, _datetime):
valid_datetime = False
@@ -796,6 +872,7 @@ class Event(object):
msg['To'] = ', '.join([x.__str__() for x in self.get_attendees()])
msg['Date'] = formatdate(localtime=True)
+ msg.add_header('X-Kolab-MIME-Version', '3.0')
msg.add_header('X-Kolab-Type', 'application/x-vnd.kolab.event')
text = utils.multiline_message("""