~bzr-pqm/bzr/bzr.dev

5193.4.6 by Mattias Eriksson
Register a generic gio+ handler and raise an InvalidURL inside the gio init
1
# Copyright (C) 2010 Canonical Ltd.
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
5193.4.6 by Mattias Eriksson
Register a generic gio+ handler and raise an InvalidURL inside the gio init
16
#
17
# Author: Mattias Eriksson
18
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
19
"""Implementation of Transport over gio.
20
21
Written by Mattias Eriksson <snaggen@acc.umu.se> based on the ftp transport.
22
5193.4.5 by Mattias Eriksson
pep8
23
It provides the gio+XXX:// protocols where XXX is any of the protocols
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
24
supported by gio.
25
"""
26
from cStringIO import StringIO
27
import os
28
import random
29
import stat
30
import time
5193.4.4 by Mattias Eriksson
Remove username and password from the url we are sending to GIO
31
import urlparse
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
32
33
from bzrlib import (
34
    config,
35
    errors,
36
    osutils,
37
    urlutils,
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
38
    debug,
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
39
    ui,
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
40
    )
5390.2.1 by Alexander Belchenko
`decode` parameter to get() method in FtpTransport and GioTransport classes is deprecated.
41
from bzrlib.symbol_versioning import (
42
    DEPRECATED_PARAMETER,
43
    deprecated_in,
44
    deprecated_passed,
45
    warn,
46
    )
6039.1.4 by Jelmer Vernooij
Remove unused imports.
47
from bzrlib.trace import mutter
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
48
from bzrlib.transport import (
49
    FileStream,
50
    ConnectedTransport,
51
    _file_streams,
52
    )
53
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
54
from bzrlib.tests.test_server import TestServer
55
5193.4.19 by Mattias Eriksson
Explicit check for glib and gio and raise DependencyNotPresent
56
try:
57
    import glib
58
except ImportError, e:
59
    raise errors.DependencyNotPresent('glib', e)
60
try:
61
    import gio
62
except ImportError, e:
63
    raise errors.DependencyNotPresent('gio', e)
64
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
65
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
66
class GioLocalURLServer(TestServer):
67
    """A pretend server for local transports, using file:// urls.
68
69
    Of course no actual server is required to access the local filesystem, so
70
    this just exists to tell the test code how to get to it.
71
    """
72
73
    def start_server(self):
74
        pass
75
76
    def get_url(self):
77
        """See Transport.Server.get_url."""
78
        return "gio+" + urlutils.local_path_to_url('')
79
5193.4.5 by Mattias Eriksson
pep8
80
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
81
class GioFileStream(FileStream):
82
    """A file stream object returned by open_write_stream.
83
84
    This version uses GIO to perform writes.
85
    """
86
87
    def __init__(self, transport, relpath):
88
        FileStream.__init__(self, transport, relpath)
89
        self.gio_file = transport._get_GIO(relpath)
90
        self.stream = self.gio_file.create()
91
92
    def _close(self):
93
        self.stream.close()
94
95
    def write(self, bytes):
96
        try:
97
            #Using pump_string_file seems to make things crash
98
            osutils.pumpfile(StringIO(bytes), self.stream)
99
        except gio.Error, e:
100
            #self.transport._translate_gio_error(e,self.relpath)
101
            raise errors.BzrError(str(e))
102
5193.4.5 by Mattias Eriksson
pep8
103
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
104
class GioStatResult(object):
105
106
    def __init__(self, f):
5193.4.5 by Mattias Eriksson
pep8
107
        info = f.query_info('standard::size,standard::type')
108
        self.st_size = info.get_size()
109
        type = info.get_file_type()
110
        if (type == gio.FILE_TYPE_REGULAR):
111
            self.st_mode = stat.S_IFREG
112
        elif type == gio.FILE_TYPE_DIRECTORY:
113
            self.st_mode = stat.S_IFDIR
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
114
115
116
class GioTransport(ConnectedTransport):
117
    """This is the transport agent for gio+XXX:// access."""
