diff options
author | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2013-07-23 20:48:25 +0200 |
---|---|---|
committer | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2013-07-23 20:48:25 +0200 |
commit | c1b60293efb827469aa250bb2a6cade988c0e6b8 (patch) | |
tree | 1b487839cb8565f2993bc4e22d4cf6dfc2cdcd7f | |
parent | 49087682770a35ae4cbff630359726ec5626e5e6 (diff) | |
download | pidgin-c1b60293efb827469aa250bb2a6cade988c0e6b8.tar.gz |
HTTP: get rid of the second msn's own HTTP implementation (for soap)
-rw-r--r-- | libpurple/http.c | 23 | ||||
-rw-r--r-- | libpurple/protocols/msn/contact.c | 2 | ||||
-rw-r--r-- | libpurple/protocols/msn/nexus.c | 16 | ||||
-rw-r--r-- | libpurple/protocols/msn/oim.c | 5 | ||||
-rw-r--r-- | libpurple/protocols/msn/session.c | 8 | ||||
-rw-r--r-- | libpurple/protocols/msn/session.h | 4 | ||||
-rw-r--r-- | libpurple/protocols/msn/soap.c | 750 | ||||
-rw-r--r-- | libpurple/protocols/msn/soap.h | 14 |
8 files changed, 222 insertions, 600 deletions
diff --git a/libpurple/http.c b/libpurple/http.c index 2a67da7dbb..2f296c289c 100644 --- a/libpurple/http.c +++ b/libpurple/http.c @@ -703,6 +703,9 @@ static void _purple_http_error(PurpleHttpConnection *hc, const char *format, hc->response->error = g_strdup_vprintf(format, args); va_end(args); + if (purple_debug_is_verbose()) + purple_debug_warning("http", "error: %s\n", hc->response->error); + purple_http_conn_cancel(hc); } @@ -1084,7 +1087,7 @@ static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd) hc->length_expected = hc->length_got; else if (hc->length_got == 0 && hc->socket->use_count > 1) { purple_debug_info("http", "Keep-alive connection " - "expired, retrying...\n"); + "expired (when reading), retrying...\n"); purple_http_conn_retry(hc); return FALSE; } else { @@ -1289,6 +1292,15 @@ static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond) return; if (written < 0) { + if (hc->request_header_written == 0 && + hc->socket->use_count > 1) + { + purple_debug_info("http", "Keep-alive connection " + "expired (when writing), retrying...\n"); + purple_http_conn_retry(hc); + return; + } + _purple_http_error(hc, _("Error writing to %s: %s"), hc->url->host, g_strerror(errno)); return; @@ -1635,7 +1647,14 @@ purple_http_conn_retry(PurpleHttpConnection *http_conn) void purple_http_conn_cancel_all(PurpleConnection *gc) { - GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); + GList *gc_list; + + if (purple_debug_is_verbose()) { + purple_debug_misc("http", "Cancelling all running HTTP " + "connections\n"); + } + + gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); g_hash_table_insert(purple_http_cancelling_gc, gc, GINT_TO_POINTER(TRUE)); diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c index 437305a597..e5f18c230f 100644 --- a/libpurple/protocols/msn/contact.c +++ b/libpurple/protocols/msn/contact.c @@ -288,7 +288,7 @@ msn_contact_request(MsnCallbackState *state) xmlnode_free(state->token->child); xmlnode_insert_data(state->token, msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); - msn_soap_message_send(state->session, + msn_soap_service_send_message(state->session->soap, msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), MSN_CONTACT_SERVER, state->post_url, FALSE, msn_contact_request_cb, state); diff --git a/libpurple/protocols/msn/nexus.c b/libpurple/protocols/msn/nexus.c index 95ddbbb3a7..5a8aafc43d 100644 --- a/libpurple/protocols/msn/nexus.c +++ b/libpurple/protocols/msn/nexus.c @@ -376,8 +376,6 @@ msn_nexus_connect(MsnNexus *nexus) char *request; int i; - MsnSoapMessage *soap; - purple_debug_info("msn", "Starting Windows Live ID authentication\n"); msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); @@ -409,10 +407,11 @@ msn_nexus_connect(MsnNexus *nexus) g_free(password_xml); g_string_free(domains, TRUE); - soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); + msn_soap_service_send_message(session->soap, + msn_soap_message_new(NULL, xmlnode_from_str(request, -1)), + MSN_SSO_SERVER, SSO_POST_URL, TRUE, + nexus_got_response_cb, nexus); g_free(request); - msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE, - nexus_got_response_cb, nexus); } static void @@ -532,7 +531,6 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data) guchar signature[20]; char *request; - MsnSoapMessage *soap; update = g_new0(MsnNexusUpdateCallback, 1); update->cb = cb; @@ -623,10 +621,10 @@ msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data) g_free(signedinfo); g_free(domain); - soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); g_free(request); - msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE, - nexus_got_update_cb, ud); + msn_soap_service_send_message(session->soap, + msn_soap_message_new(NULL, xmlnode_from_str(request, -1)), + MSN_SSO_SERVER, SSO_POST_URL, TRUE, nexus_got_update_cb, ud); } GHashTable * diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c index a08ee258f7..9b4101975e 100644 --- a/libpurple/protocols/msn/oim.c +++ b/libpurple/protocols/msn/oim.c @@ -244,10 +244,9 @@ msn_oim_request_helper(MsnOimRequestData *data) xmlnode_insert_data(xml_p, msn_p, -1); } - msn_soap_message_send(session, + msn_soap_service_send_message(session->soap, msn_soap_message_new(data->action, xmlnode_copy(data->body)), - data->host, data->url, FALSE, - msn_oim_request_cb, data); + data->host, data->url, FALSE, msn_oim_request_cb, data); return FALSE; } diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c index 05dae91d7b..5fd88eba6b 100644 --- a/libpurple/protocols/msn/session.c +++ b/libpurple/protocols/msn/session.c @@ -55,6 +55,8 @@ msn_session_new(PurpleAccount *account) session->guid = rand_guid(); + session->soap = msn_soap_service_new(session); + return session; } @@ -73,11 +75,7 @@ msn_session_destroy(MsnSession *session) if (session->connected) msn_session_disconnect(session); - if (session->soap_cleanup_handle) - purple_timeout_remove(session->soap_cleanup_handle); - - if (session->soap_table != NULL) - g_hash_table_destroy(session->soap_table); + msn_soap_service_destroy(session->soap); while (session->slplinks != NULL) msn_slplink_unref(session->slplinks->data); diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h index 73b76fcc46..24a195a49a 100644 --- a/libpurple/protocols/msn/session.h +++ b/libpurple/protocols/msn/session.h @@ -66,6 +66,7 @@ typedef enum #include "nexus.h" #include "notification.h" #include "oim.h" +#include "soap.h" #include "switchboard.h" #include "user.h" #include "userlist.h" @@ -116,8 +117,7 @@ struct _MsnSession gboolean email_enabled; } passport_info; - GHashTable *soap_table; - guint soap_cleanup_handle; + MsnSoapService *soap; char *guid; GSList *http_reqs; /**< PurpleHttpConnection to be cancelled on exit */ diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c index 4e6814cdf5..fa2d0ed29e 100644 --- a/libpurple/protocols/msn/soap.c +++ b/libpurple/protocols/msn/soap.c @@ -25,668 +25,268 @@ #include "soap.h" -#include "internal.h" - #include "debug.h" #include "http.h" -#define SOAP_TIMEOUT (5 * 60) +typedef struct _MsnSoapRequest MsnSoapRequest; struct _MsnSoapMessage { gchar *action; xmlnode *xml; - GSList *headers; }; -xmlnode * -msn_soap_message_get_xml(MsnSoapMessage *message) -{ - g_return_val_if_fail(message != NULL, NULL); - - return message->xml; -} - -typedef struct _MsnSoapRequest { - char *path; +struct _MsnSoapRequest { MsnSoapMessage *message; - gboolean secure; + MsnSoapService *soaps; MsnSoapCallback cb; gpointer cb_data; -} MsnSoapRequest; + gboolean secure; +}; -typedef struct _MsnSoapConnection { +struct _MsnSoapService { MsnSession *session; - char *host; - - time_t last_used; - PurpleSslConnection *ssl; - gboolean connected; - - guint event_handle; - guint run_timer; - GString *buf; - gsize handled_len; - gsize body_len; - int response_code; - gboolean headers_done; - gboolean close_when_done; - - MsnSoapMessage *message; - - GQueue *queue; - MsnSoapRequest *current_request; -} MsnSoapConnection; - -static gboolean msn_soap_connection_run(gpointer data); - -static MsnSoapConnection * -msn_soap_connection_new(MsnSession *session, const char *host) -{ - MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1); - conn->session = session; - conn->host = g_strdup(host); - conn->queue = g_queue_new(); - return conn; -} - -static void -msn_soap_message_destroy(MsnSoapMessage *message) -{ - g_slist_foreach(message->headers, (GFunc)g_free, NULL); - g_slist_free(message->headers); - g_free(message->action); - if (message->xml) - xmlnode_free(message->xml); - g_free(message); -} + PurpleHttpKeepalivePool *keepalive_pool; +}; static void -msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message) -{ - g_free(req->path); - if (!keep_message) - msn_soap_message_destroy(req->message); - g_free(req); -} +msn_soap_service_send_message_simple(MsnSoapService *soaps, + MsnSoapMessage *message, const gchar *url, gboolean secure, + MsnSoapCallback cb, gpointer cb_data); -static void -msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect) +MsnSoapMessage * +msn_soap_message_new(const gchar *action, xmlnode *xml) { - if (conn->event_handle) { - purple_input_remove(conn->event_handle); - conn->event_handle = 0; - } + MsnSoapMessage *msg; - if (conn->run_timer) { - purple_timeout_remove(conn->run_timer); - conn->run_timer = 0; - } + g_return_val_if_fail(xml != NULL, NULL); - if (conn->message) { - msn_soap_message_destroy(conn->message); - conn->message = NULL; - } - - if (conn->buf) { - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - } + msg = g_new0(MsnSoapMessage, 1); + msg->action = g_strdup(action); + msg->xml = xml; - if (conn->ssl && (disconnect || conn->close_when_done)) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - } - - if (conn->current_request) { - msn_soap_request_destroy(conn->current_request, FALSE); - conn->current_request = NULL; - } + return msg; } static void -msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data) -{ - MsnSoapRequest *req = item; - - req->cb(req->message, NULL, req->cb_data); - - msn_soap_request_destroy(req, FALSE); -} - -static void -msn_soap_connection_destroy(MsnSoapConnection *conn) -{ - if (conn->current_request) { - MsnSoapRequest *req = conn->current_request; - conn->current_request = NULL; - msn_soap_connection_destroy_foreach_cb(req, conn); - } - - msn_soap_connection_sanitize(conn, TRUE); - g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn); - g_queue_free(conn->queue); - - g_free(conn->host); - g_free(conn); -} - -static gboolean -msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data) +msn_soap_message_free(MsnSoapMessage *msg) { - MsnSoapConnection *conn = value; - time_t *t = data; - - if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) { - purple_debug_info("soap", "cleaning up soap conn %p\n", conn); - return TRUE; - } - - return FALSE; -} - -static gboolean -msn_soap_cleanup_for_session(gpointer data) -{ - MsnSession *sess = data; - time_t t = time(NULL); - - purple_debug_info("soap", "session cleanup timeout\n"); - - if (sess->soap_table) { - g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each, - &t); - - if (g_hash_table_size(sess->soap_table) != 0) - return TRUE; - } + if (msg == NULL) + return; - sess->soap_cleanup_handle = 0; - return FALSE; + g_free(msg->action); + if (msg->xml != NULL) + xmlnode_free(msg->xml); + g_free(msg); } -static MsnSoapConnection * -msn_soap_get_connection(MsnSession *session, const char *host) +xmlnode * +msn_soap_message_get_xml(MsnSoapMessage *message) { - MsnSoapConnection *conn = NULL; - - if (session->soap_table) { - conn = g_hash_table_lookup(session->soap_table, host); - } else { - session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, (GDestroyNotify)msn_soap_connection_destroy); - } - - if (session->soap_cleanup_handle == 0) - session->soap_cleanup_handle = purple_timeout_add_seconds(SOAP_TIMEOUT, - msn_soap_cleanup_for_session, session); - - if (conn == NULL) { - conn = msn_soap_connection_new(session, host); - g_hash_table_insert(session->soap_table, conn->host, conn); - } - - conn->last_used = time(NULL); + g_return_val_if_fail(message != NULL, NULL); - return conn; + return message->xml; } static void -msn_soap_connection_handle_next(MsnSoapConnection *conn) +msn_soap_request_free(MsnSoapRequest *sreq) { - msn_soap_connection_sanitize(conn, FALSE); + g_return_if_fail(sreq != NULL); - conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn); + msn_soap_message_free(sreq->message); + g_free(sreq); } -static void -msn_soap_message_send_internal(MsnSession *session, MsnSoapMessage *message, - const char *host, const char *path, gboolean secure, - MsnSoapCallback cb, gpointer cb_data, gboolean first) +MsnSoapService * +msn_soap_service_new(MsnSession *session) { - MsnSoapConnection *conn = msn_soap_get_connection(session, host); - MsnSoapRequest *req = g_new0(MsnSoapRequest, 1); - - req->path = g_strdup(path); - req->message = message; - req->secure = secure; - req->cb = cb; - req->cb_data = cb_data; - - if (first) { - g_queue_push_head(conn->queue, req); - } else { - g_queue_push_tail(conn->queue, req); - } + MsnSoapService *soaps; - if (conn->run_timer == 0) - conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, - conn); -} + g_return_val_if_fail(session != NULL, NULL); -void -msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, - const char *host, const char *path, gboolean secure, - MsnSoapCallback cb, gpointer cb_data) -{ - g_return_if_fail(message != NULL); - g_return_if_fail(cb != NULL); + soaps = g_new0(MsnSoapService, 1); + soaps->session = session; + soaps->keepalive_pool = purple_http_keepalive_pool_new(); + purple_http_keepalive_pool_set_limit_per_host(soaps->keepalive_pool, 1); - msn_soap_message_send_internal(session, message, host, path, secure, - cb, cb_data, FALSE); + return soaps; } -static gboolean -msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url_raw) +void +msn_soap_service_destroy(MsnSoapService *soaps) { - MsnSoapRequest *req; - PurpleHttpURL *url; - - url = purple_http_url_parse(url_raw); - if (!url) - return FALSE; - - req = conn->current_request; - conn->current_request = NULL; - - msn_soap_message_send_internal(conn->session, req->message, - purple_http_url_get_host(url), purple_http_url_get_path(url), - req->secure, req->cb, req->cb_data, TRUE); - - msn_soap_request_destroy(req, TRUE); - - purple_http_url_free(url); + if (soaps == NULL) + return; - return TRUE; + purple_http_keepalive_pool_unref(soaps->keepalive_pool); + g_free(soaps); } -static gboolean -msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response) +static void +msn_soap_service_recv(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _sreq) { - xmlnode *body = xmlnode_get_child(response->xml, "Body"); - xmlnode *fault = xmlnode_get_child(response->xml, "Fault"); + MsnSoapRequest *sreq = _sreq; + xmlnode *xml_root, *xml_body, *xml_fault; + const gchar *xml_raw; + size_t xml_len; - if (fault) { - xmlnode *faultcode = xmlnode_get_child(fault, "faultcode"); + if (purple_http_response_get_code(response) == 401) { + const gchar *errmsg; - if (faultcode != NULL) { - char *faultdata = xmlnode_get_data(faultcode); + purple_debug_error("msn-soap", "SOAP authentication failed\n"); - if (g_str_equal(faultdata, "psf:Redirect")) { - xmlnode *url = xmlnode_get_child(fault, "redirectUrl"); + errmsg = purple_http_response_get_header(response, + "WWW-Authenticate"); - if (url) { - char *urldata = xmlnode_get_data(url); - msn_soap_handle_redirect(conn, urldata); - g_free(urldata); - } + msn_session_set_error(sreq->soaps->session, MSN_ERROR_AUTH, + errmsg ? purple_url_decode(errmsg) : NULL); - g_free(faultdata); - msn_soap_message_destroy(response); - return TRUE; - } else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) { - xmlnode *reason = xmlnode_get_child(fault, "faultstring"); - char *reasondata = xmlnode_get_data(reason); - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - reasondata); - - g_free(reasondata); - g_free(faultdata); - msn_soap_message_destroy(response); - return FALSE; - } - - g_free(faultdata); - } + msn_soap_request_free(sreq); + return; } - - if (fault || body) { - if (conn->current_request) { - MsnSoapRequest *request = conn->current_request; - conn->current_request = NULL; - request->cb(request->message, response, - request->cb_data); - msn_soap_request_destroy(request, FALSE); - } - msn_soap_message_destroy(response); + if (!purple_http_response_is_successfull(response)) { + purple_debug_error("msn-soap", "SOAP request failed\n"); + msn_session_set_error(sreq->soaps->session, + MSN_ERROR_SERV_UNAVAILABLE, NULL); + msn_soap_request_free(sreq); + return; } - return TRUE; -} - -static void -msn_soap_message_add_header(MsnSoapMessage *message, - const char *name, const char *value) -{ - char *header = g_strdup_printf("%s: %s\r\n", name, value); + xml_raw = purple_http_response_get_data(response, &xml_len); + xml_root = xmlnode_from_str(xml_raw, xml_len); - message->headers = g_slist_prepend(message->headers, header); -} - -static void -msn_soap_process(MsnSoapConnection *conn) -{ - gboolean handled = FALSE; - char *cursor; - char *linebreak; - - cursor = conn->buf->str + conn->handled_len; - - if (!conn->headers_done) { - while ((linebreak = strstr(cursor, "\r\n")) != NULL) { - conn->handled_len = linebreak - conn->buf->str + 2; - - if (conn->response_code == 0) { - if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) { - /* something horribly wrong */ - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - handled = TRUE; - break; - } else if (conn->response_code == 503 && conn->session->login_step < MSN_LOGIN_STEP_END) { - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - return; - } - } else if (cursor == linebreak) { - /* blank line */ - conn->headers_done = TRUE; - cursor = conn->buf->str + conn->handled_len; - break; - } else { - char *line = g_strndup(cursor, linebreak - cursor); - char *sep = strstr(line, ": "); - char *key = line; - char *value; - - if (sep == NULL) { - purple_debug_info("soap", "ignoring malformed line: %s\n", line); - g_free(line); - goto loop_end; - } - - value = sep + 2; - *sep = '\0'; - msn_soap_message_add_header(conn->message, key, value); - - if ((conn->response_code == 301 || conn->response_code == 300) - && strcmp(key, "Location") == 0) { - - msn_soap_handle_redirect(conn, value); - - handled = TRUE; - g_free(line); - break; - } else if (conn->response_code == 401 && - strcmp(key, "WWW-Authenticate") == 0) { - char *error = strstr(value, "cbtxt="); - - if (error) { - error += strlen("cbtxt="); - } - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - error ? purple_url_decode(error) : NULL); - - g_free(line); - return; - } else if (strcmp(key, "Content-Length") == 0) { - if (sscanf(value, "%" G_GSIZE_FORMAT, &(conn->body_len)) != 1) - purple_debug_error("soap", "Unable to parse Content-Length\n"); - } else if (strcmp(key, "Connection") == 0) { - if (strcmp(value, "close") == 0) { - conn->close_when_done = TRUE; - } - } - g_free(line); - } - - loop_end: - cursor = conn->buf->str + conn->handled_len; + if (purple_debug_is_verbose()) { + if (sreq->secure && !purple_debug_is_unsafe()) { + purple_debug_misc("msn-soap", + "Received secure SOAP request.\n"); + } else { + purple_debug_misc("msn-soap", + "Received SOAP request: %s\n", xml_raw); } } - if (!handled && conn->headers_done) { - if (conn->buf->len - conn->handled_len >= - conn->body_len) { - xmlnode *node = xmlnode_from_str(cursor, conn->body_len); - - if (node == NULL) { - purple_debug_info("soap", "Malformed SOAP response: %s\n", - cursor); - } else { - MsnSoapMessage *message = conn->message; - conn->message = NULL; - message->xml = node; - - if (!msn_soap_handle_body(conn, message)) { - return; - } - } - - msn_soap_connection_handle_next(conn); - } - + if (xml_root == NULL) { + purple_debug_error("msn-soap", "SOAP response malformed\n"); + msn_session_set_error(sreq->soaps->session, + MSN_ERROR_HTTP_MALFORMED, NULL); + msn_soap_request_free(sreq); return; } - if (handled) { - msn_soap_connection_handle_next(conn); - } -} - -static void -msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - int count = 0, cnt, perrno; - /* This buffer needs to be larger than any packets received from - login.live.com or Adium will fail to receive the packet - (something weird with the login.live.com server). With NSS it works - fine, so I believe it's some bug with OS X */ - char buf[16 * 1024]; - gsize cursor; - - if (conn->message == NULL) { - conn->message = msn_soap_message_new(NULL, NULL); - } + xml_body = xmlnode_get_child(xml_root, "Body"); + xml_fault = xmlnode_get_child(xml_root, "Fault"); - if (conn->buf == NULL) { - conn->buf = g_string_new_len(buf, 0); - } + if (xml_fault != NULL) { + xmlnode *xml_faultcode; + gchar *faultdata = NULL; - cursor = conn->buf->len; - while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) { - purple_debug_info("soap", "read %d bytes\n", cnt); - count += cnt; - g_string_append_len(conn->buf, buf, cnt); - } + xml_faultcode = xmlnode_get_child(xml_fault, "faultcode"); + if (xml_faultcode != NULL) + faultdata = xmlnode_get_data(xml_faultcode); - perrno = errno; - if (cnt < 0 && perrno != EAGAIN) - purple_debug_info("soap", "read: %s\n", g_strerror(perrno)); + if (g_strcmp0(faultdata, "psf:Redirect") == 0) { + xmlnode *xml_url; + gchar *url = NULL; - if (conn->current_request && conn->current_request->secure && - !purple_debug_is_unsafe()) - purple_debug_misc("soap", "Received secure request.\n"); - else if (count != 0) - purple_debug_misc("soap", "current %s\n", conn->buf->str + cursor); + xml_url = xmlnode_get_child(xml_fault, "redirectUrl"); + if (xml_url != NULL) + url = xmlnode_get_data(xml_url); - /* && count is necessary for Adium, on OS X the last read always - return an error, so we want to proceed anyway. See #5212 for - discussion on this and the above buffer size issues */ - if(cnt < 0 && errno == EAGAIN && count == 0) - return; + msn_soap_service_send_message_simple(sreq->soaps, + sreq->message, url, sreq->secure, sreq->cb, + sreq->cb_data); - /* msn_soap_process could alter errno */ - msn_soap_process(conn); + /* Steal the message, passed to another call. */ + sreq->message = NULL; + msn_soap_request_free(sreq); - if ((cnt < 0 && perrno != EAGAIN) || cnt == 0) { - /* It's possible msn_soap_process closed the ssl connection */ - if (conn->ssl) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); + g_free(url); + g_free(faultdata); + return; } - } -} + if (g_strcmp0(faultdata, "wsse:FailedAuthentication") == 0) { + xmlnode *xml_reason = + xmlnode_get_child(xml_fault, "faultstring"); + gchar *reasondata = xmlnode_get_data(xml_reason); -static gboolean -msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, - gboolean initial) -{ - MsnSoapConnection *conn = data; - int written; - - if (cond != PURPLE_INPUT_WRITE) - return TRUE; - - written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len, - conn->buf->len - conn->handled_len); - - if (written < 0 && errno == EAGAIN) - return TRUE; - else if (written <= 0) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - if (!initial) - msn_soap_connection_handle_next(conn); - return FALSE; - } + msn_session_set_error(sreq->soaps->session, MSN_ERROR_AUTH, + reasondata); - conn->handled_len += written; + g_free(reasondata); + g_free(faultdata); + msn_soap_request_free(sreq); + return; + } + g_free(faultdata); + } - if (conn->handled_len < conn->buf->len) - return TRUE; + if (xml_fault != NULL || xml_body != NULL) { + MsnSoapMessage *resp; - /* we are done! */ - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - conn->handled_len = 0; - conn->body_len = 0; - conn->response_code = 0; - conn->headers_done = FALSE; - conn->close_when_done = FALSE; + resp = msn_soap_message_new(NULL, xml_root); + sreq->cb(sreq->message, resp, sreq->cb_data); + msn_soap_message_free(resp); + } - purple_input_remove(conn->event_handle); - conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ, - msn_soap_read_cb, conn); - return TRUE; + /* XXX: shouldn't msn_session_set_error here? */ + msn_soap_request_free(sreq); } static void -msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - msn_soap_write_cb_internal(data, fd, cond, FALSE); -} - -static void -msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error, - gpointer data) +msn_soap_service_send_message_simple(MsnSoapService *soaps, + MsnSoapMessage *message, const gchar *url, gboolean secure, + MsnSoapCallback cb, gpointer cb_data) { - MsnSoapConnection *conn = data; - - /* sslconn already frees the connection in case of error */ - conn->ssl = NULL; - - g_hash_table_remove(conn->session->soap_table, conn->host); + PurpleHttpRequest *hreq; + MsnSoapRequest *sreq; + gchar *body; + int body_len; + + sreq = g_new0(MsnSoapRequest, 1); + sreq->soaps = soaps; + sreq->cb = cb; + sreq->cb_data = cb_data; + sreq->secure = secure; + sreq->message = message; + + hreq = purple_http_request_new(url); + purple_http_request_set_method(hreq, "POST"); + purple_http_request_set_keepalive_pool(hreq, soaps->keepalive_pool); + purple_http_request_header_set(hreq, "SOAPAction", + message->action ? message->action : ""); + purple_http_request_header_set(hreq, "Content-Type", + "text/xml; charset=utf-8"); + purple_http_request_header_set(hreq, "User-Agent", + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"); + purple_http_request_header_set(hreq, "Cache-Control", "no-cache"); + + body = xmlnode_to_str(message->xml, &body_len); + purple_http_request_set_contents(hreq, body, body_len); + g_free(body); + + purple_http_request(purple_account_get_connection( + soaps->session->account), hreq, msn_soap_service_recv, sreq); + purple_http_request_unref(hreq); } -static void -msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl, - PurpleInputCondition cond) +void +msn_soap_service_send_message(MsnSoapService *soaps, MsnSoapMessage *message, + const gchar *host, const gchar *path, gboolean secure, + MsnSoapCallback cb, gpointer cb_data) { - MsnSoapConnection *conn = data; + gchar *url; - conn->connected = TRUE; + g_return_if_fail(host != NULL); + g_return_if_fail(path != NULL); - if (conn->run_timer == 0) - conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn); -} + if (path[0] == '/') + path = &path[1]; -MsnSoapMessage * -msn_soap_message_new(const char *action, xmlnode *xml) -{ - MsnSoapMessage *message = g_new0(MsnSoapMessage, 1); - - message->action = g_strdup(action); - message->xml = xml; - - return message; -} + url = g_strdup_printf("https://%s/%s", host, path); -static gboolean -msn_soap_connection_run(gpointer data) -{ - MsnSoapConnection *conn = data; - MsnSoapRequest *req = g_queue_peek_head(conn->queue); - - conn->run_timer = 0; - - if (req) { - if (conn->ssl == NULL) { - conn->ssl = purple_ssl_connect(conn->session->account, conn->host, - 443, msn_soap_connected_cb, msn_soap_error_cb, conn); - } else if (conn->connected) { - int len = -1; - char *body = xmlnode_to_str(req->message->xml, &len); - GSList *iter; - - g_queue_pop_head(conn->queue); - - conn->buf = g_string_new(""); - - g_string_append_printf(conn->buf, - "POST /%s HTTP/1.1\r\n" - "SOAPAction: %s\r\n" - "Content-Type:text/xml; charset=utf-8\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Content-Length: %d\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n", - req->path, req->message->action ? req->message->action : "", - conn->host, len); - - for (iter = req->message->headers; iter; iter = iter->next) { - g_string_append(conn->buf, (char *)iter->data); - g_string_append(conn->buf, "\r\n"); - } - - g_string_append(conn->buf, "\r\n"); - g_string_append(conn->buf, body); - - if (req->secure && !purple_debug_is_unsafe()) - purple_debug_misc("soap", "Sending secure request.\n"); - else - purple_debug_misc("soap", "%s\n", conn->buf->str); - - conn->handled_len = 0; - conn->current_request = req; - - if (conn->event_handle) - purple_input_remove(conn->event_handle); - conn->event_handle = purple_input_add(conn->ssl->fd, - PURPLE_INPUT_WRITE, msn_soap_write_cb, conn); - if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) { - /* Not connected => reconnect and retry */ - purple_debug_info("soap", "not connected, reconnecting\n"); - - conn->connected = FALSE; - conn->current_request = NULL; - msn_soap_connection_sanitize(conn, FALSE); - - g_queue_push_head(conn->queue, req); - conn->run_timer = purple_timeout_add(0, msn_soap_connection_run, conn); - } - - g_free(body); - } - } + msn_soap_service_send_message_simple(soaps, message, url, secure, + cb, cb_data); - return FALSE; + g_free(url); } diff --git a/libpurple/protocols/msn/soap.h b/libpurple/protocols/msn/soap.h index 7a66c0dd19..9222986b59 100644 --- a/libpurple/protocols/msn/soap.h +++ b/libpurple/protocols/msn/soap.h @@ -26,21 +26,29 @@ #include "xmlnode.h" -#include "session.h" - typedef struct _MsnSoapMessage MsnSoapMessage; +typedef struct _MsnSoapService MsnSoapService; + typedef void (*MsnSoapCallback)(MsnSoapMessage *request, MsnSoapMessage *response, gpointer cb_data); +#include "session.h" + MsnSoapMessage * msn_soap_message_new(const gchar *action, xmlnode *xml); xmlnode * msn_soap_message_get_xml(MsnSoapMessage *message); +MsnSoapService * +msn_soap_service_new(MsnSession *session); + +void +msn_soap_service_destroy(MsnSoapService *soaps); + void -msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, +msn_soap_service_send_message(MsnSoapService *soaps, MsnSoapMessage *message, const gchar *host, const gchar *path, gboolean secure, MsnSoapCallback cb, gpointer cb_data); |