[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Do not mix tabs and spaces in our source files; use spa...



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

Modified Files:
	BuildMessage.py ClientMain.py Common.py Crypto.py 
	MMTPClient.py Main.py Packet.py ServerInfo.py benchmark.py 
	test.py testSupport.py 
Log Message:
Do not mix tabs and spaces in our source files; use spaces only.

Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- BuildMessage.py	12 Dec 2002 19:56:46 -0000	1.22
+++ BuildMessage.py	16 Dec 2002 02:40:11 -0000	1.23
@@ -16,7 +16,7 @@
            'buildReplyBlock', 'decodePayload' ]
 
 def buildForwardMessage(payload, exitType, exitInfo, path1, path2,
-			paddingPRNG=None):
+                        paddingPRNG=None):
     """Construct a forward message.
             payload: The payload to deliver.  Must compress to under 28K-22b.
                   If it does not, MixError is raised.
@@ -24,19 +24,19 @@
             exitInfo: The routing info for the final node, not including tag.
             path1: Sequence of ServerInfo objects for the first leg of the path
             path2: Sequence of ServerInfo objects for the 2nd leg of the path
-	    paddingPRNG: random number generator used to generate padding.
-	          If None, a new PRNG is initialized.
+            paddingPRNG: random number generator used to generate padding.
+                  If None, a new PRNG is initialized.
 
         Neither path1 nor path2 may be empty.
     """
     if paddingPRNG is None: 
-	paddingPRNG = Crypto.getCommonPRNG()
+        paddingPRNG = Crypto.getCommonPRNG()
     assert path1 and path2
 
     LOG.debug("Encoding forward message for %s-byte payload",len(payload))
     LOG.debug("  Using path %s/%s",
-		   [s.getNickname() for s in path1],
-		   [s.getNickname() for s in path2])
+                   [s.getNickname() for s in path1],
+                   [s.getNickname() for s in path2])
     LOG.debug("  Delivering to %04x:%r", exitType, exitInfo)
 
     # Compress, pad, and checksum the payload.
@@ -48,7 +48,7 @@
     return _buildMessage(payload, exitType, exitInfo, path1, path2,paddingPRNG)
 
 def buildEncryptedForwardMessage(payload, exitType, exitInfo, path1, path2,
-				 key, paddingPRNG=None, secretRNG=None):
+                                 key, paddingPRNG=None, secretRNG=None):
     """Construct a forward message encrypted with the public key of a
        given user.
             payload: The payload to deliver.  Must compress to under 28K-60b.
@@ -57,19 +57,19 @@
             exitInfo: The routing info for the final node, not including tag.
             path1: Sequence of ServerInfo objects for the first leg of the path
             path2: Sequence of ServerInfo objects for the 2nd leg of the path
-	    key: Public key of this message's recipient.
-	    paddingPRNG: random number generator used to generate padding.
-	          If None, a new PRNG is initialized.
+            key: Public key of this message's recipient.
+            paddingPRNG: random number generator used to generate padding.
+                  If None, a new PRNG is initialized.
     """
     if paddingPRNG is None: 
-	paddingPRNG = Crypto.getCommonPRNG()
+        paddingPRNG = Crypto.getCommonPRNG()
     if secretRNG is None: secretRNG = paddingPRNG
 
     LOG.debug("Encoding encrypted forward message for %s-byte payload",
-		   len(payload))
+                   len(payload))
     LOG.debug("  Using path %s/%s",
-		   [s.getNickname() for s in path1],
-		   [s.getNickname() for s in path2])
+                   [s.getNickname() for s in path1],
+                   [s.getNickname() for s in path2])
     LOG.debug("  Delivering to %04x:%r", exitType, exitInfo)
 
     # Compress, pad, and checksum the payload.
@@ -91,9 +91,9 @@
     # we keep trying to encrypt until the MSBit of our encrypted value is
     # zero.
     while 1:
-	encrypted = Crypto.pk_encrypt(rsaPart, key)
-	if not (ord(encrypted[0]) & 0x80):
-	    break
+        encrypted = Crypto.pk_encrypt(rsaPart, key)
+        if not (ord(encrypted[0]) & 0x80):
+            break
     # Lioness encryption.
     k= Crypto.Keyset(sessionKey).getLionessKeys(Crypto.END_TO_END_ENCRYPT_MODE)
     lionessPart = Crypto.lioness_encrypt(lionessPart, k)
@@ -114,10 +114,10 @@
        ServerInfo for the nodes on the first leg of the path.
     """
     if paddingPRNG is None:
-	paddingPRNG = Crypto.getCommonPRNG()
+        paddingPRNG = Crypto.getCommonPRNG()
 
     LOG.debug("Encoding reply message for %s-byte payload",
-		   len(payload))
+                   len(payload))
     LOG.debug("  Using path %s/??",[s.getNickname() for s in path1])
 
     # Compress, pad, and checksum the payload.
@@ -127,14 +127,14 @@
     #  crossover note.  (We use 'decrypt' so that the message recipient can
     #  simply use 'encrypt' to reverse _all_ the steps of the reply path.)
     k = Crypto.Keyset(replyBlock.encryptionKey).getLionessKeys(
-	                 Crypto.PAYLOAD_ENCRYPT_MODE)
+                         Crypto.PAYLOAD_ENCRYPT_MODE)
     payload = Crypto.lioness_decrypt(payload, k)
 
     return _buildMessage(payload, None, None,
                          path1=path1, path2=replyBlock)
 
 def _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime=0,
-			 secretPRNG=None, tag=None):
+                         secretPRNG=None, tag=None):
     """Helper function: makes a reply block, given a tag and a PRNG to
        generate secrets. Returns a 3-tuple containing (1) a
        newly-constructed reply block, (2) a list of secrets used to
@@ -155,7 +155,7 @@
         secretPRNG = Crypto.getCommonPRNG()
 
     LOG.debug("Building reply block for path %s",
-		   [s.getNickname() for s in path])
+                   [s.getNickname() for s in path])
     LOG.debug("  Delivering to %04x:%r", exitType, exitInfo)
 
     # The message is encrypted first by the end-to-end key, then by
@@ -168,7 +168,7 @@
 
     # (This will go away when we deprecate 'stateful' reply blocks
     if tag is None:
-	tag = _getRandomTag(secretPRNG)
+        tag = _getRandomTag(secretPRNG)
 
     header = _buildHeader(path, headerSecrets, exitType, tag+exitInfo,
                           paddingPRNG=Crypto.getCommonPRNG())
@@ -179,7 +179,7 @@
 
 # Maybe we shouldn't even allow this to be called with userKey==None.
 def buildReplyBlock(path, exitType, exitInfo, userKey,
-		    expiryTime=0, secretRNG=None):
+                    expiryTime=0, secretRNG=None):
     """Construct a 'state-carrying' reply block that does not require the
        reply-message recipient to remember a list of secrets.
        Instead, all secrets are generated from an AES counter-mode
@@ -188,7 +188,7 @@
        info).
 
                path: a list of ServerInfo objects
-	       exitType,exitInfo: The address to deliver the final message.
+               exitType,exitInfo: The address to deliver the final message.
                userKey: a string used to encrypt the seed.
 
        NOTE: We used to allow another kind of 'non-state-carrying' reply
@@ -196,7 +196,7 @@
        determine
        """
     if secretRNG is None: 
-	secretRNG = Crypto.getCommonPRNG()
+        secretRNG = Crypto.getCommonPRNG()
 
     # We need to pick the seed to generate our keys.  To make the decoding
     # step a little faster, we find a seed such that H(seed|userKey|"Validate")
@@ -209,26 +209,26 @@
     # XXXX anybody who sees multiple tags.  We need to make sure that userKey
     # XXXX is stored on disk, and isn't a password.  This needs more thought.
     while 1:
-	seed = _getRandomTag(secretRNG)
-	if Crypto.sha1(seed+userKey+"Validate")[-1] == '\x00':
-	    break
+        seed = _getRandomTag(secretRNG)
+        if Crypto.sha1(seed+userKey+"Validate")[-1] == '\x00':
+            break
 
     prng = Crypto.AESCounterPRNG(Crypto.sha1(seed+userKey+"Generate")[:16])
 
     return _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime, prng,
-				seed)[0]
+                                seed)[0]
 
 #----------------------------------------------------------------------
 # MESSAGE DECODING
 
 def decodePayload(payload, tag, key=None,
-		  #storedKeys=None, # 'Stateful' reply blocks are disabled.
-		  userKey=None):
+                  #storedKeys=None, # 'Stateful' reply blocks are disabled.
+                  userKey=None):
     """Given a 28K payload and a 20-byte decoding tag, attempt to decode and
        decompress the original message.
 
            key: an RSA key to decode encrypted forward messages, or None
-	   userKey: our encryption key for reply blocks, or None.
+           userKey: our encryption key for reply blocks, or None.
 
        If we can successfully decrypt the payload, we return it.  If we
        might be able to decrypt the payload given more/different keys,
@@ -238,12 +238,12 @@
     # FFFF Allow callbacks?
 
     if len(payload) != PAYLOAD_LEN or len(tag) != TAG_LEN:
-	raise MixError("Wrong payload or tag length")
+        raise MixError("Wrong payload or tag length")
 
     # If the payload already contains a valid checksum, it's a forward
     # message.
     if _checkPayload(payload):
-	return _decodeForwardPayload(payload)
+        return _decodeForwardPayload(payload)
 
     # ('Stateful' reply blocks are disabled.)
 
@@ -251,28 +251,28 @@
 ##    # using those keys.
 
 ##     if storedKeys is not None:
-## 	secrets = storedKeys.get(tag)
-##  	if secrets is not None:
-##  	    del storedKeys[tag]
-##  	    return _decodeReplyPayload(payload, secrets)
+##      secrets = storedKeys.get(tag)
+##      if secrets is not None:
+##          del storedKeys[tag]
+##          return _decodeReplyPayload(payload, secrets)
 
     # If H(tag|userKey|"Validate") ends with 0, then the message _might_
     # be a reply message using H(tag|userKey|"Generate") as the seed for
     # its master secrets.  (There's a 1-in-256 chance that it isn't.)
     if userKey is not None:
-	if Crypto.sha1(tag+userKey+"Validate")[-1] == '\x00':
-	    try:
-		return _decodeStatelessReplyPayload(payload, tag, userKey)
-	    except MixError:
-		pass
+        if Crypto.sha1(tag+userKey+"Validate")[-1] == '\x00':
+            try:
+                return _decodeStatelessReplyPayload(payload, tag, userKey)
+            except MixError:
+                pass
 
     # If we have an RSA key, and none of the above steps get us a good
     # payload, then we may as well try to decrypt the start of tag+key with
     # our RSA key.
     if key is not None:
-	p = _decodeEncryptedForwardPayload(payload, tag, key)
-	if p is not None:
-	    return p
+        p = _decodeEncryptedForwardPayload(payload, tag, key)
+        if p is not None:
+            return p
 
     return None
 
@@ -285,8 +285,8 @@
     """Helper function: decode an encrypted forward payload.  Return values
        are the same as decodePayload.
              payload: the payload to decode
-	     tag: the decoding tag
-	     key: the RSA key of the payload's recipient."""
+             tag: the decoding tag
+             key: the RSA key of the payload's recipient."""
     assert len(tag) == TAG_LEN
     assert len(payload) == PAYLOAD_LEN
 
@@ -295,13 +295,13 @@
     # first N.  Try decrypting...
     msg = tag+payload
     try:
-	rsaPart = Crypto.pk_decrypt(msg[:key.get_modulus_bytes()], key)
+        rsaPart = Crypto.pk_decrypt(msg[:key.get_modulus_bytes()], key)
     except Crypto.CryptoError:
-	return None
+        return None
     rest = msg[key.get_modulus_bytes():]
 
     k = Crypto.Keyset(rsaPart[:SECRET_LEN]).getLionessKeys(
-	Crypto.END_TO_END_ENCRYPT_MODE)
+        Crypto.END_TO_END_ENCRYPT_MODE)
     rest = rsaPart[SECRET_LEN:] + Crypto.lioness_decrypt(rest, k)
 
     # ... and then, check the checksum and continue.
@@ -316,10 +316,10 @@
     # Reverse the 'decrypt' operations of the reply mixes, and the initial
     # 'decrypt' of the originating user...
     for sec in secrets:
-	k = Crypto.Keyset(sec).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE)
-	payload = Crypto.lioness_encrypt(payload, k)
-	if check and _checkPayload(payload):
-	    break
+        k = Crypto.Keyset(sec).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE)
+        payload = Crypto.lioness_encrypt(payload, k)
+        if check and _checkPayload(payload):
+            break
 
     # ... and then, check the checksum and continue.
     return _decodePayloadImpl(payload)
@@ -368,16 +368,16 @@
         reply = path2
         path2 = None
     else:
-	if len(exitInfo) < TAG_LEN:
-	    raise MixError("Implausibly short exit info: %r"%exitInfo)
-	if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE:
-	    raise MixError("Invalid exit type: %4x"%exitType)
+        if len(exitInfo) < TAG_LEN:
+            raise MixError("Implausibly short exit info: %r"%exitInfo)
+        if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE:
+            raise MixError("Invalid exit type: %4x"%exitType)
 
     ### SETUP CODE: let's handle all the variant cases.
 
     # Set up the random number generators.
     if paddingPRNG is None:
-	paddingPRNG = Crypto.getCommonPRNG()
+        paddingPRNG = Crypto.getCommonPRNG()
     if paranoia:
         nHops = len(path1)
         if path2: nHops += len(path2)
@@ -542,8 +542,8 @@
 def _encodePayload(payload, overhead, paddingPRNG):
     """Helper: compress a payload, pad it, and add extra fields (size and hash)
               payload: the initial payload
-	      overhead: number of bytes to omit from result
-	                (0 or ENC_FWD_OVERHEAD)
+              overhead: number of bytes to omit from result
+                        (0 or ENC_FWD_OVERHEAD)
               paddingPRNG: generator for padding.
 
        BUG: This should eventually support K-of-N.
@@ -557,7 +557,7 @@
 
     # If the compressed payload doesn't fit in 28K, then we need to bail out.
     if paddingLen < 0:
-	raise MixError("Payload too long for singleton message")
+        raise MixError("Payload too long for singleton message")
 
     # Otherwise, we pad the payload, and construct a new SingletonPayload,
     # including this payload's size and checksum.
@@ -574,13 +574,13 @@
        not encryption."""
     # Is the hash ok?
     if not _checkPayload(payload):
-	raise MixError("Hash doesn't match")
+        raise MixError("Hash doesn't match")
 
     # Parse the payload into its size, checksum, and body.
     payload = parsePayload(payload)
 
     if not payload.isSingleton():
-	raise MixError("Message fragments not yet supported")
+        raise MixError("Message fragments not yet supported")
 
     # Uncompress the body.
     return uncompressData(payload.getContents())
@@ -600,14 +600,14 @@
     """Given a string 'payload', compress it with the 'deflate' method
        as specified in the remailer spec and in RFC1951."""
     if not _ZLIB_LIBRARY_OK:
-	_validateZlib()
+        _validateZlib()
 
     # Don't change any of these options; if different Mixminion clients
     # compress their data differently, an adversary could distinguish
     # messages generated by them.
     zobj = zlib.compressobj(zlib.Z_BEST_COMPRESSION, zlib.DEFLATED,
-			    zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL,
-			    zlib.Z_DEFAULT_STRATEGY)
+                            zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL,
+                            zlib.Z_DEFAULT_STRATEGY)
     s1 = zobj.compress(payload)
     s2 = zobj.flush()
     s = s1 + s2
