/*
 * Copyright(C) 2002 Simon Howard
 *
 * 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.1 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
 */

#include <netdb.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>

#include <libgnomevfs/gnome-vfs-cancellation.h>
#include <libgnomevfs/gnome-vfs-inet-connection.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-mime-sniff-buffer.h>
#include <libgnomevfs/gnome-vfs-mime-utils.h>
#include <libgnomevfs/gnome-vfs-module-callback-module-api.h>
#include <libgnomevfs/gnome-vfs-module.h>
#include <libgnomevfs/gnome-vfs-socket-buffer.h>
#include <libgnomevfs/gnome-vfs-socket.h>
#include <libgnomevfs/gnome-vfs-ssl.h>
#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
#include <libgnomevfs/gnome-vfs-utils.h>

#include "ppsocket.h"
#include "rfsv.h"
#include "rfsv32.h"
#include "rfsvfactory.h"

static int my_uid;
static GMutex *rfsv_mutex;
static rfsv *rfsv;

// convert a vfs uri to a filename
// eg /c/blah -> c:\blah

static gchar *uri_to_filename(GnomeVFSURI *uri)
{
	const gchar *location = gnome_vfs_uri_get_path(uri);

	gchar *loc2;
	gchar *p;

	// be strict: only the pathname

	if (gnome_vfs_uri_get_user_name(uri) != NULL
	 || gnome_vfs_uri_get_password(uri) != NULL
	 || gnome_vfs_uri_get_host_name(uri) != NULL
	 || gnome_vfs_uri_get_host_port(uri) != 0)
		return NULL;

	// if this is /, special case
	
	if (!strcmp(location, "/"))
		return "\\";

	// must start with /[a-z]/
	
	if (location[0] != '/' || (location[2] != '/' && location[2] != 0)
	 || !isalpha(location[1]))
		return NULL;

	loc2 = gnome_vfs_unescape_string(location, "/");

	// /c/ -> c:/
	loc2[0] = loc2[1];
	loc2[1] = ':';
	
	// / -> \

	for (p=loc2; *p; ++p)
		if (*p == '/')
			*p = '\\';

	return loc2;
}

static inline GnomeVFSResult plp_to_vfs_error(int error)
{
	switch (error) {
		case rfsv::E_PSI_GEN_NONE:
			return GNOME_VFS_OK;
		case rfsv::E_PSI_FILE_EXIST:
			return GNOME_VFS_ERROR_FILE_EXISTS;
		case rfsv::E_PSI_FILE_NXIST:
		case rfsv::E_PSI_FILE_DEVICE:
		case rfsv::E_PSI_FILE_DIR:
		case rfsv::E_PSI_FILE_NOTREADY:
		case rfsv::E_PSI_FILE_UNKNOWN:
			return GNOME_VFS_ERROR_NOT_FOUND;
		case rfsv::E_PSI_FILE_ACCESS:
		case rfsv::E_PSI_FILE_LOCKED:
		case rfsv::E_PSI_GEN_INUSE:
			return GNOME_VFS_ERROR_ACCESS_DENIED;
		case rfsv::E_PSI_FILE_RDONLY:
		case rfsv::E_PSI_FILE_PROTECT:
			return GNOME_VFS_ERROR_READ_ONLY;
		case rfsv::E_PSI_FILE_EOF:
			return GNOME_VFS_ERROR_EOF;
		case rfsv::E_PSI_FILE_NAME:
		case rfsv::E_PSI_FILE_VOLUME:
			return GNOME_VFS_ERROR_INVALID_URI;

		default:
			fprintf(stderr, "vfs/psion: plp error %i\n", error);
			return GNOME_VFS_ERROR_GENERIC;
	}
}

