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