/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/

//===[ Include files ]=====================================================

#include "internal.h"

//===[ Type definitions ]==================================================

//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// Tables for Base64 encoding and decoding
static const int8_t  g_Base64[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const uint8_t g_Expand64[256] =
{
    /* ASCII table */
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  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, 64, 64, 64, 64, 64,
    64, 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, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};


//++=======================================================================
CasaStatus
EncodeData(
   IN    const void *pData,
   IN    const uint32_t dataLen,
   INOUT char **ppEncodedData,
   INOUT uint32_t *pEncodedDataLen)
//
//  Arguments: 
//
//  Returns:   
//
//  Description:  
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus;
   uint32_t    encodedSize;
   char        *pTmp;

   DbgTrace(3, "-EncodeData- Start\n", 0);

   // Determine the encoded size and allocate a buffer to hold the encoded data
   encodedSize = ((dataLen * 4 + 2) / 3) - (dataLen % 3 ) + 4;
   pTmp = (char*) malloc(encodedSize);
   *ppEncodedData = pTmp;
   if (*ppEncodedData)
   {
      uint8_t  *pOut, *pIn;
      uint32_t i;

      // Setup pointers to move through the buffers
      pIn = (uint8_t*) pData;
      pOut = (uint8_t*) *ppEncodedData;

      // Perform the encoding
      for (i = 0; i < dataLen - 2; i += 3)
      {
          *pOut++ = g_Base64[(pIn[i] >> 2) & 0x3F];
          *pOut++ = g_Base64[((pIn[i] & 0x3) << 4) |
                          ((int32_t)(pIn[i + 1] & 0xF0) >> 4)];
          *pOut++ = g_Base64[((pIn[i + 1] & 0xF) << 2) |
                          ((int32_t)(pIn[i + 2] & 0xC0) >> 6)];
          *pOut++ = g_Base64[pIn[i + 2] & 0x3F];
      }
      if (i < dataLen)
      {
          *pOut++ = g_Base64[(pIn[i] >> 2) & 0x3F];
          if (i == (dataLen - 1))
          {
              *pOut++ = g_Base64[((pIn[i] & 0x3) << 4)];
              *pOut++ = '=';
          }
          else
          {
              *pOut++ = g_Base64[((pIn[i] & 0x3) << 4) |
                              ((int32_t)(pIn[i + 1] & 0xF0) >> 4)];
              *pOut++ = g_Base64[((pIn[i + 1] & 0xF) << 2)];
          }
          *pOut++ = '=';
      }
      *pOut++ = '\0';

      // Return the encoded data length
      *pEncodedDataLen = (int32_t)(pOut - (uint8_t*)*ppEncodedData); 

      // Success
      retStatus = CASA_STATUS_SUCCESS;
   }
   else
   {
      DbgTrace(0, "-EncodeData- Buffer allocation failure\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_INSUFFICIENT_RESOURCES);
   }

   DbgTrace(3, "-EncodeData- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
CasaStatus
DecodeData(
   IN    const char *pEncodedData,
   IN    const uint32_t encodedDataLen, // Does not include NULL terminator
   INOUT void **ppData,
   INOUT uint32_t *pDataLen)
//
//  Arguments: 
//
//  Returns:   
//
//  Description:  
//
// L2
//=======================================================================--
{
   CasaStatus  retStatus;
   uint32_t    i, j;
   size_t      decodedSize;

   DbgTrace(3, "-DecodeData- Start\n", 0);

   // Determine the decoded size
   for (i = 0, j = 0; i < encodedDataLen; i++)
       if (g_Expand64[((uint8_t*) pEncodedData)[i]] < 64)
           j++;
   decodedSize = (j * 3 + 3) / 4;

   // Verify that we are not going to overflow the uint32
   if (decodedSize <= UINT32_MAX)
   {
      // Allocate buffer to hold the decoded data
      *ppData = malloc(decodedSize);
      if (*ppData)
      {
         bool  endReached = false;
         uint8_t  c0, c1, c2, c3;
         uint8_t  *p, *q;

         // Initialize parameters that will be used during the decode operation
         c0 = c1 = c2 = c3 = 0;
         p = (uint8_t*) pEncodedData;
         q = (uint8_t*) *ppData;

         // Decode the data
         //
         // Loop through the data, piecing back information. Any newlines, and/or
         // carriage returns need to be skipped.
         while (j > 4)
         {
             while ((64 == g_Expand64[*p]) && (('\n' == *p) || ('\r' == *p)))
                 p++;
             if (64 == g_Expand64[*p])
             {
                 endReached = true;
                 break;
             }
             c0 = *(p++);

             while ((64 == g_Expand64[*p]) && (('\n' == *p) || ('\r' == *p)))
                 p++;
             if (64 == g_Expand64[*p])
             {
                 *(q++) = (uint8_t)(g_Expand64[c0] << 2);
                 j--;
                 endReached = true;
                 break;
             }
             c1 = *(p++);

             while ((64 == g_Expand64[*p]) && (('\n' == *p) || ('\r' == *p)))
                 p++;
             if (64 == g_Expand64[*p])
             {
                 *(q++) = (uint8_t)(g_Expand64[c0] << 2 | g_Expand64[c1] >> 4);
                 *(q++) = (uint8_t)(g_Expand64[c1] << 4);
                 j -= 2;
                 endReached = true;
                 break;
             }
             c2 = *(p++);

             while ((64 == g_Expand64[*p]) && (('\n' == *p) || ('\r' == *p)))
                 p++;
             if (64 == g_Expand64[*p])
             {
                 *(q++) = (uint8_t)(g_Expand64[c0] << 2 | g_Expand64[c1] >> 4);
                 *(q++) = (uint8_t)(g_Expand64[c1] << 4 | g_Expand64[c2] >> 2);
                 *(q++) = (uint8_t)(g_Expand64[c2] << 6);
                 j -= 3;
                 endReached = true;
                 break;
             }
             c3 = *(p++);

             *(q++) = (uint8_t)(g_Expand64[c0] << 2 | g_Expand64[c1] >> 4);
             *(q++) = (uint8_t)(g_Expand64[c1] << 4 | g_Expand64[c2] >> 2);
             *(q++) = (uint8_t)(g_Expand64[c2] << 6 | g_Expand64[c3]);
             j -= 4;
         }
         if (!endReached)
         {
             if (j > 1)
                 *(q++) = (uint8_t)(g_Expand64[*p] << 2 | g_Expand64[p[1]] >> 4);
             if (j > 2)
                 *(q++) = (uint8_t)(g_Expand64[p[1]] << 4 | g_Expand64[p[2]] >> 2);
             if (j > 3)
                 *(q++) = (uint8_t)(g_Expand64[p[2]] << 6 | g_Expand64[p[3]]);
         }

         // Return the length of the decoded data
         *pDataLen = (int32_t)(q - (uint8_t*)*ppData);

         // Success
         retStatus = CASA_STATUS_SUCCESS;
      }
      else
      {
         DbgTrace(0, "-DecodeData- Buffer allocation failure\n", 0);

         retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                     CASA_FACILITY_AUTHTOKEN,
                                     CASA_STATUS_INSUFFICIENT_RESOURCES);
      }
   }
   else
   {
      DbgTrace(0, "-DecodeData- Prevented uint32 overflow\n", 0);

      retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR,
                                  CASA_FACILITY_AUTHTOKEN,
                                  CASA_STATUS_UNSUCCESSFUL);
   }

   DbgTrace(3, "-DecodeData- End, retStatus = %0X\n", retStatus);

   return retStatus;
}


//++=======================================================================
int
dtoul(
   IN    const char *cp,
   IN    const int len)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   n = 0;
   int   i;

   DbgTrace(2, "-dtoul- Start\n", 0);

   for (i = 0; i < len; i++, cp++)
   {
      // Verify that we are dealing with a valid digit
      if (*cp >= '0' && *cp <= '9')
      {
         n = 10 * n + (*cp - '0');
      }
      else
      {
         DbgTrace(0, "-dtoul- Found invalid digit\n", 0);
         break;
      }
   }
      
   DbgTrace(2, "-dtoul- End, result = %0X\n", n);

   return n;
}


//++=======================================================================
//++=======================================================================
//++=======================================================================