static inline int vfs_to_plp_mode(GnomeVFSOpenMode mode)
{
	int result = 0;

	if (mode & GNOME_VFS_OPEN_READ) {
		if (mode & GNOME_VFS_OPEN_WRITE)
			return rfsv::PSI_O_RDWR;
		else
			return rfsv::PSI_O_RDONLY;
	} else {
		return rfsv::PSI_O_WRONLY;
	}
}
 
static GnomeVFSResult psion_open(GnomeVFSMethod *method,
                                 GnomeVFSMethodHandle **method_handle_return,
                                 GnomeVFSURI *uri,
                                 GnomeVFSOpenMode mode,
                                 GnomeVFSContext *context)
{
	GnomeVFSResult result;
	int plp_mode;
	char *location;
	u_int32_t handle;

	//printf("psion_open\n");
	
	location = uri_to_filename(uri);

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_NOT_FOUND;

	plp_mode = vfs_to_plp_mode(mode);

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->fopen(rfsv->opMode(plp_mode), 
					      location,
					      handle));
	g_mutex_unlock(rfsv_mutex);

	free(location);

	if (!result) {
		*((u_int32_t *)method_handle_return) = handle;
	}

	return result;
}

static GnomeVFSResult psion_create(GnomeVFSMethod *method,
                                   GnomeVFSMethodHandle **method_handle_return,
                                   GnomeVFSURI *uri,
                                   GnomeVFSOpenMode mode,
                                   gboolean exclusive,
                                   guint perm,
                                   GnomeVFSContext *context)
{
	GnomeVFSResult result;
	int plp_mode;
	char *location;
	u_int32_t handle;
	
	//printf("psion_create\n");

	location = uri_to_filename(uri);

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_NOT_FOUND;
	
	plp_mode = vfs_to_plp_mode(mode);

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->fcreatefile(rfsv->opMode(plp_mode), 
						    location,
						    handle));
	g_mutex_unlock(rfsv_mutex);

	free(location);

	if (!result) {
		*((u_int32_t *)method_handle_return) = handle;
	}

	return result;
}

static GnomeVFSResult psion_close(GnomeVFSMethod *method,
                                  GnomeVFSMethodHandle *method_handle,
                                  GnomeVFSContext *context)
{
	GnomeVFSResult result;
	u_int32_t handle = (u_int32_t) method_handle;

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->fclose(handle));
	g_mutex_unlock(rfsv_mutex);

	return result;
}

static GnomeVFSResult psion_read(GnomeVFSMethod *method,
                                 GnomeVFSMethodHandle *method_handle,
                                 gpointer buffer,
                                 GnomeVFSFileSize num_bytes,
                                 GnomeVFSFileSize *bytes_read_return,
                                 GnomeVFSContext *context)
{
	GnomeVFSResult result;
	u_int32_t handle;
	u_int32_t bytes_read;
	
	handle = (u_int32_t) method_handle;

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->fread(handle,
					      (unsigned char *) buffer,
					      num_bytes, bytes_read));
	g_mutex_unlock(rfsv_mutex);

	//printf("psion_read: read %i/%i bytes\n", bytes_read, num_bytes);

	if (!result) {
		*bytes_read_return = bytes_read;
	}

	return result;
}

static GnomeVFSResult psion_write(GnomeVFSMethod *method,
                                  GnomeVFSMethodHandle *method_handle,
                                  gconstpointer buffer,
                                  GnomeVFSFileSize num_bytes,
                                  GnomeVFSFileSize *bytes_written_return,
				  GnomeVFSContext *context)
{
	GnomeVFSResult result;
	u_int32_t handle;
	u_int32_t bytes_wrote;
	
	handle = (u_int32_t) method_handle;

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->fwrite(handle,
					       (const unsigned char *) buffer, 
					       num_bytes, bytes_wrote));
	g_mutex_unlock(rfsv_mutex);

	if (!result) {
		*bytes_written_return = bytes_wrote;
	}

	return result;
}

