1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* gvfs - extensions for gio
3 *
4 * Copyright (C) 2012, 2013 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: Debarshi Ray <debarshir@gnome.org>
22 */
23
24 #include <config.h>
25
26 #include <stdio.h>
27
28 #include <glib.h>
29 #include <gio/gio.h>
30
31 #define GOA_API_IS_SUBJECT_TO_CHANGE
32 #include <goa/goa.h>
33
34 #include <gvfsproxyvolumemonitordaemon.h>
35 #include "goavolume.h"
36 #include "goavolumemonitor.h"
37
38 struct _GVfsGoaVolumeMonitor
39 {
40 GNativeVolumeMonitor parent;
41 GList *accounts;
42 GList *volumes;
43 GoaClient *client;
44 };
45
46 struct _GVfsGoaVolumeMonitorClass
47 {
48 GVolumeMonitorClass parent_class;
49 };
50
G_DEFINE_TYPE(GVfsGoaVolumeMonitor,g_vfs_goa_volume_monitor,G_TYPE_VOLUME_MONITOR)51 G_DEFINE_TYPE(GVfsGoaVolumeMonitor, g_vfs_goa_volume_monitor, G_TYPE_VOLUME_MONITOR)
52
53 /* ---------------------------------------------------------------------------------------------------- */
54
55 static void
56 diff_sorted_lists (GList *list1,
57 GList *list2,
58 GCompareFunc compare,
59 GList **added,
60 GList **removed,
61 GList **unchanged)
62 {
63 int order;
64
65 *added = *removed = NULL;
66 if (unchanged != NULL)
67 *unchanged = NULL;
68
69 while (list1 != NULL &&
70 list2 != NULL)
71 {
72 order = (*compare) (list1->data, list2->data);
73 if (order < 0)
74 {
75 *removed = g_list_prepend (*removed, list1->data);
76 list1 = list1->next;
77 }
78 else if (order > 0)
79 {
80 *added = g_list_prepend (*added, list2->data);
81 list2 = list2->next;
82 }
83 else
84 { /* same item */
85 if (unchanged != NULL)
86 *unchanged = g_list_prepend (*unchanged, list1->data);
87 list1 = list1->next;
88 list2 = list2->next;
89 }
90 }
91
92 while (list1 != NULL)
93 {
94 *removed = g_list_prepend (*removed, list1->data);
95 list1 = list1->next;
96 }
97 while (list2 != NULL)
98 {
99 *added = g_list_prepend (*added, list2->data);
100 list2 = list2->next;
101 }
102 }
103
104 /* ---------------------------------------------------------------------------------------------------- */
105
106 static gint
account_compare(GoaObject * a,GoaObject * b)107 account_compare (GoaObject *a, GoaObject *b)
108 {
109 GoaAccount *account_a;
110 GoaAccount *account_b;
111
112 account_a = goa_object_peek_account (a);
113 account_b = goa_object_peek_account (b);
114
115 return g_strcmp0 (goa_account_get_id (account_a), goa_account_get_id (account_b));
116 }
117
118 /* ---------------------------------------------------------------------------------------------------- */
119
120 static gint
uuid_compare(GVolume * volume_a,const gchar * uuid_b)121 uuid_compare (GVolume *volume_a, const gchar *uuid_b)
122 {
123 gchar *uuid_a;
124 gint ret_val;
125
126 uuid_a = g_volume_get_uuid (volume_a);
127 ret_val = g_strcmp0 (uuid_a, uuid_b);
128 g_free (uuid_a);
129
130 return ret_val;
131 }
132
133 /* ---------------------------------------------------------------------------------------------------- */
134
135 static gint
volume_compare(GVolume * a,GVolume * b)136 volume_compare (GVolume *a, GVolume *b)
137 {
138 gchar *uuid_a;
139 gchar *uuid_b;
140 gint ret_val;
141
142 uuid_a = g_volume_get_uuid (a);
143 uuid_b = g_volume_get_uuid (b);
144
145 ret_val = g_strcmp0 (uuid_a, uuid_b);
146
147 g_free (uuid_a);
148 g_free (uuid_b);
149
150 return ret_val;
151 }
152
153 /* ---------------------------------------------------------------------------------------------------- */
154
155 static void
object_list_emit(GVfsGoaVolumeMonitor * monitor,const gchar * monitor_signal,const gchar * object_signal,GList * objects)156 object_list_emit (GVfsGoaVolumeMonitor *monitor,
157 const gchar *monitor_signal,
158 const gchar *object_signal,
159 GList *objects)
160 {
161 GList *l;
162
163 for (l = objects; l != NULL; l = l->next)
164 {
165 g_signal_emit_by_name (monitor, monitor_signal, l->data);
166 if (object_signal)
167 g_signal_emit_by_name (l->data, object_signal);
168 }
169 }
170
171 /* ---------------------------------------------------------------------------------------------------- */
172
173 static void
update_accounts(GVfsGoaVolumeMonitor * self,GList ** added_accounts,GList ** removed_accounts)174 update_accounts (GVfsGoaVolumeMonitor *self, GList **added_accounts, GList **removed_accounts)
175 {
176 GList *added;
177 GList *l;
178 GList *new_accounts;
179 GList *new_files_accounts = NULL;
180 GList *removed;
181
182 if (added_accounts != NULL)
183 *added_accounts = NULL;
184
185 if (removed_accounts != NULL)
186 *removed_accounts = NULL;
187
188 new_accounts = goa_client_get_accounts (self->client);
189 for (l = new_accounts; l != NULL; l = l->next)
190 {
191 GoaObject *object = GOA_OBJECT (l->data);
192
193 if (goa_object_peek_files (object) != NULL)
194 new_files_accounts = g_list_prepend (new_files_accounts, object);
195 }
196
197 new_files_accounts = g_list_sort (new_files_accounts, (GCompareFunc) account_compare);
198 diff_sorted_lists (self->accounts,
199 new_files_accounts,
200 (GCompareFunc) account_compare,
201 &added,
202 &removed,
203 NULL);
204
205 for (l = removed; l != NULL; l = l->next)
206 {
207 GList *llink;
208 GoaObject *object = GOA_OBJECT (l->data);
209
210 if (removed_accounts != NULL)
211 *removed_accounts = g_list_prepend (*removed_accounts, g_object_ref (object));
212
213 llink = g_list_find_custom (self->accounts, object, (GCompareFunc) account_compare);
214 self->accounts = g_list_remove_link (self->accounts, llink);
215 g_list_free_full (llink, g_object_unref);
216 }
217
218 for (l = added; l != NULL; l = l->next)
219 {
220 GoaObject *object = GOA_OBJECT (l->data);
221
222 self->accounts = g_list_prepend (self->accounts, g_object_ref (object));
223
224 if (added_accounts != NULL)
225 *added_accounts = g_list_prepend (*added_accounts, g_object_ref (object));
226 }
227
228 self->accounts = g_list_sort (self->accounts, (GCompareFunc) account_compare);
229
230 g_list_free (added);
231 g_list_free (removed);
232 g_list_free (new_files_accounts);
233 g_list_free_full (new_accounts, g_object_unref);
234 }
235
236 /* ---------------------------------------------------------------------------------------------------- */
237
238 static void
update_volumes(GVfsGoaVolumeMonitor * self,GList ** added_volumes,GList ** removed_volumes)239 update_volumes (GVfsGoaVolumeMonitor *self, GList **added_volumes, GList **removed_volumes)
240 {
241 GList *added;
242 GList *l;
243 GList *new_volumes = NULL;
244 GList *removed;
245
246 if (added_volumes != NULL)
247 *added_volumes = NULL;
248
249 if (removed_volumes != NULL)
250 *removed_volumes = NULL;
251
252 for (l = self->accounts; l != NULL; l = l->next)
253 {
254 GVolume *volume;
255 GoaFiles *files;
256 GoaObject *object = GOA_OBJECT (l->data);
257 const gchar *uri;
258
259 files = goa_object_peek_files (object);
260 uri = goa_files_get_uri (files);
261
262 volume = g_vfs_goa_volume_new (object, uri);
263 new_volumes = g_list_prepend (new_volumes, volume);
264 }
265
266 new_volumes = g_list_sort (new_volumes, (GCompareFunc) volume_compare);
267 diff_sorted_lists (self->volumes,
268 new_volumes,
269 (GCompareFunc) volume_compare,
270 &added,
271 &removed,
272 NULL);
273
274 for (l = removed; l != NULL; l = l->next)
275 {
276 GList *llink;
277 GVolume *volume = G_VOLUME (l->data);
278
279 if (removed_volumes != NULL)
280 *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume));
281
282 llink = g_list_find_custom (self->volumes, volume, (GCompareFunc) volume_compare);
283 self->volumes = g_list_remove_link (self->volumes, llink);
284 g_list_free_full (llink, g_object_unref);
285 }
286
287 for (l = added; l != NULL; l = l->next)
288 {
289 GVolume *volume = G_VOLUME (l->data);
290
291 self->volumes = g_list_prepend (self->volumes, g_object_ref (volume));
292
293 if (added_volumes != NULL)
294 *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
295 }
296
297 self->volumes = g_list_sort (self->volumes, (GCompareFunc) volume_compare);
298
299 g_list_free (added);
300 g_list_free (removed);
301 g_list_free_full (new_volumes, g_object_unref);
302 }
303
304 /* ---------------------------------------------------------------------------------------------------- */
305
306 static void
update_all(GVfsGoaVolumeMonitor * self)307 update_all (GVfsGoaVolumeMonitor *self)
308 {
309 GList *added_volumes;
310 GList *removed_volumes;
311
312 update_accounts (self, NULL, NULL);
313 update_volumes (self, &added_volumes, &removed_volumes);
314
315 object_list_emit (self, "volume-removed", "removed", removed_volumes);
316 object_list_emit (self, "volume-added", NULL, added_volumes);
317
318 g_list_free_full (added_volumes, g_object_unref);
319 g_list_free_full (removed_volumes, g_object_unref);
320 }
321
322 /* ---------------------------------------------------------------------------------------------------- */
323
324 static GoaClient *
get_goa_client_sync(GError ** error)325 get_goa_client_sync (GError **error)
326 {
327 static GoaClient *client = NULL;
328 static GError *_error = NULL;
329 static volatile gsize initialized = 0;
330
331 if (g_once_init_enter (&initialized))
332 {
333 client = goa_client_new_sync (NULL, &_error);
334 g_once_init_leave (&initialized, 1);
335 }
336
337 if (_error != NULL && error != NULL)
338 *error = g_error_copy (_error);
339
340 return client;
341 }
342
343 /* ---------------------------------------------------------------------------------------------------- */
344
345 static GList *
g_vfs_goa_volume_monitor_get_connected_drives(GVolumeMonitor * _self)346 g_vfs_goa_volume_monitor_get_connected_drives (GVolumeMonitor *_self)
347 {
348 return NULL;
349 }
350
351 static GMount *
g_vfs_goa_volume_monitor_get_mount_for_uuid(GVolumeMonitor * _self,const gchar * uuid)352 g_vfs_goa_volume_monitor_get_mount_for_uuid (GVolumeMonitor *_self, const gchar *uuid)
353 {
354 GMount *mount;
355 GVolume *volume;
356
357 mount = NULL;
358
359 volume = g_volume_monitor_get_volume_for_uuid (_self, uuid);
360 if (volume != NULL)
361 mount = g_volume_get_mount (G_VOLUME (volume));
362
363 return mount;
364 }
365
366 static GList *
g_vfs_goa_volume_monitor_get_mounts(GVolumeMonitor * _self)367 g_vfs_goa_volume_monitor_get_mounts (GVolumeMonitor *_self)
368 {
369 GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self);
370 GList *l;
371 GList *mounts = NULL;
372
373 for (l = self->volumes; l != NULL; l = l->next)
374 {
375 GMount *mount;
376 GVolume *volume = G_VOLUME (l->data);
377
378 mount = g_volume_get_mount (volume);
379 if (mount != NULL)
380 mounts = g_list_prepend (mounts, mount);
381 }
382
383 return mounts;
384 }
385
386 static GVolume *
g_vfs_goa_volume_monitor_get_volume_for_uuid(GVolumeMonitor * _self,const gchar * uuid)387 g_vfs_goa_volume_monitor_get_volume_for_uuid (GVolumeMonitor *_self, const gchar *uuid)
388 {
389 GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self);
390 GList *llink;
391 GVolume *volume = NULL;
392
393 llink = g_list_find_custom (self->volumes, uuid, (GCompareFunc) uuid_compare);
394 if (llink != NULL)
395 volume = G_VOLUME (g_object_ref (llink->data));
396
397 return volume;
398 }
399
400 static GList *
g_vfs_goa_volume_monitor_get_volumes(GVolumeMonitor * _self)401 g_vfs_goa_volume_monitor_get_volumes (GVolumeMonitor *_self)
402 {
403 GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self);
404
405 return g_list_copy_deep (self->volumes, (GCopyFunc) g_object_ref, NULL);
406 }
407
408 static gboolean
g_vfs_goa_volume_monitor_is_supported(void)409 g_vfs_goa_volume_monitor_is_supported (void)
410 {
411 if (get_goa_client_sync (NULL) != NULL)
412 return TRUE;
413 return FALSE;
414 }
415
416 static void
g_vfs_goa_volume_monitor_dispose(GObject * _self)417 g_vfs_goa_volume_monitor_dispose (GObject *_self)
418 {
419 GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self);
420
421 g_list_free_full (self->accounts, g_object_unref);
422 self->accounts = NULL;
423
424 g_list_free_full (self->volumes, g_object_unref);
425 self->volumes = NULL;
426
427 g_clear_object (&self->client);
428
429 G_OBJECT_CLASS (g_vfs_goa_volume_monitor_parent_class)->dispose (_self);
430 }
431
432 static void
g_vfs_goa_volume_monitor_class_init(GVfsGoaVolumeMonitorClass * klass)433 g_vfs_goa_volume_monitor_class_init (GVfsGoaVolumeMonitorClass * klass)
434 {
435 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
436 GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
437
438 gobject_class->dispose = g_vfs_goa_volume_monitor_dispose;
439
440 monitor_class->get_connected_drives = g_vfs_goa_volume_monitor_get_connected_drives;
441 monitor_class->get_mount_for_uuid = g_vfs_goa_volume_monitor_get_mount_for_uuid;
442 monitor_class->get_mounts = g_vfs_goa_volume_monitor_get_mounts;
443 monitor_class->get_volume_for_uuid = g_vfs_goa_volume_monitor_get_volume_for_uuid;
444 monitor_class->get_volumes = g_vfs_goa_volume_monitor_get_volumes;
445 monitor_class->is_supported = g_vfs_goa_volume_monitor_is_supported;
446 }
447
448 static void
g_vfs_goa_volume_monitor_init(GVfsGoaVolumeMonitor * self)449 g_vfs_goa_volume_monitor_init (GVfsGoaVolumeMonitor *self)
450 {
451 GError *error;
452
453 error = NULL;
454 self->client = get_goa_client_sync (&error);
455 if (self->client == NULL)
456 {
457 g_warning ("Failed to connect to GOA: %s", error->message);
458 g_error_free (error);
459 return;
460 }
461
462 update_all (self);
463
464 g_signal_connect_swapped (self->client, "account-added", G_CALLBACK (update_all), self);
465 g_signal_connect_swapped (self->client, "account-changed", G_CALLBACK (update_all), self);
466 g_signal_connect_swapped (self->client, "account-removed", G_CALLBACK (update_all), self);
467
468 g_vfs_proxy_volume_monitor_daemon_set_always_call_mount (TRUE);
469 }
470
471 GVolumeMonitor *
g_vfs_goa_volume_monitor_new(void)472 g_vfs_goa_volume_monitor_new (void)
473 {
474 return g_object_new (G_VFS_TYPE_GOA_VOLUME_MONITOR, NULL);
475 }
476