768 lines
25 KiB
Python
768 lines
25 KiB
Python
|
import BaseHTTPServer
|
||
|
import SimpleHTTPServer
|
||
|
import os
|
||
|
import sys
|
||
|
import urllib, urlparse
|
||
|
import posixpath
|
||
|
import StringIO
|
||
|
import re
|
||
|
import shutil
|
||
|
import threading
|
||
|
import time
|
||
|
import socket
|
||
|
import itertools
|
||
|
|
||
|
import Reporter
|
||
|
import ConfigParser
|
||
|
|
||
|
###
|
||
|
# Various patterns matched or replaced by server.
|
||
|
|
||
|
kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
|
||
|
|
||
|
kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
|
||
|
|
||
|
# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
|
||
|
|
||
|
kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
|
||
|
kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
|
||
|
|
||
|
kReportReplacements = []
|
||
|
|
||
|
# Add custom javascript.
|
||
|
kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
|
||
|
<script language="javascript" type="text/javascript">
|
||
|
function load(url) {
|
||
|
if (window.XMLHttpRequest) {
|
||
|
req = new XMLHttpRequest();
|
||
|
} else if (window.ActiveXObject) {
|
||
|
req = new ActiveXObject("Microsoft.XMLHTTP");
|
||
|
}
|
||
|
if (req != undefined) {
|
||
|
req.open("GET", url, true);
|
||
|
req.send("");
|
||
|
}
|
||
|
}
|
||
|
</script>"""))
|
||
|
|
||
|
# Insert additional columns.
|
||
|
kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
|
||
|
'<td></td><td></td>'))
|
||
|
|
||
|
# Insert report bug and open file links.
|
||
|
kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
|
||
|
('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
|
||
|
'<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
|
||
|
|
||
|
kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
|
||
|
'<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
|
||
|
|
||
|
kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
|
||
|
'<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
|
||
|
|
||
|
# Insert report crashes link.
|
||
|
|
||
|
# Disabled for the time being until we decide exactly when this should
|
||
|
# be enabled. Also the radar reporter needs to be fixed to report
|
||
|
# multiple files.
|
||
|
|
||
|
#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
|
||
|
# '<br>These files will automatically be attached to ' +
|
||
|
# 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
|
||
|
|
||
|
###
|
||
|
# Other simple parameters
|
||
|
|
||
|
kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
|
||
|
kConfigPath = os.path.expanduser('~/.scanview.cfg')
|
||
|
|
||
|
###
|
||
|
|
||
|
__version__ = "0.1"
|
||
|
|
||
|
__all__ = ["create_server"]
|
||
|
|
||
|
class ReporterThread(threading.Thread):
|
||
|
def __init__(self, report, reporter, parameters, server):
|
||
|
threading.Thread.__init__(self)
|
||
|
self.report = report
|
||
|
self.server = server
|
||
|
self.reporter = reporter
|
||
|
self.parameters = parameters
|
||
|
self.success = False
|
||
|
self.status = None
|
||
|
|
||
|
def run(self):
|
||
|
result = None
|
||
|
try:
|
||
|
if self.server.options.debug:
|
||
|
print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
|
||
|
self.status = self.reporter.fileReport(self.report, self.parameters)
|
||
|
self.success = True
|
||
|
time.sleep(3)
|
||
|
if self.server.options.debug:
|
||
|
print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
|
||
|
except Reporter.ReportFailure,e:
|
||
|
self.status = e.value
|
||
|
except Exception,e:
|
||
|
s = StringIO.StringIO()
|
||
|
import traceback
|
||
|
print >>s,'<b>Unhandled Exception</b><br><pre>'
|
||
|
traceback.print_exc(e,file=s)
|
||
|
print >>s,'</pre>'
|
||
|
self.status = s.getvalue()
|
||
|
|
||
|
class ScanViewServer(BaseHTTPServer.HTTPServer):
|
||
|
def __init__(self, address, handler, root, reporters, options):
|
||
|
BaseHTTPServer.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 >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
|
||
|
|
||
|
def serve_forever(self):
|
||
|
while not self.halted:
|
||
|
if self.options.debug > 1:
|
||
|
print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
|
||
|
try:
|
||
|
self.handle_request()
|
||
|
except OSError,e:
|
||
|
print 'OSError',e.errno
|
||
|
|
||
|
def finish_request(self, request, client_address):
|
||
|
if self.options.autoReload:
|
||
|
import ScanView
|
||
|
self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
|
||
|
BaseHTTPServer.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 >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
|
||
|
return
|
||
|
BaseHTTPServer.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 filter(None, qs.split('&')):
|
||
|
if '=' not in chunk:
|
||
|
name = chunk
|
||
|
value = ''
|
||
|
else:
|
||
|
name, value = chunk.split('=', 1)
|
||
|
name = urllib.unquote(name.replace('+', ' '))
|
||
|
value = urllib.unquote(value.replace('+', ' '))
|
||
|
item = fields.get(name)
|
||
|
if item is None:
|
||
|
fields[name] = [value]
|
||
|
else:
|
||
|
item.append(value)
|
||
|
return fields
|
||
|
|
||
|
class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||
|
server_version = "ScanViewServer/" + __version__
|
||
|
dynamic_mtime = time.time()
|
||
|
|
||
|
def do_HEAD(self):
|
||
|
try:
|
||
|
SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
|
||
|
except Exception,e:
|
||
|
self.handle_exception(e)
|
||
|
|
||
|
def do_GET(self):
|
||
|
try:
|
||
|
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||
|
except Exception,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,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.StringIO()
|
||
|
print >>s, "INTERNAL ERROR\n"
|
||
|
traceback.print_exc(exc, 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(.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 = """\
|
||
|
<a href="/report_crashes">File Bug</a> > """%locals()
|
||
|
else:
|
||
|
reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
|
||
|
report)
|
||
|
fileBug = '<a href="/report/%s">File Bug</a> > ' % 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 = """
|
||
|
<head>
|
||
|
<title>Bug Submission</title>
|
||
|
<link rel="stylesheet" type="text/css" href="/scanview.css" />
|
||
|
</head>
|
||
|
<body>
|
||
|
<h3>
|
||
|
<a href="/">Summary</a> >
|
||
|
%(reportingFor)s
|
||
|
%(fileBug)s
|
||
|
Submit</h3>
|
||
|
<form name="form" action="">
|
||
|
<table class="form">
|
||
|
<tr><td>
|
||
|
<table class="form_group">
|
||
|
<tr>
|
||
|
<td class="form_clabel">Title:</td>
|
||
|
<td class="form_value">
|
||
|
<input type="text" name="title" size="50" value="%(title)s" disabled>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td class="form_label">Description:</td>
|
||
|
<td class="form_value">
|
||
|
<textarea rows="10" cols="80" name="description" disabled>
|
||
|
%(description)s
|
||
|
</textarea>
|
||
|
</td>
|
||
|
</table>
|
||
|
</td></tr>
|
||
|
</table>
|
||
|
</form>
|
||
|
<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
|
||
|
%(message)s
|
||
|
<p>
|
||
|
<hr>
|
||
|
<a href="/">Return to Summary</a>
|
||
|
</body>
|
||
|
</html>"""%locals()
|
||
|
return self.send_string(result)
|
||
|
|
||
|
def send_open_report(self, report):
|
||
|
try:
|
||
|
keys = self.load_report(report)
|
||
|
except IOError:
|
||
|
return self.send_error(400, 'Invalid report.')
|
||
|
|
||
|
file = keys.get('FILE')
|
||
|
if not file or not posixpath.exists(file):
|
||
|
return self.send_error(400, 'File does not exist: "%s"' % file)
|
||
|
|
||
|
import startfile
|
||
|
if self.server.options.debug:
|
||
|
print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
|
||
|
file)
|
||
|
|
||
|
status = startfile.open(file)
|
||
|
if status:
|
||
|
res = 'Opened: "%s"' % file
|
||
|
else:
|
||
|
res = 'Open failed: "%s"' % file
|
||
|
|
||
|
return self.send_string(res, 'text/plain')
|
||
|
|
||
|
def get_report_context(self, report):
|
||
|
class Context:
|
||
|
pass
|
||
|
if report is None or report == 'None':
|
||
|
data = self.load_crashes()
|
||
|
# Don't allow empty reports.
|
||
|
if not data:
|
||
|
raise ValueError, 'No crashes detected!'
|
||
|
c = Context()
|
||
|
c.title = 'clang static analyzer failures'
|
||
|
|
||
|
stderrSummary = ""
|
||
|
for item in data:
|
||
|
if 'stderr' in item:
|
||
|
path = posixpath.join(self.server.root, item['stderr'])
|
||
|
if os.path.exists(path):
|
||
|
lns = itertools.islice(open(path), 0, 10)
|
||
|
stderrSummary += '%s\n--\n%s' % (item.get('src',
|
||
|
'<unknown>'),
|
||
|
''.join(lns))
|
||
|
|
||
|
c.description = """\
|
||
|
The clang static analyzer failed on these inputs:
|
||
|
%s
|
||
|
|
||
|
STDERR Summary
|
||
|
--------------
|
||
|
%s
|
||
|
""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
|
||
|
stderrSummary)
|
||
|
c.reportSource = None
|
||
|
c.navMarkup = "Report Crashes > "
|
||
|
c.files = []
|
||
|
for item in data:
|
||
|
c.files.append(item.get('src',''))
|
||
|
c.files.append(posixpath.join(self.server.root,
|
||
|
item.get('file','')))
|
||
|
c.files.append(posixpath.join(self.server.root,
|
||
|
item.get('clangfile','')))
|
||
|
c.files.append(posixpath.join(self.server.root,
|
||
|
item.get('stderr','')))
|
||
|
c.files.append(posixpath.join(self.server.root,
|
||
|
item.get('info','')))
|
||
|
# Just in case something failed, ignore files which don't
|
||
|
# exist.
|
||
|
c.files = [f for f in c.files
|
||
|
if os.path.exists(f) and os.path.isfile(f)]
|
||
|
else:
|
||
|
# Check that this is a valid report.
|
||
|
path = posixpath.join(self.server.root, 'report-%s.html' % report)
|
||
|
if not posixpath.exists(path):
|
||
|
raise ValueError, 'Invalid report ID'
|
||
|
keys = self.load_report(report)
|
||
|
c = Context()
|
||
|
c.title = keys.get('DESC','clang error (unrecognized')
|
||
|
c.description = """\
|
||
|
Bug reported by the clang static analyzer.
|
||
|
|
||
|
Description: %s
|
||
|
File: %s
|
||
|
Line: %s
|
||
|
"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
|
||
|
c.reportSource = 'report-%s.html' % report
|
||
|
c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
|
||
|
report)
|
||
|
|
||
|
c.files = [path]
|
||
|
return c
|
||
|
|
||
|
def send_report(self, report, configOverrides=None):
|
||
|
def getConfigOption(section, field):
|
||
|
if (configOverrides is not None and
|
||
|
section in configOverrides and
|
||
|
field in configOverrides[section]):
|
||
|
return configOverrides[section][field]
|
||
|
return self.server.config.get(section, field)
|
||
|
|
||
|
# report is None is used for crashes
|
||
|
try:
|
||
|
c = self.get_report_context(report)
|
||
|
except ValueError, e:
|
||
|
return self.send_error(400, e.message)
|
||
|
|
||
|
title = c.title
|
||
|
description= c.description
|
||
|
reportingFor = c.navMarkup
|
||
|
if c.reportSource is None:
|
||
|
extraIFrame = ""
|
||
|
else:
|
||
|
extraIFrame = """\
|
||
|
<iframe src="/%s" width="100%%" height="40%%"
|
||
|
scrolling="auto" frameborder="1">
|
||
|
<a href="/%s">View Bug Report</a>
|
||
|
</iframe>""" % (c.reportSource, c.reportSource)
|
||
|
|
||
|
reporterSelections = []
|
||
|
reporterOptions = []
|
||
|
|
||
|
try:
|
||
|
active = int(getConfigOption('ScanView','reporter'))
|
||
|
except:
|
||
|
active = 0
|
||
|
for i,r in enumerate(self.server.reporters):
|
||
|
selected = (i == active)
|
||
|
if selected:
|
||
|
selectedStr = ' selected'
|
||
|
else:
|
||
|
selectedStr = ''
|
||
|
reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
|
||
|
options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
|
||
|
display = ('none','')[selected]
|
||
|
reporterOptions.append("""\
|
||
|
<tr id="%sReporterOptions" style="display:%s">
|
||
|
<td class="form_label">%s Options</td>
|
||
|
<td class="form_value">
|
||
|
<table class="form_inner_group">
|
||
|
%s
|
||
|
</table>
|
||
|
</td>
|
||
|
</tr>
|
||
|
"""%(r.getName(),display,r.getName(),options))
|
||
|
reporterSelections = '\n'.join(reporterSelections)
|
||
|
reporterOptionsDivs = '\n'.join(reporterOptions)
|
||
|
reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
|
||
|
|
||
|
if c.files:
|
||
|
fieldSize = min(5, len(c.files))
|
||
|
attachFileOptions = '\n'.join(["""\
|
||
|
<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
|
||
|
attachFileRow = """\
|
||
|
<tr>
|
||
|
<td class="form_label">Attach:</td>
|
||
|
<td class="form_value">
|
||
|
<select style="width:100%%" name="files" multiple size=%d>
|
||
|
%s
|
||
|
</select>
|
||
|
</td>
|
||
|
</tr>
|
||
|
""" % (min(5, len(c.files)), attachFileOptions)
|
||
|
else:
|
||
|
attachFileRow = ""
|
||
|
|
||
|
result = """<html>
|
||
|
<head>
|
||
|
<title>File Bug</title>
|
||
|
<link rel="stylesheet" type="text/css" href="/scanview.css" />
|
||
|
</head>
|
||
|
<script language="javascript" type="text/javascript">
|
||
|
var reporters = %(reportersArray)s;
|
||
|
function updateReporterOptions() {
|
||
|
index = document.getElementById('reporter').selectedIndex;
|
||
|
for (var i=0; i < reporters.length; ++i) {
|
||
|
o = document.getElementById(reporters[i] + "ReporterOptions");
|
||
|
if (i == index) {
|
||
|
o.style.display = "";
|
||
|
} else {
|
||
|
o.style.display = "none";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
<body onLoad="updateReporterOptions()">
|
||
|
<h3>
|
||
|
<a href="/">Summary</a> >
|
||
|
%(reportingFor)s
|
||
|
File Bug</h3>
|
||
|
<form name="form" action="/report_submit" method="post">
|
||
|
<input type="hidden" name="report" value="%(report)s">
|
||
|
|
||
|
<table class="form">
|
||
|
<tr><td>
|
||
|
<table class="form_group">
|
||
|
<tr>
|
||
|
<td class="form_clabel">Title:</td>
|
||
|
<td class="form_value">
|
||
|
<input type="text" name="title" size="50" value="%(title)s">
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td class="form_label">Description:</td>
|
||
|
<td class="form_value">
|
||
|
<textarea rows="10" cols="80" name="description">
|
||
|
%(description)s
|
||
|
</textarea>
|
||
|
</td>
|
||
|
</tr>
|
||
|
|
||
|
%(attachFileRow)s
|
||
|
|
||
|
</table>
|
||
|
<br>
|
||
|
<table class="form_group">
|
||
|
<tr>
|
||
|
<td class="form_clabel">Method:</td>
|
||
|
<td class="form_value">
|
||
|
<select id="reporter" name="reporter" onChange="updateReporterOptions()">
|
||
|
%(reporterSelections)s
|
||
|
</select>
|
||
|
</td>
|
||
|
</tr>
|
||
|
%(reporterOptionsDivs)s
|
||
|
</table>
|
||
|
<br>
|
||
|
</td></tr>
|
||
|
<tr><td class="form_submit">
|
||
|
<input align="right" type="submit" name="Submit" value="Submit">
|
||
|
</td></tr>
|
||
|
</table>
|
||
|
</form>
|
||
|
|
||
|
%(extraIFrame)s
|
||
|
|
||
|
</body>
|
||
|
</html>"""%locals()
|
||
|
|
||
|
return self.send_string(result)
|
||
|
|
||
|
def send_head(self, fields=None):
|
||
|
if (self.server.options.onlyServeLocal and
|
||
|
self.client_address[0] != '127.0.0.1'):
|
||
|
return self.send_error(401, 'Unauthorized host.')
|
||
|
|
||
|
if fields is None:
|
||
|
fields = {}
|
||
|
self.fields = fields
|
||
|
|
||
|
o = urlparse.urlparse(self.path)
|
||
|
self.fields = parse_query(o.query, fields)
|
||
|
path = posixpath.normpath(urllib.unquote(o.path))
|
||
|
|
||
|
# Split the components and strip the root prefix.
|
||
|
components = path.split('/')[1:]
|
||
|
|
||
|
# Special case some top-level entries.
|
||
|
if components:
|
||
|
name = components[0]
|
||
|
if len(components)==2:
|
||
|
if name=='report':
|
||
|
return self.send_report(components[1])
|
||
|
elif name=='open':
|
||
|
return self.send_open_report(components[1])
|
||
|
elif len(components)==1:
|
||
|
if name=='quit':
|
||
|
self.server.halt()
|
||
|
return self.send_string('Goodbye.', 'text/plain')
|
||
|
elif name=='report_submit':
|
||
|
return self.send_report_submit()
|
||
|
elif name=='report_crashes':
|
||
|
overrides = { 'ScanView' : {},
|
||
|
'Radar' : {},
|
||
|
'Email' : {} }
|
||
|
for i,r in enumerate(self.server.reporters):
|
||
|
if r.getName() == 'Radar':
|
||
|
overrides['ScanView']['reporter'] = i
|
||
|
break
|
||
|
overrides['Radar']['Component'] = 'llvm - checker'
|
||
|
overrides['Radar']['Component Version'] = 'X'
|
||
|
return self.send_report(None, overrides)
|
||
|
elif name=='favicon.ico':
|
||
|
return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
|
||
|
|
||
|
# Match directory entries.
|
||
|
if components[-1] == '':
|
||
|
components[-1] = 'index.html'
|
||
|
|
||
|
relpath = '/'.join(components)
|
||
|
path = posixpath.join(self.server.root, relpath)
|
||
|
|
||
|
if self.server.options.debug > 1:
|
||
|
print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
|
||
|
path)
|
||
|
return self.send_path(path)
|
||
|
|
||
|
def send_404(self):
|
||
|
self.send_error(404, "File not found")
|
||
|
return None
|
||
|
|
||
|
def send_path(self, path):
|
||
|
# If the requested path is outside the root directory, do not open it
|
||
|
rel = os.path.abspath(path)
|
||
|
if not rel.startswith(os.path.abspath(self.server.root)):
|
||
|
return self.send_404()
|
||
|
|
||
|
ctype = self.guess_type(path)
|
||
|
if ctype.startswith('text/'):
|
||
|
# Patch file instead
|
||
|
return self.send_patched_file(path, ctype)
|
||
|
else:
|
||
|
mode = 'rb'
|
||
|
try:
|
||
|
f = open(path, mode)
|
||
|
except IOError:
|
||
|
return self.send_404()
|
||
|
return self.send_file(f, ctype)
|
||
|
|
||
|
def send_file(self, f, ctype):
|
||
|
# Patch files to add links, but skip binary files.
|
||
|
self.send_response(200)
|
||
|
self.send_header("Content-type", ctype)
|
||
|
fs = os.fstat(f.fileno())
|
||
|
self.send_header("Content-Length", str(fs[6]))
|
||
|
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
|
||
|
self.end_headers()
|
||
|
return f
|
||
|
|
||
|
def send_string(self, s, ctype='text/html', headers=True, mtime=None):
|
||
|
if headers:
|
||
|
self.send_response(200)
|
||
|
self.send_header("Content-type", ctype)
|
||
|
self.send_header("Content-Length", str(len(s)))
|
||
|
if mtime is None:
|
||
|
mtime = self.dynamic_mtime
|
||
|
self.send_header("Last-Modified", self.date_time_string(mtime))
|
||
|
self.end_headers()
|
||
|
return StringIO.StringIO(s)
|
||
|
|
||
|
def send_patched_file(self, path, ctype):
|
||
|
# Allow a very limited set of variables. This is pretty gross.
|
||
|
variables = {}
|
||
|
variables['report'] = ''
|
||
|
m = kReportFileRE.match(path)
|
||
|
if m:
|
||
|
variables['report'] = m.group(2)
|
||
|
|
||
|
try:
|
||
|
f = open(path,'r')
|
||
|
except IOError:
|
||
|
return self.send_404()
|
||
|
fs = os.fstat(f.fileno())
|
||
|
data = f.read()
|
||
|
for a,b in kReportReplacements:
|
||
|
data = a.sub(b % variables, data)
|
||
|
return self.send_string(data, ctype, mtime=fs.st_mtime)
|
||
|
|
||
|
|
||
|
def create_server(address, options, root):
|
||
|
import Reporter
|
||
|
|
||
|
reporters = Reporter.getReporters()
|
||
|
|
||
|
return ScanViewServer(address, ScanViewRequestHandler,
|
||
|
root,
|
||
|
reporters,
|
||
|
options)
|