/*
 * File:	receive.c
 *
 * Author:	Ulli Horlacher (framstag@rus.uni-stuttgart.de)
 *
 * Contrib.:	Christian Recktenwald (chris@yeti.faveve.uni-stuttgart.de)
 * 		Rainer Bawidamann (widi@sol.wohnheim.uni-ulm.de)
 * 		Beate Herrmann (beate@juhu.lake.de)
 * 		Stefan Zehl (sec@42.org)
 *
 * History:	
 * 
 *  1995-08-11	Framstag	initial version
 *  1995-08-14	Framstag	corrected bug when receiving archives
 *  1995-09-10	Framstag	extracted functions to spool.c
 *  1995-10-31	Framstag	fixed security problems
 *  1995-11-05	Framstag	added NeXT support
 *  1995-11-07	Framstag	corrected bug when receiving to
 *              	 	a non-writable directory
 *  1995-12-02	Framstag	added rename option
 *  1996-01-15	Framstag	better error handling
 *  1996-02-06	Framstag	added ATTRIBUTE=EXE
 *  1996-02-07	Chris		better Convex-OS support
 *  1996-02-21	widi		better Solaris-2 support
 *  1996-02-22	Framstag	added bounce (forward) option
 *  1996-03-06	Framstag	moved time stamp setting to get_date
 *  1996-03-17	Framstag	some bug fixes
 *  1996-03-27	Framstag	added quiet mode: no questions asked
 *  1996-03-28	Framstag	extended search for support programs
 *  1996-04-01	Framstag	corrected forking bug
 *  1996-04-02	Framstag	added verbose bouncing
 *  1996-04-04	Framstag	added check for dangerous file names in archives
 *  1996-04-05	Framstag	correction: 1 KB is now 1024 bytes
 *  1996-04-08	Framstag	added question for renaming better dangerous 
 *				file name checking
 *  1996-04-12	Framstag	added pgp support
 *  1996-04-13	Framstag	added -f from option
 *  1996-04-13	Beate		added -s list option
 *  1996-04-18	Framstag	delete empty file when pgp fails
 *  1996-04-20	Framstag	added preserve option for pgp and tar
 *				added -k keep option
 *  1996-04-22	Framstag	some code cleanup
 *  1996-04-23	Framstag	moved fcopy to io.c
 *  1996-04-24	Framstag	changed bouncing option syntax
 *  1996-05-02	Framstag	better tar error checking
 *  1996-05-12	Framstag	better checking of not complete files
 *  1996-05-23	Framstag	added check for writeable tmp-files
 *  1996-05-26	Framstag	fixed bug with overwriting links
 * 		                fixed bug with returning from fork
 *  1996-06-24	Framstag	added -P to stdout option
 *  1996-09-13	Framstag	added rename option to "."-file checking
 *  1996-11-26	Framstag	substituted all gets() with fgets()
 *  1996-11-27	Framstag	corrected bug when receiving file is not 
 * 				writable
 *  1996-12-01	Framstag	better file name questioning
 *  1996-12-20	Framstag	don't ask for directory overwriting
 *  1997-01-20	GNUish		modified to reflect the GNUs needs
 *  1997-02-12	Framstag	fixed boolean bugs in receive_sf
 *  1997-02-20	GNUish		renamed error to error_log to support glibc
 *  1997-02-23	Framstag	modified str_* function names
 *  				extended with TYPE=MIME
 *  1997-02-24	Framstag	sprintf() -> snprintf()
 *  1997-06-23	Framstag	added readline support
 *  1997-08-01	Framstag	added -S secure option (signature check)
 *  1997-08-07	Framstag	no tar-file checking with quiet-mode
 *  1997-08-27	Framstag	ignore symlink errors when extracting tar files
 *  1997-09-21	Framstag	added multi-spool support and -Z option
 *  1997-09-30	Framstag	corrected list bug
 *  1997-10-06	Framstag	corrected bug with -n -b options together
 *  1997-11-14	Framstag	better verbose mode for system() calls
 *  1997-12-02	Framstag	fixed -b -a conjunction bug
 *  1997-12-11	Framstag	added bzip2 support
 *  1997-12-16	Sec		better xhoppel support for BSD
 *  1998-03-11	Framstag	added -H option to display the headers
 *  1998-05-10  Framstag	added -R option to renumber the spool files
 *  1999-02-15	Framstag	added check for dangerous symbolic links in 
 *				archive files
 *  1999-02-19	Framstag	option -L with selecting arguments
 *  2001-01-10	Framstag	fopen() ==> rfopen()
 *  2001-02-04	Framstag	added secure mktmpdir()
 *
 * The receive client of the sendfile package.
 * Receives, lists and deletes files from the sendfile spool.
 *
 * Copyright © 1995-2001 Ulli Horlacher
 * This file is covered by the GNU General Public License
 */

#include "config.h"   /* various #defines */

#ifdef ULTRIX
  #define S_IFMT   0170000 /* type of file */
  #define S_IFDIR  0040000 /* directory */
#endif

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <time.h>
#include <utime.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include "message.h"    /* information, warning and error messages */
#include "utf7.h"       /* UTF-7 coding */
#include "io.h"		/* read/write routines */
#include "string.h"     /* extended string functions */
#include "spool.h"	/* operations on files in the sendfile spool */
#include "getline.h"	/* get a line of text from stdin */
#include "lock.h"	/* file locking */

#if defined(HAVE_GETOPT_H)
  #include <getopt.h>
#else
  int getopt(int, char * const *, const char *);
  extern int opterr;
  extern int optind;
  extern int optopt;
  extern char *optarg;
#endif

#ifndef AIX
  #ifndef CONVEXOS
    FILE *popen(const char *, const char *);
  #endif
  int pclose(FILE *);
#endif


/* global variables */
char 
  *lll,			/* last list line */
  *prg,			/* name of the game */
  *pgpvm,		/* pgp verbose mode string */
  *verbose,		/* verbose mode on bouncing files */
  *tmpdir,		/* directory for temporary files */
  error_log[MAXLEN],	/* error log file */
  tartmp[MAXLEN],	/* tar temporary file */
  userspool[MAXLEN],	/* user spool directory */
  pgp_bin[MAXLEN],	/* the pgp binary */
  tar_bin[MAXLEN],	/* the tar binary */
  gzip_bin[MAXLEN],	/* the gzip binary */
  bzip2_bin[MAXLEN],	/* the bzip2 binary */
  recode_bin[MAXLEN],	/* the recode binary */
  metamail_bin[MAXLEN];	/* the metamail binary */
int 
  keep=0,		/* flag for keeping files in spool */
  pipeout=0,		/* flag for receiving to stdout (via pipe) */
  quiet=0,		/* quiet mode: no questions asked */
  client=1,		/* flag to determine client or server */
  ren=0,		/* flag for renaming files */
  pgppass=0,		/* flag if the pgp password is set as an env variable */
  preserve=0,		/* preserve pgp and tar attributes */
  nometamail=0;		/* metamail-usage flag */
mode_t 
  cmask;		/* umask value */


/* clean termination routine */
void cleanexit();

/* delete tmp-file */
void cleanup();

/* print short help usage text */
int usage(void);

/* list all spool files */
int list(struct senderlist *, int, char *, char *, int, int, char **);

/* receive a spool file */
void receive_sf(struct filelist *, char *, int);

/* check what to do when file with same name already exists */
int checkfile(int, char *, char *, char *, char *);

/* create detached pgp signature file */
int create_sigfile(const char *, const char *, const char *, char *);

/* translate NVT format file to Unix text format file */
void crlf2lf(FILE *, FILE *, const char *, const char *);

/* spawn a subprocess and direct output to a file */
int spawn(char **, const char *, mode_t);

/* shameless plug from GNU fileutils (getdate.c) :-) */
time_t get_date(char *, void *);

/* check or print pgp signature */
int check_signature(struct filelist *, char *, int);

/* renumber spool files */
void renumber(struct senderlist *);


