SMB module authentication rewrite patch



Attached is a patch which contains a rewrite of the SMB module's authentication. This is to prevent deadlocks and fix other problems discussed on this list in November.

The patch compiles, but hasn't seen any testing yet. I'll go through and test each change in time. Just thought I'd post it and see if others had any ideas or can spot completely broken logic.

Cheers,
Nate
Index: smb-method.c
===================================================================
RCS file: /cvs/gnome/gnome-vfs/modules/smb-method.c,v
retrieving revision 1.12
diff -u -3 -p -r1.12 smb-method.c
--- smb-method.c	11 Oct 2004 08:53:06 -0000	1.12
+++ smb-method.c	5 Jan 2005 02:45:04 -0000
@@ -102,40 +102,55 @@ static guint server_cache_reap_timeout =
 #define WORKGROUP_CACHE_TIMEOUT (5*60)
 
 static GHashTable *workgroups = NULL;
-static int workgroups_errno;
 static time_t workgroups_timestamp = 0;
 
 static GHashTable *default_user_hashtable = NULL;
 
-static GnomeVFSURI *current_uri = NULL;
+/* Authentication ----------------------------------------------------------- */
 
-/* Auth stuff: */
+#define SMB_AUTH_STATE_PREFILLED	0x00000010 	/* Have asked gnome-auth for prefilled auth */
+#define SMB_AUTH_STATE_PROMPTED		0x00000020 	/* Have asked gnome-auth for to prompt user */
 
-static gboolean done_pre_auth;
-static gboolean done_auth;
-static gboolean auth_cancelled;
-static gboolean auth_save_password;
-static char *auth_keyring;
-static char *last_pwd;
+/* TODO: Move this to the top of the file */
+typedef struct _SmbAuthContext {
+
+	GnomeVFSURI *uri;		/* Uri being worked with. Does not own this URI */
+	GnomeVFSResult res;		/* Current error code */
+	
+	/* Internal state */
+	guint passes;			/* Number of passes through authentication code */
+	guint state;			/* Various flags (above) */
+	
+	/* For saving passwords into gnome-keyring */
+	gchar *keyring;			/* The keyring to save passwords in */
+	
+	/* Used in chat between perform_authentication and auth_callback */
+	gboolean auth_called;		/* Set by auth_callback */
+	gchar *for_server;		/* Set by auth_callback */
+	gchar *for_share;		/* Set by auth_callback */
+	gchar *use_user;		/* Set by perform_authentication */
+	gchar *use_domain;		/* Set by perform_authentication */
+	gchar *use_password;		/* Set by perform_authentication */
+	
+} SmbAuthContext;
+
+static void init_authentication (SmbAuthContext *actx, GnomeVFSURI *uri);
+static int  perform_authentication (SmbAuthContext *actx);
+
+static SmbAuthContext *current_auth_context = NULL;
 
 /* Used to detect failed logins */
 static gboolean cache_access_failed = FALSE;
 
-static void init_auth (GnomeVFSURI *uri);
+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 gboolean invoke_save_auth (const char *server,
-				  const char *share,
-				  const char *username,
-				  const char *domain,
-				  const char *password,
-				  const char *keyring);
 
-#if 0
 #define DEBUG_SMB_ENABLE
-#endif
-#if 0
 #define DEBUG_SMB_LOCKS
-#endif
 
 #ifdef DEBUG_SMB_ENABLE
 #define DEBUG_SMB(x) g_print x
