diff -ruN msnlib-d2/INSTALL msnlib-d3/INSTALL
--- msnlib-d2/INSTALL	Mon Oct 28 20:55:02 2002
+++ msnlib-d3/INSTALL	Tue Nov 26 11:44:30 2002
@@ -7,7 +7,7 @@
 
 Then, you can run (as your user) 'msnsetup' to create the initial
 configuration file and directories (placed in ~/.msn); and finally run the
-client with 'msn'.
+client with 'msn'. Skip this step if you are upgrading.
 
 Alternatively, you can create your own ~/.msn/msnrc file based on an example
 named 'msnrc.sample'.
@@ -24,7 +24,7 @@
 # now drop the root privileges
 exit
 
-# run the setup to create the configuration
+# run the setup to create the configuration (only if you are not upgrading)
 msnsetup
 
 # and finally start the client
diff -ruN msnlib-d2/doc/Changelog msnlib-d3/doc/Changelog
--- msnlib-d2/doc/Changelog	Wed Oct 30 19:14:58 2002
+++ msnlib-d3/doc/Changelog	Tue Nov 26 11:44:30 2002
@@ -1,6 +1,39 @@
+26 Nov 02 11.36.00 - Alberto <albertogli@telpin.com.ar>
+ * tag: d3 tag
+
+24 Nov 02 12.50.11 - Alberto <albertogli@telpin.com.ar>
+ * msn: completed the FAQ, and added a reporting_bugs document
+ * msn: small changes to the INSTALL file
+ * msn: return nothing on log_msg as the return value is not important
+
+19 Nov 02 15.55.52 - Alberto <albertogli@telpin.com.ar>
+ * msn: fix two big bugs with terminal handling; using nonblocking io makes
+	flushes and write fail under certain conditions (quite easily
+	triggered when running into a X terminal), so now they're replaced
+	with safe wrappers. Thanks to kusamochi@msn.com for the report, and
+	ameoba@opn for the huge help with the fix.
+ * msnlib: improved the close call, so now it won't raise an exception on a
+	sbd that has a non-established socket
+
+17 Nov 02 10.26.31 - Alberto <albertogli@telpin.com.ar>
+ * msn: enabling debug prints the terminal size and termios use
+
+12 Nov 02 00.42.58 - Alberto <albertogli@telpin.com.ar>
+ * msn: changed some command output to look less cryptic
+ * doc: add the FAQ
+
+08 Nov 02 00.03.43 - Alberto <albertogli@telpin.com.ar>
+ * msn: improved tab completion adding basic cycling
+ * msn: be able to disable logging using the already-existing 'log history'
+	configuration variable
+
+31 Oct 02 15.38.45 - Alberto <albertogli@telpin.com.ar>
+ * msnlib: remove unused constant BSIZE
+ * msnlib: update VERSION
+
 30 Oct 02 18.35.28 - Alberto <albertogli@telpin.com.ar>
- * msn: esthetic changes
- * msn: d2 tag
+ * msn: esthetic code changes
+ * tag: d2 tag
 
 29 Oct 02 08.50.14 - Alberto <albertogli@telpin.com.ar>
  * msn: fix a small bug with the up and down keys