int main(int argc, char *argv[]) {
  int
    i,			/* simple loop count */
    number,		/* flag for specifying a spool file number */
    listformat,		/* format of listing */
    del,		/* flag for deleting */
    all,		/* flag for receiving all */
    header,		/* flag for receiving only the header (to stdout) */
    id,			/* spool id */
    found,		/* flag for found spool id */
    opt;		/* option to test for */
  char
    *cp,		/* a simple string pointer */
    bounce[FLEN],	/* bounce address */
    from[FLEN],		/* from which sender */
    pgpring[FLEN],	/* flag for receiveing only signed files */
    tmp[3*MAXLEN],	/* temporary string */
    fname[MAXLEN],	/* displayble file name */
    bouncelist[MAXLEN],	/* list of spool files to bounce */
    pattern[MAXLEN];	/* file name pattern to search for */
  struct stat finfo;	/* information about a file */
  struct passwd *pwe;	/* password entry */
  struct filelist *flp;	/* file list pointer */
  struct senderlist
    *sls,		/* sender list start */
    *slp;		/* sender list pointer */
  FILE *inf;

  del=0;
  all=0;
  found=0;
  number=0;
  header=0;
  listformat=0;
  lll="";
  verbose="";
  *bounce=0;
  *pgpring=0;
  *userspool=0;
  *bouncelist=0;
  strcpy(from,"*");
  cmask=umask(0);
  umask(cmask);

  /* get program name */
  prg=argv[0];
  if ((cp=strrchr(prg,'/'))) prg=cp+1;

  /* get pgp password environment variable */
  if (getenv("PGPPASS")) {
    pgppass=1;
    pgpvm="+verbose=0";
  } else {
    pgppass=0;
    pgpvm="+verbose=1";
  }

  /* get metamail usage environment variable */
  if (getenv("NOMETAMAIL")) nometamail=1;
    
  /* scan the command line */
  while ((opt=getopt(argc,argv,"h?lLsndarRVvqpkHPS:f:Z:b:")) > 0) {
    switch (opt) {
	case ':':
        case 'h':
        case '?': exit(usage());
        case 's': listformat=1; break;
        case 'l': listformat=2; break;
        case 'L': listformat=3; break;
        case 'n': number=1; break;
        case 'r': ren=1; break;
        case 'R': ren=2; break;
        case 'd': del=1; break;
        case 'a': all=1; break;
        case 'P': pipeout=1; break;
        case 'k': keep=1; break;
        case 'q': quiet=1; break;
        case 'p': preserve=1; break;
        case 'H': header=1; break;
        case 'S': snprintf(MAXS(pgpring),"%s",optarg); break;
        case 'v': verbose="-v"; break;
        case 'f': snprintf(MAXS(from),"%s",optarg); break;
        case 'b': snprintf(MAXS(bounce),"%s",optarg); break;
        case 'Z': snprintf(MAXS(userspool),"%s",optarg); break;
        case 'V': message(prg,'I',"version "VERSION" revision "REVISION"");
	          exit(0);
    }
  }

  /* no arguments? */
  if (optind==argc && !all && !listformat) {
    listformat=2;
    lll="Type \"receive -a\" to receive all files or \"receive -h\" "
        "for a short help.\n\n";
  }

  /* too few arguments? */
  if ((argc-optind==0 && !all && ren<2 &&
       (ren || del || (!str_eq(from,"*") && !listformat))) ||
      (argc-optind>0 && ((*bounce && all) || ren==2)) ||
      (argc-optind<1 && *bounce && !all))
    exit(usage());

  /* get the own user name */
  if ((pwe=getpwuid(getuid())) == NULL)
    message(prg,'F',"cannot determine own user name");

  /* determine the spool directory */
  if (!*userspool) {
    if ((cp=getenv("SF_SPOOL"))) {
      snprintf(MAXS(userspool),"%s",cp);
    } else {
      snprintf(MAXS(userspool),"%s/.sfspool",pwe->pw_dir);
      if (stat(userspool,&finfo)<0 || !(finfo.st_mode&S_IFDIR))
	snprintf(MAXS(userspool),SPOOL"/%s",pwe->pw_name);
    }
  }
  if (*userspool=='Z') snprintf(MAXS(userspool),SPOOL"/%s",pwe->pw_name);

  /* does the spool directory exist? */
  if (stat(userspool,&finfo)<0 || (finfo.st_mode&S_IFMT)!=S_IFDIR) {
    snprintf(MAXS(tmp),"spool directory %s does not exist",userspool);
    errno=0;
    message(prg,'E',tmp);
    exit(1);
  }

  /* correct permissions for the spool directory? */
  if (!(finfo.st_mode&S_IRWXU) || finfo.st_uid!=getuid()) {
    snprintf(MAXS(tmp),
	     "no access to spool directory %s (wrong permissions)",
	     userspool);
    errno=0;
    message(prg,'F',tmp);
    exit(1);
  }

  /* are there any files to receive? */
  sls=scanspool(from);
  if (sls==NULL) {
    snprintf(MAXS(tmp),"no files found in spool directory %s",userspool);
    message(prg,'W',tmp);
    exit(1);
  }

  if (ren==2) {
    renumber(sls);
    sls=scanspool("");
  }

  /* set log file read status (st_atime) for xhoppel */
  snprintf(MAXS(tmp),"%s/log",userspool);
  inf=rfopen(tmp,"r");
  if (inf) {
    fgetl(tmp,inf);
    fclose(inf);
  }

  /* incompatible options? */
  if (*bounce) {
    if (listformat||ren||del||preserve)
      message(prg,'W',"you cannot use any other option "
		      "when bouncing a file - ignored");
    listformat=ren=del=0;
  }

  if (!str_eq(from,"*") && number)
      message(prg,'W',"ignoring -f option when specifying a spool number");

  if (del&&keep)
      message(prg,'F',"you cannot delete and keep a file at the same time");

  /* support programs defaults */
  strcpy(pgp_bin,PGP);
  strcpy(tar_bin,TAR);
  strcpy(gzip_bin,GZIP);
  strcpy(bzip2_bin,BZIP2);
  strcpy(recode_bin,RECODE);
  strcpy(metamail_bin,RECODE);

  /* look for environment variables */
  if ((cp=getenv("SF_PGP")))	  strcpy(pgp_bin,cp);
  if ((cp=getenv("SF_TAR")))	  strcpy(tar_bin,cp);
  if ((cp=getenv("SF_GZIP")))	  strcpy(gzip_bin,cp);
  if ((cp=getenv("SF_BZIP2")))	  strcpy(bzip2_bin,cp);
  if ((cp=getenv("SF_RECODE")))   strcpy(recode_bin,cp);
  if ((cp=getenv("SF_METAMAIL"))) strcpy(metamail_bin,cp);

  /* do the support programs really exist? */
  if (access(pgp_bin,X_OK)<0)	   strcpy(pgp_bin,"pgp");
  if (access(tar_bin,X_OK)<0)	   strcpy(tar_bin,"tar");
  if (access(gzip_bin,X_OK)<0)	   strcpy(gzip_bin,"gzip");
  if (access(bzip2_bin,X_OK)<0)	   strcpy(bzip2_bin,"bzip2");
  if (access(recode_bin,X_OK)<0)   strcpy(recode_bin,"recode");
  if (access(metamail_bin,X_OK)<0) strcpy(metamail_bin,"metamail");

  /* enable simple interrupt handler */
  signal(SIGTERM,cleanexit);
  signal(SIGABRT,cleanexit);
  signal(SIGQUIT,cleanexit);
  signal(SIGHUP,cleanexit);
  signal(SIGINT,cleanexit);

  /* set tmp file names */
  tmpdir=mktmpdir(strlen(verbose));
  snprintf(MAXS(tartmp),"%s/receive.tar",tmpdir);
  snprintf(MAXS(error_log),"%s/error.log",tmpdir);


  /* list files? */
  if (listformat) {
    if (list(sls,listformat,from,pgpring,number,argc,argv)<0) {
      snprintf(MAXS(tmp),"no files in spool directory %s",userspool);
      message(prg,'W',tmp);
    }
    cleanup();
    exit(0);
  }

  /* number specified? */
  if (number) {
   
    /* loop over all args */
    for (i=optind; i<argc; i++) {
      id=atoi(argv[i]);

      /* loop over sender list */
      for (slp=sls, found=0; slp!=NULL && found==0; slp=slp->next) {
       
	/* loop over files list */
        for (flp=slp->flist; flp!=NULL && found==0; flp=flp->next) {
	 
          /* spool id found and spool file complete? */
	  if (flp->id==id && flp->csize==flp->tsize) {
	   
	    /* delete, bounce or receive spool file? */
	    if (del)
	      delete_sf(flp,1);
	    else if (*bounce) {
	      if (*bouncelist) {
		snprintf(MAXS(tmp),"%s %d",bouncelist,id);
		strcpy(bouncelist,tmp);
	      } else
		snprintf(MAXS(bouncelist),"%d",id);
	    } else 
	      receive_sf(flp,pgpring,header);

	    found=1;

	  }
	}
      }

      /* not found? */
      if (!found && id) {
        snprintf(MAXS(tmp),"spool file #%d not found",id);
	message(prg,'W',tmp);
      }

    }

  }  else  /* file name specified */ {
   
    /* loop over all args */
    for (i=optind; i<argc || all; i++) {
      if (all)
        strcpy(pattern,"*");
      else
      	strcpy(pattern,argv[i]);

      /* loop over sender list */
      for (slp=sls; slp!=NULL; slp=slp->next) {
       
	/* loop over files list */
	for (flp=slp->flist; flp!=NULL; flp=flp->next) {
	 
	  /* spool file incomplete? */
	  if (flp->csize!=flp->tsize) continue;

	  /* translate UTF-7 name to the displayable file name */
	  utf2iso(1,NULL,fname,NULL,flp->fname);

	  /* match? */
	  if (simplematch(fname,pattern,0)==1) {
	   
	    /* delete, bounce or receive spool file? */
	    if (del)
	      delete_sf(flp,1);
	    else if (*bounce) {
	      if (*bouncelist) {
		snprintf(MAXS(tmp),"%s %d",bouncelist,flp->id);
		strcpy(bouncelist,tmp);
	      } else
		snprintf(MAXS(bouncelist),"%d",flp->id);
	    } else
	      receive_sf(flp,pgpring,header);

	    found=1;

	  }
	}
      }

      /* not found? */
      if (!found && !all) {
        snprintf(MAXS(tmp),"file %s not found",pattern);
	message(prg,'W',tmp);
      }

      all=0;

    }
  }

  /* files to bounce? */
  if (*bounce && *bouncelist) {
    if (keep)
      snprintf(MAXS(tmp),
	       "sendfile -bk=y %s %s %s",verbose,bouncelist,bounce);
    else
      snprintf(MAXS(tmp),
	       "sendfile -bk=n %s %s %s",verbose,bouncelist,bounce);
    if (*verbose) printf("call: %s\n",tmp);
    system(tmp);
  }

  cleanup();
  exit(0);
}


