home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1437 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  92.4 KB  |  2,963 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __version__ = '4.1'
  5. __license__ = "Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE."
  6. __author__ = 'Mark Pilgrim <http://diveintomark.org/>'
  7. __contributors__ = [
  8.     'Jason Diamond <http://injektilo.org/>',
  9.     'John Beimler <http://john.beimler.org/>',
  10.     'Fazal Majid <http://www.majid.info/mylos/weblog/>',
  11.     'Aaron Swartz <http://aaronsw.com/>',
  12.     'Kevin Marks <http://epeus.blogspot.com/>']
  13. _debug = 0
  14. USER_AGENT = 'Mozilla/5.0 (X11; U; i686 Linux; en_US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4'
  15. ACCEPT_HEADER = 'application/atom+xml,application/rdf+xml,application/rss+xml,application/x-netcdf,application/xml;q=0.9,text/xml;q=0.2,*/*;q=0.1'
  16. PREFERRED_XML_PARSERS = [
  17.     'drv_libxml2']
  18. TIDY_MARKUP = 0
  19. PREFERRED_TIDY_INTERFACES = [
  20.     'uTidy',
  21.     'mxTidy']
  22. import sgmllib
  23. import re
  24. import sys
  25. import copy
  26. import urlparse
  27. import time
  28. import rfc822
  29. import types
  30. import cgi
  31. import urllib
  32. import urllib2
  33.  
  34. try:
  35.     from cStringIO import StringIO as _StringIO
  36. except:
  37.     from StringIO import StringIO as _StringIO
  38.  
  39.  
  40. try:
  41.     import gzip
  42. except:
  43.     gzip = None
  44.  
  45.  
  46. try:
  47.     import zlib
  48. except:
  49.     zlib = None
  50.  
  51.  
  52. try:
  53.     import xml.sax as xml
  54.     xml.sax.make_parser(PREFERRED_XML_PARSERS)
  55.     from xml.sax.saxutils import escape as _xmlescape
  56.     _XML_AVAILABLE = 1
  57. except:
  58.     _XML_AVAILABLE = 0
  59.     
  60.     def _xmlescape(data):
  61.         data = data.replace('&', '&')
  62.         data = data.replace('>', '>')
  63.         data = data.replace('<', '<')
  64.         return data
  65.  
  66.  
  67.  
  68. try:
  69.     import base64
  70.     import binascii
  71. except:
  72.     base64 = binascii = None
  73.  
  74.  
  75. try:
  76.     import cjkcodecs.aliases as cjkcodecs
  77. except:
  78.     pass
  79.  
  80.  
  81. try:
  82.     import iconv_codec
  83. except:
  84.     pass
  85.  
  86.  
  87. try:
  88.     import calibre.ebooks.chardet as chardet
  89. except:
  90.     chardet = None
  91.  
  92.  
  93. class ThingsNobodyCaresAboutButMe(Exception):
  94.     pass
  95.  
  96.  
  97. class CharacterEncodingOverride(ThingsNobodyCaresAboutButMe):
  98.     pass
  99.  
  100.  
  101. class CharacterEncodingUnknown(ThingsNobodyCaresAboutButMe):
  102.     pass
  103.  
  104.  
  105. class NonXMLContentType(ThingsNobodyCaresAboutButMe):
  106.     pass
  107.  
  108.  
  109. class UndeclaredNamespace(Exception):
  110.     pass
  111.  
  112. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
  113. sgmllib.special = re.compile('<!')
  114. sgmllib.charref = re.compile('&#(x?[0-9A-Fa-f]+)[^0-9A-Fa-f]')
  115. SUPPORTED_VERSIONS = {
  116.     '': 'unknown',
  117.     'rss090': 'RSS 0.90',
  118.     'rss091n': 'RSS 0.91 (Netscape)',
  119.     'rss091u': 'RSS 0.91 (Userland)',
  120.     'rss092': 'RSS 0.92',
  121.     'rss093': 'RSS 0.93',
  122.     'rss094': 'RSS 0.94',
  123.     'rss20': 'RSS 2.0',
  124.     'rss10': 'RSS 1.0',
  125.     'rss': 'RSS (unknown version)',
  126.     'atom01': 'Atom 0.1',
  127.     'atom02': 'Atom 0.2',
  128.     'atom03': 'Atom 0.3',
  129.     'atom10': 'Atom 1.0',
  130.     'atom': 'Atom (unknown version)',
  131.     'cdf': 'CDF',
  132.     'hotrss': 'Hot RSS' }
  133.  
  134. try:
  135.     UserDict = dict
  136. except NameError:
  137.     from UserDict import UserDict
  138.     
  139.     def dict(aList):
  140.         rc = { }
  141.         for k, v in aList:
  142.             rc[k] = v
  143.         
  144.         return rc
  145.  
  146.  
  147.  
  148. class FeedParserDict(UserDict):
  149.     keymap = {
  150.         'channel': 'feed',
  151.         'items': 'entries',
  152.         'guid': 'id',
  153.         'date': 'updated',
  154.         'date_parsed': 'updated_parsed',
  155.         'description': [
  156.             'subtitle',
  157.             'summary'],
  158.         'url': [
  159.             'href'],
  160.         'modified': 'updated',
  161.         'modified_parsed': 'updated_parsed',
  162.         'issued': 'published',
  163.         'issued_parsed': 'published_parsed',
  164.         'copyright': 'rights',
  165.         'copyright_detail': 'rights_detail',
  166.         'tagline': 'subtitle',
  167.         'tagline_detail': 'subtitle_detail' }
  168.     
  169.     def __getitem__(self, key):
  170.         if key == 'category':
  171.             return UserDict.__getitem__(self, 'tags')[0]['term']
  172.         if key == 'categories':
  173.             return [ (tag['scheme'], tag['term']) for tag in UserDict.__getitem__(self, 'tags') ]
  174.         realkey = self.keymap.get(key, key)
  175.         if UserDict.has_key(self, key):
  176.             return UserDict.__getitem__(self, key)
  177.         return UserDict.__getitem__(self, realkey)
  178.  
  179.     
  180.     def __setitem__(self, key, value):
  181.         for k in self.keymap.keys():
  182.             if key == k:
  183.                 key = self.keymap[k]
  184.                 if type(key) == types.ListType:
  185.                     key = key[0]
  186.                 
  187.             type(key) == types.ListType
  188.         
  189.         return UserDict.__setitem__(self, key, value)
  190.  
  191.     
  192.     def get(self, key, default = None):
  193.         if self.has_key(key):
  194.             return self[key]
  195.         return default
  196.  
  197.     
  198.     def setdefault(self, key, value):
  199.         if not self.has_key(key):
  200.             self[key] = value
  201.         
  202.         return self[key]
  203.  
  204.     
  205.     def has_key(self, key):
  206.         
  207.         try:
  208.             if not hasattr(self, key):
  209.                 pass
  210.             return UserDict.has_key(self, key)
  211.         except AttributeError:
  212.             return False
  213.  
  214.  
  215.     
  216.     def __getattr__(self, key):
  217.         
  218.         try:
  219.             return self.__dict__[key]
  220.         except KeyError:
  221.             pass
  222.  
  223.         
  224.         try:
  225.             return self.__getitem__(key)
  226.         except:
  227.             raise AttributeError, "object has no attribute '%s'" % key
  228.  
  229.  
  230.     
  231.     def __setattr__(self, key, value):
  232.         if key.startswith('_') or key == 'data':
  233.             self.__dict__[key] = value
  234.         else:
  235.             return self.__setitem__(key, value)
  236.         return key == 'data'
  237.  
  238.     
  239.     def __contains__(self, key):
  240.         return self.has_key(key)
  241.  
  242.  
  243.  
  244. def zopeCompatibilityHack():
  245.     global FeedParserDict, FeedParserDict
  246.     del FeedParserDict
  247.     
  248.     def FeedParserDict(aDict = None):
  249.         rc = { }
  250.         if aDict:
  251.             rc.update(aDict)
  252.         
  253.         return rc
  254.  
  255.  
  256. _ebcdic_to_ascii_map = None
  257.  
  258. def _ebcdic_to_ascii(s):
  259.     global _ebcdic_to_ascii_map
  260.     if not _ebcdic_to_ascii_map:
  261.         emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  262.         import string
  263.         _ebcdic_to_ascii_map = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  264.     
  265.     return s.translate(_ebcdic_to_ascii_map)
  266.  
  267. _urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
  268.  
  269. def _urljoin(base, uri):
  270.     uri = _urifixer.sub('\\1\\3', uri)
  271.     return urlparse.urljoin(base, uri)
  272.  
  273.  
  274. class _FeedParserMixin:
  275.     namespaces = {
  276.         '': '',
  277.         'http://backend.userland.com/rss': '',
  278.         'http://blogs.law.harvard.edu/tech/rss': '',
  279.         'http://purl.org/rss/1.0/': '',
  280.         'http://my.netscape.com/rdf/simple/0.9/': '',
  281.         'http://example.com/newformat#': '',
  282.         'http://example.com/necho': '',
  283.         'http://purl.org/echo/': '',
  284.         'uri/of/echo/namespace#': '',
  285.         'http://purl.org/pie/': '',
  286.         'http://purl.org/atom/ns#': '',
  287.         'http://www.w3.org/2005/Atom': '',
  288.         'http://purl.org/rss/1.0/modules/rss091#': '',
  289.         'http://webns.net/mvcb/': 'admin',
  290.         'http://purl.org/rss/1.0/modules/aggregation/': 'ag',
  291.         'http://purl.org/rss/1.0/modules/annotate/': 'annotate',
  292.         'http://media.tangent.org/rss/1.0/': 'audio',
  293.         'http://backend.userland.com/blogChannelModule': 'blogChannel',
  294.         'http://web.resource.org/cc/': 'cc',
  295.         'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons',
  296.         'http://purl.org/rss/1.0/modules/company': 'co',
  297.         'http://purl.org/rss/1.0/modules/content/': 'content',
  298.         'http://my.theinfo.org/changed/1.0/rss/': 'cp',
  299.         'http://purl.org/dc/elements/1.1/': 'dc',
  300.         'http://purl.org/dc/terms/': 'dcterms',
  301.         'http://purl.org/rss/1.0/modules/email/': 'email',
  302.         'http://purl.org/rss/1.0/modules/event/': 'ev',
  303.         'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner',
  304.         'http://freshmeat.net/rss/fm/': 'fm',
  305.         'http://xmlns.com/foaf/0.1/': 'foaf',
  306.         'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo',
  307.         'http://postneo.com/icbm/': 'icbm',
  308.         'http://purl.org/rss/1.0/modules/image/': 'image',
  309.         'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes',
  310.         'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes',
  311.         'http://purl.org/rss/1.0/modules/link/': 'l',
  312.         'http://search.yahoo.com/mrss': 'media',
  313.         'http://madskills.com/public/xml/rss/module/pingback/': 'pingback',
  314.         'http://prismstandard.org/namespaces/1.2/basic/': 'prism',
  315.         'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf',
  316.         'http://www.w3.org/2000/01/rdf-schema#': 'rdfs',
  317.         'http://purl.org/rss/1.0/modules/reference/': 'ref',
  318.         'http://purl.org/rss/1.0/modules/richequiv/': 'reqv',
  319.         'http://purl.org/rss/1.0/modules/search/': 'search',
  320.         'http://purl.org/rss/1.0/modules/slash/': 'slash',
  321.         'http://schemas.xmlsoap.org/soap/envelope/': 'soap',
  322.         'http://purl.org/rss/1.0/modules/servicestatus/': 'ss',
  323.         'http://hacks.benhammersley.com/rss/streaming/': 'str',
  324.         'http://purl.org/rss/1.0/modules/subscription/': 'sub',
  325.         'http://purl.org/rss/1.0/modules/syndication/': 'sy',
  326.         'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo',
  327.         'http://purl.org/rss/1.0/modules/threading/': 'thr',
  328.         'http://purl.org/rss/1.0/modules/textinput/': 'ti',
  329.         'http://madskills.com/public/xml/rss/module/trackback/': 'trackback',
  330.         'http://wellformedweb.org/commentAPI/': 'wfw',
  331.         'http://purl.org/rss/1.0/modules/wiki/': 'wiki',
  332.         'http://www.w3.org/1999/xhtml': 'xhtml',
  333.         'http://www.w3.org/XML/1998/namespace': 'xml',
  334.         'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf' }
  335.     _matchnamespaces = { }
  336.     can_be_relative_uri = [
  337.         'link',
  338.         'id',
  339.         'wfw_comment',
  340.         'wfw_commentrss',
  341.         'docs',
  342.         'url',
  343.         'href',
  344.         'comments',
  345.         'license',
  346.         'icon',
  347.         'logo']
  348.     can_contain_relative_uris = [
  349.         'content',
  350.         'title',
  351.         'summary',
  352.         'info',
  353.         'tagline',
  354.         'subtitle',
  355.         'copyright',
  356.         'rights',
  357.         'description']
  358.     can_contain_dangerous_markup = [
  359.         'content',
  360.         'title',
  361.         'summary',
  362.         'info',
  363.         'tagline',
  364.         'subtitle',
  365.         'copyright',
  366.         'rights',
  367.         'description']
  368.     html_types = [
  369.         'text/html',
  370.         'application/xhtml+xml']
  371.     
  372.     def __init__(self, baseuri = None, baselang = None, encoding = 'utf-8'):
  373.         if _debug:
  374.             sys.stderr.write('initializing FeedParser\n')
  375.         
  376.         if not self._matchnamespaces:
  377.             for k, v in self.namespaces.items():
  378.                 self._matchnamespaces[k.lower()] = v
  379.             
  380.         
  381.         self.feeddata = FeedParserDict()
  382.         self.encoding = encoding
  383.         self.entries = []
  384.         self.version = ''
  385.         self.namespacesInUse = { }
  386.         self.infeed = 0
  387.         self.inentry = 0
  388.         self.incontent = 0
  389.         self.intextinput = 0
  390.         self.inimage = 0
  391.         self.inauthor = 0
  392.         self.incontributor = 0
  393.         self.inpublisher = 0
  394.         self.insource = 0
  395.         self.sourcedata = FeedParserDict()
  396.         self.contentparams = FeedParserDict()
  397.         self._summaryKey = None
  398.         self.namespacemap = { }
  399.         self.elementstack = []
  400.         self.basestack = []
  401.         self.langstack = []
  402.         if not baseuri:
  403.             pass
  404.         self.baseuri = ''
  405.         if not baselang:
  406.             pass
  407.         self.lang = None
  408.         if baselang:
  409.             self.feeddata['language'] = baselang
  410.         
  411.  
  412.     
  413.     def unknown_starttag(self, tag, attrs):
  414.         if _debug:
  415.             sys.stderr.write('start %s with %s\n' % (tag, attrs))
  416.         
  417.         attrs = [ (k.lower(), v) for k, v in attrs ]
  418.         attrs = [ (k, v) for k, v in attrs ]
  419.         attrsD = dict(attrs)
  420.         if not attrsD.get('xml:base', attrsD.get('base')):
  421.             pass
  422.         baseuri = self.baseuri
  423.         self.baseuri = _urljoin(self.baseuri, baseuri)
  424.         lang = attrsD.get('xml:lang', attrsD.get('lang'))
  425.         if lang == '':
  426.             lang = None
  427.         elif lang is None:
  428.             lang = self.lang
  429.         
  430.         self.lang = lang
  431.         self.basestack.append(self.baseuri)
  432.         self.langstack.append(lang)
  433.         for prefix, uri in attrs:
  434.             if prefix.startswith('xmlns:'):
  435.                 self.trackNamespace(prefix[6:], uri)
  436.                 continue
  437.             None if lang else []
  438.             if prefix == 'xmlns':
  439.                 self.trackNamespace(None, uri)
  440.                 continue
  441.         
  442.         if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  443.             self.contentparams['type'] = 'application/xhtml+xml'
  444.         
  445.         if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
  446.             tag = tag.split(':')[-1]
  447.             return tag(''.join % ([], []([ ' %s="%s"' % t for t in attrs ])), escape = 0)
  448.         if tag.find(':') != -1:
  449.             (prefix, suffix) = tag.split(':', 1)
  450.         else:
  451.             prefix = ''
  452.             suffix = tag
  453.         prefix = self.namespacemap.get(prefix, prefix)
  454.         if prefix:
  455.             prefix = prefix + '_'
  456.         
  457.         if not prefix and tag not in ('title', 'link', 'description', 'name'):
  458.             self.intextinput = 0
  459.         
  460.         if not prefix and tag not in ('title', 'link', 'description', 'url', 'href', 'width', 'height'):
  461.             self.inimage = 0
  462.         
  463.         methodname = '_start_' + prefix + suffix
  464.         
  465.         try:
  466.             method = getattr(self, methodname)
  467.             return method(attrsD)
  468.         except AttributeError:
  469.             return self.push(prefix + suffix, 1)
  470.  
  471.  
  472.     
  473.     def unknown_endtag(self, tag):
  474.         if _debug:
  475.             sys.stderr.write('end %s\n' % tag)
  476.         
  477.         if tag.find(':') != -1:
  478.             (prefix, suffix) = tag.split(':', 1)
  479.         else:
  480.             prefix = ''
  481.             suffix = tag
  482.         prefix = self.namespacemap.get(prefix, prefix)
  483.         if prefix:
  484.             prefix = prefix + '_'
  485.         
  486.         methodname = '_end_' + prefix + suffix
  487.         
  488.         try:
  489.             method = getattr(self, methodname)
  490.             method()
  491.         except AttributeError:
  492.             self.pop(prefix + suffix)
  493.  
  494.         if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  495.             self.contentparams['type'] = 'application/xhtml+xml'
  496.         
  497.         if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
  498.             tag = tag.split(':')[-1]
  499.             self.handle_data('</%s>' % tag, escape = 0)
  500.         
  501.         if self.basestack:
  502.             self.basestack.pop()
  503.             if self.basestack and self.basestack[-1]:
  504.                 self.baseuri = self.basestack[-1]
  505.             
  506.         
  507.         if self.langstack:
  508.             self.langstack.pop()
  509.             if self.langstack:
  510.                 self.lang = self.langstack[-1]
  511.             
  512.         
  513.  
  514.     
  515.     def handle_charref(self, ref):
  516.         if not self.elementstack:
  517.             return None
  518.         ref = ref.lower()
  519.         if ref in ('34', '38', '39', '60', '62', 'x22', 'x26', 'x27', 'x3c', 'x3e'):
  520.             text = '&#%s;' % ref
  521.         elif ref[0] == 'x':
  522.             c = int(ref[1:], 16)
  523.         else:
  524.             c = int(ref)
  525.         text = unichr(c).encode('utf-8')
  526.         self.elementstack[-1][2].append(text)
  527.  
  528.     
  529.     def handle_entityref(self, ref):
  530.         if not self.elementstack:
  531.             return None
  532.         if _debug:
  533.             sys.stderr.write('entering handle_entityref with %s\n' % ref)
  534.         
  535.         if ref in ('lt', 'gt', 'quot', 'amp', 'apos'):
  536.             text = '&%s;' % ref
  537.         else:
  538.             
  539.             def name2cp(k):
  540.                 import htmlentitydefs
  541.                 if hasattr(htmlentitydefs, 'name2codepoint'):
  542.                     return htmlentitydefs.name2codepoint[k]
  543.                 k = htmlentitydefs.entitydefs[k]
  544.                 if k.startswith('&#') and k.endswith(';'):
  545.                     return int(k[2:-1])
  546.                 return ord(k)
  547.  
  548.             
  549.             try:
  550.                 name2cp(ref)
  551.             except KeyError:
  552.                 text = '&%s;' % ref
  553.  
  554.             text = unichr(name2cp(ref)).encode('utf-8')
  555.         self.elementstack[-1][2].append(text)
  556.  
  557.     
  558.     def handle_data(self, text, escape = 1):
  559.         if not self.elementstack:
  560.             return None
  561.         if escape and self.contentparams.get('type') == 'application/xhtml+xml':
  562.             text = _xmlescape(text)
  563.         
  564.         self.elementstack[-1][2].append(text)
  565.  
  566.     
  567.     def handle_comment(self, text):
  568.         pass
  569.  
  570.     
  571.     def handle_pi(self, text):
  572.         pass
  573.  
  574.     
  575.     def handle_decl(self, text):
  576.         pass
  577.  
  578.     
  579.     def parse_declaration(self, i):
  580.         if _debug:
  581.             sys.stderr.write('entering parse_declaration\n')
  582.         
  583.         if self.rawdata[i:i + 9] == '<![CDATA[':
  584.             k = self.rawdata.find(']]>', i)
  585.             if k == -1:
  586.                 k = len(self.rawdata)
  587.             
  588.             self.handle_data(_xmlescape(self.rawdata[i + 9:k]), 0)
  589.             return k + 3
  590.         k = self.rawdata.find('>', i)
  591.         return k + 1
  592.  
  593.     
  594.     def mapContentType(self, contentType):
  595.         contentType = contentType.lower()
  596.         if contentType == 'text':
  597.             contentType = 'text/plain'
  598.         elif contentType == 'html':
  599.             contentType = 'text/html'
  600.         elif contentType == 'xhtml':
  601.             contentType = 'application/xhtml+xml'
  602.         
  603.         return contentType
  604.  
  605.     
  606.     def trackNamespace(self, prefix, uri):
  607.         loweruri = uri.lower()
  608.         if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not (self.version):
  609.             self.version = 'rss090'
  610.         
  611.         if loweruri == 'http://purl.org/rss/1.0/' and not (self.version):
  612.             self.version = 'rss10'
  613.         
  614.         if loweruri == 'http://www.w3.org/2005/atom' and not (self.version):
  615.             self.version = 'atom10'
  616.         
  617.         if loweruri.find('backend.userland.com/rss') != -1:
  618.             uri = 'http://backend.userland.com/rss'
  619.             loweruri = uri
  620.         
  621.         if self._matchnamespaces.has_key(loweruri):
  622.             self.namespacemap[prefix] = self._matchnamespaces[loweruri]
  623.             self.namespacesInUse[self._matchnamespaces[loweruri]] = uri
  624.         elif not prefix:
  625.             pass
  626.         self.namespacesInUse[''] = uri
  627.  
  628.     
  629.     def resolveURI(self, uri):
  630.         if not self.baseuri:
  631.             pass
  632.         return _urljoin('', uri)
  633.  
  634.     
  635.     def decodeEntities(self, element, data):
  636.         return data
  637.  
  638.     
  639.     def push(self, element, expectingText):
  640.         self.elementstack.append([
  641.             element,
  642.             expectingText,
  643.             []])
  644.  
  645.     
  646.     def pop(self, element, stripWhitespace = 1):
  647.         if not self.elementstack:
  648.             return None
  649.         if self.elementstack[-1][0] != element:
  650.             return None
  651.         (element, expectingText, pieces) = self.elementstack.pop()
  652.         output = ''.join(pieces)
  653.         if not expectingText:
  654.             return output
  655.         if base64 and self.contentparams.get('base64', 0):
  656.             
  657.             try:
  658.                 output = base64.decodestring(output)
  659.             except binascii.Error:
  660.                 expectingText
  661.                 expectingText
  662.                 None if stripWhitespace else self.elementstack
  663.             except binascii.Incomplete:
  664.                 expectingText
  665.                 expectingText
  666.                 expectingText
  667.             except:
  668.                 expectingText<EXCEPTION MATCH>binascii.Error
  669.             
  670.  
  671.         expectingText
  672.         if not self.contentparams.get('base64', 0):
  673.             output = self.decodeEntities(element, output)
  674.         
  675.         
  676.         try:
  677.             del self.contentparams['mode']
  678.         except KeyError:
  679.             pass
  680.  
  681.         
  682.         try:
  683.             del self.contentparams['base64']
  684.         except KeyError:
  685.             pass
  686.  
  687.         if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
  688.             if element in self.can_contain_relative_uris:
  689.                 output = _resolveRelativeURIs(output, self.baseuri, self.encoding)
  690.             
  691.         
  692.         if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
  693.             if element in self.can_contain_dangerous_markup:
  694.                 output = _sanitizeHTML(output, self.encoding)
  695.             
  696.         
  697.         if self.encoding and type(output) != type(u''):
  698.             
  699.             try:
  700.                 output = unicode(output, self.encoding)
  701.  
  702.         
  703.         if element == 'category':
  704.             return output
  705.         if self.inentry and not (self.insource):
  706.             if element == 'content':
  707.                 self.entries[-1].setdefault(element, [])
  708.                 contentparams = copy.deepcopy(self.contentparams)
  709.                 contentparams['value'] = output
  710.                 self.entries[-1][element].append(contentparams)
  711.             elif element == 'link':
  712.                 self.entries[-1][element] = output
  713.                 if output:
  714.                     self.entries[-1]['links'][-1]['href'] = output
  715.                 
  716.             elif element == 'description':
  717.                 element = 'summary'
  718.             
  719.             self.entries[-1][element] = output
  720.             if self.incontent:
  721.                 contentparams = copy.deepcopy(self.contentparams)
  722.                 contentparams['value'] = output
  723.                 self.entries[-1][element + '_detail'] = contentparams
  724.             
  725.         elif (self.infeed or self.insource) and not (self.intextinput) and not (self.inimage):
  726.             context = self._getContext()
  727.             if element == 'description':
  728.                 element = 'subtitle'
  729.             
  730.             context[element] = output
  731.             if element == 'link':
  732.                 context['links'][-1]['href'] = output
  733.             elif self.incontent:
  734.                 contentparams = copy.deepcopy(self.contentparams)
  735.                 contentparams['value'] = output
  736.                 context[element + '_detail'] = contentparams
  737.             
  738.         
  739.         return output
  740.  
  741.     
  742.     def pushContent(self, tag, attrsD, defaultContentType, expectingText):
  743.         self.incontent += 1
  744.         self.contentparams = FeedParserDict({
  745.             'type': self.mapContentType(attrsD.get('type', defaultContentType)),
  746.             'language': self.lang,
  747.             'base': self.baseuri })
  748.         self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
  749.         self.push(tag, expectingText)
  750.  
  751.     
  752.     def popContent(self, tag):
  753.         value = self.pop(tag)
  754.         self.incontent -= 1
  755.         self.contentparams.clear()
  756.         return value
  757.  
  758.     
  759.     def _mapToStandardPrefix(self, name):
  760.         colonpos = name.find(':')
  761.         if colonpos != -1:
  762.             prefix = name[:colonpos]
  763.             suffix = name[colonpos + 1:]
  764.             prefix = self.namespacemap.get(prefix, prefix)
  765.             name = prefix + ':' + suffix
  766.         
  767.         return name
  768.  
  769.     
  770.     def _getAttribute(self, attrsD, name):
  771.         return attrsD.get(self._mapToStandardPrefix(name))
  772.  
  773.     
  774.     def _isBase64(self, attrsD, contentparams):
  775.         if attrsD.get('mode', '') == 'base64':
  776.             return 1
  777.         if self.contentparams['type'].startswith('text/'):
  778.             return 0
  779.         if self.contentparams['type'].endswith('+xml'):
  780.             return 0
  781.         if self.contentparams['type'].endswith('/xml'):
  782.             return 0
  783.         return 1
  784.  
  785.     
  786.     def _itsAnHrefDamnIt(self, attrsD):
  787.         href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
  788.         if href:
  789.             
  790.             try:
  791.                 del attrsD['url']
  792.             except KeyError:
  793.                 pass
  794.  
  795.             
  796.             try:
  797.                 del attrsD['uri']
  798.             except KeyError:
  799.                 pass
  800.  
  801.             attrsD['href'] = href
  802.         
  803.         return attrsD
  804.  
  805.     
  806.     def _save(self, key, value):
  807.         context = self._getContext()
  808.         context.setdefault(key, value)
  809.  
  810.     
  811.     def _start_rss(self, attrsD):
  812.         versionmap = {
  813.             '0.91': 'rss091u',
  814.             '0.92': 'rss092',
  815.             '0.93': 'rss093',
  816.             '0.94': 'rss094' }
  817.         if not self.version:
  818.             attr_version = attrsD.get('version', '')
  819.             version = versionmap.get(attr_version)
  820.             if version:
  821.                 self.version = version
  822.             elif attr_version.startswith('2.'):
  823.                 self.version = 'rss20'
  824.             else:
  825.                 self.version = 'rss'
  826.         
  827.  
  828.     
  829.     def _start_dlhottitles(self, attrsD):
  830.         self.version = 'hotrss'
  831.  
  832.     
  833.     def _start_channel(self, attrsD):
  834.         self.infeed = 1
  835.         self._cdf_common(attrsD)
  836.  
  837.     _start_feedinfo = _start_channel
  838.     
  839.     def _cdf_common(self, attrsD):
  840.         if attrsD.has_key('lastmod'):
  841.             self._start_modified({ })
  842.             self.elementstack[-1][-1] = attrsD['lastmod']
  843.             self._end_modified()
  844.         
  845.         if attrsD.has_key('href'):
  846.             self._start_link({ })
  847.             self.elementstack[-1][-1] = attrsD['href']
  848.             self._end_link()
  849.         
  850.  
  851.     
  852.     def _start_feed(self, attrsD):
  853.         self.infeed = 1
  854.         versionmap = {
  855.             '0.1': 'atom01',
  856.             '0.2': 'atom02',
  857.             '0.3': 'atom03' }
  858.         if not self.version:
  859.             attr_version = attrsD.get('version')
  860.             version = versionmap.get(attr_version)
  861.             if version:
  862.                 self.version = version
  863.             else:
  864.                 self.version = 'atom'
  865.         
  866.  
  867.     
  868.     def _end_channel(self):
  869.         self.infeed = 0
  870.  
  871.     _end_feed = _end_channel
  872.     
  873.     def _start_image(self, attrsD):
  874.         self.inimage = 1
  875.         self.push('image', 0)
  876.         context = self._getContext()
  877.         context.setdefault('image', FeedParserDict())
  878.  
  879.     
  880.     def _end_image(self):
  881.         self.pop('image')
  882.         self.inimage = 0
  883.  
  884.     
  885.     def _start_textinput(self, attrsD):
  886.         self.intextinput = 1
  887.         self.push('textinput', 0)
  888.         context = self._getContext()
  889.         context.setdefault('textinput', FeedParserDict())
  890.  
  891.     _start_textInput = _start_textinput
  892.     
  893.     def _end_textinput(self):
  894.         self.pop('textinput')
  895.         self.intextinput = 0
  896.  
  897.     _end_textInput = _end_textinput
  898.     
  899.     def _start_author(self, attrsD):
  900.         self.inauthor = 1
  901.         self.push('author', 1)
  902.  
  903.     _start_managingeditor = _start_author
  904.     _start_dc_author = _start_author
  905.     _start_dc_creator = _start_author
  906.     _start_itunes_author = _start_author
  907.     
  908.     def _end_author(self):
  909.         self.pop('author')
  910.         self.inauthor = 0
  911.         self._sync_author_detail()
  912.  
  913.     _end_managingeditor = _end_author
  914.     _end_dc_author = _end_author
  915.     _end_dc_creator = _end_author
  916.     _end_itunes_author = _end_author
  917.     
  918.     def _start_itunes_owner(self, attrsD):
  919.         self.inpublisher = 1
  920.         self.push('publisher', 0)
  921.  
  922.     
  923.     def _end_itunes_owner(self):
  924.         self.pop('publisher')
  925.         self.inpublisher = 0
  926.         self._sync_author_detail('publisher')
  927.  
  928.     
  929.     def _start_contributor(self, attrsD):
  930.         self.incontributor = 1
  931.         context = self._getContext()
  932.         context.setdefault('contributors', [])
  933.         context['contributors'].append(FeedParserDict())
  934.         self.push('contributor', 0)
  935.  
  936.     
  937.     def _end_contributor(self):
  938.         self.pop('contributor')
  939.         self.incontributor = 0
  940.  
  941.     
  942.     def _start_dc_contributor(self, attrsD):
  943.         self.incontributor = 1
  944.         context = self._getContext()
  945.         context.setdefault('contributors', [])
  946.         context['contributors'].append(FeedParserDict())
  947.         self.push('name', 0)
  948.  
  949.     
  950.     def _end_dc_contributor(self):
  951.         self._end_name()
  952.         self.incontributor = 0
  953.  
  954.     
  955.     def _start_name(self, attrsD):
  956.         self.push('name', 0)
  957.  
  958.     _start_itunes_name = _start_name
  959.     
  960.     def _end_name(self):
  961.         value = self.pop('name')
  962.         if self.inpublisher:
  963.             self._save_author('name', value, 'publisher')
  964.         elif self.inauthor:
  965.             self._save_author('name', value)
  966.         elif self.incontributor:
  967.             self._save_contributor('name', value)
  968.         elif self.intextinput:
  969.             context = self._getContext()
  970.             context['textinput']['name'] = value
  971.         
  972.  
  973.     _end_itunes_name = _end_name
  974.     
  975.     def _start_width(self, attrsD):
  976.         self.push('width', 0)
  977.  
  978.     
  979.     def _end_width(self):
  980.         value = self.pop('width')
  981.         
  982.         try:
  983.             value = int(value)
  984.         except:
  985.             value = 0
  986.  
  987.         if self.inimage:
  988.             context = self._getContext()
  989.             context['image']['width'] = value
  990.         
  991.  
  992.     
  993.     def _start_height(self, attrsD):
  994.         self.push('height', 0)
  995.  
  996.     
  997.     def _end_height(self):
  998.         value = self.pop('height')
  999.         
  1000.         try:
  1001.             value = int(value)
  1002.         except:
  1003.             value = 0
  1004.  
  1005.         if self.inimage:
  1006.             context = self._getContext()
  1007.             context['image']['height'] = value
  1008.         
  1009.  
  1010.     
  1011.     def _start_url(self, attrsD):
  1012.         self.push('href', 1)
  1013.  
  1014.     _start_homepage = _start_url
  1015.     _start_uri = _start_url
  1016.     
  1017.     def _end_url(self):
  1018.         value = self.pop('href')
  1019.         if self.inauthor:
  1020.             self._save_author('href', value)
  1021.         elif self.incontributor:
  1022.             self._save_contributor('href', value)
  1023.         elif self.inimage:
  1024.             context = self._getContext()
  1025.             context['image']['href'] = value
  1026.         elif self.intextinput:
  1027.             context = self._getContext()
  1028.             context['textinput']['link'] = value
  1029.         
  1030.  
  1031.     _end_homepage = _end_url
  1032.     _end_uri = _end_url
  1033.     
  1034.     def _start_email(self, attrsD):
  1035.         self.push('email', 0)
  1036.  
  1037.     _start_itunes_email = _start_email
  1038.     
  1039.     def _end_email(self):
  1040.         value = self.pop('email')
  1041.         if self.inpublisher:
  1042.             self._save_author('email', value, 'publisher')
  1043.         elif self.inauthor:
  1044.             self._save_author('email', value)
  1045.         elif self.incontributor:
  1046.             self._save_contributor('email', value)
  1047.         
  1048.  
  1049.     _end_itunes_email = _end_email
  1050.     
  1051.     def _getContext(self):
  1052.         if self.insource:
  1053.             context = self.sourcedata
  1054.         elif self.inentry:
  1055.             context = self.entries[-1]
  1056.         else:
  1057.             context = self.feeddata
  1058.         return context
  1059.  
  1060.     
  1061.     def _save_author(self, key, value, prefix = 'author'):
  1062.         context = self._getContext()
  1063.         context.setdefault(prefix + '_detail', FeedParserDict())
  1064.         context[prefix + '_detail'][key] = value
  1065.         self._sync_author_detail()
  1066.  
  1067.     
  1068.     def _save_contributor(self, key, value):
  1069.         context = self._getContext()
  1070.         context.setdefault('contributors', [
  1071.             FeedParserDict()])
  1072.         context['contributors'][-1][key] = value
  1073.  
  1074.     
  1075.     def _sync_author_detail(self, key = 'author'):
  1076.         context = self._getContext()
  1077.         detail = context.get('%s_detail' % key)
  1078.         if detail:
  1079.             name = detail.get('name')
  1080.             email = detail.get('email')
  1081.             if name and email:
  1082.                 context[key] = '%s (%s)' % (name, email)
  1083.             elif name:
  1084.                 context[key] = name
  1085.             elif email:
  1086.                 context[key] = email
  1087.             
  1088.         else:
  1089.             author = context.get(key)
  1090.             if not author:
  1091.                 return None
  1092.             emailmatch = re.search('(([a-zA-Z0-9\\_\\-\\.\\+]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?))', author)
  1093.             if not emailmatch:
  1094.                 return None
  1095.             email = emailmatch.group(0)
  1096.             author = author.replace(email, '')
  1097.             author = author.replace('()', '')
  1098.             author = author.strip()
  1099.             if author and author[-1] == ')':
  1100.                 author = author[:-1]
  1101.             
  1102.             author = author.strip()
  1103.             context.setdefault('%s_detail' % key, FeedParserDict())
  1104.             context['%s_detail' % key]['name'] = author
  1105.             context['%s_detail' % key]['email'] = email
  1106.  
  1107.     
  1108.     def _start_subtitle(self, attrsD):
  1109.         self.pushContent('subtitle', attrsD, 'text/plain', 1)
  1110.  
  1111.     _start_tagline = _start_subtitle
  1112.     _start_itunes_subtitle = _start_subtitle
  1113.     
  1114.     def _end_subtitle(self):
  1115.         self.popContent('subtitle')
  1116.  
  1117.     _end_tagline = _end_subtitle
  1118.     _end_itunes_subtitle = _end_subtitle
  1119.     
  1120.     def _start_rights(self, attrsD):
  1121.         self.pushContent('rights', attrsD, 'text/plain', 1)
  1122.  
  1123.     _start_dc_rights = _start_rights
  1124.     _start_copyright = _start_rights
  1125.     
  1126.     def _end_rights(self):
  1127.         self.popContent('rights')
  1128.  
  1129.     _end_dc_rights = _end_rights
  1130.     _end_copyright = _end_rights
  1131.     
  1132.     def _start_item(self, attrsD):
  1133.         self.entries.append(FeedParserDict())
  1134.         self.push('item', 0)
  1135.         self.inentry = 1
  1136.         self.guidislink = 0
  1137.         id = self._getAttribute(attrsD, 'rdf:about')
  1138.         if id:
  1139.             context = self._getContext()
  1140.             context['id'] = id
  1141.         
  1142.         self._cdf_common(attrsD)
  1143.  
  1144.     _start_entry = _start_item
  1145.     _start_product = _start_item
  1146.     
  1147.     def _end_item(self):
  1148.         self.pop('item')
  1149.         self.inentry = 0
  1150.  
  1151.     _end_entry = _end_item
  1152.     
  1153.     def _start_dc_language(self, attrsD):
  1154.         self.push('language', 1)
  1155.  
  1156.     _start_language = _start_dc_language
  1157.     
  1158.     def _end_dc_language(self):
  1159.         self.lang = self.pop('language')
  1160.  
  1161.     _end_language = _end_dc_language
  1162.     
  1163.     def _start_dc_publisher(self, attrsD):
  1164.         self.push('publisher', 1)
  1165.  
  1166.     _start_webmaster = _start_dc_publisher
  1167.     
  1168.     def _end_dc_publisher(self):
  1169.         self.pop('publisher')
  1170.         self._sync_author_detail('publisher')
  1171.  
  1172.     _end_webmaster = _end_dc_publisher
  1173.     
  1174.     def _start_published(self, attrsD):
  1175.         self.push('published', 1)
  1176.  
  1177.     _start_dcterms_issued = _start_published
  1178.     _start_issued = _start_published
  1179.     
  1180.     def _end_published(self):
  1181.         value = self.pop('published')
  1182.         self._save('published_parsed', _parse_date(value))
  1183.  
  1184.     _end_dcterms_issued = _end_published
  1185.     _end_issued = _end_published
  1186.     
  1187.     def _start_updated(self, attrsD):
  1188.         self.push('updated', 1)
  1189.  
  1190.     _start_modified = _start_updated
  1191.     _start_dcterms_modified = _start_updated
  1192.     _start_pubdate = _start_updated
  1193.     _start_dc_date = _start_updated
  1194.     
  1195.     def _end_updated(self):
  1196.         value = self.pop('updated')
  1197.         parsed_value = _parse_date(value)
  1198.         self._save('updated_parsed', parsed_value)
  1199.  
  1200.     _end_modified = _end_updated
  1201.     _end_dcterms_modified = _end_updated
  1202.     _end_pubdate = _end_updated
  1203.     _end_dc_date = _end_updated
  1204.     
  1205.     def _start_created(self, attrsD):
  1206.         self.push('created', 1)
  1207.  
  1208.     _start_dcterms_created = _start_created
  1209.     
  1210.     def _end_created(self):
  1211.         value = self.pop('created')
  1212.         self._save('created_parsed', _parse_date(value))
  1213.  
  1214.     _end_dcterms_created = _end_created
  1215.     
  1216.     def _start_expirationdate(self, attrsD):
  1217.         self.push('expired', 1)
  1218.  
  1219.     
  1220.     def _end_expirationdate(self):
  1221.         self._save('expired_parsed', _parse_date(self.pop('expired')))
  1222.  
  1223.     
  1224.     def _start_cc_license(self, attrsD):
  1225.         self.push('license', 1)
  1226.         value = self._getAttribute(attrsD, 'rdf:resource')
  1227.         if value:
  1228.             self.elementstack[-1][2].append(value)
  1229.         
  1230.         self.pop('license')
  1231.  
  1232.     
  1233.     def _start_creativecommons_license(self, attrsD):
  1234.         self.push('license', 1)
  1235.  
  1236.     
  1237.     def _end_creativecommons_license(self):
  1238.         self.pop('license')
  1239.  
  1240.     
  1241.     def _addTag(self, term, scheme, label):
  1242.         context = self._getContext()
  1243.         tags = context.setdefault('tags', [])
  1244.         if not term and not scheme and not label:
  1245.             return None
  1246.         value = FeedParserDict({
  1247.             'term': term,
  1248.             'scheme': scheme,
  1249.             'label': label })
  1250.         if value not in tags:
  1251.             tags.append(FeedParserDict({
  1252.                 'term': term,
  1253.                 'scheme': scheme,
  1254.                 'label': label }))
  1255.         
  1256.  
  1257.     
  1258.     def _start_category(self, attrsD):
  1259.         if _debug:
  1260.             sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
  1261.         
  1262.         term = attrsD.get('term')
  1263.         scheme = attrsD.get('scheme', attrsD.get('domain'))
  1264.         label = attrsD.get('label')
  1265.         self._addTag(term, scheme, label)
  1266.         self.push('category', 1)
  1267.  
  1268.     _start_dc_subject = _start_category
  1269.     _start_keywords = _start_category
  1270.     
  1271.     def _end_itunes_keywords(self):
  1272.         for term in self.pop('itunes_keywords').split():
  1273.             self._addTag(term, 'http://www.itunes.com/', None)
  1274.         
  1275.  
  1276.     
  1277.     def _start_itunes_category(self, attrsD):
  1278.         self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
  1279.         self.push('category', 1)
  1280.  
  1281.     
  1282.     def _end_category(self):
  1283.         value = self.pop('category')
  1284.         if not value:
  1285.             return None
  1286.         context = self._getContext()
  1287.         tags = context['tags']
  1288.         if value and len(tags) and not tags[-1]['term']:
  1289.             tags[-1]['term'] = value
  1290.         else:
  1291.             self._addTag(value, None, None)
  1292.  
  1293.     _end_dc_subject = _end_category
  1294.     _end_keywords = _end_category
  1295.     _end_itunes_category = _end_category
  1296.     
  1297.     def _start_cloud(self, attrsD):
  1298.         self._getContext()['cloud'] = FeedParserDict(attrsD)
  1299.  
  1300.     
  1301.     def _start_link(self, attrsD):
  1302.         attrsD.setdefault('rel', 'alternate')
  1303.         attrsD.setdefault('type', 'text/html')
  1304.         attrsD = self._itsAnHrefDamnIt(attrsD)
  1305.         if attrsD.has_key('href'):
  1306.             attrsD['href'] = self.resolveURI(attrsD['href'])
  1307.         
  1308.         if not self.infeed and self.inentry:
  1309.             pass
  1310.         expectingText = self.insource
  1311.         context = self._getContext()
  1312.         context.setdefault('links', [])
  1313.         context['links'].append(FeedParserDict(attrsD))
  1314.         if attrsD['rel'] == 'enclosure':
  1315.             self._start_enclosure(attrsD)
  1316.         
  1317.         if attrsD.has_key('href'):
  1318.             expectingText = 0
  1319.             if attrsD.get('rel') == 'alternate' and self.mapContentType(attrsD.get('type')) in self.html_types:
  1320.                 context['link'] = attrsD['href']
  1321.             
  1322.         else:
  1323.             self.push('link', expectingText)
  1324.  
  1325.     _start_producturl = _start_link
  1326.     
  1327.     def _end_link(self):
  1328.         value = self.pop('link')
  1329.         context = self._getContext()
  1330.         if self.intextinput:
  1331.             context['textinput']['link'] = value
  1332.         
  1333.         if self.inimage:
  1334.             context['image']['link'] = value
  1335.         
  1336.  
  1337.     _end_producturl = _end_link
  1338.     
  1339.     def _start_guid(self, attrsD):
  1340.         self.guidislink = attrsD.get('ispermalink', 'true') == 'true'
  1341.         self.push('id', 1)
  1342.  
  1343.     
  1344.     def _end_guid(self):
  1345.         value = self.pop('id')
  1346.         if self.guidislink:
  1347.             pass
  1348.         self._save('guidislink', not self._getContext().has_key('link'))
  1349.         if self.guidislink:
  1350.             self._save('link', value)
  1351.         
  1352.  
  1353.     
  1354.     def _start_title(self, attrsD):
  1355.         if not self.infeed and self.inentry:
  1356.             pass
  1357.         self.pushContent('title', attrsD, 'text/plain', self.insource)
  1358.  
  1359.     _start_dc_title = _start_title
  1360.     _start_media_title = _start_title
  1361.     
  1362.     def _end_title(self):
  1363.         value = self.popContent('title')
  1364.         context = self._getContext()
  1365.         if self.intextinput:
  1366.             context['textinput']['title'] = value
  1367.         elif self.inimage:
  1368.             context['image']['title'] = value
  1369.         
  1370.  
  1371.     _end_dc_title = _end_title
  1372.     _end_media_title = _end_title
  1373.     
  1374.     def _start_description(self, attrsD):
  1375.         context = self._getContext()
  1376.         if context.has_key('summary'):
  1377.             self._summaryKey = 'content'
  1378.             self._start_content(attrsD)
  1379.         elif not self.infeed and self.inentry:
  1380.             pass
  1381.         self.pushContent('description', attrsD, 'text/html', self.insource)
  1382.  
  1383.     
  1384.     def _start_abstract(self, attrsD):
  1385.         if not self.infeed and self.inentry:
  1386.             pass
  1387.         self.pushContent('description', attrsD, 'text/plain', self.insource)
  1388.  
  1389.     
  1390.     def _end_description(self):
  1391.         if self._summaryKey == 'content':
  1392.             self._end_content()
  1393.         else:
  1394.             value = self.popContent('description')
  1395.             context = self._getContext()
  1396.             if self.intextinput:
  1397.                 context['textinput']['description'] = value
  1398.             elif self.inimage:
  1399.                 context['image']['description'] = value
  1400.             
  1401.         self._summaryKey = None
  1402.  
  1403.     _end_abstract = _end_description
  1404.     
  1405.     def _start_info(self, attrsD):
  1406.         self.pushContent('info', attrsD, 'text/plain', 1)
  1407.  
  1408.     _start_feedburner_browserfriendly = _start_info
  1409.     
  1410.     def _end_info(self):
  1411.         self.popContent('info')
  1412.  
  1413.     _end_feedburner_browserfriendly = _end_info
  1414.     
  1415.     def _start_generator(self, attrsD):
  1416.         if attrsD:
  1417.             attrsD = self._itsAnHrefDamnIt(attrsD)
  1418.             if attrsD.has_key('href'):
  1419.                 attrsD['href'] = self.resolveURI(attrsD['href'])
  1420.             
  1421.         
  1422.         self._getContext()['generator_detail'] = FeedParserDict(attrsD)
  1423.         self.push('generator', 1)
  1424.  
  1425.     
  1426.     def _end_generator(self):
  1427.         value = self.pop('generator')
  1428.         context = self._getContext()
  1429.         if context.has_key('generator_detail'):
  1430.             context['generator_detail']['name'] = value
  1431.         
  1432.  
  1433.     
  1434.     def _start_admin_generatoragent(self, attrsD):
  1435.         self.push('generator', 1)
  1436.         value = self._getAttribute(attrsD, 'rdf:resource')
  1437.         if value:
  1438.             self.elementstack[-1][2].append(value)
  1439.         
  1440.         self.pop('generator')
  1441.         self._getContext()['generator_detail'] = FeedParserDict({
  1442.             'href': value })
  1443.  
  1444.     
  1445.     def _start_admin_errorreportsto(self, attrsD):
  1446.         self.push('errorreportsto', 1)
  1447.         value = self._getAttribute(attrsD, 'rdf:resource')
  1448.         if value:
  1449.             self.elementstack[-1][2].append(value)
  1450.         
  1451.         self.pop('errorreportsto')
  1452.  
  1453.     
  1454.     def _start_summary(self, attrsD):
  1455.         context = self._getContext()
  1456.         if context.has_key('summary'):
  1457.             self._summaryKey = 'content'
  1458.             self._start_content(attrsD)
  1459.         else:
  1460.             self._summaryKey = 'summary'
  1461.             self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
  1462.  
  1463.     _start_itunes_summary = _start_summary
  1464.     
  1465.     def _end_summary(self):
  1466.         if self._summaryKey == 'content':
  1467.             self._end_content()
  1468.         elif not self._summaryKey:
  1469.             pass
  1470.         self.popContent('summary')
  1471.         self._summaryKey = None
  1472.  
  1473.     _end_itunes_summary = _end_summary
  1474.     
  1475.     def _start_enclosure(self, attrsD):
  1476.         attrsD = self._itsAnHrefDamnIt(attrsD)
  1477.         self._getContext().setdefault('enclosures', []).append(FeedParserDict(attrsD))
  1478.         href = attrsD.get('href')
  1479.         if href:
  1480.             context = self._getContext()
  1481.             if not context.get('id'):
  1482.                 context['id'] = href
  1483.             
  1484.         
  1485.  
  1486.     
  1487.     def _start_source(self, attrsD):
  1488.         self.insource = 1
  1489.  
  1490.     
  1491.     def _end_source(self):
  1492.         self.insource = 0
  1493.         self._getContext()['source'] = copy.deepcopy(self.sourcedata)
  1494.         self.sourcedata.clear()
  1495.  
  1496.     
  1497.     def _start_content(self, attrsD):
  1498.         self.pushContent('content', attrsD, 'text/plain', 1)
  1499.         src = attrsD.get('src')
  1500.         if src:
  1501.             self.contentparams['src'] = src
  1502.         
  1503.         self.push('content', 1)
  1504.  
  1505.     
  1506.     def _start_prodlink(self, attrsD):
  1507.         self.pushContent('content', attrsD, 'text/html', 1)
  1508.  
  1509.     
  1510.     def _start_body(self, attrsD):
  1511.         self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
  1512.  
  1513.     _start_xhtml_body = _start_body
  1514.     
  1515.     def _start_content_encoded(self, attrsD):
  1516.         self.pushContent('content', attrsD, 'text/html', 1)
  1517.  
  1518.     _start_fullitem = _start_content_encoded
  1519.     
  1520.     def _end_content(self):
  1521.         copyToDescription = self.mapContentType(self.contentparams.get('type')) in [
  1522.             'text/plain'] + self.html_types
  1523.         value = self.popContent('content')
  1524.         if copyToDescription:
  1525.             self._save('description', value)
  1526.         
  1527.  
  1528.     _end_body = _end_content
  1529.     _end_xhtml_body = _end_content
  1530.     _end_content_encoded = _end_content
  1531.     _end_fullitem = _end_content
  1532.     _end_prodlink = _end_content
  1533.     
  1534.     def _start_itunes_image(self, attrsD):
  1535.         self.push('itunes_image', 0)
  1536.         self._getContext()['image'] = FeedParserDict({
  1537.             'href': attrsD.get('href') })
  1538.  
  1539.     _start_itunes_link = _start_itunes_image
  1540.     
  1541.     def _end_itunes_block(self):
  1542.         value = self.pop('itunes_block', 0)
  1543.         if not value == 'yes' or 1:
  1544.             pass
  1545.         self._getContext()['itunes_block'] = 0
  1546.  
  1547.     
  1548.     def _end_itunes_explicit(self):
  1549.         value = self.pop('itunes_explicit', 0)
  1550.         if not value == 'yes' or 1:
  1551.             pass
  1552.         self._getContext()['itunes_explicit'] = 0
  1553.  
  1554.  
  1555. if _XML_AVAILABLE:
  1556.     
  1557.     class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
  1558.         
  1559.         def __init__(self, baseuri, baselang, encoding):
  1560.             if _debug:
  1561.                 sys.stderr.write('trying StrictFeedParser\n')
  1562.             
  1563.             xml.sax.handler.ContentHandler.__init__(self)
  1564.             _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
  1565.             self.bozo = 0
  1566.             self.exc = None
  1567.  
  1568.         
  1569.         def startPrefixMapping(self, prefix, uri):
  1570.             self.trackNamespace(prefix, uri)
  1571.  
  1572.         
  1573.         def startElementNS(self, name, qname, attrs):
  1574.             (namespace, localname) = name
  1575.             if not namespace:
  1576.                 pass
  1577.             lowernamespace = str('').lower()
  1578.             if lowernamespace.find('backend.userland.com/rss') != -1:
  1579.                 namespace = 'http://backend.userland.com/rss'
  1580.                 lowernamespace = namespace
  1581.             
  1582.             if qname and qname.find(':') > 0:
  1583.                 givenprefix = qname.split(':')[0]
  1584.             else:
  1585.                 givenprefix = None
  1586.             prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
  1587.             if givenprefix:
  1588.                 if (prefix == None or prefix == '' or lowernamespace == '') and not self.namespacesInUse.has_key(givenprefix):
  1589.                     raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
  1590.             not self.namespacesInUse.has_key(givenprefix)
  1591.             if prefix:
  1592.                 localname = prefix + ':' + localname
  1593.             
  1594.             localname = str(localname).lower()
  1595.             if _debug:
  1596.                 sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
  1597.             
  1598.             attrsD = { }
  1599.             for namespace, attrlocalname in attrs._attrs.items():
  1600.                 attrvalue = None
  1601.                 if not namespace:
  1602.                     pass
  1603.                 lowernamespace = ''.lower()
  1604.                 prefix = self._matchnamespaces.get(lowernamespace, '')
  1605.                 if prefix:
  1606.                     attrlocalname = prefix + ':' + attrlocalname
  1607.                 
  1608.                 attrsD[str(attrlocalname).lower()] = attrvalue
  1609.             
  1610.             for qname in attrs.getQNames():
  1611.                 attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
  1612.             
  1613.             self.unknown_starttag(localname, attrsD.items())
  1614.  
  1615.         
  1616.         def characters(self, text):
  1617.             self.handle_data(text)
  1618.  
  1619.         
  1620.         def endElementNS(self, name, qname):
  1621.             (namespace, localname) = name
  1622.             if not namespace:
  1623.                 pass
  1624.             lowernamespace = str('').lower()
  1625.             if qname and qname.find(':') > 0:
  1626.                 givenprefix = qname.split(':')[0]
  1627.             else:
  1628.                 givenprefix = ''
  1629.             prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
  1630.             if prefix:
  1631.                 localname = prefix + ':' + localname
  1632.             
  1633.             localname = str(localname).lower()
  1634.             self.unknown_endtag(localname)
  1635.  
  1636.         
  1637.         def error(self, exc):
  1638.             self.bozo = 1
  1639.             self.exc = exc
  1640.  
  1641.         
  1642.         def fatalError(self, exc):
  1643.             self.error(exc)
  1644.             raise exc
  1645.  
  1646.  
  1647.  
  1648.  
  1649. class _BaseHTMLProcessor(sgmllib.SGMLParser):
  1650.     elements_no_end_tag = [
  1651.         'area',
  1652.         'base',
  1653.         'basefont',
  1654.         'br',
  1655.         'col',
  1656.         'frame',
  1657.         'hr',
  1658.         'img',
  1659.         'input',
  1660.         'isindex',
  1661.         'link',
  1662.         'meta',
  1663.         'param']
  1664.     
  1665.     def __init__(self, encoding):
  1666.         self.encoding = encoding
  1667.         if _debug:
  1668.             sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
  1669.         
  1670.         sgmllib.SGMLParser.__init__(self)
  1671.  
  1672.     
  1673.     def reset(self):
  1674.         self.pieces = []
  1675.         sgmllib.SGMLParser.reset(self)
  1676.  
  1677.     
  1678.     def _shorttag_replace(self, match):
  1679.         tag = match.group(1)
  1680.         if tag in self.elements_no_end_tag:
  1681.             return '<' + tag + ' />'
  1682.         return '<' + tag + '></' + tag + '>'
  1683.  
  1684.     
  1685.     def feed(self, data):
  1686.         data = re.compile('<!((?!DOCTYPE|--|\\[))', re.IGNORECASE).sub('<!\\1', data)
  1687.         data = re.sub('<([^<\\s]+?)\\s*/>', self._shorttag_replace, data)
  1688.         data = data.replace(''', "'")
  1689.         data = data.replace('"', '"')
  1690.         if self.encoding and type(data) == type(u''):
  1691.             data = data.encode(self.encoding)
  1692.         
  1693.         sgmllib.SGMLParser.feed(self, data)
  1694.  
  1695.     
  1696.     def normalize_attrs(self, attrs):
  1697.         attrs = [ (k.lower(), v) for k, v in attrs ]
  1698.         attrs = [ (k, v) for k, v in attrs ]
  1699.         return attrs
  1700.  
  1701.     
  1702.     def unknown_starttag(self, tag, attrs):
  1703.         if _debug:
  1704.             sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
  1705.         
  1706.         uattrs = []
  1707.         for key, value in attrs:
  1708.             if type(value) != type(u''):
  1709.                 value = unicode(value, self.encoding, 'replace')
  1710.             
  1711.             uattrs.append((unicode(key, self.encoding), value))
  1712.         
  1713.         strattrs = []([ u' %s="%s"' % (key, value) for key, value in uattrs ]).encode(self.encoding)
  1714.  
  1715.     
  1716.     def unknown_endtag(self, tag):
  1717.         if tag not in self.elements_no_end_tag:
  1718.             self.pieces.append('</%(tag)s>' % locals())
  1719.         
  1720.  
  1721.     
  1722.     def handle_charref(self, ref):
  1723.         self.pieces.append('&#%(ref)s;' % locals())
  1724.  
  1725.     
  1726.     def handle_entityref(self, ref):
  1727.         self.pieces.append('&%(ref)s;' % locals())
  1728.  
  1729.     
  1730.     def handle_data(self, text):
  1731.         if _debug:
  1732.             sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
  1733.         
  1734.         self.pieces.append(text)
  1735.  
  1736.     
  1737.     def handle_comment(self, text):
  1738.         self.pieces.append('<!--%(text)s-->' % locals())
  1739.  
  1740.     
  1741.     def handle_pi(self, text):
  1742.         self.pieces.append('<?%(text)s>' % locals())
  1743.  
  1744.     
  1745.     def handle_decl(self, text):
  1746.         self.pieces.append('<!%(text)s>' % locals())
  1747.  
  1748.     _new_declname_match = re.compile('[a-zA-Z][-_.a-zA-Z0-9:]*\\s*').match
  1749.     
  1750.     def _scan_name(self, i, declstartpos):
  1751.         rawdata = self.rawdata
  1752.         n = len(rawdata)
  1753.         if i == n:
  1754.             return (None, -1)
  1755.         m = self._new_declname_match(rawdata, i)
  1756.         if m:
  1757.             s = m.group()
  1758.             name = s.strip()
  1759.             if i + len(s) == n:
  1760.                 return (None, -1)
  1761.             return (name.lower(), m.end())
  1762.         self.handle_data(rawdata)
  1763.         return (None, -1)
  1764.  
  1765.     
  1766.     def output(self):
  1767.         return []([ str(p) for p in self.pieces ])
  1768.  
  1769.  
  1770.  
  1771. class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
  1772.     
  1773.     def __init__(self, baseuri, baselang, encoding):
  1774.         sgmllib.SGMLParser.__init__(self)
  1775.         _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
  1776.  
  1777.     
  1778.     def decodeEntities(self, element, data):
  1779.         data = data.replace('<', '<')
  1780.         data = data.replace('<', '<')
  1781.         data = data.replace('>', '>')
  1782.         data = data.replace('>', '>')
  1783.         data = data.replace('&', '&')
  1784.         data = data.replace('&', '&')
  1785.         data = data.replace('"', '"')
  1786.         data = data.replace('"', '"')
  1787.         data = data.replace(''', ''')
  1788.         data = data.replace(''', ''')
  1789.         if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  1790.             data = data.replace('<', '<')
  1791.             data = data.replace('>', '>')
  1792.             data = data.replace('&', '&')
  1793.             data = data.replace('"', '"')
  1794.             data = data.replace(''', "'")
  1795.         
  1796.         return data
  1797.  
  1798.  
  1799.  
  1800. class _RelativeURIResolver(_BaseHTMLProcessor):
  1801.     relative_uris = [
  1802.         ('a', 'href'),
  1803.         ('applet', 'codebase'),
  1804.         ('area', 'href'),
  1805.         ('blockquote', 'cite'),
  1806.         ('body', 'background'),
  1807.         ('del', 'cite'),
  1808.         ('form', 'action'),
  1809.         ('frame', 'longdesc'),
  1810.         ('frame', 'src'),
  1811.         ('iframe', 'longdesc'),
  1812.         ('iframe', 'src'),
  1813.         ('head', 'profile'),
  1814.         ('img', 'longdesc'),
  1815.         ('img', 'src'),
  1816.         ('img', 'usemap'),
  1817.         ('input', 'src'),
  1818.         ('input', 'usemap'),
  1819.         ('ins', 'cite'),
  1820.         ('link', 'href'),
  1821.         ('object', 'classid'),
  1822.         ('object', 'codebase'),
  1823.         ('object', 'data'),
  1824.         ('object', 'usemap'),
  1825.         ('q', 'cite'),
  1826.         ('script', 'src')]
  1827.     
  1828.     def __init__(self, baseuri, encoding):
  1829.         _BaseHTMLProcessor.__init__(self, encoding)
  1830.         self.baseuri = baseuri
  1831.  
  1832.     
  1833.     def resolveURI(self, uri):
  1834.         return _urljoin(self.baseuri, uri)
  1835.  
  1836.     
  1837.     def unknown_starttag(self, tag, attrs):
  1838.         attrs = self.normalize_attrs(attrs)
  1839.         attrs = [ (key, value) for key, value in attrs ]
  1840.         _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
  1841.  
  1842.  
  1843.  
  1844. def _resolveRelativeURIs(htmlSource, baseURI, encoding):
  1845.     if _debug:
  1846.         sys.stderr.write('entering _resolveRelativeURIs\n')
  1847.     
  1848.     p = _RelativeURIResolver(baseURI, encoding)
  1849.     p.feed(htmlSource)
  1850.     return p.output()
  1851.  
  1852.  
  1853. class _HTMLSanitizer(_BaseHTMLProcessor):
  1854.     acceptable_elements = [
  1855.         'a',
  1856.         'abbr',
  1857.         'acronym',
  1858.         'address',
  1859.         'area',
  1860.         'b',
  1861.         'big',
  1862.         'blockquote',
  1863.         'br',
  1864.         'button',
  1865.         'caption',
  1866.         'center',
  1867.         'cite',
  1868.         'code',
  1869.         'col',
  1870.         'colgroup',
  1871.         'dd',
  1872.         'del',
  1873.         'dfn',
  1874.         'dir',
  1875.         'div',
  1876.         'dl',
  1877.         'dt',
  1878.         'em',
  1879.         'fieldset',
  1880.         'font',
  1881.         'form',
  1882.         'h1',
  1883.         'h2',
  1884.         'h3',
  1885.         'h4',
  1886.         'h5',
  1887.         'h6',
  1888.         'hr',
  1889.         'i',
  1890.         'img',
  1891.         'input',
  1892.         'ins',
  1893.         'kbd',
  1894.         'label',
  1895.         'legend',
  1896.         'li',
  1897.         'map',
  1898.         'menu',
  1899.         'ol',
  1900.         'optgroup',
  1901.         'option',
  1902.         'p',
  1903.         'pre',
  1904.         'q',
  1905.         's',
  1906.         'samp',
  1907.         'select',
  1908.         'small',
  1909.         'span',
  1910.         'strike',
  1911.         'strong',
  1912.         'sub',
  1913.         'sup',
  1914.         'table',
  1915.         'tbody',
  1916.         'td',
  1917.         'textarea',
  1918.         'tfoot',
  1919.         'th',
  1920.         'thead',
  1921.         'tr',
  1922.         'tt',
  1923.         'u',
  1924.         'ul',
  1925.         'var']
  1926.     acceptable_attributes = [
  1927.         'abbr',
  1928.         'accept',
  1929.         'accept-charset',
  1930.         'accesskey',
  1931.         'action',
  1932.         'align',
  1933.         'alt',
  1934.         'axis',
  1935.         'border',
  1936.         'cellpadding',
  1937.         'cellspacing',
  1938.         'char',
  1939.         'charoff',
  1940.         'charset',
  1941.         'checked',
  1942.         'cite',
  1943.         'class',
  1944.         'clear',
  1945.         'cols',
  1946.         'colspan',
  1947.         'color',
  1948.         'compact',
  1949.         'coords',
  1950.         'datetime',
  1951.         'dir',
  1952.         'disabled',
  1953.         'enctype',
  1954.         'for',
  1955.         'frame',
  1956.         'headers',
  1957.         'height',
  1958.         'href',
  1959.         'hreflang',
  1960.         'hspace',
  1961.         'id',
  1962.         'ismap',
  1963.         'label',
  1964.         'lang',
  1965.         'longdesc',
  1966.         'maxlength',
  1967.         'media',
  1968.         'method',
  1969.         'multiple',
  1970.         'name',
  1971.         'nohref',
  1972.         'noshade',
  1973.         'nowrap',
  1974.         'prompt',
  1975.         'readonly',
  1976.         'rel',
  1977.         'rev',
  1978.         'rows',
  1979.         'rowspan',
  1980.         'rules',
  1981.         'scope',
  1982.         'selected',
  1983.         'shape',
  1984.         'size',
  1985.         'span',
  1986.         'src',
  1987.         'start',
  1988.         'summary',
  1989.         'tabindex',
  1990.         'target',
  1991.         'title',
  1992.         'type',
  1993.         'usemap',
  1994.         'valign',
  1995.         'value',
  1996.         'vspace',
  1997.         'width']
  1998.     unacceptable_elements_with_end_tag = [
  1999.         'script',
  2000.         'applet']
  2001.     
  2002.     def reset(self):
  2003.         _BaseHTMLProcessor.reset(self)
  2004.         self.unacceptablestack = 0
  2005.  
  2006.     
  2007.     def unknown_starttag(self, tag, attrs):
  2008.         if tag not in self.acceptable_elements:
  2009.             if tag in self.unacceptable_elements_with_end_tag:
  2010.                 self.unacceptablestack += 1
  2011.             
  2012.             return None
  2013.         attrs = self.normalize_attrs(attrs)
  2014.         attrs = _[1]
  2015.         _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
  2016.  
  2017.     
  2018.     def unknown_endtag(self, tag):
  2019.         if tag not in self.acceptable_elements:
  2020.             if tag in self.unacceptable_elements_with_end_tag:
  2021.                 self.unacceptablestack -= 1
  2022.             
  2023.             return None
  2024.         _BaseHTMLProcessor.unknown_endtag(self, tag)
  2025.  
  2026.     
  2027.     def handle_pi(self, text):
  2028.         pass
  2029.  
  2030.     
  2031.     def handle_decl(self, text):
  2032.         pass
  2033.  
  2034.     
  2035.     def handle_data(self, text):
  2036.         if not self.unacceptablestack:
  2037.             _BaseHTMLProcessor.handle_data(self, text)
  2038.         
  2039.  
  2040.  
  2041.  
  2042. def _sanitizeHTML(htmlSource, encoding):
  2043.     p = _HTMLSanitizer(encoding)
  2044.     p.feed(htmlSource)
  2045.     data = p.output()
  2046.     if TIDY_MARKUP:
  2047.         _tidy = None
  2048.         for tidy_interface in PREFERRED_TIDY_INTERFACES:
  2049.             
  2050.             try:
  2051.                 if tidy_interface == 'uTidy':
  2052.                     _utidy = parseString
  2053.                     import tidy
  2054.                     
  2055.                     def _tidy(data, **kwargs):
  2056.                         return str(_utidy(data, **kwargs))
  2057.  
  2058.                     break
  2059.                 elif tidy_interface == 'mxTidy':
  2060.                     _mxtidy = Tidy
  2061.                     import mx.Tidy
  2062.                     
  2063.                     def _tidy(data, **kwargs):
  2064.                         (nerrors, nwarnings, data, errordata) = _mxtidy.tidy(data, **kwargs)
  2065.                         return data
  2066.  
  2067.                     break
  2068.             continue
  2069.             continue
  2070.  
  2071.         
  2072.         if _tidy:
  2073.             utf8 = type(data) == type(u'')
  2074.             if utf8:
  2075.                 data = data.encode('utf-8')
  2076.             
  2077.             data = _tidy(data, output_xhtml = 1, numeric_entities = 1, wrap = 0, char_encoding = 'utf8')
  2078.             if utf8:
  2079.                 data = unicode(data, 'utf-8')
  2080.             
  2081.             if data.count('<body'):
  2082.                 data = data.split('<body', 1)[1]
  2083.                 if data.count('>'):
  2084.                     data = data.split('>', 1)[1]
  2085.                 
  2086.             
  2087.             if data.count('</body'):
  2088.                 data = data.split('</body', 1)[0]
  2089.             
  2090.         
  2091.     
  2092.     data = data.strip().replace('\r\n', '\n')
  2093.     return data
  2094.  
  2095.  
  2096. class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
  2097.     
  2098.     def http_error_default(self, req, fp, code, msg, headers):
  2099.         if code / 100 == 3 and code != 304:
  2100.             return self.http_error_302(req, fp, code, msg, headers)
  2101.         infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2102.         infourl.status = code
  2103.         return infourl
  2104.  
  2105.     
  2106.     def http_error_302(self, req, fp, code, msg, headers):
  2107.         if headers.dict.has_key('location'):
  2108.             infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
  2109.         else:
  2110.             infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2111.         if not hasattr(infourl, 'status'):
  2112.             infourl.status = code
  2113.         
  2114.         return infourl
  2115.  
  2116.     
  2117.     def http_error_301(self, req, fp, code, msg, headers):
  2118.         if headers.dict.has_key('location'):
  2119.             infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
  2120.         else:
  2121.             infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2122.         if not hasattr(infourl, 'status'):
  2123.             infourl.status = code
  2124.         
  2125.         return infourl
  2126.  
  2127.     http_error_300 = http_error_302
  2128.     http_error_303 = http_error_302
  2129.     http_error_307 = http_error_302
  2130.     
  2131.     def http_error_401(self, req, fp, code, msg, headers):
  2132.         host = urlparse.urlparse(req.get_full_url())[1]
  2133.         
  2134.         try:
  2135.             (user, passw) = base64.decodestring(req.headers['Authorization'].split(' ')[1]).split(':')
  2136.             realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
  2137.             self.add_password(realm, host, user, passw)
  2138.             retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
  2139.             self.reset_retry_count()
  2140.             return retry
  2141.         except:
  2142.             return self.http_error_default(req, fp, code, msg, headers)
  2143.  
  2144.  
  2145.  
  2146.  
  2147. def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers):
  2148.     if hasattr(url_file_stream_or_string, 'read'):
  2149.         return url_file_stream_or_string
  2150.     if url_file_stream_or_string == '-':
  2151.         return sys.stdin
  2152.     
  2153.     try:
  2154.         return open(url_file_stream_or_string)
  2155.     except:
  2156.         hasattr(url_file_stream_or_string, 'read') if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp') else url_file_stream_or_string == '-'
  2157.  
  2158.     return _StringIO(str(url_file_stream_or_string))
  2159.  
  2160. _date_handlers = []
  2161.  
  2162. def registerDateHandler(func):
  2163.     _date_handlers.insert(0, func)
  2164.  
  2165. _iso8601_tmpl = [
  2166.     'YYYY-?MM-?DD',
  2167.     'YYYY-MM',
  2168.     'YYYY-?OOO',
  2169.     'YY-?MM-?DD',
  2170.     'YY-?OOO',
  2171.     'YYYY',
  2172.     '-YY-?MM',
  2173.     '-OOO',
  2174.     '-YY',
  2175.     '--MM-?DD',
  2176.     '--MM',
  2177.     '---DD',
  2178.     'CC',
  2179.     '']
  2180. _iso8601_re = [ tmpl.replace('YYYY', '(?P<year>\\d{4})').replace('YY', '(?P<year>\\d\\d)').replace('MM', '(?P<month>[01]\\d)').replace('DD', '(?P<day>[0123]\\d)').replace('OOO', '(?P<ordinal>[0123]\\d\\d)').replace('CC', '(?P<century>\\d\\d$)') + '(T?(?P<hour>\\d{2}):(?P<minute>\\d{2})' + '(:(?P<second>\\d{2}))?' + '(?P<tz>[+-](?P<tzhour>\\d{2})(:(?P<tzmin>\\d{2}))?|Z)?)?' for tmpl in _iso8601_tmpl ]
  2181. del tmpl
  2182. _iso8601_matches = [ re.compile(regex).match for regex in _iso8601_re ]
  2183. del regex
  2184.  
  2185. def _parse_date_iso8601(dateString):
  2186.     m = None
  2187.     for _iso8601_match in _iso8601_matches:
  2188.         m = _iso8601_match(dateString)
  2189.         if m:
  2190.             break
  2191.             continue
  2192.     
  2193.     if not m:
  2194.         return None
  2195.     if m.span() == (0, 0):
  2196.         return None
  2197.     params = m.groupdict()
  2198.     ordinal = params.get('ordinal', 0)
  2199.     year = params.get('year', '--')
  2200.     if not year or year == '--':
  2201.         year = time.gmtime()[0]
  2202.     elif len(year) == 2:
  2203.         year = 100 * int(time.gmtime()[0] / 100) + int(year)
  2204.     else:
  2205.         year = int(year)
  2206.     month = params.get('month', '-')
  2207.     if not month or month == '-':
  2208.         if ordinal:
  2209.             month = 1
  2210.         else:
  2211.             month = time.gmtime()[1]
  2212.     
  2213.     month = int(month)
  2214.     day = params.get('day', 0)
  2215.     if not day:
  2216.         if ordinal:
  2217.             day = ordinal
  2218.         elif params.get('century', 0) and params.get('year', 0) or params.get('month', 0):
  2219.             day = 1
  2220.         else:
  2221.             day = time.gmtime()[2]
  2222.     else:
  2223.         day = int(day)
  2224.     if 'century' in params.keys():
  2225.         year = (int(params['century']) - 1) * 100 + 1
  2226.     
  2227.     for field in [
  2228.         'hour',
  2229.         'minute',
  2230.         'second',
  2231.         'tzhour',
  2232.         'tzmin']:
  2233.         if not params.get(field, None):
  2234.             params[field] = 0
  2235.             continue
  2236.     
  2237.     hour = int(params.get('hour', 0))
  2238.     minute = int(params.get('minute', 0))
  2239.     second = int(params.get('second', 0))
  2240.     weekday = 0
  2241.     daylight_savings_flag = 0
  2242.     tm = [
  2243.         year,
  2244.         month,
  2245.         day,
  2246.         hour,
  2247.         minute,
  2248.         second,
  2249.         weekday,
  2250.         ordinal,
  2251.         daylight_savings_flag]
  2252.     tz = params.get('tz')
  2253.     if tz and tz != 'Z':
  2254.         if tz[0] == '-':
  2255.             tm[3] += int(params.get('tzhour', 0))
  2256.             tm[4] += int(params.get('tzmin', 0))
  2257.         elif tz[0] == '+':
  2258.             tm[3] -= int(params.get('tzhour', 0))
  2259.             tm[4] -= int(params.get('tzmin', 0))
  2260.         else:
  2261.             return None
  2262.     tz[0] == '-'
  2263.     return time.localtime(time.mktime(tm))
  2264.  
  2265. registerDateHandler(_parse_date_iso8601)
  2266. _korean_year = u'δàä'
  2267. _korean_month = u'∞¢ö'
  2268. _korean_day = u'∞¥╝'
  2269. _korean_am = u'∞ÿñ∞áä'
  2270. _korean_pm = u'∞ÿñφ¢ä'
  2271. _korean_onblog_date_re = re.compile('(\\d{4})%s\\s+(\\d{2})%s\\s+(\\d{2})%s\\s+(\\d{2}):(\\d{2}):(\\d{2})' % (_korean_year, _korean_month, _korean_day))
  2272. _korean_nate_date_re = re.compile(u'(\\d{4})-(\\d{2})-(\\d{2})\\s+(%s|%s)\\s+(\\d{,2}):(\\d{,2}):(\\d{,2})' % (_korean_am, _korean_pm))
  2273.  
  2274. def _parse_date_onblog(dateString):
  2275.     m = _korean_onblog_date_re.match(dateString)
  2276.     if not m:
  2277.         return None
  2278.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2279.         'year': m.group(1),
  2280.         'month': m.group(2),
  2281.         'day': m.group(3),
  2282.         'hour': m.group(4),
  2283.         'minute': m.group(5),
  2284.         'second': m.group(6),
  2285.         'zonediff': '+09:00' }
  2286.     if _debug:
  2287.         sys.stderr.write('OnBlog date parsed as: %s\n' % w3dtfdate)
  2288.     
  2289.     return _parse_date_w3dtf(w3dtfdate)
  2290.  
  2291. registerDateHandler(_parse_date_onblog)
  2292.  
  2293. def _parse_date_nate(dateString):
  2294.     m = _korean_nate_date_re.match(dateString)
  2295.     if not m:
  2296.         return None
  2297.     hour = int(m.group(5))
  2298.     ampm = m.group(4)
  2299.     if ampm == _korean_pm:
  2300.         hour += 12
  2301.     
  2302.     hour = str(hour)
  2303.     if len(hour) == 1:
  2304.         hour = '0' + hour
  2305.     
  2306.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2307.         'year': m.group(1),
  2308.         'month': m.group(2),
  2309.         'day': m.group(3),
  2310.         'hour': hour,
  2311.         'minute': m.group(6),
  2312.         'second': m.group(7),
  2313.         'zonediff': '+09:00' }
  2314.     if _debug:
  2315.         sys.stderr.write('Nate date parsed as: %s\n' % w3dtfdate)
  2316.     
  2317.     return _parse_date_w3dtf(w3dtfdate)
  2318.  
  2319. registerDateHandler(_parse_date_nate)
  2320. _mssql_date_re = re.compile('(\\d{4})-(\\d{2})-(\\d{2})\\s+(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?')
  2321.  
  2322. def _parse_date_mssql(dateString):
  2323.     m = _mssql_date_re.match(dateString)
  2324.     if not m:
  2325.         return None
  2326.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2327.         'year': m.group(1),
  2328.         'month': m.group(2),
  2329.         'day': m.group(3),
  2330.         'hour': m.group(4),
  2331.         'minute': m.group(5),
  2332.         'second': m.group(6),
  2333.         'zonediff': '+09:00' }
  2334.     if _debug:
  2335.         sys.stderr.write('MS SQL date parsed as: %s\n' % w3dtfdate)
  2336.     
  2337.     return _parse_date_w3dtf(w3dtfdate)
  2338.  
  2339. registerDateHandler(_parse_date_mssql)
  2340. _greek_months = {
  2341.     u'╬Ö╬▒╬╜': u'Jan',
  2342.     u'╬ª╬╡╬▓': u'Feb',
  2343.     u'╬£╬¼╧Ä': u'Mar',
  2344.     u'╬£╬▒╧Ä': u'Mar',
  2345.     u'╬æ╧Ç╧ü': u'Apr',
  2346.     u'╬£╬¼╬╣': u'May',
  2347.     u'╬£╬▒╧è': u'May',
  2348.     u'╬£╬▒╬╣': u'May',
  2349.     u'╬Ö╬┐╧ì╬╜': u'Jun',
  2350.     u'╬Ö╬┐╬╜': u'Jun',
  2351.     u'╬Ö╬┐╧ì╬╗': u'Jul',
  2352.     u'╬Ö╬┐╬╗': u'Jul',
  2353.     u'╬æ╧ì╬│': u'Aug',
  2354.     u'╬æ╧à╬│': u'Aug',
  2355.     u'╬ú╬╡╧Ç': u'Sep',
  2356.     u'╬ƒ╬║╧ä': u'Oct',
  2357.     u'╬¥╬┐╬¡': u'Nov',
  2358.     u'╬¥╬┐╬╡': u'Nov',
  2359.     u'╬ö╬╡╬║': u'Dec' }
  2360. _greek_wdays = {
  2361.     u'╬Ü╧à╧ü': u'Sun',
  2362.     u'╬ö╬╡╧à': u'Mon',
  2363.     u'╬ñ╧ü╬╣': u'Tue',
  2364.     u'╬ñ╬╡╧ä': u'Wed',
  2365.     u'╬á╬╡╬╝': u'Thu',
  2366.     u'╬á╬▒╧ü': u'Fri',
  2367.     u'╬ú╬▒╬▓': u'Sat' }
  2368. _greek_date_format_re = re.compile(u'([^,]+),\\s+(\\d{2})\\s+([^\\s]+)\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+([^\\s]+)')
  2369.  
  2370. def _parse_date_greek(dateString):
  2371.     m = _greek_date_format_re.match(dateString)
  2372.     if not m:
  2373.         return None
  2374.     
  2375.     try:
  2376.         wday = _greek_wdays[m.group(1)]
  2377.         month = _greek_months[m.group(3)]
  2378.     except:
  2379.         m
  2380.         return None
  2381.  
  2382.     rfc822date = '%(wday)s, %(day)s %(month)s %(year)s %(hour)s:%(minute)s:%(second)s %(zonediff)s' % {
  2383.         'wday': wday,
  2384.         'day': m.group(2),
  2385.         'month': month,
  2386.         'year': m.group(4),
  2387.         'hour': m.group(5),
  2388.         'minute': m.group(6),
  2389.         'second': m.group(7),
  2390.         'zonediff': m.group(8) }
  2391.     if _debug:
  2392.         sys.stderr.write('Greek date parsed as: %s\n' % rfc822date)
  2393.     
  2394.     return _parse_date_rfc822(rfc822date)
  2395.  
  2396. registerDateHandler(_parse_date_greek)
  2397. _hungarian_months = {
  2398.     u'janu├ír': u'01',
  2399.     u'febru├íri': u'02',
  2400.     u'm├írcius': u'03',
  2401.     u'├íprilis': u'04',
  2402.     u'm├íujus': u'05',
  2403.     u'j├║nius': u'06',
  2404.     u'j├║lius': u'07',
  2405.     u'augusztus': u'08',
  2406.     u'szeptember': u'09',
  2407.     u'okt├│ber': u'10',
  2408.     u'november': u'11',
  2409.     u'december': u'12' }
  2410. _hungarian_date_format_re = re.compile(u'(\\d{4})-([^-]+)-(\\d{,2})T(\\d{,2}):(\\d{2})((\\+|-)(\\d{,2}:\\d{2}))')
  2411.  
  2412. def _parse_date_hungarian(dateString):
  2413.     m = _hungarian_date_format_re.match(dateString)
  2414.     if not m:
  2415.         return None
  2416.     
  2417.     try:
  2418.         month = _hungarian_months[m.group(2)]
  2419.         day = m.group(3)
  2420.         if len(day) == 1:
  2421.             day = '0' + day
  2422.         
  2423.         hour = m.group(4)
  2424.         if len(hour) == 1:
  2425.             hour = '0' + hour
  2426.     except:
  2427.         m
  2428.         return None
  2429.  
  2430.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s%(zonediff)s' % {
  2431.         'year': m.group(1),
  2432.         'month': month,
  2433.         'day': day,
  2434.         'hour': hour,
  2435.         'minute': m.group(5),
  2436.         'zonediff': m.group(6) }
  2437.     if _debug:
  2438.         sys.stderr.write('Hungarian date parsed as: %s\n' % w3dtfdate)
  2439.     
  2440.     return _parse_date_w3dtf(w3dtfdate)
  2441.  
  2442. registerDateHandler(_parse_date_hungarian)
  2443.  
  2444. def _parse_date_w3dtf(dateString):
  2445.     
  2446.     def __extract_date(m):
  2447.         year = int(m.group('year'))
  2448.         if year < 100:
  2449.             year = 100 * int(time.gmtime()[0] / 100) + int(year)
  2450.         
  2451.         if year < 1000:
  2452.             return (0, 0, 0)
  2453.         julian = m.group('julian')
  2454.         if julian:
  2455.             julian = int(julian)
  2456.             month = julian / 30 + 1
  2457.             day = julian % 30 + 1
  2458.             jday = None
  2459.             while jday != julian:
  2460.                 t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
  2461.                 jday = time.gmtime(t)[-2]
  2462.                 diff = abs(jday - julian)
  2463.                 if jday > julian:
  2464.                     if diff < day:
  2465.                         day = day - diff
  2466.                     else:
  2467.                         month = month - 1
  2468.                         day = 31
  2469.                 diff < day
  2470.                 if jday < julian:
  2471.                     if day + diff < 28:
  2472.                         day = day + diff
  2473.                     else:
  2474.                         month = month + 1
  2475.                 day + diff < 28
  2476.                 continue
  2477.                 year < 1000
  2478.             return (year, month, day)
  2479.         month = m.group('month')
  2480.         day = 1
  2481.         if month is None:
  2482.             month = 1
  2483.         else:
  2484.             month = int(month)
  2485.             day = m.group('day')
  2486.             if day:
  2487.                 day = int(day)
  2488.             else:
  2489.                 day = 1
  2490.         return (year, month, day)
  2491.  
  2492.     
  2493.     def __extract_time(m):
  2494.         if not m:
  2495.             return (0, 0, 0)
  2496.         hours = m.group('hours')
  2497.         if not hours:
  2498.             return (0, 0, 0)
  2499.         hours = int(hours)
  2500.         minutes = int(m.group('minutes'))
  2501.         seconds = m.group('seconds')
  2502.         return (hours, minutes, seconds)
  2503.  
  2504.     
  2505.     def __extract_tzd(m):
  2506.         if not m:
  2507.             return 0
  2508.         tzd = m.group('tzd')
  2509.         if not tzd:
  2510.             return 0
  2511.         if tzd == 'Z':
  2512.             return 0
  2513.         hours = int(m.group('tzdhours'))
  2514.         minutes = m.group('tzdminutes')
  2515.         offset = (hours * 60 + minutes) * 60
  2516.         if tzd[0] == '+':
  2517.             return -offset
  2518.         return offset
  2519.  
  2520.     __date_re = '(?P<year>\\d\\d\\d\\d)(?:(?P<dsep>-|)(?:(?P<julian>\\d\\d\\d)|(?P<month>\\d\\d)(?:(?P=dsep)(?P<day>\\d\\d))?))?'
  2521.     __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\\d\\d)(?::?(?P<tzdminutes>\\d\\d))|Z)'
  2522.     __tzd_rx = re.compile(__tzd_re)
  2523.     __time_re = '(?P<hours>\\d\\d)(?P<tsep>:|)(?P<minutes>\\d\\d)(?:(?P=tsep)(?P<seconds>\\d\\d(?:[.,]\\d+)?))?' + __tzd_re
  2524.     __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
  2525.     __datetime_rx = re.compile(__datetime_re)
  2526.     m = __datetime_rx.match(dateString)
  2527.     if m is None or m.group() != dateString:
  2528.         return None
  2529.     gmt = __extract_date(m) + __extract_time(m) + (0, 0, 0)
  2530.     if gmt[0] == 0:
  2531.         return None
  2532.     return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone)
  2533.  
  2534. registerDateHandler(_parse_date_w3dtf)
  2535.  
  2536. def _parse_date_rfc822(dateString):
  2537.     data = dateString.split()
  2538.     if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
  2539.         del data[0]
  2540.     
  2541.     if len(data) == 4:
  2542.         s = data[3]
  2543.         i = s.find('+')
  2544.         if i > 0:
  2545.             data[3:] = [
  2546.                 s[:i],
  2547.                 s[i + 1:]]
  2548.         else:
  2549.             data.append('')
  2550.         dateString = ' '.join(data)
  2551.     
  2552.     if len(data) < 5:
  2553.         dateString += ' 00:00:00 GMT'
  2554.     
  2555.     tm = rfc822.parsedate_tz(dateString)
  2556.     if tm:
  2557.         return time.gmtime(rfc822.mktime_tz(tm))
  2558.  
  2559. _additional_timezones = {
  2560.     'AT': -400,
  2561.     'ET': -500,
  2562.     'CT': -600,
  2563.     'MT': -700,
  2564.     'PT': -800 }
  2565. rfc822._timezones.update(_additional_timezones)
  2566. registerDateHandler(_parse_date_rfc822)
  2567.  
  2568. def _parse_date(dateString):
  2569.     for handler in _date_handlers:
  2570.         
  2571.         try:
  2572.             date9tuple = handler(dateString)
  2573.             if not date9tuple:
  2574.                 continue
  2575.             
  2576.             if len(date9tuple) != 9:
  2577.                 if _debug:
  2578.                     sys.stderr.write('date handler function must return 9-tuple\n')
  2579.                 
  2580.                 raise ValueError
  2581.             len(date9tuple) != 9
  2582.             map(int, date9tuple)
  2583.             return date9tuple
  2584.         continue
  2585.         except Exception:
  2586.             e = None
  2587.             if _debug:
  2588.                 sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
  2589.             
  2590.             _debug
  2591.         
  2592.  
  2593.     
  2594.  
  2595.  
  2596. def _getCharacterEncoding(http_headers, xml_data):
  2597.     
  2598.     def _parseHTTPContentType(content_type):
  2599.         if not content_type:
  2600.             pass
  2601.         content_type = ''
  2602.         (content_type, params) = cgi.parse_header(content_type)
  2603.         return (content_type, params.get('charset', '').replace("'", ''))
  2604.  
  2605.     sniffed_xml_encoding = ''
  2606.     xml_encoding = ''
  2607.     true_encoding = ''
  2608.     (http_content_type, http_encoding) = _parseHTTPContentType(http_headers.get('content-type'))
  2609.     
  2610.     try:
  2611.         if xml_data[:4] == 'Lo\xa7\x94':
  2612.             xml_data = _ebcdic_to_ascii(xml_data)
  2613.         elif xml_data[:4] == '\x00<\x00?':
  2614.             sniffed_xml_encoding = 'utf-16be'
  2615.             xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  2616.         elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  2617.             sniffed_xml_encoding = 'utf-16be'
  2618.             xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  2619.         elif xml_data[:4] == '<\x00?\x00':
  2620.             sniffed_xml_encoding = 'utf-16le'
  2621.             xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  2622.         elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  2623.             sniffed_xml_encoding = 'utf-16le'
  2624.             xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  2625.         elif xml_data[:4] == '\x00\x00\x00<':
  2626.             sniffed_xml_encoding = 'utf-32be'
  2627.             xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  2628.         elif xml_data[:4] == '<\x00\x00\x00':
  2629.             sniffed_xml_encoding = 'utf-32le'
  2630.             xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  2631.         elif xml_data[:4] == '\x00\x00\xfe\xff':
  2632.             sniffed_xml_encoding = 'utf-32be'
  2633.             xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  2634.         elif xml_data[:4] == '\xff\xfe\x00\x00':
  2635.             sniffed_xml_encoding = 'utf-32le'
  2636.             xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  2637.         elif xml_data[:3] == '\xef\xbb\xbf':
  2638.             sniffed_xml_encoding = 'utf-8'
  2639.             xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  2640.         
  2641.         xml_encoding_match = re.compile('^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>').match(xml_data)
  2642.     except:
  2643.         xml_encoding_match = None
  2644.  
  2645.     if xml_encoding_match:
  2646.         xml_encoding = xml_encoding_match.groups()[0].lower()
  2647.         if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  2648.             xml_encoding = sniffed_xml_encoding
  2649.         
  2650.     
  2651.     acceptable_content_type = 0
  2652.     application_content_types = ('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity')
  2653.     text_content_types = ('text/xml', 'text/xml-external-parsed-entity')
  2654.     if (http_content_type in application_content_types or http_content_type.startswith('application/')) and http_content_type.endswith('+xml'):
  2655.         acceptable_content_type = 1
  2656.         if not http_encoding and xml_encoding:
  2657.             pass
  2658.         true_encoding = 'utf-8'
  2659.     elif (http_content_type in text_content_types or http_content_type.startswith('text/')) and http_content_type.endswith('+xml'):
  2660.         acceptable_content_type = 1
  2661.         if not http_encoding:
  2662.             pass
  2663.         true_encoding = 'us-ascii'
  2664.     elif http_content_type.startswith('text/'):
  2665.         if not http_encoding:
  2666.             pass
  2667.         true_encoding = 'us-ascii'
  2668.     elif http_headers and not http_headers.has_key('content-type'):
  2669.         if not xml_encoding:
  2670.             pass
  2671.         true_encoding = 'iso-8859-1'
  2672.     elif not xml_encoding:
  2673.         pass
  2674.     true_encoding = 'utf-8'
  2675.     return (true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type)
  2676.  
  2677.  
  2678. def _toUTF8(data, encoding):
  2679.     if _debug:
  2680.         sys.stderr.write('entering _toUTF8, trying encoding %s\n' % encoding)
  2681.     
  2682.     if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  2683.         if _debug:
  2684.             sys.stderr.write('stripping BOM\n')
  2685.             if encoding != 'utf-16be':
  2686.                 sys.stderr.write('trying utf-16be instead\n')
  2687.             
  2688.         
  2689.         encoding = 'utf-16be'
  2690.         data = data[2:]
  2691.     elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  2692.         if _debug:
  2693.             sys.stderr.write('stripping BOM\n')
  2694.             if encoding != 'utf-16le':
  2695.                 sys.stderr.write('trying utf-16le instead\n')
  2696.             
  2697.         
  2698.         encoding = 'utf-16le'
  2699.         data = data[2:]
  2700.     elif data[:3] == '\xef\xbb\xbf':
  2701.         if _debug:
  2702.             sys.stderr.write('stripping BOM\n')
  2703.             if encoding != 'utf-8':
  2704.                 sys.stderr.write('trying utf-8 instead\n')
  2705.             
  2706.         
  2707.         encoding = 'utf-8'
  2708.         data = data[3:]
  2709.     elif data[:4] == '\x00\x00\xfe\xff':
  2710.         if _debug:
  2711.             sys.stderr.write('stripping BOM\n')
  2712.             if encoding != 'utf-32be':
  2713.                 sys.stderr.write('trying utf-32be instead\n')
  2714.             
  2715.         
  2716.         encoding = 'utf-32be'
  2717.         data = data[4:]
  2718.     elif data[:4] == '\xff\xfe\x00\x00':
  2719.         if _debug:
  2720.             sys.stderr.write('stripping BOM\n')
  2721.             if encoding != 'utf-32le':
  2722.                 sys.stderr.write('trying utf-32le instead\n')
  2723.             
  2724.         
  2725.         encoding = 'utf-32le'
  2726.         data = data[4:]
  2727.     
  2728.     newdata = unicode(data, encoding)
  2729.     if _debug:
  2730.         sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
  2731.     
  2732.     declmatch = re.compile('^<\\?xml[^>]*?>')
  2733.     newdecl = "<?xml version='1.0' encoding='utf-8'?>"
  2734.     if declmatch.search(newdata):
  2735.         newdata = declmatch.sub(newdecl, newdata)
  2736.     else:
  2737.         newdata = newdecl + u'\n' + newdata
  2738.     return newdata.encode('utf-8')
  2739.  
  2740.  
  2741. def _stripDoctype(data):
  2742.     entity_pattern = re.compile('<!ENTITY([^>]*?)>', re.MULTILINE)
  2743.     data = entity_pattern.sub('', data)
  2744.     doctype_pattern = re.compile('<!DOCTYPE([^>]*?)>', re.MULTILINE)
  2745.     doctype_results = doctype_pattern.findall(data)
  2746.     if not doctype_results or doctype_results[0]:
  2747.         pass
  2748.     doctype = ''
  2749.     if doctype.lower().count('netscape'):
  2750.         version = 'rss091n'
  2751.     else:
  2752.         version = None
  2753.     data = doctype_pattern.sub('', data)
  2754.     return (version, data)
  2755.  
  2756.  
  2757. def parse(url_file_stream_or_string, etag = None, modified = None, agent = None, referrer = None, handlers = []):
  2758.     result = FeedParserDict()
  2759.     result['feed'] = FeedParserDict()
  2760.     result['entries'] = []
  2761.     if _XML_AVAILABLE:
  2762.         result['bozo'] = 0
  2763.     
  2764.     if type(handlers) == types.InstanceType:
  2765.         handlers = [
  2766.             handlers]
  2767.     
  2768.     
  2769.     try:
  2770.         f = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers)
  2771.         data = f.read()
  2772.     except Exception:
  2773.         e = None
  2774.         result['bozo'] = 1
  2775.         result['bozo_exception'] = e
  2776.         data = ''
  2777.         f = None
  2778.  
  2779.     if f and data and hasattr(f, 'headers'):
  2780.         if gzip and f.headers.get('content-encoding', '') == 'gzip':
  2781.             
  2782.             try:
  2783.                 data = gzip.GzipFile(fileobj = _StringIO(data)).read()
  2784.             except Exception:
  2785.                 e = None
  2786.                 result['bozo'] = 1
  2787.                 result['bozo_exception'] = e
  2788.                 data = ''
  2789.             except:
  2790.                 None<EXCEPTION MATCH>Exception
  2791.             
  2792.  
  2793.         None<EXCEPTION MATCH>Exception
  2794.         if zlib and f.headers.get('content-encoding', '') == 'deflate':
  2795.             
  2796.             try:
  2797.                 data = zlib.decompress(data, -(zlib.MAX_WBITS))
  2798.             except Exception:
  2799.                 e = None
  2800.                 result['bozo'] = 1
  2801.                 result['bozo_exception'] = e
  2802.                 data = ''
  2803.             except:
  2804.                 None<EXCEPTION MATCH>Exception
  2805.             
  2806.  
  2807.         None<EXCEPTION MATCH>Exception
  2808.     
  2809.     if hasattr(f, 'info'):
  2810.         info = f.info()
  2811.         result['etag'] = info.getheader('ETag')
  2812.         last_modified = info.getheader('Last-Modified')
  2813.         if last_modified:
  2814.             result['modified'] = _parse_date(last_modified)
  2815.         
  2816.     
  2817.     if hasattr(f, 'url'):
  2818.         result['href'] = f.url
  2819.         result['status'] = 200
  2820.     
  2821.     if hasattr(f, 'status'):
  2822.         result['status'] = f.status
  2823.     
  2824.     if hasattr(f, 'headers'):
  2825.         result['headers'] = f.headers.dict
  2826.     
  2827.     if hasattr(f, 'close'):
  2828.         f.close()
  2829.     
  2830.     http_headers = result.get('headers', { })
  2831.     (result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type) = _getCharacterEncoding(http_headers, data)
  2832.     if http_headers and not acceptable_content_type:
  2833.         if http_headers.has_key('content-type'):
  2834.             bozo_message = '%s is not an XML media type' % http_headers['content-type']
  2835.         else:
  2836.             bozo_message = 'no Content-type specified'
  2837.         result['bozo'] = 1
  2838.         result['bozo_exception'] = NonXMLContentType(bozo_message)
  2839.     
  2840.     (result['version'], data) = _stripDoctype(data)
  2841.     baseuri = http_headers.get('content-location', result.get('href'))
  2842.     baselang = http_headers.get('content-language', None)
  2843.     if result.get('status', 0) == 304:
  2844.         result['version'] = ''
  2845.         result['debug_message'] = 'The feed has not changed since you last checked, ' + 'so the server sent no data.  This is a feature, not a bug!'
  2846.         return result
  2847.     if not data:
  2848.         return result
  2849.     use_strict_parser = 0
  2850.     known_encoding = 0
  2851.     tried_encodings = []
  2852.     for proposed_encoding in (result['encoding'], xml_encoding, sniffed_xml_encoding):
  2853.         if proposed_encoding in tried_encodings:
  2854.             continue
  2855.         
  2856.         tried_encodings.append(proposed_encoding)
  2857.         
  2858.         try:
  2859.             data = _toUTF8(data, proposed_encoding)
  2860.             known_encoding = use_strict_parser = 1
  2861.         continue
  2862.         continue
  2863.  
  2864.     
  2865.     if not known_encoding and chardet:
  2866.         
  2867.         try:
  2868.             proposed_encoding = chardet.detect(data)['encoding']
  2869.             if proposed_encoding and proposed_encoding not in tried_encodings:
  2870.                 tried_encodings.append(proposed_encoding)
  2871.                 data = _toUTF8(data, proposed_encoding)
  2872.                 known_encoding = use_strict_parser = 1
  2873.  
  2874.     
  2875.     if not known_encoding and 'utf-8' not in tried_encodings:
  2876.         
  2877.         try:
  2878.             proposed_encoding = 'utf-8'
  2879.             tried_encodings.append(proposed_encoding)
  2880.             data = _toUTF8(data, proposed_encoding)
  2881.             known_encoding = use_strict_parser = 1
  2882.  
  2883.     
  2884.     if not known_encoding and 'windows-1252' not in tried_encodings:
  2885.         
  2886.         try:
  2887.             proposed_encoding = 'windows-1252'
  2888.             tried_encodings.append(proposed_encoding)
  2889.             data = _toUTF8(data, proposed_encoding)
  2890.             known_encoding = use_strict_parser = 1
  2891.  
  2892.     
  2893.     if not known_encoding:
  2894.         result['bozo'] = 1
  2895.         result['bozo_exception'] = CharacterEncodingUnknown('document encoding unknown, I tried ' + '%s, %s, utf-8, and windows-1252 but nothing worked' % (result['encoding'], xml_encoding))
  2896.         result['encoding'] = ''
  2897.     elif proposed_encoding != result['encoding']:
  2898.         result['bozo'] = 1
  2899.         result['bozo_exception'] = CharacterEncodingOverride('documented declared as %s, but parsed as %s' % (result['encoding'], proposed_encoding))
  2900.         result['encoding'] = proposed_encoding
  2901.     
  2902.     if not _XML_AVAILABLE:
  2903.         use_strict_parser = 0
  2904.     
  2905.     if use_strict_parser:
  2906.         feedparser = _StrictFeedParser(baseuri, baselang, 'utf-8')
  2907.         saxparser = xml.sax.make_parser(PREFERRED_XML_PARSERS)
  2908.         saxparser.setFeature(xml.sax.handler.feature_namespaces, 1)
  2909.         saxparser.setContentHandler(feedparser)
  2910.         saxparser.setErrorHandler(feedparser)
  2911.         source = xml.sax.xmlreader.InputSource()
  2912.         source.setByteStream(_StringIO(data))
  2913.         if hasattr(saxparser, '_ns_stack'):
  2914.             saxparser._ns_stack.append({
  2915.                 'http://www.w3.org/XML/1998/namespace': 'xml' })
  2916.         
  2917.         
  2918.         try:
  2919.             saxparser.parse(source)
  2920.         except Exception:
  2921.             e = None
  2922.             if _debug:
  2923.                 import traceback
  2924.                 traceback.print_stack()
  2925.                 traceback.print_exc()
  2926.                 sys.stderr.write('xml parsing failed\n')
  2927.             
  2928.             result['bozo'] = 1
  2929.             if not feedparser.exc:
  2930.                 pass
  2931.             result['bozo_exception'] = e
  2932.             use_strict_parser = 0
  2933.         except:
  2934.             None<EXCEPTION MATCH>Exception
  2935.         
  2936.  
  2937.     None<EXCEPTION MATCH>Exception
  2938.     if not use_strict_parser:
  2939.         if not known_encoding or 'utf-8':
  2940.             pass
  2941.         feedparser = _LooseFeedParser(baseuri, baselang, '')
  2942.         feedparser.feed(data)
  2943.     
  2944.     result['feed'] = feedparser.feeddata
  2945.     result['entries'] = feedparser.entries
  2946.     if not result['version']:
  2947.         pass
  2948.     result['version'] = feedparser.version
  2949.     result['namespaces'] = feedparser.namespacesInUse
  2950.     return result
  2951.  
  2952. if __name__ == '__main__':
  2953.     zopeCompatibilityHack()
  2954.     from pprint import pprint
  2955.     for url in urls:
  2956.         print url
  2957.         print 
  2958.         result = parse(url)
  2959.         pprint(result)
  2960.         print 
  2961.     
  2962.  
  2963.