~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):
6055.2.19 by Jelmer Vernooij
Use clone
253
        return self._parsed_url.clone(relpath).path
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
254
255
    def has(self, relpath):
256
        """Does the target location exist?"""
257
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
258
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
259
                mutter('GIO has check: %s' % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
260
            f = self._get_GIO(relpath)
261
            st = GioStatResult(f)
5193.4.5 by Mattias Eriksson
pep8
262
            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
263
                return True
264
            return False
265
        except gio.Error, e:
266
            if e.code == gio.ERROR_NOT_FOUND:
267
                return False
268
            else:
269
                self._translate_gio_error(e, relpath)
270
5390.2.1 by Alexander Belchenko
`decode` parameter to get() method in FtpTransport and GioTransport classes is deprecated.
271
    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
272
        """Get the file at the given relative path.
273
274
        :param relpath: The relative path to the file
275
        :param retries: Number of retries after temporary failures so far
276
                        for this operation.
277
278
        We're meant to return a file-like object which bzr will
279
        then read from. For now we do this via the magic of StringIO
280
        """
5390.2.1 by Alexander Belchenko
`decode` parameter to get() method in FtpTransport and GioTransport classes is deprecated.
281
        if deprecated_passed(decode):
282
            warn(deprecated_in((2,3,0)) %
283
                 '"decode" parameter to GioTransport.get()',
284
                 DeprecationWarning, stacklevel=2)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
285
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
286
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
287
                mutter("GIO get: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
288
            f = self._get_GIO(relpath)
289
            fin = f.read()
290
            buf = fin.read()
291
            fin.close()
292
            ret = StringIO(buf)
293
            return ret
294
        except gio.Error, e:
5193.4.9 by Mattias Eriksson
Cleaned up some user messages
295
            #If we get a not mounted here it might mean
296
            #that a bad path has been entered (or that mount failed)
297
            if (e.code == gio.ERROR_NOT_MOUNTED):
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
298
                raise errors.PathError(relpath,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
299
                  extra='Failed to get file, make sure the path is correct. ' \
300
                  + str(e))
5193.4.9 by Mattias Eriksson
Cleaned up some user messages
301
            else:
302
                self._translate_gio_error(e, relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
303
304
    def put_file(self, relpath, fp, mode=None):
305
        """Copy the file-like object into the location.
306
307
        :param relpath: Location to put the contents, relative to base.
308
        :param fp:       File-like or string object.
309
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
310
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
311
            mutter("GIO put_file %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
312
        tmppath = '%s.tmp.%.9f.%d.%d' % (relpath, time.time(),
5193.4.5 by Mattias Eriksson
pep8
313
                    os.getpid(), random.randint(0, 0x7FFFFFFF))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
314
        f = None
315
        fout = None
316
        try:
5268.1.1 by Vincent Ladeuil
Fix gio-related test failures.
317
            closed = True
5261.1.1 by Robert Collins
Python 2.4 friendliness is important to bzr.
318
            try:
319
                f = self._get_GIO(tmppath)
320
                fout = f.create()
321
                closed = False
322
                length = self._pump(fp, fout)
323
                fout.close()
324
                closed = True
325
                self.stat(tmppath)
326
                dest = self._get_GIO(relpath)
327
                f.move(dest, flags=gio.FILE_COPY_OVERWRITE)
328
                f = None
329
                if mode is not None:
330
                    self._setmode(relpath, mode)
331
                return length
332
            except gio.Error, e:
333
                self._translate_gio_error(e, relpath)
5193.4.14 by Mattias Eriksson
Cleanup exception handling in put_file
334
        finally:
335
            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
336
                fout.close()
5193.4.14 by Mattias Eriksson
Cleanup exception handling in put_file
337
            if f is not None and f.query_exists():
338
                f.delete()
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
339
340
    def mkdir(self, relpath, mode=None):
341
        """Create a directory at the given path."""
342
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
343
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
344
                mutter("GIO mkdir: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
345
            f = self._get_GIO(relpath)
346
            f.make_directory()
347
            self._setmode(relpath, mode)
348
        except gio.Error, e:
349
            self._translate_gio_error(e, relpath)
350
351
    def open_write_stream(self, relpath, mode=None):
352
        """See Transport.open_write_stream."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
353
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
354
            mutter("GIO open_write_stream %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
355
        if mode is not None:
356
            self._setmode(relpath, mode)
357
        result = GioFileStream(self, relpath)
358
        _file_streams[self.abspath(relpath)] = result
359
        return result
360
361
    def recommended_page_size(self):
362
        """See Transport.recommended_page_size().
363
364
        For FTP we suggest a large page size to reduce the overhead
365
        introduced by latency.
366
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
367
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
368
            mutter("GIO recommended_page")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
369
        return 64 * 1024
370
371
    def rmdir(self, relpath):
372
        """Delete the directory at rel_path"""
373
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
374
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
375
                mutter("GIO rmdir %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
376
            st = self.stat(relpath)
377
            if stat.S_ISDIR(st.st_mode):
378
                f = self._get_GIO(relpath)
379
                f.delete()
380
            else:
381
                raise errors.NotADirectory(relpath)
382
        except gio.Error, e:
383
            self._translate_gio_error(e, relpath)
384
        except errors.NotADirectory, e:
385
            #just pass it forward
386
            raise e
387
        except Exception, e:
5193.4.5 by Mattias Eriksson
pep8
388
            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
389
            raise errors.PathError(relpath)
390
391
    def append_file(self, relpath, file, mode=None):
392
        """Append the text in the file-like object into the final
393
        location.
394
        """
395
        #GIO append_to seems not to append but to truncate
396
        #Work around this.
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
397
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
398
            mutter("GIO append_file: %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
399
        tmppath = '%s.tmp.%.9f.%d.%d' % (relpath, time.time(),
5193.4.5 by Mattias Eriksson
pep8
400
                    os.getpid(), random.randint(0, 0x7FFFFFFF))
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
401
        try:
402
            result = 0
403
            fo = self._get_GIO(tmppath)
404
            fi = self._get_GIO(relpath)
405
            fout = fo.create()
5193.4.5 by Mattias Eriksson
pep8
406
            try:
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
407
                info = GioStatResult(fi)
408
                result = info.st_size
409
                fin = fi.read()
5193.4.15 by Mattias Eriksson
Make append_file more clear by adding comment explaining things
410
                self._pump(fin, fout)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
411
                fin.close()
5193.4.19 by Mattias Eriksson
Explicit check for glib and gio and raise DependencyNotPresent
412
            #This separate except is to catch and ignore the
413
            #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
414
            #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
415
            #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
416
            except gio.Error, e:
417
                if e.code != gio.ERROR_NOT_FOUND:
418
                    self._translate_gio_error(e, relpath)
5193.4.5 by Mattias Eriksson
pep8
419
            length = self._pump(file, fout)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
420
            fout.close()
5193.4.16 by Mattias Eriksson
Improved append safety by moving size check before the move
421
            info = GioStatResult(fo)
5193.4.5 by Mattias Eriksson
pep8
422
            if info.st_size != result + length:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
423
                raise errors.BzrError("Failed to append size after " \
424
                      "(%d) is not original (%d) + written (%d) total (%d)" % \
425
                      (info.st_size, result, length, result + length))
5193.4.16 by Mattias Eriksson
Improved append safety by moving size check before the move
426
            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
427
            return result
428
        except gio.Error, e:
429
            self._translate_gio_error(e, relpath)
430
431
    def _setmode(self, relpath, mode):
432
        """Set permissions on a path.
433
434
        Only set permissions on Unix systems
435
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
436
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
437
            mutter("GIO _setmode %s" % relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
438
        if mode:
439
            try:
440
                f = self._get_GIO(relpath)
5193.4.5 by Mattias Eriksson
pep8
441
                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
442
            except gio.Error, e:
443
                if e.code == gio.ERROR_NOT_SUPPORTED:
444
                    # Command probably not available on this server
445
                    mutter("GIO Could not set permissions to %s on %s. %s",
446
                        oct(mode), self._remote_path(relpath), str(e))
447
                else:
448
                    self._translate_gio_error(e, relpath)
449
450
    def rename(self, rel_from, rel_to):
451
        """Rename without special overwriting"""
452
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
453
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
454
                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
455
            f = self._get_GIO(rel_from)
456
            t = self._get_GIO(rel_to)
457
            f.move(t)
458
        except gio.Error, e:
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
459
            self._translate_gio_error(e, rel_from)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
460
461
    def move(self, rel_from, rel_to):
462
        """Move the item at rel_from to the location at rel_to"""
463
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
464
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
465
                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
466
            f = self._get_GIO(rel_from)
467
            t = self._get_GIO(rel_to)
468
            f.move(t, flags=gio.FILE_COPY_OVERWRITE)
469
        except gio.Error, e:
470
            self._translate_gio_error(e, relfrom)
471
472
    def delete(self, relpath):
473
        """Delete the item at relpath"""
474
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
475
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
476
                mutter("GIO delete: %s", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
477
            f = self._get_GIO(relpath)
478
            f.delete()
479
        except gio.Error, e:
480
            self._translate_gio_error(e, relpath)
481
482
    def external_url(self):
483
        """See bzrlib.transport.Transport.external_url."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
484
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
485
            mutter("GIO external_url", self.base)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
486
        # GIO external url
487
        return self.base
488
489
    def listable(self):
490
        """See Transport.listable."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
491
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
492
            mutter("GIO listable")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
493
        return True
494
495
    def list_dir(self, relpath):
496
        """See Transport.list_dir."""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
497
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
498
            mutter("GIO list_dir")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
499
        try:
500
            entries = []
5193.4.5 by Mattias Eriksson
pep8
501
            f = self._get_GIO(relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
502
            children = f.enumerate_children(gio.FILE_ATTRIBUTE_STANDARD_NAME)
503
            for child in children:
504
                entries.append(urlutils.escape(child.get_name()))
505
            return entries
506
        except gio.Error, e:
507
            self._translate_gio_error(e, relpath)
508
509
    def iter_files_recursive(self):
510
        """See Transport.iter_files_recursive.
511
512
        This is cargo-culted from the SFTP transport"""
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
513
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
514
            mutter("GIO iter_files_recursive")
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
515
        queue = list(self.list_dir("."))
516
        while queue:
517
            relpath = queue.pop(0)
518
            st = self.stat(relpath)
519
            if stat.S_ISDIR(st.st_mode):
520
                for i, basename in enumerate(self.list_dir(relpath)):
5193.4.5 by Mattias Eriksson
pep8
521
                    queue.insert(i, relpath + "/" + basename)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
522
            else:
523
                yield relpath
524
525
    def stat(self, relpath):
526
        """Return the stat information for a file."""
527
        try:
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
528
            if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
529
                mutter("GIO stat: %s", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
530
            f = self._get_GIO(relpath)
531
            return GioStatResult(f)
532
        except gio.Error, e:
533
            self._translate_gio_error(e, relpath, extra='error w/ stat')
534
535
    def lock_read(self, relpath):
536
        """Lock the given file for shared (read) access.
537
        :return: A lock object, which should be passed to Transport.unlock()
538
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
539
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
540
            mutter("GIO lock_read", relpath)
5193.4.5 by Mattias Eriksson
pep8
541
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
542
        class BogusLock(object):
5193.4.5 by Mattias Eriksson
pep8
543
            # The old RemoteBranch ignore lock for reading, so we will
544
            # continue that tradition and return a bogus lock object.
545
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
546
            def __init__(self, path):
547
                self.path = path
5193.4.5 by Mattias Eriksson
pep8
548
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
549
            def unlock(self):
550
                pass
5193.4.5 by Mattias Eriksson
pep8
551
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
552
        return BogusLock(relpath)
553
554
    def lock_write(self, relpath):
555
        """Lock the given file for exclusive (write) access.
556
        WARNING: many transports do not support this, so trying avoid using it
557
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
558
        :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
559
        """
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
560
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
561
            mutter("GIO lock_write", relpath)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
562
        return self.lock_read(relpath)
563
564
    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
565
        if 'gio' in debug.debug_flags:
5193.4.7 by Mattias Eriksson
Don't write debug output unless -Dgio is specified on the commandline
566
            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
567
        if extra is None:
568
            extra = str(err)
569
        if err.code == gio.ERROR_NOT_FOUND:
570
            raise errors.NoSuchFile(path, extra=extra)
571
        elif err.code == gio.ERROR_EXISTS:
572
            raise errors.FileExists(path, extra=extra)
573
        elif err.code == gio.ERROR_NOT_DIRECTORY:
574
            raise errors.NotADirectory(path, extra=extra)
575
        elif err.code == gio.ERROR_NOT_EMPTY:
576
            raise errors.DirectoryNotEmpty(path, extra=extra)
577
        elif err.code == gio.ERROR_BUSY:
578
            raise errors.ResourceBusy(path, extra=extra)
579
        elif err.code == gio.ERROR_PERMISSION_DENIED:
580
            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
581
        elif err.code == gio.ERROR_HOST_NOT_FOUND:
582
            raise errors.PathError(path, extra=extra)
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
583
        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
584
            raise errors.PathError(path, extra=extra)
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
585
        else:
586
            mutter('unable to understand error for path: %s: %s', path, err)
5193.4.20 by Mattias Eriksson
Remove not needed \ when breaking lines
587
            raise errors.PathError(path,
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
588
                    extra="Unhandled gio error: " + str(err))
589
5193.4.1 by Mattias Eriksson
A GIO transport based on the FTP and SFTP transport implementations
590
5193.4.10 by Mattias Eriksson
Set up the tests and add some fixes to make the pass
591
def get_test_permutations():
592
    """Return the permutations to be used in testing."""
593
    from bzrlib.tests import test_server
5193.4.13 by Mattias Eriksson
Cleanup, pep8 fixes and minor adjustments to make things clearer
594
    return [(GioTransport, GioLocalURLServer)]