home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / utilities / p / python / !Python / Lib / NetLib / py / urllib < prev    next >
Text File  |  1996-10-22  |  22KB  |  758 lines

  1. # Open an arbitrary URL
  2. #
  3. # See the following document for a tentative description of URLs:
  4. #     Uniform Resource Locators              Tim Berners-Lee
  5. #     INTERNET DRAFT                                    CERN
  6. #     IETF URL Working Group                    14 July 1993
  7. #     draft-ietf-uri-url-01.txt
  8. #
  9. # The object returned by URLopener().open(file) will differ per
  10. # protocol.  All you know is that is has methods read(), readline(),
  11. # readlines(), fileno(), close() and info().  The read*(), fileno()
  12. # and close() methods work like those of open files. 
  13. # The info() method returns an mimetools.Message object which can be
  14. # used to query various info about the object, if available.
  15. # (mimetools.Message objects are queried with the getheader() method.)
  16.  
  17. import string
  18. import socket
  19. import regex
  20. import os
  21.  
  22.  
  23. __version__ = '1.5'
  24.  
  25. # Helper for non-unix systems
  26. if os.name == 'mac':
  27.     from macurl2path import url2pathname, pathname2url
  28. elif os.name == 'nt':    
  29.     from nturl2path import url2pathname, pathname2url 
  30. else:
  31.     def url2pathname(pathname):
  32.         return pathname
  33.     def pathname2url(pathname):
  34.         return pathname
  35.  
  36. # This really consists of two pieces:
  37. # (1) a class which handles opening of all sorts of URLs
  38. #     (plus assorted utilities etc.)
  39. # (2) a set of functions for parsing URLs
  40. # XXX Should these be separated out into different modules?
  41.  
  42.  
  43. # Shortcut for basic usage
  44. _urlopener = None
  45. def urlopen(url):
  46.     global _urlopener
  47.     if not _urlopener:
  48.         _urlopener = FancyURLopener()
  49.     return _urlopener.open(url)
  50. def urlretrieve(url, filename=None):
  51.     global _urlopener
  52.     if not _urlopener:
  53.         _urlopener = FancyURLopener()
  54.     if filename:
  55.         return _urlopener.retrieve(url, filename)
  56.     else:
  57.         return _urlopener.retrieve(url)
  58. def urlcleanup():
  59.     if _urlopener:
  60.         _urlopener.cleanup()
  61.  
  62.  
  63. # Class to open URLs.
  64. # This is a class rather than just a subroutine because we may need
  65. # more than one set of global protocol-specific options.
  66. # Note -- this is a base class for those who don't want the
  67. # automatic handling of errors type 302 (relocated) and 401
  68. # (authorization needed).
  69. ftpcache = {}
  70. class URLopener:
  71.  
  72.     # Constructor
  73.     def __init__(self, proxies=None):
  74.         if proxies is None:
  75.             proxies = getproxies()
  76.         self.proxies = proxies
  77.         server_version = "Python-urllib/%s" % __version__
  78.         self.addheaders = [('User-agent', server_version)]
  79.         self.tempcache = None
  80.         # Undocumented feature: if you assign {} to tempcache,
  81.         # it is used to cache files retrieved with
  82.         # self.retrieve().  This is not enabled by default
  83.         # since it does not work for changing documents (and I
  84.         # haven't got the logic to check expiration headers
  85.         # yet).
  86.         self.ftpcache = ftpcache
  87.         # Undocumented feature: you can use a different
  88.         # ftp cache by assigning to the .ftpcache member;
  89.         # in case you want logically independent URL openers
  90.  
  91.     def __del__(self):
  92.         self.close()
  93.  
  94.     def close(self):
  95.         self.cleanup()
  96.  
  97.     def cleanup(self):
  98.         import os
  99.         if self.tempcache:
  100.             for url in self.tempcache.keys():
  101.                 try:
  102.                     os.unlink(self.tempcache[url][0])
  103.                 except os.error:
  104.                     pass
  105.                 del self.tempcache[url]
  106.  
  107.     # Add a header to be used by the HTTP interface only
  108.     # e.g. u.addheader('Accept', 'sound/basic')
  109.     def addheader(self, *args):
  110.         self.addheaders.append(args)
  111.  
  112.     # External interface
  113.     # Use URLopener().open(file) instead of open(file, 'r')
  114.     def open(self, fullurl):
  115.         fullurl = unwrap(fullurl)
  116.         type, url = splittype(fullurl)
  117.          if not type: type = 'file'
  118.         self.openedurl = '%s:%s' % (type, url)
  119.         if self.proxies.has_key(type):
  120.             proxy = self.proxies[type]
  121.             type, proxy = splittype(proxy)
  122.             host, selector = splithost(proxy)
  123.             url = (host, fullurl) # Signal special case to open_*()
  124.         name = 'open_' + type
  125.         if '-' in name:
  126.             import regsub
  127.             name = regsub.gsub('-', '_', name)
  128.         if not hasattr(self, name):
  129.             return self.open_unknown(fullurl)
  130.         try:
  131.             return getattr(self, name)(url)
  132.         except socket.error, msg:
  133.             raise IOError, ('socket error', msg)
  134.  
  135.     # Overridable interface to open unknown URL type
  136.     def open_unknown(self, fullurl):
  137.         type, url = splittype(fullurl)
  138.         raise IOError, ('url error', 'unknown url type', type)
  139.  
  140.     # External interface
  141.     # retrieve(url) returns (filename, None) for a local object
  142.     # or (tempfilename, headers) for a remote object
  143.     def retrieve(self, url, filename=None):
  144.         if self.tempcache and self.tempcache.has_key(url):
  145.             return self.tempcache[url]
  146.         url1 = unwrap(url)
  147.         self.openedurl = url1
  148.         if self.tempcache and self.tempcache.has_key(url1):
  149.             self.tempcache[url] = self.tempcache[url1]
  150.             return self.tempcache[url1]
  151.         type, url1 = splittype(url1)
  152.         if not filename and (not type or type == 'file'):
  153.             try:
  154.                 fp = self.open_local_file(url1)
  155.                 del fp
  156.                 return url2pathname(splithost(url1)[1]), None
  157.             except IOError, msg:
  158.                 pass
  159.         fp = self.open(url)
  160.         headers = fp.info()
  161.         if not filename:
  162.             import tempfile
  163.             filename = tempfile.mktemp()
  164.         result = filename, headers
  165.         if self.tempcache is not None:
  166.             self.tempcache[url] = result
  167.         tfp = open(filename, 'w')
  168.         bs = 1024*8
  169.         block = fp.read(bs)
  170.         while block:
  171.             tfp.write(block)
  172.             block = fp.read(bs)
  173.         del fp
  174.         del tfp
  175.         return result
  176.  
  177.     # Each method named open_<type> knows how to open that type of URL
  178.  
  179.     # Use HTTP protocol
  180.     def open_http(self, url):
  181.         import httplib
  182.         if type(url) is type(""):
  183.             host, selector = splithost(url)
  184.             user_passwd, host = splituser(host)
  185.         else:
  186.             host, selector = url
  187.             urltype, rest = splittype(selector)
  188.             if string.lower(urltype) == 'http':
  189.                 realhost, rest = splithost(rest)
  190.                 user_passwd, realhost = splituser(realhost)
  191.                 if user_passwd:
  192.                 selector = "%s://%s%s" % (urltype,
  193.                               realhost, rest)
  194.             print "proxy via http:", host, selector
  195.         if not host: raise IOError, ('http error', 'no host given')
  196.         if user_passwd:
  197.             import base64
  198.             auth = string.strip(base64.encodestring(user_passwd))
  199.         else:
  200.             auth = None
  201.         h = httplib.HTTP(host)
  202.         h.putrequest('GET', selector)
  203.         if auth: h.putheader('Authorization: Basic %s' % auth)
  204.         for args in self.addheaders: apply(h.putheader, args)
  205.         h.endheaders()
  206.         errcode, errmsg, headers = h.getreply()
  207.         fp = h.getfile()
  208.         if errcode == 200:
  209.             return addinfourl(fp, headers, self.openedurl)
  210.         else:
  211.             return self.http_error(url,
  212.                            fp, errcode, errmsg, headers)
  213.  
  214.     # Handle http errors.
  215.     # Derived class can override this, or provide specific handlers
  216.     # named http_error_DDD where DDD is the 3-digit error code
  217.     def http_error(self, url, fp, errcode, errmsg, headers):
  218.         # First check if there's a specific handler for this error
  219.         name = 'http_error_%d' % errcode
  220.         if hasattr(self, name):
  221.             method = getattr(self, name)
  222.             result = method(url, fp, errcode, errmsg, headers)
  223.             if result: return result
  224.         return self.http_error_default(
  225.             url, fp, errcode, errmsg, headers)
  226.  
  227.     # Default http error handler: close the connection and raises IOError
  228.     def http_error_default(self, url, fp, errcode, errmsg, headers):
  229.         void = fp.read()
  230.         fp.close()
  231.         raise IOError, ('http error', errcode, errmsg, headers)
  232.  
  233.     # Use Gopher protocol
  234.     def open_gopher(self, url):
  235.         import gopherlib
  236.         host, selector = splithost(url)
  237.         if not host: raise IOError, ('gopher error', 'no host given')
  238.         type, selector = splitgophertype(selector)
  239.         selector, query = splitquery(selector)
  240.         selector = unquote(selector)
  241.         if query:
  242.             query = unquote(query)
  243.             fp = gopherlib.send_query(selector, query, host)
  244.         else:
  245.             fp = gopherlib.send_selector(selector, host)
  246.         return addinfourl(fp, noheaders(), self.openedurl)
  247.  
  248.     # Use local file or FTP depending on form of URL
  249.     def open_file(self, url):
  250.         if url[:2] == '//':
  251.             return self.open_ftp(url)
  252.         else:
  253.             return self.open_local_file(url)
  254.  
  255.     # Use local file
  256.     def open_local_file(self, url):
  257.         host, file = splithost(url)
  258.         if not host:
  259.             return addinfourl(open(url2pathname(file), 'r'), noheaders(), 'file:'+file)
  260.         host, port = splitport(host)
  261.         if not port and socket.gethostbyname(host) in (
  262.               localhost(), thishost()):
  263.             file = unquote(file)
  264.             return addinfourl(open(url2pathname(file), 'r'), noheaders(), 'file:'+file)
  265.         raise IOError, ('local file error', 'not on local host')
  266.  
  267.     # Use FTP protocol
  268.     def ope