From 02f4dc6224081ba4ed4c34618ed5e365c054ad0a Mon Sep 17 00:00:00 2001 From: richard Date: Thu, 4 Dec 2003 23:34:25 +0000 Subject: [PATCH] ignore incoming email with "Precedence: bulk" (sf patch 843489) git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2013 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 1 + doc/index.txt | 1 + roundup/mailgw.py | 122 +++++++++++++++++++++++++------------------- test/test_mailgw.py | 36 +++++++++++-- 4 files changed, 104 insertions(+), 56 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 836f5af..069021a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,7 @@ Feature: alphabetically (sf feature 790512). - added script for copying user(s) from tracker to tracker (sf patch 828963) +- ignore incoming email with "Precedence: bulk" (sf patch 843489) Fixed: - mysql documentation fixed to note requirement of 4.0+ and InnoDB diff --git a/doc/index.txt b/doc/index.txt index 87d94a1..819dd9e 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -58,6 +58,7 @@ Titus Brown, Roch'e Compaan, Paul F. Dubois, Jeff Epler, +Tom Epperly, Hernan Martinez Foffani, Ajit George, Frank Gibbons, diff --git a/roundup/mailgw.py b/roundup/mailgw.py index 50a3c54..468333e 100644 --- a/roundup/mailgw.py +++ b/roundup/mailgw.py @@ -73,7 +73,7 @@ are calling the create() method to create a new node). If an auditor raises an exception, the original message is bounced back to the sender with the explanatory message given in the exception. -$Id: mailgw.py,v 1.138 2003-11-13 03:41:38 richard Exp $ +$Id: mailgw.py,v 1.139 2003-12-04 23:34:25 richard Exp $ """ import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri @@ -92,14 +92,22 @@ class MailUsageError(ValueError): pass class MailUsageHelp(Exception): - pass - -class MailLoop(Exception): - """ We've seen this message before... """ + """ We need to send the help message to the user. """ pass class Unauthorized(Exception): """ Access denied """ + pass + +class IgnoreMessage(Exception): + """ A general class of message that we should ignore. """ + pass +class IgnoreBulk(IgnoreMessage): + """ This is email from a mailing list or from a vacation program. """ + pass +class IgnoreLoop(IgnoreMessage): + """ We've seen this message before... """ + pass def initialiseSecurity(security): ''' Create some Permissions and Roles on the security object @@ -300,53 +308,7 @@ class MailGW: sendto = message.getaddrlist('resent-from') if not sendto: sendto = message.getaddrlist('from') - if sendto: - if not self.trapExceptions: - return self.handle_message(message) - try: - return self.handle_message(message) - except MailUsageHelp: - # bounce the message back to the sender with the usage message - fulldoc = '\n'.join(string.split(__doc__, '\n')[2:]) - sendto = [sendto[0][1]] - m = [''] - m.append('\n\nMail Gateway Help\n=================') - m.append(fulldoc) - self.mailer.bounce_message(message, sendto, m, - subject="Mail Gateway Help") - except MailUsageError, value: - # bounce the message back to the sender with the usage message - fulldoc = '\n'.join(string.split(__doc__, '\n')[2:]) - sendto = [sendto[0][1]] - m = [''] - m.append(str(value)) - m.append('\n\nMail Gateway Help\n=================') - m.append(fulldoc) - self.mailer.bounce_message(message, sendto, m) - except Unauthorized, value: - # just inform the user that he is not authorized - sendto = [sendto[0][1]] - m = [''] - m.append(str(value)) - self.mailer.bounce_message(message, sendto, m) - except MailLoop: - # XXX we should use a log file here... - return - except: - # bounce the message back to the sender with the error message - # XXX we should use a log file here... - sendto = [sendto[0][1], self.instance.config.ADMIN_EMAIL] - m = [''] - m.append('An unexpected error occurred during the processing') - m.append('of your message. The tracker administrator is being') - m.append('notified.\n') - m.append('---- traceback of failure ----') - s = cStringIO.StringIO() - import traceback - traceback.print_exc(None, s) - m.append(s.getvalue()) - self.mailer.bounce_message(message, sendto, m) - else: + if not sendto: # very bad-looking message - we don't even know who sent it # XXX we should use a log file here... sendto = [self.instance.config.ADMIN_EMAIL] @@ -358,6 +320,56 @@ class MailGW: m.append('') self.mailer.bounce_message(message, sendto, m, subject='Badly formed message from mail gateway') + return + + # try normal message-handling + if not self.trapExceptions: + return self.handle_message(message) + try: + return self.handle_message(message) + except MailUsageHelp: + # bounce the message back to the sender with the usage message + fulldoc = '\n'.join(string.split(__doc__, '\n')[2:]) + sendto = [sendto[0][1]] + m = [''] + m.append('\n\nMail Gateway Help\n=================') + m.append(fulldoc) + self.mailer.bounce_message(message, sendto, m, + subject="Mail Gateway Help") + except MailUsageError, value: + # bounce the message back to the sender with the usage message + fulldoc = '\n'.join(string.split(__doc__, '\n')[2:]) + sendto = [sendto[0][1]] + m = [''] + m.append(str(value)) + m.append('\n\nMail Gateway Help\n=================') + m.append(fulldoc) + self.mailer.bounce_message(message, sendto, m) + except Unauthorized, value: + # just inform the user that he is not authorized + sendto = [sendto[0][1]] + m = [''] + m.append(str(value)) + self.mailer.bounce_message(message, sendto, m) + except IgnoreMessage: + # XXX we should use a log file here... + # do not take any action + # this exception is thrown when email should be ignored + return + except: + # bounce the message back to the sender with the error message + # XXX we should use a log file here... + sendto = [sendto[0][1], self.instance.config.ADMIN_EMAIL] + m = [''] + m.append('An unexpected error occurred during the processing') + m.append('of your message. The tracker administrator is being') + m.append('notified.\n') + m.append('---- traceback of failure ----') + s = cStringIO.StringIO() + import traceback + traceback.print_exc(None, s) + m.append(s.getvalue()) + self.mailer.bounce_message(message, sendto, m) def get_part_data_decoded(self,part): encoding = part.getencoding() @@ -397,7 +409,11 @@ class MailGW: ''' # detect loops if message.getheader('x-roundup-loop', ''): - raise MailLoop + raise IgnoreLoop + + # detect Precedence: Bulk + if (message.getheader('precedence', '') == 'bulk'): + raise IgnoreBulk # XXX Don't enable. This doesn't work yet. # "[^A-z.]tracker\+(?P[^\d\s]+)(?P\d+)\@some.dom.ain[^A-z.]" diff --git a/test/test_mailgw.py b/test/test_mailgw.py index 5ffc55e..bf8c440 100644 --- a/test/test_mailgw.py +++ b/test/test_mailgw.py @@ -8,7 +8,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # -# $Id: test_mailgw.py,v 1.62 2003-11-13 03:41:38 richard Exp $ +# $Id: test_mailgw.py,v 1.63 2003-12-04 23:34:25 richard Exp $ import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822 @@ -18,7 +18,8 @@ if not os.environ.has_key('SENDMAILDEBUG'): os.environ['SENDMAILDEBUG'] = 'mail-test.log' SENDMAILDEBUG = os.environ['SENDMAILDEBUG'] -from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, parseContent +from roundup.mailgw import MailGW, Unauthorized, uidFromAddress, \ + parseContent, IgnoreLoop, IgnoreBulk from roundup import init, instance, rfc2822 @@ -961,7 +962,36 @@ This is a test submission of a new issue. l.sort() self.assertEqual(l, [self.richard_id, self.mary_id]) return nodeid - + + + def testDejaVu(self): + self.assertRaises(IgnoreLoop, self._send_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +X-Roundup-Loop: hello +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test +Message-Id: +Subject: Re: [issue] Testing... + +Hi, I've been mis-configured to loop messages back to myself. +''') + + def testItsBulkStupid(self): + self.assertRaises(IgnoreBulk, self._send_mail, + '''Content-Type: text/plain; + charset="iso-8859-1" +From: Chef +Precedence: bulk +To: issue_tracker@your.tracker.email.domain.example +Cc: richard@test +Message-Id: +Subject: Re: [issue] Testing... + +Hi, I'm on holidays, and this is a dumb auto-responder. +''') + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(MailgwTestCase)) -- 2.30.2