Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
54 KB
Referenced Files
None
Subscribers
None
diff --git a/pykolab/setup/services.py b/pykolab/setup/services.py
index 9a4c1b4..2ee2bd3 100644
--- a/pykolab/setup/services.py
+++ b/pykolab/setup/services.py
@@ -1,379 +1,395 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from augeas import Augeas
from Cheetah.Template import Template
from subprocess import call, Popen, PIPE
from os.path import isfile, abspath, join, exists
from pykolab import utils
from pykolab.translate import _
import pykolab
import sys
conf = pykolab.getConf()
# Commands with service suffix usage and argument ordering "format".
commands_and_options = {
'/bin/systemctl' : (True, ('cmd', 'operation', 'service')),
'/sbin/service' : (False, ('cmd', 'service', 'operation')),
'/usr/sbin/service' : (False, ('cmd', 'service', 'operation')),
'/sbin/chkconfig' : (False, ('cmd', 'service', 'operation')),
'/usr/sbin/update-rc.d' : (False, ('cmd', 'service', 'operation')),
}
# Configuration options corresponding to false and true values.
configuration_options = {
'/bin/systemctl' : ('disable', 'enable'),
'/sbin/chkconfig' : ('off', 'on'),
'/usr/sbin/update-rc.d' : ('disable', 'defaults'),
}
configuration_commands = configuration_options.keys()
control_commands = '/bin/systemctl', '/sbin/service', '/usr/sbin/service'
configuration_command_details = None
control_command_details = None
def get_command_options(commands):
"""
Check for the presence of the given commands, returning the first one
found along with option details.
"""
for name in commands:
if isfile(name):
return name, commands_and_options[name]
return None, None
def get_control_command_details():
"""
Obtain and cache the control command details.
"""
global control_command_details
if control_command_details is None:
control_command_details = get_command_options(control_commands)
return control_command_details
def get_configuration_command_details():
"""
Obtain and cache the configuration command details.
"""
global configuration_command_details
if configuration_command_details is None:
configuration_command_details = get_command_options(configuration_commands)
return configuration_command_details
def apply_command(name, suffix, format, service_name, operation):
"""
Call the given command, adding the applicable service name suffix to the
service name argument, using the "format" to determine the argument
ordering, and employing the service name and operation specified.
"""
cmd = []
for arg in format:
if arg == "cmd":
cmd.append(name)
elif arg == "operation":
cmd.append(operation)
elif arg == "service":
cmd.append('%s%s' % (service_name, suffix))
call(cmd, stdout=PIPE, stderr=PIPE)
def control_service(service_name, operation, suffix=".service"):
"""
Perform a service control operation on the service with the given name.
Where a service command employs a suffix on the service name, any
provided suffix will be added.
"""
name, (use_suffix, format) = get_control_command_details()
if not name:
return False
apply_command(name, use_suffix and suffix or "", format, service_name, operation)
return True
def configure_service(service_name, operation, suffix=".service"):
"""
Perform a service configuration operation on the service with the given
name. Where a service command employs a suffix on the service name, any
provided suffix will be added.
"""
name, (use_suffix, format) = get_configuration_command_details()
if not name:
return False
options = configuration_options[name]
operation = options[operation and 1 or 0]
apply_command(name, use_suffix and suffix or "", format, service_name, operation)
return True
def is_debian():
"""
Special test to deal with inconsistencies in package naming and file
locations.
"""
return exists("/etc/debian_version")
def find_file(name, paths):
for path in paths:
full_path = join(path, name)
if isfile(full_path):
return full_path
return None
def file_contains_data(filename, data, exact=False):
if not exists(filename):
return False
f = open(filename)
try:
if exact:
return f.read() == data
else:
return f.read().find(data) != -1
finally:
f.close()
+def have_command(tokens):
+ try:
+ Popen(tokens, stdout=PIPE, stderr=PIPE)
+ except OSError:
+ return False
+ else:
+ return True
+
# Message and input handling.
# These functions wrap the existing utils functions and provide for debconf
# usage instead of using the plain console. The principal additions to the
# existing functions involve message keys (to template definitions accessed by
# debconf, provided by Debian packages) and the messages that are displayed
# before each question.
debconf = None
db = None
db_passwords = set()
def use_debconf():
return is_debian() and conf.use_system_config
def start_interaction(message_key):
global db, debconf
if use_debconf():
try:
import debconf
except ImportError:
return
debconf.runFrontEnd()
db = debconf.Debconf()
db.settitle(message_key)
def ask_question(message_key, message, question=None, default="", password=False, confirm=False):
"""
Use the given message key to retrieve a question from any
system-supplied configuration system, otherwise using the given message
and question to prompt the user for an answer. Where no question is
actually given, this function just displays a message.
"""
if db is not None:
if password:
db_passwords.add(message_key)
if default:
db.set(message_key, default)
db.fset(message_key, "seen", False)
try:
db.input("high", message_key)
db.go()
return db.getString(message_key)
except debconf.DebconfError:
return None
else:
print >> sys.stderr, utils.multiline_message(message)
if question:
return utils.ask_question(question, default, password, confirm)
else:
return None
def stop_interaction():
if db is not None:
# Erase password information from debconf as suggested by...
# http://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#s6.5.3
for message_key in db_passwords:
db.set(message_key, "")
db.stop()
+# MTA-related functions.
+
+def have_postfix():
+ return have_command(['postfix', 'status'])
+
# PHP-related functions.
php_paths = ['/etc', '/etc/php5/apache2']
def get_php_path(name):
return find_file(name, php_paths)
def get_php_ini_file():
if conf.php_ini_path:
if not isfile(conf.php_ini_path):
return None
else:
return conf.php_ini_path
else:
return get_php_path("php.ini")
# Template-related functions.
template_paths = ['/etc/kolab/templates', '/usr/share/kolab/templates']
def get_template_path(name):
"""
Return the full path to the template with the given name.
"""
relative_path = abspath(join(__file__, '..', '..', '..', 'share', 'templates', name))
return find_file(name, template_paths + [relative_path])
def instantiate_template(template_file, output_file, search_list, check_only=False):
"""
Instantiate the template from the given file, producing the indicated
output file. The search list provides a collection of definition
mappings for substitutions.
When only checking template instantiation, the output file is not
produced, but instead an indication of whether the output file exists
and matches is returned.
"""
fp = open(template_file, 'r')
template_definition = fp.read()
fp.close()
t = Template(template_definition, searchList=search_list)
data = str(t)
if check_only:
return file_contains_data(output_file, data, exact=True)
else:
fp = open(output_file, 'w')
fp.write(data)
fp.close()
return False
# Defaults-related functions.
def get_system_timezone():
if not exists("/etc/timezone"):
return None
f = open("/etc/timezone")
try:
return f.read().strip()
finally:
f.close()
def set_service_default(name, setting_name, setting_value):
if isfile(join('/etc/default', name)):
myaugeas = Augeas()
setting = join('/files/etc/default/%s' % name, setting_name)
if myaugeas.get(setting) != setting_value:
myaugeas.set(setting, setting_value)
myaugeas.save()
myaugeas.close()
# Database-related functions.
def grep_for_expression(preceding, expr):
p = Popen(['/bin/grep', expr], stdin=preceding.stdout, stdout=PIPE, stderr=PIPE)
return p.wait() == 0
def get_mysql_defaults():
if is_debian():
return '/etc/mysql/debian.cnf'
else:
return '/tmp/kolab-setup-my.cnf'
def need_mysql_root():
return not is_debian()
+def have_mysql():
+ return have_command(['mysql', '--version'])
+
def have_mysql_database(config, name):
p = Popen(['mysql'] + (config and ['--defaults-file=%s' % config] or []) + [
'-e', 'show databases',
'--batch', '--skip-column-names'],
stdout=PIPE,
stderr=PIPE
)
return grep_for_expression(p, name)
def have_mysql_user(config, name):
return have_mysql_data(config, "mysql",
"select User from user where User = '%s' and Host = 'localhost'" % name,
name)
def have_mysql_data(config, database, query, expected):
p = Popen(['mysql'] + (config and ['--defaults-file=%s' % config] or []) + [
'-D', database,
'-e', query,
'--batch', '--skip-column-names'],
stdout=PIPE,
stderr=PIPE
)
return grep_for_expression(p, expected)
def have_mysql_process():
pidfile = '/var/run/mysqld/mysqld.pid'
return have_process_with_file(pidfile)
def get_mysql_defaults():
for path in '/etc/mysql/debian.cnf', '/etc/mysql/my.cnf', '/etc/my.cnf':
if exists(path):
return path
return None
def make_mysql_defaults_file(defaults_file):
utils.multiline_message(
_("Please supply the MySQL root password")
)
mysql_root_password = utils.ask_question(
_("MySQL root password"),
password=True
)
data = """
[mysql]
user=root
password='%s'
""" % mysql_root_password
fp = open(defaults_file, 'w')
try:
os.chmod(defaults_file, 0600)
fp.write(data)
finally:
fp.close()
# LDAP-related functions.
def have_slapd_process(instance):
pidfile = '/var/run/dirsrv/slapd-%s.pid' % instance
return have_process_with_file(pidfile)
# Process-related functions.
def have_process_with_file(pidfile):
if not exists(pidfile):
return False
f = open(pidfile)
try:
pid = f.read().strip()
return have_process(pid)
finally:
f.close()
def have_process(pid):
return call(['/bin/ps', '-p', pid]) == 0
diff --git a/pykolab/setup/setup_freebusy.py b/pykolab/setup/setup_freebusy.py
index 57ca71d..02b1f1b 100644
--- a/pykolab/setup/setup_freebusy.py
+++ b/pykolab/setup/setup_freebusy.py
@@ -1,105 +1,107 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from ConfigParser import RawConfigParser
from os.path import join, isfile
from shutil import copy
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(
'freebusy',
execute,
description=description(),
after=['ldap']
)
def description():
return _("Setup Free/Busy.")
def execute(*args, **kw):
prefix = "/etc/kolab-freebusy"
config = join(prefix, "config.ini")
sample = join(prefix, "config.ini.sample")
if not isfile(config) and not isfile(sample):
if conf.check_only:
utils.setup_status("freebusy", _("not installed"))
- log.error(_("Free/Busy is not installed on this system"))
- return
+ return
+ else:
+ print >> sys.stderr, _("Free/Busy is not installed on this system")
+ sys.exit(1)
if not isfile(config):
if conf.check_only:
utils.setup_status("freebusy", _("needs setup"))
return
copy(sample, config)
freebusy_settings = [
('directory "kolab-ldap"', {
'host': conf.get('ldap', 'ldap_uri'),
'base_dn': conf.get('ldap', 'base_dn'),
'bind_dn': conf.get('ldap', 'service_bind_dn'),
'bind_pw': conf.get('ldap', 'service_bind_pw'),
'fbsource': 'file:/var/lib/kolab-freebusy/%mail.ifb',
}),
('httpauth', {})
]
cfg_parser = RawConfigParser()
cfg_parser.read(config)
will_update = False
for section, definitions in freebusy_settings:
if not definitions:
cfg_parser.remove_section(section)
continue
for key, value in definitions.items():
current = cfg_parser.get(section, key)
# ConfigParser does not handle quoting for certain consumers of
# .ini files like PHP's parse_ini_file.
# See also: https://bugs.php.net/bug.php?id=36045
value = "=" in value and ('"%s"' % value) or value
if current != value:
cfg_parser.set(section, key, value)
will_update = True
if conf.check_only:
utils.setup_status("freebusy", will_update and _("needs setup") or _("setup done"))
return
if will_update:
fp = open(config, "w+")
cfg_parser.write(fp)
fp.close()
diff --git a/pykolab/setup/setup_mta.py b/pykolab/setup/setup_mta.py
index 8fb44bb..0db741b 100644
--- a/pykolab/setup/setup_mta.py
+++ b/pykolab/setup/setup_mta.py
@@ -1,452 +1,462 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from augeas import Augeas
from subprocess import call
from os.path import isdir, isfile, join, exists
from os import mkdir
import shutil
import components
import pykolab
from pykolab import utils
from pykolab.constants import *
from pykolab.setup.services import *
from pykolab.translate import _
log = pykolab.getLogger('pykolab.setup')
conf = pykolab.getConf()
def __init__():
components.register('mta', execute, description=description(), after=['ldap'])
def cli_options():
postfix_group = conf.add_cli_parser_option_group(_("Postfix Options"))
postfix_group.add_option(
"--reset-postfix-config",
dest = "reset_postfix_config",
action = "store_true",
default = False,
help = _("Reset the Postfix configuration.")
)
def description():
return _("Setup MTA.")
def execute(*args, **kw):
+ # Stop if Postfix is not actually installed.
+
+ if not have_postfix():
+ if conf.check_only:
+ utils.setup_status("mta", _("not installed"))
+ return
+ else:
+ print >> sys.stderr, _("Postfix not installed.")
+ sys.exit(1)
+
# Track the status of the configuration if only checking.
matching_config = True
# Configure Postfix.
prefix = "/etc/postfix/ldap"
group_filter = conf.get('ldap','kolab_group_filter')
if group_filter is None:
group_filter = conf.get('ldap','group_filter')
user_filter = conf.get('ldap','kolab_user_filter')
if user_filter is None:
user_filter = conf.get('ldap','user_filter')
server_host = utils.parse_ldap_uri(conf.get('ldap', 'ldap_uri'))[1]
definitions = {
"base_dn": conf.get('ldap', 'base_dn'),
"domain_base_dn": conf.get('ldap', 'domain_base_dn'),
"domain_filter": conf.get('ldap', 'domain_filter').replace('*', '%s'),
"domain_name_attribute": conf.get('ldap', 'domain_name_attribute'),
"group_base_dn": conf.get('ldap', 'group_base_dn'),
"server_host": server_host,
"service_bind_dn": conf.get('ldap', 'service_bind_dn'),
"service_bind_pw": conf.get('ldap', 'service_bind_pw'),
"kolab_user_filter": user_filter,
"kolab_group_filter": group_filter,
"resource_filter": conf.get('ldap', 'resource_filter'),
"sharedfolder_filter": conf.get('ldap', 'sharedfolder_filter'),
}
files = [
("local_recipient_maps.cf", local_recipient_maps),
("mydestination.cf", mydestination),
("mailenabled_distgroups.cf", mailenabled_distgroups),
("mailenabled_dynamic_distgroups.cf", mailenabled_dynamic_distgroups),
("transport_maps.cf", transport_maps),
("virtual_alias_maps.cf", virtual_alias_maps),
("virtual_alias_maps_sharedfolders.cf", virtual_alias_maps_sharedfolders),
]
if not isdir(prefix):
mkdir(prefix, 0770)
for filename, data in files:
pathname = join(prefix, filename)
data = data % definitions
if conf.check_only:
matching_config = matching_config and file_contains_data(pathname, data, exact=True)
else:
fp = open(pathname, 'w')
fp.write(data)
fp.close()
# Check to see if the transport file was already written.
transport_file = join(prefix, "transport")
transport_file_content = postfix_transport % {'domain': conf.get('kolab', 'primary_domain')}
matching_transport = file_contains_data(transport_file, transport_file_content)
matching_config = matching_config and matching_transport
if matching_transport and not conf.reset_postfix_config and not conf.check_only:
utils.message(_("Postfix transport file contains Kolab definition. Not updating the transport mapping."))
elif not conf.check_only:
fp = open(transport_file, 'a')
fp.write(transport_file_content)
fp.close()
call(["postmap", transport_file])
# Initialise certificate.
missing_certificate = isfile('/etc/pki/tls/certs/make-dummy-cert') and not isfile('/etc/pki/tls/private/localhost.pem')
matching_config = matching_config and not missing_certificate
if missing_certificate and not conf.check_only:
call(['/etc/pki/tls/certs/make-dummy-cert', '/etc/pki/tls/private/localhost.pem'])
# Acquire the settings from a global defined below, duplicating the global
# to be safe.
postfix_main_settings = {}
postfix_main_settings.update(_postfix_main_settings)
if isfile('/etc/pki/tls/private/localhost.pem'):
postfix_main_settings['smtpd_tls_cert_file'] = "/etc/pki/tls/private/localhost.pem"
postfix_main_settings['smtpd_tls_key_file'] = "/etc/pki/tls/private/localhost.pem"
# Establish the main Postfix configuration if necessary.
missing_main_config = not isfile('/etc/postfix/main.cf') and isfile('/usr/share/postfix/main.cf.debian')
matching_config = matching_config and not missing_main_config
if missing_main_config and not conf.check_only:
shutil.copy(
'/usr/share/postfix/main.cf.debian',
'/etc/postfix/main.cf'
)
# Copy header checks files.
for hc_file in ['inbound', 'internal', 'submission']:
if not isfile("/etc/postfix/header_checks.%s" % hc_file):
input_file = get_template_path('header_checks.%s' % hc_file)
missing_header_check_file = input_file is not None
matching_config = matching_config and not missing_header_check_file
if missing_header_check_file and not conf.check_only:
shutil.copy(input_file, "/etc/postfix/header_checks.%s" % hc_file)
call(["postmap", "/etc/postfix/header_checks.%s" % hc_file])
# Update the main Postfix configuration.
myaugeas = Augeas()
setting_base = '/files/etc/postfix/main.cf/'
for setting_key, proposed_value in postfix_main_settings.items():
setting = join(setting_base,setting_key)
current_value = myaugeas.get(setting)
# When only checking the configuration, exit the loop upon seeing any
# difference.
if conf.check_only:
matching_config = matching_config and current_value == proposed_value
if not matching_config:
break
else:
continue
# Handle absent regions of the configuration.
if current_value is None:
try:
myaugeas.set(setting, proposed_value)
except:
insert_paths = myaugeas.match('/files/etc/postfix/main.cf/*')
myaugeas.insert(insert_paths[-1], setting_key, False)
log.debug(_("Setting key %r to %r") % (setting_key, proposed_value), level=8)
myaugeas.set(setting, proposed_value)
if not conf.check_only:
myaugeas.save()
# Update the master Postfix configuration.
postfix_master_settings = {}
if exists('/usr/lib/postfix/kolab_smtp_access_policy'):
postfix_master_settings['kolab_sap_executable_path'] = '/usr/lib/postfix/kolab_smtp_access_policy'
else:
postfix_master_settings['kolab_sap_executable_path'] = '/usr/libexec/postfix/kolab_smtp_access_policy'
template_file = get_template_path('master.cf.tpl')
output_file = '/etc/postfix/master.cf'
if template_file is not None:
matching_config = matching_config and instantiate_template(template_file, output_file, [postfix_master_settings], check_only=conf.check_only)
else:
log.error(_("Could not write out Postfix configuration file %s") % output_file)
return
# Configure Amavis.
amavisd_settings = {
'ldap_server': server_host,
'ldap_bind_dn': conf.get('ldap', 'service_bind_dn'),
'ldap_bind_pw': conf.get('ldap', 'service_bind_pw'),
'primary_domain': conf.get('kolab', 'primary_domain'),
'ldap_filter': "(|(mail=%m)(alias=%m))",
'ldap_base_dn': conf.get('ldap', 'base_dn'),
}
template_file = None
# On RPM installations, Amavis configuration is contained within a single file.
if isfile("/etc/amavisd/amavisd.conf"):
template_file = get_template_path('amavisd.conf.tpl')
output_file = '/etc/amavisd/amavisd.conf'
if template_file is not None:
matching_config = matching_config and instantiate_template(template_file, output_file, [amavisd_settings], check_only=conf.check_only)
else:
log.error(_("Could not write out Amavis configuration file %s") % output_file)
return
# On APT installations, /etc/amavis/conf.d/ is a directory with many more files.
#
# Somebody could work on enhancement request #1080 to configure LDAP lookups,
# while really it isn't required.
else:
log.info(_("Not writing out any configuration for Amavis."))
# When only checking, give the status and return.
if conf.check_only:
utils.setup_status("mta", matching_config and _("setup done") or _("needs setup"))
return
# On debian wheezy amavisd-new expects '/etc/mailname' - possibly remediable through
# the #1080 enhancement mentioned above, but here's a quick fix.
f = open('/etc/mailname','w')
f.writelines(conf.get('kolab', 'primary_domain'))
f.close()
if isdir('/etc/postfix/sasl/'):
fp = open('/etc/postfix/sasl/smtpd.conf', 'w')
fp.write("pwcheck_method: saslauthd\n")
fp.write("mech_list: plain login\n")
fp.close()
# Configure and manipulate the services.
set_service_default('spamassassin', 'ENABLED', '1')
set_service_default('wallace', 'START', 'yes')
amavis = is_debian() and 'amavis' or 'amavisd'
clamav = is_debian() and 'clamav-daemon' or 'clamd.amavisd'
if not (
control_service('postfix', 'restart') and
control_service(amavis, 'restart') and
control_service(clamav, 'restart') and
control_service('wallace', 'restart')
):
log.error(_("Could not start the postfix, clamav and amavis services."))
if not (
configure_service('postfix', True) and
configure_service(amavis, True) and
configure_service(clamav, True) and
configure_service('wallace', True)
):
log.error(_("Could not configure to start on boot, the " + \
"postfix, clamav and amavis services."))
# Data used by the above code.
_postfix_main_settings = {
"inet_interfaces": "all",
"recipient_delimiter": "+",
"local_recipient_maps": "ldap:/etc/postfix/ldap/local_recipient_maps.cf",
"mydestination": "ldap:/etc/postfix/ldap/mydestination.cf",
"transport_maps": "ldap:/etc/postfix/ldap/transport_maps.cf, hash:/etc/postfix/transport",
"virtual_alias_maps": "$alias_maps, ldap:/etc/postfix/ldap/virtual_alias_maps.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_sharedfolders.cf, ldap:/etc/postfix/ldap/mailenabled_distgroups.cf, ldap:/etc/postfix/ldap/mailenabled_dynamic_distgroups.cf",
"smtpd_tls_auth_only": "yes",
"smtpd_tls_security_level": "may",
"smtp_tls_security_level": "may",
"smtpd_sasl_auth_enable": "yes",
"smtpd_sender_login_maps": "$relay_recipient_maps",
"smtpd_sender_restrictions": "permit_mynetworks, reject_sender_login_mismatch",
"smtpd_recipient_restrictions": "permit_mynetworks, reject_unauth_pipelining, reject_rbl_client zen.spamhaus.org, reject_non_fqdn_recipient, reject_invalid_helo_hostname, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service unix:private/recipient_policy_incoming, permit",
"smtpd_sender_restrictions": "permit_mynetworks, check_policy_service unix:private/sender_policy_incoming",
"submission_recipient_restrictions": "check_policy_service unix:private/submission_policy, permit_sasl_authenticated, reject",
"submission_sender_restrictions": "reject_non_fqdn_sender, check_policy_service unix:private/submission_policy, permit_sasl_authenticated, reject",
"submission_data_restrictions": "check_policy_service unix:private/submission_policy",
"content_filter": "smtp-amavis:[127.0.0.1]:10024"
}
postfix_transport = """
# Shared Folder Delivery for %(domain)s:
shared@%(domain)s\t\tlmtp:unix:/var/lib/imap/socket/lmtp
"""
local_recipient_maps = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
query_filter = (&(|(mail=%%s)(alias=%%s))(|%(kolab_user_filter)s%(kolab_group_filter)s%(resource_filter)s%(sharedfolder_filter)s))
result_attribute = mail
"""
mydestination = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(domain_base_dn)s
scope = sub
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
query_filter = %(domain_filter)s
result_attribute = %(domain_name_attribute)s
"""
mailenabled_distgroups = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(group_base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
# This finds the mail enabled distribution group LDAP entry
query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectclass=groupofuniquenames)(!(objectclass=groupofurls)))
# From this type of group, get all uniqueMember DNs
special_result_attribute = uniqueMember
# Only from those DNs, get the mail
result_attribute =
leaf_result_attribute = mail
"""
mailenabled_dynamic_distgroups = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(group_base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
# This finds the mail enabled dynamic distribution group LDAP entry
query_filter = (&(|(mail=%%s)(alias=%%s))(objectClass=kolabgroupofuniquenames)(objectClass=groupOfURLs))
# From this type of group, get all memberURL searches/references
special_result_attribute = memberURL
# Only from those DNs, get the mail
result_attribute =
leaf_result_attribute = mail
"""
transport_maps = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
query_filter = (&(|(mailAlternateAddress=%%s)(alias=%%s)(mail=%%s))(objectclass=kolabinetorgperson))
result_attribute = mail
result_format = lmtp:unix:/var/lib/imap/socket/lmtp
"""
virtual_alias_maps = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabinetorgperson))
result_attribute = mail
"""
virtual_alias_maps_sharedfolders = """\
server_host = %(server_host)s
server_port = 389
version = 3
search_base = %(base_dn)s
scope = sub
domain = ldap:/etc/postfix/ldap/mydestination.cf
bind_dn = %(service_bind_dn)s
bind_pw = %(service_bind_pw)s
query_filter = (&(|(mail=%%s)(alias=%%s))(objectclass=kolabsharedfolder))
result_attribute = kolabtargetfolder
result_format = shared+%%s
"""
diff --git a/pykolab/setup/setup_mysql.py b/pykolab/setup/setup_mysql.py
index c68ced8..d57ef86 100644
--- a/pykolab/setup/setup_mysql.py
+++ b/pykolab/setup/setup_mysql.py
@@ -1,225 +1,235 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from glob import glob
from os.path import exists, join
from subprocess import call, Popen, PIPE
import os
import components
import pykolab
from pykolab import utils
from pykolab.constants import *
from pykolab.setup.services 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(
"--reset-mysql-config",
dest = "reset_mysql_config",
action = "store_true",
default = False,
help = _("Reset the MySQL configuration.")
)
def description():
return _("Setup MySQL.")
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-mysql")
try:
_execute(*args, **kw)
finally:
stop_interaction()
def _execute(*args, **kw):
mysql = is_debian() and 'mysql' or 'mysqld'
+ # Stop if MySQL is not actually installed.
+
+ if not have_mysql():
+ if conf.check_only:
+ utils.setup_status("mysql", _("not installed"))
+ return
+ else:
+ print >> sys.stderr, _("MySQL not installed.")
+ sys.exit(1)
+
# Test MySQL daemon process availability.
if not conf.check_only and not exists('/var/run/mysqld/mysqld.sock'):
if not control_service(mysql, 'start'):
log.error(_("Could not start the MySQL database service."))
# Ensure that the service is started at boot.
if not conf.check_only and not configure_service(mysql, True):
log.error(_("Could not configure to start on boot, the " + \
"MySQL database service."))
# Defaults files exist in the distributions for various tasks, but such a
# file is used to avoid providing a password on the command line.
defaults_file = get_mysql_defaults()
if need_mysql_root():
# If a root user can be found without any credentials being supplied, the
# MySQL installation probably needs a root password setting.
if have_mysql_user(None, 'root'):
if conf.check_only:
utils.setup_status("mysql", _("needs setup"))
return
mysql_root_password = ask_question("kolab-conf/mysql-root-new",
_(create_root_password_message),
_("MySQL root password"),
default=utils.generate_password(),
password=True,
confirm=True
)
p1 = Popen(['echo', "update mysql.user set Password=PASSWORD('%s') where User='root'" %
mysql_root_password.replace("'", "''")], stdout=PIPE)
p2 = Popen(['mysql'], stdin=p1.stdout)
p2.communicate()
call(['mysql', '-e', 'flush privileges'])
# For an installation where the root password is still needed, it is
# obtained here.
else:
mysql_root_password = ask_question("kolab-conf/mysql-root-existing",
_(existing_root_password_message),
_("MySQL root password"),
password=True
)
# Write the defaults file in order to be able to connect in future.
data = mysql_defaults % mysql_root_password
fp = open(defaults_file, 'w')
os.chmod(defaults_file, 0600)
fp.write(data)
fp.close()
# Find the schema file for the database.
schema_file = find_schema_file()
if schema_file is not None:
# Test for the database.
if not have_mysql_database(defaults_file, 'kolab'):
if conf.check_only:
utils.setup_status("mysql", _("needs setup"))
return
call(['mysql', '--defaults-file=%s' % defaults_file, '-e', 'create database kolab'])
# Populate the schema.
p1 = Popen(['cat', schema_file], stdout=PIPE)
p2 = Popen(['mysql', '--defaults-file=%s' % defaults_file, 'kolab'], stdin=p1.stdout)
p2.communicate()
elif not conf.check_only:
log.info(_("A database called %s already exists. Not creating another one.") % 'kolab')
else:
log.error(_("Could not find the MySQL Kolab schema file"))
# Test for a MySQL user. Reset the user and configuration if explicitly
# requested or if the configuration has not been modified.
wap_url_needs_setting = conf.get('kolab_wap', 'sql_uri') == 'mysql://user:pass@localhost/database'
if have_mysql_user(defaults_file, 'kolab') and not conf.reset_mysql_config and not wap_url_needs_setting:
if not conf.check_only:
print >> sys.stderr, _("Kolab database account already exists and will not be configured.")
log.info(_("A user called %s already exists. Not creating another one.") % 'kolab')
else:
if conf.check_only:
utils.setup_status("mysql", _("needs setup"))
return
mysql_kolab_password = ask_question("kolab-conf/mysql-kolab",
_("""
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"),
default=utils.generate_password(),
password=True,
confirm=True
)
if not have_mysql_user(defaults_file, 'kolab'):
p1 = Popen(['echo', "grant all privileges on kolab.* to 'kolab'@'localhost' identified by '%s'" %
mysql_kolab_password.replace("'", "''")], stdout=PIPE)
p2 = Popen(['mysql', '--defaults-file=%s' % defaults_file], stdin=p1.stdout)
p2.communicate()
if wap_url_needs_setting or conf.reset_mysql_config:
conf.command_set('kolab_wap', 'sql_uri', 'mysql://kolab:%s@localhost/kolab' % mysql_kolab_password)
# If nothing needed updating, assume that the setup was done.
if conf.check_only:
utils.setup_status("mysql", _("setup done"))
def find_schema_file():
for webadmin_dir in glob('/usr/share/doc/kolab*'):
for root, directories, filenames in os.walk(webadmin_dir):
for filename in filenames:
if filename.startswith('kolab_wap') and filename.endswith('.sql'):
return join(root,filename)
return None
# Data used by the code above.
existing_root_password_message = """\
Please supply the root password for MySQL, so we can set
up user accounts for other components that use MySQL.
"""
create_root_password_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_defaults = """\
[mysql]
user=root
password='%s'
"""
diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py
index d2e4726..3dec6bb 100644
--- a/pykolab/setup/setup_roundcube.py
+++ b/pykolab/setup/setup_roundcube.py
@@ -1,241 +1,251 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from glob import glob
from os.path import isdir, join
from subprocess import call, Popen, PIPE
import hashlib
import os
import random
import re
import sys
import time
import components
import pykolab
from pykolab import utils
from pykolab.constants import *
from pykolab.setup.services import *
from pykolab.translate import _
log = pykolab.getLogger('pykolab.setup')
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 random_digest():
return hashlib.md5("%s" % random.random()).digest().encode("base64")
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-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)
+
# Use the existing root credentials.
defaults_file = get_mysql_defaults()
if not isfile(defaults_file):
if conf.check_only:
utils.setup_status("roundcube", _("needs setup"))
return
make_mysql_defaults_file(defaults_file)
# Test for a MySQL database.
if not have_mysql_database(defaults_file, 'roundcube'):
if conf.check_only:
utils.setup_status("roundcube", _("needs setup"))
return
# Gather schema files for database initialisation.
if isdir('/usr/share/roundcubemail'):
rcpath = '/usr/share/roundcubemail/'
elif isdir('/usr/share/roundcube'):
rcpath = '/usr/share/roundcube/'
else:
log.error(_("Roundcube installation path not found."))
return
schema_files = set()
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.add(join(root,filename))
break
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.add(join(root,filename))
# Create the database.
call(['mysql', '--defaults-file=%s' % defaults_file, '-e', 'create database roundcube'])
# 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:
if conf.check_only:
utils.setup_status("roundcube", _("needs setup"))
return
mysql_roundcube_password = ask_question("kolab-conf/roundcube-password",
_(roundcube_password_message),
_("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_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_auth.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)
if isdir('/etc/roundcubemail'):
output_file = join('/etc/roundcubemail', want_file)
elif isdir('/etc/roundcube'):
output_file = join('/etc/roundcube', 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])
# 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."))
# If nothing needed updating, assume that the setup was done.
if conf.check_only:
utils.setup_status("roundcube", _("setup done"))
# Data used by the above code.
roundcube_password_message = """\
Please supply a password for the MySQL user 'roundcube'.
This password will be used by the Roundcube webmail
interface.
"""
diff --git a/pykolab/setup/setup_syncroton.py b/pykolab/setup/setup_syncroton.py
index cb537f7..6dfc88d 100644
--- a/pykolab/setup/setup_syncroton.py
+++ b/pykolab/setup/setup_syncroton.py
@@ -1,104 +1,114 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2013 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, 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 <http://www.gnu.org/licenses/>.
#
from glob import glob
from os.path import join
from subprocess import Popen, PIPE
import os
import time
import sys
import components
import pykolab
from pykolab import utils
from pykolab.constants import *
from pykolab.setup.services import *
from pykolab.translate import _
log = pykolab.getLogger('pykolab.setup')
conf = pykolab.getConf()
def __init__():
components.register('syncroton', execute, description=description(), after=['mysql','ldap','roundcube'])
def description():
return _("Setup Syncroton.")
def execute(*args, **kw):
schema_files = set()
for syncroton_dir in glob('/usr/share/doc/kolab-syncroton*'):
for root, directories, filenames in os.walk(syncroton_dir):
for filename in filenames:
if filename.startswith('mysql.initial') and filename.endswith('.sql'):
schema_files.add(join(root,filename))
break
+ # Stop if MySQL is not actually installed.
+
+ if not have_mysql():
+ if conf.check_only:
+ utils.setup_status("syncroton", _("needs setup"))
+ return
+ else:
+ print >> sys.stderr, _("MySQL not installed: cannot initialise Syncroton.")
+ sys.exit(1)
+
defaults_file = get_mysql_defaults()
if not isfile(defaults_file):
if conf.check_only:
utils.setup_status("syncroton", _("needs setup"))
return
make_mysql_defaults_file(defaults_file)
# Test for the Roundcube database.
if not have_mysql_database(defaults_file, 'roundcube'):
if conf.check_only:
utils.setup_status("syncroton", _("needs setup"))
return
else:
print >> sys.stderr, _("Roundcube database not created: cannot initialise Syncroton.")
sys.exit(1)
# Test for the presence of the plugin in Roundcube.
if have_mysql_data(defaults_file, "roundcube",
"select * from system where name = 'syncroton-version'",
"syncroton-version"):
if conf.check_only:
utils.setup_status("syncroton", _("setup done"))
return
else:
print >> sys.stderr, _("Kolab database account already exists and will not be configured.")
log.info(_("A user called %s already exists. Not creating another one.") % 'kolab')
else:
if conf.check_only:
utils.setup_status("syncroton", _("needs setup"))
return
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()
time.sleep(2)
apache = is_debian() and 'apache2' or 'httpd'
if not control_service(apache, 'restart'):
log.error(_("Could not start the webserver server service."))
if not configure_service(apache, True):
log.error(_("Could not configure to start on boot, the " + \
"webserver server service."))

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 4, 6:20 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822828
Default Alt Text
(54 KB)

Event Timeline