home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / common / logger.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  13.0 KB  |  335 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5.  
  6. try:
  7.     _
  8. except:
  9.     import gettext
  10.     gettext.install('Digsby')
  11.  
  12. import sys
  13. import time
  14. import re
  15. from common import profile
  16. from datetime import datetime, timedelta
  17. from util import soupify, Storage as S, tail, boolify, fromutc
  18. from path import path
  19. from digsby import iswidget
  20. from traceback import print_exc
  21. from logging import getLogger
  22. log = getLogger('logger')
  23. from common.message import Message
  24. import libxml2
  25.  
  26. def get_default_logging_dir():
  27.     lp = localprefs
  28.     import prefs
  29.     localprefs = lp()
  30.     return path(localprefs['chatlogdir']) / DEFAULT_LOG_DIR_NAME / profile.username
  31.  
  32. DEFAULT_LOG_DIR_NAME = u'Digsby Logs'
  33. LOGSIZE_PARSE_LIMIT = 15360
  34.  
  35. def buddy_path(account, buddy):
  36.     return path(account.name).joinpath(account.username, buddy.name + '_' + buddy.service)
  37.  
  38. message_timestamp_fmt = '%Y-%m-%d %H:%M:%S'
  39. message_timestamp_fmt_OLD = '%Y-%m-%d %H:%M'
  40. filename_format_re = re.compile('\\d{4}-\\d{2}-\\d{2}\\..*')
  41. message_shorttime_fmt = '%H:%M:%S %p'
  42. html_header = u'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n    "http://www.w3.org/TR/html4/strict.dtd">\n<HTML>\n   <HEAD>\n      <TITLE>%(title)s</TITLE>\n   <style>\n     .buddy { font-weight: bold; }\n     .buddy:after { content: ":" }\n\n     .time {\n        color: #a0a0a0;\n        font-family: monaco, courier new, monospace;\n        font-size: 75%%;\n     }\n     .time:hover { color: black; }\n\n     .outgoing { background-color: #efefef; }\n     .incoming { background-color: #ffffff; }\n   </style>\n   <script type="text/javascript">\n//<![CDATA[\n    function convert_time(datetime){\n        var dt = datetime.split(" ");\n        var date = dt[0].split("-");\n        var time = dt[1].split(":");\n        var t = new Date;\n        t.setUTCFullYear(date[0],date[1]-1,date[2]);\n        t.setUTCHours(time[0],time[1],time[2]);\n        return t.toLocaleTimeString();\n    }\n\n    function utc_to_local(){\n        var node;\n        for (var i=0; i<document.body.childNodes.length; i++){\n            node = document.body.childNodes[i];\n            if(node.nodeType == 1 && node.className.match("message")){\n                var showtime = convert_time(node.getAttribute(\'timestamp\'));\n                var newspan = \'<span class="time">(\' + showtime + \') </span>\';\n                var msgnode = node;\n                msgnode.innerHTML = newspan + msgnode.innerHTML;\n            }\n        }\n    }\n//]]>\n   </script>\n   </HEAD>\n   <BODY onload="utc_to_local()">\n'
  43. html_log_entry = '<div class="%(type)s message" auto="%(auto)s" timestamp="%(timestamp)s"><span class="buddy">%(buddy)s</span> <span class="msgcontent">%(message)s</span></div>\n'
  44.  
  45. class Logger(object):
  46.     
  47.     def __init__(self, output_dir = None, log_ims = True, log_chats = True, log_widgets = False):
  48.         self.OutputDir = output_dir
  49.         self.OutputType = 'html'
  50.         self.LogChats = log_chats
  51.         self.LogIMs = log_ims
  52.         self.LogWidgets = log_widgets
  53.  
  54.     
  55.     def on_message(self, messageobj = None, convo = None):
  56.         if not self.should_log_message(messageobj):
  57.             return None
  58.         
  59.         messageobj = modify_message(messageobj)
  60.         output = self.generate_output(messageobj)
  61.         written_size = self.write_output(output, messageobj)
  62.         
  63.         try:
  64.             buddy = messageobj.conversation.buddy
  65.         except AttributeError:
  66.             pass
  67.  
  68.         buddy.increase_log_size(written_size)
  69.  
  70.     
  71.     def should_log_message(self, messageobj):
  72.         if messageobj is None:
  73.             return False
  74.         
  75.         convo = messageobj.conversation
  76.         if convo.ischat and not (self.LogChats):
  77.             return False
  78.         elif not (convo.ischat) and not (self.LogIMs):
  79.             return False
  80.         elif not (self.LogWidgets) and iswidget(convo.buddy):
  81.             return False
  82.         elif messageobj.buddy is None:
  83.             return False
  84.         elif not messageobj.buddy.protocol.should_log(messageobj):
  85.             return False
  86.         
  87.         return True
  88.  
  89.     
  90.     def history_for(self, account, buddy):
  91.         log.info('history_for(%r, %r)', account, buddy)
  92.         files = self.logfiles_for(account, buddy)
  93.         log.info('%d %s log files found', len(files), self.OutputType)
  94.         if not files:
  95.             return iter([])
  96.         
  97.         files.sort(reverse = True)
  98.         return history_from_files(files, 'html')
  99.  
  100.     
  101.     def logsize(self, account, buddy):
  102.         return sum((lambda .0: for f in .0:
  103. f.size)(self.logfiles_for(account, buddy)))
  104.  
  105.     
  106.     def logfiles_for(self, account, buddy):
  107.         logdir = self.pathfor(account, buddy)
  108.         if not logdir.exists():
  109.             return []
  110.         
  111.         return list((lambda .0: for f in .0:
  112. if filename_format_re.match(f.name):
  113. fcontinue)(logdir.files('*.' + self.OutputType)))
  114.  
  115.     
  116.     def pathfor(self, account, buddy):
  117.         return self.OutputDir.joinpath(buddy_path(account, buddy))
  118.  
  119.     
  120.     def set_outputdir(self, val):
  121.         self.output_dir = None if val is not None else get_default_logging_dir()
  122.  
  123.     
  124.     def get_outputdir(self):
  125.         return self.output_dir
  126.  
  127.     OutputDir = property(get_outputdir, set_outputdir, doc = 'where to write logs')
  128.     
  129.     def write_output(self, output, messageobj):
  130.         convo = messageobj.conversation
  131.         proto = convo.protocol
  132.         datefilename = fromutc(messageobj.timestamp).date().isoformat()
  133.         pathelems = (buddy_path(proto, convo.buddy), datefilename)
  134.         p = path(path(self.OutputDir).joinpath(*pathelems) + '.' + self.OutputType)
  135.         if not p.parent.exists():
  136.             p.parent.makedirs()
  137.         
  138.         written_size = 0
  139.         if not p.exists():
  140.             header = globals()['generate_header_' + self.OutputType](messageobj, self.output_encoding)
  141.             written_size += len(header)
  142.             p.write_bytes(header)
  143.         
  144.         written_size += len(output)
  145.         p.write_bytes(output, append = p.exists())
  146.         return written_size
  147.  
  148.     
  149.     def generate_output(self, messageobj):
  150.         return globals()['generate_output_' + self.OutputType](messageobj, self.output_encoding)
  151.  
  152.     output_encoding = 'utf-8'
  153.  
  154.  
  155. def generate_header_html(messageobj, encoding):
  156.     c = messageobj.conversation
  157.     datefmt = messageobj.timestamp.date().isoformat()
  158.     if c.ischat:
  159.         title = 'Chat in %s on %s' % (c.name, datefmt)
  160.     else:
  161.         title = 'IM Logs with %s on %s' % (c.buddy.name, datefmt)
  162.     return (html_header % dict(title = title.encode('xml'))).encode(encoding, 'replace')
  163.  
  164.  
  165. def generate_output_html(m, encoding = 'utf-8'):
  166.     return (None % html_log_entry(dict = 'buddy' if m.buddy is not None else '', timestamp = m.timestamp.strftime(message_timestamp_fmt), message = m.message, type = m.type, auto = getattr(m, 'auto', False))).encode(encoding, 'replace')
  167.  
  168. class_buddy = {
  169.     'class': 'buddy' }
  170. class_message = {
  171.     'class': 'message' }
  172. class_msgcontent = {
  173.     'class': 'msgcontent' }
  174.  
  175. def message_divs(tag):
  176.     if tag.name == 'div':
  177.         pass
  178.     return 'message' in dict(tag.attrs).get('class', '')
  179.  
  180.  
  181. def parse_html_fast(html):
  182.     doc = libxml2.htmlParseDoc(html, 'utf-8')
  183.     
  184.     try:
  185.         divs = doc.xpathEval('//html/body/div')
  186.         messages = []
  187.         for div in divs:
  188.             message_type = div.properties.content
  189.             if 'message' not in message_type:
  190.                 continue
  191.             
  192.             type = message_type.replace('message', '').strip()
  193.             if type not in ('incoming', 'outgoing'):
  194.                 log.critical('got an unknown message type: %s', type)
  195.                 raise ValueError('unknown message type')
  196.             
  197.             props = div.properties
  198.             (auto, timestamp, buddyname, message) = (None, None, None, None)
  199.             while props:
  200.                 name = props.name
  201.                 value = props.content
  202.                 if name == 'auto':
  203.                     auto = boolify(value)
  204.                 elif name == 'timestamp':
  205.                     timestamp = parse_timestamp(value)
  206.                 
  207.                 props = props.get_next()
  208.             child = div.children
  209.             while child:
  210.                 props = child.properties
  211.                 if props is not None:
  212.                     attrs = props.content
  213.                     if attrs == 'buddy':
  214.                         buddyname = child.content.decode('utf-8')
  215.                     elif attrs == 'msgcontent':
  216.                         message = child.serialize().decode('utf-8')
  217.                     
  218.                 
  219.                 child = child.next
  220.             if all((lambda .0: for a in .0:
  221. a is not None)((auto, timestamp, buddyname, message))):
  222.                 messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
  223.                 continue
  224.             raise ValueError('not all attributes could be parsed: %r' % ((auto, timestamp, buddyname, message),))
  225.         
  226.         log.info('parse_html_fast with %d bytes returning %d messages', len(html), len(messages))
  227.         return messages
  228.     finally:
  229.         doc.freeDoc()
  230.  
  231.  
  232.  
  233. def parse_html_slow(html):
  234.     soup = soupify(html, markupMassage = ((br_re, (lambda m: '<br />')),))
  235.     messages = []
  236.     strptime = datetime.strptime
  237.     for div in soup.findAll(message_divs):
  238.         
  239.         try:
  240.             buddyname = div.findAll('span', class_buddy)[0].renderContents(None)
  241.             timestamp = parse_timestamp(div['timestamp'])
  242.             message = div.findAll('span', class_msgcontent)[0].renderContents(None)
  243.             type = div['class'].replace('message', '').strip()
  244.             auto = boolify(div.get('auto', 'false'))
  245.         except Exception:
  246.             print_exc()
  247.             continue
  248.  
  249.         messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
  250.     
  251.     log.info('parse_html_slow with %d bytes returning %d messages', len(html), len(messages))
  252.     return messages
  253.  
  254. USE_LIBXML2_HTML_PARSER = False
  255.  
  256. def parse_html(html, last_parsed = [
  257.     None,
  258.     None]):
  259.     digest = hash(html)
  260.     (msghash, oldmessages) = last_parsed
  261.     if msghash == digest:
  262.         return oldmessages
  263.     
  264.     if not USE_LIBXML2_HTML_PARSER or sys.platform == 'darwin':
  265.         messages = parse_html_slow(html)
  266.     else:
  267.         
  268.         try:
  269.             messages = parse_html_fast(html)
  270.         except Exception:
  271.             messages = parse_html_slow(html)
  272.             log.info('parsed slow: got %d messages', len(messages))
  273.  
  274.     last_parsed[0] = digest
  275.     last_parsed[1] = messages
  276.     return messages
  277.  
  278.  
  279. def parse_timestamp(timestamp):
  280.     
  281.     try:
  282.         return datetime.strptime(timestamp, message_timestamp_fmt)
  283.     except Exception:
  284.         return datetime.strptime(timestamp, message_timestamp_fmt_OLD)
  285.  
  286.  
  287.  
  288. def chat_path(account, room_name, *additional):
  289.     return path(account.name).joinpath(account.username, *additional)
  290.  
  291.  
  292. def history_from_files(files, logtype = 'html', encoding = 'utf-8'):
  293.     parse = globals()['parse_' + logtype]
  294.     for logfile in files:
  295.         
  296.         try:
  297.             bytes = tail(logfile, LOGSIZE_PARSE_LIMIT, encoding = encoding)
  298.         except Exception:
  299.             print_exc()
  300.             continue
  301.  
  302.         for msg in reversed(parse(bytes)):
  303.             yield msg
  304.         
  305.         if len(bytes) < logfile.size:
  306.             break
  307.             continue
  308.     
  309.  
  310.  
  311. def modify_message(msgobj):
  312.     msg = getattr(msgobj, 'message', None)
  313.     if msg is not None:
  314.         msgobj.message = strip_html_tags(msg)
  315.     
  316.     return msgobj
  317.  
  318.  
  319. def strip_html_tags(msg):
  320.     if len(msg) >= 13:
  321.         start = msg[:6]
  322.         middle = msg[6:-7]
  323.         end = msg[-7:]
  324.         if start.lower() == '<html>' and end.lower() == '</html>':
  325.             return middle
  326.         
  327.     
  328.     return msg
  329.  
  330. import re
  331. real_br = '<br />'
  332. br_re = re.compile('<br\\s*/?>', re.IGNORECASE)
  333.  
  334. brfix = lambda s: br_re.sub(real_br, s)
  335.