[Nym3-commit] r136 - in trunk/nym3: . Client Server

jr at komite.net jr at komite.net
Fri Dec 17 01:16:18 CET 2004


Author: jr
Date: 2004-12-17 01:16:14 +0100 (Fri, 17 Dec 2004)
New Revision: 136

Modified:
   trunk/nym3/Client/Config.py
   trunk/nym3/Client/Main.py
   trunk/nym3/Client/User.py
   trunk/nym3/Common.py
   trunk/nym3/Crypto.py
   trunk/nym3/Mail.py
   trunk/nym3/Message.py
   trunk/nym3/Server/Config.py
   trunk/nym3/Server/Main.py
   trunk/nym3/Server/User.py
   trunk/nym3/TODO
Log:
- Add documentation where they were missing
- Add some item to the TODO file


Modified: trunk/nym3/Client/Config.py
===================================================================
--- trunk/nym3/Client/Config.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Client/Config.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,6 +1,16 @@
+
 DEBUG = False
+"""Tells the client whether he should send the messages he generates through
+mixminion or output them on stdout"""
+
 path = '.'
-default_settings = {}
+"""The path to the directory containing the user accounts"""
+
 defaultUser = "nym at server"
+"""idTag of the default account"""
 
+defaultAddress = "smtp:nobody at nowhere.net"
+"""address where surbs point to"""
 
+default_settings = { 'address' : defaultAdress }
+"""default_settings"""

Modified: trunk/nym3/Client/Main.py
===================================================================
--- trunk/nym3/Client/Main.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Client/Main.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,3 +1,5 @@
+"""The implementation of a client"""
+
 import sys
 import os
 from optparse import OptionParser, make_option
@@ -28,6 +30,7 @@
             pass
 
 def setUpAccount(serverName):
+    """Tries to setup a new account on the given server"""
     try:
         nymUser = User.User(1)
     except User.AlreadySuchUser:
@@ -94,11 +97,14 @@
         parser.add_option("-t", "--tag", dest = "tag", default = "", 
 	    help = "the tag that the user wish to associate with \
 	    the new account")
+        parser.add_option("-a", "--address", dest = "address", default = "", 
+	    help = "the address to which the surbs point")
         (options, args) = parser.parse_args(sys.argv[2:])
         nymUser = User.User(tag = options.tag, create = 1)
+        nymUser.setAddress(options.address)
         nymUser.generateKeys()
         nymUser.setServer(args[0])
-        #fill surbs
+        surbs = nymUser.generateSurbs(3)
         comC = Message.Create()
         comC.fromData(args[1:])
         comN = Message.Newpk()

Modified: trunk/nym3/Client/User.py
===================================================================
--- trunk/nym3/Client/User.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Client/User.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,3 +1,6 @@
+"""Contains user account information on the client side
++ way to create, access and modify them"""
+
 import os
 import nym3.Client.Config as Config
 import nym3.Mail as Mail
@@ -7,9 +10,12 @@
 import nym3.Message as Message
 
 class NoSuchUser(Exception): pass
+"""Exception thrown when a user identified by her nym can't be found"""
 
 class AlreadySuchUser(Exception): pass
+"""Exception thrown when a user identified by her nym already exists"""
 
+#bullshit we'll gonna have to take a look at
 def generateIdTag():
     while True:
         tag = Mail.genMid(8)
@@ -114,6 +120,7 @@
             sys.exit(2)
             
     def __del__(self):
+	"""Flushes the user account to the disk"""
         self._save_index()
         self._save_synbox()
         self._save_mbox()
@@ -121,23 +128,28 @@
         self._release()
     
     def __getitem__(self, key):
+	"""Gets user account field as in a hashtable"""
 	return self.data[key]
 
     def __setitem__(self, key, value):
+	"""Gets user account field as in a hashtable"""
 	self.data[key] = value
 
     def _lock(self):
-	"""Lock the user. For well behaved functions."""
+	"""Locks the user. For well behaved functions."""
 	self.lock = mixminion.Common.Lockfile(Config.path + os.sep + self.idTag + '.lck')
 	self.lock.acquire()
 
     def _release(self):
