* filenotify.el (file-notify-supported-p):
[bpt/emacs.git] / lisp / net / tramp-gvfs.el
CommitLineData
eeb44655
MA
1;;; tramp-gvfs.el --- Tramp access functions for GVFS daemon
2
ab422c4d 3;; Copyright (C) 2009-2013 Free Software Foundation, Inc.
eeb44655
MA
4
5;; Author: Michael Albinus <michael.albinus@gmx.de>
6;; Keywords: comm, processes
bd78fa1d 7;; Package: tramp
eeb44655 8
e65f32c1
GM
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
eeb44655
MA
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
e65f32c1
GM
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
eeb44655
MA
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; Access functions for the GVFS daemon from Tramp. Tested with GVFS
3675b169 27;; 1.0 (Ubuntu 8.10, Gnome 2.24). It has been reported also to run
f0dbdc25
MA
28;; with GVFS 0.2.5 (Ubuntu 8.04, Gnome 2.22), but there is an
29;; incompatibility with the mount_info structure, which has been
30;; worked around.
eeb44655 31
3675b169 32;; It has also been tested with GVFS 1.6 (Ubuntu 10.04, Gnome 2.30),
1efeec86
MA
33;; where the default_location has been added to mount_info (see
34;; <https://bugzilla.gnome.org/show_bug.cgi?id=561998>.
35
3675b169
MA
36;; With GVFS 1.14 (Ubuntu 12.10, Gnome 3.6) the interfaces have been
37;; changed, again. So we must introspect the D-Bus interfaces.
38
eeb44655
MA
39;; All actions to mount a remote location, and to retrieve mount
40;; information, are performed by D-Bus messages. File operations
41;; themselves are performed via the mounted filesystem in ~/.gvfs.
f0dbdc25 42;; Consequently, GNU Emacs 23.1 with enabled D-Bus bindings is a
eeb44655
MA
43;; precondition.
44
3675b169
MA
45;; The GVFS D-Bus interface is said to be unstable. There were even
46;; no introspection data before GVFS 1.14. The interface, as
47;; discovered during development time, is given in respective
48;; comments.
eeb44655
MA
49
50;; The customer option `tramp-gvfs-methods' contains the list of
d557e7a6
MA
51;; supported connection methods. Per default, these are "dav",
52;; "davs", "obex" and "synce". Note that with "obex" it might be
53;; necessary to pair with the other bluetooth device, if it hasn't
54;; been done already. There might be also some few seconds delay in
55;; discovering available bluetooth devices.
eeb44655
MA
56
57;; Other possible connection methods are "ftp", "sftp" and "smb".
58;; When one of these methods is added to the list, the remote access
59;; for that method is performed via GVFS instead of the native Tramp
60;; implementation.
61
62;; GVFS offers even more connection methods. The complete list of
63;; connection methods of the actual GVFS implementation can be
64;; retrieved by:
65;;
66;; (message
67;; "%s"
68;; (mapcar
69;; 'car
70;; (dbus-call-method
71;; :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
72;; tramp-gvfs-interface-mounttracker "listMountableInfo")))
73
74;; Note that all other connection methods are not tested, beside the
75;; ones offered for customization in `tramp-gvfs-methods'. If you
76;; request an additional connection method to be supported, please
77;; drop me a note.
78
79;; For hostname completion, information is retrieved either from the
d557e7a6
MA
80;; bluez daemon (for the "obex" method), the hal daemon (for the
81;; "synce" method), or from the zeroconf daemon (for the "dav",
82;; "davs", and "sftp" methods). The zeroconf daemon is pre-configured
83;; to discover services in the "local" domain. If another domain
84;; shall be used for discovering services, the customer option
85;; `tramp-gvfs-zeroconf-domain' can be set accordingly.
eeb44655
MA
86
87;; Restrictions:
88
89;; * The current GVFS implementation does not allow to write on the
90;; remote bluetooth device via OBEX.
91;;
92;; * Two shares of the same SMB server cannot be mounted in parallel.
93
94;;; Code:
95
96;; D-Bus support in the Emacs core can be disabled with configuration
97;; option "--without-dbus". Declare used subroutines and variables.
eeb44655 98(declare-function dbus-get-unique-name "dbusbind.c")
eeb44655
MA
99
100;; Pacify byte-compiler
101(eval-when-compile
102 (require 'cl)
103 (require 'custom))
104
105(require 'tramp)
03c1ad43 106
eeb44655
MA
107(require 'dbus)
108(require 'url-parse)
1efeec86 109(require 'url-util)
eeb44655
MA
110(require 'zeroconf)
111
0f34aa77 112;;;###tramp-autoload
7ae3ea65 113(defcustom tramp-gvfs-methods '("dav" "davs" "obex" "synce")
fb7ada5f 114 "List of methods for remote files, accessed with GVFS."
eeb44655 115 :group 'tramp
f0dbdc25 116 :version "23.2"
eeb44655
MA
117 :type '(repeat (choice (const "dav")
118 (const "davs")
119 (const "ftp")
120 (const "obex")
121 (const "sftp")
7ae3ea65
MA
122 (const "smb")
123 (const "synce"))))
eeb44655 124
d557e7a6
MA
125;; Add a default for `tramp-default-user-alist'. Rule: For the SYNCE
126;; method, no user is chosen.
b191c9d9 127;;;###tramp-autoload
66feec8b 128(add-to-list 'tramp-default-user-alist '("\\`synce\\'" nil nil))
d557e7a6 129
eeb44655 130(defcustom tramp-gvfs-zeroconf-domain "local"
fb7ada5f 131 "Zeroconf domain to be used for discovering services, like host names."
eeb44655 132 :group 'tramp
f0dbdc25 133 :version "23.2"
eeb44655
MA
134 :type 'string)
135
136;; Add the methods to `tramp-methods', in order to allow minibuffer
137;; completion.
0f34aa77
MA
138;;;###tramp-autoload
139(when (featurep 'dbusbind)
140 (dolist (elt tramp-gvfs-methods)
141 (unless (assoc elt tramp-methods)
142 (add-to-list 'tramp-methods (cons elt nil)))))
eeb44655 143
eeb44655 144(defconst tramp-gvfs-path-tramp (concat dbus-path-emacs "/Tramp")
97610156 145 "The preceding object path for own objects.")
eeb44655
MA
146
147(defconst tramp-gvfs-service-daemon "org.gtk.vfs.Daemon"
148 "The well known name of the GVFS daemon.")
149
91aafa16
MA
150;; D-Bus integration is available since Emacs 23 on some system types.
151;; We don't call `dbus-ping', because this would load dbus.el.
152(defconst tramp-gvfs-enabled
153 (ignore-errors
154 (and (featurep 'dbusbind)
155 (tramp-compat-funcall 'dbus-get-unique-name :session)
156 (or (tramp-compat-process-running-p "gvfs-fuse-daemon")
157 (tramp-compat-process-running-p "gvfsd-fuse"))))
158 "Non-nil when GVFS is available.")
eeb44655
MA
159
160(defconst tramp-gvfs-path-mounttracker "/org/gtk/vfs/mounttracker"
161 "The object path of the GVFS daemon.")
162
163(defconst tramp-gvfs-interface-mounttracker "org.gtk.vfs.MountTracker"
164 "The mount tracking interface in the GVFS daemon.")
165
3675b169
MA
166;; Introspection data exist since GVFS 1.14. If there are no such
167;; data, we expect an earlier interface.
168(defconst tramp-gvfs-methods-mounttracker
169 (dbus-introspect-get-method-names
170 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
171 tramp-gvfs-interface-mounttracker)
172 "The list of supported methods of the mount tracking interface.")
173
174(defconst tramp-gvfs-listmounts
175 (if (member "ListMounts" tramp-gvfs-methods-mounttracker)
176 "ListMounts"
177 "listMounts")
178 "The name of the \"listMounts\" method.
179It has been changed in GVFS 1.14.")
180
181(defconst tramp-gvfs-mountlocation
182 (if (member "MountLocation" tramp-gvfs-methods-mounttracker)
183 "MountLocation"
184 "mountLocation")
185 "The name of the \"mountLocation\" method.
186It has been changed in GVFS 1.14.")
187
188(defconst tramp-gvfs-mountlocation-signature
189 (dbus-introspect-get-signature
190 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
191 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation)
192 "The D-Bus signature of the \"mountLocation\" method.
193It has been changed in GVFS 1.14.")
194
eeb44655
MA
195;; <interface name='org.gtk.vfs.MountTracker'>
196;; <method name='listMounts'>
197;; <arg name='mount_info_list'
4f201088 198;; type='a{sosssssbay{aya{say}}ay}'
eeb44655
MA
199;; direction='out'/>
200;; </method>
201;; <method name='mountLocation'>
202;; <arg name='mount_spec' type='{aya{say}}' direction='in'/>
203;; <arg name='dbus_id' type='s' direction='in'/>
204;; <arg name='object_path' type='o' direction='in'/>
205;; </method>
206;; <signal name='mounted'>
207;; <arg name='mount_info'
4f201088 208;; type='{sosssssbay{aya{say}}ay}'/>
eeb44655
MA
209;; </signal>
210;; <signal name='unmounted'>
211;; <arg name='mount_info'
4f201088 212;; type='{sosssssbay{aya{say}}ay}'/>
eeb44655
MA
213;; </signal>
214;; </interface>
215;;
216;; STRUCT mount_info
217;; STRING dbus_id
218;; OBJECT_PATH object_path
219;; STRING display_name
220;; STRING stable_name
f0dbdc25 221;; STRING x_content_types Since GVFS 1.0 only !!!
eeb44655 222;; STRING icon
fa463103 223;; STRING preferred_filename_encoding
eeb44655
MA
224;; BOOLEAN user_visible
225;; ARRAY BYTE fuse_mountpoint
226;; STRUCT mount_spec
227;; ARRAY BYTE mount_prefix
228;; ARRAY
229;; STRUCT mount_spec_item
230;; STRING key (server, share, type, user, host, port)
231;; ARRAY BYTE value
4f201088 232;; ARRAY BYTE default_location Since GVFS 1.5 only !!!
eeb44655
MA
233
234(defconst tramp-gvfs-interface-mountoperation "org.gtk.vfs.MountOperation"
235 "Used by the dbus-proxying implementation of GMountOperation.")
236
237;; <interface name='org.gtk.vfs.MountOperation'>
238;; <method name='askPassword'>
d3e97185 239;; <arg name='message' type='s' direction='in'/>
eeb44655
MA
240;; <arg name='default_user' type='s' direction='in'/>
241;; <arg name='default_domain' type='s' direction='in'/>
242;; <arg name='flags' type='u' direction='in'/>
243;; <arg name='handled' type='b' direction='out'/>
244;; <arg name='aborted' type='b' direction='out'/>
245;; <arg name='password' type='s' direction='out'/>
246;; <arg name='username' type='s' direction='out'/>
247;; <arg name='domain' type='s' direction='out'/>
248;; <arg name='anonymous' type='b' direction='out'/>
249;; <arg name='password_save' type='u' direction='out'/>
250;; </method>
251;; <method name='askQuestion'>
252;; <arg name='message' type='s' direction='in'/>
253;; <arg name='choices' type='as' direction='in'/>
254;; <arg name='handled' type='b' direction='out'/>
255;; <arg name='aborted' type='b' direction='out'/>
256;; <arg name='choice' type='u' direction='out'/>
257;; </method>
258;; </interface>
259
260;; The following flags are used in "askPassword". They are defined in
261;; /usr/include/glib-2.0/gio/gioenums.h.
262
263(defconst tramp-gvfs-password-need-password 1
264 "Operation requires a password.")
265
266(defconst tramp-gvfs-password-need-username 2
267 "Operation requires a username.")
268
269(defconst tramp-gvfs-password-need-domain 4
270 "Operation requires a domain.")
271
272(defconst tramp-gvfs-password-saving-supported 8
273 "Operation supports saving settings.")
274
275(defconst tramp-gvfs-password-anonymous-supported 16
276 "Operation supports anonymous users.")
277
278(defconst tramp-bluez-service "org.bluez"
279 "The well known name of the BLUEZ service.")
280
281(defconst tramp-bluez-interface-manager "org.bluez.Manager"
282 "The manager interface of the BLUEZ daemon.")
283
284;; <interface name='org.bluez.Manager'>
285;; <method name='DefaultAdapter'>
286;; <arg type='o' direction='out'/>
287;; </method>
288;; <method name='FindAdapter'>
289;; <arg type='s' direction='in'/>
290;; <arg type='o' direction='out'/>
291;; </method>
292;; <method name='ListAdapters'>
293;; <arg type='ao' direction='out'/>
294;; </method>
295;; <signal name='AdapterAdded'>
296;; <arg type='o'/>
297;; </signal>
298;; <signal name='AdapterRemoved'>
299;; <arg type='o'/>
300;; </signal>
301;; <signal name='DefaultAdapterChanged'>
302;; <arg type='o'/>
303;; </signal>
304;; </interface>
305
306(defconst tramp-bluez-interface-adapter "org.bluez.Adapter"
307 "The adapter interface of the BLUEZ daemon.")
308
309;; <interface name='org.bluez.Adapter'>
310;; <method name='GetProperties'>
311;; <arg type='a{sv}' direction='out'/>
312;; </method>
313;; <method name='SetProperty'>
314;; <arg type='s' direction='in'/>
315;; <arg type='v' direction='in'/>
316;; </method>
317;; <method name='RequestMode'>
318;; <arg type='s' direction='in'/>
319;; </method>
320;; <method name='ReleaseMode'/>
321;; <method name='RequestSession'/>
322;; <method name='ReleaseSession'/>
323;; <method name='StartDiscovery'/>
324;; <method name='StopDiscovery'/>
325;; <method name='ListDevices'>
326;; <arg type='ao' direction='out'/>
327;; </method>
328;; <method name='CreateDevice'>
329;; <arg type='s' direction='in'/>
330;; <arg type='o' direction='out'/>
331;; </method>
332;; <method name='CreatePairedDevice'>
333;; <arg type='s' direction='in'/>
334;; <arg type='o' direction='in'/>
335;; <arg type='s' direction='in'/>
336;; <arg type='o' direction='out'/>
337;; </method>
338;; <method name='CancelDeviceCreation'>
339;; <arg type='s' direction='in'/>
340;; </method>
341;; <method name='RemoveDevice'>
342;; <arg type='o' direction='in'/>
343;; </method>
344;; <method name='FindDevice'>
345;; <arg type='s' direction='in'/>
346;; <arg type='o' direction='out'/>
347;; </method>
348;; <method name='RegisterAgent'>
349;; <arg type='o' direction='in'/>
350;; <arg type='s' direction='in'/>
351;; </method>
352;; <method name='UnregisterAgent'>
353;; <arg type='o' direction='in'/>
354;; </method>
355;; <signal name='DeviceCreated'>
356;; <arg type='o'/>
357;; </signal>
358;; <signal name='DeviceRemoved'>
359;; <arg type='o'/>
360;; </signal>
361;; <signal name='DeviceFound'>
362;; <arg type='s'/>
363;; <arg type='a{sv}'/>
364;; </signal>
365;; <signal name='PropertyChanged'>
366;; <arg type='s'/>
367;; <arg type='v'/>
368;; </signal>
369;; <signal name='DeviceDisappeared'>
370;; <arg type='s'/>
371;; </signal>
372;; </interface>
373
374(defcustom tramp-bluez-discover-devices-timeout 60
375 "Defines seconds since last bluetooth device discovery before rescanning.
376A value of 0 would require an immediate discovery during hostname
377completion, nil means to use always cached values for discovered
378devices."
379 :group 'tramp
f0dbdc25 380 :version "23.2"
eeb44655
MA
381 :type '(choice (const nil) integer))
382
383(defvar tramp-bluez-discovery nil
384 "Indicator for a running bluetooth device discovery.
385It keeps the timestamp of last discovery.")
386
387(defvar tramp-bluez-devices nil
388 "Alist of detected bluetooth devices.
389Every entry is a list (NAME ADDRESS).")
390
d557e7a6
MA
391(defconst tramp-hal-service "org.freedesktop.Hal"
392 "The well known name of the HAL service.")
393
394(defconst tramp-hal-path-manager "/org/freedesktop/Hal/Manager"
395 "The object path of the HAL daemon manager.")
396
397(defconst tramp-hal-interface-manager "org.freedesktop.Hal.Manager"
398 "The manager interface of the HAL daemon.")
399
400(defconst tramp-hal-interface-device "org.freedesktop.Hal.Device"
401 "The device interface of the HAL daemon.")
402
403\f
eeb44655
MA
404;; New handlers should be added here.
405(defconst tramp-gvfs-file-name-handler-alist
406 '(
407 (access-file . ignore)
408 (add-name-to-file . tramp-gvfs-handle-copy-file)
99278f8a 409 ;; `byte-compiler-base-file-name' performed by default handler.
eeb44655
MA
410 (copy-file . tramp-gvfs-handle-copy-file)
411 (delete-directory . tramp-gvfs-handle-delete-directory)
412 (delete-file . tramp-gvfs-handle-delete-file)
99278f8a 413 ;; `diff-latest-backup-file' performed by default handler.
eeb44655 414 (directory-file-name . tramp-handle-directory-file-name)
3675b169 415 (directory-files . tramp-handle-directory-files)
eeb44655 416 (directory-files-and-attributes
3675b169 417 . tramp-handle-directory-files-and-attributes)
eeb44655
MA
418 (dired-call-process . ignore)
419 (dired-compress-file . ignore)
420 (dired-uncache . tramp-handle-dired-uncache)
99278f8a 421 ;; `executable-find' is not official yet. performed by default handler.
eeb44655 422 (expand-file-name . tramp-gvfs-handle-expand-file-name)
10ffd0be 423 (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
3675b169 424 (file-acl . ignore)
eeb44655 425 (file-attributes . tramp-gvfs-handle-file-attributes)
0f34aa77 426 (file-directory-p . tramp-gvfs-handle-file-directory-p)
eeb44655 427 (file-executable-p . tramp-gvfs-handle-file-executable-p)
3675b169 428 (file-exists-p . tramp-handle-file-exists-p)
eeb44655 429 (file-local-copy . tramp-gvfs-handle-file-local-copy)
3675b169 430 (file-modes . tramp-handle-file-modes)
eeb44655
MA
431 (file-name-all-completions . tramp-gvfs-handle-file-name-all-completions)
432 (file-name-as-directory . tramp-handle-file-name-as-directory)
433 (file-name-completion . tramp-handle-file-name-completion)
434 (file-name-directory . tramp-handle-file-name-directory)
435 (file-name-nondirectory . tramp-handle-file-name-nondirectory)
99278f8a 436 ;; `file-name-sans-versions' performed by default handler.
eeb44655 437 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
80ff0c71 438 (file-notify-add-watch . tramp-handle-file-notify-add-watch)
e06ec67f 439 (file-notify-rm-watch . ignore)
eeb44655
MA
440 (file-ownership-preserved-p . ignore)
441 (file-readable-p . tramp-gvfs-handle-file-readable-p)
442 (file-regular-p . tramp-handle-file-regular-p)
632c5478 443 (file-remote-p . tramp-handle-file-remote-p)
3675b169 444 (file-selinux-context . ignore)
eeb44655 445 (file-symlink-p . tramp-handle-file-symlink-p)
99278f8a 446 ;; `file-truename' performed by default handler.
eeb44655
MA
447 (file-writable-p . tramp-gvfs-handle-file-writable-p)
448 (find-backup-file-name . tramp-handle-find-backup-file-name)
99278f8a
MA
449 ;; `find-file-noselect' performed by default handler.
450 ;; `get-file-buffer' performed by default handler.
eeb44655
MA
451 (insert-directory . tramp-gvfs-handle-insert-directory)
452 (insert-file-contents . tramp-gvfs-handle-insert-file-contents)
453 (load . tramp-handle-load)
454 (make-directory . tramp-gvfs-handle-make-directory)
455 (make-directory-internal . ignore)
456 (make-symbolic-link . ignore)
3675b169 457 (process-file . ignore)
eeb44655 458 (rename-file . tramp-gvfs-handle-rename-file)
3675b169
MA
459 (set-file-acl . ignore)
460 (set-file-modes . ignore)
461 (set-file-selinux-context . ignore)
eeb44655 462 (set-visited-file-modtime . tramp-gvfs-handle-set-visited-file-modtime)
3675b169
MA
463 (shell-command . ignore)
464 (start-file-process . ignore)
eeb44655
MA
465 (substitute-in-file-name . tramp-handle-substitute-in-file-name)
466 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
467 (vc-registered . ignore)
3675b169 468 ;; `verify-visited-file-modtime' performed by default handler.
eeb44655
MA
469 (write-region . tramp-gvfs-handle-write-region)
470)
471 "Alist of handler functions for Tramp GVFS method.
472Operations not mentioned here will be handled by the default Emacs primitives.")
473
b421decc
MA
474;; It must be a `defsubst' in order to push the whole code into
475;; tramp-loaddefs.el. Otherwise, there would be recursive autoloading.
0f34aa77
MA
476;;;###tramp-autoload
477(defsubst tramp-gvfs-file-name-p (filename)
eeb44655
MA
478 "Check if it's a filename handled by the GVFS daemon."
479 (and (tramp-tramp-file-p filename)
480 (let ((method
481 (tramp-file-name-method (tramp-dissect-file-name filename))))
482 (and (stringp method) (member method tramp-gvfs-methods)))))
483
0f34aa77 484;;;###tramp-autoload
eeb44655
MA
485(defun tramp-gvfs-file-name-handler (operation &rest args)
486 "Invoke the GVFS related OPERATION.
487First arg specifies the OPERATION, second arg is a list of arguments to
488pass to the OPERATION."
91aafa16
MA
489 (unless tramp-gvfs-enabled
490 (tramp-compat-user-error "Package `tramp-gvfs' not supported"))
eeb44655
MA
491 (let ((fn (assoc operation tramp-gvfs-file-name-handler-alist)))
492 (if fn
493 (save-match-data (apply (cdr fn) args))
494 (tramp-run-real-handler operation args))))
495
496;; This might be moved to tramp.el. It shall be the first file name
497;; handler.
0f34aa77
MA
498;;;###tramp-autoload
499(when (featurep 'dbusbind)
500 (add-to-list 'tramp-foreign-file-name-handler-alist
501 (cons 'tramp-gvfs-file-name-p 'tramp-gvfs-file-name-handler)))
eeb44655 502
3675b169
MA
503\f
504;; D-Bus helper function.
505
506(defun tramp-gvfs-dbus-string-to-byte-array (string)
507 "Like `dbus-string-to-byte-array' but add trailing \\0 if needed."
508 (dbus-string-to-byte-array
509 (if (string-match "^(aya{sv})" tramp-gvfs-mountlocation-signature)
510 (concat string (string 0)) string)))
511
512(defun tramp-gvfs-dbus-byte-array-to-string (byte-array)
513 "Like `dbus-byte-array-to-string' but remove trailing \\0 if exists."
514 ;; The byte array could be a variant. Take care.
515 (let ((byte-array
516 (if (and (consp byte-array) (atom (car byte-array)))
517 byte-array (car byte-array))))
518 (dbus-byte-array-to-string
519 (if (and (consp byte-array) (zerop (car (last byte-array))))
520 (butlast byte-array) byte-array))))
521
1efeec86
MA
522(defun tramp-gvfs-stringify-dbus-message (message)
523 "Convert a D-Bus message into readable UTF8 strings, used for traces."
524 (cond
525 ((and (consp message) (characterp (car message)))
3675b169 526 (format "%S" (tramp-gvfs-dbus-byte-array-to-string message)))
1efeec86
MA
527 ((consp message)
528 (mapcar 'tramp-gvfs-stringify-dbus-message message))
529 ((stringp message)
530 (format "%S" message))
531 (t message)))
532
eeb44655
MA
533(defmacro with-tramp-dbus-call-method
534 (vec synchronous bus service path interface method &rest args)
535 "Apply a D-Bus call on bus BUS.
536
537If SYNCHRONOUS is non-nil, the call is synchronously. Otherwise,
538it is an asynchronous call, with `ignore' as callback function.
539
540The other arguments have the same meaning as with `dbus-call-method'
541or `dbus-call-method-asynchronously'. Additionally, the call
542will be traced by Tramp with trace level 6."
543 `(let ((func (if ,synchronous
544 'dbus-call-method 'dbus-call-method-asynchronously))
545 (args (append (list ,bus ,service ,path ,interface ,method)
546 (if ,synchronous (list ,@args) (list 'ignore ,@args))))
547 result)
548 (tramp-message ,vec 6 "%s %s" func args)
549 (setq result (apply func args))
1efeec86 550 (tramp-message ,vec 6 "%s" (tramp-gvfs-stringify-dbus-message result))
eeb44655
MA
551 result))
552
553(put 'with-tramp-dbus-call-method 'lisp-indent-function 2)
554(put 'with-tramp-dbus-call-method 'edebug-form-spec '(form symbolp body))
6139f995
MA
555(tramp-compat-font-lock-add-keywords
556 'emacs-lisp-mode '("\\<with-tramp-dbus-call-method\\>"))
eeb44655
MA
557
558(defmacro with-tramp-gvfs-error-message (filename handler &rest args)
559 "Apply a Tramp GVFS `handler'.
560In case of an error, modify the error message by replacing
561`filename' with its GVFS mounted name."
562 `(let ((fuse-file-name (regexp-quote (tramp-gvfs-fuse-file-name ,filename)))
563 elt)
564 (condition-case err
0f34aa77 565 (tramp-compat-funcall ,handler ,@args)
eeb44655
MA
566 (error
567 (setq elt (cdr err))
568 (while elt
569 (when (and (stringp (car elt))
570 (string-match fuse-file-name (car elt)))
571 (setcar elt (replace-match ,filename t t (car elt))))
572 (setq elt (cdr elt)))
573 (signal (car err) (cdr err))))))
574
575(put 'with-tramp-gvfs-error-message 'lisp-indent-function 2)
576(put 'with-tramp-gvfs-error-message 'edebug-form-spec '(form symbolp body))
6139f995
MA
577(tramp-compat-font-lock-add-keywords
578 'emacs-lisp-mode '("\\<with-tramp-gvfs-error-message\\>"))
eeb44655
MA
579
580(defvar tramp-gvfs-dbus-event-vector nil
581 "Current Tramp file name to be used, as vector.
582It is needed when D-Bus signals or errors arrive, because there
583is no information where to trace the message.")
584
585(defun tramp-gvfs-dbus-event-error (event err)
7b1bf173 586 "Called when a D-Bus error message arrives, see `dbus-event-error-functions'."
d3e97185 587 (when tramp-gvfs-dbus-event-vector
d3e97185
MA
588 (tramp-message tramp-gvfs-dbus-event-vector 10 "%S" event)
589 (tramp-error tramp-gvfs-dbus-event-vector 'file-error "%s" (cadr err))))
eeb44655 590
5504e2c7
MA
591;; `dbus-event-error-hooks' has been renamed to `dbus-event-error-functions'.
592(add-hook
593 (if (boundp 'dbus-event-error-functions)
594 'dbus-event-error-functions 'dbus-event-error-hooks)
595 'tramp-gvfs-dbus-event-error)
eeb44655
MA
596
597\f
598;; File name primitives.
599
600(defun tramp-gvfs-handle-copy-file
632c5478 601 (filename newname &optional ok-if-already-exists keep-date
53b6a8b1 602 preserve-uid-gid preserve-extended-attributes)
eeb44655 603 "Like `copy-file' for Tramp files."
1efeec86
MA
604 (with-parsed-tramp-file-name
605 (if (tramp-tramp-file-p filename) filename newname) nil
3675b169
MA
606
607 (when (and (not ok-if-already-exists) (file-exists-p newname))
608 (tramp-error
609 v 'file-already-exists "File %s already exists" newname))
610
611 (if (or (and (tramp-tramp-file-p filename)
612 (not (tramp-gvfs-file-name-p filename)))
613 (and (tramp-tramp-file-p newname)
614 (not (tramp-gvfs-file-name-p newname))))
615
616 ;; We cannot copy directly.
617 (let ((tmpfile (tramp-compat-make-temp-file filename)))
618 (cond
619 (preserve-extended-attributes
620 (copy-file
621 filename tmpfile t keep-date preserve-uid-gid
622 preserve-extended-attributes))
623 (preserve-uid-gid
624 (copy-file filename tmpfile t keep-date preserve-uid-gid))
625 (t
626 (copy-file filename tmpfile t keep-date)))
627 (rename-file tmpfile newname ok-if-already-exists))
628
629 ;; Direct copy.
630 (with-tramp-progress-reporter
631 v 0 (format "Copying %s to %s" filename newname)
632 (unless
633 (let ((args
634 (append (if (or keep-date preserve-uid-gid)
635 (list "--preserve")
636 nil)
637 (list
638 (tramp-gvfs-url-file-name filename)
639 (tramp-gvfs-url-file-name newname)))))
640 (apply 'tramp-gvfs-send-command v "gvfs-copy" args))
641 ;; Propagate the error.
642 (with-current-buffer (tramp-get-connection-buffer v)
643 (goto-char (point-min))
644 (tramp-error-with-buffer
645 nil v 'file-error
646 "Copying failed, see buffer `%s' for details." (buffer-name)))))
647
648 (when (file-remote-p newname)
649 (with-parsed-tramp-file-name newname nil
650 (tramp-flush-file-property v (file-name-directory localname))
651 (tramp-flush-file-property v localname))))))
652
653(defun tramp-gvfs-handle-delete-directory (directory &optional recursive trash)
eeb44655 654 "Like `delete-directory' for Tramp files."
3675b169
MA
655 (when (and recursive (not (file-symlink-p directory)))
656 (mapc (lambda (file)
657 (if (eq t (car (file-attributes file)))
658 (tramp-compat-delete-directory file recursive trash)
659 (tramp-compat-delete-file file trash)))
660 (directory-files
661 directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")))
662 (with-parsed-tramp-file-name directory nil
663 (tramp-flush-file-property v (file-name-directory localname))
664 (tramp-flush-directory-property v localname)
665 (unless
666 (tramp-gvfs-send-command
667 v (if (and trash delete-by-moving-to-trash) "gvfs-trash" "gvfs-rm")
668 (tramp-gvfs-url-file-name directory))
669 ;; Propagate the error.
670 (with-current-buffer (tramp-get-connection-buffer v)
671 (goto-char (point-min))
672 (tramp-error-with-buffer
673 nil v 'file-error "Couldn't delete %s" directory)))))
eeb44655 674
eba082a2 675(defun tramp-gvfs-handle-delete-file (filename &optional trash)
eeb44655 676 "Like `delete-file' for Tramp files."
3675b169
MA
677 (with-parsed-tramp-file-name filename nil
678 (tramp-flush-file-property v (file-name-directory localname))
679 (tramp-flush-directory-property v localname)
680 (unless
681 (tramp-gvfs-send-command
682 v (if (and trash delete-by-moving-to-trash) "gvfs-trash" "gvfs-rm")
683 (tramp-gvfs-url-file-name filename))
684 ;; Propagate the error.
685 (with-current-buffer (tramp-get-connection-buffer v)
686 (goto-char (point-min))
687 (tramp-error-with-buffer
688 nil v 'file-error "Couldn't delete %s" filename)))))
eeb44655
MA
689
690(defun tramp-gvfs-handle-expand-file-name (name &optional dir)
691 "Like `expand-file-name' for Tramp files."
692 ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
693 (setq dir (or dir default-directory "/"))
694 ;; Unless NAME is absolute, concat DIR and NAME.
695 (unless (file-name-absolute-p name)
696 (setq name (concat (file-name-as-directory dir) name)))
697 ;; If NAME is not a Tramp file, run the real handler.
698 (if (not (tramp-tramp-file-p name))
699 (tramp-run-real-handler 'expand-file-name (list name nil))
700 ;; Dissect NAME.
701 (with-parsed-tramp-file-name name nil
4f201088
MA
702 ;; If there is a default location, expand tilde.
703 (when (string-match "\\`\\(~\\)\\(/\\|\\'\\)" localname)
704 (save-match-data
2fe4b125 705 (tramp-gvfs-maybe-open-connection (vector method user host "/" hop)))
4f201088
MA
706 (setq localname
707 (replace-match
708 (tramp-get-file-property v "/" "default-location" "~")
709 nil t localname 1)))
eeb44655
MA
710 ;; Tilde expansion is not possible.
711 (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
712 (tramp-error
713 v 'file-error
714 "Cannot expand tilde in file `%s'" name))
715 (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
716 (setq localname (concat "/" localname)))
717 ;; We do not pass "/..".
718 (if (string-equal "smb" method)
719 (when (string-match "^/[^/]+\\(/\\.\\./?\\)" localname)
720 (setq localname (replace-match "/" t t localname 1)))
721 (when (string-match "^/\\.\\./?" localname)
722 (setq localname (replace-match "/" t t localname))))
723 ;; There might be a double slash. Remove this.
724 (while (string-match "//" localname)
725 (setq localname (replace-match "/" t t localname)))
726 ;; No tilde characters in file name, do normal
727 ;; `expand-file-name' (this does "/./" and "/../").
728 (tramp-make-tramp-file-name
729 method user host
730 (tramp-run-real-handler
731 'expand-file-name (list localname))))))
732
733(defun tramp-gvfs-handle-file-attributes (filename &optional id-format)
734 "Like `file-attributes' for Tramp files."
3675b169
MA
735 (unless id-format (setq id-format 'integer))
736 ;; Don't modify `last-coding-system-used' by accident.
737 (let ((last-coding-system-used last-coding-system-used)
738 dirp res-symlink-target res-numlinks res-uid res-gid res-access
739 res-mod res-change res-size res-filemodes res-inode res-device)
740 (with-parsed-tramp-file-name filename nil
741 (with-tramp-file-property
742 v localname (format "file-attributes-%s" id-format)
743 (tramp-message v 5 "file attributes: %s" localname)
744 (tramp-gvfs-send-command
745 v "gvfs-info" (tramp-gvfs-url-file-name filename))
746 ;; Parse output ...
747 (with-current-buffer (tramp-get-connection-buffer v)
748 (goto-char (point-min))
749 (when (re-search-forward "attributes:" nil t)
750 ;; ... directory or symlink
751 (goto-char (point-min))
752 (setq dirp (if (re-search-forward "type:\\s-+directory" nil t) t))
753 (goto-char (point-min))
754 (setq res-symlink-target
755 (if (re-search-forward
756 "standard::symlink-target:\\s-+\\(\\S-+\\)" nil t)
757 (match-string 1)))
758 ;; ... number links
759 (goto-char (point-min))
760 (setq res-numlinks
761 (if (re-search-forward "unix::nlink:\\s-+\\([0-9]+\\)" nil t)
762 (string-to-number (match-string 1)) 0))
763 ;; ... uid and gid
764 (goto-char (point-min))
765 (setq res-uid
766 (or (if (eq id-format 'integer)
767 (if (re-search-forward
768 "unix::uid:\\s-+\\([0-9]+\\)" nil t)
769 (string-to-number (match-string 1)))
770 (if (re-search-forward
771 "owner::user:\\s-+\\(\\S-+\\)" nil t)
772 (match-string 1)))
773 (tramp-get-local-uid id-format)))
774 (setq res-gid
775 (or (if (eq id-format 'integer)
776 (if (re-search-forward
777 "unix::gid:\\s-+\\([0-9]+\\)" nil t)
778 (string-to-number (match-string 1)))
779 (if (re-search-forward
780 "owner::group:\\s-+\\(\\S-+\\)" nil t)
781 (match-string 1)))
782 (tramp-get-local-gid id-format)))
783 ;; ... last access, modification and change time
784 (goto-char (point-min))
785 (setq res-access
786 (if (re-search-forward
787 "time::access:\\s-+\\([0-9]+\\)" nil t)
788 (seconds-to-time (string-to-number (match-string 1)))
789 '(0 0)))
790 (goto-char (point-min))
791 (setq res-mod
792 (if (re-search-forward
793 "time::modified:\\s-+\\([0-9]+\\)" nil t)
794 (seconds-to-time (string-to-number (match-string 1)))
795 '(0 0)))
796 (goto-char (point-min))
797 (setq res-change
798 (if (re-search-forward
799 "time::changed:\\s-+\\([0-9]+\\)" nil t)
800 (seconds-to-time (string-to-number (match-string 1)))
801 '(0 0)))
802 ;; ... size
803 (goto-char (point-min))
804 (setq res-size
805 (if (re-search-forward
806 "standard::size:\\s-+\\([0-9]+\\)" nil t)
807 (string-to-number (match-string 1)) 0))
808 ;; ... file mode flags
809 (goto-char (point-min))
810 (setq res-filemodes
811 (if (re-search-forward "unix::mode:\\s-+\\([0-9]+\\)" nil t)
812 (tramp-file-mode-from-int (match-string 1))
813 (if dirp "drwx------" "-rwx------")))
814 ;; ... inode and device
815 (goto-char (point-min))
816 (setq res-inode
817 (if (re-search-forward "unix::inode:\\s-+\\([0-9]+\\)" nil t)
818 (string-to-number (match-string 1))
819 (tramp-get-inode v)))
820 (goto-char (point-min))
821 (setq res-device
822 (if (re-search-forward "unix::device:\\s-+\\([0-9]+\\)" nil t)
823 (string-to-number (match-string 1))
824 (tramp-get-device v)))
825
826 ;; Return data gathered.
827 (list
828 ;; 0. t for directory, string (name linked to) for
829 ;; symbolic link, or nil.
830 (or dirp res-symlink-target)
831 ;; 1. Number of links to file.
832 res-numlinks
833 ;; 2. File uid.
834 res-uid
835 ;; 3. File gid.
836 res-gid
837 ;; 4. Last access time, as a list of integers.
838 ;; 5. Last modification time, likewise.
839 ;; 6. Last status change time, likewise.
840 res-access res-mod res-change
841 ;; 7. Size in bytes (-1, if number is out of range).
842 res-size
843 ;; 8. File modes.
844 res-filemodes
845 ;; 9. t if file's gid would change if file were deleted
846 ;; and recreated.
847 nil
848 ;; 10. Inode number.
849 res-inode
850 ;; 11. Device number.
851 res-device
852 )))))))
eeb44655 853
0f34aa77
MA
854(defun tramp-gvfs-handle-file-directory-p (filename)
855 "Like `file-directory-p' for Tramp files."
3675b169 856 (eq t (car (file-attributes filename))))
0f34aa77 857
eeb44655
MA
858(defun tramp-gvfs-handle-file-executable-p (filename)
859 "Like `file-executable-p' for Tramp files."
3675b169
MA
860 (with-parsed-tramp-file-name filename nil
861 (with-tramp-file-property v localname "file-executable-p"
862 (tramp-check-cached-permissions v ?x))))
eeb44655
MA
863
864(defun tramp-gvfs-handle-file-local-copy (filename)
865 "Like `file-local-copy' for Tramp files."
866 (with-parsed-tramp-file-name filename nil
867 (let ((tmpfile (tramp-compat-make-temp-file filename)))
868 (unless (file-exists-p filename)
869 (tramp-error
870 v 'file-error
871 "Cannot make local copy of non-existing file `%s'" filename))
872 (copy-file filename tmpfile t t)
873 tmpfile)))
874
875(defun tramp-gvfs-handle-file-name-all-completions (filename directory)
876 "Like `file-name-all-completions' for Tramp files."
877 (unless (save-match-data (string-match "/" filename))
3675b169
MA
878 (with-parsed-tramp-file-name (expand-file-name directory) nil
879
880 (all-completions
881 filename
882 (mapcar
883 'list
884 (or
885 ;; Try cache entries for filename, filename with last
886 ;; character removed, filename with last two characters
887 ;; removed, ..., and finally the empty string - all
888 ;; concatenated to the local directory name.
889 (let ((remote-file-name-inhibit-cache
890 (or remote-file-name-inhibit-cache
891 tramp-completion-reread-directory-timeout)))
892
893 ;; This is inefficient for very long filenames, pity
894 ;; `reduce' is not available...
895 (car
896 (apply
897 'append
898 (mapcar
899 (lambda (x)
900 (let ((cache-hit
901 (tramp-get-file-property
902 v
903 (concat localname (substring filename 0 x))
904 "file-name-all-completions"
905 nil)))
906 (when cache-hit (list cache-hit))))
907 ;; We cannot use a length of 0, because file properties
908 ;; for "foo" and "foo/" are identical.
909 (tramp-compat-number-sequence (length filename) 1 -1)))))
910
911 ;; Cache expired or no matching cache entry found so we need
912 ;; to perform a remote operation.
913 (let ((result '("." ".."))
914 entry)
915 ;; Get a list of directories and files.
916 (tramp-gvfs-send-command
917 v "gvfs-ls" (tramp-gvfs-url-file-name directory))
918
919 ;; Now grab the output.
920 (with-temp-buffer
921 (insert-buffer-substring (tramp-get-connection-buffer v))
922 (goto-char (point-max))
923 (while (zerop (forward-line -1))
924 (setq entry (buffer-substring (point) (point-at-eol)))
925 (when (string-match filename entry)
926 (if (file-directory-p (expand-file-name entry directory))
927 (push (concat entry "/") result)
928 (push entry result)))))
929
930 ;; Because the remote op went through OK we know the
931 ;; directory we `cd'-ed to exists.
932 (tramp-set-file-property v localname "file-exists-p" t)
933
934 ;; Because the remote op went through OK we know every
935 ;; file listed by `ls' exists.
936 (mapc (lambda (entry)
937 (tramp-set-file-property
938 v (concat localname entry) "file-exists-p" t))
939 result)
940
941 ;; Store result in the cache.
942 (tramp-set-file-property
943 v (concat localname filename)
944 "file-name-all-completions" result))))))))
eeb44655
MA
945
946(defun tramp-gvfs-handle-file-readable-p (filename)
947 "Like `file-readable-p' for Tramp files."
3675b169
MA
948 (with-parsed-tramp-file-name filename nil
949 (with-tramp-file-property v localname "file-executable-p"
950 (tramp-check-cached-permissions v ?r))))
632c5478 951
eeb44655
MA
952(defun tramp-gvfs-handle-file-writable-p (filename)
953 "Like `file-writable-p' for Tramp files."
3675b169
MA
954 (with-parsed-tramp-file-name filename nil
955 (with-tramp-file-property v localname "file-writable-p"
956 (if (file-exists-p filename)
957 (tramp-check-cached-permissions v ?w)
958 ;; If file doesn't exist, check if directory is writable.
959 (and (file-directory-p (file-name-directory filename))
960 (file-writable-p (file-name-directory filename)))))))
eeb44655
MA
961
962(defun tramp-gvfs-handle-insert-directory
963 (filename switches &optional wildcard full-directory-p)
964 "Like `insert-directory' for Tramp files."
3675b169
MA
965 ;; gvfs-* output is hard to parse. So we let `ls-lisp' do the job.
966 (with-parsed-tramp-file-name (expand-file-name filename) nil
967 (with-tramp-progress-reporter v 0 (format "Opening directory %s" filename)
968 (require 'ls-lisp)
969 (let (ls-lisp-use-insert-directory-program)
970 (tramp-run-real-handler
971 'insert-directory
972 (list filename switches wildcard full-directory-p))))))
eeb44655
MA
973
974(defun tramp-gvfs-handle-insert-file-contents
975 (filename &optional visit beg end replace)
976 "Like `insert-file-contents' for Tramp files."
3675b169
MA
977 (barf-if-buffer-read-only)
978 (setq filename (expand-file-name filename))
979 (let (tmpfile result)
980 (unwind-protect
981 (if (not (file-exists-p filename))
982 ;; We don't raise a Tramp error, because it might be
983 ;; suppressed, like in `find-file-noselect-1'.
984 (signal 'file-error (list "File not found on remote host" filename))
985
986 (setq tmpfile (file-local-copy filename)
987 result (insert-file-contents tmpfile visit beg end replace)))
988 ;; Save exit.
989 (when visit
990 (setq buffer-file-name filename)
991 (setq buffer-read-only (not (file-writable-p filename)))
992 (set-visited-file-modtime)
993 (set-buffer-modified-p nil))
994 (when (stringp tmpfile)
995 (delete-file tmpfile)))
996
997 ;; Result.
998 (list filename (cadr result))))
eeb44655
MA
999
1000(defun tramp-gvfs-handle-make-directory (dir &optional parents)
1001 "Like `make-directory' for Tramp files."
1efeec86 1002 (with-parsed-tramp-file-name dir nil
3675b169
MA
1003 (unless
1004 (apply
1005 'tramp-gvfs-send-command v "gvfs-mkdir"
1006 (if parents
1007 (list "-p" (tramp-gvfs-url-file-name dir))
1008 (list (tramp-gvfs-url-file-name dir))))
1009 ;; Propagate the error.
1010 (tramp-error v 'file-error "Couldn't make directory %s" dir))))
99278f8a 1011
eeb44655
MA
1012(defun tramp-gvfs-handle-rename-file
1013 (filename newname &optional ok-if-already-exists)
1014 "Like `rename-file' for Tramp files."
1efeec86
MA
1015 (with-parsed-tramp-file-name
1016 (if (tramp-tramp-file-p filename) filename newname) nil
1efeec86 1017
3675b169
MA
1018 (when (and (not ok-if-already-exists) (file-exists-p newname))
1019 (tramp-error
1020 v 'file-already-exists "File %s already exists" newname))
eeb44655 1021
3675b169
MA
1022 (if (or (and (tramp-tramp-file-p filename)
1023 (not (tramp-gvfs-file-name-p filename)))
1024 (and (tramp-tramp-file-p newname)
1025 (not (tramp-gvfs-file-name-p newname))))
53b6a8b1 1026
3675b169
MA
1027 ;; We cannot move directly.
1028 (let ((tmpfile (tramp-compat-make-temp-file filename)))
1029 (rename-file filename tmpfile t)
1030 (rename-file tmpfile newname ok-if-already-exists))
eeb44655 1031
3675b169
MA
1032 ;; Direct move.
1033 (with-tramp-progress-reporter
1034 v 0 (format "Renaming %s to %s" filename newname)
1035 (unless
1036 (tramp-gvfs-send-command
1037 v "gvfs-move"
1038 (tramp-gvfs-url-file-name filename)
1039 (tramp-gvfs-url-file-name newname))
1040 ;; Propagate the error.
1041 (with-current-buffer (tramp-get-buffer v)
1042 (goto-char (point-min))
1043 (tramp-error-with-buffer
1044 nil v 'file-error
1045 "Renaming failed, see buffer `%s' for details." (buffer-name)))))
1046
1047 (when (file-remote-p filename)
1048 (with-parsed-tramp-file-name filename nil
1049 (tramp-flush-file-property v (file-name-directory localname))
1050 (tramp-flush-file-property v localname)))
1051
1052 (when (file-remote-p newname)
1053 (with-parsed-tramp-file-name newname nil
1054 (tramp-flush-file-property v (file-name-directory localname))
1055 (tramp-flush-file-property v localname))))))
632c5478 1056
eeb44655
MA
1057(defun tramp-gvfs-handle-set-visited-file-modtime (&optional time-list)
1058 "Like `set-visited-file-modtime' for Tramp files."
3675b169
MA
1059 (unless (buffer-file-name)
1060 (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file"
1061 (buffer-name)))
1062 (unless time-list
1063 (let ((f (buffer-file-name)))
1064 (with-parsed-tramp-file-name f nil
1065 (let ((remote-file-name-inhibit-cache t)
1066 (attr (file-attributes f)))
1067 ;; '(-1 65535) means file doesn't exists yet.
1068 (setq time-list (or (nth 5 attr) '(-1 65535)))))))
1069 ;; We use '(0 0) as a don't-know value.
1070 (unless (not (equal time-list '(0 0)))
1071 (tramp-run-real-handler 'set-visited-file-modtime (list time-list))))
eeb44655
MA
1072
1073(defun tramp-gvfs-handle-write-region
1074 (start end filename &optional append visit lockname confirm)
1075 "Like `write-region' for Tramp files."
1076 (with-parsed-tramp-file-name filename nil
3675b169
MA
1077 ;; XEmacs takes a coding system as the seventh argument, not `confirm'.
1078 (when (and (not (featurep 'xemacs)) confirm (file-exists-p filename))
1079 (unless (y-or-n-p (format "File %s exists; overwrite anyway? " filename))
1080 (tramp-error v 'file-error "File not overwritten")))
1081
1082 (let ((tmpfile (tramp-compat-make-temp-file filename)))
1083 (write-region start end tmpfile)
1084 (condition-case nil
1085 (rename-file tmpfile filename)
1086 (error
1087 (delete-file tmpfile)
1088 (tramp-error
1089 v 'file-error "Couldn't write region to `%s'" filename))))
1090
1091 (tramp-flush-file-property v (file-name-directory localname))
1092 (tramp-flush-file-property v localname)
eeb44655 1093
303ffde8
MA
1094 ;; Set file modification time.
1095 (when (or (eq visit t) (stringp visit))
1096 (set-visited-file-modtime (nth 5 (file-attributes filename))))
1097
eeb44655
MA
1098 ;; The end.
1099 (when (or (eq visit t) (null visit) (stringp visit))
1100 (tramp-message v 0 "Wrote %s" filename))
1101 (run-hooks 'tramp-handle-write-region-hook)))
1102
1103\f
1104;; File name conversions.
1105
1106(defun tramp-gvfs-url-file-name (filename)
1107 "Return FILENAME in URL syntax."
1efeec86 1108 ;; "/" must NOT be hexlified.
3675b169
MA
1109 (let ((url-unreserved-chars (append '(?/) url-unreserved-chars))
1110 result)
1111 (setq
1112 result
1113 (url-recreate-url
1114 (if (tramp-tramp-file-p filename)
1115 (with-parsed-tramp-file-name filename nil
1116 (when (and user (string-match tramp-user-with-domain-regexp user))
1117 (setq user
1118 (concat (match-string 2 user) ";" (match-string 1 user))))
1119 (url-parse-make-urlobj
1120 method (url-hexify-string user) nil
1121 (tramp-file-name-real-host v) (tramp-file-name-port v)
1122 (url-hexify-string localname) nil nil t))
1123 (url-parse-make-urlobj
1124 "file" nil nil nil nil
1125 (url-hexify-string (file-truename filename)) nil nil t))))
1126 (when (tramp-tramp-file-p filename)
1127 (with-parsed-tramp-file-name filename nil
1128 (tramp-message v 10 "remote file `%s' is URL `%s'" filename result)))
1129 result))
eeb44655
MA
1130
1131(defun tramp-gvfs-object-path (filename)
1132 "Create a D-Bus object path from FILENAME."
1133 (expand-file-name (dbus-escape-as-identifier filename) tramp-gvfs-path-tramp))
1134
1135(defun tramp-gvfs-file-name (object-path)
1136 "Retrieve file name from D-Bus OBJECT-PATH."
1137 (dbus-unescape-from-identifier
1138 (replace-regexp-in-string "^.*/\\([^/]+\\)$" "\\1" object-path)))
1139
1140(defun tramp-gvfs-fuse-file-name (filename)
1141 "Return FUSE file name, which is directly accessible."
1142 (with-parsed-tramp-file-name (expand-file-name filename) nil
1143 (tramp-gvfs-maybe-open-connection v)
1efeec86
MA
1144 (let ((prefix (tramp-get-file-property v "/" "prefix" ""))
1145 (fuse-mountpoint
eeb44655
MA
1146 (tramp-get-file-property v "/" "fuse-mountpoint" nil)))
1147 (unless fuse-mountpoint
1148 (tramp-error
1149 v 'file-error "There is no FUSE mount point for `%s'" filename))
1efeec86
MA
1150 ;; We must hide the prefix, if any.
1151 (when (string-match (concat "^" (regexp-quote prefix)) localname)
eeb44655 1152 (setq localname (replace-match "" t t localname)))
1efeec86
MA
1153 (tramp-message
1154 v 10 "remote file `%s' is local file `%s'"
1155 filename (concat fuse-mountpoint localname))
1156 (concat fuse-mountpoint localname))))
eeb44655
MA
1157
1158(defun tramp-bluez-address (device)
1159 "Return bluetooth device address from a given bluetooth DEVICE name."
1160 (when (stringp device)
1161 (if (string-match tramp-ipv6-regexp device)
1162 (match-string 0 device)
1163 (cadr (assoc device (tramp-bluez-list-devices))))))
1164
1165(defun tramp-bluez-device (address)
1166 "Return bluetooth device name from a given bluetooth device ADDRESS.
1167ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
1168 (when (stringp address)
1169 (while (string-match "[][]" address)
1170 (setq address (replace-match "" t t address)))
1171 (let (result)
1172 (dolist (item (tramp-bluez-list-devices) result)
1173 (when (string-match address (cadr item))
1174 (setq result (car item)))))))
1175
1176\f
1177;; D-Bus GVFS functions.
1178
1179(defun tramp-gvfs-handler-askpassword (message user domain flags)
1180 "Implementation for the \"org.gtk.vfs.MountOperation.askPassword\" method."
1181 (let* ((filename
1182 (tramp-gvfs-file-name (dbus-event-path-name last-input-event)))
1183 (pw-prompt
1184 (format
1185 "%s for %s "
1186 (if (string-match "\\([pP]assword\\|[pP]assphrase\\)" message)
1187 (capitalize (match-string 1 message))
1188 "Password")
1189 filename))
1190 password)
1191
1192 (condition-case nil
1193 (with-parsed-tramp-file-name filename l
1194 (when (and (zerop (length user))
1195 (not
1196 (zerop (logand flags tramp-gvfs-password-need-username))))
1197 (setq user (read-string "User name: ")))
1198 (when (and (zerop (length domain))
1199 (not (zerop (logand flags tramp-gvfs-password-need-domain))))
1200 (setq domain (read-string "Domain name: ")))
1201
1202 (tramp-message l 6 "%S %S %S %d" message user domain flags)
1203 (setq tramp-current-method l-method
1204 tramp-current-user user
1205 tramp-current-host l-host
1206 password (tramp-read-passwd
1207 (tramp-get-connection-process l) pw-prompt))
1208
1209 ;; Return result.
1210 (if (stringp password)
1211 (list
1212 t ;; password handled.
1213 nil ;; no abort of D-Bus.
1214 password
1215 (tramp-file-name-real-user l)
1216 domain
1217 nil ;; not anonymous.
1218 0) ;; no password save.
1219 ;; No password provided.
1220 (list nil t "" (tramp-file-name-real-user l) domain nil 0)))
1221
1222 ;; When QUIT is raised, we shall return this information to D-Bus.
1223 (quit (list nil t "" "" "" nil 0)))))
1224
1225(defun tramp-gvfs-handler-askquestion (message choices)
1226 "Implementation for the \"org.gtk.vfs.MountOperation.askQuestion\" method."
1227 (save-window-excursion
1228 (let ((enable-recursive-minibuffers t)
1229 choice)
1230
1231 (condition-case nil
1232 (with-parsed-tramp-file-name
1233 (tramp-gvfs-file-name (dbus-event-path-name last-input-event)) nil
1234 (tramp-message v 6 "%S %S" message choices)
1235
1236 ;; In theory, there can be several choices. Until now,
1237 ;; there is only the question whether to accept an unknown
1238 ;; host signature.
1239 (with-temp-buffer
9e021389 1240 ;; Preserve message for `progress-reporter'.
6139f995 1241 (tramp-compat-with-temp-message ""
9e021389
MA
1242 (insert message)
1243 (pop-to-buffer (current-buffer))
1244 (setq choice (if (yes-or-no-p (concat (car choices) " ")) 0 1))
1245 (tramp-message v 6 "%d" choice)))
eeb44655 1246
1efeec86
MA
1247 ;; When the choice is "no", we set a dummy fuse-mountpoint
1248 ;; in order to leave the timeout.
eeb44655 1249 (unless (zerop choice)
1efeec86 1250 (tramp-set-file-property v "/" "fuse-mountpoint" "/"))
eeb44655
MA
1251
1252 (list
1253 t ;; handled.
1254 nil ;; no abort of D-Bus.
1255 choice))
1256
9e021389
MA
1257 ;; When QUIT is raised, we shall return this information to D-Bus.
1258 (quit (list nil t 0))))))
eeb44655
MA
1259
1260(defun tramp-gvfs-handler-mounted-unmounted (mount-info)
1261 "Signal handler for the \"org.gtk.vfs.MountTracker.mounted\" and
1262\"org.gtk.vfs.MountTracker.unmounted\" signals."
1263 (ignore-errors
4f201088
MA
1264 (let ((signal-name (dbus-event-member-name last-input-event))
1265 (elt mount-info))
1266 ;; Jump over the first elements of the mount info. Since there
e4920bc9 1267 ;; were changes in the entries, we cannot access dedicated
4f201088
MA
1268 ;; elements.
1269 (while (stringp (car elt)) (setq elt (cdr elt)))
3675b169 1270 (let* ((fuse-mountpoint (tramp-gvfs-dbus-byte-array-to-string (cadr elt)))
4f201088 1271 (mount-spec (caddr elt))
3675b169
MA
1272 (default-location (tramp-gvfs-dbus-byte-array-to-string
1273 (cadddr elt)))
1274 (method (tramp-gvfs-dbus-byte-array-to-string
4f201088 1275 (cadr (assoc "type" (cadr mount-spec)))))
3675b169 1276 (user (tramp-gvfs-dbus-byte-array-to-string
4f201088 1277 (cadr (assoc "user" (cadr mount-spec)))))
3675b169 1278 (domain (tramp-gvfs-dbus-byte-array-to-string
4f201088 1279 (cadr (assoc "domain" (cadr mount-spec)))))
3675b169 1280 (host (tramp-gvfs-dbus-byte-array-to-string
4f201088
MA
1281 (cadr (or (assoc "host" (cadr mount-spec))
1282 (assoc "server" (cadr mount-spec))))))
3675b169 1283 (port (tramp-gvfs-dbus-byte-array-to-string
4f201088 1284 (cadr (assoc "port" (cadr mount-spec)))))
3675b169 1285 (ssl (tramp-gvfs-dbus-byte-array-to-string
4f201088 1286 (cadr (assoc "ssl" (cadr mount-spec)))))
3675b169
MA
1287 (prefix (concat (tramp-gvfs-dbus-byte-array-to-string
1288 (car mount-spec))
1289 (tramp-gvfs-dbus-byte-array-to-string
4f201088
MA
1290 (cadr (assoc "share" (cadr mount-spec)))))))
1291 (when (string-match "^smb" method)
1292 (setq method "smb"))
1293 (when (string-equal "obex" method)
1294 (setq host (tramp-bluez-device host)))
1295 (when (and (string-equal "dav" method) (string-equal "true" ssl))
1296 (setq method "davs"))
1297 (unless (zerop (length domain))
1298 (setq user (concat user tramp-prefix-domain-format domain)))
1299 (unless (zerop (length port))
1300 (setq host (concat host tramp-prefix-port-format port)))
1301 (with-parsed-tramp-file-name
1302 (tramp-make-tramp-file-name method user host "") nil
1303 (tramp-message
1304 v 6 "%s %s"
1305 signal-name (tramp-gvfs-stringify-dbus-message mount-info))
1306 (tramp-set-file-property v "/" "list-mounts" 'undef)
3675b169 1307 (if (string-equal (downcase signal-name) "unmounted")
4f201088
MA
1308 (tramp-set-file-property v "/" "fuse-mountpoint" nil)
1309 ;; Set prefix, mountpoint and location.
1310 (unless (string-equal prefix "/")
1311 (tramp-set-file-property v "/" "prefix" prefix))
1312 (tramp-set-file-property v "/" "fuse-mountpoint" fuse-mountpoint)
1313 (tramp-set-file-property
1314 v "/" "default-location" default-location)))))))
eeb44655 1315
91aafa16
MA
1316(when tramp-gvfs-enabled
1317 (dbus-register-signal
1318 :session nil tramp-gvfs-path-mounttracker
1319 tramp-gvfs-interface-mounttracker "mounted"
1320 'tramp-gvfs-handler-mounted-unmounted)
1321 (dbus-register-signal
1322 :session nil tramp-gvfs-path-mounttracker
1323 tramp-gvfs-interface-mounttracker "Mounted"
1324 'tramp-gvfs-handler-mounted-unmounted)
1325
1326 (dbus-register-signal
1327 :session nil tramp-gvfs-path-mounttracker
1328 tramp-gvfs-interface-mounttracker "unmounted"
1329 'tramp-gvfs-handler-mounted-unmounted)
1330 (dbus-register-signal
1331 :session nil tramp-gvfs-path-mounttracker
1332 tramp-gvfs-interface-mounttracker "Unmounted"
1333 'tramp-gvfs-handler-mounted-unmounted))
eeb44655
MA
1334
1335(defun tramp-gvfs-connection-mounted-p (vec)
1336 "Check, whether the location is already mounted."
1efeec86
MA
1337 (or
1338 (tramp-get-file-property vec "/" "fuse-mountpoint" nil)
1339 (catch 'mounted
1340 (dolist
1341 (elt
1d51f99c 1342 (with-tramp-file-property vec "/" "list-mounts"
1efeec86
MA
1343 (with-tramp-dbus-call-method vec t
1344 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
3675b169 1345 tramp-gvfs-interface-mounttracker tramp-gvfs-listmounts))
1efeec86 1346 nil)
4f201088 1347 ;; Jump over the first elements of the mount info. Since there
e4920bc9 1348 ;; were changes in the entries, we cannot access dedicated
4f201088
MA
1349 ;; elements.
1350 (while (stringp (car elt)) (setq elt (cdr elt)))
3675b169
MA
1351 (let* ((fuse-mountpoint (tramp-gvfs-dbus-byte-array-to-string
1352 (cadr elt)))
4f201088 1353 (mount-spec (caddr elt))
3675b169
MA
1354 (default-location (tramp-gvfs-dbus-byte-array-to-string
1355 (cadddr elt)))
1356 (method (tramp-gvfs-dbus-byte-array-to-string
4f201088 1357 (cadr (assoc "type" (cadr mount-spec)))))
3675b169 1358 (user (tramp-gvfs-dbus-byte-array-to-string
4f201088 1359 (cadr (assoc "user" (cadr mount-spec)))))
3675b169 1360 (domain (tramp-gvfs-dbus-byte-array-to-string
4f201088 1361 (cadr (assoc "domain" (cadr mount-spec)))))
3675b169 1362 (host (tramp-gvfs-dbus-byte-array-to-string
4f201088
MA
1363 (cadr (or (assoc "host" (cadr mount-spec))
1364 (assoc "server" (cadr mount-spec))))))
3675b169 1365 (port (tramp-gvfs-dbus-byte-array-to-string
4f201088 1366 (cadr (assoc "port" (cadr mount-spec)))))
3675b169 1367 (ssl (tramp-gvfs-dbus-byte-array-to-string
4f201088 1368 (cadr (assoc "ssl" (cadr mount-spec)))))
3675b169
MA
1369 (prefix (concat (tramp-gvfs-dbus-byte-array-to-string
1370 (car mount-spec))
1371 (tramp-gvfs-dbus-byte-array-to-string
4f201088 1372 (cadr (assoc "share" (cadr mount-spec)))))))
1efeec86
MA
1373 (when (string-match "^smb" method)
1374 (setq method "smb"))
1375 (when (string-equal "obex" method)
1376 (setq host (tramp-bluez-device host)))
1377 (when (and (string-equal "dav" method) (string-equal "true" ssl))
1378 (setq method "davs"))
1379 (when (and (string-equal "synce" method) (zerop (length user)))
1380 (setq user (or (tramp-file-name-user vec) "")))
1381 (unless (zerop (length domain))
1382 (setq user (concat user tramp-prefix-domain-format domain)))
1383 (unless (zerop (length port))
1384 (setq host (concat host tramp-prefix-port-format port)))
1385 (when (and
1386 (string-equal method (tramp-file-name-method vec))
1387 (string-equal user (or (tramp-file-name-user vec) ""))
1388 (string-equal host (tramp-file-name-host vec))
1389 (string-match (concat "^" (regexp-quote prefix))
1390 (tramp-file-name-localname vec)))
4f201088 1391 ;; Set prefix, mountpoint and location.
1efeec86
MA
1392 (unless (string-equal prefix "/")
1393 (tramp-set-file-property vec "/" "prefix" prefix))
4f201088
MA
1394 (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
1395 (tramp-set-file-property vec "/" "default-location" default-location)
1efeec86 1396 (throw 'mounted t)))))))
eeb44655 1397
3675b169
MA
1398(defun tramp-gvfs-mount-spec-entry (key value)
1399 "Construct a mount-spec entry to be used in a mount_spec.
1400It was \"a(say)\", but has changed to \"a{sv})\"."
1401 (if (string-match "^(aya{sv})" tramp-gvfs-mountlocation-signature)
1402 (list :dict-entry key
1403 (list :variant (tramp-gvfs-dbus-string-to-byte-array value)))
1404 (list :struct key (tramp-gvfs-dbus-string-to-byte-array value))))
1405
eeb44655
MA
1406(defun tramp-gvfs-mount-spec (vec)
1407 "Return a mount-spec for \"org.gtk.vfs.MountTracker.mountLocation\"."
1408 (let* ((method (tramp-file-name-method vec))
1409 (user (tramp-file-name-real-user vec))
1410 (domain (tramp-file-name-domain vec))
1411 (host (tramp-file-name-real-host vec))
1412 (port (tramp-file-name-port vec))
1413 (localname (tramp-file-name-localname vec))
1414 (ssl (if (string-match "^davs" method) "true" "false"))
1efeec86
MA
1415 (mount-spec '(:array))
1416 (mount-pref "/"))
eeb44655
MA
1417
1418 (setq
1419 mount-spec
1420 (append
1421 mount-spec
1422 (cond
1423 ((string-equal "smb" method)
1424 (string-match "^/?\\([^/]+\\)" localname)
3675b169
MA
1425 (list (tramp-gvfs-mount-spec-entry "type" "smb-share")
1426 (tramp-gvfs-mount-spec-entry "server" host)
1427 (tramp-gvfs-mount-spec-entry "share" (match-string 1 localname))))
eeb44655 1428 ((string-equal "obex" method)
3675b169
MA
1429 (list (tramp-gvfs-mount-spec-entry "type" method)
1430 (tramp-gvfs-mount-spec-entry
1431 "host" (concat "[" (tramp-bluez-address host) "]"))))
eeb44655 1432 ((string-match "^dav" method)
3675b169
MA
1433 (list (tramp-gvfs-mount-spec-entry "type" "dav")
1434 (tramp-gvfs-mount-spec-entry "host" host)
1435 (tramp-gvfs-mount-spec-entry "ssl" ssl)))
eeb44655 1436 (t
3675b169
MA
1437 (list (tramp-gvfs-mount-spec-entry "type" method)
1438 (tramp-gvfs-mount-spec-entry "host" host))))))
eeb44655
MA
1439
1440 (when user
1441 (add-to-list
3675b169 1442 'mount-spec (tramp-gvfs-mount-spec-entry "user" user) 'append))
eeb44655
MA
1443
1444 (when domain
1445 (add-to-list
3675b169 1446 'mount-spec (tramp-gvfs-mount-spec-entry "domain" domain) 'append))
eeb44655
MA
1447
1448 (when port
1449 (add-to-list
3675b169 1450 'mount-spec (tramp-gvfs-mount-spec-entry "port" (number-to-string port))
eeb44655
MA
1451 'append))
1452
1efeec86
MA
1453 (when (and (string-match "^dav" method)
1454 (string-match "^/?[^/]+" localname))
1455 (setq mount-pref (match-string 0 localname)))
1456
eeb44655 1457 ;; Return.
3675b169 1458 `(:struct ,(tramp-gvfs-dbus-string-to-byte-array mount-pref) ,mount-spec)))
eeb44655
MA
1459
1460\f
91aafa16 1461;; Connection functions.
eeb44655
MA
1462
1463(defun tramp-gvfs-maybe-open-connection (vec)
1464 "Maybe open a connection VEC.
1465Does not do anything if a connection is already open, but re-opens the
1466connection if a previous connection has died for some reason."
1467
1468 ;; We set the file name, in case there are incoming D-Bus signals or
1469 ;; D-Bus errors.
1470 (setq tramp-gvfs-dbus-event-vector vec)
1471
1472 ;; For password handling, we need a process bound to the connection
1473 ;; buffer. Therefore, we create a dummy process. Maybe there is a
1474 ;; better solution?
3675b169 1475 (unless (get-buffer-process (tramp-get-connection-buffer vec))
eeb44655
MA
1476 (let ((p (make-network-process
1477 :name (tramp-buffer-name vec)
3675b169 1478 :buffer (tramp-get-connection-buffer vec)
eeb44655 1479 :server t :host 'local :service t)))
bd8fadca 1480 (tramp-compat-set-process-query-on-exit-flag p nil)))
eeb44655
MA
1481
1482 (unless (tramp-gvfs-connection-mounted-p vec)
1483 (let* ((method (tramp-file-name-method vec))
1484 (user (tramp-file-name-user vec))
1485 (host (tramp-file-name-host vec))
3675b169 1486 (localname (tramp-file-name-localname vec))
eeb44655
MA
1487 (object-path
1488 (tramp-gvfs-object-path
1489 (tramp-make-tramp-file-name method user host ""))))
1490
3675b169
MA
1491 (when (and (string-equal method "smb")
1492 (string-equal localname "/"))
1493 (tramp-error vec 'file-error "Filename must contain a Windows share"))
1494
1d51f99c 1495 (with-tramp-progress-reporter
655bded0
MA
1496 vec 3
1497 (if (zerop (length user))
1498 (format "Opening connection for %s using %s" host method)
1499 (format "Opening connection for %s@%s using %s" user host method))
1500
22bcf204 1501 ;; Enable auth-source and password-cache.
655bded0
MA
1502 (tramp-set-connection-property vec "first-password-request" t)
1503
2c68ca0e 1504 ;; There will be a callback of "askPassword" when a password is
655bded0
MA
1505 ;; needed.
1506 (dbus-register-method
1507 :session dbus-service-emacs object-path
1508 tramp-gvfs-interface-mountoperation "askPassword"
1509 'tramp-gvfs-handler-askpassword)
3675b169
MA
1510 (dbus-register-method
1511 :session dbus-service-emacs object-path
1512 tramp-gvfs-interface-mountoperation "AskPassword"
1513 'tramp-gvfs-handler-askpassword)
655bded0 1514
2c68ca0e 1515 ;; There could be a callback of "askQuestion" when adding fingerprint.
655bded0
MA
1516 (dbus-register-method
1517 :session dbus-service-emacs object-path
1518 tramp-gvfs-interface-mountoperation "askQuestion"
1519 'tramp-gvfs-handler-askquestion)
3675b169
MA
1520 (dbus-register-method
1521 :session dbus-service-emacs object-path
1522 tramp-gvfs-interface-mountoperation "AskQuestion"
1523 'tramp-gvfs-handler-askquestion)
655bded0
MA
1524
1525 ;; The call must be asynchronously, because of the "askPassword"
1526 ;; or "askQuestion"callbacks.
3675b169
MA
1527 (if (string-match "(so)$" tramp-gvfs-mountlocation-signature)
1528 (with-tramp-dbus-call-method vec nil
1529 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
1530 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation
1531 (tramp-gvfs-mount-spec vec)
1532 `(:struct :string ,(dbus-get-unique-name :session)
1533 :object-path ,object-path))
1534 (with-tramp-dbus-call-method vec nil
1535 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
1536 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation
1537 (tramp-gvfs-mount-spec vec)
1538 :string (dbus-get-unique-name :session) :object-path object-path))
655bded0
MA
1539
1540 ;; We must wait, until the mount is applied. This will be
1541 ;; indicated by the "mounted" signal, i.e. the "fuse-mountpoint"
1542 ;; file property.
1543 (with-timeout
88f6a933
MA
1544 ((or (tramp-get-method-parameter method 'tramp-connection-timeout)
1545 tramp-connection-timeout)
655bded0
MA
1546 (if (zerop (length (tramp-file-name-user vec)))
1547 (tramp-error
1548 vec 'file-error
1549 "Timeout reached mounting %s using %s" host method)
eeb44655
MA
1550 (tramp-error
1551 vec 'file-error
655bded0
MA
1552 "Timeout reached mounting %s@%s using %s" user host method)))
1553 (while (not (tramp-get-file-property vec "/" "fuse-mountpoint" nil))
1554 (read-event nil nil 0.1)))
1555
1efeec86
MA
1556 ;; If `tramp-gvfs-handler-askquestion' has returned "No", it
1557 ;; is marked with the fuse-mountpoint "/". We shall react.
1558 (when (string-equal
1559 (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/")
1560 (tramp-error vec 'file-error "FUSE mount denied"))
1561
3675b169
MA
1562 ;; In `tramp-check-cached-permissions', the connection
1563 ;; properties {uig,gid}-{integer,string} are used. We set
1564 ;; them to their local counterparts.
1565 (tramp-set-connection-property
1566 vec "uid-integer" (tramp-get-local-uid 'integer))
1567 (tramp-set-connection-property
1568 vec "gid-integer" (tramp-get-local-gid 'integer))
1569 (tramp-set-connection-property
1570 vec "uid-string" (tramp-get-local-uid 'string))
1571 (tramp-set-connection-property
1572 vec "gid-string" (tramp-get-local-gid 'string))))))
eeb44655 1573
1efeec86
MA
1574(defun tramp-gvfs-send-command (vec command &rest args)
1575 "Send the COMMAND with its ARGS to connection VEC.
1576COMMAND is usually a command from the gvfs-* utilities.
3675b169 1577`call-process' is applied, and it returns `t' if the return code is zero."
1efeec86 1578 (let (result)
3675b169
MA
1579 (with-current-buffer (tramp-get-connection-buffer vec)
1580 (tramp-gvfs-maybe-open-connection vec)
1efeec86
MA
1581 (erase-buffer)
1582 (tramp-message vec 6 "%s %s" command (mapconcat 'identity args " "))
d0853629 1583 (setq result (apply 'tramp-call-process command nil t nil args))
3675b169
MA
1584 (tramp-message vec 6 "\n%s" (buffer-string))
1585 (zerop result))))
1efeec86 1586
eeb44655
MA
1587\f
1588;; D-Bus BLUEZ functions.
1589
1590(defun tramp-bluez-list-devices ()
99278f8a 1591 "Return all discovered bluetooth devices as list.
eeb44655
MA
1592Every entry is a list (NAME ADDRESS).
1593
1594If `tramp-bluez-discover-devices-timeout' is an integer, and the last
1595discovery happened more time before indicated there, a rescan will be
1596started, which lasts some ten seconds. Otherwise, cached results will
1597be used."
1598 ;; Reset the scanned devices list if time has passed.
1599 (and (integerp tramp-bluez-discover-devices-timeout)
1600 (integerp tramp-bluez-discovery)
1601 (> (tramp-time-diff (current-time) tramp-bluez-discovery)
1602 tramp-bluez-discover-devices-timeout)
1603 (setq tramp-bluez-devices nil))
1604
1605 ;; Rescan if needed.
1606 (unless tramp-bluez-devices
1607 (let ((object-path
1608 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
1609 :system tramp-bluez-service "/"
1610 tramp-bluez-interface-manager "DefaultAdapter")))
1611 (setq tramp-bluez-devices nil
1612 tramp-bluez-discovery t)
1613 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector nil
1614 :system tramp-bluez-service object-path
1615 tramp-bluez-interface-adapter "StartDiscovery")
1616 (while tramp-bluez-discovery
1617 (read-event nil nil 0.1))))
1618 (setq tramp-bluez-discovery (current-time))
1619 (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-bluez-devices)
1620 tramp-bluez-devices)
1621
1622(defun tramp-bluez-property-changed (property value)
1623 "Signal handler for the \"org.bluez.Adapter.PropertyChanged\" signal."
1624 (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" property value)
1625 (cond
1626 ((string-equal property "Discovering")
1627 (unless (car value)
1628 ;; "Discovering" FALSE means discovery run has been completed.
1629 ;; We stop it, because we don't need another run.
1630 (setq tramp-bluez-discovery nil)
1631 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
1632 :system tramp-bluez-service (dbus-event-path-name last-input-event)
1633 tramp-bluez-interface-adapter "StopDiscovery")))))
1634
1635(dbus-register-signal
1636 :system nil nil tramp-bluez-interface-adapter "PropertyChanged"
1637 'tramp-bluez-property-changed)
1638
1639(defun tramp-bluez-device-found (device args)
1640 "Signal handler for the \"org.bluez.Adapter.DeviceFound\" signal."
1641 (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" device args)
1642 (let ((alias (car (cadr (assoc "Alias" args))))
1643 (address (car (cadr (assoc "Address" args)))))
1644 ;; Maybe we shall check the device class for being a proper
1645 ;; device, and call also SDP in order to find the obex service.
1646 (add-to-list 'tramp-bluez-devices (list alias address))))
1647
1648(dbus-register-signal
1649 :system nil nil tramp-bluez-interface-adapter "DeviceFound"
1650 'tramp-bluez-device-found)
1651
1652(defun tramp-bluez-parse-device-names (ignore)
1653 "Return a list of (nil host) tuples allowed to access."
1654 (mapcar
1655 (lambda (x) (list nil (car x)))
1656 (tramp-bluez-list-devices)))
1657
1658;; Add completion function for OBEX method.
0b35b48e 1659(when (member tramp-bluez-service (dbus-list-known-names :system))
eeb44655
MA
1660 (tramp-set-completion-function
1661 "obex" '((tramp-bluez-parse-device-names ""))))
1662
1663\f
1664;; D-Bus zeroconf functions.
1665
1666(defun tramp-zeroconf-parse-workstation-device-names (ignore)
1667 "Return a list of (user host) tuples allowed to access."
1668 (mapcar
1669 (lambda (x)
1670 (list nil (zeroconf-service-host x)))
1671 (zeroconf-list-services "_workstation._tcp")))
1672
1673(defun tramp-zeroconf-parse-webdav-device-names (ignore)
1674 "Return a list of (user host) tuples allowed to access."
1675 (mapcar
1676 (lambda (x)
1677 (let ((host (zeroconf-service-host x))
1678 (port (zeroconf-service-port x))
1679 (text (zeroconf-service-txt x))
1680 user)
1681 (when port
1682 (setq host (format "%s%s%d" host tramp-prefix-port-regexp port)))
1683 ;; A user is marked in a TXT field like "u=guest".
1684 (while text
1685 (when (string-match "u=\\(.+\\)$" (car text))
1686 (setq user (match-string 1 (car text))))
1687 (setq text (cdr text)))
1688 (list user host)))
1689 (zeroconf-list-services "_webdav._tcp")))
1690
1691;; Add completion function for DAV and DAVS methods.
0b35b48e 1692(when (member zeroconf-service-avahi (dbus-list-known-names :system))
eeb44655
MA
1693 (zeroconf-init tramp-gvfs-zeroconf-domain)
1694 (tramp-set-completion-function
1695 "sftp" '((tramp-zeroconf-parse-workstation-device-names "")))
1696 (tramp-set-completion-function
1697 "dav" '((tramp-zeroconf-parse-webdav-device-names "")))
1698 (tramp-set-completion-function
1699 "davs" '((tramp-zeroconf-parse-webdav-device-names ""))))
1700
7ae3ea65
MA
1701\f
1702;; D-Bus SYNCE functions.
1703
1704(defun tramp-synce-list-devices ()
99278f8a 1705 "Return all discovered synce devices as list.
d557e7a6 1706They are retrieved from the hal daemon."
7ae3ea65
MA
1707 (let (tramp-synce-devices)
1708 (dolist (device
1709 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
d557e7a6
MA
1710 :system tramp-hal-service tramp-hal-path-manager
1711 tramp-hal-interface-manager "GetAllDevices"))
99278f8a
MA
1712 (when (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
1713 :system tramp-hal-service device tramp-hal-interface-device
1714 "PropertyExists" "sync.plugin")
7ae3ea65
MA
1715 (add-to-list
1716 'tramp-synce-devices
1717 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
d557e7a6
MA
1718 :system tramp-hal-service device tramp-hal-interface-device
1719 "GetPropertyString" "pda.pocketpc.name"))))
7ae3ea65
MA
1720 (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-synce-devices)
1721 tramp-synce-devices))
1722
1723(defun tramp-synce-parse-device-names (ignore)
1724 "Return a list of (nil host) tuples allowed to access."
1725 (mapcar
1726 (lambda (x) (list nil x))
1727 (tramp-synce-list-devices)))
1728
1729;; Add completion function for SYNCE method.
1730(tramp-set-completion-function
1731 "synce" '((tramp-synce-parse-device-names "")))
1732
0f34aa77
MA
1733(add-hook 'tramp-unload-hook
1734 (lambda ()
1735 (unload-feature 'tramp-gvfs 'force)))
1736
eeb44655
MA
1737(provide 'tramp-gvfs)
1738
1739;;; TODO:
1740
eeb44655 1741;; * Host name completion via smb-server or smb-network.
2c68ca0e 1742;; * Check how two shares of the same SMB server can be mounted in
eeb44655
MA
1743;; parallel.
1744;; * Apply SDP on bluetooth devices, in order to filter out obex
1745;; capability.
1746;; * Implement obex for other serial communication but bluetooth.
1747
1748;;; tramp-gvfs.el ends here