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

[minion-cvs] Flesh out inline documentation



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

Modified Files:
	BuildMessage.py Common.py Config.py Crypto.py Filestore.py 
	Fragments.py Packet.py ServerInfo.py test.py 
Log Message:
Flesh out inline documentation

Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -d -r1.55 -r1.56
--- BuildMessage.py	21 Aug 2003 21:34:02 -0000	1.55
+++ BuildMessage.py	25 Aug 2003 21:05:33 -0000	1.56
@@ -34,7 +34,9 @@
               overhead: number of bytes to omit from each payload,
                         given the type ofthe message encoding.
                         (0 or ENC_FWD_OVERHEAD)
-              uncompressedFragmentPrefix: DOCDOC 
+              uncompressedFragmentPrefix: If we fragment the message,
+                we add this string to the message after compression but
+                before whitening and fragmentation.
               paddingPRNG: generator for padding.
 
        Note: If multiple strings are returned, be sure to shuffle them
@@ -61,14 +63,17 @@
         p.computeHash()
         return [ p.pack() ]
 
+    # Okay, we need fo fragment the message.  First, add the prefix if needed.
     if uncompressedFragmentPrefix:
         payload = uncompressedFragmentPrefix+payload
-
-    # DOCDOC
+    # Now generate a message ID
     messageid = Crypto.getCommonPRNG().getBytes(20)
+    # Figure out how many chunks to divide it into...
     p = mixminion.Fragments.FragmentationParams(len(payload), overhead)
+    # ... fragment the payload into chunks...
     rawFragments = p.getFragments(payload)
     fragments = []
+    # ... and annotate each chunk with appropriate payload header info.
     for i in xrange(len(rawFragments)):
         pyld = FragmentPayload(i, None, messageid, p.length, rawFragments[i])
         pyld.computeHash()

Index: Common.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Common.py,v
retrieving revision 1.105
retrieving revision 1.106
diff -u -d -r1.105 -r1.106
--- Common.py	21 Aug 2003 21:34:02 -0000	1.105
+++ Common.py	25 Aug 2003 21:05:33 -0000	1.106
@@ -486,14 +486,22 @@
 # File helpers
 
 
-#DOCDOC
+# On windows, rename(f1,f2) fails if f2 already exists.  These wrappers
+# handle replacing files.
 if sys.platform == 'win32':
     def replaceFile(f1, f2):
+        """Move the file named 'f1' to a new name 'f2'.  Replace any file
+           already named 'f2'."""
+        # WWWW This isn't atomic.  Later versions of the windows API add
+        # WWWW functions named MoveFileEx and ReplaceFile that may do the
+        # WWWW right thing, but those don't exist in Windows Me/98/95.
         if os.path.exists(f2):
             os.unlink(f2)
         os.rename(f1, f2) 
 else:
     def replaceFile(f1, f2):
+        """Move the file named 'f1' to a new name 'f2'.  Replace any file
+           already named 'f2'."""
         os.rename(f1, f2)
 
 class AtomicFile:

Index: Config.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Config.py,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -d -r1.54 -r1.55
--- Config.py	21 Aug 2003 21:34:02 -0000	1.54
+++ Config.py	25 Aug 2003 21:05:33 -0000	1.55
@@ -552,6 +552,8 @@
     #         we insist on a tight data format?
     #     _restrictKeys is 1/0: do we raise a ConfigError when we see an
     #         unrecognized key, or do we simply generate a warning?
+    #     _restrictSections is 1/0: do we raise a ConfigError when we see an
+    #         unrecognized section, or do we simply generate a warning?
 
     ## Validation rules:
     # A key without a corresponding entry in _syntax gives an error.
@@ -568,7 +570,6 @@
     _syntax = None
     _restrictFormat = 0
     _restrictKeys = 1
-    #DOCDOC
     _restrictSections = 1
 
     def __init__(self, filename=None, string=None, assumeValid=0):
@@ -739,7 +740,8 @@
         return self._sections[sec]
 
     def get(self, sec, val="---"):
-        """DOCDOC"""
+        """Return a section named sec, if any such section exists.  Otherwise
+           return an empty dict, or 'val' if provided."""
         if val == "---": val = {}
         return self._sections.get(sec, val)
 

Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -d -r1.53 -r1.54
--- Crypto.py	21 Aug 2003 21:34:02 -0000	1.53
+++ Crypto.py	25 Aug 2003 21:05:34 -0000	1.54
@@ -611,7 +611,8 @@
            Random portions are generated by choosing 8 random characters
            from the set 'A-Za-z0-9+-'.
 
