/* -----------------------------------------------------------------------------

  fast conversions from/to "base64" strings
  for free distribution
  (c) Alexander Strumensky

  may be compiled on any platform
  
  MD5 algoritm 
  adaptation 
  (c) Dmirty Osipov
  ------------------------------------------------------------------------------
  contents:

  unsigned  str_to_base64(unsigned InBufLen, char *InBuf, unsigned OutBufLen, char *OutBuf)

  description:
    encodes InBuf string to "base64". if OutBuf omitted or smaller size,
    returns size, required for store result and puts result into OutBuf,
    else returns actual number of bytes copyed.


  unsigned  base64_to_str(unsigned InBufLen, char *InBuf, unsigned OutBufLen, char *OutBuf)

  description:
    decodes InBuf string from "base64". if OutBuf omitted or smaller size,
    returns size, required for store result and puts result into OutBuf,
    else returns actual number of bytes copyed.


  int       base64char2value(char C)

  description:
    converts valid input char [A-Za-z0-9+/] from "base64" to actual
    binary value (0..63). if input char is not valid "base64" char,
    then returns -1


  char      base64value2char(unsigned Val)

  description:
    converts valid input value (0..63) to "base64" character [A-Za-z0-9+/].
    if input value is out of range, then returns '=' (clarion VAL('=')).

  ------------------------------------------------------------------------------
  Clarion for windows prototypes (if compiled by Clarion IDE):

  MODULE('smtp.c')
    str_to_base64(*STRING InBuf, <*STRING OutBuf>),UNSIGNED,NAME('_str_to_base64')
    base64_to_str(*STRING InBuf, <*STRING OutBuf>),UNSIGNED,NAME('_base64_to_str')
    base64char2value(BYTE InputCar),SIGNED,NAME('_base64char2value')
    base64value2char(UNSIGNED InputVal),BYTE,NAME('_base64value2char')
 !Added for MD5 algoritm
    md5(*CSTRING _InBuf,ULONG _LenInBuf,long _AddressOutBuf),RAW,NAME('_MDString')
    hmac_md5(*CSTRING _InBuf,ULONG _LenInBuf,*CSTRING _Key,ULONG _LenKey,long _AddressOutBuf),RAW,NAME('_hmac_md5')
  END

  
  ------------------------------------------------------------------------------
  notes:

    Sometimes Clarion C/C++ compiler produces better code for following construction

      *OutBuf = <some_code>; OutBuf++;

    then for following

      *(OutBuf++) = <some_code>;


*/