@@ -627,20 +627,20 @@
     # FFFF zero bytes down to fit in a single payload and use us to
     # FFFF mailbomb people, hard.
     if len(payload) < 6 or payload[0:2] != '\x78\xDA':
-	raise ParseError("Invalid zlib header")
+        raise ParseError("Invalid zlib header")
     try:
-	# We can't just call zlib.decompress(payload), since we'll eventually
-	# want to limit the output size.
-	zobj = zlib.decompressobj(zlib.MAX_WBITS)
-	# Decompress the payload.
-	d = zobj.decompress(payload)
-	# Get any leftovers, which shouldn't exist.
-	nil = zobj.flush()
-	if nil != '':
-	    raise ParseError("Error in compressed data")
-	return d
+        # We can't just call zlib.decompress(payload), since we'll eventually
+        # want to limit the output size.
+        zobj = zlib.decompressobj(zlib.MAX_WBITS)
+        # Decompress the payload.
+        d = zobj.decompress(payload)
+        # Get any leftovers, which shouldn't exist.
+        nil = zobj.flush()
+        if nil != '':
+            raise ParseError("Error in compressed data")
+        return d
     except zlib.error:
-	raise ParseError("Error in compressed data")
+        raise ParseError("Error in compressed data")
 
 def _validateZlib():
     """Internal function:  Make sure that zlib is a recognized version, and
@@ -651,23 +651,23 @@
     global _ZLIB_LIBRARY_OK
     ver = getattr(zlib, "ZLIB_VERSION")
     if ver and ver < "1.1.2":
-	raise MixFatalError("Zlib version %s is not supported"%ver)
+        raise MixFatalError("Zlib version %s is not supported"%ver)
 
     _ZLIB_LIBRARY_OK = 0.5
     if ver in ("1.1.2", "1.1.3", "1.1.4"):
-	_ZLIB_LIBRARY_OK = 1
-	return
+        _ZLIB_LIBRARY_OK = 1
+        return
 
     LOG.warn("Unrecognized zlib version: %r. Spot-checking output", ver)
     # This test is inadequate, but it _might_ catch future incompatible
     # changes.
     _ZLIB_LIBRARY_OK = 0.5
     good = '\x78\xda\xed\xc6A\x11\x00 \x08\x00\xb0l\xd4\xf0\x87\x02\xf6o'+\
-	   '`\x0e\xef\xb6\xd7r\xed\x88S=7\xcd\xcc\xcc\xcc\xcc\xcc\xcc'+\
-	   '\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xbe\xdd\x03'+\
-	   'q\x8d\n\x93'
+           '`\x0e\xef\xb6\xd7r\xed\x88S=7\xcd\xcc\xcc\xcc\xcc\xcc\xcc'+\
+           '\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xbe\xdd\x03'+\
+           'q\x8d\n\x93'
     if compressData("aZbAAcdefg"*1000) == good:
-	_ZLIB_LIBRARY_OK = 1
+        _ZLIB_LIBRARY_OK = 1
     else:
-	_ZLIB_LIBRARY_OK = 0
-	raise MixFatalError("Zlib output not as exected.")
+        _ZLIB_LIBRARY_OK = 0
+        raise MixFatalError("Zlib output not as exected.")

Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- ClientMain.py	15 Dec 2002 03:45:30 -0000	1.16
+++ ClientMain.py	16 Dec 2002 02:40:11 -0000	1.17
@@ -62,141 +62,141 @@
     # byFilename: a map from filename within self.directory to valid
     #     ServerInfo object.
     def __init__(self, directory, now=None):
-	"""Create a new TrivialKeystore to access the descriptors stored in
-	   directory.  Selects descriptors that are valid at the time 'now',
-	   or at the current time if 'now' is None."""
-	self.directory = directory
-	createPrivateDir(directory)
-	self.byNickname = {}
-	self.byFilename = {}
+        """Create a new TrivialKeystore to access the descriptors stored in
+           directory.  Selects descriptors that are valid at the time 'now',
+           or at the current time if 'now' is None."""
+        self.directory = directory
+        createPrivateDir(directory)
+        self.byNickname = {}
+        self.byFilename = {}
 
-	if now is None:
-	    now = time.time()
+        if now is None:
+            now = time.time()
 
-	for f in os.listdir(self.directory):
-	    # Try to read a file: is it a server descriptor?
-	    p = os.path.join(self.directory, f)
-	    try:
-		info = ServerInfo(fname=p, assumeValid=0)
-	    except ConfigError:
-		LOG.warn("Invalid server descriptor %s", p)
-		continue
+        for f in os.listdir(self.directory):
+            # Try to read a file: is it a server descriptor?
+            p = os.path.join(self.directory, f)
+            try:
+                info = ServerInfo(fname=p, assumeValid=0)
+            except ConfigError:
+                LOG.warn("Invalid server descriptor %s", p)
+                continue
 
-	    # Find its nickname and normalized filename
-	    serverSection = info['Server']
-	    nickname = serverSection['Nickname']
+            # Find its nickname and normalized filename
+            serverSection = info['Server']
+            nickname = serverSection['Nickname']
 
-	    if '.' in f:
-		f = f[:f.rindex('.')]
+            if '.' in f:
+                f = f[:f.rindex('.')]
 
-	    # Skip the descriptor if it isn't valid yet...
-	    if now < serverSection['Valid-After']:
-		LOG.info("Ignoring future decriptor %s", p)
-		continue
-	    # ... or if it's expired ...
-	    if now >= serverSection['Valid-Until']:
-		LOG.info("Ignoring expired decriptor %s", p)
-		continue
-	    # ... or if it's going to expire within 3 hours (HACK!).
-	    if now + 3*60*60 >= serverSection['Valid-Until']:
-		LOG.info("Ignoring soon-to-expire decriptor %s", p)
-		continue
-	    # Only allow one server per nickname ...
-	    if self.byNickname.has_key(nickname):
-		LOG.warn(
-		    "Ignoring descriptor %s with duplicate nickname %s",
-		    p, nickname)
-		continue
-	    # ... and per normalized filename.
-	    if self.byFilename.has_key(f):
-		LOG.warn(
-		    "Ignoring descriptor %s with duplicate prefix %s",
-		    p, f)
-		continue
-	    LOG.info("Loaded server %s from %s", nickname, f)
-	    # Okay, it's good. Cache it.
-	    self.byNickname[nickname] = info
-	    self.byFilename[f] = info
+            # Skip the descriptor if it isn't valid yet...
+            if now < serverSection['Valid-After']:
+                LOG.info("Ignoring future decriptor %s", p)
+                continue
+            # ... or if it's expired ...
+            if now >= serverSection['Valid-Until']:
+                LOG.info("Ignoring expired decriptor %s", p)
+                continue
+            # ... or if it's going to expire within 3 hours (HACK!).
+            if now + 3*60*60 >= serverSection['Valid-Until']:
+                LOG.info("Ignoring soon-to-expire decriptor %s", p)
+                continue
+            # Only allow one server per nickname ...
+            if self.byNickname.has_key(nickname):
+                LOG.warn(
+                    "Ignoring descriptor %s with duplicate nickname %s",
+                    p, nickname)
+                continue
+            # ... and per normalized filename.
+            if self.byFilename.has_key(f):
+                LOG.warn(
+                    "Ignoring descriptor %s with duplicate prefix %s",
+                    p, f)
+                continue
+            LOG.info("Loaded server %s from %s", nickname, f)
+            # Okay, it's good. Cache it.
+            self.byNickname[nickname] = info
+            self.byFilename[f] = info
 
     def getServerInfo(self, name):
-	"""Return a ServerInfo object corresponding to 'name'.  If 'name' is
- 	   a ServerInfo object, returns 'name'.  Otherwise, checks server by
- 	   nickname, then by filename within the keystore, then by filename
- 	   on the file system. If no server is found, returns None."""
-	if isinstance(name, ServerInfo):
-	    return name
-	elif self.byNickname.has_key(name):
-	    return self.byNickname[name]
-	elif self.byFilename.has_key(name):
-	    return self.byFilename[name]
-	elif os.path.exists(name):
-	    try:
-		return ServerInfo(fname=name, assumeValid=0)
-	    except OSError, e:
-		raise MixError("Couldn't read descriptor %s: %s" %
-			       (name, e))
-	    except ConfigError, e:
-		raise MixError("Couldn't parse descriptor %s: %s" %
-			       (name, e))
-	else:
-	    return None
+        """Return a ServerInfo object corresponding to 'name'.  If 'name' is
+           a ServerInfo object, returns 'name'.  Otherwise, checks server by
+           nickname, then by filename within the keystore, then by filename
+           on the file system. If no server is found, returns None."""
+        if isinstance(name, ServerInfo):
+            return name
+        elif self.byNickname.has_key(name):
+            return self.byNickname[name]
+        elif self.byFilename.has_key(name):
+            return self.byFilename[name]
+        elif os.path.exists(name):
+            try:
+                return ServerInfo(fname=name, assumeValid=0)
+            except OSError, e:
+                raise MixError("Couldn't read descriptor %s: %s" %
+                               (name, e))
+            except ConfigError, e:
+                raise MixError("Couldn't parse descriptor %s: %s" %
+                               (name, e))
+        else:
+            return None
 
     def getPath(self, serverList):
-	"""Given a sequence of strings of ServerInfo objects, resolves each
-	   one according to the rule of getServerInfo, and returns a list of
-	   ServerInfos.  Raises MixError if any server can't be resolved."""
-	path = []
-	for s in serverList:
-	    if isinstance(s, ServerInfo):
-		path.append(s)
-	    elif isinstance(s, types.StringType):
-		server = self.getServerInfo(s)
-		if server is not None:
-		    path.append(server)
-		else:
-		    raise MixError("Couldn't find descriptor %s" % s)
-	return path
+        """Given a sequence of strings of ServerInfo objects, resolves each
+           one according to the rule of getServerInfo, and returns a list of
+           ServerInfos.  Raises MixError if any server can't be resolved."""
+        path = []
+        for s in serverList:
+            if isinstance(s, ServerInfo):
+                path.append(s)
+            elif isinstance(s, types.StringType):
+                server = self.getServerInfo(s)
+                if server is not None:
+                    path.append(server)
+                else:
+                    raise MixError("Couldn't find descriptor %s" % s)
+        return path
 
     def listServers(self):
-	"""Returns a linewise listing of the current servers and their caps.
-	   stdout.  This will go away or get refactored in future versions
-	   once we have real path selection and client-level modules."""
+        """Returns a linewise listing of the current servers and their caps.
+           stdout.  This will go away or get refactored in future versions
+           once we have real path selection and client-level modules."""
         lines = []
         nicknames = self.byNickname.keys()
-	nicknames.sort()
-	longestnamelen = max(map(len, nicknames))
-	fmtlen = min(longestnamelen, 20)
-	format = "%"+str(fmtlen)+"s (expires %s): %s"
-	for n in nicknames:
-	    caps = []
-	    si = self.byNickname[n]
-	    if si['Delivery/MBOX'].get('Version',None):
-		caps.append("mbox")
-	    if si['Delivery/SMTP'].get('Version',None):
-		caps.append("smtp")
-	    # XXXX This next check is highly bogus.
-	    if (si['Incoming/MMTP'].get('Version',None) and 
-		si['Outgoing/MMTP'].get('Version',None)):
-		caps.append("relay")
-	    until = formatDate(si['Server']['Valid-Until'])
-	    line = format % (n, until, " ".join(caps))
-	    lines.append(line)
-	return lines
+        nicknames.sort()
+        longestnamelen = max(map(len, nicknames))
+        fmtlen = min(longestnamelen, 20)
+        format = "%"+str(fmtlen)+"s (expires %s): %s"
+        for n in nicknames:
+            caps = []
+            si = self.byNickname[n]
+            if si['Delivery/MBOX'].get('Version',None):
+                caps.append("mbox")
+            if si['Delivery/SMTP'].get('Version',None):
+                caps.append("smtp")
+            # XXXX This next check is highly bogus.
+            if (si['Incoming/MMTP'].get('Version',None) and 
+                si['Outgoing/MMTP'].get('Version',None)):
+                caps.append("relay")
+            until = formatDate(si['Server']['Valid-Until'])
+            line = format % (n, until, " ".join(caps))
+            lines.append(line)
+        return lines
 
     def getRandomServers(self, prng, n):
-	"""Returns a list of n different servers, in random order, according
-	   to prng.  Raises MixError if not enough exist.
+        """Returns a list of n different servers, in random order, according
+           to prng.  Raises MixError if not enough exist.
 
