/*
 *	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.
 */
/*
 * Zmailer SMTP-server divided into bits
 *
 * The basic commands:
 *
 *  - HELO/EHLO
 *  - MAIL
 *  - RCPT
 *  - ETRN/TURNME
 *  - VRFY
 *  - EXPN
 *
 */

#include "smtpserver.h"

static char *orcpt_string(str)
char *str;
{
	/* Verify that the input is valid RFC 1981 XTEXT string! */
	char *stro = rfc822atom(str);
	char *s2;

	if (stro == str) return NULL;
	if (*stro != ';') {
	  rfc821_error_ptr = stro-1;
	  return NULL;
	}
	s2 = xtext_string(++stro);
	if (s2 == stro)
	  return NULL;
	return s2;
}

/* If the argument is not in valid domain name syntax, return 1 */

static int partridge __((SmtpState *, char*, struct smtpconf *));

static int
partridge(SS, s, cfinfo)
	SmtpState *SS;
	char *s;
	struct smtpconf *cfinfo;
{
	char *p = rfc821_domain(s,STYLE(cfinfo, 'R'));
	if (p == s) return 1;
	while (*p == ' ' || *p == '\n') ++p;
	if (*p == 0) return 0;

	rfc821_error_ptr = p;
	rfc821_error = "Spurious junk after the DOMAIN in HELO/EHLO argument";

	return 1;
}

/* The input this macro gets is already verified to be in RFC-821 form.
   In it we can have double-quotes in the input, but only \ -quoted!
   Therefore it is highly unlikely to need even more than two chars
   longer form for the RFC-822 format string. */

#ifdef USE_ALLOCA
#define RFC821_822QUOTE(cp) \
	if (cp && strchr(cp,'\\') != NULL && *cp != '"') {	\
	  char *s1 = cp, *s2;					\
	  s2 = newcp = alloca(strlen(cp)+8);			\
	  *s2++ = '"';						\
	  while (*s1) {						\
	    if (*s1 == '@')					\
	      break; /* Unquoted AT --> move to plain copying! */ \
	    if (*s1 == '\\' && s1[1] != 0)			\
	      *s2++ = *s1++;					\
	    /* Normal copy */					\
	    *s2++ = *s1++;					\
	  }							\
	  *s2++ = '"';						\
	  while (*s1)						\
	    *s2++ = *s1++;					\
	  cp = newcp;						\
	}
#else /* No alloca .. must do without */
#define RFC821_822QUOTE(cp) \
	if (cp && strchr(cp,'\\') != NULL && *cp != '"') {	\
	  char *s1 = cp, *s2;					\
	  s2 = newcp = emalloc(strlen(cp)+8);			\
	  *s2++ = '"';						\
	  while (*s1) {						\
	    if (*s1 == '@')					\
	      break; /* Unquoted AT --> move to plain copying! */ \
	    if (*s1 == '\\' && s1[1] != 0)			\
	      *s2++ = *s1++;					\
	    /* Normal copy */					\
	    *s2++ = *s1++;					\
	  }							\
	  *s2++ = '"';						\
	  while (*s1)						\
	    *s2++ = *s1++;					\
	  cp = newcp;						\
	}
#endif

/* SMTP-server verb subroutines */

