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