e0789ae2d015f6b2ab51ae3172605e22ea4ff89e
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
17 from email.parser import FeedParser
20 try:
21 import pyme, pyme.core
22 except ImportError:
23 pyme = None
26 from cStringIO import StringIO
28 if not os.environ.has_key('SENDMAILDEBUG'):
29 os.environ['SENDMAILDEBUG'] = 'mail-test.log'
30 SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
32 from roundup import mailgw, i18n, roundupdb
33 from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \
34 parseContent, IgnoreLoop, IgnoreBulk, MailUsageError, MailUsageHelp
35 from roundup import init, instance, password, rfc2822, __version__
36 from roundup.anypy.sets_ import set
38 #import db_test_base
39 import memorydb
41 class Message(rfc822.Message):
42 """String-based Message class with equivalence test."""
43 def __init__(self, s):
44 rfc822.Message.__init__(self, StringIO(s.strip()))
46 def __eq__(self, other):
47 return (self.dict == other.dict and
48 self.fp.read() == other.fp.read())
50 class Tracker(object):
51 def open(self, journaltag):
52 return self.db
54 class DiffHelper:
55 def compareMessages(self, new, old):
56 """Compare messages for semantic equivalence."""
57 new, old = Message(new), Message(old)
59 # all Roundup-generated messages have "Precedence: bulk"
60 old['Precedence'] = 'bulk'
62 # don't try to compare the date
63 del new['date'], old['date']
65 if not new == old:
66 res = []
68 replace = {}
69 for key in new.keys():
70 if key.startswith('from '):
71 # skip the unix from line
72 continue
73 if key.lower() == 'x-roundup-version':
74 # version changes constantly, so handle it specially
75 if new[key] != __version__:
76 res.append(' %s: %r != %r' % (key, __version__,
77 new[key]))
78 elif key.lower() == 'content-type' and 'boundary=' in new[key]:
79 # handle mime messages
80 newmime = new[key].split('=',1)[-1].strip('"')
81 oldmime = old.get(key, '').split('=',1)[-1].strip('"')
82 replace ['--' + newmime] = '--' + oldmime
83 replace ['--' + newmime + '--'] = '--' + oldmime + '--'
84 elif new.get(key, '') != old.get(key, ''):
85 res.append(' %s: %r != %r' % (key, old.get(key, ''),
86 new.get(key, '')))
88 body_diff = self.compareStrings(new.fp.read(), old.fp.read(),
89 replace=replace)
90 if body_diff:
91 res.append('')
92 res.extend(body_diff)
94 if res:
95 res.insert(0, 'Generated message not correct (diff follows, expected vs. actual):')
96 raise AssertionError, '\n'.join(res)
98 def compareStrings(self, s2, s1, replace={}):
99 '''Note the reversal of s2 and s1 - difflib.SequenceMatcher wants
100 the first to be the "original" but in the calls in this file,
101 the second arg is the original. Ho hum.
102 Do replacements over the replace dict -- used for mime boundary
103 '''
104 l1 = s1.strip().split('\n')
105 l2 = [replace.get(i,i) for i in s2.strip().split('\n')]
106 if l1 == l2:
107 return
108 s = difflib.SequenceMatcher(None, l1, l2)
109 res = []
110 for value, s1s, s1e, s2s, s2e in s.get_opcodes():
111 if value == 'equal':
112 for i in range(s1s, s1e):
113 res.append(' %s'%l1[i])
114 elif value == 'delete':
115 for i in range(s1s, s1e):
116 res.append('- %s'%l1[i])
117 elif value == 'insert':
118 for i in range(s2s, s2e):
119 res.append('+ %s'%l2[i])
120 elif value == 'replace':
121 for i, j in zip(range(s1s, s1e), range(s2s, s2e)):
122 res.append('- %s'%l1[i])
123 res.append('+ %s'%l2[j])
125 return res
127 class MailgwTestAbstractBase(unittest.TestCase, DiffHelper):
128 count = 0
129 schema = 'classic'
130 def setUp(self):
131 self.old_translate_ = mailgw._
132 roundupdb._ = mailgw._ = i18n.get_translation(language='C').gettext
133 self.__class__.count = self.__class__.count + 1
135 # and open the database / "instance"
136 self.db = memorydb.create('admin')
137 self.instance = Tracker()
138 self.instance.db = self.db
139 self.instance.config = self.db.config
140 self.instance.MailGW = MailGW
142 self.chef_id = self.db.user.create(username='Chef',
143 address='chef@bork.bork.bork', realname='Bork, Chef', roles='User')
144 self.richard_id = self.db.user.create(username='richard',
145 address='richard@test.test', roles='User')
146 self.mary_id = self.db.user.create(username='mary',
147 address='mary@test.test', roles='User', realname='Contrary, Mary')
148 self.john_id = self.db.user.create(username='john',
149 address='john@test.test', roles='User', realname='John Doe',
150 alternate_addresses='jondoe@test.test\njohn.doe@test.test')
151 self.rgg_id = self.db.user.create(username='rgg',
152 address='rgg@test.test', roles='User')
154 def tearDown(self):
155 roundupdb._ = mailgw._ = self.old_translate_
156 if os.path.exists(SENDMAILDEBUG):
157 os.remove(SENDMAILDEBUG)
158 self.db.close()
160 def _create_mailgw(self, message, args=()):
161 class MailGW(self.instance.MailGW):
162 def handle_message(self, message):
163 return self._handle_message(message)
164 handler = MailGW(self.instance, args)
165 handler.db = self.db
166 return handler
168 def _handle_mail(self, message, args=(), trap_exc=0):
169 handler = self._create_mailgw(message, args)
170 handler.trapExceptions = trap_exc
171 return handler.main(StringIO(message))
173 def _get_mail(self):
174 f = open(SENDMAILDEBUG)
175 try:
176 return f.read()
177 finally:
178 f.close()
180 # Normal test-case used for both non-pgp test and a test while pgp
181 # is enabled, so this test is run in both test suites.
182 def testEmptyMessage(self):
183 nodeid = self._handle_mail('''Content-Type: text/plain;
184 charset="iso-8859-1"
185 From: Chef <chef@bork.bork.bork>
186 To: issue_tracker@your.tracker.email.domain.example
187 Cc: richard@test.test
188 Reply-To: chef@bork.bork.bork
189 Message-Id: <dummy_test_message_id>
190 Subject: [issue] Testing...
192 ''')
193 assert not os.path.exists(SENDMAILDEBUG)
194 self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
197 class MailgwTestCase(MailgwTestAbstractBase):
199 def testMessageWithFromInIt(self):
200 nodeid = self._handle_mail('''Content-Type: text/plain;
201 charset="iso-8859-1"
202 From: Chef <chef@bork.bork.bork>
203 To: issue_tracker@your.tracker.email.domain.example
204 Cc: richard@test.test
205 Reply-To: chef@bork.bork.bork
206 Message-Id: <dummy_test_message_id>
207 Subject: [issue] Testing...
209 From here to there!
210 ''')
211 assert not os.path.exists(SENDMAILDEBUG)
212 msgid = self.db.issue.get(nodeid, 'messages')[0]
213 self.assertEqual(self.db.msg.get(msgid, 'content'), 'From here to there!')
215 def testNoMessageId(self):
216 self.instance.config['MAIL_DOMAIN'] = 'example.com'
217 nodeid = self._handle_mail('''Content-Type: text/plain;
218 charset="iso-8859-1"
219 From: Chef <chef@bork.bork.bork>
220 To: issue_tracker@your.tracker.email.domain.example
221 Cc: richard@test.test
222 Reply-To: chef@bork.bork.bork
223 Subject: [issue] Testing...
225 Hi there!
226 ''')
227 assert not os.path.exists(SENDMAILDEBUG)
228 msgid = self.db.issue.get(nodeid, 'messages')[0]
229 messageid = self.db.msg.get(msgid, 'messageid')
230 x1, x2 = messageid.split('@')
231 self.assertEqual(x2, 'example.com>')
232 x = x1.split('.')[-1]
233 self.assertEqual(x, 'issueNone')
234 nodeid = self._handle_mail('''Content-Type: text/plain;
235 charset="iso-8859-1"
236 From: Chef <chef@bork.bork.bork>
237 To: issue_tracker@your.tracker.email.domain.example
238 Subject: [issue%(nodeid)s] Testing...
240 Just a test reply
241 '''%locals())
242 msgid = self.db.issue.get(nodeid, 'messages')[-1]
243 messageid = self.db.msg.get(msgid, 'messageid')
244 x1, x2 = messageid.split('@')
245 self.assertEqual(x2, 'example.com>')
246 x = x1.split('.')[-1]
247 self.assertEqual(x, "issue%s"%nodeid)
249 def testOptions(self):
250 nodeid = self._handle_mail('''Content-Type: text/plain;
251 charset="iso-8859-1"
252 From: Chef <chef@bork.bork.bork>
253 To: issue_tracker@your.tracker.email.domain.example
254 Message-Id: <dummy_test_message_id>
255 Reply-To: chef@bork.bork.bork
256 Subject: [issue] Testing...
258 Hi there!
259 ''', (('-C', 'issue'), ('-S', 'status=chatting;priority=critical')))
260 self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
261 self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
263 def testOptionsMulti(self):
264 nodeid = self._handle_mail('''Content-Type: text/plain;
265 charset="iso-8859-1"
266 From: Chef <chef@bork.bork.bork>
267 To: issue_tracker@your.tracker.email.domain.example
268 Message-Id: <dummy_test_message_id>
269 Reply-To: chef@bork.bork.bork
270 Subject: [issue] Testing...
272 Hi there!
273 ''', (('-C', 'issue'), ('-S', 'status=chatting'), ('-S', 'priority=critical')))
274 self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
275 self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
277 def testOptionClass(self):
278 nodeid = self._handle_mail('''Content-Type: text/plain;
279 charset="iso-8859-1"
280 From: Chef <chef@bork.bork.bork>
281 To: issue_tracker@your.tracker.email.domain.example
282 Message-Id: <dummy_test_message_id>
283 Reply-To: chef@bork.bork.bork
284 Subject: [issue] Testing... [status=chatting;priority=critical]
286 Hi there!
287 ''', (('-c', 'issue'),))
288 self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
289 self.assertEqual(self.db.issue.get(nodeid, 'status'), '3')
290 self.assertEqual(self.db.issue.get(nodeid, 'priority'), '1')
292 def doNewIssue(self):
293 nodeid = self._handle_mail('''Content-Type: text/plain;
294 charset="iso-8859-1"
295 From: Chef <chef@bork.bork.bork>
296 To: issue_tracker@your.tracker.email.domain.example
297 Cc: richard@test.test
298 Message-Id: <dummy_test_message_id>
299 Subject: [issue] Testing...
301 This is a test submission of a new issue.
302 ''')
303 assert not os.path.exists(SENDMAILDEBUG)
304 l = self.db.issue.get(nodeid, 'nosy')
305 l.sort()
306 self.assertEqual(l, [self.chef_id, self.richard_id])
307 return nodeid
309 def testNewIssue(self):
310 self.doNewIssue()
312 def testNewIssueNosy(self):
313 self.instance.config.ADD_AUTHOR_TO_NOSY = 'yes'
314 nodeid = self._handle_mail('''Content-Type: text/plain;
315 charset="iso-8859-1"
316 From: Chef <chef@bork.bork.bork>
317 To: issue_tracker@your.tracker.email.domain.example
318 Cc: richard@test.test
319 Message-Id: <dummy_test_message_id>
320 Subject: [issue] Testing...
322 This is a test submission of a new issue.
323 ''')
324 assert not os.path.exists(SENDMAILDEBUG)
325 l = self.db.issue.get(nodeid, 'nosy')
326 l.sort()
327 self.assertEqual(l, [self.chef_id, self.richard_id])
329 def testAlternateAddress(self):
330 self._handle_mail('''Content-Type: text/plain;
331 charset="iso-8859-1"
332 From: John Doe <john.doe@test.test>
333 To: issue_tracker@your.tracker.email.domain.example
334 Message-Id: <dummy_test_message_id>
335 Subject: [issue] Testing...
337 This is a test submission of a new issue.
338 ''')
339 userlist = self.db.user.list()
340 assert not os.path.exists(SENDMAILDEBUG)
341 self.assertEqual(userlist, self.db.user.list(),
342 "user created when it shouldn't have been")
344 def testNewIssueNoClass(self):
345 self._handle_mail('''Content-Type: text/plain;
346 charset="iso-8859-1"
347 From: Chef <chef@bork.bork.bork>
348 To: issue_tracker@your.tracker.email.domain.example
349 Cc: richard@test.test
350 Message-Id: <dummy_test_message_id>
351 Subject: Testing...
353 This is a test submission of a new issue.
354 ''')
355 assert not os.path.exists(SENDMAILDEBUG)
357 def testNewIssueAuthMsg(self):
358 # TODO: fix the damn config - this is apalling
359 self.db.config.MESSAGES_TO_AUTHOR = 'yes'
360 self._handle_mail('''Content-Type: text/plain;
361 charset="iso-8859-1"
362 From: Chef <chef@bork.bork.bork>
363 To: issue_tracker@your.tracker.email.domain.example
364 Message-Id: <dummy_test_message_id>
365 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
367 This is a test submission of a new issue.
368 ''')
369 self.compareMessages(self._get_mail(),
370 '''FROM: roundup-admin@your.tracker.email.domain.example
371 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
372 Content-Type: text/plain; charset="utf-8"
373 Subject: [issue1] Testing...
374 To: chef@bork.bork.bork, mary@test.test, richard@test.test
375 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
376 Reply-To: Roundup issue tracker
377 <issue_tracker@your.tracker.email.domain.example>
378 MIME-Version: 1.0
379 Message-Id: <dummy_test_message_id>
380 X-Roundup-Name: Roundup issue tracker
381 X-Roundup-Loop: hello
382 X-Roundup-Issue-Status: unread
383 Content-Transfer-Encoding: quoted-printable
386 New submission from Bork, Chef <chef@bork.bork.bork>:
388 This is a test submission of a new issue.
390 ----------
391 assignedto: richard
392 messages: 1
393 nosy: Chef, mary, richard
394 status: unread
395 title: Testing...
397 _______________________________________________________________________
398 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
399 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
400 _______________________________________________________________________
401 ''')
403 def testNewIssueNoAuthorInfo(self):
404 self.db.config.MAIL_ADD_AUTHORINFO = 'no'
405 self._handle_mail('''Content-Type: text/plain;
406 charset="iso-8859-1"
407 From: Chef <chef@bork.bork.bork>
408 To: issue_tracker@your.tracker.email.domain.example
409 Message-Id: <dummy_test_message_id>
410 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
412 This is a test submission of a new issue.
413 ''')
414 self.compareMessages(self._get_mail(),
415 '''FROM: roundup-admin@your.tracker.email.domain.example
416 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
417 Content-Type: text/plain; charset="utf-8"
418 Subject: [issue1] Testing...
419 To: mary@test.test, richard@test.test
420 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
421 Reply-To: Roundup issue tracker
422 <issue_tracker@your.tracker.email.domain.example>
423 MIME-Version: 1.0
424 Message-Id: <dummy_test_message_id>
425 X-Roundup-Name: Roundup issue tracker
426 X-Roundup-Loop: hello
427 X-Roundup-Issue-Status: unread
428 Content-Transfer-Encoding: quoted-printable
430 This is a test submission of a new issue.
432 ----------
433 assignedto: richard
434 messages: 1
435 nosy: Chef, mary, richard
436 status: unread
437 title: Testing...
439 _______________________________________________________________________
440 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
441 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
442 _______________________________________________________________________
443 ''')
445 def testNewIssueNoAuthorEmail(self):
446 self.db.config.MAIL_ADD_AUTHOREMAIL = 'no'
447 self._handle_mail('''Content-Type: text/plain;
448 charset="iso-8859-1"
449 From: Chef <chef@bork.bork.bork>
450 To: issue_tracker@your.tracker.email.domain.example
451 Message-Id: <dummy_test_message_id>
452 Subject: [issue] Testing... [nosy=mary; assignedto=richard]
454 This is a test submission of a new issue.
455 ''')
456 self.compareMessages(self._get_mail(),
457 '''FROM: roundup-admin@your.tracker.email.domain.example
458 TO: chef@bork.bork.bork, mary@test.test, richard@test.test
459 Content-Type: text/plain; charset="utf-8"
460 Subject: [issue1] Testing...
461 To: mary@test.test, richard@test.test
462 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
463 Reply-To: Roundup issue tracker
464 <issue_tracker@your.tracker.email.domain.example>
465 MIME-Version: 1.0
466 Message-Id: <dummy_test_message_id>
467 X-Roundup-Name: Roundup issue tracker
468 X-Roundup-Loop: hello
469 X-Roundup-Issue-Status: unread
470 Content-Transfer-Encoding: quoted-printable
472 New submission from Bork, Chef:
474 This is a test submission of a new issue.
476 ----------
477 assignedto: richard
478 messages: 1
479 nosy: Chef, mary, richard
480 status: unread
481 title: Testing...
483 _______________________________________________________________________
484 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
485 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
486 _______________________________________________________________________
487 ''')
489 multipart_msg = '''From: mary <mary@test.test>
490 To: issue_tracker@your.tracker.email.domain.example
491 Message-Id: <followup_dummy_id>
492 In-Reply-To: <dummy_test_message_id>
493 Subject: [issue1] Testing...
494 Content-Type: multipart/mixed; boundary="bxyzzy"
495 Content-Disposition: inline
498 --bxyzzy
499 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWE"
500 Content-Disposition: inline
502 --bCsyhTFzCvuiizWE
503 Content-Type: text/plain; charset=us-ascii
504 Content-Disposition: inline
506 test attachment first text/plain
508 --bCsyhTFzCvuiizWE
509 Content-Type: application/octet-stream
510 Content-Disposition: attachment; filename="first.dvi"
511 Content-Transfer-Encoding: base64
513 SnVzdCBhIHRlc3QgAQo=
515 --bCsyhTFzCvuiizWE
516 Content-Type: text/plain; charset=us-ascii
517 Content-Disposition: inline
519 test attachment second text/plain
521 --bCsyhTFzCvuiizWE
522 Content-Type: text/html
523 Content-Disposition: inline
525 <html>
526 to be ignored.
527 </html>
529 --bCsyhTFzCvuiizWE--
531 --bxyzzy
532 Content-Type: multipart/alternative; boundary="bCsyhTFzCvuiizWF"
533 Content-Disposition: inline
535 --bCsyhTFzCvuiizWF
536 Content-Type: text/plain; charset=us-ascii
537 Content-Disposition: inline
539 test attachment third text/plain
541 --bCsyhTFzCvuiizWF
542 Content-Type: application/octet-stream
543 Content-Disposition: attachment; filename="second.dvi"
544 Content-Transfer-Encoding: base64
546 SnVzdCBhIHRlc3QK
548 --bCsyhTFzCvuiizWF--
550 --bxyzzy--
551 '''
553 multipart_msg_latin1 = '''From: mary <mary@test.test>
554 To: issue_tracker@your.tracker.email.domain.example
555 Message-Id: <followup_dummy_id>
556 In-Reply-To: <dummy_test_message_id>
557 Subject: [issue1] Testing...
558 Content-Type: multipart/alternative; boundary=001485f339f8f361fb049188dbba
561 --001485f339f8f361fb049188dbba
562 Content-Type: text/plain; charset=ISO-8859-1
563 Content-Transfer-Encoding: quoted-printable
565 umlaut =E4=F6=FC=C4=D6=DC=DF
567 --001485f339f8f361fb049188dbba
568 Content-Type: text/html; charset=ISO-8859-1
569 Content-Transfer-Encoding: quoted-printable
571 <html>umlaut =E4=F6=FC=C4=D6=DC=DF</html>
573 --001485f339f8f361fb049188dbba--
574 '''
576 multipart_msg_rfc822 = '''From: mary <mary@test.test>
577 To: issue_tracker@your.tracker.email.domain.example
578 Message-Id: <followup_dummy_id>
579 In-Reply-To: <dummy_test_message_id>
580 Subject: [issue1] Testing...
581 Content-Type: multipart/mixed; boundary=001485f339f8f361fb049188dbba
583 This is a multi-part message in MIME format.
584 --001485f339f8f361fb049188dbba
585 Content-Type: text/plain; charset=ISO-8859-15
586 Content-Transfer-Encoding: 7bit
588 First part: Text
590 --001485f339f8f361fb049188dbba
591 Content-Type: message/rfc822; name="Fwd: Original email subject.eml"
592 Content-Transfer-Encoding: 7bit
593 Content-Disposition: attachment; filename="Fwd: Original email subject.eml"
595 Message-Id: <followup_dummy_id_2>
596 In-Reply-To: <dummy_test_message_id_2>
597 MIME-Version: 1.0
598 Subject: Fwd: Original email subject
599 Date: Mon, 23 Aug 2010 08:23:33 +0200
600 Content-Type: multipart/alternative; boundary="090500050101020406060002"
602 This is a multi-part message in MIME format.
603 --090500050101020406060002
604 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
605 Content-Transfer-Encoding: 7bit
607 some text in inner email
608 ========================
610 --090500050101020406060002
611 Content-Type: text/html; charset=ISO-8859-15
612 Content-Transfer-Encoding: 7bit
614 <html>
615 some text in inner email
616 ========================
617 </html>
619 --090500050101020406060002--
621 --001485f339f8f361fb049188dbba--
622 '''
624 def testMultipartKeepAlternatives(self):
625 self.doNewIssue()
626 self._handle_mail(self.multipart_msg)
627 messages = self.db.issue.get('1', 'messages')
628 messages.sort()
629 msg = self.db.msg.getnode (messages[-1])
630 assert(len(msg.files) == 5)
631 names = {0 : 'first.dvi', 4 : 'second.dvi'}
632 content = {3 : 'test attachment third text/plain\n',
633 4 : 'Just a test\n'}
634 for n, id in enumerate (msg.files):
635 f = self.db.file.getnode (id)
636 self.assertEqual(f.name, names.get (n, 'unnamed'))
637 if n in content :
638 self.assertEqual(f.content, content [n])
639 self.assertEqual(msg.content, 'test attachment second text/plain')
641 def testMultipartSeveralAttachmentMessages(self):
642 self.doNewIssue()
643 self._handle_mail(self.multipart_msg)
644 messages = self.db.issue.get('1', 'messages')
645 messages.sort()
646 self.assertEqual(messages[-1], '2')
647 msg = self.db.msg.getnode (messages[-1])
648 self.assertEqual(len(msg.files), 5)
649 issue = self.db.issue.getnode ('1')
650 self.assertEqual(len(issue.files), 5)
651 names = {0 : 'first.dvi', 4 : 'second.dvi'}
652 content = {3 : 'test attachment third text/plain\n',
653 4 : 'Just a test\n'}
654 for n, id in enumerate (msg.files):
655 f = self.db.file.getnode (id)
656 self.assertEqual(f.name, names.get (n, 'unnamed'))
657 if n in content :
658 self.assertEqual(f.content, content [n])
659 self.assertEqual(msg.content, 'test attachment second text/plain')
660 self.assertEqual(msg.files, ['1', '2', '3', '4', '5'])
661 self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
663 self._handle_mail(self.multipart_msg)
664 issue = self.db.issue.getnode ('1')
665 self.assertEqual(len(issue.files), 10)
666 messages = self.db.issue.get('1', 'messages')
667 messages.sort()
668 self.assertEqual(messages[-1], '3')
669 msg = self.db.msg.getnode (messages[-1])
670 self.assertEqual(issue.files, [str(i+1) for i in range(10)])
671 self.assertEqual(msg.files, ['6', '7', '8', '9', '10'])
673 def testMultipartKeepFiles(self):
674 self.doNewIssue()
675 self._handle_mail(self.multipart_msg)
676 messages = self.db.issue.get('1', 'messages')
677 messages.sort()
678 msg = self.db.msg.getnode (messages[-1])
679 self.assertEqual(len(msg.files), 5)
680 issue = self.db.issue.getnode ('1')
681 self.assertEqual(len(issue.files), 5)
682 names = {0 : 'first.dvi', 4 : 'second.dvi'}
683 content = {3 : 'test attachment third text/plain\n',
684 4 : 'Just a test\n'}
685 for n, id in enumerate (msg.files):
686 f = self.db.file.getnode (id)
687 self.assertEqual(f.name, names.get (n, 'unnamed'))
688 if n in content :
689 self.assertEqual(f.content, content [n])
690 self.assertEqual(msg.content, 'test attachment second text/plain')
691 self._handle_mail('''From: mary <mary@test.test>
692 To: issue_tracker@your.tracker.email.domain.example
693 Message-Id: <followup_dummy_id2>
694 In-Reply-To: <dummy_test_message_id>
695 Subject: [issue1] Testing...
697 This ist a message without attachment
698 ''')
699 issue = self.db.issue.getnode ('1')
700 self.assertEqual(len(issue.files), 5)
701 self.assertEqual(issue.files, ['1', '2', '3', '4', '5'])
703 def testMultipartDropAlternatives(self):
704 self.doNewIssue()
705 self.db.config.MAILGW_IGNORE_ALTERNATIVES = True
706 self._handle_mail(self.multipart_msg)
707 messages = self.db.issue.get('1', 'messages')
708 messages.sort()
709 msg = self.db.msg.getnode (messages[-1])
710 self.assertEqual(len(msg.files), 2)
711 names = {1 : 'second.dvi'}
712 content = {0 : 'test attachment third text/plain\n',
713 1 : 'Just a test\n'}
714 for n, id in enumerate (msg.files):
715 f = self.db.file.getnode (id)
716 self.assertEqual(f.name, names.get (n, 'unnamed'))
717 if n in content :
718 self.assertEqual(f.content, content [n])
719 self.assertEqual(msg.content, 'test attachment second text/plain')
721 def testMultipartCharsetUTF8NoAttach(self):
722 c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
723 self.doNewIssue()
724 self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
725 self._handle_mail(self.multipart_msg_latin1)
726 messages = self.db.issue.get('1', 'messages')
727 messages.sort()
728 msg = self.db.msg.getnode (messages[-1])
729 self.assertEqual(len(msg.files), 1)
730 name = 'unnamed'
731 content = '<html>' + c + '</html>\n'
732 for n, id in enumerate (msg.files):
733 f = self.db.file.getnode (id)
734 self.assertEqual(f.name, name)
735 self.assertEqual(f.content, content)
736 self.assertEqual(msg.content, c)
737 self.compareMessages(self._get_mail(),
738 '''FROM: roundup-admin@your.tracker.email.domain.example
739 TO: chef@bork.bork.bork, richard@test.test
740 Content-Type: text/plain; charset="utf-8"
741 Subject: [issue1] Testing...
742 To: chef@bork.bork.bork, richard@test.test
743 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
744 Reply-To: Roundup issue tracker
745 <issue_tracker@your.tracker.email.domain.example>
746 MIME-Version: 1.0
747 Message-Id: <followup_dummy_id>
748 In-Reply-To: <dummy_test_message_id>
749 X-Roundup-Name: Roundup issue tracker
750 X-Roundup-Loop: hello
751 X-Roundup-Issue-Status: chatting
752 X-Roundup-Issue-Files: unnamed
753 Content-Transfer-Encoding: quoted-printable
756 Contrary, Mary <mary@test.test> added the comment:
758 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
759 File 'unnamed' not attached - you can download it from http://tracker.examp=
760 le/cgi-bin/roundup.cgi/bugs/file1.
762 ----------
763 status: unread -> chatting
765 _______________________________________________________________________
766 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
767 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
768 _______________________________________________________________________
769 ''')
771 def testMultipartCharsetLatin1NoAttach(self):
772 c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
773 self.doNewIssue()
774 self.db.config.NOSY_MAX_ATTACHMENT_SIZE = 0
775 self.db.config.MAIL_CHARSET = 'iso-8859-1'
776 self._handle_mail(self.multipart_msg_latin1)
777 messages = self.db.issue.get('1', 'messages')
778 messages.sort()
779 msg = self.db.msg.getnode (messages[-1])
780 self.assertEqual(len(msg.files), 1)
781 name = 'unnamed'
782 content = '<html>' + c + '</html>\n'
783 for n, id in enumerate (msg.files):
784 f = self.db.file.getnode (id)
785 self.assertEqual(f.name, name)
786 self.assertEqual(f.content, content)
787 self.assertEqual(msg.content, c)
788 self.compareMessages(self._get_mail(),
789 '''FROM: roundup-admin@your.tracker.email.domain.example
790 TO: chef@bork.bork.bork, richard@test.test
791 Content-Type: text/plain; charset="iso-8859-1"
792 Subject: [issue1] Testing...
793 To: chef@bork.bork.bork, richard@test.test
794 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
795 Reply-To: Roundup issue tracker
796 <issue_tracker@your.tracker.email.domain.example>
797 MIME-Version: 1.0
798 Message-Id: <followup_dummy_id>
799 In-Reply-To: <dummy_test_message_id>
800 X-Roundup-Name: Roundup issue tracker
801 X-Roundup-Loop: hello
802 X-Roundup-Issue-Status: chatting
803 X-Roundup-Issue-Files: unnamed
804 Content-Transfer-Encoding: quoted-printable
807 Contrary, Mary <mary@test.test> added the comment:
809 umlaut =E4=F6=FC=C4=D6=DC=DF
810 File 'unnamed' not attached - you can download it from http://tracker.examp=
811 le/cgi-bin/roundup.cgi/bugs/file1.
813 ----------
814 status: unread -> chatting
816 _______________________________________________________________________
817 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
818 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
819 _______________________________________________________________________
820 ''')
822 def testMultipartCharsetUTF8AttachFile(self):
823 c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
824 self.doNewIssue()
825 self._handle_mail(self.multipart_msg_latin1)
826 messages = self.db.issue.get('1', 'messages')
827 messages.sort()
828 msg = self.db.msg.getnode (messages[-1])
829 self.assertEqual(len(msg.files), 1)
830 name = 'unnamed'
831 content = '<html>' + c + '</html>\n'
832 for n, id in enumerate (msg.files):
833 f = self.db.file.getnode (id)
834 self.assertEqual(f.name, name)
835 self.assertEqual(f.content, content)
836 self.assertEqual(msg.content, c)
837 self.compareMessages(self._get_mail(),
838 '''FROM: roundup-admin@your.tracker.email.domain.example
839 TO: chef@bork.bork.bork, richard@test.test
840 Content-Type: multipart/mixed; boundary="utf-8"
841 Subject: [issue1] Testing...
842 To: chef@bork.bork.bork, richard@test.test
843 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
844 Reply-To: Roundup issue tracker
845 <issue_tracker@your.tracker.email.domain.example>
846 MIME-Version: 1.0
847 Message-Id: <followup_dummy_id>
848 In-Reply-To: <dummy_test_message_id>
849 X-Roundup-Name: Roundup issue tracker
850 X-Roundup-Loop: hello
851 X-Roundup-Issue-Status: chatting
852 X-Roundup-Issue-Files: unnamed
853 Content-Transfer-Encoding: quoted-printable
856 --utf-8
857 MIME-Version: 1.0
858 Content-Type: text/plain; charset="utf-8"
859 Content-Transfer-Encoding: quoted-printable
862 Contrary, Mary <mary@test.test> added the comment:
864 umlaut =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F
866 ----------
867 status: unread -> chatting
869 _______________________________________________________________________
870 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
871 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
872 _______________________________________________________________________
873 --utf-8
874 Content-Type: text/html
875 MIME-Version: 1.0
876 Content-Transfer-Encoding: base64
877 Content-Disposition: attachment;
878 filename="unnamed"
880 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
882 --utf-8--
883 ''')
885 def testMultipartCharsetLatin1AttachFile(self):
886 c = 'umlaut \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\xc3\x9f'
887 self.doNewIssue()
888 self.db.config.MAIL_CHARSET = 'iso-8859-1'
889 self._handle_mail(self.multipart_msg_latin1)
890 messages = self.db.issue.get('1', 'messages')
891 messages.sort()
892 msg = self.db.msg.getnode (messages[-1])
893 self.assertEqual(len(msg.files), 1)
894 name = 'unnamed'
895 content = '<html>' + c + '</html>\n'
896 for n, id in enumerate (msg.files):
897 f = self.db.file.getnode (id)
898 self.assertEqual(f.name, name)
899 self.assertEqual(f.content, content)
900 self.assertEqual(msg.content, c)
901 self.compareMessages(self._get_mail(),
902 '''FROM: roundup-admin@your.tracker.email.domain.example
903 TO: chef@bork.bork.bork, richard@test.test
904 Content-Type: multipart/mixed; boundary="utf-8"
905 Subject: [issue1] Testing...
906 To: chef@bork.bork.bork, richard@test.test
907 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
908 Reply-To: Roundup issue tracker
909 <issue_tracker@your.tracker.email.domain.example>
910 MIME-Version: 1.0
911 Message-Id: <followup_dummy_id>
912 In-Reply-To: <dummy_test_message_id>
913 X-Roundup-Name: Roundup issue tracker
914 X-Roundup-Loop: hello
915 X-Roundup-Issue-Status: chatting
916 X-Roundup-Issue-Files: unnamed
917 Content-Transfer-Encoding: quoted-printable
920 --utf-8
921 MIME-Version: 1.0
922 Content-Type: text/plain; charset="iso-8859-1"
923 Content-Transfer-Encoding: quoted-printable
926 Contrary, Mary <mary@test.test> added the comment:
928 umlaut =E4=F6=FC=C4=D6=DC=DF
930 ----------
931 status: unread -> chatting
933 _______________________________________________________________________
934 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
935 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
936 _______________________________________________________________________
937 --utf-8
938 Content-Type: text/html
939 MIME-Version: 1.0
940 Content-Transfer-Encoding: base64
941 Content-Disposition: attachment;
942 filename="unnamed"
944 PGh0bWw+dW1sYXV0IMOkw7bDvMOEw5bDnMOfPC9odG1sPgo=
946 --utf-8--
947 ''')
949 def testMultipartRFC822(self):
950 self.doNewIssue()
951 self._handle_mail(self.multipart_msg_rfc822)
952 messages = self.db.issue.get('1', 'messages')
953 messages.sort()
954 msg = self.db.msg.getnode (messages[-1])
955 self.assertEqual(len(msg.files), 1)
956 name = "Fwd: Original email subject.eml"
957 for n, id in enumerate (msg.files):
958 f = self.db.file.getnode (id)
959 self.assertEqual(f.name, name)
960 self.assertEqual(msg.content, 'First part: Text')
961 self.compareMessages(self._get_mail(),
962 '''TO: chef@bork.bork.bork, richard@test.test
963 Content-Type: text/plain; charset="utf-8"
964 Subject: [issue1] Testing...
965 To: chef@bork.bork.bork, richard@test.test
966 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
967 Reply-To: Roundup issue tracker
968 <issue_tracker@your.tracker.email.domain.example>
969 MIME-Version: 1.0
970 Message-Id: <followup_dummy_id>
971 In-Reply-To: <dummy_test_message_id>
972 X-Roundup-Name: Roundup issue tracker
973 X-Roundup-Loop: hello
974 X-Roundup-Issue-Status: chatting
975 X-Roundup-Issue-Files: Fwd: Original email subject.eml
976 Content-Transfer-Encoding: quoted-printable
979 --utf-8
980 MIME-Version: 1.0
981 Content-Type: text/plain; charset="utf-8"
982 Content-Transfer-Encoding: quoted-printable
985 Contrary, Mary <mary@test.test> added the comment:
987 First part: Text
989 ----------
990 status: unread -> chatting
992 _______________________________________________________________________
993 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
994 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
995 _______________________________________________________________________
996 --utf-8
997 Content-Type: message/rfc822
998 MIME-Version: 1.0
999 Content-Disposition: attachment;
1000 filename="Fwd: Original email subject.eml"
1002 Message-Id: <followup_dummy_id_2>
1003 In-Reply-To: <dummy_test_message_id_2>
1004 MIME-Version: 1.0
1005 Subject: Fwd: Original email subject
1006 Date: Mon, 23 Aug 2010 08:23:33 +0200
1007 Content-Type: multipart/alternative; boundary="090500050101020406060002"
1009 This is a multi-part message in MIME format.
1010 --090500050101020406060002
1011 Content-Type: text/plain; charset=ISO-8859-15; format=flowed
1012 Content-Transfer-Encoding: 7bit
1014 some text in inner email
1015 ========================
1017 --090500050101020406060002
1018 Content-Type: text/html; charset=ISO-8859-15
1019 Content-Transfer-Encoding: 7bit
1021 <html>
1022 some text in inner email
1023 ========================
1024 </html>
1026 --090500050101020406060002--
1028 --utf-8--
1029 ''')
1031 def testMultipartRFC822Unpack(self):
1032 self.doNewIssue()
1033 self.db.config.MAILGW_UNPACK_RFC822 = True
1034 self._handle_mail(self.multipart_msg_rfc822)
1035 messages = self.db.issue.get('1', 'messages')
1036 messages.sort()
1037 msg = self.db.msg.getnode (messages[-1])
1038 self.assertEqual(len(msg.files), 2)
1039 t = 'some text in inner email\n========================\n'
1040 content = {0 : t, 1 : '<html>\n' + t + '</html>\n'}
1041 for n, id in enumerate (msg.files):
1042 f = self.db.file.getnode (id)
1043 self.assertEqual(f.name, 'unnamed')
1044 if n in content :
1045 self.assertEqual(f.content, content [n])
1046 self.assertEqual(msg.content, 'First part: Text')
1048 def testSimpleFollowup(self):
1049 self.doNewIssue()
1050 self._handle_mail('''Content-Type: text/plain;
1051 charset="iso-8859-1"
1052 From: mary <mary@test.test>
1053 To: issue_tracker@your.tracker.email.domain.example
1054 Message-Id: <followup_dummy_id>
1055 In-Reply-To: <dummy_test_message_id>
1056 Subject: [issue1] Testing...
1058 This is a second followup
1059 ''')
1060 self.compareMessages(self._get_mail(),
1061 '''FROM: roundup-admin@your.tracker.email.domain.example
1062 TO: chef@bork.bork.bork, richard@test.test
1063 Content-Type: text/plain; charset="utf-8"
1064 Subject: [issue1] Testing...
1065 To: chef@bork.bork.bork, richard@test.test
1066 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
1067 Reply-To: Roundup issue tracker
1068 <issue_tracker@your.tracker.email.domain.example>
1069 MIME-Version: 1.0
1070 Message-Id: <followup_dummy_id>
1071 In-Reply-To: <dummy_test_message_id>
1072 X-Roundup-Name: Roundup issue tracker
1073 X-Roundup-Loop: hello
1074 X-Roundup-Issue-Status: chatting
1075 Content-Transfer-Encoding: quoted-printable
1078 Contrary, Mary <mary@test.test> added the comment:
1080 This is a second followup
1082 ----------
1083 status: unread -> chatting
1085 _______________________________________________________________________
1086 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1087 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1088 _______________________________________________________________________
1089 ''')
1091 def testFollowup(self):
1092 self.doNewIssue()
1094 self._handle_mail('''Content-Type: text/plain;
1095 charset="iso-8859-1"
1096 From: richard <richard@test.test>
1097 To: issue_tracker@your.tracker.email.domain.example
1098 Message-Id: <followup_dummy_id>
1099 In-Reply-To: <dummy_test_message_id>
1100 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1102 This is a followup
1103 ''')
1104 l = self.db.issue.get('1', 'nosy')
1105 l.sort()
1106 self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1107 self.john_id])
1109 self.compareMessages(self._get_mail(),
1110 '''FROM: roundup-admin@your.tracker.email.domain.example
1111 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1112 Content-Type: text/plain; charset="utf-8"
1113 Subject: [issue1] Testing...
1114 To: chef@bork.bork.bork, john@test.test, mary@test.test
1115 From: richard <issue_tracker@your.tracker.email.domain.example>
1116 Reply-To: Roundup issue tracker
1117 <issue_tracker@your.tracker.email.domain.example>
1118 MIME-Version: 1.0
1119 Message-Id: <followup_dummy_id>
1120 In-Reply-To: <dummy_test_message_id>
1121 X-Roundup-Name: Roundup issue tracker
1122 X-Roundup-Loop: hello
1123 X-Roundup-Issue-Status: chatting
1124 Content-Transfer-Encoding: quoted-printable
1127 richard <richard@test.test> added the comment:
1129 This is a followup
1131 ----------
1132 assignedto: -> mary
1133 nosy: +john, mary
1134 status: unread -> chatting
1136 _______________________________________________________________________
1137 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1138 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1139 _______________________________________________________________________
1140 ''')
1142 def testFollowupNoSubjectChange(self):
1143 self.db.config.MAILGW_SUBJECT_UPDATES_TITLE = 'no'
1144 self.doNewIssue()
1146 self._handle_mail('''Content-Type: text/plain;
1147 charset="iso-8859-1"
1148 From: richard <richard@test.test>
1149 To: issue_tracker@your.tracker.email.domain.example
1150 Message-Id: <followup_dummy_id>
1151 In-Reply-To: <dummy_test_message_id>
1152 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john]
1154 This is a followup
1155 ''')
1156 l = self.db.issue.get('1', 'nosy')
1157 l.sort()
1158 self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1159 self.john_id])
1161 self.compareMessages(self._get_mail(),
1162 '''FROM: roundup-admin@your.tracker.email.domain.example
1163 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1164 Content-Type: text/plain; charset="utf-8"
1165 Subject: [issue1] Testing...
1166 To: chef@bork.bork.bork, john@test.test, mary@test.test
1167 From: richard <issue_tracker@your.tracker.email.domain.example>
1168 Reply-To: Roundup issue tracker
1169 <issue_tracker@your.tracker.email.domain.example>
1170 MIME-Version: 1.0
1171 Message-Id: <followup_dummy_id>
1172 In-Reply-To: <dummy_test_message_id>
1173 X-Roundup-Name: Roundup issue tracker
1174 X-Roundup-Loop: hello
1175 X-Roundup-Issue-Status: chatting
1176 Content-Transfer-Encoding: quoted-printable
1179 richard <richard@test.test> added the comment:
1181 This is a followup
1183 ----------
1184 assignedto: -> mary
1185 nosy: +john, mary
1186 status: unread -> chatting
1188 _______________________________________________________________________
1189 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1190 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1191 _______________________________________________________________________
1192 ''')
1193 self.assertEqual(self.db.issue.get('1','title'), 'Testing...')
1195 def testFollowupExplicitSubjectChange(self):
1196 self.doNewIssue()
1198 self._handle_mail('''Content-Type: text/plain;
1199 charset="iso-8859-1"
1200 From: richard <richard@test.test>
1201 To: issue_tracker@your.tracker.email.domain.example
1202 Message-Id: <followup_dummy_id>
1203 In-Reply-To: <dummy_test_message_id>
1204 Subject: [issue1] Wrzlbrmft... [assignedto=mary; nosy=+john; title=new title]
1206 This is a followup
1207 ''')
1208 l = self.db.issue.get('1', 'nosy')
1209 l.sort()
1210 self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1211 self.john_id])
1213 self.compareMessages(self._get_mail(),
1214 '''FROM: roundup-admin@your.tracker.email.domain.example
1215 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1216 Content-Type: text/plain; charset="utf-8"
1217 Subject: [issue1] new title
1218 To: chef@bork.bork.bork, john@test.test, mary@test.test
1219 From: richard <issue_tracker@your.tracker.email.domain.example>
1220 Reply-To: Roundup issue tracker
1221 <issue_tracker@your.tracker.email.domain.example>
1222 MIME-Version: 1.0
1223 Message-Id: <followup_dummy_id>
1224 In-Reply-To: <dummy_test_message_id>
1225 X-Roundup-Name: Roundup issue tracker
1226 X-Roundup-Loop: hello
1227 X-Roundup-Issue-Status: chatting
1228 Content-Transfer-Encoding: quoted-printable
1231 richard <richard@test.test> added the comment:
1233 This is a followup
1235 ----------
1236 assignedto: -> mary
1237 nosy: +john, mary
1238 status: unread -> chatting
1239 title: Testing... -> new title
1241 _______________________________________________________________________
1242 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1243 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1244 _______________________________________________________________________
1245 ''')
1247 def testNosyGeneration(self):
1248 self.db.issue.create(title='test')
1250 # create a nosy message
1251 msg = self.db.msg.create(content='This is a test',
1252 author=self.richard_id, messageid='<dummy_test_message_id>')
1253 self.db.journaltag = 'richard'
1254 l = self.db.issue.create(title='test', messages=[msg],
1255 nosy=[self.chef_id, self.mary_id, self.john_id])
1257 self.compareMessages(self._get_mail(),
1258 '''FROM: roundup-admin@your.tracker.email.domain.example
1259 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1260 Content-Type: text/plain; charset="utf-8"
1261 Subject: [issue2] test
1262 To: chef@bork.bork.bork, john@test.test, mary@test.test
1263 From: richard <issue_tracker@your.tracker.email.domain.example>
1264 Reply-To: Roundup issue tracker
1265 <issue_tracker@your.tracker.email.domain.example>
1266 MIME-Version: 1.0
1267 Message-Id: <dummy_test_message_id>
1268 X-Roundup-Name: Roundup issue tracker
1269 X-Roundup-Loop: hello
1270 X-Roundup-Issue-Status: unread
1271 Content-Transfer-Encoding: quoted-printable
1274 New submission from richard <richard@test.test>:
1276 This is a test
1278 ----------
1279 messages: 1
1280 nosy: Chef, john, mary, richard
1281 status: unread
1282 title: test
1284 _______________________________________________________________________
1285 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1286 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue2>
1287 _______________________________________________________________________
1288 ''')
1290 def testPropertyChangeOnly(self):
1291 self.doNewIssue()
1292 oldvalues = self.db.getnode('issue', '1').copy()
1293 oldvalues['assignedto'] = None
1294 # reconstruct old behaviour: This would reuse the
1295 # database-handle from the doNewIssue above which has committed
1296 # as user "Chef". So we close and reopen the db as that user.
1297 #self.db.close() actually don't close 'cos this empties memorydb
1298 self.db = self.instance.open('Chef')
1299 self.db.issue.set('1', assignedto=self.chef_id)
1300 self.db.commit()
1301 self.db.issue.nosymessage('1', None, oldvalues)
1303 new_mail = ""
1304 for line in self._get_mail().split("\n"):
1305 if "Message-Id: " in line:
1306 continue
1307 if "Date: " in line:
1308 continue
1309 new_mail += line+"\n"
1311 self.compareMessages(new_mail, """
1312 FROM: roundup-admin@your.tracker.email.domain.example
1313 TO: chef@bork.bork.bork, richard@test.test
1314 Content-Type: text/plain; charset="utf-8"
1315 Subject: [issue1] Testing...
1316 To: chef@bork.bork.bork, richard@test.test
1317 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
1318 X-Roundup-Name: Roundup issue tracker
1319 X-Roundup-Loop: hello
1320 X-Roundup-Issue-Status: unread
1321 X-Roundup-Version: 1.3.3
1322 In-Reply-To: <dummy_test_message_id>
1323 MIME-Version: 1.0
1324 Reply-To: Roundup issue tracker
1325 <issue_tracker@your.tracker.email.domain.example>
1326 Content-Transfer-Encoding: quoted-printable
1329 Change by Bork, Chef <chef@bork.bork.bork>:
1332 ----------
1333 assignedto: -> Chef
1335 _______________________________________________________________________
1336 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1337 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1338 _______________________________________________________________________
1339 """)
1342 #
1343 # FOLLOWUP TITLE MATCH
1344 #
1345 def testFollowupTitleMatch(self):
1346 self.doNewIssue()
1347 self._handle_mail('''Content-Type: text/plain;
1348 charset="iso-8859-1"
1349 From: richard <richard@test.test>
1350 To: issue_tracker@your.tracker.email.domain.example
1351 Message-Id: <followup_dummy_id>
1352 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1354 This is a followup
1355 ''')
1356 self.compareMessages(self._get_mail(),
1357 '''FROM: roundup-admin@your.tracker.email.domain.example
1358 TO: chef@bork.bork.bork, john@test.test, mary@test.test
1359 Content-Type: text/plain; charset="utf-8"
1360 Subject: [issue1] Testing...
1361 To: chef@bork.bork.bork, john@test.test, mary@test.test
1362 From: richard <issue_tracker@your.tracker.email.domain.example>
1363 Reply-To: Roundup issue tracker
1364 <issue_tracker@your.tracker.email.domain.example>
1365 MIME-Version: 1.0
1366 Message-Id: <followup_dummy_id>
1367 In-Reply-To: <dummy_test_message_id>
1368 X-Roundup-Name: Roundup issue tracker
1369 X-Roundup-Loop: hello
1370 X-Roundup-Issue-Status: chatting
1371 Content-Transfer-Encoding: quoted-printable
1374 richard <richard@test.test> added the comment:
1376 This is a followup
1378 ----------
1379 assignedto: -> mary
1380 nosy: +john, mary
1381 status: unread -> chatting
1383 _______________________________________________________________________
1384 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1385 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1386 _______________________________________________________________________
1387 ''')
1389 def testFollowupTitleMatchMultiRe(self):
1390 nodeid1 = self.doNewIssue()
1391 nodeid2 = self._handle_mail('''Content-Type: text/plain;
1392 charset="iso-8859-1"
1393 From: richard <richard@test.test>
1394 To: issue_tracker@your.tracker.email.domain.example
1395 Message-Id: <followup_dummy_id>
1396 Subject: Re: Testing... [assignedto=mary; nosy=+john]
1398 This is a followup
1399 ''')
1401 nodeid3 = self._handle_mail('''Content-Type: text/plain;
1402 charset="iso-8859-1"
1403 From: richard <richard@test.test>
1404 To: issue_tracker@your.tracker.email.domain.example
1405 Message-Id: <followup2_dummy_id>
1406 Subject: Ang: Re: Testing...
1408 This is a followup
1409 ''')
1410 self.assertEqual(nodeid1, nodeid2)
1411 self.assertEqual(nodeid1, nodeid3)
1413 def testFollowupTitleMatchNever(self):
1414 nodeid = self.doNewIssue()
1415 self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'never'
1416 self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1417 charset="iso-8859-1"
1418 From: richard <richard@test.test>
1419 To: issue_tracker@your.tracker.email.domain.example
1420 Message-Id: <followup_dummy_id>
1421 Subject: Re: Testing...
1423 This is a followup
1424 '''), nodeid)
1426 def testFollowupTitleMatchNeverInterval(self):
1427 nodeid = self.doNewIssue()
1428 # force failure of the interval
1429 time.sleep(2)
1430 self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation 00:00:01'
1431 self.assertNotEqual(self._handle_mail('''Content-Type: text/plain;
1432 charset="iso-8859-1"
1433 From: richard <richard@test.test>
1434 To: issue_tracker@your.tracker.email.domain.example
1435 Message-Id: <followup_dummy_id>
1436 Subject: Re: Testing...
1438 This is a followup
1439 '''), nodeid)
1442 def testFollowupTitleMatchInterval(self):
1443 nodeid = self.doNewIssue()
1444 self.db.config.MAILGW_SUBJECT_CONTENT_MATCH = 'creation +1d'
1445 self.assertEqual(self._handle_mail('''Content-Type: text/plain;
1446 charset="iso-8859-1"
1447 From: richard <richard@test.test>
1448 To: issue_tracker@your.tracker.email.domain.example
1449 Message-Id: <followup_dummy_id>
1450 Subject: Re: Testing...
1452 This is a followup
1453 '''), nodeid)
1456 def testFollowupNosyAuthor(self):
1457 self.doNewIssue()
1458 self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1459 self._handle_mail('''Content-Type: text/plain;
1460 charset="iso-8859-1"
1461 From: john@test.test
1462 To: issue_tracker@your.tracker.email.domain.example
1463 Message-Id: <followup_dummy_id>
1464 In-Reply-To: <dummy_test_message_id>
1465 Subject: [issue1] Testing...
1467 This is a followup
1468 ''')
1470 self.compareMessages(self._get_mail(),
1471 '''FROM: roundup-admin@your.tracker.email.domain.example
1472 TO: chef@bork.bork.bork, richard@test.test
1473 Content-Type: text/plain; charset="utf-8"
1474 Subject: [issue1] Testing...
1475 To: chef@bork.bork.bork, richard@test.test
1476 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1477 Reply-To: Roundup issue tracker
1478 <issue_tracker@your.tracker.email.domain.example>
1479 MIME-Version: 1.0
1480 Message-Id: <followup_dummy_id>
1481 In-Reply-To: <dummy_test_message_id>
1482 X-Roundup-Name: Roundup issue tracker
1483 X-Roundup-Loop: hello
1484 X-Roundup-Issue-Status: chatting
1485 Content-Transfer-Encoding: quoted-printable
1488 John Doe <john@test.test> added the comment:
1490 This is a followup
1492 ----------
1493 nosy: +john
1494 status: unread -> chatting
1496 _______________________________________________________________________
1497 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1498 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1499 _______________________________________________________________________
1501 ''')
1503 def testFollowupNosyRecipients(self):
1504 self.doNewIssue()
1505 self.db.config.ADD_RECIPIENTS_TO_NOSY = 'yes'
1506 self._handle_mail('''Content-Type: text/plain;
1507 charset="iso-8859-1"
1508 From: richard@test.test
1509 To: issue_tracker@your.tracker.email.domain.example
1510 Cc: john@test.test
1511 Message-Id: <followup_dummy_id>
1512 In-Reply-To: <dummy_test_message_id>
1513 Subject: [issue1] Testing...
1515 This is a followup
1516 ''')
1517 self.compareMessages(self._get_mail(),
1518 '''FROM: roundup-admin@your.tracker.email.domain.example
1519 TO: chef@bork.bork.bork
1520 Content-Type: text/plain; charset="utf-8"
1521 Subject: [issue1] Testing...
1522 To: chef@bork.bork.bork
1523 From: richard <issue_tracker@your.tracker.email.domain.example>
1524 Reply-To: Roundup issue tracker
1525 <issue_tracker@your.tracker.email.domain.example>
1526 MIME-Version: 1.0
1527 Message-Id: <followup_dummy_id>
1528 In-Reply-To: <dummy_test_message_id>
1529 X-Roundup-Name: Roundup issue tracker
1530 X-Roundup-Loop: hello
1531 X-Roundup-Issue-Status: chatting
1532 Content-Transfer-Encoding: quoted-printable
1535 richard <richard@test.test> added the comment:
1537 This is a followup
1539 ----------
1540 nosy: +john
1541 status: unread -> chatting
1543 _______________________________________________________________________
1544 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1545 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1546 _______________________________________________________________________
1548 ''')
1550 def testFollowupNosyAuthorAndCopy(self):
1551 self.doNewIssue()
1552 self.db.config.ADD_AUTHOR_TO_NOSY = 'yes'
1553 self.db.config.MESSAGES_TO_AUTHOR = 'yes'
1554 self._handle_mail('''Content-Type: text/plain;
1555 charset="iso-8859-1"
1556 From: john@test.test
1557 To: issue_tracker@your.tracker.email.domain.example
1558 Message-Id: <followup_dummy_id>
1559 In-Reply-To: <dummy_test_message_id>
1560 Subject: [issue1] Testing...
1562 This is a followup
1563 ''')
1564 self.compareMessages(self._get_mail(),
1565 '''FROM: roundup-admin@your.tracker.email.domain.example
1566 TO: chef@bork.bork.bork, john@test.test, richard@test.test
1567 Content-Type: text/plain; charset="utf-8"
1568 Subject: [issue1] Testing...
1569 To: chef@bork.bork.bork, john@test.test, richard@test.test
1570 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1571 Reply-To: Roundup issue tracker
1572 <issue_tracker@your.tracker.email.domain.example>
1573 MIME-Version: 1.0
1574 Message-Id: <followup_dummy_id>
1575 In-Reply-To: <dummy_test_message_id>
1576 X-Roundup-Name: Roundup issue tracker
1577 X-Roundup-Loop: hello
1578 X-Roundup-Issue-Status: chatting
1579 Content-Transfer-Encoding: quoted-printable
1582 John Doe <john@test.test> added the comment:
1584 This is a followup
1586 ----------
1587 nosy: +john
1588 status: unread -> chatting
1590 _______________________________________________________________________
1591 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1592 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1593 _______________________________________________________________________
1595 ''')
1597 def testFollowupNoNosyAuthor(self):
1598 self.doNewIssue()
1599 self.instance.config.ADD_AUTHOR_TO_NOSY = 'no'
1600 self._handle_mail('''Content-Type: text/plain;
1601 charset="iso-8859-1"
1602 From: john@test.test
1603 To: issue_tracker@your.tracker.email.domain.example
1604 Message-Id: <followup_dummy_id>
1605 In-Reply-To: <dummy_test_message_id>
1606 Subject: [issue1] Testing...
1608 This is a followup
1609 ''')
1610 self.compareMessages(self._get_mail(),
1611 '''FROM: roundup-admin@your.tracker.email.domain.example
1612 TO: chef@bork.bork.bork, richard@test.test
1613 Content-Type: text/plain; charset="utf-8"
1614 Subject: [issue1] Testing...
1615 To: chef@bork.bork.bork, richard@test.test
1616 From: John Doe <issue_tracker@your.tracker.email.domain.example>
1617 Reply-To: Roundup issue tracker
1618 <issue_tracker@your.tracker.email.domain.example>
1619 MIME-Version: 1.0
1620 Message-Id: <followup_dummy_id>
1621 In-Reply-To: <dummy_test_message_id>
1622 X-Roundup-Name: Roundup issue tracker
1623 X-Roundup-Loop: hello
1624 X-Roundup-Issue-Status: chatting
1625 Content-Transfer-Encoding: quoted-printable
1628 John Doe <john@test.test> added the comment:
1630 This is a followup
1632 ----------
1633 status: unread -> chatting
1635 _______________________________________________________________________
1636 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1637 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1638 _______________________________________________________________________
1640 ''')
1642 def testFollowupNoNosyRecipients(self):
1643 self.doNewIssue()
1644 self.instance.config.ADD_RECIPIENTS_TO_NOSY = 'no'
1645 self._handle_mail('''Content-Type: text/plain;
1646 charset="iso-8859-1"
1647 From: richard@test.test
1648 To: issue_tracker@your.tracker.email.domain.example
1649 Cc: john@test.test
1650 Message-Id: <followup_dummy_id>
1651 In-Reply-To: <dummy_test_message_id>
1652 Subject: [issue1] Testing...
1654 This is a followup
1655 ''')
1656 self.compareMessages(self._get_mail(),
1657 '''FROM: roundup-admin@your.tracker.email.domain.example
1658 TO: chef@bork.bork.bork
1659 Content-Type: text/plain; charset="utf-8"
1660 Subject: [issue1] Testing...
1661 To: chef@bork.bork.bork
1662 From: richard <issue_tracker@your.tracker.email.domain.example>
1663 Reply-To: Roundup issue tracker
1664 <issue_tracker@your.tracker.email.domain.example>
1665 MIME-Version: 1.0
1666 Message-Id: <followup_dummy_id>
1667 In-Reply-To: <dummy_test_message_id>
1668 X-Roundup-Name: Roundup issue tracker
1669 X-Roundup-Loop: hello
1670 X-Roundup-Issue-Status: chatting
1671 Content-Transfer-Encoding: quoted-printable
1674 richard <richard@test.test> added the comment:
1676 This is a followup
1678 ----------
1679 status: unread -> chatting
1681 _______________________________________________________________________
1682 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1683 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1684 _______________________________________________________________________
1686 ''')
1688 def testFollowupEmptyMessage(self):
1689 self.doNewIssue()
1691 self._handle_mail('''Content-Type: text/plain;
1692 charset="iso-8859-1"
1693 From: richard <richard@test.test>
1694 To: issue_tracker@your.tracker.email.domain.example
1695 Message-Id: <followup_dummy_id>
1696 In-Reply-To: <dummy_test_message_id>
1697 Subject: [issue1] Testing... [assignedto=mary; nosy=+john]
1699 ''')
1700 l = self.db.issue.get('1', 'nosy')
1701 l.sort()
1702 self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1703 self.john_id])
1705 # should be no file created (ie. no message)
1706 assert not os.path.exists(SENDMAILDEBUG)
1708 def testFollowupEmptyMessageNoSubject(self):
1709 self.doNewIssue()
1711 self._handle_mail('''Content-Type: text/plain;
1712 charset="iso-8859-1"
1713 From: richard <richard@test.test>
1714 To: issue_tracker@your.tracker.email.domain.example
1715 Message-Id: <followup_dummy_id>
1716 In-Reply-To: <dummy_test_message_id>
1717 Subject: [issue1] [assignedto=mary; nosy=+john]
1719 ''')
1720 l = self.db.issue.get('1', 'nosy')
1721 l.sort()
1722 self.assertEqual(l, [self.chef_id, self.richard_id, self.mary_id,
1723 self.john_id])
1725 # should be no file created (ie. no message)
1726 assert not os.path.exists(SENDMAILDEBUG)
1728 def testNosyRemove(self):
1729 self.doNewIssue()
1731 self._handle_mail('''Content-Type: text/plain;
1732 charset="iso-8859-1"
1733 From: richard <richard@test.test>
1734 To: issue_tracker@your.tracker.email.domain.example
1735 Message-Id: <followup_dummy_id>
1736 In-Reply-To: <dummy_test_message_id>
1737 Subject: [issue1] Testing... [nosy=-richard]
1739 ''')
1740 l = self.db.issue.get('1', 'nosy')
1741 l.sort()
1742 self.assertEqual(l, [self.chef_id])
1744 # NO NOSY MESSAGE SHOULD BE SENT!
1745 assert not os.path.exists(SENDMAILDEBUG)
1747 def testNewUserAuthor(self):
1748 self.db.commit()
1749 l = self.db.user.list()
1750 l.sort()
1751 message = '''Content-Type: text/plain;
1752 charset="iso-8859-1"
1753 From: fubar <fubar@bork.bork.bork>
1754 To: issue_tracker@your.tracker.email.domain.example
1755 Message-Id: <dummy_test_message_id>
1756 Subject: [issue] Testing...
1758 This is a test submission of a new issue.
1759 '''
1760 self.db.security.role['anonymous'].permissions=[]
1761 anonid = self.db.user.lookup('anonymous')
1762 self.db.user.set(anonid, roles='Anonymous')
1763 try:
1764 self._handle_mail(message)
1765 except Unauthorized, value:
1766 body_diff = self.compareMessages(str(value), """
1767 You are not a registered user.
1769 Unknown address: fubar@bork.bork.bork
1770 """)
1771 assert not body_diff, body_diff
1772 else:
1773 raise AssertionError, "Unathorized not raised when handling mail"
1775 # Add Web Access role to anonymous, and try again to make sure
1776 # we get a "please register at:" message this time.
1777 p = [
1778 self.db.security.getPermission('Register', 'user'),
1779 self.db.security.getPermission('Web Access', None),
1780 ]
1781 self.db.security.role['anonymous'].permissions=p
1782 try:
1783 self._handle_mail(message)
1784 except Unauthorized, value:
1785 body_diff = self.compareMessages(str(value), """
1786 You are not a registered user. Please register at:
1788 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1790 ...before sending mail to the tracker.
1792 Unknown address: fubar@bork.bork.bork
1793 """)
1794 assert not body_diff, body_diff
1795 else:
1796 raise AssertionError, "Unauthorized not raised when handling mail"
1798 # Make sure list of users is the same as before.
1799 m = self.db.user.list()
1800 m.sort()
1801 self.assertEqual(l, m)
1803 # now with the permission
1804 p = [
1805 self.db.security.getPermission('Register', 'user'),
1806 self.db.security.getPermission('Email Access', None),
1807 ]
1808 self.db.security.role['anonymous'].permissions=p
1809 self._handle_mail(message)
1810 m = self.db.user.list()
1811 m.sort()
1812 self.assertNotEqual(l, m)
1814 def testNewUserAuthorEncodedName(self):
1815 l = set(self.db.user.list())
1816 # From: name has Euro symbol in it
1817 message = '''Content-Type: text/plain;
1818 charset="iso-8859-1"
1819 From: =?utf8?b?SOKCrGxsbw==?= <fubar@bork.bork.bork>
1820 To: issue_tracker@your.tracker.email.domain.example
1821 Message-Id: <dummy_test_message_id>
1822 Subject: [issue] Testing...
1824 This is a test submission of a new issue.
1825 '''
1826 p = [
1827 self.db.security.getPermission('Register', 'user'),
1828 self.db.security.getPermission('Email Access', None),
1829 self.db.security.getPermission('Create', 'issue'),
1830 self.db.security.getPermission('Create', 'msg'),
1831 ]
1832 self.db.security.role['anonymous'].permissions = p
1833 self._handle_mail(message)
1834 m = set(self.db.user.list())
1835 new = list(m - l)[0]
1836 name = self.db.user.get(new, 'realname')
1837 self.assertEquals(name, 'H€llo')
1839 def testNewUserAuthorMixedEncodedName(self):
1840 l = set(self.db.user.list())
1841 # From: name has Euro symbol in it
1842 message = '''Content-Type: text/plain;
1843 charset="iso-8859-1"
1844 From: Firstname =?utf-8?b?w6TDtsOf?= Last <fubar@bork.bork.bork>
1845 To: issue_tracker@your.tracker.email.domain.example
1846 Message-Id: <dummy_test_message_id>
1847 Subject: [issue] Test =?utf-8?b?w4TDlsOc?= umlauts
1848 X1
1849 X2
1851 This is a test submission of a new issue.
1852 '''
1853 p = [
1854 self.db.security.getPermission('Register', 'user'),
1855 self.db.security.getPermission('Email Access', None),
1856 self.db.security.getPermission('Create', 'issue'),
1857 self.db.security.getPermission('Create', 'msg'),
1858 ]
1859 self.db.security.role['anonymous'].permissions = p
1860 self._handle_mail(message)
1861 title = self.db.issue.get('1', 'title')
1862 self.assertEquals(title, 'Test \xc3\x84\xc3\x96\xc3\x9c umlauts X1 X2')
1863 m = set(self.db.user.list())
1864 new = list(m - l)[0]
1865 name = self.db.user.get(new, 'realname')
1866 self.assertEquals(name, 'Firstname \xc3\xa4\xc3\xb6\xc3\x9f Last')
1868 def testUnknownUser(self):
1869 l = set(self.db.user.list())
1870 message = '''Content-Type: text/plain;
1871 charset="iso-8859-1"
1872 From: Nonexisting User <nonexisting@bork.bork.bork>
1873 To: issue_tracker@your.tracker.email.domain.example
1874 Message-Id: <dummy_test_message_id>
1875 Subject: [issue] Testing nonexisting user...
1877 This is a test submission of a new issue.
1878 '''
1879 # trap_exc=1: we want a bounce message:
1880 ret = self._handle_mail(message, trap_exc=1)
1881 self.compareMessages(self._get_mail(),
1882 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1883 TO: nonexisting@bork.bork.bork
1884 From nobody Tue Jul 14 12:04:11 2009
1885 Content-Type: multipart/mixed; boundary="===============0639262320=="
1886 MIME-Version: 1.0
1887 Subject: Failed issue tracker submission
1888 To: nonexisting@bork.bork.bork
1889 From: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example>
1890 Date: Tue, 14 Jul 2009 12:04:11 +0000
1891 Precedence: bulk
1892 X-Roundup-Name: Roundup issue tracker
1893 X-Roundup-Loop: hello
1894 X-Roundup-Version: 1.4.8
1895 MIME-Version: 1.0
1897 --===============0639262320==
1898 Content-Type: text/plain; charset="us-ascii"
1899 MIME-Version: 1.0
1900 Content-Transfer-Encoding: 7bit
1904 You are not a registered user. Please register at:
1906 http://tracker.example/cgi-bin/roundup.cgi/bugs/user?template=register
1908 ...before sending mail to the tracker.
1910 Unknown address: nonexisting@bork.bork.bork
1912 --===============0639262320==
1913 Content-Type: text/plain; charset="us-ascii"
1914 MIME-Version: 1.0
1915 Content-Transfer-Encoding: 7bit
1917 Content-Type: text/plain;
1918 charset="iso-8859-1"
1919 From: Nonexisting User <nonexisting@bork.bork.bork>
1920 To: issue_tracker@your.tracker.email.domain.example
1921 Message-Id: <dummy_test_message_id>
1922 Subject: [issue] Testing nonexisting user...
1924 This is a test submission of a new issue.
1926 --===============0639262320==--
1927 ''')
1929 def testEnc01(self):
1930 self.db.user.set(self.mary_id,
1931 realname='\xe4\xf6\xfc\xc4\xd6\xdc\xdf, Mary'.decode
1932 ('latin-1').encode('utf-8'))
1933 self.doNewIssue()
1934 self._handle_mail('''Content-Type: text/plain;
1935 charset="iso-8859-1"
1936 From: mary <mary@test.test>
1937 To: issue_tracker@your.tracker.email.domain.example
1938 Message-Id: <followup_dummy_id>
1939 In-Reply-To: <dummy_test_message_id>
1940 Subject: [issue1] Testing...
1941 Content-Type: text/plain;
1942 charset="iso-8859-1"
1943 Content-Transfer-Encoding: quoted-printable
1945 A message with encoding (encoded oe =F6)
1947 ''')
1948 self.compareMessages(self._get_mail(),
1949 '''FROM: roundup-admin@your.tracker.email.domain.example
1950 TO: chef@bork.bork.bork, richard@test.test
1951 Content-Type: text/plain; charset="utf-8"
1952 Subject: [issue1] Testing...
1953 To: chef@bork.bork.bork, richard@test.test
1954 From: =?utf-8?b?w6TDtsO8w4TDlsOcw58sIE1hcnk=?=
1955 <issue_tracker@your.tracker.email.domain.example>
1956 Reply-To: Roundup issue tracker
1957 <issue_tracker@your.tracker.email.domain.example>
1958 MIME-Version: 1.0
1959 Message-Id: <followup_dummy_id>
1960 In-Reply-To: <dummy_test_message_id>
1961 X-Roundup-Name: Roundup issue tracker
1962 X-Roundup-Loop: hello
1963 X-Roundup-Issue-Status: chatting
1964 Content-Transfer-Encoding: quoted-printable
1967 =C3=A4=C3=B6=C3=BC=C3=84=C3=96=C3=9C=C3=9F, Mary <mary@test.test> added the=
1968 comment:
1970 A message with encoding (encoded oe =C3=B6)
1972 ----------
1973 status: unread -> chatting
1975 _______________________________________________________________________
1976 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
1977 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
1978 _______________________________________________________________________
1979 ''')
1981 def testEncNonUTF8(self):
1982 self.doNewIssue()
1983 self.instance.config.EMAIL_CHARSET = 'iso-8859-1'
1984 self._handle_mail('''Content-Type: text/plain;
1985 charset="iso-8859-1"
1986 From: mary <mary@test.test>
1987 To: issue_tracker@your.tracker.email.domain.example
1988 Message-Id: <followup_dummy_id>
1989 In-Reply-To: <dummy_test_message_id>
1990 Subject: [issue1] Testing...
1991 Content-Type: text/plain;
1992 charset="iso-8859-1"
1993 Content-Transfer-Encoding: quoted-printable
1995 A message with encoding (encoded oe =F6)
1997 ''')
1998 self.compareMessages(self._get_mail(),
1999 '''FROM: roundup-admin@your.tracker.email.domain.example
2000 TO: chef@bork.bork.bork, richard@test.test
2001 Content-Type: text/plain; charset="iso-8859-1"
2002 Subject: [issue1] Testing...
2003 To: chef@bork.bork.bork, richard@test.test
2004 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
2005 Reply-To: Roundup issue tracker
2006 <issue_tracker@your.tracker.email.domain.example>
2007 MIME-Version: 1.0
2008 Message-Id: <followup_dummy_id>
2009 In-Reply-To: <dummy_test_message_id>
2010 X-Roundup-Name: Roundup issue tracker
2011 X-Roundup-Loop: hello
2012 X-Roundup-Issue-Status: chatting
2013 Content-Transfer-Encoding: quoted-printable
2016 Contrary, Mary <mary@test.test> added the comment:
2018 A message with encoding (encoded oe =F6)
2020 ----------
2021 status: unread -> chatting
2023 _______________________________________________________________________
2024 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2025 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2026 _______________________________________________________________________
2027 ''')
2030 def testMultipartEnc01(self):
2031 self.doNewIssue()
2032 self._handle_mail('''Content-Type: text/plain;
2033 charset="iso-8859-1"
2034 From: mary <mary@test.test>
2035 To: issue_tracker@your.tracker.email.domain.example
2036 Message-Id: <followup_dummy_id>
2037 In-Reply-To: <dummy_test_message_id>
2038 Subject: [issue1] Testing...
2039 Content-Type: multipart/mixed;
2040 boundary="----_=_NextPart_000_01"
2042 This message is in MIME format. Since your mail reader does not understand
2043 this format, some or all of this message may not be legible.
2045 ------_=_NextPart_000_01
2046 Content-Type: text/plain;
2047 charset="iso-8859-1"
2048 Content-Transfer-Encoding: quoted-printable
2050 A message with first part encoded (encoded oe =F6)
2052 ''')
2053 self.compareMessages(self._get_mail(),
2054 '''FROM: roundup-admin@your.tracker.email.domain.example
2055 TO: chef@bork.bork.bork, richard@test.test
2056 Content-Type: text/plain; charset="utf-8"
2057 Subject: [issue1] Testing...
2058 To: chef@bork.bork.bork, richard@test.test
2059 From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
2060 Reply-To: Roundup issue tracker
2061 <issue_tracker@your.tracker.email.domain.example>
2062 MIME-Version: 1.0
2063 Message-Id: <followup_dummy_id>
2064 In-Reply-To: <dummy_test_message_id>
2065 X-Roundup-Name: Roundup issue tracker
2066 X-Roundup-Loop: hello
2067 X-Roundup-Issue-Status: chatting
2068 Content-Transfer-Encoding: quoted-printable
2071 Contrary, Mary <mary@test.test> added the comment:
2073 A message with first part encoded (encoded oe =C3=B6)
2075 ----------
2076 status: unread -> chatting
2078 _______________________________________________________________________
2079 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2080 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2081 _______________________________________________________________________
2082 ''')
2084 def testContentDisposition(self):
2085 self.doNewIssue()
2086 self._handle_mail('''Content-Type: text/plain;
2087 charset="iso-8859-1"
2088 From: mary <mary@test.test>
2089 To: issue_tracker@your.tracker.email.domain.example
2090 Message-Id: <followup_dummy_id>
2091 In-Reply-To: <dummy_test_message_id>
2092 Subject: [issue1] Testing...
2093 Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE"
2094 Content-Disposition: inline
2097 --bCsyhTFzCvuiizWE
2098 Content-Type: text/plain; charset=us-ascii
2099 Content-Disposition: inline
2101 test attachment binary
2103 --bCsyhTFzCvuiizWE
2104 Content-Type: application/octet-stream
2105 Content-Disposition: attachment; filename="main.dvi"
2106 Content-Transfer-Encoding: base64
2108 SnVzdCBhIHRlc3QgAQo=
2110 --bCsyhTFzCvuiizWE--
2111 ''')
2112 messages = self.db.issue.get('1', 'messages')
2113 messages.sort()
2114 file = self.db.file.getnode (self.db.msg.get(messages[-1], 'files')[0])
2115 self.assertEqual(file.name, 'main.dvi')
2116 self.assertEqual(file.content, 'Just a test \001\n')
2118 def testFollowupStupidQuoting(self):
2119 self.doNewIssue()
2121 self._handle_mail('''Content-Type: text/plain;
2122 charset="iso-8859-1"
2123 From: richard <richard@test.test>
2124 To: issue_tracker@your.tracker.email.domain.example
2125 Message-Id: <followup_dummy_id>
2126 In-Reply-To: <dummy_test_message_id>
2127 Subject: Re: "[issue1] Testing... "
2129 This is a followup
2130 ''')
2131 self.compareMessages(self._get_mail(),
2132 '''FROM: roundup-admin@your.tracker.email.domain.example
2133 TO: chef@bork.bork.bork
2134 Content-Type: text/plain; charset="utf-8"
2135 Subject: [issue1] Testing...
2136 To: chef@bork.bork.bork
2137 From: richard <issue_tracker@your.tracker.email.domain.example>
2138 Reply-To: Roundup issue tracker
2139 <issue_tracker@your.tracker.email.domain.example>
2140 MIME-Version: 1.0
2141 Message-Id: <followup_dummy_id>
2142 In-Reply-To: <dummy_test_message_id>
2143 X-Roundup-Name: Roundup issue tracker
2144 X-Roundup-Loop: hello
2145 X-Roundup-Issue-Status: chatting
2146 Content-Transfer-Encoding: quoted-printable
2149 richard <richard@test.test> added the comment:
2151 This is a followup
2153 ----------
2154 status: unread -> chatting
2156 _______________________________________________________________________
2157 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2158 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2159 _______________________________________________________________________
2160 ''')
2162 def testEmailQuoting(self):
2163 self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'no'
2164 self.innerTestQuoting('''This is a followup
2165 ''')
2167 def testEmailQuotingRemove(self):
2168 self.instance.config.EMAIL_KEEP_QUOTED_TEXT = 'yes'
2169 self.innerTestQuoting('''Blah blah wrote:
2170 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2171 > skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2172 >
2174 This is a followup
2175 ''')
2177 def innerTestQuoting(self, expect):
2178 nodeid = self.doNewIssue()
2180 messages = self.db.issue.get(nodeid, 'messages')
2182 self._handle_mail('''Content-Type: text/plain;
2183 charset="iso-8859-1"
2184 From: richard <richard@test.test>
2185 To: issue_tracker@your.tracker.email.domain.example
2186 Message-Id: <followup_dummy_id>
2187 In-Reply-To: <dummy_test_message_id>
2188 Subject: Re: [issue1] Testing...
2190 Blah blah wrote:
2191 > Blah bklaskdfj sdf asdf jlaskdf skj sdkfjl asdf
2192 > skdjlkjsdfalsdkfjasdlfkj dlfksdfalksd fj
2193 >
2195 This is a followup
2196 ''')
2197 # figure the new message id
2198 newmessages = self.db.issue.get(nodeid, 'messages')
2199 for msg in messages:
2200 newmessages.remove(msg)
2201 messageid = newmessages[0]
2203 self.compareMessages(self.db.msg.get(messageid, 'content'), expect)
2205 def testUserLookup(self):
2206 i = self.db.user.create(username='user1', address='user1@foo.com')
2207 self.assertEqual(uidFromAddress(self.db, ('', 'user1@foo.com'), 0), i)
2208 self.assertEqual(uidFromAddress(self.db, ('', 'USER1@foo.com'), 0), i)
2209 i = self.db.user.create(username='user2', address='USER2@foo.com')
2210 self.assertEqual(uidFromAddress(self.db, ('', 'USER2@foo.com'), 0), i)
2211 self.assertEqual(uidFromAddress(self.db, ('', 'user2@foo.com'), 0), i)
2213 def testUserAlternateLookup(self):
2214 i = self.db.user.create(username='user1', address='user1@foo.com',
2215 alternate_addresses='user1@bar.com')
2216 self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), i)
2217 self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), i)
2219 def testUserAlternateSubstringNomatch(self):
2220 i = self.db.user.create(username='user1', address='user1@foo.com',
2221 alternate_addresses='x-user1@bar.com')
2222 self.assertEqual(uidFromAddress(self.db, ('', 'user1@bar.com'), 0), 0)
2223 self.assertEqual(uidFromAddress(self.db, ('', 'USER1@bar.com'), 0), 0)
2225 def testUserCreate(self):
2226 i = uidFromAddress(self.db, ('', 'user@foo.com'), 1)
2227 self.assertNotEqual(uidFromAddress(self.db, ('', 'user@bar.com'), 1), i)
2229 def testRFC2822(self):
2230 ascii_header = "[issue243] This is a \"test\" - with 'quotation' marks"
2231 unicode_header = '[issue244] \xd0\xb0\xd0\xbd\xd0\xb4\xd1\x80\xd0\xb5\xd0\xb9'
2232 unicode_encoded = '=?utf-8?q?[issue244]_=D0=B0=D0=BD=D0=B4=D1=80=D0=B5=D0=B9?='
2233 self.assertEqual(rfc2822.encode_header(ascii_header), ascii_header)
2234 self.assertEqual(rfc2822.encode_header(unicode_header), unicode_encoded)
2236 def testRegistrationConfirmation(self):
2237 otk = "Aj4euk4LZSAdwePohj90SME5SpopLETL"
2238 self.db.getOTKManager().set(otk, username='johannes')
2239 self._handle_mail('''Content-Type: text/plain;
2240 charset="iso-8859-1"
2241 From: Chef <chef@bork.bork.bork>
2242 To: issue_tracker@your.tracker.email.domain.example
2243 Cc: richard@test.test
2244 Message-Id: <dummy_test_message_id>
2245 Subject: Re: Complete your registration to Roundup issue tracker
2246 -- key %s
2248 This is a test confirmation of registration.
2249 ''' % otk)
2250 self.db.user.lookup('johannes')
2252 def testFollowupOnNonIssue(self):
2253 self.db.keyword.create(name='Foo')
2254 self._handle_mail('''Content-Type: text/plain;
2255 charset="iso-8859-1"
2256 From: richard <richard@test.test>
2257 To: issue_tracker@your.tracker.email.domain.example
2258 Message-Id: <followup_dummy_id>
2259 In-Reply-To: <dummy_test_message_id>
2260 Subject: [keyword1] Testing... [name=Bar]
2262 ''')
2263 self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2265 def testResentFrom(self):
2266 nodeid = self._handle_mail('''Content-Type: text/plain;
2267 charset="iso-8859-1"
2268 From: Chef <chef@bork.bork.bork>
2269 Resent-From: mary <mary@test.test>
2270 To: issue_tracker@your.tracker.email.domain.example
2271 Cc: richard@test.test
2272 Message-Id: <dummy_test_message_id>
2273 Subject: [issue] Testing...
2275 This is a test submission of a new issue.
2276 ''')
2277 assert not os.path.exists(SENDMAILDEBUG)
2278 l = self.db.issue.get(nodeid, 'nosy')
2279 l.sort()
2280 self.assertEqual(l, [self.richard_id, self.mary_id])
2281 return nodeid
2283 def testDejaVu(self):
2284 self.assertRaises(IgnoreLoop, self._handle_mail,
2285 '''Content-Type: text/plain;
2286 charset="iso-8859-1"
2287 From: Chef <chef@bork.bork.bork>
2288 X-Roundup-Loop: hello
2289 To: issue_tracker@your.tracker.email.domain.example
2290 Cc: richard@test.test
2291 Message-Id: <dummy_test_message_id>
2292 Subject: Re: [issue] Testing...
2294 Hi, I've been mis-configured to loop messages back to myself.
2295 ''')
2297 def testItsBulkStupid(self):
2298 self.assertRaises(IgnoreBulk, self._handle_mail,
2299 '''Content-Type: text/plain;
2300 charset="iso-8859-1"
2301 From: Chef <chef@bork.bork.bork>
2302 Precedence: bulk
2303 To: issue_tracker@your.tracker.email.domain.example
2304 Cc: richard@test.test
2305 Message-Id: <dummy_test_message_id>
2306 Subject: Re: [issue] Testing...
2308 Hi, I'm on holidays, and this is a dumb auto-responder.
2309 ''')
2311 def testAutoReplyEmailsAreIgnored(self):
2312 self.assertRaises(IgnoreBulk, self._handle_mail,
2313 '''Content-Type: text/plain;
2314 charset="iso-8859-1"
2315 From: Chef <chef@bork.bork.bork>
2316 To: issue_tracker@your.tracker.email.domain.example
2317 Cc: richard@test.test
2318 Message-Id: <dummy_test_message_id>
2319 Subject: Re: [issue] Out of office AutoReply: Back next week
2321 Hi, I am back in the office next week
2322 ''')
2324 def testNoSubject(self):
2325 self.assertRaises(MailUsageError, self._handle_mail,
2326 '''Content-Type: text/plain;
2327 charset="iso-8859-1"
2328 From: Chef <chef@bork.bork.bork>
2329 To: issue_tracker@your.tracker.email.domain.example
2330 Cc: richard@test.test
2331 Reply-To: chef@bork.bork.bork
2332 Message-Id: <dummy_test_message_id>
2334 ''')
2336 #
2337 # TEST FOR INVALID DESIGNATOR HANDLING
2338 #
2339 def testInvalidDesignator(self):
2340 self.assertRaises(MailUsageError, self._handle_mail,
2341 '''Content-Type: text/plain;
2342 charset="iso-8859-1"
2343 From: Chef <chef@bork.bork.bork>
2344 To: issue_tracker@your.tracker.email.domain.example
2345 Subject: [frobulated] testing
2346 Cc: richard@test.test
2347 Reply-To: chef@bork.bork.bork
2348 Message-Id: <dummy_test_message_id>
2350 ''')
2351 self.assertRaises(MailUsageError, self._handle_mail,
2352 '''Content-Type: text/plain;
2353 charset="iso-8859-1"
2354 From: Chef <chef@bork.bork.bork>
2355 To: issue_tracker@your.tracker.email.domain.example
2356 Subject: [issue12345] testing
2357 Cc: richard@test.test
2358 Reply-To: chef@bork.bork.bork
2359 Message-Id: <dummy_test_message_id>
2361 ''')
2363 def testInvalidClassLoose(self):
2364 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2365 nodeid = self._handle_mail('''Content-Type: text/plain;
2366 charset="iso-8859-1"
2367 From: Chef <chef@bork.bork.bork>
2368 To: issue_tracker@your.tracker.email.domain.example
2369 Subject: [frobulated] testing
2370 Cc: richard@test.test
2371 Reply-To: chef@bork.bork.bork
2372 Message-Id: <dummy_test_message_id>
2374 ''')
2375 assert not os.path.exists(SENDMAILDEBUG)
2376 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2377 '[frobulated] testing')
2379 def testInvalidClassLooseReply(self):
2380 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2381 nodeid = self._handle_mail('''Content-Type: text/plain;
2382 charset="iso-8859-1"
2383 From: Chef <chef@bork.bork.bork>
2384 To: issue_tracker@your.tracker.email.domain.example
2385 Subject: Re: [frobulated] testing
2386 Cc: richard@test.test
2387 Reply-To: chef@bork.bork.bork
2388 Message-Id: <dummy_test_message_id>
2390 ''')
2391 assert not os.path.exists(SENDMAILDEBUG)
2392 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2393 '[frobulated] testing')
2395 def testInvalidClassLoose(self):
2396 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2397 nodeid = self._handle_mail('''Content-Type: text/plain;
2398 charset="iso-8859-1"
2399 From: Chef <chef@bork.bork.bork>
2400 To: issue_tracker@your.tracker.email.domain.example
2401 Subject: [issue1234] testing
2402 Cc: richard@test.test
2403 Reply-To: chef@bork.bork.bork
2404 Message-Id: <dummy_test_message_id>
2406 ''')
2407 assert not os.path.exists(SENDMAILDEBUG)
2408 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2409 '[issue1234] testing')
2411 def testClassLooseOK(self):
2412 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2413 self.db.keyword.create(name='Foo')
2414 nodeid = self._handle_mail('''Content-Type: text/plain;
2415 charset="iso-8859-1"
2416 From: Chef <chef@bork.bork.bork>
2417 To: issue_tracker@your.tracker.email.domain.example
2418 Subject: [keyword1] Testing... [name=Bar]
2419 Cc: richard@test.test
2420 Reply-To: chef@bork.bork.bork
2421 Message-Id: <dummy_test_message_id>
2423 ''')
2424 assert not os.path.exists(SENDMAILDEBUG)
2425 self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2427 def testClassStrictInvalid(self):
2428 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2429 self.instance.config.MAILGW_DEFAULT_CLASS = ''
2431 message = '''Content-Type: text/plain;
2432 charset="iso-8859-1"
2433 From: Chef <chef@bork.bork.bork>
2434 To: issue_tracker@your.tracker.email.domain.example
2435 Subject: Testing...
2436 Cc: richard@test.test
2437 Reply-To: chef@bork.bork.bork
2438 Message-Id: <dummy_test_message_id>
2440 '''
2441 self.assertRaises(MailUsageError, self._handle_mail, message)
2443 def testClassStrictValid(self):
2444 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'strict'
2445 self.instance.config.MAILGW_DEFAULT_CLASS = ''
2447 nodeid = self._handle_mail('''Content-Type: text/plain;
2448 charset="iso-8859-1"
2449 From: Chef <chef@bork.bork.bork>
2450 To: issue_tracker@your.tracker.email.domain.example
2451 Subject: [issue] Testing...
2452 Cc: richard@test.test
2453 Reply-To: chef@bork.bork.bork
2454 Message-Id: <dummy_test_message_id>
2456 ''')
2458 assert not os.path.exists(SENDMAILDEBUG)
2459 self.assertEqual(self.db.issue.get(nodeid, 'title'), 'Testing...')
2461 #
2462 # TEST FOR INVALID COMMANDS HANDLING
2463 #
2464 def testInvalidCommands(self):
2465 self.assertRaises(MailUsageError, self._handle_mail,
2466 '''Content-Type: text/plain;
2467 charset="iso-8859-1"
2468 From: Chef <chef@bork.bork.bork>
2469 To: issue_tracker@your.tracker.email.domain.example
2470 Subject: testing [frobulated]
2471 Cc: richard@test.test
2472 Reply-To: chef@bork.bork.bork
2473 Message-Id: <dummy_test_message_id>
2475 ''')
2477 def testInvalidCommandPassthrough(self):
2478 self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'none'
2479 nodeid = self._handle_mail('''Content-Type: text/plain;
2480 charset="iso-8859-1"
2481 From: Chef <chef@bork.bork.bork>
2482 To: issue_tracker@your.tracker.email.domain.example
2483 Subject: testing [frobulated]
2484 Cc: richard@test.test
2485 Reply-To: chef@bork.bork.bork
2486 Message-Id: <dummy_test_message_id>
2488 ''')
2489 assert not os.path.exists(SENDMAILDEBUG)
2490 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2491 'testing [frobulated]')
2493 def testInvalidCommandPassthroughLoose(self):
2494 self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2495 nodeid = self._handle_mail('''Content-Type: text/plain;
2496 charset="iso-8859-1"
2497 From: Chef <chef@bork.bork.bork>
2498 To: issue_tracker@your.tracker.email.domain.example
2499 Subject: testing [frobulated]
2500 Cc: richard@test.test
2501 Reply-To: chef@bork.bork.bork
2502 Message-Id: <dummy_test_message_id>
2504 ''')
2505 assert not os.path.exists(SENDMAILDEBUG)
2506 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2507 'testing [frobulated]')
2509 def testInvalidCommandPassthroughLooseOK(self):
2510 self.instance.config.MAILGW_SUBJECT_SUFFIX_PARSING = 'loose'
2511 nodeid = self._handle_mail('''Content-Type: text/plain;
2512 charset="iso-8859-1"
2513 From: Chef <chef@bork.bork.bork>
2514 To: issue_tracker@your.tracker.email.domain.example
2515 Subject: testing [assignedto=mary]
2516 Cc: richard@test.test
2517 Reply-To: chef@bork.bork.bork
2518 Message-Id: <dummy_test_message_id>
2520 ''')
2521 assert not os.path.exists(SENDMAILDEBUG)
2522 self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2523 self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2525 def testCommandDelimiters(self):
2526 self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2527 nodeid = self._handle_mail('''Content-Type: text/plain;
2528 charset="iso-8859-1"
2529 From: Chef <chef@bork.bork.bork>
2530 To: issue_tracker@your.tracker.email.domain.example
2531 Subject: testing {assignedto=mary}
2532 Cc: richard@test.test
2533 Reply-To: chef@bork.bork.bork
2534 Message-Id: <dummy_test_message_id>
2536 ''')
2537 assert not os.path.exists(SENDMAILDEBUG)
2538 self.assertEqual(self.db.issue.get(nodeid, 'title'), 'testing')
2539 self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), self.mary_id)
2541 def testPrefixDelimiters(self):
2542 self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2543 self.db.keyword.create(name='Foo')
2544 self._handle_mail('''Content-Type: text/plain;
2545 charset="iso-8859-1"
2546 From: richard <richard@test.test>
2547 To: issue_tracker@your.tracker.email.domain.example
2548 Message-Id: <followup_dummy_id>
2549 In-Reply-To: <dummy_test_message_id>
2550 Subject: {keyword1} Testing... {name=Bar}
2552 ''')
2553 assert not os.path.exists(SENDMAILDEBUG)
2554 self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2556 def testCommandDelimitersIgnore(self):
2557 self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '{}'
2558 nodeid = self._handle_mail('''Content-Type: text/plain;
2559 charset="iso-8859-1"
2560 From: Chef <chef@bork.bork.bork>
2561 To: issue_tracker@your.tracker.email.domain.example
2562 Subject: testing [assignedto=mary]
2563 Cc: richard@test.test
2564 Reply-To: chef@bork.bork.bork
2565 Message-Id: <dummy_test_message_id>
2567 ''')
2568 assert not os.path.exists(SENDMAILDEBUG)
2569 self.assertEqual(self.db.issue.get(nodeid, 'title'),
2570 'testing [assignedto=mary]')
2571 self.assertEqual(self.db.issue.get(nodeid, 'assignedto'), None)
2573 def testReplytoMatch(self):
2574 self.instance.config.MAILGW_SUBJECT_PREFIX_PARSING = 'loose'
2575 nodeid = self.doNewIssue()
2576 nodeid2 = self._handle_mail('''Content-Type: text/plain;
2577 charset="iso-8859-1"
2578 From: Chef <chef@bork.bork.bork>
2579 To: issue_tracker@your.tracker.email.domain.example
2580 Message-Id: <dummy_test_message_id2>
2581 In-Reply-To: <dummy_test_message_id>
2582 Subject: Testing...
2584 Followup message.
2585 ''')
2587 nodeid3 = self._handle_mail('''Content-Type: text/plain;
2588 charset="iso-8859-1"
2589 From: Chef <chef@bork.bork.bork>
2590 To: issue_tracker@your.tracker.email.domain.example
2591 Message-Id: <dummy_test_message_id3>
2592 In-Reply-To: <dummy_test_message_id2>
2593 Subject: Testing...
2595 Yet another message in the same thread/issue.
2596 ''')
2598 self.assertEqual(nodeid, nodeid2)
2599 self.assertEqual(nodeid, nodeid3)
2601 def testHelpSubject(self):
2602 message = '''Content-Type: text/plain;
2603 charset="iso-8859-1"
2604 From: Chef <chef@bork.bork.bork>
2605 To: issue_tracker@your.tracker.email.domain.example
2606 Message-Id: <dummy_test_message_id2>
2607 In-Reply-To: <dummy_test_message_id>
2608 Subject: hElp
2611 '''
2612 self.assertRaises(MailUsageHelp, self._handle_mail, message)
2614 def testMaillistSubject(self):
2615 self.instance.config.MAILGW_SUBJECT_SUFFIX_DELIMITERS = '[]'
2616 self.db.keyword.create(name='Foo')
2617 self._handle_mail('''Content-Type: text/plain;
2618 charset="iso-8859-1"
2619 From: Chef <chef@bork.bork.bork>
2620 To: issue_tracker@your.tracker.email.domain.example
2621 Subject: [mailinglist-name] [keyword1] Testing.. [name=Bar]
2622 Cc: richard@test.test
2623 Reply-To: chef@bork.bork.bork
2624 Message-Id: <dummy_test_message_id>
2626 ''')
2628 assert not os.path.exists(SENDMAILDEBUG)
2629 self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2631 def testUnknownPrefixSubject(self):
2632 self.db.keyword.create(name='Foo')
2633 self._handle_mail('''Content-Type: text/plain;
2634 charset="iso-8859-1"
2635 From: Chef <chef@bork.bork.bork>
2636 To: issue_tracker@your.tracker.email.domain.example
2637 Subject: VeryStrangeRe: [keyword1] Testing.. [name=Bar]
2638 Cc: richard@test.test
2639 Reply-To: chef@bork.bork.bork
2640 Message-Id: <dummy_test_message_id>
2642 ''')
2644 assert not os.path.exists(SENDMAILDEBUG)
2645 self.assertEqual(self.db.keyword.get('1', 'name'), 'Bar')
2647 def testOneCharSubject(self):
2648 message = '''Content-Type: text/plain;
2649 charset="iso-8859-1"
2650 From: Chef <chef@bork.bork.bork>
2651 To: issue_tracker@your.tracker.email.domain.example
2652 Subject: b
2653 Cc: richard@test.test
2654 Reply-To: chef@bork.bork.bork
2655 Message-Id: <dummy_test_message_id>
2657 '''
2658 try:
2659 self._handle_mail(message)
2660 except MailUsageError:
2661 self.fail('MailUsageError raised')
2663 def testIssueidLast(self):
2664 nodeid1 = self.doNewIssue()
2665 nodeid2 = self._handle_mail('''Content-Type: text/plain;
2666 charset="iso-8859-1"
2667 From: mary <mary@test.test>
2668 To: issue_tracker@your.tracker.email.domain.example
2669 Message-Id: <followup_dummy_id>
2670 In-Reply-To: <dummy_test_message_id>
2671 Subject: New title [issue1]
2673 This is a second followup
2674 ''')
2676 assert nodeid1 == nodeid2
2677 self.assertEqual(self.db.issue.get(nodeid2, 'title'), "Testing...")
2679 def testSecurityMessagePermissionContent(self):
2680 id = self.doNewIssue()
2681 issue = self.db.issue.getnode (id)
2682 self.db.security.addRole(name='Nomsg')
2683 self.db.security.addPermissionToRole('Nomsg', 'Email Access')
2684 for cl in 'issue', 'file', 'keyword':
2685 for p in 'View', 'Edit', 'Create':
2686 self.db.security.addPermissionToRole('Nomsg', p, cl)
2687 self.db.user.set(self.mary_id, roles='Nomsg')
2688 nodeid = self._handle_mail('''Content-Type: text/plain;
2689 charset="iso-8859-1"
2690 From: Chef <chef@bork.bork.bork>
2691 To: issue_tracker@your.tracker.email.domain.example
2692 Message-Id: <dummy_test_message_id_2>
2693 Subject: [issue%(id)s] Testing... [nosy=+mary]
2695 Just a test reply
2696 '''%locals())
2697 assert os.path.exists(SENDMAILDEBUG)
2698 self.compareMessages(self._get_mail(),
2699 '''FROM: roundup-admin@your.tracker.email.domain.example
2700 TO: chef@bork.bork.bork, richard@test.test
2701 Content-Type: text/plain; charset="utf-8"
2702 Subject: [issue1] Testing...
2703 To: richard@test.test
2704 From: "Bork, Chef" <issue_tracker@your.tracker.email.domain.example>
2705 Reply-To: Roundup issue tracker
2706 <issue_tracker@your.tracker.email.domain.example>
2707 MIME-Version: 1.0
2708 Message-Id: <dummy_test_message_id_2>
2709 In-Reply-To: <dummy_test_message_id>
2710 X-Roundup-Name: Roundup issue tracker
2711 X-Roundup-Loop: hello
2712 X-Roundup-Issue-Status: chatting
2713 Content-Transfer-Encoding: quoted-printable
2716 Bork, Chef <chef@bork.bork.bork> added the comment:
2718 Just a test reply
2720 ----------
2721 nosy: +mary
2722 status: unread -> chatting
2724 _______________________________________________________________________
2725 Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
2726 <http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
2727 _______________________________________________________________________
2728 ''')
2730 def testOutlookAttachment(self):
2731 message = '''X-MimeOLE: Produced By Microsoft Exchange V6.5
2732 Content-class: urn:content-classes:message
2733 MIME-Version: 1.0
2734 Content-Type: multipart/mixed;
2735 boundary="----_=_NextPart_001_01CACA65.40A51CBC"
2736 Subject: Example of a failed outlook attachment e-mail
2737 Date: Tue, 23 Mar 2010 01:43:44 -0700
2738 Message-ID: <CA37F17219784343816CA6613D2E339205E7D0F9@nrcwstexb1.nrc.ca>
2739 X-MS-Has-Attach: yes
2740 X-MS-TNEF-Correlator:
2741 Thread-Topic: Example of a failed outlook attachment e-mail
2742 Thread-Index: AcrKJo/t3pUBBwTpSwWNE3LE67UBDQ==
2743 From: "Hugh" <richard@test.test>
2744 To: <richard@test.test>
2745 X-OriginalArrivalTime: 23 Mar 2010 08:45:57.0350 (UTC) FILETIME=[41893860:01CACA65]
2747 This is a multi-part message in MIME format.
2749 ------_=_NextPart_001_01CACA65.40A51CBC
2750 Content-Type: multipart/alternative;
2751 boundary="----_=_NextPart_002_01CACA65.40A51CBC"
2754 ------_=_NextPart_002_01CACA65.40A51CBC
2755 Content-Type: text/plain;
2756 charset="us-ascii"
2757 Content-Transfer-Encoding: quoted-printable
2760 Hi Richard,
2762 I suppose this isn't the exact message that was sent but is a resend of
2763 one of my trial messages that failed. For your benefit I changed the
2764 subject line and am adding these words to the message body. Should
2765 still be as problematic, but if you like I can resend an exact copy of a
2766 failed message changing nothing except putting your address instead of
2767 our tracker.
2769 Thanks very much for taking time to look into this. Much appreciated.
2771 <<battery backup>>=20
2773 ------_=_NextPart_002_01CACA65.40A51CBC
2774 Content-Type: text/html;
2775 charset="us-ascii"
2776 Content-Transfer-Encoding: quoted-printable
2778 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2779 <HTML>
2780 <HEAD>
2781 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2782 charset=3Dus-ascii">
2783 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2784 6.5.7654.12">
2785 <TITLE>Example of a failed outlook attachment e-mail</TITLE>
2786 </HEAD>
2787 <BODY>
2788 <!-- Converted from text/rtf format -->
2789 <BR>
2791 <P><FONT SIZE=3D2 FACE=3D"Arial">Hi Richard,</FONT>
2792 </P>
2794 <P><FONT SIZE=3D2 FACE=3D"Arial">I suppose this isn't the exact message =
2795 that was sent but is a resend of one of my trial messages that =
2796 failed. For your benefit I changed the subject line and am adding =
2797 these words to the message body. Should still be as problematic, =
2798 but if you like I can resend an exact copy of a failed message changing =
2799 nothing except putting your address instead of our tracker.</FONT></P>
2801 <P><FONT SIZE=3D2 FACE=3D"Arial">Thanks very much for taking time to =
2802 look into this. Much appreciated.</FONT>
2803 </P>
2804 <BR>
2806 <P><FONT FACE=3D"Arial" SIZE=3D2 COLOR=3D"#000000"> <<battery =
2807 backup>> </FONT>
2808 </P>
2810 </BODY>
2811 </HTML>
2812 ------_=_NextPart_002_01CACA65.40A51CBC--
2814 ------_=_NextPart_001_01CACA65.40A51CBC
2815 Content-Type: message/rfc822
2816 Content-Transfer-Encoding: 7bit
2818 X-MimeOLE: Produced By Microsoft Exchange V6.5
2819 MIME-Version: 1.0
2820 Content-Type: multipart/alternative;
2821 boundary="----_=_NextPart_003_01CAC15A.29717800"
2822 X-OriginalArrivalTime: 11 Mar 2010 20:33:51.0249 (UTC) FILETIME=[28FEE010:01CAC15A]
2823 Content-class: urn:content-classes:message
2824 Subject: battery backup
2825 Date: Thu, 11 Mar 2010 13:33:43 -0700
2826 Message-ID: <p06240809c7bf02f9624c@[128.114.22.203]>
2827 X-MS-Has-Attach:
2828 X-MS-TNEF-Correlator:
2829 Thread-Topic: battery backup
2830 Thread-Index: AcrBWimtulTrSvBdQ2CcfZ8lyQdxmQ==
2831 From: "Jerry" <jerry@test.test>
2832 To: "Hugh" <hugh@test.test>
2834 This is a multi-part message in MIME format.
2836 ------_=_NextPart_003_01CAC15A.29717800
2837 Content-Type: text/plain;
2838 charset="iso-8859-1"
2839 Content-Transfer-Encoding: quoted-printable
2841 Dear Hugh,
2842 A car batter has an energy capacity of ~ 500Wh. A UPS=20
2843 battery is worse than this.
2845 if we need to provied 100kW for 30 minutes that will take 100 car=20
2846 batteries. This seems like an awful lot of batteries.
2848 Of course I like your idea of making the time 1 minute, so we get to=20
2849 a more modest number of batteries
2851 Jerry
2854 ------_=_NextPart_003_01CAC15A.29717800
2855 Content-Type: text/html;
2856 charset="iso-8859-1"
2857 Content-Transfer-Encoding: quoted-printable
2859 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2860 <HTML>
2861 <HEAD>
2862 <META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
2863 charset=3Diso-8859-1">
2864 <META NAME=3D"Generator" CONTENT=3D"MS Exchange Server version =
2865 6.5.7654.12">
2866 <TITLE>battery backup</TITLE>
2867 </HEAD>
2868 <BODY>
2869 <!-- Converted from text/plain format -->
2871 <P><FONT SIZE=3D2>Dear Hugh,</FONT>
2873 <BR> <FONT SIZE=3D2>A car =
2874 batter has an energy capacity of ~ 500Wh. A UPS </FONT>
2876 <BR><FONT SIZE=3D2>battery is worse than this.</FONT>
2877 </P>
2879 <P><FONT SIZE=3D2>if we need to provied 100kW for 30 minutes that will =
2880 take 100 car </FONT>
2882 <BR><FONT SIZE=3D2>batteries. This seems like an awful lot of =
2883 batteries.</FONT>
2884 </P>
2886 <P><FONT SIZE=3D2>Of course I like your idea of making the time 1 =
2887 minute, so we get to </FONT>
2889 <BR><FONT SIZE=3D2>a more modest number of batteries</FONT>
2890 </P>
2892 <P><FONT SIZE=3D2>Jerry</FONT>
2893 </P>
2895 </BODY>
2896 </HTML>
2897 ------_=_NextPart_003_01CAC15A.29717800--
2899 ------_=_NextPart_001_01CACA65.40A51CBC--
2900 '''
2901 nodeid = self._handle_mail(message)
2902 assert not os.path.exists(SENDMAILDEBUG)
2903 msgid = self.db.issue.get(nodeid, 'messages')[0]
2904 self.assert_(self.db.msg.get(msgid, 'content').startswith('Hi Richard'))
2905 self.assertEqual(self.db.msg.get(msgid, 'files'), ['1', '2'])
2906 fileid = self.db.msg.get(msgid, 'files')[0]
2907 self.assertEqual(self.db.file.get(fileid, 'type'), 'text/html')
2908 fileid = self.db.msg.get(msgid, 'files')[1]
2909 self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2911 def testForwardedMessageAttachment(self):
2912 message = '''Return-Path: <rgg@test.test>
2913 Received: from localhost(127.0.0.1), claiming to be "[115.130.26.69]"
2914 via SMTP by localhost, id smtpdAAApLaWrq; Tue Apr 13 23:10:05 2010
2915 Message-ID: <4BC4F9C7.50409@test.test>
2916 Date: Wed, 14 Apr 2010 09:09:59 +1000
2917 From: Rupert Goldie <rgg@test.test>
2918 User-Agent: Thunderbird 2.0.0.24 (Windows/20100228)
2919 MIME-Version: 1.0
2920 To: ekit issues <issues@test.test>
2921 Subject: [Fwd: PHP ERROR (fb)] post limit reached
2922 Content-Type: multipart/mixed; boundary="------------000807090608060304010403"
2924 This is a multi-part message in MIME format.
2925 --------------000807090608060304010403
2926 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
2927 Content-Transfer-Encoding: 7bit
2929 Catch this exception and log it without emailing.
2931 --------------000807090608060304010403
2932 Content-Type: message/rfc822; name="PHP ERROR (fb).eml"
2933 Content-Transfer-Encoding: 7bit
2934 Content-Disposition: inline; filename="PHP ERROR (fb).eml"
2936 Return-Path: <ektravj@test.test>
2937 X-Sieve: CMU Sieve 2.2
2938 via SMTP by crown.off.ekorp.com, id smtpdAAA1JaW1o; Tue Apr 13 23:01:04 2010
2939 X-Virus-Scanned: by amavisd-new at ekit.com
2940 To: facebook-errors@test.test
2941 From: ektravj@test.test
2942 Subject: PHP ERROR (fb)
2943 Message-Id: <20100413230100.D601D27E84@mail2.elax3.ekorp.com>
2944 Date: Tue, 13 Apr 2010 23:01:00 +0000 (UTC)
2946 [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
2947 Stack trace:
2948 #0 /app/01/www/virtual/fb.ekit.com/htdocs/gateway/ekit/feed/index.php(178): fb_exceptions(Object(FacebookRestClientException))
2949 #1 {main}
2950 thrown in /app/01/www/virtual/fb.ekit.com/htdocs/includes/functions.php on line 280
2953 --------------000807090608060304010403--
2954 '''
2955 nodeid = self._handle_mail(message)
2956 assert not os.path.exists(SENDMAILDEBUG)
2957 msgid = self.db.issue.get(nodeid, 'messages')[0]
2958 self.assertEqual(self.db.msg.get(msgid, 'content'),
2959 'Catch this exception and log it without emailing.')
2960 self.assertEqual(self.db.msg.get(msgid, 'files'), ['1'])
2961 fileid = self.db.msg.get(msgid, 'files')[0]
2962 self.assertEqual(self.db.file.get(fileid, 'type'), 'message/rfc822')
2964 pgp_test_key = """
2965 -----BEGIN PGP PRIVATE KEY BLOCK-----
2966 Version: GnuPG v1.4.10 (GNU/Linux)
2968 lQOYBE6NqtsBCADG3UUMYxjwUOpDDVvr0Y8qkvKsgdF79en1zfHtRYlmZc+EJxg8
2969 53CCFGReQWJwOjyP3/SLJwJqfiPR7MAYAqJsm/4U2lxF7sIlEnlrRpFuvB625KOQ
2970 oedCkI4nLa+4QAXHxVX2qLx7es3r2JAoitZLX7ZtUB7qGSRh98DmdAgCY3CFN7iZ
2971 w6xpvIU+LNbsHSo1sf8VP6z7NHQFacgrVvLyRJ4C5lTPU42iM5E6HKxYFExNV3Rn
2972 +2G0bsuiifHV6nJQD73onjwcC6tU97W779dllHlhG3SSP0KlnwmCCvPMlQvROk0A
2973 rLyzKWcUpZwK1aLRYByjFMH9WYXRkhf08bkDABEBAAEAB/9dcmSb6YUyiBNM5t4m
2974 9hZcXykBvw79PRVvmBLy+BYUtArLgsN0+xx3Q7XWRMtJCVSkFw0GxpHwEM4sOyAZ
2975 KEPC3ZqLmgB6LDO2z/OWYVa9vlCAiPgDYtEVCnCCIInN/ue4dBZtDeVj8NUK2n0D
2976 UBpa2OMUgu3D+4SJNK7EnAmXdOaP6yfe6SXwcQfti8UoSFMJRkQkbY1rm/6iPfON
2977 t2RBAc7jW4eRzdciWCfvJfMSj9cqxTBQWz5vVadeY9Bm/IKw1HiKNBrJratq2v+D
2978 VGr0EkE9oOa5zbgZt2CFvknE4YhGmv81xFdK5GXr8L7nluZrePMblWbkI2ICTbV0
2979 RKLhBADYLvyDFX3cCoFzWmCl5L32G6LLfTt0yU0eUHcAzXd7QjOZN289HWYEmdVi
2980 kpxQPDxhWz+m8qt0HJGFl2+BKpZJBaT/L5AcqTBODxarxCSBTIVhCjD/46XvLY0h
2981 b2ZnG8HSLyFdRj07vk+qTvcF58qUuYFSLIF2t2imTCR/PwR/LwQA632vn2/7KIHj
2982 DR0O+G9eccTtAfX4TN4Q4Ua3WByClLZu/LSAenCLZ1CHVABEH6dwwjEARLeNUdLi
2983 Xy5KKlpr2vkoh96fnw0r2yg7dlBXq4yQKjJBXwNaKpuvqgzd8en0zJGLXxzt0NT3
2984 H+QNIP2WZMJSDQcDh3HhQrH0IeNdDm0D/iyJgSMXvqjm+KhYIa3xiloQsCRlDNm+
2985 XC7Eo5hsjvBaIKba6o9oL9oEiSVUFryPWKWIpi0P7/F5voJL6KFSZTor3x3o9CcC
2986 qHyqMHfNL23EAVJulySfPYLC7S3QB+tCBLXmKxb/YXCSLVi/UDzVgvWN6KIknZg2
2987 6uDLUzPbzDGjOZ20K1JvdW5kdXAgVGVzdGtleSA8cm91bmR1cC1hZG1pbkBleGFt
2988 cGxlLmNvbT6JATgEEwECACIFAk6NqtsCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
2989 AheAAAoJEFrc/VYxw4dBG7oIAMCU9sRjK0dS7z/IGJ8KcCOQNN674AooJLn+J9Ew
2990 BT6/WxMY13nm/iK0uX2sOGnnXdg1PJ15IvD8zB5wXLbe25t6oRl5G58vmeKEyjc8
2991 QTB43/c8EsqY1ob7EVcuhrJCSS/JM8ApzQyXrh2QNmS+mBCJcx74MeipE6mNVT9j
2992 VscizixdFjqvJLkbW1kGac3Wj+c3ICNUIp0lbwb+Ve2rXlU+iXHEDqaVJDMEppme
2993 gDiZl+bKYrqljhZkH9Slv55uqqXUSg1SmTm/2orHUdAmDc6Y6azKNqQEqD2B0JyT
2994 jTJQJVMl5Oln63aZDCTxHkoqn8q06OjLJRD4on7jlanZEladA5gETo2q2wEIALEF
2995 poVkZrnqme2M8FObrQyVB+ZYT2mox56WLyInbxVFDg20qqIvQfVE0P69Yuf1OXkj
2996 q7bNI03Jvo+uzxpztOKPDo7tnbQ7bXbOmq3n4wUoN29NMrYNg6tF1ubEv1WwYUMw
2997 7LfF4BLMETXpT0JElV1+awfP9rrGiyWkH4enG612HT+1OoA0R0nNH0kslD6OhdoR
2998 VDqkyiCmdY9x176EhzhL3vCoN6ywRVTfFbAJiMv9UDzxs0SStmVOK/l5XLfWQO6f
2999 9boAHihpnxEfPIJhsD+FpVKVf3g85qWAjh2BfuzdW79vjLBdTHJQxg4HdhliWbXg
3000 PjjrVEgWEFVc+NDlNb0AEQEAAQAH/A1a6sbniI8q3DVoIP19zN7FI5UaQSuB2Jrl
3001 +Q+vlUQv3dvk2cwQmqj2vyRo2gcRS3u7LYpGDGLNqfshv22JyzId2YWo9vE7sTTP
3002 E4EJRz8CsLlMmVsoxoVBE0cnvXOpMef6z0ZyFEdMGVmi4iA9bQi3r+V6qBehQQA0
3003 U034VTCPN4yvWyq6TWsABesOx48nkQ5TlduIq2ZGNCR8Vd1fe6vGM7YXyQWxy5ke
3004 guqmph73H2bOB6hSuUnyBFKtinrF9MbCGA0PqheUVqy0p7og6x/pEoAVkKBJ9Ki+
3005 ePuQtBl5h9e3SbiN+r7aa6T0Ygx/7igl4eWPfvJYIXYXc4aKiwEEANEa5rBoN7Ta
3006 ED+R47Rg9w/EW3VDQ6R3Szy1rvIKjC6JlDyKlGgTeWEFjDeTwCB4xU7YtxVpt6bk
3007 b7RBtDkRck2+DwnscutA7Uxn267UxzNUd1IxhUccRFRfRS7OEnmlVmaLUnOeHHwe
3008 OrZyRSiNVnh0QABEJnwNjX4m139v6YD9BADYuM5XCawI63pYa3/l7UX9H5EH95OZ
3009 G9Hw7pXQ/YJYerSxRx+2q0+koRcdoby1TVaRrdDC+kOm3PI7e66S5rnaZ1FZeYQP
3010 nVYzyGqNnsvncs24kYBL8DYaDDfdm7vfzSEqia0VNqZ4TMbwJLk5f5Ys4WOF791G
3011 LPJgrAPG1jgDwQQAovKbw0u6blIUKsUYOLsviaLCyFC9DwaHqIZwjy8omnh7MaKE
3012 7+MXxJpfcVqFifj3CmqMdSmTfkgbKQPAI46Q1OKWvkvUxEvi7WATo4taEXupRFL5
3013 jnL8c4h46z8UpMX2CMwWU0k1Et/zlBoYy7gNON7tF2/uuN18zWFBlD72HuM9HIkB
3014 HwQYAQIACQUCTo2q2wIbDAAKCRBa3P1WMcOHQYI+CACDXJf1e695LpcsrVxKgiQr
3015 9fTbNJYB+tjbnd9vas92Gz1wZcQV9RjLkYQeEbOpWQud/1UeLRsFECMj7kbgAEqz
3016 7fIO4SeN8hFEvvZ+lI0AoBi4XvuUcCm5kvAodvmF8M9kQiUzF1gm+R9QQeJFDLpW
3017 8Gg7J3V3qM+N0FuXrypYcsEv7n/RJ1n+lhTW5hFzKBlNL4WrAhY/QsXEbmdsa478
3018 tzuHlETtjMm4g4DgppUdlCMegcpjjC9zKsN5xFOQmNMTO/6rPFUqk3k3T6I0LV4O
3019 zm4xNC+wwAA69ibnbrY1NR019et7RYW+qBudGbpJB1ABzkf/NsaCj6aTaubt7PZP
3020 =3uFZ
3021 -----END PGP PRIVATE KEY BLOCK-----
3022 """
3024 john_doe_key = """
3025 -----BEGIN PGP PRIVATE KEY BLOCK-----
3026 Version: GnuPG v1.4.10 (GNU/Linux)
3028 lQHYBE6NwvABBACxg7QqV2qHywwM3wae6HAHJVEo7EeYA6Lv0pZlW3Aw4CCCnpgJ
3029 jA7CekGFcmGmoCaN9ezuVAPTgUlK4yt8a7P6cT0vw1q341Om9IEKAu59RpNZN/H9
3030 6GfZ95bU51W/hdTFysH1DRwbCR3MowvLeA6Pk4cZlPsYHD0SD3De2i1BewARAQAB
3031 AAP+IRi4L6jKwPS3k3LFrj0SHhL0Fdgv5QTQjTxLNCyfN02iYhglqqoFWncm3jWc
3032 RU/YwGEYwrrBV97kBmVihzkhfgFRsxynE9PMGKKEAuRcAl21RPJDFA6Dlnp6M2No
3033 rR6eoAhrlZ8+KsK9JaXSMalzO/Yh4u3mOinq3f3XL96wAEkCAMAxeZMF5pnXARNR
3034 Y7u2clhNNnLuf+BzpENCFMaWzWPyTcvbf4xNK7ZHPxFVZpX5/qAPJ8rnTaOTHxnN
3035 5PgqbO8CAOxyrTw/muakTJLg+FXdn8BgxZGJXMT7KmkU9SReefjo7c1WlnZxKIAy
3036 6vLIG8WMGpdfCFDve0YLr/GGyDtOjDUB/RN3gn6qnAJThBnVk2wESZVx41fihbIF
3037 ACCKc9heFskzwurtvvp+bunM3quwrSH1hWvxiWJlDmGSn8zQFypGChifgLQZSm9o
3038 biBEb2UgPGpvaG5AdGVzdC50ZXN0Poi4BBMBAgAiBQJOjcLwAhsDBgsJCAcDAgYV
3039 CAIJCgsEFgIDAQIeAQIXgAAKCRC/z7qg+FujnPWiA/9T5SOGraRNIVVIyvJvYwkG
3040 OTAfQ0K3QMlLoQMPmaEbx9Q+isF15M9sOMcl1XGO4UNWuCPIIN8z/y/OLgAB0ZuL
3041 GlnAPPOOZ+MlaUXiMYo8oi416QZrMDf2H/Nkc10csiXm+zMl8RqeIQBEeljNyJ+t
3042 MG1EWn/PHTwFTd/VePuQdJ0B2AROjcLwAQQApw+72jKy0/wqg5SAtnVSkA1F3Jna
3043 /OG+ufz5dX57jkMFRvFoksWIWqHmiCjdE5QV8j+XTnjElhLsmrgjl7aAFveb30R6
3044 ImmcpKMN31vAp4RZlnyYbYUCY4IXFuz3n1CaUL+mRx5yNJykrZNfpWNf2pwozkZq
3045 lcDI69ymIW5acXUAEQEAAQAD/R7Jdf98l1scngMYo228ikYUxBqm2eX/fiQNXDWM
3046 ZR2u+TJ9O53MvFejfXX7Pd6lTDQUBwDFncjgXO0YYSrMzabhqpqoKLqOIpZmBuWC
3047 Hh1lvcFoIYoDR2LkiJ9EPBUEVUBDsUO8ajkILEE3G+DDpCaf9Vo82lCVyhDESqyt
3048 v4lxAgDOLpoq1Whv5Ejr6FifTWytCiQjH2P1SmePlQmy6oEJRUYA1t4zYrzCJUX8
3049 VAvPjh9JXilP6mhDbyQArWllewV9AgDPbVOf75ktRwfhje26tZsukqWYJCc1XvoH
3050 3PTzA7vH1HZZq7dvxa87PiSnkOLEsIAsI+4jpeMxpPlQRxUvHf1ZAf9rK3v3HMJ/
3051 2xVzwK24Oaj+g2O7D/fdqtLFGe5S5JobnTyp9xArDAhaZ/AKfDMYjUIKMP+bdNAf
3052 y8fQUtuawFltm1GInwQYAQIACQUCTo3C8AIbDAAKCRC/z7qg+FujnDzYA/9EU6Pv
3053 Ci1+DCtxjnq7IOvOjqExhFNGvN9Dw17Tl8HcyW3if9v5RxeSWYKl0DhzVdzMQgH/
3054 78q4F4W1q2IkB7SCpXizHLIc3eh8iZkbWZE+CGPvTpqyF03Yi16qhxpAbkGs2Yhq
3055 jTx5oJ4CL5fybBOZLg+BTlK4HIee6xEcbNoq+A==
3056 =ZKBW
3057 -----END PGP PRIVATE KEY BLOCK-----
3058 """
3060 ownertrust = """
3061 723762CD5A5FECB76DC72DF85ADCFD5631C38741:6:
3062 2940C247A1FBAD508A1AF24BBFCFBAA0F85BA39C:6:
3063 """
3065 class MailgwPGPTestCase(MailgwTestAbstractBase):
3066 pgphome = 'pgp-test-home'
3067 def setUp(self):
3068 MailgwTestAbstractBase.setUp(self)
3069 self.db.security.addRole(name = 'pgp', description = 'PGP Role')
3070 self.instance.config['PGP_HOMEDIR'] = self.pgphome
3071 self.instance.config['PGP_ROLES'] = 'pgp'
3072 self.instance.config['PGP_ENABLE'] = True
3073 self.instance.config['MAIL_DOMAIN'] = 'example.com'
3074 self.instance.config['ADMIN_EMAIL'] = 'roundup-admin@example.com'
3075 self.db.user.set(self.john_id, roles='User,pgp')
3076 os.mkdir(self.pgphome)
3077 os.environ['GNUPGHOME'] = self.pgphome
3078 ctx = pyme.core.Context()
3079 key = pyme.core.Data(pgp_test_key)
3080 ctx.op_import(key)
3081 key = pyme.core.Data(john_doe_key)
3082 ctx.op_import(key)
3083 # trust-modelling with pyme isn't working in 0.8.1
3084 # based on libgpgme11 1.2.0, also tried in C -- same thing.
3085 otrust = os.popen ('gpg --import-ownertrust 2> /dev/null', 'w')
3086 otrust.write(ownertrust)
3087 otrust.close()
3089 def tearDown(self):
3090 MailgwTestAbstractBase.tearDown(self)
3091 if os.path.exists(self.pgphome):
3092 shutil.rmtree(self.pgphome)
3094 def testPGPUnsignedMessage(self):
3095 self.assertRaises(MailUsageError, self._handle_mail,
3096 '''Content-Type: text/plain;
3097 charset="iso-8859-1"
3098 From: John Doe <john@test.test>
3099 To: issue_tracker@your.tracker.email.domain.example
3100 Message-Id: <dummy_test_message_id>
3101 Subject: [issue] Testing non-signed message...
3103 This is no pgp signed message.
3104 ''')
3106 signed_msg = '''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 '''
3138 def testPGPSignedMessage(self):
3139 nodeid = self._handle_mail(self.signed_msg)
3140 m = self.db.issue.get(nodeid, 'messages')[0]
3141 self.assertEqual(self.db.msg.get(m, 'content'),
3142 'This is a pgp signed message.')
3144 def testPGPSignedMessageFail(self):
3145 # require both, signing and encryption
3146 self.instance.config['PGP_REQUIRE_INCOMING'] = 'both'
3147 self.assertRaises(MailUsageError, self._handle_mail, self.signed_msg)
3149 encrypted_msg = '''Content-Disposition: inline
3150 From: John Doe <john@test.test>
3151 To: roundup-admin@example.com
3152 Subject: [issue] Testing encrypted message...
3153 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
3154 boundary="d6Gm4EdcadzBjdND"
3156 --d6Gm4EdcadzBjdND
3157 Content-Type: application/pgp-encrypted
3158 Content-Disposition: attachment
3160 Version: 1
3162 --d6Gm4EdcadzBjdND
3163 Content-Type: application/octet-stream
3164 Content-Disposition: inline; filename="msg.asc"
3166 -----BEGIN PGP MESSAGE-----
3167 Version: GnuPG v1.4.10 (GNU/Linux)
3169 hQEMAzfeQttq+Q2YAQf9FxCtZVgC7jAy6UkeAJ1imCpnh9DgKA5w40OFtrY4mVAp
3170 cL7kCkvGvJCW7uQZrmSgIiYaZGLI3GS42XutORC6E6PzBEW0fJUMIXYmoSd0OFeY
3171 3H2+854qu37W/uCOWM9OnPFIH8g8q8DgYy88i0goM+Ot9Q96yFfJ7QymanOZJgVa
3172 MNC+oKDiIZKiE3PCwtGr+8CHZN/9J6O4FeJijBlr09C5LXc+Nif5T0R0nt17MAns
3173 9g2UvGxW8U24NAS1mOg868U05hquLPIcFz9jGZGknJu7HBpOkQ9GjKqkzN8pgZVN
3174 VbN8IdDqi0QtRKE44jtWQlyNlESMjv6GtC2V9F6qKNK8AfHtBexDhyv4G9cPFFNO
3175 afQ6e4dPi89RYIQyydtwiqao8fj6jlAy2Z1cbr7YxwBG7BeUZv9yis7ShaAIo78S
3176 82MrCYpSjfHNwKiSfC5yITw22Uv4wWgixVdAsaSdtBqEKXJPG9LNey18ArsBjSM1
3177 P81iDOWUp/uyIe5ZfvNI38BBxEYslPTUlDk2GB8J2Vun7IWHoj9a4tY3IotC9jBr
3178 5Qnigzqrt7cJZX6OrN0c+wnOjXbMGYXmgSs4jeM=
3179 =XX5Q
3180 -----END PGP MESSAGE-----
3182 --d6Gm4EdcadzBjdND--
3183 '''
3184 def testPGPEncryptedUnsignedMessageError(self):
3185 self.assertRaises(MailUsageError, self._handle_mail, self.encrypted_msg)
3187 def testPGPEncryptedUnsignedMessage(self):
3188 # no error if we don't require a signature:
3189 self.instance.config['PGP_REQUIRE_INCOMING'] = 'encrypted'
3190 nodeid = self._handle_mail (self.encrypted_msg)
3191 m = self.db.issue.get(nodeid, 'messages')[0]
3192 self.assertEqual(self.db.msg.get(m, 'content'),
3193 'This is the text to be encrypted')
3195 def testPGPEncryptedUnsignedMessageFromNonPGPUser(self):
3196 msg = self.encrypted_msg.replace('John Doe <john@test.test>',
3197 '"Contrary, Mary" <mary@test.test>')
3198 nodeid = self._handle_mail (msg)
3199 m = self.db.issue.get(nodeid, 'messages')[0]
3200 self.assertEqual(self.db.msg.get(m, 'content'),
3201 'This is the text to be encrypted')
3202 self.assertEqual(self.db.msg.get(m, 'author'), self.mary_id)
3204 # check that a bounce-message that is triggered *after*
3205 # decrypting is properly encrypted:
3206 def testPGPEncryptedUnsignedMessageCheckBounce(self):
3207 # allow non-signed msg
3208 self.instance.config['PGP_REQUIRE_INCOMING'] = 'encrypted'
3209 # don't allow creation of message, trigger error *after* decrypt
3210 self.db.user.set(self.john_id, roles='pgp')
3211 self.db.security.addPermissionToRole('pgp', 'Email Access')
3212 self.db.security.addPermissionToRole('pgp', 'Create', 'issue')
3213 # trap_exc=1: we want a bounce message:
3214 self._handle_mail(self.encrypted_msg, trap_exc=1)
3215 m = self._get_mail()
3216 fp = FeedParser()
3217 fp.feed(m)
3218 parts = fp.close().get_payload()
3219 self.assertEqual(len(parts),2)
3220 self.assertEqual(parts[0].get_payload().strip(), 'Version: 1')
3221 crypt = pyme.core.Data(parts[1].get_payload())
3222 plain = pyme.core.Data()
3223 ctx = pyme.core.Context()
3224 res = ctx.op_decrypt(crypt, plain)
3225 self.assertEqual(res, None)
3226 plain.seek(0,0)
3227 fp = FeedParser()
3228 fp.feed(plain.read())
3229 parts = fp.close().get_payload()
3230 self.assertEqual(len(parts),2)
3231 self.assertEqual(parts[0].get_payload().strip(),
3232 'You are not permitted to create messages.')
3233 self.assertEqual(parts[1].get_payload().strip(),
3234 '''Content-Type: text/plain; charset=us-ascii
3235 Content-Disposition: inline
3237 This is the text to be encrypted''')
3240 def testPGPEncryptedSignedMessage(self):
3241 # require both, signing and encryption
3242 self.instance.config['PGP_REQUIRE_INCOMING'] = 'both'
3243 nodeid = self._handle_mail('''Content-Disposition: inline
3244 From: John Doe <john@test.test>
3245 To: roundup-admin@example.com
3246 Subject: Testing encrypted and signed message
3247 MIME-Version: 1.0
3248 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
3249 boundary="ReaqsoxgOBHFXBhH"
3251 --ReaqsoxgOBHFXBhH
3252 Content-Type: application/pgp-encrypted
3253 Content-Disposition: attachment
3255 Version: 1
3257 --ReaqsoxgOBHFXBhH
3258 Content-Type: application/octet-stream
3259 Content-Disposition: inline; filename="msg.asc"
3261 -----BEGIN PGP MESSAGE-----
3262 Version: GnuPG v1.4.10 (GNU/Linux)
3264 hQEMAzfeQttq+Q2YAQf+NaC3r8qBURQqxHH9IAP4vg0QAP2yj3n0v6guo1lRf5BA
3265 EUfTQ3jc3chxLvzTgoUIuMOvhlNroqR1lgLwhfSTCyuKWDZa+aVNiSgsB2MD44Xd
3266 mAkKKmnmOGLmfbICbPQZxl4xNhCMTHiAy1xQE6mTj/+pEAq5XxjJUwn/gJ3O1Wmd
3267 NyWtJY2N+TRbxUVB2WhG1j9J1D2sjhG26TciE8JeuLDZzaiVNOW9YlX2Lw5KtlkR
3268 Hkgw6Xme06G0XXZUcm9JuBU/7oFP/tSrC1tBsnVlq1pZYf6AygIBdXWb9gD/WmXh
3269 7Eu/xCKrw4RFnXnTgmBz/NHRfVDkfdSscZqexnG1D9LAwQHSuVf8sxDPNesv0W+8
3270 e49loVjvU+Y0BCFQAbWSW4iOEUYZpW/ITRE4+wIqMXZbAraeBV0KPZ4hAa3qSmf+
3271 oZBRcbzssL163Odx/OHRuK2J2CHC654+crrlTBnxd/RUKgRbSUKwrZzB2G6OPcGv
3272 wfiqXsY+XvSZtTbWuvUJxePh8vhhhjpuo1JtlrYc3hZ9OYgoCoV1JiLl5c60U5Es
3273 oUT9GDl1Qsgb4dF4TJ1IBj+riYiocYpJxPhxzsy6liSLNy2OA6VEjG0FGk53+Ok9
3274 7UzOA+WaHJHSXafZzrdP1TWJUFlOMA+dOgTKpH69eL1+IRfywOjEwp1UNSbLnJpc
3275 D0QQLwIFttplKvYkn0DZByJCVnIlGkl4s5LM5rnc8iecX8Jad0iRIlPV6CVM+Nso
3276 WdARUfyJfXAmz8uk4f2sVfeMu1gdMySdjvxwlgHDJdBPIG51r2b8L/NCTiC57YjF
3277 zGhS06FLl3V1xx6gBlpqQHjut3efrAGpXGBVpnTJMOcgYAk=
3278 =jt/n
3279 -----END PGP MESSAGE-----
3281 --ReaqsoxgOBHFXBhH--
3282 ''')
3283 m = self.db.issue.get(nodeid, 'messages')[0]
3284 self.assertEqual(self.db.msg.get(m, 'content'),
3285 'This is the text of a signed and encrypted email.')
3288 def test_suite():
3289 suite = unittest.TestSuite()
3290 suite.addTest(unittest.makeSuite(MailgwTestCase))
3291 if pyme is not None:
3292 suite.addTest(unittest.makeSuite(MailgwPGPTestCase))
3293 else:
3294 print "Skipping PGP tests"
3295 return suite
3297 if __name__ == '__main__':
3298 runner = unittest.TextTestRunner()
3299 unittest.main(testRunner=runner)
3301 # vim: set filetype=python sts=4 sw=4 et si :