void
smtp_helo(SS,buf,cp)
SmtpState *SS;
char *buf, *cp;
{

	if (SS->state != Hello && SS->state != MailOrHello) {
	  switch (SS->state) {
	    case Mail:
		cp = "Waiting for MAIL command";
		break;
	    case Recipient:
		cp = "Waiting for RCPT command";
		break;
	    default:
		cp = NULL;
		break;
	  }
	  type(SS,503, NULL, cp);
	  return;
	}
	while (*cp == ' ' || *cp == '\t') ++cp;
	SS->helobuf[0] = 0;
	sscanf(cp, "%s", SS->helobuf);

	
	SS->policyresult = policytest(policydb, &SS->policystate,
				      POLICY_HELONAME, cp);
	/* actually we accept here about anything, and mark
	   our decissions up into the 'policystate' variables */


	/*
	 * Craig P. says we have to spit back syntactically
	 * invalid helo parameters at this stage, which is
	 * hard to do right since it requires a full '822
	 * tokenizer.  We do a half-hearted attempt here.
	 */
	/*
	 * Matti A. says we have a more-than-half-hearted
	 * tokenizer -- RFC821SCN.C, lets use it :)
	 * We need it for proper handling of ESMTP anyway
	 */
 	if (checkhelo && skeptical && partridge(SS, SS->helobuf, cfinfo)) {
	  rfc821_error_ptr += (cp - SS->helobuf);
	  type821err(SS,-501, "", buf,
		     "Invalid `%.200s' parameter!",
		     buf);
	  type(SS,501, "", "Err: %s", rfc821_error);
	  strcpy(SS->helobuf,"Bad.Helo.Input");
	  return;
	}
	/* At least check the input, though say "Ok, Master" while
	   complaining.. */
	if (!checkhelo && partridge(SS, SS->helobuf, cfinfo)) {
	  if (SS->carp->cmd != Hello) {
	    rfc821_error_ptr += (cp - SS->helobuf);
	    type821err(SS,-250, "", buf, "Invalid `%.200s' parameter!", buf);
	    type(SS,-250, NULL, "Err: %s", rfc821_error);
	  }
	  if (SS->carp->cmd == Hello)
	    strcpy(SS->helobuf,"Bad.HELO.Input");
	  else
	    strcpy(SS->helobuf,"Bad.EHLO.Input");
	}
	cfinfo = findcf(SS->helobuf);
	if (cfinfo != NULL && *(cfinfo->flags) == '!') {
	  if (cfinfo->flags[1] != '\0')
	    type(SS,501, NULL, "%s", (cfinfo->flags)+1);
	  else
	    type(SS,501, NULL, "Sorry, access denied.");
	  return;
	}
	/* Check helobuf corresponds to the reverse address */
	if (skeptical && SS->ihostaddr[0] != '\0'
	    && SS->rhostname[0] != '\0' && SS->rhostname[0] != '['
	    && !CISTREQ(SS->helobuf, SS->rhostname)) {
	  if (checkhelo) {
	    type(SS,-250, NULL, "%s That hostname is inconsistent with",
	         SS->myhostname);
	    type(SS,-250, NULL, "%s your address to name mapping.",
	         SS->myhostname);
	  }
	  type(SS, SS->carp->cmd==Hello2 ? -250: 250, NULL,
	       "%s expected \"%s %s\"", SS->myhostname,
	       SS->carp->verb, SS->rhostname);
	} else
	    type(SS,SS->carp->cmd==Hello2 ? -250: 250, NULL,
		 "%s Hello %s", SS->myhostname, SS->helobuf);
	if (SS->carp->cmd == Hello2) {

	  /* ESMTP -- RFC 1651,1652,1653,1428 thingies */
	  char sizebuf[20];

	  sprintf(sizebuf,"SIZE %ld",maxsize); /* 0: No fixed max size
						  in force, else:
						  The FIXED maximum */
	  type(SS,-250,NULL, sizebuf);
	  type(SS,-250,NULL, "8BITMIME");
	  type(SS,-250,NULL, "PIPELINING");
	  type(SS,-250,NULL, "CHUNKING"); /* RFC 1830 */
	  type(SS,-250,NULL, "ENHANCEDSTATUSCODES");
	  type(SS,-250,NULL, "EXPN");
	  type(SS,-250,NULL, "VRFY");
	  type(SS,-250,NULL, "DSN");
	  type(SS,-250,NULL, "X-RCPTLIMIT 10000"); /* VERY HIGH figure, normal is 100 */
	  type(SS,-250,NULL, "ETRN");
	  type(SS, 250,NULL, "HELP");
	  SS->with_protocol = WITH_ESMTP;
	}
	SS->state = MailOrHello;
}

