diff options
author | Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> | 2013-09-18 14:17:47 +0100 |
---|---|---|
committer | Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> | 2013-09-18 14:17:47 +0100 |
commit | a4bbb85b2e377d0fe1bb76f127dc7d5f58d964a3 (patch) | |
tree | 612d13b095504f69080330c82f86c73f3b49d80d | |
parent | 1dad011fd3005577481b359426cebe5fb41743fa (diff) | |
parent | 5f76c88df172452a1947819b1a1ad872543649b6 (diff) | |
download | pykolab-a4bbb85b2e377d0fe1bb76f127dc7d5f58d964a3.tar.gz |
Merge branch 'master' of ssh://git.kolabsys.com/git/pykolab
141 files changed, 2862 insertions, 1474 deletions
@@ -9,11 +9,13 @@ Makefile.in *.rej *.spec *.tar.gz +*.tar.gz.gpg aclocal.m4 autom4te.cache/ bin/rebuild.sh bin/test* ChangeLog +conf/*-*.conf config.log config.status configure @@ -23,3 +25,4 @@ po/POTFILES pykolab/constants.py pykolab-[0-9]*.*/ src/ +test-* diff --git a/bin/kolab_parse_telemetry.py b/bin/kolab_parse_telemetry.py index ea4da25..b4fb6d0 100755 --- a/bin/kolab_parse_telemetry.py +++ b/bin/kolab_parse_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/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py index 0320973..c13bfaf 100755 --- a/bin/kolab_smtp_access_policy.py +++ b/bin/kolab_smtp_access_policy.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> # @@ -62,7 +62,7 @@ log = pykolab.getLogger('pykolab.smtp_access_policy') # TODO: Removing the stdout handler would mean one can no longer test by # means of manual execution in debug mode. -log.remove_stdout_handler() +#log.remove_stdout_handler() conf = pykolab.getConf() @@ -410,23 +410,53 @@ class PolicyRequest(object): John.Doe@example.org (mail) for example could be sending with envelope sender jdoe@example.org (mailAlternateAddress, alias). """ - search_attrs = conf.get_list(self.sasl_domain, 'mail_attributes') + + search_attrs = conf.get_list(self.sasl_domain, 'address_search_attrs') + + if search_attrs == None or \ + (isinstance(search_attrs, list) and len(search_attrs) == 0): + + search_attrs = conf.get_list(self.sasl_domain, 'mail_attributes') if search_attrs == None or \ (isinstance(search_attrs, list) and len(search_attrs) == 0): search_attrs = conf.get_list( + 'kolab_smtp_access_policy', + 'address_search_attrs' + ) + + if search_attrs == None or \ + (isinstance(search_attrs, list) and len(search_attrs) == 0): + + + search_attrs = conf.get_list( conf.get('kolab', 'auth_mechanism'), 'mail_attributes' ) + want_attrs = [] + + for search_attr in search_attrs: + if not self.sasl_user.has_key(search_attr): + want_attrs.append(search_attr) + + if len(want_attrs) > 0: + self.sasl_user.update( + self.auth.get_user_attributes( + self.sasl_domain, + self.sasl_user, + want_attrs + ) + ) + # Catch a user using one of its own alias addresses. for search_attr in search_attrs: if self.sasl_user.has_key(search_attr): if isinstance(self.sasl_user[search_attr], list): - if self.sender in self.sasl_user[search_attr]: + if self.sender.lower() in [x.lower() for x in self.sasl_user[search_attr]]: return True - elif self.sasl_user[search_attr] == self.sender: + elif self.sasl_user[search_attr].lower() == self.sender.lower(): return True return False @@ -491,7 +521,7 @@ class PolicyRequest(object): ) reject( - _("Could not find envelope sender user %s") % ( + _("Could not find envelope sender user %s (511)") % ( self.sasl_username ) ) @@ -953,7 +983,9 @@ class PolicyRequest(object): self.verify_authenticity() self.sasl_user_uses_alias = self.verify_alias() + if not self.sasl_user_uses_alias: + log.debug(_("Sender is not using an alias"), level=8) self.sasl_user_is_delegate = self.verify_delegate() # If the authenticated user is using delegate functionality, apply the @@ -1230,7 +1262,10 @@ def hold(message, policy_request=None): def permit(message, policy_request=None): log.info(_("Returning action PERMIT: %s") % (message)) - print "action=PERMIT\n\n" + if hasattr(policy_request, 'sasl_username'): + print "action=PREPEND Sender: %s\naction=PERMIT\n\n" % (policy_request.sasl_username) + else: + print "action=PERMIT\n\n" sys.exit(0) def reject(message, policy_request=None): @@ -1267,13 +1302,13 @@ def normalize_address(email_address): # Take the first part split by recipient delimiter and the last part # split by '@'. return "%s@%s" % ( - email_address.split("+")[0], + email_address.split("+")[0].lower(), # TODO: Under some conditions, the recipient may not be fully # qualified. We'll cross that bridge when we get there, though. - email_address.split('@')[1] + email_address.split('@')[1].lower() ) else: - return email_address + return email_address.lower() def read_request_input(): """ @@ -1421,4 +1456,4 @@ if __name__ == "__main__": elif not recipient_allowed: reject(_("Recipient access denied")) else: - permit(_("No objections")) + permit(_("No objections"), policy_requests[instance]) diff --git a/bin/rebuild.sh.in b/bin/rebuild.sh.in index 4e2510d..2fda4c0 100644 --- a/bin/rebuild.sh.in +++ b/bin/rebuild.sh.in @@ -13,9 +13,13 @@ declare -a product_series declare -a answers declare -a tasks -dists[${#dists[@]}]='f16'; dist_tags[${#dist_tags[@]}]='fc16'; product_series[${#product_series[@]}]="3.0" +#dists[${#dists[@]}]='f16'; dist_tags[${#dist_tags[@]}]='fc16'; product_series[${#product_series[@]}]="3.0" dists[${#dists[@]}]='f17'; dist_tags[${#dist_tags[@]}]='fc17'; product_series[${#product_series[@]}]="3.0" +dists[${#dists[@]}]='f17'; dist_tags[${#dist_tags[@]}]='fc17'; product_series[${#product_series[@]}]="3.1" +dists[${#dists[@]}]='f18'; dist_tags[${#dist_tags[@]}]='fc18'; product_series[${#product_series[@]}]="3.0" +dists[${#dists[@]}]='f18'; dist_tags[${#dist_tags[@]}]='fc18'; product_series[${#product_series[@]}]="3.1" dists[${#dists[@]}]='el6'; dist_tags[${#dist_tags[@]}]='el6'; product_series[${#product_series[@]}]="3.0" +dists[${#dists[@]}]='el6'; dist_tags[${#dist_tags[@]}]='el6'; product_series[${#product_series[@]}]="3.1" autoreconf -v && ./configure && make dist || exit 1 @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- 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/conf/kolab.conf b/conf/kolab.conf index e0f64c3..f98dca9 100644 --- a/conf/kolab.conf +++ b/conf/kolab.conf @@ -17,6 +17,11 @@ imap_backend = cyrus-imap ; The default locale for this Kolab Groupware installation default_locale = en_US +; Synchronization interval - describes the number of seconds to wait in +; between non-persistent synchronization attempts. Relevant only for +; deployments that lack persistent search and syncrepl ldap controls. +sync_interval = 300 + [ldap] ; The URI to LDAP ldap_uri = ldap://localhost:389 @@ -117,6 +122,8 @@ quota_attribute = mailquota ; ; For OpenLDAP, use 'entrydn' - the 'entryUUID' can regrettably not be searched ; with. +; +; For Active Directory, use 'objectsid'. unique_attribute = nsuniqueid ; Attribute names that hold valid, internal recipient addresses. Note the use @@ -247,11 +254,6 @@ autocreate_folders = { '/shared/vendor/kolab/folder-type': "configuration.default", }, }, - 'Drafts': { - 'annotations': { - '/private/vendor/kolab/folder-type': "mail.drafts", - }, - }, 'Contacts': { 'annotations': { '/private/vendor/kolab/folder-type': "contact.default", @@ -263,6 +265,16 @@ autocreate_folders = { '/shared/vendor/kolab/folder-type': "contact", }, }, + 'Drafts': { + 'annotations': { + '/private/vendor/kolab/folder-type': "mail.drafts", + }, + }, + 'Files': { + 'annotations': { + '/private/vendor/kolab/folder-type': "file.default", + }, + }, 'Journal': { 'annotations': { '/private/vendor/kolab/folder-type': "journal.default", diff --git a/configure.ac b/configure.ac index abf6bbd..79800f8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT([pykolab], 0.6) -AC_SUBST([RELEASE], 0.1) +AC_INIT([pykolab], 0.6.5) +AC_SUBST([RELEASE], 1) AC_CONFIG_SRCDIR(pykolab/constants.py.in) diff --git a/cyruslib.py b/cyruslib.py index 75b636f..85d7e63 100644 --- a/cyruslib.py +++ b/cyruslib.py @@ -642,49 +642,128 @@ class CYRUS: """Get Annotation""" self.__prepare('GETANNOTATION') res, data = self.__docommand('getannotation', self.decode(mailbox), pattern) + if (len(data) == 1) and data[0] is None: self.__verbose( '[GETANNOTATION %s] No results' % (mailbox) ) return {} + ann = {} - for annotation in data: - if isinstance(annotation, tuple): - annotation = annotation[0] - self.__verbose( '[GETANNOTATION] RAW %r (length %d)' % (annotation,len(annotation))) - annotation = annotation.split('"') - self.__verbose( '[GETANNOTATION] SPLIT %r (length %d)' % (annotation,len(annotation))) - if not len(annotation) in [ 9, 17, 21, 37 ]: - self.__verbose( '[GETANNOTATION] Invalid annotation entry' ) - continue - mbx = self.encode(annotation[1]) - _key = annotation[3] - - if annotation[5] == "value.shared": - key = "/shared%s" % (_key) - elif annotation[5] == "value.priv": - key = "/private%s" % (_key) - - value = annotation[7] - - self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) ) - if not ann.has_key(mbx): - ann[mbx] = {} - if not ann[mbx].has_key(key): - ann[mbx][key] = value - - if len(annotation) > 21: - # There's another one hidden in here. - if annotation[21] == "value.shared": - key = "/shared%s" % (_key) - elif annotation[21] == "value.priv": - key = "/private%s" % (_key) - - value = annotation[23] - - self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) ) - if not ann.has_key(mbx): - ann[mbx] = {} - if not ann[mbx].has_key(key): - ann[mbx][key] = value + annotations = [] + empty_values = [ "NIL", '" "', None, '', ' ' ] + + concat_items = [] + for item in data: + if isinstance(item, tuple): + item = ' '.join([str(x) for x in item]) + + if len(concat_items) > 0: + concat_items.append(item) + + if ''.join(concat_items).count('(') == ''.join(concat_items).count(')'): + annotations.append(''.join(concat_items)) + concat_items = [] + continue + else: + + if item.count('(') == item.count(')'): + annotations.append(item) + continue + else: + concat_items.append(item) + continue + + for annotation in annotations: + annotation = annotation.strip() + + if not annotation[0] == '"': + folder = annotation.split('"')[0].replace('"','').strip() + key = annotation.split('"')[1].replace('"','').replace("'","").strip() + _annot = annotation.split('(')[1].split(')')[0].strip() + else: + folder = annotation.split('"')[1].replace('"','').strip() + key = annotation.split('"')[3].replace('"','').replace("'","").strip() + _annot = annotation.split('(')[1].split(')')[0].strip() + + if not ann.has_key(folder): + ann[folder] = {} + + try: + value_priv = _annot[(_annot.index('"value.priv"')+len('"value.priv"')):_annot.index('"size.priv"')].strip() + except ValueError, errmsg: + value_priv = None + + try: + size_priv = _annot[(_annot.index('"size.priv"')+len('"size.priv"')):].strip().split('"')[1].strip() + try: + value_priv = value_priv[value_priv.index('{%s}' % (size_priv))+len('{%s}' % (size_priv)):].strip() + except Exception, errmsg: + pass + except Exception, errmsg: + pass + + if value_priv in empty_values: + value_priv = None + else: + try: + value_priv = value_priv[:value_priv.index('"content-type.priv"')].strip() + except: + pass + + try: + value_priv = value_priv[:value_priv.index('"modifiedsince.priv"')].strip() + except: + pass + + if value_priv.startswith('"'): + value_priv = value_priv[1:] + + if value_priv.endswith('"'): + value_priv = value_priv[:-1] + + if value_priv in empty_values: + value_priv = None + + try: + value_shared = _annot[(_annot.index('"value.shared"')+len('"value.shared"')):_annot.index('"size.shared"')].strip() + except ValueError, errmsg: + value_shared = None + + try: + size_shared = _annot[(_annot.index('"size.shared"')+len('"size.shared"')):].strip().split('"')[1].strip() + try: + value_shared = value_shared[value_shared.index('{%s}' % (size_shared))+len('{%s}' % (size_shared)):].strip() + except Exception, errmsg: + pass + except Exception, errmsg: + pass + + if value_shared in empty_values: + value_shared = None + else: + try: + value_shared = value_shared[:value_shared.index('"content-type.shared"')].strip() + except: + pass + + try: + value_shared = value_shared[:value_shared.index('"modifiedsince.shared"')].strip() + except: + pass + + if value_shared.startswith('"'): + value_shared = value_shared[1:] + + if value_shared.endswith('"'): + value_shared = value_shared[:-1] + + if value_shared in empty_values: + value_shared = None + + if not value_priv == None: + ann[folder]['/private' + key] = value_priv + + if not value_shared == None: + ann[folder]['/shared' + key] = value_shared return ann @@ -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> # @@ -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/kolabd/__init__.py b/kolabd/__init__.py index b345b8d..5624280 100644 --- a/kolabd/__init__.py +++ b/kolabd/__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> # diff --git a/kolabd/process.py b/kolabd/process.py index 70b535a..ffe33f5 100644 --- a/kolabd/process.py +++ b/kolabd/process.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> # @@ -30,6 +30,7 @@ conf = pykolab.getConf() class KolabdProcess(multiprocessing.Process): def __init__(self, domain): self.domain = domain + log.debug(_("Process created for domain %s") % (domain), level=8) multiprocessing.Process.__init__( self, target=self.synchronize, @@ -38,11 +39,20 @@ class KolabdProcess(multiprocessing.Process): ) def synchronize(self, domain): + log.debug(_("Synchronizing for domain %s") % (domain), level=8) + sync_interval = conf.get('kolab', 'sync_interval') + + if sync_interval == None or sync_interval == 0: + sync_interval = 300 + else: + sync_interval = (int)(sync_interval) + while True: try: auth = Auth(domain) auth.connect(domain) auth.synchronize() + time.sleep(sync_interval) except KeyboardInterrupt: break except Exception, errmsg: diff --git a/pykolab.spec.in b/pykolab.spec.in index f2a8a3c..8a43f44 100644 --- a/pykolab.spec.in +++ b/pykolab.spec.in @@ -41,6 +41,7 @@ Requires: kolab-cli = %{version}-%{release} Requires: python-ldap >= 2.4 Requires: python-pyasn1 Requires: python-pyasn1-modules +Requires: python-sqlalchemy Requires(pre): /usr/sbin/useradd Requires(pre): /usr/sbin/usermod Requires(pre): /usr/sbin/groupadd @@ -301,6 +302,9 @@ rm -rf %{buildroot} %{python_sitelib}/pykolab/plugins/defaultfolders %{python_sitelib}/pykolab/plugins/dynamicquota %{python_sitelib}/pykolab/plugins/recipientpolicy +# No sieve script management without our modified version of +# python-sievelib +%exclude %{python_sitelib}/pykolab/plugins/sievemgmt %{python_sitelib}/kolab/ %{python_sitelib}/cyruslib.py* %attr(0775,kolab,kolab-n) %dir %{_localstatedir}/lib/kolab/ @@ -331,6 +335,10 @@ rm -rf %{buildroot} %{python_sitelib}/pykolab/cli/*.py %{python_sitelib}/pykolab/cli/*.pyc %{python_sitelib}/pykolab/cli/*.pyo +%dir %{python_sitelib}/pykolab/cli/wap +%{python_sitelib}/pykolab/cli/wap/*.py +%{python_sitelib}/pykolab/cli/wap/*.pyc +%{python_sitelib}/pykolab/cli/wap/*.pyo %dir %{python_sitelib}/pykolab/setup/ %{python_sitelib}/pykolab/setup/*.py %{python_sitelib}/pykolab/setup/*.pyc @@ -339,6 +347,9 @@ rm -rf %{buildroot} %{python_sitelib}/pykolab/wap_client/*.py %{python_sitelib}/pykolab/wap_client/*.pyc %{python_sitelib}/pykolab/wap_client/*.pyo +# No sieve script management without our modified version of +# python-sievelib +%exclude %{python_sitelib}/pykolab/cli/sieve %files -n kolab-saslauthd %defattr(-,root,root,-) 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(""" diff --git a/saslauthd.py b/saslauthd.py index b24e0e3..26677bc 100755 --- a/saslauthd.py +++ b/saslauthd.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/saslauthd/__init__.py b/saslauthd/__init__.py index edbca94..4eaba67 100644 --- a/saslauthd/__init__.py +++ b/saslauthd/__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> # @@ -194,7 +194,14 @@ class SASLAuthDaemon(object): auth = Auth(domain=realm) auth.connect() - if auth.authenticate(login): + success = False + + try: + success = auth.authenticate(login) + except: + success = False + + if success: # #1170: Catch broken pipe error (incomplete authentication request) try: clientsocket.send(struct.pack("!H2s", 2, "OK")) @@ -208,6 +215,7 @@ class SASLAuthDaemon(object): pass clientsocket.close() + auth.disconnect() def reload_config(self, *args, **kw): pass diff --git a/setup-kolab.py b/setup-kolab.py index 7794e99..e70358d 100755 --- a/setup-kolab.py +++ b/setup-kolab.py @@ -1,7 +1,7 @@ #!/usr/bin/python -u # -*- 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/share/Makefile.am b/share/Makefile.am index 4bfff10..fc80b0a 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -1,35 +1,18 @@ templatedir = $(datadir)/kolab/templates template_DATA = \ - $(wildcard templates/*.tpl) - -fbtemplatedir = $(datadir)/kolab/templates/freebusy -fbtemplate_DATA = \ - $(wildcard templates/freebusy/*.tpl) - -hordetemplatedir = $(datadir)/kolab/templates/horde/conf.d/ -hordetemplate_DATA = \ - $(wildcard templates/horde/conf.d/*.tpl) + $(wildcard templates/*.tpl) \ + $(wildcard templates/header_checks.*) rctemplatedir = $(datadir)/kolab/templates/roundcubemail rctemplate_DATA = \ $(wildcard templates/roundcubemail/*.tpl) -zpushtemplatedir = $(datadir)/kolab/templates/zpush -zpushtemplate_DATA = \ - $(wildcard templates/zpush/*.tpl) - EXTRA_DIST = \ $(template_DATA) \ - $(fbtemplate_DATA) \ - $(hordetemplate_DATA) \ - $(rctemplate_DATA) \ - $(zpushtemplate_DATA) + $(rctemplate_DATA) install-exec-local: mkdir -p \ $(DESTDIR)/$(sysconfdir)/kolab/templates \ - $(DESTDIR)/$(sysconfdir)/kolab/templates/freebusy \ - $(DESTDIR)/$(sysconfdir)/kolab/templates/horde/conf.d \ - $(DESTDIR)/$(sysconfdir)/kolab/templates/roundcubemail \ - $(DESTDIR)/$(sysconfdir)/kolab/templates/zpush + $(DESTDIR)/$(sysconfdir)/kolab/templates/roundcubemail diff --git a/share/templates/cyrus.conf.tpl b/share/templates/cyrus.conf.tpl index 056c210..9a4a17a 100644 --- a/share/templates/cyrus.conf.tpl +++ b/share/templates/cyrus.conf.tpl @@ -37,18 +37,18 @@ EVENTS { # this is only necessary if using duplicate delivery suppression, # Sieve or NNTP - duplicate_prune cmd="cyr_expire -E 3" at=0400 + duplicateprune cmd="cyr_expire -E 3" at=0400 # Expire data older then 69 days. Two full months of 31 days # each includes two full backup cycles, plus 1 week margin # because we run our full backups on the first sat/sun night # of each month. - delete_prune cmd="cyr_expire -E 4 -D 69" at=0430 - expunge_prune cmd="cyr_expire -E 4 -X 69" at=0445 + deleteprune cmd="cyr_expire -E 4 -D 69" at=0430 + expungeprune cmd="cyr_expire -E 4 -X 69" at=0445 # this is only necessary if caching TLS sessions tlsprune cmd="tls_prune" at=0400 # Create search indexes regularly - squatter cmd="squatter -s -i" at=0530 + #squatter cmd="squatter -s -i" at=0530 } diff --git a/share/templates/freebusy/config.php.tpl b/share/templates/freebusy/config.php.tpl deleted file mode 100644 index f41195a..0000000 --- a/share/templates/freebusy/config.php.tpl +++ /dev/null @@ -1,212 +0,0 @@ -<?php -/** - * This file provides configuration settings for both the freebusy.php - * and the pfb.php scripts. - * - * \$Horde: framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/config.php,v 1.4.2.2 2010/07/22 13:55:30 wrobel Exp \$ - * - * Copyright 2008-2009 The Horde Project (http://www.horde.org/) - * - * @author Steffen Hansen <steffen@klaralvdalens-datakonsult.se> - * @author Gunnar Wrobel <p@rdus.de> - * @author Thomas Arendsen Hein <thomas@intevation.de> - * @package Kolab_FreeBusy - */ - -\$conf = array(); - -/* Horde::Log configuration */ -\$conf['log']['enabled'] = true; -\$conf['log']['priority'] = PEAR_LOG_DEBUG; -\$conf['log']['type'] = 'file'; -\$conf['log']['name'] = '/var/log/kolab/freebusy/freebusy.log'; -\$conf['log']['ident'] = 'Kolab Free/Busy'; -\$conf['log']['params']['append'] = true; - -/* PHP error logging */ -ini_set('error_log', '/var/log/kolab/freebusy/php.log'); - -/* Horde::Kolab::LDAP configuration */ -\$conf['kolab']['ldap']['server'] = 'localhost'; -\$conf['kolab']['ldap']['basedn'] = '$ldap_base_dn'; -\$conf['kolab']['ldap']['phpdn'] = '$ldap_service_bind_dn'; -\$conf['kolab']['ldap']['phppw'] = '$ldap_service_bind_pw'; - -/* Horde::Kolab::IMAP configuration */ -\$conf['kolab']['imap']['server'] = 'localhost'; -\$conf['kolab']['imap']['port'] = 143; -\$conf['kolab']['imap']['protocol'] = 'imap/tls/novalidate-cert/readonly'; -\$conf['kolab']['imap']['namespaces'] = array( - array('type' => 'personal', 'name' => '', 'delimiter' => '/'), - array('type' => 'other', 'name' => 'Other Users', 'delimiter' => '/'), - array('type' => 'shared', 'name' => 'Shared Folders', 'delimiter' => '/'), -); - -/* Horde::Auth configuration */ -\$conf['auth']['params']['login_block'] = 0; -\$conf['auth']['checkbrowser'] = false; -\$conf['auth']['checkip'] = false; -\$conf['umask'] = false; - -\$conf['auth']['driver'] = 'imap'; -\$conf['auth']['params']['hostspec'] = 'localhost'; -\$conf['auth']['params']['protocol'] = 'imap/tls/novalidate-cert'; - -/* Allow special users to log into the system */ -\$conf['kolab']['imap']['allow_special_users'] = true; - -/* Do not record login attempts */ -\$conf['auth']['params']['login_block'] = false; - -/* Kolab::Freebusy configuration */ - -/* Should we redirect using a Location header, if the user is not local? If this - * is false we silently download the file ourselves and output it so that it - * looks as though the free/busy information is coming from us. - */ -\$conf['fb']['redirect'] = false; - -/* What is the address of the current server where the calendar data is stored? - * This is also used as the LDAP server address where user objects reside. - */ -\$conf['kolab']['freebusy']['server'] = 'http://' . \$_SERVER["HTTP_HOST"] . '/freebusy'; - -/* What is our default mail domain? This is used if any users do not have - * '@domain' specified after their username as part of their email address. - */ -\$conf['fb']['email_domain'] = '$primary_domain'; - -/* Location of the cache files */ -\$conf['fb']['cache_dir'] = '/tmp'; -\$conf['fb']['cache']['driver'] = 'sql'; -\$conf['fb']['cache']['params']['phptype'] = 'mysql'; -\$conf['fb']['cache']['params']['username'] = 'roundcube'; -\$conf['fb']['cache']['params']['password'] = '$mysql_roundcube_password'; -\$conf['fb']['cache']['params']['hostspec'] = 'localhost'; -\$conf['fb']['cache']['params']['database'] = 'roundcube'; -\$conf['fb']['cache']['params']['charset'] = 'utf-8'; - -/* What db type to use for the freebusy caches */ -\$conf['fb']['dbformat'] = 'db4'; - -/* Should we send a Content-Type header, indicating what the mime type of the - * resulting VFB file is? - */ -\$conf['fb']['send_content_type'] = false; - -/* Should we send a Content-Length header, indicating how large the resulting - * VFB file is? - */ -\$conf['fb']['send_content_length'] = false; - -/* Should we send a Content-Disposition header, indicating what the name of the - * resulting VFB file should be? - */ -\$conf['fb']['send_content_disposition'] = false; - -/* Should we use ACLs or does everybody get full rights? DO NOT set - * this to false if you don't know what you are doing. Your free/busy - * service should not be visible to any outside networks when - * disabling the use of ACL settings. - */ -\$conf['fb']['use_acls'] = true; - -/* How many days in advance should the free/busy information be calculated? This - * is the default value that can be overwritten by the kolabFreeBusyFuture - * attribute of the users LDAP account. - */ -\$conf['fb']['future_days'] = 180; - -/* The resulting vCalendar file is being cached. The following setting - * determines how many seconds it will be delivered without checking if - * the contents of the file might have changed. A negative setting disables - * caching (which is currently required for the resource management to work). - */ -\$conf['fb']['vcal_cache']['min_age'] = -1; - -/* The resulting vCalendar file is being cached. The following setting - * determines after how many seconds it will be considered too old for - * delivery and a refresh of its contents will be enforced. - */ -\$conf['fb']['vcal_cache']['max_age'] = 259200; - -/* The IMAP namespaces on the server. @TODO: Should obviously be - * auto-detected. - */ -\$conf['fb']['namespace']['personal'] = ''; -\$conf['fb']['namespace']['other'] = 'Other Users'; - -/* In most cases you can rely on the standard event status to free/busy status - * mapping. For the default kolab server this will mean that only the event - * status "free" will be mapped to the free/busy status "FREE". All other event - * status ("tentative", "busy", "outofoffice") will be mapped to "BUSY". - * - * If this mapping should be modified you can define it like this: - * - * \$conf['fb']['status_map'] = array( - * Horde_Kolab_FreeBusy_Object_Event::STATUS_TENTATIVE => - * Horde_Kolab_FreeBusy_Helper_Freebusy_StatusMap::STATUS_BUSY_TENTATIVE, - * Horde_Kolab_FreeBusy_Object_Event::STATUS_OUTOFOFFICE => - * 'X-OUT-OF-OFFICE', - * ); - */ -require_once 'Horde/Kolab/FreeBusy/Object/Event.php'; -require_once 'Horde/Kolab/FreeBusy/Helper/Freebusy/StatusMap.php'; -\$conf['fb']['status_map'] = array( - Horde_Kolab_FreeBusy_Object_Event::STATUS_TENTATIVE => - Horde_Kolab_FreeBusy_Helper_Freebusy_StatusMap::STATUS_BUSY_TENTATIVE, - Horde_Kolab_FreeBusy_Object_Event::STATUS_OUTOFOFFICE => - 'X-OUT-OF-OFFICE', -); - -/* Are there remote servers on which users have additional (shared) - * folders? In that case free/busy information should also be fetched - * from these servers. - * - * Add them like this: - * - * array('remote1.example.com', 'remote2.example.com') - */ -\$conf['fb']['remote_servers'] = array(); - -/* Is there an exchange server that you want to relay through this free/busy - * application? - * - * Configure it like this: - * - * \$conf['fb']['exchange_server'] = array( - * 'url' => 'https://example.com', - * 'interval' => 30, - * ); - */ -#\$conf['fb']['exchange_server'] = array( -# 'url' => 'http://test90-9.test90.kolabsys.com', -# 'interval' => 30, -# 'username' => 'kolabservice', -# 'password' => 'SomePass', -#); - -/** - * Ensure we use the Kolab group driver when handling groups. - */ -\$conf['group']['driver'] = 'kolab'; -\$conf['group']['cache'] = false; - -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// If you modify this file, please do not forget to also modify the -// template in kolabd! -// -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -// DEBUGGING -// ========= -// -// Activate this to see the log messages on the screen -// \$conf['log']['type'] = 'display'; -// -// Activate this to see the php messages on the screen -// ini_set('display_errors', 1); -// -// Both settings will disrupt header delivery (which should not cause a -// problem). diff --git a/share/templates/header_checks.inbound b/share/templates/header_checks.inbound new file mode 100644 index 0000000..a824f84 --- /dev/null +++ b/share/templates/header_checks.inbound @@ -0,0 +1,6 @@ +/^X-Spam-Flag:.*YES/ REJECT +/^X-Virus-Scanned:/ IGNORE +/^X-Spam-Flag:.*NO/ IGNORE +/^X-Spam-Score:/ IGNORE +/^X-Spam-Level:/ IGNORE +/^X-Spam-Status:/ IGNORE diff --git a/share/templates/header_checks.internal b/share/templates/header_checks.internal new file mode 100644 index 0000000..2976141 --- /dev/null +++ b/share/templates/header_checks.internal @@ -0,0 +1 @@ +/^Received:.*127\.0\.0\.1/ IGNORE diff --git a/share/templates/header_checks.submission b/share/templates/header_checks.submission new file mode 100644 index 0000000..4866ef8 --- /dev/null +++ b/share/templates/header_checks.submission @@ -0,0 +1,5 @@ +/^Received:.*with ESMTPSA/ IGNORE +/^Received:.*127\.0\.0\.1/ IGNORE +/^User-Agent:/ IGNORE +/^X-Mailer:/ IGNORE +/^Mime-Version:/ IGNORE diff --git a/share/templates/imapd.conf.tpl b/share/templates/imapd.conf.tpl index 8d4e30b..874e55a 100644 --- a/share/templates/imapd.conf.tpl +++ b/share/templates/imapd.conf.tpl @@ -8,7 +8,7 @@ sasl_mech_list: PLAIN LOGIN allowplaintext: no tls_cert_file: /etc/pki/cyrus-imapd/cyrus-imapd.pem tls_key_file: /etc/pki/cyrus-imapd/cyrus-imapd.pem -tls_ca_file: /etc/pki/tls/certs/ca-bundle.crt +tls_ca_file: /etc/pki/cyrus-imapd/cyrus-imapd.pem # uncomment this if you're operating in a DSCP environment (RFC-4594) # qosmarking: af13 auth_mech: pts @@ -48,3 +48,4 @@ deletedprefix: DELETED delete_mode: delayed expunge_mode: delayed flushseenstate: 1 +postuser: shared diff --git a/share/templates/master.cf.tpl b/share/templates/master.cf.tpl index 161d053..44c983a 100644 --- a/share/templates/master.cf.tpl +++ b/share/templates/master.cf.tpl @@ -1,19 +1,17 @@ -# # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master"). -# # Do not forget to execute "postfix reload" after editing this file. -# -# ========================================================================== -# service type private unpriv chroot wakeup maxproc command + args -# (yes) (yes) (yes) (never) (100) -# ========================================================================== -smtp inet n - n - - smtpd -#smtp inet n - n - 1 postscreen -#smtpd pass - - n - - smtpd -#dnsblog unix - - n - 0 dnsblog -#tlsproxy unix - - n - 0 tlsproxy -submission inet n - n - - smtpd +# ============================================================================== +# service type private unpriv chroot wakeup maxproc command +# (yes) (yes) (yes) (never) (100) + args +# ============================================================================== +smtp inet n - n - - smtpd +#smtp inet n - n - 1 postscreen +#smtpd pass - - n - - smtpd +#dnsblog unix - - n - 0 dnsblog +#tlsproxy unix - - n - 0 tlsproxy +submission inet n - n - - smtpd + -o cleanup_service_name=cleanup_submission -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes @@ -23,119 +21,58 @@ submission inet n - n - - smtpd -o smtpd_recipient_restrictions=\$submission_recipient_restrictions -o smtpd_sender_restrictions=\$submission_sender_restrictions -#smtps inet n - n - - smtpd -# -o syslog_name=postfix/smtps -# -o smtpd_tls_wrappermode=yes -# -o smtpd_sasl_auth_enable=yes -# -o smtpd_client_restrictions=permit_sasl_authenticated,reject -# -o milter_macro_daemon_name=ORIGINATING -#628 inet n - n - - qmqpd -pickup fifo n - n 60 1 pickup -cleanup unix n - n - 0 cleanup -qmgr fifo n - n 300 1 qmgr -#qmgr fifo n - n 300 1 oqmgr -tlsmgr unix - - n 1000? 1 tlsmgr -rewrite unix - - n - - trivial-rewrite -bounce unix - - n - 0 bounce -defer unix - - n - 0 bounce -trace unix - - n - 0 bounce -verify unix - - n - 1 verify -flush unix n - n 1000? 0 flush -proxymap unix - - n - - proxymap -proxywrite unix - - n - 1 proxymap -smtp unix - - n - - smtp -relay unix - - n - - smtp -# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 -showq unix n - n - - showq -error unix - - n - - error -retry unix - - n - - error -discard unix - - n - - discard -local unix - n n - - local -virtual unix - n n - - virtual -lmtp unix - - n - - lmtp -anvil unix - - n - 1 anvil -scache unix - - n - 1 scache -# -# ==================================================================== -# Interfaces to non-Postfix software. Be sure to examine the manual -# pages of the non-Postfix software to find out what options it wants. -# -# Many of the following services use the Postfix pipe(8) delivery -# agent. See the pipe(8) man page for information about \${recipient} -# and other message envelope options. -# ==================================================================== -# -# maildrop. See the Postfix MAILDROP_README file for details. -# Also specify in main.cf: maildrop_destination_recipient_limit=1 -# -#maildrop unix - n n - - pipe -# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d \${recipient} -# -# ==================================================================== -# -# Recent Cyrus versions can use the existing "lmtp" master.cf entry. -# -# Specify in cyrus.conf: -# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4 -# -# Specify in main.cf one or more of the following: -# mailbox_transport = lmtp:inet:localhost -# virtual_transport = lmtp:inet:localhost -# -# ==================================================================== -# -# Cyrus 2.1.5 (Amos Gouaux) -# Also specify in main.cf: cyrus_destination_recipient_limit=1 -# -#cyrus unix - n n - - pipe -# user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r \${sender} -m \${extension} \${user} -# -# ==================================================================== -# -# Old example of delivery via Cyrus. -# -#old-cyrus unix - n n - - pipe -# flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m \${extension} \${user} -# -# ==================================================================== -# -# See the Postfix UUCP_README file for configuration details. -# -#uucp unix - n n - - pipe -# flags=Fqhu user=uucp argv=uux -r -n -z -a\$sender - \$nexthop!rmail (\$recipient) -# -# ==================================================================== -# -# Other external delivery methods. -# -#ifmail unix - n n - - pipe -# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r \$nexthop (\$recipient) -# -#bsmtp unix - n n - - pipe -# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f \$sender \$nexthop \$recipient -# -#scalemail-backend unix - n n - 2 pipe -# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store -# \${nexthop} \${user} \${extension} -# -#mailman unix - n n - - pipe -# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py -# \${nexthop} \${user} +#smtps inet n - n - - smtpd +# -o syslog_name=postfix/smtps +# -o smtpd_tls_wrappermode=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +# -o milter_macro_daemon_name=ORIGINATING +#628 inet n - n - - qmqpd +pickup fifo n - n 60 1 pickup +cleanup unix n - n - 0 cleanup + -o header_checks=regexp:/etc/postfix/header_checks.inbound + -o mime_header_checks=regexp:/etc/postfix/header_checks.inbound +cleanup_internal unix n - n - 0 cleanup + -o header_checks=regexp:/etc/postfix/header_checks.internal + -o mime_header_checks=regexp:/etc/postfix/header_checks.internal +cleanup_submission unix n - n - 0 cleanup + -o header_checks=regexp:/etc/postfix/header_checks.submission + -o mime_header_checks=regexp:/etc/postfix/header_checks.submission +qmgr fifo n - n 300 1 qmgr +#qmgr fifo n - n 300 1 oqmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +relay unix - - n - - smtp +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache -# # Filter email through Amavisd -# -smtp-amavis unix - - n - 3 smtp +smtp-amavis unix - - n - 3 smtp -o smtp_data_done_timeout=1800 -o disable_dns_lookups=yes -o smtp_send_xforward_command=yes -o max_use=20 + -o smtp_bind_address=127.0.0.1 -# # Listener to re-inject email from Amavisd into Postfix -# -127.0.0.1:10025 inet n - n - 100 smtpd - -o content_filter=smtp-wallace:[127.0.0.1]:10026 +127.0.0.1:10025 inet n - n - 100 smtpd + -o cleanup_service_name=cleanup_internal + -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= @@ -146,19 +83,16 @@ smtp-amavis unix - - n - 3 smtp -o mynetworks=127.0.0.0/8 -o smtpd_authorized_xforward_hosts=127.0.0.0/8 -# # Filter email through Wallace -# -smtp-wallace unix - - n - 3 smtp +smtp-wallace unix - - n - 3 smtp -o smtp_data_done_timeout=1800 -o disable_dns_lookups=yes -o smtp_send_xforward_command=yes -o max_use=20 -# # Listener to re-inject email from Wallace into Postfix -# -127.0.0.1:10027 inet n - n - 100 smtpd +127.0.0.1:10027 inet n - n - 100 smtpd + -o cleanup_service_name=cleanup_internal -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= @@ -170,18 +104,18 @@ smtp-wallace unix - - n - 3 smtp -o mynetworks=127.0.0.0/8 -o smtpd_authorized_xforward_hosts=127.0.0.0/8 -recipient_policy unix - n n - - spawn - user=kolab-n argv=${kolab_sap_executable_path} --verify-recipient +recipient_policy unix - n n - - spawn + user=kolab-n argv=/usr/libexec/postfix/kolab_smtp_access_policy --verify-recipient -recipient_policy_incoming unix - n n - - spawn - user=kolab-n argv=${kolab_sap_executable_path} --verify-recipient --allow-unauthenticated +recipient_policy_incoming unix - n n - - spawn + user=kolab-n argv=/usr/libexec/postfix/kolab_smtp_access_policy --verify-recipient --allow-unauthenticated -sender_policy unix - n n - - spawn - user=kolab-n argv=${kolab_sap_executable_path} --verify-sender +sender_policy unix - n n - - spawn + user=kolab-n argv=/usr/libexec/postfix/kolab_smtp_access_policy --verify-sender -sender_policy_incoming unix - n n - - spawn - user=kolab-n argv=${kolab_sap_executable_path} --verify-sender --allow-unauthenticated +sender_policy_incoming unix - n n - - spawn + user=kolab-n argv=/usr/libexec/postfix/kolab_smtp_access_policy --verify-sender --allow-unauthenticated -submission_policy unix - n n - - spawn - user=kolab-n argv=${kolab_sap_executable_path} --verify-sender --verify-recipient +submission_policy unix - n n - - spawn + user=kolab-n argv=/usr/libexec/postfix/kolab_smtp_access_policy --verify-sender --verify-recipient diff --git a/share/templates/roundcubemail/acl.inc.php.tpl b/share/templates/roundcubemail/acl.inc.php.tpl index f4b651c..ca1bae5 100644 --- a/share/templates/roundcubemail/acl.inc.php.tpl +++ b/share/templates/roundcubemail/acl.inc.php.tpl @@ -1,11 +1,11 @@ <?php - \$rcmail_config['acl_advanced_mode'] = false; - \$rcmail_config['acl_users_source'] = 'kolab_addressbook'; - \$rcmail_config['acl_users_field'] = 'mail'; - \$rcmail_config['acl_users_filter'] = 'objectClass=kolabInetOrgPerson'; + \$config['acl_advanced_mode'] = false; + \$config['acl_users_source'] = 'kolab_addressbook'; + \$config['acl_users_field'] = 'mail'; + \$config['acl_users_filter'] = 'objectClass=kolabInetOrgPerson'; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/calendar.inc.php.tpl b/share/templates/roundcubemail/calendar.inc.php.tpl index a7cf6d2..b119f7b 100644 --- a/share/templates/roundcubemail/calendar.inc.php.tpl +++ b/share/templates/roundcubemail/calendar.inc.php.tpl @@ -1,15 +1,15 @@ <?php - \$rcmail_config['calendar_driver'] = "kolab"; - \$rcmail_config['calendar_default_view'] = "agendaWeek"; - \$rcmail_config['calendar_timeslots'] = 2; - \$rcmail_config['calendar_first_day'] = 1; - \$rcmail_config['calendar_first_hour'] = 6; - \$rcmail_config['calendar_work_start'] = 6; - \$rcmail_config['calendar_work_end'] = 18; - \$rcmail_config['calendar_event_coloring'] = 0; + \$config['calendar_driver'] = "kolab"; + \$config['calendar_default_view'] = "agendaWeek"; + \$config['calendar_timeslots'] = 2; + \$config['calendar_first_day'] = 1; + \$config['calendar_first_hour'] = 6; + \$config['calendar_work_start'] = 6; + \$config['calendar_work_end'] = 18; + \$config['calendar_event_coloring'] = 0; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/config.inc.php.tpl b/share/templates/roundcubemail/config.inc.php.tpl new file mode 100644 index 0000000..4ec3469 --- /dev/null +++ b/share/templates/roundcubemail/config.inc.php.tpl @@ -0,0 +1,208 @@ +<?php + \$config = array(); + + \$config['db_dsnw'] = '$mysql_uri'; + + \$config['session_domain'] = ''; + \$config['des_key'] = "$des_key"; + \$config['username_domain'] = '$primary_domain'; + + \$config['mail_domain'] = ''; + + // IMAP Server Settings + \$config['default_host'] = 'tls://localhost'; + \$config['default_port'] = 143; + \$config['imap_delimiter'] = '/'; + \$config['imap_force_lsub'] = true; + + // Caching and storage settings + \$config['imap_cache'] = 'db'; + \$config['imap_cache_ttl'] = '10d'; + \$config['messages_cache'] = 'db'; + \$config['message_cache_ttl'] = '10d'; + \$config['session_storage'] = 'db'; + + // SMTP Server Settings + \$config['smtp_server'] = 'tls://localhost'; + \$config['smtp_port'] = 587; + \$config['smtp_user'] = '%u'; + \$config['smtp_pass'] = '%p'; + \$config['smtp_helo_host'] = \$_SERVER["HTTP_HOST"]; + + // LDAP Settings + \$config['ldap_cache'] = 'db'; + \$config['ldap_cache_ttl'] = '1h'; + + // Kolab specific defaults + \$config['product_name'] = 'Kolab Groupware'; + \$config['skin_logo'] = 'skins/kolab/images/kolab_logo.png'; + \$config['quota_zero_as_unlimited'] = false; + \$config['login_lc'] = 2; + \$config['auto_create_user'] = true; + \$config['enable_installer'] = false; + // The SMTP server does not allow empty identities + \$config['mdn_use_from'] = true; + + // Plugins + \$config['plugins'] = array( + 'acl', + 'archive', + 'calendar', + 'jqueryui', + 'kolab_activesync', + 'kolab_addressbook', + 'kolab_auth', + 'kolab_config', + 'kolab_folders', + 'libkolab', + 'libcalendaring', + 'managesieve', + 'newmail_notifier', + 'odfviewer', + 'password', + 'redundant_attachments', + 'tasklist', + 'threading_as_default', + // contextmenu must be after kolab_addressbook (#444) + 'contextmenu', + ); + + + // Do not show deleted messages, mark deleted messages as read, + // and flag them as deleted instead of moving them to the Trash + // folder. + \$config['skip_deleted'] = true; + \$config['read_when_deleted'] = true; + \$config['flag_for_deletion'] = true; + \$config['delete_always'] = true; + + \$config['session_lifetime'] = 180; + \$config['password_charset'] = 'UTF-8'; + \$config['useragent'] = 'Kolab 3.1/Roundcube ' . RCUBE_VERSION; + \$config['dont_override'] = Array('skin'); + + \$config['message_sort_col'] = 'date'; + + \$config['spellcheck_dictionary'] = true; + \$config['spellcheck_ignore_caps'] = true; + \$config['spellcheck_ignore_nums'] = true; + \$config['spellcheck_ignore_syms'] = true; + + \$config['undo_timeout'] = 10; + \$config['upload_progress'] = 2; + \$config['address_template'] = '{street}<br/>{locality} {zipcode}<br/>{country} {region}'; + \$config['preview_pane'] = true; + \$config['preview_pane_mark_read'] = 0; + + \$config['autoexpand_threads'] = 2; + \$config['top_posting'] = 0; + \$config['sig_above'] = false; + \$config['mdn_requests'] = 0; + \$config['mdn_default'] = false; + \$config['dsn_default'] = false; + \$config['reply_same_folder'] = false; + + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + } + + // Re-apply mandatory settings here. + + \$config['debug_level'] = 0; + \$config['devel_mode'] = false; + \$config['log_driver'] = 'file'; + \$config['log_date_format'] = 'd-M-Y H:i:s,u O'; + \$config['syslog_id'] = 'roundcube'; + \$config['syslog_facility'] = LOG_USER; + \$config['smtp_log'] = true; + \$config['log_logins'] = true; + \$config['log_session'] = true; + \$config['sql_debug'] = true; + \$config['memcache_debug'] = true; + \$config['imap_debug'] = true; + \$config['ldap_debug'] = true; + \$config['smtp_debug'] = true; + + \$config['skin'] = 'larry'; + \$config['skin_include_php'] = false; + \$config['mime_magic'] = null; + \$config['im_identify_path'] = '/usr/bin/identify'; + \$config['im_convert_path'] = '/usr/bin/convert'; + \$config['log_dir'] = 'logs/'; + \$config['temp_dir'] = '/var/lib/roundcubemail/'; + + // Some additional default folders (archive plugin) + \$config['archive_mbox'] = 'Archive'; + // The Kolab daemon by default creates 'Spam' + \$config['junk_mbox'] = 'Spam'; + \$config['default_folders'] = array('INBOX', 'Drafts', 'Sent', 'Spam', 'Trash', 'Archive'); + + \$config['address_book_type'] = 'ldap'; + \$config['autocomplete_min_length'] = 3; + \$config['autocomplete_threads'] = 0; + \$config['autocomplete_max'] = 15; + \$config['ldap_public'] = array( + 'kolab_addressbook' => array( + 'name' => 'Global Address Book', + 'hosts' => Array('localhost'), + 'port' => 389, + 'use_tls' => false, + 'base_dn' => '$ldap_user_base_dn', + 'user_specific' => true, + 'bind_dn' => '%dn', + 'bind_pass' => '', + 'search_base_dn' => '$ldap_user_base_dn', + 'search_bind_dn' => '$ldap_service_bind_dn', + 'search_bind_pw' => '$ldap_service_bind_pw', + 'search_filter' => '(&(objectClass=inetOrgPerson)(mail=%fu))', + 'writable' => false, + 'LDAP_Object_Classes' => array("top", "inetOrgPerson"), + 'required_fields' => array("cn", "sn", "mail"), + 'LDAP_rdn' => 'uid', + 'ldap_version' => 3, // using LDAPv3 + 'search_fields' => array('displayname', 'mail'), + 'sort' => array('displayname', 'sn', 'givenname', 'cn'), + 'scope' => 'sub', + 'filter' => '(objectClass=inetOrgPerson)', + 'vlv' => false, + 'vlv_search' => false, + 'fuzzy_search' => true, + 'sizelimit' => '0', + 'timelimit' => '0', + 'fieldmap' => Array( + // Roundcube => LDAP + 'name' => 'displayName', + 'surname' => 'sn', + 'firstname' => 'givenName', + 'middlename' => 'initials', + 'prefix' => 'title', + 'email:primary' => 'mail', + 'email:alias' => 'alias', + 'email:personal' => 'mailalternateaddress', + 'phone:main' => 'telephoneNumber', + 'phone:work' => 'alternateTelephoneNumber', + 'phone:mobile' => 'mobile', + 'phone:work2' => 'blackberry', + 'jobtitle' => 'title', + 'manager' => 'manager', + 'assistant' => 'secretary', + 'photo' => 'jpegphoto' + ), + 'groups' => Array( + 'base_dn' => '$ldap_group_base_dn', + 'filter' => '(&' . '$ldap_group_filter' . '(mail=*))', + 'object_classes' => Array("top", "groupOfUniqueNames"), + 'member_attr' => 'uniqueMember', + ), + ), + ); + + \$config['autocomplete_addressbooks'] = Array( + 'kolab_addressbook' + ); + + \$config['autocomplete_single'] = true; + + \$config['htmleditor'] = 0; + +?> diff --git a/share/templates/roundcubemail/db.inc.php.tpl b/share/templates/roundcubemail/db.inc.php.tpl deleted file mode 100644 index 1257f18..0000000 --- a/share/templates/roundcubemail/db.inc.php.tpl +++ /dev/null @@ -1,29 +0,0 @@ -<?php - - \$rcmail_config = array(); - - \$rcmail_config['db_dsnw'] = '$mysql_uri'; - - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); - } - - \$rcmail_config['db_max_length'] = 512000; - \$rcmail_config['db_persistent'] = TRUE; - \$rcmail_config['db_table_users'] = 'users'; - \$rcmail_config['db_table_identities'] = 'identities'; - \$rcmail_config['db_table_contacts'] = 'contacts'; - \$rcmail_config['db_table_contactgroups'] = 'contactgroups'; - \$rcmail_config['db_table_contactgroupmembers'] = 'contactgroupmembers'; - \$rcmail_config['db_table_session'] = 'session'; - \$rcmail_config['db_table_cache'] = 'cache'; - \$rcmail_config['db_table_messages'] = 'messages'; - \$rcmail_config['db_sequence_users'] = 'user_ids'; - \$rcmail_config['db_sequence_identities'] = 'identity_ids'; - \$rcmail_config['db_sequence_contacts'] = 'contact_ids'; - \$rcmail_config['db_sequence_contactgroups'] = 'contactgroups_ids'; - \$rcmail_config['db_sequence_cache'] = 'cache_ids'; - \$rcmail_config['db_sequence_messages'] = 'message_ids'; - - -?> diff --git a/share/templates/roundcubemail/kolab.inc.php.tpl b/share/templates/roundcubemail/kolab.inc.php.tpl index aa9fd0b..05711ef 100644 --- a/share/templates/roundcubemail/kolab.inc.php.tpl +++ b/share/templates/roundcubemail/kolab.inc.php.tpl @@ -1,15 +1,15 @@ <?php - \$rcmail_config['kolab_freebusy_server'] = 'http://' . \$_SERVER["HTTP_HOST"] . '/freebusy'; + \$config['kolab_freebusy_server'] = 'http://' . \$_SERVER["HTTP_HOST"] . '/freebusy'; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } - \$rcmail_config['kolab_cache'] = true; + \$config['kolab_cache'] = true; - \$rcmail_config['kolab_ssl_verify_peer'] = false; + \$config['kolab_ssl_verify_peer'] = false; - \$rcmail_config['kolab_use_subscriptions'] = true; + \$config['kolab_use_subscriptions'] = true; ?> diff --git a/share/templates/roundcubemail/kolab_auth.inc.php.tpl b/share/templates/roundcubemail/kolab_auth.inc.php.tpl index 6044232..8fad27a 100644 --- a/share/templates/roundcubemail/kolab_auth.inc.php.tpl +++ b/share/templates/roundcubemail/kolab_auth.inc.php.tpl @@ -2,7 +2,7 @@ // The id of the LDAP address book (which refers to the rcmail_config['ldap_public']) // or complete addressbook definition array. - \$rcmail_config['kolab_auth_addressbook'] = Array( + \$config['kolab_auth_addressbook'] = Array( 'name' => 'Kolab Auth', 'hosts' => Array('localhost'), 'port' => 389, @@ -35,36 +35,36 @@ // This will overwrite defined filter - \$rcmail_config['kolab_auth_filter'] = '(&' . '$ldap_user_filter' . '(|(uid=%u)(mail=%fu)(alias=%fu)))'; + \$config['kolab_auth_filter'] = '(&' . '$ldap_user_filter' . '(|(uid=%u)(mail=%fu)(alias=%fu)))'; // Use this fields (from fieldmap configuration) to get authentication ID - \$rcmail_config['kolab_auth_login'] = 'email'; + \$config['kolab_auth_login'] = 'email'; // Use this fields (from fieldmap configuration) for default identity - \$rcmail_config['kolab_auth_name'] = 'name'; - \$rcmail_config['kolab_auth_alias'] = 'alias'; - \$rcmail_config['kolab_auth_email'] = 'email'; + \$config['kolab_auth_name'] = 'name'; + \$config['kolab_auth_alias'] = 'alias'; + \$config['kolab_auth_email'] = 'email'; if (preg_match('/\/helpdesk-login\//', \$_SERVER["REQUEST_URI"]) ) { // Login and password of the admin user. Enables "Login As" feature. - \$rcmail_config['kolab_auth_admin_login'] = '$imap_admin_login'; - \$rcmail_config['kolab_auth_admin_password'] = '$imap_admin_password'; + \$config['kolab_auth_admin_login'] = '$imap_admin_login'; + \$config['kolab_auth_admin_password'] = '$imap_admin_password'; - \$rcmail_config['kolab_auth_auditlog'] = true; + \$config['kolab_auth_auditlog'] = true; } // Administrative role field (from fieldmap configuration) which must be filled with // specified value which adds privilege to login as another user. - \$rcmail_config['kolab_auth_role'] = 'role'; - \$rcmail_config['kolab_auth_role_value'] = 'cn=kolab-admin,$ldap_base_dn'; + \$config['kolab_auth_role'] = 'role'; + \$config['kolab_auth_role_value'] = 'cn=kolab-admin,$ldap_base_dn'; // Administrative group name to which user must be assigned to // which adds privilege to login as another user. - \$rcmail_config['kolab_auth_group'] = 'Kolab Helpdesk'; + \$config['kolab_auth_group'] = 'Kolab Helpdesk'; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/kolab_files.inc.php.tpl b/share/templates/roundcubemail/kolab_files.inc.php.tpl new file mode 100644 index 0000000..1c5fced --- /dev/null +++ b/share/templates/roundcubemail/kolab_files.inc.php.tpl @@ -0,0 +1,15 @@ +<?php + +// URL of kolab-chwala installation +\$config['kolab_files_url'] = 'http://' . \$_SERVER['HTTP_HOST'] . '/chwala/'; + +// List of files list columns. Available are: name, size, mtime, type +\$config['kolab_files_list_cols'] = array('name', 'mtime', 'size'); + +// Name of the column to sort files list by +\$config['kolab_files_sort_col'] = 'name'; + +// Order of the files list sort +\$config['kolab_files_sort_order'] = 'asc'; + +?> diff --git a/share/templates/roundcubemail/kolab_folders.inc.php.tpl b/share/templates/roundcubemail/kolab_folders.inc.php.tpl index 107ccec..93f6eec 100644 --- a/share/templates/roundcubemail/kolab_folders.inc.php.tpl +++ b/share/templates/roundcubemail/kolab_folders.inc.php.tpl @@ -1,19 +1,19 @@ <?php - \$rcmail_config['kolab_folders_configuration_default'] = 'Configuration'; - \$rcmail_config['kolab_folders_event_default'] = 'Calendar'; - \$rcmail_config['kolab_folders_contact_default'] = 'Contacts'; - \$rcmail_config['kolab_folders_task_default'] = ''; - \$rcmail_config['kolab_folders_note_default'] = ''; - \$rcmail_config['kolab_folders_journal_default'] = ''; - \$rcmail_config['kolab_folders_mail_inbox'] = 'INBOX'; - \$rcmail_config['kolab_folders_mail_drafts'] = 'Drafts'; - \$rcmail_config['kolab_folders_mail_sentitems'] = 'Sent'; - \$rcmail_config['kolab_folders_mail_junkemail'] = 'Spam'; - \$rcmail_config['kolab_folders_mail_outbox'] = ''; - \$rcmail_config['kolab_folders_mail_wastebasket'] = 'Trash'; + \$config['kolab_folders_configuration_default'] = 'Configuration'; + \$config['kolab_folders_event_default'] = 'Calendar'; + \$config['kolab_folders_contact_default'] = 'Contacts'; + \$config['kolab_folders_task_default'] = ''; + \$config['kolab_folders_note_default'] = ''; + \$config['kolab_folders_journal_default'] = ''; + \$config['kolab_folders_mail_inbox'] = 'INBOX'; + \$config['kolab_folders_mail_drafts'] = 'Drafts'; + \$config['kolab_folders_mail_sentitems'] = 'Sent'; + \$config['kolab_folders_mail_junkemail'] = 'Spam'; + \$config['kolab_folders_mail_outbox'] = ''; + \$config['kolab_folders_mail_wastebasket'] = 'Trash'; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/main.inc.php.tpl b/share/templates/roundcubemail/main.inc.php.tpl deleted file mode 100644 index 9e4d14f..0000000 --- a/share/templates/roundcubemail/main.inc.php.tpl +++ /dev/null @@ -1,265 +0,0 @@ -<?php - \$rcmail_config = array(); - - \$rcmail_config['imap_cache'] = 'db'; - \$rcmail_config['messages_cache'] = 'db'; - \$rcmail_config['force_https'] = false; - \$rcmail_config['use_https'] = false; - \$rcmail_config['login_autocomplete'] = 0; - \$rcmail_config['session_lifetime'] = 180; - \$rcmail_config['ip_check'] = false; - \$rcmail_config['referer_check'] = false; - \$rcmail_config['password_charset'] = 'ISO-8859-1'; - \$rcmail_config['sendmail_delay'] = 0; - \$rcmail_config['max_recipients'] = 0; - \$rcmail_config['max_group_members'] = 0; - \$rcmail_config['useragent'] = 'Roundcube Webmail/'.RCMAIL_VERSION; - \$rcmail_config['include_host_config'] = false; - \$rcmail_config['generic_message_footer'] = ''; - \$rcmail_config['generic_message_footer_html'] = ''; - \$rcmail_config['http_received_header'] = true; - \$rcmail_config['http_received_header_encrypt'] = true; - \$rcmail_config['mail_header_delimiter'] = NULL; - \$rcmail_config['line_length'] = 72; - \$rcmail_config['send_format_flowed'] = true; - \$rcmail_config['dont_override'] = Array('skin'); - \$rcmail_config['identities_level'] = 0; - \$rcmail_config['contact_photo_size'] = 160; - \$rcmail_config['email_dns_check'] = false; - - \$rcmail_config['message_sort_col'] = ''; - \$rcmail_config['message_sort_order'] = 'DESC'; - \$rcmail_config['list_cols'] = array('subject', 'status', 'from', 'date', 'size', 'flag', 'attachment'); - \$rcmail_config['language'] = null; - \$rcmail_config['date_short'] = 'D H:i'; - \$rcmail_config['date_long'] = 'd.m.Y H:i'; - \$rcmail_config['date_today'] = 'H:i'; - \$rcmail_config['date_format'] = 'Y-m-d'; - \$rcmail_config['quota_zero_as_unlimited'] = false; - \$rcmail_config['enable_spellcheck'] = true; - \$rcmail_config['spellcheck_dictionary'] = true; - \$rcmail_config['spellcheck_engine'] = 'googie'; - \$rcmail_config['spellcheck_uri'] = ''; - \$rcmail_config['spellcheck_languages'] = NULL; - \$rcmail_config['spellcheck_ignore_caps'] = true; - \$rcmail_config['spellcheck_ignore_nums'] = true; - \$rcmail_config['spellcheck_ignore_syms'] = true; - \$rcmail_config['max_pagesize'] = 200; - \$rcmail_config['min_keep_alive'] = 60; - \$rcmail_config['undo_timeout'] = 10; - \$rcmail_config['upload_progress'] = 2; - \$rcmail_config['address_book_type'] = 'ldap'; - \$rcmail_config['autocomplete_min_length'] = 3; - \$rcmail_config['autocomplete_threads'] = 0; - \$rcmail_config['autocomplete_max'] = 15; - \$rcmail_config['address_template'] = '{street}<br/>{locality} {zipcode}<br/>{country} {region}'; - \$rcmail_config['default_charset'] = 'ISO-8859-1'; - \$rcmail_config['pagesize'] = 40; - \$rcmail_config['timezone'] = 'auto'; - \$rcmail_config['dst_active'] = (bool)date('I'); - \$rcmail_config['prefer_html'] = true; - \$rcmail_config['show_images'] = 0; - \$rcmail_config['prettydate'] = true; - \$rcmail_config['draft_autosave'] = 300; - \$rcmail_config['preview_pane'] = true; - \$rcmail_config['preview_pane_mark_read'] = 0; - \$rcmail_config['logout_purge'] = false; - \$rcmail_config['logout_expunge'] = false; - \$rcmail_config['inline_images'] = true; - \$rcmail_config['mime_param_folding'] = 1; - \$rcmail_config['skip_deleted'] = true; - \$rcmail_config['read_when_deleted'] = true; - \$rcmail_config['flag_for_deletion'] = true; - \$rcmail_config['keep_alive'] = 300; - \$rcmail_config['check_all_folders'] = false; - \$rcmail_config['display_next'] = true; - \$rcmail_config['autoexpand_threads'] = 2; - \$rcmail_config['top_posting'] = false; - \$rcmail_config['strip_existing_sig'] = true; - \$rcmail_config['show_sig'] = 1; - \$rcmail_config['sig_above'] = false; - \$rcmail_config['force_7bit'] = false; - \$rcmail_config['search_mods'] = null; - \$rcmail_config['delete_always'] = true; - \$rcmail_config['mdn_requests'] = 0; - \$rcmail_config['mdn_default'] = false; - \$rcmail_config['dsn_default'] = false; - \$rcmail_config['reply_same_folder'] = false; - - \$rcmail_config['plugins'] = array( - 'acl', - 'archive', - 'calendar', - 'http_authentication', - 'jqueryui', - 'kolab_activesync', - 'kolab_addressbook', - 'kolab_auth', - 'kolab_config', - 'kolab_folders', - 'libkolab', - 'libcalendaring', - 'listcommands', - 'managesieve', - 'newmail_notifier', - 'odfviewer', - 'password', - 'redundant_attachments', - 'tasklist', - 'threading_as_default', - // contextmenu must be after kolab_addressbook (#444) - 'contextmenu', - ); - - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); - } - - // Re-apply mandatory settings here. - - \$rcmail_config['debug_level'] = 0; - \$rcmail_config['devel_mode'] = false; - \$rcmail_config['log_driver'] = 'file'; - \$rcmail_config['log_date_format'] = 'd-M-Y H:i:s,u O'; - \$rcmail_config['syslog_id'] = 'roundcube'; - \$rcmail_config['syslog_facility'] = LOG_USER; - \$rcmail_config['smtp_log'] = true; - \$rcmail_config['log_logins'] = true; - \$rcmail_config['log_session'] = true; - \$rcmail_config['sql_debug'] = true; - \$rcmail_config['memcache_debug'] = true; - \$rcmail_config['imap_debug'] = true; - \$rcmail_config['ldap_debug'] = true; - \$rcmail_config['smtp_debug'] = true; - - \$rcmail_config['product_name'] = 'Kolab Groupware'; - \$rcmail_config['skin'] = 'larry'; - \$rcmail_config['skin_logo'] = 'skins/kolab/images/kolab_logo.png'; - \$rcmail_config['skin_include_php'] = false; - \$rcmail_config['mime_magic'] = '/usr/share/misc/magic'; - \$rcmail_config['im_identify_path'] = '/usr/bin/identify'; - \$rcmail_config['im_convert_path'] = '/usr/bin/convert'; - \$rcmail_config['login_lc'] = true; - \$rcmail_config['auto_create_user'] = true; - \$rcmail_config['enable_installer'] = false; - \$rcmail_config['session_storage'] = 'db'; - \$rcmail_config['default_port'] = 143; - \$rcmail_config['imap_auth_type'] = ''; - \$rcmail_config['imap_delimiter'] = '/'; - \$rcmail_config['imap_ns_personal'] = null; - \$rcmail_config['imap_ns_other'] = null; - \$rcmail_config['imap_ns_shared'] = null; - \$rcmail_config['imap_force_caps'] = false; - \$rcmail_config['imap_force_lsub'] = true; - \$rcmail_config['imap_timeout'] = 0; - \$rcmail_config['imap_auth_cid'] = null; - \$rcmail_config['imap_auth_pw'] = null; - \$rcmail_config['smtp_port'] = 587; - \$rcmail_config['smtp_user'] = '%u'; - \$rcmail_config['smtp_pass'] = '%p'; - \$rcmail_config['smtp_auth_type'] = ''; - \$rcmail_config['smtp_auth_cid'] = null; - \$rcmail_config['smtp_auth_pw'] = null; - \$rcmail_config['smtp_helo_host'] = \$_SERVER["HTTP_HOST"]; - \$rcmail_config['smtp_timeout'] = 0; - \$rcmail_config['log_dir'] = '/var/log/roundcubemail/'; - \$rcmail_config['temp_dir'] = '\${_tmppath}'; - \$rcmail_config['message_cache_lifetime'] = '10d'; - - \$rcmail_config['archive_mbox'] = 'Archive'; - \$rcmail_config['drafts_mbox'] = 'Drafts'; - \$rcmail_config['junk_mbox'] = 'Spam'; - \$rcmail_config['sent_mbox'] = 'Sent'; - \$rcmail_config['trash_mbox'] = 'Trash'; - \$rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Spam', 'Trash'); - \$rcmail_config['create_default_folders'] = true; - \$rcmail_config['protect_default_folders'] = true; - - \$mandatory_plugins = Array( - 'calendar', - 'kolab_addressbook', - 'kolab_auth', - 'kolab_config', - 'kolab_folders', - 'libkolab', - 'password', - ); - - foreach ( \$mandatory_plugins as \$num => \$plugin ) { - if (!in_array(\$plugin, \$rcmail_config['plugins'])) { - \$rcmail_config['plugins'][] = \$plugin; - } - } - - \$rcmail_config['default_host'] = 'tls://localhost'; - \$rcmail_config['smtp_server'] = 'tls://localhost'; - \$rcmail_config['session_domain'] = ''; - \$rcmail_config['des_key'] = "$des_key"; - \$rcmail_config['username_domain'] = '$primary_domain'; - - \$rcmail_config['mail_domain'] = ''; - - \$rcmail_config['ldap_public'] = array( - 'kolab_addressbook' => array( - 'name' => 'Global Address Book', - 'hosts' => Array('localhost'), - 'port' => 389, - 'use_tls' => false, - 'base_dn' => '$ldap_user_base_dn', - 'user_specific' => true, - 'bind_dn' => '%dn', - 'bind_pass' => '', - 'search_base_dn' => '$ldap_user_base_dn', - 'search_bind_dn' => '$ldap_service_bind_dn', - 'search_bind_pw' => '$ldap_service_bind_pw', - 'search_filter' => '(&(objectClass=inetOrgPerson)(mail=%fu))', - 'writable' => false, - 'LDAP_Object_Classes' => array("top", "inetOrgPerson"), - 'required_fields' => array("cn", "sn", "mail"), - 'LDAP_rdn' => 'uid', - 'ldap_version' => 3, // using LDAPv3 - 'search_fields' => array('displayname', 'mail'), - 'sort' => array('displayname', 'sn', 'givenname', 'cn'), - 'scope' => 'sub', - 'filter' => '(objectClass=inetOrgPerson)', - 'vlv' => false, - 'vlv_search' => false, - 'fuzzy_search' => true, - 'sizelimit' => '0', - 'timelimit' => '0', - 'fieldmap' => Array( - // Roundcube => LDAP - 'name' => 'displayName', - 'surname' => 'sn', - 'firstname' => 'givenName', - 'middlename' => 'initials', - 'prefix' => 'title', - 'email:primary' => 'mail', - 'email:alias' => 'alias', - 'email:personal' => 'mailalternateaddress', - 'phone:main' => 'telephoneNumber', - 'phone:work' => 'alternateTelephoneNumber', - 'phone:mobile' => 'mobile', - 'phone:work2' => 'blackberry', - 'jobtitle' => 'title', - 'manager' => 'manager', - 'assistant' => 'secretary', - 'photo' => 'jpegphoto' - ), - 'groups' => Array( - 'base_dn' => '$ldap_group_base_dn', - 'filter' => '(&' . '$ldap_group_filter' . '(mail=*))', - 'object_classes' => Array("top", "groupOfUniqueNames"), - 'member_attr' => 'uniqueMember', - ), - ), - ); - - \$rcmail_config['autocomplete_addressbooks'] = Array( - 'kolab_addressbook' - ); - - \$rcmail_config['autocomplete_single'] = true; - - \$rcmail_config['htmleditor'] = 0; -?> diff --git a/share/templates/roundcubemail/managesieve.inc.php.tpl b/share/templates/roundcubemail/managesieve.inc.php.tpl index 8ab9998..c05a096 100644 --- a/share/templates/roundcubemail/managesieve.inc.php.tpl +++ b/share/templates/roundcubemail/managesieve.inc.php.tpl @@ -1,18 +1,18 @@ <?php - \$rcmail_config['managesieve_port'] = 4190; - \$rcmail_config['managesieve_host'] = '%h'; - \$rcmail_config['managesieve_auth_type'] = 'PLAIN'; - \$rcmail_config['managesieve_auth_cid'] = null; - \$rcmail_config['managesieve_auth_pw'] = null; - \$rcmail_config['managesieve_usetls'] = true; - \$rcmail_config['managesieve_default'] = '/etc/dovecot/sieve/global'; - \$rcmail_config['managesieve_mbox_encoding'] = 'UTF-8'; - \$rcmail_config['managesieve_replace_delimiter'] = ''; - \$rcmail_config['managesieve_disabled_extensions'] = array(); - \$rcmail_config['managesieve_debug'] = true; + \$config['managesieve_port'] = 4190; + \$config['managesieve_host'] = '%h'; + \$config['managesieve_auth_type'] = 'PLAIN'; + \$config['managesieve_auth_cid'] = null; + \$config['managesieve_auth_pw'] = null; + \$config['managesieve_usetls'] = true; + \$config['managesieve_default'] = '/etc/dovecot/sieve/global'; + \$config['managesieve_mbox_encoding'] = 'UTF-8'; + \$config['managesieve_replace_delimiter'] = ''; + \$config['managesieve_disabled_extensions'] = array(); + \$config['managesieve_debug'] = true; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/owncloud.inc.php.tpl b/share/templates/roundcubemail/owncloud.inc.php.tpl index 089849a..513a482 100644 --- a/share/templates/roundcubemail/owncloud.inc.php.tpl +++ b/share/templates/roundcubemail/owncloud.inc.php.tpl @@ -1,9 +1,9 @@ <?php // ownCloud URL - \$rcmail_config['owncloud_url'] = 'http://' . \$_SERVER["HTTP_HOST"] . '/owncloud'; + \$config['owncloud_url'] = 'http://' . \$_SERVER["HTTP_HOST"] . '/owncloud'; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/password.inc.php.tpl b/share/templates/roundcubemail/password.inc.php.tpl index 7aa45db..ca3f815 100644 --- a/share/templates/roundcubemail/password.inc.php.tpl +++ b/share/templates/roundcubemail/password.inc.php.tpl @@ -4,22 +4,22 @@ // ----------------------- // A driver to use for password change. Default: "sql". // See README file for list of supported driver names. - \$rcmail_config['password_driver'] = 'ldap'; + \$config['password_driver'] = 'ldap'; // Determine whether current password is required to change password. // Default: false. - \$rcmail_config['password_confirm_current'] = true; + \$config['password_confirm_current'] = true; // Require the new password to be a certain length. // set to blank to allow passwords of any length - \$rcmail_config['password_minimum_length'] = 6; + \$config['password_minimum_length'] = 6; // Require the new password to contain a letter and punctuation character // Change to false to remove this check. - \$rcmail_config['password_require_nonalpha'] = false; + \$config['password_require_nonalpha'] = false; // Enables logging of password changes into logs/password - \$rcmail_config['password_log'] = true; + \$config['password_log'] = true; // LDAP and LDAP_SIMPLE Driver options @@ -28,41 +28,41 @@ // You can provide one or several hosts in an array in which case the hosts are tried from left to right. // Exemple: array('ldap1.exemple.com', 'ldap2.exemple.com'); // Default: 'localhost' - \$rcmail_config['password_ldap_host'] = 'localhost'; + \$config['password_ldap_host'] = 'localhost'; // LDAP server port to connect to // Default: '389' - \$rcmail_config['password_ldap_port'] = '389'; + \$config['password_ldap_port'] = '389'; // TLS is started after connecting // Using TLS for password modification is recommended. // Default: false - \$rcmail_config['password_ldap_starttls'] = false; + \$config['password_ldap_starttls'] = false; // LDAP version // Default: '3' - \$rcmail_config['password_ldap_version'] = '3'; + \$config['password_ldap_version'] = '3'; // LDAP base name (root directory) // Exemple: 'dc=exemple,dc=com' - \$rcmail_config['password_ldap_basedn'] = '$ldap_user_base_dn'; + \$config['password_ldap_basedn'] = '$ldap_user_base_dn'; // LDAP connection method // There is two connection method for changing a user's LDAP password. // 'user': use user credential (recommanded, require password_confirm_current=true) // 'admin': use admin credential (this mode require password_ldap_adminDN and password_ldap_adminPW) // Default: 'user' - \$rcmail_config['password_ldap_method'] = 'user'; + \$config['password_ldap_method'] = 'user'; // LDAP Admin DN // Used only in admin connection mode // Default: null - \$rcmail_config['password_ldap_adminDN'] = null; + \$config['password_ldap_adminDN'] = null; // LDAP Admin Password // Used only in admin connection mode // Default: null - \$rcmail_config['password_ldap_adminPW'] = null; + \$config['password_ldap_adminPW'] = null; // LDAP user DN mask // The user's DN is mandatory and as we only have his login, @@ -72,7 +72,7 @@ // '%domain' will be replaced by the current roundcube user's domain part // '%dc' will be replaced by domain name hierarchal string e.g. "dc=test,dc=domain,dc=com" // Exemple: 'uid=%login,ou=people,dc=exemple,dc=com' - // \$rcmail_config['password_ldap_userDN_mask'] = 'uid=%login,ou=people,dc=exemple,dc=com'; + // \$config['password_ldap_userDN_mask'] = 'uid=%login,ou=people,dc=exemple,dc=com'; // LDAP search DN // The DN roundcube should bind with to find out user's DN @@ -83,7 +83,7 @@ // users login to find his DN instead. A common reason might be that // your users are placed under different ou's like engineering or // sales which cannot be derived from their login only. - \$rcmail_config['password_ldap_searchDN'] = '$ldap_service_bind_dn'; + \$config['password_ldap_searchDN'] = '$ldap_service_bind_dn'; // LDAP search password // If password_ldap_searchDN is set, the password to use for @@ -93,13 +93,13 @@ // is only accesible to roundcube and don't forget to restrict roundcube's access to // your directory as much as possible using ACLs. Should this password be compromised // you want to minimize the damage. - \$rcmail_config['password_ldap_searchPW'] = '$ldap_service_bind_pw'; + \$config['password_ldap_searchPW'] = '$ldap_service_bind_pw'; // LDAP search base // If password_ldap_searchDN is set, the base to search in using the filter below. // Note that you should comment out the default password_ldap_userDN_mask setting // for this to take effect. - \$rcmail_config['password_ldap_search_base'] = '$ldap_user_base_dn'; + \$config['password_ldap_search_base'] = '$ldap_user_base_dn'; // LDAP search filter // If password_ldap_searchDN is set, the filter to use when @@ -111,7 +111,7 @@ // '%dc' will be replaced by domain name hierarchal string e.g. "dc=test,dc=domain,dc=com" // Example: '(uid=%login)' // Example: '(&(objectClass=posixAccount)(uid=%login))' - \$rcmail_config['password_ldap_search_filter'] = '(&(|(uid=%login)(mail=%login)(mailAlternateAddress=%login)(alias=%login))(objectclass=kolabinetorgperson))'; + \$config['password_ldap_search_filter'] = '(&(|(uid=%login)(mail=%login)(mailAlternateAddress=%login)(alias=%login))(objectclass=kolabinetorgperson))'; // LDAP password hash type // Standard LDAP encryption type which must be one of: crypt, @@ -119,37 +119,37 @@ // Please note that most encodage types require external libraries // to be included in your PHP installation, see function hashPassword in drivers/ldap.php for more info. // Default: 'crypt' - \$rcmail_config['password_ldap_encodage'] = 'clear'; + \$config['password_ldap_encodage'] = 'clear'; // LDAP password attribute // Name of the ldap's attribute used for storing user password // Default: 'userPassword' - \$rcmail_config['password_ldap_pwattr'] = 'userPassword'; + \$config['password_ldap_pwattr'] = 'userPassword'; // LDAP password force replace // Force LDAP replace in cases where ACL allows only replace not read // See http://pear.php.net/package/Net_LDAP2/docs/latest/Net_LDAP2/Net_LDAP2_Entry.html#methodreplace // Default: true - \$rcmail_config['password_ldap_force_replace'] = true; + \$config['password_ldap_force_replace'] = true; // LDAP Password Last Change Date // Some places use an attribute to store the date of the last password change // The date is meassured in "days since epoch" (an integer value) // Whenever the password is changed, the attribute will be updated if set (e.g. shadowLastChange) - \$rcmail_config['password_ldap_lchattr'] = ''; + \$config['password_ldap_lchattr'] = ''; // LDAP Samba password attribute, e.g. sambaNTPassword // Name of the LDAP's Samba attribute used for storing user password - \$rcmail_config['password_ldap_samba_pwattr'] = ''; + \$config['password_ldap_samba_pwattr'] = ''; // LDAP Samba Password Last Change Date attribute, e.g. sambaPwdLastSet // Some places use an attribute to store the date of the last password change // The date is meassured in "seconds since epoch" (an integer value) // Whenever the password is changed, the attribute will be updated if set - \$rcmail_config['password_ldap_samba_lchattr'] = ''; + \$config['password_ldap_samba_lchattr'] = ''; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/roundcubemail/recipient_to_contact.inc.php.tpl b/share/templates/roundcubemail/recipient_to_contact.inc.php.tpl index f3b8d2f..b41ad79 100644 --- a/share/templates/roundcubemail/recipient_to_contact.inc.php.tpl +++ b/share/templates/roundcubemail/recipient_to_contact.inc.php.tpl @@ -1,4 +1,4 @@ <?php - \$rcmail_config['recipient_to_contact_addressbooks'] = array(); - \$rcmail_config['recipient_to_contact_enabled_by_default'] = true; + \$config['recipient_to_contact_addressbooks'] = array(); + \$config['recipient_to_contact_enabled_by_default'] = true; ?>
\ No newline at end of file diff --git a/share/templates/roundcubemail/terms.inc.php.tpl b/share/templates/roundcubemail/terms.inc.php.tpl index 05e9f36..646197d 100644 --- a/share/templates/roundcubemail/terms.inc.php.tpl +++ b/share/templates/roundcubemail/terms.inc.php.tpl @@ -3,20 +3,20 @@ /* terms plugin */ // log accepted terms - \$rcmail_config['terms_log'] = true; + \$config['terms_log'] = true; // renew agreement if older than YYYY-MM-DD HH:MM:SS // NOTICE: Must be in past and set accordingly to server Timezone!!! - \$rcmail_config['terms_date'] = '2011-02-24 00:00:00'; + \$config['terms_date'] = '2011-02-24 00:00:00'; // renew agreement automatically afer x days - \$rcmail_config['terms_renew'] = 28; // 0 = never + \$config['terms_renew'] = 28; // 0 = never // always request terms agreement after login - \$rcmail_config['terms_always'] = false; + \$config['terms_always'] = false; - if (file_exists(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { - include_once(RCMAIL_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER["HTTP_HOST"] . '/' . basename(__FILE__)); } ?> diff --git a/share/templates/zpush/config.php.tpl b/share/templates/zpush/config.php.tpl deleted file mode 100644 index 9480ae4..0000000 --- a/share/templates/zpush/config.php.tpl +++ /dev/null @@ -1,106 +0,0 @@ -<?php - /*********************************************** - * File : config.php - * Project : Z-Push - * Descr : Main configuration file - * - */ - - define('KOLAB_SERVER', "$ldap_ldap_uri"); - define('KOLAB_LDAP_BASE',"$ldap_base_dn"); - define('KOLAB_BIND_DN',"$ldap_service_bind_dn"); - define('KOLAB_BIND_PW',"$ldap_service_bind_pw"); - define("KOLAB_LDAP_ACL",""); - define('KOLAB_IMAP_SERVER', "$imap_server"); - - // Defines the default time zone - if (function_exists("date_default_timezone_set")){ - date_default_timezone_set(date_default_timezone_get()); - } - - // Defines the base path on the server, terminated by a slash - define('BASE_PATH', dirname(\$_SERVER['SCRIPT_FILENAME']) . "/"); - - // Define the include paths - ini_set( - 'include_path', - BASE_PATH . "include/" . PATH_SEPARATOR . - BASE_PATH . PATH_SEPARATOR . - ini_get('include_path') . PATH_SEPARATOR . - "/usr/share/php/" . PATH_SEPARATOR . - "/usr/share/php5/" . PATH_SEPARATOR . - "/usr/share/pear/" - ); - - define('STATE_DIR', 'state'); - - // Try to set unlimited timeout - define('SCRIPT_TIMEOUT', 0); - - //Max size of attachments to display inline. Default is 1MB - define('MAX_EMBEDDED_SIZE', 1048576); - - // Device Provisioning - define('PROVISIONING', true); - - // This option allows the 'loose enforcement' of the provisioning policies for older - // devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution - // false (default) - Enforce provisioning for all devices - // true - allow older devices, but enforce policies on devices which support it - define('LOOSE_PROVISIONING', false); - // Default conflict preference - // Some devices allow to set if the server or PIM (mobile) - // should win in case of a synchronization conflict - // SYNC_CONFLICT_OVERWRITE_SERVER - Server is overwritten, PIM wins - // SYNC_CONFLICT_OVERWRITE_PIM - PIM is overwritten, Server wins (default) - define('SYNC_CONFLICT_DEFAULT', SYNC_CONFLICT_OVERWRITE_PIM); - - // The data providers that we are using (see configuration below - \$BACKEND_PROVIDER = "BackendKolab"; - - define("KOLAB_LDAP_ACL",""); - define('KOLAB_IMAP_NAMESPACES', Array( - 'personal' => "", - 'shared' => "Shared Folders", - 'users' => "Other Users" - ) - ); - - define('KOLAB_IMAP_PORT', 143); - define('KOLAB_IMAP_OPTIONS', "/tls/novalidate-cert"); - - define('KOLAB_INDEX',"/var/cache/kolab/z-push/kolabindex"); - - //KolabMode - // 0 = FlatMode - // 1 = FolderMode - // 2 = try to determine the mode - define("KOLAB_MODE",2); - // define which mobile support foldermode - // this list is checked if KOLAB_MODE is set to 2 - define("KOLAB_MOBILES_FOLDERMODE","iphone:ipod:ipad"); - // folders by default if annotation is not found - // possiblename1:possiblename2: ...... - // if no folders found the last found will be the default - define('KOLAB_DEFAULTFOLDER_DIARY',"calendar:kalender:calendrier:agenda"); - define('KOLAB_DEFAULTFOLDER_CONTACT',"contacts:kontact"); - define('KOLAB_DEFAULTFOLDER_TASK',"task:taske"); - // If 1: shared folders will be read-only, even if the user have rights on it - define('KOLAB_SHAREDFOLDERS_RO',"1"); - - // Logfile - define('KOLAB_LOGFILE',"/var/log/z-push/access.log"); - //For Gal - define('SYNC_GAL_DISPLAYNAME','cn'); - define('SYNC_GAL_PHONE','telephonenumber'); - define('SYNC_GAL_OFFICE', ''); - define('SYNC_GAL_TITLE','title'); - define('SYNC_GAL_COMPANY','o'); - define('SYNC_GAL_ALIAS','uid'); - define('SYNC_GAL_FIRSTNAME','givenname'); - define('SYNC_GAL_LASTNAME','sn'); - define('SYNC_GAL_HOMEPHONE','homephone'); - define('SYNC_GAL_MOBILEPHONE','mobile'); - define('SYNC_GAL_EMAILADDRESS','mail'); - -?> diff --git a/test-wallace.py b/test-wallace.py index 8da3475..c49c486 100755 --- a/test-wallace.py +++ b/test-wallace.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/tests/functional/purge_users.py b/tests/functional/purge_users.py index 5bc1b82..17db9d7 100644 --- a/tests/functional/purge_users.py +++ b/tests/functional/purge_users.py @@ -11,7 +11,7 @@ def purge_users(): users = wap_client.users_list() for user in users['list']: - wap_client.user_delete({'user': user}) + wap_client.user_delete({'id': user}) from tests.functional.purge_imap import purge_imap purge_imap() diff --git a/tests/functional/test_wallace/test_001_user_add.py b/tests/functional/test_wallace/test_001_user_add.py index 9a2c587..d4e620a 100644 --- a/tests/functional/test_wallace/test_001_user_add.py +++ b/tests/functional/test_wallace/test_001_user_add.py @@ -24,7 +24,7 @@ class TestUserAdd(unittest.TestCase): } self.jane = { - 'local': 'john.doe', + 'local': 'jane.doe', 'domain': 'example.org' } diff --git a/tests/functional/test_wap_client/test_001_connect.py b/tests/functional/test_wap_client/test_001_connect.py index 42b8aa3..aa0c069 100644 --- a/tests/functional/test_wap_client/test_001_connect.py +++ b/tests/functional/test_wap_client/test_001_connect.py @@ -31,8 +31,12 @@ class TestConnect(unittest.TestCase): self.assertEqual(result['reason'], "Unknown service") self.assertEqual(result['code'], 400) - def test_003_domains_list(self): + def test_004_domains_list(self): result = wap_client.domains_list() self.assertTrue(result.has_key('count')) self.assertTrue(result.has_key('list')) self.assertEqual(result['count'], len(result['list'])) + + def test_005_get_domain(self): + result = wap_client.request_raw('GET', 'system.get_domain') + self.assertEqual(result, {u'status': u'OK', u'result': {u'domain': u'example.org'}}) diff --git a/tests/functional/test_wap_client/test_006_form_value_select_options.py b/tests/functional/test_wap_client/test_006_form_value_select_options.py new file mode 100644 index 0000000..92b4992 --- /dev/null +++ b/tests/functional/test_wap_client/test_006_form_value_select_options.py @@ -0,0 +1,31 @@ +import time +import unittest + +import pykolab +from pykolab import wap_client + +conf = pykolab.getConf() + +class TestFormValueListOptions(unittest.TestCase): + + def test_001_list_options_user_preferredlanguage(self): + conf = pykolab.getConf() + conf.finalize_conf(fatal=False) + + self.login = conf.get('ldap', 'bind_dn') + self.password = conf.get('ldap', 'bind_pw') + self.domain = conf.get('kolab', 'primary_domain') + + result = wap_client.authenticate(self.login, self.password, self.domain) + + attribute_values = wap_client.form_value_select_options( + 'user', + 1, + 'preferredlanguage' + ) + + self.assertTrue(attribute_values['preferredlanguage'].has_key('default')) + self.assertTrue(attribute_values['preferredlanguage'].has_key('list')) + self.assertTrue(len(attribute_values['preferredlanguage']['list']) > 1) + self.assertTrue(attribute_values['preferredlanguage']['default'] in attribute_values['preferredlanguage']['list']) + diff --git a/tests/functional/test_wap_client/test_007_policy_uid.py b/tests/functional/test_wap_client/test_007_policy_uid.py new file mode 100644 index 0000000..534ebda --- /dev/null +++ b/tests/functional/test_wap_client/test_007_policy_uid.py @@ -0,0 +1,176 @@ +from ConfigParser import RawConfigParser +import time +import unittest + +import pykolab +from pykolab import wap_client +from pykolab.auth import Auth +from pykolab.imap import IMAP + +conf = pykolab.getConf() + +class TestPolicyUid(unittest.TestCase): + + def remove_option(self, section, option): + self.config.remove_option(section, option) + + fp = open(conf.config_file, "w") + self.config.write(fp) + fp.close() + + def set(self, section, option, value): + self.config.set(section, option, value) + + fp = open(conf.config_file, "w") + self.config.write(fp) + fp.close() + + @classmethod + def setup_class(self, *args, **kw): + self.config = RawConfigParser() + self.config.read(conf.config_file) + + from tests.functional.purge_users import purge_users + purge_users() + + self.user = { + 'local': 'john.doe', + 'domain': 'example.org' + } + + self.login = conf.get('ldap', 'bind_dn') + self.password = conf.get('ldap', 'bind_pw') + self.domain = conf.get('kolab', 'primary_domain') + + result = wap_client.authenticate(self.login, self.password, self.domain) + + @classmethod + def teardown_class(self, *args, **kw): + self.config.remove_option('example.org', 'policy_uid') + + fp = open(conf.config_file, "w") + self.config.write(fp) + fp.close() + + from tests.functional.purge_users import purge_users + purge_users() + + def test_001_default(self): + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "doe") + + from tests.functional.purge_users import purge_users + purge_users() + + def test_002_givenname_dot_surname(self): + self.set('example.org', 'policy_uid', '%(givenname)s.%(surname)s') + + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "John.Doe") + + from tests.functional.purge_users import purge_users + purge_users() + + def test_003_givenname_fc_dot_surname(self): + self.set('example.org', 'policy_uid', "'%(givenname)s'[0:1].%(surname)s") + + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "J.Doe") + + from tests.functional.purge_users import purge_users + purge_users() + + def test_004_givenname(self): + self.set('example.org', 'policy_uid', '%(givenname)s') + + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "John") + + from tests.functional.purge_users import purge_users + purge_users() + + def test_005_lowercase_givenname(self): + self.set('example.org', 'policy_uid', '%(givenname)s.lower()') + + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "john") + + from tests.functional.purge_users import purge_users + purge_users() + + def test_006_lowercase_givenname_surname(self): + self.set('example.org', 'policy_uid', "%(givenname)s.lower().%(surname)s.lower()") + + from tests.functional.user_add import user_add + user_add("John", "Doe") + from tests.functional.synchronize import synchronize_once + synchronize_once() + + auth = Auth() + auth.connect() + + user = auth.find_recipient('john.doe@example.org') + + user_info = wap_client.user_info(user) + + self.assertEqual(user_info['uid'], "john.doe") + + from tests.functional.purge_users import purge_users + purge_users() + + diff --git a/tests/functional/user_add.py b/tests/functional/user_add.py index 2fc44cf..6af0419 100644 --- a/tests/functional/user_add.py +++ b/tests/functional/user_add.py @@ -59,12 +59,5 @@ def user_add(givenname, sn, preferredlanguage='en_US'): fvg_params['type_id'] = user_type_id fvg_params['attributes'] = [attr for attr in user_type_info['auto_form_fields'].keys() if not attr in params.keys()] - exec("retval = wap_client.form_value_generate(%r)" % (params)) - - for attribute in user_type_info['auto_form_fields'].keys(): - params[attribute] = retval[attribute] - - params['userpassword'] = user_details['userpassword'] - result = wap_client.user_add(params) diff --git a/ucs/kolab_sieve.py b/ucs/kolab_sieve.py index f2b8df0..bf593f3 100755 --- a/ucs/kolab_sieve.py +++ b/ucs/kolab_sieve.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/ucs/listener.py b/ucs/listener.py index ea19d02..232675e 100755 --- a/ucs/listener.py +++ b/ucs/listener.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> # @@ -91,6 +91,32 @@ def handler(*args, **kw): if isinstance(new, dict) and len(new.keys()) > 0: log.info("Modify entry %r" % (dn)) + mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() + + if mailserver_attribute == None: + log.error("Mail server attribute is not set") + return + + if old.has_key(mailserver_attribute): + log.info("Modified entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, new[mailserver_attribute])) + + if not old[mailserver_attribute] == constants.fqdn: + # Even though the new mailserver can be us, it is the + # *current* mail server that needs to push for the XFER. + log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) + return + + else: + # If old has no mailserver attribute, but new does, we need to create + # the user locally. + if new.has_key(mailserver_attribute): + if not new[mailserver_attribute] == constants.fqdn: + log.info("The mail server for user %r is set (in new, not old), but it is not me (%r)" % (dn, new[mailserver_attribute])) + return + else: + log.info("Entry %r does not have a mail server attribute." % (dn)) + return + auth._auth._synchronize_callback( change_type = 'modify', previous_dn = None, @@ -102,6 +128,25 @@ def handler(*args, **kw): else: log.info("Delete entry %r" % (dn)) + # See if the mailserver_attribute exists + mailserver_attribute = conf.get('ldap', 'mailserver_attribute').lower() + + if mailserver_attribute == None: + log.error("Mail server attribute is not set") + # TODO: Perhaps, query for IMAP servers. If there is only one, + # we know what to do. + return + + if old.has_key(mailserver_attribute): + log.info("Deleted entry %r has mail server attribute %s: %r" % (dn, mailserver_attribute, old[mailserver_attribute])) + + if not old[mailserver_attribute] == constants.fqdn: + log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, old[mailserver_attribute])) + return + + else: + log.info("Entry deletion notification for %r does not have a mail server attribute specified." % (dn)) + auth._auth._synchronize_callback( change_type = 'delete', previous_dn = None, @@ -130,6 +175,10 @@ def handler(*args, **kw): log.info("The mail server for user %r is set, and it is not me (%r)" % (dn, new[mailserver_attribute])) return + else: + log.info("Added entry %r does not have a mail server attribute set." % (dn)) + return + auth._auth._synchronize_callback( change_type = 'add', previous_dn = None, @@ -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/wallace/__init__.py b/wallace/__init__.py index 56d7173..2db963e 100644 --- a/wallace/__init__.py +++ b/wallace/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -63,6 +63,9 @@ def worker_process(*args, **kw): class WallaceDaemon(object): def __init__(self): + self.current_connections = 0 + self.max_connections = 24 + daemon_group = conf.add_cli_parser_option_group(_("Daemon Options")) daemon_group.add_option( @@ -215,21 +218,29 @@ class WallaceDaemon(object): if stage.lower() == "defer": continue + self.current_connections += 1 self.pool.apply_async(pickup_message, (filepath, (self.modules), {'module': module, 'stage': stage})) + self.current_connections -= 1 continue + self.current_connections += 1 self.pool.apply_async(pickup_message, (filepath, (self.modules))) + self.current_connections -= 1 try: while 1: + while self.current_connections >= self.max_connections: + time.sleep(0.5) + pair = s.accept() log.info(_("Accepted connection")) if not pair == None: + self.current_connections += 1 connection, address = pair - #print "Accepted connection from %r" % (address) channel = SMTPChannel(self, connection, address) asyncore.loop() + except Exception, errmsg: traceback.print_exc() s.shutdown(1) @@ -258,6 +269,8 @@ class WallaceDaemon(object): self.pool.apply_async(pickup_message, (filename, (self.modules))) + self.current_connections -= 1 + return def reload_config(self, *args, **kw): diff --git a/wallace/module_footer.py b/wallace/module_footer.py index 550e58f..2779461 100644 --- a/wallace/module_footer.py +++ b/wallace/module_footer.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/wallace/module_optout.py b/wallace/module_optout.py index 5540d80..7fdeb6f 100644 --- a/wallace/module_optout.py +++ b/wallace/module_optout.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/wallace/module_resources.py b/wallace/module_resources.py index 5a66dda..b2924fa 100644 --- a/wallace/module_resources.py +++ b/wallace/module_resources.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> # @@ -127,7 +127,7 @@ def execute(*args, **kw): _message = json.load(open(filepath, 'r')) log.debug("Loaded message %r" % (_message), level=9) - message = message_from_string(_message['data']) + message = message_from_string(str(_message['data'])) recipients = _message['to'] any_itips = False @@ -436,6 +436,9 @@ def execute(*args, **kw): send_response(original_resource['mail'], itip_event) + auth.disconnect() + del auth + # Disconnect IMAP or we lock the mailbox almost constantly imap.disconnect() del imap @@ -589,6 +592,8 @@ def resource_record_from_email_address(email_address): resource_records = [ resource_records ] + auth.disconnect() + return resource_records def resource_records_from_itip_events(itip_events): @@ -722,6 +727,8 @@ def resource_records_from_itip_events(itip_events): level=8 ) + auth.disconnect() + return resource_records def send_response(from_address, itip_events): diff --git a/wallace/modules.py b/wallace/modules.py index 040fe62..d256bdb 100644 --- a/wallace/modules.py +++ b/wallace/modules.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -251,8 +251,20 @@ X-Wallace-Result: REJECT def cb_action_ACCEPT(module, filepath): log.info(_("Accepting message in %s (by module %s)") % (filepath, module)) - _message = json.load(open(filepath, 'r')) - message = message_from_string(_message['data']) + try: + _message = json.load(open(filepath, 'r')) + log.debug(_(u"Message JSON loaded: %r") % (_message), level=9) + except Exception, errmsg: + log.error(_("Error loading message: %r") % (errmsg)) + return + + try: + message = message_from_string(str(_message['data']).replace('\x00','')) + except Exception, errmsg: + log.error(_("Error parsing message: %r") % (errmsg)) + return + + log.debug(_("Accepting message in: %r") %(filepath), level=8) sender = _message['from'] recipients = _message['to'] @@ -271,8 +283,7 @@ def cb_action_ACCEPT(module, filepath): # come from (TODO) # - Third, a character return is inserted somewhere. It # divides the body from the headers - and we don't like (TODO) - #unicode(message.as_string()).replace('\0', '').lstrip() - message.as_string().encode('utf-8').replace('\0','').lstrip() + message.as_string() ) except smtplib.SMTPDataError, errmsg: |