-	   (This isn't actually used.)"""
-	vals = self.byNickname.values()
-	if len(vals) < n:
-	    raise MixError("Not enough servers (%s requested)", n)
-	return prng.shuffle(vals, n)
+           (This isn't actually used.)"""
+        vals = self.byNickname.values()
+        if len(vals) < n:
+            raise MixError("Not enough servers (%s requested)", n)
+        return prng.shuffle(vals, n)
 
 def installDefaultConfig(fname):
     """Create a default, 'fail-safe' configuration in a given file"""
     LOG.warn("No configuration file found. Installing default file in %s",
-		  fname)
+                  fname)
     f = open(os.path.expanduser(fname), 'w')
     f.write("""\
 # This file contains your options for the mixminion client.
@@ -232,83 +232,83 @@
     # keystore: A TrivialKeystore object
     # prng: A pseudo-random number generator for padding and path selection
     def __init__(self, conf):
-	"""Create a new MixminionClient with a given configuration"""
-	self.config = conf
+        """Create a new MixminionClient with a given configuration"""
+        self.config = conf
 
-	# Make directories
-	userdir = os.path.expanduser(self.config['User']['UserDir'])
-	createPrivateDir(userdir)
-	#createPrivateDir(os.path.join(userdir, 'surbs'))
-	createPrivateDir(os.path.join(userdir, 'servers'))
+        # Make directories
+        userdir = os.path.expanduser(self.config['User']['UserDir'])
+        createPrivateDir(userdir)
+        #createPrivateDir(os.path.join(userdir, 'surbs'))
+        createPrivateDir(os.path.join(userdir, 'servers'))
 
-	# Get directory cache
-	self.keystore = TrivialKeystore(
-	    os.path.join(userdir,"servers"))
+        # Get directory cache
+        self.keystore = TrivialKeystore(
+            os.path.join(userdir,"servers"))
 
-	# Initialize PRNG
-	self.prng = mixminion.Crypto.getCommonPRNG()
+        # Initialize PRNG
+        self.prng = mixminion.Crypto.getCommonPRNG()
 
     def sendForwardMessage(self, address, payload, path1, path2):
-	"""Generate and send a forward message.
-	    address -- the results of a parseAddress call
-	    payload -- the contents of the message to send
-	    path1,path2 -- lists of servers or server names for the first and
-	       second legs of the path, respectively.  These are processed
-	       as described in TrivialKeystore.getServerInfo"""
-	message, firstHop = \
-		 self.generateForwardMessage(address, payload, path1, path2)
+        """Generate and send a forward message.
+            address -- the results of a parseAddress call
+            payload -- the contents of the message to send
+            path1,path2 -- lists of servers or server names for the first and
+               second legs of the path, respectively.  These are processed
+               as described in TrivialKeystore.getServerInfo"""
+        message, firstHop = \
+                 self.generateForwardMessage(address, payload, path1, path2)
 
-	self.sendMessages([message], firstHop)
+        self.sendMessages([message], firstHop)
 
     def generateForwardMessage(self, address, payload, path1, path2):
-	"""Generate a forward message, but do not send it.  Returns
-	   a tuple of (the message body, a ServerInfo for the first hop.)
+        """Generate a forward message, but do not send it.  Returns
+           a tuple of (the message body, a ServerInfo for the first hop.)
 
-	    address -- the results of a parseAddress call
-	    payload -- the contents of the message to send
-	    path1,path2 -- lists of servers or server names for the first and
-	       second legs of the path, respectively.  These are processed
-	       as described in TrivialKeystore.getServerInfo"""
+            address -- the results of a parseAddress call
+            payload -- the contents of the message to send
+            path1,path2 -- lists of servers or server names for the first and
+               second legs of the path, respectively.  These are processed
+               as described in TrivialKeystore.getServerInfo"""
         if not path1:
-	    raise MixError("No servers in first leg of path")
-	if not path2:
-	    raise MixError("No servers in second leg of path")
+            raise MixError("No servers in first leg of path")
+        if not path2:
+            raise MixError("No servers in second leg of path")
 
-	servers1 = self.keystore.getPath(path1)
-	servers2 = self.keystore.getPath(path2)
+        servers1 = self.keystore.getPath(path1)
+        servers2 = self.keystore.getPath(path2)
 
-	routingType, routingInfo, lastHop = address.getRouting()
-	if lastHop is None:
+        routingType, routingInfo, lastHop = address.getRouting()
+        if lastHop is None:
             lastServer = servers2[-1]
-	    # FFFF This is only a temporary solution.  It needs to get
-	    # FFFF rethought, or refactored into ServerInfo, or something.
-	    if routingType == SMTP_TYPE:
-		ok = lastServer['Delivery/SMTP'].get('Version',None)
-		if not ok:
-		    raise MixError("Last hop doesn't support SMTP")
-	    elif routingType == MBOX_TYPE:
-		ok = lastServer['Delivery/MBOX'].get('Version',None)
-		if not ok:
-		    raise MixError("Last hop doesn't support MBOX")
-	else:
-	    servers2.append(self.keystore.getServerInfo(lastHop))
-	msg = mixminion.BuildMessage.buildForwardMessage(
-	    payload, routingType, routingInfo, servers1, servers2,
-	    self.prng)
-	return msg, servers1[0]
+            # FFFF This is only a temporary solution.  It needs to get
+            # FFFF rethought, or refactored into ServerInfo, or something.
+            if routingType == SMTP_TYPE:
+                ok = lastServer['Delivery/SMTP'].get('Version',None)
+                if not ok:
+                    raise MixError("Last hop doesn't support SMTP")
+            elif routingType == MBOX_TYPE:
+                ok = lastServer['Delivery/MBOX'].get('Version',None)
+                if not ok:
+                    raise MixError("Last hop doesn't support MBOX")
+        else:
+            servers2.append(self.keystore.getServerInfo(lastHop))
+        msg = mixminion.BuildMessage.buildForwardMessage(
+            payload, routingType, routingInfo, servers1, servers2,
+            self.prng)
+        return msg, servers1[0]
 
     def sendMessages(self, msgList, server):
-	"""Given a list of packets and a ServerInfo object, sends the
-	   packets to the server via MMTP"""
-	con = mixminion.MMTPClient.BlockingClientConnection(server.getAddr(),
-							    server.getPort(),
-							    server.getKeyID())
-	try:
-	    con.connect()
-	    for msg in msgList:
-		con.sendPacket(msg)
-	finally:
-	    con.shutdown()
+        """Given a list of packets and a ServerInfo object, sends the
+           packets to the server via MMTP"""
+        con = mixminion.MMTPClient.BlockingClientConnection(server.getAddr(),
+                                                            server.getPort(),
+                                                            server.getKeyID())
+        try:
+            con.connect()
+            for msg in msgList:
+                con.sendPacket(msg)
+        finally:
+            con.shutdown()
 
 def parseAddress(s):
     """Parse and validate an address; takes a string, and returns an Address
@@ -317,43 +317,43 @@
        Accepts strings of the format:
               mbox:<mailboxname>@<server>
            OR smtp:<email address>
-	   OR <email address> (smtp is implicit)
-	   OR drop
-	   OR 0x<routing type>:<routing info>
+           OR <email address> (smtp is implicit)
+           OR drop
+           OR 0x<routing type>:<routing info>
     """
     # ???? Should this should get refactored into clientmodules, or someplace?
     if s.lower() == 'drop':
-	return Address(DROP_TYPE, None, None)
+        return Address(DROP_TYPE, None, None)
     elif s.lower() == 'test':
-	return Address(0xFFFE, "", None)
+        return Address(0xFFFE, "", None)
     elif ':' not in s:
-	if isSMTPMailbox(s):
-	    return Address(SMTP_TYPE, s, None)
-	else:
-	    raise ParseError("Can't parse address %s"%s)
+        if isSMTPMailbox(s):
+            return Address(SMTP_TYPE, s, None)
+        else:
+            raise ParseError("Can't parse address %s"%s)
     tp,val = s.split(':', 1)
     tp = tp.lower()
     if tp.startswith("0x"):
-	try:
-	    tp = int(tp[2:], 16)
-	except ValueError:
-	    raise ParseError("Invalid hexidecimal value %s"%tp)
-	if not (0x0000 <= tp <= 0xFFFF):
-	    raise ParseError("Invalid type: 0x%04x"%tp)
-	return Address(tp, val, None)
+        try:
+            tp = int(tp[2:], 16)
+        except ValueError:
+            raise ParseError("Invalid hexidecimal value %s"%tp)
+        if not (0x0000 <= tp <= 0xFFFF):
+            raise ParseError("Invalid type: 0x%04x"%tp)
+        return Address(tp, val, None)
     elif tp == 'mbox':
-	if "@" in val:
+        if "@" in val:
             mbox, server = val.split("@",1)
             return Address(MBOX_TYPE, parseMBOXInfo(mbox).pack(), server)
         else:
-	    return Address(MBOX_TYPE, parseMBOXInfo(val).pack(), None)
+            return Address(MBOX_TYPE, parseMBOXInfo(val).pack(), None)
     elif tp == 'smtp':
-	# May raise ParseError
-	return Address(SMTP_TYPE, parseSMTPInfo(val).pack(), None)
+        # May raise ParseError
+        return Address(SMTP_TYPE, parseSMTPInfo(val).pack(), None)
     elif tp == 'test':
-	return Address(0xFFFE, val, None)
+        return Address(0xFFFE, val, None)
     else:
-	raise ParseError("Unrecognized address type: %s"%s)
+        raise ParseError("Unrecognized address type: %s"%s)
 
 class Address:
     """Represents the target address for a Mixminion message.
@@ -361,31 +361,31 @@
        the last hop, and (optionally) a server to use as the last hop.
        """
     def __init__(self, exitType, exitAddress, lastHop=None):
-	self.exitType = exitType
-	self.exitAddress = exitAddress
-	self.lastHop = lastHop
+        self.exitType = exitType
+        self.exitAddress = exitAddress
+        self.lastHop = lastHop
     def getRouting(self):
-	return self.exitType, self.exitAddress, self.lastHop
+        return self.exitType, self.exitAddress, self.lastHop
 
 def readConfigFile(configFile):
     try:
-	return ClientConfig(fname=configFile)
+        return ClientConfig(fname=configFile)
     except (IOError, OSError), e:
-	print >>sys.stderr, "Error reading configuration file %r:"%configFile
-	print >>sys.stderr, "   ", str(e)
-	sys.exit(1)
+        print >>sys.stderr, "Error reading configuration file %r:"%configFile
+        print >>sys.stderr, "   ", str(e)
+        sys.exit(1)
     except ConfigError, e:
-	print >>sys.stderr, "Error in configuration file %r"%configFile
-	print >>sys.stderr, str(e)
-	sys.exit(1)
+        print >>sys.stderr, "Error in configuration file %r"%configFile
+        print >>sys.stderr, str(e)
+        sys.exit(1)
     return None #suppress pychecker warning
 
 # NOTE: This isn't anything LIKE the final client interface.  Many or all
 #       options will change between now and 1.0.0
 def runClient(cmd, args):
     options, args = getopt.getopt(args, "hvf:i:t:",
-				  ["help", "verbose", "config=", "input=",
-				   "path1=", "path2=", "to="])
+                                  ["help", "verbose", "config=", "input=",
+                                   "path1=", "path2=", "to="])
     configFile = '~/.mixminionrc'
     usage = 0
     inFile = "-"
@@ -394,52 +394,52 @@
     path2 = []
     address = None
     for opt,val in options:
-	if opt in ('-h', '--help'):
-	    usage=1
-	elif opt in ('-f', '--config'):
-	    configFile = val
-	elif opt in ('-i', '--input'):
-	    inFile = val
-	elif opt in ('-v', '--verbose'):
-	    verbose = 1
-	elif opt == '--path1':
-	    path1.extend(val.split(","))
-	elif opt == '--path2':
-	    path2.extend(val.split(","))
-	elif opt in ('-t', '--to'):
-	    address = parseAddress(val)
+        if opt in ('-h', '--help'):
+            usage=1
+        elif opt in ('-f', '--config'):
+            configFile = val
+        elif opt in ('-i', '--input'):
+            inFile = val
+        elif opt in ('-v', '--verbose'):
+            verbose = 1
+        elif opt == '--path1':
+            path1.extend(val.split(","))
+        elif opt == '--path2':
+            path2.extend(val.split(","))
+        elif opt in ('-t', '--to'):
+            address = parseAddress(val)
     if args:
-	print >>sys.stderr, "Unexpected options."
-	usage = 1
+        print >>sys.stderr, "Unexpected options."
+        usage = 1
     if not path1:
-	print >>sys.stderr, "First leg of path was not specified"
-	usage = 1
+        print >>sys.stderr, "First leg of path was not specified"
+        usage = 1
     if not path2:
-	print >>sys.stderr, "Second leg of path was not specified"
-	usage = 1
+        print >>sys.stderr, "Second leg of path was not specified"
+        usage = 1
     if address is None:
-	print >>sys.stderr, "No recipient specified"
-	usage = 1
+        print >>sys.stderr, "No recipient specified"
+        usage = 1
     if usage:
-	print >>sys.stderr, """\
+        print >>sys.stderr, """\
 Usage: %s [-h] [-v] [-f configfile] [-i inputfile]
           [--path1=server1,server2,...]
           [--path2=server1,server2,...] [-t <address>]"""%cmd
-	sys.exit(1)
+        sys.exit(1)
 
     if configFile is None:
-	configFile = os.environ.get("MIXMINIONRC", None)
-	if configFile is None:
-	    configFile = "~/.mixminionrc"
+        configFile = os.environ.get("MIXMINIONRC", None)
+        if configFile is None:
+            configFile = "~/.mixminionrc"
 
     configFile = os.path.expanduser(configFile)
     if not os.path.exists(configFile):
-	installDefaultConfig(configFile)
+        installDefaultConfig(configFile)
     config = readConfigFile(configFile)
 
     LOG.configure(config)
     if verbose:
-	LOG.setMinSeverity("DEBUG")
+        LOG.setMinSeverity("DEBUG")
 
     LOG.debug("Configuring client")
     mixminion.Common.configureShredCommand(config)
@@ -448,9 +448,9 @@
     client = MixminionClient(config)
 
     if inFile == '-':
-	f = sys.stdin
+        f = sys.stdin
     else:
-	f = open(inFile, 'r')
+        f = open(inFile, 'r')
     payload = f.read()
     f.close()
 
@@ -460,26 +460,26 @@
     options, args = getopt.getopt(args, "hf:", ['help', 'config='])
     configFile = None
     for o,v in options:
-	if o in ('-h', '--help'):
-	    print "Usage %s [--help] [--config=configFile]"
-	    sys.exit(1)
-	elif o in ('-f', '--config'):
-	    configFile = v
+        if o in ('-h', '--help'):
+            print "Usage %s [--help] [--config=configFile]"
+            sys.exit(1)
+        elif o in ('-f', '--config'):
+            configFile = v
 
     # XXXX duplicate code; refactor into separate method.
     if configFile is None:
-	configFile = os.environ.get("MIXMINIONRC", None)
-	if configFile is None:
-	    configFile = "~/.mixminionrc"
+        configFile = os.environ.get("MIXMINIONRC", None)
+        if configFile is None:
+            configFile = "~/.mixminionrc"
     configFile = os.path.expanduser(configFile)
     if not os.path.exists(configFile):
-	installDefaultConfig(configFile)
+        installDefaultConfig(configFile)
     config = readConfigFile(configFile)
 
     userdir = os.path.expanduser(config['User']['UserDir'])
     createPrivateDir(os.path.join(userdir, 'servers'))
 
     keystore = TrivialKeystore(os.path.join(userdir,"servers"))
-	
+        
     for line in keystore.listServers():
-	print line
+        print line

Index: Common.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Common.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- Common.py	16 Dec 2002 01:37:19 -0000	1.35
+++ Common.py	16 Dec 2002 02:40:11 -0000	1.36
@@ -6,11 +6,11 @@
    Common functionality and utility code for Mixminion"""
 
 __all__ = [ 'LOG', 'LogStream', 'MixError', 'MixFatalError',
-	    'MixProtocolError', 'ceilDiv', 'checkPrivateDir',
-	    'createPrivateDir', 'floorDiv', 'formatBase64', 'formatDate',
-	    'formatTime', 'installSignalHandlers', 'isSMTPMailbox', 'mkgmtime',
-	    'onReset', 'onTerminate', 'previousMidnight', 'secureDelete',
-	    'stringContains', 'waitForChildren' ]
+            'MixProtocolError', 'ceilDiv', 'checkPrivateDir',
+            'createPrivateDir', 'floorDiv', 'formatBase64', 'formatDate',
+            'formatTime', 'installSignalHandlers', 'isSMTPMailbox', 'mkgmtime',
+            'onReset', 'onTerminate', 'previousMidnight', 'secureDelete',
+            'stringContains', 'waitForChildren' ]
 
 import base64
 import calendar
@@ -80,15 +80,15 @@
 # String containing all printing ascii characters, and all characters that
 # may be used in an extended charset.
 _P_ASCII_CHARS_HIGH = "\t\n\v\r"+"".join(map(chr, range(0x20, 0x7F)+
-					          range(0x80, 0xFF)))
+                                                  range(0x80, 0xFF)))
 
 def isPrintingAscii(s,allowISO=0):
     """Return true iff every character in s is a printing ascii character.
        If allowISO is true, also permit characters between 0x80 and 0xFF."""
     if allowISO:
-	return len(s.translate(_ALLCHARS, _P_ASCII_CHARS_HIGH)) == 0
+        return len(s.translate(_ALLCHARS, _P_ASCII_CHARS_HIGH)) == 0
     else:
-	return len(s.translate(_ALLCHARS, _P_ASCII_CHARS)) == 0
+        return len(s.translate(_ALLCHARS, _P_ASCII_CHARS)) == 0
 
 def stripSpace(s, space=" \t\v\n"):
     """Remove all whitespace from s."""
@@ -103,12 +103,12 @@
     """Create a directory, and all parent directories, checking permissions
        as we go along.  All superdirectories must be owned by root or us."""
     if not os.path.exists(d):
-	if nocreate:
-	    raise MixFatalError("Nonexistent directory %s" % d)
-	try:
-	    os.makedirs(d, 0700)
-	except OSError:
-	    raise MixFatalError("Unable to create directory %s" % d)
+        if nocreate:
+            raise MixFatalError("Nonexistent directory %s" % d)
+        try:
+            os.makedirs(d, 0700)
+        except OSError:
+            raise MixFatalError("Unable to create directory %s" % d)
 
     checkPrivateDir(d)
 
@@ -120,41 +120,41 @@
     me = os.getuid()
 
     if not os.path.exists(d):
-	raise MixFatalError("Directory %s does not exist" % d)
+        raise MixFatalError("Directory %s does not exist" % d)
     if not os.path.isdir(d):
-	raise MixFatalError("%s is not a directory" % d)
+        raise MixFatalError("%s is not a directory" % d)
 
     st = os.stat(d)
     # check permissions
     if st[stat.ST_MODE] & 0777 != 0700:
-	raise MixFatalError("Directory %s must be mode 0700" % d)
+        raise MixFatalError("Directory %s must be mode 0700" % d)
 
     if st[stat.ST_UID] != me:
-	raise MixFatalError("Directory %s has must have owner %s" %(d, me))
+        raise MixFatalError("Directory %s has must have owner %s" %(d, me))
 
     if not recurse:
-	return
+        return
 
     # Check permissions on parents.
     while 1:
-	parent = os.path.split(d)[0]
-	if parent == d:
-	    return
-	d = parent
+        parent = os.path.split(d)[0]
+        if parent == d:
+            return
+        d = parent
 
-	st = os.stat(d)
-	mode = st[stat.ST_MODE]
-	owner = st[stat.ST_UID]
-	if owner not in (0, me):
-	    raise MixFatalError("Bad owner (uid=%s) on directory %s"
-				% (owner, d))
-	if (mode & 02) and not (mode & stat.S_ISVTX):
-	    raise MixFatalError("Bad mode (%o) on directory %s" %(mode, d))
+        st = os.stat(d)
+        mode = st[stat.ST_MODE]
+        owner = st[stat.ST_UID]
+        if owner not in (0, me):
+            raise MixFatalError("Bad owner (uid=%s) on directory %s"
+                                % (owner, d))
+        if (mode & 02) and not (mode & stat.S_ISVTX):
+            raise MixFatalError("Bad mode (%o) on directory %s" %(mode, d))
 
-	if (mode & 020) and not (mode & stat.S_ISVTX):
-	    # FFFF We may want to give an even stronger error here.
-	    LOG.warn("Iffy mode %o on directory %s (Writable by gid %s)",
-		     mode, d, st[stat.ST_GID])
+        if (mode & 020) and not (mode & stat.S_ISVTX):
+            # FFFF We may want to give an even stronger error here.
+            LOG.warn("Iffy mode %o on directory %s (Writable by gid %s)",
+                     mode, d, st[stat.ST_GID])
 
 #----------------------------------------------------------------------
 # Secure filesystem operations.
@@ -181,7 +181,7 @@
         if os.path.exists("/usr/bin/shred"):
             cmd, opts = "/usr/bin/shred", ["-uz", "-n0"]
         else:
-	    # Use built-in _overwriteFile
+            # Use built-in _overwriteFile
             cmd, opts = None, None
 
     _SHRED_CMD, _SHRED_OPTS = cmd, opts
@@ -198,21 +198,21 @@
     global _BLKSIZE
     global _NILSTR
     if not _BLKSIZE:
-	#???? this assumes that all filesystems we are using have the same
-	#???? block size.
-	if hasattr(os, 'statvfs'):
-	    _BLKSIZE = os.statvfs(f)[statvfs.F_BSIZE]
-	else:
-	    _BLKSIZE = 8192 # ???? Safe guess?
-	_NILSTR = '\x00' * _BLKSIZE
+        #???? this assumes that all filesystems we are using have the same
+        #???? block size.
+        if hasattr(os, 'statvfs'):
+            _BLKSIZE = os.statvfs(f)[statvfs.F_BSIZE]
+        else:
+            _BLKSIZE = 8192 # ???? Safe guess?
+        _NILSTR = '\x00' * _BLKSIZE
     fd = os.open(f, os.O_WRONLY)
     try:
-	size = os.fstat(fd)[stat.ST_SIZE]
-	blocks = ceilDiv(size, _BLKSIZE)
-	for _ in xrange(blocks):
-	    os.write(fd, _NILSTR)
+        size = os.fstat(fd)[stat.ST_SIZE]
+        blocks = ceilDiv(size, _BLKSIZE)
+        for _ in xrange(blocks):
+            os.write(fd, _NILSTR)
     finally:
-	os.close(fd)
+        os.close(fd)
 
 def secureDelete(fnames, blocking=0):
     """Given a list of filenames, removes the contents of all of those
@@ -256,7 +256,7 @@
 
     if not _SHRED_CMD:
         for f in fnames:
-	    _overwriteFile(f)
+            _overwriteFile(f)
             os.unlink(f)
         return None
 
@@ -267,8 +267,8 @@
 
     # Some systems are unhappy when you call them with too many options.
     for i in xrange(0, len(fnames), 250-len(_SHRED_OPTS)):
-	files = fnames[i:i+250-len(_SHRED_OPTS)]
-	os.spawnl(mode, _SHRED_CMD, _SHRED_CMD, *(_SHRED_OPTS+files))
+        files = fnames[i:i+250-len(_SHRED_OPTS)]
+        os.spawnl(mode, _SHRED_CMD, _SHRED_CMD, *(_SHRED_OPTS+files))
 
 #----------------------------------------------------------------------
 # Logging
@@ -287,30 +287,30 @@
      #     file -- a file object, or None if the file is closed.
      #     fname -- this log's associated filename
     def __init__(self, fname):
-	"Create a new FileLogHandler to append messages to fname"
+        "Create a new FileLogHandler to append messages to fname"
         self.file = None
         self.fname = fname
         self.reset()
     def reset(self):
-	"""Close and reopen our underlying file.  This behavior is needed
-	   to implement log rotation."""
+        """Close and reopen our underlying file.  This behavior is needed
+           to implement log rotation."""
         if self.file is not None:
             self.file.close()
-	try:
-	    parent = os.path.split(self.fname)[0]
-	    if not os.path.exists(parent):
-		createPrivateDir(parent)
-	    self.file = open(self.fname, 'a')
-	except OSError:
-	    self.file = None
-	    raise MixError("Unable to open log file %r"%self.fname)
+        try:
+            parent = os.path.split(self.fname)[0]
+            if not os.path.exists(parent):
+                createPrivateDir(parent)
+            self.file = open(self.fname, 'a')
+        except OSError:
+            self.file = None
+            raise MixError("Unable to open log file %r"%self.fname)
     def close(self):
-	"Close the underlying file"
+        "Close the underlying file"
         self.file.close()
     def write(self, severity, message):
-	"""(Used by Log: write a message to this log handler.)"""
-	if self.file is None:
-	    return
+        """(Used by Log: write a message to this log handler.)"""
+        if self.file is None:
+            return
         print >> self.file, "%s [%s] %s" % (_logtime(), severity, message)
         self.file.flush()
 
@@ -318,12 +318,12 @@
     """Helper class for logging: directs all log messages to a stderr-like
        file object"""
     def __init__(self, file):
-	"Create a new _ConsoleLogHandler attached to a given file."""
+        "Create a new _ConsoleLogHandler attached to a given file."""
         self.file = file
     def reset(self): pass
     def close(self): pass
     def write(self, severity, message):
-	"""(Used by Log: write a message to this log handler.)"""
+        """(Used by Log: write a message to this log handler.)"""
         print >> self.file, "%s [%s] %s" % (_logtime(), severity, message)
 
 # Map from log severity name to numeric values
@@ -343,12 +343,12 @@
               TRACE: hyperverbose mode; used for debugging fiddly
                  little details.  This is a security risk.
               DEBUG: very verbose mode; used for tracing connections
-	         and messages through the system.  This is a security risk.
+                 and messages through the system.  This is a security risk.
               INFO: non-critical events.
-	      WARN: recoverable errors
-	      ERROR: nonrecoverable errors that affect only a single
+              WARN: recoverable errors
+              ERROR: nonrecoverable errors that affect only a single
                  message or a connection.
-	      FATAL: nonrecoverable errors that affect the entire system.
+              FATAL: nonrecoverable errors that affect the entire system.
 
        In practise, we instantiate only a single instance of this class,
        accessed as mixminion.Common.LOG."""
@@ -356,14 +356,14 @@
     # handlers: a list of logHandler objects.
     # severity: a severity below which log messages are ignored.
     def __init__(self, minSeverity):
-	"""Create a new Log object that ignores all message less severe than
-	   minSeverity, and sends its output to stderr."""
-	self.configure(None)
-	self.setMinSeverity(minSeverity)
+        """Create a new Log object that ignores all message less severe than
+           minSeverity, and sends its output to stderr."""
+        self.configure(None)
+        self.setMinSeverity(minSeverity)
 
     def configure(self, config):
-	"""Set up this Log object based on a ServerConfig or ClientConfig
-	   object"""
+        """Set up this Log object based on a ServerConfig or ClientConfig
+           object"""
         # XXXX001 Don't EchoLogMessages when NoDaemon==0.
         self.handlers = []
         if config == None or not config.has_section("Server"):
@@ -376,66 +376,66 @@
                 homedir = config['Server']['Homedir']
                 if homedir:
                     logfile = os.path.join(homedir, "log")
-	    self.addHandler(_ConsoleLogHandler(sys.stderr))
+            self.addHandler(_ConsoleLogHandler(sys.stderr))
             if logfile:
-		try:
-		    self.addHandler(_FileLogHandler(logfile))
-		except MixError, e:
-		    self.error(str(e))
+                try:
+                    self.addHandler(_FileLogHandler(logfile))
+                except MixError, e:
+                    self.error(str(e))
             if logfile and not (config['Server'].get('EchoMessages',0) and
-				config['Server'].get('NoDaemon',0)):
-		del self.handlers[0]
+                                config['Server'].get('NoDaemon',0)):
+                del self.handlers[0]
 
     def setMinSeverity(self, minSeverity):
-	"""Sets the minimum severity of messages to be logged.
-	      minSeverity -- the string representation of a severity level."""
+        """Sets the minimum severity of messages to be logged.
+              minSeverity -- the string representation of a severity level."""
         self.severity = _SEVERITIES.get(minSeverity, 1)
 
     def getMinSeverity(self):
-	"""Return a string representation of this log's minimum severity
-	   level."""
+        """Return a string representation of this log's minimum severity
+           level."""
         for k,v in _SEVERITIES.items():
             if v == self.severity:
                 return k
-	return "INFO"
+        return "INFO"
 
     def addHandler(self, handler):
-	"""Add a LogHandler object to the list of objects that receive
-	   messages from this log."""
+        """Add a LogHandler object to the list of objects that receive
+           messages from this log."""
         self.handlers.append(handler)
 
     def reset(self):
-	"""Flush and re-open all logs."""
+        """Flush and re-open all logs."""
         for h in self.handlers:
-	    try:
-		h.reset()
-	    except MixError, e:
-		if len(self.handlers) > 1:
-		    self.error(str(e))
-		else:
-		    print >>sys.stderr, "Unable to reset log system"
+            try:
+                h.reset()
+            except MixError, e:
+                if len(self.handlers) > 1:
+                    self.error(str(e))
+                else:
+                    print >>sys.stderr, "Unable to reset log system"
 
     def close(self):
-	"""Close all logs"""
+        """Close all logs"""
         for h in self.handlers:
             h.close()
 
     def log(self, severity, message, *args):
-	"""Send a message of a given severity to the log.  If additional
-	   arguments are provided, write 'message % args'. """
-	self._log(severity, message, args)
+        """Send a message of a given severity to the log.  If additional
+           arguments are provided, write 'message % args'. """
+        self._log(severity, message, args)
 
     def _log(self, severity, message, args):
-	"""Helper method: If we aren't ignoring messages of level 'severity',
-	   then send message%args to all the underlying log handlers."""
+        """Helper method: If we aren't ignoring messages of level 'severity',
+           then send message%args to all the underlying log handlers."""
 
         # Enable this block to bail early in production versions
         #if _SEVERITIES.get(severity, 100) < self.severity:
         #    return
-	if args is None:
-	    m = message
-	else:
-	    m = message % args
+        if args is None:
+            m = message
+        else:
+            m = message % args
 
         # Enable this block to debug message formats.
         if _SEVERITIES.get(severity, 100) < self.severity:
@@ -445,53 +445,53 @@
             h.write(severity, m)
 
     def trace(self, message, *args):
-	"Write a trace (hyperverbose) message to the log"
+        "Write a trace (hyperverbose) message to the log"
         self.log("TRACE", message, *args)
     def debug(self, message, *args):
-	"Write a debug (verbose) message to the log"
+        "Write a debug (verbose) message to the log"
         self.log("DEBUG", message, *args)
     def info(self, message, *args):
-	"Write an info (non-error) message to the log"
+        "Write an info (non-error) message to the log"
         self.log("INFO", message, *args)
     def warn(self, message, *args):
-	"Write a warn (recoverable error) message to the log"
+        "Write a warn (recoverable error) message to the log"
         self.log("WARN", message, *args)
     def error(self, message, *args):
-	"Write an error (message loss error) message to the log"
+        "Write an error (message loss error) message to the log"
         self.log("ERROR", message, *args)
     def fatal(self, message, *args):
-	"Write a fatal (unrecoverable system error) message to the log"
+        "Write a fatal (unrecoverable system error) message to the log"
         self.log("FATAL", message, *args)
     def log_exc(self, severity, (exclass, ex, tb), message=None, *args):
-	"""Write an exception and stack trace to the log.  If message and
-	   args are provided, use them as an explanitory message; otherwise,
-	   introduce the message as "Unexpected exception".
+        """Write an exception and stack trace to the log.  If message and
+           args are provided, use them as an explanitory message; otherwise,
+           introduce the message as "Unexpected exception".
 
-	   This should usually be called as
-	       LOG.log_exc('ERROR', sys.exc_info(), message, args...)
-	   """
-	if message is not None:
-	    self.log(severity, message, *args)
-	elif tb is not None:
-	    filename = tb.tb_frame.f_code.co_filename
-	    self.log(severity, "Unexpected exception in %s", filename)
-	else:
-	    self.log(severity, "Unexpected exception")
+           This should usually be called as
+               LOG.log_exc('ERROR', sys.exc_info(), message, args...)
+           """
+        if message is not None:
+            self.log(severity, message, *args)
+        elif tb is not None:
+            filename = tb.tb_frame.f_code.co_filename
+            self.log(severity, "Unexpected exception in %s", filename)
+        else:
+            self.log(severity, "Unexpected exception")
 
-	formatted = traceback.format_exception(exclass, ex, tb)
-	formatted[1:] = [ "  %s" % line for line in formatted[1:] ]
-	indented = "".join(formatted)
-	if indented.endswith('\n'):
-	    indented = indented[:-1]
-	self._log(severity, indented, None)
+        formatted = traceback.format_exception(exclass, ex, tb)
+        formatted[1:] = [ "  %s" % line for line in formatted[1:] ]
+        indented = "".join(formatted)
+        if indented.endswith('\n'):
+            indented = indented[:-1]
+        self._log(severity, indented, None)
 
     def error_exc(self, (exclass, ex, tb), message=None, *args):
-	"Same as log_exc, but logs an error message."
-	self.log_exc("ERROR", (exclass, ex, tb), message, *args)
+        "Same as log_exc, but logs an error message."
+        self.log_exc("ERROR", (exclass, ex, tb), message, *args)
 
     def fatal_exc(self, (exclass, ex, tb), message=None, *args):
-	"Same as log_exc, but logs a fatal message."
-	self.log_exc("FATAL", (exclass, ex, tb), message, *args)
+        "Same as log_exc, but logs a fatal message."
+        self.log_exc("FATAL", (exclass, ex, tb), message, *args)
 
 # The global 'Log' instance for the mixminion client or server.
 LOG = Log('WARN')
@@ -504,10 +504,10 @@
        prints on the floor.
        """
     def __init__(self, name, severity):
-	self.name = name
-	self.severity = severity
+        self.name = name
+        self.severity = severity
     def write(self, s):
-	LOG.log(self.severity, "->%s: %s", self.name, s)
+        LOG.log(self.severity, "->%s: %s", self.name, s)
     def flush(self): pass
     def close(self): pass
 
@@ -532,11 +532,11 @@
     """Given a time in seconds since the epoch, returns a time value in the
        format used by server descriptors (YYYY/MM/DD HH:MM:SS) in GMT"""
     if localtime:
-	gmt = time.localtime(when)
+        gmt = time.localtime(when)
     else:
-	gmt = time.gmtime(when)
+        gmt = time.gmtime(when)
     return "%04d/%02d/%02d %02d:%02d:%02d" % (
-	gmt[0],gmt[1],gmt[2],  gmt[3],gmt[4],gmt[5])
+        gmt[0],gmt[1],gmt[2],  gmt[3],gmt[4],gmt[5])
 
 def formatDate(when):
     """Given a time in seconds since the epoch, returns a date value in the

Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.27
retrieving revision 1.28
diff -u -d -r1.27 -r1.28
--- Crypto.py	12 Dec 2002 19:56:46 -0000	1.27
+++ Crypto.py	16 Dec 2002 02:40:11 -0000	1.28
@@ -23,7 +23,7 @@
             'pk_decode_private_key', 'pk_decrypt', 'pk_encode_private_key',
             'pk_encrypt', 'pk_from_modulus', 'pk_generate', 'pk_get_modulus',
             'pk_sign', 'prng', 'sha1', 'strxor', 'trng',
-	    'AES_KEY_LEN', 'DIGEST_LEN', 'HEADER_SECRET_MODE', 'PRNG_MODE',
+            'AES_KEY_LEN', 'DIGEST_LEN', 'HEADER_SECRET_MODE', 'PRNG_MODE',
             'RANDOM_JUNK_MODE', 'HEADER_ENCRYPT_MODE', 'APPLICATION_KEY_MODE',
             'PAYLOAD_ENCRYPT_MODE', 'HIDE_HEADER_MODE' ]
 
@@ -44,7 +44,7 @@
         # Try to read /dev/urandom
         trng(1)
     except MixFatalError:
-	raise
+        raise
     except:
         raise MixFatalError("Error initializing entropy source")
     openssl_seed(40)
@@ -94,12 +94,12 @@
     # may look slow, but it contributes only .7% to the total time for
     # LIONESS.
     right = _ml.aes_ctr128_crypt(
- 	_ml.aes_key(_ml.sha1("".join((key1,left,key1)))[:AES_KEY_LEN]),
- 	right, 0)
+        _ml.aes_key(_ml.sha1("".join((key1,left,key1)))[:AES_KEY_LEN]),
+        right, 0)
     left = _ml.strxor(left,  _ml.sha1("".join((key2,right,key2))))
     right = _ml.aes_ctr128_crypt(
-	_ml.aes_key(_ml.sha1("".join((key3,left,key3)))[:AES_KEY_LEN]),
- 	right, 0)
+        _ml.aes_key(_ml.sha1("".join((key3,left,key3)))[:AES_KEY_LEN]),
+        right, 0)
     left = _ml.strxor(left,  _ml.sha1("".join((key4,right,key4))))
 
     # You could write the above as:
@@ -134,12 +134,12 @@
     # Equivalent-but-faster version:
     left = _ml.strxor(left, _ml.sha1("".join((key4,right,key4))))
     right = _ml.aes_ctr128_crypt(
-	_ml.aes_key(_ml.sha1("".join((key3,left, key3)))[:AES_KEY_LEN]),
-	right, 0)
+        _ml.aes_key(_ml.sha1("".join((key3,left, key3)))[:AES_KEY_LEN]),
+        right, 0)
     left = _ml.strxor(left, _ml.sha1("".join((key2,right,key2))))
     right = _ml.aes_ctr128_crypt(
-	_ml.aes_key(_ml.sha1("".join((key1,left, key1)))[:AES_KEY_LEN]),
-	right, 0)
+        _ml.aes_key(_ml.sha1("".join((key1,left, key1)))[:AES_KEY_LEN]),
+        right, 0)
 
     return left + right
 
@@ -482,29 +482,29 @@
             return res
 
     def shuffle(self, lst, n=None):
-	"""Rearranges the elements of lst so that the first n elements
-	   are randomly chosen from lst.  Returns the first n elements.
-	   (Other elements are still in lst, but may be in a nonrandom
-	   order.)  If n is None, shuffles and returns the entire list"""
+        """Rearranges the elements of lst so that the first n elements
+           are randomly chosen from lst.  Returns the first n elements.
+           (Other elements are still in lst, but may be in a nonrandom
+           order.)  If n is None, shuffles and returns the entire list"""
         size = len(lst)
-	if n is None:
-	    n = size
-	else:
-	    n = min(n, size)
+        if n is None:
+            n = size
+        else:
+            n = min(n, size)
 
-	if n == size:
-	    series = xrange(n-1)
-	else:
-	    series = xrange(n)
+        if n == size:
+            series = xrange(n-1)
+        else:
+            series = xrange(n)
 
         # This permutation algorithm yields all permutation with equal
         # probability (assuming a good rng); others do not.
-	getInt = self.getInt
+        getInt = self.getInt
         for i in series:
             swap = i+getInt(size-i)
-	    lst[swap],lst[i] = lst[i],lst[swap]
+            lst[swap],lst[i] = lst[i],lst[swap]
 
-	return lst[:n]
+        return lst[:n]
 
     def getInt(self, max):
         """Returns a random integer i s.t. 0 <= i < max.
@@ -512,33 +512,33 @@
            The value of max must be less than 2**30."""
 
         # FFFF This implementation is about 2-4x as good as the last one, but
-	# FFFF still could be better.  It's faster than getFloat()*max.
+        # FFFF still could be better.  It's faster than getFloat()*max.
 
-	# FFFF (This code assumes that integers are at least 32 bits. Maybe
-	# FFFF  we could do better.)
+        # FFFF (This code assumes that integers are at least 32 bits. Maybe
+        # FFFF  we could do better.)
 
         assert 0 < max < 0x3fffffff
-	_ord = ord
-	while 1:
-	    # Get a random positive int between 0 and 0x7fffffff.
-	    b = self.getBytes(4)
-	    o = (((((((_ord(b[0])&0x7f)<<8) +
- 		       _ord(b[1]))<<8) +
-	 	       _ord(b[2]))<<8) +
-	               _ord(b[3]))
-	    # Retry if we got a value that would fall in an incomplete
-	    # run of 'max' elements.
-	    if 0x7fffffff - max >= o:
-		return o % max
+        _ord = ord
+        while 1:
+            # Get a random positive int between 0 and 0x7fffffff.
+            b = self.getBytes(4)
+            o = (((((((_ord(b[0])&0x7f)<<8) +
+                       _ord(b[1]))<<8) +
+                       _ord(b[2]))<<8) +
+                       _ord(b[3]))
+            # Retry if we got a value that would fall in an incomplete
+            # run of 'max' elements.
+            if 0x7fffffff - max >= o:
+                return o % max
 
     def getFloat(self):
-	"""Return a floating-point number between 0 and 1."""
-	b = self.getBytes(4)
-	_ord = ord
-	o = ((((((_ord(b[0])&0x7f)<<8) + _ord(b[1]))<<8) +
-	      _ord(b[2]))<<8) + _ord(b[3])
-	#return o / float(0x7fffffff)
-	return o / 2147483647.0
+        """Return a floating-point number between 0 and 1."""
+        b = self.getBytes(4)
+        _ord = ord
+        o = ((((((_ord(b[0])&0x7f)<<8) + _ord(b[1]))<<8) +
+              _ord(b[2]))<<8) + _ord(b[3])
+        #return o / float(0x7fffffff)
+        return o / 2147483647.0
 
     def _prng(self, n):
         """Abstract method: Must be overridden to return n bytes of fresh
@@ -601,42 +601,42 @@
     if config is not None:
         requestedFile = config['Host'].get('EntropySource', None)
     else:
-	requestedFile = None
+        requestedFile = None
 
     # Build a list of candidates
-    defaults = 	PLATFORM_TRNG_DEFAULTS.get(sys.platform,
-			   PLATFORM_TRNG_DEFAULTS['***'])
+    defaults =  PLATFORM_TRNG_DEFAULTS.get(sys.platform,
+                           PLATFORM_TRNG_DEFAULTS['***'])
     files = [ requestedFile ] + defaults
 
     # Now find the first of our candidates that exists and is a character
     # device.
     randFile = None
     for file in files:
-	if file is None:
-	    continue
+        if file is None:
+            continue
 
-	verbose = 1#(file == requestedFile)
-	if not os.path.exists(file):
-	    if verbose:
-		LOG.error("No such file as %s", file)
-	else:
-	    st = os.stat(file)
-	    if not (st[stat.ST_MODE] & stat.S_IFCHR):
-		if verbose:
-		    LOG.error("Entropy source %s isn't a character device",
-				   file)
-	    else:
-		randFile = file
-		break
+        verbose = 1#(file == requestedFile)
+        if not os.path.exists(file):
+            if verbose:
+                LOG.error("No such file as %s", file)
+        else:
+            st = os.stat(file)
+            if not (st[stat.ST_MODE] & stat.S_IFCHR):
+                if verbose:
+                    LOG.error("Entropy source %s isn't a character device",
+                                   file)
+            else:
+                randFile = file
+                break
 
     if randFile is None and _TRNG_FILENAME is None:
         LOG.fatal("No entropy source available")
         raise MixFatalError("No entropy source available")
     elif randFile is None:
         LOG.warn("Falling back to previous entropy source %s",
-		 _TRNG_FILENAME)
+                 _TRNG_FILENAME)
     else:
