home *** CD-ROM | disk | FTP | other *** search
/ com!online 2005 April / com_0405_1.iso / opensource / BTpp-0.5.4-bin.exe / $INSTDIR / BT++.exe / urllib2.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2003-04-19  |  40.3 KB  |  1,086 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.2)
  3.  
  4. import socket
  5. import httplib
  6. import inspect
  7. import re
  8. import base64
  9. import types
  10. import urlparse
  11. import md5
  12. import mimetypes
  13. import mimetools
  14. import rfc822
  15. import ftplib
  16. import sys
  17. import time
  18. import os
  19. import stat
  20. import gopherlib
  21. import posixpath
  22.  
  23. try:
  24.     from cStringIO import StringIO
  25. except ImportError:
  26.     from StringIO import StringIO
  27.  
  28.  
  29. try:
  30.     import sha
  31. except ImportError:
  32.     sha = None
  33.  
  34. from urllib import unwrap, unquote, splittype, splithost, addinfourl, splitport, splitgophertype, splitquery, splitattr, ftpwrapper, noheaders
  35. from urllib import getproxies
  36. from urllib import localhost, url2pathname
  37. __version__ = '2.0a1'
  38. _opener = None
  39.  
  40. def urlopen(url, data = None):
  41.     global _opener
  42.     if _opener is None:
  43.         _opener = build_opener()
  44.     
  45.     return _opener.open(url, data)
  46.  
  47.  
  48. def install_opener(opener):
  49.     global _opener
  50.     _opener = opener
  51.  
  52.  
  53. class URLError(IOError):
  54.     
  55.     def __init__(self, reason):
  56.         self.reason = reason
  57.  
  58.     
  59.     def __str__(self):
  60.         return '<urlopen error %s>' % self.reason
  61.  
  62.  
  63.  
  64. class HTTPError(URLError, addinfourl):
  65.     __super_init = addinfourl.__init__
  66.     
  67.     def __init__(self, url, code, msg, hdrs, fp):
  68.         self._HTTPError__super_init(fp, hdrs, url)
  69.         self.code = code
  70.         self.msg = msg
  71.         self.hdrs = hdrs
  72.         self.fp = fp
  73.         self.filename = url
  74.  
  75.     
  76.     def __str__(self):
  77.         return 'HTTP Error %s: %s' % (self.code, self.msg)
  78.  
  79.     
  80.     def __del__(self):
  81.         if self.fp:
  82.             self.fp.close()
  83.         
  84.  
  85.  
  86.  
  87. class GopherError(URLError):
  88.     pass
  89.  
  90.  
  91. class Request:
  92.     
  93.     def __init__(self, url, data = None, headers = { }):
  94.         self._Request__original = unwrap(url)
  95.         self.type = None
  96.         self.host = None
  97.         self.port = None
  98.         self.data = data
  99.         self.headers = { }
  100.         self.headers.update(headers)
  101.  
  102.     
  103.     def __getattr__(self, attr):
  104.         if attr[:12] == '_Request__r_':
  105.             name = attr[12:]
  106.             if hasattr(Request, 'get_' + name):
  107.                 getattr(self, 'get_' + name)()
  108.                 return getattr(self, attr)
  109.             
  110.         
  111.         raise AttributeError, attr
  112.  
  113.     
  114.     def add_data(self, data):
  115.         self.data = data
  116.  
  117.     
  118.     def has_data(self):
  119.         return self.data is not None
  120.  
  121.     
  122.     def get_data(self):
  123.         return self.data
  124.  
  125.     
  126.     def get_full_url(self):
  127.         return self._Request__original
  128.  
  129.     
  130.     def get_type(self):
  131.         if self.type is None:
  132.             (self.type, self._Request__r_type) = splittype(self._Request__original)
  133.             if self.type is None:
  134.                 raise ValueError, 'unknown url type: %s' % self._Request__original
  135.             
  136.         
  137.         return self.type
  138.  
  139.     
  140.     def get_host(self):
  141.         if self.host is None:
  142.             (self.host, self._Request__r_host) = splithost(self._Request__r_type)
  143.             if self.host:
  144.                 self.host = unquote(self.host)
  145.             
  146.         
  147.         return self.host
  148.  
  149.     
  150.     def get_selector(self):
  151.         return self._Request__r_host
  152.  
  153.     
  154.     def set_proxy(self, host, type):
  155.         (self.host, self.type) = (host, type)
  156.         self._Request__r_host = self._Request__original
  157.  
  158.     
  159.     def add_header(self, key, val):
  160.         self.headers[key] = val
  161.  
  162.  
  163.  
  164. class OpenerDirector:
  165.     
  166.     def __init__(self):
  167.         server_version = 'Python-urllib/%s' % __version__
  168.         self.addheaders = [
  169.             ('User-agent', server_version)]
  170.         self.handlers = []
  171.         self.handle_open = { }
  172.         self.handle_error = { }
  173.  
  174.     
  175.     def add_handler(self, handler):
  176.         added = 0
  177.         for meth in dir(handler):
  178.             if meth[-5:] == '_open':
  179.                 protocol = meth[:-5]
  180.                 if self.handle_open.has_key(protocol):
  181.                     self.handle_open[protocol].append(handler)
  182.                 else:
  183.                     self.handle_open[protocol] = [
  184.                         handler]
  185.                 added = 1
  186.                 continue
  187.             
  188.             i = meth.find('_')
  189.             j = meth[i + 1:].find('_') + i + 1
  190.             if j != -1 and meth[i + 1:j] == 'error':
  191.                 proto = meth[:i]
  192.                 kind = meth[j + 1:]
  193.                 
  194.                 try:
  195.                     kind = int(kind)
  196.                 except ValueError:
  197.                     pass
  198.  
  199.                 dict = self.handle_error.get(proto, { })
  200.                 if dict.has_key(kind):
  201.                     dict[kind].append(handler)
  202.                 else:
  203.                     dict[kind] = [
  204.                         handler]
  205.                 self.handle_error[proto] = dict
  206.                 added = 1
  207.                 continue
  208.             
  209.         
  210.         if added:
  211.             self.handlers.append(handler)
  212.             handler.add_parent(self)
  213.         
  214.  
  215.     
  216.     def __del__(self):
  217.         self.close()
  218.  
  219.     
  220.     def close(self):
  221.         for handler in self.handlers:
  222.             handler.close()
  223.         
  224.         self.handlers = []
  225.  
  226.     
  227.     def _call_chain(self, chain, kind, meth_name, *args):
  228.         handlers = chain.get(kind, ())
  229.         for handler in handlers:
  230.             func = getattr(handler, meth_name)
  231.             result = func(*args)
  232.             if result is not None:
  233.                 return result
  234.             
  235.         
  236.  
  237.     
  238.     def open(self, fullurl, data = None):
  239.         if isinstance(fullurl, (types.StringType, types.UnicodeType)):
  240.             req = Request(fullurl, data)
  241.         else:
  242.             req = fullurl
  243.             if data is not None:
  244.                 req.add_data(data)
  245.             
  246.         result = self._call_chain(self.handle_open, 'default', 'default_open', req)
  247.         if result:
  248.             return result
  249.         
  250.         type_ = req.get_type()
  251.         result = self._call_chain(self.handle_open, type_, type_ + '_open', req)
  252.         if result:
  253.             return result
  254.         
  255.         return self._call_chain(self.handle_open, 'unknown', 'unknown_open', req)
  256.  
  257.     
  258.     def error(self, proto, *args):
  259.         if proto in [
  260.             'http',
  261.             'https']:
  262.             dict = self.handle_error['http']
  263.             proto = args[2]
  264.             meth_name = 'http_error_%d' % proto
  265.             http_err = 1
  266.             orig_args = args
  267.         else:
  268.             dict = self.handle_error
  269.             meth_name = proto + '_error'
  270.             http_err = 0
  271.         args = (dict, proto, meth_name) + args
  272.         result = self._call_chain(*args)
  273.         if result:
  274.             return result
  275.         
  276.         if http_err:
  277.             args = (dict, 'default', 'http_error_default') + orig_args
  278.             return self._call_chain(*args)
  279.         
  280.  
  281.  
  282.  
  283. def build_opener(*handlers):
  284.     opener = OpenerDirector()
  285.     default_classes = [
  286.         ProxyHandler,
  287.         UnknownHandler,
  288.         HTTPHandler,
  289.         HTTPDefaultErrorHandler,
  290.         HTTPRedirectHandler,
  291.         FTPHandler,
  292.         FileHandler]
  293.     if hasattr(httplib, 'HTTPS'):
  294.         default_classes.append(HTTPSHandler)
  295.     
  296.     skip = []
  297.     for klass in default_classes:
  298.         for check in handlers:
  299.             if inspect.isclass(check):
  300.                 if issubclass(check, klass):
  301.                     skip.append(klass)
  302.                 
  303.             elif isinstance(check, klass):
  304.                 skip.append(klass)
  305.             
  306.         
  307.     
  308.     for klass in skip:
  309.         default_classes.remove(klass)
  310.     
  311.     for klass in default_classes:
  312.         opener.add_handler(klass())
  313.     
  314.     for h in handlers:
  315.         if inspect.isclass(h):
  316.             h = h()
  317.         
  318.         opener.add_handler(h)
  319.     
  320.     return opener
  321.  
  322.  
  323. class BaseHandler:
  324.     
  325.     def add_parent(self, parent):
  326.         self.parent = parent
  327.  
  328.     
  329.     def close(self):
  330.         self.parent = None
  331.  
  332.  
  333.  
  334. class HTTPDefaultErrorHandler(BaseHandler):
  335.     
  336.     def http_error_default(self, req, fp, code, msg, hdrs):
  337.         raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
  338.  
  339.  
  340.  
  341. class HTTPRedirectHandler(BaseHandler):
  342.     
  343.     def http_error_302(self, req, fp, code, msg, headers):
  344.         if headers.has_key('location'):
  345.             newurl = headers['location']
  346.         elif headers.has_key('uri'):
  347.             newurl = headers['uri']
  348.         else:
  349.             return None
  350.         newurl = urlparse.urljoin(req.get_full_url(), newurl)
  351.         new = Request(newurl, req.get_data(), req.headers)
  352.         new.error_302_dict = { }
  353.         if hasattr(req, 'error_302_dict'):
  354.             if len(req.error_302_dict) > 10 or req.error_302_dict.has_key(newurl):
  355.                 raise HTTPError(req.get_full_url(), code, self.inf_msg + msg, headers, fp)
  356.             
  357.             new.error_302_dict.update(req.error_302_dict)
  358.         
  359.         new.error_302_dict[newurl] = newurl
  360.         fp.read()
  361.         fp.close()
  362.         return self.parent.open(new)
  363.  
  364.     http_error_301 = http_error_302
  365.     inf_msg = 'The HTTP server returned a redirect error that wouldlead to an infinite loop.\nThe last 302 error message was:\n'
  366.  
  367.  
  368. class ProxyHandler(BaseHandler):
  369.     
  370.     def __init__(self, proxies = None):
  371.         if proxies is None:
  372.             proxies = getproxies()
  373.         
  374.         self.proxies = proxies
  375.         for type, url in proxies.items():
  376.             setattr(self, '%s_open' % type, (lambda r, proxy = url, type = type, meth = self.proxy_open: meth(r, proxy, type)))
  377.         
  378.  
  379.     
  380.     def proxy_open(self, req, proxy, type):
  381.         orig_type = req.get_type()
  382.         (type, r_type) = splittype(proxy)
  383.         (host, XXX) = splithost(r_type)
  384.         if '@' in host:
  385.             (user_pass, host) = host.split('@', 1)
  386.             if ':' in user_pass:
  387.                 (user, password) = user_pass.split(':', 1)
  388.                 user_pass = base64.encodestring('%s:%s' % (unquote(user), unquote(password)))
  389.                 req.add_header('Proxy-Authorization', 'Basic ' + user_pass)
  390.             
  391.         
  392.         host = unquote(host)
  393.         req.set_proxy(host, type)
  394.         if orig_type == type:
  395.             return None
  396.         else:
  397.             return self.parent.open(req)
  398.  
  399.  
  400.  
  401. class CustomProxy:
  402.     
  403.     def __init__(self, proto, func = None, proxy_addr = None):
  404.         self.proto = proto
  405.         self.func = func
  406.         self.addr = proxy_addr
  407.  
  408.     
  409.     def handle(self, req):
  410.         if self.func and self.func(req):
  411.             return 1
  412.         
  413.  
  414.     
  415.     def get_proxy(self):
  416.         return self.addr
  417.  
  418.  
  419.  
  420. class CustomProxyHandler(BaseHandler):
  421.     
  422.     def __init__(self, *proxies):
  423.         self.proxies = { }
  424.  
  425.     
  426.     def proxy_open(self, req):
  427.         proto = req.get_type()
  428.         
  429.         try:
  430.             proxies = self.proxies[proto]
  431.         except KeyError:
  432.             return None
  433.  
  434.         for p in proxies:
  435.             if p.handle(req):
  436.                 req.set_proxy(p.get_proxy())
  437.                 return self.parent.open(req)
  438.             
  439.         
  440.         return None
  441.  
  442.     
  443.     def do_proxy(self, p, req):
  444.         return self.parent.open(req)
  445.  
  446.     
  447.     def add_proxy(self, cpo):
  448.         if self.proxies.has_key(cpo.proto):
  449.             self.proxies[cpo.proto].append(cpo)
  450.         else:
  451.             self.proxies[cpo.proto] = [
  452.                 cpo]
  453.  
  454.  
  455.  
  456. class HTTPPasswordMgr:
  457.     
  458.     def __init__(self):
  459.         self.passwd = { }
  460.  
  461.     
  462.     def add_password(self, realm, uri, user, passwd):
  463.         if isinstance(uri, (types.StringType, types.UnicodeType)):
  464.             uri = [
  465.                 uri]
  466.         
  467.         uri = tuple(map(self.reduce_uri, uri))
  468.         if not self.passwd.has_key(realm):
  469.             self.passwd[realm] = { }
  470.         
  471.         self.passwd[realm][uri] = (user, passwd)
  472.  
  473.     
  474.     def find_user_password(self, realm, authuri):
  475.         domains = self.passwd.get(realm, { })
  476.         authuri = self.reduce_uri(authuri)
  477.         for uris, authinfo in domains.items():
  478.             for uri in uris:
  479.                 if self.is_suburi(uri, authuri):
  480.                     return authinfo
  481.                 
  482.             
  483.         
  484.         return (None, None)
  485.  
  486.     
  487.     def reduce_uri(self, uri):
  488.         parts = urlparse.urlparse(uri)
  489.         if parts[1]:
  490.             if not parts[2]:
  491.                 pass
  492.             return (parts[1], '/')
  493.         else:
  494.             return (parts[2], '/')
  495.  
  496.     
  497.     def is_suburi(self, base, test):
  498.         if base == test:
  499.             return 1
  500.         
  501.         if base[0] != test[0]:
  502.             return 0
  503.         
  504.         common = posixpath.commonprefix((base[1], test[1]))
  505.         if len(common) == len(base[1]):
  506.             return 1
  507.         
  508.         return 0
  509.  
  510.  
  511.  
  512. class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
  513.     
  514.     def find_user_password(self, realm, authuri):
  515.         (user, password) = HTTPPasswordMgr.find_user_password(self, realm, authuri)
  516.         if user is not None:
  517.             return (user, password)
  518.         
  519.         return HTTPPasswordMgr.find_user_password(self, None, authuri)
  520.  
  521.  
  522.  
  523. class AbstractBasicAuthHandler:
  524.     rx = re.compile('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"')
  525.     
  526.     def __init__(self, password_mgr = None):
  527.         if password_mgr is None:
  528.             password_mgr = HTTPPasswordMgr()
  529.         
  530.         self.passwd = password_mgr
  531.         self.add_password = self.passwd.add_password
  532.  
  533.     
  534.     def http_error_auth_reqed(self, authreq, host, req, headers):
  535.         authreq = headers.get(authreq, None)
  536.         if authreq:
  537.             mo = AbstractBasicAuthHandler.rx.match(authreq)
  538.             if mo:
  539.                 (scheme, realm) = mo.groups()
  540.                 if scheme.lower() == 'basic':
  541.                     return self.retry_http_basic_auth(host, req, realm)
  542.                 
  543.             
  544.         
  545.  
  546.     
  547.     def retry_http_basic_auth(self, host, req, realm):
  548.         (user, pw) = self.passwd.find_user_password(realm, host)
  549.         if pw:
  550.             raw = '%s:%s' % (user, pw)
  551.             auth = 'Basic %s' % base64.encodestring(raw).strip()
  552.             if req.headers.get(self.auth_header, None) == auth:
  553.                 return None
  554.             
  555.             req.add_header(self.auth_header, auth)
  556.             return self.parent.open(req)
  557.         else:
  558.             return None
  559.  
  560.  
  561.  
  562. class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
  563.     auth_header = 'Authorization'
  564.     
  565.     def http_error_401(self, req, fp, code, msg, headers):
  566.         host = urlparse.urlparse(req.get_full_url())[1]
  567.         return self.http_error_auth_reqed('www-authenticate', host, req, headers)
  568.  
  569.  
  570.  
  571. class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
  572.     auth_header = 'Proxy-Authorization'
  573.     
  574.     def http_error_407(self, req, fp, code, msg, headers):
  575.         host = req.get_host()
  576.         return self.http_error_auth_reqed('proxy-authenticate', host, req, headers)
  577.  
  578.  
  579.  
  580. class AbstractDigestAuthHandler:
  581.     
  582.     def __init__(self, passwd = None):
  583.         if passwd is None:
  584.             passwd = HTTPPasswordMgr()
  585.         
  586.         self.passwd = passwd
  587.         self.add_password = self.passwd.add_password
  588.  
  589.     
  590.     def http_error_auth_reqed(self, authreq, host, req, headers):
  591.         authreq = headers.get(self.auth_header, None)
  592.         if authreq:
  593.             kind = authreq.split()[0]
  594.             if kind == 'Digest':
  595.                 return self.retry_http_digest_auth(req, authreq)
  596.             
  597.         
  598.  
  599.     
  600.     def retry_http_digest_auth(self, req, auth):
  601.         (token, challenge) = auth.split(' ', 1)
  602.         chal = parse_keqv_list(parse_http_list(challenge))
  603.         auth = self.get_authorization(req, chal)
  604.         if auth:
  605.             auth_val = 'Digest %s' % auth
  606.             if req.headers.get(self.auth_header, None) == auth_val:
  607.                 return None
  608.             
  609.             req.add_header(self.auth_header, auth_val)
  610.             resp = self.parent.open(req)
  611.             return resp
  612.         
  613.  
  614.     
  615.     def get_authorization(self, req, chal):
  616.         
  617.         try:
  618.             realm = chal['realm']
  619.             nonce = chal['nonce']
  620.             algorithm = chal.get('algorithm', 'MD5')
  621.             opaque = chal.get('opaque', None)
  622.         except KeyError:
  623.             return None
  624.  
  625.         (H, KD) = self.get_algorithm_impls(algorithm)
  626.         if H is None:
  627.             return None
  628.         
  629.         (user, pw) = self.passwd.find_user_password(realm, req.get_full_url())
  630.         if user is None:
  631.             return None
  632.         
  633.         if req.has_data():
  634.             entdig = self.get_entity_digest(req.get_data(), chal)
  635.         else:
  636.             entdig = None
  637.         A1 = '%s:%s:%s' % (user, realm, pw)
  638.         if not req.has_data() and 'POST':
  639.             pass
  640.         A2 = '%s:%s' % ('GET', req.get_selector())
  641.         respdig = KD(H(A1), '%s:%s' % (nonce, H(A2)))
  642.         base = 'username="%s", realm="%s", nonce="%s", uri="%s", response="%s"' % (user, realm, nonce, req.get_selector(), respdig)
  643.         if opaque:
  644.             base = base + ', opaque="%s"' % opaque
  645.         
  646.         if entdig:
  647.             base = base + ', digest="%s"' % entdig
  648.         
  649.         if algorithm != 'MD5':
  650.             base = base + ', algorithm="%s"' % algorithm
  651.         
  652.         return base
  653.  
  654.     
  655.     def get_algorithm_impls(self, algorithm):
  656.         if algorithm == 'MD5':
  657.             
  658.             H = lambda x, e = encode_digest: e(md5.new(x).digest())
  659.         elif algorithm == 'SHA':
  660.             
  661.             H = lambda x, e = encode_digest: e(sha.new(x).digest())
  662.         
  663.         
  664.         KD = lambda s, d, H = H: H('%s:%s' % (s, d))
  665.         return (H, KD)
  666.  
  667.     
  668.     def get_entity_digest(self, data, chal):
  669.         return None
  670.  
  671.  
  672.  
  673. class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
  674.     header = 'Authorization'
  675.     
  676.     def http_error_401(self, req, fp, code, msg, headers):
  677.         host = urlparse.urlparse(req.get_full_url())[1]
  678.         self.http_error_auth_reqed('www-authenticate', host, req, headers)
  679.  
  680.  
  681.  
  682. class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
  683.     header = 'Proxy-Authorization'
  684.     
  685.     def http_error_407(self, req, fp, code, msg, headers):
  686.         host = req.get_host()
  687.         self.http_error_auth_reqed('proxy-authenticate', host, req, headers)
  688.  
  689.  
  690.  
  691. def encode_digest(digest):
  692.     hexrep = []
  693.     for c in digest:
  694.         n = ord(c) >> 4 & 15
  695.         hexrep.append(hex(n)[-1])
  696.         n = ord(c) & 15
  697.         hexrep.append(hex(n)[-1])
  698.     
  699.     return ''.join(hexrep)
  700.  
  701.  
  702. class AbstractHTTPHandler(BaseHandler):
  703.     
  704.     def do_open(self, http_class, req):
  705.         host = req.get_host()
  706.         if not host:
  707.             raise URLError('no host given')
  708.         
  709.         
  710.         try:
  711.             h = http_class(host)
  712.             if req.has_data():
  713.                 data = req.get_data()
  714.                 h.putrequest('POST', req.get_selector())
  715.                 if not req.headers.has_key('Content-type'):
  716.                     h.putheader('Content-type', 'application/x-www-form-urlencoded')
  717.                 
  718.                 if not req.headers.has_key('Content-length'):
  719.                     h.putheader('Content-length', '%d' % len(data))
  720.                 
  721.             else:
  722.                 h.putrequest('GET', req.get_selector())
  723.         except socket.error:
  724.             err = None
  725.             raise URLError(err)
  726.  
  727.         (scheme, sel) = splittype(req.get_selector())
  728.         (sel_host, sel_path) = splithost(sel)
  729.         if not sel_host:
  730.             pass
  731.         h.putheader('Host', host)
  732.         for args in self.parent.addheaders:
  733.             h.putheader(*args)
  734.         
  735.         for k, v in req.headers.items():
  736.             h.putheader(k, v)
  737.         
  738.         h.endheaders()
  739.         if req.has_data():
  740.             h.send(data)
  741.         
  742.         (code, msg, hdrs) = h.getreply()
  743.         fp = h.getfile()
  744.         if code == 200:
  745.             return addinfourl(fp, hdrs, req.get_full_url())
  746.         else:
  747.             return self.parent.error('http', req, fp, code, msg, hdrs)
  748.  
  749.  
  750.  
  751. class HTTPHandler(AbstractHTTPHandler):
  752.     
  753.     def http_open(self, req):
  754.         return self.do_open(httplib.HTTP, req)
  755.  
  756.  
  757. if hasattr(httplib, 'HTTPS'):
  758.     
  759.     class HTTPSHandler(AbstractHTTPHandler):
  760.         
  761.         def https_open(self, req):
  762.             return self.do_open(httplib.HTTPS, req)
  763.  
  764.  
  765.  
  766.  
  767. class UnknownHandler(BaseHandler):
  768.     
  769.     def unknown_open(self, req):
  770.         type = req.get_type()
  771.         raise URLError('unknown url type: %s' % type)
  772.  
  773.  
  774.  
  775. def parse_keqv_list(l):
  776.     parsed = { }
  777.     for elt in l:
  778.         (k, v) = elt.split('=', 1)
  779.         if v[0] == '"' and v[-1] == '"':
  780.             v = v[1:-1]
  781.         
  782.         parsed[k] = v
  783.     
  784.     return parsed
  785.  
  786.  
  787. def parse_http_list(s):
  788.     list = []
  789.     end = len(s)
  790.     i = 0
  791.     inquote = 0
  792.     start = 0
  793.     while i < end:
  794.         cur = s[i:]
  795.         c = cur.find(',')
  796.         q = cur.find('"')
  797.         if c == -1:
  798.             list.append(s[start:])
  799.             break
  800.         
  801.         if q == -1:
  802.             if inquote:
  803.                 raise ValueError, 'unbalanced quotes'
  804.             else:
  805.                 list.append(s[start:i + c])
  806.                 i = i + c + 1
  807.         
  808.         if inquote:
  809.             if q < c:
  810.                 list.append(s[start:i + c])
  811.                 i = i + c + 1
  812.                 start = i
  813.                 inquote = 0
  814.             else:
  815.                 i = i + q
  816.         elif c < q:
  817.             list.append(s[start:i + c])
  818.             i = i + c + 1
  819.             start = i
  820.         else:
  821.             inquote = 1
  822.             i = i + q + 1
  823.     return map((lambda x: x.strip()), list)
  824.  
  825.  
  826. class FileHandler(BaseHandler):
  827.     
  828.     def file_open(self, req):
  829.         url = req.get_selector()
  830.         if url[:2] == '//' and url[2:3] != '/':
  831.             req.type = 'ftp'
  832.             return self.parent.open(req)
  833.         else:
  834.             return self.open_local_file(req)
  835.  
  836.     names = None
  837.     
  838.     def get_names(self):
  839.         if FileHandler.names is None:
  840.             FileHandler.names = (socket.gethostbyname('localhost'), socket.gethostbyname(socket.gethostname()))
  841.         
  842.         return FileHandler.names
  843.  
  844.     
  845.     def open_local_file(self, req):
  846.         host = req.get_host()
  847.         file = req.get_selector()
  848.         localfile = url2pathname(file)
  849.         stats = os.stat(localfile)
  850.         size = stats[stat.ST_SIZE]
  851.         modified = rfc822.formatdate(stats[stat.ST_MTIME])
  852.         mtype = mimetypes.guess_type(file)[0]
  853.         stats = os.stat(localfile)
  854.         if not mtype:
  855.             pass
  856.         headers = mimetools.Message(StringIO('Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % ('text/plain', size, modified)))
  857.         if host:
  858.             (host, port) = splitport(host)
  859.         
  860.         if not host and not port and socket.gethostbyname(host) in self.get_names():
  861.             return addinfourl(open(localfile, 'rb'), headers, 'file:' + file)
  862.         
  863.         raise URLError('file not on local host')
  864.  
  865.  
  866.  
  867. class FTPHandler(BaseHandler):
  868.     
  869.     def ftp_open(self, req):
  870.         host = req.get_host()
  871.         if not host:
  872.             raise IOError, ('ftp error', 'no host given')
  873.         
  874.         
  875.         try:
  876.             host = socket.gethostbyname(host)
  877.         except socket.error:
  878.             msg = None
  879.             raise URLError(msg)
  880.  
  881.         (host, port) = splitport(host)
  882.         if port is None:
  883.             port = ftplib.FTP_PORT
  884.         
  885.         (path, attrs) = splitattr(req.get_selector())
  886.         path = unquote(path)
  887.         dirs = path.split('/')
  888.         (dirs, file) = (dirs[:-1], dirs[-1])
  889.         if dirs and not dirs[0]:
  890.             dirs = dirs[1:]
  891.         
  892.         user = passwd = ''
  893.         
  894.         try:
  895.             fw = self.connect_ftp(user, passwd, host, port, dirs)
  896.             if not file and 'I':
  897.                 pass
  898.             type = 'D'
  899.             for attr in attrs:
  900.                 (attr, value) = splitattr(attr)
  901.                 if attr.lower() == 'type' and value in ('a', 'A', 'i', 'I', 'd', 'D'):
  902.                     type = value.upper()
  903.                 
  904.             
  905.             (fp, retrlen) = fw.retrfile(file, type)
  906.             headers = ''
  907.             mtype = mimetypes.guess_type(req.get_full_url())[0]
  908.             if mtype:
  909.                 headers += 'Content-Type: %s\n' % mtype
  910.             
  911.             if retrlen is not None and retrlen >= 0:
  912.                 headers += 'Content-Length: %d\n' % retrlen
  913.             
  914.             sf = StringIO(headers)
  915.             headers = mimetools.Message(sf)
  916.             return addinfourl(fp, headers, req.get_full_url())
  917.         except ftplib.all_errors:
  918.             msg = None
  919.             raise IOError, ('ftp error', msg), sys.exc_info()[2]
  920.  
  921.  
  922.     
  923.     def connect_ftp(self, user, passwd, host, port, dirs):
  924.         fw = ftpwrapper(user, passwd, host, port, dirs)
  925.         return fw
  926.  
  927.  
  928.  
  929. class CacheFTPHandler(FTPHandler):
  930.     
  931.     def __init__(self):
  932.         self.cache = { }
  933.         self.timeout = { }
  934.         self.soonest = 0
  935.         self.delay = 60
  936.         self.max_conns = 16
  937.  
  938.     
  939.     def setTimeout(self, t):
  940.         self.delay = t
  941.  
  942.     
  943.     def setMaxConns(self, m):
  944.         self.max_conns = m
  945.  
  946.     
  947.     def connect_ftp(self, user, passwd, host, port, dirs):
  948.         key = (user, passwd, host, port)
  949.         if self.cache.has_key(key):
  950.             self.timeout[key] = time.time() + self.delay
  951.         else:
  952.             self.cache[key] = ftpwrapper(user, passwd, host, port, dirs)
  953.             self.timeout[key] = time.time() + self.delay
  954.         self.check_cache()
  955.         return self.cache[key]
  956.  
  957.     
  958.     def check_cache(self):
  959.         t = time.time()
  960.         if self.soonest <= t:
  961.             for k, v in self.timeout.items():
  962.                 if v < t:
  963.                     self.cache[k].close()
  964.                     del self.cache[k]
  965.                     del self.timeout[k]
  966.                 
  967.             
  968.         
  969.         self.soonest = min(self.timeout.values())
  970.         if len(self.cache) == self.max_conns:
  971.             for k, v in self.timeout.items():
  972.                 if v == self.soonest:
  973.                     del self.cache[k]
  974.                     del self.timeout[k]
  975.                     break
  976.                 
  977.             
  978.             self.soonest = min(self.timeout.values())
  979.         
  980.  
  981.  
  982.  
  983. class GopherHandler(BaseHandler):
  984.     
  985.     def gopher_open(self, req):
  986.         host = req.get_host()
  987.         if not host:
  988.             raise GopherError('no host given')
  989.         
  990.         host = unquote(host)
  991.         selector = req.get_selector()
  992.         (type, selector) = splitgophertype(selector)
  993.         (selector, query) = splitquery(selector)
  994.         selector = unquote(selector)
  995.         if query:
  996.             query = unquote(query)
  997.             fp = gopherlib.send_query(selector, query, host)
  998.         else:
  999.             fp = gopherlib.send_selector(selector, host)
  1000.         return addinfourl(fp, noheaders(), req.get_full_url())
  1001.  
  1002.  
  1003.  
  1004. class OpenerFactory:
  1005.     default_handlers = [
  1006.         UnknownHandler,
  1007.         HTTPHandler,
  1008.         HTTPDefaultErrorHandler,
  1009.         HTTPRedirectHandler,
  1010.         FTPHandler,
  1011.         FileHandler]
  1012.     proxy_handlers = [
  1013.         ProxyHandler]
  1014.     handlers = []
  1015.     replacement_handlers = []
  1016.     
  1017.     def add_proxy_handler(self, ph):
  1018.         self.proxy_handlers = self.proxy_handlers + [
  1019.             ph]
  1020.  
  1021.     
  1022.     def add_handler(self, h):
  1023.         self.handlers = self.handlers + [
  1024.             h]
  1025.  
  1026.     
  1027.     def replace_handler(self, h):
  1028.         pass
  1029.  
  1030.     
  1031.     def build_opener(self):
  1032.         opener = OpenerDirector()
  1033.         for ph in self.proxy_handlers:
  1034.             if inspect.isclass(ph):
  1035.                 ph = ph()
  1036.             
  1037.             opener.add_handler(ph)
  1038.         
  1039.  
  1040.  
  1041. if __name__ == '__main__':
  1042.     if socket.gethostname() == 'bitdiddle':
  1043.         localhost = 'bitdiddle.cnri.reston.va.us'
  1044.     elif socket.gethostname() == 'bitdiddle.concentric.net':
  1045.         localhost = 'localhost'
  1046.     else:
  1047.         localhost = None
  1048.     urls = [
  1049.         'gopher://gopher.lib.ncsu.edu/11/library/stacks/Alex',
  1050.         'gopher://gopher.vt.edu:10010/10/33',
  1051.         'file:/etc/passwd',
  1052.         'file://nonsensename/etc/passwd',
  1053.         'ftp://www.python.org/pub/python/misc/sousa.au',
  1054.         'ftp://www.python.org/pub/tmp/blat',
  1055.         'http://www.espn.com/',
  1056.         'http://www.python.org/Spanish/Inquistion/',
  1057.         ('http://www.python.org/cgi-bin/faqw.py', 'query=pythonistas&querytype=simple&casefold=yes&req=search'),
  1058.         'http://www.python.org/',
  1059.         'ftp://gatekeeper.research.compaq.com/pub/DEC/SRC/research-reports/00README-Legal-Rules-Regs']
  1060.     cfh = CacheFTPHandler()
  1061.     cfh.setTimeout(1)
  1062.     install_opener(build_opener(cfh, GopherHandler))
  1063.     for url in urls:
  1064.         if isinstance(url, types.TupleType):
  1065.             (url, req) = url
  1066.         else:
  1067.             req = None
  1068.         print url
  1069.         
  1070.         try:
  1071.             f = urlopen(url, req)
  1072.         except IOError:
  1073.             err = None
  1074.             print 'IOError:', err
  1075.         except socket.error:
  1076.             err = None
  1077.             print 'socket.error:', err
  1078.  
  1079.         buf = f.read()
  1080.         f.close()
  1081.         print 'read %d bytes' % len(buf)
  1082.         print 
  1083.         time.sleep(0.10000000000000001)
  1084.     
  1085.  
  1086.