changeset 21362:68098b461e00

propagate from branch 'im.pidgin.pidgin.next.minor' (head 08acda06a60a49c86dd22b679a931a22c62e3c13) to branch 'im.pidgin.cpw.resiak.disconnectreason' (head 2555cdba648b7afec0f0dfb14b8ed6cbf7245499)
author Will Thompson <will.thompson@collabora.co.uk>
date Wed, 24 Oct 2007 14:20:16 +0000
parents 7f1910f62246 (current diff) 6bbd023f0387 (diff)
children b4833db5eed0
files ChangeLog.API
diffstat 40 files changed, 834 insertions(+), 292 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Wed Oct 24 10:14:58 2007 +0000
+++ b/COPYRIGHT	Wed Oct 24 14:20:16 2007 +0000
@@ -80,6 +80,7 @@
 Graham Cole
 Jono Cole
 Lorenzo Colitti
+Collabora Ltd.
 Jeff Connelly
 Nathan Conrad
 Felipe Contreras
--- a/ChangeLog.API	Wed Oct 24 10:14:58 2007 +0000
+++ b/ChangeLog.API	Wed Oct 24 14:20:16 2007 +0000
@@ -15,6 +15,22 @@
 		  to unload a plugin--fails.  This then prevents the plugin
 		  from being saved in the saved plugins list, so it'll won't
 		  be loaded at the next startup.
+
+		* PurpleDisconnectReason enumeration of machine-readable
+		  types of connection error.
+		* purple_connection_error_reason(), to be used by prpls
+		  (instead of purple_connection_error() and setting
+		  gc->wants_to_die) to report errors along with a
+		  PurpleDisconnectReason.
+		* PurpleConnectionUiOps.report_disconnect_reason, to be
+		  implemented by UIs (rather than .report_disconnect) if
+		  they want to use the reported PurpleDisconnectReason
+		  to give a more specific error.
+		* purple_connection_reason_is_fatal(), acting as a hint
+		  to whether automatic reconnection should be attempted
+		  after a connection error (rather than checking
+		  gc->wants_to_die).
+
 		* purple_util_init()
 		* purple_util_uninit()
 
--- a/libpurple/connection.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/connection.c	Wed Oct 24 14:20:16 2007 +0000
@@ -488,29 +488,116 @@
 void
 purple_connection_error(PurpleConnection *gc, const char *text)
 {
+	/* prpls that have not been updated to use disconnection reasons will
+	 * be setting wants_to_die before calling this function, so choose
+	 * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
+	 * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not.  See
+	 * the documentation in connection.h.
+	 */
+	PurpleConnectionError reason = gc->wants_to_die
+	                             ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
+	                             : PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+	purple_connection_error_reason (gc, reason, text);
+}
+
+void
+purple_connection_error_reason (PurpleConnection *gc,
+                                PurpleConnectionError reason,
+                                const char *description)
+{
 	PurpleConnectionUiOps *ops;
 
 	g_return_if_fail(gc   != NULL);
+	/* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
+	 * being the last member of the PurpleConnectionError enum in
+	 * connection.h; if other reasons are added after it, this check should
+	 * be updated.
+	 */
+	if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
+		purple_debug_error("connection",
+			"purple_connection_error_reason: reason %u isn't a "
+			"valid reason\n", reason);
+		reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+	}
 
-	if (text == NULL) {
-		purple_debug_error("connection", "purple_connection_error: check `text != NULL' failed\n");
-		text = _("Unknown error");
+	if (description == NULL) {
+		purple_debug_error("connection", "purple_connection_error_reason: check `description != NULL' failed\n");
+		description = _("Unknown error");
 	}
 
 	/* If we've already got one error, we don't need any more */
 	if (gc->disconnect_timeout)
 		return;
 
+	gc->wants_to_die = purple_connection_error_is_fatal (reason);
+
 	ops = purple_connections_get_ui_ops();
 
-	if (ops != NULL && ops->report_disconnect != NULL)
-		ops->report_disconnect(gc, text);
+	if (ops != NULL)
+	{
+		if (ops->report_disconnect_reason != NULL)
+			ops->report_disconnect_reason (gc, reason, description);
+		if (ops->report_disconnect != NULL)
+			ops->report_disconnect (gc, description);
+	}
 
 	gc->disconnect_timeout = purple_timeout_add(0, purple_connection_disconnect_cb,
 			purple_connection_get_account(gc));
 }
 
 void
+purple_connection_ssl_error (PurpleConnection *gc,
+                             PurpleSslErrorType ssl_error)
+{
+	PurpleConnectionError reason;
+
+	switch (ssl_error) {
+		case PURPLE_SSL_HANDSHAKE_FAILED:
+		case PURPLE_SSL_CONNECT_FAILED:
+			reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
+			break;
+		case PURPLE_SSL_CERTIFICATE_INVALID:
+			/* TODO: maybe PURPLE_SSL_* should be more specific? */
+			reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
+			break;
+		default:
+			g_assert_not_reached ();
+			reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
+	}
+
+	purple_connection_error_reason (gc, reason,
+		purple_ssl_strerror(ssl_error));
+}
+
+gboolean
+purple_connection_error_is_fatal (PurpleConnectionError reason)
+{
+	switch (reason)
+	{
+		case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
+		case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
+		case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
+		case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
+		case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
+		case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
+		case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
+		case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
+		case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
+		case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
+			return FALSE;
+		case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
+		case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
+		case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
+		case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
+		case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
+		case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
+			return TRUE;
+		default:
+			g_return_val_if_reached(TRUE);
+	}
+}
+
+void
 purple_connections_disconnect_all(void)
 {
 	GList *l;
--- a/libpurple/connection.h	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/connection.h	Wed Oct 24 14:20:16 2007 +0000
@@ -54,11 +54,84 @@
 
 } PurpleConnectionState;
 
+/** Possible errors that can cause a connection to be closed.
+ *  @since 2.3.0
+ */
+typedef enum
+{
+	/** There was an error sending or receiving on the network socket, or
+	 *  there was some protocol error (such as the server sending malformed
+	 *  data).
+	 */
+	PURPLE_CONNECTION_ERROR_NETWORK_ERROR = 0,
+	/** The username or password (or some other credential) was incorrect.
+	 */
+	PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED = 1,
+	/** libpurple doesn't speak any of the authentication methods the
+	 *  server offered.
+	 */
+	PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE = 2,
+	/** libpurple was built without SSL support, and the connection needs
+	 *  SSL.
+	 */
+	PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT = 3,
+	/** There was an error negotiating SSL on this connection, or the
+	 *  server does not support encryption but an account option was set to
+	 *  require it.
+	 */
+	PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR = 4,
+	/** Someone is already connected to the server using the name you are
+	 *  trying to connect with.
+	 */
+	PURPLE_CONNECTION_ERROR_NAME_IN_USE = 5,
+
+	/** The username/server/other preference for the account isn't valid.
+	 *  For instance, on IRC the screen name cannot contain white space.
+	 *  This reason should not be used for incorrect passwords etc: use
+	 *  #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED for that.
+	 *
+	 *  @todo This reason really shouldn't be necessary.  Usernames and
+	 *        other account preferences should be validated when the
+	 *        account is created.
+	 */
+	PURPLE_CONNECTION_ERROR_INVALID_SETTINGS = 6,
+
+	/** The server did not provide a SSL certificate. */
+	PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED = 7,
+	/** The server's SSL certificate could not be trusted. */
+	PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED = 8,
+	/** The server's SSL certificate has expired. */
+	PURPLE_CONNECTION_ERROR_CERT_EXPIRED = 9,
+	/** The server's SSL certificate is not yet valid. */
+	PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED = 10,
+	/** The server's SSL certificate did not match its hostname. */
+	PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH = 11,
+	/** The server's SSL certificate does not have the expected
+	 *  fingerprint.
+	 */
+	PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 12,
+	/** The server's SSL certificate is self-signed.  */
+	PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED = 13,
+	/** There was some other error validating the server's SSL certificate.
+	 */
+	PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR = 14,
+
+	/** Some other error occured which fits into none of the other
+	 *  categories.
+	 */
+	/* purple_connection_error_reason() in connection.c uses the fact that
+	 * this is the last member of the enum when sanity-checking; if other
+	 * reasons are added after it, the check must be updated.
+	 */
+	PURPLE_CONNECTION_ERROR_OTHER_ERROR = 15
+} PurpleConnectionError;
+
 #include <time.h>
 
 #include "account.h"
 #include "plugin.h"
 #include "status.h"
+#include "sslconn.h"
 
 /** Connection UI operations.  Used to notify the user of changes to
  *  connections, such as being disconnected, and to respond to the
@@ -73,11 +146,13 @@
 	 *  the UI of what is happening, as well as which @a step out of @a
 	 *  step_count has been reached (which might be displayed as a progress
 	 *  bar).
+	 *  @see #purple_connection_update_progress
 	 */
 	void (*connect_progress)(PurpleConnection *gc,
 	                         const char *text,
 	                         size_t step,
 	                         size_t step_count);
+
 	/** Called when a connection is established (just before the
 	 *  @ref signed-on signal).
 	 */
@@ -86,17 +161,23 @@
 	 *  and @ref signed-off signals).
 	 */
 	void (*disconnected)(PurpleConnection *gc);
+
 	/** Used to display connection-specific notices.  (Pidgin's Gtk user
 	 *  interface implements this as a no-op; #purple_connection_notice(),
 	 *  which uses this operation, is not used by any of the protocols
 	 *  shipped with libpurple.)
 	 */
 	void (*notice)(PurpleConnection *gc, const char *text);
+
 	/** Called when an error causes a connection to be disconnected.
 	 *  Called before #disconnected.
 	 *  @param text  a localized error message.
+	 *  @see #purple_connection_error
+	 *  @deprecated in favour of
+	 *              #PurpleConnectionUiOps.report_disconnect_reason.
 	 */
 	void (*report_disconnect)(PurpleConnection *gc, const char *text);
+
 	/** Called when libpurple discovers that the computer's network
 	 *  connection is active.  On Linux, this uses Network Manager if
 	 *  available; on Windows, it uses Win32's network change notification
@@ -108,10 +189,24 @@
 	 */
 	void (*network_disconnected)();
 
+	/** Called when an error causes a connection to be disconnected.
+	 *  Called before #disconnected.  This op is intended to replace
+	 *  #report_disconnect.  If both are implemented, this will be called
+	 *  first; however, there's no real reason to implement both.
+	 *  @param reason  why the connection ended, if known, or
+	 *                 #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
+	 *  @param text  a localized message describing the disconnection
+	 *               in more detail to the user.
+	 *  @see #purple_connection_error_reason
+	 *  @since 2.3.0
+	 */
+	void (*report_disconnect_reason)(PurpleConnection *gc,
+	                                 PurpleConnectionError reason,
+	                                 const char *text);
+
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
 } PurpleConnectionUiOps;
 
 struct _PurpleConnection
@@ -125,19 +220,23 @@
 	char *password;              /**< The password used.                 */
 	int inpa;                    /**< The input watcher.                 */
 
