~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""XML-RPC APIs for Malone."""

__metaclass__ = type
__all__ = ["FileBugAPI", "ExternalBugTrackerTokenAPI"]

from zope.component import getUtility
from zope.interface import implements

from canonical.launchpad.interfaces.authtoken import LoginTokenType
from canonical.launchpad.interfaces.logintoken import ILoginTokenSet
from canonical.launchpad.webapp import (
    canonical_url,
    LaunchpadXMLRPCView,
    )
from canonical.launchpad.xmlrpc import faults
from lp.app.errors import NotFoundError
from lp.bugs.interfaces.bug import CreateBugParams
from lp.bugs.interfaces.externalbugtracker import IExternalBugTrackerTokenAPI
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.product import IProductSet


class FileBugAPI(LaunchpadXMLRPCView):
    """The XML-RPC API for filing bugs in Malone."""

    def filebug(self, params):
        """Report a bug in a distribution or product.

        :params: A dict containing the following keys:

        REQUIRED:
          summary: A string
          comment: A string

        OPTIONAL:
          product: The product name, as a string. Default None.
          distro: The distro name, as a string. Default None.
          package: A string, allowed only if distro is specified.
                   Default None.
          security_related: Is this a security vulnerability?
                            Default False.
          subscribers: A list of email addresses. Default None.

        Either product or distro must be provided.
        """
        product = params.get('product')
        distro = params.get('distro')
        package = params.get('package')
        summary = params.get('summary')
        comment = params.get('comment')
        security_related = params.get('security_related')
        subscribers = params.get('subscribers')

        if product and distro:
            return faults.FileBugGotProductAndDistro()

        if product:
            target = getUtility(IProductSet).getByName(product)
            if target is None:
                return faults.NoSuchProduct(product)
        elif distro:
            distro_object = getUtility(IDistributionSet).getByName(distro)

            if distro_object is None:
                return faults.NoSuchDistribution(distro)

            if package:
                try:
                    spname = distro_object.guessPublishedSourcePackageName(
                        package)
                except NotFoundError:
                    return faults.NoSuchPackage(package)

                target = distro_object.getSourcePackage(spname)
            else:
                target = distro_object
        else:
            return faults.FileBugMissingProductOrDistribution()

        if not summary:
            return faults.RequiredParameterMissing('summary')

        if not comment:
            return faults.RequiredParameterMissing('comment')

        # Convert arguments into values that IBugTarget.createBug
        # understands.
        personset = getUtility(IPersonSet)
        subscriber_list = []
        if subscribers:
            for subscriber_email in subscribers:
                subscriber = personset.getByEmail(subscriber_email)
                if not subscriber:
                    return faults.NoSuchPerson(
                        type="subscriber", email_address=subscriber_email)
                else:
                    subscriber_list.append(subscriber)

        security_related = bool(security_related)

        # Privacy is always set the same as security, by default.
        private = security_related

        params = CreateBugParams(
            owner=self.user, title=summary, comment=comment,
            security_related=security_related, private=private,
            subscribers=subscriber_list)

        bug = target.createBug(params)

        return canonical_url(bug)


class ExternalBugTrackerTokenAPI(LaunchpadXMLRPCView):
    """The private XML-RPC API for generating bug tracker login tokens."""

    implements(IExternalBugTrackerTokenAPI)

    def newBugTrackerToken(self):
        """Generate a new `LoginToken` for a bug tracker and return it.

        The `LoginToken` will be of `LoginTokenType` BUGTRACKER.
        """
        login_token = getUtility(ILoginTokenSet).new(
            None, None, 'externalbugtrackers@launchpad.net',
            LoginTokenType.BUGTRACKER)

        return login_token.token