/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program 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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    This program 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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* midnightcode-base64.c :: Midnight Code base64 encode/decode library	*/

/* Includes software to support RFCs :: 1113 */


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>

#include "preferences.h"
#include "midnightcode-parse.h"
#include "midnightcode-base64.h"



int
midnightcode_base64_encode(unsigned char *string_dst,
                             unsigned char *binary_src,
                             int string_len, int binary_len) {
  unsigned char buffer[3];
  unsigned char * string_ptr;
  unsigned char * binary_ptr;
  unsigned char * buffer_ptr;
  int binary_offset;
  int string_offset;
  int padding;

  struct midnightcode_base64_lookup_int_t lookup_base64[] = {
    {  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 }
  };


  if(!string_dst)
    return -1;
  if(!binary_src)
    return -1;
  if(!binary_len)
    return -1;

  binary_offset = 0;
  string_offset = 0;
  string_ptr = string_dst;
  binary_ptr = binary_src;
  buffer_ptr = buffer;

  for(; (string_offset + 4 <= string_len) && (binary_offset < binary_len);
    binary_offset += 3, string_offset += 4) {
    padding = 3 - (binary_len - binary_offset);
    binary_ptr = (unsigned char *)(binary_src + binary_offset);
    memset(buffer, 0, 3);
    if(padding == 2) {
      memcpy(buffer, binary_ptr, 1);
    } else if(padding == 1) {
      memcpy(buffer, binary_ptr, 2);
    } else {
      memcpy(buffer, binary_ptr, 3);
    }

    string_ptr[string_offset] =
      lookup_base64[(((int)buffer_ptr[0] & 252) >> 2)].value;
    string_ptr[(string_offset + 1)] =
      lookup_base64[(((int)buffer_ptr[0] & 3) << 4) |
      (((int)buffer_ptr[1] & 240) >> 4)].value;
    if(padding == 2) {
      string_ptr[(string_offset + 2)] = 61;
    } else {
      string_ptr[(string_offset + 2)] =
        lookup_base64[(((int)buffer_ptr[1] & 15) << 2) |
        (((int)buffer_ptr[2] & 192) >> 6)].value;
    }
    if(padding == 2 || padding == 1) {
      string_ptr[(string_offset + 3)] = 61;
    } else {
      string_ptr[(string_offset + 3)] =
        lookup_base64[((int)buffer_ptr[2] & 63)].value;
    }
  }
  string_ptr[(string_offset + 4)] = '\0';


  return string_offset;
}



int
midnightcode_base64_decode(unsigned char *binary_dst,
                             unsigned char *string_src,
                             int binary_len, int string_len) {
  unsigned char buffer[4];
  unsigned char * string_ptr;
  unsigned char * binary_ptr;
  unsigned char * buffer_ptr;
  int binary_offset;
  int string_offset;
  int data_value;

  struct midnightcode_base64_lookup_int_t lookup_base64[] = {
    { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
    { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
    { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
    { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
    { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 },
    { -1 }, { -1 }, { -1 }, { 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 }, { -1 }, { -1 }, { -1 }, { -1 }, { -1 }
  };


  if(!binary_dst)
    return -1;
  if(!string_src)
    return -1;
  if(!string_len)
    return -1;
  if(string_len % 4 != 0)
    return -1;

  binary_offset = 0;
  string_offset = 0;
  string_ptr = string_src;
  binary_ptr = binary_dst;
  buffer_ptr = buffer;
  data_value = 0;

  for(; (binary_offset + 3 <= binary_len) && (string_offset < string_len);
    binary_offset += 3, string_offset += 4) {

    string_ptr = (unsigned char *)(string_src + string_offset);
    memset(buffer, 0, 4);


    if((int)string_ptr[0] < 43 || (int)string_ptr[0] > 122)
      return -1;
    if(lookup_base64[(int)string_ptr[0]].value == -1)
      return -1;
    buffer[0] = lookup_base64[(int)string_ptr[0]].value;
    if((int)string_ptr[1] < 43 || (int)string_ptr[1] > 122)
      return -1;
    if(lookup_base64[(int)string_ptr[1]].value == -1)
      return -1;
    buffer[1] = lookup_base64[(int)string_ptr[1]].value;

    if(string_ptr[2] != 61) {
      if((int)string_ptr[2] < 43 || (int)string_ptr[2] > 122)
        return -1;
      if(lookup_base64[(int)string_ptr[2]].value == -1)
        return -1;
      buffer[2] = lookup_base64[(int)string_ptr[2]].value;
      if(string_ptr[3] != 61) {
        if((int)string_ptr[3] < 43 || (int)string_ptr[3] > 122)
          return -1;
        if(lookup_base64[(int)string_ptr[3]].value == -1)
          return -1;
        buffer[3] = lookup_base64[(int)string_ptr[3]].value;
      }
    }

    binary_ptr[binary_offset] = (buffer_ptr[0] << 2) |
      ((buffer_ptr[1] & 48) >> 4);
    binary_ptr[(binary_offset + 1)] = ((buffer_ptr[1] & 15) << 4) |
      ((buffer_ptr[2] & 60) >> 2);
    binary_ptr[(binary_offset + 2)] = ((buffer_ptr[2] & 3) << 6) |
      buffer_ptr[3];

  }
  binary_ptr[(binary_offset + 3)] = '\0';

  if(string_ptr[3] == 61) {
    binary_offset--;
    if(string_ptr[2] == 61) {
      binary_offset--;
    }
  }


  return binary_offset;
}


