File gnome-vfs2-net-usershare.diff of Package gnome-vfs2

Index: modules/Makefile.am
================================================================================
--- modules/Makefile.am
+++ modules/Makefile.am
@@ -140,7 +140,7 @@
 
 ###  `network' method
 
-libnetwork_la_SOURCES = network-method.c
+libnetwork_la_SOURCES = network-method.c shares.c shares.h
 libnetwork_la_LDFLAGS = $(module_flags)
 libnetwork_la_LIBADD = $(MODULES_FILE_LIBS) ../libgnomevfs/libgnomevfs-2.la
 
--- modules/network-method.c
+++ modules/network-method.c
@@ -40,6 +40,8 @@
 
 #include <glib/gi18n-lib.h>
 
+#include "shares.h"
+
 #define PATH_GCONF_GNOME_VFS_SMB "/system/smb"
 #define PATH_GCONF_GNOME_VFS_SMB_WORKGROUP "/system/smb/workgroup"
 #define PATH_GCONF_GNOME_VFS_DNS_SD "/system/dns_sd"
@@ -52,6 +54,7 @@
 	char *icon;
 	char *target_uri;
         char *filename;
+	char *desktop_item_type;
 } NetworkLink;
 
 typedef struct {
@@ -84,6 +87,7 @@
 
 
 static GList *current_dns_sd_domains = NULL;
+static GSList *current_shares = NULL;
 
 static GList *active_links;
 static GList *active_redirects;
@@ -152,26 +156,6 @@
 	return NETWORK_LOCAL_DISABLED;
 }
 
-static char *
-get_data_for_link (const char *uri, 
-		   const char *display_name, 
-		   const char *icon)
-{
-	char *data;
-
-	data = g_strdup_printf ("[Desktop Entry]\n"
-				"Encoding=UTF-8\n"
-				"Name=%s\n"
-				"Type=FSDevice\n"
-				"Icon=%s\n"
-				"URL=%s\n",
-				display_name,
-				icon,
-				uri);
-	return data;
-}
-
-
 /* Called with lock held */
 static void
 do_link_event (const char *filename,
@@ -227,6 +211,7 @@
 		g_free (found->target_uri);
 		g_free (found->display_name);
 		g_free (found->icon);
+		g_free (found->desktop_item_type);
 		g_free (found);
 	}
 }
@@ -236,7 +221,8 @@
 add_link (const char *filename,
 	  const char *target_uri,
 	  const char *display_name,
-	  const char *icon)
+	  const char *icon,
+	  const char *desktop_item_type)
 {
 	NetworkLink *link;
 
@@ -245,6 +231,7 @@
 	link->target_uri = g_strdup (target_uri);
 	link->display_name = g_strdup (display_name);
 	link->icon = g_strdup (icon);
+	link->desktop_item_type = g_strdup (desktop_item_type);
 
 	active_links = g_list_prepend (active_links, link);
 	do_link_event (filename,
@@ -265,7 +252,8 @@
 	add_link (filename,
 		  link_uri,
 		  domain,
-		  "gnome-fs-network");
+		  "gnome-fs-network",
+		  "FSDevice");
 	g_free (filename);
 	g_free (link_uri);
 }
@@ -355,9 +343,20 @@
 static char *
 network_link_create_data (NetworkLink *link)
 {
-	return get_data_for_link (link->target_uri, 
-				  link->display_name, 
-				  link->icon);
+	char *data;
+
+	data = g_strdup_printf ("[Desktop Entry]\n"
+				"Encoding=UTF-8\n"
+				"Name=%s\n"
+				"Type=%s\n"
+				"Icon=%s\n"
+				"URL=%s\n",
+				link->display_name,
+				link->desktop_item_type,
+				link->icon,
+				link->target_uri);
+
+	return data;
 }
 
 /* Call with lock held */
@@ -754,6 +753,18 @@
 	dir_handle->filenames = g_list_prepend (dir_handle->filenames, g_strdup (filename));
 } 
 
+static char *
+build_fake_share_filename (ShareInfo *share_info)
+{
+	return g_strdup_printf ("share-%s", share_info->share_name);
+}
+
+static char *
+build_share_target_uri (ShareInfo *share_info)
+{
+	return gnome_vfs_get_uri_from_local_path (share_info->path);
+}
+
 static GnomeVFSResult
 do_open_directory (GnomeVFSMethod *method,
 		   GnomeVFSMethodHandle **method_handle,
@@ -1262,6 +1273,104 @@
 }
 
 static void
+diff_sorted_slists (GSList *list1, GSList *list2, GCompareFunc compare,
+		    GSList **added, GSList **removed)
+{
+	int order;
+	
+	*added = *removed = NULL;
+
+	while (list1 != NULL &&
+	       list2 != NULL) {
+		order = (*compare) (list1->data, list2->data);
+		if (order < 0) {
+			*removed = g_slist_prepend (*removed, list1->data);
+			list1 = list1->next;
+		} else if (order > 0) {
+			*added = g_slist_prepend (*added, list2->data);
+			list2 = list2->next;
+		} else { /* same item */
+			list1 = list1->next;
+			list2 = list2->next;
+		}
+	}
+
+	while (list1 != NULL) {
+		*removed = g_slist_prepend (*removed, list1->data);
+		list1 = list1->next;
+	}
+	while (list2 != NULL) {
+		*added = g_slist_prepend (*added, list2->data);
+		list2 = list2->next;
+	}
+}
+
+static gint
+compare_share_info_name_cb (gconstpointer a, gconstpointer b)
+{
+	const ShareInfo *share_a, *share_b;
+
+	share_a = a;
+	share_b = b;
+
+	return g_utf8_collate (share_a->share_name, share_b->share_name);
+}
+
+/* Call with the lock held */
+static void
+refresh_shares_links (void)
+{
+	GSList *share_info_list;
+	GSList *l;
+	GSList *added;
+	GSList *removed;
+
+	G_LOCK (network);
+
+	/* FIXME: NULL GError */
+	if (!shares_get_share_info_list (&share_info_list, NULL)) {
+		goto out;
+	}
+
+	share_info_list = g_slist_sort (share_info_list, compare_share_info_name_cb);
+	diff_sorted_slists (current_shares, share_info_list, compare_share_info_name_cb, &added, &removed);
+
+	for (l = removed; l; l = l->next) {
+		ShareInfo *share_info;
+		char *filename;
+
+		share_info = l->data;
+		filename = build_fake_share_filename (share_info);
+		remove_link (filename);
+		g_free (filename);
+	}
+
+	for (l = added; l; l = l->next) {
+		ShareInfo *share_info;
+		char *filename;
+		char *target_uri;
+
+		share_info = l->data;
+
+		filename = build_fake_share_filename (share_info);
+		target_uri = build_share_target_uri (share_info);
+		add_link (filename, target_uri, share_info->share_name, "gnome-fs-directory", "Link");
+		g_free (filename);
+		g_free (target_uri);
+	}
+
+	g_slist_free (added);
+	g_slist_free (removed);
+
+	shares_free_share_info_list (current_shares);
+	current_shares = share_info_list;
+
+ out:
+
+	G_UNLOCK (network);
+}
+
+static void
 refresh_link_lists (void)
 {
 	char hostname[256];
@@ -1330,6 +1439,8 @@
 
 		G_UNLOCK (network);
 	}
+
+	refresh_shares_links ();
 }
 
 
@@ -1397,7 +1508,8 @@
 		add_link ("smblink-root",
 			  "smb://",
 			  _("Windows Network"),
-			  "gnome-fs-network");
+			  "gnome-fs-network",
+			  "FSDevice");
 	}
 
 	
