[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/server
In directory moria.mit.edu:/tmp/cvs-serv30719/lib/mixminion/server

Modified Files:
	Modules.py PacketHandler.py ServerMain.py 
Log Message:
First cut at implementing headers (server-side)

Index: Modules.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/Modules.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -d -r1.45 -r1.46
--- Modules.py	25 Jun 2003 17:03:15 -0000	1.45
+++ Modules.py	30 Jun 2003 17:33:33 -0000	1.46
@@ -765,12 +765,40 @@
         return "SMTP"
     def getExitTypes(self):
         return [ mixminion.Packet.SMTP_TYPE ]
+    def _formatSMTPMessage(self, address, packet):
+        """DOCDOC"""
+        #DOCDOC implied fields
+
+        headers = packet.getHeaders()
+        subject = headers.get("SUBJECT", self.subject)
+        fromAddr = headers.get("FROM")
+        if fromAddr:
+            fromAddr = '"%s %s" <%s>' % (self.fromTag, fromAddr,
+                                         self.returnAddress)
+        else:
+            fromAddr = self.returnAddress
+
+        morelines = []
+        if headers.has_key("IN-REPLY-TO"):
+            morelines.append("In-Reply-To: %s\n" % headers['IN-REPLY-TO'])
+        if headers.has_key("REFERENCES"):
+            morelines.append("References: %s\n" % headers['REFERENCES'])
+
+        # Decode and escape the message, and get ready to send it.
+        msg = _escapeMessageForEmail(packet)
+        msg = "To: %s\nFrom: %s\nSubject: %s\n%s%s\n\n%s"%(
+            address, fromAddr, subject, "".join(morelines), self.header, msg)
+
+        return msg
 
 class DirectSMTPModule(SMTPModule):
     """Module that delivers SMTP messages via a local MTA."""
     ## Fields
     # server -- Name of the MTA server.
-    # header -- The string, minus "To:"-line, that gets prepended to all
+    # subject: The default subject line we use for outgoing messages
+    # fromPattern: A printf format string with a field for user-supplied
+    #    from addresses.
+    # header -- A string, minus "To:"-line, that gets prepended to all
     #    outgoing messages.
     # returnAddress -- The address to use in the "From:" line.
     # blacklist -- An EmailAddressSet of addresses to which we refuse
@@ -792,6 +820,7 @@
                    'SMTPServer' : ('ALLOW', None, 'localhost'),
                    'Message' : ('ALLOW', None, ""),
                    'ReturnAddress': ('ALLOW', None, None), #Required on e
+                   'FromTag' : ('ALLOW', None, "[Anon]"),
                    'SubjectLine' : ('ALLOW', None,
                                     'Type III Anonymous Message'),
 
@@ -827,11 +856,13 @@
         else:
             self.blacklist = None
         message = "\n".join(textwrap.wrap(sec.get('Message',""))).strip()
-        subject = sec['SubjectLine']
+        self.subject = sec['SubjectLine']
         self.returnAddress = sec['ReturnAddress']
-
-        self.header = "From: %s\nSubject: %s\nX-Anonymous: yes\n\n%s" %(
-            self.returnAddress, subject, message)
+        self.fromTag = sec.get('FromTag', "[Anon]")
+        if message:
+            self.header = "X-Anonymous: yes\n\n%s" %(message)
+        else:
+            self.header = "X-Anonymous: yes"
 
         manager.enableModule(self)
 
@@ -851,10 +882,7 @@
             LOG.warn("Dropping message to blacklisted address %r", address)
             return DELIVER_FAIL_NORETRY
 
-        # Decode and escape the message, and get ready to send it.
-        msg = _escapeMessageForEmail(packet)
-        msg = "To: %s\n%s\n\n%s"%(address, self.header, msg)
-
+        msg = self._formatSMTPMessage(address, packet)
         # Send the message.
         return sendSMTPMessage(self.server, [address], self.returnAddress, msg)
 
@@ -868,7 +896,9 @@
     ## Fields:
     # server: The path (usually a single server) to use for outgoing messages.
     #    Multiple servers should be separated by commas.
-    # subject: The subject line we use for outgoing messages
+    # subject: The default subject line we use for outgoing messages
+    # fromPattern: A printf format string with a field for user-supplied
+    #    from addresses.
     # command: The Mixmaster binary.
     # options: Options to pass to the Mixmaster binary when queueing messages
     # tmpQueue: An auxiliary Queue used to hold files so we can pass them to
@@ -887,6 +917,7 @@
                              "7 hours for 6 days"),
                    'MixCommand' : ('REQUIRE', _parseCommand, None),
                    'Server' : ('REQUIRE', None, None),
+                   'FromTag' : ('ALLOW', None, "[Anon]"),
                    'SubjectLine' : ('ALLOW', None,
                                     'Type III Anonymous Message'),
                    }
@@ -908,9 +939,11 @@
         self.server = sec['Server']
         self.subject = sec['SubjectLine']
         self.retrySchedule = sec['Retry']
+        self.fromTag = sec.get('FromTag', "[Anon]")
         self.command = cmd[0]
