/* * 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 #include #include #include #include #include #include #include #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); }