/*
 * Copyright (c) 1997 Stanford University
 *
 * The use of this software for revenue-generating purposes may require a
 * license from the owners of the underlying intellectual property.
 *
 * Within that constraint, permission to use, copy, modify, and distribute
 * this software and its documentation for any purpose is hereby granted
 * without fee, provided that the above copyright notices and this permission
 * notice appear in all copies of the software and related documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define _POSIX_SOURCE
#include "t_defines.h"

static int
hexDigitToInt(c)
     char c;
{
  if(c >= '0' && c <= '9')
    return c - '0';
  else if(c >= 'a' && c <= 'f')
    return c - 'a' + 10;
  else if(c >= 'A' && c <= 'F')
    return c - 'A' + 10;
  else
    return 0;
}

/*
 * Convert a hex string to a string of bytes; return size of dst
 */
_TYPE( int )
t_fromhex(dst, src)
     register char *dst, *src;
{
  register char *chp = dst;
  register unsigned size = strlen(src);

  /* FIXME: handle whitespace and non-hex digits by setting size and src
     appropriately. */

  if(size % 2 == 1) {
    *chp++ = hexDigitToInt(*src++);
    --size;
  }
  while(size > 0) {
    *chp++ = (hexDigitToInt(*src) << 4) | hexDigitToInt(*(src + 1));
    src += 2;
    size -= 2;
  }
  return chp - dst;
}

/*
 * Convert a string of bytes to their hex representation
 */
_TYPE( char * )
t_tohex(dst, src, size)
     register char *dst, *src;
     register unsigned size;
{
   int notleading = 0;

   register char *chp = dst;
   if (size != 0) do {
      if(notleading || *src != '\0') {
	notleading = 1;
	sprintf(chp, "%.2x", * (unsigned char *) src);
	chp += 2;
      }
      ++src;
   } while (--size != 0);
   return dst;
}

static char b64table[] =
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";

/*
 * Convert a base64 string into raw byte array representation.
 */
_TYPE( int )
t_fromb64(dst, src)
     register char *dst, *src;
{
  unsigned char *a;
  char *loc;
  int i, j;
  unsigned int size;

  while(*src && (*src == ' ' || *src == '\t' || *src == '\n'))
      ++src;
  size = strlen(src);

  a = malloc((size + 1) * sizeof(unsigned char));
  if(a == (unsigned char *) 0)
    return -1;

  i = 0;
  while(i < size) {
    loc = strchr(b64table, src[i]);
    if(loc == (char *) 0)
      break;
    else
      a[i] = loc - b64table;
    ++i;
  }
  size = i;

  i = size - 1;
  j = size;
  while(1) {
    a[j] = a[i];
    if(--i < 0)
      break;
    a[j] |= (a[i] & 3) << 6;
    --j;
    a[j] = (unsigned char) ((a[i] & 0x3c) >> 2);
    if(--i < 0)
      break;
    a[j] |= (a[i] & 0xf) << 4;
    --j;
    a[j] = (unsigned char) ((a[i] & 0x30) >> 4);
    if(--i < 0)
      break;
    a[j] |= (a[i] << 2);

    a[--j] = 0;
    if(--i < 0)
      break;
  }

  while(a[j] == 0 && j <= size)
    ++j;

  memcpy(dst, a + j, size - j + 1);
  free(a);
  return size - j + 1;
}

/*
 * Convert a raw byte string into a null-terminated base64 ASCII string.
 */
_TYPE( char * )
t_tob64(dst, src, size)
     register char *dst, *src;
     register unsigned size;
{
  int c, pos = size % 3;
  unsigned char b0 = 0, b1 = 0, b2 = 0, notleading = 0;
  char *olddst = dst;

  switch(pos) {
  case 1:
    b2 = src[0];
    break;
  case 2:
    b1 = src[0];
    b2 = src[1];
    break;
  }

  while(1) {
    c = (b0 & 0xfc) >> 2;
    if(notleading || c != 0) {
      *dst++ = b64table[c];
      notleading = 1;
    }
    c = ((b0 & 3) << 4) | ((b1 & 0xf0) >> 4);
    if(notleading || c != 0) {
      *dst++ = b64table[c];
      notleading = 1;
    }
    c = ((b1 & 0xf) << 2) | ((b2 & 0xc0) >> 6);
    if(notleading || c != 0) {
      *dst++ = b64table[c];
      notleading = 1;
    }
    c = b2 & 0x3f;
    if(notleading || c != 0) {
      *dst++ = b64table[c];
      notleading = 1;
    }
    if(pos >= size)
      break;
    else {
      b0 = src[pos++];
      b1 = src[pos++];
      b2 = src[pos++];
    }
  }

  *dst++ = '\0';
  return olddst;
}
