/*
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
* Base64 encoding and decoding functions.
*
*/




/**
 @file
*/

#include "base64.h"

// Constants used by the Base64 algorithm
const TUint8 TBase64::iAlphabet[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
const TInt8 KImcvLookUpStartOffset = 43;
const TUint8 KImcvConvEquals = '=';
const TInt8 AsciiToBase64[80]=
	{
	 62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
	 57, 58, 59, 60, 61, -1, -1, -1, 64, -1,
	 -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,
	  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
	 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
	 -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
	 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
	};
const TInt8 Base64ToAscii[65]=
	{
	 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
	 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
	 85, 86, 87, 88, 89, 90, 97, 98, 99,100,
	101,102,103,104,105,106,107,108,109,110,
	111,112,113,114,115,116,117,118,119,120,
	121,122, 48, 49, 50, 51, 52, 53, 54, 55,
	 56, 57, 43, 47, 61
	};

EXPORT_C TBase64::TBase64() :
	iShiftStored(0),
	iMaskShiftStored(ESix)
	{
	}

EXPORT_C void TBase64::Initialize()
	{
	iMaskShiftStored = ESix;
	}

//Encodes data to Base64.
EXPORT_C HBufC8* TBase64::EncodeLC(const TDesC8& aSrcString)
	{
	// Allocate and clear destination string
	TInt destLen = ((aSrcString.Length() - 1) / 3 + 1) * 4;
	HBufC8* destString = HBufC8::NewMaxLC(destLen);
	TPtr8 destPtr = destString->Des();

	// Initialise variables
	const TUint8* srcStringPtr = aSrcString.Ptr();
	const TUint8* srcStringEnd = aSrcString.Length() + srcStringPtr;
	TUint8* destStringPtr = (TUint8*)destString->Ptr();
	TUint8* destStringPtrBase = destStringPtr;

	TInt character = 0;
	TUint8 encodedChar = 0;
	TInt charStorage = 0;
 	TInt maskShift = EZero;

	while (srcStringPtr <= srcStringEnd)
		{
		// maskShift is used as a char read counter
		if(maskShift == ESix)
			{
			// If the 3rd char read is also the last char then the while loop
			// is broken on the next check.
			if(srcStringPtr == srcStringEnd)
				srcStringPtr++;
			maskShift = EZero;
			character = 0;
			}
		else
			{
			if (srcStringPtr == srcStringEnd)
				character=0;
			else
				character = *srcStringPtr;

			srcStringPtr++;
			// Shifts charStorage ready for the next char
			charStorage = charStorage << 8;
			maskShift += ETwo;
			}
		charStorage = charStorage | character;
		// Shifts the mask to the correct bit location
		// Masks (AND's) the valid bits from charStorage
		// Shifts the valid bits into the low order 8bits
		// Converts to BASE64 char, Casts the result to an unsigned
		// char (which it should be ?....I hope)
		encodedChar = (TUint8)Base64ToAscii[((charStorage >> maskShift) & ESixBitMask)];
		*destStringPtr++ = encodedChar;
		}

	// Check for not enough chars and pad if required
	if (maskShift == EFour)
		{
		*destStringPtr++ = KImcvConvEquals;
		*destStringPtr++ = KImcvConvEquals;
		}
	else
		if (maskShift == ESix)
			*destStringPtr++ = KImcvConvEquals;

	destPtr.SetLength((TInt)(destStringPtr - destStringPtrBase));
	return destString;
	}

//Decodes Base64-encoded data.
EXPORT_C HBufC8* TBase64::DecodeLC(const TDesC8& aSrcString, TBool& aNeedMoreData)
	{
	// The source string must be a multiple of 4 bytes long
	if (aSrcString.Length() & 0x3)
		{
		User::Leave(KErrCorrupt);
		}

	TInt decodedInt = 0;
	TInt8 offsetChar = 0;
	TUint8 decodedChar = 0;

    // Find out how long the decoded string will be.
    TInt outLen = aSrcString.Length() / 4 * 3;
    if (aSrcString[aSrcString.Length() - 1] == '=')
        outLen--;
    if (aSrcString[aSrcString.Length() - 2] == '=')
        outLen--;

	// Construct output buffer.
	HBufC8* destString = HBufC8::NewMaxLC(outLen);
	TPtr8 destPtr = destString->Des();
	destPtr.Zero();

	// Initialise variables
	const TUint8* srcStringPtr = aSrcString.Ptr();
	const TUint8* srcStringEnd = aSrcString.Length() + srcStringPtr;
	TUint8* destStringPtr = (TUint8*)destString->Ptr();
	TUint8* destStringPtrBase = destStringPtr;
	TInt maskShift = iMaskShiftStored;
	TInt shiftStorage = iShiftStored;

	// Main character process loop
	while (srcStringPtr<srcStringEnd)
		{
		offsetChar = (TInt8)(*srcStringPtr - KImcvLookUpStartOffset);
		srcStringPtr++;

		// Check for valid B64 character
		if ((offsetChar >= 0) && (offsetChar < 80))
			{
			// Read in next character and B64 decode
			decodedInt = AsciiToBase64[offsetChar];

			// Exits when a PAD char is reached
			if(decodedInt == EPadChar)
				{
				destPtr.SetLength((TInt)(destStringPtr - destStringPtrBase));
				aNeedMoreData = EFalse;
				return destString;
				}

			// Ensures the first 2 chars of 4 are received before processing
			if(maskShift == ESix)
				maskShift = EFour;
			else
				{
				shiftStorage = shiftStorage << ESix;
				shiftStorage = shiftStorage | decodedInt;
				decodedChar = (TUint8)((shiftStorage >> maskShift) & EEightBitMask);

				if((maskShift -= ETwo) < EZero)
					maskShift = ESix;

				*destStringPtr++ = decodedChar;
				}
			shiftStorage = decodedInt;
			}
		}
	iShiftStored = shiftStorage;
	iMaskShiftStored = maskShift;

	destPtr.SetLength((TInt)(destStringPtr - destStringPtrBase));
	aNeedMoreData = maskShift < ESix;
	return destString;
	}
	
