[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