Blob Blame History Raw
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */

/* desktop-method.c

   Copyright (C) 2001 Red Hat, Inc.

   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome 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
   Library 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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

/* URI scheme for remapping directories under magic URIs, used
 * for the magic desktop file directories such as start-here.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <glib.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#include <libgnomevfs/gnome-vfs-mime.h>

#include <libgnomevfs/gnome-vfs-module.h>
#include <libgnomevfs/gnome-vfs-method.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-module-shared.h>
#include <libgnomevfs/gnome-vfs-monitor-private.h>

/* FIXME Maybe when chaining to file:, we should call the gnome-vfs wrapper
 * functions, instead of the file: methods directly.
 */

#define N_ELEMENTS(arr)		(sizeof (arr) / sizeof ((arr)[0]))

static GnomeVFSURI* desktop_uri_to_file_uri (GnomeVFSURI *desktop_uri);

static GnomeVFSResult open_merged_directory (GnomeVFSMethod *method,
					     GnomeVFSMethodHandle **method_handle,
					     GnomeVFSURI *uri,
					     GnomeVFSFileInfoOptions options,
					     GnomeVFSContext *context);

static char*         create_file_uri_in_dir (const char  *dir,
					     const char  *filename);

static GnomeVFSMethod *parent_method = NULL;

static GnomeVFSResult
do_open (GnomeVFSMethod *method,
	 GnomeVFSMethodHandle **method_handle,
	 GnomeVFSURI *uri,
	 GnomeVFSOpenMode mode,
	 GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->open) (parent_method,
					  method_handle,
					  file_uri,
					  mode,
					  context);
	gnome_vfs_uri_unref (file_uri);

	return result;
}

static GnomeVFSResult
do_create (GnomeVFSMethod *method,
	   GnomeVFSMethodHandle **method_handle,
	   GnomeVFSURI *uri,
	   GnomeVFSOpenMode mode,
	   gboolean exclusive,
	   guint perm,
	   GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->create) (parent_method,
					    method_handle,
					    file_uri,
					    mode,
					    exclusive,
					    perm,
					    context);
	gnome_vfs_uri_unref (file_uri);

	return result;
}

static GnomeVFSResult
do_close (GnomeVFSMethod *method,
	  GnomeVFSMethodHandle *method_handle,
	  GnomeVFSContext *context)
{
	GnomeVFSResult result;
	
	result = (* parent_method->close) (parent_method,
					   method_handle,
					   context);

	return result;
}

static GnomeVFSResult
do_read (GnomeVFSMethod *method,
	 GnomeVFSMethodHandle *method_handle,
	 gpointer buffer,
	 GnomeVFSFileSize num_bytes,
	 GnomeVFSFileSize *bytes_read,
	 GnomeVFSContext *context)
{
	GnomeVFSResult result;
	
	result = (* parent_method->read) (parent_method,
					  method_handle,
					  buffer, num_bytes,
					  bytes_read,
					  context);

	return result;
}

static GnomeVFSResult
do_write (GnomeVFSMethod *method,
	  GnomeVFSMethodHandle *method_handle,
	  gconstpointer buffer,
	  GnomeVFSFileSize num_bytes,
	  GnomeVFSFileSize *bytes_written,
	  GnomeVFSContext *context)
{
	GnomeVFSResult result;
	
	result = (* parent_method->write) (parent_method,
					   method_handle,
					   buffer, num_bytes,
					   bytes_written,
					   context);

	return result;
}


static GnomeVFSResult
do_seek (GnomeVFSMethod *method,
	 GnomeVFSMethodHandle *method_handle,
	 GnomeVFSSeekPosition whence,
	 GnomeVFSFileOffset offset,
	 GnomeVFSContext *context)
{
	GnomeVFSResult result;
	
	result = (* parent_method->seek) (parent_method,
					  method_handle,
					  whence, offset,
					  context);

	return result;
}

