diff -ruN msnlib-c1/INSTALL msnlib-c2/INSTALL
--- msnlib-c1/INSTALL	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/INSTALL	Tue Oct 22 22:07:44 2002
@@ -9,6 +9,7 @@
 configuration file and directories (placed in ~/.msn); and finally run the
 client with 'msn'.
 
+
 Here is a command line summary:
 
 # we became root
@@ -20,14 +21,14 @@
 # now drop the root privileges
 exit
 
-# run the setup
+# run the setup to create the configuration
 msnsetup
 
-# and finally the client
+# and finally start the client
 msn
 
 
-I would really like to hear your opinion, so please send me an email to
+I would really like to hear your opinion, so please drop me a line to
 albertogli@telpin.com.ar.
 
 
diff -ruN msnlib-c1/README msnlib-c2/README
--- msnlib-c1/README	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/README	Tue Oct 22 22:07:43 2002
@@ -10,14 +10,14 @@
 and the library.
 
 The client is really simple, uses a text-mode interface with commands similar
-to 'micq' (http://micq.ukeer.de/).
+to 'micq' (http://micq.ukeer.de/), which is a really great icq client; if
+you're looking for a good messaging system, forget about messenger and try it.
 
 The basic idea for the library is a main class which represents a connection
-with the server and holds all the information.
-
-It has only the a few methods to login, change some info and status, and send
-messages; all the rest is driven by an asyncronous callback scheme registered
-at runtime which can be changed on the fly.
+with the server and holds all the relevant information, it has only the a few
+methods to login, change some info and status, and send messages; all the rest
+is driven by an asyncronous callback scheme registered at runtime which can be
+changed on the fly.
 
 If you're intrested, read the code to find out more; most of the documentation
 is there either as command or as python documentation strings. You might also
diff -ruN msnlib-c1/doc/Changelog msnlib-c2/doc/Changelog
--- msnlib-c1/doc/Changelog	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/doc/Changelog	Tue Oct 22 22:07:43 2002
@@ -1,3 +1,37 @@
+22 Oct 02 21.49.07 - Alberto <albertogli@telpin.com.ar>
+ * msn: use quit() instead of sys.exit() in several places
+ * msn: handle network errors when logging in
+ * tag: c2 tag
+ 
+10 Oct 02 18.43.59 - Alberto <albertogli@telpin.com.ar>
+ * msn: be able to use emails as nicks (can be useful for weird nicks)
+ * msn: fix a simple bug when tried to close a socket that didn't exist
+ * msn: unified the quit procedure and handle a keyboard interrupt properly
+ * msn: improved parameter handling in 'del' and 'privacy' commands, avoiding
+	crashes with incorrect user input
+ * msn: added 'ww' and 'ee' commands, which are like 'w' and 'e' but show also
+	email addresses
+ * msn: added some help
+ * msn: improved 'status' a bit
+ * msnlib: added a 'priv' field in the user class (as a dictionary) which can
+	be used by the client for private purposes
+ * msn: display 'typing' only once, and show the last typing time in the user
+	info
+ * msn: improved some code indentation, documentation and comments
+ * doc: minor changes in the INSTALL and README files
+ * msn: display time with status changes
+
+04 Oct 02 10.20.46 - Alberto <albertogli@telpin.com.ar>
+ * msn: now lstrip before parsing the command, so we don't crash on spaces
+
+28 Sep 02 13.26.23 - Alberto <albertogli@telpin.com.ar>
+ * msn: added a handler for a socket.socket exception when reading from
+	sockets, and now print the error
+
+24 Sep 02 16.56.12 - Alberto <albertogli@telpin.com.ar>
+ * msn: fixed some small error displays on unknown nick.
+ * msn: fixed a crash when not giving enough parameters to 'm'
+
 24 Sep 02 10.50.59 - Alberto <albertogli@telpin.com.ar>
  * msnlib: fixed the only (known =) remaining bug, now connections are no
 	longer closed when we send a message
diff -ruN msnlib-c1/doc/TODO msnlib-c2/doc/TODO
--- msnlib-c1/doc/TODO	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/doc/TODO	Tue Oct 22 22:07:43 2002
@@ -3,25 +3,61 @@
 package TODO
 * more documentation (client manpages, basic api, etc.)
 
-msnclient TODO
+msn client TODO
+* be able to use nick with spaces
+	worthy?
 * better stdin reading (ie. like micq/readline)
-* find something out about the different char codes support
+* find something out about the different char codes support (ñ, ç, accents, and so on)
 * make colors configurable (either as colors or as random strings (so you can,
 	for instance, output in html)
 
-msnlib TODO
-* make login async (maybe using something like contuations?)
-	continuations would have worked nicely, but are only available in
-	python >= 2.2 (which many people don't have), so it will have to wait
-	for a bit
-* group support (part is in place (eg. gid)) (do we really want this?)
+
+msn lib TODO
 * blocked and allowed lists
-* multi-user chat handling (part in place, the ugly sb.emails[0])
-	is this useful?
+	this isn't hard and it might be handy for some people
+
+
+Future / In doubt
+-----------------
+(things listed here are either marked to do in some future (because we have to
+wait on some feature being popular) or are in doubt of ever being implemented
+at all)
+
+msn client
+* signal handling
+	when the signal module becames popular on distros, we can get rid of
+	the select() ugly stuff for auto-away and re-implement it using
+	alarm() or things like that
+* create a detached client with a server/client model
+	this can be either a one-pc-only detached mode or maybe some kind of
+	web interface; after all it should have very much in common, and with
+	a couple of cookie handling and a browser which supports auto-refresh,
+	a piece of cake (and java-free)
+
+msn lib 
 * higher level callbacks
 	I'm not that sure about this one.. it would be really nice to have ALL
 	the protocol encapsulated and then call upper-level callbacks if the
 	user just don't want to care about, for instance, message parsing.
 	But, OTOH, maybe it's not worthy.
-
+* make login async (maybe using something like continuations?)
+	continuations would have worked nicely, but are only available in
+	python >= 2.2 (which many people don't have), so it will have to wait
+	for a bit
+* group support (part is in place (eg. gid))
+	do we really want this? If not, drop partial support
+* multi-user chat handling 
+ 	part in place, the ugly sb.emails[0]
+	is this useful? If not, drop partial support
+* file transfer
+	this is waaaaay below in my priority lists. there are thousand of
+	better ways to do file transfer between two hosts; plus the protocol
+	is even more ugly than the messaging one (yes, it really is)
+* micq integration
+	in a perfect world, this wouldn't even exist and we would have micq
+	talk the msn protocol. so the ideal solution would be to rewrite all
+	this in C and integrate it with micq; probably working in two
+	different levels: one would be the C implementation of this, and the
+	other one micq multiprotocol support, that allows a clean integration
+	with it
 
diff -ruN msnlib-c1/msn msnlib-c2/msn
--- msnlib-c1/msn	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/msn	Tue Oct 22 22:07:43 2002
@@ -3,6 +3,7 @@
 
 import sys
 import os
+import socket
 import select
 import string
 import traceback
@@ -12,10 +13,14 @@
 import msnlib
 import msncb
 
+
 """
 MSN Client
 
-This is an example (yet usable) msn client.
+This is a fully usable msn client, which also serves as reference
+implementation for msnlib.
+For further information refer to the documentation or the source (which is
+always preferred).
 """
 
 #
@@ -74,7 +79,7 @@
 	sys.stdout.write('\n')
 	sys.stdout.flush()
 
-def print_list(md, only_online = '', userlist = None, include_emails = 0):
+def print_list(md, only_online = None, userlist = None, include_emails = 0):
 	"Prints the user list"
 	if not userlist:
 		userlist = md.users
@@ -102,6 +107,9 @@
 	if u.homep: out += c.bold + 'Home phone:\t' + c.normal + u.homep + '\n'
 	if u.workp: out += c.bold + 'Work phone:\t' + c.normal + u.workp + '\n'
 	if u.mobilep: out += c.bold + 'Mobile phone:\t' + c.normal + u.mobilep + '\n'
+	if u.priv.has_key('typing') and u.priv['typing']:
+		out += c.bold + 'Last typing at:\t' + c.normal
+		out += time.asctime(time.localtime(u.priv['typing'])) + '\n'
 	if u.sbd: 
 		out += c.bold + 'Switchboard:\t' + c.normal + str(u.sbd) + '\n'
 		if u.sbd.msgqueue:
@@ -156,11 +164,22 @@
 # useful functions
 #
 
+def quit(code = 0):
+	"Exits"
+	printl('Closing\n', c.green, 1)
+	try:
+		m.disconnect()
+	except:
+		pass
+	sys.exit(code)
+
 def nick2email(nick):
 	"Returns an email according to the given nick, or None if noone	matches"
 	for email in m.users.keys():
 		if m.users[email].nick == nick:
 			return email
+	if nick in m.users.keys():
+		return nick
 	return None
 
 def email2nick(email):
@@ -228,40 +247,38 @@
 #
 
 def parse_cmd(cmd):
-	global c, last_sent, last_received
-	# only newline or empty string
-	if len(cmd) <= 1:
-		if len(cmd) == 0:
-			print
-			print '!!! EOF'
-			m.disconnect()
-			sys.exit(0)
-		else:
-			return ''
+	"""Parses the commands introduced by the user. It's pretty long and
+	boring, as expected."""
+	
+	global c, last_sent, last_received	# ugly but necesary
+	
+	if len(cmd) == 0:
+		quit()
+	elif len(cmd) == 1:
+		return ''
 			
-	# cut trailing newline
+	# cut trailing newline and clean up
 	if cmd[-1] == '\n':
 		cmd = cmd[:-1]
-
+	cmd = cmd.lstrip()
 	cmd = cmd.split()
 	if len(cmd) > 1:
 		cmd, params = cmd[0], string.join(cmd[1:])
 	else:
+		if not cmd: return ''
 		cmd = cmd[0]
 		params = ''
 	
 	# parse
 	if   cmd == 'status': 		# change status
 		if not params:
-			return 'Status: %s' % msnlib.reverse_status[m.status]
+			return 'Your current status is %s' % msnlib.reverse_status[m.status]
 		if not m.change_status(params):
 			return 'Status must be one of: online, away, busy, brb, phone, lunch, invisible, idle or offline'
 		return 'Status changed to: %s' % params
 		
 	elif cmd == 'q':		# quit
-		printl('Closing\n', c.green, 1)
-		m.disconnect()
-		sys.exit(0)
+		quit()
 	
 	elif cmd == 'reload':		# reload callbacks
 		reload(msncb)
@@ -270,11 +287,17 @@
 	elif cmd == 'w':		# list
 		print_list(m)
 	
-	elif cmd == 'e':		# list (online only)
-		print_list(m, 1)
+	elif cmd == 'ww':		# list, include emails
+		print_list(m, include_emails = 1)
 	
 	elif cmd == 'wr':		# reverse list
 		print_list(m, userlist = m.reverse, include_emails = 1)
+	
+	elif cmd == 'e':		# list (online only)
+		print_list(m, only_online = 1)
+
+	elif cmd == 'ee':
+		print_list(m, only_online = 1, include_emails = 1)
 
 	elif cmd == 'raw':		# send a raw message
 		try:
@@ -314,17 +337,25 @@
 			return 'Error parsing cmd'
 		email = nick2email(p[0])
 		if not email: 
-			return 'Unknown nick (%s)' % str(email)
+			return 'Unknown nick (%s)' % p[0]
 		if not m.users[email].sbd:
-			return 'No socket opened for %s' % nick
+			return 'No socket opened for %s' % p[0]
 		desc = str(m.users[email].sbd)
 		m.close(m.users[email].sbd)
 		return 'Closed socket %s' % desc
 	
 	elif cmd == 'privacy':		# set privacy mode
 		p = params.split()
-		if len(p) != 2: return 'Error parsing cmd'
-		m.privacy(int(p[0]), int(p[1]))
+		if len(p) != 2: 
+			return 'Error parsing cmd'
+		try:
+			public = int(p[0])
+			auth = int(p[1])
+			if public not in (0, 1) or auth not in (0, 1):
+				return 'Error: both parameters must be 1 or 0'
+		except:
+			return 'Error: both parameters must be 1 or 0'
+		m.privacy(public, auth)
 	
 	elif cmd == 'add':		# add a user
 		p = params.split()
@@ -341,6 +372,8 @@
 		p = params.split()
 		if len(p) != 1: return 'Error parsing cmd'
 		email = nick2email(p[0])
+		if not email:
+			return 'Unknown nick (%s)' % p[0]
 		m.userdel(email)
 	
 	elif cmd == 'nick':		# change our nick
@@ -370,7 +403,7 @@
 		else:
 			email = nick2email(p[0])
 			if not email:
-				return 'Unknown nick (%s)' % str(email)
+				return 'Unknown nick (%s)' % str(p[0])
 			print_user_info(email)
 	
 	elif cmd == 'sync':		# manual sync
@@ -388,6 +421,8 @@
 	elif cmd == 'm' or cmd == 'msg' or cmd == 'r' or cmd == 'a':
 		if cmd == 'm' or cmd == 'msg':
 			p = params.split()
+			if len(p) < 1:
+				return 'Please enter a nick and a message'
 			nick = p[0]
 			email = nick2email(nick)
 			msg = string.join(p[1:])
@@ -402,7 +437,9 @@
 			if not nick: nick = email
 			msg = params
 		if not email:
-			return 'Unknown nick (%s)' % str(email)
+			if cmd == 'a': return 'Please write a message first'
+			if cmd == 'r': return 'Please reply a message first'
+			else: return 'Unknown nick %s' % str(p[0])
 		r = m.sendmsg(email, msg)
 		last_sent = email
 		if r == 1:
@@ -418,12 +455,13 @@
 	elif cmd == 'help' or cmd == '?':
 		r = ''
 		r += 'Command list:\n'
-		r += 'status [mode]\t Changes status to "mode", which can be '
-		r += 'one of: online, away, busy, brb, phone, lunch, '
-		r += 'invisible, idle or offline; or show the current status\n'
+		r += 'status [mode]\t Shows the current status, or changes it to "mode", which can be one of:\n'
+		r += '\t\t\t online, away, busy, brb, phone, lunch, invisible, idle or offline\n'
 		r += 'q\t\t Quits the program\n'
 		r += 'w\t\t Prints your entire contact list\n'
+		r += 'ww\t\t Prints your entire contact list, including email addresses\n'
 		r += 'e\t\t Prints your online contacts\n'
+		r += 'ee\t\t Prints your online contacts, including email addresses\n'
 		r += 'wr\t\t Prints your reverse contact list\n'
 		r += 'h\t\t Shows your incoming message history\n'
 		r += 'add email nick\t Adds the user "email" with the nick "nick"\n'
@@ -432,12 +470,13 @@
 		r += 'close nick\t Closes the switchboard connection with "nick"\n'
 		r += 'config\t\t Shows the configuration\n'
 		r += 'nick newnick\t Changes your nick to "newnick"\n'
+		r += 'privacy p a\t Sets whether accept messages from people not on your list (p) and require authorization (a)\n'
 		r += 'm nick text\t Sends a message to "nick" with the "text"\n'
-		r += 'a text\t Sends a message with "text" to the last person you sent a message to\n'
-		r += 'r text\t Sends a message with "text" to the last person that sent you a message\n'
+		r += 'a text\t\t Sends a message with "text" to the last person you sent a message to\n'
+		r += 'r text\t\t Sends a message with "text" to the last person that sent you a message\n'
 		return r
 	else:
-		return 'Unk cmd'
+		return 'Unknown command, type "help" for help'
 	
 	return ''
 
@@ -458,7 +497,9 @@
 	status = msnlib.reverse_status[t[0]]
 	email = t[1]
 	nick = md.users[email].nick
-	printl('\n' + nick, c.blue, 1)
+	ctime = time.strftime('%I:%M:%S%p')
+	printl('\n%s ' % ctime, c.blue)
+	printl(nick, c.blue, 1)
 	printl(' changed status to ', c.magenta)
 	printl('%s\n' % status, c.magenta, 1)
 	log_msg(email, 'status', status)
@@ -470,7 +511,9 @@
 	t = string.split(params)
 	email = t[0]
 	nick = md.users[email].nick
-	printl('\n' + nick, c.blue, 1)
+	ctime = time.strftime('%I:%M:%S%p')
+	printl('\n%s ' % ctime, c.blue)
+	printl(nick, c.blue, 1)
 	printl(' changed status to ', c.magenta)
 	printl('%s\n' % status, c.magenta, 1)
 	log_msg(email, 'status', status)
@@ -480,7 +523,9 @@
 def cb_fln(md, type, tid, params):
 	email = tid
 	nick = md.users[email].nick
-	printl('\n' + nick, c.blue, 1)
+	ctime = time.strftime('%I:%M:%S%p')
+	printl('\n%s ' % ctime, c.blue)
+	printl(nick, c.blue, 1)
 	printl(' disconnected\n', c.magenta)
 	log_msg(email, 'status', 'disconnect')
 	msncb.cb_fln(md, type, tid, params)
@@ -498,8 +543,12 @@
 	global last_received
 	t = string.split(tid)
 	email = t[0]
+	
 	# messages from hotmail are only when we connect, and send things
-	# regarding, aparently, hotmail issues. we ignore them
+	# regarding, aparently, hotmail issues. we ignore them (basically
+	# because i couldn't care less; however if somebody has intrest in
+	# these and provides some debug output i'll be happy to implement
+	# parsing).
 	if email == 'Hotmail':
 		return
 
@@ -520,19 +569,26 @@
 	
 	eoh +=1
 	if headers.has_key('Content-Type') and headers['Content-Type'] == 'text/x-msmsgscontrol':
+		# the typing notices
 		nick = email2nick(email)
 		if not nick: nick = email
-		printl('\n')
-		ctime = time.strftime('%I:%M:%S%p')
-		printl('%s ' % ctime, c.blue)
-		printl('%s' % nick, c.cyan, 1)
-		printl(' is typing\n', c.magenta)
+		if not m.users[email].priv.has_key('typing'):
+			m.users[email].priv['typing'] = 0
+		if not m.users[email].priv['typing']:
+			printl('\n')
+			ctime = time.strftime('%I:%M:%S%p')
+			printl('%s ' % ctime, c.blue)
+			printl('%s' % nick, c.cyan, 1)
+			printl(' is typing\n', c.magenta)
+		m.users[email].priv['typing'] = time.time()
 	else:
+		# messages
+		m.users[email].priv['typing'] = 0
 		print
 		print_inc_msg(email, lines, eoh)
 		log_msg(email, 'in', string.join(lines[eoh:], '\n'))
 
-		# append the message to the list, keeping it below the configured limit
+		# append the message to the history, keeping it below the configured limit
 		if len(history_ring) > config['history size']: 
 			del(history_ring[0])
 		history_ring.append((time.time(), email, lines[eoh:]))
@@ -603,7 +659,7 @@
 #
 # now the real thing
 #
-printl('Starting up MSN Client C1\n', c.yellow, 1)
+printl('Starting up MSN Client C2\n', c.yellow, 1)
 
 # first, the configuration
 printl('Loading config... ', c.green, 1)
@@ -615,20 +671,20 @@
 config = get_config(file)
 if not config:
 	perror('Error opening config file (%s), try running "msnsetup"\n' % file)
-	sys.exit(1)
+	quit(1)
 
 # set the mandatory values
 if config.has_key('email'): 
 	m.email = config['email']
 else: 
 	perror('Error: email not specified in config file\n')
-	sys.exit(1)
+	quit(1)
 
 if config.has_key('password'):
 	m.pwd = config['password']
 else:
 	perror('Error: password not specified in config file\n')
-	sys.exit(1)
+	quit(1)
 
 # and the optional ones, setting the defaults if not present
 # history size
@@ -697,10 +753,16 @@
 	else:
 		desc = msncb.error_table[errno]
 	perror('Error: %s\n' % desc)
-	sys.exit(1)
+	quit(1)
+except KeyboardInterrupt:
+	quit()
+except ('SocketError', socket.error), info:
+	desc = info[1]
+	perror('Network error: ' + desc + '\n')
+	quit()
 except:
 	pexc('Exception logging in\n')
-	sys.exit(1)
+	quit()
 
 
 # call sync to get the lists and refresh
@@ -728,8 +790,6 @@
 auto_away = 0
 
 
-beep()
-
 # loop
 print_prompt()
 while 1:
@@ -738,7 +798,10 @@
 	outfd = fds[1]
 	infd.append(sys.stdin)
 	print_prompt()
-	fds = select.select(infd, outfd, [], timeout)
+	try:
+		fds = select.select(infd, outfd, [], timeout)
+	except KeyboardInterrupt:
+		quit()
 	
 	if timeout and len(fds[0] + fds[1]) == 0:
 		# timeout, set auto away
@@ -760,18 +823,17 @@
 		else:
 			try:
 				m.read(i)
-			except 'SocketError':
+			except ('SocketError', socket.error), err:
 				if i != m:
-					#printl('\n!!! Socket %s closed\n' % i, c.green)
 					if i.msgqueue:
 						nick = email2nick(i.emails[0])
-						printl("\nConnection with %s closed - the following messages couldn't be sent:\n" % nick, c.green, 1)
+						printl("\nConnection with %s closed - the following messages couldn't be sent:\n" % (nick), c.green, 1)
 						for msg in i.msgqueue:
 							printl(c.bold + '\t>>> ' + c.normal + msg + '\n')
 					m.close(i)
 				else:
-					printl('\nMain socket closed\n', c.red)
-					sys.exit(1)
+					printl('\nMain socket closed (%s)\n' % str(err), c.red)
+					quit(1)
 
 
 
diff -ruN msnlib-c1/msnlib.py msnlib-c2/msnlib.py
--- msnlib-c1/msnlib.py	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/msnlib.py	Tue Oct 22 22:07:43 2002
@@ -50,8 +50,7 @@
 
 
 class user:
-	"""User class, used to store your 'friends'. Obviously it's only a
-	placeholder for now."""
+	"""User class, used to store your 'friends'"""
 	def __init__(self, email = None, nick = None, gid = None):
 		self.email = email
 		self.nick = nick
@@ -62,6 +61,7 @@
 		self.workp = None
 		self.mobilep = None
 		self.sbd = None
+		self.priv = {}
 	
 	def __repr__(self):
 		return '<user email:%s nick:"%s" gid:%s>' % (email, nick, gid)
diff -ruN msnlib-c1/msnsetup msnlib-c2/msnsetup
--- msnlib-c1/msnsetup	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/msnsetup	Tue Oct 22 22:07:43 2002
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-VER="C1"
+VER="C2"
 
 function intro() {
 	echo
diff -ruN msnlib-c1/setup.py msnlib-c2/setup.py
--- msnlib-c1/setup.py	Tue Sep 24 12:04:50 2002
+++ msnlib-c2/setup.py	Tue Oct 22 22:07:43 2002
@@ -3,7 +3,7 @@
 from distutils.core import setup
 
 setup(name="msnlib",
-	version="C1",
+	version="C2",
 	description="MSN Messenger Library and Client",
 	author="Alberto Bertogli",
 	author_email="albertogli@telpin.com.ar",
