Explorar o código

ipv6 address support

also: Location: more informative exception when parsing fails
Benedikt Neuffer %!s(int64=8) %!d(string=hai) anos
pai
achega
97c770e318
Modificáronse 2 ficheiros con 47 adicións e 3 borrados
  1. 7 3
      borg/helpers.py
  2. 40 0
      borg/testsuite/helpers.py

+ 7 - 3
borg/helpers.py

@@ -929,7 +929,7 @@ class Location:
     ssh_re = re.compile(r"""
     ssh_re = re.compile(r"""
         (?P<proto>ssh)://                                   # ssh://
         (?P<proto>ssh)://                                   # ssh://
         """ + optional_user_re + r"""                       # user@  (optional)
         """ + optional_user_re + r"""                       # user@  (optional)
-        (?P<host>[^:/]+)(?::(?P<port>\d+))?                 # host or host:port
+        (?P<host>([^:/]+|\[[0-9a-fA-F:.]+\]))(?::(?P<port>\d+))?  # host or host:port or [ipv6] or [ipv6]:port
         """ + abs_path_re + optional_archive_re, re.VERBOSE)  # path or path::archive
         """ + abs_path_re + optional_archive_re, re.VERBOSE)  # path or path::archive
 
 
     file_re = re.compile(r"""
     file_re = re.compile(r"""
@@ -940,7 +940,7 @@ class Location:
     scp_re = re.compile(r"""
     scp_re = re.compile(r"""
         (
         (
             """ + optional_user_re + r"""                   # user@  (optional)
             """ + optional_user_re + r"""                   # user@  (optional)
-            (?P<host>[^:/]+):                               # host: (don't match / in host to disambiguate from file:)
+            (?P<host>([^:/]+|\[[0-9a-fA-F:.]+\])):          # host: (don't match / or [ipv6] in host to disambiguate from file:)
         )?                                                  # user@host: part is optional
         )?                                                  # user@host: part is optional
         """ + scp_path_re + optional_archive_re, re.VERBOSE)  # path with optional archive
         """ + scp_path_re + optional_archive_re, re.VERBOSE)  # path with optional archive
 
 
@@ -956,7 +956,7 @@ class Location:
     def __init__(self, text=''):
     def __init__(self, text=''):
         self.orig = text
         self.orig = text
         if not self.parse(self.orig):
         if not self.parse(self.orig):
-            raise ValueError
+            raise ValueError('Location: parse failed: %s' % self.orig)
 
 
     def parse(self, text):
     def parse(self, text):
         text = replace_placeholders(text)
         text = replace_placeholders(text)
@@ -987,6 +987,8 @@ class Location:
             self.proto = m.group('proto')
             self.proto = m.group('proto')
             self.user = m.group('user')
             self.user = m.group('user')
             self.host = m.group('host')
             self.host = m.group('host')
+            if self.host is not None:
+                self.host = self.host.lstrip('[').rstrip(']')
             self.port = m.group('port') and int(m.group('port')) or None
             self.port = m.group('port') and int(m.group('port')) or None
             self.path = normpath_special(m.group('path'))
             self.path = normpath_special(m.group('path'))
             self.archive = m.group('archive')
             self.archive = m.group('archive')
@@ -1001,6 +1003,8 @@ class Location:
         if m:
         if m:
             self.user = m.group('user')
             self.user = m.group('user')
             self.host = m.group('host')
             self.host = m.group('host')
+            if isinstance(self.host, str):
+                self.host = self.host.lstrip('[').rstrip(']')
             self.path = normpath_special(m.group('path'))
             self.path = normpath_special(m.group('path'))
             self.archive = m.group('archive')
             self.archive = m.group('archive')
             self.proto = self.host and 'ssh' or 'file'
             self.proto = self.host and 'ssh' or 'file'

+ 40 - 0
borg/testsuite/helpers.py

@@ -42,6 +42,30 @@ class TestLocationWithoutEnv:
             "Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
             "Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
         assert repr(Location('ssh://user@host/some/path')) == \
         assert repr(Location('ssh://user@host/some/path')) == \
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[::]:1234/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive='archive')"
+        assert repr(Location('ssh://user@[::]:1234/some/path')) == \
+            "Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[::]/some/path')) == \
+            "Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::]:1234/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive='archive')"
+        assert repr(Location('ssh://user@[2001:db8::]:1234/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::]/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive='archive')"
+        assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::c0:ffee]/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive='archive')"
+        assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive=None)"
+        assert repr(Location('ssh://user@[2001:db8::192.0.2.1]/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
 
 
     def test_file(self, monkeypatch):
     def test_file(self, monkeypatch):
         monkeypatch.delenv('BORG_REPO', raising=False)
         monkeypatch.delenv('BORG_REPO', raising=False)
@@ -56,6 +80,22 @@ class TestLocationWithoutEnv:
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')"
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')"
         assert repr(Location('user@host:/some/path')) == \
         assert repr(Location('user@host:/some/path')) == \
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
             "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
+        assert repr(Location('user@[::]:/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive='archive')"
+        assert repr(Location('user@[::]:/some/path')) == \
+            "Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
+        assert repr(Location('user@[2001:db8::]:/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive='archive')"
+        assert repr(Location('user@[2001:db8::]:/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
+        assert repr(Location('user@[2001:db8::c0:ffee]:/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive='archive')"
+        assert repr(Location('user@[2001:db8::c0:ffee]:/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
+        assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path::archive')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive='archive')"
+        assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path')) == \
+            "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
 
 
     def test_smb(self, monkeypatch):
     def test_smb(self, monkeypatch):
         monkeypatch.delenv('BORG_REPO', raising=False)
         monkeypatch.delenv('BORG_REPO', raising=False)