void
smtp_mail(SS,buf,cp,insecure)
SmtpState *SS;
char *buf, *cp;
int insecure;
{
	char *s;
	int rc;
	char *drpt_envid;
	char *drpt_ret;
	char *bodytype = NULL;
	char *newcp = NULL;

	SS->sender_ok = 0; /* Set it, when we are sure.. */

	/* For ESMTP SIZE-option use we need to know
	   how much space we have, it is easiest by
	   opening the spool file here, and asking
	   filesystem to report free space..
	   We use at most half of the free space,
	   and each recipient will use the claimed
	   size, thus marking up its reception..	*/

	if (SS->mfp == NULL
	    && (SS->mfp = mail_open(MSG_RFC822)) == NULL) {
	  type(SS,452,m430, "mail_open() failed with errno %d: %s", errno, strerror(errno));
	  return;
	}

	availspace = fd_statfs(FILENO(SS->mfp));
	if (availspace < 0)
	  availspace = 2000000000; /* Over 2G ? */
	availspace >>= 1;

	if (SS->carp->cmd == Mail2 || SS->carp->cmd == Send2) {
	  SS->with_protocol = WITH_ESMTP;
	}
	if (SS->state != Mail && SS->state != MailOrHello) {
	  switch (SS->state) {
	    case Hello:
		cp = "Waiting for HELO/EHLO command";
		break;
	    case Recipient:
		cp = "Waiting for RCPT command";
		break;
	    default:
		cp = NULL;
		break;
	  }
	  type(SS,503, m551, cp);
	  return;
	}
	if (!CISTREQN(cp, "From:", 5)) {
	  type(SS,501, m552, "where is From: in that?");
	  return;
	}
	for (cp = cp+5; *cp != '\0' && *cp != '<'; ++cp)
	  if (!isascii(*cp) || !isspace(*cp)) {
	    type(SS,501, m552, "where is <...> in that?");
	    return;
	  }
	if (*cp == '\0') {
	  type(SS,501, m552, "where is <...> in that?");
	  return;
	} else if (*cp != '<') {
	  type(SS,501, m552, "strangeness between : and <");
	  return;
	}
	if (*(cp+1) == '<') {
	  type(SS,501, m552, "there are too many <'s in that!");
	  return;
	}

	/* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
	s = rfc821_path(cp,STYLE(cfinfo, 'R'));
	if (s == cp) {
	  /* Failure.. */
	  type821err(SS,501,m552,buf,"Path data: %.200s",rfc821_error);
	  return;
	}
	if (*s == '>') {
	  type(SS,501, m552, "there are too many >'s in that!");
	  return;
	}
	++cp;		/* Skip the initial '<' */
	*(s-1) = 0;	/* Scrub the final  '>' */

	/* BODY=8BITMIME SIZE=nnnn ENVID=xxxxxx RET=xxxx */
	SS->sizeoptval = -1;
	SS->sizeoptsum = -1;
	drpt_envid = NULL;
	drpt_ret   = NULL;
	rc = 0;
	while (*s) {
	  while (*s && (*s == ' '||*s == '\t')) ++s;
	  if (CISTREQN("RET=",s,4)) {
	    if (drpt_ret) {
	      type(SS,501,m554,"RET-param double defined!");
	      return;
	    }
	    s += 4;
	    drpt_ret = s;
	    if (CISTREQN("FULL",s,4) ||
		CISTREQN("HDRS",s,4))
	      s += 4;
	    if (*s && *s != ' ' && *s != '\t') {
	      type(SS,501,m454,"RET-param data error");
	      return;
	    }
	    if (*s) *s++ = '\0';
	    continue;
	  }
	  if (CISTREQN("BODY=",s,5)) {
	    /* Actually we do not use this data... */
	    s += 5;
	    if (bodytype != NULL) {
	      type(SS,501,m554,"BODY= double definition!");
	      rc = 1;
	      break;
	    }
	    if (CISTREQN(s,"8BITMIME",8)) {
	      bodytype = "8BITMIME";
	      s += 8;
	    } else if (CISTREQN(s,"BINARYMIME",10)) {
	      bodytype = "BINARYMIME";
	      s += 10;
	    } else if (CISTREQN(s,"7BIT",4)) {
	      bodytype = "7BIT";
	      s += 4;
	    }
	    if (*s && *s != ' ' && *s != '\t') {
	      type(SS,455, m554, "BODY-param data error, must be one of: 8BITMIME/BINARYMIME/7BIT");
	      rc = 1;
	      break;
	    }
	    continue;
	  }
	  if (CISTREQN("SIZE=",s,5)) {
	    s += 5;
	    if (SS->sizeoptval != -1) {
	      type(SS,455,m554,"SIZE-param double definition!");
	      rc = 1;
	      break;
	    }
	    /* This data we use, gather the value*/
	    SS->sizeoptval = 0;
	    while (isascii(*s) && isdigit(*s)) {
	      SS->sizeoptval *= 10;
	      SS->sizeoptval += (*s - '0');
	      ++s;
	    }
	    if (*s && *s != ' ' && *s != '\t') {
	      type(SS,501,m554,"SIZE-param data error");
	      rc = 1;
	      break;
	    }
	    continue;
	  }
	  /* IETF-NOTARY  SMTP-DRPT extensions */
	  if (CISTREQN("ENVID=",s,6)) {
	    if (drpt_envid != NULL) {
	      type(SS,501,m554,"ENVID double definition!");
	      rc = 1;
	      break;
	    }
	    drpt_envid = s+6;
	    s = xtext_string(s+6);
	    if (s == s+6) {
	      type821err(SS,-501,m554,buf,"Invalid ENVID value '%.200s'", drpt_envid);
	      type(SS,501,m554,"ENVID data contains illegal characters!");
	      rc = 1;
	      break;
	    }
	    *s++ = 0;
	    if (*drpt_envid == 0) {
	      type(SS,501,m554,"ENVID= without data!");
	      rc = 1;
	      break;
	    }
	    continue;
	  }
	  type(SS,555,m454,"Unknown MAIL FROM:<> parameter: %s",s);
	  rc = 1;
	  break;
	}
	if (rc != 0) return; /* Error(s) in previous loop.. */

	/*printf("  <path>: len=%d \"%s\"\n",cp-s,cp);*/

	RFC821_822QUOTE(cp) ;
	
	SS->policyresult = policytest(policydb, &SS->policystate,
				      POLICY_MAILFROM, cp);
	if (SS->policyresult < 0) {
	  type(SS,553,"5.1.8","The source address is in a reject list");
	  if (s) free(s);
#ifndef USE_ALLOCA
	  if (newcp) free(newcp);
#endif
	  return;
	}

	s = NULL;
	if (*cp != '\0' && STYLE(cfinfo, 'f')) {
	  if ((s = router(SS, RKEY_FROM, 1, cp)) == NULL)
	    /* the error was printed in router() */
#ifndef USE_ALLOCA
	    if (newcp) free(newcp);
#endif
	    return;
	  if (atoi(s) / 100 != 2) {
	    /* verification failed */
	    type(SS,atoi(s), s+4, "Failed", "Failed");
	    free(s);
#ifndef USE_ALLOCA
	    if (newcp) free(newcp);
#endif
	    return;
	  }
	}

	if (SS->mfp == NULL &&
	    (SS->mfp = mail_open(MSG_RFC822)) == NULL) {
	  if (s)
	    free(s);
	  type(SS,452,m430, (char *)NULL);
#ifndef USE_ALLOCA
	  if (newcp) free(newcp);
#endif
	  return;
	}
	rewind(SS->mfp);
	if (insecure)
	  fprintf(SS->mfp, "external\n");
	if (SS->ihostaddr[0] != '\0')
	  fprintf(SS->mfp, "rcvdfrom %s (%s)\n", SS->ident_username,
		  SS->ihostaddr);
	else
	  fprintf(SS->mfp, "rcvdfrom %s\n", SS->ident_username);
	if (bodytype != NULL)
	  fprintf(SS->mfp, "bodytype %s\n", bodytype);
	fprintf(SS->mfp, "with %s\n", SS->with_protocol);
	if (ident_flag)
	  fprintf(SS->mfp, "identinfo %s\n", SS->ident_username);

	if (*cp == '\0')
	  fprintf(SS->mfp, "channel error\n"); /* Empty  MAIL FROM:<> */
	else
	  fprintf(SS->mfp, "from <%s>\n", cp);

