#include "args.h"

#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/sendmail.c,v 1.1 2005/05/28 22:27:53 beebe Exp $";
#endif

/**
 **  sendmail.c - mm interface to sendmail(8)
 **/

#include "mm.h"
#include "message.h"
#include "rd.h"
#include "parse.h"

static FILE *message_tempfile ARGS((msgvec *m, char *signature, int for_sendmail));
static addr_unit *addr_to_str ARGS((addr_unit *au, char **addr, int expand));
static char **add_string ARGS((char **argv, int *c, const  char *s));
static char *maybe_append_signature ARGS((void));
static int build_recipients ARGS((char ***argvp, char ***Filesp, mail_msg *m));
static int deliver_to_files ARGS((FILE *tfp, mail_msg *m, char **Files));
static void add_recipients ARGS((char ***argvp, char ***Filesp, int *argcount, int *filecount, headers *h));
static void sendmail_who ARGS((char *str));

extern fdb aliasfdb;
extern int append_signature;		/* VAR_MAYBE */
extern int errno;
extern int mm_patch_level;
extern int sendmail_background;

#ifndef FALSE
#define FALSE (0)
#define TRUE  (!FALSE)
#endif

#ifdef HAVE_FLEXFILENAMES
#define PRE_SENDMAIL ".mm-sendmail."
#define PRE_DEADLETTER ".mm-deadletter."
#else
#define PRE_SENDMAIL ".mm-sm"
#define PRE_DEADLETTER ".mm-dl"
#endif

#define INDENT 8			/* amount to indent when folding */
#define ALLOCINC 20			/* increment for realloc */

#define SIGNATURE ".signature"
#define sendmail_who_cmd "/usr/lib/sendmail -bv"

/* From REMAIL debugging session 8 Jan 2006 */
void
xxmsg(mail_msg *m, char *name, char c) {
    if (m->resent_date)
      printf(">>> %s[%c] m->resent_date->string=[%s]\n",name,c,
	     m->resent_date->string ? m->resent_date->string : "");
    else
      printf(">>> %s[%c] m->resent_date=[(NULL)]\n",name,c);
    if (m->resent_from)
      printf(">>> %s[%c] m->resent_from->string=[%s]\n",name,c,
	     m->resent_from->string ? m->resent_from->string : "");
    else
      printf(">>> %s[%c] m->resent_from=[(NULL)]\n",name,c);
    if (m->from)
      printf(">>> %s[%c] m->from->string=[%s]\n",name,c,
	     m->from->string ? m->from->string : "");
    else
      printf(">>> %s[%c] m->from=[(NULL)]\n",name,c);
}

/*
 * sendmail:
 * accepts a mail_msg and forks up sendmail(8) and sends the message.
 */
