diff libpurple/protocols/mxit/chunk.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 06fabb28bc69
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/mxit/chunk.c	Sun Nov 08 23:55:56 2009 +0000
@@ -0,0 +1,659 @@
+/*
+ *					MXit Protocol libPurple Plugin
+ *
+ *			-- handle chunked data (multimedia messages) --
+ *
+ *				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	"purple.h"
+#include	"protocol.h"
+#include	"mxit.h"
+#include	"chunk.h"
+#include	"filexfer.h"
+
+
+/*========================================================================================================================
+ * Data-Type encoding
+ */
+
+#if	0
+#include	<byteswap.h>
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+#define SWAP_64(x)  (x)
+#else
+#define SWAP_64(x)  bswap_64(x)
+#endif
+#endif
+
+/*------------------------------------------------------------------------
+ * Encode a single byte in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The byte
+ *  @return					The number of bytes added.
+ */
+static int add_int8( char* chunkdata, char value )
+{
+	*chunkdata = value;
+
+	return sizeof( char );
+}
+
+/*------------------------------------------------------------------------
+ * Encode a 16-bit value in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 16-bit value
+ *  @return					The number of bytes added.
+ */
+static int add_int16( char* chunkdata, short value )
+{
+	value = htons( value );		/* network byte-order */
+	memcpy( chunkdata, &value, sizeof( short ) );
+
+	return sizeof( short );
+}
+
+/*------------------------------------------------------------------------
+ * Encode a 32-bit value in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 32-bit value
+ *  @return					The number of bytes added.
+ */
+static int add_int32( char* chunkdata, int value )
+{
+	value = htonl( value );		/* network byte-order */
+	memcpy( chunkdata, &value, sizeof( int ) );
+
+	return sizeof( int );
+}
+
+#if	0
+/*------------------------------------------------------------------------
+ * Encode a 64-bit value in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 64-bit value
+ *  @return					The number of bytes added.
+ */
+static int add_int64( char* chunkdata, int64_t value )
+{
+	value = SWAP_64( value );	/* network byte-order */
+	memcpy( chunkdata, &value, sizeof( int64_t ) );
+
+	return sizeof( int64_t );
+}
+#endif
+
+/*------------------------------------------------------------------------
+ * Encode a block of data in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param data				The data to add
+ *  @param datalen			The length of the data to add
+ *  @return					The number of bytes added.
+ */
+static int add_data( char* chunkdata, const char* data, int datalen )
+{
+	memcpy( chunkdata, data, datalen );
+
+	return datalen;
+}
+
+/*------------------------------------------------------------------------
+ * Encode a string as UTF-8 in the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param str				The string to encode
+ *  @return					The number of bytes in the string
+ */
+static int add_utf8_string( char* chunkdata, const char* str )
+{
+	int		pos		= 0;
+	size_t	len		= strlen( str );
+
+	/* utf8 string length [2 bytes] */
+	pos += add_int16( &chunkdata[pos], len );
+
+	/* utf8 string */
+	pos += add_data( &chunkdata[pos], str, len );
+
+	return pos;
+}
+
+
+/*========================================================================================================================
+ * Data-Type decoding
+ */
+
+/*------------------------------------------------------------------------
+ * Extract a single byte from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The byte
+ *  @return					The number of bytes extracted.
+ */
+static int get_int8( const char* chunkdata, char* value )
+{
+	*value = *chunkdata;
+
+	return sizeof( char );
+}
+
+/*------------------------------------------------------------------------
+ * Extract a 16-bit value from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 16-bit value
+ *  @return					The number of bytes extracted
+ */
+static int get_int16( const char* chunkdata, short* value )
+{
+	*value = ntohs( *( (const short*) chunkdata ) );	/* host byte-order */
+
+	return sizeof( short );
+}
+
+/*------------------------------------------------------------------------
+ * Extract a 32-bit value from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 32-bit value
+ *  @return					The number of bytes extracted
+ */
+static int get_int32( const char* chunkdata, int* value )
+{
+	*value = ntohl( *( (const int*) chunkdata ) );	/* host byte-order */
+
+	return sizeof( int );
+}
+
+#if	0
+/*------------------------------------------------------------------------
+ * Extract a 64-bit value from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param value			The 64-bit value
+ *  @return					The number of bytes extracted
+ */
+static int get_int64( const char* chunkdata, int64_t* value )
+{
+	*value = SWAP_64( *( (const int64_t*) chunkdata ) );	/* host byte-order */
+
+	return sizeof( int64_t );
+}
+#endif
+
+/*------------------------------------------------------------------------
+ * Copy a block of data from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param dest				Where to store the extract data
+ *  @param datalen			The length of the data to extract
+ *  @return					The number of bytes extracted
+ */
+static int get_data( const char* chunkdata, char* dest, int datalen )
+{
+	memcpy( dest, chunkdata, datalen );
+
+	return datalen;
+}
+
+/*------------------------------------------------------------------------
+ * Extract a UTF-8 encoded string from the chunked data.
+ *
+ *  @param chunkdata		The chunked-data buffer
+ *  @param str				A pointer to extracted string.  Must be g_free()'d.
+ *  @return					The number of bytes consumed
+ */
+static int get_utf8_string( const char* chunkdata, char* str, int maxstrlen )
+{
+	int		pos = 0;
+	short	len;
+	int		skip = 0;
+
+	/* string length [2 bytes] */
+	pos += get_int16( &chunkdata[pos], &len );
+
+	if ( len > maxstrlen ) {
+		/* possible buffer overflow */
+		purple_debug_error( MXIT_PLUGIN_ID, "Buffer overflow detected (get_utf8_string)\n" );
+		skip = len - maxstrlen;
+		len = maxstrlen;
+	}
+
+	/* string data */
+	pos += get_data( &chunkdata[pos], str, len );
+	str[len] = '\0';		/* terminate string */
+
+	return pos + skip;
+}
+
+
+/*========================================================================================================================
+ * Chunked Data encoding
+ */
+
+/*------------------------------------------------------------------------
+ * Encode a "reject file" chunk.  (Chunk type 7)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param fileid			A unique ID that identifies this file
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_reject( char* chunkdata, const char* fileid )
+{
+	int		pos		= 0;
+
+	/* file id [8 bytes] */
+	pos += add_data( &chunkdata[pos], fileid, MXIT_CHUNK_FILEID_LEN );
+
+	/* rejection reason [1 byte] */
+	pos += add_int8( &chunkdata[pos], REJECT_BY_USER );
+
+	/* rejection description [UTF-8 (optional)] */
+	pos += add_utf8_string( &chunkdata[pos], "" );
+
+	return pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Encode a "get file" request chunk.  (Chunk type 8)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param fileid			A unique ID that identifies this file
+ *  @param filesize			The number of bytes to retrieve
+ *  @param offset			The start offset in the file
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_get( char* chunkdata, const char* fileid, int filesize, int offset )
+{
+	int		pos		= 0;
+
+	/* file id [8 bytes] */
+	pos += add_data( &chunkdata[pos], fileid, MXIT_CHUNK_FILEID_LEN );
+
+	/* offset [4 bytes] */
+	pos += add_int32( &chunkdata[pos], offset );
+
+	/* length [4 bytes] */
+	pos += add_int32( &chunkdata[pos], filesize );
+
+	return pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Encode a "received file" chunk.  (Chunk type 9)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param fileid			A unique ID that identifies this file
+ *  @param status			The status of the file transfer (see chunk.h)
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_received( char* chunkdata, const char* fileid, unsigned char status )
+{
+	int		pos		= 0;
+
+	/* file id [8 bytes] */
+	pos += add_data( &chunkdata[pos], fileid, MXIT_CHUNK_FILEID_LEN );
+
+	/* status [1 byte] */
+	pos += add_int8( &chunkdata[pos], status );
+
+	return pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Encode a "send file direct" chunk.  (Chunk type 10)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param username			The username of the recipient
+ *  @param filename			The name of the file being sent
+ *  @param data				The file contents
+ *  @param datalen			The size of the file contents
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_senddirect( char* chunkdata, const char* username, const char* filename, const unsigned char* data, int datalen )
+{
+	int			pos		= 0;
+	const char*	mime	= NULL;
+
+	/* data length [4 bytes] */
+	pos += add_int32( &chunkdata[pos], datalen );
+
+	/* number of username(s) [2 bytes] */
+	pos += add_int16( &chunkdata[pos], 1 );
+
+	/* username(s) [UTF-8] */
+	pos += add_utf8_string( &chunkdata[pos], username );
+
+	/* filename [UTF-8] */
+	pos += add_utf8_string( &chunkdata[pos], filename );
+
+	/* file mime type [UTF-8] */
+	mime = file_mime_type( filename, (const char*) data, datalen );
+	pos += add_utf8_string( &chunkdata[pos], mime );
+
+	/* human readable description [UTF-8 (optional)] */
+	pos += add_utf8_string( &chunkdata[pos], "" );
+
+	/* crc [4 bytes] (0 = optional) */
+	pos += add_int32( &chunkdata[pos], 0 );
+
+	/* the actual file data */
+	pos += add_data( &chunkdata[pos], (const char *) data, datalen );
+
+	return pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Encode a "set avatar" chunk.  (Chunk type 13)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param data				The avatar data
+ *  @param datalen			The size of the avatar data
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_set_avatar( char* chunkdata, const unsigned char* data, int datalen )
+{
+	const char	fileid[MXIT_CHUNK_FILEID_LEN];
+	int			pos = 0;
+
+	/* id [8 bytes] */
+	memset( &fileid, 0, sizeof( fileid ) );		/* set to 0 for file upload */
+	pos += add_data( &chunkdata[pos], fileid, MXIT_CHUNK_FILEID_LEN );
+
+	/* size [4 bytes] */
+	pos += add_int32( &chunkdata[pos], datalen );
+
+	/* crc [4 bytes] (0 = optional) */
+	pos += add_int32( &chunkdata[pos], 0 );
+
+	/* the actual file data */
+	pos += add_data( &chunkdata[pos], (const char *) data, datalen );
+
+	return pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Encode a "get avatar" chunk.  (Chunk type 14)
+ *
+ *  @param chunkdata		Chunked-data buffer
+ *  @param mxitId			The username who's avatar to download
+ *  @param avatarId			The Id of the avatar image (as string)
+ *  @param imgsize			The resolution of the avatar image
+ *  @return					The number of bytes encoded in the buffer
+ */
+int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId, unsigned int imgsize )
+{
+	int			pos = 0;
+
+	/* number of avatars [4 bytes] */
+	pos += add_int32( &chunkdata[pos], 1 );
+
+	/* username [UTF-8] */
+	pos += add_utf8_string( &chunkdata[pos], mxitId );
+
+	/* avatar id [UTF-8] */
+	pos += add_utf8_string( &chunkdata[pos], avatarId );
+
+	/* avatar format [UTF-8] */
+	pos += add_utf8_string( &chunkdata[pos], MXIT_AVATAR_TYPE );
+
+	/* avatar bit depth [1 byte] */
+	pos += add_int8( &chunkdata[pos], MXIT_AVATAR_BITDEPT );
+
+	/* number of sizes [2 bytes] */
+	pos += add_int16( &chunkdata[pos], 1 );
+
+	/* image size [4 bytes] */
+	pos += add_int32( &chunkdata[pos], imgsize );
+
+	return pos;
+}
+
+
+/*========================================================================================================================
+ * Chunked Data decoding
+ */
+
+/*------------------------------------------------------------------------
+ * Parse a received "offer file" chunk.  (Chunk 6)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param offer			Decoded offerfile information
+ */
+void mxit_chunk_parse_offer( char* chunkdata, int datalen, struct offerfile_chunk* offer )
+{
+	int			pos			= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_offer (%i bytes)\n", datalen );
+
+	/* id [8 bytes] */
+	pos += get_data( &chunkdata[pos], offer->fileid, 8);
+
+	/* from username [UTF-8] */
+	pos += get_utf8_string( &chunkdata[pos], offer->username, sizeof( offer->username ) );
+	mxit_strip_domain( offer->username );
+
+	/* file size [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(offer->filesize) );
+
+	/* filename [UTF-8] */
+	pos += get_utf8_string( &chunkdata[pos], offer->filename, sizeof( offer->filename) );
+
+	/* mime type [UTF-8] */
+	/* not used by libPurple */
+
+	/* timestamp [8 bytes] */
+	/* not used by libPurple */
+
+	/* file description [UTF-8] */
+	/* not used by libPurple */
+
+	/* file alternative [UTF-8] */
+	/* not used by libPurple */
+
+	/* flags [4 bytes] */
+	/* not used by libPurple */
+}
+
+
+/*------------------------------------------------------------------------
+ * Parse a received "get file" response chunk.  (Chunk 8)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param offer			Decoded getfile information
+ */
+void mxit_chunk_parse_get( char* chunkdata, int datalen, struct getfile_chunk* getfile )
+{
+	int			pos			= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_file (%i bytes)\n", datalen );
+
+	/* id [8 bytes] */
+	pos += get_data( &chunkdata[pos], getfile->fileid, 8 );
+
+	/* offset [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(getfile->offset) );
+
+	/* file length [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(getfile->length) );
+
+	/* crc [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(getfile->crc) );
+
+	/* file data */
+	getfile->data = &chunkdata[pos];
+}
+
+
+/*------------------------------------------------------------------------
+ * Parse a received splash screen chunk.  (Chunk 2)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param splash			Decoded splash image information
+ */
+static void mxit_chunk_parse_splash( char* chunkdata, int datalen, struct splash_chunk* splash )
+{
+	int			pos			= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_splash (%i bytes)\n", datalen );
+
+	/* anchor [1 byte] */
+	pos += get_int8( &chunkdata[pos], &(splash->anchor) );
+
+	/* time to show [1 byte] */
+	pos += get_int8( &chunkdata[pos], &(splash->showtime) );
+
+	/* background color [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(splash->bgcolor) );
+
+	/* file data */
+	splash->data = &chunkdata[pos];
+
+	/* data length */
+	splash->datalen = datalen - pos;
+}
+
+
+/*------------------------------------------------------------------------
+ * Parse a received "custom resource" chunk.  (Chunk 1)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param offer			Decoded custom resource
+ */
+void mxit_chunk_parse_cr( char* chunkdata, int datalen, struct cr_chunk* cr )
+{
+	int			pos			= 0;
+	int			chunklen	= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_cr (%i bytes)\n", datalen );
+
+	/* id [UTF-8] */
+	pos += get_utf8_string( &chunkdata[pos], cr->id, sizeof( cr->id ) );
+
+	/* handle [UTF-8] */
+	pos += get_utf8_string( &chunkdata[pos], cr->handle, sizeof( cr->handle ) );
+
+	/* operation [1 byte] */
+	pos += get_int8( &chunkdata[pos], &(cr->operation) );
+
+	/* chunk size [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &chunklen );
+
+	/* parse the resource chunks */
+	while ( chunklen > 0 ) {
+		struct raw_chunk* chunkhdr = ( struct raw_chunk * ) &chunkdata[pos];
+		chunkhdr->length = ntohl( chunkhdr->length );		/* host byte-order */
+
+		/* start of chunk data */
+		pos += sizeof( struct raw_chunk );
+
+		switch ( chunkhdr->type ) {
+			case CP_CHUNK_SPLASH :			/* splash image */
+				{
+					struct splash_chunk* splash = g_new0( struct splash_chunk, 1 );
+
+					mxit_chunk_parse_splash( &chunkdata[pos], chunkhdr->length, splash );
+
+					cr->resources = g_list_append( cr->resources, splash );
+					break;
+				}
+			case CP_CHUNK_CLICK :			/* splash click */
+				{
+					struct splash_click_chunk* click = g_new0( struct splash_click_chunk, 1 );
+					
+					cr->resources = g_list_append( cr->resources, click );
+					break;
+				}
+			default:
+				purple_debug_info( MXIT_PLUGIN_ID, "Unsupported custom resource chunk received (%i)\n", chunkhdr->type );
+		}
+
+		/* skip over data to next resource chunk */
+		pos += chunkhdr->length;
+		chunklen -= ( sizeof( struct raw_chunk ) + chunkhdr->length );
+	}
+}
+
+
+/*------------------------------------------------------------------------
+ * Parse a received "get avatar" response chunk.  (Chunk 14)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param avatar			Decoded avatar information
+ */
+void mxit_chunk_parse_get_avatar( char* chunkdata, int datalen, struct getavatar_chunk* avatar )
+{
+	int			pos			= 0;
+	int			numfiles	= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_get_avatar (%i bytes)\n", datalen );
+
+	/* number of files [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &numfiles );
+
+	if ( numfiles < 1 )		/* no data */
+		return;
+
+	/* mxitId [UTF-8 string] */
+	pos += get_utf8_string( &chunkdata[pos], avatar->mxitid, sizeof( avatar->mxitid ) );
+
+	/* avatar id [UTF-8 string] */
+	pos += get_utf8_string( &chunkdata[pos], avatar->avatarid, sizeof( avatar->avatarid ) );
+
+	/* format [UTF-8 string] */
+	pos += get_utf8_string( &chunkdata[pos], avatar->format, sizeof( avatar->format ) );
+
+	/* bit depth [1 byte] */
+	pos += get_int8( &chunkdata[pos], &(avatar->bitdepth) );
+
+	/* crc [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(avatar->crc) );
+
+	/* width [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(avatar->width) );
+
+	/* height [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(avatar->height) );
+
+	/* file length [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(avatar->length) );
+
+	/* file data */
+	avatar->data = &chunkdata[pos];
+}