227 lines · python
1#!/usr/bin/env python2# -*- coding: utf-8 -*-3 4"""Methods for reporting bugs."""5 6import subprocess, sys, os7 8__all__ = ["ReportFailure", "BugReport", "getReporters"]9 10#11 12 13class ReportFailure(Exception):14 """Generic exception for failures in bug reporting."""15 16 def __init__(self, value):17 self.value = value18 19 20# Collect information about a bug.21 22 23class BugReport(object):24 def __init__(self, title, description, files):25 self.title = title26 self.description = description27 self.files = files28 29 30# Reporter interfaces.31 32import os33 34import email, mimetypes, smtplib35from email import encoders36from email.message import Message37from email.mime.base import MIMEBase38from email.mime.multipart import MIMEMultipart39from email.mime.text import MIMEText40 41# ===------------------------------------------------------------------------===#42# ReporterParameter43# ===------------------------------------------------------------------------===#44 45 46class ReporterParameter(object):47 def __init__(self, n):48 self.name = n49 50 def getName(self):51 return self.name52 53 def getValue(self, r, bugtype, getConfigOption):54 return getConfigOption(r.getName(), self.getName())55 56 def saveConfigValue(self):57 return True58 59 60class TextParameter(ReporterParameter):61 def getHTML(self, r, bugtype, getConfigOption):62 return """\63<tr>64<td class="form_clabel">%s:</td>65<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>66</tr>""" % (67 self.getName(),68 r.getName(),69 self.getName(),70 self.getValue(r, bugtype, getConfigOption),71 )72 73 74class SelectionParameter(ReporterParameter):75 def __init__(self, n, values):76 ReporterParameter.__init__(self, n)77 self.values = values78 79 def getHTML(self, r, bugtype, getConfigOption):80 default = self.getValue(r, bugtype, getConfigOption)81 return """\82<tr>83<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s">84%s85</select></td>""" % (86 self.getName(),87 r.getName(),88 self.getName(),89 "\n".join(90 [91 """\92<option value="%s"%s>%s</option>"""93 % (o[0], o[0] == default and ' selected="selected"' or "", o[1])94 for o in self.values95 ]96 ),97 )98 99 100# ===------------------------------------------------------------------------===#101# Reporters102# ===------------------------------------------------------------------------===#103 104 105class EmailReporter(object):106 def getName(self):107 return "Email"108 109 def getParameters(self):110 return [TextParameter(x) for x in ["To", "From", "SMTP Server", "SMTP Port"]]111 112 # Lifted from python email module examples.113 def attachFile(self, outer, path):114 # Guess the content type based on the file's extension. Encoding115 # will be ignored, although we should check for simple things like116 # gzip'd or compressed files.117 ctype, encoding = mimetypes.guess_type(path)118 if ctype is None or encoding is not None:119 # No guess could be made, or the file is encoded (compressed), so120 # use a generic bag-of-bits type.121 ctype = "application/octet-stream"122 maintype, subtype = ctype.split("/", 1)123 if maintype == "text":124 fp = open(path)125 # Note: we should handle calculating the charset126 msg = MIMEText(fp.read(), _subtype=subtype)127 fp.close()128 else:129 fp = open(path, "rb")130 msg = MIMEBase(maintype, subtype)131 msg.set_payload(fp.read())132 fp.close()133 # Encode the payload using Base64134 encoders.encode_base64(msg)135 # Set the filename parameter136 msg.add_header(137 "Content-Disposition", "attachment", filename=os.path.basename(path)138 )139 outer.attach(msg)140 141 def fileReport(self, report, parameters):142 mainMsg = """\143BUG REPORT144---145Title: %s146Description: %s147""" % (148 report.title,149 report.description,150 )151 152 if not parameters.get("To"):153 raise ReportFailure('No "To" address specified.')154 if not parameters.get("From"):155 raise ReportFailure('No "From" address specified.')156 157 msg = MIMEMultipart()158 msg["Subject"] = "BUG REPORT: %s" % (report.title)159 # FIXME: Get config parameters160 msg["To"] = parameters.get("To")161 msg["From"] = parameters.get("From")162 msg.preamble = mainMsg163 164 msg.attach(MIMEText(mainMsg, _subtype="text/plain"))165 for file in report.files:166 self.attachFile(msg, file)167 168 try:169 s = smtplib.SMTP(170 host=parameters.get("SMTP Server"), port=parameters.get("SMTP Port")171 )172 s.sendmail(msg["From"], msg["To"], msg.as_string())173 s.close()174 except:175 raise ReportFailure("Unable to send message via SMTP.")176 177 return "Message sent!"178 179 180class BugzillaReporter(object):181 def getName(self):182 return "Bugzilla"183 184 def getParameters(self):185 return [TextParameter(x) for x in ["URL", "Product"]]186 187 def fileReport(self, report, parameters):188 raise NotImplementedError189 190 191class RadarClassificationParameter(SelectionParameter):192 def __init__(self):193 SelectionParameter.__init__(194 self,195 "Classification",196 [197 ["1", "Security"],198 ["2", "Crash/Hang/Data Loss"],199 ["3", "Performance"],200 ["4", "UI/Usability"],201 ["6", "Serious Bug"],202 ["7", "Other"],203 ],204 )205 206 def saveConfigValue(self):207 return False208 209 def getValue(self, r, bugtype, getConfigOption):210 if bugtype.find("leak") != -1:211 return "3"212 elif bugtype.find("dereference") != -1:213 return "2"214 elif bugtype.find("missing ivar release") != -1:215 return "3"216 else:217 return "7"218 219 220###221 222 223def getReporters():224 reporters = []225 reporters.append(EmailReporter())226 return reporters227