Code

Fix PGP implementation -- the pyme API has changed significantly since
[roundup.git] / test / test_mailgw.py
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 $
14 # TODO: test bcc
16 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time
19 try:
20     import pyme, pyme.core
21 except ImportError:
22     pyme = None
25 from cStringIO import StringIO
27 if not os.environ.has_key('SENDMAILDEBUG'):
28     os.environ['SENDMAILDEBUG'] = 'mail-test.log'
29 SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
31 from roundup import mailgw, i18n, roundupdb
32 from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \
33     parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp
34 from roundup import init, instance, password, rfc2822, __version__
35 from roundup.anypy.sets_ import set
37 #import db_test_base
38 import memorydb
40 class Message(rfc822.Message):
41     """String-based Message class with equivalence test."""
42     def __init__(self, s):
43         rfc822.Message.__init__(self, StringIO(s.strip()))
45     def __eq__(self, other):
46         return (self.dict == other.dict and
47                 self.fp.read() == other.fp.read())
49 class Tracker(object):
50     def open(self, journaltag):
51         return self.db
53 class DiffHelper:
54     def compareMessages(self, new, old):
55         """Compare messages for semantic equivalence."""
56         new, old = Message(new), Message(old)
58         # all Roundup-generated messages have "Precedence: bulk"
59         old['Precedence'] = 'bulk'
61         # don't try to compare the date
62         del new['date'], old['date']
64         if not new == old:
65             res = []
67             replace = {}
68             for key in new.keys():
69                 if key.startswith('from '):
70                     # skip the unix from line
71                     continue
72                 if key.lower() == 'x-roundup-version':
73                     # version changes constantly, so handle it specially
74                     if new[key] != __version__:
75                         res.append('  %s: %r != %r' % (key, __version__,
76                             new[key]))
77                 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
78                     # handle mime messages
79                     newmime = new[key].split('=',1)[-1].strip('"')
80                     oldmime = old.get(key, '').split('=',1)[-1].strip('"')
81                     replace ['--' + newmime] = '--' + oldmime
82                     replace ['--' + newmime + '--'] = '--' + oldmime + '--'
83                 elif new.get(key, '') != old.get(key, ''):
84                     res.append('  %s: %r != %r' % (key, old.get(key, ''),
85                         new.get(key, '')))
87             body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
88                 replace=replace)
89             if body_diff:
90                 res.append('')
91                 res.extend(body_diff)
93             if res:
94                 res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):')
95                 raise AssertionError, '\n'.join(res)
97     def compareStrings(self, s2, s1, replace={}):
98         '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
99            the first to be the "original" but in the calls in this file,
100            the second arg is the original. Ho hum.
101            Do replacements over the replace dict -- used for mime boundary
102         '''
103         l1 = s1.strip().split('\n')
104         l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
105         if l1 == l2:
106             return
107         s = difflib.SequenceMatcher(None, l1, l2)
108         res = []
109         for value, s1s, s1e, s2s, s2e in s.get_opcodes():
110             if value == 'equal':
111                 for i in range(s1s, s1e):
112                     res.append('  %s'%l1[i])
113             elif value == 'delete':
114                 for i in range(s1s, s1e):
115                     res.append('- %s'%l1[i])
116             elif value == 'insert':
117                 for i in range(s2s, s2e):
118                     res.append('+ %s'%l2[i])
119             elif value == 'replace':
120                 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
121                     res.append('- %s'%l1[i])
122                     res.append('+ %s'%l2[j])
124         return res
126 class MailgwTestAbstractBase(unittest.TestCase, DiffHelper):
127     count = 0
128     schema = 'classic'
129     def setUp(self):
130         self.old_translate_ = mailgw._
131         roundupdb._ = mailgw._ = i18n.get_translation(language='C').gettext
132         self.__class__.count = self.__class__.count + 1
134         # and open the database / "instance"
135         self.db = memorydb.create('admin')
136         self.instance = Tracker()
137         self.instance.db = self.db
138         self.instance.config = self.db.config
139         self.instance.MailGW = MailGW
141         self.chef_id = self.db.user.create(username='Chef',
142             address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
143         self.richard_id = self.db.user.create(username='richard',
144             address='richard@test.test', roles='User')
145         self.mary_id = self.db.user.create(username='mary',
146             address='mary@test.test', roles='User', realname='Contrary, Mary')
147         self.john_id = self.db.user.create(username='john',
148             address='john@test.test', roles='User', realname='John Doe',
149             alternate_addresses='jondoe@test.test\njohn.doe@test.test')
150         self.rgg_id = self.db.user.create(username='rgg',
151             address='rgg@test.test', roles='User')
153     def tearDown(self):
154         roundupdb._ = mailgw._ = self.old_translate_
155         if os.path.exists(SENDMAILDEBUG):
156             os.remove(SENDMAILDEBUG)
157         self.db.close()
159     def _create_mailgw(self, message, args=()):
160         class MailGW(self.instance.MailGW):
161             def handle_message(self, message):
162                 return self._handle_message(message)
163         handler = MailGW(self.instance, args)
164         handler.db = self.db
165         return handler
167     def _handle_mail(self, message, args=()):
168         handler = self._create_mailgw(message, args)
169         handler.trapExceptions = 0
170         return handler.main(StringIO(message))
172     def _get_mail(self):
173         f = open(SENDMAILDEBUG)
174         try:
175             return f.read()
176         finally:
177             f.close()
179     # Normal test-case used for both non-pgp test and a test while pgp
180     # is enabled, so this test is run in both test suites.
181     def testEmptyMessage(self):
182         nodeid = self._handle_mail('''Content-Type: text/plain;
183   charset="iso-8859-1"
184 From: Chef <chef@bork.bork.bork>
185 To: issue_tracker@your.tracker.email.domain.example
186 Cc: richard@test.test
187 Reply-To: chef@bork.bork.bork
188 Message-Id: <dummy_test_message_id>
189 Subject: [issue] Testing...
191 ''')
192         assert not os.path.exists(SENDMAILDEBUG)
193         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
196 class MailgwTestCase(MailgwTestAbstractBase):
198     def testMessageWithFromInIt(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 Reply-To: chef@bork.bork.bork
205 Message-Id: <dummy_test_message_id>
206 Subject: [issue] Testing...
208 From here to there!
209 ''')
210         assert not os.path.exists(SENDMAILDEBUG)
211         msgid = self.db.issue.get(nodeid, 'messages')[0]
212         self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!')
214     def testNoMessageId(self):
215         self.instance.config['MAIL_DOMAIN'] = 'example.com'
216         nodeid = self._handle_mail('''Content-Type: text/plain;
217   charset="iso-8859-1"
218 From: Chef <chef@bork.bork.bork>
219 To: issue_tracker@your.tracker.email.domain.example
220 Cc: richard@test.test
221 Reply-To: chef@bork.bork.bork
222 Subject: [issue] Testing...
224 Hi there!
225 ''')
226         assert not os.path.exists(SENDMAILDEBUG)
227         msgid = self.db.issue.get(nodeid, 'messages')[0]
228         messageid = self.db.msg.get(msgid, 'messageid')
229         x1, x2 = messageid.split('@')
230         self.assertEqual(x2, 'example.com>')
231         x = x1.split('.')[-1]
232         self.assertEqual(x, 'issueNone')
233         nodeid = self._handle_mail('''Content-Type: text/plain;
234   charset="iso-8859-1"
235 From: Chef <chef@bork.bork.bork>
236 To: issue_tracker@your.tracker.email.domain.example
237 Subject: [issue%(nodeid)s] Testing...
239 Just a test reply
240 '''%locals())
241         msgid = self.db.issue.get(nodeid, 'messages')[-1]
242         messageid = self.db.msg.get(msgid, 'messageid')
243         x1, x2 = messageid.split('@')
244         self.assertEqual(x2, 'example.com>')
245         x = x1.split('.')[-1]
246         self.assertEqual(x, "issue%s"%nodeid)
248     def testOptions(self):
249         nodeid = self._handle_mail('''Content-Type: text/plain;
250   charset="iso-8859-1"
251 From: Chef <chef@bork.bork.bork>
252 To: issue_tracker@your.tracker.email.domain.example
253 Message-Id: <dummy_test_message_id>
254 Reply-To: chef@bork.bork.bork
255 Subject: [issue] Testing...
257 Hi there!
258 ''', (('-C', 'issue'), ('-S', 'status=chatting;priority=critical')))
259         self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
260         self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
262     def testOptionsMulti(self):
263         nodeid = self._handle_mail('''Content-Type: text/plain;
264   charset="iso-8859-1"
265 From: Chef <chef@bork.bork.bork>
266 To: issue_tracker@your.tracker.email.domain.example
267 Message-Id: <dummy_test_message_id>
268 Reply-To: chef@bork.bork.bork
269 Subject: [issue] Testing...
271 Hi there!
272 ''', (('-C', 'issue'), ('-S', 'status=chatting'), ('-S', 'priority=critical')))
273         self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
274         self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
276     def testOptionClass(self):
277         nodeid = self._handle_mail('''Content-Type: text/plain;
278   charset="iso-8859-1"
279 From: Chef <chef@bork.bork.bork>
280 To: issue_tracker@your.tracker.email.domain.example
281 Message-Id: <dummy_test_message_id>
282 Reply-To: chef@bork.bork.bork
283 Subject: [issue] Testing... [status=chatting;priority=critical]
285 Hi there!
286 ''', (('-c', 'issue'),))
287         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
288         self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
289         self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
291     def doNewIssue(self):
292         nodeid = self._handle_mail('''Content-Type: text/plain;
293   charset="iso-8859-1"
294 From: Chef <chef@bork.bork.bork>
295 To: issue_tracker@your.tracker.email.domain.example
296 Cc: richard@test.test
297 Message-Id: <dummy_test_message_id>
298 Subject: [issue] Testing...
300 This is a test submission of a new issue.
301 ''')
302         assert not os.path.exists(SENDMAILDEBUG)
303         l = self.db.issue.get(nodeid, 'nosy')
304         l.sort()
305         self.assertEqual(l, [self.chef_id, self.richard_id])
306         return nodeid
308     def testNewIssue(self):
309         self.doNewIssue()
311     def testNewIssueNosy(self):
312         self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
313         nodeid = self._handle_mail('''Content-Type: text/plain;
314   charset="iso-8859-1"
315 From: Chef <chef@bork.bork.bork>
316 To: issue_tracker@your.tracker.email.domain.example
317 Cc: richard@test.test
318 Message-Id: <dummy_test_message_id>
319 Subject: [issue] Testing...
321 This is a test submission of a new issue.
322 ''')
323         assert not os.path.exists(SENDMAILDEBUG)
324         l = self.db.issue.get(nodeid, 'nosy')
325         l.sort()
326         self.assertEqual(l, [self.chef_id, self.richard_id])
328     def testAlternateAddress(self):
329         self._handle_mail('''Content-Type: text/plain;
330   charset="iso-8859-1"
331 From: John Doe <john.doe@test.test>
332 To: issue_tracker@your.tracker.email.domain.example
333 Message-Id: <dummy_test_message_id>
334 Subject: [issue] Testing...
336 This is a test submission of a new issue.
337 ''')
338         userlist = self.db.user.list()
339         assert not os.path.exists(SENDMAILDEBUG)
340         self.assertEqual(userlist, self.db.user.list(),
341             "user created when it shouldn't have been")
343     def testNewIssueNoClass(self):
344         self._handle_mail('''Content-Type: text/plain;
345   charset="iso-8859-1"
346 From: Chef <chef@bork.bork.bork>
347 To: issue_tracker@your.tracker.email.domain.example
348 Cc: richard@test.test
349 Message-Id: <dummy_test_message_id>
350 Subject: Testing...
352 This is a test submission of a new issue.
353 ''')
354         assert not os.path.exists(SENDMAILDEBUG)
356     def testNewIssueAuthMsg(self):
357         # TODO: fix the damn config - this is apalling
358         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
359         self._handle_mail('''Content-Type: text/plain;
360   charset="iso-8859-1"
361 From: Chef <chef@bork.bork.bork>
362 To: issue_tracker@your.tracker.email.domain.example
363 Message-Id: <dummy_test_message_id>
364 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
366 This is a test submission of a new issue.
367 ''')
368         self.compareMessages(self._get_mail(),
369 '''FROM: roundup-admin@your.tracker.email.domain.example
370 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
371 Content-Type: text/plain; charset="utf-8"
372 Subject: [issue1] Testing...
373 To: chef@bork.bork.bork, mary@test.test, richard@test.test
374 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
375 Reply-To: Roundup issue tracker
376  <issue_tracker@your.tracker.email.domain.example>
377 MIME-Version: 1.0
378 Message-Id: <dummy_test_message_id>
379 X-Roundup-Name: Roundup issue tracker
380 X-Roundup-Loop: hello
381 X-Roundup-Issue-Status: unread
382 Content-Transfer-Encoding: quoted-printable
385 New submission from Bork, Chef <chef@bork.bork.bork>:
387 This is a test submission of a new issue.
389 ----------
390 assignedto: richard
391 messages: 1
392 nosy: Chef, mary, richard
393 status: unread
394 title: Testing...
396 _______________________________________________________________________
397 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
398 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
399 _______________________________________________________________________
400 ''')
402     def testNewIssueNoAuthorInfo(self):
403         self.db.config.MAIL_ADD_AUTHORINFO = 'no'
404         self._handle_mail('''Content-Type: text/plain;
405   charset="iso-8859-1"
406 From: Chef <chef@bork.bork.bork>
407 To: issue_tracker@your.tracker.email.domain.example
408 Message-Id: <dummy_test_message_id>
409 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
411 This is a test submission of a new issue.
412 ''')
413         self.compareMessages(self._get_mail(),
414 '''FROM: roundup-admin@your.tracker.email.domain.example
415 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
416 Content-Type: text/plain; charset="utf-8"
417 Subject: [issue1] Testing...
418 To: mary@test.test, richard@test.test
419 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
420 Reply-To: Roundup issue tracker
421  <issue_tracker@your.tracker.email.domain.example>
422 MIME-Version: 1.0
423 Message-Id: <dummy_test_message_id>
424 X-Roundup-Name: Roundup issue tracker
425 X-Roundup-Loop: hello
426 X-Roundup-Issue-Status: unread
427 Content-Transfer-Encoding: quoted-printable
429 This is a test submission of a new issue.
431 ----------
432 assignedto: richard
433 messages: 1
434 nosy: Chef, mary, richard
435 status: unread
436 title: Testing...
438 _______________________________________________________________________
439 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
440 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
441 _______________________________________________________________________
442 ''')
444     def testNewIssueNoAuthorEmail(self):
445         self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
446         self._handle_mail('''Content-Type: text/plain;
447   charset="iso-8859-1"
448 From: Chef <chef@bork.bork.bork>
449 To: issue_tracker@your.tracker.email.domain.example
450 Message-Id: <dummy_test_message_id>
451 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
453 This is a test submission of a new issue.
454 ''')
455         self.compareMessages(self._get_mail(),
456 '''FROM: roundup-admin@your.tracker.email.domain.example
457 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
458 Content-Type: text/plain; charset="utf-8"
459 Subject: [issue1] Testing...
460 To: mary@test.test, richard@test.test
461 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
462 Reply-To: Roundup issue tracker
463  <issue_tracker@your.tracker.email.domain.example>
464 MIME-Version: 1.0
465 Message-Id: <dummy_test_message_id>
466 X-Roundup-Name: Roundup issue tracker
467 X-Roundup-Loop: hello
468 X-Roundup-Issue-Status: unread
469 Content-Transfer-Encoding: quoted-printable
471 New submission from Bork, Chef:
473 This is a test submission of a new issue.
475 ----------
476 assignedto: richard
477 messages: 1
478 nosy: Chef, mary, richard
479 status: unread
480 title: Testing...
482 _______________________________________________________________________
483 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
484 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
485 _______________________________________________________________________
486 ''')
488     multipart_msg = '''From: mary <mary@test.test>
489 To: issue_tracker@your.tracker.email.domain.example
490 Message-Id: <followup_dummy_id>
491 In-Reply-To: <dummy_test_message_id>
492 Subject: [issue1] Testing...
493 Content-Type: multipart/mixed; boundary="bxyzzy"
494 Content-Disposition: inline
497 --bxyzzy
498 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
499 Content-Disposition: inline
501 --bCsyhTFzCvuiizWE
502 Content-Type: text/plain; charset=us-ascii
503 Content-Disposition: inline
505 test attachment first text/plain
507 --bCsyhTFzCvuiizWE
508 Content-Type: application/octet-stream
509 Content-Disposition: attachment; filename="first.dvi"
510 Content-Transfer-Encoding: base64
512 SnVzdCBhIHRlc3QgAQo=
514 --bCsyhTFzCvuiizWE
515 Content-Type: text/plain; charset=us-ascii
516 Content-Disposition: inline
518 test attachment second text/plain
520 --bCsyhTFzCvuiizWE
521 Content-Type: text/html
522 Content-Disposition: inline
524 <html>
525 to be ignored.
526 </html>
528 --bCsyhTFzCvuiizWE--
530 --bxyzzy
531 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
532 Content-Disposition: inline
534 --bCsyhTFzCvuiizWF
535 Content-Type: text/plain; charset=us-ascii
536 Content-Disposition: inline
538 test attachment third text/plain
540 --bCsyhTFzCvuiizWF
541 Content-Type: application/octet-stream
542 Content-Disposition: attachment; filename="second.dvi"
543 Content-Transfer-Encoding: base64
545 SnVzdCBhIHRlc3QK
547 --bCsyhTFzCvuiizWF--
549 --bxyzzy--
550 '''
552     multipart_msg_latin1 = '''From: mary <mary@test.test>
553 To: issue_tracker@your.tracker.email.domain.example
554 Message-Id: <followup_dummy_id>
555 In-Reply-To: <dummy_test_message_id>
556 Subject: [issue1] Testing...
557 Content-Type: multipart/alternative; boundary=001485f339f8f361fb049188dbba
560 --001485f339f8f361fb049188dbba
561 Content-Type: text/plain; charset=ISO-8859-1
562 Content-Transfer-Encoding: quoted-printable
564 umlaut =E4=F6=FC=C4=D6=DC=DF
566 --001485f339f8f361fb049188dbba
567 Content-Type: text/html; charset=ISO-8859-1
568 Content-Transfer-Encoding: quoted-printable
570 <html>umlaut =E4=F6=FC=C4=D6=DC=DF</html>
572 --001485f339f8f361fb049188dbba--
573 '''
575     multipart_msg_rfc822 = '''From: mary <mary@test.test>
576 To: issue_tracker@your.tracker.email.domain.example
577 Message-Id: <followup_dummy_id>
578 In-Reply-To: <dummy_test_message_id>
579 Subject: [issue1] Testing...
580 Content-Type: multipart/mixed; boundary=001485f339f8f361fb049188dbba
582 This is a multi-part message in MIME format.
583 --001485f339f8f361fb049188dbba
584 Content-Type: text/plain; charset=ISO-8859-15
585 Content-Transfer-Encoding: 7bit
587 First part: Text
589 --001485f339f8f361fb049188dbba
590 Content-Type: message/rfc822; name="Fwd: Original email subject.eml"
591 Content-Transfer-Encoding: 7bit
592 Content-Disposition: attachment; filename="Fwd: Original email subject.eml"
594 Message-Id: <followup_dummy_id_2>
595 In-Reply-To: <dummy_test_message_id_2>
596 MIME-Version: 1.0
597 Subject: Fwd: Original email subject
598 Date: Mon, 23 Aug 2010 08:23:33 +0200
599 Content-Type: multipart/alternative; boundary="090500050101020406060002"
601 This is a multi-part message in MIME format.
602 --090500050101020406060002
603 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
604 Content-Transfer-Encoding: 7bit
606 some text in inner email
607 ========================
609 --090500050101020406060002
610 Content-Type: text/html; charset=ISO-8859-15
611 Content-Transfer-Encoding: 7bit
613 <html>
614 some text in inner email
615 ========================
616 </html>
618 --090500050101020406060002--
620 --001485f339f8f361fb049188dbba--
621 '''
623     def testMultipartKeepAlternatives(self):
624         self.doNewIssue()
625         self._handle_mail(self.multipart_msg)
626         messages = self.db.issue.get('1', 'messages')
627         messages.sort()
628         msg = self.db.msg.getnode (messages[-1])
629         assert(len(msg.files) == 5)
630         names = {0 : 'first.dvi', 4 : 'second.dvi'}
631         content = {3 : 'test attachment third text/plain\n',
632                    4 : 'Just a test\n'}
633         for n, id in enumerate (msg.files):
634             f = self.db.file.getnode (id)
635             self.assertEqual(f.name, names.get (n, 'unnamed'))
636             if n in content :
637                 self.assertEqual(f.content, content [n])
638         self.assertEqual(msg.content, 'test attachment second text/plain')
640     def testMultipartSeveralAttachmentMessages(self):
641         self.doNewIssue()
642         self._handle_mail(self.multipart_msg)
643         messages = self.db.issue.get('1', 'messages')
644         messages.sort()
645         self.assertEqual(messages[-1], '2')
646         msg = self.db.msg.getnode (messages[-1])
647         self.assertEqual(len(msg.files), 5)
648         issue = self.db.issue.getnode ('1')
649         self.assertEqual(len(issue.files), 5)
650         names = {0 : 'first.dvi', 4 : 'second.dvi'}
651         content = {3 : 'test attachment third text/plain\n',
652                    4 : 'Just a test\n'}
653         for n, id in enumerate (msg.files):
654             f = self.db.file.getnode (id)
655             self.assertEqual(f.name, names.get (n, 'unnamed'))
656             if n in content :
657                 self.assertEqual(f.content, content [n])
658         self.assertEqual(msg.content, 'test attachment second text/plain')
659         self.assertEqual(msg.files, ['1', '2', '3', '4', '5'])
660         self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
662         self._handle_mail(self.multipart_msg)
663         issue = self.db.issue.getnode ('1')
664         self.assertEqual(len(issue.files), 10)
665         messages = self.db.issue.get('1', 'messages')
666         messages.sort()
667         self.assertEqual(messages[-1], '3')
668         msg = self.db.msg.getnode (messages[-1])
669         self.assertEqual(issue.files, [str(i+1) for i in range(10)])
670         self.assertEqual(msg.files, ['6', '7', '8', '9', '10'])
672     def testMultipartKeepFiles(self):
673         self.doNewIssue()
674         self._handle_mail(self.multipart_msg)
675         messages = self.db.issue.get('1', 'messages')
676         messages.sort()
677         msg = self.db.msg.getnode (messages[-1])
678         self.assertEqual(len(msg.files), 5)
679         issue = self.db.issue.getnode ('1')
680         self.assertEqual(len(issue.files), 5)
681         names = {0 : 'first.dvi', 4 : 'second.dvi'}
682         content = {3 : 'test attachment third text/plain\n',
683                    4 : 'Just a test\n'}
684         for n, id in enumerate (msg.files):
685             f = self.db.file.getnode (id)
686             self.assertEqual(f.name, names.get (n, 'unnamed'))
687             if n in content :
688                 self.assertEqual(f.content, content [n])
689         self.assertEqual(msg.content, 'test attachment second text/plain')
690         self._handle_mail('''From: mary <mary@test.test>
691 To: issue_tracker@your.tracker.email.domain.example
692 Message-Id: <followup_dummy_id2>
693 In-Reply-To: <dummy_test_message_id>
694 Subject: [issue1] Testing...
696 This ist a message without attachment
697 ''')
698         issue = self.db.issue.getnode ('1')
699         self.assertEqual(len(issue.files), 5)
700         self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
702     def testMultipartDropAlternatives(self):
703         self.doNewIssue()
704         self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
705         self._handle_mail(self.multipart_msg)
706         messages = self.db.issue.get('1', 'messages')
707         messages.sort()
708         msg = self.db.msg.getnode (messages[-1])
709         self.assertEqual(len(msg.files), 2)
710         names = {1 : 'second.dvi'}
711         content = {0 : 'test attachment third text/plain\n',
712                    1 : 'Just a test\n'}
713         for n, id in enumerate (msg.files):
714             f = self.db.file.getnode (id)
715             self.assertEqual(f.name, names.get (n, 'unnamed'))
716             if n in content :
717                 self.assertEqual(f.content, content [n])
718         self.assertEqual(msg.content, 'test attachment second text/plain')
720     def testMultipartCharsetUTF8NoAttach(self):
721         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
722         self.doNewIssue()
723         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
724         self._handle_mail(self.multipart_msg_latin1)
725         messages = self.db.issue.get('1', 'messages')
726         messages.sort()
727         msg = self.db.msg.getnode (messages[-1])
728         self.assertEqual(len(msg.files), 1)
729         name = 'unnamed'
730         content = '<html>' + c + '</html>\n'
731         for n, id in enumerate (msg.files):
732             f = self.db.file.getnode (id)
733             self.assertEqual(f.name, name)
734             self.assertEqual(f.content, content)
735         self.assertEqual(msg.content, c)
736         self.compareMessages(self._get_mail(),
737 '''FROM: roundup-admin@your.tracker.email.domain.example
738 TO: chef@bork.bork.bork, richard@test.test
739 Content-Type: text/plain; charset="utf-8"
740 Subject: [issue1] Testing...
741 To: chef@bork.bork.bork, richard@test.test
742 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
743 Reply-To: Roundup issue tracker
744  <issue_tracker@your.tracker.email.domain.example>
745 MIME-Version: 1.0
746 Message-Id: <followup_dummy_id>
747 In-Reply-To: <dummy_test_message_id>
748 X-Roundup-Name: Roundup issue tracker
749 X-Roundup-Loop: hello
750 X-Roundup-Issue-Status: chatting
751 X-Roundup-Issue-Files: unnamed
752 Content-Transfer-Encoding: quoted-printable
755 Contrary, Mary <mary@test.test> added the comment:
757 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
758 File 'unnamed' not attached - you can download it from http://tracker.examp=
759 le/cgi-bin/roundup.cgi/bugs/file1.
761 ----------
762 status: unread -> chatting
764 _______________________________________________________________________
765 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
766 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
767 _______________________________________________________________________
768 ''')
770     def testMultipartCharsetLatin1NoAttach(self):
771         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
772         self.doNewIssue()
773         self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
774         self.db.config.MAIL_CHARSET = 'iso-8859-1'
775         self._handle_mail(self.multipart_msg_latin1)
776         messages = self.db.issue.get('1', 'messages')
777         messages.sort()
778         msg = self.db.msg.getnode (messages[-1])
779         self.assertEqual(len(msg.files), 1)
780         name = 'unnamed'
781         content = '<html>' + c + '</html>\n'
782         for n, id in enumerate (msg.files):
783             f = self.db.file.getnode (id)
784             self.assertEqual(f.name, name)
785             self.assertEqual(f.content, content)
786         self.assertEqual(msg.content, c)
787         self.compareMessages(self._get_mail(),
788 '''FROM: roundup-admin@your.tracker.email.domain.example
789 TO: chef@bork.bork.bork, richard@test.test
790 Content-Type: text/plain; charset="iso-8859-1"
791 Subject: [issue1] Testing...
792 To: chef@bork.bork.bork, richard@test.test
793 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
794 Reply-To: Roundup issue tracker
795  <issue_tracker@your.tracker.email.domain.example>
796 MIME-Version: 1.0
797 Message-Id: <followup_dummy_id>
798 In-Reply-To: <dummy_test_message_id>
799 X-Roundup-Name: Roundup issue tracker
800 X-Roundup-Loop: hello
801 X-Roundup-Issue-Status: chatting
802 X-Roundup-Issue-Files: unnamed
803 Content-Transfer-Encoding: quoted-printable
806 Contrary, Mary <mary@test.test> added the comment:
808 umlaut =E4=F6=FC=C4=D6=DC=DF
809 File 'unnamed' not attached - you can download it from http://tracker.examp=
810 le/cgi-bin/roundup.cgi/bugs/file1.
812 ----------
813 status: unread -> chatting
815 _______________________________________________________________________
816 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
817 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
818 _______________________________________________________________________
819 ''')
821     def testMultipartCharsetUTF8AttachFile(self):
822         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
823         self.doNewIssue()
824         self._handle_mail(self.multipart_msg_latin1)
825         messages = self.db.issue.get('1', 'messages')
826         messages.sort()
827         msg = self.db.msg.getnode (messages[-1])
828         self.assertEqual(len(msg.files), 1)
829         name = 'unnamed'
830         content = '<html>' + c + '</html>\n'
831         for n, id in enumerate (msg.files):
832             f = self.db.file.getnode (id)
833             self.assertEqual(f.name, name)
834             self.assertEqual(f.content, content)
835         self.assertEqual(msg.content, c)
836         self.compareMessages(self._get_mail(),
837 '''FROM: roundup-admin@your.tracker.email.domain.example
838 TO: chef@bork.bork.bork, richard@test.test
839 Content-Type: multipart/mixed; boundary="utf-8"
840 Subject: [issue1] Testing...
841 To: chef@bork.bork.bork, richard@test.test
842 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
843 Reply-To: Roundup issue tracker
844  <issue_tracker@your.tracker.email.domain.example>
845 MIME-Version: 1.0
846 Message-Id: <followup_dummy_id>
847 In-Reply-To: <dummy_test_message_id>
848 X-Roundup-Name: Roundup issue tracker
849 X-Roundup-Loop: hello
850 X-Roundup-Issue-Status: chatting
851 X-Roundup-Issue-Files: unnamed
852 Content-Transfer-Encoding: quoted-printable
855 --utf-8
856 MIME-Version: 1.0
857 Content-Type: text/plain; charset="utf-8"
858 Content-Transfer-Encoding: quoted-printable
861 Contrary, Mary <mary@test.test> added the comment:
863 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
865 ----------
866 status: unread -> chatting
868 _______________________________________________________________________
869 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
870 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
871 _______________________________________________________________________
872 --utf-8
873 Content-Type: text/html
874 MIME-Version: 1.0
875 Content-Transfer-Encoding: base64
876 Content-Disposition: attachment;
877  filename="unnamed"
879 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
881 --utf-8--
882 ''')
884     def testMultipartCharsetLatin1AttachFile(self):
885         c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
886         self.doNewIssue()
887         self.db.config.MAIL_CHARSET = 'iso-8859-1'
888         self._handle_mail(self.multipart_msg_latin1)
889         messages = self.db.issue.get('1', 'messages')
890         messages.sort()
891         msg = self.db.msg.getnode (messages[-1])
892         self.assertEqual(len(msg.files), 1)
893         name = 'unnamed'
894         content = '<html>' + c + '</html>\n'
895         for n, id in enumerate (msg.files):
896             f = self.db.file.getnode (id)
897             self.assertEqual(f.name, name)
898             self.assertEqual(f.content, content)
899         self.assertEqual(msg.content, c)
900         self.compareMessages(self._get_mail(),
901 '''FROM: roundup-admin@your.tracker.email.domain.example
902 TO: chef@bork.bork.bork, richard@test.test
903 Content-Type: multipart/mixed; boundary="utf-8"
904 Subject: [issue1] Testing...
905 To: chef@bork.bork.bork, richard@test.test
906 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
907 Reply-To: Roundup issue tracker
908  <issue_tracker@your.tracker.email.domain.example>
909 MIME-Version: 1.0
910 Message-Id: <followup_dummy_id>
911 In-Reply-To: <dummy_test_message_id>
912 X-Roundup-Name: Roundup issue tracker
913 X-Roundup-Loop: hello
914 X-Roundup-Issue-Status: chatting
915 X-Roundup-Issue-Files: unnamed
916 Content-Transfer-Encoding: quoted-printable
919 --utf-8
920 MIME-Version: 1.0
921 Content-Type: text/plain; charset="iso-8859-1"
922 Content-Transfer-Encoding: quoted-printable
925 Contrary, Mary <mary@test.test> added the comment:
927 umlaut =E4=F6=FC=C4=D6=DC=DF
929 ----------
930 status: unread -> chatting
932 _______________________________________________________________________
933 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
934 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
935 _______________________________________________________________________
936 --utf-8
937 Content-Type: text/html
938 MIME-Version: 1.0
939 Content-Transfer-Encoding: base64
940 Content-Disposition: attachment;
941  filename="unnamed"
943 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
945 --utf-8--
946 ''')
948     def testMultipartRFC822(self):
949         self.doNewIssue()
950         self._handle_mail(self.multipart_msg_rfc822)
951         messages = self.db.issue.get('1', 'messages')
952         messages.sort()
953         msg = self.db.msg.getnode (messages[-1])
954         self.assertEqual(len(msg.files), 1)
955         name = "Fwd: Original email subject.eml"
956         for n, id in enumerate (msg.files):
957             f = self.db.file.getnode (id)
958             self.assertEqual(f.name, name)
959         self.assertEqual(msg.content, 'First part: Text')
960         self.compareMessages(self._get_mail(),
961 '''TO: chef@bork.bork.bork, richard@test.test
962 Content-Type: text/plain; charset="utf-8"
963 Subject: [issue1] Testing...
964 To: chef@bork.bork.bork, richard@test.test
965 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
966 Reply-To: Roundup issue tracker
967  <issue_tracker@your.tracker.email.domain.example>
968 MIME-Version: 1.0
969 Message-Id: <followup_dummy_id>
970 In-Reply-To: <dummy_test_message_id>
971 X-Roundup-Name: Roundup issue tracker
972 X-Roundup-Loop: hello
973 X-Roundup-Issue-Status: chatting
974 X-Roundup-Issue-Files: Fwd: Original email subject.eml
975 Content-Transfer-Encoding: quoted-printable
978 --utf-8
979 MIME-Version: 1.0
980 Content-Type: text/plain; charset="utf-8"
981 Content-Transfer-Encoding: quoted-printable
984 Contrary, Mary <mary@test.test> added the comment:
986 First part: Text
988 ----------
989 status: unread -> chatting
991 _______________________________________________________________________
992 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
993 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
994 _______________________________________________________________________
995 --utf-8
996 Content-Type: message/rfc822
997 MIME-Version: 1.0
998 Content-Disposition: attachment;
999  filename="Fwd: Original email subject.eml"
1001 Message-Id: <followup_dummy_id_2>
1002 In-Reply-To: <dummy_test_message_id_2>
1003 MIME-Version: 1.0
1004 Subject: Fwd: Original email subject
1005 Date: Mon, 23 Aug 2010 08:23:33 +0200
1006 Content-Type: multipart/alternative; boundary="090500050101020406060002"
1008 This is a multi-part message in MIME format.
1009 --090500050101020406060002
1010 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
1011 Content-Transfer-Encoding: 7bit
1013 some text in inner email
1014 ========================
1016 --090500050101020406060002
1017 Content-Type: text/html; charset=ISO-8859-15
1018 Content-Transfer-Encoding: 7bit
1020 <html>
1021 some text in inner email
1022 ========================
1023 </html>
1025 --090500050101020406060002--
1027 --utf-8--
1028 ''')
1030     def testMultipartRFC822Unpack(self):
1031         self.doNewIssue()
1032         self.db.config.MAILGW_UNPACK_RFC822 = True
1033         self._handle_mail(self.multipart_msg_rfc822)
1034         messages = self.db.issue.get('1', 'messages')
1035         messages.sort()
1036         msg = self.db.msg.getnode (messages[-1])
1037         self.assertEqual(len(msg.files), 2)
1038         t = 'some text in inner email\n========================\n'
1039         content = {0 : t, 1 : '<html>\n' + t + '</html>\n'}
1040         for n, id in enumerate (msg.files):
1041             f = self.db.file.getnode (id)
1042             self.assertEqual(f.name, 'unnamed')
1043             if n in content :
1044                 self.assertEqual(f.content, content [n])
1045         self.assertEqual(msg.content, 'First part: Text')
1047     def testSimpleFollowup(self):
1048         self.doNewIssue()
1049         self._handle_mail('''Content-Type: text/plain;
1050   charset="iso-8859-1"
1051 From: mary <mary@test.test>
1052 To: issue_tracker@your.tracker.email.domain.example
1053 Message-Id: <followup_dummy_id>
1054 In-Reply-To: <dummy_test_message_id>
1055 Subject: [issue1] Testing...
1057 This is a second followup
1058 ''')
1059         self.compareMessages(self._get_mail(),
1060 '''FROM: roundup-admin@your.tracker.email.domain.example
1061 TO: chef@bork.bork.bork, richard@test.test
1062 Content-Type: text/plain; charset="utf-8"
1063 Subject: [issue1] Testing...
1064 To: chef@bork.bork.bork, richard@test.test
1065 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1066 Reply-To: Roundup issue tracker
1067  <issue_tracker@your.tracker.email.domain.example>
1068 MIME-Version: 1.0
1069 Message-Id: <followup_dummy_id>
1070 In-Reply-To: <dummy_test_message_id>
1071 X-Roundup-Name: Roundup issue tracker
1072 X-Roundup-Loop: hello
1073 X-Roundup-Issue-Status: chatting
1074 Content-Transfer-Encoding: quoted-printable
1077 Contrary, Mary <mary@test.test> added the comment:
1079 This is a second followup
1081 ----------
1082 status: unread -> chatting
1084 _______________________________________________________________________
1085 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1086 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1087 _______________________________________________________________________
1088 ''')
1090     def testFollowup(self):
1091         self.doNewIssue()
1093         self._handle_mail('''Content-Type: text/plain;
1094   charset="iso-8859-1"
1095 From: richard <richard@test.test>
1096 To: issue_tracker@your.tracker.email.domain.example
1097 Message-Id: <followup_dummy_id>
1098 In-Reply-To: <dummy_test_message_id>
1099 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1101 This is a followup
1102 ''')
1103         l = self.db.issue.get('1', 'nosy')
1104         l.sort()
1105         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1106             self.john_id])
1108         self.compareMessages(self._get_mail(),
1109 '''FROM: roundup-admin@your.tracker.email.domain.example
1110 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1111 Content-Type: text/plain; charset="utf-8"
1112 Subject: [issue1] Testing...
1113 To: chef@bork.bork.bork, john@test.test, mary@test.test
1114 From: richard <issue_tracker@your.tracker.email.domain.example>
1115 Reply-To: Roundup issue tracker
1116  <issue_tracker@your.tracker.email.domain.example>
1117 MIME-Version: 1.0
1118 Message-Id: <followup_dummy_id>
1119 In-Reply-To: <dummy_test_message_id>
1120 X-Roundup-Name: Roundup issue tracker
1121 X-Roundup-Loop: hello
1122 X-Roundup-Issue-Status: chatting
1123 Content-Transfer-Encoding: quoted-printable
1126 richard <richard@test.test> added the comment:
1128 This is a followup
1130 ----------
1131 assignedto:  -> mary
1132 nosy: +john, mary
1133 status: unread -> chatting
1135 _______________________________________________________________________
1136 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1137 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1138 _______________________________________________________________________
1139 ''')
1141     def testFollowupNoSubjectChange(self):
1142         self.db.config.MAILGW_SUBJECT_UPDATES_TITLE = 'no'
1143         self.doNewIssue()
1145         self._handle_mail('''Content-Type: text/plain;
1146   charset="iso-8859-1"
1147 From: richard <richard@test.test>
1148 To: issue_tracker@your.tracker.email.domain.example
1149 Message-Id: <followup_dummy_id>
1150 In-Reply-To: <dummy_test_message_id>
1151 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john]
1153 This is a followup
1154 ''')
1155         l = self.db.issue.get('1', 'nosy')
1156         l.sort()
1157         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1158             self.john_id])
1160         self.compareMessages(self._get_mail(),
1161 '''FROM: roundup-admin@your.tracker.email.domain.example
1162 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1163 Content-Type: text/plain; charset="utf-8"
1164 Subject: [issue1] Testing...
1165 To: chef@bork.bork.bork, john@test.test, mary@test.test
1166 From: richard <issue_tracker@your.tracker.email.domain.example>
1167 Reply-To: Roundup issue tracker
1168  <issue_tracker@your.tracker.email.domain.example>
1169 MIME-Version: 1.0
1170 Message-Id: <followup_dummy_id>
1171 In-Reply-To: <dummy_test_message_id>
1172 X-Roundup-Name: Roundup issue tracker
1173 X-Roundup-Loop: hello
1174 X-Roundup-Issue-Status: chatting
1175 Content-Transfer-Encoding: quoted-printable
1178 richard <richard@test.test> added the comment:
1180 This is a followup
1182 ----------
1183 assignedto:  -> mary
1184 nosy: +john, mary
1185 status: unread -> chatting
1187 _______________________________________________________________________
1188 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1189 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1190 _______________________________________________________________________
1191 ''')
1192         self.assertEqual(self.db.issue.get('1','title'), 'Testing...')
1194     def testFollowupExplicitSubjectChange(self):
1195         self.doNewIssue()
1197         self._handle_mail('''Content-Type: text/plain;
1198   charset="iso-8859-1"
1199 From: richard <richard@test.test>
1200 To: issue_tracker@your.tracker.email.domain.example
1201 Message-Id: <followup_dummy_id>
1202 In-Reply-To: <dummy_test_message_id>
1203 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john; title=new title]
1205 This is a followup
1206 ''')
1207         l = self.db.issue.get('1', 'nosy')
1208         l.sort()
1209         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1210             self.john_id])
1212         self.compareMessages(self._get_mail(),
1213 '''FROM: roundup-admin@your.tracker.email.domain.example
1214 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1215 Content-Type: text/plain; charset="utf-8"
1216 Subject: [issue1] new title
1217 To: chef@bork.bork.bork, john@test.test, mary@test.test
1218 From: richard <issue_tracker@your.tracker.email.domain.example>
1219 Reply-To: Roundup issue tracker
1220  <issue_tracker@your.tracker.email.domain.example>
1221 MIME-Version: 1.0
1222 Message-Id: <followup_dummy_id>
1223 In-Reply-To: <dummy_test_message_id>
1224 X-Roundup-Name: Roundup issue tracker
1225 X-Roundup-Loop: hello
1226 X-Roundup-Issue-Status: chatting
1227 Content-Transfer-Encoding: quoted-printable
1230 richard <richard@test.test> added the comment:
1232 This is a followup
1234 ----------
1235 assignedto:  -> mary
1236 nosy: +john, mary
1237 status: unread -> chatting
1238 title: Testing... -> new title
1240 _______________________________________________________________________
1241 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1242 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1243 _______________________________________________________________________
1244 ''')
1246     def testNosyGeneration(self):
1247         self.db.issue.create(title='test')
1249         # create a nosy message
1250         msg = self.db.msg.create(content='This is a test',
1251             author=self.richard_id, messageid='<dummy_test_message_id>')
1252         self.db.journaltag = 'richard'
1253         l = self.db.issue.create(title='test', messages=[msg],
1254             nosy=[self.chef_id, self.mary_id, self.john_id])
1256         self.compareMessages(self._get_mail(),
1257 '''FROM: roundup-admin@your.tracker.email.domain.example
1258 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1259 Content-Type: text/plain; charset="utf-8"
1260 Subject: [issue2] test
1261 To: chef@bork.bork.bork, john@test.test, mary@test.test
1262 From: richard <issue_tracker@your.tracker.email.domain.example>
1263 Reply-To: Roundup issue tracker
1264  <issue_tracker@your.tracker.email.domain.example>
1265 MIME-Version: 1.0
1266 Message-Id: <dummy_test_message_id>
1267 X-Roundup-Name: Roundup issue tracker
1268 X-Roundup-Loop: hello
1269 X-Roundup-Issue-Status: unread
1270 Content-Transfer-Encoding: quoted-printable
1273 New submission from richard <richard@test.test>:
1275 This is a test
1277 ----------
1278 messages: 1
1279 nosy: Chef, john, mary, richard
1280 status: unread
1281 title: test
1283 _______________________________________________________________________
1284 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1285 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
1286 _______________________________________________________________________
1287 ''')
1289     def testPropertyChangeOnly(self):
1290         self.doNewIssue()
1291         oldvalues = self.db.getnode('issue', '1').copy()
1292         oldvalues['assignedto'] = None
1293         # reconstruct old behaviour: This would reuse the
1294         # database-handle from the doNewIssue above which has committed
1295         # as user "Chef". So we close and reopen the db as that user.
1296         #self.db.close() actually don't close 'cos this empties memorydb
1297         self.db = self.instance.open('Chef')
1298         self.db.issue.set('1', assignedto=self.chef_id)
1299         self.db.commit()
1300         self.db.issue.nosymessage('1', None, oldvalues)
1302         new_mail = ""
1303         for line in self._get_mail().split("\n"):
1304             if "Message-Id: " in line:
1305                 continue
1306             if "Date: " in line:
1307                 continue
1308             new_mail += line+"\n"
1310         self.compareMessages(new_mail, """
1311 FROM: roundup-admin@your.tracker.email.domain.example
1312 TO: chef@bork.bork.bork, richard@test.test
1313 Content-Type: text/plain; charset="utf-8"
1314 Subject: [issue1] Testing...
1315 To: chef@bork.bork.bork, richard@test.test
1316 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
1317 X-Roundup-Name: Roundup issue tracker
1318 X-Roundup-Loop: hello
1319 X-Roundup-Issue-Status: unread
1320 X-Roundup-Version: 1.3.3
1321 In-Reply-To: <dummy_test_message_id>
1322 MIME-Version: 1.0
1323 Reply-To: Roundup issue tracker
1324  <issue_tracker@your.tracker.email.domain.example>
1325 Content-Transfer-Encoding: quoted-printable
1328 Change by Bork, Chef <chef@bork.bork.bork>:
1331 ----------
1332 assignedto:  -> Chef
1334 _______________________________________________________________________
1335 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1336 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1337 _______________________________________________________________________
1338 """)
1341     #
1342     # FOLLOWUP TITLE MATCH
1343     #
1344     def testFollowupTitleMatch(self):
1345         self.doNewIssue()
1346         self._handle_mail('''Content-Type: text/plain;
1347   charset="iso-8859-1"
1348 From: richard <richard@test.test>
1349 To: issue_tracker@your.tracker.email.domain.example
1350 Message-Id: <followup_dummy_id>
1351 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1353 This is a followup
1354 ''')
1355         self.compareMessages(self._get_mail(),
1356 '''FROM: roundup-admin@your.tracker.email.domain.example
1357 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1358 Content-Type: text/plain; charset="utf-8"
1359 Subject: [issue1] Testing...
1360 To: chef@bork.bork.bork, john@test.test, mary@test.test
1361 From: richard <issue_tracker@your.tracker.email.domain.example>
1362 Reply-To: Roundup issue tracker
1363  <issue_tracker@your.tracker.email.domain.example>
1364 MIME-Version: 1.0
1365 Message-Id: <followup_dummy_id>
1366 In-Reply-To: <dummy_test_message_id>
1367 X-Roundup-Name: Roundup issue tracker
1368 X-Roundup-Loop: hello
1369 X-Roundup-Issue-Status: chatting
1370 Content-Transfer-Encoding: quoted-printable
1373 richard <richard@test.test> added the comment:
1375 This is a followup
1377 ----------
1378 assignedto:  -> mary
1379 nosy: +john, mary
1380 status: unread -> chatting
1382 _______________________________________________________________________
1383 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1384 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1385 _______________________________________________________________________
1386 ''')
1388     def testFollowupTitleMatchMultiRe(self):
1389         nodeid1 = self.doNewIssue()
1390         nodeid2 = self._handle_mail('''Content-Type: text/plain;
1391   charset="iso-8859-1"
1392 From: richard <richard@test.test>
1393 To: issue_tracker@your.tracker.email.domain.example
1394 Message-Id: <followup_dummy_id>
1395 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1397 This is a followup
1398 ''')
1400         nodeid3 = self._handle_mail('''Content-Type: text/plain;
1401   charset="iso-8859-1"
1402 From: richard <richard@test.test>
1403 To: issue_tracker@your.tracker.email.domain.example
1404 Message-Id: <followup2_dummy_id>
1405 Subject: Ang: Re: Testing...
1407 This is a followup
1408 ''')
1409         self.assertEqual(nodeid1, nodeid2)
1410         self.assertEqual(nodeid1, nodeid3)
1412     def testFollowupTitleMatchNever(self):
1413         nodeid = self.doNewIssue()
1414         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
1415         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1416   charset="iso-8859-1"
1417 From: richard <richard@test.test>
1418 To: issue_tracker@your.tracker.email.domain.example
1419 Message-Id: <followup_dummy_id>
1420 Subject: Re: Testing...
1422 This is a followup
1423 '''), nodeid)
1425     def testFollowupTitleMatchNeverInterval(self):
1426         nodeid = self.doNewIssue()
1427         # force failure of the interval
1428         time.sleep(2)
1429         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
1430         self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1431   charset="iso-8859-1"
1432 From: richard <richard@test.test>
1433 To: issue_tracker@your.tracker.email.domain.example
1434 Message-Id: <followup_dummy_id>
1435 Subject: Re: Testing...
1437 This is a followup
1438 '''), nodeid)
1441     def testFollowupTitleMatchInterval(self):
1442         nodeid = self.doNewIssue()
1443         self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
1444         self.assertEqual(self._handle_mail('''Content-Type: text/plain;
1445   charset="iso-8859-1"
1446 From: richard <richard@test.test>
1447 To: issue_tracker@your.tracker.email.domain.example
1448 Message-Id: <followup_dummy_id>
1449 Subject: Re: Testing...
1451 This is a followup
1452 '''), nodeid)
1455     def testFollowupNosyAuthor(self):
1456         self.doNewIssue()
1457         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1458         self._handle_mail('''Content-Type: text/plain;
1459   charset="iso-8859-1"
1460 From: john@test.test
1461 To: issue_tracker@your.tracker.email.domain.example
1462 Message-Id: <followup_dummy_id>
1463 In-Reply-To: <dummy_test_message_id>
1464 Subject: [issue1] Testing...
1466 This is a followup
1467 ''')
1469         self.compareMessages(self._get_mail(),
1470 '''FROM: roundup-admin@your.tracker.email.domain.example
1471 TO: chef@bork.bork.bork, richard@test.test
1472 Content-Type: text/plain; charset="utf-8"
1473 Subject: [issue1] Testing...
1474 To: chef@bork.bork.bork, richard@test.test
1475 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1476 Reply-To: Roundup issue tracker
1477  <issue_tracker@your.tracker.email.domain.example>
1478 MIME-Version: 1.0
1479 Message-Id: <followup_dummy_id>
1480 In-Reply-To: <dummy_test_message_id>
1481 X-Roundup-Name: Roundup issue tracker
1482 X-Roundup-Loop: hello
1483 X-Roundup-Issue-Status: chatting
1484 Content-Transfer-Encoding: quoted-printable
1487 John Doe <john@test.test> added the comment:
1489 This is a followup
1491 ----------
1492 nosy: +john
1493 status: unread -> chatting
1495 _______________________________________________________________________
1496 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1497 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1498 _______________________________________________________________________
1500 ''')
1502     def testFollowupNosyRecipients(self):
1503         self.doNewIssue()
1504         self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
1505         self._handle_mail('''Content-Type: text/plain;
1506   charset="iso-8859-1"
1507 From: richard@test.test
1508 To: issue_tracker@your.tracker.email.domain.example
1509 Cc: john@test.test
1510 Message-Id: <followup_dummy_id>
1511 In-Reply-To: <dummy_test_message_id>
1512 Subject: [issue1] Testing...
1514 This is a followup
1515 ''')
1516         self.compareMessages(self._get_mail(),
1517 '''FROM: roundup-admin@your.tracker.email.domain.example
1518 TO: chef@bork.bork.bork
1519 Content-Type: text/plain; charset="utf-8"
1520 Subject: [issue1] Testing...
1521 To: chef@bork.bork.bork
1522 From: richard <issue_tracker@your.tracker.email.domain.example>
1523 Reply-To: Roundup issue tracker
1524  <issue_tracker@your.tracker.email.domain.example>
1525 MIME-Version: 1.0
1526 Message-Id: <followup_dummy_id>
1527 In-Reply-To: <dummy_test_message_id>
1528 X-Roundup-Name: Roundup issue tracker
1529 X-Roundup-Loop: hello
1530 X-Roundup-Issue-Status: chatting
1531 Content-Transfer-Encoding: quoted-printable
1534 richard <richard@test.test> added the comment:
1536 This is a followup
1538 ----------
1539 nosy: +john
1540 status: unread -> chatting
1542 _______________________________________________________________________
1543 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1544 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1545 _______________________________________________________________________
1547 ''')
1549     def testFollowupNosyAuthorAndCopy(self):
1550         self.doNewIssue()
1551         self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1552         self.db.config.MESSAGES_TO_AUTHOR = 'yes'
1553         self._handle_mail('''Content-Type: text/plain;
1554   charset="iso-8859-1"
1555 From: john@test.test
1556 To: issue_tracker@your.tracker.email.domain.example
1557 Message-Id: <followup_dummy_id>
1558 In-Reply-To: <dummy_test_message_id>
1559 Subject: [issue1] Testing...
1561 This is a followup
1562 ''')
1563         self.compareMessages(self._get_mail(),
1564 '''FROM: roundup-admin@your.tracker.email.domain.example
1565 TO: chef@bork.bork.bork, john@test.test, richard@test.test
1566 Content-Type: text/plain; charset="utf-8"
1567 Subject: [issue1] Testing...
1568 To: chef@bork.bork.bork, john@test.test, richard@test.test
1569 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1570 Reply-To: Roundup issue tracker
1571  <issue_tracker@your.tracker.email.domain.example>
1572 MIME-Version: 1.0
1573 Message-Id: <followup_dummy_id>
1574 In-Reply-To: <dummy_test_message_id>
1575 X-Roundup-Name: Roundup issue tracker
1576 X-Roundup-Loop: hello
1577 X-Roundup-Issue-Status: chatting
1578 Content-Transfer-Encoding: quoted-printable
1581 John Doe <john@test.test> added the comment:
1583 This is a followup
1585 ----------
1586 nosy: +john
1587 status: unread -> chatting
1589 _______________________________________________________________________
1590 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1591 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1592 _______________________________________________________________________
1594 ''')
1596     def testFollowupNoNosyAuthor(self):
1597         self.doNewIssue()
1598         self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
1599         self._handle_mail('''Content-Type: text/plain;
1600   charset="iso-8859-1"
1601 From: john@test.test
1602 To: issue_tracker@your.tracker.email.domain.example
1603 Message-Id: <followup_dummy_id>
1604 In-Reply-To: <dummy_test_message_id>
1605 Subject: [issue1] Testing...
1607 This is a followup
1608 ''')
1609         self.compareMessages(self._get_mail(),
1610 '''FROM: roundup-admin@your.tracker.email.domain.example
1611 TO: chef@bork.bork.bork, richard@test.test
1612 Content-Type: text/plain; charset="utf-8"
1613 Subject: [issue1] Testing...
1614 To: chef@bork.bork.bork, richard@test.test
1615 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1616 Reply-To: Roundup issue tracker
1617  <issue_tracker@your.tracker.email.domain.example>
1618 MIME-Version: 1.0
1619 Message-Id: <followup_dummy_id>
1620 In-Reply-To: <dummy_test_message_id>
1621 X-Roundup-Name: Roundup issue tracker
1622 X-Roundup-Loop: hello
1623 X-Roundup-Issue-Status: chatting
1624 Content-Transfer-Encoding: quoted-printable
1627 John Doe <john@test.test> added the comment:
1629 This is a followup
1631 ----------
1632 status: unread -> chatting
1634 _______________________________________________________________________
1635 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1636 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1637 _______________________________________________________________________
1639 ''')
1641     def testFollowupNoNosyRecipients(self):
1642         self.doNewIssue()
1643         self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
1644         self._handle_mail('''Content-Type: text/plain;
1645   charset="iso-8859-1"
1646 From: richard@test.test
1647 To: issue_tracker@your.tracker.email.domain.example
1648 Cc: john@test.test
1649 Message-Id: <followup_dummy_id>
1650 In-Reply-To: <dummy_test_message_id>
1651 Subject: [issue1] Testing...
1653 This is a followup
1654 ''')
1655         self.compareMessages(self._get_mail(),
1656 '''FROM: roundup-admin@your.tracker.email.domain.example
1657 TO: chef@bork.bork.bork
1658 Content-Type: text/plain; charset="utf-8"
1659 Subject: [issue1] Testing...
1660 To: chef@bork.bork.bork
1661 From: richard <issue_tracker@your.tracker.email.domain.example>
1662 Reply-To: Roundup issue tracker
1663  <issue_tracker@your.tracker.email.domain.example>
1664 MIME-Version: 1.0
1665 Message-Id: <followup_dummy_id>
1666 In-Reply-To: <dummy_test_message_id>
1667 X-Roundup-Name: Roundup issue tracker
1668 X-Roundup-Loop: hello
1669 X-Roundup-Issue-Status: chatting
1670 Content-Transfer-Encoding: quoted-printable
1673 richard <richard@test.test> added the comment:
1675 This is a followup
1677 ----------
1678 status: unread -> chatting
1680 _______________________________________________________________________
1681 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1682 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1683 _______________________________________________________________________
1685 ''')
1687     def testFollowupEmptyMessage(self):
1688         self.doNewIssue()
1690         self._handle_mail('''Content-Type: text/plain;
1691   charset="iso-8859-1"
1692 From: richard <richard@test.test>
1693 To: issue_tracker@your.tracker.email.domain.example
1694 Message-Id: <followup_dummy_id>
1695 In-Reply-To: <dummy_test_message_id>
1696 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1698 ''')
1699         l = self.db.issue.get('1', 'nosy')
1700         l.sort()
1701         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1702             self.john_id])
1704         # should be no file created (ie. no message)
1705         assert not os.path.exists(SENDMAILDEBUG)
1707     def testFollowupEmptyMessageNoSubject(self):
1708         self.doNewIssue()
1710         self._handle_mail('''Content-Type: text/plain;
1711   charset="iso-8859-1"
1712 From: richard <richard@test.test>
1713 To: issue_tracker@your.tracker.email.domain.example
1714 Message-Id: <followup_dummy_id>
1715 In-Reply-To: <dummy_test_message_id>
1716 Subject: [issue1] [assignedto=mary; nosy=+john]
1718 ''')
1719         l = self.db.issue.get('1', 'nosy')
1720         l.sort()
1721         self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1722             self.john_id])
1724         # should be no file created (ie. no message)
1725         assert not os.path.exists(SENDMAILDEBUG)
1727     def testNosyRemove(self):
1728         self.doNewIssue()
1730         self._handle_mail('''Content-Type: text/plain;
1731   charset="iso-8859-1"
1732 From: richard <richard@test.test>
1733 To: issue_tracker@your.tracker.email.domain.example
1734 Message-Id: <followup_dummy_id>
1735 In-Reply-To: <dummy_test_message_id>
1736 Subject: [issue1] Testing... [nosy=-richard]
1738 ''')
1739         l = self.db.issue.get('1', 'nosy')
1740         l.sort()
1741         self.assertEqual(l, [self.chef_id])
1743         # NO NOSY MESSAGE SHOULD BE SENT!
1744         assert not os.path.exists(SENDMAILDEBUG)
1746     def testNewUserAuthor(self):
1747         self.db.commit()
1748         l = self.db.user.list()
1749         l.sort()
1750         message = '''Content-Type: text/plain;
1751   charset="iso-8859-1"
1752 From: fubar <fubar@bork.bork.bork>
1753 To: issue_tracker@your.tracker.email.domain.example
1754 Message-Id: <dummy_test_message_id>
1755 Subject: [issue] Testing...
1757 This is a test submission of a new issue.
1758 '''
1759         self.db.security.role['anonymous'].permissions=[]
1760         anonid = self.db.user.lookup('anonymous')
1761         self.db.user.set(anonid, roles='Anonymous')
1762         try:
1763             self._handle_mail(message)
1764         except Unauthorized, value:
1765             body_diff = self.compareMessages(str(value), """
1766 You are not a registered user.
1768 Unknown address: fubar@bork.bork.bork
1769 """)
1770             assert not body_diff, body_diff
1771         else:
1772             raise AssertionError, "Unathorized not raised when handling mail"
1774         # Add Web Access role to anonymous, and try again to make sure
1775         # we get a "please register at:" message this time.
1776         p = [
1777             self.db.security.getPermission('Register', 'user'),
1778             self.db.security.getPermission('Web Access', None),
1779         ]
1780         self.db.security.role['anonymous'].permissions=p
1781         try:
1782             self._handle_mail(message)
1783         except Unauthorized, value:
1784             body_diff = self.compareMessages(str(value), """
1785 You are not a registered user. Please register at:
1787 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1789 ...before sending mail to the tracker.
1791 Unknown address: fubar@bork.bork.bork
1792 """)
1793             assert not body_diff, body_diff
1794         else:
1795             raise AssertionError, "Unauthorized not raised when handling mail"
1797         # Make sure list of users is the same as before.
1798         m = self.db.user.list()
1799         m.sort()
1800         self.assertEqual(l, m)
1802         # now with the permission
1803         p = [
1804             self.db.security.getPermission('Register', 'user'),
1805             self.db.security.getPermission('Email Access', None),
1806         ]
1807         self.db.security.role['anonymous'].permissions=p
1808         self._handle_mail(message)
1809         m = self.db.user.list()
1810         m.sort()
1811         self.assertNotEqual(l, m)
1813     def testNewUserAuthorEncodedName(self):
1814         l = set(self.db.user.list())
1815         # From: name has Euro symbol in it
1816         message = '''Content-Type: text/plain;
1817   charset="iso-8859-1"
1818 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1819 To: issue_tracker@your.tracker.email.domain.example
1820 Message-Id: <dummy_test_message_id>
1821 Subject: [issue] Testing...
1823 This is a test submission of a new issue.
1824 '''
1825         p = [
1826             self.db.security.getPermission('Register', 'user'),
1827             self.db.security.getPermission('Email Access', None),
1828             self.db.security.getPermission('Create', 'issue'),
1829             self.db.security.getPermission('Create', 'msg'),
1830         ]
1831         self.db.security.role['anonymous'].permissions = p
1832         self._handle_mail(message)
1833         m = set(self.db.user.list())
1834         new = list(m - l)[0]
1835         name = self.db.user.get(new, 'realname')
1836         self.assertEquals(name, 'H€llo')
1838     def testNewUserAuthorMixedEncodedName(self):
1839         l = set(self.db.user.list())
1840         # From: name has Euro symbol in it
1841         message = '''Content-Type: text/plain;
1842   charset="iso-8859-1"
1843 From: Firstname =?utf-8?b?w6TDtsOf?= Last <fubar@bork.bork.bork>
1844 To: issue_tracker@your.tracker.email.domain.example
1845 Message-Id: <dummy_test_message_id>
1846 Subject: [issue] Test =?utf-8?b?w4TDlsOc?= umlauts
1847  X1
1848  X2
1850 This is a test submission of a new issue.
1851 '''
1852         p = [
1853             self.db.security.getPermission('Register', 'user'),
1854             self.db.security.getPermission('Email Access', None),
1855             self.db.security.getPermission('Create', 'issue'),
1856             self.db.security.getPermission('Create', 'msg'),
1857         ]
1858         self.db.security.role['anonymous'].permissions = p
1859         self._handle_mail(message)
1860         title = self.db.issue.get('1', 'title')
1861         self.assertEquals(title, 'Test \xc3\x84\xc3\x96\xc3\x9c umlauts X1 X2')
1862         m = set(self.db.user.list())
1863         new = list(m - l)[0]
1864         name = self.db.user.get(new, 'realname')
1865         self.assertEquals(name, 'Firstname \xc3\xa4\xc3\xb6\xc3\x9f Last')
1867     def testUnknownUser(self):
1868         l = set(self.db.user.list())
1869         message = '''Content-Type: text/plain;
1870   charset="iso-8859-1"
1871 From: Nonexisting User <nonexisting@bork.bork.bork>
1872 To: issue_tracker@your.tracker.email.domain.example
1873 Message-Id: <dummy_test_message_id>
1874 Subject: [issue] Testing nonexisting user...
1876 This is a test submission of a new issue.
1877 '''
1878         handler = self._create_mailgw(message)
1879         # we want a bounce message:
1880         handler.trapExceptions = 1
1881         ret = handler.main(StringIO(message))
1882         self.compareMessages(self._get_mail(),
1883 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1884 TO: nonexisting@bork.bork.bork
1885 From nobody Tue Jul 14 12:04:11 2009
1886 Content-Type: multipart/mixed; boundary="===============0639262320=="
1887 MIME-Version: 1.0
1888 Subject: Failed issue tracker submission
1889 To: nonexisting@bork.bork.bork
1890 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1891 Date: Tue, 14 Jul 2009 12:04:11 +0000
1892 Precedence: bulk
1893 X-Roundup-Name: Roundup issue tracker
1894 X-Roundup-Loop: hello
1895 X-Roundup-Version: 1.4.8
1896 MIME-Version: 1.0
1898 --===============0639262320==
1899 Content-Type: text/plain; charset="us-ascii"
1900 MIME-Version: 1.0
1901 Content-Transfer-Encoding: 7bit
1905 You are not a registered user. Please register at:
1907 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1909 ...before sending mail to the tracker.
1911 Unknown address: nonexisting@bork.bork.bork
1913 --===============0639262320==
1914 Content-Type: text/plain; charset="us-ascii"
1915 MIME-Version: 1.0
1916 Content-Transfer-Encoding: 7bit
1918 Content-Type: text/plain;
1919   charset="iso-8859-1"
1920 From: Nonexisting User <nonexisting@bork.bork.bork>
1921 To: issue_tracker@your.tracker.email.domain.example
1922 Message-Id: <dummy_test_message_id>
1923 Subject: [issue] Testing nonexisting user...
1925 This is a test submission of a new issue.
1927 --===============0639262320==--
1928 ''')
1930     def testEnc01(self):
1931         self.db.user.set(self.mary_id,
1932             realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1933             ('latin-1').encode('utf-8'))
1934         self.doNewIssue()
1935         self._handle_mail('''Content-Type: text/plain;
1936   charset="iso-8859-1"
1937 From: mary <mary@test.test>
1938 To: issue_tracker@your.tracker.email.domain.example
1939 Message-Id: <followup_dummy_id>
1940 In-Reply-To: <dummy_test_message_id>
1941 Subject: [issue1] Testing...
1942 Content-Type: text/plain;
1943         charset="iso-8859-1"
1944 Content-Transfer-Encoding: quoted-printable
1946 A message with encoding (encoded oe =F6)
1948 ''')
1949         self.compareMessages(self._get_mail(),
1950 '''FROM: roundup-admin@your.tracker.email.domain.example
1951 TO: chef@bork.bork.bork, richard@test.test
1952 Content-Type: text/plain; charset="utf-8"
1953 Subject: [issue1] Testing...
1954 To: chef@bork.bork.bork, richard@test.test
1955 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1956  <issue_tracker@your.tracker.email.domain.example>
1957 Reply-To: Roundup issue tracker
1958  <issue_tracker@your.tracker.email.domain.example>
1959 MIME-Version: 1.0
1960 Message-Id: <followup_dummy_id>
1961 In-Reply-To: <dummy_test_message_id>
1962 X-Roundup-Name: Roundup issue tracker
1963 X-Roundup-Loop: hello
1964 X-Roundup-Issue-Status: chatting
1965 Content-Transfer-Encoding: quoted-printable
1968 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1969  comment:
1971 A message with encoding (encoded oe =C3=B6)
1973 ----------
1974 status: unread -> chatting
1976 _______________________________________________________________________
1977 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1978 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1979 _______________________________________________________________________
1980 ''')
1982     def testEncNonUTF8(self):
1983         self.doNewIssue()
1984         self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1985         self._handle_mail('''Content-Type: text/plain;
1986   charset="iso-8859-1"
1987 From: mary <mary@test.test>
1988 To: issue_tracker@your.tracker.email.domain.example
1989 Message-Id: <followup_dummy_id>
1990 In-Reply-To: <dummy_test_message_id>
1991 Subject: [issue1] Testing...
1992 Content-Type: text/plain;
1993         charset="iso-8859-1"
1994 Content-Transfer-Encoding: quoted-printable
1996 A message with encoding (encoded oe =F6)
1998 ''')
1999         self.compareMessages(self._get_mail(),
2000 '''FROM: roundup-admin@your.tracker.email.domain.example
2001 TO: chef@bork.bork.bork, richard@test.test
2002 Content-Type: text/plain; charset="iso-8859-1"
2003 Subject: [issue1] Testing...
2004 To: chef@bork.bork.bork, richard@test.test
2005 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
2006 Reply-To: Roundup issue tracker
2007  <issue_tracker@your.tracker.email.domain.example>
2008 MIME-Version: 1.0
2009 Message-Id: <followup_dummy_id>
2010 In-Reply-To: <dummy_test_message_id>
2011 X-Roundup-Name: Roundup issue tracker
2012 X-Roundup-Loop: hello
2013 X-Roundup-Issue-Status: chatting
2014 Content-Transfer-Encoding: quoted-printable
2017 Contrary, Mary <mary@test.test> added the comment:
2019 A message with encoding (encoded oe =F6)
2021 ----------
2022 status: unread -> chatting
2024 _______________________________________________________________________
2025 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2026 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2027 _______________________________________________________________________
2028 ''')
2031     def testMultipartEnc01(self):
2032         self.doNewIssue()
2033         self._handle_mail('''Content-Type: text/plain;
2034   charset="iso-8859-1"
2035 From: mary <mary@test.test>
2036 To: issue_tracker@your.tracker.email.domain.example
2037 Message-Id: <followup_dummy_id>
2038 In-Reply-To: <dummy_test_message_id>
2039 Subject: [issue1] Testing...
2040 Content-Type: multipart/mixed;
2041         boundary="----_=_NextPart_000_01"
2043 This message is in MIME format. Since your mail reader does not understand
2044 this format, some or all of this message may not be legible.
2046 ------_=_NextPart_000_01
2047 Content-Type: text/plain;
2048         charset="iso-8859-1"
2049 Content-Transfer-Encoding: quoted-printable
2051 A message with first part encoded (encoded oe =F6)
2053 ''')
2054         self.compareMessages(self._get_mail(),
2055 '''FROM: roundup-admin@your.tracker.email.domain.example
2056 TO: chef@bork.bork.bork, richard@test.test
2057 Content-Type: text/plain; charset="utf-8"
2058 Subject: [issue1] Testing...
2059 To: chef@bork.bork.bork, richard@test.test
2060 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
2061 Reply-To: Roundup issue tracker
2062  <issue_tracker@your.tracker.email.domain.example>
2063 MIME-Version: 1.0
2064 Message-Id: <followup_dummy_id>
2065 In-Reply-To: <dummy_test_message_id>
2066 X-Roundup-Name: Roundup issue tracker
2067 X-Roundup-Loop: hello
2068 X-Roundup-Issue-Status: chatting
2069 Content-Transfer-Encoding: quoted-printable
2072 Contrary, Mary <mary@test.test> added the comment:
2074 A message with first part encoded (encoded oe =C3=B6)
2076 ----------
2077 status: unread -> chatting
2079 _______________________________________________________________________
2080 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2081 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2082 _______________________________________________________________________
2083 ''')
2085     def testContentDisposition(self):
2086         self.doNewIssue()
2087         self._handle_mail('''Content-Type: text/plain;
2088   charset="iso-8859-1"
2089 From: mary <mary@test.test>
2090 To: issue_tracker@your.tracker.email.domain.example
2091 Message-Id: <followup_dummy_id>
2092 In-Reply-To: <dummy_test_message_id>
2093 Subject: [issue1] Testing...
2094 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
2095 Content-Disposition: inline
2098 --bCsyhTFzCvuiizWE
2099 Content-Type: text/plain; charset=us-ascii
2100 Content-Disposition: inline
2102 test attachment binary
2104 --bCsyhTFzCvuiizWE
2105 Content-Type: application/octet-stream
2106 Content-Disposition: attachment; filename="main.dvi"
2107 Content-Transfer-Encoding: base64
2109 SnVzdCBhIHRlc3QgAQo=
2111 --bCsyhTFzCvuiizWE--
2112 ''')
2113         messages = self.db.issue.get('1', 'messages')
2114         messages.sort()
2115         file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
2116         self.assertEqual(file.name, 'main.dvi')
2117         self.assertEqual(file.content, 'Just a test \001\n')
2119     def testFollowupStupidQuoting(self):
2120         self.doNewIssue()
2122         self._handle_mail('''Content-Type: text/plain;
2123   charset="iso-8859-1"
2124 From: richard <richard@test.test>
2125 To: issue_tracker@your.tracker.email.domain.example
2126 Message-Id: <followup_dummy_id>
2127 In-Reply-To: <dummy_test_message_id>
2128 Subject: Re: "[issue1] Testing... "
2130 This is a followup
2131 ''')
2132         self.compareMessages(self._get_mail(),
2133 '''FROM: roundup-admin@your.tracker.email.domain.example
2134 TO: chef@bork.bork.bork
2135 Content-Type: text/plain; charset="utf-8"
2136 Subject: [issue1] Testing...
2137 To: chef@bork.bork.bork
2138 From: richard <issue_tracker@your.tracker.email.domain.example>
2139 Reply-To: Roundup issue tracker
2140  <issue_tracker@your.tracker.email.domain.example>
2141 MIME-Version: 1.0
2142 Message-Id: <followup_dummy_id>
2143 In-Reply-To: <dummy_test_message_id>
2144 X-Roundup-Name: Roundup issue tracker
2145 X-Roundup-Loop: hello
2146 X-Roundup-Issue-Status: chatting
2147 Content-Transfer-Encoding: quoted-printable
2150 richard <richard@test.test> added the comment:
2152 This is a followup
2154 ----------
2155 status: unread -> chatting
2157 _______________________________________________________________________
2158 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2159 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2160 _______________________________________________________________________
2161 ''')
2163     def testEmailQuoting(self):
2164         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
2165         self.innerTestQuoting('''This is a followup
2166 ''')
2168     def testEmailQuotingRemove(self):
2169         self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
2170         self.innerTestQuoting('''Blah blah wrote:
2171 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2172 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2175 This is a followup
2176 ''')
2178     def innerTestQuoting(self, expect):
2179         nodeid = self.doNewIssue()
2181         messages = self.db.issue.get(nodeid, 'messages')
2183         self._handle_mail('''Content-Type: text/plain;
2184   charset="iso-8859-1"
2185 From: richard <richard@test.test>
2186 To: issue_tracker@your.tracker.email.domain.example
2187 Message-Id: <followup_dummy_id>
2188 In-Reply-To: <dummy_test_message_id>
2189 Subject: Re: [issue1] Testing...
2191 Blah blah wrote:
2192 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2193 >  skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2196 This is a followup
2197 ''')
2198         # figure the new message id
2199         newmessages = self.db.issue.get(nodeid, 'messages')
2200         for msg in messages:
2201             newmessages.remove(msg)
2202         messageid = newmessages[0]
2204         self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
2206     def testUserLookup(self):
2207         i = self.db.user.create(username='user1', address='user1@foo.com')
2208         self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
2209         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
2210         i = self.db.user.create(username='user2', address='USER2@foo.com')
2211         self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
2212         self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
2214     def testUserAlternateLookup(self):
2215         i = self.db.user.create(username='user1', address='user1@foo.com',
2216                                 alternate_addresses='user1@bar.com')
2217         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
2218         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
2220     def testUserAlternateSubstringNomatch(self):
2221         i = self.db.user.create(username='user1', address='user1@foo.com',
2222                                 alternate_addresses='x-user1@bar.com')
2223         self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), 0)
2224         self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), 0)
2226     def testUserCreate(self):
2227         i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
2228         self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
2230     def testRFC2822(self):
2231         ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
2232         unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
2233         unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
2234         self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
2235         self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
2237     def testRegistrationConfirmation(self):
2238         otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
2239         self.db.getOTKManager().set(otk, username='johannes')
2240         self._handle_mail('''Content-Type: text/plain;
2241   charset="iso-8859-1"
2242 From: Chef <chef@bork.bork.bork>
2243 To: issue_tracker@your.tracker.email.domain.example
2244 Cc: richard@test.test
2245 Message-Id: <dummy_test_message_id>
2246 Subject: Re: Complete your registration to Roundup issue tracker
2247  -- key %s
2249 This is a test confirmation of registration.
2250 ''' % otk)
2251         self.db.user.lookup('johannes')
2253     def testFollowupOnNonIssue(self):
2254         self.db.keyword.create(name='Foo')
2255         self._handle_mail('''Content-Type: text/plain;
2256   charset="iso-8859-1"
2257 From: richard <richard@test.test>
2258 To: issue_tracker@your.tracker.email.domain.example
2259 Message-Id: <followup_dummy_id>
2260 In-Reply-To: <dummy_test_message_id>
2261 Subject: [keyword1] Testing... [name=Bar]
2263 ''')
2264         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2266     def testResentFrom(self):
2267         nodeid = self._handle_mail('''Content-Type: text/plain;
2268   charset="iso-8859-1"
2269 From: Chef <chef@bork.bork.bork>
2270 Resent-From: mary <mary@test.test>
2271 To: issue_tracker@your.tracker.email.domain.example
2272 Cc: richard@test.test
2273 Message-Id: <dummy_test_message_id>
2274 Subject: [issue] Testing...
2276 This is a test submission of a new issue.
2277 ''')
2278         assert not os.path.exists(SENDMAILDEBUG)
2279         l = self.db.issue.get(nodeid, 'nosy')
2280         l.sort()
2281         self.assertEqual(l, [self.richard_id, self.mary_id])
2282         return nodeid
2284     def testDejaVu(self):
2285         self.assertRaises(IgnoreLoop, self._handle_mail,
2286             '''Content-Type: text/plain;
2287   charset="iso-8859-1"
2288 From: Chef <chef@bork.bork.bork>
2289 X-Roundup-Loop: hello
2290 To: issue_tracker@your.tracker.email.domain.example
2291 Cc: richard@test.test
2292 Message-Id: <dummy_test_message_id>
2293 Subject: Re: [issue] Testing...
2295 Hi, I've been mis-configured to loop messages back to myself.
2296 ''')
2298     def testItsBulkStupid(self):
2299         self.assertRaises(IgnoreBulk, self._handle_mail,
2300             '''Content-Type: text/plain;
2301   charset="iso-8859-1"
2302 From: Chef <chef@bork.bork.bork>
2303 Precedence: bulk
2304 To: issue_tracker@your.tracker.email.domain.example
2305 Cc: richard@test.test
2306 Message-Id: <dummy_test_message_id>
2307 Subject: Re: [issue] Testing...
2309 Hi, I'm on holidays, and this is a dumb auto-responder.
2310 ''')
2312     def testAutoReplyEmailsAreIgnored(self):
2313         self.assertRaises(IgnoreBulk, self._handle_mail,
2314             '''Content-Type: text/plain;
2315   charset="iso-8859-1"
2316 From: Chef <chef@bork.bork.bork>
2317 To: issue_tracker@your.tracker.email.domain.example
2318 Cc: richard@test.test
2319 Message-Id: <dummy_test_message_id>
2320 Subject: Re: [issue] Out of office AutoReply: Back next week
2322 Hi, I am back in the office next week
2323 ''')
2325     def testNoSubject(self):
2326         self.assertRaises(MailUsageError, self._handle_mail,
2327             '''Content-Type: text/plain;
2328   charset="iso-8859-1"
2329 From: Chef <chef@bork.bork.bork>
2330 To: issue_tracker@your.tracker.email.domain.example
2331 Cc: richard@test.test
2332 Reply-To: chef@bork.bork.bork
2333 Message-Id: <dummy_test_message_id>
2335 ''')
2337     #
2338     # TEST FOR INVALID DESIGNATOR HANDLING
2339     #
2340     def testInvalidDesignator(self):
2341         self.assertRaises(MailUsageError, self._handle_mail,
2342             '''Content-Type: text/plain;
2343   charset="iso-8859-1"
2344 From: Chef <chef@bork.bork.bork>
2345 To: issue_tracker@your.tracker.email.domain.example
2346 Subject: [frobulated] testing
2347 Cc: richard@test.test
2348 Reply-To: chef@bork.bork.bork
2349 Message-Id: <dummy_test_message_id>
2351 ''')
2352         self.assertRaises(MailUsageError, self._handle_mail,
2353             '''Content-Type: text/plain;
2354   charset="iso-8859-1"
2355 From: Chef <chef@bork.bork.bork>
2356 To: issue_tracker@your.tracker.email.domain.example
2357 Subject: [issue12345] testing
2358 Cc: richard@test.test
2359 Reply-To: chef@bork.bork.bork
2360 Message-Id: <dummy_test_message_id>
2362 ''')
2364     def testInvalidClassLoose(self):
2365         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2366         nodeid = self._handle_mail('''Content-Type: text/plain;
2367   charset="iso-8859-1"
2368 From: Chef <chef@bork.bork.bork>
2369 To: issue_tracker@your.tracker.email.domain.example
2370 Subject: [frobulated] testing
2371 Cc: richard@test.test
2372 Reply-To: chef@bork.bork.bork
2373 Message-Id: <dummy_test_message_id>
2375 ''')
2376         assert not os.path.exists(SENDMAILDEBUG)
2377         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2378             '[frobulated] testing')
2380     def testInvalidClassLooseReply(self):
2381         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2382         nodeid = self._handle_mail('''Content-Type: text/plain;
2383   charset="iso-8859-1"
2384 From: Chef <chef@bork.bork.bork>
2385 To: issue_tracker@your.tracker.email.domain.example
2386 Subject: Re: [frobulated] testing
2387 Cc: richard@test.test
2388 Reply-To: chef@bork.bork.bork
2389 Message-Id: <dummy_test_message_id>
2391 ''')
2392         assert not os.path.exists(SENDMAILDEBUG)
2393         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2394             '[frobulated] testing')
2396     def testInvalidClassLoose(self):
2397         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2398         nodeid = self._handle_mail('''Content-Type: text/plain;
2399   charset="iso-8859-1"
2400 From: Chef <chef@bork.bork.bork>
2401 To: issue_tracker@your.tracker.email.domain.example
2402 Subject: [issue1234] testing
2403 Cc: richard@test.test
2404 Reply-To: chef@bork.bork.bork
2405 Message-Id: <dummy_test_message_id>
2407 ''')
2408         assert not os.path.exists(SENDMAILDEBUG)
2409         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2410             '[issue1234] testing')
2412     def testClassLooseOK(self):
2413         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2414         self.db.keyword.create(name='Foo')
2415         nodeid = self._handle_mail('''Content-Type: text/plain;
2416   charset="iso-8859-1"
2417 From: Chef <chef@bork.bork.bork>
2418 To: issue_tracker@your.tracker.email.domain.example
2419 Subject: [keyword1] Testing... [name=Bar]
2420 Cc: richard@test.test
2421 Reply-To: chef@bork.bork.bork
2422 Message-Id: <dummy_test_message_id>
2424 ''')
2425         assert not os.path.exists(SENDMAILDEBUG)
2426         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2428     def testClassStrictInvalid(self):
2429         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2430         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2432         message = '''Content-Type: text/plain;
2433   charset="iso-8859-1"
2434 From: Chef <chef@bork.bork.bork>
2435 To: issue_tracker@your.tracker.email.domain.example
2436 Subject: Testing...
2437 Cc: richard@test.test
2438 Reply-To: chef@bork.bork.bork
2439 Message-Id: <dummy_test_message_id>
2441 '''
2442         self.assertRaises(MailUsageError, self._handle_mail, message)
2444     def testClassStrictValid(self):
2445         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2446         self.instance.config.MAILGW_DEFAULT_CLASS = ''
2448         nodeid = self._handle_mail('''Content-Type: text/plain;
2449   charset="iso-8859-1"
2450 From: Chef <chef@bork.bork.bork>
2451 To: issue_tracker@your.tracker.email.domain.example
2452 Subject: [issue] Testing...
2453 Cc: richard@test.test
2454 Reply-To: chef@bork.bork.bork
2455 Message-Id: <dummy_test_message_id>
2457 ''')
2459         assert not os.path.exists(SENDMAILDEBUG)
2460         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
2462     #
2463     # TEST FOR INVALID COMMANDS HANDLING
2464     #
2465     def testInvalidCommands(self):
2466         self.assertRaises(MailUsageError, self._handle_mail,
2467             '''Content-Type: text/plain;
2468   charset="iso-8859-1"
2469 From: Chef <chef@bork.bork.bork>
2470 To: issue_tracker@your.tracker.email.domain.example
2471 Subject: testing [frobulated]
2472 Cc: richard@test.test
2473 Reply-To: chef@bork.bork.bork
2474 Message-Id: <dummy_test_message_id>
2476 ''')
2478     def testInvalidCommandPassthrough(self):
2479         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
2480         nodeid = self._handle_mail('''Content-Type: text/plain;
2481   charset="iso-8859-1"
2482 From: Chef <chef@bork.bork.bork>
2483 To: issue_tracker@your.tracker.email.domain.example
2484 Subject: testing [frobulated]
2485 Cc: richard@test.test
2486 Reply-To: chef@bork.bork.bork
2487 Message-Id: <dummy_test_message_id>
2489 ''')
2490         assert not os.path.exists(SENDMAILDEBUG)
2491         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2492             'testing [frobulated]')
2494     def testInvalidCommandPassthroughLoose(self):
2495         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2496         nodeid = self._handle_mail('''Content-Type: text/plain;
2497   charset="iso-8859-1"
2498 From: Chef <chef@bork.bork.bork>
2499 To: issue_tracker@your.tracker.email.domain.example
2500 Subject: testing [frobulated]
2501 Cc: richard@test.test
2502 Reply-To: chef@bork.bork.bork
2503 Message-Id: <dummy_test_message_id>
2505 ''')
2506         assert not os.path.exists(SENDMAILDEBUG)
2507         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2508             'testing [frobulated]')
2510     def testInvalidCommandPassthroughLooseOK(self):
2511         self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2512         nodeid = self._handle_mail('''Content-Type: text/plain;
2513   charset="iso-8859-1"
2514 From: Chef <chef@bork.bork.bork>
2515 To: issue_tracker@your.tracker.email.domain.example
2516 Subject: testing [assignedto=mary]
2517 Cc: richard@test.test
2518 Reply-To: chef@bork.bork.bork
2519 Message-Id: <dummy_test_message_id>
2521 ''')
2522         assert not os.path.exists(SENDMAILDEBUG)
2523         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2524         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2526     def testCommandDelimiters(self):
2527         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2528         nodeid = self._handle_mail('''Content-Type: text/plain;
2529   charset="iso-8859-1"
2530 From: Chef <chef@bork.bork.bork>
2531 To: issue_tracker@your.tracker.email.domain.example
2532 Subject: testing {assignedto=mary}
2533 Cc: richard@test.test
2534 Reply-To: chef@bork.bork.bork
2535 Message-Id: <dummy_test_message_id>
2537 ''')
2538         assert not os.path.exists(SENDMAILDEBUG)
2539         self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2540         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2542     def testPrefixDelimiters(self):
2543         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2544         self.db.keyword.create(name='Foo')
2545         self._handle_mail('''Content-Type: text/plain;
2546   charset="iso-8859-1"
2547 From: richard <richard@test.test>
2548 To: issue_tracker@your.tracker.email.domain.example
2549 Message-Id: <followup_dummy_id>
2550 In-Reply-To: <dummy_test_message_id>
2551 Subject: {keyword1} Testing... {name=Bar}
2553 ''')
2554         assert not os.path.exists(SENDMAILDEBUG)
2555         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2557     def testCommandDelimitersIgnore(self):
2558         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2559         nodeid = self._handle_mail('''Content-Type: text/plain;
2560   charset="iso-8859-1"
2561 From: Chef <chef@bork.bork.bork>
2562 To: issue_tracker@your.tracker.email.domain.example
2563 Subject: testing [assignedto=mary]
2564 Cc: richard@test.test
2565 Reply-To: chef@bork.bork.bork
2566 Message-Id: <dummy_test_message_id>
2568 ''')
2569         assert not os.path.exists(SENDMAILDEBUG)
2570         self.assertEqual(self.db.issue.get(nodeid, 'title'),
2571             'testing [assignedto=mary]')
2572         self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
2574     def testReplytoMatch(self):
2575         self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2576         nodeid = self.doNewIssue()
2577         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2578   charset="iso-8859-1"
2579 From: Chef <chef@bork.bork.bork>
2580 To: issue_tracker@your.tracker.email.domain.example
2581 Message-Id: <dummy_test_message_id2>
2582 In-Reply-To: <dummy_test_message_id>
2583 Subject: Testing...
2585 Followup message.
2586 ''')
2588         nodeid3 = self._handle_mail('''Content-Type: text/plain;
2589   charset="iso-8859-1"
2590 From: Chef <chef@bork.bork.bork>
2591 To: issue_tracker@your.tracker.email.domain.example
2592 Message-Id: <dummy_test_message_id3>
2593 In-Reply-To: <dummy_test_message_id2>
2594 Subject: Testing...
2596 Yet another message in the same thread/issue.
2597 ''')
2599         self.assertEqual(nodeid, nodeid2)
2600         self.assertEqual(nodeid, nodeid3)
2602     def testHelpSubject(self):
2603         message = '''Content-Type: text/plain;
2604   charset="iso-8859-1"
2605 From: Chef <chef@bork.bork.bork>
2606 To: issue_tracker@your.tracker.email.domain.example
2607 Message-Id: <dummy_test_message_id2>
2608 In-Reply-To: <dummy_test_message_id>
2609 Subject: hElp
2612 '''
2613         self.assertRaises(MailUsageHelp, self._handle_mail, message)
2615     def testMaillistSubject(self):
2616         self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
2617         self.db.keyword.create(name='Foo')
2618         self._handle_mail('''Content-Type: text/plain;
2619   charset="iso-8859-1"
2620 From: Chef <chef@bork.bork.bork>
2621 To: issue_tracker@your.tracker.email.domain.example
2622 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
2623 Cc: richard@test.test
2624 Reply-To: chef@bork.bork.bork
2625 Message-Id: <dummy_test_message_id>
2627 ''')
2629         assert not os.path.exists(SENDMAILDEBUG)
2630         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2632     def testUnknownPrefixSubject(self):
2633         self.db.keyword.create(name='Foo')
2634         self._handle_mail('''Content-Type: text/plain;
2635   charset="iso-8859-1"
2636 From: Chef <chef@bork.bork.bork>
2637 To: issue_tracker@your.tracker.email.domain.example
2638 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
2639 Cc: richard@test.test
2640 Reply-To: chef@bork.bork.bork
2641 Message-Id: <dummy_test_message_id>
2643 ''')
2645         assert not os.path.exists(SENDMAILDEBUG)
2646         self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2648     def testOneCharSubject(self):
2649         message = '''Content-Type: text/plain;
2650   charset="iso-8859-1"
2651 From: Chef <chef@bork.bork.bork>
2652 To: issue_tracker@your.tracker.email.domain.example
2653 Subject: b
2654 Cc: richard@test.test
2655 Reply-To: chef@bork.bork.bork
2656 Message-Id: <dummy_test_message_id>
2658 '''
2659         try:
2660             self._handle_mail(message)
2661         except MailUsageError:
2662             self.fail('MailUsageError raised')
2664     def testIssueidLast(self):
2665         nodeid1 = self.doNewIssue()
2666         nodeid2 = self._handle_mail('''Content-Type: text/plain;
2667   charset="iso-8859-1"
2668 From: mary <mary@test.test>
2669 To: issue_tracker@your.tracker.email.domain.example
2670 Message-Id: <followup_dummy_id>
2671 In-Reply-To: <dummy_test_message_id>
2672 Subject: New title [issue1]
2674 This is a second followup
2675 ''')
2677         assert nodeid1 == nodeid2
2678         self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
2680     def testSecurityMessagePermissionContent(self):
2681         id = self.doNewIssue()
2682         issue = self.db.issue.getnode (id)
2683         self.db.security.addRole(name='Nomsg')
2684         self.db.security.addPermissionToRole('Nomsg', 'Email Access')
2685         for cl in 'issue', 'file', 'keyword':
2686             for p in 'View', 'Edit', 'Create':
2687                 self.db.security.addPermissionToRole('Nomsg', p, cl)
2688         self.db.user.set(self.mary_id, roles='Nomsg')
2689         nodeid = self._handle_mail('''Content-Type: text/plain;
2690   charset="iso-8859-1"
2691 From: Chef <chef@bork.bork.bork>
2692 To: issue_tracker@your.tracker.email.domain.example
2693 Message-Id: <dummy_test_message_id_2>
2694 Subject: [issue%(id)s] Testing... [nosy=+mary]
2696 Just a test reply
2697 '''%locals())
2698         assert os.path.exists(SENDMAILDEBUG)
2699         self.compareMessages(self._get_mail(),
2700 '''FROM: roundup-admin@your.tracker.email.domain.example
2701 TO: chef@bork.bork.bork, richard@test.test
2702 Content-Type: text/plain; charset="utf-8"
2703 Subject: [issue1] Testing...
2704 To: richard@test.test
2705 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2706 Reply-To: Roundup issue tracker
2707  <issue_tracker@your.tracker.email.domain.example>
2708 MIME-Version: 1.0
2709 Message-Id: <dummy_test_message_id_2>
2710 In-Reply-To: <dummy_test_message_id>
2711 X-Roundup-Name: Roundup issue tracker
2712 X-Roundup-Loop: hello
2713 X-Roundup-Issue-Status: chatting
2714 Content-Transfer-Encoding: quoted-printable
2717 Bork, Chef <chef@bork.bork.bork> added the comment:
2719 Just a test reply
2721 ----------
2722 nosy: +mary
2723 status: unread -> chatting
2725 _______________________________________________________________________
2726 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2727 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2728 _______________________________________________________________________
2729 ''')
2731     def testOutlookAttachment(self):
2732         message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2733 Content-class: urn:content-classes:message
2734 MIME-Version: 1.0
2735 Content-Type: multipart/mixed;
2736         boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2737 Subject: Example of a failed outlook attachment e-mail
2738 Date: Tue, 23 Mar 2010 01:43:44 -0700
2739 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2740 X-MS-Has-Attach: yes
2741 X-MS-TNEF-Correlator: 
2742 Thread-Topic: Example of a failed outlook attachment e-mail
2743 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2744 From: "Hugh" <richard@test.test>
2745 To: <richard@test.test>
2746 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2748 This is a multi-part message in MIME format.
2750 ------_=_NextPart_001_01CACA65.40A51CBC
2751 Content-Type: multipart/alternative;
2752         boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2755 ------_=_NextPart_002_01CACA65.40A51CBC
2756 Content-Type: text/plain;
2757         charset="us-ascii"
2758 Content-Transfer-Encoding: quoted-printable
2761 Hi Richard,
2763 I suppose this isn't the exact message that was sent but is a resend of
2764 one of my trial messages that failed.  For your benefit I changed the
2765 subject line and am adding these words to the message body.  Should
2766 still be as problematic, but if you like I can resend an exact copy of a
2767 failed message changing nothing except putting your address instead of
2768 our tracker.
2770 Thanks very much for taking time to look into this.  Much appreciated.
2772  <<battery backup>>=20
2774 ------_=_NextPart_002_01CACA65.40A51CBC
2775 Content-Type: text/html;
2776         charset="us-ascii"
2777 Content-Transfer-Encoding: quoted-printable
2779 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2780 <HTML>
2781 <HEAD>
2782 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2783 charset=3Dus-ascii">
2784 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2785 6.5.7654.12">
2786 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2787 </HEAD>
2788 <BODY>
2789 <!-- Converted from text/rtf format -->
2790 <BR>
2792 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2793 </P>
2795 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2796 that was sent but is a resend of one of my trial messages that =
2797 failed.&nbsp; For your benefit I changed the subject line and am adding =
2798 these words to the message body.&nbsp; Should still be as problematic, =
2799 but if you like I can resend an exact copy of a failed message changing =
2800 nothing except putting your address instead of our tracker.</FONT></P>
2802 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2803 look into this.&nbsp; Much appreciated.</FONT>
2804 </P>
2805 <BR>
2807 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> &lt;&lt;battery =
2808 backup&gt;&gt; </FONT>
2809 </P>
2811 </BODY>
2812 </HTML>
2813 ------_=_NextPart_002_01CACA65.40A51CBC--
2815 ------_=_NextPart_001_01CACA65.40A51CBC
2816 Content-Type: message/rfc822
2817 Content-Transfer-Encoding: 7bit
2819 X-MimeOLE: Produced By Microsoft Exchange V6.5
2820 MIME-Version: 1.0
2821 Content-Type: multipart/alternative;
2822         boundary="----_=_NextPart_003_01CAC15A.29717800"
2823 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2824 Content-class: urn:content-classes:message
2825 Subject: battery backup
2826 Date: Thu, 11 Mar 2010 13:33:43 -0700
2827 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2828 X-MS-Has-Attach: 
2829 X-MS-TNEF-Correlator: 
2830 Thread-Topic: battery backup
2831 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2832 From: "Jerry" <jerry@test.test>
2833 To: "Hugh" <hugh@test.test>
2835 This is a multi-part message in MIME format.
2837 ------_=_NextPart_003_01CAC15A.29717800
2838 Content-Type: text/plain;
2839         charset="iso-8859-1"
2840 Content-Transfer-Encoding: quoted-printable
2842 Dear Hugh,
2843         A car batter has an energy capacity of ~ 500Wh.  A UPS=20
2844 battery is worse than this.
2846 if we need to provied 100kW for 30 minutes that will take 100 car=20
2847 batteries.  This seems like an awful lot of batteries.
2849 Of course I like your idea of making the time 1 minute, so we get to=20
2850 a more modest number of batteries
2852 Jerry
2855 ------_=_NextPart_003_01CAC15A.29717800
2856 Content-Type: text/html;
2857         charset="iso-8859-1"
2858 Content-Transfer-Encoding: quoted-printable
2860 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2861 <HTML>
2862 <HEAD>
2863 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2864 charset=3Diso-8859-1">
2865 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2866 6.5.7654.12">
2867 <TITLE>battery backup</TITLE>
2868 </HEAD>
2869 <BODY>
2870 <!-- Converted from text/plain format -->
2872 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2874 <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT SIZE=3D2>A car =
2875 batter has an energy capacity of ~ 500Wh.&nbsp; A UPS </FONT>
2877 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2878 </P>
2880 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2881 take 100 car </FONT>
2883 <BR><FONT SIZE=3D2>batteries.&nbsp; This seems like an awful lot of =
2884 batteries.</FONT>
2885 </P>
2887 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2888 minute, so we get to </FONT>
2890 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2891 </P>
2893 <P><FONT SIZE=3D2>Jerry</FONT>
2894 </P>
2896 </BODY>
2897 </HTML>
2898 ------_=_NextPart_003_01CAC15A.29717800--
2900 ------_=_NextPart_001_01CACA65.40A51CBC--
2901 '''
2902         nodeid = self._handle_mail(message)
2903         assert not os.path.exists(SENDMAILDEBUG)
2904         msgid = self.db.issue.get(nodeid, 'messages')[0]
2905         self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2906         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2907         fileid = self.db.msg.get(msgid, 'files')[0]
2908         self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2909         fileid = self.db.msg.get(msgid, 'files')[1]
2910         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2912     def testForwardedMessageAttachment(self):
2913         message = '''Return-Path: <rgg@test.test>
2914 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2915 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2916 Message-ID: <4BC4F9C7.50409@test.test>
2917 Date: Wed, 14 Apr 2010 09:09:59 +1000
2918 From: Rupert Goldie <rgg@test.test>
2919 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2920 MIME-Version: 1.0
2921 To: ekit issues <issues@test.test>
2922 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2923 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2925 This is a multi-part message in MIME format.
2926 --------------000807090608060304010403
2927 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2928 Content-Transfer-Encoding: 7bit
2930 Catch this exception and log it without emailing.
2932 --------------000807090608060304010403
2933 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2934 Content-Transfer-Encoding: 7bit
2935 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2937 Return-Path: <ektravj@test.test>
2938 X-Sieve: CMU Sieve 2.2
2939 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2940 X-Virus-Scanned: by amavisd-new at ekit.com
2941 To: facebook-errors@test.test
2942 From: ektravj@test.test
2943 Subject: PHP ERROR (fb)
2944 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2945 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2947 [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
2948 Stack trace:
2949 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2950 #1 {main}
2951  thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2954 --------------000807090608060304010403--
2955 '''
2956         nodeid = self._handle_mail(message)
2957         assert not os.path.exists(SENDMAILDEBUG)
2958         msgid = self.db.issue.get(nodeid, 'messages')[0]
2959         self.assertEqual(self.db.msg.get(msgid, 'content'),
2960             'Catch this exception and log it without emailing.')
2961         self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2962         fileid = self.db.msg.get(msgid, 'files')[0]
2963         self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2965 pgp_test_key = """
2966 -----BEGIN PGP PRIVATE KEY BLOCK-----
2967 Version: GnuPG v1.4.10 (GNU/Linux)
2969 lQOYBE6NqtsBCADG3UUMYxjwUOpDDVvr0Y8qkvKsgdF79en1zfHtRYlmZc+EJxg8
2970 53CCFGReQWJwOjyP3/SLJwJqfiPR7MAYAqJsm/4U2lxF7sIlEnlrRpFuvB625KOQ
2971 oedCkI4nLa+4QAXHxVX2qLx7es3r2JAoitZLX7ZtUB7qGSRh98DmdAgCY3CFN7iZ
2972 w6xpvIU+LNbsHSo1sf8VP6z7NHQFacgrVvLyRJ4C5lTPU42iM5E6HKxYFExNV3Rn
2973 +2G0bsuiifHV6nJQD73onjwcC6tU97W779dllHlhG3SSP0KlnwmCCvPMlQvROk0A
2974 rLyzKWcUpZwK1aLRYByjFMH9WYXRkhf08bkDABEBAAEAB/9dcmSb6YUyiBNM5t4m
2975 9hZcXykBvw79PRVvmBLy+BYUtArLgsN0+xx3Q7XWRMtJCVSkFw0GxpHwEM4sOyAZ
2976 KEPC3ZqLmgB6LDO2z/OWYVa9vlCAiPgDYtEVCnCCIInN/ue4dBZtDeVj8NUK2n0D
2977 UBpa2OMUgu3D+4SJNK7EnAmXdOaP6yfe6SXwcQfti8UoSFMJRkQkbY1rm/6iPfON
2978 t2RBAc7jW4eRzdciWCfvJfMSj9cqxTBQWz5vVadeY9Bm/IKw1HiKNBrJratq2v+D
2979 VGr0EkE9oOa5zbgZt2CFvknE4YhGmv81xFdK5GXr8L7nluZrePMblWbkI2ICTbV0
2980 RKLhBADYLvyDFX3cCoFzWmCl5L32G6LLfTt0yU0eUHcAzXd7QjOZN289HWYEmdVi
2981 kpxQPDxhWz+m8qt0HJGFl2+BKpZJBaT/L5AcqTBODxarxCSBTIVhCjD/46XvLY0h
2982 b2ZnG8HSLyFdRj07vk+qTvcF58qUuYFSLIF2t2imTCR/PwR/LwQA632vn2/7KIHj
2983 DR0O+G9eccTtAfX4TN4Q4Ua3WByClLZu/LSAenCLZ1CHVABEH6dwwjEARLeNUdLi
2984 Xy5KKlpr2vkoh96fnw0r2yg7dlBXq4yQKjJBXwNaKpuvqgzd8en0zJGLXxzt0NT3
2985 H+QNIP2WZMJSDQcDh3HhQrH0IeNdDm0D/iyJgSMXvqjm+KhYIa3xiloQsCRlDNm+
2986 XC7Eo5hsjvBaIKba6o9oL9oEiSVUFryPWKWIpi0P7/F5voJL6KFSZTor3x3o9CcC
2987 qHyqMHfNL23EAVJulySfPYLC7S3QB+tCBLXmKxb/YXCSLVi/UDzVgvWN6KIknZg2
2988 6uDLUzPbzDGjOZ20K1JvdW5kdXAgVGVzdGtleSA8cm91bmR1cC1hZG1pbkBleGFt
2989 cGxlLmNvbT6JATgEEwECACIFAk6NqtsCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
2990 AheAAAoJEFrc/VYxw4dBG7oIAMCU9sRjK0dS7z/IGJ8KcCOQNN674AooJLn+J9Ew
2991 BT6/WxMY13nm/iK0uX2sOGnnXdg1PJ15IvD8zB5wXLbe25t6oRl5G58vmeKEyjc8
2992 QTB43/c8EsqY1ob7EVcuhrJCSS/JM8ApzQyXrh2QNmS+mBCJcx74MeipE6mNVT9j
2993 VscizixdFjqvJLkbW1kGac3Wj+c3ICNUIp0lbwb+Ve2rXlU+iXHEDqaVJDMEppme
2994 gDiZl+bKYrqljhZkH9Slv55uqqXUSg1SmTm/2orHUdAmDc6Y6azKNqQEqD2B0JyT
2995 jTJQJVMl5Oln63aZDCTxHkoqn8q06OjLJRD4on7jlanZEladA5gETo2q2wEIALEF
2996 poVkZrnqme2M8FObrQyVB+ZYT2mox56WLyInbxVFDg20qqIvQfVE0P69Yuf1OXkj
2997 q7bNI03Jvo+uzxpztOKPDo7tnbQ7bXbOmq3n4wUoN29NMrYNg6tF1ubEv1WwYUMw
2998 7LfF4BLMETXpT0JElV1+awfP9rrGiyWkH4enG612HT+1OoA0R0nNH0kslD6OhdoR
2999 VDqkyiCmdY9x176EhzhL3vCoN6ywRVTfFbAJiMv9UDzxs0SStmVOK/l5XLfWQO6f
3000 9boAHihpnxEfPIJhsD+FpVKVf3g85qWAjh2BfuzdW79vjLBdTHJQxg4HdhliWbXg
3001 PjjrVEgWEFVc+NDlNb0AEQEAAQAH/A1a6sbniI8q3DVoIP19zN7FI5UaQSuB2Jrl
3002 +Q+vlUQv3dvk2cwQmqj2vyRo2gcRS3u7LYpGDGLNqfshv22JyzId2YWo9vE7sTTP
3003 E4EJRz8CsLlMmVsoxoVBE0cnvXOpMef6z0ZyFEdMGVmi4iA9bQi3r+V6qBehQQA0
3004 U034VTCPN4yvWyq6TWsABesOx48nkQ5TlduIq2ZGNCR8Vd1fe6vGM7YXyQWxy5ke
3005 guqmph73H2bOB6hSuUnyBFKtinrF9MbCGA0PqheUVqy0p7og6x/pEoAVkKBJ9Ki+
3006 ePuQtBl5h9e3SbiN+r7aa6T0Ygx/7igl4eWPfvJYIXYXc4aKiwEEANEa5rBoN7Ta
3007 ED+R47Rg9w/EW3VDQ6R3Szy1rvIKjC6JlDyKlGgTeWEFjDeTwCB4xU7YtxVpt6bk
3008 b7RBtDkRck2+DwnscutA7Uxn267UxzNUd1IxhUccRFRfRS7OEnmlVmaLUnOeHHwe
3009 OrZyRSiNVnh0QABEJnwNjX4m139v6YD9BADYuM5XCawI63pYa3/l7UX9H5EH95OZ
3010 G9Hw7pXQ/YJYerSxRx+2q0+koRcdoby1TVaRrdDC+kOm3PI7e66S5rnaZ1FZeYQP
3011 nVYzyGqNnsvncs24kYBL8DYaDDfdm7vfzSEqia0VNqZ4TMbwJLk5f5Ys4WOF791G
3012 LPJgrAPG1jgDwQQAovKbw0u6blIUKsUYOLsviaLCyFC9DwaHqIZwjy8omnh7MaKE
3013 7+MXxJpfcVqFifj3CmqMdSmTfkgbKQPAI46Q1OKWvkvUxEvi7WATo4taEXupRFL5
3014 jnL8c4h46z8UpMX2CMwWU0k1Et/zlBoYy7gNON7tF2/uuN18zWFBlD72HuM9HIkB
3015 HwQYAQIACQUCTo2q2wIbDAAKCRBa3P1WMcOHQYI+CACDXJf1e695LpcsrVxKgiQr
3016 9fTbNJYB+tjbnd9vas92Gz1wZcQV9RjLkYQeEbOpWQud/1UeLRsFECMj7kbgAEqz
3017 7fIO4SeN8hFEvvZ+lI0AoBi4XvuUcCm5kvAodvmF8M9kQiUzF1gm+R9QQeJFDLpW
3018 8Gg7J3V3qM+N0FuXrypYcsEv7n/RJ1n+lhTW5hFzKBlNL4WrAhY/QsXEbmdsa478
3019 tzuHlETtjMm4g4DgppUdlCMegcpjjC9zKsN5xFOQmNMTO/6rPFUqk3k3T6I0LV4O
3020 zm4xNC+wwAA69ibnbrY1NR019et7RYW+qBudGbpJB1ABzkf/NsaCj6aTaubt7PZP
3021 =3uFZ
3022 -----END PGP PRIVATE KEY BLOCK-----
3023 """
3025 john_doe_key = """
3026 -----BEGIN PGP PRIVATE KEY BLOCK-----
3027 Version: GnuPG v1.4.10 (GNU/Linux)
3029 lQHYBE6NwvABBACxg7QqV2qHywwM3wae6HAHJVEo7EeYA6Lv0pZlW3Aw4CCCnpgJ
3030 jA7CekGFcmGmoCaN9ezuVAPTgUlK4yt8a7P6cT0vw1q341Om9IEKAu59RpNZN/H9
3031 6GfZ95bU51W/hdTFysH1DRwbCR3MowvLeA6Pk4cZlPsYHD0SD3De2i1BewARAQAB
3032 AAP+IRi4L6jKwPS3k3LFrj0SHhL0Fdgv5QTQjTxLNCyfN02iYhglqqoFWncm3jWc
3033 RU/YwGEYwrrBV97kBmVihzkhfgFRsxynE9PMGKKEAuRcAl21RPJDFA6Dlnp6M2No
3034 rR6eoAhrlZ8+KsK9JaXSMalzO/Yh4u3mOinq3f3XL96wAEkCAMAxeZMF5pnXARNR
3035 Y7u2clhNNnLuf+BzpENCFMaWzWPyTcvbf4xNK7ZHPxFVZpX5/qAPJ8rnTaOTHxnN
3036 5PgqbO8CAOxyrTw/muakTJLg+FXdn8BgxZGJXMT7KmkU9SReefjo7c1WlnZxKIAy
3037 6vLIG8WMGpdfCFDve0YLr/GGyDtOjDUB/RN3gn6qnAJThBnVk2wESZVx41fihbIF
3038 ACCKc9heFskzwurtvvp+bunM3quwrSH1hWvxiWJlDmGSn8zQFypGChifgLQZSm9o
3039 biBEb2UgPGpvaG5AdGVzdC50ZXN0Poi4BBMBAgAiBQJOjcLwAhsDBgsJCAcDAgYV
3040 CAIJCgsEFgIDAQIeAQIXgAAKCRC/z7qg+FujnPWiA/9T5SOGraRNIVVIyvJvYwkG
3041 OTAfQ0K3QMlLoQMPmaEbx9Q+isF15M9sOMcl1XGO4UNWuCPIIN8z/y/OLgAB0ZuL
3042 GlnAPPOOZ+MlaUXiMYo8oi416QZrMDf2H/Nkc10csiXm+zMl8RqeIQBEeljNyJ+t
3043 MG1EWn/PHTwFTd/VePuQdJ0B2AROjcLwAQQApw+72jKy0/wqg5SAtnVSkA1F3Jna
3044 /OG+ufz5dX57jkMFRvFoksWIWqHmiCjdE5QV8j+XTnjElhLsmrgjl7aAFveb30R6
3045 ImmcpKMN31vAp4RZlnyYbYUCY4IXFuz3n1CaUL+mRx5yNJykrZNfpWNf2pwozkZq
3046 lcDI69ymIW5acXUAEQEAAQAD/R7Jdf98l1scngMYo228ikYUxBqm2eX/fiQNXDWM
3047 ZR2u+TJ9O53MvFejfXX7Pd6lTDQUBwDFncjgXO0YYSrMzabhqpqoKLqOIpZmBuWC
3048 Hh1lvcFoIYoDR2LkiJ9EPBUEVUBDsUO8ajkILEE3G+DDpCaf9Vo82lCVyhDESqyt
3049 v4lxAgDOLpoq1Whv5Ejr6FifTWytCiQjH2P1SmePlQmy6oEJRUYA1t4zYrzCJUX8
3050 VAvPjh9JXilP6mhDbyQArWllewV9AgDPbVOf75ktRwfhje26tZsukqWYJCc1XvoH
3051 3PTzA7vH1HZZq7dvxa87PiSnkOLEsIAsI+4jpeMxpPlQRxUvHf1ZAf9rK3v3HMJ/
3052 2xVzwK24Oaj+g2O7D/fdqtLFGe5S5JobnTyp9xArDAhaZ/AKfDMYjUIKMP+bdNAf
3053 y8fQUtuawFltm1GInwQYAQIACQUCTo3C8AIbDAAKCRC/z7qg+FujnDzYA/9EU6Pv
3054 Ci1+DCtxjnq7IOvOjqExhFNGvN9Dw17Tl8HcyW3if9v5RxeSWYKl0DhzVdzMQgH/
3055 78q4F4W1q2IkB7SCpXizHLIc3eh8iZkbWZE+CGPvTpqyF03Yi16qhxpAbkGs2Yhq
3056 jTx5oJ4CL5fybBOZLg+BTlK4HIee6xEcbNoq+A==
3057 =ZKBW
3058 -----END PGP PRIVATE KEY BLOCK-----
3059 """
3061 ownertrust = """
3062 723762CD5A5FECB76DC72DF85ADCFD5631C38741:6:
3063 2940C247A1FBAD508A1AF24BBFCFBAA0F85BA39C:6:
3064 """
3066 class MailgwPGPTestCase(MailgwTestAbstractBase):
3067     pgphome = 'pgp-test-home'
3068     def setUp(self):
3069         MailgwTestAbstractBase.setUp(self)
3070         self.db.security.addRole (name = 'pgp', description = 'PGP Role')
3071         self.instance.config['PGP_HOMEDIR'] = self.pgphome
3072         self.instance.config['PGP_ROLES'] = 'pgp'
3073         self.instance.config['PGP_ENABLE'] = True
3074         self.db.user.set(self.john_id, roles='User,pgp')
3075         os.mkdir(self.pgphome)
3076         os.environ['GNUPGHOME'] = self.pgphome
3077         ctx = pyme.core.Context()
3078         key = pyme.core.Data(pgp_test_key)
3079         ctx.op_import(key)
3080         key = pyme.core.Data(john_doe_key)
3081         ctx.op_import(key)
3082         # trust-modelling with pyme isn't working in 0.8.1
3083         # based on libgpgme11 1.2.0, also tried in C -- same thing.
3084         otrust = os.popen ('gpg --import-ownertrust 2> /dev/null', 'w')
3085         otrust.write(ownertrust)
3086         otrust.close()
3088     def tearDown(self):
3089         MailgwTestAbstractBase.tearDown(self)
3090         if os.path.exists(self.pgphome):
3091             shutil.rmtree(self.pgphome)
3093     def testUnsignedMessage(self):
3094         self.assertRaises(MailUsageError, self._handle_mail,
3095             '''Content-Type: text/plain;
3096   charset="iso-8859-1"
3097 From: John Doe <john@test.test>
3098 To: issue_tracker@your.tracker.email.domain.example
3099 Message-Id: <dummy_test_message_id>
3100 Subject: [issue] Testing non-signed message...
3102 This is no pgp signed message.
3103 ''')
3105     def testSignedMessage(self):
3106         nodeid = self._handle_mail('''Content-Disposition: inline
3107 From: John Doe <john@test.test>
3108 To: issue_tracker@your.tracker.email.domain.example
3109 Subject: [issue] Testing signed message...
3110 Content-Type: multipart/signed; micalg=pgp-sha1;
3111         protocol="application/pgp-signature"; boundary="cWoXeonUoKmBZSoM"
3114 --cWoXeonUoKmBZSoM
3115 Content-Type: text/plain; charset=us-ascii
3116 Content-Disposition: inline
3118 This is a pgp signed message.
3120 --cWoXeonUoKmBZSoM
3121 Content-Type: application/pgp-signature; name="signature.asc"
3122 Content-Description: Digital signature
3123 Content-Disposition: inline
3125 -----BEGIN PGP SIGNATURE-----
3126 Version: GnuPG v1.4.10 (GNU/Linux)
3128 iJwEAQECAAYFAk6N4A4ACgkQv8+6oPhbo5x5nAP/d7R7SxTvLoVESI+1r7eDXp1J
3129 LvBVU2EF3YFYKBHMLcWmjG92fNjnHX6NENTEhTeBynba5IPEwUfITC+7PmgPmQkA
3130 VXnFZnwraHxsYgyFsVFN1kkTSbwRUlWl9+nTEsr0yBLTpZN0QSIDcwu+i/xVcg+t
3131 ZQ4K6R3m3AOw7BLdvZs=
3132 =wpYk
3133 -----END PGP SIGNATURE-----
3135 --cWoXeonUoKmBZSoM--
3136 ''')
3137         m = self.db.issue.get (nodeid, 'messages') [0]
3138         self.assertEqual(self.db.msg.get(m, 'content'), 
3139             'This is a pgp signed message.')
3141 def test_suite():
3142     suite = unittest.TestSuite()
3143     suite.addTest(unittest.makeSuite(MailgwTestCase))
3144     if pyme is not None:
3145         suite.addTest(unittest.makeSuite(MailgwPGPTestCase))
3146     else:
3147         print "Skipping PGP tests"
3148     return suite
3150 if __name__ == '__main__':
3151     runner = unittest.TextTestRunner()
3152     unittest.main(testRunner=runner)
3154 # vim: set filetype=python sts=4 sw=4 et si :