changeset 29772:3ac7f10d13d4

jabber: Send whitespace keepalives every two minutes of (outgoing) silence. This works around stupid behavior of Openfire hopefully (otherwise we may need to send a full IQ ping instead of a whitespace tab). Closes #10767
author Paul Aurich <paul@darkrain42.org>
date Wed, 21 Apr 2010 19:07:19 +0000
parents fb270a3d0732
children 9e60e300541a
files ChangeLog libpurple/protocols/jabber/bosh.c libpurple/protocols/jabber/bosh.h libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h
diffstat 5 files changed, 70 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Apr 21 17:43:36 2010 +0000
+++ b/ChangeLog	Wed Apr 21 19:07:19 2010 +0000
@@ -84,6 +84,10 @@
 	  BoB XEP).
 	* Present a better error message when authentication fails while trying
 	  to connect to Facebook.  (David Reiss, Facebook)
+	* Send whitespace keepalives if we haven't sent data in a while (2
+	  minutes).  This fixes an issue with Openfire disconnecting a
+	  libpurple-baesd client that has just been quiet for about 6
+	  minutes.
 
 	Yahoo/Yahoo JAPAN:
 	* Attempt to better handle transparent proxies interfering with HTTP-based
--- a/libpurple/protocols/jabber/bosh.c	Wed Apr 21 17:43:36 2010 +0000
+++ b/libpurple/protocols/jabber/bosh.c	Wed Apr 21 19:07:19 2010 +0000
@@ -78,13 +78,11 @@
 	} state;
 	guint8 failed_connections;
 
-	int max_inactivity;
 	int wait;
 
 	int max_requests;
 	int requests;
 
-	guint inactivity_timer;
 	guint send_timer;
 };
 
@@ -239,8 +237,6 @@
 
 	if (conn->send_timer)
 		purple_timeout_remove(conn->send_timer);
-	if (conn->inactivity_timer)
-		purple_timeout_remove(conn->inactivity_timer);
 
 	purple_circ_buffer_destroy(conn->pending);
 
@@ -433,34 +429,14 @@
 	return FALSE;
 }
 
-static gboolean
-bosh_inactivity_cb(gpointer data)
+void
+jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *bosh)
 {
-	PurpleBOSHConnection *bosh = data;
-	bosh->inactivity_timer = 0;
-
 	if (bosh->send_timer != 0)
 		purple_timeout_remove(bosh->send_timer);
 
 	/* clears bosh->send_timer */
 	send_timer_cb(bosh);
-
-	return FALSE;
-}
-
-static void
-restart_inactivity_timer(PurpleBOSHConnection *conn)
-{
-	if (conn->inactivity_timer != 0) {
-		purple_timeout_remove(conn->inactivity_timer);
-		conn->inactivity_timer = 0;
-	}
-
-	if (conn->max_inactivity != 0) {
-		conn->inactivity_timer =
-			purple_timeout_add_seconds(conn->max_inactivity - 5 /* rounding */,
-			                           bosh_inactivity_cb, conn);
-	}
 }
 
 static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
@@ -541,19 +517,20 @@
 	}
 
 	if (inactivity) {
-		conn->max_inactivity = atoi(inactivity);
-		if (conn->max_inactivity <= 5) {
+		js->max_inactivity = atoi(inactivity);
+		if (js->max_inactivity <= 5) {
 			purple_debug_warning("jabber", "Ignoring bogusly small inactivity: %s\n",
 			                     inactivity);
-			conn->max_inactivity = 0;
+			/* Leave it at the default */
 		} else {
-			/* TODO: Integrate this with jabber.c keepalive checks... */
 			/* TODO: Can this check fail? It shouldn't */
-			if (conn->inactivity_timer == 0) {
+			js->max_inactivity -= 5; /* rounding */
+
+			if (js->inactivity_timer == 0) {
 				purple_debug_misc("jabber", "Starting BOSH inactivity timer "
 						"for %d secs (compensating for rounding)\n",
-						conn->max_inactivity - 5);
-				restart_inactivity_timer(conn);
+						js->max_inactivity);
+				jabber_stream_restart_inactivity_timer(js);
 			}
 		}
 	}
@@ -976,7 +953,7 @@
 	size_t len;
 
 	/* Sending something to the server, restart the inactivity timer */
-	restart_inactivity_timer(conn->bosh);
+	jabber_stream_restart_inactivity_timer(conn->bosh->js);
 
 	data = g_strdup_printf("POST %s HTTP/1.1\r\n"
 	                       "Host: %s\r\n"
--- a/libpurple/protocols/jabber/bosh.h	Wed Apr 21 17:43:36 2010 +0000
+++ b/libpurple/protocols/jabber/bosh.h	Wed Apr 21 19:07:19 2010 +0000
@@ -35,6 +35,7 @@
 void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn);
 
 gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn);
