Merge lp:~cjwatson/launchpad/destroy-ascii-smash into lp:launchpad
- destroy-ascii-smash
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 17611 |
Proposed branch: | lp:~cjwatson/launchpad/destroy-ascii-smash |
Merge into: | lp:launchpad |
Diff against target: |
708 lines (+101/-302) 8 files modified
lib/lp/answers/doc/person.txt (+2/-3) lib/lp/answers/doc/questionsets.txt (+14/-15) lib/lp/app/stories/launchpad-root/site-search.txt (+1/-3) lib/lp/archiveuploader/tests/nascentupload-announcements.txt (+4/-7) lib/lp/archiveuploader/tests/safe_fix_maintainer.txt (+24/-22) lib/lp/archiveuploader/utils.py (+5/-10) lib/lp/services/encoding.py (+1/-200) lib/lp/soyuz/adapters/notification.py (+50/-42) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/destroy-ascii-smash |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+264039@code.launchpad.net |
Commit message
Perform proper RFC2047-encoding of mail notification headers, and remove ascii_smash.
Description of the change
ascii_smash made some sense for shipit, where there were third-party limitations on label printing; but now that that's gone, there is no valid justification for using it anywhere in Launchpad. Its rendering of Arabic text is particularly terrible. I've replaced all uses of ascii_smash with either straightforward Unicode or RFC2047-encoding.
The Soyuz case requires delicate treatment, as we need fix_maintainer to cope with the particular variant of RFC822 used in Maintainer and Changed-By fields, but it isn't necessarily possible to run the RFC822 output of fix_maintainer back through fix_maintainer, because it may output a parenthesised form rather than an angle-bracketed form. I avoided this problem by keeping track of the e-mail address on its own in a few more places, which is enough for a Person lookup.
William Grant (wgrant) : | # |
Colin Watson (cjwatson) : | # |
Preview Diff
1 | === modified file 'lib/lp/answers/doc/person.txt' | |||
2 | --- lib/lp/answers/doc/person.txt 2013-05-01 00:23:31 +0000 | |||
3 | +++ lib/lp/answers/doc/person.txt 2015-07-07 13:35:34 +0000 | |||
4 | @@ -170,13 +170,12 @@ | |||
5 | 170 | But Carlos has one. | 170 | But Carlos has one. |
6 | 171 | 171 | ||
7 | 172 | # Because not everyone uses a real editor <wink> | 172 | # Because not everyone uses a real editor <wink> |
8 | 173 | >>> from lp.services.encoding import ascii_smash | ||
9 | 174 | >>> carlos_raw = personset.getByName('carlos') | 173 | >>> carlos_raw = personset.getByName('carlos') |
10 | 175 | >>> carlos = IQuestionsPerson(carlos_raw) | 174 | >>> carlos = IQuestionsPerson(carlos_raw) |
11 | 176 | >>> for question in carlos.searchQuestions( | 175 | >>> for question in carlos.searchQuestions( |
12 | 177 | ... language=(english, spanish)): | 176 | ... language=(english, spanish)): |
15 | 178 | ... print ascii_smash(question.title), question.language.code | 177 | ... print question.title, question.language.code |
16 | 179 | Problema al recompilar kernel con soporte smp (doble-nucleo) es | 178 | Problema al recompilar kernel con soporte smp (doble-núcleo) es |
17 | 180 | 179 | ||
18 | 181 | 180 | ||
19 | 182 | Questions needing attention | 181 | Questions needing attention |
20 | 183 | 182 | ||
21 | === modified file 'lib/lp/answers/doc/questionsets.txt' | |||
22 | --- lib/lp/answers/doc/questionsets.txt 2013-05-01 00:23:31 +0000 | |||
23 | +++ lib/lp/answers/doc/questionsets.txt 2015-07-07 13:35:34 +0000 | |||
24 | @@ -48,16 +48,15 @@ | |||
25 | 48 | regular full text algorithm. | 48 | regular full text algorithm. |
26 | 49 | 49 | ||
27 | 50 | # Because not everyone uses a real editor <wink> | 50 | # Because not everyone uses a real editor <wink> |
28 | 51 | >>> from lp.services.encoding import ascii_smash | ||
29 | 52 | >>> for question in question_set.searchQuestions(search_text=u'firefox'): | 51 | >>> for question in question_set.searchQuestions(search_text=u'firefox'): |
32 | 53 | ... print ascii_smash(question.title), question.target.displayname | 52 | ... print question.title, question.target.displayname |
33 | 54 | Problemas de Impressao no Firefox Mozilla Firefox | 53 | Problemas de Impressão no Firefox Mozilla Firefox |
34 | 55 | Firefox loses focus and gets stuck Mozilla Firefox | 54 | Firefox loses focus and gets stuck Mozilla Firefox |
35 | 56 | Firefox cannot render Bank Site Mozilla Firefox | 55 | Firefox cannot render Bank Site Mozilla Firefox |
36 | 57 | mailto: problem in webpage mozilla-firefox in Ubuntu | 56 | mailto: problem in webpage mozilla-firefox in Ubuntu |
37 | 58 | Newly installed plug-in doesn't seem to be used Mozilla Firefox | 57 | Newly installed plug-in doesn't seem to be used Mozilla Firefox |
38 | 59 | Problem showing the SVG demo on W3C site Mozilla Firefox | 58 | Problem showing the SVG demo on W3C site Mozilla Firefox |
40 | 60 | AINKAFSEEN ALEFLAMTEHGHAINYEHYEHREHALEFTEH ... Ubuntu | 59 | عكس التغييرات غير المحفوظة للمستن؟ Ubuntu |
41 | 61 | 60 | ||
42 | 62 | 61 | ||
43 | 63 | Status | 62 | Status |
44 | @@ -93,8 +92,8 @@ | |||
45 | 93 | >>> from lp.services.worlddata.interfaces.language import ILanguageSet | 92 | >>> from lp.services.worlddata.interfaces.language import ILanguageSet |
46 | 94 | >>> spanish = getUtility(ILanguageSet)['es'] | 93 | >>> spanish = getUtility(ILanguageSet)['es'] |
47 | 95 | >>> for t in question_set.searchQuestions(language=spanish): | 94 | >>> for t in question_set.searchQuestions(language=spanish): |
50 | 96 | ... print ascii_smash(t.title) | 95 | ... print t.title |
51 | 97 | Problema al recompilar kernel con soporte smp (doble-nucleo) | 96 | Problema al recompilar kernel con soporte smp (doble-núcleo) |
52 | 98 | 97 | ||
53 | 99 | 98 | ||
54 | 100 | Combinations | 99 | Combinations |
55 | @@ -106,14 +105,14 @@ | |||
56 | 106 | >>> for question in question_set.searchQuestions( | 105 | >>> for question in question_set.searchQuestions( |
57 | 107 | ... search_text=u'firefox', | 106 | ... search_text=u'firefox', |
58 | 108 | ... status=(QuestionStatus.OPEN, QuestionStatus.INVALID)): | 107 | ... status=(QuestionStatus.OPEN, QuestionStatus.INVALID)): |
60 | 109 | ... print ascii_smash(question.title), question.status.title, ( | 108 | ... print question.title, question.status.title, ( |
61 | 110 | ... question.target.displayname) | 109 | ... question.target.displayname) |
63 | 111 | Problemas de Impressao no Firefox Open Mozilla Firefox | 110 | Problemas de Impressão no Firefox Open Mozilla Firefox |
64 | 112 | Firefox is slow and consumes too much ... mozilla-firefox in Ubuntu | 111 | Firefox is slow and consumes too much ... mozilla-firefox in Ubuntu |
65 | 113 | Firefox loses focus and gets stuck Open Mozilla Firefox | 112 | Firefox loses focus and gets stuck Open Mozilla Firefox |
66 | 114 | Firefox cannot render Bank Site Open Mozilla Firefox | 113 | Firefox cannot render Bank Site Open Mozilla Firefox |
67 | 115 | Problem showing the SVG demo on W3C site Open Mozilla Firefox | 114 | Problem showing the SVG demo on W3C site Open Mozilla Firefox |
69 | 116 | AINKAFSEEN ALEFLAMTEHGHAINYEHYEHREHALEFTEH ... Ubuntu | 115 | عكس التغييرات غير المحفوظة للمستن؟ Open Ubuntu |
70 | 117 | 116 | ||
71 | 118 | 117 | ||
72 | 119 | Sort order | 118 | Sort order |
73 | @@ -126,24 +125,24 @@ | |||
74 | 126 | >>> from lp.answers.enums import QuestionSort | 125 | >>> from lp.answers.enums import QuestionSort |
75 | 127 | >>> for question in question_set.searchQuestions( | 126 | >>> for question in question_set.searchQuestions( |
76 | 128 | ... search_text=u'firefox', sort=QuestionSort.OLDEST_FIRST): | 127 | ... search_text=u'firefox', sort=QuestionSort.OLDEST_FIRST): |
78 | 129 | ... print question.id, ascii_smash(question.title), ( | 128 | ... print question.id, question.title, ( |
79 | 130 | ... question.target.displayname) | 129 | ... question.target.displayname) |
81 | 131 | 14 AINKAFSEEN ALEFLAMTEHGHAINYEHYEHREHALEFTEH ... Ubuntu | 130 | 14 عكس التغييرات غير المحفوظة للمستن؟ Ubuntu |
82 | 132 | 1 Firefox cannot render Bank Site Mozilla Firefox | 131 | 1 Firefox cannot render Bank Site Mozilla Firefox |
83 | 133 | 2 Problem showing the SVG demo on W3C site Mozilla Firefox | 132 | 2 Problem showing the SVG demo on W3C site Mozilla Firefox |
84 | 134 | 4 Firefox loses focus and gets stuck Mozilla Firefox | 133 | 4 Firefox loses focus and gets stuck Mozilla Firefox |
85 | 135 | 6 Newly installed plug-in doesn't seem to be used Mozilla Firefox | 134 | 6 Newly installed plug-in doesn't seem to be used Mozilla Firefox |
86 | 136 | 9 mailto: problem in webpage mozilla-firefox in Ubuntu | 135 | 9 mailto: problem in webpage mozilla-firefox in Ubuntu |
88 | 137 | 13 Problemas de Impressao no Firefox Mozilla Firefox | 136 | 13 Problemas de Impressão no Firefox Mozilla Firefox |
89 | 138 | 137 | ||
90 | 139 | When no text search is done, the default sort order is by newest first. | 138 | When no text search is done, the default sort order is by newest first. |
91 | 140 | 139 | ||
92 | 141 | >>> for question in question_set.searchQuestions( | 140 | >>> for question in question_set.searchQuestions( |
93 | 142 | ... status=QuestionStatus.OPEN)[:5]: | 141 | ... status=QuestionStatus.OPEN)[:5]: |
95 | 143 | ... print question.id, ascii_smash(question.title), ( | 142 | ... print question.id, question.title, ( |
96 | 144 | ... question.target.displayname) | 143 | ... question.target.displayname) |
99 | 145 | 13 Problemas de Impressao no Firefox Mozilla Firefox | 144 | 13 Problemas de Impressão no Firefox Mozilla Firefox |
100 | 146 | 12 Problema al recompilar kernel con soporte smp (doble-nucleo) Ubuntu | 145 | 12 Problema al recompilar kernel con soporte smp (doble-núcleo) Ubuntu |
101 | 147 | 11 Continue playing after shutdown Ubuntu | 146 | 11 Continue playing after shutdown Ubuntu |
102 | 148 | 5 Installation failed Ubuntu | 147 | 5 Installation failed Ubuntu |
103 | 149 | 4 Firefox loses focus and gets stuck Mozilla Firefox | 148 | 4 Firefox loses focus and gets stuck Mozilla Firefox |
104 | 150 | 149 | ||
105 | === modified file 'lib/lp/app/stories/launchpad-root/site-search.txt' | |||
106 | --- lib/lp/app/stories/launchpad-root/site-search.txt 2013-09-27 04:13:23 +0000 | |||
107 | +++ lib/lp/app/stories/launchpad-root/site-search.txt 2015-07-07 13:35:34 +0000 | |||
108 | @@ -5,8 +5,6 @@ | |||
109 | 5 | specific search with Launchpad's prominent objects (projects, bugs, | 5 | specific search with Launchpad's prominent objects (projects, bugs, |
110 | 6 | teams, etc.). | 6 | teams, etc.). |
111 | 7 | 7 | ||
112 | 8 | >>> from lp.services.encoding import ascii_smash | ||
113 | 9 | |||
114 | 10 | # Our very helpful function for printing all the page results. | 8 | # Our very helpful function for printing all the page results. |
115 | 11 | 9 | ||
116 | 12 | >>> def print_search_results(contents=None): | 10 | >>> def print_search_results(contents=None): |
117 | @@ -14,7 +12,7 @@ | |||
118 | 14 | ... contents = anon_browser.contents | 12 | ... contents = anon_browser.contents |
119 | 15 | ... tag = find_tag_by_id(contents, 'search-results') | 13 | ... tag = find_tag_by_id(contents, 'search-results') |
120 | 16 | ... if tag: | 14 | ... if tag: |
122 | 17 | ... print ascii_smash(extract_text(tag)) | 15 | ... print extract_text(tag) |
123 | 18 | 16 | ||
124 | 19 | # Another helper to make searching convenient. | 17 | # Another helper to make searching convenient. |
125 | 20 | 18 | ||
126 | 21 | 19 | ||
127 | === modified file 'lib/lp/archiveuploader/tests/nascentupload-announcements.txt' | |||
128 | --- lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2014-11-08 23:53:17 +0000 | |||
129 | +++ lib/lp/archiveuploader/tests/nascentupload-announcements.txt 2015-07-07 13:35:34 +0000 | |||
130 | @@ -512,7 +512,7 @@ | |||
131 | 512 | DEBUG * Changer using non-preferred email | 512 | DEBUG * Changer using non-preferred email |
132 | 513 | DEBUG | 513 | DEBUG |
133 | 514 | DEBUG Date: Tue, 25 Apr 2006 10:36:14 -0300 | 514 | DEBUG Date: Tue, 25 Apr 2006 10:36:14 -0300 |
135 | 515 | DEBUG Changed-By: Celso R. Providelo <cprov@ubuntu.com> | 515 | DEBUG Changed-By: cprov@ubuntu.com (Celso R. Providelo) |
136 | 516 | DEBUG Maintainer: Launchpad team <launchpad@lists.canonical.com> | 516 | DEBUG Maintainer: Launchpad team <launchpad@lists.canonical.com> |
137 | 517 | DEBUG http://launchpad.dev/ubuntu/+source/bar/1.0-4 | 517 | DEBUG http://launchpad.dev/ubuntu/+source/bar/1.0-4 |
138 | 518 | DEBUG | 518 | DEBUG |
139 | @@ -679,8 +679,7 @@ | |||
140 | 679 | 0 | 679 | 0 |
141 | 680 | 680 | ||
142 | 681 | Uploads with UTF-8 characters in email addresses in the changes file are | 681 | Uploads with UTF-8 characters in email addresses in the changes file are |
145 | 682 | permitted, but converted to ASCII, which is a limitation of the mailer. | 682 | permitted, but RFC2047-encoded. UTF-8 in the mail content is preserved. |
144 | 683 | However, UTF-8 in the mail content is preserved. | ||
146 | 684 | 683 | ||
147 | 685 | >>> hoary.status = SeriesStatus.DEVELOPMENT | 684 | >>> hoary.status = SeriesStatus.DEVELOPMENT |
148 | 686 | >>> anything_policy = getPolicy( | 685 | >>> anything_policy = getPolicy( |
149 | @@ -701,10 +700,8 @@ | |||
150 | 701 | >>> len(msgs) | 700 | >>> len(msgs) |
151 | 702 | 2 | 701 | 2 |
152 | 703 | 702 | ||
157 | 704 | "Cihar" should actually be "Čihař" but the mailer will convert to ASCII. | 703 | >>> [message['From'].replace('\n ', ' ') for message in msgs] |
158 | 705 | 704 | ['Root <root@localhost>', '=?utf-8?q?Non-ascii_changed-by_=C4=8Ciha=C5=99?= | |
155 | 706 | >>> [message['From'] for message in msgs] | ||
156 | 707 | ['Root <root@localhost>', 'Non-ascii changed-by Cihar | ||
159 | 708 | <daniel.silverstone@canonical.com>'] | 705 | <daniel.silverstone@canonical.com>'] |
160 | 709 | 706 | ||
161 | 710 | UTF-8 text in the changes file that is sent on the email is preserved | 707 | UTF-8 text in the changes file that is sent on the email is preserved |
162 | 711 | 708 | ||
163 | === modified file 'lib/lp/archiveuploader/tests/safe_fix_maintainer.txt' | |||
164 | --- lib/lp/archiveuploader/tests/safe_fix_maintainer.txt 2010-07-24 09:12:37 +0000 | |||
165 | +++ lib/lp/archiveuploader/tests/safe_fix_maintainer.txt 2015-07-07 13:35:34 +0000 | |||
166 | @@ -1,41 +1,43 @@ | |||
168 | 1 | Test some utils method inheritaded from DAK: | 1 | Test some utils method inherited from DAK: |
169 | 2 | 2 | ||
170 | 3 | safe_fix_maintainer() is a function used to sanitise the | 3 | safe_fix_maintainer() is a function used to sanitise the |
171 | 4 | identification fields coming from the Debian control files (changes | 4 | identification fields coming from the Debian control files (changes |
172 | 5 | and dsc). It allows safe unicode and non-unicode inputs. | 5 | and dsc). It allows safe unicode and non-unicode inputs. |
173 | 6 | 6 | ||
176 | 7 | >>> from lp.archiveuploader.utils import ( | 7 | >>> from lp.archiveuploader.utils import safe_fix_maintainer |
175 | 8 | ... safe_fix_maintainer) | ||
177 | 9 | 8 | ||
180 | 10 | >>> maintainer_field = 'maintainer' | 9 | >>> maintainer_field = 'maintainer' |
181 | 11 | >>> changer_field = 'changed-by' | 10 | >>> changer_field = 'changed-by' |
182 | 12 | 11 | ||
183 | 13 | Pure ASCII content using the two available fieldname (pretty much the same) | 12 | Pure ASCII content using the two available fieldname (pretty much the same) |
184 | 14 | 13 | ||
188 | 15 | >>> content = 'Hello World <hello@world.com>' | 14 | >>> content = 'Hello World <hello@world.com>' |
189 | 16 | >>> safe_fix_maintainer(content, maintainer_field) | 15 | >>> safe_fix_maintainer(content, maintainer_field) |
190 | 17 | ('Hello World <hello@world.com>', 'Hello World <hello@world.com>', 'Hello World', 'hello@world.com') | 16 | ('Hello World <hello@world.com>', 'Hello World <hello@world.com>', |
191 | 17 | 'Hello World', 'hello@world.com') | ||
192 | 18 | 18 | ||
196 | 19 | >>> content = 'Hello World <hello@world.com>' | 19 | >>> content = 'Hello World <hello@world.com>' |
197 | 20 | >>> safe_fix_maintainer(content, changer_field) | 20 | >>> safe_fix_maintainer(content, changer_field) |
198 | 21 | ('Hello World <hello@world.com>', 'Hello World <hello@world.com>', 'Hello World', 'hello@world.com') | 21 | ('Hello World <hello@world.com>', 'Hello World <hello@world.com>', |
199 | 22 | 'Hello World', 'hello@world.com') | ||
200 | 22 | 23 | ||
201 | 23 | 24 | ||
202 | 24 | Passing Unicode: | 25 | Passing Unicode: |
203 | 25 | 26 | ||
206 | 26 | # XXX cprov 2006-02-20 bug=32148: Not sure if it is working properly, | 27 | # XXX cprov 2006-02-20 bug=32148: Not sure if it is working properly, |
207 | 27 | # at least doesn't raise any exception like in bug #32148. | 28 | # at least doesn't raise any exception like in bug #32148. |
208 | 28 | 29 | ||
212 | 29 | >>> content = u'Rapha\xc3l Pinson <raphink@ubuntu.com>' | 30 | >>> content = u'Rapha\xc3l Pinson <raphink@ubuntu.com>' |
213 | 30 | >>> safe_fix_maintainer(content, maintainer_field) | 31 | >>> safe_fix_maintainer(content, maintainer_field) |
214 | 31 | ('RaphaAl Pinson <raphink@ubuntu.com>', 'RaphaAl Pinson <raphink@ubuntu.com>', 'RaphaAl Pinson', 'raphink@ubuntu.com') | 32 | ('Rapha\xc3\x83l Pinson <raphink@ubuntu.com>', |
215 | 33 | '=?utf-8?q?Rapha=C3=83l_Pinson?= <raphink@ubuntu.com>', | ||
216 | 34 | 'Rapha\xc3\x83l Pinson', 'raphink@ubuntu.com') | ||
217 | 32 | 35 | ||
218 | 33 | 36 | ||
219 | 34 | Passing latin encoded string: | 37 | Passing latin encoded string: |
220 | 35 | 38 | ||
227 | 36 | >>> content = 'Rapha\xebl Pinson <raphink@ubuntu.com>' | 39 | >>> content = 'Rapha\xebl Pinson <raphink@ubuntu.com>' |
228 | 37 | >>> safe_fix_maintainer(content, maintainer_field) | 40 | >>> safe_fix_maintainer(content, maintainer_field) |
229 | 38 | ('Raphael Pinson <raphink@ubuntu.com>', 'Raphael Pinson <raphink@ubuntu.com>', 'Raphael Pinson', 'raphink@ubuntu.com') | 41 | ('Rapha\xc3\xabl Pinson <raphink@ubuntu.com>', |
230 | 39 | 42 | '=?utf-8?q?Rapha=C3=ABl_Pinson?= <raphink@ubuntu.com>', | |
231 | 40 | 43 | 'Rapha\xc3\xabl Pinson', 'raphink@ubuntu.com') | |
226 | 41 | |||
232 | 42 | 44 | ||
233 | === modified file 'lib/lp/archiveuploader/utils.py' | |||
234 | --- lib/lp/archiveuploader/utils.py 2015-03-13 19:05:50 +0000 | |||
235 | +++ lib/lp/archiveuploader/utils.py 2015-07-07 13:35:34 +0000 | |||
236 | @@ -1,4 +1,4 @@ | |||
238 | 1 | # Copyright 2009-2012 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2015 Canonical Ltd. This software is licensed under the |
239 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
240 | 3 | 3 | ||
241 | 4 | """Archive uploader utilities.""" | 4 | """Archive uploader utilities.""" |
242 | @@ -37,10 +37,7 @@ | |||
243 | 37 | import signal | 37 | import signal |
244 | 38 | import subprocess | 38 | import subprocess |
245 | 39 | 39 | ||
250 | 40 | from lp.services.encoding import ( | 40 | from lp.services.encoding import guess as guess_encoding |
247 | 41 | ascii_smash, | ||
248 | 42 | guess as guess_encoding, | ||
249 | 43 | ) | ||
251 | 44 | from lp.soyuz.enums import BinaryPackageFileType | 41 | from lp.soyuz.enums import BinaryPackageFileType |
252 | 45 | 42 | ||
253 | 46 | 43 | ||
254 | @@ -269,15 +266,13 @@ | |||
255 | 269 | def safe_fix_maintainer(content, fieldname): | 266 | def safe_fix_maintainer(content, fieldname): |
256 | 270 | """Wrapper for fix_maintainer() to handle unicode and string argument. | 267 | """Wrapper for fix_maintainer() to handle unicode and string argument. |
257 | 271 | 268 | ||
260 | 272 | It verifies the content type and transform it in a unicode with guess() | 269 | It verifies the content type and transforms it to a unicode with |
261 | 273 | before call ascii_smash(). Then we can safely call fix_maintainer(). | 270 | guess(). Then we can safely call fix_maintainer(). |
262 | 274 | """ | 271 | """ |
263 | 275 | if type(content) != unicode: | 272 | if type(content) != unicode: |
264 | 276 | content = guess_encoding(content) | 273 | content = guess_encoding(content) |
265 | 277 | 274 | ||
269 | 278 | content = ascii_smash(content) | 275 | return fix_maintainer(content.encode("utf-8"), fieldname) |
267 | 279 | |||
268 | 280 | return fix_maintainer(content, fieldname) | ||
270 | 281 | 276 | ||
271 | 282 | 277 | ||
272 | 283 | def extract_dpkg_source(dsc_filepath, target, vendor=None): | 278 | def extract_dpkg_source(dsc_filepath, target, vendor=None): |
273 | 284 | 279 | ||
274 | === modified file 'lib/lp/services/encoding.py' | |||
275 | --- lib/lp/services/encoding.py 2011-12-19 23:38:16 +0000 | |||
276 | +++ lib/lp/services/encoding.py 2015-07-07 13:35:34 +0000 | |||
277 | @@ -1,20 +1,17 @@ | |||
279 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2015 Canonical Ltd. This software is licensed under the |
280 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
281 | 3 | 3 | ||
282 | 4 | """Character encoding utilities""" | 4 | """Character encoding utilities""" |
283 | 5 | 5 | ||
284 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
285 | 7 | __all__ = [ | 7 | __all__ = [ |
286 | 8 | 'ascii_smash', | ||
287 | 9 | 'escape_nonascii_uniquely', | 8 | 'escape_nonascii_uniquely', |
288 | 10 | 'guess', | 9 | 'guess', |
289 | 11 | 'is_ascii_only', | 10 | 'is_ascii_only', |
290 | 12 | ] | 11 | ] |
291 | 13 | 12 | ||
292 | 14 | import codecs | 13 | import codecs |
293 | 15 | from cStringIO import StringIO | ||
294 | 16 | import re | 14 | import re |
295 | 17 | import unicodedata | ||
296 | 18 | 15 | ||
297 | 19 | 16 | ||
298 | 20 | _boms = [ | 17 | _boms = [ |
299 | @@ -156,202 +153,6 @@ | |||
300 | 156 | return unicode(s, 'ISO-8859-1', 'replace') | 153 | return unicode(s, 'ISO-8859-1', 'replace') |
301 | 157 | 154 | ||
302 | 158 | 155 | ||
303 | 159 | def ascii_smash(unicode_string): | ||
304 | 160 | """Attempt to convert the Unicode string, possibly containing accents, | ||
305 | 161 | to an ASCII string. | ||
306 | 162 | |||
307 | 163 | This is used for generating shipping labels because our shipping company | ||
308 | 164 | can only deal with ASCII despite being European :-/ | ||
309 | 165 | |||
310 | 166 | ASCII goes through just fine | ||
311 | 167 | |||
312 | 168 | >>> ascii_smash(u"Hello") | ||
313 | 169 | 'Hello' | ||
314 | 170 | |||
315 | 171 | Latin-1 accented characters have their accents stripped. | ||
316 | 172 | |||
317 | 173 | >>> ascii_smash(u"Ol\N{LATIN SMALL LETTER E WITH ACUTE}") | ||
318 | 174 | 'Ole' | ||
319 | 175 | >>> ascii_smash(u"\N{LATIN CAPITAL LETTER A WITH RING ABOVE}iste") | ||
320 | 176 | 'Aiste' | ||
321 | 177 | >>> ascii_smash( | ||
322 | 178 | ... u"\N{LATIN SMALL LETTER AE}" | ||
323 | 179 | ... u"\N{LATIN SMALL LETTER I WITH GRAVE}" | ||
324 | 180 | ... u"\N{LATIN SMALL LETTER O WITH STROKE}" | ||
325 | 181 | ... u"\N{LATIN SMALL LETTER U WITH CIRCUMFLEX}" | ||
326 | 182 | ... ) | ||
327 | 183 | 'aeiou' | ||
328 | 184 | >>> ascii_smash( | ||
329 | 185 | ... u"\N{LATIN CAPITAL LETTER AE}" | ||
330 | 186 | ... u"\N{LATIN CAPITAL LETTER I WITH GRAVE}" | ||
331 | 187 | ... u"\N{LATIN CAPITAL LETTER O WITH STROKE}" | ||
332 | 188 | ... u"\N{LATIN CAPITAL LETTER U WITH TILDE}" | ||
333 | 189 | ... ) | ||
334 | 190 | 'AEIOU' | ||
335 | 191 | >>> ascii_smash(u"Stra\N{LATIN SMALL LETTER SHARP S}e") | ||
336 | 192 | 'Strasse' | ||
337 | 193 | |||
338 | 194 | Moving further into Eastern Europe we get more odd letters | ||
339 | 195 | |||
340 | 196 | >>> ascii_smash( | ||
341 | 197 | ... u"\N{LATIN CAPITAL LETTER Z WITH CARON}" | ||
342 | 198 | ... u"ivkovi\N{LATIN SMALL LETTER C WITH CARON}" | ||
343 | 199 | ... ) | ||
344 | 200 | 'Zivkovic' | ||
345 | 201 | |||
346 | 202 | >>> ascii_smash(u"\N{LATIN CAPITAL LIGATURE OE}\N{LATIN SMALL LIGATURE OE}") | ||
347 | 203 | 'OEoe' | ||
348 | 204 | |||
349 | 205 | """ | ||
350 | 206 | out = StringIO() | ||
351 | 207 | for char in unicode_string: | ||
352 | 208 | out.write(ascii_char_smash(char)) | ||
353 | 209 | return out.getvalue() | ||
354 | 210 | |||
355 | 211 | |||
356 | 212 | def ascii_char_smash(char): | ||
357 | 213 | """Smash a single Unicode character into an ASCII representation. | ||
358 | 214 | |||
359 | 215 | >>> ascii_char_smash(u"\N{KATAKANA LETTER SMALL A}") | ||
360 | 216 | 'a' | ||
361 | 217 | >>> ascii_char_smash(u"\N{KATAKANA LETTER A}") | ||
362 | 218 | 'A' | ||
363 | 219 | >>> ascii_char_smash(u"\N{KATAKANA LETTER KA}") | ||
364 | 220 | 'KA' | ||
365 | 221 | >>> ascii_char_smash(u"\N{HIRAGANA LETTER SMALL A}") | ||
366 | 222 | 'a' | ||
367 | 223 | >>> ascii_char_smash(u"\N{HIRAGANA LETTER A}") | ||
368 | 224 | 'A' | ||
369 | 225 | >>> ascii_char_smash(u"\N{BOPOMOFO LETTER ANG}") | ||
370 | 226 | 'ANG' | ||
371 | 227 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER H WITH STROKE}") | ||
372 | 228 | 'H' | ||
373 | 229 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER LONG S}") | ||
374 | 230 | 's' | ||
375 | 231 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER THORN}") | ||
376 | 232 | 'TH' | ||
377 | 233 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER THORN}") | ||
378 | 234 | 'th' | ||
379 | 235 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER I WITH OGONEK}") | ||
380 | 236 | 'I' | ||
381 | 237 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER AE}") | ||
382 | 238 | 'AE' | ||
383 | 239 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER A WITH DIAERESIS}") | ||
384 | 240 | 'Ae' | ||
385 | 241 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER A WITH DIAERESIS}") | ||
386 | 242 | 'ae' | ||
387 | 243 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER O WITH DIAERESIS}") | ||
388 | 244 | 'Oe' | ||
389 | 245 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER O WITH DIAERESIS}") | ||
390 | 246 | 'oe' | ||
391 | 247 | >>> ascii_char_smash(u"\N{LATIN CAPITAL LETTER U WITH DIAERESIS}") | ||
392 | 248 | 'Ue' | ||
393 | 249 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER U WITH DIAERESIS}") | ||
394 | 250 | 'ue' | ||
395 | 251 | >>> ascii_char_smash(u"\N{LATIN SMALL LETTER SHARP S}") | ||
396 | 252 | 'ss' | ||
397 | 253 | |||
398 | 254 | Latin-1 and other symbols are lost | ||
399 | 255 | |||
400 | 256 | >>> ascii_char_smash(u"\N{POUND SIGN}") | ||
401 | 257 | '' | ||
402 | 258 | |||
403 | 259 | Unless they also happen to be letters of some kind, such as greek | ||
404 | 260 | |||
405 | 261 | >>> ascii_char_smash(u"\N{MICRO SIGN}") | ||
406 | 262 | 'mu' | ||
407 | 263 | |||
408 | 264 | Fractions | ||
409 | 265 | |||
410 | 266 | >>> ascii_char_smash(u"\N{VULGAR FRACTION ONE HALF}") | ||
411 | 267 | '1/2' | ||
412 | 268 | |||
413 | 269 | """ | ||
414 | 270 | mapping = { | ||
415 | 271 | u"\N{LATIN CAPITAL LETTER AE}": "AE", | ||
416 | 272 | u"\N{LATIN SMALL LETTER AE}": "ae", | ||
417 | 273 | |||
418 | 274 | u"\N{LATIN CAPITAL LETTER A WITH DIAERESIS}": "Ae", | ||
419 | 275 | u"\N{LATIN SMALL LETTER A WITH DIAERESIS}": "ae", | ||
420 | 276 | |||
421 | 277 | u"\N{LATIN CAPITAL LETTER O WITH DIAERESIS}": "Oe", | ||
422 | 278 | u"\N{LATIN SMALL LETTER O WITH DIAERESIS}": "oe", | ||
423 | 279 | |||
424 | 280 | u"\N{LATIN CAPITAL LETTER U WITH DIAERESIS}": "Ue", | ||
425 | 281 | u"\N{LATIN SMALL LETTER U WITH DIAERESIS}": "ue", | ||
426 | 282 | |||
427 | 283 | u"\N{LATIN SMALL LETTER SHARP S}": "ss", | ||
428 | 284 | |||
429 | 285 | u"\N{LATIN CAPITAL LETTER THORN}": "TH", | ||
430 | 286 | u"\N{LATIN SMALL LETTER THORN}": "th", | ||
431 | 287 | |||
432 | 288 | u"\N{FRACTION SLASH}": "/", | ||
433 | 289 | u"\N{MULTIPLICATION SIGN}": "x", | ||
434 | 290 | |||
435 | 291 | u"\N{KATAKANA-HIRAGANA DOUBLE HYPHEN}": "=", | ||
436 | 292 | } | ||
437 | 293 | |||
438 | 294 | # Pass through ASCII | ||
439 | 295 | if ord(char) < 127: | ||
440 | 296 | return char | ||
441 | 297 | |||
442 | 298 | # Handle manual mappings | ||
443 | 299 | if mapping.has_key(char): | ||
444 | 300 | return mapping[char] | ||
445 | 301 | |||
446 | 302 | # Regress to decomposed form and recurse if necessary. | ||
447 | 303 | decomposed = unicodedata.normalize("NFKD", char) | ||
448 | 304 | if decomposed != char: | ||
449 | 305 | out = StringIO() | ||
450 | 306 | for char in decomposed: | ||
451 | 307 | out.write(ascii_char_smash(char)) | ||
452 | 308 | return out.getvalue() | ||
453 | 309 | |||
454 | 310 | # Handle whitespace | ||
455 | 311 | if char.isspace(): | ||
456 | 312 | return " " | ||
457 | 313 | |||
458 | 314 | # Handle digits | ||
459 | 315 | if char.isdigit(): | ||
460 | 316 | return unicodedata.digit(char) | ||
461 | 317 | |||
462 | 318 | # Handle decimal (probably pointless given isdigit above) | ||
463 | 319 | if char.isdecimal(): | ||
464 | 320 | return unicodedata.decimal(char) | ||
465 | 321 | |||
466 | 322 | # Handle numerics, such as 1/2 | ||
467 | 323 | if char.isnumeric(): | ||
468 | 324 | formatted = "%f" % unicodedata.numeric(char) | ||
469 | 325 | # Strip leading and trailing 0 | ||
470 | 326 | return formatted.strip("0") | ||
471 | 327 | |||
472 | 328 | # Ignore unprintables, such as the accents we denormalized | ||
473 | 329 | if not char.isalnum(): | ||
474 | 330 | return "" | ||
475 | 331 | |||
476 | 332 | # Return modified latin characters as just the latin part. | ||
477 | 333 | name = unicodedata.name(char) | ||
478 | 334 | |||
479 | 335 | match = re.search("LATIN CAPITAL LIGATURE (\w+)", name) | ||
480 | 336 | if match is not None: | ||
481 | 337 | return match.group(1) | ||
482 | 338 | |||
483 | 339 | match = re.search("LATIN SMALL LIGATURE (\w+)", name) | ||
484 | 340 | if match is not None: | ||
485 | 341 | return match.group(1).lower() | ||
486 | 342 | |||
487 | 343 | match = re.search("(?:LETTER SMALL|SMALL LETTER) (\w+)", name) | ||
488 | 344 | if match is not None: | ||
489 | 345 | return match.group(1).lower() | ||
490 | 346 | |||
491 | 347 | match = re.search("LETTER (\w+)", name) | ||
492 | 348 | if match is not None: | ||
493 | 349 | return match.group(1) | ||
494 | 350 | |||
495 | 351 | # Something we can't represent. Return empty string. | ||
496 | 352 | return "" | ||
497 | 353 | |||
498 | 354 | |||
499 | 355 | def escape_nonascii_uniquely(bogus_string): | 156 | def escape_nonascii_uniquely(bogus_string): |
500 | 356 | """Replace non-ascii characters with a hex representation. | 157 | """Replace non-ascii characters with a hex representation. |
501 | 357 | 158 | ||
502 | 358 | 159 | ||
503 | === modified file 'lib/lp/soyuz/adapters/notification.py' | |||
504 | --- lib/lp/soyuz/adapters/notification.py 2015-03-13 19:05:50 +0000 | |||
505 | +++ lib/lp/soyuz/adapters/notification.py 2015-07-07 13:35:34 +0000 | |||
506 | @@ -1,4 +1,4 @@ | |||
508 | 1 | # Copyright 2011-2014 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2011-2015 Canonical Ltd. This software is licensed under the |
509 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
510 | 3 | 3 | ||
511 | 4 | """Notification for uploads and copies.""" | 4 | """Notification for uploads and copies.""" |
512 | @@ -27,10 +27,7 @@ | |||
513 | 27 | from lp.registry.interfaces.person import IPersonSet | 27 | from lp.registry.interfaces.person import IPersonSet |
514 | 28 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 28 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
515 | 29 | from lp.services.config import config | 29 | from lp.services.config import config |
520 | 30 | from lp.services.encoding import ( | 30 | from lp.services.encoding import guess as guess_encoding |
517 | 31 | ascii_smash, | ||
518 | 32 | guess as guess_encoding, | ||
519 | 33 | ) | ||
521 | 34 | from lp.services.mail.helpers import get_email_template | 31 | from lp.services.mail.helpers import get_email_template |
522 | 35 | from lp.services.mail.sendmail import ( | 32 | from lp.services.mail.sendmail import ( |
523 | 36 | format_address, | 33 | format_address, |
524 | @@ -232,9 +229,11 @@ | |||
525 | 232 | 229 | ||
526 | 233 | info = fetch_information(spr, bprs, changes) | 230 | info = fetch_information(spr, bprs, changes) |
527 | 234 | from_addr = info['changedby'] | 231 | from_addr = info['changedby'] |
528 | 232 | from_email = info['changedby_email'] | ||
529 | 235 | if announce_from_person is not None: | 233 | if announce_from_person is not None: |
530 | 236 | if announce_from_person.preferredemail is not None: | 234 | if announce_from_person.preferredemail is not None: |
531 | 237 | from_addr = format_address_for_person(announce_from_person) | 235 | from_addr = format_address_for_person(announce_from_person) |
532 | 236 | from_email = announce_from_person.preferredemail.email | ||
533 | 238 | 237 | ||
534 | 239 | # If we're sending an acceptance notification for a non-PPA upload, | 238 | # If we're sending an acceptance notification for a non-PPA upload, |
535 | 240 | # announce if possible. Avoid announcing backports, binary-only | 239 | # announce if possible. Avoid announcing backports, binary-only |
536 | @@ -243,7 +242,7 @@ | |||
537 | 243 | and not archive.is_ppa | 242 | and not archive.is_ppa |
538 | 244 | and pocket != PackagePublishingPocket.BACKPORTS | 243 | and pocket != PackagePublishingPocket.BACKPORTS |
539 | 245 | and not (pocket == PackagePublishingPocket.SECURITY and spr is None) | 244 | and not (pocket == PackagePublishingPocket.SECURITY and spr is None) |
541 | 246 | and not is_auto_sync_upload(spr, bprs, pocket, from_addr)): | 245 | and not is_auto_sync_upload(spr, bprs, pocket, from_email)): |
542 | 247 | name = None | 246 | name = None |
543 | 248 | bcc_addr = None | 247 | bcc_addr = None |
544 | 249 | if spr: | 248 | if spr: |
545 | @@ -301,17 +300,17 @@ | |||
546 | 301 | # Some syncs (e.g. from Debian) will involve packages whose | 300 | # Some syncs (e.g. from Debian) will involve packages whose |
547 | 302 | # changed-by person was auto-created in LP and hence does not have a | 301 | # changed-by person was auto-created in LP and hence does not have a |
548 | 303 | # preferred email address set. We'll get a None here. | 302 | # preferred email address set. We'll get a None here. |
550 | 304 | changedby_person = email_to_person(info['changedby']) | 303 | changedby_person = email_to_person(info['changedby_email']) |
551 | 305 | 304 | ||
552 | 306 | if blamer is not None and blamer != changedby_person: | 305 | if blamer is not None and blamer != changedby_person: |
553 | 307 | signer_signature = person_to_email(blamer) | 306 | signer_signature = person_to_email(blamer) |
554 | 308 | if signer_signature != info['changedby']: | 307 | if signer_signature != info['changedby']: |
555 | 309 | information['SIGNER'] = '\nSigned-By: %s' % signer_signature | 308 | information['SIGNER'] = '\nSigned-By: %s' % signer_signature |
556 | 310 | # Add maintainer if present and different from changed-by. | 309 | # Add maintainer if present and different from changed-by. |
561 | 311 | maintainer = info['maintainer'] | 310 | maintainer_displayname = info['maintainer_displayname'] |
562 | 312 | changedby = info['changedby'] | 311 | if (maintainer_displayname and |
563 | 313 | if maintainer and maintainer != changedby: | 312 | maintainer_displayname != changedby_displayname): |
564 | 314 | information['MAINTAINER'] = '\nMaintainer: %s' % maintainer | 313 | information['MAINTAINER'] = '\nMaintainer: %s' % maintainer_displayname |
565 | 315 | return get_template(archive, action) % information | 314 | return get_template(archive, action) % information |
566 | 316 | 315 | ||
567 | 317 | 316 | ||
568 | @@ -360,24 +359,15 @@ | |||
569 | 360 | config.uploader.default_sender_name, | 359 | config.uploader.default_sender_name, |
570 | 361 | config.uploader.default_sender_address) | 360 | config.uploader.default_sender_address) |
571 | 362 | 361 | ||
572 | 363 | # `sendmail`, despite handling unicode message bodies, can't | ||
573 | 364 | # cope with non-ascii sender/recipient addresses, so ascii_smash | ||
574 | 365 | # is used on all addresses. | ||
575 | 366 | |||
576 | 367 | # All emails from here have a Bcc to the default recipient. | 362 | # All emails from here have a Bcc to the default recipient. |
577 | 368 | bcc_text = format_address( | 363 | bcc_text = format_address( |
578 | 369 | config.uploader.default_recipient_name, | 364 | config.uploader.default_recipient_name, |
579 | 370 | config.uploader.default_recipient_address) | 365 | config.uploader.default_recipient_address) |
580 | 371 | if bcc: | 366 | if bcc: |
581 | 372 | bcc_text = "%s, %s" % (bcc_text, bcc) | 367 | bcc_text = "%s, %s" % (bcc_text, bcc) |
583 | 373 | extra_headers['Bcc'] = ascii_smash(bcc_text) | 368 | extra_headers['Bcc'] = bcc_text |
584 | 374 | 369 | ||
591 | 375 | recipients = ascii_smash(", ".join(to_addrs)) | 370 | recipients = ", ".join(to_addrs) |
586 | 376 | if isinstance(from_addr, unicode): | ||
587 | 377 | # ascii_smash only works on unicode strings. | ||
588 | 378 | from_addr = ascii_smash(from_addr) | ||
589 | 379 | else: | ||
590 | 380 | from_addr.encode('ascii') | ||
592 | 381 | 371 | ||
593 | 382 | if dry_run and logger is not None: | 372 | if dry_run and logger is not None: |
594 | 383 | debug(logger, "Would have sent a mail:") | 373 | debug(logger, "Would have sent a mail:") |
595 | @@ -471,8 +461,8 @@ | |||
596 | 471 | candidate_recipients = [blamer] | 461 | candidate_recipients = [blamer] |
597 | 472 | info = fetch_information(spr, bprs, changes) | 462 | info = fetch_information(spr, bprs, changes) |
598 | 473 | 463 | ||
601 | 474 | changer = email_to_person(info['changedby']) | 464 | changer = email_to_person(info['changedby_email']) |
602 | 475 | maintainer = email_to_person(info['maintainer']) | 465 | maintainer = email_to_person(info['maintainer_email']) |
603 | 476 | 466 | ||
604 | 477 | if blamer is None and not archive.is_copy: | 467 | if blamer is None and not archive.is_copy: |
605 | 478 | debug(logger, "Changes file is unsigned; adding changer as recipient.") | 468 | debug(logger, "Changes file is unsigned; adding changer as recipient.") |
606 | @@ -565,23 +555,16 @@ | |||
607 | 565 | return summary | 555 | return summary |
608 | 566 | 556 | ||
609 | 567 | 557 | ||
612 | 568 | def email_to_person(fullemail): | 558 | def email_to_person(email): |
613 | 569 | """Return an `IPerson` given an RFC2047 email address. | 559 | """Return an `IPerson` given an email address (without a name). |
614 | 570 | 560 | ||
616 | 571 | :param fullemail: Potential email address. | 561 | :param email: Potential email address. |
617 | 572 | :return: `IPerson` with the given email address. None if there | 562 | :return: `IPerson` with the given email address. None if there |
619 | 573 | isn't one, or if `fullemail` isn't a proper email address. | 563 | isn't one, or if `email` is None. |
620 | 574 | """ | 564 | """ |
631 | 575 | if not fullemail: | 565 | if not email: |
632 | 576 | return None | 566 | return None |
633 | 577 | 567 | return getUtility(IPersonSet).getByEmail(email) | |
624 | 578 | try: | ||
625 | 579 | # The 2nd arg to s_f_m() doesn't matter as it won't fail since every- | ||
626 | 580 | # thing will have already parsed at this point. | ||
627 | 581 | rfc822, rfc2047, name, email = safe_fix_maintainer(fullemail, "email") | ||
628 | 582 | return getUtility(IPersonSet).getByEmail(email) | ||
629 | 583 | except ParseMaintError: | ||
630 | 584 | return None | ||
634 | 585 | 568 | ||
635 | 586 | 569 | ||
636 | 587 | def person_to_email(person): | 570 | def person_to_email(person): |
637 | @@ -591,6 +574,25 @@ | |||
638 | 591 | return format_address_for_person(person) | 574 | return format_address_for_person(person) |
639 | 592 | 575 | ||
640 | 593 | 576 | ||
641 | 577 | def fix_email(fullemail, field_name): | ||
642 | 578 | """Turn an email address from .changes into various useful forms. | ||
643 | 579 | |||
644 | 580 | The input address may be None, or anything that `fix_maintainer` | ||
645 | 581 | understands. | ||
646 | 582 | |||
647 | 583 | :return: A tuple of (RFC2047-compatible address, Unicode | ||
648 | 584 | RFC822-compatible address, email). | ||
649 | 585 | """ | ||
650 | 586 | if not fullemail: | ||
651 | 587 | return None, None, None | ||
652 | 588 | |||
653 | 589 | try: | ||
654 | 590 | rfc822, rfc2047, _, email = safe_fix_maintainer(fullemail, field_name) | ||
655 | 591 | return rfc2047, rfc822.decode('utf-8'), email | ||
656 | 592 | except ParseMaintError: | ||
657 | 593 | return None, None, None | ||
658 | 594 | |||
659 | 595 | |||
660 | 594 | def is_auto_sync_upload(spr, bprs, pocket, changed_by_email): | 596 | def is_auto_sync_upload(spr, bprs, pocket, changed_by_email): |
661 | 595 | """Return True if this is a (Debian) auto sync upload. | 597 | """Return True if this is a (Debian) auto sync upload. |
662 | 596 | 598 | ||
663 | @@ -609,17 +611,19 @@ | |||
664 | 609 | def fetch_information(spr, bprs, changes, previous_version=None): | 611 | def fetch_information(spr, bprs, changes, previous_version=None): |
665 | 610 | changedby = None | 612 | changedby = None |
666 | 611 | changedby_displayname = None | 613 | changedby_displayname = None |
667 | 614 | changedby_email = None | ||
668 | 612 | maintainer = None | 615 | maintainer = None |
669 | 613 | maintainer_displayname = None | 616 | maintainer_displayname = None |
670 | 617 | maintainer_email = None | ||
671 | 614 | 618 | ||
672 | 615 | if changes: | 619 | if changes: |
673 | 616 | changesfile = ChangesFile.formatChangesComment( | 620 | changesfile = ChangesFile.formatChangesComment( |
674 | 617 | sanitize_string(changes.get('Changes'))) | 621 | sanitize_string(changes.get('Changes'))) |
675 | 618 | date = changes.get('Date') | 622 | date = changes.get('Date') |
680 | 619 | changedby = sanitize_string(changes.get('Changed-By')) | 623 | changedby, changedby_displayname, changedby_email = fix_email( |
681 | 620 | maintainer = sanitize_string(changes.get('Maintainer')) | 624 | changes.get('Changed-By'), 'Changed-By') |
682 | 621 | changedby_displayname = changedby | 625 | maintainer, maintainer_displayname, maintainer_email = fix_email( |
683 | 622 | maintainer_displayname = maintainer | 626 | changes.get('Maintainer'), 'Maintainer') |
684 | 623 | elif spr or bprs: | 627 | elif spr or bprs: |
685 | 624 | if not spr and bprs: | 628 | if not spr and bprs: |
686 | 625 | spr = bprs[0].build.source_package_release | 629 | spr = bprs[0].build.source_package_release |
687 | @@ -631,10 +635,12 @@ | |||
688 | 631 | addr = formataddr((spr.creator.displayname, | 635 | addr = formataddr((spr.creator.displayname, |
689 | 632 | spr.creator.preferredemail.email)) | 636 | spr.creator.preferredemail.email)) |
690 | 633 | changedby_displayname = sanitize_string(addr) | 637 | changedby_displayname = sanitize_string(addr) |
691 | 638 | changedby_email = spr.creator.preferredemail.email | ||
692 | 634 | if maintainer: | 639 | if maintainer: |
693 | 635 | addr = formataddr((spr.maintainer.displayname, | 640 | addr = formataddr((spr.maintainer.displayname, |
694 | 636 | spr.maintainer.preferredemail.email)) | 641 | spr.maintainer.preferredemail.email)) |
695 | 637 | maintainer_displayname = sanitize_string(addr) | 642 | maintainer_displayname = sanitize_string(addr) |
696 | 643 | maintainer_email = spr.maintainer.preferredemail.email | ||
697 | 638 | else: | 644 | else: |
698 | 639 | changesfile = date = None | 645 | changesfile = date = None |
699 | 640 | 646 | ||
700 | @@ -643,8 +649,10 @@ | |||
701 | 643 | 'date': date, | 649 | 'date': date, |
702 | 644 | 'changedby': changedby, | 650 | 'changedby': changedby, |
703 | 645 | 'changedby_displayname': changedby_displayname, | 651 | 'changedby_displayname': changedby_displayname, |
704 | 652 | 'changedby_email': changedby_email, | ||
705 | 646 | 'maintainer': maintainer, | 653 | 'maintainer': maintainer, |
706 | 647 | 'maintainer_displayname': maintainer_displayname, | 654 | 'maintainer_displayname': maintainer_displayname, |
707 | 655 | 'maintainer_email': maintainer_email, | ||
708 | 648 | } | 656 | } |
709 | 649 | 657 | ||
710 | 650 | 658 |