-	GSList *buddy_chats;         /**< A list of active chats.            */
+	GSList *buddy_chats;         /**< A list of active chats
+	                                  (#PurpleConversation structs of type
+	                                  #PURPLE_CONV_TYPE_CHAT).           */
 	void *proto_data;            /**< Protocol-specific data.            */
 
 	char *display_name;          /**< How you appear to other people.    */
 	guint keepalive;             /**< Keep-alive.                        */
 
+	/** Wants to Die state.  This is set when the user chooses to log out, or
+	 * when the protocol is disconnected and should not be automatically
+	 * reconnected (incorrect password, etc.).  prpls should rely on
+	 * purple_connection_error_reason() to set this for them rather than
+	 * setting it themselves.
+	 * @see purple_connection_error_is_fatal
+	 */
+	gboolean wants_to_die;
 
-	gboolean wants_to_die;	     /**< Wants to Die state.  This is set
-	                                  when the user chooses to log out,
-	                                  or when the protocol is
-	                                  disconnected and should not be
-	                                  automatically reconnected
-	                                  (incorrect password, etc.)         */
 	guint disconnect_timeout;    /**< Timer used for nasty stack tricks  */
 };
 
@@ -209,7 +308,7 @@
 
 /**
  * Sets the connection state.  PRPLs should call this and pass in
- * the state "PURPLE_CONNECTED" when the account is completely
+ * the state #PURPLE_CONNECTED when the account is completely
  * signed on.  What does it mean to be completely signed on?  If
  * the core can call prpl->set_status, and it successfully changes
  * your status, then the account is online.
@@ -302,10 +401,63 @@
  * Closes a connection with an error.
  *
  * @param gc     The connection.
- * @param reason The error text.
+ * @param reason The error text, which may not be @c NULL.
+ * @deprecated in favour of #purple_connection_error_reason.  Calling
+ *  @c purple_connection_error(gc, text) is equivalent to calling
+ *  @c purple_connection_error_reason(gc, reason, text) where @c reason is
+ *  #PURPLE_CONNECTION_ERROR_OTHER_ERROR if @c gc->wants_to_die is @c TRUE, and
+ *  #PURPLE_CONNECTION_ERROR_NETWORK_ERROR if not.  (This is to keep
+ *  auto-reconnection behaviour the same when using old prpls which don't use
+ *  reasons yet.)
  */
 void purple_connection_error(PurpleConnection *gc, const char *reason);
 
+/**
+ * Closes a connection with an error and a human-readable description of the
+ * error.  It also sets @c gc->wants_to_die to the value of
+ * #purple_connection_error_is_fatal(@a reason).
+ *
+ * @param reason      why the connection is closing.
+ * @param description a non-@c NULL localized description of the error.
+ * @since 2.3.0
+ */
+void
+purple_connection_error_reason (PurpleConnection *gc,
+                                PurpleConnectionError reason,
+                                const char *description);
+
+/**
+ * Closes a connection due to an SSL error; this is basically a shortcut to
+ * turning the #PurpleSslErrorType into a #PurpleConnectionError and a
+ * human-readable string and then calling purple_connection_error_reason().
+ * @since 2.3.0
+ */
+void
+purple_connection_ssl_error (PurpleConnection *gc,
+                             PurpleSslErrorType ssl_error);
+
+/**
+ * Reports whether a disconnection reason is fatal (in which case the account
+ * should probably not be automatically reconnected) or transient (so
+ * auto-reconnection is a good idea).
+ * For instance, #PURPLE_CONNECTION_ERROR_NETWORK_ERROR is a temporary error,
+ * which might be caused by losing the network connection, so <tt>
+ * purple_connection_error_is_fatal (PURPLE_CONNECTION_ERROR_NETWORK_ERROR)</tt>
+ * is @c FALSE.  On the other hand,
+ * #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED probably indicates a
+ * misconfiguration of the account which needs the user to go fix it up, so
+ * <tt> purple_connection_error_is_fatal
+ * (PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED)</tt> is @c TRUE.
+ *
+ * (This function is meant to replace checking PurpleConnection.wants_to_die.)
+ *
+ * @return @c TRUE if the account should not be automatically reconnected, and
+ *         @c FALSE otherwise.
+ * @since 2.3.0
+ */
+gboolean
+purple_connection_error_is_fatal (PurpleConnectionError reason);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/protocols/bonjour/bonjour.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Wed Oct 24 14:20:16 2007 +0000
@@ -102,8 +102,8 @@
 
 #ifdef _WIN32
 	if (!dns_sd_available()) {
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc,
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: "
 			  "http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging"
 			  " for more information."));
