~bzr-pqm/bzr/bzr.dev

5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
1
# Copyright (C) 2007-2011 Canonical Ltd
2245.8.3 by Martin Pool
Start adding indirection transport
2
#
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.
7
#
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
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2245.8.3 by Martin Pool
Start adding indirection transport
16
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Directory lookup that uses Launchpad."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
2245.8.3 by Martin Pool
Start adding indirection transport
20
5121.2.1 by Jelmer Vernooij
Remove unused imports in lp_directory.
21
from urlparse import urlsplit
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
22
import xmlrpclib
23
2245.8.4 by Martin Pool
lp:/// indirection works
24
from bzrlib import (
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
25
    debug,
2245.8.4 by Martin Pool
lp:/// indirection works
26
    errors,
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
27
    trace,
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
28
    transport,
2245.8.4 by Martin Pool
lp:/// indirection works
29
    )
6150.3.12 by Jonathan Riddell
add missing import
30
from bzrlib.i18n import gettext
2245.8.4 by Martin Pool
lp:/// indirection works
31
2898.4.7 by James Henstridge
Fix up tests.
32
from bzrlib.plugins.launchpad.lp_registration import (
33
    LaunchpadService, ResolveLaunchpadPathRequest)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
34
from bzrlib.plugins.launchpad.account import get_lp_login
35
36
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
37
# As bzrlib.transport.remote may not be loaded yet, make sure bzr+ssh
38
# is counted as a netloc protocol.
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
39
transport.register_urlparse_netloc_protocol('bzr+ssh')
40
transport.register_urlparse_netloc_protocol('lp')
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
41
5462.4.5 by Barry Warsaw
We only need to include distroseries shortcuts.
42
_ubuntu_series_shortcuts = {
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
43
    'n': 'natty',
44
    'm': 'maverick',
45
    'l': 'lucid',
46
    'k': 'karmic',
47
    'j': 'jaunty',
48
    'h': 'hardy',
49
    'd': 'dapper',
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
50
    }
51
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
52
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
53
class LaunchpadDirectory(object):
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
54
3031.2.4 by jml at canonical
Only split the URL once.
55
    def _requires_launchpad_login(self, scheme, netloc, path, query,
56
                                  fragment):
57
        """Does the URL require a Launchpad login in order to be reached?
58
59
        The URL is specified by its parsed components, as returned from
60
        urlsplit.
61
        """
3031.2.3 by jml at canonical
Make the test pass -- don't include sftp URLs if there's no lp login.
62
        return (scheme in ('bzr+ssh', 'sftp')
63
                and (netloc.endswith('launchpad.net')
64
                     or netloc.endswith('launchpad.dev')))
3031.2.1 by jml at canonical
Factor out the method that determines if a URL is a LP url.
65
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
66
    def look_up(self, name, url):
3251.4.5 by Aaron Bentley
Add docstring
67
        """See DirectoryService.look_up"""
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
68
        return self._resolve(url)
69
5609.24.9 by John Arbash Meinel
A bunch more tests and bug fixes for the local resolution.
70
    def _resolve_locally(self, path, url, _request_factory):
71
        # This is the best I could work out about XMLRPC. If an lp: url
72
        # includes ~user, then it is specially validated. Otherwise, it is just
73
        # sent to +branch/$path.
74
        _, netloc, _, _, _ = urlsplit(url)
75
        if netloc == '':
76
            netloc = LaunchpadService.DEFAULT_INSTANCE
77
        base_url = LaunchpadService.LAUNCHPAD_DOMAINS[netloc]
78
        base = 'bzr+ssh://bazaar.%s/' % (base_url,)
79
        maybe_invalid = False
80
        if path.startswith('~'):
81
            # A ~user style path, validate it a bit.
82
            # If a path looks fishy, fall back to asking XMLRPC to
83
            # resolve it for us. That way we still get their nicer error
84
            # messages.