diff -ruN msnlib-d2/doc/FAQ msnlib-d3/doc/FAQ
--- msnlib-d2/doc/FAQ	Wed Dec 31 21:00:00 1969
+++ msnlib-d3/doc/FAQ	Tue Nov 26 11:44:30 2002
@@ -0,0 +1,122 @@
+
+* What are the 'Message for NNN queued for delivery' and 'Flushing messages
+	to...' and what do they do?
+
+When you send a message to someone, the client has to open a connection first;
+as this takes time, the message is put in a queue until the connection is
+ready, and then they are sent.
+The 'Message for NNN queued for delivery' tells you that the message has been
+queued, then, a connection is tried to establish, and once this is done, the
+messages in the queue are sent, and you are told with 'Flushing messages
+to...'.
+The connection is not done for every message, so you don't see this every
+time, but usually only once for the first message.
+
+
+* What's the meaning of 'User NNN is typing' and why only appears once?
+
+Those are special messages telling you that the user 'NNN' is typing a
+message, and are sent by the client. Note that this msn client does _not_ send
+these messages.
+You only see them one because they are usually sent every 5 seconds, which
+makes them highly annoying, so the client only displays one. If you want to
+know the last time one of these messages was sent, you can do it using the
+'info' command.
+
+
+* How does tab completion work?
+
+At this moment, it's simple and very basic: you can cycle through the person
+who last you received a message from, and the one you last sent one to.
+Note that this cycling only works when the commandline is empty, or when you
+have previosuly tabbed.
+
+
+* How does auto-away work?
+
+It's quite simple: you configure it in your msnrc file, using a line like
+'auto away = SECS' where SECS is the number of seconds after that, if you
+didn't type anything, you're automatically set to away.
+Then, when you come back and type, your status is automatically changed back
+to online.
+Note that this will work only when your status is online, because if i'm
+'busy' i don't want it going back to away =)
+
+
+* Where are my log files, and what's the format to read them?
+
+The log files are kept by default in $HOME/.msn/history, but it can be changed
+using the 'history directory' option in your msnrc file.
+The format is described in doc/log_format.
+
+
+* What are the different ways of sending messages?
+
+You have three basic commands for sending messages:
+The first one is 'm' (or its alias, 'msg'), which takes a nick or an email as
+a parameter, and sends a message to that person. This is what tab completion
+types for you.
+The other two are the 'a' and 'r' messages; 'a' sends a message to the last
+person you sent a message to; and 'r' replies a message to the last person you
+received a message from.
+
+
+* What's the reverse contact list?
+
+It's simply the list of all the people who have you on their contact list. You
+can see it with the 'wr' command.
+
+
+* How do the privacy option work? What is it for?
+
+The privacy option lets you set if you want people to ask you for
+authorization to add you (i never implemented user authorization, so i have no
+idea about how or if this works), and to block messages from people which are
+not on your list. This is all done by the server so no permanent configuration
+is needed.
+
+The command is called 'privacy', and has two parameters, called 'a' and 'p';
+which are set to 1 (yes) or 0 (no), and come from 'Authorization' and
+'Privacy', the two policies we just talked about'.
+
+So, if you don't want to receive messages from people which are not on your
+list, but you want people to add you without any authorization, the command
+would be 'privacy 0 1'.
+
+
+* Is there a way of enabling debugging without it getting in the middle of my
+	session?
+
+Sure, just run "msn 2> msn.debug.output"; it will redirect stderr (where debug
+messages go) to the file called 'msn.debug.output'. Remember to enable
+debugging too, using either the 'debug' command or adding 'debug = yes' to the
+msnrc file.
+
+
+* Why does the contact list has this weird order?
+
+The order in the contact list is given by the email addresses, which obviously
+has no reference whatsoever to the nicks; so even if it looks unsorted, you
+notice an alphabetic sort quite clearly when you do 'ww'.
+
+
+* I get 'Main socket closed' followed by a strange error everyonce in a while
+
+Output sample:
+	[msn] status invisible
+	Status changed to: invisible
+	[msn]
+	Main socket closed ((104, 'Connection reset by peer'))
+	Closing
+	; 
+
+This happens when some unexpected network error occurs. It depends highly on
+the error, but most of the times it's because the MSN server has closed our
+main connection without reason, or our internet connection just dropped. There
+isn't much we can do about it; we could reconnect and that's on the TODO list.
+But if you get these errors normally, after checking your internet connection
+(it could be just that) please send me the error report (a copy/paste of the
+output would be just fine).
+
+
+
diff -ruN msnlib-d2/doc/reporting_bugs msnlib-d3/doc/reporting_bugs
--- msnlib-d2/doc/reporting_bugs	Wed Dec 31 21:00:00 1969
+++ msnlib-d3/doc/reporting_bugs	Tue Nov 26 11:44:30 2002
@@ -0,0 +1,38 @@
+
+How to report bugs
+------------------
+
+If you think you've got a bug (or you are sure about it =), please report it
+to albertogli@telpin.com.ar; specifying:
+
+* msnlib version
+* python version
+* operating system information
+* platform information
+* obviously, a bug description
+
+Now, depending on what's the bug about, i'd like you to to include some of
+these:
+
+* a copy of the output when the bug hits (see below for a complete to do this)
+* reports on other versions (does this happens with an older version?)
+* instructions on how to reproduce it
+* terminal information (ie. running under normal console, some kind of xterm,
+	cygwin terminal, beos one, etc.)
+* an strace -tt of the session
+* the debug output
+
+On these last two, they're oftenly quite useful. Just in case you don't know
+how to do it, a nice way of doing both at the same time and save to files
+(which then you can send to me as attachments) is:
+
+* first, add 'debug = yes' to $HOME/.msn/msnrc; that enables debugging output
+* then, run the command:
+	strace -tt -o msn_output-strace msn 2>msn_output-debug | tee msn_output-stdout
+	
+and try to reproduce the bug, or just wait for it to happen =)
+
+Then, when reporting, send me these 3 files (msn_output-strace,
+msn_output-debug, and msn_output-stdout), compressed if necesary. They are
+really helpful, because I can see what's going on when the bug happened.
+
diff -ruN msnlib-d2/msn msnlib-d3/msn
--- msnlib-d2/msn	Wed Oct 30 18:35:02 2002
+++ msnlib-d3/msn	Tue Nov 26 11:44:30 2002
@@ -55,8 +55,8 @@
 	if bold:
 		out = c.bold
 	out = color + out + line + c.normal
-	sys.stdout.write(out)
-	sys.stdout.flush()
+	safe_write(out)
+	safe_flush()
 
 def perror(line):
 	"Prints an error"
@@ -65,20 +65,20 @@
 	out += c.red + c.bold + '!' + c.normal
 	out += c.blue + c.bold + '!' + c.normal
 	out += ' ' + c.green + c.bold + line + c.normal + '\a'
-	sys.stdout.write(out)
-	sys.stdout.flush()
+	safe_write(out)
+	safe_flush()
 
 def pexc(line):
 	"Prints an exception"
 	out = '\n'
 	out += ( c.cyan + c.bold + '!' + c.normal ) * 3
-	sys.stdout.write(out)
-	sys.stdout.write(c.bold + line)
-	sys.stdout.flush()
+	safe_write(out)
+	safe_write(c.bold + line)
+	safe_flush()
 	traceback.print_exc()
-	sys.stdout.write(c.normal)
-	sys.stdout.write('\n')
-	sys.stdout.flush()
+	safe_write(c.normal)
+	safe_write('\n')
+	safe_flush()
 
 def print_list(md, only_online = None, userlist = None, include_emails = 0):
 	"Prints the user list"
@@ -89,7 +89,7 @@
 	for email in ul:
 		u = userlist[email]
 		if u.status != 'FLN': 
-			sys.stdout.write(c.bold)
+			safe_write(c.bold)
 		else:
 			if only_online:		# this is not nice, but it's simple enough
 				continue
@@ -121,8 +121,8 @@
 	
 def print_prompt():
 	"Prints the user prompt"
-	sys.stdout.write('\r' + c.red + c.bold + '[msn] ' + c.normal)
-	sys.stdout.flush()
+	safe_write('\r' + c.red + c.bold + '[msn] ' + c.normal)
+	safe_flush()
 
 def print_inc_msg(email, lines, eoh = 0, quiet = 0, ptime = 1, recvtime = 0):
 	"""Prints an incoming message from a list of lines and an optional
@@ -160,7 +160,34 @@
 	if not q:
 		printl('\a')
 	
-	
+
+def safe_flush():
+	"""Safely flushes stdout. It fixes a strange issue with flush and
+	nonblocking io, when flushing too fast."""
+	c = 0
+	while c < 100:
+		try:
+			sys.stdout.flush()
+			return
+		except IOError:
+			c +=1
+			time.sleep(0.01 * c)
+	raise Exception, 'flushed too many times, giving up. Please report!'
+
+def safe_write(text):
+	"""Safely writes to stdout. It fixes the same issue that safe_flush,
+	that is, writing too fast raises errors due to nonblocking fd."""
+	c = 1
+	while c:
+		try:
+			sys.stdout.write(text)
+			return
+		except IOError:
+			c += 1
+			time.sleep(0.01 * c)
+	raise Exception, 'wrote too many times, giving up. Please report!'
+
+
 #
 # useful functions
 #
@@ -217,6 +244,14 @@
 	pass
 		
 def log_msg(email, type, msg, mtime = 0):
+	"""Logs the message or event of the 'type', related to 'email',
+	with the content 'msg', to a file in the specified directory.  See
+	documentation for more specific details, specially about
+	formatting."""
+	
+	if not config['log history']:
+		return
+	
 	file = config['history directory'] + '/' + email
 	if not mtime:
 		mtime = time.time()
@@ -242,7 +277,7 @@
 	fd.write(out)
 	fd.close()
 	del(fd)
-	return 1
+	return
 
 
 
@@ -324,28 +359,68 @@
 			inbuf_history[0].append(inbuf[:-1])
 			inbuf_history[1] = len(inbuf_history[0]) - 1 # moves the pointer
 			
-			sys.stdout.write(char)
+			safe_write(char)
 			tmpbuf = inbuf
 			inbuf = ''
 			out = parse_cmd(tmpbuf)
 			printl(out + '\n', c.green, 1)
 			redraw_cli()
+			
 		elif char == '\b' or ord(char) == 127:		# ^H / DEL
 			inbuf = inbuf[:-2]
 			redraw_cli()
+			
 		elif char == '\t':				# tab
-			if len(inbuf) == 1 and email2nick(last_received):
-				inbuf = 'm ' + email2nick(last_received) + ' '
-			elif len(inbuf) == 1 and email2nick(last_sent):
-				inbuf = 'm ' + email2nick(last_sent) + ' '
+			# we do a basic cycling between the last received and
+			# last sent; first we build the two strings and then
+			# we see which one applies according to some messy
+			# logic
+			if email2nick(last_received):
+				mtolrecv = 'm ' + email2nick(last_received) + ' '
 			else:
-				inbuf = inbuf[:-1]
-				beep()
+				mtolrecv = None
+
+			if email2nick(last_sent):
+				mtolsent = 'm ' + email2nick(last_sent) + ' '
+			else:
+				mtolsent = None
+			
+			if len(inbuf) == 1:
+				if mtolsent:
+					inbuf = mtolsent
+				elif mtolrecv:
+					inbuf = mtolrecv
+				else:
+					inbuf = inbuf[:-1]
+					beep()
+			else:
+				# if we have mtolsent, replace with mtolrecv
+				# (if possible, otherwise beep)
+				if mtolsent and inbuf.strip() == mtolsent.strip():
+					if mtolrecv:
+						inbuf = mtolrecv
+					else:
+						inbuf = inbuf[:-1]
+						beep()
+				# the opposite case
+				elif mtolrecv and inbuf.strip() == mtolrecv.strip():
+					if mtolsent:
+						inbuf = mtolsent
+					else:
+						inbuf = inbuf[:-1]
+						beep()
+				# we have something that is neither mtolsent or
+				# mtolrecv, so we just beep
+				else:
+					inbuf = inbuf[:-1]
+					beep()
 			redraw_cli()
+			
 		elif ord(char) == 4:				# EOT
-			sys.stdout.write('\n')
+			safe_write('\n')
 			out = parse_cmd('')
 			printl(out + '\n', c.green, 1)
+			
 		elif ord(char) == 27:				# ESC
 			# we use in_esc for escape secuenses (composed of
 			# ESC + '[' + LETTER). 1 means got ESC, 2 means got
@@ -353,9 +428,11 @@
 			# generic handling
 			in_esc = 1
 			inbuf = inbuf[:-1]
+			
 		elif ord(char) < 32:				# unhandled control
 			print 'Got weird char: %d' % ord(char)
 			redraw_cli_cond(char)
+			
 		else:						# normal
 			if not in_esc:
 				redraw_cli_cond(char)
@@ -407,8 +484,8 @@
 	clear_line()
 	print_prompt()
 	lenght = screenwidth - 7	# we subsctract the prompt lenght + 1
-	sys.stdout.write(inbuf[-lenght:])
-	sys.stdout.flush()
+	safe_write(inbuf[-lenght:])
+	safe_flush()
 
 def redraw_cli_cond(char):
 	"""Same as redraw_cli, but conditional over the lenght of stdin. That
@@ -419,14 +496,14 @@
 	if len(inbuf) >= (screenwidth - 7):
 		redraw_cli()
 	else:
-		sys.stdout.write(char)
-		sys.stdout.flush()
+		safe_write(char)
+		safe_flush()
 
 def clear_line():
 	"""Clears the current line by overwriting it with spaces."""
 	global inbuf, screenwidth
 	if use_termios:
-		sys.stdout.write('\r' + (screenwidth - 1) * ' ' + '\r')
+		safe_write('\r' + (screenwidth - 1) * ' ' + '\r')
 
 
 
@@ -492,13 +569,13 @@
 			c = params[0:3]
 			p = params[4:]
 		except:
-			return 'Error parsing cmd'
+			return 'Error parsing command'
 		m._send(c, p)
 	
 	elif cmd == 'debug':		# enable/disable debugging
 		p = params.split()
 		if len(p) != 1:
