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