#define fixup_dir(dir) 						\
	if ((dir)[strlen(dir)-1] != '\\') {			\
		char *loc2 = (dir);				\
		dir = g_strdup_printf("%s\\", (dir));		\
		free(loc2);					\
	}

struct dir_handle {
	gboolean root;             // is this the special root device listing?
	PlpDir *dir;
	u_int32_t root_devlist;
	int n;
};

static GnomeVFSResult psion_open_dir(GnomeVFSMethod *method,
                                     GnomeVFSMethodHandle **method_handle,
                                     GnomeVFSURI *uri,
                                     GnomeVFSFileInfoOptions options,
                                     GnomeVFSContext *context)
{
	GnomeVFSResult result;
	struct dir_handle *dc;
	gchar *location;

	//printf("open_dir\n");

	location = uri_to_filename(uri);

	if (!location)
		return GNOME_VFS_ERROR_NOT_FOUND;

	dc = (struct dir_handle *) malloc(sizeof(struct dir_handle));

	if (!strcmp(location, "\\")) {

		// special root folder: list of drives

		u_int32_t devlist;

		dc->root = 1;
		dc->n = -1;

		g_mutex_lock(rfsv_mutex);
		result = plp_to_vfs_error(rfsv->devlist(devlist));
		g_mutex_unlock(rfsv_mutex);

		if (!result)
			dc->root_devlist = devlist;
	} else {
		// open a directory

		dc->root = 0;
		dc->dir = new PlpDir;
		dc->n = 0;

		// make sure we put \ on the end of directories
		
		fixup_dir(location);
	
		//printf("open_dir: %s\n", location);
	
		g_mutex_lock(rfsv_mutex);
		result = plp_to_vfs_error(rfsv->dir(location, *dc->dir));
		g_mutex_unlock(rfsv_mutex);

		free(location);

		//printf("open_dir: %i entries\n", dc->dir->size());
	}

	// return dc or free it back depending on whether we succeeded

	if (result)
		free(dc);         // failed
	else
		*((struct dir_handle **) method_handle) = dc;
			
	return result;
}

static GnomeVFSResult psion_close_dir(GnomeVFSMethod *method,
                                      GnomeVFSMethodHandle *method_handle,
                                      GnomeVFSContext *context)
{
	//printf("close_dir\n");
	struct dir_handle *dc = (struct dir_handle *) method_handle;

	if (!dc->root)
		delete dc->dir;

	free(dc);

	return GNOME_VFS_OK;
}

static GnomeVFSResult get_drive_attributes(GnomeVFSFileInfo *file_info, int drive)
{
	PlpDrive driveinfo;
	int result;

	g_mutex_lock(rfsv_mutex);
	result = rfsv->devinfo('a' + drive, driveinfo);
	g_mutex_unlock(rfsv_mutex);

	//printf("drive attributes: %i\n", result);

	// error
	
	if (result)
		return plp_to_vfs_error(result);

	(int) file_info->valid_fields
		= GNOME_VFS_FILE_INFO_FIELDS_TYPE
		| GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS
		| GNOME_VFS_FILE_INFO_FIELDS_FLAGS
		| GNOME_VFS_FILE_INFO_FIELDS_SIZE;

	file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
	(int) file_info->permissions = 01555;

	if (~driveinfo.getDriveAttribute() & 0x2)
		(int) file_info->permissions |= 0777;
	
	file_info->flags = GNOME_VFS_FILE_FLAGS_NONE;
	file_info->size = driveinfo.getSize();
	file_info->mime_type = g_strdup("x-directory/normal");

	return GNOME_VFS_OK;
}

