[Nym3-commit] r456 - in trunk/nymbaron: . Server

zax at conuropsis.org zax at conuropsis.org
Wed Mar 8 02:16:11 CET 2006


Author: zax
Date: 2006-03-08 02:16:08 +0100 (Wed, 08 Mar 2006)
New Revision: 456

Modified:
   trunk/nymbaron/Mail.py
   trunk/nymbaron/Server/Config.py
   trunk/nymbaron/Server/Main.py
Log:
Introduce SMTP and Sendmail relaying for outgoing nymholder messages.
- Note: Retabbed for compliance with PEP-8, compare with diff -E



Modified: trunk/nymbaron/Mail.py
===================================================================
--- trunk/nymbaron/Mail.py	2006-03-06 20:49:42 UTC (rev 455)
+++ trunk/nymbaron/Mail.py	2006-03-08 01:16:08 UTC (rev 456)
@@ -1,5 +1,6 @@
 # $Id$
 # -*- coding: utf-8 -*-
+# vim: tabstop=4 expandtab shiftwidth=4 autoindent
 # Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
 # and Laurent Fousse <laurent at komite.net>.
 # 
@@ -26,12 +27,17 @@
 
     This package contains utilities to process mails."""
 
-import os
-import tempfile
-import random
 import base64
 import email.Parser
 import nymbaron.Common as Common
+import os
+import random
+import smtplib
+import socket
+import string
+import sys
+import tempfile
+from email.MIMEText import MIMEText
 
 slen = 128
 """The length of a summary in bytes"""
@@ -58,32 +64,32 @@
     #TODO assert type(seq) == int?
     #TODO: this is not RFC2822-compliant
     vheaders = [ 'Cc', 'From', 'Date', 'In-Reply-To', 'Sender',
-		 'Message-Id', 'References', 'Return-Path', 'Subject',
-		 'To', 'X-Anonymous', 'X-Spam-Level']
+                 'Message-Id', 'References', 'Return-Path', 'Subject',
+                 'To', 'X-Anonymous', 'X-Spam-Level']
     par = email.Parser.HeaderParser() 
     message = par.parsestr(msg)
     res = ''
     for i in vheaders:
-	if i in message:
-	    res = res + i + ': ' + message[i][0:80] + "\n"
+        if i in message:
+            res = res + i + ': ' + message[i][0:80] + "\n"
     l = message.get_all('Received')
     if l:
-	res = res + 'Received: ' + l[0][0:80] + "\n"
+        res = res + 'Received: ' + l[0][0:80] + "\n"
     res = res + 'X-Octets: ' + str(len(msg)) + "\n"
     res = res + 'X-Nym-Sequence: ' + str(seq) + "\n"
     res = res + "\n"
     print res
     if not message.is_multipart():
-	res = res + message.get_payload()[0:slen]
+        res = res + message.get_payload()[0:slen]
     else:
-	n = 0
-	p = message.get_payload()
-	j = 0
-	while n < slen and j < len(p):
-	    s = p[j].as_string(True)[0:slen - n]
-	    res = res + s
-	    n = n + len(s)
-	    j = j + 1
+        n = 0
+        p = message.get_payload()
+        j = 0
+        while n < slen and j < len(p):
+            s = p[j].as_string(True)[0:slen - n]
+            res = res + s
+            n = n + len(s)
+            j = j + 1
     return res
 
 def syn_summary(synopsis):
@@ -94,11 +100,11 @@
     summary_headers = ['From', 'Subject', 'Date']
     res = ""
     for header in summary_headers:
-	if syn[header] == None:
-	    syn[header] = ""
-	res = res + header + ': ' + syn[header] + '\n'
+        if syn[header] == None:
+            syn[header] = ""
+        res = res + header + ': ' + syn[header] + '\n'
     if len(res):
-	res = res[:-1]
+        res = res[:-1]
     return res
 
 def XNymSeq(synopsis):
@@ -109,7 +115,7 @@
     syn = par.parsestr(synopsis)
     seq = syn['X-Nym-Sequence']
     if seq == None:
-	raise SeqNotFound()
+        raise SeqNotFound()
     return int(seq)
 
 def tmpFileMsg(body):
@@ -130,13 +136,13 @@
     r1 = 0
     offset = 0
     for i in l[:8]:
-	if i: r1 |= (1 << offset)
-	offset += 1
+        if i: r1 |= (1 << offset)
+        offset += 1
     r2 = 0
     offset = 0
     for i in l[8:]:
-	if i: r2 |= (1 << offset)
-	offset += 1
+        if i: r2 |= (1 << offset)
+        offset += 1
     return chr(r2) + chr(r1)
 
 def bf2list(bf):
@@ -145,9 +151,9 @@
     assert (len(bf) == 2 and type(bf) == str)
     l = []
     for i in range(2):
-	for j in range(8):
-	    if ord(bf[i]) & (1 << j):
-		l.append((1 - i) * 8 + j)
+        for j in range(8):
+            if ord(bf[i]) & (1 << j):
+                l.append((1 - i) * 8 + j)
     return l
 
 def b2s(c):
@@ -160,27 +166,73 @@
     par = email.Parser.HeaderParser() 
     message = par.parsestr(msg)
     for h in forbidden_headers:
-	if message.has_key(h):
-	    del message[h]
+        if message.has_key(h):
+            del message[h]
     return message.as_string()
 
 def relay(nym, rt, ri, sname, body):
+    #TODO: This function is unused at this time and can probably be removed
     """relay the e-mail in body according to the routing type rt
     and the routing info ri for the nymholder of nym"""
     body = filter_header(body)
     b = 'From: ' + nym + '@' + sname + "\n\n" + body
     fname = tmpFileMsg(b) #TODO we can avoid the tempfile with some fd manipultation : exec mixminion, write b in its stdin
-    relaycmd = "mixminion send -t 0x" + b2s(rt[0]) + b2s(rt[1]) + ":" + ri + " -i " + fname
-    return relaycmd
+    mixcmd = "mixminion send -t 0x" + b2s(rt[0]) + b2s(rt[1]) + ":" + ri + " -i " + fname
+    print mixcmd
+    ec = os.system(mixcmd)
+    # TODO : debugging cruft
+    #os.unlink(fname)
+    print "Raw relayed message left in " + fname
+    return ec
 
+def relay_prep(nym, you, sname, body, abuse):
+    """Format a message as a single string that can be passed
+    to various rfc822 compliant email routines"""
+
+    # Make a From header and include the nymholder as a real_name.
+    fromhdr = '%s <%s@%s>' % (nym,nym,sname)
+
+    msg = MIMEText(body)
+    msg['From'] = fromhdr
+    msg['To'] = you
+    # TODO Headers such as Subject,  References etc. need to be read from
+    # either config or from the incoming relay message.
+    msg['Subject'] = 'Type-III Pseudonym Message'
+    if abuse:
+        msg['X-Abuse-Contact'] = abuse
+    return msg.as_string()
+
+def relay_smtp(nym, you, sname, payload, timeout, smtphost):
+    """ Relay an email message through Python's internal
+    smtplib library."""
+    socket.setdefaulttimeout(timeout)
+    me = nym + '@' + sname
+    # Send the message to the designated smtp server
+    # TODO Need some error handling when logging gets enabled
+    mailserver = smtplib.SMTP(smtphost)
+    #mailserver.set_debuglevel(1)
+    mailserver.sendmail(me, [you], payload)
+    mailserver.quit()
+
+def relay_sendmail(payload, sm):
+    """ Depreciated in favour of using Python smtplib, but this
+    works fine on systems with sendmail"""
+    # TODO Need some error handling when logging gets enabled
+    p = os.popen("%s -t" % sm, "w")
+    p.write(payload)
+    status = p.close()
+    if status:
+        print "Sendmail failed with status", status
+        sys.exit(2)
+
 def genMid(length = midLen):
     """Randomly generates a mid of given length different from oldestMid"""
     ret = oldestMid
     while ret == oldestMid:
-	res = ""
-	for _ in range(0, length):
+        res = ""
+        for _ in range(0, length):
             res = res + chr(random.randint(0, 255))
-	ret = res
+        ret = res
     return ret
 
 def mid2filename(mid):

Modified: trunk/nymbaron/Server/Config.py
===================================================================
--- trunk/nymbaron/Server/Config.py	2006-03-06 20:49:42 UTC (rev 455)
+++ trunk/nymbaron/Server/Config.py	2006-03-08 01:16:08 UTC (rev 456)
@@ -1,5 +1,6 @@
 # $Id$
 # -*- coding: utf-8 -*-
+# vim: tabstop=4 expandtab shiftwidth=4 autoindent
 #
 # Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
 # and Laurent Fousse <laurent at komite.net>.
@@ -32,77 +33,107 @@
 
 def userfile():
     if os.environ.has_key('HOME'):
-	return os.environ['HOME'] + '/.nymbaronrc'
+        return os.environ['HOME'] + '/.nymbaronrc'
     else:
-	import pwd
-	return pwd.getpwuid(os.getuid())[5] + '/.nymbaronrc'
+        import pwd
+        return pwd.getpwuid(os.getuid())[5] + '/.nymbaronrc'
 
 def string2loglevel(s):
     s = s.lower()
     if s == 'debug':
-	return logging.DEBUG
+        return logging.DEBUG
     if s == 'info':
-	return logging.INFO
+        return logging.INFO
     return logging.DEBUG
 
 class Config:
 
     def __init__(self, filename = None):
-	self.DEBUG = False
-	"""Whether the server erases the messages it generates from tmp"""
+        self.DEBUG = False
+        """Whether the server erases the messages it generates from tmp"""
 
-	self.online = True
-	"""Whether the server sends the messages it generates on the mixminion
-	network"""
+        self.online = True
+        """Whether the server sends the messages it generates on the mixminion
+        network"""
 
-	self.serverName = 'nymserv.komite.net'
-	"""The hostname of the host on which the nymserver is running."""
+        self.serverName = 'nymserv.komite.net'
+        """The hostname of the host on which the nymserver is running."""
 
-	self.path = '/tmp'
-	"""The path to the directory containing the server data"""
+        self.path = '/tmp'
+        """The path to the directory containing the server data"""
 
-	self.logfile = '/var/tmp/nymbaron.log'
-	"""The file where logging informations are written"""
+        self.logfile = '/var/tmp/nymbaron.log'
+        """The file where logging informations are written"""
 
-	self.loglevel = logging.DEBUG
-	"""The logging verbosity"""
+        self.loglevel = logging.DEBUG
+        """The logging verbosity"""
 
-	self.default_settings = { 'quota' : 10 * 2**20, 'usage' : 0,
-			    'idKey' : None, 'encKey' : None, 'policy' : None,
-			    'SendMsgAfter' : 24, 'SendSummaryAfter' : 12, 
-			    'MaxSURBsPerDay' : 10, 'EncryptSummaryAfter' : 24,
-			    'HoldUntilAck' : 'always', 'ShutdownPhrase' : None,
-			    'HoldSURBs' : 5, 'nSurbs' : 0, 'up' : False,
-			    'cr' : "", 'inidgst' : "" }
-	"""The default settings of a user account."""
+        self.relay_method = 'smtp'
+        """ The method for relaying email to a non-anonymous recipient.
+        Currently 'smtp' or 'sendmail' are supported."""
 
-	self.setupscript = None
-	"""The path to a script to execute upon successful new account
-	   creation"""
-	
-	if filename:
-	    config = ConfigParser.RawConfigParser()
-	    config.read(filename)
-	    
-	    if config.has_section('general'):
-		if config.has_option('general', 'online'):
-		    self.online = config.getboolean('general', 'online')
-		if config.has_option('general', 'DEBUG'):
-		    self.DEBUG = config.getboolean('general', 'DEBUG')
-		if config.has_option('general', 'path'):
-		    self.path = config.get('general', 'path')
-		if config.has_option('general', 'serverName'):
-		    self.serverName = config.get('general', 'serverName')
-		if config.has_option('general', 'setupscript'):
-		    self.setupscript = config.get('general', 'setupscript')
+        self.abuse_address = ''
+        """ The address that will be included in a X-Abuse-Contact header
+        on outgoing relay messages"""
 
-	    if config.has_section('accounts'):
-		for k in self.default_settings.keys():
-		    if config.has_option('accounts', k):
-			self.default_settings[k] = config.get('accounts', k)
+        self.sendmail_path = '/usr/sbin/sendmail'
+        """ Path to the sendmail program.  This is used if relay_method is
+        set to sendmail."""
 
-	    if config.has_section('logging'):
-		if config.has_option('logging', 'logfile'):
-		    self.logfile = config.get('logging', 'logfile')
-		if config.has_option('logging', 'loglevel'):
-		    self.loglevel = string2loglevel(config.get('logging', 'loglevel'))
+        self.timeout = 10
+        """ Seconds to wait before smtp socket timeout."""
+
+        self.smtphost = 'localhost'
+        """ SMTP host to relay messages through."""
+
+        self.default_settings = { 'quota' : 10 * 2**20, 'usage' : 0,
+                            'idKey' : None, 'encKey' : None, 'policy' : None,
+                            'SendMsgAfter' : 24, 'SendSummaryAfter' : 12, 
+                            'MaxSURBsPerDay' : 10, 'EncryptSummaryAfter' : 24,
+                            'HoldUntilAck' : 'always', 'ShutdownPhrase' : None,
+                            'HoldSURBs' : 5, 'nSurbs' : 0, 'up' : False,
+                            'cr' : "", 'inidgst' : "" }
+        """The default settings of a user account."""
+
+        self.setupscript = None
+        """The path to a script to execute upon successful new account
+           creation"""
+        
+        if filename:
+            config = ConfigParser.RawConfigParser()
+            config.read(filename)
+            
+            if config.has_section('general'):
+                if config.has_option('general', 'online'):
+                    self.online = config.getboolean('general', 'online')
+                if config.has_option('general', 'DEBUG'):
+                    self.DEBUG = config.getboolean('general', 'DEBUG')
+                if config.has_option('general', 'path'):
+                    self.path = config.get('general', 'path')
+                if config.has_option('general', 'serverName'):
+                    self.serverName = config.get('general', 'serverName')
+                if config.has_option('general', 'setupscript'):
+                    self.setupscript = config.get('general', 'setupscript')
+
+            if config.has_section('accounts'):
+                for k in self.default_settings.keys():
+                    if config.has_option('accounts', k):
+                        self.default_settings[k] = config.get('accounts', k)
+
+            if config.has_section('logging'):
+                if config.has_option('logging', 'logfile'):
+                    self.logfile = config.get('logging', 'logfile')
+                if config.has_option('logging', 'loglevel'):
+                    self.loglevel = string2loglevel(config.get('logging', 'loglevel'))
+
+            if config.has_section('relay'):
+                if config.has_option('relay', 'relay_method'):
+                    self.relay_method = config.get('relay', 'relay_method')
+                if config.has_option('relay', 'abuse_address'):
+                    self.abuse_address = config.get('relay', 'abuse_address')
+                if config.has_option('relay', 'sendmail_path'):
+                    self.sendmail_path = config.get('relay', 'sendmail_path')
+                if config.has_option('relay', 'timeout'):
+                    self.timeout = config.getfloat('relay', 'timeout')
+                if config.has_option('relay', 'smtphost'):
+                    self.smtphost = config.get('relay', 'smtphost')

Modified: trunk/nymbaron/Server/Main.py
===================================================================
--- trunk/nymbaron/Server/Main.py	2006-03-06 20:49:42 UTC (rev 455)
+++ trunk/nymbaron/Server/Main.py	2006-03-08 01:16:08 UTC (rev 456)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # -*- coding: iso-8859-1 -*-
 # $Id$
+# vim: tabstop=4 expandtab shiftwidth=4 autoindent
 #
 # Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
 # and Laurent Fousse <laurent at komite.net>.
@@ -58,19 +59,19 @@
 
 def readMessage(filename):
     if filename:
-	try:
-	    f = open(filename, "r")
-	    msg = f.read()
-	    f.close()
-	except IOError:
-	    logger.error("Can't read incoming message in specified file %s" % filename)
-	    sys.exit(1)
+        try:
+            f = open(filename, "r")
+            msg = f.read()
+            f.close()
+        except IOError:
+            logger.error("Can't read incoming message in specified file %s" % filename)
+            sys.exit(1)
     else:
-	try:
-	    msg = sys.stdin.read()
-	except KeyboardInterrupt:
-	    logger.error("Interrupted by the user while reading incoming message from stdin")
-	    sys.exit(2)
+        try:
+            msg = sys.stdin.read()
+        except KeyboardInterrupt:
+            logger.error("Interrupted by the user while reading incoming message from stdin")
+            sys.exit(2)
     return msg
 
 def load_config():
@@ -91,13 +92,13 @@
     fun is: fun(account[, tuple of arguments specific to fun]), returns nothing
     """
     for f in os.listdir(config.path):
