diff options
-rwxr-xr-x | kolabd.py | 2 | ||||
-rw-r--r-- | kolabd/__init__.py | 93 | ||||
-rw-r--r-- | pykolab/auth/__init__.py | 61 | ||||
-rw-r--r-- | pykolab/auth/ldap/__init__.py | 99 | ||||
-rw-r--r-- | pykolab/conf/__init__.py | 13 | ||||
-rw-r--r-- | pykolab/conf/defaults.py | 5 | ||||
-rw-r--r-- | pykolab/conf/runtime.py | 2 | ||||
-rw-r--r-- | pykolab/imap/__init__.py | 209 | ||||
-rw-r--r-- | pykolab/logger.py | 26 | ||||
-rw-r--r-- | pykolab/plugin/__init__.py | 0 | ||||
-rw-r--r-- | pykolab/plugin/defaultfolders/__init__.py | 68 | ||||
-rw-r--r-- | pykolab/plugin/dynamicquota/__init__.py | 55 | ||||
-rw-r--r-- | pykolab/plugin/recipientpolicy/__init__.py | 68 | ||||
-rw-r--r-- | pykolab/plugins.py | 52 |
14 files changed, 661 insertions, 92 deletions
@@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 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 74bb2be..557957d 100644 --- a/kolabd/__init__.py +++ b/kolabd/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -19,14 +19,19 @@ from optparse import OptionParser from ConfigParser import SafeConfigParser -import traceback +import os import shutil +import time +import traceback +from pykolab.auth import Auth +from pykolab.conf import Conf +from pykolab.imap import IMAP from pykolab.constants import * from pykolab.translate import _ class KolabDaemon(object): - def __init__(self, init_base=True): + def __init__(self): """ self.args == Arguments passed on the CLI self.cli_options == Parser results (again, CLI) @@ -34,48 +39,21 @@ class KolabDaemon(object): self.plugins == Our Kolab Plugins """ - self.args = None - self.cli_options = None - self.parser = None - self.plugins = None + self.conf = Conf() - # Create and parse the options - self.parse_options() + daemon_group = self.conf.parser.add_option_group(_("Daemon Options")) - def parse_options(self, load_plugins=True): - """ - Create the OptionParser for the options passed to us from runtime - Command Line Interface. - """ + daemon_group.add_option( "--fork", + dest = "fork_mode", + action = "store_true", + default = False, + help = _("For to the background.")) - epilog = "The Kolab Daemon is part of the Kolab Groupware Solution. For" + \ - "about Kolab or PyKolab, visit http://www.kolabsys.com" + self.conf.finalize_conf() - # Enterprise Linux 5 does not have an "epilog" parameter to OptionParser - try: - self.parser = OptionParser(epilog=epilog) - except: - self.parser = OptionParser() - - ## - ## Runtime Options - ## - runtime_group = self.parser.add_option_group(_("Runtime Options")) - runtime_group.add_option( "-d", "--debug", - dest = "debuglevel", - type = 'int', - default = 0, - help = _("Set the debugging verbosity. Maximum is 99")) - - ## - ## Get options from plugins - ## - if load_plugins: - self.plugins = pykolab.plugins.KolabPlugins(init=True) - self.plugins.add_options(self.parser) - - # Parse Options - (self.cli_options, self.args) = self.parser.parse_args() + self.log = self.conf.log + + self.thread_count = 0 def run(self): """Run Forest, RUN!""" @@ -83,31 +61,40 @@ class KolabDaemon(object): exitcode = 0 try: - self.base.run() + if self.conf.fork_mode: + pid = os.fork() + else: + self.do_sync() + + if pid == 0: + self.log.remove_stdout_handler() + self.do_sync() + except SystemExit, e: exitcode = e except KeyboardInterrupt: exitcode = 1 - self.base.log.info(_("Interrupted by user")) + self.log.info(_("Interrupted by user")) except AttributeError, e: exitcode = 1 traceback.print_exc() print >> sys.stderr, _("Traceback occurred, please report a bug at http://issues.kolab.org") except TypeError, e: + exitcode = 1 + traceback.print_exc() self.log.error(_("Type Error: %s") % e) except: exitcode = 2 traceback.print_exc() print >> sys.stderr, _("Traceback occurred, please report a bug at http://issues.kolab.org") - finally: - if self.base.cfg.clean_up == 0: - # Leave everything as it is - pass - if self.base.cfg.clean_up > 0: - # Remove our directories in the working directory - pass - if self.base.cfg.clean_up > 1: - # Remove everything - pass sys.exit(exitcode) + def do_sync(self): + while 1: + self.log.debug(_("Sleeping for 10 seconds..."), 5) + time.sleep(10) + auth = Auth(self.conf) + users = auth.users() + imap = IMAP(self.conf) + imap.synchronize(users) + diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py new file mode 100644 index 0000000..5a74675 --- /dev/null +++ b/pykolab/auth/__init__.py @@ -0,0 +1,61 @@ +# Copyright 2010-2011 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 2 only +# +# 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 pykolab.auth import ldap +from pykolab.auth import sql +from pykolab.conf import Conf +from pykolab.imap import IMAP + +from pykolab.translate import _ + +class Auth(object): + """ + This is the Authentication and Authorization module for PyKolab. + """ + + def __init__(self, conf=None): + """ + Initialize the authentication class. + """ + if not conf: + self.conf = Conf() + self.conf.finalize_conf() + self.log = self.conf.log + else: + self.conf = conf + self.log = conf.log + + self._auth = None + + def _connect(self): + if not self._auth == None: + return + + if self.conf.get('kolab', 'auth_mechanism') == 'ldap': + self._auth = ldap.LDAP(self.conf) + + def users(self): + self._connect() + users = self._auth._kolab_users() + return users + + def set_user_attribute(self, user, attribute, value): + print "Setting attribute %s to %s for user %s" %(attribute, value, user) + self._connect() + self._auth._set_user_attribute(user, attribute, value) + diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py new file mode 100644 index 0000000..aaa3f07 --- /dev/null +++ b/pykolab/auth/ldap/__init__.py @@ -0,0 +1,99 @@ +# Copyright 2010-2011 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 2 only +# +# 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 ldap + +from pykolab.conf import Conf +from pykolab.constants import * +from pykolab.translate import _ + +class LDAP(object): + def __init__(self, conf=None): + if not conf: + self.conf = Conf() + self.conf.finalize_conf() + self.log = self.conf.log + else: + self.conf = conf + self.log = conf.log + + self.ldap = None + + def _connect(self): + + if not self.ldap == None: + return + + self.log.debug(_("Connecting to LDAP..."), 9) + uri = self.conf.get('ldap', 'uri') + self.ldap = ldap.initialize(uri) + + def _disconnect(self): + del self.ldap + self.ldap = None + + def _set_user_attribute(self, dn, attribute, value): + self._connect() + bind_dn = self.conf.get('ldap', 'bind_dn') + bind_pw = self.conf.get('ldap', 'bind_pw') + user_base_dn = self.conf.get('ldap', 'user_base_dn') + kolab_user_filter = self.conf.get('ldap', 'kolab_user_filter') + + self.ldap.simple_bind(bind_dn, bind_pw) + + self.ldap.modify_s(dn, [(ldap.MOD_REPLACE, attribute, value)]) + + def _kolab_users(self): + self._connect() + + bind_dn = self.conf.get('ldap', 'bind_dn') + bind_pw = self.conf.get('ldap', 'bind_pw') + user_base_dn = self.conf.get('ldap', 'user_base_dn') + kolab_user_filter = self.conf.get('ldap', 'kolab_user_filter') + + self.ldap.simple_bind(bind_dn, bind_pw) + + _users = self.ldap.search_s(user_base_dn, ldap.SCOPE_SUBTREE, kolab_user_filter) + + self._disconnect() + + users = [] + + for _user in _users: + user_attrs = {} + + (user_dn, _user_attrs) = _user + _user_attrs['dn'] = user_dn + + for key in _user_attrs.keys(): + if type(_user_attrs[key]) == list: + if len(_user_attrs[key]) == 1: + user_attrs[key.lower()] = ''.join(_user_attrs[key]) + else: + user_attrs[key.lower()] = _user_attrs[key] + else: + # What the heck? + user_attrs[key.lower()] = _user_attrs[key] + + + user_attrs = self.conf.plugins.exec_hook("set_user_attrs", args=(user_attrs)) + + users.append(user_attrs) + + return users + diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py index fb37a8c..12d12e1 100644 --- a/pykolab/conf/__init__.py +++ b/pykolab/conf/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -168,6 +168,8 @@ class Conf(object): # Other then default? if not self.cli_options.config_file == self.defaults.config_file: self.config_file = self.cli_options.config_file + else: + self.config_file = self.defaults.config_file config = self.check_config() self.load_config(config) @@ -422,6 +424,13 @@ class Conf(object): self.log.debug(_("Setting %s to %r (from the default values for CLI options)") %(option, self.parser._long_opt[long_opt].default), level=9) setattr(self,option,self.parser._long_opt[long_opt].default) + def get_raw(self, section, key): + if not self.cfg_parser: + self.read_config() + + if self.cfg_parser.has_option(section, key): + return self.cfg_parser.get(section,key, 1) + def get(self, section, key): if not self.cfg_parser: self.read_config() @@ -429,7 +438,7 @@ class Conf(object): if self.cfg_parser.has_option(section, key): return self.cfg_parser.get(section,key) else: - self.log.warning(_("Option does not exist in config file, pulling from defaults")) + self.log.warning(_("Option %s/%s does not exist in config file %s, pulling from defaults") %(section, key, self.config_file)) if hasattr(self.defaults, "%s_%s" %(section,key)): return getattr(self.defaults, "%s_%s" %(section,key)) elif hasattr(self.defaults, "%s" %(section)): diff --git a/pykolab/conf/defaults.py b/pykolab/conf/defaults.py index 97b2892..6d752b5 100644 --- a/pykolab/conf/defaults.py +++ b/pykolab/conf/defaults.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -34,7 +34,8 @@ class Defaults(object): 'bind_dn': "", 'bind_pw': "", 'user_base_dn': "ou=People,%(base_dn)s", - 'group_base_dn': "ou=Groups,%(base_dn)s" + 'group_base_dn': "ou=Groups,%(base_dn)s", + 'kolab_user_filter': '(objectClass=*)' } self.testing = { diff --git a/pykolab/conf/runtime.py b/pykolab/conf/runtime.py index da4ddb2..a0d5632 100644 --- a/pykolab/conf/runtime.py +++ b/pykolab/conf/runtime.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 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 new file mode 100644 index 0000000..d044269 --- /dev/null +++ b/pykolab/imap/__init__.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +# Copyright 2010-2011 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 2 only +# +# 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.auth + +from pykolab.conf import Conf +from pykolab.translate import _ + +class IMAP(object): + def __init__(self, conf=None): + if not conf: + self.conf = Conf() + self.conf.finalize_conf() + self.log = self.conf.log + else: + self.conf = conf + self.log = conf.log + + self.auth = pykolab.auth.Auth(self.conf) + + self.imap = None + + def _connect(self): + if not self.imap: + if self.conf.get('kolab', 'imap_backend') == 'cyrus-imap': + import cyruslib + self.imap = cyruslib.CYRUS(self.conf.get('cyrus-imap', 'uri')) + if self.conf.debuglevel > 8: + self.imap.VERBOSE = 1 + self.imap.login('cyrus-admin', 'VerySecret') + self.seperator = self.imap.m.getsep() + else: + import dovecotlib + self.imap = dovecotlib.IMAP4() + + def _disconnect(self): + del self.imap + self.imap = None + + def create_user_folders(self): + inbox_folders = [] + + folders = self.list_user_folders() + + # See if the folder belongs to any of the users + _match_attr = self.conf.get('cyrus-sasl', 'result_attribute') + + for user in self.auth.users(): + if user.has_key(_match_attr): + inbox_folders.append(user[_match_attr]) + + for folder in inbox_folders: + additional_folders = None + try: + if folders.index(folder) > -1: + continue + else: + self.log.info(_("Creating new INBOX for user: %s") %(folder)) + self.imap.cm("user/%s" %(folder)) + self.imap.sq("user/%s" %(folder), 0) + additional_folders = self.conf.plugins.exec_hook("create_user_folders", args=(folder)) + except: + self.log.info(_("Creating new INBOX for user: %s") %(folder)) + self.imap.cm("user/%s" %(folder)) + self.imap.sq("user/%s" %(folder), 0) + additional_folders = self.conf.plugins.exec_hook("create_user_folders", args=(folder)) + + if not additional_folders == None: + self.create_user_additional_folders(folder, additional_folders) + + def create_user_additional_folders(self, folder, additional_folders): + for additional_folder in additional_folders.keys(): + _add_folder = {} + if len(folder.split('@')) > 1: + folder_name = "user%(seperator)s%(username)s%(seperator)s%(additional_folder_name)s@%(domainname)s" + _add_folder['username'] = folder.split('@')[0] + _add_folder['domainname'] = folder.split('@')[1] + _add_folder['additional_folder_name'] = additional_folder + _add_folder['seperator'] = self.seperator + folder_name = folder_name % _add_folder + else: + folder_name = "user%(seperator)s%(username)s%(seperator)s%(additional_folder_name)s" % { + "username": folder, + "seperator": self.seperator, + "additional_folder_name": additional_folder + } + + self.imap.cm(folder_name) + if additional_folders[additional_folder].has_key("annotations"): + for annotation in additional_folders[additional_folder]["annotations"].keys(): + self.imap.setannotation( + folder_name, + "%s" %(annotation), + "%s" %(additional_folders[additional_folder]["annotations"][annotation]) + ) + + def set_user_folder_quota(self): + self._connect() + + _quota_attr = self.conf.get('cyrus-imap', 'quota_attribute') + _inbox_folder_attr = self.conf.get('cyrus-sasl', 'result_attribute') + + for user in self.auth.users(): + quota = None + + if user.has_key(_quota_attr): + if type(user[_quota_attr]) == list: + quota = user[_quota_attr].pop(0) + elif type(user[_quota_attr]) == str: + quota = user[_quota_attr] + else: + quota = 0 + + if not user.has_key(_inbox_folder_attr): + continue + else: + if type(user[_inbox_folder_attr]) == list: + folder = "user/%s" % user[_inbox_folder_attr].pop(0) + elif type(user[_inbox_folder_attr]) == str: + folder = "user/%s" % user[_inbox_folder_attr] + + (used,current_quota) = self.imap.lq(folder) + + new_quota = self.conf.plugins.exec_hook("set_user_folder_quota", args=(used, current_quota, int(quota))) + + print new_quota + + if new_quota and not new_quota == int(quota): + self.log.debug(_("Setting new quota for folder %s to %r") %(folder, new_quota), level=9) + quota = new_quota + self.auth.set_user_attribute(user['dn'], _quota_attr, new_quota) + + self.log.debug(_("Quota for %s currently is %s") %(folder, current_quota), level=7) + + if not int(current_quota) == int(quota): + self.log.debug(_("Correcting quota for %s to %s (currently %s)") %(folder, quota, current_quota), level=7) + self.imap.sq(folder, quota) + + def expunge_user_folders(self): + self._connect() + + inbox_folders = [] + + folders = self.list_user_folders() + + # See if the folder belongs to any of the users + _match_attr = self.conf.get('cyrus-sasl', 'result_attribute') + + for user in self.auth.users(): + if user.has_key(_match_attr): + if not user[_match_attr] in inbox_folders: + inbox_folders.append(user[_match_attr]) + + inbox_folders = list(set(inbox_folders)) + + for folder in folders: + print "Checking folder: %s" %(folder) + try: + if inbox_folders.index(folder) > -1: + continue + else: + self.log.info(_("Folder has no corresponding user (1): %s") %(folder)) + self.imap.dm("user/%s" %(folder)) + except: + self.log.info(_("Folder has no corresponding user (2): %s") %(folder)) + self.imap.dm("user/%s" %(folder)) + + def list_user_folders(self): + """ + List the INBOX folders in the IMAP backend. Returns a list of unique + base folder names. + """ + self._connect() + + _folders = self.imap.lm("user/*") + folders = [] + + for folder in _folders: + if len(folder.split('@')) > 1: + folder_name = "%s@%s" %(folder.split(self.imap.m.getsep())[1].split('@')[0],folder.split('@')[1]) + else: + folder_name = "%s" %(folder.split(self.imap.m.getsep())[1]) + + if not folder_name in folders: + folders.append(folder_name) + + return folders + + def synchronize(self, users=[]): + self._connect() + self.expunge_user_folders() + self.create_user_folders() + self.set_user_folder_quota() diff --git a/pykolab/logger.py b/pykolab/logger.py index e6d5a91..48c840d 100644 --- a/pykolab/logger.py +++ b/pykolab/logger.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2010 Kolab Systems AG (http://www.kolabsys.com) +# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com> # @@ -26,13 +26,14 @@ from pykolab.translate import _, N_ class Logger: def __init__(self, loglevel=logging.INFO, debuglevel=0, logfile="/var/log/kolab/kolabd.log"): + self.loglevel = loglevel self.debuglevel = debuglevel plaintextformatter = logging.Formatter("%(message)s") - console_stdout = logging.StreamHandler(sys.stdout) - console_stdout.setFormatter(plaintextformatter) + self.console_stdout = logging.StreamHandler(sys.stdout) + self.console_stdout.setFormatter(plaintextformatter) try: filelog_handler = logging.FileHandler(filename=logfile) @@ -40,14 +41,19 @@ class Logger: except IOError, e: print >> sys.stderr, _("Cannot log to file %s: %s") % (logfile, e) - self.log = logging.getLogger() - self.log.addHandler(console_stdout) - try: - self.log.addHandler(filelog_handler) - except: - pass + self.log = logging.getLogger('pykolab') + + if not len(self.log.handlers) > 1: + self.log.addHandler(self.console_stdout) + try: + self.log.addHandler(filelog_handler) + except: + pass + + self.log.setLevel(self.loglevel) - self.log.setLevel(self.loglevel) + def remove_stdout_handler(self): + self.log.removeHandler(self.console_stdout) def set_config(self, cfg): """Let the Logger instance know what our configuration is and she might diff --git a/pykolab/plugin/__init__.py b/pykolab/plugin/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pykolab/plugin/__init__.py diff --git a/pykolab/plugin/defaultfolders/__init__.py b/pykolab/plugin/defaultfolders/__init__.py new file mode 100644 index 0000000..53429d5 --- /dev/null +++ b/pykolab/plugin/defaultfolders/__init__.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2010-2011 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 2 only +# +# 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. +# + +class KolabDefaultfolders(object): + """ + Example plugin to create a set of default folders. + """ + + def __init__(self): + pass + + def create_user_folders(self, kw={}, args=()): + """ + The arguments passed to the 'create_user_folders' hook: + + - imap connection + - user folder + """ + + (folder) = args + + folders_to_create = {} + + folders_to_create["Calendar"] = { + "annotations": { + "/vendor/kolab/folder-test": "true", + "/vendor/kolab/folder-type": "event.default" + } + } + + folders_to_create["Calendar/Personlich"] = { + "annotations": { + "/vendor/kolab/folder-test": "true", + "/vendor/kolab/folder-type": "event" + } + } + + folders_to_create["Contacts"] = { + "annotations": { + "/vendor/kolab/folder-test": "true", + "/vendor/kolab/folder-type": "contact.default" + } + } + + folders_to_create["Dagboek"] = { + "annotations": { + "/vendor/kolab/folder-test": "true", + "/vendor/kolab/folder-type": "journal.default" + } + } + + return folders_to_create
\ No newline at end of file diff --git a/pykolab/plugin/dynamicquota/__init__.py b/pykolab/plugin/dynamicquota/__init__.py new file mode 100644 index 0000000..ecde07e --- /dev/null +++ b/pykolab/plugin/dynamicquota/__init__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2010-2011 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 2 only +# +# 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. +# + +class KolabDynamicquota(object): + """ + Example plugin making quota adjustments given arbitrary conditions. + """ + + def __init__(self): + pass + + def set_user_folder_quota(self, kw={}, args=()): + """ + The arguments passed to the 'set_user_folder_quota' hook: + + - used (integer, in KB) + - current quota (integer, in KB) + - quota (integer, in KB) + """ + + (used, current_quota, new_quota) = args + + print args + + # Escape the user without quota + if new_quota == 0: + return 0 + + # Make your adjustments here, for example: + # + # - increase the quota by 10% if the currently used storage size + # is over 90% + + if new_quota < int(float(used) * 1.1): + new_quota = int(float(used) * 1.1) + elif new_quota > int(float(used) * 1.1): + new_quota = int(float(current_quota) * 0.9) + + return new_quota diff --git a/pykolab/plugin/recipientpolicy/__init__.py b/pykolab/plugin/recipientpolicy/__init__.py new file mode 100644 index 0000000..e20548e --- /dev/null +++ b/pykolab/plugin/recipientpolicy/__init__.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2010-2011 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 2 only +# +# 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 pykolab.auth import Auth + +class KolabRecipientpolicy(object): + """ + Example plugin making quota adjustments given arbitrary conditions. + """ + + def __init__(self): + pass + + def set_user_attrs(self, kw={}, args=()): + """ + The arguments passed to the 'set_user_folder_quota' hook: + + - used (integer, in KB) + - current quota (integer, in KB) + - quota (integer, in KB) + """ + + (user_attrs) = args + + auth = Auth() + + user_attrs['mail'] = auth.conf.get_raw('recipient_policy', 'primary_email') % self.normalize(user_attrs) + other_email_routines = auth.conf.get_raw('recipient_policy', 'other_email') + + exec("other_email_routines = %s" % other_email_routines) + + other_email = [] + + for routine in other_email_routines.keys(): + exec("retval = '%s'.%s" % (routine,other_email_routines[routine] % self.normalize(user_attrs))) + other_email.append(retval) + + print other_email + + auth.set_user_attribute(user_attrs['dn'], 'mail', user_attrs['mail']) + + return user_attrs + + def normalize(self, user_attrs): + if user_attrs.has_key('sn'): + user_attrs['surname'] = user_attrs['sn'].replace(' ', '') + + if user_attrs.has_key('mail'): + if len(user_attrs['mail'].split('@')) > 1: + user_attrs['domain'] = user_attrs['mail'].split('@')[1] + + return user_attrs
\ No newline at end of file diff --git a/pykolab/plugins.py b/pykolab/plugins.py index 5d0e012..f62663f 100644 --- a/pykolab/plugins.py +++ b/pykolab/plugins.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- +# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com) # -# Copyright 2007-2010 Fedora Unity Project (http://fedoraunity.org) -# -# Jonathan Steffan <jon a fedoraunity.org> -# Jeroen van Meeuwen <kanarip a fedoraunity.org> +# 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 @@ -17,6 +15,7 @@ # 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 logging import os @@ -27,6 +26,8 @@ import pdb # Translation from pykolab.translate import _ +import pykolab.plugin + class KolabPlugins: """Detects, loads and interfaces with plugins for Kolab""" def __init__(self, init=False): @@ -35,32 +36,20 @@ class KolabPlugins: """ self.plugins = {} - for plugin_path in [ '/usr/share/pykolab/plugins/', './plugins/' ]: + for plugin_path in [ '/usr/share/pykolab/plugin/', './pykolab/plugin/' ]: if os.path.isdir(plugin_path): for plugin in os.listdir(plugin_path): - if os.path.isdir('%s/%s/' % (plugin_path,plugin,)): + if os.path.isdir('%s/%s/' %(plugin_path,plugin,)): self.plugins[plugin] = False self.check_plugins(init=init) - def load_plugins(self, plugins=[], init=False): - """Loads plugins specified by a list of plugins or loads them all""" - if len(plugins) < 1: - plugins = self.plugins.keys() - - for plugin in plugins: - if self.plugins[plugin]: - try: - exec("self.%s = revisor.%s.Revisor%s()" % (plugin,plugin,plugin.replace("mod","").capitalize())) - except Exception, e: - if not init: print >> sys.stderr, _("Plugin %s failed to load (%s: %s)") % (plugin, e.__class__, e) - def check_plugins(self, init=False): """Checks all plugins in self.plugins and sets the values to True (loadable) or False (not enabled, not installed or not loadable)""" for plugin in self.plugins: try: - exec("import revisor.%s" % plugin) + exec("from pykolab.plugin.%s import Kolab%s" %(plugin,plugin.capitalize())) self.plugins[plugin] = True self.load_plugins(plugins=[plugin], init=init) except ImportError, e: @@ -72,6 +61,16 @@ class KolabPlugins: except Exception, e: if not init: print >> sys.stderr, _("Plugin %s failed to load (%s: %s)") % (plugin, e.__class__, e) + def load_plugins(self, plugins=[], init=False): + """Loads plugins specified by a list of plugins or loads them all""" + + if len(plugins) < 1: + plugins = self.plugins.keys() + + for plugin in plugins: + if self.plugins[plugin]: + exec("self.%s = pykolab.plugin.%s.Kolab%s()" % (plugin,plugin,plugin.capitalize())) + def set_defaults(self, defaults, plugins=[]): """Test for a function set_defaults() in all available and loaded plugins and execute plugin.set_defaults()""" if len(plugins) < 1: @@ -134,7 +133,7 @@ class KolabPlugins: else: print >> sys.stderr, _("Not adding options for plugin %s: No function 'add_options()'") % plugin - def check_options(self, cfg, plugins=[]): + def check_options(self, conf, plugins=[]): """Executes plugin.check_plugins() for all enabled plugins or the list of plugin names specified.""" if len(plugins) < 1: @@ -148,7 +147,7 @@ class KolabPlugins: if hasattr(getattr(self,plugin),"check_options"): try: - exec("self.%s.check_options(cfg, cfg.cli_options)" % plugin) + exec("self.%s.check_options(conf, conf.cli_options)" % plugin) except AttributeError, e: print >> sys.stderr, _("Cannot check options for plugin %s: %s") % (plugin,e) else: @@ -172,7 +171,7 @@ class KolabPlugins: return False - def exec_hook(self, hook, plugins=[]): + def exec_hook(self, hook, plugins=[], args=()): """Execute a hook""" if len(plugins) < 1: @@ -184,12 +183,19 @@ class KolabPlugins: if not hasattr(self,plugin): continue + retval = None + if hasattr(getattr(self,plugin),hook): try: - exec("self.%s.%s()" % (plugin,hook)) + exec("retval = self.%s.%s(args=%r)" % (plugin,hook,args)) + except TypeError, e: + print >> sys.stderr, _("Cannot execute hook %s for plugin %s: %s") % (hook,plugin,e) except AttributeError, e: print >> sys.stderr, _("Cannot execute hook %s for plugin %s: %s") % (hook,plugin,e) + return retval + + def return_true_boolean_from_plugins(self, bool, plugins=[]): """Given the name of a boolean, walks all specified plugins, or all available plugins, and returns True if a plugin has it set to true""" if len(plugins) < 1: |