1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* gvfs - extensions for gio
3  *
4  * Copyright (C) 2006-2008 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Author: David Zeuthen <davidz@redhat.com>
22  */
23 
24 #include <config.h>
25 
26 #include <string.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32 #include <gio/gio.h>
33 
34 #include "gproxydrive.h"
35 #include "gproxyvolume.h"
36 #include "gproxymount.h"
37 
38 #include "gproxymountoperation.h"
39 
40 static void signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object);
41 
42 /* Protects all fields of GProxyVolume that can change */
43 G_LOCK_DEFINE_STATIC(proxy_volume);
44 
45 struct _GProxyVolume {
46   GObject parent;
47 
48   GProxyVolumeMonitor *volume_monitor;
49 
50   /* non-NULL only if activation_uri != NULL */
51   GVolumeMonitor *union_monitor;
52 
53   char *id;
54   char *name;
55   char *uuid;
56   char *activation_uri;
57   GIcon *icon;
58   GIcon *symbolic_icon;
59   char *drive_id;
60   char *mount_id;
61   GHashTable *identifiers;
62 
63   gboolean can_mount;
64   gboolean should_automount;
65   gboolean always_call_mount;
66 
67   GProxyShadowMount *shadow_mount;
68 
69   gchar *sort_key;
70 };
71 
72 static void g_proxy_volume_volume_iface_init (GVolumeIface *iface);
73 
74 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolume, g_proxy_volume, G_TYPE_OBJECT, 0,
75                                 G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_VOLUME,
76                                                                g_proxy_volume_volume_iface_init))
77 
78 
79 static void union_monitor_mount_added (GVolumeMonitor *union_monitor,
80                                        GMount         *mount,
81                                        GProxyVolume   *volume);
82 
83 static void union_monitor_mount_removed (GVolumeMonitor *union_monitor,
84                                          GMount         *mount,
85                                          GProxyVolume   *volume);
86 
87 static void union_monitor_mount_changed (GVolumeMonitor *union_monitor,
88                                          GMount         *mount,
89                                          GProxyVolume   *volume);
90 
91 static void update_shadow_mount (GProxyVolume *volume);
92 
93 GProxyShadowMount *
g_proxy_volume_get_shadow_mount(GProxyVolume * volume)94 g_proxy_volume_get_shadow_mount (GProxyVolume *volume)
95 {
96   if (volume->shadow_mount != NULL)
97     return g_object_ref (volume->shadow_mount);
98   else
99     return NULL;
100 }
101 
102 static void
g_proxy_volume_finalize(GObject * object)103 g_proxy_volume_finalize (GObject *object)
104 {
105   GProxyVolume *volume;
106 
107   volume = G_PROXY_VOLUME (object);
108 
109   g_free (volume->id);
110   g_free (volume->name);
111   g_free (volume->uuid);
112   g_free (volume->activation_uri);
113   if (volume->icon != NULL)
114     g_object_unref (volume->icon);
115   if (volume->symbolic_icon != NULL)
116     g_object_unref (volume->symbolic_icon);
117   g_free (volume->drive_id);
118   g_free (volume->mount_id);
119   if (volume->identifiers != NULL)
120     g_hash_table_unref (volume->identifiers);
121 
122   if (volume->volume_monitor != NULL)
123     {
124       g_object_unref (volume->volume_monitor);
125     }
126   g_free (volume->sort_key);
127 
128   if (G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize)
129     (*G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize) (object);
130 }
131 
132 static void
g_proxy_volume_dispose(GObject * object)133 g_proxy_volume_dispose (GObject *object)
134 {
135   GProxyVolume *volume;
136 
137   volume = G_PROXY_VOLUME (object);
138 
139   if (volume->shadow_mount != NULL)
140     {
141       signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL);
142       signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount);
143       g_proxy_shadow_mount_remove (volume->shadow_mount);
144       g_object_unref (volume->shadow_mount);
145 
146       volume->shadow_mount = NULL;
147     }
148 
149   if (volume->union_monitor != NULL)
150     {
151       g_signal_handlers_disconnect_by_func (volume->union_monitor,
152                                             union_monitor_mount_added, volume);
153       g_signal_handlers_disconnect_by_func (volume->union_monitor,
154                                             union_monitor_mount_removed, volume);
155       g_signal_handlers_disconnect_by_func (volume->union_monitor,
156                                             union_monitor_mount_changed, volume);
157       g_object_unref (volume->union_monitor);
158 
159       volume->union_monitor = NULL;
160     }
161 
162   if (G_OBJECT_CLASS (g_proxy_volume_parent_class)->dispose)
163     (*G_OBJECT_CLASS (g_proxy_volume_parent_class)->dispose) (object);
164 }
165 
166 
167 static void
g_proxy_volume_class_init(GProxyVolumeClass * klass)168 g_proxy_volume_class_init (GProxyVolumeClass *klass)
169 {
170   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
171 
172   gobject_class->dispose = g_proxy_volume_dispose;
173   gobject_class->finalize = g_proxy_volume_finalize;
174 }
175 
176 static void
g_proxy_volume_class_finalize(GProxyVolumeClass * klass)177 g_proxy_volume_class_finalize (GProxyVolumeClass *klass)
178 {
179 }
180 
181 static void
g_proxy_volume_init(GProxyVolume * proxy_volume)182 g_proxy_volume_init (GProxyVolume *proxy_volume)
183 {
184 }
185 
186 GProxyVolume *
g_proxy_volume_new(GProxyVolumeMonitor * volume_monitor)187 g_proxy_volume_new (GProxyVolumeMonitor *volume_monitor)
188 {
189   GProxyVolume *volume;
190   volume = g_object_new (G_TYPE_PROXY_VOLUME, NULL);
191   volume->volume_monitor = g_object_ref (volume_monitor);
192   g_object_set_data (G_OBJECT (volume),
193                      "g-proxy-volume-volume-monitor-name",
194                      (gpointer) g_type_name (G_TYPE_FROM_INSTANCE (volume_monitor)));
195   return volume;
196 }
197 
198 static void
union_monitor_mount_added(GVolumeMonitor * union_monitor,GMount * mount,GProxyVolume * volume)199 union_monitor_mount_added (GVolumeMonitor *union_monitor,
200                            GMount         *mount,
201                            GProxyVolume   *volume)
202 {
203   update_shadow_mount (volume);
204 }
205 
206 static void
union_monitor_mount_removed(GVolumeMonitor * union_monitor,GMount * mount,GProxyVolume * volume)207 union_monitor_mount_removed (GVolumeMonitor *union_monitor,
208                              GMount         *mount,
209                              GProxyVolume   *volume)
210 {
211   update_shadow_mount (volume);
212 }
213 
214 static void
union_monitor_mount_changed(GVolumeMonitor * union_monitor,GMount * mount,GProxyVolume * volume)215 union_monitor_mount_changed (GVolumeMonitor *union_monitor,
216                              GMount         *mount,
217                              GProxyVolume   *volume)
218 {
219   if (volume->shadow_mount != NULL)
220     {
221       GMount *real_mount;
222       real_mount = g_proxy_shadow_mount_get_real_mount (volume->shadow_mount);
223       if (mount == real_mount)
224         {
225           signal_emit_in_idle (volume->shadow_mount, "changed", NULL);
226           signal_emit_in_idle (volume->volume_monitor, "mount-changed", volume->shadow_mount);
227         }
228       g_object_unref (real_mount);
229     }
230 }
231 
232 static void
update_shadow_mount(GProxyVolume * volume)233 update_shadow_mount (GProxyVolume *volume)
234 {
235   GFile *activation_root;
236   GList *mounts;
237   GList *l;
238   GMount *mount_to_shadow;
239 
240   activation_root = NULL;
241   mount_to_shadow = NULL;
242 
243   if (volume->activation_uri == NULL)
244     goto out;
245 
246   activation_root = g_file_new_for_uri (volume->activation_uri);
247 
248   if (volume->union_monitor == NULL)
249     {
250       volume->union_monitor = g_volume_monitor_get ();
251       g_signal_connect (volume->union_monitor, "mount-added", (GCallback) union_monitor_mount_added, volume);
252       g_signal_connect (volume->union_monitor, "mount-removed", (GCallback) union_monitor_mount_removed, volume);
253       g_signal_connect (volume->union_monitor, "mount-changed", (GCallback) union_monitor_mount_changed, volume);
254     }
255 
256   mounts = g_volume_monitor_get_mounts (volume->union_monitor);
257   for (l = mounts; l != NULL; l = l->next)
258     {
259       GMount *mount = G_MOUNT (l->data);
260       GFile *mount_root;
261       gboolean prefix_matches;
262 
263       /* don't consider our (possibly) existing shadow mount */
264       if (G_IS_PROXY_SHADOW_MOUNT (mount))
265         continue;
266 
267       mount_root = g_mount_get_root (mount);
268       prefix_matches = (g_file_has_prefix (activation_root, mount_root) ||
269                         g_file_equal (activation_root, mount_root));
270       g_object_unref (mount_root);
271       if (prefix_matches)
272         {
273           mount_to_shadow = g_object_ref (mount);
274           break;
275         }
276     }
277   g_list_free_full (mounts, g_object_unref);
278 
279   if (mount_to_shadow != NULL)
280     {
281       /* there's now a mount to shadow, if we don't have a GProxyShadowMount then create one */
282       if (volume->shadow_mount == NULL)
283         {
284           volume->shadow_mount = g_proxy_shadow_mount_new (volume->volume_monitor,
285                                                            volume,
286                                                            mount_to_shadow);
287           signal_emit_in_idle (volume->volume_monitor, "mount-added", volume->shadow_mount);
288         }
289       else
290         {
291           GFile *current_activation_root;
292 
293           /* we have a GProxyShadowMount already. However, we need to replace it if the
294            * activation root has changed.
295            */
296           current_activation_root = g_proxy_shadow_mount_get_activation_root (volume->shadow_mount);
297           if (!g_file_equal (current_activation_root, activation_root))
298             {
299               signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL);
300               signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount);
301               g_proxy_shadow_mount_remove (volume->shadow_mount);
302               g_object_unref (volume->shadow_mount);
303               volume->shadow_mount = NULL;
304 
305               volume->shadow_mount = g_proxy_shadow_mount_new (volume->volume_monitor,
306                                                                volume,
307                                                                mount_to_shadow);
308               signal_emit_in_idle (volume->volume_monitor, "mount-added", volume->shadow_mount);
309             }
310           g_object_unref (current_activation_root);
311         }
312     }
313   else
314     {
315       /* no mount to shadow; if we have a GProxyShadowMount then remove it */
316       if (volume->shadow_mount != NULL)
317         {
318           signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL);
319           signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount);
320           g_proxy_shadow_mount_remove (volume->shadow_mount);
321           g_object_unref (volume->shadow_mount);
322           volume->shadow_mount = NULL;
323         }
324     }
325 
326  out:
327 
328   if (activation_root != NULL)
329     g_object_unref (activation_root);
330 
331   if (mount_to_shadow != NULL)
332     g_object_unref (mount_to_shadow);
333 }
334 
335 static gboolean
update_shadow_mount_in_idle_do(GProxyVolume * volume)336 update_shadow_mount_in_idle_do (GProxyVolume *volume)
337 {
338   update_shadow_mount (volume);
339   g_object_unref (volume);
340   return FALSE;
341 }
342 
343 static void
update_shadow_mount_in_idle(GProxyVolume * volume)344 update_shadow_mount_in_idle (GProxyVolume *volume)
345 {
346   g_idle_add ((GSourceFunc) update_shadow_mount_in_idle_do, g_object_ref (volume));
347 }
348 
349 /* string               id
350  * string               name
351  * string               gicon_data
352  * string               symbolic_gicon_data
353  * string               uuid
354  * string               activation_uri
355  * boolean              can-mount
356  * boolean              should-automount
357  * string               drive-id
358  * string               mount-id
359  * dict:string->string  identifiers
360  * string               sort_key
361  * a{sv}                expansion
362  */
363 
364 #define VOLUME_STRUCT_TYPE "(&s&s&s&s&s&sbb&s&sa{ss}&s@a{sv})"
365 
g_proxy_volume_update(GProxyVolume * volume,GVariant * iter)366 void g_proxy_volume_update (GProxyVolume    *volume,
367                             GVariant        *iter)
368 {
369   const char *id;
370   const char *name;
371   const char *gicon_data;
372   const char *symbolic_gicon_data = NULL;
373   const char *uuid;
374   const char *activation_uri;
375   const char *drive_id;
376   const char *mount_id;
377   gboolean can_mount;
378   gboolean should_automount;
379   GHashTable *identifiers;
380   const gchar *sort_key;
381   GVariantIter *iter_identifiers;
382   GVariant *expansion;
383 
384   sort_key = NULL;
385   g_variant_get (iter, VOLUME_STRUCT_TYPE,
386                  &id, &name, &gicon_data,
387                  &symbolic_gicon_data,
388                  &uuid, &activation_uri,
389                  &can_mount, &should_automount,
390                  &drive_id, &mount_id,
391                  &iter_identifiers,
392                  &sort_key,
393                  &expansion);
394 
395   identifiers = _get_identifiers (iter_identifiers);
396 
397   if (volume->id != NULL && strcmp (volume->id, id) != 0)
398     {
399       g_warning ("id mismatch during update of volume");
400       goto out;
401     }
402 
403   if (strlen (name) == 0)
404     name = NULL;
405   if (strlen (uuid) == 0)
406     uuid = NULL;
407   if (strlen (activation_uri) == 0)
408     activation_uri = NULL;
409   if (sort_key != NULL && strlen (sort_key) == 0)
410     sort_key = NULL;
411 
412   /* out with the old */
413   g_free (volume->id);
414   g_free (volume->name);
415   g_free (volume->uuid);
416   g_free (volume->activation_uri);
417   if (volume->icon != NULL)
418     g_object_unref (volume->icon);
419   if (volume->symbolic_icon != NULL)
420     g_object_unref (volume->symbolic_icon);
421   g_free (volume->drive_id);
422   g_free (volume->mount_id);
423   if (volume->identifiers != NULL)
424     g_hash_table_unref (volume->identifiers);
425   g_free (volume->sort_key);
426 
427   /* in with the new */
428   volume->id = g_strdup (id);
429   volume->name = g_strdup (name);
430   volume->uuid = g_strdup (uuid);
431   volume->activation_uri = g_strdup (activation_uri);
432   if (*gicon_data == 0)
433     volume->icon = NULL;
434   else
435     volume->icon = g_icon_new_for_string (gicon_data, NULL);
436   if (*symbolic_gicon_data == 0)
437     volume->symbolic_icon = NULL;
438   else
439     volume->symbolic_icon = g_icon_new_for_string (symbolic_gicon_data, NULL);
440   volume->drive_id = g_strdup (drive_id);
441   volume->mount_id = g_strdup (mount_id);
442   volume->can_mount = can_mount;
443   volume->should_automount = should_automount;
444   volume->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
445   volume->sort_key = g_strdup (sort_key);
446 
447   if (volume->activation_uri)
448     {
449       if (!g_variant_lookup (expansion, "always-call-mount", "b", &volume->always_call_mount))
450         volume->always_call_mount = FALSE;
451     }
452   else
453     volume->always_call_mount = FALSE;
454 
455   /* this calls into the union monitor; do it in idle to avoid locking issues */
456   update_shadow_mount_in_idle (volume);
457 
458  out:
459   g_variant_iter_free (iter_identifiers);
460   g_variant_unref (expansion);
461   g_hash_table_unref (identifiers);
462 }
463 
464 const char *
g_proxy_volume_get_id(GProxyVolume * volume)465 g_proxy_volume_get_id (GProxyVolume *volume)
466 {
467   return volume->id;
468 }
469 
470 static GIcon *
g_proxy_volume_get_icon(GVolume * volume)471 g_proxy_volume_get_icon (GVolume *volume)
472 {
473   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
474   GIcon *icon;
475 
476   G_LOCK (proxy_volume);
477   icon = proxy_volume->icon != NULL ? g_object_ref (proxy_volume->icon) : NULL;
478   G_UNLOCK (proxy_volume);
479   return icon;
480 }
481 
482 static GIcon *
g_proxy_volume_get_symbolic_icon(GVolume * volume)483 g_proxy_volume_get_symbolic_icon (GVolume *volume)
484 {
485   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
486   GIcon *icon;
487 
488   G_LOCK (proxy_volume);
489   icon = proxy_volume->symbolic_icon != NULL ? g_object_ref (proxy_volume->symbolic_icon) : NULL;
490   G_UNLOCK (proxy_volume);
491   return icon;
492 }
493 
494 static char *
g_proxy_volume_get_name(GVolume * volume)495 g_proxy_volume_get_name (GVolume *volume)
496 {
497   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
498   char *name;
499 
500   G_LOCK (proxy_volume);
501   name = g_strdup (proxy_volume->name);
502   G_UNLOCK (proxy_volume);
503   return name;
504 }
505 
506 static char *
g_proxy_volume_get_uuid(GVolume * volume)507 g_proxy_volume_get_uuid (GVolume *volume)
508 {
509   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
510   char *uuid;
511 
512   G_LOCK (proxy_volume);
513   uuid = g_strdup (proxy_volume->uuid);
514   G_UNLOCK (proxy_volume);
515   return uuid;
516 }
517 
518 static gboolean
g_proxy_volume_can_mount(GVolume * volume)519 g_proxy_volume_can_mount (GVolume *volume)
520 {
521   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
522   gboolean res;
523 
524   G_LOCK (proxy_volume);
525   res = proxy_volume->can_mount;
526   G_UNLOCK (proxy_volume);
527   return res;
528 }
529 
530 static gboolean
g_proxy_volume_can_eject(GVolume * volume)531 g_proxy_volume_can_eject (GVolume *volume)
532 {
533   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
534   GProxyDrive *drive;
535   gboolean res;
536 
537   G_LOCK (proxy_volume);
538   res = FALSE;
539   if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
540     {
541       drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
542                                                        proxy_volume->drive_id);
543       if (drive != NULL)
544         {
545           res = g_drive_can_eject (G_DRIVE (drive));
546           g_object_unref (drive);
547         }
548     }
549   G_UNLOCK (proxy_volume);
550 
551   return res;
552 }
553 
554 static gboolean
g_proxy_volume_should_automount(GVolume * volume)555 g_proxy_volume_should_automount (GVolume *volume)
556 {
557   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
558   gboolean res;
559 
560   G_LOCK (proxy_volume);
561   res = proxy_volume->should_automount;
562   G_UNLOCK (proxy_volume);
563 
564   return res;
565 }
566 
567 static GDrive *
g_proxy_volume_get_drive(GVolume * volume)568 g_proxy_volume_get_drive (GVolume *volume)
569 {
570   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
571   GProxyDrive *drive;
572 
573   G_LOCK (proxy_volume);
574   drive = NULL;
575   if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
576     drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
577                                                      proxy_volume->drive_id);
578   G_UNLOCK (proxy_volume);
579 
580   return drive != NULL ? G_DRIVE (drive) : NULL;
581 }
582 
583 static GMount *
g_proxy_volume_get_mount(GVolume * volume)584 g_proxy_volume_get_mount (GVolume *volume)
585 {
586   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
587   GMount *mount;
588 
589   mount = NULL;
590 
591   G_LOCK (proxy_volume);
592 
593   if (proxy_volume->shadow_mount != NULL)
594     {
595       mount = G_MOUNT (g_object_ref (proxy_volume->shadow_mount));
596     }
597   else if (proxy_volume->mount_id != NULL && strlen (proxy_volume->mount_id) > 0)
598     {
599       GProxyMount *proxy_mount;
600       proxy_mount = g_proxy_volume_monitor_get_mount_for_id (proxy_volume->volume_monitor,
601                                                              proxy_volume->mount_id);
602       if (proxy_mount != NULL)
603         mount = G_MOUNT (proxy_mount);
604     }
605   G_UNLOCK (proxy_volume);
606 
607   return mount;
608 }
609 
610 typedef struct {
611   GObject *object;
612   GAsyncReadyCallback callback;
613   gpointer user_data;
614 } EjectWrapperOp;
615 
616 static void
eject_wrapper_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)617 eject_wrapper_callback (GObject *source_object,
618                         GAsyncResult *res,
619                         gpointer user_data)
620 {
621   EjectWrapperOp *data  = user_data;
622   data->callback (data->object, res, data->user_data);
623   g_object_unref (data->object);
624   g_free (data);
625 }
626 
627 static void
g_proxy_volume_eject_with_operation(GVolume * volume,GMountUnmountFlags flags,GMountOperation * mount_operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)628 g_proxy_volume_eject_with_operation (GVolume             *volume,
629                                      GMountUnmountFlags   flags,
630                                      GMountOperation     *mount_operation,
631                                      GCancellable        *cancellable,
632                                      GAsyncReadyCallback  callback,
633                                      gpointer             user_data)
634 {
635   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
636   GProxyDrive *drive;
637 
638   drive = NULL;
639   G_LOCK (proxy_volume);
640   if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
641     {
642       drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
643                                                        proxy_volume->drive_id);
644     }
645   G_UNLOCK (proxy_volume);
646 
647   if (drive != NULL)
648     {
649       EjectWrapperOp *data;
650       data = g_new0 (EjectWrapperOp, 1);
651       data->object = G_OBJECT (g_object_ref (volume));
652       data->callback = callback;
653       data->user_data = user_data;
654       g_drive_eject_with_operation (G_DRIVE (drive), flags, mount_operation, cancellable, eject_wrapper_callback, data);
655       g_object_unref (drive);
656     }
657 }
658 
659 static gboolean
g_proxy_volume_eject_with_operation_finish(GVolume * volume,GAsyncResult * result,GError ** error)660 g_proxy_volume_eject_with_operation_finish (GVolume        *volume,
661                                             GAsyncResult  *result,
662                                             GError       **error)
663 {
664   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
665   GProxyDrive *drive;
666   gboolean res;
667 
668   G_LOCK (proxy_volume);
669   res = TRUE;
670   drive = NULL;
671   if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
672     drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
673                                                      proxy_volume->drive_id);
674   G_UNLOCK (proxy_volume);
675 
676   if (drive != NULL)
677     {
678       res = g_drive_eject_with_operation_finish (G_DRIVE (drive), result, error);
679       g_object_unref (drive);
680     }
681 
682   return res;
683 }
684 
685 /* ---------------------------------------------------------------------------------------------------- */
686 
687 static void
g_proxy_volume_eject(GVolume * volume,GMountUnmountFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)688 g_proxy_volume_eject (GVolume              *volume,
689                       GMountUnmountFlags   flags,
690                       GCancellable        *cancellable,
691                       GAsyncReadyCallback  callback,
692                       gpointer             user_data)
693 {
694   g_proxy_volume_eject_with_operation (volume, flags, NULL, cancellable, callback, user_data);
695 }
696 
697 static gboolean
g_proxy_volume_eject_finish(GVolume * volume,GAsyncResult * result,GError ** error)698 g_proxy_volume_eject_finish (GVolume        *volume,
699                              GAsyncResult  *result,
700                              GError       **error)
701 {
702   return g_proxy_volume_eject_with_operation_finish (volume, result, error);
703 }
704 
705 /* ---------------------------------------------------------------------------------------------------- */
706 
707 static char *
g_proxy_volume_get_identifier(GVolume * volume,const char * kind)708 g_proxy_volume_get_identifier (GVolume              *volume,
709                                const char          *kind)
710 {
711   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
712   char *res;
713 
714   G_LOCK (proxy_volume);
715   if (proxy_volume->identifiers != NULL)
716     res = g_strdup (g_hash_table_lookup (proxy_volume->identifiers, kind));
717   else
718     res = NULL;
719   G_UNLOCK (proxy_volume);
720 
721   return res;
722 }
723 
724 static void
add_identifier_key(const char * key,const char * value,GPtrArray * res)725 add_identifier_key (const char *key, const char *value, GPtrArray *res)
726 {
727   g_ptr_array_add (res, g_strdup (key));
728 }
729 
730 static char **
g_proxy_volume_enumerate_identifiers(GVolume * volume)731 g_proxy_volume_enumerate_identifiers (GVolume *volume)
732 {
733   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
734   GPtrArray *res;
735 
736   res = g_ptr_array_new ();
737 
738   G_LOCK (proxy_volume);
739   if (proxy_volume->identifiers != NULL)
740     g_hash_table_foreach (proxy_volume->identifiers, (GHFunc) add_identifier_key, res);
741   G_UNLOCK (proxy_volume);
742 
743   /* Null-terminate */
744   g_ptr_array_add (res, NULL);
745 
746   return (char **) g_ptr_array_free (res, FALSE);
747 }
748 
749 typedef struct {
750   gchar *cancellation_id;
751   gulong cancelled_handler_id;
752 
753   const gchar *mount_op_id;
754 } DBusOp;
755 
756 static void
dbus_op_free(DBusOp * data)757 dbus_op_free (DBusOp *data)
758 {
759   g_free (data->cancellation_id);
760 
761   if (data->mount_op_id)
762     g_proxy_mount_operation_destroy (data->mount_op_id);
763 
764   g_free (data);
765 }
766 
767 static void
mount_cb(GVfsRemoteVolumeMonitor * proxy,GAsyncResult * res,gpointer user_data)768 mount_cb (GVfsRemoteVolumeMonitor *proxy,
769           GAsyncResult *res,
770           gpointer user_data)
771 {
772   GTask *task = G_TASK (user_data);
773   DBusOp *data = g_task_get_task_data (task);
774   GError *error = NULL;
775 
776   gvfs_remote_volume_monitor_call_volume_mount_finish (proxy,
777                                                        res,
778                                                        &error);
779 
780   if (data->cancelled_handler_id > 0)
781     g_signal_handler_disconnect (g_task_get_cancellable (task), data->cancelled_handler_id);
782 
783   if (!g_cancellable_is_cancelled (g_task_get_cancellable (task)))
784     {
785       if (error != NULL)
786         {
787           g_dbus_error_strip_remote_error (error);
788           g_task_return_error (task, error);
789           error = NULL;
790         }
791       else
792         {
793           g_task_return_boolean (task, TRUE);
794         }
795     }
796 
797   g_object_unref (task);
798   if (error != NULL)
799     g_error_free (error);
800 }
801 
802 static void
mount_foreign_callback(GObject * source_object,GAsyncResult * res,gpointer user_data)803 mount_foreign_callback (GObject *source_object,
804                         GAsyncResult *res,
805                         gpointer user_data)
806 {
807   GTask *task = G_TASK (user_data);
808   GError *error = NULL;
809 
810   if (g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error))
811     g_task_return_boolean (task, TRUE);
812   else
813     g_task_return_error (task, error);
814 }
815 
816 static void
cancel_operation_reply_cb(GVfsRemoteVolumeMonitor * proxy,GAsyncResult * res,gpointer user_data)817 cancel_operation_reply_cb (GVfsRemoteVolumeMonitor *proxy,
818                            GAsyncResult *res,
819                            gpointer user_data)
820 {
821   gboolean out_WasCancelled;
822   GError *error = NULL;
823 
824   if (!gvfs_remote_volume_monitor_call_cancel_operation_finish (proxy,
825                                                                 &out_WasCancelled,
826                                                                 res,
827                                                                 &error))
828     {
829       g_warning ("Error from CancelOperation(): %s", error->message);
830       g_error_free (error);
831     }
832 }
833 
834 static void
mount_cancelled(GCancellable * cancellable,gpointer user_data)835 mount_cancelled (GCancellable *cancellable,
836                  gpointer      user_data)
837 {
838   GTask *task = G_TASK (user_data);
839   DBusOp *data = g_task_get_task_data (task);
840   GProxyVolume *volume = G_PROXY_VOLUME (g_task_get_source_object (task));
841   GVfsRemoteVolumeMonitor *proxy;
842 
843   G_LOCK (proxy_volume);
844 
845   /* Now tell the remote volume monitor that the op has been cancelled */
846   proxy = g_proxy_volume_monitor_get_dbus_proxy (volume->volume_monitor);
847   gvfs_remote_volume_monitor_call_cancel_operation (proxy,
848                                                     data->cancellation_id,
849                                                     NULL,
850                                                     (GAsyncReadyCallback) cancel_operation_reply_cb,
851                                                     NULL);
852   g_object_unref (proxy);
853 
854   G_UNLOCK (proxy_volume);
855 
856   g_task_return_error_if_cancelled (task);
857 }
858 
859 static void
g_proxy_volume_mount(GVolume * volume,GMountMountFlags flags,GMountOperation * mount_operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)860 g_proxy_volume_mount (GVolume             *volume,
861                       GMountMountFlags     flags,
862                       GMountOperation     *mount_operation,
863                       GCancellable        *cancellable,
864                       GAsyncReadyCallback  callback,
865                       gpointer             user_data)
866 {
867   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
868   GTask *task;
869 
870   task = g_task_new (volume, cancellable, callback, user_data);
871   g_task_set_source_tag (task, g_proxy_volume_mount);
872 
873   G_LOCK (proxy_volume);
874   if (proxy_volume->activation_uri != NULL &&
875       !proxy_volume->always_call_mount)
876     {
877       GFile *root;
878 
879       root = g_file_new_for_uri (proxy_volume->activation_uri);
880 
881       G_UNLOCK (proxy_volume);
882 
883       g_file_mount_enclosing_volume (root,
884                                      flags,
885                                      mount_operation,
886                                      cancellable,
887                                      mount_foreign_callback,
888                                      task);
889 
890       g_object_unref (root);
891     }
892   else
893     {
894       DBusOp *data;
895       GVfsRemoteVolumeMonitor *proxy;
896 
897       if (g_cancellable_is_cancelled (cancellable))
898         {
899           G_UNLOCK (proxy_volume);
900           g_task_return_error_if_cancelled (task);
901           g_object_unref (task);
902           return;
903         }
904 
905       data = g_new0 (DBusOp, 1);
906       if (cancellable != NULL)
907         {
908           data->cancellation_id = g_strdup_printf ("%p", cancellable);
909           data->cancelled_handler_id = g_signal_connect (cancellable,
910                                                          "cancelled",
911                                                          G_CALLBACK (mount_cancelled),
912                                                          task);
913         }
914       else
915         {
916           data->cancellation_id = g_strdup ("");
917         }
918 
919       data->mount_op_id = g_proxy_mount_operation_wrap (mount_operation, proxy_volume->volume_monitor);
920 
921       g_task_set_task_data (task, data, (GDestroyNotify)dbus_op_free);
922 
923       proxy = g_proxy_volume_monitor_get_dbus_proxy (proxy_volume->volume_monitor);
924       g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT);  /* 30 minute timeout */
925 
926       gvfs_remote_volume_monitor_call_volume_mount (proxy,
927                                                     proxy_volume->id,
928                                                     data->cancellation_id,
929                                                     flags,
930                                                     data->mount_op_id,
931                                                     NULL,
932                                                     (GAsyncReadyCallback) mount_cb,
933                                                     task);
934 
935       g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), -1);
936       g_object_unref (proxy);
937 
938       G_UNLOCK (proxy_volume);
939     }
940 }
941 
942 static gboolean
g_proxy_volume_mount_finish(GVolume * volume,GAsyncResult * result,GError ** error)943 g_proxy_volume_mount_finish (GVolume        *volume,
944                              GAsyncResult  *result,
945                              GError       **error)
946 {
947   g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);
948   g_return_val_if_fail (g_async_result_is_tagged (result, g_proxy_volume_mount), FALSE);
949 
950   return g_task_propagate_boolean (G_TASK (result), error);
951 }
952 
953 static GFile *
g_proxy_volume_get_activation_root(GVolume * volume)954 g_proxy_volume_get_activation_root (GVolume *volume)
955 {
956   GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
957   if (proxy_volume->activation_uri == NULL)
958     return NULL;
959   else
960     return g_file_new_for_uri (proxy_volume->activation_uri);
961 }
962 
963 static const gchar *
g_proxy_volume_get_sort_key(GVolume * _volume)964 g_proxy_volume_get_sort_key (GVolume *_volume)
965 {
966   GProxyVolume *volume = G_PROXY_VOLUME (_volume);
967   return volume->sort_key;
968 }
969 
970 static void
g_proxy_volume_volume_iface_init(GVolumeIface * iface)971 g_proxy_volume_volume_iface_init (GVolumeIface *iface)
972 {
973   iface->get_name = g_proxy_volume_get_name;
974   iface->get_icon = g_proxy_volume_get_icon;
975   iface->get_symbolic_icon = g_proxy_volume_get_symbolic_icon;
976   iface->get_uuid = g_proxy_volume_get_uuid;
977   iface->get_drive = g_proxy_volume_get_drive;
978   iface->get_mount = g_proxy_volume_get_mount;
979   iface->can_mount = g_proxy_volume_can_mount;
980   iface->can_eject = g_proxy_volume_can_eject;
981   iface->should_automount = g_proxy_volume_should_automount;
982   iface->mount_fn = g_proxy_volume_mount;
983   iface->mount_finish = g_proxy_volume_mount_finish;
984   iface->eject = g_proxy_volume_eject;
985   iface->eject_finish = g_proxy_volume_eject_finish;
986   iface->eject_with_operation = g_proxy_volume_eject_with_operation;
987   iface->eject_with_operation_finish = g_proxy_volume_eject_with_operation_finish;
988   iface->get_identifier = g_proxy_volume_get_identifier;
989   iface->enumerate_identifiers = g_proxy_volume_enumerate_identifiers;
990   iface->get_activation_root = g_proxy_volume_get_activation_root;
991   iface->get_sort_key = g_proxy_volume_get_sort_key;
992 }
993 
994 void
g_proxy_volume_register(GIOModule * module)995 g_proxy_volume_register (GIOModule *module)
996 {
997   g_proxy_volume_register_type (G_TYPE_MODULE (module));
998 }
999 
1000 typedef struct {
1001   const char *signal_name;
1002   GObject *object;
1003   GObject *other_object;
1004 } SignalEmitIdleData;
1005 
1006 static gboolean
signal_emit_in_idle_do(SignalEmitIdleData * data)1007 signal_emit_in_idle_do (SignalEmitIdleData *data)
1008 {
1009   if (data->other_object != NULL)
1010     {
1011       g_signal_emit_by_name (data->object, data->signal_name, data->other_object);
1012       g_object_unref (data->other_object);
1013     }
1014   else
1015     {
1016       g_signal_emit_by_name (data->object, data->signal_name);
1017     }
1018   g_object_unref (data->object);
1019   g_free (data);
1020 
1021   return FALSE;
1022 }
1023 
1024 static void
signal_emit_in_idle(gpointer object,const char * signal_name,gpointer other_object)1025 signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object)
1026 {
1027   SignalEmitIdleData *data;
1028 
1029   data = g_new0 (SignalEmitIdleData, 1);
1030   data->signal_name = signal_name;
1031   data->object = g_object_ref (G_OBJECT (object));
1032   data->other_object = other_object != NULL ? g_object_ref (G_OBJECT (other_object)) : NULL;
1033   g_idle_add ((GSourceFunc) signal_emit_in_idle_do, data);
1034 }
1035