static void get_file_attributes(GnomeVFSFileInfo *file_info,
			       PlpDirent *de)
{
	int attr;

	(int) file_info->valid_fields
		= GNOME_VFS_FILE_INFO_FIELDS_TYPE
		| GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS
		| GNOME_VFS_FILE_INFO_FIELDS_FLAGS
		| GNOME_VFS_FILE_INFO_FIELDS_SIZE
		| GNOME_VFS_FILE_INFO_FIELDS_MTIME;

	attr = de->getAttr();
	file_info->name = g_strdup(de->getName());
	file_info->size = de->getSize();
	file_info->mtime = de->getPsiTime().getTime();
	(int) file_info->permissions = 0444;

	if (~attr & rfsv::PSI_A_RDONLY)
		(int) file_info->permissions |= 0222;

	if (attr & rfsv::PSI_A_DIR) {
		(int) file_info->permissions |= 0111;
		file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
		file_info->mime_type = g_strdup("x-directory/normal");
	} else {
		file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
		file_info->mime_type = (char *)
			gnome_vfs_mime_type_from_name_or_default
			((const char *)file_info->name, 
			 (const char *)GNOME_VFS_MIME_TYPE_UNKNOWN);
		file_info->mime_type = g_strdup(file_info->mime_type);
	}

	file_info->uid = my_uid;
}

static GnomeVFSResult psion_read_dir(GnomeVFSMethod *method,
                                     GnomeVFSMethodHandle *method_handle,
                                     GnomeVFSFileInfo *file_info,
                                     GnomeVFSContext *context)
{
	struct dir_handle *dc = (struct dir_handle *) method_handle;
	GnomeVFSResult result = GNOME_VFS_OK;

	//printf("read_dir\n");

	if (dc->root) {
		// jump to the next drive that is set
		// return if no more set

		do {
			if (dc->n >= 26)
				return GNOME_VFS_ERROR_EOF;
			++dc->n;
		} while (!(dc->root_devlist & (1 << dc->n)));


		file_info->name = g_strdup_printf("%c", 'a' + dc->n);
		result = get_drive_attributes(file_info, dc->n);

		//printf("file: %s\n", file_info->name);
	} else {
		//printf("entries: %i/%i\n", dc->n, dc->dir->size());
		
		if (dc->n >= dc->dir->size()) {
			result = GNOME_VFS_ERROR_EOF;
		} else {
			PlpDirent *de;
		
			de = &((*dc->dir)[dc->n]);
		
			++dc->n;

			get_file_attributes(file_info, de);
		}
	}

	return result;
}

static GnomeVFSResult psion_get_file_info(GnomeVFSMethod *method,
                                          GnomeVFSURI *uri,
                                          GnomeVFSFileInfo *file_info,
                                          GnomeVFSFileInfoOptions options,
                                          GnomeVFSContext *context)
{
	GnomeVFSResult result = GNOME_VFS_OK;
	char *location;

	//printf("get_file_info: ");
	//printf("%s, ", gnome_vfs_uri_get_path(uri));
	//printf("%i\n", options);
	
	location = uri_to_filename(uri);

	if (!location)
		return GNOME_VFS_ERROR_NOT_FOUND;
	
	// special case: root, listing of devices
	
	if (!strcmp(location, "\\")) {

		(int) file_info->valid_fields 
			= GNOME_VFS_FILE_INFO_FIELDS_TYPE
			| GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS
			| GNOME_VFS_FILE_INFO_FIELDS_FLAGS
			| GNOME_VFS_FILE_INFO_FIELDS_SIZE
			| GNOME_VFS_FILE_INFO_FIELDS_MTIME;

		file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
		(int) file_info->permissions = S_IRUSR|S_IRGRP|S_IROTH;
		file_info->flags = GNOME_VFS_FILE_FLAGS_NONE;
		file_info->size = 0;
		file_info->mtime = 0;
		file_info->mime_type = g_strdup("x-directory/normal");
	} else {
		if (strlen(location) == 2) {
			// drive attributes
	
			result = get_drive_attributes(file_info, 
					tolower(location[0]) - 'a');
		} else {
			PlpDirent de;
			
			// file attributes
	
			g_mutex_lock(rfsv_mutex);
			result = plp_to_vfs_error(rfsv->fgeteattr(location, de));
			g_mutex_unlock(rfsv_mutex);
	
			if (!result) {
				get_file_attributes(file_info, &de);
			}
		}
	
		free(location);
	}

	if (!result) {
		// extract the base filename and put it in the 
		// info struct (provided we are returning ok)

		char *filename = strrchr(gnome_vfs_uri_get_path(uri), '/');
		file_info->name = gnome_vfs_unescape_string(filename+1, "/");
	} else {
		//printf("error: %s\n", gnome_vfs_result_to_string(result));
	}

	return result;
}