@@ -121,7 +121,9 @@
 
 	if (bonjour_jabber_start(bd->jabber_data) == -1) {
 		/* Send a message about the connection error */
-		purple_connection_error(gc, _("Unable to listen for incoming IM connections\n"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to listen for incoming IM connections\n"));
 		return;
 	}
 
@@ -146,7 +148,9 @@
 	bd->dns_sd_data->account = account;
 	if (!bonjour_dns_sd_start(bd->dns_sd_data))
 	{
-		purple_connection_error(gc, _("Unable to establish connection with the local mDNS server.  Is it running?"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to establish connection with the local mDNS server.  Is it running?"));
 		return;
 	}
 
--- a/libpurple/protocols/bonjour/jabber.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Oct 24 14:20:16 2007 +0000
@@ -580,7 +580,9 @@
 	if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
 	{
 		purple_debug_error("bonjour", "Cannot open socket: %s\n", strerror(errno));
-		purple_connection_error(data->account->gc, _("Cannot open socket"));
+		purple_connection_error_reason (data->account->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Cannot open socket"));
 		return -1;
 	}
 
@@ -588,7 +590,9 @@
 	if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
 	{
 		purple_debug_error("bonjour", "Error setting socket options: %s\n", strerror(errno));
-		purple_connection_error(data->account->gc, _("Error setting socket options"));
+		purple_connection_error_reason (data->account->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Error setting socket options"));
 		return -1;
 	}
 
@@ -612,7 +616,9 @@
 	if (!bind_successful)
 	{
 		purple_debug_error("bonjour", "Cannot bind socket: %s\n", strerror(errno));
-		purple_connection_error(data->account->gc, _("Could not bind socket to port"));
+		purple_connection_error_reason (data->account->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not bind socket to port"));
 		return -1;
 	}
 
@@ -620,7 +626,9 @@
 	if (listen(data->socket, 10) != 0)
 	{
 		purple_debug_error("bonjour", "Cannot listen on socket: %s\n", strerror(errno));
-		purple_connection_error(data->account->gc, _("Could not listen on socket"));
+		purple_connection_error_reason (data->account->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not listen on socket"));
 		return -1;
 	}
 
--- a/libpurple/protocols/gg/gg.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/gg/gg.c	Wed Oct 24 14:20:16 2007 +0000
@@ -381,12 +381,16 @@
 
 	if (email == NULL || p1 == NULL || p2 == NULL || t == NULL ||
 	    *email == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
-		purple_connection_error(gc, _("Fill in the registration fields."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+			_("Fill in the registration fields."));
 		goto exit_err;
 	}
 
 	if (g_utf8_collate(p1, p2) != 0) {
-		purple_connection_error(gc, _("Passwords do not match."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+			_("Passwords do not match."));
 		goto exit_err;
 	}
 
@@ -394,7 +398,8 @@
 			token->id, t);
 	h = gg_register3(email, p1, token->id, t, 0);
 	if (h == NULL || !(s = h->data) || !s->success) {
-		purple_connection_error(gc,
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("Unable to register new account. Error occurred.\n"));
 		goto exit_err;
 	}
@@ -1307,7 +1312,9 @@
 	if (!(ev = gg_watch_fd(info->session))) {
 		purple_debug_error("gg",
 			"ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n");
-		purple_connection_error(gc, _("Unable to read socket"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to read socket"));
 		return;
 	}
 
@@ -1460,7 +1467,9 @@
 
 	if (!(ev = gg_watch_fd(info->session))) {
 		purple_debug_error("gg", "login_handler: gg_watch_fd failed!\n");
-		purple_connection_error(gc, _("Unable to read socket"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to read socket"));
 		return;
 	}
 	purple_debug_info("gg", "login_handler: session->fd = %d\n", info->session->fd);
@@ -1506,7 +1515,9 @@
 		case GG_EVENT_CONN_FAILED:
 			purple_input_remove(gc->inpa);
 			gc->inpa = 0;
-			purple_connection_error(gc, _("Connection failed."));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Connection failed."));
 			break;
 		default:
 			purple_debug_error("gg", "strange event: %d\n", ev->type);
@@ -1712,7 +1723,9 @@
 
 	info->session = gg_login(glp);
 	if (info->session == NULL) {
-		purple_connection_error(gc, _("Connection failed."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Connection failed."));
 		g_free(glp);
 		return;
 	}
@@ -1995,7 +2008,9 @@
 	if (gg_ping(info->session) < 0) {
 		purple_debug_info("gg", "Not connected to the server "
 				"or gg_session is not correct\n");
-		purple_connection_error(gc, _("Not connected to the server."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Not connected to the server."));
 	}
 }
 /* }}} */
--- a/libpurple/protocols/irc/irc.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/irc/irc.c	Wed Oct 24 14:20:16 2007 +0000
@@ -123,8 +123,10 @@
 	if (ret < 0 && errno == EAGAIN)
 		return;
 	else if (ret <= 0) {
-		purple_connection_error(purple_account_get_connection(irc->account),
-			      _("Server has disconnected"));
+		PurpleConnection *gc = purple_account_get_connection(irc->account);
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server has disconnected"));
 		return;
 	}
 
@@ -161,8 +163,10 @@
 	/* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s",
 		irc->gsc ? " (ssl)" : "", tosend); */
 	if (ret <= 0 && errno != EAGAIN) {
-		purple_connection_error(purple_account_get_connection(irc->account),
-				      _("Server has disconnected"));
+		PurpleConnection *gc = purple_account_get_connection(irc->account);
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server has disconnected"));
 	} else if (ret < buflen) {
 		if (ret < 0)
 			ret = 0;
@@ -295,7 +299,9 @@
 	gc->flags |= PURPLE_CONNECTION_NO_NEWLINES;
 
 	if (strpbrk(username, " \t\v\r\n") != NULL) {
-		purple_connection_error(gc, _("IRC nicks may not contain whitespace"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("IRC nicks may not contain whitespace"));
 		return;
 	}
 
@@ -324,7 +330,9 @@
 					purple_account_get_int(account, "port", IRC_DEFAULT_SSL_PORT),
 					irc_login_cb_ssl, irc_ssl_connect_failure, gc);
 		} else {
-			purple_connection_error(gc, _("SSL support unavailable"));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("SSL support unavailable"));
 			return;
 		}
 	}
@@ -335,7 +343,9 @@
 				 purple_account_get_int(account, "port", IRC_DEFAULT_PORT),
 				 irc_login_cb, gc) == NULL)
 		{
-			purple_connection_error(gc, _("Couldn't create socket"));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Couldn't create socket"));
 			return;
 		}
 	}
@@ -352,7 +362,6 @@
 	if (pass && *pass) {
 		buf = irc_format(irc, "vv", "PASS", pass);
 		if (irc_send(irc, buf) < 0) {
-/*			purple_connection_error(gc, "Error sending password"); */
 			g_free(buf);
 			return FALSE;
 		}
@@ -384,14 +393,12 @@
 			      strlen(realname) ? realname : IRC_DEFAULT_ALIAS);
 	g_free(tmp);
 	if (irc_send(irc, buf) < 0) {
-/*		purple_connection_error(gc, "Error registering with server");*/
 		g_free(buf);
 		return FALSE;
 	}
 	g_free(buf);
 	buf = irc_format(irc, "vn", "NICK", purple_connection_get_display_name(gc));
 	if (irc_send(irc, buf) < 0) {
-/*		purple_connection_error(gc, "Error sending nickname");*/
 		g_free(buf);
 		return FALSE;
 	}
@@ -418,7 +425,9 @@
 	struct irc_conn *irc = gc->proto_data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Couldn't connect to host"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't connect to host"));
 		return;
 	}
 
@@ -438,7 +447,7 @@
 
 	irc->gsc = NULL;
 
-	purple_connection_error(gc, purple_ssl_strerror(error));
+	purple_connection_ssl_error (gc, error);
 }
 
 static void irc_close(PurpleConnection *gc)
@@ -606,10 +615,14 @@
 		/* Try again later */
 		return;
 	} else if (len < 0) {
-		purple_connection_error(gc, _("Read error"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read error"));
 		return;
 	} else if (len == 0) {
-		purple_connection_error(gc, _("Server has disconnected"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server has disconnected"));
 		return;
 	}
 
@@ -631,10 +644,14 @@
 	if (len < 0 && errno == EAGAIN) {
 		return;
 	} else if (len < 0) {
-		purple_connection_error(gc, _("Read error"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read error"));
 		return;
 	} else if (len == 0) {
-		purple_connection_error(gc, _("Server has disconnected"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server has disconnected"));
 		return;
 	}
 
--- a/libpurple/protocols/irc/msgs.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/irc/msgs.c	Wed Oct 24 14:20:16 2007 +0000
@@ -913,9 +913,9 @@
 				  _("Your selected nickname was rejected by the server.  It probably contains invalid characters."));
 
 	} else {
-		gc->wants_to_die = TRUE;
-		purple_connection_error(purple_account_get_connection(irc->account),
-				      _("Your selected account name was rejected by the server.  It probably contains invalid characters."));
+		purple_connection_error_reason (gc,
+				  PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+				  _("Your selected account name was rejected by the server.  It probably contains invalid characters."));
 	}
 }
 
--- a/libpurple/protocols/irc/parse.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Wed Oct 24 14:20:16 2007 +0000
@@ -612,6 +612,7 @@
 	struct _irc_msg *msgent;
 	char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg;
 	guint i;
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
 	irc->recv_time = time(NULL);
 
@@ -620,7 +621,7 @@
 	 * TODO: It should be passed as an array of bytes and a length
 	 * instead of a null terminated string.
 	 */
-	purple_signal_emit(_irc_plugin, "irc-receiving-text", purple_account_get_connection(irc->account), &input);
+	purple_signal_emit(_irc_plugin, "irc-receiving-text", gc, &input);
 	
 	if (!strncmp(input, "PING ", 5)) {
 		msg = irc_format(irc, "vv", "PONG", input + 5);
@@ -630,10 +631,13 @@
 	} else if (!strncmp(input, "ERROR ", 6)) {
 		if (g_utf8_validate(input, -1, NULL)) {
 			char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input);
-			purple_connection_error(purple_account_get_connection(irc->account), tmp);
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 			g_free(tmp);
 		} else
-			purple_connection_error(purple_account_get_connection(irc->account), _("Disconnected."));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Disconnected."));
 		return;
 	}
 
--- a/libpurple/protocols/jabber/adhoccommands.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Wed Oct 24 14:20:16 2007 +0000
@@ -132,7 +132,7 @@
 	const char *type = xmlnode_get_attrib(packet,"type");
 	
 	if(type && !strcmp(type,"error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 		if(!msg)
 			msg = g_strdup(_("Unknown Error"));
 		
--- a/libpurple/protocols/jabber/auth.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Wed Oct 24 14:20:16 2007 +0000
@@ -50,7 +50,9 @@
 					"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>", -1);
 			return TRUE;
 		} else if(xmlnode_get_child(starttls, "required")) {
-			purple_connection_error(js->gc, _("Server requires TLS/SSL for login.  No TLS/SSL support found."));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("Server requires TLS/SSL for login.  No TLS/SSL support found."));
 			return TRUE;
 		}
 	}
@@ -113,7 +115,9 @@
 
 static void disallow_plaintext_auth(PurpleAccount *account)
 {
-	purple_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream"));
+	purple_connection_error_reason (account->gc,
+		PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+		_("Server requires plaintext authentication over an unencrypted stream"));
 }
 
 #ifdef HAVE_CYRUS_SASL
@@ -331,7 +335,9 @@
 				 * error here.
 				 */
 				} else {
-					purple_connection_error(js->gc, _("Server does not use any supported authentication method"));
+					purple_connection_error_reason (js->gc,
+						PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+						_("Server does not use any supported authentication method"));
 					return;
 				}
 				/* not reached */
@@ -386,7 +392,9 @@
 		jabber_send(js, auth);
 		xmlnode_free(auth);
 	} else {
-		purple_connection_error(js->gc, "SASL authentication failed\n");
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+			"SASL authentication failed\n");
 	}
 }
 
@@ -459,7 +467,9 @@
 	mechs = xmlnode_get_child(packet, "mechanisms");
 
 	if(!mechs) {
-		purple_connection_error(js->gc, _("Invalid response from server."));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Invalid response from server."));
 		return;
 	}
 
@@ -519,7 +529,8 @@
 		}
 		finish_plaintext_authentication(js);
 	} else {
-		purple_connection_error(js->gc,
+		purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
 				_("Server does not use any supported authentication method"));
 	}
 #endif
@@ -532,20 +543,22 @@
 	if(type && !strcmp(type, "result")) {
 		jabber_stream_set_state(js, JABBER_STREAM_CONNECTED);
 	} else {
-		char *msg = jabber_parse_error(js, packet);
+		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+		char *msg = jabber_parse_error(js, packet, &reason);
 		xmlnode *error;
 		const char *err_code;
 
+		/* FIXME: Why is this not in jabber_parse_error? */
 		if((error = xmlnode_get_child(packet, "error")) &&
 					(err_code = xmlnode_get_attrib(error, "code")) &&
 					!strcmp(err_code, "401")) {
-			js->gc->wants_to_die = TRUE;
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 			/* Clear the pasword if it isn't being saved */
 			if (!purple_account_get_remember_password(js->gc->account))
 				purple_account_set_password(js->gc->account, NULL);
 		}
 
-		purple_connection_error(js->gc, msg);
+		purple_connection_error_reason (js->gc, reason, msg);
 		g_free(msg);
 	}
 }
@@ -558,11 +571,14 @@
 	const char *pw = purple_connection_get_password(js->gc);
 
 	if(!type) {
-		purple_connection_error(js->gc, _("Invalid response from server."));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Invalid response from server."));
 		return;
 	} else if(!strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
-		purple_connection_error(js->gc, msg);
+		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+		char *msg = jabber_parse_error(js, packet, &reason);
+		purple_connection_error_reason (js->gc, reason, msg);
 		g_free(msg);
 	} else if(!strcmp(type, "result")) {
 		query = xmlnode_get_child(packet, "query");
@@ -606,8 +622,9 @@
 			}
 			finish_plaintext_authentication(js);
 		} else {
-			purple_connection_error(js->gc,
-					_("Server does not use any supported authentication method"));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+				_("Server does not use any supported authentication method"));
 			return;
 		}
 	}
@@ -773,7 +790,9 @@
 		GHashTable *parts;
 
 		if(!enc_in) {
-			purple_connection_error(js->gc, _("Invalid response from server."));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Invalid response from server."));
 			return;
 		}
 
@@ -794,7 +813,9 @@
 						"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />",
 						-1);
 			} else {
-				purple_connection_error(js->gc, _("Invalid challenge from server"));
+				purple_connection_error_reason (js->gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Invalid challenge from server"));
 			}
 			g_free(js->expected_rspauth);
 		} else {
@@ -817,7 +838,9 @@
 				realm = js->user->domain;
 
 			if (nonce == NULL || realm == NULL)
-				purple_connection_error(js->gc, _("Invalid challenge from server"));
+				purple_connection_error_reason (js->gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Invalid challenge from server"));
 			else {
 				GString *response = g_string_new("");
 				char *a2;
@@ -889,7 +912,9 @@
 		g_free(dec_in);
 		if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) {
 			purple_debug_error("jabber", "Error is %d : %s\n",js->sasl_state,sasl_errdetail(js->sasl));
-			purple_connection_error(js->gc, _("SASL error"));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("SASL error"));
 			return;
 		} else {
 			response = xmlnode_new("response");
@@ -914,7 +939,9 @@
 #endif
 
 	if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) {
-		purple_connection_error(js->gc, _("Invalid response from server."));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Invalid response from server."));
 		return;
 	}
 
@@ -939,7 +966,9 @@
 
 		if (js->sasl_state != SASL_OK) {
 			/* This should never happen! */
-			purple_connection_error(js->gc, _("Invalid response from server."));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Invalid response from server."));
 		}
 	}
 	/* If we've negotiated a security layer, we need to enable it */
@@ -955,12 +984,15 @@
 
 void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet)
 {
-	char *msg = jabber_parse_error(js, packet);
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+	char *msg = jabber_parse_error(js, packet, &reason);
 
 	if(!msg) {
-		purple_connection_error(js->gc, _("Invalid response from server."));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Invalid response from server."));
 	} else {
-		purple_connection_error(js->gc, msg);
+		purple_connection_error_reason (js->gc, reason, msg);
 		g_free(msg);
 	}
 }
--- a/libpurple/protocols/jabber/buddy.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed Oct 24 14:20:16 2007 +0000
@@ -2334,7 +2334,7 @@
 		return;
 
 	if(!(type = xmlnode_get_attrib(packet, "type")) || !strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		if(!msg)
 			msg = g_strdup(_("Unknown error"));
--- a/libpurple/protocols/jabber/chat.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/chat.c	Wed Oct 24 14:20:16 2007 +0000
@@ -391,7 +391,7 @@
 			}
 		}
 	} else if(!strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		purple_notify_error(js->gc, _("Configuration error"), _("Configuration error"), msg);
 
@@ -465,7 +465,7 @@
 	const char *type = xmlnode_get_attrib(packet, "type");
 
 	if(type && !strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg);
 
@@ -534,7 +534,7 @@
 			}
 		}
 	} else if(!strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg);
 
@@ -673,7 +673,7 @@
 		return;
 
 	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
