1e5dd7070Spatrickfrom __future__ import print_function 2e5dd7070Spatricktry: 3e5dd7070Spatrick from http.server import HTTPServer, SimpleHTTPRequestHandler 4e5dd7070Spatrickexcept ImportError: 5e5dd7070Spatrick from BaseHTTPServer import HTTPServer 6e5dd7070Spatrick from SimpleHTTPServer import SimpleHTTPRequestHandler 7e5dd7070Spatrickimport os 8e5dd7070Spatrickimport sys 9e5dd7070Spatricktry: 10e5dd7070Spatrick from urlparse import urlparse 11e5dd7070Spatrick from urllib import unquote 12e5dd7070Spatrickexcept ImportError: 13e5dd7070Spatrick from urllib.parse import urlparse, unquote 14e5dd7070Spatrick 15e5dd7070Spatrickimport posixpath 16e5dd7070Spatrick 17e5dd7070Spatrickif sys.version_info.major >= 3: 18e5dd7070Spatrick from io import StringIO, BytesIO 19e5dd7070Spatrickelse: 20e5dd7070Spatrick from io import BytesIO, BytesIO as StringIO 21e5dd7070Spatrick 22e5dd7070Spatrickimport re 23e5dd7070Spatrickimport shutil 24e5dd7070Spatrickimport threading 25e5dd7070Spatrickimport time 26e5dd7070Spatrickimport socket 27e5dd7070Spatrickimport itertools 28e5dd7070Spatrick 29e5dd7070Spatrickimport Reporter 30e5dd7070Spatricktry: 31e5dd7070Spatrick import configparser 32e5dd7070Spatrickexcept ImportError: 33e5dd7070Spatrick import ConfigParser as configparser 34e5dd7070Spatrick 35e5dd7070Spatrick### 36e5dd7070Spatrick# Various patterns matched or replaced by server. 37e5dd7070Spatrick 38e5dd7070SpatrickkReportFileRE = re.compile('(.*/)?report-(.*)\\.html') 39e5dd7070Spatrick 40e5dd7070SpatrickkBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') 41e5dd7070Spatrick 42e5dd7070Spatrick# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> 43e5dd7070Spatrick 44e5dd7070SpatrickkReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') 45e5dd7070SpatrickkReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') 46e5dd7070Spatrick 47e5dd7070SpatrickkReportReplacements = [] 48e5dd7070Spatrick 49e5dd7070Spatrick# Add custom javascript. 50e5dd7070SpatrickkReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ 51e5dd7070Spatrick<script language="javascript" type="text/javascript"> 52e5dd7070Spatrickfunction load(url) { 53e5dd7070Spatrick if (window.XMLHttpRequest) { 54e5dd7070Spatrick req = new XMLHttpRequest(); 55e5dd7070Spatrick } else if (window.ActiveXObject) { 56e5dd7070Spatrick req = new ActiveXObject("Microsoft.XMLHTTP"); 57e5dd7070Spatrick } 58e5dd7070Spatrick if (req != undefined) { 59e5dd7070Spatrick req.open("GET", url, true); 60e5dd7070Spatrick req.send(""); 61e5dd7070Spatrick } 62e5dd7070Spatrick} 63e5dd7070Spatrick</script>""")) 64e5dd7070Spatrick 65e5dd7070Spatrick# Insert additional columns. 66e5dd7070SpatrickkReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), 67e5dd7070Spatrick '<td></td><td></td>')) 68e5dd7070Spatrick 69e5dd7070Spatrick# Insert report bug and open file links. 70e5dd7070SpatrickkReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), 71e5dd7070Spatrick ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + 72e5dd7070Spatrick '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) 73e5dd7070Spatrick 74e5dd7070SpatrickkReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), 75e5dd7070Spatrick '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) 76e5dd7070Spatrick 77e5dd7070SpatrickkReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), 78e5dd7070Spatrick '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) 79e5dd7070Spatrick 80e5dd7070Spatrick# Insert report crashes link. 81e5dd7070Spatrick 82e5dd7070Spatrick# Disabled for the time being until we decide exactly when this should 83e5dd7070Spatrick# be enabled. Also the radar reporter needs to be fixed to report 84e5dd7070Spatrick# multiple files. 85e5dd7070Spatrick 86e5dd7070Spatrick#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), 87e5dd7070Spatrick# '<br>These files will automatically be attached to ' + 88e5dd7070Spatrick# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) 89e5dd7070Spatrick 90e5dd7070Spatrick### 91e5dd7070Spatrick# Other simple parameters 92e5dd7070Spatrick 93e5dd7070SpatrickkShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view') 94e5dd7070SpatrickkConfigPath = os.path.expanduser('~/.scanview.cfg') 95e5dd7070Spatrick 96e5dd7070Spatrick### 97e5dd7070Spatrick 98e5dd7070Spatrick__version__ = "0.1" 99e5dd7070Spatrick 100e5dd7070Spatrick__all__ = ["create_server"] 101e5dd7070Spatrick 102e5dd7070Spatrickclass ReporterThread(threading.Thread): 103e5dd7070Spatrick def __init__(self, report, reporter, parameters, server): 104e5dd7070Spatrick threading.Thread.__init__(self) 105e5dd7070Spatrick self.report = report 106e5dd7070Spatrick self.server = server 107e5dd7070Spatrick self.reporter = reporter 108e5dd7070Spatrick self.parameters = parameters 109e5dd7070Spatrick self.success = False 110e5dd7070Spatrick self.status = None 111e5dd7070Spatrick 112e5dd7070Spatrick def run(self): 113e5dd7070Spatrick result = None 114e5dd7070Spatrick try: 115e5dd7070Spatrick if self.server.options.debug: 116e5dd7070Spatrick print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr) 117e5dd7070Spatrick self.status = self.reporter.fileReport(self.report, self.parameters) 118e5dd7070Spatrick self.success = True 119e5dd7070Spatrick time.sleep(3) 120e5dd7070Spatrick if self.server.options.debug: 121e5dd7070Spatrick print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr) 122e5dd7070Spatrick except Reporter.ReportFailure as e: 123e5dd7070Spatrick self.status = e.value 124e5dd7070Spatrick except Exception as e: 125e5dd7070Spatrick s = StringIO() 126e5dd7070Spatrick import traceback 127e5dd7070Spatrick print('<b>Unhandled Exception</b><br><pre>', file=s) 128e5dd7070Spatrick traceback.print_exc(file=s) 129e5dd7070Spatrick print('</pre>', file=s) 130e5dd7070Spatrick self.status = s.getvalue() 131e5dd7070Spatrick 132e5dd7070Spatrickclass ScanViewServer(HTTPServer): 133e5dd7070Spatrick def __init__(self, address, handler, root, reporters, options): 134e5dd7070Spatrick HTTPServer.__init__(self, address, handler) 135e5dd7070Spatrick self.root = root 136e5dd7070Spatrick self.reporters = reporters 137e5dd7070Spatrick self.options = options 138e5dd7070Spatrick self.halted = False 139e5dd7070Spatrick self.config = None 140e5dd7070Spatrick self.load_config() 141e5dd7070Spatrick 142e5dd7070Spatrick def load_config(self): 143e5dd7070Spatrick self.config = configparser.RawConfigParser() 144e5dd7070Spatrick 145e5dd7070Spatrick # Add defaults 146e5dd7070Spatrick self.config.add_section('ScanView') 147e5dd7070Spatrick for r in self.reporters: 148e5dd7070Spatrick self.config.add_section(r.getName()) 149e5dd7070Spatrick for p in r.getParameters(): 150e5dd7070Spatrick if p.saveConfigValue(): 151e5dd7070Spatrick self.config.set(r.getName(), p.getName(), '') 152e5dd7070Spatrick 153e5dd7070Spatrick # Ignore parse errors 154e5dd7070Spatrick try: 155e5dd7070Spatrick self.config.read([kConfigPath]) 156e5dd7070Spatrick except: 157e5dd7070Spatrick pass 158e5dd7070Spatrick 159e5dd7070Spatrick # Save on exit 160e5dd7070Spatrick import atexit 161e5dd7070Spatrick atexit.register(lambda: self.save_config()) 162e5dd7070Spatrick 163e5dd7070Spatrick def save_config(self): 164e5dd7070Spatrick # Ignore errors (only called on exit). 165e5dd7070Spatrick try: 166e5dd7070Spatrick f = open(kConfigPath,'w') 167e5dd7070Spatrick self.config.write(f) 168e5dd7070Spatrick f.close() 169e5dd7070Spatrick except: 170e5dd7070Spatrick pass 171e5dd7070Spatrick 172e5dd7070Spatrick def halt(self): 173e5dd7070Spatrick self.halted = True 174e5dd7070Spatrick if self.options.debug: 175e5dd7070Spatrick print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) 176e5dd7070Spatrick 177e5dd7070Spatrick def serve_forever(self): 178e5dd7070Spatrick while not self.halted: 179e5dd7070Spatrick if self.options.debug > 1: 180e5dd7070Spatrick print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) 181e5dd7070Spatrick try: 182e5dd7070Spatrick self.handle_request() 183e5dd7070Spatrick except OSError as e: 184e5dd7070Spatrick print('OSError',e.errno) 185e5dd7070Spatrick 186e5dd7070Spatrick def finish_request(self, request, client_address): 187e5dd7070Spatrick if self.options.autoReload: 188e5dd7070Spatrick import ScanView 189e5dd7070Spatrick self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler 190e5dd7070Spatrick HTTPServer.finish_request(self, request, client_address) 191e5dd7070Spatrick 192e5dd7070Spatrick def handle_error(self, request, client_address): 193e5dd7070Spatrick # Ignore socket errors 194e5dd7070Spatrick info = sys.exc_info() 195e5dd7070Spatrick if info and isinstance(info[1], socket.error): 196e5dd7070Spatrick if self.options.debug > 1: 197e5dd7070Spatrick print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr) 198e5dd7070Spatrick return 199e5dd7070Spatrick HTTPServer.handle_error(self, request, client_address) 200e5dd7070Spatrick 201e5dd7070Spatrick# Borrowed from Quixote, with simplifications. 202e5dd7070Spatrickdef parse_query(qs, fields=None): 203e5dd7070Spatrick if fields is None: 204e5dd7070Spatrick fields = {} 205e5dd7070Spatrick for chunk in (_f for _f in qs.split('&') if _f): 206e5dd7070Spatrick if '=' not in chunk: 207e5dd7070Spatrick name = chunk 208e5dd7070Spatrick value = '' 209e5dd7070Spatrick else: 210e5dd7070Spatrick name, value = chunk.split('=', 1) 211e5dd7070Spatrick name = unquote(name.replace('+', ' ')) 212e5dd7070Spatrick value = unquote(value.replace('+', ' ')) 213e5dd7070Spatrick item = fields.get(name) 214e5dd7070Spatrick if item is None: 215e5dd7070Spatrick fields[name] = [value] 216e5dd7070Spatrick else: 217e5dd7070Spatrick item.append(value) 218e5dd7070Spatrick return fields 219e5dd7070Spatrick 220e5dd7070Spatrickclass ScanViewRequestHandler(SimpleHTTPRequestHandler): 221e5dd7070Spatrick server_version = "ScanViewServer/" + __version__ 222e5dd7070Spatrick dynamic_mtime = time.time() 223e5dd7070Spatrick 224e5dd7070Spatrick def do_HEAD(self): 225e5dd7070Spatrick try: 226e5dd7070Spatrick SimpleHTTPRequestHandler.do_HEAD(self) 227e5dd7070Spatrick except Exception as e: 228e5dd7070Spatrick self.handle_exception(e) 229e5dd7070Spatrick 230e5dd7070Spatrick def do_GET(self): 231e5dd7070Spatrick try: 232e5dd7070Spatrick SimpleHTTPRequestHandler.do_GET(self) 233e5dd7070Spatrick except Exception as e: 234e5dd7070Spatrick self.handle_exception(e) 235e5dd7070Spatrick 236e5dd7070Spatrick def do_POST(self): 237e5dd7070Spatrick """Serve a POST request.""" 238e5dd7070Spatrick try: 239e5dd7070Spatrick length = self.headers.getheader('content-length') or "0" 240e5dd7070Spatrick try: 241e5dd7070Spatrick length = int(length) 242e5dd7070Spatrick except: 243e5dd7070Spatrick length = 0 244e5dd7070Spatrick content = self.rfile.read(length) 245e5dd7070Spatrick fields = parse_query(content) 246e5dd7070Spatrick f = self.send_head(fields) 247e5dd7070Spatrick if f: 248e5dd7070Spatrick self.copyfile(f, self.wfile) 249e5dd7070Spatrick f.close() 250e5dd7070Spatrick except Exception as e: 251e5dd7070Spatrick self.handle_exception(e) 252e5dd7070Spatrick 253e5dd7070Spatrick def log_message(self, format, *args): 254e5dd7070Spatrick if self.server.options.debug: 255e5dd7070Spatrick sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % 256e5dd7070Spatrick (sys.argv[0], 257e5dd7070Spatrick self.address_string(), 258e5dd7070Spatrick self.log_date_time_string(), 259e5dd7070Spatrick format%args)) 260e5dd7070Spatrick 261e5dd7070Spatrick def load_report(self, report): 262e5dd7070Spatrick path = os.path.join(self.server.root, 'report-%s.html'%report) 263e5dd7070Spatrick data = open(path).read() 264e5dd7070Spatrick keys = {} 265e5dd7070Spatrick for item in kBugKeyValueRE.finditer(data): 266e5dd7070Spatrick k,v = item.groups() 267e5dd7070Spatrick keys[k] = v 268e5dd7070Spatrick return keys 269e5dd7070Spatrick 270e5dd7070Spatrick def load_crashes(self): 271e5dd7070Spatrick path = posixpath.join(self.server.root, 'index.html') 272e5dd7070Spatrick data = open(path).read() 273e5dd7070Spatrick problems = [] 274e5dd7070Spatrick for item in kReportCrashEntryRE.finditer(data): 275e5dd7070Spatrick fieldData = item.group(1) 276e5dd7070Spatrick fields = dict([i.groups() for i in 277e5dd7070Spatrick kReportCrashEntryKeyValueRE.finditer(fieldData)]) 278e5dd7070Spatrick problems.append(fields) 279e5dd7070Spatrick return problems 280e5dd7070Spatrick 281e5dd7070Spatrick def handle_exception(self, exc): 282e5dd7070Spatrick import traceback 283e5dd7070Spatrick s = StringIO() 284e5dd7070Spatrick print("INTERNAL ERROR\n", file=s) 285e5dd7070Spatrick traceback.print_exc(file=s) 286e5dd7070Spatrick f = self.send_string(s.getvalue(), 'text/plain') 287e5dd7070Spatrick if f: 288e5dd7070Spatrick self.copyfile(f, self.wfile) 289e5dd7070Spatrick f.close() 290e5dd7070Spatrick 291e5dd7070Spatrick def get_scalar_field(self, name): 292e5dd7070Spatrick if name in self.fields: 293e5dd7070Spatrick return self.fields[name][0] 294e5dd7070Spatrick else: 295e5dd7070Spatrick return None 296e5dd7070Spatrick 297e5dd7070Spatrick def submit_bug(self, c): 298e5dd7070Spatrick title = self.get_scalar_field('title') 299e5dd7070Spatrick description = self.get_scalar_field('description') 300e5dd7070Spatrick report = self.get_scalar_field('report') 301e5dd7070Spatrick reporterIndex = self.get_scalar_field('reporter') 302e5dd7070Spatrick files = [] 303e5dd7070Spatrick for fileID in self.fields.get('files',[]): 304e5dd7070Spatrick try: 305e5dd7070Spatrick i = int(fileID) 306e5dd7070Spatrick except: 307e5dd7070Spatrick i = None 308e5dd7070Spatrick if i is None or i<0 or i>=len(c.files): 309e5dd7070Spatrick return (False, 'Invalid file ID') 310e5dd7070Spatrick files.append(c.files[i]) 311e5dd7070Spatrick 312e5dd7070Spatrick if not title: 313e5dd7070Spatrick return (False, "Missing title.") 314e5dd7070Spatrick if not description: 315e5dd7070Spatrick return (False, "Missing description.") 316e5dd7070Spatrick try: 317e5dd7070Spatrick reporterIndex = int(reporterIndex) 318e5dd7070Spatrick except: 319e5dd7070Spatrick return (False, "Invalid report method.") 320e5dd7070Spatrick 321e5dd7070Spatrick # Get the reporter and parameters. 322e5dd7070Spatrick reporter = self.server.reporters[reporterIndex] 323e5dd7070Spatrick parameters = {} 324e5dd7070Spatrick for o in reporter.getParameters(): 325e5dd7070Spatrick name = '%s_%s'%(reporter.getName(),o.getName()) 326e5dd7070Spatrick if name not in self.fields: 327e5dd7070Spatrick return (False, 328e5dd7070Spatrick 'Missing field "%s" for %s report method.'%(name, 329e5dd7070Spatrick reporter.getName())) 330e5dd7070Spatrick parameters[o.getName()] = self.get_scalar_field(name) 331e5dd7070Spatrick 332e5dd7070Spatrick # Update config defaults. 333e5dd7070Spatrick if report != 'None': 334e5dd7070Spatrick self.server.config.set('ScanView', 'reporter', reporterIndex) 335e5dd7070Spatrick for o in reporter.getParameters(): 336e5dd7070Spatrick if o.saveConfigValue(): 337e5dd7070Spatrick name = o.getName() 338e5dd7070Spatrick self.server.config.set(reporter.getName(), name, parameters[name]) 339e5dd7070Spatrick 340e5dd7070Spatrick # Create the report. 341e5dd7070Spatrick bug = Reporter.BugReport(title, description, files) 342e5dd7070Spatrick 343e5dd7070Spatrick # Kick off a reporting thread. 344e5dd7070Spatrick t = ReporterThread(bug, reporter, parameters, self.server) 345e5dd7070Spatrick t.start() 346e5dd7070Spatrick 347e5dd7070Spatrick # Wait for thread to die... 348e5dd7070Spatrick while t.isAlive(): 349e5dd7070Spatrick time.sleep(.25) 350e5dd7070Spatrick submitStatus = t.status 351e5dd7070Spatrick 352e5dd7070Spatrick return (t.success, t.status) 353e5dd7070Spatrick 354e5dd7070Spatrick def send_report_submit(self): 355e5dd7070Spatrick report = self.get_scalar_field('report') 356e5dd7070Spatrick c = self.get_report_context(report) 357e5dd7070Spatrick if c.reportSource is None: 358e5dd7070Spatrick reportingFor = "Report Crashes > " 359e5dd7070Spatrick fileBug = """\ 360e5dd7070Spatrick<a href="/report_crashes">File Bug</a> > """%locals() 361e5dd7070Spatrick else: 362e5dd7070Spatrick reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, 363e5dd7070Spatrick report) 364e5dd7070Spatrick fileBug = '<a href="/report/%s">File Bug</a> > ' % report 365e5dd7070Spatrick title = self.get_scalar_field('title') 366e5dd7070Spatrick description = self.get_scalar_field('description') 367e5dd7070Spatrick 368e5dd7070Spatrick res,message = self.submit_bug(c) 369e5dd7070Spatrick 370e5dd7070Spatrick if res: 371e5dd7070Spatrick statusClass = 'SubmitOk' 372e5dd7070Spatrick statusName = 'Succeeded' 373e5dd7070Spatrick else: 374e5dd7070Spatrick statusClass = 'SubmitFail' 375e5dd7070Spatrick statusName = 'Failed' 376e5dd7070Spatrick 377e5dd7070Spatrick result = """ 378e5dd7070Spatrick<head> 379e5dd7070Spatrick <title>Bug Submission</title> 380e5dd7070Spatrick <link rel="stylesheet" type="text/css" href="/scanview.css" /> 381e5dd7070Spatrick</head> 382e5dd7070Spatrick<body> 383e5dd7070Spatrick<h3> 384e5dd7070Spatrick<a href="/">Summary</a> > 385e5dd7070Spatrick%(reportingFor)s 386e5dd7070Spatrick%(fileBug)s 387e5dd7070SpatrickSubmit</h3> 388e5dd7070Spatrick<form name="form" action=""> 389e5dd7070Spatrick<table class="form"> 390e5dd7070Spatrick<tr><td> 391e5dd7070Spatrick<table class="form_group"> 392e5dd7070Spatrick<tr> 393e5dd7070Spatrick <td class="form_clabel">Title:</td> 394e5dd7070Spatrick <td class="form_value"> 395e5dd7070Spatrick <input type="text" name="title" size="50" value="%(title)s" disabled> 396e5dd7070Spatrick </td> 397e5dd7070Spatrick</tr> 398e5dd7070Spatrick<tr> 399e5dd7070Spatrick <td class="form_label">Description:</td> 400e5dd7070Spatrick <td class="form_value"> 401e5dd7070Spatrick<textarea rows="10" cols="80" name="description" disabled> 402e5dd7070Spatrick%(description)s 403e5dd7070Spatrick</textarea> 404e5dd7070Spatrick </td> 405e5dd7070Spatrick</table> 406e5dd7070Spatrick</td></tr> 407e5dd7070Spatrick</table> 408e5dd7070Spatrick</form> 409e5dd7070Spatrick<h1 class="%(statusClass)s">Submission %(statusName)s</h1> 410e5dd7070Spatrick%(message)s 411e5dd7070Spatrick<p> 412e5dd7070Spatrick<hr> 413e5dd7070Spatrick<a href="/">Return to Summary</a> 414e5dd7070Spatrick</body> 415e5dd7070Spatrick</html>"""%locals() 416e5dd7070Spatrick return self.send_string(result) 417e5dd7070Spatrick 418e5dd7070Spatrick def send_open_report(self, report): 419e5dd7070Spatrick try: 420e5dd7070Spatrick keys = self.load_report(report) 421e5dd7070Spatrick except IOError: 422e5dd7070Spatrick return self.send_error(400, 'Invalid report.') 423e5dd7070Spatrick 424e5dd7070Spatrick file = keys.get('FILE') 425e5dd7070Spatrick if not file or not posixpath.exists(file): 426e5dd7070Spatrick return self.send_error(400, 'File does not exist: "%s"' % file) 427e5dd7070Spatrick 428e5dd7070Spatrick import startfile 429e5dd7070Spatrick if self.server.options.debug: 430e5dd7070Spatrick print('%s: SERVER: opening "%s"'%(sys.argv[0], 431e5dd7070Spatrick file), file=sys.stderr) 432e5dd7070Spatrick 433e5dd7070Spatrick status = startfile.open(file) 434e5dd7070Spatrick if status: 435e5dd7070Spatrick res = 'Opened: "%s"' % file 436e5dd7070Spatrick else: 437e5dd7070Spatrick res = 'Open failed: "%s"' % file 438e5dd7070Spatrick 439e5dd7070Spatrick return self.send_string(res, 'text/plain') 440e5dd7070Spatrick 441e5dd7070Spatrick def get_report_context(self, report): 442e5dd7070Spatrick class Context(object): 443e5dd7070Spatrick pass 444e5dd7070Spatrick if report is None or report == 'None': 445e5dd7070Spatrick data = self.load_crashes() 446e5dd7070Spatrick # Don't allow empty reports. 447e5dd7070Spatrick if not data: 448e5dd7070Spatrick raise ValueError('No crashes detected!') 449e5dd7070Spatrick c = Context() 450e5dd7070Spatrick c.title = 'clang static analyzer failures' 451e5dd7070Spatrick 452e5dd7070Spatrick stderrSummary = "" 453e5dd7070Spatrick for item in data: 454e5dd7070Spatrick if 'stderr' in item: 455e5dd7070Spatrick path = posixpath.join(self.server.root, item['stderr']) 456e5dd7070Spatrick if os.path.exists(path): 457e5dd7070Spatrick lns = itertools.islice(open(path), 0, 10) 458e5dd7070Spatrick stderrSummary += '%s\n--\n%s' % (item.get('src', 459e5dd7070Spatrick '<unknown>'), 460e5dd7070Spatrick ''.join(lns)) 461e5dd7070Spatrick 462e5dd7070Spatrick c.description = """\ 463e5dd7070SpatrickThe clang static analyzer failed on these inputs: 464e5dd7070Spatrick%s 465e5dd7070Spatrick 466e5dd7070SpatrickSTDERR Summary 467e5dd7070Spatrick-------------- 468e5dd7070Spatrick%s 469e5dd7070Spatrick""" % ('\n'.join([item.get('src','<unknown>') for item in data]), 470e5dd7070Spatrick stderrSummary) 471e5dd7070Spatrick c.reportSource = None 472e5dd7070Spatrick c.navMarkup = "Report Crashes > " 473e5dd7070Spatrick c.files = [] 474e5dd7070Spatrick for item in data: 475e5dd7070Spatrick c.files.append(item.get('src','')) 476e5dd7070Spatrick c.files.append(posixpath.join(self.server.root, 477e5dd7070Spatrick item.get('file',''))) 478e5dd7070Spatrick c.files.append(posixpath.join(self.server.root, 479e5dd7070Spatrick item.get('clangfile',''))) 480e5dd7070Spatrick c.files.append(posixpath.join(self.server.root, 481e5dd7070Spatrick item.get('stderr',''))) 482e5dd7070Spatrick c.files.append(posixpath.join(self.server.root, 483e5dd7070Spatrick item.get('info',''))) 484e5dd7070Spatrick # Just in case something failed, ignore files which don't 485e5dd7070Spatrick # exist. 486e5dd7070Spatrick c.files = [f for f in c.files 487e5dd7070Spatrick if os.path.exists(f) and os.path.isfile(f)] 488e5dd7070Spatrick else: 489e5dd7070Spatrick # Check that this is a valid report. 490e5dd7070Spatrick path = posixpath.join(self.server.root, 'report-%s.html' % report) 491e5dd7070Spatrick if not posixpath.exists(path): 492e5dd7070Spatrick raise ValueError('Invalid report ID') 493e5dd7070Spatrick keys = self.load_report(report) 494e5dd7070Spatrick c = Context() 495e5dd7070Spatrick c.title = keys.get('DESC','clang error (unrecognized') 496e5dd7070Spatrick c.description = """\ 497e5dd7070SpatrickBug reported by the clang static analyzer. 498e5dd7070Spatrick 499e5dd7070SpatrickDescription: %s 500e5dd7070SpatrickFile: %s 501e5dd7070SpatrickLine: %s 502e5dd7070Spatrick"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) 503e5dd7070Spatrick c.reportSource = 'report-%s.html' % report 504e5dd7070Spatrick c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, 505e5dd7070Spatrick report) 506e5dd7070Spatrick 507e5dd7070Spatrick c.files = [path] 508e5dd7070Spatrick return c 509e5dd7070Spatrick 510e5dd7070Spatrick def send_report(self, report, configOverrides=None): 511e5dd7070Spatrick def getConfigOption(section, field): 512e5dd7070Spatrick if (configOverrides is not None and 513e5dd7070Spatrick section in configOverrides and 514e5dd7070Spatrick field in configOverrides[section]): 515e5dd7070Spatrick return configOverrides[section][field] 516e5dd7070Spatrick return self.server.config.get(section, field) 517e5dd7070Spatrick 518e5dd7070Spatrick # report is None is used for crashes 519e5dd7070Spatrick try: 520e5dd7070Spatrick c = self.get_report_context(report) 521e5dd7070Spatrick except ValueError as e: 522e5dd7070Spatrick return self.send_error(400, e.message) 523e5dd7070Spatrick 524e5dd7070Spatrick title = c.title 525e5dd7070Spatrick description= c.description 526e5dd7070Spatrick reportingFor = c.navMarkup 527e5dd7070Spatrick if c.reportSource is None: 528e5dd7070Spatrick extraIFrame = "" 529e5dd7070Spatrick else: 530e5dd7070Spatrick extraIFrame = """\ 531e5dd7070Spatrick<iframe src="/%s" width="100%%" height="40%%" 532e5dd7070Spatrick scrolling="auto" frameborder="1"> 533e5dd7070Spatrick <a href="/%s">View Bug Report</a> 534e5dd7070Spatrick</iframe>""" % (c.reportSource, c.reportSource) 535e5dd7070Spatrick 536e5dd7070Spatrick reporterSelections = [] 537e5dd7070Spatrick reporterOptions = [] 538e5dd7070Spatrick 539e5dd7070Spatrick try: 540e5dd7070Spatrick active = int(getConfigOption('ScanView','reporter')) 541e5dd7070Spatrick except: 542e5dd7070Spatrick active = 0 543e5dd7070Spatrick for i,r in enumerate(self.server.reporters): 544e5dd7070Spatrick selected = (i == active) 545e5dd7070Spatrick if selected: 546e5dd7070Spatrick selectedStr = ' selected' 547e5dd7070Spatrick else: 548e5dd7070Spatrick selectedStr = '' 549e5dd7070Spatrick reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) 550e5dd7070Spatrick options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) 551e5dd7070Spatrick display = ('none','')[selected] 552e5dd7070Spatrick reporterOptions.append("""\ 553e5dd7070Spatrick<tr id="%sReporterOptions" style="display:%s"> 554e5dd7070Spatrick <td class="form_label">%s Options</td> 555e5dd7070Spatrick <td class="form_value"> 556e5dd7070Spatrick <table class="form_inner_group"> 557e5dd7070Spatrick%s 558e5dd7070Spatrick </table> 559e5dd7070Spatrick </td> 560e5dd7070Spatrick</tr> 561e5dd7070Spatrick"""%(r.getName(),display,r.getName(),options)) 562e5dd7070Spatrick reporterSelections = '\n'.join(reporterSelections) 563e5dd7070Spatrick reporterOptionsDivs = '\n'.join(reporterOptions) 564e5dd7070Spatrick reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters])) 565e5dd7070Spatrick 566e5dd7070Spatrick if c.files: 567e5dd7070Spatrick fieldSize = min(5, len(c.files)) 568e5dd7070Spatrick attachFileOptions = '\n'.join(["""\ 569e5dd7070Spatrick<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) 570e5dd7070Spatrick attachFileRow = """\ 571e5dd7070Spatrick<tr> 572e5dd7070Spatrick <td class="form_label">Attach:</td> 573e5dd7070Spatrick <td class="form_value"> 574e5dd7070Spatrick<select style="width:100%%" name="files" multiple size=%d> 575e5dd7070Spatrick%s 576e5dd7070Spatrick</select> 577e5dd7070Spatrick </td> 578e5dd7070Spatrick</tr> 579e5dd7070Spatrick""" % (min(5, len(c.files)), attachFileOptions) 580e5dd7070Spatrick else: 581e5dd7070Spatrick attachFileRow = "" 582e5dd7070Spatrick 583e5dd7070Spatrick result = """<html> 584e5dd7070Spatrick<head> 585e5dd7070Spatrick <title>File Bug</title> 586e5dd7070Spatrick <link rel="stylesheet" type="text/css" href="/scanview.css" /> 587e5dd7070Spatrick</head> 588e5dd7070Spatrick<script language="javascript" type="text/javascript"> 589e5dd7070Spatrickvar reporters = %(reportersArray)s; 590e5dd7070Spatrickfunction updateReporterOptions() { 591e5dd7070Spatrick index = document.getElementById('reporter').selectedIndex; 592e5dd7070Spatrick for (var i=0; i < reporters.length; ++i) { 593e5dd7070Spatrick o = document.getElementById(reporters[i] + "ReporterOptions"); 594e5dd7070Spatrick if (i == index) { 595e5dd7070Spatrick o.style.display = ""; 596e5dd7070Spatrick } else { 597e5dd7070Spatrick o.style.display = "none"; 598e5dd7070Spatrick } 599e5dd7070Spatrick } 600e5dd7070Spatrick} 601e5dd7070Spatrick</script> 602e5dd7070Spatrick<body onLoad="updateReporterOptions()"> 603e5dd7070Spatrick<h3> 604e5dd7070Spatrick<a href="/">Summary</a> > 605e5dd7070Spatrick%(reportingFor)s 606e5dd7070SpatrickFile Bug</h3> 607e5dd7070Spatrick<form name="form" action="/report_submit" method="post"> 608e5dd7070Spatrick<input type="hidden" name="report" value="%(report)s"> 609e5dd7070Spatrick 610e5dd7070Spatrick<table class="form"> 611e5dd7070Spatrick<tr><td> 612e5dd7070Spatrick<table class="form_group"> 613e5dd7070Spatrick<tr> 614e5dd7070Spatrick <td class="form_clabel">Title:</td> 615e5dd7070Spatrick <td class="form_value"> 616e5dd7070Spatrick <input type="text" name="title" size="50" value="%(title)s"> 617e5dd7070Spatrick </td> 618e5dd7070Spatrick</tr> 619e5dd7070Spatrick<tr> 620e5dd7070Spatrick <td class="form_label">Description:</td> 621e5dd7070Spatrick <td class="form_value"> 622e5dd7070Spatrick<textarea rows="10" cols="80" name="description"> 623e5dd7070Spatrick%(description)s 624e5dd7070Spatrick</textarea> 625e5dd7070Spatrick </td> 626e5dd7070Spatrick</tr> 627e5dd7070Spatrick 628e5dd7070Spatrick%(attachFileRow)s 629e5dd7070Spatrick 630e5dd7070Spatrick</table> 631e5dd7070Spatrick<br> 632e5dd7070Spatrick<table class="form_group"> 633e5dd7070Spatrick<tr> 634e5dd7070Spatrick <td class="form_clabel">Method:</td> 635e5dd7070Spatrick <td class="form_value"> 636e5dd7070Spatrick <select id="reporter" name="reporter" onChange="updateReporterOptions()"> 637e5dd7070Spatrick %(reporterSelections)s 638e5dd7070Spatrick </select> 639e5dd7070Spatrick </td> 640e5dd7070Spatrick</tr> 641e5dd7070Spatrick%(reporterOptionsDivs)s 642e5dd7070Spatrick</table> 643e5dd7070Spatrick<br> 644e5dd7070Spatrick</td></tr> 645e5dd7070Spatrick<tr><td class="form_submit"> 646e5dd7070Spatrick <input align="right" type="submit" name="Submit" value="Submit"> 647e5dd7070Spatrick</td></tr> 648e5dd7070Spatrick</table> 649e5dd7070Spatrick</form> 650e5dd7070Spatrick 651e5dd7070Spatrick%(extraIFrame)s 652e5dd7070Spatrick 653e5dd7070Spatrick</body> 654e5dd7070Spatrick</html>"""%locals() 655e5dd7070Spatrick 656e5dd7070Spatrick return self.send_string(result) 657e5dd7070Spatrick 658e5dd7070Spatrick def send_head(self, fields=None): 659e5dd7070Spatrick if (self.server.options.onlyServeLocal and 660e5dd7070Spatrick self.client_address[0] != '127.0.0.1'): 661e5dd7070Spatrick return self.send_error(401, 'Unauthorized host.') 662e5dd7070Spatrick 663e5dd7070Spatrick if fields is None: 664e5dd7070Spatrick fields = {} 665e5dd7070Spatrick self.fields = fields 666e5dd7070Spatrick 667e5dd7070Spatrick o = urlparse(self.path) 668e5dd7070Spatrick self.fields = parse_query(o.query, fields) 669e5dd7070Spatrick path = posixpath.normpath(unquote(o.path)) 670e5dd7070Spatrick 671e5dd7070Spatrick # Split the components and strip the root prefix. 672e5dd7070Spatrick components = path.split('/')[1:] 673e5dd7070Spatrick 674e5dd7070Spatrick # Special case some top-level entries. 675e5dd7070Spatrick if components: 676e5dd7070Spatrick name = components[0] 677e5dd7070Spatrick if len(components)==2: 678e5dd7070Spatrick if name=='report': 679e5dd7070Spatrick return self.send_report(components[1]) 680e5dd7070Spatrick elif name=='open': 681e5dd7070Spatrick return self.send_open_report(components[1]) 682e5dd7070Spatrick elif len(components)==1: 683e5dd7070Spatrick if name=='quit': 684e5dd7070Spatrick self.server.halt() 685e5dd7070Spatrick return self.send_string('Goodbye.', 'text/plain') 686e5dd7070Spatrick elif name=='report_submit': 687e5dd7070Spatrick return self.send_report_submit() 688e5dd7070Spatrick elif name=='report_crashes': 689e5dd7070Spatrick overrides = { 'ScanView' : {}, 690e5dd7070Spatrick 'Radar' : {}, 691e5dd7070Spatrick 'Email' : {} } 692e5dd7070Spatrick for i,r in enumerate(self.server.reporters): 693e5dd7070Spatrick if r.getName() == 'Radar': 694e5dd7070Spatrick overrides['ScanView']['reporter'] = i 695e5dd7070Spatrick break 696e5dd7070Spatrick overrides['Radar']['Component'] = 'llvm - checker' 697e5dd7070Spatrick overrides['Radar']['Component Version'] = 'X' 698e5dd7070Spatrick return self.send_report(None, overrides) 699e5dd7070Spatrick elif name=='favicon.ico': 700e5dd7070Spatrick return self.send_path(posixpath.join(kShare,'bugcatcher.ico')) 701e5dd7070Spatrick 702e5dd7070Spatrick # Match directory entries. 703e5dd7070Spatrick if components[-1] == '': 704e5dd7070Spatrick components[-1] = 'index.html' 705e5dd7070Spatrick 706e5dd7070Spatrick relpath = '/'.join(components) 707e5dd7070Spatrick path = posixpath.join(self.server.root, relpath) 708e5dd7070Spatrick 709e5dd7070Spatrick if self.server.options.debug > 1: 710e5dd7070Spatrick print('%s: SERVER: sending path "%s"'%(sys.argv[0], 711e5dd7070Spatrick path), file=sys.stderr) 712e5dd7070Spatrick return self.send_path(path) 713e5dd7070Spatrick 714e5dd7070Spatrick def send_404(self): 715e5dd7070Spatrick self.send_error(404, "File not found") 716e5dd7070Spatrick return None 717e5dd7070Spatrick 718e5dd7070Spatrick def send_path(self, path): 719e5dd7070Spatrick # If the requested path is outside the root directory, do not open it 720e5dd7070Spatrick rel = os.path.abspath(path) 721e5dd7070Spatrick if not rel.startswith(os.path.abspath(self.server.root)): 722e5dd7070Spatrick return self.send_404() 723e5dd7070Spatrick 724e5dd7070Spatrick ctype = self.guess_type(path) 725e5dd7070Spatrick if ctype.startswith('text/'): 726e5dd7070Spatrick # Patch file instead 727e5dd7070Spatrick return self.send_patched_file(path, ctype) 728e5dd7070Spatrick else: 729e5dd7070Spatrick mode = 'rb' 730e5dd7070Spatrick try: 731e5dd7070Spatrick f = open(path, mode) 732e5dd7070Spatrick except IOError: 733e5dd7070Spatrick return self.send_404() 734e5dd7070Spatrick return self.send_file(f, ctype) 735e5dd7070Spatrick 736e5dd7070Spatrick def send_file(self, f, ctype): 737e5dd7070Spatrick # Patch files to add links, but skip binary files. 738e5dd7070Spatrick self.send_response(200) 739e5dd7070Spatrick self.send_header("Content-type", ctype) 740e5dd7070Spatrick fs = os.fstat(f.fileno()) 741e5dd7070Spatrick self.send_header("Content-Length", str(fs[6])) 742e5dd7070Spatrick self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 743e5dd7070Spatrick self.end_headers() 744e5dd7070Spatrick return f 745e5dd7070Spatrick 746e5dd7070Spatrick def send_string(self, s, ctype='text/html', headers=True, mtime=None): 747*a9ac8606Spatrick encoded_s = s.encode('utf-8') 748e5dd7070Spatrick if headers: 749e5dd7070Spatrick self.send_response(200) 750e5dd7070Spatrick self.send_header("Content-type", ctype) 751e5dd7070Spatrick self.send_header("Content-Length", str(len(encoded_s))) 752e5dd7070Spatrick if mtime is None: 753e5dd7070Spatrick mtime = self.dynamic_mtime 754e5dd7070Spatrick self.send_header("Last-Modified", self.date_time_string(mtime)) 755e5dd7070Spatrick self.end_headers() 756e5dd7070Spatrick return BytesIO(encoded_s) 757e5dd7070Spatrick 758e5dd7070Spatrick def send_patched_file(self, path, ctype): 759e5dd7070Spatrick # Allow a very limited set of variables. This is pretty gross. 760e5dd7070Spatrick variables = {} 761e5dd7070Spatrick variables['report'] = '' 762e5dd7070Spatrick m = kReportFileRE.match(path) 763e5dd7070Spatrick if m: 764e5dd7070Spatrick variables['report'] = m.group(2) 765e5dd7070Spatrick 766e5dd7070Spatrick try: 767e5dd7070Spatrick f = open(path,'rb') 768e5dd7070Spatrick except IOError: 769e5dd7070Spatrick return self.send_404() 770e5dd7070Spatrick fs = os.fstat(f.fileno()) 771e5dd7070Spatrick data = f.read().decode('utf-8') 772e5dd7070Spatrick for a,b in kReportReplacements: 773e5dd7070Spatrick data = a.sub(b % variables, data) 774e5dd7070Spatrick return self.send_string(data, ctype, mtime=fs.st_mtime) 775e5dd7070Spatrick 776e5dd7070Spatrick 777e5dd7070Spatrickdef create_server(address, options, root): 778e5dd7070Spatrick import Reporter 779e5dd7070Spatrick 780e5dd7070Spatrick reporters = Reporter.getReporters() 781e5dd7070Spatrick 782e5dd7070Spatrick return ScanViewServer(address, ScanViewRequestHandler, 783e5dd7070Spatrick root, 784e5dd7070Spatrick reporters, 785e5dd7070Spatrick options) 786