##// END OF EJS Templates
svn: fixed use of hgsubversion in the code
super-admin -
r1046:05a103e8 python3
parent child Browse files
Show More
@@ -0,0 +1,160 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18 import os
19 import tempfile
20
21 from svn import client
22 from svn import core
23 from svn import ra
24
25 from mercurial import error
26
27 from vcsserver.utils import safe_bytes
28
29 core.svn_config_ensure(None)
30 svn_config = core.svn_config_get_config(None)
31
32
33 class RaCallbacks(ra.Callbacks):
34 @staticmethod
35 def open_tmp_file(pool): # pragma: no cover
36 (fd, fn) = tempfile.mkstemp()
37 os.close(fd)
38 return fn
39
40 @staticmethod
41 def get_client_string(pool):
42 return b'RhodeCode-subversion-url-checker'
43
44
45 class SubversionException(Exception):
46 pass
47
48
49 class SubversionConnectionException(SubversionException):
50 """Exception raised when a generic error occurs when connecting to a repository."""
51
52
53 def normalize_url(url):
54 if not url:
55 return url
56 if url.startswith(b'svn+http://') or url.startswith(b'svn+https://'):
57 url = url[4:]
58 url = url.rstrip(b'/')
59 return url
60
61
62 def _create_auth_baton(pool):
63 """Create a Subversion authentication baton. """
64 # Give the client context baton a suite of authentication
65 # providers.h
66 platform_specific = [
67 'svn_auth_get_gnome_keyring_simple_provider',
68 'svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider',
69 'svn_auth_get_keychain_simple_provider',
70 'svn_auth_get_keychain_ssl_client_cert_pw_provider',
71 'svn_auth_get_kwallet_simple_provider',
72 'svn_auth_get_kwallet_ssl_client_cert_pw_provider',
73 'svn_auth_get_ssl_client_cert_file_provider',
74 'svn_auth_get_windows_simple_provider',
75 'svn_auth_get_windows_ssl_server_trust_provider',
76 ]
77
78 providers = []
79
80 for p in platform_specific:
81 if getattr(core, p, None) is not None:
82 try:
83 providers.append(getattr(core, p)())
84 except RuntimeError:
85 pass
86
87 providers += [
88 client.get_simple_provider(),
89 client.get_username_provider(),
90 client.get_ssl_client_cert_file_provider(),
91 client.get_ssl_client_cert_pw_file_provider(),
92 client.get_ssl_server_trust_file_provider(),
93 ]
94
95 return core.svn_auth_open(providers, pool)
96
97
98 class SubversionRepo(object):
99 """Wrapper for a Subversion repository.
100
101 It uses the SWIG Python bindings, see above for requirements.
102 """
103 def __init__(self, svn_url: bytes = b'', username: bytes = b'', password: bytes = b''):
104
105 self.username = username
106 self.password = password
107 self.svn_url = core.svn_path_canonicalize(svn_url)
108
109 self.auth_baton_pool = core.Pool()
110 self.auth_baton = _create_auth_baton(self.auth_baton_pool)
111 # self.init_ra_and_client() assumes that a pool already exists
112 self.pool = core.Pool()
113
114 self.ra = self.init_ra_and_client()
115 self.uuid = ra.get_uuid(self.ra, self.pool)
116
117 def init_ra_and_client(self):
118 """Initializes the RA and client layers, because sometimes getting
119 unified diffs runs the remote server out of open files.
120 """
121
122 if self.username:
123 core.svn_auth_set_parameter(self.auth_baton,
124 core.SVN_AUTH_PARAM_DEFAULT_USERNAME,
125 self.username)
126 if self.password:
127 core.svn_auth_set_parameter(self.auth_baton,
128 core.SVN_AUTH_PARAM_DEFAULT_PASSWORD,
129 self.password)
130
131 callbacks = RaCallbacks()
132 callbacks.auth_baton = self.auth_baton
133
134 try:
135 return ra.open2(self.svn_url, callbacks, svn_config, self.pool)
136 except SubversionException as e:
137 # e.child contains a detailed error messages
138 msglist = []
139 svn_exc = e
140 while svn_exc:
141 if svn_exc.args[0]:
142 msglist.append(svn_exc.args[0])
143 svn_exc = svn_exc.child
144 msg = '\n'.join(msglist)
145 raise SubversionConnectionException(msg)
146
147
148 class svnremoterepo(object):
149 """ the dumb wrapper for actual Subversion repositories """
150
151 def __init__(self, username: bytes = b'', password: bytes = b'', svn_url: bytes = b''):
152 self.username = username or b''
153 self.password = password or b''
154 self.path = normalize_url(svn_url)
155
156 def svn(self):
157 try:
158 return SubversionRepo(self.path, self.username, self.password)
159 except SubversionConnectionException as e:
160 raise error.Abort(safe_bytes(e))
@@ -7,7 +7,6 b' dogpile.cache==1.1.8'
7
7
8 decorator==5.1.1
8 decorator==5.1.1
9 dulwich==0.21.3
9 dulwich==0.21.3
10 hgsubversion==1.9.3
11 hg-evolve==11.0.0
10 hg-evolve==11.0.0
12
11
13 mercurial==6.3.2
12 mercurial==6.3.2
@@ -23,7 +23,9 b' import urllib.parse'
23 import logging
23 import logging
24 import posixpath as vcspath
24 import posixpath as vcspath
25 import io
25 import io
26 import urllib.request, urllib.parse, urllib.error
26 import urllib.request
27 import urllib.parse
28 import urllib.error
27 import traceback
29 import traceback
28
30
29 import svn.client
31 import svn.client
@@ -38,7 +40,7 b' from vcsserver.base import RepoFactory, '
38 from vcsserver.exceptions import NoContentException
40 from vcsserver.exceptions import NoContentException
39 from vcsserver.utils import safe_str
41 from vcsserver.utils import safe_str
40 from vcsserver.vcs_base import RemoteBase
42 from vcsserver.vcs_base import RemoteBase
41
43 from vcsserver.lib.svnremoterepo import svnremoterepo
42 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
43
45
44
46
@@ -50,7 +52,7 b' svn_compatible_versions_map = {'
50 'pre-1.9-compatible': '1.8',
52 'pre-1.9-compatible': '1.8',
51 }
53 }
52
54
53 current_compatible_version = '1.12'
55 current_compatible_version = '1.14'
54
56
55
57
56 def reraise_safe_exceptions(func):
58 def reraise_safe_exceptions(func):
@@ -105,9 +107,6 b' class SvnRemote(RemoteBase):'
105
107
106 def __init__(self, factory, hg_factory=None):
108 def __init__(self, factory, hg_factory=None):
107 self._factory = factory
109 self._factory = factory
108 # TODO: Remove once we do not use internal Mercurial objects anymore
109 # for subversion
110 self._hg_factory = hg_factory
111
110
112 @reraise_safe_exceptions
111 @reraise_safe_exceptions
113 def discover_svn_version(self):
112 def discover_svn_version(self):
@@ -127,15 +126,13 b' class SvnRemote(RemoteBase):'
127 log.exception("failed to read object_store")
126 log.exception("failed to read object_store")
128 return False
127 return False
129
128
130 def check_url(self, url, config_items):
129 def check_url(self, url):
131 # this can throw exception if not installed, but we detect this
132 from hgsubversion import svnrepo
133
130
134 baseui = self._hg_factory._create_config(config_items)
135 # uuid function get's only valid UUID from proper repo, else
131 # uuid function get's only valid UUID from proper repo, else
136 # throws exception
132 # throws exception
133 username, password, src_url = self.get_url_and_credentials(url)
137 try:
134 try:
138 svnrepo.svnremoterepo(baseui, url).svn.uuid
135 svnremoterepo(username, password, src_url).svn().uuid
139 except Exception:
136 except Exception:
140 tb = traceback.format_exc()
137 tb = traceback.format_exc()
141 log.debug("Invalid Subversion url: `%s`, tb: %s", url, tb)
138 log.debug("Invalid Subversion url: `%s`, tb: %s", url, tb)
@@ -359,6 +356,7 b' class SvnRemote(RemoteBase):'
359
356
360 cache_on, context_uid, repo_id = self._cache_on(wire)
357 cache_on, context_uid, repo_id = self._cache_on(wire)
361 region = self._region(wire)
358 region = self._region(wire)
359
362 @region.conditional_cache_on_arguments(condition=cache_on)
360 @region.conditional_cache_on_arguments(condition=cache_on)
363 def _get_file_size(_repo_id, _path, _revision):
361 def _get_file_size(_repo_id, _path, _revision):
364 repo = self._factory.repo(wire)
362 repo = self._factory.repo(wire)
@@ -37,60 +37,57 b' def safe_int(val, default=None):'
37 return val
37 return val
38
38
39
39
40 def safe_str(unicode_, to_encoding=None):
40 def safe_str(str_, to_encoding=None) -> str:
41 """
41 """
42 safe str function. Does few trick to turn unicode_ into string
42 safe str function. Does few trick to turn unicode_ into string
43
43
44 :param unicode_: unicode to encode
44 :param str_: str to encode
45 :param to_encoding: encode to this type UTF8 default
45 :param to_encoding: encode to this type UTF8 default
46 :rtype: str
46 :rtype: str
47 :returns: str object
47 :returns: str object
48 """
48 """
49 if isinstance(str_, str):
50 return str_
51
52 # if it's bytes cast to str
53 if not isinstance(str_, bytes):
54 return str(str_)
55
49 to_encoding = to_encoding or ['utf8']
56 to_encoding = to_encoding or ['utf8']
50 # if it's not basestr cast to str
51 if not isinstance(unicode_, basestring):
52 return str(unicode_)
53
54 if isinstance(unicode_, str):
55 return unicode_
56
57 if not isinstance(to_encoding, (list, tuple)):
57 if not isinstance(to_encoding, (list, tuple)):
58 to_encoding = [to_encoding]
58 to_encoding = [to_encoding]
59
59
60 for enc in to_encoding:
60 for enc in to_encoding:
61 try:
61 try:
62 return unicode_.encode(enc)
62 return str(str_, enc)
63 except UnicodeEncodeError:
63 except UnicodeDecodeError:
64 pass
64 pass
65
65
66 return unicode_.encode(to_encoding[0], 'replace')
66 return str(str_, to_encoding[0], 'replace')
67
67
68
68
69 def safe_unicode(str_, from_encoding=None):
69 def safe_bytes(str_, from_encoding=None) -> bytes:
70 """
70 """
71 safe unicode function. Does few trick to turn str_ into unicode
71 safe bytes function. Does few trick to turn str_ into bytes string:
72
72
73 :param str_: string to decode
73 :param str_: string to decode
74 :param from_encoding: encode from this type UTF8 default
74 :param from_encoding: encode from this type UTF8 default
75 :rtype: unicode
75 :rtype: unicode
76 :returns: unicode object
76 :returns: unicode object
77 """
77 """
78 from_encoding = from_encoding or ['utf8']
78 if isinstance(str_, bytes):
79
80 if isinstance(str_, unicode):
81 return str_
79 return str_
82
80
81 if not isinstance(str_, str):
82 raise ValueError('safe_bytes cannot convert other types than str: got: {}'.format(type(str_)))
83
84 from_encoding = from_encoding or ['utf8']
83 if not isinstance(from_encoding, (list, tuple)):
85 if not isinstance(from_encoding, (list, tuple)):
84 from_encoding = [from_encoding]
86 from_encoding = [from_encoding]
85
87
86 try:
87 return unicode(str_)
88 except UnicodeDecodeError:
89 pass
90
91 for enc in from_encoding:
88 for enc in from_encoding:
92 try:
89 try:
93 return unicode(str_, enc)
90 return str_.encode(enc)
94 except UnicodeDecodeError:
91 except UnicodeDecodeError:
95 pass
92 pass
96
93
General Comments 0
You need to be logged in to leave comments. Login now