gvfs/daemon/gvfsbackendnetwork.c

1023 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GIO - GLib Input, Output and Streaming Library
* Original work, Copyright (C) 2003 Red Hat, Inc
* GVFS port, Copyright (c) 2008 Andrew Walton.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
* Andrew Walton <awalton@svn.gnome.org> (port only)
*/
#include <config.h>
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include "gvfsbackendnetwork.h"
#include "gvfsdaemonprotocol.h"
#include "gvfsjobcreatemonitor.h"
#include "gvfsjobenumerate.h"
#include "gvfsjobqueryinfo.h"
#include "gvfsmonitor.h"
#include "gvfs-enums.h"
#define DEFAULT_WORKGROUP_NAME "X-GNOME-DEFAULT-WORKGROUP"
#define NETWORK_FILE_ATTRIBUTES "standard::name,standard::display-name,standard::target-uri"
typedef struct {
char *file_name;
char *display_name;
char *target_uri;
GIcon *icon;
GIcon *symbolic_icon;
guint num_duplicates;
} NetworkFile;
static NetworkFile root = { "/" };
struct _GVfsBackendNetwork
{
GVfsBackend parent_instance;
GVfsMonitor *root_monitor;
GMountSpec *mount_spec;
GList *files; /* list of NetworkFiles */
int idle_tag;
GSettings *smb_settings;
GSettings *dnssd_settings;
/* SMB Stuff */
gboolean have_smb;
char *current_workgroup;
GFileMonitor *smb_monitor;
GMutex smb_mount_lock;
GVfsJobMount *mount_job;
/* DNS-SD Stuff */
gboolean have_dnssd;
GDnsSdDisplayMode local_setting;
char *extra_domains;
GFileMonitor *dnssd_monitor;
/* Icons */
GIcon *workgroup_icon; /* GThemedIcon = "network-workgroup" */
GIcon *server_icon; /* GThemedIcon = "network-server" */
GIcon *workgroup_symbolic_icon; /* GThemedIcon = "network-workgroup-symbolic" */
GIcon *server_symbolic_icon; /* GThemedIcon = "network-server-symbolic" */
};
typedef struct _GVfsBackendNetwork GVfsBackendNetwork;
G_DEFINE_TYPE (GVfsBackendNetwork, g_vfs_backend_network, G_VFS_TYPE_BACKEND);
static NetworkFile *
network_file_new (const char *file_name,
const char *display_name,
const char *target_uri,
GIcon *icon,
GIcon *symbolic_icon)
{
NetworkFile *file;
file = g_slice_new0 (NetworkFile);
file->file_name = g_strdup (file_name);
file->display_name = g_strdup (display_name);
file->target_uri = g_strdup (target_uri);
file->icon = g_object_ref (icon);
file->symbolic_icon = g_object_ref (symbolic_icon);
return file;
}
static void
network_file_free (NetworkFile *file)
{
g_free (file->file_name);
g_free (file->display_name);
g_free (file->target_uri);
if (file->icon)
g_object_unref (file->icon);
if (file->symbolic_icon)
g_object_unref (file->symbolic_icon);
g_slice_free (NetworkFile, file);
}
/* Assumes file_name is equal and compares for
metadata changes */
static gboolean
network_file_equal (NetworkFile *a,
NetworkFile *b)
{
if (!g_icon_equal (a->icon, b->icon))
return FALSE;
if (!g_icon_equal (a->symbolic_icon, b->symbolic_icon))
return FALSE;
if ((a->display_name != NULL && b->display_name == NULL) ||
(a->display_name == NULL && b->display_name != NULL))
return FALSE;
if ((a->display_name != NULL && b->display_name != NULL) &&
strcmp (a->display_name, b->display_name) != 0)
return FALSE;
return TRUE;
}
static int
sort_file_by_file_name (NetworkFile *a, NetworkFile *b)
{
return strcmp (a->file_name, b->file_name);
}
static char *
get_pretty_scheme_for_uri (const char *uri)
{
GFile *file;
char *scheme;
char *pretty = NULL;
file = g_file_new_for_uri (uri);
if (file == NULL)
return NULL;
scheme = g_file_get_uri_scheme (file);
if (g_strcmp0 (scheme, "afp") == 0
|| g_strcmp0 (scheme, "smb") == 0)
{
pretty = g_strdup (_("File Sharing"));
}
else if (g_strcmp0 (scheme, "sftp") == 0
|| g_strcmp0 (scheme, "ssh") == 0)
{
pretty = g_strdup (_("Remote Login"));
}
else
{
pretty = g_strdup (scheme);
}
g_free (scheme);
return pretty;
}
static void
network_file_append_service_name (NetworkFile *file)
{
char *name;
char *service;
service = get_pretty_scheme_for_uri (file->target_uri);
name = g_strdup_printf ("%s (%s)", file->display_name, service);
g_free (service);
g_free (file->display_name);
file->display_name = name;
}
static void
uniquify_display_names (GVfsBackendNetwork *backend)
{
GHashTable *names;
GList *l;
names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (l = backend->files; l != NULL; l = l->next)
{
NetworkFile *prev_file;
NetworkFile *file = l->data;
prev_file = g_hash_table_lookup (names, file->display_name);
if (prev_file != NULL)
{
prev_file->num_duplicates++;
/* only change the first file once */
if (prev_file->num_duplicates == 1)
network_file_append_service_name (prev_file);
network_file_append_service_name (file);
}
g_hash_table_replace (names, g_strdup (file->display_name), file);
}
g_hash_table_destroy (names);
}
static void
update_from_files (GVfsBackendNetwork *backend,
GList *files)
{
GList *old_files;
GList *oldl, *newl;
char *file_name;
NetworkFile *old, *new;
int cmp;
old_files = backend->files;
backend->files = g_list_sort (files, (GCompareFunc)sort_file_by_file_name);
uniquify_display_names (backend);
/* Generate change events */
oldl = old_files;
newl = backend->files;
while (oldl != NULL || newl != NULL)
{
if (oldl == NULL)
{
cmp = 1;
new = newl->data;
old = NULL;
}
else if (newl == NULL)
{
cmp = -1;
new = NULL;
old = oldl->data;
}
else
{
new = newl->data;
old = oldl->data;
cmp = sort_file_by_file_name (old, new);
}
if (cmp == 0)
{
if (!network_file_equal (old, new))
{
file_name = g_strconcat ("/", new->file_name, NULL);
g_vfs_monitor_emit_event (backend->root_monitor,
G_FILE_MONITOR_EVENT_CHANGED,
file_name,
NULL);
g_free (file_name);
}
oldl = oldl->next;
newl = newl->next;
}
else if (cmp < 0)
{
file_name = g_strconcat ("/", old->file_name, NULL);
g_vfs_monitor_emit_event (backend->root_monitor,
G_FILE_MONITOR_EVENT_DELETED,
file_name,
NULL);
g_free (file_name);
oldl = oldl->next;
}
else
{
file_name = g_strconcat ("/", new->file_name, NULL);
g_vfs_monitor_emit_event (backend->root_monitor,
G_FILE_MONITOR_EVENT_CREATED,
file_name,
NULL);
g_free (file_name);
newl = newl->next;
}
}
g_list_free_full (old_files, (GDestroyNotify)network_file_free);
}
static void
notify_dnssd_local_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
GFileMonitorEvent event_type, gpointer user_data);
static void
notify_smb_files_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
GFileMonitorEvent event_type, gpointer user_data);
static void
recompute_files (GVfsBackendNetwork *backend)
{
GFile *server_file;
GFileEnumerator *enumer;
GFileInfo *info;
GFileMonitor *monitor;
GError *error;
GList *files;
NetworkFile *file;
char *file_name, *link_uri;
files = NULL;
error = NULL;
if (backend->have_smb)
{
char *workgroup;
/* smb:/// root link */
file = network_file_new ("smb-root",
_("Windows Network"),
"smb:///",
backend->workgroup_icon,
backend->workgroup_symbolic_icon);
files = g_list_prepend (files, file);
if (backend->current_workgroup == NULL ||
backend->current_workgroup[0] == 0)
workgroup = g_strconcat ("smb://", DEFAULT_WORKGROUP_NAME, "/", NULL);
else
workgroup = g_strconcat ("smb://", backend->current_workgroup, "/", NULL);
server_file = g_file_new_for_uri (workgroup);
/* recreate monitor if our workgroup changed or we don't have a monitor */
if (backend->smb_monitor == NULL)
{
monitor = g_file_monitor_directory (server_file, G_FILE_MONITOR_NONE, NULL, &error);
if (monitor)
{
g_signal_connect (monitor, "changed",
(GCallback)notify_smb_files_changed, (gpointer)backend);
/* takes ref */
backend->smb_monitor = monitor;
}
else
{
char *uri = g_file_get_uri (server_file);
g_debug ("Couldn't create directory monitor on %s. Error: %s\n",
uri, error ? error->message : "");
g_free (uri);
g_clear_error (&error);
}
}
/* children of current workgroup */
enumer = g_file_enumerate_children (server_file,
NETWORK_FILE_ATTRIBUTES,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (enumer != NULL)
{
info = g_file_enumerator_next_file (enumer, NULL, NULL);
while (info != NULL)
{
file_name = g_strconcat("smb-server-", g_file_info_get_name (info), NULL);
link_uri = g_strconcat("smb://", g_file_info_get_name (info), "/", NULL);
file = network_file_new (file_name,
g_file_info_get_display_name (info),
link_uri,
backend->server_icon,
backend->server_symbolic_icon);
files = g_list_prepend (files, file);
g_free (link_uri);
g_free (file_name);
g_object_unref (info);
info = g_file_enumerator_next_file (enumer, NULL, NULL);
}
g_file_enumerator_close (enumer, NULL, NULL);
g_object_unref (enumer);
}
g_object_unref (server_file);
g_free (workgroup);
}
if (backend->have_dnssd)
{
server_file = g_file_new_for_uri ("dns-sd://local/");
/* create directory monitor if we haven't already */
if (backend->dnssd_monitor == NULL)
{
monitor = g_file_monitor_directory (server_file, G_FILE_MONITOR_NONE, NULL, &error);
if (monitor)
{
g_signal_connect (monitor, "changed",
(GCallback)notify_dnssd_local_changed, (gpointer)backend);
/* takes ref */
backend->dnssd_monitor = monitor;
}
else
{
char *uri = g_file_get_uri(server_file);
g_warning ("Couldn't create directory monitor on %s. Error: %s",
uri, error->message);
g_free (uri);
g_clear_error (&error);
}
}
if (backend->local_setting == G_DNS_SD_DISPLAY_MODE_MERGED)
{
/* "merged": add local domains to network:/// */
enumer = g_file_enumerate_children (server_file,
NETWORK_FILE_ATTRIBUTES,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (enumer != NULL)
{
info = g_file_enumerator_next_file (enumer, NULL, NULL);
while (info != NULL)
{
file_name = g_strconcat("dnssd-domain-", g_file_info_get_name (info), NULL);
link_uri = g_strdup(g_file_info_get_attribute_string (info,
"standard::target-uri"));
file = network_file_new (file_name,
g_file_info_get_display_name (info),
link_uri,
backend->server_icon,
backend->server_symbolic_icon);
files = g_list_prepend (files, file);
g_free (link_uri);
g_free (file_name);
g_object_unref (info);
info = g_file_enumerator_next_file (enumer, NULL, NULL);
}
}
g_file_enumerator_close (enumer, NULL, NULL);
g_object_unref (enumer);
}
else
{
/* "separate": a link to dns-sd://local/ */
file = network_file_new ("dnssd-local",
_("Local Network"),
"dns-sd://local/",
backend->workgroup_icon,
backend->workgroup_symbolic_icon);
files = g_list_prepend (files, file);
}
g_object_unref (server_file);
/* If gsettings key "extra-domains" (org.gnome.system.dns_sd) is set to a list of domains:
* links to dns-sd://$domain/ */
if (backend->extra_domains != NULL &&
backend->extra_domains[0] != 0)
{
char **domains;
int i;
domains = g_strsplit (backend->extra_domains, ",", 0);
for (i=0; domains[i] != NULL; i++)
{
file_name = g_strconcat("dnssd-domain-", domains[i], NULL);
link_uri = g_strconcat("dns-sd://", domains[i], "/", NULL);
file = network_file_new (file_name,
domains[i],
link_uri,
backend->workgroup_icon,
backend->workgroup_symbolic_icon);
files = g_list_prepend (files, file);
g_free (link_uri);
g_free (file_name);
}
g_strfreev (domains);
}
}
update_from_files (backend, files);
}
static gboolean
idle_add_recompute (GVfsBackendNetwork *backend)
{
backend->idle_tag = 0;
recompute_files (backend);
return FALSE;
}
static void
mount_smb_done_cb (GObject *object,
GAsyncResult *res,
gpointer user_data)
{
GVfsBackendNetwork *backend = G_VFS_BACKEND_NETWORK(user_data);
GError *error = NULL;
g_file_mount_enclosing_volume_finish (G_FILE (object), res, &error);
if (error)
g_error_free (error);
recompute_files (backend);
/* We've been spawned from try_mount */
if (backend->mount_job)
{
g_vfs_job_succeeded (G_VFS_JOB (backend->mount_job));
g_object_unref (backend->mount_job);
}
g_mutex_unlock (&backend->smb_mount_lock);
g_object_unref (backend);
}
static void
remount_smb (GVfsBackendNetwork *backend, GVfsJobMount *job)
{
GFile *file;
char *workgroup;
if (! g_mutex_trylock (&backend->smb_mount_lock))
/* Do nothing when the mount operation is already active */
return;
backend->mount_job = job ? g_object_ref (job) : NULL;
if (backend->current_workgroup == NULL ||
backend->current_workgroup[0] == 0)
workgroup = g_strconcat ("smb://", DEFAULT_WORKGROUP_NAME, "/", NULL);
else
workgroup = g_strconcat ("smb://", backend->current_workgroup, "/", NULL);
file = g_file_new_for_uri (workgroup);
g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE,
NULL, NULL, mount_smb_done_cb, g_object_ref (backend));
g_free (workgroup);
g_object_unref (file);
}
static void
notify_smb_files_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
GFileMonitorEvent event_type, gpointer user_data)
{
GVfsBackendNetwork *backend = G_VFS_BACKEND_NETWORK(user_data);
switch (event_type)
{
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
case G_FILE_MONITOR_EVENT_CREATED:
case G_FILE_MONITOR_EVENT_DELETED:
if (backend->idle_tag == 0)
backend->idle_tag = g_idle_add ((GSourceFunc)idle_add_recompute, backend);
break;
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
case G_FILE_MONITOR_EVENT_UNMOUNTED:
/* in either event, our smb backend is/will be gone. */
if (backend->idle_tag == 0)
backend->idle_tag = g_idle_add ((GSourceFunc)idle_add_recompute, backend);
/* stop monitoring as the backend's gone. */
if (backend->smb_monitor)
{
g_file_monitor_cancel (backend->smb_monitor);
g_object_unref (backend->smb_monitor);
backend->smb_monitor = NULL;
}
break;
default:
break;
}
}
static void
notify_dnssd_local_changed (GFileMonitor *monitor, GFile *file, GFile *other_file,
GFileMonitorEvent event_type, gpointer user_data)
{
GVfsBackendNetwork *backend = G_VFS_BACKEND_NETWORK(user_data);
switch (event_type)
{
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
case G_FILE_MONITOR_EVENT_CREATED:
case G_FILE_MONITOR_EVENT_DELETED:
if (backend->idle_tag == 0)
backend->idle_tag = g_idle_add ((GSourceFunc)idle_add_recompute, backend);
break;
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
case G_FILE_MONITOR_EVENT_UNMOUNTED:
/* in either event, our dns-sd backend is/will be gone. */
if (backend->idle_tag == 0)
backend->idle_tag = g_idle_add ((GSourceFunc)idle_add_recompute, backend);
/* stop monitoring as the backend's gone. */
g_file_monitor_cancel (backend->dnssd_monitor);
g_object_unref (backend->dnssd_monitor);
backend->dnssd_monitor = NULL;
break;
default:
break;
}
}
static gboolean
dnssd_settings_change_event_cb (GSettings *settings,
gpointer keys,
gint n_keys,
gpointer user_data)
{
GVfsBackendNetwork *backend = G_VFS_BACKEND_NETWORK(user_data);
g_free (backend->extra_domains);
backend->extra_domains = g_settings_get_string (settings, "extra-domains");
backend->local_setting = g_settings_get_enum (settings, "display-local");
/* don't re-issue recomputes if we've already queued one. */
if (backend->idle_tag == 0)
backend->idle_tag = g_idle_add ((GSourceFunc)idle_add_recompute, backend);
return FALSE;
}
static gboolean
smb_settings_change_event_cb (GSettings *settings,
gpointer keys,
gint n_keys,
gpointer user_data)
{
GVfsBackendNetwork *backend = G_VFS_BACKEND_NETWORK(user_data);
g_free (backend->current_workgroup);
backend->current_workgroup = g_settings_get_string (settings, "workgroup");
/* cancel the smb monitor */
if (backend->smb_monitor)
{
g_signal_handlers_disconnect_by_func (backend->smb_monitor,
notify_smb_files_changed,
backend);
g_file_monitor_cancel (backend->smb_monitor);
g_object_unref (backend->smb_monitor);
backend->smb_monitor = NULL;
}
remount_smb (backend, NULL);
return FALSE;
}
static NetworkFile *
lookup_network_file (GVfsBackendNetwork *backend,
GVfsJob *job,
const char *file_name)
{
GList *l;
NetworkFile *file;
if (*file_name != '/')
goto out;
while (*file_name == '/')
file_name++;
if (*file_name == 0)
return &root;
if (strchr (file_name, '/') != NULL)
goto out;
for (l = backend->files; l != NULL; l = l->next)
{
file = l->data;
if (strcmp (file->file_name, file_name) == 0)
return file;
}
out:
g_vfs_job_failed (job, G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
_("File doesnt exist"));
return NULL;
}
static void
file_info_from_file (NetworkFile *file,
GFileInfo *info)
{
g_return_if_fail (file != NULL || info != NULL);
g_file_info_set_name (info, file->file_name);
g_file_info_set_display_name (info, file->display_name);
if (file->icon)
g_file_info_set_icon (info, file->icon);
if (file->symbolic_icon)
g_file_info_set_symbolic_icon (info, file->symbolic_icon);
g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
file->target_uri);
}
/* Backend Functions */
static gboolean
try_enumerate (GVfsBackend *backend,
GVfsJobEnumerate *job,
const char *file_name,
GFileAttributeMatcher *attribute_matcher,
GFileQueryInfoFlags flags)
{
NetworkFile *file;
GList *l;
GFileInfo *info;
file = lookup_network_file (G_VFS_BACKEND_NETWORK (backend),
G_VFS_JOB (job), file_name);
if (file != &root)
{
if (file != NULL)
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_DIRECTORY,
_("The file is not a directory"));
return TRUE;
}
g_vfs_job_succeeded (G_VFS_JOB(job));
/* Enumerate root */
for (l = G_VFS_BACKEND_NETWORK (backend)->files; l != NULL; l = l->next)
{
file = l->data;
info = g_file_info_new ();
file_info_from_file (file, info);
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
}
g_vfs_job_enumerate_done (job);
return TRUE;
}
static gboolean
try_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job,
const char *file_name,
GFileQueryInfoFlags flags,
GFileInfo *info,
GFileAttributeMatcher *matcher)
{
NetworkFile *file;
file = lookup_network_file (G_VFS_BACKEND_NETWORK (backend),
G_VFS_JOB (job), file_name);
if (file == &root)
{
GIcon *icon;
g_file_info_set_name (info, "/");
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
g_file_info_set_display_name (info, _("Network"));
icon = g_themed_icon_new ("network-workgroup");
g_file_info_set_icon (info, icon);
g_object_unref (icon);
icon = g_themed_icon_new ("network-workgroup-symbolic");
g_file_info_set_symbolic_icon (info, icon);
g_object_unref (icon);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_content_type (info, "inode/directory");
g_vfs_job_succeeded (G_VFS_JOB (job));
}
else if (file != NULL)
{
file_info_from_file (file, info);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
return TRUE;
}
static gboolean
try_mount (GVfsBackend *backend,
GVfsJobMount *job,
GMountSpec *mount_spec,
GMountSource *mount_source,
gboolean is_automount)
{
GVfsBackendNetwork *network_backend = G_VFS_BACKEND_NETWORK (backend);
network_backend->root_monitor = g_vfs_monitor_new (backend);
if (network_backend->have_smb)
{
remount_smb (network_backend, job);
}
else
{
recompute_files (network_backend);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
return TRUE;
}
/* handles both file and dir monitors
* for now we only fire events on the root directory.
* are individual file monitors needed for this backend?
*/
static gboolean
try_create_monitor (GVfsBackend *backend,
GVfsJobCreateMonitor *job,
const char *file_name,
GFileMonitorFlags flags)
{
NetworkFile *file;
GVfsBackendNetwork *network_backend;
network_backend = G_VFS_BACKEND_NETWORK (backend);
file = lookup_network_file (network_backend, G_VFS_JOB (job), file_name);
if (file != &root)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Cant monitor file or directory."));
return TRUE;
}
g_vfs_job_create_monitor_set_monitor (job, network_backend->root_monitor);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static gboolean
try_query_fs_info (GVfsBackend *backend,
GVfsJobQueryFsInfo *job,
const char *filename,
GFileInfo *info,
GFileAttributeMatcher *matcher)
{
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "network");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE;
}
static void
g_vfs_backend_network_init (GVfsBackendNetwork *network_backend)
{
GVfsBackend *backend = G_VFS_BACKEND (network_backend);
GMountSpec *mount_spec;
char *current_workgroup;
const char * const* supported_vfs;
int i;
g_mutex_init (&network_backend->smb_mount_lock);
supported_vfs = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
network_backend->have_smb = FALSE;
network_backend->have_dnssd = FALSE;
for (i=0; supported_vfs[i]!=NULL; i++)
{
if (strcmp(supported_vfs[i], "smb") == 0)
network_backend->have_smb = TRUE;
if (strcmp(supported_vfs[i], "dns-sd") == 0)
network_backend->have_dnssd = TRUE;
}
if (network_backend->have_smb)
{
network_backend->smb_settings = g_settings_new ("org.gnome.system.smb");
current_workgroup = g_settings_get_string (network_backend->smb_settings, "workgroup");
if (current_workgroup == NULL ||
current_workgroup[0] == 0)
/* it's okay if current_workgroup is null here,
* it's checked before the NetworkFile is added anyway. */
network_backend->current_workgroup = NULL;
else
network_backend->current_workgroup = current_workgroup;
g_signal_connect (network_backend->smb_settings,
"change-event",
G_CALLBACK (smb_settings_change_event_cb),
network_backend);
}
if (network_backend->have_dnssd)
{
network_backend->dnssd_settings = g_settings_new ("org.gnome.system.dns_sd");
network_backend->local_setting = g_settings_get_enum (network_backend->dnssd_settings, "display-local");
network_backend->extra_domains = g_settings_get_string (network_backend->dnssd_settings, "extra-domains");
g_signal_connect (network_backend->dnssd_settings,
"change-event",
G_CALLBACK (dnssd_settings_change_event_cb),
network_backend);
}
g_vfs_backend_set_display_name (backend, _("Network"));
g_vfs_backend_set_stable_name (backend, _("Network"));
g_vfs_backend_set_icon_name (backend, "network-workgroup");
g_vfs_backend_set_symbolic_icon_name (backend, "network-workgroup-symbolic");
g_vfs_backend_set_user_visible (backend, FALSE);
mount_spec = g_mount_spec_new ("network");
g_vfs_backend_set_mount_spec (backend, mount_spec);
network_backend->mount_spec = mount_spec;
network_backend->workgroup_icon = g_themed_icon_new ("network-workgroup");
network_backend->server_icon = g_themed_icon_new ("network-server");
network_backend->workgroup_symbolic_icon = g_themed_icon_new ("network-workgroup-symbolic");
network_backend->server_symbolic_icon = g_themed_icon_new ("network-server-symbolic");
}
static void
g_vfs_backend_network_finalize (GObject *object)
{
GVfsBackendNetwork *backend;
backend = G_VFS_BACKEND_NETWORK (object);
g_mutex_clear (&backend->smb_mount_lock);
g_mount_spec_unref (backend->mount_spec);
g_object_unref (backend->root_monitor);
g_object_unref (backend->workgroup_icon);
g_object_unref (backend->server_icon);
g_object_unref (backend->workgroup_symbolic_icon);
g_object_unref (backend->server_symbolic_icon);
if (backend->smb_settings)
{
g_signal_handlers_disconnect_by_func (backend->smb_settings, smb_settings_change_event_cb, backend);
g_clear_object (&backend->smb_settings);
}
if (backend->dnssd_settings)
{
g_signal_handlers_disconnect_by_func (backend->dnssd_settings, dnssd_settings_change_event_cb, backend);
g_clear_object (&backend->dnssd_settings);
}
if (backend->dnssd_monitor)
{
g_signal_handlers_disconnect_by_func (backend->dnssd_monitor, notify_dnssd_local_changed, backend);
g_clear_object (&backend->dnssd_monitor);
}
if (backend->smb_monitor)
{
g_signal_handlers_disconnect_by_func (backend->smb_monitor, notify_smb_files_changed, backend);
g_clear_object (&backend->smb_monitor);
}
if (backend->idle_tag)
{
g_source_remove (backend->idle_tag);
backend->idle_tag = 0;
}
if (backend->files)
{
g_list_free_full (backend->files, (GDestroyNotify)network_file_free);
backend->files = NULL;
}
g_free (backend->current_workgroup);
g_free (backend->extra_domains);
if (G_OBJECT_CLASS (g_vfs_backend_network_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_backend_network_parent_class)->finalize) (object);
}
static void
g_vfs_backend_network_class_init (GVfsBackendNetworkClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
gobject_class->finalize = g_vfs_backend_network_finalize;
backend_class->try_mount = try_mount;
backend_class->try_query_info = try_query_info;
backend_class->try_query_fs_info = try_query_fs_info;
backend_class->try_enumerate = try_enumerate;
backend_class->try_create_dir_monitor = try_create_monitor;
backend_class->try_create_file_monitor = try_create_monitor;
}
void
g_vfs_network_daemon_init (void)
{
/* Translators: this is the friendly name of the 'network://' backend that
* shows computers in your local network. */
g_set_application_name (_("Network Location Monitor"));
}