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

laurent at conuropsis.org laurent at conuropsis.org
Sat Sep 17 15:18:49 CEST 2005


Author: laurent
Date: 2005-09-17 15:18:46 +0200 (Sat, 17 Sep 2005)
New Revision: 307

Added:
   trunk/nymbaron/
   trunk/nymbaron/Client/Account.py
   trunk/nymbaron/Client/Keyring.py
   trunk/nymbaron/Client/Main.py
   trunk/nymbaron/Mail.py
   trunk/nymbaron/Message.py
   trunk/nymbaron/Server/Main.py
   trunk/nymbaron/Server/User.py
Removed:
   trunk/nym3/
   trunk/nymbaron/Client/Account.py
   trunk/nymbaron/Client/Keyring.py
   trunk/nymbaron/Client/Main.py
   trunk/nymbaron/Mail.py
   trunk/nymbaron/Message.py
   trunk/nymbaron/Server/Main.py
   trunk/nymbaron/Server/User.py
Log:
Complete the move nym3 -> nymbaron.


Copied: trunk/nymbaron (from rev 303, trunk/nym3)

Deleted: trunk/nymbaron/Client/Account.py
===================================================================
--- trunk/nym3/Client/Account.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Client/Account.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,495 +0,0 @@
-# $Id$
-# -*- coding: iso-8859-1 -*-
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Client.Acount
-
-    This package contains user account information representation on the client
-    side and ways to create, access to and modify them."""
-
-import copy
-import os
-import nym3.Client.Config as Config
-import nym3.Client.Keyring as Keyring
-import nym3.Mail as Mail
-import mixminion.Common
-import nym3.Common as Common
-import mixminion.Crypto as _cr
-import nym3.Message as Message
-import nym3.Crypto as Crypto
-#import mixminion.ClientAPI as mapi
-import pickle
-import tempfile
-import time
-import re
-#TODO debugging cruft associated to seqno display
-import binascii
-
-SEQNO_LEN = 20
-SURB_LEN = 2104
-
-class NoSuchAccount(Exception): pass
-"""Exception thrown when a user identified by her nym can't be found"""
-
-class AlreadySuchAccount(Exception): pass
-"""Exception thrown when a user identified by her nym already exists"""
-
-class TagMap:
-    """Contains mapping between idTag and nickname"""
-    def __init__(self, filename):
-	self.lock = None
-	self.id2nick = None
-	self.nick2id = None
-	self.filename = filename
-
-    def __del__(self):
-	if self.lock: self._release()
-
-    def _lock(self):
-        """Lock the tagmap file"""
-        self.lock = mixminion.Common.Lockfile(self.filename + '.lck')
-        self.lock.acquire()
-
-    def _release(self):
-        self.lock.release()
-	self.lock = None
-
-    def _tagfile(self):
-        return self.filename
-
-    def _load(self):
-	tag = self._tagfile()
-	try:
-	    f = open(tag, 'r')
-	    self.id2nick = pickle.load(f)
-	    f.close()
-	except IOError:
-	    self.id2nick = {}
-	self.nick2id = {}
-	for i in self.id2nick.keys():
-	    self.nick2id[self.id2nick[i]] = i
-
-    def _loadifneeded(self):
-	if self.id2nick == None:
-	    self._lock()
-	    self._load()
-	    self._release()
-        
-    def _save(self):
-        if self.id2nick == None: return
-	tagmap = self._tagfile()
-	f = open(tagmap, 'w')
-	pickle.dump(self.id2nick, f)
-	f.close()
-
-    def nickFromId(self, idTag):
-        self._loadifneeded()
-	try:
-	    return self.id2nick[idTag]
-	except KeyError:
-	    raise NoSuchAccount()
-
-    def idFromNick(self, nick):
-        self._loadifneeded()
-	try:
-	    return self.nick2id[nick]
-	except KeyError:
-	    raise NoSuchAccount()
-
-    def getnewId(self, nick):
-        self._lock()
-	self._load()
-	if self.nick2id.has_key(nick):
-	    self._release()
-	    raise AlreadySuchAccount()
-	while True:
-	    tag = Mail.genMid(8)
-	    rtag = Mail.mid2filename(tag)
-	    if not self.id2nick.has_key(rtag): break
-	self.nick2id[nick] = rtag
-	self.id2nick[rtag] = nick
-	self._save()
-	self._release()
-	return rtag
-
-class Account:
-    """Hold account data. Specifically, this means:
-
-	- idTag is the identity string used to generate SURBs for this
-	  account. It is therefore unique to this account. Additionnaly
-	  it is used to name the subdirectory holding this account's data
-	- 'handshake' tells how far we are in the account creation handshake,
-	  it is one of 'initiated', 'got_challenge', 'answered_challenge' or
-	  'completed'.
-	- misc parameters that needs further documentation.
-	- data is the hash that contains all of this
-	- datafile() is the file where we pickle from and to the data hash
-	- synbox is the synopsis box, stored in synboxfile()
-	  It is a hashtable that associates to a XNymSeq a t-uple
-	  (mid, flag_is_present_on server, synopsis)
-	  it may be represented by a string : the encrypted pickled hashtable
-	  (status = 'encrypted') or a hashtable (status = decrypted).
-	  When saved, it is put in the encrypted form.
-	- mbox is the couple of a hash mid to message and a list of the keys of
-	  the hash to order these keys, stored in mboxfile()
-	- a journal which is a hash of seqno -> encrypted (message, time sent)		  and is pickled from/to journalfile()
-	- the keys used by the account. The actual keys are stored in the
-	  Keyring, we only store the handles in the account. An account can
-	  have at most two identity keys at the same time, one active and one
-	  pending acknowledgment. The active key is named idKey (string), and
-	  the pending one is name pendingKey (string * seqno) and holds the
-	  sequence number of the message whose ack by the server would turn
-	  into the main idKey. The number of encryption keys is not limited,
-	  they are stored in the encKeys list, last generated first."""
-
-    def __init__(self, config, nickname, create = False):
-	"""Load from an existing account, or create a new, or fail"""
-	# Some data related to the account are loaded only on demand,
-	# this includes the journal, the mailbox, the synbox.
-	self.lock = None
-	self.succeeded = True
-	self.config = config
-	# If home directory does not exist, create it:
-	try:
-	    os.stat(config.path)
-	except OSError:
-	    os.makedirs(config.path)
-	tagmap = TagMap(config.path + os.sep + 'tagmap')
-	if create:
-	    self.data = {}
-	    self.succeeded = False
-	    self['idTag'] = tagmap.getnewId(nickname)
-	    # If the nick already existed, we're out of this because of
-	    # a thrown AlreadySuchAccount.
-	    # Create the dir that holds this account data
-	    self.base_path = config.path + os.sep + self['idTag']
-	    os.mkdir(self.base_path)
-	    self.data_status = 'dirty'
-	    self.journal = {}
-	    self.journal_status = 'dirty'
-	    self.mbox = {}, []
-	    self.mbox_status = 'dirty'
-	    self.synbox = {}
-	    self.synbox_status = 'dirty'
-	    self.synbox_enc_status = 'decrypted'
-	    self['encKeys'] = []
-	    self.idKey = None
-	    self.admKey = None 
-	    self.pendingKey = None
-	else:
-	    self.data_status = 'unloaded'
-	    self.journal_status = 'unloaded'
-	    self.mbox_status = 'unloaded'
-	    self.synbox_status = 'unloaded'
-	    idTag = tagmap.idFromNick(nickname)
-	    self.base_path = config.path + os.sep + idTag
-	    self._load_data()
-	    self.journal = None
-	    self.mbox = None
-	    self.synbox = None
-	    self.synbox_enc_status = None
-	self.succeeded = True
-	self._lock()
-
-    def generateSurbs(self, n):
-        """Generate surbs"""
-	# TODO : this should be done via ClientAPI.
-	surbdir = tempfile.mkdtemp()
-	surbspath = surbdir + os.sep + "surbs"
-	mixcall = "mixminion generate-surb -t %s -b -n %d -o %s --identity='%s'"
-	mixcall = mixcall % (self['returnaddress'], n, surbspath, self['idTag'])
-	os.system(mixcall)
-	f = open(surbspath, "r")
-	data = f.read()
-	f.close()
-	os.unlink(surbspath)
-	os.rmdir(surbdir)
-	return data
-
-    def __getitem__(self, key):
-	"""Gets user account field as in a hashtable"""
-	if self.data_status == 'unloaded': self._load_data()
-	return self.data[key]
-
-    def __setitem__(self, key, value):
-	"""Gets user account field as in a hashtable"""
-	self.data[key] = value
-	self.data_status = 'dirty'
-
-    def send(self, body):
-	# TODO : this should be done via ClientAPI.
-	(f, msgpath) = tempfile.mkstemp()
-	os.close(f)
-	f = open(msgpath, "w")
-	f.write(body)
-	f.close()
-	mixcall = "mixminion send -t %s -i %s" % \
-		  (self['servername'], msgpath)
-	os.system(mixcall)
-	# TODO : debugging cruft
-	#os.unlink(msgpath)
-	print "Raw control message left in " + msgpath
-
-    def mboxfile(self):
-	"""Gets the path of the mailbox"""
-	return self.base_path + os.sep + 'mbox'
-
-    def datafile(self):
-	"""Gets the path of the datafile"""
-	return self.base_path + os.sep + 'data'
-
-    def synboxfile(self):
-	"""Gets the path of the synbox"""
-	return self.base_path + os.sep + 'synbox'
-
-    def journalfile(self):
-	"""Gets the path of the journal"""
-	return self.base_path + os.sep + 'journal'
-
-    def _lock(self):
-	"""Locks the user. For well behaved functions."""
-	self.lock = mixminion.Common.Lockfile(self.config.path + os.sep + 
-		    self['idTag'] + '.lck')
-	self.lock.acquire()
-
-    def _release(self):
-	"""Releases the lock"""
-	self.lock.release()
-	self.lock = None
-
-    def homeDir(self):
-	"""Gets the home directory of this account"""
-        return self.base_path
-
-    def sendControl(self, l, idKey):
-	"""Sends a list of command messages to the nymserver"""
-        msg = Message.buildMessage(l)
-	seqno = self.get_seqno()
-	#TODO debugging cruft
-	print "send message: %s" % binascii.hexlify(seqno)
-        hdr = self.buildHeader(msg, seqno, idKey)
-	self.record(seqno, hdr + msg)
-	self.send(hdr + msg)
-
-    def buildHeader(self, msg, seqno, idKey):
-	"""Generates a header for a message using the identification key"""
-        h = Message.Header()
-        sig = _cr.pk_sign(_cr.sha1(chr(len(self['username'])) +
-				   self['username'] + seqno + msg), idKey)
-        h.fromData(self.data['username'], seqno, sig)
-        return str(h)
-
-    def get_admPubKey(self):
-	pubring = Keyring.Keyring(self.config.pubring_path, create = False)
-	pubring.decrypt("nym3")
-	return pubring.get_key(self['admKey'])
-    
-    def record(self, seqno, msg):
-	"""Store a control message in the journal"""
-	if self.journal_status == 'unloaded': self._load_journal()
-	clear = pickle.dumps((msg, int(time.time())))
-	key = self.get_admPubKey()
-	self.journal[seqno] = Crypto.nym_encrypt(clear, key)
-	self.journal_status = 'dirty'
-
-    def acknowledge(self, seqno_list):
-	if seqno_list == None or seqno_list == []: return
-	if self.journal_status == 'unloaded': self._load_journal()
-	for el in seqno_list:
-	    if self.journal.has_key(el):
-		del self.journal[el]
-		#TODO debugging cruft
-		print "acknowledged message : %s" % binascii.hexlify(el)
-		self.journal_status = 'dirty'
-
-    def get_seqno(self):
-	"""Return a previously unused sequence number"""
-	if self.journal_status == 'unloaded': self._load_journal()
-	ret = Mail.genMid(SEQNO_LEN)
-	while self.journal.has_key(ret):
-	    ret = Mail.genMid(SEQNO_LEN)
-	return ret
-
-    def _save_data(self):
-	"""Flushes the data to the disk"""
-	if not self.data_status == 'dirty': return
-	data = self.datafile()
-	f = open(data, 'w')
-	pickle.dump(self.data, f)
-	f.close()
-	self.data_status = 'ok'
-
-    def _save_mbox(self):
-	"""Flushes the mbox to the disk"""
-	if not self.mbox_status == 'dirty': return
-	mbox = self.mboxfile()
-	f = open(mbox, 'w')
-	pickle.dump(self.mbox, f)
-	f.close()
-	self.mbox_status = 'ok'
-
-    def _save_synbox(self):
-	"""Flushes the synbox to the disk"""
-	if not self.synbox_status == 'dirty': return
-	synbox = self.synboxfile()
-	f = open(synbox, 'w')
-	if self.synbox_enc_status == 'decrypted':
-	    key = self.get_admPubKey()
-	    s = Crypto.nym_encrypt(pickle.dumps(self.synbox), key)
-	else:
-	    assert self.synbox_enc_status == 'encrypted'
-	    s = self.synbox
-	f.write(s)
-	f.close()
-	self.synbox_status = 'ok'
-
-    def _save_journal(self):
-	"""Flushes the journal to the disk"""
-	if not self.journal_status == 'dirty': return
-	journal = self.journalfile()
-	f = open(journal, 'w')
-	pickle.dump(self.journal, f)
-	f.close()
-	self.journal_status = 'ok'
-
-    def __del__(self):
-	"""Flushes the user account to the disk"""
-	if not self.succeeded: return
-        self._save_synbox()
-        self._save_mbox()
-	self._save_journal()
-        self._save_data()
-        if self.lock: self._release()
-
-    def _load_mbox(self):
-	"""Loads the mailbox from the disk"""
-	if not self.mbox_status == 'unloaded': return
-	mbox = self.mboxfile()
-	try:
-	    f = open(mbox, 'r')
-	    self.mbox = pickle.load(f)
-	    f.close()
-	except IOError:
-	    self.mbox = {}
-	self.mbox_status = 'ok'
-
-    def _load_data(self):
-	"""Loads the datafile from the disk"""
-	if not self.data_status == 'unloaded': return
-	datafile = self.datafile()
-	try:
-	    f = open(datafile, 'r')
-	    self.data = pickle.load(f)
-	    f.close()
-	except IOError:
-	    self.data = {}
-	self.data_status = 'ok'
-
-    def _load_synbox(self):
-	"""Loads the synbox from the disk"""
-	if not self.synbox_status == 'unloaded': return
-	synbox = self.synboxfile()
-	try:
-	    f = open(synbox, 'r')
-	    self.synbox = f.read()
-	    self.synbox_enc_status = 'encrypted'
-	    f.close()
-	except IOError:
-	    self.synbox = {}
-	    self.synbox_enc_status = 'decrypted'
-	self.synbox_status = 'ok'
-
-    def _load_journal(self):
-	"""Loads the journal from the disk"""
-	if not self.journal_status == 'unloaded': return
-	journal = self.journalfile()
-	try:
-	    f = open(journal, 'r')
-	    self.journal = pickle.load(f)
-	    f.close()
-	except IOError:
-	    self.journal = {}
-	self.journal_status = 'ok'
-
-    def _decrypt_synbox(self, secring):
-	"""After a call to this method synbox is loaded and decrypted.
-	secring contains the private admKey"""
-	self._load_synbox()
-	if self.synbox_enc_status == 'decrypted': return
-	assert self.synbox_enc_status == 'encrypted'
-	key = secring.get_key(self['admKey'])
-	self.synbox = pickle.loads(Crypto.nym_decrypt(self.synbox, key))
-	self.synbox_enc_status = 'decrypted'
-	
-    def get_synbox(self, secring):
-	"""return a copy of the synbox, trying to decrypt it if it is encrypted
-	"""
-	self._decrypt_synbox(secring)
-	return copy.deepcopy(self.synbox)
-
-    def get_mbox(self):
-	"""return a copy of the mbox"""
-	self._load_mbox()
-	return copy.deepcopy(self.mbox)
-
-    def get_journal(self):
-	"""return a copy of the journal"""
-	self._load_journal()
-	return copy.deepcopy(self.journal)
-
-    def add_syn(self, secring, xnymseq, mid, flag, syn):
-	"""add a set of syn to the"""
-	self._decrypt_synbox(secring)
-	self.synbox[str(xnymseq)] = (mid, flag, syn)
-	self.synbox_status = "dirty"
-
-    def delete_syn(self, secring, mid):
-	self._decrypt_synbox(secring)
-	l = []
-	for i, (m, flag, syn) in self.synbox.iteritems():
-	    if m == mid:
-		l.append(i)
-	if len(l) > 0:
-	    self.synbox_status = "dirty"
-	for i in l:
-	    del(self.synbox[i])
-		
-    def add_msg(self, mid, msg):
-	self._load_mbox()
-	#this ensures there is no more than one occurence of a mid in
-	#the list
-	if self.mbox[0].has_key(mid):
-	    self.mbox[1].remove(mid)
-	self.mbox[0][mid] = msg
-	self.mbox[1].append(mid)
-	self.mbox_status = "dirty"
-
-    def delete_msg(self, mid):
-	self._load_mbox()
-	if self.mbox[0].has_key(mid):
-	    del(self.mbox[0][mid])
-	    self.mbox[1].remove(mid)
-	    self.mbox_status = "dirty"
-    
-    def add_enckey(self, key):
-	self['encKeys'].insert(0, key)