85
            parts = path.split('/')
86
            if (len(parts) < 3
87
                or (parts[1] in ('ubuntu', 'debian') and len(parts) < 5)):
88
                # This special case requires 5-parts to be valid.
89
                maybe_invalid = True
90
        else:
91
            base += '+branch/'
92
        if maybe_invalid:
93
            return self._resolve_via_xmlrpc(path, url, _request_factory)
94
        return {'urls': [base + path]}
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
95
96
    def _resolve_via_xmlrpc(self, path, url, _request_factory):
97
        service = LaunchpadService.for_url(url)
98
        resolve = _request_factory(path)
99
        try:
100
            result = resolve.submit(service)
101
        except xmlrpclib.Fault, fault:
102
            raise errors.InvalidURL(
103
                path=url, extra=fault.faultString)
104
        return result
105
106
    def _update_url_scheme(self, url):
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
107
        # Do ubuntu: and debianlp: expansions.
5462.4.10 by Vincent Ladeuil
Fix python2.4 compatibility.
108
        scheme, netloc, path, query, fragment = urlsplit(url)
109
        if scheme in ('ubuntu', 'debianlp'):
110
            if scheme == 'ubuntu':
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
111
                distro = 'ubuntu'
5462.4.5 by Barry Warsaw
We only need to include distroseries shortcuts.
112
                distro_series = _ubuntu_series_shortcuts
5462.4.10 by Vincent Ladeuil
Fix python2.4 compatibility.
113
            elif scheme == 'debianlp':
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
114
                distro = 'debian'
5462.4.5 by Barry Warsaw
We only need to include distroseries shortcuts.
115
                # No shortcuts for Debian distroseries.
116
                distro_series = {}
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
117
            else:
118
                raise AssertionError('scheme should be ubuntu: or debianlp:')
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
119
            # Split the path.  It's either going to be 'project' or
120
            # 'series/project', but recognize that it may be a series we don't
121
            # know about.
5462.4.10 by Vincent Ladeuil
Fix python2.4 compatibility.
122
            path_parts = path.split('/')
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
123
            if len(path_parts) == 1:
124
                # It's just a project name.
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
125
                lp_url_template = 'lp:%(distro)s/%(project)s'
126
                project = path_parts[0]
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
127
                series = None
128
            elif len(path_parts) == 2:
129
                # It's a series and project.
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
130
                lp_url_template = 'lp:%(distro)s/%(series)s/%(project)s'
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
131
                series, project = path_parts
132
            else:
133
                # There are either 0 or > 2 path parts, neither of which is
134
                # supported for these schemes.
6191.2.1 by Martin Pool
Avoid NameError when given an invalid ubuntu: launchpad url
135
                raise errors.InvalidURL('Bad path: %s' % url)
5462.4.3 by Barry Warsaw
* Add back Ubuntu distroseries shortcuts.
136
            # Expand any series shortcuts, but keep unknown series.
137
            series = distro_series.get(series, series)
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
138
            # Hack the url and let the following do the final resolution.
139
            url = lp_url_template % dict(
140
                distro=distro,
141
                series=series,
142
                project=project)
5462.4.10 by Vincent Ladeuil
Fix python2.4 compatibility.
143
            scheme, netloc, path, query, fragment = urlsplit(url)
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
144
        return url, path
145
5609.24.8 by John Arbash Meinel
Fix the launchpad-login tests. And fix a bad commit typo.
146
    def _expand_user(self, path, url, lp_login):
5361.1.1 by John Arbash Meinel
Handle a simple ~ in lp: urls.
147
        if path.startswith('~/'):
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
148
            if lp_login is None:
5361.1.1 by John Arbash Meinel
Handle a simple ~ in lp: urls.
149
                raise errors.InvalidURL(path=url,
150
                    extra='Cannot resolve "~" to your username.'
151
                          ' See "bzr help launchpad-login"')
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
152
            path = '~' + lp_login + path[1:]
