1 # Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
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
11 # GNU General Public License for more details.
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
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 from bzrlib.errors import NoSuchFile, NotBranchError, TransportNotPossible
18 from bzrlib.trace import mutter
19 from bzrlib.transport import Transport
20 import bzrlib.urlutils as urlutils
22 from cStringIO import StringIO
24 from tempfile import mktemp
26 from svn.core import SubversionException, Pool
30 # Some older versions of the Python bindings need to be
31 # explicitly initialized
34 svn_config = svn.core.svn_config_get_config(None)
37 def _create_auth_baton(pool):
38 """Create a Subversion authentication baton. """
40 # Give the client context baton a suite of authentication
43 svn.client.get_simple_provider(pool),
44 svn.client.get_username_provider(pool),
45 svn.client.get_ssl_client_cert_file_provider(pool),
46 svn.client.get_ssl_client_cert_pw_file_provider(pool),
47 svn.client.get_ssl_server_trust_file_provider(pool),
49 return svn.core.svn_auth_open(providers, pool)
52 # Don't run any tests on SvnTransport as it is not intended to be
53 # a full implementation of Transport
54 def get_test_permutations():
58 def svn_to_bzr_url(url):
59 """Convert a Subversion URL to a URL understood by Bazaar.
61 This will optionally prefix the URL with "svn+".
63 if not (url.startswith("svn+") or url.startswith("svn:")):
68 def bzr_to_svn_url(url):
69 """Convert a Bazaar URL to a URL understood by Subversion.
71 This will possibly remove the svn+ prefix.
73 if (url.startswith("svn+http://") or
74 url.startswith("svn+file://") or
75 url.startswith("svn+https://")):
76 url = url[len("svn+"):] # Skip svn+
78 # The SVN libraries don't like trailing slashes...
79 return url.rstrip('/')
82 class SvnRaCallbacks(svn.ra.callbacks2_t):
83 """Remote access callbacks implementation for bzr-svn."""
84 def __init__(self, pool):
85 svn.ra.callbacks2_t.__init__(self)
86 self.auth_baton = _create_auth_baton(pool)
89 def open_tmp_file(self, pool):
90 return mktemp(prefix='bzr-svn')
92 class SvnRaTransport(Transport):
93 """Fake transport for Subversion-related namespaces.
95 This implements just as much of Transport as is necessary
96 to fool Bazaar-NG. """
97 def __init__(self, url="", ra=None):
100 self.svn_url = bzr_to_svn_url(url)
101 Transport.__init__(self, bzr_url)
103 # Only Subversion 1.4 has reparent()
104 if ra is None or not hasattr(svn.ra, 'reparent'):
105 self.callbacks = SvnRaCallbacks(self.pool)
107 mutter('opening SVN RA connection to %r' % self.svn_url)
108 self.ra = svn.ra.open2(self.svn_url.encode('utf8'), self.callbacks, svn_config, None)
109 except SubversionException, (_, num):
110 if num == svn.core.SVN_ERR_RA_ILLEGAL_URL:
111 raise NotBranchError(path=url)
112 if num == svn.core.SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED:
113 raise NotBranchError(path=url)
114 if num == svn.core.SVN_ERR_BAD_URL:
115 raise NotBranchError(path=url)
120 mutter('svn reparent %r' % self.svn_url)
121 svn.ra.reparent(self.ra, self.svn_url.encode('utf8'))
123 def has(self, relpath):
124 """See Transport.has()."""
125 # TODO: Raise TransportNotPossible here instead and
126 # catch it in bzrdir.py
129 def get(self, relpath):
130 """See Transport.get()."""
131 # TODO: Raise TransportNotPossible here instead and
132 # catch it in bzrdir.py
133 raise NoSuchFile(path=relpath)
135 def stat(self, relpath):
136 """See Transport.stat()."""
137 raise TransportNotPossible('stat not supported on Subversion')
140 """Open a connection to the root of this repository.
142 :return: A new instance of SvnRaTransport connected to the root.
144 return SvnRaTransport(svn_to_bzr_url(svn.ra.get_repos_root(self.ra)), ra=self.ra)
147 """See Transport.listable().
153 # There is no real way to do locking directly on the transport
154 # nor is there a need to.
159 def lock_write(self, relpath):
160 """See Transport.lock_write()."""
161 return self.PhonyLock()
163 def lock_read(self, relpath):
164 """See Transport.lock_read()."""
165 return self.PhonyLock()
167 def clone(self, offset=None):
168 """See Transport.clone()."""
170 return self.__class__(self.base)
172 return SvnRaTransport(urlutils.join(self.base, offset), ra=self.ra)