-	if len(f) >= 4 and f[-4:] == ".dat":
-	    username = f[:-4]
-	    nymuser = User.User(username, config)
-	    if funargs:
-		fun(nymuser, *funargs)
-	    else:
-		fun(nymuser)
+        if len(f) >= 4 and f[-4:] == ".dat":
+            username = f[:-4]
+            nymuser = User.User(username, config)
+            if funargs:
+                fun(nymuser, *funargs)
+            else:
+                fun(nymuser)
 
 def processIncoming(localpart, msg):
     """Process incoming mail from the MTA
@@ -105,15 +106,15 @@
     - checks whether the new mail violates its quota
     - finally stores the message """
     try:
-	nymuser = User.User(localpart, config)
+        nymuser = User.User(localpart, config)
     except User.NoSuchUser:
-	logger.error("No such user %s while processing incoming email" % localpart)
-	sys.exit(73)
+        logger.error("No such user %s while processing incoming email" % localpart)
+        sys.exit(73)
     # TODO : we should honor the "Quota" sending policy.
     if nymuser.usage() + len(msg) > nymuser.quota():
-	logger.error("Quota exceeded for user %s : %s > %s" % (localpart, repr(nymuser.usage()),
-							       repr(nymuser.quota())))
-	sys.exit(75)
+        logger.error("Quota exceeded for user %s : %s > %s" % (localpart, repr(nymuser.usage()),
+                                                               repr(nymuser.quota())))
+        sys.exit(75)
     # Valid user, not over quota. Filtering policy can wait.
     nymuser.store(msg)
     sys.exit(0)
