/*
 * Copyright (C) 2001, Shilad Sen, Sourcelight Technologies, Inc.
 * See xmlrpc.h or the README for more copyright information.
 */


#include <assert.h>
#include "xmlrpc.h"
#include "rpcInternal.h"

static  void            rpcBase64Dealloc(rpcBase64 *bp);
static  PyObject        *rpcBase64Repr(rpcBase64 *bp);
static  PyObject        *rpcBase64Str(rpcBase64 *bp);
static	PyObject	*rpcBase64GetAttr(rpcBase64 *bp, char *name);
static	int		rpcBase64SetAttr(
				rpcBase64 *bp,
				char *name,
				PyObject *pyval
			);



/****************************************************************
 *                                                              *
 *   The following code was ruthlessly stolen from binascii.c   *
 *   Jack Jansen is the listed author of the module             *
 *                                                              *
 ****************************************************************/


static	PyObject	*binascii_b2a_base64(PyObject *self, PyObject *args);
static	PyObject	*binascii_a2b_base64(PyObject *self, PyObject *args);


static PyObject *Error;

static char table_a2b_base64[] = {
	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
	-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
	52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */
	-1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
	15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
	-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
	41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};

#define BASE64_PAD '='
#define BASE64_MAXBIN 57	/* Max binary chunk size (76 char line) */

static unsigned char table_b2a_base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


static PyObject *
binascii_a2b_base64(self, args)
	PyObject *self;
        PyObject *args;
{
	unsigned char *ascii_data, *bin_data;
	int leftbits = 0;
	unsigned char this_ch;
	unsigned int leftchar = 0;
	int npad = 0;
	PyObject *rv;
	int ascii_len, bin_len;
	
	if ( !PyArg_ParseTuple(args, "t#", &ascii_data, &ascii_len) )
		return NULL;

	bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */

	/* Allocate the buffer */
	if ( (rv=PyString_FromStringAndSize(NULL, bin_len)) == NULL )
		return NULL;
	bin_data = (unsigned char *)PyString_AsString(rv);
	bin_len = 0;
	for( ; ascii_len > 0 ; ascii_len--, ascii_data++ ) {
		/* Skip some punctuation */
		this_ch = (*ascii_data & 0x7f);
		if ( this_ch == '\r' || this_ch == '\n' || this_ch == ' ' )
			continue;
		
		if ( this_ch == BASE64_PAD )
			npad++;
		this_ch = table_a2b_base64[(*ascii_data) & 0x7f];
		if ( this_ch == (unsigned char) -1 ) continue;
		/*
		** Shift it in on the low end, and see if there's
		** a byte ready for output.
		*/
		leftchar = (leftchar << 6) | (this_ch);
		leftbits += 6;
		if ( leftbits >= 8 ) {
			leftbits -= 8;
			*bin_data++ = (leftchar >> leftbits) & 0xff;
			leftchar &= ((1 << leftbits) - 1);
			bin_len++;
		}
	}
	/* Check that no bits are left */
	if ( leftbits ) {
		PyErr_SetString(Error, "Incorrect padding");
		Py_DECREF(rv);
		return NULL;
	}
	/* and remove any padding */
	bin_len -= npad;
	/* and set string size correctly */
	_PyString_Resize(&rv, bin_len);
	return rv;
}

	
static PyObject *
binascii_b2a_base64(self, args)
	PyObject *self;
        PyObject *args;
{
	unsigned char *ascii_data, *bin_data;
	int leftbits = 0;
	unsigned char this_ch;
	unsigned int leftchar = 0;
	PyObject *rv;
	int bin_len;
	
	if ( !PyArg_ParseTuple(args, "s#", &bin_data, &bin_len) )
		return NULL;
	
	/* We're lazy and allocate to much (fixed up later) */
	if ( (rv=PyString_FromStringAndSize(NULL, bin_len*2)) == NULL )
		return NULL;
	ascii_data = (unsigned char *)PyString_AsString(rv);

	for( ; bin_len > 0 ; bin_len--, bin_data++ ) {
		/* Shift the data into our buffer */
		leftchar = (leftchar << 8) | *bin_data;
		leftbits += 8;

		/* See if there are 6-bit groups ready */
		while ( leftbits >= 6 ) {
			this_ch = (leftchar >> (leftbits-6)) & 0x3f;
			leftbits -= 6;
			*ascii_data++ = table_b2a_base64[this_ch];
		}
	}
	if ( leftbits == 2 ) {
		*ascii_data++ = table_b2a_base64[(leftchar&3) << 4];
		*ascii_data++ = BASE64_PAD;
		*ascii_data++ = BASE64_PAD;
	} else if ( leftbits == 4 ) {
		*ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2];
		*ascii_data++ = BASE64_PAD;
	} 
	*ascii_data++ = '\n';	/* Append a courtesy newline */
	
	_PyString_Resize(&rv, (ascii_data -
			       (unsigned char *)PyString_AsString(rv)));
	return rv;
}

