1
# Copyright (C) 2006 by Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details.
12
# You should have received a copy of the GNU General Public License
13
# along with this program; if not, write to the Free Software
14
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from getpass import getpass
19
from urlparse import urlsplit, urlunsplit
25
class LaunchpadService(object):
26
"""A service to talk to Launchpad via XMLRPC.
28
See http://bazaar-vcs.org/Specs/LaunchpadRpc for the methods we can call.
31
# NB: this should always end in a slash to avoid xmlrpclib appending
33
DEFAULT_SERVICE_URL = 'http://xmlrpc.launchpad.net/bazaar/'
36
registrant_email = None
37
registrant_password = None
40
def __init__(self, transport=None):
41
"""Construct a new service talking to the launchpad rpc server"""
43
uri_type = urllib.splittype(self.service_url)[0]
44
if uri_type == 'https':
45
transport = xmlrpclib.SafeTransport()
47
transport = xmlrpclib.Transport()
48
transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \
49
% (bzrlib.__version__, xmlrpclib.__version__)
50
self.transport = transport
54
def service_url(self):
55
"""Return the http or https url for the xmlrpc server.
57
This does not include the username/password credentials.
59
key = 'BZR_LP_XMLRPC_URL'
61
return os.environ[key]
63
return self.DEFAULT_SERVICE_URL
66
"""Return the proxy for XMLRPC requests."""
67
# auth info must be in url
68
# TODO: if there's no registrant email perhaps we should just connect
70
scheme, hostinfo, path = urlsplit(self.service_url)[:3]
71
assert '@' not in hostinfo
72
assert self.registrant_email is not None
73
assert self.registrant_password is not None
74
hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
75
urllib.quote(self.registrant_password),
77
url = urlunsplit((scheme, hostinfo, path, '', ''))
78
return xmlrpclib.ServerProxy(url, transport=self.transport)
80
def gather_user_credentials(self):
81
"""Get the password from the user."""
82
config = bzrlib.config.GlobalConfig()
83
self.registrant_email = config.user_email()
84
if self.registrant_password is None:
85
prompt = 'launchpad.net password for %s: ' % \
87
self.registrant_password = getpass(prompt)
89
def send_request(self, method_name, method_params):
90
proxy = self.get_proxy()
92
method = getattr(proxy, method_name)
93
result = method(*method_params)
97
class BaseRequest(object):
98
"""Base request for talking to a XMLRPC server."""
100
# Set this to the XMLRPC method name.
103
def _request_params(self):
104
"""Return the arguments to pass to the method"""
105
raise NotImplementedError(self._request_params)
107
def submit(self, service):
108
"""Submit request to Launchpad XMLRPC server.
110
:param service: LaunchpadService indicating where to send
111
the request and the authentication credentials.
113
return service.send_request(self._methodname, self._request_params())
116
class BranchRegistrationRequest(BaseRequest):
117
"""Request to tell Launchpad about a bzr branch."""
119
_methodname = 'register_branch'
121
def __init__(self, branch_url,
124
branch_description='',
129
self.branch_url = branch_url
131
self.branch_name = branch_name
133
self.branch_name = self._find_default_branch_name(self.branch_url)
134
self.branch_title = branch_title
135
self.branch_description = branch_description
136
self.author_email = author_email
137
self.product_name = product_name
139
def _request_params(self):
140
"""Return xmlrpc request parameters"""
141
# This must match the parameter tuple expected by Launchpad for this
143
return (self.branch_url,
146
self.branch_description,
151
def _find_default_branch_name(self, branch_url):
152
i = branch_url.rfind('/')
153
return branch_url[i+1:]
156
class BranchBugLinkRequest(BaseRequest):
157
"""Request to link a bzr branch in Launchpad to a bug."""
159
_methodname = 'link_branch_to_bug'
161
def __init__(self, branch_url, bug_id):
164
self.branch_url = branch_url
166
def _request_params(self):
167
"""Return xmlrpc request parameters"""
168
# This must match the parameter tuple expected by Launchpad for this
170
return (self.branch_url, self.bug_id, '')