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