blob: 98745eaa07913302752ca873b651eec6d6c3dd94 [file] [log] [blame]
Daniel Dunbare33d3682008-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 Dunbar7ebe0ed2008-09-25 06:05:31 +000013import itertools
Daniel Dunbare33d3682008-09-19 23:32:11 +000014
Daniel Dunbar54722492008-09-20 01:43:16 +000015import Reporter
Daniel Dunbar8dcd9402008-09-22 02:27:45 +000016import ConfigParser
Daniel Dunbare33d3682008-09-19 23:32:11 +000017
Daniel Dunbar17fded62008-09-22 21:43:43 +000018###
19# Various patterns matched or replaced by server.
20
21kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
Daniel Dunbare33d3682008-09-19 23:32:11 +000022
Daniel Dunbare33d3682008-09-19 23:32:11 +000023kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
24
Daniel Dunbar7ebe0ed2008-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 Dunbar4d20cf72008-09-22 18:44:46 +000030kReportReplacements = []
Daniel Dunbarcb028b02008-09-21 20:34:58 +000031
Daniel Dunbar4d20cf72008-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 Dunbar17fded62008-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 Dunbar7ebe0ed2008-09-25 06:05:31 +000063# Insert report crashes link.
64
65kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
66 '<br>These files will automatically be attached to ' +
67 'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
68
Daniel Dunbar4d20cf72008-09-22 18:44:46 +000069###
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000070# Other simple parameters
71
72kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar8dcd9402008-09-22 02:27:45 +000073kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000074
Daniel Dunbare33d3682008-09-19 23:32:11 +000075###
76
77__version__ = "0.1"
78
79__all__ = ["create_server"]
80
81class ReporterThread(threading.Thread):
82 def __init__(self, report, reporter, parameters, server):
83 threading.Thread.__init__(self)
84 self.report = report
85 self.server = server
86 self.reporter = reporter
87 self.parameters = parameters
Daniel Dunbar47cceff2008-09-22 01:21:30 +000088 self.success = False
Daniel Dunbare33d3682008-09-19 23:32:11 +000089 self.status = None
90
91 def run(self):
92 result = None
93 try:
94 if self.server.options.debug:
95 print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
Daniel Dunbar47cceff2008-09-22 01:21:30 +000096 self.status = self.reporter.fileReport(self.report, self.parameters)
97 self.success = True
Daniel Dunbare33d3682008-09-19 23:32:11 +000098 time.sleep(3)
99 if self.server.options.debug:
100 print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
Daniel Dunbar54722492008-09-20 01:43:16 +0000101 except Reporter.ReportFailure,e:
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000102 self.status = e.value
Daniel Dunbare33d3682008-09-19 23:32:11 +0000103 except Exception,e:
104 s = StringIO.StringIO()
105 import traceback
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000106 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbare33d3682008-09-19 23:32:11 +0000107 traceback.print_exc(e,file=s)
108 print >>s,'</pre>'
109 self.status = s.getvalue()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000110
111class ScanViewServer(BaseHTTPServer.HTTPServer):
112 def __init__(self, address, handler, root, reporters, options):
113 BaseHTTPServer.HTTPServer.__init__(self, address, handler)
114 self.root = root
115 self.reporters = reporters
116 self.options = options
117 self.halted = False
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000118 self.config = None
119 self.load_config()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000120
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000121 def load_config(self):
122 self.config = ConfigParser.RawConfigParser()
123
124 # Add defaults
125 self.config.add_section('ScanView')
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000126 for r in self.reporters:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000127 self.config.add_section(r.getName())
128 for p in r.getParameterNames():
129 self.config.set(r.getName(), p, '')
130
131 # Ignore parse errors
132 try:
133 self.config.read([kConfigPath])
134 except:
135 pass
136
137 # Save on exit
138 import atexit
139 atexit.register(lambda: self.save_config())
140
141 def save_config(self):
142 # Ignore errors (only called on exit).
143 try:
144 f = open(kConfigPath,'w')
145 self.config.write(f)
146 f.close()
147 except:
148 pass
149
Daniel Dunbare33d3682008-09-19 23:32:11 +0000150 def halt(self):
151 self.halted = True
152 if self.options.debug:
153 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
154
155 def serve_forever(self):
156 while not self.halted:
157 if self.options.debug > 1:
158 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
159 try:
160 self.handle_request()
161 except OSError,e:
162 print 'OSError',e.errno
163
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000164 def finish_request(self, request, client_address):
165 if self.options.autoReload:
166 import ScanView
167 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
168 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
169
Daniel Dunbare33d3682008-09-19 23:32:11 +0000170 def handle_error(self, request, client_address):
171 # Ignore socket errors
172 info = sys.exc_info()
173 if info and isinstance(info[1], socket.error):
174 if self.options.debug > 1:
175 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
176 return
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000177 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000178
179# Borrowed from Quixote, with simplifications.
180def parse_query(qs, fields=None):
181 if fields is None:
182 fields = {}
183 for chunk in filter(None, qs.split('&')):
184 if '=' not in chunk:
185 name = chunk
186 value = ''
187 else:
188 name, value = chunk.split('=', 1)
189 name = urllib.unquote(name.replace('+', ' '))
190 value = urllib.unquote(value.replace('+', ' '))
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000191 item = fields.get(name)
192 if item is None:
193 fields[name] = [value]
194 else:
195 item.append(value)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000196 return fields
197
198class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
199 server_version = "ScanViewServer/" + __version__
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000200 dynamic_mtime = time.time()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000201
202 def do_HEAD(self):
203 try:
204 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
205 except Exception,e:
206 self.handle_exception(e)
207
208 def do_GET(self):
209 try:
210 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
211 except Exception,e:
212 self.handle_exception(e)
213
214 def do_POST(self):
215 """Serve a POST request."""
216 try:
217 length = self.headers.getheader('content-length') or "0"
218 try:
219 length = int(length)
220 except:
221 length = 0
222 content = self.rfile.read(length)
223 fields = parse_query(content)
224 f = self.send_head(fields)
225 if f:
226 self.copyfile(f, self.wfile)
227 f.close()
228 except Exception,e:
229 self.handle_exception(e)
230
231 def log_message(self, format, *args):
232 if self.server.options.debug:
233 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
234 (sys.argv[0],
235 self.address_string(),
236 self.log_date_time_string(),
237 format%args))
238
239 def load_report(self, report):
240 path = os.path.join(self.server.root, 'report-%s.html'%report)
241 data = open(path).read()
242 keys = {}
243 for item in kBugKeyValueRE.finditer(data):
244 k,v = item.groups()
245 keys[k] = v
246 return keys
247
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000248 def load_crashes(self):
249 path = posixpath.join(self.server.root, 'index.html')
250 data = open(path).read()
251 problems = []
252 for item in kReportCrashEntryRE.finditer(data):
253 fieldData = item.group(1)
254 fields = dict([i.groups() for i in
255 kReportCrashEntryKeyValueRE.finditer(fieldData)])
256 problems.append(fields)
257 return problems
258
Daniel Dunbare33d3682008-09-19 23:32:11 +0000259 def handle_exception(self, exc):
260 import traceback
261 s = StringIO.StringIO()
262 print >>s, "INTERNAL ERROR\n"
263 traceback.print_exc(exc, s)
264 f = self.send_string(s.getvalue(), 'text/plain')
265 if f:
266 self.copyfile(f, self.wfile)
267 f.close()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000268
269 def get_scalar_field(self, name):
270 if name in self.fields:
271 return self.fields[name][0]
272 else:
273 return None
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000274
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000275 def submit_bug(self, c):
276 title = self.get_scalar_field('title')
277 description = self.get_scalar_field('description')
278 report = self.get_scalar_field('report')
279 reporterIndex = self.get_scalar_field('reporter')
280 files = []
281 for fileID in self.fields.get('files',[]):
282 try:
283 i = int(fileID)
284 except:
285 i = None
286 if i is None or i<0 or i>=len(c.files):
287 return (False, 'Invalid file ID')
288 files.append(c.files[i])
289 print files
290
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000291 if not title:
292 return (False, "Missing title.")
293 if not description:
294 return (False, "Missing description.")
295 try:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000296 reporterIndex = int(reporterIndex)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000297 except:
298 return (False, "Invalid report method.")
Daniel Dunbare33d3682008-09-19 23:32:11 +0000299
300 # Get the reporter and parameters.
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000301 reporter = self.server.reporters[reporterIndex]
Daniel Dunbare33d3682008-09-19 23:32:11 +0000302 parameters = {}
303 for o in reporter.getParameterNames():
304 name = '%s_%s'%(reporter.getName(),o)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000305 if name not in self.fields:
306 return (False,
307 'Missing field "%s" for %s report method.'%(name,
308 reporter.getName()))
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000309 parameters[o] = self.get_scalar_field(name)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000310
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000311 # Update config defaults.
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000312 if report != 'None':
313 self.server.config.set('ScanView', 'reporter', reporterIndex)
314 for o in reporter.getParameterNames():
315 self.server.config.set(reporter.getName(), o, parameters[o])
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000316
Daniel Dunbare33d3682008-09-19 23:32:11 +0000317 # Create the report.
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000318 bug = Reporter.BugReport(title, description, files)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000319
Daniel Dunbare33d3682008-09-19 23:32:11 +0000320 # Kick off a reporting thread.
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000321 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000322 t.start()
323
324 # Wait for thread to die...
325 while t.isAlive():
Daniel Dunbare33d3682008-09-19 23:32:11 +0000326 time.sleep(.25)
327 submitStatus = t.status
328
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000329 return (t.success, t.status)
330
331 def send_report_submit(self):
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000332 print self.fields
333 report = self.get_scalar_field('report')
334 c = self.get_report_context(report)
335 if c.reportSource is None:
336 reportingFor = "Report Crashes > "
337 fileBug = """\
338<a href="/report_crashes">File Bug</a> > """%locals()
339 else:
340 reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
341 report)
342 fileBug = '<a href="/report/%s">File Bug</a> > ' % report
343 title = self.get_scalar_field('title')
344 description = self.get_scalar_field('description')
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000345
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000346 res,message = self.submit_bug(c)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000347
348 if res:
349 statusClass = 'SubmitOk'
350 statusName = 'Succeeded'
351 else:
352 statusClass = 'SubmitFail'
353 statusName = 'Failed'
354
355 result = """
356<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000357 <title>Bug Submission</title>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000358 <link rel="stylesheet" type="text/css" href="/scanview.css" />
359</head>
360<body>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000361<h3>
362<a href="/">Summary</a> >
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000363%(reportingFor)s
364%(fileBug)s
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000365Submit</h3>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000366<form name="form" action="">
367<table class="form">
368<tr><td>
369<table class="form_group">
370<tr>
371 <td class="form_clabel">Title:</td>
372 <td class="form_value">
373 <input type="text" name="title" size="50" value="%(title)s" disabled>
374 </td>
375</tr>
376<tr>
377 <td class="form_label">Description:</td>
378 <td class="form_value">
379<textarea rows="10" cols="80" name="description" disabled>
380%(description)s
381</textarea>
382 </td>
383</table>
384</td></tr>
385</table>
386</form>
387<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
388%(message)s
389<p>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000390<hr>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000391<a href="/">Return to Summary</a>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000392</body>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000393</html>"""%locals()
394 return self.send_string(result)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000395
Daniel Dunbar28870e12008-09-22 18:05:49 +0000396 def send_open_report(self, report):
397 try:
398 keys = self.load_report(report)
399 except IOError:
400 return self.send_error(400, 'Invalid report.')
401
402 file = keys.get('FILE')
403 if not file or not posixpath.exists(file):
404 return self.send_error(400, 'File does not exist: "%s"' % file)
405
406 import startfile
407 if self.server.options.debug:
408 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
409 file)
410
411 status = startfile.open(file)
412 if status:
413 res = 'Opened: "%s"' % file
414 else:
415 res = 'Open failed: "%s"' % file
416
417 return self.send_string(res, 'text/plain')
418
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000419 def get_report_context(self, report):
420 class Context:
421 pass
422 if report is None or report == 'None':
423 data = self.load_crashes()
424 # Don't allow empty reports.
425 if not data:
426 raise ValueError, 'No crashes detected!'
427 c = Context()
428 c.title = 'clang static analyzer failures'
Daniel Dunbare33d3682008-09-19 23:32:11 +0000429
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000430 stderrSummary = ""
431 for item in data:
432 if 'stderr' in item:
433 path = posixpath.join(self.server.root, item['stderr'])
434 if os.path.exists(path):
435 lns = itertools.islice(open(path), 0, 10)
436 stderrSummary += '%s\n--\n%s' % (item.get('src',
437 '<unknown>'),
438 ''.join(lns))
439
440 c.description = """\
441The clang static analyzer failed on these inputs:
442%s
443
444STDERR Summary
445--------------
446%s
447""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
448 stderrSummary)
449 c.reportSource = None
450 c.navMarkup = "Report Crashes > "
451 c.files = []
452 for item in data:
453 c.files.append(item.get('src',''))
454 c.files.append(posixpath.join(self.server.root,
455 item.get('file','')))
456 c.files.append(posixpath.join(self.server.root,
457 item.get('clangfile','')))
458 c.files.append(posixpath.join(self.server.root,
459 item.get('stderr','')))
460 c.files.append(posixpath.join(self.server.root,
461 item.get('info','')))
462 # Just in case something failed, ignore files which don't
463 # exist.
464 c.files = [f for f in c.files
465 if os.path.exists(f) and os.path.isfile(f)]
466 else:
467 # Check that this is a valid report.
468 path = posixpath.join(self.server.root, 'report-%s.html' % report)
469 if not posixpath.exists(path):
470 raise ValueError, 'Invalid report ID'
471 keys = self.load_report(report)
472 c = Context()
473 c.title = keys.get('DESC','clang error (unrecognized')
474 c.description = """\
475Bug reported by the clang static analyzer.
Daniel Dunbare33d3682008-09-19 23:32:11 +0000476
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000477Description: %s
478File: %s
479Line: %s
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000480"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
481 c.reportSource = 'report-%s.html' % report
482 c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
483 report)
484
485 c.files = [path]
486 return c
487
488 def send_report(self, report, configOverrides=None):
489 def getConfigOption(section, field):
490 if (configOverrides is not None and
491 section in configOverrides and
492 field in configOverrides[section]):
493 return configOverrides[section][field]
494 return self.server.config.get(section, field)
495
496 # report is None is used for crashes
497 try:
498 c = self.get_report_context(report)
499 except ValueError, e:
500 return self.send_error(400, e.message)
501
502 title = c.title
503 description= c.description
504 reportingFor = c.navMarkup
505 if c.reportSource is None:
506 extraIFrame = ""
507 else:
508 extraIFrame = """\
509<iframe src="/%s" width="100%%" height="40%%"
510 scrolling="auto" frameborder="1">
511 <a href="/%s">View Bug Report</a>
512</iframe>""" % (c.reportSource, c.reportSource)
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000513
Daniel Dunbare33d3682008-09-19 23:32:11 +0000514 reporterSelections = []
515 reporterOptions = []
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000516
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000517 try:
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000518 active = int(getConfigOption('ScanView','reporter'))
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000519 except:
520 active = 0
Daniel Dunbare33d3682008-09-19 23:32:11 +0000521 for i,r in enumerate(self.server.reporters):
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000522 selected = (i == active)
523 if selected:
524 selectedStr = ' selected'
525 else:
526 selectedStr = ''
527 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Daniel Dunbar78266292008-09-22 00:11:51 +0000528 options = '\n'.join(["""\
529<tr>
530 <td class="form_clabel">%s:</td>
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000531 <td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000532</tr>"""%(o,r.getName(),o,getConfigOption(r.getName(),o)) for o in r.getParameterNames()])
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000533 display = ('none','')[selected]
Daniel Dunbar78266292008-09-22 00:11:51 +0000534 reporterOptions.append("""\
535<tr id="%sReporterOptions" style="display:%s">
536 <td class="form_label">%s Options</td>
537 <td class="form_value">
538 <table class="form_inner_group">
539%s
540 </table>
541 </td>
542</tr>
543"""%(r.getName(),display,r.getName(),options))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000544 reporterSelections = '\n'.join(reporterSelections)
545 reporterOptionsDivs = '\n'.join(reporterOptions)
546 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
547
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000548 if c.files:
549 fieldSize = min(5, len(c.files))
550 attachFileOptions = '\n'.join(["""\
551<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
552 attachFileRow = """\
553<tr>
554 <td class="form_label">Attach:</td>
555 <td class="form_value">
556<select style="width:100%%" name="files" multiple size=%d>
557%s
558</select>
559 </td>
560</tr>
561""" % (min(5, len(c.files)), attachFileOptions)
562 else:
563 attachFileRow = ""
564
Daniel Dunbare33d3682008-09-19 23:32:11 +0000565 result = """<html>
566<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000567 <title>File Bug</title>
Daniel Dunbar78266292008-09-22 00:11:51 +0000568 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbare33d3682008-09-19 23:32:11 +0000569</head>
570<script language="javascript" type="text/javascript">
571var reporters = %(reportersArray)s;
572function updateReporterOptions() {
573 index = document.getElementById('reporter').selectedIndex;
574 for (var i=0; i < reporters.length; ++i) {
575 o = document.getElementById(reporters[i] + "ReporterOptions");
576 if (i == index) {
Daniel Dunbar78266292008-09-22 00:11:51 +0000577 o.style.display = "";
Daniel Dunbare33d3682008-09-19 23:32:11 +0000578 } else {
579 o.style.display = "none";
580 }
581 }
582}
583</script>
Daniel Dunbar7345e472008-09-22 01:40:14 +0000584<body onLoad="updateReporterOptions()">
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000585<h3>
586<a href="/">Summary</a> >
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000587%(reportingFor)s
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000588File Bug</h3>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000589<form name="form" action="/report_submit" method="post">
Daniel Dunbar78266292008-09-22 00:11:51 +0000590<input type="hidden" name="report" value="%(report)s">
591
592<table class="form">
593<tr><td>
594<table class="form_group">
595<tr>
596 <td class="form_clabel">Title:</td>
597 <td class="form_value">
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000598 <input type="text" name="title" size="50" value="%(title)s">
Daniel Dunbar78266292008-09-22 00:11:51 +0000599 </td>
600</tr>
601<tr>
602 <td class="form_label">Description:</td>
603 <td class="form_value">
Daniel Dunbare33d3682008-09-19 23:32:11 +0000604<textarea rows="10" cols="80" name="description">
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000605%(description)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000606</textarea>
607 </td>
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000608</tr>
609
610%(attachFileRow)s
611
Daniel Dunbar78266292008-09-22 00:11:51 +0000612</table>
613<br>
614<table class="form_group">
615<tr>
616 <td class="form_clabel">Method:</td>
617 <td class="form_value">
618 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
619 %(reporterSelections)s
620 </select>
621 </td>
622</tr>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000623%(reporterOptionsDivs)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000624</table>
625<br>
626</td></tr>
627<tr><td class="form_submit">
628 <input align="right" type="submit" name="Submit" value="Submit">
629</td></tr>
630</table>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000631</form>
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000632
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000633%(extraIFrame)s
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000634
Daniel Dunbare33d3682008-09-19 23:32:11 +0000635</body>
636</html>"""%locals()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000637
Daniel Dunbare33d3682008-09-19 23:32:11 +0000638 return self.send_string(result)
639
640 def send_head(self, fields=None):
Daniel Dunbarf6a415f2008-09-24 17:59:41 +0000641 if (self.server.options.onlyServeLocal and
642 self.client_address[0] != '127.0.0.1'):
643 return self.send_error('401', 'Unauthorized host.')
644
Daniel Dunbare33d3682008-09-19 23:32:11 +0000645 if fields is None:
646 fields = {}
647 self.fields = fields
648
649 o = urlparse.urlparse(self.path)
650 self.fields = parse_query(o.query, fields)
651 path = posixpath.normpath(urllib.unquote(o.path))
652
653 # Split the components and strip the root prefix.
654 components = path.split('/')[1:]
655
656 # Special case some top-level entries.
657 if components:
658 name = components[0]
Daniel Dunbar78266292008-09-22 00:11:51 +0000659 if len(components)==2:
660 if name=='report':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000661 return self.send_report(components[1])
Daniel Dunbar28870e12008-09-22 18:05:49 +0000662 elif name=='open':
663 return self.send_open_report(components[1])
Daniel Dunbar78266292008-09-22 00:11:51 +0000664 elif len(components)==1:
665 if name=='quit':
666 self.server.halt()
667 return self.send_string('Goodbye.', 'text/plain')
668 elif name=='report_submit':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000669 return self.send_report_submit()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000670 elif name=='report_crashes':
671 overrides = { 'ScanView' : {},
672 'Radar' : {},
673 'Email' : {} }
674 for i,r in enumerate(self.server.reporters):
675 if r.getName() == 'Radar':
676 overrides['ScanView']['reporter'] = i
677 break
678 overrides['Radar']['Component'] = 'llvm - checker'
679 overrides['Radar']['Component Version'] = 'X'
680 return self.send_report(None, overrides)
Daniel Dunbar78266292008-09-22 00:11:51 +0000681 elif name=='favicon.ico':
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000682 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000683
684 # Match directory entries.
685 if components[-1] == '':
686 components[-1] = 'index.html'
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000687
688 suffix = '/'.join(components)
689
690 # The summary may reference source files on disk using rooted
691 # paths. Make sure these resolve correctly for now.
692 # FIXME: This isn't a very good idea... we should probably
693 # mark rooted paths somehow.
694 if os.path.exists(posixpath.join('/', suffix)):
695 path = posixpath.join('/', suffix)
696 else:
697 path = posixpath.join(self.server.root, suffix)
698
Daniel Dunbare33d3682008-09-19 23:32:11 +0000699 if self.server.options.debug > 1:
700 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
701 path)
702 return self.send_path(path)
703
704 def send_404(self):
705 self.send_error(404, "File not found")
706 return None
707
708 def send_path(self, path):
709 ctype = self.guess_type(path)
710 if ctype.startswith('text/'):
711 # Patch file instead
712 return self.send_patched_file(path, ctype)
713 else:
714 mode = 'rb'
715 try:
716 f = open(path, mode)
717 except IOError:
718 return self.send_404()
719 return self.send_file(f, ctype)
720
721 def send_file(self, f, ctype):
722 # Patch files to add links, but skip binary files.
723 self.send_response(200)
724 self.send_header("Content-type", ctype)
725 fs = os.fstat(f.fileno())
726 self.send_header("Content-Length", str(fs[6]))
727 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
728 self.end_headers()
729 return f
730
731 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
732 if headers:
733 self.send_response(200)
734 self.send_header("Content-type", ctype)
735 self.send_header("Content-Length", str(len(s)))
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000736 if mtime is None:
737 mtime = self.dynamic_mtime
738 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000739 self.end_headers()
740 return StringIO.StringIO(s)
741
742 def send_patched_file(self, path, ctype):
Daniel Dunbar17fded62008-09-22 21:43:43 +0000743 # Allow a very limited set of variables. This is pretty gross.
744 variables = {}
745 variables['report'] = ''
746 m = kReportFileRE.match(path)
747 if m:
748 variables['report'] = m.group(2)
749
Daniel Dunbar7345e472008-09-22 01:40:14 +0000750 try:
751 f = open(path,'r')
752 except IOError:
753 return self.send_404()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000754 fs = os.fstat(f.fileno())
755 data = f.read()
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000756 for a,b in kReportReplacements:
Daniel Dunbar17fded62008-09-22 21:43:43 +0000757 data = a.sub(b % variables, data)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000758 return self.send_string(data, ctype, mtime=fs.st_mtime)
759
760
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000761def create_server(address, options, root):
Daniel Dunbare33d3682008-09-19 23:32:11 +0000762 import Reporter
763
764 reporters = Reporter.getReporters()
765
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000766 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbare33d3682008-09-19 23:32:11 +0000767 root,
768 reporters,
769 options)