summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Boddie <paul@boddie.org.uk>2014-08-07 12:19:41 (GMT)
committerPaul Boddie <paul@boddie.org.uk>2014-08-07 12:19:41 (GMT)
commit669ce64acc92bce68f89fe2fc277b1ab8781c391 (patch)
tree3185bfc138bdf6f3ceadca25e69adcb050639f86
parent12917f5ee82369cd21da413aa921fceade898050 (diff)
downloadpykolab-669ce64acc92bce68f89fe2fc277b1ab8781c391.tar.gz
Tidied up the imap setup.
Added Dovecot support. Added debconf support. Added remote IMAP server support.
-rw-r--r--pykolab/setup/setup_imap.py334
1 files changed, 237 insertions, 97 deletions
diff --git a/pykolab/setup/setup_imap.py b/pykolab/setup/setup_imap.py
index 57b4023..e3b5563 100644
--- a/pykolab/setup/setup_imap.py
+++ b/pykolab/setup/setup_imap.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
+# Copyright 2013, 2014 Paul Boddie <paul@boddie.org.uk>
#
# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
#
@@ -17,18 +18,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-from augeas import Augeas
-from Cheetah.Template import Template
-import os
-import subprocess
-
import components
import pykolab
from pykolab import utils
from pykolab.constants import *
+from pykolab.setup.services import *
from pykolab.translate import _
+import re
log = pykolab.getLogger('pykolab.setup')
conf = pykolab.getConf()
@@ -41,20 +39,144 @@ def __init__():
after=['ldap']
)
+def cli_options():
+ imap_group = conf.add_cli_parser_option_group(_("IMAP Options"))
+
+ imap_group.add_option(
+ "--choose-imap-server",
+ dest = "choose_imap_server",
+ action = "store_true",
+ default = False,
+ help = _("Provide a choice of IMAP server.")
+ )
+
def description():
return _("Setup IMAP.")
def execute(*args, **kw):
+
+ # Signal that interaction may occur. This will involve debconf and similar
+ # system-specific mechanisms if available.
+
+ start_interaction("kolab-conf/title-imap")
+ try:
+ _execute(*args, **kw)
+ finally:
+ stop_interaction()
+
+def _execute(*args, **kw):
"""
Apply the necessary settings to /etc/imapd.conf
"""
- imapd_settings = {
+ unconfigured = conf.get('imap', 'admin_password') == "Welcome123"
+
+ ask_for_server = unconfigured or conf.choose_imap_server
+
+ # Where the IMAP server is to be chosen explicitly, interact with the user.
+
+ if not conf.has_section('imap'):
+ conf.add_section('imap')
+
+ imap_uri = conf.get('imap', 'uri')
+ imap_uri_default = "imaps://localhost:993/"
+
+ if not conf.check_only:
+ if ask_for_server:
+ imap_uri = ask_question("kolab-conf/imap-server-selection",
+ _("""
+ Please indicate the IMAP server to be used by the Kolab
+ components. (If this is a remote server, no attempt will be
+ made to set up server instances or to configure services.)
+ """),
+ _("IMAP URI"),
+ default=(imap_uri or imap_uri_default)
+ ).strip()
+
+ # Remember the chosen server, used to connect and perform work.
+
+ conf.command_set('imap', 'uri', imap_uri or imap_uri_default)
+
+ imap_server = get_host_from_url(conf.get('imap', 'uri'))
+ local_server = imap_server == "localhost"
+ local_server_type = local_server and have_local_imap_server()
+
+ # Only attempt to configure a local server.
+
+ if local_server and not local_server_type:
+ if conf.check_only:
+ utils.setup_status("imap", _("not installed"))
+ return
+
+ # If there wasn't any URI specified, indicate that setup needs doing.
+
+ elif not local_server:
+ if conf.check_only:
+ utils.setup_status("imap", not imap_uri and _("needs setup") or _("setup done"))
+ return
+
+ # Propagate the IMAP URI to any local server setting.
+
+ if local_server:
+ if not conf.has_section(local_server_type):
+ conf.add_section(local_server_type)
+ conf.command_set(local_server_type, 'uri', conf.get('imap', 'uri'))
+
+ conf.command_set('kolab', 'imap_backend', local_server_type or 'remote-imap')
+
+ # Obtain the settings.
+
+ imapd_settings = get_imap_settings()
+
+ # Apply the settings.
+
+ if local_server_type == "cyrus-imap":
+ matching_config = configure_cyrus_imapd(imapd_settings)
+ elif local_server_type == "dovecot-imap":
+ matching_config = configure_dovecot_imapd(imapd_settings)
+ else:
+ matching_config = True
+
+ # When only checking, test the annotations file, give the status and return.
+
+ if conf.check_only:
+ if local_server_type == "cyrus-imap":
+ matching_config = matching_config and file_contains_data('/etc/imapd.annotations.conf', annotations, exact=True)
+ utils.setup_status("imap", matching_config and _("setup done") or _("needs setup"))
+ return
+
+ # Write the annotations file.
+
+ if local_server_type == "cyrus-imap":
+ fp = open('/etc/imapd.annotations.conf', 'w')
+ fp.write(annotations)
+ fp.close()
+
+ # Update the configuration file.
+
+ fp = open(conf.defaults.config_file, "w+")
+ conf.cfg_parser.write(fp)
+ fp.close()
+
+ # Administer any local server.
+
+ if local_server_type == "cyrus-imap":
+ administer_cyrus_imapd()
+ elif local_server_type == "dovecot-imap":
+ administer_dovecot_imapd()
+
+def get_imap_settings():
+ return {
"ldap_servers": conf.get('ldap', 'ldap_uri'),
+ "ldap_host": get_host_from_url(conf.get('ldap', 'ldap_uri')),
"ldap_base": conf.get('ldap', 'base_dn'),
"ldap_bind_dn": conf.get('ldap', 'service_bind_dn'),
"ldap_password": conf.get('ldap', 'service_bind_pw'),
- "ldap_filter": '(|(&(|(uid=%s)(uid=cyrus-murder))(uid=%%U))(&(|(uid=%%U)(mail=%%U@%%d)(mail=%%U@%%r))(objectclass=kolabinetorgperson)))' % (conf.get('cyrus-imap', 'admin_login')),
+ "ldap_user_filter": conf.get('ldap', 'kolab_user_filter'),
+ "ldap_filter": '(|'
+ '(&(|(uid=%s)(uid=cyrus-murder))(uid=%%U))'
+ '(&(|(uid=%%U)(mail=%%U@%%d)(mail=%%U@%%r))(objectclass=kolabinetorgperson))'
+ ')' % conf.get('cyrus-imap', 'admin_login'),
"ldap_user_attribute": conf.get('cyrus-sasl', 'result_attribute'),
"ldap_group_base": conf.get('ldap', 'base_dn'),
"ldap_group_filter": "(&(cn=%u)(objectclass=ldapsubentry)(objectclass=nsroledefinition))",
@@ -64,111 +186,129 @@ def execute(*args, **kw):
"ldap_member_attribute": "nsrole",
"admins": conf.get('cyrus-imap', 'admin_login'),
"postuser": "shared",
+ "dovecot_mail_uid": conf.get('dovecot-imap', 'mail_uid') or 'vmail',
+ "dovecot_mail_gid": conf.get('dovecot-imap', 'mail_gid') or 'vmail',
+ "dovecot_mail_location": conf.get('dovecot-imap', 'mail_location') or (
+ 'mbox:/var/mail/%s/%%u' % (conf.get('dovecot-imap', 'mail_uid') or 'vmail')
+ ),
}
- template_file = None
+def configure_cyrus_imapd(imapd_settings):
+ matching_config = True
- if os.path.isfile('/etc/kolab/templates/imapd.conf.tpl'):
- template_file = '/etc/kolab/templates/imapd.conf.tpl'
- elif os.path.isfile('/usr/share/kolab/templates/imapd.conf.tpl'):
- template_file = '/usr/share/kolab/templates/imapd.conf.tpl'
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'imapd.conf.tpl'))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'imapd.conf.tpl'))
+ for template_basename, output_file, settings in [
+ ('imapd.conf.tpl', '/etc/imapd.conf', [imapd_settings]),
+ ('cyrus.conf.tpl', '/etc/cyrus.conf', [{}])
+ ]:
- if not template_file == None:
- fp = open(template_file, 'r')
- template_definition = fp.read()
- fp.close()
+ template_file = get_template_path(template_basename)
- t = Template(template_definition, searchList=[imapd_settings])
- fp = open('/etc/imapd.conf', 'w')
- fp.write(t.__str__())
- fp.close()
+ # Instantiate the template unless only checking is being performed.
- else:
- log.error(_("Could not write out Cyrus IMAP configuration file /etc/imapd.conf"))
- return
+ if template_file is not None:
+ matching_config = instantiate_template(template_file, output_file, settings, check_only=conf.check_only) and matching_config
+ else:
+ log.error(_("Could not write out Cyrus IMAP configuration file %s") % output_file)
+ sys.exit(1)
- cyrus_settings = {}
+ return matching_config
- template_file = None
+def configure_dovecot_imapd(imapd_settings):
+ matching_config = True
- if os.path.isfile('/etc/kolab/templates/cyrus.conf.tpl'):
- template_file = '/etc/kolab/templates/cyrus.conf.tpl'
- elif os.path.isfile('/usr/share/kolab/templates/cyrus.conf.tpl'):
- template_file = '/usr/share/kolab/templates/cyrus.conf.tpl'
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'cyrus.conf.tpl'))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'cyrus.conf.tpl'))
+ # See: http://wiki2.dovecot.org/AuthDatabase/LDAP/AuthBinds
+ # See: http://wiki2.dovecot.org/Dict
+ # See: http://hg.dovecot.org/dovecot-metadata-plugin/file/tip/README
- if not template_file == None:
- fp = open(template_file, 'r')
- template_definition = fp.read()
- fp.close()
+ for filename, substitutions in [
+ ("/etc/dovecot/conf.d/10-auth.conf", [
+ ('^!include auth-system.conf.ext', '#!include auth-system.conf.ext'),
+ ('^#*!include auth-ldap.conf.ext', '!include auth-ldap.conf.ext'),
+ ]),
+ ("/etc/dovecot/dovecot-ldap.conf.ext", [
+ ('^#*hosts =.*?$', 'hosts = %(ldap_host)s' % imapd_settings),
+ ('^#*dn =.*?$', 'dn = %(ldap_bind_dn)s' % imapd_settings),
+ ('^#*dnpass =.*?$', 'dnpass = %(ldap_password)s' % imapd_settings),
+ ('^#*base =.*?$', 'base = %(ldap_base)s' % imapd_settings),
+ ('^#*auth_bind =.*?$', 'auth_bind = yes'),
+ ('^#*auth_bind_userdn =.*?$', 'auth_bind_userdn = uid=%%u,%(ldap_member_base)s' % imapd_settings),
+ ('^#*user_filter =.*?$', 'user_filter = (&%(ldap_user_filter)s(uid=%%u))' % imapd_settings),
+ ]),
+ ("/etc/dovecot/dovecot.conf", [
+ ("dict {.*?}", "dict {\n metadata = file:/var/lib/dovecot/shared-metadata\n}")
+ ]),
+ ("/etc/dovecot/conf.d/10-master.conf", [
+ ("unix_listener dict {.*?}", "unix_listener dict {\n mode = 0600\n user = %(dovecot_mail_uid)s\n }" % imapd_settings)
+ ]),
+ ("/etc/dovecot/conf.d/10-mail.conf", [
+ ("^#*mail_plugins =.*?$", "mail_plugins = metadata"),
+ ("^#*mail_uid =.*?$", "mail_uid = %(dovecot_mail_uid)s" % imapd_settings),
+ ("^#*mail_gid =.*?$", "mail_gid = %(dovecot_mail_gid)s" % imapd_settings),
+ ("^#*mail_location =.*?$", "mail_location = %(dovecot_mail_location)s" % imapd_settings)
+ ]),
+ ("/etc/dovecot/conf.d/20-imap.conf", [
+ ("#*mail_plugins =.*?$", "mail_plugins = $mail_plugins imap_metadata imap_annotatemore")
+ ]),
+ ("/etc/dovecot/conf.d/90-plugin.conf", [
+ ("plugin {\n (?!metadata_dict = proxy::metadata)", "plugin {\n metadata_dict = proxy::metadata\n")
+ ]),
+ ]:
- t = Template(template_definition, searchList=[cyrus_settings])
- fp = open('/etc/cyrus.conf', 'w')
- fp.write(t.__str__())
- fp.close()
+ s = readfile(filename)
+ original = s
- else:
- log.error(_("Could not write out Cyrus IMAP configuration file /etc/cyrus.conf"))
- return
+ for pattern, substitution in substitutions:
+ s = re.sub(pattern, substitution, s, flags=re.DOTALL | re.MULTILINE)
- annotations = [
- "/vendor/kolab/activesync,mailbox,string,backend,value.priv,r",
- "/vendor/kolab/color,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/displayname,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/folder-test,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/folder-type,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/incidences-for,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/pxfb-readable-for,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/uniqueid,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/kolab/h-share-attr-desc,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/horde/share-params,mailbox,string,backend,value.shared value.priv,a",
- "/vendor/x-toltec/test,mailbox,string,backend,value.shared value.priv,a",
- ]
-
- fp = open('/etc/imapd.annotations.conf', 'w')
- fp.write("\n".join(annotations))
- fp.close()
+ matching_config = matching_config and original == s
+
+ if not matching_config and not conf.check_only:
+ writefile(filename, s)
+
+ return matching_config
+
+def administer_cyrus_imapd():
+ set_service_default('kolab-saslauthd', 'START', 'yes')
+
+ if not (
+ control_service('saslauthd', 'stop') and
+ control_service('kolab-saslauthd', 'restart') and
+ control_service('cyrus-imapd', 'restart')
+ ):
- if os.path.isfile('/etc/default/kolab-saslauthd'):
- myaugeas = Augeas()
- setting = os.path.join('/files/etc/default/kolab-saslauthd','START')
-
- if not myaugeas.get(setting) == 'yes':
- myaugeas.set(setting,'yes')
- myaugeas.save()
-
- myaugeas.close()
-
- if os.path.isfile('/bin/systemctl'):
- subprocess.call(['systemctl', 'stop', 'saslauthd.service'])
- subprocess.call(['systemctl', 'restart', 'kolab-saslauthd.service'])
- subprocess.call(['systemctl', 'restart', 'cyrus-imapd.service'])
- elif os.path.isfile('/sbin/service'):
- subprocess.call(['service', 'saslauthd', 'stop'])
- subprocess.call(['service', 'kolab-saslauthd', 'restart'])
- subprocess.call(['service', 'cyrus-imapd', 'restart'])
- elif os.path.isfile('/usr/sbin/service'):
- subprocess.call(['/usr/sbin/service','saslauthd','stop'])
- subprocess.call(['/usr/sbin/service','kolab-saslauthd','restart'])
- subprocess.call(['/usr/sbin/service','cyrus-imapd','restart'])
- else:
log.error(_("Could not start the cyrus-imapd and kolab-saslauthd services."))
- if os.path.isfile('/bin/systemctl'):
- subprocess.call(['systemctl', 'disable', 'saslauthd.service'])
- subprocess.call(['systemctl', 'enable', 'kolab-saslauthd.service'])
- subprocess.call(['systemctl', 'enable', 'cyrus-imapd.service'])
- elif os.path.isfile('/sbin/chkconfig'):
- subprocess.call(['chkconfig', 'saslauthd', 'off'])
- subprocess.call(['chkconfig', 'kolab-saslauthd', 'on'])
- subprocess.call(['chkconfig', 'cyrus-imapd', 'on'])
- elif os.path.isfile('/usr/sbin/update-rc.d'):
- subprocess.call(['/usr/sbin/update-rc.d', 'saslauthd', 'disable'])
- subprocess.call(['/usr/sbin/update-rc.d', 'kolab-saslauthd', 'defaults'])
- subprocess.call(['/usr/sbin/update-rc.d', 'cyrus-imapd', 'defaults'])
- else:
+ if not (
+ configure_service('saslauthd', False) and
+ configure_service('kolab-saslauthd', True) and
+ configure_service('cyrus-imapd', True)
+ ):
+
log.error(_("Could not configure to start on boot, the " + \
"cyrus-imapd and kolab-saslauthd services."))
+
+def administer_dovecot_imapd():
+ set_service_default('dovecot', 'START', 'yes')
+
+ if not control_service('dovecot', 'restart'):
+ log.error(_("Could not start the dovecot service."))
+
+ if not configure_service('dovecot', True):
+ log.error(_("Could not configure to start on boot, the " + \
+ "dovecot service."))
+
+# Data used above.
+
+annotations = """\
+/vendor/kolab/activesync,mailbox,string,backend,value.priv,r
+/vendor/kolab/color,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/displayname,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/folder-test,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/folder-type,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/incidences-for,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/pxfb-readable-for,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/uniqueid,mailbox,string,backend,value.shared value.priv,a
+/vendor/kolab/h-share-attr-desc,mailbox,string,backend,value.shared value.priv,a
+/vendor/horde/share-params,mailbox,string,backend,value.shared value.priv,a
+/vendor/x-toltec/test,mailbox,string,backend,value.shared value.priv,a
+"""