summaryrefslogtreecommitdiffstats
path: root/pykolab
diff options
context:
space:
mode:
authorJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2011-07-04 14:32:58 +0100
committerJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2011-07-04 14:32:58 +0100
commit209af0b2cb5529fff2ae7db5bc30cf14e1a7c7d6 (patch)
treee8fb8b4093c7b48302aee5cd897da3065231f7a6 /pykolab
parent3a55c071692e1846d4a36ae4589d6924360c49cd (diff)
downloadpykolab-209af0b2cb5529fff2ae7db5bc30cf14e1a7c7d6.tar.gz
Provide supportControls search before actual search, but use normal search where appropriate
Diffstat (limited to 'pykolab')
-rw-r--r--pykolab/auth/__init__.py17
-rw-r--r--pykolab/auth/ldap/__init__.py593
2 files changed, 475 insertions, 135 deletions
diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index 17ab4b8..3e44532 100644
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -127,6 +127,10 @@ class Auth(object):
self._auth[domain]._disconnect()
+ def find_user(self, attr, value, domain=None):
+ self.connect(domain)
+ return self._auth[domain]._find_user(attr, value, domain=domain)
+
def list_domains(self):
"""
List the domains using the auth_mechanism setting in the kolab
@@ -152,12 +156,19 @@ class Auth(object):
return domains
- def list_users(self, primary_domain, secondary_domains=[]):
+ def list_users(self, primary_domain, secondary_domains=[], callback=None):
self.connect(domain=primary_domain)
- users = self._auth[primary_domain]._list_users(primary_domain, secondary_domains)
+ users = self._auth[primary_domain]._list_users(
+ primary_domain,
+ secondary_domains,
+ callback
+ )
self.disconnect(domain=primary_domain)
return users
+ def synchronize(self, primary_domain, secondary_domains=[]):
+ self.list_users(primary_domain, secondary_domains, callback=self._auth[primary_domain].sync_user)
+
def domain_default_quota(self, domain):
self.connect(domain=domain)
return self._auth[domain]._domain_default_quota(domain)
@@ -167,7 +178,9 @@ class Auth(object):
return self._auth[domain]._domain_section(domain)
def get_user_attribute(self, domain, user, attribute):
+ self.connect(domain=domain)
return self._auth[domain]._get_user_attribute(user, attribute)
def set_user_attribute(self, domain, user, attribute, value):
+ self.connect(domain=domain)
self._auth[domain]._set_user_attribute(user, attribute, value)
diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 162c266..309ed58 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -36,11 +36,17 @@ conf = pykolab.getConf()
# Catch python-ldap-2.4 changes
from distutils import version
+
if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
LDAP_CONTROL_PAGED_RESULTS = ldap.CONTROL_PAGEDRESULTS
else:
LDAP_CONTROL_PAGED_RESULTS = ldap.LDAP_CONTROL_PAGE_OID
+try:
+ from ldap.controls import psearch
+except:
+ log.warning(_("Python LDAP library does not support persistent search"))
+
class SimplePagedResultsControl(ldap.controls.SimplePagedResultsControl):
"""
@@ -126,12 +132,12 @@ class LDAP(object):
trace_level = 0
- # TODO: Perhaps we can be smarter then this!
- if conf.debuglevel >= 9:
+ if conf.debuglevel > 8:
trace_level = 1
self.ldap = ldap.initialize(uri, trace_level=trace_level)
self.ldap.protocol_version = 3
+ self.ldap.supported_controls = []
def _bind(self):
# TODO: Implement some mechanism for r/o, r/w and mgmt binding.
@@ -236,19 +242,147 @@ class LDAP(object):
return _user_dn
- def _search(self,
+ def _find_user(self, attr, value, domain=None):
+ self._connect()
+ self._bind()
+
+ if domain == None:
+ domain = conf.get('kolab', 'primary_domain')
+
+ domain_root_dn = self._kolab_domain_root_dn(domain)
+
+ if conf.has_option(domain_root_dn, 'user_base_dn'):
+ section = domain_root_dn
+ else:
+ section = 'ldap'
+
+ user_base_dn = conf.get_raw(
+ section,
+ 'user_base_dn'
+ ) %({'base_dn': domain_root_dn})
+
+ search_filter = "(%s=%s)" %(
+ attr,
+ value
+ )
+
+ _results = self.ldap.search_s(
+ user_base_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr=search_filter,
+ attrlist=[ 'dn' ]
+ )
+
+ if len(_results) == 1:
+ (_user_dn, _user_attrs) = _results[0]
+ else:
+ # Retry to find the user_dn with just uid=%s against the root_dn,
+ # if the login is not fully qualified
+ if len(login.split('@')) < 2:
+ search_filter = "(uid=%s)" %(login)
+ _results = self.ldap.search_s(
+ domain_root_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr=search_filter,
+ attrlist=[ 'dn' ]
+ )
+ if len(_results) == 1:
+ (_user_dn, _user_attrs) = _results[0]
+ else:
+ # Overall fail
+ return False
+ else:
+ return False
+
+ return _user_dn
+
+ def _persistent_search(self,
base_dn,
scope=ldap.SCOPE_SUBTREE,
filterstr="(objectClass=*)",
attrlist=None,
attrsonly=0,
- timeout=-1
+ timeout=-1,
+ callback=False,
+ primary_domain=None,
+ secondary_domains=[]
):
-
_results = []
+ psearch_server_controls = []
+
+ psearch_server_controls.append(psearch.PersistentSearchControl(
+ criticality=True,
+ changeTypes=[ 'add', 'delete', 'modify', 'modDN' ],
+ changesOnly=False,
+ returnECs=True
+ )
+ )
+
+ _search = self.ldap.search_ext(
+ base_dn,
+ scope=scope,
+ filterstr=filterstr,
+ attrlist=attrlist,
+ attrsonly=attrsonly,
+ timeout=timeout,
+ serverctrls=psearch_server_controls
+ )
+
+ while True:
+ res_type,res_data,res_msgid,_,_,_ = self.ldap.result4(
+ _search,
+ all=0,
+ add_ctrls=1,
+ add_intermediates=1,
+ resp_ctrl_classes={psearch.EntryChangeNotificationControl.controlType:psearch.EntryChangeNotificationControl}
+ )
+
+ change_type = None
+ previous_dn = None
+ change_number = None
+
+ for dn,entry,srv_ctrls in res_data:
+ ecn_ctrls = [
+ c for c in srv_ctrls
+ if c.controlType == psearch.EntryChangeNotificationControl.controlType
+ ]
+
+ if ecn_ctrls:
+ change_type = ecn_ctrls[0].changeType
+ previous_dn = ecn_ctrls[0].previousDN
+ change_number = ecn_ctrls[0].changeNumber
+ change_type_desc = psearch.CHANGE_TYPES_STR[change_type]
+ #if change_type == 'modDN':
+ #log.info(_("Object relocated from %r to %r") %(previous_dn,dn))
+ #else:
+ #log.info(_("Object %r modified (%r)") %(dn,change_type_desc))
+
+ if callback:
+ callback(
+ dn=dn,
+ previous_dn=previous_dn,
+ change_type=change_type,
+ change_number=change_number,
+ primary_domain=primary_domain,
+ secondary_domains=secondary_domains
+ )
+
+ def _paged_search(self,
+ base_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr="(objectClass=*)",
+ attrlist=None,
+ attrsonly=0,
+ timeout=-1,
+ callback=False,
+ primary_domain=None,
+ secondary_domains=[]
+ ):
+
page_size = 500
critical = True
+ _results = []
server_page_control = SimplePagedResultsControl(page_size=page_size)
@@ -275,6 +409,14 @@ class LDAP(object):
except ldap.NO_SUCH_OBJECT, e:
log.warning(_("Object %s searched no longer exists") %(base_dn))
break
+
+ if callback:
+ callback(
+ user=_result_data,
+ primary_domain=primary_domain,
+ secondary_domains=secondary_domains
+ )
+
_results.extend(_result_data)
if (pages % 2) == 0:
log.debug(_("%d results...") %(len(_results)))
@@ -288,7 +430,7 @@ class LDAP(object):
size = pctrls[0].size
cookie = pctrls[0].cookie
if cookie:
- server_page_control.controlValue = (page_size, cookie)
+ server_page_control.cookie = cookie
_search = self.ldap.search_ext(
base_dn,
scope=scope,
@@ -307,6 +449,87 @@ class LDAP(object):
return _results
+ def _vlv_search(self,
+ base_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr="(objectClass=*)",
+ attrlist=None,
+ attrsonly=0,
+ timeout=-1,
+ callback=False,
+ primary_domain=None,
+ secondary_domains=[]
+ ):
+ pass
+
+ def _search(self,
+ base_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr="(objectClass=*)",
+ attrlist=None,
+ attrsonly=0,
+ timeout=-1,
+ override_search=False,
+ callback=False,
+ primary_domain=None,
+ secondary_domains=[]
+ ):
+ """
+ Search LDAP.
+
+ Use the priority ordered SUPPORTED_LDAP_CONTROLS and use
+ the first one supported.
+ """
+
+ if len(self.ldap.supported_controls) < 1:
+ for control_num in SUPPORTED_LDAP_CONTROLS.keys():
+ log.debug(_("Checking for support for %s") %(SUPPORTED_LDAP_CONTROLS[control_num]['desc']), level=8)
+
+ _search = self.ldap.search_s(
+ '',
+ scope=ldap.SCOPE_BASE,
+ attrlist=['supportedControl']
+ )
+
+ for (_result,_supported_controls) in _search:
+ supported_controls = _supported_controls.values()[0]
+ for control_num in SUPPORTED_LDAP_CONTROLS.keys():
+ if SUPPORTED_LDAP_CONTROLS[control_num]['oid'] in supported_controls:
+ self.ldap.supported_controls.append(SUPPORTED_LDAP_CONTROLS[control_num]['func'])
+
+ _results = []
+
+ if not override_search == False:
+ _use_ldap_controls = [ override_search ]
+ else:
+ _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
+ )
+ )
+
+ return _results
+
def _result(self, msgid=ldap.RES_ANY, all=1, timeout=-1):
return self.ldap.result(msgid, all, timeout)
@@ -331,15 +554,26 @@ class LDAP(object):
def _get_user_attribute(self, user, attribute):
self._bind()
- result = self._search(
+ attribute = attribute.lower()
+
+ _result_type = None
+
+ _search = self.ldap.search_ext(
user['dn'],
ldap.SCOPE_BASE,
'(objectclass=*)',
[ attribute ]
)
- if len(result) >= 1:
- (user_dn, user_attrs) = result[0]
+ (
+ _result_type,
+ _result_data,
+ _result_msgid,
+ _result_controls
+ ) = self.ldap.result3(_search)
+
+ if len(_result_data) >= 1:
+ (user_dn, user_attrs) = _result_data[0]
else:
log.warning(_("Could not get user attribute %s for %s")
%(attribute,user['dn']))
@@ -349,32 +583,46 @@ class LDAP(object):
user_attrs = utils.normalize(user_attrs)
if not user_attrs.has_key(attribute):
+ log.debug(_("Attribute wanted does not exist"), level=8)
user_attrs[attribute] = None
return user_attrs[attribute]
def _get_user_attributes(self, user, attributes):
+ _user_attrs = {}
+
self._bind()
- result = self._search(
+ _search = self.ldap.search_ext(
user['dn'],
ldap.SCOPE_BASE,
'(objectclass=*)',
attributes
)
- if len(result) >= 1:
- (user_dn, user_attrs) = result[0]
- else:
- log.warning(_("Could not get user attributes for %s") %(user['dn']))
- return None
+ try:
+ (
+ _result_type,
+ _result_data,
+ _result_msgid,
+ _result_controls
+ ) = self.ldap.result3(_search)
+
+ if len(_result_data) >= 1:
+ (user_dn, user_attrs) = _result_data[0]
+ else:
+ log.warning(_("Could not get user attributes for %s") %(user['dn']))
+ return None
- user_attrs = utils.normalize(user_attrs)
+ user_attrs = utils.normalize(user_attrs)
- # Only return the attributes requested in the function call.
- _user_attrs = []
- for _attr in attributes:
- _user_attrs[_attr] = user_attrs[_attr]
+ # Only return the attributes requested in the function call.
+ _user_attrs = {}
+ for _attr in attributes:
+ _user_attrs[_attr] = user_attrs[_attr]
+
+ except ldap.NO_SUCH_OBJECT, e:
+ log.warning(_("Object %s has changed") %(user['dn']))
return _user_attrs
@@ -446,7 +694,8 @@ class LDAP(object):
ldap.SCOPE_SUBTREE,
kolab_domain_filter,
# TODO: Where we use associateddomain is actually configurable
- [ 'associateddomain' ]
+ [ 'associateddomain' ],
+ override_search='_paged_search'
)
domains = []
@@ -486,7 +735,8 @@ class LDAP(object):
_results = self._search(
domain_base_dn,
ldap.SCOPE_SUBTREE,
- "(%s=%s)" %(domain_name_attribute,domain)
+ "(%s=%s)" %(domain_name_attribute,domain),
+ override_search='_paged_search'
)
domains = []
@@ -500,9 +750,16 @@ class LDAP(object):
if _domain_attrs.has_key(domain_rootdn_attribute):
return _domain_attrs[domain_rootdn_attribute]
+ else:
+ if conf.has_option('ldap', 'base_dn'):
+ return conf.get('ldap', 'base_dn')
+
return utils.standard_root_dn(domain)
- def _list_users(self, primary_domain, secondary_domains=[]):
+ def _list_users(self, primary_domain, secondary_domains=[], callback=None):
+
+ # Track state for psearch and paged searches.
+ self._initial_sync_done = False
log.info(_("Listing users for domain %s (and %s)")
%(primary_domain, ' '.join(secondary_domains)))
@@ -534,6 +791,19 @@ class LDAP(object):
kolab_user_filter = conf.get(section, 'kolab_user_filter', quiet=True)
+ if conf.has_option(domain_root_dn, 'kolab_user_scope'):
+ section = domain_root_dn
+ else:
+ section = 'ldap'
+
+ _kolab_user_scope = conf.get(section, 'kolab_user_scope', quiet=True)
+
+ if LDAP_SCOPE.has_key(_kolab_user_scope):
+ kolab_user_scope = LDAP_SCOPE[_kolab_user_scope]
+ else:
+ log.warning(_("LDAP Search scope %s not found, using 'sub'") %(_ldap_user_scope))
+ kolab_user_scope = ldap.SCOPE_SUBTREE
+
# TODO: Is, perhaps, a domain specific setting
result_attribute = conf.get(
'cyrus-sasl',
@@ -547,7 +817,7 @@ class LDAP(object):
# supposed to be settings.
_search = self._search(
user_base_dn,
- ldap.SCOPE_SUBTREE,
+ kolab_user_scope,
kolab_user_filter,
attrlist=[
'dn',
@@ -557,133 +827,190 @@ class LDAP(object):
'cn',
'uid'
],
- attrsonly=0
+ attrsonly=0,
+ callback=callback,
+ primary_domain=primary_domain,
+ secondary_domains=secondary_domains
)
- log.info(_("Found %d users") %(len(_search)))
+ if callback == None:
+ log.info(_("Found %d users") %(len(_search)))
- log.debug(_("Iterating over %d users, making sure we have the " + \
- "necessary attributes...") %(len(_search)), level=6)
+ log.debug(_("Iterating over %d users, making sure we have the " + \
+ "necessary attributes...") %(len(_search)), level=6)
- users = []
+ users = []
- num_users = len(_search)
- num_user = 0
+ num_users = len(_search)
+ num_user = 0
- for user_dn, user_attrs in _search:
- num_user += 1
+ for user_dn, user_attrs in _search:
+ num_user += 1
- # Placeholder for the user attributes
- user = {}
- user['dn'] = user_dn
- if not user.has_key('standard_domain'):
- user['standard_domain'] = (primary_domain, secondary_domains)
+ # Placeholder for the user attributes
+ user = user_attrs
+ user['dn'] = user_dn
- user_attrs = utils.normalize(user_attrs)
+ user = self._get_user_details(user, primary_domain, secondary_domains)
+
+ if user:
+ users.append(user)
+
+ if (num_user % 1000) == 0:
+ log.debug(
+ _("Done iterating over user %d of %d")
+ %(num_user,num_users),
+ level=3
+ )
+
+ return users
+
+ def _get_user_details(self, user, primary_domain, secondary_domains=[]):
+
+ # TODO: Is, perhaps, a domain specific setting
+ result_attribute = conf.get(
+ 'cyrus-sasl',
+ 'result_attribute',
+ quiet=True
+ )
+
+ if not user.has_key('standard_domain'):
+ user['standard_domain'] = (primary_domain, secondary_domains)
- _get_attrs = []
-
- for attribute in [
- result_attribute,
- 'sn',
- 'givenname',
- 'cn',
- 'uid'
- ]:
- if not user_attrs.has_key(attribute):
- _get_attrs.append(attribute)
- user[attribute] = self._get_user_attribute(user, attribute)
+ user = utils.normalize(user)
+
+ _get_attrs = []
+
+ for attribute in [
+ result_attribute,
+ 'sn',
+ 'givenname',
+ 'cn',
+ 'uid'
+ ]:
+ if not user.has_key(attribute):
+ _get_attrs.append(attribute)
+ #user[attribute] = self._get_user_attribute(user, attribute)
+
+ if len(_get_attrs) > 0:
+ _user_attrs = self._get_user_attributes(user, _get_attrs)
+ for key in _user_attrs.keys():
+ user[key] = _user_attrs[key]
+ else:
+ return False
+
+ domain_root_dn = self._kolab_domain_root_dn(primary_domain)
+
+ # Check to see if we want to apply a primary mail recipient policy
+ if conf.has_option(domain_root_dn, 'primary_mail'):
+ primary_mail = conf.plugins.exec_hook(
+ "set_primary_mail",
+ kw={
+ 'primary_mail':
+ conf.get_raw(
+ domain_root_dn,
+ 'primary_mail'
+ ),
+ 'user_attrs': user,
+ 'primary_domain': primary_domain,
+ 'secondary_domains': secondary_domains
+ }
+ )
+
+ if not primary_mail == None:
+ if not user.has_key('mail'):
+ self._set_user_attribute(user, 'mail', primary_mail)
+ user['mail'] = primary_mail
else:
- user[attribute] = user_attrs[attribute]
+ if not primary_mail == user['mail']:
+ self._set_user_attribute(user, 'mail', primary_mail)
+ user['old_mail'] = user['mail']
+ user['mail'] = primary_mail
- if len(_get_attrs) > 0:
- _user_attrs = self._get_user_attributes(user, _get_attrs)
- for key in _user_attrs.keys():
- user[key] = _user_attrs[key]
+ # Check to see if we want to apply a secondary mail recipient
+ # policy.
+ section = None
- # Check to see if we want to apply a primary mail recipient policy
- if conf.has_option(domain_root_dn, 'primary_mail'):
- primary_mail = conf.plugins.exec_hook(
- "set_primary_mail",
+ if conf.has_option(domain_root_dn, 'secondary_mail'):
+ section = domain_root_dn
+ elif conf.has_option('kolab', 'secondary_mail'):
+ section = 'kolab'
+
+ if not section == None:
+ # Execute the plugin hook
+ secondary_mail = conf.plugins.exec_hook(
+ "set_secondary_mail",
kw={
- 'primary_mail':
+ 'secondary_mail':
conf.get_raw(
domain_root_dn,
- 'primary_mail'
+ 'secondary_mail'
),
- 'user_attrs': user_attrs,
+ 'user_attrs': user,
'primary_domain': primary_domain,
'secondary_domains': secondary_domains
}
- )
-
- if not primary_mail == None:
- if not user.has_key('mail'):
- self._set_user_attribute(user, 'mail', primary_mail)
- user['mail'] = primary_mail
+ ) # end of conf.plugins.exec_hook() call
+
+ if not secondary_mail == None:
+ secondary_mail = list(set(secondary_mail))
+ # Avoid duplicates
+ while primary_mail in secondary_mail:
+ secondary_mail.pop(secondary_mail.index(primary_mail))
+
+ if not user.has_key('mailalternateaddress'):
+ if not len(secondary_mail) == 0:
+ self._set_user_attribute(
+ user,
+ 'mailalternateaddress',
+ secondary_mail
+ )
+
+ user['mailalternateaddress'] = secondary_mail
else:
- if not primary_mail == user['mail']:
- self._set_user_attribute(user, 'mail', primary_mail)
- user['old_mail'] = user['mail']
- user['mail'] = primary_mail
-
- # Check to see if we want to apply a secondary mail recipient
- # policy.
- section = None
-
- if conf.has_option(domain_root_dn, 'secondary_mail'):
- section = domain_root_dn
- elif conf.has_option('kolab', 'secondary_mail'):
- section = 'kolab'
-
- if not section == None:
- # Execute the plugin hook
- secondary_mail = conf.plugins.exec_hook(
- "set_secondary_mail",
- kw={
- 'secondary_mail':
- conf.get_raw(
- domain_root_dn,
- 'secondary_mail'
- ),
- 'user_attrs': user_attrs,
- 'primary_domain': primary_domain,
- 'secondary_domains': secondary_domains
- }
- ) # end of conf.plugins.exec_hook() call
-
- if not secondary_mail == None:
- secondary_mail = list(set(secondary_mail))
- # Avoid duplicates
- while primary_mail in secondary_mail:
- secondary_mail.pop(secondary_mail.index(primary_mail))
-
- if not user.has_key('mailalternateaddress'):
- if not len(secondary_mail) == 0:
- self._set_user_attribute(
- user,
- 'mailalternateaddress',
- secondary_mail
- )
-
- user['mailalternateaddress'] = secondary_mail
- else:
- if not secondary_mail == user['mailalternateaddress']:
- self._set_user_attribute(
- user,
- 'mailalternateaddress',
- secondary_mail
- )
-
- user['mailalternateaddress'] = secondary_mail
-
- users.append(user)
-
- if (num_user % 1000) == 0:
- log.debug(
- _("Done iterating over user %d of %d")
- %(num_user,num_users),
- level=3
- )
+ if not secondary_mail == user['mailalternateaddress']:
+ self._set_user_attribute(
+ user,
+ 'mailalternateaddress',
+ secondary_mail
+ )
+
+ user['mailalternateaddress'] = secondary_mail
+
+ return user
+
+ def sync_user(self, *args, **kw):
+ # See if kw['dn'] has been set.
+ if kw.has_key('dn'):
+ self.sync_ldap_user(*args, **kw)
+ elif kw.has_key('user'):
+ for user_dn, user_attrs in kw['user']:
+ _user = utils.normalize(user_attrs)
+ _user['dn'] = user_dn
+ kw['user'] = _user
+ self.sync_ldap_user(*args, **kw)
+ else:
+ # TODO: Not yet implemented
+ pass
+
+ def sync_ldap_user(self, *args, **kw):
+ user = None
+
+ if kw.has_key('change_type'):
+ # This is a EntryChangeControl notification
+ user = {'dn': kw['dn']}
+ if kw['change_type'] == None:
+ # This user has not been changed, but existed already.
+ user = self._get_user_details(user, kw['primary_domain'], kw['secondary_domains'])
+ else:
+ self._initial_sync_done = True
+
+ if kw.has_key('user'):
+ user = kw['user']
+
+ if user:
+ pykolab.imap.synchronize(users=[user], primary_domain=kw['primary_domain'], secondary_domains=kw['secondary_domains'])
- return users
+ if self._initial_sync_done:
+ log.debug(_("Initial synchronization is done, now purging old mailboxes"), level=8)
+ pykolab.imap.expunge_user_folders(inbox_folders=pykolab.imap.inbox_folders)