-	LOG.info("Setting entropy source to %r", randFile)
+        LOG.info("Setting entropy source to %r", randFile)
         _TRNG_FILENAME = randFile
 
 class _TrueRNG(RNG):
@@ -648,13 +648,13 @@
         RNG.__init__(self,n)
     def _prng(self,n):
         "Returns n fresh bytes from our true RNG."
-	if _TRNG_FILENAME is None:
-	    configure_trng(None)
+        if _TRNG_FILENAME is None:
+            configure_trng(None)
 
-	f = open(_TRNG_FILENAME, 'rb')
-	d = f.read(n)
-	f.close()
-	return d
+        f = open(_TRNG_FILENAME, 'rb')
+        d = f.read(n)
+        f.close()
+        return d
 
 # Global _TrueRNG instance, for use by trng().
 _theTrueRNG = _TrueRNG(1024)

Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- MMTPClient.py	12 Dec 2002 19:56:46 -0000	1.13
+++ MMTPClient.py	16 Dec 2002 02:40:11 -0000	1.14
@@ -40,26 +40,26 @@
         self.targetPort = targetPort
         self.targetKeyID = targetKeyID
         self.context = _ml.TLSContext_new()
-	self.tls = None
-	self.sock = None
+        self.tls = None
+        self.sock = None
 
     def connect(self):
         """Negotiate the handshake and protocol."""
