cpw.darkrain42.xmpp.bosh: 42b48d18: Jabber BOSH: more fixes.
paul at darkrain42.org
paul at darkrain42.org
Sat Jan 17 23:55:50 EST 2009
-----------------------------------------------------------------
Revision: 42b48d18527b01b2ce8d375b6a104fb81abef2aa
Ancestor: 0098a78f82c93a34ff44fee055c56c1eaf466da9
Author: paul at darkrain42.org
Date: 2008-12-04T23:59:44
Branch: im.pidgin.cpw.darkrain42.xmpp.bosh
URL: http://d.pidgin.im/viewmtn/revision/info/42b48d18527b01b2ce8d375b6a104fb81abef2aa
Modified files:
libpurple/protocols/jabber/bosh.c
libpurple/protocols/jabber/bosh.h
libpurple/protocols/jabber/jabber.c
libpurple/protocols/jabber/libxmpp.c
ChangeLog:
Jabber BOSH: more fixes.
Clean up some more of the structures and leaks
Add jabber_bosh_(un)?init functions
Properly send raw data (and add a _close function that terminates the stream)
Normalize HTTP headers
Throw a few more connection errors
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/bosh.c 44aa033d1b66ab3d8725cd7d3833747a76727ca6
+++ libpurple/protocols/jabber/bosh.c 3f62e7ff3c81d4a1b7c7bae85113ce0b17788f76
@@ -19,6 +19,7 @@
*
*/
#include "internal.h"
+#include "core.h"
#include "cipher.h"
#include "debug.h"
#include "prpl.h"
@@ -37,20 +38,19 @@ typedef void (*PurpleBOSHConnectionRecei
typedef void (*PurpleBOSHConnectionConnectFunction)(PurpleBOSHConnection *conn);
typedef void (*PurpleBOSHConnectionReceiveFunction)(PurpleBOSHConnection *conn, xmlnode *node);
+static char *bosh_useragent = NULL;
+
struct _PurpleBOSHConnection {
/* decoded URL */
char *host;
int port;
char *path;
- char *user;
- char *passwd;
int rid;
char *sid;
int wait;
JabberStream *js;
- PurpleAccount *account;
gboolean pipelining;
PurpleHTTPConnection *conn_a;
PurpleHTTPConnection *conn_b;
@@ -64,16 +64,14 @@ struct _PurpleHTTPConnection {
int fd;
char *host;
int port;
- int handle;
int ie_handle;
- PurpleConnection *conn;
GQueue *requests;
PurpleHTTPResponse *current_response;
char *current_data;
int current_len;
- int pih;
+ int pih; /* what? */
PurpleHTTPConnectionConnectFunction connect_cb;
PurpleHTTPConnectionConnectFunction disconnect_cb;
void *userdata;
@@ -108,17 +106,38 @@ static void jabber_bosh_http_connection_
static PurpleHTTPConnection* jabber_bosh_http_connection_init(const char *host, int port);
static void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn);
static void jabber_bosh_http_connection_send_request(PurpleHTTPConnection *conn, PurpleHTTPRequest *req);
-static void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn);
+static void jabber_bosh_http_connection_destroy(PurpleHTTPConnection *conn);
-static void jabber_bosh_http_request_init(PurpleHTTPRequest *req, const char *method, const char *path, PurpleHTTPRequestCallback cb, void *userdata);
+static PurpleHTTPRequest* jabber_bosh_http_request_init(const char *method, const char *path, PurpleHTTPRequestCallback cb, void *userdata);
static void jabber_bosh_http_request_add_to_header(PurpleHTTPRequest *req, const char *field, const char *value);
static void jabber_bosh_http_request_set_data(PurpleHTTPRequest *req, char *data, int len);
-static void jabber_bosh_http_request_clean(PurpleHTTPRequest *req);
+static void jabber_bosh_http_request_destroy(PurpleHTTPRequest *req);
-static void jabber_bosh_http_response_init(PurpleHTTPResponse *res);
-static void jabber_bosh_http_response_clean(PurpleHTTPResponse *res);
+static PurpleHTTPResponse* jabber_bosh_http_response_init(void);
+static void jabber_bosh_http_response_destroy(PurpleHTTPResponse *res);
-PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url) {
+void jabber_bosh_init(void)
+{
+ GHashTable *ui_info = purple_core_get_ui_info();
+ const char *ui_version = NULL;
+
+ if (ui_info)
+ ui_version = g_hash_table_lookup(ui_info, "version");
+
+ if (ui_version)
+ bosh_useragent = g_strdup_printf("%s (libpurple " VERSION ")", ui_version);
+ else
+ bosh_useragent = g_strdup("libpurple " VERSION);
+}
+
+void jabber_bosh_uninit(void)
+{
+ g_free(bosh_useragent);
+ bosh_useragent = NULL;
+}
+
+PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url)
+{
PurpleBOSHConnection *conn;
char *host, *path, *user, *passwd;
int port;
@@ -132,16 +151,18 @@ PurpleBOSHConnection* jabber_bosh_connec
conn->host = host;
conn->port = port;
conn->path = path;
- conn->user = user;
- conn->passwd = passwd;
conn->pipelining = TRUE;
- if (conn->user || conn->passwd) {
- purple_debug_info("jabber", "Ignoring unsupported BOSH HTTP "
- "Authentication username and password.\n");
+ if ((user && user[0] != '\0') || (passwd && passwd[0] != '\0')) {
+ purple_debug_info("jabber", "Ignoring unexpected username and password "
+ "in BOSH URL.\n");
}
+ g_free(user);
+ g_free(passwd);
+
conn->js = js;
+ /* FIXME: This doesn't seem very random */
conn->rid = rand() % 100000 + 1728679472;
conn->ready = FALSE;
conn->conn_a = jabber_bosh_http_connection_init(conn->host, conn->port);
@@ -150,6 +171,35 @@ PurpleBOSHConnection* jabber_bosh_connec
return conn;
}
+void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn)
+{
+ g_free(conn->host);
+ g_free(conn->path);
+
+ if (conn->conn_a)
+ jabber_bosh_http_connection_destroy(conn->conn_a);
+ if (conn->conn_b)
+ jabber_bosh_http_connection_destroy(conn->conn_b);
+
+ g_free(conn);
+}
+
+void jabber_bosh_connection_close(PurpleBOSHConnection *conn)
+{
+ xmlnode *packet = xmlnode_new("body");
+ char *tmp;
+
+ xmlnode_set_attrib(packet, "type", "terminate");
+ xmlnode_set_attrib(packet, "xmlns", "http://jabber.org/protocol/httpbind");
+ xmlnode_set_attrib(packet, "sid", conn->sid);
+ tmp = g_strdup_printf("%d", ++conn->rid);
+ xmlnode_set_attrib(packet, "rid", tmp);
+ g_free(tmp);
+
+ jabber_bosh_connection_send_native(conn, packet);
+ xmlnode_free(packet);
+}
+
static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) {
xmlnode *restart = xmlnode_new("body");
char *tmp = NULL;
@@ -164,6 +214,7 @@ static void jabber_bosh_connection_strea
xmlnode_set_attrib(restart, "xmlns:xmpp", "urn:xmpp:xbosh");
jabber_bosh_connection_send_native(conn, restart);
+ xmlnode_free(restart);
}
static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) {
@@ -242,20 +293,22 @@ static void jabber_bosh_connection_boot_
int major = atoi(version);
int minor = atoi(dot + 1);
+ purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
+
if (major > 1 || (major == 1 && minor >= 6)) {
xmlnode *packet = xmlnode_get_child(node, "features");
conn->js->use_bosh = TRUE;
conn->receive_cb = jabber_bosh_connection_auth_response;
jabber_stream_features_parse(conn->js, packet);
} else {
- purple_debug_info("jabber", "Unsupported version of BOSH protocol. The connection manager must at least support version 1.6!\n");
- /* XXX This *must* handle this by killing the connection and
- * reporting an error. */
+ purple_connection_error_reason(conn->js->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unsupported version of BOSH protocol"));
}
g_free(version);
} else {
- purple_debug_info("jabber", "Missing version in session creation response!\n");
+ purple_debug_info("jabber", "Missing version in BOSH initiation\n");
}
}
@@ -279,6 +332,7 @@ static void jabber_bosh_connection_boot(
conn->receive_cb = jabber_bosh_connection_boot_response;
jabber_bosh_connection_send_native(conn, init);
+ xmlnode_free(init);
}
static void jabber_bosh_connection_http_received_cb(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata) {
@@ -312,16 +366,35 @@ void jabber_bosh_connection_send(PurpleB
if (conn->ready == TRUE) xmlnode_set_attrib(node, "xmlns", "jabber:client");
}
jabber_bosh_connection_send_native(conn, packet);
+ xmlnode_free(packet);
}
+void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn,
+ const char *data, int len)
+{
+ xmlnode *node = xmlnode_from_str(data, len);
+ if (node) {
+ jabber_bosh_connection_send(conn, node);
+ xmlnode_free(node);
+ } else {
+ /*
+ * This best emulates what a normal XMPP server would do
+ * if you send bad XML.
+ */
+ purple_connection_error_reason(conn->js->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Cannot send malformed XML"));
+ }
+}
+
static void jabber_bosh_connection_send_native(PurpleBOSHConnection *conn, xmlnode *node) {
- PurpleHTTPRequest *request = g_new0(PurpleHTTPRequest, 1);
+ PurpleHTTPRequest *request;
char *txt = xmlnode_to_formatted_str(node, NULL);
printf("\njabber_bosh_connection_send\n%s\n", txt);
g_free(txt);
- jabber_bosh_http_request_init(request, "POST", g_strdup_printf("/%s", conn->path), jabber_bosh_connection_http_received_cb, conn);
+ request = jabber_bosh_http_request_init("POST", g_strdup_printf("/%s", conn->path), jabber_bosh_connection_http_received_cb, conn);
jabber_bosh_http_request_add_to_header(request, "Content-Encoding", "text/xml; charset=utf-8");
request->data = xmlnode_to_str(node, &(request->data_len));
jabber_bosh_http_request_add_to_header(request, "Content-Length", g_strdup_printf("%d", (int)strlen(request->data)));
@@ -377,7 +450,7 @@ static void jabber_bosh_http_connection_
value = beginning + 1;
} else if (*beginning == '\r') {
*beginning = 0;
- g_hash_table_replace(header, g_strdup(field), g_strdup(value));
+ g_hash_table_replace(header, g_ascii_strdown(field, -1), g_strdup(value));
value = field = 0;
++beginning;
}
@@ -415,11 +488,11 @@ static void jabber_bosh_http_connection_
/* check for header footer */
char *found = g_strstr_len(conn->current_data, conn->current_len, "\r\n\r\n");
if (found) {
- // new response
- response = conn->current_response = g_new0(PurpleHTTPResponse, 1);
- jabber_bosh_http_response_init(response);
+ /* New response */
+ response = conn->current_response = jabber_bosh_http_response_init();
jabber_bosh_http_connection_receive_parse_header(response, &(conn->current_data), &(conn->current_len));
- response->data_len = atoi(g_hash_table_lookup(response->header, "Content-Length"));
+ /* XXX: Crash if there is no Content-Length header */
+ response->data_len = atoi(g_hash_table_lookup(response->header, "content-length"));
} else {
printf("\nDid not receive HTTP header\n");
}
@@ -445,61 +518,97 @@ static void jabber_bosh_http_connection_
if (request->cb) request->cb(request, response, conn->userdata);
else purple_debug_info("jabber", "missing request callback!\n");
- jabber_bosh_http_request_clean(request);
- jabber_bosh_http_response_clean(response);
+ jabber_bosh_http_request_destroy(request);
+ jabber_bosh_http_response_destroy(response);
conn->current_response = NULL;
- g_free(request);
- g_free(response);
} else {
purple_debug_info("jabber", "received HTTP response but haven't requested anything yet.\n");
}
}
}
} else if (len == 0) {
- purple_input_remove(conn->ie_handle);
- if (conn->disconnect_cb) conn->disconnect_cb(conn);
+ if (conn->ie_handle) {
+ purple_input_remove(conn->ie_handle);
+ conn->ie_handle = 0;
+ }
+ if (conn->disconnect_cb)
+ conn->disconnect_cb(conn);
} else {
purple_debug_info("jabber", "jabber_bosh_http_connection_receive: problem receiving data (%d)\n", len);
}
}
-PurpleHTTPConnection *jabber_bosh_http_connection_init(const char *host, int port)
+static PurpleHTTPConnection* jabber_bosh_http_connection_init(const char *host, int port)
{
PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1);
conn->host = g_strdup(host);
conn->port = port;
+ conn->fd = -1;
conn->requests = g_queue_new();
return conn;
}
-void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn) {
- g_queue_free(conn->requests);
+static void jabber_bosh_http_connection_destroy(PurpleHTTPConnection *conn)
+{
+ g_free(conn->current_data);
+ g_free(conn->host);
+
+ if (conn->requests) {
+ g_queue_foreach(conn->requests, (GFunc)jabber_bosh_http_request_destroy, NULL);
+ g_queue_free(conn->requests);
+ }
+
+ if (conn->current_response)
+ jabber_bosh_http_response_destroy(conn->current_response);
+
+ if (conn->ie_handle)
+ purple_input_remove(conn->ie_handle);
+ if (conn->fd > 0)
+ close(conn->fd);
+
+ g_free(conn);
}
-static void jabber_bosh_http_connection_callback(gpointer data, gint source, const gchar *error) {
+static void jabber_bosh_http_connection_callback(gpointer data, gint source, const gchar *error)
+{
PurpleHTTPConnection *conn = data;
+ PurpleBOSHConnection *bosh_conn = conn->userdata;
+ PurpleConnection *gc = bosh_conn->js->gc;
+
if (source < 0) {
- purple_debug_info("jabber", "Couldn't connect becasue of: %s\n", error);
+ gchar *tmp;
+ tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+ error);
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ g_free(tmp);
return;
}
+
conn->fd = source;
- if (conn->connect_cb) conn->connect_cb(conn);
- else purple_debug_info("jabber", "No connect callback for HTTP connection.\n");
- conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ, jabber_bosh_http_connection_receive, conn);
+
+ if (conn->connect_cb)
+ conn->connect_cb(conn);
+
+ conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ,
+ jabber_bosh_http_connection_receive, conn);
}
-void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn) {
+static void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn)
+{
PurpleBOSHConnection *bosh_conn = conn->userdata;
PurpleConnection *gc = bosh_conn->js->gc;
PurpleAccount *account = purple_connection_get_account(gc);
- if((purple_proxy_connect(&(conn->handle), account, conn->host, conn->port, jabber_bosh_http_connection_callback, conn)) == NULL) {
- purple_debug_info("jabber", "Unable to connect to %s.\n", conn->host);
+ if ((purple_proxy_connect(conn, account, conn->host, conn->port, jabber_bosh_http_connection_callback, conn)) == NULL) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to create socket"));
}
}
-static void jabber_bosh_http_connection_send_request_add_field_to_string(gpointer key, gpointer value, gpointer user_data) {
+static void jabber_bosh_http_connection_send_request_add_field_to_string(gpointer key, gpointer value, gpointer user_data)
+{
char **ppacket = user_data;
char *tmp = *ppacket;
char *field = key;
@@ -512,7 +621,8 @@ void jabber_bosh_http_connection_send_re
char *packet;
char *tmp;
jabber_bosh_http_request_add_to_header(req, "Host", conn->host);
- jabber_bosh_http_request_add_to_header(req, "User-Agent", "libpurple");
+ jabber_bosh_http_request_add_to_header(req, "User-Agent", bosh_useragent);
+
packet = tmp = g_strdup_printf("%s %s HTTP/1.1\r\n", req->method, req->path);
g_hash_table_foreach(req->header, jabber_bosh_http_connection_send_request_add_field_to_string, &packet);
tmp = packet;
@@ -522,12 +632,16 @@ void jabber_bosh_http_connection_send_re
g_queue_push_tail(conn->requests, req);
}
-void jabber_bosh_http_request_init(PurpleHTTPRequest *req, const char *method, const char *path, PurpleHTTPRequestCallback cb, void *userdata) {
+static PurpleHTTPRequest* jabber_bosh_http_request_init(const char *method,
+ const char *path, PurpleHTTPRequestCallback cb, void *userdata)
+{
+ PurpleHTTPRequest *req = g_new(PurpleHTTPRequest, 1);
req->method = g_strdup(method);
req->path = g_strdup(path);
req->cb = cb;
req->userdata = userdata;
- req->header = g_hash_table_new(g_str_hash, g_str_equal);
+ req->header = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ return req;
}
static void jabber_bosh_http_request_add_to_header(PurpleHTTPRequest *req, const char *field, const char *value) {
@@ -541,20 +655,25 @@ void jabber_bosh_http_request_set_data(P
req->data_len = len;
}
-void jabber_bosh_http_request_clean(PurpleHTTPRequest *req) {
+static void jabber_bosh_http_request_destroy(PurpleHTTPRequest *req)
+{
g_hash_table_destroy(req->header);
g_free(req->method);
g_free(req->path);
g_free(req->data);
+ g_free(req);
}
-void jabber_bosh_http_response_init(PurpleHTTPResponse *res) {
- res->status = 0;
- res->header = g_hash_table_new(g_str_hash, g_str_equal);
+static PurpleHTTPResponse* jabber_bosh_http_response_init(void)
+{
+ PurpleHTTPResponse *res = g_new0(PurpleHTTPResponse, 1);
+ res->header = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ return res;
}
-
-void jabber_bosh_http_response_clean(PurpleHTTPResponse *res) {
+static void jabber_bosh_http_response_destroy(PurpleHTTPResponse *res)
+{
g_hash_table_destroy(res->header);
g_free(res->data);
+ g_free(res);
}
============================================================
--- libpurple/protocols/jabber/bosh.h c87e1756ecee914264277778be583d8f55962d24
+++ libpurple/protocols/jabber/bosh.h 8762bed96aa1807750ed885fa3a2d369c5ae0bd3
@@ -26,7 +26,14 @@ typedef struct _PurpleBOSHConnection Pur
#include "jabber.h"
+void jabber_bosh_init(void);
+void jabber_bosh_uninit(void);
+
PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url);
+void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn);
+
void jabber_bosh_connection_connect(PurpleBOSHConnection *conn);
+void jabber_bosh_connection_close(PurpleBOSHConnection *conn);
void jabber_bosh_connection_send(PurpleBOSHConnection *conn, xmlnode *node);
+void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn, const char *data, int len);
#endif /* _PURPLE_JABBER_BOSH_H_ */
============================================================
--- libpurple/protocols/jabber/jabber.c ac7d7877aba174d0a042300ce1742b5c8c6cf0b6
+++ libpurple/protocols/jabber/jabber.c 731fa7238927b1c6eb72593b0caf6e03543852f0
@@ -391,16 +391,10 @@ void jabber_send_raw(JabberStream *js, c
if (len == -1)
len = strlen(data);
- if (js->use_bosh) {
- xmlnode *xnode = xmlnode_from_str(data, len);
- if (xnode) jabber_bosh_connection_send(js->bosh, xnode);
- else {
- purple_connection_error_reason(js->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- _("Someone tried to send non-XML in a Jabber world."));
- }
- } else {
+ if (js->use_bosh)
+ jabber_bosh_connection_send_raw(js->bosh, data, len);
+ else
do_jabber_send_raw(js, data, len);
- }
}
int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
@@ -421,9 +415,13 @@ void jabber_send(JabberStream *js, xmlno
if(NULL == packet)
return;
- txt = xmlnode_to_str(packet, &len);
- jabber_send_raw(js, txt, len);
- g_free(txt);
+ if (js->use_bosh)
+ jabber_bosh_connection_send(js->bosh, packet);
+ else {
+ txt = xmlnode_to_str(packet, &len);
+ jabber_send_raw(js, txt, len);
+ g_free(txt);
+ }
}
static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused)
@@ -592,6 +590,7 @@ txt_resolved_cb(PurpleTxtResponse *resp,
if (!strcmp(token[0], "_xmpp-client-xbosh")) {
purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]);
js->bosh = jabber_bosh_connection_init(js, token[1]);
+ js->use_bosh = TRUE;
g_strfreev(token);
break;
}
@@ -1342,8 +1341,12 @@ void jabber_close(PurpleConnection *gc)
* if we were forcibly disconnected because it will crash
* on some SSL backends.
*/
- if (!gc->disconnect_timeout)
- jabber_send_raw(js, "</stream:stream>", -1);
+ if (!gc->disconnect_timeout) {
+ if (js->use_bosh)
+ jabber_bosh_connection_close(js->bosh);
+ else
+ jabber_send_raw(js, "</stream:stream>", -1);
+ }
if (js->srv_query_data)
purple_srv_cancel(js->srv_query_data);
@@ -1359,6 +1362,9 @@ void jabber_close(PurpleConnection *gc)
close(js->fd);
}
+ if (js->bosh)
+ jabber_bosh_connection_destroy(js->bosh);
+
jabber_buddy_remove_all_pending_buddy_info_requests(js);
jabber_parser_free(js);
============================================================
--- libpurple/protocols/jabber/libxmpp.c d3b44e74835751fa6faabf516be00ab88fe46084
+++ libpurple/protocols/jabber/libxmpp.c 187a32c8669c1fa0eb7a179af8e2fb4853884591
@@ -150,6 +150,7 @@ static gboolean unload_plugin(PurplePlug
purple_signal_unregister(plugin, "jabber-sending-text");
/* reverse order of init_plugin */
+ jabber_bosh_uninit();
jabber_data_uninit();
/* PEP things should be uninit via jabber_pep_uninit, not here */
jabber_pep_uninit();
@@ -293,6 +294,7 @@ init_plugin(PurplePlugin *plugin)
/* PEP things should be init via jabber_pep_init, not here */
jabber_pep_init();
jabber_data_init();
+ jabber_bosh_init();
#warning implement adding and retrieving own features via IPC API
More information about the Commits
mailing list