Esempio n. 1
0
def monkey_patch(mailman_path, config):
    """Monkey-patch an installed Mailman 2.1 tree.

    Rather than maintain a forked tree of Mailman 2.1, we apply a set of
    changes to an installed Mailman tree.  This tree can be found rooted at
    mailman_path.

    This should usually mean just copying a file from this directory into
    mailman_path.  Rather than build a lot of process into the mix, just hard
    code each transformation here.
    """
    # Hook Mailman to Launchpad by writing a custom mm_cfg.py file which adds
    # the top of our Launchpad tree to Mailman's sys.path.  The mm_cfg.py file
    # won't do much more than set up sys.path and do an from-import-* to get
    # everything that doesn't need to be dynamically calculated at run-time.
    # Things that can only be calculated at run-time are written to mm_cfg.py
    # now.  It's okay to simply overwrite any existing mm_cfg.py, since we'll
    # provide everything Mailman needs.
    #
    # Remember, don't rely on Launchpad's config object in the mm_cfg.py file
    # or in the lp.services.mailman.monkeypatches.defaults module because
    # Mailman will not be able to initialize Launchpad's configuration system.
    # Instead, anything that's needed from config should be written to the
    # mm_cfg.py file now.
    #
    # Calculate the parent directory of the lp module.  This directory
    # will get appended to Mailman's sys.path.
    import lp
    from lp.services.mailman.config import configure_siteowner
    launchpad_top = os.path.abspath(
        os.path.join(os.path.dirname(lp.__file__), os.pardir, os.pardir))
    # Read the email footer template for all Launchpad messages.
    from lp.services.mail.helpers import get_email_template
    footer = get_email_template(
        'mailinglist-footer.txt', app='services/mailman/monkeypatches')
    # Write the mm_cfg.py file, filling in the dynamic values now.
    host, port = as_host_port(config.mailman.smtp)
    owner_address, owner_password = configure_siteowner(
        config.mailman.build_site_list_owner)
    config_path_in = os.path.join(os.path.dirname(__file__), 'mm_cfg.py.in')
    config_file_in = open(config_path_in)
    try:
        config_template = config_file_in.read()
    finally:
        config_file_in.close()
    config_path_out = os.path.join(mailman_path, 'Mailman', 'mm_cfg.py')
    config_file_out = open(config_path_out, 'w')
    try:
        print >> config_file_out, config_template % dict(
            launchpad_top=launchpad_top,
            smtp_host=host,
            smtp_port=port,
            smtp_max_rcpts=config.mailman.smtp_max_rcpts,
            smtp_max_sesions_per_connection=(
                config.mailman.smtp_max_sesions_per_connection),
            xmlrpc_url=config.mailman.xmlrpc_url,
            xmlrpc_sleeptime=config.mailman.xmlrpc_runner_sleep,
            xmlrpc_timeout=config.mailman.xmlrpc_timeout,
            xmlrpc_subscription_batch_size=(
                config.mailman.subscription_batch_size),
            site_list_owner=owner_address,
            list_help_header=config.mailman.list_help_header,
            list_subscription_headers=(
                config.mailman.list_subscription_headers),
            archive_url_template=config.mailman.archive_url_template,
            list_owner_header_template=(
                config.mailman.list_owner_header_template),
            footer=footer,
            var_dir=config.mailman.build_var_dir,
            shared_secret=config.mailman.shared_secret,
            soft_max_size=config.mailman.soft_max_size,
            hard_max_size=config.mailman.hard_max_size,
            register_bounces_every=config.mailman.register_bounces_every,
            )
    finally:
        config_file_out.close()
    # Mailman's qrunner system requires runner modules to live in the
    # Mailman.Queue package.  Set things up so that there's a hook module in
    # there for the XMLRPCRunner.
    runner_path = os.path.join(mailman_path,
                               'Mailman', 'Queue', 'XMLRPCRunner.py')
    runner_file = open(runner_path, 'w')
    try:
        print >> runner_file, (
            'from lp.services.mailman.monkeypatches.xmlrpcrunner '
            'import *')
    finally:
        runner_file.close()
    # Install our handler wrapper modules so that Mailman can find them.  Most
    # of the actual code of the handler comes from our monkey patches modules.
    for mm_name, lp_name in (('LaunchpadMember', 'lphandler'),
                             ('LaunchpadHeaders', 'lpheaders'),
                             ('LPStanding', 'lpstanding'),
                             ('LPModerate', 'lpmoderate'),
                             ('LPSize', 'lpsize'),
                             ):
        handler_path = os.path.join(
            mailman_path, 'Mailman', 'Handlers', mm_name + '.py')
        handler_file = open(handler_path, 'w')
        try:
            package = 'lp.services.mailman.monkeypatches'
            module = package + '.' + lp_name
            print >> handler_file, 'from', module, 'import *'
        finally:
            handler_file.close()

    here = os.path.dirname(__file__)
    # Install the MHonArc control file.
    mhonarc_rc_file = os.path.join(here, 'lp-mhonarc-common.mrc')
    runtime_data_dir = os.path.join(config.mailman.build_var_dir, 'data')
    shutil.copy(mhonarc_rc_file, runtime_data_dir)
    # Install the launchpad site templates.
    launchpad_template_path = os.path.join(here, 'sitetemplates')
    site_template_path = os.path.join(mailman_path, 'templates', 'site')
    if os.path.isdir(site_template_path):
        shutil.rmtree(site_template_path)
    shutil.copytree(launchpad_template_path, site_template_path)
