summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Boddie <paul@boddie.org.uk>2014-08-07 12:26:48 (GMT)
committerPaul Boddie <paul@boddie.org.uk>2014-08-07 12:26:48 (GMT)
commit53d4ddb5685673261b54ecadedabcbc08816fc76 (patch)
treee67b481201982e385b42fc0c40546749c6fe20e4
parent258a9829cdf6f661ab4213586ad910ed47f5360d (diff)
downloadpykolab-53d4ddb5685673261b54ecadedabcbc08816fc76.tar.gz
Tidied roundcube setup.
Added debconf support.
-rw-r--r--pykolab/setup/setup_roundcube.py407
1 files changed, 226 insertions, 181 deletions
diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py
index 56110cb..dfc2417 100644
--- a/pykolab/setup/setup_roundcube.py
+++ b/pykolab/setup/setup_roundcube.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,12 +18,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-from Cheetah.Template import Template
+from glob import glob
+from os.path import join
+from subprocess import call, Popen, PIPE
import hashlib
import os
import random
import re
-import subprocess
import sys
import time
@@ -32,6 +34,7 @@ import pykolab
from pykolab import utils
from pykolab.constants import *
+from pykolab.setup.services import *
from pykolab.translate import _
log = pykolab.getLogger('pykolab.setup')
@@ -40,200 +43,242 @@ conf = pykolab.getConf()
def __init__():
components.register('roundcube', execute, description=description(), after=['mysql','ldap'])
+def cli_options():
+ roundcube_group = conf.add_cli_parser_option_group(_("Roundcube Options"))
+
+ roundcube_group.add_option(
+ "--reset-roundcube-config",
+ dest = "reset_roundcube_config",
+ action = "store_true",
+ default = False,
+ help = _("Reset the Roundcube configuration.")
+ )
+
def description():
return _("Setup Roundcube.")
-def execute(*args, **kw):
- print >> sys.stderr, utils.multiline_message(
- _("""
- Please supply a password for the MySQL user 'roundcube'.
- This password will be used by the Roundcube webmail
- interface.
- """)
- )
+def random_digest():
+ return hashlib.md5("%s" % random.random()).digest().encode("base64")
- mysql_roundcube_password = utils.ask_question(
- _("MySQL roundcube password"),
- default=utils.generate_password(),
- password=True,
- confirm=True
- )
+def execute(*args, **kw):
- conf.mysql_roundcube_password = mysql_roundcube_password
-
- rc_settings = {
- 'des_key': re.sub(
- r'[^a-zA-Z0-9]',
- "",
- "%s%s" % (
- hashlib.md5("%s" % random.random()).digest().encode("base64"),
- hashlib.md5("%s" % random.random()).digest().encode("base64")
- )
- )[:24],
-
- 'imap_admin_login': conf.get('cyrus-imap', 'admin_login'),
- 'imap_admin_password': conf.get('cyrus-imap', 'admin_password'),
- 'ldap_base_dn': conf.get('ldap', 'base_dn'),
- 'ldap_group_base_dn': conf.get('ldap', 'group_base_dn'),
- 'ldap_group_filter': conf.get('ldap', 'group_filter'),
- 'ldap_ldap_uri': conf.get('ldap', 'ldap_uri'),
- 'ldap_resource_base_dn': conf.get('ldap', 'resource_base_dn'),
- 'ldap_resource_filter': conf.get('ldap', 'resource_filter'),
- 'ldap_service_bind_dn': conf.get('ldap', 'service_bind_dn'),
- 'ldap_service_bind_pw': conf.get('ldap', 'service_bind_pw'),
- 'ldap_user_base_dn': conf.get('ldap', 'user_base_dn'),
- 'ldap_user_filter': conf.get('ldap', 'user_filter'),
- 'primary_domain': conf.get('kolab','primary_domain'),
- 'mysql_uri': 'mysqli://roundcube:%s@localhost/roundcube' % (mysql_roundcube_password),
- 'conf': conf
- }
-
-
- want_files = [
- 'acl.inc.php',
- 'calendar.inc.php',
- 'config.inc.php',
- 'kolab_addressbook.inc.php',
- 'kolab_auth.inc.php',
- 'kolab_delegation.inc.php',
- 'kolab_files.inc.php',
- 'kolab_folders.inc.php',
- 'libkolab.inc.php',
- 'managesieve.inc.php',
- 'owncloud.inc.php',
- 'password.inc.php',
- 'recipient_to_contact.inc.php',
- 'terms.html',
- 'terms.inc.php'
- ]
-
- for want_file in want_files:
- template_file = None
- if os.path.isfile('/etc/kolab/templates/roundcubemail/%s.tpl' % (want_file)):
- template_file = '/etc/kolab/templates/roundcubemail/%s.tpl' % (want_file)
- elif os.path.isfile('/usr/share/kolab/templates/roundcubemail/%s.tpl' % (want_file)):
- template_file = '/usr/share/kolab/templates/roundcubemail/%s.tpl' % (want_file)
- elif os.path.isfile(os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'roundcubemail', '%s.tpl' % (want_file)))):
- template_file = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'share', 'templates', 'roundcubemail', '%s.tpl' % (want_file)))
-
- if not template_file == None:
- log.debug(_("Using template file %r") % (template_file), level=8)
- fp = open(template_file, 'r')
- template_definition = fp.read()
- fp.close()
-
- t = Template(template_definition, searchList=[rc_settings])
- log.debug(
- _("Successfully compiled template %r, writing out to %r") % (template_file, want_file),
- level=8
- )
-
- fp = None
- if os.path.isdir('/etc/roundcubemail'):
- fp = open('/etc/roundcubemail/%s' % (want_file), 'w')
- elif os.path.isdir('/etc/roundcube'):
- fp = open('/etc/roundcube/%s' % (want_file), 'w')
-
- if not fp == None:
- fp.write(t.__str__())
- fp.close()
-
- schema_files = []
- for root, directories, filenames in os.walk('/usr/share/doc/'):
- for directory in directories:
- if directory.startswith("roundcubemail"):
- for root, directories, filenames in os.walk(os.path.join(root, directory)):
- for filename in filenames:
- if filename.startswith('mysql.initial') and filename.endswith('.sql'):
- schema_filepath = os.path.join(root,filename)
- if not schema_filepath in schema_files:
- schema_files.append(schema_filepath)
-
- break
- break
-
- if os.path.isdir('/usr/share/roundcubemail'):
- rcpath = '/usr/share/roundcubemail/'
- elif os.path.isdir('/usr/share/roundcube'):
- rcpath = '/usr/share/roundcube/'
- else:
- log.error(_("Roundcube installation path not found."))
- return
-
- for root, directories, filenames in os.walk(rcpath + 'plugins/calendar/drivers/kolab/'):
- for filename in filenames:
- if filename.startswith('mysql') and filename.endswith('.sql'):
- schema_filepath = os.path.join(root,filename)
- if not schema_filepath in schema_files:
- schema_files.append(schema_filepath)
-
- for root, directories, filenames in os.walk(rcpath + 'plugins/libkolab/'):
- for filename in filenames:
- if filename.startswith('mysql') and filename.endswith('.sql'):
- schema_filepath = os.path.join(root,filename)
- if not schema_filepath in schema_files:
- schema_files.append(schema_filepath)
-
- if not os.path.isfile('/tmp/kolab-setup-my.cnf'):
- utils.multiline_message(
- """Please supply the MySQL root password"""
+ # Signal that interaction may occur. This will involve debconf and similar
+ # system-specific mechanisms if available.
+
+ start_interaction("kolab-conf/title-roundcube")
+ try:
+ _execute(*args, **kw)
+ finally:
+ stop_interaction()
+
+def _execute(*args, **kw):
+
+ # Stop if MySQL is not actually installed.
+
+ if not have_mysql():
+ if conf.check_only:
+ utils.setup_status("roundcube", _("needs setup"))
+ return
+ else:
+ print >> sys.stderr, _("MySQL not installed: cannot initialise Roundcube.")
+ sys.exit(1)
+
+ # Obtain an SMTP server.
+
+ smtp_host = conf.get('smtp', 'host')
+
+ if not smtp_host:
+ if conf.check_only:
+ utils.setup_status("roundcube", _("needs setup"))
+ return
+
+ smtp_host = ask_question("kolab-conf/smtp-server-selection",
+ _("""
+ Please indicate the SMTP 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.)
+ """),
+ _("SMTP server host"),
+ default="localhost"
)
- mysql_root_password = utils.ask_question(
- _("MySQL root password"),
- password=True
- )
+ # Use the existing root credentials.
- data = """
-[mysql]
-user=root
-password='%s'
-""" % (mysql_root_password)
+ defaults_file = get_mysql_defaults()
- fp = open('/tmp/kolab-setup-my.cnf', 'w')
- os.chmod('/tmp/kolab-setup-my.cnf', 0600)
- fp.write(data)
- fp.close()
+ if not isfile(defaults_file):
+ if conf.check_only:
+ utils.setup_status("roundcube", _("needs setup"))
+ return
+ make_mysql_defaults_file(defaults_file)
- p1 = subprocess.Popen(['echo', 'create database roundcube;'], stdout=subprocess.PIPE)
- p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
- p1.stdout.close()
- p2.communicate()
+ # Test for a MySQL database.
- p1 = subprocess.Popen(['echo', 'GRANT ALL PRIVILEGES ON roundcube.* TO \'roundcube\'@\'localhost\' IDENTIFIED BY \'%s\';' % (mysql_roundcube_password)], stdout=subprocess.PIPE)
- p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
- p1.stdout.close()
- p2.communicate()
+ if not have_mysql_database(defaults_file, 'roundcube'):
+ if conf.check_only:
+ utils.setup_status("roundcube", _("needs setup"))
+ return
- for schema_file in schema_files:
- p1 = subprocess.Popen(['cat', schema_file], stdout=subprocess.PIPE)
- p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf', 'roundcube'], stdin=p1.stdout)
- p1.stdout.close()
- p2.communicate()
+ # Gather schema files for database initialisation.
+
+ rcpath = get_roundcube_resources()
+
+ if not rcpath:
+ log.error(_("Roundcube installation path not found."))
+ return
+
+ # Retain the ordering.
+
+ schema_files = []
+
+ for roundcube_dir in glob('/usr/share/doc/roundcubemail*'):
+ for root, directories, filenames in os.walk(roundcube_dir):
+ for filename in filenames:
+ if filename.startswith('mysql.initial') and filename.endswith('.sql'):
+ schema_files.append(join(root,filename))
+
+ for search_root in [
+ join(rcpath, 'plugins/calendar/drivers/kolab/'),
+ join(rcpath, 'plugins/libkolab/')
+ ]:
+
+ for root, directories, filenames in os.walk(search_root):
+ for filename in filenames:
+ if filename.startswith('mysql') and filename.endswith('.sql'):
+ schema_files.append(join(root,filename))
- p1 = subprocess.Popen(['echo', 'FLUSH PRIVILEGES;'], stdout=subprocess.PIPE)
- p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
- p1.stdout.close()
- p2.communicate()
+ # Create the database.
- time.sleep(2)
+ call(['mysql', '--defaults-file=%s' % defaults_file, '-e', 'create database roundcube'])
- if os.path.isfile('/bin/systemctl'):
- subprocess.call(['/bin/systemctl', 'restart', 'httpd.service'])
- elif os.path.isfile('/sbin/service'):
- subprocess.call(['/sbin/service', 'httpd', 'restart'])
- elif os.path.isfile('/usr/sbin/service'):
- subprocess.call(['/usr/sbin/service','apache2','restart'])
+ # Apply each schema file.
+ # MySQL does not have a read-from-file option as such.
+
+ for schema_file in schema_files:
+ p1 = Popen(['cat', schema_file], stdout=PIPE)
+ p2 = Popen(['mysql', '--defaults-file=%s' % defaults_file, 'roundcube'], stdin=p1.stdout)
+ p2.communicate()
+
+ # Test for a MySQL user. Reset the user and configuration if explicitly
+ # requested.
+
+ if have_mysql_user(defaults_file, 'roundcube') and not conf.reset_roundcube_config:
+ if not conf.check_only:
+ print >> sys.stderr, _("Roundcube database account already exists and will not be configured.")
+ log.info(_("A user called %s already exists. Not creating another one.") % 'roundcube')
else:
- log.error(_("Could not start the webserver server service."))
-
- if os.path.isfile('/bin/systemctl'):
- subprocess.call(['/bin/systemctl', 'enable', 'httpd.service'])
- elif os.path.isfile('/sbin/chkconfig'):
- subprocess.call(['/sbin/chkconfig', 'httpd', 'on'])
- elif os.path.isfile('/usr/sbin/update-rc.d'):
- subprocess.call(['/usr/sbin/update-rc.d', 'apache2', 'defaults'])
+ if conf.check_only:
+ utils.setup_status("roundcube", _("needs setup"))
+ return
+
+ mysql_roundcube_password = ask_question("kolab-conf/roundcube-password",
+ _("""
+ Please supply a password for the MySQL user 'roundcube'.
+ This password will be used by the Roundcube webmail
+ interface.
+ """),
+ _("MySQL roundcube password"),
+ default=utils.generate_password(),
+ password=True,
+ confirm=True
+ )
+
+ conf.mysql_roundcube_password = mysql_roundcube_password
+
+ # Send the password details via a pipe to avoid leakage via the process list.
+
+ p1 = Popen(['echo', "grant all privileges on roundcube.* to 'roundcube'@'localhost' identified by '%s'" %
+ mysql_roundcube_password.replace("'", "''")], stdout=PIPE)
+ p2 = Popen(['mysql', '--defaults-file=%s' % defaults_file], stdin=p1.stdout)
+ p2.communicate()
+
+ call(['mysql', '--defaults-file=%s' % defaults_file, '-e', 'flush privileges'])
+
+ # Write out the settings including the password details.
+
+ rc_settings = {
+ 'des_key': re.sub(
+ r'[^a-zA-Z0-9]',
+ "",
+ "%s%s" % (random_digest(), random_digest())
+ )[:24],
+
+ 'imap_admin_login': conf.get('cyrus-imap', 'admin_login'),
+ 'imap_admin_password': conf.get('cyrus-imap', 'admin_password'),
+ 'ldap_base_dn': conf.get('ldap', 'base_dn'),
+ 'ldap_group_base_dn': conf.get('ldap', 'group_base_dn'),
+ 'ldap_group_filter': conf.get('ldap', 'group_filter'),
+ 'ldap_ldap_uri': conf.get('ldap', 'ldap_uri'),
+ 'ldap_resource_base_dn': conf.get('ldap', 'resource_base_dn'),
+ 'ldap_resource_filter': conf.get('ldap', 'resource_filter'),
+ 'ldap_service_bind_dn': conf.get('ldap', 'service_bind_dn'),
+ 'ldap_service_bind_pw': conf.get('ldap', 'service_bind_pw'),
+ 'ldap_user_base_dn': conf.get('ldap', 'user_base_dn'),
+ 'ldap_user_filter': conf.get('ldap', 'user_filter'),
+ 'primary_domain': conf.get('kolab','primary_domain'),
+ 'mysql_uri': 'mysqli://roundcube:%s@localhost/roundcube' % (mysql_roundcube_password),
+ 'ldap_host': get_host_from_url(conf.get('ldap', 'ldap_uri')),
+ 'imap_host': get_host_from_url(conf.get('cyrus-imap', 'uri')),
+ 'smtp_host': smtp_host,
+ 'conf': conf,
+ }
+
+ want_files = [
+ 'acl.inc.php',
+ 'calendar.inc.php',
+ 'config.inc.php',
+ 'kolab_addressbook.inc.php',
+ 'kolab_auth.inc.php',
+ 'kolab_delegation.inc.php',
+ 'kolab_files.inc.php',
+ 'kolab_folders.inc.php',
+ 'libkolab.inc.php',
+ 'managesieve.inc.php',
+ 'owncloud.inc.php',
+ 'password.inc.php',
+ 'recipient_to_contact.inc.php',
+ 'terms.html',
+ 'terms.inc.php'
+ ]
+
+ for want_file in want_files:
+ template_file = get_template_path('roundcubemail/%s.tpl' % want_file)
+ roundcube_dir = get_roundcube_config()
+
+ if roundcube_dir:
+ output_file = join(roundcube_dir, want_file)
+ else:
+ output_file = None
+
+ if template_file is not None and output_file is not None:
+ log.debug(_("Using template file %r") % (template_file), level=8)
+ instantiate_template(template_file, output_file, [rc_settings])
+
+ # If nothing needed updating, assume that the setup was done.
+
+ if conf.check_only:
+ utils.setup_status("roundcube", _("setup done"))
+
else:
- log.error(_("Could not configure to start on boot, the " + \
- "webserver server service."))
+ # Update the configuration file.
+
+ if not conf.has_section('smtp'):
+ conf.add_section('smtp')
+ conf.command_set('smtp', 'host', smtp_host)
+
+ fp = open(conf.defaults.config_file, "w+")
+ conf.cfg_parser.write(fp)
+ fp.close()
+
+ # Restart the Web server.
+
+ time.sleep(2)
+
+ apache = is_debian() and "apache2" or "httpd"
+ if not control_service(apache, 'restart'):
+ log.error(_("Could not start the webserver service."))
+ if not configure_service(apache, True):
+ log.error(_("Could not configure to start on boot, the " + \
+ "webserver service."))