+void jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *conn);
 
 void jabber_bosh_connection_connect(PurpleBOSHConnection *conn);
 void jabber_bosh_connection_close(PurpleBOSHConnection *conn);
--- a/libpurple/protocols/jabber/jabber.c	Wed Apr 21 17:43:36 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Apr 21 19:07:19 2010 +0000
@@ -71,6 +71,10 @@
 #include "jingle/rtp.h"
 
 #define PING_TIMEOUT 60
+/* Send a whitespace keepalive to the server if we haven't sent
+ * anything in the last 120 seconds
+ */
+#define DEFAULT_INACTIVITY_TIME 120
 
 GList *jabber_features = NULL;
 GList *jabber_identities = NULL;
@@ -363,6 +367,9 @@
 	if (len == -1)
 		len = strlen(data);
 
+	if (js->state == JABBER_STREAM_CONNECTED)
+		jabber_stream_restart_inactivity_timer(js);
+
 	if (js->writeh == 0)
 		ret = jabber_do_send(js, data, len);
 	else {
@@ -889,6 +896,7 @@
 	js->write_buffer = purple_circ_buffer_new(512);
 	js->old_length = 0;
 	js->keepalive_timeout = 0;
+	js->max_inactivity = DEFAULT_INACTIVITY_TIME;
 	/* Set the default protocol version to 1.0. Overridden in parser.c. */
 	js->protocol_version.major = 1;
 	js->protocol_version.minor = 0;
@@ -1583,6 +1591,8 @@
 
 	if (js->keepalive_timeout != 0)
 		purple_timeout_remove(js->keepalive_timeout);
+	if (js->inactivity_timer != 0)
+		purple_timeout_remove(js->inactivity_timer);
 
 	g_free(js->srv_rec);
 	js->srv_rec = NULL;
@@ -1634,6 +1644,9 @@
 		case JABBER_STREAM_CONNECTED:
 			/* Send initial presence */
 			jabber_presence_send(js, TRUE);
+			/* Start up the inactivity timer */
+			jabber_stream_restart_inactivity_timer(js);
+
 			purple_connection_set_state(js->gc, PURPLE_CONNECTED);
 			break;
 	}
@@ -1922,6 +1935,38 @@
 	       (!js->bosh && js->gsc);
 }
 
+static gboolean
+inactivity_cb(gpointer data)
+{
+	JabberStream *js = data;
+
+	/* We want whatever is sent to set this.  It's okay because
+	 * the eventloop unsets it via the return FALSE.
+	 */
+	js->inactivity_timer = 0;
+
+	if (js->bosh)
+		jabber_bosh_connection_send_keepalive(js->bosh);
+	else
+		jabber_send_raw(js, "\t", 1);
+
+	return FALSE;
+}
+
+void jabber_stream_restart_inactivity_timer(JabberStream *js)
+{
+	if (js->inactivity_timer != 0) {
+		purple_timeout_remove(js->inactivity_timer);
+		js->inactivity_timer = 0;
+	}
+
+	g_return_if_fail(js->max_inactivity > 0);
+
+	js->inactivity_timer =
+		purple_timeout_add_seconds(js->max_inactivity,
+		                           inactivity_cb, js);
+}
+
 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b)
 {
 	return "jabber";
--- a/libpurple/protocols/jabber/jabber.h	Wed Apr 21 17:43:36 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Wed Apr 21 19:07:19 2010 +0000
@@ -255,6 +255,8 @@
 
 	/* A purple timeout tag for the keepalive */
 	guint keepalive_timeout;
+	guint max_inactivity;
+	guint inactivity_timer;
 
 	PurpleSrvResponse *srv_rec;
 	guint srv_rec_idx;
@@ -349,6 +351,13 @@
  */
 gboolean jabber_stream_is_ssl(JabberStream *js);
 
+/**
+ * Restart the "we haven't sent anything in a while and should send
+ * something or the server will kick us off" timer (obviously
+ * called when sending something.  It's exposed for BOSH.)
+ */
+void jabber_stream_restart_inactivity_timer(JabberStream *js);
+
 /** PRPL functions */
 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b);
 const char* jabber_list_emblem(PurpleBuddy *b);