static GnomeVFSResult psion_get_file_info_from_handle
	          (GnomeVFSMethod *method,
                   GnomeVFSMethodHandle *method_handle,
                   GnomeVFSFileInfo *file_info,
                   GnomeVFSFileInfoOptions options,
                   GnomeVFSContext *context)
{
	//printf("get_file_info2\n");
}

static gboolean psion_is_local(GnomeVFSMethod *method,
                               const GnomeVFSURI *uri)
{
	//printf("is_local: %s\n", gnome_vfs_uri_get_path(uri));
	return 0;
}

static GnomeVFSResult psion_mkdir(GnomeVFSMethod *method,
                                  GnomeVFSURI *uri,
                                  guint perm,
                                  GnomeVFSContext *context)
{
	char *location = uri_to_filename(uri);
	GnomeVFSResult result;

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_ACCESS_DENIED;

	// must have a \ on the end

	fixup_dir(location);

	//printf("mkdir: %s\n", location);

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->mkdir(location));
	g_mutex_unlock(rfsv_mutex);

	free(location);

	return result;
}

static GnomeVFSResult psion_rmdir(GnomeVFSMethod *method,
                                  GnomeVFSURI *uri,
                                  GnomeVFSContext *context)
{
	char *location = uri_to_filename(uri);
	GnomeVFSResult result;

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_ACCESS_DENIED;
	
	// add \ on the ned if neccesary

	fixup_dir(location);

	//printf("rmdir: %s\n", location);

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->rmdir(location));
	g_mutex_unlock(rfsv_mutex);

	free(location);

	return result;
}

static GnomeVFSResult psion_move(GnomeVFSMethod *method,
                                 GnomeVFSURI *old_uri,
                                 GnomeVFSURI *new_uri,
                                 gboolean force_replace,
                                 GnomeVFSContext *context)
{
	char *old_loc, *new_loc;
	GnomeVFSResult result;
	
	//printf("move\n");

	// convert to psion filenames:

	old_loc = uri_to_filename(old_uri);

	if (!old_loc || !strcmp(old_loc, "\\"))
		return GNOME_VFS_ERROR_ACCESS_DENIED;

	new_loc = uri_to_filename(new_uri);

	if (!new_loc || !strcmp(new_loc, "\\")) {
		free(old_loc);
		return GNOME_VFS_ERROR_ACCESS_DENIED;
	}

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->rename(old_loc, new_loc));
	g_mutex_unlock(rfsv_mutex);

	free(old_loc);
	free(new_loc);

	return result;
}

static GnomeVFSResult psion_unlink(GnomeVFSMethod *method,
                                   GnomeVFSURI *uri,
                                   GnomeVFSContext *context)
{
	GnomeVFSResult result;
	char *location;
	
	//printf("unlink\n");

	location = uri_to_filename(uri);

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_ACCESS_DENIED;

	g_mutex_lock(rfsv_mutex);
	result = plp_to_vfs_error(rfsv->remove(location));
	g_mutex_unlock(rfsv_mutex);

	return result;
}

static GnomeVFSResult psion_same_fs(GnomeVFSMethod *method,
                                         GnomeVFSURI *a,
                                         GnomeVFSURI *b,
                                         gboolean *same_fs_return,
                                         GnomeVFSContext *context)
{
	//printf("same_fs\n");

	*same_fs_return = 1;

	return GNOME_VFS_OK;
}

