P8000/WEGA/contrib/K5JB/smtpserv.c

1024 lines
23 KiB
C

/* 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 <stdio.h>
#include <time.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#include <ctype.h>
#include <string.h>
#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 <user@host>, 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 */