diff libpurple/protocols/jabber/jabber.c @ 25125:907ca9a36fe0

explicit merge of '714a7c7f903d11c96ffade34966121da549d998f' and 'd2c40fe4e2181eda5c1c631c7805f17e6b5d22c3' to branch 'org.darkrain42.pidgin.xmpp'
author Paul Aurich <paul@darkrain42.org>
date Thu, 20 Nov 2008 21:13:56 +0000
parents a03a953ba63d ba362a67278c
children 9ab681f23007
line wrap: on
line diff
--- a/libpurple/protocols/jabber/jabber.c	Mon Aug 18 17:08:01 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Nov 20 21:13:56 2008 +0000
@@ -68,6 +68,7 @@
 GHashTable *jabber_contact_info = NULL;
 
 static void jabber_unregister_account_cb(JabberStream *js);
+static void try_srv_connect(JabberStream *js);
 
 static void jabber_stream_init(JabberStream *js)
 {
@@ -278,9 +279,42 @@
 	purple_circ_buffer_mark_read(js->write_buffer, ret);
 }
 
+static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
+{
+	int ret;
+	gboolean success = TRUE;
+
+	if (len == -1)
+		len = strlen(data);
+
+	if (js->writeh == 0)
+		ret = jabber_do_send(js, data, len);
+	else {
+		ret = -1;
+		errno = EAGAIN;
+	}
+
+	if (ret < 0 && errno != EAGAIN) {
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Write error"));
+		success = FALSE;
+	} else if (ret < len) {
+		if (ret < 0)
+			ret = 0;
+		if (js->writeh == 0)
+			js->writeh = purple_input_add(
+				js->gsc ? js->gsc->fd : js->fd,
+				PURPLE_INPUT_WRITE, jabber_send_cb, js);
+		purple_circ_buffer_append(js->write_buffer,
+			data + ret, len - ret);
+	}
+
+	return success;
+}
+
 void jabber_send_raw(JabberStream *js, const char *data, int len)
 {
-	int ret;
 
 	/* because printing a tab to debug every minute gets old */
 	if(strcmp(data, "\t"))
@@ -289,55 +323,33 @@
 
 	/* If we've got a security layer, we need to encode the data,
 	 * splitting it on the maximum buffer length negotiated */
-	
+
 	purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data);
 	if (data == NULL)
 		return;
-	
+
 #ifdef HAVE_CYRUS_SASL
 	if (js->sasl_maxbuf>0) {
-		int pos;
+		int pos = 0;
 
 		if (!js->gsc && js->fd<0)
 			return;
-		pos = 0;
+
 		if (len == -1)
 			len = strlen(data);
+
 		while (pos < len) {
 			int towrite;
 			const char *out;
 			unsigned olen;
 
-			if ((len - pos) < js->sasl_maxbuf)
-				towrite = len - pos;
-			else
-				towrite = js->sasl_maxbuf;
+			towrite = MIN((len - pos), js->sasl_maxbuf);
 
 			sasl_encode(js->sasl, &data[pos], towrite, &out, &olen);
 			pos += towrite;
 
-			if (js->writeh == 0)
-				ret = jabber_do_send(js, out, olen);
-			else {
-				ret = -1;
-				errno = EAGAIN;
-			}
-
-			if (ret < 0 && errno != EAGAIN)
-				purple_connection_error_reason (js->gc,
-					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Write error"));
-			else if (ret < olen) {
-				if (ret < 0)
-					ret = 0;
-				if (js->writeh == 0)
-					js->writeh = purple_input_add(
-						js->gsc ? js->gsc->fd : js->fd,
-						PURPLE_INPUT_WRITE,
-						jabber_send_cb, js);
-				purple_circ_buffer_append(js->write_buffer,
-					out + ret, olen - ret);
-			}
+			if (!do_jabber_send_raw(js, out, olen))
+				break;
 		}
 		return;
 	}
@@ -354,29 +366,8 @@
 							_("Someone tried to send non-XML in a Jabber world."));
 		}
 	} else {
-		if (js->writeh == 0)
-			ret = jabber_do_send(js, data, len);
-		else {
-			ret = -1;
-			errno = EAGAIN;
-		}
-
-		if (ret < 0 && errno != EAGAIN)
-			purple_connection_error_reason (js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Write error"));
-		else if (ret < len) {
-			if (ret < 0)
-				ret = 0;
-			if (js->writeh == 0)
-				js->writeh = purple_input_add(
-					js->gsc ? js->gsc->fd : js->fd,
-					PURPLE_INPUT_WRITE, jabber_send_cb, js);
-			purple_circ_buffer_append(js->write_buffer,
-				data + ret, len - ret);
-		}
+		do_jabber_send_raw(js, data, len);
 	}
-	return;
 }
 
 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
@@ -402,9 +393,9 @@
 	g_free(txt);
 }
 
-static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) 
+static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused)
 {
-	purple_timeout_remove(GPOINTER_TO_INT(timeout));
+	purple_timeout_remove(js->keepalive_timeout);
 	js->keepalive_timeout = -1;
 }
 
