home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 January / Chip_2003-01_cd2.bin / convert / eJayMp3Pro / mp3pro_demo.exe / FTPLIB.PYC (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2000-06-06  |  28.5 KB  |  832 lines

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