/*
**
** 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"
#include <assert.h>

/*
**	Calculates the CRC of a BinHex file
**
*/

unsigned long
calc_crc(unsigned long crc,unsigned int c)
{
  register int i;
  register unsigned long tmk;

  tmk = crc;
  for (i = 0; i < 8; i++)
    {
      c <<= 1;
      if ((tmk <<= 1) & 0x10000)
	tmk = (tmk & 0xffff) ^ 0x1021;
      tmk ^= (c >> 8);
      c &= 0xff;
    }
  assert(tmk <= 65535); /* ELSE TRUNC / TW */
  return((unsigned short)tmk);
}

/*
**
**	Extract and check the CRC of a BinHex file.
**
*/

int
get_binhex_crc(struct data *d, unsigned long checksum)
{
  int partlen;
  /* CRC for the CRC */
  checksum = calc_crc(checksum, (unsigned char)'\0');
  checksum = calc_crc(checksum, (unsigned char)'\0');

  if (d->offset + 2 > d->end)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*   ERROR get_binhex_crc: Premature end of data in BinHexed file %lu", d->offset);
#endif
      logger(LOG_WARNING, "get_binhex_crc: Premature end of data in BinHexed file");
      return(NOK);
    }
  for (partlen = 1; partlen >= 0; partlen--, d->offset += 1)
    {
      if (((0xFF & *(d->contents + d->offset)) ^ (0xFF & (checksum >> (8*partlen))))
	  != 0)
	  return(NOK);
    }
  return(OK);
}

/*
**
**	Extract header from a BinHex file
**
*/

int
get_binhex_binary(struct message *m)
{
  unsigned long checksum;
  unsigned int filenamelen;
  char flags[3];
  struct data *d;
  struct data *r = NULL;
  unsigned long partlen;
  unsigned long rlen;
  char cdlen[5];


  d = m->td;
  checksum = 0;
  d->offset = d->bodystart;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "Extracting applefile");
