changeset 16068:ac1a32ebd62c

patch from Simon Wilkinson to support Jabber/XMPP w/o passwords
author Nathan Walp <nwalp@pidgin.im>
date Sun, 15 Apr 2007 00:14:34 +0000
parents 153c696be069
children e5af828c2f54
files libpurple/account.c libpurple/account.h libpurple/protocols/jabber/auth.c libpurple/protocols/jabber/jabber.c
diffstat 4 files changed, 147 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/account.c	Tue Apr 10 22:23:13 2007 +0000
+++ b/libpurple/account.c	Sun Apr 15 00:14:34 2007 +0000
@@ -932,8 +932,9 @@
 	purple_connection_new(account, FALSE, entry);
 }
 
-static void
-request_password(PurpleAccount *account)
+void
+purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
+				GCallback cancel_cb, void *user_data)
 {
 	gchar *primary;
 	const gchar *username;
@@ -965,9 +966,9 @@
                         primary,
                         NULL,
                         fields,
-                        _("OK"), G_CALLBACK(request_password_ok_cb),
-                        _("Cancel"), NULL,
-                        account);
+                        _("OK"), ok_cb,
+                        _("Cancel"), cancel_cb,
+                        user_data);
 	g_free(primary);
 }
 
@@ -1003,7 +1004,7 @@
 	if ((password == NULL) &&
 		!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 		!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
-		request_password(account);
+		purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), NULL, account);
 	else
 		purple_connection_new(account, FALSE, password);
 }
--- a/libpurple/account.h	Tue Apr 10 22:23:13 2007 +0000
+++ b/libpurple/account.h	Sun Apr 15 00:14:34 2007 +0000
@@ -29,6 +29,7 @@
 
 #include <glib-object.h>
 #include <glib.h>
+#include <glib-object.h>
 
 typedef struct _PurpleAccountUiOps PurpleAccountUiOps;
 typedef struct _PurpleAccount      PurpleAccount;
@@ -223,6 +224,18 @@
 void purple_account_request_close(void *ui_handle);
 
 /**
+ * Requests a password from the user for the account. Does not set the
+ * account password on success; do that in ok_cb if desired.
+ *
+ * @param account     The account to request the password for.
+ * @param ok_cb       The callback for the OK button.
+ * @param cancel_cb   The callback for the cancel button.
+ * @param user_data   User data to be passed into callbacks.
+ */
+void purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
+				     GCallback cancel_cb, void *user_data);
+
+/**
  * Requests information from the user to change the account's password.
  *
  * @param account The account to change the password on.
--- a/libpurple/protocols/jabber/auth.c	Tue Apr 10 22:23:13 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Sun Apr 15 00:14:34 2007 +0000
@@ -33,6 +33,7 @@
 #include "auth.h"
 #include "jabber.h"
 #include "iq.h"
+#include "notify.h"
 
 static void auth_old_result_cb(JabberStream *js, xmlnode *packet,
 		gpointer data);
@@ -117,6 +118,7 @@
 #ifdef HAVE_CYRUS_SASL
 
 static void jabber_auth_start_cyrus(JabberStream *);
+static void jabber_sasl_build_callbacks(JabberStream *);
 
 /* Callbacks for Cyrus SASL */
 
@@ -179,6 +181,58 @@
 	jabber_auth_start_cyrus(account->gc->proto_data);
 }
 