--- modules/shares.c
+++ modules/shares.c
@@ -0,0 +1,890 @@
+#include <config.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib/gi18n-lib.h>
+#include "shares.h"
+
+#undef DEBUG_SHARES
+#ifdef DEBUG_SHARES
+#  define NET_USERSHARE_ARGV0 "debug-net-usershare"
+#else
+#  define NET_USERSHARE_ARGV0 "net"
+#endif
+
+static GHashTable *path_share_info_hash;
+static GHashTable *share_name_share_info_hash;
+
+#define NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES 100
+#define TIMESTAMP_THRESHOLD 10	/* seconds */
+static int refresh_timestamp_update_counter;
+static time_t refresh_timestamp;
+
+#define KEY_PATH "path"
+#define KEY_COMMENT "comment"
+#define KEY_ACL "usershare_acl"
+
+/* Debugging flags */
+static gboolean throw_error_on_refresh;
+static gboolean throw_error_on_add;
+static gboolean throw_error_on_modify;
+static gboolean throw_error_on_remove;
+
+
+
+/* Interface to "net usershare" */
+
+static gboolean
+net_usershare_run (int argc, char **argv, GKeyFile **ret_key_file, GError **error)
+{
+	int real_argc;
+	int i;
+	char **real_argv;
+	gboolean retval;
+	char *stdout_contents;
+	char *stderr_contents;
+	int exit_status;
+	int exit_code;
+	GKeyFile *key_file;
+	GError *real_error;
+
+	g_assert (argc > 0);
+	g_assert (argv != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (ret_key_file)
+		*ret_key_file = NULL;
+
+	/* Build command line */
+
+	real_argc = 2 + argc + 1; /* "net" "usershare" [argv] NULL */
+	real_argv = g_new (char *, real_argc);
+
+	real_argv[0] = NET_USERSHARE_ARGV0;
+	real_argv[1] = "usershare";
+
+	for (i = 0; i < argc; i++) {
+		g_assert (argv[i] != NULL);
+		real_argv[i + 2] = argv[i];
+	}
+
+	real_argv[real_argc - 1] = NULL;
+
+	/* Launch */
+
+	stdout_contents = NULL;
+	stderr_contents = NULL;
+
+	{
+		char **p;
+
+		g_message ("------------------------------------------");
+
+		for (p = real_argv; *p; p++)
+			g_message ("spawn arg \"%s\"", *p);
+
+		g_message ("end of spawn args; SPAWNING\n");
+	}
+
+	real_error = NULL;
+	retval = g_spawn_sync (NULL,			/* cwd */
+			       real_argv,
+			       NULL, 			/* envp */
+			       G_SPAWN_SEARCH_PATH,
+			       NULL, 			/* GSpawnChildSetupFunc */
+			       NULL,			/* user_data */
+			       &stdout_contents,
+			       &stderr_contents,
+			       &exit_status,
+			       &real_error);
+
+	g_message ("returned from spawn: %s: %s", retval ? "SUCCESS" : "FAIL", retval ? "" : real_error->message);
+
+	if (!retval) {
+		g_propagate_error (error, real_error);
+		goto out;
+	}
+
+	if (!WIFEXITED (exit_status)) {
+		g_message ("WIFEXITED(%d) was false!", exit_status);
+		retval = FALSE;
+
+		if (WIFSIGNALED (exit_status)) {
+			int signal_num;
+
+			signal_num = WTERMSIG (exit_status);
+			g_message ("Child got signal %d", signal_num);
+
+			g_set_error (error,
+				     SHARES_ERROR,
+				     SHARES_ERROR_FAILED,
+				     _("%s %s %s returned with signal %d"),
+				     real_argv[0],
+				     real_argv[1],
+				     real_argv[2],
+				     signal_num);
+		} else
+			g_set_error (error,
+				     SHARES_ERROR,
+				     SHARES_ERROR_FAILED,
+				     _("%s %s %s failed for an unknown reason"),
+				     real_argv[0],
+				     real_argv[1],
+				     real_argv[2]);
+
+		goto out;
+	}
+
+	exit_code = WEXITSTATUS (exit_status);
+
+	g_message ("exit code %d", exit_code);
+	if (exit_code != 0) {
+		char *str;
+		char *message;
+
+		/* stderr_contents is in the system locale encoding, not UTF-8 */
+
+		str = g_locale_to_utf8 (stderr_contents, -1, NULL, NULL, NULL);
+
+		if (str && str[0])
+			message = g_strdup_printf (_("'net usershare' returned error %d: %s"), exit_code, str);
+		else
+			message = g_strdup_printf (_("'net usershare' returned error %d"), exit_code);
+
+		g_free (str);
+
+		g_set_error (error,
+			     G_SPAWN_ERROR,
+			     G_SPAWN_ERROR_FAILED,
+			     "%s",
+			     message);
+
+		g_free (message);
+
+		retval = FALSE;
+		goto out;
+	}
+
+	if (ret_key_file) {
+		g_message ("caller wants GKeyFile");
+
+		*ret_key_file = NULL;
+
+		/* FIXME: jeallison@novell.com says the output of "net usershare" is nearly always
+		 * in UTF-8, but that it can be configured in the master smb.conf.  We assume
+		 * UTF-8 for now.
+		 */
+
+		if (!g_utf8_validate (stdout_contents, -1, NULL)) {
+			g_message ("stdout of net usershare was not in valid UTF-8");
+			g_set_error (error,
+				     G_SPAWN_ERROR,
+				     G_SPAWN_ERROR_FAILED,
+				     _("the output of 'net usershare' is not in valid UTF-8 encoding"));
+			retval = FALSE;
+			goto out;
+		}
+
+		key_file = g_key_file_new ();
+
+		real_error = NULL;
+		if (!g_key_file_load_from_data (key_file, stdout_contents, -1, 0, &real_error)) {
+			g_message ("Error when parsing key file {\n%s\n}: %s", stdout_contents, real_error->message);
+			g_propagate_error (error, real_error);
+			g_key_file_free (key_file);
+			retval = FALSE;
+			goto out;
+		}
+
+		retval = TRUE;
+		*ret_key_file = key_file;
+	} else
+		retval = TRUE;
+
+	g_message ("success from calling net usershare and parsing its output");
+
+ out:
+	g_free (real_argv);
+	g_free (stdout_contents);
+	g_free (stderr_contents);
+
+	g_message ("------------------------------------------");
+
+	return retval;
+}
+
+
+
+/* Internals */
+
+static void
+ensure_hashes (void)
+{
+	if (path_share_info_hash == NULL) {
+		g_assert (share_name_share_info_hash == NULL);
+
+		path_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal);
+		share_name_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal);
+	} else
+		g_assert (share_name_share_info_hash != NULL);
+}
+
+static ShareInfo *
+lookup_share_by_path (const char *path)
+{
+	ensure_hashes ();
+	return g_hash_table_lookup (path_share_info_hash, path);
+}
+
+static ShareInfo *
+lookup_share_by_share_name (const char *share_name)
+{
+	ensure_hashes ();
+	return g_hash_table_lookup (share_name_share_info_hash, share_name);
+}
+
+static void
+add_share_info_to_hashes (ShareInfo *info)
+{
+	ensure_hashes ();
+	g_hash_table_insert (path_share_info_hash, info->path, info);
+	g_hash_table_insert (share_name_share_info_hash, info->share_name, info);
+}
+
+static void
+remove_share_info_from_hashes (ShareInfo *info)
+{
+	ensure_hashes ();
+	g_hash_table_remove (path_share_info_hash, info->path);
+	g_hash_table_remove (share_name_share_info_hash, info->share_name);
+}
+
+static gboolean
+remove_from_path_hash_cb (gpointer key,
+			  gpointer value,
+			  gpointer data)
+{
+	ShareInfo *info;
+
+	info = value;
+	shares_free_share_info (info);
+
+	return TRUE;
+}
+
+static gboolean
+remove_from_share_name_hash_cb (gpointer key,
+				gpointer value,
+				gpointer data)
+{
+	/* The ShareInfo was already freed in remove_from_path_hash_cb() */
+	return TRUE;
+}
+
+static void
+free_all_shares (void)
+{
+	ensure_hashes ();
+	g_hash_table_foreach_remove (path_share_info_hash, remove_from_path_hash_cb, NULL);
+	g_hash_table_foreach_remove (share_name_share_info_hash, remove_from_share_name_hash_cb, NULL);
+}
+
+static char *
+get_string_from_key_file (GKeyFile *key_file, const char *group, const char *key)
+{
+	GError *error;
+	char *str;
+
+	error = NULL;
+	str = NULL;
+
+	if (g_key_file_has_key (key_file, group, key, &error)) {
+		str = g_key_file_get_string (key_file, group, key, &error);
+		if (!str) {
+			g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)
+				  && !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND));
+
+			g_error_free (error);
+		}
+	} else {
+		g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND));
+		g_error_free (error);
+	}
+
+	return str;
+}
+
+static void
+add_key_group_to_hashes (GKeyFile *key_file, const char *group)
+{
+	char *path;
+	char *comment;
+	char *acl;
+	gboolean is_writable;
+	ShareInfo *info;
+	ShareInfo *old_info;
+
+	/* Remove the old share based on the name */
+
+	old_info = lookup_share_by_share_name (group);
+	if (old_info) {
+		remove_share_info_from_hashes (old_info);
+		shares_free_share_info (old_info);
+	}
+
+	/* Start parsing, and remove the old share based on the path */
+
+	path = get_string_from_key_file (key_file, group, KEY_PATH);
+	if (!path) {
+		g_message ("group '%s' doesn't have a '%s' key!  Ignoring group.", group, KEY_PATH);
+		return;
+	}
+
+	old_info = lookup_share_by_path (path);
+	if (old_info) {
+		remove_share_info_from_hashes (old_info);
+		shares_free_share_info (old_info);
+	}
+
+	/* Finish parsing */
+
+	comment = get_string_from_key_file (key_file, group, KEY_COMMENT);
+
+	acl = get_string_from_key_file (key_file, group, KEY_ACL);
+	if (acl) {
+		if (strcmp (acl, "Everyone:R") == 0)
+			is_writable = FALSE;
+		else if (strcmp (acl, "Everyone:F") == 0)
+			is_writable = TRUE;
+		else {
+			g_message ("unknown format for key '%s/%s' as it contains '%s'.  Assuming that the share is read-only",
+				   group, KEY_ACL, acl);
+			is_writable = FALSE;
+		}
+
+		g_free (acl);
+	} else {
+		g_message ("group '%s' doesn't have a '%s' key!  Assuming that the share is read-only.", group, KEY_ACL);
+		is_writable = FALSE;
+	}
+
+	g_assert (path != NULL);
+	g_assert (group != NULL);
+
+	info = g_new (ShareInfo, 1);
+	info->path = path;
+	info->share_name = g_strdup (group);
+	info->comment = comment;
+	info->is_writable = is_writable;
+
+	add_share_info_to_hashes (info);
+}
+
+static void
+replace_shares_from_key_file (GKeyFile *key_file)
+{
+	gsize num_groups;
+	char **group_names;
+	gsize i;
+
+	group_names = g_key_file_get_groups (key_file, &num_groups);
+
+	/* FIXME: In add_key_group_to_hashes(), we simply ignore key groups
+	 * which have invalid data (i.e. no path).  We could probably accumulate a
+	 * GError with the list of invalid groups and propagate it upwards.
+	 */
+	for (i = 0; i < num_groups; i++) {
+		g_assert (group_names[i] != NULL);
+		add_key_group_to_hashes (key_file, group_names[i]);
+	}
+
+	g_strfreev (group_names);
+}
+
+static gboolean
+refresh_shares (GError **error)
+{
+	GKeyFile *key_file;
+	char *argv[1];
+	GError *real_error;
+
+	free_all_shares ();
+
+	if (throw_error_on_refresh) {
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_FAILED,
+			     _("Failed"));
+		return FALSE;
+	}
+
+	argv[0] = "info";
+
+	real_error = NULL;
+	if (!net_usershare_run (G_N_ELEMENTS (argv), argv, &key_file, &real_error)) {
+		g_message ("Called \"net usershare info\" but it failed: %s", real_error->message);
+		g_propagate_error (error, real_error);
+		return FALSE;
+	}
+
+	g_assert (key_file != NULL);
+
+	replace_shares_from_key_file (key_file);
+	g_key_file_free (key_file);
+
+	return TRUE;
+}
+
+static gboolean
+refresh_if_needed (GError **error)
+{
+	gboolean retval;
+
+	if (refresh_timestamp_update_counter == 0) {
+		time_t new_timestamp;
+
+		refresh_timestamp_update_counter = NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES;
+
+		new_timestamp = time (NULL);
+		if (new_timestamp - refresh_timestamp > TIMESTAMP_THRESHOLD) {
+			g_message ("REFRESHING SHARES");
+			retval = refresh_shares (error);
+		} else
+			retval = TRUE;
+
+		refresh_timestamp = new_timestamp;
+	} else {
+		refresh_timestamp_update_counter--;
+		retval = TRUE;
+	}
+
+	return retval;
+}
+
+static ShareInfo *
+copy_share_info (ShareInfo *info)
+{
+	ShareInfo *copy;
+
+	if (!info)
+		return NULL;
+
+	copy = g_new (ShareInfo, 1);
+	copy->path = g_strdup (info->path);
+	copy->share_name = g_strdup (info->share_name);
+	copy->comment = g_strdup (info->comment);
+	copy->is_writable = info->is_writable;
+
+	return copy;
+}
+
+static gboolean
+add_share (ShareInfo *info, GError **error)
+{
+	char *argv[6];
+	ShareInfo *copy;
+	GKeyFile *key_file;
+	GError *real_error;
+
+	g_message ("add_share() start");
+
+	if (throw_error_on_add) {
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_FAILED,
+			     _("Failed"));
+		g_message ("add_share() end FAIL");
+		return FALSE;
+	}
+
+	argv[0] = "add";
+	argv[1] = "-l";
+	argv[2] = info->share_name;
+	argv[3] = info->path;
+	argv[4] = info->comment;
+	argv[5] = info->is_writable ? "Everyone:F" : "Everyone:R";
+
+	real_error = NULL;
+	if (!net_usershare_run (G_N_ELEMENTS (argv), argv, &key_file, &real_error)) {
+		g_message ("Called \"net usershare add\" but it failed: %s", real_error->message);
+		g_propagate_error (error, real_error);
+		return FALSE;
+	}
+
+	replace_shares_from_key_file (key_file);
+
+	copy = copy_share_info (info);
+	add_share_info_to_hashes (copy);
+
+	g_message ("add_share() end SUCCESS");
+
+	return TRUE;
+}
+
+static gboolean
+remove_share (const char *path, GError **error)
+{
+	ShareInfo *old_info;
+	char *argv[2];
+	GError *real_error;
+
+	g_message ("remove_share() start");
+
+	if (throw_error_on_remove) {
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_FAILED,
+			     "Failed");
+		g_message ("remove_share() end FAIL");
+		return FALSE;
+	}
+
+	old_info = lookup_share_by_path (path);
+	if (!old_info) {
+		char *display_name;
+
+		display_name = g_filename_display_name (path);
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_NONEXISTENT,
+			     _("Cannot remove the share for path %s: that path is not shared"),
+			     display_name);
+		g_free (display_name);
+
+		g_message ("remove_share() end FAIL: path %s was not in our hashes", path);
+		return FALSE;
+	}
+
+	argv[0] = "delete";
+	argv[1] = old_info->share_name;
+
+	real_error = NULL;
+	if (!net_usershare_run (G_N_ELEMENTS (argv), argv, NULL, &real_error)) {
+		g_message ("Called \"net usershare delete\" but it failed: %s", real_error->message);
+		g_propagate_error (error, real_error);
+		g_message ("remove_share() end FAIL");
+		return FALSE;
+	}
+
+	remove_share_info_from_hashes (old_info);
+	shares_free_share_info (old_info);
+
+	g_message ("remove_share() end SUCCESS");
+
+	return TRUE;
+}
+
+static gboolean
+modify_share (const char *old_path, ShareInfo *info, GError **error)
+{
+	ShareInfo *old_info;
+
+	g_message ("modify_share() start");
+
+	old_info = lookup_share_by_path (old_path);
+	if (old_info == NULL) {
+		g_message ("modify_share() end; calling add_share() instead");
+		return add_share (info, error);
+	}
+
+	g_assert (old_info != NULL);
+
+	if (strcmp (info->path, old_info->path) != 0) {
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_FAILED,
+			     _("Cannot change the path of an existing share; please remove the old share first and add a new one"));
+		g_message ("modify_share() end FAIL: tried to change the path in a share!");
+		return FALSE;
+	}
+
+	if (throw_error_on_modify) {
+		g_set_error (error,
+			     SHARES_ERROR,
+			     SHARES_ERROR_FAILED,
+			     "Failed");
+		g_message ("modify_share() end FAIL");
+		return FALSE;
+	}
+
+	/* Although "net usershare add" will modify an existing share if it has the same share name
+	 * as the one that gets passed in, our semantics are different.  We have a one-to-one mapping
+	 * between paths and share names; "net usershare" supports a one-to-many mapping from paths
+	 * to share names.  So, we must first remove the old share and then add the new/modified one.
+	 */
+
+	if (!remove_share (old_path, error)) {
+		g_message ("modify_share() end FAIL: error when removing old share");
+		return FALSE;
+	}
+
+	g_message ("modify_share() end: will call add_share() with the new share info");
+	return add_share (info, error);
+}
+
+
+
+/* Public API */
+
+GQuark
+shares_error_quark (void)
+{
+	static GQuark quark;
+
+	if (quark == 0)
+		quark = g_quark_from_string ("nautilus-shares-error-quark"); /* not from_static_string since we are a module */
+
+	return quark;
+}
+
+/**
+ * shares_free_share_info:
+ * @info: A #ShareInfo structure.
+ *
+ * Frees a #ShareInfo structure.
+ **/
+void
+shares_free_share_info (ShareInfo *info)
+{
+	g_assert (info != NULL);
+
+	g_free (info->path);
+	g_free (info->share_name);
+	g_free (info->comment);
+	g_free (info);
+}
+
+/**
+ * shares_get_path_is_shared:
+ * @path: A full path name ("/foo/bar/baz") in file system encoding.
+ * @ret_is_shared: Location to store result value (#TRUE if the path is shared, #FALSE otherwise)
+ * @error: Location to store error, or #NULL.
+ *
+ * Checks whether a path is shared through Samba.
+ *
+ * Return value: #TRUE if the info could be queried successfully, #FALSE
+ * otherwise.  If this function returns #FALSE, an error code will be returned in the
+ * @error argument, and *@ret_is_shared will be set to #FALSE.
+ **/
+gboolean
+shares_get_path_is_shared (const char *path, gboolean *ret_is_shared, GError **error)
+{
+	g_assert (ret_is_shared != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error)) {
+		*ret_is_shared = FALSE;
+		return FALSE;
+	}
+
+	*ret_is_shared = (lookup_share_by_path (path) != NULL);
+
+	return TRUE;
+}
+
+/**
+ * shares_get_share_info_for_path:
+ * @path: A full path name ("/foo/bar/baz") in file system encoding.
+ * @ret_share_info: Location to store result with the share's info - on return,
+ * will be non-NULL if the path is indeed shared, or #NULL if the path is not
+ * shared.  You must free the non-NULL value with shares_free_share_info().
+ * @error: Location to store error, or #NULL.
+ *
+ * Queries the information for a shared path:  its share name, its read-only status, etc.
+ *
+ * Return value: #TRUE if the info could be queried successfully, #FALSE
+ * otherwise.  If this function returns #FALSE, an error code will be returned in the
+ * @error argument, and *@ret_share_info will be set to #NULL.
+ **/
+gboolean
+shares_get_share_info_for_path (const char *path, ShareInfo **ret_share_info, GError **error)
+{
+	ShareInfo *info;
+
+	g_assert (path != NULL);
+	g_assert (ret_share_info != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error)) {
+		*ret_share_info = NULL;
+		return FALSE;
+	}
+
+	info = lookup_share_by_path (path);
+	*ret_share_info = copy_share_info (info);
+
+	return TRUE;
+}
+
+/**
+ * shares_get_share_name_exists:
+ * @share_name: Name of a share.
+ * @ret_exists: Location to store return value; #TRUE if the share name exists, #FALSE otherwise.
+ *
+ * Queries whether a share name already exists in the user's list of shares.
+ *
+ * Return value: #TRUE if the info could be queried successfully, #FALSE
+ * otherwise.  If this function returns #FALSE, an error code will be returned in the
+ * @error argument, and *@ret_exists will be set to #FALSE.
+ **/
+gboolean
+shares_get_share_name_exists (const char *share_name, gboolean *ret_exists, GError **error)
+{
+	g_assert (share_name != NULL);
+	g_assert (ret_exists != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error)) {
+		*ret_exists = FALSE;
+		return FALSE;
+	}
+
+	*ret_exists = (lookup_share_by_share_name (share_name) != NULL);
+
+	return TRUE;
+}
+
+/**
+ * shares_get_share_info_for_share_name:
+ * @share_name: Name of a share.
+ * @ret_share_info: Location to store result with the share's info - on return,
+ * will be non-NULL if there is a share for the specified name, or #NULL if no
+ * share has such name.  You must free the non-NULL value with
+ * shares_free_share_info().
+ * @error: Location to store error, or #NULL.
+ *
+ * Queries the information for the share which has a specific name.
+ *
+ * Return value: #TRUE if the info could be queried successfully, #FALSE
+ * otherwise.  If this function returns #FALSE, an error code will be returned in the
+ * @error argument, and *@ret_share_info will be set to #NULL.
+ **/
+gboolean
+shares_get_share_info_for_share_name (const char *share_name, ShareInfo **ret_share_info, GError **error)
+{
+	ShareInfo *info;
+
+	g_assert (share_name != NULL);
+	g_assert (ret_share_info != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error)) {
+		*ret_share_info = NULL;
+		return FALSE;
+	}
+
+	info = lookup_share_by_share_name (share_name);
+	*ret_share_info = copy_share_info (info);
+
+	return TRUE;
+}
+
+/**
+ * shares_modify_share:
+ * @old_path: Path of the share to modify, or %NULL.
+ * @info: Info of the share to modify/add, or %NULL to delete a share.
+ * @error: Location to store error, or #NULL.
+ *
+ * Can add, modify, or delete shares.  To add a share, pass %NULL for @old_path,
+ * and a non-null @info.  To modify a share, pass a non-null @old_path and
+ * non-null @info; in this case, @info->path must have the same contents as
+ * @old_path.  To remove a share, pass a non-NULL @old_path and a %NULL @info.
+ *
+ * Return value: TRUE if the share could be modified, FALSE otherwise.  If this returns
+ * FALSE, then the error information will be placed in @error.
+ **/
+gboolean
+shares_modify_share (const char *old_path, ShareInfo *info, GError **error)
+{
+	g_assert ((old_path == NULL && info != NULL)
+		  || (old_path != NULL && info == NULL)
+		  || (old_path != NULL && info != NULL));
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error))
+		return FALSE;
+
+	if (old_path == NULL)
+		return add_share (info, error);
+	else if (info == NULL)
+		return remove_share (old_path, error);
+	else
+		return modify_share (old_path, info, error);
+}
+
+static void
+copy_to_slist_cb (gpointer key, gpointer value, gpointer data)
+{
+	ShareInfo *info;
+	ShareInfo *copy;
+	GSList **list;
+
+	info = value;
+	list = data;
+
+	copy = copy_share_info (info);
+	*list = g_slist_prepend (*list, copy);
+}
+
+/**
+ * shares_get_share_info_list:
+ * @ret_info_list: Location to store the return value, which is a list
+ * of #ShareInfo structures.  Free this with shares_free_share_info_list().
+ * @error: Location to store error, or #NULL.
+ *
+ * Gets the list of shared folders and their information.
+ *
+ * Return value: #TRUE if the info could be queried successfully, #FALSE
+ * otherwise.  If this function returns #FALSE, an error code will be returned in the
+ * @error argument, and *@ret_info_list will be set to #NULL.
+ **/
+gboolean
+shares_get_share_info_list (GSList **ret_info_list, GError **error)
+{
+	g_assert (ret_info_list != NULL);
+	g_assert (error == NULL || *error == NULL);
+
+	if (!refresh_if_needed (error)) {
+		*ret_info_list = NULL;
+		return FALSE;
+	}
+
+	*ret_info_list = NULL;
+	g_hash_table_foreach (path_share_info_hash, copy_to_slist_cb, ret_info_list);
+
+	return TRUE;
+}
+
+/**
+ * shares_free_share_info_list:
+ * @list: List of #ShareInfo structures, or %NULL.
+ *
+ * Frees a list of #ShareInfo structures as returned by shares_get_share_info_list().
+ **/
+void
+shares_free_share_info_list (GSList *list)
+{
+	GSList *l;
+
+	for (l = list; l; l = l->next) {
+		ShareInfo *info;
+
+		info = l->data;
+		shares_free_share_info (l->data);
+	}
+
+	g_slist_free (list);
+}
+
+void
+shares_set_debug (gboolean error_on_refresh,
+		  gboolean error_on_add,
+		  gboolean error_on_modify,
+		  gboolean error_on_remove)
+{
+	throw_error_on_refresh = error_on_refresh;
+	throw_error_on_add = error_on_add;
+	throw_error_on_modify = error_on_modify;
+	throw_error_on_remove = error_on_remove;
+}
--- modules/shares.h
+++ modules/shares.h
@@ -0,0 +1,43 @@
+#ifndef SHARES_H
+#define SHARES_H
+
+#include <glib.h>
+
+typedef struct {
+	char *path;
+	char *share_name;
+	char *comment;
+	gboolean is_writable;
+} ShareInfo;
+
+#define SHARES_ERROR (shares_error_quark ())
+
+typedef enum {
+	SHARES_ERROR_FAILED,
+	SHARES_ERROR_NONEXISTENT
+} SharesError;
+
+GQuark shares_error_quark (void);
+
+void shares_free_share_info (ShareInfo *info);
+
+gboolean shares_get_path_is_shared (const char *path, gboolean *ret_is_shared, GError **error);
+
+gboolean shares_get_share_info_for_path (const char *path, ShareInfo **ret_share_info, GError **error);
+
+gboolean shares_get_share_name_exists (const char *share_name, gboolean *ret_exists, GError **error);
+
+gboolean shares_get_share_info_for_share_name (const char *share_name, ShareInfo **ret_share_info, GError **error);
+
+gboolean shares_modify_share (const char *old_path, ShareInfo *info, GError **error);
+
+gboolean shares_get_share_info_list (GSList **ret_info_list, GError **error);
+
+void shares_free_share_info_list (GSList *list);
+
+void shares_set_debug (gboolean error_on_refresh,
+		       gboolean error_on_add,
+		       gboolean error_on_modify,
+		       gboolean error_on_remove);
+
+#endif
--- modules/smb-method.c
+++ modules/smb-method.c
@@ -37,6 +37,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <stdio.h>
 
 #include <gconf/gconf-client.h>
 #include <libgnomevfs/gnome-vfs.h>
