from http.server import HTTPServer, SimpleHTTPRequestHandler import os import sys from urllib.parse import urlparse, unquote import posixpath from io import StringIO, BytesIO import re import shutil import threading import time import socket import itertools import configparser import Reporter ### # Various patterns matched or replaced by server. kReportFileRE = re.compile("(.*/)?report-(.*)\\.html") kBugKeyValueRE = re.compile("") # kReportCrashEntryRE = re.compile("") kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') kReportReplacements = [] # Add custom javascript. kReportReplacements.append( ( re.compile(""), """\ """, ) ) # Insert additional columns. kReportReplacements.append((re.compile(""), "
", file=s)
traceback.print_exc(file=s)
print("", file=s)
self.status = s.getvalue()
class ScanViewServer(HTTPServer):
def __init__(self, address, handler, root, reporters, options):
HTTPServer.__init__(self, address, handler)
self.root = root
self.reporters = reporters
self.options = options
self.halted = False
self.config = None
self.load_config()
def load_config(self):
self.config = configparser.RawConfigParser()
# Add defaults
self.config.add_section("ScanView")
for r in self.reporters:
self.config.add_section(r.getName())
for p in r.getParameters():
if p.saveConfigValue():
self.config.set(r.getName(), p.getName(), "")
# Ignore parse errors
try:
self.config.read([kConfigPath])
except:
pass
# Save on exit
import atexit
atexit.register(lambda: self.save_config())
def save_config(self):
# Ignore errors (only called on exit).
try:
f = open(kConfigPath, "w")
self.config.write(f)
f.close()
except:
pass
def halt(self):
self.halted = True
if self.options.debug:
print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr)
def serve_forever(self):
while not self.halted:
if self.options.debug > 1:
print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr)
try:
self.handle_request()
except OSError as e:
print("OSError", e.errno)
def finish_request(self, request, client_address):
if self.options.autoReload:
import ScanView
self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
HTTPServer.finish_request(self, request, client_address)
def handle_error(self, request, client_address):
# Ignore socket errors
info = sys.exc_info()
if info and isinstance(info[1], socket.error):
if self.options.debug > 1:
print(
"%s: SERVER: ignored socket error." % (sys.argv[0],),
file=sys.stderr,
)
return
HTTPServer.handle_error(self, request, client_address)
# Borrowed from Quixote, with simplifications.
def parse_query(qs, fields=None):
if fields is None:
fields = {}
for chunk in (_f for _f in qs.split("&") if _f):
if "=" not in chunk:
name = chunk
value = ""
else:
name, value = chunk.split("=", 1)
name = unquote(name.replace("+", " "))
value = unquote(value.replace("+", " "))
item = fields.get(name)
if item is None:
fields[name] = [value]
else:
item.append(value)
return fields
class ScanViewRequestHandler(SimpleHTTPRequestHandler):
server_version = "ScanViewServer/" + __version__
dynamic_mtime = time.time()
def do_HEAD(self):
try:
SimpleHTTPRequestHandler.do_HEAD(self)
except Exception as e:
self.handle_exception(e)
def do_GET(self):
try:
SimpleHTTPRequestHandler.do_GET(self)
except Exception as e:
self.handle_exception(e)
def do_POST(self):
"""Serve a POST request."""
try:
length = self.headers.getheader("content-length") or "0"
try:
length = int(length)
except:
length = 0
content = self.rfile.read(length)
fields = parse_query(content)
f = self.send_head(fields)
if f:
self.copyfile(f, self.wfile)
f.close()
except Exception as e:
self.handle_exception(e)
def log_message(self, format, *args):
if self.server.options.debug:
sys.stderr.write(
"%s: SERVER: %s - - [%s] %s\n"
% (
sys.argv[0],
self.address_string(),
self.log_date_time_string(),
format % args,
)
)
def load_report(self, report):
path = os.path.join(self.server.root, "report-%s.html" % report)
data = open(path).read()
keys = {}
for item in kBugKeyValueRE.finditer(data):
k, v = item.groups()
keys[k] = v
return keys
def load_crashes(self):
path = posixpath.join(self.server.root, "index.html")
data = open(path).read()
problems = []
for item in kReportCrashEntryRE.finditer(data):
fieldData = item.group(1)
fields = dict(
[i.groups() for i in kReportCrashEntryKeyValueRE.finditer(fieldData)]
)
problems.append(fields)
return problems
def handle_exception(self, exc):
import traceback
s = StringIO()
print("INTERNAL ERROR\n", file=s)
traceback.print_exc(file=s)
f = self.send_string(s.getvalue(), "text/plain")
if f:
self.copyfile(f, self.wfile)
f.close()
def get_scalar_field(self, name):
if name in self.fields:
return self.fields[name][0]
else:
return None
def submit_bug(self, c):
title = self.get_scalar_field("title")
description = self.get_scalar_field("description")
report = self.get_scalar_field("report")
reporterIndex = self.get_scalar_field("reporter")
files = []
for fileID in self.fields.get("files", []):
try:
i = int(fileID)
except:
i = None
if i is None or i < 0 or i >= len(c.files):
return (False, "Invalid file ID")
files.append(c.files[i])
if not title:
return (False, "Missing title.")
if not description:
return (False, "Missing description.")
try:
reporterIndex = int(reporterIndex)
except:
return (False, "Invalid report method.")
# Get the reporter and parameters.
reporter = self.server.reporters[reporterIndex]
parameters = {}
for o in reporter.getParameters():
name = "%s_%s" % (reporter.getName(), o.getName())
if name not in self.fields:
return (
False,
'Missing field "%s" for %s report method.'
% (name, reporter.getName()),
)
parameters[o.getName()] = self.get_scalar_field(name)
# Update config defaults.
if report != "None":
self.server.config.set("ScanView", "reporter", reporterIndex)
for o in reporter.getParameters():
if o.saveConfigValue():
name = o.getName()
self.server.config.set(reporter.getName(), name, parameters[name])
# Create the report.
bug = Reporter.BugReport(title, description, files)
# Kick off a reporting thread.
t = ReporterThread(bug, reporter, parameters, self.server)
t.start()
# Wait for thread to die...
while t.isAlive():
time.sleep(0.25)
submitStatus = t.status
return (t.success, t.status)
def send_report_submit(self):
report = self.get_scalar_field("report")
c = self.get_report_context(report)
if c.reportSource is None:
reportingFor = "Report Crashes > "
fileBug = (
"""\
File Bug > """
% locals()
)
else:
reportingFor = 'Report %s > ' % (c.reportSource, report)
fileBug = 'File Bug > ' % report
title = self.get_scalar_field("title")
description = self.get_scalar_field("description")
res, message = self.submit_bug(c)
if res:
statusClass = "SubmitOk"
statusName = "Succeeded"
else:
statusClass = "SubmitFail"
statusName = "Failed"
result = (
"""