-		char *err = jabber_parse_error(js,packet);
+		char *err = jabber_parse_error(js, packet, NULL);
 		purple_notify_error(js->gc, _("Error"),
 				_("Error retrieving room list"), err);
 		purple_roomlist_set_in_progress(js->roomlist, FALSE);
@@ -684,7 +684,7 @@
 	}
 
 	if(!(query = xmlnode_get_child(packet, "query"))) {
-		char *err = jabber_parse_error(js, packet);
+		char *err = jabber_parse_error(js, packet, NULL);
 		purple_notify_error(js->gc, _("Error"),
 				_("Error retrieving room list"), err);
 		purple_roomlist_set_in_progress(js->roomlist, FALSE);
--- a/libpurple/protocols/jabber/jabber.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Oct 24 14:20:16 2007 +0000
@@ -89,7 +89,9 @@
 		if(js->unregistration)
 			jabber_unregister_account_cb(js);
 	} else {
-		purple_connection_error(js->gc, _("Error initializing session"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			("Error initializing session"));
 	}
 }
 
@@ -120,15 +122,18 @@
 			JabberBuddy *my_jb = NULL;
 			jabber_id_free(js->user);
 			if(!(js->user = jabber_id_new(full_jid))) {
-				purple_connection_error(js->gc, _("Invalid response from server."));
+				purple_connection_error_reason (js->gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Invalid response from server."));
 			}
 			if((my_jb = jabber_buddy_find(js, full_jid, TRUE)))
 				my_jb->subscription |= JABBER_SUB_BOTH;
 			g_free(full_jid);
 		}
 	} else {
-		char *msg = jabber_parse_error(js, packet);
-		purple_connection_error(js->gc, msg);
+		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+		char *msg = jabber_parse_error(js, packet, &reason);
+		purple_connection_error_reason (js->gc, reason, msg);
 		g_free(msg);
 	}
 
@@ -141,8 +146,9 @@
 		if(jabber_process_starttls(js, packet))
 			return;
 	} else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE) && !js->gsc) {
-		js->gc->wants_to_die = TRUE;
-		purple_connection_error(js->gc, _("You require encryption, but it is not available on this server."));
+		purple_connection_error_reason (js->gc,
+			 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+			_("You require encryption, but it is not available on this server."));
 		return;
 	}
 
@@ -173,9 +179,11 @@
 
 static void jabber_stream_handle_error(JabberStream *js, xmlnode *packet)
 {
-	char *msg = jabber_parse_error(js, packet);
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+	char *msg = jabber_parse_error(js, packet, &reason);
 
-	purple_connection_error(js->gc, msg);
+	purple_connection_error_reason (js->gc, reason, msg);
+
 	g_free(msg);
 }
 
@@ -256,7 +264,9 @@
 	if (ret < 0 && errno == EAGAIN)
 		return;
 	else if (ret <= 0) {
-		purple_connection_error(js->gc, _("Write error"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Write error"));
 		return;
 	}
 
@@ -309,7 +319,9 @@
 			}
 
 			if (ret < 0 && errno != EAGAIN)
-				purple_connection_error(js->gc, _("Write error"));
+				purple_connection_error_reason (js->gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Write error"));
 			else if (ret < olen) {
 				if (ret < 0)
 					ret = 0;
@@ -337,7 +349,9 @@
 	}
 
 	if (ret < 0 && errno != EAGAIN)
-		purple_connection_error(js->gc, _("Write error"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Write error"));
 	else if (ret < len) {
 		if (ret < 0)
 			ret = 0;
@@ -405,7 +419,9 @@
 	if(errno == EAGAIN)
 		return;
 	else
-		purple_connection_error(gc, _("Read Error"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read Error"));
 }
 
 static void
@@ -442,7 +458,9 @@
 	} else if(errno == EAGAIN) {
 		return;
 	} else {
-		purple_connection_error(gc, _("Read Error"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read Error"));
 	}
 }
 
@@ -481,7 +499,8 @@
 		gchar *tmp;
 		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
 				error);
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	}
@@ -509,7 +528,7 @@
 	js = gc->proto_data;
 	js->gsc = NULL;
 
-	purple_connection_error(gc, purple_ssl_strerror(error));
+	purple_connection_ssl_error (gc, error);
 }
 
 static void tls_init(JabberStream *js)
@@ -526,7 +545,9 @@
 
 	if (purple_proxy_connect(js->gc, js->gc->account, host,
 			port, jabber_login_callback, js->gc) == NULL)
-		purple_connection_error(js->gc, _("Unable to create socket"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to create socket"));
 }
 
 static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data)
@@ -572,12 +593,16 @@
 	js->old_length = -1;
 
 	if(!js->user) {
-		purple_connection_error(gc, _("Invalid XMPP ID"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("Invalid XMPP ID"));
 		return;
 	}
 	
 	if (!js->user->domain || *(js->user->domain) == '\0') {
-		purple_connection_error(gc, _("Invalid XMPP ID. Domain must be set."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("Invalid XMPP ID. Domain must be set."));
 		return;
 	}
 	
@@ -607,7 +632,9 @@
 					purple_account_get_int(account, "port", 5223), jabber_login_callback_ssl,
 					jabber_ssl_connect_failure, js->gc);
 		} else {
-			purple_connection_error(js->gc, _("SSL support unavailable"));
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("SSL support unavailable"));
 		}
 	}
 
@@ -665,7 +692,7 @@
 				_("Registration Successful"), buf);
 		g_free(buf);
 	} else {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		if(!msg)
 			msg = g_strdup(_("Unknown Error"));
@@ -695,7 +722,7 @@
 						   _("Unregistration Successful"), buf);
 		g_free(buf);
 	} else {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 		
 		if(!msg)
 			msg = g_strdup(_("Unknown Error"));
@@ -1060,7 +1087,9 @@
 	js->old_length = -1;
 
 	if(!js->user) {
-		purple_connection_error(gc, _("Invalid XMPP ID"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("Invalid XMPP ID"));
 		return;
 	}
 
@@ -1092,7 +1121,9 @@
 					purple_account_get_int(account, "port", 5222),
 					jabber_login_callback_ssl, jabber_ssl_connect_failure, gc);
 		} else {
-			purple_connection_error(gc, _("SSL support unavailable"));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("SSL support unavailable"));
 		}
 	}
 
@@ -1115,7 +1146,7 @@
 	PurpleAccount *account = purple_connection_get_account(js->gc);
 	const char *type = xmlnode_get_attrib(packet,"type");
 	if(!strcmp(type,"error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 		
 		purple_notify_error(js->gc, _("Error unregistering account"),
 							_("Error unregistering account"), msg);
@@ -1641,7 +1672,7 @@
 		purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"),
 				_("Your password has been changed."));
 	} else {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		purple_notify_error(js->gc, _("Error changing password"),
 				_("Error changing password"), msg);
@@ -1805,13 +1836,18 @@
 }
 
 
-char *jabber_parse_error(JabberStream *js, xmlnode *packet)
+char *jabber_parse_error(JabberStream *js,
+                         xmlnode *packet,
+                         PurpleConnectionError *reason)
 {
 	xmlnode *error;
 	const char *code = NULL, *text = NULL;
 	const char *xmlns = xmlnode_get_namespace(packet);
 	char *cdata = NULL;
 
+#define SET_REASON(x) \
+	if(reason != NULL) { *reason = x; }
+
 	if((error = xmlnode_get_child(packet, "error"))) {
 		cdata = xmlnode_get_data(error);
 		code = xmlnode_get_attrib(error, "code");
@@ -1863,41 +1899,41 @@
 			text = _("Unknown Error");
 		}
 	} else if(xmlns && !strcmp(xmlns, "urn:ietf:params:xml:ns:xmpp-sasl")) {
+		/* Most common reason can be the default */
+		SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED);
 		if(xmlnode_get_child(packet, "aborted")) {
-			js->gc->wants_to_die = TRUE;
 			text = _("Authorization Aborted");
 		} else if(xmlnode_get_child(packet, "incorrect-encoding")) {
+			SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR);
 			text = _("Incorrect encoding in authorization");
 		} else if(xmlnode_get_child(packet, "invalid-authzid")) {
-			js->gc->wants_to_die = TRUE;
 			text = _("Invalid authzid");
 		} else if(xmlnode_get_child(packet, "invalid-mechanism")) {
-			js->gc->wants_to_die = TRUE;
 			text = _("Invalid Authorization Mechanism");
 		} else if(xmlnode_get_child(packet, "mechanism-too-weak")) {
-			js->gc->wants_to_die = TRUE;
 			text = _("Authorization mechanism too weak");
 		} else if(xmlnode_get_child(packet, "not-authorized")) {
-			js->gc->wants_to_die = TRUE;
 			/* Clear the pasword if it isn't being saved */
 			if (!purple_account_get_remember_password(js->gc->account))
 				purple_account_set_password(js->gc->account, NULL);
 			text = _("Not Authorized");
 		} else if(xmlnode_get_child(packet, "temporary-auth-failure")) {
+			SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR);
 			text = _("Temporary Authentication Failure");
 		} else {
-			js->gc->wants_to_die = TRUE;
 			text = _("Authentication Failure");
 		}
 	} else if(!strcmp(packet->name, "stream:error") ||
 			 (!strcmp(packet->name, "error") && xmlns &&
 				!strcmp(xmlns, "http://etherx.jabber.org/streams"))) {
+		/* Most common reason as default: */
+		SET_REASON(PURPLE_CONNECTION_ERROR_NETWORK_ERROR);
 		if(xmlnode_get_child(packet, "bad-format")) {
 			text = _("Bad Format");
 		} else if(xmlnode_get_child(packet, "bad-namespace-prefix")) {
 			text = _("Bad Namespace Prefix");
 		} else if(xmlnode_get_child(packet, "conflict")) {
-			js->gc->wants_to_die = TRUE;
+			SET_REASON(PURPLE_CONNECTION_ERROR_NAME_IN_USE);
 			text = _("Resource Conflict");
 		} else if(xmlnode_get_child(packet, "connection-timeout")) {
 			text = _("Connection Timeout");
@@ -1946,6 +1982,8 @@
 		}
 	}
 
+#undef SET_REASON
+
 	if(text || cdata) {
 		char *ret = g_strdup_printf("%s%s%s", code ? code : "",
 				code ? ": " : "", text ? text : cdata);
--- a/libpurple/protocols/jabber/jabber.h	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Wed Oct 24 14:20:16 2007 +0000
@@ -216,7 +216,14 @@
 
 char *jabber_get_next_id(JabberStream *js);
 
-char *jabber_parse_error(JabberStream *js, xmlnode *packet);
+/** Parse an error into a human-readable string and optionally a disconnect
+ *  reason.
+ *  @param js     the stream on which the error occurred.
+ *  @param packet the error packet
+ *  @param reason where to store the disconnection reason, or @c NULL if you
+ *                don't care or you don't intend to close the connection.
+ */
+char *jabber_parse_error(JabberStream *js, xmlnode *packet, PurpleConnectionError *reason);
 
 void jabber_add_feature(const gchar *shortname, const gchar *namespace, JabberFeatureEnabled cb); /* cb may be NULL */
 void jabber_remove_feature(const gchar *shortname);
--- a/libpurple/protocols/jabber/parser.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/parser.c	Wed Oct 24 14:20:16 2007 +0000
@@ -193,7 +193,9 @@
 		js->context = xmlCreatePushParserCtxt(&jabber_parser_libxml, js, buf, len, NULL);
 		xmlParseChunk(js->context, "", 0, 0);
 	} else if (xmlParseChunk(js->context, buf, len, 0) < 0) {
-		purple_connection_error(js->gc, _("XML Parse error"));
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("XML Parse error"));
 	}
 }
 
