730
730
return osutils.pathjoin(*segments)
736
def __init__(self, scheme, quoted_user, quoted_password, quoted_host,
739
self.quoted_host = quoted_host
740
self.host = urllib.unquote(self.quoted_host)
741
self.quoted_user = quoted_user
742
if self.quoted_user is not None:
743
self.user = urllib.unquote(self.quoted_user)
746
self.quoted_password = quoted_password
747
if self.quoted_password is not None:
748
self.password = urllib.unquote(self.quoted_password)
752
self.quoted_path = quoted_path
753
self.path = urllib.unquote(self.quoted_path)
755
def __eq__(self, other):
756
return (isinstance(other, self.__class__) and
757
self.scheme == other.scheme and
758
self.host == other.host and
759
self.user == other.user and
760
self.password == other.password and
761
self.path == other.path)
764
return "<%s(%r, %r, %r, %r, %r, %r)>" % (
765
self.__class__.__name__,
766
self.scheme, self.quoted_user, self.quoted_password,
767
self.quoted_host, self.port, self.quoted_path)
770
def from_string(cls, url):
771
"""Create a URL object from a string.
773
:param url: URL as bytestring
775
if isinstance(url, unicode):
776
raise errors.InvalidURL('should be ascii:\n%r' % url)
777
url = url.encode('utf-8')
778
(scheme, netloc, path, params,
779
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
780
user = password = host = port = None
782
user, host = netloc.rsplit('@', 1)
784
user, password = user.split(':', 1)
788
if ':' in host and not (host[0] == '[' and host[-1] == ']'):
790
host, port = host.rsplit(':',1)
794
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
796
if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
799
return cls(scheme, user, password, host, port, path)
802
netloc = self.quoted_host
804
netloc = "[%s]" % netloc
805
if self.quoted_user is not None:
806
# Note that we don't put the password back even if we
807
# have one so that it doesn't get accidentally
809
netloc = '%s@%s' % (self.quoted_user, netloc)
810
if self.port is not None:
811
netloc = '%s:%d' % (netloc, self.port)
812
return urlparse.urlunparse(
813
(self.scheme, netloc, self.quoted_path, None, None, None))
816
def _combine_paths(base_path, relpath):
817
"""Transform a Transport-relative path to a remote absolute path.
819
This does not handle substitution of ~ but does handle '..' and '.'
824
t._combine_paths('/home/sarah', 'project/foo')
825
=> '/home/sarah/project/foo'
826
t._combine_paths('/home/sarah', '../../etc')
828
t._combine_paths('/home/sarah', '/etc')
831
:param base_path: base path
832
:param relpath: relative url string for relative part of remote path.
833
:return: urlencoded string for final path.
835
if not isinstance(relpath, str):
836
raise errors.InvalidURL(relpath)
837
if relpath.startswith('/'):
840
base_parts = base_path.split('/')
841
if len(base_parts) > 0 and base_parts[-1] == '':
842
base_parts = base_parts[:-1]
843
for p in relpath.split('/'):
845
if len(base_parts) == 0:
846
# In most filesystems, a request for the parent
847
# of root, just returns root.
854
path = '/'.join(base_parts)
855
if not path.startswith('/'):
859
def clone(self, offset=None):
860
"""Return a new URL for a path relative to this URL.
862
:param offset: A relative path, already urlencoded
863
:return: `URL` instance
865
if offset is not None:
866
relative = unescape(offset).encode('utf-8')
867
path = self._combine_paths(self.path, relative)
868
path = urllib.quote(path)
870
path = self.quoted_path
871
return self.__class__(self.scheme, self.quoted_user,
872
self.quoted_password, self.quoted_host, self.port,
876
734
def parse_url(url):
877
735
"""Extract the server address, the credentials and the path from the url.
882
740
:param url: an quoted url
883
742
:return: (scheme, user, password, host, port, path) tuple, all fields
886
parsed_url = URL.from_string(url)
887
return (parsed_url.scheme, parsed_url.user, parsed_url.password,
888
parsed_url.host, parsed_url.port, parsed_url.path)
745
if isinstance(url, unicode):
746
raise errors.InvalidURL('should be ascii:\n%r' % url)
747
url = url.encode('utf-8')
748
(scheme, netloc, path, params,
749
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
750
user = password = host = port = None
752
user, host = netloc.rsplit('@', 1)
754
user, password = user.split(':', 1)
755
password = urllib.unquote(password)
756
user = urllib.unquote(user)
760
if ':' in host and not (host[0] == '[' and host[-1] == ']'): #there *is* port
761
host, port = host.rsplit(':',1)
765
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
767
if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
770
host = urllib.unquote(host)
771
path = urllib.unquote(path)
773
return (scheme, user, password, host, port, path)