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