-	# Connect to the server
+        # Connect to the server
         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.sock.setblocking(1)
-	LOG.debug("Connecting to %s:%s", self.targetIP, self.targetPort)
+        LOG.debug("Connecting to %s:%s", self.targetIP, self.targetPort)
 
-	# Do the TLS handshaking
+        # Do the TLS handshaking
         self.sock.connect((self.targetIP,self.targetPort))
         LOG.debug("Handshaking with %s:%s",self.targetIP, self.targetPort)
         self.tls = self.context.sock(self.sock.fileno())
         # FFFF session resumption
         self.tls.connect()
-	LOG.debug("Connected.")
+        LOG.debug("Connected.")
 
-	# Check the public key of the server to prevent man-in-the-middle
-	# attacks.
+        # Check the public key of the server to prevent man-in-the-middle
+        # attacks.
         peer_pk = self.tls.get_peer_cert_pk()
         keyID = sha1(peer_pk.encode_key(public=1))
         if self.targetKeyID is not None and (keyID != self.targetKeyID):
@@ -69,43 +69,43 @@
         ####
         # Protocol negotiation
         # For now, we only support 1.0, but we call it 0.1 so we can
-	# change our mind between now and a release candidate, and so we
-	# can obsolete betas come release time.
-	LOG.debug("Negotiatiating MMTP protocol")
+        # change our mind between now and a release candidate, and so we
+        # can obsolete betas come release time.
+        LOG.debug("Negotiatiating MMTP protocol")
         self.tls.write("MMTP 0.1\r\n")
         inp = self.tls.read(len("MMTP 0.1\r\n"))
         if inp != "MMTP 0.1\r\n":
             raise MixProtocolError("Protocol negotiation failed")
-	LOG.debug("MMTP protocol negotated: version 0.1")
+        LOG.debug("MMTP protocol negotated: version 0.1")
 
     def sendPacket(self, packet):
         """Send a single packet to a server."""
         assert len(packet) == 1<<15
-	LOG.debug("Sending packet")
-	##
-	# We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
+        LOG.debug("Sending packet")
+        ##
+        # We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
         self.tls.write("SEND\r\n")
         self.tls.write(packet)
         self.tls.write(sha1(packet+"SEND"))
-	LOG.debug("Packet sent; waiting for ACK")
+        LOG.debug("Packet sent; waiting for ACK")
 
-	# And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
+        # And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
         inp = self.tls.read(len("RECEIVED\r\n")+20)
         if inp != "RECEIVED\r\n"+sha1(packet+"RECEIVED"):
             raise MixProtocolError("Bad ACK received")
-	LOG.debug("ACK received; packet successfully delivered")
+        LOG.debug("ACK received; packet successfully delivered")
 
     # FFFF we need a sendJunkPacket method.
 
     def shutdown(self):
         """Close this connection."""
-	LOG.debug("Shutting down connection to %s:%s",
-		       self.targetIP, self.targetPort)
-	if self.tls is not None:
-	    self.tls.shutdown()
-	if self.sock is not None:
-	    self.sock.close()
-	LOG.debug("Connection closed")
+        LOG.debug("Shutting down connection to %s:%s",
+                       self.targetIP, self.targetPort)
+        if self.tls is not None:
+            self.tls.shutdown()
+        if self.sock is not None:
+            self.sock.close()
+        LOG.debug("Connection closed")
 
 def sendMessages(targetIP, targetPort, targetKeyID, packetList):
     """Sends a list of messages to a server."""

