[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Finish implementing 0.0.1 client CLI; debug.
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv8695/lib/mixminion
Modified Files:
ClientMain.py
Log Message:
Finish implementing 0.0.1 client CLI; debug.
ClientMain changes
- Implement CLI
- Implement 'parseAddress'
- Simplify and debug getPath, getServerInfo
- Debug MixminionClient
- Add stopgap code to make sure servers support the requested
delivery methods.
- Remove obsolete 'sendTestMessage' function
- Clarify XXXX comments
- Normalize whitespace
Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- ClientMain.py 22 Nov 2002 21:05:22 -0000 1.7
+++ ClientMain.py 2 Dec 2002 03:19:44 -0000 1.8
@@ -5,11 +5,12 @@
Code for Mixminion command-line client.
- XXXX THIS ISN'T IMPLEMENTED YET! This file is just a placeholder that
- XXXX routes a testing message through a few servers so I can make sure that
- XXXX at least one configuration works.
+ NOTE: THIS IS NOT THE FINAL VERSION OF THE CODE. It needs to
+ support replies and end-to-end encryption. It also needs to
+ support directories.
"""
+# (NOTE: The stuff in the next comment isn't implemented yet.)
# The client needs to store:
# - config
# - keys for pending SURBs
@@ -18,7 +19,7 @@
# info.)
# (Don't *name* files in dir; or at least, don't make their names
# magic. Files can be: ServerInfos, ServerDirectories, or 'fake'
-# directories. Each server can have any number of virtual or
+# directories. Each server can have any number of virtual or
# official tags. Users should use the CLI to add/remove entries from
# dir.)
# - Per-system directory location is a neat idea, but individual users
@@ -28,6 +29,7 @@
import getopt
import sys
import time
+import types
from mixminion.Common import getLog, floorDiv, createPrivateDir, MixError, \
MixFatalError
@@ -36,46 +38,51 @@
import mixminion.MMTPClient
import mixminion.Modules
from mixminion.ServerInfo import ServerInfo
-from mixminion.Config import ClientConfig
+from mixminion.Config import ClientConfig, ConfigError
+from mixminion.Packet import ParseError, parseMBOXInfo, parseSMTPInfo
+from mixminion.Modules import MBOX_TYPE, SMTP_TYPE, DROP_TYPE
-class TrivialDirectoryCache:
- def __init__(self, directory):
+class TrivialKeystore:
+ '''This is a temporary keystore implementation until we get a working
+ directory server implementation.'''
+ def __init__(self, directory, now=None):
self.directory = directory
createPrivateDir(directory)
self.byNickname = {}
self.byFilename = {}
+ if now is None:
+ now = time.time()
+
for f in os.listdir(self.directory):
p = os.path.join(self.directory, f)
try:
info = ServerInfo(fname=p, assumeValid=0)
- except ConfigError, e:
+ except ConfigError, _:
getLog().warn("Invalid server descriptor %s", p)
continue
serverSection = info['Server']
nickname = serverSection['Nickname']
-
+
if '.' in f:
f = f[:f.rindex('.')]
-
- now = time.time()
- if now < serverSec['Valid-After']:
- getLog().warn("Ignoring future decriptor %s", p)
+ if now < serverSection['Valid-After']:
+ getLog().info("Ignoring future decriptor %s", p)
continue
- if now >= serverSec['Valid-Until']:
- getLog().warn("Ignoring expired decriptor %s", p)
+ if now >= serverSection['Valid-Until']:
+ getLog().info("Ignoring expired decriptor %s", p)
continue
- if now + 3*60*60 >= serverSec['Valid-Until']:
- getLog().warn("Ignoring soon-to-expire decriptor %s", p)
+ if now + 3*60*60 >= serverSection['Valid-Until']:
+ getLog().info("Ignoring soon-to-expire decriptor %s", p)
continue
if self.byNickname.has_key(nickname):
getLog().warn(
"Ignoring descriptor %s with duplicate nickname %s",
p, nickname)
continue
- if self.byFilename.has_key(fname):
+ if self.byFilename.has_key(f):
getLog().warn(
"Ignoring descriptor %s with duplicate prefix %s",
p, f)
@@ -87,18 +94,18 @@
if isinstance(name, ServerInfo):
return name
if self.byNickname.has_key(name):
- return self.byNickname(name)
+ return self.byNickname[name]
if self.byFilename.has_key(name):
- return self.byFilename(name)
+ return self.byFilename[name]
return None
- def getPath(self, minLength, serverList):
+ def getPath(self, serverList):
path = []
for s in serverList:
if isinstance(s, ServerInfo):
path.append(s)
elif isinstance(s, types.StringType):
- server = self.dirCache.getServer(s)
+ server = self.getServerInfo(s)
if server is not None:
path.append(server)
elif os.path.exists(s):
@@ -106,16 +113,13 @@
server = ServerInfo(fname=s, assumeValid=0)
path.append(server)
except OSError, e:
- getLog().error("Couldn't read descriptor %s: %s",
- s, e)
- sys.exit(1)
+ raise MixError("Couldn't read descriptor %s: %s" %
+ (s, e))
except ConfigError, e:
- getLog().error("Couldn't parse descriptor %s: %s",
- s, e)
- sys.exit(1)
+ raise MixError("Couldn't parse descriptor %s: %s" %
+ (s, e))
else:
- getLog().error("Couldn't find descriptor %s")
- sys.exit(1)
+ raise MixError("Couldn't find descriptor %s" % s)
return path
def getRandomServers(self, prng, n):
@@ -123,13 +127,13 @@
if len(vals) < n:
raise MixFatalError("Not enough servers (%s requested)", n)
return prng.shuffle(vals, n)
-
+
def installDefaultConfig(fname):
"""Create a default, 'fail-safe' configuration in a given file"""
getLog().warn("No configuration file found. Installing default file in %s",
fname)
f = open(os.path.expanduser(fname), 'w')
- f.write("""\
+ f.write("""\
# This file contains your options for the mixminion client.
[Host]
## Use this option to specify a 'secure remove' command.
@@ -156,29 +160,17 @@
f.close()
class MixminionClient:
- def __init__(self, conf=None):
- if conf is None:
- conf = os.environ.get("MINIONRC", None)
- if conf is None:
- conf = "~/.minionrc"
- if not os.path.exists(conf):
- installDefaultConfig(conf)
- conf = os.path.expanduser(conf)
- self.config = ClientConfig(fname=conf)
-
- getLog().configure(self.config)
- getLog().debug("Configuring client")
- mixminion.Common.configureShredCommand(self.config)
- mixminion.Crypto.init_crypto(self.config)
+ def __init__(self, conf):
+ self.config = conf
# Make directories
- userdir = self.config['User']['UserDir']
+ 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.dirCache = TrivialDirectoryCache(
+ self.keystore = TrivialKeystore(
os.path.join(userdir,"servers"))
# Initialize PRNG
@@ -191,13 +183,23 @@
self.sendMessages([message], firstHop)
def generateForwardMessage(self, address, payload, path1, path2):
- servers1 = self.dirCache.getPath(path1)
- servers2 = self.dirCache.getPath(path2)
- # XXXXencode payloadXXXX
-
+ servers1 = self.keystore.getPath(path1)
+ servers2 = self.keystore.getPath(path2)
+
routingType, routingInfo, lastHop = address.getRouting()
- if lastHop != None:
- servers2.append(self.dirCache.getServerInfo(lastHop))
+ if lastHop is None:
+ # FFFF This is only a temporary solution. It needs to get
+ # FFFF rethought, or refactored into ServerInfo, or something.
+ if routingType == SMTP_TYPE:
+ ok = path2[-1]['Delivery/SMTP'].get('Version',None)
+ if not ok:
+ raise MixError("Last hop doesn't support SMTP")
+ elif routingType == MBOX_TYPE:
+ ok = path2[-1]['Delivery/MBOX'].get('Version',None)
+ if not ok:
+ raise MixError("Last hop doesn't support MBOX")
+ else:
+ servers2.append(self.keystore.getServerInfo(lastHop))
msg = buildForwardMessage(payload,
routingType, routingInfo,
servers1, servers2)
@@ -214,7 +216,43 @@
finally:
con.shutdown()
-def parseAddress():
+def parseAddress(s):
+ """DOCDOC
+ format is mbox:name@server OR [smtp:]mailbox OR drop OR test:rinfo
+ or 0xABCD:address """
+ # DOCDOC
+ # ???? Should this should get refactored into clientmodules, or someplace?
+ if s.lower() == 'drop':
+ return Address(DROP_TYPE, None, None)
+ elif s.lower() == 'test':
+ return Address(0xFFFE, "", None)
+ elif ':' not in s:
+ try:
+ return Address(SMTP_TYPE, parseSMTPInfo(s).pack(), None)
+ except ParseError, _:
+ 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)
+ elif tp == 'mbox':
+ if "@" not in val:
+ raise ParseError("No server for mailbox %s" % s)
+ mbox, server = val.split("@",1)
+ return Address(MBOX_TYPE, parseMBOXInfo(mbox).pack(), server)
+ elif tp == 'smtp':
+ # May raise ParseError
+ return Address(SMTP_TYPE, parseSMTPInfo(val).pack(), None)
+ elif tp == 'test':
+ return Address(0xFFFE, val, None)
+ else:
+ raise ParseError("Unrecognized address type: %s"%s)
class Address:
def __init__(self, exitType, exitAddress, lastHop=None):
@@ -224,26 +262,6 @@
def getRouting(self):
return self.exitType, self.exitAddress, self.lastHop
-def sendTestMessage(servers1, servers2):
- assert len(servers1)
- assert len(servers2)
- payload = """
- Insert
- Example
- Message
- Here.
- """
- rt, ri = 0xFFFE, "deliver"
- m = buildForwardMessage(payload, rt, ri, servers1, servers2)
-
- firstHop = servers1[0]
- b = mixminion.MMTPClient.BlockingClientConnection(firstHop.getAddr(),
- firstHop.getPort(),
- firstHop.getKeyID())
- b.connect()
- b.sendPacket(m)
- b.shutdown()
-
def readConfigFile(configFile):
try:
return ClientConfig(fname=configFile)
@@ -251,31 +269,84 @@
print >>sys.stderr, "Error reading configuration file %r:"%configFile
print >>sys.stderr, " ", str(e)
sys.exit(1)
- except mixminion.Config.ConfigError, e:
+ except ConfigError, e:
print >>sys.stderr, "Error in configuration file %r"%configFile
print >>sys.stderr, str(e)
sys.exit(1)
+ return None #suppress pychecker warning
-# XXXX This isn't anything LIKE the final client interface: for now, I'm
-# XXXX just testing the server.
+# 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, "hf:", ["help", "config="])
- configFile = '~/.mixminion/mixminion.conf'
+ options, args = getopt.getopt(args, "hvf:i:t:",
+ ["help", "verbose", "config=", "input=",
+ "path1=", "path2=", "to="])
+ configFile = '~/.mixminionrc'
usage = 0
+ inFile = "-"
+ verbose = 0
+ path1 = []
+ 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 args:
+ print >>sys.stderr, "Unexpected options."
+ usage = 1
+ if not path1:
+ 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
+ if address is None:
+ print >>sys.stderr, "No recipient specified"
+ usage = 1
if usage:
- print >>sys.stderr, "Usage: %s [-h] [-f configfile] server1 server2..."%cmd
+ print >>sys.stderr, """\
+Usage: %s [-h] [-v] [-f configfile] [-i inputfile]
+ [--path1=server1,server2,...]
+ [--path2=server1,server2,...] [-t <address>]"""%cmd
sys.exit(1)
- config = readConfigFile(os.path.expanduser(configFile))
+ if configFile is None:
+ 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)
+ config = readConfigFile(configFile)
+
+ getLog().configure(config)
+ if verbose:
+ getLog().setMinSeverity("DEBUG")
+
+ getLog().debug("Configuring client")
+ mixminion.Common.configureShredCommand(config)
mixminion.Crypto.init_crypto(config)
- if len(args) < 2:
- print >> sys.stderr, "I need at least 2 servers"
- servers = [ ServerInfo(fn) for fn in args ]
- idx = floorDiv(len(servers),2)
- sendTestMessage(servers[:idx], servers[idx:])
+ client = MixminionClient(config)
+
+ if inFile == '-':
+ f = sys.stdin
+ else:
+ f = open(inFile, 'r')
+ payload = f.read()
+ f.close()
+
+ client.sendForwardMessage(address, payload, path1, path2)