#ifndef USE_ALLOCA
	if (newcp) free(newcp);
#endif

	if (drpt_envid != NULL)
	  fprintf(SS->mfp, "envid %s\n", drpt_envid);
	if (drpt_ret != NULL)
	  fprintf(SS->mfp, "notaryret %s\n", drpt_ret);

	if (ferror(SS->mfp)) {
	  type(SS,452,m430,(char *)NULL);
	} else if (SS->sizeoptval > maxsize && maxsize > 0) {
	  type(SS,552,"5.3.4","The message size exceeds fixed maximum of %ld chars for message size", maxsize);
	} else if (SS->sizeoptval > availspace) {
	  type(SS,452,"4.3.1","Try again later, insufficient storage available at the moment");
	} else {
	  if (s)
	    type(SS, atoi(s), s+4, "Ok");
	  else
	    type(SS,250, "2.1.0", "Sender syntax Ok");
	  SS->sender_ok = 1;
	}
	if (s)
	  free(s);
	SS->state = Recipient;
	SS->rcpt_count = 0;
	SS->from_box = (*cp == 0);
}


void
smtp_rcpt(SS,buf,cp)
SmtpState *SS;
char *buf, *cp;
{
	char *s;
	char *drpt_notify, *drpt_orcpt;
	char *newcp = NULL;

	if (SS->state != Recipient && SS->state != RecipientOrData) {
	  switch (SS->state) {
	    case Hello:
		cp = "Waiting for HELO command";
		break;
	    case Mail:
	    case MailOrHello:
		cp = "Waiting for MAIL command";
		break;
	    default:
		cp = NULL;
		break;
	  }
	  type(SS,503, m551, cp);
	  return;
	}
	if (!CISTREQN(cp, "To:", 3)) {
	  type(SS,501, m552, "where is To: in that?");
	  return;
	}
	for (cp = cp+3; *cp != '\0' && *cp != '<'; ++cp)
	  if (!isascii(*cp) || !isspace(*cp)) {
	    type(SS,501, m552, "where is <...> in that?");
	    return;
	  }
	if (*cp == '\0') {
	  type(SS,501, m552, "where is <...> in that?");
	  return;
	} else if (*cp != '<') {
	  type(SS,501, m552, "strangeness between : and <");
	  return;
	}
	if (*(cp+1) == '<') {
	  type(SS,501, m552, "there are too many <'s in that!");
	  return;
	}

	/* "<" [ <a-t-l> ":" ] <localpart> "@" <domain> ">" */
	s = rfc821_path(cp,STYLE(cfinfo, 'R'));
	if (s == cp) {
	  /* Failure.. */
	  type821err(SS,501,m552,buf,"Path data: %s",rfc821_error);
	  return;
	}
	if (*s == '>') {
	  type(SS,501, m552, "there are too many >'s in that!");
	  return;
	}
	++cp;		/* Skip the initial '<' */
	*(s-1) = 0;	/* Scrub the final  '>' */

	/*printf("  <path>: len=%d \"%s\"\n",cp-s,cp);*/

	if (*cp == '\0') {
#if 0
	  SS->mfp->_flags |= _IO_ERR_SEEN; /* test at Linux libc 5.x */
#endif
	  type(SS,501, "5.1.3", "what is a null recipient?");
	  return;
	}

	drpt_notify = NULL;
	drpt_orcpt  = NULL;

	while (*s) {
	  while (isascii(*s) && isspace(*s)) ++s;
	  /* IETF-NOTARY  SMTP-RCPT-DRPT extensions */
	  if (CISTREQN("NOTIFY=",s,7)) {
	    if (drpt_notify) {
	      type(SS,501,m554,"NOTIFY-param double defined!");
	      return;
	    }
	    drpt_notify = s;
	    s += 7;
	    while (*s) {
	      if (CISTREQN("SUCCESS",s,7))
		s += 7;
	      else if (CISTREQN("FAILURE",s,7))
		s += 7;
	      else if (CISTREQN("DELAY",s,5))
		s += 5;
	      else if (CISTREQN("NEVER",s,5))
		s += 5;
	      if (*s != ',')
		break;
	      ++s;
	    }
	    if (*s && *s != ' ' && *s != '\t') {
	      type(SS,455,m454,"NOTIFY-param data error");
	      return;
	    }
	    if (*s) *s++ = '\0';
	    continue;
	  }
	  if (CISTREQN("ORCPT=",s,6)) {
	    if (drpt_orcpt) {
	      type(SS,501,m554,"ORCPT-param double defined!");
	      return;
	    }
	    drpt_orcpt = s;
	    s = orcpt_string(s+6);
	    if (s == NULL) {
	      type821err(SS,-501,m454,buf,"Invalid ORCPT value '%s'", drpt_orcpt);
	      type(SS,501,m454,"ORCPT-param data error!");
	      return;
	    }
	    if (s && *s != 0) *s = '\0';
	    ++s;
	    continue;
	  }
	  type(SS,555,"Unknown RCPT TO:<> parameter: %s",s);
	  return;
	}


	RFC821_822QUOTE(cp) ;

	SS->policyresult = policytest(policydb, &SS->policystate,
				      POLICY_RCPTTO, cp);
	if (SS->policyresult < 0) {
	  type(SS,553,"5.1.8","This target address is in a reject list");
	  if (s) free(s);
#ifndef USE_ALLOCA
	  if (newcp) free(newcp);
#endif
	  return;
	}

	s = NULL;
	if (STYLE(cfinfo, 't')) {
	  if ((s = router(SS, RKEY_TO, 1, cp)) == NULL)
	    /* the error was printed in router() */
	    return;
	  if (atoi(s) / 100 != 2) {
	    /* verification failed */
	    type(SS,atoi(s), s+4, "Failed", "Failed");
	    free(s);
#ifndef USE_ALLOCA
	    if (newcp) free(newcp);
#endif
	    return;
	  }
	}

	/* FIRST 'todsn', THEN 'to' -HEADER */

	/* IETF-NOTARY DSN data: */
	fputs("todsn",SS->mfp);
	if (drpt_notify) fprintf(SS->mfp, " %s", drpt_notify);
	if (drpt_orcpt) {
	  fprintf(SS->mfp, " %s", drpt_orcpt);
	} else {
	  char *s = cp;
	  fputs(" ORCPT=rfc822;",SS->mfp);
	  while (*s) {
	    u_char c = *s;
	    if ('!' <= c && c <= '~' && c != '+' && c != '=')
	      putc(c,SS->mfp);
	    else
	      fprintf(SS->mfp,"+%02X",c);
	    ++s;
	  }
	}
	putc('\n',SS->mfp);

	/* Normal "RCPT TO:<>" data: */
	fprintf(SS->mfp, "to <%s>\n", cp);

#ifndef USE_ALLOCA
	if (newcp) free(newcp);
#endif

	if (SS->sizeoptval < 0)
	  SS->sizeoptval = 0;
	SS->sizeoptsum += SS->sizeoptval;
	if (ferror(SS->mfp)) {
	  type(SS,452,m430,(char *)NULL);
	} else if (maxsize > 0 && SS->sizeoptsum > maxsize) {
	  type(SS,552, "5.3.4", "Message size exceeds fixed maximum size of %ld chars for acceptable email", maxsize);
	} else if (SS->sizeoptsum > availspace) {
	  type(SS,452, "4.3.1", "insufficient storage space, try again later");
	} else if (s) {
	  if (SS->from_box && SS->rcpt_count > MaxErrorRecipients) {
	    type(SS,552, "5.0.0", "SPAM trap -- too many recipients for an empty source address!");
	  } else
	    type(SS, atoi(s), s+4, "Ok");
	  SS->rcpt_count += 1;
	} else {
	  if (SS->from_box && SS->rcpt_count > MaxErrorRecipients) {
	    type(SS,552, "5.0.0", "SPAM trap -- too many recipients for an empty source address!");
	  } else
	    if (SS->sizeoptval)
	      type(SS,250, "2.1.5", "Ok; can accomodate %d byte message",
		   SS->sizeoptval);
	    else
	      type(SS,250, "2.1.5", "Recipient address syntax Ok");
	  SS->rcpt_count += 1;
	}
	if (s)
	  free(s);
	SS->state = RecipientOrData;
}