Index: Main.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Main.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- Main.py	15 Dec 2002 03:45:30 -0000	1.14
+++ Main.py	16 Dec 2002 02:40:11 -0000	1.15
@@ -34,14 +34,14 @@
 def filesAreSame(f1, f2):
     "Return true if f1 and f2 are exactly the same file."
     if os.path.normpath(f1) == os.path.normpath(f2):
-	return 1
+        return 1
     try:
         # FFFF what happens on systems that (shudder) lack inodes?
-	ino1 = os.stat(f1)[stat.ST_INO]
-	ino2 = os.stat(f2)[stat.ST_INO]
-	return ino1 and ino1 > 0 and ino1 == ino2
+        ino1 = os.stat(f1)[stat.ST_INO]
+        ino2 = os.stat(f2)[stat.ST_INO]
+        return ino1 and ino1 > 0 and ino1 == ino2
     except OSError:
-	return 0
+        return 0
 
 def correctPath(myself):
     "Given a command (sys.argv[0]), fix sys.path so 'import mixminion' works"
@@ -52,15 +52,15 @@
     # If we can import mixminion.Main, we bail out early: let's not mess
     # with anything.
     try:
-	__import__('mixminion.Main')
-	return
+        __import__('mixminion.Main')
+        return
     except ImportError:
-	pass
+        pass
 
     orig_cmd = myself
     # First, resolve all links.
     while os.path.islink(myself):
-	myself = os.readlink(myself)
+        myself = os.readlink(myself)
 
     # Now, the module ought to be living in x/y/z/mixminon/Foo.py.
     # The "x/y/z" is the part we're interested in.
@@ -68,38 +68,38 @@
     parentdir, miniondir = os.path.split(mydir)
     if not miniondir == 'mixminion':
         sys.stderr.write(("Bad mixminion installation:\n"+
-	 " I resolved %s to %s, but expected to find ../mixminion/Main.py\n")%(
-	     orig_cmd, myself))
+        " I resolved %s to %s, but expected to find ../mixminion/Main.py\n")%(
+            orig_cmd, myself))
 
     # Now we check whether there's already an entry in sys.path.  If not,
     # we add the directory we found.
     parentdir = os.path.normpath(parentdir)
     foundEntry = 0
     for pathEntry in sys.path:
-	# There are intimations on Python-dev that sys.path may eventually
-	# contain non-strings.
-	if not isinstance(pathEntry, types.StringType):
-	    continue
-	if os.path.normpath(pathEntry) == parentdir:
-	    foundEntry = 1; break
+        # There are intimations on Python-dev that sys.path may eventually
+        # contain non-strings.
+        if not isinstance(pathEntry, types.StringType):
+            continue
+        if os.path.normpath(pathEntry) == parentdir:
+            foundEntry = 1; break
 
-	ent = os.path.join(pathEntry, 'mixminion', 'Main.py')
-	if os.path.exists(ent) and filesAreSame(pathEntry, myself):
-	    foundEntry = 1; break
+        ent = os.path.join(pathEntry, 'mixminion', 'Main.py')
+        if os.path.exists(ent) and filesAreSame(pathEntry, myself):
+            foundEntry = 1; break
 
     if not foundEntry:
-	sys.stderr.write("Adding %s to PYTHONPATH\n" % parentdir)
-	sys.path[0:0] = [ parentdir ]
+        sys.stderr.write("Adding %s to PYTHONPATH\n" % parentdir)
+        sys.path[0:0] = [ parentdir ]
 
     # Finally, we make sure it all works.
     try:
         # We use __import__ here instead of 'import' so that we can stay
         #   parseable by Python 1.1.  You're welcome.
-	__import__('mixminion.Main')
+        __import__('mixminion.Main')
     except ImportError, e:
-	sys.stderr.write(str(e)+"\n")
-	sys.stderr.write("Unable to find correct path for mixminion.\n")
-	sys.exit(1)
+        sys.stderr.write(str(e)+"\n")
+        sys.stderr.write("Unable to find correct path for mixminion.\n")
+        sys.exit(1)
 
 # Global map from command name to 2-tuples of (module_name, function_name).
 # The function 'main' below uses this map to pick which module to import,
@@ -140,12 +140,12 @@
 
     # Check whether we have a recognized command.
     if len(args) == 1 or not _COMMANDS.has_key(args[1]):
-	# FFFF we could do better in generating a usage message here.
-	cmds = _COMMANDS.keys()
-	cmds.sort()
-	sys.stderr.write("Usage: %s {%s} [arguments]\n" %(
-	    args[0], "|".join(cmds)))
-	sys.exit(1)
+        # FFFF we could do better in generating a usage message here.
+        cmds = _COMMANDS.keys()
+        cmds.sort()
+        sys.stderr.write("Usage: %s {%s} [arguments]\n" %(
+            args[0], "|".join(cmds)))
+        sys.exit(1)
 
     # Read the module and function.
     command_module, command_fn = _COMMANDS[args[1]]

Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- Packet.py	12 Dec 2002 19:56:46 -0000	1.21
+++ Packet.py	16 Dec 2002 02:40:11 -0000	1.22
@@ -17,10 +17,10 @@
             'parseSMTPInfo', 'parseMBOXInfo', 'ReplyBlock',
             'parseReplyBlock', 'ENC_SUBHEADER_LEN', 'HEADER_LEN',
             'PAYLOAD_LEN', 'MAJOR_NO', 'MINOR_NO', 'SECRET_LEN', 'TAG_LEN',
-	    'SINGLETON_PAYLOAD_OVERHEAD', 'OAEP_OVERHEAD',
-	    'FRAGMENT_PAYLOAD_OVERHEAD', 'ENC_FWD_OVERHEAD',
-	    'DROP_TYPE', 'FWD_TYPE', 'SWAP_FWD_TYPE',
-	    'SMTP_TYPE', 'MBOX_TYPE', 'MIN_EXIT_TYPE'
+            'SINGLETON_PAYLOAD_OVERHEAD', 'OAEP_OVERHEAD',
+            'FRAGMENT_PAYLOAD_OVERHEAD', 'ENC_FWD_OVERHEAD',
+            'DROP_TYPE', 'FWD_TYPE', 'SWAP_FWD_TYPE',
+            'SMTP_TYPE', 'MBOX_TYPE', 'MIN_EXIT_TYPE'
 ]
 
 import struct
@@ -159,7 +159,7 @@
     if rlen < len(ri):
         ri = ri[:rlen]
     if rt >= MIN_EXIT_TYPE and rlen < 20:
-	raise ParseError("Subheader missing tag")
+        raise ParseError("Subheader missing tag")
     return Subheader(major,minor,secret,digest,rt,ri,rlen)
 
 def getTotalBlocksForRoutingInfoLen(bytes):
@@ -203,18 +203,18 @@
                 "routinglen=%(routinglen)r)")% self.__dict__
 
     def getExitAddress(self):
-	"""Return the part of the routingInfo that contains the delivery
-	   address.  (Requires that routingType is an exit type.)"""
-	assert self.routingtype >= MIN_EXIT_TYPE
-	assert len(self.routinginfo) >= TAG_LEN
-	return self.routinginfo[TAG_LEN:]
+        """Return the part of the routingInfo that contains the delivery
+           address.  (Requires that routingType is an exit type.)"""
+        assert self.routingtype >= MIN_EXIT_TYPE
+        assert len(self.routinginfo) >= TAG_LEN
+        return self.routinginfo[TAG_LEN:]
 
     def getTag(self):
-	"""Return the part of the routingInfo that contains the decoding
-	   tag. (Requires that routingType is an exit type.)"""
-	assert self.routingtype >= MIN_EXIT_TYPE
-	assert len(self.routinginfo) >= TAG_LEN
-	return self.routinginfo[:TAG_LEN]
+        """Return the part of the routingInfo that contains the decoding
+           tag. (Requires that routingType is an exit type.)"""
+        assert self.routingtype >= MIN_EXIT_TYPE
+        assert len(self.routinginfo) >= TAG_LEN
+        return self.routinginfo[:TAG_LEN]
 
     def setRoutingInfo(self, info):
         """Change the routinginfo, and the routinglength to correspond."""
@@ -295,25 +295,25 @@
        FragmentPayload object.  Raise ParseError on failure or data
        corruption."""
     if len(payload) not in (PAYLOAD_LEN, PAYLOAD_LEN-ENC_FWD_OVERHEAD):
-	raise ParseError("Payload has bad length")
+        raise ParseError("Payload has bad length")
     bit0 = ord(payload[0]) & 0x80
     if bit0:
-	# We have a fragment
-	idx, hash, msgID, msgLen = struct.unpack(FRAGMENT_UNPACK_PATTERN,
-					 payload[:FRAGMENT_PAYLOAD_OVERHEAD])
-	idx &= 0x7f
-	contents = payload[FRAGMENT_PAYLOAD_OVERHEAD:]
-	if msgLen <= len(contents):
-	    raise ParseError("Payload has an invalid size field")
-	return FragmentPayload(idx,hash,msgID,msgLen,contents)
+        # We have a fragment
+        idx, hash, msgID, msgLen = struct.unpack(FRAGMENT_UNPACK_PATTERN,
+                                         payload[:FRAGMENT_PAYLOAD_OVERHEAD])
+        idx &= 0x7f
+        contents = payload[FRAGMENT_PAYLOAD_OVERHEAD:]
+        if msgLen <= len(contents):
+            raise ParseError("Payload has an invalid size field")
+        return FragmentPayload(idx,hash,msgID,msgLen,contents)
     else:
-	# We have a singleton
-	size, hash = struct.unpack(SINGLETON_UNPACK_PATTERN,
-				   payload[:SINGLETON_PAYLOAD_OVERHEAD])
-	contents = payload[SINGLETON_PAYLOAD_OVERHEAD:]
-	if size > len(contents):
-	    raise ParseError("Payload has invalid size field")
-	return SingletonPayload(size,hash,contents)
+        # We have a singleton
+        size, hash = struct.unpack(SINGLETON_UNPACK_PATTERN,
+                                   payload[:SINGLETON_PAYLOAD_OVERHEAD])
+        contents = payload[SINGLETON_PAYLOAD_OVERHEAD:]
+        if size > len(contents):
+            raise ParseError("Payload has invalid size field")
+        return SingletonPayload(size,hash,contents)
 
 # A singleton payload starts with a 0 bit, 15 bits of size, and a 20-byte hash
 SINGLETON_UNPACK_PATTERN = "!H%ds" % (DIGEST_LEN)
@@ -329,28 +329,28 @@
     """Represents the payload for a standalone mixminion message.
        Fields:  size, hash, data.  (Note that data is padded.)"""
     def __init__(self, size, hash, data):
-	self.size = size
-	self.hash = hash
-	self.data = data
+        self.size = size
+        self.hash = hash
+        self.data = data
 
     def isSingleton(self):
-	"""Returns true; this is a singleton payload."""
-	return 1
+        """Returns true; this is a singleton payload."""
+        return 1
 
     def getContents(self):
-	"""Returns the non-padding portion of this payload's data"""
-	return self.data[:self.size]
+        """Returns the non-padding portion of this payload's data"""
+        return self.data[:self.size]
 
     def pack(self):
-	"""Check for reasonable values of fields, and return a packed payload.
+        """Check for reasonable values of fields, and return a packed payload.
         """
-	assert (0x8000 & self.size) == 0
-	assert 0 <= self.size <= len(self.data)
-	assert len(self.hash) == DIGEST_LEN
-	assert (PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - len(self.data)) in \
-	       (0, ENC_FWD_OVERHEAD)
-	header = struct.pack(SINGLETON_UNPACK_PATTERN, self.size, self.hash)
-	return "%s%s" % (header, self.data)
+        assert (0x8000 & self.size) == 0
+        assert 0 <= self.size <= len(self.data)
+        assert len(self.hash) == DIGEST_LEN
+        assert (PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - len(self.data)) in \
+               (0, ENC_FWD_OVERHEAD)
+        header = struct.pack(SINGLETON_UNPACK_PATTERN, self.size, self.hash)
+        return "%s%s" % (header, self.data)
 
 class FragmentPayload(_Payload):
     """Represents the fields of a decoded fragment payload.