@@ -151,10 +166,22 @@ static gboolean invoke_save_auth (const 
 #define UNLOCK_SMB() 	g_mutex_unlock (smb_lock)
 #endif
 
-static void auth_fn (const char *server_name, const char *share_name,
-		     char *domain, int domainmaxlen,
-		     char *username, int unmaxlen,
-		     char *password, int pwmaxlen);
+static gchar*
+string_dup_nzero (const gchar *s)
+{
+	if (!s || !s[0])
+		return NULL;
+	return g_strdup (s);
+}
+
+static gchar*
+string_ndup_nzero (const gchar *s, const guint n)
+{
+	if(!s || !s[0] || !n)
+		return NULL;
+	return g_strndup (s, n);
+}
+	     	   
 static gboolean
 string_compare (const char *a, const char *b)
 {
@@ -324,7 +351,6 @@ add_cached_server (SMBCCTX *context, SMB
 	SmbServerCacheEntry *entry = NULL;
 	GnomeVFSToplevelURI *toplevel;
 	SmbDefaultUser *default_user;
-	const char *ask_share_name;
 
 	DEBUG_SMB(("add_cached_server: server: %s, share: %s, domain: %s, user: %s\n",
 		   server_name, share_name, domain, username));
@@ -335,42 +361,30 @@ add_cached_server (SMBCCTX *context, SMB
 	
 	entry->server = new;
 	
-	entry->server_name = g_strdup (server_name);
-	entry->share_name = g_strdup (share_name);
-	entry->domain = g_strdup (domain);
-	entry->username = g_strdup (username);
+	entry->server_name = string_dup_nzero (server_name);
+	entry->share_name = string_dup_nzero (share_name);
+	entry->domain = string_dup_nzero (domain);
+	entry->username = string_dup_nzero (username);
 	entry->last_time = time (NULL);
 
 	g_hash_table_insert (server_cache, entry, entry);
 
 	cache_access_failed = FALSE;
 
-	if (current_uri != NULL) {
-		toplevel = (GnomeVFSToplevelURI *)current_uri;
+	if (current_auth_context && current_auth_context->uri != NULL) {
+		toplevel = (GnomeVFSToplevelURI *)current_auth_context->uri;
 
 		if (toplevel->user_name == NULL ||
 		    toplevel->user_name[0] == 0) {
 			default_user = g_new0 (SmbDefaultUser, 1);
-			default_user->server_name = g_strdup (server_name);
-			default_user->share_name = g_strdup (share_name);
-			default_user->username = g_strdup (username);
-			default_user->domain = g_strdup (domain);
+			default_user->server_name = string_dup_nzero (server_name);
+			default_user->share_name = string_dup_nzero (share_name);
+			default_user->username = string_dup_nzero (username);
+			default_user->domain = string_dup_nzero (domain);
 			g_hash_table_insert (default_user_hashtable, default_user, default_user);
 		}
 	}
 
-	if (auth_save_password) {
-		if (strcmp (share_name,"IPC$") == 0) {
-			ask_share_name = NULL;
-		} else {
-			ask_share_name = share_name;
-		}
-		invoke_save_auth (server_name, ask_share_name,
-				  username, domain,
-				  last_pwd?last_pwd:"",
-				  auth_keyring);
-	}
-	
 	return 0;
 }
 
@@ -488,7 +502,8 @@ remove_all (gpointer  key,
 static void
 update_workgroup_cache (void)
 {
-	SMBCFILE *dir;
+	SmbAuthContext actx;
+	SMBCFILE *dir = NULL;
 	time_t t;
 	struct smbc_dirent *dirent;
 	
@@ -507,25 +522,30 @@ update_workgroup_cache (void)
 	g_hash_table_foreach_remove (workgroups, remove_all, NULL);
 	
 	LOCK_SMB();
-	workgroups_errno = 0;
-	init_auth (NULL);
-	dir = smb_context->opendir (smb_context, "smb://");
+	
+	init_authentication (&actx, NULL);
+	
+	/* Important: perform_authentication leaves and re-enters the lock! */
+	while (perform_authentication (&actx) > 0) {
+		dir = smb_context->opendir (smb_context, "smb://");
+		actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+	}
+
 	if (dir != NULL) {
 		while ((dirent = smb_context->readdir (smb_context, dir)) != NULL) {
-			if (dirent->smbc_type == SMBC_WORKGROUP &&
-			    dirent->name != NULL &&
-			    strlen (dirent->name) > 0) {
+		       	if (dirent->smbc_type == SMBC_WORKGROUP && dirent->name != NULL &&
+		    	    strlen (dirent->name) > 0) {
 				g_hash_table_insert (workgroups,
 						     g_ascii_strdown (dirent->name, -1),
 						     GINT_TO_POINTER (1));
-			} else {
+		    	} else {
 				g_warning ("non-workgroup at smb toplevel\n");
 			}
-		}
+		} 
+
 		smb_context->closedir (smb_context, dir);
-	} else {
-		workgroups_errno = errno;
 	}
+		
 	UNLOCK_SMB();
 }
 
@@ -617,7 +637,7 @@ try_init (void)
 	smb_context = smbc_new_context ();
 	if (smb_context != NULL) {
 		smb_context->debug = 0;
-		smb_context->callbacks.auth_fn = auth_fn;
+		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;
@@ -628,10 +648,14 @@ try_init (void)
 			smb_context = NULL;
 		}
 
-#if defined(HAVE_SAMBA_FLAGS) && defined(SMB_CTX_FLAG_USE_KERBEROS) && defined(SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS)
+#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;
 #endif
-		
+#if defined(SMBCCTX_FLAG_NO_AUTO_ANON)
+		smb_context->flags |= SMBCCTX_FLAG_NO_AUTO_ANON;
+#endif
+#endif		
 	}
 
 	server_cache = g_hash_table_new_full (server_hash,
@@ -656,52 +680,128 @@ try_init (void)
 	return TRUE;
 }
 
+
+/* Authentication ----------------------------------------------------------- */
+
+static char*
+get_auth_display_uri (SmbAuthContext *actx)
+{
+        if (actx->uri != NULL)
+		return gnome_vfs_uri_to_string (actx->uri, 0);
+    	else
+        	return g_strdup_printf ("smb://%s%s%s%s", 
+					actx->for_server ? actx->for_server : "", 
+					actx->for_server ? "/" : "",
+					actx->for_share ? actx->for_share : "",
+					actx->for_share ? "/" : "");
+}
+
+static gboolean
+initial_authentication (SmbAuthContext *actx)
+{
+	/* IMPORTANT: We are IN the lock at this point */
+	
+	GnomeVFSToplevelURI *toplevel_uri;
+	SmbServerCacheEntry server_lookup;
+	SmbServerCacheEntry *server;
+	SmbDefaultUser *default_user;
+	SmbDefaultUser lookup;
+	char *tmp;
+
+	toplevel_uri =	(GnomeVFSToplevelURI*)actx->uri;
+	
+	/* Try parsing a user and domain out of the URI */
+	if (toplevel_uri && toplevel_uri->user_name != NULL && 
+  	    toplevel_uri->user_name[0] != 0) {
+
+		tmp = strchr (toplevel_uri->user_name, ';');
+		if (tmp != NULL) {
+			g_free (actx->use_domain);
+			actx->use_domain = string_ndup_nzero (toplevel_uri->user_name,
+					    	  	      tmp - toplevel_uri->user_name);
+			g_free (actx->use_user);
+			actx->use_user = string_dup_nzero (tmp + 1);
+		} else {
+			g_free (actx->use_user);
+			actx->use_user = string_dup_nzero (toplevel_uri->user_name);
+			g_free (actx->use_domain);
+			actx->use_domain = NULL;
+		}
+
+	/* Lookup a default user and domain */
+	} else {
+		
+		/* lookup default user/domain */
+		lookup.server_name = actx->for_server;
+		lookup.share_name = actx->for_share;
+
+		default_user = g_hash_table_lookup (default_user_hashtable, &lookup);
+		if (default_user != NULL) {
+			g_free (actx->use_user);
+			actx->use_user = string_dup_nzero (default_user->username);
+			g_free (actx->use_domain);
+			actx->use_domain = string_dup_nzero (default_user->domain);
+		}
+	}
+	
+	/* Lookup the password in our internal cache */
+	if (actx->use_user != NULL && actx->use_user[0] != 0) {
+		
+		server_lookup.server_name = (char*)actx->for_server;
+		server_lookup.share_name = (char*)actx->for_share;
+		server_lookup.username = (char*)actx->use_user;
+		server_lookup.domain = (char*)actx->use_domain;
+		
+		server = g_hash_table_lookup (server_cache, &server_lookup);
+		if (server != NULL) {
+			/* Server is in cache already, no need to get password */
+			g_free (actx->use_password);
+			actx->use_password = g_strdup ("");
+			return TRUE;
+		}
+	}	
+
+	/* TODO: Complete full cache authentication loading */
+	return FALSE;
+}
+
 static gboolean
-invoke_fill_auth (const char *server,
-		 const char *share,
-		 const char *username,
-		 const char *domain,
-		 char **username_out,
-		 char **domain_out,
-		 char **password_out)
+prefill_authentication (SmbAuthContext *actx)
 {
+	/* IMPORTANT: We are NOT IN the lock at this point */
+	
 	GnomeVFSModuleCallbackFillAuthenticationIn in_args;
 	GnomeVFSModuleCallbackFillAuthenticationOut out_args;
 	gboolean invoked;
-
-	if (username != NULL && username[0] == 0) {
-		username = NULL;
-	}
-	if (domain != NULL && domain[0] == 0) {
-		domain = NULL;
-	}
+	
+	g_return_val_if_fail (actx != NULL, FALSE);
+	g_return_val_if_fail (actx->for_server != NULL, FALSE);	
 
 	memset (&in_args, 0, sizeof (in_args));
-	in_args.uri = gnome_vfs_uri_to_string (current_uri, 0);
+	in_args.uri = get_auth_display_uri (actx);
 	in_args.protocol = "smb";
-	in_args.server = (char *)server;
-	in_args.object = (char *)share;
-	in_args.username = (char *)username;
-	in_args.domain = (char *)domain;
-	in_args.port = ((GnomeVFSToplevelURI *)current_uri)->host_port;
+	in_args.server = (char*)actx->for_server;
+	in_args.object = (char*)actx->for_share;
+	in_args.username = (char*)actx->use_user;
+	in_args.domain = (char*)actx->use_domain;
+	in_args.port = actx->uri ? ((GnomeVFSToplevelURI*)actx->uri)->host_port : 0;
 
 	memset (&out_args, 0, sizeof (out_args));
 
 	invoked = gnome_vfs_module_callback_invoke
-		(GNOME_VFS_MODULE_CALLBACK_FILL_AUTHENTICATION,
-		 &in_args, sizeof (in_args),
-		 &out_args, sizeof (out_args));
+			(GNOME_VFS_MODULE_CALLBACK_FILL_AUTHENTICATION,
+			 &in_args, sizeof (in_args),
+			 &out_args, sizeof (out_args));
 
 	if (invoked && out_args.valid) {
-		*username_out = g_strdup (out_args.username);
-		*domain_out = g_strdup (out_args.domain);
-		*password_out = g_strdup (out_args.password);
-	} else {
-		*username_out = NULL;
-		*domain_out = NULL;
-		*password_out = NULL;
-	}
-
+		g_free (actx->use_user);
+		actx->use_user = string_dup_nzero (out_args.username);
+		g_free (actx->use_domain);
+		actx->use_domain = string_dup_nzero (out_args.domain);
+		g_free (actx->use_password);
+		actx->use_password = g_strdup (out_args.password);
+	} 
+	
 	g_free (in_args.uri);
 	g_free (out_args.username);
 	g_free (out_args.domain);
@@ -711,68 +811,56 @@ invoke_fill_auth (const char *server,
 }
 
 static gboolean
-invoke_full_auth (const char *server,
-		  const char *share,
-		  const char *username,
-		  const char *domain,
-		  gboolean *cancel_auth_out,
-		  char **username_out,
-		  char **domain_out,
-		  char **password_out,
-		  gboolean *save_password_out,
-		  char **keyring_out)
+prompt_authentication (SmbAuthContext *actx)
 {
+	/* IMPORTANT: We are NOT in the lock at this point */
+
 	GnomeVFSModuleCallbackFullAuthenticationIn in_args;
 	GnomeVFSModuleCallbackFullAuthenticationOut out_args;
-	gboolean invoked;
+	gboolean invoked, cancelled = FALSE;
 	
-	if (username != NULL && username[0] == 0) {
-		username = NULL;
-	}
-	if (domain != NULL && domain[0] == 0) {
-		domain = NULL;
-	}
+	g_return_val_if_fail (actx != NULL, FALSE);
+	g_return_val_if_fail (actx->for_server != NULL, FALSE);
 	
 	memset (&in_args, 0, sizeof (in_args));
-	in_args.uri = gnome_vfs_uri_to_string (current_uri, 0);
+	
 	in_args.flags = GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_PASSWORD | GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_SAVING_SUPPORTED;
-	if (done_auth) {
+	if (actx->state & SMB_AUTH_STATE_PROMPTED)
 		in_args.flags |= GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_PREVIOUS_ATTEMPT_FAILED;
-	}
-	if (((GnomeVFSToplevelURI *)current_uri)->user_name == NULL) {
-		in_args.flags |=
-			GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME |
-			GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN;
-	}
+	if (actx->use_user == NULL)
+		in_args.flags |= GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME;
+	if (actx->use_domain == NULL)
+		in_args.flags |= GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN;
+
+	in_args.uri = get_auth_display_uri (actx);
 	in_args.protocol = "smb";
-	in_args.server = (char *)server;
-	in_args.object = (char *)share;
-	in_args.username = (char *)username;
-	in_args.domain = (char *)domain;
-	in_args.port = ((GnomeVFSToplevelURI *)current_uri)->host_port;
+	in_args.server = (char*)actx->for_server;
+	in_args.object = (char*)actx->for_share;
+	in_args.username = (char*)actx->use_user;
+	in_args.domain = (char*)actx->use_domain;
+	in_args.port = actx->uri ? ((GnomeVFSToplevelURI*)actx->uri)->host_port : 0;
 
 	/* TODO: set default_user & default_domain? */
 	
 	memset (&out_args, 0, sizeof (out_args));
 
 	invoked = gnome_vfs_module_callback_invoke
-		(GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION,
-		 &in_args, sizeof (in_args),
-		 &out_args, sizeof (out_args));
+			(GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION,
+		 	 &in_args, sizeof (in_args),
+		 	 &out_args, sizeof (out_args));
 
 	if (invoked) {
-		*cancel_auth_out = out_args.abort_auth;
-		*username_out = g_strdup (out_args.username);
-		*domain_out = g_strdup (out_args.domain);
-		*password_out = g_strdup (out_args.password);
-		*save_password_out = out_args.save_password;
-		*keyring_out = g_strdup (out_args.keyring);
-	} else {
-		*cancel_auth_out = FALSE;
-		*username_out = NULL;
-		*domain_out = NULL;
-		*password_out = NULL;
-	}
+		cancelled = out_args.abort_auth;
+		g_free (actx->use_user);
+		actx->use_user = string_dup_nzero (out_args.username);
+		g_free (actx->use_domain);
+		actx->use_domain = string_dup_nzero (out_args.domain);
+		g_free (actx->use_password);
+		actx->use_password = g_strdup (out_args.password);
+		g_free (actx->keyring);
+		actx->keyring = out_args.save_password ? string_dup_nzero (out_args.keyring) : NULL;
+	} 
+	
 	g_free (out_args.username);
 	g_free (out_args.domain);
 	g_free (out_args.password);
@@ -780,262 +868,253 @@ invoke_full_auth (const char *server,
 
 	g_free (in_args.uri);
 
-	return invoked;
+	return invoked && !cancelled;
 }
 
-
-static gboolean
-invoke_save_auth (const char *server,
-		  const char *share,
-		  const char *username,
-		  const char *domain,
-		  const char *password,
-		  const char *keyring)
+static void
+save_authentication (SmbAuthContext *actx)
 {
 	GnomeVFSModuleCallbackSaveAuthenticationIn in_args;
 	GnomeVFSModuleCallbackSaveAuthenticationOut out_args;
 	gboolean invoked;
-
-	if (username != NULL && username[0] == 0) {
-		username = NULL;
-	}
-	if (domain != NULL && domain[0] == 0) {
-		domain = NULL;
-	}
-	if (keyring != NULL && keyring[0] == 0) {
-		keyring = NULL;
-	}
 	
+	if (actx->keyring == NULL)
+		return;
+
 	memset (&in_args, 0, sizeof (in_args));
-	in_args.uri = gnome_vfs_uri_to_string (current_uri, 0);
-	in_args.keyring = (char *)keyring;
+	in_args.uri = get_auth_display_uri (actx);
+	in_args.keyring = (char*)actx->keyring;
 	in_args.protocol = "smb";
-	in_args.server = (char *)server;
-	in_args.object = (char *)share;
-	in_args.port = ((GnomeVFSToplevelURI *)current_uri)->host_port;
+	in_args.server = (char*)actx->for_server;
+	in_args.object = (char*)actx->for_share;
+	in_args.port = actx->uri ? ((GnomeVFSToplevelURI*)actx->uri)->host_port : 0;
 	in_args.authtype = NULL;
-	in_args.username = (char *)username;
-	in_args.domain = (char *)domain;
-	in_args.password = (char *)password;
+	in_args.username = (char*)actx->use_user;
+	in_args.domain = (char*)actx->use_domain;
+	in_args.password = (char*)actx->use_password;
 
 	memset (&out_args, 0, sizeof (out_args));
 
 	invoked = gnome_vfs_module_callback_invoke
-		(GNOME_VFS_MODULE_CALLBACK_SAVE_AUTHENTICATION,
-		 &in_args, sizeof (in_args),
-		 &out_args, sizeof (out_args));
+			(GNOME_VFS_MODULE_CALLBACK_SAVE_AUTHENTICATION,
+		 	 &in_args, sizeof (in_args),
+		 	 &out_args, sizeof (out_args));
 
 	g_free (in_args.uri);
-	return invoked;
 }
-
-
+	
 static void
-init_auth (GnomeVFSURI *uri)
+cleanup_authentication (SmbAuthContext *actx)
 {
-	done_pre_auth = FALSE;
-	done_auth = FALSE;
-	auth_cancelled = FALSE;
-	cache_access_failed = FALSE;
-	current_uri = uri;
-	auth_save_password = FALSE;
-	if (last_pwd != NULL) {
-		memset (last_pwd, 0, strlen (last_pwd));
-		g_free (last_pwd);
-		last_pwd = NULL;
-	}
-}
+	/* IMPORTANT: We are IN the lock at this point */
+	
+	g_return_if_fail (actx != NULL);
+	
+	g_free (actx->for_server);
+	actx->for_server = NULL;
+	
+	g_free (actx->for_share);
+	actx->for_share = NULL;
+	
+	g_free (actx->use_user);
+	actx->use_user = NULL;
+	
+	g_free (actx->use_domain);
+	actx->use_domain = NULL;
+	
+	g_free (actx->use_password);
+	actx->use_password = NULL;
+	
+	g_free (actx->keyring);
+	actx->keyring = NULL;
+	
+	g_return_if_fail (current_auth_context == actx);
+	current_auth_context = NULL;
+}
+
+/* 
+ * This is the workhorse of all the authentication and caching work.
+ * It is called in a loop, and must be called from within the lock:
+ * 
+ * static GnomeVFSResult
+ * function_xxxx (GnomeVFSURI* uri)
+ * {
+ * 	SmbAuthContext actx;
+ * 
+ * 	LOCK_SMB ();
+ * 	init_authentication (&actx);
+ * 
+ * 	while (perform_authentication (&actx) > 0) {
+ * 		actx.res = gnome_vfs_result_from_errno_code (the_operation_here ());
+ * 	}
+ * 
+ * 	UNLOCK_SMB();
+ * 
+ * 	return actx.err;
+ * }
+ * 
+ * On different passes it performs seperate operations, such as checking
+ * the cache, prompting the user for a password, saving valid passwords, 
+ * cleaning up etc.... 
+ * 
+ * The loop must never be broken on it's own when perform_authentication
+ * has not returned a 0 or negative return value.
+ */
 
-static gboolean
-auth_failed (void)
+static void 
+init_authentication (SmbAuthContext *actx, GnomeVFSURI *uri)
 {
-	return cache_access_failed &&
-		(errno == EACCES || errno == EPERM) &&
-		!auth_cancelled;
+	memset (actx, 0, sizeof(*actx));
+	actx->uri = uri;
+	cache_access_failed = FALSE;
 }
 
-static void
-auth_fn (const char *server_name, const char *share_name,
-	 char *domain_out, int domainmaxlen,
-	 char *username_out, int unmaxlen,
-	 char *password_out, int pwmaxlen)
-{
-	char *username, *domain, *tmp;
-	char *real_username, *real_domain, *real_password;
-	char *ask_share_name;
-	GnomeVFSToplevelURI *current_toplevel_uri;
-	gboolean cancel_auth;
-	gboolean got_default_user;
-
-	DEBUG_SMB (("auth_fn called: server: %s share: %s\n",
-		    server_name, share_name));
-
-	if (server_name == NULL || server_name[0] == 0) {
-		/* We never authenticate for the toplevel (enumerating workgroups) */
-		return;
+static int
+perform_authentication (SmbAuthContext *actx)
+{
+	gboolean cont, auth_failed = FALSE;
+	int ret = -1;
+	
+	/* IMPORTANT: We are IN the lock at this point */
+	
+	switch (actx->res) {
+	case GNOME_VFS_OK:
+		auth_failed = FALSE;
+		break;
+		
+	/* Authentication errors are special */
+	case GNOME_VFS_ERROR_ACCESS_DENIED:
+	case GNOME_VFS_ERROR_NOT_PERMITTED:
+	case GNOME_VFS_ERROR_LOGIN_FAILED:
+		auth_failed = TRUE;
+		break;
+	
+	/* Other errors mean we're done */
+	default:
+		cleanup_authentication (actx);
+		return -1;
 	}
+	
+	/* TODO: What about cache_access_failed? */
+	
+	actx->passes++;
 
-	if (current_uri == NULL) {
-		/* TODO: What to do with this,
-		   comes from e.g. enumerating workgroups which needs login on
-		   master browser $IPC.
-		*/
-		DEBUG_SMB (("auth_fn - no current_uri, ignoring\n"));
-		return;
-	}
+	/* First pass */
+	if (actx->passes == 1) {
 	
-	got_default_user = FALSE;
-	username = NULL;
-	domain = NULL;
-	current_toplevel_uri =	(GnomeVFSToplevelURI *)current_uri;
-	if (current_toplevel_uri->user_name != NULL &&
-	    current_toplevel_uri->user_name[0] != 0) {
-		tmp = strchr (current_toplevel_uri->user_name, ';');
-		if (tmp != NULL) {
-			domain = g_strndup (current_toplevel_uri->user_name,
-					    tmp - current_toplevel_uri->user_name);
-			username = g_strdup (tmp + 1);
-		} else {
-			username = g_strdup (current_toplevel_uri->user_name);
-			domain = NULL;
-		}
+		/* Our auth context is the global one for the moment */
+		g_return_val_if_fail (current_auth_context == NULL, GNOME_VFS_ERROR_INTERNAL);
+		current_auth_context = actx;
+			
+		/* Continue with perform_authentication loop ... */
+		ret = 1;
+		
+	/* Subsequent passes */
 	} else {
-		SmbDefaultUser lookup;
-		SmbDefaultUser *default_user;
+
+		/* We should still be the global context at this point */
+		g_return_val_if_fail (current_auth_context == actx, GNOME_VFS_ERROR_INTERNAL);
 		
-		/* lookup default user/domain */
-		lookup.server_name = (char *)server_name;
-		lookup.share_name = (char *)share_name;
+		/* A successful operation. Done! */
+		if (!auth_failed) {
+			
+			if (actx->auth_called)
+				save_authentication (actx);
+			ret = 0;
+	
+		/* 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);
+			
+			/* We won't be the global context for now */
+			current_auth_context = NULL;
+			cont = FALSE;
+			
+			UNLOCK_SMB();
 
-		default_user = g_hash_table_lookup (default_user_hashtable, &lookup);
-		if (default_user != NULL) {
-			got_default_user = TRUE;
-			username = g_strdup (default_user->username);
-			domain = g_strdup (default_user->domain);
+				if (!(actx->state & SMB_AUTH_STATE_PREFILLED)) {
+					actx->state |= SMB_AUTH_STATE_PREFILLED;
+					cont = prefill_authentication (actx);
+				}
+				
+				if (!cont)
+					cont = prompt_authentication (actx);
+				
+			LOCK_SMB();
+			
+			/* Claim the global context back */
+			g_return_val_if_fail (current_auth_context == NULL, GNOME_VFS_ERROR_INTERNAL);
+			current_auth_context = actx;
+			
+			if (cont)
+				ret = 1;
+			else {
+				actx->res = GNOME_VFS_ERROR_CANCELLED;
+				ret = -1;
+			}
+					
+		/* Weird, don't want authentication, but failed */
+		} else {
+			ret = -1;
 		}
 	}
 
-	if (strcmp (share_name,"IPC$") == 0) {
-		/* Don't authenticate to IPC$ using dialog, but allow name+domain in uri */
-		if (username != NULL) {
-			strncpy (username_out, username, unmaxlen);
-		}
-		if (domain != NULL) {
-			strncpy (domain_out, domain, domainmaxlen);
-		}
-		strncpy (password_out, "", pwmaxlen);
-		g_free (username);
-		g_free (domain);
-		return;
-	}
+	if (ret <= 0)
+		cleanup_authentication (actx);
+	return ret;
 
-	if (got_default_user ||
-	    (username != NULL && username[0] != 0)) {
-		SmbServerCacheEntry server_lookup;
-		SmbServerCacheEntry *server;
-		
-		server_lookup.server_name = (char *)server_name;
-		server_lookup.share_name = (char *)share_name;
-		server_lookup.username = username;
-		server_lookup.domain = domain;
-		
-		server = g_hash_table_lookup (server_cache, &server_lookup);
-		if (server != NULL) {
-			strncpy (username_out, username, unmaxlen);
-			if (domain != NULL) {
-				strncpy (domain_out, domain, domainmaxlen);
-			} 
-			strncpy (password_out, "", pwmaxlen);
+	/* IMPORTANT: We need to still be in the lock when returning from this func */
+}
 
-			/* Server is in cache already, no need to get password */
-			return ;
-		}
-	}
-	
-	if (strcmp (share_name,"IPC$") == 0) {
-		ask_share_name = NULL;
-	} else {
-		ask_share_name = (char *)share_name;
-	}
+static void
+auth_callback (const char *server_name, const char *share_name,
+	       char *domain_out, int domainmaxlen,
+	       char *username_out, int unmaxlen,
+	       char *password_out, int pwmaxlen)
+{
+	/* IMPORTANT: We are IN the global lock */
+	SmbAuthContext *actx;
 	
-	if (!done_pre_auth) {
-		/* call pre-auth, if got filled, return to test auth */
-		done_pre_auth = TRUE;
-		if (invoke_fill_auth (server_name, ask_share_name,
-				      username, domain,
-				      &real_username,
-				      &real_domain,
-				      &real_password)) {
-			g_free (username);
-			g_free (domain);
-			
-			if (real_username != NULL) {
-				strncpy (username_out, real_username, unmaxlen);
-			}
-			if (real_domain != NULL) {
-				strncpy (domain_out, real_domain, domainmaxlen);
-			}
-			if (real_password != NULL) {
-				strncpy (password_out, real_password, pwmaxlen);
-			}
+	DEBUG_SMB (("auth_callback called: server: %s share: %s\n",
+		    server_name, share_name));
 
-			g_free (real_username);
-			g_free (real_domain);
-			g_free (real_password);
-
-			return;
-		}
-	}
+	g_return_if_fail (current_auth_context != NULL);
+	actx = current_auth_context;
+	
+	/* We never authenticate for the toplevel (enumerating workgroups) */
+	if (!server_name || !server_name[0])
+		return;
 
-	g_free (auth_keyring);
-	auth_keyring = NULL;
-	if (invoke_full_auth (server_name, ask_share_name,
-			      username, domain,
-			      &cancel_auth,
-			      &real_username,
-			      &real_domain,
-			      &real_password,
-			      &auth_save_password,
-			      &auth_keyring)) {
-		if (cancel_auth) {
-			auth_cancelled = TRUE;
-			strncpy (username_out, "not", unmaxlen);
-			strncpy (password_out, "matching", unmaxlen);
-		} else {
-			/* Try this auth */
-			if (real_username != NULL) {
-				strncpy (username_out, real_username, unmaxlen);
-			}
-			if (real_domain != NULL) {
-				strncpy (domain_out, real_domain, domainmaxlen);
-			}
-			if (real_password != NULL) {
-				strncpy (password_out, real_password, pwmaxlen);
-			}
+	actx->auth_called = TRUE;	
+		
+	/* The authentication location */
+	g_free (actx->for_server);
+	actx->for_server = string_dup_nzero (server_name);
+	g_free (actx->for_share);
+	actx->for_share = !share_name || strcmp (share_name,"IPC$") == 0 
+				? NULL : string_dup_nzero (share_name);
+
+	/* The first pass, try the cache, fill in anything we know */
+	if (actx->passes == 1)
+		initial_authentication (actx);
+	
+	/* If we have a valid user and password then go for it */
+	if (actx->use_user && actx->use_password) {
+		strncpy (username_out, actx->use_user, unmaxlen);
+		strncpy (password_out, actx->use_password, pwmaxlen);
+		if (actx->use_domain)
+			strncpy (domain_out, actx->use_domain, domainmaxlen);
 
-			if (auth_save_password) {
-				last_pwd = g_strdup (real_password);
-			}
-			
-			g_free (real_username);
-			g_free (real_domain);
-			g_free (real_password);
-		}
+	/* We have no credentials ... */			
 	} else {
-		if (done_auth) {
-			auth_cancelled = TRUE;
-			strncpy (username_out, "not", unmaxlen);
-			strncpy (password_out, "matching", unmaxlen);
-		}
-		/* else, no auth callback registered and first try, try anon */
+		strncpy (username_out, "", unmaxlen);
+		strncpy (password_out, "", pwmaxlen);
+		strncpy (domain_out, "", domainmaxlen);
 	}
-	
-	done_auth = TRUE;
-
-	return;
 }
 
-
 static char *
 get_workgroup_data (const char *display_name, const char *name)
 {
@@ -1090,11 +1169,12 @@ do_open (GnomeVFSMethod *method,
 	 GnomeVFSOpenMode mode,
 	 GnomeVFSContext *context)
 {
+	SmbAuthContext actx;
 	FileHandle *handle = NULL;
 	char *path, *name, *unescaped_name;
 	int type;
 	mode_t unix_mode;
-	SMBCFILE *file;
+	SMBCFILE *file = NULL;
 	
 	DEBUG_SMB(("do_open() %s mode %d\n",
 				gnome_vfs_uri_to_string (uri, 0), mode));
@@ -1170,19 +1250,20 @@ do_open (GnomeVFSMethod *method,
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 	
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	file = smb_context->open (smb_context, path, unix_mode, 0666);
-	if (file == NULL && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, uri);
+
+	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 ();
 	}
+
 	UNLOCK_SMB();
 	
 	if (file == NULL) {
 		g_free (path);
-		return gnome_vfs_result_from_errno ();
-
+		return actx.res;
 	}
+	
 	g_free (path);
 	handle = g_new (FileHandle, 1);
 	handle->is_data = FALSE;
@@ -1200,7 +1281,9 @@ do_close (GnomeVFSMethod *method,
 
 {
 	FileHandle *handle = (FileHandle *)method_handle;
+	SmbAuthContext actx;
 	GnomeVFSResult res;
+	int r;
 
 	DEBUG_SMB(("do_close()\n"));
 
@@ -1210,15 +1293,18 @@ do_close (GnomeVFSMethod *method,
 		g_free (handle->file_data);
 	} else {
 		LOCK_SMB();
-		init_auth (NULL);
-		if (smb_context->close (smb_context, handle->file) < 0) {
-			res = gnome_vfs_result_from_errno ();
+		init_authentication (&actx, NULL);
+
+		while (perform_authentication (&actx) > 0) {
+			r = smb_context->close (smb_context, handle->file);
+			actx.res = (r >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 		}
+
+		res = actx.res;		
 		UNLOCK_SMB();
 	}
 
 	g_free (handle);
-
 	return res;
 }
 
@@ -1231,8 +1317,9 @@ do_read (GnomeVFSMethod *method,
 	 GnomeVFSContext *context)
 {
 	FileHandle *handle = (FileHandle *)method_handle;
-	GnomeVFSResult res;
-	ssize_t n;
+	GnomeVFSResult res = GNOME_VFS_OK;;
+	SmbAuthContext actx;
+	ssize_t n = 0;
 
 	DEBUG_SMB(("do_read() %Lu bytes\n", num_bytes));
 
@@ -1245,24 +1332,21 @@ do_read (GnomeVFSMethod *method,
 		}
 	} else {
 		LOCK_SMB();
-		init_auth (NULL);
-		n = smb_context->read (smb_context, handle->file, buffer, num_bytes);
+		init_authentication (&actx, NULL);
+		
+		while (perform_authentication (&actx) > 0) {
+			n = smb_context->read (smb_context, handle->file, buffer, num_bytes);
+			actx.res = (n >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		}
+		
+		res = actx.res;
 		UNLOCK_SMB();
 	}
 
-	/* Can only happen when reading from smb: */
-	if (n < 0) {
-		*bytes_read = 0;
-		res = gnome_vfs_result_from_errno ();
-	} else {
-		res = GNOME_VFS_OK;
-	}
-
 	*bytes_read = n;
 
-	if (n == 0) {
+	if (n == 0) 
 		return GNOME_VFS_ERROR_EOF;
-	}
 
 	handle->offset += n;
 
@@ -1279,30 +1363,27 @@ do_write (GnomeVFSMethod *method,
 
 
 {
-	GnomeVFSResult res;
 	FileHandle *handle = (FileHandle *)method_handle;
-	ssize_t written;
+	SmbAuthContext actx;
+	ssize_t written = 0;
 
 	DEBUG_SMB (("do_write() %p\n", method_handle));
 
-	if (handle->is_data) {
+	if (handle->is_data)
 		return GNOME_VFS_ERROR_READ_ONLY;
-	}
 
 	LOCK_SMB();
-	init_auth (NULL);
-	written = smb_context->write (smb_context, handle->file, (void *)buffer, num_bytes);
-	UNLOCK_SMB();
+	init_authentication (&actx, NULL);
 
-	if (written < 0) {
-		res = gnome_vfs_result_from_errno ();
-		*bytes_written = 0;
-	} else {
-		res = GNOME_VFS_OK;
-		*bytes_written = written;
+	while (perform_authentication (&actx) > 0) {
+		written = smb_context->write (smb_context, handle->file, (void *)buffer, num_bytes);
+		actx.res = (written >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
+	
+	UNLOCK_SMB();
 
-	return res;
+	*bytes_written = (written < 0) ? 0 : written;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -1317,8 +1398,9 @@ do_create (GnomeVFSMethod *method,
 	int type;
 	mode_t unix_mode;
 	char *path;
-	SMBCFILE *file;
+	SMBCFILE *file = NULL;
 	FileHandle *handle;
+	SmbAuthContext actx;
 	
 	DEBUG_SMB (("do_create() %s mode %d\n",
 				gnome_vfs_uri_to_string (uri, 0), mode));
@@ -1326,21 +1408,18 @@ do_create (GnomeVFSMethod *method,
 	
 	type = smb_uri_type (uri);
 
-	if (type == SMB_URI_ERROR) {
+	if (type == SMB_URI_ERROR)
 		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)
 		return GNOME_VFS_ERROR_IS_DIRECTORY;
-	}
 
 	if (type == SMB_URI_WORKGROUP_LINK ||
-	    type == SMB_URI_SERVER_LINK) {
+	    type == SMB_URI_SERVER_LINK) 
 		return GNOME_VFS_ERROR_NOT_PERMITTED;
-	}
 	
 	unix_mode = O_CREAT | O_TRUNC;
 	
@@ -1358,20 +1437,20 @@ do_create (GnomeVFSMethod *method,
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	file = smb_context->open (smb_context, path, unix_mode, perm);
-	if (file == NULL && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, uri);
+	
+	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 ();
 	}
-	if (file == NULL) {
-		UNLOCK_SMB();
-		g_free (path);
-		return gnome_vfs_result_from_errno ();
 
-	}
 	UNLOCK_SMB();
+
 	g_free (path);
+
+	if (file == NULL)
+		return actx.res;
+	
 	handle = g_new (FileHandle, 1);
 	handle->is_data = FALSE;
 	handle->file = file;
@@ -1391,8 +1470,9 @@ do_get_file_info (GnomeVFSMethod *method
 {
 	struct stat st;
 	char *path;
-	int err, type;
+	int type, err = -1;
 	const char *mime_type;
+	SmbAuthContext actx;
 
 	DEBUG_SMB (("do_get_file_info() %s\n",
 				gnome_vfs_uri_to_string (uri, 0)));
@@ -1422,7 +1502,7 @@ do_get_file_info (GnomeVFSMethod *method
 		 */
 		if (type != SMB_URI_SHARE) {
 			file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
-			file_info->permissions =
+			file_info->permissions = 
 				GNOME_VFS_PERM_USER_READ |
 				GNOME_VFS_PERM_OTHER_READ |
 				GNOME_VFS_PERM_GROUP_READ;
@@ -1451,22 +1531,19 @@ do_get_file_info (GnomeVFSMethod *method
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	err = smb_context->stat (smb_context, path, &st);
-	if (err < 0 && auth_failed ()) {
-		goto again;
-	}
-	
-	if (err < 0) {
-		UNLOCK_SMB();
-		g_free (path);
-		return gnome_vfs_result_from_errno ();
+	init_authentication (&actx, uri);
+
+	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 ();
 	}
 
 	UNLOCK_SMB();
-	
+
 	g_free (path);
+
+	if (err < 0)
+		return actx.res;
 	
 	gnome_vfs_stat_to_file_info (file_info, &st);
 	file_info->name = get_base_from_uri (uri);
@@ -1503,17 +1580,23 @@ do_get_file_info_from_handle (GnomeVFSMe
 		GnomeVFSContext *context)
 {
 	FileHandle *handle = (FileHandle *)method_handle;
+	SmbAuthContext actx;
 	struct stat st;
-	int err;
+	int err = -1;
 
 	LOCK_SMB();
-	init_auth (NULL);
-	err = smb_context->fstat (smb_context, handle->file, &st);
-	UNLOCK_SMB();
-	if (err < 0) {
-		return gnome_vfs_result_from_errno ();
+	init_authentication (&actx, NULL);
+	
+	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 ();
 	}
 	
+	UNLOCK_SMB();
+	
+	if (err < 0) 
+		return actx.res;
+	
 	gnome_vfs_stat_to_file_info (file_info, &st);
 
 	file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE;
@@ -1560,7 +1643,8 @@ do_open_directory (GnomeVFSMethod *metho
 	const char *host_name;
 	char *path;
 	SmbUriType type;
-	SMBCFILE *dir;
+	SMBCFILE *dir = NULL;
+	SmbAuthContext actx;
 
 	DEBUG_SMB(("do_open_directory() %s\n",
 		gnome_vfs_uri_to_string (uri, 0)));
@@ -1570,10 +1654,6 @@ do_open_directory (GnomeVFSMethod *metho
 	if (type == SMB_URI_WHOLE_NETWORK) {
 		update_workgroup_cache ();
 		
-		if (workgroups_errno != 0) {
-			gnome_vfs_result_from_errno_code (workgroups_errno);
-		}
-		
 		directory_handle = g_new0 (DirectoryHandle, 1);
 		g_hash_table_foreach (workgroups, add_workgroup, directory_handle);
 		*method_handle = (GnomeVFSMethodHandle *) directory_handle;
@@ -1604,23 +1684,22 @@ do_open_directory (GnomeVFSMethod *metho
 	DEBUG_SMB(("do_open_directory() path %s\n", path));
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	dir = smb_context->opendir (smb_context, path);
-	if (dir == NULL && auth_failed ()) {
-		goto again;
-	}
-	
-	if (dir == NULL) {
-		UNLOCK_SMB();
-		g_free (path);
-		if (new_uri) gnome_vfs_uri_unref (new_uri);
-		return gnome_vfs_result_from_errno ();
+	init_authentication (&actx, uri);
+
+	while (perform_authentication (&actx) > 0) {
+		dir = smb_context->opendir (smb_context, path);
+		actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
 
 	UNLOCK_SMB();
+	
+	g_free (path);
 
-	if (new_uri) gnome_vfs_uri_unref (new_uri);
+	if (new_uri) 
+		gnome_vfs_uri_unref (new_uri);
+	
+	if (dir == NULL)
+		return actx.res;
 
 	/* Construct the handle */
 	directory_handle = g_new0 (DirectoryHandle, 1);
@@ -1638,8 +1717,9 @@ do_close_directory (GnomeVFSMethod *meth
 {
 	DirectoryHandle *directory_handle = (DirectoryHandle *) method_handle;
 	GnomeVFSResult res;
+	SmbAuthContext actx;
 	GList *l;
-	int err;
+	int err = -1;
 
 	DEBUG_SMB(("do_close_directory: %p\n", directory_handle));
 
@@ -1657,11 +1737,14 @@ do_close_directory (GnomeVFSMethod *meth
 	
 	if (directory_handle->dir != NULL) {
 		LOCK_SMB ();
-		init_auth (NULL);
-		err = smb_context->closedir (smb_context, directory_handle->dir);
-		if (err < 0) {
-			res = gnome_vfs_result_from_errno ();
-		} 
+		init_authentication (&actx, NULL);
+
+		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 ();
+		}
+		
+		res = actx.res;
 		UNLOCK_SMB ();
 	}
 	g_free (directory_handle->path);
@@ -1677,10 +1760,12 @@ do_read_directory (GnomeVFSMethod *metho
 		   GnomeVFSContext *context)
 {
 	DirectoryHandle *dh = (DirectoryHandle *) method_handle;
-	struct smbc_dirent *entry;
+	struct smbc_dirent *entry = NULL;
+	SmbAuthContext actx;
 	struct stat st;
 	char *statpath;
 	char *path;
+	int r = -1;
 	GList *l;
 
 	DEBUG_SMB (("do_read_directory()\n"));
@@ -1708,18 +1793,22 @@ do_read_directory (GnomeVFSMethod *metho
 	LOCK_SMB();
 	do {
 		errno = 0;
-		init_auth (NULL);
-		entry = smb_context->readdir (smb_context, dh->dir);
+		
+		init_authentication (&actx, NULL);
+		
+		while (perform_authentication (&actx) > 0) {
+			entry = smb_context->readdir (smb_context, dh->dir);
+			actx.res = (entry != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
+		}
 		
 		if (entry == NULL) {
 			UNLOCK_SMB();
 			
-			if (errno != 0) {
-				return gnome_vfs_result_from_errno ();
-			} else {
+			if (actx.res == GNOME_VFS_OK) 
 				return GNOME_VFS_ERROR_EOF;
-			}
+			return actx.res;
 		}
+		
 	} while (entry->smbc_type == SMBC_COMMS_SHARE ||
 		 entry->smbc_type == SMBC_IPC_SHARE ||
 		 entry->smbc_type == SMBC_PRINTER_SHARE ||
@@ -1782,14 +1871,19 @@ do_read_directory (GnomeVFSMethod *metho
 		*/
 		
 		LOCK_SMB();
-		init_auth (NULL);
-		if (smb_context->stat (smb_context, statpath, &st) == 0) {
+		init_authentication (&actx, NULL);
+		
+		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 ();
+		}
+		UNLOCK_SMB();
+		
+		if (r == 0) {
 			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;
 		}
-		UNLOCK_SMB();
 		g_free (statpath);
 
 		file_info->valid_fields = file_info->valid_fields
@@ -1827,9 +1921,9 @@ do_seek (GnomeVFSMethod *method,
 		GnomeVFSContext *context)
 {
 	FileHandle *handle = (FileHandle *)method_handle;
-	GnomeVFSResult res;
+	SmbAuthContext actx;
 	int meth_whence;
-	off_t ret;
+	off_t ret = (off_t) -1;
 
 	if (handle->is_data) {
 		switch (whence) {
@@ -1867,16 +1961,15 @@ do_seek (GnomeVFSMethod *method,
 	}
 
 	LOCK_SMB();
-	init_auth (NULL);
-	ret = smb_context->lseek (smb_context, handle->file, (off_t) offset, meth_whence);
-	UNLOCK_SMB();
-	if (ret == (off_t) -1) {
-		res = gnome_vfs_result_from_errno ();
-	} else {
-		res = GNOME_VFS_OK;
+	init_authentication (&actx, NULL);
+	
+	while (perform_authentication (&actx) > 0) {
+		ret = smb_context->lseek (smb_context, handle->file, (off_t) offset, meth_whence);
+		actx.res = (ret != (off_t) -1) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
-
-	return res;
+	UNLOCK_SMB();
+	
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -1885,8 +1978,8 @@ do_tell (GnomeVFSMethod *method,
 		GnomeVFSFileOffset *offset_return)
 {
 	FileHandle *handle = (FileHandle *)method_handle;
-	GnomeVFSResult res;
-	off_t ret;
+	SmbAuthContext actx;
+	off_t ret = (off_t) -1;
 
 	if (handle->is_data) {
 		*offset_return = handle->offset;
@@ -1894,18 +1987,16 @@ do_tell (GnomeVFSMethod *method,
 	}
 	
 	LOCK_SMB();
-	init_auth (NULL);
-	ret = smb_context->lseek (smb_context, handle->file, (off_t) 0, SEEK_CUR);
-	UNLOCK_SMB();
-	if (ret == (off_t) -1) {
-		*offset_return = 0;
-		res = gnome_vfs_result_from_errno ();
-	} else {
-		*offset_return = (GnomeVFSFileOffset) ret;
-		res = GNOME_VFS_OK;
+	init_authentication (&actx, NULL);
+	
+	while (perform_authentication (&actx) > 0) {
+		ret = smb_context->lseek (smb_context, handle->file, (off_t) 0, SEEK_CUR);
+		actx.res = (ret != (off_t) -1) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
-
-	return res;
+	UNLOCK_SMB();
+	
+	*offset_return = (ret == (off_t) -1) ? 0 : (GnomeVFSFileOffset) ret;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -1914,7 +2005,8 @@ do_unlink (GnomeVFSMethod *method,
 	   GnomeVFSContext *context)
 {
 	char *path;
-	int type, err;
+	SmbAuthContext actx;
+	int type, err = -1;
 
 	DEBUG_SMB (("do_unlink() %s\n",
 				gnome_vfs_uri_to_string (uri, 0)));
@@ -1937,19 +2029,18 @@ do_unlink (GnomeVFSMethod *method,
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	err = smb_context->unlink (smb_context, path);
-	if (err < 0 && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, uri);
+	
+	while (perform_authentication (&actx) > 0) {
+		err = smb_context->unlink (smb_context, path);
+		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
+	
 	UNLOCK_SMB();
+
 	g_free (path);
 	
-	if (err < 0) {
-		return gnome_vfs_result_from_errno ();
-	}
-	return GNOME_VFS_OK;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -2023,10 +2114,10 @@ do_move (GnomeVFSMethod *method,
 	 gboolean force_replace,
 	 GnomeVFSContext *context)
 {
-	GnomeVFSResult res;
 	char *old_path, *new_path;
-	int err;
+	int errnox = 0, err = -1;
 	gboolean tried_once;
+	SmbAuthContext actx;
 	int old_type, new_type;
 	
 	
@@ -2048,48 +2139,45 @@ do_move (GnomeVFSMethod *method,
 
 	tried_once = FALSE;
  retry:
-	res = GNOME_VFS_OK;
 	LOCK_SMB();
-	init_auth (old_uri);
- again:
-	err = smb_context->rename (smb_context, old_path,
-				   smb_context, new_path);
-	if (err < 0 && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, old_uri);
+	
+	while (perform_authentication (&actx) > 0) {
+		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 ();
 	}
 	UNLOCK_SMB();
+	
 	if (err < 0) {
-		err = errno;
-		if (err == EXDEV) {
-			res = GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
+		if (errnox == EXDEV) {
+			actx.res = GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
+			
 		} else if (err == EEXIST && force_replace != FALSE) {
 			/* If the target exists and force_replace is TRUE */
 			LOCK_SMB();
-			init_auth (new_uri);
-		again2:
-			err = smb_context->unlink (smb_context, new_path);
-			if (err < 0 && auth_failed ()) {
-				goto again2;
+			init_authentication (&actx, new_uri);
+
+			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 ();
 			}
 			UNLOCK_SMB();
-			if (err < 0) {
-				res = gnome_vfs_result_from_errno ();
-			} else {
+
+			if (err >= 0) {
 				if (!tried_once) {
 					tried_once = TRUE;
 					goto retry;
 				}
-				res = GNOME_VFS_ERROR_FILE_EXISTS;
+				actx.res = GNOME_VFS_ERROR_FILE_EXISTS;
 			}
-		} else {
-			res = gnome_vfs_result_from_errno ();
 		}
 	}
 
 	g_free (old_path);
 	g_free (new_path);
 
-	return res;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -2110,7 +2198,8 @@ do_make_directory (GnomeVFSMethod *metho
 		   GnomeVFSContext *context)
 {
 	char *path;
-	int err, type;
+	int type, err = -1;
+	SmbAuthContext actx;
 
 	type = smb_uri_type (uri);
 
@@ -2131,20 +2220,19 @@ do_make_directory (GnomeVFSMethod *metho
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	err = smb_context->mkdir (smb_context, path, perm);
-	if (err < 0 && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, uri);
+
+	/* Important: perform_authentication leaves and re-enters the lock! */
+	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 ();
 	}
+
 	UNLOCK_SMB();
-	g_free (path);
 
-	if (err < 0) {
-		return gnome_vfs_result_from_errno ();
-	}
+	g_free (path);
 
-	return GNOME_VFS_OK;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -2153,7 +2241,8 @@ do_remove_directory (GnomeVFSMethod *met
 		     GnomeVFSContext *context)
 {
 	char *path;
-	int err, type;
+	int err = -1, type;
+	SmbAuthContext actx;
 
 	type = smb_uri_type (uri);
 
@@ -2174,20 +2263,17 @@ do_remove_directory (GnomeVFSMethod *met
 	path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD);
 
 	LOCK_SMB();
-	init_auth (uri);
- again:
-	err = smb_context->rmdir (smb_context, path);
-	if (err < 0 && auth_failed ()) {
-		goto again;
+	init_authentication (&actx, uri);
+
+	while (perform_authentication (&actx) > 0) {
+		err = smb_context->rmdir (smb_context, path);
+		actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno ();
 	}
 	UNLOCK_SMB();
-	g_free (path);
 
-	if (err < 0) {
-		return gnome_vfs_result_from_errno ();
-	}
+	g_free (path);
 
-	return GNOME_VFS_OK;
+	return actx.res;
 }
 
 static GnomeVFSResult
@@ -2198,8 +2284,8 @@ do_set_file_info (GnomeVFSMethod *method
 		  GnomeVFSContext *context)
 {
 	char *path;
-	int err, type;
-	GnomeVFSResult res;
+	int err = -1, errnox = 0, type;
+	SmbAuthContext actx;	
 
 	DEBUG_SMB (("do_set_file_info: mask %x\n", mask));
 
@@ -2232,30 +2318,25 @@ do_set_file_info (GnomeVFSMethod *method
 
 
 		LOCK_SMB();
-		init_auth (uri);
-	again:
-		err = smb_context->rename (smb_context, path,
-					   smb_context, new_path);
-		if (err < 0 && auth_failed ()) {
-			goto again;
+		init_authentication (&actx, uri);
+		
+		while (perform_authentication (&actx) > 0) {
+			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 ();
 		}
+		
 		UNLOCK_SMB();
 
-		res = GNOME_VFS_OK;
-		if (err < 0) {
-			if (errno == EXDEV) {
-				res = GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
-			} else {
-				res = gnome_vfs_result_from_errno ();
-			}
-		}
+		if (err < 0 && errnox == EXDEV)
+			actx.res = GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM;
 		
 		g_free (path);
 		path = new_path;
 
-		if (res != GNOME_VFS_OK) {
+		if (actx.res != GNOME_VFS_OK) {
 			g_free (path);
-			return res;
+			return actx.res;
 		}
 	}
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]