source: titan/mediathek/localhoster/lib/python2.7/ftplib.py @ 40523

Last change on this file since 40523 was 40094, checked in by obi, 7 years ago

tithek add yoztube-dl support

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