14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""An FTP client class and some helper functions. 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmBased on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmExample: 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> from ftplib import FTP 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp = FTP('ftp.python.org') # connect to host, default port 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@ 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'230 Guest login ok, access restrictions apply.' 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.retrlines('LIST') # list directory contents 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtotal 9 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 8 root wheel 1024 Jan 3 1994 . 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 8 root wheel 1024 Jan 3 1994 .. 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmd-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'226 Transfer complete.' 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.quit() 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'221 Goodbye.' 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmA nice test that reveals some of the network dialogue would be: 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmpython ftplib.py -d localhost -l -p -l 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm""" 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Changes and improvements suggested by Steve Majewski. 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Jack to work on the mac. 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Siebren to support docstrings and PASV. 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Phil Schwartz to add storbinary and storlines callbacks. 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Giampaolo Rodola' to add TLS support. 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Import SOCKS module if it exists, else standard socket module socket 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry: 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError: 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import socket 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom socket import _GLOBAL_DEFAULT_TIMEOUT 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["FTP","Netrc"] 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Magic number from <socket.h> 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmMSG_OOB = 0x1 # Process data out of band 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# The standard FTP server control port 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmFTP_PORT = 21 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Exception raised when an error or invalid response is received 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass Error(Exception): pass 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_reply(Error): pass # unexpected [123]xx reply 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_temp(Error): pass # 4xx errors 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_perm(Error): pass # 5xx errors 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_proto(Error): pass # response does not begin with [1-5] 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# All exceptions (hopefully) that may be raised here and that aren't 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# (always) programming errors on our side 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmall_errors = (Error, IOError, EOFError) 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCRLF = '\r\n' 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# The class itself 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass FTP: 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''An FTP client class. 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm To create a connection, call the class using these arguments: 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host, user, passwd, acct, timeout 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The first four arguments are all strings, and have default value ''. 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm timeout must be numeric and defaults to None if not passed, 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm meaning that no timeout will be set on any ftp socket(s) 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If a timeout is passed, then this is now the default timeout for all ftp 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm socket operations for this instance. 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Then use self.connect() with optional host and port argument. 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm To download a file, use ftp.retrlines('RETR ' + filename), 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm or ftp.retrbinary() with slightly different arguments. 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm To upload a file, use ftp.storlines() or ftp.storbinary(), 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm which have an open file as argument (see their definitions 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm below for details). 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The download/upload functions first issue appropriate TYPE 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm and PORT or PASV commands. 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm''' 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm debugging = 0 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = '' 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = FTP_PORT 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock = None 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm file = None 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm welcome = None 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passiveserver = 1 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Initialization method (called by class instantiation). 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Initialize host to localhost, port to standard ftp port 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Optional arguments are host (for connect()), 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # and user, passwd, acct (for login()) 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, host='', user='', passwd='', acct='', 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm timeout=_GLOBAL_DEFAULT_TIMEOUT): 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.timeout = timeout 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if host: 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.connect(host) 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if user: 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.login(user, passwd, acct) 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def connect(self, host='', port=0, timeout=-999): 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Connect to host. Arguments are: 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm - host: hostname to connect to (string, default previous host) 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm - port: port to connect to (integer, default previous port) 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ''' 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if host != '': 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.host = host 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if port > 0: 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.port = port 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if timeout != -999: 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.timeout = timeout 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = socket.create_connection((self.host, self.port), self.timeout) 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.af = self.sock.family 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file = self.sock.makefile('rb') 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.welcome = self.getresp() 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.welcome 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getwelcome(self): 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Get the welcome message from the server. 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm (this is read and squirreled away by connect())''' 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging: 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '*welcome*', self.sanitize(self.welcome) 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.welcome 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def set_debuglevel(self, level): 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Set the debugging level. 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The required argument level means: 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 0: no debugging output (default) 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1: print commands and responses but not body text etc. 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2: also print raw lines read and sent before stripping CR/LF''' 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.debugging = level 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm debug = set_debuglevel 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def set_pasv(self, val): 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Use passive or active mode for data transfers. 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm With a false argument, use the normal PORT mode, 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm With a true argument, use the PASV command.''' 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.passiveserver = val 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: "sanitize" a string for printing 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def sanitize(self, s): 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if s[:5] == 'pass ' or s[:5] == 'PASS ': 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = len(s) 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while i > 5 and s[i-1] in '\r\n': 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i-1 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = s[:5] + '*'*(i-5) + s[i:] 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return repr(s) 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: send one line to the server, appending CRLF 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def putline(self, line): 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line + CRLF 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging > 1: print '*put*', self.sanitize(line) 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.sendall(line) 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: send one command to the server (through putline()) 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def putcmd(self, line): 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging: print '*cmd*', self.sanitize(line) 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.putline(line) 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: return one line from the server, stripping CRLF. 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Raise EOFError if the connection is closed 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getline(self): 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = self.file.readline() 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging > 1: 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '*get*', self.sanitize(line) 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: raise EOFError 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: line = line[:-2] 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif line[-1:] in CRLF: line = line[:-1] 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: get a response from the server, which may possibly 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # consist of multiple lines. Return a single string with no 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # trailing CRLF. If the response consists of multiple lines, 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # these are separated by '\n' characters in the string 1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getmultiline(self): 1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = self.getline() 1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[3:4] == '-': 1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm code = line[:3] 1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm nextline = self.getline() 2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line + ('\n' + nextline) 2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if nextline[:3] == code and \ 2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm nextline[3:4] != '-': 2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return line 2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Internal: get a response from the server. 2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Raise various errors if the response indicates an error 2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def getresp(self): 2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.getmultiline() 2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging: print '*resp*', self.sanitize(resp) 2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.lastresp = resp[:3] 2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm c = resp[:1] 2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if c in ('1', '2', '3'): 2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if c == '4': 2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_temp, resp 2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if c == '5': 2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_perm, resp 2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp 2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def voidresp(self): 2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Expect a response beginning with '2'.""" 2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.getresp() 2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:1] != '2': 2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def abort(self): 2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Abort a file transfer. Uses out-of-band data. 2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm This does not follow the procedure from the RFC to send Telnet 2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm IP and Synch; that doesn't seem to work with the servers I've 2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tried. Instead, just send the ABOR command as OOB data.''' 2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = 'ABOR' + CRLF 2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging > 1: print '*put urgent*', self.sanitize(line) 2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.sendall(line, MSG_OOB) 2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.getmultiline() 2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] not in ('426', '225', '226'): 2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp 2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def sendcmd(self, cmd): 2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Send a command and return the response.''' 2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.putcmd(cmd) 2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.getresp() 2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def voidcmd(self, cmd): 2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Send a command and expect a response beginning with '2'.""" 2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.putcmd(cmd) 2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def sendport(self, host, port): 2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Send a PORT command with the current host and the given 2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port number. 2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ''' 2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm hbytes = host.split('.') 2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm pbytes = [repr(port//256), repr(port%256)] 2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm bytes = hbytes + pbytes 2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'PORT ' + ','.join(bytes) 2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd(cmd) 2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def sendeprt(self, host, port): 2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Send a EPRT command with the current host and the given port number.''' 2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm af = 0 2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.af == socket.AF_INET: 2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm af = 1 2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.af == socket.AF_INET6: 2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm af = 2 2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if af == 0: 2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, 'unsupported address family' 2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fields = ['', repr(af), host, repr(port), ''] 2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'EPRT ' + '|'.join(fields) 2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd(cmd) 2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def makeport(self): 2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Create a new socket and send a PORT command for it.''' 2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm msg = "getaddrinfo returns an empty list" 2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock = None 2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): 2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm af, socktype, proto, canonname, sa = res 2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock = socket.socket(af, socktype, proto) 2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock.bind(sa) 2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except socket.error, msg: 2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sock: 2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock.close() 2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock = None 2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not sock: 2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise socket.error, msg 2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock.listen(1) 2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = sock.getsockname()[1] # Get proper port 2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = self.sock.getsockname()[0] # Get proper host 2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.af == socket.AF_INET: 2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendport(host, port) 2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendeprt(host, port) 2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: 2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock.settimeout(self.timeout) 3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return sock 3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def makepasv(self): 3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.af == socket.AF_INET: 3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host, port = parse227(self.sendcmd('PASV')) 3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) 3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return host, port 3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def ntransfercmd(self, cmd, rest=None): 3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Initiate a transfer over the data connection. 3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm If the transfer is active, send a port command and the 3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm transfer command, and accept the connection. If the server is 3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passive, send a pasv command, connect to it, and start the 3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm transfer command. Either way, return the socket for the 3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm connection and the expected size of the transfer. The 3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm expected size may be None if it could not be determined. 3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Optional `rest' argument can be a string that is sent as the 3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm argument to a REST command. This is essentially a server 3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm marker used to tell the server to skip over any data up to the 3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm given marker. 3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm size = None 3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.passiveserver: 3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host, port = self.makepasv() 3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = socket.create_connection((host, port), self.timeout) 3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if rest is not None: 3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sendcmd("REST %s" % rest) 3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd(cmd) 3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Some servers apparently send a 200 reply to 3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # a LIST or STOR command, before the 150 reply 3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # (and way before the 226 reply). This seems to 3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # be in violation of the protocol (which only allows 3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 1xx or error messages for LIST), so we just discard 3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # this response. 3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] == '2': 3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.getresp() 3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] != '1': 3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sock = self.makeport() 3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if rest is not None: 3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sendcmd("REST %s" % rest) 3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd(cmd) 3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # See above. 3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] == '2': 3484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.getresp() 3494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] != '1': 3504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 3514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn, sockaddr = sock.accept() 3524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: 3534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.settimeout(self.timeout) 3544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] == '150': 3554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # this is conditional in case we received a 125 3564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm size = parse150(resp) 3574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return conn, size 3584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def transfercmd(self, cmd, rest=None): 3604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Like ntransfercmd() but returns only the socket.""" 3614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.ntransfercmd(cmd, rest)[0] 3624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def login(self, user = '', passwd = '', acct = ''): 3644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Login, default anonymous.''' 3654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not user: user = 'anonymous' 3664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not passwd: passwd = '' 3674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not acct: acct = '' 3684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if user == 'anonymous' and passwd in ('', '-'): 3694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # If there is no anonymous ftp password specified 3704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # then we'll just use anonymous@ 3714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # We don't send any other thing because: 3724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # - We want to remain anonymous 3734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # - We want to stop SPAM 3744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # - We don't want to let ftp sites to discriminate by the user, 3754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # host or country. 3764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passwd = passwd + 'anonymous@' 3774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('USER ' + user) 3784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd) 3794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct) 3804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] != '2': 3814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 3824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 3834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def retrbinary(self, cmd, callback, blocksize=8192, rest=None): 3854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Retrieve data in binary mode. A new port is created for you. 3864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Args: 3884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd: A RETR command. 3894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback: A single parameter callable to be called on each 3904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm block of data read. 3914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm blocksize: The maximum number of bytes to read from the 3924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm socket at one time. [default: 8192] 3934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm rest: Passed to transfercmd(). [default: None] 3944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns: 3964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The response code. 3974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 3984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE I') 3994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd, rest) 4004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 4014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = conn.recv(blocksize) 4024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not data: 4034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 4044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback(data) 4054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 4064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 4074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def retrlines(self, cmd, callback = None): 4094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Retrieve data in line mode. A new port is created for you. 4104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Args: 4124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd: A RETR, LIST, NLST, or MLSD command. 4134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback: An optional single parameter callable that is called 4144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for each line with the trailing CRLF stripped. 4154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm [default: print_line()] 4164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns: 4184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The response code. 4194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 4204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback is None: callback = print_line 4214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('TYPE A') 4224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd) 4234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp = conn.makefile('rb') 4244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 4254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = fp.readline() 4264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging > 2: print '*retr*', repr(line) 4274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: 4284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 4294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 4304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-2] 4314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif line[-1:] == '\n': 4324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-1] 4334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback(line) 4344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp.close() 4354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 4364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 4374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None): 4394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Store a file in binary mode. A new port is created for you. 4404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Args: 4424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd: A STOR command. 4434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp: A file-like object with a read(num_bytes) method. 4444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm blocksize: The maximum data size to read from fp and send over 4454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm the connection at once. [default: 8192] 4464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback: An optional single parameter callable that is called on 4474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm on each block of data after it is sent. [default: None] 4484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm rest: Passed to transfercmd(). [default: None] 4494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns: 4514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The response code. 4524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 4534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE I') 4544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd, rest) 4554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 4564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = fp.read(blocksize) 4574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not buf: break 4584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.sendall(buf) 4594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback: callback(buf) 4604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 4614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 4624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def storlines(self, cmd, fp, callback=None): 4644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Store a file in line mode. A new port is created for you. 4654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Args: 4674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd: A STOR command. 4684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp: A file-like object with a readline() method. 4694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback: An optional single parameter callable that is called on 4704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm on each line after it is sent. [default: None] 4714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns: 4734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The response code. 4744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 4754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE A') 4764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd) 4774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 4784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = fp.readline() 4794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not buf: break 4804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if buf[-2:] != CRLF: 4814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if buf[-1] in CRLF: buf = buf[:-1] 4824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = buf + CRLF 4834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.sendall(buf) 4844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback: callback(buf) 4854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 4864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 4874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def acct(self, password): 4894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Send new account name.''' 4904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'ACCT ' + password 4914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd(cmd) 4924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 4934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def nlst(self, *args): 4944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Return a list of files in a given directory (default the current).''' 4954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'NLST' 4964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for arg in args: 4974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = cmd + (' ' + arg) 4984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm files = [] 4994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.retrlines(cmd, files.append) 5004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return files 5014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def dir(self, *args): 5034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''List a directory in long form. 5044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm By default list current directory to stdout. 5054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Optional last argument is callback function; all 5064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm non-empty arguments before it are concatenated to the 5074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm LIST command. (This *should* only be used for a pathname.)''' 5084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'LIST' 5094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm func = None 5104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if args[-1:] and type(args[-1]) != type(''): 5114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm args, func = args[:-1], args[-1] 5124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for arg in args: 5134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if arg: 5144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = cmd + (' ' + arg) 5154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.retrlines(cmd, func) 5164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def rename(self, fromname, toname): 5184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Rename a file.''' 5194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('RNFR ' + fromname) 5204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[0] != '3': 5214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 5224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd('RNTO ' + toname) 5234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def delete(self, filename): 5254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Delete a file.''' 5264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('DELE ' + filename) 5274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] in ('250', '200'): 5284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 5294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 5304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 5314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def cwd(self, dirname): 5334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Change to a directory.''' 5344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if dirname == '..': 5354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 5364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd('CDUP') 5374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except error_perm, msg: 5384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if msg.args[0][:3] != '500': 5394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise 5404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif dirname == '': 5414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dirname = '.' # does nothing, but could return error 5424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'CWD ' + dirname 5434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd(cmd) 5444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def size(self, filename): 5464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Retrieve the size of a file.''' 5474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # The SIZE command is defined in RFC-3659 5484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('SIZE ' + filename) 5494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] == '213': 5504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = resp[3:].strip() 5514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 5524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return int(s) 5534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except (OverflowError, ValueError): 5544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return long(s) 5554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def mkd(self, dirname): 5574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Make a directory, return its full pathname.''' 5584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('MKD ' + dirname) 5594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return parse257(resp) 5604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def rmd(self, dirname): 5624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Remove a directory.''' 5634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidcmd('RMD ' + dirname) 5644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def pwd(self): 5664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Return current working directory.''' 5674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('PWD') 5684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return parse257(resp) 5694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def quit(self): 5714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Quit, and close the connection.''' 5724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.voidcmd('QUIT') 5734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.close() 5744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 5754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def close(self): 5774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Close the connection without assuming anything about it.''' 5784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.file: 5794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file.close() 5804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock.close() 5814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file = self.sock = None 5824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry: 5854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import ssl 5864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError: 5874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm pass 5884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmelse: 5894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm class FTP_TLS(FTP): 5904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''A FTP subclass which adds TLS support to FTP as described 5914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm in RFC-4217. 5924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Connect as usual to port 21 implicitly securing the FTP control 5944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm connection before authenticating. 5954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Securing the data connection requires user to explicitly ask 5974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for it by calling prot_p() method. 5984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 5994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Usage example: 6004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> from ftplib import FTP_TLS 6014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> ftps = FTP_TLS('ftp.python.org') 6024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> ftps.login() # login anonymously previously securing control channel 6034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '230 Guest login ok, access restrictions apply.' 6044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> ftps.prot_p() # switch to secure data connection 6054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '200 Protection level set to P' 6064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> ftps.retrlines('LIST') # list directory content securely 6074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm total 9 6084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 8 root wheel 1024 Jan 3 1994 . 6094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .. 6104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin 6114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc 6124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming 6134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib 6144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub 6154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr 6164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg 6174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '226 Transfer complete.' 6184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> ftps.quit() 6194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '221 Goodbye.' 6204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm >>> 6214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ''' 6224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ssl_version = ssl.PROTOCOL_TLSv1 6234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, host='', user='', passwd='', acct='', keyfile=None, 6254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT): 6264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.keyfile = keyfile 6274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.certfile = certfile 6284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._prot_p = False 6294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm FTP.__init__(self, host, user, passwd, acct, timeout) 6304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def login(self, user='', passwd='', acct='', secure=True): 6324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if secure and not isinstance(self.sock, ssl.SSLSocket): 6334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.auth() 6344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return FTP.login(self, user, passwd, acct) 6354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def auth(self): 6374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Set up secure control connection by using TLS/SSL.''' 6384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(self.sock, ssl.SSLSocket): 6394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise ValueError("Already using TLS") 6404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.ssl_version == ssl.PROTOCOL_TLSv1: 6414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.voidcmd('AUTH TLS') 6424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 6434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.voidcmd('AUTH SSL') 6444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, 6454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ssl_version=self.ssl_version) 6464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.file = self.sock.makefile(mode='rb') 6474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 6484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def prot_p(self): 6504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Set up secure data connection.''' 6514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # PROT defines whether or not the data channel is to be protected. 6524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Though RFC-2228 defines four possible protection levels, 6534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # RFC-4217 only recommends two, Clear and Private. 6544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Clear (PROT C) means that no security is to be used on the 6554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # data-channel, Private (PROT P) means that the data-channel 6564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # should be protected by TLS. 6574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # PBSZ command MUST still be issued, but must have a parameter of 6584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # '0' to indicate that no buffering is taking place and the data 6594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # connection should not be encapsulated. 6604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('PBSZ 0') 6614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.voidcmd('PROT P') 6624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._prot_p = True 6634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 6644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def prot_c(self): 6664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Set up clear text data connection.''' 6674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.voidcmd('PROT C') 6684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._prot_p = False 6694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return resp 6704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # --- Overridden FTP methods 6724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def ntransfercmd(self, cmd, rest=None): 6744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn, size = FTP.ntransfercmd(self, cmd, rest) 6754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._prot_p: 6764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = ssl.wrap_socket(conn, self.keyfile, self.certfile, 6774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ssl_version=self.ssl_version) 6784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return conn, size 6794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def retrbinary(self, cmd, callback, blocksize=8192, rest=None): 6814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE I') 6824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd, rest) 6834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 6844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 6854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = conn.recv(blocksize) 6864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not data: 6874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 6884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback(data) 6894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # shutdown ssl layer 6904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(conn, ssl.SSLSocket): 6914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.unwrap() 6924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm finally: 6934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 6944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 6954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 6964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def retrlines(self, cmd, callback = None): 6974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback is None: callback = print_line 6984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = self.sendcmd('TYPE A') 6994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd) 7004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp = conn.makefile('rb') 7014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 7024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 7034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = fp.readline() 7044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self.debugging > 2: print '*retr*', repr(line) 7054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: 7064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 7074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 7084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-2] 7094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif line[-1:] == '\n': 7104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-1] 7114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm callback(line) 7124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # shutdown ssl layer 7134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(conn, ssl.SSLSocket): 7144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.unwrap() 7154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm finally: 7164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp.close() 7174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 7184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 7194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None): 7214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE I') 7224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd, rest) 7234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 7244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 7254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = fp.read(blocksize) 7264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not buf: break 7274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.sendall(buf) 7284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback: callback(buf) 7294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # shutdown ssl layer 7304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(conn, ssl.SSLSocket): 7314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.unwrap() 7324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm finally: 7334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 7344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 7354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def storlines(self, cmd, fp, callback=None): 7374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.voidcmd('TYPE A') 7384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn = self.transfercmd(cmd) 7394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 7404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 7414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = fp.readline() 7424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not buf: break 7434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if buf[-2:] != CRLF: 7444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if buf[-1] in CRLF: buf = buf[:-1] 7454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm buf = buf + CRLF 7464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.sendall(buf) 7474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if callback: callback(buf) 7484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # shutdown ssl layer 7494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if isinstance(conn, ssl.SSLSocket): 7504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.unwrap() 7514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm finally: 7524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm conn.close() 7534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.voidresp() 7544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm __all__.append('FTP_TLS') 7564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm all_errors = (Error, IOError, EOFError, ssl.SSLError) 7574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_150_re = None 7604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse150(resp): 7624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Parse the '150' response for a RETR request. 7634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns the expected transfer size or None; size is not guaranteed to 7644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm be present in the 150 message. 7654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ''' 7664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] != '150': 7674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 7684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm global _150_re 7694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if _150_re is None: 7704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import re 7714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE) 7724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm m = _150_re.match(resp) 7734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not m: 7744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 7754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = m.group(1) 7764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 7774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return int(s) 7784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except (OverflowError, ValueError): 7794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return long(s) 7804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_227_re = None 7834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse227(resp): 7854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Parse the '227' response for a PASV request. 7864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' 7874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Return ('host.addr.as.numbers', port#) tuple.''' 7884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 7894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] != '227': 7904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 7914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm global _227_re 7924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if _227_re is None: 7934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm import re 7944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)') 7954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm m = _227_re.search(resp) 7964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not m: 7974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp 7984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm numbers = m.groups() 7994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = '.'.join(numbers[:4]) 8004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = (int(numbers[4]) << 8) + int(numbers[5]) 8014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return host, port 8024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse229(resp, peer): 8054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Parse the '229' response for a EPSV request. 8064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Raises error_proto if it does not contain '(|||port|)' 8074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Return ('host.addr.as.numbers', port#) tuple.''' 8084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] != '229': 8104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 8114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm left = resp.find('(') 8124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if left < 0: raise error_proto, resp 8134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm right = resp.find(')', left + 1) 8144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if right < 0: 8154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp # should contain '(|||port|)' 8164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[left + 1] != resp[right - 1]: 8174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp 8184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm parts = resp[left + 1:right].split(resp[left+1]) 8194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(parts) != 5: 8204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_proto, resp 8214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = peer[0] 8224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = int(parts[3]) 8234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return host, port 8244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse257(resp): 8274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Parse the '257' response for a MKD or PWD request. 8284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm This is a response to a MKD or PWD request: a directory name. 8294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Returns the directoryname in the 257 reply.''' 8304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[:3] != '257': 8324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise error_reply, resp 8334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if resp[3:5] != ' "': 8344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return '' # Not compliant to RFC 959, but UNIX ftpd does this 8354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dirname = '' 8364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = 5 8374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm n = len(resp) 8384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while i < n: 8394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm c = resp[i] 8404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i+1 8414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if c == '"': 8424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if i >= n or resp[i] != '"': 8434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 8444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i+1 8454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm dirname = dirname + c 8464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return dirname 8474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef print_line(line): 8504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Default retrlines callback to print a line.''' 8514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print line 8524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef ftpcp(source, sourcename, target, targetname = '', type = 'I'): 8554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Copy file from one FTP-instance to another.''' 8564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not targetname: targetname = sourcename 8574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm type = 'TYPE ' + type 8584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm source.voidcmd(type) 8594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm target.voidcmd(type) 8604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sourcehost, sourceport = parse227(source.sendcmd('PASV')) 8614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm target.sendport(sourcehost, sourceport) 8624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # RFC 959: the user must "listen" [...] BEFORE sending the 8634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # transfer request. 8644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # So: STOR before RETR, because here the target is a "user". 8654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm treply = target.sendcmd('STOR ' + targetname) 8664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if treply[:3] not in ('125', '150'): raise error_proto # RFC 959 8674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sreply = source.sendcmd('RETR ' + sourcename) 8684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959 8694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm source.voidresp() 8704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm target.voidresp() 8714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass Netrc: 8744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Class to parse & provide access to 'netrc' format files. 8754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm See the netrc(4) man page for information on the file format. 8774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WARNING: This class is obsolete -- use module netrc instead. 8794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 8814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm __defuser = None 8824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm __defpasswd = None 8834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm __defacct = None 8844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 8854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, filename=None): 8864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if filename is None: 8874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if "HOME" in os.environ: 8884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = os.path.join(os.environ["HOME"], 8894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ".netrc") 8904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 8914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise IOError, \ 8924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm "specify file to load or set $HOME" 8934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__hosts = {} 8944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__macros = {} 8954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp = open(filename, "r") 8964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm in_macro = 0 8974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 8984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = fp.readline() 8994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: break 9004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if in_macro and line.strip(): 9014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm macro_lines.append(line) 9024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 9034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif in_macro: 9044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__macros[macro_name] = tuple(macro_lines) 9054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm in_macro = 0 9064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm words = line.split() 9074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = user = passwd = acct = None 9084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm default = 0 9094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = 0 9104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while i < len(words): 9114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm w1 = words[i] 9124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if i+1 < len(words): 9134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm w2 = words[i + 1] 9144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 9154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm w2 = None 9164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if w1 == 'default': 9174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm default = 1 9184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif w1 == 'machine' and w2: 9194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = w2.lower() 9204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i + 1 9214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif w1 == 'login' and w2: 9224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user = w2 9234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i + 1 9244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif w1 == 'password' and w2: 9254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passwd = w2 9264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i + 1 9274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif w1 == 'account' and w2: 9284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm acct = w2 9294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i + 1 9304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif w1 == 'macdef' and w2: 9314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm macro_name = w2 9324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm macro_lines = [] 9334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm in_macro = 1 9344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 9354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm i = i + 1 9364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if default: 9374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__defuser = user or self.__defuser 9384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__defpasswd = passwd or self.__defpasswd 9394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__defacct = acct or self.__defacct 9404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if host: 9414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if host in self.__hosts: 9424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ouser, opasswd, oacct = \ 9434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__hosts[host] 9444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user = user or ouser 9454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passwd = passwd or opasswd 9464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm acct = acct or oacct 9474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.__hosts[host] = user, passwd, acct 9484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp.close() 9494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_hosts(self): 9514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Return a list of hosts mentioned in the .netrc file.""" 9524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.__hosts.keys() 9534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_account(self, host): 9554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Returns login information for the named host. 9564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The return value is a triple containing userid, 9584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm password, and the accounting field. 9594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 9614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = host.lower() 9624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user = passwd = acct = None 9634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if host in self.__hosts: 9644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user, passwd, acct = self.__hosts[host] 9654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm user = user or self.__defuser 9664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm passwd = passwd or self.__defpasswd 9674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm acct = acct or self.__defacct 9684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return user, passwd, acct 9694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_macros(self): 9714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Return a list of all defined macro names.""" 9724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.__macros.keys() 9734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_macro(self, macro): 9754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Return a sequence of lines which define a named macro.""" 9764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self.__macros[macro] 9774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef test(): 9814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '''Test program. 9824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ... 9834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm -d dir 9854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm -l list 9864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm -p password 9874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ''' 9884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(sys.argv) < 2: 9904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print test.__doc__ 9914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.exit(0) 9924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 9934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm debugging = 0 9944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm rcfile = None 9954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while sys.argv[1] == '-d': 9964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm debugging = debugging+1 9974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm del sys.argv[1] 9984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sys.argv[1][:2] == '-r': 9994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # get name of alternate ~/.netrc file: 10004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm rcfile = sys.argv[1][2:] 10014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm del sys.argv[1] 10024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = sys.argv[1] 10034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp = FTP(host) 10044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.set_debuglevel(debugging) 10054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm userid = passwd = acct = '' 10064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 10074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm netrc = Netrc(rcfile) 10084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError: 10094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if rcfile is not None: 10104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.stderr.write("Could not open account file" 10114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm " -- using anonymous login.") 10124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 10134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 10144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm userid, passwd, acct = netrc.get_account(host) 10154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 10164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # no account for host 10174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.stderr.write( 10184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm "No account -- using anonymous login.") 10194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.login(userid, passwd, acct) 10204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for file in sys.argv[2:]: 10214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if file[:2] == '-l': 10224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.dir(file[2:]) 10234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif file[:2] == '-d': 10244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = 'CWD' 10254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if file[2:]: cmd = cmd + ' ' + file[2:] 10264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm resp = ftp.sendcmd(cmd) 10274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif file == '-p': 10284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.set_pasv(not ftp.passiveserver) 10294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 10304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.retrbinary('RETR ' + file, \ 10314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.stdout.write, 1024) 10324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ftp.quit() 10334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 10344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 10354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == '__main__': 10364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm test() 1037