static GnomeVFSResult
do_tell (GnomeVFSMethod *method,
	 GnomeVFSMethodHandle *method_handle,
	 GnomeVFSFileOffset *offset_return)
{
	GnomeVFSResult result;
	
	result = (* parent_method->tell) (parent_method,
					  method_handle,
					  offset_return);

	return result;
}


static GnomeVFSResult
do_truncate_handle (GnomeVFSMethod *method,
		    GnomeVFSMethodHandle *method_handle,
		    GnomeVFSFileSize where,
		    GnomeVFSContext *context)
{
	GnomeVFSResult result;
	
	result = (* parent_method->truncate_handle) (parent_method,
						     method_handle,
						     where,
						     context);

	return result;
}

static GnomeVFSResult
do_truncate (GnomeVFSMethod *method,
	     GnomeVFSURI *uri,
	     GnomeVFSFileSize where,
	     GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->truncate) (parent_method,
					      file_uri,
					      where,
					      context);

	gnome_vfs_uri_unref (file_uri);

	return result;
}

typedef struct _DirHandle DirHandle;

struct _DirHandle
{
	GSList *next;
	GSList *handles;
};

static GnomeVFSResult
do_open_directory (GnomeVFSMethod *method,
		   GnomeVFSMethodHandle **method_handle,
		   GnomeVFSURI *uri,
		   GnomeVFSFileInfoOptions options,
		   GnomeVFSContext *context)
{
	return open_merged_directory (method, method_handle,
				      uri, options, context);
}

static GnomeVFSResult
do_close_directory (GnomeVFSMethod *method,
		    GnomeVFSMethodHandle *method_handle,
		    GnomeVFSContext *context)
{
	GnomeVFSResult result;
	GSList *tmp;
	DirHandle *dh;

	dh = (DirHandle*) method_handle;

	result = GNOME_VFS_OK;
	tmp = dh->handles;
	while (tmp != NULL) {
		GnomeVFSResult this_result;
		
		this_result = (* parent_method->close_directory) (parent_method,
								  tmp->data,
								  context);

		if (this_result != GNOME_VFS_OK)
			result = this_result;
		
		tmp = tmp->next;
	}

	g_slist_free (dh->handles);
	g_free (dh);
	
	return result;
}

static GnomeVFSResult
do_read_directory (GnomeVFSMethod *method,
		   GnomeVFSMethodHandle *method_handle,
		   GnomeVFSFileInfo *file_info,
		   GnomeVFSContext *context)
{
	GnomeVFSResult result;
	GnomeVFSMethodHandle *parent_handle;
	DirHandle *dh;

	dh = (DirHandle*) method_handle;

	if (dh->next == NULL) {
		return GNOME_VFS_ERROR_EOF;
	}

 next:
	parent_handle = dh->next->data;
	
	result = (* parent_method->read_directory) (parent_method,
						    parent_handle,
						    file_info,
						    context);

	if (result != GNOME_VFS_OK) {
		dh->next = dh->next->next;
		if (dh->next)
			goto next;
		else
			return result;
	} else {
		return GNOME_VFS_OK;
	}
}


static void
set_directory_mime_type (GnomeVFSFileInfo *file_info)
{
	g_free (file_info->mime_type);

	file_info->mime_type = g_strdup ("x-directory/vfolder-desktop");
	file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
}

static GnomeVFSResult
do_get_file_info (GnomeVFSMethod *method,
		  GnomeVFSURI *uri,
		  GnomeVFSFileInfo *file_info,
		  GnomeVFSFileInfoOptions options,
		  GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->get_file_info) (parent_method,
						   file_uri,
						   file_info,
						   options,
						   context);

	if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
		set_directory_mime_type (file_info);
	
	gnome_vfs_uri_unref (file_uri);

	return result;
}

