14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""An FTP client class and some helper functions.
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmBased on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmExample:
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> from ftplib import FTP
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp = FTP('ftp.python.org') # connect to host, default port
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'230 Guest login ok, access restrictions apply.'
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.retrlines('LIST') # list directory contents
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtotal 9
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmd-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdrwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'226 Transfer complete.'
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>> ftp.quit()
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'221 Goodbye.'
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm>>>
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmA nice test that reveals some of the network dialogue would be:
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmpython ftplib.py -d localhost -l -p -l
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm#
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Changes and improvements suggested by Steve Majewski.
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Jack to work on the mac.
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Siebren to support docstrings and PASV.
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Phil Schwartz to add storbinary and storlines callbacks.
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Modified by Giampaolo Rodola' to add TLS support.
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm#
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Import SOCKS module if it exists, else standard socket module socket
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry:
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError:
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    import socket
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom socket import _GLOBAL_DEFAULT_TIMEOUT
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["FTP","Netrc"]
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Magic number from <socket.h>
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmMSG_OOB = 0x1                           # Process data out of band
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# The standard FTP server control port
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmFTP_PORT = 21
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Exception raised when an error or invalid response is received
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass Error(Exception): pass
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_reply(Error): pass          # unexpected [123]xx reply
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_temp(Error): pass           # 4xx errors
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_perm(Error): pass           # 5xx errors
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass error_proto(Error): pass          # response does not begin with [1-5]
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# All exceptions (hopefully) that may be raised here and that aren't
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# (always) programming errors on our side
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmall_errors = (Error, IOError, EOFError)
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCRLF = '\r\n'
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# The class itself
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass FTP:
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''An FTP client class.
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    To create a connection, call the class using these arguments:
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            host, user, passwd, acct, timeout
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The first four arguments are all strings, and have default value ''.
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    timeout must be numeric and defaults to None if not passed,
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    meaning that no timeout will be set on any ftp socket(s)
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    If a timeout is passed, then this is now the default timeout for all ftp
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    socket operations for this instance.
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Then use self.connect() with optional host and port argument.
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    To download a file, use ftp.retrlines('RETR ' + filename),
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    or ftp.retrbinary() with slightly different arguments.
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    To upload a file, use ftp.storlines() or ftp.storbinary(),
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    which have an open file as argument (see their definitions
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    below for details).
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The download/upload functions first issue appropriate TYPE
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    and PORT or PASV commands.
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm'''
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    debugging = 0
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    host = ''
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    port = FTP_PORT
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    sock = None
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    file = None
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    welcome = None
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    passiveserver = 1
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Initialization method (called by class instantiation).
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Initialize host to localhost, port to standard ftp port
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Optional arguments are host (for connect()),
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # and user, passwd, acct (for login())
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, host='', user='', passwd='', acct='',
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                 timeout=_GLOBAL_DEFAULT_TIMEOUT):
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.timeout = timeout
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if host:
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.connect(host)
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if user:
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.login(user, passwd, acct)
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def connect(self, host='', port=0, timeout=-999):
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Connect to host.  Arguments are:
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm         - host: hostname to connect to (string, default previous host)
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm         - port: port to connect to (integer, default previous port)
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if host != '':
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.host = host
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if port > 0:
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.port = port
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if timeout != -999:
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.timeout = timeout
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.sock = socket.create_connection((self.host, self.port), self.timeout)
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.af = self.sock.family
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.file = self.sock.makefile('rb')
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.welcome = self.getresp()
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.welcome
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def getwelcome(self):
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Get the welcome message from the server.
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        (this is read and squirreled away by connect())'''
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging:
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            print '*welcome*', self.sanitize(self.welcome)
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.welcome
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def set_debuglevel(self, level):
1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Set the debugging level.
1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        The required argument level means:
1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        0: no debugging output (default)
1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        1: print commands and responses but not body text etc.
1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        2: also print raw lines read and sent before stripping CR/LF'''
1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.debugging = level
1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    debug = set_debuglevel
1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def set_pasv(self, val):
1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Use passive or active mode for data transfers.
1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        With a false argument, use the normal PORT mode,
1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        With a true argument, use the PASV command.'''
1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.passiveserver = val
1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: "sanitize" a string for printing
1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def sanitize(self, s):
1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if s[:5] == 'pass ' or s[:5] == 'PASS ':
1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            i = len(s)
1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            while i > 5 and s[i-1] in '\r\n':
1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                i = i-1
1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            s = s[:5] + '*'*(i-5) + s[i:]
1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return repr(s)
1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: send one line to the server, appending CRLF
1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def putline(self, line):
1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line = line + CRLF
1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging > 1: print '*put*', self.sanitize(line)
1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.sock.sendall(line)
1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: send one command to the server (through putline())
1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def putcmd(self, line):
1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging: print '*cmd*', self.sanitize(line)
1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.putline(line)
1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: return one line from the server, stripping CRLF.
1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Raise EOFError if the connection is closed
1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def getline(self):
1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line = self.file.readline()
1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging > 1:
1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            print '*get*', self.sanitize(line)
1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not line: raise EOFError
1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if line[-2:] == CRLF: line = line[:-2]
1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif line[-1:] in CRLF: line = line[:-1]
1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return line
1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: get a response from the server, which may possibly
1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # consist of multiple lines.  Return a single string with no
1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # trailing CRLF.  If the response consists of multiple lines,
1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # these are separated by '\n' characters in the string
1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def getmultiline(self):
1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line = self.getline()
1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if line[3:4] == '-':
1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            code = line[:3]
1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            while 1:
2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                nextline = self.getline()
2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                line = line + ('\n' + nextline)
2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if nextline[:3] == code and \
2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        nextline[3:4] != '-':
2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    break
2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return line
2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Internal: get a response from the server.
2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Raise various errors if the response indicates an error
2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def getresp(self):
2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.getmultiline()
2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging: print '*resp*', self.sanitize(resp)
2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.lastresp = resp[:3]
2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        c = resp[:1]
2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if c in ('1', '2', '3'):
2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return resp
2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if c == '4':
2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_temp, resp
2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if c == '5':
2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_perm, resp
2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_proto, resp
2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def voidresp(self):
2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Expect a response beginning with '2'."""
2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.getresp()
2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[:1] != '2':
2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_reply, resp
2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return resp
2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def abort(self):
2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Abort a file transfer.  Uses out-of-band data.
2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        This does not follow the procedure from the RFC to send Telnet
2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        IP and Synch; that doesn't seem to work with the servers I've
2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        tried.  Instead, just send the ABOR command as OOB data.'''
2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line = 'ABOR' + CRLF
2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.debugging > 1: print '*put urgent*', self.sanitize(line)
2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.sock.sendall(line, MSG_OOB)
2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.getmultiline()
2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[:3] not in ('426', '225', '226'):
2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_proto, resp
2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def sendcmd(self, cmd):
2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Send a command and return the response.'''
2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.putcmd(cmd)
2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.getresp()
2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def voidcmd(self, cmd):
2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Send a command and expect a response beginning with '2'."""
2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.putcmd(cmd)
2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidresp()
2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def sendport(self, host, port):
2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Send a PORT command with the current host and the given
2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        port number.
2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''
2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        hbytes = host.split('.')
2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pbytes = [repr(port//256), repr(port%256)]
2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        bytes = hbytes + pbytes
2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'PORT ' + ','.join(bytes)
2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd(cmd)
2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def sendeprt(self, host, port):
2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Send a EPRT command with the current host and the given port number.'''
2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        af = 0
2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.af == socket.AF_INET:
2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            af = 1
2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.af == socket.AF_INET6:
2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            af = 2
2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if af == 0:
2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_proto, 'unsupported address family'
2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fields = ['', repr(af), host, repr(port), '']
2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'EPRT ' + '|'.join(fields)
2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd(cmd)
2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def makeport(self):
2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Create a new socket and send a PORT command for it.'''
2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        msg = "getaddrinfo returns an empty list"
2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sock = None
2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            af, socktype, proto, canonname, sa = res
2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sock = socket.socket(af, socktype, proto)
2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sock.bind(sa)
2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except socket.error, msg:
2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if sock:
2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    sock.close()
2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sock = None
2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                continue
2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            break
2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not sock:
2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise socket.error, msg
2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sock.listen(1)
2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        port = sock.getsockname()[1] # Get proper port
2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        host = self.sock.getsockname()[0] # Get proper host
2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.af == socket.AF_INET:
2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.sendport(host, port)
2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.sendeprt(host, port)
2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            sock.settimeout(self.timeout)
3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return sock
3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def makepasv(self):
3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.af == socket.AF_INET:
3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            host, port = parse227(self.sendcmd('PASV'))
3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return host, port
3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def ntransfercmd(self, cmd, rest=None):
3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Initiate a transfer over the data connection.
3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        If the transfer is active, send a port command and the
3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        transfer command, and accept the connection.  If the server is
3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        passive, send a pasv command, connect to it, and start the
3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        transfer command.  Either way, return the socket for the
3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        connection and the expected size of the transfer.  The
3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        expected size may be None if it could not be determined.
3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Optional `rest' argument can be a string that is sent as the
3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        argument to a REST command.  This is essentially a server
3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        marker used to tell the server to skip over any data up to the
3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        given marker.
3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        size = None
3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.passiveserver:
3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            host, port = self.makepasv()
3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn = socket.create_connection((host, port), self.timeout)
3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if rest is not None:
3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.sendcmd("REST %s" % rest)
3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.sendcmd(cmd)
3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # Some servers apparently send a 200 reply to
3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # a LIST or STOR command, before the 150 reply
3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # (and way before the 226 reply). This seems to
3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # be in violation of the protocol (which only allows
3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # 1xx or error messages for LIST), so we just discard
3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # this response.
3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if resp[0] == '2':
3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                resp = self.getresp()
3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if resp[0] != '1':
3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise error_reply, resp
3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            sock = self.makeport()
3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if rest is not None:
3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.sendcmd("REST %s" % rest)
3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.sendcmd(cmd)
3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # See above.
3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if resp[0] == '2':
3484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                resp = self.getresp()
3494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if resp[0] != '1':
3504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise error_reply, resp
3514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn, sockaddr = sock.accept()
3524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
3534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn.settimeout(self.timeout)
3544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[:3] == '150':
3554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # this is conditional in case we received a 125
3564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            size = parse150(resp)
3574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return conn, size
3584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def transfercmd(self, cmd, rest=None):
3604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Like ntransfercmd() but returns only the socket."""
3614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.ntransfercmd(cmd, rest)[0]
3624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def login(self, user = '', passwd = '', acct = ''):
3644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Login, default anonymous.'''
3654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not user: user = 'anonymous'
3664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not passwd: passwd = ''
3674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not acct: acct = ''
3684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if user == 'anonymous' and passwd in ('', '-'):
3694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # If there is no anonymous ftp password specified
3704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # then we'll just use anonymous@
3714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # We don't send any other thing because:
3724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # - We want to remain anonymous
3734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # - We want to stop SPAM
3744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # - We don't want to let ftp sites to discriminate by the user,
3754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            #   host or country.
3764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            passwd = passwd + 'anonymous@'
3774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('USER ' + user)
3784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
3794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
3804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[0] != '2':
3814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_reply, resp
3824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return resp
3834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
3854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Retrieve data in binary mode.  A new port is created for you.
3864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Args:
3884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          cmd: A RETR command.
3894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          callback: A single parameter callable to be called on each
3904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    block of data read.
3914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          blocksize: The maximum number of bytes to read from the
3924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                     socket at one time.  [default: 8192]
3934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          rest: Passed to transfercmd().  [default: None]
3944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Returns:
3964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          The response code.
3974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
3984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.voidcmd('TYPE I')
3994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn = self.transfercmd(cmd, rest)
4004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while 1:
4014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            data = conn.recv(blocksize)
4024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not data:
4034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
4044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            callback(data)
4054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn.close()
4064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidresp()
4074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def retrlines(self, cmd, callback = None):
4094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Retrieve data in line mode.  A new port is created for you.
4104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Args:
4124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          cmd: A RETR, LIST, NLST, or MLSD command.
4134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          callback: An optional single parameter callable that is called
4144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    for each line with the trailing CRLF stripped.
4154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    [default: print_line()]
4164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Returns:
4184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          The response code.
4194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
4204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if callback is None: callback = print_line
4214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('TYPE A')
4224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn = self.transfercmd(cmd)
4234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fp = conn.makefile('rb')
4244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while 1:
4254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            line = fp.readline()
4264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self.debugging > 2: print '*retr*', repr(line)
4274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not line:
4284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
4294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if line[-2:] == CRLF:
4304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                line = line[:-2]
4314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            elif line[-1:] == '\n':
4324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                line = line[:-1]
4334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            callback(line)
4344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fp.close()
4354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn.close()
4364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidresp()
4374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
4394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Store a file in binary mode.  A new port is created for you.
4404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Args:
4424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          cmd: A STOR command.
4434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          fp: A file-like object with a read(num_bytes) method.
4444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          blocksize: The maximum data size to read from fp and send over
4454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                     the connection at once.  [default: 8192]
4464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          callback: An optional single parameter callable that is called on
4474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    on each block of data after it is sent.  [default: None]
4484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          rest: Passed to transfercmd().  [default: None]
4494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Returns:
4514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          The response code.
4524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
4534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.voidcmd('TYPE I')
4544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn = self.transfercmd(cmd, rest)
4554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while 1:
4564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            buf = fp.read(blocksize)
4574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not buf: break
4584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn.sendall(buf)
4594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if callback: callback(buf)
4604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn.close()
4614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidresp()
4624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def storlines(self, cmd, fp, callback=None):
4644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Store a file in line mode.  A new port is created for you.
4654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Args:
4674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          cmd: A STOR command.
4684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          fp: A file-like object with a readline() method.
4694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          callback: An optional single parameter callable that is called on
4704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    on each line after it is sent.  [default: None]
4714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Returns:
4734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm          The response code.
4744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
4754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.voidcmd('TYPE A')
4764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn = self.transfercmd(cmd)
4774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while 1:
4784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            buf = fp.readline()
4794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not buf: break
4804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if buf[-2:] != CRLF:
4814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if buf[-1] in CRLF: buf = buf[:-1]
4824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                buf = buf + CRLF
4834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn.sendall(buf)
4844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if callback: callback(buf)
4854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        conn.close()
4864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidresp()
4874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def acct(self, password):
4894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Send new account name.'''
4904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'ACCT ' + password
4914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd(cmd)
4924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def nlst(self, *args):
4944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Return a list of files in a given directory (default the current).'''
4954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'NLST'
4964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for arg in args:
4974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            cmd = cmd + (' ' + arg)
4984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        files = []
4994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.retrlines(cmd, files.append)
5004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return files
5014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def dir(self, *args):
5034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''List a directory in long form.
5044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        By default list current directory to stdout.
5054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Optional last argument is callback function; all
5064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        non-empty arguments before it are concatenated to the
5074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        LIST command.  (This *should* only be used for a pathname.)'''
5084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'LIST'
5094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        func = None
5104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if args[-1:] and type(args[-1]) != type(''):
5114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            args, func = args[:-1], args[-1]
5124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for arg in args:
5134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if arg:
5144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                cmd = cmd + (' ' + arg)
5154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.retrlines(cmd, func)
5164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def rename(self, fromname, toname):
5184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Rename a file.'''
5194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('RNFR ' + fromname)
5204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[0] != '3':
5214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_reply, resp
5224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd('RNTO ' + toname)
5234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def delete(self, filename):
5254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Delete a file.'''
5264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('DELE ' + filename)
5274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[:3] in ('250', '200'):
5284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return resp
5294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
5304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise error_reply, resp
5314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def cwd(self, dirname):
5334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Change to a directory.'''
5344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if dirname == '..':
5354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
5364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                return self.voidcmd('CDUP')
5374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except error_perm, msg:
5384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if msg.args[0][:3] != '500':
5394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    raise
5404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif dirname == '':
5414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            dirname = '.'  # does nothing, but could return error
5424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cmd = 'CWD ' + dirname
5434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd(cmd)
5444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def size(self, filename):
5464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Retrieve the size of a file.'''
5474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # The SIZE command is defined in RFC-3659
5484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('SIZE ' + filename)
5494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if resp[:3] == '213':
5504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            s = resp[3:].strip()
5514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
5524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                return int(s)
5534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except (OverflowError, ValueError):
5544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                return long(s)
5554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def mkd(self, dirname):
5574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Make a directory, return its full pathname.'''
5584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('MKD ' + dirname)
5594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return parse257(resp)
5604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def rmd(self, dirname):
5624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Remove a directory.'''
5634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.voidcmd('RMD ' + dirname)
5644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def pwd(self):
5664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Return current working directory.'''
5674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.sendcmd('PWD')
5684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return parse257(resp)
5694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def quit(self):
5714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Quit, and close the connection.'''
5724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        resp = self.voidcmd('QUIT')
5734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.close()
5744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return resp
5754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def close(self):
5774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''Close the connection without assuming anything about it.'''
5784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.file:
5794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.file.close()
5804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.sock.close()
5814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.file = self.sock = None
5824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry:
5854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    import ssl
5864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError:
5874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    pass
5884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmelse:
5894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    class FTP_TLS(FTP):
5904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''A FTP subclass which adds TLS support to FTP as described
5914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        in RFC-4217.
5924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Connect as usual to port 21 implicitly securing the FTP control
5944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        connection before authenticating.
5954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Securing the data connection requires user to explicitly ask
5974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for it by calling prot_p() method.
5984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
5994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Usage example:
6004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> from ftplib import FTP_TLS
6014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> ftps = FTP_TLS('ftp.python.org')
6024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> ftps.login()  # login anonymously previously securing control channel
6034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '230 Guest login ok, access restrictions apply.'
6044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> ftps.prot_p()  # switch to secure data connection
6054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '200 Protection level set to P'
6064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> ftps.retrlines('LIST')  # list directory content securely
6074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        total 9
6084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
6094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
6104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
6114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
6124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
6134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
6144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
6154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr
6164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        -rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg
6174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '226 Transfer complete.'
6184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>> ftps.quit()
6194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '221 Goodbye.'
6204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        >>>
6214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        '''
6224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        ssl_version = ssl.PROTOCOL_TLSv1
6234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
6254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                     certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
6264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.keyfile = keyfile
6274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.certfile = certfile
6284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._prot_p = False
6294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            FTP.__init__(self, host, user, passwd, acct, timeout)
6304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def login(self, user='', passwd='', acct='', secure=True):
6324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if secure and not isinstance(self.sock, ssl.SSLSocket):
6334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.auth()
6344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return FTP.login(self, user, passwd, acct)
6354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def auth(self):
6374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            '''Set up secure control connection by using TLS/SSL.'''
6384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if isinstance(self.sock, ssl.SSLSocket):
6394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise ValueError("Already using TLS")
6404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self.ssl_version == ssl.PROTOCOL_TLSv1:
6414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                resp = self.voidcmd('AUTH TLS')
6424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
6434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                resp = self.voidcmd('AUTH SSL')
6444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile,
6454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                        ssl_version=self.ssl_version)
6464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.file = self.sock.makefile(mode='rb')
6474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return resp
6484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def prot_p(self):
6504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            '''Set up secure data connection.'''
6514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # PROT defines whether or not the data channel is to be protected.
6524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # Though RFC-2228 defines four possible protection levels,
6534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # RFC-4217 only recommends two, Clear and Private.
6544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # Clear (PROT C) means that no security is to be used on the
6554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # data-channel, Private (PROT P) means that the data-channel
6564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # should be protected by TLS.
6574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # PBSZ command MUST still be issued, but must have a parameter of
6584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # '0' to indicate that no buffering is taking place and the data
6594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # connection should not be encapsulated.
6604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.voidcmd('PBSZ 0')
6614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.voidcmd('PROT P')
6624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._prot_p = True
6634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return resp
6644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def prot_c(self):
6664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            '''Set up clear text data connection.'''
6674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.voidcmd('PROT C')
6684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._prot_p = False
6694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return resp
6704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # --- Overridden FTP methods
6724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def ntransfercmd(self, cmd, rest=None):
6744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn, size = FTP.ntransfercmd(self, cmd, rest)
6754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self._prot_p:
6764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn = ssl.wrap_socket(conn, self.keyfile, self.certfile,
6774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                       ssl_version=self.ssl_version)
6784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return conn, size
6794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
6814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.voidcmd('TYPE I')
6824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn = self.transfercmd(cmd, rest)
6834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
6844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                while 1:
6854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    data = conn.recv(blocksize)
6864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if not data:
6874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        break
6884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    callback(data)
6894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # shutdown ssl layer
6904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if isinstance(conn, ssl.SSLSocket):
6914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.unwrap()
6924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            finally:
6934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn.close()
6944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self.voidresp()
6954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
6964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def retrlines(self, cmd, callback = None):
6974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if callback is None: callback = print_line
6984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = self.sendcmd('TYPE A')
6994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn = self.transfercmd(cmd)
7004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            fp = conn.makefile('rb')
7014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
7024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                while 1:
7034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    line = fp.readline()
7044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if self.debugging > 2: print '*retr*', repr(line)
7054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if not line:
7064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        break
7074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if line[-2:] == CRLF:
7084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        line = line[:-2]
7094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    elif line[-1:] == '\n':
7104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        line = line[:-1]
7114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    callback(line)
7124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # shutdown ssl layer
7134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if isinstance(conn, ssl.SSLSocket):
7144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.unwrap()
7154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            finally:
7164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                fp.close()
7174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn.close()
7184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self.voidresp()
7194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
7214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.voidcmd('TYPE I')
7224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn = self.transfercmd(cmd, rest)
7234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
7244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                while 1:
7254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    buf = fp.read(blocksize)
7264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if not buf: break
7274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.sendall(buf)
7284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if callback: callback(buf)
7294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # shutdown ssl layer
7304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if isinstance(conn, ssl.SSLSocket):
7314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.unwrap()
7324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            finally:
7334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn.close()
7344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self.voidresp()
7354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        def storlines(self, cmd, fp, callback=None):
7374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.voidcmd('TYPE A')
7384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            conn = self.transfercmd(cmd)
7394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
7404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                while 1:
7414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    buf = fp.readline()
7424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if not buf: break
7434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if buf[-2:] != CRLF:
7444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        if buf[-1] in CRLF: buf = buf[:-1]
7454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                        buf = buf + CRLF
7464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.sendall(buf)
7474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    if callback: callback(buf)
7484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # shutdown ssl layer
7494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if isinstance(conn, ssl.SSLSocket):
7504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    conn.unwrap()
7514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            finally:
7524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                conn.close()
7534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return self.voidresp()
7544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __all__.append('FTP_TLS')
7564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    all_errors = (Error, IOError, EOFError, ssl.SSLError)
7574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_150_re = None
7604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse150(resp):
7624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Parse the '150' response for a RETR request.
7634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Returns the expected transfer size or None; size is not guaranteed to
7644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    be present in the 150 message.
7654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''
7664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[:3] != '150':
7674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_reply, resp
7684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    global _150_re
7694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if _150_re is None:
7704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        import re
7714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
7724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    m = _150_re.match(resp)
7734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if not m:
7744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return None
7754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    s = m.group(1)
7764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
7774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return int(s)
7784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except (OverflowError, ValueError):
7794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return long(s)
7804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_227_re = None
7834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse227(resp):
7854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Parse the '227' response for a PASV request.
7864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
7874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Return ('host.addr.as.numbers', port#) tuple.'''
7884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
7894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[:3] != '227':
7904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_reply, resp
7914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    global _227_re
7924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if _227_re is None:
7934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        import re
7944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
7954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    m = _227_re.search(resp)
7964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if not m:
7974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_proto, resp
7984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    numbers = m.groups()
7994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    host = '.'.join(numbers[:4])
8004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    port = (int(numbers[4]) << 8) + int(numbers[5])
8014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    return host, port
8024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse229(resp, peer):
8054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Parse the '229' response for a EPSV request.
8064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Raises error_proto if it does not contain '(|||port|)'
8074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Return ('host.addr.as.numbers', port#) tuple.'''
8084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[:3] != '229':
8104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_reply, resp
8114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    left = resp.find('(')
8124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if left < 0: raise error_proto, resp
8134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    right = resp.find(')', left + 1)
8144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if right < 0:
8154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_proto, resp # should contain '(|||port|)'
8164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[left + 1] != resp[right - 1]:
8174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_proto, resp
8184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    parts = resp[left + 1:right].split(resp[left+1])
8194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if len(parts) != 5:
8204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_proto, resp
8214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    host = peer[0]
8224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    port = int(parts[3])
8234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    return host, port
8244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef parse257(resp):
8274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Parse the '257' response for a MKD or PWD request.
8284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    This is a response to a MKD or PWD request: a directory name.
8294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Returns the directoryname in the 257 reply.'''
8304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[:3] != '257':
8324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise error_reply, resp
8334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if resp[3:5] != ' "':
8344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return '' # Not compliant to RFC 959, but UNIX ftpd does this
8354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    dirname = ''
8364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    i = 5
8374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    n = len(resp)
8384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    while i < n:
8394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        c = resp[i]
8404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        i = i+1
8414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if c == '"':
8424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if i >= n or resp[i] != '"':
8434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
8444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            i = i+1
8454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        dirname = dirname + c
8464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    return dirname
8474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef print_line(line):
8504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Default retrlines callback to print a line.'''
8514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print line
8524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef ftpcp(source, sourcename, target, targetname = '', type = 'I'):
8554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Copy file from one FTP-instance to another.'''
8564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if not targetname: targetname = sourcename
8574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    type = 'TYPE ' + type
8584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    source.voidcmd(type)
8594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    target.voidcmd(type)
8604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    sourcehost, sourceport = parse227(source.sendcmd('PASV'))
8614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    target.sendport(sourcehost, sourceport)
8624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # RFC 959: the user must "listen" [...] BEFORE sending the
8634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # transfer request.
8644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # So: STOR before RETR, because here the target is a "user".
8654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    treply = target.sendcmd('STOR ' + targetname)
8664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if treply[:3] not in ('125', '150'): raise error_proto  # RFC 959
8674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    sreply = source.sendcmd('RETR ' + sourcename)
8684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if sreply[:3] not in ('125', '150'): raise error_proto  # RFC 959
8694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    source.voidresp()
8704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    target.voidresp()
8714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass Netrc:
8744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Class to parse & provide access to 'netrc' format files.
8754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    See the netrc(4) man page for information on the file format.
8774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    WARNING: This class is obsolete -- use module netrc instead.
8794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
8814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __defuser = None
8824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __defpasswd = None
8834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __defacct = None
8844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
8854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, filename=None):
8864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if filename is None:
8874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if "HOME" in os.environ:
8884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                filename = os.path.join(os.environ["HOME"],
8894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                        ".netrc")
8904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
8914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                raise IOError, \
8924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                      "specify file to load or set $HOME"
8934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__hosts = {}
8944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__macros = {}
8954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fp = open(filename, "r")
8964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        in_macro = 0
8974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while 1:
8984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            line = fp.readline()
8994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not line: break
9004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if in_macro and line.strip():
9014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                macro_lines.append(line)
9024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                continue
9034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            elif in_macro:
9044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__macros[macro_name] = tuple(macro_lines)
9054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                in_macro = 0
9064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            words = line.split()
9074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            host = user = passwd = acct = None
9084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            default = 0
9094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            i = 0
9104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            while i < len(words):
9114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                w1 = words[i]
9124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if i+1 < len(words):
9134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    w2 = words[i + 1]
9144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                else:
9154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    w2 = None
9164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if w1 == 'default':
9174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    default = 1
9184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                elif w1 == 'machine' and w2:
9194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    host = w2.lower()
9204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    i = i + 1
9214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                elif w1 == 'login' and w2:
9224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    user = w2
9234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    i = i + 1
9244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                elif w1 == 'password' and w2:
9254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    passwd = w2
9264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    i = i + 1
9274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                elif w1 == 'account' and w2:
9284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    acct = w2
9294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    i = i + 1
9304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                elif w1 == 'macdef' and w2:
9314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    macro_name = w2
9324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    macro_lines = []
9334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    in_macro = 1
9344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    break
9354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                i = i + 1
9364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if default:
9374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__defuser = user or self.__defuser
9384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__defpasswd = passwd or self.__defpasswd
9394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__defacct = acct or self.__defacct
9404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if host:
9414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if host in self.__hosts:
9424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    ouser, opasswd, oacct = \
9434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                           self.__hosts[host]
9444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    user = user or ouser
9454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    passwd = passwd or opasswd
9464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    acct = acct or oacct
9474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__hosts[host] = user, passwd, acct
9484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fp.close()
9494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def get_hosts(self):
9514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Return a list of hosts mentioned in the .netrc file."""
9524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.__hosts.keys()
9534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def get_account(self, host):
9554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Returns login information for the named host.
9564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        The return value is a triple containing userid,
9584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        password, and the accounting field.
9594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
9614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        host = host.lower()
9624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        user = passwd = acct = None
9634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if host in self.__hosts:
9644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            user, passwd, acct = self.__hosts[host]
9654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        user = user or self.__defuser
9664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        passwd = passwd or self.__defpasswd
9674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        acct = acct or self.__defacct
9684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return user, passwd, acct
9694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def get_macros(self):
9714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Return a list of all defined macro names."""
9724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.__macros.keys()
9734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def get_macro(self, macro):
9754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Return a sequence of lines which define a named macro."""
9764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.__macros[macro]
9774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef test():
9814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''Test program.
9824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
9834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    -d dir
9854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    -l list
9864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    -p password
9874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    '''
9884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if len(sys.argv) < 2:
9904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        print test.__doc__
9914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sys.exit(0)
9924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
9934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    debugging = 0
9944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    rcfile = None
9954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    while sys.argv[1] == '-d':
9964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        debugging = debugging+1
9974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        del sys.argv[1]
9984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if sys.argv[1][:2] == '-r':
9994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # get name of alternate ~/.netrc file:
10004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        rcfile = sys.argv[1][2:]
10014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        del sys.argv[1]
10024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    host = sys.argv[1]
10034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    ftp = FTP(host)
10044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    ftp.set_debuglevel(debugging)
10054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    userid = passwd = acct = ''
10064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
10074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        netrc = Netrc(rcfile)
10084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except IOError:
10094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if rcfile is not None:
10104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            sys.stderr.write("Could not open account file"
10114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                             " -- using anonymous login.")
10124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    else:
10134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
10144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            userid, passwd, acct = netrc.get_account(host)
10154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except KeyError:
10164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # no account for host
10174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            sys.stderr.write(
10184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    "No account -- using anonymous login.")
10194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    ftp.login(userid, passwd, acct)
10204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for file in sys.argv[2:]:
10214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if file[:2] == '-l':
10224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            ftp.dir(file[2:])
10234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif file[:2] == '-d':
10244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            cmd = 'CWD'
10254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if file[2:]: cmd = cmd + ' ' + file[2:]
10264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            resp = ftp.sendcmd(cmd)
10274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif file == '-p':
10284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            ftp.set_pasv(not ftp.passiveserver)
10294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
10304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            ftp.retrbinary('RETR ' + file, \
10314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                           sys.stdout.write, 1024)
10324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    ftp.quit()
10334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
10344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
10354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == '__main__':
10364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    test()
1037