xref: /llvm-project/clang/tools/scan-view/share/ScanView.py (revision dd3c26a045c081620375a878159f536758baba6e)
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