Copied: trunk/nymbaron/Client/Account.py (from rev 306, trunk/nym3/Client/Account.py)

Deleted: trunk/nymbaron/Client/Keyring.py
===================================================================
--- trunk/nym3/Client/Keyring.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Client/Keyring.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,108 +0,0 @@
-# $Id$
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Client.KeyRing
-
-    This package contains the implementation of a Keyring designed to store
-    keys in files protected by a password and to easily access those keys."""
-
-import pickle
-import random
-import nym3.Mail as Mail
-from mixminion.Crypto import sha1, ctr_crypt, AES_KEY_LEN, DIGEST_LEN
-
-SALT_LEN = 8
-
-class NewKeyring(Exception): pass
-
-class Keyring:
-    """Class that holds a user keyring.
-       Keys are stored in a file and are accessed via an abstract
-       handle (a string)."""
-       # TODO : this would need locking. Somewhere.
-
-    def __init__(self, keyfile, create = False):
-	self.keyfile = keyfile
-	self.status = 'encrypted'
-	try:
-	    f = open(keyfile, 'r')
-	    self.datastring = f.read()
-	    f.close()
-	except IOError:
-	    if create:
-		f = open(keyfile, "w")
-		f.close()
-		self.data = {}
-		self.status = "clear"
-	    else: raise NewKeyring()
-
-    def _get_unused_handle(self):
-	handle = "42"
-	while self.data.has_key(handle):
-	    handle = Mail.genMid(8)
-	return handle
-
-    def store(self, key):
-	handle = self._get_unused_handle()
-	self.data[handle] = key
-	return handle
-
-    def update_key(self, handle, key):
-	self.data[handle] = key
-
-    def get_key(self, handle):
-	return self.data[handle]
-
-    def save(self, passphrase):
-	"""Save the current keyring to file"""
-	salt = ""
-	for i in range(0, SALT_LEN):
-	    salt = salt + chr(random.randint(0, 255))
-	key = sha1(salt + passphrase + salt)[:AES_KEY_LEN]
-	clear = pickle.dumps(self.data)
-	digest = sha1(clear + salt)
-	encrypted = ctr_crypt(clear + digest, key)
-	try:
-	    f = open(self.keyfile, 'w')
-	    f.write(salt + encrypted)
-	    f.close
-	except IOError:
-	    raise "Duh"
-
-    def decrypt(self, passphrase):
-	"""Decrypt the keyring"""
-	salt = self.datastring[:SALT_LEN]
-	key = sha1(salt + passphrase + salt)
-	key = key[:AES_KEY_LEN]
-	clear = ctr_crypt(self.datastring[SALT_LEN:], key)
-	digest = clear[-DIGEST_LEN:]
-	clear = clear[:-DIGEST_LEN]
-	if sha1(clear + salt) != digest:
-	    return False
-	self.data = pickle.loads(clear)
-	self.status = 'clear'
-	self.passphrase = passphrase
-	return True
-

Copied: trunk/nymbaron/Client/Keyring.py (from rev 306, trunk/nym3/Client/Keyring.py)

Deleted: trunk/nymbaron/Client/Main.py
===================================================================
--- trunk/nym3/Client/Main.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Client/Main.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,856 +0,0 @@
-# $Id$
-# -*- coding: iso-8859-1 -*-
-#
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Client.Main
-
-    This package contains the implementation of a nym3 command line client."""
-
-import sys
-import os
-import string
-import binascii
-import termios
-from optparse import OptionParser, make_option
-from nym3.Message import SToCCODE
-import nym3.Client.Account as Account
-import nym3.Client.Config as Config
-import nym3.Message as Message
-import nym3.Common as Common
-import nym3.Mail as Mail
-import nym3.Crypto as Crypto
-import nym3.Client.Keyring as Keyring
-import mixminion.Crypto as _cr
-import mixminion.ClientDirectory as _cl
-
-usage_string = """Usage: nym3 <command> [arguments]
-where <command> is one of:
-    \tcreate\t\tCreate a new account
-    \tsend\t\tSend an anonymous mail with your nym
-    \tsummarize\tRetrieve email summaries
-    \tdelete\t\tRequest email deletion
-    \tget\t\tRetrieve email
-    \tprocess\t\tProcess returning server messages
-    \tlist-syn\tList already fetched summaries
-    \tdump-syn\tDump already fetched summaries
-    \tlist-mbox\tList already fetched emails
-    \tlist-journal\tList commands recorded in the journal (sent but not
-    \t\t\tacknowledged yet)
-    \tsend-surb\tSend SURBs
-    \texport\t\tExport already fetched emails to a file
-    \tresend-command\tResend a command that has been sent but not acknowledged
-    \t\t\tyet
-    \tldelete\tDelete messages from the local mbox
-    \tldelete-syn\tDelete synopses from the local synbox"""
-
-class CLI:
-    def __init__(self):
-	pass
-
-    def prompt(self, s):
-	try:
-	    return raw_input(s + ": ")
-	except EOFError:
-	    return ""
-	
-    def promptmultiline(self, s):
-	res = []
-	while True:
-	    if len(res) == 0:
-		nym = self.prompt(s)
-	    else:
-		nym = raw_input()
-	    if nym == "":
-		break
-	    else:
-		res.append(nym)
-	return res
-	    
-    def prompthidden(self, s):
-	fd = sys.stdin.fileno()
-	old = termios.tcgetattr(fd)
-	new = termios.tcgetattr(fd)
-	new[3] = new[3] & ~termios.ECHO          # lflags
-	try:
-	    termios.tcsetattr(fd, termios.TCSADRAIN, new)
-	    passwd = self.prompt(s)
-	finally:
-	    termios.tcsetattr(fd, termios.TCSADRAIN, old)
-	print ''	
-	return passwd
-
-    def display(self, s):
-	print s
-
-    def promptblock(self, s):
-	print s
-	return sys.stdin.read()
-
-def decode_secring(config, ui):
-    secring = Keyring.Keyring(config.secring_path)
-    ui.display("You need to provide your passphrase to unlock your keyring")
-    while True:
-	passphrase1 = ui.prompthidden("Passphrase")
-	if secring.decrypt(passphrase1): break
-	ui.display("wrong passphrase")
-    return secring
-
-def decipher_string(string, keyring, key_handles):
-    """Wrapper around Crypto.nym_decrypt which tries every available
-       keys in keyring[key_handles] until one is able to decrypt"""
-    clear = None
-    for k in key_handles:
-	enckey = keyring.get_key(k)
-	try:
-	    clear = Crypto.nym_decrypt(string, enckey)
-	    break
-	except: pass
-    return clear
-
-def get_account_from_nickname(ui, config, nickname, fail_none):
-    """Test if the nickname is different from None. If not display fail_none
-    on the ui and exit. Then try to get an existing Account for this nickname.
-    If none can be found, display a message on the ui and exit,
-    else return it"""
-    if not nickname:
-	ui.display(fail_none)
-	sys.exit(1)
-    try:
-	return Account.Account(config, nickname)
-    except Account.NoSuchAccount:
-	ui.display("No account relative to the provided nickname, abort")
-	sys.exit(1)
-
-def build_syn_index(ui, config, account):
-    """Builds the index of synopses of the given account, using ui to get
-    the password to decrypt the keyring. Returns a hash containing the
-    correspondance index -> mid, where index is a XNymSeq as a str"""
-    secring = decode_secring(config, ui)
-    synbox = account.get_synbox(secring)
-    index = {}
-    for xnymseq, (mid, flag, syn) in synbox.iteritems():
-	index[xnymseq] = mid
-    return index
-
-def build_mbox_index(account):
-    """Builds the index of the mbox of the given account. Returns a hash
-    containing the correspondance index -> mid, index as a str"""
-    mbox, index = account.get_mbox()
-    return dict([(str(i), j) for (i, j) in enumerate(index)])
-
-def build_journal_index(ui, config, account):
-    """Builds the index of the journal of the given account. Returns a hash
-    containing the correspondance index -> seqno, index as a str"""
-    journal = account.get_journal()
-    secring = decode_secring(config, ui)
-    key = secring.get_key(account['admKey'])
-    l = []
-    for seqno in journal.keys():
-	clear = Crypto.nym_decrypt(journal[seqno], key)
-	m, t = pickle.loads(clear)
-	l.append((seqno, m, t))
-    l.sort(journal_time_cmp)
-    return dict([(str(i), s) for (i, (s, m, t)) in enumerate(l)])
-
-def is_hex_mid(s):
-    try:
-	ds = binascii.unhexlify(s)
-    except TypeError:
-	return False
-    return len(ds) == Common.midLength
-
-class DecodeException(Exception):
-    """Exception raised by a decoding function in case of error"""
-    pass
-
-def decode_message_references(refs, keys, h):
-    """Generate a list of mids from a list of message references, refs,
-    a list of keys and a hashtable.
-    Return the list of mid if all the mids can be decoded.
-    Throw a DecodeException with as argument a string describing the cause of
-    error. 
-    
-    A reference is either a mid or a string of the format <key>:<val>,
-    where key is in keys and does not contain the caracter ':'.
-    h is a hashtable: h[<key>] contains a 2-uple (f, a)
-    where f is a function to call with the arguments in the tuple a to
-    get a hash h'. h'[s] contains the mid corresponding to the string s
-    for the key <key>."""
-
-    #we chose this signature so that we can have lazy loading of the
-    #correspondance table.
-    l = []
-    loaded_h = {}
-    if refs == None:
-	return []
-    for e in refs:
-	if ':' not in e:
-	    if is_hex_mid(e):
-		l.append(binascii.unhexlify(e))
-	    else:
-		raise DecodeException("%s: not a valid mid" % e)
-	else:
-	    s = e.split(':', 1)
-	    if s[0] not in keys:
-		raise DecodeException("%s: not a valid index key" % s[0])
-	    try:
-		if not loaded_h.has_key(s[0]):
-		    if not h.has_key(s[0]):
-			raise DecodeException("%s: no correspondance table " +
-					"callback provided for this key" % s[0])
-		    f, a = h[s[0]]
-		    try:
-			loaded_h[s[0]] = f(*a)
-		    except:
-			raise DecodeException("%s: defective callback " +
-					"provided for this key" % s[0])
-		e2 = loaded_h[s[0]][s[1]]
-		if len(e2) == Common.midLength:
-		    l.append(e2)
-		else:
-		    raise DecodeException("%s: dereferenced value not " +
-					"a valid mid" % e)
-	    except KeyError:
-		raise DecodeException("%s: not a valid index for key %s" % 
-					(s[1], s[0]))
-    return l
-
-def processMessage(msg, config, ui, nickname):
-    """process incoming control message"""
-      
-    sr = Message.StrReader(msg)
-    comList = sr.readCommandSToCList()
-    try:
-	account = Account.Account(config, nickname)
-    except Account.NoSuchAccount:
-	ui.display("%s: no account corresponding to this nickname" % nickname)
-	sys.exit(1)
-    for com in comList:
-	if (com.ct() == SToCCODE['Created']):
-	    # No challenge/response scheme decided yet:
-	    account['handshake'] = 'completed'
-	    account['username'] = com.nym
-	    ui.display("Account " + nickname + " successfully registered"
-		       " with username " + com.nym)
-	elif (com.ct() == SToCCODE['Status']):
-	    #update the fields
-	    account['nMsg'] = com.nMsg
-	    account['nSurb'] = com.nSurb
-	    account['quota'] = com.quota
-	    account['used'] = com.used
-	    #remove from the journal the acknowledged messages
-	    account.acknowledge(com.acks)
-	    # TODO raise some warnings, exec some automatic actions and warn
-	    # the user
-	elif (com.ct() == SToCCODE['Summary']):
-	    # We need to decrypt the set of synopsis to retrieve the MIDs
-	    try:
-		secring = decode_secring(config, ui)
-	    except Keyring.NewKeyring:
-	    # The Keyring is new. That shouldn't happen
-		raise Exception('Bug keyring?')
-	    clear = decipher_string(com.synSet, secring, account['encKeys'])
-	    if not clear:
-		ui.display("Could not decrypt synopsis")
-		sys.exit(1)
-	    index = 0
-	    bflist = Mail.bf2list(com.bf)
-	    while len(clear) > 0:
-		mid = clear[:20]
-		synlen = Message.strToIntBE(clear[20:22])
-		syn = clear[22:22 + synlen]
-		clear = clear[22 + synlen:]
-		try:
-		    xnymseq = Mail.XNymSeq(syn)
-		except:
-		    #there was a problem determining the XNymSeq: bug
-		    #TODO is it the right play?
-		    raise Exception("unable to determine XNymSeq")
-		account.add_syn(secring, xnymseq, mid, index in bflist, syn)
-		index += 1
-	elif (com.ct() == SToCCODE['Msg']):
-	    account.add_msg(com.mid, com.msg)
-	elif (com.ct() == SToCCODE['Dropped']):
-	    pass
-	elif (com.ct() == SToCCODE['Error']):
-	    pass
-
-def setupAccount(config, ui, serverName = None, usernamelist = None,
-		 emailAddress = None, nickname = None):
-    """Tries to setup a new account on the given server"""
-    if not nickname:
-	nickname = ui.prompt("How do you want to name this account")
-    while True: # chose a suitable account name
-	account = None
-	try:
-	    account = Account.Account(config, nickname, create = True)
-	except Account.AlreadySuchAccount:
-	    ui.display("This account name is already in use")
-	    nickname = ui.prompt("New account name")
-	if account: break
-    account['nickname'] = nickname
-    if not serverName:
-	serverName = ui.prompt("At which server do you want to register"
-			       " this account")
-    account['servername'] = serverName
-    if not usernamelist:
-	ui.display("You need a username for this account.")
-	usernamelist = ui.promptmultiline("Enter several choices, one per line,"
-				 " finish by a blank line")
-    account['username'] = "" # Not yet created
-    if not emailAddress:
-	emailAddress = ui.prompt("What is the default email address for "
-				 "returning messages")
-    account['returnaddress'] = emailAddress
-    ui.display("Please wait, generating keys for this account...")
-    idKey = _cr.pk_generate(bits=config.idkey_length)
-    encKey = _cr.pk_generate(bits=config.enckey_length)
-    admKey = _cr.pk_generate()
-    # We have gathered the relevant information for this account, except for
-    # the policy which we don't let the user change at this point for the sake
-    # of simplicity. So, let's store all of that in the account and prepare the
-    # message for the server.
-    pubring = None
-    secring = None
-    try:
-	pubring = Keyring.Keyring(config.pubring_path, create = True)
-	pubring.decrypt("nym3")
-    except: pass
-    try:
-	secring = Keyring.Keyring(config.secring_path)
-	ui.display("You need to provide your passphrase to unlock your keyring")
-	while True:
-		passphrase1 = ui.prompthidden("Passphrase")
-		if secring.decrypt(passphrase1): break
-		ui.display("wrong passphrase")
-    except Keyring.NewKeyring:
-	# The Keyring is new. We need to ask the user for a password.
-	# Twice.
-	while True:
-	    passphrase1 = ui.prompthidden("Please enter a passphrase to"
-					  " protect your secret keyring")
-	    passphrase2 = ui.prompthidden("Again")
-	    if passphrase1 == passphrase2: break
-	    # TODO : warn for an empty passphrase.
-	    ui.display("Passphrases do not match.")
-	secring = Keyring.Keyring(config.secring_path, create = True)
-    
-    idtag = secring.store(_cr.pk_encode_private_key(idKey))
-    enctag = secring.store(_cr.pk_encode_private_key(encKey))
-    admtag = secring.store(_cr.pk_encode_private_key(admKey))
-    account.add_enckey(enctag)
-    account['idKey'] = idtag
-    account['admKey'] = admtag
-    pubring.update_key(idtag, _cr.pk_encode_public_key(idKey))
-    pubring.update_key(enctag, _cr.pk_encode_public_key(encKey))
-    pubring.update_key(admtag, _cr.pk_encode_public_key(admKey))
-    secring.save(passphrase1)
-    pubring.save("nym3")
-    createc = Message.Create()
-    createc.fromData(usernamelist, "")
-    newpkc = Message.Newpk()
-    newpkc.fromData(idKey, encKey)
-    surbc = Message.Surb()
-    surbc.fromData(account.generateSurbs(7))
-    account.sendControl([createc, newpkc, surbc], idKey)
-    account['handshake'] = "initiated"
-
-def relayMessage(ui, config, nickname = None, to = None, input = None):
-    body = None
-    exAdr = None
-    if not to:
-	ui.display("No destination given, abort")
-	ui.display("Use -t <dest>")
-	sys.exit(1)
-    try:
-        exAdr = _cl.parseAddress(to)
-    except _cl.ParseError:
-	ui.display("Address format unknown")
-	#TODO add here the supported formats?
-	sys.exit(1)
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    if input:
-	try:
-	    fd = open(input)
-	    body = fd.read()
-	    fd.close()
-	except IOError:
-	    ui.display("Unable to read message from input file")
-	    sys.exit(1)
-    else:
-	try:
-	    body = ui.promptblock("Enter your message:")
-	except IOError:
-	    ui.display("Unable to read message from stdin")
-	    sys.exit(1)
-    rt, ri, lh = exAdr.getRouting()
-    relayc = Message.Relay()
-    relayc.fromData(Message.intToStrBE(rt, 2), ri, body)
-    try:
-	secring = decode_secring(config, ui)
-    except Keyring.NewKeyring:
-	# The Keyring is new. That shouldn't happen
-	raise Exception('Bug keyring?')
-    idKey = _cr.pk_decode_private_key(secring.get_key(account['idKey']))
-    account.sendControl([relayc], idKey)
-
-def Summarize(ui, config, nickname = None, max = None, older = None):
-    if not max: max = config.max_syn
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    if older:
-	if is_hex_mid(older):
-	    older = binascii.unhexlify(older)
-	else:
-	    try:
-		l = decode_message_references([older], ['syn'],
-		    {'syn': (build_syn_index, (ui, config, account))})
-	    except DecodeException, inst:
-		ui.display(str(inst))
-		sys.exit(1)
-	    if len(l) == 1:
-		older = l[0]
-	    else:
-		#The passed reference didn't correspond to a valid mid, abort
-		ui.display('the reference passed did not correspond to a ' +
-		'valid mid, abort')
-		sys.exit(1)		
-    else:
-	#no older given, defaults to the oldest mid
-	older = chr(0) * 20
-    summ = Message.Summarize()
-    summ.fromData(max, older)
-    try:
-	secring = decode_secring(config, ui)
-    except Keyring.NewKeyring:
-	# The Keyring is new. That shouldn't happen
-	raise Exception('Bug keyring?')
-    idKey = _cr.pk_decode_private_key(secring.get_key(account['idKey']))
-    account.sendControl([summ], idKey)
-
-def Delete(ui, config, nickname = None, midlist = None):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")   
-    delmsg = Message.Delete()
-    try:
-	midlist = decode_message_references(midlist, ['syn'],
-	        {'syn': (build_syn_index, (ui, config, account))})
-    except DecodeException, inst:
-	ui.display(str(inst))
-	sys.exit(1)
-    delmsg.fromData(midlist)
-    try:
-	secring = Keyring.Keyring(config.secring_path)
-	ui.display("You need to provide your passphrase to unlock your keyring")
-	while True:
-		passphrase1 = ui.prompthidden("Passphrase")
-		if secring.decrypt(passphrase1): break
-		ui.display("wrong passphrase")
-    except Keyring.NewKeyring:
-	# The Keyring is new. That shouldn't happen
-	raise Exception('Bug keyring?')
-    idKey = _cr.pk_decode_private_key(secring.get_key(account['idKey']))
-    account.sendControl([delmsg], idKey)
-
-def Get(ui, config, nickname = None, midlist = None):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    getmsg = Message.Get()
-    try:
-	midlist = decode_message_references(midlist, ['syn'],
-	        {'syn': (build_syn_index, (ui, config, account))})
-    except DecodeException, inst:
-	ui.display(str(inst))
-	sys.exit(1)
-    getmsg.fromData(midlist)
-    try:
-	secring = Keyring.Keyring(config.secring_path)
-	ui.display("You need to provide your passphrase to unlock your keyring")
-	while True:
-		passphrase1 = ui.prompthidden("Passphrase")
-		if secring.decrypt(passphrase1): break
-		ui.display("wrong passphrase")
-    except Keyring.NewKeyring:
-	# The Keyring is new. That shouldn't happen
-	raise Exception('Bug keyring?')
-    idKey = _cr.pk_decode_private_key(secring.get_key(account['idKey']))
-    account.sendControl([getmsg], idKey)
-
-def resend_command(ui, config, nickname = None, seqnolist = None):
-    #TODO avoid the double input of passwd (in build_journal_index and
-    #decode_secring)
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    try:
-	#TODO ! this works only because seqNoLength == midLength
-	#modify the sugnature of decode message to take the length of
-	#the items to decode as an argument?
-	seqnolist = decode_message_references(seqnolist, ['journal'],
-	        {'journal': (build_journal_index, (ui, config, account))})
-    except DecodeException, inst:
-	ui.display(str(inst))
-	sys.exit(1)
-    secring = decode_secring(config, ui)
-    key = secring.get_key(account['admKey'])
-    for seqno in seqnolist:
-	clear = Crypto.nym_decrypt(journal[seqno], key)
-	m, t = pickle.loads(clear)
-	account.send(m)
-	account.record(seqno, m)
-	ui.display("resend message seqno: %s" % binascii.hexlify(seqno))
-    
-def SendSurb(ui, config, nickname = None, number = 42):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    try:
-	secring = decode_secring(config, ui)
-    except Keyring.NewKeyring:
-	# The Keyring is new. That shouldn't happen
-	raise Exception('Bug keyring?')
-    idKey = _cr.pk_decode_private_key(secring.get_key(account['idKey']))
-    surbc = Message.Surb()
-    surbc.fromData(account.generateSurbs(number))
-    account.sendControl([surbc], idKey)
-
-def list_syn(ui, config, nickname = None, summarize = True):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    secring = decode_secring(config, ui)
-    synbox = account.get_synbox(secring)
-    mbox = account.get_mbox()[0]
-    for xnymseq, (mid, flag, syn) in synbox.iteritems():
-	if mbox.has_key(mid):
-	    avail = "email available"
-	elif flag:
-	    avail = "email available on server"
-	else:
-	    avail = "email not available"
-	ui.display("%s %s %s" % (xnymseq, binascii.hexlify(mid), avail))
-	if summarize:
-	    ui.display(Mail.syn_summary(syn))
-	else:
-	    ui.display(syn)
-
-def list_mbox(ui, config, nickname = None):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    mbox, mids = account.get_mbox()
-    secring = decode_secring(config, ui)
-    for i, mid in enumerate(mids):
-	clear = decipher_string(mbox[mid], secring, account['encKeys'])
-	ui.display("%d %s" % (i, binascii.hexlify(mid)))
-	ui.display(Mail.syn_summary(clear))
-
-def journal_time_cmp(a, b):
-    seqnoa, msga, ta = a
-    seqnob, msgb, tb = b
-    return ta < tb
-
-def list_journal(ui, config, nickname = None):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    journal = account.get_journal()
-    secring = decode_secring(config, ui)
-    key = secring.get_key(account['admKey'])
-    l = []
-    for seqno in journal.keys():
-	clear = Crypto.nym_decrypt(journal[seqno], key)
-	m, t = pickle.loads(clear)
-	l.append((seqno, m, t))
-    l.sort(journal_time_cmp)
-    for i, (s, m, t) in enumerate(l):
-	ui.display("%d %s %s" % (i, binascii.hexlify(s), str(t)))
-	#TODO display the content of the command
-	#ui.display()
-
-def export(ui, config, nickname, output = None, args = []):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    output_f = sys.stdout
-    secring = decode_secring(config, ui)
-    mbox = account.get_mbox()[0]
-    args = decode_message_references(args, ['mbox'],
-	        {'mbox': (build_mbox_index, (account,))})
-    if output != None:
-	try:
-	    output_f = open(output, "w")
-	except IOError:
-	    ui.display("Unable to write in output file, abort")
-	    sys.exit(1)
-    for mid in args:
-	if mbox.has_key(mid):
-	    clear = decipher_string(mbox[mid], secring, account['encKeys'])
-	    output_f.write(clear)
-	else:
-	    ui.display("%s: no email relative to that mid" % 
-						    binascii.hexlify(mid))
-    if output_f != sys.stdout:
-	output_f.close()
-    else:
-	sys.stdout.flush()
-
-def ldelete(ui, config, nickname, args = []):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    mids = decode_message_references(args, ['mbox'],
-	        {'mbox': (build_mbox_index, (account,))})
-    for mid in mids:
-	account.delete_msg(mid)
-
-def ldelete_syn(ui, config, nickname, args = []):
-    account = get_account_from_nickname(ui, config, nickname,
-	    "No nickname given, abort\nUse -n <nickname>")
-    mids = decode_message_references(args, ['syn'],
-	        {'syn': (build_syn_index, (ui, config, account))})
-    secring = decode_secring(config, ui)
-    for mid in mids:
-	account.delete_syn(secring, mid)
-
-def main(args):
-    if len(args) < 2:
-	print usage_string
-	sys.exit(1)
-    if args[1] == "create":
-	parser = OptionParser()
-	parser.add_option("-s", "--server", action = "store", dest = "server",
-			  help = "Specify the server")
-	parser.add_option("-u", "--username", action = "store",
-			  dest = "username", 
-			  help = "Specify usernames to attempt to register in"
-			  " the form user1:user2:user3")
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "How to refer to this "
-			  "account locally")
-	parser.add_option("-e", "--email", help = "Specify the destination "
-			  "email address for returning surbs", action = "store",
-			  dest = "email")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	myusernamelist = []
-	if options.username:
-	    myusernamelist = string.split(options.username, ":")
-	config = Config.Config() # TODO load from file
-	setupAccount(config, ui, serverName = options.server, 
-		     usernamelist = myusernamelist,
-		     emailAddress = options.email, nickname = options.nickname)
-	sys.exit(0)
-
-    if args[1] == "send":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname refering "
-			  "to the account used to send a message")
-	parser.add_option("-t", "--to", help = "Specify the recipient's "
-			  "address", action = "store", dest = "to")
-	parser.add_option("-i", "--input", action = "store",
-			dest = "input", help = "The file to read the message "
-			"from (defaults to stdin)")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	relayMessage(ui, config, options.nickname, options.to, options.input)
-	sys.exit(0)
-
-    if args[1] == "summarize":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose summaries to request")
-	parser.add_option("-m", "--max", action = "store",
-			  dest = "max", help = "Maximum number of summaries"
-			  " to request", type = "int")
-	parser.add_option("-o", "--older", action = "store",
-			  dest = "older", help = "Retrieve only summaries "
-			  "older than msgid")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	Summarize(ui, config, options.nickname, options.max, options.older)
-	sys.exit(0)
-
-    if args[1] == "delete":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request deletion")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	Delete(ui, config, options.nickname, args)
-	sys.exit(0)
-
-    if args[1] == "get":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	Get(ui, config, options.nickname, args)
-	sys.exit(0)
-
-    if args[1] == "send-surb":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose surbs to replenish")
-	parser.add_option("-N", "--number", action = "store",
-			  dest = "number", help = "The number "
-			  "of SURBs to send", type = "int", default = 42)
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	SendSurb(ui, config, options.nickname, options.number)
-	sys.exit(0)
-
-    if args[1] == "process":
-	parser = OptionParser()
-	parser.add_option("-i", "--idtag", action = "store",
-			  dest = "idtag", help = "The idTag contained in the"
-			  "enclosing SURB")
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname of the"
-			  "account to use")
-	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:])
-	ui = CLI()
-	if (not options.idtag) and (not options.nickname):
-	    ui.display("Must provide an idTag or a nickname")
-	    sys.exit(1)
-	config = Config.Config() # TODO load from file
-	if options.nickname: nick= options.nickname
-	else:
-	    tm = Account.TagMap(config.path + os.sep + 'tagmap')
-	    nick = tm.nickFromId(options.idtag)
-	if options.file:
-	    try:
-		f = open(options.file, "r")
-		msg = f.read()
-		f.close()
-	    except IOError:
-		ui.display("Can't read requested file")
-		sys.exit(1)
-	else: msg = sys.stdin.read()
-	processMessage(msg, config, ui, nick)
-	sys.exit(0)
-
-    if args[1] == "list-syn":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose synopsis to list")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	list_syn(ui, config, options.nickname)
-	sys.exit(0)
-
-    if args[1] == "dump-syn":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose synopsis to dump")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	list_syn(ui, config, options.nickname, summarize = False)
-	sys.exit(0)
-
-    if args[1] == "list-mbox":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	list_mbox(ui, config, options.nickname)
-	sys.exit(0)
-
-    if args[1] == "list-journal":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			    dest = "nickname", help = "The nickname "
-			    "of the account whose journal to list")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	list_journal(ui, config, options.nickname)
-	sys.exit(0)
-
-    if args[1] == "resend-command":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	resend_command(ui, config, options.nickname, args)
-	sys.exit(0)
-
-    if args[1] == "export":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	parser.add_option("-o", "--output", action = "store",
-			  dest = "output", help = "The file to export the " +
-			  "message to, stdout if omitted")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	export(ui, config, options.nickname, options.output, args)
-	sys.exit(0)
-
-    if args[1] == "ldelete":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	ldelete(ui, config, options.nickname, args)
-	sys.exit(0)
-
-    if args[1] == "ldelete-syn":
-	parser = OptionParser()
-	parser.add_option("-n", "--nickname", action = "store",
-			  dest = "nickname", help = "The nickname "
-			  "of the account whose emails to request")
-	(options, args) = parser.parse_args(args[2:])
-	ui = CLI()
-	config = Config.Config()
-	ldelete_syn(ui, config, options.nickname, args)
-	sys.exit(0)
-
-
-if __name__ == '__main__':
-    main(sys.argv)
-    sys.exit(0)

