blob: da30f3618747147328f2ad6734e3af6c275a2f2e [file] [log] [blame]
Serge Gueltonc0ebe772018-12-18 08:36:33 +00001from __future__ import print_function
Serge Gueltondc7d7e32018-12-03 20:26:51 +00002try:
3 from http.server import HTTPServer, SimpleHTTPRequestHandler
4except ImportError:
5 from BaseHTTPServer import HTTPServer
6 from SimpleHTTPServer import SimpleHTTPRequestHandler
Daniel Dunbard4c23372008-09-19 23:32:11 +00007import os
8import sys
Serge Gueltonc177c3a2018-12-19 13:46:13 +00009try:
10 from urlparse import urlparse
11 from urllib import unquote
12except ImportError:
13 from urllib.parse import urlparse, unquote
14
Daniel Dunbard4c23372008-09-19 23:32:11 +000015import posixpath
16import StringIO
17import re
18import shutil
19import threading
20import time
21import socket
Daniel Dunbar0dbad462008-09-25 06:05:31 +000022import itertools
Daniel Dunbard4c23372008-09-19 23:32:11 +000023
Daniel Dunbar025b48d2008-09-20 01:43:16 +000024import Reporter
Serge Guelton73cf7522018-12-18 08:25:25 +000025try:
26 import configparser
27except ImportError:
28 import ConfigParser as configparser
Daniel Dunbard4c23372008-09-19 23:32:11 +000029
Daniel Dunbarfd462af2008-09-22 21:43:43 +000030###
31# Various patterns matched or replaced by server.
32
33kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
Daniel Dunbard4c23372008-09-19 23:32:11 +000034
Daniel Dunbard4c23372008-09-19 23:32:11 +000035kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
36
Daniel Dunbar0dbad462008-09-25 06:05:31 +000037# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
38
39kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
40kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
41
Daniel Dunbard3b096b2008-09-22 18:44:46 +000042kReportReplacements = []
Daniel Dunbardaa26f82008-09-21 20:34:58 +000043
Daniel Dunbard3b096b2008-09-22 18:44:46 +000044# Add custom javascript.
45kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
46<script language="javascript" type="text/javascript">
47function load(url) {
48 if (window.XMLHttpRequest) {
49 req = new XMLHttpRequest();
50 } else if (window.ActiveXObject) {
51 req = new ActiveXObject("Microsoft.XMLHTTP");
52 }
53 if (req != undefined) {
54 req.open("GET", url, true);
55 req.send("");
56 }
57}
58</script>"""))
59
60# Insert additional columns.
61kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
62 '<td></td><td></td>'))
63
64# Insert report bug and open file links.
65kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
66 ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
67 '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
68
Daniel Dunbarfd462af2008-09-22 21:43:43 +000069kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
70 '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
71
72kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
73 '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
74
Daniel Dunbar0dbad462008-09-25 06:05:31 +000075# Insert report crashes link.
76
Daniel Dunbar8e519d02008-09-25 19:59:17 +000077# Disabled for the time being until we decide exactly when this should
78# be enabled. Also the radar reporter needs to be fixed to report
79# multiple files.
80
81#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
82# '<br>These files will automatically be attached to ' +
83# 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
Daniel Dunbar0dbad462008-09-25 06:05:31 +000084
Daniel Dunbard3b096b2008-09-22 18:44:46 +000085###
Daniel Dunbar19af4ea2008-09-21 23:02:25 +000086# Other simple parameters
87
Jonathan Roelofs5e205182015-11-13 00:25:04 +000088kShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view')
Daniel Dunbar34525a92008-09-22 02:27:45 +000089kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbar19af4ea2008-09-21 23:02:25 +000090
Daniel Dunbard4c23372008-09-19 23:32:11 +000091###
92
93__version__ = "0.1"
94
95__all__ = ["create_server"]
96
97class ReporterThread(threading.Thread):
98 def __init__(self, report, reporter, parameters, server):
99 threading.Thread.__init__(self)
100 self.report = report
101 self.server = server
102 self.reporter = reporter
103 self.parameters = parameters
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000104 self.success = False
Daniel Dunbard4c23372008-09-19 23:32:11 +0000105 self.status = None
106
107 def run(self):
108 result = None
109 try:
110 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000111 print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000112 self.status = self.reporter.fileReport(self.report, self.parameters)
113 self.success = True
Daniel Dunbard4c23372008-09-19 23:32:11 +0000114 time.sleep(3)
115 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000116 print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr)
Serge Guelton3de41082018-12-03 12:11:21 +0000117 except Reporter.ReportFailure as e:
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000118 self.status = e.value
Serge Guelton3de41082018-12-03 12:11:21 +0000119 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000120 s = StringIO.StringIO()
121 import traceback
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000122 print('<b>Unhandled Exception</b><br><pre>', file=s)
123 traceback.print_exc(file=s)
124 print('</pre>', file=s)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000125 self.status = s.getvalue()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000126
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000127class ScanViewServer(HTTPServer):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000128 def __init__(self, address, handler, root, reporters, options):
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000129 HTTPServer.__init__(self, address, handler)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000130 self.root = root
131 self.reporters = reporters
132 self.options = options
133 self.halted = False
Daniel Dunbar34525a92008-09-22 02:27:45 +0000134 self.config = None
135 self.load_config()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000136
Daniel Dunbar34525a92008-09-22 02:27:45 +0000137 def load_config(self):
Serge Guelton73cf7522018-12-18 08:25:25 +0000138 self.config = configparser.RawConfigParser()
Daniel Dunbar34525a92008-09-22 02:27:45 +0000139
140 # Add defaults
141 self.config.add_section('ScanView')
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000142 for r in self.reporters:
Daniel Dunbar34525a92008-09-22 02:27:45 +0000143 self.config.add_section(r.getName())
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000144 for p in r.getParameters():
Ted Kremenekfef3c402008-09-30 16:11:33 +0000145 if p.saveConfigValue():
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000146 self.config.set(r.getName(), p.getName(), '')
Daniel Dunbar34525a92008-09-22 02:27:45 +0000147
148 # Ignore parse errors
149 try:
150 self.config.read([kConfigPath])
151 except:
152 pass
153
154 # Save on exit
155 import atexit
156 atexit.register(lambda: self.save_config())
157
158 def save_config(self):
159 # Ignore errors (only called on exit).
160 try:
161 f = open(kConfigPath,'w')
162 self.config.write(f)
163 f.close()
164 except:
165 pass
166
Daniel Dunbard4c23372008-09-19 23:32:11 +0000167 def halt(self):
168 self.halted = True
169 if self.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000170 print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000171
172 def serve_forever(self):
173 while not self.halted:
174 if self.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000175 print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000176 try:
177 self.handle_request()
Serge Guelton3de41082018-12-03 12:11:21 +0000178 except OSError as e:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000179 print('OSError',e.errno)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000180
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000181 def finish_request(self, request, client_address):
182 if self.options.autoReload:
183 import ScanView
184 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000185 HTTPServer.finish_request(self, request, client_address)
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000186
Daniel Dunbard4c23372008-09-19 23:32:11 +0000187 def handle_error(self, request, client_address):
188 # Ignore socket errors
189 info = sys.exc_info()
190 if info and isinstance(info[1], socket.error):
191 if self.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000192 print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000193 return
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000194 HTTPServer.handle_error(self, request, client_address)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000195
196# Borrowed from Quixote, with simplifications.
197def parse_query(qs, fields=None):
198 if fields is None:
199 fields = {}
Serge Gueltond4589742018-12-18 16:04:21 +0000200 for chunk in (_f for _f in qs.split('&') if _f):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000201 if '=' not in chunk:
202 name = chunk
203 value = ''
204 else:
205 name, value = chunk.split('=', 1)
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000206 name = unquote(name.replace('+', ' '))
207 value = unquote(value.replace('+', ' '))
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000208 item = fields.get(name)
209 if item is None:
210 fields[name] = [value]
211 else:
212 item.append(value)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000213 return fields
214
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000215class ScanViewRequestHandler(SimpleHTTPRequestHandler):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000216 server_version = "ScanViewServer/" + __version__
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000217 dynamic_mtime = time.time()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000218
219 def do_HEAD(self):
220 try:
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000221 SimpleHTTPRequestHandler.do_HEAD(self)
Serge Guelton3de41082018-12-03 12:11:21 +0000222 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000223 self.handle_exception(e)
224
225 def do_GET(self):
226 try:
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000227 SimpleHTTPRequestHandler.do_GET(self)
Serge Guelton3de41082018-12-03 12:11:21 +0000228 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000229 self.handle_exception(e)
230
231 def do_POST(self):
232 """Serve a POST request."""
233 try:
234 length = self.headers.getheader('content-length') or "0"
235 try:
236 length = int(length)
237 except:
238 length = 0
239 content = self.rfile.read(length)
240 fields = parse_query(content)
241 f = self.send_head(fields)
242 if f:
243 self.copyfile(f, self.wfile)
244 f.close()
Serge Guelton3de41082018-12-03 12:11:21 +0000245 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000246 self.handle_exception(e)
247
248 def log_message(self, format, *args):
249 if self.server.options.debug:
250 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
251 (sys.argv[0],
252 self.address_string(),
253 self.log_date_time_string(),
254 format%args))
255
256 def load_report(self, report):
257 path = os.path.join(self.server.root, 'report-%s.html'%report)
258 data = open(path).read()
259 keys = {}
260 for item in kBugKeyValueRE.finditer(data):
261 k,v = item.groups()
262 keys[k] = v
263 return keys
264
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000265 def load_crashes(self):
266 path = posixpath.join(self.server.root, 'index.html')
267 data = open(path).read()
268 problems = []
269 for item in kReportCrashEntryRE.finditer(data):
270 fieldData = item.group(1)
271 fields = dict([i.groups() for i in
272 kReportCrashEntryKeyValueRE.finditer(fieldData)])
273 problems.append(fields)
274 return problems
275
Daniel Dunbard4c23372008-09-19 23:32:11 +0000276 def handle_exception(self, exc):
277 import traceback
278 s = StringIO.StringIO()
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000279 print("INTERNAL ERROR\n", file=s)
280 traceback.print_exc(file=s)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000281 f = self.send_string(s.getvalue(), 'text/plain')
282 if f:
283 self.copyfile(f, self.wfile)
284 f.close()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000285
286 def get_scalar_field(self, name):
287 if name in self.fields:
288 return self.fields[name][0]
289 else:
290 return None
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000291
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000292 def submit_bug(self, c):
293 title = self.get_scalar_field('title')
294 description = self.get_scalar_field('description')
295 report = self.get_scalar_field('report')
296 reporterIndex = self.get_scalar_field('reporter')
297 files = []
298 for fileID in self.fields.get('files',[]):
299 try:
300 i = int(fileID)
301 except:
302 i = None
303 if i is None or i<0 or i>=len(c.files):
304 return (False, 'Invalid file ID')
305 files.append(c.files[i])
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000306
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000307 if not title:
308 return (False, "Missing title.")
309 if not description:
310 return (False, "Missing description.")
311 try:
Daniel Dunbar34525a92008-09-22 02:27:45 +0000312 reporterIndex = int(reporterIndex)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000313 except:
314 return (False, "Invalid report method.")
Daniel Dunbard4c23372008-09-19 23:32:11 +0000315
316 # Get the reporter and parameters.
Daniel Dunbar34525a92008-09-22 02:27:45 +0000317 reporter = self.server.reporters[reporterIndex]
Daniel Dunbard4c23372008-09-19 23:32:11 +0000318 parameters = {}
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000319 for o in reporter.getParameters():
320 name = '%s_%s'%(reporter.getName(),o.getName())
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000321 if name not in self.fields:
322 return (False,
323 'Missing field "%s" for %s report method.'%(name,
324 reporter.getName()))
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000325 parameters[o.getName()] = self.get_scalar_field(name)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000326
Daniel Dunbar34525a92008-09-22 02:27:45 +0000327 # Update config defaults.
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000328 if report != 'None':
329 self.server.config.set('ScanView', 'reporter', reporterIndex)
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000330 for o in reporter.getParameters():
Ted Kremenekfef3c402008-09-30 16:11:33 +0000331 if o.saveConfigValue():
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000332 name = o.getName()
333 self.server.config.set(reporter.getName(), name, parameters[name])
Daniel Dunbar34525a92008-09-22 02:27:45 +0000334
Daniel Dunbard4c23372008-09-19 23:32:11 +0000335 # Create the report.
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000336 bug = Reporter.BugReport(title, description, files)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000337
Daniel Dunbard4c23372008-09-19 23:32:11 +0000338 # Kick off a reporting thread.
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000339 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000340 t.start()
341
342 # Wait for thread to die...
343 while t.isAlive():
Daniel Dunbard4c23372008-09-19 23:32:11 +0000344 time.sleep(.25)
345 submitStatus = t.status
346
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000347 return (t.success, t.status)
348
349 def send_report_submit(self):
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000350 report = self.get_scalar_field('report')
351 c = self.get_report_context(report)
352 if c.reportSource is None:
353 reportingFor = "Report Crashes > "
354 fileBug = """\
355<a href="/report_crashes">File Bug</a> > """%locals()
356 else:
357 reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
358 report)
359 fileBug = '<a href="/report/%s">File Bug</a> > ' % report
360 title = self.get_scalar_field('title')
361 description = self.get_scalar_field('description')
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000362
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000363 res,message = self.submit_bug(c)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000364
365 if res:
366 statusClass = 'SubmitOk'
367 statusName = 'Succeeded'
368 else:
369 statusClass = 'SubmitFail'
370 statusName = 'Failed'
371
372 result = """
373<head>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000374 <title>Bug Submission</title>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000375 <link rel="stylesheet" type="text/css" href="/scanview.css" />
376</head>
377<body>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000378<h3>
379<a href="/">Summary</a> >
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000380%(reportingFor)s
381%(fileBug)s
Daniel Dunbared4e3212008-09-22 03:08:32 +0000382Submit</h3>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000383<form name="form" action="">
384<table class="form">
385<tr><td>
386<table class="form_group">
387<tr>
388 <td class="form_clabel">Title:</td>
389 <td class="form_value">
390 <input type="text" name="title" size="50" value="%(title)s" disabled>
391 </td>
392</tr>
393<tr>
394 <td class="form_label">Description:</td>
395 <td class="form_value">
396<textarea rows="10" cols="80" name="description" disabled>
397%(description)s
398</textarea>
399 </td>
400</table>
401</td></tr>
402</table>
403</form>
404<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
405%(message)s
406<p>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000407<hr>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000408<a href="/">Return to Summary</a>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000409</body>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000410</html>"""%locals()
411 return self.send_string(result)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000412
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000413 def send_open_report(self, report):
414 try:
415 keys = self.load_report(report)
416 except IOError:
417 return self.send_error(400, 'Invalid report.')
418
419 file = keys.get('FILE')
420 if not file or not posixpath.exists(file):
421 return self.send_error(400, 'File does not exist: "%s"' % file)
422
423 import startfile
424 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000425 print('%s: SERVER: opening "%s"'%(sys.argv[0],
426 file), file=sys.stderr)
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000427
428 status = startfile.open(file)
429 if status:
430 res = 'Opened: "%s"' % file
431 else:
432 res = 'Open failed: "%s"' % file
433
434 return self.send_string(res, 'text/plain')
435
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000436 def get_report_context(self, report):
Serge Guelton09616bd2018-12-03 12:12:48 +0000437 class Context(object):
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000438 pass
439 if report is None or report == 'None':
440 data = self.load_crashes()
441 # Don't allow empty reports.
442 if not data:
Serge Guelton3de41082018-12-03 12:11:21 +0000443 raise ValueError('No crashes detected!')
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000444 c = Context()
445 c.title = 'clang static analyzer failures'
Daniel Dunbard4c23372008-09-19 23:32:11 +0000446
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000447 stderrSummary = ""
448 for item in data:
449 if 'stderr' in item:
450 path = posixpath.join(self.server.root, item['stderr'])
451 if os.path.exists(path):
452 lns = itertools.islice(open(path), 0, 10)
453 stderrSummary += '%s\n--\n%s' % (item.get('src',
454 '<unknown>'),
455 ''.join(lns))
456
457 c.description = """\
458The clang static analyzer failed on these inputs:
459%s
460
461STDERR Summary
462--------------
463%s
464""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
465 stderrSummary)
466 c.reportSource = None
467 c.navMarkup = "Report Crashes > "
468 c.files = []
469 for item in data:
470 c.files.append(item.get('src',''))
471 c.files.append(posixpath.join(self.server.root,
472 item.get('file','')))
473 c.files.append(posixpath.join(self.server.root,
474 item.get('clangfile','')))
475 c.files.append(posixpath.join(self.server.root,
476 item.get('stderr','')))
477 c.files.append(posixpath.join(self.server.root,
478 item.get('info','')))
479 # Just in case something failed, ignore files which don't
480 # exist.
481 c.files = [f for f in c.files
482 if os.path.exists(f) and os.path.isfile(f)]
483 else:
484 # Check that this is a valid report.
485 path = posixpath.join(self.server.root, 'report-%s.html' % report)
486 if not posixpath.exists(path):
Serge Guelton3de41082018-12-03 12:11:21 +0000487 raise ValueError('Invalid report ID')
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000488 keys = self.load_report(report)
489 c = Context()
490 c.title = keys.get('DESC','clang error (unrecognized')
491 c.description = """\
492Bug reported by the clang static analyzer.
Daniel Dunbard4c23372008-09-19 23:32:11 +0000493
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000494Description: %s
495File: %s
496Line: %s
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000497"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
498 c.reportSource = 'report-%s.html' % report
499 c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
500 report)
501
502 c.files = [path]
503 return c
504
505 def send_report(self, report, configOverrides=None):
506 def getConfigOption(section, field):
507 if (configOverrides is not None and
508 section in configOverrides and
509 field in configOverrides[section]):
510 return configOverrides[section][field]
511 return self.server.config.get(section, field)
512
513 # report is None is used for crashes
514 try:
515 c = self.get_report_context(report)
Serge Guelton3de41082018-12-03 12:11:21 +0000516 except ValueError as e:
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000517 return self.send_error(400, e.message)
518
519 title = c.title
520 description= c.description
521 reportingFor = c.navMarkup
522 if c.reportSource is None:
523 extraIFrame = ""
524 else:
525 extraIFrame = """\
526<iframe src="/%s" width="100%%" height="40%%"
527 scrolling="auto" frameborder="1">
528 <a href="/%s">View Bug Report</a>
529</iframe>""" % (c.reportSource, c.reportSource)
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000530
Daniel Dunbard4c23372008-09-19 23:32:11 +0000531 reporterSelections = []
532 reporterOptions = []
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000533
Daniel Dunbar34525a92008-09-22 02:27:45 +0000534 try:
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000535 active = int(getConfigOption('ScanView','reporter'))
Daniel Dunbar34525a92008-09-22 02:27:45 +0000536 except:
537 active = 0
Daniel Dunbard4c23372008-09-19 23:32:11 +0000538 for i,r in enumerate(self.server.reporters):
Daniel Dunbar34525a92008-09-22 02:27:45 +0000539 selected = (i == active)
540 if selected:
541 selectedStr = ' selected'
542 else:
543 selectedStr = ''
544 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000545 options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
Daniel Dunbar34525a92008-09-22 02:27:45 +0000546 display = ('none','')[selected]
Daniel Dunbar49351e72008-09-22 00:11:51 +0000547 reporterOptions.append("""\
548<tr id="%sReporterOptions" style="display:%s">
549 <td class="form_label">%s Options</td>
550 <td class="form_value">
551 <table class="form_inner_group">
552%s
553 </table>
554 </td>
555</tr>
556"""%(r.getName(),display,r.getName(),options))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000557 reporterSelections = '\n'.join(reporterSelections)
558 reporterOptionsDivs = '\n'.join(reporterOptions)
Serge Gueltonf606a5a2018-12-03 20:12:34 +0000559 reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters]))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000560
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000561 if c.files:
562 fieldSize = min(5, len(c.files))
563 attachFileOptions = '\n'.join(["""\
564<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
565 attachFileRow = """\
566<tr>
567 <td class="form_label">Attach:</td>
568 <td class="form_value">
569<select style="width:100%%" name="files" multiple size=%d>
570%s
571</select>
572 </td>
573</tr>
574""" % (min(5, len(c.files)), attachFileOptions)
575 else:
576 attachFileRow = ""
577
Daniel Dunbard4c23372008-09-19 23:32:11 +0000578 result = """<html>
579<head>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000580 <title>File Bug</title>
Daniel Dunbar49351e72008-09-22 00:11:51 +0000581 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbard4c23372008-09-19 23:32:11 +0000582</head>
583<script language="javascript" type="text/javascript">
584var reporters = %(reportersArray)s;
585function updateReporterOptions() {
586 index = document.getElementById('reporter').selectedIndex;
587 for (var i=0; i < reporters.length; ++i) {
588 o = document.getElementById(reporters[i] + "ReporterOptions");
589 if (i == index) {
Daniel Dunbar49351e72008-09-22 00:11:51 +0000590 o.style.display = "";
Daniel Dunbard4c23372008-09-19 23:32:11 +0000591 } else {
592 o.style.display = "none";
593 }
594 }
595}
596</script>
Daniel Dunbar8d139d32008-09-22 01:40:14 +0000597<body onLoad="updateReporterOptions()">
Daniel Dunbared4e3212008-09-22 03:08:32 +0000598<h3>
599<a href="/">Summary</a> >
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000600%(reportingFor)s
Daniel Dunbared4e3212008-09-22 03:08:32 +0000601File Bug</h3>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000602<form name="form" action="/report_submit" method="post">
Daniel Dunbar49351e72008-09-22 00:11:51 +0000603<input type="hidden" name="report" value="%(report)s">
604
605<table class="form">
606<tr><td>
607<table class="form_group">
608<tr>
609 <td class="form_clabel">Title:</td>
610 <td class="form_value">
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000611 <input type="text" name="title" size="50" value="%(title)s">
Daniel Dunbar49351e72008-09-22 00:11:51 +0000612 </td>
613</tr>
614<tr>
615 <td class="form_label">Description:</td>
616 <td class="form_value">
Daniel Dunbard4c23372008-09-19 23:32:11 +0000617<textarea rows="10" cols="80" name="description">
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000618%(description)s
Daniel Dunbar49351e72008-09-22 00:11:51 +0000619</textarea>
620 </td>
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000621</tr>
622
623%(attachFileRow)s
624
Daniel Dunbar49351e72008-09-22 00:11:51 +0000625</table>
626<br>
627<table class="form_group">
628<tr>
629 <td class="form_clabel">Method:</td>
630 <td class="form_value">
631 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
632 %(reporterSelections)s
633 </select>
634 </td>
635</tr>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000636%(reporterOptionsDivs)s
Daniel Dunbar49351e72008-09-22 00:11:51 +0000637</table>
638<br>
639</td></tr>
640<tr><td class="form_submit">
641 <input align="right" type="submit" name="Submit" value="Submit">
642</td></tr>
643</table>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000644</form>
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000645
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000646%(extraIFrame)s
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000647
Daniel Dunbard4c23372008-09-19 23:32:11 +0000648</body>
649</html>"""%locals()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000650
Daniel Dunbard4c23372008-09-19 23:32:11 +0000651 return self.send_string(result)
652
653 def send_head(self, fields=None):
Daniel Dunbard8d1fec2008-09-24 17:59:41 +0000654 if (self.server.options.onlyServeLocal and
655 self.client_address[0] != '127.0.0.1'):
Ted Kremenekbda17492011-02-21 19:26:48 +0000656 return self.send_error(401, 'Unauthorized host.')
Daniel Dunbard8d1fec2008-09-24 17:59:41 +0000657
Daniel Dunbard4c23372008-09-19 23:32:11 +0000658 if fields is None:
659 fields = {}
660 self.fields = fields
661
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000662 o = urlparse(self.path)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000663 self.fields = parse_query(o.query, fields)
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000664 path = posixpath.normpath(unquote(o.path))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000665
666 # Split the components and strip the root prefix.
667 components = path.split('/')[1:]
668
669 # Special case some top-level entries.
670 if components:
671 name = components[0]
Daniel Dunbar49351e72008-09-22 00:11:51 +0000672 if len(components)==2:
673 if name=='report':
Daniel Dunbard4c23372008-09-19 23:32:11 +0000674 return self.send_report(components[1])
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000675 elif name=='open':
676 return self.send_open_report(components[1])
Daniel Dunbar49351e72008-09-22 00:11:51 +0000677 elif len(components)==1:
678 if name=='quit':
679 self.server.halt()
680 return self.send_string('Goodbye.', 'text/plain')
681 elif name=='report_submit':
Daniel Dunbard4c23372008-09-19 23:32:11 +0000682 return self.send_report_submit()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000683 elif name=='report_crashes':
684 overrides = { 'ScanView' : {},
685 'Radar' : {},
686 'Email' : {} }
687 for i,r in enumerate(self.server.reporters):
688 if r.getName() == 'Radar':
689 overrides['ScanView']['reporter'] = i
690 break
691 overrides['Radar']['Component'] = 'llvm - checker'
692 overrides['Radar']['Component Version'] = 'X'
693 return self.send_report(None, overrides)
Daniel Dunbar49351e72008-09-22 00:11:51 +0000694 elif name=='favicon.ico':
Jonathan Roelofs5e205182015-11-13 00:25:04 +0000695 return self.send_path(posixpath.join(kShare,'bugcatcher.ico'))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000696
697 # Match directory entries.
698 if components[-1] == '':
699 components[-1] = 'index.html'
Daniel Dunbar19af4ea2008-09-21 23:02:25 +0000700
Jordan Rose63524b92013-04-29 17:23:06 +0000701 relpath = '/'.join(components)
702 path = posixpath.join(self.server.root, relpath)
Daniel Dunbar19af4ea2008-09-21 23:02:25 +0000703
Daniel Dunbard4c23372008-09-19 23:32:11 +0000704 if self.server.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000705 print('%s: SERVER: sending path "%s"'%(sys.argv[0],
706 path), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000707 return self.send_path(path)
708
709 def send_404(self):
710 self.send_error(404, "File not found")
711 return None
712
713 def send_path(self, path):
Ted Kremenek3073c582012-10-12 19:16:31 +0000714 # If the requested path is outside the root directory, do not open it
Jordan Rose63524b92013-04-29 17:23:06 +0000715 rel = os.path.abspath(path)
716 if not rel.startswith(os.path.abspath(self.server.root)):
Ted Kremenek3073c582012-10-12 19:16:31 +0000717 return self.send_404()
718
Daniel Dunbard4c23372008-09-19 23:32:11 +0000719 ctype = self.guess_type(path)
720 if ctype.startswith('text/'):
721 # Patch file instead
722 return self.send_patched_file(path, ctype)
723 else:
724 mode = 'rb'
725 try:
726 f = open(path, mode)
727 except IOError:
728 return self.send_404()
729 return self.send_file(f, ctype)
730
731 def send_file(self, f, ctype):
732 # Patch files to add links, but skip binary files.
733 self.send_response(200)
734 self.send_header("Content-type", ctype)
735 fs = os.fstat(f.fileno())
736 self.send_header("Content-Length", str(fs[6]))
737 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
738 self.end_headers()
739 return f
740
741 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
742 if headers:
743 self.send_response(200)
744 self.send_header("Content-type", ctype)
745 self.send_header("Content-Length", str(len(s)))
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000746 if mtime is None:
747 mtime = self.dynamic_mtime
748 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000749 self.end_headers()
750 return StringIO.StringIO(s)
751
752 def send_patched_file(self, path, ctype):
Daniel Dunbarfd462af2008-09-22 21:43:43 +0000753 # Allow a very limited set of variables. This is pretty gross.
754 variables = {}
755 variables['report'] = ''
756 m = kReportFileRE.match(path)
757 if m:
758 variables['report'] = m.group(2)
759
Daniel Dunbar8d139d32008-09-22 01:40:14 +0000760 try:
761 f = open(path,'r')
762 except IOError:
763 return self.send_404()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000764 fs = os.fstat(f.fileno())
765 data = f.read()
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000766 for a,b in kReportReplacements:
Daniel Dunbarfd462af2008-09-22 21:43:43 +0000767 data = a.sub(b % variables, data)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000768 return self.send_string(data, ctype, mtime=fs.st_mtime)
769
770
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000771def create_server(address, options, root):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000772 import Reporter
773
774 reporters = Reporter.getReporters()
775
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000776 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbard4c23372008-09-19 23:32:11 +0000777 root,
778 reporters,
779 options)