730
732
return osutils.pathjoin(*segments)
738
def __init__(self, scheme, quoted_user, quoted_password, quoted_host,
741
self.quoted_host = quoted_host
742
self.host = urllib.unquote(self.quoted_host)
743
self.quoted_user = quoted_user
744
if self.quoted_user is not None:
745
self.user = urllib.unquote(self.quoted_user)
748
self.quoted_password = quoted_password
749
if self.quoted_password is not None:
750
self.password = urllib.unquote(self.quoted_password)
754
self.quoted_path = quoted_path
755
self.path = urllib.unquote(self.quoted_path)
757
def __eq__(self, other):
758
return (isinstance(other, self.__class__) and
759
self.scheme == other.scheme and
760
self.host == other.host and
761
self.user == other.user and
762
self.password == other.password and
763
self.path == other.path)
766
return "<%s(%r, %r, %r, %r, %r, %r)>" % (
767
self.__class__.__name__,
768
self.scheme, self.quoted_user, self.quoted_password,
769
self.quoted_host, self.port, self.quoted_path)
772
def from_string(cls, url):
773
"""Create a URL object from a string.
775
:param url: URL as bytestring
777
if isinstance(url, unicode):
778
raise errors.InvalidURL('should be ascii:\n%r' % url)
779
url = url.encode('utf-8')
780
(scheme, netloc, path, params,
781
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
782
user = password = host = port = None
784
user, host = netloc.rsplit('@', 1)
786
user, password = user.split(':', 1)
790
if ':' in host and not (host[0] == '[' and host[-1] == ']'):
792
host, port = host.rsplit(':',1)
796
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
798
if host != "" and host[0] == '[' and host[-1] == ']': #IPv6
801
return cls(scheme, user, password, host, port, path)
804
netloc = self.quoted_host
806
netloc = "[%s]" % netloc
807
if self.quoted_user is not None:
808
# Note that we don't put the password back even if we
809
# have one so that it doesn't get accidentally
811
netloc = '%s@%s' % (self.quoted_user, netloc)
812
if self.port is not None:
813
netloc = '%s:%d' % (netloc, self.port)
814
return urlparse.urlunparse(
815
(self.scheme, netloc, self.quoted_path, None, None, None))
818
def _combine_paths(base_path, relpath):
819
"""Transform a Transport-relative path to a remote absolute path.
821
This does not handle substitution of ~ but does handle '..' and '.'
826
t._combine_paths('/home/sarah', 'project/foo')
827
=> '/home/sarah/project/foo'
828
t._combine_paths('/home/sarah', '../../etc')
830
t._combine_paths('/home/sarah', '/etc')
833
:param base_path: base path
834
:param relpath: relative url string for relative part of remote path.
835
:return: urlencoded string for final path.
837
if not isinstance(relpath, str):
838
raise errors.InvalidURL(relpath)
839
if relpath.startswith('/'):
842
base_parts = base_path.split('/')
843
if len(base_parts) > 0 and base_parts[-1] == '':
844
base_parts = base_parts[:-1]
845
for p in relpath.split('/'):
847
if len(base_parts) == 0:
848
# In most filesystems, a request for the parent
849
# of root, just returns root.
856
path = '/'.join(base_parts)
857
if not path.startswith('/'):
861
def clone(self, offset=None):
862
"""Return a new URL for a path relative to this URL.
864
:param offset: A relative path, already urlencoded
865
:return: `URL` instance
867
if offset is not None:
868
relative = unescape(offset).encode('utf-8')
869
path = self._combine_paths(self.path, relative)
870
path = urllib.quote(path)
872
path = self.quoted_path
873
return self.__class__(self.scheme, self.quoted_user,
874
self.quoted_password, self.quoted_host, self.port,
734
878
def parse_url(url):
735
879
"""Extract the server address, the credentials and the path from the url.
740
884
:param url: an quoted url
742
885
:return: (scheme, user, password, host, port, path) tuple, all fields
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)
888
parsed_url = URL.from_string(url)
889
return (parsed_url.scheme, parsed_url.user, parsed_url.password,
890
parsed_url.host, parsed_url.port, parsed_url.path)