#include "common.h"
#include "base64.h"

char *base64Encode(char *input, size_t inplen)
/* Use base64 to encode a string.  Returns one long encoded
 * string which need to be freeMem'd. Note: big-endian algorithm.
 * For some applications you may need to break the base64 output
 * of this function into lines no longer than 76 chars.
 */
{
char b64[] = B64CHARS;
int words = (inplen+2)/3;
int remains = inplen % 3;
char *result = (char *)needMem(4*words+1);
size_t i=0, j=0;
int word = 0;
unsigned char *p = (unsigned char*) input;  
/* p must be unsigned char*,  because without "unsigned",
sign extend messes up last group outputted
when the value of the chars following last in input
happens to be char 0x80 or higher */
for(i=1; i<=words; i++)
    {
    word = 0;
    word |= *p++;
    word <<= 8;
    word |= *p++;
    word <<= 8;
    word |= *p++;
    if (i==words && remains>0)
	{
	word &= 0x00FFFF00;
    	if (remains==1)
    	    word &= 0x00FF0000;
	}
    result[j++]=b64[word >> 18 & 0x3F];
    result[j++]=b64[word >> 12 & 0x3F];
    result[j++]=b64[word >> 6 & 0x3F];
    result[j++]=b64[word & 0x3F];
    }
result[j] = 0;
if (remains >0) result[j-1] = '=';    
if (remains==1) result[j-2] = '=';    
return result;
}


boolean base64Validate(char *input)
/* Return true if input is valid base64.
 * Note that the input string is changed by 
 * eraseWhiteSpace(). */
{
size_t i = 0, l = 0;
char *p = input;
boolean validB64 = TRUE;

/* remove whitespace which is unnecessary and  */
eraseWhiteSpace(input);  

l = strlen(p);
for(i=0;i<l;i++)
    {
    char c = ' ';
    if (!strchr(B64CHARS,c=*p++))
        {
        if (c != '=')
            {
            validB64 = FALSE;
            break;
            }
        }
    }
if (l%4)
    validB64 = FALSE;
return validB64;
}

char *base64Decode(char *input, size_t *returnSize)
/* Use base64 to decode a string.  Return decoded
 * string which will be freeMem'd. Note: big-endian algorithm.
 * Call eraseWhiteSpace() and check for invalid input 
 * before passing in input if needed.  
 * Optionally set return size for use with binary data.
 */
{
static int *map=NULL;
char b64[] = B64CHARS;
size_t inplen = strlen(input);
int words = (inplen+3)/4;
char *result = (char *)needMem(3*words+1);
size_t i=0, j=0;
int word = 0;
char *p = input;

if (!map)
    {
    int i = 0;
    map = needMem(256*sizeof(int));
    for (i = 0; i < 256; ++i)
	{
	map[i]=0;
	}
    for (i = 0; i < 64; ++i)
	{
	map[(int)b64[i]]=i;
	}
    }
for(i=0; i<words; i++)
    {
    word = 0;
    word |= map[(int)*p++];
    word <<= 6;
    word |= map[(int)*p++];
    word <<= 6;
    word |= map[(int)*p++];
    word <<= 6;
    word |= map[(int)*p++];
    result[j++]=word >> 16 & 0xFF;
    result[j++]=word >> 8 & 0xFF;
    result[j++]=word & 0xFF;
    }
result[j] = 0;
if (returnSize)
    *returnSize = j;
     
return result;
}