/*
 * list - list spool files with attributes
 *
 * INPUT:  sls		- sender list start
 *         format	- format of listing
 * 	   from		- select from user
 *         pgpring	- pgp ring file
 * 	   number	- getopt number flag
 * 	   argc		- from main()
 * 	   argv		- from main()
 *
 * RETURN: 0 if files found, -1 if no files to list
 */
int list(struct senderlist *sls, int format, char *from, char *pgpring,
	 int number, int argc, char *argv[]) {
  int
    i,				/* simple loop counter */
    found,			/* flag for files found */
    mff,			/* matching file flag */
    fff;			/* first file flag */
  char
    *cp1,*cp2,	    		/* simple character pointer */
    line[MAXLEN],		/* line to read in */
    showtar[MAXLEN],		/* shell command to look inside tar archive */
    show_fname[MAXLEN];		/* file name to display */
  struct senderlist *slp;	/* sender list pointer */
  struct filelist *flp;		/* file list pointer */
  FILE *pp;			/* pipe input stream */

  found=0;
  
  /* loop over sender list */
  for (slp=sls; slp!=NULL; slp=slp->next) {
   
    /* match this sender? */
    if (simplematch(slp->from,from,1)==1) {
     
      /* loop over files list */
      for (flp=slp->flist,fff=1; flp!=NULL; flp=flp->next) {
       
	/* file not completly transfered? */
	if (flp->csize!=flp->tsize) continue;
	
	/* wanted by CLI options? */
	if (optind<argc) {
	  mff = 0;
	  for (i=optind; i<argc; i++) {
	    if (number) {
	      if (atoi(argv[i])==flp->id) 
		{ mff = 1; break; }
	    } else {
	      if (simplematch(flp->fname,argv[i],0)==1) 
		{ mff = 1; break; }
	    }
	  }
	  
	  /* file is not selected by the user, try next one */
	  if (!mff) continue;
	}

	if (!found && format>1) 
	  printf("\nFiles in spool directory %s :\n",userspool);
	
	found=1;
	
	/* first file from this sender? */
	if (fff) {
	  fff=0;

	  /* print from header */
	  printf("\nFrom %s\n",slp->from);
	  for (i=1; i<80 && i<=strlen(slp->from)+5; i++) printf("-");
	  printf("\n");

	}

	/* print spool file informations */
	utf2iso(1,NULL,show_fname,NULL,flp->fname);
	printf("%3d) %s  %8lu kB  %s",
	       flp->id,flp->rdate,(flp->osize+1023)/1024,show_fname);

	if (format>1) {
	 
	  /* tar archive, MIME file or encrypted? */
	  if (flp->flags&F_TAR && flp->flags&F_CRYPT)
	    printf(" (archive,encrypted)");
	  else if (flp->flags&F_MIME && flp->flags&F_CRYPT)
	    printf(" (MIME,encrypted)");
	  else if (flp->flags&F_MIME)
	    printf(" (MIME)");
	  else if (flp->flags&F_TAR)
	    printf(" (archive)");
	  else if (flp->flags&F_CRYPT)
	    printf(" (encrypted)");
	}

	printf("\n");

	/* check signature */
	if (*(flp->sign) && format>1) check_signature(flp,pgpring,1);
	
	/* comment available? */
	cp1=flp->comment;
	if (*cp1 && format>1) {
	  while ((cp2=strchr(cp1,'\n'))) {
	    *cp2=0;
	    printf("     \"%s\"\n",cp1);
	    cp1=cp2+1;
	  }
	  printf("     \"%s\"\n",cp1);
	}

	/* on verbose mode look inside tar */
	if (flp->flags&F_TAR && format==3 &&
	    !(flp->flags&F_CRYPT && (quiet || preserve) && !pgppass)) {
	 
	  /* encrypted, compressed or normal tar file? */
	  if (flp->flags&F_CRYPT)
	    snprintf(MAXS(showtar),"%s %s -f < %s/%d.d | %s tvf -",
		      pgp_bin,pgpvm,userspool,flp->id,tar_bin);
	  else if (flp->flags&F_COMPRESS) {
	    if (str_eq(flp->compress,S_BZIP2))
	      snprintf(MAXS(showtar),"%s -d < %s/%d.d | %s tvf -",
		       bzip2_bin,userspool,flp->id,tar_bin);
	    else
	      snprintf(MAXS(showtar),"%s -d < %s/%d.d | %s tvf -",
		       gzip_bin,userspool,flp->id,tar_bin);
	  } else
	    snprintf(MAXS(showtar),"%s tvf %s/%d.d",tar_bin,userspool,flp->id);

	  /* sneak inside... */
	  if (*verbose) printf("call: %s\n",showtar);
	  if ((pp=popen(showtar,"r")) == NULL)
	    message(prg,'E',"contents of archive is not accessible");
	  else {
	    if (flp->flags&F_CRYPT && !pgppass && fgetl(line,pp))
	      printf("\n\n     %s",line);
	    while (fgetl(line,pp)) printf("     %s",line);
	    if (flp->flags&F_CRYPT && !pgppass) printf("\n");
	  }
	  pclose(pp);

	}
      }
    }
  }

  if (found) {
    printf("\n%s",lll);
    return(0);
  }
  else
    return(-1);

}


