/* SMTP Server state machine - see RFC 821 * enhanced 4/88 Dave Trulli nn2z * fixed truncation of ill formed lines from foreign systems - K5JB * fixed handling of eol in MSDOS mail files - K5JB */ #include #include #ifdef UNIX #include #endif #include #include #include "options.h" #include "global.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "tcp.h" #include "smtp.h" char *getname(); void mail_delete(),free(),log(); int queuejob(),tprintf(),mlock(); int validate_address(); long get_msgid(); struct list *addlist(); struct list * expandalias(); #ifdef REWRITE #define RWBUFLEN 80 #define TRUE 1 #define FALSE 0 char *rewrite_address(); int dorewrite(); #endif /* 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 NULLCHAR }; /* Reply messages */ static char help[] = "214-Commands:\015\012\62\61\64-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\015\012\62\61\64 End\015\012"; static char banner[] = "220 %s SMTP ready\015\012"; static char closing[] = "221 Closing\015\012"; static char ok[] = "250 Ok\015\012"; static char reset[] = "250 Reset state\015\012"; static char sent[] = "250 Sent\015\012"; static char ourname[] = "250 %s, Share and Enjoy!\015\012"; static char enter[] = "354 Enter mail, end with .\015\012"; static char ioerr[] = "452 Temp file write error\015\012"; static char mboxerr[] = "452 Mailbox write error\015\012"; static char badcmd[] = "500 Say WHAT??\015\012"; #ifdef notdef static char syntax[] = "501 Syntax error\015\012"; #endif static char badfrom[] = "503 Bad originator\015\012"; /* k35a */ static char needrcpt[] = "503 Bad recipient\015\012"; static char unknown[] = "550 <%s> address unknown\015\012"; static struct tcb *smtp_tcb; /* Start up SMTP receiver service */ smtp1(argc,argv) int argc; char *argv[]; { struct socket lsocket; void r_mail(),s_mail(); int atoi(); extern int dotimer(); static char *amount[] = { "","1800" }; lsocket.address = ip_addr; if(argc < 2) lsocket.port = SMTP_PORT; else lsocket.port = atoi(argv[1]); smtp_tcb = open_tcp(&lsocket,NULLSOCK, TCP_SERVER,0,r_mail,NULLVFP,s_mail,0,NULLCHAR); /* K5JB */ dotimer(2,amount); /* k34 */ } /* Shutdown SMTP service (existing connections are allowed to finish) */ smtp0() { if(smtp_tcb != NULLTCB) close_tcp(smtp_tcb); } /* SMTP connection state change upcall handler */ static void s_mail(tcb,unused,new) struct tcb *tcb; char unused,new; { struct mail *mp,*mail_create(); switch(new){ #ifdef QUICKSTART case SYN_RECEIVED: #else case ESTABLISHED: #endif if((mp = mail_create(tcb)) == NULLMAIL){ close_tcp(tcb); break; } (void) tprintf(mp->tcb,banner,hostname); log(tcb,"open SMTP"); break; case CLOSE_WAIT: close_tcp(tcb); break; case CLOSED: log(tcb,"close SMTP"); mp = (struct mail *)tcb->user; mail_delete(mp); del_tcp(tcb); /* Check if server is being shut down */ if(tcb == smtp_tcb) smtp_tcb = NULLTCB; break; } } /* SMTP receiver upcall handler */ static void r_mail(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct mail *mp; char c; struct mbuf *bp; char *inet_ntoa(); void docommand(),doline(); if((mp = (struct mail *)tcb->user) == NULLMAIL){ /* Unknown session */ close_tcp(tcb); return; } recv_tcp(tcb,&bp,cnt); /* Assemble an input line in the session buffer. * Return if incomplete */ while(pullup(&bp,&c,1) == 1){ switch(c){ case '\015': /* Strip cr's */ #ifdef MSDOS case '\032': /* Strip ctrl/Z's */ #endif continue; case '\012': /* Complete line; process it */ mp->buf[mp->cnt] = '\0'; doline(mp); break; default: /* Assemble line */ mp->buf[mp->cnt++] = c; /* too-long line fix - K5JB */ if(mp->cnt == LINELEN-1){ mp->buf[mp->cnt] = '\0'; /* stop that line */ doline(mp); } break; } } } /* Process a line read on an SMTP connection (any state) */ static void doline(mp) register struct mail *mp; { void docommand(),deliver(); switch(mp->state){ case COMMAND_STATE: docommand(mp); break; case DATA_STATE: tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */ if(mp->buf[0] == '.' && mp->buf[1] == '\0'){ mp->state = COMMAND_STATE; /* Also sends appropriate response */ deliver(mp); #if (defined(_OSK) && !defined(_UCC)) tmpclose(mp->data); #else fclose(mp->data); #endif mp->data = NULLFILE; del_list(mp->to); mp->to = NULLLIST; break; } /* for UNIX mail compatiblity */ if (strncmp(mp->buf,"From ",5) == 0) (void) putc('>',mp->data); /* Append to data file */ if(fprintf(mp->data,"%s\n",mp->buf) < 0){ mp->state = COMMAND_STATE; (void) tprintf(mp->tcb,ioerr); } break; } mp->cnt = 0; } /* Create control block, initialize */ static struct mail * mail_create(tcb) register struct tcb *tcb; { register struct mail *mp; if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL) return NULLMAIL; mp->tcb = tcb; /* Downward pointer */ tcb->user = (char *)mp; /* Upward pointer */ return mp; } /* Free resources, delete control block */ static void mail_delete(mp) register struct mail *mp; { if (mp == NULLMAIL) return; if(mp->system != NULLCHAR) free(mp->system); if(mp->from != NULLCHAR) free(mp->from); if(mp->data != NULLFILE) #if (defined(_OSK) && !defined(_UCC)) tmpclose(mp->data); #else fclose(mp->data); #endif del_list(mp->to); free((char *)mp); } /* Parse and execute mail commands */ static void docommand(mp) register struct mail *mp; { register char **cmdp,*arg,*cp,*cmd; FILE *tmpfile(); time_t t; char address_type; #ifdef REWRITE char *newaddr, buf[RWBUFLEN]; #endif cmd = mp->buf; if(mp->cnt < 4){ /* Can't be a legal SMTP command */ (void) tprintf(mp->tcb,badcmd); return; } cmd = mp->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) tprintf(mp->tcb,badcmd); return; } arg = &cmd[strlen(*cmdp)]; /* Skip spaces after command */ while(*arg == ' ') arg++; /* Execute specific command */ switch(cmdp-commands){ case HELO_CMD: if(mp->system != NULLCHAR) free(mp->system); if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULL){ /* If the system is out of memory, just close */ close_tcp(mp->tcb); break; } else { strcpy(mp->system,arg); (void) tprintf(mp->tcb,ourname,hostname); } break; case NOOP_CMD: (void) tprintf(mp->tcb,ok); break; case MAIL_CMD: if(mp->from != NULLCHAR) free(mp->from); if((mp->from = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){ /* If the system is out of memory, just close */ close_tcp(mp->tcb); break; } else { if((cp = getname(arg)) == NULLCHAR){ (void) tprintf(mp->tcb,badfrom); break; /* memory leak here? Note mp->from now unused */ } strcpy(mp->from,cp); (void) tprintf(mp->tcb,ok); } break; case QUIT_CMD: (void) tprintf(mp->tcb,closing); close_tcp(mp->tcb); break; case RCPT_CMD: /* Specify recipient - corrected getname, added 2 tests */ if((cp = getname(arg)) == NULLCHAR){ (void) tprintf(mp->tcb,needrcpt); /* was syntax - k35a */ break; } #ifdef REWRITE if((newaddr = rewrite_address(cp)) != NULLCHAR){ strcpy(buf,newaddr); cp = buf; free(newaddr); } #endif /* check if address is ok */ if ((address_type = validate_address(cp)) == BADADDR) { (void) tprintf(mp->tcb,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) tprintf(mp->tcb,ok); break; case HELP_CMD: (void) tprintf(mp->tcb,help); break; case DATA_CMD: if(mp->to == NULLLIST){ (void) tprintf(mp->tcb,needrcpt); break; } tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */ if((mp->data = tmpfile()) == NULLFILE){ (void) tprintf(mp->tcb,ioerr); break; } /* 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) tprintf(mp->tcb,ioerr); } else { mp->state = DATA_STATE; (void) tprintf(mp->tcb,enter); } break; case RSET_CMD: del_list(mp->to); mp->to = NULLLIST; mp->state = COMMAND_STATE; (void) tprintf(mp->tcb,reset); break; } } /* Given a string of the form , extract the part inside the * brackets and return a pointer to it. Wasn't working. If it is null * we want to return null pointer. While we are at it, reject "@host" * or " @host", "host" can be interpreted as "user" and error handled * later in mailing cycle. - k35a */ static 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'; if(*cp == '\0' || *cp == '@' || *cp == ' ') /* k35a */ return NULLCHAR; return cp; } /* Deliver mail to the appropriate mail boxes and delete temp file */ static void deliver(mp) register struct mail *mp; { int ret,router_queue(),mailit(); /* send to the rqueue */ if ((smtpmode & QUEUE) != 0) { ret = router_queue(mp->tcb,mp->data,mp->from,mp->to); if (ret != 0) (void) tprintf(mp->tcb,ioerr); } else { ret = mailit(mp->tcb,mp->data,mp->from,mp->to); if (ret != 0) (void) tprintf(mp->tcb,mboxerr); } if (ret == 0) (void) tprintf(mp->tcb,sent); } /* used to save local mail or reroute remote mail */ int mailit(tcb,data,from,to) struct tcb *tcb; FILE *data; char *from; struct list *to; { register struct list *ap; register FILE *fp; int c; char mailbox[50]; char *cp; char *desthost; int fail = 0; time_t t; for(ap = to;ap != NULLLIST;ap = ap->next) { fseek(data,0L,0); /* rewind */ /* non local mail queue it */ if (ap->type == DOMAIN) { /* strange I just found a bug ; after next line - 12/17/92 */ if ((desthost = strchr(ap->val,'@')) != NULLCHAR) desthost++; fail = queuejob(tcb,data,desthost,ap->val,from); } else { /* strip off host name */ 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)) fail = queuejob(tcb,data,hostname,ap->val,from); else { sprintf(mailbox,"%s/%s.txt",mailspool,ap->val); #ifdef MSDOS /* eol fix in mail files - K5JB */ if((fp = fopen(mailbox,"a+t")) != NULLFILE) #else if((fp = fopen(mailbox,"a+")) != NULLFILE) #endif { /* this newline stuff is to strip off surplus newlines that * are added by various mailing agents. This _should_ work * as with _OS9 - K5JB */ int newlines = 0; /* k35 */ time(&t); fprintf(fp,"From %s %s",from,ctime(&t)); while((c = getc(data)) != EOF){ if(c == '\n'){ newlines++; continue; } for(;newlines;newlines--) if(putc('\n',fp) == EOF) break; /* next line will catch error */ if(putc(c,fp) == EOF) break; } if(ferror(fp)) fail = 1; else /* Leave only one blank line between msgs */ fprintf(fp,"\n\n"); fclose(fp); printf("New mail arrived for %s\n",ap->val); fflush(stdout); } else fail = 1; (void) rmlock(mailspool,ap->val); if (fail) break; log(tcb,"SMTP recv: To: %s From: %s",ap->val,from); } /* else */ } /* else */ } /* for */ 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]; extern char *getenv(); extern struct tm *localtime(); char *p; static char *days[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static char *months[12] = { "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec" }; /* Fill the tm structure */ ltm = localtime(t); /* this version caused timezone to go away when localtime() was called a second time - see note below */ if (*tz == '\0'){ /* did some work to prevent array over-writes */ if ((p = getenv("TZ")) == NULLCHAR) strcpy(tz,"GMT"); else{ if(ltm->tm_isdst && strlen(p) >= 7) p += 4; /* TZ will be 7 chars for CST6CDT */ /* p[3] = '\0'; This was scrambling the env vbl TZ! */ 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; long atol(); sprintf(sfilename,"%s/sequence.seq",mailqdir); sfile = fopen(sfilename,"r"); /* this can fail but we recover later */ /* if sequence file exists, get the value, otherwise set it */ if (sfile != NULLFILE) { (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 */ /* this was a tough bug to find. If user has no directories set up * we want this to fail gracefully - K5JB */ if((sfile = fopen(sfilename,"w")) == NULLFILE) return 0l; 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; int32 mailroute(); /* if address has @ in it the check dest address */ if ((cp = strchr(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 known site * or one of our aliases */ if (strcmp(cp,hostname) != 0) { if ((addr = mailroute(cp)) == 0 && (smtpmode & QUEUE) == 0) return BADADDR; if (addr != ip_addr) 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 */ /* k35 changed from strchr() so user%host2%host2@host would work */ if ((cp = strrchr(s,'%')) != NULLCHAR) { *cp = '@'; cp++; /* reroute based on host name following the % separator */ 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(tcb,dfile,host,to,from) struct tcb *tcb; FILE *dfile; char *host,*to,*from; { FILE *fp; char tmpstring[50]; char prefix[9]; register int c; sprintf(prefix,"%ld",get_msgid()); log(tcb,"SMTP queue job %s To: %s From: %s",prefix,to,from); mlock(mailqdir,prefix); /* this can fail, but we won't return yet */ sprintf(tmpstring,"%s/%s.txt",mailqdir,prefix); #ifdef MSDOS if((fp = fopen(tmpstring,"wt")) == NULLFILE) #else if((fp = fopen(tmpstring,"w")) == NULLFILE) #endif { (void) rmlock(mailqdir,prefix); return 1; } while((c = getc(dfile)) != EOF){ if(c == 0x1a) /* found ^z making it into n.txt - K5JB */ continue; if(putc(c,fp) == EOF) break; } if(ferror(fp)){ fclose(fp); (void) rmlock(mailqdir,prefix); return 1; } fclose(fp); sprintf(tmpstring,"%s/%s.wrk",mailqdir,prefix); #ifdef MSDOS if((fp = fopen(tmpstring,"wt")) == NULLFILE) #else if((fp = fopen(tmpstring,"w")) == NULLFILE) #endif { (void) rmlock(mailqdir,prefix); return 1; } fprintf(fp,"%s\n%s\n%s\n",host,from,to); fclose(fp); (void) rmlock(mailqdir,prefix); return 0; } /* Deliver mail to the appropriate mail boxes */ int router_queue(tcb,data,from,to) struct tcb *tcb; 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); #ifdef MSDOS if((fp = fopen(tmpstring,"wt")) == NULLFILE) #else if((fp = fopen(tmpstring,"w")) == NULLFILE) #endif { (void) rmlock(routeqdir,prefix); return 1; } fseek(data,0L,0); /* rewind */ 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); #ifdef MSDOS if((fp = fopen(tmpstring,"wt")) == NULLFILE) #else if((fp = fopen(tmpstring,"w")) == NULLFILE) #endif { (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); log(tcb,"SMTP 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 *)calloc(1,sizeof(struct list)); if (tp == NULLLIST) return NULLLIST; tp->next = NULLLIST; /* allocate storage for the char string */ if ((tp->val = (char *)malloc((unsigned)strlen(val)+1)) == NULLCHAR) { (void) free((char *)tp); return NULLLIST; } strcpy(tp->val,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 */ struct list * expandalias(head, user) struct list **head; char *user; { void rip(); FILE *fp; register char *s,*p; int inalias; struct list *tp; char buf[LINELEN]; /* no alias file found */ if ((fp = fopen(alias, "r")) == NULLFILE) return addlist(head, user, LOCAL); inalias = 0; 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); } #ifdef REWRITE /* swiped from NOS */ static int Star(s,p,argv) register char *s; register char *p; register char **argv; { int splatmat(); char *cp = s; while (splatmat(cp, p, argv) == FALSE) if(*++cp == '\0') return -1; return (int)(cp - s); } /* shortened the RE pattern matcher here to only what we need - K5JB */ int splatmat(s,p,argv) register char *s; register char *p; register char **argv; { register int cnt; for(; *p; s++,p++){ switch(*p){ default: if(*s != *p) return FALSE; continue; case '*': /* Trailing star matches everything. */ if(argv == (char **)0) return *++p ? 1 + Star(s, p, (char **)0) : TRUE; if(*++p == '\0') cnt = strlen(s); else if((cnt = Star(s, p, argv+1)) == -1) return FALSE; *argv = malloc(cnt+1); strncpy(*argv,s,cnt); *(*argv + cnt) = '\0'; return TRUE; } } return *s == '\0'; } /* Read the rewrite file for lines where the first word is a regular * expression and the second word are rewriting rules. The special * character '$' followed by a digit denotes the string that matched * a '*' character. The '*' characters are numbered from 1 to 9. * Example: the line "*@*.* $2@$1.ampr.org" would rewrite the address * "foo@bar.xxx" to "bar@foo.ampr.org". * $H is replaced by our hostname, and $$ is an escaped $ character. * If the third word on the line has an 'r' character in it, the function * will recurse with the new address. */ char * rewrite_address(addr) char *addr; { char *argv[10], buf[RWBUFLEN], *cp, *cp2, *retstr; int cnt; FILE *fp; extern char hostname[],*Rewritefile; void rip(); if ((fp = fopen(Rewritefile,"rt")) == NULLFILE) return NULLCHAR; memset((char *)argv,0,10*sizeof(char *)); while(fgets(buf,sizeof(buf),fp) != NULLCHAR) { if(*buf == '#') /* skip commented lines */ continue; if((cp = strchr(buf,' ')) == NULLCHAR) /* get the first word */ if((cp = strchr(buf,'\t')) == NULLCHAR) continue; *cp = '\0'; if((cp2 = strchr(buf,'\t')) != NULLCHAR){ *cp = ' '; cp = cp2; *cp = '\0'; } if(!splatmat(addr,buf,argv)) continue; /* no match */ rip(++cp); /* scan past additional whitespaces */ while (*cp == ' ' || *cp == '\t') ++cp; cp2 = retstr = (char *) calloc(1,RWBUFLEN); while(*cp != '\0' && *cp != ' ' && *cp != '\t') if(*cp == '$'){ if(isdigit(*(++cp))) if(argv[*cp - '0'-1] != '\0') strcat(cp2,argv[*cp - '0'-1]); if(*cp == 'h' || *cp == 'H') /* Our hostname */ strcat(cp2,hostname); if(*cp == '$') /* Escaped $ character */ strcat(cp2,"$"); cp2 = retstr + strlen(retstr); cp++; }else *cp2++ = *cp++; for(cnt=0; argv[cnt] != NULLCHAR; ++cnt) free(argv[cnt]); fclose(fp); /* If there remains an 'r' character on the line, repeat * everything by recursing. */ if(strchr(cp,'r') != NULLCHAR || strchr(cp,'R') != NULLCHAR) { if((cp2 = rewrite_address(retstr)) != NULLCHAR) { free(retstr); return cp2; } } return retstr; } fclose(fp); return NULLCHAR; } #ifdef REWRITECMD /* smtp EXPN can be used instead of this - K5JB */ int dorewrite(argc,argv) int argc; char *argv[]; { char *address = rewrite_address(argv[1]); printf("to: %s\n", address ? address : argv[1]); if(address) free(address); return 0; } #endif /* REWRITECMD */ #endif /* REWRITE */