@@ -90,6 +91,17 @@
 
 static GHashTable *user_cache = NULL;
 
+/* smbcctx->readdir() can give us a struct smbc_dirent that identifies a
+ * printer, but you cannot perform a smbcctx->stat() on a path to know if that
+ * path refers to a printer.  So, when doing readdir(), we must remember the
+ * URIs of the printers we have found.  Later in our own do_get_file_info(), we
+ * will match the passed URI to this hash table and return a magic "it is a
+ * printer" datum if appropriate.
+ */
+static GHashTable *printer_hash = NULL;
+
+static FILE *debug_file;
+
 #define SMB_BLOCK_SIZE (32*1024)
 
 /* Reap unused server connections and user cache after 30 minutes */
@@ -149,32 +161,105 @@
 static void init_authentication (SmbAuthContext *actx, GnomeVFSURI *uri);
 static int  perform_authentication (SmbAuthContext *actx);
 
+static gboolean is_printer (const char *uri);
+
 static SmbAuthContext *current_auth_context = NULL;
 
 static void auth_callback (const char *server_name, const char *share_name,
 		     	   char *domain, int domainmaxlen,
 		     	   char *username, int unmaxlen,
 		     	   char *password, int pwmaxlen);
+
+static void debug_print (const char *format, ...);
+static void debug_indent (int amount);
+static int debug_indentation;
 		     	   
