/*
 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
 * 
 * The initial version of this code was written by Dragos Vingarzan
 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
 * Fruanhofer Institute. It was and still is maintained in a separate
 * branch of the original SER. We are therefore migrating it to
 * Kamailio/SR and look forward to maintaining it from here on out.
 * 2011/2012 Smile Communications, Pty. Ltd.
 * ported/maintained/improved by 
 * Jason Penton (jason(dot)penton(at)smilecoms.com and
 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
 * effort to add full IMS support to Kamailio/SR using a new and
 * improved architecture
 * 
 * NB: Alot of this code was originally part of OpenIMSCore,
 * FhG Fokus. 
 * Copyright (C) 2004-2006 FhG Fokus
 * Thanks for great work! This is an effort to 
 * break apart the various CSCF functions into logically separate
 * components. We hope this will drive wider use. We also feel
 * that in this way the architecture is more complete and thereby easier
 * to manage in the Kamailio/SR environment
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is 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
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 */
 
/**
 * \file
 * 
 * Serving-CSCF - Conversion between base16, base64 and base256
 * 
 *  \author Dragos Vingarzan vingarzan -at- fokus dot fraunhofer dot de
 * 
 */
 
/** base16 char constants */ 
char *hexchars="0123456789abcdef";
/**
 * Converts a binary encoded value to its base16 representation.
 * @param from - buffer containing the input data
 * @param len - the size of from
 * @param to - the output buffer  !!! must have at least len*2 allocated memory 
 * @returns the written length 
 */
int bin_to_base16(char *from,int len, char *to)
{
	int i,j;
	for(i=0,j=0;i<len;i++,j+=2){
		to[j] = hexchars[(((unsigned char)from[i]) >>4 )&0x0F];
		to[j+1] = hexchars[(((unsigned char)from[i]))&0x0F];
	}	
	return 2*len;
}

/** from base16 char to int */
#define HEX_DIGIT(x) \
	((x>='0'&&x<='9')?x-'0':((x>='a'&&x<='f')?x-'a'+10:((x>='A'&&x<='F')?x-'A'+10:0)))
/**
 * Converts a hex encoded value to its binary value
 * @param from - buffer containing the input data
 * @param len - the size of from
 * @param to - the output buffer  !!! must have at least len/2 allocated memory 
 * @returns the written length 
 */
int base16_to_bin(char *from,int len, char *to)
{
	int i,j;
	for(i=0,j=0;j<len;i++,j+=2){
		to[i] = (unsigned char) ( HEX_DIGIT(from[j])<<4 | HEX_DIGIT(from[j+1]));
	}	
	return i;
}

/**
 * Convert on character from base64 encoding to integer.
 *"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 * @param x - characted to convert
 * @returns the int value or -1 if terminal character ('=')
 */ 
static int base64_val(char x)\
{
	switch(x){
		case '=': return -1;
		case 'A': return 0;
		case 'B': return 1;
		case 'C': return 2;
		case 'D': return 3;
		case 'E': return 4;
		case 'F': return 5;
		case 'G': return 6;
		case 'H': return 7;
		case 'I': return 8;
		case 'J': return 9;
		case 'K': return 10;
		case 'L': return 11;
		case 'M': return 12;
		case 'N': return 13;
		case 'O': return 14;
		case 'P': return 15;
		case 'Q': return 16;
		case 'R': return 17;
		case 'S': return 18;
		case 'T': return 19;
		case 'U': return 20;
		case 'V': return 21;
		case 'W': return 22;
		case 'X': return 23;
		case 'Y': return 24;
		case 'Z': return 25;
		case 'a': return 26;
		case 'b': return 27;
		case 'c': return 28;
		case 'd': return 29;
		case 'e': return 30;
		case 'f': return 31;
		case 'g': return 32;
		case 'h': return 33;
		case 'i': return 34;
		case 'j': return 35;
		case 'k': return 36;
		case 'l': return 37;
		case 'm': return 38;
		case 'n': return 39;
		case 'o': return 40;
		case 'p': return 41;
		case 'q': return 42;
		case 'r': return 43;
		case 's': return 44;
		case 't': return 45;
		case 'u': return 46;
		case 'v': return 47;
		case 'w': return 48;
		case 'x': return 49;
		case 'y': return 50;
		case 'z': return 51;
		case '0': return 52;
		case '1': return 53;
		case '2': return 54;
		case '3': return 55;
		case '4': return 56;
		case '5': return 57;
		case '6': return 58;
		case '7': return 59;
		case '8': return 60;
		case '9': return 61;
		case '+': return 62;
		case '/': return 63;
	}
	return 0;
}

/**
 * Convert a string encoded in base64 to binary value.
 * @param from - buffer containing the input data
 * @param from_len - the size of from
 * @param to - the output buffer  !!! must have at least len*2 allocated memory 
 * @returns the written length 
 */
int base64_to_bin(char *from,int from_len, char *to)
{
	int i,j,x1,x2,x3,x4;

	for(i=0,j=0;i<from_len;i+=4){
		x1=base64_val(from[i]);
		x2=base64_val(from[i+1]);
		x3=base64_val(from[i+2]);
		x4=base64_val(from[i+3]);
		to[j++]=(x1<<2) | ((x2 & 0x30)>>4);
		if (x3==-1) break;
		to[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2);
		if (x4==-1) break;
		to[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F);
	}
	return j;
}

/** base64 characters constant */
char base64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
 * Convert a binary string to base64 encoding.
 * @param src - the source buffer
 * @param src_len - length of the source buffer
 * @param ptr - the destination buffer - must be allocated to at least src_len/3*4+4 
 * @returns the length of the resulted buffer 
 */
int bin_to_base64(char *src,int src_len,char* ptr)
{
	int i,k;
	int triplets,rest;
	char *s;
	s=ptr;

	triplets = src_len/3;
	rest = src_len%3;
	for(i=0;i<triplets*3;i+=3){
		k = (((unsigned char) src[i])&0xFC)>>2;
		*ptr=base64[k];ptr++;

		k = (((unsigned char) src[i])&0x03)<<4;
		k |=(((unsigned char) src[i+1])&0xF0)>>4;
		*ptr=base64[k];ptr++;

		k = (((unsigned char) src[i+1])&0x0F)<<2;
		k |=(((unsigned char) src[i+2])&0xC0)>>6;
		*ptr=base64[k];ptr++;

		k = (((unsigned char) src[i+2])&0x3F);
		*ptr=base64[k];ptr++;
	}
	i=triplets*3;
	switch(rest){
		case 0:
			break;
		case 1:
			k = (((unsigned char) src[i])&0xFC)>>2;
			*ptr=base64[k];ptr++;

			k = (((unsigned char) src[i])&0x03)<<4;
			*ptr=base64[k];ptr++;

			*ptr='=';ptr++;

			*ptr='=';ptr++;
			break;
		case 2:
			k = (((unsigned char) src[i])&0xFC)>>2;
			*ptr=base64[k];ptr++;

			k = (((unsigned char) src[i])&0x03)<<4;
			k |=(((unsigned char) src[i+1])&0xF0)>>4;
			*ptr=base64[k];ptr++;

			k = (((unsigned char) src[i+1])&0x0F)<<2;
			*ptr=base64[k];ptr++;

			*ptr='=';ptr++;
			break;
	}

	return ptr-s;
}

