? limit_attachment_size.patch
? limit_attachment_size_with_unittest.patch
Index: roundup/configuration.py
===================================================================
RCS file: /cvsroot/roundup/roundup/roundup/configuration.py,v
retrieving revision 1.43
diff -u -r1.43 configuration.py
--- roundup/configuration.py	12 May 2007 16:14:54 -0000	1.43
+++ roundup/configuration.py	15 Aug 2007 20:57:23 -0000
@@ -728,6 +728,10 @@
             "\"multiple\" then a separate email is sent to each\n"
             "recipient. If \"single\" then a single email is sent with\n"
             "each recipient as a CC address."),
+        (IntegerNumberOption, "max_attachment_size", str (sys.maxint),
+            "Attachments larger then the given number of bytes "
+            "won't be attached to nosy mails. They will be replaced by a link "
+            "to the roundup tracker's download page for the file.")
     ), "Nosy messages sending"),
 )
 
Index: roundup/roundupdb.py
===================================================================
RCS file: /cvsroot/roundup/roundup/roundup/roundupdb.py,v
retrieving revision 1.128
diff -u -r1.128 roundupdb.py
--- roundup/roundupdb.py	18 Dec 2006 11:34:41 -0000	1.128
+++ roundup/roundupdb.py	15 Aug 2007 20:57:23 -0000
@@ -318,6 +318,23 @@
         if msgid is not None:
             m.append(messages.get(msgid, 'content', ''))
 
+        # get the files for this message
+        message_files = []
+        if msgid :
+            max_size = self.db.config.NOSY_MAX_ATTACHMENT_SIZE
+            base     = self.db.config.TRACKER_WEB
+            if not base.endswith("/") :
+                base = base + "/"
+            for fileid in messages.get(msgid, 'files') :
+                filenode = files.getnode(fileid)
+                if len(filenode.content) <= max_size :
+                    message_files.append(filenode)
+                else :
+                    filename = filenode.name
+                    link     = "".join((base, files.classname, fileid))
+                    m.append(_("File '%(filename)s' not attached - you can "
+                               "download it from %(link)s." % locals()))
+
         # add the change note
         if note:
             m.append(note)
@@ -336,12 +353,6 @@
         quopri.encode(content, content_encoded, 0)
         content_encoded = content_encoded.getvalue()
 
-        # get the files for this message
-        if msgid is None:
-            message_files = None
-        else:
-            message_files = messages.get(msgid, 'files')
-
         # make sure the To line is always the same (for testing mostly)
         sendto.sort()
 
@@ -394,10 +405,10 @@
                 part.addheader('Content-Transfer-Encoding', 'quoted-printable')
                 body = part.startbody('text/plain; charset=%s'%charset)
                 body.write(content_encoded)
-                for fileid in message_files:
-                    name = files.get(fileid, 'name')
-                    mime_type = files.get(fileid, 'type')
-                    content = files.get(fileid, 'content')
+                for filenode in message_files:
+                    name = filenode.name
+                    mime_type = filenode.type
+                    content = filenode.content
                     part = writer.nextpart()
                     if mime_type == 'text/plain':
                         part.addheader('Content-Disposition',
Index: test/db_test_base.py
===================================================================
RCS file: /cvsroot/roundup/roundup/test/db_test_base.py,v
retrieving revision 1.85
diff -u -r1.85 db_test_base.py
--- test/db_test_base.py	11 Apr 2007 20:04:06 -0000	1.85
+++ test/db_test_base.py	15 Aug 2007 20:57:23 -0000
@@ -17,10 +17,11 @@
 #
 # $Id: db_test_base.py,v 1.85 2007/04/11 20:04:06 forsberg Exp $
 
-import unittest, os, shutil, errno, imp, sys, time, pprint, sets
+import unittest, os, shutil, errno, imp, sys, time, pprint, sets, base64
 
 from roundup.hyperdb import String, Password, Link, Multilink, Date, \
     Interval, DatabaseError, Boolean, Number, Node
+from roundup.mailer import Mailer
 from roundup import date, password, init, instance, configuration, support
 
 from mocknull import MockNull
@@ -69,8 +70,8 @@
     priority = module.Class(db, "priority", name=String(), order=String())
     priority.setkey("name")
     user = module.Class(db, "user", username=String(), password=Password(),
-        assignable=Boolean(), age=Number(), roles=String(),
-        supervisor=Link('user'))
+        assignable=Boolean(), age=Number(), roles=String(), address=String(),
+        supervisor=Link('user'),realname=String())
     user.setkey("username")
     file = module.FileClass(db, "file", name=String(), type=String(),
         comment=String(indexme="yes"), fooz=Password())