@@ -428,7 +419,7 @@
 		xmlnode_set_namespace(ping, "urn:xmpp:ping");
 		
 		js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
-		jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
+		jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
 		jabber_iq_send(iq);
 	}
 }
@@ -457,12 +448,17 @@
 			jabber_stream_init(js);
 	}
 
-	if(errno == EAGAIN)
+	if(len < 0 && errno == EAGAIN)
 		return;
-	else
+	else {
+		if (len == 0)
+			purple_debug_info("jabber", "Server closed the connection.\n");
+		else
+			purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno));
 		purple_connection_error_reason (js->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Read Error"));
+	}
 }
 
 static void
@@ -497,9 +493,13 @@
 		jabber_parser_process(js, buf, len);
 		if(js->reinit)
 			jabber_stream_init(js);
-	} else if(errno == EAGAIN) {
+	} else if(len < 0 && errno == EAGAIN) {
 		return;
 	} else {
+		if (len == 0)
+			purple_debug_info("jabber", "Server closed the connection.\n");
+		else
+			purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno));
 		purple_connection_error_reason (js->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Read Error"));
@@ -578,11 +578,19 @@
 	JabberStream *js = gc->proto_data;
 
 	if (source < 0) {
-		purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
-		purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc);
+		if (js->srv_rec != NULL) {
+			purple_debug_error("jabber", "Unable to connect to server: %s.  Trying next SRV record.\n", error);
+			try_srv_connect(js);
+		} else {
+			purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
+			purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc);
+		}
 		return;
 	}
 
+	g_free(js->srv_rec);
+	js->srv_rec = NULL;
+
 	js->fd = source;
 
 	if(js->state == JABBER_STREAM_CONNECTING)
@@ -617,37 +625,62 @@
 			jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc);
 }
 
-static void jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port)
+static gboolean jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port,
+				 gboolean fatal_failure)
 {
 	/* host should be used in preference to domain to
 	 * allow SASL authentication to work with FQDN of the server,
 	 * but we use domain as fallback for when users enter IP address
 	 * in connect server */
+	g_free(js->serverFQDN);
 	if (purple_ip_address_is_valid(host))
 		js->serverFQDN = g_strdup(domain);
 	else
 		js->serverFQDN = g_strdup(host);
 
 	if (purple_proxy_connect(js->gc, js->gc->account, host,
-			port, jabber_login_callback, js->gc) == NULL)
-		purple_connection_error_reason (js->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to create socket"));
+			port, jabber_login_callback, js->gc) == NULL) {
+		if (fatal_failure) {
+			purple_connection_error_reason (js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to create socket"));
+		}
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void try_srv_connect(JabberStream *js)
+{
+	while (js->srv_rec != NULL && js->srv_rec_idx < js->max_srv_rec_idx) {
+		PurpleSrvResponse *tmp_resp = js->srv_rec + (js->srv_rec_idx++);
+		if (jabber_login_connect(js, tmp_resp->hostname, tmp_resp->hostname, tmp_resp->port, FALSE))
+			return;
+	}
+
+	g_free(js->srv_rec);
+	js->srv_rec = NULL;
+
+	/* Fall back to the defaults (I'm not sure if we should actually do this) */
+	jabber_login_connect(js, js->user->domain, js->user->domain,
+		purple_account_get_int(js->gc->account, "port", 5222), TRUE);
 }
 
 static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data)
 {
-	JabberStream *js;
-
-	js = data;
+	JabberStream *js = data;
 	js->srv_query_data = NULL;
 
 	if(results) {
-		jabber_login_connect(js, resp->hostname, resp->hostname, resp->port);
-		g_free(resp);
+		js->srv_rec = resp;
+		js->srv_rec_idx = 0;
+		js->max_srv_rec_idx = results;
+		try_srv_connect(js);
 	} else {
 		jabber_login_connect(js, js->user->domain, js->user->domain,
-			purple_account_get_int(js->gc->account, "port", 5222));
+			purple_account_get_int(js->gc->account, "port", 5222), TRUE);
 	}
 }
 
@@ -729,7 +762,7 @@
 	 * invoke the magic of SRV lookups, to figure out host and port */
 	if(!js->gsc) {
 		if(connect_server[0]) { 
-			jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222));
+			jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE);
 		} else {
 			js->srv_query_data = purple_srv_resolve("xmpp-client",
 					"tcp", js->user->domain, srv_resolved_cb, js);
@@ -1210,7 +1243,7 @@
 		if (connect_server[0]) {
 			jabber_login_connect(js, js->user->domain, server,
 			                     purple_account_get_int(account,
-			                                          "port", 5222));
+			                                          "port", 5222), TRUE);
 		} else {
 			js->srv_query_data = purple_srv_resolve("xmpp-client",
 			                                      "tcp",
@@ -1381,7 +1414,10 @@
 
 	if (js->keepalive_timeout != -1)
 		purple_timeout_remove(js->keepalive_timeout);
-	
+
+	g_free(js->srv_rec);
+	js->srv_rec = NULL;
+
 	g_free(js);
 
 	gc->proto_data = NULL;