Premiere version : mise en route du suivi.
[auf_roundup.git] / test / .svn / text-base / test_mailgw.py.svn-base
1 # -*- encoding: utf-8 -*-
2 #
3 # Copyright (c) 2001 Richard Jones, richard@bofh.asn.au.
4 # This module is free software, and you may redistribute it and/or modify
5 # under the same terms as Python, so long as this copyright message and
6 # disclaimer are retained in their original form.
7 #
8 # This module is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 #
12 # $Id: test_mailgw.py,v 1.96 2008-08-19 01:40:59 richard Exp $
13
14 # TODO: test bcc
15
16 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time
17
18 from cStringIO import StringIO
19
20 if not os.environ.has_key('SENDMAILDEBUG'):
21     os.environ['SENDMAILDEBUG'] = 'mail-test.log'
22 SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
23
24 from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \
25     parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp
26 from roundup import init, instance, password, rfc2822, __version__
27 from roundup.anypy.sets_ import set
28
29 #import db_test_base
30 import memorydb
31
32 class Message(rfc822.Message):
33     """String-based Message class with equivalence test."""
34     def __init__(self, s):
35         rfc822.Message.__init__(self, StringIO(s.strip()))
36
37     def __eq__(self, other):
38         return (self.dict == other.dict and
39                 self.fp.read() == other.fp.read())
40
41 class Tracker(object):
42     def open(self, journaltag):
43         return self.db
44
45 class DiffHelper:
46     def compareMessages(self, new, old):
47         """Compare messages for semantic equivalence."""
48         new, old = Message(new), Message(old)
49
50         # all Roundup-generated messages have "Precedence: bulk"
51         old['Precedence'] = 'bulk'
52
53         # don't try to compare the date
54         del new['date'], old['date']
55
56         if not new == old:
57             res = []
58
59             replace = {}
60             for key in new.keys():
61                 if key.startswith('from '):
62                     # skip the unix from line
63                     continue
64                 if key.lower() == 'x-roundup-version':
65                     # version changes constantly, so handle it specially
66                     if new[key] != __version__:
67                         res.append('  %s: %r != %r' % (key, __version__,
68                             new[key]))
69                 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
70                     # handle mime messages
71                     newmime = new[key].split('=',1)[-1].strip('"')
72                     oldmime = old.get(key, '').split('=',1)[-1].strip('"')
73                     replace ['--' + newmime] = '--' + oldmime
74                     replace ['--' + newmime + '--'] = '--' + oldmime + '--'
75                 elif new.get(key, '') != old.get(key, ''):
76                     res.append('  %s: %r != %r' % (key, old.get(key, ''),
77                         new.get(key, '')))
78
79             body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
80                 replace=replace)
81             if body_diff:
82                 res.append('')
83                 res.extend(body_diff)
84
85             if res:
86                 res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):')
87                 raise AssertionError, '\n'.join(res)
88
89     def compareStrings(self, s2, s1, replace={}):
90         '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
91            the first to be the "original" but in the calls in this file,
92            the second arg is the original. Ho hum.
93            Do replacements over the replace dict -- used for mime boundary
94         '''
95         l1 = s1.strip().split('\n')
96         l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
97         if l1 == l2:
98             return
99         s = difflib.SequenceMatcher(None, l1, l2)
100         res = []
101         for value, s1s, s1e, s2s, s2e in s.get_opcodes():
102             if value == 'equal':
103                 for i in range(s1s, s1e):
104                     res.append('  %s'%l1[i])
105             elif value == 'delete':
106                 for i in range(s1s, s1e):
107                     res.append('- %s'%l1[i])
108             elif value == 'insert':
109                 for i in range(s2s, s2e):
110                     res.append('+ %s'%l2[i])
111             elif value == 'replace':
112                 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
113                     res.append('- %s'%l1[i])
114                     res.append('+ %s'%l2[j])
115
116         return res
117
118 class MailgwTestCase(unittest.TestCase, DiffHelper):
119     count = 0
120     schema = 'classic'
121     def setUp(self):
122         MailgwTestCase.count = MailgwTestCase.count + 1
123
124         # and open the database / "instance"
125         self.db = memorydb.create('admin')
126         self.instance = Tracker()
127         self.instance.db = self.db
128         self.instance.config = self.db.config
129         self.instance.MailGW = MailGW
130
131         self.chef_id = self.db.user.create(username='Chef',
132             address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
133         self.richard_id = self.db.user.create(username='richard',
134             address='richard@test.test', roles='User')
135         self.mary_id = self.db.user.create(username='mary',
136             address='mary@test.test', roles='User', realname='Contrary, Mary')
137         self.john_id = self.db.user.create(username='john',
138             address='john@test.test', roles='User', realname='John Doe',
139             alternate_addresses='jondoe@test.test\njohn.doe@test.test')
140         self.rgg_id = self.db.user.create(username='rgg',
141             address='rgg@test.test', roles='User')
142
143     def tearDown(self):
144         if os.path.exists(SENDMAILDEBUG):
145             os.remove(SENDMAILDEBUG)
146         self.db.close()
147
148     def _create_mailgw(self, message):
149         class MailGW(self.instance.MailGW):
150             def handle_message(self, message):
151                 return self._handle_message(message)
152         handler = MailGW(self.instance)
153         handler.db = self.db
154         return handler
155
156     def _handle_mail(self, message):
157         handler = self._create_mailgw(message)
158         handler.trapExceptions = 0
159         return handler.main(StringIO(message))
160
161     def _get_mail(self):
162         f = open(SENDMAILDEBUG)
163         try:
164             return f.read()
165         finally:
166             f.close()
167
168     def testEmptyMessage(self):
169         nodeid = self._handle_mail('''Content-Type: text/plain;
170   charset="iso-8859-1"
171 From: Chef <chef@bork.bork.bork>
172 To: issue_tracker@your.tracker.email.domain.example
173 Cc: richard@test.test
174 Reply-To: chef@bork.bork.bork
175 Message-Id: <dummy_test_message_id>
176 Subject: [issue] Testing...
177
178 ''')
179         assert not os.path.exists(SENDMAILDEBUG)
180         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
181
182     def testMessageWithFromInIt(self):
183         nodeid = self._handle_mail('''Content-Type: text/plain;
184   charset="iso-8859-1"
185 From: Chef <chef@bork.bork.bork>
186 To: issue_tracker@your.tracker.email.domain.example
187 Cc: richard@test.test
188 Reply-To: chef@bork.bork.bork
189 Message-Id: <dummy_test_message_id>
190 Subject: [issue] Testing...
191
192 From here to there!
193 ''')
194         assert not os.path.exists(SENDMAILDEBUG)
195         msgid = self.db.issue.get(nodeid, 'messages')[0]
196         self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!')
197
198     def doNewIssue(self):
199         nodeid = self._handle_mail('''Content-Type: text/plain;
200   charset="iso-8859-1"
201 From: Chef <chef@bork.bork.bork>
202 To: issue_tracker@your.tracker.email.domain.example
203 Cc: richard@test.test
204 Message-Id: <dummy_test_message_id>
205 Subject: [issue] Testing...
206
207 This is a test submission of a new issue.
208 ''')
209         assert not os.path.exists(SENDMAILDEBUG)
210         l = self.db.issue.get(nodeid, 'nosy')
211         l.sort()
212         self.assertEqual(l, [self.chef_id, self.richard_id])
213         return nodeid
214
215     def testNewIssue(self):
216         self.doNewIssue()
217
218     def testNewIssueNosy(self):
219         self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
220         nodeid = self._handle_mail('''Content-Type: text/plain;
221   charset="iso-8859-1"
222 From: Chef <chef@bork.bork.bork>
223 To: issue_tracker@your.tracker.email.domain.example
224 Cc: richard@test.test
225 Message-Id: <dummy_test_message_id>
226 Subject: [issue] Testing...
227
228 This is a test submission of a new issue.
229 ''')
230         assert not os.path.exists(SENDMAILDEBUG)
231         l = self.db.issue.get(nodeid, 'nosy')
232         l.sort()
233         self.assertEqual(l, [self.chef_id, self.richard_id])
234
235     def testAlternateAddress(self):
236         self._handle_mail('''Content-Type: text/plain;
237   charset="iso-8859-1"
238 From: John Doe <john.doe@test.test>
239 To: issue_tracker@your.tracker.email.domain.example
240 Message-Id: <dummy_test_message_id>
241 Subject: [issue] Testing...
242
243 This is a test submission of a new issue.
244 ''')
245         userlist = self.db.user.list()
246         assert not os.path.exists(SENDMAILDEBUG)
247         self.assertEqual(userlist, self.db.user.list(),
248             "user created when it shouldn't have been")
249
250     def testNewIssueNoClass(self):
251         self._handle_mail('''Content-Type: text/plain;
252   charset="iso-8859-1"
253 From: Chef <chef@bork.bork.bork>
254 To: issue_tracker@your.tracker.email.domain.example
255 Cc: richard@test.test
256 Message-Id: <dummy_test_message_id>
257 Subject: Testing...
258
259 This is a test submission of a new issue.
260 ''')
261         assert not os.path.exists(SENDMAILDEBUG)
262
263     def testNewIssueAuthMsg(self):
264         # TODO: fix the damn config - this is apalling
265         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
266         self._handle_mail('''Content-Type: text/plain;
267   charset="iso-8859-1"
268 From: Chef <chef@bork.bork.bork>
269 To: issue_tracker@your.tracker.email.domain.example
270 Message-Id: <dummy_test_message_id>
271 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
272
273 This is a test submission of a new issue.
274 ''')
275         self.compareMessages(self._get_mail(),
276 '''FROM: roundup-admin@your.tracker.email.domain.example
277 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
278 Content-Type: text/plain; charset="utf-8"
279 Subject: [issue1] Testing...
280 To: chef@bork.bork.bork, mary@test.test, richard@test.test
281 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
282 Reply-To: Roundup issue tracker
283  <issue_tracker@your.tracker.email.domain.example>
284 MIME-Version: 1.0
285 Message-Id: <dummy_test_message_id>
286 X-Roundup-Name: Roundup issue tracker
287 X-Roundup-Loop: hello
288 X-Roundup-Issue-Status: unread
289 Content-Transfer-Encoding: quoted-printable
290
291
292 New submission from Bork, Chef <chef@bork.bork.bork>:
293
294 This is a test submission of a new issue.
295
296 ----------
297 assignedto: richard
298 messages: 1
299 nosy: Chef, mary, richard
300 status: unread
301 title: Testing...
302
303 _______________________________________________________________________
304 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
305 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
306 _______________________________________________________________________
307 ''')
308
309     def testNewIssueNoAuthorInfo(self):
310         self.db.config.MAIL_ADD_AUTHORINFO = 'no'
311         self._handle_mail('''Content-Type: text/plain;
312   charset="iso-8859-1"
313 From: Chef <chef@bork.bork.bork>
314 To: issue_tracker@your.tracker.email.domain.example
315 Message-Id: <dummy_test_message_id>
316 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
317
318 This is a test submission of a new issue.
319 ''')
320         self.compareMessages(self._get_mail(),
321 '''FROM: roundup-admin@your.tracker.email.domain.example
322 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
323 Content-Type: text/plain; charset="utf-8"
324 Subject: [issue1] Testing...
325 To: mary@test.test, richard@test.test
326 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
327 Reply-To: Roundup issue tracker
328  <issue_tracker@your.tracker.email.domain.example>
329 MIME-Version: 1.0
330 Message-Id: <dummy_test_message_id>
331 X-Roundup-Name: Roundup issue tracker
332 X-Roundup-Loop: hello
333 X-Roundup-Issue-Status: unread
334 Content-Transfer-Encoding: quoted-printable
335
336 This is a test submission of a new issue.
337
338 ----------
339 assignedto: richard
340 messages: 1
341 nosy: Chef, mary, richard
342 status: unread
343 title: Testing...
344
345 _______________________________________________________________________
346 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
347 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
348 _______________________________________________________________________
349 ''')
350
351     def testNewIssueNoAuthorEmail(self):
352         self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
353         self._handle_mail('''Content-Type: text/plain;
354   charset="iso-8859-1"
355 From: Chef <chef@bork.bork.bork>
356 To: issue_tracker@your.tracker.email.domain.example
357 Message-Id: <dummy_test_message_id>
358 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
359
360 This is a test submission of a new issue.
361 ''')
362         self.compareMessages(self._get_mail(),
363 '''FROM: roundup-admin@your.tracker.email.domain.example
364 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
365 Content-Type: text/plain; charset="utf-8"
366 Subject: [issue1] Testing...
367 To: mary@test.test, richard@test.test
368 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
369 Reply-To: Roundup issue tracker
370  <issue_tracker@your.tracker.email.domain.example>
371 MIME-Version: 1.0
372 Message-Id: <dummy_test_message_id>
373 X-Roundup-Name: Roundup issue tracker
374 X-Roundup-Loop: hello
375 X-Roundup-Issue-Status: unread
376 Content-Transfer-Encoding: quoted-printable
377
378 New submission from Bork, Chef:
379
380 This is a test submission of a new issue.
381
382 ----------
383 assignedto: richard
384 messages: 1
385 nosy: Chef, mary, richard
386 status: unread
387 title: Testing...
388
389 _______________________________________________________________________
390 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
391 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
392 _______________________________________________________________________
393 ''')
394
395     multipart_msg = '''From: mary <mary@test.test>
396 To: issue_tracker@your.tracker.email.domain.example
397 Message-Id: <followup_dummy_id>
398 In-Reply-To: <dummy_test_message_id>
399 Subject: [issue1] Testing...
400 Content-Type: multipart/mixed; boundary="bxyzzy"
401 Content-Disposition: inline
402
403
404 --bxyzzy
405 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
406 Content-Disposition: inline
407
408 --bCsyhTFzCvuiizWE
409 Content-Type: text/plain; charset=us-ascii
410 Content-Disposition: inline
411
412 test attachment first text/plain
413
414 --bCsyhTFzCvuiizWE
415 Content-Type: application/octet-stream
416 Content-Disposition: attachment; filename="first.dvi"
417 Content-Transfer-Encoding: base64
418
419 SnVzdCBhIHRlc3QgAQo=
420
421 --bCsyhTFzCvuiizWE
422 Content-Type: text/plain; charset=us-ascii
423 Content-Disposition: inline
424
425 test attachment second text/plain
426
427 --bCsyhTFzCvuiizWE
428 Content-Type: text/html
429 Content-Disposition: inline
430
431 <html>
432 to be ignored.
433 </html>
434
435 --bCsyhTFzCvuiizWE--
436
437 --bxyzzy
438 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
439 Content-Disposition: inline
440
441 --bCsyhTFzCvuiizWF
442 Content-Type: text/plain; charset=us-ascii
443 Content-Disposition: inline
444
445 test attachment third text/plain
446
447 --bCsyhTFzCvuiizWF
448 Content-Type: application/octet-stream
449 Content-Disposition: attachment; filename="second.dvi"
450 Content-Transfer-Encoding: base64
451
452 SnVzdCBhIHRlc3QK
453
454 --bCsyhTFzCvuiizWF--
455
456 --bxyzzy--
457 '''
458
459     def testMultipartKeepAlternatives(self):
460         self.doNewIssue()
461         self._handle_mail(self.multipart_msg)
462         messages = self.db.issue.get('1', 'messages')
463         messages.sort()
464         msg = self.db.msg.getnode (messages[-1])
465         assert(len(msg.files) == 5)
466         names = {0 : 'first.dvi', 4 : 'second.dvi'}
467         content = {3 : 'test attachment third text/plain\n',
468                    4 : 'Just a test\n'}
469         for n, id in enumerate (msg.files):
470             f = self.db.file.getnode (id)
471             self.assertEqual(f.name, names.get (n, 'unnamed'))
472             if n in content :
473                 self.assertEqual(f.content, content [n])
474         self.assertEqual(msg.content, 'test attachment second text/plain')
475
476     def testMultipartDropAlternatives(self):
477         self.doNewIssue()
478         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
479         self._handle_mail(self.multipart_msg)
480         messages = self.db.issue.get('1', 'messages')
481         messages.sort()
482         msg = self.db.msg.getnode (messages[-1])
483         assert(len(msg.files) == 2)
484         names = {1 : 'second.dvi'}
485         content = {0 : 'test attachment third text/plain\n',
486                    1 : 'Just a test\n'}
487         for n, id in enumerate (msg.files):
488             f = self.db.file.getnode (id)
489             self.assertEqual(f.name, names.get (n, 'unnamed'))
490             if n in content :
491                 self.assertEqual(f.content, content [n])
492         self.assertEqual(msg.content, 'test attachment second text/plain')
493
494     def testSimpleFollowup(self):
495         self.doNewIssue()
496         self._handle_mail('''Content-Type: text/plain;
497   charset="iso-8859-1"
498 From: mary <mary@test.test>
499 To: issue_tracker@your.tracker.email.domain.example
500 Message-Id: <followup_dummy_id>
501 In-Reply-To: <dummy_test_message_id>
502 Subject: [issue1] Testing...
503
504 This is a second followup
505 ''')
506         self.compareMessages(self._get_mail(),
507 '''FROM: roundup-admin@your.tracker.email.domain.example
508 TO: chef@bork.bork.bork, richard@test.test
509 Content-Type: text/plain; charset="utf-8"
510 Subject: [issue1] Testing...
511 To: chef@bork.bork.bork, richard@test.test
512 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
513 Reply-To: Roundup issue tracker
514  <issue_tracker@your.tracker.email.domain.example>
515 MIME-Version: 1.0
516 Message-Id: <followup_dummy_id>
517 In-Reply-To: <dummy_test_message_id>
518 X-Roundup-Name: Roundup issue tracker
519 X-Roundup-Loop: hello
520 X-Roundup-Issue-Status: chatting
521 Content-Transfer-Encoding: quoted-printable
522
523
524 Contrary, Mary <mary@test.test> added the comment:
525
526 This is a second followup
527
528 ----------
529 status: unread -> chatting
530
531 _______________________________________________________________________
532 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
533 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
534 _______________________________________________________________________
535 ''')
536
537     def testFollowup(self):
538         self.doNewIssue()
539
540         self._handle_mail('''Content-Type: text/plain;
541   charset="iso-8859-1"
542 From: richard <richard@test.test>
543 To: issue_tracker@your.tracker.email.domain.example
544 Message-Id: <followup_dummy_id>
545 In-Reply-To: <dummy_test_message_id>
546 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
547
548 This is a followup
549 ''')
550         l = self.db.issue.get('1', 'nosy')
551         l.sort()
552         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
553             self.john_id])
554
555         self.compareMessages(self._get_mail(),
556 '''FROM: roundup-admin@your.tracker.email.domain.example
557 TO: chef@bork.bork.bork, john@test.test, mary@test.test
558 Content-Type: text/plain; charset="utf-8"
559 Subject: [issue1] Testing...
560 To: chef@bork.bork.bork, john@test.test, mary@test.test
561 From: richard <issue_tracker@your.tracker.email.domain.example>
562 Reply-To: Roundup issue tracker
563  <issue_tracker@your.tracker.email.domain.example>
564 MIME-Version: 1.0
565 Message-Id: <followup_dummy_id>
566 In-Reply-To: <dummy_test_message_id>
567 X-Roundup-Name: Roundup issue tracker
568 X-Roundup-Loop: hello
569 X-Roundup-Issue-Status: chatting
570 Content-Transfer-Encoding: quoted-printable
571
572
573 richard <richard@test.test> added the comment:
574
575 This is a followup
576
577 ----------
578 assignedto:  -> mary
579 nosy: +john, mary
580 status: unread -> chatting
581
582 _______________________________________________________________________
583 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
584 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
585 _______________________________________________________________________
586 ''')
587
588     def testNosyGeneration(self):
589         self.db.issue.create(title='test')
590
591         # create a nosy message
592         msg = self.db.msg.create(content='This is a test',
593             author=self.richard_id, messageid='<dummy_test_message_id>')
594         self.db.journaltag = 'richard'
595         l = self.db.issue.create(title='test', messages=[msg],
596             nosy=[self.chef_id, self.mary_id, self.john_id])
597
598         self.compareMessages(self._get_mail(),
599 '''FROM: roundup-admin@your.tracker.email.domain.example
600 TO: chef@bork.bork.bork, john@test.test, mary@test.test
601 Content-Type: text/plain; charset="utf-8"
602 Subject: [issue2] test
603 To: chef@bork.bork.bork, john@test.test, mary@test.test
604 From: richard <issue_tracker@your.tracker.email.domain.example>
605 Reply-To: Roundup issue tracker
606  <issue_tracker@your.tracker.email.domain.example>
607 MIME-Version: 1.0
608 Message-Id: <dummy_test_message_id>
609 X-Roundup-Name: Roundup issue tracker
610 X-Roundup-Loop: hello
611 X-Roundup-Issue-Status: unread
612 Content-Transfer-Encoding: quoted-printable
613
614
615 New submission from richard <richard@test.test>:
616
617 This is a test
618
619 ----------
620 messages: 1
621 nosy: Chef, john, mary, richard
622 status: unread
623 title: test
624
625 _______________________________________________________________________
626 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
627 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
628 _______________________________________________________________________
629 ''')
630
631     def testPropertyChangeOnly(self):
632         self.doNewIssue()
633         oldvalues = self.db.getnode('issue', '1').copy()
634         oldvalues['assignedto'] = None
635         # reconstruct old behaviour: This would reuse the
636         # database-handle from the doNewIssue above which has committed
637         # as user "Chef". So we close and reopen the db as that user.
638         #self.db.close() actually don't close 'cos this empties memorydb
639         self.db = self.instance.open('Chef')
640         self.db.issue.set('1', assignedto=self.chef_id)
641         self.db.commit()
642         self.db.issue.nosymessage('1', None, oldvalues)
643
644         new_mail = ""
645         for line in self._get_mail().split("\n"):
646             if "Message-Id: " in line:
647                 continue
648             if "Date: " in line:
649                 continue
650             new_mail += line+"\n"
651
652         self.compareMessages(new_mail, """
653 FROM: roundup-admin@your.tracker.email.domain.example
654 TO: chef@bork.bork.bork, richard@test.test
655 Content-Type: text/plain; charset="utf-8"
656 Subject: [issue1] Testing...
657 To: chef@bork.bork.bork, richard@test.test
658 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
659 X-Roundup-Name: Roundup issue tracker
660 X-Roundup-Loop: hello
661 X-Roundup-Issue-Status: unread
662 X-Roundup-Version: 1.3.3
663 In-Reply-To: <dummy_test_message_id>
664 MIME-Version: 1.0
665 Reply-To: Roundup issue tracker
666  <issue_tracker@your.tracker.email.domain.example>
667 Content-Transfer-Encoding: quoted-printable
668
669
670 Change by Bork, Chef <chef@bork.bork.bork>:
671
672
673 ----------
674 assignedto:  -> Chef
675
676 _______________________________________________________________________
677 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
678 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
679 _______________________________________________________________________
680 """)
681
682
683     #
684     # FOLLOWUP TITLE MATCH
685     #
686     def testFollowupTitleMatch(self):
687         self.doNewIssue()
688         self._handle_mail('''Content-Type: text/plain;
689   charset="iso-8859-1"
690 From: richard <richard@test.test>
691 To: issue_tracker@your.tracker.email.domain.example
692 Message-Id: <followup_dummy_id>
693 Subject: Re: Testing... [assignedto=mary; nosy=+john]
694
695 This is a followup
696 ''')
697         self.compareMessages(self._get_mail(),
698 '''FROM: roundup-admin@your.tracker.email.domain.example
699 TO: chef@bork.bork.bork, john@test.test, mary@test.test
700 Content-Type: text/plain; charset="utf-8"
701 Subject: [issue1] Testing...
702 To: chef@bork.bork.bork, john@test.test, mary@test.test
703 From: richard <issue_tracker@your.tracker.email.domain.example>
704 Reply-To: Roundup issue tracker
705  <issue_tracker@your.tracker.email.domain.example>
706 MIME-Version: 1.0
707 Message-Id: <followup_dummy_id>
708 In-Reply-To: <dummy_test_message_id>
709 X-Roundup-Name: Roundup issue tracker
710 X-Roundup-Loop: hello
711 X-Roundup-Issue-Status: chatting
712 Content-Transfer-Encoding: quoted-printable
713
714
715 richard <richard@test.test> added the comment:
716
717 This is a followup
718
719 ----------
720 assignedto:  -> mary
721 nosy: +john, mary
722 status: unread -> chatting
723
724 _______________________________________________________________________
725 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
726 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
727 _______________________________________________________________________
728 ''')
729
730     def testFollowupTitleMatchMultiRe(self):
731         nodeid1 = self.doNewIssue()
732         nodeid2 = self._handle_mail('''Content-Type: text/plain;
733   charset="iso-8859-1"
734 From: richard <richard@test.test>
735 To: issue_tracker@your.tracker.email.domain.example
736 Message-Id: <followup_dummy_id>
737 Subject: Re: Testing... [assignedto=mary; nosy=+john]
738
739 This is a followup
740 ''')
741
742         nodeid3 = self._handle_mail('''Content-Type: text/plain;
743   charset="iso-8859-1"
744 From: richard <richard@test.test>
745 To: issue_tracker@your.tracker.email.domain.example
746 Message-Id: <followup2_dummy_id>
747 Subject: Ang: Re: Testing...
748
749 This is a followup
750 ''')
751         self.assertEqual(nodeid1, nodeid2)
752         self.assertEqual(nodeid1, nodeid3)
753
754     def testFollowupTitleMatchNever(self):
755         nodeid = self.doNewIssue()
756         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
757         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
758   charset="iso-8859-1"
759 From: richard <richard@test.test>
760 To: issue_tracker@your.tracker.email.domain.example
761 Message-Id: <followup_dummy_id>
762 Subject: Re: Testing...
763
764 This is a followup
765 '''), nodeid)
766
767     def testFollowupTitleMatchNeverInterval(self):
768         nodeid = self.doNewIssue()
769         # force failure of the interval
770         time.sleep(2)
771         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
772         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
773   charset="iso-8859-1"
774 From: richard <richard@test.test>
775 To: issue_tracker@your.tracker.email.domain.example
776 Message-Id: <followup_dummy_id>
777 Subject: Re: Testing...
778
779 This is a followup
780 '''), nodeid)
781
782
783     def testFollowupTitleMatchInterval(self):
784         nodeid = self.doNewIssue()
785         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
786         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
787   charset="iso-8859-1"
788 From: richard <richard@test.test>
789 To: issue_tracker@your.tracker.email.domain.example
790 Message-Id: <followup_dummy_id>
791 Subject: Re: Testing...
792
793 This is a followup
794 '''), nodeid)
795
796
797     def testFollowupNosyAuthor(self):
798         self.doNewIssue()
799         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
800         self._handle_mail('''Content-Type: text/plain;
801   charset="iso-8859-1"
802 From: john@test.test
803 To: issue_tracker@your.tracker.email.domain.example
804 Message-Id: <followup_dummy_id>
805 In-Reply-To: <dummy_test_message_id>
806 Subject: [issue1] Testing...
807
808 This is a followup
809 ''')
810
811         self.compareMessages(self._get_mail(),
812 '''FROM: roundup-admin@your.tracker.email.domain.example
813 TO: chef@bork.bork.bork, richard@test.test
814 Content-Type: text/plain; charset="utf-8"
815 Subject: [issue1] Testing...
816 To: chef@bork.bork.bork, richard@test.test
817 From: John Doe <issue_tracker@your.tracker.email.domain.example>
818 Reply-To: Roundup issue tracker
819  <issue_tracker@your.tracker.email.domain.example>
820 MIME-Version: 1.0
821 Message-Id: <followup_dummy_id>
822 In-Reply-To: <dummy_test_message_id>
823 X-Roundup-Name: Roundup issue tracker
824 X-Roundup-Loop: hello
825 X-Roundup-Issue-Status: chatting
826 Content-Transfer-Encoding: quoted-printable
827
828
829 John Doe <john@test.test> added the comment:
830
831 This is a followup
832
833 ----------
834 nosy: +john
835 status: unread -> chatting
836
837 _______________________________________________________________________
838 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
839 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
840 _______________________________________________________________________
841
842 ''')
843
844     def testFollowupNosyRecipients(self):
845         self.doNewIssue()
846         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
847         self._handle_mail('''Content-Type: text/plain;
848   charset="iso-8859-1"
849 From: richard@test.test
850 To: issue_tracker@your.tracker.email.domain.example
851 Cc: john@test.test
852 Message-Id: <followup_dummy_id>
853 In-Reply-To: <dummy_test_message_id>
854 Subject: [issue1] Testing...
855
856 This is a followup
857 ''')
858         self.compareMessages(self._get_mail(),
859 '''FROM: roundup-admin@your.tracker.email.domain.example
860 TO: chef@bork.bork.bork
861 Content-Type: text/plain; charset="utf-8"
862 Subject: [issue1] Testing...
863 To: chef@bork.bork.bork
864 From: richard <issue_tracker@your.tracker.email.domain.example>
865 Reply-To: Roundup issue tracker
866  <issue_tracker@your.tracker.email.domain.example>
867 MIME-Version: 1.0
868 Message-Id: <followup_dummy_id>
869 In-Reply-To: <dummy_test_message_id>
870 X-Roundup-Name: Roundup issue tracker
871 X-Roundup-Loop: hello
872 X-Roundup-Issue-Status: chatting
873 Content-Transfer-Encoding: quoted-printable
874
875
876 richard <richard@test.test> added the comment:
877
878 This is a followup
879
880 ----------
881 nosy: +john
882 status: unread -> chatting
883
884 _______________________________________________________________________
885 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
886 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
887 _______________________________________________________________________
888
889 ''')
890
891     def testFollowupNosyAuthorAndCopy(self):
892         self.doNewIssue()
893         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
894         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
895         self._handle_mail('''Content-Type: text/plain;
896   charset="iso-8859-1"
897 From: john@test.test
898 To: issue_tracker@your.tracker.email.domain.example
899 Message-Id: <followup_dummy_id>
900 In-Reply-To: <dummy_test_message_id>
901 Subject: [issue1] Testing...
902
903 This is a followup
904 ''')
905         self.compareMessages(self._get_mail(),
906 '''FROM: roundup-admin@your.tracker.email.domain.example
907 TO: chef@bork.bork.bork, john@test.test, richard@test.test
908 Content-Type: text/plain; charset="utf-8"
909 Subject: [issue1] Testing...
910 To: chef@bork.bork.bork, john@test.test, richard@test.test
911 From: John Doe <issue_tracker@your.tracker.email.domain.example>
912 Reply-To: Roundup issue tracker
913  <issue_tracker@your.tracker.email.domain.example>
914 MIME-Version: 1.0
915 Message-Id: <followup_dummy_id>
916 In-Reply-To: <dummy_test_message_id>
917 X-Roundup-Name: Roundup issue tracker
918 X-Roundup-Loop: hello
919 X-Roundup-Issue-Status: chatting
920 Content-Transfer-Encoding: quoted-printable
921
922
923 John Doe <john@test.test> added the comment:
924
925 This is a followup
926
927 ----------
928 nosy: +john
929 status: unread -> chatting
930
931 _______________________________________________________________________
932 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
933 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
934 _______________________________________________________________________
935
936 ''')
937
938     def testFollowupNoNosyAuthor(self):
939         self.doNewIssue()
940         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
941         self._handle_mail('''Content-Type: text/plain;
942   charset="iso-8859-1"
943 From: john@test.test
944 To: issue_tracker@your.tracker.email.domain.example
945 Message-Id: <followup_dummy_id>
946 In-Reply-To: <dummy_test_message_id>
947 Subject: [issue1] Testing...
948
949 This is a followup
950 ''')
951         self.compareMessages(self._get_mail(),
952 '''FROM: roundup-admin@your.tracker.email.domain.example
953 TO: chef@bork.bork.bork, richard@test.test
954 Content-Type: text/plain; charset="utf-8"
955 Subject: [issue1] Testing...
956 To: chef@bork.bork.bork, richard@test.test
957 From: John Doe <issue_tracker@your.tracker.email.domain.example>
958 Reply-To: Roundup issue tracker
959  <issue_tracker@your.tracker.email.domain.example>
960 MIME-Version: 1.0
961 Message-Id: <followup_dummy_id>
962 In-Reply-To: <dummy_test_message_id>
963 X-Roundup-Name: Roundup issue tracker
964 X-Roundup-Loop: hello
965 X-Roundup-Issue-Status: chatting
966 Content-Transfer-Encoding: quoted-printable
967
968
969 John Doe <john@test.test> added the comment:
970
971 This is a followup
972
973 ----------
974 status: unread -> chatting
975
976 _______________________________________________________________________
977 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
978 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
979 _______________________________________________________________________
980
981 ''')
982
983     def testFollowupNoNosyRecipients(self):
984         self.doNewIssue()
985         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
986         self._handle_mail('''Content-Type: text/plain;
987   charset="iso-8859-1"
988 From: richard@test.test
989 To: issue_tracker@your.tracker.email.domain.example
990 Cc: john@test.test
991 Message-Id: <followup_dummy_id>
992 In-Reply-To: <dummy_test_message_id>
993 Subject: [issue1] Testing...
994
995 This is a followup
996 ''')
997         self.compareMessages(self._get_mail(),
998 '''FROM: roundup-admin@your.tracker.email.domain.example
999 TO: chef@bork.bork.bork
1000 Content-Type: text/plain; charset="utf-8"
1001 Subject: [issue1] Testing...
1002 To: chef@bork.bork.bork
1003 From: richard <issue_tracker@your.tracker.email.domain.example>
1004 Reply-To: Roundup issue tracker
1005  <issue_tracker@your.tracker.email.domain.example>
1006 MIME-Version: 1.0
1007 Message-Id: <followup_dummy_id>
1008 In-Reply-To: <dummy_test_message_id>
1009 X-Roundup-Name: Roundup issue tracker
1010 X-Roundup-Loop: hello
1011 X-Roundup-Issue-Status: chatting
1012 Content-Transfer-Encoding: quoted-printable
1013
1014
1015 richard <richard@test.test> added the comment:
1016
1017 This is a followup
1018
1019 ----------
1020 status: unread -> chatting
1021
1022 _______________________________________________________________________
1023 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1024 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1025 _______________________________________________________________________
1026
1027 ''')
1028
1029     def testFollowupEmptyMessage(self):
1030         self.doNewIssue()
1031
1032         self._handle_mail('''Content-Type: text/plain;
1033   charset="iso-8859-1"
1034 From: richard <richard@test.test>
1035 To: issue_tracker@your.tracker.email.domain.example
1036 Message-Id: <followup_dummy_id>
1037 In-Reply-To: <dummy_test_message_id>
1038 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1039
1040 ''')
1041         l = self.db.issue.get('1', 'nosy')
1042         l.sort()
1043         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1044             self.john_id])
1045
1046         # should be no file created (ie. no message)
1047         assert not os.path.exists(SENDMAILDEBUG)
1048
1049     def testFollowupEmptyMessageNoSubject(self):
1050         self.doNewIssue()
1051
1052         self._handle_mail('''Content-Type: text/plain;
1053   charset="iso-8859-1"
1054 From: richard <richard@test.test>
1055 To: issue_tracker@your.tracker.email.domain.example
1056 Message-Id: <followup_dummy_id>
1057 In-Reply-To: <dummy_test_message_id>
1058 Subject: [issue1] [assignedto=mary; nosy=+john]
1059
1060 ''')
1061         l = self.db.issue.get('1', 'nosy')
1062         l.sort()
1063         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1064             self.john_id])
1065
1066         # should be no file created (ie. no message)
1067         assert not os.path.exists(SENDMAILDEBUG)
1068
1069     def testNosyRemove(self):
1070         self.doNewIssue()
1071
1072         self._handle_mail('''Content-Type: text/plain;
1073   charset="iso-8859-1"
1074 From: richard <richard@test.test>
1075 To: issue_tracker@your.tracker.email.domain.example
1076 Message-Id: <followup_dummy_id>
1077 In-Reply-To: <dummy_test_message_id>
1078 Subject: [issue1] Testing... [nosy=-richard]
1079
1080 ''')
1081         l = self.db.issue.get('1', 'nosy')
1082         l.sort()
1083         self.assertEqual(l, [self.chef_id])
1084
1085         # NO NOSY MESSAGE SHOULD BE SENT!
1086         assert not os.path.exists(SENDMAILDEBUG)
1087
1088     def testNewUserAuthor(self):
1089         self.db.commit()
1090         l = self.db.user.list()
1091         l.sort()
1092         message = '''Content-Type: text/plain;
1093   charset="iso-8859-1"
1094 From: fubar <fubar@bork.bork.bork>
1095 To: issue_tracker@your.tracker.email.domain.example
1096 Message-Id: <dummy_test_message_id>
1097 Subject: [issue] Testing...
1098
1099 This is a test submission of a new issue.
1100 '''
1101         self.db.security.role['anonymous'].permissions=[]
1102         anonid = self.db.user.lookup('anonymous')
1103         self.db.user.set(anonid, roles='Anonymous')
1104         try:
1105             self._handle_mail(message)
1106         except Unauthorized, value:
1107             body_diff = self.compareMessages(str(value), """
1108 You are not a registered user.
1109
1110 Unknown address: fubar@bork.bork.bork
1111 """)
1112             assert not body_diff, body_diff
1113         else:
1114             raise AssertionError, "Unathorized not raised when handling mail"
1115
1116         # Add Web Access role to anonymous, and try again to make sure
1117         # we get a "please register at:" message this time.
1118         p = [
1119             self.db.security.getPermission('Register', 'user'),
1120             self.db.security.getPermission('Web Access', None),
1121         ]
1122         self.db.security.role['anonymous'].permissions=p
1123         try:
1124             self._handle_mail(message)
1125         except Unauthorized, value:
1126             body_diff = self.compareMessages(str(value), """
1127 You are not a registered user. Please register at:
1128
1129 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1130
1131 ...before sending mail to the tracker.
1132
1133 Unknown address: fubar@bork.bork.bork
1134 """)
1135             assert not body_diff, body_diff
1136         else:
1137             raise AssertionError, "Unathorized not raised when handling mail"
1138
1139         # Make sure list of users is the same as before.
1140         m = self.db.user.list()
1141         m.sort()
1142         self.assertEqual(l, m)
1143
1144         # now with the permission
1145         p = [
1146             self.db.security.getPermission('Register', 'user'),
1147             self.db.security.getPermission('Email Access', None),
1148         ]
1149         self.db.security.role['anonymous'].permissions=p
1150         self._handle_mail(message)
1151         m = self.db.user.list()
1152         m.sort()
1153         self.assertNotEqual(l, m)
1154
1155     def testNewUserAuthorEncodedName(self):
1156         l = set(self.db.user.list())
1157         # From: name has Euro symbol in it
1158         message = '''Content-Type: text/plain;
1159   charset="iso-8859-1"
1160 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1161 To: issue_tracker@your.tracker.email.domain.example
1162 Message-Id: <dummy_test_message_id>
1163 Subject: [issue] Testing...
1164
1165 This is a test submission of a new issue.
1166 '''
1167         p = [
1168             self.db.security.getPermission('Register', 'user'),
1169             self.db.security.getPermission('Email Access', None),
1170             self.db.security.getPermission('Create', 'issue'),
1171             self.db.security.getPermission('Create', 'msg'),
1172         ]
1173         self.db.security.role['anonymous'].permissions = p
1174         self._handle_mail(message)
1175         m = set(self.db.user.list())
1176         new = list(m - l)[0]
1177         name = self.db.user.get(new, 'realname')
1178         self.assertEquals(name, 'H€llo')
1179
1180     def testUnknownUser(self):
1181         l = set(self.db.user.list())
1182         message = '''Content-Type: text/plain;
1183   charset="iso-8859-1"
1184 From: Nonexisting User <nonexisting@bork.bork.bork>
1185 To: issue_tracker@your.tracker.email.domain.example
1186 Message-Id: <dummy_test_message_id>
1187 Subject: [issue] Testing nonexisting user...
1188
1189 This is a test submission of a new issue.
1190 '''
1191         handler = self._create_mailgw(message)
1192         # we want a bounce message:
1193         handler.trapExceptions = 1
1194         ret = handler.main(StringIO(message))
1195         self.compareMessages(self._get_mail(),
1196 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1197 TO: nonexisting@bork.bork.bork
1198 From nobody Tue Jul 14 12:04:11 2009
1199 Content-Type: multipart/mixed; boundary="===============0639262320=="
1200 MIME-Version: 1.0
1201 Subject: Failed issue tracker submission
1202 To: nonexisting@bork.bork.bork
1203 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1204 Date: Tue, 14 Jul 2009 12:04:11 +0000
1205 Precedence: bulk
1206 X-Roundup-Name: Roundup issue tracker
1207 X-Roundup-Loop: hello
1208 X-Roundup-Version: 1.4.8
1209 MIME-Version: 1.0
1210
1211 --===============0639262320==
1212 Content-Type: text/plain; charset="us-ascii"
1213 MIME-Version: 1.0
1214 Content-Transfer-Encoding: 7bit
1215
1216
1217
1218 You are not a registered user. Please register at:
1219
1220 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1221
1222 ...before sending mail to the tracker.
1223
1224 Unknown address: nonexisting@bork.bork.bork
1225
1226 --===============0639262320==
1227 Content-Type: text/plain; charset="us-ascii"
1228 MIME-Version: 1.0
1229 Content-Transfer-Encoding: 7bit
1230
1231 Content-Type: text/plain;
1232   charset="iso-8859-1"
1233 From: Nonexisting User <nonexisting@bork.bork.bork>
1234 To: issue_tracker@your.tracker.email.domain.example
1235 Message-Id: <dummy_test_message_id>
1236 Subject: [issue] Testing nonexisting user...
1237
1238 This is a test submission of a new issue.
1239
1240 --===============0639262320==--
1241 ''')
1242
1243     def testEnc01(self):
1244         self.db.user.set(self.mary_id,
1245             realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1246             ('latin-1').encode('utf-8'))
1247         self.doNewIssue()
1248         self._handle_mail('''Content-Type: text/plain;
1249   charset="iso-8859-1"
1250 From: mary <mary@test.test>
1251 To: issue_tracker@your.tracker.email.domain.example
1252 Message-Id: <followup_dummy_id>
1253 In-Reply-To: <dummy_test_message_id>
1254 Subject: [issue1] Testing...
1255 Content-Type: text/plain;
1256         charset="iso-8859-1"
1257 Content-Transfer-Encoding: quoted-printable
1258
1259 A message with encoding (encoded oe =F6)
1260
1261 ''')
1262         self.compareMessages(self._get_mail(),
1263 '''FROM: roundup-admin@your.tracker.email.domain.example
1264 TO: chef@bork.bork.bork, richard@test.test
1265 Content-Type: text/plain; charset="utf-8"
1266 Subject: [issue1] Testing...
1267 To: chef@bork.bork.bork, richard@test.test
1268 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1269  <issue_tracker@your.tracker.email.domain.example>
1270 Reply-To: Roundup issue tracker
1271  <issue_tracker@your.tracker.email.domain.example>
1272 MIME-Version: 1.0
1273 Message-Id: <followup_dummy_id>
1274 In-Reply-To: <dummy_test_message_id>
1275 X-Roundup-Name: Roundup issue tracker
1276 X-Roundup-Loop: hello
1277 X-Roundup-Issue-Status: chatting
1278 Content-Transfer-Encoding: quoted-printable
1279
1280
1281 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1282  comment:
1283
1284 A message with encoding (encoded oe =C3=B6)
1285
1286 ----------
1287 status: unread -> chatting
1288
1289 _______________________________________________________________________
1290 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1291 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1292 _______________________________________________________________________
1293 ''')
1294
1295     def testEncNonUTF8(self):
1296         self.doNewIssue()
1297         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1298         self._handle_mail('''Content-Type: text/plain;
1299   charset="iso-8859-1"
1300 From: mary <mary@test.test>
1301 To: issue_tracker@your.tracker.email.domain.example
1302 Message-Id: <followup_dummy_id>
1303 In-Reply-To: <dummy_test_message_id>
1304 Subject: [issue1] Testing...
1305 Content-Type: text/plain;
1306         charset="iso-8859-1"
1307 Content-Transfer-Encoding: quoted-printable
1308
1309 A message with encoding (encoded oe =F6)
1310
1311 ''')
1312         self.compareMessages(self._get_mail(),
1313 '''FROM: roundup-admin@your.tracker.email.domain.example
1314 TO: chef@bork.bork.bork, richard@test.test
1315 Content-Type: text/plain; charset="iso-8859-1"
1316 Subject: [issue1] Testing...
1317 To: chef@bork.bork.bork, richard@test.test
1318 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1319 Reply-To: Roundup issue tracker
1320  <issue_tracker@your.tracker.email.domain.example>
1321 MIME-Version: 1.0
1322 Message-Id: <followup_dummy_id>
1323 In-Reply-To: <dummy_test_message_id>
1324 X-Roundup-Name: Roundup issue tracker
1325 X-Roundup-Loop: hello
1326 X-Roundup-Issue-Status: chatting
1327 Content-Transfer-Encoding: quoted-printable
1328
1329
1330 Contrary, Mary <mary@test.test> added the comment:
1331
1332 A message with encoding (encoded oe =F6)
1333
1334 ----------
1335 status: unread -> chatting
1336
1337 _______________________________________________________________________
1338 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1339 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1340 _______________________________________________________________________
1341 ''')
1342
1343
1344     def testMultipartEnc01(self):
1345         self.doNewIssue()
1346         self._handle_mail('''Content-Type: text/plain;
1347   charset="iso-8859-1"
1348 From: mary <mary@test.test>
1349 To: issue_tracker@your.tracker.email.domain.example
1350 Message-Id: <followup_dummy_id>
1351 In-Reply-To: <dummy_test_message_id>
1352 Subject: [issue1] Testing...
1353 Content-Type: multipart/mixed;
1354         boundary="----_=_NextPart_000_01"
1355
1356 This message is in MIME format. Since your mail reader does not understand
1357 this format, some or all of this message may not be legible.
1358
1359 ------_=_NextPart_000_01
1360 Content-Type: text/plain;
1361         charset="iso-8859-1"
1362 Content-Transfer-Encoding: quoted-printable
1363
1364 A message with first part encoded (encoded oe =F6)
1365
1366 ''')
1367         self.compareMessages(self._get_mail(),
1368 '''FROM: roundup-admin@your.tracker.email.domain.example
1369 TO: chef@bork.bork.bork, richard@test.test
1370 Content-Type: text/plain; charset="utf-8"
1371 Subject: [issue1] Testing...
1372 To: chef@bork.bork.bork, richard@test.test
1373 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1374 Reply-To: Roundup issue tracker
1375  <issue_tracker@your.tracker.email.domain.example>
1376 MIME-Version: 1.0
1377 Message-Id: <followup_dummy_id>
1378 In-Reply-To: <dummy_test_message_id>
1379 X-Roundup-Name: Roundup issue tracker
1380 X-Roundup-Loop: hello
1381 X-Roundup-Issue-Status: chatting
1382 Content-Transfer-Encoding: quoted-printable
1383
1384
1385 Contrary, Mary <mary@test.test> added the comment:
1386
1387 A message with first part encoded (encoded oe =C3=B6)
1388
1389 ----------
1390 status: unread -> chatting
1391
1392 _______________________________________________________________________
1393 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1394 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1395 _______________________________________________________________________
1396 ''')
1397
1398     def testContentDisposition(self):
1399         self.doNewIssue()
1400         self._handle_mail('''Content-Type: text/plain;
1401   charset="iso-8859-1"
1402 From: mary <mary@test.test>
1403 To: issue_tracker@your.tracker.email.domain.example
1404 Message-Id: <followup_dummy_id>
1405 In-Reply-To: <dummy_test_message_id>
1406 Subject: [issue1] Testing...
1407 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
1408 Content-Disposition: inline
1409
1410
1411 --bCsyhTFzCvuiizWE
1412 Content-Type: text/plain; charset=us-ascii
1413 Content-Disposition: inline
1414
1415 test attachment binary
1416
1417 --bCsyhTFzCvuiizWE
1418 Content-Type: application/octet-stream
1419 Content-Disposition: attachment; filename="main.dvi"
1420 Content-Transfer-Encoding: base64
1421
1422 SnVzdCBhIHRlc3QgAQo=
1423
1424 --bCsyhTFzCvuiizWE--
1425 ''')
1426         messages = self.db.issue.get('1', 'messages')
1427         messages.sort()
1428         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
1429         self.assertEqual(file.name, 'main.dvi')
1430         self.assertEqual(file.content, 'Just a test \001\n')
1431
1432     def testFollowupStupidQuoting(self):
1433         self.doNewIssue()
1434
1435         self._handle_mail('''Content-Type: text/plain;
1436   charset="iso-8859-1"
1437 From: richard <richard@test.test>
1438 To: issue_tracker@your.tracker.email.domain.example
1439 Message-Id: <followup_dummy_id>
1440 In-Reply-To: <dummy_test_message_id>
1441 Subject: Re: "[issue1] Testing... "
1442
1443 This is a followup
1444 ''')
1445         self.compareMessages(self._get_mail(),
1446 '''FROM: roundup-admin@your.tracker.email.domain.example
1447 TO: chef@bork.bork.bork
1448 Content-Type: text/plain; charset="utf-8"
1449 Subject: [issue1] Testing...
1450 To: chef@bork.bork.bork
1451 From: richard <issue_tracker@your.tracker.email.domain.example>
1452 Reply-To: Roundup issue tracker
1453  <issue_tracker@your.tracker.email.domain.example>
1454 MIME-Version: 1.0
1455 Message-Id: <followup_dummy_id>
1456 In-Reply-To: <dummy_test_message_id>
1457 X-Roundup-Name: Roundup issue tracker
1458 X-Roundup-Loop: hello
1459 X-Roundup-Issue-Status: chatting
1460 Content-Transfer-Encoding: quoted-printable
1461
1462
1463 richard <richard@test.test> added the comment:
1464
1465 This is a followup
1466
1467 ----------
1468 status: unread -> chatting
1469
1470 _______________________________________________________________________
1471 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1472 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1473 _______________________________________________________________________
1474 ''')
1475
1476     def testEmailQuoting(self):
1477         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
1478         self.innerTestQuoting('''This is a followup
1479 ''')
1480
1481     def testEmailQuotingRemove(self):
1482         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
1483         self.innerTestQuoting('''Blah blah wrote:
1484 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1485 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1486 >
1487
1488 This is a followup
1489 ''')
1490
1491     def innerTestQuoting(self, expect):
1492         nodeid = self.doNewIssue()
1493
1494         messages = self.db.issue.get(nodeid, 'messages')
1495
1496         self._handle_mail('''Content-Type: text/plain;
1497   charset="iso-8859-1"
1498 From: richard <richard@test.test>
1499 To: issue_tracker@your.tracker.email.domain.example
1500 Message-Id: <followup_dummy_id>
1501 In-Reply-To: <dummy_test_message_id>
1502 Subject: Re: [issue1] Testing...
1503
1504 Blah blah wrote:
1505 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
1506 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
1507 >
1508
1509 This is a followup
1510 ''')
1511         # figure the new message id
1512         newmessages = self.db.issue.get(nodeid, 'messages')
1513         for msg in messages:
1514             newmessages.remove(msg)
1515         messageid = newmessages[0]
1516
1517         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
1518
1519     def testUserLookup(self):
1520         i = self.db.user.create(username='user1', address='user1@foo.com')
1521         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
1522         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
1523         i = self.db.user.create(username='user2', address='USER2@foo.com')
1524         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
1525         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
1526
1527     def testUserAlternateLookup(self):
1528         i = self.db.user.create(username='user1', address='user1@foo.com',
1529                                 alternate_addresses='user1@bar.com')
1530         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
1531         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
1532
1533     def testUserCreate(self):
1534         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
1535         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
1536
1537     def testRFC2822(self):
1538         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
1539         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
1540         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
1541         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
1542         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
1543
1544     def testRegistrationConfirmation(self):
1545         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
1546         self.db.getOTKManager().set(otk, username='johannes')
1547         self._handle_mail('''Content-Type: text/plain;
1548   charset="iso-8859-1"
1549 From: Chef <chef@bork.bork.bork>
1550 To: issue_tracker@your.tracker.email.domain.example
1551 Cc: richard@test.test
1552 Message-Id: <dummy_test_message_id>
1553 Subject: Re: Complete your registration to Roundup issue tracker
1554  -- key %s
1555
1556 This is a test confirmation of registration.
1557 ''' % otk)
1558         self.db.user.lookup('johannes')
1559
1560     def testFollowupOnNonIssue(self):
1561         self.db.keyword.create(name='Foo')
1562         self._handle_mail('''Content-Type: text/plain;
1563   charset="iso-8859-1"
1564 From: richard <richard@test.test>
1565 To: issue_tracker@your.tracker.email.domain.example
1566 Message-Id: <followup_dummy_id>
1567 In-Reply-To: <dummy_test_message_id>
1568 Subject: [keyword1] Testing... [name=Bar]
1569
1570 ''')
1571         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1572
1573     def testResentFrom(self):
1574         nodeid = self._handle_mail('''Content-Type: text/plain;
1575   charset="iso-8859-1"
1576 From: Chef <chef@bork.bork.bork>
1577 Resent-From: mary <mary@test.test>
1578 To: issue_tracker@your.tracker.email.domain.example
1579 Cc: richard@test.test
1580 Message-Id: <dummy_test_message_id>
1581 Subject: [issue] Testing...
1582
1583 This is a test submission of a new issue.
1584 ''')
1585         assert not os.path.exists(SENDMAILDEBUG)
1586         l = self.db.issue.get(nodeid, 'nosy')
1587         l.sort()
1588         self.assertEqual(l, [self.richard_id, self.mary_id])
1589         return nodeid
1590
1591     def testDejaVu(self):
1592         self.assertRaises(IgnoreLoop, self._handle_mail,
1593             '''Content-Type: text/plain;
1594   charset="iso-8859-1"
1595 From: Chef <chef@bork.bork.bork>
1596 X-Roundup-Loop: hello
1597 To: issue_tracker@your.tracker.email.domain.example
1598 Cc: richard@test.test
1599 Message-Id: <dummy_test_message_id>
1600 Subject: Re: [issue] Testing...
1601
1602 Hi, I've been mis-configured to loop messages back to myself.
1603 ''')
1604
1605     def testItsBulkStupid(self):
1606         self.assertRaises(IgnoreBulk, self._handle_mail,
1607             '''Content-Type: text/plain;
1608   charset="iso-8859-1"
1609 From: Chef <chef@bork.bork.bork>
1610 Precedence: bulk
1611 To: issue_tracker@your.tracker.email.domain.example
1612 Cc: richard@test.test
1613 Message-Id: <dummy_test_message_id>
1614 Subject: Re: [issue] Testing...
1615
1616 Hi, I'm on holidays, and this is a dumb auto-responder.
1617 ''')
1618
1619     def testAutoReplyEmailsAreIgnored(self):
1620         self.assertRaises(IgnoreBulk, self._handle_mail,
1621             '''Content-Type: text/plain;
1622   charset="iso-8859-1"
1623 From: Chef <chef@bork.bork.bork>
1624 To: issue_tracker@your.tracker.email.domain.example
1625 Cc: richard@test.test
1626 Message-Id: <dummy_test_message_id>
1627 Subject: Re: [issue] Out of office AutoReply: Back next week
1628
1629 Hi, I am back in the office next week
1630 ''')
1631
1632     def testNoSubject(self):
1633         self.assertRaises(MailUsageError, self._handle_mail,
1634             '''Content-Type: text/plain;
1635   charset="iso-8859-1"
1636 From: Chef <chef@bork.bork.bork>
1637 To: issue_tracker@your.tracker.email.domain.example
1638 Cc: richard@test.test
1639 Reply-To: chef@bork.bork.bork
1640 Message-Id: <dummy_test_message_id>
1641
1642 ''')
1643
1644     #
1645     # TEST FOR INVALID DESIGNATOR HANDLING
1646     #
1647     def testInvalidDesignator(self):
1648         self.assertRaises(MailUsageError, self._handle_mail,
1649             '''Content-Type: text/plain;
1650   charset="iso-8859-1"
1651 From: Chef <chef@bork.bork.bork>
1652 To: issue_tracker@your.tracker.email.domain.example
1653 Subject: [frobulated] testing
1654 Cc: richard@test.test
1655 Reply-To: chef@bork.bork.bork
1656 Message-Id: <dummy_test_message_id>
1657
1658 ''')
1659         self.assertRaises(MailUsageError, self._handle_mail,
1660             '''Content-Type: text/plain;
1661   charset="iso-8859-1"
1662 From: Chef <chef@bork.bork.bork>
1663 To: issue_tracker@your.tracker.email.domain.example
1664 Subject: [issue12345] testing
1665 Cc: richard@test.test
1666 Reply-To: chef@bork.bork.bork
1667 Message-Id: <dummy_test_message_id>
1668
1669 ''')
1670
1671     def testInvalidClassLoose(self):
1672         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1673         nodeid = self._handle_mail('''Content-Type: text/plain;
1674   charset="iso-8859-1"
1675 From: Chef <chef@bork.bork.bork>
1676 To: issue_tracker@your.tracker.email.domain.example
1677 Subject: [frobulated] testing
1678 Cc: richard@test.test
1679 Reply-To: chef@bork.bork.bork
1680 Message-Id: <dummy_test_message_id>
1681
1682 ''')
1683         assert not os.path.exists(SENDMAILDEBUG)
1684         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1685             '[frobulated] testing')
1686
1687     def testInvalidClassLooseReply(self):
1688         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1689         nodeid = self._handle_mail('''Content-Type: text/plain;
1690   charset="iso-8859-1"
1691 From: Chef <chef@bork.bork.bork>
1692 To: issue_tracker@your.tracker.email.domain.example
1693 Subject: Re: [frobulated] testing
1694 Cc: richard@test.test
1695 Reply-To: chef@bork.bork.bork
1696 Message-Id: <dummy_test_message_id>
1697
1698 ''')
1699         assert not os.path.exists(SENDMAILDEBUG)
1700         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1701             '[frobulated] testing')
1702
1703     def testInvalidClassLoose(self):
1704         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1705         nodeid = self._handle_mail('''Content-Type: text/plain;
1706   charset="iso-8859-1"
1707 From: Chef <chef@bork.bork.bork>
1708 To: issue_tracker@your.tracker.email.domain.example
1709 Subject: [issue1234] testing
1710 Cc: richard@test.test
1711 Reply-To: chef@bork.bork.bork
1712 Message-Id: <dummy_test_message_id>
1713
1714 ''')
1715         assert not os.path.exists(SENDMAILDEBUG)
1716         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1717             '[issue1234] testing')
1718
1719     def testClassLooseOK(self):
1720         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1721         self.db.keyword.create(name='Foo')
1722         nodeid = self._handle_mail('''Content-Type: text/plain;
1723   charset="iso-8859-1"
1724 From: Chef <chef@bork.bork.bork>
1725 To: issue_tracker@your.tracker.email.domain.example
1726 Subject: [keyword1] Testing... [name=Bar]
1727 Cc: richard@test.test
1728 Reply-To: chef@bork.bork.bork
1729 Message-Id: <dummy_test_message_id>
1730
1731 ''')
1732         assert not os.path.exists(SENDMAILDEBUG)
1733         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1734
1735     def testClassStrictInvalid(self):
1736         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1737         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1738
1739         message = '''Content-Type: text/plain;
1740   charset="iso-8859-1"
1741 From: Chef <chef@bork.bork.bork>
1742 To: issue_tracker@your.tracker.email.domain.example
1743 Subject: Testing...
1744 Cc: richard@test.test
1745 Reply-To: chef@bork.bork.bork
1746 Message-Id: <dummy_test_message_id>
1747
1748 '''
1749         self.assertRaises(MailUsageError, self._handle_mail, message)
1750
1751     def testClassStrictValid(self):
1752         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
1753         self.instance.config.MAILGW_DEFAULT_CLASS = ''
1754
1755         nodeid = self._handle_mail('''Content-Type: text/plain;
1756   charset="iso-8859-1"
1757 From: Chef <chef@bork.bork.bork>
1758 To: issue_tracker@your.tracker.email.domain.example
1759 Subject: [issue] Testing...
1760 Cc: richard@test.test
1761 Reply-To: chef@bork.bork.bork
1762 Message-Id: <dummy_test_message_id>
1763
1764 ''')
1765
1766         assert not os.path.exists(SENDMAILDEBUG)
1767         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
1768
1769     #
1770     # TEST FOR INVALID COMMANDS HANDLING
1771     #
1772     def testInvalidCommands(self):
1773         self.assertRaises(MailUsageError, self._handle_mail,
1774             '''Content-Type: text/plain;
1775   charset="iso-8859-1"
1776 From: Chef <chef@bork.bork.bork>
1777 To: issue_tracker@your.tracker.email.domain.example
1778 Subject: testing [frobulated]
1779 Cc: richard@test.test
1780 Reply-To: chef@bork.bork.bork
1781 Message-Id: <dummy_test_message_id>
1782
1783 ''')
1784
1785     def testInvalidCommandPassthrough(self):
1786         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
1787         nodeid = self._handle_mail('''Content-Type: text/plain;
1788   charset="iso-8859-1"
1789 From: Chef <chef@bork.bork.bork>
1790 To: issue_tracker@your.tracker.email.domain.example
1791 Subject: testing [frobulated]
1792 Cc: richard@test.test
1793 Reply-To: chef@bork.bork.bork
1794 Message-Id: <dummy_test_message_id>
1795
1796 ''')
1797         assert not os.path.exists(SENDMAILDEBUG)
1798         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1799             'testing [frobulated]')
1800
1801     def testInvalidCommandPassthroughLoose(self):
1802         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1803         nodeid = self._handle_mail('''Content-Type: text/plain;
1804   charset="iso-8859-1"
1805 From: Chef <chef@bork.bork.bork>
1806 To: issue_tracker@your.tracker.email.domain.example
1807 Subject: testing [frobulated]
1808 Cc: richard@test.test
1809 Reply-To: chef@bork.bork.bork
1810 Message-Id: <dummy_test_message_id>
1811
1812 ''')
1813         assert not os.path.exists(SENDMAILDEBUG)
1814         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1815             'testing [frobulated]')
1816
1817     def testInvalidCommandPassthroughLooseOK(self):
1818         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
1819         nodeid = self._handle_mail('''Content-Type: text/plain;
1820   charset="iso-8859-1"
1821 From: Chef <chef@bork.bork.bork>
1822 To: issue_tracker@your.tracker.email.domain.example
1823 Subject: testing [assignedto=mary]
1824 Cc: richard@test.test
1825 Reply-To: chef@bork.bork.bork
1826 Message-Id: <dummy_test_message_id>
1827
1828 ''')
1829         assert not os.path.exists(SENDMAILDEBUG)
1830         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1831         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1832
1833     def testCommandDelimiters(self):
1834         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1835         nodeid = self._handle_mail('''Content-Type: text/plain;
1836   charset="iso-8859-1"
1837 From: Chef <chef@bork.bork.bork>
1838 To: issue_tracker@your.tracker.email.domain.example
1839 Subject: testing {assignedto=mary}
1840 Cc: richard@test.test
1841 Reply-To: chef@bork.bork.bork
1842 Message-Id: <dummy_test_message_id>
1843
1844 ''')
1845         assert not os.path.exists(SENDMAILDEBUG)
1846         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
1847         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
1848
1849     def testPrefixDelimiters(self):
1850         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1851         self.db.keyword.create(name='Foo')
1852         self._handle_mail('''Content-Type: text/plain;
1853   charset="iso-8859-1"
1854 From: richard <richard@test.test>
1855 To: issue_tracker@your.tracker.email.domain.example
1856 Message-Id: <followup_dummy_id>
1857 In-Reply-To: <dummy_test_message_id>
1858 Subject: {keyword1} Testing... {name=Bar}
1859
1860 ''')
1861         assert not os.path.exists(SENDMAILDEBUG)
1862         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1863
1864     def testCommandDelimitersIgnore(self):
1865         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
1866         nodeid = self._handle_mail('''Content-Type: text/plain;
1867   charset="iso-8859-1"
1868 From: Chef <chef@bork.bork.bork>
1869 To: issue_tracker@your.tracker.email.domain.example
1870 Subject: testing [assignedto=mary]
1871 Cc: richard@test.test
1872 Reply-To: chef@bork.bork.bork
1873 Message-Id: <dummy_test_message_id>
1874
1875 ''')
1876         assert not os.path.exists(SENDMAILDEBUG)
1877         self.assertEqual(self.db.issue.get(nodeid, 'title'),
1878             'testing [assignedto=mary]')
1879         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
1880
1881     def testReplytoMatch(self):
1882         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
1883         nodeid = self.doNewIssue()
1884         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1885   charset="iso-8859-1"
1886 From: Chef <chef@bork.bork.bork>
1887 To: issue_tracker@your.tracker.email.domain.example
1888 Message-Id: <dummy_test_message_id2>
1889 In-Reply-To: <dummy_test_message_id>
1890 Subject: Testing...
1891
1892 Followup message.
1893 ''')
1894
1895         nodeid3 = self._handle_mail('''Content-Type: text/plain;
1896   charset="iso-8859-1"
1897 From: Chef <chef@bork.bork.bork>
1898 To: issue_tracker@your.tracker.email.domain.example
1899 Message-Id: <dummy_test_message_id3>
1900 In-Reply-To: <dummy_test_message_id2>
1901 Subject: Testing...
1902
1903 Yet another message in the same thread/issue.
1904 ''')
1905
1906         self.assertEqual(nodeid, nodeid2)
1907         self.assertEqual(nodeid, nodeid3)
1908
1909     def testHelpSubject(self):
1910         message = '''Content-Type: text/plain;
1911   charset="iso-8859-1"
1912 From: Chef <chef@bork.bork.bork>
1913 To: issue_tracker@your.tracker.email.domain.example
1914 Message-Id: <dummy_test_message_id2>
1915 In-Reply-To: <dummy_test_message_id>
1916 Subject: hElp
1917
1918
1919 '''
1920         self.assertRaises(MailUsageHelp, self._handle_mail, message)
1921
1922     def testMaillistSubject(self):
1923         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
1924         self.db.keyword.create(name='Foo')
1925         self._handle_mail('''Content-Type: text/plain;
1926   charset="iso-8859-1"
1927 From: Chef <chef@bork.bork.bork>
1928 To: issue_tracker@your.tracker.email.domain.example
1929 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
1930 Cc: richard@test.test
1931 Reply-To: chef@bork.bork.bork
1932 Message-Id: <dummy_test_message_id>
1933
1934 ''')
1935
1936         assert not os.path.exists(SENDMAILDEBUG)
1937         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1938
1939     def testUnknownPrefixSubject(self):
1940         self.db.keyword.create(name='Foo')
1941         self._handle_mail('''Content-Type: text/plain;
1942   charset="iso-8859-1"
1943 From: Chef <chef@bork.bork.bork>
1944 To: issue_tracker@your.tracker.email.domain.example
1945 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
1946 Cc: richard@test.test
1947 Reply-To: chef@bork.bork.bork
1948 Message-Id: <dummy_test_message_id>
1949
1950 ''')
1951
1952         assert not os.path.exists(SENDMAILDEBUG)
1953         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
1954
1955     def testOneCharSubject(self):
1956         message = '''Content-Type: text/plain;
1957   charset="iso-8859-1"
1958 From: Chef <chef@bork.bork.bork>
1959 To: issue_tracker@your.tracker.email.domain.example
1960 Subject: b
1961 Cc: richard@test.test
1962 Reply-To: chef@bork.bork.bork
1963 Message-Id: <dummy_test_message_id>
1964
1965 '''
1966         try:
1967             self._handle_mail(message)
1968         except MailUsageError:
1969             self.fail('MailUsageError raised')
1970
1971     def testIssueidLast(self):
1972         nodeid1 = self.doNewIssue()
1973         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1974   charset="iso-8859-1"
1975 From: mary <mary@test.test>
1976 To: issue_tracker@your.tracker.email.domain.example
1977 Message-Id: <followup_dummy_id>
1978 In-Reply-To: <dummy_test_message_id>
1979 Subject: New title [issue1]
1980
1981 This is a second followup
1982 ''')
1983
1984         assert nodeid1 == nodeid2
1985         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
1986
1987     def testSecurityMessagePermissionContent(self):
1988         id = self.doNewIssue()
1989         issue = self.db.issue.getnode (id)
1990         self.db.security.addRole(name='Nomsg')
1991         self.db.security.addPermissionToRole('Nomsg', 'Email Access')
1992         for cl in 'issue', 'file', 'keyword':
1993             for p in 'View', 'Edit', 'Create':
1994                 self.db.security.addPermissionToRole('Nomsg', p, cl)
1995         self.db.user.set(self.mary_id, roles='Nomsg')
1996         nodeid = self._handle_mail('''Content-Type: text/plain;
1997   charset="iso-8859-1"
1998 From: Chef <chef@bork.bork.bork>
1999 To: issue_tracker@your.tracker.email.domain.example
2000 Message-Id: <dummy_test_message_id_2>
2001 Subject: [issue%(id)s] Testing... [nosy=+mary]
2002
2003 Just a test reply
2004 '''%locals())
2005         assert os.path.exists(SENDMAILDEBUG)
2006         self.compareMessages(self._get_mail(),
2007 '''FROM: roundup-admin@your.tracker.email.domain.example
2008 TO: chef@bork.bork.bork, richard@test.test
2009 Content-Type: text/plain; charset="utf-8"
2010 Subject: [issue1] Testing...
2011 To: richard@test.test
2012 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2013 Reply-To: Roundup issue tracker
2014  <issue_tracker@your.tracker.email.domain.example>
2015 MIME-Version: 1.0
2016 Message-Id: <dummy_test_message_id_2>
2017 In-Reply-To: <dummy_test_message_id>
2018 X-Roundup-Name: Roundup issue tracker
2019 X-Roundup-Loop: hello
2020 X-Roundup-Issue-Status: chatting
2021 Content-Transfer-Encoding: quoted-printable
2022
2023
2024 Bork, Chef <chef@bork.bork.bork> added the comment:
2025
2026 Just a test reply
2027
2028 ----------
2029 nosy: +mary
2030 status: unread -> chatting
2031
2032 _______________________________________________________________________
2033 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2034 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2035 _______________________________________________________________________
2036 ''')
2037
2038     def testOutlookAttachment(self):
2039         message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2040 Content-class: urn:content-classes:message
2041 MIME-Version: 1.0
2042 Content-Type: multipart/mixed;
2043         boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2044 Subject: Example of a failed outlook attachment e-mail
2045 Date: Tue, 23 Mar 2010 01:43:44 -0700
2046 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2047 X-MS-Has-Attach: yes
2048 X-MS-TNEF-Correlator: 
2049 Thread-Topic: Example of a failed outlook attachment e-mail
2050 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2051 From: "Hugh" <richard@test.test>
2052 To: <richard@test.test>
2053 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2054
2055 This is a multi-part message in MIME format.
2056
2057 ------_=_NextPart_001_01CACA65.40A51CBC
2058 Content-Type: multipart/alternative;
2059         boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2060
2061
2062 ------_=_NextPart_002_01CACA65.40A51CBC
2063 Content-Type: text/plain;
2064         charset="us-ascii"
2065 Content-Transfer-Encoding: quoted-printable
2066
2067
2068 Hi Richard,
2069
2070 I suppose this isn't the exact message that was sent but is a resend of
2071 one of my trial messages that failed.  For your benefit I changed the
2072 subject line and am adding these words to the message body.  Should
2073 still be as problematic, but if you like I can resend an exact copy of a
2074 failed message changing nothing except putting your address instead of
2075 our tracker.
2076
2077 Thanks very much for taking time to look into this.  Much appreciated.
2078
2079  <<battery backup>>=20
2080
2081 ------_=_NextPart_002_01CACA65.40A51CBC
2082 Content-Type: text/html;
2083         charset="us-ascii"
2084 Content-Transfer-Encoding: quoted-printable
2085
2086 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2087 <HTML>
2088 <HEAD>
2089 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2090 charset=3Dus-ascii">
2091 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2092 6.5.7654.12">
2093 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2094 </HEAD>
2095 <BODY>
2096 <!-- Converted from text/rtf format -->
2097 <BR>
2098
2099 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2100 </P>
2101
2102 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2103 that was sent but is a resend of one of my trial messages that =
2104 failed.&nbsp; For your benefit I changed the subject line and am adding =
2105 these words to the message body.&nbsp; Should still be as problematic, =
2106 but if you like I can resend an exact copy of a failed message changing =
2107 nothing except putting your address instead of our tracker.</FONT></P>
2108
2109 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2110 look into this.&nbsp; Much appreciated.</FONT>
2111 </P>
2112 <BR>
2113
2114 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> &lt;&lt;battery =
2115 backup&gt;&gt; </FONT>
2116 </P>
2117
2118 </BODY>
2119 </HTML>
2120 ------_=_NextPart_002_01CACA65.40A51CBC--
2121
2122 ------_=_NextPart_001_01CACA65.40A51CBC
2123 Content-Type: message/rfc822
2124 Content-Transfer-Encoding: 7bit
2125
2126 X-MimeOLE: Produced By Microsoft Exchange V6.5
2127 MIME-Version: 1.0
2128 Content-Type: multipart/alternative;
2129         boundary="----_=_NextPart_003_01CAC15A.29717800"
2130 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2131 Content-class: urn:content-classes:message
2132 Subject: battery backup
2133 Date: Thu, 11 Mar 2010 13:33:43 -0700
2134 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2135 X-MS-Has-Attach: 
2136 X-MS-TNEF-Correlator: 
2137 Thread-Topic: battery backup
2138 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2139 From: "Jerry" <jerry@test.test>
2140 To: "Hugh" <hugh@test.test>
2141
2142 This is a multi-part message in MIME format.
2143
2144 ------_=_NextPart_003_01CAC15A.29717800
2145 Content-Type: text/plain;
2146         charset="iso-8859-1"
2147 Content-Transfer-Encoding: quoted-printable
2148
2149 Dear Hugh,
2150         A car batter has an energy capacity of ~ 500Wh.  A UPS=20
2151 battery is worse than this.
2152
2153 if we need to provied 100kW for 30 minutes that will take 100 car=20
2154 batteries.  This seems like an awful lot of batteries.
2155
2156 Of course I like your idea of making the time 1 minute, so we get to=20
2157 a more modest number of batteries
2158
2159 Jerry
2160
2161
2162 ------_=_NextPart_003_01CAC15A.29717800
2163 Content-Type: text/html;
2164         charset="iso-8859-1"
2165 Content-Transfer-Encoding: quoted-printable
2166
2167 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2168 <HTML>
2169 <HEAD>
2170 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2171 charset=3Diso-8859-1">
2172 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2173 6.5.7654.12">
2174 <TITLE>battery backup</TITLE>
2175 </HEAD>
2176 <BODY>
2177 <!-- Converted from text/plain format -->
2178
2179 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2180
2181 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT SIZE=3D2>A car =
2182 batter has an energy capacity of ~ 500Wh.&nbsp; A UPS </FONT>
2183
2184 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2185 </P>
2186
2187 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2188 take 100 car </FONT>
2189
2190 <BR><FONT SIZE=3D2>batteries.&nbsp; This seems like an awful lot of =
2191 batteries.</FONT>
2192 </P>
2193
2194 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2195 minute, so we get to </FONT>
2196
2197 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2198 </P>
2199
2200 <P><FONT SIZE=3D2>Jerry</FONT>
2201 </P>
2202
2203 </BODY>
2204 </HTML>
2205 ------_=_NextPart_003_01CAC15A.29717800--
2206
2207 ------_=_NextPart_001_01CACA65.40A51CBC--
2208 '''
2209         nodeid = self._handle_mail(message)
2210         assert not os.path.exists(SENDMAILDEBUG)
2211         msgid = self.db.issue.get(nodeid, 'messages')[0]
2212         self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2213         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2214         fileid = self.db.msg.get(msgid, 'files')[0]
2215         self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2216         fileid = self.db.msg.get(msgid, 'files')[1]
2217         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2218
2219     def testForwardedMessageAttachment(self):
2220         message = '''Return-Path: <rgg@test.test>
2221 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2222 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2223 Message-ID: <4BC4F9C7.50409@test.test>
2224 Date: Wed, 14 Apr 2010 09:09:59 +1000
2225 From: Rupert Goldie <rgg@test.test>
2226 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2227 MIME-Version: 1.0
2228 To: ekit issues <issues@test.test>
2229 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2230 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2231
2232 This is a multi-part message in MIME format.
2233 --------------000807090608060304010403
2234 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2235 Content-Transfer-Encoding: 7bit
2236
2237 Catch this exception and log it without emailing.
2238
2239 --------------000807090608060304010403
2240 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2241 Content-Transfer-Encoding: 7bit
2242 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2243
2244 Return-Path: <ektravj@test.test>
2245 X-Sieve: CMU Sieve 2.2
2246 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2247 X-Virus-Scanned: by amavisd-new at ekit.com
2248 To: facebook-errors@test.test
2249 From: ektravj@test.test
2250 Subject: PHP ERROR (fb)
2251 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2252 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2253
2254 [13-Apr-2010 22:49:02] PHP Fatal error:  Uncaught exception 'Exception' with message 'Facebook Error Message: Feed action request limit reached' in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php:280
2255 Stack trace:
2256 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2257 #1 {main}
2258  thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2259
2260
2261 --------------000807090608060304010403--
2262 '''
2263         nodeid = self._handle_mail(message)
2264         assert not os.path.exists(SENDMAILDEBUG)
2265         msgid = self.db.issue.get(nodeid, 'messages')[0]
2266         self.assertEqual(self.db.msg.get(msgid, 'content'),
2267             'Catch this exception and log it without emailing.')
2268         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2269         fileid = self.db.msg.get(msgid, 'files')[0]
2270         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2271
2272 def test_suite():
2273     suite = unittest.TestSuite()
2274     suite.addTest(unittest.makeSuite(MailgwTestCase))
2275     return suite
2276
2277 if __name__ == '__main__':
2278     runner = unittest.TextTestRunner()
2279     unittest.main(testRunner=runner)
2280
2281 # vim: set filetype=python sts=4 sw=4 et si :
2282
2283
2284
2285