-        self.options = tuple(cmd[1]) + ("-l", self.server,
-                                        "-s", self.subject)
+        self.options = tuple(cmd[1]) + ("-l", self.server)
+        self.returnAddress = "nobody"
+        self.header = "X-Anonymous: yes"
         manager.enableModule(self)
 
     def getName(self):
@@ -934,12 +967,11 @@
                      packet.getAddress())
             return DELIVER_FAIL_NORETRY
 
-        msg = _escapeMessageForEmail(packet)
+        msg = self._formatSMTPMessage(info.email, packet)
         handle = self.tmpQueue.queueMessage(msg)
 
         cmd = self.command
-        opts = self.options + ("-t", info.email,
-                               self.tmpQueue.getMessagePath(handle))
+        opts = self.options + (self.tmpQueue.getMessagePath(handle),)
         code = os.spawnl(os.P_WAIT, cmd, cmd, *opts)
         LOG.debug("Queued Mixmaster message: exit code %s", code)
         self.tmpQueue.removeMessage(handle)
@@ -962,6 +994,20 @@
 
 #----------------------------------------------------------------------
 
+MAIL_HEADERS = ["SUBJECT", "FROM", "IN-REPLY-TO", "REFERENCES"]
+def checkMailHeaders(headers):
+    """DOCDOC"""
+    for k in headers.keys():
+        if k not in MAIL_HEADERS:
+            #XXXX this should raise parse error instead.
+            LOG.warn("Skipping unrecognized mail header %s"%k)
+        
+    fromAddr = headers['FROM']
+    if re.search(r'[\[\]:"]', fromAddr):
+        raise ParseError("Invalid FROM address: %r", fromAddr)
+
+#----------------------------------------------------------------------
+
 def sendSMTPMessage(server, toList, fromAddr, message):
     """Send a single SMTP message.  The message will be delivered to
        toList, and seem to originate from fromAddr.  We use 'server' as an
@@ -991,6 +1037,7 @@
     """Helper function: Given a message and tag, escape the message if
        it is not plaintext ascii, and wrap it in some standard
        boilerplate.  Add a disclaimer if the message is not ascii.
+       Extracts headers if possible.  Returns a 2-tuple of message/headers.
 
           packet -- an instance of DeliveryPacket
 

Index: PacketHandler.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/PacketHandler.py,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- PacketHandler.py	6 Jun 2003 06:04:58 -0000	1.17
+++ PacketHandler.py	30 Jun 2003 17:33:33 -0000	1.18
@@ -4,10 +4,11 @@
 """mixminion.PacketHandler: Code to process mixminion packets on a server"""
 
 import binascii
+import re
 import threading
 import types
 
-from mixminion.Common import encodeBase64, formatBase64
+from mixminion.Common import encodeBase64, formatBase64, LOG
 import mixminion.Crypto as Crypto
 import mixminion.Packet as Packet
 import mixminion.Common as Common
@@ -276,6 +277,7 @@
         self.payload = payload
         self.contents = None
         self.type = None
+        self.headers = None#DOCDOC
 
     def isDelivery(self):
         """Return true iff this packet is a delivery (non-relay) packet."""
@@ -333,17 +335,20 @@
                 # encrypted message
                 self.type = 'enc'
                 self.contents = message
+                self.headers = {}
             else:
                 # forward message
                 self.type = 'plain'
-                # self.contents is right
+                self.contents, self.headers = \
+                               Packet.parseMessageAndHeaders(self.contents)
         except Packet.CompressedDataTooLong, _:
-            self.contents = (mixminion.Packet.parsePayload(message)
-                                             .getContents())
+            self.contents = Packet.parsePayload(message).getContents()
             self.type = 'long'
+            self.headers = {}
         except MixError:
             self.contents = message
             self.type = 'err'
+            self.headers = {}
 
         self.payload = None
 
@@ -358,6 +363,13 @@
         else:
             return encodeBase64(self.contents)
 
+    def getHeaders(self):
+        """DOCDOC"""
+        if self.type is None:
+            self.decode()
+        assert self.headers is not None
+        return self.headers
+
     def getAsciiTag(self):
         """Return a base64-representation of this message's decoding handle."""
         return formatBase64(self.tag)
@@ -378,3 +390,4 @@
             tp = 'BIN'
 
         return Packet.TextEncodedMessage(self.contents, tp, tag)
+

Index: ServerMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerMain.py,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -d -r1.82 -r1.83
--- ServerMain.py	26 Jun 2003 17:52:09 -0000	1.82
+++ ServerMain.py	30 Jun 2003 17:33:33 -0000	1.83
@@ -643,7 +643,12 @@
                 raise UIError("The server homedir contains keys for an "
                               "unrecognized version of the server.")
             else:
-                raise
+                raise UIError("""\
+For some reason, your generated server descriptors cannot be parsed.  You
+may want to delete all your keysets with server-DELKEYS and have the server
+generate new ones.  [Messages sent to the old keys will be lost].\n
+The original error message was '%s'."""%e)
+            
         self.keyring.createKeysAsNeeded()
         self.keyring.checkDescriptorConsistency()