--- a/src/protocols/msn/httpconn.c Wed Oct 18 16:28:51 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,776 +0,0 @@ -/** - * @file httpmethod.c HTTP connection method - * - * gaim - * - * Gaim is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "msn.h" -#include "debug.h" -#include "httpconn.h" - -static void read_cb(gpointer data, gint source, GaimInputCondition cond); -gboolean msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, - size_t size, char **ret_buf, size_t *ret_size, - gboolean *error); - -MsnHttpConn * -msn_httpconn_new(MsnServConn *servconn) -{ - MsnHttpConn *httpconn; - - g_return_val_if_fail(servconn != NULL, NULL); - - httpconn = g_new0(MsnHttpConn, 1); - - gaim_debug_info("msn", "new httpconn (%p)\n", httpconn); - - /* TODO: Remove this */ - httpconn->session = servconn->session; - - httpconn->servconn = servconn; - - httpconn->tx_buf = gaim_circ_buffer_new(MSN_BUF_LEN); - httpconn->tx_handler = 0; - - return httpconn; -} - -void -msn_httpconn_destroy(MsnHttpConn *httpconn) -{ - g_return_if_fail(httpconn != NULL); - - gaim_debug_info("msn", "destroy httpconn (%p)\n", httpconn); - - if (httpconn->connected) - msn_httpconn_disconnect(httpconn); - - g_free(httpconn->full_session_id); - - g_free(httpconn->session_id); - - g_free(httpconn->host); - - gaim_circ_buffer_destroy(httpconn->tx_buf); - if (httpconn->tx_handler > 0) - gaim_input_remove(httpconn->tx_handler); - - g_free(httpconn); -} - -static char * -msn_httpconn_proxy_auth(MsnHttpConn *httpconn) -{ - GaimAccount *account; - GaimProxyInfo *gpi; - const char *username, *password; - char *auth = NULL; - - account = httpconn->session->account; - - if (gaim_account_get_proxy_info(account) == NULL) - gpi = gaim_global_proxy_get_info(); - else - gpi = gaim_account_get_proxy_info(account); - - if (gpi == NULL || !(gaim_proxy_info_get_type(gpi) == GAIM_PROXY_HTTP || - gaim_proxy_info_get_type(gpi) == GAIM_PROXY_USE_ENVVAR)) - return NULL; - - username = gaim_proxy_info_get_username(gpi); - password = gaim_proxy_info_get_password(gpi); - - if (username != NULL) { - char *tmp; - auth = g_strdup_printf("%s:%s", username, password ? password : ""); - tmp = gaim_base64_encode((const guchar *)auth, strlen(auth)); - g_free(auth); - auth = g_strdup_printf("Proxy-Authorization: Basic %s\r\n", tmp); - g_free(tmp); - } - - return auth; -} - -static void -httpconn_write_cb(gpointer data, gint source, GaimInputCondition cond) -{ - MsnHttpConn *httpconn = data; - int ret, writelen; - - if (httpconn->waiting_response) - return; - - writelen = gaim_circ_buffer_get_max_read(httpconn->tx_buf); - - if (writelen == 0) { - httpconn->waiting_response = TRUE; - gaim_input_remove(httpconn->tx_handler); - httpconn->tx_handler = 0; - return; - } - - ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen); - - if (ret < 0 && errno == EAGAIN) - return; - else if (ret <= 0) { - msn_servconn_got_error(httpconn->servconn, - MSN_SERVCONN_ERROR_WRITE); - return; - } - - gaim_circ_buffer_mark_read(httpconn->tx_buf, ret); - - if (ret == writelen) - httpconn_write_cb(data, source, cond); -} - -static ssize_t -write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len) -{ - ssize_t res; /* result of the write operation */ - -#ifdef MSN_DEBUG_HTTP - gaim_debug_misc("msn", "Writing HTTP (header): {%s}\n", header); -#endif - - - if (httpconn->tx_handler == 0 && !httpconn->waiting_response) - res = write(httpconn->fd, data, data_len); - else - { - res = -1; - errno = EAGAIN; - } - - if (res <= 0 && errno != EAGAIN) - { - msn_servconn_got_error(httpconn->servconn, - MSN_SERVCONN_ERROR_WRITE); - return -1; - } else if (res < 0 || res < data_len) { - if (res < 0) - res = 0; - if (httpconn->tx_handler == 0 && httpconn->fd) - httpconn->tx_handler = gaim_input_add(httpconn->fd, - GAIM_INPUT_WRITE, httpconn_write_cb, httpconn); - gaim_circ_buffer_append(httpconn->tx_buf, data + res, - data_len - res); - } - - return res; -} - -static void -msn_httpconn_poll(MsnHttpConn *httpconn) -{ - char *header; - char *auth; - int r; - - g_return_if_fail(httpconn != NULL); - - if (httpconn->waiting_response || - httpconn->tx_handler > 0) - { - return; - } - - /* It is OK if this is buffered because it will only be buffered if - nothing else is in the buffer */ - - auth = msn_httpconn_proxy_auth(httpconn); - - header = g_strdup_printf( - "POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n" - "Accept: */*\r\n" - "Accept-Language: en-us\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Proxy-Connection: Keep-Alive\r\n" - "%s" /* Proxy auth */ - "Connection: Keep-Alive\r\n" - "Pragma: no-cache\r\n" - "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: 0\r\n\r\n", - httpconn->host, - httpconn->full_session_id, - httpconn->host, - auth ? auth : ""); - - g_free(auth); - - r = write_raw(httpconn, header, strlen(header)); - - g_free(header); - - if (r >= 0) - { - httpconn->waiting_response = TRUE; - httpconn->dirty = FALSE; - } -} - -static gboolean -do_poll(gpointer data) -{ - MsnHttpConn *httpconn; - - httpconn = data; - - g_return_val_if_fail(httpconn != NULL, TRUE); - -#if 0 - gaim_debug_info("msn", "polling from %s\n", httpconn->session_id); -#endif - - if ((httpconn->host == NULL) || (httpconn->full_session_id == NULL)) - { - gaim_debug_warning("msn", "Attempted HTTP poll before session is established\n"); - return TRUE; - } - - if (httpconn->dirty) - msn_httpconn_poll(httpconn); - - return TRUE; -} - -static void -connect_cb(gpointer data, gint source, GaimInputCondition cond) -{ - MsnHttpConn *httpconn = data; - - httpconn->fd = source; - - if (source > 0) - { - httpconn->inpa = gaim_input_add(httpconn->fd, GAIM_INPUT_READ, - read_cb, data); - - httpconn->timer = gaim_timeout_add(2000, do_poll, httpconn); - - httpconn->waiting_response = FALSE; - if (httpconn->tx_handler > 0) - gaim_input_remove(httpconn->tx_handler); - - httpconn->tx_handler = gaim_input_add(source, - GAIM_INPUT_WRITE, httpconn_write_cb, httpconn); - - httpconn_write_cb(httpconn, source, GAIM_INPUT_WRITE); - } - else - { - gaim_debug_error("msn", "HTTP: Connection error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT); - } -} - -gboolean -msn_httpconn_connect(MsnHttpConn *httpconn, const char *host, int port) -{ - int r; - - g_return_val_if_fail(httpconn != NULL, FALSE); - g_return_val_if_fail(host != NULL, FALSE); - g_return_val_if_fail(port > 0, FALSE); - - if (httpconn->connected) - msn_httpconn_disconnect(httpconn); - - r = gaim_proxy_connect(httpconn->session->account, - "gateway.messenger.hotmail.com", 80, connect_cb, httpconn); - - if (r == 0) - { - httpconn->waiting_response = TRUE; - httpconn->connected = TRUE; - } - - return httpconn->connected; -} - -void -msn_httpconn_disconnect(MsnHttpConn *httpconn) -{ - g_return_if_fail(httpconn != NULL); - - if (!httpconn->connected) - return; - - if (httpconn->timer) - gaim_timeout_remove(httpconn->timer); - - httpconn->timer = 0; - - if (httpconn->inpa > 0) - { - gaim_input_remove(httpconn->inpa); - httpconn->inpa = 0; - } - - close(httpconn->fd); - - g_free(httpconn->rx_buf); - httpconn->rx_buf = NULL; - httpconn->rx_len = 0; - - httpconn->connected = FALSE; - - /* msn_servconn_disconnect(httpconn->servconn); */ -} - -static void -read_cb(gpointer data, gint source, GaimInputCondition cond) -{ - MsnHttpConn *httpconn; - MsnServConn *servconn; - MsnSession *session; - char buf[MSN_BUF_LEN]; - char *cur, *end, *old_rx_buf; - int len, cur_len; - char *result_msg = NULL; - size_t result_len = 0; - gboolean error; - - httpconn = data; - servconn = NULL; - session = httpconn->session; - - len = read(httpconn->fd, buf, sizeof(buf) - 1); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) - { - gaim_debug_error("msn", "HTTP: Read error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - buf[len] = '\0'; - - httpconn->rx_buf = g_realloc(httpconn->rx_buf, len + httpconn->rx_len + 1); - memcpy(httpconn->rx_buf + httpconn->rx_len, buf, len + 1); - httpconn->rx_len += len; - - if (!msn_httpconn_parse_data(httpconn, httpconn->rx_buf, httpconn->rx_len, - &result_msg, &result_len, &error)) - { - /* We must wait for more input, or something went wrong */ - if (error) - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - httpconn->servconn->processing = FALSE; - - servconn = httpconn->servconn; - - if (error) - { - gaim_debug_error("msn", "HTTP: Special error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - g_free(httpconn->rx_buf); - httpconn->rx_buf = NULL; - httpconn->rx_len = 0; - - if (result_len == 0) - { - /* Nothing to do here */ -#if 0 - gaim_debug_info("msn", "HTTP: nothing to do here\n"); -#endif - g_free(result_msg); - return; - } - - g_free(servconn->rx_buf); - servconn->rx_buf = result_msg; - servconn->rx_len = result_len; - - end = old_rx_buf = servconn->rx_buf; - - servconn->processing = TRUE; - - do - { - cur = end; - - if (servconn->payload_len) - { - if (servconn->payload_len > servconn->rx_len) - /* The payload is still not complete. */ - break; - - cur_len = servconn->payload_len; - end += cur_len; - } - else - { - end = strstr(cur, "\r\n"); - - if (end == NULL) - /* The command is still not complete. */ - break; - - *end = '\0'; - end += 2; - cur_len = end - cur; - } - - servconn->rx_len -= cur_len; - - if (servconn->payload_len) - { - msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len); - servconn->payload_len = 0; - } - else - { - msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); - } - } while (servconn->connected && servconn->rx_len > 0); - - if (servconn->connected) - { - if (servconn->rx_len > 0) - servconn->rx_buf = g_memdup(cur, servconn->rx_len); - else - servconn->rx_buf = NULL; - } - - servconn->processing = FALSE; - - if (servconn->wasted) - msn_servconn_destroy(servconn); - - g_free(old_rx_buf); -} - -ssize_t -msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t size) -{ - char *params; - char *data; - char *auth; - const char *server_types[] = { "NS", "SB" }; - const char *server_type; - ssize_t r; /* result of the write operation */ - char *host; - MsnServConn *servconn; - - /* TODO: remove http data from servconn */ - - g_return_val_if_fail(httpconn != NULL, 0); - g_return_val_if_fail(body != NULL, 0); - g_return_val_if_fail(size > 0, 0); - - servconn = httpconn->servconn; - - server_type = server_types[servconn->type]; - - if (httpconn->virgin) - { - host = "gateway.messenger.hotmail.com"; - - /* The first time servconn->host is the host we should connect to. */ - params = g_strdup_printf("Action=open&Server=%s&IP=%s", - server_type, - servconn->host); - httpconn->virgin = FALSE; - } - else - { - /* The rest of the times servconn->host is the gateway host. */ - host = httpconn->host; - - if (host == NULL || httpconn->full_session_id == NULL) - { - gaim_debug_warning("msn", "Attempted HTTP write before session is established\n"); - return -1; - } - - params = g_strdup_printf("SessionID=%s", - httpconn->full_session_id); - } - - auth = msn_httpconn_proxy_auth(httpconn); - - data = g_strdup_printf( - "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n" - "Accept: */*\r\n" - "Accept-Language: en-us\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Proxy-Connection: Keep-Alive\r\n" - "%s" /* Proxy auth */ - "Connection: Keep-Alive\r\n" - "Pragma: no-cache\r\n" - "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: %d\r\n\r\n" - "%s", - host, - params, - host, - auth ? auth : "", - (int) size, - body ? body : ""); - - - g_free(params); - - g_free(auth); - - r = write_raw(httpconn, data, strlen(data)); - - g_free(data); - - if (r >= 0) - { - httpconn->waiting_response = TRUE; - httpconn->dirty = FALSE; - } - - return r; -} - -gboolean -msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, - size_t size, char **ret_buf, size_t *ret_size, - gboolean *error) -{ - const char *s, *c; - char *header, *body; - const char *body_start; - char *tmp; - size_t body_len = 0; - gboolean wasted = FALSE; - - g_return_val_if_fail(httpconn != NULL, FALSE); - g_return_val_if_fail(buf != NULL, FALSE); - g_return_val_if_fail(size > 0, FALSE); - g_return_val_if_fail(ret_buf != NULL, FALSE); - g_return_val_if_fail(ret_size != NULL, FALSE); - g_return_val_if_fail(error != NULL, FALSE); - -#if 0 - gaim_debug_info("msn", "HTTP: parsing data {%s}\n", buf); -#endif - - httpconn->waiting_response = FALSE; - - /* Healthy defaults. */ - body = NULL; - - *ret_buf = NULL; - *ret_size = 0; - *error = FALSE; - - /* First, some tests to see if we have a full block of stuff. */ - if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) && - (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) && - ((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) && - (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0))) - { - *error = TRUE; - - return FALSE; - } - - if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0) - { - if ((s = strstr(buf, "\r\n\r\n")) == NULL) - return FALSE; - - s += 4; - - if (*s == '\0') - { - *ret_buf = g_strdup(""); - *ret_size = 0; - - if (httpconn->tx_handler > 0) - httpconn_write_cb(httpconn, httpconn->fd, - GAIM_INPUT_WRITE); - else - httpconn->dirty = TRUE; - - return TRUE; - } - - buf = s; - size -= (s - buf); - } - - if ((s = strstr(buf, "\r\n\r\n")) == NULL) - return FALSE; - - s += 4; /* Skip \r\n */ - header = g_strndup(buf, s - buf); - body_start = s; - body_len = size - (body_start - buf); - - if ((s = gaim_strcasestr(header, "Content-Length: ")) != NULL) - { - int tmp_len; - - s += strlen("Content-Length: "); - - if ((c = strchr(s, '\r')) == NULL) - { - g_free(header); - - return FALSE; - } - - tmp = g_strndup(s, c - s); - tmp_len = atoi(tmp); - g_free(tmp); - - if (body_len != tmp_len) - { - g_free(header); - -#if 0 - gaim_debug_warning("msn", - "body length (%d) != content length (%d)\n", - body_len, tmp_len); -#endif - - return FALSE; - } - } - - body = g_malloc0(body_len + 1); - memcpy(body, body_start, body_len); - -#ifdef MSN_DEBUG_HTTP - gaim_debug_misc("msn", "Incoming HTTP buffer (header): {%s\r\n}\n", - header); -#endif - - /* Now we should be able to process the data. */ - if ((s = gaim_strcasestr(header, "X-MSN-Messenger: ")) != NULL) - { - char *full_session_id, *gw_ip, *session_action; - char *t, *session_id; - char **elems, **cur, **tokens; - - full_session_id = gw_ip = session_action = NULL; - - s += strlen("X-MSN-Messenger: "); - - if ((c = strchr(s, '\r')) == NULL) - { - msn_session_set_error(httpconn->session, - MSN_ERROR_HTTP_MALFORMED, NULL); - gaim_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}", - buf); - - g_free(body); - return FALSE; - } - - tmp = g_strndup(s, c - s); - - elems = g_strsplit(tmp, "; ", 0); - - for (cur = elems; *cur != NULL; cur++) - { - tokens = g_strsplit(*cur, "=", 2); - - if (strcmp(tokens[0], "SessionID") == 0) - full_session_id = tokens[1]; - else if (strcmp(tokens[0], "GW-IP") == 0) - gw_ip = tokens[1]; - else if (strcmp(tokens[0], "Session") == 0) - session_action = tokens[1]; - - g_free(tokens[0]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); - } - - g_strfreev(elems); - - g_free(tmp); - - if ((session_action != NULL) && (strcmp(session_action, "close") == 0)) - wasted = TRUE; - - g_free(session_action); - - t = strchr(full_session_id, '.'); - session_id = g_strndup(full_session_id, t - full_session_id); - - if (!wasted) - { - g_free(httpconn->full_session_id); - - httpconn->full_session_id = full_session_id; - - g_free(httpconn->session_id); - - httpconn->session_id = session_id; - - g_free(httpconn->host); - - httpconn->host = gw_ip; - } - else - { - MsnServConn *servconn; - - /* It's going to die. */ - /* poor thing */ - - servconn = httpconn->servconn; - - /* I'll be honest, I don't fully understand all this, but this - * causes crashes, Stu. */ - /* if (servconn != NULL) - servconn->wasted = TRUE; */ - - g_free(full_session_id); - g_free(session_id); - g_free(gw_ip); - } - } - - g_free(header); - - *ret_buf = body; - *ret_size = body_len; - - if (httpconn->tx_handler > 0) - httpconn_write_cb(httpconn, httpconn->fd, GAIM_INPUT_WRITE); - else - httpconn->dirty = TRUE; - - return TRUE; -}