int
#if HAVE_STDC
sendmail (mail_msg *m)
#else /* K&R style */
sendmail (m)
mail_msg *m;
#endif /* HAVE_STDC */
{
  FILE *tfp;
  char **argv = NULL,			/* arguments to sendmail(8) */
       **Files = NULL;			/* file recipients */
  int i, pid, mailit, fileit, saveit, failed = FALSE;
  char *signature = NULL;

  /* xxmsg(m, "SENDMAIL", 'A'); */

  /*
   * Make sure the headers are complete.  This is probably the wrong
   * place to do it, but...
   */
  if (!m->date) {
      headers *h = m->hdrs, *t = m->last, *x;
      m->date = x = new_header (DATE, "Date", HEAD_KNOWN, m);
      x->string = safe_strcpy (rfctime ((time_t)0));
      if (t) {
	  /*
	   * XXX kludge to make Date: header appear first.
	   */
	  t->next = x->next;		/* unlink new header */
	  m->last = t;
	  x->next = h;			/* cons it to front of list */
	  m->hdrs = x;
      }
  }
  /* xxmsg(m, "SENDMAIL", 'B'); */

  if (!m->message_id) {
      char temp[256];

      sprintf (temp, "<CMM.%d.%d.%d.%ld.%s@%s>",
	       mm_major_version, mm_minor_version, mm_patch_level,
	       (long)time(0), user_name, fullhostname);
      m->message_id = new_header (MESSAGE_ID, "Message-ID", HEAD_KNOWN, m);
      m->message_id->string = safe_strcpy (temp);
  }

  /* xxmsg(m, "SENDMAIL", 'C'); */

  /* build recipient lists */
  mailit = build_recipients (&argv, &Files, m);

  /* xxmsg(m, "SENDMAIL", 'D'); */


  /* do we need to file this? */
  saveit = *saved_messages_file &&
      strcmp (saved_messages_file, "/dev/null") != 0;
  fileit = Files || saveit;
  signature = maybe_append_signature();

  /*
   * Remind the user of who's on the recipient list
   */
  if (send_verbose && !sendmail_verbose) {
    int k;

    for (k = 1; argv[k]; k++)
	if (argv[k][0] != '-')
	    break;

    while (argv[k] != NULL) {
      printf ("%s... Queued\n", argv[k]);
      k++;
    }
  }

  if (!sendmail_verbose && !send_verbose) {
    printf("Sending... "); fflush(stdout);
  }
  /* xxmsg(m, "SENDMAIL", 'E'); */
  /*
   * Fire up sendmail for non-file recipients
   */
  if (mailit) {

      tfp = message_tempfile ((msgvec *)m, signature, true);
      if (tfp) {

	  fix_signals_for_fork (true);	/* reset SIGCHLD */

	  pid = vfork ();
	  if (pid == 0) {
	      int n;

	      (void) close (0);
	      (void) dup (fileno (tfp));

	      for (n = 3; n < 20; ++n)
		  (void) close (n);

	      new_process_group ();

	      /* xxmsg(m, "SENDMAIL", 'F'); */
	      (void) execv (SENDMAIL, argv);
	      _exit (errno);
	  }
	  if (pid > 0)
	      if (sendmail_background)
		  maybe_wait_for_process (pid);
	      else
		  wait_for_process (pid);
	  else
	      fprintf (stderr, "?Could not invoke %s: %s",
		       SENDMAIL, errstr (errno));

	  fix_signals_for_fork (false);	/* reenable SIGCHLD handler */

	  fclose (tfp);
      }
      else
	  failed = true;
  }

  /*
   * Deliver the message to any specified files
   */
  if (fileit) {
      tfp = message_tempfile ((msgvec *)m, NULL, false);

      if (tfp) {
	  if (Files != NULL) {
	      if (!deliver_to_files(tfp, m, Files))
		  failed = true;
	      rewind(tfp);
	  }

	  if (saveit) {
	      char *av[2];
	      av[0] = saved_messages_file;
	      av[1] = nil;

	      if (!deliver_to_files (tfp, m, av))
		  failed = true;
	  }
	  fclose (tfp);
      }
      else
	  failed = true;
  }

  if (!sendmail_verbose && !send_verbose && !failed)
      printf ("Done.\n");

  if (argv) {
      for (i = 0; argv[i]; i++)
	  free (argv[i]);
      free (argv);
  }
  if (signature)
      free (signature);
  return (!failed);
}




/*
 * write_addresses:
 * write the header name (i.e "To:", "Cc:") followed by a folded list of
 * recipients to the temp file
 */

void
#if HAVE_STDC
write_addresses (FILE *tfp, headers *h, int expand)
#else /* K&R style */
write_addresses (tfp, h, expand)
FILE *tfp;
headers *h;
int expand;
#endif /* HAVE_STDC */
{
  int c = 0;				/* count what column we are up to */
  addresslist *a = h->address;		/* point at addresses */
  addr_unit *oau,*au = a->first;	/* point at first address unit */

  char *addr;				/* the address string */
  int i;
  int first = TRUE;			/* true if this is the first address */
  int wrote_header = FALSE;


  if (au) {				/* we have an address unit */
    fprintf (tfp, "%s:", h->name);	/* write header name */
    c += strlen(h->name) + 1;		/* update column count */
    wrote_header = TRUE;
  }

  while (au) {
    oau = au;
    au = addr_to_str (au, &addr, expand); /* get next address string and */
					/* update au to next address unit */

    if (addr) {
	if (!first &&			/* if not first address */
	    !(oau->prev->type == ADR_GROUP ||
	      oau->prev->type == ADR_AL_EXPAND ||
	      oau->type == ADR_GROUPEND))
	{
	    fputc (',', tfp);		/* put a ',' at end of the prev addr */
	    c++;
	}
	else
	    first = FALSE;
					/* >>> use term width instead <<< */
	if (c + (i = strlen(addr)) + 1 > 78) {
	    fprintf (tfp, "\n%*s", INDENT, ""); /* end this line */
	    c = INDENT;			/* reset to beginning of line */
					/* (plus indentation) */
	}

	fprintf (tfp, " %s", addr);	/* display the address */
	c += i+1;			/* increment by address length */
    }
  }
  if (wrote_header)
    fputc('\n', tfp);			/* terminate with new line */
}


