Projects
Kolab:16:Enterprise
pykolab
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 24
View file
pykolab.spec
Changed
@@ -2,6 +2,11 @@ #!BuildIgnore: systemd %endif + +%if 0%{?fedora} || 0%{?rhel} >= 8 +%global py2 2 +%endif + %{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %if 0%{?suse_version} || 0%{?fedora} > 17 || 0%{?rhel} > 6 @@ -29,7 +34,7 @@ Summary: Kolab Groupware Solution Name: pykolab -Version: 0.8.10 +Version: 0.8.11 Release: 1%{?dist} License: GPLv3+ Group: Applications/System @@ -64,48 +69,27 @@ BuildRequires: MySQL-python %endif -BuildRequires: python -BuildRequires: python-augeas -BuildRequires: python-gnupg -BuildRequires: python-icalendar -BuildRequires: python-kolab -BuildRequires: python-kolabformat -BuildRequires: python-ldap -%if 0%{?fedora} > 24 -BuildRequires: python2-nose -%else -BuildRequires: python-nose -%endif -BuildRequires: python-pep8 -BuildRequires: python-pyasn1 -BuildRequires: python-pyasn1-modules - -%if 0%{?suse_version} -BuildRequires: python-pytz -%else -BuildRequires: pytz -%endif - -BuildRequires: python-sievelib -BuildRequires: python-sqlalchemy - -%if 0%{?fedora} >= 23 -# Fedora 23 has python2-twisted and python-twisted -BuildRequires: python-twisted -%else -BuildRequires: python-twisted-core -%endif -BuildRequires: python-tzlocal - -%if 0%{?fedora} >= 21 -# Fedora 21 has qca2 and qca, qca2 has been renamed to qca, required by kdelibs -BuildRequires: qca -%endif +BuildRequires: python%{?py2} +BuildRequires: python%{?py2}-augeas +BuildRequires: python%{?py2}-gnupg +BuildRequires: python%{?py2}-icalendar +BuildRequires: python%{?py2}-kolab >= 2.0 +BuildRequires: python%{?py2}-kolabformat +BuildRequires: python%{?py2}-ldap +BuildRequires: python%{?py2}-nose +BuildRequires: python%{?py2}-pep8 +BuildRequires: python%{?py2}-pyasn1 +BuildRequires: python%{?py2}-pyasn1-modules +BuildRequires: python%{?py2}-pytz +BuildRequires: python%{?py2}-sievelib +BuildRequires: python%{?py2}-sqlalchemy +BuildRequires: python%{?py2}-twisted-core +BuildRequires: python%{?py2}-tzlocal Requires: kolab-cli = %{version}-%{release} -Requires: python-ldap >= 2.4 -Requires: python-pyasn1 -Requires: python-pyasn1-modules +Requires: python%{?py2}-ldap >= 2.4 +Requires: python%{?py2}-pyasn1 +Requires: python%{?py2}-pyasn1-modules Requires(pre): /usr/sbin/useradd Requires(pre): /usr/sbin/usermod Requires(pre): /usr/sbin/groupadd @@ -584,6 +568,9 @@ %attr(0700,%{kolab_user},%{kolab_group}) %dir %{_var}/spool/pykolab/wallace %changelog +* Fri May 17 2019 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.8.11-1 +- Release of version 0.8.11 + * Fri Jul 27 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.8.10-1 - Release of version 0.8.10
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +pykolab (0.8.11-0~kolab1) unstable; urgency=low + + * Release of version 0.8.11 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Fri, 17 May 2019 01:49:00 +0100 + pykolab (0.8.10-0~kolab2) unstable; urgency=low * Correct dependencies for Ubuntu 18.04
View file
pykolab-0.8.10.tar.gz/.arclint -> pykolab-0.8.11.tar.gz/.arclint
Changed
@@ -24,6 +24,7 @@ "E251": "disabled", "E261": "disabled", "E265": "disabled", + "E266": "disabled", "E302": "disabled", "E303": "disabled", "E402": "disabled",
View file
pykolab-0.8.10.tar.gz/bin/kolab_smtp_access_policy.py -> pykolab-0.8.11.tar.gz/bin/kolab_smtp_access_policy.py
Changed
@@ -20,15 +20,13 @@ import datetime import os +import sqlalchemy import sys import time from optparse import OptionParser from ConfigParser import SafeConfigParser -cache = None - -import sqlalchemy from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import Date @@ -50,8 +48,6 @@ from sqlalchemy.schema import Index from sqlalchemy.schema import UniqueConstraint -sys.path = '..','.' + sys.path - import pykolab from pykolab import utils @@ -59,6 +55,10 @@ from pykolab.constants import * from pykolab.translate import _ +cache = None + +sys.path = '..', '.' + sys.path + # TODO: Figure out how to make our logger do some syslogging as well. log = pykolab.getLogger('pykolab.smtp_access_policy') @@ -85,27 +85,28 @@ session = None policy_result_table = Table( - 'policy_result', metadata, - Column('id', Integer, Sequence('seq_id_result'), primary_key=True), - Column('key', String(16), nullable=False), - Column('value', Boolean, nullable=False), - Column('sender', String(254), nullable=True), - Column('recipient', String(254), nullable=False), - Column('sasl_username', String(64)), - Column('sasl_sender', String(64)), - Column('created', Integer, nullable=False), - Column('data', PickleType, nullable=True), - ) + 'policy_result', metadata, + Column('id', Integer, Sequence('seq_id_result'), primary_key=True), + Column('key', String(16), nullable=False), + Column('value', Boolean, nullable=False), + Column('sender', String(254), nullable=True), + Column('recipient', String(254), nullable=False), + Column('sasl_username', String(64)), + Column('sasl_sender', String(64)), + Column('created', Integer, nullable=False), + Column('data', PickleType, nullable=True), +) Index( - 'fsrss', - policy_result_table.c.key, - policy_result_table.c.sender, - policy_result_table.c.recipient, - policy_result_table.c.sasl_username, - policy_result_table.c.sasl_sender, - unique=True - ) + 'fsrss', + policy_result_table.c.key, + policy_result_table.c.sender, + policy_result_table.c.recipient, + policy_result_table.c.sasl_username, + policy_result_table.c.sasl_sender, + unique=True +) + class PolicyResult(object): def __init__( @@ -117,7 +118,7 @@ sasl_username=None, sasl_sender=None, data=None - ): + ): self.key = key self.value = value @@ -128,24 +129,26 @@ self.created = (int)(time.time()) self.data = data + mapper(PolicyResult, policy_result_table) statistic_table = Table( - 'statistic', metadata, - Column('id', Integer, Sequence('seq_id_statistic'), primary_key=True), - Column('sender', String(254), nullable=False), - Column('recipient', String(254), nullable=False), - Column('date', Date, nullable=False), - Column('count', Integer, nullable=False), - ) + 'statistic', metadata, + Column('id', Integer, Sequence('seq_id_statistic'), primary_key=True), + Column('sender', String(254), nullable=False), + Column('recipient', String(254), nullable=False), + Column('date', Date, nullable=False), + Column('count', Integer, nullable=False), +) Index( - 'srd', - statistic_table.c.sender, - statistic_table.c.recipient, - statistic_table.c.date, - unique=True - ) + 'srd', + statistic_table.c.sender, + statistic_table.c.recipient, + statistic_table.c.date, + unique=True +) + class Statistic(object): def __init__(self, sender, recipient, date=datetime.date.today(), count=0): @@ -154,10 +157,12 @@ self.date = date self.count = count + mapper(Statistic, statistic_table) + class PolicyRequest(object): - email_address_keys = 'sender', 'recipient' + email_address_keys = 'sender', 'recipient' recipients = auth = None @@ -191,7 +196,9 @@ else: if not policy_request'recipient'.strip() == '': - self.recipients = list(set(self.recipients + policy_request'recipient')) + self.recipients = list( + set(self.recipients + policy_request'recipient') + ) def add_request(self, policy_request={}): """ @@ -211,18 +218,18 @@ pass log.debug( - _("Adding policy request to instance %s") % (self.instance), - level=8 - ) + _("Adding policy request to instance %s") % (self.instance), + level=8 + ) # Normalize email addresses (they may contain recipient delimiters) - if policy_request.has_key('recipient'): - policy_request'recipient' = normalize_address( - policy_request'recipient' - ) + if 'recipient' in policy_request: + policy_request'recipient' = normalize_address(policy_request'recipient') if not policy_request'recipient'.strip() == '': - self.recipients = list(set(self.recipients + policy_request'recipient')) + self.recipients = list( + set(self.recipients + policy_request'recipient') + ) def parse_ldap_dn(self, dn): """ @@ -244,19 +251,16 @@ return None if len(ldap_dn) > 0: - search_attrs = conf.get_list( - 'kolab_smtp_access_policy', - 'address_search_attrs' - ) + search_attrs = conf.get_list('kolab_smtp_access_policy', 'address_search_attrs') rule_subject = self.auth.get_user_attributes( - self.sasl_domain, - { 'dn': dn }, - search_attrs + 'objectclass' - ) + self.sasl_domain, + {'dn': dn}, + search_attrs + 'objectclass' + ) for search_attr in search_attrs: - if rule_subject.has_key(search_attr): + if search_attr in rule_subject: if isinstance(rule_subjectsearch_attr, basestring): values.append(rule_subjectsearch_attr) else: @@ -274,30 +278,25 @@ parsed_uri = utils.parse_ldap_uri(uri) - if parsed_uri == None: + if parsed_uri is None: return None - (_protocol, _server, _port, _base_dn, _attrs, _scope, _filter) = \ - parsed_uri + (_protocol, _server, _port, _base_dn, _attrs, _scope, _filter) = parsed_uri if len(_attrs) == 0: - search_attrs = conf.get_list( - 'kolab_smtp_access_policy', - 'address_search_attrs' - ) + search_attrs = conf.get_list('kolab_smtp_access_policy', 'address_search_attrs') else: - search_attrs = _attrs - - users = + search_attrs = _attrs self.auth._auth._bind() + _users = self.auth._auth._search( - _base_dn, - scope=LDAP_SCOPE_scope, - filterstr=_filter, - attrlist=search_attrs + 'objectclass' , - override_search="_regular_search" - ) + _base_dn, + scope=LDAP_SCOPE_scope, + filterstr=_filter, + attrlist=search_attrs + 'objectclass', + override_search="_regular_search" + ) for _user in _users: for search_attr in search_attrs: @@ -322,7 +321,7 @@ '$mydomains': expand_mydomains } - rules = { 'allow': , 'deny': } + rules = {'allow': , 'deny': } if isinstance(policy, basestring): policy = policy @@ -357,7 +356,7 @@ # See if the value is an LDAP DN ldap_dn = self.parse_ldap_dn(_rule) - if not ldap_dn == None and len(ldap_dn) > 0: + if ldap_dn is not None and len(ldap_dn) > 0: if _prefix == '-': rules'deny'.extend(ldap_dn) else: @@ -365,7 +364,7 @@ else: ldap_uri = self.parse_ldap_uri(_rule) - if not ldap_uri == None and len(ldap_uri) > 0: + if ldap_uri is not None and len(ldap_uri) > 0: if _prefix == '-': rules'deny'.extend(ldap_uri) else: @@ -421,46 +420,42 @@ search_attrs = conf.get_list(self.sasl_domain, 'address_search_attrs') - if search_attrs == None or \ + if search_attrs is 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 \ + if search_attrs is None or \ (isinstance(search_attrs, list) and len(search_attrs) == 0): - search_attrs = conf.get_list( - 'kolab_smtp_access_policy', - 'address_search_attrs' - ) + search_attrs = conf.get_list('kolab_smtp_access_policy', 'address_search_attrs') - if search_attrs == None or \ + if search_attrs is None or \ (isinstance(search_attrs, list) and len(search_attrs) == 0): - search_attrs = conf.get_list( - conf.get('kolab', 'auth_mechanism'), - 'mail_attributes' - ) + conf.get('kolab', 'auth_mechanism'), + 'mail_attributes' + ) want_attrs = for search_attr in search_attrs: - if not self.sasl_user.has_key(search_attr): + if search_attr not in self.sasl_user: 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 - ) + 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 search_attr in self.sasl_user: if isinstance(self.sasl_usersearch_attr, list): if self.sender.lower() in x.lower() for x in self.sasl_usersearch_attr: return True @@ -479,7 +474,7 @@ interested in. """ - if self.sasl_username == None: + if self.sasl_username is None: if not conf.allow_unauthenticated: reject(_("Unauthorized access not allowed")) else: @@ -491,93 +486,92 @@ # If we have an sasl_username, find the user object in the # authentication database, along with the attributes we are # interested in. - if self.sasl_domain == None: + if self.sasl_domain is None: if len(self.sasl_username.split('@')) > 1: self.sasl_domain = self.sasl_username.split('@')1 else: self.sasl_domain = conf.get('kolab', 'primary_domain') - sasl_username = "%s@%s" % (self.sasl_username, self.sasl_domain) + sasl_username = "%s@%s" % ( + self.sasl_username, self.sasl_domain + ) - if self.auth == None: + if self.auth is None: self.auth = Auth(self.sasl_domain) elif not self.auth.domain == self.sasl_domain: self.auth = Auth(self.sasl_domain) - sasl_users = self.auth.find_recipient( - sasl_username, - domain=self.sasl_domain - ) + sasl_users = self.auth.find_recipient(sasl_username, domain=self.sasl_domain) if isinstance(sasl_users, list): if len(sasl_users) == 0: log.error(_("Could not find recipient")) return False else: - self.sasl_user = { 'dn': sasl_users0 } + self.sasl_user = {'dn': sasl_users0} elif isinstance(sasl_users, basestring): - self.sasl_user = { 'dn': sasl_users } + self.sasl_user = {'dn': sasl_users} if not self.sasl_user'dn': # Got a final answer here, do the caching thing. cache_update( - function='verify_sender', - sender=self.sender, - recipients=self.recipients, - result=(int)(False), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_sender', + sender=self.sender, + recipients=self.recipients, + result=(int)(False), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) reject( - _("Could not find envelope sender user %s (511)") % ( - self.sasl_username - ) + _("Could not find envelope sender user %s (511)") % ( + self.sasl_username ) + ) attrs = conf.get_list(self.sasl_domain, 'auth_attributes') - if attrs == None or (isinstance(attrs, list) and len(attrs) == 0): + if attrs is None or (isinstance(attrs, list) and len(attrs) == 0): attrs = conf.get_list( - conf.get('kolab', 'auth_mechanism'), - 'auth_attributes' - ) + conf.get('kolab', 'auth_mechanism'), + 'auth_attributes' + ) mail_attrs = conf.get_list(self.sasl_domain, 'mail_attributes') - if mail_attrs == None or \ + if mail_attrs is None or \ (isinstance(mail_attrs, list) and len(mail_attrs) == 0): mail_attrs = conf.get_list( - conf.get('kolab', 'auth_mechanism'), - 'mail_attributes' - ) + conf.get('kolab', 'auth_mechanism'), + 'mail_attributes' + ) - if not mail_attrs == None: + if mail_attrs is not None: attrs.extend(mail_attrs) attrs.extend( - - 'kolabAllowSMTPRecipient', - 'kolabAllowSMTPSender' - - ) + + 'kolabAllowSMTPRecipient', + 'kolabAllowSMTPSender' + + ) attrs = list(set(attrs)) user_attrs = self.auth.get_user_attributes( - self.sasl_domain, - self.sasl_user, - attrs - ) + self.sasl_domain, + self.sasl_user, + attrs + ) user_attrs'dn' = self.sasl_user'dn' self.sasl_user = utils.normalize(user_attrs) log.debug( - _("Obtained authenticated user details for %r: %r") % ( - self.sasl_user'dn', - self.sasl_user.keys() - ), - level=8 - ) + _("Obtained authenticated user details for %r: %r") % ( + self.sasl_user'dn', + self.sasl_user.keys() + ), + level=8 + ) def verify_delegate(self): """ @@ -586,7 +580,7 @@ """ sender_is_delegate = False - if self.sender_domain == None: + if self.sender_domain is None: if len(self.sender.split('@')) > 1: self.sender_domain = self.sender.split('@')1 else: @@ -596,160 +590,159 @@ return True search_attrs = conf.get_list(self.sender_domain, 'mail_attributes') - if search_attrs == None: + if search_attrs is None: search_attrs = conf.get_list( - conf.get('kolab', 'auth_mechanism'), - 'mail_attributes' - ) + conf.get('kolab', 'auth_mechanism'), + 'mail_attributes' + ) sender_users = self.auth.find_recipient( - self.sender, - domain=self.sender_domain - ) + self.sender, + domain=self.sender_domain + ) if isinstance(sender_users, list): if len(sender_users) > 1: # More then one sender user with this recipient address. # TODO: check each of the sender users found. - self.sender_user = { 'dn': sender_users0 } + self.sender_user = {'dn': sender_users0} elif len(sender_users) == 1: - self.sender_user = { 'dn': sender_users } + self.sender_user = {'dn': sender_users} else: - self.sender_user = { 'dn': False } + self.sender_user = {'dn': False} elif isinstance(sender_users, basestring): - self.sender_user = { 'dn': sender_users } + self.sender_user = {'dn': sender_users} if not self.sender_user'dn': cache_update( - function='verify_sender', - sender=self.sender, - recipients=self.recipients, - result=(int)(False), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_sender', + sender=self.sender, + recipients=self.recipients, + result=(int)(False), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) reject(_("Could not find envelope sender user %s") % (self.sender)) attrs = search_attrs attrs.extend( - - 'kolabAllowSMTPRecipient', - 'kolabAllowSMTPSender', - 'kolabDelegate' - - ) + + 'kolabAllowSMTPRecipient', + 'kolabAllowSMTPSender', + 'kolabDelegate' + + ) user_attrs = self.auth.get_user_attributes( - self.sender_domain, - self.sender_user, - attrs - ) + self.sender_domain, + self.sender_user, + attrs + ) user_attrs'dn' = self.sender_user'dn' self.sender_user = utils.normalize(user_attrs) - if not self.sender_user.has_key('kolabdelegate'): + if 'kolabdelegate' not in self.sender_user: # Got a final answer here, do the caching thing. - if not cache == False: - record_id = cache_update( - function='verify_sender', - sender=self.sender, - recipients=self.recipients, - result=(int)(False), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + if cache is not False: + cache_update( + function='verify_sender', + sender=self.sender, + recipients=self.recipients, + result=(int)(False), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) reject( - _("%s is unauthorized to send on behalf of %s") % ( - self.sasl_user'dn', - self.sender_user'dn' - ) + _("%s is unauthorized to send on behalf of %s") % ( + self.sasl_user'dn', + self.sender_user'dn' ) + ) - elif self.sender_user'kolabdelegate' == None: + elif self.sender_user'kolabdelegate' is None: # No delegates for this sender could be found. The user is # definitely NOT a delegate of the sender. log.warning( _("User %s attempted to use envelope sender address %s without authorization") % ( - policy_request"sasl_username", - policy_request"sender" - ) + policy_request"sasl_username", + policy_request"sender" ) + ) # Got a final answer here, do the caching thing. - if not cache == False: - record_id = cache_update( - function='verify_sender', - sender=self.sender, - recipients=self.recipients, - result=(int)(False), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + if cache is not False: + cache_update( + function='verify_sender', + sender=self.sender, + recipients=self.recipients, + result=(int)(False), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) reject( - _("%s is unauthorized to send on behalf of %s") % ( - self.sasl_user'dn', - self.sender_user'dn' - ) + _("%s is unauthorized to send on behalf of %s") % ( + self.sasl_user'dn', + self.sender_user'dn' ) + ) else: # See if we can match the value of the envelope sender delegates to # the actual sender sasl_username - if self.sasl_user == None: + if self.sasl_user is None: sasl_users = self.auth.find_recipient( - self.sasl_username, - domain=self.sasl_domain - ) + self.sasl_username, + domain=self.sasl_domain + ) if isinstance(sasl_users, list): if len(sasl_users) == 0: log.error(_("Could not find recipient")) return False else: - self.sasl_user = { 'dn': sasl_users0 } + self.sasl_user = {'dn': sasl_users0} elif isinstance(sasl_users, basestring): - self.sasl_user = { 'dn': sasl_users } + self.sasl_user = {'dn': sasl_users} # Possible values for the kolabDelegate attribute are: # a 'uid', a 'dn'. - if not self.sasl_user.has_key('uid'): + if 'uid' not in self.sasl_user: self.sasl_user'uid' = self.auth.get_user_attribute( - self.sasl_domain, - self.sasl_user, - 'uid' - ) - + self.sasl_domain, + self.sasl_user, + 'uid' + ) sender_delegates = self.sender_user'kolabdelegate' if not type(sender_delegates) == list: - sender_delegates = sender_delegates + sender_delegates = sender_delegates for sender_delegate in sender_delegates: if self.sasl_user'dn' == sender_delegate: log.debug( - _("Found user %s to be a delegate user of %s") % ( - policy_request"sasl_username", - policy_request"sender" - ), - level=8 - ) + _("Found user %s to be a delegate user of %s") % ( + policy_request"sasl_username", + policy_request"sender" + ), + level=8 + ) sender_is_delegate = True elif self.sasl_user'uid' == sender_delegate: log.debug( - _("Found user %s to be a delegate user of %s") % ( - policy_request"sasl_username", - policy_request"sender" - ), - level=8 - ) + _("Found user %s to be a delegate user of %s") % ( + policy_request"sasl_username", + policy_request"sender" + ), + level=8 + ) sender_is_delegate = True @@ -763,31 +756,42 @@ self.recipient = recipient - if not self.sasl_username == '' and not self.sasl_username == None: - log.debug(_("Verifying authenticated sender '%(sender)s' with sasl_username '%(sasl_username)s' for recipient '%(recipient)s'") % (self.__dict__) - ) + if not self.sasl_username == '' and self.sasl_username is not None: + log.debug( + _("Verifying authenticated sender '%(sender)s' with sasl_username '%(sasl_username)s' for recipient '%(recipient)s'") % ( + self.__dict__ + ), + level=8 + ) + else: - log.debug(_("Verifying unauthenticated sender '%(sender)s' for recipient '%(recipient)s'") % (self.__dict__) - ) + log.debug( + _( + "Verifying unauthenticated sender '%(sender)s' for recipient '%(recipient)s'" + ) % ( + self.__dict__ + ), + level=8 + ) recipient_verified = False - if not cache == False: + if cache is not False: records = cache_select( - function='verify_recipient', - sender=self.sender, - recipient=recipient, - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender, - ) + function='verify_recipient', + sender=self.sender, + recipient=recipient, + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender, + ) - if not records == None and len(records) == 1: + if records is not None and len(records) == 1: log.info( - _("Reproducing verify_recipient(%s, %s) from cache") % ( - self.sender, - recipient - ) + _("Reproducing verify_recipient(%s, %s) from cache") % ( + self.sender, + recipient ) + ) return records0.value @@ -797,100 +801,94 @@ sasl_domain = recipient.split('@')1 else: sasl_domain = conf.get('kolab', 'primary_domain') - recipient = "%s@%s" % (recipient,sasl_domain) + recipient = "%s@%s" % (recipient, sasl_domain) if not verify_domain(sasl_domain): - if not cache == False: + if cache is not False: cache_update( - function='verify_recipient', - sender=self.sender, - recipient=recipient, - result=(int)(True), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=recipient, + result=(int)(True), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) return True - if self.auth == None: + if self.auth is None: self.auth = Auth(sasl_domain) elif not self.auth.domain == sasl_domain: self.auth = Auth(sasl_domain) if verify_domain(sasl_domain): - if not self.auth.domains == None and self.auth.domains.has_key(sasl_domain): + if self.auth.domains is not None and sasl_domain in self.auth.domains: log.debug( - _("Using authentication domain %s instead of %s") % ( - self.auth.domainssasl_domain, - sasl_domain - ), - level=8 - ) + _("Using authentication domain %s instead of %s") % ( + self.auth.domainssasl_domain, + sasl_domain + ), + level=8 + ) sasl_domain = self.auth.domainssasl_domain else: log.debug( - _("Domain %s is a primary domain") % ( - sasl_domain - ), - level=8 - ) + _("Domain %s is a primary domain") % (sasl_domain), + level=8 + ) else: log.warning( - _("Checking the recipient for domain %s that is not ours. This is probably a configuration error.") % ( - sasl_domain - ) - ) + _("Checking the recipient for domain %s that is not ours.") % (sasl_domain) + ) return True recipients = self.auth.find_recipient( - normalize_address(recipient), - domain=sasl_domain, - ) + normalize_address(recipient), + domain=sasl_domain, + ) if isinstance(recipients, list): if len(recipients) > 1: log.info( - _("This recipient address is related to multiple object entries and the SMTP Access Policy can therefore not restrict message flow") - ) + _("This recipient address is related to multiple object entries and the SMTP Access Policy can therefore not restrict message flow") + ) cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(True), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(True), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) return True elif len(recipients) == 1: - _recipient = { 'dn': recipients0 } + _recipient = {'dn': recipients0} else: log.debug( - _("Recipient address %r not found. Allowing since the MTA was configured to accept the recipient.") % ( - normalize_address(recipient) - ), - level=3 - ) + _("Recipient address %r not found. Allowing since the MTA was configured to accept the recipient.") % ( + normalize_address(recipient) + ), + level=3 + ) cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(True), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(True), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) return True elif isinstance(recipients, basestring): - _recipient = { - 'dn': recipients - } + _recipient = {'dn': recipients} # We have gotten an invalid recipient. We need to catch this case, # because testing can input invalid recipients, and so can faulty @@ -898,45 +896,45 @@ if not _recipient'dn': if not conf.allow_unauthenticated: cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(False), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(False), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) reject(_("Invalid recipient")) else: cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(True), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(True), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) log.debug(_("Could not find this user, accepting"), level=8) return True - if not _recipient'dn' == False: + if _recipient'dn' is not False: recipient_policy = self.auth.get_entry_attribute( - sasl_domain, - _recipient'dn', - 'kolabAllowSMTPSender' - ) + sasl_domain, + _recipient'dn', + 'kolabAllowSMTPSender' + ) # If no such attribute has been specified, allow - if recipient_policy == None: + if recipient_policy is None: cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(True), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(True), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) recipient_verified = True @@ -945,19 +943,19 @@ # sender. else: recipient_verified = self.parse_policy( - recipient, - self.sender, - recipient_policy - ) + recipient, + self.sender, + recipient_policy + ) cache_update( - function='verify_recipient', - sender=self.sender, - recipient=normalize_address(recipient), - result=(int)(recipient_verified), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender - ) + function='verify_recipient', + sender=self.sender, + recipient=normalize_address(recipient), + result=(int)(recipient_verified), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender + ) return recipient_verified @@ -972,16 +970,16 @@ recipients_verified = True - if not cache == False: + if cache is not False: records = cache_select( - function='verify_recipient', - sender=self.sender, - recipients=self.recipients, - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender, - ) + function='verify_recipient', + sender=self.sender, + recipients=self.recipients, + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender, + ) - if not records == None and len(records) == len(self.recipients): + if records is not None and len(records) == len(self.recipients): log.debug("Euh, what am I doing here?") for record in records: recipient_found = False @@ -990,7 +988,12 @@ recipient_found = True if not recipient_found: - reject(_("Sender %s is not allowed to send to recipient %s") % (self.sender,recipient)) + reject( + _("Sender %s is not allowed to send to recipient %s") % ( + self.sender, + recipient + ) + ) for recipient in self.recipients: recipient_verified = self.verify_recipient(recipient) @@ -1019,11 +1022,11 @@ sender_verified = False - if self.sender == None: + if self.sender is None: # Trusted host? if not hasattr(self, 'client_address') or \ self.client_address == "" or \ - self.client_address == None: + self.client_address is None: # Nothing to compare to. return False @@ -1032,32 +1035,30 @@ import netaddr networks = conf.get_list( - 'kolab_smtp_access_policy', - 'empty_sender_hosts' - ) + 'kolab_smtp_access_policy', + 'empty_sender_hosts' + ) - trusted = False for network in networks: if netaddr.IPNetwork(self.client_address) in netaddr.IPNetwork(network): return True - except ImportError, errmsg: + except ImportError as errmsg: return False - if not cache == False: + if cache is not False: records = cache_select( - sender=self.sender, - recipients=self.recipients, - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender, - function='verify_sender' - ) + sender=self.sender, + recipients=self.recipients, + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender, + function='verify_sender' + ) - if not records == None and len(records) == len(self.recipients): - log.info(_("Reproducing verify_sender(%r) from cache") % ( - self.__dict__ - ) - ) + if records is not None and len(records) == len(self.recipients): + log.info( + _("Reproducing verify_sender(%r) from cache") % (self.__dict__) + ) for record in records: recipient_found = False @@ -1067,16 +1068,22 @@ if recipient_found: if not record.value: - reject(_("Sender %s is not allowed to send to recipient %s") % (self.sender,recipient)) + reject( + _("Sender %s is not allowed to send to recipient %s") % ( + self.sender, + recipient + ) + ) + if record.data is not None: self.__dict__.update(record.data) return True - if self.verify_authenticity() == False: + if self.verify_authenticity() is False: reject(_("Unverifiable sender.")) - if not self.sasl_user == None: + if self.sasl_user is not None: self.sasl_user_uses_alias = self.verify_alias() if not self.sasl_user_uses_alias: @@ -1085,20 +1092,19 @@ # If the authenticated user is using delegate functionality, apply the # recipient policy attribute for the envelope sender. - if self.sasl_user_is_delegate == False and \ - self.sasl_user_uses_alias == False and \ - not conf.allow_unauthenticated: + if self.sasl_user_is_delegate is False: + if self.sasl_user_uses_alias is False: + if not conf.allow_unauthenticated: + reject(_("Sender uses unauthorized envelope sender address")) - reject(_("Sender uses unauthorized envelope sender address")) - - elif self.sasl_user_is_delegate: + if self.sasl_user_is_delegate: # Apply the recipient policy for the sender using the envelope # sender user object. recipient_policy_domain = self.sender_domain recipient_policy_sender = self.sender recipient_policy_user = self.sender_user - elif not self.sasl_user == None: + elif self.sasl_user is not None: # Apply the recipient policy from the authenticated user. recipient_policy_domain = self.sasl_domain recipient_policy_sender = self.sasl_username @@ -1113,84 +1119,88 @@ recipient_policy_user = self.sender_user log.debug( - _("Verifying whether sender is allowed to send to recipient using sender policy"), - level=8 - ) + _("Verifying whether sender is allowed to send to recipient using sender policy"), + level=8 + ) - if recipient_policy_user.has_key('kolaballowsmtprecipient'): + if 'kolaballowsmtprecipient' in recipient_policy_user: recipient_policy = recipient_policy_user'kolaballowsmtprecipient' else: recipient_policy = self.auth.get_user_attribute( - recipient_policy_domain, - recipient_policy_user, - 'kolabAllowSMTPRecipient' - ) + recipient_policy_domain, + recipient_policy_user, + 'kolabAllowSMTPRecipient' + ) log.debug(_("Result is %r") % (recipient_policy), level=8) # If no such attribute has been specified, allow - if recipient_policy == None: + if recipient_policy is None: log.debug( - _("No recipient policy restrictions exist for this sender"), - level=8 - ) + _("No recipient policy restrictions exist for this sender"), + level=8 + ) sender_verified = True # Otherwise,parse the policy obtained. else: log.debug( - _("Found a recipient policy to apply for this sender."), - level=8 - ) + _("Found a recipient policy to apply for this sender."), + level=8 + ) recipient_allowed = None for recipient in self.recipients: recipient_allowed = self.parse_policy( - recipient_policy_sender, - recipient, - recipient_policy - ) + recipient_policy_sender, + recipient, + recipient_policy + ) if not recipient_allowed: reject( - _("Sender %s not allowed to send to recipient %s") % (recipient_policy_user'dn',recipient) + _("Sender %s not allowed to send to recipient %s") % ( + recipient_policy_user'dn', + recipient ) + ) sender_verified = True - if not cache == False: + if cache is not False: data = { - 'sasl_user_uses_alias': self.sasl_user_uses_alias, - 'sasl_user_is_delegate': self.sasl_user_is_delegate, - 'sender_domain': self.sender_domain, - } + 'sasl_user_uses_alias': self.sasl_user_uses_alias, + 'sasl_user_is_delegate': self.sasl_user_is_delegate, + 'sender_domain': self.sender_domain, + } - record_id = cache_update( - function='verify_sender', - sender=self.sender, - recipients=self.recipients, - result=(int)(sender_verified), - sasl_username=self.sasl_username, - sasl_sender=self.sasl_sender, - data=data - ) + cache_update( + function='verify_sender', + sender=self.sender, + recipients=self.recipients, + result=(int)(sender_verified), + sasl_username=self.sasl_username, + sasl_sender=self.sasl_sender, + data=data + ) return sender_verified + def cache_cleanup(): - if not cache == True: + if cache is not True: return log.debug(_("Cleaning up the cache"), level=8) - session.query( - PolicyResult - ).filter( - PolicyResult.created < ((int)(time.time()) - cache_expire) - ).delete() + session.query(PolicyResult).filter( + PolicyResult.created < ((int)(time.time()) - cache_expire) + ).delete() + session.commit() + def cache_init(): global cache, cache_expire, session @@ -1200,17 +1210,15 @@ cache = True if conf.has_option('kolab_smtp_access_policy', 'retention'): cache_expire = (int)( - conf.get( - 'kolab_smtp_access_policy', - 'retention' - ) + conf.get( + 'kolab_smtp_access_policy', + 'retention' ) - elif conf.has_option('kolab_smtp_access_policy', 'uri'): - log.warning(_("The 'uri' setting in the kolab_smtp_access_policy section is soon going to be deprecated in favor of 'cache_uri'")) - cache_uri = conf.get('kolab_smtp_access_policy', 'uri') - cache = True + ) + else: return False + else: return False @@ -1227,7 +1235,7 @@ try: metadata.create_all(engine) - except sqlalchemy.exc.OperationalError, e: + except sqlalchemy.exc.OperationalError as e: log.error(_("Operational Error in caching: %s" % (e))) return False @@ -1237,34 +1245,34 @@ return cache + def cache_select( function, sender, recipient='', recipients=, sasl_username='', - sasl_sender='' - ): + sasl_sender=''): - if not cache == True: + if cache is not True: return None if not recipient == '' and recipients == : recipients = recipient return session.query( - PolicyResult - ).filter_by( - key=function, - sender=sender, - sasl_username=sasl_username, - sasl_sender=sasl_sender - ).filter( - PolicyResult.recipient.in_(recipients) - ).filter( - PolicyResult.created >= \ - ((int)(time.time()) - cache_expire) - ).all() + PolicyResult + ).filter_by( + key=function, + sender=sender, + sasl_username=sasl_username, + sasl_sender=sasl_sender + ).filter( + PolicyResult.recipient.in_(recipients) + ).filter( + PolicyResult.created >= ((int)(time.time()) - cache_expire) + ).all() + def cache_insert( function, @@ -1274,37 +1282,37 @@ result=None, sasl_username='', sasl_sender='', - data=None - ): + data=None): - if not cache == True: + if cache is notTrue: return log.debug( - _("Caching the policy result with timestamp %d") % ( - (int)(time.time()) - ), - level=8 - ) + _("Caching the policy result with timestamp %d") % ( + (int)(time.time()) + ), + level=8 + ) if not recipient == '' and recipients == : recipients = recipient for recipient in recipients: session.add( - PolicyResult( - key=function, - value=result, - sender=sender, - recipient=recipient, - sasl_username=sasl_username, - sasl_sender=sasl_sender, - data=data - ) + PolicyResult( + key=function, + value=result, + sender=sender, + recipient=recipient, + sasl_username=sasl_username, + sasl_sender=sasl_sender, + data=data ) + ) session.commit() + def cache_update( function, sender, @@ -1313,26 +1321,25 @@ result=None, sasl_username='', sasl_sender='', - data=None - ): + data=None): """ Insert an updated set of rows into the cache depending on the necessity """ - if not cache == True: + if cache is not True: return records = _records = cache_select( - function, - sender, - recipient, - recipients, - sasl_username, - sasl_sender - ) + function, + sender, + recipient, + recipients, + sasl_username, + sasl_sender + ) for record in _records: if record.value == (int)(result): @@ -1348,48 +1355,52 @@ recipient_found = True if not recipient_found: cache_insert( - function=function, - sender=sender, - recipient=recipient, - result=result, - sasl_username=sasl_username, - sasl_sender=sasl_sender, - data=data - ) + function=function, + sender=sender, + recipient=recipient, + result=result, + sasl_username=sasl_username, + sasl_sender=sasl_sender, + data=data + ) + def defer_if_permit(message, policy_request=None): log.info(_("Returning action DEFER_IF_PERMIT: %s") % (message)) - print "action=DEFER_IF_PERMIT %s\n\n" % (message) + print("action=DEFER_IF_PERMIT %s\n\n" % (message)) sys.exit(0) + def dunno(message, policy_request=None): log.info(_("Returning action DUNNO: %s") % (message)) - print "action=DUNNO %s\n\n" % (message) + print("action=DUNNO %s\n\n" % (message)) sys.exit(0) + def hold(message, policy_request=None): log.info(_("Returning action HOLD: %s") % (message)) - print "action=HOLD %s\n\n" % (message) + print("action=HOLD %s\n\n" % (message)) sys.exit(0) + def permit(message, policy_request=None): log.info(_("Returning action PERMIT: %s") % (message)) # If we have no policy request, we have been called for a reason, # and everything relevant has been figured out already. - if policy_request == None: - print "action=PERMIT\n\n" + if policy_request is None: + print("action=PERMIT\n\n") sys.exit(0) # If the user is not authenticated, there's no reason to do # extra checks here -- to have been performed already. if not hasattr(policy_request, 'sasl_username'): - print "action=PERMIT\n\n" + print("action=PERMIT\n\n") sys.exit(0) # Same here. - if policy_request.sasl_username == None: - print "action=PERMIT\n\n" + if policy_request.sasl_username is None: + print("action=PERMIT\n\n") sys.exit(0) delegate_sender_header = None @@ -1403,15 +1414,15 @@ # if policy_request.sasl_user_is_delegate: # Domain-specific setting? - if not policy_request.sender_domain == None: + if policy_request.sender_domain is not None: delegate_sender_header = conf.get(policy_request.sender_domain, 'delegate_sender_header') # Global setting? - if delegate_sender_header == None: + if delegate_sender_header is None: delegate_sender_header = conf.get('kolab_smtp_access_policy', 'delegate_sender_header') # Default - if delegate_sender_header == None: + if delegate_sender_header is None: delegate_sender_header = True # If the sender is using an alias as the envelope sender address, take @@ -1419,15 +1430,15 @@ # and/or X-Sender headers. elif policy_request.sasl_user_uses_alias: # Domain-specific setting? - if not policy_request.sender_domain == None: + if policy_request.sender_domain is not None: alias_sender_header = conf.get(policy_request.sender_domain, 'alias_sender_header') # Global setting? - if alias_sender_header == None: + if alias_sender_header is None: alias_sender_header = conf.get('kolab_smtp_access_policy', 'alias_sender_header') # Default - if alias_sender_header == None: + if alias_sender_header is None: alias_sender_header = True # Make the values booleans @@ -1453,22 +1464,22 @@ sender_header = conf.get(policy_request.sender_domain, 'sender_header') # Global setting? - if sender_header == None: + if sender_header is None: sender_header = conf.get('kolab_smtp_access_policy', 'sender_header') # Default - if sender_header == None: + if sender_header is None: sender_header = True # Domain specific? xsender_header = conf.get(policy_request.sender_domain, 'xsender_header') # Global setting? - if xsender_header == None: + if xsender_header is None: xsender_header = conf.get('kolab_smtp_access_policy', 'xsender_header') # Default - if xsender_header == None: + if xsender_header is None: xsender_header = True # Note that if the user is not a delegatee, and not using an alias, the sender @@ -1482,7 +1493,7 @@ if sender_header or xsender_header: # Do the encoding, if any - if not enc_key == None: + if enc_key is not None: header = 'X-Authenticated-As' xheader = None sender = utils.encode(enc_key, policy_request.sasl_username) @@ -1492,27 +1503,29 @@ sender = policy_request.sasl_username if sender_header: - print "action=PREPEND %s: %s" % (header, sender) + print("action=PREPEND %s: %s" % (header, sender)) - if xsender_header and not xheader == None: - print "action=PREPEND %s: %s" % (xheader, sender) + if xsender_header and xheader is not None: + print("action=PREPEND %s: %s" % (xheader, sender)) - print "action=PERMIT\n\n" + print("action=PERMIT\n\n") sys.exit(0) + def reject(message, policy_request=None): log.info(_("Returning action REJECT: %s") % (message)) - print "action=REJECT %s\n\n" % (message) + print("action=REJECT %s\n\n" % (message)) sys.exit(0) + def expand_mydomains(): """ Return a list of my domains. """ - global auth,mydomains + global auth, mydomains - if not mydomains == None: + if mydomains is not None: return mydomains auth.connect() @@ -1521,6 +1534,7 @@ return mydomains.keys() + def normalize_address(email_address): """ Parse an address; Strip off anything after a recipient delimiter. @@ -1531,14 +1545,16 @@ # Take the first part split by recipient delimiter and the last part # split by '@'. return "%s@%s" % ( - 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.lower() - ) + 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.lower() + ) + else: return email_address.lower() + def read_request_input(): """ Read a single policy request from sys.stdin, and return a dictionary @@ -1553,13 +1569,13 @@ end_of_request = False while not end_of_request: - if (time.time()-start_time) >= conf.timeout: + if (time.time() - start_time) >= conf.timeout: log.warning(_("Timeout for policy request reading exceeded")) sys.exit(0) request_line = sys.stdin.readline() if request_line.strip() == '': - if policy_request.has_key('request'): + if 'request' in policy_request: log.debug(_("End of current request"), level=8) end_of_request = True else: @@ -1572,14 +1588,15 @@ return policy_request + def verify_domain(domain): """ Verify whether the domain is internal (mine) or external. """ - global auth,mydomains + global auth, mydomains - if not mydomains == None: + if mydomains is not None: return domain in mydomains.keys() auth.connect() @@ -1588,48 +1605,58 @@ mydomains = auth.list_domains() - if not mydomains == None and mydomains.has_key(domain): + if mydomains is not None and domain in mydomains: domain_verified = True else: domain_verified = False return domain_verified + if __name__ == "__main__": access_policy_group = conf.add_cli_parser_option_group( - _("Access Policy Options") - ) + _("Access Policy Options") + ) + + access_policy_group.add_option( + "--drop-tables", + dest="drop_tables", + action="store_true", + default=False, + help=_("Drop the caching tables from the database and exit.") + ) + + access_policy_group.add_option( + "--timeout", + dest="timeout", + action="store", + default=10, + help=_("SMTP Policy request timeout.") + ) - access_policy_group.add_option( "--drop-tables", - dest = "drop_tables", - action = "store_true", - default = False, - help = _("Drop the caching tables from the " + \ - "database and exit.")) - - access_policy_group.add_option( "--timeout", - dest = "timeout", - action = "store", - default = 10, - help = _("SMTP Policy request timeout.")) - - access_policy_group.add_option( "--verify-recipient", - dest = "verify_recipient", - action = "store_true", - default = False, - help = _("Verify the recipient access policy.")) - - access_policy_group.add_option( "--verify-sender", - dest = "verify_sender", - action = "store_true", - default = False, - help = _("Verify the sender access policy.")) - - access_policy_group.add_option( "--allow-unauthenticated", - dest = "allow_unauthenticated", - action = "store_true", - default = False, - help = _("Allow unauthenticated senders.")) + access_policy_group.add_option( + "--verify-recipient", + dest="verify_recipient", + action="store_true", + default=False, + help=_("Verify the recipient access policy.") + ) + + access_policy_group.add_option( + "--verify-sender", + dest="verify_sender", + action="store_true", + default=False, + help=_("Verify the sender access policy.") + ) + + access_policy_group.add_option( + "--allow-unauthenticated", + dest="allow_unauthenticated", + action="store_true", + default=False, + help=_("Allow unauthenticated senders.") + ) conf.finalize_conf() @@ -1647,7 +1674,8 @@ policy_request = read_request_input() instance = policy_request'instance' log.debug(_("Got request instance %s") % (instance)) - if policy_requests.has_key(instance): + + if instance in policy_requests: policy_requestsinstance.add_request(policy_request) else: policy_requestsinstance = PolicyRequest(policy_request) @@ -1655,14 +1683,14 @@ protocol_state = policy_request'protocol_state'.strip().lower() log.debug( - _("Request instance %s is in state %s") % ( - instance, - protocol_state - ) + _("Request instance %s is in state %s") % ( + instance, + protocol_state ) + ) if not protocol_state == 'data': - print "action=DUNNO\n\n" + print("action=DUNNO\n\n") sys.stdout.flush() # We can recognize being in the DATA part by the recipient_count being @@ -1679,13 +1707,12 @@ sender_allowed = True if conf.verify_recipient: - recipient_allowed = \ - policy_requestsinstance.verify_recipients() + recipient_allowed = policy_requestsinstance.verify_recipients() else: recipient_allowed = True - except Exception, errmsg: + except Exception as errmsg: import traceback log.error(_("Unhandled exception caught: %r") % (errmsg)) log.error(traceback.format_exc())
View file
pykolab-0.8.10.tar.gz/configure.ac -> pykolab-0.8.11.tar.gz/configure.ac
Changed
@@ -1,4 +1,4 @@ -AC_INIT(pykolab, 0.8.10) +AC_INIT(pykolab, 0.8.11) AC_SUBST(RELEASE, 1) AC_CONFIG_SRCDIR(pykolab/constants.py.in)
View file
pykolab-0.8.10.tar.gz/kolabd/__init__.py -> pykolab-0.8.11.tar.gz/kolabd/__init__.py
Changed
@@ -202,6 +202,10 @@ os.close(1) os.close(2) + os.open(os.devnull, os.O_RDONLY) + os.open(os.devnull, os.O_WRONLY) + os.open(os.devnull, os.O_WRONLY) + log.remove_stdout_handler() self.set_signal_handlers() self.write_pid()
View file
pykolab-0.8.10.tar.gz/pykolab/auth/ldap/__init__.py -> pykolab-0.8.11.tar.gz/pykolab/auth/ldap/__init__.py
Changed
@@ -502,10 +502,12 @@ else: base_dn = config_base_dn + _filter = "(%s=%s)" % (unique_attribute, ldap.filter.escape_filter_chars(entry_id)) + _search = self.ldap.search_ext( base_dn, ldap.SCOPE_SUBTREE, - '(%s=%s)' % (unique_attribute, entry_id), + _filter, 'entrydn' ) @@ -718,7 +720,7 @@ __filter_prefix = "(&" __filter_suffix = "(!(%s=%s)))" % ( self.config_get('unique_attribute'), - exclude_entry_id + ldap.filter.escape_filter_chars(exclude_entry_id) ) else: @@ -794,7 +796,7 @@ __filter_prefix = "(&" __filter_suffix = "(!(%s=%s)))" % ( self.config_get('unique_attribute'), - exclude_entry_id + ldap.filter.escape_filter_chars(exclude_entry_id) ) else: @@ -845,11 +847,8 @@ attrsonly=True ) - _entry_dns = - - for _result in _results: - (_entry_id, _entry_attrs) = _result - _entry_dns.append(_entry_id) + # Remove referrals + _entry_dns = _e for _e in _results if _e0 is not None return _entry_dns @@ -1232,7 +1231,7 @@ else: base_dn = config_base_dn - return self._search( + _results = self._search( base_dn, filterstr=_filter, attrlist= @@ -1241,6 +1240,11 @@ override_search='_regular_search' ) + # Remove referrals + _entry_dns = _e for _e in _results if _e0 is not None + + return _entry_dns + def set_entry_attribute(self, entry_id, attribute, value): log.debug(_("Setting entry attribute %r to %r for %r") % (attribute, value, entry_id), level=8) self.set_entry_attributes(entry_id, { attribute: value }) @@ -1296,7 +1300,7 @@ if not conf.resync: modified_after = self.get_latest_sync_timestamp() else: - modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format') + modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format').replace('%%', '%') if modifytimestamp_format == None: modifytimestamp_format = "%Y%m%d%H%M%SZ" @@ -1306,7 +1310,7 @@ _filter = "(&%s(modifytimestamp>=%s))" % (_filter,modified_after) - log.debug(_("Using filter %r") % (_filter), level=8) + log.debug(_("Synchronization is using filter %r") % (_filter), level=8) if not mode == 0: override_search = mode @@ -2304,7 +2308,8 @@ # The list of naming contexts in the LDAP server attrs = self.get_entry_attributes("", 'namingContexts') - naming_contexts = attrs'namingcontexts' + # Lower case of naming contexts - primarily for AD + naming_contexts = utils.normalize(attrs'namingcontexts') if isinstance(naming_contexts, basestring): naming_contexts = naming_contexts @@ -2643,6 +2648,9 @@ # Typical for Persistent Change Control EntryChangeNotification if kw.has_key('change_type'): + + log.debug(_("change_type defined, typical for Persistent Change Control EntryChangeNotification"), level=5) + change_type = None change_dict = { @@ -2693,6 +2701,9 @@ # Typical for Paged Results Control elif kw.has_key('entry') and isinstance(kw'entry', list): + + log.debug(_("No change_type, typical for Paged Results Control"), level=5) + for entry_dn,entry_attrs in kw'entry': # This is a referral if entry_dn == None: @@ -2703,7 +2714,7 @@ for attr in entry_attrs.keys(): entryattr.lower() = entry_attrsattr - unique_attr = self.config_get('unique_attribute') + unique_attr = self.config_get('unique_attribute').lower() entry'id' = entryunique_attr try: @@ -2711,7 +2722,7 @@ except: entry'type' = "unknown" - log.debug(_("Entry type: %s") % (entry'type'), level=8) + log.debug(_("Entry type for dn: %s is: %s") % (entry'dn', entry'type'), level=8) eval("self._change_none_%s(entry, None)" % (entry'type')) @@ -2878,6 +2889,9 @@ break + # Remove referrals + _result_data = _e for _e in _result_data if _e0 is not None + if callback: callback(entry=_result_data)
View file
pykolab-0.8.10.tar.gz/pykolab/auth/ldap/cache.py -> pykolab-0.8.11.tar.gz/pykolab/auth/ldap/cache.py
Changed
@@ -31,6 +31,8 @@ from sqlalchemy import create_engine from sqlalchemy.orm import mapper +from uuid import UUID + try: from sqlalchemy.orm import relationship except: @@ -63,7 +65,7 @@ self.uniqueid = uniqueid self.result_attribute = result_attr - modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format') + modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format').replace('%%', '%') if modifytimestamp_format == None: modifytimestamp_format = "%Y%m%d%H%M%SZ" @@ -110,52 +112,59 @@ _entry = None db = init_db(domain) + + try: + _uniqueid = str(UUID(bytes_le=entry'id')) + log.debug(_("Entry uniqueid was converted from binary form to string: %s") % _uniqueid, level=8) + except ValueError: + _uniqueid = entry'id' + try: - _entry = db.query(Entry).filter_by(uniqueid=entry'id').first() + _entry = db.query(Entry).filter_by(uniqueid=_uniqueid).first() except sqlalchemy.exc.OperationalError, errmsg: db = init_db(domain,reinit=True) except sqlalchemy.exc.InvalidRequestError, errmsg: db = init_db(domain,reinit=True) finally: - _entry = db.query(Entry).filter_by(uniqueid=entry'id').first() + _entry = db.query(Entry).filter_by(uniqueid=_uniqueid).first() if not update: return _entry if _entry == None: - log.debug(_("Inserting cache entry %r") % (entry'id'), level=8) + log.debug(_("Inserting cache entry %r") % (_uniqueid), level=8) if not entry.has_key(result_attribute): entryresult_attribute = '' db.add( Entry( - entry'id', + _uniqueid, entryresult_attribute, entry'modifytimestamp' ) ) db.commit() - _entry = db.query(Entry).filter_by(uniqueid=entry'id').first() + _entry = db.query(Entry).filter_by(uniqueid=_uniqueid).first() else: - modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format') + modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format').replace('%%', '%') 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) + log.debug(_("Updating timestamp for cache entry %r") % (_uniqueid), level=8) 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() + _entry = db.query(Entry).filter_by(uniqueid=_uniqueid).first() if entry.has_key(result_attribute): if not _entry.result_attribute == entryresult_attribute: - log.debug(_("Updating result_attribute for cache entry %r") % (entry'id'), level=8) + log.debug(_("Updating result_attribute for cache entry %r") % (_uniqueid), level=8) _entry.result_attribute = entryresult_attribute db.commit() - _entry = db.query(Entry).filter_by(uniqueid=entry'id').first() + _entry = db.query(Entry).filter_by(uniqueid=_uniqueid).first() return _entry @@ -189,7 +198,7 @@ return dbdomain def last_modify_timestamp(domain): - modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format') + modifytimestamp_format = conf.get_raw('ldap', 'modifytimestamp_format').replace('%%', '%') if modifytimestamp_format == None: modifytimestamp_format = "%Y%m%d%H%M%SZ"
View file
pykolab-0.8.10.tar.gz/pykolab/cli/cmd_add_user_subscription.py -> pykolab-0.8.11.tar.gz/pykolab/cli/cmd_add_user_subscription.py
Changed
@@ -31,7 +31,12 @@ conf = pykolab.getConf() def __init__(): - commands.register('add_user_subscription', execute, description=description()) + commands.register( + 'add_user_subscription', + execute, + aliases='aus', 'subscribe', + description=description() + ) def description(): return _("Subscribe a user to a folder.")
View file
pykolab-0.8.10.tar.gz/pykolab/cli/cmd_list_mailbox_metadata.py -> pykolab-0.8.11.tar.gz/pykolab/cli/cmd_list_mailbox_metadata.py
Changed
@@ -31,7 +31,7 @@ conf = pykolab.getConf() def __init__(): - commands.register('list_mailbox_metadata', execute, description=description()) + commands.register('list_mailbox_metadata', execute, aliases='lmm', description=description()) def cli_options(): my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
View file
pykolab-0.8.10.tar.gz/pykolab/cli/cmd_list_user_subscriptions.py -> pykolab-0.8.11.tar.gz/pykolab/cli/cmd_list_user_subscriptions.py
Changed
@@ -30,7 +30,7 @@ conf = pykolab.getConf() def __init__(): - commands.register('list_user_subscriptions', execute, description=description()) + commands.register('list_user_subscriptions', execute, aliases='lus', description=description()) def cli_options(*args, **kw): my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
View file
pykolab-0.8.10.tar.gz/pykolab/cli/cmd_mailbox_cleanup.py -> pykolab-0.8.11.tar.gz/pykolab/cli/cmd_mailbox_cleanup.py
Changed
@@ -38,12 +38,23 @@ def cli_options(): my_option_group = conf.add_cli_parser_option_group(_("CLI Options")) my_option_group.add_option( - '--dry-run', - dest = "dryrun", - action = "store_true", - default = False, - help = _("Do not actually delete mailboxes, but report what mailboxes would have been deleted.") + '--dry-run', + dest = "dryrun", + action = "store_true", + default = False, + help = _( + "Do not actually delete mailboxes, but report what mailboxes would have been deleted." ) + ) + + my_option_group.add_option( + '--server', + dest = "connect_server", + action = "store", + default = None, + metavar = "SERVER", + help = _("Evaluate mailboxes on server SERVER only.") + ) def description(): return _("Clean up mailboxes that do no longer have an owner.") @@ -57,7 +68,11 @@ domains = auth.list_domains() imap = IMAP() - imap.connect() + + if not conf.connect_server == None: + imap.connect(server=conf.connect_server) + else: + imap.connect() domain_folders = {}
View file
pykolab-0.8.10.tar.gz/pykolab/cli/cmd_remove_user_subscription.py -> pykolab-0.8.11.tar.gz/pykolab/cli/cmd_remove_user_subscription.py
Changed
@@ -31,7 +31,12 @@ conf = pykolab.getConf() def __init__(): - commands.register('remove_user_subscription', execute, description=description()) + commands.register( + 'remove_user_subscription', + execute, + aliases='rus', 'unsubscribe', + description=description() + ) def description(): return _("Unsubscribe a user from a folder.")
View file
pykolab-0.8.10.tar.gz/pykolab/constants.py.in -> pykolab-0.8.11.tar.gz/pykolab/constants.py.in
Changed
@@ -118,3 +118,9 @@ # 'func': '_sync_repl' # } # } + +# Binay attributes which should not be stripped +BINARY_ATTRS = ( + 'objectguid', + 'objectsid' + )
View file
pykolab-0.8.10.tar.gz/pykolab/setup/setup_ldap.py -> pykolab-0.8.11.tar.gz/pykolab/setup/setup_ldap.py
Changed
@@ -326,8 +326,8 @@ if os.path.isfile("/usr/sbin/setup-ds-admin.pl"): setup_ds_admin = "/usr/sbin/setup-ds-admin.pl" - #elif os.path.isfile("/usr/sbin/setup-ds-admin"): - #setup_ds_admin = "/usr/sbin/setup-ds-admin" + elif os.path.isfile("/usr/sbin/setup-ds-admin"): + setup_ds_admin = "/usr/sbin/setup-ds-admin" elif os.path.isfile("/usr/sbin/setup-ds.pl"): setup_ds_admin = "/usr/sbin/setup-ds.pl" elif os.path.isfile("/usr/sbin/setup-ds"): @@ -417,7 +417,7 @@ schema_error = True if os.path.isfile('/bin/systemctl'): - subprocess.call('/bin/systemctl', 'restart', 'dirsrv.target') + subprocess.call('/bin/systemctl', 'restart', 'dirsrv@' + _input'hostname') time.sleep(20) elif os.path.isfile('/sbin/service'): subprocess.call('/sbin/service', 'dirsrv', 'restart') @@ -429,7 +429,7 @@ log.error(_("Could not start the directory server service.")) if os.path.isfile('/bin/systemctl'): - subprocess.call('/bin/systemctl', 'enable', 'dirsrv.target') + subprocess.call('/bin/systemctl', 'enable', 'dirsrv@' + _input'hostname') elif os.path.isfile('/sbin/chkconfig'): subprocess.call('/sbin/chkconfig', 'dirsrv', 'on') elif os.path.isfile('/usr/sbin/update-rc.d'):
View file
pykolab-0.8.10.tar.gz/pykolab/setup/setup_mta.py -> pykolab-0.8.11.tar.gz/pykolab/setup/setup_mta.py
Changed
@@ -487,6 +487,9 @@ if os.path.isfile('/lib/systemd/system/amavis.service'): amavisservice = 'amavis.service' + if os.path.isfile('/etc/init.d/amavis'): + amavisservice = 'amavis.service' + if os.path.isfile('/usr/lib/systemd/system/clamd.service'): clamavservice = 'clamd.service'
View file
pykolab-0.8.10.tar.gz/pykolab/setup/setup_php.py -> pykolab-0.8.11.tar.gz/pykolab/setup/setup_php.py
Changed
@@ -84,6 +84,13 @@ else: # Search and destroy php_ini = "/etc/php.ini" + + if not os.path.isfile(php_ini): + php_ini = "/etc/php/7.2/apache2/php.ini" + + if not os.path.isfile(php_ini): + php_ini = "/etc/php/7.0/apache2/php.ini" + if not os.path.isfile(php_ini): php_ini = "/etc/php5/apache2/php.ini"
View file
pykolab-0.8.10.tar.gz/pykolab/utils.py -> pykolab-0.8.11.tar.gz/pykolab/utils.py
Changed
@@ -364,7 +364,12 @@ if _objectkey is None: continue - val = map(_strip, _objectkey) + # Dont run strip anything from attributes which + # hold byte strings + if key.lower() in constants.BINARY_ATTRS: + val = _objectkey + else: + val = map(_strip, _objectkey) if len(val) == 1: resultkey.lower() = ''.join(val)
View file
pykolab-0.8.10.tar.gz/pykolab/xml/event.py -> pykolab-0.8.11.tar.gz/pykolab/xml/event.py
Changed
@@ -822,7 +822,7 @@ raise ValueError, _("Invalid classification %r") % (classification) def set_created(self, _datetime=None): - if _datetime == None: + if _datetime is None or isinstance(_datetime, datetime.time): _datetime = datetime.datetime.utcnow() self.event.setCreated(xmlutils.to_cdatetime(_datetime, False, True)) @@ -1012,7 +1012,7 @@ if isinstance(_datetime, datetime.datetime): valid_datetime = True - if _datetime == None: + if _datetime is None or isinstance(_datetime, datetime.time): valid_datetime = True _datetime = datetime.datetime.utcnow()
View file
pykolab-0.8.10.tar.gz/pykolab/xml/utils.py -> pykolab-0.8.11.tar.gz/pykolab/xml/utils.py
Changed
@@ -85,28 +85,16 @@ datetime = _datetime.replace(tzinfo=pytz.utc) with_timezone = False - ( - year, - month, - day, - ) = ( - _datetime.year, - _datetime.month, - _datetime.day, - ) + # Sometimes we deal with dummy 00000000T000000 values from iCalendar + # in such cases we end up with datetime.time objects + if not hasattr(_datetime, 'year'): + (year, month, day) = (1970, 1, 1) + else: + (year, month, day) = (_datetime.year, _datetime.month, _datetime.day) if hasattr(_datetime, 'hour'): - ( - hour, - minute, - second - ) = ( - _datetime.hour, - _datetime.minute, - _datetime.second - ) + (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)
View file
pykolab-0.8.10.tar.gz/share/templates/roundcubemail/config.inc.php.tpl -> pykolab-0.8.11.tar.gz/share/templates/roundcubemail/config.inc.php.tpl
Changed
@@ -105,10 +105,23 @@ \$config'message_sort_col' = 'date'; + \$config'spellcheck_engine' = 'pspell'; \$config'spellcheck_dictionary' = true; \$config'spellcheck_ignore_caps' = true; \$config'spellcheck_ignore_nums' = true; \$config'spellcheck_ignore_syms' = true; + \$config'spellcheck_languages' = array( + 'da' => 'Dansk', + 'de' => 'Deutsch', + 'en' => 'English', + 'es' => 'Español', + 'fr' => 'Français', + 'it' => 'Italiano', + 'nl' => 'Nederlands', + 'pt' => 'Português', + 'ru' => 'Русский', + 'sv' => 'Svenska' + ); \$config'undo_timeout' = 10; \$config'upload_progress' = 2; @@ -233,4 +246,6 @@ \$config'fileapi_manticore' = 'http://' . \$_SERVER'HTTP_HOST' . ':8080'; + @include('/etc/roundcubemail/kolab_syncroton.inc.php'); + ?>
View file
pykolab-0.8.10.tar.gz/tests/unit/test-003-event.py -> pykolab-0.8.11.tar.gz/tests/unit/test-003-event.py
Changed
@@ -1005,6 +1005,24 @@ self.assertEqual(rdates, "20140530T110000", "20140620T110000", "20140815T100000") + def test_029_dummy_datetime(self): + ical = """ +BEGIN:VEVENT +UID:8515D49BA15EFF7DB34F080877BE11F5-D1F2672D6F04F316 +DTSTAMP:00000000T000000 +DTSTART:20190514T060000 +DTEND:20190514T073000 +SUMMARY:Summary +SEQUENCE:1 +CREATED:00000000T000000 +LAST-MODIFIED:00000000T000000 +ORGANIZER:MAILTO:tests@test.com +END:VEVENT +""" + event = event_from_ical(ical) + self.assertEqual(str(event.get_lastmodified()), "1970-01-01 00:00:00+00:00") + + def _find_prop_in_list(self, diff, name): for prop in diff: if prop'property' == name:
View file
pykolab-0.8.10.tar.gz/wallace/__init__.py -> pykolab-0.8.11.tar.gz/wallace/__init__.py
Changed
@@ -286,11 +286,18 @@ filepath = os.path.join(root, filename) try: + # ignore calls on too young files if os.stat(filepath).st_mtime + 150 > time.time(): - log.debug("Skipping %s" % (filepath), level=8) + log.debug("File not more than 150s old. Skipping %s" % (filepath), level=8) continue - except: + # ignore calls on lock files + if '/locks/' in filepath: + log.debug("File is in locks directory. Skipping %s" % (filepath), level=8) + continue + + except Exception, errmsg: + log.error("Error: %s. Skipping %s" % (errmsg, filepath)) continue if not root == pickup_path: @@ -501,6 +508,10 @@ os.close(1) os.close(2) + os.open(os.devnull, os.O_RDONLY) + os.open(os.devnull, os.O_WRONLY) + os.open(os.devnull, os.O_WRONLY) + log.remove_stdout_handler() self.set_signal_handlers() self.write_pid()
View file
pykolab-0.8.10.tar.gz/wallace/module_invitationpolicy.py -> pykolab-0.8.11.tar.gz/wallace/module_invitationpolicy.py
Changed
@@ -19,6 +19,7 @@ import datetime import os +import random import signal import tempfile import time
View file
pykolab-0.8.10.tar.gz/wallace/module_resources.py -> pykolab-0.8.11.tar.gz/wallace/module_resources.py
Changed
@@ -324,7 +324,11 @@ continue # ignore updates and cancellations to resource collections who already delegated the event - if len(receiving_attendee.get_delegated_to()) > 0 or receiving_attendee.get_role() == kolabformat.NonParticipant: + att_delegated = (len(receiving_attendee.get_delegated_to()) > 0) + att_nonpart = (receiving_attendee.get_role() == kolabformat.NonParticipant) + att_rsvp = receiving_attendee.get_rsvp() + + if (att_delegated or att_nonpart) and not att_rsvp: done = True log.debug(_("Recipient %r is non-participant, ignoring message") % (receiving_resource'mail'), level=8) @@ -334,12 +338,14 @@ if resourcesresource'mail' in a.get_email() for a in itip_event'xml'.get_attendees() \ and resourcesresource.has_key('kolabtargetfolder'): (event, master) = find_existing_event(itip_event'uid', itip_event'recurrence-id', resourcesresource) + if event is None: + log.debug(_("Cancellation for an event %r: not found, skipping") % (itip_event'uid'), level=8) # remove entire event - if event and master is None: + elif master is None: log.debug(_("Cancellation for entire event %r: deleting") % (itip_event'uid'), level=8) delete_resource_event(itip_event'uid', resourcesresource, event._msguid) # just cancel one single occurrence: add exception with status=cancelled - elif master is not None: + else: log.debug(_("Cancellation for a single occurrence %r of %r: updating...") % (itip_event'recurrence-id', itip_event'uid'), level=8) event.set_status('CANCELLED') event.set_transparency(True) @@ -358,7 +364,7 @@ # do the magic for the receiving attendee (available_resource, itip_event) = check_availability(itip_events, resource_dns, resources, receiving_attendee) - reject = False + _reject = False resource = None original_resource = None @@ -401,10 +407,10 @@ if invitationpolicy is not None: for policy in invitationpolicy: if policy & ACT_REJECT: - reject = True + _reject = True break - if resource is not None and not reject: + if resource is not None and not _reject: log.debug(_("Accept invitation for individual resource %r / %r") % (resource'dn', resource'mail'), level=8) accept_reservation_request(itip_event, resource, original_resource, False, invitationpolicy) else:
View file
pykolab-0.8.10.tar.gz/wallace/modules.py -> pykolab-0.8.11.tar.gz/wallace/modules.py
Changed
@@ -332,13 +332,17 @@ # parse message headers message = Parser().parse(open(filepath, 'r'), True) + messageid = message'message-id' if 'message-id' in message else None sender = formataddr(x) for x in getaddresses(message.get_all('X-Kolab-From', )) recipients = formataddr(x) for x in getaddresses(message.get_all('X-Kolab-To', )) - log.debug(_("recipients: %r") % (recipients)) + log.debug( + _("Message-ID: %s, sender: %r, recipients: %r") % (messageid, sender, recipients), level=6 + ) # delete X-Kolab-* headers del message'X-Kolab-From' del message'X-Kolab-To' + log.debug(_("Removed X-Kolab- headers"), level=8) result = _sendmail( sender,
View file
pykolab.dsc
Changed
@@ -2,7 +2,7 @@ Source: pykolab Binary: pykolab, kolab-cli, kolab-conf, kolab-saslauthd, kolab-server, kolab-telemetry, kolab-xml, wallace Architecture: all -Version: 0.8.10-0~kolab2 +Version: 0.8.11-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org @@ -40,5 +40,5 @@ pykolab deb python optional wallace deb python optional Files: - 00000000000000000000000000000000 0 pykolab-0.8.10.tar.gz + 00000000000000000000000000000000 0 pykolab-0.8.11.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.