#!/usr/local/bin/python
import string
import cgi
import cgitb; cgitb.enable()
import sys, smtplib
import os
import re
from urllib import unquote_plus
from time import strftime, time

class OsInfo:
     name=None
     version=None
     architecture=None

class MemInfo:
    free=None
    total=None
    
class BugReport:
    version=None
    build=None
    javaVersion=None
    osInfo=None
    memInfo=None
    isFatalError=False
    stackTrace=None
    rawMessage=None

    def __init__(self, reportAsArray, reportAsString):
        self.rawMessage = unquote_plus(reportAsString)
        VER_BUILD_PREG = re.compile(r'.*?(\d+.\d+.\d+) build (\d+).*?')
        r = VER_BUILD_PREG.match(reportAsArray[0])
        if r is not None:
            self.version = r.group(1)
            self.build = r.group(2)

    def __str__(self):
         d = {'version':self.version,
              'build':self.build,
              'javaVersion':self.javaVersion,
              'osInfo':self.osInfo,
              'memInfo':self.memInfo,
              'isFatalError':self.isFatalError,
              'stackTrace':self.stackTrace,
              'rawMessage':self.rawMessage}
         return '<BugManager@' + hex(id(self))  + '>' + str(d)

def log(message):
    fd = open('debug.log','a')
    fd.write(message + "\r\n")
    fd.close()
    
def fastCGIHandler(env, response):
     errorReportAsArray=env['wsgi.input'].readlines()
     #log("fastCGIHandler(): errorReportAsArray = " + str(errorReportAsArray))
     #log("fastCGIHandler(): env = " + str(env))
     #log("fastCGIHandler(): response = " + str(env))
     message = "".join(errorReportAsArray)
     unquoted_message = unquote_plus(message)
     bugReport = BugReport(errorReportAsArray, message)

     if bugReport.version is None or bugReport.build is None:
          return "KO"
     #log("fastCGHIHandler(): bugReport.rawMessage =" + bugReport.rawMessage)
     #log('bugReport ->' + str(bugReport))
     fileName = str(int(time())) + '.report'
     reportsPath = '/mnt/ebs/frostwire-files/frostwire-data/doctor.frostwire.com'
     versionBuildFolder = "%s-%s" % (bugReport.version, bugReport.build)
     reportFolder = reportsPath + os.path.sep + versionBuildFolder
     if not os.path.exists(reportFolder):
         os.makedirs(reportFolder)
     reportPath = reportFolder + os.path.sep + fileName
     #log("report path: " + reportPath)
     fd = open(reportPath, 'w')
     fd.write('version=' + bugReport.version + "\n")
     fd.write('build=' + bugReport.build + "\n")
     # TODO: parse javaVersion, memInfo, osInfo, this data hasn't been parsed yet with the constructor we're using
     fd.write("==========\n")
     fd.write(bugReport.rawMessage + "\n")
     fd.write("==========\n")
     fd.flush()
     fd.close()
     resp = "OK"
     return resp

# In this folders we'll store and manage the error reports we receive from doctor.frostwire.com
#
# The goal will be to:
# 2. Process the reports and find crash clusters
# 3. Have a web UI to parse through these guys by build number
#
# Let's see if we can group the reports by build number, or <version>-<build-number>

