~launchpad-pqm/launchpad/devel

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# pylint: disable-msg=E0211,E0213

"""Interfaces specific to mail handling."""

__metaclass__ = type
__all__ = ['IWeaklyAuthenticatedPrincipal',
           'ISignedMessage',
           'IMailHandler',
           'EmailProcessingError',
           'BugTargetNotFound',
           'IEmailCommand',
           'IBugEmailCommand',
           'IBugTaskEmailCommand',
           'IBugEditEmailCommand',
           'IBugTaskEditEmailCommand']

from zope.interface import (
    Attribute,
    Interface,
    )
from zope.schema import (
    ASCII,
    Bool,
    )

from canonical.launchpad import _


class IWeaklyAuthenticatedPrincipal(Interface):
    """The principal has been weakly authenticated.

    At the moment it means that the user was authenticated simply by
    looking at the From address in an email.
    """


class ISignedMessage(Interface):
    """A message that's possibly signed with an OpenPGP key.

    If the message wasn't signed, all attributes will be None.
    """

    def __getitem__(name):
        """Returns the message header with the given name."""

    signedMessage = Attribute("The part that was signed, represented "
                              "as an email.Message.")

    signedContent = ASCII(title=_("Signed Content"),
                          description=_("The text that was signed."))

    signature = ASCII(title=_("Signature"),
                      description=_("The OpenPGP signature used to sign "
                                    "the message."))

    parsed_string = Attribute(
        "The string that was parsed to create the SignedMessage.")


class IMailHandler(Interface):
    """Handles incoming mail sent to a specific email domain.

    For example, in email address '1@bugs.launchpad.ubuntu.com',
    'bugs.launchpad.ubuntu.com' is the email domain.

    The handler should be registered as a named utility, with the domain
    it handles as the name.
    """

    allow_unknown_users = Bool(
        title=u"Allow unknown users",
        description=u"The handler can handle emails from persons not"
                    " registered in Launchpad (which will result in an"
                    " anonymous interaction being set up.")

    def process(signed_msg, to_address, filealias, log=None):
        """Processes a ISignedMessage

        The 'to_address' is the address the mail was sent to.
        The 'filealias' is an ILibraryFileAlias.
        The 'log' is the logger to be used.

        Return False if to_address does not exist/is bad.
        Return True if the mesage was processed, successfully or
        unsuccessfully.  This includes user or input errors.
        Programming errors should cause exceptions to be raised.
        """


class EmailProcessingError(Exception):
    """Something went wrong while processing an email command."""

    def __init__(self, args, stop_processing=False):
        """Initialize

        :args: The standard exception extra arguments.
        "stop_processing: Should the processing of the email be stopped?
        """
        Exception.__init__(self, args)
        self.stop_processing = stop_processing


class BugTargetNotFound(Exception):
    """A bug target couldn't be found."""


class IEmailCommand(Interface):
    """An email command.

    Email commands can be embedded in mails sent to Launchpad. For
    example in comments to bugs sent via email, you can include:

      private yes

    in order to make the bug private.
    """

    def execute(context):
        """Execute the command in a context."""

    def setAttributeValue(context, attr_name, attr_value):
        """Set the value of the attribute.

        Subclasses may want to override this if, for example, the
        attribute is set through a special method instead of a normal
        attribute.
        """

    def __str__():
        """Return a textual representation of the command and its arguments.
        """


class IBugEmailCommand(IEmailCommand):
    """An email command specific to getting or creating a bug."""

    RANK = Attribute(
        "The int used to determine the order of execution of many commands.")

    def execute(parsed_msg, filealias):
        """Either create or get an exiting bug.

        If a bug is created, parsed_msg and filealias will be used to
        create the initial comment of the bug.

        The bug and an event is returned as a two-tuple.
        """


class IBugTaskEmailCommand(IEmailCommand):
    """An email command specific to getting or creating a bug task."""

    RANK = Attribute(
        "The int used to determine the order of execution of many commands.")

    def execute(bug):
        """Either create or get an exiting bug task.

        The bug task and an event is returned as a two-tuple.
        """


class IBugEditEmailCommand(IEmailCommand):
    """An email command specific to editing a bug."""

    RANK = Attribute(
        "The int used to determine the order of execution of many commands.")

    def execute(bug, current_event):
        """Execute the command in the context of the bug.

        The modified bug and an event is returned.
        """


class IBugTaskEditEmailCommand(IEmailCommand):
    """An email command specific to editing a bug task."""

    RANK = Attribute(
        "The int used to determine the order of execution of many commands.")

    def execute(bugtask, current_event):
        """Execute the command in the context of the bug task.

        The modified bug task and an event is returned.
        """