/*********************************************/
/* scol v 4                                  */
/*                                           */
/* base64.cpp                                */
/*                                           */
/* implements base64 encoding and decoding   */
/*                                           */
/* by Loïc Berthelot, CryoNetworks, jul 2001 */
/*********************************************/

//
// Modifications History
//
// $ LB (11 / 07 / 2001) : move _base64_getEncodedSize and _base64_getDecodedSize from 
//                         internal body to external body
//
// $ LB (16/07/2001) : uncomment _6bits28bits function and add _8bits26bits function in internal body
//                     call the _6bits28bits conversion at the end of base64_encode
//                     call the _8bits26bits conversion at the beginning of base64_decode
//
// $ LB (08/11/2001) : - change the Mpushstrbloc to Mpushstrblocn at the end of SCbase64_decode function, cause
//                       the decoded string can contained '0' characters (the content may be binary, not only ascii)
//                     - change all (char*) to (unsigned char*), in order to be sure the process is correct
//





/*******************************************************************************************/
/*******************************************************************************************/
//
//                        C O N T E N T
//
//
//
//            I N T E R N A L     B O D Y                                                     
//                                                                                            
// /* void _getBinStr (char* dest, int nb) */ commented
//
// void _6bits28bits (unsigned char* dest, unsigned char* src, int nb)
//
// void _8bits26bits (unsigned char* dest, unsigned char* src, int nb)
//
// #define _BASE64_ENCODE_PACKET(dest, src) 
//
// #define _BASE64_DECODE_PACKET(dest, src)                                     
//
//
//
//            E X T E R N A L     B O D Y                                                     
//                                                                                            
// int _base64_getEncodedSize(int n)
//                     
// int _base64_getDecodedSize(int n)
//
// int base64_encode (unsigned char* dest, int destSize, const unsigned char* src, int srcSize)
//
// int SCbase64_encode (mmachine m)
//
// int base64_decode (unsigned char* dest, int* destSize, const unsigned char* src8, int srcSize)
//
// int SCbase64_decode (mmachine m)
//
// 
/*******************************************************************************************/
/*******************************************************************************************/







#include <stdlib.h>
#include <memory.h>
extern "C" 
{
#include "mmemory.h"
#include "./include/kernel.h"
}
#include "base64.h"






/**********************************************************************************************/
/*                                                                                            */
/*            I N T E R N A L     B O D Y                                                     */
/*                                                                                            */
/**********************************************************************************************/



/***************************************************************/
/* functions provided for debugging                            */
/*

/* put the number nb in binary (8 bits), in the string  */
/*
void _getBinStr (char* dest, int nb)
{
int dec;
char *ptr;
  
    for (dec=7, ptr=dest; dec >= 0; dec--, ptr++)
		*ptr = ((nb>>dec)&0x01) + 48;
	memcpy (dest+5, dest+4, 4);
	*(dest+4) = 32;
	*(ptr+1) = 	'\0';
}
*/

/*                                                                 */
/********************************************************************/





/************************************************************************/
/*                                                                      */
/* void _6bits28bits (unsigned char* dest, unsigned char* src, int nb)  */
/*                                                                      */
/* convert the ascii 6 bits to ascii 8 bits                             */
/*                                                                      */
/************************************************************************/
void _6bits28bits (unsigned char* dest, unsigned char* src, int nb)
{
 unsigned char* ptrDest;
 unsigned char* ptrSrc;
 int i;
 unsigned char car;

 
    for (i=0, ptrDest = dest, ptrSrc = src; i < nb; i++, ptrDest+=1, ptrSrc+=1)
	{
		car = *ptrSrc;

		if ((car >= 0) && (car <= 25))
			car+=65;
		else if ((car >= 26) && (car <= 51))
			car+=71;
		else if ((car >= 52) && (car <= 61))
			car-=4;
		else if (car == 62)
			car=43;
		else if (car == 63)
			car=47;
		else if (car == 64)
			car=61;

		*ptrDest = car;
	}
}