--- a/libpurple/protocols/jabber/presence.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Wed Oct 24 14:20:16 2007 +0000
@@ -429,7 +429,7 @@
 	}
 
 	if(type && !strcmp(type, "error")) {
-		char *msg = jabber_parse_error(js, packet);
+		char *msg = jabber_parse_error(js, packet, NULL);
 
 		state = JABBER_BUDDY_STATE_ERROR;
 		jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
@@ -561,7 +561,7 @@
 		char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
 
 		if(state == JABBER_BUDDY_STATE_ERROR) {
-			char *title, *msg = jabber_parse_error(js, packet);
+			char *title, *msg = jabber_parse_error(js, packet, NULL);
 
 			if(chat->conv) {
 				title = g_strdup_printf(_("Error in chat %s"), from);
--- a/libpurple/protocols/msn/contact.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Wed Oct 24 14:20:16 2007 +0000
@@ -945,7 +945,7 @@
 		msn_get_address_book(contact, NULL, NULL);
 		*/
 		msn_session_disconnect(session);
-		purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book"));
+		purple_connection_error_reason(session->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to retrieve MSN Address Book"));
 		return FALSE;
 	}
 }
--- a/libpurple/protocols/msn/msn.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Wed Oct 24 14:20:16 2007 +0000
@@ -820,8 +820,8 @@
 
 	if (!purple_ssl_is_supported())
 	{
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc,
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
 			_("SSL support is needed for MSN. Please install a supported "
 			  "SSL library."));
 		return;
@@ -850,7 +850,9 @@
 		purple_account_set_username(account, username);
 
 	if (!msn_session_connect(session, host, port, http_method))
-		purple_connection_error(gc, _("Failed to connect to server."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Failed to connect to server."));
 }
 
 static void
--- a/libpurple/protocols/msn/session.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Wed Oct 24 14:20:16 2007 +0000
@@ -325,6 +325,7 @@
 					  const char *info)
 {
 	PurpleConnection *gc;
+	PurpleConnectionError reason;
 	char *msg;
 
 	gc = purple_account_get_connection(session->account);
@@ -332,49 +333,56 @@
 	switch (error)
 	{
 		case MSN_ERROR_SERVCONN:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(info);
 			break;
 		case MSN_ERROR_UNSUPPORTED_PROTOCOL:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("Our protocol is not supported by the "
 							 "server."));
 			break;
 		case MSN_ERROR_HTTP_MALFORMED:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("Error parsing HTTP."));
 			break;
 		case MSN_ERROR_SIGN_OTHER:
-			gc->wants_to_die = TRUE;
+			reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
 			msg = g_strdup(_("You have signed on from another location."));
 			if (!purple_account_get_remember_password(session->account))
 				purple_account_set_password(session->account, NULL);
 			break;
 		case MSN_ERROR_SERV_UNAVAILABLE:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("The MSN servers are temporarily "
 							 "unavailable. Please wait and try "
 							 "again."));
 			break;
 		case MSN_ERROR_SERV_DOWN:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("The MSN servers are going down "
 							 "temporarily."));
 			break;
 		case MSN_ERROR_AUTH:
-			gc->wants_to_die = TRUE;
+			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 			msg = g_strdup_printf(_("Unable to authenticate: %s"),
 								  (info == NULL ) ?
 								  _("Unknown error") : info);
 			break;
 		case MSN_ERROR_BAD_BLIST:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("Your MSN buddy list is temporarily "
 							 "unavailable. Please wait and try "
 							 "again."));
 			break;
 		default:
+			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 			msg = g_strdup(_("Unknown error."));
 			break;
 	}
 
 	msn_session_disconnect(session);
 
-	purple_connection_error(gc, msg);
+	purple_connection_error_reason (gc, reason, msg);
 
 	g_free(msg);
 }
--- a/libpurple/protocols/myspace/myspace.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed Oct 24 14:20:16 2007 +0000
@@ -291,8 +291,8 @@
 		/* Notify an error message also, because this is important! */
 		purple_notify_error(acct, _("MySpaceIM Error"), str, NULL);
 
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, str);
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, str);
 		g_free(str);
 		return;
 	}
@@ -315,7 +315,9 @@
 	if (!purple_proxy_connect(gc, acct, host, port, msim_connect_cb, gc)) {
 		/* TODO: try other ports if in auto mode, then save
 		 * working port and try that first next time. */
-		purple_connection_error(gc, _("Couldn't create socket"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't create socket"));
 		return;
 	}
 }
@@ -353,7 +355,9 @@
 
 	if (nc_len != MSIM_AUTH_CHALLENGE_LENGTH) {
 		purple_debug_info("msim", "bad nc length: %x != 0x%x\n", nc_len, MSIM_AUTH_CHALLENGE_LENGTH);
-		purple_connection_error(session->gc, _("Unexpected challenge length from server"));
+		purple_connection_error_reason (session->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unexpected challenge length from server"));
 		return FALSE;
 	}
 
@@ -826,7 +830,6 @@
 	serv_got_typing_stopped(session->gc, username);
 
 	g_free(username);
-	g_free(text);
 
 	return TRUE;
 }
@@ -1290,7 +1293,8 @@
 
 		purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
 				errmsg, MSIM_KEEPALIVE_INTERVAL);
-		purple_connection_error(session->gc, errmsg);
+		purple_connection_error_reason (session->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg);
 
 		purple_notify_error(session->gc, NULL, errmsg, NULL);
 
@@ -1556,7 +1560,8 @@
 		purple_notify_error(session->account, 
 				_("No username set"),
 				_("Please go to http://editprofile.myspace.com/index.cfm?fuseaction=profile.username and choose a username and try to login again."), NULL);
-		purple_connection_error(session->gc, _("No username set"));
+		purple_connection_error_reason (session->gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No username set"));
 		return FALSE;
 	}
 
@@ -1792,16 +1797,21 @@
 
 	/* Destroy session if fatal. */
 	if (msim_msg_get(msg, "fatal")) {
+		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 		purple_debug_info("msim", "fatal error, closing\n");
 		switch (err) {
 			case 260: /* Incorrect password */
+				reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+				if (!purple_account_get_remember_password(session->account))
+					purple_account_set_password(session->account, NULL);
+				break;
 			case 6: /* Logged in elsewhere */
-				session->gc->wants_to_die = TRUE;
+				reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
 				if (!purple_account_get_remember_password(session->account))
 					purple_account_set_password(session->account, NULL);
 				break;
 		}
-		purple_connection_error(session->gc, full_errmsg);
+		purple_connection_error_reason (session->gc, reason, full_errmsg);
 	} else {
 		purple_notify_error(session->account, _("MySpaceIM Error"), full_errmsg, NULL);
 	}
@@ -1871,7 +1881,6 @@
 		purple_blist_add_buddy(buddy, NULL, NULL, NULL);
 
 		user = msim_get_user_from_buddy(buddy);
-		/* TODO: free user. memory leak? */
 
 		/* All buddies on list should have 'uid' integer associated with them. */
 		purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f"));
@@ -2313,7 +2322,9 @@
 	/* libpurple/eventloop.h only defines these two */
 	if (cond != PURPLE_INPUT_READ && cond != PURPLE_INPUT_WRITE) {
 		purple_debug_info("msim_input_cb", "unknown condition=%d\n", cond);
-		purple_connection_error(gc, _("Invalid input condition"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Invalid input condition"));
 		return;
 	}
 
@@ -2331,7 +2342,9 @@
 		purple_debug_error("msim", 
 				"msim_input_cb: %d-byte read buffer full! rxoff=%d\n",
 				MSIM_READ_BUF_SIZE, session->rxoff);
-		purple_connection_error(gc, _("Read buffer full"));
+		purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Read buffer full"));
 		return;
 	}
 
@@ -2350,11 +2363,15 @@
 		purple_debug_error("msim", "msim_input_cb: read error, ret=%d, "
 			"error=%s, source=%d, fd=%d (%X))\n", 
 			n, strerror(errno), source, session->fd, session->fd);
-		purple_connection_error(gc, _("Read error"));
+		purple_connection_error_reason (gc, 
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read error"));
 		return;
 	} else if (n == 0) {
 		purple_debug_info("msim", "msim_input_cb: server disconnected\n");
-		purple_connection_error(gc, _("Server has disconnected"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server has disconnected"));
 		return;
 	}
 
@@ -2362,7 +2379,9 @@
 		purple_debug_info("msim_input_cb", "received %d bytes, pushing rxoff to %d, over buffer size of %d\n",
 				n, n + session->rxoff, MSIM_READ_BUF_SIZE);
 		/* TODO: g_realloc like msn, yahoo, irc, jabber? */
-		purple_connection_error(gc, _("Read buffer full"));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Read buffer full"));
 	}
 
 	/* Null terminate */
@@ -2377,7 +2396,9 @@
 		purple_debug_info("msim", "msim_input_cb: strlen=%d, but read %d bytes"
 				"--null byte encountered?\n", 
 				strlen(session->rxbuf + session->rxoff), n);
-		//purple_connection_error(gc, "Invalid message - null byte on input");
+		/*purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				"Invalid message - null byte on input"); */
 		return;
 	}
 #endif
@@ -2400,7 +2421,9 @@
 		msg = msim_parse(g_strdup(session->rxbuf));
 		if (!msg) {
 			purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n");
-			purple_connection_error(gc, _("Unparseable message"));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unparseable message"));
 		} else {
 			/* Process message and then free it (processing function should
 			 * clone message if it wants to keep it afterwards.) */
@@ -2467,9 +2490,9 @@
 	session = (MsimSession *)gc->proto_data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Couldn't connect to host"));
-		purple_connection_error(gc, g_strdup_printf(
-					_("Couldn't connect to host: %s (%d)"), 
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			g_strdup_printf(_("Couldn't connect to host: %s (%d)"), 
 					error_message ? error_message : "no message given", 
 					source));
 		return;
@@ -2670,7 +2693,6 @@
 	/* TODO: other fields, store in 'user' */
 
 	msim_msg_free(contact_info);
-	g_free(username);
 }
 
 /** Add first ContactID in contact_info to buddy's list. Used to add
--- a/libpurple/protocols/novell/novell.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/novell/novell.c	Wed Oct 24 14:20:16 2007 +0000
@@ -120,21 +120,27 @@
 		_check_for_disconnect(user, rc);
 
 	} else {
-
+		PurpleConnectionError reason;
 		char *err = g_strdup_printf(_("Login failed (%s)."),
 					    nm_error_to_string (ret_code));
 
-		/* Don't attempt to auto-reconnect if our password
-		 * was invalid.
-		 */
-		if (ret_code == NMERR_AUTHENTICATION_FAILED ||
-			ret_code == NMERR_CREDENTIALS_MISSING ||
-			ret_code == NMERR_PASSWORD_INVALID) {
-			if (!purple_account_get_remember_password(gc->account))
-				purple_account_set_password(gc->account, NULL);
-			gc->wants_to_die = TRUE;
+		switch (ret_code) {
+			case NMERR_AUTHENTICATION_FAILED:
+			case NMERR_CREDENTIALS_MISSING:
+			case NMERR_PASSWORD_INVALID:
+				/* Don't attempt to auto-reconnect if our
+				 * password was invalid.
+				 */
+				if (!purple_account_get_remember_password(gc->account))
+					purple_account_set_password(gc->account, NULL);
+				reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+				break;
+			default:
+				/* FIXME: There are other reasons login could fail */
+				reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
 		}
-		purple_connection_error(gc, err);
+
+		purple_connection_error_reason (gc, reason, err);
 		g_free(err);
 	}
 }
@@ -1120,8 +1126,9 @@
 
 	if (_is_disconnect_error(err)) {
 
-		purple_connection_error(gc, _("Error communicating with server."
-									" Closing connection."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Error communicating with server. Closing connection."));
 		return TRUE;
 
 	}
@@ -1667,7 +1674,7 @@
 	user = gc->proto_data;
 	user->conn->ssl_conn->data = NULL;
 
-	purple_connection_error(gc, _("Unable to make SSL connection to server."));
+	purple_connection_ssl_error (gc, error);
 }
 
 static void
@@ -1690,9 +1697,9 @@
 
 		if (_is_disconnect_error(rc)) {
 
-			purple_connection_error(gc,
-								  _("Error communicating with server."
-									" Closing connection."));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Error communicating with server. Closing connection."));
 		} else {
 			purple_debug(PURPLE_DEBUG_INFO, "novell",
 					   "Error processing event or response (%d).\n", rc);
@@ -1731,7 +1738,9 @@
 		conn->connected = TRUE;
 		purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc);
 	} else {
-		purple_connection_error(gc, _("Unable to connect to server."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to connect to server."));
 	}
 
 	purple_connection_update_progress(gc, _("Waiting for response..."),
@@ -2011,11 +2020,12 @@
 	gc = purple_account_get_connection(account);
 	if (gc)
 	{
-		gc->wants_to_die = TRUE; /* we don't want to reconnect in this case */
 		if (!purple_account_get_remember_password(account))
 			purple_account_set_password(account, NULL);
-		purple_connection_error(gc, _("You have been logged out because you"
-									" logged in at another workstation."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_NAME_IN_USE,
+			_("You have been logged out because you"
+			  " logged in at another workstation."));
 	}
 }
 
@@ -2169,9 +2179,10 @@
 		 */
 
 		/* ...but for now just error out with a nice message. */
-		purple_connection_error(gc, _("Unable to connect to server."
-									" Please enter the address of the server"
-									" you wish to connect to."));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("Unable to connect to server. Please enter the "
+			  "address of the server you wish to connect to."));
 		return;
 	}
 
@@ -2197,8 +2208,9 @@
 													  user->conn->addr, user->conn->port,
 													  novell_ssl_connected_cb, novell_ssl_connect_error, gc);
 		if (user->conn->ssl_conn->data == NULL) {
-			purple_connection_error(gc, _("Error."
-										" SSL support is not installed."));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
+				_("Error. SSL support is not installed."));
 		}
 	}
 }