118
119
    def __init__(self, base, _from_transport=None):
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
120
        """Initialize the GIO transport and make sure the url is correct."""
121
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
122
        if not base.startswith('gio+'):
123
            raise ValueError(base)
124
5193.4.17 by Mattias Eriksson
Use urlparse for both parsing and unparsing, since urlutils do decoding and other things.
125
        (scheme, netloc, path, params, query, fragment) = \
126
                urlparse.urlparse(base[len('gio+'):], allow_fragments=False)
127
        if '@' in netloc:
128
            user, netloc = netloc.rsplit('@', 1)
5193.4.6 by Mattias Eriksson
Register a generic gio+ handler and raise an InvalidURL inside the gio init
129
        #Seems it is not possible to list supported backends for GIO
130
        #so a hardcoded list it is then.
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
131
        gio_backends = ['dav', 'file', 'ftp', 'obex', 'sftp', 'ssh', 'smb']
5193.4.6 by Mattias Eriksson
Register a generic gio+ handler and raise an InvalidURL inside the gio init
132
        if scheme not in gio_backends:
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
133
            raise errors.InvalidURL(base,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
134
                    extra="GIO support is only available for " + \
135
                    ', '.join(gio_backends))
5193.4.5 by Mattias Eriksson
pep8
136
5193.4.4 by Mattias Eriksson
Remove username and password from the url we are sending to GIO
137
        #Remove the username and password from the url we send to GIO
5193.4.17 by Mattias Eriksson
Use urlparse for both parsing and unparsing, since urlutils do decoding and other things.
138
        #by rebuilding the url again.
5193.4.5 by Mattias Eriksson
pep8
139
        u = (scheme, netloc, path, '', '', '')
5193.4.4 by Mattias Eriksson
Remove username and password from the url we are sending to GIO
140
        self.url = urlparse.urlunparse(u)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
141
5193.4.6 by Mattias Eriksson
Register a generic gio+ handler and raise an InvalidURL inside the gio init
142
        # And finally initialize super
143
        super(GioTransport, self).__init__(base,
144
            _from_transport=_from_transport)
145
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
146
    def _relpath_to_url(self, relpath):
147
        full_url = urlutils.join(self.url, relpath)
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
148
        if isinstance(full_url, unicode):
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
149
            raise errors.InvalidURL(full_url)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
150
        return full_url
151
152
    def _get_GIO(self, relpath):
153
        """Return the ftplib.GIO instance for this object."""
154
        # Ensures that a connection is established
155
        connection = self._get_connection()
156
        if connection is None:
157
            # First connection ever
158
            connection, credentials = self._create_connection()
159
            self._set_connection(connection, credentials)
160
        fileurl = self._relpath_to_url(relpath)
5193.4.5 by Mattias Eriksson
pep8
161
        file = gio.File(fileurl)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
162
        return file
163
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
164
    def _auth_cb(self, op, message, default_user, default_domain, flags):
5193.4.5 by Mattias Eriksson
pep8
165
        #really use bzrlib.auth get_password for this
166
        #or possibly better gnome-keyring?
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
167
        auth = config.AuthenticationConfig()
6055.2.7 by Jelmer Vernooij
Change parse_url to URL.from_string.
168
        parsed_url = urlutils.URL.from_string(self.url)
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
169
        user = None
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
170
        if (flags & gio.ASK_PASSWORD_NEED_USERNAME and
171
                flags & gio.ASK_PASSWORD_NEED_DOMAIN):
6055.2.1 by Jelmer Vernooij
Add UnparsedUrl.
172
            prompt = (u'%s' % (parsed_url.scheme.upper(),) +
173
                      u' %(host)s DOMAIN\\username')
174
            user_and_domain = auth.get_user(parsed_url.scheme,
175
                parsed_url.host, port=parsed_url.port, ask=True,
176
                prompt=prompt)
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
177
            (domain, user) = user_and_domain.split('\\', 1)
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
178
            op.set_username(user)
