/*
**
** 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"

int fromuu[] = {
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,

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, 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, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,

0x00, 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, 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_uuencode(struct message *m)
{
  struct data *inbuf, *outbuf;
  int datalen;
  int i, l;
  int left;
  unsigned long triple;
  unsigned char *inb;
  char outb[256];

  inbuf = m->td;
  inbuf->offset = inbuf->bodystart;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "* Encoding UUencode.\n");
#endif
  logger(LOG_DEBUG, "encode uuencode");

  /* Exit on empty input */
  if (!inbuf->size)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** Empty input (failed).\n");
#endif
      return(NOK);
    }

  /* Is this first line? */
  if (inbuf->offset == 0)
    datalen = 0;

  outbuf = (struct data *)Yalloc(sizeof(struct data ));
  outbuf->encoding = EUUENCODE;
  /* Initialize working pointers */
  inb = inbuf->contents + inbuf->offset;
  triple = 0;

  /* Start with uuencode preamble */
  fix_filename(m);

  sprintf(outb,"begin 644 %s\n", m->sd->name);
  append_data(outbuf, outb, strlen(outb), pz);
  outbuf->lineend += 1;
  i = 0;
  left = inbuf->bodyend - inbuf->offset;
  datalen = (45 < left) ? 45 : left;
  append_char(outbuf, datalen + ' ', pz);
  /*
   * Process entire inbuf.
   */
  while (left != 0)
    {
      left--;
      datalen--;
      i++;
      triple = (triple<<8) | *inb;
      if (i == 3 || datalen == 0)
	{
	  switch (i) 
	    {
	    case 1:
	      triple = triple<<4;
	      break;
	    case 2:
	      triple = triple <<2;
	      break;
	    default:
	      break;
	    }
	  for (l = i; l >= 0; l--)
	    {
	      char tmpi;
	      tmpi = 0x3f & (triple>>(6*l));
	      append_char(outbuf, (tmpi != 0) ? (tmpi + ' '): '`', pz);
	    }

	  if (datalen == 0)
	    {
	      datalen = (45 < left) ? 45 : left;
	      if (datalen == 0)
		{
		  switch (i)
		    {
		    case 1:
		      append_data(outbuf, "JJ\n \nend\n", 11, pz);
		      outbuf->lineend += 3;
		      left = 0;
		      break;
		    case 2:
		      append_data(outbuf, "J\n \nend\n", 10, pz);
		      outbuf->lineend += 3;
		      left = 0;
		      break;
		    default:
		      append_data(outbuf, "\n \nend\n", 9, pz);
		      outbuf->lineend += 3;
		      left = 0;
		      break;
		    }
		}
	      else
		{
		  append_char(outbuf, '\n', pz);
		  append_char(outbuf, datalen + ' ', pz);
		  outbuf->lineend += 1;
		}
	    }
	  triple = 0;
	  i = 0;
	}
      inb++;
      inbuf->offset += 1;
    }
  safe_mchange(m, outbuf);
  return(OK);
}


/*
 * decode_uuencode()
 *
 * Decode an uuencoded encoding
 */

int
decode_uuencode(struct message *m)
{
  struct data *inbuf, *outbuf;
  char *inb;
  unsigned int i;
  int	ii;
  int l;
  int dl = 0;
  int datalen;
  char filename[512];
  unsigned long triple;


  inbuf = m->td;
  inbuf->offset = inbuf->bodystart;
  triple = 0;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "* Decoding UUencode.\n");
#endif
  logger(LOG_DEBUG, "decode uuencode");

  /* Exit on empty input */
  if (!inbuf->size)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** Empty input (failed).\n");
#endif

      return(NOK);
    }
  outbuf = (struct data *)Yalloc(sizeof(struct data ));
  outbuf->encoding = EBINARY;

  inb = inbuf->contents + inbuf->offset;
  while (isspace(*inb))
    {
      inb++;
      inbuf->offset += 1;
    }
  if ((i = sscanf(inb, "begin%*1[ ]%*3[0-7]%*1[ ]%s", filename)) != 1)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** Illegal start string at offset %lu (failed).\n",inbuf->offset);