Esempio n. 2
0
def build_mailman():
    # Build and install Mailman if it is enabled and not yet built.
    if not config.mailman.build:
        # There's nothing to do.
        return 0
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')
    var_dir = os.path.abspath(config.mailman.build_var_dir)
    executable = os.path.abspath('bin/py')

    # If we can import the package, we assume Mailman is properly built at
    # the least.  This does not catch re-installs that might be necessary
    # should our copy in sourcecode be updated.  Do that manually.
    try:
        import Mailman
    except ImportError:
        need_build = need_install = True
    else:
        need_build = need_install = False
        # Make sure that the configure prefix is correct, in case this tree
        # was moved after building Mailman.
        try:
            from Mailman import Defaults
        except ImportError:
            need_build = need_install = True
        else:
            if Defaults.PYTHON != executable:
                need_build = need_install = True
                # We'll need to remove this; "make install" won't overwrite
                # the existing file, and then installation will fail due to
                # it having the wrong sys.path.
                try:
                    os.unlink(
                        os.path.join(mailman_path, 'Mailman', 'mm_cfg.py'))
                except OSError as e:
                    if e.errno != errno.ENOENT:
                        raise
    if not need_install:
        # Also check for Launchpad-specific bits stuck into the source tree by
        # monkey_patch(), in case this is half-installed.  See
        # <https://bugs.launchpad.net/launchpad-registry/+bug/683486>.
        try:
            from Mailman.Queue import XMLRPCRunner
            from Mailman.Handlers import LPModerate
        except ImportError:
            # Monkey patches not present, redo install and patch steps.
            need_install = True

    # Make sure the target directories exist and have the correct
    # permissions, otherwise configure will complain.
    user, group = as_username_groupname(config.mailman.build_user_group)
    # Now work backwards to get the uid and gid
    try:
        uid = pwd.getpwnam(user).pw_uid
    except KeyError:
        print('No user found:', user, file=sys.stderr)
        sys.exit(1)
    try:
        gid = grp.getgrnam(group).gr_gid
    except KeyError:
        print('No group found:', group, file=sys.stderr)
        sys.exit(1)

    # Ensure that the var_dir exists, is owned by the user:group, and has
    # the necessary permissions.  Set the mode separately after the
    # makedirs() call because some platforms ignore mkdir()'s mode (though
    # I think Linux does not ignore it -- better safe than sorry).
    try:
        os.makedirs(var_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
    else:
        # Just created the var directory, will need to install mailmain bits.
        need_install = True
    os.chown(var_dir, uid, gid)
    os.chmod(var_dir, 0o2775)

    # Skip mailman setup if nothing so far has shown a reinstall needed.
    if not need_install:
        return 0

    mailman_source = os.path.join('sourcecode', 'mailman')
    if config.mailman.build_host_name:
        build_host_name = config.mailman.build_host_name
    else:
        build_host_name = socket.getfqdn()

    # Build and install the Mailman software.  Note that we don't care about
    # --with-cgi-gid because we're not going to use that Mailman subsystem.
    configure_args = (
        './configure',
        '--prefix',
        mailman_path,
        '--with-var-prefix=' + var_dir,
        '--with-python=' + executable,
        '--with-username='******'--with-groupname=' + group,
        '--with-mail-gid=' + group,
        '--with-mailhost=' + build_host_name,
        '--with-urlhost=' + build_host_name,
    )
    if need_build:
        # Configure.
        retcode = subprocess.call(configure_args, cwd=mailman_source)
        if retcode:
            print('Could not configure Mailman:', file=sys.stderr)
            sys.exit(retcode)
        # Make.
        retcode = subprocess.call(('make', ), cwd=mailman_source)
        if retcode:
            print('Could not make Mailman.', file=sys.stderr)
            sys.exit(retcode)
    retcode = subprocess.call(('make', 'install'), cwd=mailman_source)
    if retcode:
        print('Could not install Mailman.', file=sys.stderr)
        sys.exit(retcode)
    # Symlink Mailman's Python modules into the import path.
    try:
        os.unlink(os.path.join('lib', 'Mailman'))
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
    os.symlink(os.path.join('mailman', 'Mailman'),
               os.path.join('lib', 'Mailman'))
    # Try again to import the package.
    try:
        import Mailman
    except ImportError:
        print('Could not import the Mailman package', file=sys.stderr)
        return 1

    # Check to see if the site list exists.  The output can go to /dev/null
    # because we don't really care about it.  The site list exists if
    # config_list returns a zero exit status, otherwise it doesn't
    # (probably).  Before we can do this however, we must monkey patch
    # Mailman, otherwise mm_cfg.py won't be set up correctly.
    monkey_patch(mailman_path, config)

    import Mailman.mm_cfg
    retcode = subprocess.call(
        ('./config_list', '-o', '/dev/null', Mailman.mm_cfg.MAILMAN_SITE_LIST),
        cwd=mailman_bin,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)

    if retcode:
        addr, password = configure_siteowner(
            config.mailman.build_site_list_owner)

        # The site list does not yet exist, so create it now.
        retcode = subprocess.call(
            ('./newlist', '--quiet', '--emailhost=' + build_host_name,
             Mailman.mm_cfg.MAILMAN_SITE_LIST, addr, password),
            cwd=mailman_bin)
        if retcode:
            print('Could not create site list', file=sys.stderr)
            return retcode

    retcode = configure_site_list(mailman_bin,
                                  Mailman.mm_cfg.MAILMAN_SITE_LIST)
    if retcode:
        print('Could not configure site list', file=sys.stderr)
        return retcode

    # Create a directory to hold the gzip'd tarballs for the directories of
    # deactivated lists.
    try:
        os.mkdir(os.path.join(Mailman.mm_cfg.VAR_PREFIX, 'backups'))
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

    return 0
def build_mailman():
    # Build and install Mailman if it is enabled and not yet built.
    if not config.mailman.build:
        # There's nothing to do.
        return 0
    mailman_path = configure_prefix(config.mailman.build_prefix)
    mailman_bin = os.path.join(mailman_path, 'bin')
    var_dir = os.path.abspath(config.mailman.build_var_dir)

    # If we can import the package, we assume Mailman is properly built at
    # the least.  This does not catch re-installs that might be necessary
    # should our copy in sourcecode be updated.  Do that manually.
    sys.path.append(mailman_path)
    try:
        import Mailman
    except ImportError:
        # sys.path_importer_cache is a mapping of elements of sys.path to
        # importer objects used to handle them. In Python2.5+ when an element
        # of sys.path is found to not exist on disk, a NullImporter is created
        # and cached - this causes Python to never bother re-inspecting the
        # disk for that path element. We must clear that cache element so that
        # our second attempt to import MailMan after building it will actually
        # check the disk.
        del sys.path_importer_cache[mailman_path]
        need_build = need_install = True
    else:
        need_build = need_install = False
        # Also check for Launchpad-specific bits stuck into the source tree by
        # monkey_patch(), in case this is half-installed.  See
        # <https://bugs.launchpad.net/launchpad-registry/+bug/683486>.
        try:
            from Mailman.Queue import XMLRPCRunner
            from Mailman.Handlers import LPModerate
        except ImportError:
            # Monkey patches not present, redo install and patch steps.
            need_install = True

    # Make sure the target directories exist and have the correct
    # permissions, otherwise configure will complain.
    user, group = as_username_groupname(config.mailman.build_user_group)
    # Now work backwards to get the uid and gid
    try:
        uid = pwd.getpwnam(user).pw_uid
    except KeyError:
        print >> sys.stderr, 'No user found:', user
        sys.exit(1)
    try:
        gid = grp.getgrnam(group).gr_gid
    except KeyError:
        print >> sys.stderr, 'No group found:', group
        sys.exit(1)

    # Ensure that the var_dir exists, is owned by the user:group, and has
    # the necessary permissions.  Set the mode separately after the
    # makedirs() call because some platforms ignore mkdir()'s mode (though
    # I think Linux does not ignore it -- better safe than sorry).
    try:
        os.makedirs(var_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
    else:
        # Just created the var directory, will need to install mailmain bits.
        need_install = True
    os.chown(var_dir, uid, gid)
    os.chmod(var_dir, 02775)

    # Skip mailman setup if nothing so far has shown a reinstall needed.
    if not need_install:
        return 0

    mailman_source = os.path.join('sourcecode', 'mailman')
    if config.mailman.build_host_name:
        build_host_name = config.mailman.build_host_name
    else:
        build_host_name = socket.getfqdn()

    # Build and install the Mailman software.  Note that we don't care about
    # --with-cgi-gid because we're not going to use that Mailman subsystem.
    executable = os.path.abspath('bin/py')
    configure_args = (
        './configure',
        '--prefix', mailman_path,
        '--with-var-prefix=' + var_dir,
        '--with-python=' + executable,
        '--with-username='******'--with-groupname=' + group,
        '--with-mail-gid=' + group,
        '--with-mailhost=' + build_host_name,
        '--with-urlhost=' + build_host_name,
        )
    if need_build:
        # Configure.
        retcode = subprocess.call(configure_args, cwd=mailman_source)
        if retcode:
            print >> sys.stderr, 'Could not configure Mailman:'
            sys.exit(retcode)
        # Make.
        retcode = subprocess.call(('make', ), cwd=mailman_source)
        if retcode:
            print >> sys.stderr, 'Could not make Mailman.'
            sys.exit(retcode)
    retcode = subprocess.call(('make', 'install'), cwd=mailman_source)
    if retcode:
        print >> sys.stderr, 'Could not install Mailman.'
        sys.exit(retcode)
    # Try again to import the package.
    try:
        import Mailman
    except ImportError:
        print >> sys.stderr, 'Could not import the Mailman package'
        return 1

    # Check to see if the site list exists.  The output can go to /dev/null
    # because we don't really care about it.  The site list exists if
    # config_list returns a zero exit status, otherwise it doesn't
    # (probably).  Before we can do this however, we must monkey patch
    # Mailman, otherwise mm_cfg.py won't be set up correctly.
    monkey_patch(mailman_path, config)

    import Mailman.mm_cfg
    retcode = subprocess.call(
        ('./config_list', '-o', '/dev/null',
         Mailman.mm_cfg.MAILMAN_SITE_LIST),
        cwd=mailman_bin,
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    if retcode:
        addr, password = configure_siteowner(
            config.mailman.build_site_list_owner)

        # The site list does not yet exist, so create it now.
        retcode = subprocess.call(
            ('./newlist', '--quiet',
             '--emailhost=' + build_host_name,
             Mailman.mm_cfg.MAILMAN_SITE_LIST,
             addr, password),
            cwd=mailman_bin)
        if retcode:
            print >> sys.stderr, 'Could not create site list'
            return retcode

    retcode = configure_site_list(
        mailman_bin, Mailman.mm_cfg.MAILMAN_SITE_LIST)
    if retcode:
        print >> sys.stderr, 'Could not configure site list'
        return retcode

    # Create a directory to hold the gzip'd tarballs for the directories of
    # deactivated lists.
    try:
        os.mkdir(os.path.join(Mailman.mm_cfg.VAR_PREFIX, 'backups'))
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

    return 0