1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008, 2010 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Transport is an abstraction layer to handle file access.
91
90
modules.add(factory._module_name)
93
92
modules.add(factory._obj.__module__)
94
# Add chroot directly, because there is no handler registered for it.
93
# Add chroot and pathfilter directly, because there is no handler
95
95
modules.add('bzrlib.transport.chroot')
96
modules.add('bzrlib.transport.pathfilter')
96
97
result = list(modules)
107
108
register_transport_provider( ) ( and the "lazy" variant )
109
110
This is needed because:
110
a) a single provider can support multple protcol ( like the ftp
111
a) a single provider can support multiple protocols ( like the ftp
111
112
provider which supports both the ftp:// and the aftp:// protocols )
112
113
b) a single protocol can have multiple providers ( like the http://
113
114
protocol which is supported by both the urllib and pycurl provider )
273
274
from/to a storage location.
275
276
Most functions have a _multi variant, which allows you to queue up
276
multiple requests. They generally have a dumb base implementation
277
multiple requests. They generally have a dumb base implementation
277
278
which just iterates over the arguments, but smart Transport
278
279
implementations can do pipelining.
279
280
In general implementations should support having a generator or a list
325
326
def clone(self, offset=None):
326
327
"""Return a new Transport object, cloned from the current location,
327
using a subdirectory or parent directory. This allows connections
328
using a subdirectory or parent directory. This allows connections
328
329
to be pooled, rather than a new one needed for each subdir.
330
331
raise NotImplementedError(self.clone)
333
def create_prefix(self):
334
"""Create all the directories leading down to self.base."""
336
needed = [cur_transport]
337
# Recurse upwards until we can create a directory successfully
339
new_transport = cur_transport.clone('..')
340
if new_transport.base == cur_transport.base:
341
raise errors.BzrCommandError(
342
"Failed to create path prefix for %s."
343
% cur_transport.base)
345
new_transport.mkdir('.')
346
except errors.NoSuchFile:
347
needed.append(new_transport)
348
cur_transport = new_transport
349
except errors.FileExists:
353
# Now we only need to create child directories
355
cur_transport = needed.pop()
356
cur_transport.ensure_base()
332
358
def ensure_base(self):
333
359
"""Ensure that the directory this transport references exists.
368
394
raise NotImplementedError(self.external_url)
370
396
def _pump(self, from_file, to_file):
371
"""Most children will need to copy from one file-like
397
"""Most children will need to copy from one file-like
372
398
object or string to another one.
373
399
This just gives them something easy to call.
452
478
t._combine_paths('/home/sarah', '/etc')
455
:param base_path: urlencoded path for the transport root; typically a
481
:param base_path: urlencoded path for the transport root; typically a
456
482
URL but need not contain scheme/host/etc.
457
483
:param relpath: relative url string for relative part of remote path.
458
484
:return: urlencoded string for final path.
485
511
"""Return the recommended page size for this transport.
487
513
This is potentially different for every path in a given namespace.
488
For example, local transports might use an operating system call to
514
For example, local transports might use an operating system call to
489
515
get the block size for a given path, which can vary due to mount
497
523
"""Return the local path portion from a given absolute path.
499
525
This default implementation is not suitable for filesystems with
500
aliasing, such as that given by symlinks, where a path may not
501
start with our base, but still be a relpath once aliasing is
526
aliasing, such as that given by symlinks, where a path may not
527
start with our base, but still be a relpath once aliasing is
504
530
# TODO: This might want to use bzrlib.osutils.relpath
517
543
raise errors.NotLocalUrl(self.abspath(relpath))
520
545
def has(self, relpath):
521
546
"""Does the file relpath exist?
523
548
Note that some transports MAY allow querying on directories, but this
524
is not part of the protocol. In other words, the results of
549
is not part of the protocol. In other words, the results of
525
550
t.has("a_directory_name") are undefined.
590
@deprecated_method(one_four)
591
def get_smart_client(self):
592
"""Return a smart client for this transport if possible.
594
A smart client doesn't imply the presence of a smart server: it implies
595
that the smart protocol can be tunnelled via this transport.
597
:raises NoSmartServer: if no smart server client is available.
599
raise errors.NoSmartServer(self.base)
601
615
def get_smart_medium(self):
602
616
"""Return a smart client medium for this transport if possible.
609
623
raise errors.NoSmartMedium(self)
611
@deprecated_method(one_four)
612
def get_shared_medium(self):
613
"""Return a smart client shared medium for this transport if possible.
615
A smart medium doesn't imply the presence of a smart server: it implies
616
that the smart protocol can be tunnelled via this transport.
618
:raises NoSmartMedium: if no smart server medium is available.
620
raise errors.NoSmartMedium(self)
622
625
def readv(self, relpath, offsets, adjust_for_latency=False,
623
626
upper_limit=None):
624
627
"""Get parts of the file at the given relative path.
845
848
"""Get a list of file-like objects, one for each entry in relpaths.
847
850
:param relpaths: A list of relative paths.
848
:param pb: An optional ProgressBar for indicating percent done.
851
:param pb: An optional ProgressTask for indicating percent done.
849
852
:return: A list or generator of file-like objects
851
854
# TODO: Consider having this actually buffer the requests,
965
968
be synchronised with any internal buffering that may be present.
967
970
:param relpath: The relative path to the file.
968
:param mode: The mode for the newly created file,
971
:param mode: The mode for the newly created file,
969
972
None means just use the default
970
973
:return: A FileStream. FileStream objects have two methods, write() and
971
974
close(). There is no guarantee that data is committed to the file
1014
1017
the supplied location.
1016
1019
:param files: A set of (path, f) entries
1017
:param pb: An optional ProgressBar for indicating percent done.
1020
:param pb: An optional ProgressTask for indicating percent done.
1019
1022
return self._iterate_over(files, self.append_file, pb, 'append', expand=True)
1021
1024
def copy(self, rel_from, rel_to):
1022
1025
"""Copy the item at rel_from to the location at rel_to.
1024
Override this for efficiency if a specific transport can do it
1027
Override this for efficiency if a specific transport can do it
1025
1028
faster than this default implementation.
1027
1030
self.put_file(rel_to, self.get(rel_from))
1029
1032
def copy_multi(self, relpaths, pb=None):
1030
1033
"""Copy a bunch of entries.
1032
1035
:param relpaths: A list of tuples of the form [(from, to), (from, to),...]
1034
1037
# This is the non-pipelined implementation, so that
1052
1055
def copy_tree(self, from_relpath, to_relpath):
1053
1056
"""Copy a subtree from one relpath to another.
1055
If a faster implementation is available, specific transports should
1058
If a faster implementation is available, specific transports should
1058
1061
source = self.clone(from_relpath)
1120
1123
def move_multi(self, relpaths, pb=None):
1121
1124
"""Move a bunch of entries.
1123
1126
:param relpaths: A list of tuples of the form [(from1, to1), (from2, to2),...]
1125
1128
return self._iterate_over(relpaths, self.move, pb, 'move', expand=True)
1178
1181
NOTE: This returns an object with fields such as 'st_size'. It MAY
1179
1182
or MAY NOT return the literal result of an os.stat() call, so all
1180
1183
access should be via named fields.
1181
ALSO NOTE: Stats of directories may not be supported on some
1184
ALSO NOTE: Stats of directories may not be supported on some
1184
1187
raise NotImplementedError(self.stat)
1248
1251
"""Return true if this transport can store and retrieve unix modebits.
1250
1253
(For example, 0700 to make a directory owner-private.)
1252
Note: most callers will not want to switch on this, but should rather
1255
Note: most callers will not want to switch on this, but should rather
1253
1256
just try and set permissions and let them be either stored or not.
1254
1257
This is intended mainly for the use of the test suite.
1256
Warning: this is not guaranteed to be accurate as sometimes we can't
1259
Warning: this is not guaranteed to be accurate as sometimes we can't
1257
1260
be sure: for example with vfat mounted on unix, or a windows sftp
1259
1262
# TODO: Perhaps return a e.g. TransportCharacteristics that can answer
1359
1362
def _split_url(url):
1361
Extract the server address, the credentials and the path from the url.
1363
user, password, host and path should be quoted if they contain reserved
1366
:param url: an quoted url
1368
:return: (scheme, user, password, host, port, path) tuple, all fields
1371
if isinstance(url, unicode):
1372
raise errors.InvalidURL('should be ascii:\n%r' % url)
1373
url = url.encode('utf-8')
1374
(scheme, netloc, path, params,
1375
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
1376
user = password = host = port = None
1378
user, host = netloc.rsplit('@', 1)
1380
user, password = user.split(':', 1)
1381
password = urllib.unquote(password)
1382
user = urllib.unquote(user)
1387
host, port = host.rsplit(':', 1)
1391
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
1394
raise errors.InvalidURL('Host empty in: %s' % url)
1396
host = urllib.unquote(host)
1397
path = urllib.unquote(path)
1399
return (scheme, user, password, host, port, path)
1363
return urlutils.parse_url(url)
1402
1366
def _unsplit_url(scheme, user, password, host, port, path):
1428
1392
netloc = '%s@%s' % (urllib.quote(user), netloc)
1429
1393
if port is not None:
1430
1394
netloc = '%s:%d' % (netloc, port)
1431
path = urllib.quote(path)
1395
path = urlutils.escape(path)
1432
1396
return urlparse.urlunparse((scheme, netloc, path, None, None, None))
1434
1398
def relpath(self, abspath):
1593
1557
def convert_path_to_url(base, error_str):
1594
1558
m = _urlRE.match(base)
1596
# This looks like a URL, but we weren't able to
1560
# This looks like a URL, but we weren't able to
1597
1561
# instantiate it as such raise an appropriate error
1598
1562
# FIXME: we have a 'error_str' unused and we use last_err below
1599
1563
raise errors.UnsupportedProtocol(base, last_err)
1665
1629
:param action: A callable, what the caller want to do while catching
1667
1631
:param transport: The initial transport used.
1668
:param redirected: A callable receiving the redirected transport and the
1632
:param redirected: A callable receiving the redirected transport and the
1669
1633
RedirectRequested exception.
1671
1635
:return: Whatever 'action' returns
1699
1663
class Server(object):
1700
1664
"""A Transport Server.
1702
1666
The Server interface provides a server for a given transport. We use
1703
1667
these servers as loopback testing tools. For any given transport the
1704
1668
Servers it provides must either allow writing, or serve the contents
1705
of os.getcwdu() at the time setUp is called.
1669
of os.getcwdu() at the time start_server is called.
1707
1671
Note that these are real servers - they must implement all the things
1708
1672
that we want bzr transports to take advantage of.
1675
def start_server(self):
1712
1676
"""Setup the server to service requests."""
1678
def stop_server(self):
1715
1679
"""Remove the server and cleanup any resources it owns."""
1717
1681
def get_url(self):
1718
1682
"""Return a url for this server.
1720
If the transport does not represent a disk directory (i.e. it is
1684
If the transport does not represent a disk directory (i.e. it is
1721
1685
a database like svn, or a memory only transport, it should return
1722
1686
a connection to a newly established resource for this Server.
1723
1687
Otherwise it should return a url that will provide access to the path
1724
that was os.getcwdu() when setUp() was called.
1688
that was os.getcwdu() when start_server() was called.
1726
1690
Subsequent calls will return the same resource.
1728
1692
raise NotImplementedError
1730
1694
def get_bogus_url(self):
1731
1695
"""Return a url for this protocol, that will fail to connect.
1733
1697
This may raise NotImplementedError to indicate that this server cannot
1734
1698
provide bogus urls.
1787
1751
register_transport_proto('aftp://', help="Access using active FTP.")
1788
1752
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1790
# Default to trying GSSAPI authentication (if the kerberos module is available)
1791
register_transport_proto('ftp+gssapi://', register_netloc=True)
1792
register_lazy_transport('ftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1793
'GSSAPIFtpTransport')
1794
register_transport_proto('aftp+gssapi://', register_netloc=True)
1795
register_lazy_transport('aftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1796
'GSSAPIFtpTransport')
1797
register_transport_proto('ftp+nogssapi://', register_netloc=True)
1798
register_transport_proto('aftp+nogssapi://', register_netloc=True)
1800
register_lazy_transport('ftp://', 'bzrlib.transport.ftp._gssapi',
1801
'GSSAPIFtpTransport')
1802
register_lazy_transport('aftp://', 'bzrlib.transport.ftp._gssapi',
1803
'GSSAPIFtpTransport')
1804
register_lazy_transport('ftp+nogssapi://', 'bzrlib.transport.ftp',
1806
register_lazy_transport('aftp+nogssapi://', 'bzrlib.transport.ftp',
1756
kerberos_available = True
1758
kerberos_available = False
1760
if kerberos_available:
1761
# Default to trying GSSAPI authentication (if the kerberos module is
1763
register_transport_proto('ftp+gssapi://', register_netloc=True)
1764
register_lazy_transport('ftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1765
'GSSAPIFtpTransport')
1766
register_transport_proto('aftp+gssapi://', register_netloc=True)
1767
register_lazy_transport('aftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1768
'GSSAPIFtpTransport')
1769
register_transport_proto('ftp+nogssapi://', register_netloc=True)
1770
register_transport_proto('aftp+nogssapi://', register_netloc=True)
1772
register_lazy_transport('ftp://', 'bzrlib.transport.ftp._gssapi',
1773
'GSSAPIFtpTransport')
1774
register_lazy_transport('aftp://', 'bzrlib.transport.ftp._gssapi',
1775
'GSSAPIFtpTransport')
1776
register_lazy_transport('ftp+nogssapi://', 'bzrlib.transport.ftp',
1778
register_lazy_transport('aftp+nogssapi://', 'bzrlib.transport.ftp',
1809
1781
register_transport_proto('memory://')
1810
1782
register_lazy_transport('memory://', 'bzrlib.transport.memory',
1879
1851
register_netloc=True)
1880
1852
register_lazy_transport('bzr+ssh://', 'bzrlib.transport.remote',
1881
1853
'RemoteSSHTransport')
1855
register_transport_proto('ssh:')
1856
register_lazy_transport('ssh:', 'bzrlib.transport.remote',
1857
'HintingSSHTransport')
1860
transport_server_registry = registry.Registry()
1861
transport_server_registry.register_lazy('bzr', 'bzrlib.smart.server',
1862
'serve_bzr', help="The Bazaar smart server protocol over TCP. (default port: 4155)")
1863
transport_server_registry.default_key = 'bzr'