-			return 'Error parsing cmd'
+			return 'Error parsing command'
 		if p[0] == 'off':
 			msnlib.debug = null
 			msncb.debug = null
@@ -522,7 +599,7 @@
 	elif cmd == 'close':		# close a connection
 		p = params.split()
 		if len(p) != 1: 
-			return 'Error parsing cmd'
+			return 'Error parsing command'
 		email = nick2email(p[0])
 		if not email: 
 			return 'Unknown nick (%s)' % p[0]
@@ -535,7 +612,7 @@
 	elif cmd == 'privacy':		# set privacy mode
 		p = params.split()
 		if len(p) != 2: 
-			return 'Error parsing cmd'
+			return 'Error parsing command'
 		try:
 			public = int(p[0])
 			auth = int(p[1])
@@ -548,7 +625,7 @@
 	elif cmd == 'add':		# add a user
 		p = params.split()
 		if   len(p) == 0: 
-			return 'Error parsing cmd'
+			return 'Error parsing command'
 		elif len(p) == 1: 
 			email = nick = p[0]
 		else: 
@@ -558,7 +635,7 @@
 	
 	elif cmd == 'del':		# delete a user
 		p = params.split()
-		if len(p) != 1: return 'Error parsing cmd'
+		if len(p) != 1: return 'Error parsing command'
 		email = nick2email(p[0])
 		if not email:
 			return 'Unknown nick (%s)' % p[0]
@@ -566,7 +643,7 @@
 	
 	elif cmd == 'nick':		# change our nick
 		p = params.split()
-		if len(p) != 1: return 'Error parsing cmd'
+		if len(p) != 1: return 'Error parsing command'
 		nick = p[0]
 		m.change_nick(nick)
 	
@@ -851,7 +928,7 @@
 #
 # now the real thing
 #
-printl('* MSN Client (release D2) *\n', c.yellow, 1)
+printl('* MSN Client (release D3) *\n', c.yellow, 1)
 
 # first, the configuration
 printl('Loading config... ', c.green, 1)
@@ -942,6 +1019,9 @@
 	msnlib.debug = null
 	msncb.debug = null
 
+# debug some internal variables
+msnlib.debug("Terminal Handling: %d" % use_termios)
+msnlib.debug("Terminal Size: %s" % str(winsize))
 
 # login to msn
 printl('Logging in... ', c.green, 1)
@@ -961,10 +1041,10 @@
 except ('SocketError', socket.error), info:
 	desc = info[1]
 	perror('Network error: ' + desc + '\n')
-	quit()
+	quit(1)
 except:
 	pexc('Exception logging in\n')
-	quit()
+	quit(1)
 
 
 # call sync to get the lists and refresh
diff -ruN msnlib-d2/msnlib.py msnlib-d3/msnlib.py
--- msnlib-d2/msnlib.py	Mon Oct 28 20:55:02 2002
+++ msnlib-d3/msnlib.py	Tue Nov 26 11:44:30 2002
@@ -14,10 +14,9 @@
 """
 
 # constants
-VERSION = 0xb1
+VERSION = 0xD3
 LOGIN_HOST = 'messenger.hotmail.com'
 LOGIN_PORT = 1863
-BSIZE = 256
 
 status_table = {
 	'online':       'NLN',
@@ -364,7 +363,10 @@
 		"Closes a given sbd"
 		self.sb_fds.remove(sb)
 		self.users[sb.emails[0]].sbd = None
-		sb.fd.close()
+		try:
+			sb.fd.close()
+		except:
+			pass
 		del(sb)
 	
 	
diff -ruN msnlib-d2/msnsetup msnlib-d3/msnsetup
--- msnlib-d2/msnsetup	Wed Oct 30 18:13:36 2002
+++ msnlib-d3/msnsetup	Tue Nov 26 11:44:30 2002
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-VER="D2"
+VER="D3"
 
 function intro() {
 	echo
diff -ruN msnlib-d2/setup.py msnlib-d3/setup.py
--- msnlib-d2/setup.py	Wed Oct 30 18:13:45 2002
+++ msnlib-d3/setup.py	Tue Nov 26 11:44:30 2002
@@ -2,7 +2,7 @@
 from distutils.core import setup
 
 setup(name="msnlib",
-	version="D2",
+	version="D3",
 	description="MSN Messenger Library and Client",
 	author="Alberto Bertogli",
 	author_email="albertogli@telpin.com.ar",
