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