/* SMTP Server state machine - see RFC 821 * enhanced 4/88 Dave Trulli nn2z */ #include #include #ifdef UNIX #include #endif #if defined(__STDC__) || defined(__TURBOC__) #include #endif #include #include #include "global.h" #include "mbuf.h" #include "cmdparse.h" #include "socket.h" #include "iface.h" #include "proc.h" #include "smtp.h" #include "commands.h" #include "dirutil.h" #include "mailbox.h" #include "bm.h" #include "domain.h" char *Days[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec" }; static struct list *expandalias __ARGS((struct list **head,char *user)); static int getmsgtxt __ARGS((struct smtpsv *mp)); static struct smtpsv *mail_create __ARGS((void)); static void mail_clean __ARGS((struct smtpsv *mp)); static int mailit __ARGS((FILE *data,char *from,struct list *tolist)); static int router_queue __ARGS((FILE *data,char *from,struct list *to)); static void smtplog __ARGS((char *fmt,...)); static void smtpserv __ARGS((int s,void *unused,void *p)); static int mailuser __ARGS((FILE *data,char *from,char *to)); /* Command table */ static char *commands[] = { "helo", #define HELO_CMD 0 "noop", #define NOOP_CMD 1 "mail from:", #define MAIL_CMD 2 "quit", #define QUIT_CMD 3 "rcpt to:", #define RCPT_CMD 4 "help", #define HELP_CMD 5 "data", #define DATA_CMD 6 "rset", #define RSET_CMD 7 "expn", #define EXPN_CMD 8 NULLCHAR }; /* Reply messages */ static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n"; static char Banner[] = "220 %s SMTP ready\n"; static char Closing[] = "221 Closing\n"; static char Ok[] = "250 Ok\n"; static char Reset[] = "250 Reset state\n"; static char Sent[] = "250 Sent\n"; static char Ourname[] = "250 %s, Share and Enjoy!\n"; static char Enter[] = "354 Enter mail, end with .\n"; static char Ioerr[] = "452 Temp file write error\n"; static char Badcmd[] = "500 Command unrecognized\n"; static char Lowmem[] = "421 System overloaded, try again later\n"; static char Syntax[] = "501 Syntax error\n"; static char Needrcpt[] = "503 Need RCPT (recipient)\n"; static char Unknown[] = "550 <%s> address unknown\n"; static char Noalias[] = "550 No alias for <%s>\n"; static int Ssmtp = -1; /* prototype socket for service */ /* Start up SMTP receiver service */ int smtp1(argc,argv,p) int argc; char *argv[]; void *p; { struct sockaddr_in lsocket; int s; if(Ssmtp != -1){ return 0; } psignal(Curproc,0); /* Don't keep the parser waiting */ chname(Curproc,"SMTP listener"); lsocket.sin_family = AF_INET; lsocket.sin_addr.s_addr = INADDR_ANY; if(argc < 2) lsocket.sin_port = IPPORT_SMTP; else lsocket.sin_port = atoi(argv[1]); Ssmtp = socket(AF_INET,SOCK_STREAM,0); bind(Ssmtp,(char *)&lsocket,sizeof(lsocket)); listen(Ssmtp,1); for(;;){ if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1) break; /* Service is shutting down */ if(availmem() < Memthresh){ usprintf(s,Lowmem); shutdown(s,1); } else { /* Spawn a server */ newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0); } } return 0; } /* Shutdown SMTP service (existing connections are allowed to finish) */ int smtp0(argc,argv,p) int argc; char *argv[]; void *p; { close_s(Ssmtp); Ssmtp = -1; return 0; } static void smtpserv(s,unused,p) int s; void *unused; void *p; { struct smtpsv *mp; char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr; struct list *ap,*list; int cnt; char address_type; sockmode(s,SOCK_ASCII); sockowner(s,Curproc); /* We own it now */ log(s,"open SMTP"); if((mp = mail_create()) == NULLSMTPSV){ printf(Nospace); log(s,"close SMTP - no space"); close_s(s); return; } mp->s = s; (void) usprintf(s,Banner,Hostname); loop: if ((cnt = recvline(s,buf,sizeof(buf))) == -1) { /* He closed on us */ goto quit; } if(cnt < 4){ /* Can't be a legal command */ usprintf(mp->s,Badcmd); goto loop; } rip(buf); cmd = buf; /* Translate entire buffer to lower case */ for(cp = cmd;*cp != '\0';cp++) *cp = tolower(*cp); /* Find command in table; if not present, return syntax error */ for(cmdp = commands;*cmdp != NULLCHAR;cmdp++) if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0) break; if(*cmdp == NULLCHAR){ (void) usprintf(mp->s,Badcmd); goto loop; } arg = &cmd[strlen(*cmdp)]; /* Skip spaces after command */ while(*arg == ' ') arg++; /* Execute specific command */ switch(cmdp-commands) { case HELO_CMD: free(mp->system); mp->system = strdup(arg); (void) usprintf(mp->s,Ourname,Hostname); break; case NOOP_CMD: (void) usprintf(mp->s,Ok); break; case MAIL_CMD: if((cp = getname(arg)) == NULLCHAR){ (void) usprintf(mp->s,Syntax); break; } free(mp->from); mp->from = strdup(cp); (void) usprintf(mp->s,Ok); break; case QUIT_CMD: (void) usprintf(mp->s,Closing); goto quit; case RCPT_CMD: /* Specify recipient */ if((cp = getname(arg)) == NULLCHAR){ (void) usprintf(mp->s,Syntax); break; } /* rewrite address if possible */ if((newaddr = rewrite_address(cp)) != NULLCHAR) { strcpy(buf,newaddr); cp = buf; free(newaddr); } /* check if address is ok */ if ((address_type = validate_address(cp)) == BADADDR) { (void) usprintf(mp->s,Unknown,cp); break; } /* if a local address check for an alias */ if (address_type == LOCAL) expandalias(&mp->to, cp); else /* a remote address is added to the list */ addlist(&mp->to, cp, address_type); (void) usprintf(mp->s,Ok); break; case HELP_CMD: (void) usprintf(mp->s,Help); break; case DATA_CMD: if(mp->to == NULLLIST) (void) usprintf(mp->s,Needrcpt); else if ((mp->data = tmpfile()) == NULLFILE) (void) usprintf(mp->s,Ioerr); else getmsgtxt(mp); break; case RSET_CMD: del_list(mp->to); mp->to = NULLLIST; (void) usprintf(mp->s,Reset); break; case EXPN_CMD: if (*arg == '\0') { (void) usprintf(mp->s,Syntax); break; } list = NULLLIST; /* rewrite address if possible */ if((newaddr = rewrite_address(arg)) != NULLCHAR) if(strcmp(newaddr,arg) == 0) { free(newaddr); newaddr = NULLCHAR; } else { strcpy(buf,newaddr); arg = buf; } list = NULLLIST; expandalias(&list,arg); if (strcmp(list->val,arg) == 0 && list->next == NULLLIST) if(newaddr == NULLCHAR) { (void) usprintf(mp->s,Noalias,arg); del_list(list); break; } ap = list; while (ap->next != NULLLIST) { (void) usprintf(mp->s,"250-%s\n",ap->val); ap = ap->next; } usprintf(mp->s,"250 %s\n",ap->val); del_list(list); free(newaddr); break; } goto loop; quit: log(mp->s,"close SMTP"); close_s(mp->s); mail_clean(mp); smtptick(NULL); /* start SMTP daemon immediately */ } /* read the message text */ static int getmsgtxt(mp) struct smtpsv *mp; { char buf[LINELEN]; register char *p = buf; long t; /* Add timestamp; ptime adds newline */ time(&t); fprintf(mp->data,"Received: "); if(mp->system != NULLCHAR) fprintf(mp->data,"from %s ",mp->system); fprintf(mp->data,"by %s with SMTP\n\tid AA%ld ; %s", Hostname, get_msgid(), ptime(&t)); if(ferror(mp->data)){ (void) usprintf(mp->s,Ioerr); return 1; } else { (void) usprintf(mp->s,Enter); } while(1) { if(recvline(mp->s,p,sizeof(buf)) == -1){ return 1; } rip(p); /* check for end of message ie a . or escaped .. */ if (*p == '.') { if (*++p == '\0') { /* Also sends appropriate response */ if (mailit(mp->data,mp->from,mp->to) != 0) (void) usprintf(mp->s,Ioerr); else (void) usprintf(mp->s,Sent); fclose(mp->data); mp->data = NULLFILE; del_list(mp->to); mp->to = NULLLIST; return 0; } else if (!(*p == '.' && *(p+1) == '\0')) p--; } /* for UNIX mail compatiblity */ if (strncmp(p,"From ",5) == 0) (void) putc('>',mp->data); /* Append to data file */ if(fprintf(mp->data,"%s\n",p) < 0) { (void) usprintf(mp->s,Ioerr); return 1; } } return 0; } /* Create control block, initialize */ static struct smtpsv * mail_create() { register struct smtpsv *mp; mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv)); mp->from = strdup(""); /* Default to null From address */ return mp; } /* Free resources, delete control block */ static void mail_clean(mp) register struct smtpsv *mp; { if (mp == NULLSMTPSV) return; free(mp->system); free(mp->from); if(mp->data != NULLFILE) fclose(mp->data); del_list(mp->to); free((char *)mp); } /* Given a string of the form , extract the part inside the * brackets and return a pointer to it. */ char * getname(cp) register char *cp; { register char *cp1; if ((cp = strchr(cp,'<')) == NULLCHAR) return NULLCHAR; cp++; /* cp -> first char of name */ if ((cp1 = strchr(cp,'>')) == NULLCHAR) return NULLCHAR; *cp1 = '\0'; return cp; } /* General mailit function. It takes a list of addresses which have already ** been verified and expanded for aliases. Base on the current mode the message ** is place in an mbox, the outbound smtp queue or the rqueue interface */ static int mailit(data,from,tolist) FILE *data; char *from; struct list *tolist; { struct list *ap, *dlist = NULLLIST; register FILE *fp; char mailbox[50], *cp, *host, *qhost; int c, fail = 0; time_t t; if ((Smtpmode & QUEUE) != 0) return(router_queue(data,from,tolist)); do { qhost = NULLCHAR; for(ap = tolist;ap != NULLLIST;ap = ap->next) if (ap->type == DOMAIN) { if ((host = strrchr(ap->val,'@')) != NULLCHAR) host++; else host = Hostname; if(qhost == NULLCHAR) qhost = host; if(stricmp(qhost,host) == 0) { ap->type = BADADDR; addlist(&dlist,ap->val,0); } } if(qhost != NULLCHAR) { rewind(data); queuejob(data,qhost,dlist,from); del_list(dlist); dlist = NULLLIST; } } while(qhost != NULLCHAR); for(ap = tolist;ap != NULLLIST;ap = ap->next) { if(ap->type != LOCAL) { ap->type = DOMAIN; continue; } rewind(data); /* strip off host name of LOCAL addresses */ if ((cp = strchr(ap->val,'@')) != NULLCHAR) *cp = '\0'; /* truncate long user names */ if (strlen(ap->val) > MBOXLEN) ap->val[MBOXLEN] = '\0'; /* if mail file is busy save it in our smtp queue * and let the smtp daemon try later. */ if (mlock(Mailspool,ap->val)) { addlist(&dlist,ap->val,0); fail = queuejob(data,Hostname,dlist,from); del_list(dlist); dlist = NULLLIST; } else { char buf[LINELEN]; int tocnt = 0; sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val); #ifndef AMIGA if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE) { #else if((fp = fopen(mailbox,"r+")) != NULLFILE) { (void) fseek(fp, 0L, 2); #endif time(&t); fprintf(fp,"From %s %s",from,ctime(&t)); host = NULLCHAR; while(fgets(buf,sizeof(buf),data) != NULLCHAR){ if(buf[0] == '\n'){ if(tocnt == 0) fprintf(fp,"%s%s\n", Hdrs[APPARTO], ap->val); fputc('\n',fp); break; } fputs(buf,fp); rip(buf); switch(htype(buf)){ case TO: case CC: ++tocnt; break; case RRECEIPT: if((cp = getaddress(buf,0)) != NULLCHAR){ free(host); host = strdup(cp); } break; } } while((c = fread(buf,1,sizeof(buf),data)) > 0) if(fwrite(buf,1,c,fp) != c) break; if(ferror(fp)) fail = 1; else fprintf(fp,"\n"); /* Leave a blank line between msgs */ fclose(fp); printf("New mail arrived for %s\n",ap->val); if(host != NULLCHAR){ rewind(data); /* Send return receipt */ mdaemon(data,host,NULLLIST,0); free(host); } } else fail = 1; (void) rmlock(Mailspool,ap->val); if (fail) break; smtplog("deliver: To: %s From: %s",ap->val,from); } } return fail; } /* Return Date/Time in Arpanet format in passed string */ char * ptime(t) long *t; { /* Print out the time and date field as * "DAY day MONTH year hh:mm:ss ZONE" */ register struct tm *ltm; static char tz[4]; static char str[40]; char *p, *getenv(); /* Read the system time */ ltm = localtime(t); if (*tz == '\0') if ((p = getenv("TZ")) == NULL) strcpy(tz,"UTC"); else strncpy(tz,p,3); /* rfc 822 format */ sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n", Days[ltm->tm_wday], ltm->tm_mday, Months[ltm->tm_mon], ltm->tm_year, ltm->tm_hour, ltm->tm_min, ltm->tm_sec, tz); return(str); } long get_msgid() { char sfilename[LINELEN]; char s[20]; register long sequence = 0; FILE *sfile; sprintf(sfilename,"%s/sequence.seq",Mailqdir); sfile = fopen(sfilename,READ_TEXT); /* if sequence file exists, get the value, otherwise set it */ if (sfile != NULL) { (void) fgets(s,sizeof(s),sfile); sequence = atol(s); /* Keep it in range of and 8 digit number to use for dos name prefix. */ if (sequence < 0L || sequence > 99999999L ) sequence = 0; fclose(sfile); } /* increment sequence number, and write to sequence file */ sfile = fopen(sfilename,WRITE_TEXT); fprintf(sfile,"%ld",++sequence); fclose(sfile); return sequence; } #ifdef MSDOS /* Illegal characters in a DOS filename */ static char baddoschars[] = "\"[]:|<>+=;,"; #endif /* test if mail address is valid */ int validate_address(s) char *s; { char *cp; int32 addr; /* if address has @ in it the check dest address */ if ((cp = strrchr(s,'@')) != NULLCHAR) { cp++; /* 1st check if its our hostname * if not then check the hosts file and see * if we can resolve ther address to a know site * or one of our aliases */ if (strcmp(cp,Hostname) != 0) { if ((addr = mailroute(cp)) == 0 && (Smtpmode & QUEUE) == 0) return BADADDR; if (ismyaddr(addr) == NULLIF) return DOMAIN; } /* on a local address remove the host name part */ *--cp = '\0'; } /* if using an external router leave address alone */ if ((Smtpmode & QUEUE) != 0) return LOCAL; /* check for the user%host hack */ if ((cp = strrchr(s,'%')) != NULLCHAR) { *cp = '@'; cp++; /* reroute based on host name following the % seperator */ if (mailroute(cp) == 0) return BADADDR; else return DOMAIN; } #ifdef MSDOS /* dos file name checks */ /* Check for characters illegal in MS-DOS file names */ for(cp = baddoschars;*cp != '\0';cp++){ if(strchr(s,*cp) != NULLCHAR) return BADADDR; } #endif return LOCAL; } /* place a mail job in the outbound queue */ int queuejob(dfile,host,to,from) FILE *dfile; char *host; struct list *to; char *from; { FILE *fp; struct list *ap; char tmpstring[50], prefix[9], buf[LINELEN]; register int cnt; sprintf(prefix,"%ld",get_msgid()); mlock(Mailqdir,prefix); sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix); if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) { (void) rmlock(Mailqdir,prefix); return 1; } while((cnt = fread(buf, 1, LINELEN, dfile)) > 0) if(fwrite(buf, 1, cnt, fp) != cnt) break; if(ferror(fp)){ fclose(fp); (void) rmlock(Mailqdir,prefix); return 1; } fclose(fp); sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix); if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) { (void) rmlock(Mailqdir,prefix); return 1; } fprintf(fp,"%s\n%s\n",host,from); for(ap = to; ap != NULLLIST; ap = ap->next) { fprintf(fp,"%s\n",ap->val); smtplog("queue job %s To: %s From: %s",prefix,ap->val,from); } fclose(fp); (void) rmlock(Mailqdir,prefix); return 0; } /* Deliver mail to the appropriate mail boxes */ static int router_queue(data,from,to) FILE *data; char *from; struct list *to; { int c; register struct list *ap; FILE *fp; char tmpstring[50]; char prefix[9]; sprintf(prefix,"%ld",get_msgid()); mlock(Routeqdir,prefix); sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix); if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) { (void) rmlock(Routeqdir,prefix); return 1; } rewind(data); while((c = getc(data)) != EOF) if(putc(c,fp) == EOF) break; if(ferror(fp)){ fclose(fp); (void) rmlock(Routeqdir,prefix); return 1; } fclose(fp); sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix); if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) { (void) rmlock(Routeqdir,prefix); return 1; } fprintf(fp,"From: %s\n",from); for(ap = to;ap != NULLLIST;ap = ap->next) { fprintf(fp,"To: %s\n",ap->val); } fclose(fp); (void) rmlock(Routeqdir,prefix); smtplog("rqueue job %s From: %s",prefix,from); return 0; } /* add an element to the front of the list pointed to by head ** return NULLLIST if out of memory. */ struct list * addlist(head,val,type) struct list **head; char *val; int type; { register struct list *tp; tp = (struct list *)callocw(1,sizeof(struct list)); tp->next = NULLLIST; /* allocate storage for the char string */ tp->val = strdup(val); tp->type = type; /* add entry to front of existing list */ if (*head == NULLLIST) *head = tp; else { tp->next = *head; *head = tp; } return tp; } #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++; #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++; /* check for and alias and expand alias into a address list */ static struct list * expandalias(head, user) struct list **head; char *user; { FILE *fp; register char *s,*p; struct rr *rrp, *rrlp; int inalias = 0; struct list *tp; char buf[LINELEN]; /* no alias file found */ if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE) { /* Try MB, MG or MR domain name records */ rrlp = rrp = resolve_mailb(user); while(rrp != NULLRR){ if(rrp->rdlength > 0){ /* remove the trailing dot */ rrp->rdata.name[rrp->rdlength-1] = '\0'; /* replace first dot with @ if there is no @ */ if(strchr(rrp->rdata.name,'@') == NULLCHAR && (p = strchr(rrp->rdata.name,'.')) != NULLCHAR) *p = '@'; if(strchr(rrp->rdata.name,'@') != NULLCHAR) tp = addlist(head,rrp->rdata.name, DOMAIN); else tp = addlist(head,rrp->rdata.name, LOCAL); ++inalias; } rrp = rrp->next; } free_rr(rrlp); if(inalias) return tp; else return addlist(head, user, LOCAL); } while (fgets(buf,LINELEN,fp) != NULLCHAR) { p = buf; if ( *p == '#' || *p == '\0') continue; rip(p); /* if not in an matching entry skip continuation lines */ if (!inalias && isspace(*p)) continue; /* when processing an active alias check for a continuation */ if (inalias) { if (!isspace(*p)) break; /* done */ } else { s = p; SKIPWORD(p); *p++ = '\0'; /* end the alias name */ if (strcmp(s,user) != 0) continue; /* no match go on */ inalias = 1; } /* process the recipients on the alias line */ SKIPSPACE(p); while(*p != '\0' && *p != '#') { s = p; SKIPWORD(p); if (*p != '\0') *p++ = '\0'; /* find hostname */ if (strchr(s,'@') != NULLCHAR) tp = addlist(head,s,DOMAIN); else tp = addlist(head,s,LOCAL); SKIPSPACE(p); } } (void) fclose(fp); if (inalias) /* found and processed and alias. */ return tp; /* no alias found treat as a local address */ return addlist(head, user, LOCAL); } #if defined(ANSIPROTO) static void smtplog(char *fmt, ...) { va_list ap; char *cp; long t; FILE *fp; if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE) return; time(&t); cp = ctime(&t); rip(cp); fprintf(fp,"%s ",cp); va_start(ap,fmt); vfprintf(fp,fmt,ap); va_end(ap); fprintf(fp,"\n"); fclose(fp); } #else static void smtplog(fmt,arg1,arg2,arg3,arg4) char *fmt; int arg1,arg2,arg3,arg4; { char *cp; long t; FILE *fp; if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE) return; time(&t); cp = ctime(&t); rip(cp); fprintf(fp,"%s ",cp); fprintf(fp,fmt,arg1,arg2,arg3,arg4); fprintf(fp,"\n"); fclose(fp); } #endif /* send mail to a single user. Can be called from the ax24 mailbox or ** from the return mail function in the smtp client */ static int mailuser(data,from,to) FILE *data; char *from; char *to; { int address_type, ret; struct list *tolist = NULLLIST; /* check if address is ok */ if ((address_type = validate_address(to)) == BADADDR) { return 1; } /* if a local address check for an alias */ if (address_type == LOCAL) expandalias(&tolist, to); else /* a remote address is added to the list */ addlist(&tolist, to, address_type); ret = mailit(data,from,tolist); del_list(tolist); return ret; } /* Mailer daemon return mail mechanism */ int mdaemon(data,to,lp,bounce) FILE *data; /* pointer to rewound data file */ char *to; /* Overridden by Errors-To: line if bounce is true */ struct list *lp; /* error log for failed mail */ int bounce; /* True for failed mail, otherwise return receipt */ { time_t t; FILE *tfile; char buf[LINELEN], *cp, *newto = NULLCHAR; int cnt; if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){ while(fgets(buf,sizeof(buf),data) != NULLCHAR) { if(buf[0] == '\n') break; /* Look for Errors-To: */ if(htype(buf) == ERRORSTO && (cp = getaddress(buf,0)) != NULLCHAR){ free(newto); newto = strdup(cp); break; } } if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') || to == NULLCHAR)) return -1; rewind(data); } if((tfile = tmpfile()) == NULLFILE) return -1; time(&t); fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t)); fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname); fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n", Hdrs[FROM],Hostname); fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to); fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT], bounce ? "Failed mail" : "Return receipt"); if(bounce) { fprintf(tfile," ===== transcript follows =====\n\n"); for (; lp != NULLLIST; lp = lp->next) fprintf(tfile,"%s\n",lp->val); fprintf(tfile,"\n"); } fprintf(tfile," ===== %s follows ====\n", bounce ? "Unsent message" : "Message header"); while(fgets(buf,sizeof(buf),data) != NULLCHAR){ if(buf[0] == '\n') break; fputs(buf,tfile); } if(bounce){ fputc('\n',tfile); while((cnt = fread(buf,1,sizeof(buf),data)) > 0) fwrite(buf,1,cnt,tfile); } fseek(tfile,0L,0); /* A null From<> so no looping replys to MAIL-DAEMONS */ (void) mailuser(tfile,"",newto != NULLCHAR ? newto : to); fclose(tfile); free(newto); return 0; }