blob: b32caf50b41653a531e07be991f048d36f17b605 [file] [log] [blame]
Daniel Dunbare33d3682008-09-19 23:32:11 +00001"""Methods for reporting bugs."""
2
3import subprocess, sys, os
4
Daniel Dunbar54722492008-09-20 01:43:16 +00005__all__ = ['ReportFailure', 'BugReport', 'getReporters']
6
7#
8
9class ReportFailure(Exception):
10 """Generic exception for failures in bug reporting."""
11 def __init__(self, value):
12 self.value = value
Daniel Dunbare33d3682008-09-19 23:32:11 +000013
14# Collect information about a bug.
15
16class BugReport:
17 def __init__(self, title, description, files):
18 self.title = title
19 self.description = description
20 self.files = files
21
22# Reporter interfaces.
23
24import os
25
26import email, mimetypes, smtplib
27from email import encoders
28from email.message import Message
29from email.mime.base import MIMEBase
30from email.mime.multipart import MIMEMultipart
31from email.mime.text import MIMEText
32
33class EmailReporter:
34 def getName(self):
35 return 'Email'
36
37 def getParameterNames(self):
38 return ['To', 'From', 'SMTP Server', 'SMTP Port']
39
40 # Lifted from python email module examples.
41 def attachFile(self, outer, path):
42 # Guess the content type based on the file's extension. Encoding
43 # will be ignored, although we should check for simple things like
44 # gzip'd or compressed files.
45 ctype, encoding = mimetypes.guess_type(path)
46 if ctype is None or encoding is not None:
47 # No guess could be made, or the file is encoded (compressed), so
48 # use a generic bag-of-bits type.
49 ctype = 'application/octet-stream'
50 maintype, subtype = ctype.split('/', 1)
51 if maintype == 'text':
52 fp = open(path)
53 # Note: we should handle calculating the charset
54 msg = MIMEText(fp.read(), _subtype=subtype)
55 fp.close()
56 else:
57 fp = open(path, 'rb')
58 msg = MIMEBase(maintype, subtype)
59 msg.set_payload(fp.read())
60 fp.close()
61 # Encode the payload using Base64
62 encoders.encode_base64(msg)
63 # Set the filename parameter
64 msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path))
65 outer.attach(msg)
66
67 def fileReport(self, report, parameters):
68 mainMsg = """\
69BUG REPORT
70---
71Title: %s
72Description: %s
73"""%(report.title, report.description)
74
75 if not parameters.get('To') or not parameters.get('From'):
76 raise ValueError,'Invalid email parameters.'
77
78 msg = MIMEMultipart()
79 msg['Subject'] = 'BUG REPORT: %s'%(report.title)
80 # FIXME: Get config parameters
81 msg['To'] = parameters.get('To')
82 msg['From'] = parameters.get('From')
83 msg.preamble = mainMsg
84
85 msg.attach(MIMEText(mainMsg, _subtype='text/plain'))
86 for file in report.files:
87 self.attachFile(msg, file)
88
89 s = smtplib.SMTP(host=parameters.get('SMTP Server'),
90 port=parameters.get('SMTP Port'))
91 s.sendmail(msg['From'], msg['To'], msg.as_string())
92 s.close()
93
94class BugzillaReporter:
95 def getName(self):
96 return 'Bugzilla'
97
98 def getParameterNames(self):
99 return ['URL', 'Product']
100
101 def fileReport(self, report, parameters):
102 raise NotImplementedError
103
104class RadarReporter:
105 @staticmethod
106 def isAvailable():
107 # FIXME: Find this .scpt better
108 path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt')
109 try:
110 p = subprocess.Popen(['osascript',path],
111 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
112 except:
113 return False
114 data,err = p.communicate()
115 res = p.wait()
116 # FIXME: Check version? Check for no errors?
117 return res == 0
118
119 def getName(self):
120 return 'Radar'
121
122 def getParameterNames(self):
123 return ['Component', 'Component Version']
124
125 def fileReport(self, report, parameters):
126 component = parameters.get('Component', '')
127 componentVersion = parameters.get('Component Version', '')
128 personID = ""
129 diagnosis = ""
130 config = ""
131
132 if not component.strip():
133 component = 'Bugs found by clang Analyzer'
134 if not componentVersion.strip():
135 componentVersion = 'X'
136
137 script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt')
138 args = ['osascript', script, component, componentVersion, personID, report.title,
139 report.description, diagnosis, config] + map(os.path.abspath, report.files)
140# print >>sys.stderr, args
141 try:
142 p = subprocess.Popen(args,
143 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
144 except:
Daniel Dunbar54722492008-09-20 01:43:16 +0000145 raise ReportFailure("Unable to file radar (AppleScript failure).")
Daniel Dunbare33d3682008-09-19 23:32:11 +0000146 data, err = p.communicate()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000147 res = p.wait()
Daniel Dunbare33d3682008-09-19 23:32:11 +0000148
149 if res:
Daniel Dunbar54722492008-09-20 01:43:16 +0000150 raise ReportFailure("Unable to file radar (AppleScript failure).")
Daniel Dunbare33d3682008-09-19 23:32:11 +0000151
Daniel Dunbar54722492008-09-20 01:43:16 +0000152 try:
153 values = eval(data)
154 except:
155 raise ReportFailure("Unable to process radar results.")
156
157 # We expect (int: bugID, str: message)
158 if len(values) != 2 or not isinstance(values[0], int):
159 raise ReportFailure("Unable to process radar results.")
160
161 bugID,message = values
162 bugID = int(bugID)
163
164 if not bugID:
165 raise ReportFailure(message)
166
167 return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID)
Daniel Dunbare33d3682008-09-19 23:32:11 +0000168
169###
170
171def getReporters():
172 reporters = []
173 if RadarReporter.isAvailable():
174 reporters.append(RadarReporter())
Daniel Dunbarfeee21a2008-09-21 19:06:51 +0000175 reporters.append(EmailReporter())
Daniel Dunbare33d3682008-09-19 23:32:11 +0000176 return reporters
177