xref: /minix3/external/bsd/llvm/dist/clang/tools/scan-view/ScanView.py (revision f4a2713ac843a11c696ec80c0a5e3e5d80b4d338)
1*f4a2713aSLionel Sambucimport BaseHTTPServer
2*f4a2713aSLionel Sambucimport SimpleHTTPServer
3*f4a2713aSLionel Sambucimport os
4*f4a2713aSLionel Sambucimport sys
5*f4a2713aSLionel Sambucimport urllib, urlparse
6*f4a2713aSLionel Sambucimport posixpath
7*f4a2713aSLionel Sambucimport StringIO
8*f4a2713aSLionel Sambucimport re
9*f4a2713aSLionel Sambucimport shutil
10*f4a2713aSLionel Sambucimport threading
11*f4a2713aSLionel Sambucimport time
12*f4a2713aSLionel Sambucimport socket
13*f4a2713aSLionel Sambucimport itertools
14*f4a2713aSLionel Sambuc
15*f4a2713aSLionel Sambucimport Reporter
16*f4a2713aSLionel Sambucimport ConfigParser
17*f4a2713aSLionel Sambuc
18*f4a2713aSLionel Sambuc###
19*f4a2713aSLionel Sambuc# Various patterns matched or replaced by server.
20*f4a2713aSLionel Sambuc
21*f4a2713aSLionel SambuckReportFileRE = re.compile('(.*/)?report-(.*)\\.html')
22*f4a2713aSLionel Sambuc
23*f4a2713aSLionel SambuckBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->')
24*f4a2713aSLionel Sambuc
25*f4a2713aSLionel Sambuc#  <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" -->
26*f4a2713aSLionel Sambuc
27*f4a2713aSLionel SambuckReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->')
28*f4a2713aSLionel SambuckReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"')
29*f4a2713aSLionel Sambuc
30*f4a2713aSLionel SambuckReportReplacements = []
31*f4a2713aSLionel Sambuc
32*f4a2713aSLionel Sambuc# Add custom javascript.
33*f4a2713aSLionel SambuckReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\
34*f4a2713aSLionel Sambuc<script language="javascript" type="text/javascript">
35*f4a2713aSLionel Sambucfunction load(url) {
36*f4a2713aSLionel Sambuc  if (window.XMLHttpRequest) {
37*f4a2713aSLionel Sambuc    req = new XMLHttpRequest();
38*f4a2713aSLionel Sambuc  } else if (window.ActiveXObject) {
39*f4a2713aSLionel Sambuc    req = new ActiveXObject("Microsoft.XMLHTTP");
40*f4a2713aSLionel Sambuc  }
41*f4a2713aSLionel Sambuc  if (req != undefined) {
42*f4a2713aSLionel Sambuc    req.open("GET", url, true);
43*f4a2713aSLionel Sambuc    req.send("");
44*f4a2713aSLionel Sambuc  }
45*f4a2713aSLionel Sambuc}
46*f4a2713aSLionel Sambuc</script>"""))
47*f4a2713aSLionel Sambuc
48*f4a2713aSLionel Sambuc# Insert additional columns.
49*f4a2713aSLionel SambuckReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'),
50*f4a2713aSLionel Sambuc                            '<td></td><td></td>'))
51*f4a2713aSLionel Sambuc
52*f4a2713aSLionel Sambuc# Insert report bug and open file links.
53*f4a2713aSLionel SambuckReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'),
54*f4a2713aSLionel Sambuc                            ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' +
55*f4a2713aSLionel Sambuc                             '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>')))
56*f4a2713aSLionel Sambuc
57*f4a2713aSLionel SambuckReportReplacements.append((re.compile('<!-- REPORTHEADER -->'),
58*f4a2713aSLionel Sambuc                                       '<h3><a href="/">Summary</a> > Report %(report)s</h3>'))
59*f4a2713aSLionel Sambuc
60*f4a2713aSLionel SambuckReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'),
61*f4a2713aSLionel Sambuc                            '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>'))
62*f4a2713aSLionel Sambuc
63*f4a2713aSLionel Sambuc# Insert report crashes link.
64*f4a2713aSLionel Sambuc
65*f4a2713aSLionel Sambuc# Disabled for the time being until we decide exactly when this should
66*f4a2713aSLionel Sambuc# be enabled. Also the radar reporter needs to be fixed to report
67*f4a2713aSLionel Sambuc# multiple files.
68*f4a2713aSLionel Sambuc
69*f4a2713aSLionel Sambuc#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'),
70*f4a2713aSLionel Sambuc#                            '<br>These files will automatically be attached to ' +
71*f4a2713aSLionel Sambuc#                            'reports filed here: <a href="report_crashes">Report Crashes</a>.'))
72*f4a2713aSLionel Sambuc
73*f4a2713aSLionel Sambuc###
74*f4a2713aSLionel Sambuc# Other simple parameters
75*f4a2713aSLionel Sambuc
76*f4a2713aSLionel SambuckResources = posixpath.join(posixpath.dirname(__file__), 'Resources')
77*f4a2713aSLionel SambuckConfigPath = os.path.expanduser('~/.scanview.cfg')
78*f4a2713aSLionel Sambuc
79*f4a2713aSLionel Sambuc###
80*f4a2713aSLionel Sambuc
81*f4a2713aSLionel Sambuc__version__ = "0.1"
82*f4a2713aSLionel Sambuc
83*f4a2713aSLionel Sambuc__all__ = ["create_server"]
84*f4a2713aSLionel Sambuc
85*f4a2713aSLionel Sambucclass ReporterThread(threading.Thread):
86*f4a2713aSLionel Sambuc    def __init__(self, report, reporter, parameters, server):
87*f4a2713aSLionel Sambuc        threading.Thread.__init__(self)
88*f4a2713aSLionel Sambuc        self.report = report
89*f4a2713aSLionel Sambuc        self.server = server
90*f4a2713aSLionel Sambuc        self.reporter = reporter
91*f4a2713aSLionel Sambuc        self.parameters = parameters
92*f4a2713aSLionel Sambuc        self.success = False
93*f4a2713aSLionel Sambuc        self.status = None
94*f4a2713aSLionel Sambuc
95*f4a2713aSLionel Sambuc    def run(self):
96*f4a2713aSLionel Sambuc        result = None
97*f4a2713aSLionel Sambuc        try:
98*f4a2713aSLionel Sambuc            if self.server.options.debug:
99*f4a2713aSLionel Sambuc                print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],)
100*f4a2713aSLionel Sambuc            self.status = self.reporter.fileReport(self.report, self.parameters)
101*f4a2713aSLionel Sambuc            self.success = True
102*f4a2713aSLionel Sambuc            time.sleep(3)
103*f4a2713aSLionel Sambuc            if self.server.options.debug:
104*f4a2713aSLionel Sambuc                print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],)
105*f4a2713aSLionel Sambuc        except Reporter.ReportFailure,e:
106*f4a2713aSLionel Sambuc            self.status = e.value
107*f4a2713aSLionel Sambuc        except Exception,e:
108*f4a2713aSLionel Sambuc            s = StringIO.StringIO()
109*f4a2713aSLionel Sambuc            import traceback
110*f4a2713aSLionel Sambuc            print >>s,'<b>Unhandled Exception</b><br><pre>'
111*f4a2713aSLionel Sambuc            traceback.print_exc(e,file=s)
112*f4a2713aSLionel Sambuc            print >>s,'</pre>'
113*f4a2713aSLionel Sambuc            self.status = s.getvalue()
114*f4a2713aSLionel Sambuc
115*f4a2713aSLionel Sambucclass ScanViewServer(BaseHTTPServer.HTTPServer):
116*f4a2713aSLionel Sambuc    def __init__(self, address, handler, root, reporters, options):
117*f4a2713aSLionel Sambuc        BaseHTTPServer.HTTPServer.__init__(self, address, handler)
118*f4a2713aSLionel Sambuc        self.root = root
119*f4a2713aSLionel Sambuc        self.reporters = reporters
120*f4a2713aSLionel Sambuc        self.options = options
121*f4a2713aSLionel Sambuc        self.halted = False
122*f4a2713aSLionel Sambuc        self.config = None
123*f4a2713aSLionel Sambuc        self.load_config()
124*f4a2713aSLionel Sambuc
125*f4a2713aSLionel Sambuc    def load_config(self):
126*f4a2713aSLionel Sambuc        self.config = ConfigParser.RawConfigParser()
127*f4a2713aSLionel Sambuc
128*f4a2713aSLionel Sambuc        # Add defaults
129*f4a2713aSLionel Sambuc        self.config.add_section('ScanView')
130*f4a2713aSLionel Sambuc        for r in self.reporters:
131*f4a2713aSLionel Sambuc            self.config.add_section(r.getName())
132*f4a2713aSLionel Sambuc            for p in r.getParameters():
133*f4a2713aSLionel Sambuc              if p.saveConfigValue():
134*f4a2713aSLionel Sambuc                self.config.set(r.getName(), p.getName(), '')
135*f4a2713aSLionel Sambuc
136*f4a2713aSLionel Sambuc        # Ignore parse errors
137*f4a2713aSLionel Sambuc        try:
138*f4a2713aSLionel Sambuc            self.config.read([kConfigPath])
139*f4a2713aSLionel Sambuc        except:
140*f4a2713aSLionel Sambuc            pass
141*f4a2713aSLionel Sambuc
142*f4a2713aSLionel Sambuc        # Save on exit
143*f4a2713aSLionel Sambuc        import atexit
144*f4a2713aSLionel Sambuc        atexit.register(lambda: self.save_config())
145*f4a2713aSLionel Sambuc
146*f4a2713aSLionel Sambuc    def save_config(self):
147*f4a2713aSLionel Sambuc        # Ignore errors (only called on exit).
148*f4a2713aSLionel Sambuc        try:
149*f4a2713aSLionel Sambuc            f = open(kConfigPath,'w')
150*f4a2713aSLionel Sambuc            self.config.write(f)
151*f4a2713aSLionel Sambuc            f.close()
152*f4a2713aSLionel Sambuc        except:
153*f4a2713aSLionel Sambuc            pass
154*f4a2713aSLionel Sambuc
155*f4a2713aSLionel Sambuc    def halt(self):
156*f4a2713aSLionel Sambuc        self.halted = True
157*f4a2713aSLionel Sambuc        if self.options.debug:
158*f4a2713aSLionel Sambuc            print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],)
159*f4a2713aSLionel Sambuc
160*f4a2713aSLionel Sambuc    def serve_forever(self):
161*f4a2713aSLionel Sambuc        while not self.halted:
162*f4a2713aSLionel Sambuc            if self.options.debug > 1:
163*f4a2713aSLionel Sambuc                print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],)
164*f4a2713aSLionel Sambuc            try:
165*f4a2713aSLionel Sambuc                self.handle_request()
166*f4a2713aSLionel Sambuc            except OSError,e:
167*f4a2713aSLionel Sambuc                print 'OSError',e.errno
168*f4a2713aSLionel Sambuc
169*f4a2713aSLionel Sambuc    def finish_request(self, request, client_address):
170*f4a2713aSLionel Sambuc        if self.options.autoReload:
171*f4a2713aSLionel Sambuc            import ScanView
172*f4a2713aSLionel Sambuc            self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler
173*f4a2713aSLionel Sambuc        BaseHTTPServer.HTTPServer.finish_request(self, request, client_address)
174*f4a2713aSLionel Sambuc
175*f4a2713aSLionel Sambuc    def handle_error(self, request, client_address):
176*f4a2713aSLionel Sambuc        # Ignore socket errors
177*f4a2713aSLionel Sambuc        info = sys.exc_info()
178*f4a2713aSLionel Sambuc        if info and isinstance(info[1], socket.error):
179*f4a2713aSLionel Sambuc            if self.options.debug > 1:
180*f4a2713aSLionel Sambuc                print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],)
181*f4a2713aSLionel Sambuc            return
182*f4a2713aSLionel Sambuc        BaseHTTPServer.HTTPServer.handle_error(self, request, client_address)
183*f4a2713aSLionel Sambuc
184*f4a2713aSLionel Sambuc# Borrowed from Quixote, with simplifications.
185*f4a2713aSLionel Sambucdef parse_query(qs, fields=None):
186*f4a2713aSLionel Sambuc    if fields is None:
187*f4a2713aSLionel Sambuc        fields = {}
188*f4a2713aSLionel Sambuc    for chunk in filter(None, qs.split('&')):
189*f4a2713aSLionel Sambuc        if '=' not in chunk:
190*f4a2713aSLionel Sambuc            name = chunk
191*f4a2713aSLionel Sambuc            value = ''
192*f4a2713aSLionel Sambuc        else:
193*f4a2713aSLionel Sambuc            name, value = chunk.split('=', 1)
194*f4a2713aSLionel Sambuc        name = urllib.unquote(name.replace('+', ' '))
195*f4a2713aSLionel Sambuc        value = urllib.unquote(value.replace('+', ' '))
196*f4a2713aSLionel Sambuc        item = fields.get(name)
197*f4a2713aSLionel Sambuc        if item is None:
198*f4a2713aSLionel Sambuc            fields[name] = [value]
199*f4a2713aSLionel Sambuc        else:
200*f4a2713aSLionel Sambuc            item.append(value)
201*f4a2713aSLionel Sambuc    return fields
202*f4a2713aSLionel Sambuc
203*f4a2713aSLionel Sambucclass ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
204*f4a2713aSLionel Sambuc    server_version = "ScanViewServer/" + __version__
205*f4a2713aSLionel Sambuc    dynamic_mtime = time.time()
206*f4a2713aSLionel Sambuc
207*f4a2713aSLionel Sambuc    def do_HEAD(self):
208*f4a2713aSLionel Sambuc        try:
209*f4a2713aSLionel Sambuc            SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self)
210*f4a2713aSLionel Sambuc        except Exception,e:
211*f4a2713aSLionel Sambuc            self.handle_exception(e)
212*f4a2713aSLionel Sambuc
213*f4a2713aSLionel Sambuc    def do_GET(self):
214*f4a2713aSLionel Sambuc        try:
215*f4a2713aSLionel Sambuc            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
216*f4a2713aSLionel Sambuc        except Exception,e:
217*f4a2713aSLionel Sambuc            self.handle_exception(e)
218*f4a2713aSLionel Sambuc
219*f4a2713aSLionel Sambuc    def do_POST(self):
220*f4a2713aSLionel Sambuc        """Serve a POST request."""
221*f4a2713aSLionel Sambuc        try:
222*f4a2713aSLionel Sambuc            length = self.headers.getheader('content-length') or "0"
223*f4a2713aSLionel Sambuc            try:
224*f4a2713aSLionel Sambuc                length = int(length)
225*f4a2713aSLionel Sambuc            except:
226*f4a2713aSLionel Sambuc                length = 0
227*f4a2713aSLionel Sambuc            content = self.rfile.read(length)
228*f4a2713aSLionel Sambuc            fields = parse_query(content)
229*f4a2713aSLionel Sambuc            f = self.send_head(fields)
230*f4a2713aSLionel Sambuc            if f:
231*f4a2713aSLionel Sambuc                self.copyfile(f, self.wfile)
232*f4a2713aSLionel Sambuc                f.close()
233*f4a2713aSLionel Sambuc        except Exception,e:
234*f4a2713aSLionel Sambuc            self.handle_exception(e)
235*f4a2713aSLionel Sambuc
236*f4a2713aSLionel Sambuc    def log_message(self, format, *args):
237*f4a2713aSLionel Sambuc        if self.server.options.debug:
238*f4a2713aSLionel Sambuc            sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" %
239*f4a2713aSLionel Sambuc                             (sys.argv[0],
240*f4a2713aSLionel Sambuc                              self.address_string(),
241*f4a2713aSLionel Sambuc                              self.log_date_time_string(),
242*f4a2713aSLionel Sambuc                              format%args))
243*f4a2713aSLionel Sambuc
244*f4a2713aSLionel Sambuc    def load_report(self, report):
245*f4a2713aSLionel Sambuc        path = os.path.join(self.server.root, 'report-%s.html'%report)
246*f4a2713aSLionel Sambuc        data = open(path).read()
247*f4a2713aSLionel Sambuc        keys = {}
248*f4a2713aSLionel Sambuc        for item in kBugKeyValueRE.finditer(data):
249*f4a2713aSLionel Sambuc            k,v = item.groups()
250*f4a2713aSLionel Sambuc            keys[k] = v
251*f4a2713aSLionel Sambuc        return keys
252*f4a2713aSLionel Sambuc
253*f4a2713aSLionel Sambuc    def load_crashes(self):
254*f4a2713aSLionel Sambuc        path = posixpath.join(self.server.root, 'index.html')
255*f4a2713aSLionel Sambuc        data = open(path).read()
256*f4a2713aSLionel Sambuc        problems = []
257*f4a2713aSLionel Sambuc        for item in kReportCrashEntryRE.finditer(data):
258*f4a2713aSLionel Sambuc            fieldData = item.group(1)
259*f4a2713aSLionel Sambuc            fields = dict([i.groups() for i in
260*f4a2713aSLionel Sambuc                           kReportCrashEntryKeyValueRE.finditer(fieldData)])
261*f4a2713aSLionel Sambuc            problems.append(fields)
262*f4a2713aSLionel Sambuc        return problems
263*f4a2713aSLionel Sambuc
264*f4a2713aSLionel Sambuc    def handle_exception(self, exc):
265*f4a2713aSLionel Sambuc        import traceback
266*f4a2713aSLionel Sambuc        s = StringIO.StringIO()
267*f4a2713aSLionel Sambuc        print >>s, "INTERNAL ERROR\n"
268*f4a2713aSLionel Sambuc        traceback.print_exc(exc, s)
269*f4a2713aSLionel Sambuc        f = self.send_string(s.getvalue(), 'text/plain')
270*f4a2713aSLionel Sambuc        if f:
271*f4a2713aSLionel Sambuc            self.copyfile(f, self.wfile)
272*f4a2713aSLionel Sambuc            f.close()
273*f4a2713aSLionel Sambuc
274*f4a2713aSLionel Sambuc    def get_scalar_field(self, name):
275*f4a2713aSLionel Sambuc        if name in self.fields:
276*f4a2713aSLionel Sambuc            return self.fields[name][0]
277*f4a2713aSLionel Sambuc        else:
278*f4a2713aSLionel Sambuc            return None
279*f4a2713aSLionel Sambuc
280*f4a2713aSLionel Sambuc    def submit_bug(self, c):
281*f4a2713aSLionel Sambuc        title = self.get_scalar_field('title')
282*f4a2713aSLionel Sambuc        description = self.get_scalar_field('description')
283*f4a2713aSLionel Sambuc        report = self.get_scalar_field('report')
284*f4a2713aSLionel Sambuc        reporterIndex = self.get_scalar_field('reporter')
285*f4a2713aSLionel Sambuc        files = []
286*f4a2713aSLionel Sambuc        for fileID in self.fields.get('files',[]):
287*f4a2713aSLionel Sambuc            try:
288*f4a2713aSLionel Sambuc                i = int(fileID)
289*f4a2713aSLionel Sambuc            except:
290*f4a2713aSLionel Sambuc                i = None
291*f4a2713aSLionel Sambuc            if i is None or i<0 or i>=len(c.files):
292*f4a2713aSLionel Sambuc                return (False, 'Invalid file ID')
293*f4a2713aSLionel Sambuc            files.append(c.files[i])
294*f4a2713aSLionel Sambuc
295*f4a2713aSLionel Sambuc        if not title:
296*f4a2713aSLionel Sambuc            return (False, "Missing title.")
297*f4a2713aSLionel Sambuc        if not description:
298*f4a2713aSLionel Sambuc            return (False, "Missing description.")
299*f4a2713aSLionel Sambuc        try:
300*f4a2713aSLionel Sambuc            reporterIndex = int(reporterIndex)
301*f4a2713aSLionel Sambuc        except:
302*f4a2713aSLionel Sambuc            return (False, "Invalid report method.")
303*f4a2713aSLionel Sambuc
304*f4a2713aSLionel Sambuc        # Get the reporter and parameters.
305*f4a2713aSLionel Sambuc        reporter = self.server.reporters[reporterIndex]
306*f4a2713aSLionel Sambuc        parameters = {}
307*f4a2713aSLionel Sambuc        for o in reporter.getParameters():
308*f4a2713aSLionel Sambuc            name = '%s_%s'%(reporter.getName(),o.getName())
309*f4a2713aSLionel Sambuc            if name not in self.fields:
310*f4a2713aSLionel Sambuc                return (False,
311*f4a2713aSLionel Sambuc                        'Missing field "%s" for %s report method.'%(name,
312*f4a2713aSLionel Sambuc                                                                    reporter.getName()))
313*f4a2713aSLionel Sambuc            parameters[o.getName()] = self.get_scalar_field(name)
314*f4a2713aSLionel Sambuc
315*f4a2713aSLionel Sambuc        # Update config defaults.
316*f4a2713aSLionel Sambuc        if report != 'None':
317*f4a2713aSLionel Sambuc            self.server.config.set('ScanView', 'reporter', reporterIndex)
318*f4a2713aSLionel Sambuc            for o in reporter.getParameters():
319*f4a2713aSLionel Sambuc              if o.saveConfigValue():
320*f4a2713aSLionel Sambuc                name = o.getName()
321*f4a2713aSLionel Sambuc                self.server.config.set(reporter.getName(), name, parameters[name])
322*f4a2713aSLionel Sambuc
323*f4a2713aSLionel Sambuc        # Create the report.
324*f4a2713aSLionel Sambuc        bug = Reporter.BugReport(title, description, files)
325*f4a2713aSLionel Sambuc
326*f4a2713aSLionel Sambuc        # Kick off a reporting thread.
327*f4a2713aSLionel Sambuc        t = ReporterThread(bug, reporter, parameters, self.server)
328*f4a2713aSLionel Sambuc        t.start()
329*f4a2713aSLionel Sambuc
330*f4a2713aSLionel Sambuc        # Wait for thread to die...
331*f4a2713aSLionel Sambuc        while t.isAlive():
332*f4a2713aSLionel Sambuc            time.sleep(.25)
333*f4a2713aSLionel Sambuc        submitStatus = t.status
334*f4a2713aSLionel Sambuc
335*f4a2713aSLionel Sambuc        return (t.success, t.status)
336*f4a2713aSLionel Sambuc
337*f4a2713aSLionel Sambuc    def send_report_submit(self):
338*f4a2713aSLionel Sambuc        report = self.get_scalar_field('report')
339*f4a2713aSLionel Sambuc        c = self.get_report_context(report)
340*f4a2713aSLionel Sambuc        if c.reportSource is None:
341*f4a2713aSLionel Sambuc            reportingFor = "Report Crashes > "
342*f4a2713aSLionel Sambuc            fileBug = """\
343*f4a2713aSLionel Sambuc<a href="/report_crashes">File Bug</a> > """%locals()
344*f4a2713aSLionel Sambuc        else:
345*f4a2713aSLionel Sambuc            reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource,
346*f4a2713aSLionel Sambuc                                                                   report)
347*f4a2713aSLionel Sambuc            fileBug = '<a href="/report/%s">File Bug</a> > ' % report
348*f4a2713aSLionel Sambuc        title = self.get_scalar_field('title')
349*f4a2713aSLionel Sambuc        description = self.get_scalar_field('description')
350*f4a2713aSLionel Sambuc
351*f4a2713aSLionel Sambuc        res,message = self.submit_bug(c)
352*f4a2713aSLionel Sambuc
353*f4a2713aSLionel Sambuc        if res:
354*f4a2713aSLionel Sambuc            statusClass = 'SubmitOk'
355*f4a2713aSLionel Sambuc            statusName = 'Succeeded'
356*f4a2713aSLionel Sambuc        else:
357*f4a2713aSLionel Sambuc            statusClass = 'SubmitFail'
358*f4a2713aSLionel Sambuc            statusName = 'Failed'
359*f4a2713aSLionel Sambuc
360*f4a2713aSLionel Sambuc        result = """
361*f4a2713aSLionel Sambuc<head>
362*f4a2713aSLionel Sambuc  <title>Bug Submission</title>
363*f4a2713aSLionel Sambuc  <link rel="stylesheet" type="text/css" href="/scanview.css" />
364*f4a2713aSLionel Sambuc</head>
365*f4a2713aSLionel Sambuc<body>
366*f4a2713aSLionel Sambuc<h3>
367*f4a2713aSLionel Sambuc<a href="/">Summary</a> >
368*f4a2713aSLionel Sambuc%(reportingFor)s
369*f4a2713aSLionel Sambuc%(fileBug)s
370*f4a2713aSLionel SambucSubmit</h3>
371*f4a2713aSLionel Sambuc<form name="form" action="">
372*f4a2713aSLionel Sambuc<table class="form">
373*f4a2713aSLionel Sambuc<tr><td>
374*f4a2713aSLionel Sambuc<table class="form_group">
375*f4a2713aSLionel Sambuc<tr>
376*f4a2713aSLionel Sambuc  <td class="form_clabel">Title:</td>
377*f4a2713aSLionel Sambuc  <td class="form_value">
378*f4a2713aSLionel Sambuc    <input type="text" name="title" size="50" value="%(title)s" disabled>
379*f4a2713aSLionel Sambuc  </td>
380*f4a2713aSLionel Sambuc</tr>
381*f4a2713aSLionel Sambuc<tr>
382*f4a2713aSLionel Sambuc  <td class="form_label">Description:</td>
383*f4a2713aSLionel Sambuc  <td class="form_value">
384*f4a2713aSLionel Sambuc<textarea rows="10" cols="80" name="description" disabled>
385*f4a2713aSLionel Sambuc%(description)s
386*f4a2713aSLionel Sambuc</textarea>
387*f4a2713aSLionel Sambuc  </td>
388*f4a2713aSLionel Sambuc</table>
389*f4a2713aSLionel Sambuc</td></tr>
390*f4a2713aSLionel Sambuc</table>
391*f4a2713aSLionel Sambuc</form>
392*f4a2713aSLionel Sambuc<h1 class="%(statusClass)s">Submission %(statusName)s</h1>
393*f4a2713aSLionel Sambuc%(message)s
394*f4a2713aSLionel Sambuc<p>
395*f4a2713aSLionel Sambuc<hr>
396*f4a2713aSLionel Sambuc<a href="/">Return to Summary</a>
397*f4a2713aSLionel Sambuc</body>
398*f4a2713aSLionel Sambuc</html>"""%locals()
399*f4a2713aSLionel Sambuc        return self.send_string(result)
400*f4a2713aSLionel Sambuc
401*f4a2713aSLionel Sambuc    def send_open_report(self, report):
402*f4a2713aSLionel Sambuc        try:
403*f4a2713aSLionel Sambuc            keys = self.load_report(report)
404*f4a2713aSLionel Sambuc        except IOError:
405*f4a2713aSLionel Sambuc            return self.send_error(400, 'Invalid report.')
406*f4a2713aSLionel Sambuc
407*f4a2713aSLionel Sambuc        file = keys.get('FILE')
408*f4a2713aSLionel Sambuc        if not file or not posixpath.exists(file):
409*f4a2713aSLionel Sambuc            return self.send_error(400, 'File does not exist: "%s"' % file)
410*f4a2713aSLionel Sambuc
411*f4a2713aSLionel Sambuc        import startfile
412*f4a2713aSLionel Sambuc        if self.server.options.debug:
413*f4a2713aSLionel Sambuc            print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0],
414*f4a2713aSLionel Sambuc                                                            file)
415*f4a2713aSLionel Sambuc
416*f4a2713aSLionel Sambuc        status = startfile.open(file)
417*f4a2713aSLionel Sambuc        if status:
418*f4a2713aSLionel Sambuc            res = 'Opened: "%s"' % file
419*f4a2713aSLionel Sambuc        else:
420*f4a2713aSLionel Sambuc            res = 'Open failed: "%s"' % file
421*f4a2713aSLionel Sambuc
422*f4a2713aSLionel Sambuc        return self.send_string(res, 'text/plain')
423*f4a2713aSLionel Sambuc
424*f4a2713aSLionel Sambuc    def get_report_context(self, report):
425*f4a2713aSLionel Sambuc        class Context:
426*f4a2713aSLionel Sambuc            pass
427*f4a2713aSLionel Sambuc        if report is None or report == 'None':
428*f4a2713aSLionel Sambuc            data = self.load_crashes()
429*f4a2713aSLionel Sambuc            # Don't allow empty reports.
430*f4a2713aSLionel Sambuc            if not data:
431*f4a2713aSLionel Sambuc                raise ValueError, 'No crashes detected!'
432*f4a2713aSLionel Sambuc            c = Context()
433*f4a2713aSLionel Sambuc            c.title = 'clang static analyzer failures'
434*f4a2713aSLionel Sambuc
435*f4a2713aSLionel Sambuc            stderrSummary = ""
436*f4a2713aSLionel Sambuc            for item in data:
437*f4a2713aSLionel Sambuc                if 'stderr' in item:
438*f4a2713aSLionel Sambuc                    path = posixpath.join(self.server.root, item['stderr'])
439*f4a2713aSLionel Sambuc                    if os.path.exists(path):
440*f4a2713aSLionel Sambuc                        lns = itertools.islice(open(path), 0, 10)
441*f4a2713aSLionel Sambuc                        stderrSummary += '%s\n--\n%s' % (item.get('src',
442*f4a2713aSLionel Sambuc                                                                  '<unknown>'),
443*f4a2713aSLionel Sambuc                                                         ''.join(lns))
444*f4a2713aSLionel Sambuc
445*f4a2713aSLionel Sambuc            c.description = """\
446*f4a2713aSLionel SambucThe clang static analyzer failed on these inputs:
447*f4a2713aSLionel Sambuc%s
448*f4a2713aSLionel Sambuc
449*f4a2713aSLionel SambucSTDERR Summary
450*f4a2713aSLionel Sambuc--------------
451*f4a2713aSLionel Sambuc%s
452*f4a2713aSLionel Sambuc""" % ('\n'.join([item.get('src','<unknown>') for item in data]),
453*f4a2713aSLionel Sambuc       stderrSummary)
454*f4a2713aSLionel Sambuc            c.reportSource = None
455*f4a2713aSLionel Sambuc            c.navMarkup = "Report Crashes > "
456*f4a2713aSLionel Sambuc            c.files = []
457*f4a2713aSLionel Sambuc            for item in data:
458*f4a2713aSLionel Sambuc                c.files.append(item.get('src',''))
459*f4a2713aSLionel Sambuc                c.files.append(posixpath.join(self.server.root,
460*f4a2713aSLionel Sambuc                                              item.get('file','')))
461*f4a2713aSLionel Sambuc                c.files.append(posixpath.join(self.server.root,
462*f4a2713aSLionel Sambuc                                              item.get('clangfile','')))
463*f4a2713aSLionel Sambuc                c.files.append(posixpath.join(self.server.root,
464*f4a2713aSLionel Sambuc                                              item.get('stderr','')))
465*f4a2713aSLionel Sambuc                c.files.append(posixpath.join(self.server.root,
466*f4a2713aSLionel Sambuc                                              item.get('info','')))
467*f4a2713aSLionel Sambuc            # Just in case something failed, ignore files which don't
468*f4a2713aSLionel Sambuc            # exist.
469*f4a2713aSLionel Sambuc            c.files = [f for f in c.files
470*f4a2713aSLionel Sambuc                       if os.path.exists(f) and os.path.isfile(f)]
471*f4a2713aSLionel Sambuc        else:
472*f4a2713aSLionel Sambuc            # Check that this is a valid report.
473*f4a2713aSLionel Sambuc            path = posixpath.join(self.server.root, 'report-%s.html' % report)
474*f4a2713aSLionel Sambuc            if not posixpath.exists(path):
475*f4a2713aSLionel Sambuc                raise ValueError, 'Invalid report ID'
476*f4a2713aSLionel Sambuc            keys = self.load_report(report)
477*f4a2713aSLionel Sambuc            c = Context()
478*f4a2713aSLionel Sambuc            c.title = keys.get('DESC','clang error (unrecognized')
479*f4a2713aSLionel Sambuc            c.description = """\
480*f4a2713aSLionel SambucBug reported by the clang static analyzer.
481*f4a2713aSLionel Sambuc
482*f4a2713aSLionel SambucDescription: %s
483*f4a2713aSLionel SambucFile: %s
484*f4a2713aSLionel SambucLine: %s
485*f4a2713aSLionel Sambuc"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>'))
486*f4a2713aSLionel Sambuc            c.reportSource = 'report-%s.html' % report
487*f4a2713aSLionel Sambuc            c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource,
488*f4a2713aSLionel Sambuc                                                                  report)
489*f4a2713aSLionel Sambuc
490*f4a2713aSLionel Sambuc            c.files = [path]
491*f4a2713aSLionel Sambuc        return c
492*f4a2713aSLionel Sambuc
493*f4a2713aSLionel Sambuc    def send_report(self, report, configOverrides=None):
494*f4a2713aSLionel Sambuc        def getConfigOption(section, field):
495*f4a2713aSLionel Sambuc            if (configOverrides is not None and
496*f4a2713aSLionel Sambuc                section in configOverrides and
497*f4a2713aSLionel Sambuc                field in configOverrides[section]):
498*f4a2713aSLionel Sambuc                return configOverrides[section][field]
499*f4a2713aSLionel Sambuc            return self.server.config.get(section, field)
500*f4a2713aSLionel Sambuc
501*f4a2713aSLionel Sambuc        # report is None is used for crashes
502*f4a2713aSLionel Sambuc        try:
503*f4a2713aSLionel Sambuc            c = self.get_report_context(report)
504*f4a2713aSLionel Sambuc        except ValueError, e:
505*f4a2713aSLionel Sambuc            return self.send_error(400, e.message)
506*f4a2713aSLionel Sambuc
507*f4a2713aSLionel Sambuc        title = c.title
508*f4a2713aSLionel Sambuc        description= c.description
509*f4a2713aSLionel Sambuc        reportingFor = c.navMarkup
510*f4a2713aSLionel Sambuc        if c.reportSource is None:
511*f4a2713aSLionel Sambuc            extraIFrame = ""
512*f4a2713aSLionel Sambuc        else:
513*f4a2713aSLionel Sambuc            extraIFrame = """\
514*f4a2713aSLionel Sambuc<iframe src="/%s" width="100%%" height="40%%"
515*f4a2713aSLionel Sambuc        scrolling="auto" frameborder="1">
516*f4a2713aSLionel Sambuc  <a href="/%s">View Bug Report</a>
517*f4a2713aSLionel Sambuc</iframe>""" % (c.reportSource, c.reportSource)
518*f4a2713aSLionel Sambuc
519*f4a2713aSLionel Sambuc        reporterSelections = []
520*f4a2713aSLionel Sambuc        reporterOptions = []
521*f4a2713aSLionel Sambuc
522*f4a2713aSLionel Sambuc        try:
523*f4a2713aSLionel Sambuc            active = int(getConfigOption('ScanView','reporter'))
524*f4a2713aSLionel Sambuc        except:
525*f4a2713aSLionel Sambuc            active = 0
526*f4a2713aSLionel Sambuc        for i,r in enumerate(self.server.reporters):
527*f4a2713aSLionel Sambuc            selected = (i == active)
528*f4a2713aSLionel Sambuc            if selected:
529*f4a2713aSLionel Sambuc                selectedStr = ' selected'
530*f4a2713aSLionel Sambuc            else:
531*f4a2713aSLionel Sambuc                selectedStr = ''
532*f4a2713aSLionel Sambuc            reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName()))
533*f4a2713aSLionel Sambuc            options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()])
534*f4a2713aSLionel Sambuc            display = ('none','')[selected]
535*f4a2713aSLionel Sambuc            reporterOptions.append("""\
536*f4a2713aSLionel Sambuc<tr id="%sReporterOptions" style="display:%s">
537*f4a2713aSLionel Sambuc  <td class="form_label">%s Options</td>
538*f4a2713aSLionel Sambuc  <td class="form_value">
539*f4a2713aSLionel Sambuc    <table class="form_inner_group">
540*f4a2713aSLionel Sambuc%s
541*f4a2713aSLionel Sambuc    </table>
542*f4a2713aSLionel Sambuc  </td>
543*f4a2713aSLionel Sambuc</tr>
544*f4a2713aSLionel Sambuc"""%(r.getName(),display,r.getName(),options))
545*f4a2713aSLionel Sambuc        reporterSelections = '\n'.join(reporterSelections)
546*f4a2713aSLionel Sambuc        reporterOptionsDivs = '\n'.join(reporterOptions)
547*f4a2713aSLionel Sambuc        reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters]))
548*f4a2713aSLionel Sambuc
549*f4a2713aSLionel Sambuc        if c.files:
550*f4a2713aSLionel Sambuc            fieldSize = min(5, len(c.files))
551*f4a2713aSLionel Sambuc            attachFileOptions = '\n'.join(["""\
552*f4a2713aSLionel Sambuc<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)])
553*f4a2713aSLionel Sambuc            attachFileRow = """\
554*f4a2713aSLionel Sambuc<tr>
555*f4a2713aSLionel Sambuc  <td class="form_label">Attach:</td>
556*f4a2713aSLionel Sambuc  <td class="form_value">
557*f4a2713aSLionel Sambuc<select style="width:100%%" name="files" multiple size=%d>
558*f4a2713aSLionel Sambuc%s
559*f4a2713aSLionel Sambuc</select>
560*f4a2713aSLionel Sambuc  </td>
561*f4a2713aSLionel Sambuc</tr>
562*f4a2713aSLionel Sambuc""" % (min(5, len(c.files)), attachFileOptions)
563*f4a2713aSLionel Sambuc        else:
564*f4a2713aSLionel Sambuc            attachFileRow = ""
565*f4a2713aSLionel Sambuc
566*f4a2713aSLionel Sambuc        result = """<html>
567*f4a2713aSLionel Sambuc<head>
568*f4a2713aSLionel Sambuc  <title>File Bug</title>
569*f4a2713aSLionel Sambuc  <link rel="stylesheet" type="text/css" href="/scanview.css" />
570*f4a2713aSLionel Sambuc</head>
571*f4a2713aSLionel Sambuc<script language="javascript" type="text/javascript">
572*f4a2713aSLionel Sambucvar reporters = %(reportersArray)s;
573*f4a2713aSLionel Sambucfunction updateReporterOptions() {
574*f4a2713aSLionel Sambuc  index = document.getElementById('reporter').selectedIndex;
575*f4a2713aSLionel Sambuc  for (var i=0; i < reporters.length; ++i) {
576*f4a2713aSLionel Sambuc    o = document.getElementById(reporters[i] + "ReporterOptions");
577*f4a2713aSLionel Sambuc    if (i == index) {
578*f4a2713aSLionel Sambuc      o.style.display = "";
579*f4a2713aSLionel Sambuc    } else {
580*f4a2713aSLionel Sambuc      o.style.display = "none";
581*f4a2713aSLionel Sambuc    }
582*f4a2713aSLionel Sambuc  }
583*f4a2713aSLionel Sambuc}
584*f4a2713aSLionel Sambuc</script>
585*f4a2713aSLionel Sambuc<body onLoad="updateReporterOptions()">
586*f4a2713aSLionel Sambuc<h3>
587*f4a2713aSLionel Sambuc<a href="/">Summary</a> >
588*f4a2713aSLionel Sambuc%(reportingFor)s
589*f4a2713aSLionel SambucFile Bug</h3>
590*f4a2713aSLionel Sambuc<form name="form" action="/report_submit" method="post">
591*f4a2713aSLionel Sambuc<input type="hidden" name="report" value="%(report)s">
592*f4a2713aSLionel Sambuc
593*f4a2713aSLionel Sambuc<table class="form">
594*f4a2713aSLionel Sambuc<tr><td>
595*f4a2713aSLionel Sambuc<table class="form_group">
596*f4a2713aSLionel Sambuc<tr>
597*f4a2713aSLionel Sambuc  <td class="form_clabel">Title:</td>
598*f4a2713aSLionel Sambuc  <td class="form_value">
599*f4a2713aSLionel Sambuc    <input type="text" name="title" size="50" value="%(title)s">
600*f4a2713aSLionel Sambuc  </td>
601*f4a2713aSLionel Sambuc</tr>
602*f4a2713aSLionel Sambuc<tr>
603*f4a2713aSLionel Sambuc  <td class="form_label">Description:</td>
604*f4a2713aSLionel Sambuc  <td class="form_value">
605*f4a2713aSLionel Sambuc<textarea rows="10" cols="80" name="description">
606*f4a2713aSLionel Sambuc%(description)s
607*f4a2713aSLionel Sambuc</textarea>
608*f4a2713aSLionel Sambuc  </td>
609*f4a2713aSLionel Sambuc</tr>
610*f4a2713aSLionel Sambuc
611*f4a2713aSLionel Sambuc%(attachFileRow)s
612*f4a2713aSLionel Sambuc
613*f4a2713aSLionel Sambuc</table>
614*f4a2713aSLionel Sambuc<br>
615*f4a2713aSLionel Sambuc<table class="form_group">
616*f4a2713aSLionel Sambuc<tr>
617*f4a2713aSLionel Sambuc  <td class="form_clabel">Method:</td>
618*f4a2713aSLionel Sambuc  <td class="form_value">
619*f4a2713aSLionel Sambuc    <select id="reporter" name="reporter" onChange="updateReporterOptions()">
620*f4a2713aSLionel Sambuc    %(reporterSelections)s
621*f4a2713aSLionel Sambuc    </select>
622*f4a2713aSLionel Sambuc  </td>
623*f4a2713aSLionel Sambuc</tr>
624*f4a2713aSLionel Sambuc%(reporterOptionsDivs)s
625*f4a2713aSLionel Sambuc</table>
626*f4a2713aSLionel Sambuc<br>
627*f4a2713aSLionel Sambuc</td></tr>
628*f4a2713aSLionel Sambuc<tr><td class="form_submit">
629*f4a2713aSLionel Sambuc  <input align="right" type="submit" name="Submit" value="Submit">
630*f4a2713aSLionel Sambuc</td></tr>
631*f4a2713aSLionel Sambuc</table>
632*f4a2713aSLionel Sambuc</form>
633*f4a2713aSLionel Sambuc
634*f4a2713aSLionel Sambuc%(extraIFrame)s
635*f4a2713aSLionel Sambuc
636*f4a2713aSLionel Sambuc</body>
637*f4a2713aSLionel Sambuc</html>"""%locals()
638*f4a2713aSLionel Sambuc
639*f4a2713aSLionel Sambuc        return self.send_string(result)
640*f4a2713aSLionel Sambuc
641*f4a2713aSLionel Sambuc    def send_head(self, fields=None):
642*f4a2713aSLionel Sambuc        if (self.server.options.onlyServeLocal and
643*f4a2713aSLionel Sambuc            self.client_address[0] != '127.0.0.1'):
644*f4a2713aSLionel Sambuc            return self.send_error(401, 'Unauthorized host.')
645*f4a2713aSLionel Sambuc
646*f4a2713aSLionel Sambuc        if fields is None:
647*f4a2713aSLionel Sambuc            fields = {}
648*f4a2713aSLionel Sambuc        self.fields = fields
649*f4a2713aSLionel Sambuc
650*f4a2713aSLionel Sambuc        o = urlparse.urlparse(self.path)
651*f4a2713aSLionel Sambuc        self.fields = parse_query(o.query, fields)
652*f4a2713aSLionel Sambuc        path = posixpath.normpath(urllib.unquote(o.path))
653*f4a2713aSLionel Sambuc
654*f4a2713aSLionel Sambuc        # Split the components and strip the root prefix.
655*f4a2713aSLionel Sambuc        components = path.split('/')[1:]
656*f4a2713aSLionel Sambuc
657*f4a2713aSLionel Sambuc        # Special case some top-level entries.
658*f4a2713aSLionel Sambuc        if components:
659*f4a2713aSLionel Sambuc            name = components[0]
660*f4a2713aSLionel Sambuc            if len(components)==2:
661*f4a2713aSLionel Sambuc                if name=='report':
662*f4a2713aSLionel Sambuc                    return self.send_report(components[1])
663*f4a2713aSLionel Sambuc                elif name=='open':
664*f4a2713aSLionel Sambuc                    return self.send_open_report(components[1])
665*f4a2713aSLionel Sambuc            elif len(components)==1:
666*f4a2713aSLionel Sambuc                if name=='quit':
667*f4a2713aSLionel Sambuc                    self.server.halt()
668*f4a2713aSLionel Sambuc                    return self.send_string('Goodbye.', 'text/plain')
669*f4a2713aSLionel Sambuc                elif name=='report_submit':
670*f4a2713aSLionel Sambuc                    return self.send_report_submit()
671*f4a2713aSLionel Sambuc                elif name=='report_crashes':
672*f4a2713aSLionel Sambuc                    overrides = { 'ScanView' : {},
673*f4a2713aSLionel Sambuc                                  'Radar' : {},
674*f4a2713aSLionel Sambuc                                  'Email' : {} }
675*f4a2713aSLionel Sambuc                    for i,r in enumerate(self.server.reporters):
676*f4a2713aSLionel Sambuc                        if r.getName() == 'Radar':
677*f4a2713aSLionel Sambuc                            overrides['ScanView']['reporter'] = i
678*f4a2713aSLionel Sambuc                            break
679*f4a2713aSLionel Sambuc                    overrides['Radar']['Component'] = 'llvm - checker'
680*f4a2713aSLionel Sambuc                    overrides['Radar']['Component Version'] = 'X'
681*f4a2713aSLionel Sambuc                    return self.send_report(None, overrides)
682*f4a2713aSLionel Sambuc                elif name=='favicon.ico':
683*f4a2713aSLionel Sambuc                    return self.send_path(posixpath.join(kResources,'bugcatcher.ico'))
684*f4a2713aSLionel Sambuc
685*f4a2713aSLionel Sambuc        # Match directory entries.
686*f4a2713aSLionel Sambuc        if components[-1] == '':
687*f4a2713aSLionel Sambuc            components[-1] = 'index.html'
688*f4a2713aSLionel Sambuc
689*f4a2713aSLionel Sambuc        relpath = '/'.join(components)
690*f4a2713aSLionel Sambuc        path = posixpath.join(self.server.root, relpath)
691*f4a2713aSLionel Sambuc
692*f4a2713aSLionel Sambuc        if self.server.options.debug > 1:
693*f4a2713aSLionel Sambuc            print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0],
694*f4a2713aSLionel Sambuc                                                                 path)
695*f4a2713aSLionel Sambuc        return self.send_path(path)
696*f4a2713aSLionel Sambuc
697*f4a2713aSLionel Sambuc    def send_404(self):
698*f4a2713aSLionel Sambuc        self.send_error(404, "File not found")
699*f4a2713aSLionel Sambuc        return None
700*f4a2713aSLionel Sambuc
701*f4a2713aSLionel Sambuc    def send_path(self, path):
702*f4a2713aSLionel Sambuc        # If the requested path is outside the root directory, do not open it
703*f4a2713aSLionel Sambuc        rel = os.path.abspath(path)
704*f4a2713aSLionel Sambuc        if not rel.startswith(os.path.abspath(self.server.root)):
705*f4a2713aSLionel Sambuc          return self.send_404()
706*f4a2713aSLionel Sambuc
707*f4a2713aSLionel Sambuc        ctype = self.guess_type(path)
708*f4a2713aSLionel Sambuc        if ctype.startswith('text/'):
709*f4a2713aSLionel Sambuc            # Patch file instead
710*f4a2713aSLionel Sambuc            return self.send_patched_file(path, ctype)
711*f4a2713aSLionel Sambuc        else:
712*f4a2713aSLionel Sambuc            mode = 'rb'
713*f4a2713aSLionel Sambuc        try:
714*f4a2713aSLionel Sambuc            f = open(path, mode)
715*f4a2713aSLionel Sambuc        except IOError:
716*f4a2713aSLionel Sambuc            return self.send_404()
717*f4a2713aSLionel Sambuc        return self.send_file(f, ctype)
718*f4a2713aSLionel Sambuc
719*f4a2713aSLionel Sambuc    def send_file(self, f, ctype):
720*f4a2713aSLionel Sambuc        # Patch files to add links, but skip binary files.
721*f4a2713aSLionel Sambuc        self.send_response(200)
722*f4a2713aSLionel Sambuc        self.send_header("Content-type", ctype)
723*f4a2713aSLionel Sambuc        fs = os.fstat(f.fileno())
724*f4a2713aSLionel Sambuc        self.send_header("Content-Length", str(fs[6]))
725*f4a2713aSLionel Sambuc        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
726*f4a2713aSLionel Sambuc        self.end_headers()
727*f4a2713aSLionel Sambuc        return f
728*f4a2713aSLionel Sambuc
729*f4a2713aSLionel Sambuc    def send_string(self, s, ctype='text/html', headers=True, mtime=None):
730*f4a2713aSLionel Sambuc        if headers:
731*f4a2713aSLionel Sambuc            self.send_response(200)
732*f4a2713aSLionel Sambuc            self.send_header("Content-type", ctype)
733*f4a2713aSLionel Sambuc            self.send_header("Content-Length", str(len(s)))
734*f4a2713aSLionel Sambuc            if mtime is None:
735*f4a2713aSLionel Sambuc                mtime = self.dynamic_mtime
736*f4a2713aSLionel Sambuc            self.send_header("Last-Modified", self.date_time_string(mtime))
737*f4a2713aSLionel Sambuc            self.end_headers()
738*f4a2713aSLionel Sambuc        return StringIO.StringIO(s)
739*f4a2713aSLionel Sambuc
740*f4a2713aSLionel Sambuc    def send_patched_file(self, path, ctype):
741*f4a2713aSLionel Sambuc        # Allow a very limited set of variables. This is pretty gross.
742*f4a2713aSLionel Sambuc        variables = {}
743*f4a2713aSLionel Sambuc        variables['report'] = ''
744*f4a2713aSLionel Sambuc        m = kReportFileRE.match(path)
745*f4a2713aSLionel Sambuc        if m:
746*f4a2713aSLionel Sambuc            variables['report'] = m.group(2)
747*f4a2713aSLionel Sambuc
748*f4a2713aSLionel Sambuc        try:
749*f4a2713aSLionel Sambuc            f = open(path,'r')
750*f4a2713aSLionel Sambuc        except IOError:
751*f4a2713aSLionel Sambuc            return self.send_404()
752*f4a2713aSLionel Sambuc        fs = os.fstat(f.fileno())
753*f4a2713aSLionel Sambuc        data = f.read()
754*f4a2713aSLionel Sambuc        for a,b in kReportReplacements:
755*f4a2713aSLionel Sambuc            data = a.sub(b % variables, data)
756*f4a2713aSLionel Sambuc        return self.send_string(data, ctype, mtime=fs.st_mtime)
757*f4a2713aSLionel Sambuc
758*f4a2713aSLionel Sambuc
759*f4a2713aSLionel Sambucdef create_server(address, options, root):
760*f4a2713aSLionel Sambuc    import Reporter
761*f4a2713aSLionel Sambuc
762*f4a2713aSLionel Sambuc    reporters = Reporter.getReporters()
763*f4a2713aSLionel Sambuc
764*f4a2713aSLionel Sambuc    return ScanViewServer(address, ScanViewRequestHandler,
765*f4a2713aSLionel Sambuc                          root,
766*f4a2713aSLionel Sambuc                          reporters,
767*f4a2713aSLionel Sambuc                          options)
768