blob: 4a9c175ac0659187a10cd30949503597794d63c2 [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
Daniel Dunbarb5f9a4e2008-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 Dunbar7ebe0ed2008-09-25 06:05:31 +000072
Daniel Dunbar4d20cf72008-09-22 18:44:46 +000073###
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000074# Other simple parameters
75
76kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar8dcd9402008-09-22 02:27:45 +000077kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000078
Daniel Dunbare33d3682008-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 Dunbar47cceff2008-09-22 01:21:30 +000092 self.success = False
Daniel Dunbare33d3682008-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 Dunbar47cceff2008-09-22 01:21:30 +0000100 self.status = self.reporter.fileReport(self.report, self.parameters)
101 self.success = True
Daniel Dunbare33d3682008-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 Dunbar54722492008-09-20 01:43:16 +0000105 except Reporter.ReportFailure,e:
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000106 self.status = e.value
Daniel Dunbare33d3682008-09-19 23:32:11 +0000107 except Exception,e:
108 s = StringIO.StringIO()
109 import traceback
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000110 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbare33d3682008-09-19 23:32:11 +0000111 traceback.print_exc(e,file=s)
112 print >>s,'</pre>'
113 self.status = s.getvalue()
Daniel Dunbare33d3682008-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 Dunbar8dcd9402008-09-22 02:27:45 +0000122 self.config = None
123 self.load_config()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000124
Daniel Dunbar8dcd9402008-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 Dunbar7ad535d2008-09-22 02:53:12 +0000130 for r in self.reporters:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000131 self.config.add_section(r.getName())
Ted Kremenek19c88202008-09-30 16:08:13 +0000132 for p in r.getParameters():
Ted Kremenek480bc342008-09-30 16:11:33 +0000133 if p.saveConfigValue():
Ted Kremenek19c88202008-09-30 16:08:13 +0000134 self.config.set(r.getName(), p.getName(), '')
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000135
136 # Ignore parse errors
137 try:
138 self.config.read([kConfigPath])
139 except:
140 pass
141
142 # Save on exit
143 import atexit
144 atexit.register(lambda: self.save_config())
145
146 def save_config(self):
147 # Ignore errors (only called on exit).
148 try:
149 f = open(kConfigPath,'w')
150 self.config.write(f)
151 f.close()
152 except:
153 pass
154
Daniel Dunbare33d3682008-09-19 23:32:11 +0000155 def halt(self):
156 self.halted = True
157 if self.options.debug:
158 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
159
160 def serve_forever(self):
161 while not self.halted:
162 if self.options.debug > 1:
163 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
164 try:
165 self.handle_request()
166 except OSError,e:
167 print 'OSError',e.errno
168
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000169 def finish_request(self, request, client_address):
170 if self.options.autoReload:
171 import ScanView
172 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
173 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
174
Daniel Dunbare33d3682008-09-19 23:32:11 +0000175 def handle_error(self, request, client_address):
176 # Ignore socket errors
177 info = sys.exc_info()
178 if info and isinstance(info[1], socket.error):
179 if self.options.debug > 1:
180 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
181 return
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000182 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000183
184# Borrowed from Quixote, with simplifications.
185def parse_query(qs, fields=None):
186 if fields is None:
187 fields = {}
188 for chunk in filter(None, qs.split('&')):
189 if '=' not in chunk:
190 name = chunk
191 value = ''
192 else:
193 name, value = chunk.split('=', 1)
194 name = urllib.unquote(name.replace('+', ' '))
195 value = urllib.unquote(value.replace('+', ' '))
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000196 item = fields.get(name)
197 if item is None:
198 fields[name] = [value]
199 else:
200 item.append(value)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000201 return fields
202
203class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
204 server_version = "ScanViewServer/" + __version__
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000205 dynamic_mtime = time.time()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000206
207 def do_HEAD(self):
208 try:
209 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
210 except Exception,e:
211 self.handle_exception(e)
212
213 def do_GET(self):
214 try:
215 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
216 except Exception,e:
217 self.handle_exception(e)
218
219 def do_POST(self):
220 """Serve a POST request."""
221 try:
222 length = self.headers.getheader('content-length') or "0"
223 try:
224 length = int(length)
225 except:
226 length = 0
227 content = self.rfile.read(length)
228 fields = parse_query(content)
229 f = self.send_head(fields)
230 if f:
231 self.copyfile(f, self.wfile)
232 f.close()
233 except Exception,e:
234 self.handle_exception(e)
235
236 def log_message(self, format, *args):
237 if self.server.options.debug:
238 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
239 (sys.argv[0],
240 self.address_string(),
241 self.log_date_time_string(),
242 format%args))
243
244 def load_report(self, report):
245 path = os.path.join(self.server.root, 'report-%s.html'%report)
246 data = open(path).read()
247 keys = {}
248 for item in kBugKeyValueRE.finditer(data):
249 k,v = item.groups()
250 keys[k] = v
251 return keys
252
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000253 def load_crashes(self):
254 path = posixpath.join(self.server.root, 'index.html')
255 data = open(path).read()
256 problems = []
257 for item in kReportCrashEntryRE.finditer(data):
258 fieldData = item.group(1)
259 fields = dict([i.groups() for i in
260 kReportCrashEntryKeyValueRE.finditer(fieldData)])
261 problems.append(fields)
262 return problems
263
Daniel Dunbare33d3682008-09-19 23:32:11 +0000264 def handle_exception(self, exc):
265 import traceback
266 s = StringIO.StringIO()
267 print >>s, "INTERNAL ERROR\n"
268 traceback.print_exc(exc, s)
269 f = self.send_string(s.getvalue(), 'text/plain')
270 if f:
271 self.copyfile(f, self.wfile)
272 f.close()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000273
274 def get_scalar_field(self, name):
275 if name in self.fields:
276 return self.fields[name][0]
277 else:
278 return None
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000279
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000280 def submit_bug(self, c):
281 title = self.get_scalar_field('title')
282 description = self.get_scalar_field('description')
283 report = self.get_scalar_field('report')
284 reporterIndex = self.get_scalar_field('reporter')
285 files = []
286 for fileID in self.fields.get('files',[]):
287 try:
288 i = int(fileID)
289 except:
290 i = None
291 if i is None or i<0 or i>=len(c.files):
292 return (False, 'Invalid file ID')
293 files.append(c.files[i])
294 print files
295
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000296 if not title:
297 return (False, "Missing title.")
298 if not description:
299 return (False, "Missing description.")
300 try:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000301 reporterIndex = int(reporterIndex)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000302 except:
303 return (False, "Invalid report method.")
Daniel Dunbare33d3682008-09-19 23:32:11 +0000304
305 # Get the reporter and parameters.
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000306 reporter = self.server.reporters[reporterIndex]
Daniel Dunbare33d3682008-09-19 23:32:11 +0000307 parameters = {}
Ted Kremenek19c88202008-09-30 16:08:13 +0000308 for o in reporter.getParameters():
309 name = '%s_%s'%(reporter.getName(),o.getName())
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000310 if name not in self.fields:
311 return (False,
312 'Missing field "%s" for %s report method.'%(name,
313 reporter.getName()))
Ted Kremenek19c88202008-09-30 16:08:13 +0000314 parameters[o.getName()] = self.get_scalar_field(name)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000315
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000316 # Update config defaults.
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000317 if report != 'None':
318 self.server.config.set('ScanView', 'reporter', reporterIndex)
Ted Kremenek19c88202008-09-30 16:08:13 +0000319 for o in reporter.getParameters():
Ted Kremenek480bc342008-09-30 16:11:33 +0000320 if o.saveConfigValue():
Ted Kremenek19c88202008-09-30 16:08:13 +0000321 name = o.getName()
322 self.server.config.set(reporter.getName(), name, parameters[name])
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000323
Daniel Dunbare33d3682008-09-19 23:32:11 +0000324 # Create the report.
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000325 bug = Reporter.BugReport(title, description, files)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000326
Daniel Dunbare33d3682008-09-19 23:32:11 +0000327 # Kick off a reporting thread.
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000328 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000329 t.start()
330
331 # Wait for thread to die...
332 while t.isAlive():
Daniel Dunbare33d3682008-09-19 23:32:11 +0000333 time.sleep(.25)
334 submitStatus = t.status
335
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000336 return (t.success, t.status)
337
338 def send_report_submit(self):
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000339 print self.fields
340 report = self.get_scalar_field('report')
341 c = self.get_report_context(report)
342 if c.reportSource is None:
343 reportingFor = "Report Crashes > "
344 fileBug = """\
345<a href="/report_crashes">File Bug</a> > """%locals()
346 else:
347 reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
348 report)
349 fileBug = '<a href="/report/%s">File Bug</a> > ' % report
350 title = self.get_scalar_field('title')
351 description = self.get_scalar_field('description')
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000352
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000353 res,message = self.submit_bug(c)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000354
355 if res:
356 statusClass = 'SubmitOk'
357 statusName = 'Succeeded'
358 else:
359 statusClass = 'SubmitFail'
360 statusName = 'Failed'
361
362 result = """
363<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000364 <title>Bug Submission</title>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000365 <link rel="stylesheet" type="text/css" href="/scanview.css" />
366</head>
367<body>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000368<h3>
369<a href="/">Summary</a> >
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000370%(reportingFor)s
371%(fileBug)s
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000372Submit</h3>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000373<form name="form" action="">
374<table class="form">
375<tr><td>
376<table class="form_group">
377<tr>
378 <td class="form_clabel">Title:</td>
379 <td class="form_value">
380 <input type="text" name="title" size="50" value="%(title)s" disabled>
381 </td>
382</tr>
383<tr>
384 <td class="form_label">Description:</td>
385 <td class="form_value">
386<textarea rows="10" cols="80" name="description" disabled>
387%(description)s
388</textarea>
389 </td>
390</table>
391</td></tr>
392</table>
393</form>
394<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
395%(message)s
396<p>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000397<hr>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000398<a href="/">Return to Summary</a>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000399</body>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000400</html>"""%locals()
401 return self.send_string(result)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000402
Daniel Dunbar28870e12008-09-22 18:05:49 +0000403 def send_open_report(self, report):
404 try:
405 keys = self.load_report(report)
406 except IOError:
407 return self.send_error(400, 'Invalid report.')
408
409 file = keys.get('FILE')
410 if not file or not posixpath.exists(file):
411 return self.send_error(400, 'File does not exist: "%s"' % file)
412
413 import startfile
414 if self.server.options.debug:
415 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
416 file)
417
418 status = startfile.open(file)
419 if status:
420 res = 'Opened: "%s"' % file
421 else:
422 res = 'Open failed: "%s"' % file
423
424 return self.send_string(res, 'text/plain')
425
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000426 def get_report_context(self, report):
427 class Context:
428 pass
429 if report is None or report == 'None':
430 data = self.load_crashes()
431 # Don't allow empty reports.
432 if not data:
433 raise ValueError, 'No crashes detected!'
434 c = Context()
435 c.title = 'clang static analyzer failures'
Daniel Dunbare33d3682008-09-19 23:32:11 +0000436
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000437 stderrSummary = ""
438 for item in data:
439 if 'stderr' in item:
440 path = posixpath.join(self.server.root, item['stderr'])
441 if os.path.exists(path):
442 lns = itertools.islice(open(path), 0, 10)
443 stderrSummary += '%s\n--\n%s' % (item.get('src',
444 '<unknown>'),
445 ''.join(lns))
446
447 c.description = """\
448The clang static analyzer failed on these inputs:
449%s
450
451STDERR Summary
452--------------
453%s
454""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
455 stderrSummary)
456 c.reportSource = None
457 c.navMarkup = "Report Crashes > "
458 c.files = []
459 for item in data:
460 c.files.append(item.get('src',''))
461 c.files.append(posixpath.join(self.server.root,
462 item.get('file','')))
463 c.files.append(posixpath.join(self.server.root,
464 item.get('clangfile','')))
465 c.files.append(posixpath.join(self.server.root,
466 item.get('stderr','')))
467 c.files.append(posixpath.join(self.server.root,
468 item.get('info','')))
469 # Just in case something failed, ignore files which don't
470 # exist.
471 c.files = [f for f in c.files
472 if os.path.exists(f) and os.path.isfile(f)]
473 else:
474 # Check that this is a valid report.
475 path = posixpath.join(self.server.root, 'report-%s.html' % report)
476 if not posixpath.exists(path):
477 raise ValueError, 'Invalid report ID'
478 keys = self.load_report(report)
479 c = Context()
480 c.title = keys.get('DESC','clang error (unrecognized')
481 c.description = """\
482Bug reported by the clang static analyzer.
Daniel Dunbare33d3682008-09-19 23:32:11 +0000483
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000484Description: %s
485File: %s
486Line: %s
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000487"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
488 c.reportSource = 'report-%s.html' % report
489 c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
490 report)
491
492 c.files = [path]
493 return c
494
495 def send_report(self, report, configOverrides=None):
496 def getConfigOption(section, field):
497 if (configOverrides is not None and
498 section in configOverrides and
499 field in configOverrides[section]):
500 return configOverrides[section][field]
501 return self.server.config.get(section, field)
502
503 # report is None is used for crashes
504 try:
505 c = self.get_report_context(report)
506 except ValueError, e:
507 return self.send_error(400, e.message)
508
509 title = c.title
510 description= c.description
511 reportingFor = c.navMarkup
512 if c.reportSource is None:
513 extraIFrame = ""
514 else:
515 extraIFrame = """\
516<iframe src="/%s" width="100%%" height="40%%"
517 scrolling="auto" frameborder="1">
518 <a href="/%s">View Bug Report</a>
519</iframe>""" % (c.reportSource, c.reportSource)
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000520
Daniel Dunbare33d3682008-09-19 23:32:11 +0000521 reporterSelections = []
522 reporterOptions = []
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000523
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000524 try:
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000525 active = int(getConfigOption('ScanView','reporter'))
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000526 except:
527 active = 0
Daniel Dunbare33d3682008-09-19 23:32:11 +0000528 for i,r in enumerate(self.server.reporters):
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000529 selected = (i == active)
530 if selected:
531 selectedStr = ' selected'
532 else:
533 selectedStr = ''
534 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Ted Kremenek19c88202008-09-30 16:08:13 +0000535 options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000536 display = ('none','')[selected]
Daniel Dunbar78266292008-09-22 00:11:51 +0000537 reporterOptions.append("""\
538<tr id="%sReporterOptions" style="display:%s">
539 <td class="form_label">%s Options</td>
540 <td class="form_value">
541 <table class="form_inner_group">
542%s
543 </table>
544 </td>
545</tr>
546"""%(r.getName(),display,r.getName(),options))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000547 reporterSelections = '\n'.join(reporterSelections)
548 reporterOptionsDivs = '\n'.join(reporterOptions)
549 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
550
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000551 if c.files:
552 fieldSize = min(5, len(c.files))
553 attachFileOptions = '\n'.join(["""\
554<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
555 attachFileRow = """\
556<tr>
557 <td class="form_label">Attach:</td>
558 <td class="form_value">
559<select style="width:100%%" name="files" multiple size=%d>
560%s
561</select>
562 </td>
563</tr>
564""" % (min(5, len(c.files)), attachFileOptions)
565 else:
566 attachFileRow = ""
567
Daniel Dunbare33d3682008-09-19 23:32:11 +0000568 result = """<html>
569<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000570 <title>File Bug</title>
Daniel Dunbar78266292008-09-22 00:11:51 +0000571 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbare33d3682008-09-19 23:32:11 +0000572</head>
573<script language="javascript" type="text/javascript">
574var reporters = %(reportersArray)s;
575function updateReporterOptions() {
576 index = document.getElementById('reporter').selectedIndex;
577 for (var i=0; i < reporters.length; ++i) {
578 o = document.getElementById(reporters[i] + "ReporterOptions");
579 if (i == index) {
Daniel Dunbar78266292008-09-22 00:11:51 +0000580 o.style.display = "";
Daniel Dunbare33d3682008-09-19 23:32:11 +0000581 } else {
582 o.style.display = "none";
583 }
584 }
585}
586</script>
Daniel Dunbar7345e472008-09-22 01:40:14 +0000587<body onLoad="updateReporterOptions()">
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000588<h3>
589<a href="/">Summary</a> >
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000590%(reportingFor)s
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000591File Bug</h3>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000592<form name="form" action="/report_submit" method="post">
Daniel Dunbar78266292008-09-22 00:11:51 +0000593<input type="hidden" name="report" value="%(report)s">
594
595<table class="form">
596<tr><td>
597<table class="form_group">
598<tr>
599 <td class="form_clabel">Title:</td>
600 <td class="form_value">
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000601 <input type="text" name="title" size="50" value="%(title)s">
Daniel Dunbar78266292008-09-22 00:11:51 +0000602 </td>
603</tr>
604<tr>
605 <td class="form_label">Description:</td>
606 <td class="form_value">
Daniel Dunbare33d3682008-09-19 23:32:11 +0000607<textarea rows="10" cols="80" name="description">
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000608%(description)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000609</textarea>
610 </td>
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000611</tr>
612
613%(attachFileRow)s
614
Daniel Dunbar78266292008-09-22 00:11:51 +0000615</table>
616<br>
617<table class="form_group">
618<tr>
619 <td class="form_clabel">Method:</td>
620 <td class="form_value">
621 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
622 %(reporterSelections)s
623 </select>
624 </td>
625</tr>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000626%(reporterOptionsDivs)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000627</table>
628<br>
629</td></tr>
630<tr><td class="form_submit">
631 <input align="right" type="submit" name="Submit" value="Submit">
632</td></tr>
633</table>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000634</form>
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000635
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000636%(extraIFrame)s
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000637
Daniel Dunbare33d3682008-09-19 23:32:11 +0000638</body>
639</html>"""%locals()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000640
Daniel Dunbare33d3682008-09-19 23:32:11 +0000641 return self.send_string(result)
642
643 def send_head(self, fields=None):
Daniel Dunbarf6a415f2008-09-24 17:59:41 +0000644 if (self.server.options.onlyServeLocal and
645 self.client_address[0] != '127.0.0.1'):
646 return self.send_error('401', 'Unauthorized host.')
647
Daniel Dunbare33d3682008-09-19 23:32:11 +0000648 if fields is None:
649 fields = {}
650 self.fields = fields
651
652 o = urlparse.urlparse(self.path)
653 self.fields = parse_query(o.query, fields)
654 path = posixpath.normpath(urllib.unquote(o.path))
655
656 # Split the components and strip the root prefix.
657 components = path.split('/')[1:]
658
659 # Special case some top-level entries.
660 if components:
661 name = components[0]
Daniel Dunbar78266292008-09-22 00:11:51 +0000662 if len(components)==2:
663 if name=='report':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000664 return self.send_report(components[1])
Daniel Dunbar28870e12008-09-22 18:05:49 +0000665 elif name=='open':
666 return self.send_open_report(components[1])
Daniel Dunbar78266292008-09-22 00:11:51 +0000667 elif len(components)==1:
668 if name=='quit':
669 self.server.halt()
670 return self.send_string('Goodbye.', 'text/plain')
671 elif name=='report_submit':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000672 return self.send_report_submit()
Daniel Dunbar7ebe0ed2008-09-25 06:05:31 +0000673 elif name=='report_crashes':
674 overrides = { 'ScanView' : {},
675 'Radar' : {},
676 'Email' : {} }
677 for i,r in enumerate(self.server.reporters):
678 if r.getName() == 'Radar':
679 overrides['ScanView']['reporter'] = i
680 break
681 overrides['Radar']['Component'] = 'llvm - checker'
682 overrides['Radar']['Component Version'] = 'X'
683 return self.send_report(None, overrides)
Daniel Dunbar78266292008-09-22 00:11:51 +0000684 elif name=='favicon.ico':
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000685 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000686
687 # Match directory entries.
688 if components[-1] == '':
689 components[-1] = 'index.html'
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000690
691 suffix = '/'.join(components)
692
693 # The summary may reference source files on disk using rooted
694 # paths. Make sure these resolve correctly for now.
695 # FIXME: This isn't a very good idea... we should probably
696 # mark rooted paths somehow.
697 if os.path.exists(posixpath.join('/', suffix)):
698 path = posixpath.join('/', suffix)
699 else:
700 path = posixpath.join(self.server.root, suffix)
701
Daniel Dunbare33d3682008-09-19 23:32:11 +0000702 if self.server.options.debug > 1:
703 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
704 path)
705 return self.send_path(path)
706
707 def send_404(self):
708 self.send_error(404, "File not found")
709 return None
710
711 def send_path(self, path):
712 ctype = self.guess_type(path)
713 if ctype.startswith('text/'):
714 # Patch file instead
715 return self.send_patched_file(path, ctype)
716 else:
717 mode = 'rb'
718 try:
719 f = open(path, mode)
720 except IOError:
721 return self.send_404()
722 return self.send_file(f, ctype)
723
724 def send_file(self, f, ctype):
725 # Patch files to add links, but skip binary files.
726 self.send_response(200)
727 self.send_header("Content-type", ctype)
728 fs = os.fstat(f.fileno())
729 self.send_header("Content-Length", str(fs[6]))
730 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
731 self.end_headers()
732 return f
733
734 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
735 if headers:
736 self.send_response(200)
737 self.send_header("Content-type", ctype)
738 self.send_header("Content-Length", str(len(s)))
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000739 if mtime is None:
740 mtime = self.dynamic_mtime
741 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000742 self.end_headers()
743 return StringIO.StringIO(s)
744
745 def send_patched_file(self, path, ctype):
Daniel Dunbar17fded62008-09-22 21:43:43 +0000746 # Allow a very limited set of variables. This is pretty gross.
747 variables = {}
748 variables['report'] = ''
749 m = kReportFileRE.match(path)
750 if m:
751 variables['report'] = m.group(2)
752
Daniel Dunbar7345e472008-09-22 01:40:14 +0000753 try:
754 f = open(path,'r')
755 except IOError:
756 return self.send_404()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000757 fs = os.fstat(f.fileno())
758 data = f.read()
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000759 for a,b in kReportReplacements:
Daniel Dunbar17fded62008-09-22 21:43:43 +0000760 data = a.sub(b % variables, data)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000761 return self.send_string(data, ctype, mtime=fs.st_mtime)
762
763
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000764def create_server(address, options, root):
Daniel Dunbare33d3682008-09-19 23:32:11 +0000765 import Reporter
766
767 reporters = Reporter.getReporters()
768
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000769 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbare33d3682008-09-19 23:32:11 +0000770 root,
771 reporters,
772 options)