-#if 0
+#if 1
 #define DEBUG_SMB_ENABLE
 #define DEBUG_SMB_LOCKS
 #endif
 
 #ifdef DEBUG_SMB_ENABLE
-#define DEBUG_SMB(x) g_print x
+#define DEBUG_SMB(x) debug_print x
+#define DEBUG_IN() debug_print ("%s() {\n", G_STRFUNC); debug_indent(1)
+#define DEBUG_OUT() debug_indent(-1); debug_print ("} %s()\n", G_STRFUNC)
 #else
-#define DEBUG_SMB(x) 
+#define DEBUG_SMB(x)
+#define DEBUG_IN()
+#define DEBUG_OUT()
 #endif
 
 #ifdef DEBUG_SMB_LOCKS
-#define LOCK_SMB() 	{g_mutex_lock (smb_lock); g_print ("LOCK %s\n", G_GNUC_PRETTY_FUNCTION);}
-#define UNLOCK_SMB() 	{g_print ("UNLOCK %s\n", G_GNUC_PRETTY_FUNCTION); g_mutex_unlock (smb_lock);}
+#define LOCK_SMB() 	{g_mutex_lock (smb_lock); debug_print ("LOCK %s\n", G_STRFUNC);}
+#define UNLOCK_SMB() 	{debug_print ("UNLOCK %s\n", G_STRFUNC); g_mutex_unlock (smb_lock);}
 #else
 #define LOCK_SMB() 	g_mutex_lock (smb_lock)
 #define UNLOCK_SMB() 	g_mutex_unlock (smb_lock)
 #endif
 
+static void
+debug_print (const char *format, ...)
+{
+	va_list args;
+	char *str;
+	int i;
+
+	if (!debug_file)
+		return;
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	fprintf (debug_file, "%p: ", g_thread_self ());
+
+	for (i = 0; i < debug_indentation * 4; i++)
+		fputc (' ', debug_file);
+
+	fputs (str, debug_file);
+	g_free (str);
+	fflush (debug_file);
+}
+
+static void
+debug_indent (int amount)
+{
+	debug_indentation += amount;
+	if (debug_indentation < 0)
+		g_error ("You fucked up your indentation");
+}
+
+static void
+DEBUG_DUMP_AUTH_CONTEXT (SmbAuthContext *actx)
+{
+	char *str_uri;
+
+	if (actx->uri)
+		str_uri = gnome_vfs_uri_to_string (actx->uri, 0);
+	else
+		str_uri = g_strdup ("(null)");
+
+	debug_print ("AUTH CONTEXT %p {\n", actx);
+	debug_print ("             uri: %s\n", str_uri);
+	debug_print ("      vfs_result: %d\n", (int) actx->res);
+	debug_print ("          passes: %d\n", actx->passes);
+	debug_print ("           state: %x\n", actx->state);
+	debug_print ("       save_auth: %d\n", actx->save_auth);
+	debug_print ("         keyring: %s\n", actx->keyring);
+	debug_print ("     auth_called: %d\n", actx->auth_called);
+	debug_print ("      for_server: %s\n", actx->for_server);
+	debug_print ("       for_share: %s\n", actx->for_share);
+	debug_print ("        use_user: %s\n", actx->use_user);
+	debug_print ("      use_domain: %s\n", actx->use_domain);
+	debug_print ("    use_password: %s\n", actx->use_password);
+	debug_print ("     cache_added: %d\n", actx->cache_added);
+	debug_print ("      cache_used: %d\n", actx->cache_used);
+	debug_print ("    prompt_flags: %x\n", actx->prompt_flags);
+	debug_print ("}\n");
+
+	g_free (str_uri);
+}
+
 static gchar*
 string_dup_nzero (const gchar *s)
 {
@@ -319,7 +404,8 @@
          * sure when we'll be called */
         if (!g_mutex_trylock (smb_lock))
                 return TRUE;
-        DEBUG_SMB(("LOCK %s\n", G_GNUC_PRETTY_FUNCTION));
+	DEBUG_IN ();
+        DEBUG_SMB(("LOCK %s\n", G_STRFUNC));
 
 	size = g_hash_table_size (server_cache);
 	servers = g_ptr_array_sized_new (size);
@@ -342,6 +428,7 @@
         if (!ret)
                 cache_reap_timeout = 0;
 
+	DEBUG_OUT ();
         UNLOCK_SMB();
 
         return ret;        
@@ -363,6 +450,7 @@
 {
 	SmbServerCacheEntry *entry = NULL;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("[auth] adding cached server: server: %s, share: %s, domain: %s, user: %s\n",
 		   server_name ? server_name : "", 
 		   share_name ? share_name : "",
@@ -383,6 +471,8 @@
 
 	g_hash_table_insert (server_cache, entry, entry);
 	current_auth_context->cache_added = TRUE;
+
+	DEBUG_OUT ();
 	return 0;
 }
 
@@ -393,6 +483,7 @@
 	SmbServerCacheEntry entry;
 	SmbServerCacheEntry *res;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("find_cached_server: server: %s, share: %s, domain: %s, user: %s\n", 
 		   server_name ? server_name : "", 
 		   share_name ? share_name : "",
@@ -409,9 +500,13 @@
 
 	if (res != NULL) {
 		res->last_time = time (NULL);
+		DEBUG_OUT ();
+		DEBUG_SMB (("found server %p\n", res->server));
 		return res->server;
-	} 
+	}
 
+	DEBUG_SMB (("found nothing; returning NULL\n"));
+	DEBUG_OUT ();
 	return NULL;
 }
 
@@ -422,6 +517,8 @@
 {
 	SMBCSRV *srv;
 	
+	DEBUG_IN ();
+
 	srv = find_cached_server (server_name, share_name, domain, username);
 	if (srv != NULL) {
 		DEBUG_SMB(("got cached server: server: %s, share: %s, domain: %s, user: %s\n",
@@ -430,8 +527,10 @@
 			   domain ? domain : "", 
 			   username ? username : ""));
 		current_auth_context->cache_used = TRUE;
+		DEBUG_OUT ();
 		return srv;
 	}
+	DEBUG_OUT ();
 	return NULL;
 }
 
@@ -454,9 +553,13 @@
 static int remove_cached_server(SMBCCTX * context, SMBCSRV * server)
 {
 	int removed;
+
+	DEBUG_IN ();
 	
 	removed = g_hash_table_foreach_remove (server_cache, remove_server, server);
 
+	DEBUG_OUT ();
+
 	/* return 1 if failed */
 	return removed == 0;
 }
@@ -483,6 +586,8 @@
 	gboolean could_not_purge_all;
 	int i;
 
+	DEBUG_IN ();
+
 	size = g_hash_table_size (server_cache);
 	servers = g_ptr_array_sized_new (size);
 
@@ -499,6 +604,10 @@
 	}
 
 	g_ptr_array_free (servers, TRUE);
+
+	DEBUG_SMB (("returning could_not_purge_all = %d\n", could_not_purge_all));
+
+	DEBUG_OUT ();
 	
 	return could_not_purge_all;
 }
@@ -519,6 +628,8 @@
 	SMBCFILE *dir = NULL;
 	time_t t;
 	struct smbc_dirent *dirent;
+
+	DEBUG_IN ();
 	
 	t = time (NULL);
 
@@ -526,6 +637,7 @@
 	    workgroups_timestamp < t &&
 	    t < workgroups_timestamp + WORKGROUP_CACHE_TIMEOUT) {
 		/* Up to date */
+		DEBUG_OUT ();
 		return;
 	}
 	workgroups_timestamp = t;
@@ -537,15 +649,20 @@
 	LOCK_SMB();
 	
 	init_authentication (&actx, NULL);
+	DEBUG_DUMP_AUTH_CONTEXT (&actx);
 	
 	/* Important: perform_authentication leaves and re-enters the lock! */
 	while (perform_authentication (&actx) > 0) {
+		DEBUG_SMB (("calling ctx->opendir (\"smb://\")\n"));
 		dir = smb_context->opendir (smb_context, "smb://");
 		actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB (("it returned %d\n", (int) actx.res));
 	}
 
 	if (dir != NULL) {
+		DEBUG_SMB (("calling ctx->readdir() in a loop\n"));
 		while ((dirent = smb_context->readdir (smb_context, dir)) != NULL) {
+			DEBUG_SMB (("got dirent '%s' of type %d\n", dirent->name, dirent->smbc_type));
 			if (dirent->smbc_type == SMBC_WORKGROUP &&
 			    dirent->name != NULL &&
 			    strlen (dirent->name) > 0) {
@@ -557,9 +674,13 @@
 			}
 		}
 
+		DEBUG_SMB (("calling ctx->closedir()\n"));
 		smb_context->closedir (smb_context, dir);
 	}
+	DEBUG_DUMP_AUTH_CONTEXT (&actx);
 	UNLOCK_SMB();
+
+	DEBUG_OUT ();
 }
 
 static SmbUriType
@@ -567,6 +688,9 @@
 {
 	GnomeVFSToplevelURI *toplevel;
 	char *first_slash;
+	SmbUriType type;
+
+	DEBUG_IN ();
 
 	toplevel = (GnomeVFSToplevelURI *)uri;
 
@@ -575,12 +699,16 @@
 		if (uri->text == NULL ||
 		    uri->text[0] == 0 ||
 		    strcmp (uri->text, "/") == 0) {
-			return SMB_URI_WHOLE_NETWORK;
+			type = SMB_URI_WHOLE_NETWORK;
+			goto out;
 		}
 		if (strchr (uri->text + 1, '/')) {
-			return SMB_URI_ERROR;
+			type = SMB_URI_ERROR;
+			goto out;
 		}
-		return SMB_URI_WORKGROUP_LINK;
+
+		type = SMB_URI_WORKGROUP_LINK;
+		goto out;
 	}
 	if (uri->text == NULL ||
 	    uri->text[0] == 0 ||
@@ -590,9 +718,11 @@
 		if (!g_ascii_strcasecmp(toplevel->host_name,
 					DEFAULT_WORKGROUP_NAME) ||
 		    g_hash_table_lookup (workgroups, toplevel->host_name)) {
-			return SMB_URI_WORKGROUP;
+			type = SMB_URI_WORKGROUP;
+			goto out;
 		} else {
-			return SMB_URI_SERVER;
+			type = SMB_URI_SERVER;
+			goto out;
 		}
 	}
 	first_slash = strchr (uri->text + 1, '/');
@@ -602,13 +732,21 @@
 		if (!g_ascii_strcasecmp(toplevel->host_name,
 					DEFAULT_WORKGROUP_NAME) ||
 		    g_hash_table_lookup (workgroups, toplevel->host_name)) {
-			return SMB_URI_SERVER_LINK;
+			type = SMB_URI_SERVER_LINK;
+			goto out;
 		} else {
-			return SMB_URI_SHARE;
+			type = SMB_URI_SHARE;
+			goto out;
 		}
 	}
-	
-	return SMB_URI_SHARE_FILE;
+
+	type = SMB_URI_SHARE_FILE;
+
+ out:
+
+	DEBUG_OUT ();
+
+	return type;
 }
 
 
@@ -665,42 +803,56 @@
 	g_free (path);
 
 	smb_context = smbc_new_context ();