-           DOCDOC conflictPrefix
+           If 'conflictPrefix' is set, do not return any file named
+           prefix+H if a file named conflictPrefix+H already exists.
            """
         flags = os.O_WRONLY|os.O_CREAT|os.O_EXCL
         mode = "w"
@@ -783,9 +784,8 @@
         return b
 
 if hasattr(_ml, "win32_get_random_bytes"):
-    print "WAHOO!"
     class _WinTrueRNG(RNG):
-        """DOCDOC"""
+        """A random number generator using the windows crypto API."""
         def __init__(self):
             RNG.__init__(self, 1024)
             self.getBytes(1)
@@ -793,15 +793,15 @@
             return _ml.win32_get_random_bytes(n)
 
 class _OpensslRNG(RNG):
-    """DOCDOC"""
+    """Random number generator that falls back to openssl's implementation."""
     def __init__(self):
-        """DOCDOC"""
         RNG.__init__(self, 1024)
     def _prng(self,n):
         return _ml.openssl_rand(n)
 
 class _XorRNG(RNG):
-    """DOCDOC"""
+    """A random number generator that takes random data from two sources
+       and XORs the two streams together."""
     def __init__(self, base1, base2):
         RNG.__init__(self, 1024)
         self.base1 = base1

Index: Filestore.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Filestore.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- Filestore.py	21 Aug 2003 21:34:02 -0000	1.7
+++ Filestore.py	25 Aug 2003 21:05:34 -0000	1.8
@@ -157,7 +157,8 @@
         return hs
 
     def messageExists(self, handle):
-        """DOCDOC"""
+        """Return true iff this filestore contains a message with the handle
+           'handle'."""
         return os.path.exists(os.path.join(self.dir, "msg_"+handle))
 
     def removeMessage(self, handle):

Index: Fragments.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Fragments.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- Fragments.py	21 Aug 2003 21:34:02 -0000	1.5
+++ Fragments.py	25 Aug 2003 21:05:34 -0000	1.6
@@ -401,7 +401,7 @@
     # inserted -- the midnight (GMT) of the day on the first packet
     #     associated with this message was inserted.
     # nym -- the name of the identity receiving this message.  Used to
-    #     prevent linkage attacks. XXXX005 specify this need!
+    #     prevent linkage attacks.
     #
     # params -- an instance of FragmentationParams for this message.
     # chunks -- a map from chunk number to tuples of (handle within the pool,
@@ -412,7 +412,8 @@
     #     are ready for reconstruction, but haven't been reconstructed
     #     yet.
     def __init__(self, messageid, length, overhead, inserted, nym):
-        """Create a new MessageState."""
+        """Create a new MessageState. 
+        """
         self.messageid = messageid
         self.overhead = overhead
         self.inserted = inserted

Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.56
retrieving revision 1.57
diff -u -d -r1.56 -r1.57
--- Packet.py	21 Aug 2003 21:34:02 -0000	1.56
+++ Packet.py	25 Aug 2003 21:05:34 -0000	1.57
@@ -86,7 +86,7 @@
 FRAGMENT_TYPE  = 0x0103  # Find the actual deliver info in the message payload 
 MAX_EXIT_TYPE  = 0xFFFF
 
-#DOCDOC
+# Set of exit types that don't get tag fields.
 _TYPES_WITHOUT_TAGS = { FRAGMENT_TYPE : 1 }
 
 class ParseError(MixError):
@@ -312,7 +312,8 @@
         self.data = data
 
     def computeHash(self):
-        """DOCDOC"""
+        """Update the hash field of this payload to correspond to the hash
+           of the data."""
         self.hash = sha1(self.data)
 
     def isSingleton(self):
@@ -359,7 +360,8 @@
         self.data = data
 
     def computeHash(self):
-        """DOCDOC"""
+        """Update the hash field of this payload to correspond to the hash
+           of the data."""
         self.hash = "X"*DIGEST_LEN
         p = self.pack()
         self.hash = sha1(p[23:])
@@ -386,7 +388,12 @@
         return PAYLOAD_LEN - FRAGMENT_PAYLOAD_OVERHEAD - len(self.data)
 
 #----------------------------------------------------------------------
-#DOCDOC
+# Encoding of messages fragmented for reassembly by the exit server.
+#
+# Such messages are encoded by adding routing-type, routing-len, and
+# routing-info fields at the start of the payload before fragmentation,
+# so that the server doesn't recover the delivery address before it's time
+# to deliver the message.
 
 SSF_UNPACK_PATTERN = "!HH"
 SSF_PREFIX_LEN = 4
@@ -403,7 +410,6 @@
     return ServerSideFragmentedMessage(rt, ri, comp)
 
 class ServerSideFragmentedMessage:
