1*cda5da8dSAndroid Build Coastguard Worker"""An FTP client class and some helper functions. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerBased on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard WorkerExample: 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker>>> from ftplib import FTP 8*cda5da8dSAndroid Build Coastguard Worker>>> ftp = FTP('ftp.python.org') # connect to host, default port 9*cda5da8dSAndroid Build Coastguard Worker>>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@ 10*cda5da8dSAndroid Build Coastguard Worker'230 Guest login ok, access restrictions apply.' 11*cda5da8dSAndroid Build Coastguard Worker>>> ftp.retrlines('LIST') # list directory contents 12*cda5da8dSAndroid Build Coastguard Workertotal 9 13*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 8 root wheel 1024 Jan 3 1994 . 14*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 8 root wheel 1024 Jan 3 1994 .. 15*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin 16*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc 17*cda5da8dSAndroid Build Coastguard Workerd-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming 18*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib 19*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub 20*cda5da8dSAndroid Build Coastguard Workerdrwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr 21*cda5da8dSAndroid Build Coastguard Worker-rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg 22*cda5da8dSAndroid Build Coastguard Worker'226 Transfer complete.' 23*cda5da8dSAndroid Build Coastguard Worker>>> ftp.quit() 24*cda5da8dSAndroid Build Coastguard Worker'221 Goodbye.' 25*cda5da8dSAndroid Build Coastguard Worker>>> 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard WorkerA nice test that reveals some of the network dialogue would be: 28*cda5da8dSAndroid Build Coastguard Workerpython ftplib.py -d localhost -l -p -l 29*cda5da8dSAndroid Build Coastguard Worker""" 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Worker# 32*cda5da8dSAndroid Build Coastguard Worker# Changes and improvements suggested by Steve Majewski. 33*cda5da8dSAndroid Build Coastguard Worker# Modified by Jack to work on the mac. 34*cda5da8dSAndroid Build Coastguard Worker# Modified by Siebren to support docstrings and PASV. 35*cda5da8dSAndroid Build Coastguard Worker# Modified by Phil Schwartz to add storbinary and storlines callbacks. 36*cda5da8dSAndroid Build Coastguard Worker# Modified by Giampaolo Rodola' to add TLS support. 37*cda5da8dSAndroid Build Coastguard Worker# 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Workerimport sys 40*cda5da8dSAndroid Build Coastguard Workerimport socket 41*cda5da8dSAndroid Build Coastguard Workerfrom socket import _GLOBAL_DEFAULT_TIMEOUT 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Worker__all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", 44*cda5da8dSAndroid Build Coastguard Worker "all_errors"] 45*cda5da8dSAndroid Build Coastguard Worker 46*cda5da8dSAndroid Build Coastguard Worker# Magic number from <socket.h> 47*cda5da8dSAndroid Build Coastguard WorkerMSG_OOB = 0x1 # Process data out of band 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker 50*cda5da8dSAndroid Build Coastguard Worker# The standard FTP server control port 51*cda5da8dSAndroid Build Coastguard WorkerFTP_PORT = 21 52*cda5da8dSAndroid Build Coastguard Worker# The sizehint parameter passed to readline() calls 53*cda5da8dSAndroid Build Coastguard WorkerMAXLINE = 8192 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker 56*cda5da8dSAndroid Build Coastguard Worker# Exception raised when an error or invalid response is received 57*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception): pass 58*cda5da8dSAndroid Build Coastguard Workerclass error_reply(Error): pass # unexpected [123]xx reply 59*cda5da8dSAndroid Build Coastguard Workerclass error_temp(Error): pass # 4xx errors 60*cda5da8dSAndroid Build Coastguard Workerclass error_perm(Error): pass # 5xx errors 61*cda5da8dSAndroid Build Coastguard Workerclass error_proto(Error): pass # response does not begin with [1-5] 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker# All exceptions (hopefully) that may be raised here and that aren't 65*cda5da8dSAndroid Build Coastguard Worker# (always) programming errors on our side 66*cda5da8dSAndroid Build Coastguard Workerall_errors = (Error, OSError, EOFError) 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker 69*cda5da8dSAndroid Build Coastguard Worker# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) 70*cda5da8dSAndroid Build Coastguard WorkerCRLF = '\r\n' 71*cda5da8dSAndroid Build Coastguard WorkerB_CRLF = b'\r\n' 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Worker# The class itself 74*cda5da8dSAndroid Build Coastguard Workerclass FTP: 75*cda5da8dSAndroid Build Coastguard Worker '''An FTP client class. 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker To create a connection, call the class using these arguments: 78*cda5da8dSAndroid Build Coastguard Worker host, user, passwd, acct, timeout, source_address, encoding 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker The first four arguments are all strings, and have default value ''. 81*cda5da8dSAndroid Build Coastguard Worker The parameter ´timeout´ must be numeric and defaults to None if not 82*cda5da8dSAndroid Build Coastguard Worker passed, meaning that no timeout will be set on any ftp socket(s). 83*cda5da8dSAndroid Build Coastguard Worker If a timeout is passed, then this is now the default timeout for all ftp 84*cda5da8dSAndroid Build Coastguard Worker socket operations for this instance. 85*cda5da8dSAndroid Build Coastguard Worker The last parameter is the encoding of filenames, which defaults to utf-8. 86*cda5da8dSAndroid Build Coastguard Worker 87*cda5da8dSAndroid Build Coastguard Worker Then use self.connect() with optional host and port argument. 88*cda5da8dSAndroid Build Coastguard Worker 89*cda5da8dSAndroid Build Coastguard Worker To download a file, use ftp.retrlines('RETR ' + filename), 90*cda5da8dSAndroid Build Coastguard Worker or ftp.retrbinary() with slightly different arguments. 91*cda5da8dSAndroid Build Coastguard Worker To upload a file, use ftp.storlines() or ftp.storbinary(), 92*cda5da8dSAndroid Build Coastguard Worker which have an open file as argument (see their definitions 93*cda5da8dSAndroid Build Coastguard Worker below for details). 94*cda5da8dSAndroid Build Coastguard Worker The download/upload functions first issue appropriate TYPE 95*cda5da8dSAndroid Build Coastguard Worker and PORT or PASV commands. 96*cda5da8dSAndroid Build Coastguard Worker ''' 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker debugging = 0 99*cda5da8dSAndroid Build Coastguard Worker host = '' 100*cda5da8dSAndroid Build Coastguard Worker port = FTP_PORT 101*cda5da8dSAndroid Build Coastguard Worker maxline = MAXLINE 102*cda5da8dSAndroid Build Coastguard Worker sock = None 103*cda5da8dSAndroid Build Coastguard Worker file = None 104*cda5da8dSAndroid Build Coastguard Worker welcome = None 105*cda5da8dSAndroid Build Coastguard Worker passiveserver = True 106*cda5da8dSAndroid Build Coastguard Worker # Disables https://bugs.python.org/issue43285 security if set to True. 107*cda5da8dSAndroid Build Coastguard Worker trust_server_pasv_ipv4_address = False 108*cda5da8dSAndroid Build Coastguard Worker 109*cda5da8dSAndroid Build Coastguard Worker def __init__(self, host='', user='', passwd='', acct='', 110*cda5da8dSAndroid Build Coastguard Worker timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *, 111*cda5da8dSAndroid Build Coastguard Worker encoding='utf-8'): 112*cda5da8dSAndroid Build Coastguard Worker """Initialization method (called by class instantiation). 113*cda5da8dSAndroid Build Coastguard Worker Initialize host to localhost, port to standard ftp port. 114*cda5da8dSAndroid Build Coastguard Worker Optional arguments are host (for connect()), 115*cda5da8dSAndroid Build Coastguard Worker and user, passwd, acct (for login()). 116*cda5da8dSAndroid Build Coastguard Worker """ 117*cda5da8dSAndroid Build Coastguard Worker self.encoding = encoding 118*cda5da8dSAndroid Build Coastguard Worker self.source_address = source_address 119*cda5da8dSAndroid Build Coastguard Worker self.timeout = timeout 120*cda5da8dSAndroid Build Coastguard Worker if host: 121*cda5da8dSAndroid Build Coastguard Worker self.connect(host) 122*cda5da8dSAndroid Build Coastguard Worker if user: 123*cda5da8dSAndroid Build Coastguard Worker self.login(user, passwd, acct) 124*cda5da8dSAndroid Build Coastguard Worker 125*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 126*cda5da8dSAndroid Build Coastguard Worker return self 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker # Context management protocol: try to quit() if active 129*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *args): 130*cda5da8dSAndroid Build Coastguard Worker if self.sock is not None: 131*cda5da8dSAndroid Build Coastguard Worker try: 132*cda5da8dSAndroid Build Coastguard Worker self.quit() 133*cda5da8dSAndroid Build Coastguard Worker except (OSError, EOFError): 134*cda5da8dSAndroid Build Coastguard Worker pass 135*cda5da8dSAndroid Build Coastguard Worker finally: 136*cda5da8dSAndroid Build Coastguard Worker if self.sock is not None: 137*cda5da8dSAndroid Build Coastguard Worker self.close() 138*cda5da8dSAndroid Build Coastguard Worker 139*cda5da8dSAndroid Build Coastguard Worker def connect(self, host='', port=0, timeout=-999, source_address=None): 140*cda5da8dSAndroid Build Coastguard Worker '''Connect to host. Arguments are: 141*cda5da8dSAndroid Build Coastguard Worker - host: hostname to connect to (string, default previous host) 142*cda5da8dSAndroid Build Coastguard Worker - port: port to connect to (integer, default previous port) 143*cda5da8dSAndroid Build Coastguard Worker - timeout: the timeout to set against the ftp socket(s) 144*cda5da8dSAndroid Build Coastguard Worker - source_address: a 2-tuple (host, port) for the socket to bind 145*cda5da8dSAndroid Build Coastguard Worker to as its source address before connecting. 146*cda5da8dSAndroid Build Coastguard Worker ''' 147*cda5da8dSAndroid Build Coastguard Worker if host != '': 148*cda5da8dSAndroid Build Coastguard Worker self.host = host 149*cda5da8dSAndroid Build Coastguard Worker if port > 0: 150*cda5da8dSAndroid Build Coastguard Worker self.port = port 151*cda5da8dSAndroid Build Coastguard Worker if timeout != -999: 152*cda5da8dSAndroid Build Coastguard Worker self.timeout = timeout 153*cda5da8dSAndroid Build Coastguard Worker if self.timeout is not None and not self.timeout: 154*cda5da8dSAndroid Build Coastguard Worker raise ValueError('Non-blocking socket (timeout=0) is not supported') 155*cda5da8dSAndroid Build Coastguard Worker if source_address is not None: 156*cda5da8dSAndroid Build Coastguard Worker self.source_address = source_address 157*cda5da8dSAndroid Build Coastguard Worker sys.audit("ftplib.connect", self, self.host, self.port) 158*cda5da8dSAndroid Build Coastguard Worker self.sock = socket.create_connection((self.host, self.port), self.timeout, 159*cda5da8dSAndroid Build Coastguard Worker source_address=self.source_address) 160*cda5da8dSAndroid Build Coastguard Worker self.af = self.sock.family 161*cda5da8dSAndroid Build Coastguard Worker self.file = self.sock.makefile('r', encoding=self.encoding) 162*cda5da8dSAndroid Build Coastguard Worker self.welcome = self.getresp() 163*cda5da8dSAndroid Build Coastguard Worker return self.welcome 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker def getwelcome(self): 166*cda5da8dSAndroid Build Coastguard Worker '''Get the welcome message from the server. 167*cda5da8dSAndroid Build Coastguard Worker (this is read and squirreled away by connect())''' 168*cda5da8dSAndroid Build Coastguard Worker if self.debugging: 169*cda5da8dSAndroid Build Coastguard Worker print('*welcome*', self.sanitize(self.welcome)) 170*cda5da8dSAndroid Build Coastguard Worker return self.welcome 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker def set_debuglevel(self, level): 173*cda5da8dSAndroid Build Coastguard Worker '''Set the debugging level. 174*cda5da8dSAndroid Build Coastguard Worker The required argument level means: 175*cda5da8dSAndroid Build Coastguard Worker 0: no debugging output (default) 176*cda5da8dSAndroid Build Coastguard Worker 1: print commands and responses but not body text etc. 177*cda5da8dSAndroid Build Coastguard Worker 2: also print raw lines read and sent before stripping CR/LF''' 178*cda5da8dSAndroid Build Coastguard Worker self.debugging = level 179*cda5da8dSAndroid Build Coastguard Worker debug = set_debuglevel 180*cda5da8dSAndroid Build Coastguard Worker 181*cda5da8dSAndroid Build Coastguard Worker def set_pasv(self, val): 182*cda5da8dSAndroid Build Coastguard Worker '''Use passive or active mode for data transfers. 183*cda5da8dSAndroid Build Coastguard Worker With a false argument, use the normal PORT mode, 184*cda5da8dSAndroid Build Coastguard Worker With a true argument, use the PASV command.''' 185*cda5da8dSAndroid Build Coastguard Worker self.passiveserver = val 186*cda5da8dSAndroid Build Coastguard Worker 187*cda5da8dSAndroid Build Coastguard Worker # Internal: "sanitize" a string for printing 188*cda5da8dSAndroid Build Coastguard Worker def sanitize(self, s): 189*cda5da8dSAndroid Build Coastguard Worker if s[:5] in {'pass ', 'PASS '}: 190*cda5da8dSAndroid Build Coastguard Worker i = len(s.rstrip('\r\n')) 191*cda5da8dSAndroid Build Coastguard Worker s = s[:5] + '*'*(i-5) + s[i:] 192*cda5da8dSAndroid Build Coastguard Worker return repr(s) 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker # Internal: send one line to the server, appending CRLF 195*cda5da8dSAndroid Build Coastguard Worker def putline(self, line): 196*cda5da8dSAndroid Build Coastguard Worker if '\r' in line or '\n' in line: 197*cda5da8dSAndroid Build Coastguard Worker raise ValueError('an illegal newline character should not be contained') 198*cda5da8dSAndroid Build Coastguard Worker sys.audit("ftplib.sendcmd", self, line) 199*cda5da8dSAndroid Build Coastguard Worker line = line + CRLF 200*cda5da8dSAndroid Build Coastguard Worker if self.debugging > 1: 201*cda5da8dSAndroid Build Coastguard Worker print('*put*', self.sanitize(line)) 202*cda5da8dSAndroid Build Coastguard Worker self.sock.sendall(line.encode(self.encoding)) 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker # Internal: send one command to the server (through putline()) 205*cda5da8dSAndroid Build Coastguard Worker def putcmd(self, line): 206*cda5da8dSAndroid Build Coastguard Worker if self.debugging: print('*cmd*', self.sanitize(line)) 207*cda5da8dSAndroid Build Coastguard Worker self.putline(line) 208*cda5da8dSAndroid Build Coastguard Worker 209*cda5da8dSAndroid Build Coastguard Worker # Internal: return one line from the server, stripping CRLF. 210*cda5da8dSAndroid Build Coastguard Worker # Raise EOFError if the connection is closed 211*cda5da8dSAndroid Build Coastguard Worker def getline(self): 212*cda5da8dSAndroid Build Coastguard Worker line = self.file.readline(self.maxline + 1) 213*cda5da8dSAndroid Build Coastguard Worker if len(line) > self.maxline: 214*cda5da8dSAndroid Build Coastguard Worker raise Error("got more than %d bytes" % self.maxline) 215*cda5da8dSAndroid Build Coastguard Worker if self.debugging > 1: 216*cda5da8dSAndroid Build Coastguard Worker print('*get*', self.sanitize(line)) 217*cda5da8dSAndroid Build Coastguard Worker if not line: 218*cda5da8dSAndroid Build Coastguard Worker raise EOFError 219*cda5da8dSAndroid Build Coastguard Worker if line[-2:] == CRLF: 220*cda5da8dSAndroid Build Coastguard Worker line = line[:-2] 221*cda5da8dSAndroid Build Coastguard Worker elif line[-1:] in CRLF: 222*cda5da8dSAndroid Build Coastguard Worker line = line[:-1] 223*cda5da8dSAndroid Build Coastguard Worker return line 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker # Internal: get a response from the server, which may possibly 226*cda5da8dSAndroid Build Coastguard Worker # consist of multiple lines. Return a single string with no 227*cda5da8dSAndroid Build Coastguard Worker # trailing CRLF. If the response consists of multiple lines, 228*cda5da8dSAndroid Build Coastguard Worker # these are separated by '\n' characters in the string 229*cda5da8dSAndroid Build Coastguard Worker def getmultiline(self): 230*cda5da8dSAndroid Build Coastguard Worker line = self.getline() 231*cda5da8dSAndroid Build Coastguard Worker if line[3:4] == '-': 232*cda5da8dSAndroid Build Coastguard Worker code = line[:3] 233*cda5da8dSAndroid Build Coastguard Worker while 1: 234*cda5da8dSAndroid Build Coastguard Worker nextline = self.getline() 235*cda5da8dSAndroid Build Coastguard Worker line = line + ('\n' + nextline) 236*cda5da8dSAndroid Build Coastguard Worker if nextline[:3] == code and \ 237*cda5da8dSAndroid Build Coastguard Worker nextline[3:4] != '-': 238*cda5da8dSAndroid Build Coastguard Worker break 239*cda5da8dSAndroid Build Coastguard Worker return line 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker # Internal: get a response from the server. 242*cda5da8dSAndroid Build Coastguard Worker # Raise various errors if the response indicates an error 243*cda5da8dSAndroid Build Coastguard Worker def getresp(self): 244*cda5da8dSAndroid Build Coastguard Worker resp = self.getmultiline() 245*cda5da8dSAndroid Build Coastguard Worker if self.debugging: 246*cda5da8dSAndroid Build Coastguard Worker print('*resp*', self.sanitize(resp)) 247*cda5da8dSAndroid Build Coastguard Worker self.lastresp = resp[:3] 248*cda5da8dSAndroid Build Coastguard Worker c = resp[:1] 249*cda5da8dSAndroid Build Coastguard Worker if c in {'1', '2', '3'}: 250*cda5da8dSAndroid Build Coastguard Worker return resp 251*cda5da8dSAndroid Build Coastguard Worker if c == '4': 252*cda5da8dSAndroid Build Coastguard Worker raise error_temp(resp) 253*cda5da8dSAndroid Build Coastguard Worker if c == '5': 254*cda5da8dSAndroid Build Coastguard Worker raise error_perm(resp) 255*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 256*cda5da8dSAndroid Build Coastguard Worker 257*cda5da8dSAndroid Build Coastguard Worker def voidresp(self): 258*cda5da8dSAndroid Build Coastguard Worker """Expect a response beginning with '2'.""" 259*cda5da8dSAndroid Build Coastguard Worker resp = self.getresp() 260*cda5da8dSAndroid Build Coastguard Worker if resp[:1] != '2': 261*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 262*cda5da8dSAndroid Build Coastguard Worker return resp 263*cda5da8dSAndroid Build Coastguard Worker 264*cda5da8dSAndroid Build Coastguard Worker def abort(self): 265*cda5da8dSAndroid Build Coastguard Worker '''Abort a file transfer. Uses out-of-band data. 266*cda5da8dSAndroid Build Coastguard Worker This does not follow the procedure from the RFC to send Telnet 267*cda5da8dSAndroid Build Coastguard Worker IP and Synch; that doesn't seem to work with the servers I've 268*cda5da8dSAndroid Build Coastguard Worker tried. Instead, just send the ABOR command as OOB data.''' 269*cda5da8dSAndroid Build Coastguard Worker line = b'ABOR' + B_CRLF 270*cda5da8dSAndroid Build Coastguard Worker if self.debugging > 1: 271*cda5da8dSAndroid Build Coastguard Worker print('*put urgent*', self.sanitize(line)) 272*cda5da8dSAndroid Build Coastguard Worker self.sock.sendall(line, MSG_OOB) 273*cda5da8dSAndroid Build Coastguard Worker resp = self.getmultiline() 274*cda5da8dSAndroid Build Coastguard Worker if resp[:3] not in {'426', '225', '226'}: 275*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 276*cda5da8dSAndroid Build Coastguard Worker return resp 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Worker def sendcmd(self, cmd): 279*cda5da8dSAndroid Build Coastguard Worker '''Send a command and return the response.''' 280*cda5da8dSAndroid Build Coastguard Worker self.putcmd(cmd) 281*cda5da8dSAndroid Build Coastguard Worker return self.getresp() 282*cda5da8dSAndroid Build Coastguard Worker 283*cda5da8dSAndroid Build Coastguard Worker def voidcmd(self, cmd): 284*cda5da8dSAndroid Build Coastguard Worker """Send a command and expect a response beginning with '2'.""" 285*cda5da8dSAndroid Build Coastguard Worker self.putcmd(cmd) 286*cda5da8dSAndroid Build Coastguard Worker return self.voidresp() 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard Worker def sendport(self, host, port): 289*cda5da8dSAndroid Build Coastguard Worker '''Send a PORT command with the current host and the given 290*cda5da8dSAndroid Build Coastguard Worker port number. 291*cda5da8dSAndroid Build Coastguard Worker ''' 292*cda5da8dSAndroid Build Coastguard Worker hbytes = host.split('.') 293*cda5da8dSAndroid Build Coastguard Worker pbytes = [repr(port//256), repr(port%256)] 294*cda5da8dSAndroid Build Coastguard Worker bytes = hbytes + pbytes 295*cda5da8dSAndroid Build Coastguard Worker cmd = 'PORT ' + ','.join(bytes) 296*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd(cmd) 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Worker def sendeprt(self, host, port): 299*cda5da8dSAndroid Build Coastguard Worker '''Send an EPRT command with the current host and the given port number.''' 300*cda5da8dSAndroid Build Coastguard Worker af = 0 301*cda5da8dSAndroid Build Coastguard Worker if self.af == socket.AF_INET: 302*cda5da8dSAndroid Build Coastguard Worker af = 1 303*cda5da8dSAndroid Build Coastguard Worker if self.af == socket.AF_INET6: 304*cda5da8dSAndroid Build Coastguard Worker af = 2 305*cda5da8dSAndroid Build Coastguard Worker if af == 0: 306*cda5da8dSAndroid Build Coastguard Worker raise error_proto('unsupported address family') 307*cda5da8dSAndroid Build Coastguard Worker fields = ['', repr(af), host, repr(port), ''] 308*cda5da8dSAndroid Build Coastguard Worker cmd = 'EPRT ' + '|'.join(fields) 309*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd(cmd) 310*cda5da8dSAndroid Build Coastguard Worker 311*cda5da8dSAndroid Build Coastguard Worker def makeport(self): 312*cda5da8dSAndroid Build Coastguard Worker '''Create a new socket and send a PORT command for it.''' 313*cda5da8dSAndroid Build Coastguard Worker sock = socket.create_server(("", 0), family=self.af, backlog=1) 314*cda5da8dSAndroid Build Coastguard Worker port = sock.getsockname()[1] # Get proper port 315*cda5da8dSAndroid Build Coastguard Worker host = self.sock.getsockname()[0] # Get proper host 316*cda5da8dSAndroid Build Coastguard Worker if self.af == socket.AF_INET: 317*cda5da8dSAndroid Build Coastguard Worker resp = self.sendport(host, port) 318*cda5da8dSAndroid Build Coastguard Worker else: 319*cda5da8dSAndroid Build Coastguard Worker resp = self.sendeprt(host, port) 320*cda5da8dSAndroid Build Coastguard Worker if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: 321*cda5da8dSAndroid Build Coastguard Worker sock.settimeout(self.timeout) 322*cda5da8dSAndroid Build Coastguard Worker return sock 323*cda5da8dSAndroid Build Coastguard Worker 324*cda5da8dSAndroid Build Coastguard Worker def makepasv(self): 325*cda5da8dSAndroid Build Coastguard Worker """Internal: Does the PASV or EPSV handshake -> (address, port)""" 326*cda5da8dSAndroid Build Coastguard Worker if self.af == socket.AF_INET: 327*cda5da8dSAndroid Build Coastguard Worker untrusted_host, port = parse227(self.sendcmd('PASV')) 328*cda5da8dSAndroid Build Coastguard Worker if self.trust_server_pasv_ipv4_address: 329*cda5da8dSAndroid Build Coastguard Worker host = untrusted_host 330*cda5da8dSAndroid Build Coastguard Worker else: 331*cda5da8dSAndroid Build Coastguard Worker host = self.sock.getpeername()[0] 332*cda5da8dSAndroid Build Coastguard Worker else: 333*cda5da8dSAndroid Build Coastguard Worker host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) 334*cda5da8dSAndroid Build Coastguard Worker return host, port 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker def ntransfercmd(self, cmd, rest=None): 337*cda5da8dSAndroid Build Coastguard Worker """Initiate a transfer over the data connection. 338*cda5da8dSAndroid Build Coastguard Worker 339*cda5da8dSAndroid Build Coastguard Worker If the transfer is active, send a port command and the 340*cda5da8dSAndroid Build Coastguard Worker transfer command, and accept the connection. If the server is 341*cda5da8dSAndroid Build Coastguard Worker passive, send a pasv command, connect to it, and start the 342*cda5da8dSAndroid Build Coastguard Worker transfer command. Either way, return the socket for the 343*cda5da8dSAndroid Build Coastguard Worker connection and the expected size of the transfer. The 344*cda5da8dSAndroid Build Coastguard Worker expected size may be None if it could not be determined. 345*cda5da8dSAndroid Build Coastguard Worker 346*cda5da8dSAndroid Build Coastguard Worker Optional `rest' argument can be a string that is sent as the 347*cda5da8dSAndroid Build Coastguard Worker argument to a REST command. This is essentially a server 348*cda5da8dSAndroid Build Coastguard Worker marker used to tell the server to skip over any data up to the 349*cda5da8dSAndroid Build Coastguard Worker given marker. 350*cda5da8dSAndroid Build Coastguard Worker """ 351*cda5da8dSAndroid Build Coastguard Worker size = None 352*cda5da8dSAndroid Build Coastguard Worker if self.passiveserver: 353*cda5da8dSAndroid Build Coastguard Worker host, port = self.makepasv() 354*cda5da8dSAndroid Build Coastguard Worker conn = socket.create_connection((host, port), self.timeout, 355*cda5da8dSAndroid Build Coastguard Worker source_address=self.source_address) 356*cda5da8dSAndroid Build Coastguard Worker try: 357*cda5da8dSAndroid Build Coastguard Worker if rest is not None: 358*cda5da8dSAndroid Build Coastguard Worker self.sendcmd("REST %s" % rest) 359*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd(cmd) 360*cda5da8dSAndroid Build Coastguard Worker # Some servers apparently send a 200 reply to 361*cda5da8dSAndroid Build Coastguard Worker # a LIST or STOR command, before the 150 reply 362*cda5da8dSAndroid Build Coastguard Worker # (and way before the 226 reply). This seems to 363*cda5da8dSAndroid Build Coastguard Worker # be in violation of the protocol (which only allows 364*cda5da8dSAndroid Build Coastguard Worker # 1xx or error messages for LIST), so we just discard 365*cda5da8dSAndroid Build Coastguard Worker # this response. 366*cda5da8dSAndroid Build Coastguard Worker if resp[0] == '2': 367*cda5da8dSAndroid Build Coastguard Worker resp = self.getresp() 368*cda5da8dSAndroid Build Coastguard Worker if resp[0] != '1': 369*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 370*cda5da8dSAndroid Build Coastguard Worker except: 371*cda5da8dSAndroid Build Coastguard Worker conn.close() 372*cda5da8dSAndroid Build Coastguard Worker raise 373*cda5da8dSAndroid Build Coastguard Worker else: 374*cda5da8dSAndroid Build Coastguard Worker with self.makeport() as sock: 375*cda5da8dSAndroid Build Coastguard Worker if rest is not None: 376*cda5da8dSAndroid Build Coastguard Worker self.sendcmd("REST %s" % rest) 377*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd(cmd) 378*cda5da8dSAndroid Build Coastguard Worker # See above. 379*cda5da8dSAndroid Build Coastguard Worker if resp[0] == '2': 380*cda5da8dSAndroid Build Coastguard Worker resp = self.getresp() 381*cda5da8dSAndroid Build Coastguard Worker if resp[0] != '1': 382*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 383*cda5da8dSAndroid Build Coastguard Worker conn, sockaddr = sock.accept() 384*cda5da8dSAndroid Build Coastguard Worker if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: 385*cda5da8dSAndroid Build Coastguard Worker conn.settimeout(self.timeout) 386*cda5da8dSAndroid Build Coastguard Worker if resp[:3] == '150': 387*cda5da8dSAndroid Build Coastguard Worker # this is conditional in case we received a 125 388*cda5da8dSAndroid Build Coastguard Worker size = parse150(resp) 389*cda5da8dSAndroid Build Coastguard Worker return conn, size 390*cda5da8dSAndroid Build Coastguard Worker 391*cda5da8dSAndroid Build Coastguard Worker def transfercmd(self, cmd, rest=None): 392*cda5da8dSAndroid Build Coastguard Worker """Like ntransfercmd() but returns only the socket.""" 393*cda5da8dSAndroid Build Coastguard Worker return self.ntransfercmd(cmd, rest)[0] 394*cda5da8dSAndroid Build Coastguard Worker 395*cda5da8dSAndroid Build Coastguard Worker def login(self, user = '', passwd = '', acct = ''): 396*cda5da8dSAndroid Build Coastguard Worker '''Login, default anonymous.''' 397*cda5da8dSAndroid Build Coastguard Worker if not user: 398*cda5da8dSAndroid Build Coastguard Worker user = 'anonymous' 399*cda5da8dSAndroid Build Coastguard Worker if not passwd: 400*cda5da8dSAndroid Build Coastguard Worker passwd = '' 401*cda5da8dSAndroid Build Coastguard Worker if not acct: 402*cda5da8dSAndroid Build Coastguard Worker acct = '' 403*cda5da8dSAndroid Build Coastguard Worker if user == 'anonymous' and passwd in {'', '-'}: 404*cda5da8dSAndroid Build Coastguard Worker # If there is no anonymous ftp password specified 405*cda5da8dSAndroid Build Coastguard Worker # then we'll just use anonymous@ 406*cda5da8dSAndroid Build Coastguard Worker # We don't send any other thing because: 407*cda5da8dSAndroid Build Coastguard Worker # - We want to remain anonymous 408*cda5da8dSAndroid Build Coastguard Worker # - We want to stop SPAM 409*cda5da8dSAndroid Build Coastguard Worker # - We don't want to let ftp sites to discriminate by the user, 410*cda5da8dSAndroid Build Coastguard Worker # host or country. 411*cda5da8dSAndroid Build Coastguard Worker passwd = passwd + 'anonymous@' 412*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('USER ' + user) 413*cda5da8dSAndroid Build Coastguard Worker if resp[0] == '3': 414*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('PASS ' + passwd) 415*cda5da8dSAndroid Build Coastguard Worker if resp[0] == '3': 416*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('ACCT ' + acct) 417*cda5da8dSAndroid Build Coastguard Worker if resp[0] != '2': 418*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 419*cda5da8dSAndroid Build Coastguard Worker return resp 420*cda5da8dSAndroid Build Coastguard Worker 421*cda5da8dSAndroid Build Coastguard Worker def retrbinary(self, cmd, callback, blocksize=8192, rest=None): 422*cda5da8dSAndroid Build Coastguard Worker """Retrieve data in binary mode. A new port is created for you. 423*cda5da8dSAndroid Build Coastguard Worker 424*cda5da8dSAndroid Build Coastguard Worker Args: 425*cda5da8dSAndroid Build Coastguard Worker cmd: A RETR command. 426*cda5da8dSAndroid Build Coastguard Worker callback: A single parameter callable to be called on each 427*cda5da8dSAndroid Build Coastguard Worker block of data read. 428*cda5da8dSAndroid Build Coastguard Worker blocksize: The maximum number of bytes to read from the 429*cda5da8dSAndroid Build Coastguard Worker socket at one time. [default: 8192] 430*cda5da8dSAndroid Build Coastguard Worker rest: Passed to transfercmd(). [default: None] 431*cda5da8dSAndroid Build Coastguard Worker 432*cda5da8dSAndroid Build Coastguard Worker Returns: 433*cda5da8dSAndroid Build Coastguard Worker The response code. 434*cda5da8dSAndroid Build Coastguard Worker """ 435*cda5da8dSAndroid Build Coastguard Worker self.voidcmd('TYPE I') 436*cda5da8dSAndroid Build Coastguard Worker with self.transfercmd(cmd, rest) as conn: 437*cda5da8dSAndroid Build Coastguard Worker while 1: 438*cda5da8dSAndroid Build Coastguard Worker data = conn.recv(blocksize) 439*cda5da8dSAndroid Build Coastguard Worker if not data: 440*cda5da8dSAndroid Build Coastguard Worker break 441*cda5da8dSAndroid Build Coastguard Worker callback(data) 442*cda5da8dSAndroid Build Coastguard Worker # shutdown ssl layer 443*cda5da8dSAndroid Build Coastguard Worker if _SSLSocket is not None and isinstance(conn, _SSLSocket): 444*cda5da8dSAndroid Build Coastguard Worker conn.unwrap() 445*cda5da8dSAndroid Build Coastguard Worker return self.voidresp() 446*cda5da8dSAndroid Build Coastguard Worker 447*cda5da8dSAndroid Build Coastguard Worker def retrlines(self, cmd, callback = None): 448*cda5da8dSAndroid Build Coastguard Worker """Retrieve data in line mode. A new port is created for you. 449*cda5da8dSAndroid Build Coastguard Worker 450*cda5da8dSAndroid Build Coastguard Worker Args: 451*cda5da8dSAndroid Build Coastguard Worker cmd: A RETR, LIST, or NLST command. 452*cda5da8dSAndroid Build Coastguard Worker callback: An optional single parameter callable that is called 453*cda5da8dSAndroid Build Coastguard Worker for each line with the trailing CRLF stripped. 454*cda5da8dSAndroid Build Coastguard Worker [default: print_line()] 455*cda5da8dSAndroid Build Coastguard Worker 456*cda5da8dSAndroid Build Coastguard Worker Returns: 457*cda5da8dSAndroid Build Coastguard Worker The response code. 458*cda5da8dSAndroid Build Coastguard Worker """ 459*cda5da8dSAndroid Build Coastguard Worker if callback is None: 460*cda5da8dSAndroid Build Coastguard Worker callback = print_line 461*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('TYPE A') 462*cda5da8dSAndroid Build Coastguard Worker with self.transfercmd(cmd) as conn, \ 463*cda5da8dSAndroid Build Coastguard Worker conn.makefile('r', encoding=self.encoding) as fp: 464*cda5da8dSAndroid Build Coastguard Worker while 1: 465*cda5da8dSAndroid Build Coastguard Worker line = fp.readline(self.maxline + 1) 466*cda5da8dSAndroid Build Coastguard Worker if len(line) > self.maxline: 467*cda5da8dSAndroid Build Coastguard Worker raise Error("got more than %d bytes" % self.maxline) 468*cda5da8dSAndroid Build Coastguard Worker if self.debugging > 2: 469*cda5da8dSAndroid Build Coastguard Worker print('*retr*', repr(line)) 470*cda5da8dSAndroid Build Coastguard Worker if not line: 471*cda5da8dSAndroid Build Coastguard Worker break 472*cda5da8dSAndroid Build Coastguard Worker if line[-2:] == CRLF: 473*cda5da8dSAndroid Build Coastguard Worker line = line[:-2] 474*cda5da8dSAndroid Build Coastguard Worker elif line[-1:] == '\n': 475*cda5da8dSAndroid Build Coastguard Worker line = line[:-1] 476*cda5da8dSAndroid Build Coastguard Worker callback(line) 477*cda5da8dSAndroid Build Coastguard Worker # shutdown ssl layer 478*cda5da8dSAndroid Build Coastguard Worker if _SSLSocket is not None and isinstance(conn, _SSLSocket): 479*cda5da8dSAndroid Build Coastguard Worker conn.unwrap() 480*cda5da8dSAndroid Build Coastguard Worker return self.voidresp() 481*cda5da8dSAndroid Build Coastguard Worker 482*cda5da8dSAndroid Build Coastguard Worker def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None): 483*cda5da8dSAndroid Build Coastguard Worker """Store a file in binary mode. A new port is created for you. 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker Args: 486*cda5da8dSAndroid Build Coastguard Worker cmd: A STOR command. 487*cda5da8dSAndroid Build Coastguard Worker fp: A file-like object with a read(num_bytes) method. 488*cda5da8dSAndroid Build Coastguard Worker blocksize: The maximum data size to read from fp and send over 489*cda5da8dSAndroid Build Coastguard Worker the connection at once. [default: 8192] 490*cda5da8dSAndroid Build Coastguard Worker callback: An optional single parameter callable that is called on 491*cda5da8dSAndroid Build Coastguard Worker each block of data after it is sent. [default: None] 492*cda5da8dSAndroid Build Coastguard Worker rest: Passed to transfercmd(). [default: None] 493*cda5da8dSAndroid Build Coastguard Worker 494*cda5da8dSAndroid Build Coastguard Worker Returns: 495*cda5da8dSAndroid Build Coastguard Worker The response code. 496*cda5da8dSAndroid Build Coastguard Worker """ 497*cda5da8dSAndroid Build Coastguard Worker self.voidcmd('TYPE I') 498*cda5da8dSAndroid Build Coastguard Worker with self.transfercmd(cmd, rest) as conn: 499*cda5da8dSAndroid Build Coastguard Worker while 1: 500*cda5da8dSAndroid Build Coastguard Worker buf = fp.read(blocksize) 501*cda5da8dSAndroid Build Coastguard Worker if not buf: 502*cda5da8dSAndroid Build Coastguard Worker break 503*cda5da8dSAndroid Build Coastguard Worker conn.sendall(buf) 504*cda5da8dSAndroid Build Coastguard Worker if callback: 505*cda5da8dSAndroid Build Coastguard Worker callback(buf) 506*cda5da8dSAndroid Build Coastguard Worker # shutdown ssl layer 507*cda5da8dSAndroid Build Coastguard Worker if _SSLSocket is not None and isinstance(conn, _SSLSocket): 508*cda5da8dSAndroid Build Coastguard Worker conn.unwrap() 509*cda5da8dSAndroid Build Coastguard Worker return self.voidresp() 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker def storlines(self, cmd, fp, callback=None): 512*cda5da8dSAndroid Build Coastguard Worker """Store a file in line mode. A new port is created for you. 513*cda5da8dSAndroid Build Coastguard Worker 514*cda5da8dSAndroid Build Coastguard Worker Args: 515*cda5da8dSAndroid Build Coastguard Worker cmd: A STOR command. 516*cda5da8dSAndroid Build Coastguard Worker fp: A file-like object with a readline() method. 517*cda5da8dSAndroid Build Coastguard Worker callback: An optional single parameter callable that is called on 518*cda5da8dSAndroid Build Coastguard Worker each line after it is sent. [default: None] 519*cda5da8dSAndroid Build Coastguard Worker 520*cda5da8dSAndroid Build Coastguard Worker Returns: 521*cda5da8dSAndroid Build Coastguard Worker The response code. 522*cda5da8dSAndroid Build Coastguard Worker """ 523*cda5da8dSAndroid Build Coastguard Worker self.voidcmd('TYPE A') 524*cda5da8dSAndroid Build Coastguard Worker with self.transfercmd(cmd) as conn: 525*cda5da8dSAndroid Build Coastguard Worker while 1: 526*cda5da8dSAndroid Build Coastguard Worker buf = fp.readline(self.maxline + 1) 527*cda5da8dSAndroid Build Coastguard Worker if len(buf) > self.maxline: 528*cda5da8dSAndroid Build Coastguard Worker raise Error("got more than %d bytes" % self.maxline) 529*cda5da8dSAndroid Build Coastguard Worker if not buf: 530*cda5da8dSAndroid Build Coastguard Worker break 531*cda5da8dSAndroid Build Coastguard Worker if buf[-2:] != B_CRLF: 532*cda5da8dSAndroid Build Coastguard Worker if buf[-1] in B_CRLF: buf = buf[:-1] 533*cda5da8dSAndroid Build Coastguard Worker buf = buf + B_CRLF 534*cda5da8dSAndroid Build Coastguard Worker conn.sendall(buf) 535*cda5da8dSAndroid Build Coastguard Worker if callback: 536*cda5da8dSAndroid Build Coastguard Worker callback(buf) 537*cda5da8dSAndroid Build Coastguard Worker # shutdown ssl layer 538*cda5da8dSAndroid Build Coastguard Worker if _SSLSocket is not None and isinstance(conn, _SSLSocket): 539*cda5da8dSAndroid Build Coastguard Worker conn.unwrap() 540*cda5da8dSAndroid Build Coastguard Worker return self.voidresp() 541*cda5da8dSAndroid Build Coastguard Worker 542*cda5da8dSAndroid Build Coastguard Worker def acct(self, password): 543*cda5da8dSAndroid Build Coastguard Worker '''Send new account name.''' 544*cda5da8dSAndroid Build Coastguard Worker cmd = 'ACCT ' + password 545*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd(cmd) 546*cda5da8dSAndroid Build Coastguard Worker 547*cda5da8dSAndroid Build Coastguard Worker def nlst(self, *args): 548*cda5da8dSAndroid Build Coastguard Worker '''Return a list of files in a given directory (default the current).''' 549*cda5da8dSAndroid Build Coastguard Worker cmd = 'NLST' 550*cda5da8dSAndroid Build Coastguard Worker for arg in args: 551*cda5da8dSAndroid Build Coastguard Worker cmd = cmd + (' ' + arg) 552*cda5da8dSAndroid Build Coastguard Worker files = [] 553*cda5da8dSAndroid Build Coastguard Worker self.retrlines(cmd, files.append) 554*cda5da8dSAndroid Build Coastguard Worker return files 555*cda5da8dSAndroid Build Coastguard Worker 556*cda5da8dSAndroid Build Coastguard Worker def dir(self, *args): 557*cda5da8dSAndroid Build Coastguard Worker '''List a directory in long form. 558*cda5da8dSAndroid Build Coastguard Worker By default list current directory to stdout. 559*cda5da8dSAndroid Build Coastguard Worker Optional last argument is callback function; all 560*cda5da8dSAndroid Build Coastguard Worker non-empty arguments before it are concatenated to the 561*cda5da8dSAndroid Build Coastguard Worker LIST command. (This *should* only be used for a pathname.)''' 562*cda5da8dSAndroid Build Coastguard Worker cmd = 'LIST' 563*cda5da8dSAndroid Build Coastguard Worker func = None 564*cda5da8dSAndroid Build Coastguard Worker if args[-1:] and type(args[-1]) != type(''): 565*cda5da8dSAndroid Build Coastguard Worker args, func = args[:-1], args[-1] 566*cda5da8dSAndroid Build Coastguard Worker for arg in args: 567*cda5da8dSAndroid Build Coastguard Worker if arg: 568*cda5da8dSAndroid Build Coastguard Worker cmd = cmd + (' ' + arg) 569*cda5da8dSAndroid Build Coastguard Worker self.retrlines(cmd, func) 570*cda5da8dSAndroid Build Coastguard Worker 571*cda5da8dSAndroid Build Coastguard Worker def mlsd(self, path="", facts=[]): 572*cda5da8dSAndroid Build Coastguard Worker '''List a directory in a standardized format by using MLSD 573*cda5da8dSAndroid Build Coastguard Worker command (RFC-3659). If path is omitted the current directory 574*cda5da8dSAndroid Build Coastguard Worker is assumed. "facts" is a list of strings representing the type 575*cda5da8dSAndroid Build Coastguard Worker of information desired (e.g. ["type", "size", "perm"]). 576*cda5da8dSAndroid Build Coastguard Worker 577*cda5da8dSAndroid Build Coastguard Worker Return a generator object yielding a tuple of two elements 578*cda5da8dSAndroid Build Coastguard Worker for every file found in path. 579*cda5da8dSAndroid Build Coastguard Worker First element is the file name, the second one is a dictionary 580*cda5da8dSAndroid Build Coastguard Worker including a variable number of "facts" depending on the server 581*cda5da8dSAndroid Build Coastguard Worker and whether "facts" argument has been provided. 582*cda5da8dSAndroid Build Coastguard Worker ''' 583*cda5da8dSAndroid Build Coastguard Worker if facts: 584*cda5da8dSAndroid Build Coastguard Worker self.sendcmd("OPTS MLST " + ";".join(facts) + ";") 585*cda5da8dSAndroid Build Coastguard Worker if path: 586*cda5da8dSAndroid Build Coastguard Worker cmd = "MLSD %s" % path 587*cda5da8dSAndroid Build Coastguard Worker else: 588*cda5da8dSAndroid Build Coastguard Worker cmd = "MLSD" 589*cda5da8dSAndroid Build Coastguard Worker lines = [] 590*cda5da8dSAndroid Build Coastguard Worker self.retrlines(cmd, lines.append) 591*cda5da8dSAndroid Build Coastguard Worker for line in lines: 592*cda5da8dSAndroid Build Coastguard Worker facts_found, _, name = line.rstrip(CRLF).partition(' ') 593*cda5da8dSAndroid Build Coastguard Worker entry = {} 594*cda5da8dSAndroid Build Coastguard Worker for fact in facts_found[:-1].split(";"): 595*cda5da8dSAndroid Build Coastguard Worker key, _, value = fact.partition("=") 596*cda5da8dSAndroid Build Coastguard Worker entry[key.lower()] = value 597*cda5da8dSAndroid Build Coastguard Worker yield (name, entry) 598*cda5da8dSAndroid Build Coastguard Worker 599*cda5da8dSAndroid Build Coastguard Worker def rename(self, fromname, toname): 600*cda5da8dSAndroid Build Coastguard Worker '''Rename a file.''' 601*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('RNFR ' + fromname) 602*cda5da8dSAndroid Build Coastguard Worker if resp[0] != '3': 603*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 604*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd('RNTO ' + toname) 605*cda5da8dSAndroid Build Coastguard Worker 606*cda5da8dSAndroid Build Coastguard Worker def delete(self, filename): 607*cda5da8dSAndroid Build Coastguard Worker '''Delete a file.''' 608*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('DELE ' + filename) 609*cda5da8dSAndroid Build Coastguard Worker if resp[:3] in {'250', '200'}: 610*cda5da8dSAndroid Build Coastguard Worker return resp 611*cda5da8dSAndroid Build Coastguard Worker else: 612*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 613*cda5da8dSAndroid Build Coastguard Worker 614*cda5da8dSAndroid Build Coastguard Worker def cwd(self, dirname): 615*cda5da8dSAndroid Build Coastguard Worker '''Change to a directory.''' 616*cda5da8dSAndroid Build Coastguard Worker if dirname == '..': 617*cda5da8dSAndroid Build Coastguard Worker try: 618*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd('CDUP') 619*cda5da8dSAndroid Build Coastguard Worker except error_perm as msg: 620*cda5da8dSAndroid Build Coastguard Worker if msg.args[0][:3] != '500': 621*cda5da8dSAndroid Build Coastguard Worker raise 622*cda5da8dSAndroid Build Coastguard Worker elif dirname == '': 623*cda5da8dSAndroid Build Coastguard Worker dirname = '.' # does nothing, but could return error 624*cda5da8dSAndroid Build Coastguard Worker cmd = 'CWD ' + dirname 625*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd(cmd) 626*cda5da8dSAndroid Build Coastguard Worker 627*cda5da8dSAndroid Build Coastguard Worker def size(self, filename): 628*cda5da8dSAndroid Build Coastguard Worker '''Retrieve the size of a file.''' 629*cda5da8dSAndroid Build Coastguard Worker # The SIZE command is defined in RFC-3659 630*cda5da8dSAndroid Build Coastguard Worker resp = self.sendcmd('SIZE ' + filename) 631*cda5da8dSAndroid Build Coastguard Worker if resp[:3] == '213': 632*cda5da8dSAndroid Build Coastguard Worker s = resp[3:].strip() 633*cda5da8dSAndroid Build Coastguard Worker return int(s) 634*cda5da8dSAndroid Build Coastguard Worker 635*cda5da8dSAndroid Build Coastguard Worker def mkd(self, dirname): 636*cda5da8dSAndroid Build Coastguard Worker '''Make a directory, return its full pathname.''' 637*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('MKD ' + dirname) 638*cda5da8dSAndroid Build Coastguard Worker # fix around non-compliant implementations such as IIS shipped 639*cda5da8dSAndroid Build Coastguard Worker # with Windows server 2003 640*cda5da8dSAndroid Build Coastguard Worker if not resp.startswith('257'): 641*cda5da8dSAndroid Build Coastguard Worker return '' 642*cda5da8dSAndroid Build Coastguard Worker return parse257(resp) 643*cda5da8dSAndroid Build Coastguard Worker 644*cda5da8dSAndroid Build Coastguard Worker def rmd(self, dirname): 645*cda5da8dSAndroid Build Coastguard Worker '''Remove a directory.''' 646*cda5da8dSAndroid Build Coastguard Worker return self.voidcmd('RMD ' + dirname) 647*cda5da8dSAndroid Build Coastguard Worker 648*cda5da8dSAndroid Build Coastguard Worker def pwd(self): 649*cda5da8dSAndroid Build Coastguard Worker '''Return current working directory.''' 650*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('PWD') 651*cda5da8dSAndroid Build Coastguard Worker # fix around non-compliant implementations such as IIS shipped 652*cda5da8dSAndroid Build Coastguard Worker # with Windows server 2003 653*cda5da8dSAndroid Build Coastguard Worker if not resp.startswith('257'): 654*cda5da8dSAndroid Build Coastguard Worker return '' 655*cda5da8dSAndroid Build Coastguard Worker return parse257(resp) 656*cda5da8dSAndroid Build Coastguard Worker 657*cda5da8dSAndroid Build Coastguard Worker def quit(self): 658*cda5da8dSAndroid Build Coastguard Worker '''Quit, and close the connection.''' 659*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('QUIT') 660*cda5da8dSAndroid Build Coastguard Worker self.close() 661*cda5da8dSAndroid Build Coastguard Worker return resp 662*cda5da8dSAndroid Build Coastguard Worker 663*cda5da8dSAndroid Build Coastguard Worker def close(self): 664*cda5da8dSAndroid Build Coastguard Worker '''Close the connection without assuming anything about it.''' 665*cda5da8dSAndroid Build Coastguard Worker try: 666*cda5da8dSAndroid Build Coastguard Worker file = self.file 667*cda5da8dSAndroid Build Coastguard Worker self.file = None 668*cda5da8dSAndroid Build Coastguard Worker if file is not None: 669*cda5da8dSAndroid Build Coastguard Worker file.close() 670*cda5da8dSAndroid Build Coastguard Worker finally: 671*cda5da8dSAndroid Build Coastguard Worker sock = self.sock 672*cda5da8dSAndroid Build Coastguard Worker self.sock = None 673*cda5da8dSAndroid Build Coastguard Worker if sock is not None: 674*cda5da8dSAndroid Build Coastguard Worker sock.close() 675*cda5da8dSAndroid Build Coastguard Worker 676*cda5da8dSAndroid Build Coastguard Workertry: 677*cda5da8dSAndroid Build Coastguard Worker import ssl 678*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 679*cda5da8dSAndroid Build Coastguard Worker _SSLSocket = None 680*cda5da8dSAndroid Build Coastguard Workerelse: 681*cda5da8dSAndroid Build Coastguard Worker _SSLSocket = ssl.SSLSocket 682*cda5da8dSAndroid Build Coastguard Worker 683*cda5da8dSAndroid Build Coastguard Worker class FTP_TLS(FTP): 684*cda5da8dSAndroid Build Coastguard Worker '''A FTP subclass which adds TLS support to FTP as described 685*cda5da8dSAndroid Build Coastguard Worker in RFC-4217. 686*cda5da8dSAndroid Build Coastguard Worker 687*cda5da8dSAndroid Build Coastguard Worker Connect as usual to port 21 implicitly securing the FTP control 688*cda5da8dSAndroid Build Coastguard Worker connection before authenticating. 689*cda5da8dSAndroid Build Coastguard Worker 690*cda5da8dSAndroid Build Coastguard Worker Securing the data connection requires user to explicitly ask 691*cda5da8dSAndroid Build Coastguard Worker for it by calling prot_p() method. 692*cda5da8dSAndroid Build Coastguard Worker 693*cda5da8dSAndroid Build Coastguard Worker Usage example: 694*cda5da8dSAndroid Build Coastguard Worker >>> from ftplib import FTP_TLS 695*cda5da8dSAndroid Build Coastguard Worker >>> ftps = FTP_TLS('ftp.python.org') 696*cda5da8dSAndroid Build Coastguard Worker >>> ftps.login() # login anonymously previously securing control channel 697*cda5da8dSAndroid Build Coastguard Worker '230 Guest login ok, access restrictions apply.' 698*cda5da8dSAndroid Build Coastguard Worker >>> ftps.prot_p() # switch to secure data connection 699*cda5da8dSAndroid Build Coastguard Worker '200 Protection level set to P' 700*cda5da8dSAndroid Build Coastguard Worker >>> ftps.retrlines('LIST') # list directory content securely 701*cda5da8dSAndroid Build Coastguard Worker total 9 702*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 8 root wheel 1024 Jan 3 1994 . 703*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .. 704*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin 705*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc 706*cda5da8dSAndroid Build Coastguard Worker d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming 707*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib 708*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub 709*cda5da8dSAndroid Build Coastguard Worker drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr 710*cda5da8dSAndroid Build Coastguard Worker -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg 711*cda5da8dSAndroid Build Coastguard Worker '226 Transfer complete.' 712*cda5da8dSAndroid Build Coastguard Worker >>> ftps.quit() 713*cda5da8dSAndroid Build Coastguard Worker '221 Goodbye.' 714*cda5da8dSAndroid Build Coastguard Worker >>> 715*cda5da8dSAndroid Build Coastguard Worker ''' 716*cda5da8dSAndroid Build Coastguard Worker ssl_version = ssl.PROTOCOL_TLS_CLIENT 717*cda5da8dSAndroid Build Coastguard Worker 718*cda5da8dSAndroid Build Coastguard Worker def __init__(self, host='', user='', passwd='', acct='', 719*cda5da8dSAndroid Build Coastguard Worker keyfile=None, certfile=None, context=None, 720*cda5da8dSAndroid Build Coastguard Worker timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *, 721*cda5da8dSAndroid Build Coastguard Worker encoding='utf-8'): 722*cda5da8dSAndroid Build Coastguard Worker if context is not None and keyfile is not None: 723*cda5da8dSAndroid Build Coastguard Worker raise ValueError("context and keyfile arguments are mutually " 724*cda5da8dSAndroid Build Coastguard Worker "exclusive") 725*cda5da8dSAndroid Build Coastguard Worker if context is not None and certfile is not None: 726*cda5da8dSAndroid Build Coastguard Worker raise ValueError("context and certfile arguments are mutually " 727*cda5da8dSAndroid Build Coastguard Worker "exclusive") 728*cda5da8dSAndroid Build Coastguard Worker if keyfile is not None or certfile is not None: 729*cda5da8dSAndroid Build Coastguard Worker import warnings 730*cda5da8dSAndroid Build Coastguard Worker warnings.warn("keyfile and certfile are deprecated, use a " 731*cda5da8dSAndroid Build Coastguard Worker "custom context instead", DeprecationWarning, 2) 732*cda5da8dSAndroid Build Coastguard Worker self.keyfile = keyfile 733*cda5da8dSAndroid Build Coastguard Worker self.certfile = certfile 734*cda5da8dSAndroid Build Coastguard Worker if context is None: 735*cda5da8dSAndroid Build Coastguard Worker context = ssl._create_stdlib_context(self.ssl_version, 736*cda5da8dSAndroid Build Coastguard Worker certfile=certfile, 737*cda5da8dSAndroid Build Coastguard Worker keyfile=keyfile) 738*cda5da8dSAndroid Build Coastguard Worker self.context = context 739*cda5da8dSAndroid Build Coastguard Worker self._prot_p = False 740*cda5da8dSAndroid Build Coastguard Worker super().__init__(host, user, passwd, acct, 741*cda5da8dSAndroid Build Coastguard Worker timeout, source_address, encoding=encoding) 742*cda5da8dSAndroid Build Coastguard Worker 743*cda5da8dSAndroid Build Coastguard Worker def login(self, user='', passwd='', acct='', secure=True): 744*cda5da8dSAndroid Build Coastguard Worker if secure and not isinstance(self.sock, ssl.SSLSocket): 745*cda5da8dSAndroid Build Coastguard Worker self.auth() 746*cda5da8dSAndroid Build Coastguard Worker return super().login(user, passwd, acct) 747*cda5da8dSAndroid Build Coastguard Worker 748*cda5da8dSAndroid Build Coastguard Worker def auth(self): 749*cda5da8dSAndroid Build Coastguard Worker '''Set up secure control connection by using TLS/SSL.''' 750*cda5da8dSAndroid Build Coastguard Worker if isinstance(self.sock, ssl.SSLSocket): 751*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Already using TLS") 752*cda5da8dSAndroid Build Coastguard Worker if self.ssl_version >= ssl.PROTOCOL_TLS: 753*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('AUTH TLS') 754*cda5da8dSAndroid Build Coastguard Worker else: 755*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('AUTH SSL') 756*cda5da8dSAndroid Build Coastguard Worker self.sock = self.context.wrap_socket(self.sock, server_hostname=self.host) 757*cda5da8dSAndroid Build Coastguard Worker self.file = self.sock.makefile(mode='r', encoding=self.encoding) 758*cda5da8dSAndroid Build Coastguard Worker return resp 759*cda5da8dSAndroid Build Coastguard Worker 760*cda5da8dSAndroid Build Coastguard Worker def ccc(self): 761*cda5da8dSAndroid Build Coastguard Worker '''Switch back to a clear-text control connection.''' 762*cda5da8dSAndroid Build Coastguard Worker if not isinstance(self.sock, ssl.SSLSocket): 763*cda5da8dSAndroid Build Coastguard Worker raise ValueError("not using TLS") 764*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('CCC') 765*cda5da8dSAndroid Build Coastguard Worker self.sock = self.sock.unwrap() 766*cda5da8dSAndroid Build Coastguard Worker return resp 767*cda5da8dSAndroid Build Coastguard Worker 768*cda5da8dSAndroid Build Coastguard Worker def prot_p(self): 769*cda5da8dSAndroid Build Coastguard Worker '''Set up secure data connection.''' 770*cda5da8dSAndroid Build Coastguard Worker # PROT defines whether or not the data channel is to be protected. 771*cda5da8dSAndroid Build Coastguard Worker # Though RFC-2228 defines four possible protection levels, 772*cda5da8dSAndroid Build Coastguard Worker # RFC-4217 only recommends two, Clear and Private. 773*cda5da8dSAndroid Build Coastguard Worker # Clear (PROT C) means that no security is to be used on the 774*cda5da8dSAndroid Build Coastguard Worker # data-channel, Private (PROT P) means that the data-channel 775*cda5da8dSAndroid Build Coastguard Worker # should be protected by TLS. 776*cda5da8dSAndroid Build Coastguard Worker # PBSZ command MUST still be issued, but must have a parameter of 777*cda5da8dSAndroid Build Coastguard Worker # '0' to indicate that no buffering is taking place and the data 778*cda5da8dSAndroid Build Coastguard Worker # connection should not be encapsulated. 779*cda5da8dSAndroid Build Coastguard Worker self.voidcmd('PBSZ 0') 780*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('PROT P') 781*cda5da8dSAndroid Build Coastguard Worker self._prot_p = True 782*cda5da8dSAndroid Build Coastguard Worker return resp 783*cda5da8dSAndroid Build Coastguard Worker 784*cda5da8dSAndroid Build Coastguard Worker def prot_c(self): 785*cda5da8dSAndroid Build Coastguard Worker '''Set up clear text data connection.''' 786*cda5da8dSAndroid Build Coastguard Worker resp = self.voidcmd('PROT C') 787*cda5da8dSAndroid Build Coastguard Worker self._prot_p = False 788*cda5da8dSAndroid Build Coastguard Worker return resp 789*cda5da8dSAndroid Build Coastguard Worker 790*cda5da8dSAndroid Build Coastguard Worker # --- Overridden FTP methods 791*cda5da8dSAndroid Build Coastguard Worker 792*cda5da8dSAndroid Build Coastguard Worker def ntransfercmd(self, cmd, rest=None): 793*cda5da8dSAndroid Build Coastguard Worker conn, size = super().ntransfercmd(cmd, rest) 794*cda5da8dSAndroid Build Coastguard Worker if self._prot_p: 795*cda5da8dSAndroid Build Coastguard Worker conn = self.context.wrap_socket(conn, 796*cda5da8dSAndroid Build Coastguard Worker server_hostname=self.host) 797*cda5da8dSAndroid Build Coastguard Worker return conn, size 798*cda5da8dSAndroid Build Coastguard Worker 799*cda5da8dSAndroid Build Coastguard Worker def abort(self): 800*cda5da8dSAndroid Build Coastguard Worker # overridden as we can't pass MSG_OOB flag to sendall() 801*cda5da8dSAndroid Build Coastguard Worker line = b'ABOR' + B_CRLF 802*cda5da8dSAndroid Build Coastguard Worker self.sock.sendall(line) 803*cda5da8dSAndroid Build Coastguard Worker resp = self.getmultiline() 804*cda5da8dSAndroid Build Coastguard Worker if resp[:3] not in {'426', '225', '226'}: 805*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 806*cda5da8dSAndroid Build Coastguard Worker return resp 807*cda5da8dSAndroid Build Coastguard Worker 808*cda5da8dSAndroid Build Coastguard Worker __all__.append('FTP_TLS') 809*cda5da8dSAndroid Build Coastguard Worker all_errors = (Error, OSError, EOFError, ssl.SSLError) 810*cda5da8dSAndroid Build Coastguard Worker 811*cda5da8dSAndroid Build Coastguard Worker 812*cda5da8dSAndroid Build Coastguard Worker_150_re = None 813*cda5da8dSAndroid Build Coastguard Worker 814*cda5da8dSAndroid Build Coastguard Workerdef parse150(resp): 815*cda5da8dSAndroid Build Coastguard Worker '''Parse the '150' response for a RETR request. 816*cda5da8dSAndroid Build Coastguard Worker Returns the expected transfer size or None; size is not guaranteed to 817*cda5da8dSAndroid Build Coastguard Worker be present in the 150 message. 818*cda5da8dSAndroid Build Coastguard Worker ''' 819*cda5da8dSAndroid Build Coastguard Worker if resp[:3] != '150': 820*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 821*cda5da8dSAndroid Build Coastguard Worker global _150_re 822*cda5da8dSAndroid Build Coastguard Worker if _150_re is None: 823*cda5da8dSAndroid Build Coastguard Worker import re 824*cda5da8dSAndroid Build Coastguard Worker _150_re = re.compile( 825*cda5da8dSAndroid Build Coastguard Worker r"150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII) 826*cda5da8dSAndroid Build Coastguard Worker m = _150_re.match(resp) 827*cda5da8dSAndroid Build Coastguard Worker if not m: 828*cda5da8dSAndroid Build Coastguard Worker return None 829*cda5da8dSAndroid Build Coastguard Worker return int(m.group(1)) 830*cda5da8dSAndroid Build Coastguard Worker 831*cda5da8dSAndroid Build Coastguard Worker 832*cda5da8dSAndroid Build Coastguard Worker_227_re = None 833*cda5da8dSAndroid Build Coastguard Worker 834*cda5da8dSAndroid Build Coastguard Workerdef parse227(resp): 835*cda5da8dSAndroid Build Coastguard Worker '''Parse the '227' response for a PASV request. 836*cda5da8dSAndroid Build Coastguard Worker Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' 837*cda5da8dSAndroid Build Coastguard Worker Return ('host.addr.as.numbers', port#) tuple.''' 838*cda5da8dSAndroid Build Coastguard Worker if resp[:3] != '227': 839*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 840*cda5da8dSAndroid Build Coastguard Worker global _227_re 841*cda5da8dSAndroid Build Coastguard Worker if _227_re is None: 842*cda5da8dSAndroid Build Coastguard Worker import re 843*cda5da8dSAndroid Build Coastguard Worker _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re.ASCII) 844*cda5da8dSAndroid Build Coastguard Worker m = _227_re.search(resp) 845*cda5da8dSAndroid Build Coastguard Worker if not m: 846*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 847*cda5da8dSAndroid Build Coastguard Worker numbers = m.groups() 848*cda5da8dSAndroid Build Coastguard Worker host = '.'.join(numbers[:4]) 849*cda5da8dSAndroid Build Coastguard Worker port = (int(numbers[4]) << 8) + int(numbers[5]) 850*cda5da8dSAndroid Build Coastguard Worker return host, port 851*cda5da8dSAndroid Build Coastguard Worker 852*cda5da8dSAndroid Build Coastguard Worker 853*cda5da8dSAndroid Build Coastguard Workerdef parse229(resp, peer): 854*cda5da8dSAndroid Build Coastguard Worker '''Parse the '229' response for an EPSV request. 855*cda5da8dSAndroid Build Coastguard Worker Raises error_proto if it does not contain '(|||port|)' 856*cda5da8dSAndroid Build Coastguard Worker Return ('host.addr.as.numbers', port#) tuple.''' 857*cda5da8dSAndroid Build Coastguard Worker if resp[:3] != '229': 858*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 859*cda5da8dSAndroid Build Coastguard Worker left = resp.find('(') 860*cda5da8dSAndroid Build Coastguard Worker if left < 0: raise error_proto(resp) 861*cda5da8dSAndroid Build Coastguard Worker right = resp.find(')', left + 1) 862*cda5da8dSAndroid Build Coastguard Worker if right < 0: 863*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) # should contain '(|||port|)' 864*cda5da8dSAndroid Build Coastguard Worker if resp[left + 1] != resp[right - 1]: 865*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 866*cda5da8dSAndroid Build Coastguard Worker parts = resp[left + 1:right].split(resp[left+1]) 867*cda5da8dSAndroid Build Coastguard Worker if len(parts) != 5: 868*cda5da8dSAndroid Build Coastguard Worker raise error_proto(resp) 869*cda5da8dSAndroid Build Coastguard Worker host = peer[0] 870*cda5da8dSAndroid Build Coastguard Worker port = int(parts[3]) 871*cda5da8dSAndroid Build Coastguard Worker return host, port 872*cda5da8dSAndroid Build Coastguard Worker 873*cda5da8dSAndroid Build Coastguard Worker 874*cda5da8dSAndroid Build Coastguard Workerdef parse257(resp): 875*cda5da8dSAndroid Build Coastguard Worker '''Parse the '257' response for a MKD or PWD request. 876*cda5da8dSAndroid Build Coastguard Worker This is a response to a MKD or PWD request: a directory name. 877*cda5da8dSAndroid Build Coastguard Worker Returns the directoryname in the 257 reply.''' 878*cda5da8dSAndroid Build Coastguard Worker if resp[:3] != '257': 879*cda5da8dSAndroid Build Coastguard Worker raise error_reply(resp) 880*cda5da8dSAndroid Build Coastguard Worker if resp[3:5] != ' "': 881*cda5da8dSAndroid Build Coastguard Worker return '' # Not compliant to RFC 959, but UNIX ftpd does this 882*cda5da8dSAndroid Build Coastguard Worker dirname = '' 883*cda5da8dSAndroid Build Coastguard Worker i = 5 884*cda5da8dSAndroid Build Coastguard Worker n = len(resp) 885*cda5da8dSAndroid Build Coastguard Worker while i < n: 886*cda5da8dSAndroid Build Coastguard Worker c = resp[i] 887*cda5da8dSAndroid Build Coastguard Worker i = i+1 888*cda5da8dSAndroid Build Coastguard Worker if c == '"': 889*cda5da8dSAndroid Build Coastguard Worker if i >= n or resp[i] != '"': 890*cda5da8dSAndroid Build Coastguard Worker break 891*cda5da8dSAndroid Build Coastguard Worker i = i+1 892*cda5da8dSAndroid Build Coastguard Worker dirname = dirname + c 893*cda5da8dSAndroid Build Coastguard Worker return dirname 894*cda5da8dSAndroid Build Coastguard Worker 895*cda5da8dSAndroid Build Coastguard Worker 896*cda5da8dSAndroid Build Coastguard Workerdef print_line(line): 897*cda5da8dSAndroid Build Coastguard Worker '''Default retrlines callback to print a line.''' 898*cda5da8dSAndroid Build Coastguard Worker print(line) 899*cda5da8dSAndroid Build Coastguard Worker 900*cda5da8dSAndroid Build Coastguard Worker 901*cda5da8dSAndroid Build Coastguard Workerdef ftpcp(source, sourcename, target, targetname = '', type = 'I'): 902*cda5da8dSAndroid Build Coastguard Worker '''Copy file from one FTP-instance to another.''' 903*cda5da8dSAndroid Build Coastguard Worker if not targetname: 904*cda5da8dSAndroid Build Coastguard Worker targetname = sourcename 905*cda5da8dSAndroid Build Coastguard Worker type = 'TYPE ' + type 906*cda5da8dSAndroid Build Coastguard Worker source.voidcmd(type) 907*cda5da8dSAndroid Build Coastguard Worker target.voidcmd(type) 908*cda5da8dSAndroid Build Coastguard Worker sourcehost, sourceport = parse227(source.sendcmd('PASV')) 909*cda5da8dSAndroid Build Coastguard Worker target.sendport(sourcehost, sourceport) 910*cda5da8dSAndroid Build Coastguard Worker # RFC 959: the user must "listen" [...] BEFORE sending the 911*cda5da8dSAndroid Build Coastguard Worker # transfer request. 912*cda5da8dSAndroid Build Coastguard Worker # So: STOR before RETR, because here the target is a "user". 913*cda5da8dSAndroid Build Coastguard Worker treply = target.sendcmd('STOR ' + targetname) 914*cda5da8dSAndroid Build Coastguard Worker if treply[:3] not in {'125', '150'}: 915*cda5da8dSAndroid Build Coastguard Worker raise error_proto # RFC 959 916*cda5da8dSAndroid Build Coastguard Worker sreply = source.sendcmd('RETR ' + sourcename) 917*cda5da8dSAndroid Build Coastguard Worker if sreply[:3] not in {'125', '150'}: 918*cda5da8dSAndroid Build Coastguard Worker raise error_proto # RFC 959 919*cda5da8dSAndroid Build Coastguard Worker source.voidresp() 920*cda5da8dSAndroid Build Coastguard Worker target.voidresp() 921*cda5da8dSAndroid Build Coastguard Worker 922*cda5da8dSAndroid Build Coastguard Worker 923*cda5da8dSAndroid Build Coastguard Workerdef test(): 924*cda5da8dSAndroid Build Coastguard Worker '''Test program. 925*cda5da8dSAndroid Build Coastguard Worker Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ... 926*cda5da8dSAndroid Build Coastguard Worker 927*cda5da8dSAndroid Build Coastguard Worker -d dir 928*cda5da8dSAndroid Build Coastguard Worker -l list 929*cda5da8dSAndroid Build Coastguard Worker -p password 930*cda5da8dSAndroid Build Coastguard Worker ''' 931*cda5da8dSAndroid Build Coastguard Worker 932*cda5da8dSAndroid Build Coastguard Worker if len(sys.argv) < 2: 933*cda5da8dSAndroid Build Coastguard Worker print(test.__doc__) 934*cda5da8dSAndroid Build Coastguard Worker sys.exit(0) 935*cda5da8dSAndroid Build Coastguard Worker 936*cda5da8dSAndroid Build Coastguard Worker import netrc 937*cda5da8dSAndroid Build Coastguard Worker 938*cda5da8dSAndroid Build Coastguard Worker debugging = 0 939*cda5da8dSAndroid Build Coastguard Worker rcfile = None 940*cda5da8dSAndroid Build Coastguard Worker while sys.argv[1] == '-d': 941*cda5da8dSAndroid Build Coastguard Worker debugging = debugging+1 942*cda5da8dSAndroid Build Coastguard Worker del sys.argv[1] 943*cda5da8dSAndroid Build Coastguard Worker if sys.argv[1][:2] == '-r': 944*cda5da8dSAndroid Build Coastguard Worker # get name of alternate ~/.netrc file: 945*cda5da8dSAndroid Build Coastguard Worker rcfile = sys.argv[1][2:] 946*cda5da8dSAndroid Build Coastguard Worker del sys.argv[1] 947*cda5da8dSAndroid Build Coastguard Worker host = sys.argv[1] 948*cda5da8dSAndroid Build Coastguard Worker ftp = FTP(host) 949*cda5da8dSAndroid Build Coastguard Worker ftp.set_debuglevel(debugging) 950*cda5da8dSAndroid Build Coastguard Worker userid = passwd = acct = '' 951*cda5da8dSAndroid Build Coastguard Worker try: 952*cda5da8dSAndroid Build Coastguard Worker netrcobj = netrc.netrc(rcfile) 953*cda5da8dSAndroid Build Coastguard Worker except OSError: 954*cda5da8dSAndroid Build Coastguard Worker if rcfile is not None: 955*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write("Could not open account file" 956*cda5da8dSAndroid Build Coastguard Worker " -- using anonymous login.") 957*cda5da8dSAndroid Build Coastguard Worker else: 958*cda5da8dSAndroid Build Coastguard Worker try: 959*cda5da8dSAndroid Build Coastguard Worker userid, acct, passwd = netrcobj.authenticators(host) 960*cda5da8dSAndroid Build Coastguard Worker except KeyError: 961*cda5da8dSAndroid Build Coastguard Worker # no account for host 962*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write( 963*cda5da8dSAndroid Build Coastguard Worker "No account -- using anonymous login.") 964*cda5da8dSAndroid Build Coastguard Worker ftp.login(userid, passwd, acct) 965*cda5da8dSAndroid Build Coastguard Worker for file in sys.argv[2:]: 966*cda5da8dSAndroid Build Coastguard Worker if file[:2] == '-l': 967*cda5da8dSAndroid Build Coastguard Worker ftp.dir(file[2:]) 968*cda5da8dSAndroid Build Coastguard Worker elif file[:2] == '-d': 969*cda5da8dSAndroid Build Coastguard Worker cmd = 'CWD' 970*cda5da8dSAndroid Build Coastguard Worker if file[2:]: cmd = cmd + ' ' + file[2:] 971*cda5da8dSAndroid Build Coastguard Worker resp = ftp.sendcmd(cmd) 972*cda5da8dSAndroid Build Coastguard Worker elif file == '-p': 973*cda5da8dSAndroid Build Coastguard Worker ftp.set_pasv(not ftp.passiveserver) 974*cda5da8dSAndroid Build Coastguard Worker else: 975*cda5da8dSAndroid Build Coastguard Worker ftp.retrbinary('RETR ' + file, \ 976*cda5da8dSAndroid Build Coastguard Worker sys.stdout.write, 1024) 977*cda5da8dSAndroid Build Coastguard Worker ftp.quit() 978*cda5da8dSAndroid Build Coastguard Worker 979*cda5da8dSAndroid Build Coastguard Worker 980*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 981*cda5da8dSAndroid Build Coastguard Worker test() 982