-	if (smb_context != NULL) {
-		smb_context->debug = 0;
-		smb_context->callbacks.auth_fn 		    = auth_callback;
-		smb_context->callbacks.add_cached_srv_fn    = add_cached_server;
-		smb_context->callbacks.get_cached_srv_fn    = get_cached_server;
-		smb_context->callbacks.remove_cached_srv_fn = remove_cached_server;
-		smb_context->callbacks.purge_cached_fn      = purge_cached;
-
-		gclient = gconf_client_get_default ();
-		if (gclient) {
-			workgroup = gconf_client_get_string (gclient, 
-					PATH_GCONF_GNOME_VFS_SMB_WORKGROUP, NULL);
-
-			/* libsmbclient frees this on it's own, so make sure 
-			 * to use simple system malloc */
-			if (workgroup && workgroup[0])
-				smb_context->workgroup = strdup (workgroup);
-			
-			g_free (workgroup);
-			g_object_unref (gclient);
-		}
+	if (smb_context == NULL)
+		goto out;
 
-		if (!smbc_init_context (smb_context)) {
-			smbc_free_context (smb_context, FALSE);
-			smb_context = NULL;
-		}
+	smb_context->debug = 0;
+	smb_context->callbacks.auth_fn 		    = auth_callback;
+	smb_context->callbacks.add_cached_srv_fn    = add_cached_server;
+	smb_context->callbacks.get_cached_srv_fn    = get_cached_server;
+	smb_context->callbacks.remove_cached_srv_fn = remove_cached_server;
+	smb_context->callbacks.purge_cached_fn      = purge_cached;
+
+	DEBUG_SMB (("created the SMBCCTX; it has smbcctx->workgroup=\"%s\"\n",
+		    smb_context->workgroup ? smb_context->workgroup : "(null)"));
+	
+	gclient = gconf_client_get_default ();
+	if (gclient) {
+		workgroup = gconf_client_get_string (gclient, 
+						     PATH_GCONF_GNOME_VFS_SMB_WORKGROUP, NULL);
+		
+		/* libsmbclient frees this on it's own, so make sure 
+		 * to use simple system malloc */
+		if (workgroup && workgroup[0])
+			smb_context->workgroup = strdup (workgroup);
+		
+		g_free (workgroup);
+		g_object_unref (gclient);
+	}
+	
+	DEBUG_SMB (("after reading from gconf, we have smbcctx->workgroup=\"%s\"\n",
+		    smb_context->workgroup ? smb_context->workgroup : "(null)"));
 
+	if (!smbc_init_context (smb_context)) {
+		smbc_free_context (smb_context, FALSE);
+		smb_context = NULL;
+		DEBUG_SMB (("smbc_init_context() failed!\n"));
+		goto out;
+	}
+
+	DEBUG_SMB (("called smbc_init_context(); we have smbcctx->workgroup=\"%s\"\n",
+		    smb_context->workgroup ? smb_context->workgroup : "(null)"));
+	
 #if defined(HAVE_SAMBA_FLAGS) 
 #if defined(SMB_CTX_FLAG_USE_KERBEROS) && defined(SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS)
-		smb_context->flags |= SMB_CTX_FLAG_USE_KERBEROS | SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
+	smb_context->flags |= SMB_CTX_FLAG_USE_KERBEROS | SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
 #endif
-#if defined(SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON)
-		smb_context->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+#  if 0
+#    if defined(SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON)
+   	smb_context->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+#    endif
+#  endif
 #endif
-#endif		
-	}
 
 	server_cache = g_hash_table_new_full (server_hash, server_equal,
 					      (GDestroyNotify)server_free, NULL);
@@ -708,7 +860,10 @@
 					    g_free, NULL);
 	user_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
                                             g_free, (GDestroyNotify)user_free);
-	
+	printer_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+					      g_free, NULL);
+
+ out:
 	UNLOCK_SMB();
 
 	if (smb_context == NULL) {
@@ -749,6 +904,8 @@
 {
         SmbCachedUser *user;
         gchar *key;
+
+	DEBUG_IN ();
         
         g_return_if_fail (actx->for_server != NULL);
         
@@ -772,6 +929,8 @@
         user->username = string_realloc (user->username, actx->use_user);
         user->password = string_realloc (user->password, actx->use_password);
         user->stamp = time (NULL);
+
+	DEBUG_OUT ();
 }
 
 static gboolean
@@ -779,9 +938,13 @@
 {
         SmbCachedUser *user;
         gchar *key;
-       
+	gboolean retval;
+
         g_return_val_if_fail (actx->for_server != NULL, FALSE);
        
+	DEBUG_IN ();
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+       
         key = g_strdup_printf ("%s/%s", actx->for_server, with_share ? actx->for_share : "");
         user = (SmbCachedUser*)g_hash_table_lookup (user_cache, key);
         g_free (key);
@@ -789,11 +952,16 @@
         if (user) {
                 /* If we already have a user name or domain double check that... */
 		if (!(actx->prompt_flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) &&
-		    !string_compare(user->username, actx->use_user))
-			return FALSE;
+		    !string_compare(user->username, actx->use_user)) {
+			retval = FALSE;
+			goto out;
+		}
+
 		if (!(actx->prompt_flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN) &&
-		    !string_compare(user->domain, actx->use_domain))
-			return FALSE;
+		    !string_compare(user->domain, actx->use_domain)) {
+			retval = FALSE;
+			goto out;
+		}
 
                 actx->use_user = string_realloc (actx->use_user, user->username);
                 actx->use_domain = string_realloc (actx->use_domain, user->domain);
@@ -802,10 +970,16 @@
 			   actx->use_user ? actx->use_user : "",
 			   actx->use_domain ? actx->use_domain : "",
 			   actx->use_password ? actx->use_password : ""));
-                return TRUE;
+                retval = TRUE;
+		goto out;
         }
         
-        return FALSE;
+        retval = FALSE;
+
+ out:
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+	DEBUG_OUT ();
+	return retval;
 }      
 
 static gboolean
@@ -819,7 +993,10 @@
         gboolean found_user = FALSE;
 	char *tmp;
 
+	DEBUG_IN ();
+
 	DEBUG_SMB(("[auth] Initial authentication lookups\n"));
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
 
 	toplevel_uri =	(GnomeVFSToplevelURI*)actx->uri;
 	actx->prompt_flags = GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME |
@@ -888,6 +1065,8 @@
         	}
         }
 
+	DEBUG_OUT ();
+
 	return found_user;
 }
 
@@ -902,7 +1081,10 @@
 	gboolean filled = FALSE;
 
 	g_return_val_if_fail (actx != NULL, FALSE);
-	g_return_val_if_fail (actx->for_server != NULL, FALSE);	
+	g_return_val_if_fail (actx->for_server != NULL, FALSE);
+
+	DEBUG_IN ();
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
 
 	memset (&in_args, 0, sizeof (in_args));
 	in_args.uri = get_auth_display_uri (actx, FALSE);
@@ -923,6 +1105,8 @@
 		              &in_args, sizeof (in_args),
 		              &out_args, sizeof (out_args));
 
+	DEBUG_SMB(("[auth] vfs module callback for FILL_AUTHENTICATION returned invoked=%d\n", invoked));
+
         g_free (in_args.uri);
                
         /* If that didn't work then try without the share name */
@@ -949,7 +1133,8 @@
                 invoked = gnome_vfs_module_callback_invoke
                                         (GNOME_VFS_MODULE_CALLBACK_FILL_AUTHENTICATION,
                                         &in_args, sizeof (in_args),
-                                        &out_args, sizeof (out_args));                              
+                                        &out_args, sizeof (out_args));
+		DEBUG_SMB(("[auth] vfs module callback for FILL_AUTHENTICATION returned invoked=%d\n", invoked));
         }
         
 	if (invoked && out_args.valid) {
@@ -975,6 +1160,9 @@
 	g_free (out_args.domain);
 	g_free (out_args.password);
 
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+	DEBUG_OUT ();
+
 	return filled;
 }
 
@@ -990,6 +1178,9 @@
 	
 	g_return_val_if_fail (actx != NULL, FALSE);
 	g_return_val_if_fail (actx->for_server != NULL, FALSE);
+
+	DEBUG_IN ();
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
 	
 	memset (&in_args, 0, sizeof (in_args));
 	
@@ -1009,21 +1200,38 @@
 	in_args.default_user = actx->use_user;
 	if (string_compare (in_args.default_user, GUEST_LOGIN))
 		in_args.default_user = NULL;
-	if (!in_args.default_user)
-		in_args.default_user = (char*)g_get_user_name ();
-	
+	if (!in_args.default_user) {
+               const char *unix_username;
+               const char *backslash;
+
+               unix_username = g_get_user_name ();
+               backslash = strchr (unix_username, '\\');
+               if (!backslash)
+                       in_args.default_user = (char *) unix_username;
+               else
+                       in_args.default_user = (char *) backslash + 1;
+	}
+
 	in_args.default_domain = actx->use_domain ? actx->use_domain : smb_context->workgroup;
 	
 	memset (&out_args, 0, sizeof (out_args));
 
-	DEBUG_SMB(("[auth] Prompting credentials for: %s\n", 
-		   in_args.uri ? in_args.uri : ""));
+	DEBUG_SMB(("[auth] Prompting credentials for: uri=%s, server=%s, object=%s, username=%s, domain=%s, default_user=%s, default_domain=%s\n",
+		   in_args.uri,
+		   in_args.server,
+		   in_args.object,
+		   in_args.username,
+		   in_args.domain,
+		   in_args.default_user,
+		   in_args.default_domain));
 
 	invoked = gnome_vfs_module_callback_invoke
 		(GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION,
 		 &in_args, sizeof (in_args),
 		 &out_args, sizeof (out_args));
 
+	DEBUG_SMB(("[auth] vfs module callback for FULL_AUTHENTICATION returned invoked=%d\n", invoked));
+
 	if (invoked) {
                 if (in_args.flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) {
                      g_free (actx->use_user);
@@ -1038,10 +1246,7 @@
 		g_free (actx->keyring);
 		actx->save_auth = out_args.save_password;
 		actx->keyring = actx->save_auth && out_args.keyring ? g_strdup (out_args.keyring) : NULL;
-		DEBUG_SMB(("[auth] Prompted credentials: %s@%s:%s\n", 
-			    actx->use_user ? actx->use_user : "", 
-			    actx->use_domain ? actx->use_domain : "",
-			    actx->use_password ? actx->use_password : ""));
+		DEBUG_SMB(("[auth] Prompted credentials: %s@%s:%s keyring=%s\n", actx->use_user, actx->use_domain, actx->use_password, actx->keyring));
 	} 
 
 	*cancelled = out_args.abort_auth;
@@ -1055,6 +1260,9 @@
 
 	g_free (in_args.uri);
 
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+	DEBUG_OUT ();
+
 	return invoked && !*cancelled;
 }
 
@@ -1065,14 +1273,19 @@
 	GnomeVFSModuleCallbackSaveAuthenticationOut out_args;
 	gboolean invoked;
 
+	DEBUG_IN ();
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+
         /* Add to the user cache both with and without shares */
         if (actx->for_server) {
                 update_user_cache (actx, TRUE);
                 update_user_cache (actx, FALSE);
         }
 
-        if (!actx->save_auth)
+        if (!actx->save_auth) {
+		DEBUG_OUT ();
                 return;
+	}
       
         /* Save with the domain name */
 	memset (&in_args, 0, sizeof (in_args));
@@ -1101,6 +1314,8 @@
 		                &in_args, sizeof (in_args),
 		                &out_args, sizeof (out_args));
         
+	DEBUG_SMB(("[auth] vfs module callback for SAVE_AUTHENTICATION returned invoked=%d\n", invoked));
+
         g_free (in_args.uri);
               
         /* Save without the domain name */
@@ -1130,14 +1345,18 @@
                                 &in_args, sizeof (in_args),
                                 &out_args, sizeof (out_args));
         
+	DEBUG_SMB(("[auth] vfs module callback for SAVE_AUTHENTICATION returned invoked=%d\n", invoked));
+
 	g_free (in_args.uri);
+
+	DEBUG_OUT ();
 }
 	
 static void
 cleanup_authentication (SmbAuthContext *actx)
 {
 	/* IMPORTANT: We are IN the lock at this point */
-	
+	DEBUG_IN ();
 	DEBUG_SMB(("[auth] Cleaning up Authentication\n"));
 	g_return_if_fail (actx != NULL);
 	
@@ -1161,6 +1380,9 @@
 	
 	g_return_if_fail (current_auth_context == actx);
 	current_auth_context = NULL;
+
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+	DEBUG_OUT ();
 }
 
 /* 
@@ -1205,9 +1427,12 @@
 {
 	gboolean cont, auth_failed = FALSE, auth_cancelled = FALSE;
 	int ret = -1;
+
+	DEBUG_IN ();
 	
 	/* IMPORTANT: We are IN the lock at this point */
 	DEBUG_SMB(("[auth] perform_authentication called.\n"));
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
 	
 	switch (actx->res) {
 	case GNOME_VFS_OK:
@@ -1223,8 +1448,9 @@
 	
 	/* Other errors mean we're done */
 	default:
-		DEBUG_SMB(("[auth] Non-authentication error. Leaving auth loop.\n"));
+		DEBUG_SMB(("[auth] Non-authentication VFS error %d. Leaving auth loop.\n", (int) actx->res));
 		cleanup_authentication (actx);
+		DEBUG_OUT ();
 		return -1;
 	}
 	