static GnomeVFSResult
do_get_file_info_from_handle (GnomeVFSMethod *method,
			      GnomeVFSMethodHandle *method_handle,
			      GnomeVFSFileInfo *file_info,
			      GnomeVFSFileInfoOptions options,
			      GnomeVFSContext *context)
{
	GnomeVFSResult result;

	result = (* parent_method->get_file_info_from_handle) (parent_method,
							       method_handle,
							       file_info,
							       options,
							       context);

	if (file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY)
		set_directory_mime_type (file_info);

	return result;
}


static gboolean
do_is_local (GnomeVFSMethod *method,
	     const GnomeVFSURI *uri)
{
	return TRUE;
}


static GnomeVFSResult
do_make_directory (GnomeVFSMethod *method,
		   GnomeVFSURI *uri,
		   guint perm,
		   GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->make_directory) (parent_method,
						    file_uri,
						    perm,
						    context);
	
	gnome_vfs_uri_unref (file_uri);

	return result;
}

static GnomeVFSResult
do_remove_directory (GnomeVFSMethod *method,
		     GnomeVFSURI *uri,
		     GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->remove_directory) (parent_method,
						      file_uri,
						      context);
	
	gnome_vfs_uri_unref (file_uri);

	return result;
}

static GnomeVFSResult
do_find_directory (GnomeVFSMethod *method,
		   GnomeVFSURI *near_uri,
		   GnomeVFSFindDirectoryKind kind,
		   GnomeVFSURI **result_uri,
		   gboolean create_if_needed,
		   gboolean find_if_needed,
		   guint permissions,
		   GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSURI *file_result_uri;
	GnomeVFSResult result;

	file_result_uri = NULL;
	file_uri = desktop_uri_to_file_uri (near_uri);
	result = (* parent_method->find_directory) (parent_method,
						    file_uri,
						    kind,
						    &file_result_uri,
						    create_if_needed,
						    find_if_needed,
						    permissions,
						    context);
	
	gnome_vfs_uri_unref (file_uri);

	if (result_uri)
		*result_uri = file_result_uri;
	
	if (file_result_uri == NULL)
		result = GNOME_VFS_ERROR_NOT_FOUND;
		
	return result;
}

static GnomeVFSResult
do_move (GnomeVFSMethod *method,
	 GnomeVFSURI *old_uri,
	 GnomeVFSURI *new_uri,
	 gboolean force_replace,
	 GnomeVFSContext *context)
{
	GnomeVFSURI *old_file_uri;
	GnomeVFSURI *new_file_uri;
	GnomeVFSResult result;

	old_file_uri = desktop_uri_to_file_uri (old_uri);
	new_file_uri = desktop_uri_to_file_uri (new_uri);

	result = (* parent_method->move) (parent_method,
					  old_file_uri,
					  new_file_uri,
					  force_replace,
					  context);
	gnome_vfs_uri_unref (old_file_uri);
	gnome_vfs_uri_unref (new_file_uri);

	return result;
}

static GnomeVFSResult
do_unlink (GnomeVFSMethod *method,
	   GnomeVFSURI *uri,
	   GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->unlink) (parent_method,
					    file_uri,
					    context);
	
	gnome_vfs_uri_unref (file_uri);

	return result;	
}

static GnomeVFSResult
do_create_symbolic_link (GnomeVFSMethod *method,
			 GnomeVFSURI *uri,
			 const char *target_reference,
			 GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->create_symbolic_link) (parent_method,
							  file_uri,
							  target_reference,
							  context);
	
	gnome_vfs_uri_unref (file_uri);

	return result;	
}

static GnomeVFSResult
do_check_same_fs (GnomeVFSMethod *method,
		  GnomeVFSURI *source_uri,
		  GnomeVFSURI *target_uri,
		  gboolean *same_fs_return,
		  GnomeVFSContext *context)
{
	GnomeVFSURI *source_file_uri;
	GnomeVFSURI *target_file_uri;
	GnomeVFSResult result;

	source_file_uri = desktop_uri_to_file_uri (source_uri);
	target_file_uri = desktop_uri_to_file_uri (target_uri);

	result = (* parent_method->check_same_fs) (parent_method,
						   source_file_uri,
						   target_file_uri,
						   same_fs_return,
						   context);
	gnome_vfs_uri_unref (source_file_uri);
	gnome_vfs_uri_unref (target_file_uri);

	return result;	
}

