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