@@ -1265,7 +1491,7 @@
 
 		/* A failed authentication */
 		} else if (actx->auth_called) {
-			
+
 			/* We need a server to perform any authentication */
 			g_return_val_if_fail (actx->for_server != NULL, GNOME_VFS_ERROR_INTERNAL);
 			
@@ -1276,9 +1502,12 @@
 			UNLOCK_SMB();
 			
 				/* Do we have gnome-keyring credentials for this? */
-				if (!(actx->state & SMB_AUTH_STATE_PREFILLED)) {
+			        if (!(actx->state & SMB_AUTH_STATE_PREFILLED)) {
+					DEBUG_SMB(("[auth] failed authentication; will prefill from the vfs callback\n"));
 					actx->state |= SMB_AUTH_STATE_PREFILLED;
 					cont = prefill_authentication (actx);
+				} else {
+					DEBUG_SMB(("[auth] failed authentication; will prompt the user for authentication\n"));
 				}
 
 				/* Then we try a guest credentials... */
@@ -1303,9 +1532,10 @@
 			g_return_val_if_fail (current_auth_context == NULL, GNOME_VFS_ERROR_INTERNAL);
 			current_auth_context = actx;
 			
-			if (cont)
+			if (cont) {
+				DEBUG_SMB(("[auth] prefill or prompt returned 1\n"));
 				ret = 1;
-			else {
+			} else {
 				ret = -1;
 
 				if (auth_cancelled) {
@@ -1320,12 +1550,15 @@
 					
 		/* Weird, don't want authentication, but failed */
 		} else {
+			DEBUG_SMB(("[auth] don't want authentication, but failed\n"));
 			ret = -1;
 		}
 	}
 
 	if (ret <= 0)
 		cleanup_authentication (actx);
+
+	DEBUG_OUT ();
 	return ret;
 
 	/* IMPORTANT: We need to still be in the lock when returning from this func */
@@ -1341,16 +1574,25 @@
 	SmbAuthContext *actx;
 	SMBCSRV *server;
 	
-	DEBUG_SMB (("[auth] auth_callback called: server: %s share: %s\n",
+	DEBUG_IN ();
+
+	DEBUG_SMB (("[auth] auth_callback called: server: %s share: %s domain_out: %s, username_out: %s, password_out=%s\n",
 		    server_name ? server_name : "", 
-		    share_name ? share_name : ""));
+		    share_name ? share_name : "",
+		    domain_out ? domain_out : "",
+		    username_out ? username_out : "",
+		    password_out ? password_out : ""));
 
 	g_return_if_fail (current_auth_context != NULL);
 	actx = current_auth_context;
 	
+	DEBUG_DUMP_AUTH_CONTEXT (actx);
+
 	/* We never authenticate for the toplevel (enumerating workgroups) */
-	if (!server_name || !server_name[0])
+	if (!server_name || !server_name[0]) {
+		DEBUG_OUT ();
 		return;
+	}
 
 	actx->auth_called = TRUE;	
 		
@@ -1361,8 +1603,10 @@
 	actx->for_share = string_dup_nzero (share_name);
 
 	/* The first pass, try the cache, fill in anything we know */
-	if (actx->passes == 1)
+	if (actx->passes == 1) {
+		DEBUG_SMB(("[auth] first pass; call initial_authentication()\n"));
 		initial_authentication (actx);
+	}
 	
 	/* If we have a valid user then go for it */
 	if (actx->use_user) {
@@ -1380,9 +1624,9 @@
 		g_assert (!actx->preset_user);
 		
 		if (actx->passes == 1)
-			DEBUG_SMB(("[auth] No credentials, trying anonymous user login\n"));
+			DEBUG_SMB(("[auth] No credentials, trying anonymous user login with empty password\n"));
 		else
-			DEBUG_SMB(("[auth] No credentials, returning null values\n"));
+			DEBUG_SMB(("[auth] No credentials, returning empty user and password\n"));
 		
 		strncpy (username_out, "", unmaxlen);
 		strncpy (password_out, "", pwmaxlen);
@@ -1398,15 +1642,30 @@
 	 * this doesn't make much sense, but such is life with libsmbclient.
 	 */
 	if ((actx->state & SMB_AUTH_STATE_PROMPTED) && actx->res != GNOME_VFS_OK) {
+		DEBUG_SMB(("[auth] we had prompted already but auth failed.  Calling find_cached_server() again\n"));
 		server = find_cached_server (server_name, share_name, domain_out, username_out);
 		if (server) {
 			DEBUG_SMB (("[auth] auth_callback. Remove the wrong server entry from server_cache.\n"));
 			g_hash_table_foreach_remove (server_cache, remove_server, server);
 		}
 	}
+
+	DEBUG_OUT ();
 }
 
 static char *
+get_printer_data (const char *display_name, const char *path)
+{
+	return g_strdup_printf ("[Desktop Entry]\n"
+			"Encoding=UTF-8\n"
+			"Name=%s\n"
+			"Type=Application\n"
+			"Exec=gnome-cups-add --printer=%s\n"
+			"Icon=printer-remote\n", /* per the freedesktop.org icon naming spec */
+			display_name, path);
+}
+                                                                                
+static char *
 get_workgroup_data (const char *display_name, const char *name)
 {
 	return g_strdup_printf ("[Desktop Entry]\n"
@@ -1453,6 +1712,21 @@
 	GnomeVFSFileOffset file_size;
 } FileHandle;
 
+/* Takes ownership of desktop_file_contents */
+static FileHandle *
+file_handle_new_from_desktop_file_contents (char *desktop_file_contents)
+{
+	FileHandle *handle;
+
+	handle = g_new (FileHandle, 1);
+	handle->is_data = TRUE;
+	handle->offset = 0;
+	handle->file_data = desktop_file_contents;
+	handle->file_size = strlen (handle->file_data);
+
+	return handle;
+}
+
 static GnomeVFSResult
 do_open (GnomeVFSMethod *method,
 	 GnomeVFSMethodHandle **method_handle,
@@ -1466,59 +1740,84 @@
 	int type;
 	mode_t unix_mode;
 	SMBCFILE *file = NULL;
-	
+	GnomeVFSResult result;
+
+	DEBUG_IN ();
 	DEBUG_SMB(("do_open() %s mode %d\n",
 				gnome_vfs_uri_to_string (uri, 0), mode));
 
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
+
+	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
+
+	if (is_printer (path)) {
+		if (mode & GNOME_VFS_OPEN_WRITE) {
+			result = GNOME_VFS_ERROR_READ_ONLY;
+			goto out;
+		}
+
+		unescaped_name = get_base_from_uri (uri);
+		name = gnome_vfs_uri_extract_short_path_name (uri);
+
+		handle = file_handle_new_from_desktop_file_contents (get_printer_data (unescaped_name, path));
+		*method_handle = (GnomeVFSMethodHandle *)handle;
+
+		g_free (unescaped_name);
+		g_free (name);
+
+		result = GNOME_VFS_OK;
+		goto out;
+	}
 	
 	if (type == SMB_URI_WHOLE_NETWORK ||
 	    type == SMB_URI_WORKGROUP ||
 	    type == SMB_URI_SERVER ||
 	    type == SMB_URI_SHARE) {
-		return GNOME_VFS_ERROR_IS_DIRECTORY;
+		result = GNOME_VFS_ERROR_IS_DIRECTORY;
+		goto out;
 	}
 
 	if (type == SMB_URI_WORKGROUP_LINK) {
 		if (mode & GNOME_VFS_OPEN_WRITE) {
-			return GNOME_VFS_ERROR_READ_ONLY;
+			result = GNOME_VFS_ERROR_READ_ONLY;
+			goto out;
 		}
-		handle = g_new (FileHandle, 1);
-		handle->is_data = TRUE;
-		handle->offset = 0;
+
 		unescaped_name = get_base_from_uri (uri);
 		name = gnome_vfs_uri_extract_short_path_name (uri);
-		handle->file_data = get_workgroup_data (unescaped_name, name);
-		handle->file_size = strlen (handle->file_data);
+
+		handle = file_handle_new_from_desktop_file_contents (get_workgroup_data (unescaped_name, name));
+		*method_handle = (GnomeVFSMethodHandle *)handle;
+
 		g_free (unescaped_name);
 		g_free (name);
 		
-		*method_handle = (GnomeVFSMethodHandle *)handle;
-		
-		return GNOME_VFS_OK;
+		result = GNOME_VFS_OK;
+		goto out;
 	}
 
 	if (type == SMB_URI_SERVER_LINK) {
 		if (mode & GNOME_VFS_OPEN_WRITE) {
-			return GNOME_VFS_ERROR_READ_ONLY;
+			result = GNOME_VFS_ERROR_READ_ONLY;
+			goto out;
 		}
-		handle = g_new (FileHandle, 1);
-		handle->is_data = TRUE;
-		handle->offset = 0;
+
 		unescaped_name = get_base_from_uri (uri);
 		name = gnome_vfs_uri_extract_short_path_name (uri);
-		handle->file_data = get_computer_data (unescaped_name, name);
-		handle->file_size = strlen (handle->file_data);
+
+		handle = file_handle_new_from_desktop_file_contents (get_computer_data (unescaped_name, name));
+		*method_handle = (GnomeVFSMethodHandle *)handle;
+
 		g_free (unescaped_name);
 		g_free (name);
 		
-		*method_handle = (GnomeVFSMethodHandle *)handle;
-		
-		return GNOME_VFS_OK;
+		result = GNOME_VFS_OK;
+		goto out;
 	}
 
 	g_assert (type == SMB_URI_SHARE_FILE);
@@ -1531,16 +1830,16 @@
 	} else {
 		if (mode & GNOME_VFS_OPEN_WRITE)
 			unix_mode = O_WRONLY;
-		else
-			return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
+		else {
+			result = GNOME_VFS_ERROR_INVALID_OPEN_MODE;
+			goto out;
+		}
 	}
 
 	if ((mode & GNOME_VFS_OPEN_TRUNCATE) ||
 	    (!(mode & GNOME_VFS_OPEN_RANDOM) && (mode & GNOME_VFS_OPEN_WRITE)))
 		unix_mode |= O_TRUNC;
 	
-	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
-	
 	LOCK_SMB();
 	init_authentication (&actx, uri);
 
@@ -1548,14 +1847,15 @@
 	while (perform_authentication (&actx) > 0) {
 		file = smb_context->open (smb_context, path, unix_mode, 0666);
 		actx.res = (file != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->open(\"%s\") returned file %p and error %d\n", path, file, (int) actx.res));
 	}
 
 	UNLOCK_SMB();
 
-	g_free (path);
-	
-	if (file == NULL)
+	if (file == NULL) {
+		DEBUG_OUT ();
 		return actx.res;
+	}
 	
 	handle = g_new (FileHandle, 1);
 	handle->is_data = FALSE;
@@ -1563,7 +1863,14 @@
 
 	*method_handle = (GnomeVFSMethodHandle *)handle;
 
-	return GNOME_VFS_OK;
+	result = GNOME_VFS_OK;
+
+ out:
+
+	g_free (path);
+
+	DEBUG_OUT ();
+	return result;
 }
 
 static GnomeVFSResult
@@ -1577,6 +1884,7 @@
 	GnomeVFSResult res;
 	int r;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("do_close()\n"));
 
 	res = GNOME_VFS_OK;
@@ -1595,6 +1903,7 @@
 			r = smb_context->close_fn (smb_context, handle->file);
 #endif
 			actx.res = (r >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+			DEBUG_SMB(("ctx->close(%p) returned error %d\n", handle->file, (int) actx.res));
 		}
 
 		res = actx.res;		
@@ -1602,6 +1911,7 @@
 	}
 
 	g_free (handle);
+	DEBUG_OUT ();
 	return res;
 }
 
