1c0ebe773SSerge Gueltonfrom __future__ import print_function 2*dd3c26a0STobias Hieta 3dc7d7e3fSSerge Gueltontry: 4dc7d7e3fSSerge Guelton from http.server import HTTPServer, SimpleHTTPRequestHandler 5dc7d7e3fSSerge Gueltonexcept ImportError: 6dc7d7e3fSSerge Guelton from BaseHTTPServer import HTTPServer 7dc7d7e3fSSerge Guelton from SimpleHTTPServer import SimpleHTTPRequestHandler 880df3d26SJonathan Roelofsimport os 980df3d26SJonathan Roelofsimport sys 10*dd3c26a0STobias Hieta 11c177c3a5SSerge Gueltontry: 12c177c3a5SSerge Guelton from urlparse import urlparse 13c177c3a5SSerge Guelton from urllib import unquote 14c177c3a5SSerge Gueltonexcept ImportError: 15c177c3a5SSerge Guelton from urllib.parse import urlparse, unquote 16c177c3a5SSerge Guelton 1780df3d26SJonathan Roelofsimport posixpath 18f886c03eSSerge Guelton 19f886c03eSSerge Gueltonif sys.version_info.major >= 3: 20f886c03eSSerge Guelton from io import StringIO, BytesIO 21f886c03eSSerge Gueltonelse: 22f886c03eSSerge Guelton from io import BytesIO, BytesIO as StringIO 23f886c03eSSerge Guelton 2480df3d26SJonathan Roelofsimport re 2580df3d26SJonathan Roelofsimport shutil 2680df3d26SJonathan Roelofsimport threading 2780df3d26SJonathan Roelofsimport time 2880df3d26SJonathan Roelofsimport socket 2980df3d26SJonathan Roelofsimport itertools 3080df3d26SJonathan Roelofs 3180df3d26SJonathan Roelofsimport Reporter 32*dd3c26a0STobias Hieta 3373cf752fSSerge Gueltontry: 3473cf752fSSerge Guelton import configparser 3573cf752fSSerge Gueltonexcept ImportError: 3673cf752fSSerge Guelton import ConfigParser as configparser 3780df3d26SJonathan Roelofs 3880df3d26SJonathan Roelofs### 3980df3d26SJonathan Roelofs# Various patterns matched or replaced by server. 4080df3d26SJonathan Roelofs 41*dd3c26a0STobias HietakReportFileRE = re.compile("(.*/)?report-(.*)\\.html") 4280df3d26SJonathan Roelofs 43*dd3c26a0STobias HietakBugKeyValueRE = re.compile("<!-- BUG([^ ]*) (.*) -->") 4480df3d26SJonathan Roelofs 4580df3d26SJonathan Roelofs# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> 4680df3d26SJonathan Roelofs 47*dd3c26a0STobias HietakReportCrashEntryRE = re.compile("<!-- REPORTPROBLEM (.*?)-->") 4880df3d26SJonathan RoelofskReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') 4980df3d26SJonathan Roelofs 5080df3d26SJonathan RoelofskReportReplacements = [] 5180df3d26SJonathan Roelofs 5280df3d26SJonathan Roelofs# Add custom javascript. 53*dd3c26a0STobias HietakReportReplacements.append( 54*dd3c26a0STobias Hieta ( 55*dd3c26a0STobias Hieta re.compile("<!-- SUMMARYENDHEAD -->"), 56*dd3c26a0STobias Hieta """\ 5780df3d26SJonathan Roelofs<script language="javascript" type="text/javascript"> 5880df3d26SJonathan Roelofsfunction load(url) { 5980df3d26SJonathan Roelofs if (window.XMLHttpRequest) { 6080df3d26SJonathan Roelofs req = new XMLHttpRequest(); 6180df3d26SJonathan Roelofs } else if (window.ActiveXObject) { 6280df3d26SJonathan Roelofs req = new ActiveXObject("Microsoft.XMLHTTP"); 6380df3d26SJonathan Roelofs } 6480df3d26SJonathan Roelofs if (req != undefined) { 6580df3d26SJonathan Roelofs req.open("GET", url, true); 6680df3d26SJonathan Roelofs req.send(""); 6780df3d26SJonathan Roelofs } 6880df3d26SJonathan Roelofs} 69*dd3c26a0STobias Hieta</script>""", 70*dd3c26a0STobias Hieta ) 71*dd3c26a0STobias Hieta) 7280df3d26SJonathan Roelofs 7380df3d26SJonathan Roelofs# Insert additional columns. 74*dd3c26a0STobias HietakReportReplacements.append((re.compile("<!-- REPORTBUGCOL -->"), "<td></td><td></td>")) 7580df3d26SJonathan Roelofs 7680df3d26SJonathan Roelofs# Insert report bug and open file links. 77*dd3c26a0STobias HietakReportReplacements.append( 78*dd3c26a0STobias Hieta ( 79*dd3c26a0STobias Hieta re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), 80*dd3c26a0STobias Hieta ( 81*dd3c26a0STobias Hieta '<td class="Button"><a href="report/\\1">Report Bug</a></td>' 82*dd3c26a0STobias Hieta + '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>' 83*dd3c26a0STobias Hieta ), 84*dd3c26a0STobias Hieta ) 85*dd3c26a0STobias Hieta) 8680df3d26SJonathan Roelofs 87*dd3c26a0STobias HietakReportReplacements.append( 88*dd3c26a0STobias Hieta ( 89*dd3c26a0STobias Hieta re.compile("<!-- REPORTHEADER -->"), 90*dd3c26a0STobias Hieta '<h3><a href="/">Summary</a> > Report %(report)s</h3>', 91*dd3c26a0STobias Hieta ) 92*dd3c26a0STobias Hieta) 9380df3d26SJonathan Roelofs 94*dd3c26a0STobias HietakReportReplacements.append( 95*dd3c26a0STobias Hieta ( 96*dd3c26a0STobias Hieta re.compile("<!-- REPORTSUMMARYEXTRA -->"), 97*dd3c26a0STobias Hieta '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>', 98*dd3c26a0STobias Hieta ) 99*dd3c26a0STobias Hieta) 10080df3d26SJonathan Roelofs 10180df3d26SJonathan Roelofs# Insert report crashes link. 10280df3d26SJonathan Roelofs 10380df3d26SJonathan Roelofs# Disabled for the time being until we decide exactly when this should 10480df3d26SJonathan Roelofs# be enabled. Also the radar reporter needs to be fixed to report 10580df3d26SJonathan Roelofs# multiple files. 10680df3d26SJonathan Roelofs 10780df3d26SJonathan Roelofs# kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), 10880df3d26SJonathan Roelofs# '<br>These files will automatically be attached to ' + 10980df3d26SJonathan Roelofs# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) 11080df3d26SJonathan Roelofs 11180df3d26SJonathan Roelofs### 11280df3d26SJonathan Roelofs# Other simple parameters 11380df3d26SJonathan Roelofs 114*dd3c26a0STobias HietakShare = posixpath.join(posixpath.dirname(__file__), "../share/scan-view") 115*dd3c26a0STobias HietakConfigPath = os.path.expanduser("~/.scanview.cfg") 11680df3d26SJonathan Roelofs 11780df3d26SJonathan Roelofs### 11880df3d26SJonathan Roelofs 11980df3d26SJonathan Roelofs__version__ = "0.1" 12080df3d26SJonathan Roelofs 12180df3d26SJonathan Roelofs__all__ = ["create_server"] 12280df3d26SJonathan Roelofs 123*dd3c26a0STobias Hieta 12480df3d26SJonathan Roelofsclass ReporterThread(threading.Thread): 12580df3d26SJonathan Roelofs def __init__(self, report, reporter, parameters, server): 12680df3d26SJonathan Roelofs threading.Thread.__init__(self) 12780df3d26SJonathan Roelofs self.report = report 12880df3d26SJonathan Roelofs self.server = server 12980df3d26SJonathan Roelofs self.reporter = reporter 13080df3d26SJonathan Roelofs self.parameters = parameters 13180df3d26SJonathan Roelofs self.success = False 13280df3d26SJonathan Roelofs self.status = None 13380df3d26SJonathan Roelofs 13480df3d26SJonathan Roelofs def run(self): 13580df3d26SJonathan Roelofs result = None 13680df3d26SJonathan Roelofs try: 13780df3d26SJonathan Roelofs if self.server.options.debug: 138c0ebe773SSerge Guelton print("%s: SERVER: submitting bug." % (sys.argv[0],), file=sys.stderr) 13980df3d26SJonathan Roelofs self.status = self.reporter.fileReport(self.report, self.parameters) 14080df3d26SJonathan Roelofs self.success = True 14180df3d26SJonathan Roelofs time.sleep(3) 14280df3d26SJonathan Roelofs if self.server.options.debug: 143*dd3c26a0STobias Hieta print( 144*dd3c26a0STobias Hieta "%s: SERVER: submission complete." % (sys.argv[0],), file=sys.stderr 145*dd3c26a0STobias Hieta ) 1463de41084SSerge Guelton except Reporter.ReportFailure as e: 14780df3d26SJonathan Roelofs self.status = e.value 1483de41084SSerge Guelton except Exception as e: 149f886c03eSSerge Guelton s = StringIO() 15080df3d26SJonathan Roelofs import traceback 151*dd3c26a0STobias Hieta 152*dd3c26a0STobias Hieta print("<b>Unhandled Exception</b><br><pre>", file=s) 153c0ebe773SSerge Guelton traceback.print_exc(file=s) 154*dd3c26a0STobias Hieta print("</pre>", file=s) 15580df3d26SJonathan Roelofs self.status = s.getvalue() 15680df3d26SJonathan Roelofs 157*dd3c26a0STobias Hieta 158dc7d7e3fSSerge Gueltonclass ScanViewServer(HTTPServer): 15980df3d26SJonathan Roelofs def __init__(self, address, handler, root, reporters, options): 160dc7d7e3fSSerge Guelton HTTPServer.__init__(self, address, handler) 16180df3d26SJonathan Roelofs self.root = root 16280df3d26SJonathan Roelofs self.reporters = reporters 16380df3d26SJonathan Roelofs self.options = options 16480df3d26SJonathan Roelofs self.halted = False 16580df3d26SJonathan Roelofs self.config = None 16680df3d26SJonathan Roelofs self.load_config() 16780df3d26SJonathan Roelofs 16880df3d26SJonathan Roelofs def load_config(self): 16973cf752fSSerge Guelton self.config = configparser.RawConfigParser() 17080df3d26SJonathan Roelofs 17180df3d26SJonathan Roelofs # Add defaults 172*dd3c26a0STobias Hieta self.config.add_section("ScanView") 17380df3d26SJonathan Roelofs for r in self.reporters: 17480df3d26SJonathan Roelofs self.config.add_section(r.getName()) 17580df3d26SJonathan Roelofs for p in r.getParameters(): 17680df3d26SJonathan Roelofs if p.saveConfigValue(): 177*dd3c26a0STobias Hieta self.config.set(r.getName(), p.getName(), "") 17880df3d26SJonathan Roelofs 17980df3d26SJonathan Roelofs # Ignore parse errors 18080df3d26SJonathan Roelofs try: 18180df3d26SJonathan Roelofs self.config.read([kConfigPath]) 18280df3d26SJonathan Roelofs except: 18380df3d26SJonathan Roelofs pass 18480df3d26SJonathan Roelofs 18580df3d26SJonathan Roelofs # Save on exit 18680df3d26SJonathan Roelofs import atexit 187*dd3c26a0STobias Hieta 18880df3d26SJonathan Roelofs atexit.register(lambda: self.save_config()) 18980df3d26SJonathan Roelofs 19080df3d26SJonathan Roelofs def save_config(self): 19180df3d26SJonathan Roelofs # Ignore errors (only called on exit). 19280df3d26SJonathan Roelofs try: 193*dd3c26a0STobias Hieta f = open(kConfigPath, "w") 19480df3d26SJonathan Roelofs self.config.write(f) 19580df3d26SJonathan Roelofs f.close() 19680df3d26SJonathan Roelofs except: 19780df3d26SJonathan Roelofs pass 19880df3d26SJonathan Roelofs 19980df3d26SJonathan Roelofs def halt(self): 20080df3d26SJonathan Roelofs self.halted = True 20180df3d26SJonathan Roelofs if self.options.debug: 202c0ebe773SSerge Guelton print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) 20380df3d26SJonathan Roelofs 20480df3d26SJonathan Roelofs def serve_forever(self): 20580df3d26SJonathan Roelofs while not self.halted: 20680df3d26SJonathan Roelofs if self.options.debug > 1: 207c0ebe773SSerge Guelton print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) 20880df3d26SJonathan Roelofs try: 20980df3d26SJonathan Roelofs self.handle_request() 2103de41084SSerge Guelton except OSError as e: 211*dd3c26a0STobias Hieta print("OSError", e.errno) 21280df3d26SJonathan Roelofs 21380df3d26SJonathan Roelofs def finish_request(self, request, client_address): 21480df3d26SJonathan Roelofs if self.options.autoReload: 21580df3d26SJonathan Roelofs import ScanView 216*dd3c26a0STobias Hieta 21780df3d26SJonathan Roelofs self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler 218dc7d7e3fSSerge Guelton HTTPServer.finish_request(self, request, client_address) 21980df3d26SJonathan Roelofs 22080df3d26SJonathan Roelofs def handle_error(self, request, client_address): 22180df3d26SJonathan Roelofs # Ignore socket errors 22280df3d26SJonathan Roelofs info = sys.exc_info() 22380df3d26SJonathan Roelofs if info and isinstance(info[1], socket.error): 22480df3d26SJonathan Roelofs if self.options.debug > 1: 225*dd3c26a0STobias Hieta print( 226*dd3c26a0STobias Hieta "%s: SERVER: ignored socket error." % (sys.argv[0],), 227*dd3c26a0STobias Hieta file=sys.stderr, 228*dd3c26a0STobias Hieta ) 22980df3d26SJonathan Roelofs return 230dc7d7e3fSSerge Guelton HTTPServer.handle_error(self, request, client_address) 23180df3d26SJonathan Roelofs 232*dd3c26a0STobias Hieta 23380df3d26SJonathan Roelofs# Borrowed from Quixote, with simplifications. 23480df3d26SJonathan Roelofsdef parse_query(qs, fields=None): 23580df3d26SJonathan Roelofs if fields is None: 23680df3d26SJonathan Roelofs fields = {} 237*dd3c26a0STobias Hieta for chunk in (_f for _f in qs.split("&") if _f): 238*dd3c26a0STobias Hieta if "=" not in chunk: 23980df3d26SJonathan Roelofs name = chunk 240*dd3c26a0STobias Hieta value = "" 24180df3d26SJonathan Roelofs else: 242*dd3c26a0STobias Hieta name, value = chunk.split("=", 1) 243*dd3c26a0STobias Hieta name = unquote(name.replace("+", " ")) 244*dd3c26a0STobias Hieta value = unquote(value.replace("+", " ")) 24580df3d26SJonathan Roelofs item = fields.get(name) 24680df3d26SJonathan Roelofs if item is None: 24780df3d26SJonathan Roelofs fields[name] = [value] 24880df3d26SJonathan Roelofs else: 24980df3d26SJonathan Roelofs item.append(value) 25080df3d26SJonathan Roelofs return fields 25180df3d26SJonathan Roelofs 252*dd3c26a0STobias Hieta 253dc7d7e3fSSerge Gueltonclass ScanViewRequestHandler(SimpleHTTPRequestHandler): 25480df3d26SJonathan Roelofs server_version = "ScanViewServer/" + __version__ 25580df3d26SJonathan Roelofs dynamic_mtime = time.time() 25680df3d26SJonathan Roelofs 25780df3d26SJonathan Roelofs def do_HEAD(self): 25880df3d26SJonathan Roelofs try: 259dc7d7e3fSSerge Guelton SimpleHTTPRequestHandler.do_HEAD(self) 2603de41084SSerge Guelton except Exception as e: 26180df3d26SJonathan Roelofs self.handle_exception(e) 26280df3d26SJonathan Roelofs 26380df3d26SJonathan Roelofs def do_GET(self): 26480df3d26SJonathan Roelofs try: 265dc7d7e3fSSerge Guelton SimpleHTTPRequestHandler.do_GET(self) 2663de41084SSerge Guelton except Exception as e: 26780df3d26SJonathan Roelofs self.handle_exception(e) 26880df3d26SJonathan Roelofs 26980df3d26SJonathan Roelofs def do_POST(self): 27080df3d26SJonathan Roelofs """Serve a POST request.""" 27180df3d26SJonathan Roelofs try: 272*dd3c26a0STobias Hieta length = self.headers.getheader("content-length") or "0" 27380df3d26SJonathan Roelofs try: 27480df3d26SJonathan Roelofs length = int(length) 27580df3d26SJonathan Roelofs except: 27680df3d26SJonathan Roelofs length = 0 27780df3d26SJonathan Roelofs content = self.rfile.read(length) 27880df3d26SJonathan Roelofs fields = parse_query(content) 27980df3d26SJonathan Roelofs f = self.send_head(fields) 28080df3d26SJonathan Roelofs if f: 28180df3d26SJonathan Roelofs self.copyfile(f, self.wfile) 28280df3d26SJonathan Roelofs f.close() 2833de41084SSerge Guelton except Exception as e: 28480df3d26SJonathan Roelofs self.handle_exception(e) 28580df3d26SJonathan Roelofs 28680df3d26SJonathan Roelofs def log_message(self, format, *args): 28780df3d26SJonathan Roelofs if self.server.options.debug: 288*dd3c26a0STobias Hieta sys.stderr.write( 289*dd3c26a0STobias Hieta "%s: SERVER: %s - - [%s] %s\n" 290*dd3c26a0STobias Hieta % ( 291*dd3c26a0STobias Hieta sys.argv[0], 29280df3d26SJonathan Roelofs self.address_string(), 29380df3d26SJonathan Roelofs self.log_date_time_string(), 294*dd3c26a0STobias Hieta format % args, 295*dd3c26a0STobias Hieta ) 296*dd3c26a0STobias Hieta ) 29780df3d26SJonathan Roelofs 29880df3d26SJonathan Roelofs def load_report(self, report): 299*dd3c26a0STobias Hieta path = os.path.join(self.server.root, "report-%s.html" % report) 30080df3d26SJonathan Roelofs data = open(path).read() 30180df3d26SJonathan Roelofs keys = {} 30280df3d26SJonathan Roelofs for item in kBugKeyValueRE.finditer(data): 30380df3d26SJonathan Roelofs k, v = item.groups() 30480df3d26SJonathan Roelofs keys[k] = v 30580df3d26SJonathan Roelofs return keys 30680df3d26SJonathan Roelofs 30780df3d26SJonathan Roelofs def load_crashes(self): 308*dd3c26a0STobias Hieta path = posixpath.join(self.server.root, "index.html") 30980df3d26SJonathan Roelofs data = open(path).read() 31080df3d26SJonathan Roelofs problems = [] 31180df3d26SJonathan Roelofs for item in kReportCrashEntryRE.finditer(data): 31280df3d26SJonathan Roelofs fieldData = item.group(1) 313*dd3c26a0STobias Hieta fields = dict( 314*dd3c26a0STobias Hieta [i.groups() for i in kReportCrashEntryKeyValueRE.finditer(fieldData)] 315*dd3c26a0STobias Hieta ) 31680df3d26SJonathan Roelofs problems.append(fields) 31780df3d26SJonathan Roelofs return problems 31880df3d26SJonathan Roelofs 31980df3d26SJonathan Roelofs def handle_exception(self, exc): 32080df3d26SJonathan Roelofs import traceback 321*dd3c26a0STobias Hieta 322f886c03eSSerge Guelton s = StringIO() 323c0ebe773SSerge Guelton print("INTERNAL ERROR\n", file=s) 324c0ebe773SSerge Guelton traceback.print_exc(file=s) 325*dd3c26a0STobias Hieta f = self.send_string(s.getvalue(), "text/plain") 32680df3d26SJonathan Roelofs if f: 32780df3d26SJonathan Roelofs self.copyfile(f, self.wfile) 32880df3d26SJonathan Roelofs f.close() 32980df3d26SJonathan Roelofs 33080df3d26SJonathan Roelofs def get_scalar_field(self, name): 33180df3d26SJonathan Roelofs if name in self.fields: 33280df3d26SJonathan Roelofs return self.fields[name][0] 33380df3d26SJonathan Roelofs else: 33480df3d26SJonathan Roelofs return None 33580df3d26SJonathan Roelofs 33680df3d26SJonathan Roelofs def submit_bug(self, c): 337*dd3c26a0STobias Hieta title = self.get_scalar_field("title") 338*dd3c26a0STobias Hieta description = self.get_scalar_field("description") 339*dd3c26a0STobias Hieta report = self.get_scalar_field("report") 340*dd3c26a0STobias Hieta reporterIndex = self.get_scalar_field("reporter") 34180df3d26SJonathan Roelofs files = [] 342*dd3c26a0STobias Hieta for fileID in self.fields.get("files", []): 34380df3d26SJonathan Roelofs try: 34480df3d26SJonathan Roelofs i = int(fileID) 34580df3d26SJonathan Roelofs except: 34680df3d26SJonathan Roelofs i = None 34780df3d26SJonathan Roelofs if i is None or i < 0 or i >= len(c.files): 348*dd3c26a0STobias Hieta return (False, "Invalid file ID") 34980df3d26SJonathan Roelofs files.append(c.files[i]) 35080df3d26SJonathan Roelofs 35180df3d26SJonathan Roelofs if not title: 35280df3d26SJonathan Roelofs return (False, "Missing title.") 35380df3d26SJonathan Roelofs if not description: 35480df3d26SJonathan Roelofs return (False, "Missing description.") 35580df3d26SJonathan Roelofs try: 35680df3d26SJonathan Roelofs reporterIndex = int(reporterIndex) 35780df3d26SJonathan Roelofs except: 35880df3d26SJonathan Roelofs return (False, "Invalid report method.") 35980df3d26SJonathan Roelofs 36080df3d26SJonathan Roelofs # Get the reporter and parameters. 36180df3d26SJonathan Roelofs reporter = self.server.reporters[reporterIndex] 36280df3d26SJonathan Roelofs parameters = {} 36380df3d26SJonathan Roelofs for o in reporter.getParameters(): 364*dd3c26a0STobias Hieta name = "%s_%s" % (reporter.getName(), o.getName()) 36580df3d26SJonathan Roelofs if name not in self.fields: 366*dd3c26a0STobias Hieta return ( 367*dd3c26a0STobias Hieta False, 368*dd3c26a0STobias Hieta 'Missing field "%s" for %s report method.' 369*dd3c26a0STobias Hieta % (name, reporter.getName()), 370*dd3c26a0STobias Hieta ) 37180df3d26SJonathan Roelofs parameters[o.getName()] = self.get_scalar_field(name) 37280df3d26SJonathan Roelofs 37380df3d26SJonathan Roelofs # Update config defaults. 374*dd3c26a0STobias Hieta if report != "None": 375*dd3c26a0STobias Hieta self.server.config.set("ScanView", "reporter", reporterIndex) 37680df3d26SJonathan Roelofs for o in reporter.getParameters(): 37780df3d26SJonathan Roelofs if o.saveConfigValue(): 37880df3d26SJonathan Roelofs name = o.getName() 37980df3d26SJonathan Roelofs self.server.config.set(reporter.getName(), name, parameters[name]) 38080df3d26SJonathan Roelofs 38180df3d26SJonathan Roelofs # Create the report. 38280df3d26SJonathan Roelofs bug = Reporter.BugReport(title, description, files) 38380df3d26SJonathan Roelofs 38480df3d26SJonathan Roelofs # Kick off a reporting thread. 38580df3d26SJonathan Roelofs t = ReporterThread(bug, reporter, parameters, self.server) 38680df3d26SJonathan Roelofs t.start() 38780df3d26SJonathan Roelofs 38880df3d26SJonathan Roelofs # Wait for thread to die... 38980df3d26SJonathan Roelofs while t.isAlive(): 390*dd3c26a0STobias Hieta time.sleep(0.25) 39180df3d26SJonathan Roelofs submitStatus = t.status 39280df3d26SJonathan Roelofs 39380df3d26SJonathan Roelofs return (t.success, t.status) 39480df3d26SJonathan Roelofs 39580df3d26SJonathan Roelofs def send_report_submit(self): 396*dd3c26a0STobias Hieta report = self.get_scalar_field("report") 39780df3d26SJonathan Roelofs c = self.get_report_context(report) 39880df3d26SJonathan Roelofs if c.reportSource is None: 39980df3d26SJonathan Roelofs reportingFor = "Report Crashes > " 400*dd3c26a0STobias Hieta fileBug = ( 401*dd3c26a0STobias Hieta """\ 402*dd3c26a0STobias Hieta<a href="/report_crashes">File Bug</a> > """ 403*dd3c26a0STobias Hieta % locals() 404*dd3c26a0STobias Hieta ) 40580df3d26SJonathan Roelofs else: 406*dd3c26a0STobias Hieta reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, report) 40780df3d26SJonathan Roelofs fileBug = '<a href="/report/%s">File Bug</a> > ' % report 408*dd3c26a0STobias Hieta title = self.get_scalar_field("title") 409*dd3c26a0STobias Hieta description = self.get_scalar_field("description") 41080df3d26SJonathan Roelofs 41180df3d26SJonathan Roelofs res, message = self.submit_bug(c) 41280df3d26SJonathan Roelofs 41380df3d26SJonathan Roelofs if res: 414*dd3c26a0STobias Hieta statusClass = "SubmitOk" 415*dd3c26a0STobias Hieta statusName = "Succeeded" 41680df3d26SJonathan Roelofs else: 417*dd3c26a0STobias Hieta statusClass = "SubmitFail" 418*dd3c26a0STobias Hieta statusName = "Failed" 41980df3d26SJonathan Roelofs 420*dd3c26a0STobias Hieta result = ( 421*dd3c26a0STobias Hieta """ 42280df3d26SJonathan Roelofs<head> 42380df3d26SJonathan Roelofs <title>Bug Submission</title> 42480df3d26SJonathan Roelofs <link rel="stylesheet" type="text/css" href="/scanview.css" /> 42580df3d26SJonathan Roelofs</head> 42680df3d26SJonathan Roelofs<body> 42780df3d26SJonathan Roelofs<h3> 42880df3d26SJonathan Roelofs<a href="/">Summary</a> > 42980df3d26SJonathan Roelofs%(reportingFor)s 43080df3d26SJonathan Roelofs%(fileBug)s 43180df3d26SJonathan RoelofsSubmit</h3> 43280df3d26SJonathan Roelofs<form name="form" action=""> 43380df3d26SJonathan Roelofs<table class="form"> 43480df3d26SJonathan Roelofs<tr><td> 43580df3d26SJonathan Roelofs<table class="form_group"> 43680df3d26SJonathan Roelofs<tr> 43780df3d26SJonathan Roelofs <td class="form_clabel">Title:</td> 43880df3d26SJonathan Roelofs <td class="form_value"> 43980df3d26SJonathan Roelofs <input type="text" name="title" size="50" value="%(title)s" disabled> 44080df3d26SJonathan Roelofs </td> 44180df3d26SJonathan Roelofs</tr> 44280df3d26SJonathan Roelofs<tr> 44380df3d26SJonathan Roelofs <td class="form_label">Description:</td> 44480df3d26SJonathan Roelofs <td class="form_value"> 44580df3d26SJonathan Roelofs<textarea rows="10" cols="80" name="description" disabled> 44680df3d26SJonathan Roelofs%(description)s 44780df3d26SJonathan Roelofs</textarea> 44880df3d26SJonathan Roelofs </td> 44980df3d26SJonathan Roelofs</table> 45080df3d26SJonathan Roelofs</td></tr> 45180df3d26SJonathan Roelofs</table> 45280df3d26SJonathan Roelofs</form> 45380df3d26SJonathan Roelofs<h1 class="%(statusClass)s">Submission %(statusName)s</h1> 45480df3d26SJonathan Roelofs%(message)s 45580df3d26SJonathan Roelofs<p> 45680df3d26SJonathan Roelofs<hr> 45780df3d26SJonathan Roelofs<a href="/">Return to Summary</a> 45880df3d26SJonathan Roelofs</body> 459*dd3c26a0STobias Hieta</html>""" 460*dd3c26a0STobias Hieta % locals() 461*dd3c26a0STobias Hieta ) 46280df3d26SJonathan Roelofs return self.send_string(result) 46380df3d26SJonathan Roelofs 46480df3d26SJonathan Roelofs def send_open_report(self, report): 46580df3d26SJonathan Roelofs try: 46680df3d26SJonathan Roelofs keys = self.load_report(report) 46780df3d26SJonathan Roelofs except IOError: 468*dd3c26a0STobias Hieta return self.send_error(400, "Invalid report.") 46980df3d26SJonathan Roelofs 470*dd3c26a0STobias Hieta file = keys.get("FILE") 47180df3d26SJonathan Roelofs if not file or not posixpath.exists(file): 47280df3d26SJonathan Roelofs return self.send_error(400, 'File does not exist: "%s"' % file) 47380df3d26SJonathan Roelofs 47480df3d26SJonathan Roelofs import startfile 475*dd3c26a0STobias Hieta 47680df3d26SJonathan Roelofs if self.server.options.debug: 477*dd3c26a0STobias Hieta print('%s: SERVER: opening "%s"' % (sys.argv[0], file), file=sys.stderr) 47880df3d26SJonathan Roelofs 47980df3d26SJonathan Roelofs status = startfile.open(file) 48080df3d26SJonathan Roelofs if status: 48180df3d26SJonathan Roelofs res = 'Opened: "%s"' % file 48280df3d26SJonathan Roelofs else: 48380df3d26SJonathan Roelofs res = 'Open failed: "%s"' % file 48480df3d26SJonathan Roelofs 485*dd3c26a0STobias Hieta return self.send_string(res, "text/plain") 48680df3d26SJonathan Roelofs 48780df3d26SJonathan Roelofs def get_report_context(self, report): 48809616bdbSSerge Guelton class Context(object): 48980df3d26SJonathan Roelofs pass 490*dd3c26a0STobias Hieta 491*dd3c26a0STobias Hieta if report is None or report == "None": 49280df3d26SJonathan Roelofs data = self.load_crashes() 49380df3d26SJonathan Roelofs # Don't allow empty reports. 49480df3d26SJonathan Roelofs if not data: 495*dd3c26a0STobias Hieta raise ValueError("No crashes detected!") 49680df3d26SJonathan Roelofs c = Context() 497*dd3c26a0STobias Hieta c.title = "clang static analyzer failures" 49880df3d26SJonathan Roelofs 49980df3d26SJonathan Roelofs stderrSummary = "" 50080df3d26SJonathan Roelofs for item in data: 501*dd3c26a0STobias Hieta if "stderr" in item: 502*dd3c26a0STobias Hieta path = posixpath.join(self.server.root, item["stderr"]) 50380df3d26SJonathan Roelofs if os.path.exists(path): 50480df3d26SJonathan Roelofs lns = itertools.islice(open(path), 0, 10) 505*dd3c26a0STobias Hieta stderrSummary += "%s\n--\n%s" % ( 506*dd3c26a0STobias Hieta item.get("src", "<unknown>"), 507*dd3c26a0STobias Hieta "".join(lns), 508*dd3c26a0STobias Hieta ) 50980df3d26SJonathan Roelofs 51080df3d26SJonathan Roelofs c.description = """\ 51180df3d26SJonathan RoelofsThe clang static analyzer failed on these inputs: 51280df3d26SJonathan Roelofs%s 51380df3d26SJonathan Roelofs 51480df3d26SJonathan RoelofsSTDERR Summary 51580df3d26SJonathan Roelofs-------------- 51680df3d26SJonathan Roelofs%s 517*dd3c26a0STobias Hieta""" % ( 518*dd3c26a0STobias Hieta "\n".join([item.get("src", "<unknown>") for item in data]), 519*dd3c26a0STobias Hieta stderrSummary, 520*dd3c26a0STobias Hieta ) 52180df3d26SJonathan Roelofs c.reportSource = None 52280df3d26SJonathan Roelofs c.navMarkup = "Report Crashes > " 52380df3d26SJonathan Roelofs c.files = [] 52480df3d26SJonathan Roelofs for item in data: 525*dd3c26a0STobias Hieta c.files.append(item.get("src", "")) 526*dd3c26a0STobias Hieta c.files.append(posixpath.join(self.server.root, item.get("file", ""))) 527*dd3c26a0STobias Hieta c.files.append( 528*dd3c26a0STobias Hieta posixpath.join(self.server.root, item.get("clangfile", "")) 529*dd3c26a0STobias Hieta ) 530*dd3c26a0STobias Hieta c.files.append(posixpath.join(self.server.root, item.get("stderr", ""))) 531*dd3c26a0STobias Hieta c.files.append(posixpath.join(self.server.root, item.get("info", ""))) 53280df3d26SJonathan Roelofs # Just in case something failed, ignore files which don't 53380df3d26SJonathan Roelofs # exist. 534*dd3c26a0STobias Hieta c.files = [f for f in c.files if os.path.exists(f) and os.path.isfile(f)] 53580df3d26SJonathan Roelofs else: 53680df3d26SJonathan Roelofs # Check that this is a valid report. 537*dd3c26a0STobias Hieta path = posixpath.join(self.server.root, "report-%s.html" % report) 53880df3d26SJonathan Roelofs if not posixpath.exists(path): 539*dd3c26a0STobias Hieta raise ValueError("Invalid report ID") 54080df3d26SJonathan Roelofs keys = self.load_report(report) 54180df3d26SJonathan Roelofs c = Context() 542*dd3c26a0STobias Hieta c.title = keys.get("DESC", "clang error (unrecognized") 54380df3d26SJonathan Roelofs c.description = """\ 54480df3d26SJonathan RoelofsBug reported by the clang static analyzer. 54580df3d26SJonathan Roelofs 54680df3d26SJonathan RoelofsDescription: %s 54780df3d26SJonathan RoelofsFile: %s 54880df3d26SJonathan RoelofsLine: %s 549*dd3c26a0STobias Hieta""" % ( 550*dd3c26a0STobias Hieta c.title, 551*dd3c26a0STobias Hieta keys.get("FILE", "<unknown>"), 552*dd3c26a0STobias Hieta keys.get("LINE", "<unknown>"), 553*dd3c26a0STobias Hieta ) 554*dd3c26a0STobias Hieta c.reportSource = "report-%s.html" % report 555*dd3c26a0STobias Hieta c.navMarkup = """<a href="/%s">Report %s</a> > """ % ( 556*dd3c26a0STobias Hieta c.reportSource, 557*dd3c26a0STobias Hieta report, 558*dd3c26a0STobias Hieta ) 55980df3d26SJonathan Roelofs 56080df3d26SJonathan Roelofs c.files = [path] 56180df3d26SJonathan Roelofs return c 56280df3d26SJonathan Roelofs 56380df3d26SJonathan Roelofs def send_report(self, report, configOverrides=None): 56480df3d26SJonathan Roelofs def getConfigOption(section, field): 565*dd3c26a0STobias Hieta if ( 566*dd3c26a0STobias Hieta configOverrides is not None 567*dd3c26a0STobias Hieta and section in configOverrides 568*dd3c26a0STobias Hieta and field in configOverrides[section] 569*dd3c26a0STobias Hieta ): 57080df3d26SJonathan Roelofs return configOverrides[section][field] 57180df3d26SJonathan Roelofs return self.server.config.get(section, field) 57280df3d26SJonathan Roelofs 57380df3d26SJonathan Roelofs # report is None is used for crashes 57480df3d26SJonathan Roelofs try: 57580df3d26SJonathan Roelofs c = self.get_report_context(report) 5763de41084SSerge Guelton except ValueError as e: 57780df3d26SJonathan Roelofs return self.send_error(400, e.message) 57880df3d26SJonathan Roelofs 57980df3d26SJonathan Roelofs title = c.title 58080df3d26SJonathan Roelofs description = c.description 58180df3d26SJonathan Roelofs reportingFor = c.navMarkup 58280df3d26SJonathan Roelofs if c.reportSource is None: 58380df3d26SJonathan Roelofs extraIFrame = "" 58480df3d26SJonathan Roelofs else: 58580df3d26SJonathan Roelofs extraIFrame = """\ 58680df3d26SJonathan Roelofs<iframe src="/%s" width="100%%" height="40%%" 58780df3d26SJonathan Roelofs scrolling="auto" frameborder="1"> 58880df3d26SJonathan Roelofs <a href="/%s">View Bug Report</a> 589*dd3c26a0STobias Hieta</iframe>""" % ( 590*dd3c26a0STobias Hieta c.reportSource, 591*dd3c26a0STobias Hieta c.reportSource, 592*dd3c26a0STobias Hieta ) 59380df3d26SJonathan Roelofs 59480df3d26SJonathan Roelofs reporterSelections = [] 59580df3d26SJonathan Roelofs reporterOptions = [] 59680df3d26SJonathan Roelofs 59780df3d26SJonathan Roelofs try: 598*dd3c26a0STobias Hieta active = int(getConfigOption("ScanView", "reporter")) 59980df3d26SJonathan Roelofs except: 60080df3d26SJonathan Roelofs active = 0 60180df3d26SJonathan Roelofs for i, r in enumerate(self.server.reporters): 602*dd3c26a0STobias Hieta selected = i == active 60380df3d26SJonathan Roelofs if selected: 604*dd3c26a0STobias Hieta selectedStr = " selected" 60580df3d26SJonathan Roelofs else: 606*dd3c26a0STobias Hieta selectedStr = "" 607*dd3c26a0STobias Hieta reporterSelections.append( 608*dd3c26a0STobias Hieta '<option value="%d"%s>%s</option>' % (i, selectedStr, r.getName()) 609*dd3c26a0STobias Hieta ) 610*dd3c26a0STobias Hieta options = "\n".join( 611*dd3c26a0STobias Hieta [o.getHTML(r, title, getConfigOption) for o in r.getParameters()] 612*dd3c26a0STobias Hieta ) 613*dd3c26a0STobias Hieta display = ("none", "")[selected] 614*dd3c26a0STobias Hieta reporterOptions.append( 615*dd3c26a0STobias Hieta """\ 61680df3d26SJonathan Roelofs<tr id="%sReporterOptions" style="display:%s"> 61780df3d26SJonathan Roelofs <td class="form_label">%s Options</td> 61880df3d26SJonathan Roelofs <td class="form_value"> 61980df3d26SJonathan Roelofs <table class="form_inner_group"> 62080df3d26SJonathan Roelofs%s 62180df3d26SJonathan Roelofs </table> 62280df3d26SJonathan Roelofs </td> 62380df3d26SJonathan Roelofs</tr> 624*dd3c26a0STobias Hieta""" 625*dd3c26a0STobias Hieta % (r.getName(), display, r.getName(), options) 626*dd3c26a0STobias Hieta ) 627*dd3c26a0STobias Hieta reporterSelections = "\n".join(reporterSelections) 628*dd3c26a0STobias Hieta reporterOptionsDivs = "\n".join(reporterOptions) 629*dd3c26a0STobias Hieta reportersArray = "[%s]" % ( 630*dd3c26a0STobias Hieta ",".join([repr(r.getName()) for r in self.server.reporters]) 631*dd3c26a0STobias Hieta ) 63280df3d26SJonathan Roelofs 63380df3d26SJonathan Roelofs if c.files: 63480df3d26SJonathan Roelofs fieldSize = min(5, len(c.files)) 635*dd3c26a0STobias Hieta attachFileOptions = "\n".join( 636*dd3c26a0STobias Hieta [ 637*dd3c26a0STobias Hieta """\ 638*dd3c26a0STobias Hieta<option value="%d" selected>%s</option>""" 639*dd3c26a0STobias Hieta % (i, v) 640*dd3c26a0STobias Hieta for i, v in enumerate(c.files) 641*dd3c26a0STobias Hieta ] 642*dd3c26a0STobias Hieta ) 64380df3d26SJonathan Roelofs attachFileRow = """\ 64480df3d26SJonathan Roelofs<tr> 64580df3d26SJonathan Roelofs <td class="form_label">Attach:</td> 64680df3d26SJonathan Roelofs <td class="form_value"> 64780df3d26SJonathan Roelofs<select style="width:100%%" name="files" multiple size=%d> 64880df3d26SJonathan Roelofs%s 64980df3d26SJonathan Roelofs</select> 65080df3d26SJonathan Roelofs </td> 65180df3d26SJonathan Roelofs</tr> 652*dd3c26a0STobias Hieta""" % ( 653*dd3c26a0STobias Hieta min(5, len(c.files)), 654*dd3c26a0STobias Hieta attachFileOptions, 655*dd3c26a0STobias Hieta ) 65680df3d26SJonathan Roelofs else: 65780df3d26SJonathan Roelofs attachFileRow = "" 65880df3d26SJonathan Roelofs 659*dd3c26a0STobias Hieta result = ( 660*dd3c26a0STobias Hieta """<html> 66180df3d26SJonathan Roelofs<head> 66280df3d26SJonathan Roelofs <title>File Bug</title> 66380df3d26SJonathan Roelofs <link rel="stylesheet" type="text/css" href="/scanview.css" /> 66480df3d26SJonathan Roelofs</head> 66580df3d26SJonathan Roelofs<script language="javascript" type="text/javascript"> 66680df3d26SJonathan Roelofsvar reporters = %(reportersArray)s; 66780df3d26SJonathan Roelofsfunction updateReporterOptions() { 66880df3d26SJonathan Roelofs index = document.getElementById('reporter').selectedIndex; 66980df3d26SJonathan Roelofs for (var i=0; i < reporters.length; ++i) { 67080df3d26SJonathan Roelofs o = document.getElementById(reporters[i] + "ReporterOptions"); 67180df3d26SJonathan Roelofs if (i == index) { 67280df3d26SJonathan Roelofs o.style.display = ""; 67380df3d26SJonathan Roelofs } else { 67480df3d26SJonathan Roelofs o.style.display = "none"; 67580df3d26SJonathan Roelofs } 67680df3d26SJonathan Roelofs } 67780df3d26SJonathan Roelofs} 67880df3d26SJonathan Roelofs</script> 67980df3d26SJonathan Roelofs<body onLoad="updateReporterOptions()"> 68080df3d26SJonathan Roelofs<h3> 68180df3d26SJonathan Roelofs<a href="/">Summary</a> > 68280df3d26SJonathan Roelofs%(reportingFor)s 68380df3d26SJonathan RoelofsFile Bug</h3> 68480df3d26SJonathan Roelofs<form name="form" action="/report_submit" method="post"> 68580df3d26SJonathan Roelofs<input type="hidden" name="report" value="%(report)s"> 68680df3d26SJonathan Roelofs 68780df3d26SJonathan Roelofs<table class="form"> 68880df3d26SJonathan Roelofs<tr><td> 68980df3d26SJonathan Roelofs<table class="form_group"> 69080df3d26SJonathan Roelofs<tr> 69180df3d26SJonathan Roelofs <td class="form_clabel">Title:</td> 69280df3d26SJonathan Roelofs <td class="form_value"> 69380df3d26SJonathan Roelofs <input type="text" name="title" size="50" value="%(title)s"> 69480df3d26SJonathan Roelofs </td> 69580df3d26SJonathan Roelofs</tr> 69680df3d26SJonathan Roelofs<tr> 69780df3d26SJonathan Roelofs <td class="form_label">Description:</td> 69880df3d26SJonathan Roelofs <td class="form_value"> 69980df3d26SJonathan Roelofs<textarea rows="10" cols="80" name="description"> 70080df3d26SJonathan Roelofs%(description)s 70180df3d26SJonathan Roelofs</textarea> 70280df3d26SJonathan Roelofs </td> 70380df3d26SJonathan Roelofs</tr> 70480df3d26SJonathan Roelofs 70580df3d26SJonathan Roelofs%(attachFileRow)s 70680df3d26SJonathan Roelofs 70780df3d26SJonathan Roelofs</table> 70880df3d26SJonathan Roelofs<br> 70980df3d26SJonathan Roelofs<table class="form_group"> 71080df3d26SJonathan Roelofs<tr> 71180df3d26SJonathan Roelofs <td class="form_clabel">Method:</td> 71280df3d26SJonathan Roelofs <td class="form_value"> 71380df3d26SJonathan Roelofs <select id="reporter" name="reporter" onChange="updateReporterOptions()"> 71480df3d26SJonathan Roelofs %(reporterSelections)s 71580df3d26SJonathan Roelofs </select> 71680df3d26SJonathan Roelofs </td> 71780df3d26SJonathan Roelofs</tr> 71880df3d26SJonathan Roelofs%(reporterOptionsDivs)s 71980df3d26SJonathan Roelofs</table> 72080df3d26SJonathan Roelofs<br> 72180df3d26SJonathan Roelofs</td></tr> 72280df3d26SJonathan Roelofs<tr><td class="form_submit"> 72380df3d26SJonathan Roelofs <input align="right" type="submit" name="Submit" value="Submit"> 72480df3d26SJonathan Roelofs</td></tr> 72580df3d26SJonathan Roelofs</table> 72680df3d26SJonathan Roelofs</form> 72780df3d26SJonathan Roelofs 72880df3d26SJonathan Roelofs%(extraIFrame)s 72980df3d26SJonathan Roelofs 73080df3d26SJonathan Roelofs</body> 731*dd3c26a0STobias Hieta</html>""" 732*dd3c26a0STobias Hieta % locals() 733*dd3c26a0STobias Hieta ) 73480df3d26SJonathan Roelofs 73580df3d26SJonathan Roelofs return self.send_string(result) 73680df3d26SJonathan Roelofs 73780df3d26SJonathan Roelofs def send_head(self, fields=None): 738*dd3c26a0STobias Hieta if self.server.options.onlyServeLocal and self.client_address[0] != "127.0.0.1": 739*dd3c26a0STobias Hieta return self.send_error(401, "Unauthorized host.") 74080df3d26SJonathan Roelofs 74180df3d26SJonathan Roelofs if fields is None: 74280df3d26SJonathan Roelofs fields = {} 74380df3d26SJonathan Roelofs self.fields = fields 74480df3d26SJonathan Roelofs 745c177c3a5SSerge Guelton o = urlparse(self.path) 74680df3d26SJonathan Roelofs self.fields = parse_query(o.query, fields) 747c177c3a5SSerge Guelton path = posixpath.normpath(unquote(o.path)) 74880df3d26SJonathan Roelofs 74980df3d26SJonathan Roelofs # Split the components and strip the root prefix. 750*dd3c26a0STobias Hieta components = path.split("/")[1:] 75180df3d26SJonathan Roelofs 75280df3d26SJonathan Roelofs # Special case some top-level entries. 75380df3d26SJonathan Roelofs if components: 75480df3d26SJonathan Roelofs name = components[0] 75580df3d26SJonathan Roelofs if len(components) == 2: 756*dd3c26a0STobias Hieta if name == "report": 75780df3d26SJonathan Roelofs return self.send_report(components[1]) 758*dd3c26a0STobias Hieta elif name == "open": 75980df3d26SJonathan Roelofs return self.send_open_report(components[1]) 76080df3d26SJonathan Roelofs elif len(components) == 1: 761*dd3c26a0STobias Hieta if name == "quit": 76280df3d26SJonathan Roelofs self.server.halt() 763*dd3c26a0STobias Hieta return self.send_string("Goodbye.", "text/plain") 764*dd3c26a0STobias Hieta elif name == "report_submit": 76580df3d26SJonathan Roelofs return self.send_report_submit() 766*dd3c26a0STobias Hieta elif name == "report_crashes": 767*dd3c26a0STobias Hieta overrides = {"ScanView": {}, "Radar": {}, "Email": {}} 76880df3d26SJonathan Roelofs for i, r in enumerate(self.server.reporters): 769*dd3c26a0STobias Hieta if r.getName() == "Radar": 770*dd3c26a0STobias Hieta overrides["ScanView"]["reporter"] = i 77180df3d26SJonathan Roelofs break 772*dd3c26a0STobias Hieta overrides["Radar"]["Component"] = "llvm - checker" 773*dd3c26a0STobias Hieta overrides["Radar"]["Component Version"] = "X" 77480df3d26SJonathan Roelofs return self.send_report(None, overrides) 775*dd3c26a0STobias Hieta elif name == "favicon.ico": 776*dd3c26a0STobias Hieta return self.send_path(posixpath.join(kShare, "bugcatcher.ico")) 77780df3d26SJonathan Roelofs 77880df3d26SJonathan Roelofs # Match directory entries. 779*dd3c26a0STobias Hieta if components[-1] == "": 780*dd3c26a0STobias Hieta components[-1] = "index.html" 78180df3d26SJonathan Roelofs 782*dd3c26a0STobias Hieta relpath = "/".join(components) 78380df3d26SJonathan Roelofs path = posixpath.join(self.server.root, relpath) 78480df3d26SJonathan Roelofs 78580df3d26SJonathan Roelofs if self.server.options.debug > 1: 786*dd3c26a0STobias Hieta print( 787*dd3c26a0STobias Hieta '%s: SERVER: sending path "%s"' % (sys.argv[0], path), file=sys.stderr 788*dd3c26a0STobias Hieta ) 78980df3d26SJonathan Roelofs return self.send_path(path) 79080df3d26SJonathan Roelofs 79180df3d26SJonathan Roelofs def send_404(self): 79280df3d26SJonathan Roelofs self.send_error(404, "File not found") 79380df3d26SJonathan Roelofs return None 79480df3d26SJonathan Roelofs 79580df3d26SJonathan Roelofs def send_path(self, path): 79680df3d26SJonathan Roelofs # If the requested path is outside the root directory, do not open it 79780df3d26SJonathan Roelofs rel = os.path.abspath(path) 79880df3d26SJonathan Roelofs if not rel.startswith(os.path.abspath(self.server.root)): 79980df3d26SJonathan Roelofs return self.send_404() 80080df3d26SJonathan Roelofs 80180df3d26SJonathan Roelofs ctype = self.guess_type(path) 802*dd3c26a0STobias Hieta if ctype.startswith("text/"): 80380df3d26SJonathan Roelofs # Patch file instead 80480df3d26SJonathan Roelofs return self.send_patched_file(path, ctype) 80580df3d26SJonathan Roelofs else: 806*dd3c26a0STobias Hieta mode = "rb" 80780df3d26SJonathan Roelofs try: 80880df3d26SJonathan Roelofs f = open(path, mode) 80980df3d26SJonathan Roelofs except IOError: 81080df3d26SJonathan Roelofs return self.send_404() 81180df3d26SJonathan Roelofs return self.send_file(f, ctype) 81280df3d26SJonathan Roelofs 81380df3d26SJonathan Roelofs def send_file(self, f, ctype): 81480df3d26SJonathan Roelofs # Patch files to add links, but skip binary files. 81580df3d26SJonathan Roelofs self.send_response(200) 81680df3d26SJonathan Roelofs self.send_header("Content-type", ctype) 81780df3d26SJonathan Roelofs fs = os.fstat(f.fileno()) 81880df3d26SJonathan Roelofs self.send_header("Content-Length", str(fs[6])) 81980df3d26SJonathan Roelofs self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 82080df3d26SJonathan Roelofs self.end_headers() 82180df3d26SJonathan Roelofs return f 82280df3d26SJonathan Roelofs 823*dd3c26a0STobias Hieta def send_string(self, s, ctype="text/html", headers=True, mtime=None): 824*dd3c26a0STobias Hieta encoded_s = s.encode("utf-8") 82580df3d26SJonathan Roelofs if headers: 82680df3d26SJonathan Roelofs self.send_response(200) 82780df3d26SJonathan Roelofs self.send_header("Content-type", ctype) 828f886c03eSSerge Guelton self.send_header("Content-Length", str(len(encoded_s))) 82980df3d26SJonathan Roelofs if mtime is None: 83080df3d26SJonathan Roelofs mtime = self.dynamic_mtime 83180df3d26SJonathan Roelofs self.send_header("Last-Modified", self.date_time_string(mtime)) 83280df3d26SJonathan Roelofs self.end_headers() 833f886c03eSSerge Guelton return BytesIO(encoded_s) 83480df3d26SJonathan Roelofs 83580df3d26SJonathan Roelofs def send_patched_file(self, path, ctype): 83680df3d26SJonathan Roelofs # Allow a very limited set of variables. This is pretty gross. 83780df3d26SJonathan Roelofs variables = {} 838*dd3c26a0STobias Hieta variables["report"] = "" 83980df3d26SJonathan Roelofs m = kReportFileRE.match(path) 84080df3d26SJonathan Roelofs if m: 841*dd3c26a0STobias Hieta variables["report"] = m.group(2) 84280df3d26SJonathan Roelofs 84380df3d26SJonathan Roelofs try: 844*dd3c26a0STobias Hieta f = open(path, "rb") 84580df3d26SJonathan Roelofs except IOError: 84680df3d26SJonathan Roelofs return self.send_404() 84780df3d26SJonathan Roelofs fs = os.fstat(f.fileno()) 848*dd3c26a0STobias Hieta data = f.read().decode("utf-8") 84980df3d26SJonathan Roelofs for a, b in kReportReplacements: 85080df3d26SJonathan Roelofs data = a.sub(b % variables, data) 85180df3d26SJonathan Roelofs return self.send_string(data, ctype, mtime=fs.st_mtime) 85280df3d26SJonathan Roelofs 85380df3d26SJonathan Roelofs 85480df3d26SJonathan Roelofsdef create_server(address, options, root): 85580df3d26SJonathan Roelofs import Reporter 85680df3d26SJonathan Roelofs 85780df3d26SJonathan Roelofs reporters = Reporter.getReporters() 85880df3d26SJonathan Roelofs 859*dd3c26a0STobias Hieta return ScanViewServer(address, ScanViewRequestHandler, root, reporters, options) 860