--- a/libpurple/protocols/oscar/flap_connection.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Wed Oct 24 14:20:16 2007 +0000
@@ -380,11 +380,13 @@
 	{
 		/* No more FLAP connections!  Sign off this PurpleConnection! */
 		gchar *tmp;
+		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+
 		if (conn->disconnect_code == 0x0001) {
+			reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
 			tmp = g_strdup(_("You have signed on from another location."));
 			if (!purple_account_get_remember_password(account))
 				purple_account_set_password(account, NULL);
-			od->gc->wants_to_die = TRUE;
 		} else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
 			tmp = g_strdup(_("Server closed the connection."));
 		else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
@@ -404,7 +406,7 @@
 
 		if (tmp != NULL)
 		{
-			purple_connection_error(od->gc, tmp);
+			purple_connection_error_reason(od->gc, reason, tmp);
 			g_free(tmp);
 		}
 	}
--- a/libpurple/protocols/oscar/oscar.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Oct 24 14:20:16 2007 +0000
@@ -994,7 +994,7 @@
 			gchar *msg;
 			msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"),
 					error_message);
-			purple_connection_error(gc, msg);
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
 			g_free(msg);
 		}
 		else if (conn->type == SNAC_FAMILY_LOCATE)
@@ -1002,7 +1002,7 @@
 			gchar *msg;
 			msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"),
 					error_message);
-			purple_connection_error(gc, msg);
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
 			g_free(msg);
 		}
 		else
