llvm/clang/tools/scan-view/share/Reporter.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Methods for reporting bugs."""

import subprocess, sys, os

__all__ = ["ReportFailure", "BugReport", "getReporters"]

#


class ReportFailure(Exception):
    """Generic exception for failures in bug reporting."""

    def __init__(self, value):
        self.value = value


# Collect information about a bug.


class BugReport(object):
    def __init__(self, title, description, files):
        self.title = title
        self.description = description
        self.files = files


# Reporter interfaces.

import os

import email, mimetypes, smtplib
from email import encoders
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# ===------------------------------------------------------------------------===#
# ReporterParameter
# ===------------------------------------------------------------------------===#


class ReporterParameter(object):
    def __init__(self, n):
        self.name = n

    def getName(self):
        return self.name

    def getValue(self, r, bugtype, getConfigOption):
        return getConfigOption(r.getName(), self.getName())

    def saveConfigValue(self):
        return True


class TextParameter(ReporterParameter):
    def getHTML(self, r, bugtype, getConfigOption):
        return """\
<tr>
<td class="form_clabel">%s:</td>
<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
</tr>""" % (
            self.getName(),
            r.getName(),
            self.getName(),
            self.getValue(r, bugtype, getConfigOption),
        )


class SelectionParameter(ReporterParameter):
    def __init__(self, n, values):
        ReporterParameter.__init__(self, n)
        self.values = values

    def getHTML(self, r, bugtype, getConfigOption):
        default = self.getValue(r, bugtype, getConfigOption)
        return """\
<tr>
<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s">
%s
</select></td>""" % (
            self.getName(),
            r.getName(),
            self.getName(),
            "\n".join(
                [
                    """\
<option value="%s"%s>%s</option>"""
                    % (o[0], o[0] == default and ' selected="selected"' or "", o[1])
                    for o in self.values
                ]
            ),
        )


# ===------------------------------------------------------------------------===#
# Reporters
# ===------------------------------------------------------------------------===#


class EmailReporter(object):
    def getName(self):
        return "Email"

    def getParameters(self):
        return [TextParameter(x) for x in ["To", "From", "SMTP Server", "SMTP Port"]]

    # Lifted from python email module examples.
    def attachFile(self, outer, path):
        # Guess the content type based on the file's extension.  Encoding
        # will be ignored, although we should check for simple things like
        # gzip'd or compressed files.
        ctype, encoding = mimetypes.guess_type(path)
        if ctype is None or encoding is not None:
            # No guess could be made, or the file is encoded (compressed), so
            # use a generic bag-of-bits type.
            ctype = "application/octet-stream"
        maintype, subtype = ctype.split("/", 1)
        if maintype == "text":
            fp = open(path)
            # Note: we should handle calculating the charset
            msg = MIMEText(fp.read(), _subtype=subtype)
            fp.close()
        else:
            fp = open(path, "rb")
            msg = MIMEBase(maintype, subtype)
            msg.set_payload(fp.read())
            fp.close()
            # Encode the payload using Base64
            encoders.encode_base64(msg)
        # Set the filename parameter
        msg.add_header(
            "Content-Disposition", "attachment", filename=os.path.basename(path)
        )
        outer.attach(msg)

    def fileReport(self, report, parameters):
        mainMsg = """\
BUG REPORT
---
Title: %s
Description: %s
""" % (
            report.title,
            report.description,
        )

        if not parameters.get("To"):
            raise ReportFailure('No "To" address specified.')
        if not parameters.get("From"):
            raise ReportFailure('No "From" address specified.')

        msg = MIMEMultipart()
        msg["Subject"] = "BUG REPORT: %s" % (report.title)
        # FIXME: Get config parameters
        msg["To"] = parameters.get("To")
        msg["From"] = parameters.get("From")
        msg.preamble = mainMsg

        msg.attach(MIMEText(mainMsg, _subtype="text/plain"))
        for file in report.files:
            self.attachFile(msg, file)

        try:
            s = smtplib.SMTP(
                host=parameters.get("SMTP Server"), port=parameters.get("SMTP Port")
            )
            s.sendmail(msg["From"], msg["To"], msg.as_string())
            s.close()
        except:
            raise ReportFailure("Unable to send message via SMTP.")

        return "Message sent!"


class BugzillaReporter(object):
    def getName(self):
        return "Bugzilla"

    def getParameters(self):
        return [TextParameter(x) for x in ["URL", "Product"]]

    def fileReport(self, report, parameters):
        raise NotImplementedError


class RadarClassificationParameter(SelectionParameter):
    def __init__(self):
        SelectionParameter.__init__(
            self,
            "Classification",
            [
                ["1", "Security"],
                ["2", "Crash/Hang/Data Loss"],
                ["3", "Performance"],
                ["4", "UI/Usability"],
                ["6", "Serious Bug"],
                ["7", "Other"],
            ],
        )

    def saveConfigValue(self):
        return False

    def getValue(self, r, bugtype, getConfigOption):
        if bugtype.find("leak") != -1:
            return "3"
        elif bugtype.find("dereference") != -1:
            return "2"
        elif bugtype.find("missing ivar release") != -1:
            return "3"
        else:
            return "7"


###


def getReporters():
    reporters = []
    reporters.append(EmailReporter())
    return reporters