@@ -129,198 +130,208 @@
     logger.debug("Processing control message:\n%s" % base64.encodestring(msg))
     sr = Message.StrReader(msg)
     try:
-	h = sr.readHeader()
-	if (h.nym == ""):
-	    comList = sr.readCommandCToSList()
-	    try:
-		if (len(comList) != 3):
-		    raise MyException()
-		#this may be an account setup message
-		#we suppose there is a exactly 1 Create Command in the message,
-		# 1 Newpk, and 1 surb. more will raise an error
+        h = sr.readHeader()
+        if (h.nym == ""):
+            comList = sr.readCommandCToSList()
+            try:
+                if (len(comList) != 3):
+                    raise MyException()
+                #this may be an account setup message
+                #we suppose there is a exactly 1 Create Command in the message,
+                # 1 Newpk, and 1 surb. more will raise an error
 
-		nymUser = None
-		mdgst = _cr.sha1(msg)
-		#phase 1 we look for the command create
-		for idx, com in enumerate(comList):
-		    if(com.ct() == 0): # TODO : evil numeric litteral
-			#the Create command
-			for pnym in com.list:
-			    try:
-				nymUser = User.User(pnym, config, 1)
-			    except User.AlreadySuchUser:
-				#a nym pnym has been created : check it
-				#doesn't relate to the creat being processed
-				nymUser = User.User(pnym, config, 0)
-				if nymUser['inidgst'] == mdgst:
-				    #it does: we resend the challenge
-				    #and quit
-				    nymUser.received(h.seqNo)
-				    created = Message.Created()
-				    created.fromData(nymUser.username,
-					    nymUser['cr'])
-				    nymUser.advanced_send(
-					Message.buildMessage([created]))
-				    return
-				else:
-				    #it doesn't: we clean and continue
-				    del nymUser
-				    nymUser = None
-			    if nymUser != None:
-				break
-			if (nymUser == None):
-			    #TODO send an Error message to the client when surbs become available?
-			    #for the time being just ignore
-			    logger.info("All nyms proposed in the list were already attributed")
-			    raise MyException()
-			del comList[idx]
-			break
-		if (nymUser == None):
-		    #TODO send an Error message to the client when surbs become available?
-		    #for the time being just ignore
-		    logger.info("No Create Command")
-		    raise MyException()
-			
-		#phase 2 we look for the command surb
-		for idx, com in enumerate(comList):
-		    if(com.ct()==2): # TODO : evil numeric litteral
-			nymUser.addSurbs(com.surbs)
-			if nymUser['nSurbs'] < 3:
-			    logger.info("Not Enough Surbs")
-			    raise MyException()
-			del comList[idx]
-			break
-		if(len(comList) != 1):
-		    nymUser.abort()
-		    raise MyException()
-		#phase 3 the last command should be a Newpk
-		com = comList[0]
-		if(com.ct() != 3): # TODO : evil numeric litteral
-		    nymUser.abort()
-		    raise MyException()
-		nymUser.setKeys(com.ekid, com.ekenc)
-		if(not nymUser.checkMessageSign(msg[Message.sigLength:],h.sig)):
-		   nymUser.abort()
-		   raise MyException()
-		else: # Valid account creation request. Send CREATED
-		    #remember we received this register request
-		    nymUser['inidgst'] = mdgst
-		    nymUser.received(h.seqNo)
-		    #we forget about CREATE2 message and consider the account as
-		    #up
-		    nymUser.setUp()
-		    #send back the challenge
-		    created = Message.Created()
-		    #TODO non empty challenge?
-		    created.fromData(nymUser.username, "")
-		    nymUser.advanced_send(Message.buildMessage([created]))
-	    except MyException:
-		   #if you come here something went wrong during the account
-		   #initialization
-		   logger.debug("Bad formed account creation message")
-		   sys.exit(2) #TODO smart error code
-	else: # NYM is not empty
-	    try:
-		nymUser = User.User(h.nym, config)
-	    except User.NoSuchUser:
-		logger.debug("No such user %s" % h.nym)
-		sys.exit(73) #TODO is it the smart error code
-	    if(not nymUser.checkMessageSign(msg[Message.sigLength:],h.sig)):
-		return
-	    #The message comes from a known nick and is authenticated
-	    #register the seqNo as received
-	    #TODO is it ok?
-	    nymUser.received(h.seqNo)
-	    comList = sr.readCommandCToSList()
-	    for com in comList:
-		if (com.ct() == Message.CToSCODE['Create']):
-		    #we ignore the Create command if it comes from the nymholder of an account, should we rise an error?
-		    pass
-		#if the account is not initialized, or if it is already up we ignore Create2 messages
-		elif (com.ct() == Message.CToSCODE['Create2']):
-		    #for the time being we forget about CREATE2 messages
-		    pass
-		    #if not nymUser.isUp():
-		#	if(nymUser.checkChallenge(com.cr)):
-		#	    nymUser.setUp()
-		#other commands are only taken into account if the account is up
-		elif (nymUser.isUp()):
-		    if (com.ct() == Message.CToSCODE['Surb']):
-			if (len(com.surbs) == 0):
-			    nymUser.delSurbs()
-			else:
-			    nymUser.addSurbs(com.surbs)
-		    elif (com.ct() == Message.CToSCODE['Newpk']):
-			nymUser.setKeys(com.kid,com.kenc)
-		    elif (com.ct() == Message.CToSCODE['Relay']):
-			relaycmd = Mail.relay(h.nym, com.rt, com.ri,
-					config.serverName, com.body)
-			logger.debug('Attempting to relay nymholder message using command %s' % relaycmd)
-			ec = os.system(relaycmd)
-			if (ec == 0):
-			    logger.debug('Message relay successfully processed')
-			else:
-			    logger.error("mixminion exited abnormally with error code %d" % ec)
-			    sys.exit(2)
-			    
-		    elif (com.ct() == Message.CToSCODE['Get']):
-			msgList = []
-			sendList = []
-			for m in com.l:
-			    if nymUser.hasMail(m):
-				msgCom = Message.Msg()
-				msgCom.fromData(m,nymUser.getMail(m))
-				msgList.append(msgCom)
-				sendList.append(m)
-			ec = nymUser.advanced_send(
-				Message.buildMessage(msgList))
-			logger.debug("EC is " + str(ec))
-			if (ec == 0):
-			    nymUser.markMid(sendList,lifeCycle['sent-in-full'])
-			    if(nymUser['HoldUntilAck'] == 'never'):
-				map(nymUser.delete_msg,sendList)
-			    
-			else:
-			    logger.error("mixminion exited abnormally with error code %d" % ec)
-			    sys.exit(2)
-				
-		    elif (com.ct() == Message.CToSCODE['Summarize']):
-			comList = []
-			sendList = nymUser.prepareSummary(com.num, com.after)
-			mList = []
-			if sendList:
-			    for (ml, bf, blob) in sendList:
-				sumCom = Message.Summary()
-				sumCom.fromData(bf, blob)
-				comList.append(sumCom)
-				mList = mList + ml
-			else:
-			    sumCom = Message.Summary()
-			    sumCom.fromData("\x00\x00","")
-			    comList = [sumCom]
-			ec = nymUser.send(Message.buildMessage(comList))
-			if (ec == 0):
-			    nymUser.markMid(mList, lifeCycle['synopsis-sent'])
-			else:
-			    logger.error("mixminion exited abnormally with error code %d" % ec)
-			    sys.exit(2)
+                nymUser = None
+                mdgst = _cr.sha1(msg)
+                #phase 1 we look for the command create
+                for idx, com in enumerate(comList):
+                    if(com.ct() == 0): # TODO : evil numeric litteral
+                        #the Create command
+                        for pnym in com.list:
+                            try:
+                                nymUser = User.User(pnym, config, 1)
+                            except User.AlreadySuchUser:
+                                #a nym pnym has been created : check it
+                                #doesn't relate to the creat being processed
+                                nymUser = User.User(pnym, config, 0)
+                                if nymUser['inidgst'] == mdgst:
+                                    #it does: we resend the challenge
+                                    #and quit
+                                    nymUser.received(h.seqNo)
+                                    created = Message.Created()
+                                    created.fromData(nymUser.username,
+                                            nymUser['cr'])
+                                    nymUser.advanced_send(
+                                        Message.buildMessage([created]))
+                                    return
+                                else:
+                                    #it doesn't: we clean and continue
+                                    del nymUser
+                                    nymUser = None
+                            if nymUser != None:
+                                break
+                        if (nymUser == None):
+                            #TODO send an Error message to the client when surbs become available?
+                            #for the time being just ignore
+                            logger.info("All nyms proposed in the list were already attributed")
+                            raise MyException()
+                        del comList[idx]
+                        break
+                if (nymUser == None):
+                    #TODO send an Error message to the client when surbs become available?
+                    #for the time being just ignore
+                    logger.info("No Create Command")
+                    raise MyException()
+                        
+                #phase 2 we look for the command surb
+                for idx, com in enumerate(comList):
+                    if(com.ct()==2): # TODO : evil numeric litteral
+                        nymUser.addSurbs(com.surbs)
+                        if nymUser['nSurbs'] < 3:
+                            logger.info("Not Enough Surbs")
+                            raise MyException()
+                        del comList[idx]
+                        break
+                if(len(comList) != 1):
+                    nymUser.abort()
+                    raise MyException()
+                #phase 3 the last command should be a Newpk
+                com = comList[0]
+                if(com.ct() != 3): # TODO : evil numeric litteral
+                    nymUser.abort()
+                    raise MyException()
+                nymUser.setKeys(com.ekid, com.ekenc)
+                if(not nymUser.checkMessageSign(msg[Message.sigLength:],h.sig)):
+                   nymUser.abort()
+                   raise MyException()
+                else: # Valid account creation request. Send CREATED
+                    #remember we received this register request
+                    nymUser['inidgst'] = mdgst
+                    nymUser.received(h.seqNo)
+                    #we forget about CREATE2 message and consider the account as
+                    #up
+                    nymUser.setUp()
+                    #send back the challenge
+                    created = Message.Created()
+                    #TODO non empty challenge?
+                    created.fromData(nymUser.username, "")
+                    nymUser.advanced_send(Message.buildMessage([created]))
+            except MyException:
+                   #if you come here something went wrong during the account
+                   #initialization
+                   logger.debug("Bad formed account creation message")
+                   sys.exit(2) #TODO smart error code
+        else: # NYM is not empty
+            try:
+                nymUser = User.User(h.nym, config)
+            except User.NoSuchUser:
+                logger.debug("No such user %s" % h.nym)
+                sys.exit(73) #TODO is it the smart error code
+            if(not nymUser.checkMessageSign(msg[Message.sigLength:],h.sig)):
+                return
+            #The message comes from a known nick and is authenticated
+            #register the seqNo as received
+            #TODO is it ok?
+            nymUser.received(h.seqNo)
+            comList = sr.readCommandCToSList()
+            for com in comList:
+                if (com.ct() == Message.CToSCODE['Create']):
+                    #we ignore the Create command if it comes from the nymholder of an account, should we rise an error?
+                    pass
+                #if the account is not initialized, or if it is already up we ignore Create2 messages
+                elif (com.ct() == Message.CToSCODE['Create2']):
+                    #for the time being we forget about CREATE2 messages
+                    pass
+                    #if not nymUser.isUp():
+                #       if(nymUser.checkChallenge(com.cr)):
+                #           nymUser.setUp()
+                #other commands are only taken into account if the account is up
+                elif (nymUser.isUp()):
+                    if (com.ct() == Message.CToSCODE['Surb']):
+                        if (len(com.surbs) == 0):
+                            nymUser.delSurbs()
+                        else:
+                            nymUser.addSurbs(com.surbs)
+                    elif (com.ct() == Message.CToSCODE['Newpk']):
+                        nymUser.setKeys(com.kid,com.kenc)
+                    elif (com.ct() == Message.CToSCODE['Relay']):
+#                        ec = Mail.relay(h.nym, com.rt, com.ri,
+#                                        config.serverName, com.body)
+#                        if (ec != 0):
+#                            logger.error("mixminion exited abnormally with error code %d" % ec)
+#                            sys.exit(2)
 
