/*
* File: spool.c
*
* Author: Ulli Horlacher (framstag@rus.uni-stuttgart.de)
*
* History:
*
* 1995-09-01 Framstag initial version
* 1995-10-10 Framstag open bug fixed
* 1995-11-01 Framstag added pgp signature (dummy)
* 1995-11-05 Framstag added NeXT support
* 1995-11-15 Framstag fixed memory leak (oops :-) )
* 1996-01-24 Framstag new (received) DATEFORMAT
* 1996-01-25 Framstag added keep and deljunk config
* 1996-02-06 Framstag added ATTRIBUTE=EXE
* 1996-03-16 Framstag recognize file size 0 correctly
* 1996-04-02 Framstag FROM now in UTF-7
* 1996-04-12 Framstag sign is now in filelist
* sign, comment and fname are now dynamic
* added pgp support
* 1996-04-20 Framstag build sender list only for requested sender
* (wildcards are ok)
* 1996-04-24 Framstag added scanoutspool function
* 1996-06-24 Framstag fixed bug when FROM contains no real name
* 1997-01-07 Framstag fixed bug when spool data file does not exist
* 1997-02-03 Framstag sprintf() -> snprintf()
* 1997-01-20 Framstag fixed bug with TEXT=charset attribute
* 1997-02-23 Framstag modified str_* function names
* extended with TYPE=MIME
* 1997-03-19 Framstag fixed TYPE=TEXT parsing bug
* 1997-09-12 Framstag empty date header is now allowed
* 1997-09-14 Framstag moved spoolid() from sendfiled.c to spool.c
* 1997-09-17 Framstag sender comparision ignores now real name
* 1997-09-18 Framstag better header line parsing
* 1997-09-24 Framstag more effizient spool junk removing
* 1997-11-15 Framstag expire too big junk files, too
* 1997-12-10 Framstag added compression method for filelist
* 1998-05-19 Framstag fixed maxfiles counting bug in spoolid()
* 1998-11-12 Framstag scanoutspool() now gets file size, too
*
* Functions for operations on files in the sendfile spool directory.
*
* Copyright © 1995-1998 Ulli Horlacher
* This file is covered by the GNU General Public License
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include "config.h" /* various #defines */
#include "io.h" /* misc IO routines */
#include "spool.h" /* operations on files in the sendfile spool */
#include "utf7.h" /* UTF-7 coding */
#include "message.h" /* information, warning and error messages */
#include "string.h" /* extended string functions */
#include "reply.h" /* the 3 digit reply codes with text messages */
/*
* scanspool - scan through spool directory, build list-structures and
* delete old files if necessary
*
* INPUT: sender - sender name (wildcards * and ? are allowed)
*
* RETURN: start of sender list
*/
struct senderlist *scanspool(char *sender) {
extern int client; /* flag to determine client or server */
extern char userspool[]; /* user spool directory */
unsigned char *ucp; /* simple unsigned character pointer */
char
*cp, /* simple character pointer */
*arg, /* the argument(s) of the header line */
*compress, /* compression method */
line[MAXLEN], /* config line */
hline[MAXLEN], /* header line */
from[MAXLEN], /* header from line */
fname[MAXLEN], /* header file line */
comment[MAXLEN], /* file comment (printable string) */
tmp[MAXLEN], /* temporary string */
msg[MAXLEN], /* information/error message */
sign[MAXLEN], /* pgp signature (Latin-1) */
date[DLEN+1], /* file date */
charset[DLEN+1], /* character set name */
rdate[DLEN+1], /* receive date */
file[FLEN+1]; /* spool file name */
int
i,
id, /* spool file id number */
hfc, /* header file corruption flag */
keep, /* keep spool files at least xx days */
deljunk, /* delete corrupted spool files after xx days */
flags; /* source, text, compress, tar and exe flag */
unsigned long
osize, /* original file size */
csize, /* compressed file size */
tsize; /* true spool data file size */
time_t
ctime, /* current time */
rtime; /* receiving time */
FILE
*hf, /* header file */
*inf; /* config file */
struct stat finfo; /* information about a file */
#ifdef NEXT
FILE *pp; /* pipe */
#else
struct dirent *dire; /* directory entry */
DIR *dp; /* directory pointer */
#endif
struct filelist *flp, /* file list pointer */
*fln; /* new file list element */
struct senderlist *sls, /* sender list start */
*sln, /* sender list next pointer */
*slp; /* sender list pointer */
static struct senderlist *sll=NULL; /* last sender list start */
keep=0;
deljunk=10;
ctime=time(NULL);
flp=NULL;
/* is there already an old senderlist? */
if (sll)
/* delete old senderlist and free memory */
for (slp=sll; slp!=NULL;) {
for (flp=slp->flist; flp!=NULL; ) {
fln=flp->next;
free(flp->sign);
free(flp->fname);
free(flp->comment);
free(flp);
flp=fln;
}
sln=slp->next;
free(slp);
slp=sln;
}
sls=sll=NULL;
/* parse the config-file */
if ((inf=rfopen(CONFIG,"r"))) {
while ((fgetl(line,inf))) {
/* prepare line to be parsed */
if ((cp=strchr(line,'#'))) *cp=0;
if ((cp=strchr(line,'\n'))) *cp=0;
if ((cp=strchr(line,'='))) *cp=' ';
str_trim(line);
str_tolower(line);
/* is there an option and an argument? */
if ((cp=strchr(line,' '))) {
*cp=0; cp++;
if (str_eq(line,"keep")) {
keep=atoi(cp);
continue;
}
if (str_eq(line,"deljunk")) {
deljunk=atoi(cp);
continue;
}
}
}
fclose(inf);
}
/* mega stupid NeXT has broken readdir() */
#ifdef NEXT
/* open spool dir */
snprintf(MAXS(tmp),"ls %s 2>/dev/null",userspool);
if ((pp=popen(tmp,"r")) == NULL) return(NULL);
/* scan through spool directory */
while (fgetl(tmp,pp)) {
if ((cp=strrchr(tmp,'\n'))) *cp=0;
/* look for header files */
if ((cp=strchr(tmp,'.')) && str_eq(cp,".h")) {
/* extract spool id */
*cp=0;
id=atoi(tmp);
#ifdef JEDPARSE
}}
#endif
#else
/* open spool dir */
if ((dp=opendir(userspool)) == NULL) return(NULL);
/* scan through spool directory */
while ((dire=readdir(dp))) {
/* look for header files */
if ((cp=strchr(dire->d_name,'.')) && str_eq(cp,".h")) {
/* extract spool id */
*cp=0;
id=atoi(dire->d_name);
#endif
/* open header file */
snprintf(MAXS(file),"%s/%d.h",userspool,id);
hf=rfopen(file,"r");
/* error? */
if (!hf) {
/* called from receive client? */
if (client) {
snprintf(MAXS(msg),"cannot open spool file %s",file);
message("",'E',msg);
}
/* skip this spool file */
continue;
}
/* initialisize header entries */
flags=0;
csize=0;
tsize=0;
osize=0;
*from=0;
*date=0;
*sign=0;
*fname=0;
*charset=0;
*comment=0;
compress="";
/* does the spool data file exist? */
snprintf(MAXS(file),"%s/%d.d",userspool,id);
if (stat(file,&finfo)<0) {
snprintf(MAXS(file),"%s/%d.h",userspool,id);
unlink(file);
continue;
}
/* get time of receiving (local) */
rtime=finfo.st_mtime;
/* spool file expired? */
if (keep>0 && (ctime-rtime)/DAYSEC>=keep) {
fclose(hf);
unlink(file);
snprintf(MAXS(file),"%s/%d.h",userspool,id);
unlink(file);
continue;
}
strftime(rdate,21,DATEFORMAT,localtime(&rtime));
/* read header file */
hfc=0;
while ((fgetl(hline,hf)) && hfc==0) {
/* prepare the header line */
if ((cp=strchr(hline,'\n'))) *cp=0;
cp=strchr(hline,'\t');
/* corrupt header file line? */
if (cp==NULL) {
hfc=1;
continue;
}
str_trim(hline);
cp=strchr(hline,' ');
if (cp==NULL) continue;
arg=cp+1;
*cp=0;
/* extract the header-name and the argument */
if (str_eq(hline,"FROM")) {
if ((cp=strchr(arg,' '))) {
*cp=0;
snprintf(MAXS(tmp),"%s (%s)",arg,cp+1);
} else
strcpy(tmp,arg);
utf2iso(0,from,NULL,NULL,tmp);
continue;
}
if (str_eq(hline,"FILE")) {
strcpy(fname,arg);
continue;
}
if (str_eq(hline,"DATE")) {
strncpy(date,arg,DLEN);
continue;
}
if (str_eq(hline,"SIZE")) {
sscanf(arg,"%ld %ld",&csize,&osize);
tsize=finfo.st_size;
continue;
}
if (str_eq(hline,"ATTR")) {
str_toupper(arg);
if (str_eq(arg,"TAR")) flags=flags|F_TAR;
if (str_eq(arg,"EXE")) flags=flags|F_EXE;
continue;
}
if (str_eq(hline,"TYPE")) {
str_toupper(arg);
if (strstr(arg,"SOURCE")) flags=flags|F_SOURCE;
if (strstr(arg,"TEXT")) flags=flags|F_TEXT;
if (strstr(arg,"MIME")) flags=flags|F_MIME;
if (strstr(arg,"CRYPTED")) flags=flags|F_CRYPT;
if (strstr(arg,"COMPRESSED")) {
flags=flags|F_COMPRESS;
if (strstr(arg,"COMPRESSED=BZIP2"))
compress=S_BZIP2;
else
compress=S_GZIP;
}
if (strstr(arg,"TEXT=")) {
strncpy(charset,strchr(arg,'=')+1,DLEN);
charset[DLEN]=0;
if ((cp=strchr(charset,' '))) *cp=0;
}
continue;
}
/* comment and signature are only needed for receive client */
if (client) {
if (str_eq(hline,"COMMENT")) {
/* save comment in printable code */
utf2iso(0,comment,NULL,NULL,arg);
for (ucp=(unsigned char *)comment,i=0; *ucp; ucp++)
if (*ucp==9 || *ucp==10 || (*ucp>31 && *ucp<127) || *ucp>159)
comment[i++]=*ucp;
comment[i]=0;
continue;
}
if (str_eq(hline,"SIGN")) {
utf2iso(0,sign,NULL,NULL,arg);
continue;
}
}
}
fclose(hf);
/* junk file expired? */
if (*from==0 || *fname==0 ||
(tsize!=csize && deljunk>0 && (ctime-rtime)/DAYSEC>=deljunk)) {
snprintf(MAXS(file),"%s/%d.d",userspool,id);
unlink(file);
snprintf(MAXS(file),"%s/%d.h",userspool,id);
unlink(file);
continue;
}
/* bad header file? */
if (*from==0 || *fname==0) continue;
/* is it a requested sender name? (ignore real name) */
strcpy(line,from); if ((cp=strchr(line,' '))) *cp=0;
strcpy(tmp,sender); if ((cp=strchr(tmp,' '))) *cp=0;
if (sender==NULL || *sender==0 || simplematch(line,tmp,1)) {
/* create new file list element */
if ((fln=(struct filelist *) malloc(sizeof(struct filelist))) == NULL ||
(fln->fname= (char *) malloc(strlen(fname)+1)) == NULL ||
(fln->comment= (char *) malloc(strlen(comment)+1)) == NULL ||
(fln->sign= (char *) malloc(strlen(sign)+1)) == NULL) {
if (client) message("",'F',"cannot allocate memory"); else reply(453);
}
/* fill in new file list element */
fln->id=id;
fln->next=NULL;
fln->osize=osize;
fln->csize=csize;
fln->tsize=tsize;
fln->flags=flags;
fln->rtime=rtime;
strcpy(fln->date,date);
strcpy(fln->sign,sign);
strcpy(fln->fname,fname);
strcpy(fln->rdate,rdate);
strcpy(fln->charset,charset);
strcpy(fln->comment,comment);
strcpy(fln->compress,compress);
/* first sender? */
if (sls==NULL)
sls=newsle(fln,from);
else {
/* search for sender in senderlist */
slp=sls;
while (slp->next && !str_eq(slp->from,from)) slp=slp->next;
/* sender not found in sender list? */
if (!str_eq(slp->from,from))
/* create new sender list element and append it */
slp->next=newsle(fln,from);
else {
/* is it the smallest id? */
if (id < slp->flist->id) {
fln->next=slp->flist;
slp->flist=fln;
} else {
/* insert into files list */
for (flp=slp->flist; flp->next!=NULL; flp=flp->next) {
if (id < flp->next->id) {
fln->next=flp->next;
flp->next=fln;
break;
}
}
/* last element? */
if (flp->next==NULL) flp->next=fln;
}
}
}
}
}
}
#ifdef NEXT
pclose(pp);
#else
closedir(dp);
#endif
sll=sls;
return(sls);
}
/*
* scanoutspool - scan through outgoing spool directory, build list-structures
* and delete bad files if necessary
*
* INPUT: sender - sender name
*
* RETURN: start of host list, NULL on error
*
* REMARK: if no files in outgoing spool are found a special hostlist structure
* will be returned with all zeros values.
*
* All header lines in the outgoing spool files are SAFT-conform (strings are
* in UTF-7, etc).
*/
struct hostlist *scanoutspool(char *sender) {
extern int client; /* flag to determine client or server */
char
*cp, /* simple character pointer */
*arg, /* the argument(s) of the header line */
hline[MAXLEN], /* header line */
host[MAXLEN], /* header recipient host line */
to[MAXLEN], /* header recipient user line */
from[MAXLEN], /* header from line */
fname[MAXLEN], /* header file line */
tmp[MAXLEN], /* temporary string */
msg[MAXLEN], /* information/error message */
hfn[MAXLEN], /* outgoing spool header file name */
dfn[MAXLEN]; /* outgoing spool data file name */
int hfc; /* header file corruption flag */
unsigned long size; /* size of outgoing spool data file */
FILE *hf; /* header file */
struct stat finfo; /* information about a file */
#ifdef NEXT
FILE *pp; /* pipe */
#else
struct dirent *dire; /* directory entry */
DIR *dp; /* directory pointer */
#endif
struct outfilelist
*oflp, /* outgoing file list pointer */
*ofln; /* new file list element */
struct hostlist
*hls, /* host list start */
*hln, /* host list next pointer */
*hlp; /* host list pointer */
static struct hostlist
*hll=NULL; /* last host list start */
oflp=NULL;
/* is there already an old hostlist? */
if (hll) {
/* delete old hostlist and free memory */
for (hlp=hll; hlp;) {
for (oflp=hlp->flist; oflp;) {
ofln=oflp->next;
free(oflp->to);
free(oflp->oshfn);
free(oflp->fname);
free(oflp);
oflp=ofln;
}
hln=hlp->next;
free(hlp);
hlp=hln;
}
}
/* create first empty host list element */
hls=hll=newhle(NULL,"");
/* mega stupid NeXT has broken readdir() */
#ifdef NEXT
/* open spool dir */
snprintf(MAXS(tmp),"cd %s;ls %s_*.h 2>/dev/null",OUTGOING,sender);
if ((pp=popen(tmp,"r")) == NULL) return(NULL);
/* scan through spool directory */
while (fgetl(hfn,pp)) {
if ((cp=strrchr(hfn,'\n'))) *cp=0;
#ifdef JEDPARSE
}
#endif
#else
/* open spool dir */
if (!(dp=opendir(OUTGOING))) return(NULL);
/* scan through outgoing spool directory */
while ((dire=readdir(dp))) {
strcpy(hfn,dire->d_name);
#endif
/* look for header files */
snprintf(MAXS(tmp),"%s*.h",sender);
if (simplematch(hfn,tmp,1)==0) continue;
strcpy(tmp,hfn);
snprintf(MAXS(hfn),OUTGOING"/%s",tmp);
/* open header file */
if ((hf=rfopen(hfn,"r")) == NULL) {
/* called from receive client? */
if (client) {
snprintf(MAXS(msg),"cannot open outgoing spool file %s",hfn);
message("",'E',msg);
}
continue;
}
/* initialisize header entries */
size=0;
*to=0;
*host=0;
*from=0;
*fname=0;
/* read header file */
hfc=0;
while (fgetl(hline,hf) && hfc==0) {
/* prepare the header line */
if ((cp=strchr(hline,'\n'))) *cp=0;
cp=strchr(hline,'\t');
/* corrupt header file line? */
if (cp==NULL) {
hfc=1;
continue;
}
arg=cp+1;
*cp=0;
/* extract the header-name and the argument */
/* check the from line */
if (str_eq(hline,"FROM")) {
if ((cp=strchr(arg,' '))) *cp=0;
utf2iso(0,from,NULL,NULL,arg);
/* wrong from line? */
if (*sender && !str_eq(from,sender)) hfc=1;
continue;
}
/* check the to line */
if (str_eq(hline,"TO")) {
if (!(cp=strchr(arg,'@')))
hfc=1;
else {
strcpy(host,cp+1);
*cp=0;
utf2iso(0,to,NULL,NULL,arg);
}
continue;
}
if (str_eq(hline,"FILE")) {
strcpy(fname,arg);
continue;
}
if (str_eq(hline,"SIZE")) {
sscanf(arg,"%ld",&size);
strcpy(dfn,hfn);
dfn[strlen(dfn)-1]='d';
/* wrong size? */
if (stat(dfn,&finfo)<0 || finfo.st_size!=size) hfc=1;
continue;
}
}
fclose(hf);
/* delete bad files */
if (*from==0 || *fname==0 || *to==0 || *host==0 || hfc) {
strcpy(dfn,hfn);
dfn[strlen(dfn)-1]='d';
unlink(dfn);
unlink(hfn);
continue;
}
/* create new outgoing file list element */
if ((ofln=(struct outfilelist *)malloc(sizeof(struct outfilelist)))==NULL||
(ofln->fname= (char *)malloc(strlen(fname)+1)) ==NULL||
(ofln->to= (char *)malloc(strlen(to)+1)) ==NULL||
(ofln->oshfn= (char *)malloc(strlen(hfn)+1)) ==NULL||
(ofln->from= (char *)malloc(strlen(from)+1)) ==NULL){
if (client) message("",'F',"cannot allocate memory"); else reply(453);
}
/* fill in new outgoing file list element */
ofln->next=NULL;
ofln->size=size;
strcpy(ofln->to,to);
strcpy(ofln->from,from);
strcpy(ofln->oshfn,hfn);
strcpy(ofln->fname,fname);
/* first host? */
if (hls->flist==NULL) {
hls->flist=ofln;
strcpy(hls->host,host);
} else {
/* search for host in hostlist */
hlp=hls;
while (hlp->next && !str_eq(hlp->host,host)) hlp=hlp->next;
/* host not found in host list? */
if (!str_eq(hlp->host,host))
/* create new host list element */
hlp->next=newhle(ofln,host);
else /* append to outgoing file list */ {
for (oflp=hlp->flist; oflp->next!=NULL; oflp=oflp->next);
oflp->next=ofln;
}
}
}
#ifdef NEXT
pclose(pp);
#else
closedir(dp);
#endif
hll=hls;
return(hls);
}
/*
* newsle - create new sender list element and fill it out
*
* INPUT: flp - first file list element
* from - sendername
*
* RETURN: start of sender list
*/
struct senderlist *newsle(struct filelist *flp, const char *from) {
struct senderlist *sln; /* new sender list element */
extern int client; /* flag to determine client or server */
/* create new sender list element */
if ((sln=(struct senderlist *)malloc(sizeof(struct senderlist))) == NULL) {
if (client) message("",'F',"cannot allocate memory"); else reply(453);
}
/* fill it out */
sln->next=NULL;
sln->flist=flp;
strcpy(sln->from,from);
return(sln);
}
/*
* newhle - create new host list element and fill it out
*
* INPUT: oflp - first outgoing file list element
* host - host name
*
* RETURN: start of sender list
*/
struct hostlist *newhle(struct outfilelist *oflp, const char *host) {
struct hostlist *hln; /* new host list element */
extern int client; /* flag to determine client or server */
/* create new host list element */
if ((hln=(struct hostlist *)malloc(sizeof(struct hostlist))) == NULL) {
if (client) message("",'F',"cannot allocate memory"); else reply(453);
}
/* fill it out */
hln->next=NULL;
hln->flist=oflp;
strcpy(hln->host,host);
return(hln);
}
/*
* delete_sf - delete a spool file
*
* INPUT: flp - file list pointer
* verbose - if set, print success message
*
* RETURN: 0 on success, -1 on fail
*/
int delete_sf(struct filelist *flp, int verbose) {
char msg[MAXLEN], /* information/error message */
file[MAXLEN], /* spool file */
fname[MAXLEN]; /* displayble file name */
extern int client; /* flag to determine client or server */
extern char userspool[]; /* user spool directory */
snprintf(MAXS(file),"%s/%d.d",userspool,flp->id);
unlink(file);
snprintf(MAXS(file),"%s/%d.h",userspool,flp->id);
utf2iso(1,NULL,fname,NULL,flp->fname);
if(unlink(file)<0) {
if (client) {
snprintf(MAXS(msg),"cannot delete spoolfile #%d",flp->id);
message("",'W',msg);
}
return(-1);
} else {
if (verbose) {
snprintf(MAXS(msg),"%s deleted",fname);
message("",'I',msg);
}
return(0);
}
}
/*
* spoolid - find the next spool id and touch header and data file
*
* INPUT: maxfiles - maximum number of allowed spool files
*
* RETURN: spool id, 0 if failed, -number of files in spool if maxfiles exceeded
*
* The working directory has to be the user spool!
*/
int spoolid(int maxfiles) {
int
i, /* simple loop count */
n, /* number of files in spool */
fd, /* file descriptor of spool id header file */
id, /* id number */
idmax; /* biggest id number */
char
*cp, /* character pointer to '.' in file name */
file[MAXLEN]; /* complete file name */
#ifdef NEXT
char tmp[MAXLEN]; /* tmp string */
FILE *pp; /* pipe */
#else
struct dirent *dire; /* directory entry */
DIR *dp; /* directory pointer */
#endif
/* try to create next spool files */
for (i=1; i<5; i++) {
/* initialisize */
n=0;
id=0;
fd=0;
idmax=0;
#ifdef NEXT
/* stupid NeXT has a broken readdir(); this is a dirty workaround */
/* open spool dir */
if ((pp=popen("ls . 2>/dev/null","r")) == NULL) return(NULL);
/* scan through spool directory */
while (fgetl(tmp,pp)) {
if ((cp=strrchr(tmp,'\n'))) *cp=0;
cp=strchr(tmp,'.');
if (cp && str_eq(cp,".h")) {
n++;
*cp=0;
id=atoi(tmp);
if (id>idmax) idmax=id;
}
}
pclose(pp);
#else
/* open spool dir */
dp=opendir(".");
/* scan through spool directory and get the highest spool id */
while ((dire=readdir(dp))) {
cp=strchr(dire->d_name,'.');
if (cp && str_eq(cp,".h")) {
*cp=0;
n++;
id=atoi(dire->d_name);
if (id>idmax) idmax=id;
}
}
closedir(dp);
#endif
id=idmax+1;
if (n>maxfiles) return(-n);
/* try to create header spool file */
snprintf(MAXS(file),"%d.h",id);
fd=open(file,O_CREAT|O_EXCL,S_IRUSR|S_IWUSR);
/* successfull? */
if (fd>=0) {
close(fd);
/* create data spool file */
snprintf(MAXS(file),"%d.d",id);
close(open(file,O_CREAT,S_IRUSR|S_IWUSR));
return(id);
}
/* wait and test again */
sleep(1);
}
/* failed */
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1