blob: 76a790dabcfa725aa20a9c64eed28a39186b07f9 [file] [log] [blame]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +00001import BaseHTTPServer
2import SimpleHTTPServer
3import os
4import sys
5import urllib, urlparse
6import posixpath
7import StringIO
8import re
9import shutil
10import threading
11import time
12import socket
Daniel Dunbarcd1a6462008-09-25 06:05:31 +000013import itertools
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000014
Daniel Dunbar61717b32008-09-20 01:43:16 +000015import Reporter
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000016import ConfigParser
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000017
Daniel Dunbarb84ec332008-09-22 21:43:43 +000018###
19# Various patterns matched or replaced by server.
20
21kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000022
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000023kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
24
Daniel Dunbarcd1a6462008-09-25 06:05:31 +000025# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
26
27kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
28kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
29
Daniel Dunbar6fd6bd02008-09-22 18:44:46 +000030kReportReplacements = []
Daniel Dunbar14cad832008-09-21 20:34:58 +000031
Daniel Dunbar6fd6bd02008-09-22 18:44:46 +000032# Add custom javascript.
33kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
34<script language="javascript" type="text/javascript">
35function load(url) {
36 if (window.XMLHttpRequest) {
37 req = new XMLHttpRequest();
38 } else if (window.ActiveXObject) {
39 req = new ActiveXObject("Microsoft.XMLHTTP");
40 }
41 if (req != undefined) {
42 req.open("GET", url, true);
43 req.send("");
44 }
45}
46</script>"""))
47
48# Insert additional columns.
49kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
50 '<td></td><td></td>'))
51
52# Insert report bug and open file links.
53kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
54 ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
55 '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
56
Daniel Dunbarb84ec332008-09-22 21:43:43 +000057kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
58 '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
59
60kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
61 '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
62
Daniel Dunbarcd1a6462008-09-25 06:05:31 +000063# Insert report crashes link.
64
Daniel Dunbar2e3ecfa2008-09-25 19:59:17 +000065# Disabled for the time being until we decide exactly when this should
66# be enabled. Also the radar reporter needs to be fixed to report
67# multiple files.
68
69#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
70# '<br>These files will automatically be attached to ' +
71# 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
Daniel Dunbarcd1a6462008-09-25 06:05:31 +000072
Daniel Dunbar6fd6bd02008-09-22 18:44:46 +000073###
Daniel Dunbar2b353482008-09-21 23:02:25 +000074# Other simple parameters
75
76kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar3c697dd2008-09-22 02:27:45 +000077kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbar2b353482008-09-21 23:02:25 +000078
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000079###
80
81__version__ = "0.1"
82
83__all__ = ["create_server"]
84
85class ReporterThread(threading.Thread):
86 def __init__(self, report, reporter, parameters, server):
87 threading.Thread.__init__(self)
88 self.report = report
89 self.server = server
90 self.reporter = reporter
91 self.parameters = parameters
Daniel Dunbar55c5aa42008-09-22 01:21:30 +000092 self.success = False
Daniel Dunbar2b0662c2008-09-19 23:32:11 +000093 self.status = None
94
95 def run(self):
96 result = None
97 try:
98 if self.server.options.debug:
99 print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000100 self.status = self.reporter.fileReport(self.report, self.parameters)
101 self.success = True
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000102 time.sleep(3)
103 if self.server.options.debug:
104 print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
Daniel Dunbar61717b32008-09-20 01:43:16 +0000105 except Reporter.ReportFailure,e:
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000106 self.status = e.value
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000107 except Exception,e:
108 s = StringIO.StringIO()
109 import traceback
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000110 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000111 traceback.print_exc(e,file=s)
112 print >>s,'</pre>'
113 self.status = s.getvalue()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000114
115class ScanViewServer(BaseHTTPServer.HTTPServer):
116 def __init__(self, address, handler, root, reporters, options):
117 BaseHTTPServer.HTTPServer.__init__(self, address, handler)
118 self.root = root
119 self.reporters = reporters
120 self.options = options
121 self.halted = False
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000122 self.config = None
123 self.load_config()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000124
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000125 def load_config(self):
126 self.config = ConfigParser.RawConfigParser()
127
128 # Add defaults
129 self.config.add_section('ScanView')
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000130 for r in self.reporters:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000131 self.config.add_section(r.getName())
132 for p in r.getParameterNames():
133 self.config.set(r.getName(), p, '')
134
135 # Ignore parse errors
136 try:
137 self.config.read([kConfigPath])
138 except:
139 pass
140
141 # Save on exit
142 import atexit
143 atexit.register(lambda: self.save_config())
144
145 def save_config(self):
146 # Ignore errors (only called on exit).
147 try:
148 f = open(kConfigPath,'w')
149 self.config.write(f)
150 f.close()
151 except:
152 pass
153
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000154 def halt(self):
155 self.halted = True
156 if self.options.debug:
157 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
158
159 def serve_forever(self):
160 while not self.halted:
161 if self.options.debug > 1:
162 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
163 try:
164 self.handle_request()
165 except OSError,e:
166 print 'OSError',e.errno
167
Daniel Dunbar14cad832008-09-21 20:34:58 +0000168 def finish_request(self, request, client_address):
169 if self.options.autoReload:
170 import ScanView
171 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
172 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
173
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000174 def handle_error(self, request, client_address):
175 # Ignore socket errors
176 info = sys.exc_info()
177 if info and isinstance(info[1], socket.error):
178 if self.options.debug > 1:
179 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
180 return
Daniel Dunbar14cad832008-09-21 20:34:58 +0000181 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000182
183# Borrowed from Quixote, with simplifications.
184def parse_query(qs, fields=None):
185 if fields is None:
186 fields = {}
187 for chunk in filter(None, qs.split('&')):
188 if '=' not in chunk:
189 name = chunk
190 value = ''
191 else:
192 name, value = chunk.split('=', 1)
193 name = urllib.unquote(name.replace('+', ' '))
194 value = urllib.unquote(value.replace('+', ' '))
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000195 item = fields.get(name)
196 if item is None:
197 fields[name] = [value]
198 else:
199 item.append(value)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000200 return fields
201
202class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
203 server_version = "ScanViewServer/" + __version__
Daniel Dunbar14cad832008-09-21 20:34:58 +0000204 dynamic_mtime = time.time()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000205
206 def do_HEAD(self):
207 try:
208 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
209 except Exception,e:
210 self.handle_exception(e)
211
212 def do_GET(self):
213 try:
214 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
215 except Exception,e:
216 self.handle_exception(e)
217
218 def do_POST(self):
219 """Serve a POST request."""
220 try:
221 length = self.headers.getheader('content-length') or "0"
222 try:
223 length = int(length)
224 except:
225 length = 0
226 content = self.rfile.read(length)
227 fields = parse_query(content)
228 f = self.send_head(fields)
229 if f:
230 self.copyfile(f, self.wfile)
231 f.close()
232 except Exception,e:
233 self.handle_exception(e)
234
235 def log_message(self, format, *args):
236 if self.server.options.debug:
237 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
238 (sys.argv[0],
239 self.address_string(),
240 self.log_date_time_string(),
241 format%args))
242
243 def load_report(self, report):
244 path = os.path.join(self.server.root, 'report-%s.html'%report)
245 data = open(path).read()
246 keys = {}
247 for item in kBugKeyValueRE.finditer(data):
248 k,v = item.groups()
249 keys[k] = v
250 return keys
251
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000252 def load_crashes(self):
253 path = posixpath.join(self.server.root, 'index.html')
254 data = open(path).read()
255 problems = []
256 for item in kReportCrashEntryRE.finditer(data):
257 fieldData = item.group(1)
258 fields = dict([i.groups() for i in
259 kReportCrashEntryKeyValueRE.finditer(fieldData)])
260 problems.append(fields)
261 return problems
262
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000263 def handle_exception(self, exc):
264 import traceback
265 s = StringIO.StringIO()
266 print >>s, "INTERNAL ERROR\n"
267 traceback.print_exc(exc, s)
268 f = self.send_string(s.getvalue(), 'text/plain')
269 if f:
270 self.copyfile(f, self.wfile)
271 f.close()
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000272
273 def get_scalar_field(self, name):
274 if name in self.fields:
275 return self.fields[name][0]
276 else:
277 return None
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000278
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000279 def submit_bug(self, c):
280 title = self.get_scalar_field('title')
281 description = self.get_scalar_field('description')
282 report = self.get_scalar_field('report')
283 reporterIndex = self.get_scalar_field('reporter')
284 files = []
285 for fileID in self.fields.get('files',[]):
286 try:
287 i = int(fileID)
288 except:
289 i = None
290 if i is None or i<0 or i>=len(c.files):
291 return (False, 'Invalid file ID')
292 files.append(c.files[i])
293 print files
294
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000295 if not title:
296 return (False, "Missing title.")
297 if not description:
298 return (False, "Missing description.")
299 try:
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000300 reporterIndex = int(reporterIndex)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000301 except:
302 return (False, "Invalid report method.")
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000303
304 # Get the reporter and parameters.
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000305 reporter = self.server.reporters[reporterIndex]
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000306 parameters = {}
307 for o in reporter.getParameterNames():
308 name = '%s_%s'%(reporter.getName(),o)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000309 if name not in self.fields:
310 return (False,
311 'Missing field "%s" for %s report method.'%(name,
312 reporter.getName()))
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000313 parameters[o] = self.get_scalar_field(name)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000314
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000315 # Update config defaults.
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000316 if report != 'None':
317 self.server.config.set('ScanView', 'reporter', reporterIndex)
318 for o in reporter.getParameterNames():
319 self.server.config.set(reporter.getName(), o, parameters[o])
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000320
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000321 # Create the report.
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000322 bug = Reporter.BugReport(title, description, files)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000323
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000324 # Kick off a reporting thread.
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000325 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000326 t.start()
327
328 # Wait for thread to die...
329 while t.isAlive():
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000330 time.sleep(.25)
331 submitStatus = t.status
332
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000333 return (t.success, t.status)
334
335 def send_report_submit(self):
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000336 print self.fields
337 report = self.get_scalar_field('report')
338 c = self.get_report_context(report)
339 if c.reportSource is None:
340 reportingFor = "Report Crashes > "
341 fileBug = """\
342<a href="/report_crashes">File Bug</a> > """%locals()
343 else:
344 reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
345 report)
346 fileBug = '<a href="/report/%s">File Bug</a> > ' % report
347 title = self.get_scalar_field('title')
348 description = self.get_scalar_field('description')
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000349
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000350 res,message = self.submit_bug(c)
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000351
352 if res:
353 statusClass = 'SubmitOk'
354 statusName = 'Succeeded'
355 else:
356 statusClass = 'SubmitFail'
357 statusName = 'Failed'
358
359 result = """
360<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000361 <title>Bug Submission</title>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000362 <link rel="stylesheet" type="text/css" href="/scanview.css" />
363</head>
364<body>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000365<h3>
366<a href="/">Summary</a> >
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000367%(reportingFor)s
368%(fileBug)s
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000369Submit</h3>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000370<form name="form" action="">
371<table class="form">
372<tr><td>
373<table class="form_group">
374<tr>
375 <td class="form_clabel">Title:</td>
376 <td class="form_value">
377 <input type="text" name="title" size="50" value="%(title)s" disabled>
378 </td>
379</tr>
380<tr>
381 <td class="form_label">Description:</td>
382 <td class="form_value">
383<textarea rows="10" cols="80" name="description" disabled>
384%(description)s
385</textarea>
386 </td>
387</table>
388</td></tr>
389</table>
390</form>
391<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
392%(message)s
393<p>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000394<hr>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000395<a href="/">Return to Summary</a>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000396</body>
Daniel Dunbar55c5aa42008-09-22 01:21:30 +0000397</html>"""%locals()
398 return self.send_string(result)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000399
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000400 def send_open_report(self, report):
401 try:
402 keys = self.load_report(report)
403 except IOError:
404 return self.send_error(400, 'Invalid report.')
405
406 file = keys.get('FILE')
407 if not file or not posixpath.exists(file):
408 return self.send_error(400, 'File does not exist: "%s"' % file)
409
410 import startfile
411 if self.server.options.debug:
412 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
413 file)
414
415 status = startfile.open(file)
416 if status:
417 res = 'Opened: "%s"' % file
418 else:
419 res = 'Open failed: "%s"' % file
420
421 return self.send_string(res, 'text/plain')
422
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000423 def get_report_context(self, report):
424 class Context:
425 pass
426 if report is None or report == 'None':
427 data = self.load_crashes()
428 # Don't allow empty reports.
429 if not data:
430 raise ValueError, 'No crashes detected!'
431 c = Context()
432 c.title = 'clang static analyzer failures'
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000433
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000434 stderrSummary = ""
435 for item in data:
436 if 'stderr' in item:
437 path = posixpath.join(self.server.root, item['stderr'])
438 if os.path.exists(path):
439 lns = itertools.islice(open(path), 0, 10)
440 stderrSummary += '%s\n--\n%s' % (item.get('src',
441 '<unknown>'),
442 ''.join(lns))
443
444 c.description = """\
445The clang static analyzer failed on these inputs:
446%s
447
448STDERR Summary
449--------------
450%s
451""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
452 stderrSummary)
453 c.reportSource = None
454 c.navMarkup = "Report Crashes > "
455 c.files = []
456 for item in data:
457 c.files.append(item.get('src',''))
458 c.files.append(posixpath.join(self.server.root,
459 item.get('file','')))
460 c.files.append(posixpath.join(self.server.root,
461 item.get('clangfile','')))
462 c.files.append(posixpath.join(self.server.root,
463 item.get('stderr','')))
464 c.files.append(posixpath.join(self.server.root,
465 item.get('info','')))
466 # Just in case something failed, ignore files which don't
467 # exist.
468 c.files = [f for f in c.files
469 if os.path.exists(f) and os.path.isfile(f)]
470 else:
471 # Check that this is a valid report.
472 path = posixpath.join(self.server.root, 'report-%s.html' % report)
473 if not posixpath.exists(path):
474 raise ValueError, 'Invalid report ID'
475 keys = self.load_report(report)
476 c = Context()
477 c.title = keys.get('DESC','clang error (unrecognized')
478 c.description = """\
479Bug reported by the clang static analyzer.
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000480
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000481Description: %s
482File: %s
483Line: %s
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000484"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
485 c.reportSource = 'report-%s.html' % report
486 c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
487 report)
488
489 c.files = [path]
490 return c
491
492 def send_report(self, report, configOverrides=None):
493 def getConfigOption(section, field):
494 if (configOverrides is not None and
495 section in configOverrides and
496 field in configOverrides[section]):
497 return configOverrides[section][field]
498 return self.server.config.get(section, field)
499
500 # report is None is used for crashes
501 try:
502 c = self.get_report_context(report)
503 except ValueError, e:
504 return self.send_error(400, e.message)
505
506 title = c.title
507 description= c.description
508 reportingFor = c.navMarkup
509 if c.reportSource is None:
510 extraIFrame = ""
511 else:
512 extraIFrame = """\
513<iframe src="/%s" width="100%%" height="40%%"
514 scrolling="auto" frameborder="1">
515 <a href="/%s">View Bug Report</a>
516</iframe>""" % (c.reportSource, c.reportSource)
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000517
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000518 reporterSelections = []
519 reporterOptions = []
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000520
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000521 try:
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000522 active = int(getConfigOption('ScanView','reporter'))
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000523 except:
524 active = 0
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000525 for i,r in enumerate(self.server.reporters):
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000526 selected = (i == active)
527 if selected:
528 selectedStr = ' selected'
529 else:
530 selectedStr = ''
531 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Daniel Dunbar0174b282008-09-22 00:11:51 +0000532 options = '\n'.join(["""\
533<tr>
534 <td class="form_clabel">%s:</td>
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000535 <td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000536</tr>"""%(o,r.getName(),o,getConfigOption(r.getName(),o)) for o in r.getParameterNames()])
Daniel Dunbar3c697dd2008-09-22 02:27:45 +0000537 display = ('none','')[selected]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000538 reporterOptions.append("""\
539<tr id="%sReporterOptions" style="display:%s">
540 <td class="form_label">%s Options</td>
541 <td class="form_value">
542 <table class="form_inner_group">
543%s
544 </table>
545 </td>
546</tr>
547"""%(r.getName(),display,r.getName(),options))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000548 reporterSelections = '\n'.join(reporterSelections)
549 reporterOptionsDivs = '\n'.join(reporterOptions)
550 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
551
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000552 if c.files:
553 fieldSize = min(5, len(c.files))
554 attachFileOptions = '\n'.join(["""\
555<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
556 attachFileRow = """\
557<tr>
558 <td class="form_label">Attach:</td>
559 <td class="form_value">
560<select style="width:100%%" name="files" multiple size=%d>
561%s
562</select>
563 </td>
564</tr>
565""" % (min(5, len(c.files)), attachFileOptions)
566 else:
567 attachFileRow = ""
568
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000569 result = """<html>
570<head>
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000571 <title>File Bug</title>
Daniel Dunbar0174b282008-09-22 00:11:51 +0000572 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000573</head>
574<script language="javascript" type="text/javascript">
575var reporters = %(reportersArray)s;
576function updateReporterOptions() {
577 index = document.getElementById('reporter').selectedIndex;
578 for (var i=0; i < reporters.length; ++i) {
579 o = document.getElementById(reporters[i] + "ReporterOptions");
580 if (i == index) {
Daniel Dunbar0174b282008-09-22 00:11:51 +0000581 o.style.display = "";
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000582 } else {
583 o.style.display = "none";
584 }
585 }
586}
587</script>
Daniel Dunbarfe421482008-09-22 01:40:14 +0000588<body onLoad="updateReporterOptions()">
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000589<h3>
590<a href="/">Summary</a> >
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000591%(reportingFor)s
Daniel Dunbarb76387e2008-09-22 03:08:32 +0000592File Bug</h3>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000593<form name="form" action="/report_submit" method="post">
Daniel Dunbar0174b282008-09-22 00:11:51 +0000594<input type="hidden" name="report" value="%(report)s">
595
596<table class="form">
597<tr><td>
598<table class="form_group">
599<tr>
600 <td class="form_clabel">Title:</td>
601 <td class="form_value">
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000602 <input type="text" name="title" size="50" value="%(title)s">
Daniel Dunbar0174b282008-09-22 00:11:51 +0000603 </td>
604</tr>
605<tr>
606 <td class="form_label">Description:</td>
607 <td class="form_value">
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000608<textarea rows="10" cols="80" name="description">
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000609%(description)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000610</textarea>
611 </td>
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000612</tr>
613
614%(attachFileRow)s
615
Daniel Dunbar0174b282008-09-22 00:11:51 +0000616</table>
617<br>
618<table class="form_group">
619<tr>
620 <td class="form_clabel">Method:</td>
621 <td class="form_value">
622 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
623 %(reporterSelections)s
624 </select>
625 </td>
626</tr>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000627%(reporterOptionsDivs)s
Daniel Dunbar0174b282008-09-22 00:11:51 +0000628</table>
629<br>
630</td></tr>
631<tr><td class="form_submit">
632 <input align="right" type="submit" name="Submit" value="Submit">
633</td></tr>
634</table>
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000635</form>
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000636
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000637%(extraIFrame)s
Daniel Dunbar8b24edb2008-09-21 19:08:54 +0000638
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000639</body>
640</html>"""%locals()
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000641
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000642 return self.send_string(result)
643
644 def send_head(self, fields=None):
Daniel Dunbar09c46b32008-09-24 17:59:41 +0000645 if (self.server.options.onlyServeLocal and
646 self.client_address[0] != '127.0.0.1'):
647 return self.send_error('401', 'Unauthorized host.')
648
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000649 if fields is None:
650 fields = {}
651 self.fields = fields
652
653 o = urlparse.urlparse(self.path)
654 self.fields = parse_query(o.query, fields)
655 path = posixpath.normpath(urllib.unquote(o.path))
656
657 # Split the components and strip the root prefix.
658 components = path.split('/')[1:]
659
660 # Special case some top-level entries.
661 if components:
662 name = components[0]
Daniel Dunbar0174b282008-09-22 00:11:51 +0000663 if len(components)==2:
664 if name=='report':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000665 return self.send_report(components[1])
Daniel Dunbarf5d446f2008-09-22 18:05:49 +0000666 elif name=='open':
667 return self.send_open_report(components[1])
Daniel Dunbar0174b282008-09-22 00:11:51 +0000668 elif len(components)==1:
669 if name=='quit':
670 self.server.halt()
671 return self.send_string('Goodbye.', 'text/plain')
672 elif name=='report_submit':
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000673 return self.send_report_submit()
Daniel Dunbarcd1a6462008-09-25 06:05:31 +0000674 elif name=='report_crashes':
675 overrides = { 'ScanView' : {},
676 'Radar' : {},
677 'Email' : {} }
678 for i,r in enumerate(self.server.reporters):
679 if r.getName() == 'Radar':
680 overrides['ScanView']['reporter'] = i
681 break
682 overrides['Radar']['Component'] = 'llvm - checker'
683 overrides['Radar']['Component Version'] = 'X'
684 return self.send_report(None, overrides)
Daniel Dunbar0174b282008-09-22 00:11:51 +0000685 elif name=='favicon.ico':
Daniel Dunbar2b353482008-09-21 23:02:25 +0000686 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000687
688 # Match directory entries.
689 if components[-1] == '':
690 components[-1] = 'index.html'
Daniel Dunbar2b353482008-09-21 23:02:25 +0000691
692 suffix = '/'.join(components)
693
694 # The summary may reference source files on disk using rooted
695 # paths. Make sure these resolve correctly for now.
696 # FIXME: This isn't a very good idea... we should probably
697 # mark rooted paths somehow.
698 if os.path.exists(posixpath.join('/', suffix)):
699 path = posixpath.join('/', suffix)
700 else:
701 path = posixpath.join(self.server.root, suffix)
702
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000703 if self.server.options.debug > 1:
704 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
705 path)
706 return self.send_path(path)
707
708 def send_404(self):
709 self.send_error(404, "File not found")
710 return None
711
712 def send_path(self, path):
713 ctype = self.guess_type(path)
714 if ctype.startswith('text/'):
715 # Patch file instead
716 return self.send_patched_file(path, ctype)
717 else:
718 mode = 'rb'
719 try:
720 f = open(path, mode)
721 except IOError:
722 return self.send_404()
723 return self.send_file(f, ctype)
724
725 def send_file(self, f, ctype):
726 # Patch files to add links, but skip binary files.
727 self.send_response(200)
728 self.send_header("Content-type", ctype)
729 fs = os.fstat(f.fileno())
730 self.send_header("Content-Length", str(fs[6]))
731 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
732 self.end_headers()
733 return f
734
735 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
736 if headers:
737 self.send_response(200)
738 self.send_header("Content-type", ctype)
739 self.send_header("Content-Length", str(len(s)))
Daniel Dunbar14cad832008-09-21 20:34:58 +0000740 if mtime is None:
741 mtime = self.dynamic_mtime
742 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000743 self.end_headers()
744 return StringIO.StringIO(s)
745
746 def send_patched_file(self, path, ctype):
Daniel Dunbarb84ec332008-09-22 21:43:43 +0000747 # Allow a very limited set of variables. This is pretty gross.
748 variables = {}
749 variables['report'] = ''
750 m = kReportFileRE.match(path)
751 if m:
752 variables['report'] = m.group(2)
753
Daniel Dunbarfe421482008-09-22 01:40:14 +0000754 try:
755 f = open(path,'r')
756 except IOError:
757 return self.send_404()
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000758 fs = os.fstat(f.fileno())
759 data = f.read()
Daniel Dunbar14cad832008-09-21 20:34:58 +0000760 for a,b in kReportReplacements:
Daniel Dunbarb84ec332008-09-22 21:43:43 +0000761 data = a.sub(b % variables, data)
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000762 return self.send_string(data, ctype, mtime=fs.st_mtime)
763
764
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000765def create_server(address, options, root):
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000766 import Reporter
767
768 reporters = Reporter.getReporters()
769
Daniel Dunbar3266baa2008-09-22 02:53:12 +0000770 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbar2b0662c2008-09-19 23:32:11 +0000771 root,
772 reporters,
773 options)