diff options
author | Guido van Rossum <guido@python.org> | 1992-11-05 22:22:37 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1992-11-05 22:22:37 (GMT) |
commit | c567c60135a33b23cd5f0363e4a80b2d21de81d9 (patch) | |
tree | f87a57b78bb18ae09a3979887b67938b67e15cb9 /Lib/ftplib.py | |
parent | c629d34c4f1797b690a6c93ea3e2a5b82698b686 (diff) | |
download | cpython-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.py | 214 |
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': |