-    """DOCDOC"""
     def __init__(self, routingtype, routinginfo, compressedContents):
         self.routingtype = routingtype
         self.routinginfo = routinginfo
@@ -694,9 +700,8 @@
        over a text-based medium."""
     def __init__(self, contents, messageType, tag=None):
         """Create a new TextEncodedMessage given a set of contents, a
-           messageType ('TXT', 'ENC', 'LONG', or 'BIN'), and optionally
+           messageType ('TXT', 'ENC', 'LONG', 'FRAG' or 'BIN'), and optionally
            a tag.
-           DOCDOC FRAG
            """
         assert messageType in ('TXT', 'ENC', 'LONG', 'BIN', 'FRAG')
         assert tag is None or (messageType == 'ENC' and len(tag) == 20)
@@ -716,7 +721,7 @@
         """Return true iff this is an overcompressed plaintext packet."""
         return self.messageType == 'LONG'
     def isFragment(self):
-        """DOCDOC"""
+        """Return true iff this is a fragment packet."""
         return self.messageType == 'FRAG'
     def getContents(self):
         """Return the (unencoded) contents of this packet."""
@@ -744,12 +749,14 @@
 #----------------------------------------------------------------------
 # Header encoding
 
-#DOCDOC
+# Longest allowed length of a single header.
 MAX_HEADER_LEN = 900
 
 def encodeMailHeaders(subject=None, fromAddr=None, inReplyTo=None,
                       references=None):
-    """DOCDOC.  Raise MixError on failure."""
+    """Given (optionally) any of the headers permissible for email
+       messages, return a string to be prepended to a message before
+       encoding.  Raise MixError on failure."""
     headers = {}
     if subject:
         headers['SUBJECT'] = subject
@@ -766,9 +773,10 @@
     return encodeMessageHeaders(headers)
 
 def encodeMessageHeaders(headers):
-    """DOCDOC dict, max size
-
-       Requires that headers are in acceptable format.
+    """Given a dictionary of (header,header-value) entries, encode the
+       entries and return a string to be prepended to a message before
+       encoding.  Requires that headers are in acceptable format.
+       Raises MixError on failure.
     """
     items = []
     hitems = headers.items()
@@ -786,7 +794,9 @@
 HEADER_RE = re.compile(r'^([!-9;-~]+):([ -~]*)\n')
 
 def parseMessageAndHeaders(message):
-    """DOCDOC -> message, {header : value}"""
+    """Given a message with encoded headers, return a 2-tuple containing
+       the message, and a dictionary mapping header names to header values.
+       Skips improperly formatted headers."""
     headers = {}
     msg = message
     while 1:

Index: ServerInfo.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerInfo.py,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -d -r1.53 -r1.54
--- ServerInfo.py	21 Aug 2003 21:34:02 -0000	1.53
+++ ServerInfo.py	25 Aug 2003 21:05:34 -0000	1.54
@@ -105,6 +105,14 @@
                      "Configuration": ("ALLOW", None, None),
                      },
         }
+    expected_versions = {
+         "Server" : ( "Descriptor-Version", "0.2"),
+         "Incoming/MMTP" : ("Version", "0.1"),
+         "Outgoing/MMTP" : ("Version", "0.1"),
+         "Delivery/Fragmented" : ("Version", "0.1"),
+         "Delivery/MBOX" : ("Version", "0.1"),
+         "Delivery/SMTP" : ("Version", "0.1"),
+         }
 
     def __init__(self, fname=None, string=None, assumeValid=0,
                  validatedDigests=None):
@@ -129,10 +137,25 @@
                     if k == 'Descriptor-Version' and v.strip() != '0.2':
                         raise ConfigError("Unrecognized descriptor version: %s"
                                           % v.strip())
-            # FFFF005 Remove sections with unrecognized versions.
-
-        return contents
+        
+        
+        # Remove any sections with unrecognized versions.
+        revisedContents = []
+        for name, ents in contents:
+            v = self.expected_versions.get(name)
+            if not v: 
+                revisedContents.append((name, ents))
+                continue
+            versionkey, versionval = v
+            for k,v,_ in ents:
+                if k == versionkey and v.strip() != versionval:
+                    LOG.warn("Skipping %s section with unrecognized version %s"
+                             , name, v.strip())
+                    break
+            else:
+                revisedContents.append((name, ents))
 
