106f32e7eSjoergfrom __future__ import print_function 206f32e7eSjoergtry: 306f32e7eSjoerg from http.server import HTTPServer, SimpleHTTPRequestHandler 406f32e7eSjoergexcept ImportError: 506f32e7eSjoerg from BaseHTTPServer import HTTPServer 606f32e7eSjoerg from SimpleHTTPServer import SimpleHTTPRequestHandler 706f32e7eSjoergimport os 806f32e7eSjoergimport sys 906f32e7eSjoergtry: 1006f32e7eSjoerg from urlparse import urlparse 1106f32e7eSjoerg from urllib import unquote 1206f32e7eSjoergexcept ImportError: 1306f32e7eSjoerg from urllib.parse import urlparse, unquote 1406f32e7eSjoerg 1506f32e7eSjoergimport posixpath 1606f32e7eSjoerg 1706f32e7eSjoergif sys.version_info.major >= 3: 1806f32e7eSjoerg from io import StringIO, BytesIO 1906f32e7eSjoergelse: 2006f32e7eSjoerg from io import BytesIO, BytesIO as StringIO 2106f32e7eSjoerg 2206f32e7eSjoergimport re 2306f32e7eSjoergimport shutil 2406f32e7eSjoergimport threading 2506f32e7eSjoergimport time 2606f32e7eSjoergimport socket 2706f32e7eSjoergimport itertools 2806f32e7eSjoerg 2906f32e7eSjoergimport Reporter 3006f32e7eSjoergtry: 3106f32e7eSjoerg import configparser 3206f32e7eSjoergexcept ImportError: 3306f32e7eSjoerg import ConfigParser as configparser 3406f32e7eSjoerg 3506f32e7eSjoerg### 3606f32e7eSjoerg# Various patterns matched or replaced by server. 3706f32e7eSjoerg 3806f32e7eSjoergkReportFileRE = re.compile('(.*/)?report-(.*)\\.html') 3906f32e7eSjoerg 4006f32e7eSjoergkBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') 4106f32e7eSjoerg 4206f32e7eSjoerg# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> 4306f32e7eSjoerg 4406f32e7eSjoergkReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') 4506f32e7eSjoergkReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') 4606f32e7eSjoerg 4706f32e7eSjoergkReportReplacements = [] 4806f32e7eSjoerg 4906f32e7eSjoerg# Add custom javascript. 5006f32e7eSjoergkReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ 5106f32e7eSjoerg<script language="javascript" type="text/javascript"> 5206f32e7eSjoergfunction load(url) { 5306f32e7eSjoerg if (window.XMLHttpRequest) { 5406f32e7eSjoerg req = new XMLHttpRequest(); 5506f32e7eSjoerg } else if (window.ActiveXObject) { 5606f32e7eSjoerg req = new ActiveXObject("Microsoft.XMLHTTP"); 5706f32e7eSjoerg } 5806f32e7eSjoerg if (req != undefined) { 5906f32e7eSjoerg req.open("GET", url, true); 6006f32e7eSjoerg req.send(""); 6106f32e7eSjoerg } 6206f32e7eSjoerg} 6306f32e7eSjoerg</script>""")) 6406f32e7eSjoerg 6506f32e7eSjoerg# Insert additional columns. 6606f32e7eSjoergkReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), 6706f32e7eSjoerg '<td></td><td></td>')) 6806f32e7eSjoerg 6906f32e7eSjoerg# Insert report bug and open file links. 7006f32e7eSjoergkReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), 7106f32e7eSjoerg ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + 7206f32e7eSjoerg '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) 7306f32e7eSjoerg 7406f32e7eSjoergkReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), 7506f32e7eSjoerg '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) 7606f32e7eSjoerg 7706f32e7eSjoergkReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), 7806f32e7eSjoerg '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) 7906f32e7eSjoerg 8006f32e7eSjoerg# Insert report crashes link. 8106f32e7eSjoerg 8206f32e7eSjoerg# Disabled for the time being until we decide exactly when this should 8306f32e7eSjoerg# be enabled. Also the radar reporter needs to be fixed to report 8406f32e7eSjoerg# multiple files. 8506f32e7eSjoerg 8606f32e7eSjoerg#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), 8706f32e7eSjoerg# '<br>These files will automatically be attached to ' + 8806f32e7eSjoerg# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) 8906f32e7eSjoerg 9006f32e7eSjoerg### 9106f32e7eSjoerg# Other simple parameters 9206f32e7eSjoerg 9306f32e7eSjoergkShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view') 9406f32e7eSjoergkConfigPath = os.path.expanduser('~/.scanview.cfg') 9506f32e7eSjoerg 9606f32e7eSjoerg### 9706f32e7eSjoerg 9806f32e7eSjoerg__version__ = "0.1" 9906f32e7eSjoerg 10006f32e7eSjoerg__all__ = ["create_server"] 10106f32e7eSjoerg 10206f32e7eSjoergclass ReporterThread(threading.Thread): 10306f32e7eSjoerg def __init__(self, report, reporter, parameters, server): 10406f32e7eSjoerg threading.Thread.__init__(self) 10506f32e7eSjoerg self.report = report 10606f32e7eSjoerg self.server = server 10706f32e7eSjoerg self.reporter = reporter 10806f32e7eSjoerg self.parameters = parameters 10906f32e7eSjoerg self.success = False 11006f32e7eSjoerg self.status = None 11106f32e7eSjoerg 11206f32e7eSjoerg def run(self): 11306f32e7eSjoerg result = None 11406f32e7eSjoerg try: 11506f32e7eSjoerg if self.server.options.debug: 11606f32e7eSjoerg print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr) 11706f32e7eSjoerg self.status = self.reporter.fileReport(self.report, self.parameters) 11806f32e7eSjoerg self.success = True 11906f32e7eSjoerg time.sleep(3) 12006f32e7eSjoerg if self.server.options.debug: 12106f32e7eSjoerg print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr) 12206f32e7eSjoerg except Reporter.ReportFailure as e: 12306f32e7eSjoerg self.status = e.value 12406f32e7eSjoerg except Exception as e: 12506f32e7eSjoerg s = StringIO() 12606f32e7eSjoerg import traceback 12706f32e7eSjoerg print('<b>Unhandled Exception</b><br><pre>', file=s) 12806f32e7eSjoerg traceback.print_exc(file=s) 12906f32e7eSjoerg print('</pre>', file=s) 13006f32e7eSjoerg self.status = s.getvalue() 13106f32e7eSjoerg 13206f32e7eSjoergclass ScanViewServer(HTTPServer): 13306f32e7eSjoerg def __init__(self, address, handler, root, reporters, options): 13406f32e7eSjoerg HTTPServer.__init__(self, address, handler) 13506f32e7eSjoerg self.root = root 13606f32e7eSjoerg self.reporters = reporters 13706f32e7eSjoerg self.options = options 13806f32e7eSjoerg self.halted = False 13906f32e7eSjoerg self.config = None 14006f32e7eSjoerg self.load_config() 14106f32e7eSjoerg 14206f32e7eSjoerg def load_config(self): 14306f32e7eSjoerg self.config = configparser.RawConfigParser() 14406f32e7eSjoerg 14506f32e7eSjoerg # Add defaults 14606f32e7eSjoerg self.config.add_section('ScanView') 14706f32e7eSjoerg for r in self.reporters: 14806f32e7eSjoerg self.config.add_section(r.getName()) 14906f32e7eSjoerg for p in r.getParameters(): 15006f32e7eSjoerg if p.saveConfigValue(): 15106f32e7eSjoerg self.config.set(r.getName(), p.getName(), '') 15206f32e7eSjoerg 15306f32e7eSjoerg # Ignore parse errors 15406f32e7eSjoerg try: 15506f32e7eSjoerg self.config.read([kConfigPath]) 15606f32e7eSjoerg except: 15706f32e7eSjoerg pass 15806f32e7eSjoerg 15906f32e7eSjoerg # Save on exit 16006f32e7eSjoerg import atexit 16106f32e7eSjoerg atexit.register(lambda: self.save_config()) 16206f32e7eSjoerg 16306f32e7eSjoerg def save_config(self): 16406f32e7eSjoerg # Ignore errors (only called on exit). 16506f32e7eSjoerg try: 16606f32e7eSjoerg f = open(kConfigPath,'w') 16706f32e7eSjoerg self.config.write(f) 16806f32e7eSjoerg f.close() 16906f32e7eSjoerg except: 17006f32e7eSjoerg pass 17106f32e7eSjoerg 17206f32e7eSjoerg def halt(self): 17306f32e7eSjoerg self.halted = True 17406f32e7eSjoerg if self.options.debug: 17506f32e7eSjoerg print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) 17606f32e7eSjoerg 17706f32e7eSjoerg def serve_forever(self): 17806f32e7eSjoerg while not self.halted: 17906f32e7eSjoerg if self.options.debug > 1: 18006f32e7eSjoerg print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) 18106f32e7eSjoerg try: 18206f32e7eSjoerg self.handle_request() 18306f32e7eSjoerg except OSError as e: 18406f32e7eSjoerg print('OSError',e.errno) 18506f32e7eSjoerg 18606f32e7eSjoerg def finish_request(self, request, client_address): 18706f32e7eSjoerg if self.options.autoReload: 18806f32e7eSjoerg import ScanView 18906f32e7eSjoerg self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler 19006f32e7eSjoerg HTTPServer.finish_request(self, request, client_address) 19106f32e7eSjoerg 19206f32e7eSjoerg def handle_error(self, request, client_address): 19306f32e7eSjoerg # Ignore socket errors 19406f32e7eSjoerg info = sys.exc_info() 19506f32e7eSjoerg if info and isinstance(info[1], socket.error): 19606f32e7eSjoerg if self.options.debug > 1: 19706f32e7eSjoerg print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr) 19806f32e7eSjoerg return 19906f32e7eSjoerg HTTPServer.handle_error(self, request, client_address) 20006f32e7eSjoerg 20106f32e7eSjoerg# Borrowed from Quixote, with simplifications. 20206f32e7eSjoergdef parse_query(qs, fields=None): 20306f32e7eSjoerg if fields is None: 20406f32e7eSjoerg fields = {} 20506f32e7eSjoerg for chunk in (_f for _f in qs.split('&') if _f): 20606f32e7eSjoerg if '=' not in chunk: 20706f32e7eSjoerg name = chunk 20806f32e7eSjoerg value = '' 20906f32e7eSjoerg else: 21006f32e7eSjoerg name, value = chunk.split('=', 1) 21106f32e7eSjoerg name = unquote(name.replace('+', ' ')) 21206f32e7eSjoerg value = unquote(value.replace('+', ' ')) 21306f32e7eSjoerg item = fields.get(name) 21406f32e7eSjoerg if item is None: 21506f32e7eSjoerg fields[name] = [value] 21606f32e7eSjoerg else: 21706f32e7eSjoerg item.append(value) 21806f32e7eSjoerg return fields 21906f32e7eSjoerg 22006f32e7eSjoergclass ScanViewRequestHandler(SimpleHTTPRequestHandler): 22106f32e7eSjoerg server_version = "ScanViewServer/" + __version__ 22206f32e7eSjoerg dynamic_mtime = time.time() 22306f32e7eSjoerg 22406f32e7eSjoerg def do_HEAD(self): 22506f32e7eSjoerg try: 22606f32e7eSjoerg SimpleHTTPRequestHandler.do_HEAD(self) 22706f32e7eSjoerg except Exception as e: 22806f32e7eSjoerg self.handle_exception(e) 22906f32e7eSjoerg 23006f32e7eSjoerg def do_GET(self): 23106f32e7eSjoerg try: 23206f32e7eSjoerg SimpleHTTPRequestHandler.do_GET(self) 23306f32e7eSjoerg except Exception as e: 23406f32e7eSjoerg self.handle_exception(e) 23506f32e7eSjoerg 23606f32e7eSjoerg def do_POST(self): 23706f32e7eSjoerg """Serve a POST request.""" 23806f32e7eSjoerg try: 23906f32e7eSjoerg length = self.headers.getheader('content-length') or "0" 24006f32e7eSjoerg try: 24106f32e7eSjoerg length = int(length) 24206f32e7eSjoerg except: 24306f32e7eSjoerg length = 0 24406f32e7eSjoerg content = self.rfile.read(length) 24506f32e7eSjoerg fields = parse_query(content) 24606f32e7eSjoerg f = self.send_head(fields) 24706f32e7eSjoerg if f: 24806f32e7eSjoerg self.copyfile(f, self.wfile) 24906f32e7eSjoerg f.close() 25006f32e7eSjoerg except Exception as e: 25106f32e7eSjoerg self.handle_exception(e) 25206f32e7eSjoerg 25306f32e7eSjoerg def log_message(self, format, *args): 25406f32e7eSjoerg if self.server.options.debug: 25506f32e7eSjoerg sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % 25606f32e7eSjoerg (sys.argv[0], 25706f32e7eSjoerg self.address_string(), 25806f32e7eSjoerg self.log_date_time_string(), 25906f32e7eSjoerg format%args)) 26006f32e7eSjoerg 26106f32e7eSjoerg def load_report(self, report): 26206f32e7eSjoerg path = os.path.join(self.server.root, 'report-%s.html'%report) 26306f32e7eSjoerg data = open(path).read() 26406f32e7eSjoerg keys = {} 26506f32e7eSjoerg for item in kBugKeyValueRE.finditer(data): 26606f32e7eSjoerg k,v = item.groups() 26706f32e7eSjoerg keys[k] = v 26806f32e7eSjoerg return keys 26906f32e7eSjoerg 27006f32e7eSjoerg def load_crashes(self): 27106f32e7eSjoerg path = posixpath.join(self.server.root, 'index.html') 27206f32e7eSjoerg data = open(path).read() 27306f32e7eSjoerg problems = [] 27406f32e7eSjoerg for item in kReportCrashEntryRE.finditer(data): 27506f32e7eSjoerg fieldData = item.group(1) 27606f32e7eSjoerg fields = dict([i.groups() for i in 27706f32e7eSjoerg kReportCrashEntryKeyValueRE.finditer(fieldData)]) 27806f32e7eSjoerg problems.append(fields) 27906f32e7eSjoerg return problems 28006f32e7eSjoerg 28106f32e7eSjoerg def handle_exception(self, exc): 28206f32e7eSjoerg import traceback 28306f32e7eSjoerg s = StringIO() 28406f32e7eSjoerg print("INTERNAL ERROR\n", file=s) 28506f32e7eSjoerg traceback.print_exc(file=s) 28606f32e7eSjoerg f = self.send_string(s.getvalue(), 'text/plain') 28706f32e7eSjoerg if f: 28806f32e7eSjoerg self.copyfile(f, self.wfile) 28906f32e7eSjoerg f.close() 29006f32e7eSjoerg 29106f32e7eSjoerg def get_scalar_field(self, name): 29206f32e7eSjoerg if name in self.fields: 29306f32e7eSjoerg return self.fields[name][0] 29406f32e7eSjoerg else: 29506f32e7eSjoerg return None 29606f32e7eSjoerg 29706f32e7eSjoerg def submit_bug(self, c): 29806f32e7eSjoerg title = self.get_scalar_field('title') 29906f32e7eSjoerg description = self.get_scalar_field('description') 30006f32e7eSjoerg report = self.get_scalar_field('report') 30106f32e7eSjoerg reporterIndex = self.get_scalar_field('reporter') 30206f32e7eSjoerg files = [] 30306f32e7eSjoerg for fileID in self.fields.get('files',[]): 30406f32e7eSjoerg try: 30506f32e7eSjoerg i = int(fileID) 30606f32e7eSjoerg except: 30706f32e7eSjoerg i = None 30806f32e7eSjoerg if i is None or i<0 or i>=len(c.files): 30906f32e7eSjoerg return (False, 'Invalid file ID') 31006f32e7eSjoerg files.append(c.files[i]) 31106f32e7eSjoerg 31206f32e7eSjoerg if not title: 31306f32e7eSjoerg return (False, "Missing title.") 31406f32e7eSjoerg if not description: 31506f32e7eSjoerg return (False, "Missing description.") 31606f32e7eSjoerg try: 31706f32e7eSjoerg reporterIndex = int(reporterIndex) 31806f32e7eSjoerg except: 31906f32e7eSjoerg return (False, "Invalid report method.") 32006f32e7eSjoerg 32106f32e7eSjoerg # Get the reporter and parameters. 32206f32e7eSjoerg reporter = self.server.reporters[reporterIndex] 32306f32e7eSjoerg parameters = {} 32406f32e7eSjoerg for o in reporter.getParameters(): 32506f32e7eSjoerg name = '%s_%s'%(reporter.getName(),o.getName()) 32606f32e7eSjoerg if name not in self.fields: 32706f32e7eSjoerg return (False, 32806f32e7eSjoerg 'Missing field "%s" for %s report method.'%(name, 32906f32e7eSjoerg reporter.getName())) 33006f32e7eSjoerg parameters[o.getName()] = self.get_scalar_field(name) 33106f32e7eSjoerg 33206f32e7eSjoerg # Update config defaults. 33306f32e7eSjoerg if report != 'None': 33406f32e7eSjoerg self.server.config.set('ScanView', 'reporter', reporterIndex) 33506f32e7eSjoerg for o in reporter.getParameters(): 33606f32e7eSjoerg if o.saveConfigValue(): 33706f32e7eSjoerg name = o.getName() 33806f32e7eSjoerg self.server.config.set(reporter.getName(), name, parameters[name]) 33906f32e7eSjoerg 34006f32e7eSjoerg # Create the report. 34106f32e7eSjoerg bug = Reporter.BugReport(title, description, files) 34206f32e7eSjoerg 34306f32e7eSjoerg # Kick off a reporting thread. 34406f32e7eSjoerg t = ReporterThread(bug, reporter, parameters, self.server) 34506f32e7eSjoerg t.start() 34606f32e7eSjoerg 34706f32e7eSjoerg # Wait for thread to die... 34806f32e7eSjoerg while t.isAlive(): 34906f32e7eSjoerg time.sleep(.25) 35006f32e7eSjoerg submitStatus = t.status 35106f32e7eSjoerg 35206f32e7eSjoerg return (t.success, t.status) 35306f32e7eSjoerg 35406f32e7eSjoerg def send_report_submit(self): 35506f32e7eSjoerg report = self.get_scalar_field('report') 35606f32e7eSjoerg c = self.get_report_context(report) 35706f32e7eSjoerg if c.reportSource is None: 35806f32e7eSjoerg reportingFor = "Report Crashes > " 35906f32e7eSjoerg fileBug = """\ 36006f32e7eSjoerg<a href="/report_crashes">File Bug</a> > """%locals() 36106f32e7eSjoerg else: 36206f32e7eSjoerg reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, 36306f32e7eSjoerg report) 36406f32e7eSjoerg fileBug = '<a href="/report/%s">File Bug</a> > ' % report 36506f32e7eSjoerg title = self.get_scalar_field('title') 36606f32e7eSjoerg description = self.get_scalar_field('description') 36706f32e7eSjoerg 36806f32e7eSjoerg res,message = self.submit_bug(c) 36906f32e7eSjoerg 37006f32e7eSjoerg if res: 37106f32e7eSjoerg statusClass = 'SubmitOk' 37206f32e7eSjoerg statusName = 'Succeeded' 37306f32e7eSjoerg else: 37406f32e7eSjoerg statusClass = 'SubmitFail' 37506f32e7eSjoerg statusName = 'Failed' 37606f32e7eSjoerg 37706f32e7eSjoerg result = """ 37806f32e7eSjoerg<head> 37906f32e7eSjoerg <title>Bug Submission</title> 38006f32e7eSjoerg <link rel="stylesheet" type="text/css" href="/scanview.css" /> 38106f32e7eSjoerg</head> 38206f32e7eSjoerg<body> 38306f32e7eSjoerg<h3> 38406f32e7eSjoerg<a href="/">Summary</a> > 38506f32e7eSjoerg%(reportingFor)s 38606f32e7eSjoerg%(fileBug)s 38706f32e7eSjoergSubmit</h3> 38806f32e7eSjoerg<form name="form" action=""> 38906f32e7eSjoerg<table class="form"> 39006f32e7eSjoerg<tr><td> 39106f32e7eSjoerg<table class="form_group"> 39206f32e7eSjoerg<tr> 39306f32e7eSjoerg <td class="form_clabel">Title:</td> 39406f32e7eSjoerg <td class="form_value"> 39506f32e7eSjoerg <input type="text" name="title" size="50" value="%(title)s" disabled> 39606f32e7eSjoerg </td> 39706f32e7eSjoerg</tr> 39806f32e7eSjoerg<tr> 39906f32e7eSjoerg <td class="form_label">Description:</td> 40006f32e7eSjoerg <td class="form_value"> 40106f32e7eSjoerg<textarea rows="10" cols="80" name="description" disabled> 40206f32e7eSjoerg%(description)s 40306f32e7eSjoerg</textarea> 40406f32e7eSjoerg </td> 40506f32e7eSjoerg</table> 40606f32e7eSjoerg</td></tr> 40706f32e7eSjoerg</table> 40806f32e7eSjoerg</form> 40906f32e7eSjoerg<h1 class="%(statusClass)s">Submission %(statusName)s</h1> 41006f32e7eSjoerg%(message)s 41106f32e7eSjoerg<p> 41206f32e7eSjoerg<hr> 41306f32e7eSjoerg<a href="/">Return to Summary</a> 41406f32e7eSjoerg</body> 41506f32e7eSjoerg</html>"""%locals() 41606f32e7eSjoerg return self.send_string(result) 41706f32e7eSjoerg 41806f32e7eSjoerg def send_open_report(self, report): 41906f32e7eSjoerg try: 42006f32e7eSjoerg keys = self.load_report(report) 42106f32e7eSjoerg except IOError: 42206f32e7eSjoerg return self.send_error(400, 'Invalid report.') 42306f32e7eSjoerg 42406f32e7eSjoerg file = keys.get('FILE') 42506f32e7eSjoerg if not file or not posixpath.exists(file): 42606f32e7eSjoerg return self.send_error(400, 'File does not exist: "%s"' % file) 42706f32e7eSjoerg 42806f32e7eSjoerg import startfile 42906f32e7eSjoerg if self.server.options.debug: 43006f32e7eSjoerg print('%s: SERVER: opening "%s"'%(sys.argv[0], 43106f32e7eSjoerg file), file=sys.stderr) 43206f32e7eSjoerg 43306f32e7eSjoerg status = startfile.open(file) 43406f32e7eSjoerg if status: 43506f32e7eSjoerg res = 'Opened: "%s"' % file 43606f32e7eSjoerg else: 43706f32e7eSjoerg res = 'Open failed: "%s"' % file 43806f32e7eSjoerg 43906f32e7eSjoerg return self.send_string(res, 'text/plain') 44006f32e7eSjoerg 44106f32e7eSjoerg def get_report_context(self, report): 44206f32e7eSjoerg class Context(object): 44306f32e7eSjoerg pass 44406f32e7eSjoerg if report is None or report == 'None': 44506f32e7eSjoerg data = self.load_crashes() 44606f32e7eSjoerg # Don't allow empty reports. 44706f32e7eSjoerg if not data: 44806f32e7eSjoerg raise ValueError('No crashes detected!') 44906f32e7eSjoerg c = Context() 45006f32e7eSjoerg c.title = 'clang static analyzer failures' 45106f32e7eSjoerg 45206f32e7eSjoerg stderrSummary = "" 45306f32e7eSjoerg for item in data: 45406f32e7eSjoerg if 'stderr' in item: 45506f32e7eSjoerg path = posixpath.join(self.server.root, item['stderr']) 45606f32e7eSjoerg if os.path.exists(path): 45706f32e7eSjoerg lns = itertools.islice(open(path), 0, 10) 45806f32e7eSjoerg stderrSummary += '%s\n--\n%s' % (item.get('src', 45906f32e7eSjoerg '<unknown>'), 46006f32e7eSjoerg ''.join(lns)) 46106f32e7eSjoerg 46206f32e7eSjoerg c.description = """\ 46306f32e7eSjoergThe clang static analyzer failed on these inputs: 46406f32e7eSjoerg%s 46506f32e7eSjoerg 46606f32e7eSjoergSTDERR Summary 46706f32e7eSjoerg-------------- 46806f32e7eSjoerg%s 46906f32e7eSjoerg""" % ('\n'.join([item.get('src','<unknown>') for item in data]), 47006f32e7eSjoerg stderrSummary) 47106f32e7eSjoerg c.reportSource = None 47206f32e7eSjoerg c.navMarkup = "Report Crashes > " 47306f32e7eSjoerg c.files = [] 47406f32e7eSjoerg for item in data: 47506f32e7eSjoerg c.files.append(item.get('src','')) 47606f32e7eSjoerg c.files.append(posixpath.join(self.server.root, 47706f32e7eSjoerg item.get('file',''))) 47806f32e7eSjoerg c.files.append(posixpath.join(self.server.root, 47906f32e7eSjoerg item.get('clangfile',''))) 48006f32e7eSjoerg c.files.append(posixpath.join(self.server.root, 48106f32e7eSjoerg item.get('stderr',''))) 48206f32e7eSjoerg c.files.append(posixpath.join(self.server.root, 48306f32e7eSjoerg item.get('info',''))) 48406f32e7eSjoerg # Just in case something failed, ignore files which don't 48506f32e7eSjoerg # exist. 48606f32e7eSjoerg c.files = [f for f in c.files 48706f32e7eSjoerg if os.path.exists(f) and os.path.isfile(f)] 48806f32e7eSjoerg else: 48906f32e7eSjoerg # Check that this is a valid report. 49006f32e7eSjoerg path = posixpath.join(self.server.root, 'report-%s.html' % report) 49106f32e7eSjoerg if not posixpath.exists(path): 49206f32e7eSjoerg raise ValueError('Invalid report ID') 49306f32e7eSjoerg keys = self.load_report(report) 49406f32e7eSjoerg c = Context() 49506f32e7eSjoerg c.title = keys.get('DESC','clang error (unrecognized') 49606f32e7eSjoerg c.description = """\ 49706f32e7eSjoergBug reported by the clang static analyzer. 49806f32e7eSjoerg 49906f32e7eSjoergDescription: %s 50006f32e7eSjoergFile: %s 50106f32e7eSjoergLine: %s 50206f32e7eSjoerg"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) 50306f32e7eSjoerg c.reportSource = 'report-%s.html' % report 50406f32e7eSjoerg c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, 50506f32e7eSjoerg report) 50606f32e7eSjoerg 50706f32e7eSjoerg c.files = [path] 50806f32e7eSjoerg return c 50906f32e7eSjoerg 51006f32e7eSjoerg def send_report(self, report, configOverrides=None): 51106f32e7eSjoerg def getConfigOption(section, field): 51206f32e7eSjoerg if (configOverrides is not None and 51306f32e7eSjoerg section in configOverrides and 51406f32e7eSjoerg field in configOverrides[section]): 51506f32e7eSjoerg return configOverrides[section][field] 51606f32e7eSjoerg return self.server.config.get(section, field) 51706f32e7eSjoerg 51806f32e7eSjoerg # report is None is used for crashes 51906f32e7eSjoerg try: 52006f32e7eSjoerg c = self.get_report_context(report) 52106f32e7eSjoerg except ValueError as e: 52206f32e7eSjoerg return self.send_error(400, e.message) 52306f32e7eSjoerg 52406f32e7eSjoerg title = c.title 52506f32e7eSjoerg description= c.description 52606f32e7eSjoerg reportingFor = c.navMarkup 52706f32e7eSjoerg if c.reportSource is None: 52806f32e7eSjoerg extraIFrame = "" 52906f32e7eSjoerg else: 53006f32e7eSjoerg extraIFrame = """\ 53106f32e7eSjoerg<iframe src="/%s" width="100%%" height="40%%" 53206f32e7eSjoerg scrolling="auto" frameborder="1"> 53306f32e7eSjoerg <a href="/%s">View Bug Report</a> 53406f32e7eSjoerg</iframe>""" % (c.reportSource, c.reportSource) 53506f32e7eSjoerg 53606f32e7eSjoerg reporterSelections = [] 53706f32e7eSjoerg reporterOptions = [] 53806f32e7eSjoerg 53906f32e7eSjoerg try: 54006f32e7eSjoerg active = int(getConfigOption('ScanView','reporter')) 54106f32e7eSjoerg except: 54206f32e7eSjoerg active = 0 54306f32e7eSjoerg for i,r in enumerate(self.server.reporters): 54406f32e7eSjoerg selected = (i == active) 54506f32e7eSjoerg if selected: 54606f32e7eSjoerg selectedStr = ' selected' 54706f32e7eSjoerg else: 54806f32e7eSjoerg selectedStr = '' 54906f32e7eSjoerg reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) 55006f32e7eSjoerg options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) 55106f32e7eSjoerg display = ('none','')[selected] 55206f32e7eSjoerg reporterOptions.append("""\ 55306f32e7eSjoerg<tr id="%sReporterOptions" style="display:%s"> 55406f32e7eSjoerg <td class="form_label">%s Options</td> 55506f32e7eSjoerg <td class="form_value"> 55606f32e7eSjoerg <table class="form_inner_group"> 55706f32e7eSjoerg%s 55806f32e7eSjoerg </table> 55906f32e7eSjoerg </td> 56006f32e7eSjoerg</tr> 56106f32e7eSjoerg"""%(r.getName(),display,r.getName(),options)) 56206f32e7eSjoerg reporterSelections = '\n'.join(reporterSelections) 56306f32e7eSjoerg reporterOptionsDivs = '\n'.join(reporterOptions) 56406f32e7eSjoerg reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters])) 56506f32e7eSjoerg 56606f32e7eSjoerg if c.files: 56706f32e7eSjoerg fieldSize = min(5, len(c.files)) 56806f32e7eSjoerg attachFileOptions = '\n'.join(["""\ 56906f32e7eSjoerg<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) 57006f32e7eSjoerg attachFileRow = """\ 57106f32e7eSjoerg<tr> 57206f32e7eSjoerg <td class="form_label">Attach:</td> 57306f32e7eSjoerg <td class="form_value"> 57406f32e7eSjoerg<select style="width:100%%" name="files" multiple size=%d> 57506f32e7eSjoerg%s 57606f32e7eSjoerg</select> 57706f32e7eSjoerg </td> 57806f32e7eSjoerg</tr> 57906f32e7eSjoerg""" % (min(5, len(c.files)), attachFileOptions) 58006f32e7eSjoerg else: 58106f32e7eSjoerg attachFileRow = "" 58206f32e7eSjoerg 58306f32e7eSjoerg result = """<html> 58406f32e7eSjoerg<head> 58506f32e7eSjoerg <title>File Bug</title> 58606f32e7eSjoerg <link rel="stylesheet" type="text/css" href="/scanview.css" /> 58706f32e7eSjoerg</head> 58806f32e7eSjoerg<script language="javascript" type="text/javascript"> 58906f32e7eSjoergvar reporters = %(reportersArray)s; 59006f32e7eSjoergfunction updateReporterOptions() { 59106f32e7eSjoerg index = document.getElementById('reporter').selectedIndex; 59206f32e7eSjoerg for (var i=0; i < reporters.length; ++i) { 59306f32e7eSjoerg o = document.getElementById(reporters[i] + "ReporterOptions"); 59406f32e7eSjoerg if (i == index) { 59506f32e7eSjoerg o.style.display = ""; 59606f32e7eSjoerg } else { 59706f32e7eSjoerg o.style.display = "none"; 59806f32e7eSjoerg } 59906f32e7eSjoerg } 60006f32e7eSjoerg} 60106f32e7eSjoerg</script> 60206f32e7eSjoerg<body onLoad="updateReporterOptions()"> 60306f32e7eSjoerg<h3> 60406f32e7eSjoerg<a href="/">Summary</a> > 60506f32e7eSjoerg%(reportingFor)s 60606f32e7eSjoergFile Bug</h3> 60706f32e7eSjoerg<form name="form" action="/report_submit" method="post"> 60806f32e7eSjoerg<input type="hidden" name="report" value="%(report)s"> 60906f32e7eSjoerg 61006f32e7eSjoerg<table class="form"> 61106f32e7eSjoerg<tr><td> 61206f32e7eSjoerg<table class="form_group"> 61306f32e7eSjoerg<tr> 61406f32e7eSjoerg <td class="form_clabel">Title:</td> 61506f32e7eSjoerg <td class="form_value"> 61606f32e7eSjoerg <input type="text" name="title" size="50" value="%(title)s"> 61706f32e7eSjoerg </td> 61806f32e7eSjoerg</tr> 61906f32e7eSjoerg<tr> 62006f32e7eSjoerg <td class="form_label">Description:</td> 62106f32e7eSjoerg <td class="form_value"> 62206f32e7eSjoerg<textarea rows="10" cols="80" name="description"> 62306f32e7eSjoerg%(description)s 62406f32e7eSjoerg</textarea> 62506f32e7eSjoerg </td> 62606f32e7eSjoerg</tr> 62706f32e7eSjoerg 62806f32e7eSjoerg%(attachFileRow)s 62906f32e7eSjoerg 63006f32e7eSjoerg</table> 63106f32e7eSjoerg<br> 63206f32e7eSjoerg<table class="form_group"> 63306f32e7eSjoerg<tr> 63406f32e7eSjoerg <td class="form_clabel">Method:</td> 63506f32e7eSjoerg <td class="form_value"> 63606f32e7eSjoerg <select id="reporter" name="reporter" onChange="updateReporterOptions()"> 63706f32e7eSjoerg %(reporterSelections)s 63806f32e7eSjoerg </select> 63906f32e7eSjoerg </td> 64006f32e7eSjoerg</tr> 64106f32e7eSjoerg%(reporterOptionsDivs)s 64206f32e7eSjoerg</table> 64306f32e7eSjoerg<br> 64406f32e7eSjoerg</td></tr> 64506f32e7eSjoerg<tr><td class="form_submit"> 64606f32e7eSjoerg <input align="right" type="submit" name="Submit" value="Submit"> 64706f32e7eSjoerg</td></tr> 64806f32e7eSjoerg</table> 64906f32e7eSjoerg</form> 65006f32e7eSjoerg 65106f32e7eSjoerg%(extraIFrame)s 65206f32e7eSjoerg 65306f32e7eSjoerg</body> 65406f32e7eSjoerg</html>"""%locals() 65506f32e7eSjoerg 65606f32e7eSjoerg return self.send_string(result) 65706f32e7eSjoerg 65806f32e7eSjoerg def send_head(self, fields=None): 65906f32e7eSjoerg if (self.server.options.onlyServeLocal and 66006f32e7eSjoerg self.client_address[0] != '127.0.0.1'): 66106f32e7eSjoerg return self.send_error(401, 'Unauthorized host.') 66206f32e7eSjoerg 66306f32e7eSjoerg if fields is None: 66406f32e7eSjoerg fields = {} 66506f32e7eSjoerg self.fields = fields 66606f32e7eSjoerg 66706f32e7eSjoerg o = urlparse(self.path) 66806f32e7eSjoerg self.fields = parse_query(o.query, fields) 66906f32e7eSjoerg path = posixpath.normpath(unquote(o.path)) 67006f32e7eSjoerg 67106f32e7eSjoerg # Split the components and strip the root prefix. 67206f32e7eSjoerg components = path.split('/')[1:] 67306f32e7eSjoerg 67406f32e7eSjoerg # Special case some top-level entries. 67506f32e7eSjoerg if components: 67606f32e7eSjoerg name = components[0] 67706f32e7eSjoerg if len(components)==2: 67806f32e7eSjoerg if name=='report': 67906f32e7eSjoerg return self.send_report(components[1]) 68006f32e7eSjoerg elif name=='open': 68106f32e7eSjoerg return self.send_open_report(components[1]) 68206f32e7eSjoerg elif len(components)==1: 68306f32e7eSjoerg if name=='quit': 68406f32e7eSjoerg self.server.halt() 68506f32e7eSjoerg return self.send_string('Goodbye.', 'text/plain') 68606f32e7eSjoerg elif name=='report_submit': 68706f32e7eSjoerg return self.send_report_submit() 68806f32e7eSjoerg elif name=='report_crashes': 68906f32e7eSjoerg overrides = { 'ScanView' : {}, 69006f32e7eSjoerg 'Radar' : {}, 69106f32e7eSjoerg 'Email' : {} } 69206f32e7eSjoerg for i,r in enumerate(self.server.reporters): 69306f32e7eSjoerg if r.getName() == 'Radar': 69406f32e7eSjoerg overrides['ScanView']['reporter'] = i 69506f32e7eSjoerg break 69606f32e7eSjoerg overrides['Radar']['Component'] = 'llvm - checker' 69706f32e7eSjoerg overrides['Radar']['Component Version'] = 'X' 69806f32e7eSjoerg return self.send_report(None, overrides) 69906f32e7eSjoerg elif name=='favicon.ico': 70006f32e7eSjoerg return self.send_path(posixpath.join(kShare,'bugcatcher.ico')) 70106f32e7eSjoerg 70206f32e7eSjoerg # Match directory entries. 70306f32e7eSjoerg if components[-1] == '': 70406f32e7eSjoerg components[-1] = 'index.html' 70506f32e7eSjoerg 70606f32e7eSjoerg relpath = '/'.join(components) 70706f32e7eSjoerg path = posixpath.join(self.server.root, relpath) 70806f32e7eSjoerg 70906f32e7eSjoerg if self.server.options.debug > 1: 71006f32e7eSjoerg print('%s: SERVER: sending path "%s"'%(sys.argv[0], 71106f32e7eSjoerg path), file=sys.stderr) 71206f32e7eSjoerg return self.send_path(path) 71306f32e7eSjoerg 71406f32e7eSjoerg def send_404(self): 71506f32e7eSjoerg self.send_error(404, "File not found") 71606f32e7eSjoerg return None 71706f32e7eSjoerg 71806f32e7eSjoerg def send_path(self, path): 71906f32e7eSjoerg # If the requested path is outside the root directory, do not open it 72006f32e7eSjoerg rel = os.path.abspath(path) 72106f32e7eSjoerg if not rel.startswith(os.path.abspath(self.server.root)): 72206f32e7eSjoerg return self.send_404() 72306f32e7eSjoerg 72406f32e7eSjoerg ctype = self.guess_type(path) 72506f32e7eSjoerg if ctype.startswith('text/'): 72606f32e7eSjoerg # Patch file instead 72706f32e7eSjoerg return self.send_patched_file(path, ctype) 72806f32e7eSjoerg else: 72906f32e7eSjoerg mode = 'rb' 73006f32e7eSjoerg try: 73106f32e7eSjoerg f = open(path, mode) 73206f32e7eSjoerg except IOError: 73306f32e7eSjoerg return self.send_404() 73406f32e7eSjoerg return self.send_file(f, ctype) 73506f32e7eSjoerg 73606f32e7eSjoerg def send_file(self, f, ctype): 73706f32e7eSjoerg # Patch files to add links, but skip binary files. 73806f32e7eSjoerg self.send_response(200) 73906f32e7eSjoerg self.send_header("Content-type", ctype) 74006f32e7eSjoerg fs = os.fstat(f.fileno()) 74106f32e7eSjoerg self.send_header("Content-Length", str(fs[6])) 74206f32e7eSjoerg self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 74306f32e7eSjoerg self.end_headers() 74406f32e7eSjoerg return f 74506f32e7eSjoerg 74606f32e7eSjoerg def send_string(self, s, ctype='text/html', headers=True, mtime=None): 747*13fbcb42Sjoerg encoded_s = s.encode('utf-8') 74806f32e7eSjoerg if headers: 74906f32e7eSjoerg self.send_response(200) 75006f32e7eSjoerg self.send_header("Content-type", ctype) 75106f32e7eSjoerg self.send_header("Content-Length", str(len(encoded_s))) 75206f32e7eSjoerg if mtime is None: 75306f32e7eSjoerg mtime = self.dynamic_mtime 75406f32e7eSjoerg self.send_header("Last-Modified", self.date_time_string(mtime)) 75506f32e7eSjoerg self.end_headers() 75606f32e7eSjoerg return BytesIO(encoded_s) 75706f32e7eSjoerg 75806f32e7eSjoerg def send_patched_file(self, path, ctype): 75906f32e7eSjoerg # Allow a very limited set of variables. This is pretty gross. 76006f32e7eSjoerg variables = {} 76106f32e7eSjoerg variables['report'] = '' 76206f32e7eSjoerg m = kReportFileRE.match(path) 76306f32e7eSjoerg if m: 76406f32e7eSjoerg variables['report'] = m.group(2) 76506f32e7eSjoerg 76606f32e7eSjoerg try: 76706f32e7eSjoerg f = open(path,'rb') 76806f32e7eSjoerg except IOError: 76906f32e7eSjoerg return self.send_404() 77006f32e7eSjoerg fs = os.fstat(f.fileno()) 77106f32e7eSjoerg data = f.read().decode('utf-8') 77206f32e7eSjoerg for a,b in kReportReplacements: 77306f32e7eSjoerg data = a.sub(b % variables, data) 77406f32e7eSjoerg return self.send_string(data, ctype, mtime=fs.st_mtime) 77506f32e7eSjoerg 77606f32e7eSjoerg 77706f32e7eSjoergdef create_server(address, options, root): 77806f32e7eSjoerg import Reporter 77906f32e7eSjoerg 78006f32e7eSjoerg reporters = Reporter.getReporters() 78106f32e7eSjoerg 78206f32e7eSjoerg return ScanViewServer(address, ScanViewRequestHandler, 78306f32e7eSjoerg root, 78406f32e7eSjoerg reporters, 78506f32e7eSjoerg options) 786