Merge from emacs-23
[bpt/emacs.git] / lisp / net / tramp-gvfs.el
index f045589..054875f 100644 (file)
@@ -1,9 +1,10 @@
 ;;; tramp-gvfs.el --- Tramp access functions for GVFS daemon
 
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
 
 ;; Author: Michael Albinus <michael.albinus@gmx.de>
 ;; Keywords: comm, processes
+;; Package: tramp
 
 ;; This file is part of GNU Emacs.
 
 ;; incompatibility with the mount_info structure, which has been
 ;; worked around.
 
+;; It has also been tested with GVFS 1.6.2 (Ubuntu 10.04, Gnome 2.30),
+;; where the default_location has been added to mount_info (see
+;; <https://bugzilla.gnome.org/show_bug.cgi?id=561998>.
+
 ;; All actions to mount a remote location, and to retrieve mount
 ;; information, are performed by D-Bus messages.  File operations
 ;; themselves are performed via the mounted filesystem in ~/.gvfs.
   (require 'custom))
 
 (require 'tramp)
+
 (require 'dbus)
 (require 'url-parse)
+(require 'url-util)
 (require 'zeroconf)
 
+;;;###tramp-autoload
 (defcustom tramp-gvfs-methods '("dav" "davs" "obex" "synce")
   "*List of methods for remote files, accessed with GVFS."
   :group 'tramp
 
 ;; Add a default for `tramp-default-user-alist'.  Rule: For the SYNCE
 ;; method, no user is chosen.
-(add-to-list 'tramp-default-user-alist
-            '("synce" nil nil))
+;;;###tramp-autoload
+(add-to-list 'tramp-default-user-alist '("\\`synce\\'" nil nil))
 
 (defcustom tramp-gvfs-zeroconf-domain "local"
   "*Zeroconf domain to be used for discovering services, like host names."
 
 ;; Add the methods to `tramp-methods', in order to allow minibuffer
 ;; completion.
-(eval-after-load "tramp-gvfs"
-  '(when (featurep 'tramp-gvfs)
-     (dolist (elt tramp-gvfs-methods)
-       (unless (assoc elt tramp-methods)
-        (add-to-list 'tramp-methods (cons elt nil))))))
-
-(defconst tramp-gvfs-mount-point
-  (file-name-as-directory (expand-file-name ".gvfs" "~/"))
-  "The directory name, fuses mounts remote ressources.")
+;;;###tramp-autoload
+(when (featurep 'dbusbind)
+  (dolist (elt tramp-gvfs-methods)
+    (unless (assoc elt tramp-methods)
+      (add-to-list 'tramp-methods (cons elt nil)))))
 
 (defconst tramp-gvfs-path-tramp (concat dbus-path-emacs "/Tramp")
   "The preceeding object path for own objects.")
 (defconst tramp-gvfs-service-daemon "org.gtk.vfs.Daemon"
   "The well known name of the GVFS daemon.")
 
-;; Check that GVFS is available.
-(unless (dbus-ping :session tramp-gvfs-service-daemon 100)
-  (throw 'tramp-loading nil))
+;; Check that GVFS is available.  D-Bus integration is available since
+;; Emacs 23 on some system types.  We don't call `dbus-ping', because
+;; this would load dbus.el.
+(unless (and (tramp-compat-funcall 'dbus-get-unique-name :session)
+            (tramp-compat-process-running-p "gvfs-fuse-daemon"))
+  (error "Package `tramp-gvfs' not supported"))
 
 (defconst tramp-gvfs-path-mounttracker "/org/gtk/vfs/mounttracker"
   "The object path of the GVFS daemon.")
 ;; <interface name='org.gtk.vfs.MountTracker'>
 ;;   <method name='listMounts'>
 ;;     <arg name='mount_info_list'
-;;          type='a{sosssssbay{aya{say}}}'
+;;          type='a{sosssssbay{aya{say}}ay}'
 ;;          direction='out'/>
 ;;   </method>
 ;;   <method name='mountLocation'>
 ;;   </method>
 ;;   <signal name='mounted'>
 ;;     <arg name='mount_info'
-;;          type='{sosssssbay{aya{say}}}'/>
+;;          type='{sosssssbay{aya{say}}ay}'/>
 ;;   </signal>
 ;;   <signal name='unmounted'>
 ;;     <arg name='mount_info'
-;;          type='{sosssssbay{aya{say}}}'/>
+;;          type='{sosssssbay{aya{say}}ay}'/>
 ;;   </signal>
 ;; </interface>
 ;;
 ;;       STRUCT                    mount_spec_item
 ;;         STRING            key (server, share, type, user, host, port)
 ;;         ARRAY BYTE        value
+;;   ARRAY BYTE           default_location     Since GVFS 1.5 only !!!
 
 (defconst tramp-gvfs-interface-mountoperation "org.gtk.vfs.MountOperation"
   "Used by the dbus-proxying implementation of GMountOperation.")
@@ -382,7 +390,7 @@ Every entry is a list (NAME ADDRESS).")
     (expand-file-name . tramp-gvfs-handle-expand-file-name)
     ;; `file-accessible-directory-p' performed by default handler.
     (file-attributes . tramp-gvfs-handle-file-attributes)
-    (file-directory-p . tramp-smb-handle-file-directory-p)
+    (file-directory-p . tramp-gvfs-handle-file-directory-p)
     (file-executable-p . tramp-gvfs-handle-file-executable-p)
     (file-exists-p . tramp-gvfs-handle-file-exists-p)
     (file-local-copy . tramp-gvfs-handle-file-local-copy)
@@ -428,13 +436,15 @@ Every entry is a list (NAME ADDRESS).")
   "Alist of handler functions for Tramp GVFS method.
 Operations not mentioned here will be handled by the default Emacs primitives.")
 
-(defun tramp-gvfs-file-name-p (filename)
+;;;###tramp-autoload
+(defsubst tramp-gvfs-file-name-p (filename)
   "Check if it's a filename handled by the GVFS daemon."
   (and (tramp-tramp-file-p filename)
        (let ((method
              (tramp-file-name-method (tramp-dissect-file-name filename))))
         (and (stringp method) (member method tramp-gvfs-methods)))))
 
+;;;###tramp-autoload
 (defun tramp-gvfs-file-name-handler (operation &rest args)
   "Invoke the GVFS related OPERATION.
 First arg specifies the OPERATION, second arg is a list of arguments to
@@ -446,8 +456,21 @@ pass to the OPERATION."
 
 ;; This might be moved to tramp.el.  It shall be the first file name
 ;; handler.
-(add-to-list 'tramp-foreign-file-name-handler-alist
-            (cons 'tramp-gvfs-file-name-p 'tramp-gvfs-file-name-handler))
+;;;###tramp-autoload
+(when (featurep 'dbusbind)
+  (add-to-list 'tramp-foreign-file-name-handler-alist
+              (cons 'tramp-gvfs-file-name-p 'tramp-gvfs-file-name-handler)))
+
+(defun tramp-gvfs-stringify-dbus-message (message)
+  "Convert a D-Bus message into readable UTF8 strings, used for traces."
+  (cond
+   ((and (consp message) (characterp (car message)))
+    (format "%S" (dbus-byte-array-to-string message)))
+   ((consp message)
+    (mapcar 'tramp-gvfs-stringify-dbus-message message))
+   ((stringp message)
+    (format "%S" message))
+   (t message)))
 
 (defmacro with-tramp-dbus-call-method
   (vec synchronous bus service path interface method &rest args)
@@ -466,12 +489,13 @@ will be traced by Tramp with trace level 6."
         result)
      (tramp-message ,vec 6 "%s %s" func args)
      (setq result (apply func args))
-     (tramp-message ,vec 6 "\n%s" result)
+     (tramp-message ,vec 6 "%s" (tramp-gvfs-stringify-dbus-message result))
      result))
 
 (put 'with-tramp-dbus-call-method 'lisp-indent-function 2)
 (put 'with-tramp-dbus-call-method 'edebug-form-spec '(form symbolp body))
-(font-lock-add-keywords 'emacs-lisp-mode '("\\<with-tramp-dbus-call-method\\>"))
+(tramp-compat-font-lock-add-keywords
+ 'emacs-lisp-mode '("\\<with-tramp-dbus-call-method\\>"))
 
 (defmacro with-tramp-gvfs-error-message (filename handler &rest args)
   "Apply a Tramp GVFS `handler'.
@@ -480,7 +504,7 @@ In case of an error, modify the error message by replacing
   `(let ((fuse-file-name  (regexp-quote (tramp-gvfs-fuse-file-name ,filename)))
         elt)
      (condition-case err
-        (apply ,handler (list ,@args))
+        (tramp-compat-funcall ,handler ,@args)
        (error
        (setq elt (cdr err))
        (while elt
@@ -492,7 +516,8 @@ In case of an error, modify the error message by replacing
 
 (put 'with-tramp-gvfs-error-message 'lisp-indent-function 2)
 (put 'with-tramp-gvfs-error-message 'edebug-form-spec '(form symbolp body))
-(font-lock-add-keywords 'emacs-lisp-mode '("\\<with-tramp-gvfs-error-message\\>"))
+(tramp-compat-font-lock-add-keywords
+ 'emacs-lisp-mode '("\\<with-tramp-gvfs-error-message\\>"))
 
 (defvar tramp-gvfs-dbus-event-vector nil
   "Current Tramp file name to be used, as vector.
@@ -502,7 +527,6 @@ is no information where to trace the message.")
 (defun tramp-gvfs-dbus-event-error (event err)
   "Called when a D-Bus error message arrives, see `dbus-event-error-hooks'."
   (when tramp-gvfs-dbus-event-vector
-    ;(tramp-cleanup-connection tramp-gvfs-dbus-event-vector)
     (tramp-message tramp-gvfs-dbus-event-vector 10 "%S" event)
     (tramp-error tramp-gvfs-dbus-event-vector 'file-error "%s" (cadr err))))
 
@@ -515,27 +539,53 @@ is no information where to trace the message.")
   (filename newname &optional ok-if-already-exists keep-date
            preserve-uid-gid preserve-selinux-context)
   "Like `copy-file' for Tramp files."
-  (let ((args
-        (list
-         (if (tramp-gvfs-file-name-p filename)
-             (tramp-gvfs-fuse-file-name filename)
-           filename)
-         (if (tramp-gvfs-file-name-p newname)
-             (tramp-gvfs-fuse-file-name newname)
-           newname)
-         ok-if-already-exists keep-date preserve-uid-gid)))
-    (when preserve-selinux-context
-      (setq args (append args (list preserve-selinux-context))))
-    (apply 'copy-file args)))
+  (with-parsed-tramp-file-name
+      (if (tramp-tramp-file-p filename) filename newname) nil
+    (with-progress-reporter
+       v 0 (format "Copying %s to %s" filename newname)
+      (condition-case err
+         (let ((args
+                (list
+                 (if (tramp-gvfs-file-name-p filename)
+                     (tramp-gvfs-fuse-file-name filename)
+                   filename)
+                 (if (tramp-gvfs-file-name-p newname)
+                     (tramp-gvfs-fuse-file-name newname)
+                   newname)
+                 ok-if-already-exists keep-date preserve-uid-gid)))
+           (when preserve-selinux-context
+             (setq args (append args (list preserve-selinux-context))))
+           (apply 'copy-file args))
+
+       ;; Error case.  Let's try it with the GVFS utilities.
+       (error
+        (tramp-message v 4 "`copy-file' failed, trying `gvfs-copy'")
+        (unless
+            (zerop
+             (let ((args
+                    (append (if (or keep-date preserve-uid-gid)
+                                (list "--preserve")
+                              nil)
+                            (list
+                             (tramp-gvfs-url-file-name filename)
+                             (tramp-gvfs-url-file-name newname)))))
+               (apply 'tramp-gvfs-send-command v "gvfs-copy" args)))
+          ;; Propagate the error.
+          (tramp-error v (car err) "%s" (cdr err)))))))
+
+  (when (file-remote-p newname)
+    (with-parsed-tramp-file-name newname nil
+      (tramp-flush-file-property v (file-name-directory localname))
+      (tramp-flush-file-property v localname))))
 
 (defun tramp-gvfs-handle-delete-directory (directory &optional recursive)
   "Like `delete-directory' for Tramp files."
   (tramp-compat-delete-directory
    (tramp-gvfs-fuse-file-name directory) recursive))
 
-(defun tramp-gvfs-handle-delete-file (filename &optional force)
+(defun tramp-gvfs-handle-delete-file (filename &optional trash)
   "Like `delete-file' for Tramp files."
-  (tramp-compat-delete-file (tramp-gvfs-fuse-file-name filename) force))
+  (tramp-compat-delete-file (tramp-gvfs-fuse-file-name filename) trash))
 
 (defun tramp-gvfs-handle-directory-files
   (directory &optional full match nosort)
@@ -572,6 +622,14 @@ is no information where to trace the message.")
       (tramp-run-real-handler 'expand-file-name (list name nil))
     ;; Dissect NAME.
     (with-parsed-tramp-file-name name nil
+      ;; If there is a default location, expand tilde.
+      (when (string-match "\\`\\(~\\)\\(/\\|\\'\\)" localname)
+       (save-match-data
+         (tramp-gvfs-maybe-open-connection (vector method user host "/")))
+       (setq localname
+             (replace-match
+              (tramp-get-file-property  v "/" "default-location" "~")
+              nil t localname 1)))
       ;; Tilde expansion is not possible.
       (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
        (tramp-error
@@ -599,6 +657,10 @@ is no information where to trace the message.")
   "Like `file-attributes' for Tramp files."
   (file-attributes (tramp-gvfs-fuse-file-name filename) id-format))
 
+(defun tramp-gvfs-handle-file-directory-p (filename)
+  "Like `file-directory-p' for Tramp files."
+  (file-directory-p (tramp-gvfs-fuse-file-name filename)))
+
 (defun tramp-gvfs-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
   (file-executable-p (tramp-gvfs-fuse-file-name filename)))
@@ -629,8 +691,8 @@ is no information where to trace the message.")
 
 (defun tramp-gvfs-handle-file-selinux-context (filename)
   "Like `file-selinux-context' for Tramp files."
-  (funcall (symbol-function 'file-selinux-context)
-          (tramp-gvfs-fuse-file-name filename)))
+  (tramp-compat-funcall
+   'file-selinux-context (tramp-gvfs-fuse-file-name filename)))
 
 (defun tramp-gvfs-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
@@ -657,19 +719,20 @@ is no information where to trace the message.")
 
 (defun tramp-gvfs-handle-make-directory (dir &optional parents)
   "Like `make-directory' for Tramp files."
-  (condition-case err
-      (with-tramp-gvfs-error-message dir 'make-directory
-       (tramp-gvfs-fuse-file-name dir) parents)
-    ;; Error case.  Let's try it with the GVFS utilities.
-    (error
-     (with-parsed-tramp-file-name dir nil
+  (with-parsed-tramp-file-name dir nil
+    (condition-case err
+       (with-tramp-gvfs-error-message dir 'make-directory
+         (tramp-gvfs-fuse-file-name dir) parents)
+
+      ;; Error case.  Let's try it with the GVFS utilities.
+      (error
        (tramp-message v 4 "`make-directory' failed, trying `gvfs-mkdir'")
        (unless
           (zerop
-           (tramp-local-call-process
-            "gvfs-mkdir" nil (tramp-get-buffer v) nil
-            (tramp-gvfs-url-file-name dir)))
-        (signal (car err) (cdr err)))))))
+           (tramp-gvfs-send-command
+            v "gvfs-mkdir" (tramp-gvfs-url-file-name dir)))
+        ;; Propagate the error.
+        (tramp-error v (car err) "%s" (cdr err)))))))
 
 (defun tramp-gvfs-handle-process-file
   (program &optional infile destination display &rest args)
@@ -680,14 +743,41 @@ is no information where to trace the message.")
 (defun tramp-gvfs-handle-rename-file
   (filename newname &optional ok-if-already-exists)
   "Like `rename-file' for Tramp files."
-  (rename-file
-   (if (tramp-gvfs-file-name-p filename)
-       (tramp-gvfs-fuse-file-name filename)
-     filename)
-   (if (tramp-gvfs-file-name-p newname)
-       (tramp-gvfs-fuse-file-name newname)
-     newname)
-   ok-if-already-exists))
+  (with-parsed-tramp-file-name
+      (if (tramp-tramp-file-p filename) filename newname) nil
+    (with-progress-reporter
+       v 0 (format "Renaming %s to %s" filename newname)
+      (condition-case err
+         (rename-file
+          (if (tramp-gvfs-file-name-p filename)
+              (tramp-gvfs-fuse-file-name filename)
+            filename)
+          (if (tramp-gvfs-file-name-p newname)
+              (tramp-gvfs-fuse-file-name newname)
+            newname)
+          ok-if-already-exists)
+
+       ;; Error case.  Let's try it with the GVFS utilities.
+       (error
+        (tramp-message v 4 "`rename-file' failed, trying `gvfs-move'")
+        (unless
+            (zerop
+             (tramp-gvfs-send-command
+              v "gvfs-move"
+              (tramp-gvfs-url-file-name filename)
+              (tramp-gvfs-url-file-name newname)))
+          ;; Propagate the error.
+          (tramp-error v (car err) "%s" (cdr err)))))))
+
+  (when (file-remote-p filename)
+    (with-parsed-tramp-file-name filename nil
+      (tramp-flush-file-property v (file-name-directory localname))
+      (tramp-flush-file-property v localname)))
+
+  (when (file-remote-p newname)
+    (with-parsed-tramp-file-name newname nil
+      (tramp-flush-file-property v (file-name-directory localname))
+      (tramp-flush-file-property v localname))))
 
 (defun tramp-gvfs-handle-set-file-modes (filename mode)
   "Like `set-file-modes' for Tramp files."
@@ -730,19 +820,16 @@ is no information where to trace the message.")
          start end (tramp-gvfs-fuse-file-name filename)
          append visit lockname confirm)
 
-      ;; Error case.  Let's try it with the GVFS utilities.
+      ;; Error case.  Let's try rename.
       (error
        (let ((tmpfile (tramp-compat-make-temp-file filename)))
-        (tramp-message v 4 "`write-region' failed, trying `gvfs-save'")
+        (tramp-message v 4 "`write-region' failed, trying `rename-file'")
         (write-region start end tmpfile)
-        (unwind-protect
-            (unless
-                (zerop
-                 (tramp-local-call-process
-                  "gvfs-save" tmpfile (tramp-get-buffer v) nil
-                  (tramp-gvfs-url-file-name filename)))
-              (signal (car err) (cdr err)))
-          (tramp-compat-delete-file tmpfile 'force)))))
+        (condition-case nil
+            (rename-file tmpfile filename)
+          (error
+           (delete-file tmpfile)
+           (tramp-error v (car err) "%s" (cdr err)))))))
 
     ;; Set file modification time.
     (when (or (eq visit t) (stringp visit))
@@ -758,16 +845,20 @@ is no information where to trace the message.")
 
 (defun tramp-gvfs-url-file-name (filename)
   "Return FILENAME in URL syntax."
-  (url-recreate-url
-   (if (tramp-tramp-file-p filename)
-       (with-parsed-tramp-file-name (file-truename filename) nil
-        (when (string-match tramp-user-with-domain-regexp user)
-          (setq user
-                (concat (match-string 2 user) ";"  (match-string 2 user))))
-        (url-parse-make-urlobj
-         method user nil
-         (tramp-file-name-real-host v) (tramp-file-name-port v) localname))
-     (url-parse-make-urlobj "file" nil nil nil nil (file-truename filename)))))
+  ;; "/" must NOT be hexlified.
+  (let ((url-unreserved-chars (append '(?/) url-unreserved-chars)))
+    (url-recreate-url
+     (if (tramp-tramp-file-p filename)
+        (with-parsed-tramp-file-name (file-truename filename) nil
+          (when (string-match tramp-user-with-domain-regexp user)
+            (setq user
+                  (concat (match-string 2 user) ";"  (match-string 2 user))))
+          (url-parse-make-urlobj
+           method user nil
+           (tramp-file-name-real-host v) (tramp-file-name-port v)
+           (url-hexify-string localname)))
+       (url-parse-make-urlobj
+       "file" nil nil nil nil (url-hexify-string (file-truename filename)))))))
 
 (defun tramp-gvfs-object-path (filename)
   "Create a D-Bus object path from FILENAME."
@@ -782,15 +873,19 @@ is no information where to trace the message.")
   "Return FUSE file name, which is directly accessible."
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (tramp-gvfs-maybe-open-connection v)
-    (let ((fuse-mountpoint
+    (let ((prefix (tramp-get-file-property v "/" "prefix" ""))
+         (fuse-mountpoint
           (tramp-get-file-property v "/" "fuse-mountpoint" nil)))
       (unless fuse-mountpoint
        (tramp-error
         v 'file-error "There is no FUSE mount point for `%s'" filename))
-      ;; We must remove the share from the local name.
-      (when (and (string-equal "smb" method) (string-match "/[^/]+" localname))
+      ;; We must hide the prefix, if any.
+      (when (string-match (concat "^" (regexp-quote prefix)) localname)
        (setq localname (replace-match "" t t localname)))
-      (concat tramp-gvfs-mount-point fuse-mountpoint localname))))
+      (tramp-message
+       v 10 "remote file `%s' is local file `%s'"
+       filename (concat fuse-mountpoint localname))
+      (concat fuse-mountpoint localname))))
 
 (defun tramp-bluez-address (device)
   "Return bluetooth device address from a given bluetooth DEVICE name."
@@ -874,113 +969,149 @@ ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
            ;; there is only the question whether to accept an unknown
            ;; host signature.
            (with-temp-buffer
-             (insert message)
-             (pop-to-buffer (current-buffer))
-             (setq choice (if (yes-or-no-p (concat (car choices) " ")) 0 1))
-             (tramp-message v 6 "%d" choice))
-
-           ;; When the choice is "no", we set an empty
-           ;; fuse-mountpoint in order to leave the timeout.
+             ;; Preserve message for `progress-reporter'.
+             (tramp-compat-with-temp-message ""
+               (insert message)
+               (pop-to-buffer (current-buffer))
+               (setq choice (if (yes-or-no-p (concat (car choices) " ")) 0 1))
+               (tramp-message v 6 "%d" choice)))
+
+           ;; When the choice is "no", we set a dummy fuse-mountpoint
+           ;; in order to leave the timeout.
            (unless (zerop choice)
-             (tramp-set-file-property v "/" "fuse-mountpoint" ""))
+             (tramp-set-file-property v "/" "fuse-mountpoint" "/"))
 
            (list
             t ;; handled.
             nil ;; no abort of D-Bus.
             choice))
 
-         ;; When QUIT is raised, we shall return this information to D-Bus.
-         (quit (list nil t 0))))))
+       ;; When QUIT is raised, we shall return this information to D-Bus.
+       (quit (list nil t 0))))))
 
 (defun tramp-gvfs-handler-mounted-unmounted (mount-info)
   "Signal handler for the \"org.gtk.vfs.MountTracker.mounted\" and
 \"org.gtk.vfs.MountTracker.unmounted\" signals."
   (ignore-errors
-    (let* ((signal-name (dbus-event-member-name last-input-event))
-          (mount-spec (cadar (last mount-info)))
-          (method (dbus-byte-array-to-string (cadr (assoc "type" mount-spec))))
-          (user (dbus-byte-array-to-string (cadr (assoc "user" mount-spec))))
-          (domain (dbus-byte-array-to-string
-                   (cadr (assoc "domain" mount-spec))))
-          (host (dbus-byte-array-to-string
-                 (cadr (or (assoc "host" mount-spec)
-                           (assoc "server" mount-spec)))))
-          (port (dbus-byte-array-to-string (cadr (assoc "port" mount-spec))))
-          (ssl (dbus-byte-array-to-string (cadr (assoc "ssl" mount-spec)))))
-      (when (string-match "^smb" method)
-       (setq method "smb"))
-      (when (string-equal "obex" method)
-       (setq host (tramp-bluez-device host)))
-      (when (and (string-equal "dav" method) (string-equal "true" ssl))
-       (setq method "davs"))
-      (unless (zerop (length domain))
-       (setq user (concat user tramp-prefix-domain-format domain)))
-      (unless (zerop (length port))
-       (setq host (concat host tramp-prefix-port-format port)))
-      (with-parsed-tramp-file-name
-         (tramp-make-tramp-file-name method user host "") nil
-       (tramp-message v 6 "%s %s" signal-name mount-info)
-       (tramp-set-file-property v "/" "list-mounts" 'undef)
-       (if (string-equal signal-name "unmounted")
-           (tramp-set-file-property v "/" "fuse-mountpoint" nil)
-         (tramp-set-file-property
-          v "/" "fuse-mountpoint"
-          (file-name-nondirectory
-           (dbus-byte-array-to-string (car (last mount-info 2))))))))))
-
-(dbus-register-signal
- :session nil tramp-gvfs-path-mounttracker
- tramp-gvfs-interface-mounttracker "mounted"
- 'tramp-gvfs-handler-mounted-unmounted)
-
-(dbus-register-signal
- :session nil tramp-gvfs-path-mounttracker
- tramp-gvfs-interface-mounttracker "unmounted"
- 'tramp-gvfs-handler-mounted-unmounted)
-
-(defun tramp-gvfs-connection-mounted-p (vec)
-  "Check, whether the location is already mounted."
-  (catch 'mounted
-    (dolist
-       (elt
-        (with-file-property vec "/" "list-mounts"
-          (with-tramp-dbus-call-method vec t
-            :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
-            tramp-gvfs-interface-mounttracker "listMounts"))
-        nil)
-      (let* ((mount-spec (cadar (last elt)))
+    (let ((signal-name (dbus-event-member-name last-input-event))
+         (elt mount-info))
+      ;; Jump over the first elements of the mount info. Since there
+      ;; were changes in the antries, we cannot access dedicated
+      ;; elements.
+      (while (stringp (car elt)) (setq elt (cdr elt)))
+      (let* ((fuse-mountpoint (dbus-byte-array-to-string (cadr elt)))
+            (mount-spec (caddr elt))
+            (default-location (dbus-byte-array-to-string (cadddr elt)))
             (method (dbus-byte-array-to-string
-                     (cadr (assoc "type" mount-spec))))
+                     (cadr (assoc "type" (cadr mount-spec)))))
             (user (dbus-byte-array-to-string
-                   (cadr (assoc "user" mount-spec))))
+                   (cadr (assoc "user" (cadr mount-spec)))))
             (domain (dbus-byte-array-to-string
-                     (cadr (assoc "domain" mount-spec))))
+                     (cadr (assoc "domain" (cadr mount-spec)))))
             (host (dbus-byte-array-to-string
-                   (cadr (or (assoc "host" mount-spec)
-                             (assoc "server" mount-spec)))))
-            (port (dbus-byte-array-to-string (cadr (assoc "port" mount-spec))))
-            (ssl (dbus-byte-array-to-string (cadr (assoc "ssl" mount-spec)))))
+                   (cadr (or (assoc "host" (cadr mount-spec))
+                             (assoc "server" (cadr mount-spec))))))
+            (port (dbus-byte-array-to-string
+                   (cadr (assoc "port" (cadr mount-spec)))))
+            (ssl (dbus-byte-array-to-string
+                  (cadr (assoc "ssl" (cadr mount-spec)))))
+            (prefix (concat (dbus-byte-array-to-string (car mount-spec))
+                            (dbus-byte-array-to-string
+                             (cadr (assoc "share" (cadr mount-spec)))))))
        (when (string-match "^smb" method)
          (setq method "smb"))
        (when (string-equal "obex" method)
          (setq host (tramp-bluez-device host)))
        (when (and (string-equal "dav" method) (string-equal "true" ssl))
          (setq method "davs"))
-       (when (and (string-equal "synce" method) (zerop (length user)))
-         (setq user (or (tramp-file-name-user vec) "")))
        (unless (zerop (length domain))
          (setq user (concat user tramp-prefix-domain-format domain)))
        (unless (zerop (length port))
          (setq host (concat host tramp-prefix-port-format port)))
-       (when (and
-              (string-equal method (tramp-file-name-method vec))
-              (string-equal user (or (tramp-file-name-user vec) ""))
-              (string-equal host (tramp-file-name-host vec)))
-         (tramp-set-file-property
-          vec "/" "fuse-mountpoint"
-          (file-name-nondirectory
-           (dbus-byte-array-to-string (car (last elt 2)))))
-         (throw 'mounted t))))))
+       (with-parsed-tramp-file-name
+           (tramp-make-tramp-file-name method user host "") nil
+         (tramp-message
+          v 6 "%s %s"
+          signal-name (tramp-gvfs-stringify-dbus-message mount-info))
+         (tramp-set-file-property v "/" "list-mounts" 'undef)
+         (if (string-equal signal-name "unmounted")
+             (tramp-set-file-property v "/" "fuse-mountpoint" nil)
+           ;; Set prefix, mountpoint and location.
+           (unless (string-equal prefix "/")
+             (tramp-set-file-property v "/" "prefix" prefix))
+           (tramp-set-file-property v "/" "fuse-mountpoint" fuse-mountpoint)
+           (tramp-set-file-property
+            v "/" "default-location" default-location)))))))
+
+(dbus-register-signal
+ :session nil tramp-gvfs-path-mounttracker
+ tramp-gvfs-interface-mounttracker "mounted"
+ 'tramp-gvfs-handler-mounted-unmounted)
+
+(dbus-register-signal
+ :session nil tramp-gvfs-path-mounttracker
+ tramp-gvfs-interface-mounttracker "unmounted"
+ 'tramp-gvfs-handler-mounted-unmounted)
+
+(defun tramp-gvfs-connection-mounted-p (vec)
+  "Check, whether the location is already mounted."
+  (or
+   (tramp-get-file-property vec "/" "fuse-mountpoint" nil)
+   (catch 'mounted
+     (dolist
+        (elt
+         (with-file-property vec "/" "list-mounts"
+           (with-tramp-dbus-call-method vec t
+             :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
+             tramp-gvfs-interface-mounttracker "listMounts"))
+         nil)
+       ;; Jump over the first elements of the mount info. Since there
+       ;; were changes in the antries, we cannot access dedicated
+       ;; elements.
+       (while (stringp (car elt)) (setq elt (cdr elt)))
+       (let* ((fuse-mountpoint (dbus-byte-array-to-string (cadr elt)))
+             (mount-spec (caddr elt))
+             (default-location (dbus-byte-array-to-string (cadddr elt)))
+             (method (dbus-byte-array-to-string
+                      (cadr (assoc "type" (cadr mount-spec)))))
+             (user (dbus-byte-array-to-string
+                    (cadr (assoc "user" (cadr mount-spec)))))
+             (domain (dbus-byte-array-to-string
+                      (cadr (assoc "domain" (cadr mount-spec)))))
+             (host (dbus-byte-array-to-string
+                    (cadr (or (assoc "host" (cadr mount-spec))
+                              (assoc "server" (cadr mount-spec))))))
+             (port (dbus-byte-array-to-string
+                    (cadr (assoc "port" (cadr mount-spec)))))
+             (ssl (dbus-byte-array-to-string
+                   (cadr (assoc "ssl" (cadr mount-spec)))))
+             (prefix (concat (dbus-byte-array-to-string (car mount-spec))
+                             (dbus-byte-array-to-string
+                              (cadr (assoc "share" (cadr mount-spec)))))))
+        (when (string-match "^smb" method)
+          (setq method "smb"))
+        (when (string-equal "obex" method)
+          (setq host (tramp-bluez-device host)))
+        (when (and (string-equal "dav" method) (string-equal "true" ssl))
+          (setq method "davs"))
+        (when (and (string-equal "synce" method) (zerop (length user)))
+          (setq user (or (tramp-file-name-user vec) "")))
+        (unless (zerop (length domain))
+          (setq user (concat user tramp-prefix-domain-format domain)))
+        (unless (zerop (length port))
+          (setq host (concat host tramp-prefix-port-format port)))
+        (when (and
+               (string-equal method (tramp-file-name-method vec))
+               (string-equal user (or (tramp-file-name-user vec) ""))
+               (string-equal host (tramp-file-name-host vec))
+               (string-match (concat "^" (regexp-quote prefix))
+                             (tramp-file-name-localname vec)))
+          ;; Set prefix, mountpoint and location.
+          (unless (string-equal prefix "/")
+            (tramp-set-file-property vec "/" "prefix" prefix))
+          (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
+          (tramp-set-file-property vec "/" "default-location" default-location)
+          (throw 'mounted t)))))))
 
 (defun tramp-gvfs-mount-spec (vec)
   "Return a mount-spec for \"org.gtk.vfs.MountTracker.mountLocation\"."
@@ -991,7 +1122,8 @@ ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
         (port (tramp-file-name-port vec))
         (localname (tramp-file-name-localname vec))
         (ssl (if (string-match "^davs" method) "true" "false"))
-        (mount-spec `(:array)))
+        (mount-spec '(:array))
+        (mount-pref "/"))
 
     (setq
      mount-spec
@@ -1034,8 +1166,12 @@ ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
        `(:struct "port" ,(dbus-string-to-byte-array (number-to-string port)))
        'append))
 
+    (when (and (string-match "^dav" method)
+              (string-match "^/?[^/]+" localname))
+      (setq mount-pref (match-string 0 localname)))
+
     ;; Return.
-    mount-spec))
+    `(:struct ,(dbus-string-to-byte-array mount-pref) ,mount-spec)))
 
 \f
 ;; Connection functions
@@ -1057,7 +1193,7 @@ connection if a previous connection has died for some reason."
              :name (tramp-buffer-name vec)
              :buffer (tramp-get-buffer vec)
              :server t :host 'local :service t)))
-      (tramp-set-process-query-on-exit-flag p nil)))
+      (tramp-compat-set-process-query-on-exit-flag p nil)))
 
   (unless (tramp-gvfs-connection-mounted-p vec)
     (let* ((method (tramp-file-name-method vec))
@@ -1067,65 +1203,73 @@ connection if a previous connection has died for some reason."
            (tramp-gvfs-object-path
             (tramp-make-tramp-file-name method user host ""))))
 
-      (if (zerop (length (tramp-file-name-user vec)))
-         (tramp-message
-          vec 3 "Opening connection for %s using %s..." host method)
-       (tramp-message
-        vec 3 "Opening connection for %s@%s using %s..." user host method))
-
-      ;; Enable auth-sorce and password-cache.
-      (tramp-set-connection-property vec "first-password-request" t)
-
-      ;; There will be a callback of "askPassword", when a password is
-      ;; needed.
-      (dbus-register-method
-       :session dbus-service-emacs object-path
-       tramp-gvfs-interface-mountoperation "askPassword"
-       'tramp-gvfs-handler-askpassword)
-
-      ;; There could be a callback of "askQuestion", when adding fingerprint.
-      (dbus-register-method
-       :session dbus-service-emacs object-path
-       tramp-gvfs-interface-mountoperation "askQuestion"
-       'tramp-gvfs-handler-askquestion)
-
-      ;; The call must be asynchronously, because of the "askPassword"
-      ;; or "askQuestion"callbacks.
-      (with-tramp-dbus-call-method vec nil
-       :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
-       tramp-gvfs-interface-mounttracker "mountLocation"
-       `(:struct
-         ,(dbus-string-to-byte-array "/")
-         ,(tramp-gvfs-mount-spec vec))
-       (dbus-get-unique-name :session)
-       :object-path object-path)
-
-      ;; We must wait, until the mount is applied.  This will be
-      ;; indicated by the "mounted" signal, i.e. the "fuse-mountpoint"
-      ;; file property.
-      (with-timeout
-         (60
-          (if (zerop (length (tramp-file-name-user vec)))
+      (with-progress-reporter
+         vec 3
+         (if (zerop (length user))
+             (format "Opening connection for %s using %s" host method)
+           (format "Opening connection for %s@%s using %s" user host method))
+
+       ;; Enable auth-sorce and password-cache.
+       (tramp-set-connection-property vec "first-password-request" t)
+
+       ;; There will be a callback of "askPassword", when a password is
+       ;; needed.
+       (dbus-register-method
+        :session dbus-service-emacs object-path
+        tramp-gvfs-interface-mountoperation "askPassword"
+        'tramp-gvfs-handler-askpassword)
+
+       ;; There could be a callback of "askQuestion", when adding fingerprint.
+       (dbus-register-method
+        :session dbus-service-emacs object-path
+        tramp-gvfs-interface-mountoperation "askQuestion"
+        'tramp-gvfs-handler-askquestion)
+
+       ;; The call must be asynchronously, because of the "askPassword"
+       ;; or "askQuestion"callbacks.
+       (with-tramp-dbus-call-method vec nil
+         :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
+         tramp-gvfs-interface-mounttracker "mountLocation"
+         (tramp-gvfs-mount-spec vec) (dbus-get-unique-name :session)
+         :object-path object-path)
+
+       ;; We must wait, until the mount is applied.  This will be
+       ;; indicated by the "mounted" signal, i.e. the "fuse-mountpoint"
+       ;; file property.
+       (with-timeout
+           (60
+            (if (zerop (length (tramp-file-name-user vec)))
+                (tramp-error
+                 vec 'file-error
+                 "Timeout reached mounting %s using %s" host method)
               (tramp-error
                vec 'file-error
-               "Timeout reached mounting %s using %s" host method)
-            (tramp-error
-             vec 'file-error
-             "Timeout reached mounting %s@%s using %s" user host method)))
-       (while (not (tramp-get-file-property vec "/" "fuse-mountpoint" nil))
-         (read-event nil nil 0.1)))
-
-      ;; We set the connection property "started" in order to put the
-      ;; remote location into the cache, which is helpful for further
-      ;; completion.
-      (tramp-set-connection-property vec "started" t)
-
-      (if (zerop (length (tramp-file-name-user vec)))
-         (tramp-message
-          vec 3 "Opening connection for %s using %s...done" host method)
-       (tramp-message
-        vec 3
-        "Opening connection for %s@%s using %s...done" user host method)))))
+               "Timeout reached mounting %s@%s using %s" user host method)))
+         (while (not (tramp-get-file-property vec "/" "fuse-mountpoint" nil))
+           (read-event nil nil 0.1)))
+
+       ;; If `tramp-gvfs-handler-askquestion' has returned "No", it
+       ;; is marked with the fuse-mountpoint "/".  We shall react.
+       (when (string-equal
+              (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/")
+         (tramp-error vec 'file-error "FUSE mount denied"))
+
+       ;; We set the connection property "started" in order to put the
+       ;; remote location into the cache, which is helpful for further
+       ;; completion.
+       (tramp-set-connection-property vec "started" t)))))
+
+(defun tramp-gvfs-send-command (vec command &rest args)
+  "Send the COMMAND with its ARGS to connection VEC.
+COMMAND is usually a command from the gvfs-* utilities.
+`call-process' is applied, and its return code is returned."
+  (let (result)
+    (with-current-buffer (tramp-get-buffer vec)
+      (erase-buffer)
+      (tramp-message vec 6 "%s %s" command (mapconcat 'identity args " "))
+      (setq result (apply 'tramp-local-call-process command nil t nil args))
+      (tramp-message vec 6 "%s" (buffer-string))
+      result)))
 
 \f
 ;; D-Bus BLUEZ functions.
@@ -1273,6 +1417,10 @@ They are retrieved from the hal daemon."
 (tramp-set-completion-function
  "synce" '((tramp-synce-parse-device-names "")))
 
+(add-hook 'tramp-unload-hook
+         (lambda ()
+           (unload-feature 'tramp-gvfs 'force)))
+
 (provide 'tramp-gvfs)
 
 ;;; TODO:
@@ -1284,5 +1432,4 @@ They are retrieved from the hal daemon."
 ;;   capability.
 ;; * Implement obex for other serial communication but bluetooth.
 
-;; arch-tag: f7f660ce-77f4-4132-9663-f5c25a47f7ed
 ;;; tramp-gvfs.el ends here