/************************************************************************/
/*                                                                      */
/* void _8bits26bits (unsigned char* dest, unsigned char* src, int nb)  */
/*                                                                      */
/* convert the ascii 8 bits to ascii 6 bits                             */
/*                                                                      */
/************************************************************************/
void _8bits26bits (unsigned char* dest, unsigned char* src, int nb)
{
unsigned  char* ptrDest;
 unsigned char* ptrSrc;
 int i;
 unsigned char car;

 
    for (i=0, ptrDest = dest, ptrSrc = src; i < nb; i++, ptrDest+=1, ptrSrc+=1)
	{
		car = *ptrSrc;


		if ((car >= 65) && (car <= 90))
			car-=65;
		else if ((car >= 97) && (car <= 122))
			car-=71;
		else if ((car >= 48) && (car <= 57))
			car+=4;
		else if (car == 43)
			car=62;
		else if (car == 47)
			car=63;
		else if (car == 61)
			car=64;

		*ptrDest = car;
	}

}







/************************************************************************/
/*                                                                      */
/* _BASE64_ENCODE_PACKET(dest, src)                                     */
/*                                                                      */
/* macro.                                                               */
/* encode the src string, into dest string, as base64 encoding          */
/*                                                                      */
/************************************************************************/
#define _BASE64_ENCODE_PACKET(dest, src)                                \
{                                                                       \
		*(dest)     = ((*(src))>>2)                            & 0x3F;  \
		*((dest)+1) = (((*(src))<<4)     | ((*((src)+1))>>4))  & 0x3F;  \
		*((dest)+2) = (((*((src)+1))<<2) | ((*((src)+2))>>6))  & 0x3F;  \
		*((dest)+3) = (*((src)+2))                             & 0x3F;  \
}





/************************************************************************/
/*                                                                      */
/* _BASE64_DECODE_PACKET(dest, src)                                     */
/*                                                                      */
/* macro.                                                               */
/* decode the src string, into dest string, as base64 decoding          */
/*                                                                      */
/************************************************************************/
#define _BASE64_DECODE_PACKET(dest, src)                                      \
{                                                                             \
	*(dest)     = (((*(src))<<2) & 0xFC)      | (((*((src)+1))>>4) & 0x03);   \
	*((dest)+1) = (((*((src)+1))<<4) & 0xF0)  |   (((*((src)+2))>>2) & 0x0F); \
	*((dest)+2) = (((*((src)+2))<<6) & 0xC0)  |    ((*((src)+3)) & 0x3F);     \
}

















/**********************************************************************************************/
/*                                                                                            */
/*            E X T E R N A L     B O D Y                                                     */
/*                                                                                            */
/**********************************************************************************************/
   







/************************************************************************/
/*                                                                      */
/* _base64_getEncodedSize                                               */
/*                                                                      */
/* gives the size of a base64 string in function of the number of       */
/* characters from the source string.                                   */ 
/*                                                                      */
/************************************************************************/
int _base64_getEncodedSize(int n)                     
{                
	if (n <= 0)
		return 0;

	if (n <= 3)                                        
		return 4;

	if ((n % 3) == 0)
		return (4 * n) / 3;

	return (4 * (n + 3 - (n % 3))) / 3;
}




/************************************************************************/
/*                                                                      */
/* _base64_getDecodedSize                                               */
/*                                                                      */
/* gives the size needed to decode a base64 string.                     */
/* n is the number of characters from the base64 string                 */ 
/*                                                                      */
/* !! you have to check that (n modulo 4) == 0  !!                      */
/*                                                                      */
/* !! be aware that this size is the needed size to decode. the size of */
/* string once decoded can be size, or size-1, or size-2.               */
/************************************************************************/
int _base64_getDecodedSize(int n)
{
	if (n <= 4)
		return 3;

	return (n * 3) / 4;
}