/*
 * receive_sf - receive a spool file
 *
 * INPUT:  flp     - file list element
 *         pgpring - pgp ring file (force pgp signature checking)
 * 	   header  - flag for receiving onnly the header to stdout
 */
void receive_sf(struct filelist *flp, char *pgpring, int header) {
  int
    utf,			/* return code from utf2iso */
    terr,			/* tar error flag */
    tom;			/* tar overwrite message flag */
  char
    *cp,			/* simple character pointer */
    *sad[10],			/* spawn argument descriptor */
    answer[FLEN],		/* answer string */
    line[MAXLEN],		/* one line of text */
    tmp[2*MAXLEN],		/* temporary string */
    tmpfile[MAXLEN],		/* temporary file name */
    cmd[2*MAXLEN],		/* command string for system() */
    sfile[MAXLEN],		/* spool data file */
    danger[OVERSIZE],		/* dangerous file names */
    fname[MAXLEN],		/* file name to write */
    nname[MAXLEN],		/* file name in display format */
    sname[MAXLEN];		/* file name in shell handling format */
  static char
    overwrite='n';		/* overwrite mode */
  struct stat finfo;		/* information about a file */
  struct utimbuf utb;		/* binary time */
  time_t dummy;			/* dummy arg for localtime() */
  FILE
    *pp,			/* pipe input stream */
    *inf,			/* spool data file to read */
    *outf;			/* file to create */

  tom=0;
  terr=0;
  *danger=0;
  *answer=0;

  /* oops? */
  if (flp==NULL) return;

  /* translate UTF-7 file name */
  utf=utf2iso(1,fname,nname,sname,flp->fname);
  fname[FLEN-1]=nname[FLEN-1]=sname[FLEN-1]=0;
  
  if (*pgpring) {
    switch (check_signature(flp,pgpring,0)) {
	case 1:  break;
	case 0:  snprintf(MAXS(tmp),"no signature found for '%s'",nname);
                 errno=0;
                 message(prg,'E',tmp);
                 return;
	case -1: snprintf(MAXS(tmp),"no public key found to check "
			  "signature for '%s'",nname);
                 errno=0;
                 message(prg,'E',tmp);
                 return;
	case -2: snprintf(MAXS(tmp),"bad signature for '%s'",nname);
                 errno=0;
                 message(prg,'E',tmp);
		 return;
        default: return;
    }
  }

  /* determine overwrite mode */
  if (quiet) overwrite='Y';
  if (!strchr("YNS",overwrite)) overwrite='n';

  (void) localtime(&dummy);

  /* show only the header? */
  if (header) {
    snprintf(MAXS(tmp),"%s/%d.h",userspool,flp->id);
    printf("%d) %s\n",flp->id,nname);
    inf=rfopen(tmp,"r");
    while (fgetl(line,inf)) printf("%s",line);
    printf("\n");
    fclose(inf);
    return;
  }
  
  /* pipe to stdout? */
  if (pipeout) {
    
    /* encrypted spool file? */
    if (flp->flags&F_CRYPT) {
      snprintf(MAXS(cmd),"%s %s -f < %s/%d.d",pgp_bin,pgpvm,userspool,flp->id);
      if (*verbose) printf("call: %s\n",cmd);
      if (system(cmd)!=0) {
        errno=0;
	snprintf(MAXS(tmp),"cannot decrypt '%s' :",nname);
	message(prg,'E',tmp);
	return;
      }
    }
    /* compressed spool file? */
    else if (flp->flags&F_COMPRESS) {
      if (str_eq(flp->compress,S_BZIP2))
	snprintf(MAXS(cmd),"%s -d < %s/%d.d",bzip2_bin,userspool,flp->id);
      else
	snprintf(MAXS(cmd),"%s -d < %s/%d.d",gzip_bin,userspool,flp->id);
      if (*verbose) printf("call: %s\n",cmd);
      if (system(cmd)!=0 && !(flp->flags&F_TAR)) {
        errno=0;
	snprintf(MAXS(tmp),"cannot decompress '%s' :",nname);
	message(prg,'E',tmp);
	return;
      }
    }
    else /* copy spool file to stdout */ {
      
      /* copy file */
      snprintf(MAXS(tmp),"%s/%d.d",userspool,flp->id);
      if (fcopy(tmp,"",0)<0) {
        errno=0;
	snprintf(MAXS(tmp),"cannot read '%s'",nname);
        message(prg,'E',tmp);
	return;
      }

    }

    /* delete tar spool file if required */
    if (!keep) delete_sf(flp,0);

    return;
  }

  /* tar file to receive? */
  if (flp->flags&F_TAR) {
   
    /* save file in transfer shape? */
    if (preserve) {
     
      /* set new file name */
      fname[FLEN-12]=nname[FLEN-12]=sname[FLEN-12]=0;
      strcat(fname,".tar");
      strcat(nname,".tar");
      strcat(sname,".tar");
      if (flp->flags&F_COMPRESS) {
        strcat(fname,".gz");
	strcat(nname,".gz");
	strcat(sname,".gz");
      }
      if (flp->flags&F_CRYPT) {
        strcat(fname,".pgp");
	strcat(nname,".pgp");
	strcat(sname,".pgp");
      }

      /* if file with same name already exists check what to do */
      if (!quiet && checkfile(utf,fname,nname,sname,&overwrite)) return;

      /* copy file */
      snprintf(MAXS(tmp),"%s/%d.d",userspool,flp->id);
      if (fcopy(tmp,fname,0666&~cmask)<0) {
        snprintf(MAXS(tmp),"cannot receive '%s'",nname);
	errno=0;
        message(prg,'E',tmp);
	return;
      }

      /* pgp signature to save? */
      create_sigfile(flp->sign,fname,nname,&overwrite);

      if (!keep) delete_sf(flp,0);
      snprintf(MAXS(tmp),"'%s' received",nname);
      message(prg,'I',tmp);

      return;
    }

    /* encrypted tar spool file? */
    if (flp->flags&F_CRYPT) {
      
      snprintf(MAXS(cmd),"%s %s -f < %s/%d.d > %s",
	      pgp_bin,pgpvm,userspool,flp->id,tartmp);

      /* create temporary decrypted tar file */
      if (*verbose) printf("call: %s\n",cmd);
      system(cmd);
      if (stat(tartmp,&finfo)<0 || finfo.st_size==0) {
        errno=0;
	snprintf(MAXS(tmp),"cannot decrypt '%s' :",nname);
	message(prg,'E',tmp);
	return;
      }

      if (!pgppass) printf("\n\n");

    }

    /* test on (dangerous) symbolic links in tar file */

    /* compressed, encrypted or normal tar file? */
    if (flp->flags&F_COMPRESS) {
      if (str_eq(flp->compress,S_BZIP2))
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s tvf -",
		 bzip2_bin,userspool,flp->id,tar_bin);
      else
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s tvf -",
		 gzip_bin,userspool,flp->id,tar_bin);
    } else if (flp->flags&F_CRYPT) {
      snprintf(MAXS(cmd),"%s tvf %s",tar_bin,tartmp);
    } else {
      snprintf(MAXS(cmd),"%s tvf %s/%d.d",tar_bin,userspool,flp->id);
    }

    /* open pipe to read tar file-info */
    if (*verbose) printf("call: %s\n",cmd);
    if ((pp=popen(cmd,"r")) == NULL) {
      message(prg,'E',"cannot open spool file for reading");
      return;
    }

    /* loop over all files in tar archive */
    while (fgetl(line,pp)) {
      if ((cp=strchr(line,'\n'))) *cp=0;
     
      /* is it a (dangerous) symbolic link? */
      if (*line == 'l') {
	if (strlen(danger)+strlen(line)+4 > sizeof(danger)) {
	  snprintf(MAXS(tmp),
		   "archive %s has too many dangerous file names inside",
		   nname);
	  message(prg,'F',tmp);
	}
	strcat(danger,"\n  ");
	strcat(danger,line);
      }

    }

    pclose(pp);

    /* test on dangerous file namess in tar file */

    /* compressed, encrypted or normal tar file? */
    if (flp->flags&F_COMPRESS) {
      if (str_eq(flp->compress,S_BZIP2))
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s tf -",
		 bzip2_bin,userspool,flp->id,tar_bin);
      else
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s tf -",
		 gzip_bin,userspool,flp->id,tar_bin);
    } else if (flp->flags&F_CRYPT) {
      snprintf(MAXS(cmd),"%s tf %s",tar_bin,tartmp);
    } else {
      snprintf(MAXS(cmd),"%s tf %s/%d.d",tar_bin,userspool,flp->id);
    }

    /* open pipe to read tar file-info */
    if (*verbose) printf("call: %s\n",cmd);
    if ((pp=popen(cmd,"r")) == NULL) {
      message(prg,'E',"cannot open spool file for reading");
      return;
    }

    /* loop over all files in tar archive */
    while (fgetl(fname,pp)) {
     
      /* does the file already exist? */
      if ((cp=strchr(fname,'\n'))) *cp=0;
      if (overwrite!='Y' && stat(fname,&finfo)==0 && !S_ISDIR(finfo.st_mode)) {
        if (!tom) printf("Archive '%s' will overwrite:\n",nname);
        tom=1;
        printf("  %s\n",fname);
      }

      /* is it a dangerous file name? */
      if (strstr(fname,"../") || strstr(fname,"/.") || fname[0]=='/' ||
	  (fname[0]=='.' && fname[1]!='/')) {
	if (strlen(danger)+strlen(fname)+4 > sizeof(danger)) {
	  snprintf(MAXS(tmp),
		   "archive %s has too many dangerous file names inside",
		   nname);
	  message(prg,'F',tmp);
	}
        strcat(danger,"\n  ");
	strcat(danger,fname);
      }

    }

    pclose(pp);

    /* ask user for overwriting */
    if (tom && overwrite!='Y') {
      printf("Overwrite (yYnN)? ");
      fgetl(answer,stdin);
      overwrite=answer[0];
      if (toupper(overwrite)!='Y') return;
    }

    /* ask user for extracting dangerous files */
    if (*danger && overwrite!='Y') {
      printf("Archive contains files with dangerous names:%s\n",danger);
      printf("Continue with receiving (yYnN)? ");
      fgetl(tmp,stdin);
      overwrite=tmp[0];
      if (toupper(overwrite)!='Y') return;
    }

    /* receive from tar file */
    snprintf(MAXS(tmp),"receiving from archive '%s' :",nname);
    message(prg,'I',tmp);

    /* compressed, encrypted or normal tar file? */
    if (flp->flags&F_COMPRESS) {
      if (str_eq(flp->compress,S_BZIP2))
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s xvf - 2>%s",
		 bzip2_bin,userspool,flp->id,tar_bin,error_log);
      else
	snprintf(MAXS(cmd),"%s -d < %s/%d.d | %s xvf - 2>%s",
		 gzip_bin,userspool,flp->id,tar_bin,error_log);
    } else if (flp->flags&F_CRYPT)
      snprintf(MAXS(cmd),"%s xvf %s 2>%s",tar_bin,tartmp,error_log);
    else
      snprintf(MAXS(cmd),"%s xvf %s/%d.d 2>%s",
	       tar_bin,userspool,flp->id,error_log);

    /* receive tar archive and check for errors */
    terr=0;
    if (*verbose) printf("call: %s\n",cmd);
    system(cmd);
    if (stat(error_log,&finfo)==0 && finfo.st_size>10) {
      errno=0;
      inf=rfopen(error_log,"r");
      while (fgetl(line,inf)) {
	if (str_beq(TAR": ",line) && 
	    !simplematch(line,TAR": Could not create symlink*File exists*",1)) {
	  if (!terr) {
	    terr=1;
	    snprintf(MAXS(tmp),"errors while receive '%s' :",nname);
	    message(prg,'E',tmp);
	  }
	  printf("%s",line);
	}
      }
      fclose(inf);
    }

    /* was there an error with tar? */
    if (terr) {
      snprintf(MAXS(tmp),"leaving '%s' in spool intact",nname);
      message(prg,'I',tmp);
    } else {
     
      /* delete tar spool file */
      if (!keep) delete_sf(flp,0);
    }

    cleanup();
    return;
  }

  /* receive non-tar file */

  /* save file in transfer shape? */
  if (preserve && flp->flags&F_CRYPT) {
    fname[FLEN-5]=nname[FLEN-5]=sname[FLEN-5]=0;
    strcat(fname,".pgp");
    strcat(nname,".pgp");
    strcat(sname,".pgp");
  }

  /* if file with same name already exists check what to do */
  if (!quiet && checkfile(utf,fname,nname,sname,&overwrite)) return;

  /* leading . in file name? */
  if (!strchr("yYr",overwrite) && fname[0]=='.') {
   
    /* skipping? */
    if (overwrite=='S' || overwrite=='N') return;

    /* ask user what to do */
    printf("'%s' starts with a '.' which may be dangerous\n",nname);
    printf("Receive it anyway (yY), rename (r) or skip (sS) it? ");
    fgetl(answer,stdin);
    overwrite=answer[0];

    /* skipping this file? */
    if (toupper(overwrite)=='S') return;

    /* request for renaming? */
    if (overwrite=='r') {
      sprintf(fname,"Rename '%s' to? ",nname);
      if (getpromptline(fname,MAXLEN-1)==NULL || *fname=='\n' || *fname==0)
	strcpy(fname,nname);
      if ((cp=strrchr(fname,'\n'))) *cp=0;
      strcpy(nname,fname);
    }

  }

  /* safety fallback: try to delete an old file with the same name */
  unlink(fname);
  if (stat(fname,&finfo)==0) {
    snprintf(MAXS(tmp),"cannot create '%s' : "
	     "file does already exist and is not deletable",fname);
    errno=0;
    message(prg,'E',tmp);
    return;
  }

  /* save file encrypted? */
  if (preserve && flp->flags&F_CRYPT) {
   
    /* copy file */
    snprintf(MAXS(tmp),"%s/%d.d",userspool,flp->id);
    if (fcopy(tmp,fname,0666&~cmask)<0) {
      snprintf(MAXS(tmp),"cannot receive '%s'",nname);
      errno=0;
      message(prg,'E',tmp);
      return;
    }

    if ((flp->flags&F_SOURCE || flp->flags&F_TEXT) && !quiet) {
      snprintf(MAXS(tmp),
	       "'%s' has a SOURCE or TEXT attribute, you have to decode it "
	       "after pgp-decrypting with:  recode %s:"CHARSET" '%s'",
	       nname,flp->charset,nname);
      message(prg,'W',tmp);
    }

    if (flp->flags&F_MIME && !quiet) {
      snprintf(MAXS(tmp),
	       "'%s' has the MIME attribute, you have to run it through"
	       "metamail after pgp-decrypting",nname);
      message(prg,'W',tmp);
    }

    /* pgp signature to save? */
    create_sigfile(flp->sign,fname,nname,&overwrite);

    if (!keep) delete_sf(flp,0);
    snprintf(MAXS(tmp),"'%s' received",nname);
    message(prg,'I',tmp);

    return;
  }

  /* spool file compressed or encrypted? */
  if (flp->flags&F_COMPRESS || flp->flags&F_CRYPT) {
   
    /* source, text or MIME format? */
    if ((flp->flags&F_SOURCE) || (flp->flags&F_TEXT) || (flp->flags&F_MIME)) {
     
      /* open pipe to uncompress or decrypt spool file */
      if (flp->flags&F_COMPRESS) {
	if (str_eq(flp->compress,S_BZIP2))
	  snprintf(MAXS(cmd),"%s -d < %s/%d.d",bzip2_bin,userspool,flp->id);
	else
	  snprintf(MAXS(cmd),"%s -d < %s/%d.d",gzip_bin,userspool,flp->id);
      } else if (flp->flags&F_CRYPT)
	  snprintf(MAXS(cmd),
		   "%s %s -f < %s/%d.d",pgp_bin,pgpvm,userspool,flp->id);
      if (*verbose) printf("call: %s\n",cmd);
      if ((pp=popen(cmd,"r")) == NULL) {
        message(prg,'E',"cannot open spool file for reading");
        return;
      }

      /* open output file */
      if (!(outf=rfopen(fname,"w"))) {
	pclose(pp);
	printf("\n"); 
	snprintf(MAXS(tmp),"cannot open '%s' for writing",nname);
        message(prg,'E',tmp);
        return;
      }

      /* translate CR LF to LF and write to output file */
      crlf2lf(pp,outf,fname,nname);

      /* ready */
      pclose(pp);
      fclose(outf);

      if (flp->flags&F_CRYPT && !pgppass) printf("\n\n");

    } else  /* binary format */ {
     
      snprintf(MAXS(sfile),"%s/%d.d",userspool,flp->id);

      /* try to create destination file */
      /* open output file */
      if (!(outf=rfopen(fname,"w"))) {
        snprintf(MAXS(tmp),"cannot open '%s' for writing",nname);
        message(prg,'E',tmp);
        return;
      }
      fclose(outf);

      /* compressed spool file? */
      if (flp->flags&F_COMPRESS) {
       
	/* uncompress and receive binary file */
	
	/* stupid bzip2 NEEDS file from stdin */
	if (str_eq(flp->compress,S_BZIP2)) {
	  strcpy(tmp,fname);
	  if ((cp=strrchr(tmp,'/')))
	    *++cp=0;
	  else
	    *tmp=0;
	  snprintf(MAXS(tmpfile),"%sreceive-%d.tmp",tmp,(int)getpid());
	  snprintf(MAXS(tmp),"%s -d < %s > %s",bzip2_bin,sfile,tmpfile);
	  if (system(tmp)) {
	    snprintf(MAXS(tmp),"call to %s failed, cannot receive '%s'",
		     bzip2_bin,nname);
	    message(prg,'E',tmp); 
	    return;
	  }
	  if (rename(tmpfile,fname)<0) {
	    snprintf(MAXS(tmp),"cannot write to '%s'",nname);
	    message(prg,'E',tmp); 
	    unlink(tmpfile);
	    return;
	  }
	  
	} else { /* gzip format */
	  
	  sad[0]=gzip_bin;
	  sad[1]="-dc";
	  sad[2]=sfile;
	  sad[3]=NULL;

	  if (spawn(sad,fname,cmask)<0) {
	    errno=0;
	    snprintf(MAXS(tmp),
		     "call to %s failed, cannot receive '%s'",sad[0],nname);
	    message(prg,'E',tmp); 
	    return;
	  }
	  
	}

      }

      /* encrypted spool file? */
      if (flp->flags&F_CRYPT) {
       
	snprintf(MAXS(cmd),"%s %s -f < %s > '%s'",pgp_bin,pgpvm,sfile,fname);
	if (*verbose) printf("call: %s\n",cmd);
	if (system(cmd)!=0) {
	  errno=0;
	  snprintf(MAXS(tmp),"cannot receive '%s', pgp failed",nname);
	  message(prg,'E',tmp);
	  unlink(fname);
	  return;
	}
	if (!pgppass) printf("\n\n");
			    
      }
    }

  } else   /* not compressed and not encrypted or keep encryption */ {
   
    /* source, text or MIME format? */
    if (((flp->flags&F_SOURCE) || 
	 (flp->flags&F_TEXT)   ||
	 (flp->flags&F_MIME))  && 
       !(flp->flags&F_CRYPT)) {
     
      /* open input file */
      snprintf(MAXS(sfile),"%s/%d.d",userspool,flp->id);
      if ((inf=rfopen(sfile,"r")) == NULL) {
        message(prg,'E',"cannot open spool file for reading");
        return;
      }

      /* open output file */
      if ((outf=rfopen(fname,"w")) == NULL) {
        snprintf(MAXS(tmp),"cannot open '%s' for writing",nname);
        message(prg,'E',tmp);
        fclose(inf);
        return;
      }

      /* translate CR LF to LF and write to output file */
      crlf2lf(inf,outf,fname,nname);

      /* ready */
      fclose(inf);
      fclose(outf);

    } else   /* binary file */ {
     
      /* copy file */
      snprintf(MAXS(tmp),"%s/%d.d",userspool,flp->id);
      if (fcopy(tmp,fname,0666&~cmask)<0) {
        snprintf(MAXS(tmp),"cannot receive '%s'",nname);
	errno=0;
        message(prg,'E',tmp);
	return;
      }
    }
  }

  /* pgp signature to save? */