#endif
  if (d->contents == NULL)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no input (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: getting namelen: called with empty data");
      return(NOK);
    }


  /* Extract filenamelen */
  filenamelen = *(d->contents + d->offset);
  checksum = calc_crc(0, (unsigned char) *(d->contents + d->offset));
  d->offset += 1;

  /* Extract name from a BinHex file */
  m->sd->name = (char *)Yalloc(filenamelen + 1);
  if (d->offset + filenamelen > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no name (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: getting name: premature end of data");
      return(NOK);
    }
  assert (filenamelen > 0);
  bcopy(d->contents + d->offset, m->sd->name, (unsigned) filenamelen);
  for (partlen = 0; partlen < filenamelen; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));
  
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", filename - %s, namelength %d", m->sd->name, filenamelen);
#endif

  if (*(d->contents + d->offset) != '\0')
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, ", no null byte (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Mandantory null byte unavailable in binhex header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }

  /* Move past NULL */
  checksum = calc_crc(checksum, (unsigned char)'\0');
  d->offset += 1;


  /* Extract type of a BinHex file */
  if ((d->offset + 4) > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no type (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Type unavailable in binhex header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  m->sd->appletype = (char *)Yalloc(9);
  bcopy(d->contents + d->offset, m->sd->appletype, 4);
  for (partlen = 0; partlen < 4; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));


  /* Extract auth of a BinHex file */
  if ((d->offset + 4) > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no auth (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Auth unavailable in binhex header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  bcopy(d->contents + d->offset, m->sd->appletype + 4, 4);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", type&creator - %s", m->sd->appletype);
  fflush(stderr);
#endif

  for (partlen = 0; partlen < 4; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));

  if ((m->sd->type = confextr("APPLEFILE", m->sd->appletype, NULL)) == NULL)
    if ((m->sd->type = confextr("APPLEFILE", "DEFAULT", NULL)) == NULL)
      m->sd->type = NEWSTR("APPLICATION");

  /* Extract flags of a BinHex file. */
  if ((d->offset + 2) > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no flags (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Flags unavailable in binhex header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  /* These might just as well be junked */
  bzero(flags, 3);
  bcopy(d->contents + d->offset, flags, 2);
  for (partlen = 0; partlen < 2; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));


  /* Extract datafork length of a BinHex file. */
  if ((d->offset + 4) > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no data fork length (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Data length unavailable in binhex header");

      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  
  bzero(cdlen, 5);
  bcopy(d->contents + d->offset, cdlen, 4);
  for (partlen = 0; partlen < 4; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));
  d->length = getblong(cdlen);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", data fork length: %lu", d->length);
#endif

  /* Extract resource fork length of a BinHex file. */
  /* This really not interesting until we support resource forks */
  if ((d->offset + 4) > d->end)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", no resource fork length (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: Resource length unavailable in binhex header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  bzero(cdlen, 5);
  bcopy(d->contents + d->offset, cdlen, 4);
  for (partlen = 0; partlen < 4; partlen++, d->offset += 1)
    checksum = calc_crc(checksum, *(d->contents + d->offset));
  if ((rlen = getblong(cdlen)) != 0)
    m->sd->applefile = ABINHEX;

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", resource fork length: %lu", rlen);
#endif

  /* Check header CRC */
  if (get_binhex_crc(d, checksum) != OK)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", header CRC error (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: CRC Error in header");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }


  /* Extract and output the datafork */
  if (process)
    {
      d->bodystart = d->offset;
      d->bodyend = d->offset + d->length;
    }
  if (d->offset + d->length > d->end)
    {
#ifdef DEBUG
  if (edebug && process)
    {
      fprintf(stderr, ", premature end of data in data fork (failed)\n");
      fprintf(stderr, "suggested offset %lu suggested length %lu real length %lu\n",
	      d->offset, d->length, d->end);
    }
#endif
  if (process)
      logger(LOG_WARNING, "get_binhex_binary: getting datafork: premature end of data");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
  checksum = 0;
  for(partlen = 0; partlen < d->length; partlen++, d->offset += 1)
    {
      checksum = calc_crc(checksum, (unsigned char)*(d->contents + d->offset));
    }
  if (get_binhex_crc(d, checksum) != OK)
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", data fork CRC error at offset %lu (failed)\n", d->offset);
#endif
      logger(LOG_WARNING, "get_binhex_binary: CRC Error in data fork");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }


  /* Extract and output the resource fork */
  if (rlen != 0)
    {
      if (d->offset + rlen <= d->end)
	{
	  if (process)
	    {
	      r = (struct data *)Yalloc(sizeof(struct data));
	      r->bodystart = d->offset;
	      r->bodyend = d->offset + rlen;
	      r->contents = d->contents;
	      r->appletype = d->appletype;
	      r->name = d->name;
	      r->type = d->type;
	      r->applefile = ARESOURCE;
	      d->applefile = AFILE;
	    }
	}
      else
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, ", premature end of data in resource fork (failed) %lu\n", r->offset);
#endif
	  logger(LOG_WARNING, "get_binhex_binary: getting resource fork: premature end of data");
	  if (m->td != m->sd)
	    free_data(m->td);
	  m->td = m->sd;
	  return(NOK);
	}
    }
  checksum = 0; 
  for(partlen = 0; partlen < rlen; partlen++, d->offset += 1)
    {
       checksum = calc_crc(checksum, (unsigned char)*(d->contents + d->offset));
    }
  if (get_binhex_crc(d, checksum) == OK)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, ", done (OK).\n");
#endif
      
      if (r != NULL)
	{
	  d->next = r;
	  r->encoding = EBINARY;
	}
      d->encoding = EBINARY;
      return(OK);
    }
  else
    {
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", resource fork CRC error (failed)\n");
#endif
      logger(LOG_WARNING, "get_binhex_binary: CRC Error in resource fork");
      if (m->td != m->sd)
	free_data(m->td);
      m->td = m->sd;
      return(NOK);
    }
}


int
create_binhex_binary(struct message *m)
{
  unsigned long filenamelen;
  char four[5];
  struct data *d, *r, *h;
  unsigned long crc;
  unsigned long dlen, rlen;
  unsigned long partlen;


  h = (struct data *) Yalloc(sizeof(struct data ));
  if ((d = m->td) != NULL)
    {
      d->offset = d->bodystart;
    }
  if ((r = d->next) != NULL)
    {
      r->offset = r->bodystart;
    }
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, " Creating binhex binary");
#endif
  if (m->sd->name == NULL)
    m->sd->name = NEWSTR("noname");

  filenamelen = strlen(m->sd->name);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", filename: %s", m->sd->name);