Copied: trunk/nymbaron/Client/Main.py (from rev 306, trunk/nym3/Client/Main.py)

Deleted: trunk/nymbaron/Mail.py
===================================================================
--- trunk/nym3/Mail.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Mail.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,208 +0,0 @@
-# $Id$
-# -*- coding: utf-8 -*-
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Mail
-
-    This package contains utilities to process mails."""
-
-import re
-import os
-import tempfile
-import random
-import base64
-import string
-import email.Parser
-import nym3.Common as Common
-
-slen = 128
-"""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"""
-
-forbidden_headers = ['from', 'received', 'sender', 'reply-to', 'return-path',
-                    'user-agent']
-"""The list of headers that are removed from a message before it is relayed"""
-#TODO complete this list, how do we process msgid? remove and add another one?
-
-class SeqNotFound(Exception): pass 
-
-def synopsize(msg, seq):
-    """Synopsizes a message : keeps a set of headers and the beginning of
-    the body of the mail
-    seq is the sequence number of the generated synopsis"""
-    #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']
-    par = email.Parser.HeaderParser() 
-    message = par.parsestr(msg)
-    res = ''
-    print message.keys()
-    for i in vheaders:
-	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 + '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]
-    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
-    return res
-
-def syn_summary(synopsis):
-    """returns a summary for the provided synopsis.
-    """
-    par = email.Parser.HeaderParser()
-    syn = par.parsestr(synopsis)
-    from_f = syn['From']
-    subj_f = syn['Subject']
-    if from_f == None:
-	from_f = ""
-    if subj_f == None:
-	subj_f = ""
-    return "From: %s\nSubject: %s" % (from_f, subj_f)
-
-def XNymSeq(synopsis):
-    """returns an integer, the sequence number of the provided synopsis.
-    if it is absent throws a SeqNotFound exception, ValueError if not an
-    integer string """
-    par = email.Parser.HeaderParser()
-    syn = par.parsestr(synopsis)
-    seq = syn['X-Nym-Sequence']
-    if seq == None:
-	raise SeqNotFound()
-    return int(seq)
-
-def tmpFileMsg(body):
-    """write body in a temp file name and return a file name of this file
-    """
-    (fd, fname) = tempfile.mkstemp()
-    os.close(fd)
-    f = open(fname, "w")
-    f.write(body)
-    f.close()
-    return fname
-    
-
-def bf(l):
-    """Generate 2 octets bitfield from boolean list (hasmail values). LSB
-       corresponds to first value in list"""
-    assert len(l) <= 16
-    r1 = 0
-    offset = 0
-    for i in l[:8]:
-	if i: r1 |= (1 << offset)
-	offset += 1
-    r2 = 0
-    offset = 0
-    for i in l[8:]:
-	if i: r2 |= (1 << offset)
-	offset += 1
-    return chr(r2) + chr(r1)
-
-def bf2list(bf):
-    """take a 2 octets big endian string and output the associated 
-    list of indexes"""
-    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)
-    return l
-
-def b2s(c):
-    """return a string of length 2 representing the byte c in hex"""
-    i = ord(c)
-    (q, r) = divmod(i, 16)
-    return hex(q)[2:] + hex(r)[2:]
-
-def filter_header(body):
-    par = email.Parser.HeaderParser() 
-    message = par.parsestr(msg)
-    for h in forbidden_headers:
-	if message.has_key(h):
-	    del message[h]
-    return message.as_string()
-
-def relay(nym, rt, ri, sname, body):
-    """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
-    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 genMid(length = midLen):
-    """Randomly generates a mid of given length different from oldestMid"""
-    ret = oldestMid
-    while ret == oldestMid:
-	res = ""
-	for i in range(0, length): res = res + chr(random.randint(0, 255))
-	ret = res
-    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)
-    return res
-
-if __name__ == '__main__':
-    import sys
-    print synopsize(sys.stdin.read())
-    print "And now, a random encoded mid: " + mid2filename(genMid(20))

