#include "config.h"

#include <sys/types.h>
#include <stdio.h>
#include <string.h>


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: base64.c,v 1.8 1997/01/18 16:13:20 root Exp $";
#endif

#if defined(HTTP) || defined(BROWSER) || defined(TEST)

char *strToBase64 (char *str);
char *base64ToStr (char *b64);
static int findBase64 (char c);
extern void *mallocw (unsigned nb);

#define NULLCHAR ((char *)0)

static char lkBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int
findBase64 (c)
char c;	
{
int k;
	for (k = 0; k < 64; k++)
		if (lkBase64[k] == c)
			return k;
	return -1;
}


/* returns a malloc'ed string which is the original string converted to
   base64, or NULLCHAR if cannot malloc, per RFC 1521 */
char *
strToBase64 (char *thestr)
{
char *b64, *out;
unsigned char *str = (unsigned char *)thestr;
int multiple, remainder, len;
int val;

	len = (int) strlen((char *)str);
	multiple = len / 3;
	remainder = len % 3;

	out = b64 = mallocw ((unsigned int)(((multiple + 1) * 4) + 1));
	if (out == NULLCHAR)
		return out;

	while (multiple--)	{
		out[0] = lkBase64[(str[0] >> 2) & 0x3f];
		val = (str[1] >> 4) & 0x0f;
		out[1] = lkBase64[val | ((str[0] << 4) & 0x30)];
		val = (str[1] << 2) & 0x3c;
		out[2] = lkBase64[val | ((str[2] >> 6) & 3)];
		out[3] = lkBase64[str[2] & 0x3f];

		out += 4;
		str += 3;
	}
	out[0] = 0;
	if (remainder != 0)	{
		out[0] = lkBase64[(str[0] >> 2) & 0x3f];
		out[1] = out[2] = out[3] = '=';
		out[4] = 0;
		if (remainder == 2) {
			val = (str[1] >> 4) & 0x0f;
			out[1] = lkBase64[val | ((str[0] << 4) & 0x30)];
			out[2] = lkBase64[(str[1] << 2) & 0x3c];
		} else {
			out[1] = lkBase64[(str[0] << 4) & 0x30];
		}
	}
	return (b64);
}


/* returns a malloc'ed string which is the original string converted from
   base64, per RFC 1521 - Returns a NULLCHAR if invalid characters
   found or memory cannot be allocated, otherwise the memory must
   STILL be freed. */
char *
base64ToStr (char *b64)
{
char *str, *out;
int multiple, remainder = 0, pad = 0, len;
int val, temp;

	len = (int) strlen ((char *)b64);
	multiple = len / 4;
	if (b64[len - 1] == '=')
		pad++;
	if (b64[len - 2] == '=')
		pad++;
	switch (pad)	{
		case 2:		remainder = 1;
				break;
		case 1:		remainder = 2;
				break;
		case 0:
		default:	break;
				
	}
	if (pad)
		multiple--;
	out = str = mallocw ((unsigned int)((multiple * 3) + 1));
	if (out == NULLCHAR)
		return out;

	while (multiple--)	{
		if ((temp = findBase64 (b64[0])) == -1)
			goto error;
		val = (temp << 2);			/*lint !e701 */
		if ((temp = findBase64 (b64[1])) == -1)
			goto error;
		out[0] = val | ((temp >> 4) & 3);	/*lint !e702 !e734 */
		val = (temp & 0x0f) << 4;
		if ((temp = findBase64 (b64[2])) == -1)
			goto error;
		out[1] = val | ((temp >> 2) & 0x0f);	/*lint !e702 !e734 */
		out[2] = (temp << 6);			/*lint !e701 !e734 */
		if ((temp = findBase64 (b64[3])) == -1)
			goto error;
		out[2] |= temp;				/*lint !e734 */
			
		out += 3;
		b64 += 4;
	}
	out[0] = 0;
	if (remainder != 0)	{
		if ((temp = findBase64 (b64[0])) == -1)
			goto error;
		val = (temp << 2);			/*lint !e701 */
		if ((temp = findBase64 (b64[1])) == -1)
			goto error;
		out[0] = val | ((temp >> 4) & 3);	/*lint !e702 !e734 */
		out[1] = 0;
		if (remainder == 2)	{
			if ((temp = findBase64 (b64[1])) == -1)
				goto error;
			val = (temp & 0x0f) << 4;
			if ((temp = findBase64 (b64[2])) == -1)
				goto error;
			out[1] = val | ((temp >> 2) & 0x0f);	/*lint !e702 !e734 */
			out[2] = 0;
		}
	}
	return (str);

error:
	str[0] = 0;		/*lint !e613 */
	return (str);
}

#ifdef TEST
void
main (argc, argv)
int argc;
char *argv[];
{
	if (argc != 3)
		exit (0);

	if (argv[1][0] == 't')
		printf ("Encoding '%s': '%s'\n", argv[2], strToBase64 ((unsigned char *)argv[2]));
	else
		printf ("Decoding '%s': '%s'\n", argv[2], base64ToStr ((unsigned char *)argv[2]));
}


#endif	/* TEST */

#endif	/* HTTP */