@@ -82,14 +83,18 @@
     stuff = module.Class(db, "stuff", stuff=String())
     session = module.Class(db, 'session', title=String())
     msg = module.FileClass(db, "msg", date=Date(),
-                           author=Link("user", do_journal='no'))
+                           author=Link("user", do_journal='no'),
+                           files=Multilink('file'), inreplyto=String(),
+                           messageid=String(),
+                           recipients=Multilink("user", do_journal='no')
+                           )
     session.disableJournalling()
     db.post_init()
     if create:
         user.create(username="admin", roles='Admin',
             password=password.Password('sekrit'))
         user.create(username="fred", roles='User',
-            password=password.Password('sekrit'))
+            password=password.Password('sekrit'), address='fred@example.com')
         status.create(name="unread")
         status.create(name="in-progress")
         status.create(name="testing")
@@ -881,14 +886,15 @@
 
     def testIndexingOnImport(self):
         msgcontent = 'Glrk'
-        msgid = self.db.msg.import_list(['content'], [repr(msgcontent)])
+        msgid = self.db.msg.import_list(['content', 'files', 'recipients'],
+                                        [repr(msgcontent), '[]', '[]'])
         msg_filename = self.db.filename(self.db.msg.classname, msgid,
                                         create=1)
         support.ensureParentsExist(msg_filename)
         msg_file = open(msg_filename, 'w')
         msg_file.write(msgcontent)
         msg_file.close()
-        
+
 
         filecontent = 'Brrk'
         fileid = self.db.file.import_list(['content'], [repr(filecontent)])
@@ -897,7 +903,7 @@
         support.ensureParentsExist(file_filename)
         file_file = open(file_filename, 'w')
         file_file.write(filecontent)
-        file_file.close()        
+        file_file.close()
 
         title = 'Bzzt'
         nodeid = self.db.issue.import_list(['title', 'messages', 'files',
@@ -922,7 +928,7 @@
         self.assertEquals(self.db.indexer.search([filecontent], self.db.issue),
                           {str(nodeid):{'files':[str(fileid)]}})
 
-        
+
 
     #
     # searching tests follow
@@ -1204,8 +1210,8 @@
 
     def testFilteringMultilinkSort(self):
         # 1: []                 Reverse:  1: []
-        # 2: []                           2: []              
-        # 3: ['admin','fred']             3: ['fred','admin']       
+        # 2: []                           2: []
+        # 3: ['admin','fred']             3: ['fred','admin']
         # 4: ['admin','bleep','fred']     4: ['fred','bleep','admin']
         # Note the sort order for the multilink doen't change when
         # reversing the sort direction due to the re-sorting of the
@@ -1496,7 +1502,7 @@
             ['1', '2', '3', '4', '5', '8', '6', '7'])
         ae(filt(None, {}, [('+','messages.author'), ('+','messages')]),
             ['6', '7', '8', '5', '4', '3', '1', '2'])
-        # The following will sort by 
+        # The following will sort by
         # author.supervisor.username and then by
         # author.username
         # I've resited the tempation to implement recursive orderprop
@@ -1687,6 +1693,38 @@
             'nosy', 'priority', 'spam', 'status', 'superseder'])
         self.assertEqual(self.db.issue.list(), ['1'])
 
+    def testNosyMail(self) :
+        """Creates one issue with two attachments, one smaller and one larger
+           than the set max_attachment_size.
+        """
+        db = self.db
+        db.config.NOSY_MAX_ATTACHMENT_SIZE = 4096
+        res = dict(mail_to = None, mail_msg = None)
+        def dummy_snd(s, to, msg, res=res) :
+            res["mail_to"], res["mail_msg"] = to, msg
+        backup, Mailer.smtp_send = Mailer.smtp_send, dummy_snd
+        try :
+            f1 = db.file.create(name="test1.txt", content="x" * 20)
+            f2 = db.file.create(name="test2.txt", content="y" * 5000)
+            m  = db.msg.create(content="one two", author="admin",
+                files = [f1, f2])
+            i  = db.issue.create(title='spam', files = [f1, f2],
+                messages = [m], nosy = [db.user.lookup("fred")])
+
+            db.issue.nosymessage(i, m, {})
+            mail_msg = res["mail_msg"].getvalue()
+            self.assertEqual(res["mail_to"], ["fred@example.com"])
+            self.failUnless("From: admin" in mail_msg)
+            self.failUnless("Subject: [issue1] spam" in mail_msg)
+            self.failUnless("New submission from admin" in mail_msg)
+            self.failUnless("one two" in mail_msg)
+            self.failIf("File 'test1.txt' not attached" in mail_msg)
+            self.failUnless(base64.b64encode("xxx") in mail_msg)
+            self.failUnless("File 'test2.txt' not attached" in mail_msg)
+            self.failIf(base64.b64encode("yyy") in mail_msg)
+        finally :
+            Mailer.smtp_send = backup
+
 class ROTest(MyTestCase):
     def setUp(self):
         # remove previous test, ignore errors

 	  	 
