blob: 7887636559b5db9cc260e100c3fa8ff484349d86 [file] [log] [blame]
Vassil Vassilev3dd966b2017-03-02 18:47:22 +00001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
Daniel Dunbard4c23372008-09-19 23:32:11 +00004"""Methods for reporting bugs."""
5
6import subprocess, sys, os
7
Daniel Dunbar025b48d2008-09-20 01:43:16 +00008__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
Daniel Dunbard4c23372008-09-19 23:32:11 +000016
17# Collect information about a bug.
18
Serge Guelton09616bd2018-12-03 12:12:48 +000019class BugReport(object):
Daniel Dunbard4c23372008-09-19 23:32:11 +000020 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
Ted Kremenek094ff0b2008-09-30 16:08:13 +000036#===------------------------------------------------------------------------===#
37# ReporterParameter
38#===------------------------------------------------------------------------===#
39
Serge Guelton09616bd2018-12-03 12:12:48 +000040class ReporterParameter(object):
Ted Kremenek094ff0b2008-09-30 16:08:13 +000041 def __init__(self, n):
42 self.name = n
Ted Kremenek094ff0b2008-09-30 16:08:13 +000043 def getName(self):
44 return self.name
Ted Kremenekb1662992008-09-30 16:37:50 +000045 def getValue(self,r,bugtype,getConfigOption):
46 return getConfigOption(r.getName(),self.getName())
Ted Kremenek094ff0b2008-09-30 16:08:13 +000047 def saveConfigValue(self):
48 return True
49
50class TextParameter (ReporterParameter):
Ted Kremenek094ff0b2008-09-30 16:08:13 +000051 def getHTML(self,r,bugtype,getConfigOption):
52 return """\
53<tr>
Ted Kremenekb1662992008-09-30 16:37:50 +000054<td class="form_clabel">%s:</td>
55<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
Ted Kremenek094ff0b2008-09-30 16:08:13 +000056</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption))
57
Ted Kremenekb1662992008-09-30 16:37:50 +000058class 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):
Ted Kremenek9f403162008-09-30 17:12:32 +000064 default = self.getValue(r,bugtype,getConfigOption)
Ted Kremenekb1662992008-09-30 16:37:50 +000065 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(["""\
Ted Kremenek9f403162008-09-30 17:12:32 +000070<option value="%s"%s>%s</option>"""%(o[0],
71 o[0] == default and ' selected="selected"' or '',
72 o[1]) for o in self.values]))
Ted Kremenekb1662992008-09-30 16:37:50 +000073
Ted Kremenek094ff0b2008-09-30 16:08:13 +000074#===------------------------------------------------------------------------===#
75# Reporters
76#===------------------------------------------------------------------------===#
77
Serge Guelton09616bd2018-12-03 12:12:48 +000078class EmailReporter(object):
Daniel Dunbard4c23372008-09-19 23:32:11 +000079 def getName(self):
80 return 'Email'
81
Ted Kremenek094ff0b2008-09-30 16:08:13 +000082 def getParameters(self):
83 return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port'])
Daniel Dunbard4c23372008-09-19 23:32:11 +000084
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
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000120 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.')
Daniel Dunbard4c23372008-09-19 23:32:11 +0000124
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)
Daniel Dunbar553fdc62008-09-22 01:21:30 +0000135
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!"
Daniel Dunbard4c23372008-09-19 23:32:11 +0000145
Serge Guelton09616bd2018-12-03 12:12:48 +0000146class BugzillaReporter(object):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000147 def getName(self):
148 return 'Bugzilla'
149
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000150 def getParameters(self):
151 return map(lambda x:TextParameter(x),['URL','Product'])
Daniel Dunbard4c23372008-09-19 23:32:11 +0000152
153 def fileReport(self, report, parameters):
154 raise NotImplementedError
Ted Kremenekfb0fef92008-09-30 17:00:30 +0000155
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
Ted Kremenek9f403162008-09-30 17:12:32 +0000166
167 def getValue(self,r,bugtype,getConfigOption):
Ted Kremenek4bc52fd2008-10-24 21:23:51 +0000168 if bugtype.find("leak") != -1:
Ted Kremenek9f403162008-09-30 17:12:32 +0000169 return '3'
Ted Kremenek049ba7f2008-10-23 21:36:52 +0000170 elif bugtype.find("dereference") != -1:
171 return '2'
Ted Kremenek1e0d95e2008-10-30 17:29:54 +0000172 elif bugtype.find("missing ivar release") != -1:
173 return '3'
Ted Kremenek9f403162008-09-30 17:12:32 +0000174 else:
175 return '7'
Ted Kremenekfb0fef92008-09-30 17:00:30 +0000176
Serge Guelton09616bd2018-12-03 12:12:48 +0000177class RadarReporter(object):
Daniel Dunbard4c23372008-09-19 23:32:11 +0000178 @staticmethod
179 def isAvailable():
180 # FIXME: Find this .scpt better
Jonathan Roelofse9614322015-11-09 16:12:56 +0000181 path = os.path.join(os.path.dirname(__file__),'../share/scan-view/GetRadarVersion.scpt')
Ted Kremeneka930b562008-09-30 05:45:59 +0000182 try:
183 p = subprocess.Popen(['osascript',path],
184 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
185 except:
Daniel Dunbard4c23372008-09-19 23:32:11 +0000186 return False
187 data,err = p.communicate()
188 res = p.wait()
189 # FIXME: Check version? Check for no errors?
190 return res == 0
191
192 def getName(self):
193 return 'Radar'
194
Ted Kremenek094ff0b2008-09-30 16:08:13 +0000195 def getParameters(self):
Ted Kremenekb1662992008-09-30 16:37:50 +0000196 return [ TextParameter('Component'), TextParameter('Component Version'),
Ted Kremenekfb0fef92008-09-30 17:00:30 +0000197 RadarClassificationParameter() ]
Daniel Dunbard4c23372008-09-19 23:32:11 +0000198
199 def fileReport(self, report, parameters):
200 component = parameters.get('Component', '')
201 componentVersion = parameters.get('Component Version', '')
Ted Kremenekb1662992008-09-30 16:37:50 +0000202 classification = parameters.get('Classification', '')
Daniel Dunbard4c23372008-09-19 23:32:11 +0000203 personID = ""
204 diagnosis = ""
205 config = ""
206
207 if not component.strip():
208 component = 'Bugs found by clang Analyzer'
209 if not componentVersion.strip():
210 componentVersion = 'X'
211
Jonathan Roelofse9614322015-11-09 16:12:56 +0000212 script = os.path.join(os.path.dirname(__file__),'../share/scan-view/FileRadar.scpt')
Ted Kremenekb1662992008-09-30 16:37:50 +0000213 args = ['osascript', script, component, componentVersion, classification, personID, report.title,
Daniel Dunbard4c23372008-09-19 23:32:11 +0000214 report.description, diagnosis, config] + map(os.path.abspath, report.files)
215# print >>sys.stderr, args
Ted Kremenekb1662992008-09-30 16:37:50 +0000216 try:
217 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
218 except:
Daniel Dunbar025b48d2008-09-20 01:43:16 +0000219 raise ReportFailure("Unable to file radar (AppleScript failure).")
Daniel Dunbard4c23372008-09-19 23:32:11 +0000220 data, err = p.communicate()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000221 res = p.wait()
Daniel Dunbard4c23372008-09-19 23:32:11 +0000222
223 if res:
Daniel Dunbar025b48d2008-09-20 01:43:16 +0000224 raise ReportFailure("Unable to file radar (AppleScript failure).")
Daniel Dunbard4c23372008-09-19 23:32:11 +0000225
Daniel Dunbar025b48d2008-09-20 01:43:16 +0000226 try:
227 values = eval(data)
228 except:
229 raise ReportFailure("Unable to process radar results.")
230
231 # We expect (int: bugID, str: message)
232 if len(values) != 2 or not isinstance(values[0], int):
233 raise ReportFailure("Unable to process radar results.")
234
235 bugID,message = values
236 bugID = int(bugID)
237
238 if not bugID:
239 raise ReportFailure(message)
240
241 return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID)
Daniel Dunbard4c23372008-09-19 23:32:11 +0000242
243###
244
245def getReporters():
246 reporters = []
247 if RadarReporter.isAvailable():
248 reporters.append(RadarReporter())
Daniel Dunbara00b7a82008-09-21 19:06:51 +0000249 reporters.append(EmailReporter())
Daniel Dunbard4c23372008-09-19 23:32:11 +0000250 return reporters
251