#For testing purposes only	 
if __name__ == '__main__':
    errorReportAsArray = ['FrostWire version 6.5.6 build 243\n', 'Java version 1.8.0_121 from Oracle Corporation\n', 'Mac OS X v. 10.12.6 on x86_64\n', 'Free/total memory: 156209760/287834112\n', '\n', 'java.lang.Exception: Example Bug\n', '\tat com.limegroup.gnutella.gui.options.panes.BugsPaneItem$1.actionPerformed(Unknown Source)\n', '\tat javax.swing.AbstractButton.fireActionPerformed(Unknown Source)\n', '\tat javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)\n', '\tat javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)\n', '\tat javax.swing.DefaultButtonModel.setPressed(Unknown Source)\n', '\tat javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)\n', '\tat java.awt.Component.processMouseEvent(Unknown Source)\n', '\tat javax.swing.JComponent.processMouseEvent(Unknown Source)\n', '\tat java.awt.Component.processEvent(Unknown Source)\n', '\tat java.awt.Container.processEvent(Unknown Source)\n', '\tat java.awt.Component.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Container.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Component.dispatchEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)\n', '\tat java.awt.Container.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Window.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Component.dispatchEvent(Unknown Source)\n', '\tat java.awt.EventQueue.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.EventQueue.access$500(Unknown Source)\n', '\tat java.awt.EventQueue$3.run(Unknown Source)\n', '\tat java.awt.EventQueue$3.run(Unknown Source)\n', '\tat java.security.AccessController.doPrivileged(Native Method)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.awt.EventQueue$4.run(Unknown Source)\n', '\tat java.awt.EventQueue$4.run(Unknown Source)\n', '\tat java.security.AccessController.doPrivileged(Native Method)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.awt.EventQueue.dispatchEvent(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEvents(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEvents(Unknown Source)\n', '\tat java.awt.EventDispatchThread.run(Unknown Source)\n', '\n', '\n', 'Detail: FrostWire version 6.5.6 build 243\n', 'Java version 1.8.0_121 from Oracle Corporation\n', 'Mac OS X v. 10.12.6 on x86_64\n', 'Free/total memory: 156209760/287834112\n', '\n', 'java.lang.Exception: Example Bug\n', '\tat com.limegroup.gnutella.gui.options.panes.BugsPaneItem$1.actionPerformed(Unknown Source)\n', '\tat javax.swing.AbstractButton.fireActionPerformed(Unknown Source)\n', '\tat javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)\n', '\tat javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)\n', '\tat javax.swing.DefaultButtonModel.setPressed(Unknown Source)\n', '\tat javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)\n', '\tat java.awt.Component.processMouseEvent(Unknown Source)\n', '\tat javax.swing.JComponent.processMouseEvent(Unknown Source)\n', '\tat java.awt.Component.processEvent(Unknown Source)\n', '\tat java.awt.Container.processEvent(Unknown Source)\n', '\tat java.awt.Component.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Container.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Component.dispatchEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)\n', '\tat java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)\n', '\tat java.awt.Container.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Window.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.Component.dispatchEvent(Unknown Source)\n', '\tat java.awt.EventQueue.dispatchEventImpl(Unknown Source)\n', '\tat java.awt.EventQueue.access$500(Unknown Source)\n', '\tat java.awt.EventQueue$3.run(Unknown Source)\n', '\tat java.awt.EventQueue$3.run(Unknown Source)\n', '\tat java.security.AccessController.doPrivileged(Native Method)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.awt.EventQueue$4.run(Unknown Source)\n', '\tat java.awt.EventQueue$4.run(Unknown Source)\n', '\tat java.security.AccessController.doPrivileged(Native Method)\n', '\tat java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)\n', '\tat java.awt.EventQueue.dispatchEvent(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEvents(Unknown Source)\n', '\tat java.awt.EventDispatchThread.pumpEvents(Unknown Source)\n', '\tat java.awt.EventDispatchThread.run(Unknown Source)\n', '\n', '\n', 'Detail: Example\n', '\n', '-- class path --\n', '/Users/gubatron/Library/Application Support/FrostWire/frostwire.jar\n', '/Users/gubatron/Library/Application Support/FrostWire/rt.jar\n', '-- listing session information --\n', 'Current thread: AWT-EventQueue-0\n', 'Active Threads: 13\n', 'Peak Number of Thread: 30\n', 'System Load Avg: 2.66064453125\n', 'Objects Pending GC: 0\n', 'Free Space In Settings: 100596531200\n', 'Free Space In Incomplete: \n', 'Heap Memory Usage: init = 268435456(262144K) used = 131624352(128539K) committed = 287834112(281088K) max = 477626368(466432K)\n', 'Non-Heap Memory Usage: init = 2555904(2496K) used = 71700712(70020K) committed = 72900608(71192K) max = -1(-1K)\n', '\n', '-- listing threads --\n', 'Deadlock Detection Thread: 1\n', 'ManualGC: 1\n', 'MPlayer output parser: 1\n', 'AWT-EventQueue-0: 1\n', 'Okio Watchdog: 1\n', 'MVStore background writer nio:/Users/gubatron/Library/Preferences/FrostWire5/dbs/crawldb.1/crawldb.mv.db: 1\n', 'GUIMediator-updateConnectionQuality-thread-idle: 1\n', 'SessionManager-alertsLoop: 1\n', 'DestroyJavaVM: 1\n', 'OkHttp ConnectionPool: 1\n', 'MVStore background writer nio:/Users/gubatron/Library/Preferences/FrostWire5/library_db/library_db.mv.db: 1\n', 'VPNStatusRefresher-thread-idle: 1\n', 'Thread-2: 1\n', '\n', '\n', '-- listing properties --\n', 'APP_HEIGHT=792\n', 'MONOVA_SEARCH2_ENABLED=false\n', 'UX_STATS_ENABLED=false\n', 'LAST_EXPIRE_TIME=1503011715538\n', 'SOUNDCLOUD_SEARCH2_ENABLED=false\n', 'LAST_MEDIA_TYPE_USED=video\n', 'TORRENTDOWNLOADS_SEARCH_ENABLED=false\n', 'RUN_ONCE=true\n', 'EZTV_SEARCH_ENABLED=false\n', 'COUNTRY=\n', 'LAST_SELECTED_LIBRARY_DIRECTORY_HOLDER_OFFSET=5\n', 'YOUTUBE_SEARCH2_ENABLED=false\n', 'TORLOCK_SEARCH_ENABLED=false\n', 'LIMETORRENTS_SEARCH_ENABLED=false\n', 'OPTIONS_LAST_SELECTED_KEY=OPTIONS_BUGS_MAIN_TITLE\n', 'YIFY_SEARCH_ENABLED=false\n', 'FROSTCLICK_SEARCH_ENABLED=false\n', 'ZOOQLE_SEARCH_ENABLED=false\n', 'BTMEDIATOR_COLUMN_SORT_INDEX=16\n', 'APP_WIDTH=1236\n', 'INSTALLED=true\n', 'TPB_SEARCH2_ENABLED=false\n', 'WINDOW_Y=23\n', 'WINDOW_X=99\n', '\n', '\n', '**************** Comments from the user ****************\n', 'null\n', '\n', 'CLASSPATH:\n', '  /Users/gubatron/Library/Application%20Support/FrostWire/frostwire.jar\n', '  /Users/gubatron/Library/Application%20Support/FrostWire/rt.jar\n', '\n', 'EXPERIMENTAL FEATURES SETTINGS:\n', '    ALPHA FEATURES: false\n', '    BETA FEATURES: true\n', '\n', '\n', '-- class path --\n', '/Users/gubatron/Library/Application Support/FrostWire/frostwire.jar\n', '/Users/gubatron/Library/Application Support/FrostWire/rt.jar\n', '-- listing session information --\n', 'Current thread: test-bug-report-thread\n', 'Active Threads: 13\n', 'Peak Number of Thread: 30\n', 'System Load Avg: 2.66064453125\n', 'Objects Pending GC: 0\n', 'Free Space In Settings: 100596531200\n', 'Free Space In Incomplete: \n', 'Heap Memory Usage: init = 268435456(262144K) used = 131624352(128539K) committed = 287834112(281088K) max = 477626368(466432K)\n', 'Non-Heap Memory Usage: init = 2555904(2496K) used = 71701672(70021K) committed = 72900608(71192K) max = -1(-1K)\n', '\n', '-- listing threads --\n', 'Deadlock Detection Thread: 1\n', 'ManualGC: 1\n', 'MPlayer output parser: 1\n', 'AWT-EventQueue-0: 1\n', 'Okio Watchdog: 1\n', 'MVStore background writer nio:/Users/gubatron/Library/Preferences/FrostWire5/dbs/crawldb.1/crawldb.mv.db: 1\n', 'GUIMediator-updateConnectionQuality-thread-idle: 1\n', 'SessionManager-alertsLoop: 1\n', 'DestroyJavaVM: 1\n', 'OkHttp ConnectionPool: 1\n', 'MVStore background writer nio:/Users/gubatron/Library/Preferences/FrostWire5/library_db/library_db.mv.db: 1\n', 'VPNStatusRefresher-thread-idle: 1\n', 'Thread-2: 1\n', '\n', '\n', '-- listing properties --\n', 'APP_HEIGHT=792\n', 'MONOVA_SEARCH2_ENABLED=false\n', 'UX_STATS_ENABLED=false\n', 'LAST_EXPIRE_TIME=1503011715538\n', 'SOUNDCLOUD_SEARCH2_ENABLED=false\n', 'LAST_MEDIA_TYPE_USED=video\n', 'TORRENTDOWNLOADS_SEARCH_ENABLED=false\n', 'RUN_ONCE=true\n', 'EZTV_SEARCH_ENABLED=false\n', 'COUNTRY=\n', 'LAST_SELECTED_LIBRARY_DIRECTORY_HOLDER_OFFSET=5\n', 'YOUTUBE_SEARCH2_ENABLED=false\n', 'TORLOCK_SEARCH_ENABLED=false\n', 'LIMETORRENTS_SEARCH_ENABLED=false\n', 'OPTIONS_LAST_SELECTED_KEY=OPTIONS_BUGS_MAIN_TITLE\n', 'YIFY_SEARCH_ENABLED=false\n', 'FROSTCLICK_SEARCH_ENABLED=false\n', 'ZOOQLE_SEARCH_ENABLED=false\n', 'BTMEDIATOR_COLUMN_SORT_INDEX=16\n', 'APP_WIDTH=1236\n', 'INSTALLED=true\n', 'TPB_SEARCH2_ENABLED=false\n', 'WINDOW_Y=23\n', 'WINDOW_X=99\n', '\n', '\n', '**************** Comments from the user ****************\n', 'Please add any comments you may have (e.g what caused the error).\n', 'Thank you and please use English.\n'] 
    print errorReportAsArray[0]
    rawReport = "".join(errorReportAsArray)
    bugReport = BugReport(errorReportAsArray, rawReport)
    print bugReport.rawMessage
    