#endif
  /* Add filenamelen */
  append_char(h, (char) (0xff & filenamelen), pz);
  
  /* Add filename */
  append_data(h, m->sd->name, filenamelen, pz);
  
  /* Add mandantory NULL byte */
  append_char(h, '\0', pz);
  
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", type&creator: %s\n", m->sd->appletype);
#endif
  /* Add type */
  
  append_data(h, m->sd->appletype, 4, pz);

  /* Add auth */
  append_data(h, m->sd->appletype + 4, 4, pz);

  /* Add flags */
  append_data(h, "\0\0\0", 2, pz);

  /* Add data fork length */
  dlen = d->bodyend - d->bodystart;
  bzero(four, 5);
  for (partlen = 4; partlen > 0; partlen--)
    {
      four[4 - partlen] = (char) 0xff & (dlen >> ((partlen - 1) * 8));
    }
  append_data(h, four, 4, pz);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", data length: %lu", dlen);
#endif
  /* Add resource fork length */
  if (r != NULL)
    {
      rlen = r->bodyend - r->bodystart;
    }
  else
    {  
      rlen = 0; /* Resource fork is empty */
    }
  bzero(four, 5);
  for (partlen = 4; partlen > 0; partlen--)
    {
      four[4 - partlen] = (char) 0xff & (rlen >> ((partlen - 1) * 8));
    }
  append_data(h, four, 4, pz);

#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", resource length: %lu\n", rlen);
#endif
  /* Generate header CRC */
  crc = 0;
  for (partlen = 0; partlen < h->bodyend; partlen++)
    {
      crc = calc_crc(crc, *(h->contents + partlen));
    }
  crc = calc_crc(crc, '\0');
  crc = calc_crc(crc, '\0');
  bzero(four, 5);
  for (partlen = 2; partlen > 0; partlen--)
    {
      four[2 - partlen] = (char) 0xff & (crc >> ((partlen - 1) * 8));
    }
  append_data(h, four, 2, pz);

  dlen = d->bodyend - d->bodystart;
  partlen = h->bodyend;
  /* Append data fork */
  append_data(h, d->contents + d->bodystart, dlen, pz);
#ifdef DEBUG
  if (edebug)
      fprintf(stderr, "Append data fork: old end %lu, new end %lu, length %lu\n", partlen, h->bodyend,  dlen);
#endif
  /* Append data CRC */
  crc = 0;
  for (; partlen < h->bodyend; partlen++)
    {
      crc = calc_crc(crc, *(h->contents + partlen));
    }
  crc = calc_crc(crc, (unsigned char)'\0');
  crc = calc_crc(crc, (unsigned char)'\0');
  bzero(four, 5);
  for (partlen = 2; partlen > 0; partlen--)
    {
      four[2 - partlen] = (char) 0xff & (crc >> ((partlen - 1) * 8));
    }
  append_data(h, four, 2, pz);

  /* Append resource fork */
  crc = 0;
  partlen = h->bodyend;
  if (r != NULL)
    {
      rlen = r->bodyend - r->bodystart;
      append_data(h, r->contents + r->bodystart, rlen, pz);
#ifdef DEBUG
  if (edebug)
      fprintf(stderr, "Append resource fork: old end %lu, new end %lu, length %lu\n", partlen, h->bodyend,  rlen);
#endif

      /* Append resource CRC */
      for (partlen = h->bodyend - rlen; partlen < h->bodyend; partlen++)
	{
	  crc = calc_crc(crc, *(h->contents + partlen));
	}
    }
  crc = calc_crc(crc, '\0');
  crc = calc_crc(crc, '\0');
  bzero(four, 5);
  for (partlen = 2; partlen > 0; partlen--)
    {
      four[2 - partlen] = (char) 0xff & (crc >> ((partlen - 1) * 8));
    }
  append_data(h, four, 2, pz);
  safe_mchange(m, h);
  return(OK);
}