static GnomeVFSResult
do_set_file_info (GnomeVFSMethod *method,
		  GnomeVFSURI *uri,
		  const GnomeVFSFileInfo *info,
		  GnomeVFSSetFileInfoMask mask,
		  GnomeVFSContext *context)
{
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	file_uri = desktop_uri_to_file_uri (uri);
	result = (* parent_method->set_file_info) (parent_method,
						   file_uri,
						   info,
						   mask,
						   context);
	
	gnome_vfs_uri_unref (file_uri);

	return result;	
}

typedef struct {
	GnomeVFSMonitorHandle *handle;
	GnomeVFSURI           *desktop_uri;
} DesktopMonitorHandle;

static void 
monitor_notify_cb (GnomeVFSMonitorHandle    *handle,
		   const gchar              *monitor_uri,
		   const gchar              *info_uri,
		   GnomeVFSMonitorEventType  event_type,
		   gpointer                  user_data)
{
	DesktopMonitorHandle *monitor_handle;
	GnomeVFSURI *desktop_info_uri;
	const gchar *uri_diff;
	gint monitor_uri_len;

	monitor_handle = (DesktopMonitorHandle *) user_data;
	desktop_info_uri = NULL;
	monitor_uri_len = strlen (monitor_uri);

	if (info_uri != NULL &&
	    strncmp (info_uri, monitor_uri, monitor_uri_len) == 0) {
		uri_diff = &info_uri [monitor_uri_len];

		if (*uri_diff != '\0') {
			desktop_info_uri = 
				gnome_vfs_uri_append_string (
					monitor_handle->desktop_uri,
					uri_diff);
		} else {
			desktop_info_uri = monitor_handle->desktop_uri;
			gnome_vfs_uri_ref (desktop_info_uri);
		}
	}

	gnome_vfs_monitor_callback ((GnomeVFSMethodHandle *) monitor_handle,
				    desktop_info_uri,
				    event_type);

	gnome_vfs_uri_unref (desktop_info_uri);
}

static GnomeVFSResult
do_monitor_add (GnomeVFSMethod *method,
		GnomeVFSMethodHandle **method_handle_return,
		GnomeVFSURI *uri,
		GnomeVFSMonitorType monitor_type)
{
	DesktopMonitorHandle *monitor_handle;
	GnomeVFSURI *file_uri;
	GnomeVFSResult result;

	monitor_handle = g_new0 (DesktopMonitorHandle, 1);
	monitor_handle->desktop_uri = uri;
	gnome_vfs_uri_ref (uri);

	file_uri = desktop_uri_to_file_uri (uri);
	result = gnome_vfs_monitor_do_add (parent_method,
					   &monitor_handle->handle,
					   file_uri,
					   monitor_type,
					   monitor_notify_cb,
					   monitor_handle);
	gnome_vfs_uri_unref (file_uri);

	if (result != GNOME_VFS_OK) {
		gnome_vfs_uri_unref (monitor_handle->desktop_uri);
		g_free (monitor_handle);
	}

	*method_handle_return = (GnomeVFSMethodHandle *) monitor_handle;

	return result;	
}

static GnomeVFSResult
do_monitor_cancel (GnomeVFSMethod *method,
		   GnomeVFSMethodHandle *method_handle)
{
	DesktopMonitorHandle *monitor_handle;
	GnomeVFSResult result;

	monitor_handle = (DesktopMonitorHandle *) method_handle;

	result = gnome_vfs_monitor_do_cancel (monitor_handle->handle);

	gnome_vfs_uri_unref (monitor_handle->desktop_uri);
	g_free (monitor_handle);

	return result;
}



/* gnome-vfs bureaucracy */

