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