+        return revisedContents
 
     def validate(self, lines, contents):
         ####

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.147
retrieving revision 1.148
diff -u -d -r1.147 -r1.148
--- test.py	21 Aug 2003 21:34:03 -0000	1.147
+++ test.py	25 Aug 2003 21:05:34 -0000	1.148
@@ -117,7 +117,7 @@
         return abs(f1-f2) < .00001
 
 def fileURL(fname):
-    """DOCDOC"""
+    """Return a file url suitable for a file named 'fname'"""
     fname = os.path.abspath(fname)
     return "file:%s"%fname
 
@@ -165,29 +165,38 @@
 # Add some helpful functions to unittest.TestCase
 
 class TestCase(unittest.TestCase):
-    """DOCDOC"""
+    """Extended version of unittest.TestCase with a few extra
+       'assertFoo' functions."""
     def __init__(self, *args, **kwargs):
         unittest.TestCase.__init__(self, *args, **kwargs)
     def assertFloatEq(self, f1, f2):
+        """Fail unless f1 and f2 are very close to one another."""
         if not floatEq(f1, f2):
             self.fail("%s != %s" % (f1, f2))
     def assertLongStringEq(self, s1, s2):
+        """Fail unless the string s1 equals the string s2.  If they aren't 
+           equal, give a failure message that highlights the first point at
+           which they differ."""
         if s1 != s2:
             d = findFirstDiff(s1, s2)
             self.fail("Strings unequal.  First difference at %s: %r vs %r"
                       % (d, s1[d:+10], s2[d:d+10]))
     def assertUnorderedEq(self, l1, l2):
+        """Fail unless the lists l1 and l2 have the same elements.  The
+           order of elements in the two lists need not be the same."""
         l1 = list(l1)[:]
         l2 = list(l2)[:]
         l1.sort()
         l2.sort()
         self.assertEquals(l1, l2)
     def assertStartsWith(self, s1, s2):
+        """Fail unless string s1 begins with string s2."""
         if not s1.startswith(s2):
             if len(s1) > min(40, len(s2)+5):
                 s1 = s1[:len(s2)+5]+"..."
             self.fail("%r does not start with %r"%(s1,s2))
     def assertEndsWith(self, s1, s2):
+        """Fail unless string s1 ends with string s2."""
         if not s1.endswith(s2):
             if len(s1) > min(40, len(s2)+5):
                 s1 = "..."+s1[-(len(s2)+5):]
@@ -6446,28 +6455,24 @@
         self.assertEquals([], pool.listReadyMessages())
         pool.unchunkMessages()
 
-        #DOCDOC comment this rats' nets
-
         ####
         # Reconstruct: simple case.
         ####
-        pool.addFragment(pkts1[2])
-        self.assertEquals(1, pool.store.count())
-        self.assertEquals([], pool.listReadyMessages())
-        pool.addFragment(pkts1[0])
-        self.assertEquals(2, pool.store.count())
-        pool.addFragment(pkts1[1]) # should be 'unnneedeed'
-        self.assertEquals(2, pool.store.count())
-        self.assertEquals([], pool.listReadyMessages())
-        pool.unchunkMessages()
-        self.assertEquals([pkts1[0].msgID], pool.listReadyMessages())
-        mid = pool.listReadyMessages()[0]
-        #print len(M1), len(pool.getReadyMessage(mid))
-        self.assertLongStringEq(M1, uncompressData(pool.getReadyMessage(mid)))
+        pool.addFragment(pkts1[2]) # Add a single packet.
+        self.assertEquals(1, pool.store.count()) # Is it there?
+        self.assertEquals([], pool.listReadyMessages()) # Message isn't ready.
+        pool.addFragment(pkts1[0]) # Add another packet.
+        self.assertEquals(2, pool.store.count()) # Two packets waiting.
+        pool.addFragment(pkts1[1]) # duplicate; should be 'unnneeded'
+        self.assertEquals(2, pool.store.count()) # Still two waiting.
+        self.assertEquals([], pool.listReadyMessages()) # Still not ready.
+        pool.unchunkMessages() # Now try to unchunk the message.
+        self.assertEquals([pkts1[0].msgID], pool.listReadyMessages()) # Ready!
+        mid = pool.listReadyMessages()[0] # Get the message; is it right?
         self.assertLongStringEq(M1, uncompressData(pool.getReadyMessage(mid)))
-        pool.markMessageCompleted(mid)
-        self.assertEquals([], pool.listReadyMessages())
-        pool.addFragment(pkts1[1])
+        pool.markMessageCompleted(mid) # Mark it as done.
+        self.assertEquals([], pool.listReadyMessages()) # Not there any more.
+        pool.addFragment(pkts1[1]) # Adding more fragments to it doesn't work.
         self.assertEquals([], pool.store.getAllMessages())
 
         ####