static GnomeVFSMethod method = {
	sizeof (GnomeVFSMethod),
	do_open,
	do_create,
	do_close,
	do_read,
	do_write,
	do_seek,
	do_tell,
	do_truncate_handle,
	do_open_directory,
	do_close_directory,
	do_read_directory,
	do_get_file_info,
	do_get_file_info_from_handle,
	do_is_local,
	do_make_directory,
	do_remove_directory,
	do_move,
	do_unlink,
	do_check_same_fs,
	do_set_file_info,
	do_truncate,
	do_find_directory,
	do_create_symbolic_link,
	do_monitor_add,
	do_monitor_cancel
};


typedef enum
{
	SCHEME_FAVORITES,
	SCHEME_PREFERENCES,
	SCHEME_START_HERE,
	SCHEME_SYSTEM_SETTINGS,
	SCHEME_SERVER_SETTINGS,
	SCHEME_PROGRAMS
} SchemeID;

#define MAX_DIRECTORIES 3
#define DIRECTORIES_INITIALIZER { NULL, NULL, NULL }

typedef struct _SchemeDescription SchemeDescription;

struct _SchemeDescription
{
	SchemeID id;
	
	const char *scheme;

	char *directories[MAX_DIRECTORIES];
};

static SchemeDescription schemes[] = 
{
	{ SCHEME_FAVORITES, "favorites",
	  DIRECTORIES_INITIALIZER },
	{ SCHEME_PREFERENCES, "preferences",
	  DIRECTORIES_INITIALIZER },
	{ SCHEME_START_HERE, "start-here",
	  DIRECTORIES_INITIALIZER },
	{ SCHEME_SYSTEM_SETTINGS, "system-settings",
	  DIRECTORIES_INITIALIZER },
	{ SCHEME_SERVER_SETTINGS, "server-settings",
	  DIRECTORIES_INITIALIZER },
	{ SCHEME_PROGRAMS, "programs",
	  DIRECTORIES_INITIALIZER }
};

GnomeVFSMethod *
vfs_module_init (const char *method_name, 
		 const char *args)
{
	int i;
	
	parent_method = gnome_vfs_method_get ("file");

	if (parent_method == NULL) {
		g_error ("Could not find 'file' method for gnome-vfs");
		return NULL;
	}

	i = 0;
	while (i < N_ELEMENTS (schemes)) {
		switch (schemes[i].id) {
		case SCHEME_FAVORITES:
			schemes[i].directories[0] =
				g_strconcat (g_get_home_dir (),
					     "/.gnome/apps",
					     NULL);
			break;
		case SCHEME_PREFERENCES:
			/* FIXME I think the GNOME 2 control center will move
			 * this, but we don't know where to yet
			 */
			schemes[i].directories[0] =
				g_strconcat (DATADIR, "/control-center/capplets", NULL);
			break;
		case SCHEME_START_HERE:
			schemes[i].directories[0] = g_strconcat (SYSCONFDIR,
								 "/X11/starthere",
								 NULL);
			break;
		case SCHEME_SYSTEM_SETTINGS:
			schemes[i].directories[0] =
				g_strconcat (SYSCONFDIR, "/X11/sysconfig", NULL);
			break;
		case SCHEME_SERVER_SETTINGS:
			schemes[i].directories[0] =
				g_strconcat (SYSCONFDIR, "/X11/serverconfig", NULL);
			break;
		case SCHEME_PROGRAMS:
			schemes[i].directories[0] = g_strconcat (SYSCONFDIR,
								 "/X11/applnk",
								 NULL);
			schemes[i].directories[1] =
				g_strconcat (DATADIR, "gnome/apps", NULL);
			break;
		default:
			g_assert_not_reached ();
			break;
		}

		++i;
	}
	
	return &method;
}

void
vfs_module_shutdown (GnomeVFSMethod *method)
{
	int i;
	
	i = 0;
	while (i < N_ELEMENTS (schemes)) {
		int j;

		j = 0;
		while (j < MAX_DIRECTORIES) {
			g_free (schemes[i].directories[j]);
			schemes[i].directories[j] = NULL;
			++j;
		}
		
		++i;
	}
}