+	"""Releases the lock"""
 	self.lock.release()
 
     def homeDir(self):
+	"""Gets the home directory of this account"""
         return  Config.path + os.sep + self.idTag + os.sep
 
     def load_mbox(self):
+	"""Loads the mailbox from the disk"""
 	if not self.mbox == None: return
 	mbox = self.mboxfile()
 	try:
@@ -148,9 +160,11 @@
 	    self.mbox = {}
 
     def mboxfile(self):
+	"""Gets the path of the mailbox"""
 	return self.home + 'mbox'
 
     def _save_mbox(self):
+	"""Flushes the mailbox to the disk"""
 	if self.mbox == None: return
 	mbox = self.mboxfile()
 	f = open(mbox, 'w')
@@ -158,12 +172,15 @@
 	f.close()
 
     def synboxfile(self):
+	"""Gets the path of the synbox"""
 	return self.home + 'syn'
 
     def indexfile(self):
+	"""Gets the path of the index"""
 	return self.home + 'idx'
 	
     def load_synbox(self):
+	"""Loads the synbox from the disk"""
 	if not self.syn == None: return
 	synbox = self.synboxfile()
 	try:
@@ -174,6 +191,7 @@
 	    self.syn = []
 
     def _save_synbox(self):
+	"""Flushes the synbox to the disk"""
 	if self.syn == None: return
 	synbox = self.synboxfile()
 	f = open(synbox, 'w')
@@ -181,6 +199,7 @@
 	f.close()
     
     def load_index(self):
+	"""Loads the index from the disk"""
 	if not self.index == None: return
 	index = self.indexfile()
 	try:
@@ -191,6 +210,7 @@
 	    self.index = {}
     
     def _save_index(self):
+	"""Flushes the index to the disk"""
 	if self.index == None: return
 	index = self.indexfile()
 	f = open(index, 'w')
@@ -198,29 +218,49 @@
 	f.close()
 
     def _save_data(self):
-        pass
+	"""Flushes the data to the disk"""
+	pass
 
     def generateKeys(self):
+	"""Generates keys for this account"""
         self.data['idKey'] = _cr.pk_generate()
         self.data['encKey'] = _cr.pk_generate()
         
     def getSeqNo(self):
+	"""?"""
         return chr(0)*Common.seqNoLength
 
     def addHeader(self, msg):
+	"""Generates a header for a message using the identification key"""
         h = Header()
         sig = _cr.pk_sign(_cr.sha1(msg), self.data['idKey'])
         h.fromData(self.data['username'], self.getSeqNo(), sig)
         return str(h) + msg
 
     def setServer(self, serv):
+	"""Sets the name of the nymserver hosting the nym account"""
         self.data['server'] = serv
+
+    def setAddress(self, add):
+	"""?"""
+        #TODOcheck that add has a good format?
+        self.data['address'] = add
     
     def send(self, msg):
+	"""Sends a message from the nymholder to the nymserver"""
         pass
 
     def sendControl(self, l):
+	"""Sends a list of command messages to the nymserver"""
         msg = Message.buildMessage(l)
         msg = self.addHeader(msg)
         nymUser.send(msg)
         
+    def generateSurbs(self, n):
+        """Generate surbs
+	TODO use Mixminion Client API"""
+        com = "mixminion generate-surb -n " + str(n) + " "
+        com = com + "-t " + self.data['address'] + " "
+        com = com + "--identity=" + self.idTag + " "
+        ec = os.system(com)
+    

Modified: trunk/nym3/Common.py
===================================================================
--- trunk/nym3/Common.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Common.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,14 +1,25 @@
 # $Id$
 
+"""Stuff used by both the server and our client"""
+
 #constants
 surbLength = 2104
+"""The length of the surbs in bytes"""
+
 sigLength = 256
+"""The length of signatures in bytes"""
+
 seqNoLength = 20
+"""The length of sequence numbers in bytes"""
+
 midLength = 20
