1 /*
2  *      backendgvfs.c
3  *
4  *      Copyright 2008-2011 Enrico Tröger <enrico(at)xfce(dot)org>
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; version 2 of the License.
9  *
10  *      This program 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
13  *      GNU General Public License for more details.
14  *
15  *      You should have received a copy of the GNU General Public License
16  *      along with this program; if not, write to the Free Software
17  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 
21 #include <glib-object.h>
22 #include <glib/gi18n.h>
23 #include <gio/gio.h>
24 #include <gtk/gtk.h>
25 
26 #include "common.h"
27 #include "backendgvfs.h"
28 #include "bookmark.h"
29 #include "settings.h"
30 #include "window.h"
31 
32 typedef struct _GigoloBackendGVFSPrivate			GigoloBackendGVFSPrivate;
33 
34 enum
35 {
36     PROP_0,
37     PROP_PARENT,
38     PROP_STORE
39 };
40 
41 enum
42 {
43 	MOUNTS_CHANGED,
44 	OPERATION_FAILED,
45 	BROWSE_NETWORK_FINISHED,
46 	BROWSE_HOST_FINISHED,
47 
48 	LAST_SIGNAL
49 };
50 static guint signals[LAST_SIGNAL];
51 
52 enum
53 {
54     BROWSE_MODE_INVALID,
55     BROWSE_MODE_DOMAINS,
56     BROWSE_MODE_HOSTS,
57     BROWSE_MODE_SHARES,
58 };
59 
60 typedef struct
61 {
62 	GigoloBackendGVFS *self;
63 	GtkWidget *dialog;
64 	gboolean show_errors;
65 } MountInfo;
66 
67 typedef struct BrowseData
68 {
69 	GigoloBackendGVFS *self;
70 
71 	gint mode;
72 	gchar *uri;
73 	GtkWindow *parent;
74 	GtkTreeStore *store;
75 	GtkTreePath *parent_path;
76 
77 	void (*browse_func) (struct BrowseData *bd);
78 } BrowseData;
79 
80 
81 struct _GigoloBackendGVFSPrivate
82 {
83 	GtkWindow *parent;
84 	GtkListStore *store;
85 
86 	gint browse_counter;
87 };
88 
89 static void gigolo_backend_gvfs_finalize  			(GObject *object);
90 static void gigolo_backend_gvfs_set_property		(GObject *object, guint prop_id,
91 													 const GValue *value, GParamSpec *pspec);
92 static void browse_network_real						(BrowseData *bd);
93 static void browse_host_real						(BrowseData *bd);
94 
95 
96 G_DEFINE_TYPE_WITH_PRIVATE(GigoloBackendGVFS, gigolo_backend_gvfs, G_TYPE_OBJECT);
97 
98 
99 
gigolo_backend_gvfs_cclosure_marshal_VOID__STRING_STRING(GClosure * closure,G_GNUC_UNUSED GValue * return_value,guint n_param_values,const GValue * param_values,G_GNUC_UNUSED gpointer invocation_hint,gpointer marshal_data)100 static void gigolo_backend_gvfs_cclosure_marshal_VOID__STRING_STRING(
101 											GClosure		*closure,
102 							  G_GNUC_UNUSED GValue			*return_value,
103 											guint			 n_param_values,
104 											const GValue	*param_values,
105 							  G_GNUC_UNUSED gpointer		 invocation_hint,
106 											gpointer		 marshal_data)
107 {
108 	typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer		 data1,
109 													  const gchar	*arg_1,
110 													  const gchar	*arg_2,
111 													  gpointer		 data2);
112 	register GMarshalFunc_VOID__STRING_STRING callback;
113 	register GCClosure* cc = (GCClosure*) closure;
114 	register gpointer data1, data2;
115 
116 	g_return_if_fail(n_param_values == 3);
117 
118 	if (G_CCLOSURE_SWAP_DATA(closure))
119 	{
120 		data1 = closure->data;
121 		data2 = g_value_peek_pointer(param_values + 0);
122 	}
123 	else
124 	{
125 		data1 = g_value_peek_pointer(param_values + 0);
126 		data2 = closure->data;
127 	}
128 	callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
129 	callback(
130 		data1,
131 		g_value_get_string(param_values + 1),
132 		g_value_get_string(param_values + 2),
133 		data2);
134 }
135 
136 
gigolo_backend_gvfs_class_init(GigoloBackendGVFSClass * klass)137 static void gigolo_backend_gvfs_class_init(GigoloBackendGVFSClass *klass)
138 {
139 	GObjectClass *g_object_class;
140 
141 	g_object_class = G_OBJECT_CLASS(klass);
142 
143 	g_object_class->finalize = gigolo_backend_gvfs_finalize;
144 	g_object_class->set_property = gigolo_backend_gvfs_set_property;
145 
146 	g_object_class_install_property(g_object_class,
147 										PROP_PARENT,
148 										g_param_spec_object(
149 										"parent",
150 										"Parent",
151 										"Parent window",
152 										GTK_TYPE_WINDOW,
153 										G_PARAM_WRITABLE));
154 
155 	g_object_class_install_property(g_object_class,
156 										PROP_STORE,
157 										g_param_spec_object(
158 										"store",
159 										"Liststore",
160 										"The list store",
161 										GTK_TYPE_LIST_STORE,
162 										G_PARAM_WRITABLE));
163 
164 	signals[MOUNTS_CHANGED] = g_signal_new("mounts-changed",
165 										G_TYPE_FROM_CLASS(klass),
166 										(GSignalFlags) 0,
167 										0,
168 										0,
169 										NULL,
170 										g_cclosure_marshal_VOID__VOID,
171 										G_TYPE_NONE, 0);
172 	signals[OPERATION_FAILED] = g_signal_new("operation-failed",
173 										G_TYPE_FROM_CLASS(klass),
174 										(GSignalFlags) 0,
175 										0,
176 										0,
177 										NULL,
178 										gigolo_backend_gvfs_cclosure_marshal_VOID__STRING_STRING,
179 										G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
180 	signals[BROWSE_NETWORK_FINISHED] = g_signal_new("browse-network-finished",
181 										G_TYPE_FROM_CLASS(klass),
182 										(GSignalFlags) 0,
183 										0,
184 										0,
185 										NULL,
186 										g_cclosure_marshal_VOID__VOID,
187 										G_TYPE_NONE, 0);
188 	signals[BROWSE_HOST_FINISHED] = g_signal_new("browse-host-finished",
189 										G_TYPE_FROM_CLASS(klass),
190 										(GSignalFlags) 0,
191 										0,
192 										0,
193 										NULL,
194 										/* FIXME use a proper closure, arg1 is a GList* */
195 										g_cclosure_marshal_VOID__POINTER,
196 										G_TYPE_NONE, 1, G_TYPE_POINTER);
197 }
198 
199 
gigolo_backend_gvfs_finalize(GObject * object)200 static void gigolo_backend_gvfs_finalize(GObject *object)
201 {
202 	G_OBJECT_CLASS(gigolo_backend_gvfs_parent_class)->finalize(object);
203 }
204 
205 
get_tooltip_text(GigoloBackendGVFS * backend,gpointer ref,gint ref_type,const gchar * type)206 static gchar *get_tooltip_text(GigoloBackendGVFS *backend, gpointer ref, gint ref_type, const gchar *type)
207 {
208 	gchar *result = NULL;
209 	GigoloBackendGVFSPrivate *priv = gigolo_backend_gvfs_get_instance_private(backend);
210 	switch (ref_type)
211 	{
212 		case GIGOLO_WINDOW_REF_TYPE_MOUNT:
213 		{
214 			gchar *uri, *name, *clean_uri;
215 			const gchar *bookmark_name = _("No bookmark");
216 			GigoloBookmark *b;
217 			GigoloSettings *settings;
218 
219 			gigolo_backend_gvfs_get_name_and_uri_from_mount(ref, &name, &uri);
220 			clean_uri = g_uri_unescape_string(uri, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO);
221 
222 			settings = gigolo_window_get_settings(GIGOLO_WINDOW(priv->parent));
223 			b = gigolo_settings_get_bookmark_by_uri(settings, uri);
224 			if (b != NULL)
225 			{
226 				const gchar *folder = gigolo_bookmark_get_folder(b);
227 				if (NZV(folder))
228 					setptr(clean_uri, g_build_filename(clean_uri, folder, NULL));
229 				bookmark_name = gigolo_bookmark_get_name(b);
230 			}
231 
232 			result = g_strdup_printf(
233 				_("<b>%s</b>\n\nURI: %s\nConnected: Yes\nService Type: %s\nBookmark: %s"),
234 					name, clean_uri, type, bookmark_name);
235 
236 			g_free(clean_uri);
237 			g_free(uri);
238 			g_free(name);
239 			return result;
240 		}
241 		case GIGOLO_WINDOW_REF_TYPE_VOLUME:
242 		default:
243 		{
244 			gchar *label = gigolo_backend_gvfs_get_volume_identifier(ref);
245 
246 			if (NZV(label))
247 			{
248 				result = g_strdup_printf(_("<b>Unix device: %s</b>"), label);
249 			}
250 			g_free(label);
251 			return result;
252 		}
253 	}
254 }
255 
256 
mount_volume_changed_cb(GVolumeMonitor * vm,G_GNUC_UNUSED GMount * mnt,gpointer backend)257 static void mount_volume_changed_cb(GVolumeMonitor *vm, G_GNUC_UNUSED GMount *mnt, gpointer backend)
258 {
259 	GList *mounts, *volumes, *item;
260 	GFile *file;
261 	GMount *mount;
262 	GVolume *volume;
263 	GIcon *icon;
264 	GtkTreeIter iter;
265 	gchar *vol_name, *mount_name, *display_name, *scheme, *uri, *tooltip_text;
266 	const gchar *scheme_name;
267 	GigoloBackendGVFSPrivate *priv = gigolo_backend_gvfs_get_instance_private(backend);
268 	GigoloBookmark *bookmark;
269 	GigoloSettings *settings = gigolo_window_get_settings(GIGOLO_WINDOW(priv->parent));
270 
271 	gtk_list_store_clear(priv->store);
272 
273 	/* list mounts */
274 	mounts = g_volume_monitor_get_mounts(vm);
275 	for (item = mounts; item != NULL; item = g_list_next(item))
276 	{
277 		display_name = NULL;
278 		mount = G_MOUNT(item->data);
279 		mount_name = g_mount_get_name(mount);
280 		file = g_mount_get_root(mount);
281 		scheme = g_file_get_uri_scheme(file);
282 		if (gigolo_str_equal(scheme, "burn"))
283 		{	/* ignore empty CDs which are listed as mounted to burn:// */
284 			g_free(mount_name);
285 			g_free(scheme);
286 			g_object_unref(file);
287 			continue;
288 		}
289 		scheme_name = gigolo_describe_scheme(scheme);
290 		uri = g_file_get_uri(file);
291 		icon = g_mount_get_icon(mount);
292 		tooltip_text = get_tooltip_text(backend, mount, GIGOLO_WINDOW_REF_TYPE_MOUNT, scheme_name);
293 
294 		bookmark = gigolo_settings_get_bookmark_by_uri(settings, uri);
295 		if (bookmark != NULL)
296 		{
297 			display_name = g_strdup_printf("%s (%s)",
298 				gigolo_bookmark_get_name(bookmark), mount_name);
299 		}
300 
301 		gtk_list_store_insert_with_values(priv->store, &iter, -1,
302 				GIGOLO_WINDOW_COL_IS_MOUNTED, TRUE,
303 				GIGOLO_WINDOW_COL_NAME, display_name ? display_name : mount_name,
304 				GIGOLO_WINDOW_COL_SCHEME, scheme_name,
305 				GIGOLO_WINDOW_COL_REF, mount,
306 				GIGOLO_WINDOW_COL_REF_TYPE, GIGOLO_WINDOW_REF_TYPE_MOUNT,
307 				GIGOLO_WINDOW_COL_PIXBUF, icon,
308 				GIGOLO_WINDOW_COL_ICON_NAME, "folder-remote",
309 				GIGOLO_WINDOW_COL_TOOLTIP, tooltip_text,
310 				-1);
311 		g_free(mount_name);
312 		g_free(display_name);
313 		g_free(scheme);
314 		g_free(uri);
315 		g_free(tooltip_text);
316 		g_object_unref(file);
317 		g_object_unref(icon);
318 	}
319 	g_list_free_full(mounts, g_object_unref);
320 
321 	/* list volumes */
322 	volumes = g_volume_monitor_get_volumes(vm);
323 	for (item = volumes; item != NULL; item = g_list_next(item))
324 	{
325 		volume = G_VOLUME(item->data);
326 		mount = g_volume_get_mount(volume);
327 		/* display this volume only if it is not mounted, otherwise it will be listed as mounted */
328 		if (mount == NULL)
329 		{
330 			icon = g_volume_get_icon(volume);
331 			vol_name = g_volume_get_name(volume);
332 			tooltip_text = get_tooltip_text(backend, volume, GIGOLO_WINDOW_REF_TYPE_VOLUME, NULL);
333 
334 			gtk_list_store_insert_with_values(priv->store, &iter, -1,
335 					GIGOLO_WINDOW_COL_IS_MOUNTED, FALSE,
336 					GIGOLO_WINDOW_COL_NAME, vol_name,
337 					GIGOLO_WINDOW_COL_SCHEME, gigolo_describe_scheme("file"),
338 					GIGOLO_WINDOW_COL_REF, volume,
339 					GIGOLO_WINDOW_COL_REF_TYPE, GIGOLO_WINDOW_REF_TYPE_VOLUME,
340 					GIGOLO_WINDOW_COL_PIXBUF, icon,
341 					GIGOLO_WINDOW_COL_ICON_NAME, "folder-remote",
342 					GIGOLO_WINDOW_COL_TOOLTIP, tooltip_text,
343 					-1);
344 			g_free(vol_name);
345 			g_free(tooltip_text);
346 			g_object_unref(icon);
347 		}
348 		else
349 			g_object_unref(mount);
350 	}
351 	g_list_free_full(volumes, g_object_unref);
352 
353 	g_signal_emit(backend, signals[MOUNTS_CHANGED], 0);
354 }
355 
356 
gigolo_backend_gvfs_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)357 static void gigolo_backend_gvfs_set_property(GObject *object, guint prop_id,
358 											 const GValue *value, GParamSpec *pspec)
359 {
360 	GigoloBackendGVFSPrivate *priv = gigolo_backend_gvfs_get_instance_private(GIGOLO_BACKEND_GVFS(object));
361 
362 	switch (prop_id)
363 	{
364 	case PROP_PARENT:
365 	{
366 		priv->parent = g_value_get_object(value);
367 		break;
368 	}
369 	case PROP_STORE:
370 	{
371 		GVolumeMonitor *gvm;
372 
373 		priv->store = g_value_get_object(value);
374 
375 		gvm = g_volume_monitor_get();
376 		g_signal_connect(gvm, "mount-added", G_CALLBACK(mount_volume_changed_cb), object);
377 		g_signal_connect(gvm, "mount-changed", G_CALLBACK(mount_volume_changed_cb), object);
378 		g_signal_connect(gvm, "mount-removed", G_CALLBACK(mount_volume_changed_cb), object);
379 		g_signal_connect(gvm, "volume-added", G_CALLBACK(mount_volume_changed_cb), object);
380 		g_signal_connect(gvm, "volume-changed", G_CALLBACK(mount_volume_changed_cb), object);
381 		g_signal_connect(gvm, "volume-removed", G_CALLBACK(mount_volume_changed_cb), object);
382 
383 		/* fill the list store once */
384 		mount_volume_changed_cb(gvm, NULL, object);
385 		break;
386 	}
387 	default:
388 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
389 		break;
390 	}
391 }
392 
393 
gigolo_backend_gvfs_init(G_GNUC_UNUSED GigoloBackendGVFS * self)394 static void gigolo_backend_gvfs_init(G_GNUC_UNUSED GigoloBackendGVFS *self)
395 {
396 }
397 
398 
gigolo_backend_gvfs_new(void)399 GigoloBackendGVFS *gigolo_backend_gvfs_new(void)
400 {
401 	return g_object_new(GIGOLO_BACKEND_GVFS_TYPE, NULL);
402 }
403 
404 
gigolo_backend_gvfs_is_mount(gpointer mount)405 gboolean gigolo_backend_gvfs_is_mount(gpointer mount)
406 {
407 	g_return_val_if_fail(mount != NULL, FALSE);
408 
409 	return G_IS_MOUNT(mount);
410 }
411 
412 
gigolo_backend_gvfs_get_name_and_uri_from_mount(gpointer mount,gchar ** name,gchar ** uri)413 void gigolo_backend_gvfs_get_name_and_uri_from_mount(gpointer mount, gchar **name, gchar **uri)
414 {
415 	GFile *file;
416 
417 	g_return_if_fail(mount != NULL);
418 
419 	file = g_mount_get_root(G_MOUNT(mount));
420 	if (name != NULL)
421 		*name = g_mount_get_name(G_MOUNT(mount));
422 	if (uri != NULL)
423 		*uri = g_file_get_uri(file);
424 
425 	g_object_unref(file);
426 }
427 
428 
volume_mount_finished_cb(GObject * src,GAsyncResult * res,gpointer backend)429 static void volume_mount_finished_cb(GObject *src, GAsyncResult *res, gpointer backend)
430 {
431 	GError *error = NULL;
432 
433 	if (! g_volume_mount_finish(G_VOLUME(src), res, &error))
434 	{
435 		if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
436 		{
437 			gchar *name, *msg;
438 
439 			if (G_IS_VOLUME(src))
440 				name = g_volume_get_name(G_VOLUME(src));
441 			else
442 			{
443 				gigolo_backend_gvfs_get_name_and_uri_from_mount(G_MOUNT(src), &name, NULL);
444 				if (name == NULL)
445 					name = g_strdup(_("unknown"));
446 			}
447 
448 			g_warning("Mounting of \"%s\" failed: %s", name, error->message);
449 			if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
450 			{
451 				msg = g_strdup_printf(_("Connecting to \"%s\" failed."), name);
452 				g_signal_emit(backend, signals[OPERATION_FAILED], 0, msg, error->message);
453 				g_free(msg);
454 			}
455 			g_error_free(error);
456 			g_free(name);
457 		}
458 	}
459 	else
460 		verbose("Mount finished successfully");
461 }
462 
463 
unmount_finished_cb(GObject * src,GAsyncResult * res,gpointer backend)464 static void unmount_finished_cb(GObject *src, GAsyncResult *res, gpointer backend)
465 {
466 	GError *error = NULL;
467 
468 #if GLIB_CHECK_VERSION(2, 22, 0)
469 	if (! g_mount_unmount_with_operation_finish(G_MOUNT(src), res, &error))
470 #else
471 	if (! g_mount_unmount_finish(G_MOUNT(src), res, &error))
472 #endif
473 	{
474 		if (! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
475 		{
476 			gchar *name, *msg;
477 
478 			if (G_IS_VOLUME(src))
479 				name = g_volume_get_name(G_VOLUME(src));
480 			else
481 			{
482 				gigolo_backend_gvfs_get_name_and_uri_from_mount(G_MOUNT(src), &name, NULL);
483 				if (name == NULL)
484 					name = g_strdup(_("unknown"));
485 			}
486 
487 			g_warning("Unmounting of \"%s\" failed: %s", name, error->message);
488 			msg = g_strdup_printf(_("Disconnecting from \"%s\" failed."), name);
489 
490 			g_signal_emit(backend, signals[OPERATION_FAILED], 0, msg, error->message);
491 
492 			g_error_free(error);
493 			g_free(name);
494 			g_free(msg);
495 		}
496 	}
497 }
498 
499 
gigolo_backend_gvfs_mount_volume(GigoloBackendGVFS * backend,GtkWindow * window,gpointer vol)500 gboolean gigolo_backend_gvfs_mount_volume(GigoloBackendGVFS *backend, GtkWindow *window, gpointer vol)
501 {
502 	g_return_val_if_fail(backend != NULL, FALSE);
503 	g_return_val_if_fail(window != NULL, FALSE);
504 	g_return_val_if_fail(vol != NULL, FALSE);
505 
506 	if (! G_IS_MOUNT(vol) && G_IS_VOLUME(vol) && g_volume_can_mount(G_VOLUME(vol)))
507 	{
508 		GMountOperation *op = gtk_mount_operation_new(window);
509 
510 		g_volume_mount(G_VOLUME(vol), G_MOUNT_MOUNT_NONE, op, NULL, volume_mount_finished_cb, backend);
511 
512 		g_object_unref(op);
513 		return TRUE;
514 	}
515 
516 	return FALSE;
517 }
518 
519 
gigolo_backend_gvfs_unmount_mount(GigoloBackendGVFS * backend,gpointer mount,GtkWindow * parent)520 void gigolo_backend_gvfs_unmount_mount(GigoloBackendGVFS *backend, gpointer mount, GtkWindow *parent)
521 {
522 #if GLIB_CHECK_VERSION(2, 22, 0)
523 	GMountOperation *op;
524 #endif
525 
526 	g_return_if_fail(backend != NULL);
527 	g_return_if_fail(mount != NULL);
528 
529 #if GLIB_CHECK_VERSION(2, 22, 0)
530 	op = gtk_mount_operation_new(parent);
531 	g_mount_unmount_with_operation(
532 		G_MOUNT(mount), G_MOUNT_UNMOUNT_NONE, op, NULL, unmount_finished_cb, backend);
533 	g_object_unref(op);
534 #else
535 	g_mount_unmount(G_MOUNT(mount), G_MOUNT_UNMOUNT_NONE, NULL, unmount_finished_cb, backend);
536 #endif
537 }
538 
539 
gigolo_backend_gvfs_update_mounts_and_volumes(GigoloBackendGVFS * backend)540 void gigolo_backend_gvfs_update_mounts_and_volumes(GigoloBackendGVFS *backend)
541 {
542 	GVolumeMonitor *gvm;
543 
544 	gvm = g_volume_monitor_get();
545 	mount_volume_changed_cb(gvm, NULL, backend);
546 }
547 
548 
mount_ready_cb(GFile * location,GAsyncResult * res,MountInfo * mi)549 static void mount_ready_cb(GFile *location, GAsyncResult *res, MountInfo *mi)
550 {
551 	gchar *uri;
552 	GError *error = NULL;
553 
554 	uri = g_file_get_uri(location);
555 	g_file_mount_enclosing_volume_finish(location, res, &error);
556 
557 	if (error != NULL && ! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
558 	{
559 		gchar *msg = g_strdup_printf(_("Connecting to \"%s\" failed."), uri);
560 		if (mi->show_errors && ! g_error_matches(error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
561 			g_signal_emit(mi->self, signals[OPERATION_FAILED], 0, msg, error->message);
562 		else
563 			verbose("%s (%s)", msg, error->message);
564 		g_free(msg);
565 	}
566 
567 	if (error != NULL)
568 		g_error_free(error);
569 
570 	if (mi->dialog != NULL && GTK_IS_WIDGET(mi->dialog))
571 		gtk_widget_destroy(mi->dialog);
572 
573 	g_free(uri);
574 	g_free(mi);
575 }
576 
577 
gigolo_backend_gvfs_mount_uri(GigoloBackendGVFS * backend,const gchar * uri,GtkWindow * parent,GtkWidget * dialog,gboolean show_errors)578 void gigolo_backend_gvfs_mount_uri(GigoloBackendGVFS *backend, const gchar *uri,
579 								   GtkWindow *parent, GtkWidget *dialog, gboolean show_errors)
580 {
581 	GMountOperation *op;
582 	GFile *file;
583 	MountInfo *mi;
584 
585 	g_return_if_fail(uri != NULL);
586 	g_return_if_fail(backend != NULL);
587 
588 	op = gtk_mount_operation_new(GTK_WINDOW(parent));
589 	file = g_file_new_for_uri(uri);
590 	mi = g_new0(MountInfo, 1);
591 	mi->self = backend;
592 	mi->dialog = dialog;
593 	mi->show_errors = show_errors;
594 
595 	g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, op, NULL,
596 		(GAsyncReadyCallback) mount_ready_cb, mi);
597 
598 	g_object_unref(file);
599 	g_object_unref(op);
600 }
601 
602 
gigolo_backend_gvfs_get_volume_identifier(gpointer volume)603 gchar *gigolo_backend_gvfs_get_volume_identifier(gpointer volume)
604 {
605 	g_return_val_if_fail(volume != NULL, NULL);
606 
607 	return g_volume_get_identifier(G_VOLUME(volume), G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
608 }
609 
610 
gigolo_backend_gvfs_get_mount_path(gpointer mount)611 gchar *gigolo_backend_gvfs_get_mount_path(gpointer mount)
612 {
613 	GFile *file;
614 	gchar *path = NULL;
615 
616 	g_return_val_if_fail(mount != NULL, NULL);
617 
618 	file = g_mount_get_default_location(G_MOUNT(mount));
619 	if (file != NULL)
620 	{
621 		path = g_file_get_path(file);
622 		g_object_unref(file);
623 	}
624 	return path;
625 }
626 
627 
browse_network_ready_cb(gpointer backend)628 static gboolean browse_network_ready_cb(gpointer backend)
629 {
630 	GigoloBackendGVFSPrivate *priv;
631 	priv = gigolo_backend_gvfs_get_instance_private(GIGOLO_BACKEND_GVFS(backend));
632 
633 	g_return_val_if_fail(backend != NULL, FALSE);
634 
635 	if (priv->browse_counter <= 0)
636 	{
637 		g_signal_emit(backend, signals[BROWSE_NETWORK_FINISHED], 0);
638 		verbose("Browse Network finished");
639 		return FALSE;
640 	}
641 	return TRUE;
642 }
643 
644 
browse_network_mount_ready_cb(GFile * location,GAsyncResult * res,BrowseData * bd)645 static void browse_network_mount_ready_cb(GFile *location, GAsyncResult *res, BrowseData *bd)
646 {
647 	GError *error = NULL;
648 	GigoloBackendGVFSPrivate *priv;
649 
650 	g_return_if_fail(bd != NULL);
651 	g_return_if_fail(bd->self != NULL);
652 
653 	g_file_mount_enclosing_volume_finish(location, res, &error);
654 
655 	priv = gigolo_backend_gvfs_get_instance_private(bd->self);
656 	priv->browse_counter--;
657 
658 	if (error != NULL)
659 	{
660 		verbose("%s (%s)", G_STRFUNC, error->message);
661 		g_error_free(error);
662 
663 		/* If we are looking only for shares, we need to emit the finished signal otherwise the
664 		 * caller will never know we are done. */
665 		if (bd->browse_func == browse_host_real)
666 			g_signal_emit(bd->self, signals[BROWSE_HOST_FINISHED], 0, NULL);
667 	}
668 	else
669 	{
670 		bd->browse_func(bd);
671 	}
672 }
673 
674 
browse_network_real(BrowseData * bd)675 static void browse_network_real(BrowseData *bd)
676 {
677 	GigoloBackendGVFS *backend;
678 	GigoloBackendGVFSPrivate *priv;
679 	GFile *file;
680 	GFileInfo *info;
681 	GError *error = NULL;
682 	GFileEnumerator *e;
683 	GtkTreeStore *store;
684 	GtkWindow *parent;
685 	gint mode;
686 
687 	g_return_if_fail(bd != NULL);
688 	g_return_if_fail(bd->self != NULL);
689 
690 	store = bd->store;
691 	mode = bd->mode;
692 	parent = bd->parent;
693 	backend = bd->self;
694 	priv = gigolo_backend_gvfs_get_instance_private(backend);
695 	priv->browse_counter++;
696 
697 	file = g_file_new_for_uri(bd->uri);
698 
699 	e = g_file_enumerate_children(file,
700 		G_FILE_ATTRIBUTE_STANDARD_TARGET_URI ","
701 		G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
702 		G_FILE_ATTRIBUTE_STANDARD_ICON,
703 		G_FILE_QUERY_INFO_NONE, NULL, &error);
704 
705 	if (error != NULL)
706 	{
707 		if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
708 		{
709 			GMountOperation *op = gtk_mount_operation_new(bd->parent);
710 			/* if the URI wasn't mounted yet, mount it and try again from the mount ready callback */
711 			g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, op, NULL,
712 				(GAsyncReadyCallback) browse_network_mount_ready_cb, bd);
713 
714 			g_error_free(error);
715 			g_object_unref(file);
716 			g_object_unref(op);
717 			return;
718 		}
719 		else
720 		{
721 			verbose("%s: %s", G_STRFUNC, error->message);
722 			g_error_free(error);
723 		}
724 	}
725 	else
726 	{
727 		const gchar *uri;
728 		GtkTreeIter iter, parent_iter_tmp;
729 		GtkTreeIter *parent_iter;
730 		gint child_mode;
731 
732 		verbose("Querying \"%s\" for available groups/hosts/shares", bd->uri);
733 
734 		while ((info = g_file_enumerator_next_file(e, NULL, NULL)) != NULL)
735 		{
736 			uri = g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
737 			if (uri != NULL)
738 			{
739 				/* set parent item */
740 				if (bd->parent_path != NULL)
741 				{
742 					gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent_iter_tmp, bd->parent_path);
743 					parent_iter = &parent_iter_tmp;
744 				}
745 				else
746 					parent_iter = NULL;
747 
748 				/* insert the item into the tree store */
749 				gtk_tree_store_insert_with_values(store, &iter, parent_iter, -1,
750 					GIGOLO_BROWSE_NETWORK_COL_URI, uri,
751 					GIGOLO_BROWSE_NETWORK_COL_NAME, g_file_info_get_display_name(info),
752 					GIGOLO_BROWSE_NETWORK_COL_ICON, g_file_info_get_icon(info),
753 					GIGOLO_BROWSE_NETWORK_COL_CAN_MOUNT, (mode == BROWSE_MODE_SHARES),
754 					-1);
755 
756 				/* set mode for the next level or stop recursion */
757 				switch (mode)
758 				{
759 					case BROWSE_MODE_DOMAINS:
760 						child_mode = BROWSE_MODE_HOSTS;
761 						break;
762 					case BROWSE_MODE_HOSTS:
763 						child_mode = BROWSE_MODE_SHARES;
764 						break;
765 					default:
766 						child_mode = BROWSE_MODE_INVALID;
767 				}
768 
769 				/* setup child data and fire it */
770 				if (child_mode != BROWSE_MODE_INVALID)
771 				{
772 					BrowseData *bd_child = g_new0(BrowseData, 1);
773 					bd_child->mode = child_mode;
774 					bd_child->uri = g_strdup(uri);
775 					bd_child->store = store;
776 					bd_child->self = backend;
777 					bd_child->parent = parent;
778 					bd_child->parent_path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
779 					bd_child->browse_func = browse_network_real;
780 
781 					/* recurse into the next level */
782 					browse_network_real(bd_child);
783 				}
784 			}
785 			g_object_unref(info);
786 		}
787 		g_object_unref(e);
788 	}
789 	priv->browse_counter--;
790 
791 	g_object_unref(file);
792 	g_free(bd->uri);
793 	gtk_tree_path_free(bd->parent_path);
794 	g_free(bd);
795 }
796 
797 
gigolo_backend_gvfs_browse_network(GigoloBackendGVFS * backend,GtkWindow * parent,GtkTreeStore * store)798 void gigolo_backend_gvfs_browse_network(GigoloBackendGVFS *backend, GtkWindow *parent, GtkTreeStore *store)
799 {
800 	BrowseData *bd = g_new0(BrowseData, 1);
801 	GigoloBackendGVFSPrivate *priv;
802 
803 	g_return_if_fail(backend != NULL);
804 
805 	bd->mode = BROWSE_MODE_DOMAINS;
806 	bd->parent_path = NULL;
807 	bd->parent = parent;
808 	bd->store = store;
809 	bd->uri = g_strdup("smb://");
810 	bd->self = backend;
811 	bd->browse_func = browse_network_real;
812 
813 	priv = gigolo_backend_gvfs_get_instance_private(backend);
814 	priv->browse_counter = 0;
815 
816 	browse_network_real(bd);
817 
818 	/* When we are here, we initiated the network browsing. Then check the 'browse_counter' and
819 	 * once it is 0 again, we are probably done with browsing. */
820 	g_timeout_add(250, browse_network_ready_cb, backend);
821 }
822 
823 
browse_host_real(BrowseData * bd)824 static void browse_host_real(BrowseData *bd)
825 {
826 	GFile *file;
827 	GFileInfo *info;
828 	GError *error = NULL;
829 	GFileEnumerator *e;
830 	GSList *list = NULL;
831 
832 	g_return_if_fail(bd != NULL);
833 	g_return_if_fail(bd->self != NULL);
834 
835 	file = g_file_new_for_uri(bd->uri);
836 
837 	e = g_file_enumerate_children(file,
838 		G_FILE_ATTRIBUTE_STANDARD_NAME,
839 		G_FILE_QUERY_INFO_NONE, NULL, &error);
840 
841 	if (error != NULL)
842 	{
843 		if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
844 		{
845 			GMountOperation *op = gtk_mount_operation_new(bd->parent);
846 			/* if the URI wasn't mounted yet, mount it and try again from the mount ready callback */
847 			g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, op, NULL,
848 				(GAsyncReadyCallback) browse_network_mount_ready_cb, bd);
849 
850 			g_error_free(error);
851 			g_object_unref(file);
852 			g_object_unref(op);
853 			return;
854 		}
855 		else
856 		{
857 			verbose("%s: %s", G_STRFUNC, error->message);
858 			g_error_free(error);
859 		}
860 	}
861 	else
862 	{
863 		verbose("Querying \"%s\" for available shares", bd->uri);
864 
865 		while ((info = g_file_enumerator_next_file(e, NULL, NULL)) != NULL)
866 		{
867 			list = g_slist_append(list, g_strdup(g_file_info_get_name(info)));
868 			g_object_unref(info);
869 		}
870 		g_object_unref(e);
871 	}
872 
873 	/* propagate our results */
874 	g_signal_emit(bd->self, signals[BROWSE_HOST_FINISHED], 0, list);
875 
876 	g_slist_free_full(list, g_free);
877 	g_object_unref(file);
878 	g_free(bd->uri);
879 	g_free(bd);
880 }
881 
882 
gigolo_backend_gvfs_browse_host(GigoloBackendGVFS * backend,GtkWindow * parent,const gchar * hostname)883 void gigolo_backend_gvfs_browse_host(GigoloBackendGVFS *backend, GtkWindow *parent, const gchar *hostname)
884 {
885 	BrowseData *bd = g_new0(BrowseData, 1);
886 
887 	g_return_if_fail(backend != NULL);
888 	g_return_if_fail(NZV(hostname));
889 
890 	bd->uri = g_strdup_printf("smb://%s", hostname);
891 	bd->self = backend;
892 	bd->parent = parent;
893 	bd->browse_func = browse_host_real;
894 
895 	browse_host_real(bd);
896 }
897 
898 
gigolo_backend_gvfs_get_supported_uri_schemes(void)899 const gchar *const *gigolo_backend_gvfs_get_supported_uri_schemes(void)
900 {
901 	return g_vfs_get_supported_uri_schemes(g_vfs_get_default());
902 }
903 
904 
gigolo_backend_gvfs_is_scheme_supported(const gchar * scheme)905 gboolean gigolo_backend_gvfs_is_scheme_supported(const gchar *scheme)
906 {
907 	const gchar *const *schemes = gigolo_backend_gvfs_get_supported_uri_schemes();
908 	guint i;
909 
910 	g_return_val_if_fail(scheme != NULL, FALSE);
911 
912 	for (i = 0; schemes[i] != NULL; i++)
913 	{
914 		if (gigolo_str_equal(schemes[i], scheme))
915 			return TRUE;
916 	}
917 
918 	return FALSE;
919 }
920