@@ -1260,8 +1260,7 @@
 	if (!aim_snvalid(purple_account_get_username(account))) {
 		gchar *buf;
 		buf = g_strdup_printf(_("Unable to login: Could not sign on as %s because the screen name is invalid.  Screen names must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, buf);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
 		g_free(buf);
 	}
 
@@ -1282,7 +1281,8 @@
 			connection_established_cb, newconn);
 	if (newconn->connect_data == NULL)
 	{
-		purple_connection_error(gc, _("Couldn't connect to host"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Couldn't connect to host"));
 		return;
 	}
 
@@ -1343,43 +1343,37 @@
 		switch (info->errorcode) {
 		case 0x01:
 			/* Unregistered screen name */
-			gc->wants_to_die = TRUE;
-			purple_connection_error(gc, _("Invalid screen name."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Invalid screen name."));
 			break;
 		case 0x05:
 			/* Incorrect password */
-			gc->wants_to_die = TRUE;
 			if (!purple_account_get_remember_password(account))
 				purple_account_set_password(account, NULL);
-			purple_connection_error(gc, _("Incorrect password."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
 			break;
 		case 0x11:
 			/* Suspended account */
-			gc->wants_to_die = TRUE;
-			purple_connection_error(gc, _("Your account is currently suspended."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended."));
 			break;
 		case 0x14:
 			/* service temporarily unavailable */
-			purple_connection_error(gc, _("The AOL Instant Messenger service is temporarily unavailable."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
 			break;
 		case 0x18:
 			/* screen name connecting too frequently */
-			gc->wants_to_die = TRUE;
-			purple_connection_error(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
 			break;
 		case 0x1c:
 			/* client too old */
-			gc->wants_to_die = TRUE;
 			g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), PURPLE_WEBSITE);
-			purple_connection_error(gc, buf);
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
 			break;
 		case 0x1d:
 			/* IP address connecting too frequently */
-			gc->wants_to_die = TRUE;
-			purple_connection_error(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
 			break;
 		default:
-			purple_connection_error(gc, _("Authentication failed"));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Authentication failed"));
 			break;
 		}
 		purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
@@ -1409,7 +1403,7 @@
 	g_free(host);
 	if (newconn->connect_data == NULL)
 	{
-		purple_connection_error(gc, _("Could Not Connect"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Could Not Connect"));
 		return 0;
 	}
 
@@ -1434,8 +1428,9 @@
 	PurpleConnection *gc = user_data;
 
 	/* Disconnect */
-	gc->wants_to_die = TRUE;
-	purple_connection_error(gc, _("The SecurID key entered is invalid."));
+	purple_connection_error_reason(gc,
+		PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+		_("The SecurID key entered is invalid."));
 }
 
 static int
--- a/libpurple/protocols/qq/keep_alive.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/keep_alive.c	Wed Oct 24 14:20:16 2007 +0000
@@ -84,7 +84,8 @@
 		/* segments[0] and segment[1] are all 0x30 ("0") */
 		qd->all_online = strtol(segments[2], NULL, 10);
 		if(0 == qd->all_online)
-			purple_connection_error(gc, _("Keep alive error"));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Keep alive error"));
 		g_free(qd->my_ip);
 		qd->my_ip = g_strdup(segments[3]);
 		qd->my_port = strtol(segments[4], NULL, 10);
--- a/libpurple/protocols/qq/login_logout.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/login_logout.c	Wed Oct 24 14:20:16 2007 +0000
@@ -405,7 +405,7 @@
 				">>> %d bytes -> [default] decrypt and dump\n%s",
 				buf_len, hex_dump);
 		try_dump_as_gbk(buf, buf_len);
-		purple_connection_error(gc, _("Error requesting login token"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token"));
 	}
 	g_free(hex_dump);
 }
@@ -479,13 +479,14 @@
 
 	switch (ret) {
 	case QQ_LOGIN_REPLY_PWD_ERROR:
-		gc->wants_to_die = TRUE;
 		if (!purple_account_get_remember_password(gc->account))
 			purple_account_set_password(gc->account, NULL);
-		purple_connection_error(gc, _("Incorrect password."));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
 		break;
 	case QQ_LOGIN_REPLY_MISC_ERROR:
-		purple_connection_error(gc, _("Unable to login, check debug log"));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login, check debug log"));
 		break;
 	case QQ_LOGIN_REPLY_OK:
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login replys OK, everything is fine\n");
--- a/libpurple/protocols/qq/qq.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 24 14:20:16 2007 +0000
@@ -136,7 +136,8 @@
 	purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS);
 
 	if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0)
-		purple_connection_error(gc, _("Unable to connect."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to connect."));
 }
 
 /* directly goes for qq_disconnect */
--- a/libpurple/protocols/qq/qq_proxy.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/qq_proxy.c	Wed Oct 24 14:20:16 2007 +0000
@@ -139,7 +139,7 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 
 	if (source < 0) {	/* socket returns -1 */
-		purple_connection_error(gc, error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
 		return;
 	}
 
@@ -494,7 +494,7 @@
 		ret = send(qd->fd, data, len, 0);
 	}
 	if (ret == -1)
-		purple_connection_error(qd->gc, strerror(errno));
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, strerror(errno));
 
 	return ret;
 }
--- a/libpurple/protocols/qq/recv_core.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/recv_core.c	Wed Oct 24 14:20:16 2007 +0000
@@ -306,7 +306,8 @@
 	gc = (PurpleConnection *) data;
 
 	if(cond != PURPLE_INPUT_READ) {
-		purple_connection_error(gc, _("Socket error"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Socket error"));
 		return;
 	}
 
@@ -316,7 +317,8 @@
 	/* here we have UDP proxy suppport */
 	len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE);
 	if (len <= 0) {
-		purple_connection_error(gc, _("Unable to read from socket"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to read from socket"));
 		return;
 	} else {
 		_qq_packet_process(buf, len, gc);
--- a/libpurple/protocols/qq/sendqueue.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/qq/sendqueue.c	Wed Oct 24 14:20:16 2007 +0000
@@ -120,7 +120,8 @@
 			case QQ_CMD_KEEP_ALIVE:
 				if (qd->logged_in) {
 					purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
-					purple_connection_error(gc, _("Connection lost"));
+					purple_connection_error_reason(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
 					qd->logged_in = FALSE;
 				}
 				p->resend_times = -1;
@@ -128,7 +129,8 @@
 			case QQ_CMD_LOGIN:
 			case QQ_CMD_REQUEST_LOGIN_TOKEN:
 				if (!qd->logged_in)	/* cancel login progress */
-					purple_connection_error(gc, _("Login failed, no reply"));
+					purple_connection_error_reason(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
 				p->resend_times = -1;
 				break;
 			default:{
--- a/libpurple/protocols/sametime/sametime.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Wed Oct 24 14:20:16 2007 +0000
@@ -414,7 +414,9 @@
 
   } else if(len > 0) {
     DEBUG_ERROR("write returned %i, %i bytes left unwritten\n", ret, len);
-    purple_connection_error(pd->gc, _("Connection closed (writing)"));
+    purple_connection_error_reason(pd->gc,
+                                   PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+                                   _("Connection closed (writing)"));
 
 #if 0
     close(pd->socket);
@@ -1552,7 +1554,38 @@
 
     if(GPOINTER_TO_UINT(info) & ERR_FAILURE) {
       char *err = mwError(GPOINTER_TO_UINT(info));
-      purple_connection_error(gc, err);
+      PurpleConnectionError reason;
+      switch (GPOINTER_TO_UINT(info)) {
+      case VERSION_MISMATCH:
+        reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+        break;
+
+      case USER_RESTRICTED:
+      case INCORRECT_LOGIN:
+      case USER_UNREGISTERED:
+      case GUEST_IN_USE:
+        reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+        break;
+
+      case ENCRYPT_MISMATCH:
+      case ERR_ENCRYPT_NO_SUPPORT:
+      case ERR_NO_COMMON_ENCRYPT:
+        reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
+        break;
+
+      case VERIFICATION_DOWN:
+        reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE;
+        break;
+
+      case MULTI_SERVER_LOGIN:
+      case MULTI_SERVER_LOGIN2:
+        reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
+        break;
+
+      default:
+        reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+      }
+      purple_connection_error_reason(gc, reason, err);
       g_free(err);
     }
     break;
@@ -1698,8 +1731,11 @@
   }
 
   if(! ret) {
+    const char *msg = _("Connection reset");
     DEBUG_INFO("connection reset\n");
-    purple_connection_error(pd->gc, _("Connection reset"));
+    purple_connection_error_reason(pd->gc,
+                                   PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+                                   msg);
 
   } else if(ret < 0) {
     char *msg = strerror(err);
@@ -1707,7 +1743,9 @@
     DEBUG_INFO("error in read callback: %s\n", msg);
 
     msg = g_strdup_printf(_("Error reading from socket: %s"), msg);
-    purple_connection_error(pd->gc, msg);
+    purple_connection_error_reason(pd->gc,
+                                   PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+                                   msg);
     g_free(msg);
   }
 }
@@ -1729,7 +1767,10 @@
 
     } else {
       /* this is a regular connect, error out */
-      purple_connection_error(pd->gc, _("Unable to connect to host"));
+      const char *msg = _("Unable to connect to host");
+      purple_connection_error_reason(pd->gc,
+                                     PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+                                     msg);
     }
 
     return;
@@ -3611,7 +3652,10 @@
 
 
 static void prompt_host_cancel_cb(PurpleConnection *gc) {
-  purple_connection_error(gc, _("No Sametime Community Server specified"));
+  const char *msg = _("No Sametime Community Server specified");
+  purple_connection_error_reason(gc,
+                                 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+                                 msg);
 }
 
 
@@ -3723,7 +3767,8 @@
   purple_connection_update_progress(gc, _("Connecting"), 1, MW_CONNECT_STEPS);
 
   if (purple_proxy_connect(gc, account, host, port, connect_cb, pd) == NULL) {
-    purple_connection_error(gc, _("Unable to connect to host"));
+    purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+                                   _("Unable to connect to host"));
   }
 }
 
--- a/libpurple/protocols/silc/silc.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Wed Oct 24 14:20:16 2007 +0000
@@ -214,34 +214,40 @@
 
 		/* Close the connection */
 		if (!sg->detaching)
-		  purple_connection_error(gc, _("Disconnected by server"));
+		  purple_connection_error_reason(gc,
+		                                 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                                 _("Disconnected by server"));
 		else
 		  /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
 		  purple_account_disconnect(purple_connection_get_account(gc));
 		break;
 
 	case SILC_CLIENT_CONN_ERROR:
-		purple_connection_error(gc, _("Error during connecting to SILC Server"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                             _("Error during connecting to SILC Server"));
 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
 		break;
 
 	case SILC_CLIENT_CONN_ERROR_KE:
-		purple_connection_error(gc, _("Key Exchange failed"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+		                             _("Key Exchange failed"));
 		break;
 
 	case SILC_CLIENT_CONN_ERROR_AUTH:
-		purple_connection_error(gc, _("Authentication failed"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+		                             _("Authentication failed"));
 		break;
 
 	case SILC_CLIENT_CONN_ERROR_RESUME:
-		purple_connection_error(gc,
-				      _("Resuming detached session failed. "
-					"Press Reconnect to create new connection."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		                             _("Resuming detached session failed. "
+		                               "Press Reconnect to create new connection."));
 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
 		break;
 
 	case SILC_CLIENT_CONN_ERROR_TIMEOUT:
-		purple_connection_error(gc, _("Connection Timeout"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                             _("Connection Timeout"));
 		break;
 	}
 
@@ -262,7 +268,8 @@
 	sg = gc->proto_data;
 
 	if (status != SILC_SOCKET_OK) {
-		purple_connection_error(gc, _("Connection failed"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                             _("Connection failed"));
 		silc_pkcs_public_key_free(sg->public_key);
 		silc_pkcs_private_key_free(sg->private_key);
 		silc_free(sg);
@@ -308,7 +315,8 @@
 	sg = gc->proto_data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Connection failed"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                             _("Connection failed"));
 		silc_pkcs_public_key_free(sg->public_key);
 		silc_pkcs_private_key_free(sg->private_key);
 		silc_free(sg);
@@ -349,8 +357,8 @@
 				(char *)purple_account_get_string(account, "private-key", prd),
 				(gc->password == NULL) ? "" : gc->password,
 				&sg->public_key, &sg->private_key)) {
-		g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair"));
-		purple_connection_error(gc, pkd);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		                             _("Could not load SILC key pair"));
 		gc->proto_data = NULL;
 		silc_free(sg);
 		return;
@@ -363,7 +371,8 @@
 				 purple_account_get_int(account, "port", 706),
 				 silcpurple_login_connected, gc) == NULL)
 	{
-		purple_connection_error(gc, _("Unable to create connection"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                             _("Unable to create connection"));
 		gc->proto_data = NULL;
 		silc_free(sg);
 		return;
@@ -392,7 +401,8 @@
 	/* Allocate SILC client */
 	client = silc_client_alloc(&ops, &params, gc, NULL);
 	if (!client) {
-		purple_connection_error(gc, _("Out of memory"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		                             _("Out of memory"));
 		return;
 	}
 
@@ -434,15 +444,15 @@
 	/* Init SILC client */
 	if (!silc_client_init(client, username, hostname, realname,
 			      silcpurple_running, account)) {
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, _("Cannot initialize SILC protocol"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		                             _("Cannot initialize SILC protocol"));
 		return;
 	}
 
 	/* Check the ~/.silc dir and create it, and new key pair if necessary. */
 	if (!silcpurple_check_silc_dir(gc)) {
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, _("Error loading SILC key pair"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		                             _("Error loading SILC key pair"));
 		return;
 	}
 
--- a/libpurple/protocols/silc/util.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/silc/util.c	Wed Oct 24 14:20:16 2007 +0000
@@ -212,7 +212,8 @@
 						  (gc->password == NULL)
 						  ? "" : gc->password,
 						  NULL, NULL, FALSE)) {
-				purple_connection_error(gc, _("Cannot create SILC key pair\n"));
+				purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+				                             _("Cannot create SILC key pair\n"));
 				return FALSE;
 			}
 
@@ -254,7 +255,8 @@
 						  (gc->password == NULL)
 						  ? "" : gc->password,
 						  NULL, NULL, FALSE)) {
-				purple_connection_error(gc, _("Cannot create SILC key pair\n"));
+				purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+				                             _("Cannot create SILC key pair\n"));
 				return FALSE;
 			}
 
--- a/libpurple/protocols/simple/simple.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Wed Oct 24 14:20:16 2007 +0000
@@ -414,7 +414,9 @@
 		written = 0;
 	else if(written <= 0) {
 		/*TODO: do we really want to disconnect on a failure to write?*/
-		purple_connection_error(gc, _("Could not write"));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not write"));
 		return;
 	}
 
@@ -436,7 +438,9 @@
 	}
 
 	if(source < 0) {
-		purple_connection_error(gc, _("Could not connect"));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not connect"));
 		return;
 	}
 
@@ -462,7 +466,7 @@
 	if(!sip->connecting) {
 		purple_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
 		if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
-			purple_connection_error(gc, _("Couldn't create socket"));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Couldn't create socket"));
 		}
 		sip->connecting = TRUE;
 	}
@@ -1041,11 +1045,11 @@
 			if(sip->registerstatus != SIMPLE_REGISTER_RETRY) {
 				purple_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries);
 				if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) {
-					purple_debug_info("simple", "Setting wants_to_die to true.\n");
-					sip->gc->wants_to_die = TRUE;
 					if (!purple_account_get_remember_password(sip->gc->account))
 						purple_account_set_password(sip->gc->account, NULL);
-					purple_connection_error(sip->gc, _("Incorrect password."));
+					purple_connection_error_reason(sip->gc,
+						PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+						_("Incorrect password."));
 					return TRUE;
 				}
 				tmp = sipmsg_find_header(msg, "WWW-Authenticate");
@@ -1058,8 +1062,9 @@
 			if (sip->registerstatus != SIMPLE_REGISTER_RETRY) {
 				purple_debug_info("simple", "Unrecognized return code for REGISTER.\n");
 				if (sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) {
-					sip->gc->wants_to_die = TRUE;
-					purple_connection_error(sip->gc, _("Unknown server response."));
+					purple_connection_error_reason(sip->gc,
+						PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+						_("Unknown server response."));
 					return TRUE;
 				}
 				sip->registerstatus = SIMPLE_REGISTER_RETRY;
@@ -1542,7 +1547,9 @@
 	}
 
 	if(source < 0) {
-		purple_connection_error(gc, _("Could not connect"));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not connect"));
 		return;
 	}
 
@@ -1576,7 +1583,9 @@
 	sip->listen_data = NULL;
 
 	if(listenfd == -1) {
-		purple_connection_error(sip->gc, _("Could not create listen socket"));
+		purple_connection_error_reason(sip->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not create listen socket"));
 		return;
 	}
 
@@ -1599,7 +1608,9 @@
 	sip->query_data = NULL;
 
 	if (!hosts || !hosts->data) {
-		purple_connection_error(sip->gc, _("Couldn't resolve host"));
+		purple_connection_error_reason(sip->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't resolve host"));
 		return;
 	}
 
@@ -1618,7 +1629,9 @@
 	sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
 				simple_udp_host_resolved_listen_cb, sip);
 	if (sip->listen_data == NULL) {
-		purple_connection_error(sip->gc, _("Could not create listen socket"));
+		purple_connection_error_reason(sip->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not create listen socket"));
 		return;
 	}
 }
@@ -1631,7 +1644,9 @@
 
 	sip->listenfd = listenfd;
 	if(sip->listenfd == -1) {
-		purple_connection_error(sip->gc, _("Could not create listen socket"));
+		purple_connection_error_reason(sip->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not create listen socket"));
 		return;
 	}
 
@@ -1644,7 +1659,9 @@
 	/* open tcp connection to the server */
 	if (purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
 			sip->realport, login_cb, sip->gc) == NULL) {
-		purple_connection_error(sip->gc, _("Couldn't create socket"));
+		purple_connection_error_reason(sip->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't create socket"));
 	}
 }
 
@@ -1682,7 +1699,9 @@
 		sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
 					simple_tcp_connect_listen_cb, sip);
 		if (sip->listen_data == NULL) {
-			purple_connection_error(sip->gc, _("Could not create listen socket"));
+			purple_connection_error_reason(sip->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Could not create listen socket"));
 			return;
 		}
 	} else { /* UDP */
@@ -1690,7 +1709,9 @@
 
 		sip->query_data = purple_dnsquery_a(hostname, port, simple_udp_host_resolved, sip);
 		if (sip->query_data == NULL) {
-			purple_connection_error(sip->gc, _("Could not resolve hostname"));
+			purple_connection_error_reason(sip->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Could not resolve hostname"));
 		}
 	}
 }
@@ -1706,8 +1727,9 @@
 	gc = purple_account_get_connection(account);
 
 	if (strpbrk(username, " \t\v\r\n") != NULL) {
-		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, _("SIP screen names may not contain whitespaces or @ symbols"));
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("SIP screen names may not contain whitespaces or @ symbols"));
 		return;
 	}
 
--- a/libpurple/protocols/yahoo/yahoo.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed Oct 24 14:20:16 2007 +0000
@@ -201,10 +201,10 @@
 	char *message = NULL;
 
 	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
-		gc->wants_to_die = TRUE;
 		if (!purple_account_get_remember_password(account))
 			purple_account_set_password(account, NULL);
-		purple_connection_error(gc, _("You have signed on from another location."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
+			_("You have signed on from another location."));
 		return;
 	}
 
@@ -2139,8 +2139,7 @@
 	else
 		fullmsg = g_strdup(msg);
 
-	gc->wants_to_die = TRUE;
-	purple_connection_error(gc, fullmsg);
+	purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, fullmsg);
 	g_free(msg);
 	g_free(fullmsg);
 }
@@ -2464,11 +2463,12 @@
 
 		tmp = g_strdup_printf(_("Lost connection with server:\n%s"),
 				strerror(errno));
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	} else if (len == 0) {
-		purple_connection_error(gc, _("Server closed the connection."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Server closed the connection."));
 		return;
 	}
 
@@ -2559,7 +2559,7 @@
 		gchar *tmp;
 		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
 				error_message);
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	}
@@ -2591,7 +2591,7 @@
 		gchar *tmp;
 		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
 				error_message);
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	}
@@ -2631,11 +2631,12 @@
 
 		tmp = g_strdup_printf(_("Lost connection with server:\n%s"),
 				strerror(errno));
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	} else if (len == 0) {
-		purple_connection_error(gc, _("Server closed the connection."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Server closed the connection."));
 		return;
 	}
 
@@ -2650,7 +2651,8 @@
 
 	if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) &&
 			  strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
-		purple_connection_error(gc, _("Received unexpected HTTP response from server."));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Received unexpected HTTP response from server."));
 		return;
 	}
 