-		    elif (com.ct() == Message.CToSCODE['Delete']):
-			for mid in com.l:
-			    nymUser.delete_msg(mid)
-		    elif (com.ct() == Message.CToSCODE['Policy']):
-			if(com.opt in Common.userPolicy):
-			    nymUser[com.opt] = com.val
-		    else:
-			pass
+                        #TODO: Passing this many args is messy.  Perhaps they should be
+                        # written to a dictionary and passed as one?
+                        payload = Mail.relay_prep(h.nym, com.ri, config.serverName, com.body,
+                                                  config.abuse_address)
+                        if config.relay_method == 'smtp':
+                            Mail.relay_smtp(h.nym, com.ri, config.serverName, payload,
+                                            config.timeout, config.smtphost)
+                        elif config.relay_method == 'sendmail':
+                            Mail.relay_sendmail(payload, config.sendmail_path)
+                        else:
+                            logger.error('Unsupported relay option %s specified' % config.relay_method)
+                            sys.exit(2)
+
+                            
+                    elif (com.ct() == Message.CToSCODE['Get']):
+                        msgList = []
+                        sendList = []
+                        for m in com.l:
+                            if nymUser.hasMail(m):
+                                msgCom = Message.Msg()
+                                msgCom.fromData(m,nymUser.getMail(m))
+                                msgList.append(msgCom)
+                                sendList.append(m)
+                        ec = nymUser.advanced_send(
+                                Message.buildMessage(msgList))
+                        logger.debug("EC is " + str(ec))
+                        if (ec == 0):
+                            nymUser.markMid(sendList,lifeCycle['sent-in-full'])
+                            if(nymUser['HoldUntilAck'] == 'never'):
+                                map(nymUser.delete_msg,sendList)
+                            
+                        else:
+                            logger.error("mixminion exited abnormally with error code %d" % ec)
+                            sys.exit(2)
+                                
+                    elif (com.ct() == Message.CToSCODE['Summarize']):
+                        comList = []
+                        sendList = nymUser.prepareSummary(com.num, com.after)
+                        mList = []
+                        if sendList:
+                            for (ml, bf, blob) in sendList:
+                                sumCom = Message.Summary()
+                                sumCom.fromData(bf, blob)
+                                comList.append(sumCom)
+                                mList = mList + ml
+                        else:
+                            sumCom = Message.Summary()
+                            sumCom.fromData("\x00\x00","")
+                            comList = [sumCom]
+                        ec = nymUser.send(Message.buildMessage(comList))
+                        if (ec == 0):
+                            nymUser.markMid(mList, lifeCycle['synopsis-sent'])
+                        else:
+                            logger.error("mixminion exited abnormally with error code %d" % ec)
+                            sys.exit(2)
+
+                    elif (com.ct() == Message.CToSCODE['Delete']):
+                        for mid in com.l:
+                            nymUser.delete_msg(mid)
+                    elif (com.ct() == Message.CToSCODE['Policy']):
+                        if(com.opt in Common.userPolicy):
+                            nymUser[com.opt] = com.val
+                    else:
+                        pass
     except Message.ParseError, inst:
