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