/* no more needed, because already saved in transfer shape!
  if (preserve) create_sigfile(flp->sign,fname,nname,&overwrite);
*/
  /* executable flag set? */
  if (flp->flags&F_EXE) chmod(fname,(S_IRWXU|S_IRWXG|S_IRWXO)&~cmask);

  snprintf(MAXS(tmp),"'%s' received",nname);
  message(prg,'I',tmp);

  /* foreign character set in text file? */
  if ((flp->flags&F_TEXT) && !str_eq(flp->charset,CHARSET)) {
   
    /* call GNU recode */
    snprintf(MAXS(tmp),"%s:"CHARSET,flp->charset);
    sad[0]=recode_bin;
    sad[1]=tmp;
    sad[2]=fname;
    sad[3]=NULL;
    if (spawn(sad,NULL,cmask)<0) {
      snprintf(MAXS(tmp),
	       "call to %s failed, cannot translate character set in '%s'",
	       recode_bin,nname);
      message(prg,'E',tmp);
    }

  }

  /* set the original date */
  if (*(flp->date)) {
    if (!strstr(flp->date,"UTC")) strcat(flp->date," UTC");
    utb.actime=utb.modtime=get_date(flp->date,NULL);
    utime(fname,&utb);
  }

  /* MIME? */
  if (flp->flags&F_MIME) {
    
    /* metamail call allowed? */
    if (nometamail) {
      snprintf(MAXS(tmp),
	       "'%s' is a MIME file, you have to run it through metamail",
	       nname);
      message(prg,'I',tmp);
      if (!keep) delete_sf(flp,0);
    } else {
      
      /* call metamail */
      sad[0]=metamail_bin;
      sad[1]=fname;
      sad[2]=NULL;
      if (spawn(sad,NULL,cmask)<0) {
	snprintf(MAXS(tmp),
		 "call to %s failed, keeping local file '%s'",
		 metamail_bin,nname);
	message(prg,'E',tmp);
      } else {
      
	/* delete spool file and received MIME-file*/
	if (!keep) delete_sf(flp,0);
	if (!nometamail) unlink(fname);

      }
    }

  } else
      
    /* delete spool file */
    if (!keep) delete_sf(flp,0);
    
}


