17330f729Sjoergfrom __future__ import print_function 27330f729Sjoergtry: 37330f729Sjoerg from http.server import HTTPServer, SimpleHTTPRequestHandler 47330f729Sjoergexcept ImportError: 57330f729Sjoerg from BaseHTTPServer import HTTPServer 67330f729Sjoerg from SimpleHTTPServer import SimpleHTTPRequestHandler 77330f729Sjoergimport os 87330f729Sjoergimport sys 97330f729Sjoergtry: 107330f729Sjoerg from urlparse import urlparse 117330f729Sjoerg from urllib import unquote 127330f729Sjoergexcept ImportError: 137330f729Sjoerg from urllib.parse import urlparse, unquote 147330f729Sjoerg 157330f729Sjoergimport posixpath 167330f729Sjoerg 177330f729Sjoergif sys.version_info.major >= 3: 187330f729Sjoerg from io import StringIO, BytesIO 197330f729Sjoergelse: 207330f729Sjoerg from io import BytesIO, BytesIO as StringIO 217330f729Sjoerg 227330f729Sjoergimport re 237330f729Sjoergimport shutil 247330f729Sjoergimport threading 257330f729Sjoergimport time 267330f729Sjoergimport socket 277330f729Sjoergimport itertools 287330f729Sjoerg 297330f729Sjoergimport Reporter 307330f729Sjoergtry: 317330f729Sjoerg import configparser 327330f729Sjoergexcept ImportError: 337330f729Sjoerg import ConfigParser as configparser 347330f729Sjoerg 357330f729Sjoerg### 367330f729Sjoerg# Various patterns matched or replaced by server. 377330f729Sjoerg 387330f729SjoergkReportFileRE = re.compile('(.*/)?report-(.*)\\.html') 397330f729Sjoerg 407330f729SjoergkBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') 417330f729Sjoerg 427330f729Sjoerg# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> 437330f729Sjoerg 447330f729SjoergkReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') 457330f729SjoergkReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') 467330f729Sjoerg 477330f729SjoergkReportReplacements = [] 487330f729Sjoerg 497330f729Sjoerg# Add custom javascript. 507330f729SjoergkReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ 517330f729Sjoerg<script language="javascript" type="text/javascript"> 527330f729Sjoergfunction load(url) { 537330f729Sjoerg if (window.XMLHttpRequest) { 547330f729Sjoerg req = new XMLHttpRequest(); 557330f729Sjoerg } else if (window.ActiveXObject) { 567330f729Sjoerg req = new ActiveXObject("Microsoft.XMLHTTP"); 577330f729Sjoerg } 587330f729Sjoerg if (req != undefined) { 597330f729Sjoerg req.open("GET", url, true); 607330f729Sjoerg req.send(""); 617330f729Sjoerg } 627330f729Sjoerg} 637330f729Sjoerg</script>""")) 647330f729Sjoerg 657330f729Sjoerg# Insert additional columns. 667330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), 677330f729Sjoerg '<td></td><td></td>')) 687330f729Sjoerg 697330f729Sjoerg# Insert report bug and open file links. 707330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), 717330f729Sjoerg ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + 727330f729Sjoerg '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) 737330f729Sjoerg 747330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), 757330f729Sjoerg '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) 767330f729Sjoerg 777330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), 787330f729Sjoerg '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) 797330f729Sjoerg 807330f729Sjoerg# Insert report crashes link. 817330f729Sjoerg 827330f729Sjoerg# Disabled for the time being until we decide exactly when this should 837330f729Sjoerg# be enabled. Also the radar reporter needs to be fixed to report 847330f729Sjoerg# multiple files. 857330f729Sjoerg 867330f729Sjoerg#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), 877330f729Sjoerg# '<br>These files will automatically be attached to ' + 887330f729Sjoerg# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) 897330f729Sjoerg 907330f729Sjoerg### 917330f729Sjoerg# Other simple parameters 927330f729Sjoerg 937330f729SjoergkShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view') 947330f729SjoergkConfigPath = os.path.expanduser('~/.scanview.cfg') 957330f729Sjoerg 967330f729Sjoerg### 977330f729Sjoerg 987330f729Sjoerg__version__ = "0.1" 997330f729Sjoerg 1007330f729Sjoerg__all__ = ["create_server"] 1017330f729Sjoerg 1027330f729Sjoergclass ReporterThread(threading.Thread): 1037330f729Sjoerg def __init__(self, report, reporter, parameters, server): 1047330f729Sjoerg threading.Thread.__init__(self) 1057330f729Sjoerg self.report = report 1067330f729Sjoerg self.server = server 1077330f729Sjoerg self.reporter = reporter 1087330f729Sjoerg self.parameters = parameters 1097330f729Sjoerg self.success = False 1107330f729Sjoerg self.status = None 1117330f729Sjoerg 1127330f729Sjoerg def run(self): 1137330f729Sjoerg result = None 1147330f729Sjoerg try: 1157330f729Sjoerg if self.server.options.debug: 1167330f729Sjoerg print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr) 1177330f729Sjoerg self.status = self.reporter.fileReport(self.report, self.parameters) 1187330f729Sjoerg self.success = True 1197330f729Sjoerg time.sleep(3) 1207330f729Sjoerg if self.server.options.debug: 1217330f729Sjoerg print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr) 1227330f729Sjoerg except Reporter.ReportFailure as e: 1237330f729Sjoerg self.status = e.value 1247330f729Sjoerg except Exception as e: 1257330f729Sjoerg s = StringIO() 1267330f729Sjoerg import traceback 1277330f729Sjoerg print('<b>Unhandled Exception</b><br><pre>', file=s) 1287330f729Sjoerg traceback.print_exc(file=s) 1297330f729Sjoerg print('</pre>', file=s) 1307330f729Sjoerg self.status = s.getvalue() 1317330f729Sjoerg 1327330f729Sjoergclass ScanViewServer(HTTPServer): 1337330f729Sjoerg def __init__(self, address, handler, root, reporters, options): 1347330f729Sjoerg HTTPServer.__init__(self, address, handler) 1357330f729Sjoerg self.root = root 1367330f729Sjoerg self.reporters = reporters 1377330f729Sjoerg self.options = options 1387330f729Sjoerg self.halted = False 1397330f729Sjoerg self.config = None 1407330f729Sjoerg self.load_config() 1417330f729Sjoerg 1427330f729Sjoerg def load_config(self): 1437330f729Sjoerg self.config = configparser.RawConfigParser() 1447330f729Sjoerg 1457330f729Sjoerg # Add defaults 1467330f729Sjoerg self.config.add_section('ScanView') 1477330f729Sjoerg for r in self.reporters: 1487330f729Sjoerg self.config.add_section(r.getName()) 1497330f729Sjoerg for p in r.getParameters(): 1507330f729Sjoerg if p.saveConfigValue(): 1517330f729Sjoerg self.config.set(r.getName(), p.getName(), '') 1527330f729Sjoerg 1537330f729Sjoerg # Ignore parse errors 1547330f729Sjoerg try: 1557330f729Sjoerg self.config.read([kConfigPath]) 1567330f729Sjoerg except: 1577330f729Sjoerg pass 1587330f729Sjoerg 1597330f729Sjoerg # Save on exit 1607330f729Sjoerg import atexit 1617330f729Sjoerg atexit.register(lambda: self.save_config()) 1627330f729Sjoerg 1637330f729Sjoerg def save_config(self): 1647330f729Sjoerg # Ignore errors (only called on exit). 1657330f729Sjoerg try: 1667330f729Sjoerg f = open(kConfigPath,'w') 1677330f729Sjoerg self.config.write(f) 1687330f729Sjoerg f.close() 1697330f729Sjoerg except: 1707330f729Sjoerg pass 1717330f729Sjoerg 1727330f729Sjoerg def halt(self): 1737330f729Sjoerg self.halted = True 1747330f729Sjoerg if self.options.debug: 1757330f729Sjoerg print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) 1767330f729Sjoerg 1777330f729Sjoerg def serve_forever(self): 1787330f729Sjoerg while not self.halted: 1797330f729Sjoerg if self.options.debug > 1: 1807330f729Sjoerg print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) 1817330f729Sjoerg try: 1827330f729Sjoerg self.handle_request() 1837330f729Sjoerg except OSError as e: 1847330f729Sjoerg print('OSError',e.errno) 1857330f729Sjoerg 1867330f729Sjoerg def finish_request(self, request, client_address): 1877330f729Sjoerg if self.options.autoReload: 1887330f729Sjoerg import ScanView 1897330f729Sjoerg self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler 1907330f729Sjoerg HTTPServer.finish_request(self, request, client_address) 1917330f729Sjoerg 1927330f729Sjoerg def handle_error(self, request, client_address): 1937330f729Sjoerg # Ignore socket errors 1947330f729Sjoerg info = sys.exc_info() 1957330f729Sjoerg if info and isinstance(info[1], socket.error): 1967330f729Sjoerg if self.options.debug > 1: 1977330f729Sjoerg print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr) 1987330f729Sjoerg return 1997330f729Sjoerg HTTPServer.handle_error(self, request, client_address) 2007330f729Sjoerg 2017330f729Sjoerg# Borrowed from Quixote, with simplifications. 2027330f729Sjoergdef parse_query(qs, fields=None): 2037330f729Sjoerg if fields is None: 2047330f729Sjoerg fields = {} 2057330f729Sjoerg for chunk in (_f for _f in qs.split('&') if _f): 2067330f729Sjoerg if '=' not in chunk: 2077330f729Sjoerg name = chunk 2087330f729Sjoerg value = '' 2097330f729Sjoerg else: 2107330f729Sjoerg name, value = chunk.split('=', 1) 2117330f729Sjoerg name = unquote(name.replace('+', ' ')) 2127330f729Sjoerg value = unquote(value.replace('+', ' ')) 2137330f729Sjoerg item = fields.get(name) 2147330f729Sjoerg if item is None: 2157330f729Sjoerg fields[name] = [value] 2167330f729Sjoerg else: 2177330f729Sjoerg item.append(value) 2187330f729Sjoerg return fields 2197330f729Sjoerg 2207330f729Sjoergclass ScanViewRequestHandler(SimpleHTTPRequestHandler): 2217330f729Sjoerg server_version = "ScanViewServer/" + __version__ 2227330f729Sjoerg dynamic_mtime = time.time() 2237330f729Sjoerg 2247330f729Sjoerg def do_HEAD(self): 2257330f729Sjoerg try: 2267330f729Sjoerg SimpleHTTPRequestHandler.do_HEAD(self) 2277330f729Sjoerg except Exception as e: 2287330f729Sjoerg self.handle_exception(e) 2297330f729Sjoerg 2307330f729Sjoerg def do_GET(self): 2317330f729Sjoerg try: 2327330f729Sjoerg SimpleHTTPRequestHandler.do_GET(self) 2337330f729Sjoerg except Exception as e: 2347330f729Sjoerg self.handle_exception(e) 2357330f729Sjoerg 2367330f729Sjoerg def do_POST(self): 2377330f729Sjoerg """Serve a POST request.""" 2387330f729Sjoerg try: 2397330f729Sjoerg length = self.headers.getheader('content-length') or "0" 2407330f729Sjoerg try: 2417330f729Sjoerg length = int(length) 2427330f729Sjoerg except: 2437330f729Sjoerg length = 0 2447330f729Sjoerg content = self.rfile.read(length) 2457330f729Sjoerg fields = parse_query(content) 2467330f729Sjoerg f = self.send_head(fields) 2477330f729Sjoerg if f: 2487330f729Sjoerg self.copyfile(f, self.wfile) 2497330f729Sjoerg f.close() 2507330f729Sjoerg except Exception as e: 2517330f729Sjoerg self.handle_exception(e) 2527330f729Sjoerg 2537330f729Sjoerg def log_message(self, format, *args): 2547330f729Sjoerg if self.server.options.debug: 2557330f729Sjoerg sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % 2567330f729Sjoerg (sys.argv[0], 2577330f729Sjoerg self.address_string(), 2587330f729Sjoerg self.log_date_time_string(), 2597330f729Sjoerg format%args)) 2607330f729Sjoerg 2617330f729Sjoerg def load_report(self, report): 2627330f729Sjoerg path = os.path.join(self.server.root, 'report-%s.html'%report) 2637330f729Sjoerg data = open(path).read() 2647330f729Sjoerg keys = {} 2657330f729Sjoerg for item in kBugKeyValueRE.finditer(data): 2667330f729Sjoerg k,v = item.groups() 2677330f729Sjoerg keys[k] = v 2687330f729Sjoerg return keys 2697330f729Sjoerg 2707330f729Sjoerg def load_crashes(self): 2717330f729Sjoerg path = posixpath.join(self.server.root, 'index.html') 2727330f729Sjoerg data = open(path).read() 2737330f729Sjoerg problems = [] 2747330f729Sjoerg for item in kReportCrashEntryRE.finditer(data): 2757330f729Sjoerg fieldData = item.group(1) 2767330f729Sjoerg fields = dict([i.groups() for i in 2777330f729Sjoerg kReportCrashEntryKeyValueRE.finditer(fieldData)]) 2787330f729Sjoerg problems.append(fields) 2797330f729Sjoerg return problems 2807330f729Sjoerg 2817330f729Sjoerg def handle_exception(self, exc): 2827330f729Sjoerg import traceback 2837330f729Sjoerg s = StringIO() 2847330f729Sjoerg print("INTERNAL ERROR\n", file=s) 2857330f729Sjoerg traceback.print_exc(file=s) 2867330f729Sjoerg f = self.send_string(s.getvalue(), 'text/plain') 2877330f729Sjoerg if f: 2887330f729Sjoerg self.copyfile(f, self.wfile) 2897330f729Sjoerg f.close() 2907330f729Sjoerg 2917330f729Sjoerg def get_scalar_field(self, name): 2927330f729Sjoerg if name in self.fields: 2937330f729Sjoerg return self.fields[name][0] 2947330f729Sjoerg else: 2957330f729Sjoerg return None 2967330f729Sjoerg 2977330f729Sjoerg def submit_bug(self, c): 2987330f729Sjoerg title = self.get_scalar_field('title') 2997330f729Sjoerg description = self.get_scalar_field('description') 3007330f729Sjoerg report = self.get_scalar_field('report') 3017330f729Sjoerg reporterIndex = self.get_scalar_field('reporter') 3027330f729Sjoerg files = [] 3037330f729Sjoerg for fileID in self.fields.get('files',[]): 3047330f729Sjoerg try: 3057330f729Sjoerg i = int(fileID) 3067330f729Sjoerg except: 3077330f729Sjoerg i = None 3087330f729Sjoerg if i is None or i<0 or i>=len(c.files): 3097330f729Sjoerg return (False, 'Invalid file ID') 3107330f729Sjoerg files.append(c.files[i]) 3117330f729Sjoerg 3127330f729Sjoerg if not title: 3137330f729Sjoerg return (False, "Missing title.") 3147330f729Sjoerg if not description: 3157330f729Sjoerg return (False, "Missing description.") 3167330f729Sjoerg try: 3177330f729Sjoerg reporterIndex = int(reporterIndex) 3187330f729Sjoerg except: 3197330f729Sjoerg return (False, "Invalid report method.") 3207330f729Sjoerg 3217330f729Sjoerg # Get the reporter and parameters. 3227330f729Sjoerg reporter = self.server.reporters[reporterIndex] 3237330f729Sjoerg parameters = {} 3247330f729Sjoerg for o in reporter.getParameters(): 3257330f729Sjoerg name = '%s_%s'%(reporter.getName(),o.getName()) 3267330f729Sjoerg if name not in self.fields: 3277330f729Sjoerg return (False, 3287330f729Sjoerg 'Missing field "%s" for %s report method.'%(name, 3297330f729Sjoerg reporter.getName())) 3307330f729Sjoerg parameters[o.getName()] = self.get_scalar_field(name) 3317330f729Sjoerg 3327330f729Sjoerg # Update config defaults. 3337330f729Sjoerg if report != 'None': 3347330f729Sjoerg self.server.config.set('ScanView', 'reporter', reporterIndex) 3357330f729Sjoerg for o in reporter.getParameters(): 3367330f729Sjoerg if o.saveConfigValue(): 3377330f729Sjoerg name = o.getName() 3387330f729Sjoerg self.server.config.set(reporter.getName(), name, parameters[name]) 3397330f729Sjoerg 3407330f729Sjoerg # Create the report. 3417330f729Sjoerg bug = Reporter.BugReport(title, description, files) 3427330f729Sjoerg 3437330f729Sjoerg # Kick off a reporting thread. 3447330f729Sjoerg t = ReporterThread(bug, reporter, parameters, self.server) 3457330f729Sjoerg t.start() 3467330f729Sjoerg 3477330f729Sjoerg # Wait for thread to die... 3487330f729Sjoerg while t.isAlive(): 3497330f729Sjoerg time.sleep(.25) 3507330f729Sjoerg submitStatus = t.status 3517330f729Sjoerg 3527330f729Sjoerg return (t.success, t.status) 3537330f729Sjoerg 3547330f729Sjoerg def send_report_submit(self): 3557330f729Sjoerg report = self.get_scalar_field('report') 3567330f729Sjoerg c = self.get_report_context(report) 3577330f729Sjoerg if c.reportSource is None: 3587330f729Sjoerg reportingFor = "Report Crashes > " 3597330f729Sjoerg fileBug = """\ 3607330f729Sjoerg<a href="/report_crashes">File Bug</a> > """%locals() 3617330f729Sjoerg else: 3627330f729Sjoerg reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, 3637330f729Sjoerg report) 3647330f729Sjoerg fileBug = '<a href="/report/%s">File Bug</a> > ' % report 3657330f729Sjoerg title = self.get_scalar_field('title') 3667330f729Sjoerg description = self.get_scalar_field('description') 3677330f729Sjoerg 3687330f729Sjoerg res,message = self.submit_bug(c) 3697330f729Sjoerg 3707330f729Sjoerg if res: 3717330f729Sjoerg statusClass = 'SubmitOk' 3727330f729Sjoerg statusName = 'Succeeded' 3737330f729Sjoerg else: 3747330f729Sjoerg statusClass = 'SubmitFail' 3757330f729Sjoerg statusName = 'Failed' 3767330f729Sjoerg 3777330f729Sjoerg result = """ 3787330f729Sjoerg<head> 3797330f729Sjoerg <title>Bug Submission</title> 3807330f729Sjoerg <link rel="stylesheet" type="text/css" href="/scanview.css" /> 3817330f729Sjoerg</head> 3827330f729Sjoerg<body> 3837330f729Sjoerg<h3> 3847330f729Sjoerg<a href="/">Summary</a> > 3857330f729Sjoerg%(reportingFor)s 3867330f729Sjoerg%(fileBug)s 3877330f729SjoergSubmit</h3> 3887330f729Sjoerg<form name="form" action=""> 3897330f729Sjoerg<table class="form"> 3907330f729Sjoerg<tr><td> 3917330f729Sjoerg<table class="form_group"> 3927330f729Sjoerg<tr> 3937330f729Sjoerg <td class="form_clabel">Title:</td> 3947330f729Sjoerg <td class="form_value"> 3957330f729Sjoerg <input type="text" name="title" size="50" value="%(title)s" disabled> 3967330f729Sjoerg </td> 3977330f729Sjoerg</tr> 3987330f729Sjoerg<tr> 3997330f729Sjoerg <td class="form_label">Description:</td> 4007330f729Sjoerg <td class="form_value"> 4017330f729Sjoerg<textarea rows="10" cols="80" name="description" disabled> 4027330f729Sjoerg%(description)s 4037330f729Sjoerg</textarea> 4047330f729Sjoerg </td> 4057330f729Sjoerg</table> 4067330f729Sjoerg</td></tr> 4077330f729Sjoerg</table> 4087330f729Sjoerg</form> 4097330f729Sjoerg<h1 class="%(statusClass)s">Submission %(statusName)s</h1> 4107330f729Sjoerg%(message)s 4117330f729Sjoerg<p> 4127330f729Sjoerg<hr> 4137330f729Sjoerg<a href="/">Return to Summary</a> 4147330f729Sjoerg</body> 4157330f729Sjoerg</html>"""%locals() 4167330f729Sjoerg return self.send_string(result) 4177330f729Sjoerg 4187330f729Sjoerg def send_open_report(self, report): 4197330f729Sjoerg try: 4207330f729Sjoerg keys = self.load_report(report) 4217330f729Sjoerg except IOError: 4227330f729Sjoerg return self.send_error(400, 'Invalid report.') 4237330f729Sjoerg 4247330f729Sjoerg file = keys.get('FILE') 4257330f729Sjoerg if not file or not posixpath.exists(file): 4267330f729Sjoerg return self.send_error(400, 'File does not exist: "%s"' % file) 4277330f729Sjoerg 4287330f729Sjoerg import startfile 4297330f729Sjoerg if self.server.options.debug: 4307330f729Sjoerg print('%s: SERVER: opening "%s"'%(sys.argv[0], 4317330f729Sjoerg file), file=sys.stderr) 4327330f729Sjoerg 4337330f729Sjoerg status = startfile.open(file) 4347330f729Sjoerg if status: 4357330f729Sjoerg res = 'Opened: "%s"' % file 4367330f729Sjoerg else: 4377330f729Sjoerg res = 'Open failed: "%s"' % file 4387330f729Sjoerg 4397330f729Sjoerg return self.send_string(res, 'text/plain') 4407330f729Sjoerg 4417330f729Sjoerg def get_report_context(self, report): 4427330f729Sjoerg class Context(object): 4437330f729Sjoerg pass 4447330f729Sjoerg if report is None or report == 'None': 4457330f729Sjoerg data = self.load_crashes() 4467330f729Sjoerg # Don't allow empty reports. 4477330f729Sjoerg if not data: 4487330f729Sjoerg raise ValueError('No crashes detected!') 4497330f729Sjoerg c = Context() 4507330f729Sjoerg c.title = 'clang static analyzer failures' 4517330f729Sjoerg 4527330f729Sjoerg stderrSummary = "" 4537330f729Sjoerg for item in data: 4547330f729Sjoerg if 'stderr' in item: 4557330f729Sjoerg path = posixpath.join(self.server.root, item['stderr']) 4567330f729Sjoerg if os.path.exists(path): 4577330f729Sjoerg lns = itertools.islice(open(path), 0, 10) 4587330f729Sjoerg stderrSummary += '%s\n--\n%s' % (item.get('src', 4597330f729Sjoerg '<unknown>'), 4607330f729Sjoerg ''.join(lns)) 4617330f729Sjoerg 4627330f729Sjoerg c.description = """\ 4637330f729SjoergThe clang static analyzer failed on these inputs: 4647330f729Sjoerg%s 4657330f729Sjoerg 4667330f729SjoergSTDERR Summary 4677330f729Sjoerg-------------- 4687330f729Sjoerg%s 4697330f729Sjoerg""" % ('\n'.join([item.get('src','<unknown>') for item in data]), 4707330f729Sjoerg stderrSummary) 4717330f729Sjoerg c.reportSource = None 4727330f729Sjoerg c.navMarkup = "Report Crashes > " 4737330f729Sjoerg c.files = [] 4747330f729Sjoerg for item in data: 4757330f729Sjoerg c.files.append(item.get('src','')) 4767330f729Sjoerg c.files.append(posixpath.join(self.server.root, 4777330f729Sjoerg item.get('file',''))) 4787330f729Sjoerg c.files.append(posixpath.join(self.server.root, 4797330f729Sjoerg item.get('clangfile',''))) 4807330f729Sjoerg c.files.append(posixpath.join(self.server.root, 4817330f729Sjoerg item.get('stderr',''))) 4827330f729Sjoerg c.files.append(posixpath.join(self.server.root, 4837330f729Sjoerg item.get('info',''))) 4847330f729Sjoerg # Just in case something failed, ignore files which don't 4857330f729Sjoerg # exist. 4867330f729Sjoerg c.files = [f for f in c.files 4877330f729Sjoerg if os.path.exists(f) and os.path.isfile(f)] 4887330f729Sjoerg else: 4897330f729Sjoerg # Check that this is a valid report. 4907330f729Sjoerg path = posixpath.join(self.server.root, 'report-%s.html' % report) 4917330f729Sjoerg if not posixpath.exists(path): 4927330f729Sjoerg raise ValueError('Invalid report ID') 4937330f729Sjoerg keys = self.load_report(report) 4947330f729Sjoerg c = Context() 4957330f729Sjoerg c.title = keys.get('DESC','clang error (unrecognized') 4967330f729Sjoerg c.description = """\ 4977330f729SjoergBug reported by the clang static analyzer. 4987330f729Sjoerg 4997330f729SjoergDescription: %s 5007330f729SjoergFile: %s 5017330f729SjoergLine: %s 5027330f729Sjoerg"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) 5037330f729Sjoerg c.reportSource = 'report-%s.html' % report 5047330f729Sjoerg c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, 5057330f729Sjoerg report) 5067330f729Sjoerg 5077330f729Sjoerg c.files = [path] 5087330f729Sjoerg return c 5097330f729Sjoerg 5107330f729Sjoerg def send_report(self, report, configOverrides=None): 5117330f729Sjoerg def getConfigOption(section, field): 5127330f729Sjoerg if (configOverrides is not None and 5137330f729Sjoerg section in configOverrides and 5147330f729Sjoerg field in configOverrides[section]): 5157330f729Sjoerg return configOverrides[section][field] 5167330f729Sjoerg return self.server.config.get(section, field) 5177330f729Sjoerg 5187330f729Sjoerg # report is None is used for crashes 5197330f729Sjoerg try: 5207330f729Sjoerg c = self.get_report_context(report) 5217330f729Sjoerg except ValueError as e: 5227330f729Sjoerg return self.send_error(400, e.message) 5237330f729Sjoerg 5247330f729Sjoerg title = c.title 5257330f729Sjoerg description= c.description 5267330f729Sjoerg reportingFor = c.navMarkup 5277330f729Sjoerg if c.reportSource is None: 5287330f729Sjoerg extraIFrame = "" 5297330f729Sjoerg else: 5307330f729Sjoerg extraIFrame = """\ 5317330f729Sjoerg<iframe src="/%s" width="100%%" height="40%%" 5327330f729Sjoerg scrolling="auto" frameborder="1"> 5337330f729Sjoerg <a href="/%s">View Bug Report</a> 5347330f729Sjoerg</iframe>""" % (c.reportSource, c.reportSource) 5357330f729Sjoerg 5367330f729Sjoerg reporterSelections = [] 5377330f729Sjoerg reporterOptions = [] 5387330f729Sjoerg 5397330f729Sjoerg try: 5407330f729Sjoerg active = int(getConfigOption('ScanView','reporter')) 5417330f729Sjoerg except: 5427330f729Sjoerg active = 0 5437330f729Sjoerg for i,r in enumerate(self.server.reporters): 5447330f729Sjoerg selected = (i == active) 5457330f729Sjoerg if selected: 5467330f729Sjoerg selectedStr = ' selected' 5477330f729Sjoerg else: 5487330f729Sjoerg selectedStr = '' 5497330f729Sjoerg reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) 5507330f729Sjoerg options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) 5517330f729Sjoerg display = ('none','')[selected] 5527330f729Sjoerg reporterOptions.append("""\ 5537330f729Sjoerg<tr id="%sReporterOptions" style="display:%s"> 5547330f729Sjoerg <td class="form_label">%s Options</td> 5557330f729Sjoerg <td class="form_value"> 5567330f729Sjoerg <table class="form_inner_group"> 5577330f729Sjoerg%s 5587330f729Sjoerg </table> 5597330f729Sjoerg </td> 5607330f729Sjoerg</tr> 5617330f729Sjoerg"""%(r.getName(),display,r.getName(),options)) 5627330f729Sjoerg reporterSelections = '\n'.join(reporterSelections) 5637330f729Sjoerg reporterOptionsDivs = '\n'.join(reporterOptions) 5647330f729Sjoerg reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters])) 5657330f729Sjoerg 5667330f729Sjoerg if c.files: 5677330f729Sjoerg fieldSize = min(5, len(c.files)) 5687330f729Sjoerg attachFileOptions = '\n'.join(["""\ 5697330f729Sjoerg<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) 5707330f729Sjoerg attachFileRow = """\ 5717330f729Sjoerg<tr> 5727330f729Sjoerg <td class="form_label">Attach:</td> 5737330f729Sjoerg <td class="form_value"> 5747330f729Sjoerg<select style="width:100%%" name="files" multiple size=%d> 5757330f729Sjoerg%s 5767330f729Sjoerg</select> 5777330f729Sjoerg </td> 5787330f729Sjoerg</tr> 5797330f729Sjoerg""" % (min(5, len(c.files)), attachFileOptions) 5807330f729Sjoerg else: 5817330f729Sjoerg attachFileRow = "" 5827330f729Sjoerg 5837330f729Sjoerg result = """<html> 5847330f729Sjoerg<head> 5857330f729Sjoerg <title>File Bug</title> 5867330f729Sjoerg <link rel="stylesheet" type="text/css" href="/scanview.css" /> 5877330f729Sjoerg</head> 5887330f729Sjoerg<script language="javascript" type="text/javascript"> 5897330f729Sjoergvar reporters = %(reportersArray)s; 5907330f729Sjoergfunction updateReporterOptions() { 5917330f729Sjoerg index = document.getElementById('reporter').selectedIndex; 5927330f729Sjoerg for (var i=0; i < reporters.length; ++i) { 5937330f729Sjoerg o = document.getElementById(reporters[i] + "ReporterOptions"); 5947330f729Sjoerg if (i == index) { 5957330f729Sjoerg o.style.display = ""; 5967330f729Sjoerg } else { 5977330f729Sjoerg o.style.display = "none"; 5987330f729Sjoerg } 5997330f729Sjoerg } 6007330f729Sjoerg} 6017330f729Sjoerg</script> 6027330f729Sjoerg<body onLoad="updateReporterOptions()"> 6037330f729Sjoerg<h3> 6047330f729Sjoerg<a href="/">Summary</a> > 6057330f729Sjoerg%(reportingFor)s 6067330f729SjoergFile Bug</h3> 6077330f729Sjoerg<form name="form" action="/report_submit" method="post"> 6087330f729Sjoerg<input type="hidden" name="report" value="%(report)s"> 6097330f729Sjoerg 6107330f729Sjoerg<table class="form"> 6117330f729Sjoerg<tr><td> 6127330f729Sjoerg<table class="form_group"> 6137330f729Sjoerg<tr> 6147330f729Sjoerg <td class="form_clabel">Title:</td> 6157330f729Sjoerg <td class="form_value"> 6167330f729Sjoerg <input type="text" name="title" size="50" value="%(title)s"> 6177330f729Sjoerg </td> 6187330f729Sjoerg</tr> 6197330f729Sjoerg<tr> 6207330f729Sjoerg <td class="form_label">Description:</td> 6217330f729Sjoerg <td class="form_value"> 6227330f729Sjoerg<textarea rows="10" cols="80" name="description"> 6237330f729Sjoerg%(description)s 6247330f729Sjoerg</textarea> 6257330f729Sjoerg </td> 6267330f729Sjoerg</tr> 6277330f729Sjoerg 6287330f729Sjoerg%(attachFileRow)s 6297330f729Sjoerg 6307330f729Sjoerg</table> 6317330f729Sjoerg<br> 6327330f729Sjoerg<table class="form_group"> 6337330f729Sjoerg<tr> 6347330f729Sjoerg <td class="form_clabel">Method:</td> 6357330f729Sjoerg <td class="form_value"> 6367330f729Sjoerg <select id="reporter" name="reporter" onChange="updateReporterOptions()"> 6377330f729Sjoerg %(reporterSelections)s 6387330f729Sjoerg </select> 6397330f729Sjoerg </td> 6407330f729Sjoerg</tr> 6417330f729Sjoerg%(reporterOptionsDivs)s 6427330f729Sjoerg</table> 6437330f729Sjoerg<br> 6447330f729Sjoerg</td></tr> 6457330f729Sjoerg<tr><td class="form_submit"> 6467330f729Sjoerg <input align="right" type="submit" name="Submit" value="Submit"> 6477330f729Sjoerg</td></tr> 6487330f729Sjoerg</table> 6497330f729Sjoerg</form> 6507330f729Sjoerg 6517330f729Sjoerg%(extraIFrame)s 6527330f729Sjoerg 6537330f729Sjoerg</body> 6547330f729Sjoerg</html>"""%locals() 6557330f729Sjoerg 6567330f729Sjoerg return self.send_string(result) 6577330f729Sjoerg 6587330f729Sjoerg def send_head(self, fields=None): 6597330f729Sjoerg if (self.server.options.onlyServeLocal and 6607330f729Sjoerg self.client_address[0] != '127.0.0.1'): 6617330f729Sjoerg return self.send_error(401, 'Unauthorized host.') 6627330f729Sjoerg 6637330f729Sjoerg if fields is None: 6647330f729Sjoerg fields = {} 6657330f729Sjoerg self.fields = fields 6667330f729Sjoerg 6677330f729Sjoerg o = urlparse(self.path) 6687330f729Sjoerg self.fields = parse_query(o.query, fields) 6697330f729Sjoerg path = posixpath.normpath(unquote(o.path)) 6707330f729Sjoerg 6717330f729Sjoerg # Split the components and strip the root prefix. 6727330f729Sjoerg components = path.split('/')[1:] 6737330f729Sjoerg 6747330f729Sjoerg # Special case some top-level entries. 6757330f729Sjoerg if components: 6767330f729Sjoerg name = components[0] 6777330f729Sjoerg if len(components)==2: 6787330f729Sjoerg if name=='report': 6797330f729Sjoerg return self.send_report(components[1]) 6807330f729Sjoerg elif name=='open': 6817330f729Sjoerg return self.send_open_report(components[1]) 6827330f729Sjoerg elif len(components)==1: 6837330f729Sjoerg if name=='quit': 6847330f729Sjoerg self.server.halt() 6857330f729Sjoerg return self.send_string('Goodbye.', 'text/plain') 6867330f729Sjoerg elif name=='report_submit': 6877330f729Sjoerg return self.send_report_submit() 6887330f729Sjoerg elif name=='report_crashes': 6897330f729Sjoerg overrides = { 'ScanView' : {}, 6907330f729Sjoerg 'Radar' : {}, 6917330f729Sjoerg 'Email' : {} } 6927330f729Sjoerg for i,r in enumerate(self.server.reporters): 6937330f729Sjoerg if r.getName() == 'Radar': 6947330f729Sjoerg overrides['ScanView']['reporter'] = i 6957330f729Sjoerg break 6967330f729Sjoerg overrides['Radar']['Component'] = 'llvm - checker' 6977330f729Sjoerg overrides['Radar']['Component Version'] = 'X' 6987330f729Sjoerg return self.send_report(None, overrides) 6997330f729Sjoerg elif name=='favicon.ico': 7007330f729Sjoerg return self.send_path(posixpath.join(kShare,'bugcatcher.ico')) 7017330f729Sjoerg 7027330f729Sjoerg # Match directory entries. 7037330f729Sjoerg if components[-1] == '': 7047330f729Sjoerg components[-1] = 'index.html' 7057330f729Sjoerg 7067330f729Sjoerg relpath = '/'.join(components) 7077330f729Sjoerg path = posixpath.join(self.server.root, relpath) 7087330f729Sjoerg 7097330f729Sjoerg if self.server.options.debug > 1: 7107330f729Sjoerg print('%s: SERVER: sending path "%s"'%(sys.argv[0], 7117330f729Sjoerg path), file=sys.stderr) 7127330f729Sjoerg return self.send_path(path) 7137330f729Sjoerg 7147330f729Sjoerg def send_404(self): 7157330f729Sjoerg self.send_error(404, "File not found") 7167330f729Sjoerg return None 7177330f729Sjoerg 7187330f729Sjoerg def send_path(self, path): 7197330f729Sjoerg # If the requested path is outside the root directory, do not open it 7207330f729Sjoerg rel = os.path.abspath(path) 7217330f729Sjoerg if not rel.startswith(os.path.abspath(self.server.root)): 7227330f729Sjoerg return self.send_404() 7237330f729Sjoerg 7247330f729Sjoerg ctype = self.guess_type(path) 7257330f729Sjoerg if ctype.startswith('text/'): 7267330f729Sjoerg # Patch file instead 7277330f729Sjoerg return self.send_patched_file(path, ctype) 7287330f729Sjoerg else: 7297330f729Sjoerg mode = 'rb' 7307330f729Sjoerg try: 7317330f729Sjoerg f = open(path, mode) 7327330f729Sjoerg except IOError: 7337330f729Sjoerg return self.send_404() 7347330f729Sjoerg return self.send_file(f, ctype) 7357330f729Sjoerg 7367330f729Sjoerg def send_file(self, f, ctype): 7377330f729Sjoerg # Patch files to add links, but skip binary files. 7387330f729Sjoerg self.send_response(200) 7397330f729Sjoerg self.send_header("Content-type", ctype) 7407330f729Sjoerg fs = os.fstat(f.fileno()) 7417330f729Sjoerg self.send_header("Content-Length", str(fs[6])) 7427330f729Sjoerg self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 7437330f729Sjoerg self.end_headers() 7447330f729Sjoerg return f 7457330f729Sjoerg 7467330f729Sjoerg def send_string(self, s, ctype='text/html', headers=True, mtime=None): 747*e038c9c4Sjoerg encoded_s = s.encode('utf-8') 7487330f729Sjoerg if headers: 7497330f729Sjoerg self.send_response(200) 7507330f729Sjoerg self.send_header("Content-type", ctype) 7517330f729Sjoerg self.send_header("Content-Length", str(len(encoded_s))) 7527330f729Sjoerg if mtime is None: 7537330f729Sjoerg mtime = self.dynamic_mtime 7547330f729Sjoerg self.send_header("Last-Modified", self.date_time_string(mtime)) 7557330f729Sjoerg self.end_headers() 7567330f729Sjoerg return BytesIO(encoded_s) 7577330f729Sjoerg 7587330f729Sjoerg def send_patched_file(self, path, ctype): 7597330f729Sjoerg # Allow a very limited set of variables. This is pretty gross. 7607330f729Sjoerg variables = {} 7617330f729Sjoerg variables['report'] = '' 7627330f729Sjoerg m = kReportFileRE.match(path) 7637330f729Sjoerg if m: 7647330f729Sjoerg variables['report'] = m.group(2) 7657330f729Sjoerg 7667330f729Sjoerg try: 7677330f729Sjoerg f = open(path,'rb') 7687330f729Sjoerg except IOError: 7697330f729Sjoerg return self.send_404() 7707330f729Sjoerg fs = os.fstat(f.fileno()) 7717330f729Sjoerg data = f.read().decode('utf-8') 7727330f729Sjoerg for a,b in kReportReplacements: 7737330f729Sjoerg data = a.sub(b % variables, data) 7747330f729Sjoerg return self.send_string(data, ctype, mtime=fs.st_mtime) 7757330f729Sjoerg 7767330f729Sjoerg 7777330f729Sjoergdef create_server(address, options, root): 7787330f729Sjoerg import Reporter 7797330f729Sjoerg 7807330f729Sjoerg reporters = Reporter.getReporters() 7817330f729Sjoerg 7827330f729Sjoerg return ScanViewServer(address, ScanViewRequestHandler, 7837330f729Sjoerg root, 7847330f729Sjoerg reporters, 7857330f729Sjoerg options) 786