Copied: trunk/nymbaron/Mail.py (from rev 306, trunk/nym3/Mail.py)

Deleted: trunk/nymbaron/Message.py
===================================================================
--- trunk/nym3/Message.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Message.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,1114 +0,0 @@
-#!/usr/bin/python
-# -*- coding: iso-8859-1 -*-
-# $Id$
-# 
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Message
-
-    This module contains representations of the message of the nym protocol
-    and methods to parse and generate messages from these representations."""
-
-import types
-import nym3.Common as Common
-import mixminion.Crypto as _cr
-
-# Every command type has a numerical code as described in nym-spec.
-CToSCODE = { 'Create': 0, 'Create2': 1, 'Surb': 2, 'Newpk': 3,
-	     'Relay': 4, 'Get': 5, 'Summarize': 6, 'Delete': 7,
-	     'Policy': 8 }
-# nym-spec says MSGS and later MSG. use Msg for consistency.
-SToCCODE = { 'Created': 0, 'Status': 1, 'Summary': 2, 'Msg': 3,
-	     'Dropped': 4, 'Error': 5 }
-
-class ParseError(Exception):
-	"""ParseError : error raised if anything goes wrong during parsing"""
-	def __init__(self, value):
-		self.value = value
-
-	def __str__(self):
-		return repr(self.value)
-
-class BadArgument(Exception):
-	"""BadArgument : error raised if a function is given an irrelevant argument"""
-	def __init__(self, value):
-		self.value = value
-
-	def __str__(self):
-		return repr(self.value)
-
-def strToIntBE(s):
-	"""return the Int coded by the string s with the big endian convention"""
-	def aux(s, ac):
-		if(s == ""):
-			return ac
-		else:
-			return aux(s[1:], 256 * ac + ord(s[0]))
-	return aux(s, 0)
-
-def intToStrBE(n,mod):
-	"""return a string coding the n modulo 256^mod in big endian"""
-	def aux(n, ac, mod):
-		if(mod == 0):
-			return ac
-		else:
-			return aux(n / 256, chr(n % 256) + ac, mod - 1)
-	return aux(n, "", mod)
-
-def nbBits(n):
-	"""return the number of bits of a strictly positive integer,
-	0 otherwise"""
-	res = 0
-	p = 1
-	while(n >= p):
-		p = 2 * p
-		res = res + 1
-	return res
-
-def isMidList(l, n):
-    """true if l is a list of strings of length n"""
-    for i in l:
-	if ((len(i) != n) or (type(i) != types.StringType)): return False
-    return True
-
-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"""
-    def __init__(self, s):
-	"""initialize by wrapping a string"""
-	self.s = s
-	self.a = 0
-	self.b = 0
-	    
-    def isEnd(self):
-	"""True if we have read the whole string"""
-	return (self.b == len(self.s))
-
-    def next(self, n):
-	"""return the next n characters"""
-	if n < 0: raise BadArgument("StrReader.next : n < 0")
-	if self.b + n > len(self.s):
-	    raise IndexError("StrReader.next : n too long")
-	self.a = self.b
-	self.b = self.b + n
-	return self.s[self.a:self.b]
-
-    def readHeader(self):
-    	"""Read next header from the string"""
-    	h = Header()
-    	h.fromStrReader(self)
-    	return h
-
-    def readCommandCToS(self):
-	"""Read next CommandCToS from string"""
-	try:
-	    ct = ord(self.next(1))
-	    cs = strToIntBE(self.next(3))
-    	    if (ct == CToSCODE['Create']): a = Create()
-    	    elif (ct == CToSCODE['Create2']): a = Create2()
-    	    elif (ct == CToSCODE['Surb']): a = Surb()
-    	    elif (ct == CToSCODE['Newpk']): a = Newpk()
-    	    elif (ct == CToSCODE['Relay']): a = Relay()
-    	    elif (ct == CToSCODE['Get']): a = Get()
-    	    elif (ct == CToSCODE['Summarize']): a = Summarize()
-    	    elif (ct == CToSCODE['Delete']): a = Delete()
-    	    elif (ct == CToSCODE['Policy']): a = Policy()
-    	    else:
-    		raise ParseError("Undefined Command Type")
-    	    a.fromStrReader(self, cs)
-    	    return a
-    	except IndexError: #is it really necessary? is it not considered in the various fromStrReader ?
-    		raise ParseError("Bad Formed Command")
-
-    def readCommandCToSList(self):
-    	"""Read a list of CommandSToC from string, until
-    	the string is completely read"""
-    	l = []
-    	while (not self.isEnd()):
-    		l.append(self.readCommandCToS())
-    	return l
-
-    def readCommandSToC(self):
-	"""Read next CommandSToC from string"""
-	try:
-	    ct = ord(self.next(1))
-    	    cs = strToIntBE(self.next(3))
-    	    if (ct == SToCCODE['Created']): a = Created()
-    	    elif (ct == SToCCODE['Status']): a = Status()
-    	    elif (ct == SToCCODE['Summary']): a = Summary()
-    	    elif (ct == SToCCODE['Msg']): a = Msg()
-    	    elif (ct == SToCCODE['Dropped']): a = Dropped()
-    	    elif (ct == SToCCODE['Error']): a = Error()
-    	    else:
-		print "Got ct %u" % ct
-    		raise ParseError("Undefined Command Type")
-    	    a.fromStrReader(self, cs)
-    	    return a
-    	except IndexError:
-	    # is it really necessary? is it not considered in the various
-	    # fromStrReader ? it should, if not consider it a bug
-    	    raise ParseError("Bad Formed Command")
-
-    def readCommandSToCList(self):
-    	"""Read a list of CommandSToC from string, until
-    	the string is completely read"""
-    	l = []
-    	while (not self.isEnd()):
-    		l.append(self.readCommandSToC())
-    	return l
-
-    def readNym(self, start, cs):
-    	"""Read a Nym length + a nym
-    	Raise ParseError if it takes more characters from the string that previously advertised"""
-    	nl = ord(self.next(1))
-    	if(self.b + nl - start > cs):
-    		raise ParseError("Bad Formed Command")
-    	return self.next(nl)
-
-    def readNymList(self, start, cs):
-    	"""Read a list of (Nym length + a nym)
-    	Raise ParseError if it takes more characters from the string that previously advertised"""
-    	try:
-    		l = []
-    		while(self.b < start + cs):
-    			l.append(self.readNym(start, cs))
-    		return l
-    	except ParseError: #is it really necessary?
-    		raise
-
-    def readMid(self, length, start, cs):
-    	"""Read a str of len = length
-    	Raise ParseError if it takes more characters from the string that previously advertised, in that case, doesn't read the StrReader"""
-    	if(self.b + length - start > cs):
-    		raise ParseError("Bad Formed Command")
-    	return self.next(length)
-
-    def readMidList(self, length, start, cs):
-    	"""Read a list of str of len length
-    	Raise ParseError if it takes more characters from the string that previously advertised"""
-    	try:
-    		l = []
-    		while(self.b < start + cs):
-    			l.append(self.readMid(length, start, cs))
-    		return l
-    	except ParseError: #is it really necessary?
-    		raise
-	
-class Header:
-    """Header Object
-       sig : signature
-       nl : nym length
-       nym : nym
-       seqNo : sequence number which identifies the message
-       length : header length"""
-    def __init__(self):
-	"""Build a Header empty object"""
-	pass
-
-    def fromData(self, nym, seqNo, sig = ""):
-	"""Fill a Header Object from a nym and sequence number and sig 
-	   if provided.
-	   sig : signature
-	   nl : nym length
-	   nym : nym
-	   seqNo : sequence number which identifies the message
-	   length : header length"""
-	if(len(seqNo) != seqNoLength):
-	    raise BadArgument("Buildheader.__init__ : seqNo length doesn't match seqNoLength")  
-	if(sig == ""): self.sig = chr(0) * sigLength
-	else:
-	    if(len(sig) != sigLength):
-		self.sig = chr(0) * (sigLength - len(sig)) + sig
-	    else: self.sig = sig
-	self.nl = len(nym)
-	self.nym = nym
-	self.seqNo = seqNo
-	#self.length = sigLength + 1 + self.nl + seqNoLength
-
-    def __str__(self):
-	"""Give the string of the header that would be sent in a control message"""
-	return self.sig + chr(self.nl) + self.nym + self.seqNo
-
-    def fromStrReader(self,sr):
-	"""Fill a Header from a StrReader"""
-	try:
-	    self.sig = sr.next(sigLength)
-	    self.nl = ord(sr.next(1))
-	    self.nym = sr.next(self.nl)
-	    self.seqNo = sr.next(seqNoLength)
-	except IndexError: raise ParseError("Header too short")
-
-
-## class ReadHeader(Header):
-##     def __init__(self,sr):
-##	   """builds a header from a StrReader 
-##	   sig : signature
-##	   nl : nym length
-##	   nym : nym
-##	   seqNo : sequence number which identifies the message
-##	   length : header length"""
-##	   try:
-##	       self.sig = sr.next(sigLength)
-##	       self.nl = ord(sr.next(1))
-##	       self.nym = sr.next(nl)
-##	       self.seqNo = sr.next(seqNoLength)
-##	       self.length = sr.b
-##	   except IndexError:
-##	       raise ParseError("Parse Error : Header too short")
-	
-## class BuiltHeader(Header):
-##     def __init__(self,nym,seqNo):
-##	   """Builds a header from a nym and and sequence number
-##	   sig : signature
-##	   nl : nym length
-##	   nym : nym
-##	   seqNo : sequence number which identifies the message
-##	   length : header length"""
-##	   if(len(seqNo) != seqNoLength):
-##	       raise BadArgument("Buildheader.__init__ : seqNo length doesn't match seqNoLength")  
-##	   self.sig = chr(0) * sigLength
-##	   self.nl = len(nym)
-##	   self.nym = nym
-##	   self.seqNo = seqNo
-##	   self.length = sigLength + 1 + self.nl + seqNoLength
-
-class CommandCToS:
-	"""CommandCToS astract class
-	the CommandCToS derive from this class"""
-	pass
-
-class Create(CommandCToS):
-	"""Create command
-	self.list : list of nym (string list)
-	self.pw : proof of work"""
-	def __init__(self):
-		"""Build a Create empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Create']
-
-	def fromData(self, l, pw = ""):
-		"""Fill a Create Object from a list of nyms and a proof of work"""
-		if(len(l) > 255):
-			raise BadArgument("Create.fromData : len(l) too big")
-		for i in range(len(l)):
-			if(len(l[i]) > 255):
-				raise BadArgument("Create.fromData : nym too long")
-		self.list = l
-		self.pw = pw
-		#if len(pw) isn't too big, we are sure that the command size is small enough because of the size and the number of nyms 
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Create command from a StrReader
-		raise ParseError if it is malformed"""
-		start = sr.b
-		try:
-			nNym = ord(sr.next(1))
-			self.pw = "" #what is a proof of work?
-			self.list = sr.readNymList(start, cs)
-		except (ParseError, IndexError):
-			raise ParseError("Bad Formed Command : Create")
-		#nNym is redundant, we use it to make sure the message is well formed
-		if(nNym != len(self.list)):
-		   raise ParseError("Bad Formed Command : Create")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = chr(len(self.list)) + self.pw
-		for i in range(len(self.list)):
-			s = s + chr(len(self.list[i])) + self.list[i]
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-		
-class Create2(CommandCToS):
-	"""Create2 command
-	self.cr : challenge response (str)"""
-	def __init__(self):
-		"""Build a Create2 empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Create2']
-
-	def fromData(self, cr):
-		"""Fill Create2 from a string containing the response to a challenge"""
-		self.cr = cr
-		#if cr is small enough we are sure that the command size is small enough
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Create2 command from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			self.cr = sr.next(cs)
-		except IndexError:
-			raise ParseError("Bad Formed Command : Create2")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = self.cr
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-	
-class Surb(CommandCToS):
-	"""Surb command
-	self.surbs : set of surbs (str)"""
-	def __init__(self):
-		"""Build a Surb empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Surb']
-
-	def fromData(self, s):
-		"""Fill a Surb Object from a string containing Surbs"""
-		if len(s) >= pow(256, 3):
-			raise BadArgument("Surb.fromData : command body too long")
-		self.surbs = s
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Surb Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			self.surbs = sr.next(cs)
-		except IndexError:
-			raise ParseError("Bad Formed Command : Surb")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = self.surbs
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Newpk(CommandCToS):
-	"""Newpk command
-	self.ekid : ASN.1 encoding of the identity key
-	self.ekenc : ASN.1 encoding of the encryption key"""
-	
-	def __init__(self):
-		"""Build a Newpk empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Newpk']
-
-	def fromData(self, sid, senc):
-		"""Fill a Newpk Object from 2 keys"""
-		if sid == None or senc == None:
-		    raise BadArgument("Newpk.fromData : sid and senc mustn't be None")
-		id_l = nbBits(_cr.pk_get_modulus(sid))
-		id_l2 = nbBits(_cr.pk_get_modulus(senc))
-		if( ((id_l != 1024) and (id_l != 2048)) or ((id_l2 != 1024) and (id_l2 != 2048))):
-		    raise BadArgument("Newpk.fromData : sid or senc as not a valid size")
-		self.ekid = _cr.pk_encode_public_key(sid)
-		self.ekenc = _cr.pk_encode_public_key(senc)
-		#command body is small enough
-
-	def check_key(self, ekey):
-	    key = _cr.pk_decode_public_key(ekey)
-	    mod = _cr.pk_get_modulus(key)
-	    nb = nbBits(mod)
-	    return (nb == 1024 or nb == 2048) and _cr.pk_same_public_key(key, _cr.pk_from_modulus(mod)) 
-
-	def fromStrReader(self, sr, cs):
-	    """Fill a Newpk Object from a StrReader
-	    raise ParseError if it is malformed"""
-	    try:
-	    	id_l = strToIntBE(sr.next(2))
-	    	id_l2 = cs-2 - id_l
-	    
-		self.ekid = sr.next(id_l)
-		self.ekenc = sr.next(id_l2)
-		if not (self.check_key(self.ekid) and self.check_key(self.ekenc)):
-		    raise BadArgument("Illegal keys : Newpk")
-			
-	    except IndexError:
-	    	raise ParseError("Bad Formed Command : Newpk")
-
-	def __str__(self):
-	    """Give the string of the Command that would be sent in a control message"""
-	    s=intToStrBE(len(self.ekid), 2) + self.ekid + self.ekenc
-	    if(len(s)>=pow(256,3)): #should not be possible, otherwise bug
-	    	raise BadArgument("Newpk.__str__ : command body too long")
-	    return chr(self.ct()) + intToStrBE(len(s),3) + s
-
-	
-class Relay(CommandCToS):
-	"""Relay command
-	self.rs : routing info size (int)
-	self.rt : routing type (str of length 2)
-	self.ri : routing info (str)
-	self.body : body of the message (str)"""
-	def __init__(self):
-		"""Build a Relay empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Relay']
-
-	def fromData(self, rt, ri, body):
-		"""Fill a Relay Object from a 3 strings : routing type(2 octets) routing info and body"""
-		if len(rt) != 2:
-			raise BadArgument("Relay.fromData : RT size isn't 2 bytes")
-		self.rt = rt
-		self.rs = len(ri)
-		if self.rs >= 256 * 256:
-			raise BadArgument("Relay.fromData : RS size isn't 2 bytes")
-		self.ri = ri
-		self.body = body
-		if(4 + self.rs + len(body) >= pow(256, 3)):
-			raise BadArgument("Relay.fromData : Command body too long")
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Relay Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			self.rs = strToIntBE(sr.next(2))
-			self.rt = sr.next(2)
-			self.ri = sr.next(self.rs)
-			self.body = sr.next(cs - 4 - self.rs)
-		except (BadArgument, IndexError):
-			raise ParseError("Bad Formed Command : Relay")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = intToStrBE(self.rs, 2) + self.rt + self.ri + self.body
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-	
-class Get(CommandCToS):
-	"""Get command
-	self.l : list of mid (list of str of len midLength)"""
-	def __init__(self):
-		"""Build a Get empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Get']
-
-	def fromData(self, li):
-		"""Fill a Get Object from a list of message id (=string of len 20)"""
-		#check that each element of the list has 20 bytes
-		if not isMidList(li, midLength):
-			raise BadArgument("Get.fromData : li is not a list of message id")
-		self.l = li
-		if midLength * len(self.l) >= 256 ** 3:
-			raise BadArgument("Get.fromData : Command body too long")
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Get Object from a StrReader
-		raise ParseError if it is malformed"""
-		start = sr.b
-		try:
-			self.l = sr.readMidList(midLength, start, cs)
-		except (ParseError, IndexError):
-			raise ParseError("Bad Formed Command : Get")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = ""
-		for i in range(len(self.l)):
-			s = s + self.l[i]
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-	
-class Summarize(CommandCToS):
-	"""Summarize command
-	self.num : maximum number of synopsis to retrieve (int)
-	self.after : mid older than the synopsese retrieved"""
-	def __init__(self):
-		"""Build a Summarize empty object"""
-		pass
-		
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Summarize']
-
-	def fromData(self, n, m):
-		"""Fill a Summarize Object from the max number of synopsis to retrieve (int)  and the message id of a message older than the messages we want the synopsis from (string of len 20)
-		the int is considered modulo 256^2"""
-		n_mod = n % (256 * 256)
-		self.num = n_mod
-		if len(m) != midLength: # or m is not a str
-			raise BadArgument(" Summarize.fromData : m is not a valid message ID")
-		self.after = m
-		#command body is small enough
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Summarize Object from a StrReader
-		raise ParseError if it is malformed"""
-		if cs != 2 + midLength:
-			raise ParseError("Bad Formed Command : Summarize")
-		try:
-			self.num = strToIntBE(sr.next(2))
-			self.after = sr.next(midLength)
-		except IndexError:
-			raise ParseError("Bad Formed Command : Summarize")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = intToStrBE(self.num, 2) + self.after
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-	
-class Delete(CommandCToS):
-	"""Delete command
-	self.l : list of mid (list of str of len midLength)"""
-	def __init__(self):
-		"""Build a Delete empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Delete']
-
-	def fromData(self, li):
-		"""Fill a Delete Object from a list of message ID (string of length midLength"""
-		#check that each element of the list has 20 bytes
-		if not isMidList(li, midLength):
-		     raise BadArgument("Delete.fromData : li is not a list of message id")
-		if midLength * len(li) >= pow(256, 3):
-		    raise BadArgument("Delete.fromData : Command body too long")
-		self.l = li
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Delete Object from a StrReader
-		raise ParseError if it is malformed"""
-		start = sr.b
-		try:
-		    self.l = sr.readMidList(midLength, start, cs)
-		except (ParseError, IndexError):
-		    raise ParseError("Bad Formed Command : Delete")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = ""
-		for i in range(len(self.l)):
-			s = s + self.l[i]
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-	
-class Policy(CommandCToS):
-	"""Policy command
-	self.opt : option name (str)
-	self.val : option value (str)"""
-	def __init__(self):
-		"""Build a Policy empty object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return CToSCODE['Policy']
-
-	def fromData(self, opt, val):
-		"""Fill a Policy Object from 1 str : the option name
-		and its value (either a str or a int depending on the option
-		"""
-		if len(opt) > 255:
-			raise BadArgument("Policy.fromData : Option name length too big")
-		if opt in Common.userPolicy:
-			self.opt = opt
-		if opt == "SendMsgAfter":
-			if val > pow(256, 2):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-		elif (opt == "SendSummaryAfter"):
-			if val > pow(256, 2):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-		elif (opt == "EncryptSummaryAfter"):
-			if val > pow(256, 2):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-		elif (opt == "MaxSURBsPerDay"):
-			if val > pow(256, 2):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-		elif (opt == "HoldSURBs"):
-			if val > pow(256, 2):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-		elif (opt == "HoldUntilAck"):
-			if val in [ 'never', 'quota', 'always' ]:
-				self.val = val
-		elif (opt == "ShutdownPhrase"):
-			if len(val) + 1 + len(opt) > pow(256, 3):
-				raise BadArgument("Policy.fromData : Option value too long")
-			self.val = val
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Policy Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			le = ord(sr.next(1))
-			self.opt = sr.next(le)
-			inter = sr.next(cs - 1 - le)
-			if(self.opt == "SendMsgAfter"):
-				if len(inter) != 2:
-					raise ParseError("Bad Formed Command : Policy")
-				self.val = strToIntBE(inter)
-			elif (self.opt == "SendSummaryAfter"):
-				if len(inter) != 2:
-					raise ParseError("Bad Formed Command : Policy")
-				self.val = strToIntBE(inter)
-			elif (self.opt == "EncryptSummaryAfter"):
-				if len(inter) != 2:
-					raise ParseError("Bad Formed Command : Policy")
-				self.val = strToIntBE(inter)
-			elif (self.opt == "MaxSURBsPerDay"):
-				if len(inter) != 2:
-					raise ParseError("Bad Formed Command : Policy")
-				self.val = strToIntBE(inter)
-			elif (self.opt == "HoldSURBs"):
-				if len(inter) != 2:
-					raise ParseError("Bad Formed Command : Policy")
-				self.val = strToIntBE(inter)
-			elif (self.opt == "HoldUntilAck"):
-				if inter in [ 'never', 'quota', 'always']:
-					self.val = inter
-			elif (self.opt == "ShutdownPhrase"):
-				self.val = inter
-		except (IndexError, BadArgument):
-			raise ParseError("Bad Formed Command : Policy")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = chr(len(self.opt)) + self.opt
-		if self.opt in Common.userPolicy[:5]:
-			s = s + intToStrBE(self.val, 2)
-		if self.opt in Common.userPolicy[5:]:
-			s = s + self.val
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-		
-	
-class CommandSToC:
-	"""CommandSToC astract class
-	the CommandSToC derive from this class"""
-	pass
-
-class Created(CommandSToC):
-	"""command Created
-	self.nym : nym of the created account (str)
-	self.challenge : challenge (str)"""
-	def __init__(self):
-		"""Build a empty Created object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Created']
-
-	def fromData(self, nym, ch):
-		"""Fill a Created Object from a nym(str) and a challenge(str)"""
-		if len(nym) > 255:
-			raise BadArgument("Created.fromData : nym too long")
-		if 1 + len(nym) + len(ch) >= pow(256, 3):
-			raise BadArgument("Created.fromData : challenge too long")
-		self.nym = nym
-		self.challenge = ch
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Created Object from a StrReader
-		raise ParseError if it is malformed"""
-		start = sr.b
-		try:
-			self.nym = sr.readNym(start, cs)
-			self.challenge = sr.next(cs + start - sr.b)
-		except (IndexError, ParseError, BadArgument):
-			raise ParseError("Bad Formed Command : Created")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = chr(len(self.nym)) + self.nym + self.challenge
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Status(CommandSToC):
-	"""command Status
-	self.nMsg : number of email waiting (int)
-	self.nSurb : number of surbs left (int)
-	self.quota : maximum storage on nymserver in byte (int)
-	self.used : used storage in byte (int)
-	self.acks : list of seqno (list of (str of length seqNoLength))"""
-	def __init__(self):
-		"""Build a empty Status object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Status']
-
-	def fromData(self, nM, nS, q, u, l):
-		"""Fill a Status Object from a number of message(int), a number of surbs(int), a quota(int), a used(int) and a sequence of seqno(list of (str of size seqNoLength)"""
-		#TODO replace the error by the taking of the maximal value?
-		if nM >= pow(256, 4):
-			raise BadArgument("Status.fromData : number of waiting e-mail too large")
-		if nM >= pow(256, 2):
-			raise BadArgument("Status.fromData : number of available surbs too large")
-		if q >= pow(256, 4):
-			raise BadArgument("Status.fromData : quota too large")
-		if u >= pow(256, 4):
-			raise BadArgument("Status.fromData : used space too large")
-		if not isMidList(l, seqNoLength):
-			raise BadArgument("Status.fromData : l is not a list of Sequence Number")
-		if 14 + seqNoLength * len(l) >= pow(256, 3):
-		# avoid numeric literals where possible TODO
-			raise BadArgument("Status.fromData : l is too long")
-		self.nMsg = nM
-		self.nSurb = nS
-		self.quota = q
-		self.used = u
-		self.acks = l
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Status Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			start = sr.b
-			self.nMsg = strToIntBE(sr.next(4))
-			self.nSurb = strToIntBE(sr.next(2))
-			self.quota = strToIntBE(sr.next(4))
-			self.used = strToIntBE(sr.next(4))
-			self.acks = sr.readMidList(seqNoLength, start, cs)
-		except (IndexError, ParseError):
-			raise ParseError("Bad Formed Command : Status")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = intToStrBE(self.nMsg, 4) + intToStrBE(self.nSurb, 2) +\
-		    intToStrBE(self.quota, 4) + intToStrBE(self.used, 4)
-		for i in range(len(self.acks)):
-			s = s + self.acks[i]
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Summary(CommandSToC):
-	"""command Summary
-	self.bf : bit field indicating which one of the synopsis as a mail
-	self.synSet : Encrypted synopses set
-	"""
-	def __init__(self):
-		"""Build a empty Summary object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Summary']
-
-	def fromData(self, bf, ess):
-		"""Fill a Object from bit field (str of len 2) and an encrypted set of synopses(str)"""
-		if len(bf) != 2:
-			raise BadArgument("Summary.fromData : bit field has not the good size, and size DOES matter")
-		if 2 + len(ess) >= pow(256, 3):
-			raise BadArgument("Summary.fromData : Encrypted set of synopses too long")
-		self.bf = bf
-		self.synSet = ess
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			self.bf = sr.next(2)
-			self.synSet = sr.next(cs - 2)
-		except IndexError:
-			ParseError("Bad Formed Command : Summary")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = self.bf + self.synSet
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Msg(CommandSToC):
-	"""command Msg
-	self.mid : message ID (str of length midLength)
-	self.msg : encrypted email (str)
-	"""
-	def __init__(self):
-		"""Build a empty Msg object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Msg']
-
-	def fromData(self, mid, msg):
-		"""Fill a Msg Object from a Mid (str of length midLength) and an encrypted msg (str)"""
-		if len(mid) != midLength:
-			raise BadArgument("Msg.fromData : mid has not the good length")
-		if midLength + len(msg) >= pow(256, 3):
-			raise BadArgument("Msg.fromData : msg too long")
-		self.mid = mid
-		self.msg = msg
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Msg Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			start = sr.b
-			self.mid = sr.readMid(midLength, start, cs)
-			self.msg = sr.next(cs - midLength)
-		except (IndexError, ParseError):
-			raise ParseError("Bad Formed Command : Msg")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = self.mid + self.msg
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Dropped(CommandSToC):
-	"""command Dropped
-	self.list : list of mid (list of (str of size midLength))
-	"""
-	def __init__(self):
-		"""Build a empty Dropped object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Dropped']
-
-	def fromData(self, l):
-		"""Fill a Dropped Object from a list of mid"""
-		if( not isMidList(l, midLength)):
-			raise BadArgument("Dropped.fromData : l is not a list of message id")
-		if midLength * len(l) >= pow(256, 3):
-			raise BadArgument("Dropped.fromData : Command body too long")
-		self.list = l
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Dropped Object from a StrReader
-		raise ParseError if it is malformed"""
-		start = sr.b
-		try:
-			self.list = sr.readMidList(midLength, start, cs)
-		except (ParseError, IndexError):
-			raise ParseError("Bad Formed Command : Dropped")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = ""
-		for i in range(len(self.list)):
-			s = s + self.list[i]
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-class Error(CommandSToC):
-	"""command Error
-	self.nonce : nonce from client message (str of size seqNo)
-	self.error : english langage error description (str)
-	"""
-	def __init__(self):
-		"""Build a empty Error object"""
-		pass
-
-	def ct(self):
-		"""return a int for the code of the command"""
-		return SToCCODE['Error']
-
-	def fromData(self, seqNo, error):
-		"""Fill a Object from a seqno (str of len seqNoLength) and an error message(str)"""
-		if len(seqNo) != seqNoLength:
-			raise BadArgument("Error.fromData : seqNo has not the good length")
-		if seqNoLength + len(error) >= pow(256, 3):
-			raise BadArgument("Error.fromData : msg too long")
-		self.nonce = seqNo
-		self.error = error
-
-	def fromStrReader(self, sr, cs):
-		"""Fill a Object from a StrReader
-		raise ParseError if it is malformed"""
-		try:
-			start = sr.b
-			self.mid = sr.readMid(seqNoLength, start, cs)
-			self.msg = sr.next(cs - seqNoLength)
-		except (IndexError, ParseError):
-			raise ParseError("Bad Formed Command : Error")
-
-	def __str__(self):
-		"""Give the string of the Command that would be sent in a control message"""
-		s = self.nonce + self.error
-		assert len(s) < pow(256, 3)
-		return chr(self.ct()) + intToStrBE(len(s), 3) + s
-
-def buildMessage(comList):
-    """Turn a list of Command into a sendable message"""
-    s = ""
-    for c in comList:
-    	s = s + str(c)
-    return s
-
-if (__name__ == '__main__'):
-	import Mail
-	l = []
-	for i in range(3):
-		l.append(Mail.genMid(length = 20))
-	H = Header()
-	H.fromData("JR", "01234567890123456789")
-	print H
-	del H
-	print "test Create"
-	C = Create()
-	C.fromData(["Lolo_", "Marsux", "Azatoth_", "Keeh"])
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Create2"
-	C = Create2()
-	C.fromData("azertyuiop42")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Surb"
-	C = Surb()
-	C.fromData("-" * 2104)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Newpk"
-	C = Newpk()
-        k1 = _cr.pk_generate()
-        k2 = _cr.pk_generate()
-	C.fromData(k1, k2)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Relay"
-	C = Relay()
-	C.fromData("ab", "too lazy to make real ones here", "this is the body of the email. It sholud contain some kind of header, but for testing the parser, who cares?")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Get"
-	C = Get()
-	C.fromData(l)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Summarize"
-	C = Summarize()
-	C.fromData(10, l[0])
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Delete"
-	C = Delete()
-	C.fromData(l)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-	print "test Policy"
-	C = Policy()
-	C.fromData("HoldUntilAck", "never")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandCToS()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Created"
-	C = Created()
-	C.fromData("Marsux", "what is the answer to universe life and everything")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Status"
-	C = Status()
-	C.fromData(5, 10, 424242, 212121, l)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Summary"
-	C = Summary()
-	C.fromData("ab","this should be an encrypted set of synopses")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Msg"
-	C = Msg()
-	C.fromData(l[0], "Say something stupid, like, I am wearing female's underwear")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Dropped"
-	C = Dropped()
-	C.fromData(l)
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-
-	print "test Error"
-	C = Msg()
-	C.fromData(l[0], "Help! Help! I am being repressed!")
-	s1 = str(C)
-	S = StrReader(s1)
-	D = S.readCommandSToC()
-	s2 = str(D)
-	assert s1 == s2
-

Copied: trunk/nymbaron/Message.py (from rev 306, trunk/nym3/Message.py)

Deleted: trunk/nymbaron/Server/Main.py
===================================================================
--- trunk/nym3/Server/Main.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Server/Main.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,238 +0,0 @@
-#!/usr/bin/python
-# -*- coding: iso-8859-1 -*-
-# $Id$
-#
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Server.Main
-
-    This package contains an implementation of a nym3 server."""
-
-import sys
-import os
-import getopt
-import nym3.Server.User as User
-import nym3.Server.Config as Config
-import nym3.Message as Message
-import nym3.Common as Common
-import nym3.Mail as Mail
-
-lifeCycle = User.lifeCycle
-"""The life cycle of a mail received by the server for a nym""" 
-
-def processIncoming(localpart, msg):
-    """Process incoming mail from the MTA
-    - 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:
-	print "No such user " + localpart
-	sys.exit(73)
-    # TODO : we should honor the "Quota" sending policy.
-    if nymuser.usage() + len(msg) > nymuser.quota():
-	print "Quota exceeded."
-	print repr(nymuser.usage()) + " > " + repr(nymuser.quota())
-	sys.exit(75)
-    # Valid user, not over quota. Filtering policy can wait.
-    nymuser.store(msg)
-    sys.exit(0)
-
-def processMessage(msg):
-    """process incoming control message from the Mixminion serveur
-    - verifies signature
-    - parse the message into header + sequence on control commands
-    - run appropriate actions"""
-    
-    class MyException(Exception): pass
-    
-    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
-
-		nymUser = None
-		#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,1)
-			    except User.AlreadySuchUser: pass
-			    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
-			    print "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
-		    print "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)
-			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
-		    nymUser.received(h.seqNo)
-		    created = Message.Created()
-		    created.fromData(nymUser.username, "")
-		    nymUser.setUp()
-		    nymUser.advanced_send(Message.buildMessage([created]))
-	    except MyException:
-		   #if you come here something went wrong during the account
-		   #initialization
-		   print "Bad formed account creation message"
-		   sys.exit(2) #TODO smart error code
-	else: # NYM is not empty
-	    try:
-		nymUser = User.User(h.nym)
-	    except User.NoSuchUser:
-		print "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']):
-		    if (nymUser.isInitialized() and (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):
-			    print "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))
-			print "EC is " + str(ec)
-			if (ec == 0):
-			    nymUser.markMid(sendList,lifeCycle['sent-in-full'])
-			    if(nymUser['HoldUntilAck'] == 'never'):
-				map(nymUser.delete_msg,sendList)
-			    
-			else:
-			    print "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 = []
-			print str(sendList)
-			for (ml, bf, blob) in sendList:
-			    sumCom = Message.Summary()
-			    sumCom.fromData(bf, blob)
-			    comList.append(sumCom)
-			    mList = mList + ml
-			ec = nymUser.send(Message.buildMessage(comList))
-			if (ec == 0):
-			    nymUser.markMid(mList, lifeCycle['synopsis-sent'])
-			else:
-			    print "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:
-	print inst
-	sys.exit(2) #TODO error code		
-	      
-def main(argv):
-    optlist, pholder = getopt.getopt(argv[1:], 'D:d:m')
-    for o, a in optlist:
-	if o == "-D":
-	    Config.DEBUG = True
-	
-    for o, a in optlist:
-	if o == "-d": # mail delivery
-	    processIncoming(a, sys.stdin.read())
-	    sys.exit(0)
-	if o == "-m":
-	    processMessage(sys.stdin.read())
-	    sys.exit(0)
-
-if __name__ == '__main__':
-    main(sys.argv)

Copied: trunk/nymbaron/Server/Main.py (from rev 306, trunk/nym3/Server/Main.py)

Deleted: trunk/nymbaron/Server/User.py
===================================================================
--- trunk/nym3/Server/User.py	2005-07-12 19:53:38 UTC (rev 303)
+++ trunk/nymbaron/Server/User.py	2005-09-17 13:18:46 UTC (rev 307)
@@ -1,602 +0,0 @@
-# $Id$
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2004,2005 Jean-René Reinhard <jr at komite.net>
-# and Laurent Fousse <laurent at komite.net>.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, and/or sell copies
-# of the Software, and to permit persons to whom the Software is furnished to
-# do so, provided that the above copyright notice(s) and this permission
-# notice appear in all copies of the Software and that both the above
-# copyright notice(s) and this permission notice appear in supporting
-# documentation.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
-# LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""nym3.Server.User
-
-    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
-import nym3.Mail as Mail
-import nym3.Crypto as Crypto
-import nym3.Message as Message
-import pickle
-import string
-import time
-import mixminion.Common
-from nym3.Message import intToStrBE
-import mixminion.Crypto as _cr
-from mixminion.BuildMessage import getNPacketsToEncode
-from mixminion.Packet import parseReplyBlocks, ParseError
-
-lifeCycle = { 'nothing-sent' : 0, 'synopsis-sent' : 1,
-              'sent-in-full' : 2, 'deleted' : 3 }
-"""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 the first index i of l for which l[i] is true,
-       assuming prop is increasing over l"""
-    min = 0
-    max = len(l) - 1
-    if not prop(l[max]): return IndexError
-    if prop(l[min]): return min
-
-    current = (max + min) / 2
-    while max - min > 1:
-	if prop(l[current]):
-	    max = current
-	    current = (max + min) / 2
-	else:
-	    min = current
-	    current = (max + min) / 2
-    return max
-
-class User:
-    """Hold user data"""
-    def __init__(self, username, create = 0):
-        """0 : load user data throw an error if the user doesn't exist
-        1 : create user data throw an error if the user exists
-        _ : load a user data, if it doesn't exist create a new user silently
-	"""
-        self.datafile = Config.path + os.sep + username + '.dat'
-	self.username = username
-	self.index = None
-	self.mbox = None
-	self.syn = None
-	self.data = None
-        self._abort = False
-	self._lock()
-	try:
-	    f = open(self.datafile, 'r')
-            if (create == 1):
-                f.close()
-                raise AlreadySuchUser
-	    self.data = pickle.load(f)
-	    f.close()
-	except IOError:
-	    if create == 0: raise NoSuchUser()
-	    self.data = Config.default_settings
-	    self.data['username'] = username
-    	    self.data['received'] = []
-	    self.data['nSurbs'] = 0
-	    self.data['seqNo'] = 0
-
-    def __del__(self):
-	"""Flush the information contained in this User to the disk
-	"""
-        if (not self._abort):
-            self._save_index()
-            self._save_synbox()
-            self._save_mbox()
-            self._save_data()
-        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):
-	"""Lock the user. For well behaved functions."""
-	self.lock = mixminion.Common.Lockfile(Config.path + os.sep + 
-					      self.username + '.lck')
-	self.lock.acquire(blocking = 1)
-
-    def _release(self):
-	"""Releases the lock"""
-	self.lock.release()
-
-    def abort(self):
-        """permit to destroy a User object without saving its data
-        Can only be used if the object is being created for the
-        first time. If username.dat exist, it is ignored
-        """
-        try:
-            os.stat(self.datafile)
-        except:
-            self._abort = True
-
-    def timecmp(self, a, b):
-	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]
-	except: pass
-	try:
-	    sum = sum + os.stat(self.indexfile())[6]
-	except: pass
-	try:
-	    sum = sum + os.stat(self.synboxfile())[6]
-	except: pass
-	try:
-	    sum = sum + os.stat(self.mboxfile())[6]
-	except: pass
-	self.data['usage'] = sum
-	return sum
-
-    def received(self, seqNo):
-	self['received'].append(seqNo)
-
-    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):
-        """encrypts a set of synopses
-        l is a list of tuples (mid, status, synopsis) """
-	if len(l) == 0: return None
-        s = ""
-        m = []
-        for mid, status, syn in l:
-	    assert status == "clear"
-	    assert len(mid) == 1
-	    m = m + mid
-	    #print "mid = %s, status = %s, syn = %s\n" % (str(mid), str(status), str(syn))
-            s = s + mid[0] + intToStrBE(len(syn), 2) + syn
-        return (m, 'encrypted', Crypto.nym_encrypt(s, self.encKey()))
-
-    def getSyn(self, mid):
-	"""Retrieve a blurb consisting of the synopsis of the
-	   requested synopsis mid. May contain unwanted synopsis
-	   too."""
-	self.load_synbox()
-	for i, (midlist, status, synblob) in enumerate(self.syn):
-	    if mid in midlist: return i, (midlist, status, synblob)
-	raise ValueError()
-
-    def getMail(self, mid):
-        """Retrieve an encrypted mail with a given mid from the mbox
-        raise ValueError if the mail cannot be found"""
-        self.load_mbox()
-        try:
-            return self.mbox[mid]
-        except:
-            raise ValueError()
-        
-    def midAfter(self, mid):
-	"""Retrieve mids of messages that came strictly after message `mid'
-        the elements of the output are ordered by ascending
-        order of arrival time"""
-	self.load_index()
-
-	ret = []
-	#TODO if the mid can't be found in the index tab, what do we do?
-	if mid == Mail.oldestMid: ret = self.index.keys()
-	else:
-	    midtime = self.index[mid]['time']
-	    for msg in self.index.keys():
-		if self.index[msg]['time'] > midtime: ret.append(msg)
-	ret.sort(self.timecmp)
-	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 advanced_send(self, msg, add_status = True):
-	"""Sends a message to the nymholder through the mixminion network,
-	using the surbs provided by the nymholder. Adds a status control
-	message if the add_status argument is True. Also updates the number
-	of surbs"""
-	#TODO add the filling of the remaining place available in the the
-	#message by syns and msgs here
-	statusc = Message.Status()
-	nMsg = 0
-	usage = 0
-	cmsg = msg
-	if add_status:
-	    #determine the status elements, the number of surbs is assumed
-	    #to stay equal to its value
-	    self.load_index()
-	    for mid, v in self.index.iteritems():
-		if v['status'] in [lifeCycle['nothing-sent'], lifeCycle['synopsis-sent']]:
-		    nMsg += 1
-	    usage = self.usage()
-	    statusc.fromData(nMsg, 0, self.quota(), usage, self['received'])
-	    cmsg += str(statusc)
-	nb_req_surb = getNPacketsToEncode(cmsg, 0)
-	if add_status:
-	    while True:
-		statusc.fromData(nMsg, self['nSurbs'] - nb_req_surb,
-			self.quota(), usage, self['received'])
-		cmsg = msg + str(statusc)
-		act_nb = getNPacketsToEncode(cmsg, 0)
-		old_nb_req_surb = nb_req_surb
-		nb_req_surb = act_nb
-		if nb_req_surb <= old_nb_req_surb:
-		    break
-       	if nb_req_surb > self['nSurbs']:
-	    #TODO
-	    # - send an Error message "low on surbs" if a surb is available
-	    # - store the message we couldn't send for later
-	    return 1
-	#TODO debbuging cruft
-	print "%d surbs used to send control messages" % nb_req_surb
-	self['nSurbs'] -= nb_req_surb
-	self['received'] = []
-	return self.send(cmsg)
-	    	
-
-    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
-        else:
-            fname = Mail.tmpFileMsg(msg)
-            ec = os.system("mixminion send -R " + self.surbfile() + " -i " + fname)
-	    # TODO : debugging cruft
-            #os.unlink(fname)
-	    print "Message file left in " + fname
-            return ec
-
-    def clean_surbs(self):
-	"""Inspect the surbs and delete the used/outdated"""
-	pass
-	# TODO : this function relied on the false assumption that
-	# SURBs have fixed length. A rewrite is needed.
-	fname = self.surbfile()
-	f = open(fname, "r")
-	buffer = f.read()
-	f.close()
-	surbs = parseReplyBlocks(buffer)
-	goods = []
-	for surb in surbs:
-	    fname = '/tmp/' + Mail.mid2filename(Mail.genMid())
-	    fname = string.strip(fname)
-	    f = open(fname, 'w')
-	    f.write(surb)
-	    f.close()
-	    ec = os.system("mixminion inspect-surb " + fname + 
-			   " |grep 'Used: no'")
-	    os.unlink(fname)
-	    if ec == 0: goods.append(surb)
-	self.data['nSurbs'] = 0
-	f = open(self.surbfile(), "w")
-	for surb in goods:
-	    f.write(surb)
-	    self.data['nSurbs'] = self.data['nSurbs'] + 1
-	f.close()
-
-    def addSurbs(self, surbs):
-	"""Adds surbs to the store of surbs"""
-	try:
-	    #TODO debbuging cruft to remove
-	    nb = len(parseReplyBlocks(surbs)) 
-	    self['nSurbs'] += nb
-	except ParseError:
-	    #the provided binary data isn't a succession of binary surbs
-	    return
-	fname = self.surbfile()
-	f = open(fname, "a")
-	f.write(surbs)
-	f.close()
-	print "%d surbs added" % nb
-	
-    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:
-	    f = open(mbox, 'r')
-	    self.mbox = pickle.load(f)
-	    f.close()
-	except IOError:
-	    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')
-	pickle.dump(self.mbox, f)
-	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:
-	    f = open(synbox, 'r')
-	    self.syn = pickle.load(f)
-	    f.cl