/*
**
** Copyright (C) 1994 Swedish University Network (SUNET)
**
**
** This program is developed by UDAC, Uppsala University by commission
** of the Swedish University Network (SUNET). 
**
** 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; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITTNESS 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
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**
**                                        Martin.Wendel@udac.uu.se
**                                        Torbjorn.Wictorin@udac.uu.se
**
**                                        UDAC	
**                                        P.O. Box 174
**                                        S-751 04 Uppsala
**                                        Sweden
**
*/


#include "emil.h"

char tob64[] = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";



int fromb64[] = {
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, SKIP, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  

SKIP, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, 0x3e, FAIL, FAIL, FAIL, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,  
0x3c, 0x3d, FAIL, FAIL, FAIL, SKIP, FAIL, FAIL,

FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,  
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,  
0x17, 0x18, 0x19, FAIL, FAIL, FAIL, FAIL, FAIL,

FAIL, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,  
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,  
0x31, 0x32, 0x33, FAIL, FAIL, FAIL, FAIL, FAIL,

FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,  
FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL
};

int
encode_base64(struct message *m)
{
  struct data *inbuf, *outbuf;
  int linelen;
  int i, l, left;
  unsigned long triple;
  unsigned char *inb;
  
  inbuf = m->td;
  logger(LOG_DEBUG, "encode base64");
  /* Exit on empty input */
  if (!inbuf->size)
    return(NOK);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "Encoding base64");
#endif
  linelen = 0;

  outbuf = (struct data *)Yalloc(sizeof(struct data ));

  outbuf->encoding = EBASE64;
  
  /* Initialize working pointers */
  inb = (unsigned char *)inbuf->contents + inbuf->offset;
  i = 0;
  triple = 0;
  left = inbuf->bodyend - inbuf->offset;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", data length: %i\n", left);
#endif
  /*
   * Process entire inbuf.
   */
  while (left != 0)
    {
      i++;
      left--;

      triple = (triple <<8) | *inb;
      if (i == 3 || left == 0)
	{
	  switch (i) 
	    {
	    case 1:
	      triple = triple<<4;
	      break;
	    case 2:
	      triple = triple<<2;
	      break;
	    default:
	      break;
	    }
	  for (l = i; l >= 0; l--)
	    {
	      /* register */ 
	      int rr; rr = 0x3f & (triple>>(6*l)); assert (rr < 64); 
	      append_char(outbuf, tob64[rr]);
	      if (linelen == 72)
		{
		  append_char(outbuf, '\n');
		  outbuf->lineend += 1;
		  linelen = 0;
		}
	      else
		{
		  linelen++; 
		}
	    }
	  if (left == 0)
	    switch(i)
	      {
	      case 2:
		append_data(outbuf, "=\n", 2);
		break;
	      case 1:
		append_data(outbuf, "==\n", 3);
		break;
	      default:
		break;
	      }
	  triple = 0;
	  i = 0;
	}
      inb++;
      inbuf->offset += 1;
    }
  m->td = outbuf;
  return(OK);
}

int
decode_base64(struct message *m)
{
  struct data *inbuf, *outbuf;
  char *inb;
  int i;
  int l;
  int left;
  unsigned long triple;

  inbuf = m->td;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "Decoding base64");
#endif
  logger(LOG_DEBUG, "decode base64");
  /* Exit on empty input */
  if (!inbuf->size)
    return(NOK);



  /* Initialize working pointers */
  inb = inbuf->contents + inbuf->offset;
  if (process)
    {
      outbuf = (struct data *)Yalloc(sizeof(struct data ));
      outbuf->encoding = EBINARY;
    }
  l = 0;
  triple = 0;
  left = inbuf->bodyend - inbuf->offset;
  /*
   * Process entire inbuf.
   */
  while (left != 0)
    {
      left--;
      i = fromb64[(unsigned char)*inb];
      switch(i)
	{
	case FAIL:
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, ", illegal char at %i: %c (failed).\n", 
		    inbuf->offset, *inb);
#endif
	  sprintf(ebuf, "decode_base64: Illegal character: %c", *inb);
	  logger(LOG_WARNING, ebuf);
	  return(NOK);
	  break;
	case SKIP:
	  break;
	default:
	  triple = triple<<6 | (0x3f & i);
	  l++;
	  break;
	}
      if (l == 4 || left == 0)
	{
	  switch(l)
	    {
	    case 2:
	      triple = triple>>4;
	      break;
	    case 3:
	      triple = triple>>2;
	      break;
	    default:
	      break;
	    }
	  if (process)
	    for (l  -= 2; l >= 0; l--)
	      {
		append_char(outbuf,( 0xff & (triple>>(l*8))));
	      }
	  triple = 0;
	  l = 0;
	}
      inb++;
      inbuf->offset += 1;
    }
  if (process)
    m->td = outbuf;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", done.\n");
#endif
  return(OK);
}

