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