/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */
/*
 *	Several extensive changes by Matti Aarnio <mea@nic.funet.fi>
 *	Copyright 1991-1997.
 */

#include "smtpserver.h"

/*
 * The way VRFY and EXPN are implemented, and even MAIL FROM and RCPT TO
 * checking, we somehow connect to a router and ask it to do stuff for us.
 * There are three routines, one to connect to the router, one to kill it
 * off again, and the line-getting routine that gets everything the router
 * prints at us, one line at a time.
 */

static char promptbuf[30];
static int  promptlen;
static FILE *tofp, *fromfp;

static char *mgets __((SmtpState *SS, int *, FILE *));

static char *mgets(SS, flagp, fp)
	SmtpState *SS;
	int *flagp;
	FILE *fp;
{
	register int bufsize, c;
	register char *cp;
	char *buf;
	int ccnt = 0;

	if (*flagp) {
	  *flagp = 0;
	  return NULL;
	}

	bufsize = 16;
	cp = buf = emalloc(bufsize);
	while ((c = getc(fp)) != EOF) {
	  *cp = c;
	  ++ccnt;
	  if (c == '\n') {
	    *flagp = 0;
	    break;
	  }
	  ++cp;
	  if (strncmp(cp - promptlen, promptbuf, promptlen) == 0) {
	    free(buf);
	    buf = NULL;
	    *flagp = 1;
	    return NULL;
	  }
	  if (bufsize - 2 < ccnt) {
	    bufsize <<= 1;
	    buf = erealloc(buf, bufsize);
	    cp  = buf + ccnt;
	  }
	}

	if (buf)
	  *cp = '\0';

	if (debug && buf)
	  fprintf(stdout, "000 '%s'\r\n",buf);

	if (c == EOF) {
	  printf("got EOF!\n");
	  free(buf);
	  return NULL;
	}

	return buf;
}

static char *newenviron[] = { "SMTPSERVER=y", (char *)0 };

static int callr __((SmtpState *SS));
static int
callr(SS)
	SmtpState *SS;
{
	int sawend, pid, to[2], from[2];
	char *bufp, *cp;

	if (pipe(to) < 0 || pipe(from) < 0)
		return -1;

#if 0
#if 0
	SIGNAL_HANDLE(SIGCHLD, SIG_DFL); /* ?? SIG_DFL  on SVR4 ? */
#else
	SIGNAL_HANDLE(SIGCHLD, reaper);
#endif
	SIGNAL_HANDLE(SIGPIPE, SIG_IGN);
#endif

	if (routerprog == NULL) {
	  if ((cp = getzenv("MAILBIN")) == NULL) {
	    zsyslog((LOG_ERR, "MAILBIN unspecified in zmailer.conf"));
	    return -1;
	  }
	  routerprog = emalloc(strlen(cp)+sizeof "router"+2);
	  sprintf(routerprog, "%s/router", cp);
	}

	if ((pid = fork()) == 0) { /* child */
	  pid = getpid();
	  dup2(to[0], 0);
	  dup2(from[1], 1);
	  dup2(from[1], 2);
	  close(to[0]); close(to[1]);
	  close(from[0]); close(from[1]);
	  runasrootuser();	/* XXX: security alert! */
	  environ = newenviron;
	  execl(routerprog, "router", "-io-i", (char *)NULL);
#define	BADEXEC	"8@$#&(\n\n"
	  write(1, BADEXEC, strlen(BADEXEC));
	  _exit(1);
	} else if (pid < 0)
	  return -1;

	close(to[0]); close(from[1]);
	tofp = fdopen(to[1], "w");
	fromfp = fdopen(from[0], "r");

	/* stuff a command to print something recognizable down the pipe */
	sprintf(promptbuf, "%d# ", getpid());
	promptlen = strlen(promptbuf);

	/*
	 * Gotta be careful here: ihostaddr is generated within the program
	 * from the IP address, but rhostname is specified by the remote host.
	 * Although it may be caught by partridge(), lets make extra sure.
	 */
	for (cp = SS->rhostname; *cp; ++cp)
	  if (*cp == '\'')
	    *cp = '?';
	if (SS->rhostname[strlen(SS->rhostname)-1] == '\\')
	  SS->rhostname[strlen(SS->rhostname)-1] = '?';

	fprintf(tofp, "PS1='%s' ; %s %s '%s' '%s'\n", promptbuf,
		      ROUTER_SERVER, RKEY_INIT, SS->rhostname, SS->ihostaddr);
	fflush(tofp);

	sawend = 0;
	while ((bufp = mgets(SS, &sawend, fromfp)) != NULL) {
	  if (strncmp(cp, BADEXEC, strlen(BADEXEC)-2) == 0) {
	    free(cp);
	    killr(SS, pid);
	    return -1;
	  }
	  /*printf("241%c%s\n", sawend == 1 ? ' ' : '-', buf);*/
	  free(bufp);
	}

	return pid;
}

void
killr(SS, pid)
	SmtpState *SS;
	int pid;
{
	if (pid > 0) {
	  fclose(tofp);
	  fclose(fromfp);
	  kill(pid, SIGKILL);
	}
}


/*
 * Now we can do VRFY et al using the router we have connected to.
 */

char *
router(SS, function, holdlast, args)
	SmtpState *SS;
	char *function, *args;
	int holdlast;
{
	char *bufp, *prevb = NULL;
	int sawend = 0;

	if (args == NULL) {
	  type(SS,501,NULL,NULL);
	  return NULL;
	}

	if (routerpid <= 0 && (routerpid = callr(SS)) <= 0) {
	  type(SS,451,NULL,NULL);
	  return NULL;
	}

	fprintf(tofp, "%s %s \"", ROUTER_SERVER, function);

	/* Process all double-quotes and backslashes so that
	   no surprises happen.. */

	for ( ; *args ; ++args ) {
	  if (*args == '\\' || *args == '"')
	    putc('\\', tofp);
	  putc(*args, tofp);
	}
	fprintf(tofp, "\"\n");
	fflush(tofp);

	for (;;) {
	  /*
	   * We want to give the router the opportunity to report
	   * result codes, e.g. for boolean requests.  If the first
	   * three characters are digits and the 4th a space or '-',
	   * then pass through.
	   */
	  bufp = mgets(SS, &sawend, fromfp);
	  if (!bufp) break;

	  if (debug) {
	    fprintf(stdout,"001 Got string: '%s'\r\n", bufp);
	    typeflush(SS);
	  }

	  if (prevb != NULL) {
	    if (strlen(prevb) > 4 &&
		isdigit(prevb[0]) && isdigit(prevb[1]) && isdigit(prevb[2])
		&& (prevb[3] == ' ' || prevb[3] == '-')) {
	      printf("%s\r\n", prevb);
	    } else {
	      printf("250-%s\r\n", prevb);
	    }
	    free(prevb);
	  }
	  prevb = bufp;
	}

	if (!holdlast) {
	  /* -------- Print the last line too -------- */
	  if (strlen(prevb) > 4 &&
	      isdigit(prevb[0]) && isdigit(prevb[1]) && isdigit(prevb[2])
	      && (prevb[3] == ' ')) {
	    printf("%s\r\n", prevb);
	  } else {
	    printf("250 %s\r\n", prevb);
	  }
	  free(prevb);
	  prevb = "<any-pointer>";
	} else {
	  /* Uhhh.... No output! */
	  if (!prevb)
	    printf("500 **INTERNAL*ERROR**\r\n");
	}
	typeflush(SS);

	return prevb; /* It may be unfreeable pointer... */
}
