#include "AMMimeUtils.h"

#ifndef __FreeBSD__
    #include <malloc.h>
#endif/*__FreeBSD__*/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

////////// a small helper function, to do case insensitive search ////////

#define strupos(x, y) (strustr(x, y) != NULL ? strustr(x, y) - x : -1) //char version

char* _strupr(char *s)
{
	for (int i=0; i<strlen(s); i++)
		s[i] = toupper(s[i]);
	return (char*)s;
}

char* strustr(char *source, char *s)
{
  //make an uppercase copy af source and s
  char *csource = strdup(source);
  char *cs = strdup(s);
  _strupr(csource);
  _strupr(cs);
  //find cs in csource...
  char *result = strstr(csource, cs);
  if (result != NULL)
  {
    //cs is somewhere in csource
    int pos = (int)(result - csource);
    result = source;
    result += pos;
  }
  //clean up
  free(csource);
  free(cs);
  return result;
}


////////// The "real" decoding stuff //////////

const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#define SKIP '\202'
#define NOSKIP 'A'
#define MaxLineLength 76

const char base64map[] =
{
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,  SKIP,  SKIP,   SKIP,  SKIP,  SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,    62,   SKIP,   SKIP,   SKIP,    63,
      52,    53,    54,    55,    56,    57,    58,    59,
      60,    61,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    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,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    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,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

const char hexmap[] = {
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
      0 ,    1 ,    2 ,    3 ,    4 ,    5 ,    6 ,    7 ,
      8 ,    9 ,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    10,    11,    12,    13,    14,    15,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

const char QpEncodeMap[] = {
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   NOSKIP,   SKIP,   SKIP,   NOSKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   NOSKIP,   NOSKIP,
     SKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   NOSKIP,
     SKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

CBase64Utils::CBase64Utils()
{
  ErrorCode = 0;
}

CBase64Utils::~CBase64Utils()
{
}

char* CBase64Utils::Encode(char *input, int bufsize)
{
  int alsize = ((bufsize * 4) / 3);
  char *finalresult = (char*)calloc(alsize + ((alsize / 76) * 2) + (10 * sizeof(char)), sizeof(char));
  int count = 0;
  int LineLen = 0;
  char* fresult = finalresult;
  char *s = input;
  int tmp = 0;
  //let's step through the buffer and encode it...
  while (count <= bufsize)
  {
    if (count % 3 == 0 && count != 0)
    {
      tmp >>= 8;
      tmp &= 0xFFFFFF;
      //we have 4 new b64 chars, add them to finalresult
      int mid = tmp;
      mid >>= 18;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
      LineLen++;
      mid = tmp;
      mid >>= 12;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
      LineLen++;
      mid = tmp;
      mid >>= 6;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
      LineLen++;
      mid = tmp;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
      LineLen++;
      //reset tmp
      tmp = 0;
      //should we break the line...
      if (LineLen >= MaxLineLength)
      {
        *(fresult++) = '\r';
        *(fresult++) = '\n';
        LineLen = 0;
      }
      if (bufsize - count < 3)
        break;
    }
    unsigned char mid = (256 - (0 - *s));
    tmp |= mid;
    tmp <<= 8;
    count++;
    s++;
  }
  //do we have some chars left...
  int rest = (bufsize - count) % 3;
  if (rest != 0)
  {
    tmp = 0;
    int i;
    for (i = 0; i < 3; i++)
    {
      if (i < rest)
      {
        unsigned char mid = (256 - (0 - *s));
        tmp |= mid;
        tmp |= *s;
        tmp <<= 8;
        count++;
        s++;
      }
      else
      {
        tmp |= 0;
        tmp <<= 8;
      }
    }
    tmp >>= 8;
    tmp &= 0xFFFFFF;
    //we have some new b64 chars, add them to finalresult
    int mid = tmp;
    if (rest >= 1)
    {
      mid >>= 18;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
      mid = tmp;
      mid >>= 12;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
    }
    if (rest >= 2)
    {
      mid = tmp;
      mid >>= 6;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
    }
    if (rest >= 3)
    {
      mid = tmp;
      mid &= 0x3F;
      *(fresult++) = base64chars[mid];
    }
    for (int c = 3; c > rest; c--)
    {
      *(fresult++) = '=';
    }
  }
  return finalresult;
}

char* CBase64Utils::Decode(char *input, int *bufsize)
{
  int std = 0, count = 1, resultlen = 0;
  char *finalresult = (char*)calloc(*bufsize + sizeof(char), sizeof(char));
  char *s = input, *result = finalresult;
  while (*s != '=' && count <= *bufsize)
  {
    //check to see if it's a legal base64 char...
    while (base64map[*s] == SKIP)
    {
      if (*s != '\r' && *s != '\n')
      {
        //bad char...
        //we might want to tell the user that there was error in the encoded data...
        ErrorCode = 1;
      }
      s++;
      (*bufsize)--;
      if (count >= *bufsize)
      {
        break;
      }
    }
    //add the base64 char to std...
    std |= base64map[*(s++) & 0xFF];
    std <<= 6;
    if (count % 4 == 0) //we have 3 more real chars...
    {
      //put std in the next 3 chars in finalresult
      int tmp;
      std >>= 6;
      tmp = std;
      tmp >>= 16;
      tmp &= 0xFF;
      *(result++) = (tmp);
      tmp = std;
      tmp >>= 8;
      tmp &= 0xFF;
      *(result++) = (tmp);
      tmp = std;
      tmp &= 0xFF;
      *(result++) = (tmp);
      std = 0; //empty std
      resultlen += 3;
    }
    count++;
  }
  //find and decode the remaining chars, if any...
  count--;
  if (count % 4 != 0)
  {
    //we have some remaining chars, now decode them...
    for (int i = 0; i < 4 - (count % 4); i++)
    {
      std <<= 6;
      resultlen++;
    }
    int tmp;
    std >>= 6;
    tmp = std;
    tmp >>= 16;
    tmp &= 0xFF;
    *(result++) = (tmp);
    tmp = std;
    tmp >>= 8;
    tmp &= 0xFF;
    *(result++) = (tmp);
    tmp = std;
    tmp &= 0xFF;
    *(result++) = (tmp);
  }
  *bufsize = resultlen;
  return finalresult;
}

CQPUtils::CQPUtils()
{
  ErrorCode = 0;
}

CQPUtils::~CQPUtils()
{
}

char* CQPUtils::Decode(char *input)
{
  char *s = input;
  char *finalresult = (char*)calloc(strlen(input) + sizeof(char), sizeof(char));
  char *result = finalresult;
  while (*s != '\0') //loop through the entire string...
  {
    if (*s == '=') //woops, needs to be decoded...
    {
      for (int i = 0; i < 3; i++) //is s more than 3 chars long...
      {
        if (s[i] == '\0')
        {
          //error in the decoding...
          ErrorCode = 1;
          return finalresult;
        }
      }
      char mid[3];
      s++; //move past the "="
      //let's put the hex part into mid...
      bool ok = true;
      for (int i = 0; i < 2; i++)
      {
        if (hexmap[s[i]] == SKIP)
        {
          //we have an error, or a linebreak, in the encoding...
          ok = false;
          if (s[i] == '\r' && s[i + 1] == '\n')
          {
            s += 2;
            //*(result++) = '\r';
            //*(result++) = '\n';
            break;
          }
          else
          {
            //we have an error in the encoding...
            ErrorCode = 1;
            //s--;
          }
        }
        mid[i] = s[i];
      }
      //now we just have to convert the hex string to an char...
      if (ok)
      {
        s += 2;
        int m = hexmap[mid[0]];
        m <<= 4;
        m |= hexmap[mid[1]];
        *(result++) = m;
      }
    }
    else
    {
      if (*s != '\0') *(result++) = *(s++);
    }
  }

  return finalresult;
}

#define BufAdd 10 //the size that we expands the buffer with...

char* CQPUtils::ExpandBuffer(char *buffer, int UsedSize, int *BufSize, bool SingleChar)
{
  //should we expand the buffer now...
  int AddVal;
  if (SingleChar) AddVal = 3;
  else AddVal = 5;
  if (UsedSize >= *BufSize - AddVal)
  {
    //expand the buffer
    *BufSize += BufAdd;
    return (char*)realloc(buffer, *BufSize * sizeof(char));
  }
  return buffer;
}

char* CQPUtils::Encode(char *input)
{
  int BufSize = (int)strlen(input) + BufAdd; //size of the result buffer
  int UsedSize = 0; //used space in result buffer
  int LineLen = 0; //length of the current line...
  char *finalresult = (char*)calloc(BufSize, sizeof(char)); //the result buffer
  char *fresult = finalresult;
  char *s = input;
  while (*s != '\0')
  {
    //convert the signed char to an unsigned char...
    unsigned char mid = (256 - (0 - *s));
    //should we reset the linelength...
    if (*s == '\n')
      LineLen = 0; //we are starting on a new line...
    if (QpEncodeMap[mid] == SKIP)
    {
      //we need to encode this char...
      //is the line too long...
      if (LineLen >= MaxLineLength - 4)
      {
        //wrap the line...
        finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
        *(fresult++) = '=';
        *(fresult++) = '\r';
        *(fresult++) = '\n';
        UsedSize += 3;
        LineLen = 0;
      }
      //check buffersize...
      finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
      //add the hex value for the char...
      char mids[3];
		sprintf(mids, "%X", mid);
      //itoa(, mids, 16);
      //strupr(mids);
      *(fresult++) = '=';
      *(fresult++) = mids[0];
      *(fresult++) = mids[1];
      UsedSize += 3;
      LineLen += 2;
      s++;
    }
    else
    {
      //just add the char...
      //is the line too long...
      if (LineLen >= MaxLineLength - 4)
      {
        //wrap the line...
        finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
        *(fresult++) = '=';
        *(fresult++) = '\r';
        *(fresult++) = '\n';
        UsedSize += 3;
        LineLen = 0;
      }
      //check buffersize...
      finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize);
      UsedSize++;
      LineLen++;
      *(fresult++) = *(s++);
    }
  }
  *(fresult++) = '\0';
  return finalresult;
}

