xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/scan-view/share/ScanView.py (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoergfrom __future__ import print_function
27330f729Sjoergtry:
37330f729Sjoerg    from http.server import HTTPServer, SimpleHTTPRequestHandler
47330f729Sjoergexcept ImportError:
57330f729Sjoerg    from BaseHTTPServer import HTTPServer
67330f729Sjoerg    from SimpleHTTPServer import SimpleHTTPRequestHandler
77330f729Sjoergimport os
87330f729Sjoergimport sys
97330f729Sjoergtry:
107330f729Sjoerg    from urlparse import urlparse
117330f729Sjoerg    from urllib import unquote
127330f729Sjoergexcept ImportError:
137330f729Sjoerg    from urllib.parse import urlparse, unquote
147330f729Sjoerg
157330f729Sjoergimport posixpath
167330f729Sjoerg
177330f729Sjoergif sys.version_info.major >= 3:
187330f729Sjoerg    from io import StringIO, BytesIO
197330f729Sjoergelse:
207330f729Sjoerg    from io import BytesIO, BytesIO as StringIO
217330f729Sjoerg
227330f729Sjoergimport re
237330f729Sjoergimport shutil
247330f729Sjoergimport threading
257330f729Sjoergimport time
267330f729Sjoergimport socket
277330f729Sjoergimport itertools
287330f729Sjoerg
297330f729Sjoergimport Reporter
307330f729Sjoergtry:
317330f729Sjoerg    import configparser
327330f729Sjoergexcept ImportError:
337330f729Sjoerg    import ConfigParser as configparser
347330f729Sjoerg
357330f729Sjoerg###
367330f729Sjoerg# Various patterns matched or replaced by server.
377330f729Sjoerg
387330f729SjoergkReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
397330f729Sjoerg
407330f729SjoergkBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
417330f729Sjoerg
427330f729Sjoerg#  <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
437330f729Sjoerg
447330f729SjoergkReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
457330f729SjoergkReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
467330f729Sjoerg
477330f729SjoergkReportReplacements = []
487330f729Sjoerg
497330f729Sjoerg# Add custom javascript.
507330f729SjoergkReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
517330f729Sjoerg<script language="javascript" type="text/javascript">
527330f729Sjoergfunction load(url) {
537330f729Sjoerg  if (window.XMLHttpRequest) {
547330f729Sjoerg    req = new XMLHttpRequest();
557330f729Sjoerg  } else if (window.ActiveXObject) {
567330f729Sjoerg    req = new ActiveXObject("Microsoft.XMLHTTP");
577330f729Sjoerg  }
587330f729Sjoerg  if (req != undefined) {
597330f729Sjoerg    req.open("GET", url, true);
607330f729Sjoerg    req.send("");
617330f729Sjoerg  }
627330f729Sjoerg}
637330f729Sjoerg</script>"""))
647330f729Sjoerg
657330f729Sjoerg# Insert additional columns.
667330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
677330f729Sjoerg                            '<td></td><td></td>'))
687330f729Sjoerg
697330f729Sjoerg# Insert report bug and open file links.
707330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
717330f729Sjoerg                            ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
727330f729Sjoerg                             '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
737330f729Sjoerg
747330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
757330f729Sjoerg                                       '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
767330f729Sjoerg
777330f729SjoergkReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
787330f729Sjoerg                            '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
797330f729Sjoerg
807330f729Sjoerg# Insert report crashes link.
817330f729Sjoerg
827330f729Sjoerg# Disabled for the time being until we decide exactly when this should
837330f729Sjoerg# be enabled. Also the radar reporter needs to be fixed to report
847330f729Sjoerg# multiple files.
857330f729Sjoerg
867330f729Sjoerg#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
877330f729Sjoerg#                            '<br>These files will automatically be attached to ' +
887330f729Sjoerg#                            'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
897330f729Sjoerg
907330f729Sjoerg###
917330f729Sjoerg# Other simple parameters
927330f729Sjoerg
937330f729SjoergkShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view')
947330f729SjoergkConfigPath = os.path.expanduser('~/.scanview.cfg')
957330f729Sjoerg
967330f729Sjoerg###
977330f729Sjoerg
987330f729Sjoerg__version__ = "0.1"
997330f729Sjoerg
1007330f729Sjoerg__all__ = ["create_server"]
1017330f729Sjoerg
1027330f729Sjoergclass ReporterThread(threading.Thread):
1037330f729Sjoerg    def __init__(self, report, reporter, parameters, server):
1047330f729Sjoerg        threading.Thread.__init__(self)
1057330f729Sjoerg        self.report = report
1067330f729Sjoerg        self.server = server
1077330f729Sjoerg        self.reporter = reporter
1087330f729Sjoerg        self.parameters = parameters
1097330f729Sjoerg        self.success = False
1107330f729Sjoerg        self.status = None
1117330f729Sjoerg
1127330f729Sjoerg    def run(self):
1137330f729Sjoerg        result = None
1147330f729Sjoerg        try:
1157330f729Sjoerg            if self.server.options.debug:
1167330f729Sjoerg                print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr)
1177330f729Sjoerg            self.status = self.reporter.fileReport(self.report, self.parameters)
1187330f729Sjoerg            self.success = True
1197330f729Sjoerg            time.sleep(3)
1207330f729Sjoerg            if self.server.options.debug:
1217330f729Sjoerg                print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr)
1227330f729Sjoerg        except Reporter.ReportFailure as e:
1237330f729Sjoerg            self.status = e.value
1247330f729Sjoerg        except Exception as e:
1257330f729Sjoerg            s = StringIO()
1267330f729Sjoerg            import traceback
1277330f729Sjoerg            print('<b>Unhandled Exception</b><br><pre>', file=s)
1287330f729Sjoerg            traceback.print_exc(file=s)
1297330f729Sjoerg            print('</pre>', file=s)
1307330f729Sjoerg            self.status = s.getvalue()
1317330f729Sjoerg
1327330f729Sjoergclass ScanViewServer(HTTPServer):
1337330f729Sjoerg    def __init__(self, address, handler, root, reporters, options):
1347330f729Sjoerg        HTTPServer.__init__(self, address, handler)
1357330f729Sjoerg        self.root = root
1367330f729Sjoerg        self.reporters = reporters
1377330f729Sjoerg        self.options = options
1387330f729Sjoerg        self.halted = False
1397330f729Sjoerg        self.config = None
1407330f729Sjoerg        self.load_config()
1417330f729Sjoerg
1427330f729Sjoerg    def load_config(self):
1437330f729Sjoerg        self.config = configparser.RawConfigParser()
1447330f729Sjoerg
1457330f729Sjoerg        # Add defaults
1467330f729Sjoerg        self.config.add_section('ScanView')
1477330f729Sjoerg        for r in self.reporters:
1487330f729Sjoerg            self.config.add_section(r.getName())
1497330f729Sjoerg            for p in r.getParameters():
1507330f729Sjoerg              if p.saveConfigValue():
1517330f729Sjoerg                self.config.set(r.getName(), p.getName(), '')
1527330f729Sjoerg
1537330f729Sjoerg        # Ignore parse errors
1547330f729Sjoerg        try:
1557330f729Sjoerg            self.config.read([kConfigPath])
1567330f729Sjoerg        except:
1577330f729Sjoerg            pass
1587330f729Sjoerg
1597330f729Sjoerg        # Save on exit
1607330f729Sjoerg        import atexit
1617330f729Sjoerg        atexit.register(lambda: self.save_config())
1627330f729Sjoerg
1637330f729Sjoerg    def save_config(self):
1647330f729Sjoerg        # Ignore errors (only called on exit).
1657330f729Sjoerg        try:
1667330f729Sjoerg            f = open(kConfigPath,'w')
1677330f729Sjoerg            self.config.write(f)
1687330f729Sjoerg            f.close()
1697330f729Sjoerg        except:
1707330f729Sjoerg            pass
1717330f729Sjoerg
1727330f729Sjoerg    def halt(self):
1737330f729Sjoerg        self.halted = True
1747330f729Sjoerg        if self.options.debug:
1757330f729Sjoerg            print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr)
1767330f729Sjoerg
1777330f729Sjoerg    def serve_forever(self):
1787330f729Sjoerg        while not self.halted:
1797330f729Sjoerg            if self.options.debug > 1:
1807330f729Sjoerg                print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr)
1817330f729Sjoerg            try:
1827330f729Sjoerg                self.handle_request()
1837330f729Sjoerg            except OSError as e:
1847330f729Sjoerg                print('OSError',e.errno)
1857330f729Sjoerg
1867330f729Sjoerg    def finish_request(self, request, client_address):
1877330f729Sjoerg        if self.options.autoReload:
1887330f729Sjoerg            import ScanView
1897330f729Sjoerg            self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
1907330f729Sjoerg        HTTPServer.finish_request(self, request, client_address)
1917330f729Sjoerg
1927330f729Sjoerg    def handle_error(self, request, client_address):
1937330f729Sjoerg        # Ignore socket errors
1947330f729Sjoerg        info = sys.exc_info()
1957330f729Sjoerg        if info and isinstance(info[1], socket.error):
1967330f729Sjoerg            if self.options.debug > 1:
1977330f729Sjoerg                print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr)
1987330f729Sjoerg            return
1997330f729Sjoerg        HTTPServer.handle_error(self, request, client_address)
2007330f729Sjoerg
2017330f729Sjoerg# Borrowed from Quixote, with simplifications.
2027330f729Sjoergdef parse_query(qs, fields=None):
2037330f729Sjoerg    if fields is None:
2047330f729Sjoerg        fields = {}
2057330f729Sjoerg    for chunk in (_f for _f in qs.split('&') if _f):
2067330f729Sjoerg        if '=' not in chunk:
2077330f729Sjoerg            name = chunk
2087330f729Sjoerg            value = ''
2097330f729Sjoerg        else:
2107330f729Sjoerg            name, value = chunk.split('=', 1)
2117330f729Sjoerg        name = unquote(name.replace('+', ' '))
2127330f729Sjoerg        value = unquote(value.replace('+', ' '))
2137330f729Sjoerg        item = fields.get(name)
2147330f729Sjoerg        if item is None:
2157330f729Sjoerg            fields[name] = [value]
2167330f729Sjoerg        else:
2177330f729Sjoerg            item.append(value)
2187330f729Sjoerg    return fields
2197330f729Sjoerg
2207330f729Sjoergclass ScanViewRequestHandler(SimpleHTTPRequestHandler):
2217330f729Sjoerg    server_version = "ScanViewServer/" + __version__
2227330f729Sjoerg    dynamic_mtime = time.time()
2237330f729Sjoerg
2247330f729Sjoerg    def do_HEAD(self):
2257330f729Sjoerg        try:
2267330f729Sjoerg            SimpleHTTPRequestHandler.do_HEAD(self)
2277330f729Sjoerg        except Exception as e:
2287330f729Sjoerg            self.handle_exception(e)
2297330f729Sjoerg
2307330f729Sjoerg    def do_GET(self):
2317330f729Sjoerg        try:
2327330f729Sjoerg            SimpleHTTPRequestHandler.do_GET(self)
2337330f729Sjoerg        except Exception as e:
2347330f729Sjoerg            self.handle_exception(e)
2357330f729Sjoerg
2367330f729Sjoerg    def do_POST(self):
2377330f729Sjoerg        """Serve a POST request."""
2387330f729Sjoerg        try:
2397330f729Sjoerg            length = self.headers.getheader('content-length') or "0"
2407330f729Sjoerg            try:
2417330f729Sjoerg                length = int(length)
2427330f729Sjoerg            except:
2437330f729Sjoerg                length = 0
2447330f729Sjoerg            content = self.rfile.read(length)
2457330f729Sjoerg            fields = parse_query(content)
2467330f729Sjoerg            f = self.send_head(fields)
2477330f729Sjoerg            if f:
2487330f729Sjoerg                self.copyfile(f, self.wfile)
2497330f729Sjoerg                f.close()
2507330f729Sjoerg        except Exception as e:
2517330f729Sjoerg            self.handle_exception(e)
2527330f729Sjoerg
2537330f729Sjoerg    def log_message(self, format, *args):
2547330f729Sjoerg        if self.server.options.debug:
2557330f729Sjoerg            sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
2567330f729Sjoerg                             (sys.argv[0],
2577330f729Sjoerg                              self.address_string(),
2587330f729Sjoerg                              self.log_date_time_string(),
2597330f729Sjoerg                              format%args))
2607330f729Sjoerg
2617330f729Sjoerg    def load_report(self, report):
2627330f729Sjoerg        path = os.path.join(self.server.root, 'report-%s.html'%report)
2637330f729Sjoerg        data = open(path).read()
2647330f729Sjoerg        keys = {}
2657330f729Sjoerg        for item in kBugKeyValueRE.finditer(data):
2667330f729Sjoerg            k,v = item.groups()
2677330f729Sjoerg            keys[k] = v
2687330f729Sjoerg        return keys
2697330f729Sjoerg
2707330f729Sjoerg    def load_crashes(self):
2717330f729Sjoerg        path = posixpath.join(self.server.root, 'index.html')
2727330f729Sjoerg        data = open(path).read()
2737330f729Sjoerg        problems = []
2747330f729Sjoerg        for item in kReportCrashEntryRE.finditer(data):
2757330f729Sjoerg            fieldData = item.group(1)
2767330f729Sjoerg            fields = dict([i.groups() for i in
2777330f729Sjoerg                           kReportCrashEntryKeyValueRE.finditer(fieldData)])
2787330f729Sjoerg            problems.append(fields)
2797330f729Sjoerg        return problems
2807330f729Sjoerg
2817330f729Sjoerg    def handle_exception(self, exc):
2827330f729Sjoerg        import traceback
2837330f729Sjoerg        s = StringIO()
2847330f729Sjoerg        print("INTERNAL ERROR\n", file=s)
2857330f729Sjoerg        traceback.print_exc(file=s)
2867330f729Sjoerg        f = self.send_string(s.getvalue(), 'text/plain')
2877330f729Sjoerg        if f:
2887330f729Sjoerg            self.copyfile(f, self.wfile)
2897330f729Sjoerg            f.close()
2907330f729Sjoerg
2917330f729Sjoerg    def get_scalar_field(self, name):
2927330f729Sjoerg        if name in self.fields:
2937330f729Sjoerg            return self.fields[name][0]
2947330f729Sjoerg        else:
2957330f729Sjoerg            return None
2967330f729Sjoerg
2977330f729Sjoerg    def submit_bug(self, c):
2987330f729Sjoerg        title = self.get_scalar_field('title')
2997330f729Sjoerg        description = self.get_scalar_field('description')
3007330f729Sjoerg        report = self.get_scalar_field('report')
3017330f729Sjoerg        reporterIndex = self.get_scalar_field('reporter')
3027330f729Sjoerg        files = []
3037330f729Sjoerg        for fileID in self.fields.get('files',[]):
3047330f729Sjoerg            try:
3057330f729Sjoerg                i = int(fileID)
3067330f729Sjoerg            except:
3077330f729Sjoerg                i = None
3087330f729Sjoerg            if i is None or i<0 or i>=len(c.files):
3097330f729Sjoerg                return (False, 'Invalid file ID')
3107330f729Sjoerg            files.append(c.files[i])
3117330f729Sjoerg
3127330f729Sjoerg        if not title:
3137330f729Sjoerg            return (False, "Missing title.")
3147330f729Sjoerg        if not description:
3157330f729Sjoerg            return (False, "Missing description.")
3167330f729Sjoerg        try:
3177330f729Sjoerg            reporterIndex = int(reporterIndex)
3187330f729Sjoerg        except:
3197330f729Sjoerg            return (False, "Invalid report method.")
3207330f729Sjoerg
3217330f729Sjoerg        # Get the reporter and parameters.
3227330f729Sjoerg        reporter = self.server.reporters[reporterIndex]
3237330f729Sjoerg        parameters = {}
3247330f729Sjoerg        for o in reporter.getParameters():
3257330f729Sjoerg            name = '%s_%s'%(reporter.getName(),o.getName())
3267330f729Sjoerg            if name not in self.fields:
3277330f729Sjoerg                return (False,
3287330f729Sjoerg                        'Missing field "%s" for %s report method.'%(name,
3297330f729Sjoerg                                                                    reporter.getName()))
3307330f729Sjoerg            parameters[o.getName()] = self.get_scalar_field(name)
3317330f729Sjoerg
3327330f729Sjoerg        # Update config defaults.
3337330f729Sjoerg        if report != 'None':
3347330f729Sjoerg            self.server.config.set('ScanView', 'reporter', reporterIndex)
3357330f729Sjoerg            for o in reporter.getParameters():
3367330f729Sjoerg              if o.saveConfigValue():
3377330f729Sjoerg                name = o.getName()
3387330f729Sjoerg                self.server.config.set(reporter.getName(), name, parameters[name])
3397330f729Sjoerg
3407330f729Sjoerg        # Create the report.
3417330f729Sjoerg        bug = Reporter.BugReport(title, description, files)
3427330f729Sjoerg
3437330f729Sjoerg        # Kick off a reporting thread.
3447330f729Sjoerg        t = ReporterThread(bug, reporter, parameters, self.server)
3457330f729Sjoerg        t.start()
3467330f729Sjoerg
3477330f729Sjoerg        # Wait for thread to die...
3487330f729Sjoerg        while t.isAlive():
3497330f729Sjoerg            time.sleep(.25)
3507330f729Sjoerg        submitStatus = t.status
3517330f729Sjoerg
3527330f729Sjoerg        return (t.success, t.status)
3537330f729Sjoerg
3547330f729Sjoerg    def send_report_submit(self):
3557330f729Sjoerg        report = self.get_scalar_field('report')
3567330f729Sjoerg        c = self.get_report_context(report)
3577330f729Sjoerg        if c.reportSource is None:
3587330f729Sjoerg            reportingFor = "Report Crashes > "
3597330f729Sjoerg            fileBug = """\
3607330f729Sjoerg<a href="/report_crashes">File Bug</a> > """%locals()
3617330f729Sjoerg        else:
3627330f729Sjoerg            reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
3637330f729Sjoerg                                                                   report)
3647330f729Sjoerg            fileBug = '<a href="/report/%s">File Bug</a> > ' % report
3657330f729Sjoerg        title = self.get_scalar_field('title')
3667330f729Sjoerg        description = self.get_scalar_field('description')
3677330f729Sjoerg
3687330f729Sjoerg        res,message = self.submit_bug(c)
3697330f729Sjoerg
3707330f729Sjoerg        if res:
3717330f729Sjoerg            statusClass = 'SubmitOk'
3727330f729Sjoerg            statusName = 'Succeeded'
3737330f729Sjoerg        else:
3747330f729Sjoerg            statusClass = 'SubmitFail'
3757330f729Sjoerg            statusName = 'Failed'
3767330f729Sjoerg
3777330f729Sjoerg        result = """
3787330f729Sjoerg<head>
3797330f729Sjoerg  <title>Bug Submission</title>
3807330f729Sjoerg  <link rel="stylesheet" type="text/css" href="/scanview.css" />
3817330f729Sjoerg</head>
3827330f729Sjoerg<body>
3837330f729Sjoerg<h3>
3847330f729Sjoerg<a href="/">Summary</a> >
3857330f729Sjoerg%(reportingFor)s
3867330f729Sjoerg%(fileBug)s
3877330f729SjoergSubmit</h3>
3887330f729Sjoerg<form name="form" action="">
3897330f729Sjoerg<table class="form">
3907330f729Sjoerg<tr><td>
3917330f729Sjoerg<table class="form_group">
3927330f729Sjoerg<tr>
3937330f729Sjoerg  <td class="form_clabel">Title:</td>
3947330f729Sjoerg  <td class="form_value">
3957330f729Sjoerg    <input type="text" name="title" size="50" value="%(title)s" disabled>
3967330f729Sjoerg  </td>
3977330f729Sjoerg</tr>
3987330f729Sjoerg<tr>
3997330f729Sjoerg  <td class="form_label">Description:</td>
4007330f729Sjoerg  <td class="form_value">
4017330f729Sjoerg<textarea rows="10" cols="80" name="description" disabled>
4027330f729Sjoerg%(description)s
4037330f729Sjoerg</textarea>
4047330f729Sjoerg  </td>
4057330f729Sjoerg</table>
4067330f729Sjoerg</td></tr>
4077330f729Sjoerg</table>
4087330f729Sjoerg</form>
4097330f729Sjoerg<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
4107330f729Sjoerg%(message)s
4117330f729Sjoerg<p>
4127330f729Sjoerg<hr>
4137330f729Sjoerg<a href="/">Return to Summary</a>
4147330f729Sjoerg</body>
4157330f729Sjoerg</html>"""%locals()
4167330f729Sjoerg        return self.send_string(result)
4177330f729Sjoerg
4187330f729Sjoerg    def send_open_report(self, report):
4197330f729Sjoerg        try:
4207330f729Sjoerg            keys = self.load_report(report)
4217330f729Sjoerg        except IOError:
4227330f729Sjoerg            return self.send_error(400, 'Invalid report.')
4237330f729Sjoerg
4247330f729Sjoerg        file = keys.get('FILE')
4257330f729Sjoerg        if not file or not posixpath.exists(file):
4267330f729Sjoerg            return self.send_error(400, 'File does not exist: "%s"' % file)
4277330f729Sjoerg
4287330f729Sjoerg        import startfile
4297330f729Sjoerg        if self.server.options.debug:
4307330f729Sjoerg            print('%s: SERVER: opening "%s"'%(sys.argv[0],
4317330f729Sjoerg                                                            file), file=sys.stderr)
4327330f729Sjoerg
4337330f729Sjoerg        status = startfile.open(file)
4347330f729Sjoerg        if status:
4357330f729Sjoerg            res = 'Opened: "%s"' % file
4367330f729Sjoerg        else:
4377330f729Sjoerg            res = 'Open failed: "%s"' % file
4387330f729Sjoerg
4397330f729Sjoerg        return self.send_string(res, 'text/plain')
4407330f729Sjoerg
4417330f729Sjoerg    def get_report_context(self, report):
4427330f729Sjoerg        class Context(object):
4437330f729Sjoerg            pass
4447330f729Sjoerg        if report is None or report == 'None':
4457330f729Sjoerg            data = self.load_crashes()
4467330f729Sjoerg            # Don't allow empty reports.
4477330f729Sjoerg            if not data:
4487330f729Sjoerg                raise ValueError('No crashes detected!')
4497330f729Sjoerg            c = Context()
4507330f729Sjoerg            c.title = 'clang static analyzer failures'
4517330f729Sjoerg
4527330f729Sjoerg            stderrSummary = ""
4537330f729Sjoerg            for item in data:
4547330f729Sjoerg                if 'stderr' in item:
4557330f729Sjoerg                    path = posixpath.join(self.server.root, item['stderr'])
4567330f729Sjoerg                    if os.path.exists(path):
4577330f729Sjoerg                        lns = itertools.islice(open(path), 0, 10)
4587330f729Sjoerg                        stderrSummary += '%s\n--\n%s' % (item.get('src',
4597330f729Sjoerg                                                                  '<unknown>'),
4607330f729Sjoerg                                                         ''.join(lns))
4617330f729Sjoerg
4627330f729Sjoerg            c.description = """\
4637330f729SjoergThe clang static analyzer failed on these inputs:
4647330f729Sjoerg%s
4657330f729Sjoerg
4667330f729SjoergSTDERR Summary
4677330f729Sjoerg--------------
4687330f729Sjoerg%s
4697330f729Sjoerg""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
4707330f729Sjoerg       stderrSummary)
4717330f729Sjoerg            c.reportSource = None
4727330f729Sjoerg            c.navMarkup = "Report Crashes > "
4737330f729Sjoerg            c.files = []
4747330f729Sjoerg            for item in data:
4757330f729Sjoerg                c.files.append(item.get('src',''))
4767330f729Sjoerg                c.files.append(posixpath.join(self.server.root,
4777330f729Sjoerg                                              item.get('file','')))
4787330f729Sjoerg                c.files.append(posixpath.join(self.server.root,
4797330f729Sjoerg                                              item.get('clangfile','')))
4807330f729Sjoerg                c.files.append(posixpath.join(self.server.root,
4817330f729Sjoerg                                              item.get('stderr','')))
4827330f729Sjoerg                c.files.append(posixpath.join(self.server.root,
4837330f729Sjoerg                                              item.get('info','')))
4847330f729Sjoerg            # Just in case something failed, ignore files which don't
4857330f729Sjoerg            # exist.
4867330f729Sjoerg            c.files = [f for f in c.files
4877330f729Sjoerg                       if os.path.exists(f) and os.path.isfile(f)]
4887330f729Sjoerg        else:
4897330f729Sjoerg            # Check that this is a valid report.
4907330f729Sjoerg            path = posixpath.join(self.server.root, 'report-%s.html' % report)
4917330f729Sjoerg            if not posixpath.exists(path):
4927330f729Sjoerg                raise ValueError('Invalid report ID')
4937330f729Sjoerg            keys = self.load_report(report)
4947330f729Sjoerg            c = Context()
4957330f729Sjoerg            c.title = keys.get('DESC','clang error (unrecognized')
4967330f729Sjoerg            c.description = """\
4977330f729SjoergBug reported by the clang static analyzer.
4987330f729Sjoerg
4997330f729SjoergDescription: %s
5007330f729SjoergFile: %s
5017330f729SjoergLine: %s
5027330f729Sjoerg"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
5037330f729Sjoerg            c.reportSource = 'report-%s.html' % report
5047330f729Sjoerg            c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
5057330f729Sjoerg                                                                  report)
5067330f729Sjoerg
5077330f729Sjoerg            c.files = [path]
5087330f729Sjoerg        return c
5097330f729Sjoerg
5107330f729Sjoerg    def send_report(self, report, configOverrides=None):
5117330f729Sjoerg        def getConfigOption(section, field):
5127330f729Sjoerg            if (configOverrides is not None and
5137330f729Sjoerg                section in configOverrides and
5147330f729Sjoerg                field in configOverrides[section]):
5157330f729Sjoerg                return configOverrides[section][field]
5167330f729Sjoerg            return self.server.config.get(section, field)
5177330f729Sjoerg
5187330f729Sjoerg        # report is None is used for crashes
5197330f729Sjoerg        try:
5207330f729Sjoerg            c = self.get_report_context(report)
5217330f729Sjoerg        except ValueError as e:
5227330f729Sjoerg            return self.send_error(400, e.message)
5237330f729Sjoerg
5247330f729Sjoerg        title = c.title
5257330f729Sjoerg        description= c.description
5267330f729Sjoerg        reportingFor = c.navMarkup
5277330f729Sjoerg        if c.reportSource is None:
5287330f729Sjoerg            extraIFrame = ""
5297330f729Sjoerg        else:
5307330f729Sjoerg            extraIFrame = """\
5317330f729Sjoerg<iframe src="/%s" width="100%%" height="40%%"
5327330f729Sjoerg        scrolling="auto" frameborder="1">
5337330f729Sjoerg  <a href="/%s">View Bug Report</a>
5347330f729Sjoerg</iframe>""" % (c.reportSource, c.reportSource)
5357330f729Sjoerg
5367330f729Sjoerg        reporterSelections = []
5377330f729Sjoerg        reporterOptions = []
5387330f729Sjoerg
5397330f729Sjoerg        try:
5407330f729Sjoerg            active = int(getConfigOption('ScanView','reporter'))
5417330f729Sjoerg        except:
5427330f729Sjoerg            active = 0
5437330f729Sjoerg        for i,r in enumerate(self.server.reporters):
5447330f729Sjoerg            selected = (i == active)
5457330f729Sjoerg            if selected:
5467330f729Sjoerg                selectedStr = ' selected'
5477330f729Sjoerg            else:
5487330f729Sjoerg                selectedStr = ''
5497330f729Sjoerg            reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
5507330f729Sjoerg            options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
5517330f729Sjoerg            display = ('none','')[selected]
5527330f729Sjoerg            reporterOptions.append("""\
5537330f729Sjoerg<tr id="%sReporterOptions" style="display:%s">
5547330f729Sjoerg  <td class="form_label">%s Options</td>
5557330f729Sjoerg  <td class="form_value">
5567330f729Sjoerg    <table class="form_inner_group">
5577330f729Sjoerg%s
5587330f729Sjoerg    </table>
5597330f729Sjoerg  </td>
5607330f729Sjoerg</tr>
5617330f729Sjoerg"""%(r.getName(),display,r.getName(),options))
5627330f729Sjoerg        reporterSelections = '\n'.join(reporterSelections)
5637330f729Sjoerg        reporterOptionsDivs = '\n'.join(reporterOptions)
5647330f729Sjoerg        reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters]))
5657330f729Sjoerg
5667330f729Sjoerg        if c.files:
5677330f729Sjoerg            fieldSize = min(5, len(c.files))
5687330f729Sjoerg            attachFileOptions = '\n'.join(["""\
5697330f729Sjoerg<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
5707330f729Sjoerg            attachFileRow = """\
5717330f729Sjoerg<tr>
5727330f729Sjoerg  <td class="form_label">Attach:</td>
5737330f729Sjoerg  <td class="form_value">
5747330f729Sjoerg<select style="width:100%%" name="files" multiple size=%d>
5757330f729Sjoerg%s
5767330f729Sjoerg</select>
5777330f729Sjoerg  </td>
5787330f729Sjoerg</tr>
5797330f729Sjoerg""" % (min(5, len(c.files)), attachFileOptions)
5807330f729Sjoerg        else:
5817330f729Sjoerg            attachFileRow = ""
5827330f729Sjoerg
5837330f729Sjoerg        result = """<html>
5847330f729Sjoerg<head>
5857330f729Sjoerg  <title>File Bug</title>
5867330f729Sjoerg  <link rel="stylesheet" type="text/css" href="/scanview.css" />
5877330f729Sjoerg</head>
5887330f729Sjoerg<script language="javascript" type="text/javascript">
5897330f729Sjoergvar reporters = %(reportersArray)s;
5907330f729Sjoergfunction updateReporterOptions() {
5917330f729Sjoerg  index = document.getElementById('reporter').selectedIndex;
5927330f729Sjoerg  for (var i=0; i < reporters.length; ++i) {
5937330f729Sjoerg    o = document.getElementById(reporters[i] + "ReporterOptions");
5947330f729Sjoerg    if (i == index) {
5957330f729Sjoerg      o.style.display = "";
5967330f729Sjoerg    } else {
5977330f729Sjoerg      o.style.display = "none";
5987330f729Sjoerg    }
5997330f729Sjoerg  }
6007330f729Sjoerg}
6017330f729Sjoerg</script>
6027330f729Sjoerg<body onLoad="updateReporterOptions()">
6037330f729Sjoerg<h3>
6047330f729Sjoerg<a href="/">Summary</a> >
6057330f729Sjoerg%(reportingFor)s
6067330f729SjoergFile Bug</h3>
6077330f729Sjoerg<form name="form" action="/report_submit" method="post">
6087330f729Sjoerg<input type="hidden" name="report" value="%(report)s">
6097330f729Sjoerg
6107330f729Sjoerg<table class="form">
6117330f729Sjoerg<tr><td>
6127330f729Sjoerg<table class="form_group">
6137330f729Sjoerg<tr>
6147330f729Sjoerg  <td class="form_clabel">Title:</td>
6157330f729Sjoerg  <td class="form_value">
6167330f729Sjoerg    <input type="text" name="title" size="50" value="%(title)s">
6177330f729Sjoerg  </td>
6187330f729Sjoerg</tr>
6197330f729Sjoerg<tr>
6207330f729Sjoerg  <td class="form_label">Description:</td>
6217330f729Sjoerg  <td class="form_value">
6227330f729Sjoerg<textarea rows="10" cols="80" name="description">
6237330f729Sjoerg%(description)s
6247330f729Sjoerg</textarea>
6257330f729Sjoerg  </td>
6267330f729Sjoerg</tr>
6277330f729Sjoerg
6287330f729Sjoerg%(attachFileRow)s
6297330f729Sjoerg
6307330f729Sjoerg</table>
6317330f729Sjoerg<br>
6327330f729Sjoerg<table class="form_group">
6337330f729Sjoerg<tr>
6347330f729Sjoerg  <td class="form_clabel">Method:</td>
6357330f729Sjoerg  <td class="form_value">
6367330f729Sjoerg    <select id="reporter" name="reporter" onChange="updateReporterOptions()">
6377330f729Sjoerg    %(reporterSelections)s
6387330f729Sjoerg    </select>
6397330f729Sjoerg  </td>
6407330f729Sjoerg</tr>
6417330f729Sjoerg%(reporterOptionsDivs)s
6427330f729Sjoerg</table>
6437330f729Sjoerg<br>
6447330f729Sjoerg</td></tr>
6457330f729Sjoerg<tr><td class="form_submit">
6467330f729Sjoerg  <input align="right" type="submit" name="Submit" value="Submit">
6477330f729Sjoerg</td></tr>
6487330f729Sjoerg</table>
6497330f729Sjoerg</form>
6507330f729Sjoerg
6517330f729Sjoerg%(extraIFrame)s
6527330f729Sjoerg
6537330f729Sjoerg</body>
6547330f729Sjoerg</html>"""%locals()
6557330f729Sjoerg
6567330f729Sjoerg        return self.send_string(result)
6577330f729Sjoerg
6587330f729Sjoerg    def send_head(self, fields=None):
6597330f729Sjoerg        if (self.server.options.onlyServeLocal and
6607330f729Sjoerg            self.client_address[0] != '127.0.0.1'):
6617330f729Sjoerg            return self.send_error(401, 'Unauthorized host.')
6627330f729Sjoerg
6637330f729Sjoerg        if fields is None:
6647330f729Sjoerg            fields = {}
6657330f729Sjoerg        self.fields = fields
6667330f729Sjoerg
6677330f729Sjoerg        o = urlparse(self.path)
6687330f729Sjoerg        self.fields = parse_query(o.query, fields)
6697330f729Sjoerg        path = posixpath.normpath(unquote(o.path))
6707330f729Sjoerg
6717330f729Sjoerg        # Split the components and strip the root prefix.
6727330f729Sjoerg        components = path.split('/')[1:]
6737330f729Sjoerg
6747330f729Sjoerg        # Special case some top-level entries.
6757330f729Sjoerg        if components:
6767330f729Sjoerg            name = components[0]
6777330f729Sjoerg            if len(components)==2:
6787330f729Sjoerg                if name=='report':
6797330f729Sjoerg                    return self.send_report(components[1])
6807330f729Sjoerg                elif name=='open':
6817330f729Sjoerg                    return self.send_open_report(components[1])
6827330f729Sjoerg            elif len(components)==1:
6837330f729Sjoerg                if name=='quit':
6847330f729Sjoerg                    self.server.halt()
6857330f729Sjoerg                    return self.send_string('Goodbye.', 'text/plain')
6867330f729Sjoerg                elif name=='report_submit':
6877330f729Sjoerg                    return self.send_report_submit()
6887330f729Sjoerg                elif name=='report_crashes':
6897330f729Sjoerg                    overrides = { 'ScanView' : {},
6907330f729Sjoerg                                  'Radar' : {},
6917330f729Sjoerg                                  'Email' : {} }
6927330f729Sjoerg                    for i,r in enumerate(self.server.reporters):
6937330f729Sjoerg                        if r.getName() == 'Radar':
6947330f729Sjoerg                            overrides['ScanView']['reporter'] = i
6957330f729Sjoerg                            break
6967330f729Sjoerg                    overrides['Radar']['Component'] = 'llvm - checker'
6977330f729Sjoerg                    overrides['Radar']['Component Version'] = 'X'
6987330f729Sjoerg                    return self.send_report(None, overrides)
6997330f729Sjoerg                elif name=='favicon.ico':
7007330f729Sjoerg                    return self.send_path(posixpath.join(kShare,'bugcatcher.ico'))
7017330f729Sjoerg
7027330f729Sjoerg        # Match directory entries.
7037330f729Sjoerg        if components[-1] == '':
7047330f729Sjoerg            components[-1] = 'index.html'
7057330f729Sjoerg
7067330f729Sjoerg        relpath = '/'.join(components)
7077330f729Sjoerg        path = posixpath.join(self.server.root, relpath)
7087330f729Sjoerg
7097330f729Sjoerg        if self.server.options.debug > 1:
7107330f729Sjoerg            print('%s: SERVER: sending path "%s"'%(sys.argv[0],
7117330f729Sjoerg                                                                 path), file=sys.stderr)
7127330f729Sjoerg        return self.send_path(path)
7137330f729Sjoerg
7147330f729Sjoerg    def send_404(self):
7157330f729Sjoerg        self.send_error(404, "File not found")
7167330f729Sjoerg        return None
7177330f729Sjoerg
7187330f729Sjoerg    def send_path(self, path):
7197330f729Sjoerg        # If the requested path is outside the root directory, do not open it
7207330f729Sjoerg        rel = os.path.abspath(path)
7217330f729Sjoerg        if not rel.startswith(os.path.abspath(self.server.root)):
7227330f729Sjoerg          return self.send_404()
7237330f729Sjoerg
7247330f729Sjoerg        ctype = self.guess_type(path)
7257330f729Sjoerg        if ctype.startswith('text/'):
7267330f729Sjoerg            # Patch file instead
7277330f729Sjoerg            return self.send_patched_file(path, ctype)
7287330f729Sjoerg        else:
7297330f729Sjoerg            mode = 'rb'
7307330f729Sjoerg        try:
7317330f729Sjoerg            f = open(path, mode)
7327330f729Sjoerg        except IOError:
7337330f729Sjoerg            return self.send_404()
7347330f729Sjoerg        return self.send_file(f, ctype)
7357330f729Sjoerg
7367330f729Sjoerg    def send_file(self, f, ctype):
7377330f729Sjoerg        # Patch files to add links, but skip binary files.
7387330f729Sjoerg        self.send_response(200)
7397330f729Sjoerg        self.send_header("Content-type", ctype)
7407330f729Sjoerg        fs = os.fstat(f.fileno())
7417330f729Sjoerg        self.send_header("Content-Length", str(fs[6]))
7427330f729Sjoerg        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
7437330f729Sjoerg        self.end_headers()
7447330f729Sjoerg        return f
7457330f729Sjoerg
7467330f729Sjoerg    def send_string(self, s, ctype='text/html', headers=True, mtime=None):
747*e038c9c4Sjoerg        encoded_s = s.encode('utf-8')
7487330f729Sjoerg        if headers:
7497330f729Sjoerg            self.send_response(200)
7507330f729Sjoerg            self.send_header("Content-type", ctype)
7517330f729Sjoerg            self.send_header("Content-Length", str(len(encoded_s)))
7527330f729Sjoerg            if mtime is None:
7537330f729Sjoerg                mtime = self.dynamic_mtime
7547330f729Sjoerg            self.send_header("Last-Modified", self.date_time_string(mtime))
7557330f729Sjoerg            self.end_headers()
7567330f729Sjoerg        return BytesIO(encoded_s)
7577330f729Sjoerg
7587330f729Sjoerg    def send_patched_file(self, path, ctype):
7597330f729Sjoerg        # Allow a very limited set of variables. This is pretty gross.
7607330f729Sjoerg        variables = {}
7617330f729Sjoerg        variables['report'] = ''
7627330f729Sjoerg        m = kReportFileRE.match(path)
7637330f729Sjoerg        if m:
7647330f729Sjoerg            variables['report'] = m.group(2)
7657330f729Sjoerg
7667330f729Sjoerg        try:
7677330f729Sjoerg            f = open(path,'rb')
7687330f729Sjoerg        except IOError:
7697330f729Sjoerg            return self.send_404()
7707330f729Sjoerg        fs = os.fstat(f.fileno())
7717330f729Sjoerg        data = f.read().decode('utf-8')
7727330f729Sjoerg        for a,b in kReportReplacements:
7737330f729Sjoerg            data = a.sub(b % variables, data)
7747330f729Sjoerg        return self.send_string(data, ctype, mtime=fs.st_mtime)
7757330f729Sjoerg
7767330f729Sjoerg
7777330f729Sjoergdef create_server(address, options, root):
7787330f729Sjoerg    import Reporter
7797330f729Sjoerg
7807330f729Sjoerg    reporters = Reporter.getReporters()
7817330f729Sjoerg
7827330f729Sjoerg    return ScanViewServer(address, ScanViewRequestHandler,
7837330f729Sjoerg                          root,
7847330f729Sjoerg                          reporters,
7857330f729Sjoerg                          options)
786