+static gboolean auth_pass_generic(JabberStream *js, PurpleRequestFields *fields)
+{
+	const char *entry;
+	gboolean remember;
+
+	entry = purple_request_fields_get_string(fields, "password");
+	remember = purple_request_fields_get_bool(fields, "remember");
+
+	if (!entry || !*entry)
+	{
+		purple_notify_error(js->gc->account, NULL, _("Password is required to sign on."), NULL);
+		return FALSE;
+	}
+
+	if (remember)
+		purple_account_set_remember_password(js->gc->account, TRUE);
+
+	purple_account_set_password(js->gc->account, entry);
+
+	return TRUE;
+}
+	
+static void auth_pass_cb(JabberStream *js, PurpleRequestFields *fields)
+{
+
+	if (!auth_pass_generic(js, fields))
+		return;
+
+	/* Rebuild our callbacks as we now have a password to offer */
+	jabber_sasl_build_callbacks(js);
+
+	/* Restart our connection */
+	jabber_auth_start_cyrus(js);
+}
+
+static void
+auth_old_pass_cb(JabberStream *js, PurpleRequestFields *fields)
+{
+	if (!auth_pass_generic(js, fields))
+		return;
+	
+	/* Restart our connection */
+	jabber_auth_start_old(js);
+}
+
+
+static void
+auth_no_pass_cb(JabberStream *js, PurpleRequestFields *fields)
+{
+	purple_connection_error(js->gc, _("Password is required to sign on."));
+}
+
 static void jabber_auth_start_cyrus(JabberStream *js)
 {
 	const char *clientout = NULL, *mech = NULL;
@@ -222,12 +276,20 @@
 			case SASL_CONTINUE:
 				break;
 			case SASL_NOMECH:
-				/* No mechanisms do what we want. See if we can add
-				 * plaintext ones to the list. */
+				/* No mechanisms have offered to help */
+
+				/* Firstly, if we don't have a password try
+				 * to get one
+				 */
 
 				if (!purple_account_get_password(js->gc->account)) {
-					purple_connection_error(js->gc, _("Server couldn't authenticate you without a password"));
+					purple_account_request_password(js->gc->account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js);
 					return;
+
+				/* If we've got a password, but aren't sending
+				 * it in plaintext, see if we can turn on
+				 * plaintext auth
+				 */
 				} else if (!plaintext) {
 					purple_request_yes_no(js->gc, _("Plaintext Authentication"),
 							_("Plaintext Authentication"),
@@ -236,6 +298,10 @@
 							allow_cyrus_plaintext_auth,
 							disallow_plaintext_auth);
 					return;
+				/* Everything else has failed, so fail the
+				 * connection. Should probably have a better
+				 * error here.
+				 */
 				} else {
 					purple_connection_error(js->gc, _("Server does not use any supported authentication method"));
 					return;
@@ -305,14 +371,52 @@
 	return SASL_OK;
 }
 
+void
+jabber_sasl_build_callbacks(JabberStream *js)
+{
+	int id;
+
+	/* Set up our callbacks structure */
+	if (js->sasl_cb == NULL)
+		js->sasl_cb = g_new0(sasl_callback_t,6);
+
+	id = 0;
+	js->sasl_cb[id].id = SASL_CB_GETREALM;
+	js->sasl_cb[id].proc = jabber_sasl_cb_realm;
+	js->sasl_cb[id].context = (void *)js;
+	id++;
+
+	js->sasl_cb[id].id = SASL_CB_AUTHNAME;
+	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+	js->sasl_cb[id].context = (void *)js;
+	id++;
+
+	js->sasl_cb[id].id = SASL_CB_USER;
+	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
+	js->sasl_cb[id].context = (void *)js;
+	id++;
+
+	if (purple_account_get_password(js->gc->account) != NULL ) {
+		js->sasl_cb[id].id = SASL_CB_PASS;
+		js->sasl_cb[id].proc = jabber_sasl_cb_secret;
+		js->sasl_cb[id].context = (void *)js;
+		id++;
+	}
+
+	js->sasl_cb[id].id = SASL_CB_LOG;
+	js->sasl_cb[id].proc = jabber_sasl_cb_log;
+	js->sasl_cb[id].context = (void*)js;
+	id++;
+
+	js->sasl_cb[id].id = SASL_CB_LIST_END;
+}
+
 #endif
 
 void
 jabber_auth_start(JabberStream *js, xmlnode *packet)
 {
-#ifdef HAVE_CYRUS_SASL
-	int id;
-#else
+#ifndef HAVE_CYRUS_SASL
 	gboolean digest_md5 = FALSE, plain=FALSE;
 #endif
 
@@ -354,38 +458,7 @@
 #ifdef HAVE_CYRUS_SASL
 	js->auth_type = JABBER_AUTH_CYRUS;
 
-	/* Set up our callbacks structure */
-	js->sasl_cb = g_new0(sasl_callback_t,6);
-
-	id = 0;
-	js->sasl_cb[id].id = SASL_CB_GETREALM;
-	js->sasl_cb[id].proc = jabber_sasl_cb_realm;
-	js->sasl_cb[id].context = (void *)js;
-	id++;
-
-	js->sasl_cb[id].id = SASL_CB_AUTHNAME;
-	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
-	js->sasl_cb[id].context = (void *)js;
-	id++;
-
-	js->sasl_cb[id].id = SASL_CB_USER;
-	js->sasl_cb[id].proc = jabber_sasl_cb_simple;
-	js->sasl_cb[id].context = (void *)js;
-	id++;
-
-	if (purple_account_get_password(js->gc->account)) {
-		js->sasl_cb[id].id = SASL_CB_PASS;
-		js->sasl_cb[id].proc = jabber_sasl_cb_secret;
-		js->sasl_cb[id].context = (void *)js;
-		id++;
-	}
-
-	js->sasl_cb[id].id = SASL_CB_LOG;
-	js->sasl_cb[id].proc = jabber_sasl_cb_log;
-	js->sasl_cb[id].context = (void*)js;
-	id++;
-
-	js->sasl_cb[id].id = SASL_CB_LIST_END;
+	jabber_sasl_build_callbacks(js);
 
 	jabber_auth_start_cyrus(js);
 #else
@@ -507,6 +580,17 @@
 	JabberIq *iq;
 	xmlnode *query, *username;
 
+#ifdef HAVE_CYRUS_SASL
+	/* If we have Cyrus SASL, then passwords will have been set
+	 * to OPTIONAL for this protocol. So, we need to do our own
+	 * password prompting here
+	 */
+	
+	if (!purple_account_get_password(js->gc->account)) {
+		purple_account_request_password(js->gc->account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js);
+		return;
+	}
+#endif
 	iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:auth");
 
 	query = xmlnode_get_child(iq->node, "query");
--- a/libpurple/protocols/jabber/jabber.c	Tue Apr 10 22:23:13 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Apr 15 00:14:34 2007 +0000
@@ -1858,7 +1858,12 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+#ifdef HAVE_CYRUS_SASL
+	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
+	OPT_PROTO_MAIL_CHECK | OPT_PROTO_PASSWORD_OPTIONAL,
+#else
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK,
+#endif
 	NULL,							/* user_splits */
 	NULL,							/* protocol_options */
 	{"png,gif,jpeg", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */