summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xkolabd.py2
-rw-r--r--kolabd/__init__.py93
-rw-r--r--pykolab/auth/__init__.py61
-rw-r--r--pykolab/auth/ldap/__init__.py99
-rw-r--r--pykolab/conf/__init__.py13
-rw-r--r--pykolab/conf/defaults.py5
-rw-r--r--pykolab/conf/runtime.py2
-rw-r--r--pykolab/imap/__init__.py209
-rw-r--r--pykolab/logger.py26
-rw-r--r--pykolab/plugin/__init__.py0
-rw-r--r--pykolab/plugin/defaultfolders/__init__.py68
-rw-r--r--pykolab/plugin/dynamicquota/__init__.py55
-rw-r--r--pykolab/plugin/recipientpolicy/__init__.py68
-rw-r--r--pykolab/plugins.py52
14 files changed, 661 insertions, 92 deletions
diff --git a/kolabd.py b/kolabd.py
index 49f2a38..adc7e91 100755
--- a/kolabd.py
+++ b/kolabd.py
@@ -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: