Added much functionality, changed some names (errors, login).
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index b360942..4e74090 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -2,22 +2,45 @@
 # (FTP), by J. Postel and J. Reynolds
 
 
+# Example:
+#
+# >>> from ftplib import FTP
+# >>> ftp = FTP().init('ftp.cwi.nl') # connect to host, default port
+# >>> ftp.login() # default, i.e.: user anonymous, passwd user@hostname
+# >>> def handle_one_line(line): # callback for ftp.retrlines
+# ...     print line
+# ... 
+# >>> ftp.retrlines('LIST', handle_one_line) # list directory contents
+# total 43
+# d--x--x--x   2 root     root         512 Jul  1 16:50 bin
+# d--x--x--x   2 root     root         512 Sep 16  1991 etc
+# drwxr-xr-x   2 root     ftp        10752 Sep 16  1991 lost+found
+# drwxr-srwt  15 root     ftp        10240 Nov  5 20:43 pub
+# >>> ftp.quit()
+#
+# To download a file, use ftp.retrlines('RETR ' + filename, handle_one_line),
+# or ftp.retrbinary() with slightly different arguments.
+# To upload a file, use ftp.storlines() or ftp.storbinary(), which have
+# an open file as argument.
+# The download/upload functions first issue appropriate TYPE and PORT
+# commands.
+
+
 import os
 import sys
 import socket
 import string
 
 
-# Default port numbers used by the FTP protocol
+# The standard FTP server control port
 FTP_PORT = 21
-FTP_DATA_PORT = 20
 
 
 # Exception raiseds when an error or invalid response is received
-error_reply = 'nntp.error_reply'	# unexpected [123]xx reply
-error_function = 'nntp.error_function'	# 4xx errors
-error_form = 'nntp.error_form'		# 5xx errors
-error_protocol = 'nntp.error_protocol'	# response does not begin with [1-5]
+error_reply = 'ftplib.error_reply'	# unexpected [123]xx reply
+error_temp = 'ftplib.error_temp'	# 4xx errors
+error_perm = 'ftplib.error_perm'	# 5xx errors
+error_proto = 'ftplib.error_proto'	# response does not begin with [1-5]
 
 
 # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
@@ -30,7 +53,7 @@
 PORT_CYCLE = 1000
 # XXX This is a nuisance: when using the program several times in a row,
 # reusing the port doesn't work and you have to edit the first port
-# assignment...
+# assignment...  We need getsockname()!
 
 
 # The class itself
@@ -111,18 +134,30 @@
 		self.lastresp = resp[:3]
 		c = resp[:1]
 		if c == '4':
-			raise error_function, resp
+			raise error_temp, resp
 		if c == '5':
-			raise error_form, resp
+			raise error_perm, resp
 		if c not in '123':
-			raise error_protocol, resp
+			raise error_proto, resp
 		return resp
 
+	# Expect a response beginning with '2'
+	def voidresp(self):
+		resp = self.getresp()
+		if resp[0] <> '2':
+			raise error_reply, resp
+
 	# Send a command and return the response
 	def sendcmd(self, cmd):
 		self.putcmd(cmd)
 		return self.getresp()
 
+	# Send a command and ignore the response, which must begin with '2'
+	def voidcmd(self, cmd):
+		resp = self.sendcmd(cmd)
+		if resp[0] <> '2':
+			raise error_reply, resp
+
 	# Send a PORT command with the current host and the given port number
 	def sendport(self, port):
 		hostname = socket.gethostname()
@@ -131,9 +166,7 @@
 		pbytes = [`port/256`, `port%256`]
 		bytes = hbytes + pbytes
 		cmd = 'PORT ' + string.joinfields(bytes, ',')
-		resp = self.sendcmd(cmd)
-		if resp[:3] <> '200':
-			raise error_reply, resp
+		self.voidcmd(cmd)
 
 	# Create a new socket and send a PORT command for it
 	def makeport(self):
@@ -146,38 +179,62 @@
 		resp = self.sendport(port)
 		return sock
 
-	# Retrieve data in binary mode.  (You must set the mode first.)
-	# The argument is a RETR command.
-	# The callback function is called for each block.
-	# This creates a new port for you
-	def retrbinary(self, cmd, callback, blocksize):
+	# Send a port command and a transfer command, accept the connection
+	# and return the socket for the connection
+	def transfercmd(self, cmd):
 		sock = self.makeport()
 		resp = self.sendcmd(cmd)
 		if resp[0] <> '1':
 			raise error_reply, resp
