Merge lp:~gary/launchpad/bug650343 into lp:launchpad
- bug650343
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Māris Fogels | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 11705 | ||||
Proposed branch: | lp:~gary/launchpad/bug650343 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
313 lines (+85/-48) 3 files modified
lib/canonical/launchpad/doc/incomingmail.txt (+65/-32) lib/canonical/launchpad/mail/incoming.py (+19/-15) lib/lp/blueprints/doc/spec-mail-exploder.txt (+1/-1) |
||||
To merge this branch: | bzr merge lp:~gary/launchpad/bug650343 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Francis J. Lacoste (community) | Approve | ||
Māris Fogels (community) | Approve | ||
Review via email: mp+37633@code.launchpad.net |
Commit message
Description of the change
See bug 650343 for the motivation. This is primarily just reverting the change done described in that bug, because the constraint/problem in the data center email configuration has been resolved.
I kept a spelling correction from the change. I also changed things per what lint described (e.g., converted to ReST), except for the singleton tuple complaints (e.g., in "log.warning('DKIM error: %r' % (e,))" the linter wants to see "(e, )").
./lib/canonical
126: E231 missing whitespace after ','
130: E231 missing whitespace after ','
133: E231 missing whitespace after ','
134: E231 missing whitespace after ','
142: E231 missing whitespace after ','
153: E231 missing whitespace after ','
Gary Poster (gary) wrote : | # |
Māris Fogels (mars) wrote : | # |
Hi Gary,
The code for this change looks good to land. r=mars. I have a few questions about the tests though:
* Do we want to silence the log warnings in the doctest output on line 26 of the diff? They look really odd. If we can not silence them, we could at least explain why they were left in there, and why there are four of them.
* We need some way to move tests like this into lib/lp. The problem is that this test lives in canonical/
This looks good to me. r=mars.
Maris
Jonathan Lange (jml) wrote : | # |
On Tue, Oct 5, 2010 at 5:37 PM, Māris Fogels <email address hidden> wrote:
> * We need some way to move tests like this into lib/lp. The problem is that this test lives in canonical/
It's easy enough to move them. Just copy what other lib/lp packages do
for their doctests.
jml
Francis J. Lacoste (flacoste) wrote : | # |
The header name should be changed to X-Launchpad-
Gary Poster (gary) wrote : | # |
This branch now has the change Francis pointed out was necessary, and so is ready for his review.
The requests for moving files around from jml and mars resulted in a 600+ line diff (`make lint` always gets its chunk of flesh) that can be found in its own branch: https:/
Francis J. Lacoste (flacoste) wrote : | # |
Looks good now, thanks.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/doc/incomingmail.txt' | |||
2 | --- lib/canonical/launchpad/doc/incomingmail.txt 2010-10-04 19:50:45 +0000 | |||
3 | +++ lib/canonical/launchpad/doc/incomingmail.txt 2010-10-12 13:37:43 +0000 | |||
4 | @@ -1,4 +1,5 @@ | |||
6 | 1 | = Incoming Mail = | 1 | Incoming Mail |
7 | 2 | ============= | ||
8 | 2 | 3 | ||
9 | 3 | When an email is sent to Launchpad we need to handle it somehow. This | 4 | When an email is sent to Launchpad we need to handle it somehow. This |
10 | 4 | is done by handleEmails: | 5 | is done by handleEmails: |
11 | @@ -14,7 +15,9 @@ | |||
12 | 14 | * Deletes the message from the mail box | 15 | * Deletes the message from the mail box |
13 | 15 | 16 | ||
14 | 16 | 17 | ||
16 | 17 | == Mail Handlers == | 18 | ------------- |
17 | 19 | Mail Handlers | ||
18 | 20 | ------------- | ||
19 | 18 | 21 | ||
20 | 19 | A mail handler is a utility which knows how to handle mail sent to a | 22 | A mail handler is a utility which knows how to handle mail sent to a |
21 | 20 | specific domain. It is registered as a named utility providing | 23 | specific domain. It is registered as a named utility providing |
22 | @@ -79,9 +82,16 @@ | |||
23 | 79 | >>> config.push('bugmail_error_from_address', bugmail_error_from_address) | 82 | >>> config.push('bugmail_error_from_address', bugmail_error_from_address) |
24 | 80 | 83 | ||
25 | 81 | The test mails are now in Launchpad's mail box, so now we can call | 84 | The test mails are now in Launchpad's mail box, so now we can call |
27 | 82 | handleMail, so that every mail gets handled by the correct handler. | 85 | handleMail, so that every mail gets handled by the correct handler. (We |
28 | 86 | see warnings about missing `X-Launchpad-Original-To`_ headers, which are | ||
29 | 87 | discussed below; this output merely shows that we emit warnings when the | ||
30 | 88 | header is missing.) | ||
31 | 83 | 89 | ||
32 | 84 | >>> handleMail(zopeless_transaction) | 90 | >>> handleMail(zopeless_transaction) |
33 | 91 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | ||
34 | 92 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | ||
35 | 93 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | ||
36 | 94 | WARNING:canonical.launchpad.mail:No X-Launchpad-Original-To header was present ... | ||
37 | 85 | 95 | ||
38 | 86 | Now we can see that each handler handled the emails sent to its domain: | 96 | Now we can see that each handler handled the emails sent to its domain: |
39 | 87 | 97 | ||
40 | @@ -90,8 +100,9 @@ | |||
41 | 90 | >>> set(bar_handler.handledMails) ^ set(msgids['bar.com']) | 100 | >>> set(bar_handler.handledMails) ^ set(msgids['bar.com']) |
42 | 91 | set([]) | 101 | set([]) |
43 | 92 | 102 | ||
46 | 93 | 103 | -------------- | |
47 | 94 | == Unhandled Mail == | 104 | Unhandled Mail |
48 | 105 | -------------- | ||
49 | 95 | 106 | ||
50 | 96 | So, what happened to the message that got sent to baz.com? Since there | 107 | So, what happened to the message that got sent to baz.com? Since there |
51 | 97 | wasn't a handler registered for that domain, an OOPS was recorded with | 108 | wasn't a handler registered for that domain, an OOPS was recorded with |
52 | @@ -118,14 +129,15 @@ | |||
53 | 118 | 129 | ||
54 | 119 | >>> stub.test_emails = [] | 130 | >>> stub.test_emails = [] |
55 | 120 | 131 | ||
58 | 121 | 132 | --------------------------------------------- | |
59 | 122 | == Mail from Persons not registered in Launchpad == | 133 | Mail from Persons not registered in Launchpad |
60 | 134 | --------------------------------------------- | ||
61 | 123 | 135 | ||
62 | 124 | If a Person who isn't registered in Launchpad sends an email, we'll | 136 | If a Person who isn't registered in Launchpad sends an email, we'll |
63 | 125 | most of the time reject the email: | 137 | most of the time reject the email: |
64 | 126 | 138 | ||
65 | 127 | >>> moin_change = read_test_message('moin-change.txt') | 139 | >>> moin_change = read_test_message('moin-change.txt') |
67 | 128 | >>> moin_change['X-Original-To'] = '123@foo.com' | 140 | >>> moin_change['X-Launchpad-Original-To'] = '123@foo.com' |
68 | 129 | >>> msgid = "<%s>" % sendmail(moin_change) | 141 | >>> msgid = "<%s>" % sendmail(moin_change) |
69 | 130 | >>> handleMail(zopeless_transaction) | 142 | >>> handleMail(zopeless_transaction) |
70 | 131 | >>> msgid not in foo_handler.handledMails | 143 | >>> msgid not in foo_handler.handledMails |
71 | @@ -140,7 +152,7 @@ | |||
72 | 140 | 152 | ||
73 | 141 | So if we send the mail to bar.com, bar_handler will handle the mail: | 153 | So if we send the mail to bar.com, bar_handler will handle the mail: |
74 | 142 | 154 | ||
76 | 143 | >>> moin_change.replace_header('To', '123@bar.com') | 155 | >>> moin_change.replace_header('X-Launchpad-Original-To', '123@bar.com') |
77 | 144 | >>> msgid = "<%s>" % sendmail(moin_change) | 156 | >>> msgid = "<%s>" % sendmail(moin_change) |
78 | 145 | >>> handleMail(zopeless_transaction) | 157 | >>> handleMail(zopeless_transaction) |
79 | 146 | >>> msgid in bar_handler.handledMails | 158 | >>> msgid in bar_handler.handledMails |
80 | @@ -148,8 +160,9 @@ | |||
81 | 148 | 160 | ||
82 | 149 | >>> stub.test_emails = [] | 161 | >>> stub.test_emails = [] |
83 | 150 | 162 | ||
86 | 151 | 163 | --------------------------------------------------------- | |
87 | 152 | == Mail from Persons with with an inactive Launchpad account == | 164 | Mail from Persons with with an inactive Launchpad account |
88 | 165 | --------------------------------------------------------- | ||
89 | 153 | 166 | ||
90 | 154 | If a Person who's account is inactive sends an email, it will be | 167 | If a Person who's account is inactive sends an email, it will be |
91 | 155 | silently rejected. | 168 | silently rejected. |
92 | @@ -173,15 +186,31 @@ | |||
93 | 173 | >>> msgid not in foo_handler.handledMails | 186 | >>> msgid not in foo_handler.handledMails |
94 | 174 | True | 187 | True |
95 | 175 | 188 | ||
105 | 176 | == X-Original-To == | 189 | ----------------------- |
106 | 177 | 190 | X-Launchpad-Original-To | |
107 | 178 | Ideally, we would use X-Original-To, as this reflects the actual address | 191 | ----------------------- |
108 | 179 | that the mail was sent to. Unfortunately, due to mail configuration issues, | 192 | |
109 | 180 | X-Original-To is very wrong, so we must rely on To and Cc, even though these | 193 | If available, the X-Launchpad-Original-To header is used to determine to |
110 | 181 | may be inaccurate. | 194 | which address the email was sent to: |
111 | 182 | 195 | ||
112 | 183 | 196 | >>> msg = read_test_message('signed_detached.txt') | |
113 | 184 | == OOPSes processing incoming mail == | 197 | >>> msg.replace_header('To', '123@foo.com') |
114 | 198 | >>> msg['CC'] = '123@foo.com' | ||
115 | 199 | >>> msg['X-Launchpad-Original-To'] = '123@bar.com' | ||
116 | 200 | >>> msgid = '<%s>' % sendmail (msg, ['123@bar.com']) | ||
117 | 201 | >>> handleMail(zopeless_transaction) | ||
118 | 202 | >>> msgid in bar_handler.handledMails | ||
119 | 203 | True | ||
120 | 204 | |||
121 | 205 | Only the address in X-Launchpad-Original-To header will be used. The | ||
122 | 206 | addresses in the To and CC headers will be ignored: | ||
123 | 207 | |||
124 | 208 | >>> msgid in foo_handler.handledMails | ||
125 | 209 | False | ||
126 | 210 | |||
127 | 211 | ------------------------------- | ||
128 | 212 | OOPSes processing incoming mail | ||
129 | 213 | ------------------------------- | ||
130 | 185 | 214 | ||
131 | 186 | If an unhandled exception occurs when we try to process an email from | 215 | If an unhandled exception occurs when we try to process an email from |
132 | 187 | a user, we record an OOPS with the exception and send it to the user. | 216 | a user, we record an OOPS with the exception and send it to the user. |
133 | @@ -203,7 +232,7 @@ | |||
134 | 203 | >>> msg = email.message_from_string( | 232 | >>> msg = email.message_from_string( |
135 | 204 | ... """From: Foo Bar <foo.bar@canonical.com> | 233 | ... """From: Foo Bar <foo.bar@canonical.com> |
136 | 205 | ... To: launchpad@oops.com | 234 | ... To: launchpad@oops.com |
138 | 206 | ... X-Original-To: launchpad@oops.com | 235 | ... X-Launchpad-Original-To: launchpad@oops.com |
139 | 207 | ... Subject: doesn't matter | 236 | ... Subject: doesn't matter |
140 | 208 | ... | 237 | ... |
141 | 209 | ... doesn't matter | 238 | ... doesn't matter |
142 | @@ -232,7 +261,7 @@ | |||
143 | 232 | ... | 261 | ... |
144 | 233 | From: Foo Bar <foo.bar@canonical.com> | 262 | From: Foo Bar <foo.bar@canonical.com> |
145 | 234 | To: launchpad@oops.com | 263 | To: launchpad@oops.com |
147 | 235 | X-Original-To: launchpad@oops.com | 264 | X-Launchpad-Original-To: launchpad@oops.com |
148 | 236 | Subject: doesn't matter | 265 | Subject: doesn't matter |
149 | 237 | ... | 266 | ... |
150 | 238 | 267 | ||
151 | @@ -251,7 +280,7 @@ | |||
152 | 251 | >>> msg = email.message_from_string( | 280 | >>> msg = email.message_from_string( |
153 | 252 | ... """From: Foo Bar <foo.bar@canonical.com> | 281 | ... """From: Foo Bar <foo.bar@canonical.com> |
154 | 253 | ... To: launchpad@unauthorized.com | 282 | ... To: launchpad@unauthorized.com |
156 | 254 | ... X-Original-To: launchpad@unauthorized.com | 283 | ... X-Launchpad-Original-To: launchpad@unauthorized.com |
157 | 255 | ... Subject: doesn't matter | 284 | ... Subject: doesn't matter |
158 | 256 | ... | 285 | ... |
159 | 257 | ... doesn't matter | 286 | ... doesn't matter |
160 | @@ -277,14 +306,15 @@ | |||
161 | 277 | ... | 306 | ... |
162 | 278 | From: Foo Bar <foo.bar@canonical.com> | 307 | From: Foo Bar <foo.bar@canonical.com> |
163 | 279 | To: launchpad@unauthorized.com | 308 | To: launchpad@unauthorized.com |
165 | 280 | X-Original-To: launchpad@unauthorized.com | 309 | X-Launchpad-Original-To: launchpad@unauthorized.com |
166 | 281 | Subject: doesn't matter | 310 | Subject: doesn't matter |
167 | 282 | ... | 311 | ... |
168 | 283 | 312 | ||
169 | 284 | >>> stub.test_emails = [] | 313 | >>> stub.test_emails = [] |
170 | 285 | 314 | ||
173 | 286 | 315 | ------------- | |
174 | 287 | == DB exceptions == | 316 | DB exceptions |
175 | 317 | ------------- | ||
176 | 288 | 318 | ||
177 | 289 | If something goes wrongs in the handler, a DB exception can be raised, | 319 | If something goes wrongs in the handler, a DB exception can be raised, |
178 | 290 | leaving the database in a bad state. If that happens a traceback should | 320 | leaving the database in a bad state. If that happens a traceback should |
179 | @@ -305,7 +335,7 @@ | |||
180 | 305 | >>> exception_raiser = email.message_from_string( | 335 | >>> exception_raiser = email.message_from_string( |
181 | 306 | ... """From: Foo Bar <foo.bar@canonical.com> | 336 | ... """From: Foo Bar <foo.bar@canonical.com> |
182 | 307 | ... To: something@except.com | 337 | ... To: something@except.com |
184 | 308 | ... X-Original-To: something@except.com | 338 | ... X-Launchpad-Original-To: something@except.com |
185 | 309 | ... Subject: Raise an exception | 339 | ... Subject: Raise an exception |
186 | 310 | ... | 340 | ... |
187 | 311 | ... This part is not important. | 341 | ... This part is not important. |
188 | @@ -328,6 +358,7 @@ | |||
189 | 328 | ... | 358 | ... |
190 | 329 | DataError: division by zero | 359 | DataError: division by zero |
191 | 330 | <BLANKLINE> | 360 | <BLANKLINE> |
192 | 361 | WARNING... | ||
193 | 331 | 362 | ||
194 | 332 | The second mail we sent got handled despite the exception: | 363 | The second mail we sent got handled despite the exception: |
195 | 333 | 364 | ||
196 | @@ -340,8 +371,9 @@ | |||
197 | 340 | >>> len(stub.test_emails) | 371 | >>> len(stub.test_emails) |
198 | 341 | 1 | 372 | 1 |
199 | 342 | 373 | ||
202 | 343 | 374 | --------------------- | |
203 | 344 | == Librarian not running == | 375 | Librarian not running |
204 | 376 | --------------------- | ||
205 | 345 | 377 | ||
206 | 346 | If for some reason the Librarian isn't up and running, we shouldn't | 378 | If for some reason the Librarian isn't up and running, we shouldn't |
207 | 347 | lose any emails. All that should happen is that an error should get | 379 | lose any emails. All that should happen is that an error should get |
208 | @@ -368,8 +400,9 @@ | |||
209 | 368 | >>> LibrarianLayer.reveal() | 400 | >>> LibrarianLayer.reveal() |
210 | 369 | >>> stub.test_emails = [] | 401 | >>> stub.test_emails = [] |
211 | 370 | 402 | ||
214 | 371 | 403 | ---------------- | |
215 | 372 | == Handling bounces == | 404 | Handling bounces |
216 | 405 | ---------------- | ||
217 | 373 | 406 | ||
218 | 374 | Some broken mailers might not respect the Errors-To and Return-Path | 407 | Some broken mailers might not respect the Errors-To and Return-Path |
219 | 375 | headers, send error messages back to the address, from which the email | 408 | headers, send error messages back to the address, from which the email |
220 | @@ -427,7 +460,7 @@ | |||
221 | 427 | 0 | 460 | 0 |
222 | 428 | 461 | ||
223 | 429 | 462 | ||
225 | 430 | == Doctest cleanup == | 463 | .. Doctest cleanup |
226 | 431 | 464 | ||
227 | 432 | >>> config_data = config.pop('bugmail_error_from_address') | 465 | >>> config_data = config.pop('bugmail_error_from_address') |
228 | 433 | >>> mail_handlers.add('foo.com', None) | 466 | >>> mail_handlers.add('foo.com', None) |
229 | 434 | 467 | ||
230 | === modified file 'lib/canonical/launchpad/mail/incoming.py' | |||
231 | --- lib/canonical/launchpad/mail/incoming.py 2010-10-03 15:30:06 +0000 | |||
232 | +++ lib/canonical/launchpad/mail/incoming.py 2010-10-12 13:37:43 +0000 | |||
233 | @@ -149,7 +149,7 @@ | |||
234 | 149 | % (signing_domain, from_domain)) | 149 | % (signing_domain, from_domain)) |
235 | 150 | return False | 150 | return False |
236 | 151 | if not _isDkimDomainTrusted(signing_domain): | 151 | if not _isDkimDomainTrusted(signing_domain): |
238 | 152 | log.warning("valid DKIM signature from untrusted domain %s" | 152 | log.warning("valid DKIM signature from untrusted domain %s" |
239 | 153 | % (signing_domain,)) | 153 | % (signing_domain,)) |
240 | 154 | return False | 154 | return False |
241 | 155 | return True | 155 | return True |
242 | @@ -338,8 +338,7 @@ | |||
243 | 338 | if mail['Return-Path'] == '<>': | 338 | if mail['Return-Path'] == '<>': |
244 | 339 | _handle_error( | 339 | _handle_error( |
245 | 340 | "Message had an empty Return-Path.", | 340 | "Message had an empty Return-Path.", |
248 | 341 | file_alias_url, notify=False | 341 | file_alias_url, notify=False) |
247 | 342 | ) | ||
249 | 343 | continue | 342 | continue |
250 | 344 | if mail.get_content_type() == 'multipart/report': | 343 | if mail.get_content_type() == 'multipart/report': |
251 | 345 | # Mails with a content type of multipart/report are | 344 | # Mails with a content type of multipart/report are |
252 | @@ -351,8 +350,7 @@ | |||
253 | 351 | if 'precedence' in mail: | 350 | if 'precedence' in mail: |
254 | 352 | _handle_error( | 351 | _handle_error( |
255 | 353 | "Got a message with a precedence header.", | 352 | "Got a message with a precedence header.", |
258 | 354 | file_alias_url, notify=False | 353 | file_alias_url, notify=False) |
257 | 355 | ) | ||
259 | 356 | continue | 354 | continue |
260 | 357 | 355 | ||
261 | 358 | try: | 356 | try: |
262 | @@ -366,14 +364,21 @@ | |||
263 | 366 | file_alias_url, notify=False) | 364 | file_alias_url, notify=False) |
264 | 367 | continue | 365 | continue |
265 | 368 | 366 | ||
274 | 369 | # Extract the domain the mail was sent to. Mails sent to | 367 | # Extract the domain the mail was sent to. Mails sent to |
275 | 370 | # Launchpad should have an X-Original-To header, but | 368 | # Launchpad should have an X-Launchpad-Original-To header. |
276 | 371 | # it has an incorrect address. | 369 | if 'X-Launchpad-Original-To' in mail: |
277 | 372 | # Process all addresses found as a fall back. | 370 | addresses = [mail['X-Launchpad-Original-To']] |
278 | 373 | cc = mail.get_all('cc') or [] | 371 | else: |
279 | 374 | to = mail.get_all('to') or [] | 372 | log = logging.getLogger('canonical.launchpad.mail') |
280 | 375 | names_addresses = getaddresses(to + cc) | 373 | log.warn( |
281 | 376 | addresses = [addr for name, addr in names_addresses] | 374 | "No X-Launchpad-Original-To header was present " |
282 | 375 | "in email: %s" % | ||
283 | 376 | file_alias_url) | ||
284 | 377 | # Process all addresses found as a fall back. | ||
285 | 378 | cc = mail.get_all('cc') or [] | ||
286 | 379 | to = mail.get_all('to') or [] | ||
287 | 380 | names_addresses = getaddresses(to + cc) | ||
288 | 381 | addresses = [addr for name, addr in names_addresses] | ||
289 | 377 | 382 | ||
290 | 378 | try: | 383 | try: |
291 | 379 | do_paranoid_envelope_to_validation(addresses) | 384 | do_paranoid_envelope_to_validation(addresses) |
292 | @@ -400,8 +405,7 @@ | |||
293 | 400 | if principal is None and not handler.allow_unknown_users: | 405 | if principal is None and not handler.allow_unknown_users: |
294 | 401 | _handle_error( | 406 | _handle_error( |
295 | 402 | 'Unknown user: %s ' % mail['From'], | 407 | 'Unknown user: %s ' % mail['From'], |
298 | 403 | file_alias_url, notify=False | 408 | file_alias_url, notify=False) |
297 | 404 | ) | ||
299 | 405 | continue | 409 | continue |
300 | 406 | 410 | ||
301 | 407 | handled = handler.process(mail, email_addr, file_alias) | 411 | handled = handler.process(mail, email_addr, file_alias) |
302 | 408 | 412 | ||
303 | === modified file 'lib/lp/blueprints/doc/spec-mail-exploder.txt' | |||
304 | --- lib/lp/blueprints/doc/spec-mail-exploder.txt 2010-07-30 12:56:27 +0000 | |||
305 | +++ lib/lp/blueprints/doc/spec-mail-exploder.txt 2010-10-12 13:37:43 +0000 | |||
306 | @@ -264,7 +264,7 @@ | |||
307 | 264 | True | 264 | True |
308 | 265 | 265 | ||
309 | 266 | >>> moin_change = read_test_message('moin-change.txt') | 266 | >>> moin_change = read_test_message('moin-change.txt') |
311 | 267 | >>> moin_change['To'] += ", notifications@%s" % ( | 267 | >>> moin_change['X-Launchpad-Original-To'] = "notifications@%s" % ( |
312 | 268 | ... config.launchpad.specs_domain) | 268 | ... config.launchpad.specs_domain) |
313 | 269 | >>> moin_change['Sender'] = 'webmaster@ubuntu.com' | 269 | >>> moin_change['Sender'] = 'webmaster@ubuntu.com' |
314 | 270 | 270 |
Sorry, bug 650343.