/*
 * crlf2lf - translate NVT format file to Unix text format file
 *
 * INPUT:  inf	  - file to read from
 *         outf   - file to write to
 *         fname  - file name to write
 */
void crlf2lf(FILE *inf, FILE *outf, const char *fname, const char *nname) {
  int c1,c2;		/* characters to read in */
  char tmp[MAXLEN];	/* temporary string */

  /* read first char */
  c1=fgetc(inf);

  /* loop until EOF */
  while ((c2=fgetc(inf)) != EOF) {
   
    /* crlf? */
    if (c1=='\r' && c2=='\n') {
     
      /* write lf */
      if(fputc(c2,outf)==EOF) {
        snprintf(MAXS(tmp),"cannot write to %s",nname);
        message(prg,'E',tmp);
        return;
      }

      /* read next char */
      if ((c2=fgetc(inf)) == EOF) return;

    } else {
     
      /* write char */
      if(fputc(c1,outf)==EOF) {
        snprintf(MAXS(tmp),"cannot write to %s",nname);
        message(prg,'E',tmp);
        return;
      }

    }
    c1=c2;
  }

  /* write last char */
  if(fputc(c1,outf)==EOF) {
    snprintf(MAXS(tmp),"cannot write to %s",nname);
    message(prg,'E',tmp);
    return;
  }

}


/*
 * spawn  - spawn a subprocess and direct output to a file
 *
 * INPUT:  sad     - spawn argument descriptor
 *         output  - output file
 *         cmask   - protection mask
 *
 * RETURN: 0 on success, -1 on failure
 *
 */
