summaryrefslogtreecommitdiffstats
path: root/Lib/ftplib.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1992-11-05 22:22:37 (GMT)
committerGuido van Rossum <guido@python.org>1992-11-05 22:22:37 (GMT)
commitc567c60135a33b23cd5f0363e4a80b2d21de81d9 (patch)
treef87a57b78bb18ae09a3979887b67938b67e15cb9 /Lib/ftplib.py
parentc629d34c4f1797b690a6c93ea3e2a5b82698b686 (diff)
downloadcpython-c567c60135a33b23cd5f0363e4a80b2d21de81d9.zip
cpython-c567c60135a33b23cd5f0363e4a80b2d21de81d9.tar.gz
cpython-c567c60135a33b23cd5f0363e4a80b2d21de81d9.tar.bz2
Added much functionality, changed some names (errors, login).
Diffstat (limited to 'Lib/ftplib.py')
-rw-r--r--Lib/ftplib.py214
1 files changed, 160 insertions, 54 deletions
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_OFFSET = 40000
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 @@ class FTP:
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 @@ class FTP:
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 @@ class FTP:
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 @@ class FTP:
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 @@ def test():
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':