179
            op.set_domain(domain)
180
        elif flags & gio.ASK_PASSWORD_NEED_USERNAME:
6055.2.1 by Jelmer Vernooij
Add UnparsedUrl.
181
            user = auth.get_user(parsed_url.scheme, parsed_url.host,
182
                    port=parsed_url.port, ask=True)
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
183
            op.set_username(user)
184
        elif flags & gio.ASK_PASSWORD_NEED_DOMAIN:
185
            #Don't know how common this case is, but anyway
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
186
            #a DOMAIN and a username prompt should be the
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
187
            #same so I will missuse the ui_factory get_username
188
            #a little bit here.
6055.2.1 by Jelmer Vernooij
Add UnparsedUrl.
189
            prompt = (u'%s' % (parsed_url.scheme.upper(),) +
190
                      u' %(host)s DOMAIN')
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
191
            domain = ui.ui_factory.get_username(prompt=prompt)
192
            op.set_domain(domain)
193
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
194
        if flags & gio.ASK_PASSWORD_NEED_PASSWORD:
5193.4.8 by Mattias Eriksson
Rework the authentication code to use the native bzrlib methods. This also means that it will use gnome-keyring if bzr-gtk plugins are available
195
            if user is None:
196
                user = op.get_username()
6055.2.1 by Jelmer Vernooij
Add UnparsedUrl.
197
            password = auth.get_password(parsed_url.scheme, parsed_url.host,
198
                    user, port=parsed_url.port)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
199
            op.set_password(password)
200
        op.reply(gio.MOUNT_OPERATION_HANDLED)
5193.4.5 by Mattias Eriksson
pep8
201
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
202
    def _mount_done_cb(self, obj, res):
203
        try:
204
            obj.mount_enclosing_volume_finish(res)
5193.4.24 by Mattias Eriksson
Make sure the loop.quit is run before raising the exception
205
            self.loop.quit()
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
206
        except gio.Error, e:
5193.4.24 by Mattias Eriksson
Make sure the loop.quit is run before raising the exception
207
            self.loop.quit()
5193.4.23 by Mattias Eriksson
Cant use the translation, so just raise a BzrError instead
208
            raise errors.BzrError("Failed to mount the given location: " + str(e));
5193.4.5 by Mattias Eriksson
pep8
209
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
210
    def _create_connection(self, credentials=None):
211
        if credentials is None:
6055.2.1 by Jelmer Vernooij
Add UnparsedUrl.
212
            user, password = self._parsed_url.user, self._parsed_url.password
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
213
        else:
214
            user, password = credentials
215
216
        try:
5193.4.5 by Mattias Eriksson
pep8
217
            connection = gio.File(self.url)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
218
            mount = None
219
            try:
220
                mount = connection.find_enclosing_mount()
221
            except gio.Error, e:
222
                if (e.code == gio.ERROR_NOT_MOUNTED):
5193.4.18 by Mattias Eriksson
Remove dependency on gtk+, run a glib.MainLoop instead
223
                    self.loop = glib.MainLoop()
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
224
                    ui.ui_factory.show_message('Mounting %s using GIO' % \
225
                            self.url)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
226
                    op = gio.MountOperation()
5193.4.2 by Mattias Eriksson
Set the username and password before mount if we have them available
227
                    if user:
228
                        op.set_username(user)
229
                    if password:
230
                        op.set_password(password)
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
231
                    op.connect('ask-password', self._auth_cb)
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
232
                    m = connection.mount_enclosing_volume(op,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
233
                            self._mount_done_cb)
5193.4.18 by Mattias Eriksson
Remove dependency on gtk+, run a glib.MainLoop instead
234
                    self.loop.run()
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
235
        except gio.Error, e:
236
            raise errors.TransportError(msg="Error setting up connection:"
237
                                        " %s" % str(e), orig_error=e)