@@ -1618,6 +1928,7 @@
 	SmbAuthContext actx;
 	ssize_t n = 0;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("do_read() %Lu bytes\n", num_bytes));
 
 	if (handle->is_data) {
@@ -1643,11 +1954,14 @@
 
 	*bytes_read = (n < 0) ? 0 : n;
 
-	if (n == 0) 
+	if (n == 0) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_EOF;
+	}
 
 	handle->offset += n;
 
+	DEBUG_OUT ();
 	return res;
 }
 
@@ -1665,10 +1979,13 @@
 	SmbAuthContext actx;
 	ssize_t written = 0;
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_write() %p\n", method_handle));
 
-	if (handle->is_data)
+	if (handle->is_data) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_READ_ONLY;
+	}
 
 	LOCK_SMB();
 	init_authentication (&actx, NULL);
@@ -1682,6 +1999,7 @@
 	UNLOCK_SMB();
 
 	*bytes_written = (written < 0) ? 0 : written;
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -1700,30 +2018,39 @@
 	SMBCFILE *file = NULL;
 	FileHandle *handle;
 	SmbAuthContext actx;
-	
+
+	DEBUG_IN ();
 	DEBUG_SMB (("do_create() %s mode %d\n",
 				gnome_vfs_uri_to_string (uri, 0), mode));
 
 	
 	type = smb_uri_type (uri);
 
-	if (type == SMB_URI_ERROR)
+	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
+	}
 
 	if (type == SMB_URI_WHOLE_NETWORK ||
 	    type == SMB_URI_WORKGROUP ||
 	    type == SMB_URI_SERVER ||
-	    type == SMB_URI_SHARE)
+	    type == SMB_URI_SHARE) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_IS_DIRECTORY;
+	}
 
 	if (type == SMB_URI_WORKGROUP_LINK ||
-	    type == SMB_URI_SERVER_LINK) 
+	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
+	}
 	
 	unix_mode = O_CREAT | O_TRUNC;
 	
-	if (!(mode & GNOME_VFS_OPEN_WRITE))
+	if (!(mode & GNOME_VFS_OPEN_WRITE)) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_OPEN_MODE;
+	}
 
 	if (mode & GNOME_VFS_OPEN_READ)
 		unix_mode |= O_RDWR;
@@ -1742,24 +2069,44 @@
 	while (perform_authentication (&actx) > 0) {
 		file = smb_context->open (smb_context, path, unix_mode, perm);
 		actx.res = (file != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->open(\"%s\") returned file %p and error %d\n", path, file, (int) actx.res));
 	}
 
 	UNLOCK_SMB();
 
 	g_free (path);
 
-	if (file == NULL)
+	if (file == NULL) {
+		DEBUG_OUT ();
 		return actx.res;
+	}
 	
 	handle = g_new (FileHandle, 1);
 	handle->is_data = FALSE;
 	handle->file = file;
 
 	*method_handle = (GnomeVFSMethodHandle *)handle;
-	
+
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
+static void
+set_file_info_to_readonly_desktop_file (GnomeVFSFileInfo *file_info, GnomeVFSURI *uri)
+{
+	file_info->name = get_base_from_uri (uri);
+	file_info->valid_fields = file_info->valid_fields
+		| GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE
+		| GNOME_VFS_FILE_INFO_FIELDS_TYPE
+		| GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
+	file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
+	file_info->mime_type = g_strdup ("application/x-desktop");
+	file_info->permissions =
+		GNOME_VFS_PERM_USER_READ |
+		GNOME_VFS_PERM_OTHER_READ |
+		GNOME_VFS_PERM_GROUP_READ;
+}
+
 static GnomeVFSResult
 do_get_file_info (GnomeVFSMethod *method,
 		  GnomeVFSURI *uri,
@@ -1774,19 +2121,34 @@
 	const char *mime_type;
 	SmbAuthContext actx;
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_get_file_info() %s\n",
 				gnome_vfs_uri_to_string (uri, 0)));
 
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
+
+	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
+
+	if (is_printer (path)) {
+		DEBUG_SMB (("is a printer we already saw; will fill in info\n"));
+
+		set_file_info_to_readonly_desktop_file (file_info, uri);
+		g_free (path);
+
+		DEBUG_OUT ();
+		return GNOME_VFS_OK;
+	}
 	
 	if (type == SMB_URI_WHOLE_NETWORK ||
 	    type == SMB_URI_WORKGROUP ||
 	    type == SMB_URI_SERVER ||
 	    type == SMB_URI_SHARE) {
+		DEBUG_SMB (("is whole network, workgroup, server, or share\n"));
 		file_info->name = get_base_from_uri (uri);
 		file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE |
 			GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
@@ -1807,29 +2169,22 @@
  				GNOME_VFS_PERM_OTHER_READ |
  				GNOME_VFS_PERM_GROUP_READ;
 		}
+		DEBUG_OUT();
 		return GNOME_VFS_OK;
 	}
 
 	if (type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
-		file_info->name = get_base_from_uri (uri);
-		file_info->valid_fields = file_info->valid_fields
-			| GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE
-			| GNOME_VFS_FILE_INFO_FIELDS_TYPE
-			| GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
-		file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
-		file_info->mime_type = g_strdup ("application/x-desktop");
-		file_info->permissions =
-			GNOME_VFS_PERM_USER_READ |
-			GNOME_VFS_PERM_OTHER_READ |
-			GNOME_VFS_PERM_GROUP_READ;
+		DEBUG_SMB (("is workgroup link, or server link\n"));
+		set_file_info_to_readonly_desktop_file (file_info, uri);
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
 	}
 
 	g_assert (type == SMB_URI_SHARE_FILE);
-	    
-	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
+	DEBUG_SMB (("is share file\n"));
+	    
 	LOCK_SMB();
 	init_authentication (&actx, uri);
 
@@ -1837,14 +2192,17 @@
 	while (perform_authentication (&actx) > 0) {
 		err = smb_context->stat (smb_context, path, &st);
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->stat(\"%s\") returned error %d\n", path, (int) actx.res));
 	}
 
 	UNLOCK_SMB();
 
 	g_free (path);
 
-	if (err < 0)
+	if (err < 0) {
+		DEBUG_OUT ();
 		return actx.res;
+	}
 	
 	gnome_vfs_stat_to_file_info (file_info, &st);
 	file_info->name = get_base_from_uri (uri);
@@ -1870,6 +2228,7 @@
 				file_info->name, type,
 				file_info->mime_type, file_info->type));
 
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -1885,6 +2244,7 @@
 	struct stat st;
 	int err = -1;
 
+	DEBUG_IN ();
 	LOCK_SMB();
 	init_authentication (&actx, NULL);
 	
@@ -1892,17 +2252,22 @@
 	while (perform_authentication (&actx) > 0) {
 		err = smb_context->fstat (smb_context, handle->file, &st);
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->fstat(%p) returned error %d\n", handle->file, (int) actx.res));
 	}
 	
 	UNLOCK_SMB();
 	
-	if (err < 0) 
+	if (err < 0) {
+		DEBUG_OUT ();
 		return actx.res;
+	}
 	
 	gnome_vfs_stat_to_file_info (file_info, &st);
 
 	file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE;
 	file_info->io_block_size = SMB_BLOCK_SIZE;
+
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -1910,7 +2275,19 @@
 do_is_local (GnomeVFSMethod *method,
 	     const GnomeVFSURI *uri)
 {
-	return FALSE;
+	char *path;
+	gboolean is_local;
+
+	/* FIXME: This is a hack.  In get_printer_data(), we generate data for a desktop item.  This item
+	 * is a "Type=Application" launcher, which launches gnome-cups-add.  However, since we can't execute
+	 * .desktop files from remote sites, we only advertise that printers "are local files".
+	 */
+
+	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
+	is_local = is_printer (path);
+	g_free (path);
+	
+	return is_local;
 }
 
 typedef struct {
@@ -1948,6 +2325,7 @@
 	SMBCFILE *dir = NULL;
 	SmbAuthContext actx;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("do_open_directory() %s\n",
 		gnome_vfs_uri_to_string (uri, 0)));
 
@@ -1959,12 +2337,14 @@
 		directory_handle = g_new0 (DirectoryHandle, 1);
 		g_hash_table_foreach (workgroups, add_workgroup, directory_handle);
 		*method_handle = (GnomeVFSMethodHandle *) directory_handle;
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
 	}
 
 	if (type == SMB_URI_ERROR ||
 	    type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_A_DIRECTORY;
 	}
 
@@ -1973,11 +2353,22 @@
 	host_name = gnome_vfs_uri_get_host_name (uri);
 	if (type == SMB_URI_WORKGROUP && host_name != NULL &&
 	    !g_ascii_strcasecmp(host_name, DEFAULT_WORKGROUP_NAME)) {
+		char *new_workgroup;
+
 		new_uri = gnome_vfs_uri_dup (uri);
-		gnome_vfs_uri_set_host_name (new_uri,
-					     smb_context->workgroup
-					     ? smb_context->workgroup
-					     : "WORKGROUP");
+		if (smb_context->workgroup)
+			new_workgroup = smb_context->workgroup;
+		else
+			new_workgroup = "WORKGROUP";
+
+		DEBUG_SMB (("we are being asked for %s; substituting it for workgroup \"%s\"%s\n",
+			    DEFAULT_WORKGROUP_NAME,
+			    new_workgroup,
+			    (smb_context->workgroup
+			     ? " because that is what was in the smbcctx->workgroup"
+			     : " because smbcctx->workgroup=NULL, so we use this as a last resort")));
+
+		gnome_vfs_uri_set_host_name (new_uri, new_workgroup);
 		uri = new_uri;
 	}
 		
@@ -1992,6 +2383,7 @@
 	while (perform_authentication (&actx) > 0) {
 		dir = smb_context->opendir (smb_context, path);
 		actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->opendir(\"%s\") returned dir %p and error %d\n", path, dir, (int) actx.res));
 	}
 
 	UNLOCK_SMB();
@@ -2001,6 +2393,7 @@
 	
 	if (dir == NULL) {
 		g_free (path);
+		DEBUG_OUT ();
 		return actx.res;
 	}
 	
@@ -2010,6 +2403,7 @@
 	directory_handle->path = path;
 	*method_handle = (GnomeVFSMethodHandle *) directory_handle;
 
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -2024,10 +2418,13 @@
 	GList *l;
 	int err = -1;
 
+	DEBUG_IN ();
 	DEBUG_SMB(("do_close_directory: %p\n", directory_handle));
 
