/*
 * This file is part of uudeview, the simple and friendly multi-part multi-
 * file uudecoder  program  (c)  1994 by Frank Pilhofer. The author may be
 * contacted by his email address,          fp@informatik.uni-frankfurt.de
 *
 * 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 FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>

#ifndef NO_POSIX
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#endif

#ifndef NO_POSIX
#ifndef _DOS
#include <unistd.h>
#endif
#endif

#include "uudeview.h"
#include "uuerror.h"
#include "uuutil.h"
#include "uutar.h"
#include "uunconc.h"
#include "toolbox.h"

static char ungotline[255];
static int  ungotflag  = 0;
static long oldposition= 0;
static char wrbuffer[1024];
static long yctr=0;

/*
 * In DOS, we must open the file binary, O_BINARY is defined there
 */

#ifndef _DOS
#ifndef O_BINARY
#define O_BINARY      0
#endif
#endif

/*
 * Load a part of a file. Search for the Subject Line (ptr), the
 * "Origin" (fptr); for begin's and end's and the filename (nptr)
 **/

fileread *ScanPart (FILE *datei, char *fname)
{
  int subflag=0, encoding=0, dflag=0, ctline=0, vflag, cflag=0, nflag;
  static char line[255], *ptr, *p2;
  fileread *result;

  if (datei == NULL || feof (datei)) {
    uuerror = EOFILE;
    return NULL;
  }

  if ((result = (fileread *) malloc (sizeof (fileread))) == NULL) {
    uuerror = NOMEM;
    return NULL;
  }
  memset (result, 0, sizeof (fileread));

  if ((result->sfname = _CopyLine (fname, -1)) == NULL) {
    uuerror = NOMEM;
    free (result);
    return NULL;
  }
  result->startpos = (ungotflag) ? oldposition : ftell (datei);

  while (!feof (datei) || ungotflag) {
    if (ungotflag) {
      strcpy (line, ungotline);
      ungotflag = 0;
    }
    else {
      oldposition = ftell (datei);
 scanempty:
      if (fgets (line, 255, datei) == NULL)
	break;
      if (line[0] == '\x0d' || line[0] == '\x0a') /* line empty? */
        goto scanempty;                           /* then ignore */
    }
    ctline++;
    /*
     * If in Windows, yield some time to other apps.
     * You call that multitasking?
     */
#ifdef _WINDOWS
    if ((++yctr % 100) == 0)
      _wyield ();
#endif      

    line[255] = '\0'; /* For Safety of string functions */

    if (_FP_strnicmp (line, "Subject:", 8) == 0) {
      if ((subflag & 1) || result->uudet) {
	strcpy (ungotline, line);
	ungotflag = 1;
	break;
      }
      else {
	result->subject = _CopyLine (line+8, -1);
	subflag        |= 1;
      }
    }
    if (_FP_strnicmp (line, "from ", 5) == 0) {
      if (subflag & 2) {
        strcpy (ungotline, line);
        ungotflag = 1;
        break;
      }
      if (strchr (line, '@') != NULL) {
        result->origin = _CopyLine (line+5, -1);
      }
      subflag |= 2;
    }

    if ((strncmp      (line, "begin ",       6) == 0 ||
	 _FP_strnicmp (line, "<pre>begin ", 11) == 0) && 
	result->uudet != B64ENCODED) {
      if (result->begin || result->end) {
	strcpy (ungotline, line);
	ungotflag = 1;
	break;
      }

      if (*line == '<')
	ptr = line + 10;
      else
	ptr = line + 5;

      while (*ptr == ' ') ptr++;
      while (isdigit (*ptr)) 
	result->mode = result->mode * 8 + *ptr++ - '0';
      while (*ptr == ' ') ptr++;

      result->filename = _CopyLine (ptr, -1);
      result->begin    = 1;
    }

    if (strncmp (line, "end", 3) == 0 && result->uudet != B64ENCODED) {
      result->end = 1;
      if (dflag && encoding)
        result->uudet = encoding;
    }

    if (_FP_strnicmp (line, "Content-Type", 12) == 0) {
      /*
       * We use Content-Type to mark a new attachment and split the file.
       * However, we do not split if we haven't found anything encoded yet.
       */
      if (result->uudet) {
	strcpy (ungotline, line);
	ungotflag = 1;
	break;
      }
      ctline=0;
    }
    if ((ptr = _FP_stristr (line, "number=")) && ctline<3) {
      ptr += 7; if (*ptr == '"') ptr++;
      result->partno = atoi (ptr);
    }
    if ((ptr = _FP_stristr (line, "total=")) && ctline<3) {
      ptr += 6; if (*ptr == '"') ptr++;
      result->maxpno = atoi (ptr);
    }
    if ((ptr = _FP_stristr (line, "name=")) && ctline<3) {
      p2 = ptr += 5;
      if (*p2 == '"' || *p2 == '\'') {
	p2++; ptr++;
      }
      while (isalnum(*p2) || 
             *p2 == '.' || *p2=='_' || *p2 == '-' ||
	     *p2 == '!' || *p2=='@' || *p2 == '$')
	p2++;
      *p2 = '\0';
      if (p2 != ptr && result->filename == NULL)
	result->filename = _FP_strdup (ptr);
    }
    if ((ptr = _FP_stristr (line, "id=")) && ctline<3) {
      p2 = ptr += 3;
      if (*p2 == '"') {
        p2 = strchr (++ptr, '"');
      }
      else {
        while (*p2 && isprint(*p2) && !isspace(*p2) && *p2 != ';')
          p2++;
      }
      if (p2 && *p2 && p2!=ptr) {
        *p2 = '\0';
        if (result->mimeid)
          free (result->mimeid);
        result->mimeid = _FP_strdup (ptr);
      }
    }

    /*
     * if we haven't yet found anything encoded, try to find something
     */
      
    if (!(result->uudet)) {
      /*
       * Netscape-Repair code is the same as in uunconc.c
       */
      if ((vflag = valid_data (line)) == 0) { /* Can we Netscape-repair? */
	nflag = BrokenByNetscape (line);

	while (vflag == 0 && nflag) {
	  if (nflag == 1) {
	    ptr = line + strlen (line);
	    while (ptr>line && (*(ptr-1)=='\x0d' || *(ptr-1)=='\x0a'))
	      ptr--;
	    if (fgets (ptr, 255-(ptr-line), datei) == NULL)
	      break;
	  }
	  else {                       /* don't need next line to repair */
	  }
	  if (NetscapeCollapse (line)) {
	    if ((vflag = valid_data (line)) == 0)
	      nflag = BrokenByNetscape (line);
	  }
	  else
	    nflag = 0;
	}
	/*
	 * Sometimes, a line is garbled even without it being split into
	 * the next line. Then we try this in our despair
	 */
	if (vflag == 0) {
	  if (NetscapeCollapse (line))
	    vflag = valid_data (line);
	}
      }
      if (vflag) {
        if (dflag==3) {
	  result->uudet = vflag;
          encoding = dflag = 0;

	  /*
	   * if we're in fast mode, we're told we don't have to look
	   * for anything below, so we can as well break out of every-
	   * thing
	   */
	  if (fast)
	    break;

	  /*
	   * we assume here that two parts have at least a single empty
	   * line inbetween. To speed up scanning, we don't run throuth
	   * the whole loop but just search the next empty line. But
	   * we also try to pick up an "end"
	   */

	  while (!feof (datei)) {
	    /*
	     * If in Windows, yield some time to other apps.
	     * You call that multitasking?
	     */
#ifdef _WINDOWS
	    if ((++yctr % 100) == 0)
	      _wyield ();
#endif      
	    oldposition = ftell (datei);
	    if (fgets (line, 255, datei) == NULL)
	      break;
	    if (line[0] == '\x0d' || line[0] == '\x0a')
	      break;
	    else if (line[0] == 'e') {
	      if (strncmp(line, "end", 3)==0 && result->uudet!=B64ENCODED) {
		result->end = 1;
		break;
	      }
	    }
	  }
        }
        else {
          encoding = vflag;
          dflag++;
        }
      }
      else
        dflag = 0;
    } /* if (!uudet) */
    /*
     * End of scanning loop
     */
  } /* while (!feof (datei)) */

  if (dflag && encoding == B64ENCODED)
    result->uudet = B64ENCODED;

  if (result->uudet == B64ENCODED) {
    result->begin = result->end = 0;
    result->mode  = 6*64+4*8+4;
  }

  /*
   * In fast mode, this length will yield a false value. Ee don't care.
   * This must be checked for in uunconc(), where we usually stop decoding
   * after reaching startpos+length
   */

  result->length = ftell (datei) - result->startpos;

  return result;
}

