changeset 20527:0034d7e89032

mostly done with soap level parsing, compiles
author Ka-Hing Cheung <khc@hxbc.us>
date Fri, 21 Sep 2007 06:50:40 +0000
parents 915e11fbaeb0
children 06527cc0f79b
files libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h
diffstat 2 files changed, 249 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/soap2.c	Wed Sep 19 06:20:08 2007 +0000
+++ b/libpurple/protocols/msn/soap2.c	Fri Sep 21 06:50:40 2007 +0000
@@ -25,6 +25,7 @@
 
 #include "soap2.h"
 
+#include "debug.h"
 #include "xmlnode.h"
 
 #include <glib.h>
@@ -65,13 +66,82 @@
 	msn_soap_connection2_cleanup(conn);
 }
 
+static gboolean
+msn_soap_handle_redirect(MsnSoapConnection2 *conn, const char *url)
+{
+	char *c;
+
+	/* Skip the http:// */
+	if ((c = strchr(url, '/')) != NULL)
+		url += 2;
+
+	if ((c = strchr(url, '/')) != NULL) {
+		g_free(conn->request->host);
+		g_free(conn->request->path);
+
+		conn->request->host = g_strndup(url, c - url);
+		conn->request->path = g_strdup(c);
+
+		purple_input_remove(conn->io_handle);
+		conn->io_handle = 0;
+
+		msn_soap_connection2_post(conn, conn->request,
+			conn->request->cb, conn->request->data);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+msn_soap_handle_body(MsnSoapConnection2 *conn, MsnSoapResponse *response)
+{
+	xmlnode *node = response->message->xml;
+
+	if (strcmp(node->name, "Envelop") == 0 &&
+		node->child && strcmp(node->child->name, "Header") == 0 &&
+		node->child->next) {
+		xmlnode *body = node->child->next;
+
+		if (strcmp(body->name, "Fault")) {
+			xmlnode *fault = xmlnode_get_child(body, "faultcode");
+
+			if (fault != NULL) {
+				if (strcmp(fault->data, "psf:Redirect") == 0) {
+					xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
+
+					if (url && !msn_soap_handle_redirect(conn, url->data)) {
+						return TRUE;
+					}
+				} else if (strcmp(fault->data, "wsse:FailedAuthentication")) {
+					xmlnode *reason = xmlnode_get_child(fault, "faultstring");
+
+					msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+						reason ? reason->data : NULL);
+
+					return TRUE;
+				}
+			}
+
+			conn->request->cb(conn, conn->request, conn->response,
+				conn->request->data);
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 static void
 msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
 {
     MsnSoapConnection2 *conn = data;
+	MsnSoapMessage *message;
 	int count;
 	char buf[8192];
-	int linebreak;
+	char *linebreak;
 
 	g_return_if_fail(cond == PURPLE_INPUT_READ);
 
@@ -83,21 +153,88 @@
 		return;
 	}
 
-	if (conn->buf == NULL) {
-		conn->buf = g_memdup(buf, count);
-		conn->buf_len = len;
+	if (conn->response == NULL) {
+		conn->response = msn_soap_response_new();
+		conn->response->message = msn_soap_message_new();
+	}
+
+	message = conn->response->message;
+
+	if (message->buf == NULL) {
+		message->buf = g_memdup(buf, count);
+		message->buf_len = count;
 	} else {
-		conn->buf = g_realloc(conn->buf, conn->buf_len + count);
-		memcpy(conn->buf + conn->buf_len, buf, count);
-		conn->buf_len += count;
+		message->buf = g_realloc(message->buf, message->buf_len + count);
+		memcpy(message->buf + message->buf_len,	buf, count);
+		message->buf_len += count;
+	}
+
+	if (conn->response->seen_newline) {
+		if (message->buf_len - message->buf_count >=
+			conn->response->body_len) {
+			xmlnode *node = xmlnode_from_str(
+				message->buf + message->buf_count, conn->response->body_len);
+
+			if (node == NULL) {
+				purple_debug_info("soap", "Malformed SOAP response: %s\n",
+					message->buf + message->buf_count);
+			} else {
+				conn->response->message->xml = node;
+				msn_soap_handle_body(conn, conn->response);
+			}
+		}
+
+		return;
 	}
 
-	if (conn->response == NULL) {
-		conn->response = msn_soap_message_new(conn->current->action, NULL);
-	}
+	while ((linebreak = strstr(message->buf + message->buf_count, "\r\n"))
+		!= NULL) {
+		message->buf_count = linebreak - message->buf + 2;
+
+		if (conn->response->code == -1) {
+			if (sscanf(message->buf + message->buf_count, "HTTP/1.1 %d",
+					&conn->response->code) != 1) {
+				/* something horribly wrong */
+				msn_soap_connection2_destroy_foreach_cb(conn->request, conn);
+				conn->request = NULL;
+			} else if (conn->response->code == 503) {
+				msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
+			}
+		} else if (message->buf + message->buf_count == linebreak) {
+			/* blank line */
+			conn->response->seen_newline = TRUE;
+		} else {
+			char *sep = strstr(message->buf + message->buf_count, ": ");
+			char *key = message->buf + message->buf_count;
+			char *value = sep + 2;
 
-	while ((linebreak = strstr(conn->buf + conn->buf_count, "\r\n")) != NULL) {
-		
+			*sep = '\0';
+			*linebreak = '\0';
+			msn_soap_message_add_header(message, key, value);
+
+			if ((conn->response->code == 301 || conn->response->code == 300)
+				&& strcmp(key, "Location") == 0) {
+
+				if (msn_soap_handle_redirect(conn, value)) {
+
+				} else if (conn->request->cb) {
+					conn->request->cb(conn, conn->request, NULL,
+						conn->request->data);
+				}
+			} else if (conn->response->code == 401 &&
+				strcmp(key, "WWW-Authenticate") == 0) {
+				char *error = strstr(value, "cbtxt=");
+
+				if (error) {
+					error += strlen("cbtxt=");
+				}
+
+				msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+					error ? purple_url_decode(error) : NULL);
+			} else if (strcmp(key, "Content-Length") == 0) {
+				conn->response->body_len = atoi(value);
+			}
+		}
 	}
 }
 
@@ -105,12 +242,13 @@
 msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
 {
 	MsnSoapConnection2 *conn = data;
+	MsnSoapMessage *message = conn->request->message;
 	int written;
 
 	g_return_if_fail(cond == PURPLE_INPUT_WRITE);
 
-	written = purple_ssl_write(conn->ssl, conn->buf + conn->buf_count,
-		conn->buf_len - conn->buf_count);
+	written = purple_ssl_write(conn->ssl, message->buf + message->buf_count,
+		message->buf_len - message->buf_count);
 
 	if (written < 0 && errno == EAGAIN)
 		return;
@@ -119,16 +257,12 @@
 		return;
 	}
 
-	conn->buf_count += written;
+	message->buf_count += written;
 
-	if (conn->buf_count < conn->buf_len)
+	if (message->buf_count < message->buf_len)
 		return;
 
 	/* we are done! */
-	g_free(conn->buf);
-	conn->buf_len = 0;
-	conn->buf_count = 0;
-
 	purple_input_remove(conn->io_handle);
 	conn->io_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
 		msn_soap_read_cb, conn);
@@ -138,12 +272,14 @@
 msn_soap_connection2_run(gpointer data)
 {
 	MsnSoapConnection2 *conn = data;
-	MsnSoapMessage *req = g_queue_peek_head(conn->queue);
+	MsnSoapRequest *req = g_queue_peek_head(conn->queue);
 
 	if (req) {
 		if (conn->ssl) {
 			if (strcmp(conn->ssl->host, req->host) != 0 ||
 				strcmp(conn->path, req->path) != 0) {
+				purple_input_remove(conn->io_handle);
+				conn->io_handle = 0;
 				purple_ssl_close(conn->ssl);
 				conn->ssl = NULL;
 				g_free(conn->path);
@@ -157,7 +293,8 @@
 			conn->path = g_strdup(req->path);
 		} else {
 			int len = -1;
-			char *body = xmlnode_to_str(req->message, &len);
+			MsnSoapMessage *message = req->message;
+			char *body = xmlnode_to_str(message->xml, &len);
 			GString *str = g_string_new("");
 			GSList *iter;
 
@@ -178,7 +315,7 @@
 				conn->session->passport_info.mspauth,
 				req->host, len);
 
-			for (iter = req->headers; iter; iter = iter->next) {
+			for (iter = req->message->headers; iter; iter = iter->next) {
 				g_string_append(str, (char *)iter->data);
 				g_string_append(str, "\r\n");
 			}
@@ -186,10 +323,10 @@
 			g_string_append(str, "\r\n");
 			g_string_append(str, body);
 
-			conn->buf_len = str->len;
-			conn->buf = g_string_free(str, FALSE);
-			conn->buf_count = 0;
-			conn->current = req;
+			message->buf_len = str->len;
+			message->buf = g_string_free(str, FALSE);
+			message->buf_count = 0;
+			conn->request = req;
 
 			conn->io_handle = purple_input_add(conn->ssl->fd,
 				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
@@ -202,13 +339,11 @@
 }
 
 void
-msn_soap_connection2_post(MsnSoapConnection2 *conn, MsnSoapMessage *req,
-		const char *host, const char *path, MsnSoapCallback cb, gpointer data)
+msn_soap_connection2_post(MsnSoapConnection2 *conn, MsnSoapRequest *req,
+	MsnSoapCallback cb, gpointer data)
 {
 	req->cb = cb;
 	req->data = data;
-	req->host = g_strdup(host);
-	req->path = g_strdup(path);
 
 	g_queue_push_tail(conn->queue, req);
 
@@ -219,22 +354,22 @@
 static void
 msn_soap_connection2_destroy_foreach_cb(gpointer item, gpointer data)
 {
-	MsnSoapMessage *req = item;
+	MsnSoapRequest *req = item;
 	MsnSoapConnection2 *conn = data;
 
 	if (req->cb)
 		req->cb(conn, req, NULL, req->data);
 
-	msn_soap_message_destroy(req);
+	msn_soap_request2_destroy(req);
 }
 
 static void
 msn_soap_connection2_cleanup(MsnSoapConnection2 *conn)
 {
 	g_queue_foreach(conn->queue, msn_soap_connection2_destroy_foreach_cb, conn);
-	if (conn->current) {
-		msn_soap_connection2_destroy_foreach_cb(conn->current, conn);
-		conn->current = NULL;
+	if (conn->request) {
+		msn_soap_connection2_destroy_foreach_cb(conn->request, conn);
+		conn->request = NULL;
 	}
 
 	purple_input_remove(conn->io_handle);
@@ -242,10 +377,6 @@
 	g_source_remove(conn->idle_handle);
 	conn->idle_handle = 0;
 
-	g_free(conn->buf);
-	conn->buf_len = 0;
-	conn->buf_count = 0;
-
 	if (conn->ssl) {
 		purple_ssl_close(conn->ssl);
 		conn->ssl = NULL;
@@ -265,24 +396,19 @@
 }
 
 MsnSoapMessage *
-msn_soap_message_new(const char *action, xmlnode *message)
+msn_soap_message_new()
 {
 	MsnSoapMessage *req = g_new0(MsnSoapMessage, 1);
 
-	req->action = g_strdup(action);
-	req->message = message;
-
 	return req;
 }
 
 void
-msn_soap_message_destroy(MsnSoapMessage *req)
+msn_soap_message_destroy(MsnSoapMessage *message)
 {
-	g_free(req->action);
-	g_slist_foreach(req->headers, (GFunc)g_free, NULL);
-	g_free(req->host);
-	g_free(req->path);
-	g_free(req);
+	g_slist_foreach(message->headers, (GFunc)g_free, NULL);
+	g_free(message->buf);
+	g_free(message);
 }
 
 void
@@ -293,3 +419,42 @@
 
 	req->headers = g_slist_prepend(req->headers, header);
 }
+
+MsnSoapRequest *
+msn_soap_request2_new(const char *host, const char *path, const char *action)
+{
+	MsnSoapRequest *req = g_new0(MsnSoapRequest, 1);
+
+	req->host = g_strdup(host);
+	req->path = g_strdup(path);
+	req->action = g_strdup(action);
+
+	return req;
+}
+
+void
+msn_soap_request2_destroy(MsnSoapRequest *req)
+{
+	g_free(req->action);
+	g_free(req->host);
+	g_free(req->path);
+	msn_soap_message_destroy(req->message);
+	g_free(req);
+}
+
+MsnSoapResponse *
+msn_soap_response_new(void)
+{
+	MsnSoapResponse *resp = g_new0(MsnSoapResponse, 1);
+
+	return resp;
+}
+
+void
+msn_soap_response_destroy(MsnSoapResponse *resp)
+{
+	msn_soap_message_destroy(resp->message);
+	resp->code = -1;
+	resp->body_len = -1;
+	g_free(resp);
+}
--- a/libpurple/protocols/msn/soap2.h	Wed Sep 19 06:20:08 2007 +0000
+++ b/libpurple/protocols/msn/soap2.h	Fri Sep 21 06:50:40 2007 +0000
@@ -34,14 +34,24 @@
 
 typedef struct _MsnSoapMessage MsnSoapMessage;
 typedef struct _MsnSoapConnection2 MsnSoapConnection2;
+typedef struct _MsnSoapRequest MsnSoapRequest;
+typedef struct _MsnSoapResponse MsnSoapResponse;
 
 typedef void (*MsnSoapCallback)(MsnSoapConnection2 *conn,
-		MsnSoapMessage *req, MsnSoapMessage *resp, gpointer cb_data);
+		MsnSoapRequest *req, MsnSoapResponse *resp, gpointer cb_data);
 
 struct _MsnSoapMessage {
+	xmlnode *xml;
+	GSList *headers;
+
+	char *buf;
+	gsize buf_len;
+	gsize buf_count;
+};
+
+struct _MsnSoapRequest {
+	MsnSoapMessage *message;
 	char *action;
-	xmlnode *message;
-	GSList *headers;
 
 	MsnSoapCallback cb;
 	gpointer data;
@@ -50,6 +60,13 @@
 	char *path;
 };
 
+struct _MsnSoapResponse {
+	MsnSoapMessage *message;
+	int code;
+	gboolean seen_newline;
+	int body_len;
+};
+
 struct _MsnSoapConnection2 {
 	MsnSession *session;
 
@@ -62,25 +79,31 @@
 	guint io_handle;
 	GQueue *queue;
 
-	MsnSoapMessage *current;
-	MsnSoapMessage *response;
-	char *buf;
-	gsize buf_len;
-	gsize buf_count;
+	MsnSoapRequest *request;
+	MsnSoapResponse *response;
 };
 
 MsnSoapConnection2 *msn_soap_connection2_new(MsnSession *session);
 
-void msn_soap_connection2_post(MsnSoapConnection2 *conn, MsnSoapMessage *req,
-		const char *host, const char *path, MsnSoapCallback cb, gpointer data);
+void msn_soap_connection2_post(MsnSoapConnection2 *conn, MsnSoapRequest *req,
+		MsnSoapCallback cb, gpointer data);
 
 void msn_soap_connection2_destroy(MsnSoapConnection2 *conn);
 
-MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *message);
+MsnSoapMessage *msn_soap_message_new(void);
 
 void msn_soap_message_destroy(MsnSoapMessage *req);
 
 void msn_soap_message_add_header(MsnSoapMessage *req,
 	const char *name, const char *value);
 
+MsnSoapRequest *msn_soap_request2_new(const char *host, const char *path,
+		const char *action);
+
+void msn_soap_request2_destroy(MsnSoapRequest *req);
+
+MsnSoapResponse *msn_soap_response_new(void);
+
+void msn_soap_response_destroy(MsnSoapResponse *resp);
+
 #endif