#ifdef __cplusplus
extern "C" {
#endif
/* Base64 ---------------------------------------------------------------*/
static char to_base64[64] =
{
  'A','B','C','D','E','F','G','H','I','J','K','L','M',
  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
  'a','b','c','d','e','f','g','h','i','j','k','l','m',
  'n','o','p','q','r','s','t','u','v','w','x','y','z',
  '0','1','2','3','4','5','6','7','8','9','+','/'
};

static char from_base64[] =
{
  62, -1, -1, -1, 63,
  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -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
};
/* Base64 ---------------------------------------------------------------*/
/*MD5--------------------------------------------------------------------*/

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
typedef unsigned char *POINTER;
typedef unsigned short int UINT2;
typedef unsigned long int UINT4;
typedef struct {
  UINT4 state[4];    
  UINT4 count[2];  
  unsigned char buffer[64]; 
} MD5_CTX;

static void MD5Transform (UINT4 [4], unsigned char [64]);
static void Encode(unsigned char *, UINT4 *, unsigned int);
static void Decode(UINT4 *, unsigned char *, unsigned int);
static void MD5_memcpy(POINTER, POINTER, unsigned int);
static void MD5_memset(POINTER, int, unsigned int);
static unsigned char PADDING[64] = {
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static unsigned char digest[16];
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
#define FF(a, b, c, d, x, s, ac) { \
 (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define GG(a, b, c, d, x, s, ac) { \
 (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define HH(a, b, c, d, x, s, ac) { \
 (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define II(a, b, c, d, x, s, ac) { \
 (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
void MD5Init (MD5_CTX *context)
{
  context->count[0] = context->count[1] = 0;
  context->state[0] = 0x67452301;
  context->state[1] = 0xefcdab89;
  context->state[2] = 0x98badcfe;
  context->state[3] = 0x10325476;
}
void MD5Update (MD5_CTX *context,unsigned char *input, unsigned int inputLen)
{
  unsigned int i, index, partLen;
  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
  if ((context->count[0] += ((UINT4)inputLen << 3))
   < ((UINT4)inputLen << 3))
  context->count[1]++;
  context->count[1] += ((UINT4)inputLen >> 29);
  partLen = 64 - index;
  if (inputLen >= partLen) {
   MD5_memcpy
    ((POINTER)&context->buffer[index], (POINTER)input, partLen);
   MD5Transform (context->state, context->buffer);
   for (i = partLen; i + 63 < inputLen; i += 64)
    MD5Transform (context->state, &input[i]);
   index = 0;
   }
  else
   i = 0;
   MD5_memcpy
   ((POINTER)&context->buffer[index], (POINTER)&input[i],
   inputLen-i);
 }
void MD5Final (unsigned char digest[16], MD5_CTX *context)
{
  unsigned char bits[8];
  unsigned int index, padLen;
  Encode (bits, context->count, 8);
  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
  padLen = (index < 56) ? (56 - index) : (120 - index);
  MD5Update (context, PADDING, padLen);
  MD5Update (context, bits, 8);
  Encode (digest, context->state, 16);
  MD5_memset ((POINTER)context, 0, sizeof (*context));
}
static void MD5Transform (UINT4 state[4], unsigned char block[64])
{
  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
  Decode (x, block, 64);
  FF (a, b, c, d, x[ 0], S11, 0xd76aa478);
  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); 
  FF (c, d, a, b, x[ 2], S13, 0x242070db); 
  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); 
  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); 
  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); 
  FF (c, d, a, b, x[ 6], S13, 0xa8304613); 
  FF (b, c, d, a, x[ 7], S14, 0xfd469501); 
  FF (a, b, c, d, x[ 8], S11, 0x698098d8); 
  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); 
  FF (c, d, a, b, x[10], S13, 0xffff5bb1); 
  FF (b, c, d, a, x[11], S14, 0x895cd7be); 
  FF (a, b, c, d, x[12], S11, 0x6b901122); 
  FF (d, a, b, c, x[13], S12, 0xfd987193); 
  FF (c, d, a, b, x[14], S13, 0xa679438e); 
  FF (b, c, d, a, x[15], S14, 0x49b40821); 
  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); 
  GG (d, a, b, c, x[ 6], S22, 0xc040b340); 
  GG (c, d, a, b, x[11], S23, 0x265e5a51); 
  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); 
  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); 
  GG (d, a, b, c, x[10], S22,  0x2441453); 
  GG (c, d, a, b, x[15], S23, 0xd8a1e681); 
  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); 
  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); 
  GG (d, a, b, c, x[14], S22, 0xc33707d6); 
  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); 
  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); 
  GG (a, b, c, d, x[13], S21, 0xa9e3e905); 
  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); 
  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); 
  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); 
  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); 
  HH (d, a, b, c, x[ 8], S32, 0x8771f681); 
  HH (c, d, a, b, x[11], S33, 0x6d9d6122); 
  HH (b, c, d, a, x[14], S34, 0xfde5380c); 
  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); 
  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); 
  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); 
  HH (b, c, d, a, x[10], S34, 0xbebfbc70); 
  HH (a, b, c, d, x[13], S31, 0x289b7ec6); 
  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); 
  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); 
  HH (b, c, d, a, x[ 6], S34,  0x4881d05); 
  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); 
  HH (d, a, b, c, x[12], S32, 0xe6db99e5); 
  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); 
  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); 
  II (a, b, c, d, x[ 0], S41, 0xf4292244); 
  II (d, a, b, c, x[ 7], S42, 0x432aff97); 
  II (c, d, a, b, x[14], S43, 0xab9423a7); 
  II (b, c, d, a, x[ 5], S44, 0xfc93a039); 
  II (a, b, c, d, x[12], S41, 0x655b59c3); 
  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); 
  II (c, d, a, b, x[10], S43, 0xffeff47d); 
  II (b, c, d, a, x[ 1], S44, 0x85845dd1); 
  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); 
  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); 
  II (c, d, a, b, x[ 6], S43, 0xa3014314); 
  II (b, c, d, a, x[13], S44, 0x4e0811a1); 
  II (a, b, c, d, x[ 4], S41, 0xf7537e82); 
  II (d, a, b, c, x[11], S42, 0xbd3af235); 
  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); 
  II (b, c, d, a, x[ 9], S44, 0xeb86d391); 
  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
  MD5_memset ((POINTER)x, 0, sizeof (x));
}
static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
{
  unsigned int i, j;
  for (i = 0, j = 0; j < len; i++, j += 4) {
   output[j] = (unsigned char)(input[i] & 0xff);
   output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
   output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
   output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
  }
}
static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
{
  unsigned int i, j;
  for (i = 0, j = 0; j < len; i++, j += 4)
   output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
    (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}
static void MD5_memcpy (POINTER output, POINTER input, unsigned int len)
{
  unsigned int i;
  for (i = 0; i < len; i++)
   output[i] = input[i];
}
static void MD5_memset (POINTER output, int value, unsigned int len)
{
  unsigned int i;
  for (i = 0; i < len; i++)
   ((char *)output)[i] = (char)value;
}

unsigned char* MDString (unsigned char* string,unsigned int len)
{
  MD5_CTX context;
  MD5Init (&context);
  MD5Update (&context, string , len);
  MD5Final (digest, &context);
  return(digest);
}

/*
** Function: hmac_md5
*/


unsigned char* hmac_md5(unsigned char* text,unsigned int text_len,unsigned char* key,unsigned int key_len)
/*
  text;                 pointer to data stream
  text_len;             length of data stream
  key;                  pointer to authentication key
  key_len;              length of authentication key
  digest;               caller digest to be filled in
*/

{
        MD5_CTX context;
        unsigned char k_ipad[65];    /* inner padding -
                                      * key XORd with ipad
                                      */
        unsigned char k_opad[65];    /* outer padding -
                                      * key XORd with opad
                                      */
        unsigned char tk[16];
        int i;
        /* if key is longer than 64 bytes reset it to key=MD5(key) */
        if (key_len > 64) {

                MD5_CTX      tctx;

                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(tk, &tctx);

                key = tk;
                key_len = 16;
        }

        /*
         * the HMAC_MD5 transform looks like:
         *
         * MD5(K XOR opad, MD5(K XOR ipad, text))
         *
         * where K is an n byte key
         * ipad is the byte 0x36 repeated 64 times

         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */

        /* bzero( k_ipad, sizeof k_ipad);
           bzero( k_opad, sizeof k_opad);
           bcopy( key, k_ipad, key_len);
           bcopy( key, k_opad, key_len); */


        MD5_memset( k_ipad,0,sizeof k_ipad);
        MD5_memset( k_opad,0,sizeof k_opad);
        MD5_memcpy( k_ipad, key , key_len);
        MD5_memcpy( k_opad, key , key_len);

        /* XOR key with ipad and opad values */
        for (i=0; i<64; i++) {
                k_ipad[i] ^= 0x36;
                k_opad[i] ^= 0x5c;
        }
        /*
         * perform inner MD5
         */
        MD5Init(&context);                   /* init context for 1st
                                              * pass */
        MD5Update(&context, k_ipad, 64);     /* start with inner pad */
        MD5Update(&context, text, text_len); /* then text of datagram */
        MD5Final(digest, &context);          /* finish up 1st pass */
        /*
         * perform outer MD5
         */
        MD5Init(&context);                   /* init context for 2nd
                                              * pass */
        MD5Update(&context, k_opad, 64);     /* start with outer pad */
        MD5Update(&context, digest, 16);     /* then results of 1st
                                              * hash */
        MD5Final(digest, &context);          /* finish up 2nd pass */
        return(digest);
}
/*MD5--------------------------------------------------------------------*/

/* Base64 ---------------------------------------------------------------*/
int base64char2value(char C)
{
  unsigned Idx = (unsigned)C - '+';  // '+' - minimum ASCII code
  if (Idx < sizeof(from_base64))
    return from_base64[Idx];

  return -1;
}

char base64value2char(unsigned Val)
{
  if (Val < sizeof(to_base64))
    return to_base64[Val];

  return '=';
}

unsigned str_to_base64(unsigned InBufLen, char *InBuf, unsigned OutBufLen, char *OutBuf)
{
  unsigned Ln = 0,i,j;
  if (InBufLen)
  {
    Ln = (InBufLen - 1) / 3; Ln = (Ln + 1) * 4;
    if (OutBufLen >= Ln)
    if (OutBuf)
       do
      {
        unsigned C1, C2;
__cyclein:   /* Добавлено ДО */
        C1 = *(unsigned char*)InBuf;     InBuf++;

        *OutBuf = to_base64[(C1 >> 2) & 63];            OutBuf++;
        C2 = (C1 & 3) << 4;

        if (--InBufLen)
        {
          C1 = *(unsigned char*)InBuf;   InBuf++;

          *OutBuf = to_base64[((C1 >> 4) & 15) | C2 ];  OutBuf++;
          C2 = (C1 & 15) << 2;

          if (--InBufLen)
          {
            C1 = *(unsigned char*)InBuf; InBuf++;

            *OutBuf = to_base64[((C1 >> 6) & 3) | C2];  OutBuf++;
            *OutBuf = to_base64[C1 & 63];               OutBuf++;
            if (--InBufLen)
              goto __cyclein; /* Добавлено ДО (было continue)*/
          }
          else
          {
            *OutBuf = to_base64[C2];  OutBuf++;
            goto __WriteTerm;
          }
        }
        else
        {
          *OutBuf = to_base64[C2];    OutBuf++;
          *OutBuf = '=';              OutBuf++;
__WriteTerm:
          *OutBuf = '=';              OutBuf++;
        }
      }
      while (0);
  }
  return Ln;
}

unsigned base64_to_str(unsigned InBufLen, char *InBuf, unsigned OutBufLen, char *OutBuf)
{
  unsigned Ln = 0;
  if (InBufLen)
  if (!(InBufLen & 3))
  {
    Ln = (InBufLen >> 2) * 3;

    if (InBuf[InBufLen-1] == '=') Ln--;
    if (InBuf[InBufLen-2] == '=') Ln--;

    if (OutBufLen >= Ln)
    if (OutBuf)
    {
      OutBufLen = Ln;
      do
      {
        unsigned char C1, C2;

        C1 = base64char2value(*(InBuf++));  if (C1 == ((unsigned char)-1)) break;
        C2 = base64char2value(*(InBuf++));  if (C2 == ((unsigned char)-1)) break;
        *(OutBuf++) = ((C2 >>4) & 3) | (C1 << 2); 
        if (!--OutBufLen) break;
        C1 = base64char2value(*(InBuf++));  if (C1 == ((unsigned char)-1)) break;
        *(OutBuf++) = ((C2 & 15) << 4) | ((C1 >> 2) & 15); 
        if (!--OutBufLen) break;
        C2 = base64char2value(*(InBuf++));  if (C2 == ((unsigned char)-1)) break;
        *(OutBuf++) = ((C1 & 3 ) << 6) | C2  ;
      }
      while (--OutBufLen);

      Ln -= OutBufLen;
    }
  }
  return Ln;
}
/* Base64 ---------------------------------------------------------------*/
#ifdef __cplusplus
}
#endif






