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