static const SchemeDescription*
get_desc_for_uri (GnomeVFSURI *desktop_uri)
{
	const SchemeDescription *desc;
	int i;
	const char *scheme;
	
	scheme = gnome_vfs_uri_get_scheme (desktop_uri);

	desc = NULL;
	i = 0;
	while (i < N_ELEMENTS (schemes)) {
		if (strcmp (schemes[i].scheme, scheme) == 0) {
			desc = &schemes[i];
			break;
		}
		
		++i;
	}

	return desc;
}

static GnomeVFSURI*
desktop_uri_to_file_uri (GnomeVFSURI *desktop_uri)
{
	const SchemeDescription *desc;
	GnomeVFSURI *new_uri;
	const char *path;
	int i;

	desc = get_desc_for_uri (desktop_uri);

	if (desc == NULL) {
		gnome_vfs_uri_ref (desktop_uri);
		return desktop_uri;
	}

	/* Prepend the base for the desktop URI.
	 * If the SchemeDescription contains > 1 directory, we use the directory
	 * after the first if the given file actually exists there.
	 */
	new_uri = NULL;
	path = gnome_vfs_uri_get_path (desktop_uri);
	i = 0;
	while (desc->directories[i])
		++i;
	do {
		char *s;

		--i;
		
		s = create_file_uri_in_dir (desc->directories[i], path);

		new_uri = gnome_vfs_uri_new (s);

		g_free (s);
		
		if (i == 0 ||
		    gnome_vfs_uri_exists (new_uri)) {
			return new_uri;
		} else {
			gnome_vfs_uri_unref (new_uri);
			new_uri = NULL;
		}
	} while (i > 0);


	g_assert_not_reached ();

	return NULL;
}


static GnomeVFSResult
open_merged_directory (GnomeVFSMethod *method,
		       GnomeVFSMethodHandle **method_handle,
		       GnomeVFSURI *desktop_uri,
		       GnomeVFSFileInfoOptions options,
		       GnomeVFSContext *context)
{
	GnomeVFSResult result;
	DirHandle *dh;
	const SchemeDescription *desc;
	int i;
	gboolean found;
	const char *path;
	
	desc = get_desc_for_uri (desktop_uri);
	
	if (desc == NULL) {
		return GNOME_VFS_ERROR_NOT_FOUND;
	}

	dh = g_new0 (DirHandle, 1);
	
	/* Prepend the base for the desktop URI.
	 * If the SchemeDescription contains > 1 directory, we use the directory
	 * after the first if the given file actually exists there.
	 */
	found = FALSE;
	path = gnome_vfs_uri_get_path (desktop_uri);
	i = 0;
	while (desc->directories[i]) {
		char *s;
		GnomeVFSURI *file_uri;
		GnomeVFSMethodHandle *parent_handle = NULL;
		
		s = create_file_uri_in_dir (desc->directories[i], path);

		file_uri = gnome_vfs_uri_new (s);

		g_free (s);

		result = (* parent_method->open_directory) (parent_method,
							    &parent_handle,
							    file_uri,
							    options,
							    context);

		if (result == GNOME_VFS_OK) {
			found = TRUE;
			dh->handles = g_slist_prepend (dh->handles, parent_handle);
		}

		gnome_vfs_uri_unref (file_uri);

		++i;
	}

	dh->next = dh->handles;

	*method_handle = (GnomeVFSMethodHandle*) dh;
	
	return found ? GNOME_VFS_OK : GNOME_VFS_ERROR_NOT_FOUND;
}


static char*
create_file_uri_in_dir (const char  *dir,
			const char  *filename)
{
	char *dir_uri;
	char *retval;
	
	dir_uri = gnome_vfs_get_uri_from_local_path (dir);

	retval = g_strconcat (dir_uri, "/", filename, NULL);

	g_free (dir_uri);
	
	return retval;
}