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