diff --git a/daemon/gvfsbackendnds.c b/daemon/gvfsbackendnds.c new file mode 100644 index 0000000..960d457 --- /dev/null +++ b/daemon/gvfsbackendnds.c @@ -0,0 +1,747 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode; nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "gvfsbackendnds.h" +#include "gvfsjobmountmountable.h" +#include "gvfsjobopenforread.h" +#include "gvfsjobread.h" +#include "gvfsjobseekread.h" +#include "gvfsjobqueryinfo.h" +#include "gvfsjobenumerate.h" +#include "gvfsdaemonprotocol.h" +#include "gmounttracker.h" +#include "gvfsmonitor.h" + + + +typedef struct { + char *name; + char *name_normalized; + char *name_utf8; +} BrowseEntry; + +struct _GVfsBackendNds +{ + GVfsBackend parent_instance; + + char *server; + char *mounted_server; + + GMutex entries_lock; + GList *entries; + int entry_errno; +}; + +static GMountTracker *mount_tracker = NULL; + +G_DEFINE_TYPE (GVfsBackendNds, g_vfs_backend_nds, G_VFS_TYPE_BACKEND) + +static gboolean +is_root (const char *filename) +{ + const char *p; + + p = filename; + while (*p == '/') + p++; + + return *p == 0; +} + +static char * +normalize_nds_name_helper (const char *name, gssize len, gboolean valid_utf8) +{ + if (valid_utf8) + return g_utf8_casefold (name, len); + else + return g_ascii_strdown (name, len); +} + +static char * +normalize_nds_name (const char *name, gssize len) +{ + gboolean valid_utf8; + + valid_utf8 = g_utf8_validate (name, len, NULL); + return normalize_nds_name_helper (name, len, valid_utf8); +} + +static char * +nds_name_to_utf8 (const char *name, gboolean *valid_utf8_out) +{ + GString *string; + const gchar *remainder, *invalid; + gint remaining_bytes, valid_bytes; + gboolean valid_utf8; + + remainder = name; + remaining_bytes = strlen (name); + valid_utf8 = TRUE; + + string = g_string_sized_new (remaining_bytes); + while (remaining_bytes != 0) + { + if (g_utf8_validate (remainder, remaining_bytes, &invalid)) + break; + valid_utf8 = FALSE; + + valid_bytes = invalid - remainder; + + g_string_append_len (string, remainder, valid_bytes); + /* append U+FFFD REPLACEMENT CHARACTER */ + g_string_append (string, "\357\277\275"); + + remaining_bytes -= valid_bytes + 1; + remainder = invalid + 1; + } + + g_string_append (string, remainder); + + if (valid_utf8_out) + *valid_utf8_out = valid_utf8; + + return g_string_free (string, FALSE); +} + +static void +browse_entry_free (BrowseEntry *entry) +{ + if(entry->name != NULL) + g_free (entry->name); + if(entry != NULL) + g_free (entry); +} + + +static void +g_vfs_backend_nds_finalize (GObject *object) +{ + GVfsBackendNds *backend; + + backend = G_VFS_BACKEND_NDS (object); + + if(backend->mounted_server != NULL) + g_free (backend->mounted_server); + if(backend->server != NULL) + g_free (backend->server); + + g_mutex_clear (&backend->entries_lock); + + g_list_foreach (backend->entries, (GFunc)browse_entry_free, NULL); + if(backend->entries != NULL) + g_list_free (backend->entries); + + if (G_OBJECT_CLASS (g_vfs_backend_nds_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_backend_nds_parent_class)->finalize) (object); +} + +static void +g_vfs_backend_nds_init (GVfsBackendNds *backend) +{ + g_mutex_init(&backend->entries_lock); + + if (mount_tracker == NULL) + mount_tracker = g_mount_tracker_new (NULL, FALSE); +} + + +static void +update_cache (GVfsBackendNds *backend,const char *filename) +{ + GList *entries; + int entry_errno; + char *objectname=NULL; + char *treename=NULL; + GList *objectlist=NULL; + GList *object=NULL; + FILE *fptr=NULL; + void *handle=NULL; //handle for dlopen + int res; + char *ptr=NULL; + char *server_name=NULL; + struct utsname utsbuf; +/* Function Pointer to /opt/novell/lib/libinterface.so */ + int (*retrieve_children)(char *,char ***,int *); + char **object_list_array=NULL; + int num_objects=0; + int i; + + entries = NULL; + entry_errno = 0; + + if(backend->server != NULL) + { + server_name = g_strdup(backend->server); + if((ptr = strchr(server_name,'%')) != NULL) + *ptr = '\0'; + + } + + if(backend->server == NULL) //retrieve Tree names + { + treename = (char *)malloc(sizeof(char) * 80); + system("/opt/novell/ncl/bin/retrieve_trees.pl"); + fptr = fopen("/tmp/ndstrees.txt","r"); + if(fptr == NULL) + { + entry_errno = -1; + goto out; + } + while (fgets(treename,80,fptr) != NULL) + { + objectlist = g_list_append(objectlist,g_strdup(treename)); + memset(treename,'\0',80); + } + fclose(fptr); + free(treename); + } + + else //retrieve object names + { + uname(&utsbuf); + if(!strcmp(utsbuf.machine,"x86_64")) + handle = dlopen("/usr/lib64/libinterface.so",RTLD_NOW | RTLD_DEEPBIND); + else + handle = dlopen("/usr/lib/libinterface.so",RTLD_NOW | RTLD_DEEPBIND); + + if(handle == NULL) + { + return; + } + *(void **)(&retrieve_children) = dlsym(handle,"retrieve_children"); + + if(retrieve_children != NULL) + { + res = (*retrieve_children)(server_name,&object_list_array,&num_objects); + if(res == 0) + { + for(i=0;i < num_objects; i++) + { + objectlist = g_list_append(objectlist,g_strdup(*(object_list_array+ i))); + } + } + } + else + { + return; + } + dlclose(handle); + } + + + gboolean valid_utf8; + BrowseEntry *entry; + + for(object = objectlist;object != NULL;object = object->next) + { + entry = g_new (BrowseEntry, 1); + objectname = object->data; + entry->name = g_strdup (objectname); + entry->name_utf8 = nds_name_to_utf8 (objectname, &valid_utf8); + entry->name_normalized = normalize_nds_name_helper (objectname, -1, valid_utf8); + + entries = g_list_append (entries, entry); + } + + //if(objectlist != NULL) + //g_list_free(objectlist); + +out: + + if(entry_errno == 0) + { + g_mutex_lock (&backend->entries_lock); + backend->entries = entries; + g_mutex_unlock (&backend->entries_lock); + } + backend->entry_errno = entry_errno; + +} + +static BrowseEntry * +find_entry_unlocked (GVfsBackendNds *backend, + const char *filename) +{ + BrowseEntry *entry, *found; + GList *l; + char *end; + int len; + + while (*filename == '/') + filename++; + + end = strchr (filename, '/'); + if (end) + { + len = end - filename; + + while (*end == '/') + end++; + + if (*end != 0) + return NULL; + } + else + len = strlen (filename); + + /* First look for an exact filename match */ + found = NULL; + for (l = backend->entries; l != NULL; l = l->next) + { + entry = l->data; + + if (strncmp (filename, entry->name, len) == 0 && + strlen (entry->name) == len) + { + found = entry; + break; + } + } + + if (found == NULL) + { + char *normalized; + /* That failed, try normalizing the filename */ + normalized = normalize_nds_name (filename, len); + + for (l = backend->entries; l != NULL; l = l->next) + { + entry = l->data; + + if (strcmp (normalized, entry->name_normalized) == 0) + { + found = entry; + break; + } + } + if(normalized != NULL) + g_free (normalized); + } + + return found; +} + +static GMountSpec * +get_mount_spec_for_share (const char *server, const char *share) +{ + GMountSpec *mount_spec; + char *normalized; + + mount_spec = g_mount_spec_new ("nds"); + g_mount_spec_set (mount_spec, "host", server); + g_mount_spec_set (mount_spec, "share", share); + + return mount_spec; +} + + + +static void +do_mount (GVfsBackend *backend, + GVfsJobMount *job, + GMountSpec *mount_spec, + GMountSource *mount_source, + gboolean is_automount) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + char *display_name; + char *icon; + GMountSpec *browse_mount_spec; + + + icon = NULL; + if (op_backend->server == NULL) + { + display_name = g_strdup (_("NVVFS Top Directory")); + browse_mount_spec = g_mount_spec_new ("nds"); + icon = "network-workgroup"; + } + else + { + display_name = g_strdup_printf (_("NVVFS Objects")); + browse_mount_spec = g_mount_spec_new ("nds"); + //g_mount_spec_set (browse_mount_spec, "host", op_backend->mounted_server); + g_mount_spec_set (browse_mount_spec, "host", op_backend->server); + icon = "network-server"; + } + + g_vfs_backend_set_display_name (backend, display_name); + g_free (display_name); + if (icon) + g_vfs_backend_set_icon_name (backend, icon); + g_vfs_backend_set_user_visible (backend, FALSE); + g_vfs_backend_set_mount_spec (backend, browse_mount_spec); + g_mount_spec_unref (browse_mount_spec); + + g_vfs_job_succeeded (G_VFS_JOB (job)); +} + +static gboolean +try_mount (GVfsBackend *backend, + GVfsJobMount *job, + GMountSpec *mount_spec, + GMountSource *mount_source, + gboolean is_automount) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + const char *server; + + server = g_mount_spec_get (mount_spec, "host"); + if (server) + { + op_backend->server = g_strdup (server); + op_backend->mounted_server = g_strdup (server); + } + else + { + op_backend->server = NULL; + op_backend->mounted_server = NULL; + } + + return FALSE; +} + +static void +run_mount_mountable (GVfsBackendNds *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GMountSpec *mount_spec; + + g_mutex_lock (&backend->entries_lock); + + backend->server = g_strdup(filename); + + mount_spec = get_mount_spec_for_share (backend->server, filename); + g_vfs_job_mount_mountable_set_target (job, mount_spec, "/", TRUE); + g_mount_spec_unref (mount_spec); + g_mutex_unlock (&backend->entries_lock); + + g_vfs_job_succeeded (G_VFS_JOB (job)); +} + + +static gboolean +do_mount_mountable (GVfsBackend *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_mount_mountable (op_backend, + job, + filename, + mount_source); + return TRUE; +} + +static gboolean +try_mount_mountable (GVfsBackend *backend, + GVfsJobMountMountable *job, + const char *filename, + GMountSource *mount_source) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + + if (is_root (filename)) + { + g_vfs_job_failed (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE, + _("The file is not a mountable")); + return TRUE; + } + +update_cache (op_backend,filename); + + run_mount_mountable (op_backend, + job, + filename, + mount_source); + return TRUE; +} + + + +#define SUB_DELIM_CHARS "!$&'()*+,;=" + +static gboolean +is_valid (char c, const char *reserved_chars_allowed) +{ + if (g_ascii_isalnum (c) || + c == '-' || + c == '.' || + c == '_' || + c == '~') + return TRUE; + + if (reserved_chars_allowed && + strchr (reserved_chars_allowed, c) != NULL) + return TRUE; + + return FALSE; +} + +static void +g_string_append_encoded (GString *string, + const char *encoded, + const char *encoded_end, + const char *reserved_chars_allowed) +{ + char c; + static const gchar hex[16] = "0123456789ABCDEF"; + + if (encoded_end == NULL) + encoded_end = encoded + strlen (encoded); + + while (encoded < encoded_end) + { + c = *encoded++; + + if (is_valid (c, reserved_chars_allowed)) + g_string_append_c (string, c); + else + { + g_string_append_c (string, '%'); + g_string_append_c (string, hex[((guchar)c) >> 4]); + g_string_append_c (string, hex[((guchar)c) & 0xf]); + } + } +} + +static void +get_file_info_from_entry (GVfsBackendNds *backend, BrowseEntry *entry, GFileInfo *info) +{ + GString *uri; + GIcon *icon; + + g_file_info_set_name (info, entry->name); + g_file_info_set_display_name (info, entry->name_utf8); + g_file_info_set_edit_name (info, entry->name_utf8); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE); + + if(backend->server == NULL) //NDS-Tree icon + icon = g_themed_icon_new ("ncl-ndstree"); + else + icon = g_themed_icon_new ("ncl-nwcontext"); + + if (icon) + { + g_file_info_set_icon (info, icon); + g_object_unref (icon); + } + + g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT); + + uri = g_string_new ("nds://"); + g_string_append_encoded (uri, entry->name, NULL, NULL); + g_string_append_c (uri, '/'); + + g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, uri->str); + + g_string_free (uri, TRUE); + +} + +static void +run_query_info (GVfsBackendNds *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + BrowseEntry *entry; + + g_mutex_lock (&backend->entries_lock); + + entry = find_entry_unlocked (backend, filename); + + if (entry) + get_file_info_from_entry (backend, entry, info); + + g_mutex_unlock (&backend->entries_lock); + + if (entry) + g_vfs_job_succeeded (G_VFS_JOB (job)); + else + g_vfs_job_failed (G_VFS_JOB (job), + G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("File doesn't exist")); +} + +static void +do_query_info (GVfsBackend *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileQueryInfoFlags flags, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + run_query_info (op_backend, job, filename, info, matcher); +} + +static gboolean +try_query_info (GVfsBackend *backend, + GVfsJobQueryInfo *job, + const char *filename, + GFileQueryInfoFlags flags, + GFileInfo *info, + GFileAttributeMatcher *matcher) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + const char *icon_name = NULL; + GIcon *icon; + + if (filename && is_root (filename)) + { + g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); + g_file_info_set_name (info, "/"); + g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend)); + /* FIXME: This API does not seem to be in gvfs-1.2.0. Find a suitable replacement? */ + /* icon_name = g_vfs_backend_get_icon_name (backend); */ + if (icon_name) + { + icon = g_themed_icon_new (icon_name); + g_file_info_set_icon (info, icon); + g_object_unref (icon); + } + g_vfs_job_succeeded (G_VFS_JOB (job)); + + return TRUE; + } + + return FALSE; + + run_query_info (op_backend, job, filename, info, matcher); + return TRUE; +} + +static void +run_enumerate (GVfsBackendNds *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher) +{ + GList *files, *l; + GFileInfo *info; + + g_vfs_job_succeeded (G_VFS_JOB (job)); + + files = NULL; + g_mutex_lock (&backend->entries_lock); + for (l = backend->entries; l != NULL; l = l->next) + { + BrowseEntry *entry = l->data; + + info = g_file_info_new (); + get_file_info_from_entry (backend, entry, info); + + files = g_list_prepend (files, info); + } + g_mutex_unlock (&backend->entries_lock); + + files = g_list_reverse (files); + + g_vfs_job_enumerate_add_infos (job, files); + g_list_foreach (files, (GFunc)g_object_unref, NULL); + g_list_free (files); + + g_vfs_job_enumerate_done (job); +} + +static void +do_enumerate (GVfsBackend *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher, + GFileQueryInfoFlags flags) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_enumerate (op_backend, job, filename, matcher); +} + +static gboolean +try_enumerate (GVfsBackend *backend, + GVfsJobEnumerate *job, + const char *filename, + GFileAttributeMatcher *matcher, + GFileQueryInfoFlags flags) +{ + GVfsBackendNds *op_backend = G_VFS_BACKEND_NDS (backend); + + update_cache (op_backend,filename); + + run_enumerate (op_backend, job, filename, matcher); + return TRUE; +} + + +static void +g_vfs_backend_nds_class_init (GVfsBackendNdsClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass); + + gobject_class->finalize = g_vfs_backend_nds_finalize; + + backend_class->mount = do_mount; + backend_class->try_mount = try_mount; + backend_class->query_info = do_query_info; + backend_class->try_query_info = try_query_info; + backend_class->enumerate = do_enumerate; + backend_class->try_enumerate = try_enumerate; + backend_class->try_mount_mountable = try_mount_mountable; + backend_class->mount_mountable = do_mount_mountable; +} + +void +g_vfs_nds_daemon_init (void) +{ + g_set_application_name (_("Displaying Embedded Objects")); +} diff --git a/daemon/gvfsbackendnds.h b/daemon/gvfsbackendnds.h new file mode 100644 index 0000000..0b48ad6 --- /dev/null +++ b/daemon/gvfsbackendnds.h @@ -0,0 +1,53 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#ifndef __G_VFS_BACKEND_NDS_H__ +#define __G_VFS_BACKEND_NDS_H__ + +#include +#include + +G_BEGIN_DECLS + +#define G_VFS_TYPE_BACKEND_NDS (g_vfs_backend_nds_get_type ()) +#define G_VFS_BACKEND_NDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNds)) +#define G_VFS_BACKEND_NDS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNdsClass)) +#define G_VFS_IS_BACKEND_NDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_NDS)) +#define G_VFS_IS_BACKEND_NDS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_NDS)) +#define G_VFS_BACKEND_NDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_NDS, GVfsBackendNdsClass)) + +typedef struct _GVfsBackendNds GVfsBackendNds; +typedef struct _GVfsBackendNdsClass GVfsBackendNdsClass; + +struct _GVfsBackendNdsClass +{ + GVfsBackendClass parent_class; +}; + +GType g_vfs_backend_nds_get_type (void) G_GNUC_CONST; + +#define BACKEND_SETUP_FUNC g_vfs_nds_daemon_init +void g_vfs_nds_daemon_init (void); + +G_END_DECLS + +#endif /* __G_VFS_BACKEND_NDS_H__ */ diff --git a/daemon/meson.build b/daemon/meson.build index dffeef3..dabdcb2 100644 --- a/daemon/meson.build +++ b/daemon/meson.build @@ -530,6 +530,22 @@ if enable_afp mounts += ['afp-browse'] endif +if enable_nds + sources = daemon_main_sources + files('gvfsbackendnds.c') + + deps = [libdl_dep] + + cflags = [ + '-DBACKEND_HEADER=gvfsbackendnds.h', + '-DDEFAULT_BACKEND_TYPE=nds', + '-DBACKEND_TYPES="nds", G_VFS_TYPE_BACKEND_NDS,', + '-DMAX_JOB_THREADS=1', + ] + + programs += {'gvfsd-nds': {'sources': sources, 'dependencies': deps, 'c_args': cflags}} + mounts += ['nds'] +endif + if enable_nfs cflags = [ '-DBACKEND_HEADER=gvfsbackendnfs.h', diff --git a/daemon/nds.mount.in b/daemon/nds.mount.in new file mode 100644 index 0000000..1068344 --- /dev/null +++ b/daemon/nds.mount.in @@ -0,0 +1,5 @@ +[Mount] +Type=nds +Exec=@libexecdir@/gvfsd-nds +AutoMount=false +Scheme=nds diff --git a/meson.build b/meson.build index d844335..34d754f 100644 --- a/meson.build +++ b/meson.build @@ -448,6 +448,12 @@ config_h.set('HAVE_LIBMTP', enable_mtp) # *** AFP backend *** enable_afp = get_option('afp') +# *** NDS backend *** +enable_nds = get_option('nds') +if enable_nds + libdl_dep = cc.find_library('dl') +endif + # *** NFS backend *** enable_nfs = get_option('nfs') if enable_nfs @@ -507,6 +513,7 @@ summary({ 'gphoto2': enable_gphoto2, 'http': enable_http, 'mtp': enable_mtp, + 'nds': enable_nds, 'nfs': enable_nfs, 'sftp': enable_sftp, 'smb': enable_samba, diff --git a/meson_options.txt b/meson_options.txt index 32f10d4..dced004 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,6 +12,7 @@ option('google', type: 'boolean', value: true, description: 'build with google b option('gphoto2', type: 'boolean', value: true, description: 'build with gphoto2 backend and volume monitor') option('http', type: 'boolean', value: true, description: 'build with http/dav backends') option('mtp', type: 'boolean', value: true, description: 'build with mtp backend and volume monitor') +option('nds', type: 'boolean', value: true, description: 'build with nds backend') option('nfs', type: 'boolean', value: true, description: 'build with nfs backend') option('sftp', type: 'boolean', value: true, description: 'build with sftp backend') option('smb', type: 'boolean', value: true, description: 'build with smb backends')