/* base64.c 
 *
 * This code is based on code from base64.c from GnuPG, which is:
 *
 *	Copyright (C) 2001, 2003 Free Software Foundation, Inc.
 *
 * And from uuencode.c from Busybox, which is (C) Erik Anderson.
 *
 * Both projects are free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <glib.h>
#include <stdio.h>
#include <string.h>
#include "util.h"

/* The base-64 character list */
static unsigned char bintoasc[64] = 
       "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
       "abcdefghijklmnopqrstuvwxyz" 
       "0123456789+/"; 
/* The reverse base-64 list */
static unsigned char asctobin[256] = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
  0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
  0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
  0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff
};

gchar *base64_decode( const gchar *from ) {
    int idx = 0;
    unsigned char c, c2, val = '\0';
    int n = 0;
    unsigned char *buffer;
 
    buffer = g_new0(gchar, strlen(from));
    
    for (idx = 0, c = *from; c; c = *(++from), idx = (idx + 1) % 4) {
	if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
	    continue;
	
	if (c == '=') {		/* pad character: stop */
	    if (idx == 1)
		buffer[n++] = val;
	    break;
	}
    
	if ((c = asctobin[(c2 = c)]) == 255) {
	    g_warning("invalid radix64 character %02x skipped\n", c2);
	    continue;
	}
	switch (idx) {
	    case 0:
		val = c << 2;
		break;
	    case 1:
		val |= (c >> 4) & 3;
		buffer[n++] = val;
		val = (c << 4) & 0xf0;
		break;
	    case 2:
		val |= (c >> 2) & 15;
		buffer[n++] = val;
		val = (c << 6) & 0xc0;
		break;
	    case 3:
		val |= c & 0x3f;
		buffer[n++] = val;
		break;
	}
	
    }
    return buffer;
}

/*
 * Encode the string S of length LENGTH to base64 format and place it
 * to STORE.  STORE will be 0-terminated, and must point to a writable
 * buffer of at least 1+BASE64_LENGTH(length) bytes.
 * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
 */
gchar *base64_encode (const gchar *s, const int length)
{
	int i;
	unsigned char *buffer = g_new0(gchar, strlen(s));
	unsigned char *p = buffer;
    
	/* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
	for (i = 0; i < length; i += 3) {
		*p++ = bintoasc[s[0] >> 2];
		*p++ = bintoasc[((s[0] & 3) << 4) + (s[1] >> 4)];
		*p++ = bintoasc[((s[1] & 0xf) << 2) + (s[2] >> 6)];
		*p++ = bintoasc[s[2] & 0x3f];
		s += 3;
	}
	/* Pad the result if necessary...  */
	if (i == length + 1) {
		*(p - 1) = '=';
	}
	else if (i == length + 2) {
		*(p - 1) = *(p - 2) = '=';
	}
	/* ...and zero-terminate it.  */
	*p = '\0';
	return buffer;
}