@@ -359,28 +359,28 @@
             or decode them.
     """
     def __init__(self, index, hash, msgID, msgLen, data):
-	self.index = index
-	self.hash = hash
-	self.msgID = msgID
-	self.msgLen = msgLen
-	self.data = data
+        self.index = index
+        self.hash = hash
+        self.msgID = msgID
+        self.msgLen = msgLen
+        self.data = data
 
     def isSingleton(self):
-	"""Return false; not a singleton"""
-	return 0
+        """Return false; not a singleton"""
+        return 0
 
     def pack(self):
-	"""Returns the string value of this payload."""
-	assert 0 <= self.index <= MAX_N_FRAGMENTS
-	assert len(self.hash) == DIGEST_LEN
-	assert len(self.msgID) == FRAGMENT_MESSAGEID_LEN
-	assert len(self.data) < self.msgLen < 0x100000000L
-	assert (PAYLOAD_LEN - FRAGMENT_PAYLOAD_OVERHEAD - len(self.data)) in \
-	       (0, ENC_FWD_OVERHEAD)
-	idx = self.index | 0x8000
-	header = struct.pack(FRAGMENT_UNPACK_PATTERN, idx, self.hash,
-			     self.msgID, self.msgLen)
-	return "%s%s" % (header, self.data)
+        """Returns the string value of this payload."""
+        assert 0 <= self.index <= MAX_N_FRAGMENTS
+        assert len(self.hash) == DIGEST_LEN
+        assert len(self.msgID) == FRAGMENT_MESSAGEID_LEN
+        assert len(self.data) < self.msgLen < 0x100000000L
+        assert (PAYLOAD_LEN - FRAGMENT_PAYLOAD_OVERHEAD - len(self.data)) in \
+               (0, ENC_FWD_OVERHEAD)
+        idx = self.index | 0x8000
+        header = struct.pack(FRAGMENT_UNPACK_PATTERN, idx, self.hash,
+                             self.msgID, self.msgLen)
+        return "%s%s" % (header, self.data)
 
 #----------------------------------------------------------------------
 # REPLY BLOCKS
@@ -408,7 +408,7 @@
 
     if major != 0x00 or minor != 0x01:
         raise ParseError("Unrecognized version on reply block %s.%s",
-			 major,minor)
+                         major,minor)
 
     ri = s[MIN_RB_LEN:]
     if len(ri) != rlen:
@@ -426,14 +426,14 @@
         self.timestamp = useBy
         self.routingType = rt
         self.routingInfo = ri
-	self.encryptionKey = key
+        self.encryptionKey = key
 
     def pack(self):
         """Returns the external representation of this reply block"""
         return struct.pack(RB_UNPACK_PATTERN,
                            "SURB", 0x00, 0x01, self.timestamp, self.header,
                            len(self.routingInfo), self.routingType,
-			   self.encryptionKey) + self.routingInfo
+                           self.encryptionKey) + self.routingInfo
 
 #----------------------------------------------------------------------
 # Routing info
@@ -471,19 +471,19 @@
         """Return the routing info for this address"""
         assert len(self.keyinfo) == DIGEST_LEN
         return struct.pack(IPV4_PAT, inet_aton(self.ip),
-			   self.port, self.keyinfo)
+                           self.port, self.keyinfo)
 
     def __hash__(self):
-	return hash(self.pack())
+        return hash(self.pack())
 
     def __eq__(self, other):
-	return (type(self) == type(other) and self.ip == other.ip and
-		self.port == other.port and self.keyinfo == other.keyinfo)
+        return (type(self) == type(other) and self.ip == other.ip and
+                self.port == other.port and self.keyinfo == other.keyinfo)
 
 def parseSMTPInfo(s):
     """Convert the encoding of an SMTP exitinfo into an SMTPInfo object."""
     if not isSMTPMailbox(s):
-	raise ParseError("Invalid rfc822 mailbox %r" % s)
+        raise ParseError("Invalid rfc822 mailbox %r" % s)
     return SMTPInfo(s)
 
 class SMTPInfo:
@@ -495,7 +495,7 @@
 
     def pack(self):
         """Returns the wire representation of this SMTPInfo"""
-	return self.email
+        return self.email
 
 def parseMBOXInfo(s):
     """Convert the encoding of an MBOX exitinfo into an MBOXInfo address"""
@@ -512,4 +512,4 @@
 
     def pack(self):
         """Return the external representation of this routing info."""
-	return self.user
+        return self.user

Index: ServerInfo.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerInfo.py,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- ServerInfo.py	12 Dec 2002 19:56:46 -0000	1.25
+++ ServerInfo.py	16 Dec 2002 02:40:11 -0000	1.26
@@ -38,121 +38,121 @@
     """A ServerInfo object holds a parsed server descriptor."""
     _restrictFormat = 1
     _syntax = {
-	"Server" : { "__SECTION__": ("REQUIRE", None, None),
+        "Server" : { "__SECTION__": ("REQUIRE", None, None),
                      "Descriptor-Version": ("REQUIRE", None, None),
-		     "IP": ("REQUIRE", C._parseIP, None),
-		     "Nickname": ("REQUIRE", None, None),
-		     "Identity": ("REQUIRE", C._parsePublicKey, None),
-		     "Digest": ("REQUIRE", C._parseBase64, None),
-		     "Signature": ("REQUIRE", C._parseBase64, None),
-		     "Published": ("REQUIRE", C._parseTime, None),
-		     "Valid-After": ("REQUIRE", C._parseDate, None),
-		     "Valid-Until": ("REQUIRE", C._parseDate, None),
-		     "Contact": ("ALLOW", None, None),
-		     "Comments": ("ALLOW", None, None),
-		     "Packet-Key": ("REQUIRE", C._parsePublicKey, None),
-		     },
-	"Incoming/MMTP" : {
- 	             "Version": ("REQUIRE", None, None),
-		     "Port": ("REQUIRE", C._parseInt, None),
-		     "Key-Digest": ("REQUIRE", C._parseBase64, None),
-		     "Protocols": ("REQUIRE", None, None),
+                     "IP": ("REQUIRE", C._parseIP, None),
+                     "Nickname": ("REQUIRE", None, None),
+                     "Identity": ("REQUIRE", C._parsePublicKey, None),
+                     "Digest": ("REQUIRE", C._parseBase64, None),
+                     "Signature": ("REQUIRE", C._parseBase64, None),
+                     "Published": ("REQUIRE", C._parseTime, None),
+                     "Valid-After": ("REQUIRE", C._parseDate, None),
+                     "Valid-Until": ("REQUIRE", C._parseDate, None),
+                     "Contact": ("ALLOW", None, None),
+                     "Comments": ("ALLOW", None, None),
+                     "Packet-Key": ("REQUIRE", C._parsePublicKey, None),
+                     },
+        "Incoming/MMTP" : {
+                     "Version": ("REQUIRE", None, None),
+                     "Port": ("REQUIRE", C._parseInt, None),
+                     "Key-Digest": ("REQUIRE", C._parseBase64, None),
+                     "Protocols": ("REQUIRE", None, None),
                      "Allow": ("ALLOW*", C._parseAddressSet_allow, None),
                      "Deny": ("ALLOW*", C._parseAddressSet_deny, None),
-		     },
-	"Outgoing/MMTP" : {
- 	             "Version": ("REQUIRE", None, None),
-		     "Protocols": ("REQUIRE", None, None),
+                     },
+        "Outgoing/MMTP" : {
+                     "Version": ("REQUIRE", None, None),
+                     "Protocols": ("REQUIRE", None, None),
                      "Allow": ("ALLOW*", C._parseAddressSet_allow, None),
                      "Deny": ("ALLOW*", C._parseAddressSet_deny, None),
-		     },
-	"Delivery/MBOX" : {
-   	             "Version": ("REQUIRE", None, None),
-		     },
-	"Delivery/SMTP" : {
-           	     "Version": ("REQUIRE", None, None),
-		     }
-	}
+                     },
+        "Delivery/MBOX" : {
+                     "Version": ("REQUIRE", None, None),
+                     },
+        "Delivery/SMTP" : {
+                     "Version": ("REQUIRE", None, None),
+                     }
+        }
 
     def __init__(self, fname=None, string=None, assumeValid=0):
-	mixminion.Config._ConfigFile.__init__(self, fname, string, assumeValid)
-	LOG.trace("Reading server descriptor %s from %s",
-		       self['Server']['Nickname'],
-		       fname or "<string>")
+        mixminion.Config._ConfigFile.__init__(self, fname, string, assumeValid)
+        LOG.trace("Reading server descriptor %s from %s",
+                       self['Server']['Nickname'],
+                       fname or "<string>")
 
     def validate(self, sections, entries, lines, contents):
-	####
-	# Check 'Server' section.
-	server = sections['Server']
-	if server['Descriptor-Version'] != '0.1':
-	    raise ConfigError("Unrecognized descriptor version %r",
-			      server['Descriptor-Version'])
-	if len(server['Nickname']) > MAX_NICKNAME:
-	    raise ConfigError("Nickname too long")
-	identityKey = server['Identity']
-	identityBytes = identityKey.get_modulus_bytes()
-	if not (MIN_IDENTITY_BYTES <= identityBytes <= MAX_IDENTITY_BYTES):
-	    raise ConfigError("Invalid length on identity key")
-	if server['Valid-Until'] <= server['Valid-After']:
-	    raise ConfigError("Server is never valid")
-	if server['Contact'] and len(server['Contact']) > MAX_CONTACT:
-	    raise ConfigError("Contact too long")
+        ####
+        # Check 'Server' section.
+        server = sections['Server']
+        if server['Descriptor-Version'] != '0.1':
+            raise ConfigError("Unrecognized descriptor version %r",
+                              server['Descriptor-Version'])
+        if len(server['Nickname']) > MAX_NICKNAME:
+            raise ConfigError("Nickname too long")
+        identityKey = server['Identity']
+        identityBytes = identityKey.get_modulus_bytes()
+        if not (MIN_IDENTITY_BYTES <= identityBytes <= MAX_IDENTITY_BYTES):
+            raise ConfigError("Invalid length on identity key")
+        if server['Valid-Until'] <= server['Valid-After']:
+            raise ConfigError("Server is never valid")
+        if server['Contact'] and len(server['Contact']) > MAX_CONTACT:
+            raise ConfigError("Contact too long")
         if server['Comments'] and len(server['Comments']) > MAX_COMMENTS:
-	    raise ConfigError("Comments too long")
-	packetKeyBytes = server['Packet-Key'].get_modulus_bytes()
-	if packetKeyBytes != PACKET_KEY_BYTES:
-	    raise ConfigError("Invalid length on packet key")
+            raise ConfigError("Comments too long")
+        packetKeyBytes = server['Packet-Key'].get_modulus_bytes()
+        if packetKeyBytes != PACKET_KEY_BYTES:
+            raise ConfigError("Invalid length on packet key")
 
-	####
-	# Check Digest of file
-	digest = getServerInfoDigest(contents)
-	if digest != server['Digest']:
-	    raise ConfigError("Invalid digest")
+        ####
+        # Check Digest of file
+        digest = getServerInfoDigest(contents)
+        if digest != server['Digest']:
+            raise ConfigError("Invalid digest")
 
-	# Check signature
-	if digest != mixminion.Crypto.pk_check_signature(server['Signature'],
-							 identityKey):
-	    raise ConfigError("Invalid signature")
+        # Check signature
+        if digest != mixminion.Crypto.pk_check_signature(server['Signature'],
+                                                         identityKey):
+            raise ConfigError("Invalid signature")
 
-	## Incoming/MMTP section
-	inMMTP = sections['Incoming/MMTP']
-	if inMMTP:
-	    if inMMTP['Version'] != '0.1':
-		raise ConfigError("Unrecognized MMTP descriptor version %s"%
-				  inMMTP['Version'])
-	    if len(inMMTP['Key-Digest']) != DIGEST_LEN:
-		raise ConfigError("Invalid key digest %s"%
-				  formatBase64(inMMTP['Key-Digest']))
+        ## Incoming/MMTP section
+        inMMTP = sections['Incoming/MMTP']
+        if inMMTP:
+            if inMMTP['Version'] != '0.1':
+                raise ConfigError("Unrecognized MMTP descriptor version %s"%
+                                  inMMTP['Version'])
+            if len(inMMTP['Key-Digest']) != DIGEST_LEN:
+                raise ConfigError("Invalid key digest %s"%
+                                  formatBase64(inMMTP['Key-Digest']))
 
-	## Outgoing/MMTP section
-	outMMTP = sections['Outgoing/MMTP']
-	if outMMTP:
-	    if outMMTP['Version'] != '0.1':
-		raise ConfigError("Unrecognized MMTP descriptor version %s"%
-				  inMMTP['Version'])
+        ## Outgoing/MMTP section
+        outMMTP = sections['Outgoing/MMTP']
+        if outMMTP:
+            if outMMTP['Version'] != '0.1':
+                raise ConfigError("Unrecognized MMTP descriptor version %s"%
+                                  inMMTP['Version'])
 
-	# FFFF When a better client module system exists, check the
-	# FFFF module descriptors.
+        # FFFF When a better client module system exists, check the
+        # FFFF module descriptors.
 
     def getNickname(self):
-	"""Returns this server's nickname"""
-	return self['Server']['Nickname']
+        """Returns this server's nickname"""
+        return self['Server']['Nickname']
 
     def getAddr(self):
-	"""Returns this server's IP address"""
-	return self['Server']['IP']
+        """Returns this server's IP address"""
+        return self['Server']['IP']
 
     def getPort(self):
-	"""Returns this server's IP port"""
-	return self['Incoming/MMTP']['Port']
+        """Returns this server's IP port"""
+        return self['Incoming/MMTP']['Port']
 
     def getPacketKey(self):
-	"""Returns the RSA key this server uses to decrypt messages"""
-	return self['Server']['Packet-Key']
+        """Returns the RSA key this server uses to decrypt messages"""
+        return self['Server']['Packet-Key']
 
     def getKeyID(self):