/*
 * addr_to_str:
 * Address takes a pointer to an address unit, and a pointer to a char
 * pointer.  It creates a string containing the address as it should be
 * written in the header and sets addr to point to it.
 * It returns an updated address unit pointer (skips past a group end
 * if the au was pointing at a group head originally).
 */

static addr_unit *
#if HAVE_STDC
addr_to_str (addr_unit *au, char **addr, int expand)
#else /* K&R style */
addr_to_str (au, addr, expand)
addr_unit *au;
char **addr;
int expand;
#endif /* HAVE_STDC */
{
  static int saw_alias = false;
  switch (au->type) {
  case ADR_ADDRESS:			/* simple address */
    *addr = (char *) malloc(strlen(au->data)+1);/* allocate space for addr */
    strcpy (*addr, au->data);		/* copy the address string */
    au = au->next;			/* move on to the next address unit */
    return (au);
  case ADR_GROUP:			/* address group */
    *addr = (char *) malloc (strlen(au->data)+4); /* space for group name */
					/* and ": ;" */
    if (!expand) {
	sprintf (*addr, "%s: ;", au->data);	/* write group name */
	while (au->next && au->next->type != ADR_GROUPEND)
	    au = au->next;			/* skip all group members */
	au = au->next->next;		/* skip over group end */
	return (au);
    }
    sprintf(*addr, "%s: ", au->data);
    return(au->next);
  case ADR_GROUPEND:
    if (saw_alias && !expand) {
	saw_alias = false;
	*addr = NULL;
	return(au->next);
    }
    if (!expand) {
	fprintf (stderr, "address: Found unexpected ADR_GROUPEND\n");
	return (NULL);
    }
    *addr = (char *) malloc(strlen(";") + 1);
    strcpy(*addr, ";");
    return(au->next);
 case ADR_AL_EXPAND:
    if (!expand) {
	return(au->next);
    }
    else {
	*addr = (char *) malloc (strlen(au->data)+3);/* space for group name */
					/* and ": " */
	sprintf(*addr, "%s: ", au->data);
	saw_alias = true;
	return(au->next);
    }
  case ADR_FILE:
    *addr = (char *) malloc(strlen(au->data)+2);
    sprintf (*addr, "*%s", au->data);
    au = au->next;
    return (au);
  default:
    fprintf (stderr, "address: bogus type\n");
    return (NULL);
  }
}


/*
 * write_sender:
 * add a sender field of the form:
 * Sender: Personal Name <username@hostname>
 */

