Allow 'browse-url-emacs' to fetch URL in the selected window
[emacs.git] / lisp / net / tramp-gvfs.el
blobd0385f3ba28a1c024eeef751ebfbe3a0cb0d3ec1
1 ;;; tramp-gvfs.el --- Tramp access functions for GVFS daemon -*- lexical-binding:t -*-
3 ;; Copyright (C) 2009-2018 Free Software Foundation, Inc.
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
6 ;; Keywords: comm, processes
7 ;; Package: tramp
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
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.
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.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
24 ;;; Commentary:
26 ;; Access functions for the GVFS daemon from Tramp. Tested with GVFS
27 ;; 1.0 (Ubuntu 8.10, Gnome 2.24). It has been reported also to run
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.
32 ;; It has also been tested with GVFS 1.6 (Ubuntu 10.04, Gnome 2.30),
33 ;; where the default_location has been added to mount_info (see
34 ;; <https://bugzilla.gnome.org/show_bug.cgi?id=561998>.
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.
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.
42 ;; Consequently, GNU Emacs with enabled D-Bus bindings is a
43 ;; precondition.
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.
50 ;; The custom option `tramp-gvfs-methods' contains the list of
51 ;; supported connection methods. Per default, these are "afp", "dav",
52 ;; "davs", "gdrive", "obex", "owncloud", "sftp" and "synce". Note
53 ;; that with "obex" it might be necessary to pair with the other
54 ;; bluetooth device, if it hasn't been done already. There might be
55 ;; also some few seconds delay in discovering available bluetooth
56 ;; devices.
58 ;; "gdrive" and "owncloud" connection methods require a respective
59 ;; account in GNOME Online Accounts, with enabled "Files" service.
61 ;; Other possible connection methods are "ftp", "http", "https" and
62 ;; "smb". When one of these methods is added to the list, the remote
63 ;; access for that method is performed via GVFS instead of the native
64 ;; Tramp implementation. However, this is not recommended. These
65 ;; methods are listed here for the benefit of file archives, see
66 ;; tramp-archive.el.
68 ;; GVFS offers even more connection methods. The complete list of
69 ;; connection methods of the actual GVFS implementation can be
70 ;; retrieved by:
72 ;; (message
73 ;; "%s"
74 ;; (mapcar
75 ;; 'car
76 ;; (dbus-call-method
77 ;; :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
78 ;; tramp-gvfs-interface-mounttracker "ListMountableInfo")))
80 ;; Note that all other connection methods are not tested, beside the
81 ;; ones offered for customization in `tramp-gvfs-methods'. If you
82 ;; request an additional connection method to be supported, please
83 ;; drop me a note.
85 ;; For hostname completion, information is retrieved either from the
86 ;; bluez daemon (for the "obex" method), the hal daemon (for the
87 ;; "synce" method), or from the zeroconf daemon (for the "afp", "dav",
88 ;; "davs", and "sftp" methods). The zeroconf daemon is pre-configured
89 ;; to discover services in the "local" domain. If another domain
90 ;; shall be used for discovering services, the custom option
91 ;; `tramp-gvfs-zeroconf-domain' can be set accordingly.
93 ;; Restrictions:
95 ;; * The current GVFS implementation does not allow writing on the
96 ;; remote bluetooth device via OBEX.
98 ;; * Two shares of the same SMB server cannot be mounted in parallel.
100 ;;; Code:
102 ;; D-Bus support in the Emacs core can be disabled with configuration
103 ;; option "--without-dbus". Declare used subroutines and variables.
104 (declare-function dbus-get-unique-name "dbusbind.c")
106 (eval-when-compile (require 'cl-lib))
107 (require 'tramp)
109 (require 'dbus)
110 (require 'url-parse)
111 (require 'url-util)
112 (require 'zeroconf)
114 ;; Pacify byte-compiler.
115 (eval-when-compile
116 (require 'custom))
118 ;; We don't call `dbus-ping', because this would load dbus.el.
119 (defconst tramp-gvfs-enabled
120 (ignore-errors
121 (and (featurep 'dbusbind)
122 (tramp-compat-funcall 'dbus-get-unique-name :system)
123 (tramp-compat-funcall 'dbus-get-unique-name :session)
124 (or (tramp-compat-process-running-p "gvfs-fuse-daemon")
125 (tramp-compat-process-running-p "gvfsd-fuse"))))
126 "Non-nil when GVFS is available.")
128 ;;;###tramp-autoload
129 (defcustom tramp-gvfs-methods
130 '("afp" "dav" "davs" "gdrive" "obex" "owncloud" "sftp" "synce")
131 "List of methods for remote files, accessed with GVFS."
132 :group 'tramp
133 :version "26.1"
134 :type '(repeat (choice (const "afp")
135 (const "dav")
136 (const "davs")
137 (const "ftp")
138 (const "gdrive")
139 (const "http")
140 (const "https")
141 (const "obex")
142 (const "owncloud")
143 (const "sftp")
144 (const "smb")
145 (const "synce")))
146 :require 'tramp)
148 (defconst tramp-goa-methods '("gdrive" "owncloud")
149 "List of methods which require registration at GNOME Online Accounts.")
151 ;; Remove GNOME Online Accounts methods if not supported.
152 (unless (and tramp-gvfs-enabled
153 (member tramp-goa-service (dbus-list-known-names :session)))
154 (dolist (method tramp-goa-methods)
155 (setq tramp-gvfs-methods (delete method tramp-gvfs-methods))))
157 ;; Add defaults for `tramp-default-user-alist' and `tramp-default-host-alist'.
158 ;;;###tramp-autoload
159 (when (string-match "\\(.+\\)@\\(\\(?:gmail\\|googlemail\\)\\.com\\)"
160 user-mail-address)
161 (add-to-list 'tramp-default-user-alist
162 `("\\`gdrive\\'" nil ,(match-string 1 user-mail-address)))
163 (add-to-list 'tramp-default-host-alist
164 '("\\`gdrive\\'" nil ,(match-string 2 user-mail-address))))
165 ;;;###tramp-autoload
166 (add-to-list 'tramp-default-user-alist '("\\`synce\\'" nil nil))
168 ;;;###tramp-autoload
169 (defcustom tramp-gvfs-zeroconf-domain "local"
170 "Zeroconf domain to be used for discovering services, like host names."
171 :group 'tramp
172 :version "23.2"
173 :type 'string
174 :require 'tramp)
176 ;; Add the methods to `tramp-methods', in order to allow minibuffer
177 ;; completion.
178 ;;;###tramp-autoload
179 (when (featurep 'dbusbind)
180 (dolist (elt tramp-gvfs-methods)
181 (unless (assoc elt tramp-methods)
182 (add-to-list 'tramp-methods (cons elt nil)))))
184 (defconst tramp-gvfs-path-tramp (concat dbus-path-emacs "/Tramp")
185 "The preceding object path for own objects.")
187 (defconst tramp-gvfs-service-daemon "org.gtk.vfs.Daemon"
188 "The well known name of the GVFS daemon.")
190 (defconst tramp-gvfs-path-mounttracker "/org/gtk/vfs/mounttracker"
191 "The object path of the GVFS daemon.")
193 (defconst tramp-gvfs-interface-mounttracker "org.gtk.vfs.MountTracker"
194 "The mount tracking interface in the GVFS daemon.")
196 ;; Introspection data exist since GVFS 1.14. If there are no such
197 ;; data, we expect an earlier interface.
198 (defconst tramp-gvfs-methods-mounttracker
199 (and tramp-gvfs-enabled
200 (dbus-introspect-get-method-names
201 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
202 tramp-gvfs-interface-mounttracker))
203 "The list of supported methods of the mount tracking interface.")
205 (defconst tramp-gvfs-listmounts
206 (if (member "ListMounts" tramp-gvfs-methods-mounttracker)
207 "ListMounts"
208 "listMounts")
209 "The name of the \"listMounts\" method.
210 It has been changed in GVFS 1.14.")
212 (defconst tramp-gvfs-mountlocation
213 (if (member "MountLocation" tramp-gvfs-methods-mounttracker)
214 "MountLocation"
215 "mountLocation")
216 "The name of the \"mountLocation\" method.
217 It has been changed in GVFS 1.14.")
219 (defconst tramp-gvfs-mountlocation-signature
220 (and tramp-gvfs-enabled
221 (dbus-introspect-get-signature
222 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
223 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation))
224 "The D-Bus signature of the \"mountLocation\" method.
225 It has been changed in GVFS 1.14.")
227 ;; <interface name='org.gtk.vfs.MountTracker'>
228 ;; <method name='listMounts'>
229 ;; <arg name='mount_info_list'
230 ;; type='a{sosssssbay{aya{say}}ay}'
231 ;; direction='out'/>
232 ;; </method>
233 ;; <method name='mountLocation'>
234 ;; <arg name='mount_spec' type='{aya{say}}' direction='in'/>
235 ;; <arg name='dbus_id' type='s' direction='in'/>
236 ;; <arg name='object_path' type='o' direction='in'/>
237 ;; </method>
238 ;; <signal name='mounted'>
239 ;; <arg name='mount_info'
240 ;; type='{sosssssbay{aya{say}}ay}'/>
241 ;; </signal>
242 ;; <signal name='unmounted'>
243 ;; <arg name='mount_info'
244 ;; type='{sosssssbay{aya{say}}ay}'/>
245 ;; </signal>
246 ;; </interface>
248 ;; STRUCT mount_info
249 ;; STRING dbus_id
250 ;; OBJECT_PATH object_path
251 ;; STRING display_name
252 ;; STRING stable_name
253 ;; STRING x_content_types Since GVFS 1.0 only !!!
254 ;; STRING icon
255 ;; STRING preferred_filename_encoding
256 ;; BOOLEAN user_visible
257 ;; ARRAY BYTE fuse_mountpoint
258 ;; STRUCT mount_spec
259 ;; ARRAY BYTE mount_prefix
260 ;; ARRAY
261 ;; STRUCT mount_spec_item
262 ;; STRING key (type, user, domain, host, server,
263 ;; share, volume, port, ssl)
264 ;; ARRAY BYTE value
265 ;; ARRAY BYTE default_location Since GVFS 1.5 only !!!
267 (defconst tramp-gvfs-interface-mountoperation "org.gtk.vfs.MountOperation"
268 "Used by the dbus-proxying implementation of GMountOperation.")
270 ;; <interface name='org.gtk.vfs.MountOperation'>
271 ;; <method name='askPassword'>
272 ;; <arg name='message' type='s' direction='in'/>
273 ;; <arg name='default_user' type='s' direction='in'/>
274 ;; <arg name='default_domain' type='s' direction='in'/>
275 ;; <arg name='flags' type='u' direction='in'/>
276 ;; <arg name='handled' type='b' direction='out'/>
277 ;; <arg name='aborted' type='b' direction='out'/>
278 ;; <arg name='password' type='s' direction='out'/>
279 ;; <arg name='username' type='s' direction='out'/>
280 ;; <arg name='domain' type='s' direction='out'/>
281 ;; <arg name='anonymous' type='b' direction='out'/>
282 ;; <arg name='password_save' type='u' direction='out'/>
283 ;; </method>
284 ;; <method name='askQuestion'>
285 ;; <arg name='message' type='s' direction='in'/>
286 ;; <arg name='choices' type='as' direction='in'/>
287 ;; <arg name='handled' type='b' direction='out'/>
288 ;; <arg name='aborted' type='b' direction='out'/>
289 ;; <arg name='choice' type='u' direction='out'/>
290 ;; </method>
291 ;; </interface>
293 ;; The following flags are used in "askPassword". They are defined in
294 ;; /usr/include/glib-2.0/gio/gioenums.h.
296 (defconst tramp-gvfs-password-need-password 1
297 "Operation requires a password.")
299 (defconst tramp-gvfs-password-need-username 2
300 "Operation requires a username.")
302 (defconst tramp-gvfs-password-need-domain 4
303 "Operation requires a domain.")
305 (defconst tramp-gvfs-password-saving-supported 8
306 "Operation supports saving settings.")
308 (defconst tramp-gvfs-password-anonymous-supported 16
309 "Operation supports anonymous users.")
311 ;; For the time being, we just need org.goa.Account and org.goa.Files
312 ;; interfaces. We document the other ones, just in case.
314 ;;;###tramp-autoload
315 (defconst tramp-goa-service "org.gnome.OnlineAccounts"
316 "The well known name of the GNOME Online Accounts service.")
318 (defconst tramp-goa-path "/org/gnome/OnlineAccounts"
319 "The object path of the GNOME Online Accounts.")
321 (defconst tramp-goa-path-accounts (concat tramp-goa-path "/Accounts")
322 "The object path of the GNOME Online Accounts accounts.")
324 (defconst tramp-goa-interface-documents "org.gnome.OnlineAccounts.Documents"
325 "The documents interface of the GNOME Online Accounts.")
327 ;; <interface name='org.gnome.OnlineAccounts.Documents'>
328 ;; </interface>
330 (defconst tramp-goa-interface-printers "org.gnome.OnlineAccounts.Printers"
331 "The printers interface of the GNOME Online Accounts.")
333 ;; <interface name='org.gnome.OnlineAccounts.Printers'>
334 ;; </interface>
336 (defconst tramp-goa-interface-files "org.gnome.OnlineAccounts.Files"
337 "The files interface of the GNOME Online Accounts.")
339 ;; <interface name='org.gnome.OnlineAccounts.Files'>
340 ;; <property type='b' name='AcceptSslErrors' access='read'/>
341 ;; <property type='s' name='Uri' access='read'/>
342 ;; </interface>
344 (defconst tramp-goa-interface-contacts "org.gnome.OnlineAccounts.Contacts"
345 "The contacts interface of the GNOME Online Accounts.")
347 ;; <interface name='org.gnome.OnlineAccounts.Contacts'>
348 ;; <property type='b' name='AcceptSslErrors' access='read'/>
349 ;; <property type='s' name='Uri' access='read'/>
350 ;; </interface>
352 (defconst tramp-goa-interface-calendar "org.gnome.OnlineAccounts.Calendar"
353 "The calendar interface of the GNOME Online Accounts.")
355 ;; <interface name='org.gnome.OnlineAccounts.Calendar'>
356 ;; <property type='b' name='AcceptSslErrors' access='read'/>
357 ;; <property type='s' name='Uri' access='read'/>
358 ;; </interface>
360 (defconst tramp-goa-interface-oauth2based "org.gnome.OnlineAccounts.OAuth2Based"
361 "The oauth2based interface of the GNOME Online Accounts.")
363 ;; <interface name='org.gnome.OnlineAccounts.OAuth2Based'>
364 ;; <method name='GetAccessToken'>
365 ;; <arg type='s' name='access_token' direction='out'/>
366 ;; <arg type='i' name='expires_in' direction='out'/>
367 ;; </method>
368 ;; <property type='s' name='ClientId' access='read'/>
369 ;; <property type='s' name='ClientSecret' access='read'/>
370 ;; </interface>
372 (defconst tramp-goa-interface-account "org.gnome.OnlineAccounts.Account"
373 "The account interface of the GNOME Online Accounts.")
375 ;; <interface name='org.gnome.OnlineAccounts.Account'>
376 ;; <method name='Remove'/>
377 ;; <method name='EnsureCredentials'>
378 ;; <arg type='i' name='expires_in' direction='out'/>
379 ;; </method>
380 ;; <property type='s' name='ProviderType' access='read'/>
381 ;; <property type='s' name='ProviderName' access='read'/>
382 ;; <property type='s' name='ProviderIcon' access='read'/>
383 ;; <property type='s' name='Id' access='read'/>
384 ;; <property type='b' name='IsLocked' access='read'/>
385 ;; <property type='b' name='IsTemporary' access='readwrite'/>
386 ;; <property type='b' name='AttentionNeeded' access='read'/>
387 ;; <property type='s' name='Identity' access='read'/>
388 ;; <property type='s' name='PresentationIdentity' access='read'/>
389 ;; <property type='b' name='MailDisabled' access='readwrite'/>
390 ;; <property type='b' name='CalendarDisabled' access='readwrite'/>
391 ;; <property type='b' name='ContactsDisabled' access='readwrite'/>
392 ;; <property type='b' name='ChatDisabled' access='readwrite'/>
393 ;; <property type='b' name='DocumentsDisabled' access='readwrite'/>
394 ;; <property type='b' name='MapsDisabled' access='readwrite'/>
395 ;; <property type='b' name='MusicDisabled' access='readwrite'/>
396 ;; <property type='b' name='PrintersDisabled' access='readwrite'/>
397 ;; <property type='b' name='PhotosDisabled' access='readwrite'/>
398 ;; <property type='b' name='FilesDisabled' access='readwrite'/>
399 ;; <property type='b' name='TicketingDisabled' access='readwrite'/>
400 ;; <property type='b' name='TodoDisabled' access='readwrite'/>
401 ;; <property type='b' name='ReadLaterDisabled' access='readwrite'/>
402 ;; </interface>
404 (defconst tramp-goa-identity-regexp
405 (concat "^" "\\(" tramp-user-regexp "\\)?"
406 "@" "\\(" tramp-host-regexp "\\)?"
407 "\\(?:" ":""\\(" tramp-port-regexp "\\)" "\\)?")
408 "Regexp matching GNOME Online Accounts \"PresentationIdentity\" property.")
410 (defconst tramp-goa-interface-mail "org.gnome.OnlineAccounts.Mail"
411 "The mail interface of the GNOME Online Accounts.")
413 ;; <interface name='org.gnome.OnlineAccounts.Mail'>
414 ;; <property type='s' name='EmailAddress' access='read'/>
415 ;; <property type='s' name='Name' access='read'/>
416 ;; <property type='b' name='ImapSupported' access='read'/>
417 ;; <property type='b' name='ImapAcceptSslErrors' access='read'/>
418 ;; <property type='s' name='ImapHost' access='read'/>
419 ;; <property type='b' name='ImapUseSsl' access='read'/>
420 ;; <property type='b' name='ImapUseTls' access='read'/>
421 ;; <property type='s' name='ImapUserName' access='read'/>
422 ;; <property type='b' name='SmtpSupported' access='read'/>
423 ;; <property type='b' name='SmtpAcceptSslErrors' access='read'/>
424 ;; <property type='s' name='SmtpHost' access='read'/>
425 ;; <property type='b' name='SmtpUseAuth' access='read'/>
426 ;; <property type='b' name='SmtpAuthLogin' access='read'/>
427 ;; <property type='b' name='SmtpAuthPlain' access='read'/>
428 ;; <property type='b' name='SmtpAuthXoauth2' access='read'/>
429 ;; <property type='b' name='SmtpUseSsl' access='read'/>
430 ;; <property type='b' name='SmtpUseTls' access='read'/>
431 ;; <property type='s' name='SmtpUserName' access='read'/>
432 ;; </interface>
434 (defconst tramp-goa-interface-chat "org.gnome.OnlineAccounts.Chat"
435 "The chat interface of the GNOME Online Accounts.")
437 ;; <interface name='org.gnome.OnlineAccounts.Chat'>
438 ;; </interface>
440 (defconst tramp-goa-interface-photos "org.gnome.OnlineAccounts.Photos"
441 "The photos interface of the GNOME Online Accounts.")
443 ;; <interface name='org.gnome.OnlineAccounts.Photos'>
444 ;; </interface>
446 (defconst tramp-goa-path-manager (concat tramp-goa-path "/Manager")
447 "The object path of the GNOME Online Accounts manager.")
449 (defconst tramp-goa-interface-documents "org.gnome.OnlineAccounts.Manager"
450 "The manager interface of the GNOME Online Accounts.")
452 ;; <interface name='org.gnome.OnlineAccounts.Manager'>
453 ;; <method name='AddAccount'>
454 ;; <arg type='s' name='provider' direction='in'/>
455 ;; <arg type='s' name='identity' direction='in'/>
456 ;; <arg type='s' name='presentation_identity' direction='in'/>
457 ;; <arg type='a{sv}' name='credentials' direction='in'/>
458 ;; <arg type='a{ss}' name='details' direction='in'/>
459 ;; <arg type='o' name='account_object_path' direction='out'/>
460 ;; </method>
461 ;; </interface>
463 ;; The basic structure for GNOME Online Accounts. We use a list :type,
464 ;; in order to be compatible with Emacs 24 and 25.
465 (cl-defstruct (tramp-goa-name (:type list) :named) method user host port)
467 (defconst tramp-bluez-service "org.bluez"
468 "The well known name of the BLUEZ service.")
470 (defconst tramp-bluez-interface-manager "org.bluez.Manager"
471 "The manager interface of the BLUEZ daemon.")
473 ;; <interface name='org.bluez.Manager'>
474 ;; <method name='DefaultAdapter'>
475 ;; <arg type='o' direction='out'/>
476 ;; </method>
477 ;; <method name='FindAdapter'>
478 ;; <arg type='s' direction='in'/>
479 ;; <arg type='o' direction='out'/>
480 ;; </method>
481 ;; <method name='ListAdapters'>
482 ;; <arg type='ao' direction='out'/>
483 ;; </method>
484 ;; <signal name='AdapterAdded'>
485 ;; <arg type='o'/>
486 ;; </signal>
487 ;; <signal name='AdapterRemoved'>
488 ;; <arg type='o'/>
489 ;; </signal>
490 ;; <signal name='DefaultAdapterChanged'>
491 ;; <arg type='o'/>
492 ;; </signal>
493 ;; </interface>
495 (defconst tramp-bluez-interface-adapter "org.bluez.Adapter"
496 "The adapter interface of the BLUEZ daemon.")
498 ;; <interface name='org.bluez.Adapter'>
499 ;; <method name='GetProperties'>
500 ;; <arg type='a{sv}' direction='out'/>
501 ;; </method>
502 ;; <method name='SetProperty'>
503 ;; <arg type='s' direction='in'/>
504 ;; <arg type='v' direction='in'/>
505 ;; </method>
506 ;; <method name='RequestMode'>
507 ;; <arg type='s' direction='in'/>
508 ;; </method>
509 ;; <method name='ReleaseMode'/>
510 ;; <method name='RequestSession'/>
511 ;; <method name='ReleaseSession'/>
512 ;; <method name='StartDiscovery'/>
513 ;; <method name='StopDiscovery'/>
514 ;; <method name='ListDevices'>
515 ;; <arg type='ao' direction='out'/>
516 ;; </method>
517 ;; <method name='CreateDevice'>
518 ;; <arg type='s' direction='in'/>
519 ;; <arg type='o' direction='out'/>
520 ;; </method>
521 ;; <method name='CreatePairedDevice'>
522 ;; <arg type='s' direction='in'/>
523 ;; <arg type='o' direction='in'/>
524 ;; <arg type='s' direction='in'/>
525 ;; <arg type='o' direction='out'/>
526 ;; </method>
527 ;; <method name='CancelDeviceCreation'>
528 ;; <arg type='s' direction='in'/>
529 ;; </method>
530 ;; <method name='RemoveDevice'>
531 ;; <arg type='o' direction='in'/>
532 ;; </method>
533 ;; <method name='FindDevice'>
534 ;; <arg type='s' direction='in'/>
535 ;; <arg type='o' direction='out'/>
536 ;; </method>
537 ;; <method name='RegisterAgent'>
538 ;; <arg type='o' direction='in'/>
539 ;; <arg type='s' direction='in'/>
540 ;; </method>
541 ;; <method name='UnregisterAgent'>
542 ;; <arg type='o' direction='in'/>
543 ;; </method>
544 ;; <signal name='DeviceCreated'>
545 ;; <arg type='o'/>
546 ;; </signal>
547 ;; <signal name='DeviceRemoved'>
548 ;; <arg type='o'/>
549 ;; </signal>
550 ;; <signal name='DeviceFound'>
551 ;; <arg type='s'/>
552 ;; <arg type='a{sv}'/>
553 ;; </signal>
554 ;; <signal name='PropertyChanged'>
555 ;; <arg type='s'/>
556 ;; <arg type='v'/>
557 ;; </signal>
558 ;; <signal name='DeviceDisappeared'>
559 ;; <arg type='s'/>
560 ;; </signal>
561 ;; </interface>
563 ;;;###tramp-autoload
564 (defcustom tramp-bluez-discover-devices-timeout 60
565 "Defines seconds since last bluetooth device discovery before rescanning.
566 A value of 0 would require an immediate discovery during hostname
567 completion, nil means to use always cached values for discovered
568 devices."
569 :group 'tramp
570 :version "23.2"
571 :type '(choice (const nil) integer)
572 :require 'tramp)
574 (defvar tramp-bluez-discovery nil
575 "Indicator for a running bluetooth device discovery.
576 It keeps the timestamp of last discovery.")
578 (defvar tramp-bluez-devices nil
579 "Alist of detected bluetooth devices.
580 Every entry is a list (NAME ADDRESS).")
582 (defconst tramp-hal-service "org.freedesktop.Hal"
583 "The well known name of the HAL service.")
585 (defconst tramp-hal-path-manager "/org/freedesktop/Hal/Manager"
586 "The object path of the HAL daemon manager.")
588 (defconst tramp-hal-interface-manager "org.freedesktop.Hal.Manager"
589 "The manager interface of the HAL daemon.")
591 (defconst tramp-hal-interface-device "org.freedesktop.Hal.Device"
592 "The device interface of the HAL daemon.")
594 ;; "gvfs-<command>" utilities have been deprecated in GVFS 1.31.1. We
595 ;; must use "gio <command>" tool instead.
596 (defconst tramp-gvfs-gio-mapping
597 '(("gvfs-copy" . "copy")
598 ("gvfs-info" . "info")
599 ("gvfs-ls" . "list")
600 ("gvfs-mkdir" . "mkdir")
601 ("gvfs-monitor-file" . "monitor")
602 ("gvfs-mount" . "mount")
603 ("gvfs-move" . "move")
604 ("gvfs-rm" . "remove")
605 ("gvfs-trash" . "trash"))
606 "List of cons cells, mapping \"gvfs-<command>\" to \"gio <command>\".")
608 ;; <http://www.pygtk.org/docs/pygobject/gio-constants.html>
609 (defconst tramp-gvfs-file-attributes
610 '("name"
611 "type"
612 "standard::display-name"
613 "standard::symlink-target"
614 "unix::nlink"
615 "unix::uid"
616 "owner::user"
617 "unix::gid"
618 "owner::group"
619 "time::access"
620 "time::modified"
621 "time::changed"
622 "standard::size"
623 "unix::mode"
624 "access::can-read"
625 "access::can-write"
626 "access::can-execute"
627 "unix::inode"
628 "unix::device")
629 "GVFS file attributes.")
631 (defconst tramp-gvfs-file-attributes-with-gvfs-ls-regexp
632 (concat "[[:blank:]]" (regexp-opt tramp-gvfs-file-attributes t) "=\\(.+?\\)")
633 "Regexp to parse GVFS file attributes with `gvfs-ls'.")
635 (defconst tramp-gvfs-file-attributes-with-gvfs-info-regexp
636 (concat "^[[:blank:]]*"
637 (regexp-opt tramp-gvfs-file-attributes t)
638 ":[[:blank:]]+\\(.*\\)$")
639 "Regexp to parse GVFS file attributes with `gvfs-info'.")
641 (defconst tramp-gvfs-file-system-attributes
642 '("filesystem::free"
643 "filesystem::size"
644 "filesystem::used")
645 "GVFS file system attributes.")
647 (defconst tramp-gvfs-file-system-attributes-regexp
648 (concat "^[[:blank:]]*"
649 (regexp-opt tramp-gvfs-file-system-attributes t)
650 ":[[:blank:]]+\\(.*\\)$")
651 "Regexp to parse GVFS file system attributes with `gvfs-info'.")
653 (defconst tramp-gvfs-owncloud-default-prefix "/remote.php/webdav"
654 "Default prefix for owncloud / nextcloud methods.")
656 (defconst tramp-gvfs-owncloud-default-prefix-regexp
657 (concat (regexp-quote tramp-gvfs-owncloud-default-prefix) "$")
658 "Regexp of default prefix for owncloud / nextcloud methods.")
661 ;; New handlers should be added here.
662 ;;;###tramp-autoload
663 (defconst tramp-gvfs-file-name-handler-alist
664 '((access-file . ignore)
665 (add-name-to-file . tramp-handle-add-name-to-file)
666 ;; `byte-compiler-base-file-name' performed by default handler.
667 ;; `copy-directory' performed by default handler.
668 (copy-file . tramp-gvfs-handle-copy-file)
669 (delete-directory . tramp-gvfs-handle-delete-directory)
670 (delete-file . tramp-gvfs-handle-delete-file)
671 ;; `diff-latest-backup-file' performed by default handler.
672 (directory-file-name . tramp-handle-directory-file-name)
673 (directory-files . tramp-handle-directory-files)
674 (directory-files-and-attributes
675 . tramp-handle-directory-files-and-attributes)
676 (dired-compress-file . ignore)
677 (dired-uncache . tramp-handle-dired-uncache)
678 (expand-file-name . tramp-gvfs-handle-expand-file-name)
679 (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
680 (file-acl . ignore)
681 (file-attributes . tramp-gvfs-handle-file-attributes)
682 (file-directory-p . tramp-handle-file-directory-p)
683 (file-equal-p . tramp-handle-file-equal-p)
684 (file-executable-p . tramp-gvfs-handle-file-executable-p)
685 (file-exists-p . tramp-handle-file-exists-p)
686 (file-in-directory-p . tramp-handle-file-in-directory-p)
687 (file-local-copy . tramp-gvfs-handle-file-local-copy)
688 (file-modes . tramp-handle-file-modes)
689 (file-name-all-completions . tramp-gvfs-handle-file-name-all-completions)
690 (file-name-as-directory . tramp-handle-file-name-as-directory)
691 (file-name-case-insensitive-p . tramp-handle-file-name-case-insensitive-p)
692 (file-name-completion . tramp-handle-file-name-completion)
693 (file-name-directory . tramp-handle-file-name-directory)
694 (file-name-nondirectory . tramp-handle-file-name-nondirectory)
695 ;; `file-name-sans-versions' performed by default handler.
696 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
697 (file-notify-add-watch . tramp-gvfs-handle-file-notify-add-watch)
698 (file-notify-rm-watch . tramp-handle-file-notify-rm-watch)
699 (file-notify-valid-p . tramp-handle-file-notify-valid-p)
700 (file-ownership-preserved-p . ignore)
701 (file-readable-p . tramp-gvfs-handle-file-readable-p)
702 (file-regular-p . tramp-handle-file-regular-p)
703 (file-remote-p . tramp-handle-file-remote-p)
704 (file-selinux-context . tramp-handle-file-selinux-context)
705 (file-symlink-p . tramp-handle-file-symlink-p)
706 (file-system-info . tramp-gvfs-handle-file-system-info)
707 (file-truename . tramp-handle-file-truename)
708 (file-writable-p . tramp-gvfs-handle-file-writable-p)
709 (find-backup-file-name . tramp-handle-find-backup-file-name)
710 ;; `find-file-noselect' performed by default handler.
711 ;; `get-file-buffer' performed by default handler.
712 (insert-directory . tramp-handle-insert-directory)
713 (insert-file-contents . tramp-handle-insert-file-contents)
714 (load . tramp-handle-load)
715 (make-auto-save-file-name . tramp-handle-make-auto-save-file-name)
716 (make-directory . tramp-gvfs-handle-make-directory)
717 (make-directory-internal . ignore)
718 (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
719 (make-symbolic-link . tramp-handle-make-symbolic-link)
720 (process-file . ignore)
721 (rename-file . tramp-gvfs-handle-rename-file)
722 (set-file-acl . ignore)
723 (set-file-modes . ignore)
724 (set-file-selinux-context . ignore)
725 (set-file-times . ignore)
726 (set-visited-file-modtime . tramp-handle-set-visited-file-modtime)
727 (shell-command . ignore)
728 (start-file-process . ignore)
729 (substitute-in-file-name . tramp-handle-substitute-in-file-name)
730 (temporary-file-directory . tramp-handle-temporary-file-directory)
731 (unhandled-file-name-directory . ignore)
732 (vc-registered . ignore)
733 (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
734 (write-region . tramp-gvfs-handle-write-region))
735 "Alist of handler functions for Tramp GVFS method.
736 Operations not mentioned here will be handled by the default Emacs primitives.")
738 ;; It must be a `defsubst' in order to push the whole code into
739 ;; tramp-loaddefs.el. Otherwise, there would be recursive autoloading.
740 ;;;###tramp-autoload
741 (defsubst tramp-gvfs-file-name-p (filename)
742 "Check if it's a filename handled by the GVFS daemon."
743 (and (tramp-tramp-file-p filename)
744 (let ((method
745 (tramp-file-name-method (tramp-dissect-file-name filename))))
746 (and (stringp method) (member method tramp-gvfs-methods)))))
748 ;;;###tramp-autoload
749 (defun tramp-gvfs-file-name-handler (operation &rest args)
750 "Invoke the GVFS related OPERATION.
751 First arg specifies the OPERATION, second arg is a list of arguments to
752 pass to the OPERATION."
753 (unless tramp-gvfs-enabled
754 (tramp-compat-user-error nil "Package `tramp-gvfs' not supported"))
755 (let ((fn (assoc operation tramp-gvfs-file-name-handler-alist)))
756 (if fn
757 (save-match-data (apply (cdr fn) args))
758 (tramp-run-real-handler operation args))))
760 ;;;###tramp-autoload
761 (when (featurep 'dbusbind)
762 (tramp-register-foreign-file-name-handler
763 'tramp-gvfs-file-name-p 'tramp-gvfs-file-name-handler))
766 ;; D-Bus helper function.
768 (defun tramp-gvfs-dbus-string-to-byte-array (string)
769 "Like `dbus-string-to-byte-array' but add trailing \\0 if needed."
770 (dbus-string-to-byte-array
771 (if (string-match "^(aya{sv})" tramp-gvfs-mountlocation-signature)
772 (concat string (string 0)) string)))
774 (defun tramp-gvfs-dbus-byte-array-to-string (byte-array)
775 "Like `dbus-byte-array-to-string' but remove trailing \\0 if exists.
776 Return nil for null BYTE-ARRAY."
777 ;; The byte array could be a variant. Take care.
778 (let ((byte-array
779 (if (and (consp byte-array) (atom (car byte-array)))
780 byte-array (car byte-array))))
781 (and byte-array
782 (dbus-byte-array-to-string
783 (if (and (consp byte-array) (zerop (car (last byte-array))))
784 (butlast byte-array) byte-array)))))
786 (defun tramp-gvfs-stringify-dbus-message (message)
787 "Convert a D-Bus message into readable UTF8 strings, used for traces."
788 (cond
789 ((and (consp message) (characterp (car message)))
790 (format "%S" (tramp-gvfs-dbus-byte-array-to-string message)))
791 ((and (consp message) (not (consp (cdr message))))
792 (cons (tramp-gvfs-stringify-dbus-message (car message))
793 (tramp-gvfs-stringify-dbus-message (cdr message))))
794 ((consp message)
795 (mapcar 'tramp-gvfs-stringify-dbus-message message))
796 ((stringp message)
797 (format "%S" message))
798 (t message)))
800 (defun tramp-dbus-function (vec func args)
801 "Apply a D-Bus function FUNC from dbus.el.
802 The call will be traced by Tramp with trace level 6."
803 (let (result)
804 (tramp-message vec 6 "%s" (cons func args))
805 (setq result (apply func args))
806 (tramp-message vec 6 "%s" result(tramp-gvfs-stringify-dbus-message result))
807 result))
809 (defmacro with-tramp-dbus-call-method
810 (vec synchronous bus service path interface method &rest args)
811 "Apply a D-Bus call on bus BUS.
813 If SYNCHRONOUS is non-nil, the call is synchronously. Otherwise,
814 it is an asynchronous call, with `ignore' as callback function.
816 The other arguments have the same meaning as with `dbus-call-method'
817 or `dbus-call-method-asynchronously'."
818 `(let ((func (if ,synchronous
819 'dbus-call-method 'dbus-call-method-asynchronously))
820 (args (append (list ,bus ,service ,path ,interface ,method)
821 (if ,synchronous (list ,@args) (list 'ignore ,@args)))))
822 (tramp-dbus-function ,vec func args)))
824 (put 'with-tramp-dbus-call-method 'lisp-indent-function 2)
825 (put 'with-tramp-dbus-call-method 'edebug-form-spec '(form symbolp body))
826 (font-lock-add-keywords 'emacs-lisp-mode '("\\<with-tramp-dbus-call-method\\>"))
828 (defmacro with-tramp-dbus-get-all-properties
829 (vec bus service path interface)
830 "Return all properties of INTERFACE.
831 The call will be traced by Tramp with trace level 6."
832 ;; Check, that interface exists at object path. Retrieve properties.
833 `(when (member
834 ,interface
835 (tramp-dbus-function
836 ,vec 'dbus-introspect-get-interface-names
837 (list ,bus ,service ,path)))
838 (tramp-dbus-function
839 ,vec 'dbus-get-all-properties (list ,bus ,service ,path ,interface))))
841 (put 'with-tramp-dbus-get-all-properties 'lisp-indent-function 1)
842 (put 'with-tramp-dbus-get-all-properties 'edebug-form-spec '(form symbolp body))
843 (font-lock-add-keywords 'emacs-lisp-mode '("\\<with-tramp-dbus-get-all-properties\\>"))
845 (defvar tramp-gvfs-dbus-event-vector nil
846 "Current Tramp file name to be used, as vector.
847 It is needed when D-Bus signals or errors arrive, because there
848 is no information where to trace the message.")
850 (defun tramp-gvfs-dbus-event-error (event err)
851 "Called when a D-Bus error message arrives, see `dbus-event-error-functions'."
852 (when tramp-gvfs-dbus-event-vector
853 (tramp-message tramp-gvfs-dbus-event-vector 6 "%S" event)
854 (tramp-error tramp-gvfs-dbus-event-vector 'file-error "%s" (cadr err))))
856 ;; `dbus-event-error-hooks' has been renamed to
857 ;; `dbus-event-error-functions' in Emacs 24.3.
858 (add-hook
859 (if (boundp 'dbus-event-error-functions)
860 'dbus-event-error-functions 'dbus-event-error-hooks)
861 'tramp-gvfs-dbus-event-error)
864 ;; File name primitives.
866 (defun tramp-gvfs-do-copy-or-rename-file
867 (op filename newname &optional ok-if-already-exists keep-date
868 preserve-uid-gid preserve-extended-attributes)
869 "Copy or rename a remote file.
870 OP must be `copy' or `rename' and indicates the operation to perform.
871 FILENAME specifies the file to copy or rename, NEWNAME is the name of
872 the new file (for copy) or the new name of the file (for rename).
873 OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already.
874 KEEP-DATE means to make sure that NEWNAME has the same timestamp
875 as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep
876 the uid and gid if both files are on the same host.
877 PRESERVE-EXTENDED-ATTRIBUTES is ignored.
879 This function is invoked by `tramp-gvfs-handle-copy-file' and
880 `tramp-gvfs-handle-rename-file'. It is an error if OP is neither
881 of `copy' and `rename'. FILENAME and NEWNAME must be absolute
882 file names."
883 (unless (memq op '(copy rename))
884 (error "Unknown operation `%s', must be `copy' or `rename'" op))
886 (setq filename (file-truename filename))
887 (if (file-directory-p filename)
888 (progn
889 (copy-directory filename newname keep-date t)
890 (when (eq op 'rename) (delete-directory filename 'recursive)))
892 (let ((t1 (tramp-tramp-file-p filename))
893 (t2 (tramp-tramp-file-p newname))
894 (equal-remote (tramp-equal-remote filename newname))
895 (gvfs-operation (if (eq op 'copy) "gvfs-copy" "gvfs-move"))
896 (msg-operation (if (eq op 'copy) "Copying" "Renaming")))
898 (with-parsed-tramp-file-name (if t1 filename newname) nil
899 (when (and (not ok-if-already-exists) (file-exists-p newname))
900 (tramp-error v 'file-already-exists newname))
902 (if (or (and equal-remote
903 (tramp-get-connection-property v "direct-copy-failed" nil))
904 (and t1 (not (tramp-gvfs-file-name-p filename)))
905 (and t2 (not (tramp-gvfs-file-name-p newname))))
907 ;; We cannot copy or rename directly.
908 (let ((tmpfile (tramp-compat-make-temp-file filename)))
909 (if (eq op 'copy)
910 (copy-file
911 filename tmpfile t keep-date preserve-uid-gid
912 preserve-extended-attributes)
913 (rename-file filename tmpfile t))
914 (rename-file tmpfile newname ok-if-already-exists))
916 ;; Direct action.
917 (with-tramp-progress-reporter
918 v 0 (format "%s %s to %s" msg-operation filename newname)
919 (unless
920 (apply
921 'tramp-gvfs-send-command v gvfs-operation
922 (append
923 (and (eq op 'copy) (or keep-date preserve-uid-gid)
924 '("--preserve"))
925 (list
926 (tramp-gvfs-url-file-name filename)
927 (tramp-gvfs-url-file-name newname))))
929 (if (or (not equal-remote)
930 (and equal-remote
931 (tramp-get-connection-property
932 v "direct-copy-failed" nil)))
933 ;; Propagate the error.
934 (with-current-buffer (tramp-get-connection-buffer v)
935 (goto-char (point-min))
936 (tramp-error-with-buffer
937 nil v 'file-error
938 "%s failed, see buffer `%s' for details."
939 msg-operation (buffer-name)))
941 ;; Some WebDAV server, like the one from QNAP, do not
942 ;; support direct copy/move. Try a fallback.
943 (tramp-set-connection-property v "direct-copy-failed" t)
944 (tramp-gvfs-do-copy-or-rename-file
945 op filename newname ok-if-already-exists keep-date
946 preserve-uid-gid preserve-extended-attributes))))
948 (when (and t1 (eq op 'rename))
949 (with-parsed-tramp-file-name filename nil
950 (tramp-flush-file-properties v (file-name-directory localname))
951 (tramp-flush-file-properties v localname)))
953 (when t2
954 (with-parsed-tramp-file-name newname nil
955 (tramp-flush-file-properties v (file-name-directory localname))
956 (tramp-flush-file-properties v localname))))))))
958 (defun tramp-gvfs-handle-copy-file
959 (filename newname &optional ok-if-already-exists keep-date
960 preserve-uid-gid preserve-extended-attributes)
961 "Like `copy-file' for Tramp files."
962 (setq filename (expand-file-name filename))
963 (setq newname (expand-file-name newname))
964 ;; At least one file a Tramp file?
965 (if (or (tramp-tramp-file-p filename)
966 (tramp-tramp-file-p newname))
967 (tramp-gvfs-do-copy-or-rename-file
968 'copy filename newname ok-if-already-exists keep-date
969 preserve-uid-gid preserve-extended-attributes)
970 (tramp-run-real-handler
971 'copy-file
972 (list filename newname ok-if-already-exists keep-date
973 preserve-uid-gid preserve-extended-attributes))))
975 (defun tramp-gvfs-handle-delete-directory (directory &optional recursive trash)
976 "Like `delete-directory' for Tramp files."
977 (with-parsed-tramp-file-name directory nil
978 (if (and recursive (not (file-symlink-p directory)))
979 (mapc (lambda (file)
980 (if (eq t (tramp-compat-file-attribute-type
981 (file-attributes file)))
982 (delete-directory file recursive trash)
983 (delete-file file trash)))
984 (directory-files
985 directory 'full directory-files-no-dot-files-regexp))
986 (when (directory-files directory nil directory-files-no-dot-files-regexp)
987 (tramp-error
988 v 'file-error "Couldn't delete non-empty %s" directory)))
990 (tramp-flush-file-properties v (file-name-directory localname))
991 (tramp-flush-directory-properties v localname)
992 (unless
993 (tramp-gvfs-send-command
994 v (if (and trash delete-by-moving-to-trash) "gvfs-trash" "gvfs-rm")
995 (tramp-gvfs-url-file-name directory))
996 ;; Propagate the error.
997 (with-current-buffer (tramp-get-connection-buffer v)
998 (goto-char (point-min))
999 (tramp-error-with-buffer
1000 nil v 'file-error "Couldn't delete %s" directory)))))
1002 (defun tramp-gvfs-handle-delete-file (filename &optional trash)
1003 "Like `delete-file' for Tramp files."
1004 (with-parsed-tramp-file-name filename nil
1005 (tramp-flush-file-properties v (file-name-directory localname))
1006 (tramp-flush-file-properties v localname)
1007 (unless
1008 (tramp-gvfs-send-command
1009 v (if (and trash delete-by-moving-to-trash) "gvfs-trash" "gvfs-rm")
1010 (tramp-gvfs-url-file-name filename))
1011 ;; Propagate the error.
1012 (with-current-buffer (tramp-get-connection-buffer v)
1013 (goto-char (point-min))
1014 (tramp-error-with-buffer
1015 nil v 'file-error "Couldn't delete %s" filename)))))
1017 (defun tramp-gvfs-handle-expand-file-name (name &optional dir)
1018 "Like `expand-file-name' for Tramp files."
1019 ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
1020 (setq dir (or dir default-directory "/"))
1021 ;; Unless NAME is absolute, concat DIR and NAME.
1022 (unless (file-name-absolute-p name)
1023 (setq name (concat (file-name-as-directory dir) name)))
1024 ;; If NAME is not a Tramp file, run the real handler.
1025 (if (not (tramp-tramp-file-p name))
1026 (tramp-run-real-handler 'expand-file-name (list name nil))
1027 ;; Dissect NAME.
1028 (with-parsed-tramp-file-name name nil
1029 ;; If there is a default location, expand tilde.
1030 (when (string-match "\\`\\(~\\)\\(/\\|\\'\\)" localname)
1031 (save-match-data
1032 (tramp-gvfs-maybe-open-connection
1033 (make-tramp-file-name
1034 :method method :user user :domain domain
1035 :host host :port port :localname "/" :hop hop)))
1036 (setq localname
1037 (replace-match
1038 (tramp-get-connection-property v "default-location" "~")
1039 nil t localname 1)))
1040 ;; Tilde expansion is not possible.
1041 (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
1042 (tramp-error
1043 v 'file-error
1044 "Cannot expand tilde in file `%s'" name))
1045 (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
1046 (setq localname (concat "/" localname)))
1047 ;; We do not pass "/..".
1048 (if (string-match "^\\(afp\\|davs?\\|smb\\)$" method)
1049 (when (string-match "^/[^/]+\\(/\\.\\./?\\)" localname)
1050 (setq localname (replace-match "/" t t localname 1)))
1051 (when (string-match "^/\\.\\./?" localname)
1052 (setq localname (replace-match "/" t t localname))))
1053 ;; There might be a double slash. Remove this.
1054 (while (string-match "//" localname)
1055 (setq localname (replace-match "/" t t localname)))
1056 ;; No tilde characters in file name, do normal
1057 ;; `expand-file-name' (this does "/./" and "/../").
1058 (tramp-make-tramp-file-name
1059 method user domain host port
1060 (tramp-run-real-handler 'expand-file-name (list localname))))))
1062 (defun tramp-gvfs-get-directory-attributes (directory)
1063 "Return GVFS attributes association list of all files in DIRECTORY."
1064 (ignore-errors
1065 ;; Don't modify `last-coding-system-used' by accident.
1066 (let ((last-coding-system-used last-coding-system-used)
1067 result)
1068 (with-parsed-tramp-file-name directory nil
1069 (with-tramp-file-property v localname "directory-attributes"
1070 (tramp-message v 5 "directory gvfs attributes: %s" localname)
1071 ;; Send command.
1072 (tramp-gvfs-send-command
1073 v "gvfs-ls" "-h" "-n" "-a"
1074 (mapconcat 'identity tramp-gvfs-file-attributes ",")
1075 (tramp-gvfs-url-file-name directory))
1076 ;; Parse output.
1077 (with-current-buffer (tramp-get-connection-buffer v)
1078 (goto-char (point-min))
1079 (while (looking-at
1080 (concat "^\\(.+\\)[[:blank:]]"
1081 "\\([[:digit:]]+\\)[[:blank:]]"
1082 "(\\(.+?\\))"
1083 tramp-gvfs-file-attributes-with-gvfs-ls-regexp))
1084 (let ((item (list (cons "type" (match-string 3))
1085 (cons "standard::size" (match-string 2))
1086 (cons "name" (match-string 1)))))
1087 (goto-char (1+ (match-end 3)))
1088 (while (looking-at
1089 (concat
1090 tramp-gvfs-file-attributes-with-gvfs-ls-regexp
1091 "\\(" tramp-gvfs-file-attributes-with-gvfs-ls-regexp
1092 "\\|" "$" "\\)"))
1093 (push (cons (match-string 1) (match-string 2)) item)
1094 (goto-char (match-end 2)))
1095 ;; Add display name as head.
1096 (push
1097 (cons (cdr (or (assoc "standard::display-name" item)
1098 (assoc "name" item)))
1099 (nreverse item))
1100 result))
1101 (forward-line)))
1102 result)))))
1104 (defun tramp-gvfs-get-root-attributes (filename &optional file-system)
1105 "Return GVFS attributes association list of FILENAME.
1106 If FILE-SYSTEM is non-nil, return file system attributes."
1107 (ignore-errors
1108 ;; Don't modify `last-coding-system-used' by accident.
1109 (let ((last-coding-system-used last-coding-system-used)
1110 result)
1111 (with-parsed-tramp-file-name filename nil
1112 (with-tramp-file-property
1113 v localname
1114 (if file-system "file-system-attributes" "file-attributes")
1115 (tramp-message
1116 v 5 "file%s gvfs attributes: %s"
1117 (if file-system " system" "") localname)
1118 ;; Send command.
1119 (if file-system
1120 (tramp-gvfs-send-command
1121 v "gvfs-info" "--filesystem" (tramp-gvfs-url-file-name filename))
1122 (tramp-gvfs-send-command
1123 v "gvfs-info" (tramp-gvfs-url-file-name filename)))
1124 ;; Parse output.
1125 (with-current-buffer (tramp-get-connection-buffer v)
1126 (goto-char (point-min))
1127 (while (re-search-forward
1128 (if file-system
1129 tramp-gvfs-file-system-attributes-regexp
1130 tramp-gvfs-file-attributes-with-gvfs-info-regexp)
1131 nil t)
1132 (push (cons (match-string 1) (match-string 2)) result))
1133 result))))))
1135 (defun tramp-gvfs-get-file-attributes (filename)
1136 "Return GVFS attributes association list of FILENAME."
1137 (setq filename (directory-file-name (expand-file-name filename)))
1138 (with-parsed-tramp-file-name filename nil
1139 (setq localname (tramp-compat-file-name-unquote localname))
1140 (if (or (and (string-match "^\\(afp\\|davs?\\|smb\\)$" method)
1141 (string-match "^/?\\([^/]+\\)$" localname))
1142 (string-equal localname "/"))
1143 (tramp-gvfs-get-root-attributes filename)
1144 (assoc
1145 (file-name-nondirectory filename)
1146 (tramp-gvfs-get-directory-attributes (file-name-directory filename))))))
1148 (defun tramp-gvfs-handle-file-attributes (filename &optional id-format)
1149 "Like `file-attributes' for Tramp files."
1150 (unless id-format (setq id-format 'integer))
1151 (ignore-errors
1152 (let ((attributes (tramp-gvfs-get-file-attributes filename))
1153 dirp res-symlink-target res-numlinks res-uid res-gid res-access
1154 res-mod res-change res-size res-filemodes res-inode res-device)
1155 (when attributes
1156 ;; ... directory or symlink
1157 (setq dirp (if (equal "directory" (cdr (assoc "type" attributes))) t))
1158 (setq res-symlink-target
1159 (cdr (assoc "standard::symlink-target" attributes)))
1160 ;; ... number links
1161 (setq res-numlinks
1162 (string-to-number
1163 (or (cdr (assoc "unix::nlink" attributes)) "0")))
1164 ;; ... uid and gid
1165 (setq res-uid
1166 (if (eq id-format 'integer)
1167 (string-to-number
1168 (or (cdr (assoc "unix::uid" attributes))
1169 (format "%s" tramp-unknown-id-integer)))
1170 (or (cdr (assoc "owner::user" attributes))
1171 (cdr (assoc "unix::uid" attributes))
1172 tramp-unknown-id-string)))
1173 (setq res-gid
1174 (if (eq id-format 'integer)
1175 (string-to-number
1176 (or (cdr (assoc "unix::gid" attributes))
1177 (format "%s" tramp-unknown-id-integer)))
1178 (or (cdr (assoc "owner::group" attributes))
1179 (cdr (assoc "unix::gid" attributes))
1180 tramp-unknown-id-string)))
1181 ;; ... last access, modification and change time
1182 (setq res-access
1183 (seconds-to-time
1184 (string-to-number
1185 (or (cdr (assoc "time::access" attributes)) "0"))))
1186 (setq res-mod
1187 (seconds-to-time
1188 (string-to-number
1189 (or (cdr (assoc "time::modified" attributes)) "0"))))
1190 (setq res-change
1191 (seconds-to-time
1192 (string-to-number
1193 (or (cdr (assoc "time::changed" attributes)) "0"))))
1194 ;; ... size
1195 (setq res-size
1196 (string-to-number
1197 (or (cdr (assoc "standard::size" attributes)) "0")))
1198 ;; ... file mode flags
1199 (setq res-filemodes
1200 (let ((n (cdr (assoc "unix::mode" attributes))))
1201 (if n
1202 (tramp-file-mode-from-int (string-to-number n))
1203 (format
1204 "%s%s%s%s------"
1205 (if dirp "d" (if res-symlink-target "l" "-"))
1206 (if (equal (cdr (assoc "access::can-read" attributes))
1207 "FALSE")
1208 "-" "r")
1209 (if (equal (cdr (assoc "access::can-write" attributes))
1210 "FALSE")
1211 "-" "w")
1212 (if (equal (cdr (assoc "access::can-execute" attributes))
1213 "FALSE")
1214 "-" "x")))))
1215 ;; ... inode and device
1216 (setq res-inode
1217 (let ((n (cdr (assoc "unix::inode" attributes))))
1218 (if n
1219 (string-to-number n)
1220 (tramp-get-inode (tramp-dissect-file-name filename)))))
1221 (setq res-device
1222 (let ((n (cdr (assoc "unix::device" attributes))))
1223 (if n
1224 (string-to-number n)
1225 (tramp-get-device (tramp-dissect-file-name filename)))))
1227 ;; Return data gathered.
1228 (list
1229 ;; 0. t for directory, string (name linked to) for
1230 ;; symbolic link, or nil.
1231 (or dirp res-symlink-target)
1232 ;; 1. Number of links to file.
1233 res-numlinks
1234 ;; 2. File uid.
1235 res-uid
1236 ;; 3. File gid.
1237 res-gid
1238 ;; 4. Last access time, as a list of integers.
1239 ;; 5. Last modification time, likewise.
1240 ;; 6. Last status change time, likewise.
1241 res-access res-mod res-change
1242 ;; 7. Size in bytes (-1, if number is out of range).
1243 res-size
1244 ;; 8. File modes.
1245 res-filemodes
1246 ;; 9. t if file's gid would change if file were deleted
1247 ;; and recreated.
1249 ;; 10. Inode number.
1250 res-inode
1251 ;; 11. Device number.
1252 res-device
1253 )))))
1255 (defun tramp-gvfs-handle-file-executable-p (filename)
1256 "Like `file-executable-p' for Tramp files."
1257 (with-parsed-tramp-file-name filename nil
1258 (with-tramp-file-property v localname "file-executable-p"
1259 (tramp-check-cached-permissions v ?x))))
1261 (defun tramp-gvfs-handle-file-local-copy (filename)
1262 "Like `file-local-copy' for Tramp files."
1263 (with-parsed-tramp-file-name filename nil
1264 (unless (file-exists-p filename)
1265 (tramp-error
1266 v tramp-file-missing
1267 "Cannot make local copy of non-existing file `%s'" filename))
1268 (let ((tmpfile (tramp-compat-make-temp-file filename)))
1269 (copy-file filename tmpfile 'ok-if-already-exists 'keep-time)
1270 tmpfile)))
1272 (defun tramp-gvfs-handle-file-name-all-completions (filename directory)
1273 "Like `file-name-all-completions' for Tramp files."
1274 (unless (save-match-data (string-match "/" filename))
1275 (all-completions
1276 filename
1277 (with-parsed-tramp-file-name (expand-file-name directory) nil
1278 (with-tramp-file-property v localname "file-name-all-completions"
1279 (let ((result '("./" "../")))
1280 ;; Get a list of directories and files.
1281 (dolist (item (tramp-gvfs-get-directory-attributes directory) result)
1282 (if (string-equal (cdr (assoc "type" item)) "directory")
1283 (push (file-name-as-directory (car item)) result)
1284 (push (car item) result)))))))))
1286 (defun tramp-gvfs-handle-file-notify-add-watch (file-name flags _callback)
1287 "Like `file-notify-add-watch' for Tramp files."
1288 (setq file-name (expand-file-name file-name))
1289 (with-parsed-tramp-file-name file-name nil
1290 ;; TODO: We cannot watch directories, because `gio monitor' is not
1291 ;; supported for gvfs-mounted directories. However,
1292 ;; `file-notify-add-watch' uses directories.
1293 (when (or (not (tramp-gvfs-gio-tool-p v)) (file-directory-p file-name))
1294 (tramp-error
1295 v 'file-notify-error "Monitoring not supported for `%s'" file-name))
1296 (let* ((default-directory (file-name-directory file-name))
1297 (events
1298 (cond
1299 ((and (memq 'change flags) (memq 'attribute-change flags))
1300 '(created changed changes-done-hint moved deleted
1301 attribute-changed))
1302 ((memq 'change flags)
1303 '(created changed changes-done-hint moved deleted))
1304 ((memq 'attribute-change flags) '(attribute-changed))))
1305 (p (apply
1306 'start-process
1307 "gvfs-monitor" (generate-new-buffer " *gvfs-monitor*")
1308 `("gio" "monitor" ,(tramp-gvfs-url-file-name file-name)))))
1309 (if (not (processp p))
1310 (tramp-error
1311 v 'file-notify-error "Monitoring not supported for `%s'" file-name)
1312 (tramp-message
1313 v 6 "Run `%s', %S" (mapconcat 'identity (process-command p) " ") p)
1314 (process-put p 'vector v)
1315 (process-put p 'events events)
1316 (process-put p 'watch-name localname)
1317 (process-put p 'adjust-window-size-function 'ignore)
1318 (set-process-query-on-exit-flag p nil)
1319 (set-process-filter p 'tramp-gvfs-monitor-process-filter)
1320 ;; There might be an error if the monitor is not supported.
1321 ;; Give the filter a chance to read the output.
1322 (tramp-accept-process-output p 1)
1323 (unless (process-live-p p)
1324 (tramp-error
1325 p 'file-notify-error "Monitoring not supported for `%s'" file-name))
1326 p))))
1328 (defun tramp-gvfs-monitor-process-filter (proc string)
1329 "Read output from \"gvfs-monitor-file\" and add corresponding \
1330 file-notify events."
1331 (let* ((events (process-get proc 'events))
1332 (rest-string (process-get proc 'rest-string))
1333 (dd (with-current-buffer (process-buffer proc) default-directory))
1334 (ddu (regexp-quote (tramp-gvfs-url-file-name dd))))
1335 (when rest-string
1336 (tramp-message proc 10 "Previous string:\n%s" rest-string))
1337 (tramp-message proc 6 "%S\n%s" proc string)
1338 (setq string (concat rest-string string)
1339 ;; Fix action names.
1340 string (replace-regexp-in-string
1341 "attributes changed" "attribute-changed" string)
1342 string (replace-regexp-in-string
1343 "changes done" "changes-done-hint" string)
1344 string (replace-regexp-in-string
1345 "renamed to" "moved" string))
1346 ;; https://bugs.launchpad.net/bugs/1742946
1347 (when (string-match "Monitoring not supported\\|No locations given" string)
1348 (delete-process proc))
1350 (while (string-match
1351 (concat "^.+:"
1352 "[[:space:]]\\(.+\\):"
1353 "[[:space:]]" (regexp-opt tramp-gio-events t)
1354 "\\([[:space:]]\\(.+\\)\\)?$")
1355 string)
1357 (let ((file (match-string 1 string))
1358 (file1 (match-string 4 string))
1359 (action (intern-soft (match-string 2 string))))
1360 (setq string (replace-match "" nil nil string))
1361 ;; File names are returned as URL paths. We must convert them.
1362 (when (string-match ddu file)
1363 (setq file (replace-match dd nil nil file)))
1364 (while (string-match "%\\([0-9A-F]\\{2\\}\\)" file)
1365 (setq file (url-unhex-string file)))
1366 (when (string-match ddu (or file1 ""))
1367 (setq file1 (replace-match dd nil nil file1)))
1368 (while (string-match "%\\([0-9A-F]\\{2\\}\\)" (or file1 ""))
1369 (setq file1 (url-unhex-string file1)))
1370 ;; Remove watch when file or directory to be watched is deleted.
1371 (when (and (member action '(moved deleted))
1372 (string-equal file (process-get proc 'watch-name)))
1373 (delete-process proc))
1374 ;; Usually, we would add an Emacs event now. Unfortunately,
1375 ;; `unread-command-events' does not accept several events at
1376 ;; once. Therefore, we apply the callback directly.
1377 (when (member action events)
1378 (tramp-compat-funcall
1379 'file-notify-callback (list proc action file file1)))))
1381 ;; Save rest of the string.
1382 (when (zerop (length string)) (setq string nil))
1383 (when string (tramp-message proc 10 "Rest string:\n%s" string))
1384 (process-put proc 'rest-string string)))
1386 (defun tramp-gvfs-handle-file-readable-p (filename)
1387 "Like `file-readable-p' for Tramp files."
1388 (with-parsed-tramp-file-name filename nil
1389 (with-tramp-file-property v localname "file-readable-p"
1390 (tramp-check-cached-permissions v ?r))))
1392 (defun tramp-gvfs-handle-file-system-info (filename)
1393 "Like `file-system-info' for Tramp files."
1394 (setq filename (directory-file-name (expand-file-name filename)))
1395 (with-parsed-tramp-file-name filename nil
1396 ;; We don't use cached values.
1397 (tramp-flush-file-property v localname "file-system-attributes")
1398 (let* ((attr (tramp-gvfs-get-root-attributes filename 'file-system))
1399 (size (cdr (assoc "filesystem::size" attr)))
1400 (used (cdr (assoc "filesystem::used" attr)))
1401 (free (cdr (assoc "filesystem::free" attr))))
1402 (when (and (stringp size) (stringp used) (stringp free))
1403 (list (string-to-number (concat size "e0"))
1404 (- (string-to-number (concat size "e0"))
1405 (string-to-number (concat used "e0")))
1406 (string-to-number (concat free "e0")))))))
1408 (defun tramp-gvfs-handle-file-writable-p (filename)
1409 "Like `file-writable-p' for Tramp files."
1410 (with-parsed-tramp-file-name filename nil
1411 (with-tramp-file-property v localname "file-writable-p"
1412 (if (file-exists-p filename)
1413 (tramp-check-cached-permissions v ?w)
1414 ;; If file doesn't exist, check if directory is writable.
1415 (and (file-directory-p (file-name-directory filename))
1416 (file-writable-p (file-name-directory filename)))))))
1418 (defun tramp-gvfs-handle-make-directory (dir &optional parents)
1419 "Like `make-directory' for Tramp files."
1420 (setq dir (directory-file-name (expand-file-name dir)))
1421 (with-parsed-tramp-file-name dir nil
1422 (tramp-flush-file-properties v (file-name-directory localname))
1423 (tramp-flush-directory-properties v localname)
1424 (save-match-data
1425 (let ((ldir (file-name-directory dir)))
1426 ;; Make missing directory parts. "gvfs-mkdir -p ..." does not
1427 ;; work robust.
1428 (when (and parents (not (file-directory-p ldir)))
1429 (make-directory ldir parents))
1430 ;; Just do it.
1431 (unless (or (tramp-gvfs-send-command
1432 v "gvfs-mkdir" (tramp-gvfs-url-file-name dir))
1433 (and parents (file-directory-p dir)))
1434 (tramp-error v 'file-error "Couldn't make directory %s" dir))))))
1436 (defun tramp-gvfs-handle-rename-file
1437 (filename newname &optional ok-if-already-exists)
1438 "Like `rename-file' for Tramp files."
1439 ;; Check if both files are local -- invoke normal rename-file.
1440 ;; Otherwise, use Tramp from local system.
1441 (setq filename (expand-file-name filename))
1442 (setq newname (expand-file-name newname))
1443 ;; At least one file a Tramp file?
1444 (if (or (tramp-tramp-file-p filename)
1445 (tramp-tramp-file-p newname))
1446 (tramp-gvfs-do-copy-or-rename-file
1447 'rename filename newname ok-if-already-exists
1448 'keep-date 'preserve-uid-gid)
1449 (tramp-run-real-handler
1450 'rename-file (list filename newname ok-if-already-exists))))
1452 (defun tramp-gvfs-handle-write-region
1453 (start end filename &optional append visit lockname mustbenew)
1454 "Like `write-region' for Tramp files."
1455 (setq filename (expand-file-name filename))
1456 (with-parsed-tramp-file-name filename nil
1457 (when (and mustbenew (file-exists-p filename)
1458 (or (eq mustbenew 'excl)
1459 (not
1460 (y-or-n-p
1461 (format "File %s exists; overwrite anyway? " filename)))))
1462 (tramp-error v 'file-already-exists filename))
1464 (let ((tmpfile (tramp-compat-make-temp-file filename)))
1465 (when (and append (file-exists-p filename))
1466 (copy-file filename tmpfile 'ok))
1467 ;; We say `no-message' here because we don't want the visited file
1468 ;; modtime data to be clobbered from the temp file. We call
1469 ;; `set-visited-file-modtime' ourselves later on.
1470 (tramp-run-real-handler
1471 'write-region (list start end tmpfile append 'no-message lockname))
1472 (condition-case nil
1473 (rename-file tmpfile filename 'ok-if-already-exists)
1474 (error
1475 (delete-file tmpfile)
1476 (tramp-error
1477 v 'file-error "Couldn't write region to `%s'" filename))))
1479 (tramp-flush-file-properties v (file-name-directory localname))
1480 (tramp-flush-file-properties v localname)
1482 ;; Set file modification time.
1483 (when (or (eq visit t) (stringp visit))
1484 (set-visited-file-modtime
1485 (tramp-compat-file-attribute-modification-time
1486 (file-attributes filename))))
1488 ;; The end.
1489 (when (and (null noninteractive)
1490 (or (eq visit t) (null visit) (stringp visit)))
1491 (tramp-message v 0 "Wrote %s" filename))
1492 (run-hooks 'tramp-handle-write-region-hook)))
1495 ;; File name conversions.
1497 (defun tramp-gvfs-url-file-name (filename)
1498 "Return FILENAME in URL syntax."
1499 ;; "/" must NOT be hexified.
1500 (setq filename (tramp-compat-file-name-unquote filename))
1501 (let ((url-unreserved-chars (cons ?/ url-unreserved-chars))
1502 result)
1503 (setq
1504 result
1505 (url-recreate-url
1506 (if (tramp-tramp-file-p filename)
1507 (with-parsed-tramp-file-name filename nil
1508 (when (string-equal "gdrive" method)
1509 (setq method "google-drive"))
1510 (when (string-equal "owncloud" method)
1511 (setq method "davs"
1512 localname
1513 (concat (tramp-gvfs-get-remote-prefix v) localname)))
1514 (when (and user domain)
1515 (setq user (concat domain ";" user)))
1516 (url-parse-make-urlobj
1517 method (and user (url-hexify-string user))
1518 nil (and host (url-hexify-string host))
1519 (if (stringp port) (string-to-number port) port)
1520 (and localname (url-hexify-string localname)) nil nil t))
1521 (url-parse-make-urlobj
1522 "file" nil nil nil nil
1523 (url-hexify-string (file-truename filename)) nil nil t))))
1524 (when (tramp-tramp-file-p filename)
1525 (with-parsed-tramp-file-name filename nil
1526 (tramp-message v 10 "remote file `%s' is URL `%s'" filename result)))
1527 result))
1529 (defun tramp-gvfs-object-path (filename)
1530 "Create a D-Bus object path from FILENAME."
1531 (expand-file-name (dbus-escape-as-identifier filename) tramp-gvfs-path-tramp))
1533 (defun tramp-gvfs-file-name (object-path)
1534 "Retrieve file name from D-Bus OBJECT-PATH."
1535 (dbus-unescape-from-identifier
1536 (replace-regexp-in-string "^.*/\\([^/]+\\)$" "\\1" object-path)))
1539 ;; D-Bus GVFS functions.
1541 (defun tramp-gvfs-handler-askpassword (message user domain flags)
1542 "Implementation for the \"org.gtk.vfs.MountOperation.askPassword\" method."
1543 (let* ((filename
1544 (tramp-gvfs-file-name (dbus-event-path-name last-input-event)))
1545 (pw-prompt
1546 (format
1547 "%s for %s "
1548 (if (string-match "\\([pP]assword\\|[pP]assphrase\\)" message)
1549 (capitalize (match-string 1 message))
1550 "Password")
1551 filename))
1552 password)
1554 (condition-case nil
1555 (with-parsed-tramp-file-name filename l
1556 (when (and (zerop (length user))
1557 (not
1558 (zerop (logand flags tramp-gvfs-password-need-username))))
1559 (setq user (read-string "User name: ")))
1560 (when (and (zerop (length domain))
1561 (not
1562 (zerop (logand flags tramp-gvfs-password-need-domain))))
1563 (setq domain (read-string "Domain name: ")))
1565 (tramp-message l 6 "%S %S %S %d" message user domain flags)
1566 (unless (tramp-get-connection-property l "first-password-request" nil)
1567 (tramp-clear-passwd l))
1569 (setq password (tramp-read-passwd
1570 (tramp-get-connection-process l) pw-prompt))
1572 ;; Return result.
1573 (if (stringp password)
1574 (list
1575 t ;; password handled.
1576 nil ;; no abort of D-Bus.
1577 password
1578 (tramp-file-name-user l)
1579 domain
1580 nil ;; not anonymous.
1581 0) ;; no password save.
1582 ;; No password provided.
1583 (list nil t "" (tramp-file-name-user l) domain nil 0)))
1585 ;; When QUIT is raised, we shall return this information to D-Bus.
1586 (quit (list nil t "" "" "" nil 0)))))
1588 (defun tramp-gvfs-handler-askquestion (message choices)
1589 "Implementation for the \"org.gtk.vfs.MountOperation.askQuestion\" method."
1590 (save-window-excursion
1591 (let ((enable-recursive-minibuffers t)
1592 (use-dialog-box (and use-dialog-box (null noninteractive)))
1593 result)
1595 (with-parsed-tramp-file-name
1596 (tramp-gvfs-file-name (dbus-event-path-name last-input-event)) nil
1597 (tramp-message v 6 "%S %S" message choices)
1599 (setq result
1600 (condition-case nil
1601 (list
1602 t ;; handled.
1603 nil ;; no abort of D-Bus.
1604 (with-tramp-connection-property
1605 (tramp-get-connection-process v) message
1606 ;; In theory, there can be several choices.
1607 ;; Until now, there is only the question whether
1608 ;; to accept an unknown host signature or certificate.
1609 (with-temp-buffer
1610 ;; Preserve message for `progress-reporter'.
1611 (with-temp-message ""
1612 (insert message)
1613 (goto-char (point-max))
1614 (if noninteractive
1615 (message "%s" message)
1616 (pop-to-buffer (current-buffer)))
1617 (if (yes-or-no-p
1618 (concat
1619 (buffer-substring
1620 (line-beginning-position) (point))
1621 " "))
1622 0 1)))))
1624 ;; When QUIT is raised, we shall return this
1625 ;; information to D-Bus.
1626 (quit (list nil t 1))))
1628 (tramp-message v 6 "%s" result)
1630 ;; When the choice is "no", we set a dummy fuse-mountpoint in
1631 ;; order to leave the timeout.
1632 (unless (zerop (cl-caddr result))
1633 (tramp-set-file-property v "/" "fuse-mountpoint" "/"))
1635 result))))
1637 (defun tramp-gvfs-handler-mounted-unmounted (mount-info)
1638 "Signal handler for the \"org.gtk.vfs.MountTracker.mounted\" and
1639 \"org.gtk.vfs.MountTracker.unmounted\" signals."
1640 (ignore-errors
1641 (let ((signal-name (dbus-event-member-name last-input-event))
1642 (elt mount-info))
1643 ;; Jump over the first elements of the mount info. Since there
1644 ;; were changes in the entries, we cannot access dedicated
1645 ;; elements.
1646 (while (stringp (car elt)) (setq elt (cdr elt)))
1647 (let* ((fuse-mountpoint (tramp-gvfs-dbus-byte-array-to-string (cadr elt)))
1648 (mount-spec (cl-caddr elt))
1649 (prefix (tramp-gvfs-dbus-byte-array-to-string (car mount-spec)))
1650 (default-location (tramp-gvfs-dbus-byte-array-to-string
1651 (cl-cadddr elt)))
1652 (method (tramp-gvfs-dbus-byte-array-to-string
1653 (cadr (assoc "type" (cadr mount-spec)))))
1654 (user (tramp-gvfs-dbus-byte-array-to-string
1655 (cadr (assoc "user" (cadr mount-spec)))))
1656 (domain (tramp-gvfs-dbus-byte-array-to-string
1657 (cadr (assoc "domain" (cadr mount-spec)))))
1658 (host (tramp-gvfs-dbus-byte-array-to-string
1659 (cadr (or (assoc "host" (cadr mount-spec))
1660 (assoc "server" (cadr mount-spec))))))
1661 (port (tramp-gvfs-dbus-byte-array-to-string
1662 (cadr (assoc "port" (cadr mount-spec)))))
1663 (ssl (tramp-gvfs-dbus-byte-array-to-string
1664 (cadr (assoc "ssl" (cadr mount-spec)))))
1665 (uri (tramp-gvfs-dbus-byte-array-to-string
1666 (cadr (assoc "uri" (cadr mount-spec))))))
1667 (when (string-match "^\\(afp\\|smb\\)" method)
1668 (setq method (match-string 1 method)))
1669 (when (string-equal "obex" method)
1670 (setq host (tramp-bluez-device host)))
1671 (when (and (string-equal "dav" method) (string-equal "true" ssl))
1672 (setq method "davs"))
1673 (when (and (string-equal "davs" method)
1674 (string-match
1675 tramp-gvfs-owncloud-default-prefix-regexp prefix))
1676 (setq method "owncloud"))
1677 (when (string-equal "google-drive" method)
1678 (setq method "gdrive"))
1679 (when (and (string-equal "http" method) (stringp uri))
1680 (setq uri (url-generic-parse-url uri)
1681 method (url-type uri)
1682 user (url-user uri)
1683 host (url-host uri)
1684 port (url-portspec uri)))
1685 (with-parsed-tramp-file-name
1686 (tramp-make-tramp-file-name method user domain host port "") nil
1687 (tramp-message
1688 v 6 "%s %s"
1689 signal-name (tramp-gvfs-stringify-dbus-message mount-info))
1690 (tramp-flush-file-property v "/" "list-mounts")
1691 (if (string-equal (downcase signal-name) "unmounted")
1692 (tramp-flush-file-properties v "/")
1693 ;; Set mountpoint and location.
1694 (tramp-set-file-property v "/" "fuse-mountpoint" fuse-mountpoint)
1695 (tramp-set-connection-property
1696 v "default-location" default-location)))))))
1698 (when tramp-gvfs-enabled
1699 (dbus-register-signal
1700 :session nil tramp-gvfs-path-mounttracker
1701 tramp-gvfs-interface-mounttracker "mounted"
1702 'tramp-gvfs-handler-mounted-unmounted)
1703 (dbus-register-signal
1704 :session nil tramp-gvfs-path-mounttracker
1705 tramp-gvfs-interface-mounttracker "Mounted"
1706 'tramp-gvfs-handler-mounted-unmounted)
1708 (dbus-register-signal
1709 :session nil tramp-gvfs-path-mounttracker
1710 tramp-gvfs-interface-mounttracker "unmounted"
1711 'tramp-gvfs-handler-mounted-unmounted)
1712 (dbus-register-signal
1713 :session nil tramp-gvfs-path-mounttracker
1714 tramp-gvfs-interface-mounttracker "Unmounted"
1715 'tramp-gvfs-handler-mounted-unmounted))
1717 (defun tramp-gvfs-connection-mounted-p (vec)
1718 "Check, whether the location is already mounted."
1720 (tramp-get-file-property vec "/" "fuse-mountpoint" nil)
1721 (catch 'mounted
1722 (dolist
1723 (elt
1724 (with-tramp-file-property vec "/" "list-mounts"
1725 (with-tramp-dbus-call-method vec t
1726 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
1727 tramp-gvfs-interface-mounttracker tramp-gvfs-listmounts))
1728 nil)
1729 ;; Jump over the first elements of the mount info. Since there
1730 ;; were changes in the entries, we cannot access dedicated
1731 ;; elements.
1732 (while (stringp (car elt)) (setq elt (cdr elt)))
1733 (let* ((fuse-mountpoint (tramp-gvfs-dbus-byte-array-to-string
1734 (cadr elt)))
1735 (mount-spec (cl-caddr elt))
1736 (prefix (tramp-gvfs-dbus-byte-array-to-string (car mount-spec)))
1737 (default-location (tramp-gvfs-dbus-byte-array-to-string
1738 (cl-cadddr elt)))
1739 (method (tramp-gvfs-dbus-byte-array-to-string
1740 (cadr (assoc "type" (cadr mount-spec)))))
1741 (user (tramp-gvfs-dbus-byte-array-to-string
1742 (cadr (assoc "user" (cadr mount-spec)))))
1743 (domain (tramp-gvfs-dbus-byte-array-to-string
1744 (cadr (assoc "domain" (cadr mount-spec)))))
1745 (host (tramp-gvfs-dbus-byte-array-to-string
1746 (cadr (or (assoc "host" (cadr mount-spec))
1747 (assoc "server" (cadr mount-spec))))))
1748 (port (tramp-gvfs-dbus-byte-array-to-string
1749 (cadr (assoc "port" (cadr mount-spec)))))
1750 (ssl (tramp-gvfs-dbus-byte-array-to-string
1751 (cadr (assoc "ssl" (cadr mount-spec)))))
1752 (uri (tramp-gvfs-dbus-byte-array-to-string
1753 (cadr (assoc "uri" (cadr mount-spec)))))
1754 (share (tramp-gvfs-dbus-byte-array-to-string
1756 (cadr (assoc "share" (cadr mount-spec)))
1757 (cadr (assoc "volume" (cadr mount-spec)))))))
1758 (when (string-match "^\\(afp\\|smb\\)" method)
1759 (setq method (match-string 1 method)))
1760 (when (string-equal "obex" method)
1761 (setq host (tramp-bluez-device host)))
1762 (when (and (string-equal "dav" method) (string-equal "true" ssl))
1763 (setq method "davs"))
1764 (when (and (string-equal "davs" method)
1765 (string-match
1766 tramp-gvfs-owncloud-default-prefix-regexp prefix))
1767 (setq method "owncloud"))
1768 (when (string-equal "google-drive" method)
1769 (setq method "gdrive"))
1770 (when (and (string-equal "synce" method) (zerop (length user)))
1771 (setq user (or (tramp-file-name-user vec) "")))
1772 (when (and (string-equal "http" method) (stringp uri))
1773 (setq uri (url-generic-parse-url uri)
1774 method (url-type uri)
1775 user (url-user uri)
1776 host (url-host uri)
1777 port (url-portspec uri)))
1778 (when (and
1779 (string-equal method (tramp-file-name-method vec))
1780 (string-equal user (tramp-file-name-user vec))
1781 (string-equal domain (tramp-file-name-domain vec))
1782 (string-equal host (tramp-file-name-host vec))
1783 (string-equal port (tramp-file-name-port vec))
1784 (string-match (concat "^/" (regexp-quote (or share "")))
1785 (tramp-file-name-unquote-localname vec)))
1786 ;; Set mountpoint and location.
1787 (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
1788 (tramp-set-connection-property
1789 vec "default-location" default-location)
1790 (throw 'mounted t)))))))
1792 (defun tramp-gvfs-unmount (vec)
1793 "Unmount the object identified by VEC."
1794 (setf (tramp-file-name-localname vec) "/"
1795 (tramp-file-name-hop vec) nil)
1796 (when (tramp-gvfs-connection-mounted-p vec)
1797 (tramp-gvfs-send-command
1798 vec "gvfs-mount" "-u"
1799 (tramp-gvfs-url-file-name (tramp-make-tramp-file-name vec))))
1800 (while (tramp-gvfs-connection-mounted-p vec)
1801 (read-event nil nil 0.1))
1802 (tramp-flush-connection-properties vec)
1803 (tramp-flush-connection-properties (tramp-get-connection-process vec)))
1805 (defun tramp-gvfs-mount-spec-entry (key value)
1806 "Construct a mount-spec entry to be used in a mount_spec.
1807 It was \"a(say)\", but has changed to \"a{sv})\"."
1808 (if (string-match "^(aya{sv})" tramp-gvfs-mountlocation-signature)
1809 (list :dict-entry key
1810 (list :variant (tramp-gvfs-dbus-string-to-byte-array value)))
1811 (list :struct key (tramp-gvfs-dbus-string-to-byte-array value))))
1813 (defun tramp-gvfs-mount-spec (vec)
1814 "Return a mount-spec for \"org.gtk.vfs.MountTracker.mountLocation\"."
1815 (let* ((method (tramp-file-name-method vec))
1816 (user (tramp-file-name-user vec))
1817 (domain (tramp-file-name-domain vec))
1818 (host (tramp-file-name-host vec))
1819 (port (tramp-file-name-port vec))
1820 (localname (tramp-file-name-unquote-localname vec))
1821 (share (when (string-match "^/?\\([^/]+\\)" localname)
1822 (match-string 1 localname)))
1823 (ssl (if (string-match "^davs\\|^owncloud" method) "true" "false"))
1824 (mount-spec
1825 `(:array
1826 ,@(cond
1827 ((string-equal "smb" method)
1828 (list (tramp-gvfs-mount-spec-entry "type" "smb-share")
1829 (tramp-gvfs-mount-spec-entry "server" host)
1830 (tramp-gvfs-mount-spec-entry "share" share)))
1831 ((string-equal "obex" method)
1832 (list (tramp-gvfs-mount-spec-entry "type" method)
1833 (tramp-gvfs-mount-spec-entry
1834 "host" (concat "[" (tramp-bluez-address host) "]"))))
1835 ((string-match "^dav\\|^owncloud" method)
1836 (list (tramp-gvfs-mount-spec-entry "type" "dav")
1837 (tramp-gvfs-mount-spec-entry "host" host)
1838 (tramp-gvfs-mount-spec-entry "ssl" ssl)))
1839 ((string-equal "afp" method)
1840 (list (tramp-gvfs-mount-spec-entry "type" "afp-volume")
1841 (tramp-gvfs-mount-spec-entry "host" host)
1842 (tramp-gvfs-mount-spec-entry "volume" share)))
1843 ((string-equal "gdrive" method)
1844 (list (tramp-gvfs-mount-spec-entry "type" "google-drive")
1845 (tramp-gvfs-mount-spec-entry "host" host)))
1846 ((string-match "^http" method)
1847 (list (tramp-gvfs-mount-spec-entry "type" "http")
1848 (tramp-gvfs-mount-spec-entry
1849 "uri"
1850 (url-recreate-url
1851 (url-parse-make-urlobj
1852 method user nil host port "/" nil nil t)))))
1854 (list (tramp-gvfs-mount-spec-entry "type" method)
1855 (tramp-gvfs-mount-spec-entry "host" host))))
1856 ,@(when user
1857 (list (tramp-gvfs-mount-spec-entry "user" user)))
1858 ,@(when domain
1859 (list (tramp-gvfs-mount-spec-entry "domain" domain)))
1860 ,@(when port
1861 (list (tramp-gvfs-mount-spec-entry "port" port)))))
1862 (mount-pref
1863 (if (and (string-match "^dav" method)
1864 (string-match "^/?[^/]+" localname))
1865 (match-string 0 localname)
1866 (tramp-gvfs-get-remote-prefix vec))))
1868 ;; Return.
1869 `(:struct ,(tramp-gvfs-dbus-string-to-byte-array mount-pref) ,mount-spec)))
1872 ;; Connection functions.
1874 (defun tramp-gvfs-get-remote-uid (vec id-format)
1875 "The uid of the remote connection VEC, in ID-FORMAT.
1876 ID-FORMAT valid values are `string' and `integer'."
1877 (with-tramp-connection-property vec (format "uid-%s" id-format)
1878 (let ((method (tramp-file-name-method vec))
1879 (user (tramp-file-name-user vec))
1880 (domain (tramp-file-name-domain vec))
1881 (host (tramp-file-name-host vec))
1882 (port (tramp-file-name-port vec))
1883 (localname
1884 (tramp-get-connection-property vec "default-location" nil)))
1885 (cond
1886 ((and user (equal id-format 'string)) user)
1887 (localname
1888 (tramp-compat-file-attribute-user-id
1889 (file-attributes
1890 (tramp-make-tramp-file-name method user domain host port localname)
1891 id-format)))
1892 ((equal id-format 'integer) tramp-unknown-id-integer)
1893 ((equal id-format 'string) tramp-unknown-id-string)))))
1895 (defun tramp-gvfs-get-remote-gid (vec id-format)
1896 "The gid of the remote connection VEC, in ID-FORMAT.
1897 ID-FORMAT valid values are `string' and `integer'."
1898 (with-tramp-connection-property vec (format "gid-%s" id-format)
1899 (let ((method (tramp-file-name-method vec))
1900 (user (tramp-file-name-user vec))
1901 (domain (tramp-file-name-domain vec))
1902 (host (tramp-file-name-host vec))
1903 (port (tramp-file-name-port vec))
1904 (localname
1905 (tramp-get-connection-property vec "default-location" nil)))
1906 (cond
1907 (localname
1908 (tramp-compat-file-attribute-group-id
1909 (file-attributes
1910 (tramp-make-tramp-file-name method user domain host port localname)
1911 id-format)))
1912 ((equal id-format 'integer) tramp-unknown-id-integer)
1913 ((equal id-format 'string) tramp-unknown-id-string)))))
1915 (defvar tramp-gvfs-get-remote-uid-gid-in-progress nil
1916 "Indication, that remote uid and gid determination is in progress.")
1918 (defun tramp-gvfs-get-remote-prefix (vec)
1919 "The prefix of the remote connection VEC.
1920 This is relevant for GNOME Online Accounts."
1921 (with-tramp-connection-property vec "prefix"
1922 ;; Ensure that GNOME Online Accounts are cached.
1923 (when (member (tramp-file-name-method vec) tramp-goa-methods)
1924 (tramp-get-goa-accounts vec))
1925 (tramp-get-connection-property
1926 (make-tramp-goa-name
1927 :method (tramp-file-name-method vec)
1928 :user (tramp-file-name-user vec)
1929 :host (tramp-file-name-host vec)
1930 :port (tramp-file-name-port vec))
1931 "prefix" "/")))
1933 (defun tramp-gvfs-maybe-open-connection (vec)
1934 "Maybe open a connection VEC.
1935 Does not do anything if a connection is already open, but re-opens the
1936 connection if a previous connection has died for some reason."
1937 ;; We set the file name, in case there are incoming D-Bus signals or
1938 ;; D-Bus errors.
1939 (setq tramp-gvfs-dbus-event-vector vec)
1941 ;; For password handling, we need a process bound to the connection
1942 ;; buffer. Therefore, we create a dummy process. Maybe there is a
1943 ;; better solution?
1944 (unless (get-buffer-process (tramp-get-connection-buffer vec))
1945 (let ((p (make-network-process
1946 :name (tramp-buffer-name vec)
1947 :buffer (tramp-get-connection-buffer vec)
1948 :server t :host 'local :service t :noquery t)))
1949 (process-put p 'vector vec)
1950 (set-process-query-on-exit-flag p nil)))
1952 (unless (tramp-gvfs-connection-mounted-p vec)
1953 (let* ((method (tramp-file-name-method vec))
1954 (user (tramp-file-name-user vec))
1955 (domain (tramp-file-name-domain vec))
1956 (host (tramp-file-name-host vec))
1957 (port (tramp-file-name-port vec))
1958 (localname (tramp-file-name-unquote-localname vec))
1959 (object-path
1960 (tramp-gvfs-object-path
1961 (tramp-make-tramp-file-name method user domain host port ""))))
1963 (when (and (string-equal method "afp")
1964 (string-equal localname "/"))
1965 (tramp-error vec 'file-error "Filename must contain an AFP volume"))
1967 (when (and (string-match method "davs?")
1968 (string-equal localname "/"))
1969 (tramp-error vec 'file-error "Filename must contain a WebDAV share"))
1971 (when (and (string-equal method "smb")
1972 (string-equal localname "/"))
1973 (tramp-error vec 'file-error "Filename must contain a Windows share"))
1975 (with-tramp-progress-reporter
1976 vec 3
1977 (if (zerop (length user))
1978 (format "Opening connection for %s using %s" host method)
1979 (format "Opening connection for %s@%s using %s" user host method))
1981 ;; Enable `auth-source'.
1982 (tramp-set-connection-property
1983 vec "first-password-request" tramp-cache-read-persistent-data)
1985 ;; There will be a callback of "askPassword" when a password is needed.
1986 (dbus-register-method
1987 :session dbus-service-emacs object-path
1988 tramp-gvfs-interface-mountoperation "askPassword"
1989 'tramp-gvfs-handler-askpassword)
1990 (dbus-register-method
1991 :session dbus-service-emacs object-path
1992 tramp-gvfs-interface-mountoperation "AskPassword"
1993 'tramp-gvfs-handler-askpassword)
1995 ;; There could be a callback of "askQuestion" when adding
1996 ;; fingerprints or checking certificates.
1997 (dbus-register-method
1998 :session dbus-service-emacs object-path
1999 tramp-gvfs-interface-mountoperation "askQuestion"
2000 'tramp-gvfs-handler-askquestion)
2001 (dbus-register-method
2002 :session dbus-service-emacs object-path
2003 tramp-gvfs-interface-mountoperation "AskQuestion"
2004 'tramp-gvfs-handler-askquestion)
2006 ;; The call must be asynchronously, because of the "askPassword"
2007 ;; or "askQuestion" callbacks.
2008 (if (string-match "(so)$" tramp-gvfs-mountlocation-signature)
2009 (with-tramp-dbus-call-method vec nil
2010 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
2011 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation
2012 (tramp-gvfs-mount-spec vec)
2013 `(:struct :string ,(dbus-get-unique-name :session)
2014 :object-path ,object-path))
2015 (with-tramp-dbus-call-method vec nil
2016 :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
2017 tramp-gvfs-interface-mounttracker tramp-gvfs-mountlocation
2018 (tramp-gvfs-mount-spec vec)
2019 :string (dbus-get-unique-name :session) :object-path object-path))
2021 ;; We must wait, until the mount is applied. This will be
2022 ;; indicated by the "mounted" signal, i.e. the "fuse-mountpoint"
2023 ;; file property.
2024 (with-timeout
2025 ((or (tramp-get-method-parameter vec 'tramp-connection-timeout)
2026 tramp-connection-timeout)
2027 (if (zerop (length (tramp-file-name-user vec)))
2028 (tramp-error
2029 vec 'file-error
2030 "Timeout reached mounting %s using %s" host method)
2031 (tramp-error
2032 vec 'file-error
2033 "Timeout reached mounting %s@%s using %s" user host method)))
2034 (while (not (tramp-get-file-property vec "/" "fuse-mountpoint" nil))
2035 (read-event nil nil 0.1)))
2037 ;; If `tramp-gvfs-handler-askquestion' has returned "No", it
2038 ;; is marked with the fuse-mountpoint "/". We shall react.
2039 (when (string-equal
2040 (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/")
2041 (tramp-error vec 'file-error "FUSE mount denied"))
2043 ;; Set connection-local variables.
2044 (tramp-set-connection-local-variables vec)
2046 ;; Mark it as connected.
2047 (tramp-set-connection-property
2048 (tramp-get-connection-process vec) "connected" t))))
2050 ;; In `tramp-check-cached-permissions', the connection properties
2051 ;; {uig,gid}-{integer,string} are used. We set them to proper values.
2052 (unless tramp-gvfs-get-remote-uid-gid-in-progress
2053 (let ((tramp-gvfs-get-remote-uid-gid-in-progress t))
2054 (tramp-gvfs-get-remote-uid vec 'integer)
2055 (tramp-gvfs-get-remote-gid vec 'integer)
2056 (tramp-gvfs-get-remote-uid vec 'string)
2057 (tramp-gvfs-get-remote-gid vec 'string))))
2059 (defun tramp-gvfs-gio-tool-p (vec)
2060 "Check, whether the gio tool is available."
2061 (with-tramp-connection-property vec "gio-tool"
2062 (zerop (tramp-call-process vec "gio" nil nil nil "version"))))
2064 (defun tramp-gvfs-send-command (vec command &rest args)
2065 "Send the COMMAND with its ARGS to connection VEC.
2066 COMMAND is a command from the gvfs-* utilities. It is replaced
2067 by the corresponding gio tool call if available. `call-process'
2068 is applied, and it returns t if the return code is zero."
2069 (let* ((locale (tramp-get-local-locale vec))
2070 (process-environment
2071 (append
2072 `(,(format "LANG=%s" locale)
2073 ,(format "LANGUAGE=%s" locale)
2074 ,(format "LC_ALL=%s" locale))
2075 process-environment)))
2076 (when (tramp-gvfs-gio-tool-p vec)
2077 ;; Use gio tool.
2078 (setq args (cons (cdr (assoc command tramp-gvfs-gio-mapping)) args)
2079 command "gio"))
2081 (with-current-buffer (tramp-get-connection-buffer vec)
2082 (tramp-gvfs-maybe-open-connection vec)
2083 (erase-buffer)
2084 (or (zerop (apply 'tramp-call-process vec command nil t nil args))
2085 ;; Remove information about mounted connection.
2086 (and (tramp-flush-file-properties vec "/") nil)))))
2089 ;; D-Bus GNOME Online Accounts functions.
2091 (defun tramp-get-goa-accounts (vec)
2092 "Retrieve GNOME Online Accounts, and cache them.
2093 The hash key is a `tramp-goa-name' structure. The value is an
2094 alist of the properties of `tramp-goa-interface-account' and
2095 `tramp-goa-interface-files' of the corresponding GNOME online
2096 account. Additionally, a property \"prefix\" is added.
2097 VEC is used only for traces."
2098 (dolist
2099 (object-path
2100 (mapcar
2101 'car
2102 (tramp-dbus-function
2103 vec 'dbus-get-all-managed-objects
2104 `(:session ,tramp-goa-service ,tramp-goa-path))))
2105 (let* ((account-properties
2106 (with-tramp-dbus-get-all-properties vec
2107 :session tramp-goa-service object-path
2108 tramp-goa-interface-account))
2109 (files-properties
2110 (with-tramp-dbus-get-all-properties vec
2111 :session tramp-goa-service object-path
2112 tramp-goa-interface-files))
2113 (identity
2114 (or (cdr (assoc "PresentationIdentity" account-properties)) ""))
2115 key)
2116 ;; Only accounts which matter.
2117 (when (and
2118 (not (cdr (assoc "FilesDisabled" account-properties)))
2119 (member
2120 (cdr (assoc "ProviderType" account-properties))
2121 '("google" "owncloud"))
2122 (string-match tramp-goa-identity-regexp identity))
2123 (setq key (make-tramp-goa-name
2124 :method (cdr (assoc "ProviderType" account-properties))
2125 :user (match-string 1 identity)
2126 :host (match-string 2 identity)
2127 :port (match-string 3 identity)))
2128 (when (string-equal (tramp-goa-name-method key) "google")
2129 (setf (tramp-goa-name-method key) "gdrive"))
2130 ;; Cache all properties.
2131 (dolist (prop (nconc account-properties files-properties))
2132 (tramp-set-connection-property key (car prop) (cdr prop)))
2133 ;; Cache "prefix".
2134 (tramp-message
2135 vec 10 "%s prefix %s" key
2136 (tramp-set-connection-property
2137 key "prefix"
2138 (directory-file-name
2139 (url-filename
2140 (url-generic-parse-url
2141 (tramp-get-connection-property key "Uri" "file:///"))))))))))
2144 ;; D-Bus BLUEZ functions.
2146 (defun tramp-bluez-address (device)
2147 "Return bluetooth device address from a given bluetooth DEVICE name."
2148 (when (stringp device)
2149 (if (string-match tramp-ipv6-regexp device)
2150 (match-string 0 device)
2151 (cadr (assoc device (tramp-bluez-list-devices))))))
2153 (defun tramp-bluez-device (address)
2154 "Return bluetooth device name from a given bluetooth device ADDRESS.
2155 ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
2156 (when (stringp address)
2157 (while (string-match "[][]" address)
2158 (setq address (replace-match "" t t address)))
2159 (let (result)
2160 (dolist (item (tramp-bluez-list-devices) result)
2161 (when (string-match address (cadr item))
2162 (setq result (car item)))))))
2164 (defun tramp-bluez-list-devices ()
2165 "Return all discovered bluetooth devices as list.
2166 Every entry is a list (NAME ADDRESS).
2168 If `tramp-bluez-discover-devices-timeout' is an integer, and the last
2169 discovery happened more time before indicated there, a rescan will be
2170 started, which lasts some ten seconds. Otherwise, cached results will
2171 be used."
2172 ;; Reset the scanned devices list if time has passed.
2173 (and (integerp tramp-bluez-discover-devices-timeout)
2174 (integerp tramp-bluez-discovery)
2175 (> (tramp-time-diff (current-time) tramp-bluez-discovery)
2176 tramp-bluez-discover-devices-timeout)
2177 (setq tramp-bluez-devices nil))
2179 ;; Rescan if needed.
2180 (unless tramp-bluez-devices
2181 (let ((object-path
2182 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
2183 :system tramp-bluez-service "/"
2184 tramp-bluez-interface-manager "DefaultAdapter")))
2185 (setq tramp-bluez-devices nil
2186 tramp-bluez-discovery t)
2187 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector nil
2188 :system tramp-bluez-service object-path
2189 tramp-bluez-interface-adapter "StartDiscovery")
2190 (while tramp-bluez-discovery
2191 (read-event nil nil 0.1))))
2192 (setq tramp-bluez-discovery (current-time))
2193 (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-bluez-devices)
2194 tramp-bluez-devices)
2196 (defun tramp-bluez-property-changed (property value)
2197 "Signal handler for the \"org.bluez.Adapter.PropertyChanged\" signal."
2198 (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" property value)
2199 (cond
2200 ((string-equal property "Discovering")
2201 (unless (car value)
2202 ;; "Discovering" FALSE means discovery run has been completed.
2203 ;; We stop it, because we don't need another run.
2204 (setq tramp-bluez-discovery nil)
2205 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
2206 :system tramp-bluez-service (dbus-event-path-name last-input-event)
2207 tramp-bluez-interface-adapter "StopDiscovery")))))
2209 (when tramp-gvfs-enabled
2210 (dbus-register-signal
2211 :system nil nil tramp-bluez-interface-adapter "PropertyChanged"
2212 'tramp-bluez-property-changed))
2214 (defun tramp-bluez-device-found (device args)
2215 "Signal handler for the \"org.bluez.Adapter.DeviceFound\" signal."
2216 (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" device args)
2217 (let ((alias (car (cadr (assoc "Alias" args))))
2218 (address (car (cadr (assoc "Address" args)))))
2219 ;; Maybe we shall check the device class for being a proper
2220 ;; device, and call also SDP in order to find the obex service.
2221 (add-to-list 'tramp-bluez-devices (list alias address))))
2223 (when tramp-gvfs-enabled
2224 (dbus-register-signal
2225 :system nil nil tramp-bluez-interface-adapter "DeviceFound"
2226 'tramp-bluez-device-found))
2228 (defun tramp-bluez-parse-device-names (_ignore)
2229 "Return a list of (nil host) tuples allowed to access."
2230 (mapcar
2231 (lambda (x) (list nil (car x)))
2232 (tramp-bluez-list-devices)))
2234 ;; Add completion function for OBEX method.
2235 (when (and tramp-gvfs-enabled
2236 (member tramp-bluez-service (dbus-list-known-names :system)))
2237 (tramp-set-completion-function
2238 "obex" '((tramp-bluez-parse-device-names ""))))
2241 ;; D-Bus zeroconf functions.
2243 (defun tramp-zeroconf-parse-device-names (service)
2244 "Return a list of (user host) tuples allowed to access."
2245 (mapcar
2246 (lambda (x)
2247 (let ((host (zeroconf-service-host x))
2248 (port (zeroconf-service-port x))
2249 (text (zeroconf-service-txt x))
2250 user)
2251 (when port
2252 (setq host (format "%s%s%d" host tramp-prefix-port-regexp port)))
2253 ;; A user is marked in a TXT field like "u=guest".
2254 (while text
2255 (when (string-match "u=\\(.+\\)$" (car text))
2256 (setq user (match-string 1 (car text))))
2257 (setq text (cdr text)))
2258 (list user host)))
2259 (zeroconf-list-services service)))
2261 ;; We use the TRIM argument of `split-string', which exist since Emacs
2262 ;; 24.4. I mask this for older Emacs versions, there is no harm.
2263 (defun tramp-gvfs-parse-device-names (service)
2264 "Return a list of (user host) tuples allowed to access.
2265 This uses \"avahi-browse\" in case D-Bus is not enabled in Avahi."
2266 (let ((result
2267 (ignore-errors
2268 (tramp-compat-funcall
2269 'split-string
2270 (shell-command-to-string (format "avahi-browse -trkp %s" service))
2271 "[\n\r]+" 'omit "^\\+;.*$"))))
2272 (delete-dups
2273 (mapcar
2274 (lambda (x)
2275 (let* ((list (split-string x ";"))
2276 (host (nth 6 list))
2277 (text (tramp-compat-funcall
2278 'split-string (nth 9 list) "\" \"" 'omit "\""))
2279 user)
2280 ;; A user is marked in a TXT field like "u=guest".
2281 (while text
2282 (when (string-match "u=\\(.+\\)$" (car text))
2283 (setq user (match-string 1 (car text))))
2284 (setq text (cdr text)))
2285 (list user host)))
2286 result))))
2288 ;; Add completion functions for AFP, DAV, DAVS, SFTP and SMB methods.
2289 (when tramp-gvfs-enabled
2290 ;; Suppress D-Bus error messages.
2291 (let (tramp-gvfs-dbus-event-vector)
2292 (zeroconf-init tramp-gvfs-zeroconf-domain)
2293 (if (zeroconf-list-service-types)
2294 (progn
2295 (tramp-set-completion-function
2296 "afp" '((tramp-zeroconf-parse-device-names "_afpovertcp._tcp")))
2297 (tramp-set-completion-function
2298 "dav" '((tramp-zeroconf-parse-device-names "_webdav._tcp")))
2299 (tramp-set-completion-function
2300 "davs" '((tramp-zeroconf-parse-device-names "_webdav._tcp")))
2301 (tramp-set-completion-function
2302 "sftp" '((tramp-zeroconf-parse-device-names "_ssh._tcp")
2303 (tramp-zeroconf-parse-device-names "_workstation._tcp")))
2304 (when (member "smb" tramp-gvfs-methods)
2305 (tramp-set-completion-function
2306 "smb" '((tramp-zeroconf-parse-device-names "_smb._tcp")))))
2308 (when (executable-find "avahi-browse")
2309 (tramp-set-completion-function
2310 "afp" '((tramp-gvfs-parse-device-names "_afpovertcp._tcp")))
2311 (tramp-set-completion-function
2312 "dav" '((tramp-gvfs-parse-device-names "_webdav._tcp")))
2313 (tramp-set-completion-function
2314 "davs" '((tramp-gvfs-parse-device-names "_webdav._tcp")))
2315 (tramp-set-completion-function
2316 "sftp" '((tramp-gvfs-parse-device-names "_ssh._tcp")
2317 (tramp-gvfs-parse-device-names "_workstation._tcp")))
2318 (when (member "smb" tramp-gvfs-methods)
2319 (tramp-set-completion-function
2320 "smb" '((tramp-gvfs-parse-device-names "_smb._tcp"))))))))
2323 ;; D-Bus SYNCE functions.
2325 (defun tramp-synce-list-devices ()
2326 "Return all discovered synce devices as list.
2327 They are retrieved from the hal daemon."
2328 (let (tramp-synce-devices)
2329 (dolist (device
2330 (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
2331 :system tramp-hal-service tramp-hal-path-manager
2332 tramp-hal-interface-manager "GetAllDevices"))
2333 (when (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
2334 :system tramp-hal-service device tramp-hal-interface-device
2335 "PropertyExists" "sync.plugin")
2336 (let ((prop
2337 (with-tramp-dbus-call-method
2338 tramp-gvfs-dbus-event-vector t
2339 :system tramp-hal-service device tramp-hal-interface-device
2340 "GetPropertyString" "pda.pocketpc.name")))
2341 (unless (member prop tramp-synce-devices)
2342 (push prop tramp-synce-devices)))))
2343 (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-synce-devices)
2344 tramp-synce-devices))
2346 (defun tramp-synce-parse-device-names (_ignore)
2347 "Return a list of (nil host) tuples allowed to access."
2348 (mapcar
2349 (lambda (x) (list nil x))
2350 (tramp-synce-list-devices)))
2352 ;; Add completion function for SYNCE method.
2353 (when tramp-gvfs-enabled
2354 (tramp-set-completion-function
2355 "synce" '((tramp-synce-parse-device-names ""))))
2357 (add-hook 'tramp-unload-hook
2358 (lambda ()
2359 (unload-feature 'tramp-gvfs 'force)))
2361 (provide 'tramp-gvfs)
2363 ;;; TODO:
2365 ;; * (Customizable) unmount when exiting Emacs. See tramp-archive.el.
2367 ;; * Host name completion for existing mount points (afp-server,
2368 ;; smb-server, google-drive, owncloud) or via smb-network.
2370 ;; * Check, how two shares of the same SMB server can be mounted in
2371 ;; parallel.
2373 ;; * Apply SDP on bluetooth devices, in order to filter out obex
2374 ;; capability.
2376 ;; * Implement obex for other serial communication but bluetooth.
2378 ;;; tramp-gvfs.el ends here