int spawn(char **sad, const char *output, mode_t cmask) {
  int status,		/* fork status */
      fd;		/* output file descriptor */
  pid_t pid;		/* process id */

  /* spawn subprocess */
  pid=fork();

  /* failed? */
  if (pid<0) {
    message(prg,'E',"cannot fork subprocess");
    return(-1);
  }

  /* is this the subprocess? */
  if (pid==0) {
   
    /* redirect stdout? */
    if (output) {
     
      /* close stdout */
      close(1);

      /* open output file as stdout */
      fd=creat(output,0666&~cmask);
      if (fd!=1) {
        errno=0;
	message(prg,'E',"file descriptor mismatch");
	cleanup();
	exit(1);
      }

    }

    /* execute program */
    execvp(sad[0],sad);

    /* oops - failed */
    message(prg,'F',"execvp() failed!");
    cleanup();
    exit(2);
  }

  /* wait for termination of subprocess */
#ifdef NEXT
  wait(&status);
#else
  waitpid(pid,&status,0);
#endif

  /* error in subprocess? */
  if (status) return(-1);

  return(0);
}


/*
 * cleanexit  - clean termination routine (only called by signal handler)
 */
void cleanexit() {
 
  printf("\r\n");
  cleanup();
  exit(4);
}


/*
 * cleanup  - delete tmp files
 */
void cleanup() {
  /* ignore all relevant signals */
  signal(SIGTERM,SIG_IGN);
  signal(SIGABRT,SIG_IGN);
  signal(SIGQUIT,SIG_IGN);
  signal(SIGHUP,SIG_IGN);
  signal(SIGINT,SIG_IGN);

#ifndef DEBUG
  rmtmpdir(tmpdir);
#endif

}


/*
 * checkfile  - check file name before writing
 *
 * INPUT:  fname	- real file name
 * 	   nname	- normal file name to display
 * 	   sname	- file name for shell usage
 * 	   overwrite	- overwriting, renaming or skipping
 *
 * OUTPUT: fname	- new real file name
 * 	   nname	- new normal name to display
 * 	   sname	- file name for shell usage
 * 	   overwrite	- overwriting, renaming or skipping
 *
 * RETURN: 0 if renaming or overwriting
 *         1 if skipping
 */
int checkfile(int utf, char *fname, char *nname, char *sname, char *overwrite) {
  char
    *cp,			/* a simple string pointer */
    tmp[MAXLEN],		/* temporary string */
    answer[FLEN];		/* answer string */
  static char storeformat='c';	/* file name storing format */
  struct stat finfo;		/* information about a file */

  if (quiet) storeformat='C';

  /* rename files before receiving? */
  if (ren) {
     sprintf(fname,"Rename '%s' to? ",nname);
    if (getpromptline(fname,MAXLEN-1)==NULL || *fname=='\n' || *fname==0) 
      strcpy(fname,nname);
    if ((cp=strrchr(fname,'\n'))) *cp=0;
    strcpy(nname,fname);
  } else {
   
    /* need user request? */
    if (!strchr("CNS",storeformat)) {
     
      /* found Unicode or 0x0? */
      if (utf==1) {
        printf("The next file name contains characters which are not allowed "
	       "in Unix.\nThese characters have been substituted with '_'.\n");
      }

      /* found control or meta characters? */
      if (utf&2) {
          printf("The next file name contains characters which may cause "
		 "problems with your shell\n or may do strange things with "
		 "your terminal.\n"
	         "These characters have been substituted with '_'.\n");
	if (utf&1)
	  printf("Non-valid characters for Unix file names have been substituted"
		 ", too.\n");

	/* let user choose file name format */
	/*if (strcmp(nname,sname)!=0) */
	printf("Save the next file as:\n");
	printf("   (c)omplete file name with control code characters "
	       "(not displayable)\n");
	printf("   (n)ormal file name: '%s'\n",nname);
	printf("   (u)ntainted file name:  '%s'\n",sname);
	printf("   (s)kip it\n");
	printf("   (r)ename it\n");
	do {
	  printf("c or n may cause severe security problems "
		 "(Kids, don't try this at home!) !\n");
	  printf("Select one of c n u s r (or C N U S R for no more questions): ");
	  fgetl(tmp,stdin);
	  storeformat=tmp[0];
	} while (strchr("cnusrCNUSR",storeformat)==NULL);

      }
    }

    /* set file name */
    switch (toupper(storeformat)) {
      case 'S': return(1);
      case 'N': strcpy(fname,nname); break;
      case 'U': strcpy(fname,sname);
                strcpy(nname,sname); break;
      case 'R': sprintf(fname,"Rename '%s' to? ",nname);
                if (getpromptline(fname,MAXLEN-1)==NULL 
		    || *fname=='\n' || *fname==0) strcpy(fname,nname);
                if ((cp=strrchr(fname,'\n'))) *cp=0;
                strcpy(nname,fname);
    }

  }

  /* does the file already exist? */
  while (stat(fname,&finfo)==0 && (*overwrite!='Y')) {
   
    /* skipping? */
    if (*overwrite=='S' || *overwrite=='N') return(1);

    /* ask user what to do */
    printf("'%s' already exists.\n"
           "Overwrite (yY), rename (r) or skip (sS) it? ",nname);
    fgetl(answer,stdin);
    *overwrite=answer[0];

    /* request for renaming? */
    if (*overwrite=='r') {
      sprintf(fname,"Rename '%s' to? ",nname);
      if (getpromptline(fname,MAXLEN-1)==NULL || *fname=='\n' || *fname==0)
	strcpy(fname,nname);
      if ((cp=strrchr(fname,'\n'))) *cp=0;
      strcpy(nname,fname);
      continue;
    }

    /* overwriting or leaving? */
    if (toupper(*overwrite)=='Y') break; else return(1);

  }

  return(0);
}


/*
 * create_sigfile  - create detached pgp signature file
 *
 * INPUT:  sign		- signature
 * 	   fname	- original file name
 * 	   overwrite	- overwriting, renaming or skipping
 *
 * RETURN: 0 if ok, -1 if failed
 */
int create_sigfile(const char *sign, const char *fname, const char *nname,
		   char *overwrite) {
  char
    *cp,			/* a simple string pointer */
    tmp[MAXLEN],		/* temporary string */
    sigfile[MAXLEN],		/* signature file */
    nsigfile[MAXLEN],		/* normal displayable signature file name */
    answer[FLEN];		/* answer string */
  struct stat finfo;		/* information about a file */

  FILE *outf;			/* file to create */

  /* no pgp signature to save? */
  if (!*sign) return(0);

  snprintf(MAXS(sigfile),"%s.sig",fname);
  snprintf(MAXS(nsigfile),"%s.sig",nname);

  /* signature file does already exist? */
  while (stat(sigfile,&finfo)==0 && (*overwrite!='Y')) {
   
    /* skipping? */
    if (*overwrite=='S' || *overwrite=='N') return(-1);

    /* ask user what to do */
    printf("'%s' already exists.\n"
           "Overwrite (yY), rename (r) or skip (sS) it? ",nsigfile);
    fgetl(answer,stdin);
    *overwrite=answer[0];

    /* request for renaming? */
    if (*overwrite=='r') {
      sprintf(nsigfile,"Rename to? ");
      if (getpromptline(nsigfile,MAXLEN-1)==NULL || 
	  *nsigfile=='\n' || *nsigfile==0)
	strcpy(nsigfile,sigfile);
      if ((cp=strrchr(nsigfile,'\n'))) *cp=0;
      strcpy(sigfile,nsigfile);
      continue;
    }

    /* overwriting or leaving? */
    if (toupper(*overwrite)=='Y') break; else return(-1);

  }

  /* safety fallback: try to delete an old file with the same name */
  unlink(fname);
  if (stat(sigfile,&finfo)==0) {
    snprintf(MAXS(tmp),"cannot create '%s' : "
	     "file does already exist and is not deletable",sigfile);
    errno=0;
    message(prg,'E',tmp);
    return(-1);
  }

  if (!(outf=rfopen(sigfile,"w"))) {
    snprintf(MAXS(tmp),"cannot create signature file '%s' ",nsigfile);
    message(prg,'E',tmp);
    return(-1);
  }

  fprintf(outf,"%s",sign);
  fclose(outf);
  snprintf(MAXS(tmp),"signature file '%s' created",nsigfile);
  message(prg,'I',tmp);
  return(0);

}