-		conn, host = sock.accept()
-		sock.close()
+		conn, sockaddr = sock.accept()
+		return conn
+
+	# Login, default anonymous
+	def login(self, *args):
+		user = passwd = acct = ''
+		n = len(args)
+		if n > 3: raise TypeError, 'too many arguments'
+		if n > 0: user = args[0]
+		if n > 1: passwd = args[1]
+		if n > 2: acct = args[2]
+		if not user: user = 'anonymous'
+		if user == 'anonymous' and passwd in ('', '-'):
+			thishost = socket.gethostname()
+			if os.environ.has_key('LOGNAME'):
+				realuser = os.environ['LOGNAME']
+			elif os.environ.has_key('USER'):
+				realuser = os.environ['USER']
+			else:
+				realuser = 'anonymous'
+			passwd = passwd + realuser + '@' + thishost
+		resp = self.sendcmd('USER ' + user)
+		if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
+		if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
+		if resp[0] <> '2':
+			raise error_reply, resp
+
+	# Retrieve data in binary mode.
+	# The argument is a RETR command.
+	# The callback function is called for each block.
+	# This creates a new port for you
+	def retrbinary(self, cmd, callback, blocksize):
+		self.voidcmd('TYPE I')
+		conn = self.transfercmd(cmd)
 		while 1:
 			data = conn.recv(blocksize)
 			if not data:
 				break
 			callback(data)
 		conn.close()
-		resp = self.getresp()
-		if resp[0] <> '2':
-			raise error_reply, resp
+		self.voidresp()
 
-	# Retrieve data in line mode.  (You must set the mode first.)
+	# Retrieve data in line mode.
 	# The argument is a RETR or LIST command.
 	# The callback function is called for each line, with trailing
 	# CRLF stripped.  This creates a new port for you
 	def retrlines(self, cmd, callback):
-		sock = self.makeport()
-		resp = self.sendcmd(cmd)
-		if resp[0] <> '1':
-			raise error_reply, resp
-		conn, host = sock.accept()
-		sock.close()
+		resp = self.sendcmd('TYPE A')
+		conn = self.transfercmd(cmd)
 		fp = conn.makefile('r')
 		while 1:
 			line = fp.readline()
@@ -190,36 +247,85 @@
 			callback(line)
 		fp.close()
 		conn.close()
-		resp = self.getresp()
-		if resp[0] <> '2':
-			raise error_reply, resp
+		self.voidresp()
 
-	# Login as user anonymous with given passwd (default user@thishost)
-	def anonymouslogin(self, *args):
-		resp = self.sendcmd('USER anonymous')
-		if resp[0] == '3':
-			if args:
-				passwd = args[0]
-			else:
-				thishost = socket.gethostname()
-				if os.environ.has_key('LOGNAME'):
-					user = os.environ['LOGNAME']
-				elif os.environ.has_key('USER'):
-					user = os.environ['USER']
-				else:
-					user = 'anonymous'
-				passwd = user + '@' + thishost
-			resp = self.sendcmd('PASS ' + passwd)
-		if resp[0] <> '2':
+	# Store a file in binary mode
+	def storbinary(self, cmd, fp, blocksize):
+		self.voidcmd('TYPE I')
+		conn = self.transfercmd(cmd)
+		while 1:
+			buf = fp.read(blocksize)
+			if not buf: break
+			conn.send(buf)
+		conn.close()
+		self.voidresp()
+
+	# Store a file in line mode
+	def storlines(self, cmd, fp):
+		self.voidcmd('TYPE A')
+		conn = self.transfercmd(cmd)
+		while 1:
+			buf = fp.readline()
+			if not buf: break
+			if buf[-2:] <> CRLF:
+				if buf[-1] in CRLF: buf = buf[:-1]
+				buf = buf + CRLF
+			conn.send(buf)
+		conn.close()
+		self.voidresp()
+
+	# Return a list of files in a given directory (default the current)
+	def nlst(self, *args):
+		cmd = 'NLST'
+		for arg in args:
+			cmd = cmd + (' ' + arg)
+		files = []
+		self.retrlines(cmd, files.append)
+		return files
+
+	# Rename a file
+	def rename(self, fromname, toname):
+		resp = self.sendcmd('RNFR ' + fromname)
+		if resp[0] <> '3':
 			raise error_reply, resp
+		self.voidcmd('RNTO ' + toname)
+
+	# Make a directory, return its full pathname
+	def mkd(self, dirname):
+		resp = self.sendcmd('MKD ' + dirname)
+		return parse257(resp)
+
+	# Return current wording directory
+	def pwd(self):
+		resp = self.sendcmd('PWD')
+		return parse257(resp)
 
 	# Quit, and close the connection
 	def quit(self):
-		resp = self.sendcmd('QUIT')
-		if resp[0] <> '2':
-			raise error_reply, resp
+		self.voidcmd('QUIT')
 		self.file.close()
 		self.sock.close()
+		del self.file, self.sock
+
+
+# Parse a response type 257
+def parse257(resp):
+	if resp[:3] <> '257':
+		raise error_reply, resp
+	if resp[3:5] <> ' "':
+		return '' # Not compliant to RFC 959, but UNIX ftpd does this
+	dirname = ''
+	i = 5
+	n = len(resp)
+	while i < n:
+		c = resp[i]
+		i = i+1
+		if c == '"':
+			if i >= n or resp[i] <> '"':
+				break
+			i = i+1
+		dirname = dirname + c
+	return dirname
 
 
 # Test program.
@@ -239,7 +345,7 @@
 		host = sys.argv[1]
 		ftp = FTP().init(host)
 		ftp.debug(debugging)
-		ftp.anonymouslogin()
+		ftp.login()
 		def writeln(line): print line
 		for file in sys.argv[2:]:
 			if file[:2] == '-l':