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