/*
 * check_signature  - check or print pgp signature
 *
 * INPUT:  flp		- file list pointer to spool file
 *         pgpring	- pgp ring file name
 * 	   print	- flag for printing signature status
 *
 * RETURN:  1 if good signature
 *          0 if no signature
 *         -1 if signature not found in pgp key ring
 *         -2 if bad signature
 *         -3 if other error
 *         
 */					  
int check_signature(struct filelist *flp, char *pgpring, int print) {  
  int status;			/* return status */
  char *cp;	    		/* simple character pointer */
  char sigfile[FLEN],		/* pgp signature file */
       pgpopt[FLEN],		/* pgp options */
       tmp[MAXLEN],		/* temp-string */
       line[MAXLEN];		/* line to read in */
  FILE
    *outf,			/* output file */
    *pp;			/* pipe input stream */

  status=0;
  
  if (!(*(flp->sign))) return(0);
  
  if (str_eq(pgpring,".")) *pgpring=0;
  
  /* write signature file */
  snprintf(MAXS(sigfile),"%s/%d.d.sig",userspool,flp->id);
  if (!(outf=rfopen(sigfile,"w"))) {
    snprintf(MAXS(tmp),"cannot write signature file %s",sigfile);
    message(prg,'E',tmp);
    return(-2);
  }
  fprintf(outf,"%s",flp->sign);
  fclose(outf);

  /* build pgp options */
  if (*pgpring) {
    if (access(pgpring,R_OK)<0) {
      snprintf(MAXS(tmp),"cannot read pgp pub ring file %s",pgpring);
      message(prg,'F',tmp);
    }
    snprintf(MAXS(pgpopt),"+batchmode=on +language=en +pubring=%s",pgpring);
  } else
    snprintf(MAXS(pgpopt),"+batchmode=on +language=en");
  
  /* check signature file with pgp */
  snprintf(MAXS(tmp),"%s %s %s 2>/dev/null",pgp_bin,pgpopt,sigfile);
  if (*verbose) printf("call: %s\n",tmp);
  if (!(pp=popen(tmp,"r"))) {
    message(prg,'E',"cannot call pgp");
    unlink(sigfile);
    return(-2);
  }

  /* print or check only result */
  if (print) {
    while (fgetl(line,pp)) {
      if ((cp=strchr(line,'\n'))) *cp=0;
      if ((cp=strrchr(line,'.'))) *cp=0;
      if (strstr(line,"Good signature from user") || 
	  strstr(line,"Bad signature from user")) {
	printf("     (%s)\n",line);
	break;
      }
      if (strstr(line,"Key matching expected")) {
	printf("     (No matching signature found in pgp key file)\n");
	break;
      }
    }
  } else {  /* check only result */
    while (fgetl(line,pp)) {
      if (strstr(line,"Good signature from user")) { 
	status=1;
	break;
      }
      if (strstr(line,"Key matching expected")) { 
	status=-1;
	break;
      }
      if (strstr(line,"Could not read key")) { 
	status=-1;
	break;
      }
      if (strstr(line,"Bad signature from user")) { 
	status=-2;
	break;
      }
    }
  }
    
  /* close pipe and delete signature file */
  pclose(pp);
  unlink(sigfile);
  
  return(status);    
}


/* 
 * renumber  - renumber spool files
 */
void renumber (struct senderlist *sls) {
  int 
    i,				/* actual spool file number */
    nextfree,			/* next free spool file number */
    lastused,			/* last used spool file number */
    min,			/* smallest spool file number */
    max;			/* biggest spool file number */
  char 
    tmp[MAXLEN],		/* temporary string */
    ofile[FLEN],		/* old filename */
    nfile[FLEN];		/* new filename */
  struct filelist *flp;		/* file list pointer */
  struct senderlist *slp;	/* sender list pointer */
  struct stat finfo;            /* information about a file */
  FILE *lockf;			/* lock file */

  min = lastused = 0;
  nextfree = max = nextfree = 1;
  
  if (chdir(userspool)<0) {
    snprintf(MAXS(tmp),"cannot change to %s",userspool);
    message(prg,'F',tmp);
  }

  /* create lock file */
  lockf=rfopen("renumber","a+");
  if (!lockf) message(prg,'F',"cannot open renumber file");
  if (wlock_file(fileno(lockf))<0) {
    message(prg,'E',"spool is locked - try again later");
    return;
  }
  
  /* loop over all spool files to find max spool file number */
  for (slp=sls; slp; slp=slp->next) {
    for (flp=slp->flist; flp; flp=flp->next) {
      fprintf(lockf,"%d\n",flp->id);
      if (flp->id > max) max=flp->id;
    }
  }

  /* renumber loop */
  while (min<max) {
    rewind(lockf);
    min=max;
    while (fscanf(lockf,"%d\n",&i)!=EOF) {
      if (i<min && i>lastused && i>nextfree) min=i;
    }
    snprintf(MAXS(ofile),"%d.h",min);
    for (i=nextfree; i<min; i++) {
      snprintf(MAXS(nfile),"%d.h",i);
      if (stat(nfile,&finfo)<0 || finfo.st_size==0) {
	unlink(nfile);
	if (rename(ofile,nfile)<0) {
	  snprintf(MAXS(tmp),"cannot rename %s to %s",ofile,nfile);
	  message(prg,'F',tmp);
	}
	snprintf(MAXS(nfile),"%d.d",i);
	snprintf(MAXS(ofile),"%d.d",min);
	unlink(nfile);
	if (rename(ofile,nfile)<0) {
	  snprintf(MAXS(tmp),"cannot rename %s to %s",ofile,nfile);
	  message(prg,'F',tmp);
	}
	nextfree=i+1;
	break;
      }
    }
    lastused=min;
  }
  
  unlink("renumber");
}


/*
 * reply  - dummy function, only needed for linking
 */
void reply(int x) { }


/*
 * usage - print short help usage text
 */
int usage() {
  fprintf(stderr,"usage:  %s [-dqrpkP] [-f from] [-S keyring] file-name [...]\n",prg);
  fprintf(stderr,"   or:  %s -n [-dqrpkP] [-S keyring] file-number [...]\n",prg);
  fprintf(stderr,"   or:  %s [-slLR] [-f from]\n",prg);
  fprintf(stderr,"   or:  %s -a [-dqrpkP] [-S keyring] [-f from]\n",prg);
  fprintf(stderr,"   or:  %s -b user[@host] [-k] [-f from] file-name [...]\n",prg);
  fprintf(stderr,"   or:  %s -b user[@host] -n [-k] file-number [...]\n",prg);
  fprintf(stderr,"   or:  %s -b user[@host] -a [-k]\n",prg);
  fprintf(stderr,"options:  -n  specify a file number\n");
  fprintf(stderr,"          -a  specify all files\n");
  fprintf(stderr,"          -f  specify only files from this user\n");
  fprintf(stderr,"          -d  delete file (do not receive)\n");
  fprintf(stderr,"          -q  quiet mode: no questions asked\n");
  fprintf(stderr,"          -r  rename file when receiving\n");
  fprintf(stderr,"          -R  renumber files in spool\n");
  fprintf(stderr,"          -p  preserve tar or pgp file formats\n");
  fprintf(stderr,"          -k  keep file in spool after receiving\n");
  fprintf(stderr,"          -P  receive file to stdout (decrypt but don't untar)\n");
  fprintf(stderr,"          -b  bounce (forward) a file\n");
  fprintf(stderr,"          -S  force validating pgp signature from keyring (\".\" is default ring)\n");
  fprintf(stderr,"          -l/-L/-s  normal/full/short list of files in spool\n");
  fprintf(stderr,"examples:  receive -af frams  ! receive all files from user frams\n");
  fprintf(stderr,"           receive -n 1       ! receive file number 1\n");
  fprintf(stderr,"           receive blubb      ! receive file blubb\n");
  return(2);
}


syntax highlighted by Code2HTML, v. 0.9.1