Mercurial > pidgin.yaz
diff libpurple/protocols/mxit/http.c @ 28903:69aa4660401a
Initial addition of the MXit protocol plugin, provided by the MXit folks
themselves.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Sun, 08 Nov 2009 23:55:56 +0000 |
parents | |
children | 95f8e7fb1f67 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/http.c Sun Nov 08 23:55:56 2009 +0000 @@ -0,0 +1,331 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- MXit client protocol implementation -- + * + * Pieter Loubser <libpurple@mxit.com> + * + * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "purple.h" + +#include "mxit.h" +#include "protocol.h" +#include "http.h" + + +/* HTTP constants */ +#define HTTP_11_200_OK "HTTP/1.1 200 OK\r\n" +#define HTTP_11_100_CONT "HTTP/1.1 100 Continue\r\n" +#define HTTP_11_SEPERATOR "\r\n\r\n" +#define HTTP_CONTENT_LEN "Content-Length: " + + +/* define to enable HTTP debugging */ +#define DEBUG_HTTP + + +/*------------------------------------------------------------------------ + * This will freeup the memory used by a HTTP request structure + * + * @param req The HTTP structure's resources should be freed up + */ +static void free_http_request( struct http_request* req ) +{ + g_free( req->host ); + g_free( req->data ); + g_free( req ); +} + + +/*------------------------------------------------------------------------ + * Write the request to the HTTP server. + * + * @param fd The file descriptor + * @param pktdata The packet data + * @param pktlen The length of the packet data + * @return Return -1 on error, otherwise 0 + */ +static int mxit_http_raw_write( int fd, const char* pktdata, int pktlen ) +{ + int written; + int res; + + written = 0; + while ( written < pktlen ) { + res = write( fd, &pktdata[written], pktlen - written ); + if ( res <= 0 ) { + /* error on socket */ + if ( errno == EAGAIN ) + continue; + + purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to HTTP server (%i)\n", res ); + return -1; + } + written += res; + } + + return 0; +} + + +/*------------------------------------------------------------------------ + * Callback when data is received from the HTTP server. + * + * @param user_data The MXit session object + * @param source The file-descriptor on which data was received + * @param cond Condition which caused the callback (PURPLE_INPUT_READ) + */ +static void mxit_cb_http_read( gpointer user_data, gint source, PurpleInputCondition cond ) +{ + struct MXitSession* session = (struct MXitSession*) user_data; + char buf[256]; + int buflen; + char* body; + int bodylen; + char* ch; + int len; + char* tmp; + int res; + char* next; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_read\n" ); + + if ( session->rx_state == RX_STATE_RLEN ) { + /* we are reading in the HTTP headers */ + + /* copy partial headers if we have any part saved */ + memcpy( buf, session->rx_dbuf, session->rx_i ); + buflen = session->rx_i; + + /* read bytes from the socket */ + len = read( session->fd, buf + buflen, sizeof( buf ) - buflen ); + if ( len <= 0 ) { + /* connection has been terminated, or error occured */ + goto done; + } + +//nextpacket: + +#ifdef DEBUG_HTTP + purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 1: (%i)\n", len ); + dump_bytes( session, buf + buflen, len ); +#endif + + /* see if we have all the HTTP headers yet */ + ch = strstr( buf, HTTP_11_SEPERATOR ); + if ( !ch ) { + /* we need to wait for more input, so save what we have */ + session->rx_i = buflen + len; + memcpy( session->rx_dbuf, buf, session->rx_i ); + return; + } + buflen += len; + + /* we have the header's end now skip over the http seperator to get the body offset */ + ch += strlen( HTTP_11_SEPERATOR ); + *(ch - 1) = '\0'; + body = ch; + + res = buflen - ( ch - buf ); + if ( res > 0 ) { + /* we read more bytes than just the header so copy it over */ + memcpy( session->rx_dbuf, ch, res ); + session->rx_i = res; + } + else { + session->rx_i = 0; + } + + /* test for a good response */ + if ( ( strncmp( buf, HTTP_11_200_OK, strlen( HTTP_11_200_OK ) ) != 0 ) && ( strncmp( buf, HTTP_11_100_CONT, strlen( HTTP_11_100_CONT ) ) != 0 ) ) { + /* bad result */ + purple_debug_error( MXIT_PLUGIN_ID, "HTTP error: %s\n", ch ); + goto done; + } + + /* find the content-length */ + ch = (char*) purple_strcasestr( buf, HTTP_CONTENT_LEN ); + if ( !ch ) { + /* bad request. it does not contain a content-length header */ + purple_debug_error( MXIT_PLUGIN_ID, "HTTP reply received without content-length header (ignoring packet)\n" ); + goto done; + } + + /* parse the content-length */ + ch += strlen( HTTP_CONTENT_LEN ); + tmp = strchr( ch, '\r' ); + if ( !tmp ) { + purple_debug_error( MXIT_PLUGIN_ID, "Received bad HTTP reply packet (ignoring packet)\n" ); + goto done; + } + tmp = g_strndup( ch, tmp - ch ); + bodylen = atoi( tmp ); + g_free( tmp ); + tmp = NULL; + + if ( buflen > ( ( body - buf ) + bodylen ) ) { + /* we have a second packet here */ + next = body + bodylen; + session->rx_res = 0; + } + else { + session->rx_res = bodylen - session->rx_i; + } + + if ( session->rx_res == 0 ) { + /* we have read all the data */ + session->rx_i = bodylen; + session->rx_state = RX_STATE_PROC; + } + else { + /* there is still some data outstanding */ + session->rx_state = RX_STATE_DATA; + } + } + else if ( session->rx_state == RX_STATE_DATA ) { + /* we are reading the HTTP content (body) */ + + /* read bytes from the socket */ + len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res ); + if ( len <= 0 ) { + /* connection has been terminated, or error occured */ + goto done; + } + +#ifdef DEBUG_HTTP + purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST READ 2: (%i)\n", len ); + dump_bytes( session, &session->rx_dbuf[session->rx_i], len ); +#endif + session->rx_i += len; + session->rx_res -= len; + + if ( session->rx_res == 0 ) { + /* ok, so now we have read in the whole packet */ + session->rx_state = RX_STATE_PROC; + } + } + + if ( session->rx_state == RX_STATE_PROC ) { + mxit_parse_packet( session ); + +#if 0 + if ( next ) { + /* there is another packet of which we read some data */ + + /* reset input */ + session->rx_state = RX_STATE_RLEN; + session->rx_lbuf[0] = '\0'; + session->rx_i = 0; + session->rx_res = 0; + + /* move read data */ + len = next - buf; + buflen = len; + memcpy( buf, next, len ); + goto nextpacket; + } +#endif + + /* we are done */ + goto done; + } + + return; +done: + close( session->fd ); + purple_input_remove( session->http_handler ); + session->http_handler = 0; +} + + +/*------------------------------------------------------------------------ + * Callback invoked once the connection has been established to the HTTP server, + * or on connection failure. + * + * @param user_data The MXit session object + * @param source The file-descriptor associated with the connection + * @param error_message Message explaining why the connection failed + */ +static void mxit_cb_http_connect( gpointer user_data, gint source, const gchar* error_message ) +{ + struct http_request* req = (struct http_request*) user_data; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect\n" ); + + /* source is the file descriptor of the new connection */ + if ( source < 0 ) { + purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_http_connect failed: %s\n", error_message ); + purple_connection_error( req->session->con, _( "Unable to connect to the mxit HTTP server. Please check your server server settings." ) ); + return; + } + + /* we now have an open and active TCP connection to the mxit server */ + req->session->fd = source; + + /* reset the receive buffer */ + req->session->rx_state = RX_STATE_RLEN; + req->session->rx_lbuf[0] = '\0'; + req->session->rx_i = 0; + req->session->rx_res = 0; + + /* start listening on the open connection for messages from the server (reference: "libpurple/eventloop.h") */ + req->session->http_handler = purple_input_add( req->session->fd, PURPLE_INPUT_READ, mxit_cb_http_read, req->session ); + + /* actually send the request to the HTTP server */ + mxit_http_raw_write( req->session->fd, req->data, req->datalen ); + + /* free up resources */ + free_http_request( req ); + req = NULL; +} + + +/*------------------------------------------------------------------------ + * Create HTTP connection for sending a HTTP request + * + * @param session The MXit session object + * @param host The server name to connect to + * @param port The port number to connect to + * @param data The HTTP request data (including HTTP headers etc.) + * @param datalen The HTTP request data length + */ +void mxit_http_send_request( struct MXitSession* session, char* host, int port, const char* data, int datalen ) +{ + PurpleProxyConnectData* con = NULL; + struct http_request* req; + + /* build the http request */ + req = g_new0( struct http_request, 1 ); + req->session = session; + req->host = host; + req->port = port; + req->data = g_malloc0( datalen ); + memcpy( req->data, data, datalen ); + req->datalen = datalen; + + /* open connection to the HTTP server */ + con = purple_proxy_connect( NULL, session->acc, host, port, mxit_cb_http_connect, req ); +} +