blob: 48b8cc80d0e542ad66e830102486269e33912cdf [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
13
Daniel Dunbar54722492008-09-20 01:43:16 +000014import Reporter
Daniel Dunbar8dcd9402008-09-22 02:27:45 +000015import ConfigParser
Daniel Dunbare33d3682008-09-19 23:32:11 +000016
Daniel Dunbar17fded62008-09-22 21:43:43 +000017###
18# Various patterns matched or replaced by server.
19
20kReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
Daniel Dunbare33d3682008-09-19 23:32:11 +000021
Daniel Dunbare33d3682008-09-19 23:32:11 +000022kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
23
Daniel Dunbar4d20cf72008-09-22 18:44:46 +000024kReportReplacements = []
Daniel Dunbarcb028b02008-09-21 20:34:58 +000025
Daniel Dunbar4d20cf72008-09-22 18:44:46 +000026# Add custom javascript.
27kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
28<script language="javascript" type="text/javascript">
29function load(url) {
30 if (window.XMLHttpRequest) {
31 req = new XMLHttpRequest();
32 } else if (window.ActiveXObject) {
33 req = new ActiveXObject("Microsoft.XMLHTTP");
34 }
35 if (req != undefined) {
36 req.open("GET", url, true);
37 req.send("");
38 }
39}
40</script>"""))
41
42# Insert additional columns.
43kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
44 '<td></td><td></td>'))
45
46# Insert report bug and open file links.
47kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
48 ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
49 '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
50
Daniel Dunbar17fded62008-09-22 21:43:43 +000051kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
52 '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
53
54kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
55 '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
56
Daniel Dunbar4d20cf72008-09-22 18:44:46 +000057###
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000058# Other simple parameters
59
60kResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
Daniel Dunbar8dcd9402008-09-22 02:27:45 +000061kConfigPath = os.path.expanduser('~/.scanview.cfg')
Daniel Dunbarb131c8a2008-09-21 23:02:25 +000062
Daniel Dunbare33d3682008-09-19 23:32:11 +000063###
64
65__version__ = "0.1"
66
67__all__ = ["create_server"]
68
69class ReporterThread(threading.Thread):
70 def __init__(self, report, reporter, parameters, server):
71 threading.Thread.__init__(self)
72 self.report = report
73 self.server = server
74 self.reporter = reporter
75 self.parameters = parameters
Daniel Dunbar47cceff2008-09-22 01:21:30 +000076 self.success = False
Daniel Dunbare33d3682008-09-19 23:32:11 +000077 self.status = None
78
79 def run(self):
80 result = None
81 try:
82 if self.server.options.debug:
83 print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
Daniel Dunbar47cceff2008-09-22 01:21:30 +000084 self.status = self.reporter.fileReport(self.report, self.parameters)
85 self.success = True
Daniel Dunbare33d3682008-09-19 23:32:11 +000086 time.sleep(3)
87 if self.server.options.debug:
88 print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
Daniel Dunbar54722492008-09-20 01:43:16 +000089 except Reporter.ReportFailure,e:
Daniel Dunbar47cceff2008-09-22 01:21:30 +000090 self.status = e.value
Daniel Dunbare33d3682008-09-19 23:32:11 +000091 except Exception,e:
92 s = StringIO.StringIO()
93 import traceback
Daniel Dunbar47cceff2008-09-22 01:21:30 +000094 print >>s,'<b>Unhandled Exception</b><br><pre>'
Daniel Dunbare33d3682008-09-19 23:32:11 +000095 traceback.print_exc(e,file=s)
96 print >>s,'</pre>'
97 self.status = s.getvalue()
Daniel Dunbare33d3682008-09-19 23:32:11 +000098
99class ScanViewServer(BaseHTTPServer.HTTPServer):
100 def __init__(self, address, handler, root, reporters, options):
101 BaseHTTPServer.HTTPServer.__init__(self, address, handler)
102 self.root = root
103 self.reporters = reporters
104 self.options = options
105 self.halted = False
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000106 self.config = None
107 self.load_config()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000108
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000109 def load_config(self):
110 self.config = ConfigParser.RawConfigParser()
111
112 # Add defaults
113 self.config.add_section('ScanView')
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000114 for r in self.reporters:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000115 self.config.add_section(r.getName())
116 for p in r.getParameterNames():
117 self.config.set(r.getName(), p, '')
118
119 # Ignore parse errors
120 try:
121 self.config.read([kConfigPath])
122 except:
123 pass
124
125 # Save on exit
126 import atexit
127 atexit.register(lambda: self.save_config())
128
129 def save_config(self):
130 # Ignore errors (only called on exit).
131 try:
132 f = open(kConfigPath,'w')
133 self.config.write(f)
134 f.close()
135 except:
136 pass
137
Daniel Dunbare33d3682008-09-19 23:32:11 +0000138 def halt(self):
139 self.halted = True
140 if self.options.debug:
141 print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
142
143 def serve_forever(self):
144 while not self.halted:
145 if self.options.debug > 1:
146 print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
147 try:
148 self.handle_request()
149 except OSError,e:
150 print 'OSError',e.errno
151
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000152 def finish_request(self, request, client_address):
153 if self.options.autoReload:
154 import ScanView
155 self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
156 BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
157
Daniel Dunbare33d3682008-09-19 23:32:11 +0000158 def handle_error(self, request, client_address):
159 # Ignore socket errors
160 info = sys.exc_info()
161 if info and isinstance(info[1], socket.error):
162 if self.options.debug > 1:
163 print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
164 return
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000165 BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000166
167# Borrowed from Quixote, with simplifications.
168def parse_query(qs, fields=None):
169 if fields is None:
170 fields = {}
171 for chunk in filter(None, qs.split('&')):
172 if '=' not in chunk:
173 name = chunk
174 value = ''
175 else:
176 name, value = chunk.split('=', 1)
177 name = urllib.unquote(name.replace('+', ' '))
178 value = urllib.unquote(value.replace('+', ' '))
179 fields[name] = value
180 return fields
181
182class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
183 server_version = "ScanViewServer/" + __version__
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000184 dynamic_mtime = time.time()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000185
186 def do_HEAD(self):
187 try:
188 SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
189 except Exception,e:
190 self.handle_exception(e)
191
192 def do_GET(self):
193 try:
194 SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
195 except Exception,e:
196 self.handle_exception(e)
197
198 def do_POST(self):
199 """Serve a POST request."""
200 try:
201 length = self.headers.getheader('content-length') or "0"
202 try:
203 length = int(length)
204 except:
205 length = 0
206 content = self.rfile.read(length)
207 fields = parse_query(content)
208 f = self.send_head(fields)
209 if f:
210 self.copyfile(f, self.wfile)
211 f.close()
212 except Exception,e:
213 self.handle_exception(e)
214
215 def log_message(self, format, *args):
216 if self.server.options.debug:
217 sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
218 (sys.argv[0],
219 self.address_string(),
220 self.log_date_time_string(),
221 format%args))
222
223 def load_report(self, report):
224 path = os.path.join(self.server.root, 'report-%s.html'%report)
225 data = open(path).read()
226 keys = {}
227 for item in kBugKeyValueRE.finditer(data):
228 k,v = item.groups()
229 keys[k] = v
230 return keys
231
232 def handle_exception(self, exc):
233 import traceback
234 s = StringIO.StringIO()
235 print >>s, "INTERNAL ERROR\n"
236 traceback.print_exc(exc, s)
237 f = self.send_string(s.getvalue(), 'text/plain')
238 if f:
239 self.copyfile(f, self.wfile)
240 f.close()
241
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000242 def submit_bug(self):
Daniel Dunbare33d3682008-09-19 23:32:11 +0000243 title = self.fields.get('title')
244 description = self.fields.get('description')
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000245 report = self.fields.get('report')
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000246 reporterIndex = self.fields.get('reporter')
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000247
248 # Type check form parameters.
249 reportPath = posixpath.join(self.server.root,
250 'report-%s.html' % report)
251 if not posixpath.exists(reportPath):
252 return (False, "Invalid report ID.")
253 if not title:
254 return (False, "Missing title.")
255 if not description:
256 return (False, "Missing description.")
257 try:
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000258 reporterIndex = int(reporterIndex)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000259 except:
260 return (False, "Invalid report method.")
Daniel Dunbare33d3682008-09-19 23:32:11 +0000261
262 # Get the reporter and parameters.
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000263 reporter = self.server.reporters[reporterIndex]
Daniel Dunbare33d3682008-09-19 23:32:11 +0000264 parameters = {}
265 for o in reporter.getParameterNames():
266 name = '%s_%s'%(reporter.getName(),o)
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000267 if name not in self.fields:
268 return (False,
269 'Missing field "%s" for %s report method.'%(name,
270 reporter.getName()))
271 parameters[o] = self.fields[name]
Daniel Dunbare33d3682008-09-19 23:32:11 +0000272
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000273 # Update config defaults.
274 self.server.config.set('ScanView', 'reporter', reporterIndex)
275 for o in reporter.getParameterNames():
276 self.server.config.set(reporter.getName(), o, parameters[o])
277
Daniel Dunbare33d3682008-09-19 23:32:11 +0000278 # Create the report.
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000279 bug = Reporter.BugReport(title, description, [reportPath])
Daniel Dunbare33d3682008-09-19 23:32:11 +0000280
Daniel Dunbare33d3682008-09-19 23:32:11 +0000281 # Kick off a reporting thread.
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000282 t = ReporterThread(bug, reporter, parameters, self.server)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000283 t.start()
284
285 # Wait for thread to die...
286 while t.isAlive():
Daniel Dunbare33d3682008-09-19 23:32:11 +0000287 time.sleep(.25)
288 submitStatus = t.status
289
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000290 return (t.success, t.status)
291
292 def send_report_submit(self):
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000293 report = self.fields.get('report')
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000294 title = self.fields.get('title')
295 description = self.fields.get('description')
296
297 res,message = self.submit_bug()
298
299 if res:
300 statusClass = 'SubmitOk'
301 statusName = 'Succeeded'
302 else:
303 statusClass = 'SubmitFail'
304 statusName = 'Failed'
305
306 result = """
307<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000308 <title>Bug Submission</title>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000309 <link rel="stylesheet" type="text/css" href="/scanview.css" />
310</head>
311<body>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000312<h3>
313<a href="/">Summary</a> >
314<a href="/report-%(report)s.html">Report %(report)s</a> >
315<a href="/report/%(report)s">File Bug</a> >
316Submit</h3>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000317<form name="form" action="">
318<table class="form">
319<tr><td>
320<table class="form_group">
321<tr>
322 <td class="form_clabel">Title:</td>
323 <td class="form_value">
324 <input type="text" name="title" size="50" value="%(title)s" disabled>
325 </td>
326</tr>
327<tr>
328 <td class="form_label">Description:</td>
329 <td class="form_value">
330<textarea rows="10" cols="80" name="description" disabled>
331%(description)s
332</textarea>
333 </td>
334</table>
335</td></tr>
336</table>
337</form>
338<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
339%(message)s
340<p>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000341<hr>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000342<a href="/">Return to Summary</a>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000343</body>
Daniel Dunbar47cceff2008-09-22 01:21:30 +0000344</html>"""%locals()
345 return self.send_string(result)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000346
Daniel Dunbar28870e12008-09-22 18:05:49 +0000347 def send_open_report(self, report):
348 try:
349 keys = self.load_report(report)
350 except IOError:
351 return self.send_error(400, 'Invalid report.')
352
353 file = keys.get('FILE')
354 if not file or not posixpath.exists(file):
355 return self.send_error(400, 'File does not exist: "%s"' % file)
356
357 import startfile
358 if self.server.options.debug:
359 print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
360 file)
361
362 status = startfile.open(file)
363 if status:
364 res = 'Opened: "%s"' % file
365 else:
366 res = 'Open failed: "%s"' % file
367
368 return self.send_string(res, 'text/plain')
369
Daniel Dunbare33d3682008-09-19 23:32:11 +0000370 def send_report(self, report):
371 try:
372 keys = self.load_report(report)
373 except IOError:
Daniel Dunbar7345e472008-09-22 01:40:14 +0000374 return self.send_error(400, 'Invalid report.')
Daniel Dunbare33d3682008-09-19 23:32:11 +0000375
376 initialTitle = keys.get('DESC','')
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000377 initialDescription = """\
378Bug generated by the clang static analyzer.
Daniel Dunbare33d3682008-09-19 23:32:11 +0000379
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000380Description: %s
381File: %s
382Line: %s
383"""%(initialTitle,
384 keys.get('FILE','<unknown>'),
385 keys.get('LINE','<unknown>'))
386
Daniel Dunbare33d3682008-09-19 23:32:11 +0000387 reporterSelections = []
388 reporterOptions = []
389
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000390 try:
391 active = self.server.config.getint('ScanView','reporter')
392 except:
393 active = 0
Daniel Dunbare33d3682008-09-19 23:32:11 +0000394 for i,r in enumerate(self.server.reporters):
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000395 selected = (i == active)
396 if selected:
397 selectedStr = ' selected'
398 else:
399 selectedStr = ''
400 reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
Daniel Dunbar78266292008-09-22 00:11:51 +0000401 options = '\n'.join(["""\
402<tr>
403 <td class="form_clabel">%s:</td>
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000404 <td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
405</tr>"""%(o,r.getName(),o,self.server.config.get(r.getName(), o)) for o in r.getParameterNames()])
406 display = ('none','')[selected]
Daniel Dunbar78266292008-09-22 00:11:51 +0000407 reporterOptions.append("""\
408<tr id="%sReporterOptions" style="display:%s">
409 <td class="form_label">%s Options</td>
410 <td class="form_value">
411 <table class="form_inner_group">
412%s
413 </table>
414 </td>
415</tr>
416"""%(r.getName(),display,r.getName(),options))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000417 reporterSelections = '\n'.join(reporterSelections)
418 reporterOptionsDivs = '\n'.join(reporterOptions)
419 reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
420
421 result = """<html>
422<head>
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000423 <title>File Bug</title>
Daniel Dunbar78266292008-09-22 00:11:51 +0000424 <link rel="stylesheet" type="text/css" href="/scanview.css" />
Daniel Dunbare33d3682008-09-19 23:32:11 +0000425</head>
426<script language="javascript" type="text/javascript">
427var reporters = %(reportersArray)s;
428function updateReporterOptions() {
429 index = document.getElementById('reporter').selectedIndex;
430 for (var i=0; i < reporters.length; ++i) {
431 o = document.getElementById(reporters[i] + "ReporterOptions");
432 if (i == index) {
Daniel Dunbar78266292008-09-22 00:11:51 +0000433 o.style.display = "";
Daniel Dunbare33d3682008-09-19 23:32:11 +0000434 } else {
435 o.style.display = "none";
436 }
437 }
438}
439</script>
Daniel Dunbar7345e472008-09-22 01:40:14 +0000440<body onLoad="updateReporterOptions()">
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000441<h3>
442<a href="/">Summary</a> >
443<a href="/report-%(report)s.html">Report %(report)s</a> >
444File Bug</h3>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000445<form name="form" action="/report_submit" method="post">
Daniel Dunbar78266292008-09-22 00:11:51 +0000446<input type="hidden" name="report" value="%(report)s">
447
448<table class="form">
449<tr><td>
450<table class="form_group">
451<tr>
452 <td class="form_clabel">Title:</td>
453 <td class="form_value">
454 <input type="text" name="title" size="50" value="%(initialTitle)s">
455 </td>
456</tr>
457<tr>
458 <td class="form_label">Description:</td>
459 <td class="form_value">
Daniel Dunbare33d3682008-09-19 23:32:11 +0000460<textarea rows="10" cols="80" name="description">
461%(initialDescription)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000462</textarea>
463 </td>
464</table>
465<br>
466<table class="form_group">
467<tr>
468 <td class="form_clabel">Method:</td>
469 <td class="form_value">
470 <select id="reporter" name="reporter" onChange="updateReporterOptions()">
471 %(reporterSelections)s
472 </select>
473 </td>
474</tr>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000475%(reporterOptionsDivs)s
Daniel Dunbar78266292008-09-22 00:11:51 +0000476</table>
477<br>
478</td></tr>
479<tr><td class="form_submit">
480 <input align="right" type="submit" name="Submit" value="Submit">
481</td></tr>
482</table>
Daniel Dunbare33d3682008-09-19 23:32:11 +0000483</form>
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000484
Daniel Dunbarc2dd3452008-09-22 03:08:32 +0000485<iframe src="/report-%(report)s.html" width="100%%" height="40%%"
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000486 scrolling="auto" frameborder="1">
Daniel Dunbar8dcd9402008-09-22 02:27:45 +0000487 <a href="/report-%(report)s.html">View Bug Report</a>
Daniel Dunbar25b51d02008-09-21 19:08:54 +0000488</iframe>
489
Daniel Dunbare33d3682008-09-19 23:32:11 +0000490</body>
491</html>"""%locals()
492 return self.send_string(result)
493
494 def send_head(self, fields=None):
495 if fields is None:
496 fields = {}
497 self.fields = fields
498
499 o = urlparse.urlparse(self.path)
500 self.fields = parse_query(o.query, fields)
501 path = posixpath.normpath(urllib.unquote(o.path))
502
503 # Split the components and strip the root prefix.
504 components = path.split('/')[1:]
505
506 # Special case some top-level entries.
507 if components:
508 name = components[0]
Daniel Dunbar78266292008-09-22 00:11:51 +0000509 if len(components)==2:
510 if name=='report':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000511 return self.send_report(components[1])
Daniel Dunbar28870e12008-09-22 18:05:49 +0000512 elif name=='open':
513 return self.send_open_report(components[1])
Daniel Dunbar78266292008-09-22 00:11:51 +0000514 elif len(components)==1:
515 if name=='quit':
516 self.server.halt()
517 return self.send_string('Goodbye.', 'text/plain')
518 elif name=='report_submit':
Daniel Dunbare33d3682008-09-19 23:32:11 +0000519 return self.send_report_submit()
Daniel Dunbar78266292008-09-22 00:11:51 +0000520 elif name=='favicon.ico':
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000521 return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000522
523 # Match directory entries.
524 if components[-1] == '':
525 components[-1] = 'index.html'
Daniel Dunbarb131c8a2008-09-21 23:02:25 +0000526
527 suffix = '/'.join(components)
528
529 # The summary may reference source files on disk using rooted
530 # paths. Make sure these resolve correctly for now.
531 # FIXME: This isn't a very good idea... we should probably
532 # mark rooted paths somehow.
533 if os.path.exists(posixpath.join('/', suffix)):
534 path = posixpath.join('/', suffix)
535 else:
536 path = posixpath.join(self.server.root, suffix)
537
Daniel Dunbare33d3682008-09-19 23:32:11 +0000538 if self.server.options.debug > 1:
539 print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
540 path)
541 return self.send_path(path)
542
543 def send_404(self):
544 self.send_error(404, "File not found")
545 return None
546
547 def send_path(self, path):
548 ctype = self.guess_type(path)
549 if ctype.startswith('text/'):
550 # Patch file instead
551 return self.send_patched_file(path, ctype)
552 else:
553 mode = 'rb'
554 try:
555 f = open(path, mode)
556 except IOError:
557 return self.send_404()
558 return self.send_file(f, ctype)
559
560 def send_file(self, f, ctype):
561 # Patch files to add links, but skip binary files.
562 self.send_response(200)
563 self.send_header("Content-type", ctype)
564 fs = os.fstat(f.fileno())
565 self.send_header("Content-Length", str(fs[6]))
566 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
567 self.end_headers()
568 return f
569
570 def send_string(self, s, ctype='text/html', headers=True, mtime=None):
571 if headers:
572 self.send_response(200)
573 self.send_header("Content-type", ctype)
574 self.send_header("Content-Length", str(len(s)))
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000575 if mtime is None:
576 mtime = self.dynamic_mtime
577 self.send_header("Last-Modified", self.date_time_string(mtime))
Daniel Dunbare33d3682008-09-19 23:32:11 +0000578 self.end_headers()
579 return StringIO.StringIO(s)
580
581 def send_patched_file(self, path, ctype):
Daniel Dunbar17fded62008-09-22 21:43:43 +0000582 # Allow a very limited set of variables. This is pretty gross.
583 variables = {}
584 variables['report'] = ''
585 m = kReportFileRE.match(path)
586 if m:
587 variables['report'] = m.group(2)
588
Daniel Dunbar7345e472008-09-22 01:40:14 +0000589 try:
590 f = open(path,'r')
591 except IOError:
592 return self.send_404()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000593 fs = os.fstat(f.fileno())
594 data = f.read()
Daniel Dunbarcb028b02008-09-21 20:34:58 +0000595 for a,b in kReportReplacements:
Daniel Dunbar17fded62008-09-22 21:43:43 +0000596 data = a.sub(b % variables, data)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000597 return self.send_string(data, ctype, mtime=fs.st_mtime)
598
599
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000600def create_server(address, options, root):
Daniel Dunbare33d3682008-09-19 23:32:11 +0000601 import Reporter
602
603 reporters = Reporter.getReporters()
604
Daniel Dunbar7ad535d2008-09-22 02:53:12 +0000605 return ScanViewServer(address, ScanViewRequestHandler,
Daniel Dunbare33d3682008-09-19 23:32:11 +0000606 root,
607 reporters,
608 options)