/**************************************************************************************************/
/*                                                                                                */
/* int base64_encode (unsigned char* dest, int* destSize, const unsigned char* src, int srcSize)  */
/*                                                                                                */
/* base64 encoding                                                                                */
/*                                                                                                */
/* returns 0 if success, non-zero code if fails                                                   */
/*                                                                                                */
/* destSize is the allocated memory size for result. If it's too small, then                      */
/* the function put the needed size in destSize, and returns a non-zero code.                     */
/*                                                                                                */
/**************************************************************************************************/
int base64_encode (unsigned char* dest, int* destSize, const unsigned char* src, int srcSize)
{
unsigned char* ptrDest;
unsigned char* ptrSrc;
unsigned char* buf;

int encodedSize = _base64_getEncodedSize(srcSize);
int modulo, completion;
int i;

	// the completion of encoded string depends on the size of the source
    if (srcSize >= 3)
	{
		 modulo = srcSize % 3;
		 if (modulo == 0)
			 completion = 0;
		 else completion = 3 - modulo;
	}
	else 
	{
		modulo = 3;
		completion = 3 - srcSize;
	}


	// check the source
	if ((src == NULL) || (srcSize == 0))
	{
		*destSize = 0;
		return BASE64_ERROR;
	}



	

	// check the size of destination buffer
	if ((dest == NULL) || ((*destSize) < encodedSize))
	{
		*destSize = encodedSize;
		return BASE64_ERROR;
	}

	// alloc memory for source copying
	if (modulo == 0)
		buf = (unsigned char*) malloc (srcSize * sizeof(unsigned char));
	else
		buf = (unsigned char*) malloc ((srcSize + completion) * sizeof(unsigned char));


	// copy the source
	memcpy (buf, src, srcSize);


	// complete the source with '0' if necessary
	for (i=0; i < completion; i++)
		*(buf+srcSize+i) = '\0';



	//encode all three-octets packet from the source
	for (i=0, ptrDest=dest, ptrSrc=buf; i < (srcSize+completion)/3; i++, ptrDest+=4, ptrSrc+=3)

		_BASE64_ENCODE_PACKET (ptrDest, ptrSrc);




	//replace unused codes by "=" (in 6bits ASCII <=> code 64)
    for (i=0, ptrDest -= completion; i < completion; i++, ptrDest++)
		*ptrDest = (unsigned char)64;


	free (buf);

	//convert ascii 6 bits to ascii 8 bits
	_6bits28bits (dest, dest, encodedSize);
    
	*destSize = encodedSize;
    
	return BASE64_SUCCESS;
}






/***************************************************/
/*                                                 */
/* int SCbase64_encode (mmachine m)                */
/*                                                 */
/* scol interface of _base64_encode function       */
/*                                                 */
/* fun [S] S                                       */
/***************************************************/
int SCbase64_encode (mmachine m)
{
int srcPtr, srcSize;
unsigned char* src;
unsigned char* dest;
int   destSize;
int k;



	//get the source string to encode
	srcPtr = MMpull (m)>>1;
	src = (srcPtr == NIL) ? NULL : (unsigned char*) MMstartstr(m, srcPtr);

	if (src == NULL)
	{
		MMechostr (MSKFOO, "base64 encoding error : source or destination NULL\n");
		return MMpush(m,NIL);
	}

	//get its size
	srcSize = MMsizestr (m, srcPtr);


	// alloc memory
	destSize = _base64_getEncodedSize(srcSize);


	dest = (unsigned char*) malloc ((destSize+1) * sizeof(unsigned char));


	//encoding
    if (base64_encode (dest, &destSize, src, srcSize) != BASE64_SUCCESS)
	{
		MMechostr (MSKFOO, "base64 encoding error : source or destination NULL\n");
		free (dest);
		return MMpush(m,NIL);
	}

	*(dest+destSize) = '\0';



	//push encoded result
	//use Mpushstrblocn, instead of Mpushstrbloc, 'cause dest may contain some '\0' characters.
    k = Mpushstrblocn(m, (char*)dest, destSize);

	free (dest);
	
	return k;
}