238
        return connection, (user, password)
239
5247.2.12 by Vincent Ladeuil
Ensure that all transports close their underlying connection.
240
    def disconnect(self):
241
        # FIXME: Nothing seems to be necessary here, which sounds a bit strange
242
        # -- vila 20100601
243
        pass
244
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
245
    def _reconnect(self):
5247.2.12 by Vincent Ladeuil
Ensure that all transports close their underlying connection.
246
        # FIXME: This doesn't seem to be used -- vila 20100601
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
247
        """Create a new connection with the previously used credentials"""
248
        credentials = self._get_credentials()
249
        connection, credentials = self._create_connection(credentials)
250
        self._set_connection(connection, credentials)
251
252
    def _remote_path(self, relpath):
253
        relative = urlutils.unescape(relpath).encode('utf-8')
254
        remote_path = self._combine_paths(self._path, relative)
255
        return remote_path
256
257
    def has(self, relpath):
258
        """Does the target location exist?"""
259
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
260
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
261
                mutter('GIO has check: %s' % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
262
            f = self._get_GIO(relpath)
263
            st = GioStatResult(f)
5193.4.5 by Mattias Eriksson
pep8
264
            if stat.S_ISREG(st.st_mode) or stat.S_ISDIR(st.st_mode):
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
265
                return True
266
            return False
267
        except gio.Error, e:
268
            if e.code == gio.ERROR_NOT_FOUND:
269
                return False
270
            else:
271
                self._translate_gio_error(e, relpath)
272
5390.2.1 by Alexander Belchenko
`decode` parameter to get() method in FtpTransport and GioTransport classes is deprecated.
273
    def get(self, relpath, decode=DEPRECATED_PARAMETER, retries=0):
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
274
        """Get the file at the given relative path.
275
276
        :param relpath: The relative path to the file
277
        :param retries: Number of retries after temporary failures so far
278
                        for this operation.
279
280
        We're meant to return a file-like object which bzr will
281
        then read from. For now we do this via the magic of StringIO
282
        """
5390.2.1 by Alexander Belchenko
`decode` parameter to get() method in FtpTransport and GioTransport classes is deprecated.
283
        if deprecated_passed(decode):
284
            warn(deprecated_in((2,3,0)) %
285
                 '"decode" parameter to GioTransport.get()',
286
                 DeprecationWarning, stacklevel=2)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
287
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
288
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
289
                mutter("GIO get: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
290
            f = self._get_GIO(relpath)
291
            fin = f.read()
292
            buf = fin.read()
293
            fin.close()
294
            ret = StringIO(buf)
295
            return ret
296
        except gio.Error, e:
5193.4.9 by Mattias Eriksson
Cleaned up some user messages
297
            #If we get a not mounted here it might mean
298
            #that a bad path has been entered (or that mount failed)
299
            if (e.code == gio.ERROR_NOT_MOUNTED):
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
300
                raise errors.PathError(relpath,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
301
                  extra='Failed to get file, make sure the path is correct. ' \
302
                  + str(e))
5193.4.9 by Mattias Eriksson
Cleaned up some user messages
303
            else:
304
                self._translate_gio_error(e, relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
305
306
    def put_file(self, relpath, fp, mode=None):
307
        """Copy the file-like object into the location.
308
309
        :param relpath: Location to put the contents, relative to base.
310
        :param fp:       File-like or string object.
311
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
312
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
313
            mutter("GIO put_file %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
314
        tmppath = '%s.tmp.%.9f.%d.%d' % (relpath, time.time(),
5193.4.5 by Mattias Eriksson
pep8
315
                    os.getpid(), random.randint(0, 0x7FFFFFFF))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
316
        f = None
317
        fout = None
318
        try:
5268.1.1 by Vincent Ladeuil
Fix gio-related test failures.
319
            closed = True
5261.1.1 by Robert Collins
Python 2.4 friendliness is important to bzr.
320
            try:
321
                f = self._get_GIO(tmppath)
322
                fout = f.create()
323
                closed = False
324
                length = self._pump(fp, fout)
325
                fout.close()
326
                closed = True
327
                self.stat(tmppath)
328
                dest = self._get_GIO(relpath)
329
                f.move(dest, flags=gio.FILE_COPY_OVERWRITE)
330
                f = None
331
                if mode is not None:
332
                    self._setmode(relpath, mode)
333
                return length
334
            except gio.Error, e:
335
                self._translate_gio_error(e, relpath)
5193.4.14 by Mattias Eriksson
Cleanup exception handling in put_file
336
        finally:
337
            if not closed and fout is not None:
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
338
                fout.close()
5193.4.14 by Mattias Eriksson
Cleanup exception handling in put_file
339
            if f is not None and f.query_exists():
340
                f.delete()
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
341
342
    def mkdir(self, relpath, mode=None):
343
        """Create a directory at the given path."""
344
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
345
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
346
                mutter("GIO mkdir: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
347
            f = self._get_GIO(relpath)
348
            f.make_directory()
349
            self._setmode(relpath, mode)
350
        except gio.Error, e:
351
            self._translate_gio_error(e, relpath)
352
353
    def open_write_stream(self, relpath, mode=None):
354
        """See Transport.open_write_stream."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
355
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
356
            mutter("GIO open_write_stream %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
357
        if mode is not None:
358
            self._setmode(relpath, mode)
359
        result = GioFileStream(self, relpath)
360
        _file_streams[self.abspath(relpath)] = result
361
        return result
362
363
    def recommended_page_size(self):
364
        """See Transport.recommended_page_size().
365
366
        For FTP we suggest a large page size to reduce the overhead
367
        introduced by latency.
368
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
369
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
370
            mutter("GIO recommended_page")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
371
        return 64 * 1024
372
373
    def rmdir(self, relpath):
374
        """Delete the directory at rel_path"""
375
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
376
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
377
                mutter("GIO rmdir %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
378
            st = self.stat(relpath)
379
            if stat.S_ISDIR(st.st_mode):
380
                f = self._get_GIO(relpath)
381
                f.delete()
382
            else:
383
                raise errors.NotADirectory(relpath)
384
        except gio.Error, e:
385
            self._translate_gio_error(e, relpath)
386
        except errors.NotADirectory, e:
387
            #just pass it forward
388
            raise e
389
        except Exception, e:
5193.4.5 by Mattias Eriksson
pep8
390
            mutter('failed to rmdir %s: %s' % (relpath, e))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
391
            raise errors.PathError(relpath)
392
393
    def append_file(self, relpath, file, mode=None):
394
        """Append the text in the file-like object into the final
395
        location.
396
        """
397
        #GIO append_to seems not to append but to truncate
398
        #Work around this.
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
399
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
400
            mutter("GIO append_file: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
401
        tmppath = '%s.tmp.%.9f.%d.%d' % (relpath, time.time(),
5193.4.5 by Mattias Eriksson
pep8
402
                    os.getpid(), random.randint(0, 0x7FFFFFFF))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
403
        try:
404
            result = 0
405
            fo = self._get_GIO(tmppath)
406
            fi = self._get_GIO(relpath)
407
            fout = fo.create()
5193.4.5 by Mattias Eriksson
pep8
408
            try:
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
409
                info = GioStatResult(fi)
410
                result = info.st_size
411
                fin = fi.read()
5193.4.15 by Mattias Eriksson
Make append_file more clear by adding comment explaining things
412
                self._pump(fin, fout)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
413
                fin.close()
5193.4.19 by Mattias Eriksson
Explicit check for glib and gio and raise DependencyNotPresent
414
            #This separate except is to catch and ignore the
415
            #gio.ERROR_NOT_FOUND for the already existing file.
5193.4.15 by Mattias Eriksson
Make append_file more clear by adding comment explaining things
416
            #It is valid to open a non-existing file for append.
5193.4.19 by Mattias Eriksson
Explicit check for glib and gio and raise DependencyNotPresent
417
            #This is caused by the broken gio append_to...
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
418
            except gio.Error, e:
419
                if e.code != gio.ERROR_NOT_FOUND:
420
                    self._translate_gio_error(e, relpath)
5193.4.5 by Mattias Eriksson
pep8
421
            length = self._pump(file, fout)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
422
            fout.close()
5193.4.16 by Mattias Eriksson
Improved append safety by moving size check before the move
423
            info = GioStatResult(fo)
5193.4.5 by Mattias Eriksson
pep8
424
            if info.st_size != result + length:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
425
                raise errors.BzrError("Failed to append size after " \
426
                      "(%d) is not original (%d) + written (%d) total (%d)" % \
427
                      (info.st_size, result, length, result + length))
5193.4.16 by Mattias Eriksson
Improved append safety by moving size check before the move
428
            fo.move(fi, flags=gio.FILE_COPY_OVERWRITE)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
429
            return result
430
        except gio.Error, e:
431
            self._translate_gio_error(e, relpath)
432
433
    def _setmode(self, relpath, mode):
434
        """Set permissions on a path.
435
436
        Only set permissions on Unix systems
437
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
438
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
439
            mutter("GIO _setmode %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
440
        if mode:
441
            try:
442
                f = self._get_GIO(relpath)
5193.4.5 by Mattias Eriksson
pep8
443
                f.set_attribute_uint32(gio.FILE_ATTRIBUTE_UNIX_MODE, mode)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
444
            except gio.Error, e:
445
                if e.code == gio.ERROR_NOT_SUPPORTED:
446
                    # Command probably not available on this server
447
                    mutter("GIO Could not set permissions to %s on %s. %s",
448
                        oct(mode), self._remote_path(relpath), str(e))
449
                else:
450
                    self._translate_gio_error(e, relpath)
451
452
    def rename(self, rel_from, rel_to):
453
        """Rename without special overwriting"""
454
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
455
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
456
                mutter("GIO move (rename): %s => %s", rel_from, rel_to)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
457
            f = self._get_GIO(rel_from)
458
            t = self._get_GIO(rel_to)
459
            f.move(t)
460
        except gio.Error, e:
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
461
            self._translate_gio_error(e, rel_from)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
462
463
    def move(self, rel_from, rel_to):
464
        """Move the item at rel_from to the location at rel_to"""
465
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
466
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
467
                mutter("GIO move: %s => %s", rel_from, rel_to)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
468
            f = self._get_GIO(rel_from)
469
            t = self._get_GIO(rel_to)
470
            f.move(t, flags=gio.FILE_COPY_OVERWRITE)
471
        except gio.Error, e:
472
            self._translate_gio_error(e, relfrom)
473
474
    def delete(self, relpath):
475
        """Delete the item at relpath"""
476
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
477
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
478
                mutter("GIO delete: %s", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
479
            f = self._get_GIO(relpath)
480
            f.delete()
481
        except gio.Error, e:
482
            self._translate_gio_error(e, relpath)
483
484
    def external_url(self):
485
        """See bzrlib.transport.Transport.external_url."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
486
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
487
            mutter("GIO external_url", self.base)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
488
        # GIO external url
489
        return self.base
490
491
    def listable(self):
492
        """See Transport.listable."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
493
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
494
            mutter("GIO listable")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
495
        return True
496
497
    def list_dir(self, relpath):
498
        """See Transport.list_dir."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
499
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
500
            mutter("GIO list_dir")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
501
        try:
502
            entries = []
5193.4.5 by Mattias Eriksson
pep8
503
            f = self._get_GIO(relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
504
            children = f.enumerate_children(gio.FILE_ATTRIBUTE_STANDARD_NAME)
505
            for child in children:
506
                entries.append(urlutils.escape(child.get_name()))
507
            return entries
508
        except gio.Error, e:
509
            self._translate_gio_error(e, relpath)
510
511
    def iter_files_recursive(self):
512
        """See Transport.iter_files_recursive.
513
514
        This is cargo-culted from the SFTP transport"""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
515
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
516
            mutter("GIO iter_files_recursive")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
517
        queue = list(self.list_dir("."))
518
        while queue:
519
            relpath = queue.pop(0)
520
            st = self.stat(relpath)
521
            if stat.S_ISDIR(st.st_mode):
522
                for i, basename in enumerate(self.list_dir(relpath)):
5193.4.5 by Mattias Eriksson
pep8
523
                    queue.insert(i, relpath + "/" + basename)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
524
            else:
525
                yield relpath
526
527
    def stat(self, relpath):
528
        """Return the stat information for a file."""
529
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
530
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
531
                mutter("GIO stat: %s", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
532
            f = self._get_GIO(relpath)
533
            return GioStatResult(f)
534
        except gio.Error, e:
535
            self._translate_gio_error(e, relpath, extra='error w/ stat')
536
537
    def lock_read(self, relpath):
538
        """Lock the given file for shared (read) access.
539
        :return: A lock object, which should be passed to Transport.unlock()
540
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
541
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
542
            mutter("GIO lock_read", relpath)
5193.4.5 by Mattias Eriksson
pep8
543
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
544
        class BogusLock(object):
5193.4.5 by Mattias Eriksson
pep8
545
            # The old RemoteBranch ignore lock for reading, so we will
546
            # continue that tradition and return a bogus lock object.
547
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
548
            def __init__(self, path):
549
                self.path = path
5193.4.5 by Mattias Eriksson
pep8
550
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
551
            def unlock(self):
552
                pass
5193.4.5 by Mattias Eriksson
pep8
553
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
554
        return BogusLock(relpath)
555
556
    def lock_write(self, relpath):
557
        """Lock the given file for exclusive (write) access.
558
        WARNING: many transports do not support this, so trying avoid using it
559
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
560
        :return: A lock object, whichshould be passed to Transport.unlock()
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
561
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
562
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
563
            mutter("GIO lock_write", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
564
        return self.lock_read(relpath)
565
566
    def _translate_gio_error(self, err, path, extra=None):
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
567
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
568
            mutter("GIO Error: %s %s" % (str(err), path))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
569
        if extra is None:
570
            extra = str(err)
571
        if err.code == gio.ERROR_NOT_FOUND:
572
            raise errors.NoSuchFile(path, extra=extra)
573
        elif err.code == gio.ERROR_EXISTS:
574
            raise errors.FileExists(path, extra=extra)
575
        elif err.code == gio.ERROR_NOT_DIRECTORY:
576
            raise errors.NotADirectory(path, extra=extra)
577
        elif err.code == gio.ERROR_NOT_EMPTY:
578
            raise errors.DirectoryNotEmpty(path, extra=extra)
579
        elif err.code == gio.ERROR_BUSY:
580
            raise errors.ResourceBusy(path, extra=extra)
581
        elif err.code == gio.ERROR_PERMISSION_DENIED:
582
            raise errors.PermissionDenied(path, extra=extra)
5193.4.22 by Mattias Eriksson
Handle gio.Error on mount done and specifically handle gio.ERROR_HOST_NOT_FOUND
583
        elif err.code == gio.ERROR_HOST_NOT_FOUND:
584
            raise errors.PathError(path, extra=extra)
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
585
        elif err.code == gio.ERROR_IS_DIRECTORY:
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
586
            raise errors.PathError(path, extra=extra)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
587
        else:
588
            mutter('unable to understand error for path: %s: %s', path, err)
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
589
            raise errors.PathError(path,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
590
                    extra="Unhandled gio error: " + str(err))
591
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
592
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
593
def get_test_permutations():
594
    """Return the permutations to be used in testing."""
595
    from bzrlib.tests import test_server
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
596
    return [(GioTransport, GioLocalURLServer)]