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