-	if (directory_handle == NULL)
+	if (directory_handle == NULL) {
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
+	}
 
 	if (directory_handle->workgroups != NULL) {
 		for (l = directory_handle->workgroups; l != NULL; l = l->next) {
@@ -2046,6 +2443,7 @@
 		while (perform_authentication (&actx) > 0) {
 			err = smb_context->closedir (smb_context, directory_handle->dir);
 			actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+			DEBUG_SMB(("ctx->closedir(%p) returned error %d\n", directory_handle->dir, (int) actx.res));
 		}
 		
 		res = actx.res;
@@ -2054,9 +2452,46 @@
 	g_free (directory_handle->path);
 	g_free (directory_handle);
 
+	DEBUG_OUT ();
 	return res;
 }
 
+static char *
+make_path_from_uri_and_name (const char *path, const char *name)
+{
+	char *escaped_name;
+	char *new_path;
+
+	escaped_name = gnome_vfs_escape_string (name);
+
+	if (path[strlen(path) - 1] == '/') {
+		new_path = g_strconcat (path, escaped_name, NULL);
+	} else {
+		new_path = g_strconcat (path, "/", escaped_name, NULL);
+	}
+
+	g_free (escaped_name);
+
+	return new_path;
+}
+
+static void
+add_printer_to_hash (DirectoryHandle *dh,
+		     GnomeVFSFileInfo *file_info)
+{
+	char *path;
+
+	path = make_path_from_uri_and_name (dh->path, file_info->name);
+		
+	g_hash_table_insert (printer_hash, path, path);
+}
+
+static gboolean
+is_printer (const char *uri)
+{
+	return g_hash_table_lookup (printer_hash, uri) != NULL;
+}
+
 static GnomeVFSResult
 do_read_directory (GnomeVFSMethod *method,
 		   GnomeVFSMethodHandle *method_handle,
@@ -2068,14 +2503,15 @@
 	SmbAuthContext actx;
 	struct stat st;
 	char *statpath;
-	char *path;
 	int r = -1;
 	GList *l;
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_read_directory()\n"));
 
 	if (dh->dir == NULL) {
 		if (dh->workgroups == NULL) {
+			DEBUG_OUT ();
 			return GNOME_VFS_ERROR_EOF;
 		} else {
 			/* workgroup link */
@@ -2089,6 +2525,7 @@
 				| GNOME_VFS_FILE_INFO_FIELDS_TYPE;
 			file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
 			file_info->mime_type = g_strdup ("application/x-desktop");
+			DEBUG_OUT ();
 			return GNOME_VFS_OK;
 		}
 	}
@@ -2112,16 +2549,20 @@
 			} else {
 				actx.res = GNOME_VFS_OK;
 			}
+			DEBUG_SMB(("ctx->readdir(%p) returned entry %p and error %d\n", dh->dir, entry, (int) actx.res));
 		}
 		
 		if (entry == NULL) {
 			UNLOCK_SMB();
+			DEBUG_OUT ();
 			return actx.res;
 		}
 		
 	} while (entry->smbc_type == SMBC_COMMS_SHARE ||
 		 entry->smbc_type == SMBC_IPC_SHARE ||
+#if 0
 		 entry->smbc_type == SMBC_PRINTER_SHARE ||
+#endif
 		 entry->name == NULL ||
 		 strlen (entry->name) == 0 ||
 		 (entry->smbc_type == SMBC_FILE_SHARE &&
@@ -2151,29 +2592,24 @@
 		file_info->mime_type = g_strdup ("application/x-desktop");
 		break;
 	case SMBC_PRINTER_SHARE:
+#if 0
 		/* Ignored above for now */
+#endif
 		file_info->valid_fields = file_info->valid_fields
 			| GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE
 			| GNOME_VFS_FILE_INFO_FIELDS_TYPE;
 		file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
-		file_info->mime_type = g_strdup ("application/x-smb-printer");
+		file_info->mime_type = g_strdup ("application/x-desktop"); /* we'll generate the fake .desktop in do_open() */
+		add_printer_to_hash (dh, file_info);
+		debug_print ("GOT PRINTER: \"%s\"", file_info->name);
+		break;
 	case SMBC_COMMS_SHARE:
 	case SMBC_IPC_SHARE:
 		break;
 	case SMBC_DIR:
 	case SMBC_FILE:
-		path = dh->path;
-		
-		if (path[strlen(path)-1] == '/') {
-			statpath = g_strconcat (path, 
-						gnome_vfs_escape_string (file_info->name),
-						NULL);
-		} else {
-			statpath = g_strconcat (path,
-						"/",
-						gnome_vfs_escape_string (file_info->name),
-						NULL);
-		}
+		statpath = make_path_from_uri_and_name (dh->path, file_info->name);
+
 		/* TODO: might give an auth error, but should be rare due
 		   to the succeeding opendir. If this happens and we can't
 		   auth, we should terminate the readdir to avoid multiple
@@ -2187,6 +2623,7 @@
 		while (perform_authentication (&actx) > 0) {
 			r = smb_context->stat (smb_context, statpath, &st);
 			actx.res = (r == 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+			DEBUG_SMB(("ctx->stat(\"%s\") returned error %d\n", statpath, (int) actx.res));
 		}
 		UNLOCK_SMB();
 		
@@ -2221,6 +2658,7 @@
 		g_assert_not_reached ();
 	}
 
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -2236,6 +2674,8 @@
 	int meth_whence;
 	off_t ret = (off_t) -1;
 
+	DEBUG_IN ();
+
 	if (handle->is_data) {
 		switch (whence) {
 		case GNOME_VFS_SEEK_START:
@@ -2252,8 +2692,11 @@
 			}
 			break;
 		default:
+			DEBUG_OUT ();
 			return GNOME_VFS_ERROR_NOT_SUPPORTED;
 		}
+
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
 	}
 	
@@ -2268,6 +2711,7 @@
 		meth_whence = SEEK_END;
 		break;
 	default:
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 
@@ -2280,7 +2724,8 @@
 		actx.res = (ret != (off_t) -1) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
 	UNLOCK_SMB();
-	
+
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2293,8 +2738,11 @@
 	SmbAuthContext actx;
 	off_t ret = (off_t) -1;
 
+	DEBUG_IN ();
+
 	if (handle->is_data) {
 		*offset_return = handle->offset;
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
 	}
 	
@@ -2309,6 +2757,8 @@
 	UNLOCK_SMB();
 	
 	*offset_return = (ret == (off_t) -1) ? 0 : (GnomeVFSFileOffset) ret;
+
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2321,12 +2771,14 @@
 	SmbAuthContext actx;
 	int type, err = -1;
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_unlink() %s\n",
 				gnome_vfs_uri_to_string (uri, 0)));
 
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
 
@@ -2336,6 +2788,7 @@
 	    type == SMB_URI_SHARE ||
 	    type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
 	}
 
@@ -2348,12 +2801,14 @@
 	while (perform_authentication (&actx) > 0) {
 		err = smb_context->unlink (smb_context, path);
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->unlink(\"%s\") returned error %d\n", path, (int) actx.res));
 	}
 	
 	UNLOCK_SMB();
 
 	g_free (path);
-	
+
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2370,6 +2825,7 @@
 	char *path2;
 	char *p1, *p2;
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_check_same_fs()\n"));
 
 	server1 =
@@ -2390,6 +2846,7 @@
 		g_free (path1);
 		g_free (path2);
 		*same_fs_return = FALSE;
+		DEBUG_OUT ();
 		return GNOME_VFS_OK;
 	}
                              
@@ -2418,6 +2875,7 @@
 	g_free (path1);
 	g_free (path2);
 
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -2434,7 +2892,7 @@
 	SmbAuthContext actx;
 	int old_type, new_type;
 	
-	
+	DEBUG_IN ();
 	DEBUG_SMB (("do_move() %s %s\n",
 				gnome_vfs_uri_to_string (old_uri, 0),
 				gnome_vfs_uri_to_string (new_uri, 0)));
@@ -2444,6 +2902,7 @@
 
 	if (old_type != SMB_URI_SHARE_FILE ||
 	    new_type != SMB_URI_SHARE_FILE) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
 	}
 
@@ -2461,6 +2920,7 @@
 		err = smb_context->rename (smb_context, old_path, smb_context, new_path);
 		errnox = errno;
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->rename(\"%s\", \"%s\") returned error %d\n", old_path, new_path, (int) actx.res));
 	}
 	UNLOCK_SMB();
 	
@@ -2477,6 +2937,7 @@
 			while (perform_authentication (&actx) > 0) {			
 				err = smb_context->unlink (smb_context, new_path);
 				actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+				DEBUG_SMB(("ctx->unlink(\"%s\") returned error %d\n", new_path, (int) actx.res));
 			}
 			UNLOCK_SMB();
 
@@ -2493,6 +2954,7 @@
 	g_free (old_path);
 	g_free (new_path);
 
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2517,9 +2979,11 @@
 	int type, err = -1;
 	SmbAuthContext actx;
 
+	DEBUG_IN ();
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
 
@@ -2529,6 +2993,7 @@
 	    type == SMB_URI_SHARE ||
 	    type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
 	}
 
@@ -2542,12 +3007,14 @@
 	while (perform_authentication (&actx) > 0) {
 		err = smb_context->mkdir (smb_context, path, perm);
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->mkdir(\"%s\") returned error %d\n", path, (int) actx.res));
 	}
 
 	UNLOCK_SMB();
 
 	g_free (path);
 
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2560,9 +3027,11 @@
 	int err = -1, type;
 	SmbAuthContext actx;
 
+	DEBUG_IN ();
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
 
@@ -2572,6 +3041,7 @@
 	    type == SMB_URI_SHARE ||
 	    type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
 	}
 
@@ -2584,11 +3054,13 @@
 	while (perform_authentication (&actx) > 0) {
 		err = smb_context->rmdir (smb_context, path);
 		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		DEBUG_SMB(("ctx->rmdir(\"%s\") returned error %d\n", path, (int) actx.res));
 	}
 	UNLOCK_SMB();
 
 	g_free (path);
 
+	DEBUG_OUT ();
 	return actx.res;
 }
 
@@ -2603,11 +3075,13 @@
 	int err = -1, errnox = 0, type;
 	SmbAuthContext actx;	
 
+	DEBUG_IN ();
 	DEBUG_SMB (("do_set_file_info: mask %x\n", mask));
 
 	type = smb_uri_type (uri);
 
 	if (type == SMB_URI_ERROR) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_INVALID_URI;
 	}
 
@@ -2617,6 +3091,7 @@
 	    type == SMB_URI_SHARE ||
 	    type == SMB_URI_WORKGROUP_LINK ||
 	    type == SMB_URI_SERVER_LINK) {
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
 	}
 
@@ -2640,6 +3115,7 @@
 			err = smb_context->rename (smb_context, path, smb_context, new_path);
 			errnox = errno;
 			actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+			DEBUG_SMB(("ctx->rename(\"%s\", \"%s\") returned error %d\n", path, new_path, (int) actx.res));
 		}
 		
 		UNLOCK_SMB();
@@ -2652,31 +3128,37 @@
 
 		if (actx.res != GNOME_VFS_OK) {
 			g_free (path);
+			DEBUG_OUT ();
 			return actx.res;
 		}
 	}
 
 	if (gnome_vfs_context_check_cancellation (context)) {
 		g_free (path);
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_CANCELLED;
 	}
 
 	if (mask & GNOME_VFS_SET_FILE_INFO_PERMISSIONS) {
 		g_free (path);
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 
 	if (mask & GNOME_VFS_SET_FILE_INFO_OWNER) {
 		g_free (path);
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 	
 	if (mask & GNOME_VFS_SET_FILE_INFO_TIME) {
 		g_free (path);
+		DEBUG_OUT ();
 		return GNOME_VFS_ERROR_NOT_SUPPORTED;
 	}
 
 	g_free (path);
+	DEBUG_OUT ();
 	return GNOME_VFS_OK;
 }
 
@@ -2708,11 +3190,50 @@
 	NULL  /* do_create_symbolic_link */
 };
 
+static void
+debug_init (void)
+{
+	char *debug_flag_path;
+	struct stat st;
+
+	LOCK_SMB ();
+
+	debug_flag_path = g_build_filename (g_get_home_dir (), ".debug-gnome-vfs-smb", NULL);
+
+	if (stat (debug_flag_path, &st) == 0) {
+		char *debug_filename;
+
+		debug_filename = g_build_filename (g_get_home_dir (), "debug-gnome-vfs-smb.log", NULL);
+		debug_file = fopen (debug_filename, "w");
+		g_free (debug_filename);
+	} else
+		debug_file = NULL;
+
+	g_free (debug_flag_path);
+
+	UNLOCK_SMB ();
+}
+
+static void
+debug_shutdown (void)
+{
+	LOCK_SMB ();
+
+	if (debug_file) {
+		fclose (debug_file);
+		debug_file = NULL;
+	}
+
+	UNLOCK_SMB ();
+}
+
 GnomeVFSMethod *
 vfs_module_init (const char *method_name, const char *args)
 {
 	smb_lock = g_mutex_new ();
 
+	debug_init ();
+
 	DEBUG_SMB (("<-- smb module init called -->\n"));
 
 	if (try_init ()) {
@@ -2735,9 +3256,12 @@
 	g_hash_table_destroy (server_cache);
 	g_hash_table_destroy (workgroups);
         g_hash_table_destroy (user_cache);
+	g_hash_table_destroy (printer_hash);
 	
 	g_mutex_free (smb_lock);
 
 	DEBUG_SMB (("<-- smb module shutdown called -->\n"));
+
+	debug_shutdown ();
 }