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