1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22 
23 #include <config.h>
24 
25 #include <string.h>
26 
27 #include <gmounttracker.h>
28 #include <gvfsdaemonprotocol.h>
29 #include <gvfsdbus.h>
30 
31 enum {
32   MOUNTED,
33   UNMOUNTED,
34   LAST_SIGNAL
35 };
36 
37 enum {
38   PROP_0,
39   PROP_CONNECTION,
40   PROP_USER_VISIBLE_ONLY
41 };
42 
43 /* TODO: Real P_() */
44 #define P_(_x) (_x)
45 #define I_(string) g_intern_static_string (string)
46 
47 static guint signals[LAST_SIGNAL] = { 0 };
48 
49 struct _GMountTracker
50 {
51   GObject parent_instance;
52 
53   GMutex lock;
54   GList *mounts;
55   GDBusConnection *connection;
56   GVfsDBusMountTracker *proxy;
57 
58   gboolean user_visible_only;
59 };
60 
61 G_DEFINE_TYPE (GMountTracker, g_mount_tracker, G_TYPE_OBJECT)
62 
63 static GObject*          g_mount_tracker_constructor  (GType                  type,
64 						       guint                  n_construct_properties,
65 						       GObjectConstructParam *construct_params);
66 static void              g_mount_tracker_set_property (GObject               *object,
67 						       guint                  prop_id,
68 						       const GValue          *value,
69 						       GParamSpec            *pspec);
70 static void              g_mount_tracker_get_property (GObject               *object,
71 						       guint                  prop_id,
72 						       GValue                *value,
73 						       GParamSpec            *pspec);
74 
75 gboolean
g_mount_info_equal(GMountInfo * info1,GMountInfo * info2)76 g_mount_info_equal (GMountInfo *info1,
77 		    GMountInfo *info2)
78 {
79   return
80     strcmp (info1->dbus_id, info2->dbus_id) == 0 &&
81     strcmp (info1->object_path, info2->object_path) == 0;
82 }
83 
84 GMountInfo *
g_mount_info_dup(GMountInfo * info)85 g_mount_info_dup (GMountInfo *info)
86 {
87   GMountInfo *copy;
88 
89   copy = g_new (GMountInfo, 1);
90   copy->ref_count = 1;
91   copy->display_name = g_strdup (info->display_name);
92   copy->stable_name = g_strdup (info->stable_name);
93   copy->x_content_types = g_strdup (info->x_content_types);
94   copy->icon = g_object_ref (info->icon);
95   copy->symbolic_icon = g_object_ref (info->symbolic_icon);
96   copy->dbus_id = g_strdup (info->dbus_id);
97   copy->object_path = g_strdup (info->object_path);
98   copy->mount_spec = g_mount_spec_copy (info->mount_spec);
99   copy->user_visible = info->user_visible;
100   copy->prefered_filename_encoding = g_strdup (info->prefered_filename_encoding);
101   copy->fuse_mountpoint = g_strdup (info->fuse_mountpoint);
102   copy->default_location = g_strdup (info->default_location);
103 
104   return copy;
105 }
106 
107 GMountInfo *
g_mount_info_ref(GMountInfo * info)108 g_mount_info_ref (GMountInfo *info)
109 {
110   g_atomic_int_inc (&info->ref_count);
111   return info;
112 }
113 
114 void
g_mount_info_unref(GMountInfo * info)115 g_mount_info_unref (GMountInfo *info)
116 {
117   if (g_atomic_int_dec_and_test (&info->ref_count))
118     {
119       g_free (info->display_name);
120       g_free (info->stable_name);
121       g_free (info->x_content_types);
122       g_object_unref (info->icon);
123       g_object_unref (info->symbolic_icon);
124       g_free (info->dbus_id);
125       g_free (info->object_path);
126       g_mount_spec_unref (info->mount_spec);
127       g_free (info->prefered_filename_encoding);
128       g_free (info->fuse_mountpoint);
129       g_free (info->default_location);
130       g_free (info);
131     }
132 }
133 
134 const char *
g_mount_info_resolve_path(GMountInfo * info,const char * path)135 g_mount_info_resolve_path (GMountInfo *info,
136 			   const char *path)
137 {
138   const char *new_path;
139   int len;
140 
141   if (info->mount_spec->mount_prefix != NULL &&
142       info->mount_spec->mount_prefix[0] != 0)
143     {
144       len = strlen (info->mount_spec->mount_prefix);
145       if (info->mount_spec->mount_prefix[len-1] == '/')
146 	len--;
147       new_path = path + len;
148     }
149   else
150     new_path = path;
151 
152   if (new_path == NULL ||
153       new_path[0] == 0)
154     new_path = "/";
155 
156   return new_path;
157 }
158 
159 void
g_mount_info_apply_prefix(GMountInfo * info,char ** path)160 g_mount_info_apply_prefix (GMountInfo  *info,
161 			   char       **path)
162 {
163   GMountSpec *spec;
164 
165   spec = info->mount_spec;
166 
167   if (spec->mount_prefix != NULL &&
168       spec->mount_prefix[0] != 0)
169     {
170       char *path_with_prefix;
171       path_with_prefix = g_build_path ("/", spec->mount_prefix,
172                                        *path, NULL);
173       g_free (*path);
174       *path = path_with_prefix;
175     }
176 
177 }
178 
179 GMountInfo *
g_mount_info_from_dbus(GVariant * value)180 g_mount_info_from_dbus (GVariant *value)
181 {
182   GMountInfo *info;
183   GMountSpec *mount_spec;
184   gboolean user_visible;
185   const gchar *display_name;
186   const gchar *stable_name;
187   const gchar *x_content_types;
188   const gchar *icon_str;
189   const gchar *symbolic_icon_str;
190   const gchar *prefered_filename_encoding;
191   const gchar *dbus_id;
192   const gchar *obj_path;
193   const gchar *fuse_mountpoint;
194   const gchar *default_location;
195   GIcon *icon;
196   GIcon *symbolic_icon;
197   GVariant *iter_mount_spec;
198   GError *error;
199 
200   g_variant_get (value, "(&s&o&s&s&s&s&s&sb^&ay@(aya{sv})^&ay)",
201                  &dbus_id,
202                  &obj_path,
203                  &display_name,
204                  &stable_name,
205                  &x_content_types,
206                  &icon_str,
207                  &symbolic_icon_str,
208                  &prefered_filename_encoding,
209                  &user_visible,
210                  &fuse_mountpoint,
211                  &iter_mount_spec,
212                  &default_location);
213 
214   mount_spec = g_mount_spec_from_dbus (iter_mount_spec);
215   g_variant_unref (iter_mount_spec);
216   if (mount_spec == NULL)
217     return NULL;
218 
219   if (fuse_mountpoint && fuse_mountpoint[0] == '\0')
220     fuse_mountpoint = NULL;
221   if (default_location && default_location[0] == '\0')
222     default_location = NULL;
223 
224   if (icon_str == NULL || strlen (icon_str) == 0)
225     icon_str = "drive-removable-media";
226   error = NULL;
227   icon = g_icon_new_for_string (icon_str, &error);
228   if (icon == NULL)
229     {
230       g_warning ("Malformed icon string '%s': %s", icon_str, error->message);
231       g_error_free (error);
232       icon = g_themed_icon_new ("gtk-missing-image"); /* TODO: maybe choose a better name */
233     }
234 
235   if (symbolic_icon_str == NULL || strlen (symbolic_icon_str) == 0)
236     symbolic_icon_str = "drive-removable-media-symbolic";
237   error = NULL;
238   symbolic_icon = g_icon_new_for_string (symbolic_icon_str, &error);
239   if (symbolic_icon == NULL)
240     {
241       g_warning ("Malformed icon string '%s': %s", symbolic_icon_str, error->message);
242       g_error_free (error);
243       symbolic_icon = g_themed_icon_new ("drive-removable-media-symbolic");
244     }
245 
246   info = g_new0 (GMountInfo, 1);
247   info->ref_count = 1;
248   info->display_name = g_strdup (display_name);
249   info->stable_name = g_strdup (stable_name);
250   info->x_content_types = g_strdup (x_content_types);
251   info->icon = icon;
252   info->symbolic_icon = symbolic_icon;
253   info->dbus_id = g_strdup (dbus_id);
254   info->object_path = g_strdup (obj_path);
255   info->mount_spec = mount_spec;
256   info->user_visible = user_visible;
257   info->prefered_filename_encoding = g_strdup (prefered_filename_encoding);
258   info->fuse_mountpoint = g_strdup (fuse_mountpoint);
259   info->default_location = g_strdup (default_location);
260 
261   return info;
262 }
263 
264 static void
g_mount_tracker_finalize(GObject * object)265 g_mount_tracker_finalize (GObject *object)
266 {
267   GMountTracker *tracker;
268 
269   tracker = G_MOUNT_TRACKER (object);
270 
271   g_mutex_clear (&tracker->lock);
272 
273   g_list_free_full (tracker->mounts, (GDestroyNotify)g_mount_info_unref);
274 
275   g_clear_object (&tracker->proxy);
276   g_clear_object (&tracker->connection);
277 
278   if (G_OBJECT_CLASS (g_mount_tracker_parent_class)->finalize)
279     (*G_OBJECT_CLASS (g_mount_tracker_parent_class)->finalize) (object);
280 }
281 
282 static void
g_mount_tracker_class_init(GMountTrackerClass * klass)283 g_mount_tracker_class_init (GMountTrackerClass *klass)
284 {
285   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
286 
287   gobject_class->finalize = g_mount_tracker_finalize;
288   gobject_class->constructor = g_mount_tracker_constructor;
289   gobject_class->set_property = g_mount_tracker_set_property;
290   gobject_class->get_property = g_mount_tracker_get_property;
291 
292   signals[MOUNTED] = g_signal_new (I_("mounted"),
293 				   G_TYPE_MOUNT_TRACKER,
294 				   G_SIGNAL_RUN_LAST,
295 				   G_STRUCT_OFFSET (GMountTrackerClass, mounted),
296 				   NULL, NULL,
297 				   g_cclosure_marshal_VOID__POINTER,
298 				   G_TYPE_NONE, 1, G_TYPE_POINTER);
299 
300   signals[UNMOUNTED] = g_signal_new (I_("unmounted"),
301 				     G_TYPE_MOUNT_TRACKER,
302 				     G_SIGNAL_RUN_LAST,
303 				     G_STRUCT_OFFSET (GMountTrackerClass, unmounted),
304 				     NULL, NULL,
305 				     g_cclosure_marshal_VOID__POINTER,
306 				     G_TYPE_NONE, 1, G_TYPE_POINTER);
307 
308   g_object_class_install_property (gobject_class,
309 				   PROP_CONNECTION,
310 				   g_param_spec_pointer ("connection",
311 							 P_("DBus connection"),
312 							 P_("The dbus connection to use for ipc."),
313 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
314 							G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
315 
316   g_object_class_install_property (gobject_class,
317                                    PROP_USER_VISIBLE_ONLY,
318                                    g_param_spec_boolean ("user-visible-only",
319                                                          P_("User visible only"),
320                                                          P_("User visible only"),
321                                                          FALSE,
322                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
323                                                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
324 }
325 
326 static void
g_mount_tracker_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)327 g_mount_tracker_set_property (GObject         *object,
328 			      guint            prop_id,
329 			      const GValue    *value,
330 			      GParamSpec      *pspec)
331 {
332   GMountTracker *tracker = G_MOUNT_TRACKER (object);
333 
334   switch (prop_id)
335     {
336     case PROP_CONNECTION:
337       g_clear_object (&tracker->connection);
338       if (g_value_get_pointer (value))
339 	tracker->connection = g_object_ref (g_value_get_pointer (value));
340       break;
341     case PROP_USER_VISIBLE_ONLY:
342       tracker->user_visible_only = g_value_get_boolean (value);
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347     }
348 }
349 
350 static void
g_mount_tracker_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)351 g_mount_tracker_get_property (GObject    *object,
352 			      guint       prop_id,
353 			      GValue     *value,
354 			      GParamSpec *pspec)
355 {
356   GMountTracker *tracker = G_MOUNT_TRACKER (object);
357 
358   switch (prop_id)
359     {
360     case PROP_CONNECTION:
361       g_value_set_pointer (value, tracker->connection);
362       break;
363     case PROP_USER_VISIBLE_ONLY:
364       g_value_set_boolean (value, tracker->user_visible_only);
365       break;
366     default:
367       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368       break;
369     }
370 }
371 
372 static GList *
g_mount_tracker_find(GMountTracker * tracker,GMountInfo * info)373 g_mount_tracker_find (GMountTracker *tracker,
374 		      GMountInfo *info)
375 {
376   GList *l;
377 
378   for (l = tracker->mounts; l != NULL; l = l->next)
379     {
380       if (g_mount_info_equal (info, (GMountInfo *)l->data))
381 	return l;
382     }
383 
384   return NULL;
385 }
386 
387 static void
g_mount_tracker_add_mount(GMountTracker * tracker,GMountInfo * info)388 g_mount_tracker_add_mount (GMountTracker *tracker,
389 			   GMountInfo *info)
390 {
391   g_mutex_lock (&tracker->lock);
392 
393   /* Don't add multiple times */
394   if (g_mount_tracker_find (tracker, info))
395     {
396       g_mutex_unlock (&tracker->lock);
397       return;
398     }
399 
400   if (tracker->user_visible_only && !info->user_visible)
401     {
402       g_mutex_unlock (&tracker->lock);
403       return;
404     }
405 
406   tracker->mounts = g_list_prepend (tracker->mounts, g_mount_info_ref (info));
407 
408   g_mutex_unlock (&tracker->lock);
409 
410   g_signal_emit (tracker, signals[MOUNTED], 0, info);
411 }
412 
413 static void
g_mount_tracker_remove_mount(GMountTracker * tracker,GMountInfo * info)414 g_mount_tracker_remove_mount (GMountTracker *tracker,
415 			      GMountInfo *info)
416 {
417   GList *l;
418   GMountInfo *old_info;
419 
420   g_mutex_lock (&tracker->lock);
421 
422   l = g_mount_tracker_find (tracker, info);
423 
424   /* Don't remove multiple times */
425   if (l == NULL)
426     {
427       g_mutex_unlock (&tracker->lock);
428       return;
429     }
430 
431   old_info = l->data;
432 
433   tracker->mounts = g_list_delete_link (tracker->mounts, l);
434 
435   g_mutex_unlock (&tracker->lock);
436 
437   g_signal_emit (tracker, signals[UNMOUNTED], 0, old_info);
438   g_mount_info_unref (old_info);
439 }
440 
441 static void
list_mounts_reply(GMountTracker * tracker,GVariant * mounts)442 list_mounts_reply (GMountTracker *tracker,
443                    GVariant *mounts)
444 {
445   GMountInfo *info;
446   GVariantIter iter;
447   GVariant *child;
448 
449   g_variant_iter_init (&iter, mounts);
450   while ((child = g_variant_iter_next_value (&iter)))
451     {
452       info = g_mount_info_from_dbus (child);
453       if (info)
454         {
455           g_mount_tracker_add_mount (tracker, info);
456           g_mount_info_unref (info);
457         }
458       g_variant_unref (child);
459     }
460 }
461 
462 static void
mounted_cb(GVfsDBusMountTracker * object,GVariant * arg_mount,gpointer user_data)463 mounted_cb (GVfsDBusMountTracker *object,
464             GVariant *arg_mount,
465             gpointer user_data)
466 {
467   GMountTracker *tracker = G_MOUNT_TRACKER (user_data);
468   GMountInfo *info;
469 
470   info = g_mount_info_from_dbus (arg_mount);
471   if (info)
472     {
473       g_mount_tracker_add_mount (tracker, info);
474       g_mount_info_unref (info);
475     }
476 }
477 
478 static void
unmounted_cb(GVfsDBusMountTracker * object,GVariant * arg_mount,gpointer user_data)479 unmounted_cb (GVfsDBusMountTracker *object,
480               GVariant *arg_mount,
481               gpointer user_data)
482 {
483   GMountTracker *tracker = G_MOUNT_TRACKER (user_data);
484   GMountInfo *info;
485 
486   info = g_mount_info_from_dbus (arg_mount);
487   if (info)
488     {
489       g_mount_tracker_remove_mount (tracker, info);
490       g_mount_info_unref (info);
491     }
492 
493 }
494 
495 /* Called after construction when the construct properties (like connection) are set */
496 static void
init_connection_sync(GMountTracker * tracker)497 init_connection_sync (GMountTracker *tracker)
498 {
499   GError *error;
500   GVariant *iter_mounts;
501   gboolean res;
502 
503   if (tracker->connection == NULL)
504     tracker->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
505 
506   error = NULL;
507   tracker->proxy = gvfs_dbus_mount_tracker_proxy_new_sync (tracker->connection,
508                                                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
509                                                            G_VFS_DBUS_DAEMON_NAME,
510                                                            G_VFS_DBUS_MOUNTTRACKER_PATH,
511                                                            NULL,
512                                                            &error);
513   if (tracker->proxy == NULL)
514     {
515       g_printerr ("Error creating proxy: %s (%s, %d)\n",
516                   error->message, g_quark_to_string (error->domain), error->code);
517       g_error_free (error);
518       return;
519     }
520 
521   g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (tracker->proxy),
522                                     G_VFS_DBUS_TIMEOUT_MSECS);
523 
524   res = gvfs_dbus_mount_tracker_call_list_mounts2_sync (tracker->proxy, tracker->user_visible_only, &iter_mounts, NULL, &error);
525   if (!res)
526     {
527       if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
528         res = gvfs_dbus_mount_tracker_call_list_mounts_sync (tracker->proxy, &iter_mounts, NULL, NULL);
529       g_clear_error (&error);
530     }
531 
532   if (res)
533     {
534       list_mounts_reply (tracker, iter_mounts);
535       g_variant_unref (iter_mounts);
536     }
537 
538   g_signal_connect (tracker->proxy, "mounted", G_CALLBACK (mounted_cb), tracker);
539   g_signal_connect (tracker->proxy, "unmounted", G_CALLBACK (unmounted_cb), tracker);
540 }
541 
542 static void
g_mount_tracker_init(GMountTracker * tracker)543 g_mount_tracker_init (GMountTracker *tracker)
544 {
545   g_mutex_init (&tracker->lock);
546 }
547 
548 
549 static GObject*
g_mount_tracker_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)550 g_mount_tracker_constructor (GType                  type,
551 			     guint                  n_construct_properties,
552 			     GObjectConstructParam *construct_params)
553 {
554   GObject *object;
555   GMountTracker *tracker;
556 
557   object = (* G_OBJECT_CLASS (g_mount_tracker_parent_class)->constructor) (type,
558 									   n_construct_properties,
559 									   construct_params);
560 
561   tracker = G_MOUNT_TRACKER (object);
562 
563   init_connection_sync (tracker);
564 
565   return object;
566 }
567 
568 GMountTracker *
g_mount_tracker_new(GDBusConnection * connection,gboolean user_visible_only)569 g_mount_tracker_new (GDBusConnection *connection,
570                      gboolean         user_visible_only)
571 {
572   GMountTracker *tracker;
573 
574   tracker = g_object_new (G_TYPE_MOUNT_TRACKER, "connection", connection, "user_visible_only", user_visible_only, NULL);
575 
576   return tracker;
577 }
578 
579 GList *
g_mount_tracker_list_mounts(GMountTracker * tracker)580 g_mount_tracker_list_mounts (GMountTracker *tracker)
581 {
582   GList *res, *l;
583   GMountInfo *copy;
584 
585   g_mutex_lock (&tracker->lock);
586 
587   res = NULL;
588   for (l = tracker->mounts; l != NULL; l = l->next)
589     {
590       copy = g_mount_info_ref (l->data);
591       res = g_list_prepend (res, copy);
592     }
593 
594   g_mutex_unlock (&tracker->lock);
595 
596   return g_list_reverse (res);
597 }
598 
599 GMountInfo *
g_mount_tracker_find_by_mount_spec(GMountTracker * tracker,GMountSpec * mount_spec)600 g_mount_tracker_find_by_mount_spec (GMountTracker *tracker,
601 				    GMountSpec    *mount_spec)
602 {
603   GList *l;
604   GMountInfo *info, *found;
605 
606   g_mutex_lock (&tracker->lock);
607 
608   found = NULL;
609   for (l = tracker->mounts; l != NULL; l = l->next)
610     {
611       info = l->data;
612 
613       if (g_mount_spec_equal (info->mount_spec, mount_spec))
614 	{
615 	  found = g_mount_info_ref (info);
616 	  break;
617 	}
618     }
619 
620   g_mutex_unlock (&tracker->lock);
621 
622   return found;
623 }
624 
625 
626 gboolean
g_mount_tracker_has_mount_spec(GMountTracker * tracker,GMountSpec * mount_spec)627 g_mount_tracker_has_mount_spec (GMountTracker *tracker,
628 				GMountSpec    *mount_spec)
629 {
630   GList *l;
631   GMountInfo *info;
632   gboolean found;
633 
634   g_mutex_lock (&tracker->lock);
635 
636   found = FALSE;
637   for (l = tracker->mounts; l != NULL; l = l->next)
638     {
639       info = l->data;
640 
641       if (g_mount_spec_equal (info->mount_spec, mount_spec))
642 	{
643 	  found = TRUE;
644 	  break;
645 	}
646     }
647 
648   g_mutex_unlock (&tracker->lock);
649 
650   return found;
651 }
652 
653