731
730
return osutils.pathjoin(*segments)
737
def __init__(self, scheme, quoted_user, quoted_password, quoted_host,
740
self.quoted_host = quoted_host
741
self.host = urllib.unquote(self.quoted_host)
742
self.quoted_user = quoted_user
743
if self.quoted_user is not None:
744
self.user = urllib.unquote(self.quoted_user)
747
self.quoted_password = quoted_password
748
if self.quoted_password is not None:
749
self.password = urllib.unquote(self.quoted_password)
753
self.quoted_path = quoted_path
754
self.path = urllib.unquote(self.quoted_path)
756
def __eq__(self, other):
757
return (isinstance(other, self.__class__) and
758
self.scheme == other.scheme and
759
self.host == other.host and
760
self.user == other.user and
761
self.password == other.password and
762
self.path == other.path)
765
return "<%s(%r, %r, %r, %r, %r, %r)>" % (
766
self.__class__.__name__,
767
self.scheme, self.quoted_user, self.quoted_password,
768
self.quoted_host, self.port, self.quoted_path)
771
def from_string(cls, url):
772
"""Create a URL object from a string.
774
:param url: URL as bytestring
776
if isinstance(url, unicode):
777
raise errors.InvalidURL('should be ascii:\n%r' % url)
778
url = url.encode('utf-8')
779
(scheme, netloc, path, params,
780
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
781
user = password = host = port = None
783
user, host = netloc.rsplit('@', 1)
785
user, password = user.split(':', 1)
789
if ':' in host and not (host[0] == '[' and host[-1] == ']'):
791
host, port = host.rsplit(':',1)
795
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
797
if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
800
return cls(scheme, user, password, host, port, path)
803
netloc = self.quoted_host
805
netloc = "[%s]" % netloc
806
if self.quoted_user is not None:
807
# Note that we don't put the password back even if we
808
# have one so that it doesn't get accidentally
810
netloc = '%s@%s' % (self.quoted_user, netloc)
811
if self.port is not None:
812
netloc = '%s:%d' % (netloc, self.port)
813
return urlparse.urlunparse(
814
(self.scheme, netloc, self.quoted_path, None, None, None))
817
def _combine_paths(base_path, relpath):
818
"""Transform a Transport-relative path to a remote absolute path.
820
This does not handle substitution of ~ but does handle '..' and '.'
825
t._combine_paths('/home/sarah', 'project/foo')
826
=> '/home/sarah/project/foo'
827
t._combine_paths('/home/sarah', '../../etc')
829
t._combine_paths('/home/sarah', '/etc')
832
:param base_path: base path
833
:param relpath: relative url string for relative part of remote path.
834
:return: urlencoded string for final path.
836
if not isinstance(relpath, str):
837
raise errors.InvalidURL(relpath)
838
if relpath.startswith('/'):
841
base_parts = base_path.split('/')
842
if len(base_parts) > 0 and base_parts[-1] == '':
843
base_parts = base_parts[:-1]
844
for p in relpath.split('/'):
846
if len(base_parts) == 0:
847
# In most filesystems, a request for the parent
848
# of root, just returns root.
855
path = '/'.join(base_parts)
856
if not path.startswith('/'):
860
def clone(self, offset=None):
861
"""Return a new URL for a path relative to this URL.
863
:param offset: A relative path, already urlencoded
864
:return: `URL` instance
866
if offset is not None:
867
relative = unescape(offset).encode('utf-8')
868
path = self._combine_paths(self.path, relative)
869
path = urllib.quote(path)
871
path = self.quoted_path
872
return self.__class__(self.scheme, self.quoted_user,
873
self.quoted_password, self.quoted_host, self.port,
877
734
def parse_url(url):
878
735
"""Extract the server address, the credentials and the path from the url.
883
740
:param url: an quoted url
884
742
:return: (scheme, user, password, host, port, path) tuple, all fields
887
parsed_url = URL.from_string(url)
888
return (parsed_url.scheme, parsed_url.user, parsed_url.password,
889
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)