/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  Unless required by applicable law or agreed to in writing, software distributed 
 *  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 *  language governing permissions and limitations under the License.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "base64.h"


const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

void base64_encode_triple(uint8 triple[3], char result[4])
{
    int tripleValue, i;

    tripleValue = triple[0];
    tripleValue *= 256;
    tripleValue += triple[1];
    tripleValue *= 256;
    tripleValue += triple[2];

    for (i=0; i<4; i++)
    {
		result[3-i] = BASE64_CHARS[tripleValue % 64];
		tripleValue /= 64;
    }
}

/**
 * encode an array of bytes using Base64 (RFC 3548)
 *
 * @param source the source buffer
 * @param sourcelen the length of the source buffer
 * @param target the target buffer
 * @param targetlen the length of the target buffer
 * @return 1 on success, 0 otherwise
 */
HT_API int base64_encode(uint8 *source, uint32 sourcelen, char *target, uint32 targetlen)
{
    /* check if the result will fit in the target buffer */
    if ((sourcelen+2)/3*4 > targetlen-1)
    {
		return 0;
	}
	
    /* encode all full triples */
    while (sourcelen >= 3)
    {
		base64_encode_triple(source, target);
		sourcelen -= 3;
		source += 3;
		target += 4;
    }

    /* encode the last one or two characters */
    if (sourcelen > 0)
    {
		uint8 temp[3];
		memset(temp, 0, sizeof(temp));
		memcpy(temp, source, sourcelen);
		base64_encode_triple(temp, target);
		target[3] = '=';

		if (sourcelen == 1)
		{
		    target[2] = '=';
		}
		
		target += 4;
    }

    /* terminate the string */
    target[0] = 0;

    return 1;
}

/**
 * decode base64 encoded data
 *
 * @param source the encoded data (zero terminated)
 * @param target pointer to the target buffer
 * @param targetlen length of the target buffer
 * @return length of converted data on success, -1 otherwise
 */
HT_API int base64_decode(const char *source, uint32 sourcelen, uint8 *target, uint32 targetlen) 
{
    const char *cur;
	const char *max_src;
    uint8 *dest, *max_dest;
    int d, dlast, phase;
    uint8 c;
    static int table[256] = {
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
        52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
    };

    d = dlast = phase = 0;
    dest = target;
    max_dest = dest+targetlen;
	max_src = source + sourcelen;

    for (cur = source; *cur != '\0' && cur<max_src && dest<max_dest; ++cur) 
    {
        if (*cur == '=')
        {
            dlast = phase = 0;
            continue;
        }
        
        d = table[(int)*cur];
        if (d != -1) 
        {
            switch(phase) 
            {
			case 0:
			    ++phase;
			    break;
			case 1:
			    c = (uint8)(((dlast << 2) | ((d & 0x30) >> 4)));
			    *dest++ = c;
			    ++phase;
			    break;
			case 2:
			    c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
			    *dest++ = c;
			    ++phase;
			    break;
			case 3:
			    c = (uint8)((((dlast & 0x03 ) << 6) | d));
			    *dest++ = c;
			    phase = 0;
			    break;
		    }
		    
            dlast = d;
        }
    }

    /* we decoded the whole buffer */
    if (*cur == '\0' || cur == max_src) 
    {
		return (int)(dest-target);
    }

    /* we did not convert the whole data, buffer was to small */
    return -1;
}