153
        return path
154
155
    def _resolve(self, url,
156
                 _request_factory=ResolveLaunchpadPathRequest,
157
                 _lp_login=None):
158
        """Resolve the base URL for this transport."""
159
        url, path = self._update_url_scheme(url)
160
        if _lp_login is None:
161
            _lp_login = get_lp_login()
162
        path = path.strip('/')
5609.24.8 by John Arbash Meinel
Fix the launchpad-login tests. And fix a bad commit typo.
163
        path = self._expand_user(path, url, _lp_login)
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
164
        if _lp_login is not None:
5609.24.9 by John Arbash Meinel
A bunch more tests and bug fixes for the local resolution.
165
            result = self._resolve_locally(path, url, _request_factory)
166
            if 'launchpad' in debug.debug_flags:
5609.24.11 by John Arbash Meinel
If someone uses -Dlaunchpad, use the 'official' URL instead of the local one.
167
                local_res = result
168
                result = self._resolve_via_xmlrpc(path, url, _request_factory)
6150.3.4 by Jonathan Riddell
more launchpad plugin gettext()ing
169
                trace.note(gettext(
170
                    'resolution for {0}\n  local: {1}\n remote: {2}').format(
6150.3.11 by Jonathan Riddell
syntax fixes
171
                           url, local_res['urls'], result['urls']))
5609.24.7 by John Arbash Meinel
Fix bug #397739. Avoid doing an XMLRPC lookup if a user
172
        else:
173
            result = self._resolve_via_xmlrpc(path, url, _request_factory)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
174
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
175
        if 'launchpad' in debug.debug_flags:
4505.6.2 by Jonathan Lange
Extract a method that gets a service from a URL.
176
            trace.mutter("resolve_lp_path(%r) == %r", url, result)
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
177
3270.4.1 by James Westby
Warn the user when resolving lp: URLs if they haven't set their login.
178
        _warned_login = False
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
179
        for url in result['urls']:
180
            scheme, netloc, path, query, fragment = urlsplit(url)
3031.2.4 by jml at canonical
Only split the URL once.
181
            if self._requires_launchpad_login(scheme, netloc, path, query,
182
                                              fragment):
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
183
                # Only accept launchpad.net bzr+ssh URLs if we know
184
                # the user's Launchpad login:
3777.1.21 by Aaron Bentley
Stop including usernames in resolved lp: urls
185
                if _lp_login is not None:
186
                    break
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
187
                if _lp_login is None:
3270.4.1 by James Westby
Warn the user when resolving lp: URLs if they haven't set their login.
188
                    if not _warned_login:
3834.1.2 by Martin Pool
Better message when launchpad-login is needed
189
                        trace.warning(
3834.1.3 by Martin Pool
Shorter message about launchpad-login
190
'You have not informed bzr of your Launchpad ID, and you must do this to\n'
191
'write to Launchpad or access private data.  See "bzr help launchpad-login".')
3270.4.1 by James Westby
Warn the user when resolving lp: URLs if they haven't set their login.
192
                        _warned_login = True
2898.4.15 by James Henstridge
Use get_transport() to decide whether Bazaar supports a given URL.
193
            else:
194
                # Use the URL if we can create a transport for it.
195
                try:
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
196
                    transport.get_transport(url)
2898.4.15 by James Henstridge
Use get_transport() to decide whether Bazaar supports a given URL.
197
                except (errors.PathError, errors.TransportError):
198
                    pass
199
                else:
200
                    break
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
201
        else:
3251.4.3 by Aaron Bentley
More renames and cleanups
202
            raise errors.InvalidURL(path=url, extra='no supported schemes')
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
203
        return url
204
2245.8.3 by Martin Pool
Start adding indirection transport
205
206
def get_test_permutations():
207
    # Since this transport doesn't do anything once opened, it's not subjected
208
    # to the usual transport tests.
209
    return []