static GnomeVFSResult psion_set_file_info(GnomeVFSMethod *method,
                                          GnomeVFSURI *a,
                                          const GnomeVFSFileInfo *info,
                                          GnomeVFSSetFileInfoMask mask,
                                          GnomeVFSContext *context)
{
	GnomeVFSResult result = GNOME_VFS_OK;
	char *location = uri_to_filename(a);

	if (!location || !strcmp(location, "\\"))
		return GNOME_VFS_ERROR_ACCESS_DENIED;

	//printf("set_file_info\n");

	if (mask & GNOME_VFS_SET_FILE_INFO_NAME) {
		char *dir, *newloc;

		// extract the directory name
		
		dir = strdup(location);
		*strrchr(dir, '\\') = '\0';

		// append new name to the directory name 

		newloc = g_strdup_printf("%s\\%s", dir, info->name);
		
		// do rename

		//printf("%s -> %s\n", location, newloc);
		g_mutex_lock(rfsv_mutex);
		result = plp_to_vfs_error(rfsv->rename(location, newloc));
		g_mutex_unlock(rfsv_mutex);

		free(dir);
		free(newloc);
	}
	if (!result && mask & GNOME_VFS_SET_FILE_INFO_PERMISSIONS) {
		result = GNOME_VFS_ERROR_GENERIC;
	}
	if (!result && mask & GNOME_VFS_SET_FILE_INFO_OWNER) {
		result = GNOME_VFS_ERROR_NOT_SUPPORTED;
	}
	if (!result && mask & GNOME_VFS_SET_FILE_INFO_TIME) {
		result = GNOME_VFS_ERROR_GENERIC;
	}

	free(location);

	return result;
}

static const char *host = "127.0.0.1";
static ppsocket *ppskt;
static rfsvfactory *rfsv_factory;

// connect needs to be done in c++ land

static int cpp_connect()
{
	int portnum = 7501;
	struct servent *se = getservbyname("psion", "tcp");
	endservent();
	
	my_uid = getuid();

	if (se)
		portnum = ntohs(se->s_port);

	ppskt = new ppsocket();

	if (!ppskt->connect(host, portnum)) {
		fprintf(stderr, "vfs/psion: could not connect to ncpd\n");
	} else {

		rfsv_factory = new rfsvfactory(ppskt);
		rfsv = rfsv_factory->create(false);

		if (rfsv) {
			//printf("connected to ncpd\n");

			rfsv_mutex = g_mutex_new();

			if (rfsv_mutex)
				return 1;

			delete rfsv;
		}

		delete rfsv_factory;
	}

	delete ppskt;

	fprintf(stderr, "vfs/psion: failed to connect to ncpd\n");
	return 0;
}

void cpp_disconnect() 
{
	g_mutex_free(rfsv_mutex);
	delete rfsv;
	delete rfsv_factory;
	delete ppskt;
}

extern "C" {

static GnomeVFSMethod methods = {
	sizeof(GnomeVFSMethod),
	psion_open,
	psion_create,
	psion_close,
	psion_read,
	psion_write,
	NULL,	/* seek */
	NULL, 	/* tell */
	NULL,	/* truncate */
	psion_open_dir,
	psion_close_dir,
	psion_read_dir,
	psion_get_file_info,
	psion_get_file_info_from_handle,
	psion_is_local,
	psion_mkdir,
	psion_rmdir,
	psion_move,
	psion_unlink,
	psion_same_fs,
	psion_set_file_info,
	NULL, 	/* truncate */
	NULL, 	/* find_directory */
	NULL, 	/* create_symbolic_link */
};

GnomeVFSMethod *vfs_module_init (const char *method_name, const char *args)
{
	if (cpp_connect())
		return &methods;
	else
		return NULL;
}

void vfs_module_shutdown (GnomeVFSMethod *method)
{
	cpp_disconnect();
}

}
