blob: c40366b2e849057dd333d2efcac040c97f982a94 [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
Serge Gueltonf886c032019-01-03 14:26:56 +000016
17if sys.version_info.major >= 3:
18 from io import StringIO, BytesIO
19else:
20 from io import BytesIO, BytesIO as StringIO
21
Daniel Dunbard4c23372008-09-19 23:32:11 +000022import re
23import shutil
24import threading
25import time
26import socket
Daniel Dunbar0dbad462008-09-25 06:05:31 +000027import itertools
Daniel Dunbard4c23372008-09-19 23:32:11 +000028
Daniel Dunbar025b48d2008-09-20 01:43:16 +000029import Reporter
Serge Guelton73cf7522018-12-18 08:25:25 +000030try:
31 import configparser
32except ImportError:
33 import ConfigParser as configparser
Daniel Dunbard4c23372008-09-19 23:32:11 +000034
Daniel Dunbarfd462af2008-09-22 21:43:43 +000035###
36# Various patterns matched or replaced by server.
37
38kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
Daniel Dunbard4c23372008-09-19 23:32:11 +000039
Daniel Dunbard4c23372008-09-19 23:32:11 +000040kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
41
Daniel Dunbar0dbad462008-09-25 06:05:31 +000042# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
43
44kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
45kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
46
Daniel Dunbard3b096b2008-09-22 18:44:46 +000047kReportReplacements = []
Daniel Dunbardaa26f82008-09-21 20:34:58 +000048
Daniel Dunbard3b096b2008-09-22 18:44:46 +000049# Add custom javascript.
50kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
51<script language="javascript" type="text/javascript">
52function load(url) {
53 if (window.XMLHttpRequest) {
54 req = new XMLHttpRequest();
55 } else if (window.ActiveXObject) {
56 req = new ActiveXObject("Microsoft.XMLHTTP");
57 }
58 if (req != undefined) {
59 req.open("GET", url, true);
60 req.send("");
61 }
62}
63</script>"""))
64
65# Insert additional columns.
66kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
67 '<td></td><td></td>'))
68
69# Insert report bug and open file links.
70kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
71 ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
72 '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
73
Daniel Dunbarfd462af2008-09-22 21:43:43 +000074kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
75 '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
76
77kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
78 '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
79
Daniel Dunbar0dbad462008-09-25 06:05:31 +000080# Insert report crashes link.
81
Daniel Dunbar8e519d02008-09-25 19:59:17 +000082# Disabled for the time being until we decide exactly when this should
83# be enabled. Also the radar reporter needs to be fixed to report
84# multiple files.
85
86#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
87# '<br>These files will automatically be attached to ' +
88# 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
Daniel Dunbar0dbad462008-09-25 06:05:31 +000089
Daniel Dunbard3b096b2008-09-22 18:44:46 +000090###
Daniel Dunbar19af4ea2008-09-21 23:02:25 +000091# Other simple parameters
92
Jonathan Roelofs5e205182015-11-13 00:25:04 +000093kShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view')
Daniel Dunbar34525a92008-09-22 02:27:45 +000094kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbar19af4ea2008-09-21 23:02:25 +000095
Daniel Dunbard4c23372008-09-19 23:32:11 +000096###
97
98__version__ = "0.1"
99
100__all__ = ["create_server"]
101
102class ReporterThread(threading.Thread):
103 def __init__(self, report, reporter, parameters, server):
104 threading.Thread.__init__(self)
105 self.report = report
106 self.server = server
107 self.reporter = reporter
108 self.parameters = parameters
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000109 self.success = False
Daniel Dunbard4c23372008-09-19 23:32:11 +0000110 self.status = None
111
112 def run(self):
113 result = None
114 try:
115 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000116 print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000117 self.status = self.reporter.fileReport(self.report, self.parameters)
118 self.success = True
Daniel Dunbard4c23372008-09-19 23:32:11 +0000119 time.sleep(3)
120 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000121 print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr)
Serge Guelton3de41082018-12-03 12:11:21 +0000122 except Reporter.ReportFailure as e:
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000123 self.status = e.value
Serge Guelton3de41082018-12-03 12:11:21 +0000124 except Exception as e:
Serge Gueltonf886c032019-01-03 14:26:56 +0000125 s = StringIO()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000126 import traceback
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000127 print('<b>Unhandled Exception</b><br><pre>', file=s)
128 traceback.print_exc(file=s)
129 print('</pre>', file=s)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000130 self.status = s.getvalue()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000131
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000132class ScanViewServer(HTTPServer):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000133 def __init__(self, address, handler, root, reporters, options):
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000134 HTTPServer.__init__(self, address, handler)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000135 self.root = root
136 self.reporters = reporters
137 self.options = options
138 self.halted = False
Daniel Dunbar34525a92008-09-22 02:27:45 +0000139 self.config = None
140 self.load_config()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000141
Daniel Dunbar34525a92008-09-22 02:27:45 +0000142 def load_config(self):
Serge Guelton73cf7522018-12-18 08:25:25 +0000143 self.config = configparser.RawConfigParser()
Daniel Dunbar34525a92008-09-22 02:27:45 +0000144
145 # Add defaults
146 self.config.add_section('ScanView')
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000147 for r in self.reporters:
Daniel Dunbar34525a92008-09-22 02:27:45 +0000148 self.config.add_section(r.getName())
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000149 for p in r.getParameters():
Ted Kremenekfef3c402008-09-30 16:11:33 +0000150 if p.saveConfigValue():
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000151 self.config.set(r.getName(), p.getName(), '')
Daniel Dunbar34525a92008-09-22 02:27:45 +0000152
153 # Ignore parse errors
154 try:
155 self.config.read([kConfigPath])
156 except:
157 pass
158
159 # Save on exit
160 import atexit
161 atexit.register(lambda: self.save_config())
162
163 def save_config(self):
164 # Ignore errors (only called on exit).
165 try:
166 f = open(kConfigPath,'w')
167 self.config.write(f)
168 f.close()
169 except:
170 pass
171
Daniel Dunbard4c23372008-09-19 23:32:11 +0000172 def halt(self):
173 self.halted = True
174 if self.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000175 print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000176
177 def serve_forever(self):
178 while not self.halted:
179 if self.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000180 print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000181 try:
182 self.handle_request()
Serge Guelton3de41082018-12-03 12:11:21 +0000183 except OSError as e:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000184 print('OSError',e.errno)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000185
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000186 def finish_request(self, request, client_address):
187 if self.options.autoReload:
188 import ScanView
189 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000190 HTTPServer.finish_request(self, request, client_address)
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000191
Daniel Dunbard4c23372008-09-19 23:32:11 +0000192 def handle_error(self, request, client_address):
193 # Ignore socket errors
194 info = sys.exc_info()
195 if info and isinstance(info[1], socket.error):
196 if self.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000197 print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000198 return
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000199 HTTPServer.handle_error(self, request, client_address)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000200
201# Borrowed from Quixote, with simplifications.
202def parse_query(qs, fields=None):
203 if fields is None:
204 fields = {}
Serge Gueltond4589742018-12-18 16:04:21 +0000205 for chunk in (_f for _f in qs.split('&') if _f):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000206 if '=' not in chunk:
207 name = chunk
208 value = ''
209 else:
210 name, value = chunk.split('=', 1)
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000211 name = unquote(name.replace('+', ' '))
212 value = unquote(value.replace('+', ' '))
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000213 item = fields.get(name)
214 if item is None:
215 fields[name] = [value]
216 else:
217 item.append(value)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000218 return fields
219
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000220class ScanViewRequestHandler(SimpleHTTPRequestHandler):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000221 server_version = "ScanViewServer/" + __version__
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000222 dynamic_mtime = time.time()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000223
224 def do_HEAD(self):
225 try:
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000226 SimpleHTTPRequestHandler.do_HEAD(self)
Serge Guelton3de41082018-12-03 12:11:21 +0000227 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000228 self.handle_exception(e)
229
230 def do_GET(self):
231 try:
Serge Gueltondc7d7e32018-12-03 20:26:51 +0000232 SimpleHTTPRequestHandler.do_GET(self)
Serge Guelton3de41082018-12-03 12:11:21 +0000233 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000234 self.handle_exception(e)
235
236 def do_POST(self):
237 """Serve a POST request."""
238 try:
239 length = self.headers.getheader('content-length') or "0"
240 try:
241 length = int(length)
242 except:
243 length = 0
244 content = self.rfile.read(length)
245 fields = parse_query(content)
246 f = self.send_head(fields)
247 if f:
248 self.copyfile(f, self.wfile)
249 f.close()
Serge Guelton3de41082018-12-03 12:11:21 +0000250 except Exception as e:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000251 self.handle_exception(e)
252
253 def log_message(self, format, *args):
254 if self.server.options.debug:
255 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
256 (sys.argv[0],
257 self.address_string(),
258 self.log_date_time_string(),
259 format%args))
260
261 def load_report(self, report):
262 path = os.path.join(self.server.root, 'report-%s.html'%report)
263 data = open(path).read()
264 keys = {}
265 for item in kBugKeyValueRE.finditer(data):
266 k,v = item.groups()
267 keys[k] = v
268 return keys
269
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000270 def load_crashes(self):
271 path = posixpath.join(self.server.root, 'index.html')
272 data = open(path).read()
273 problems = []
274 for item in kReportCrashEntryRE.finditer(data):
275 fieldData = item.group(1)
276 fields = dict([i.groups() for i in
277 kReportCrashEntryKeyValueRE.finditer(fieldData)])
278 problems.append(fields)
279 return problems
280
Daniel Dunbard4c23372008-09-19 23:32:11 +0000281 def handle_exception(self, exc):
282 import traceback
Serge Gueltonf886c032019-01-03 14:26:56 +0000283 s = StringIO()
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000284 print("INTERNAL ERROR\n", file=s)
285 traceback.print_exc(file=s)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000286 f = self.send_string(s.getvalue(), 'text/plain')
287 if f:
288 self.copyfile(f, self.wfile)
289 f.close()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000290
291 def get_scalar_field(self, name):
292 if name in self.fields:
293 return self.fields[name][0]
294 else:
295 return None
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000296
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000297 def submit_bug(self, c):
298 title = self.get_scalar_field('title')
299 description = self.get_scalar_field('description')
300 report = self.get_scalar_field('report')
301 reporterIndex = self.get_scalar_field('reporter')
302 files = []
303 for fileID in self.fields.get('files',[]):
304 try:
305 i = int(fileID)
306 except:
307 i = None
308 if i is None or i<0 or i>=len(c.files):
309 return (False, 'Invalid file ID')
310 files.append(c.files[i])
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000311
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000312 if not title:
313 return (False, "Missing title.")
314 if not description:
315 return (False, "Missing description.")
316 try:
Daniel Dunbar34525a92008-09-22 02:27:45 +0000317 reporterIndex = int(reporterIndex)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000318 except:
319 return (False, "Invalid report method.")
Daniel Dunbard4c23372008-09-19 23:32:11 +0000320
321 # Get the reporter and parameters.
Daniel Dunbar34525a92008-09-22 02:27:45 +0000322 reporter = self.server.reporters[reporterIndex]
Daniel Dunbard4c23372008-09-19 23:32:11 +0000323 parameters = {}
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000324 for o in reporter.getParameters():
325 name = '%s_%s'%(reporter.getName(),o.getName())
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000326 if name not in self.fields:
327 return (False,
328 'Missing field "%s" for %s report method.'%(name,
329 reporter.getName()))
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000330 parameters[o.getName()] = self.get_scalar_field(name)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000331
Daniel Dunbar34525a92008-09-22 02:27:45 +0000332 # Update config defaults.
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000333 if report != 'None':
334 self.server.config.set('ScanView', 'reporter', reporterIndex)
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000335 for o in reporter.getParameters():
Ted Kremenekfef3c402008-09-30 16:11:33 +0000336 if o.saveConfigValue():
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000337 name = o.getName()
338 self.server.config.set(reporter.getName(), name, parameters[name])
Daniel Dunbar34525a92008-09-22 02:27:45 +0000339
Daniel Dunbard4c23372008-09-19 23:32:11 +0000340 # Create the report.
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000341 bug = Reporter.BugReport(title, description, files)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000342
Daniel Dunbard4c23372008-09-19 23:32:11 +0000343 # Kick off a reporting thread.
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000344 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000345 t.start()
346
347 # Wait for thread to die...
348 while t.isAlive():
Daniel Dunbard4c23372008-09-19 23:32:11 +0000349 time.sleep(.25)
350 submitStatus = t.status
351
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000352 return (t.success, t.status)
353
354 def send_report_submit(self):
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000355 report = self.get_scalar_field('report')
356 c = self.get_report_context(report)
357 if c.reportSource is None:
358 reportingFor = "Report Crashes > "
359 fileBug = """\
360<a href="/report_crashes">File Bug</a> > """%locals()
361 else:
362 reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
363 report)
364 fileBug = '<a href="/report/%s">File Bug</a> > ' % report
365 title = self.get_scalar_field('title')
366 description = self.get_scalar_field('description')
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000367
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000368 res,message = self.submit_bug(c)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000369
370 if res:
371 statusClass = 'SubmitOk'
372 statusName = 'Succeeded'
373 else:
374 statusClass = 'SubmitFail'
375 statusName = 'Failed'
376
377 result = """
378<head>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000379 <title>Bug Submission</title>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000380 <link rel="stylesheet" type="text/css" href="/scanview.css" />
381</head>
382<body>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000383<h3>
384<a href="/">Summary</a> >
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000385%(reportingFor)s
386%(fileBug)s
Daniel Dunbared4e3212008-09-22 03:08:32 +0000387Submit</h3>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000388<form name="form" action="">
389<table class="form">
390<tr><td>
391<table class="form_group">
392<tr>
393 <td class="form_clabel">Title:</td>
394 <td class="form_value">
395 <input type="text" name="title" size="50" value="%(title)s" disabled>
396 </td>
397</tr>
398<tr>
399 <td class="form_label">Description:</td>
400 <td class="form_value">
401<textarea rows="10" cols="80" name="description" disabled>
402%(description)s
403</textarea>
404 </td>
405</table>
406</td></tr>
407</table>
408</form>
409<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
410%(message)s
411<p>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000412<hr>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000413<a href="/">Return to Summary</a>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000414</body>
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000415</html>"""%locals()
416 return self.send_string(result)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000417
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000418 def send_open_report(self, report):
419 try:
420 keys = self.load_report(report)
421 except IOError:
422 return self.send_error(400, 'Invalid report.')
423
424 file = keys.get('FILE')
425 if not file or not posixpath.exists(file):
426 return self.send_error(400, 'File does not exist: "%s"' % file)
427
428 import startfile
429 if self.server.options.debug:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000430 print('%s: SERVER: opening "%s"'%(sys.argv[0],
431 file), file=sys.stderr)
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000432
433 status = startfile.open(file)
434 if status:
435 res = 'Opened: "%s"' % file
436 else:
437 res = 'Open failed: "%s"' % file
438
439 return self.send_string(res, 'text/plain')
440
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000441 def get_report_context(self, report):
Serge Guelton09616bd2018-12-03 12:12:48 +0000442 class Context(object):
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000443 pass
444 if report is None or report == 'None':
445 data = self.load_crashes()
446 # Don't allow empty reports.
447 if not data:
Serge Guelton3de41082018-12-03 12:11:21 +0000448 raise ValueError('No crashes detected!')
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000449 c = Context()
450 c.title = 'clang static analyzer failures'
Daniel Dunbard4c23372008-09-19 23:32:11 +0000451
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000452 stderrSummary = ""
453 for item in data:
454 if 'stderr' in item:
455 path = posixpath.join(self.server.root, item['stderr'])
456 if os.path.exists(path):
457 lns = itertools.islice(open(path), 0, 10)
458 stderrSummary += '%s\n--\n%s' % (item.get('src',
459 '<unknown>'),
460 ''.join(lns))
461
462 c.description = """\
463The clang static analyzer failed on these inputs:
464%s
465
466STDERR Summary
467--------------
468%s
469""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
470 stderrSummary)
471 c.reportSource = None
472 c.navMarkup = "Report Crashes > "
473 c.files = []
474 for item in data:
475 c.files.append(item.get('src',''))
476 c.files.append(posixpath.join(self.server.root,
477 item.get('file','')))
478 c.files.append(posixpath.join(self.server.root,
479 item.get('clangfile','')))
480 c.files.append(posixpath.join(self.server.root,
481 item.get('stderr','')))
482 c.files.append(posixpath.join(self.server.root,
483 item.get('info','')))
484 # Just in case something failed, ignore files which don't
485 # exist.
486 c.files = [f for f in c.files
487 if os.path.exists(f) and os.path.isfile(f)]
488 else:
489 # Check that this is a valid report.
490 path = posixpath.join(self.server.root, 'report-%s.html' % report)
491 if not posixpath.exists(path):
Serge Guelton3de41082018-12-03 12:11:21 +0000492 raise ValueError('Invalid report ID')
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000493 keys = self.load_report(report)
494 c = Context()
495 c.title = keys.get('DESC','clang error (unrecognized')
496 c.description = """\
497Bug reported by the clang static analyzer.
Daniel Dunbard4c23372008-09-19 23:32:11 +0000498
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000499Description: %s
500File: %s
501Line: %s
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000502"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
503 c.reportSource = 'report-%s.html' % report
504 c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
505 report)
506
507 c.files = [path]
508 return c
509
510 def send_report(self, report, configOverrides=None):
511 def getConfigOption(section, field):
512 if (configOverrides is not None and
513 section in configOverrides and
514 field in configOverrides[section]):
515 return configOverrides[section][field]
516 return self.server.config.get(section, field)
517
518 # report is None is used for crashes
519 try:
520 c = self.get_report_context(report)
Serge Guelton3de41082018-12-03 12:11:21 +0000521 except ValueError as e:
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000522 return self.send_error(400, e.message)
523
524 title = c.title
525 description= c.description
526 reportingFor = c.navMarkup
527 if c.reportSource is None:
528 extraIFrame = ""
529 else:
530 extraIFrame = """\
531<iframe src="/%s" width="100%%" height="40%%"
532 scrolling="auto" frameborder="1">
533 <a href="/%s">View Bug Report</a>
534</iframe>""" % (c.reportSource, c.reportSource)
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000535
Daniel Dunbard4c23372008-09-19 23:32:11 +0000536 reporterSelections = []
537 reporterOptions = []
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000538
Daniel Dunbar34525a92008-09-22 02:27:45 +0000539 try:
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000540 active = int(getConfigOption('ScanView','reporter'))
Daniel Dunbar34525a92008-09-22 02:27:45 +0000541 except:
542 active = 0
Daniel Dunbard4c23372008-09-19 23:32:11 +0000543 for i,r in enumerate(self.server.reporters):
Daniel Dunbar34525a92008-09-22 02:27:45 +0000544 selected = (i == active)
545 if selected:
546 selectedStr = ' selected'
547 else:
548 selectedStr = ''
549 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000550 options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
Daniel Dunbar34525a92008-09-22 02:27:45 +0000551 display = ('none','')[selected]
Daniel Dunbar49351e72008-09-22 00:11:51 +0000552 reporterOptions.append("""\
553<tr id="%sReporterOptions" style="display:%s">
554 <td class="form_label">%s Options</td>
555 <td class="form_value">
556 <table class="form_inner_group">
557%s
558 </table>
559 </td>
560</tr>
561"""%(r.getName(),display,r.getName(),options))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000562 reporterSelections = '\n'.join(reporterSelections)
563 reporterOptionsDivs = '\n'.join(reporterOptions)
Serge Gueltonf606a5a2018-12-03 20:12:34 +0000564 reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters]))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000565
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000566 if c.files:
567 fieldSize = min(5, len(c.files))
568 attachFileOptions = '\n'.join(["""\
569<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
570 attachFileRow = """\
571<tr>
572 <td class="form_label">Attach:</td>
573 <td class="form_value">
574<select style="width:100%%" name="files" multiple size=%d>
575%s
576</select>
577 </td>
578</tr>
579""" % (min(5, len(c.files)), attachFileOptions)
580 else:
581 attachFileRow = ""
582
Daniel Dunbard4c23372008-09-19 23:32:11 +0000583 result = """<html>
584<head>
Daniel Dunbared4e3212008-09-22 03:08:32 +0000585 <title>File Bug</title>
Daniel Dunbar49351e72008-09-22 00:11:51 +0000586 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbard4c23372008-09-19 23:32:11 +0000587</head>
588<script language="javascript" type="text/javascript">
589var reporters = %(reportersArray)s;
590function updateReporterOptions() {
591 index = document.getElementById('reporter').selectedIndex;
592 for (var i=0; i < reporters.length; ++i) {
593 o = document.getElementById(reporters[i] + "ReporterOptions");
594 if (i == index) {
Daniel Dunbar49351e72008-09-22 00:11:51 +0000595 o.style.display = "";
Daniel Dunbard4c23372008-09-19 23:32:11 +0000596 } else {
597 o.style.display = "none";
598 }
599 }
600}
601</script>
Daniel Dunbar8d139d32008-09-22 01:40:14 +0000602<body onLoad="updateReporterOptions()">
Daniel Dunbared4e3212008-09-22 03:08:32 +0000603<h3>
604<a href="/">Summary</a> >
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000605%(reportingFor)s
Daniel Dunbared4e3212008-09-22 03:08:32 +0000606File Bug</h3>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000607<form name="form" action="/report_submit" method="post">
Daniel Dunbar49351e72008-09-22 00:11:51 +0000608<input type="hidden" name="report" value="%(report)s">
609
610<table class="form">
611<tr><td>
612<table class="form_group">
613<tr>
614 <td class="form_clabel">Title:</td>
615 <td class="form_value">
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000616 <input type="text" name="title" size="50" value="%(title)s">
Daniel Dunbar49351e72008-09-22 00:11:51 +0000617 </td>
618</tr>
619<tr>
620 <td class="form_label">Description:</td>
621 <td class="form_value">
Daniel Dunbard4c23372008-09-19 23:32:11 +0000622<textarea rows="10" cols="80" name="description">
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000623%(description)s
Daniel Dunbar49351e72008-09-22 00:11:51 +0000624</textarea>
625 </td>
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000626</tr>
627
628%(attachFileRow)s
629
Daniel Dunbar49351e72008-09-22 00:11:51 +0000630</table>
631<br>
632<table class="form_group">
633<tr>
634 <td class="form_clabel">Method:</td>
635 <td class="form_value">
636 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
637 %(reporterSelections)s
638 </select>
639 </td>
640</tr>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000641%(reporterOptionsDivs)s
Daniel Dunbar49351e72008-09-22 00:11:51 +0000642</table>
643<br>
644</td></tr>
645<tr><td class="form_submit">
646 <input align="right" type="submit" name="Submit" value="Submit">
647</td></tr>
648</table>
Daniel Dunbard4c23372008-09-19 23:32:11 +0000649</form>
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000650
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000651%(extraIFrame)s
Daniel Dunbarb9c42102008-09-21 19:08:54 +0000652
Daniel Dunbard4c23372008-09-19 23:32:11 +0000653</body>
654</html>"""%locals()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000655
Daniel Dunbard4c23372008-09-19 23:32:11 +0000656 return self.send_string(result)
657
658 def send_head(self, fields=None):
Daniel Dunbard8d1fec2008-09-24 17:59:41 +0000659 if (self.server.options.onlyServeLocal and
660 self.client_address[0] != '127.0.0.1'):
Ted Kremenekbda17492011-02-21 19:26:48 +0000661 return self.send_error(401, 'Unauthorized host.')
Daniel Dunbard8d1fec2008-09-24 17:59:41 +0000662
Daniel Dunbard4c23372008-09-19 23:32:11 +0000663 if fields is None:
664 fields = {}
665 self.fields = fields
666
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000667 o = urlparse(self.path)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000668 self.fields = parse_query(o.query, fields)
Serge Gueltonc177c3a2018-12-19 13:46:13 +0000669 path = posixpath.normpath(unquote(o.path))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000670
671 # Split the components and strip the root prefix.
672 components = path.split('/')[1:]
673
674 # Special case some top-level entries.
675 if components:
676 name = components[0]
Daniel Dunbar49351e72008-09-22 00:11:51 +0000677 if len(components)==2:
678 if name=='report':
Daniel Dunbard4c23372008-09-19 23:32:11 +0000679 return self.send_report(components[1])
Daniel Dunbar6c9bf7d2008-09-22 18:05:49 +0000680 elif name=='open':
681 return self.send_open_report(components[1])
Daniel Dunbar49351e72008-09-22 00:11:51 +0000682 elif len(components)==1:
683 if name=='quit':
684 self.server.halt()
685 return self.send_string('Goodbye.', 'text/plain')
686 elif name=='report_submit':
Daniel Dunbard4c23372008-09-19 23:32:11 +0000687 return self.send_report_submit()
Daniel Dunbar0dbad462008-09-25 06:05:31 +0000688 elif name=='report_crashes':
689 overrides = { 'ScanView' : {},
690 'Radar' : {},
691 'Email' : {} }
692 for i,r in enumerate(self.server.reporters):
693 if r.getName() == 'Radar':
694 overrides['ScanView']['reporter'] = i
695 break
696 overrides['Radar']['Component'] = 'llvm - checker'
697 overrides['Radar']['Component Version'] = 'X'
698 return self.send_report(None, overrides)
Daniel Dunbar49351e72008-09-22 00:11:51 +0000699 elif name=='favicon.ico':
Jonathan Roelofs5e205182015-11-13 00:25:04 +0000700 return self.send_path(posixpath.join(kShare,'bugcatcher.ico'))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000701
702 # Match directory entries.
703 if components[-1] == '':
704 components[-1] = 'index.html'
Daniel Dunbar19af4ea2008-09-21 23:02:25 +0000705
Jordan Rose63524b92013-04-29 17:23:06 +0000706 relpath = '/'.join(components)
707 path = posixpath.join(self.server.root, relpath)
Daniel Dunbar19af4ea2008-09-21 23:02:25 +0000708
Daniel Dunbard4c23372008-09-19 23:32:11 +0000709 if self.server.options.debug > 1:
Serge Gueltonc0ebe772018-12-18 08:36:33 +0000710 print('%s: SERVER: sending path "%s"'%(sys.argv[0],
711 path), file=sys.stderr)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000712 return self.send_path(path)
713
714 def send_404(self):
715 self.send_error(404, "File not found")
716 return None
717
718 def send_path(self, path):
Ted Kremenek3073c582012-10-12 19:16:31 +0000719 # If the requested path is outside the root directory, do not open it
Jordan Rose63524b92013-04-29 17:23:06 +0000720 rel = os.path.abspath(path)
721 if not rel.startswith(os.path.abspath(self.server.root)):
Ted Kremenek3073c582012-10-12 19:16:31 +0000722 return self.send_404()
723
Daniel Dunbard4c23372008-09-19 23:32:11 +0000724 ctype = self.guess_type(path)
725 if ctype.startswith('text/'):
726 # Patch file instead
727 return self.send_patched_file(path, ctype)
728 else:
729 mode = 'rb'
730 try:
731 f = open(path, mode)
732 except IOError:
733 return self.send_404()
734 return self.send_file(f, ctype)
735
736 def send_file(self, f, ctype):
737 # Patch files to add links, but skip binary files.
738 self.send_response(200)
739 self.send_header("Content-type", ctype)
740 fs = os.fstat(f.fileno())
741 self.send_header("Content-Length", str(fs[6]))
742 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
743 self.end_headers()
744 return f
745
746 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
Serge Gueltonf886c032019-01-03 14:26:56 +0000747 encoded_s = s.encode()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000748 if headers:
749 self.send_response(200)
750 self.send_header("Content-type", ctype)
Serge Gueltonf886c032019-01-03 14:26:56 +0000751 self.send_header("Content-Length", str(len(encoded_s)))
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000752 if mtime is None:
753 mtime = self.dynamic_mtime
754 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbard4c23372008-09-19 23:32:11 +0000755 self.end_headers()
Serge Gueltonf886c032019-01-03 14:26:56 +0000756 return BytesIO(encoded_s)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000757
758 def send_patched_file(self, path, ctype):
Daniel Dunbarfd462af2008-09-22 21:43:43 +0000759 # Allow a very limited set of variables. This is pretty gross.
760 variables = {}
761 variables['report'] = ''
762 m = kReportFileRE.match(path)
763 if m:
764 variables['report'] = m.group(2)
765
Daniel Dunbar8d139d32008-09-22 01:40:14 +0000766 try:
767 f = open(path,'r')
768 except IOError:
769 return self.send_404()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000770 fs = os.fstat(f.fileno())
771 data = f.read()
Daniel Dunbardaa26f82008-09-21 20:34:58 +0000772 for a,b in kReportReplacements:
Daniel Dunbarfd462af2008-09-22 21:43:43 +0000773 data = a.sub(b % variables, data)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000774 return self.send_string(data, ctype, mtime=fs.st_mtime)
775
776
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000777def create_server(address, options, root):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000778 import Reporter
779
780 reporters = Reporter.getReporters()
781
Daniel Dunbar2ef31422008-09-22 02:53:12 +0000782 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbard4c23372008-09-19 23:32:11 +0000783 root,
784 reporters,
785 options)