@@ -2672,9 +2674,10 @@
 	yd->rxlen = 0;
 	/* Now we have our cookies to login with.  I'll go get the milk. */
 	if (purple_proxy_connect(gc, account, "wcs2.msg.dcn.yahoo.com",
-			       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
-			       yahoo_got_web_connected, gc) == NULL) {
-		purple_connection_error(gc, _("Connection problem"));
+	                         purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
+	                         yahoo_got_web_connected, gc) == NULL) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Connection problem"));
 		return;
 	}
 }
@@ -2702,7 +2705,7 @@
 		gc->inpa = 0;
 		tmp = g_strdup_printf(_("Lost connection with %s:\n%s"),
 				"login.yahoo.com:80", strerror(errno));
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	}
@@ -2727,7 +2730,7 @@
 		gchar *tmp;
 		tmp = g_strdup_printf(_("Could not establish a connection with %s:\n%s"),
 				"login.yahoo.com:80", error_message);
-		purple_connection_error(gc, tmp);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return;
 	}
@@ -2811,7 +2814,8 @@
 
 	if (error_message != NULL)
 	{
-		purple_connection_error(gc, error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               error_message);
 		return;
 	}
 
@@ -2860,7 +2864,8 @@
 	g_hash_table_destroy(hash);
 	yd->auth = g_string_free(url, FALSE);
 	if (purple_proxy_connect(gc, account, "login.yahoo.com", 80, yahoo_got_cookies, gc) == NULL) {
-		purple_connection_error(gc, _("Connection problem"));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Connection problem"));
 		return;
 	}
 
@@ -2963,7 +2968,8 @@
 		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
 		                       yahoo_got_connected, gc) == NULL)
 		{
-			purple_connection_error(gc, _("Connection problem"));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			                               _("Connection problem"));
 			return;
 		}
 	} else {
@@ -2973,7 +2979,8 @@
 		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
 		                       yahoo_got_connected, gc) == NULL)
 		{
-			purple_connection_error(gc, _("Connection problem"));
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			                               _("Connection problem"));
 			return;
 		}
 	}
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Wed Oct 24 14:20:16 2007 +0000
@@ -304,7 +304,8 @@
 		return;
 	else if (ret < 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
-		purple_connection_error(yd->gc, _("Write Error"));
+		purple_connection_error_reason(yd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Write Error"));
 		return;
 	}
 
--- a/libpurple/protocols/yahoo/ycht.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/protocols/yahoo/ycht.c	Wed Oct 24 14:20:16 2007 +0000
@@ -285,7 +285,8 @@
 	else if (ret <= 0) {
 		/* TODO: error handling */
 /*
-		purple_connection_error(purple_account_get_connection(irc->account),
+		purple_connection_error_reason(purple_account_get_connection(irc->account),
+			      PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			      _("Server has disconnected"));
 */
 		return;
--- a/libpurple/sslconn.h	Wed Oct 24 10:14:58 2007 +0000
+++ b/libpurple/sslconn.h	Wed Oct 24 14:20:16 2007 +0000
@@ -26,11 +26,6 @@
 #ifndef _PURPLE_SSLCONN_H_
 #define _PURPLE_SSLCONN_H_
 
-#include "certificate.h"
-#include "proxy.h"
-
-#define PURPLE_SSL_DEFAULT_PORT 443
-
 /** Possible SSL errors. */
 typedef enum
 {
@@ -39,6 +34,11 @@
 	PURPLE_SSL_CERTIFICATE_INVALID = 3
 } PurpleSslErrorType;
 
+#include "certificate.h"
+#include "proxy.h"
+
+#define PURPLE_SSL_DEFAULT_PORT 443
+
 typedef struct _PurpleSslConnection PurpleSslConnection;
 
 typedef void (*PurpleSslInputFunction)(gpointer, PurpleSslConnection *,
@@ -126,9 +126,9 @@
 	/** Obtains the certificate chain provided by the peer
 	 *
 	 * @param gsc   Connection context
-	 * @return      A newly allocated list of #PurpleCertificate containing the
-	 *              certificates the peer provided.
-	 * @see purple_ssl_get_peer_certificates
+	 * @return      A newly allocated list containing the certificates
+	 *              the peer provided.
+	 * @see PurpleCertificate
 	 * @todo        Decide whether the ordering of certificates in this
 	 *              list can be guaranteed.
 	 */
--- a/pidgin/gtkconn.c	Wed Oct 24 10:14:58 2007 +0000
+++ b/pidgin/gtkconn.c	Wed Oct 24 14:20:16 2007 +0000
@@ -42,6 +42,7 @@
 #define INITIAL_RECON_DELAY_MAX 60000
 
 #define MAX_RECON_DELAY 600000
+#define MAX_RACCOON_DELAY "shorter in urban areas"
 
 typedef struct {
 	int delay;
@@ -137,7 +138,9 @@
 }
 
 static void
-pidgin_connection_report_disconnect(PurpleConnection *gc, const char *text)
+pidgin_connection_report_disconnect_reason (PurpleConnection *gc,
+                                            PurpleConnectionError reason,
+                                            const char *text)
 {
 	PurpleAccount *account = NULL;
 	PidginAutoRecon *info;
@@ -147,7 +150,7 @@
 	info = g_hash_table_lookup(auto_reconns, account);
 
 	pidgin_blist_update_account_error_state(account, text);
-	if (!gc->wants_to_die) {
+	if (!purple_connection_error_is_fatal (reason)) {
 		if (info == NULL) {
 			info = g_new0(PidginAutoRecon, 1);
 			g_hash_table_insert(auto_reconns, account, info);
@@ -178,9 +181,29 @@
 		}
 
 		p = g_strdup_printf(_("%s disconnected"), n);
-		s = g_strdup_printf(_("%s\n\n"
-				"%s will not attempt to reconnect the account until you "
-				"correct the error and re-enable the account."), text, PIDGIN_NAME);
+		switch (reason)
+		{
+			case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
+				s = g_strdup_printf(
+					_("%s\n\n"
+					"%s will not attempt to reconnect the account until you "
+					"re-enable the account.  See %s for information on how to "
+					"compile %s with SSL support."), text, PIDGIN_NAME,
+					"http://developer.pidgin.im/wiki/FAQssl", PIDGIN_NAME);
+				break;
+			case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
+				s = g_strdup_printf(
+					_("%s\n\n"
+					"%s will not attempt to reconnect the account until you "
+					"re-enable it."), text, PIDGIN_NAME);
+				break;
+			default:
+				s = g_strdup_printf(
+					_("%s\n\n"
+					"%s will not attempt to reconnect the account until you "
+					"correct the error and re-enable the account."), text,
+					PIDGIN_NAME);
+		}
 		purple_notify_error(NULL, NULL, p, s);
 		g_free(p);
 		g_free(s);
@@ -259,10 +282,10 @@
 	pidgin_connection_connected,
 	pidgin_connection_disconnected,
 	pidgin_connection_notice,
-	pidgin_connection_report_disconnect,
+	NULL, /* report_disconnect */
 	pidgin_connection_network_connected,
 	pidgin_connection_network_disconnected,
-	NULL,
+	pidgin_connection_report_disconnect_reason,
 	NULL,
 	NULL,
 	NULL