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