summaryrefslogtreecommitdiffstats
path: root/pykolab
diff options
context:
space:
mode:
Diffstat (limited to 'pykolab')
-rw-r--r--pykolab/__init__.py.in40
-rw-r--r--pykolab/base.py69
-rw-r--r--pykolab/conf/__init__.py266
-rw-r--r--pykolab/conf/defaults.py38
-rw-r--r--pykolab/conf/runtime.py23
-rw-r--r--pykolab/constants.py71
-rw-r--r--pykolab/logger.py93
-rw-r--r--pykolab/plugins.py216
-rw-r--r--pykolab/setup/__init__.py31
-rw-r--r--pykolab/setup/imap.py29
-rw-r--r--pykolab/setup/ldap_setup.py140
-rw-r--r--pykolab/translate.py47
-rw-r--r--pykolab/utils.py76
13 files changed, 1139 insertions, 0 deletions
diff --git a/pykolab/__init__.py.in b/pykolab/__init__.py.in
new file mode 100644
index 0000000..056f3c6
--- /dev/null
+++ b/pykolab/__init__.py.in
@@ -0,0 +1,40 @@
+# Copyright 2010 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.
+#
+
+__license__ = "GPLv2+"
+__copyright__ = "Kolab Systems AG"
+__version__ = @VERSION@
+__release__ = @RELEASE@
+
+if @RELEASE@ < 1:
+ __status__ = 'snapshot'
+elif math.round(@RELEASE@,0) < @RELEASE@:
+ __status__ = 'prerelease'
+else:
+ __status__ = 'stable'
+
+import traceback
+import shutil
+import sys
+
+import pykolab
+import pykolab.plugins
+
+from pykolab.translate import _
+
+from pykolab.conf import Conf
diff --git a/pykolab/base.py b/pykolab/base.py
new file mode 100644
index 0000000..224d466
--- /dev/null
+++ b/pykolab/base.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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.conf import Defaults, Runtime
+import pykolab.conf
+
+class PyKolabBase(object):
+
+ def __init__(self, pykolab):
+ """
+ Initializes the a PyKolab class with the options specified from the command line.
+ Launches our plugin detection.
+ Creates a logger instance
+ Creates a configuration store
+ Detects whether we are in CLI or GUI mode
+ Sets the logger configuration
+ Sets up the final configuration store
+ """
+
+ # Get the options parser, it's valuable ;-)
+ self.parser = revisor.parser
+
+ # The options it has defined are valuable too
+ self.cli_options = revisor.cli_options
+ self.plugins = revisor.plugins
+ self.plugins.base = self
+
+ # At this point, 'self' isn't much yet, so:
+ # first create a simple logger instance that won't do much,
+ # then create a configuration store with that logger,
+ # then start detecting the mode that we are in (GUI / CLI),
+ # then let the logger know about the configuration store,
+ # then /really/ set up the configuration store (now that it has a
+ # valid logger that knows about the configuration store),
+ #
+ # Create logger
+ self.create_logger()
+
+ # Create ConfigStore (it needs the logger to be created!)
+ self.create_configstore()
+
+ # Detect our mode (options or try/except)
+ self.detect_mode()
+
+ # Let the logger know about cfg (it needs a ConfigStore instance!)
+ self.log.set_config(self.cfg)
+
+ # Then really setup the ConfigStore (because that needs a logger!)
+ self.cfg.setup_cfg()
+
+ misc.check_selinux(log=self.log)
+
+
diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py
new file mode 100644
index 0000000..66be1b3
--- /dev/null
+++ b/pykolab/conf/__init__.py
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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 logging
+import os
+import sys
+
+from optparse import OptionParser
+from ConfigParser import SafeConfigParser
+
+import pykolab
+from pykolab.conf.defaults import Defaults
+from pykolab.conf.runtime import Runtime
+from pykolab.constants import *
+from pykolab.translate import _
+
+class Conf(object):
+ def __init__(self):
+ """
+ self.args == Arguments passed on the CLI
+ self.cli_options == Parser results (again, CLI)
+ self.parser == The actual Parser (from OptionParser)
+ self.plugins == Our Kolab Plugins
+ """
+
+ self.args = None
+ self.cli_options = None
+ self.parser = None
+ self.plugins = None
+
+ # The location where our configuration parser is going to end up
+ self.cfg_parser = None
+
+ # Create and parse the options
+ self.parse_options()
+
+ # At this point, 'self' isn't much yet, so:
+ # first create a simple logger instance that won't do much,
+ # then create a configuration store with that logger,
+ # then start detecting the mode that we are in (GUI / CLI),
+ # then let the logger know about the configuration store,
+ # then /really/ set up the configuration store (now that it has a
+ # valid logger that knows about the configuration store),
+ #
+ # Create logger
+ self.create_logger()
+
+ # Let the logger know about cfg (it needs a ConfigStore instance!)
+ self.log.set_config(self)
+
+ # The defaults can some from;
+ # - a file we ship with the packages
+ # - a customly supplied file (by customer)
+ # - a file we write out
+ # - this python class
+ #
+ # Look, we want defaults
+ self.defaults = Defaults(self.plugins)
+
+ # This is where we check our parser for the defaults being set there.
+ self.set_defaults_from_cli_options()
+
+ # But, they should be available in our class as well
+ for option in self.defaults.__dict__.keys():
+ setattr(self,option,self.defaults.__dict__[option])
+
+ # There is also a number of runtime specific variables
+ self.runtime = Runtime(self.plugins, self.defaults)
+
+ # Which should also be available here
+ for option in self.runtime.__dict__.keys():
+ self.log.debug(_("Setting %s to %r") % (option, self.runtime.__dict__[option]), level=9)
+ setattr(self,option,self.runtime.__dict__[option])
+
+ def parse_options(self, load_plugins=True):
+ """
+ Create the OptionParser for the options passed to us from runtime
+ Command Line Interface.
+ """
+
+ # 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( "-c", "--config",
+ dest = "config_file",
+ action = "store",
+ default = "/etc/kolab/kolab.conf",
+ help = _("Configuration file to use"))
+
+ runtime_group.add_option( "-d", "--debug",
+ dest = "debuglevel",
+ type = 'int',
+ default = 0,
+ help = _("Set the debugging verbosity. Maximum is 99"))
+
+ runtime_group.add_option( "--logfile",
+ dest = "logfile",
+ action = "store",
+ default = "/var/log/kolabd/kolabd.log",
+ help = _("Log file to use"))
+
+ runtime_group.add_option( "-y", "--yes",
+ dest = "answer_yes",
+ action = "store_true",
+ default = False,
+ help = _("Configuration file to use"))
+
+ ##
+ ## 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()
+
+ def run(self):
+ """
+ Run Forest, RUN!
+ """
+ exitcode = 0
+
+ if len(self.args) >= 1:
+ if hasattr(self,"command_%s" % self.args[0].replace('-','_')):
+ exec("self.command_%s(%r)" % (self.args[0].replace('-','_'), self.args[1:]))
+ else:
+ print >> sys.stderr, _("No command supplied")
+
+
+ def command_dump(self, *args, **kw):
+ """
+ Dumps applicable, valid configuration that is not defaults.
+ """
+
+ if not self.cfg_parser:
+ self.read_config()
+
+ if not self.cfg_parser.has_section('kolab'):
+ print "No section found for kolab"
+ sys.exit(1)
+
+ # Get the sections, and then walk through the sections in a
+ # sensible way.
+ items = self.cfg_parser.options('kolab')
+
+ items.sort()
+
+ for item in items:
+ mode = self.cfg_parser.get('kolab',item)
+ print "%s = %s" %(item,mode)
+
+ if not self.cfg_parser.has_section(mode):
+ print "WARNING: No configuratino section for %s: %s" %(item,mode,)
+ continue
+
+ keys = self.cfg_parser.options(mode)
+ keys.sort()
+
+ if self.cfg_parser.has_option(mode, 'leave_this_one_to_me'):
+ print "Ignoring section %s" %(mode,)
+ continue
+
+ for key in keys:
+ print "%s_%s = %s" %(mode, key ,self.cfg_parser.get(mode,key))
+
+ def read_config(self):
+ """
+ Reads the configuration file, sets a self.cfg_parser.
+ """
+
+ self.cfg_parser = SafeConfigParser()
+ self.cfg_parser.read(self.cli_options.config_file)
+
+ def command_get(self, *args, **kw):
+ """
+ Get a configuration option.
+
+ Pass me a section and key please.
+ """
+ exec("args = %r" % args)
+
+ if not self.cfg_parser:
+ self.read_config()
+
+ if len(args) == 1:
+ self.log.error(_("Only one option supplied"), recoverable=False)
+
+ if len(args) == 2:
+ if self.cfg_parser.has_option(args[0], args[1]):
+ print "%s/%s: %r" %(args[0],args[1],self.cfg_parser.get(args[0],args[1]))
+ else:
+ self.log.warning(_("Option does not exist in config file, pulling from defaults"))
+ print "Something default"
+
+# if len(args) == 3:
+# # Return non-zero if no match
+# # Return zero if match
+# # Improvised "check" function
+
+ def command_set(self, *args, **kw):
+ """
+ Set a configuration option.
+
+ Pass me a section, key and value please. Note that the section should
+ already exist.
+
+ TODO: Add a strict parameter
+ TODO: Add key value checking
+ """
+ exec("args = %r" % args)
+
+ if not self.cfg_parser:
+ self.read_config()
+
+ if not len(args) == 3:
+ self.log.error(_("Insufficient options. Need section, key and value -in that order."), recoverable=False)
+
+ if not self.cfg_parser.has_section(args[0]):
+ self.log.error(_("No section '%s' exists.") %(args[0]))
+
+ self.cfg_parser.set(args[0], args[1], args[2])
+ fp = open(self.cli_options.config_file, "w+")
+ self.cfg_parser.write(fp)
+ fp.close()
+
+ def create_logger(self):
+ """Create a logger instance using cli_options.debuglevel"""
+ if not self.cli_options.debuglevel == None:
+ loglevel = logging.DEBUG
+ else:
+ loglevel = logging.INFO
+ self.cli_options.debuglevel = 0
+
+ # Initialize logger
+ self.log = pykolab.logger.Logger(loglevel=loglevel, debuglevel=self.cli_options.debuglevel, logfile=self.cli_options.logfile)
+
+ def set_defaults_from_cli_options(self):
+ for long_opt in self.parser.__dict__['_long_opt'].keys():
+ if long_opt == "--help":
+ continue
+ setattr(self.defaults,self.parser._long_opt[long_opt].dest,self.parser._long_opt[long_opt].default)
+
diff --git a/pykolab/conf/defaults.py b/pykolab/conf/defaults.py
new file mode 100644
index 0000000..ba0cbce
--- /dev/null
+++ b/pykolab/conf/defaults.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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 Defaults(object):
+ def __init__(self, plugins=None):
+ # Each possible section in the configuration has a dict here.
+
+ # The default authentication mechanism
+ self.kolab = {
+ 'auth_mechanism': 'ldap',
+ }
+
+ # The default LDAP URI. Note that these are
+ # prefixed with the section name.
+ self.ldap = {
+ 'uri': "ldap://localhost",
+ 'base_dn': "dc=localhost,dc=localdomain",
+ 'bind_dn': "",
+ 'bind_pw': "",
+ 'user_base_dn': "ou=People,%(base_dn)s",
+ 'group_base_dn': "ou=Groups,%(base_dn)s"
+ }
diff --git a/pykolab/conf/runtime.py b/pykolab/conf/runtime.py
new file mode 100644
index 0000000..b4a7af1
--- /dev/null
+++ b/pykolab/conf/runtime.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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 Runtime(object):
+ def __init__(self, plugins, defaults):
+ pass
+
diff --git a/pykolab/constants.py b/pykolab/constants.py
new file mode 100644
index 0000000..84d4a32
--- /dev/null
+++ b/pykolab/constants.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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 socket
+import sys
+
+from pykolab.translate import _
+
+domain = 'pykolab'
+
+epilog = _( "PyKolab is a Kolab Systems product. For more information " + \
+ "about Kolab or PyKolab, visit http://www.kolabsys.com")
+
+COMPONENTS = [
+ 'imap',
+ 'ldap',
+ 'mta'
+ ]
+
+hostname = socket.gethostname()
+fqdn = socket.getfqdn()
+try:
+ domain_parts = fqdn.split('.')
+ if len(domain_parts) < 3:
+ print >> sys.stderr, _("WARNING") + ": " + _("The Fully Qualified " + \
+ "Domain Name or FQDN for this system is incorrect. Falling " + \
+ "back to 'localdomain'.")
+ domainname = "localdomain"
+ else:
+ domainname = '.'.join(domain_parts[1:])
+except IndexError:
+ domainname = "localdomain"
+
+# The system RC directory
+RC_DIR = "/etc/rc.d/init.d/"
+
+# Service map;
+#
+# Convert names of registered system services to their type. For example,
+# on Red Hat, OpenLDAP is 'slapd', whereas on Debian, OpenLDAP is 'ldap'.
+#
+SERVICE_MAP = {
+ 'dirsrv': {
+ 'type': '389ds',
+ 'description': _('389 Directory Server or Red Hat Directory Server')
+ },
+ 'ldap': {
+ 'type': 'openldap',
+ 'description': _('OpenLDAP or compatible')
+ },
+ 'slapd': {
+ 'type': 'openldap',
+ 'description': _('OpenLDAP or compatible')
+ },
+ } \ No newline at end of file
diff --git a/pykolab/logger.py b/pykolab/logger.py
new file mode 100644
index 0000000..168b338
--- /dev/null
+++ b/pykolab/logger.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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 logging
+import logging.handlers
+import sys
+
+# Translation
+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)
+
+ try:
+ filelog_handler = logging.FileHandler(filename=logfile)
+ filelog_handler.setFormatter(plaintextformatter)
+ 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.setLevel(self.loglevel)
+
+ def set_config(self, cfg):
+ """Let the Logger instance know what our configuration is and she might
+ be able to distinct between CLI and GUI mode, or even give more details
+ about what goes wrong"""
+ self.cfg = cfg
+
+ def info(self, msg):
+ self.log.info(msg)
+
+ def debug(self, msg, level=1):
+ # By default, level=1 so that debug messages are suppressed
+ if level <= self.debuglevel:
+ self.log.debug(msg)
+
+ def error(self, msg, recoverable=True):
+ self.log.error(msg)
+ if recoverable:
+ self.error_prompt(msg)
+ else:
+ sys.exit(1)
+
+ def warning(self, msg):
+ self.log.warning(msg)
+ self.warning_prompt(msg)
+
+ def error_prompt(self, text):
+ """The error has already been logged to the console, try and catch some input"""
+ if not self.cfg.answer_yes:
+ sys.stderr.write(_("Do you want to continue? [Y/n]") + " ")
+ answer = sys.stdin.readline()[:-1]
+ if answer == "n":
+ self.error(_("Abort! Abort! Abort!"), recoverable=False)
+ sys.exit(1)
+
+ def warning_prompt(self, text):
+ """The error has already been logged to the console, try and catch some input"""
+ if not self.cfg.answer_yes:
+ sys.stdout.write(_("Do you want to continue? [Y/n]") + " ")
+ answer = sys.stdin.readline()[:-1]
+ if answer == "n":
+ self.error(_("Abort! Abort! Abort!"), recoverable=False)
+ sys.exit(1)
diff --git a/pykolab/plugins.py b/pykolab/plugins.py
new file mode 100644
index 0000000..5d0e012
--- /dev/null
+++ b/pykolab/plugins.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2007-2010 Fedora Unity Project (http://fedoraunity.org)
+#
+# Jonathan Steffan <jon a fedoraunity.org>
+# Jeroen van Meeuwen <kanarip a fedoraunity.org>
+#
+# 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 logging
+import os
+import pykolab
+import sys
+import pdb
+
+# Translation
+from pykolab.translate import _
+
+class KolabPlugins:
+ """Detects, loads and interfaces with plugins for Kolab"""
+ def __init__(self, init=False):
+ """
+ Searches the plugin directory for plugins, and loads them into a list.
+ """
+ self.plugins = {}
+
+ for plugin_path in [ '/usr/share/pykolab/plugins/', './plugins/' ]:
+ if os.path.isdir(plugin_path):
+ for plugin in os.listdir(plugin_path):
+ 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)
+ self.plugins[plugin] = True
+ self.load_plugins(plugins=[plugin], init=init)
+ except ImportError, e:
+ if not init: print >> sys.stderr, _("ImportError for plugin %s: %s") % (plugin,e)
+ self.plugins[plugin] = False
+ except RuntimeError, e:
+ if not init: print >> sys.stderr, _("RuntimeError for plugin %s: %s") % (plugin,e)
+ self.plugins[plugin] = False
+ except Exception, e:
+ if not init: print >> sys.stderr, _("Plugin %s failed to load (%s: %s)") % (plugin, e.__class__, e)
+
+ 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:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),"set_defaults"):
+ try:
+ getattr(self,plugin).set_defaults(defaults)
+ except TypeError, e:
+ print >> sys.stderr, _("Cannot set defaults for plugin %s: %s") % (plugin,e)
+ except RuntimeError, e:
+ print >> sys.stderr, _("Cannot set defaults for plugin %s: %s") % (plugin,e)
+ except:
+ print >> sys.stderr, _("Cannot set defaults for plugin %s: Unknown Error") % (plugin)
+
+ else:
+ print >> sys.stderr, _("Not setting defaults for plugin %s: No function 'set_defaults()'") % plugin
+
+ def set_runtime(self, runtime, plugins=[]):
+ """Set runtime variables from plugins, like 'i_did_all_this'"""
+ if len(plugins) < 1:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),"set_runtime"):
+ try:
+ getattr(self,plugin).set_runtime(runtime)
+ except RuntimeError, e:
+ print >> sys.stderr, _("Cannot set runtime for plugin %s: %s") % (plugin,e)
+ else:
+ print >> sys.stderr, _("Not setting runtime for plugin %s: No function 'set_runtime()'") % plugin
+
+ def add_options(self, parser, plugins=[]):
+ """Add options specified in a plugin to parser. Takes a list of plugin names or does them all"""
+ if len(plugins) < 1:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),"add_options"):
+ try:
+ exec("self.%s.add_options(parser)" % plugin)
+ except RuntimeError, e:
+ print >> sys.stderr, _("Cannot add options for plugin %s: %s") % (plugin,e)
+ else:
+ print >> sys.stderr, _("Not adding options for plugin %s: No function 'add_options()'") % plugin
+
+ def check_options(self, cfg, plugins=[]):
+ """Executes plugin.check_plugins() for all enabled plugins or the list of plugin names specified."""
+
+ if len(plugins) < 1:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),"check_options"):
+ try:
+ exec("self.%s.check_options(cfg, cfg.cli_options)" % plugin)
+ except AttributeError, e:
+ print >> sys.stderr, _("Cannot check options for plugin %s: %s") % (plugin,e)
+ else:
+ print >> sys.stderr, _("Not checking options for plugin %s: No function 'check_options()'") % plugin
+
+ def plugin_check_setting(self, func, option, val, plugins=[]):
+ """Checks one setting specified by 'option' against the 'val' it is passed by all plugins or by the list of plugins specified"""
+
+ if len(plugins) < 1:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),"%s_%s" % (func,option)):
+ exec("retval = getattr(self,plugin).%s_%s(val)" % (func,option))
+ return retval
+
+ return False
+
+ def exec_hook(self, hook, plugins=[]):
+ """Execute a hook"""
+
+ if len(plugins) < 1:
+ plugins = self.plugins.keys()
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),hook):
+ try:
+ exec("self.%s.%s()" % (plugin,hook))
+ except AttributeError, e:
+ print >> sys.stderr, _("Cannot execute hook %s for plugin %s: %s") % (hook,plugin,e)
+
+ 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:
+ plugins = self.plugins.keys()
+
+ retval = False
+
+ for plugin in plugins:
+ if not self.plugins[plugin]:
+ continue
+ if not hasattr(self,plugin):
+ continue
+
+ if hasattr(getattr(self,plugin),bool):
+ try:
+ exec("boolval = self.%s.%s" % (plugin,bool))
+ except AttributeError, e:
+ pass
+ else:
+ boolval = None
+
+ if boolval: retval = True
+
+ return retval
diff --git a/pykolab/setup/__init__.py b/pykolab/setup/__init__.py
new file mode 100644
index 0000000..73859fb
--- /dev/null
+++ b/pykolab/setup/__init__.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 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 import constants
+
+__all__ = [
+ ]
+
+for component in constants.COMPONENTS:
+ try:
+ exec("from %s_setup import setup as %s_setup" % (component,component))
+ __all__.append("%s_setup" % component)
+ except:
+ pass
diff --git a/pykolab/setup/imap.py b/pykolab/setup/imap.py
new file mode 100644
index 0000000..6165259
--- /dev/null
+++ b/pykolab/setup/imap.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 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 logging
+import os
+import sys
+
+from pykolab.translate import _
+from pykolab import utils
+
+def setup():
+ print "I'm in the IMAP setup" \ No newline at end of file
diff --git a/pykolab/setup/ldap_setup.py b/pykolab/setup/ldap_setup.py
new file mode 100644
index 0000000..f381711
--- /dev/null
+++ b/pykolab/setup/ldap_setup.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010 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 getpass
+import ldap
+import logging
+import os
+import sys
+
+from pykolab.translate import _
+from pykolab import constants
+from pykolab import utils
+
+def setup():
+ """
+ Setup LDAP from here.
+
+ # Register with existing LDAP tree?
+ #* Verify schema loaded
+ #* Forget about flexibility
+ # Create new LDAP tree
+ #* OpenLDAP
+ """
+
+ (service, other_services) = utils.is_service([
+ 'dirsrv',
+ 'ldap',
+ 'slapd'
+ ])
+
+ for item in other_services:
+ print >> sys.stderr, _("Warning: LDAP Service '%s' is available on " + \
+ "this system as well.") % item
+
+ print _("Found system service %s.") % service
+
+ #ldap_uri = utils.ask_question(_("LDAP URI (read/write)"), "ldap://ldap.%s" %(constants.domainname))
+ ldap_uri = utils.ask_question(_("LDAP URI (read/write)"), "ldap://localhost")
+ manager_dn = utils.ask_question("Manager DN", "cn=Directory Manager")
+ #manager_pw = utils.ask_question("Manager Password", password=True)
+ manager_pw = utils.ask_question("Manager Password", "verysecret", password=True)
+
+ try:
+ con = ldap.initialize(ldap_uri)
+ con.bind(manager_dn, manager_pw, ldap.AUTH_SIMPLE)
+ except TypeError:
+ # This is a funny input error ("")
+ print >> sys.stderr, _("Could not connect to LDAP server due to " + \
+ "invalid LDAP URI format or no local socket")
+ sys.exit(1)
+ except ldap.INVALID_CREDENTIALS, e:
+ print >> sys.stderr, _("Your username or password are incorrect")
+ sys.exit(1)
+ except ldap.LDAPError, e:
+ print >> sys.stderr, _("Could not connect to LDAP server due to " + \
+ "invalid LDAP URI (or invalid format) or no local socket")
+ sys.exit(1)
+ except ldap.SERVER_DOWN, e:
+ print >> sys.stderr, e['desc']
+ sys.exit(1)
+
+ # Returns a list of dicts (empty list if not found)
+ kolab_config_dn_results = con.search_s('cn=kolab,cn=config', ldap.SCOPE_SUBTREE, '(cn=kolab)', ['cn'])
+
+ if len(kolab_config_dn_results) == 1:
+ print >> sys.stdout, "Success: Found cn=kolab,cn=config"
+
+ else:
+ initialize_kolab_config_dn(con)
+
+ #if not service == "":
+ #if service in constants.SERVICE_MAP.keys():
+ #exec("setup_%s()" % constants.SERVICE_MAP['%s' % service]['type'])
+ #else:
+ ## No service found on the local system, so ask a bunch of questions.
+ ##
+ ## - ldap uri
+ ## - manager dn
+ ## - manager pw
+ #pass
+
+def setup_389ds():
+ """
+ Executes for a local 389 Directory Server installation.
+ """
+
+ for (path, directories, files) in os.walk("/etc/dirsrv/"):
+ for directory in directories:
+ if directory.startswith('slapd-'):
+ print "Found a dirsrv instance %r" % directory
+ dirsrv_instance = directory
+
+# if dirsrv_instance == '':
+# # Apparently we're working with a remote dirsrv... are we going to have
+# # to set up the local directory service as well??
+# raise NotImplementedError, _("Initializing a 389 Directory Server has not been implemented yet. Please use setup-ds-admin")
+#
+# elif dirsrv_instance == 'slapd-localhost':
+# # The server is on localhost
+# ldap_conn = ldap.initialize(uri="ldap://localhost:389")
+# try:
+# ldap_conn.start_tls_s()
+# except ldap.LDAPError, e:
+# pass
+#
+# else:
+# pass
+
+def setup_openldap():
+ print "im an openldap system!"
+
+
+def initialize_kolab_config_dn(ldap_con=None):
+ if ldap_con == None:
+ return
+
+ ldif = """
+dn: cn=kolab,cn=config
+cirUpdateSchedule: New
+cn: kolab
+objectClass: top
+objectClass: extensibleobject
+"""
diff --git a/pykolab/translate.py b/pykolab/translate.py
new file mode 100644
index 0000000..6386912
--- /dev/null
+++ b/pykolab/translate.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010 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.
+#
+
+try:
+ from pykolab.constants import domain
+except ImportError:
+ domain = 'pykolab'
+
+import gettext
+import os
+
+N_ = lambda x: x
+_ = lambda x: gettext.ldgettext(domain, x)
+
+def getDefaultLangs():
+ languages = []
+ for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
+ val = os.environ.get(envar)
+ if val:
+ languages = val.split(':')
+ break
+ if 'C' not in languages:
+ languages.append('C')
+
+ # now normalize and expand the languages
+ nelangs = []
+ for lang in languages:
+ for nelang in gettext._expand_lang(lang):
+ if nelang not in nelangs:
+ nelangs.append(nelang)
+ return nelangs
diff --git a/pykolab/utils.py b/pykolab/utils.py
new file mode 100644
index 0000000..a5c0f1f
--- /dev/null
+++ b/pykolab/utils.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+
+import getpass
+import os
+
+from pykolab import constants
+
+def ask_question(question, default="", password=False):
+
+ if password:
+ answer = getpass.getpass("%s: " %(question))
+ else:
+ if default == "":
+ answer = raw_input("%s: " %(question))
+ else:
+ answer = raw_input("%s [%s]: " %(question, default))
+
+ if answer == "":
+ return default
+ else:
+ return answer
+
+def parse_input(_input, splitchars= [ ' ' ]):
+ """
+ Split the input string using the split characters defined
+ in splitchars, and remove the empty list items, then unique the
+ list items.
+
+ Takes a string as input, and a list of characters the string should be
+ split with (list of delimiter characters).
+ """
+
+ _parse_list = _input.split(splitchars.pop())
+ _output_list = []
+
+ for splitchar in splitchars:
+ __parse_list = []
+ for item in _parse_list:
+ __parse_list.extend(item.split(splitchar))
+ _parse_list = __parse_list
+
+ for item in _parse_list:
+ if not item == '':
+ if _output_list.count(item) < 1:
+ _output_list.append(item)
+
+ return _output_list
+
+def pop_empty_from_list(_input_list):
+ _output_list = []
+
+ for item in _input_list:
+ if not item == '':
+ _output_list.append(item)
+
+def is_service(services):
+ """
+ Checks each item in list services to see if it has a RC script in
+ constants.RC_DIR to see if it's a service, and returns
+ the name of the service for the first service it can find. However,
+ it also checks whether the other services exist and issues a warning if
+ more then one service exists.
+
+ Usage: utils.is_service(['dirsrv', 'ldap'])
+ """
+ _service = ''
+ _other_services = []
+
+ for service in services:
+ if os.path.isfile(os.path.join(constants.RC_DIR, service)):
+ if _service == '':
+ _service = service
+ else:
+ _other_services.append(service)
+
+ return (_service,_other_services)