-	logger.error("Parse error in incoming control message %s" % str(inst))
-	sys.exit(2) #TODO error code		
-
+        logger.error("Parse error in incoming control message %s" % str(inst))
+        sys.exit(2) #TODO error code            
+              
 def main(args):
     if len(args) < 2:
-	print usage_string
-	sys.exit(1)
+        print usage_string
+        sys.exit(1)
     # Try to load the configuration file.
     load_config()
     # The main logger object :
@@ -333,52 +344,52 @@
     logger.setLevel(config.loglevel)
     
     if args[1] == "deliver":
-	parser = OptionParser(usage =
-	"""nymbarond deliver [options]
-	Deliver a message to a nym account.""")
-	parser.add_option("-n", "--nym", action = "store", dest = "nym",
-		help = "The nym to which deliver the message")
-	parser.add_option("-f", "--file", action = "store",
-		dest = "file", help = "The file to read the message " +
-		"from, or stdin if omitted")
-	(options, args) = parser.parse_args(args[2:])
-	if not options.nym:
-	    print ("You must provide a nym to which the message is adressed")
-	    sys.exit(1)
-	processIncoming(options.nym, readMessage(options.file))
-	sys.exit(0)
+        parser = OptionParser(usage =
+        """nymbarond deliver [options]
+        Deliver a message to a nym account.""")
+        parser.add_option("-n", "--nym", action = "store", dest = "nym",
+                help = "The nym to which deliver the message")
+        parser.add_option("-f", "--file", action = "store",
+                dest = "file", help = "The file to read the message " +
+                "from, or stdin if omitted")
+        (options, args) = parser.parse_args(args[2:])
+        if not options.nym:
+            print ("You must provide a nym to which the message is adressed")
+            sys.exit(1)
+        processIncoming(options.nym, readMessage(options.file))
+        sys.exit(0)
     
     if args[1] == "process":
