import BaseHTTPServer import SimpleHTTPServer import os import sys import urllib, urlparse import posixpath import StringIO import re import shutil import threading import time import socket import Reporter # Keys replaced by server. kReportBugRE = re.compile('') kReportBugRepl = 'Report Bug' kBugKeyValueRE = re.compile('') ### __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.status = None def run(self): result = None try: if self.server.options.debug: print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],) result = self.reporter.fileReport(self.report, self.parameters) time.sleep(3) if self.server.options.debug: print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],) except Reporter.ReportFailure,e: s = StringIO.StringIO() print >>s,'Submission Failed
'
            print >>s,e.value
            print >>s,'
' self.status = s.getvalue() return except Exception,e: s = StringIO.StringIO() import traceback print >>s,'Submission Failed
'
            traceback.print_exc(e,file=s)
            print >>s,'
' self.status = s.getvalue() return s = StringIO.StringIO() print >>s, 'Submission Complete!' if result is not None: print >>s, '
' print >>s, result self.status = s.getvalue() class ScanViewServer(BaseHTTPServer.HTTPServer): def __init__(self, address, handler, root, reporters, options): BaseHTTPServer.HTTPServer.__init__(self, address, handler) self.root = root self.reporters = reporters self.options = options self.halted = False def halt(self): self.halted = True if self.options.debug: print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],) def serve_forever(self): while not self.halted: if self.options.debug > 1: print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],) try: self.handle_request() except OSError,e: print 'OSError',e.errno 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 >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],) return BaseHTTPServer.HTTPServer.handle_error(request, client_address) # Borrowed from Quixote, with simplifications. def parse_query(qs, fields=None): if fields is None: fields = {} for chunk in filter(None, qs.split('&')): if '=' not in chunk: name = chunk value = '' else: name, value = chunk.split('=', 1) name = urllib.unquote(name.replace('+', ' ')) value = urllib.unquote(value.replace('+', ' ')) fields[name] = value return fields class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): server_version = "ScanViewServer/" + __version__ def do_HEAD(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self) except Exception,e: self.handle_exception(e) def do_GET(self): try: SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) except Exception,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,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 handle_exception(self, exc): import traceback s = StringIO.StringIO() print >>s, "INTERNAL ERROR\n" traceback.print_exc(exc, s) f = self.send_string(s.getvalue(), 'text/plain') if f: self.copyfile(f, self.wfile) f.close() def send_internal_error(self, message): return self.send_string('ERROR: %s'%(message,), 'text/plain') def send_report_submit(self): s = StringIO.StringIO() report = self.fields.get('report') reporter = self.fields.get('reporter') title = self.fields.get('title') description = self.fields.get('description') # Get the reporter and parameters. reporter = self.server.reporters[int(reporter)] parameters = {} for o in reporter.getParameterNames(): name = '%s_%s'%(reporter.getName(),o) parameters[o] = self.fields.get(name) # Create the report. path = os.path.join(self.server.root, 'report-%s.html'%report) files = [path] br = Reporter.BugReport(title, description, files) # Send back an initial response and wait for the report to # finish. initial_response = """ Filing Report

Filing Report

Report: %(report)s
Title: %(title)s
Description: %(description)s

Submission in progress."""%locals() self.send_response(200) self.send_header("Content-type", 'text/html') self.end_headers() self.wfile.write(initial_response) self.wfile.flush() # Kick off a reporting thread. t = ReporterThread(br, reporter, parameters, self.server) t.start() # Wait for thread to die... while t.isAlive(): self.wfile.write('.') self.wfile.flush() time.sleep(.25) submitStatus = t.status end_response = """
%(submitStatus)s
Home """%locals() return self.send_string(end_response, headers=False) def send_report(self, report): try: keys = self.load_report(report) except IOError: return self.send_internal_error('Invalid report.') initialTitle = keys.get('DESC','') initialDescription = """\ Bug generated by the clang static analyzer. Description: %s File: %s Line: %s """%(initialTitle, keys.get('FILE',''), keys.get('LINE','')) reporterSelections = [] reporterOptions = [] for i,r in enumerate(self.server.reporters): reporterSelections.append(''%(i,r.getName())) options = '\n'.join(['%s:
'%(o,r.getName(),o) for o in r.getParameterNames()]) if i==0: display = 'inline' else: display = 'none' reporterOptions.append('
\n

%s Options

%s\n
'%(r.getName(),display,r.getName(),options)) reporterSelections = '\n'.join(reporterSelections) reporterOptionsDivs = '\n'.join(reporterOptions) reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters])) result = """ File Report

File Report


Title:
Description:


Method:

%(reporterOptionsDivs)s
"""%locals() return self.send_string(result) def send_head(self, fields=None): if fields is None: fields = {} self.fields = fields o = urlparse.urlparse(self.path) self.fields = parse_query(o.query, fields) path = posixpath.normpath(urllib.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 name=='quit': self.server.halt() return self.send_string('Goodbye.', 'text/plain') elif name=='report': if len(components)==2: return self.send_report(components[1]) else: return self.send_404() elif name=='report_submit': if len(components)==1: return self.send_report_submit() else: return self.send_404() # Match directory entries. if components[-1] == '': components[-1] = 'index.html' path = posixpath.join(self.server.root, '/'.join(components)) if self.server.options.debug > 1: print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0], path) return self.send_path(path) def send_404(self): self.send_error(404, "File not found") return None def send_path(self, path): 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): if headers: self.send_response(200) self.send_header("Content-type", ctype) self.send_header("Content-Length", str(len(s))) if mtime: self.send_header("Last-Modified", self.date_time_string(mtime)) self.end_headers() return StringIO.StringIO(s) def send_patched_file(self, path, ctype): f = open(path,'r') fs = os.fstat(f.fileno()) data = f.read() data = kReportBugRE.sub(kReportBugRepl, data) return self.send_string(data, ctype, mtime=fs.st_mtime) def create_server(options, root): import Reporter reporters = Reporter.getReporters() return ScanViewServer((options.host, options.port), ScanViewRequestHandler, root, reporters, options)