+"""The legnth of message ID in bytes"""
 
 userPolicy = ["SendMsgAfter", "SendSummaryAfter", "EncryptSummaryAfter",
               "MaxSURBsPerDay", "HoldSURBs", "HoldUntilAck",
               "ShutdownPhrase"] 
+"""The accepted strings identifying userPolicies"""
 
 lifeCycle = { 'nothing-sent' : 0, 'synopsis-sent' : 1,
               'sent-in-full' : 2, 'deleted' : 3 }
+"""The lifecycle of a message on the server"""

Modified: trunk/nym3/Crypto.py
===================================================================
--- trunk/nym3/Crypto.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Crypto.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -4,6 +4,8 @@
 import Message
 
 def nym_encrypt(data, key):
+    """Encrypts data with the given key using nymservers fashion
+    """
     n = Message.nbBits(_cr.pk_get_modulus(key)) / 8
     assert n == 128 or n == 256
     dataC = compressData(data)

Modified: trunk/nym3/Mail.py
===================================================================
--- trunk/nym3/Mail.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Mail.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,5 +1,8 @@
 # $Id$
 
+"""Contains utilities to process mails
+"""
+
 import re
 import os
 import tempfile
@@ -10,12 +13,19 @@
 import nym3.Server.Config as Sconfig
 
 slen = 180
+"""The length of a summary in bytes"""
+
 midLen = Common.midLength
+"""The length of a mid in bytes"""
+
 random.seed(None)
+
 oldestMid = chr(0) * midLen
+"""Special value of a mid, older than any othany other mid"""
 
-
 def synopsize(msg):
+    """Synopsizes a message : keeps a set of headers and the beginning of
+    the body of the mail"""
     vheaders = [ 'Cc', 'From', 'Date', 'In-Reply-To', 'Sender',
 		 'Message-Id', 'References', 'Return-Path', 'Subject',
 		 'To', 'X-Anonymous', 'X-Spam-Level']
@@ -35,6 +45,7 @@
     return res + 'X-Octets: ' + repr(len(msg)) + "\n" + body
 
 def myWrite(f, m):
+    """Safe write : Writes all the bytes of m in the file f"""
     def aux(m, l):
         w = os.write(f,m)
         aux(m[w:], l-w)
@@ -77,6 +88,7 @@
     return ec
                    
 def genMid(length = midLen):
+    """Randomly generates a mid of given length different from oldestMid"""
     ret = oldestMid
     while ret == oldestMid:
 	res = ""
@@ -85,12 +97,14 @@
     return ret
 
 def mid2filename(mid):
+    """Converts a mid in a file name"""
     res = base64.encodestring(mid)
     res = res.replace('/', '_')
     res = string.strip(res)
     return res
 
 def filename2mid(mid):
+    """Converts a file name in a mid"""
     res = mid
     res = res.replace('_', '/')
     res = base64.decodestring(mid)

Modified: trunk/nym3/Message.py
===================================================================
--- trunk/nym3/Message.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Message.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,5 +1,8 @@
 # $Id$
 
+"""This module contains representations of the message of the protocol
+and methods to parse and generate messages from these representation"""
+
 import types
 import nym3.Common as Common
 
@@ -58,9 +61,16 @@
 	return (reduce(lambda x, y: x and y, lb))
 
 surbLength = Common.surbLength
+"""The length of surbs in bytes"""
+
 sigLength = Common.sigLength
+"""The length of signatures in bytes"""
+
 seqNoLength = Common.seqNoLength
+"""The length of sequence numbers in bytes"""
+
 midLength = Common.midLength
+"""The length of message ID in bytes"""
 
 class StrReader:
 	"""wraps a string and gives method to read it chunk by chunk"""

Modified: trunk/nym3/Server/Config.py
===================================================================
--- trunk/nym3/Server/Config.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Server/Config.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,12 +1,19 @@
 # $Id$
 
+"""The configuration file of the server."""
+
+__all__ = ['DEBUG', "serverName", "path", "default_settings"]
 #path = '/var/lib/nym3'
 
 DEBUG = False