-	parser = OptionParser(usage =
-	"""nymbarond process [options]
-	Process a command message.""")
-	parser.add_option("-f", "--file", action = "store",
-		dest = "file", help = "The file to read the message " +
-		"from, or stdin if omitted")
-	(options, args) = parser.parse_args(args[2:])
-	logger.debug("Got an incoming control message")
-	try:
-	    msg = readMessage(options.file)
-	    processMessage(msg)
-	except Exception: #TODO catch a more detailed exception
-	    # see if we got a base64 encoded message.
-	    m = re.search("binary\\n\\n(.*)\\n---", msg, re.S)
-	    if m:
-		processMessage(base64.decodestring(m.group(1)))
-	    else:
-		sys.stderr.write("Unable to find valid control message")
-	sys.exit(0)
+        parser = OptionParser(usage =
+        """nymbarond process [options]
+        Process a command message.""")
+        parser.add_option("-f", "--file", action = "store",
+                dest = "file", help = "The file to read the message " +
+                "from, or stdin if omitted")
+        (options, args) = parser.parse_args(args[2:])
+        logger.debug("Got an incoming control message")
+        try:
+            msg = readMessage(options.file)
+            processMessage(msg)
+        except Exception: #TODO catch a more detailed exception
+            # see if we got a base64 encoded message.
+            m = re.search("binary\\n\\n(.*)\\n---", msg, re.S)
+            if m:
+                processMessage(base64.decodestring(m.group(1)))
+            else:
+                sys.stderr.write("Unable to find valid control message")
+        sys.exit(0)
     
     if args[1] == "clean-surbs":
-	mapAccount(User.User.clean_surbs)
-	sys.exit(0)
+        mapAccount(User.User.clean_surbs)
+        sys.exit(0)
 
     if args[1] == "encode-synopses":
         sys.exit(0)
     
     if args[1] == "help":
-	print usage_string
-	sys.exit(0)
+        print usage_string
+        sys.exit(0)
     # Unknown command
     sys.stderr.write("Error: unknown command \"" + args[1] + "\"\n")
     sys.exit(1)



More information about the Nym3-commit mailing list