summaryrefslogtreecommitdiff
path: root/src/mailman/commands/cli_import.py
blob: b9183fb82f0555fb4a2295b37f8fe6c7055d5e5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# Copyright (C) 2010 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman 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.
#
# GNU Mailman 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
# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.

"""Importing list data into Mailman 3."""

from __future__ import absolute_import, unicode_literals

__metaclass__ = type
__all__ = [
    'Import21',
    ]


import sys
import cPickle
import datetime

from zope.component import getUtility
from zope.interface import implements

from mailman.config import config
from mailman.core.i18n import _
from mailman.interfaces.action import Action
from mailman.interfaces.autorespond import ResponseAction
from mailman.interfaces.command import ICLISubCommand
from mailman.interfaces.digests import DigestFrequency
from mailman.interfaces.listmanager import IListManager
from mailman.interfaces.mailinglist import Personalization, ReplyToMunging
from mailman.interfaces.nntp import NewsModeration



def seconds_to_delta(value):
    return datetime.timedelta(seconds=value)


TYPES = dict(
    autorespond_owner=ResponseAction,
    autorespond_postings=ResponseAction,
    autorespond_requests=ResponseAction,
    bounce_info_stale_after=seconds_to_delta,
    bounce_you_are_disabled_warnings_interval=seconds_to_delta,
    digest_volume_frequency=DigestFrequency,
    member_moderation_action=Action,
    news_moderation=NewsModeration,
    personalize=Personalization,
    reply_goes_to_list=ReplyToMunging,
    )



class Import21:
    """Import Mailman 2.1 list data."""

    implements(ICLISubCommand)

    name = 'import21'

    def add(self, parser, command_parser):
        """See `ICLISubCommand`."""
        self.parser = parser
        # Required positional arguments.
        command_parser.add_argument(
            'listname', metavar='LISTNAME', nargs=1,
            help=_("""\
            The 'fully qualified list name', i.e. the posting address of the
            mailing list to inject the message into."""))
        command_parser.add_argument(
            'pickle_file', metavar='FILENAME', nargs=1,
            help=_('The path to the config.pck file to import.'))

    def process(self, args):
        """See `ICLISubCommand`."""
        # Could be None or sequence of length 0.
        if args.listname is None:
            self.parser.error(_('List name is required'))
            return
        assert len(args.listname) == 1, (
            'Unexpected positional arguments: %s' % args.listname)
        fqdn_listname = args.listname[0]
        mlist = getUtility(IListManager).get(fqdn_listname)
        if mlist is None:
            self.parser.error(_('No such list: $fqdn_listname'))
            return
        if args.pickle_file is None:
            self.parser.error(_('config.pck file is required'))
            return
        assert len(args.pickle_file) == 1, (
            'Unexpected positional arguments: %s' % args.pickle_file)
        filename = args.pickle_file[0]
        with open(filename) as fp:
            while True:
                try:
                    config_dict = cPickle.load(fp)
                except EOFError:
                    break
                except cPickle.UnpicklingError:
                    self.parser.error(
                        _('Not a Mailman 2.1 configuration file: $filename'))
                    return
                else:
                    if not isinstance(config_dict, dict):
                        print >> sys.stderr, _(
                            'Ignoring non-dictionary: {0!r}').format(
                            config_dict)
                        continue
                    import_config(mlist, config_dict, args)
        # Commit the changes to the database.
        config.db.commit()



def import_config(mlist, config_dict, args):
    """Apply a configuration dictionary to a mailing list.

    :param mlist: The mailing list.
    :type mlist: IMailingList
    :param config_dict: The Mailman 2.1 configuration dictionary.
    :type config_dict: dict
    :param args: Command line arguments.
    """
    for key, value in config_dict.items():
        # Handle the simple case where the key is an attribute of the
        # IMailingList and the types are the same (modulo 8-bit/unicode
        # strings).
        if hasattr(mlist, key):
            if isinstance(value, str):
                value = unicode(value, 'ascii')
            # Some types require conversion.
            converter = TYPES.get(key)
            if converter is not None:
                value = converter(value)
            try:
                setattr(mlist, key, value)
            except TypeError as error:
                print >> sys.stderr, 'Type conversion error:', key
                raise