[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[minion-cvs] First cut at implementing headers (server-side)



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv30719/lib/mixminion

Modified Files:
	BuildMessage.py Packet.py test.py 
Log Message:
First cut at implementing headers (server-side)

Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -d -r1.47 -r1.48
--- BuildMessage.py	11 Jun 2003 19:05:35 -0000	1.47
+++ BuildMessage.py	30 Jun 2003 17:33:33 -0000	1.48
@@ -690,3 +690,4 @@
         raise MixError("Routing info won't fit in header")
 
     return routing, sizes, totalSize
+

Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -d -r1.47 -r1.48
--- Packet.py	25 Jun 2003 17:03:11 -0000	1.47
+++ Packet.py	30 Jun 2003 17:33:33 -0000	1.48
@@ -11,6 +11,7 @@
 
 __all__ = [ 'compressData', 'CompressedDataTooLong', 'DROP_TYPE',
             'ENC_FWD_OVERHEAD', 'ENC_SUBHEADER_LEN',
+            'encodeMailHeaders', 'encodeMessageHeaders',
             'FRAGMENT_PAYLOAD_OVERHEAD', 'FWD_TYPE', 'FragmentPayload',
             'HEADER_LEN', 'IPV4Info', 'MAJOR_NO', 'MBOXInfo',
             'MBOX_TYPE', 'MINOR_NO', 'MIN_EXIT_TYPE',
@@ -20,7 +21,8 @@
             'SMTPInfo', 'SMTP_TYPE', 'SWAP_FWD_TYPE', 'SingletonPayload',
             'Subheader', 'TAG_LEN', 'TextEncodedMessage',
             'parseHeader', 'parseIPV4Info',
-            'parseMBOXInfo', 'parseMessage', 'parsePayload', 'parseReplyBlock',
+            'parseMBOXInfo', 'parseMessage', 'parseMessageAndHeaders',
+            'parsePayload', 'parseReplyBlock',
             'parseReplyBlocks', 'parseSMTPInfo', 'parseSubheader',
             'parseTextEncodedMessages', 'parseTextReplyBlocks', 'uncompressData'            ]
 
@@ -665,6 +667,58 @@
 
         return armorText(c, MESSAGE_ARMOR_NAME, headers=fields,
                          base64=(self.messageType!='TXT'))
+
+#----------------------------------------------------------------------
+# Header encoding
+
+def encodeMailHeaders(subject=None, fromAddr=None, inReplyTo=None,
+                      references=None):
+    """DOCDOC"""
+    #XXXX005 check values
+    headers = {}
+    if subject:
+        headers['SUBJECT'] = subject
+    if fromAddr:
+        headers['FROM'] = fromAddr
+    if inReplyTo:
+        headers['IN-REPLY-TO'] = inReplyTo
+    if references:
+        headers['REFERENCES'] = references
+    return encodeMessageHeaders(message, headers)
+
+def encodeMessageHeaders(headers):
+    """DOCDOC msg, dict
+
+       Requires that headers are in acceptable format.
+    """
+    items = []
+    hitems = headers.items()
+    hitems.sort()
+    for k,v in hitems:
+        items.append("%s:%s\n"%(k,v))
+    items.append("\n")
+    return "".join(items)
+
+HEADER_RE = re.compile(r'^([!-9;-~]+):([ -~]*)\n')
+
+def parseMessageAndHeaders(message):
+    """DOCDOC -> message, {header : value}"""
+    headers = {}
+    msg = message
+    while 1:
+        if msg[0] == '\n':
+            return msg[1:], headers
+        m = HEADER_RE.match(msg)
+        if m:
+            k,v = m.groups()
+            if len(v) > 900:
+                LOG.warn("Rejecting overlong exit header %r:%r...",k,v[:30])
+            else:
+                headers[k] = v
+            msg = msg[m.end():]
+        else:
+            LOG.warn("Could not parse headers on message; not using them.")
+            return message, headers
 
 #----------------------------------------------------------------------
 # COMPRESSION FOR PAYLOADS

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.126
retrieving revision 1.127
diff -u -d -r1.126 -r1.127
--- test.py	28 Jun 2003 03:34:00 -0000	1.126
+++ test.py	30 Jun 2003 17:33:33 -0000	1.127
@@ -1331,6 +1331,34 @@
         self.assert_(p.isEncrypted())
         eq(p.getTag(), "9"*20)
 
+    def testHeaders(self):
+        emh = encodeMessageHeaders
+        pmh = parseMessageAndHeaders
+        eq = self.assertEquals
+
+        encoded = emh({"ABC": "x y zzy", "X": "<42>"}) + "Hello whirled"
+        eq(encoded, "ABC:x y zzy\nX:<42>\n\nHello whirled")
+        m,h = pmh(encoded)
+        eq(m, "Hello whirled")
+        eq(h, {"ABC": "x y zzy", "X": "<42>"})
+
+        encoded = emh({}) + "A short message"
+        eq(encoded, "\nA short message")
+        eq(("A short message", {}), pmh(encoded))
+        eq(("\nA short message", {}), pmh("\n"+encoded))
+
+        try:
+            suspendLog()
+            m,h = pmh("A message that doesn't start with a header")
+            eq(m, "A message that doesn't start with a header")
+            eq(h, {})
+            encoded = "X:"+("Y"*1000)+"\nY:X\n\nZ"
+            m,h = pmh(encoded)
+            eq(h, {"Y": "X"})
+            eq(m, "Z")
+        finally:
+            resumeLog()
+
 #----------------------------------------------------------------------
 class HashLogTests(unittest.TestCase):
     def test_hashlog(self):
@@ -2218,7 +2246,7 @@
         bfm = BuildMessage.buildForwardMessage
         # A two-hop/one-hop message.
         p = "Now is the time for all good men to come to the aid"
-        m = bfm(p, SMTP_TYPE, "nobody@invalid",
+        m = bfm("\n"+p, SMTP_TYPE, "nobody@invalid",
                 [self.server1, self.server2], [self.server3])
 
         self.do_test_chain(m,
@@ -2230,7 +2258,8 @@
                            p)
 
         # A one-hop/one-hop message.
-        m = bfm(p, SMTP_TYPE, "nobody@invalid", [self.server1], [self.server3])
+        m = bfm("\n"+p,
+                SMTP_TYPE, "nobody@invalid", [self.server1], [self.server3])
 
         self.do_test_chain(m,
                            [self.sp1,self.sp3],
@@ -2240,7 +2269,8 @@
                            p)
 
         # Try servers with multiple keys
-        m = bfm(p, SMTP_TYPE, "nobody@invalid", [self.server2], [self.server3])
+        m = bfm("\n"+p,
+                SMTP_TYPE, "nobody@invalid", [self.server2], [self.server3])
         self.do_test_chain(m, [self.sp2_3, self.sp2_3], [FWD_TYPE, SMTP_TYPE],
                            [self.server3.getRoutingInfo().pack(),
                             "nobody@invalid"], p)
@@ -2248,7 +2278,7 @@
         # A 3/3 message with a long exit header.
         for i in (100,300):
             longemail = "f"*i+"@invalid"
-            m = bfm(p, SMTP_TYPE, longemail,
+            m = bfm("\n"+p, SMTP_TYPE, longemail,
                     [self.server1, self.server2, self.server1],
                     [self.server3, self.server1, self.server2])
 
@@ -2270,8 +2300,10 @@
         bfm = BuildMessage.buildForwardMessage
         befm = BuildMessage.buildEncryptedForwardMessage
 
+        h = "SUBJECT:cooper\n\n"
         p = "That gum you like, it's coming back in style."
-        m = bfm(p, SMTP_TYPE, "nobody@invalid", [self.server1], [self.server3])
+        m = bfm(h+p, SMTP_TYPE, "nobody@invalid", [self.server1],
+                [self.server3])
 
         pkt = self.do_test_chain(m,
                                  [self.sp1,self.sp3],
@@ -2292,9 +2324,10 @@
         self.assertEquals(p, pkt.getAsciiContents())
         self.assertEquals(base64.encodestring(pkt.getTag()).strip(),
                           pkt.getAsciiTag())
+        self.assertEquals({"SUBJECT":"cooper"}, pkt.getHeaders())
         # with a plaintext, nonascii packet.
         pbin = hexread("0123456789ABCDEFFEDCBA9876543210")
-        m = bfm(pbin, SMTP_TYPE, "nobody@invalid",
+        m = bfm("\n"+pbin, SMTP_TYPE, "nobody@invalid",
                 [self.server1], [self.server3])
         pkt = self.do_test_chain(m,
                                  [self.sp1,self.sp3],
@@ -4523,12 +4556,14 @@
 
 class FakeDeliveryPacket(mixminion.server.PacketHandler.DeliveryPacket):
     """Stub version of DeliveryPacket used for testing modules"""
-    def __init__(self, type, exitType, exitAddress, contents, tag=None):
+    def __init__(self, type, exitType, exitAddress, contents, tag=None,
+                 headers = {}):
         if tag is None:
             tag = "-="*10
         mixminion.server.PacketHandler.DeliveryPacket.__init__(self,
                         exitType, exitAddress, "Z"*16, tag, "Q"*(28*1024))
         self.type = type
+        self.headers = headers
         self.payload = None
         self.contents = contents
 
@@ -4634,16 +4669,17 @@
             mixfn, mixargs = calls[0][1][2], calls[0][1][3:]
             self.assertEquals("ls", mixfn)
             self.assertEquals(mixargs[:-1],
-                              ('-z', '-l', 'nonesuch', '-s', 'foobar',
-                               '-t', 'foo@bar'))
+                              ('-z', '-l', 'nonesuch'))
             # ...and, if the temporary file it used hasn't been removed yet,
             # that it contains the correct data.
             fn = mixargs[-1]
             fn = os.path.join(os.path.split(fn)[0],
                               "rmv_"+os.path.split(fn)[1][4:])
-            if os.path.exists(fn):
-                self.assert_(stringContains(readFile(fn),
-                                            "This is the message"))
+            m = readFile(fn)
+            self.assert_(m.startswith(
+                "To: foo@bar\nFrom: nobody\n"
+                "Subject: foobar\nX-Anonymous: yes\n\n"))
+            self.assert_(stringContains(m, "This is the message"))
 
             ## What about the flush command?
             self.assertEquals("spawnl", calls[-1][0])
@@ -5877,7 +5913,7 @@
     tc = loader.loadTestsFromTestCase
 
     if 0:
-        suite.addTest(tc(ServerKeysTests))
+        suite.addTest(tc(ModuleTests))
         return suite
 
     suite.addTest(tc(MiscTests))