13668.1.21
by Curtis Hovey
Updated copyrights. |
1 |
# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
12043.4.4
by Gavin Panella
Typos. |
4 |
"""Handle incoming Bugs email."""
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
5 |
|
6 |
__metaclass__ = type |
|
7 |
__all__ = [ |
|
8 |
"MaloneHandler", |
|
12043.4.4
by Gavin Panella
Typos. |
9 |
]
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
10 |
|
13779.2.3
by Curtis Hovey
Merged bug command group helpers. |
11 |
from operator import attrgetter |
13668.1.16
by Curtis Hovey
Moved bug mail error templates to lp.bugs.mail. |
12 |
import os |
13 |
||
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
14 |
from lazr.lifecycle.event import ObjectCreatedEvent |
15 |
from lazr.lifecycle.interfaces import IObjectCreatedEvent |
|
13970.10.7
by William Grant
Abolish sqlbase's begin and abort. |
16 |
import transaction |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
17 |
from zope.component import getUtility |
18 |
from zope.event import notify |
|
19 |
from zope.interface import implements |
|
20 |
||
14027.3.7
by Jeroen Vermeulen
Conflicts. |
21 |
from lp.bugs.interfaces.bug import ( |
22 |
CreateBugParams, |
|
23 |
CreatedBugWithNoBugTasksError, |
|
24 |
)
|
|
13668.1.22
by Curtis Hovey
Sorted imports. |
25 |
from lp.bugs.interfaces.bugattachment import ( |
26 |
BugAttachmentType, |
|
27 |
IBugAttachmentSet, |
|
28 |
)
|
|
29 |
from lp.bugs.interfaces.bugmessage import IBugMessageSet |
|
13668.1.24
by Curtis Hovey
Extracted ProcessMailLayer from test_system_documentation. |
30 |
from lp.bugs.mail.commands import BugEmailCommands |
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
31 |
from lp.services.identity.interfaces.emailaddress import IEmailAddressSet |
13668.1.8
by Curtis Hovey
Moved canonical.launchpad.mail.helpers to lp.services.mail. |
32 |
from lp.services.mail.helpers import ( |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
33 |
ensure_not_weakly_authenticated, |
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
34 |
get_email_template, |
13668.1.24
by Curtis Hovey
Extracted ProcessMailLayer from test_system_documentation. |
35 |
get_error_message, |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
36 |
get_main_body, |
37 |
guess_bugtask, |
|
38 |
IncomingEmailError, |
|
39 |
parse_commands, |
|
40 |
reformat_wiki_text, |
|
41 |
)
|
|
13668.1.22
by Curtis Hovey
Sorted imports. |
42 |
from lp.services.mail.interfaces import ( |
43 |
EmailProcessingError, |
|
44 |
IBugEditEmailCommand, |
|
45 |
IBugEmailCommand, |
|
46 |
IBugTaskEditEmailCommand, |
|
47 |
IBugTaskEmailCommand, |
|
48 |
IMailHandler, |
|
49 |
)
|
|
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
50 |
from lp.services.mail.mailwrapper import MailWrapper |
51 |
from lp.services.mail.notification import send_process_error_notification |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
52 |
from lp.services.mail.sendmail import simple_sendmail |
13668.1.22
by Curtis Hovey
Sorted imports. |
53 |
from lp.services.messages.interfaces.message import IMessageSet |
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
54 |
from lp.services.webapp.interfaces import ILaunchBag |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
55 |
|
56 |
||
13668.1.16
by Curtis Hovey
Moved bug mail error templates to lp.bugs.mail. |
57 |
error_templates = os.path.join(os.path.dirname(__file__), 'errortemplates') |
58 |
||
59 |
||
13779.2.3
by Curtis Hovey
Merged bug command group helpers. |
60 |
class BugTaskCommandGroup: |
61 |
||
62 |
def __init__(self, command=None): |
|
63 |
self._commands = [] |
|
64 |
if command is not None: |
|
65 |
self._commands.append(command) |
|
66 |
||
67 |
def __nonzero__(self): |
|
68 |
return len(self._commands) > 0 |
|
69 |
||
70 |
def __str__(self): |
|
71 |
text_commands = [str(cmd) for cmd in self.commands] |
|
72 |
return '\n'.join(text_commands).strip() |
|
73 |
||
74 |
@property
|
|
75 |
def commands(self): |
|
76 |
"Return the `EmailCommand`s ordered by their rank."
|
|
77 |
return sorted(self._commands, key=attrgetter('RANK')) |
|
78 |
||
79 |
def add(self, command): |
|
80 |
"Add an `EmailCommand` to the commands."
|
|
81 |
self._commands.append(command) |
|
82 |
||
83 |
||
84 |
class BugCommandGroup(BugTaskCommandGroup): |
|
85 |
||
86 |
def __init__(self, command=None): |
|
87 |
super(BugCommandGroup, self).__init__(command=command) |
|
88 |
self._groups = [] |
|
89 |
||
90 |
def __nonzero__(self): |
|
91 |
if len(self._groups) > 0: |
|
92 |
return True |
|
93 |
else: |
|
94 |
return super(BugCommandGroup, self).__nonzero__() |
|
95 |
||
96 |
def __str__(self): |
|
97 |
text_commands = [super(BugCommandGroup, self).__str__()] |
|
98 |
for group in self.groups: |
|
99 |
text_commands += [str(group)] |
|
100 |
return '\n'.join(text_commands).strip() |
|
101 |
||
102 |
@property
|
|
103 |
def groups(self): |
|
13788.1.9
by Curtis Hovey
Allow new bugs that affect only one target to place the affect-command |
104 |
"Return the `BugTaskCommandGroup`s."
|
13788.1.10
by Curtis Hovey
Revised the rule to fix the order of the affects command. |
105 |
is_new_bug = ( |
106 |
len(self.commands) > 0 |
|
13788.1.9
by Curtis Hovey
Allow new bugs that affect only one target to place the affect-command |
107 |
and self.commands[0].RANK == 0 |
13788.1.10
by Curtis Hovey
Revised the rule to fix the order of the affects command. |
108 |
and self.commands[0].string_args == ['new']) |
109 |
has_split_affects = ( |
|
110 |
len(self._groups) == 2 |
|
111 |
and self._groups[0].commands[0].RANK != 0 |
|
112 |
and self._groups[1].commands[0].RANK == 0) |
|
113 |
if is_new_bug and has_split_affects: |
|
114 |
# The affects line was in the wrong position and this
|
|
115 |
# exact case can be fixed.
|
|
116 |
self._groups[0]._commands += self._groups[1]._commands |
|
117 |
del self._groups[1] |
|
13779.2.3
by Curtis Hovey
Merged bug command group helpers. |
118 |
return list(self._groups) |
119 |
||
120 |
def add(self, command_or_group): |
|
121 |
"""Add an `EmailCommand` or `BugTaskCommandGroup` to the commands.
|
|
122 |
||
123 |
Empty BugTaskCommandGroup are ignored.
|
|
124 |
"""
|
|
125 |
if isinstance(command_or_group, BugTaskCommandGroup): |
|
126 |
if command_or_group: |
|
127 |
self._groups.append(command_or_group) |
|
128 |
else: |
|
129 |
super(BugCommandGroup, self).add(command_or_group) |
|
130 |
||
131 |
||
132 |
class BugCommandGroups(BugCommandGroup): |
|
133 |
||
134 |
def __init__(self, commands): |
|
135 |
super(BugCommandGroups, self).__init__(command=None) |
|
136 |
self._groups = [] |
|
137 |
this_bug = BugCommandGroup() |
|
138 |
this_bugtask = BugTaskCommandGroup() |
|
139 |
for command in commands: |
|
140 |
if IBugEmailCommand.providedBy(command) and command.RANK == 0: |
|
141 |
# Multiple bugs are being edited.
|
|
142 |
this_bug.add(this_bugtask) |
|
143 |
self.add(this_bug) |
|
144 |
this_bug = BugCommandGroup(command) |
|
145 |
this_bugtask = BugTaskCommandGroup() |
|
146 |
elif IBugEditEmailCommand.providedBy(command): |
|
147 |
this_bug.add(command) |
|
148 |
elif (IBugTaskEmailCommand.providedBy(command) |
|
149 |
and command.RANK == 0): |
|
150 |
# Multiple or explicit bugtasks are being edited.
|
|
151 |
this_bug.add(this_bugtask) |
|
152 |
this_bugtask = BugTaskCommandGroup(command) |
|
153 |
elif IBugTaskEditEmailCommand.providedBy(command): |
|
154 |
this_bugtask.add(command) |
|
155 |
this_bug.add(this_bugtask) |
|
156 |
self.add(this_bug) |
|
157 |
||
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
158 |
def __iter__(self): |
159 |
for bug_group in self.groups: |
|
160 |
for command in bug_group.commands: |
|
161 |
yield command |
|
162 |
for bugtask_group in bug_group.groups: |
|
163 |
for command in bugtask_group.commands: |
|
164 |
yield command |
|
165 |
||
13779.2.3
by Curtis Hovey
Merged bug command group helpers. |
166 |
def add(self, command_or_group): |
167 |
"""Add a `BugCommandGroup` to the groups of commands.
|
|
13779.2.4
by Curtis Hovey
Removed whitespace. |
168 |
|
13779.2.3
by Curtis Hovey
Merged bug command group helpers. |
169 |
Empty BugCommandGroups are ignored.
|
170 |
"""
|
|
171 |
if isinstance(command_or_group, BugCommandGroup): |
|
172 |
if command_or_group: |
|
173 |
self._groups.append(command_or_group) |
|
174 |
||
175 |
||
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
176 |
class MaloneHandler: |
177 |
"""Handles emails sent to Malone.
|
|
178 |
||
179 |
It only handles mail sent to new@... and $bugid@..., where $bugid is a
|
|
180 |
positive integer.
|
|
181 |
"""
|
|
182 |
implements(IMailHandler) |
|
183 |
||
184 |
allow_unknown_users = False |
|
185 |
||
186 |
def getCommands(self, signed_msg): |
|
187 |
"""Returns a list of all the commands found in the email."""
|
|
188 |
content = get_main_body(signed_msg) |
|
189 |
if content is None: |
|
190 |
return [] |
|
191 |
return [BugEmailCommands.get(name=name, string_args=args) for |
|
192 |
name, args in parse_commands(content, |
|
193 |
BugEmailCommands.names())] |
|
194 |
||
195 |
def extractAndAuthenticateCommands(self, signed_msg, to_addr): |
|
196 |
"""Extract commands and handle special destinations.
|
|
197 |
||
198 |
NB: The authentication is carried out against the current principal,
|
|
199 |
not directly against the message. authenticateEmail must previously
|
|
200 |
have been called on this thread.
|
|
201 |
||
202 |
:returns: (final_result, add_comment_to_bug, commands)
|
|
203 |
If final_result is non-none, stop processing and return this value
|
|
204 |
to indicate whether the message was dealt with or not.
|
|
205 |
If add_comment_to_bug, add the contents to the first bug
|
|
206 |
selected.
|
|
207 |
commands is a list of bug commands.
|
|
208 |
"""
|
|
209 |
CONTEXT = 'bug report' |
|
210 |
commands = self.getCommands(signed_msg) |
|
211 |
to_user, to_host = to_addr.split('@') |
|
212 |
add_comment_to_bug = False |
|
14033.1.1
by Curtis Hovey
Send the bug-mail help instructions to non-active users. |
213 |
from_user = getUtility(ILaunchBag).user |
14033.1.3
by Curtis Hovey
Only check for preferredemail if it will be needed. |
214 |
if to_user.lower() == 'help' or from_user is None: |
215 |
if from_user is not None and from_user.preferredemail is not None: |
|
216 |
to_address = str(from_user.preferredemail.email) |
|
14033.1.2
by Curtis Hovey
Updated tests to be clear about the kind of address and user that |
217 |
else: |
218 |
to_address = signed_msg['From'] |
|
219 |
address = getUtility(IEmailAddressSet).getByEmail(to_address) |
|
220 |
if address is None: |
|
221 |
to_address = None |
|
222 |
if to_address is not None: |
|
14033.1.1
by Curtis Hovey
Send the bug-mail help instructions to non-active users. |
223 |
self.sendHelpEmail(to_address) |
224 |
return True, False, None |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
225 |
# If there are any commands, we must have strong authentication.
|
226 |
# We send a different failure message for attempts to create a new
|
|
227 |
# bug.
|
|
14033.1.1
by Curtis Hovey
Send the bug-mail help instructions to non-active users. |
228 |
elif to_user.lower() == 'new': |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
229 |
ensure_not_weakly_authenticated(signed_msg, CONTEXT, |
13668.1.26
by Curtis Hovey
Extracted bug process mail tests to lp.bugs. |
230 |
'unauthenticated-bug-creation.txt', |
231 |
error_templates=error_templates) |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
232 |
elif len(commands) > 0: |
233 |
ensure_not_weakly_authenticated(signed_msg, CONTEXT) |
|
234 |
if to_user.lower() == 'new': |
|
235 |
commands.insert(0, BugEmailCommands.get('bug', ['new'])) |
|
236 |
elif to_user.isdigit(): |
|
237 |
# A comment to a bug. We set add_comment_to_bug to True so
|
|
238 |
# that the comment gets added to the bug later. We don't add
|
|
239 |
# the comment now, since we want to let the 'bug' command
|
|
240 |
# handle the possible errors that can occur while getting
|
|
241 |
# the bug.
|
|
242 |
add_comment_to_bug = True |
|
243 |
commands.insert(0, BugEmailCommands.get('bug', [to_user])) |
|
244 |
elif to_user.lower() != 'edit': |
|
245 |
# Indicate that we didn't handle the mail.
|
|
246 |
return False, False, None |
|
13788.1.15
by Curtis Hovey
Style fixes. |
247 |
bug_commands = list(BugCommandGroups(commands)) |
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
248 |
return None, add_comment_to_bug, bug_commands |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
249 |
|
250 |
def process(self, signed_msg, to_addr, filealias=None, log=None): |
|
251 |
"""See IMailHandler."""
|
|
252 |
||
253 |
try: |
|
254 |
(final_result, add_comment_to_bug, |
|
255 |
commands, ) = self.extractAndAuthenticateCommands( |
|
256 |
signed_msg, to_addr) |
|
257 |
if final_result is not None: |
|
258 |
return final_result |
|
259 |
||
260 |
bug = None |
|
261 |
bug_event = None |
|
262 |
bugtask = None |
|
263 |
bugtask_event = None |
|
264 |
||
265 |
processing_errors = [] |
|
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
266 |
while len(commands) > 0: |
267 |
command = commands.pop(0) |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
268 |
try: |
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
269 |
if IBugEmailCommand.providedBy(command): |
270 |
# Finish outstanding work from the previous bug.
|
|
271 |
self.notify_bug_event(bug_event) |
|
272 |
self.notify_bugtask_event(bugtask_event, bug_event) |
|
273 |
bugtask = None |
|
274 |
bugtask_event = None |
|
275 |
# Get or start building a new bug.
|
|
276 |
bug, bug_event = command.execute( |
|
277 |
signed_msg, filealias) |
|
278 |
if add_comment_to_bug: |
|
279 |
message = self.appendBugComment( |
|
280 |
bug, signed_msg, filealias) |
|
281 |
add_comment_to_bug = False |
|
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
282 |
self.processAttachments(bug, message, signed_msg) |
283 |
elif IBugTaskEmailCommand.providedBy(command): |
|
284 |
self.notify_bugtask_event(bugtask_event, bug_event) |
|
285 |
bugtask, bugtask_event, bug_event = command.execute( |
|
286 |
bug, bug_event) |
|
287 |
if isinstance(bug, CreateBugParams): |
|
288 |
bug = bugtask.bug |
|
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
289 |
message = bug.initial_message |
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
290 |
self.processAttachments(bug, message, signed_msg) |
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
291 |
elif IBugEditEmailCommand.providedBy(command): |
292 |
bug, bug_event = command.execute(bug, bug_event) |
|
293 |
elif IBugTaskEditEmailCommand.providedBy(command): |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
294 |
if bugtask is None: |
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
295 |
if isinstance(bug, CreateBugParams): |
13779.1.1
by Curtis Hovey
Merged MaloneHandler.process() refactoring. |
296 |
self.handleNoAffectsTarget() |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
297 |
bugtask = guess_bugtask( |
298 |
bug, getUtility(ILaunchBag).user) |
|
299 |
if bugtask is None: |
|
13779.1.3
by Curtis Hovey
Unwrapped lines. |
300 |
self.handleNoDefaultAffectsTarget(bug) |
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
301 |
bugtask, bugtask_event = command.execute( |
302 |
bugtask, bugtask_event) |
|
303 |
||
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
304 |
except EmailProcessingError, error: |
305 |
processing_errors.append((error, command)) |
|
306 |
if error.stop_processing: |
|
307 |
commands = [] |
|
13970.10.7
by William Grant
Abolish sqlbase's begin and abort. |
308 |
transaction.abort() |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
309 |
else: |
310 |
continue
|
|
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
311 |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
312 |
if len(processing_errors) > 0: |
313 |
raise IncomingEmailError( |
|
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
314 |
'\n'.join(str(error) for error, command |
315 |
in processing_errors), |
|
316 |
[command for error, command in processing_errors]) |
|
13939.3.18
by Curtis Hovey
Reconcile the command changes with the handler using bug-emailinterface.txt |
317 |
if isinstance(bug, CreateBugParams): |
318 |
# A new bug without any commands was sent.
|
|
319 |
self.handleNoAffectsTarget() |
|
13788.1.11
by Curtis Hovey
Added an iterator to BugCommandGroups to provide a controlled order |
320 |
self.notify_bug_event(bug_event) |
321 |
self.notify_bugtask_event(bugtask_event, bug_event) |
|
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
322 |
|
323 |
except IncomingEmailError, error: |
|
324 |
send_process_error_notification( |
|
325 |
str(getUtility(ILaunchBag).user.preferredemail.email), |
|
326 |
'Submit Request Failure', |
|
327 |
error.message, signed_msg, error.failing_command) |
|
328 |
||
329 |
return True |
|
330 |
||
331 |
def sendHelpEmail(self, to_address): |
|
332 |
"""Send usage help to `to_address`."""
|
|
333 |
# Get the help text (formatted as MoinMoin markup)
|
|
14538.2.34
by Curtis Hovey
Move email templates to lp.bugs. |
334 |
help_text = get_email_template('help.txt', app='bugs') |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
335 |
help_text = reformat_wiki_text(help_text) |
336 |
# Wrap text
|
|
337 |
mailwrapper = MailWrapper(width=72) |
|
338 |
help_text = mailwrapper.format(help_text) |
|
339 |
simple_sendmail( |
|
340 |
'help@bugs.launchpad.net', to_address, |
|
341 |
'Launchpad Bug Tracker Email Interface Help', |
|
342 |
help_text) |
|
343 |
||
344 |
# Some content types indicate that an attachment has a special
|
|
345 |
# purpose. The current set is based on parsing emails from
|
|
346 |
# one mail account and may need to be extended.
|
|
347 |
#
|
|
348 |
# Mail signatures are most likely generated by the mail client
|
|
349 |
# and hence contain not data that is interesting except for
|
|
350 |
# mail authentication.
|
|
351 |
#
|
|
352 |
# Resource forks of MacOS files are not easily represented outside
|
|
353 |
# MacOS; if a resource fork contains useful debugging information,
|
|
354 |
# the entire MacOS file should be sent encapsulated for example in
|
|
355 |
# MacBinary format.
|
|
356 |
#
|
|
357 |
# application/ms-tnef attachment are created by Outlook; they
|
|
358 |
# seem to store no more than an RTF representation of an email.
|
|
359 |
||
360 |
irrelevant_content_types = set(( |
|
13668.1.16
by Curtis Hovey
Moved bug mail error templates to lp.bugs.mail. |
361 |
'application/applefile', # the resource fork of a MacOS file |
12043.4.1
by Gavin Panella
Move MaloneHandler into lp.bugs.mail.handler. |
362 |
'application/pgp-signature', |
363 |
'application/pkcs7-signature', |
|
364 |
'application/x-pkcs7-signature', |
|
365 |
'text/x-vcard', |
|
366 |
'application/ms-tnef', |
|
367 |
))
|
|
368 |
||
369 |
def processAttachments(self, bug, message, signed_mail): |
|
370 |
"""Create Bugattachments for "reasonable" mail attachments.
|
|
371 |
||
372 |
A mail attachment is stored as a bugattachment if its
|
|
373 |
content type is not listed in irrelevant_content_types.
|
|
374 |
"""
|
|
375 |
for chunk in message.chunks: |
|
376 |
blob = chunk.blob |
|
377 |
if blob is None: |
|
378 |
continue
|
|
379 |
# Mutt (other mail clients too?) appends the filename to the
|
|
380 |
# content type.
|
|
381 |
content_type = blob.mimetype.split(';', 1)[0] |
|
382 |
if content_type in self.irrelevant_content_types: |
|
383 |
continue
|
|
384 |
||
385 |
if content_type == 'text/html' and blob.filename == 'unnamed': |
|
386 |
# This is the HTML representation of the main part of
|
|
387 |
# an email.
|
|
388 |
continue
|
|
389 |
||
390 |
if content_type in ('text/x-diff', 'text/x-patch'): |
|
391 |
attach_type = BugAttachmentType.PATCH |
|
392 |
else: |
|
393 |
attach_type = BugAttachmentType.UNSPECIFIED |
|
394 |
||
395 |
getUtility(IBugAttachmentSet).create( |
|
396 |
bug=bug, filealias=blob, attach_type=attach_type, |
|
397 |
title=blob.filename, message=message, send_notifications=True) |
|
13779.1.1
by Curtis Hovey
Merged MaloneHandler.process() refactoring. |
398 |
|
399 |
def appendBugComment(self, bug, signed_msg, filealias=None): |
|
400 |
"""Append the message text to the bug comments."""
|
|
401 |
messageset = getUtility(IMessageSet) |
|
402 |
message = messageset.fromEmail( |
|
403 |
signed_msg.as_string(), |
|
404 |
owner=getUtility(ILaunchBag).user, |
|
405 |
filealias=filealias, |
|
406 |
parsed_message=signed_msg, |
|
407 |
fallback_parent=bug.initial_message) |
|
408 |
# If the new message's parent is linked to
|
|
409 |
# a bug watch we also link this message to
|
|
410 |
# that bug watch.
|
|
411 |
bug_message_set = getUtility(IBugMessageSet) |
|
412 |
parent_bug_message = ( |
|
413 |
bug_message_set.getByBugAndMessage(bug, message.parent)) |
|
414 |
if (parent_bug_message is not None and |
|
415 |
parent_bug_message.bugwatch): |
|
416 |
bug_watch = parent_bug_message.bugwatch |
|
417 |
else: |
|
418 |
bug_watch = None |
|
419 |
bugmessage = bug.linkMessage( |
|
420 |
message, bug_watch) |
|
421 |
notify(ObjectCreatedEvent(bugmessage)) |
|
422 |
return message |
|
423 |
||
424 |
def notify_bug_event(self, bug_event): |
|
425 |
if bug_event is None: |
|
426 |
return
|
|
427 |
try: |
|
428 |
notify(bug_event) |
|
429 |
except CreatedBugWithNoBugTasksError: |
|
430 |
self.handleNoAffectsTarget() |
|
431 |
||
432 |
def notify_bugtask_event(self, bugtask_event, bug_event): |
|
433 |
if bugtask_event is None: |
|
434 |
return
|
|
435 |
if not IObjectCreatedEvent.providedBy(bug_event): |
|
436 |
notify(bugtask_event) |
|
437 |
||
438 |
def handleNoAffectsTarget(self): |
|
13970.10.7
by William Grant
Abolish sqlbase's begin and abort. |
439 |
transaction.abort() |
13779.1.1
by Curtis Hovey
Merged MaloneHandler.process() refactoring. |
440 |
raise IncomingEmailError( |
441 |
get_error_message( |
|
442 |
'no-affects-target-on-submit.txt', |
|
443 |
error_templates=error_templates)) |
|
444 |
||
445 |
def handleNoDefaultAffectsTarget(self, bug): |
|
446 |
raise IncomingEmailError(get_error_message( |
|
447 |
'no-default-affects.txt', |
|
448 |
error_templates=error_templates, |
|
449 |
bug_id=bug.id, |
|
450 |
nr_of_bugtasks=len(bug.bugtasks))) |