mirror of https://gitee.com/openkylin/gvfs.git
1023 lines
31 KiB
C
1023 lines
31 KiB
C
/* 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 doesn’t 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,
|
||
_("Can’t 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"));
|
||
}
|
||
|