| #!/usr/bin/env python |
| # -*- coding: utf-8 -*- |
| |
| """Methods for reporting bugs.""" |
| |
| import subprocess, sys, os |
| |
| __all__ = ["ReportFailure", "BugReport", "getReporters"] |
| |
| # |
| |
| |
| class ReportFailure(Exception): |
| """Generic exception for failures in bug reporting.""" |
| |
| def __init__(self, value): |
| self.value = value |
| |
| |
| # Collect information about a bug. |
| |
| |
| class BugReport(object): |
| def __init__(self, title, description, files): |
| self.title = title |
| self.description = description |
| self.files = files |
| |
| |
| # Reporter interfaces. |
| |
| import os |
| |
| import email, mimetypes, smtplib |
| from email import encoders |
| from email.message import Message |
| from email.mime.base import MIMEBase |
| from email.mime.multipart import MIMEMultipart |
| from email.mime.text import MIMEText |
| |
| # ===------------------------------------------------------------------------===# |
| # ReporterParameter |
| # ===------------------------------------------------------------------------===# |
| |
| |
| class ReporterParameter(object): |
| def __init__(self, n): |
| self.name = n |
| |
| def getName(self): |
| return self.name |
| |
| def getValue(self, r, bugtype, getConfigOption): |
| return getConfigOption(r.getName(), self.getName()) |
| |
| def saveConfigValue(self): |
| return True |
| |
| |
| class TextParameter(ReporterParameter): |
| def getHTML(self, r, bugtype, getConfigOption): |
| return """\ |
| <tr> |
| <td class="form_clabel">%s:</td> |
| <td class="form_value"><input type="text" name="%s_%s" value="%s"></td> |
| </tr>""" % ( |
| self.getName(), |
| r.getName(), |
| self.getName(), |
| self.getValue(r, bugtype, getConfigOption), |
| ) |
| |
| |
| class SelectionParameter(ReporterParameter): |
| def __init__(self, n, values): |
| ReporterParameter.__init__(self, n) |
| self.values = values |
| |
| def getHTML(self, r, bugtype, getConfigOption): |
| default = self.getValue(r, bugtype, getConfigOption) |
| return """\ |
| <tr> |
| <td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s"> |
| %s |
| </select></td>""" % ( |
| self.getName(), |
| r.getName(), |
| self.getName(), |
| "\n".join( |
| [ |
| """\ |
| <option value="%s"%s>%s</option>""" |
| % (o[0], o[0] == default and ' selected="selected"' or "", o[1]) |
| for o in self.values |
| ] |
| ), |
| ) |
| |
| |
| # ===------------------------------------------------------------------------===# |
| # Reporters |
| # ===------------------------------------------------------------------------===# |
| |
| |
| class EmailReporter(object): |
| def getName(self): |
| return "Email" |
| |
| def getParameters(self): |
| return [TextParameter(x) for x in ["To", "From", "SMTP Server", "SMTP Port"]] |
| |
| # Lifted from python email module examples. |
| def attachFile(self, outer, path): |
| # Guess the content type based on the file's extension. Encoding |
| # will be ignored, although we should check for simple things like |
| # gzip'd or compressed files. |
| ctype, encoding = mimetypes.guess_type(path) |
| if ctype is None or encoding is not None: |
| # No guess could be made, or the file is encoded (compressed), so |
| # use a generic bag-of-bits type. |
| ctype = "application/octet-stream" |
| maintype, subtype = ctype.split("/", 1) |
| if maintype == "text": |
| fp = open(path) |
| # Note: we should handle calculating the charset |
| msg = MIMEText(fp.read(), _subtype=subtype) |
| fp.close() |
| else: |
| fp = open(path, "rb") |
| msg = MIMEBase(maintype, subtype) |
| msg.set_payload(fp.read()) |
| fp.close() |
| # Encode the payload using Base64 |
| encoders.encode_base64(msg) |
| # Set the filename parameter |
| msg.add_header( |
| "Content-Disposition", "attachment", filename=os.path.basename(path) |
| ) |
| outer.attach(msg) |
| |
| def fileReport(self, report, parameters): |
| mainMsg = """\ |
| BUG REPORT |
| --- |
| Title: %s |
| Description: %s |
| """ % ( |
| report.title, |
| report.description, |
| ) |
| |
| if not parameters.get("To"): |
| raise ReportFailure('No "To" address specified.') |
| if not parameters.get("From"): |
| raise ReportFailure('No "From" address specified.') |
| |
| msg = MIMEMultipart() |
| msg["Subject"] = "BUG REPORT: %s" % (report.title) |
| # FIXME: Get config parameters |
| msg["To"] = parameters.get("To") |
| msg["From"] = parameters.get("From") |
| msg.preamble = mainMsg |
| |
| msg.attach(MIMEText(mainMsg, _subtype="text/plain")) |
| for file in report.files: |
| self.attachFile(msg, file) |
| |
| try: |
| s = smtplib.SMTP( |
| host=parameters.get("SMTP Server"), port=parameters.get("SMTP Port") |
| ) |
| s.sendmail(msg["From"], msg["To"], msg.as_string()) |
| s.close() |
| except: |
| raise ReportFailure("Unable to send message via SMTP.") |
| |
| return "Message sent!" |
| |
| |
| class BugzillaReporter(object): |
| def getName(self): |
| return "Bugzilla" |
| |
| def getParameters(self): |
| return [TextParameter(x) for x in ["URL", "Product"]] |
| |
| def fileReport(self, report, parameters): |
| raise NotImplementedError |
| |
| |
| class RadarClassificationParameter(SelectionParameter): |
| def __init__(self): |
| SelectionParameter.__init__( |
| self, |
| "Classification", |
| [ |
| ["1", "Security"], |
| ["2", "Crash/Hang/Data Loss"], |
| ["3", "Performance"], |
| ["4", "UI/Usability"], |
| ["6", "Serious Bug"], |
| ["7", "Other"], |
| ], |
| ) |
| |
| def saveConfigValue(self): |
| return False |
| |
| def getValue(self, r, bugtype, getConfigOption): |
| if bugtype.find("leak") != -1: |
| return "3" |
| elif bugtype.find("dereference") != -1: |
| return "2" |
| elif bugtype.find("missing ivar release") != -1: |
| return "3" |
| else: |
| return "7" |
| |
| |
| ### |
| |
| |
| def getReporters(): |
| reporters = [] |
| reporters.append(EmailReporter()) |
| return reporters |