#endif
      sprintf(ebuf, "decode_uuencode: Illegal start string: %d", i);
      logger(LOG_WARNING, ebuf);
      return(NOK);
    }
  m->sd->name = NEWSTR(filename);
  /* Set type */
  if (m->sd->type == NULL || strcmp(m->sd->type, "APPLICATION") == 0)
    if ((m->sd->nameext = getextension(filename)) != NULL)
      if ((m->sd->type = confextr("UUENCODE", m->sd->nameext, NULL)) == NULL)
	m->sd->type = confextr("UUENCODE", "DEFAULT", NULL);
  if (m->sd->type == NULL)
    m->sd->type = NEWSTR("APPLICATION");

  inb += 10 + strlen(filename);
  inbuf->offset += 10 + strlen(filename);

  inbuf->loffset += 1;

  l = 0;
  datalen = 0;

  /*
   * Process entire inbuf.
   */
  while (inbuf->offset < inbuf->end)
    {
      if (datalen == 0)
	{
	  while (*inb != '\n')
	    {
	      /* Clean rest of the line */
	      inb++;
	      inbuf->offset += 1;
	    }
	  /* Skip Line feed */
	  inb++;
	  inbuf->offset += 1;
	  inbuf->loffset += 1;

	  /* Check for end */
	  if (strncmp(inb, "end", 3) == 0)
	    {
	      inbuf->offset += getline(inbuf);
	      inbuf->loffset += 1;
	      inbuf->bodyend = inbuf->offset;
	      if (process)
		{
		  safe_mchange(m, outbuf);
		  if (match(m->sd->type, "TEXT"))
		    {
		      m->td->encoding = E7BIT;
		      check_bits(m->td);
		    }
		  else
		    m->td->encoding = EBINARY;
		}
	      else
		if (dl == 6)
		  {
		    m->td = outbuf;
		    check_as_ad(m);
		    m->td = m->sd;
		  }
	      return(OK);
	    }

	  /* Get data length */
	  if ((datalen = fromuu[(unsigned char)*inb]) < 0)
	    {
#ifdef DEBUG
	      if (edebug)
		fprintf(stderr, "*** Illegal datalen at %lu: %c.\n",
			inbuf->offset, *inb);
#endif
	      sprintf(ebuf, "decode_uuencode: Illegal datalen: %c at offset: %lu", 
		      *inb, inbuf->offset);
	      logger(LOG_WARNING, ebuf);
	    return(NOK);
	    }
	  inb++;
	  inbuf->offset += 1;
	}
      else
	{
	  if ((ii =  fromuu[(unsigned char)*inb]) < 0)
	    {
#ifdef DEBUG
	      if (edebug)
		fprintf(stderr, "*** Illegal character at %lu: %c.\n",
			inbuf->offset, *inb);
#endif
	      sprintf(ebuf, "decode_uuencode: Illegal character: %c", *inb);
	      logger(LOG_WARNING, ebuf);
	      return(NOK);
	    }
	  triple = triple<<6 | (0x3f & ii);
	  l++;
	  if (l == 4 || l > datalen)
	    {
	      switch(l)
		{
		case 2:
		  triple = triple>>4;
		  break;
		case 3:
		  triple = triple>>2;
		  break;
		default:
		  break;
		}
		for (l -= 2; l >= 0 && datalen != 0; l--, datalen--)
	      if (process || dl < 6)
		  {
		    append_char(outbuf,(char) (0xff & (triple>>(8*l))), pz);
		    dl++;
		  }
	      triple = 0;
	      l = 0;
	    }
	  inb++;
	  inbuf->offset += 1;
	}
    }
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "*** Premature end of data (failed).\n");
#endif
  logger(LOG_WARNING, "decode_uuencode: Premature end of data, uudecode failed");
  return(NOK);
}