void
smtp_turnme(SS,name,cp)
SmtpState *SS;
char *name, *cp;
{
	FILE *mfp = mail_open(MSG_RFC822);
	if (!mfp) {
	  type(SS,452,m400,"Failed to initiate TURNME request;  Disk full?");
	  typeflush(SS);
	  return;
	}
	fprintf(mfp,"%c%c%s\n",_CF_TURNME,_CFTAG_NORMAL,cp);
	/* printf("050-My uid=%d/%d\r\n",getuid(),geteuid()); */
	runasrootuser();
	if (mail_close_alternate(mfp,TRANSPORTDIR,"")) {
	  type(SS,452,m400,"Failed to initiate TURNME request;  Permission denied?");
	} else {
	  type(SS,-250,m200,"A TURNME request is initiated - lets hope the system");
	  type(SS,-250,m200,"has resources to honour it.   We call the remote, if");
	  type(SS, 250,m200,"we have anything to send there.");
	}
	runastrusteduser();
	typeflush(SS);
}

void
smtp_verify(SS,buf,cp,cfinfo)
	SmtpState *SS;
	char *buf, *cp;
	struct smtpconf *cfinfo;
{
	char *s;
	int cfi;
	char *newcp = NULL;

	if (SS->state == Hello) {
	  type(SS,503, m551, "Waiting for HELO/EHLO command");
	  return;
	}
	while (*cp == ' ' || *cp == '\t') ++cp;

