]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Initial revision
authorGuido van Rossum <guido@python.org>
Wed, 4 Nov 1992 15:51:30 +0000 (15:51 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 4 Nov 1992 15:51:30 +0000 (15:51 +0000)
Lib/ftplib.py [new file with mode: 0644]

diff --git a/Lib/ftplib.py b/Lib/ftplib.py
new file mode 100644 (file)
index 0000000..b360942
--- /dev/null
@@ -0,0 +1,258 @@
+# An FTP client class.  Based on RFC 959: File Transfer Protocol
+# (FTP), by J. Postel and J. Reynolds
+
+
+import os
+import sys
+import socket
+import string
+
+
+# Default port numbers used by the FTP protocol
+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]
+
+
+# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
+CRLF = '\r\n'
+
+
+# Next port to be used by makeport(), with PORT_OFFSET added
+nextport = 0
+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...
+
+
+# The class itself
+class FTP:
+
+       # Initialize an instance.  Arguments:
+       # - host: hostname to connect to
+       # - port: port to connect to (default the standard FTP port)
+       def init(self, host, *args):
+               if len(args) > 1: raise TypeError, 'too many args'
+               if args: port = args[0]
+               else: port = FTP_PORT
+               self.host = host
+               self.port = port
+               self.debugging = 0
+               self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+               self.sock.connect(self.host, self.port)
+               self.file = self.sock.makefile('r')
+               self.welcome = self.getresp()
+               return self
+
+       # Get the welcome message from the server
+       # (this is read and squirreled away by init())
+       def getwelcome(self):
+               if self.debugging: print '*welcome*', `self.welcome`
+               return self.welcome
+
+       # Set the debugging level.  Argument level means:
+       # 0: no debugging output (default)
+       # 1: print commands and responses but not body text etc.
+       # 2: also print raw lines read and sent before stripping CR/LF
+       def debug(self, level):
+               self.debugging = level
+
+       # Internal: send one line to the server, appending CRLF
+       def putline(self, line):
+               line = line + CRLF
+               if self.debugging > 1: print '*put*', `line`
+               self.sock.send(line)
+
+       # Internal: send one command to the server (through putline())
+       def putcmd(self, line):
+               if self.debugging: print '*cmd*', `line`
+               self.putline(line)
+
+       # Internal: return one line from the server, stripping CRLF.
+       # Raise EOFError if the connection is closed
+       def getline(self):
+               line = self.file.readline()
+               if self.debugging > 1:
+                       print '*get*', `line`
+               if not line: raise EOFError
+               if line[-2:] == CRLF: line = line[:-2]
+               elif line[-1:] in CRLF: line = line[:-1]
+               return line
+
+       # Internal: get a response from the server, which may possibly
+       # consist of multiple lines.  Return a single string with no
+       # trailing CRLF.  If the response consists of multiple lines,
+       # these are separated by '\n' characters in the string
+       def getmultiline(self):
+               line = self.getline()
+               if line[3:4] == '-':
+                       code = line[:3]
+                       while 1:
+                               nextline = self.getline()
+                               line = line + ('\n' + nextline)
+                               if nextline[:3] == code and \
+                                       nextline[3:4] <> '-':
+                                       break
+               return line
+
+       # Internal: get a response from the server.
+       # Raise various errors if the response indicates an error
+       def getresp(self):
+               resp = self.getmultiline()
+               if self.debugging: print '*resp*', `resp`
+               self.lastresp = resp[:3]
+               c = resp[:1]
+               if c == '4':
+                       raise error_function, resp
+               if c == '5':
+                       raise error_form, resp
+               if c not in '123':
+                       raise error_protocol, resp
+               return resp
+
+       # Send a command and return the response
+       def sendcmd(self, cmd):
+               self.putcmd(cmd)
+               return self.getresp()
+
+       # Send a PORT command with the current host and the given port number
+       def sendport(self, port):
+               hostname = socket.gethostname()
+               hostaddr = socket.gethostbyname(hostname)
+               hbytes = string.splitfields(hostaddr, '.')
+               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
+
+       # Create a new socket and send a PORT command for it
+       def makeport(self):
+               global nextport
+               port = nextport + PORT_OFFSET
+               nextport = (nextport + 1) % PORT_CYCLE
+               sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+               sock.bind('', port)
+               sock.listen(0)
+               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):
+               sock = self.makeport()
+               resp = self.sendcmd(cmd)
+               if resp[0] <> '1':
+                       raise error_reply, resp
+               conn, host = sock.accept()
+               sock.close()
+               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
+
+       # Retrieve data in line mode.  (You must set the mode first.)
+       # 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()
+               fp = conn.makefile('r')
+               while 1:
+                       line = fp.readline()
+                       if not line:
+                               break
+                       if line[-2:] == CRLF:
+                               line = line[:-2]
+                       elif line[:-1] == '\n':
+                               line = line[:-1]
+                       callback(line)
+               fp.close()
+               conn.close()
+               resp = self.getresp()
+               if resp[0] <> '2':
+                       raise error_reply, resp
+
+       # 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':
+                       raise error_reply, resp
+
+       # Quit, and close the connection
+       def quit(self):
+               resp = self.sendcmd('QUIT')
+               if resp[0] <> '2':
+                       raise error_reply, resp
+               self.file.close()
+               self.sock.close()
+
+
+# Test program.
+# Usage: ftp [-d] host [-l[dir]] [-d[dir]] [file] ...
+def test():
+       import marshal
+       global nextport
+       try:
+               nextport = marshal.load(open('.@nextport', 'r'))
+       except IOError:
+               pass
+       try:
+               debugging = 0
+               while sys.argv[1] == '-d':
+                       debugging = debugging+1
+                       del sys.argv[1]
+               host = sys.argv[1]
+               ftp = FTP().init(host)
+               ftp.debug(debugging)
+               ftp.anonymouslogin()
+               def writeln(line): print line
+               for file in sys.argv[2:]:
+                       if file[:2] == '-l':
+                               cmd = 'LIST'
+                               if file[2:]: cmd = cmd + ' ' + file[2:]
+                               ftp.retrlines(cmd, writeln)
+                       elif file[:2] == '-d':
+                               cmd = 'CWD'
+                               if file[2:]: cmd = cmd + ' ' + file[2:]
+                               resp = ftp.sendcmd(cmd)
+                       else:
+                               ftp.retrbinary('RETR ' + file, \
+                                              sys.stdout.write, 1024)
+               ftp.quit()
+       finally:
+               marshal.dump(nextport, open('.@nextport', 'w'))