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