diff --git a/pykolab/setup/setup_mysql.py b/pykolab/setup/setup_mysql.py index c4cd90f..08b7b7c 100644 --- a/pykolab/setup/setup_mysql.py +++ b/pykolab/setup/setup_mysql.py @@ -1,341 +1,341 @@ # -*- coding: utf-8 -*- # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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, either version 3 of the License, or # (at your option) any later version. # 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os import subprocess import tempfile import time import components import pykolab from pykolab import utils from pykolab.constants import * from pykolab.translate import _ log = pykolab.getLogger('pykolab.setup') conf = pykolab.getConf() def __init__(): components.register('mysql', execute, description=description()) def cli_options(): mysql_group = conf.add_cli_parser_option_group(_("MySQL Options")) mysql_group.add_option( "--mysqlserver", dest="mysqlserver", action="store", help=_("Specify whether to use an (existing), (unix_socket) or (new) MySQL server.") ) mysql_group.add_option( "--mysqlhost", dest="mysqlhost", action="store", default='127.0.0.1', help=_("The MySQL host address.") ) mysql_group.add_option( "--mysqlrootpw", dest="mysqlrootpw", action="store", help=_("The MySQL root user password.") ) def description(): return _("Setup MySQL.") def execute(*args, **kw): # noqa: C901 socket_paths = [ "/var/lib/mysql/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/run/mysql/mysql.sock", "/var/run/mysqld/mysqld.pid" ] # on CentOS7, there is MariaDB instead of MySQL if conf.mysqlserver != 'existing': mysqlservice = 'mysqld.service' if os.path.isfile('/usr/lib/systemd/system/mariadb.service'): mysqlservice = 'mariadb.service' elif os.path.isfile('/usr/lib/systemd/system/mysql.service'): mysqlservice = 'mysql.service' if not os.path.isfile('/usr/lib/systemd/system/' + mysqlservice): # on Debian Jessie, systemctl restart mysql mysqlservice = 'mysql' if os.path.isfile('/bin/systemctl'): subprocess.call(['/bin/systemctl', 'restart', mysqlservice]) elif os.path.isfile('/sbin/service'): subprocess.call(['/sbin/service', 'mysqld', 'restart']) elif os.path.isfile('/usr/sbin/service'): subprocess.call(['/usr/sbin/service', 'mysql', 'restart']) else: log.error(_("Could not start the MySQL database service.")) if os.path.isfile('/bin/systemctl'): subprocess.call(['/bin/systemctl', 'enable', mysqlservice]) elif os.path.isfile('/sbin/chkconfig'): subprocess.call(['/sbin/chkconfig', 'mysqld', 'on']) elif os.path.isfile('/usr/sbin/update-rc.d'): subprocess.call(['/usr/sbin/update-rc.d', 'mysql', 'defaults']) else: log.error( _("Could not configure to start on boot, the MySQL database service.") ) log.info(_("Waiting for at most 30 seconds for MySQL/MariaDB to settle...")) max_wait = 30 while max_wait > 0: for socket_path in socket_paths: if os.path.exists(socket_path): max_wait = 0 if max_wait > 0: max_wait = max_wait - 1 time.sleep(1) options = { 1: "Existing MySQL server (with root password already set).", 2: "Existing MySQL server (with unix_socket authentication plugin).", 3: "New MySQL server (needs to be initialized)." } answer = 0 if conf.mysqlserver != 'existing': if len([x for x in socket_paths if os.path.exists(x)]) > 0: if conf.mysqlserver: if conf.mysqlserver == 'existing': answer = 1 elif conf.mysqlserver == 'unix_socket': answer = 2 elif conf.mysqlserver == 'new': answer = 3 if answer == 0: answer = utils.ask_menu(_("What MySQL server are we setting up?"), options) else: answer = 1 if answer == "1" or answer == 1: if not conf.mysqlrootpw: print >> sys.stderr, utils.multiline_message( _(""" Please supply the root password for MySQL, so we can set up user accounts for other components that use MySQL. """) ) mysql_root_password = utils.ask_question( _("MySQL root password"), password=True ) else: mysql_root_password = conf.mysqlrootpw elif answer == "2" or answer == 2: mysql_root_password = 'unix_socket' else: print >> sys.stderr, utils.multiline_message( _(""" Please supply a root password for MySQL. This password will be the administrative user for this MySQL server, and it should be kept a secret. After this setup process has completed, Kolab is going to discard and forget about this password, but you will need it for administrative tasks in MySQL. """) ) mysql_root_password = utils.ask_question( _("MySQL root password"), default=utils.generate_password(), password=True, confirm=True ) p1 = subprocess.Popen( [ 'echo', 'UPDATE mysql.user SET Password=PASSWORD(\'%s\') WHERE User=\'root\';' % ( mysql_root_password ) ], stdout=subprocess.PIPE ) p2 = subprocess.Popen(['mysql'], stdin=p1.stdout) p1.stdout.close() p2.communicate() p1 = subprocess.Popen( [ 'echo', "UPDATE mysql.user SET authentication_string=PASSWORD('%s') WHERE User='root';" % ( mysql_root_password ) ], stdout=subprocess.PIPE ) p2 = subprocess.Popen(['mysql'], stdin=p1.stdout) p1.stdout.close() p2.communicate() p1 = subprocess.Popen( [ 'echo', """ UPDATE mysql.user SET plugin='mysql_native_password' WHERE User='root' AND plugin='auth_socket'; """ ], stdout=subprocess.PIPE ) p2 = subprocess.Popen(['mysql'], stdin=p1.stdout) p1.stdout.close() p2.communicate() p1 = subprocess.Popen(['echo', 'FLUSH PRIVILEGES;'], stdout=subprocess.PIPE) p2 = subprocess.Popen(['mysql'], stdin=p1.stdout) p1.stdout.close() p2.communicate() socket_path = None socket_paths = [ "/var/lib/mysql/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/run/mysql/mysql.sock" ] for sp in socket_paths: if os.path.exists(sp): socket_path = sp if mysql_root_password == "unix_socket" and socket_path is not None: data = """ [mysql] user=root password= host=localhost socket=%s """ % (socket_path) else: data = """ [mysql] user=root password='%s' host=%s """ % (mysql_root_password, conf.mysqlhost) fp = open('/tmp/kolab-setup-my.cnf', 'w') - os.chmod('/tmp/kolab-setup-my.cnf', 600) + os.chmod('/tmp/kolab-setup-my.cnf', 0600) fp.write(data) fp.close() schema_file = None for root, directories, filenames in os.walk('/usr/share/doc/'): for filename in filenames: if filename.startswith('kolab_wap') and filename.endswith('.sql'): # Skip the Oracle file if filename.endswith('oracle.sql'): continue schema_file = os.path.join(root, filename) if schema_file is not None: p1 = subprocess.Popen(['echo', 'create database kolab;'], stdout=subprocess.PIPE) p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout) p1.stdout.close() p2.communicate() print >> sys.stderr, utils.multiline_message( _(""" Please supply a password for the MySQL user 'kolab'. This password will be used by Kolab services, such as the Web Administration Panel. """) ) mysql_kolab_password = utils.ask_question( _("MySQL kolab password"), default=utils.generate_password(), password=True, confirm=True ) p1 = subprocess.Popen( [ 'echo', "GRANT ALL PRIVILEGES ON kolab.* TO 'kolab'@'localhost' IDENTIFIED BY '%s';" % ( mysql_kolab_password ) ], stdout=subprocess.PIPE ) p2 = subprocess.Popen( [ 'mysql', '--defaults-file=/tmp/kolab-setup-my.cnf' ], stdin=p1.stdout ) p1.stdout.close() p2.communicate() p1 = subprocess.Popen(['cat', schema_file], stdout=subprocess.PIPE) p2 = subprocess.Popen( [ 'mysql', '--defaults-file=/tmp/kolab-setup-my.cnf', 'kolab' ], stdin=p1.stdout ) p1.stdout.close() p2.communicate() conf.command_set( 'kolab_wap', 'sql_uri', 'mysql://kolab:%s@localhost/kolab' % (mysql_kolab_password) ) conf.command_set( 'kolab_smtp_access_policy', 'cache_uri', 'mysql://kolab:%s@localhost/kolab' % (mysql_kolab_password) ) else: log.warning(_("Could not find the MySQL Kolab schema file")) diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py index e10dcfe..47ac8fc 100644 --- a/pykolab/setup/setup_roundcube.py +++ b/pykolab/setup/setup_roundcube.py @@ -1,373 +1,373 @@ # -*- coding: utf-8 -*- # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) # # Jeroen van Meeuwen (Kolab Systems) # # 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, either version 3 of the License, or # (at your option) any later version. # 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import codecs import grp import hashlib import os import random import re import subprocess import sys import time from Cheetah.Template import Template import components import pykolab from pykolab import utils from pykolab.constants import * from pykolab.translate import _ # pylint: disable=invalid-name log = pykolab.getLogger('pykolab.setup') conf = pykolab.getConf() def __init__(): components.register('roundcube', execute, description=description(), after=['mysql', 'ldap']) 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. """ ) mysql_roundcube_password = utils.ask_question( "MySQL roundcube password", default=utils.generate_password(), password=True, confirm=True ) 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 } rc_paths = [ "/usr/share/roundcubemail/", "/usr/share/roundcube/", "/srv/www/roundcubemail/", "/var/www/roundcubemail/" ] rcpath = '' for rc_path in rc_paths: if os.path.isdir(rc_path): rcpath = rc_path break if not os.path.isdir(rcpath): log.error("Roundcube installation path not found.") return if os.access(rcpath + 'skins/kolab/', os.R_OK): rc_settings['skin'] = 'kolab' elif os.access(rcpath + 'skins/enterprise/', os.R_OK): rc_settings['skin'] = 'enterprise' elif os.access(rcpath + 'skins/chameleon/', os.R_OK): rc_settings['skin'] = 'chameleon' else: rc_settings['skin'] = 'larry' 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', '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) if template_file is not None: # pylint: disable=logging-not-lazy log.debug("Using template file %r" % (template_file), level=8) filep = codecs.open(template_file, 'r', encoding='utf-8') template_definition = filep.read() filep.close() t = Template(template_definition, searchList=[rc_settings]) # pylint: disable=logging-not-lazy log.debug( "Successfully compiled template %r, writing out to %r" % ( template_file, want_file ), level=8 ) filep = None if os.path.isdir('/etc/roundcubemail'): filep = codecs.open('/etc/roundcubemail/%s' % (want_file), 'w', encoding='utf-8') elif os.path.isdir('/etc/roundcube'): filep = codecs.open('/etc/roundcube/%s' % (want_file), 'w', encoding='utf-8') if filep is not None: filep.write(t.respond()) filep.close() schema_files = [] # pylint: disable=too-many-nested-blocks for root, directories, filenames in os.walk('/usr/share/doc/'): directories.sort() 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 schema_filepath not in schema_files: schema_files.append(schema_filepath) if schema_files: break if schema_files: break 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 schema_filepath not 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 schema_filepath not in schema_files: schema_files.append(schema_filepath) for root, directories, filenames in os.walk('/usr/share/doc/'): directories.sort() for directory in directories: if directory.startswith("chwala"): 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 schema_filepath not in schema_files: schema_files.append(schema_filepath) if len(schema_files) > 0: break if len(schema_files) > 0: break if not os.path.isfile('/tmp/kolab-setup-my.cnf'): print >> sys.stderr, utils.multiline_message( """Please supply the MySQL root password (use 'unix_socket' for socket based authentication)""" ) mysql_root_password = utils.ask_question( _("MySQL root password"), password=True ) socket_path = None socket_paths = [ "/var/lib/mysql/mysql.sock", "/var/run/mysqld/mysqld.sock", "/var/run/mysql/mysql.sock" ] for sp in socket_paths: if os.path.exists(sp): socket_path = sp if mysql_root_password == "unix_socket" and socket_path is not None: data = """ [mysql] user=root password= host=localhost socket=%s """ % (socket_path) else: data = """ [mysql] user=root password='%s' host=%s """ % (mysql_root_password, conf.mysqlhost) fp = open('/tmp/kolab-setup-my.cnf', 'w') - os.chmod('/tmp/kolab-setup-my.cnf', 600) + os.chmod('/tmp/kolab-setup-my.cnf', 0600) fp.write(data) fp.close() 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() 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() 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() 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() time.sleep(2) # Find Roundcube configuration that is not readable by the # webserver user/group. if os.path.isdir('/etc/roundcubemail/'): rccpath = "/etc/roundcubemail/" elif os.path.isdir('/etc/roundcube/'): rccpath = "/etc/roundcube" else: log.warning("Cannot find the configuration directory for roundcube.") rccpath = None root_uid = 0 webserver_gid = None for webserver_group in ['apache', 'www-data', 'www']: try: # pylint: disable=unused-variable (a, b, webserver_gid, d) = grp.getgrnam(webserver_group) break # pylint: disable=broad-except except Exception: pass if webserver_gid is not None: if rccpath is not None: for root, directories, filenames in os.walk(rccpath): for filename in filenames: try: os.chown( os.path.join(root, filename), root_uid, webserver_gid ) # pylint: disable=broad-except except Exception: pass httpservice = 'httpd.service' if os.path.isfile('/usr/lib/systemd/system/apache2.service'): httpservice = 'apache2.service' if os.path.isfile('/lib/systemd/system/apache2.service'): # Debian 9 httpservice = 'apache2.service' if os.path.isdir('/lib/systemd/system/apache2.service.d'): httpservice = 'apache2.service' if os.path.isfile('/bin/systemctl'): subprocess.call(['/bin/systemctl', 'restart', httpservice]) 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']) else: log.error("Could not start the webserver server service.") if os.path.isfile('/bin/systemctl'): subprocess.call(['/bin/systemctl', 'enable', httpservice]) 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']) else: log.error( "Could not configure to start on boot, the webserver server service." )