void
#if HAVE_STDC
write_sender(FILE *tfp) {
#else /* K&R style */
write_sender(tfp) FILE *tfp; {
#endif /* HAVE_STDC */
  char *sender;

  sender = create_sender();		/* create the sender header */
  fprintf (tfp, "Sender: %s\n", sender); /* write out the sender */
}

/*
 * create_sender:
 * create the sender field
 */

char *
create_sender(VOID)
{
  static char buf[256];			/* this should be large enough */
					/* (famous last words) */
  if (real_personal_name != NULL)
    sprintf (buf, "%s <%s@%s>", real_personal_name, user_name,
	     fullhostname);
  else
    sprintf (buf, "%s@%s", user_name, fullhostname);
  return (buf);
}



/*
 * build_recipients:
 * Create an argv for sendmail(8) and a list of file recipients
 * from the mail msg passed.
 * Arguments:
 *	char ***argvp: Pointer to argv (char **)
 *	char ***Filesp: Pointer to array of file names
 *	mail_msg *m: The message we are sending
 * Returns:
 *	Nothing.  Always returns.
 */
static int
#if HAVE_STDC
build_recipients(char ***argvp, char ***Filesp, mail_msg *m)
#else /* K&R style */
build_recipients(argvp, Filesp, m)
char ***argvp, ***Filesp;
mail_msg *m;
#endif /* HAVE_STDC */
{
  int argcount = 0;			/* argument count */
  int filecount = 0;			/* file recipient count */
  int smargs;

  *argvp = add_string (*argvp, &argcount, SENDMAIL); /* argv[0] prog name */

  /*
   * Tell sendmail not to treat "." by itself as EOF
   */
  *argvp = add_string (*argvp, &argcount, "-oi");

  /*
   * include ourself in any alias expansion
   */
  *argvp = add_string (*argvp, &argcount, "-om");

  /*
   * If sendmail-verbose is true, add the debugging option
   */
  if (sendmail_verbose)
    *argvp = add_string (*argvp, &argcount, "-v");

  smargs = argcount;			/* remember number of args */
  if (m->resent_to) {			/* the Resent-To: list */
    add_recipients (argvp, Filesp, &argcount, &filecount, m->resent_to);
  } else {
    if (m->to)				/* the To: list */
      add_recipients (argvp, Filesp, &argcount, &filecount, m->to);
    if (m->cc)				/* the Cc: list */
      add_recipients (argvp, Filesp, &argcount, &filecount, m->cc);
    if (m->bcc)				/* the Bcc: list */
      add_recipients (argvp, Filesp, &argcount, &filecount, m->bcc);
    if (m->fcc)				/* the Bcc: list */
      add_recipients (argvp, Filesp, &argcount, &filecount, m->fcc);
  }

  if (argcount % ALLOCINC == 0)		/* no room for null */
    *argvp = (char **) safe_realloc(*argvp, /* get more space */
				    (argcount+1)*sizeof(char *));
  (*argvp)[argcount] = NULL;		/* tie off with a null */
  if (*Filesp) {
    if (filecount % ALLOCINC == 0)	/* no room for null */
      *Filesp = (char **) safe_realloc(*Filesp, /* get more space */
				       (filecount+1)*sizeof(char *));
    (*Filesp)[filecount] = NULL;	/* tie off with a null */
  }

  return (argcount != smargs);		/* did we have recipients? */
}


/*
 * add_string:
 * Add a string to the argv array and update the count c.
 * Returns a possibly updated char **argv.
 */

static char **
#if HAVE_STDC
add_string (char **argv, int *c, const char *s)
#else /* K&R style */
add_string (argv, c, s)
char **argv;
int *c;
const char *s;
#endif /* HAVE_STDC */
{
  if (*c % ALLOCINC == 0)		/* need more room */
    argv = (char **) safe_realloc(argv,	/* get more space */
				  (*c+ALLOCINC)*sizeof(char *));
  argv[*c] = (char *) malloc (strlen(s)+1); /* space for the string */
  strcpy (argv[*c], s);			/* copy the string into arg list */
  (*c)++;				/* increment arg count */
  return (argv);
}


/*
 * add_recipients:
 * Add recipients in header h to argv and update the count c
 * Returns a possibly different char **argv (if it needs to realloc
 * more for more arguments).
 */

static void
#if HAVE_STDC
add_recipients (char ***argvp, char ***Filesp, int *argcount, int *filecount, headers *h)
#else /* K&R style */
add_recipients (argvp, Filesp, argcount, filecount, h)
char ***argvp, ***Filesp;
int *argcount, *filecount; headers *h;
#endif /* HAVE_STDC */
{
  addresslist *a = h->address;		/* point at addresses */
  addr_unit *au = a->first;		/* point at first address unit */

  while (au) {				/* step through all addr_units */
    switch (au->type) {
    case ADR_ADDRESS:			/* real address */
      if (*argcount % ALLOCINC == 0)	/* need more room */
	*argvp = (char **) safe_realloc(*argvp,
					(*argcount+ALLOCINC)*sizeof(char *));

      (*argvp)[*argcount] = (char *) malloc (strlen(au->data)+1);
      if (au->data[0] == '\\')
	strcpy ((*argvp)[*argcount], au->data+1); /* copy addr into arg list */
      else
	strcpy ((*argvp)[*argcount], au->data);
      (*argcount)++;			/* increment arg count */
      break;
    case ADR_GROUP:			/* group head */
    case ADR_AL_EXPAND:
    case ADR_GROUPEND:			/* group end */
      break;				/* do nothing, skip over */
    case ADR_FILE:			/* recipient is file */
      if (*filecount % ALLOCINC == 0)	/* need more room */
	*Filesp = (char **) safe_realloc(*Filesp,
					 (*filecount+ALLOCINC)*sizeof(char *));
      (*Filesp)[*filecount] = (char *) malloc (strlen(au->data)+1);
      strcpy ((*Filesp)[*filecount], au->data); /* copy fname into list */
      (*filecount)++;			/* increment file count */
      break;
    default:
      fprintf (stderr, "add_recipients: invalid addr_unit type\n");
      return;
    }
    au = au->next;			/* go on to the next addr_unit */
  }
}


/*
 * deliver_to_files:
 * deliver outgoing message to files by 'copying' the message
 * Note: The sendmail temp file is read into a string and a
 * message struct is created.  This message struct is copied to
 * all specified files.
 */
static int
#if HAVE_STDC
deliver_to_files (FILE *tfp, mail_msg *m, char **Files)
#else /* K&R style */
deliver_to_files (tfp, m, Files)
FILE *tfp;
mail_msg *m;
char **Files;
#endif /* HAVE_STDC */
{
  int i = 0;
  struct stat sbuf;
  int fsize;
  char *msgtext;
  message msg;
  int failed = 0;

  if (fstat(fileno(tfp), &sbuf) != 0) {
    perror ("fstat");
    return (FALSE);
  }
  fsize = sbuf.st_size;			/* get file size */
  if ((msgtext = (char *) malloc (fsize+1)) == NULL) {
    fprintf (stderr, "?Out of memory\n");
    return (FALSE);
  }

  /* read temp file into string */
  if (fread(msgtext, sizeof(char), fsize, tfp) != fsize) {
    fprintf (stderr, "?Could not reread message\n");
    free (msgtext);
    return (FALSE);
  }
  msgtext[fsize] = '\0';		/* tie off with a null */

  /* fill in required parts of message struct */
  msg.text = msgtext;			/* of course! */
  time (&msg.date);			/* need a date */
  msg.size = strlen(msg.text);		/* message size */
  msg.from = NULL;			/* no from */
  msg.keywords = NULL;			/* init keywords */
  get_incoming_keywords(NULL, &msg);	/* fill in keywords */
  msg.keybits = 0;			/* no keybits set */
  msg.flags = 0;			/* no flags */

  while (Files[i] != NULL) {		/* for all recipient files */
    msgvec *df;

    if (sendmail_verbose || send_verbose) {
      printf ("*%s... ", Files[i]); fflush(stdout);
    }
    if ((df = getdestfile(Files[i],-1,false)) == NULL) { /* open the file */
      failed++;
      if (sendmail_verbose || send_verbose)
	printf ("Failed - file may be busy, or not in proper format\n");
    }
    else {
      (*msg_ops[df->type].wr_msg) (df, &msg, 0, WR_COPY); /* copy the msg */
      (*msg_ops[df->type].close) (df->filep); /* close the file */
      free (df);
      if (sendmail_verbose || send_verbose)
	printf ("Sent\n");
    }
    i++;				/* on to the next file */
  }
  if (failed) {				/* failed on delivery to a file */
    char dlname[MAXPATHLEN+1];
    msgvec *dlf;

    sprintf (dlname, "%s/%s%d", HOME, PRE_DEADLETTER, PID);
    if ((dlf = getdestfile(dlname,-1,false)) == NULL) { /* open the file */
      if (sendmail_verbose || send_verbose)
	printf ("Dead letter file may be busy, or not in proper format\n");
    }
    else {
      (*msg_ops[dlf->type].wr_msg) (dlf, &msg, 0, WR_COPY);
      (*msg_ops[dlf->type].close) (dlf->filep); /* close the file */
      free (dlf);
      printf ("Failed message saved in %s\n", dlname);
    }
  }
  return (TRUE);
}


/*
 * maybe_append_signature:
 * check variable append_signature, and existence of signature file
 * and possibly read it in, returning the text.
 */

static char *
maybe_append_signature(VOID)
{
  char sigfile[MAXPATHLEN+1];
  struct stat sbuf;
  int sigsize;
  char *signature = NULL;
  int sfd;

  if (append_signature == SET_NO)	/* they don't want it */
    return (NULL);
  sprintf (sigfile, "%s/%s", HOME, SIGNATURE); /* create path */
  if (stat (sigfile, &sbuf) != 0) {	/* stat the file */
    if (errno != ENOENT)
      perror (sigfile);
    return (NULL);
  }
  if (append_signature == SET_ASK) {
    if (!yesno ("Append signature? ", "yes"))
      return (NULL);
  }
  /* if we get here, it was SET_ASK and they said YES or it was SET_YES */
  sigsize = sbuf.st_size;		/* get size of file */
  if ((signature = (char *) malloc (sigsize+2)) == NULL) { /* get mem */
    fprintf (stderr, "?out of memory trying to read %s\n", sigfile);
    return (NULL);
  }
  if ((sfd = open (sigfile, O_RDONLY, 0)) < 0) { /* open for read */
    perror (sigfile);
    free (signature);
    return (NULL);
  }
  if (read (sfd, signature, sigsize) != sigsize) {
    perror (sigfile);
    close (sfd);
    free (signature);
    return (NULL);
  }
  if ((sigsize > 0) && (signature[sigsize-1] != '\n')) {
    signature[sigsize++] = '\n';
  }
  signature[sigsize] = '\0';
  close (sfd);
  return (signature);
}


/*
 * valid_from_field:
 * check to see if the supplied string is a valid From: field.
 */

int
#if HAVE_STDC
valid_from_field (const char *s)
#else /* K&R style */
valid_from_field (s)
const char *s;
#endif /* HAVE_STDC */
{
    int result = true;
    char *cp, *sender, *from, *host, *str;

    sender = create_sender();
    str = (char *) safe_strcpy (s);	/* our personal copy */

    if (ustrcmp (sender, str) == 0)	/* from is identical to sender  */
	goto accept;

    if ((from = index (str, '<')) == NULL) /* strip out up to '<' */
	from = str;
    else {
	from++;
	if ((cp = index (from, '>')) != NULL)
	    *cp = '\0';			/* chop of at '>' */
    }
    if ((host = index (from, '@')) != NULL)
	*host++ = '\0';			/* point at host string */

    if (ustrcmp (from, user_name) != 0)
	goto reject;			/* incorrect user name */

    if (host == NULL)
	goto accept;			/* right user, no host specified */

    if (ustrcmp (host, fullhostname) == 0 ||
	ustrcmp (host, shorthostname) == 0 ||
	ustrcmp (host, mailhostname) == 0)
	goto accept;

 reject:
  result = false;
 accept:
  free (str);
  return result;
}


/*
 * cmd_who:
 */
int
#if HAVE_STDC
cmd_who(int n)
#else /* K&R style */
cmd_who(n)
int n;
#endif /* HAVE_STDC */
{
  static fdb text_fdb = { _CMTXT, CM_SDH, nil, nil,
			      "recipient name, text string" };

  noise ("is");
  aliasfdb._cmdat = (pdat) mk_alias_keys();
  aliasfdb._cmdef = nil;
  parse (fdbchn(&aliasfdb, &text_fdb, nil), &pv, &used);

  if (used == &text_fdb)
    sendmail_who(atmbuf);
  else {				/* an alias */
    int i;

    i = pv._pvint;
    confirm();
    disp_alias(stdout, i, false, true);
  }
  return (true);
}


/*
 * sendmail_who:
 * run sendmail(8) to expand mail address 'str'
 */

static void
#if HAVE_STDC
sendmail_who (char *str)
#else /* K&R style */
sendmail_who (str)
char *str;
#endif /* HAVE_STDC */
{
  FILE *pp;
  char cmd[BUFSIZ];
  char buf[BUFSIZ];
  int len, choplen;

  if (strlen (str) == 0)
    cmerr ("No name to expand");
  sprintf (cmd, "%s %s", sendmail_who_cmd,
	   (strcmp (str, ".") == 0) ? user_name : str);
  if ((pp = mm_popen (cmd, "r")) == NULL) { /* start up sendmail(8) */
    perror ("sendmail");
    return;
  }
  choplen = strlen("... deliverable");
  while (fgets(buf, BUFSIZ, pp) != NULL) { /* while we have output from */
    len = strlen(buf);			/* sendmail(8) */
    buf[--len] = '\0';			/* get rid of the newline */
    if (strcmp(&buf[len-choplen], "... deliverable") == 0)
    buf[len-choplen] = '\0';		/* chop of deliverable msg */
    printf ("%s\n", buf);
  }
  mm_pclose(pp);
}


/*
 * cmd_route:
 * set or unset mail routing (forwarding) for user.
 */

void
#if HAVE_STDC
cmd_route (int n)
#else /* K&R style */
cmd_route (n)
int n;
#endif /* HAVE_STDC */
{
#ifndef FORWARD_FILE
  cmerr ("The ROUTE command is not supported on this system");
#else
  char fname[MAXPATHLEN];
  char *text;
  char *cp;
  FILE *fp;
  int err;

  noise ("mail to");
  /* XXX - should probably use the address parser for this */
  text = parse_text ("text string\n  or confirm to remove current routing",
		     NULL);
  sprintf (fname, "%s/%s", HOME, FORWARD_FILE);
  if (text[0] == '\0') {		/* null text parsed */
    if ((unlink (fname) != 0) && (errno != ENOENT)) {
      perror (fname);
      return;
    }
    /*
     * Note this can be misleading--MM should simply call a separate
     * program, to allow other databases (e.g. /usr/lib/aliases) to
     * be updated and/or to determine whether some other form of mail
     * forwarding is in effect.
     */
    printf ("Mail forwarding for %s removed.\n", user_name);
    return;
  }

  if ((cp = index (text, '\n')) != NULL)
    *cp = '\0';
  if ((fp = fopen(fname, "w")) == NULL) {
    fprintf (stderr, "Could not open %s - mail forwarding not modified.\n",
	     fname);
    return;
  }
  fwrite(text, sizeof(char), strlen(text), fp);
  fputc('\n', fp);
  err = ferror (fp);
  err |= (fclose (fp) == EOF);
  if (!err) {
    printf ("Mail for %s will be forwarded to %s\n", user_name, text);
    return;
  }
  fprintf (stderr, "?Error writing %s: %s\n", fname, errstr (errno));
  if (unlink (fname) != 0)
      fprintf (stderr, "Could not unlink %s: %s", fname, errstr (errno));
  return;
#endif
}

/*
 * show_route:
 * display current mail routing
 */

void
#if HAVE_STDC
show_route (int verbose)
#else /* K&R style */
show_route (verbose)
int verbose;
#endif /* HAVE_STDC */
{
#ifdef FORWARD_FILE
  FILE *fp;
  char fname[MAXPATHLEN];

  sprintf (fname, "%s/%s", HOME, FORWARD_FILE);
  fp = fopen (fname, "r");
  if (fp) {
      int c, lastchar;
      printf (" Mail for %s is forwarded to ", user_name);

      while ((c = getc (fp)) != EOF)
	  putchar (lastchar = c);
      if (lastchar == '\n')
	  fflush (stdout);
      else
	  printf ("\n");
      (void) fclose (fp);
  }
#endif /* FORWARD_FILE */
}

static FILE *
#if HAVE_STDC
message_tempfile (msgvec *m, char *signature, int for_sendmail)
#else /* K&R style */
message_tempfile (m, signature, for_sendmail)
msgvec *m;
char *signature;
int for_sendmail;
#endif /* HAVE_STDC */
{
    char *tfile = mktempfile (PRE_SENDMAIL, TRUE);
    FILE *tfp;
 
    /* xxmsg((mail_msg *)m, "MESSAGE_TMPFILE", 'A'); */

    if ((tfp = fopen (tfile, "w+")) == NULL) {
	fprintf (stderr, "Cannot create temp file %s: %s\n",
		 tfile, errstr (errno));
	return 0;
    }
    if (chmod (tfile, 0600) != 0)
	fprintf (stderr, "Cannot change permissions of %s: %s\n",
		 tfile, errstr (errno));

    display_header (tfp, (mail_msg*)m, FALSE, for_sendmail);
    /* xxmsg((mail_msg *)m, "MESSAGE_TMPFILE", 'B'); */

    fputc ('\n', tfp);
    display_text (tfp, (mail_msg*)m);	/* write out the body of the message */
    if (signature) {
        fputc('\n', tfp);
	fwrite(signature, sizeof(char), strlen(signature), tfp);
    }
    fflush (tfp);
    rewind (tfp);			/* rewind to beginning of file */

    /* xxmsg((mail_msg *)m, "MESSAGE_TMPFILE", 'C'); */
    /* printf(">>> SENDMAIL TEMP FILE=[%s]\n",tfile); */

    unlink (tfile);
    free(tfile);
    return tfp;
}
