Home | History | Annotate | Download | only in Lib
      1 """An FTP client class and some helper functions.
      2 
      3 Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
      4 
      5 Example:
      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
     12 total 9
     13 drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
     14 drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
     15 drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
     16 drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
     17 d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
     18 drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
     19 drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
     20 drwxr-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 
     27 A nice test that reveals some of the network dialogue would be:
     28 python 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 
     39 import os
     40 import sys
     41 
     42 # Import SOCKS module if it exists, else standard socket module socket
     43 try:
     44     import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket
     45     from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn
     46 except ImportError:
     47     import socket
     48 from socket import _GLOBAL_DEFAULT_TIMEOUT
     49 
     50 __all__ = ["FTP","Netrc"]
     51 
     52 # Magic number from <socket.h>
     53 MSG_OOB = 0x1                           # Process data out of band
     54 
     55 
     56 # The standard FTP server control port
     57 FTP_PORT = 21
     58 # The sizehint parameter passed to readline() calls
     59 MAXLINE = 8192
     60 
     61 
     62 # Exception raised when an error or invalid response is received
     63 class Error(Exception): pass
     64 class error_reply(Error): pass          # unexpected [123]xx reply
     65 class error_temp(Error): pass           # 4xx errors
     66 class error_perm(Error): pass           # 5xx errors
     67 class 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
     72 all_errors = (Error, IOError, EOFError)
     73 
     74 
     75 # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
     76 CRLF = '\r\n'
     77 
     78 # The class itself
     79 class 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 
    608 try:
    609     import ssl
    610 except ImportError:
    611     pass
    612 else:
    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 
    801 def 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 
    824 def 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 
    844 def 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 
    866 def 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 
    889 def print_line(line):
    890     '''Default retrlines callback to print a line.'''
    891     print line
    892 
    893 
    894 def 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 
    913 class 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 
   1022 def 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 
   1077 if __name__ == '__main__':
   1078     test()
   1079