-	"""Returns a hash of this server's MMTP key"""
-	return self['Incoming/MMTP']['Key-Digest']
+        """Returns a hash of this server's MMTP key"""
+        return self['Incoming/MMTP']['Key-Digest']
 
     def getRoutingInfo(self):
         """Returns a mixminion.Packet.IPV4Info object for routing messages
@@ -184,11 +184,11 @@
     signatureLine = None
     infoLines = info.split("\n")
     for lineNo in range(len(infoLines)):
-	line = infoLines[lineNo]
-	if line.startswith("Digest:") and digestLine is None:
-	    digestLine = lineNo
-	elif line.startswith("Signature:") and signatureLine is None:
-	    signatureLine = lineNo
+        line = infoLines[lineNo]
+        if line.startswith("Digest:") and digestLine is None:
+            digestLine = lineNo
+        elif line.startswith("Signature:") and signatureLine is None:
+            signatureLine = lineNo
 
     assert digestLine is not None and signatureLine is not None
 
@@ -199,7 +199,7 @@
     digest = mixminion.Crypto.sha1(info)
 
     if rsa is None:
-	return digest
+        return digest
     # If we got an RSA key, we need to add the digest and signature.
 
     signature = mixminion.Crypto.pk_sign(digest,rsa)

Index: benchmark.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/benchmark.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- benchmark.py	12 Dec 2002 19:56:46 -0000	1.18
+++ benchmark.py	16 Dec 2002 02:40:11 -0000	1.19
@@ -168,11 +168,11 @@
     L10 = [ "x" ] * 10
     L1000 = [ "x" ] * 1000
     print "aesprng.shuffle (10/10)", \
-	  timeit((lambda c=c,L=L10: c.shuffle(L)), 1000)
+          timeit((lambda c=c,L=L10: c.shuffle(L)), 1000)
     print "aesprng.shuffle (1000/1000)", \
-	  timeit((lambda c=c,L=L1000: c.shuffle(L)), 30)
+          timeit((lambda c=c,L=L1000: c.shuffle(L)), 30)
     print "aesprng.shuffle (10/1000)", \
-	  timeit((lambda c=c,L=L1000: c.shuffle(L,10)), 1000)
+          timeit((lambda c=c,L=L1000: c.shuffle(L,10)), 1000)
 
     lkey = Keyset("keymaterial foo bar baz").getLionessKeys("T")
     print "lioness E (1K)", timeit((
@@ -265,7 +265,7 @@
 
     for (bits,it) in ((1536,15), (2048,10),(4096,10)):
         t = time()
-	print "[generating key...]"
+        print "[generating key...]"
         rsa2 = pk_generate(bits)
         t = time()-t
         print "RSA genrate (%d bit)"%bits, timestr(t)
@@ -300,7 +300,7 @@
 
     print "Testing hash log (%s entries)"%load
     if load > 20000:
-	print "This may take a few minutes..."
+        print "This may take a few minutes..."
     h = HashLog(fname, "A")
     hashes = [ prng.getBytes(20) for _ in xrange(load) ]
 
@@ -508,9 +508,9 @@
 
     os.mkdir(dname)
     for i in xrange(200):
-	f = open(os.path.join(dname, str(i)), 'wb')
-	f.write(s32K)
-	f.close()
+        f = open(os.path.join(dname, str(i)), 'wb')
+        f.write(s32K)
+        f.close()
     lst = [os.path.join(dname,str(i)) for i in range(100) ]
     t1 = time()
     secureDelete(lst)
@@ -524,7 +524,7 @@
     lst = [ os.path.join(dname,str(i)) for i in range(100,200) ]
     t1 = time()
     for file in lst:
-	secureDelete(file)
+        secureDelete(file)
     t = time()-t1
     print "secureDelete (1)", timestr(t/100)
 
@@ -540,36 +540,36 @@
     keytxt="a"*16
     key = _ml.aes_key(keytxt)
     while 1:
-	_ml.sha1(s20k)
-	_ml.aes_ctr128_crypt(key,s20k,0)
-	_ml.aes_ctr128_crypt(key,s20k,2000)
-	_ml.aes_ctr128_crypt(key,"",2000,20000)
-	_ml.aes_ctr128_crypt(key,"",0,20000)
-	_ml.aes_ctr128_crypt(key,s20k,0,2000)
-	try:
-	    _ml.aes_ctr128_crypt("abc",s20k,0,2000)
-	except:
-	    pass
-	_ml.strxor(s20k,s20k)
-	try:
-	    _ml.strxor(s20k,keytxt)
-	except:
-	    pass
-	_ml.openssl_seed(s20k)
-	r = _ml.add_oaep_padding("Hello",OAEP_PARAMETER,128)
-	_ml.check_oaep_padding(r,OAEP_PARAMETER,128)
-	try:
-	    _ml.check_oaep_padding("hello",OAEP_PARAMETER,128)
-	except:
-	    pass
-	try:
-	    _ml.add_oaep_padding(s20k,OAEP_PARAMETER,128)
-	except:
-	    pass
-	try:
-	    _ml.add_oaep_padding("a"*127,OAEP_PARAMETER,128)
-	except:
-	    pass
+        _ml.sha1(s20k)
+        _ml.aes_ctr128_crypt(key,s20k,0)
+        _ml.aes_ctr128_crypt(key,s20k,2000)
+        _ml.aes_ctr128_crypt(key,"",2000,20000)
+        _ml.aes_ctr128_crypt(key,"",0,20000)
+        _ml.aes_ctr128_crypt(key,s20k,0,2000)
+        try:
+            _ml.aes_ctr128_crypt("abc",s20k,0,2000)
+        except:
+            pass
+        _ml.strxor(s20k,s20k)
+        try:
+            _ml.strxor(s20k,keytxt)
+        except:
+            pass
+        _ml.openssl_seed(s20k)
+        r = _ml.add_oaep_padding("Hello",OAEP_PARAMETER,128)
+        _ml.check_oaep_padding(r,OAEP_PARAMETER,128)
+        try:
+            _ml.check_oaep_padding("hello",OAEP_PARAMETER,128)
+        except:
+            pass
+        try:
+            _ml.add_oaep_padding(s20k,OAEP_PARAMETER,128)
+        except:
+            pass
+        try:
+            _ml.add_oaep_padding("a"*127,OAEP_PARAMETER,128)
+        except:
+            pass
 
 def testLeaks2():
     print "Trying to leak (rsa)"

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -d -r1.48 -r1.49
--- test.py	15 Dec 2002 04:35:54 -0000	1.48
+++ test.py	16 Dec 2002 02:40:11 -0000	1.49
@@ -88,11 +88,11 @@
     """Helper function.  Returns the first index i for which s1[i] != s2[i],
        or -1 s1 == s2."""
     if s1 == s2:
-	return -1
+        return -1
     last = min(len(s1), len(s2))
     for i in xrange(last):
-	if s1[i] != s2[i]:
-	    return i
+        if s1[i] != s2[i]:
+            return i
[...3879 lines suppressed...]
-		m = re.search(r"^Digest: (\S+)\n", s, re.M)
-		assert m
-		ds.append(base64.decodestring(m.group(1)))
-	    else:
-		ds.append(s['Server']['Digest'])
-	return ds[0] == ds[1]
+        """s1 and s2 are either ServerInfo objects or strings containing server
+           descriptors. Returns 1 iff their digest fields match"""
+        ds = []
+        for s in s1, s2:
+            if type(s) == types.StringType:
+                m = re.search(r"^Digest: (\S+)\n", s, re.M)
+                assert m
+                ds.append(base64.decodestring(m.group(1)))
+            else:
+                ds.append(s['Server']['Digest'])
+        return ds[0] == ds[1]
 
 #----------------------------------------------------------------------
 def testSuite():

Index: testSupport.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/testSupport.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- testSupport.py	12 Dec 2002 19:56:46 -0000	1.11
+++ testSupport.py	16 Dec 2002 02:40:11 -0000	1.12
@@ -41,70 +41,70 @@
     #    all existing numbers.
     # next -- the number of the next file.
     def getConfigSyntax(self):
-	return { 'Testing/DirectoryDump':
-		 { 'Location' : ('REQUIRE', None, None),
-		   'UseQueue': ('REQUIRE', _parseBoolean, None) } }
+        return { 'Testing/DirectoryDump':
+                 { 'Location' : ('REQUIRE', None, None),
+                   'UseQueue': ('REQUIRE', _parseBoolean, None) } }
 
     def validateConfig(self, sections, entries, lines, contents):
-	# loc = sections['Testing/DirectoryDump'].get('Location')
-	pass
+        # loc = sections['Testing/DirectoryDump'].get('Location')
+        pass
 
     def configure(self, config, manager):
-	self.loc = config['Testing/DirectoryDump'].get('Location')
-	if not self.loc:
-	    return
-	self.useQueue = config['Testing/DirectoryDump']['UseQueue']
-	manager.enableModule(self)
+        self.loc = config['Testing/DirectoryDump'].get('Location')
+        if not self.loc:
+            return
+        self.useQueue = config['Testing/DirectoryDump']['UseQueue']
+        manager.enableModule(self)
 
-	if not os.path.exists(self.loc):
-	    createPrivateDir(self.loc)
+        if not os.path.exists(self.loc):
+            createPrivateDir(self.loc)
 
-	max = -1
-	for f in os.listdir(self.loc):
-	    if int(f) > max:
-		max = int(f)
-	self.next = max+1
+        max = -1
+        for f in os.listdir(self.loc):
+            if int(f) > max:
+                max = int(f)
+        self.next = max+1
 
     def getServerInfoBlock(self):
-	return ""
+        return ""
 
     def getName(self):
-	return "Testing_DirectoryDump"
+        return "Testing_DirectoryDump"
 
     def getExitTypes(self):
-	return [ 0xFFFE ]
+        return [ 0xFFFE ]
 
     def createDeliveryQueue(self, queueDir):
-	if self.useQueue:
-	    return SimpleModuleDeliveryQueue(self, queueDir)
-	else:
-	    return ImmediateDeliveryQueue(self)
+        if self.useQueue:
+            return SimpleModuleDeliveryQueue(self, queueDir)
+        else:
+            return ImmediateDeliveryQueue(self)
 
     def processMessage(self, message, tag, exitType, exitInfo):
-	assert exitType == 0xFFFE
+        assert exitType == 0xFFFE
 
-	if exitInfo == 'fail':
-	    return DELIVER_FAIL_RETRY
-	elif exitInfo == 'FAIL!':
-	    return DELIVER_FAIL_NORETRY
+        if exitInfo == 'fail':
+            return DELIVER_FAIL_RETRY
+        elif exitInfo == 'FAIL!':
+            return DELIVER_FAIL_NORETRY
 
-	LOG.debug("Delivering test message")
+        LOG.debug("Delivering test message")
 
-	m = _escapeMessageForEmail(message, tag)
-	if m is None:
-	    # Ordinarily, we'd drop corrupt messages, but this module is
-	    # meant for debugging.
-	    m = """\
+        m = _escapeMessageForEmail(message, tag)
+        if m is None:
+            # Ordinarily, we'd drop corrupt messages, but this module is
+            # meant for debugging.
+            m = """\
 ==========CORRUPT OR UNDECODABLE MESSAGE
 Decoding handle: %s%s==========MESSAGE ENDS""" % (
                       base64.encodestring(tag),
                       base64.encodestring(message))
 
-	f = open(os.path.join(self.loc, str(self.next)), 'w')
-	self.next += 1
-	f.write(m)
-	f.close()
-	return DELIVER_OK
+        f = open(os.path.join(self.loc, str(self.next)), 'w')
+        self.next += 1
+        f.write(m)
+        f.close()
+        return DELIVER_OK
 
 #----------------------------------------------------------------------
 # mix_mktemp: A secure, paranoid mktemp replacement.  (May be overkill
@@ -127,12 +127,12 @@
     global _MM_TESTING_TEMPDIR
     global _MM_TESTING_TEMPDIR_COUNTER
     if _MM_TESTING_TEMPDIR is None:
-	# We haven't configured our temporary directory yet.
-	import tempfile
+        # We haven't configured our temporary directory yet.
+        import tempfile
         paranoia = _MM_TESTING_TEMPDIR_PARANOIA
 
-	# If tempfile.mkdtemp exists, use it.  This avoids warnings, and
-	# is harder for people to exploit.
+        # If tempfile.mkdtemp exists, use it.  This avoids warnings, and
+        # is harder for people to exploit.
         if hasattr(tempfile, 'mkdtemp'):
             try:
                 temp = tempfile.mkdtemp()
@@ -140,8 +140,8 @@
                 print "mkdtemp failure: %s" % e
                 sys.exit(1)
         else:
-	# Otherwise, pick a dirname, make sure it doesn't exist, and try to
-	# create it.
+        # Otherwise, pick a dirname, make sure it doesn't exist, and try to
+        # create it.
             temp = tempfile.mktemp()
             if paranoia and os.path.exists(temp):
                 print "I think somebody's trying to exploit mktemp."
@@ -152,49 +152,49 @@
                 print "Something's up with mktemp: %s" % e
                 sys.exit(1)
 
-	# The directory must exist....
-	if not os.path.exists(temp):
-	    print "Couldn't create temp dir %r" %temp
-	    sys.exit(1)
-	st = os.stat(temp)
-	if paranoia:
-	    # And be writeable only by us...
-	    if st[stat.ST_MODE] & 077:
-		print "Couldn't make temp dir %r with secure permissions" %temp
-		sys.exit(1)
-	    # And be owned by us...
-	    if st[stat.ST_UID] != os.getuid():
-		print "The wrong user owns temp dir %r"%temp
-		sys.exit(1)
-	    parent = temp
-	    # And if, and all of its parents, must not be group-writeable
-	    # unless their sticky bit is set, and must not be owned by
-	    # anybody except us and root.
-	    while 1:
-		p = os.path.split(parent)[0]
-		if parent == p:
-		    break
-		parent = p
-		st = os.stat(parent)
-		m = st[stat.ST_MODE]
-		if m & 02 and not (m & stat.S_ISVTX):
-		    print "Directory %s has fishy permissions %o" %(parent,m)
-		    sys.exit(1)
-		if st[stat.ST_UID] not in (0, os.getuid()):
-		    print "Directory %s has bad owner %s" % (parent,
-							     st[stat.ST_UID])
-		    sys.exit(1)
+        # The directory must exist....
+        if not os.path.exists(temp):
+            print "Couldn't create temp dir %r" %temp
+            sys.exit(1)
+        st = os.stat(temp)
+        if paranoia:
+            # And be writeable only by us...
+            if st[stat.ST_MODE] & 077:
+                print "Couldn't make temp dir %r with secure permissions" %temp
+                sys.exit(1)
+            # And be owned by us...
+            if st[stat.ST_UID] != os.getuid():
+                print "The wrong user owns temp dir %r"%temp
+                sys.exit(1)
+            parent = temp
+            # And if, and all of its parents, must not be group-writeable
+            # unless their sticky bit is set, and must not be owned by
+            # anybody except us and root.
+            while 1:
+                p = os.path.split(parent)[0]
+                if parent == p:
+                    break
+                parent = p
+                st = os.stat(parent)
+                m = st[stat.ST_MODE]
+                if m & 02 and not (m & stat.S_ISVTX):
+                    print "Directory %s has fishy permissions %o" %(parent,m)
+                    sys.exit(1)
+                if st[stat.ST_UID] not in (0, os.getuid()):
+                    print "Directory %s has bad owner %s" % (parent,
+                                                             st[stat.ST_UID])
+                    sys.exit(1)
 
-	_MM_TESTING_TEMPDIR = temp
-	if _MM_TESTING_TEMPDIR_REMOVE_ON_EXIT:
-	    import atexit
-	    atexit.register(deltree, temp)
+        _MM_TESTING_TEMPDIR = temp
+        if _MM_TESTING_TEMPDIR_REMOVE_ON_EXIT:
+            import atexit
+            atexit.register(deltree, temp)
 
     # So now we have a temporary directory; return the name of a new
     # file there.
     _MM_TESTING_TEMPDIR_COUNTER += 1
     return os.path.join(_MM_TESTING_TEMPDIR,
-			"tmp%05d%s" % (_MM_TESTING_TEMPDIR_COUNTER,extra))
+                        "tmp%05d%s" % (_MM_TESTING_TEMPDIR_COUNTER,extra))
 
 _WAIT_FOR_KIDS = 1
 def deltree(*dirs):
@@ -202,17 +202,17 @@
        contents."""
     global _WAIT_FOR_KIDS
     if _WAIT_FOR_KIDS:
-	print "Waiting for shred processes to finish."
-	waitForChildren()
-	_WAIT_FOR_KIDS = 0
+        print "Waiting for shred processes to finish."
+        waitForChildren()
+        _WAIT_FOR_KIDS = 0
     for d in dirs:
         if os.path.isdir(d):
             for fn in os.listdir(d):
-		loc = os.path.join(d,fn)
-		if os.path.isdir(loc):
-		    deltree(loc)
-		else:
-		    os.unlink(loc)
+                loc = os.path.join(d,fn)
+                if os.path.isdir(loc):
+                    deltree(loc)
+                else:
+                    os.unlink(loc)
             os.rmdir(d)
         elif os.path.exists(d):
             os.unlink(d)
@@ -224,7 +224,7 @@
     """Temporarily suppress logging output"""
     log = LOG
     if hasattr(log, '_storedHandlers'):
-	resumeLog()
+        resumeLog()
     buf = cStringIO.StringIO()
     h = mixminion.Common._ConsoleLogHandler(buf)
     log._storedHandlers = log.handlers
@@ -232,7 +232,7 @@
     log._testBuf = buf
     log.handlers = []
     if severity is not None:
-	log.setMinSeverity(severity)
+        log.setMinSeverity(severity)
     log.addHandler(h)
 
 def resumeLog():
@@ -240,7 +240,7 @@
        suspend."""
     log = LOG
     if not hasattr(log, '_storedHandlers'):
-	return None
+        return None
     buf = log._testBuf
     del log._testBuf
     log.handlers = log._storedHandlers
@@ -259,9 +259,9 @@
     """Temporarily replace <object.attribute> with value.  When
        undoReplacedAttributes() is called, the old value is restored."""
     if hasattr(object, attribute):
-	tup = (object, attribute, getattr(object, attribute))
+        tup = (object, attribute, getattr(object, attribute))
     else:
-	tup = (object, attribute)
+        tup = (object, attribute)
     _REPLACED_OBJECT_STACK.append(tup)
     setattr(object, attribute, value)
 
@@ -273,14 +273,14 @@
     """Helper object: callable stub that logs its invocations to _CALL_LOG
        and delegates to an internal function."""
     def __init__(self, name, fn=None):
-	self.name = name
-	self.fn = fn
+        self.name = name
+        self.fn = fn
     def __call__(self, *args, **kwargs):
-	_CALL_LOG.append((self.name, args, kwargs))
-	if self.fn:
-	    return self.fn(*args, **kwargs)
-	else:
-	    return None
+        _CALL_LOG.append((self.name, args, kwargs))
+        if self.fn:
+            return self.fn(*args, **kwargs)
+        else:
+            return None
 
 def replaceFunction(object, attribute, fn=None):
     """Temporarily replace the function or method <object.attribute>.
@@ -305,12 +305,12 @@
     r.reverse()
     del _REPLACED_OBJECT_STACK[:]
     for item in r:
-	if len(item) == 2:
-	    o,a = item
-	    delattr(o,a)
-	else:
-	    o,a,v = item
-	    setattr(o,a,v)
+        if len(item) == 2:
+            o,a = item
+            delattr(o,a)
+        else:
+            o,a,v = item
+            setattr(o,a,v)
 
 #----------------------------------------------------------------------
 # Long keypairs: stored here to avoid regenerating them every time we need