/****************************************************************
 *                                                              *
 *                    END OF STOLEN CODE                        *
 *                                                              *
 ****************************************************************/


/*
 * create a new edb base64 object
*/

PyObject *
rpcBase64New(PyObject *po)
{
	rpcBase64	*bp;

	bp = PyObject_NEW(rpcBase64, &rpcBase64Type);
	if (bp == NULL)
		return (NULL);
	Py_INCREF(po);
	bp->value = po;	
	return (PyObject *)bp;
}


char *
rpcBase64Encode(PyObject *str)
{
	PyObject	*arg,
			*pystr;
	char		*encstr;
	int		slen;

	arg = Py_BuildValue("(S)", str);
	unless(arg)
		return (NULL);
	pystr = binascii_b2a_base64(NULL, arg);
	Py_DECREF(arg);
	if (pystr == NULL)
		return (NULL);
	assert(PyString_Check(pystr));
	slen = PyString_GET_SIZE(pystr);
	encstr = alloc(sizeof(*str) * slen + 1);
	encstr[slen] = EOS;
	if (encstr == NULL)
		return NULL;
	memcpy(encstr, PyString_AS_STRING(pystr), slen);
	Py_DECREF(pystr);
	encstr[slen-1] = '\0';		/* remove newline (??FIX??) */

	return encstr;
}


PyObject *
rpcBase64Decode(PyObject *str)
{
	PyObject	*args,
			*ret;

	args = Py_BuildValue("(O)", str);
	if (args == NULL)
		return NULL;
	ret = binascii_a2b_base64(NULL, args);
	Py_DECREF(args);

	return ret;
}


/*
 * free resources associated with a base64 object
*/

static void
rpcBase64Dealloc(rpcBase64 *bp)
{
	if (bp->value) {
		Py_DECREF(bp->value);
	}
	PyMem_DEL(bp);
}


/*
 * represent a base64 xml object
*/
static PyObject *
rpcBase64Str(rpcBase64 *bp)
{
	Py_INCREF(bp->value);
	return bp->value;
}


/*
 * convert a base64 xml to a string object
*/
static PyObject *
rpcBase64Repr(rpcBase64 *bp)
{
	char		*buff;
	PyObject	*repr,
			*res;
	
	repr = PyObject_Repr(bp->value);
	if (repr == NULL)
		return (NULL);
	assert(PyString_Check(repr));
	buff = alloc(PyString_GET_SIZE(repr) + strlen("base64()") + 1);
	Py_DECREF(repr);
	sprintf(buff, "base64(%s)", PyString_AS_STRING(repr));
	res = PyString_FromString(buff);
	free(buff);
	return (res);
}


static PyMethodDef rpcBase64Methods[] = {
	{ NULL, NULL },
};


static	PyObject
*rpcBase64GetAttr(rpcBase64 *bp, char *name)
{
	assert(bp != NULL);
	assert(name != NULL);
	if (strcmp("data", name) == 0) {
		Py_INCREF(bp->value);
		return bp->value;
	}
	return Py_FindMethod(rpcBase64Methods, (PyObject *)bp, name);
}


static	int
rpcBase64SetAttr(rpcBase64 *bp, char *name, PyObject *value)
{
	assert(bp != NULL);
	assert(name != NULL);
	if (strcmp("data", name) == 0) {
		unless (PyString_Check(value)) {
			PyErr_SetString(PyExc_TypeError, "data must be string");
			return -1;
		}
		if (bp->value) {
			Py_DECREF(bp->value);
		}
		Py_INCREF(bp->value);
		bp->value = value;
		return 0;
	} else {
		PyErr_SetString(PyExc_AttributeError, "unknown attribute");
		return -1;
	}
	return 0;
}


/*
 * map characteristics of the base64 edb object 
*/
PyTypeObject rpcBase64Type = {
	PyObject_HEAD_INIT(0)
	0,
	"rpcBase64",
	sizeof(rpcBase64),
	0,
	(destructor)rpcBase64Dealloc,
	0,
	(getattrfunc)rpcBase64GetAttr,
	(setattrfunc)rpcBase64SetAttr,
	0,
	(reprfunc)rpcBase64Repr,
	0,
	0,
	0,
	0,
	0,
	(reprfunc)rpcBase64Str,
	0,
	0,
	0,
	0,
	0,
};


syntax highlighted by Code2HTML, v. 0.9.1