- from __future__ import print_function 
- try: 
-     from http.server import HTTPServer, SimpleHTTPRequestHandler 
- except ImportError: 
-     from BaseHTTPServer import HTTPServer 
-     from SimpleHTTPServer import SimpleHTTPRequestHandler 
- import os 
- import sys 
- try: 
-     from urlparse import urlparse 
-     from urllib import unquote 
- except ImportError: 
-     from urllib.parse import urlparse, unquote 
-   
- import posixpath 
-   
- if sys.version_info.major >= 3: 
-     from io import StringIO, BytesIO 
- else: 
-     from io import BytesIO, BytesIO as StringIO 
-   
- import re 
- import shutil 
- import threading 
- import time 
- import socket 
- import itertools 
-   
- import Reporter 
- try: 
-     import configparser 
- except ImportError: 
-     import ConfigParser as configparser 
-   
- ### 
- # Various patterns matched or replaced by server. 
-   
- kReportFileRE = re.compile('(.*/)?report-(.*)\\.html') 
-   
- kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') 
-   
- #  <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> 
-   
- kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') 
- kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') 
-   
- kReportReplacements = [] 
-   
- # Add custom javascript. 
- kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ 
- <script language="javascript" type="text/javascript"> 
- function load(url) { 
-   if (window.XMLHttpRequest) { 
-     req = new XMLHttpRequest(); 
-   } else if (window.ActiveXObject) { 
-     req = new ActiveXObject("Microsoft.XMLHTTP"); 
-   } 
-   if (req != undefined) { 
-     req.open("GET", url, true); 
-     req.send(""); 
-   } 
- } 
- </script>""")) 
-   
- # Insert additional columns. 
- kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),  
-                             '<td></td><td></td>')) 
-   
- # Insert report bug and open file links. 
- kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), 
-                             ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +  
-                              '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) 
-   
- kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), 
-                                        '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) 
-   
- kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), 
-                             '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) 
-   
- # Insert report crashes link. 
-   
- # Disabled for the time being until we decide exactly when this should 
- # be enabled. Also the radar reporter needs to be fixed to report 
- # multiple files. 
-   
- #kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), 
- #                            '<br>These files will automatically be attached to ' + 
- #                            'reports filed here: <a href="report_crashes">Report Crashes</a>.')) 
-   
- ### 
- # Other simple parameters 
-   
- kShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view') 
- kConfigPath = os.path.expanduser('~/.scanview.cfg') 
-   
- ### 
-   
- __version__ = "0.1" 
-   
- __all__ = ["create_server"] 
-   
- class ReporterThread(threading.Thread): 
-     def __init__(self, report, reporter, parameters, server): 
-         threading.Thread.__init__(self) 
-         self.report = report 
-         self.server = server 
-         self.reporter = reporter 
-         self.parameters = parameters 
-         self.success = False 
-         self.status = None 
-   
-     def run(self): 
-         result = None 
-         try: 
-             if self.server.options.debug: 
-                 print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr) 
-             self.status = self.reporter.fileReport(self.report, self.parameters) 
-             self.success = True 
-             time.sleep(3) 
-             if self.server.options.debug: 
-                 print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr) 
-         except Reporter.ReportFailure as e: 
-             self.status = e.value 
-         except Exception as e: 
-             s = StringIO() 
-             import traceback 
-             print('<b>Unhandled Exception</b><br><pre>', file=s) 
-             traceback.print_exc(file=s) 
-             print('</pre>', file=s) 
-             self.status = s.getvalue() 
-   
- class ScanViewServer(HTTPServer): 
-     def __init__(self, address, handler, root, reporters, options): 
-         HTTPServer.__init__(self, address, handler) 
-         self.root = root 
-         self.reporters = reporters 
-         self.options = options         
-         self.halted = False 
-         self.config = None 
-         self.load_config() 
-   
-     def load_config(self): 
-         self.config = configparser.RawConfigParser() 
-   
-         # Add defaults 
-         self.config.add_section('ScanView') 
-         for r in self.reporters: 
-             self.config.add_section(r.getName()) 
-             for p in r.getParameters(): 
-               if p.saveConfigValue(): 
-                 self.config.set(r.getName(), p.getName(), '') 
-   
-         # Ignore parse errors 
-         try: 
-             self.config.read([kConfigPath]) 
-         except: 
-             pass 
-   
-         # Save on exit 
-         import atexit 
-         atexit.register(lambda: self.save_config()) 
-          
-     def save_config(self): 
-         # Ignore errors (only called on exit). 
-         try: 
-             f = open(kConfigPath,'w') 
-             self.config.write(f) 
-             f.close() 
-         except: 
-             pass 
-          
-     def halt(self): 
-         self.halted = True 
-         if self.options.debug: 
-             print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) 
-   
-     def serve_forever(self): 
-         while not self.halted: 
-             if self.options.debug > 1: 
-                 print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) 
-             try: 
-                 self.handle_request() 
-             except OSError as e: 
-                 print('OSError',e.errno) 
-   
-     def finish_request(self, request, client_address): 
-         if self.options.autoReload: 
-             import ScanView 
-             self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler 
-         HTTPServer.finish_request(self, request, client_address) 
-   
-     def handle_error(self, request, client_address): 
-         # Ignore socket errors 
-         info = sys.exc_info() 
-         if info and isinstance(info[1], socket.error): 
-             if self.options.debug > 1: 
-                 print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr) 
-             return 
-         HTTPServer.handle_error(self, request, client_address) 
-   
- # Borrowed from Quixote, with simplifications. 
- def parse_query(qs, fields=None): 
-     if fields is None: 
-         fields = {} 
-     for chunk in (_f for _f in qs.split('&') if _f): 
-         if '=' not in chunk: 
-             name = chunk 
-             value = '' 
-         else: 
-             name, value = chunk.split('=', 1) 
-         name = unquote(name.replace('+', ' ')) 
-         value = unquote(value.replace('+', ' ')) 
-         item = fields.get(name) 
-         if item is None: 
-             fields[name] = [value] 
-         else: 
-             item.append(value) 
-     return fields 
-   
- class ScanViewRequestHandler(SimpleHTTPRequestHandler): 
-     server_version = "ScanViewServer/" + __version__ 
-     dynamic_mtime = time.time() 
-   
-     def do_HEAD(self): 
-         try: 
-             SimpleHTTPRequestHandler.do_HEAD(self) 
-         except Exception as e: 
-             self.handle_exception(e) 
-              
-     def do_GET(self): 
-         try: 
-             SimpleHTTPRequestHandler.do_GET(self) 
-         except Exception as e: 
-             self.handle_exception(e) 
-              
-     def do_POST(self): 
-         """Serve a POST request.""" 
-         try: 
-             length = self.headers.getheader('content-length') or "0" 
-             try: 
-                 length = int(length) 
-             except: 
-                 length = 0 
-             content = self.rfile.read(length) 
-             fields = parse_query(content) 
-             f = self.send_head(fields) 
-             if f: 
-                 self.copyfile(f, self.wfile) 
-                 f.close() 
-         except Exception as e: 
-             self.handle_exception(e)             
-   
-     def log_message(self, format, *args): 
-         if self.server.options.debug: 
-             sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % 
-                              (sys.argv[0], 
-                               self.address_string(), 
-                               self.log_date_time_string(), 
-                               format%args)) 
-   
-     def load_report(self, report): 
-         path = os.path.join(self.server.root, 'report-%s.html'%report) 
-         data = open(path).read() 
-         keys = {} 
-         for item in kBugKeyValueRE.finditer(data): 
-             k,v = item.groups() 
-             keys[k] = v 
-         return keys 
-   
-     def load_crashes(self): 
-         path = posixpath.join(self.server.root, 'index.html') 
-         data = open(path).read() 
-         problems = [] 
-         for item in kReportCrashEntryRE.finditer(data): 
-             fieldData = item.group(1) 
-             fields = dict([i.groups() for i in  
-                            kReportCrashEntryKeyValueRE.finditer(fieldData)]) 
-             problems.append(fields) 
-         return problems 
-   
-     def handle_exception(self, exc): 
-         import traceback 
-         s = StringIO() 
-         print("INTERNAL ERROR\n", file=s) 
-         traceback.print_exc(file=s) 
-         f = self.send_string(s.getvalue(), 'text/plain') 
-         if f: 
-             self.copyfile(f, self.wfile) 
-             f.close()         
-              
-     def get_scalar_field(self, name): 
-         if name in self.fields: 
-             return self.fields[name][0] 
-         else: 
-             return None 
-   
-     def submit_bug(self, c): 
-         title = self.get_scalar_field('title') 
-         description = self.get_scalar_field('description') 
-         report = self.get_scalar_field('report') 
-         reporterIndex = self.get_scalar_field('reporter') 
-         files = [] 
-         for fileID in self.fields.get('files',[]): 
-             try: 
-                 i = int(fileID) 
-             except: 
-                 i = None 
-             if i is None or i<0 or i>=len(c.files): 
-                 return (False, 'Invalid file ID') 
-             files.append(c.files[i]) 
-          
-         if not title: 
-             return (False, "Missing title.") 
-         if not description: 
-             return (False, "Missing description.") 
-         try: 
-             reporterIndex = int(reporterIndex) 
-         except: 
-             return (False, "Invalid report method.") 
-          
-         # Get the reporter and parameters. 
-         reporter = self.server.reporters[reporterIndex] 
-         parameters = {} 
-         for o in reporter.getParameters(): 
-             name = '%s_%s'%(reporter.getName(),o.getName()) 
-             if name not in self.fields: 
-                 return (False,  
-                         'Missing field "%s" for %s report method.'%(name, 
-                                                                     reporter.getName())) 
-             parameters[o.getName()] = self.get_scalar_field(name) 
-   
-         # Update config defaults. 
-         if report != 'None': 
-             self.server.config.set('ScanView', 'reporter', reporterIndex) 
-             for o in reporter.getParameters(): 
-               if o.saveConfigValue(): 
-                 name = o.getName() 
-                 self.server.config.set(reporter.getName(), name, parameters[name]) 
-   
-         # Create the report. 
-         bug = Reporter.BugReport(title, description, files) 
-   
-         # Kick off a reporting thread. 
-         t = ReporterThread(bug, reporter, parameters, self.server) 
-         t.start() 
-   
-         # Wait for thread to die... 
-         while t.isAlive(): 
-             time.sleep(.25) 
-         submitStatus = t.status 
-   
-         return (t.success, t.status) 
-   
-     def send_report_submit(self): 
-         report = self.get_scalar_field('report') 
-         c = self.get_report_context(report) 
-         if c.reportSource is None: 
-             reportingFor = "Report Crashes > " 
-             fileBug = """\ 
- <a href="/report_crashes">File Bug</a> > """%locals() 
-         else: 
-             reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,  
-                                                                    report) 
-             fileBug = '<a href="/report/%s">File Bug</a> > ' % report 
-         title = self.get_scalar_field('title') 
-         description = self.get_scalar_field('description') 
-   
-         res,message = self.submit_bug(c) 
-   
-         if res: 
-             statusClass = 'SubmitOk' 
-             statusName = 'Succeeded' 
-         else: 
-             statusClass = 'SubmitFail' 
-             statusName = 'Failed' 
-   
-         result = """ 
- <head> 
-   <title>Bug Submission</title> 
-   <link rel="stylesheet" type="text/css" href="/scanview.css" /> 
- </head> 
- <body> 
- <h3> 
- <a href="/">Summary</a> >  
- %(reportingFor)s 
- %(fileBug)s 
- Submit</h3> 
- <form name="form" action=""> 
- <table class="form"> 
- <tr><td> 
- <table class="form_group"> 
- <tr> 
-   <td class="form_clabel">Title:</td> 
-   <td class="form_value"> 
-     <input type="text" name="title" size="50" value="%(title)s" disabled> 
-   </td> 
- </tr> 
- <tr> 
-   <td class="form_label">Description:</td> 
-   <td class="form_value"> 
- <textarea rows="10" cols="80" name="description" disabled> 
- %(description)s 
- </textarea> 
-   </td> 
- </table> 
- </td></tr> 
- </table> 
- </form> 
- <h1 class="%(statusClass)s">Submission %(statusName)s</h1> 
- %(message)s 
- <p> 
- <hr> 
- <a href="/">Return to Summary</a> 
- </body> 
- </html>"""%locals() 
-         return self.send_string(result) 
-   
-     def send_open_report(self, report): 
-         try: 
-             keys = self.load_report(report) 
-         except IOError: 
-             return self.send_error(400, 'Invalid report.') 
-   
-         file = keys.get('FILE') 
-         if not file or not posixpath.exists(file): 
-             return self.send_error(400, 'File does not exist: "%s"' % file) 
-   
-         import startfile 
-         if self.server.options.debug: 
-             print('%s: SERVER: opening "%s"'%(sys.argv[0], 
-                                                             file), file=sys.stderr) 
-   
-         status = startfile.open(file) 
-         if status: 
-             res = 'Opened: "%s"' % file 
-         else: 
-             res = 'Open failed: "%s"' % file 
-   
-         return self.send_string(res, 'text/plain') 
-   
-     def get_report_context(self, report): 
-         class Context(object): 
-             pass 
-         if report is None or report == 'None': 
-             data = self.load_crashes() 
-             # Don't allow empty reports. 
-             if not data: 
-                 raise ValueError('No crashes detected!') 
-             c = Context() 
-             c.title = 'clang static analyzer failures' 
-   
-             stderrSummary = "" 
-             for item in data: 
-                 if 'stderr' in item: 
-                     path = posixpath.join(self.server.root, item['stderr']) 
-                     if os.path.exists(path): 
-                         lns = itertools.islice(open(path), 0, 10) 
-                         stderrSummary += '%s\n--\n%s' % (item.get('src',  
-                                                                   '<unknown>'), 
-                                                          ''.join(lns)) 
-   
-             c.description = """\ 
- The clang static analyzer failed on these inputs: 
- %s 
-   
- STDERR Summary 
- -------------- 
- %s 
- """ % ('\n'.join([item.get('src','<unknown>') for item in data]), 
-        stderrSummary) 
-             c.reportSource = None 
-             c.navMarkup = "Report Crashes > " 
-             c.files = [] 
-             for item in data:                 
-                 c.files.append(item.get('src','')) 
-                 c.files.append(posixpath.join(self.server.root, 
-                                               item.get('file',''))) 
-                 c.files.append(posixpath.join(self.server.root, 
-                                               item.get('clangfile',''))) 
-                 c.files.append(posixpath.join(self.server.root, 
-                                               item.get('stderr',''))) 
-                 c.files.append(posixpath.join(self.server.root, 
-                                               item.get('info',''))) 
-             # Just in case something failed, ignore files which don't 
-             # exist. 
-             c.files = [f for f in c.files 
-                        if os.path.exists(f) and os.path.isfile(f)] 
-         else: 
-             # Check that this is a valid report.             
-             path = posixpath.join(self.server.root, 'report-%s.html' % report) 
-             if not posixpath.exists(path): 
-                 raise ValueError('Invalid report ID') 
-             keys = self.load_report(report) 
-             c = Context() 
-             c.title = keys.get('DESC','clang error (unrecognized') 
-             c.description = """\ 
- Bug reported by the clang static analyzer. 
-   
- Description: %s 
- File: %s 
- Line: %s 
- """%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) 
-             c.reportSource = 'report-%s.html' % report 
-             c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, 
-                                                                   report) 
-   
-             c.files = [path] 
-         return c 
-   
-     def send_report(self, report, configOverrides=None): 
-         def getConfigOption(section, field):             
-             if (configOverrides is not None and 
-                 section in configOverrides and 
-                 field in configOverrides[section]): 
-                 return configOverrides[section][field] 
-             return self.server.config.get(section, field) 
-   
-         # report is None is used for crashes 
-         try: 
-             c = self.get_report_context(report) 
-         except ValueError as e: 
-             return self.send_error(400, e.message) 
-   
-         title = c.title 
-         description= c.description 
-         reportingFor = c.navMarkup 
-         if c.reportSource is None: 
-             extraIFrame = "" 
-         else: 
-             extraIFrame = """\ 
- <iframe src="/%s" width="100%%" height="40%%" 
-         scrolling="auto" frameborder="1"> 
-   <a href="/%s">View Bug Report</a> 
- </iframe>""" % (c.reportSource, c.reportSource) 
-   
-         reporterSelections = [] 
-         reporterOptions = [] 
-   
-         try: 
-             active = int(getConfigOption('ScanView','reporter')) 
-         except: 
-             active = 0 
-         for i,r in enumerate(self.server.reporters): 
-             selected = (i == active) 
-             if selected: 
-                 selectedStr = ' selected' 
-             else: 
-                 selectedStr = '' 
-             reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) 
-             options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) 
-             display = ('none','')[selected] 
-             reporterOptions.append("""\ 
- <tr id="%sReporterOptions" style="display:%s"> 
-   <td class="form_label">%s Options</td> 
-   <td class="form_value"> 
-     <table class="form_inner_group"> 
- %s 
-     </table> 
-   </td> 
- </tr> 
- """%(r.getName(),display,r.getName(),options)) 
-         reporterSelections = '\n'.join(reporterSelections) 
-         reporterOptionsDivs = '\n'.join(reporterOptions) 
-         reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters])) 
-   
-         if c.files: 
-             fieldSize = min(5, len(c.files)) 
-             attachFileOptions = '\n'.join(["""\ 
- <option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) 
-             attachFileRow = """\ 
- <tr> 
-   <td class="form_label">Attach:</td> 
-   <td class="form_value"> 
- <select style="width:100%%" name="files" multiple size=%d> 
- %s 
- </select> 
-   </td> 
- </tr> 
- """ % (min(5, len(c.files)), attachFileOptions) 
-         else: 
-             attachFileRow = "" 
-   
-         result = """<html> 
- <head> 
-   <title>File Bug</title> 
-   <link rel="stylesheet" type="text/css" href="/scanview.css" /> 
- </head> 
- <script language="javascript" type="text/javascript"> 
- var reporters = %(reportersArray)s; 
- function updateReporterOptions() { 
-   index = document.getElementById('reporter').selectedIndex; 
-   for (var i=0; i < reporters.length; ++i) { 
-     o = document.getElementById(reporters[i] + "ReporterOptions"); 
-     if (i == index) { 
-       o.style.display = ""; 
-     } else { 
-       o.style.display = "none"; 
-     } 
-   } 
- } 
- </script> 
- <body onLoad="updateReporterOptions()"> 
- <h3> 
- <a href="/">Summary</a> >  
- %(reportingFor)s 
- File Bug</h3> 
- <form name="form" action="/report_submit" method="post"> 
- <input type="hidden" name="report" value="%(report)s"> 
-   
- <table class="form"> 
- <tr><td> 
- <table class="form_group"> 
- <tr> 
-   <td class="form_clabel">Title:</td> 
-   <td class="form_value"> 
-     <input type="text" name="title" size="50" value="%(title)s"> 
-   </td> 
- </tr> 
- <tr> 
-   <td class="form_label">Description:</td> 
-   <td class="form_value"> 
- <textarea rows="10" cols="80" name="description"> 
- %(description)s 
- </textarea> 
-   </td> 
- </tr> 
-   
- %(attachFileRow)s 
-   
- </table> 
- <br> 
- <table class="form_group"> 
- <tr> 
-   <td class="form_clabel">Method:</td> 
-   <td class="form_value"> 
-     <select id="reporter" name="reporter" onChange="updateReporterOptions()"> 
-     %(reporterSelections)s 
-     </select> 
-   </td> 
- </tr> 
- %(reporterOptionsDivs)s 
- </table> 
- <br> 
- </td></tr> 
- <tr><td class="form_submit"> 
-   <input align="right" type="submit" name="Submit" value="Submit"> 
- </td></tr> 
- </table> 
- </form> 
-   
- %(extraIFrame)s 
-   
- </body> 
- </html>"""%locals() 
-   
-         return self.send_string(result) 
-   
-     def send_head(self, fields=None): 
-         if (self.server.options.onlyServeLocal and 
-             self.client_address[0] != '127.0.0.1'): 
-             return self.send_error(401, 'Unauthorized host.') 
-   
-         if fields is None: 
-             fields = {} 
-         self.fields = fields 
-   
-         o = urlparse(self.path) 
-         self.fields = parse_query(o.query, fields) 
-         path = posixpath.normpath(unquote(o.path)) 
-   
-         # Split the components and strip the root prefix. 
-         components = path.split('/')[1:] 
-          
-         # Special case some top-level entries. 
-         if components: 
-             name = components[0] 
-             if len(components)==2: 
-                 if name=='report': 
-                     return self.send_report(components[1]) 
-                 elif name=='open': 
-                     return self.send_open_report(components[1]) 
-             elif len(components)==1: 
-                 if name=='quit': 
-                     self.server.halt() 
-                     return self.send_string('Goodbye.', 'text/plain') 
-                 elif name=='report_submit': 
-                     return self.send_report_submit() 
-                 elif name=='report_crashes': 
-                     overrides = { 'ScanView' : {}, 
-                                   'Radar' : {}, 
-                                   'Email' : {} } 
-                     for i,r in enumerate(self.server.reporters): 
-                         if r.getName() == 'Radar': 
-                             overrides['ScanView']['reporter'] = i 
-                             break 
-                     overrides['Radar']['Component'] = 'llvm - checker' 
-                     overrides['Radar']['Component Version'] = 'X' 
-                     return self.send_report(None, overrides) 
-                 elif name=='favicon.ico': 
-                     return self.send_path(posixpath.join(kShare,'bugcatcher.ico')) 
-          
-         # Match directory entries. 
-         if components[-1] == '': 
-             components[-1] = 'index.html' 
-   
-         relpath = '/'.join(components) 
-         path = posixpath.join(self.server.root, relpath) 
-   
-         if self.server.options.debug > 1: 
-             print('%s: SERVER: sending path "%s"'%(sys.argv[0], 
-                                                                  path), file=sys.stderr) 
-         return self.send_path(path) 
-   
-     def send_404(self): 
-         self.send_error(404, "File not found") 
-         return None 
-   
-     def send_path(self, path): 
-         # If the requested path is outside the root directory, do not open it 
-         rel = os.path.abspath(path) 
-         if not rel.startswith(os.path.abspath(self.server.root)): 
-           return self.send_404() 
-          
-         ctype = self.guess_type(path) 
-         if ctype.startswith('text/'): 
-             # Patch file instead 
-             return self.send_patched_file(path, ctype) 
-         else: 
-             mode = 'rb' 
-         try: 
-             f = open(path, mode) 
-         except IOError: 
-             return self.send_404() 
-         return self.send_file(f, ctype) 
-   
-     def send_file(self, f, ctype): 
-         # Patch files to add links, but skip binary files. 
-         self.send_response(200) 
-         self.send_header("Content-type", ctype) 
-         fs = os.fstat(f.fileno()) 
-         self.send_header("Content-Length", str(fs[6])) 
-         self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) 
-         self.end_headers() 
-         return f 
-   
-     def send_string(self, s, ctype='text/html', headers=True, mtime=None): 
-         encoded_s = s.encode('utf-8') 
-         if headers: 
-             self.send_response(200) 
-             self.send_header("Content-type", ctype) 
-             self.send_header("Content-Length", str(len(encoded_s))) 
-             if mtime is None: 
-                 mtime = self.dynamic_mtime 
-             self.send_header("Last-Modified", self.date_time_string(mtime)) 
-             self.end_headers() 
-         return BytesIO(encoded_s) 
-   
-     def send_patched_file(self, path, ctype): 
-         # Allow a very limited set of variables. This is pretty gross. 
-         variables = {} 
-         variables['report'] = '' 
-         m = kReportFileRE.match(path) 
-         if m: 
-             variables['report'] = m.group(2) 
-   
-         try: 
-             f = open(path,'rb') 
-         except IOError: 
-             return self.send_404() 
-         fs = os.fstat(f.fileno()) 
-         data = f.read().decode('utf-8') 
-         for a,b in kReportReplacements: 
-             data = a.sub(b % variables, data) 
-         return self.send_string(data, ctype, mtime=fs.st_mtime) 
-   
-   
- def create_server(address, options, root): 
-     import Reporter 
-   
-     reporters = Reporter.getReporters() 
-   
-     return ScanViewServer(address, ScanViewRequestHandler, 
-                           root, 
-                           reporters, 
-                           options) 
-