+"""Whether the server sends the message it generates to mixminion 
+or to stdout"""
 
 serverName = 'nymserv.komite.net'
+"""The hostname of the host on which the nymserver is running."""
 
 path = '.'
+"""The path to the directory containing the server data"""
 
 default_settings = { 'quota' : 10 * 2**20, 'usage' : 0,
 		     'idKey' : None, 'encKey' : None, 'policy' : None,
@@ -15,6 +22,6 @@
 		     'HoldUntilAck' : 'always', 'ShutdownPhrase' : None,
 		     'HoldSURBs' : 5,
 		     'nSurbs' : 0, 'up' : False, 'cr' : ""}
-
+"""The default settings of a user account."""
 #up : the user account has been initialized and the answer to the challenge is correct
 #cr : response to the challenge

Modified: trunk/nym3/Server/Main.py
===================================================================
--- trunk/nym3/Server/Main.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Server/Main.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,6 +1,8 @@
 #!/usr/bin/env python2.3
 # $Id$
 
+"""The implementation of the server."""
+
 import sys
 import os
 import getopt
@@ -11,8 +13,14 @@
 import nym3.Mail as Mail
 
 lifeCycle = Common.lifeCycle
+"""The life cycle of a mail received by the server for a nym""" 
 
+
 def processIncoming(localpart, msg):
+    """Process incoming mail
+    - identifies the nym the mail is addressed to
+    - checks whether the new mail violates its quota
+    - finally stores the message """
     try:
 	nymuser = User.User(localpart)
     except User.NoSuchUser:
@@ -192,3 +200,4 @@
 	if o == "-m":
 	    processMessage(sys.stdin.read())
 	    sys.exit(0)
+

Modified: trunk/nym3/Server/User.py
===================================================================
--- trunk/nym3/Server/User.py	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/Server/User.py	2004-12-17 00:16:14 UTC (rev 136)
@@ -1,5 +1,8 @@
 # $Id$
 
+"""This module contains methods of storing, accessing and editing user account
+information."""
+
 import os
 import nym3.Server.Config as Config
 import nym3.Common as Common
@@ -13,14 +16,21 @@
 import mixminion.Crypto as _cr
 
 surb_len = Common.surbLength
+"""The length of a surb"""
+
 lifeCycle = Common.lifeCycle
+"""The life cycle of a mail received by a nym"""
 
 class NoSuchUser(Exception): pass
+"""Exception thrown when a user account identified by its nym can't be found
+"""
 
 class AlreadySuchUser(Exception): pass
+"""Exception thrown when a user account identified by its nym already exists
+"""
 
 def binsearch(l, prop):
-    """Returns first index i of l for which l[i] is true,
+    """Returns the first index i of l for which l[i] is true,
        assuming prop is increasing over l"""
     min = 0
     max = len(l) - 1
@@ -65,6 +75,8 @@
 	    self.data['username'] = username
     
     def __del__(self):
+	"""Flush the information contained in this User to the disk
+	"""
         if (not self._abort):
             self._save_index()
             self._save_synbox()
@@ -73,9 +85,13 @@
         self._release()
     
     def __getitem__(self, key):
+	"""Gets the fields of this Object as if a hashtable
+	"""
 	return self.data[key]
 
     def __setitem__(self, key, value):
+	"""Sets the fields of this Object as if a hashtable
+	"""
 	self.data[key] = value
 
     def _lock(self):
@@ -85,6 +101,7 @@
 	self.lock.acquire()
 
     def _release(self):
+	"""Releases the lock"""
 	self.lock.release()
 
     def abort(self):
@@ -98,12 +115,16 @@
             self._abort = True
 
     def timecmp(self, a, b):
-        return cmp(self.index[a]['time'], self.index[b]['time'])
+	"""
+        """
+	return cmp(self.index[a]['time'], self.index[b]['time'])
 
     def quota(self):
+	"""Gets the quota, the maximum data size authorized"""
 	return self.data['quota']
 
     def usage(self):
+	"""Gets the usage, the actual data size used"""
 	sum = 0
 	try:
 	    sum = os.stat(self.surbfile())[6]
@@ -121,9 +142,11 @@
 	return sum
 
     def idKey(self):
+	"""Gets the user public key used for identification"""
         return self.data['idKey']
 
     def encKey(self):
+	"""Gets the user public key used for encryption"""
         return self.data['encKey']
 
     def blobify(self, l):
@@ -146,7 +169,7 @@
 	raise ValueError()
 
     def getMail(self, mid):
-        """Retrieve an encrypted mail from the mbox
+        """Retrieve an encrypted mail with a given mid from the mbox
         raise ValueError if the mail cannot be found"""
         self.load_mbox()
         try:
@@ -170,15 +193,21 @@
 	return ret
     
     def _save_data(self):
+	"""Flush to the disk the non message related data contained by this User object
+	"""
 	if self.data == None: return
 	f = open(self.datafile, 'w')
 	pickle.dump(self.data, f)
 	f.close()
 
     def surbfile(self):
+	"""Gets the path to file containing the surbs to send messages to the nymholder
+	"""
 	return Config.path + os.sep + self.data['username'] + '.surbs'
 
     def send(self, msg):
+	"""Sends a message to the nymholder through the mixminion network, using the surbs provided
+	by the nymholder"""
         if Config.DEBUG:
             print msg
             return 0
@@ -189,7 +218,8 @@
             return ec
 
     def clean_surbs(self):
-	"Inspect the surbs and delete the used/outdated"
+	"""Inspect the surbs and delete the used/outdated
+	TODO use client API when implemented"""
 	fname = self.surbfile()
 	f = open(fname, "r")
 	buffer = f.read()
@@ -218,6 +248,7 @@
 	f.close()
 
     def addSurbs(self,surbs):
+	"""Adds surbs to the store of surbs"""
 	fname = self.surbfile()
 	f = open(fname, "a")
 	f.write(surbs)
@@ -225,11 +256,13 @@
 	self.data['nSurbs'] = self.data['nSurbs'] + ( len(surbs) / surb_len)
 
     def delSurbs(self):
+	"""Deletes all the surbs of the surb store"""
 	fname = self.surbfile()
         os.unlink(fname)
 	self.data['nSurbs'] = 0
 
     def load_mbox(self):
+	"""Loads the mailbox from the disk"""
 	if not self.mbox == None: return
 	mbox = self.mboxfile()
 	try:
@@ -240,9 +273,11 @@
 	    self.mbox = {}
 
     def mboxfile(self):
+	"""Gets the mailbox file name"""
 	return Config.path + os.sep + self.data['username'] + '.mbox'
 
     def _save_mbox(self):
+	"""Flushs the mailbox to the disk"""
 	if self.mbox == None: return
 	mbox = self.mboxfile()
 	f = open(mbox, 'w')
@@ -250,12 +285,15 @@
 	f.close()
 
     def synboxfile(self):
+	"""Gets the synbox file name"""
 	return Config.path + os.sep + self.data['username'] + '.syn'
 
     def indexfile(self):
+	"""Gets the index filename"""
 	return Config.path + os.sep + self.data['username'] + '.idx'
 	
     def load_synbox(self):
+	"""Loads the synbox, structure containing synopses, from the disk"""
 	if not self.syn == None: return
 	synbox = self.synboxfile()
 	try:
@@ -266,6 +304,7 @@
 	    self.syn = []
 
     def _save_synbox(self):
+	"""Flushs the synbox to the disk"""
 	if self.syn == None: return
 	synbox = self.synboxfile()
 	f = open(synbox, 'w')
@@ -273,6 +312,8 @@
 	f.close()
     
     def load_index(self):
+	"""Loads the index, a structure associating to a message of given mid
+	the time of its arrival and its position in the lifecycle"""
 	if not self.index == None: return
 	index = self.indexfile()
 	try:
@@ -283,6 +324,7 @@
 	    self.index = {}
     
     def _save_index(self):
+	"""Flushes the index to the disk"""
 	if self.index == None: return
 	index = self.indexfile()
 	f = open(index, 'w')