	cfi = STYLE(cfinfo, 'v');

#if 0 /* The input string is ARBITARY STRING (shudder..) */
	if (*cp == '<')
	  s = rfc821_path(cp,cfi); /* with < > */
	else
	  s = rfc821_path2(cp,cfi); /* Without < > */
	if (s == cp) {
	  type821err(SS,501,m552, buf,"Path data: %s",rfc821_error);
	  return;
	}
	while (*s == ' ' || *s == '\t') ++s;
	if (*s != 0) {
	  type(SS,501, m552, "Growl! Extra junk after the VRFY argument!");
	  return;
	}
#endif

	RFC821_822QUOTE(cp) ;

	if (cfi) {
	  if ((s = router(SS, RKEY_VERIFY, 0, cp)) != NULL) {
	    /* printf("%s\r\n", s);
	       free(s); */
	  } else
	    type(SS,501,m540, "Unable to verify that address");
	} else
	  type(SS,252, "2.5.2", (char *)NULL); /* Syntax ok */

#ifndef USE_ALLOCA
	if (newcp) free(newcp);
#endif
}

void
smtp_expand(SS,buf,cp,cfinfo)
	SmtpState *SS;
	char *buf, *cp;
	struct smtpconf *cfinfo;
{
	char *s;
	int cfi;
	char *newcp = NULL;

	if (SS->state == Hello) {
	  type(SS,503, m551, "Waiting for HELO/EHLO command");
	  return;
	}
	if ((cfi = STYLE(cfinfo, 'e'))) {
	  while (*cp == ' ' || *cp == '\t') ++cp;
#if 0  /* The input string is an arbitary string! */
	  if (*cp == '<')
	    s = rfc821_path(cp,cfi); /* with < > */
	  else
	    s = rfc821_path2(cp,cfi); /* Without < > */
	  if (s == cp) {
	    type821err(SS,501,m552,buf,"Path data: %s",rfc821_error);
	    return;
	  }
	  while (*s == ' ' || *s == '\t') ++s;
	  if (*s != 0) {
	    type(SS,501, m552, "Growl! Extra junk after the EXPN argument!");
	    return;
	  }
#else
#endif

	  RFC821_822QUOTE(cp) ;

	  if ((s = router(SS, RKEY_EXPAND, 0, cp)) != NULL) {
	    /* printf("%s\r\n", s);
	       free(s); */
	  } else
	    type(SS,501,m540, "Unable to verify that address");
#ifndef USE_ALLOCA
	  if (newcp) free(newcp);
#endif
	} else
	  type(SS,502,m540, (char *)NULL);
}
