tramp-gvfs.el 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436
  1. ;;; tramp-gvfs.el --- Tramp access functions for GVFS daemon
  2. ;; Copyright (C) 2009-2012 Free Software Foundation, Inc.
  3. ;; Author: Michael Albinus <michael.albinus@gmx.de>
  4. ;; Keywords: comm, processes
  5. ;; Package: tramp
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; Access functions for the GVFS daemon from Tramp. Tested with GVFS
  19. ;; 1.0.2 (Ubuntu 8.10, Gnome 2.24). It has been reported also to run
  20. ;; with GVFS 0.2.5 (Ubuntu 8.04, Gnome 2.22), but there is an
  21. ;; incompatibility with the mount_info structure, which has been
  22. ;; worked around.
  23. ;; It has also been tested with GVFS 1.6.2 (Ubuntu 10.04, Gnome 2.30),
  24. ;; where the default_location has been added to mount_info (see
  25. ;; <https://bugzilla.gnome.org/show_bug.cgi?id=561998>.
  26. ;; All actions to mount a remote location, and to retrieve mount
  27. ;; information, are performed by D-Bus messages. File operations
  28. ;; themselves are performed via the mounted filesystem in ~/.gvfs.
  29. ;; Consequently, GNU Emacs 23.1 with enabled D-Bus bindings is a
  30. ;; precondition.
  31. ;; The GVFS D-Bus interface is said to be unstable. There are even no
  32. ;; introspection data. The interface, as discovered during
  33. ;; development time, is given in respective comments.
  34. ;; The customer option `tramp-gvfs-methods' contains the list of
  35. ;; supported connection methods. Per default, these are "dav",
  36. ;; "davs", "obex" and "synce". Note that with "obex" it might be
  37. ;; necessary to pair with the other bluetooth device, if it hasn't
  38. ;; been done already. There might be also some few seconds delay in
  39. ;; discovering available bluetooth devices.
  40. ;; Other possible connection methods are "ftp", "sftp" and "smb".
  41. ;; When one of these methods is added to the list, the remote access
  42. ;; for that method is performed via GVFS instead of the native Tramp
  43. ;; implementation.
  44. ;; GVFS offers even more connection methods. The complete list of
  45. ;; connection methods of the actual GVFS implementation can be
  46. ;; retrieved by:
  47. ;;
  48. ;; (message
  49. ;; "%s"
  50. ;; (mapcar
  51. ;; 'car
  52. ;; (dbus-call-method
  53. ;; :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
  54. ;; tramp-gvfs-interface-mounttracker "listMountableInfo")))
  55. ;; Note that all other connection methods are not tested, beside the
  56. ;; ones offered for customization in `tramp-gvfs-methods'. If you
  57. ;; request an additional connection method to be supported, please
  58. ;; drop me a note.
  59. ;; For hostname completion, information is retrieved either from the
  60. ;; bluez daemon (for the "obex" method), the hal daemon (for the
  61. ;; "synce" method), or from the zeroconf daemon (for the "dav",
  62. ;; "davs", and "sftp" methods). The zeroconf daemon is pre-configured
  63. ;; to discover services in the "local" domain. If another domain
  64. ;; shall be used for discovering services, the customer option
  65. ;; `tramp-gvfs-zeroconf-domain' can be set accordingly.
  66. ;; Restrictions:
  67. ;; * The current GVFS implementation does not allow to write on the
  68. ;; remote bluetooth device via OBEX.
  69. ;;
  70. ;; * Two shares of the same SMB server cannot be mounted in parallel.
  71. ;;; Code:
  72. ;; D-Bus support in the Emacs core can be disabled with configuration
  73. ;; option "--without-dbus". Declare used subroutines and variables.
  74. (declare-function dbus-call-method "dbusbind.c")
  75. (declare-function dbus-call-method-asynchronously "dbusbind.c")
  76. (declare-function dbus-get-unique-name "dbusbind.c")
  77. (declare-function dbus-register-method "dbusbind.c")
  78. (declare-function dbus-register-signal "dbusbind.c")
  79. ;; Pacify byte-compiler
  80. (eval-when-compile
  81. (require 'cl)
  82. (require 'custom))
  83. (require 'tramp)
  84. (require 'dbus)
  85. (require 'url-parse)
  86. (require 'url-util)
  87. (require 'zeroconf)
  88. ;;;###tramp-autoload
  89. (defcustom tramp-gvfs-methods '("dav" "davs" "obex" "synce")
  90. "*List of methods for remote files, accessed with GVFS."
  91. :group 'tramp
  92. :version "23.2"
  93. :type '(repeat (choice (const "dav")
  94. (const "davs")
  95. (const "ftp")
  96. (const "obex")
  97. (const "sftp")
  98. (const "smb")
  99. (const "synce"))))
  100. ;; Add a default for `tramp-default-user-alist'. Rule: For the SYNCE
  101. ;; method, no user is chosen.
  102. ;;;###tramp-autoload
  103. (add-to-list 'tramp-default-user-alist '("\\`synce\\'" nil nil))
  104. (defcustom tramp-gvfs-zeroconf-domain "local"
  105. "*Zeroconf domain to be used for discovering services, like host names."
  106. :group 'tramp
  107. :version "23.2"
  108. :type 'string)
  109. ;; Add the methods to `tramp-methods', in order to allow minibuffer
  110. ;; completion.
  111. ;;;###tramp-autoload
  112. (when (featurep 'dbusbind)
  113. (dolist (elt tramp-gvfs-methods)
  114. (unless (assoc elt tramp-methods)
  115. (add-to-list 'tramp-methods (cons elt nil)))))
  116. (defconst tramp-gvfs-path-tramp (concat dbus-path-emacs "/Tramp")
  117. "The preceding object path for own objects.")
  118. (defconst tramp-gvfs-service-daemon "org.gtk.vfs.Daemon"
  119. "The well known name of the GVFS daemon.")
  120. ;; Check that GVFS is available. D-Bus integration is available since
  121. ;; Emacs 23 on some system types. We don't call `dbus-ping', because
  122. ;; this would load dbus.el.
  123. (unless (and (tramp-compat-funcall 'dbus-get-unique-name :session)
  124. (tramp-compat-process-running-p "gvfs-fuse-daemon"))
  125. (error "Package `tramp-gvfs' not supported"))
  126. (defconst tramp-gvfs-path-mounttracker "/org/gtk/vfs/mounttracker"
  127. "The object path of the GVFS daemon.")
  128. (defconst tramp-gvfs-interface-mounttracker "org.gtk.vfs.MountTracker"
  129. "The mount tracking interface in the GVFS daemon.")
  130. ;; <interface name='org.gtk.vfs.MountTracker'>
  131. ;; <method name='listMounts'>
  132. ;; <arg name='mount_info_list'
  133. ;; type='a{sosssssbay{aya{say}}ay}'
  134. ;; direction='out'/>
  135. ;; </method>
  136. ;; <method name='mountLocation'>
  137. ;; <arg name='mount_spec' type='{aya{say}}' direction='in'/>
  138. ;; <arg name='dbus_id' type='s' direction='in'/>
  139. ;; <arg name='object_path' type='o' direction='in'/>
  140. ;; </method>
  141. ;; <signal name='mounted'>
  142. ;; <arg name='mount_info'
  143. ;; type='{sosssssbay{aya{say}}ay}'/>
  144. ;; </signal>
  145. ;; <signal name='unmounted'>
  146. ;; <arg name='mount_info'
  147. ;; type='{sosssssbay{aya{say}}ay}'/>
  148. ;; </signal>
  149. ;; </interface>
  150. ;;
  151. ;; STRUCT mount_info
  152. ;; STRING dbus_id
  153. ;; OBJECT_PATH object_path
  154. ;; STRING display_name
  155. ;; STRING stable_name
  156. ;; STRING x_content_types Since GVFS 1.0 only !!!
  157. ;; STRING icon
  158. ;; STRING preferred_filename_encoding
  159. ;; BOOLEAN user_visible
  160. ;; ARRAY BYTE fuse_mountpoint
  161. ;; STRUCT mount_spec
  162. ;; ARRAY BYTE mount_prefix
  163. ;; ARRAY
  164. ;; STRUCT mount_spec_item
  165. ;; STRING key (server, share, type, user, host, port)
  166. ;; ARRAY BYTE value
  167. ;; ARRAY BYTE default_location Since GVFS 1.5 only !!!
  168. (defconst tramp-gvfs-interface-mountoperation "org.gtk.vfs.MountOperation"
  169. "Used by the dbus-proxying implementation of GMountOperation.")
  170. ;; <interface name='org.gtk.vfs.MountOperation'>
  171. ;; <method name='askPassword'>
  172. ;; <arg name='message' type='s' direction='in'/>
  173. ;; <arg name='default_user' type='s' direction='in'/>
  174. ;; <arg name='default_domain' type='s' direction='in'/>
  175. ;; <arg name='flags' type='u' direction='in'/>
  176. ;; <arg name='handled' type='b' direction='out'/>
  177. ;; <arg name='aborted' type='b' direction='out'/>
  178. ;; <arg name='password' type='s' direction='out'/>
  179. ;; <arg name='username' type='s' direction='out'/>
  180. ;; <arg name='domain' type='s' direction='out'/>
  181. ;; <arg name='anonymous' type='b' direction='out'/>
  182. ;; <arg name='password_save' type='u' direction='out'/>
  183. ;; </method>
  184. ;; <method name='askQuestion'>
  185. ;; <arg name='message' type='s' direction='in'/>
  186. ;; <arg name='choices' type='as' direction='in'/>
  187. ;; <arg name='handled' type='b' direction='out'/>
  188. ;; <arg name='aborted' type='b' direction='out'/>
  189. ;; <arg name='choice' type='u' direction='out'/>
  190. ;; </method>
  191. ;; </interface>
  192. ;; The following flags are used in "askPassword". They are defined in
  193. ;; /usr/include/glib-2.0/gio/gioenums.h.
  194. (defconst tramp-gvfs-password-need-password 1
  195. "Operation requires a password.")
  196. (defconst tramp-gvfs-password-need-username 2
  197. "Operation requires a username.")
  198. (defconst tramp-gvfs-password-need-domain 4
  199. "Operation requires a domain.")
  200. (defconst tramp-gvfs-password-saving-supported 8
  201. "Operation supports saving settings.")
  202. (defconst tramp-gvfs-password-anonymous-supported 16
  203. "Operation supports anonymous users.")
  204. (defconst tramp-bluez-service "org.bluez"
  205. "The well known name of the BLUEZ service.")
  206. (defconst tramp-bluez-interface-manager "org.bluez.Manager"
  207. "The manager interface of the BLUEZ daemon.")
  208. ;; <interface name='org.bluez.Manager'>
  209. ;; <method name='DefaultAdapter'>
  210. ;; <arg type='o' direction='out'/>
  211. ;; </method>
  212. ;; <method name='FindAdapter'>
  213. ;; <arg type='s' direction='in'/>
  214. ;; <arg type='o' direction='out'/>
  215. ;; </method>
  216. ;; <method name='ListAdapters'>
  217. ;; <arg type='ao' direction='out'/>
  218. ;; </method>
  219. ;; <signal name='AdapterAdded'>
  220. ;; <arg type='o'/>
  221. ;; </signal>
  222. ;; <signal name='AdapterRemoved'>
  223. ;; <arg type='o'/>
  224. ;; </signal>
  225. ;; <signal name='DefaultAdapterChanged'>
  226. ;; <arg type='o'/>
  227. ;; </signal>
  228. ;; </interface>
  229. (defconst tramp-bluez-interface-adapter "org.bluez.Adapter"
  230. "The adapter interface of the BLUEZ daemon.")
  231. ;; <interface name='org.bluez.Adapter'>
  232. ;; <method name='GetProperties'>
  233. ;; <arg type='a{sv}' direction='out'/>
  234. ;; </method>
  235. ;; <method name='SetProperty'>
  236. ;; <arg type='s' direction='in'/>
  237. ;; <arg type='v' direction='in'/>
  238. ;; </method>
  239. ;; <method name='RequestMode'>
  240. ;; <arg type='s' direction='in'/>
  241. ;; </method>
  242. ;; <method name='ReleaseMode'/>
  243. ;; <method name='RequestSession'/>
  244. ;; <method name='ReleaseSession'/>
  245. ;; <method name='StartDiscovery'/>
  246. ;; <method name='StopDiscovery'/>
  247. ;; <method name='ListDevices'>
  248. ;; <arg type='ao' direction='out'/>
  249. ;; </method>
  250. ;; <method name='CreateDevice'>
  251. ;; <arg type='s' direction='in'/>
  252. ;; <arg type='o' direction='out'/>
  253. ;; </method>
  254. ;; <method name='CreatePairedDevice'>
  255. ;; <arg type='s' direction='in'/>
  256. ;; <arg type='o' direction='in'/>
  257. ;; <arg type='s' direction='in'/>
  258. ;; <arg type='o' direction='out'/>
  259. ;; </method>
  260. ;; <method name='CancelDeviceCreation'>
  261. ;; <arg type='s' direction='in'/>
  262. ;; </method>
  263. ;; <method name='RemoveDevice'>
  264. ;; <arg type='o' direction='in'/>
  265. ;; </method>
  266. ;; <method name='FindDevice'>
  267. ;; <arg type='s' direction='in'/>
  268. ;; <arg type='o' direction='out'/>
  269. ;; </method>
  270. ;; <method name='RegisterAgent'>
  271. ;; <arg type='o' direction='in'/>
  272. ;; <arg type='s' direction='in'/>
  273. ;; </method>
  274. ;; <method name='UnregisterAgent'>
  275. ;; <arg type='o' direction='in'/>
  276. ;; </method>
  277. ;; <signal name='DeviceCreated'>
  278. ;; <arg type='o'/>
  279. ;; </signal>
  280. ;; <signal name='DeviceRemoved'>
  281. ;; <arg type='o'/>
  282. ;; </signal>
  283. ;; <signal name='DeviceFound'>
  284. ;; <arg type='s'/>
  285. ;; <arg type='a{sv}'/>
  286. ;; </signal>
  287. ;; <signal name='PropertyChanged'>
  288. ;; <arg type='s'/>
  289. ;; <arg type='v'/>
  290. ;; </signal>
  291. ;; <signal name='DeviceDisappeared'>
  292. ;; <arg type='s'/>
  293. ;; </signal>
  294. ;; </interface>
  295. (defcustom tramp-bluez-discover-devices-timeout 60
  296. "Defines seconds since last bluetooth device discovery before rescanning.
  297. A value of 0 would require an immediate discovery during hostname
  298. completion, nil means to use always cached values for discovered
  299. devices."
  300. :group 'tramp
  301. :version "23.2"
  302. :type '(choice (const nil) integer))
  303. (defvar tramp-bluez-discovery nil
  304. "Indicator for a running bluetooth device discovery.
  305. It keeps the timestamp of last discovery.")
  306. (defvar tramp-bluez-devices nil
  307. "Alist of detected bluetooth devices.
  308. Every entry is a list (NAME ADDRESS).")
  309. (defconst tramp-hal-service "org.freedesktop.Hal"
  310. "The well known name of the HAL service.")
  311. (defconst tramp-hal-path-manager "/org/freedesktop/Hal/Manager"
  312. "The object path of the HAL daemon manager.")
  313. (defconst tramp-hal-interface-manager "org.freedesktop.Hal.Manager"
  314. "The manager interface of the HAL daemon.")
  315. (defconst tramp-hal-interface-device "org.freedesktop.Hal.Device"
  316. "The device interface of the HAL daemon.")
  317. ;; New handlers should be added here.
  318. (defconst tramp-gvfs-file-name-handler-alist
  319. '(
  320. (access-file . ignore)
  321. (add-name-to-file . tramp-gvfs-handle-copy-file)
  322. ;; `byte-compiler-base-file-name' performed by default handler.
  323. (copy-file . tramp-gvfs-handle-copy-file)
  324. (delete-directory . tramp-gvfs-handle-delete-directory)
  325. (delete-file . tramp-gvfs-handle-delete-file)
  326. ;; `diff-latest-backup-file' performed by default handler.
  327. (directory-file-name . tramp-handle-directory-file-name)
  328. (directory-files . tramp-gvfs-handle-directory-files)
  329. (directory-files-and-attributes
  330. . tramp-gvfs-handle-directory-files-and-attributes)
  331. (dired-call-process . ignore)
  332. (dired-compress-file . ignore)
  333. (dired-uncache . tramp-handle-dired-uncache)
  334. ;; `executable-find' is not official yet. performed by default handler.
  335. (expand-file-name . tramp-gvfs-handle-expand-file-name)
  336. ;; `file-accessible-directory-p' performed by default handler.
  337. (file-attributes . tramp-gvfs-handle-file-attributes)
  338. (file-directory-p . tramp-gvfs-handle-file-directory-p)
  339. (file-executable-p . tramp-gvfs-handle-file-executable-p)
  340. (file-exists-p . tramp-gvfs-handle-file-exists-p)
  341. (file-local-copy . tramp-gvfs-handle-file-local-copy)
  342. ;; `file-modes' performed by default handler.
  343. (file-name-all-completions . tramp-gvfs-handle-file-name-all-completions)
  344. (file-name-as-directory . tramp-handle-file-name-as-directory)
  345. (file-name-completion . tramp-handle-file-name-completion)
  346. (file-name-directory . tramp-handle-file-name-directory)
  347. (file-name-nondirectory . tramp-handle-file-name-nondirectory)
  348. ;; `file-name-sans-versions' performed by default handler.
  349. (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
  350. (file-ownership-preserved-p . ignore)
  351. (file-readable-p . tramp-gvfs-handle-file-readable-p)
  352. (file-regular-p . tramp-handle-file-regular-p)
  353. (file-remote-p . tramp-handle-file-remote-p)
  354. (file-selinux-context . tramp-gvfs-handle-file-selinux-context)
  355. (file-symlink-p . tramp-handle-file-symlink-p)
  356. ;; `file-truename' performed by default handler.
  357. (file-writable-p . tramp-gvfs-handle-file-writable-p)
  358. (find-backup-file-name . tramp-handle-find-backup-file-name)
  359. ;; `find-file-noselect' performed by default handler.
  360. ;; `get-file-buffer' performed by default handler.
  361. (insert-directory . tramp-gvfs-handle-insert-directory)
  362. (insert-file-contents . tramp-gvfs-handle-insert-file-contents)
  363. (load . tramp-handle-load)
  364. (make-directory . tramp-gvfs-handle-make-directory)
  365. (make-directory-internal . ignore)
  366. (make-symbolic-link . ignore)
  367. (process-file . tramp-gvfs-handle-process-file)
  368. (rename-file . tramp-gvfs-handle-rename-file)
  369. (set-file-modes . tramp-gvfs-handle-set-file-modes)
  370. (set-file-selinux-context . tramp-gvfs-handle-set-file-selinux-context)
  371. (set-visited-file-modtime . tramp-gvfs-handle-set-visited-file-modtime)
  372. (shell-command . tramp-gvfs-handle-shell-command)
  373. (start-file-process . tramp-gvfs-handle-start-file-process)
  374. (substitute-in-file-name . tramp-handle-substitute-in-file-name)
  375. (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
  376. (vc-registered . ignore)
  377. (verify-visited-file-modtime
  378. . tramp-gvfs-handle-verify-visited-file-modtime)
  379. (write-region . tramp-gvfs-handle-write-region)
  380. )
  381. "Alist of handler functions for Tramp GVFS method.
  382. Operations not mentioned here will be handled by the default Emacs primitives.")
  383. ;;;###tramp-autoload
  384. (defsubst tramp-gvfs-file-name-p (filename)
  385. "Check if it's a filename handled by the GVFS daemon."
  386. (and (tramp-tramp-file-p filename)
  387. (let ((method
  388. (tramp-file-name-method (tramp-dissect-file-name filename))))
  389. (and (stringp method) (member method tramp-gvfs-methods)))))
  390. ;;;###tramp-autoload
  391. (defun tramp-gvfs-file-name-handler (operation &rest args)
  392. "Invoke the GVFS related OPERATION.
  393. First arg specifies the OPERATION, second arg is a list of arguments to
  394. pass to the OPERATION."
  395. (let ((fn (assoc operation tramp-gvfs-file-name-handler-alist)))
  396. (if fn
  397. (save-match-data (apply (cdr fn) args))
  398. (tramp-run-real-handler operation args))))
  399. ;; This might be moved to tramp.el. It shall be the first file name
  400. ;; handler.
  401. ;;;###tramp-autoload
  402. (when (featurep 'dbusbind)
  403. (add-to-list 'tramp-foreign-file-name-handler-alist
  404. (cons 'tramp-gvfs-file-name-p 'tramp-gvfs-file-name-handler)))
  405. (defun tramp-gvfs-stringify-dbus-message (message)
  406. "Convert a D-Bus message into readable UTF8 strings, used for traces."
  407. (cond
  408. ((and (consp message) (characterp (car message)))
  409. (format "%S" (dbus-byte-array-to-string message)))
  410. ((consp message)
  411. (mapcar 'tramp-gvfs-stringify-dbus-message message))
  412. ((stringp message)
  413. (format "%S" message))
  414. (t message)))
  415. (defmacro with-tramp-dbus-call-method
  416. (vec synchronous bus service path interface method &rest args)
  417. "Apply a D-Bus call on bus BUS.
  418. If SYNCHRONOUS is non-nil, the call is synchronously. Otherwise,
  419. it is an asynchronous call, with `ignore' as callback function.
  420. The other arguments have the same meaning as with `dbus-call-method'
  421. or `dbus-call-method-asynchronously'. Additionally, the call
  422. will be traced by Tramp with trace level 6."
  423. `(let ((func (if ,synchronous
  424. 'dbus-call-method 'dbus-call-method-asynchronously))
  425. (args (append (list ,bus ,service ,path ,interface ,method)
  426. (if ,synchronous (list ,@args) (list 'ignore ,@args))))
  427. result)
  428. (tramp-message ,vec 6 "%s %s" func args)
  429. (setq result (apply func args))
  430. (tramp-message ,vec 6 "%s" (tramp-gvfs-stringify-dbus-message result))
  431. result))
  432. (put 'with-tramp-dbus-call-method 'lisp-indent-function 2)
  433. (put 'with-tramp-dbus-call-method 'edebug-form-spec '(form symbolp body))
  434. (tramp-compat-font-lock-add-keywords
  435. 'emacs-lisp-mode '("\\<with-tramp-dbus-call-method\\>"))
  436. (defmacro with-tramp-gvfs-error-message (filename handler &rest args)
  437. "Apply a Tramp GVFS `handler'.
  438. In case of an error, modify the error message by replacing
  439. `filename' with its GVFS mounted name."
  440. `(let ((fuse-file-name (regexp-quote (tramp-gvfs-fuse-file-name ,filename)))
  441. elt)
  442. (condition-case err
  443. (tramp-compat-funcall ,handler ,@args)
  444. (error
  445. (setq elt (cdr err))
  446. (while elt
  447. (when (and (stringp (car elt))
  448. (string-match fuse-file-name (car elt)))
  449. (setcar elt (replace-match ,filename t t (car elt))))
  450. (setq elt (cdr elt)))
  451. (signal (car err) (cdr err))))))
  452. (put 'with-tramp-gvfs-error-message 'lisp-indent-function 2)
  453. (put 'with-tramp-gvfs-error-message 'edebug-form-spec '(form symbolp body))
  454. (tramp-compat-font-lock-add-keywords
  455. 'emacs-lisp-mode '("\\<with-tramp-gvfs-error-message\\>"))
  456. (defvar tramp-gvfs-dbus-event-vector nil
  457. "Current Tramp file name to be used, as vector.
  458. It is needed when D-Bus signals or errors arrive, because there
  459. is no information where to trace the message.")
  460. (defun tramp-gvfs-dbus-event-error (event err)
  461. "Called when a D-Bus error message arrives, see `dbus-event-error-hooks'."
  462. (when tramp-gvfs-dbus-event-vector
  463. (tramp-message tramp-gvfs-dbus-event-vector 10 "%S" event)
  464. (tramp-error tramp-gvfs-dbus-event-vector 'file-error "%s" (cadr err))))
  465. (add-hook 'dbus-event-error-hooks 'tramp-gvfs-dbus-event-error)
  466. ;; File name primitives.
  467. (defun tramp-gvfs-handle-copy-file
  468. (filename newname &optional ok-if-already-exists keep-date
  469. preserve-uid-gid preserve-selinux-context)
  470. "Like `copy-file' for Tramp files."
  471. (with-parsed-tramp-file-name
  472. (if (tramp-tramp-file-p filename) filename newname) nil
  473. (tramp-with-progress-reporter
  474. v 0 (format "Copying %s to %s" filename newname)
  475. (condition-case err
  476. (let ((args
  477. (list
  478. (if (tramp-gvfs-file-name-p filename)
  479. (tramp-gvfs-fuse-file-name filename)
  480. filename)
  481. (if (tramp-gvfs-file-name-p newname)
  482. (tramp-gvfs-fuse-file-name newname)
  483. newname)
  484. ok-if-already-exists keep-date preserve-uid-gid)))
  485. (when preserve-selinux-context
  486. (setq args (append args (list preserve-selinux-context))))
  487. (apply 'copy-file args))
  488. ;; Error case. Let's try it with the GVFS utilities.
  489. (error
  490. (tramp-message v 4 "`copy-file' failed, trying `gvfs-copy'")
  491. (unless
  492. (zerop
  493. (let ((args
  494. (append (if (or keep-date preserve-uid-gid)
  495. (list "--preserve")
  496. nil)
  497. (list
  498. (tramp-gvfs-url-file-name filename)
  499. (tramp-gvfs-url-file-name newname)))))
  500. (apply 'tramp-gvfs-send-command v "gvfs-copy" args)))
  501. ;; Propagate the error.
  502. (tramp-error v (car err) "%s" (cdr err)))))))
  503. (when (file-remote-p newname)
  504. (with-parsed-tramp-file-name newname nil
  505. (tramp-flush-file-property v (file-name-directory localname))
  506. (tramp-flush-file-property v localname))))
  507. (defun tramp-gvfs-handle-delete-directory (directory &optional recursive)
  508. "Like `delete-directory' for Tramp files."
  509. (tramp-compat-delete-directory
  510. (tramp-gvfs-fuse-file-name directory) recursive))
  511. (defun tramp-gvfs-handle-delete-file (filename &optional trash)
  512. "Like `delete-file' for Tramp files."
  513. (tramp-compat-delete-file (tramp-gvfs-fuse-file-name filename) trash))
  514. (defun tramp-gvfs-handle-directory-files
  515. (directory &optional full match nosort)
  516. "Like `directory-files' for Tramp files."
  517. (let ((fuse-file-name (tramp-gvfs-fuse-file-name directory)))
  518. (mapcar
  519. (lambda (x)
  520. (if (string-match fuse-file-name x)
  521. (replace-match directory t t x)
  522. x))
  523. (directory-files fuse-file-name full match nosort))))
  524. (defun tramp-gvfs-handle-directory-files-and-attributes
  525. (directory &optional full match nosort id-format)
  526. "Like `directory-files-and-attributes' for Tramp files."
  527. (let ((fuse-file-name (tramp-gvfs-fuse-file-name directory)))
  528. (mapcar
  529. (lambda (x)
  530. (when (string-match fuse-file-name (car x))
  531. (setcar x (replace-match directory t t (car x))))
  532. x)
  533. (directory-files-and-attributes
  534. fuse-file-name full match nosort id-format))))
  535. (defun tramp-gvfs-handle-expand-file-name (name &optional dir)
  536. "Like `expand-file-name' for Tramp files."
  537. ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
  538. (setq dir (or dir default-directory "/"))
  539. ;; Unless NAME is absolute, concat DIR and NAME.
  540. (unless (file-name-absolute-p name)
  541. (setq name (concat (file-name-as-directory dir) name)))
  542. ;; If NAME is not a Tramp file, run the real handler.
  543. (if (not (tramp-tramp-file-p name))
  544. (tramp-run-real-handler 'expand-file-name (list name nil))
  545. ;; Dissect NAME.
  546. (with-parsed-tramp-file-name name nil
  547. ;; If there is a default location, expand tilde.
  548. (when (string-match "\\`\\(~\\)\\(/\\|\\'\\)" localname)
  549. (save-match-data
  550. (tramp-gvfs-maybe-open-connection (vector method user host "/")))
  551. (setq localname
  552. (replace-match
  553. (tramp-get-file-property v "/" "default-location" "~")
  554. nil t localname 1)))
  555. ;; Tilde expansion is not possible.
  556. (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
  557. (tramp-error
  558. v 'file-error
  559. "Cannot expand tilde in file `%s'" name))
  560. (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
  561. (setq localname (concat "/" localname)))
  562. ;; We do not pass "/..".
  563. (if (string-equal "smb" method)
  564. (when (string-match "^/[^/]+\\(/\\.\\./?\\)" localname)
  565. (setq localname (replace-match "/" t t localname 1)))
  566. (when (string-match "^/\\.\\./?" localname)
  567. (setq localname (replace-match "/" t t localname))))
  568. ;; There might be a double slash. Remove this.
  569. (while (string-match "//" localname)
  570. (setq localname (replace-match "/" t t localname)))
  571. ;; No tilde characters in file name, do normal
  572. ;; `expand-file-name' (this does "/./" and "/../").
  573. (tramp-make-tramp-file-name
  574. method user host
  575. (tramp-run-real-handler
  576. 'expand-file-name (list localname))))))
  577. (defun tramp-gvfs-handle-file-attributes (filename &optional id-format)
  578. "Like `file-attributes' for Tramp files."
  579. (file-attributes (tramp-gvfs-fuse-file-name filename) id-format))
  580. (defun tramp-gvfs-handle-file-directory-p (filename)
  581. "Like `file-directory-p' for Tramp files."
  582. (file-directory-p (tramp-gvfs-fuse-file-name filename)))
  583. (defun tramp-gvfs-handle-file-executable-p (filename)
  584. "Like `file-executable-p' for Tramp files."
  585. (file-executable-p (tramp-gvfs-fuse-file-name filename)))
  586. (defun tramp-gvfs-handle-file-exists-p (filename)
  587. "Like `file-exists-p' for Tramp files."
  588. (file-exists-p (tramp-gvfs-fuse-file-name filename)))
  589. (defun tramp-gvfs-handle-file-local-copy (filename)
  590. "Like `file-local-copy' for Tramp files."
  591. (with-parsed-tramp-file-name filename nil
  592. (let ((tmpfile (tramp-compat-make-temp-file filename)))
  593. (unless (file-exists-p filename)
  594. (tramp-error
  595. v 'file-error
  596. "Cannot make local copy of non-existing file `%s'" filename))
  597. (copy-file filename tmpfile t t)
  598. tmpfile)))
  599. (defun tramp-gvfs-handle-file-name-all-completions (filename directory)
  600. "Like `file-name-all-completions' for Tramp files."
  601. (unless (save-match-data (string-match "/" filename))
  602. (file-name-all-completions filename (tramp-gvfs-fuse-file-name directory))))
  603. (defun tramp-gvfs-handle-file-readable-p (filename)
  604. "Like `file-readable-p' for Tramp files."
  605. (file-readable-p (tramp-gvfs-fuse-file-name filename)))
  606. (defun tramp-gvfs-handle-file-selinux-context (filename)
  607. "Like `file-selinux-context' for Tramp files."
  608. (tramp-compat-funcall
  609. 'file-selinux-context (tramp-gvfs-fuse-file-name filename)))
  610. (defun tramp-gvfs-handle-file-writable-p (filename)
  611. "Like `file-writable-p' for Tramp files."
  612. (file-writable-p (tramp-gvfs-fuse-file-name filename)))
  613. (defun tramp-gvfs-handle-insert-directory
  614. (filename switches &optional wildcard full-directory-p)
  615. "Like `insert-directory' for Tramp files."
  616. (insert-directory
  617. (tramp-gvfs-fuse-file-name filename) switches wildcard full-directory-p))
  618. (defun tramp-gvfs-handle-insert-file-contents
  619. (filename &optional visit beg end replace)
  620. "Like `insert-file-contents' for Tramp files."
  621. (unwind-protect
  622. (let ((fuse-file-name (tramp-gvfs-fuse-file-name filename))
  623. (result
  624. (insert-file-contents
  625. (tramp-gvfs-fuse-file-name filename) visit beg end replace)))
  626. (when (string-match fuse-file-name (car result))
  627. (setcar result (replace-match filename t t (car result))))
  628. result)
  629. (setq buffer-file-name filename)))
  630. (defun tramp-gvfs-handle-make-directory (dir &optional parents)
  631. "Like `make-directory' for Tramp files."
  632. (with-parsed-tramp-file-name dir nil
  633. (condition-case err
  634. (with-tramp-gvfs-error-message dir 'make-directory
  635. (tramp-gvfs-fuse-file-name dir) parents)
  636. ;; Error case. Let's try it with the GVFS utilities.
  637. (error
  638. (tramp-message v 4 "`make-directory' failed, trying `gvfs-mkdir'")
  639. (unless
  640. (zerop
  641. (tramp-gvfs-send-command
  642. v "gvfs-mkdir" (tramp-gvfs-url-file-name dir)))
  643. ;; Propagate the error.
  644. (tramp-error v (car err) "%s" (cdr err)))))))
  645. (defun tramp-gvfs-handle-process-file
  646. (program &optional infile destination display &rest args)
  647. "Like `process-file' for Tramp files."
  648. (let ((default-directory (tramp-gvfs-fuse-file-name default-directory)))
  649. (apply 'call-process program infile destination display args)))
  650. (defun tramp-gvfs-handle-rename-file
  651. (filename newname &optional ok-if-already-exists)
  652. "Like `rename-file' for Tramp files."
  653. (with-parsed-tramp-file-name
  654. (if (tramp-tramp-file-p filename) filename newname) nil
  655. (tramp-with-progress-reporter
  656. v 0 (format "Renaming %s to %s" filename newname)
  657. (condition-case err
  658. (rename-file
  659. (if (tramp-gvfs-file-name-p filename)
  660. (tramp-gvfs-fuse-file-name filename)
  661. filename)
  662. (if (tramp-gvfs-file-name-p newname)
  663. (tramp-gvfs-fuse-file-name newname)
  664. newname)
  665. ok-if-already-exists)
  666. ;; Error case. Let's try it with the GVFS utilities.
  667. (error
  668. (tramp-message v 4 "`rename-file' failed, trying `gvfs-move'")
  669. (unless
  670. (zerop
  671. (tramp-gvfs-send-command
  672. v "gvfs-move"
  673. (tramp-gvfs-url-file-name filename)
  674. (tramp-gvfs-url-file-name newname)))
  675. ;; Propagate the error.
  676. (tramp-error v (car err) "%s" (cdr err)))))))
  677. (when (file-remote-p filename)
  678. (with-parsed-tramp-file-name filename nil
  679. (tramp-flush-file-property v (file-name-directory localname))
  680. (tramp-flush-file-property v localname)))
  681. (when (file-remote-p newname)
  682. (with-parsed-tramp-file-name newname nil
  683. (tramp-flush-file-property v (file-name-directory localname))
  684. (tramp-flush-file-property v localname))))
  685. (defun tramp-gvfs-handle-set-file-modes (filename mode)
  686. "Like `set-file-modes' for Tramp files."
  687. (with-tramp-gvfs-error-message filename 'set-file-modes
  688. (tramp-gvfs-fuse-file-name filename) mode))
  689. (defun tramp-gvfs-handle-set-file-selinux-context (filename context)
  690. "Like `set-file-selinux-context' for Tramp files."
  691. (with-tramp-gvfs-error-message filename 'set-file-selinux-context
  692. (tramp-gvfs-fuse-file-name filename) context))
  693. (defun tramp-gvfs-handle-set-visited-file-modtime (&optional time-list)
  694. "Like `set-visited-file-modtime' for Tramp files."
  695. (let ((buffer-file-name (tramp-gvfs-fuse-file-name (buffer-file-name))))
  696. (set-visited-file-modtime time-list)))
  697. (defun tramp-gvfs-handle-shell-command
  698. (command &optional output-buffer error-buffer)
  699. "Like `shell-command' for Tramp files."
  700. (let ((default-directory (tramp-gvfs-fuse-file-name default-directory)))
  701. (shell-command command output-buffer error-buffer)))
  702. (defun tramp-gvfs-handle-start-file-process (name buffer program &rest args)
  703. "Like `start-file-process' for Tramp files."
  704. (let ((default-directory (tramp-gvfs-fuse-file-name default-directory)))
  705. (apply 'start-process name buffer program args)))
  706. (defun tramp-gvfs-handle-verify-visited-file-modtime (buf)
  707. "Like `verify-visited-file-modtime' for Tramp files."
  708. (with-current-buffer buf
  709. (let ((buffer-file-name (tramp-gvfs-fuse-file-name (buffer-file-name))))
  710. (verify-visited-file-modtime buf))))
  711. (defun tramp-gvfs-handle-write-region
  712. (start end filename &optional append visit lockname confirm)
  713. "Like `write-region' for Tramp files."
  714. (with-parsed-tramp-file-name filename nil
  715. (condition-case err
  716. (with-tramp-gvfs-error-message filename 'write-region
  717. start end (tramp-gvfs-fuse-file-name filename)
  718. append visit lockname confirm)
  719. ;; Error case. Let's try rename.
  720. (error
  721. (let ((tmpfile (tramp-compat-make-temp-file filename)))
  722. (tramp-message v 4 "`write-region' failed, trying `rename-file'")
  723. (write-region start end tmpfile)
  724. (condition-case nil
  725. (rename-file tmpfile filename)
  726. (error
  727. (delete-file tmpfile)
  728. (tramp-error v (car err) "%s" (cdr err)))))))
  729. ;; Set file modification time.
  730. (when (or (eq visit t) (stringp visit))
  731. (set-visited-file-modtime (nth 5 (file-attributes filename))))
  732. ;; The end.
  733. (when (or (eq visit t) (null visit) (stringp visit))
  734. (tramp-message v 0 "Wrote %s" filename))
  735. (run-hooks 'tramp-handle-write-region-hook)))
  736. ;; File name conversions.
  737. (defun tramp-gvfs-url-file-name (filename)
  738. "Return FILENAME in URL syntax."
  739. ;; "/" must NOT be hexlified.
  740. (let ((url-unreserved-chars (append '(?/) url-unreserved-chars)))
  741. (url-recreate-url
  742. (if (tramp-tramp-file-p filename)
  743. (with-parsed-tramp-file-name (file-truename filename) nil
  744. (when (string-match tramp-user-with-domain-regexp user)
  745. (setq user
  746. (concat (match-string 2 user) ";" (match-string 2 user))))
  747. (url-parse-make-urlobj
  748. method user nil
  749. (tramp-file-name-real-host v) (tramp-file-name-port v)
  750. (url-hexify-string localname)))
  751. (url-parse-make-urlobj
  752. "file" nil nil nil nil (url-hexify-string (file-truename filename)))))))
  753. (defun tramp-gvfs-object-path (filename)
  754. "Create a D-Bus object path from FILENAME."
  755. (expand-file-name (dbus-escape-as-identifier filename) tramp-gvfs-path-tramp))
  756. (defun tramp-gvfs-file-name (object-path)
  757. "Retrieve file name from D-Bus OBJECT-PATH."
  758. (dbus-unescape-from-identifier
  759. (replace-regexp-in-string "^.*/\\([^/]+\\)$" "\\1" object-path)))
  760. (defun tramp-gvfs-fuse-file-name (filename)
  761. "Return FUSE file name, which is directly accessible."
  762. (with-parsed-tramp-file-name (expand-file-name filename) nil
  763. (tramp-gvfs-maybe-open-connection v)
  764. (let ((prefix (tramp-get-file-property v "/" "prefix" ""))
  765. (fuse-mountpoint
  766. (tramp-get-file-property v "/" "fuse-mountpoint" nil)))
  767. (unless fuse-mountpoint
  768. (tramp-error
  769. v 'file-error "There is no FUSE mount point for `%s'" filename))
  770. ;; We must hide the prefix, if any.
  771. (when (string-match (concat "^" (regexp-quote prefix)) localname)
  772. (setq localname (replace-match "" t t localname)))
  773. (tramp-message
  774. v 10 "remote file `%s' is local file `%s'"
  775. filename (concat fuse-mountpoint localname))
  776. (concat fuse-mountpoint localname))))
  777. (defun tramp-bluez-address (device)
  778. "Return bluetooth device address from a given bluetooth DEVICE name."
  779. (when (stringp device)
  780. (if (string-match tramp-ipv6-regexp device)
  781. (match-string 0 device)
  782. (cadr (assoc device (tramp-bluez-list-devices))))))
  783. (defun tramp-bluez-device (address)
  784. "Return bluetooth device name from a given bluetooth device ADDRESS.
  785. ADDRESS can have the form \"xx:xx:xx:xx:xx:xx\" or \"[xx:xx:xx:xx:xx:xx]\"."
  786. (when (stringp address)
  787. (while (string-match "[][]" address)
  788. (setq address (replace-match "" t t address)))
  789. (let (result)
  790. (dolist (item (tramp-bluez-list-devices) result)
  791. (when (string-match address (cadr item))
  792. (setq result (car item)))))))
  793. ;; D-Bus GVFS functions.
  794. (defun tramp-gvfs-handler-askpassword (message user domain flags)
  795. "Implementation for the \"org.gtk.vfs.MountOperation.askPassword\" method."
  796. (let* ((filename
  797. (tramp-gvfs-file-name (dbus-event-path-name last-input-event)))
  798. (pw-prompt
  799. (format
  800. "%s for %s "
  801. (if (string-match "\\([pP]assword\\|[pP]assphrase\\)" message)
  802. (capitalize (match-string 1 message))
  803. "Password")
  804. filename))
  805. password)
  806. (condition-case nil
  807. (with-parsed-tramp-file-name filename l
  808. (when (and (zerop (length user))
  809. (not
  810. (zerop (logand flags tramp-gvfs-password-need-username))))
  811. (setq user (read-string "User name: ")))
  812. (when (and (zerop (length domain))
  813. (not (zerop (logand flags tramp-gvfs-password-need-domain))))
  814. (setq domain (read-string "Domain name: ")))
  815. (tramp-message l 6 "%S %S %S %d" message user domain flags)
  816. (setq tramp-current-method l-method
  817. tramp-current-user user
  818. tramp-current-host l-host
  819. password (tramp-read-passwd
  820. (tramp-get-connection-process l) pw-prompt))
  821. ;; Return result.
  822. (if (stringp password)
  823. (list
  824. t ;; password handled.
  825. nil ;; no abort of D-Bus.
  826. password
  827. (tramp-file-name-real-user l)
  828. domain
  829. nil ;; not anonymous.
  830. 0) ;; no password save.
  831. ;; No password provided.
  832. (list nil t "" (tramp-file-name-real-user l) domain nil 0)))
  833. ;; When QUIT is raised, we shall return this information to D-Bus.
  834. (quit (list nil t "" "" "" nil 0)))))
  835. (defun tramp-gvfs-handler-askquestion (message choices)
  836. "Implementation for the \"org.gtk.vfs.MountOperation.askQuestion\" method."
  837. (save-window-excursion
  838. (let ((enable-recursive-minibuffers t)
  839. choice)
  840. (condition-case nil
  841. (with-parsed-tramp-file-name
  842. (tramp-gvfs-file-name (dbus-event-path-name last-input-event)) nil
  843. (tramp-message v 6 "%S %S" message choices)
  844. ;; In theory, there can be several choices. Until now,
  845. ;; there is only the question whether to accept an unknown
  846. ;; host signature.
  847. (with-temp-buffer
  848. ;; Preserve message for `progress-reporter'.
  849. (tramp-compat-with-temp-message ""
  850. (insert message)
  851. (pop-to-buffer (current-buffer))
  852. (setq choice (if (yes-or-no-p (concat (car choices) " ")) 0 1))
  853. (tramp-message v 6 "%d" choice)))
  854. ;; When the choice is "no", we set a dummy fuse-mountpoint
  855. ;; in order to leave the timeout.
  856. (unless (zerop choice)
  857. (tramp-set-file-property v "/" "fuse-mountpoint" "/"))
  858. (list
  859. t ;; handled.
  860. nil ;; no abort of D-Bus.
  861. choice))
  862. ;; When QUIT is raised, we shall return this information to D-Bus.
  863. (quit (list nil t 0))))))
  864. (defun tramp-gvfs-handler-mounted-unmounted (mount-info)
  865. "Signal handler for the \"org.gtk.vfs.MountTracker.mounted\" and
  866. \"org.gtk.vfs.MountTracker.unmounted\" signals."
  867. (ignore-errors
  868. (let ((signal-name (dbus-event-member-name last-input-event))
  869. (elt mount-info))
  870. ;; Jump over the first elements of the mount info. Since there
  871. ;; were changes in the entries, we cannot access dedicated
  872. ;; elements.
  873. (while (stringp (car elt)) (setq elt (cdr elt)))
  874. (let* ((fuse-mountpoint (dbus-byte-array-to-string (cadr elt)))
  875. (mount-spec (caddr elt))
  876. (default-location (dbus-byte-array-to-string (cadddr elt)))
  877. (method (dbus-byte-array-to-string
  878. (cadr (assoc "type" (cadr mount-spec)))))
  879. (user (dbus-byte-array-to-string
  880. (cadr (assoc "user" (cadr mount-spec)))))
  881. (domain (dbus-byte-array-to-string
  882. (cadr (assoc "domain" (cadr mount-spec)))))
  883. (host (dbus-byte-array-to-string
  884. (cadr (or (assoc "host" (cadr mount-spec))
  885. (assoc "server" (cadr mount-spec))))))
  886. (port (dbus-byte-array-to-string
  887. (cadr (assoc "port" (cadr mount-spec)))))
  888. (ssl (dbus-byte-array-to-string
  889. (cadr (assoc "ssl" (cadr mount-spec)))))
  890. (prefix (concat (dbus-byte-array-to-string (car mount-spec))
  891. (dbus-byte-array-to-string
  892. (cadr (assoc "share" (cadr mount-spec)))))))
  893. (when (string-match "^smb" method)
  894. (setq method "smb"))
  895. (when (string-equal "obex" method)
  896. (setq host (tramp-bluez-device host)))
  897. (when (and (string-equal "dav" method) (string-equal "true" ssl))
  898. (setq method "davs"))
  899. (unless (zerop (length domain))
  900. (setq user (concat user tramp-prefix-domain-format domain)))
  901. (unless (zerop (length port))
  902. (setq host (concat host tramp-prefix-port-format port)))
  903. (with-parsed-tramp-file-name
  904. (tramp-make-tramp-file-name method user host "") nil
  905. (tramp-message
  906. v 6 "%s %s"
  907. signal-name (tramp-gvfs-stringify-dbus-message mount-info))
  908. (tramp-set-file-property v "/" "list-mounts" 'undef)
  909. (if (string-equal signal-name "unmounted")
  910. (tramp-set-file-property v "/" "fuse-mountpoint" nil)
  911. ;; Set prefix, mountpoint and location.
  912. (unless (string-equal prefix "/")
  913. (tramp-set-file-property v "/" "prefix" prefix))
  914. (tramp-set-file-property v "/" "fuse-mountpoint" fuse-mountpoint)
  915. (tramp-set-file-property
  916. v "/" "default-location" default-location)))))))
  917. (dbus-register-signal
  918. :session nil tramp-gvfs-path-mounttracker
  919. tramp-gvfs-interface-mounttracker "mounted"
  920. 'tramp-gvfs-handler-mounted-unmounted)
  921. (dbus-register-signal
  922. :session nil tramp-gvfs-path-mounttracker
  923. tramp-gvfs-interface-mounttracker "unmounted"
  924. 'tramp-gvfs-handler-mounted-unmounted)
  925. (defun tramp-gvfs-connection-mounted-p (vec)
  926. "Check, whether the location is already mounted."
  927. (or
  928. (tramp-get-file-property vec "/" "fuse-mountpoint" nil)
  929. (catch 'mounted
  930. (dolist
  931. (elt
  932. (with-file-property vec "/" "list-mounts"
  933. (with-tramp-dbus-call-method vec t
  934. :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
  935. tramp-gvfs-interface-mounttracker "listMounts"))
  936. nil)
  937. ;; Jump over the first elements of the mount info. Since there
  938. ;; were changes in the entries, we cannot access dedicated
  939. ;; elements.
  940. (while (stringp (car elt)) (setq elt (cdr elt)))
  941. (let* ((fuse-mountpoint (dbus-byte-array-to-string (cadr elt)))
  942. (mount-spec (caddr elt))
  943. (default-location (dbus-byte-array-to-string (cadddr elt)))
  944. (method (dbus-byte-array-to-string
  945. (cadr (assoc "type" (cadr mount-spec)))))
  946. (user (dbus-byte-array-to-string
  947. (cadr (assoc "user" (cadr mount-spec)))))
  948. (domain (dbus-byte-array-to-string
  949. (cadr (assoc "domain" (cadr mount-spec)))))
  950. (host (dbus-byte-array-to-string
  951. (cadr (or (assoc "host" (cadr mount-spec))
  952. (assoc "server" (cadr mount-spec))))))
  953. (port (dbus-byte-array-to-string
  954. (cadr (assoc "port" (cadr mount-spec)))))
  955. (ssl (dbus-byte-array-to-string
  956. (cadr (assoc "ssl" (cadr mount-spec)))))
  957. (prefix (concat (dbus-byte-array-to-string (car mount-spec))
  958. (dbus-byte-array-to-string
  959. (cadr (assoc "share" (cadr mount-spec)))))))
  960. (when (string-match "^smb" method)
  961. (setq method "smb"))
  962. (when (string-equal "obex" method)
  963. (setq host (tramp-bluez-device host)))
  964. (when (and (string-equal "dav" method) (string-equal "true" ssl))
  965. (setq method "davs"))
  966. (when (and (string-equal "synce" method) (zerop (length user)))
  967. (setq user (or (tramp-file-name-user vec) "")))
  968. (unless (zerop (length domain))
  969. (setq user (concat user tramp-prefix-domain-format domain)))
  970. (unless (zerop (length port))
  971. (setq host (concat host tramp-prefix-port-format port)))
  972. (when (and
  973. (string-equal method (tramp-file-name-method vec))
  974. (string-equal user (or (tramp-file-name-user vec) ""))
  975. (string-equal host (tramp-file-name-host vec))
  976. (string-match (concat "^" (regexp-quote prefix))
  977. (tramp-file-name-localname vec)))
  978. ;; Set prefix, mountpoint and location.
  979. (unless (string-equal prefix "/")
  980. (tramp-set-file-property vec "/" "prefix" prefix))
  981. (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
  982. (tramp-set-file-property vec "/" "default-location" default-location)
  983. (throw 'mounted t)))))))
  984. (defun tramp-gvfs-mount-spec (vec)
  985. "Return a mount-spec for \"org.gtk.vfs.MountTracker.mountLocation\"."
  986. (let* ((method (tramp-file-name-method vec))
  987. (user (tramp-file-name-real-user vec))
  988. (domain (tramp-file-name-domain vec))
  989. (host (tramp-file-name-real-host vec))
  990. (port (tramp-file-name-port vec))
  991. (localname (tramp-file-name-localname vec))
  992. (ssl (if (string-match "^davs" method) "true" "false"))
  993. (mount-spec '(:array))
  994. (mount-pref "/"))
  995. (setq
  996. mount-spec
  997. (append
  998. mount-spec
  999. (cond
  1000. ((string-equal "smb" method)
  1001. (string-match "^/?\\([^/]+\\)" localname)
  1002. `((:struct "type" ,(dbus-string-to-byte-array "smb-share"))
  1003. (:struct "server" ,(dbus-string-to-byte-array host))
  1004. (:struct "share" ,(dbus-string-to-byte-array
  1005. (match-string 1 localname)))))
  1006. ((string-equal "obex" method)
  1007. `((:struct "type" ,(dbus-string-to-byte-array method))
  1008. (:struct "host" ,(dbus-string-to-byte-array
  1009. (concat "[" (tramp-bluez-address host) "]")))))
  1010. ((string-match "^dav" method)
  1011. `((:struct "type" ,(dbus-string-to-byte-array "dav"))
  1012. (:struct "host" ,(dbus-string-to-byte-array host))
  1013. (:struct "ssl" ,(dbus-string-to-byte-array ssl))))
  1014. (t
  1015. `((:struct "type" ,(dbus-string-to-byte-array method))
  1016. (:struct "host" ,(dbus-string-to-byte-array host)))))))
  1017. (when user
  1018. (add-to-list
  1019. 'mount-spec
  1020. `(:struct "user" ,(dbus-string-to-byte-array user))
  1021. 'append))
  1022. (when domain
  1023. (add-to-list
  1024. 'mount-spec
  1025. `(:struct "domain" ,(dbus-string-to-byte-array domain))
  1026. 'append))
  1027. (when port
  1028. (add-to-list
  1029. 'mount-spec
  1030. `(:struct "port" ,(dbus-string-to-byte-array (number-to-string port)))
  1031. 'append))
  1032. (when (and (string-match "^dav" method)
  1033. (string-match "^/?[^/]+" localname))
  1034. (setq mount-pref (match-string 0 localname)))
  1035. ;; Return.
  1036. `(:struct ,(dbus-string-to-byte-array mount-pref) ,mount-spec)))
  1037. ;; Connection functions
  1038. (defun tramp-gvfs-maybe-open-connection (vec)
  1039. "Maybe open a connection VEC.
  1040. Does not do anything if a connection is already open, but re-opens the
  1041. connection if a previous connection has died for some reason."
  1042. ;; We set the file name, in case there are incoming D-Bus signals or
  1043. ;; D-Bus errors.
  1044. (setq tramp-gvfs-dbus-event-vector vec)
  1045. ;; For password handling, we need a process bound to the connection
  1046. ;; buffer. Therefore, we create a dummy process. Maybe there is a
  1047. ;; better solution?
  1048. (unless (get-buffer-process (tramp-get-buffer vec))
  1049. (let ((p (make-network-process
  1050. :name (tramp-buffer-name vec)
  1051. :buffer (tramp-get-buffer vec)
  1052. :server t :host 'local :service t)))
  1053. (tramp-compat-set-process-query-on-exit-flag p nil)))
  1054. (unless (tramp-gvfs-connection-mounted-p vec)
  1055. (let* ((method (tramp-file-name-method vec))
  1056. (user (tramp-file-name-user vec))
  1057. (host (tramp-file-name-host vec))
  1058. (object-path
  1059. (tramp-gvfs-object-path
  1060. (tramp-make-tramp-file-name method user host ""))))
  1061. (tramp-with-progress-reporter
  1062. vec 3
  1063. (if (zerop (length user))
  1064. (format "Opening connection for %s using %s" host method)
  1065. (format "Opening connection for %s@%s using %s" user host method))
  1066. ;; Enable auth-source and password-cache.
  1067. (tramp-set-connection-property vec "first-password-request" t)
  1068. ;; There will be a callback of "askPassword" when a password is
  1069. ;; needed.
  1070. (dbus-register-method
  1071. :session dbus-service-emacs object-path
  1072. tramp-gvfs-interface-mountoperation "askPassword"
  1073. 'tramp-gvfs-handler-askpassword)
  1074. ;; There could be a callback of "askQuestion" when adding fingerprint.
  1075. (dbus-register-method
  1076. :session dbus-service-emacs object-path
  1077. tramp-gvfs-interface-mountoperation "askQuestion"
  1078. 'tramp-gvfs-handler-askquestion)
  1079. ;; The call must be asynchronously, because of the "askPassword"
  1080. ;; or "askQuestion"callbacks.
  1081. (with-tramp-dbus-call-method vec nil
  1082. :session tramp-gvfs-service-daemon tramp-gvfs-path-mounttracker
  1083. tramp-gvfs-interface-mounttracker "mountLocation"
  1084. (tramp-gvfs-mount-spec vec) (dbus-get-unique-name :session)
  1085. :object-path object-path)
  1086. ;; We must wait, until the mount is applied. This will be
  1087. ;; indicated by the "mounted" signal, i.e. the "fuse-mountpoint"
  1088. ;; file property.
  1089. (with-timeout
  1090. (60
  1091. (if (zerop (length (tramp-file-name-user vec)))
  1092. (tramp-error
  1093. vec 'file-error
  1094. "Timeout reached mounting %s using %s" host method)
  1095. (tramp-error
  1096. vec 'file-error
  1097. "Timeout reached mounting %s@%s using %s" user host method)))
  1098. (while (not (tramp-get-file-property vec "/" "fuse-mountpoint" nil))
  1099. (read-event nil nil 0.1)))
  1100. ;; If `tramp-gvfs-handler-askquestion' has returned "No", it
  1101. ;; is marked with the fuse-mountpoint "/". We shall react.
  1102. (when (string-equal
  1103. (tramp-get-file-property vec "/" "fuse-mountpoint" "") "/")
  1104. (tramp-error vec 'file-error "FUSE mount denied"))
  1105. ;; We set the connection property "started" in order to put the
  1106. ;; remote location into the cache, which is helpful for further
  1107. ;; completion.
  1108. (tramp-set-connection-property vec "started" t)))))
  1109. (defun tramp-gvfs-send-command (vec command &rest args)
  1110. "Send the COMMAND with its ARGS to connection VEC.
  1111. COMMAND is usually a command from the gvfs-* utilities.
  1112. `call-process' is applied, and its return code is returned."
  1113. (let (result)
  1114. (with-current-buffer (tramp-get-buffer vec)
  1115. (erase-buffer)
  1116. (tramp-message vec 6 "%s %s" command (mapconcat 'identity args " "))
  1117. (setq result (apply 'tramp-compat-call-process command nil t nil args))
  1118. (tramp-message vec 6 "%s" (buffer-string))
  1119. result)))
  1120. ;; D-Bus BLUEZ functions.
  1121. (defun tramp-bluez-list-devices ()
  1122. "Return all discovered bluetooth devices as list.
  1123. Every entry is a list (NAME ADDRESS).
  1124. If `tramp-bluez-discover-devices-timeout' is an integer, and the last
  1125. discovery happened more time before indicated there, a rescan will be
  1126. started, which lasts some ten seconds. Otherwise, cached results will
  1127. be used."
  1128. ;; Reset the scanned devices list if time has passed.
  1129. (and (integerp tramp-bluez-discover-devices-timeout)
  1130. (integerp tramp-bluez-discovery)
  1131. (> (tramp-time-diff (current-time) tramp-bluez-discovery)
  1132. tramp-bluez-discover-devices-timeout)
  1133. (setq tramp-bluez-devices nil))
  1134. ;; Rescan if needed.
  1135. (unless tramp-bluez-devices
  1136. (let ((object-path
  1137. (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
  1138. :system tramp-bluez-service "/"
  1139. tramp-bluez-interface-manager "DefaultAdapter")))
  1140. (setq tramp-bluez-devices nil
  1141. tramp-bluez-discovery t)
  1142. (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector nil
  1143. :system tramp-bluez-service object-path
  1144. tramp-bluez-interface-adapter "StartDiscovery")
  1145. (while tramp-bluez-discovery
  1146. (read-event nil nil 0.1))))
  1147. (setq tramp-bluez-discovery (current-time))
  1148. (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-bluez-devices)
  1149. tramp-bluez-devices)
  1150. (defun tramp-bluez-property-changed (property value)
  1151. "Signal handler for the \"org.bluez.Adapter.PropertyChanged\" signal."
  1152. (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" property value)
  1153. (cond
  1154. ((string-equal property "Discovering")
  1155. (unless (car value)
  1156. ;; "Discovering" FALSE means discovery run has been completed.
  1157. ;; We stop it, because we don't need another run.
  1158. (setq tramp-bluez-discovery nil)
  1159. (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
  1160. :system tramp-bluez-service (dbus-event-path-name last-input-event)
  1161. tramp-bluez-interface-adapter "StopDiscovery")))))
  1162. (dbus-register-signal
  1163. :system nil nil tramp-bluez-interface-adapter "PropertyChanged"
  1164. 'tramp-bluez-property-changed)
  1165. (defun tramp-bluez-device-found (device args)
  1166. "Signal handler for the \"org.bluez.Adapter.DeviceFound\" signal."
  1167. (tramp-message tramp-gvfs-dbus-event-vector 6 "%s %s" device args)
  1168. (let ((alias (car (cadr (assoc "Alias" args))))
  1169. (address (car (cadr (assoc "Address" args)))))
  1170. ;; Maybe we shall check the device class for being a proper
  1171. ;; device, and call also SDP in order to find the obex service.
  1172. (add-to-list 'tramp-bluez-devices (list alias address))))
  1173. (dbus-register-signal
  1174. :system nil nil tramp-bluez-interface-adapter "DeviceFound"
  1175. 'tramp-bluez-device-found)
  1176. (defun tramp-bluez-parse-device-names (ignore)
  1177. "Return a list of (nil host) tuples allowed to access."
  1178. (mapcar
  1179. (lambda (x) (list nil (car x)))
  1180. (tramp-bluez-list-devices)))
  1181. ;; Add completion function for OBEX method.
  1182. (when (member tramp-bluez-service (dbus-list-known-names :system))
  1183. (tramp-set-completion-function
  1184. "obex" '((tramp-bluez-parse-device-names ""))))
  1185. ;; D-Bus zeroconf functions.
  1186. (defun tramp-zeroconf-parse-workstation-device-names (ignore)
  1187. "Return a list of (user host) tuples allowed to access."
  1188. (mapcar
  1189. (lambda (x)
  1190. (list nil (zeroconf-service-host x)))
  1191. (zeroconf-list-services "_workstation._tcp")))
  1192. (defun tramp-zeroconf-parse-webdav-device-names (ignore)
  1193. "Return a list of (user host) tuples allowed to access."
  1194. (mapcar
  1195. (lambda (x)
  1196. (let ((host (zeroconf-service-host x))
  1197. (port (zeroconf-service-port x))
  1198. (text (zeroconf-service-txt x))
  1199. user)
  1200. (when port
  1201. (setq host (format "%s%s%d" host tramp-prefix-port-regexp port)))
  1202. ;; A user is marked in a TXT field like "u=guest".
  1203. (while text
  1204. (when (string-match "u=\\(.+\\)$" (car text))
  1205. (setq user (match-string 1 (car text))))
  1206. (setq text (cdr text)))
  1207. (list user host)))
  1208. (zeroconf-list-services "_webdav._tcp")))
  1209. ;; Add completion function for DAV and DAVS methods.
  1210. (when (member zeroconf-service-avahi (dbus-list-known-names :system))
  1211. (zeroconf-init tramp-gvfs-zeroconf-domain)
  1212. (tramp-set-completion-function
  1213. "sftp" '((tramp-zeroconf-parse-workstation-device-names "")))
  1214. (tramp-set-completion-function
  1215. "dav" '((tramp-zeroconf-parse-webdav-device-names "")))
  1216. (tramp-set-completion-function
  1217. "davs" '((tramp-zeroconf-parse-webdav-device-names ""))))
  1218. ;; D-Bus SYNCE functions.
  1219. (defun tramp-synce-list-devices ()
  1220. "Return all discovered synce devices as list.
  1221. They are retrieved from the hal daemon."
  1222. (let (tramp-synce-devices)
  1223. (dolist (device
  1224. (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
  1225. :system tramp-hal-service tramp-hal-path-manager
  1226. tramp-hal-interface-manager "GetAllDevices"))
  1227. (when (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
  1228. :system tramp-hal-service device tramp-hal-interface-device
  1229. "PropertyExists" "sync.plugin")
  1230. (add-to-list
  1231. 'tramp-synce-devices
  1232. (with-tramp-dbus-call-method tramp-gvfs-dbus-event-vector t
  1233. :system tramp-hal-service device tramp-hal-interface-device
  1234. "GetPropertyString" "pda.pocketpc.name"))))
  1235. (tramp-message tramp-gvfs-dbus-event-vector 10 "%s" tramp-synce-devices)
  1236. tramp-synce-devices))
  1237. (defun tramp-synce-parse-device-names (ignore)
  1238. "Return a list of (nil host) tuples allowed to access."
  1239. (mapcar
  1240. (lambda (x) (list nil x))
  1241. (tramp-synce-list-devices)))
  1242. ;; Add completion function for SYNCE method.
  1243. (tramp-set-completion-function
  1244. "synce" '((tramp-synce-parse-device-names "")))
  1245. (add-hook 'tramp-unload-hook
  1246. (lambda ()
  1247. (unload-feature 'tramp-gvfs 'force)))
  1248. (provide 'tramp-gvfs)
  1249. ;;; TODO:
  1250. ;; * Host name completion via smb-server or smb-network.
  1251. ;; * Check how two shares of the same SMB server can be mounted in
  1252. ;; parallel.
  1253. ;; * Apply SDP on bluetooth devices, in order to filter out obex
  1254. ;; capability.
  1255. ;; * Implement obex for other serial communication but bluetooth.
  1256. ;;; tramp-gvfs.el ends here