diff libpurple/protocols/mxit/http.c @ 28526: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 );
+}
+