| 
									
										
										
										
											2000-02-04 15:39:30 +00:00
										 |  |  | """An FTP client class and some helper functions.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-09-01 06:09:23 +00:00
										 |  |  | Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | >>> from ftplib import FTP | 
					
						
							|  |  |  | >>> ftp = FTP('ftp.python.org') # connect to host, default port | 
					
						
							| 
									
										
										
										
											2001-12-28 20:54:55 +00:00
										 |  |  | >>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@ | 
					
						
							| 
									
										
										
										
											1997-10-07 14:49:56 +00:00
										 |  |  | '230 Guest login ok, access restrictions apply.' | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | >>> ftp.retrlines('LIST') # list directory contents | 
					
						
							|  |  |  | total 9 | 
					
						
							|  |  |  | drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 . | 
					
						
							|  |  |  | drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .. | 
					
						
							|  |  |  | drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin | 
					
						
							|  |  |  | drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc | 
					
						
							|  |  |  | d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming | 
					
						
							|  |  |  | drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib | 
					
						
							|  |  |  | drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub | 
					
						
							|  |  |  | drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr | 
					
						
							|  |  |  | -rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg | 
					
						
							| 
									
										
										
										
											1997-10-07 14:49:56 +00:00
										 |  |  | '226 Transfer complete.' | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | >>> ftp.quit() | 
					
						
							| 
									
										
										
										
											1997-10-07 14:49:56 +00:00
										 |  |  | '221 Goodbye.' | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | >>> | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | A nice test that reveals some of the network dialogue would be: | 
					
						
							|  |  |  | python ftplib.py -d localhost -l -p -l | 
					
						
							| 
									
										
										
										
											2000-02-04 15:39:30 +00:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											1992-11-05 22:22:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2000-02-28 15:12:25 +00:00
										 |  |  | # Changes and improvements suggested by Steve Majewski. | 
					
						
							|  |  |  | # Modified by Jack to work on the mac. | 
					
						
							|  |  |  | # Modified by Siebren to support docstrings and PASV. | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											1992-11-05 22:22:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | # Import SOCKS module if it exists, else standard socket module socket | 
					
						
							|  |  |  | try: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket | 
					
						
							|  |  |  |     from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | except ImportError: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     import socket | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-20 23:34:12 +00:00
										 |  |  | __all__ = ["FTP","Netrc"] | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-05-24 14:16:22 +00:00
										 |  |  | # Magic number from <socket.h> | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | MSG_OOB = 0x1                           # Process data out of band | 
					
						
							| 
									
										
										
										
											1993-05-24 14:16:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-05 22:22:37 +00:00
										 |  |  | # The standard FTP server control port | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | FTP_PORT = 21 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-06 13:34:17 +00:00
										 |  |  | # Exception raised when an error or invalid response is received | 
					
						
							| 
									
										
										
										
											2000-08-17 05:06:49 +00:00
										 |  |  | class Error(Exception): pass | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | class error_reply(Error): pass          # unexpected [123]xx reply | 
					
						
							|  |  |  | class error_temp(Error): pass           # 4xx errors | 
					
						
							|  |  |  | class error_perm(Error): pass           # 5xx errors | 
					
						
							|  |  |  | class error_proto(Error): pass          # response does not begin with [1-5] | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-06 13:34:17 +00:00
										 |  |  | # All exceptions (hopefully) that may be raised here and that aren't | 
					
						
							|  |  |  | # (always) programming errors on our side | 
					
						
							| 
									
										
										
										
											2000-08-17 05:06:49 +00:00
										 |  |  | all_errors = (Error, socket.error, IOError, EOFError) | 
					
						
							| 
									
										
										
										
											1992-11-06 13:34:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) | 
					
						
							|  |  |  | CRLF = '\r\n' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # The class itself | 
					
						
							|  |  |  | class FTP: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''An FTP client class.
 | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     To create a connection, call the class using these argument: | 
					
						
							|  |  |  |             host, user, passwd, acct | 
					
						
							|  |  |  |     These are all strings, and have default value ''. | 
					
						
							|  |  |  |     Then use self.connect() with optional host and port argument. | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     To download a file, use ftp.retrlines('RETR ' + filename), | 
					
						
							|  |  |  |     or ftp.retrbinary() with slightly different arguments. | 
					
						
							|  |  |  |     To upload a file, use ftp.storlines() or ftp.storbinary(), | 
					
						
							|  |  |  |     which have an open file as argument (see their definitions | 
					
						
							|  |  |  |     below for details). | 
					
						
							|  |  |  |     The download/upload functions first issue appropriate TYPE | 
					
						
							|  |  |  |     and PORT or PASV commands. | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | '''
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-28 21:46:37 +00:00
										 |  |  |     debugging = 0 | 
					
						
							|  |  |  |     host = '' | 
					
						
							|  |  |  |     port = FTP_PORT | 
					
						
							|  |  |  |     sock = None | 
					
						
							|  |  |  |     file = None | 
					
						
							|  |  |  |     welcome = None | 
					
						
							|  |  |  |     passiveserver = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     # Initialization method (called by class instantiation). | 
					
						
							|  |  |  |     # Initialize host to localhost, port to standard ftp port | 
					
						
							|  |  |  |     # Optional arguments are host (for connect()), | 
					
						
							|  |  |  |     # and user, passwd, acct (for login()) | 
					
						
							| 
									
										
										
										
											2001-02-28 21:46:37 +00:00
										 |  |  |     def __init__(self, host='', user='', passwd='', acct=''): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         if host: | 
					
						
							| 
									
										
										
										
											2001-02-28 21:46:37 +00:00
										 |  |  |             self.connect(host) | 
					
						
							|  |  |  |             if user: self.login(user, passwd, acct) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     def connect(self, host = '', port = 0): | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         '''Connect to host.  Arguments are:
 | 
					
						
							|  |  |  |         - host: hostname to connect to (string, default previous host) | 
					
						
							|  |  |  |         - port: port to connect to (integer, default previous port)'''
 | 
					
						
							|  |  |  |         if host: self.host = host | 
					
						
							|  |  |  |         if port: self.port = port | 
					
						
							| 
									
										
										
										
											2001-07-31 08:40:21 +00:00
										 |  |  |         msg = "getaddrinfo returns an empty list" | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): | 
					
						
							|  |  |  |             af, socktype, proto, canonname, sa = res | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.sock = socket.socket(af, socktype, proto) | 
					
						
							|  |  |  |                 self.sock.connect(sa) | 
					
						
							|  |  |  |             except socket.error, msg: | 
					
						
							| 
									
										
										
										
											2001-10-07 08:53:32 +00:00
										 |  |  |                 if self.sock: | 
					
						
							|  |  |  |                     self.sock.close() | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |                 self.sock = None | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         if not self.sock: | 
					
						
							|  |  |  |             raise socket.error, msg | 
					
						
							|  |  |  |         self.af = af | 
					
						
							|  |  |  |         self.file = self.sock.makefile('rb') | 
					
						
							|  |  |  |         self.welcome = self.getresp() | 
					
						
							|  |  |  |         return self.welcome | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getwelcome(self): | 
					
						
							|  |  |  |         '''Get the welcome message from the server.
 | 
					
						
							|  |  |  |         (this is read and squirreled away by connect())'''
 | 
					
						
							|  |  |  |         if self.debugging: | 
					
						
							|  |  |  |             print '*welcome*', self.sanitize(self.welcome) | 
					
						
							|  |  |  |         return self.welcome | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_debuglevel(self, level): | 
					
						
							|  |  |  |         '''Set the debugging level.
 | 
					
						
							|  |  |  |         The required 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'''
 | 
					
						
							|  |  |  |         self.debugging = level | 
					
						
							|  |  |  |     debug = set_debuglevel | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_pasv(self, val): | 
					
						
							|  |  |  |         '''Use passive or active mode for data transfers.
 | 
					
						
							|  |  |  |         With a false argument, use the normal PORT mode, | 
					
						
							|  |  |  |         With a true argument, use the PASV command.'''
 | 
					
						
							|  |  |  |         self.passiveserver = val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Internal: "sanitize" a string for printing | 
					
						
							|  |  |  |     def sanitize(self, s): | 
					
						
							|  |  |  |         if s[:5] == 'pass ' or s[:5] == 'PASS ': | 
					
						
							|  |  |  |             i = len(s) | 
					
						
							|  |  |  |             while i > 5 and s[i-1] in '\r\n': | 
					
						
							|  |  |  |                 i = i-1 | 
					
						
							|  |  |  |             s = s[:5] + '*'*(i-5) + s[i:] | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |         return repr(s) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Internal: send one line to the server, appending CRLF | 
					
						
							|  |  |  |     def putline(self, line): | 
					
						
							|  |  |  |         line = line + CRLF | 
					
						
							|  |  |  |         if self.debugging > 1: print '*put*', self.sanitize(line) | 
					
						
							| 
									
										
										
										
											2002-02-16 23:06:19 +00:00
										 |  |  |         self.sock.sendall(line) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Internal: send one command to the server (through putline()) | 
					
						
							|  |  |  |     def putcmd(self, line): | 
					
						
							|  |  |  |         if self.debugging: print '*cmd*', self.sanitize(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*', self.sanitize(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*', self.sanitize(resp) | 
					
						
							|  |  |  |         self.lastresp = resp[:3] | 
					
						
							|  |  |  |         c = resp[:1] | 
					
						
							|  |  |  |         if c == '4': | 
					
						
							|  |  |  |             raise error_temp, resp | 
					
						
							|  |  |  |         if c == '5': | 
					
						
							|  |  |  |             raise error_perm, resp | 
					
						
							|  |  |  |         if c not in '123': | 
					
						
							|  |  |  |             raise error_proto, resp | 
					
						
							|  |  |  |         return resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def voidresp(self): | 
					
						
							|  |  |  |         """Expect a response beginning with '2'.""" | 
					
						
							|  |  |  |         resp = self.getresp() | 
					
						
							|  |  |  |         if resp[0] != '2': | 
					
						
							|  |  |  |             raise error_reply, resp | 
					
						
							|  |  |  |         return resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def abort(self): | 
					
						
							|  |  |  |         '''Abort a file transfer.  Uses out-of-band data.
 | 
					
						
							|  |  |  |         This does not follow the procedure from the RFC to send Telnet | 
					
						
							|  |  |  |         IP and Synch; that doesn't seem to work with the servers I've | 
					
						
							|  |  |  |         tried.  Instead, just send the ABOR command as OOB data.'''
 | 
					
						
							|  |  |  |         line = 'ABOR' + CRLF | 
					
						
							|  |  |  |         if self.debugging > 1: print '*put urgent*', self.sanitize(line) | 
					
						
							| 
									
										
										
										
											2002-02-16 23:06:19 +00:00
										 |  |  |         self.sock.sendall(line, MSG_OOB) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         resp = self.getmultiline() | 
					
						
							|  |  |  |         if resp[:3] not in ('426', '226'): | 
					
						
							|  |  |  |             raise error_proto, resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def sendcmd(self, cmd): | 
					
						
							|  |  |  |         '''Send a command and return the response.''' | 
					
						
							|  |  |  |         self.putcmd(cmd) | 
					
						
							|  |  |  |         return self.getresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def voidcmd(self, cmd): | 
					
						
							|  |  |  |         """Send a command and expect a response beginning with '2'.""" | 
					
						
							|  |  |  |         self.putcmd(cmd) | 
					
						
							|  |  |  |         return self.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def sendport(self, host, port): | 
					
						
							|  |  |  |         '''Send a PORT command with the current host and the given
 | 
					
						
							|  |  |  |         port number. | 
					
						
							|  |  |  |         '''
 | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |         hbytes = host.split('.') | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |         pbytes = [repr(port/256), repr(port%256)] | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         bytes = hbytes + pbytes | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |         cmd = 'PORT ' + ','.join(bytes) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         return self.voidcmd(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     def sendeprt(self, host, port): | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         '''Send a EPRT command with the current host and the given port number.''' | 
					
						
							|  |  |  |         af = 0 | 
					
						
							|  |  |  |         if self.af == socket.AF_INET: | 
					
						
							|  |  |  |             af = 1 | 
					
						
							|  |  |  |         if self.af == socket.AF_INET6: | 
					
						
							|  |  |  |             af = 2 | 
					
						
							|  |  |  |         if af == 0: | 
					
						
							|  |  |  |             raise error_proto, 'unsupported address family' | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |         fields = ['', repr(af), host, repr(port), ''] | 
					
						
							| 
									
										
										
										
											2002-05-31 14:13:04 +00:00
										 |  |  |         cmd = 'EPRT ' + '|'.join(fields) | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         return self.voidcmd(cmd) | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     def makeport(self): | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         '''Create a new socket and send a PORT command for it.''' | 
					
						
							| 
									
										
										
										
											2001-07-31 08:40:21 +00:00
										 |  |  |         msg = "getaddrinfo returns an empty list" | 
					
						
							| 
									
										
										
										
											2001-10-07 08:53:32 +00:00
										 |  |  |         sock = None | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): | 
					
						
							|  |  |  |             af, socktype, proto, canonname, sa = res | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 sock = socket.socket(af, socktype, proto) | 
					
						
							|  |  |  |                 sock.bind(sa) | 
					
						
							|  |  |  |             except socket.error, msg: | 
					
						
							| 
									
										
										
										
											2001-10-07 08:53:32 +00:00
										 |  |  |                 if sock: | 
					
						
							|  |  |  |                     sock.close() | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |                 sock = None | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |         if not sock: | 
					
						
							|  |  |  |             raise socket.error, msg | 
					
						
							|  |  |  |         sock.listen(1) | 
					
						
							|  |  |  |         port = sock.getsockname()[1] # Get proper port | 
					
						
							|  |  |  |         host = self.sock.getsockname()[0] # Get proper host | 
					
						
							|  |  |  |         if self.af == socket.AF_INET: | 
					
						
							|  |  |  |             resp = self.sendport(host, port) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             resp = self.sendeprt(host, port) | 
					
						
							|  |  |  |         return sock | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def makepasv(self): | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         if self.af == socket.AF_INET: | 
					
						
							|  |  |  |             host, port = parse227(self.sendcmd('PASV')) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) | 
					
						
							|  |  |  |         return host, port | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def ntransfercmd(self, cmd, rest=None): | 
					
						
							|  |  |  |         """Initiate a transfer over the data connection.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the transfer is active, send a port command and the | 
					
						
							|  |  |  |         transfer command, and accept the connection.  If the server is | 
					
						
							|  |  |  |         passive, send a pasv command, connect to it, and start the | 
					
						
							|  |  |  |         transfer command.  Either way, return the socket for the | 
					
						
							|  |  |  |         connection and the expected size of the transfer.  The | 
					
						
							|  |  |  |         expected size may be None if it could not be determined. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Optional `rest' argument can be a string that is sent as the | 
					
						
							|  |  |  |         argument to a RESTART command.  This is essentially a server | 
					
						
							|  |  |  |         marker used to tell the server to skip over any data up to the | 
					
						
							|  |  |  |         given marker. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         size = None | 
					
						
							|  |  |  |         if self.passiveserver: | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |             host, port = self.makepasv() | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |             af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] | 
					
						
							|  |  |  |             conn = socket.socket(af, socktype, proto) | 
					
						
							|  |  |  |             conn.connect(sa) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |             if rest is not None: | 
					
						
							|  |  |  |                 self.sendcmd("REST %s" % rest) | 
					
						
							|  |  |  |             resp = self.sendcmd(cmd) | 
					
						
							|  |  |  |             if resp[0] != '1': | 
					
						
							|  |  |  |                 raise error_reply, resp | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             sock = self.makeport() | 
					
						
							|  |  |  |             if rest is not None: | 
					
						
							|  |  |  |                 self.sendcmd("REST %s" % rest) | 
					
						
							|  |  |  |             resp = self.sendcmd(cmd) | 
					
						
							|  |  |  |             if resp[0] != '1': | 
					
						
							|  |  |  |                 raise error_reply, resp | 
					
						
							|  |  |  |             conn, sockaddr = sock.accept() | 
					
						
							|  |  |  |         if resp[:3] == '150': | 
					
						
							|  |  |  |             # this is conditional in case we received a 125 | 
					
						
							|  |  |  |             size = parse150(resp) | 
					
						
							|  |  |  |         return conn, size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def transfercmd(self, cmd, rest=None): | 
					
						
							| 
									
										
										
										
											2001-10-16 19:45:52 +00:00
										 |  |  |         """Like ntransfercmd() but returns only the socket.""" | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         return self.ntransfercmd(cmd, rest)[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def login(self, user = '', passwd = '', acct = ''): | 
					
						
							|  |  |  |         '''Login, default anonymous.''' | 
					
						
							|  |  |  |         if not user: user = 'anonymous' | 
					
						
							|  |  |  |         if not passwd: passwd = '' | 
					
						
							|  |  |  |         if not acct: acct = '' | 
					
						
							|  |  |  |         if user == 'anonymous' and passwd in ('', '-'): | 
					
						
							| 
									
										
										
										
											2002-02-16 07:34:19 +00:00
										 |  |  |             # If there is no anonymous ftp password specified | 
					
						
							|  |  |  |             # then we'll just use anonymous@ | 
					
						
							|  |  |  |             # We don't send any other thing because: | 
					
						
							|  |  |  |             # - We want to remain anonymous | 
					
						
							|  |  |  |             # - We want to stop SPAM | 
					
						
							|  |  |  |             # - We don't want to let ftp sites to discriminate by the user, | 
					
						
							|  |  |  |             #   host or country. | 
					
						
							| 
									
										
										
										
											2001-12-28 20:54:28 +00:00
										 |  |  |             passwd = passwd + 'anonymous@' | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         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 | 
					
						
							|  |  |  |         return resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def retrbinary(self, cmd, callback, blocksize=8192, rest=None): | 
					
						
							|  |  |  |         """Retrieve data in binary mode.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         `cmd' is a RETR command.  `callback' is a callback function is | 
					
						
							|  |  |  |         called for each block.  No more than `blocksize' number of | 
					
						
							|  |  |  |         bytes will be read from the socket.  Optional `rest' is passed | 
					
						
							|  |  |  |         to transfercmd(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         A new port is created for you.  Return the response code. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.voidcmd('TYPE I') | 
					
						
							|  |  |  |         conn = self.transfercmd(cmd, rest) | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             data = conn.recv(blocksize) | 
					
						
							|  |  |  |             if not data: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             callback(data) | 
					
						
							|  |  |  |         conn.close() | 
					
						
							|  |  |  |         return self.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def retrlines(self, cmd, callback = None): | 
					
						
							|  |  |  |         '''Retrieve data in line mode.
 | 
					
						
							|  |  |  |         The argument is a RETR or LIST command. | 
					
						
							|  |  |  |         The callback function (2nd argument) is called for each line, | 
					
						
							|  |  |  |         with trailing CRLF stripped.  This creates a new port for you. | 
					
						
							|  |  |  |         print_line() is the default callback.'''
 | 
					
						
							| 
									
										
										
										
											2002-05-12 05:53:51 +00:00
										 |  |  |         if callback is None: callback = print_line | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         resp = self.sendcmd('TYPE A') | 
					
						
							|  |  |  |         conn = self.transfercmd(cmd) | 
					
						
							|  |  |  |         fp = conn.makefile('rb') | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             line = fp.readline() | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             if self.debugging > 2: print '*retr*', repr(line) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |             if not line: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             if line[-2:] == CRLF: | 
					
						
							|  |  |  |                 line = line[:-2] | 
					
						
							|  |  |  |             elif line[-1:] == '\n': | 
					
						
							|  |  |  |                 line = line[:-1] | 
					
						
							|  |  |  |             callback(line) | 
					
						
							|  |  |  |         fp.close() | 
					
						
							|  |  |  |         conn.close() | 
					
						
							|  |  |  |         return self.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-15 13:50:36 +00:00
										 |  |  |     def storbinary(self, cmd, fp, blocksize=8192): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         '''Store a file in binary mode.''' | 
					
						
							|  |  |  |         self.voidcmd('TYPE I') | 
					
						
							|  |  |  |         conn = self.transfercmd(cmd) | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             buf = fp.read(blocksize) | 
					
						
							|  |  |  |             if not buf: break | 
					
						
							| 
									
										
										
										
											2002-02-16 23:06:19 +00:00
										 |  |  |             conn.sendall(buf) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         conn.close() | 
					
						
							|  |  |  |         return self.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def storlines(self, cmd, fp): | 
					
						
							|  |  |  |         '''Store a file in line mode.''' | 
					
						
							|  |  |  |         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 | 
					
						
							| 
									
										
										
										
											2002-02-16 23:06:19 +00:00
										 |  |  |             conn.sendall(buf) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         conn.close() | 
					
						
							|  |  |  |         return self.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def acct(self, password): | 
					
						
							|  |  |  |         '''Send new account name.''' | 
					
						
							|  |  |  |         cmd = 'ACCT ' + password | 
					
						
							|  |  |  |         return self.voidcmd(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def nlst(self, *args): | 
					
						
							|  |  |  |         '''Return a list of files in a given directory (default the current).''' | 
					
						
							|  |  |  |         cmd = 'NLST' | 
					
						
							|  |  |  |         for arg in args: | 
					
						
							|  |  |  |             cmd = cmd + (' ' + arg) | 
					
						
							|  |  |  |         files = [] | 
					
						
							|  |  |  |         self.retrlines(cmd, files.append) | 
					
						
							|  |  |  |         return files | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def dir(self, *args): | 
					
						
							|  |  |  |         '''List a directory in long form.
 | 
					
						
							|  |  |  |         By default list current directory to stdout. | 
					
						
							|  |  |  |         Optional last argument is callback function; all | 
					
						
							|  |  |  |         non-empty arguments before it are concatenated to the | 
					
						
							|  |  |  |         LIST command.  (This *should* only be used for a pathname.)'''
 | 
					
						
							|  |  |  |         cmd = 'LIST' | 
					
						
							|  |  |  |         func = None | 
					
						
							|  |  |  |         if args[-1:] and type(args[-1]) != type(''): | 
					
						
							|  |  |  |             args, func = args[:-1], args[-1] | 
					
						
							|  |  |  |         for arg in args: | 
					
						
							|  |  |  |             if arg: | 
					
						
							|  |  |  |                 cmd = cmd + (' ' + arg) | 
					
						
							|  |  |  |         self.retrlines(cmd, func) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def rename(self, fromname, toname): | 
					
						
							|  |  |  |         '''Rename a file.''' | 
					
						
							|  |  |  |         resp = self.sendcmd('RNFR ' + fromname) | 
					
						
							|  |  |  |         if resp[0] != '3': | 
					
						
							|  |  |  |             raise error_reply, resp | 
					
						
							|  |  |  |         return self.voidcmd('RNTO ' + toname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def delete(self, filename): | 
					
						
							|  |  |  |         '''Delete a file.''' | 
					
						
							|  |  |  |         resp = self.sendcmd('DELE ' + filename) | 
					
						
							|  |  |  |         if resp[:3] in ('250', '200'): | 
					
						
							|  |  |  |             return resp | 
					
						
							|  |  |  |         elif resp[:1] == '5': | 
					
						
							|  |  |  |             raise error_perm, resp | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise error_reply, resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def cwd(self, dirname): | 
					
						
							|  |  |  |         '''Change to a directory.''' | 
					
						
							|  |  |  |         if dirname == '..': | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return self.voidcmd('CDUP') | 
					
						
							|  |  |  |             except error_perm, msg: | 
					
						
							| 
									
										
										
										
											2002-03-10 15:59:58 +00:00
										 |  |  |                 if msg.args[0][:3] != '500': | 
					
						
							|  |  |  |                     raise | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         elif dirname == '': | 
					
						
							|  |  |  |             dirname = '.'  # does nothing, but could return error | 
					
						
							|  |  |  |         cmd = 'CWD ' + dirname | 
					
						
							|  |  |  |         return self.voidcmd(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def size(self, filename): | 
					
						
							|  |  |  |         '''Retrieve the size of a file.''' | 
					
						
							|  |  |  |         # Note that the RFC doesn't say anything about 'SIZE' | 
					
						
							|  |  |  |         resp = self.sendcmd('SIZE ' + filename) | 
					
						
							|  |  |  |         if resp[:3] == '213': | 
					
						
							| 
									
										
										
										
											2001-10-16 19:45:52 +00:00
										 |  |  |             s = resp[3:].strip() | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return int(s) | 
					
						
							| 
									
										
										
										
											2001-10-17 17:21:47 +00:00
										 |  |  |             except (OverflowError, ValueError): | 
					
						
							| 
									
										
										
										
											2001-10-16 19:45:52 +00:00
										 |  |  |                 return long(s) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def mkd(self, dirname): | 
					
						
							|  |  |  |         '''Make a directory, return its full pathname.''' | 
					
						
							|  |  |  |         resp = self.sendcmd('MKD ' + dirname) | 
					
						
							|  |  |  |         return parse257(resp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def rmd(self, dirname): | 
					
						
							|  |  |  |         '''Remove a directory.''' | 
					
						
							|  |  |  |         return self.voidcmd('RMD ' + dirname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pwd(self): | 
					
						
							|  |  |  |         '''Return current working directory.''' | 
					
						
							|  |  |  |         resp = self.sendcmd('PWD') | 
					
						
							|  |  |  |         return parse257(resp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def quit(self): | 
					
						
							|  |  |  |         '''Quit, and close the connection.''' | 
					
						
							|  |  |  |         resp = self.voidcmd('QUIT') | 
					
						
							|  |  |  |         self.close() | 
					
						
							|  |  |  |         return resp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         '''Close the connection without assuming anything about it.''' | 
					
						
							| 
									
										
										
										
											2001-02-28 21:46:37 +00:00
										 |  |  |         if self.file: | 
					
						
							|  |  |  |             self.file.close() | 
					
						
							|  |  |  |             self.sock.close() | 
					
						
							|  |  |  |             self.file = self.sock = None | 
					
						
							| 
									
										
										
										
											1992-11-05 22:22:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-10-22 20:49:52 +00:00
										 |  |  | _150_re = None | 
					
						
							| 
									
										
										
										
											1997-01-10 18:26:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def parse150(resp): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Parse the '150' response for a RETR request.
 | 
					
						
							|  |  |  |     Returns the expected transfer size or None; size is not guaranteed to | 
					
						
							|  |  |  |     be present in the 150 message. | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     if resp[:3] != '150': | 
					
						
							|  |  |  |         raise error_reply, resp | 
					
						
							|  |  |  |     global _150_re | 
					
						
							|  |  |  |     if _150_re is None: | 
					
						
							|  |  |  |         import re | 
					
						
							|  |  |  |         _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE) | 
					
						
							|  |  |  |     m = _150_re.match(resp) | 
					
						
							| 
									
										
										
										
											2001-10-16 19:45:52 +00:00
										 |  |  |     if not m: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     s = m.group(1) | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         return int(s) | 
					
						
							| 
									
										
										
										
											2001-10-17 17:21:47 +00:00
										 |  |  |     except (OverflowError, ValueError): | 
					
						
							| 
									
										
										
										
											2001-10-16 19:45:52 +00:00
										 |  |  |         return long(s) | 
					
						
							| 
									
										
										
										
											1997-01-10 18:26:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-17 17:24:29 +00:00
										 |  |  | _227_re = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | def parse227(resp): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Parse the '227' response for a PASV request.
 | 
					
						
							|  |  |  |     Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' | 
					
						
							|  |  |  |     Return ('host.addr.as.numbers', port#) tuple.''' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if resp[:3] != '227': | 
					
						
							|  |  |  |         raise error_reply, resp | 
					
						
							| 
									
										
										
										
											2001-08-17 17:24:29 +00:00
										 |  |  |     global _227_re | 
					
						
							|  |  |  |     if _227_re is None: | 
					
						
							|  |  |  |         import re | 
					
						
							|  |  |  |         _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)') | 
					
						
							|  |  |  |     m = _227_re.search(resp) | 
					
						
							|  |  |  |     if not m: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         raise error_proto, resp | 
					
						
							| 
									
										
										
										
											2001-08-17 17:24:29 +00:00
										 |  |  |     numbers = m.groups() | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |     host = '.'.join(numbers[:4]) | 
					
						
							|  |  |  |     port = (int(numbers[4]) << 8) + int(numbers[5]) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     return host, port | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  | def parse229(resp, peer): | 
					
						
							|  |  |  |     '''Parse the '229' response for a EPSV request.
 | 
					
						
							|  |  |  |     Raises error_proto if it does not contain '(|||port|)' | 
					
						
							|  |  |  |     Return ('host.addr.as.numbers', port#) tuple.''' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if resp[:3] <> '229': | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         raise error_reply, resp | 
					
						
							| 
									
										
										
										
											2002-05-31 14:13:04 +00:00
										 |  |  |     left = resp.find('(') | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     if left < 0: raise error_proto, resp | 
					
						
							| 
									
										
										
										
											2002-05-31 14:13:04 +00:00
										 |  |  |     right = resp.find(')', left + 1) | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     if right < 0: | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         raise error_proto, resp # should contain '(|||port|)' | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     if resp[left + 1] <> resp[right - 1]: | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         raise error_proto, resp | 
					
						
							| 
									
										
										
										
											2002-06-03 10:41:45 +00:00
										 |  |  |     parts = resp[left + 1:right].split(resp[left+1]) | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     if len(parts) <> 5: | 
					
						
							| 
									
										
										
										
											2001-07-26 13:37:33 +00:00
										 |  |  |         raise error_proto, resp | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     host = peer[0] | 
					
						
							| 
									
										
										
										
											2002-05-31 14:13:04 +00:00
										 |  |  |     port = int(parts[3]) | 
					
						
							| 
									
										
										
										
											2001-07-24 20:34:08 +00:00
										 |  |  |     return host, port | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-05 22:22:37 +00:00
										 |  |  | def parse257(resp): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Parse the '257' response for a MKD or PWD request.
 | 
					
						
							|  |  |  |     This is a response to a MKD or PWD request: a directory name. | 
					
						
							|  |  |  |     Returns the directoryname in the 257 reply.'''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-10-07 14:49:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-11-30 13:43:54 +00:00
										 |  |  | def print_line(line): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Default retrlines callback to print a line.''' | 
					
						
							|  |  |  |     print line | 
					
						
							| 
									
										
										
										
											1993-11-30 13:43:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-10-07 14:49:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1996-05-28 23:41:25 +00:00
										 |  |  | def ftpcp(source, sourcename, target, targetname = '', type = 'I'): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Copy file from one FTP-instance to another.''' | 
					
						
							|  |  |  |     if not targetname: targetname = sourcename | 
					
						
							|  |  |  |     type = 'TYPE ' + type | 
					
						
							|  |  |  |     source.voidcmd(type) | 
					
						
							|  |  |  |     target.voidcmd(type) | 
					
						
							|  |  |  |     sourcehost, sourceport = parse227(source.sendcmd('PASV')) | 
					
						
							|  |  |  |     target.sendport(sourcehost, sourceport) | 
					
						
							|  |  |  |     # RFC 959: the user must "listen" [...] BEFORE sending the | 
					
						
							|  |  |  |     # transfer request. | 
					
						
							|  |  |  |     # So: STOR before RETR, because here the target is a "user". | 
					
						
							|  |  |  |     treply = target.sendcmd('STOR ' + targetname) | 
					
						
							|  |  |  |     if treply[:3] not in ('125', '150'): raise error_proto  # RFC 959 | 
					
						
							|  |  |  |     sreply = source.sendcmd('RETR ' + sourcename) | 
					
						
							|  |  |  |     if sreply[:3] not in ('125', '150'): raise error_proto  # RFC 959 | 
					
						
							|  |  |  |     source.voidresp() | 
					
						
							|  |  |  |     target.voidresp() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-06-24 22:02:54 +00:00
										 |  |  | class Netrc: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     """Class to parse & provide access to 'netrc' format files.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     See the netrc(4) man page for information on the file format. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     WARNING: This class is obsolete -- use module netrc instead. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     __defuser = None | 
					
						
							|  |  |  |     __defpasswd = None | 
					
						
							|  |  |  |     __defacct = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, filename=None): | 
					
						
							| 
									
										
										
										
											2002-06-01 01:29:16 +00:00
										 |  |  |         if filename is None: | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |             if "HOME" in os.environ: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |                 filename = os.path.join(os.environ["HOME"], | 
					
						
							|  |  |  |                                         ".netrc") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise IOError, \ | 
					
						
							|  |  |  |                       "specify file to load or set $HOME" | 
					
						
							|  |  |  |         self.__hosts = {} | 
					
						
							|  |  |  |         self.__macros = {} | 
					
						
							|  |  |  |         fp = open(filename, "r") | 
					
						
							|  |  |  |         in_macro = 0 | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             line = fp.readline() | 
					
						
							|  |  |  |             if not line: break | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |             if in_macro and line.strip(): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |                 macro_lines.append(line) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             elif in_macro: | 
					
						
							|  |  |  |                 self.__macros[macro_name] = tuple(macro_lines) | 
					
						
							|  |  |  |                 in_macro = 0 | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |             words = line.split() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |             host = user = passwd = acct = None | 
					
						
							|  |  |  |             default = 0 | 
					
						
							|  |  |  |             i = 0 | 
					
						
							|  |  |  |             while i < len(words): | 
					
						
							|  |  |  |                 w1 = words[i] | 
					
						
							|  |  |  |                 if i+1 < len(words): | 
					
						
							|  |  |  |                     w2 = words[i + 1] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     w2 = None | 
					
						
							|  |  |  |                 if w1 == 'default': | 
					
						
							|  |  |  |                     default = 1 | 
					
						
							|  |  |  |                 elif w1 == 'machine' and w2: | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |                     host = w2.lower() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |                     i = i + 1 | 
					
						
							|  |  |  |                 elif w1 == 'login' and w2: | 
					
						
							|  |  |  |                     user = w2 | 
					
						
							|  |  |  |                     i = i + 1 | 
					
						
							|  |  |  |                 elif w1 == 'password' and w2: | 
					
						
							|  |  |  |                     passwd = w2 | 
					
						
							|  |  |  |                     i = i + 1 | 
					
						
							|  |  |  |                 elif w1 == 'account' and w2: | 
					
						
							|  |  |  |                     acct = w2 | 
					
						
							|  |  |  |                     i = i + 1 | 
					
						
							|  |  |  |                 elif w1 == 'macdef' and w2: | 
					
						
							|  |  |  |                     macro_name = w2 | 
					
						
							|  |  |  |                     macro_lines = [] | 
					
						
							|  |  |  |                     in_macro = 1 | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |                 i = i + 1 | 
					
						
							|  |  |  |             if default: | 
					
						
							|  |  |  |                 self.__defuser = user or self.__defuser | 
					
						
							|  |  |  |                 self.__defpasswd = passwd or self.__defpasswd | 
					
						
							|  |  |  |                 self.__defacct = acct or self.__defacct | 
					
						
							|  |  |  |             if host: | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |                 if host in self.__hosts: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |                     ouser, opasswd, oacct = \ | 
					
						
							|  |  |  |                            self.__hosts[host] | 
					
						
							|  |  |  |                     user = user or ouser | 
					
						
							|  |  |  |                     passwd = passwd or opasswd | 
					
						
							|  |  |  |                     acct = acct or oacct | 
					
						
							|  |  |  |                 self.__hosts[host] = user, passwd, acct | 
					
						
							|  |  |  |         fp.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_hosts(self): | 
					
						
							|  |  |  |         """Return a list of hosts mentioned in the .netrc file.""" | 
					
						
							|  |  |  |         return self.__hosts.keys() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_account(self, host): | 
					
						
							|  |  |  |         """Returns login information for the named host.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The return value is a triple containing userid, | 
					
						
							|  |  |  |         password, and the accounting field. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-02-09 10:06:47 +00:00
										 |  |  |         host = host.lower() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |         user = passwd = acct = None | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if host in self.__hosts: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |             user, passwd, acct = self.__hosts[host] | 
					
						
							|  |  |  |         user = user or self.__defuser | 
					
						
							|  |  |  |         passwd = passwd or self.__defpasswd | 
					
						
							|  |  |  |         acct = acct or self.__defacct | 
					
						
							|  |  |  |         return user, passwd, acct | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_macros(self): | 
					
						
							|  |  |  |         """Return a list of all defined macro names.""" | 
					
						
							|  |  |  |         return self.__macros.keys() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_macro(self, macro): | 
					
						
							|  |  |  |         """Return a sequence of lines which define a named macro.""" | 
					
						
							|  |  |  |         return self.__macros[macro] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1992-11-04 15:51:30 +00:00
										 |  |  | def test(): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     '''Test program.
 | 
					
						
							|  |  |  |     Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     debugging = 0 | 
					
						
							|  |  |  |     rcfile = None | 
					
						
							|  |  |  |     while sys.argv[1] == '-d': | 
					
						
							|  |  |  |         debugging = debugging+1 | 
					
						
							|  |  |  |         del sys.argv[1] | 
					
						
							|  |  |  |     if sys.argv[1][:2] == '-r': | 
					
						
							|  |  |  |         # get name of alternate ~/.netrc file: | 
					
						
							|  |  |  |         rcfile = sys.argv[1][2:] | 
					
						
							|  |  |  |         del sys.argv[1] | 
					
						
							|  |  |  |     host = sys.argv[1] | 
					
						
							|  |  |  |     ftp = FTP(host) | 
					
						
							|  |  |  |     ftp.set_debuglevel(debugging) | 
					
						
							|  |  |  |     userid = passwd = acct = '' | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         netrc = Netrc(rcfile) | 
					
						
							|  |  |  |     except IOError: | 
					
						
							|  |  |  |         if rcfile is not None: | 
					
						
							|  |  |  |             sys.stderr.write("Could not open account file" | 
					
						
							|  |  |  |                              " -- using anonymous login.") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             userid, passwd, acct = netrc.get_account(host) | 
					
						
							|  |  |  |         except KeyError: | 
					
						
							|  |  |  |             # no account for host | 
					
						
							|  |  |  |             sys.stderr.write( | 
					
						
							|  |  |  |                     "No account -- using anonymous login.") | 
					
						
							|  |  |  |     ftp.login(userid, passwd, acct) | 
					
						
							|  |  |  |     for file in sys.argv[2:]: | 
					
						
							|  |  |  |         if file[:2] == '-l': | 
					
						
							|  |  |  |             ftp.dir(file[2:]) | 
					
						
							|  |  |  |         elif file[:2] == '-d': | 
					
						
							|  |  |  |             cmd = 'CWD' | 
					
						
							|  |  |  |             if file[2:]: cmd = cmd + ' ' + file[2:] | 
					
						
							|  |  |  |             resp = ftp.sendcmd(cmd) | 
					
						
							|  |  |  |         elif file == '-p': | 
					
						
							|  |  |  |             ftp.set_pasv(not ftp.passiveserver) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             ftp.retrbinary('RETR ' + file, \ | 
					
						
							|  |  |  |                            sys.stdout.write, 1024) | 
					
						
							|  |  |  |     ftp.quit() | 
					
						
							| 
									
										
										
										
											1995-08-04 04:39:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2001-01-14 23:36:06 +00:00
										 |  |  |     test() |