home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 February / maximum-cd-2011-02.iso / DiscContents / digsby_setup85.exe / lib / lxml / html / clean.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-11-24  |  13.4 KB  |  558 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. import re
  5. import copy
  6.  
  7. try:
  8.     from urlparse import urlsplit
  9. except ImportError:
  10.     from urllib.parse import urlsplit
  11.  
  12. from lxml import etree
  13. from lxml.html import defs
  14. from lxml.html import fromstring, tostring, XHTML_NAMESPACE
  15. from lxml.html import _nons, _transform_result
  16.  
  17. try:
  18.     set
  19. except NameError:
  20.     from sets import Set as set
  21.  
  22.  
  23. try:
  24.     unichr = __builtins__['unichr']
  25. except (NameError, KeyError):
  26.     unichr = chr
  27.  
  28.  
  29. try:
  30.     unicode = __builtins__['unicode']
  31. except (NameError, KeyError):
  32.     unicode = str
  33.  
  34.  
  35. try:
  36.     bytes = __builtins__['bytes']
  37. except (NameError, KeyError):
  38.     bytes = str
  39.  
  40.  
  41. try:
  42.     basestring = __builtins__['basestring']
  43. except (NameError, KeyError):
  44.     basestring = (str, bytes)
  45.  
  46. __all__ = [
  47.     'clean_html',
  48.     'clean',
  49.     'Cleaner',
  50.     'autolink',
  51.     'autolink_html',
  52.     'word_break',
  53.     'word_break_html']
  54. _css_javascript_re = re.compile('expression\\s*\\(.*?\\)', re.S | re.I)
  55. _css_import_re = re.compile('@\\s*import', re.I)
  56. _javascript_scheme_re = re.compile('\\s*(?:javascript|jscript|livescript|vbscript|about|mocha):', re.I)
  57. _substitute_whitespace = re.compile('\\s+').sub
  58. _conditional_comment_re = re.compile('\\[if[\\s\\n\\r]+.*?][\\s\\n\\r]*>', re.I | re.S)
  59. _find_styled_elements = etree.XPath('descendant-or-self::*[@style]')
  60. _find_external_links = etree.XPath("descendant-or-self::a  [normalize-space(@href) and substring(normalize-space(@href),1,1) != '#'] |descendant-or-self::x:a[normalize-space(@href) and substring(normalize-space(@href),1,1) != '#']", namespaces = {
  61.     'x': XHTML_NAMESPACE })
  62.  
  63. class Cleaner(object):
  64.     scripts = True
  65.     javascript = True
  66.     comments = True
  67.     style = False
  68.     links = True
  69.     meta = True
  70.     page_structure = True
  71.     processing_instructions = True
  72.     embedded = True
  73.     frames = True
  74.     forms = True
  75.     annoying_tags = True
  76.     remove_tags = None
  77.     allow_tags = None
  78.     remove_unknown_tags = True
  79.     safe_attrs_only = True
  80.     add_nofollow = False
  81.     host_whitelist = ()
  82.     whitelist_tags = set([
  83.         'iframe',
  84.         'embed'])
  85.     
  86.     def __init__(self, **kw):
  87.         for name, value in kw.items():
  88.             if not hasattr(self, name):
  89.                 raise TypeError('Unknown parameter: %s=%r' % (name, value))
  90.             hasattr(self, name)
  91.             setattr(self, name, value)
  92.         
  93.  
  94.     _tag_link_attrs = dict(script = 'src', link = 'href', applet = [
  95.         'code',
  96.         'object'], iframe = 'src', embed = 'src', layer = 'src', a = 'href')
  97.     
  98.     def __call__(self, doc):
  99.         if hasattr(doc, 'getroot'):
  100.             doc = doc.getroot()
  101.         
  102.         for el in doc.iter():
  103.             tag = el.tag
  104.             if isinstance(tag, basestring):
  105.                 el.tag = _nons(tag)
  106.                 continue
  107.         
  108.         for el in doc.iter('image'):
  109.             el.tag = 'img'
  110.         
  111.         if not self.comments:
  112.             self.kill_conditional_comments(doc)
  113.         
  114.         kill_tags = set()
  115.         if not self.remove_tags:
  116.             pass
  117.         remove_tags = set(())
  118.         if self.allow_tags:
  119.             allow_tags = set(self.allow_tags)
  120.         else:
  121.             allow_tags = set()
  122.         if self.scripts:
  123.             kill_tags.add('script')
  124.         
  125.         if self.safe_attrs_only:
  126.             safe_attrs = set(defs.safe_attrs)
  127.             for el in doc.iter():
  128.                 attrib = el.attrib
  129.                 for aname in attrib.keys():
  130.                     if aname not in safe_attrs:
  131.                         del attrib[aname]
  132.                         continue
  133.                 
  134.             
  135.         
  136.         if self.javascript:
  137.             if not self.safe_attrs_only:
  138.                 for el in doc.iter():
  139.                     attrib = el.attrib
  140.                     for aname in attrib.keys():
  141.                         if aname.startswith('on'):
  142.                             del attrib[aname]
  143.                             continue
  144.                     
  145.                 
  146.             
  147.             doc.rewrite_links(self._remove_javascript_link, resolve_base_href = False)
  148.             if not self.style:
  149.                 for el in _find_styled_elements(doc):
  150.                     old = el.get('style')
  151.                     new = _css_javascript_re.sub('', old)
  152.                     new = _css_import_re.sub('', old)
  153.                     if self._has_sneaky_javascript(new):
  154.                         del el.attrib['style']
  155.                         continue
  156.                     if new != old:
  157.                         el.set('style', new)
  158.                         continue
  159.                 
  160.                 for el in list(doc.iter('style')):
  161.                     if el.get('type', '').lower().strip() == 'text/javascript':
  162.                         el.drop_tree()
  163.                         continue
  164.                     
  165.                     if not el.text:
  166.                         pass
  167.                     old = ''
  168.                     new = _css_javascript_re.sub('', old)
  169.                     new = _css_import_re.sub('', old)
  170.                     if self._has_sneaky_javascript(new):
  171.                         el.text = '/* deleted */'
  172.                         continue
  173.                     if new != old:
  174.                         el.text = new
  175.                         continue
  176.                 
  177.             
  178.         
  179.         if self.comments or self.processing_instructions:
  180.             kill_tags.add(etree.Comment)
  181.         
  182.         if self.processing_instructions:
  183.             kill_tags.add(etree.ProcessingInstruction)
  184.         
  185.         if self.style:
  186.             kill_tags.add('style')
  187.             for el in _find_styled_elements(doc):
  188.                 del el.attrib['style']
  189.             
  190.         
  191.         if self.links:
  192.             kill_tags.add('link')
  193.         elif self.style or self.javascript:
  194.             for el in list(doc.iter('link')):
  195.                 if 'stylesheet' in el.get('rel', '').lower():
  196.                     el.drop_tree()
  197.                     continue
  198.             
  199.         
  200.         if self.meta:
  201.             kill_tags.add('meta')
  202.         
  203.         if self.page_structure:
  204.             remove_tags.update(('head', 'html', 'title'))
  205.         
  206.         if self.embedded:
  207.             for el in list(doc.iter('param')):
  208.                 found_parent = False
  209.                 parent = el.getparent()
  210.                 while parent is not None and parent.tag not in ('applet', 'object'):
  211.                     parent = parent.getparent()
  212.                 if parent is None:
  213.                     el.drop_tree()
  214.                     continue
  215.             
  216.             kill_tags.update(('applet',))
  217.             remove_tags.update(('iframe', 'embed', 'layer', 'object', 'param'))
  218.         
  219.         if self.frames:
  220.             kill_tags.update(defs.frame_tags)
  221.         
  222.         if self.forms:
  223.             remove_tags.add('form')
  224.             kill_tags.update(('button', 'input', 'select', 'textarea'))
  225.         
  226.         if self.annoying_tags:
  227.             remove_tags.update(('blink', 'marque'))
  228.         
  229.         _remove = []
  230.         _kill = []
  231.         for el in doc.iter():
  232.             if el.tag in kill_tags:
  233.                 if self.allow_element(el):
  234.                     continue
  235.                 
  236.                 _kill.append(el)
  237.                 continue
  238.             if el.tag in remove_tags:
  239.                 if self.allow_element(el):
  240.                     continue
  241.                 
  242.                 _remove.append(el)
  243.                 continue
  244.         
  245.         if _remove and _remove[0] == doc:
  246.             el = _remove.pop(0)
  247.             el.tag = 'div'
  248.             el.attrib.clear()
  249.         elif _kill and _kill[0] == doc:
  250.             el = _kill.pop(0)
  251.             if el.tag != 'html':
  252.                 el.tag = 'div'
  253.             
  254.             el.clear()
  255.         
  256.         for el in _kill:
  257.             el.drop_tree()
  258.         
  259.         for el in _remove:
  260.             el.drop_tag()
  261.         
  262.         allow_tags = self.allow_tags
  263.         if self.remove_unknown_tags:
  264.             if allow_tags:
  265.                 raise ValueError('It does not make sense to pass in both allow_tags and remove_unknown_tags')
  266.             allow_tags
  267.             allow_tags = set(defs.tags)
  268.         
  269.         if allow_tags:
  270.             bad = []
  271.             for el in doc.iter():
  272.                 if el.tag not in allow_tags:
  273.                     bad.append(el)
  274.                     continue
  275.             
  276.             for el in bad:
  277.                 el.drop_tag()
  278.             
  279.         
  280.         if self.add_nofollow:
  281.             for el in _find_external_links(doc):
  282.                 if not self.allow_follow(el):
  283.                     el.set('rel', 'nofollow')
  284.                     continue
  285.             
  286.         
  287.  
  288.     
  289.     def allow_follow(self, anchor):
  290.         return False
  291.  
  292.     
  293.     def allow_element(self, el):
  294.         if el.tag not in self._tag_link_attrs:
  295.             return False
  296.         attr = self._tag_link_attrs[el.tag]
  297.         if isinstance(attr, (list, tuple)):
  298.             for one_attr in attr:
  299.                 url = el.get(one_attr)
  300.                 if not url:
  301.                     return False
  302.                 if not self.allow_embedded_url(el, url):
  303.                     return False
  304.             
  305.             return True
  306.         url = el.get(attr)
  307.         if not url:
  308.             return False
  309.         return self.allow_embedded_url(el, url)
  310.  
  311.     
  312.     def allow_embedded_url(self, el, url):
  313.         if self.whitelist_tags is not None and el.tag not in self.whitelist_tags:
  314.             return False
  315.         (scheme, netloc, path, query, fragment) = urlsplit(url)
  316.         netloc = netloc.lower().split(':', 1)[0]
  317.         if scheme not in ('http', 'https'):
  318.             return False
  319.         if netloc in self.host_whitelist:
  320.             return True
  321.         return False
  322.  
  323.     
  324.     def kill_conditional_comments(self, doc):
  325.         bad = []
  326.         self._kill_elements(doc, (lambda el: _conditional_comment_re.search(el.text)), etree.Comment)
  327.  
  328.     
  329.     def _kill_elements(self, doc, condition, iterate = None):
  330.         bad = []
  331.         for el in doc.iter(iterate):
  332.             if condition(el):
  333.                 bad.append(el)
  334.                 continue
  335.         
  336.         for el in bad:
  337.             el.drop_tree()
  338.         
  339.  
  340.     
  341.     def _remove_javascript_link(self, link):
  342.         new = _substitute_whitespace('', link)
  343.         if _javascript_scheme_re.search(new):
  344.             return ''
  345.         return link
  346.  
  347.     _substitute_comments = re.compile('/\\*.*?\\*/', re.S).sub
  348.     
  349.     def _has_sneaky_javascript(self, style):
  350.         style = self._substitute_comments('', style)
  351.         style = style.replace('\\', '')
  352.         style = _substitute_whitespace('', style)
  353.         style = style.lower()
  354.         if 'javascript:' in style:
  355.             return True
  356.         if 'expression(' in style:
  357.             return True
  358.         return False
  359.  
  360.     
  361.     def clean_html(self, html):
  362.         result_type = type(html)
  363.         if isinstance(html, basestring):
  364.             doc = fromstring(html)
  365.         else:
  366.             doc = copy.deepcopy(html)
  367.         self(doc)
  368.         return _transform_result(result_type, doc)
  369.  
  370.  
  371. clean = Cleaner()
  372. clean_html = clean.clean_html
  373. _link_regexes = [
  374.     re.compile('(?P<body>https?://(?P<host>[a-z0-9._-]+)(?:/[/\\-_.,a-z0-9%&?;=~]*)?(?:\\([/\\-_.,a-z0-9%&?;=~]*\\))?)', re.I),
  375.     re.compile('mailto:(?P<body>[a-z0-9._-]+@(?P<host>[a-z0-9_._]+[a-z]))', re.I)]
  376. _avoid_elements = [
  377.     'textarea',
  378.     'pre',
  379.     'code',
  380.     'head',
  381.     'select',
  382.     'a']
  383. _avoid_hosts = [
  384.     re.compile('^localhost', re.I),
  385.     re.compile('\\bexample\\.(?:com|org|net)$', re.I),
  386.     re.compile('^127\\.0\\.0\\.1$')]
  387. _avoid_classes = [
  388.     'nolink']
  389.  
  390. def autolink(el, link_regexes = _link_regexes, avoid_elements = _avoid_elements, avoid_hosts = _avoid_hosts, avoid_classes = _avoid_classes):
  391.     if el.tag in avoid_elements:
  392.         return None
  393.     class_name = el.get('class')
  394.     for child in list(el):
  395.         autolink(child, link_regexes = link_regexes, avoid_elements = avoid_elements, avoid_hosts = avoid_hosts, avoid_classes = avoid_classes)
  396.         if child.tail:
  397.             (text, tail_children) = _link_text(child.tail, link_regexes, avoid_hosts, factory = el.makeelement)
  398.             if tail_children:
  399.                 child.tail = text
  400.                 index = el.index(child)
  401.                 el[index + 1:index + 1] = tail_children
  402.             
  403.         tail_children
  404.     
  405.     if el.text:
  406.         (text, pre_children) = _link_text(el.text, link_regexes, avoid_hosts, factory = el.makeelement)
  407.         if pre_children:
  408.             el.text = text
  409.             el[:0] = pre_children
  410.         
  411.     
  412.  
  413.  
  414. def _link_text(text, link_regexes, avoid_hosts, factory):
  415.     leading_text = ''
  416.     links = []
  417.     last_pos = 0
  418.     while None:
  419.         (best_match, best_pos) = (None, None)
  420.         for regex in link_regexes:
  421.             regex_pos = last_pos
  422.             while None:
  423.                 match = regex.search(text, pos = regex_pos)
  424.                 if match is None:
  425.                     break
  426.                 
  427.                 host = match.group('host')
  428.                 for host_regex in avoid_hosts:
  429.                     if host_regex.search(host):
  430.                         regex_pos = match.end()
  431.                         break
  432.                         continue
  433.                 else:
  434.                     break
  435.                 continue
  436.                 if match is None:
  437.                     continue
  438.                 
  439.             if best_pos is None or match.start() < best_pos:
  440.                 best_match = match
  441.                 best_pos = match.start()
  442.                 continue
  443.         
  444.         if best_match is None:
  445.             if links:
  446.                 links[-1].tail = text
  447.             else:
  448.                 leading_text = text
  449.             break
  450.         
  451.         link = best_match.group(0)
  452.         end = best_match.end()
  453.         if link.endswith('.') or link.endswith(','):
  454.             end -= 1
  455.             link = link[:-1]
  456.         
  457.         prev_text = text[:best_match.start()]
  458.         if links:
  459.             links[-1].tail = prev_text
  460.         else:
  461.             leading_text = prev_text
  462.         anchor = factory('a')
  463.         body = best_match.group('body')
  464.         if not body:
  465.             body = link
  466.         
  467.         if body.endswith('.') or body.endswith(','):
  468.             body = body[:-1]
  469.         
  470.         anchor.text = body
  471.         links.append(anchor)
  472.         text = text[end:]
  473.         continue
  474.         return (leading_text, links)
  475.  
  476.  
  477. def autolink_html(html, *args, **kw):
  478.     result_type = type(html)
  479.     if isinstance(html, basestring):
  480.         doc = fromstring(html)
  481.     else:
  482.         doc = copy.deepcopy(html)
  483.     autolink(doc, *args, **kw)
  484.     return _transform_result(result_type, doc)
  485.  
  486. autolink_html.__doc__ = autolink.__doc__
  487. _avoid_word_break_elements = [
  488.     'pre',
  489.     'textarea',
  490.     'code']
  491. _avoid_word_break_classes = [
  492.     'nobreak']
  493.  
  494. def word_break(el, max_width = 40, avoid_elements = _avoid_word_break_elements, avoid_classes = _avoid_word_break_classes, break_character = unichr(8203)):
  495.     if el.tag in _avoid_word_break_elements:
  496.         return None
  497.     class_name = el.get('class')
  498.     if class_name:
  499.         dont_break = False
  500.         class_name = class_name.split()
  501.         for avoid in avoid_classes:
  502.             if avoid in class_name:
  503.                 dont_break = True
  504.                 break
  505.                 continue
  506.             el.tag in _avoid_word_break_elements
  507.         
  508.         if dont_break:
  509.             return None
  510.     
  511.     if el.text:
  512.         el.text = _break_text(el.text, max_width, break_character)
  513.     
  514.     for child in el:
  515.         word_break(child, max_width = max_width, avoid_elements = avoid_elements, avoid_classes = avoid_classes, break_character = break_character)
  516.         if child.tail:
  517.             child.tail = _break_text(child.tail, max_width, break_character)
  518.             continue
  519.     
  520.  
  521.  
  522. def word_break_html(html, *args, **kw):
  523.     result_type = type(html)
  524.     doc = fromstring(html)
  525.     word_break(doc, *args, **kw)
  526.     return _transform_result(result_type, doc)
  527.  
  528.  
  529. def _break_text(text, max_width, break_character):
  530.     words = text.split()
  531.     for word in words:
  532.         if len(word) > max_width:
  533.             replacement = _insert_break(word, max_width, break_character)
  534.             text = text.replace(word, replacement)
  535.             continue
  536.     
  537.     return text
  538.  
  539. _break_prefer_re = re.compile('[^a-z]', re.I)
  540.  
  541. def _insert_break(word, width, break_character):
  542.     orig_word = word
  543.     result = ''
  544.     while len(word) > width:
  545.         start = word[:width]
  546.         breaks = list(_break_prefer_re.finditer(start))
  547.         if breaks:
  548.             last_break = breaks[-1]
  549.             if last_break.end() > width - 10:
  550.                 start = word[:last_break.end()]
  551.             
  552.         
  553.         result += start + break_character
  554.         word = word[len(start):]
  555.     result += word
  556.     return result
  557.  
  558.