@@ -290,7 +332,7 @@
 	f.close()
         
     def store(self, msg):
-	"Store an incoming message"
+	"Stores an incoming message"
 	syn = Mail.synopsize(msg)
 	# Store the mail.
 	self.load_mbox()
@@ -307,7 +349,7 @@
 			   'status' : lifeCycle['nothing-sent'] }
 
     def delete_msg(self, mid):
-	"""Delete a stored message. Delete the synopsis if possible"""
+	"""Deletes a stored message. Delete the synopsis if possible"""
 	self.load_index()
 
 	if not self.index.has_key(mid): return
@@ -341,15 +383,21 @@
 	return (self.data['nSurbs'] > 2) and (self.data['idkey'] != None)
 
     def isUp(self):
+	"""Tells whether the account is up, ready to handle messages of the
+	nymholder"""
 	return self['up']
 
     def setUp(self):
+	"""Sets the up status"""
 	self['up'] = True
 
     def checkMessageSign(self, m, s):
+	"""Checks s is the signature of message m using sha1, RSA-OAEP and
+	the public key of this user"""
         return (s == _cr.pk_check_signature(_cr.sha1(m), self.idKey()))
 	    
     def checkChallenge(self, cr):
+	"""Checks the correctness of the challenge"""
         return (self['cr'] == cr)
 
     def encryptSyn(self, i, j):
@@ -387,7 +435,7 @@
 
 
     def markMid(self,l,mark):
-        """the status of the mids in l which had a status
+        """The status of the mids in l which had a status
         previous to mark in the lifeCycle are set to mark"""
         self.load_index()
         for e in l:
@@ -395,6 +443,7 @@
                 self.index[e]['status'] = mark
         
     def hasMail(self,mid):
+	"""Checks whether the server holds a mail for a given mid"""
         self.load_index()
         try:
             return self.index[mid]['status'] != lifeCycle['deleted']
@@ -402,8 +451,8 @@
             return False
         
     def sendList(self, num, after):
-        """returns a list of (ml, bf, synblob)
-        in the process of creating it ca modify the synbox,
+        """Returns a list of (ml, bf, synblob)
+        in the process of creating it can modify the synbox,
         doing some encryption"""
         def lhasMail(midIdx, l):
             return self.mbox.has_key(l[midIdx])

Modified: trunk/nym3/TODO
===================================================================
--- trunk/nym3/TODO	2004-11-28 17:40:42 UTC (rev 135)
+++ trunk/nym3/TODO	2004-12-17 00:16:14 UTC (rev 136)
@@ -11,7 +11,10 @@
 Notes:
  - In case something brake, remember it's emag's fault.
  - Use clean_surbs after sending something through mixminion
- 
+
+doc:
+ - add doc strings in __init__.py in every module.
+
 Message.py:
  - add the print method which prints the content of a command object in a user
    friendly form
@@ -23,11 +26,29 @@
  - delete expired surbs.
  - periodically scans for emails that need to be relayed.
 
+Common.py:
+ - move lifecycle to somewhere in the server, since the client should not use 
+ it
+
+Mail.py:
+ - Check whether oldestMid is well situated. (Shouldn't it be in Commons, or 
+ anyplace in the server module) In fact all the Mail.py module should go
+ in the server. Lolo : your module, your call :)
+
+Client.Main:
+ - Obviously, not much was done. Specify scenarios and write the parser +
+ empty functions that are supposed to do the work. Take a look at nickm's API
+ before doing that.
+
+Client.User:
+ - Look at the tag class. It was supposed to provide a nice shortcut for a user
+ to identify his accounts. But it might be just some dirty hack. Ignore it
+ to begin with, propose a nicer solution if type the whole account name is too
+ trouble some
+
 JR:
  o look at the crypto module of mixminion
  o what is the format of the keys
  o implement encrypt a mail, a set of synopses
  - implement sign a control message
  o figure what the routing type and info (cf Command relay) are
- X check the size of an ASN.1 encoded key 
- X get a life :)



More information about the Nym3-commit mailing list