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