/***********************************************************************************************/
/*                                                                                             */
/* int base64_decode (unsignedchar* dest, int destSize, const unsignedchar* src, int srcSize)  */
/*                                                                                             */
/* returns 0 if success, non-zero code if fails                                                */
/***********************************************************************************************/
int base64_decode (unsigned char* dest, int* destSize, const unsigned char* src8, int srcSize)
{
unsigned char* destBuf;
unsigned char* destPtr;
unsigned char* src;
unsigned char* srcPtr;

int decodedSize, nbNullChars;
int i;



	// check the source
	if ((src8 == NULL) || (srcSize == 0))
	{
		*destSize = 0;
		return BASE64_ERROR;
	}

	// check that source has a correct length (for base64 decoding)
	if ((srcSize % 4) != 0)
	{
		*destSize = 0;
		return BASE64_NOT_A_BASE64_STRING;
	}



	//convert ascii 8 bits to 6 bits
	src = (unsigned char*) malloc (srcSize * sizeof(unsigned char));
	_8bits26bits (src, (unsigned char*)src8, srcSize);



	// count the number of '=' characters ('=' in 6bits ascii <=> code 64 (<==> in 8 bits : 61))
	nbNullChars = 0;
	if (*(src+(srcSize-1)) == 64)
		nbNullChars++;
	if (*(src+(srcSize-2)) == 64)
		nbNullChars++;



	// get the size needed to decode and alloc the buffer memory
	decodedSize = _base64_getDecodedSize (srcSize);


	destBuf = (unsigned char*) malloc ((decodedSize+1) * sizeof(unsigned char));
	decodedSize -= nbNullChars;


	// check the destination
	if ((dest == NULL) || (*destSize < decodedSize))
	{
		*destSize = decodedSize;
		if (src) free (src);
		if (destBuf) free (destBuf);
		return BASE64_ERROR;
	}

	// decoding
    for (i=0, destPtr = destBuf, srcPtr = (unsigned char*)src; i < (srcSize/4); i++, destPtr+=3, srcPtr+=4)

		_BASE64_DECODE_PACKET (destPtr, srcPtr);




	// copy the results (without '=' decoding)
	memcpy (dest, destBuf, decodedSize);

    
	if (src) free (src);
	if (destBuf) free (destBuf);

    *destSize = decodedSize;
	return BASE64_SUCCESS;
}








/***************************************************/
/*                                                 */
/* int SCbase64_decode (mmachine m)                */
/*                                                 */
/* scol interface of _base64_decode function       */
/*                                                 */
/* fun [S] S                                       */
/***************************************************/
int SCbase64_decode (mmachine m)
{
int srcPtr, srcSize;
unsigned char* src;
int destSize;
unsigned char* dest;
int k;



	// get the source parameter (the encoded string to decode... :)
	srcPtr = MMpull (m) >> 1;
	src = (srcPtr == NIL) ? NULL : (unsigned char*) MMstartstr(m,srcPtr);

	if (src == NULL)
	{
		MMechostr (MSKFOO, "\nbase64 decoding error : source or destination error");
		return MMpush (m, NIL);
	}

	// get the size
	srcSize = MMsizestr (m, srcPtr);


	// evaluate the size of decoded string
	destSize = _base64_getDecodedSize (srcSize);

	
	// alloc memory for result
	dest = (unsigned char*)malloc ((destSize+1) * sizeof(unsigned char));


	// decoding
    if ((k = base64_decode (dest, &destSize, src, srcSize)) != BASE64_SUCCESS)
	{
		MMechostr (MSKFOO, "\nbase64 decoding error : ");
		if (k == BASE64_NOT_A_BASE64_STRING)
			MMechostr (MSKFOO,  "the source string to decode is not a base64 string : its length must be (4*n)\n");
		else MMechostr (MSKFOO, "source or destination error\n");

		free (dest);
		return MMpush (m, NIL);
	}


	*(dest+destSize) = '\0';


	// result
	//k = Mpushstrbloc (m, dest);
	if ((k=Mpushstrblocn (m, (char*)dest, destSize))) { free (dest); return k;}

	free(dest);
	return 0;
}