int write_tar (FILE *source, FILE *target, uulist *uthis)
{
  tarblock tb;
  long size=0;
  int bytes;

  fseek (source, uthis->tarblock * TARBLOCK, SEEK_SET);
  fread (&tb, TARBLOCK, 1, source);

  size = getokt (tb.header.size);

  fseek (source, (uthis->tarblock + 1) * TARBLOCK, SEEK_SET);

  while (size > 0) {
    /*
     * If in Windows, yield some time to other apps.
     * You call that multitasking?
     */
#ifdef _WINDOWS
    if ((++yctr % 100) == 0)
      _wyield ();
#endif      

    bytes = fread (wrbuffer, 1, (size_t) ((size > 1024) ? 1024 : size), source);

    if (bytes == 0 && size != 0) {
      uuerror = FILEERR;
      fclose (source);
      fclose (target);
      unlink (uthis->filename);
      return 0;
    }
    if (fwrite (wrbuffer, 1, bytes, target) != bytes) {
      uuerror = FILEERR;
      fclose (source);
      fclose (target);
      unlink (uthis->filename);
      return 0;
    }
    size -= bytes;
  }
  return 1;
}

int write_file (uulist *uthis, char *savepath)
{
  FILE *target, *source;
  int fildes, bytes;
  char sname[256];
  char *name, r;
  char *ptr;

 again_new:
  if ((name = cutdir (uthis->filename)) == NULL)
    return 0;

#ifdef _DOS
  if ((ptr = dosfname (name)) == NULL) {
    free (name);
    return 0;
  }
  free (name);
  name = ptr;
#endif

 again_same:
#ifdef _DOS
  sprintf (sname, "%s\\%s", savepath, name);
#else
  sprintf (sname, "%s/%s", savepath, name);
#endif

 again_other:
  if (!overwrite && exists (sname)) {
    printf ("\t%s exists\t(O)verwrite, Overwrite (A)ll,\n\t\t\t\t(R)ename or (N)ext [r] ", name);
    fflush (stdout);
    switch (getyn ('r')) {
    case 'n':
      return 0;
      break;
    case 'o':
      break;
    case 'a':
      overwrite = 1;
      break;
    case 'r':
      printf ("\nEnter new filename: "); fflush (stdout);
      if (fgets (sname, 250, stdin) == NULL) {
	printf ("\nERROR! Couldn't get filename.\n");
	return 0;
      }
      sname[strlen(sname)-1] = '\0';
      
      if (strlen (sname) > 1) {
	free (uthis->filename);
	uthis->filename = _FP_strdup (sname);
	goto again_new;
      }
      goto again_same;
      break;
    default:
      goto again_other;
    }
  }

  if ((source = fopen (uthis->binfile, "rb")) == NULL) {
    uuerror = FILEERR;
    free (name);
    return 0;
  }

  if ((fildes = open (sname, O_WRONLY | O_CREAT | O_BINARY | O_TRUNC, uthis->mode)) == -1) {
    uuerror = FILEERR;
    fclose (source);
    free   (name);
    return 0;
  }

  if ((target = fdopen (fildes, "wb")) == NULL) {
    uuerror = FILEERR;
    fclose (source);
    close (fildes);
    free  (name);
    return 0;
  }

  if (uthis->tarblock >= 0) {
    if (write_tar (source, target, uthis) != 1) {
      uuerror = FILEERR;
      fclose (source);
      fclose (target);
      unlink (sname);
      free   (name);
      return 0;
    }
    fclose (source);
    fclose (target);
    free   (name);

    return 1;
  }

  while (!feof (source)) {
    /*
     * If in Windows, yield some time to other apps.
     * You call that multitasking?
     */
#ifdef _WINDOWS
    if ((++yctr % 100) == 0)
      _wyield ();
#endif      

    bytes = fread (wrbuffer, 1, 1024, source);

    if (bytes == 0 && !feof (source)) {
      uuerror = FILEERR;
      fclose (source);
      fclose (target);
      unlink (sname);
      free   (name);
      return 0;
    }
    if (fwrite (wrbuffer, 1, bytes, target) != bytes) {
      uuerror = FILEERR;
      fclose (source);
      fclose (target);
      unlink (sname);
      free   (name);
      return 0;
    }
  }

  fclose (target);
  fclose (source);
  free   (name);

  return 1;
}


/*****************************************************************************
 + Frank Pilhofer                             fp@informatik.uni-frankfurt.de +
 +---------------------------------------------------------------------------+
 | Department of Computer Sciences * University of Frankfurt / Main, Germany |
 *****************************************************************************/
