Mercurial > pidgin
diff libpurple/protocols/msn/soap2.c @ 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 |
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); +}