/*
 * Copyright (C) 1993 Swedish University Network (SUNET)
 *
 *
 * This program is developed by UDAC, Uppsala University by commission
 * of the Swedish University Network (SUNET). 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 *                                          Martin.Wendel@udac.uu.se
 *
 *                                          Martin Wendel
 *                                          UDAC	
 *                                          Box 174
 *                                          S-751 04 Uppsala
 *                                          Sweden
 */






#include "mk.h"

char *Tk_HFieldMerge();
char *getbound();
char *getendbound();

BoundStruct *rmlbnd();

char *PathMk = EMILLIBDIR;
static bool headertail = FALSE;

/*
* Run TCL scripts to evaulate the current header structure.
*/

Mk_Eval_Header()
{
  int result;
  char path[BUFSIZE];
#ifdef EMILDEBUG
  Mk_Log(LOG_DEBUG, "Evaluating headers..."); 
#endif /* EMILDEBUG */
  sprintf(path, "set appLibrary %s", PathMk);
  result = Tcl_Eval(interp, path);
  if (result != TCL_OK) 
    {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR, 
	     "Mk_Eval_Header: TCL failure: %s ...reverting to transparent mode", 
	     interp->result);
#endif
      MkMess_Root->dotunnel = TRUE;
      return(0);
    }
  sprintf(path, "source %s/header.tcl", PathMk);
  result = Tcl_Eval(interp, path);
  if (result != TCL_OK) 
    {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR, 
	     "Mk_Eval_Header: header.tcl: TCL failure: %s ...reverting to transparent mode", 
	     interp->result);
#endif
      MkMess_Root->dotunnel = TRUE;
      return(0);
    }
#ifdef EMILDEBUG
  Mk_Log(LOG_DEBUG, "Headers evaluated successfully.");
#endif
}
int
Mk_Get_Header()
{
  if (isheader(MkMess_Buf->in)) 
    {
#ifdef EMILDEBUG
      Mk_Log(LOG_DEBUG, "Grabbing header...%s", MkMess_Buf->in);
#endif
      storeheader(MkMess_Buf->in, MkMess_Curr);
      MkMess_Curr->washeaders = TRUE;
      return(2);
    }
  else
    {
      if (!headertail) {
	headertail = TRUE;
	return(2);
      }
      else {
	headertail = FALSE;
	Mk_Eval_Header();
	MkMess_Curr->start_line = linecount;
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Setting start line: %i", linecount);
#endif
	if (MkMess_Curr == MkMess_Root) {
	  MkMess_Curr->end_line = linecount - 1;
#ifdef EMILDEBUG
	  Mk_Log(LOG_DEBUG, "Setting end line: %i", linecount -1);
#endif
	}
	return(0);
      }
    }
}

int
Mk_Check_New()
{
  if (MkMess_Curr->topofmulti) 
    {
      if (!MkMess_Curr->start_line) {
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Setting start_line: %i", linecount);
#endif
	MkMess_Curr->start_line = linecount;
      }
      if (!MkMess_Curr->end_line) {
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Setting end_line: -1");
#endif
	MkMess_Curr->end_line = -1;
      }
    }
  else
/* ! topofmulti */
    if (!MkMess_Root->bound)
      {
	if (!MkMess_Curr->end_line) 
	  {
	    if (linecount != 1) {
#ifdef EMILDEBUG
	      Mk_Log(LOG_DEBUG, "Setting end_line: %i", linecount);
#endif
	      MkMess_Curr->end_line = linecount;
	    }
	    else {
#ifdef EMILDEBUG
	      Mk_Log(LOG_DEBUG, "Setting end_line: -1");
#endif
	      MkMess_Curr->end_line = -1;
	    }
	  }
	mkaddmessnode();
	return(1);
      }
  return(0);
}

Mk_Get_Body()
{
  int flag;
  int s;
  int result;
  char path[BUFSIZE];
  /* Get body */  
  testdecode();
    MkMess_Buf->inlen = strlen(MkMess_Buf->in);
    if (!MkMess_Curr->start_line) {
#ifdef EMILDEBUG
      Mk_Log(LOG_DEBUG, "Setting start_line: %i", linecount);
#endif
      MkMess_Curr->start_line = linecount;
    }
    if (flag = checkbound(MkMess_Buf->in)) 
      {
	return(flag);
      }
    else
      {
	textcheck(MkMess_Curr, MkMess_Buf);
	if (MkMess_Buf->fconv != NULL && !MkMess_Curr->end_line)
	  {
	    MkMess_Buf->outlen = 0;
	    if ((s = MkMess_Buf->fconv(MkMess_Curr, MkMess_Buf)) < 0)
	      {
#ifdef EMILDEBUG
		Mk_Log(LOG_DEBUG, 
	       "Conversion failed at line %i ...conversion reset to 7bit",
		       linecount);
#endif
		MkMess_Curr->enc = ENC7BIT;
		MkMess_Curr->type = TYPETEXT;
		MkMess_Curr->fcharset = newstr("US-ASCII");
		MkMess_Buf->fconv = NULL;
		MkMess_Buf->name = newstr("Write");
	      }
	    if (s == DONE) 
	      {
#ifdef EMILDEBUG
		Mk_Log(LOG_DEBUG, "decode successful");
#endif
		if (!MkMess_Curr->end_line)
		  {
		    if (linecount != 1) {
		      MkMess_Curr->end_line = linecount;
#ifdef EMILDEBUG
		      Mk_Log(LOG_DEBUG, "Setting end_line %i", linecount);
#endif
		      sprintf(path, "source %s/check/settype.tcl", PathMk);
		      result = Tcl_Eval(interp, path);
		      if (result != TCL_OK) 
			{
#ifdef EMILDEBUG
			  Mk_Log(LOG_ERR, 
	 "Mk_Get_Body: settype.tcl: TCL failure: %s ...reverting to transparent mode", 
				 interp->result);
#endif
			  MkMess_Root->dotunnel = TRUE;
			}


		    }

		    else {
		      MkMess_Curr->end_line = -1;
#ifdef EMILDEBUG
		      Mk_Log(LOG_DEBUG, "Setting end_line: -1");
#endif
		    }
		  }
		MkMess_Buf->fconv = NULL;
		MkMess_Buf->name = (char *)newstr("Write");
		return(0);
	      }
	  }
      }
  return(1);
}

Mk_Eval_Message(name)
char *name;
{
  int result;
  char path[BUFSIZE];
  mknewallbufs();
  MkMess_Curr = MkMess_Root;
  MkMess_Buf = MkMess_Curr->buf;
Mk_Log(LOG_DEBUG, "Evaulating message");
  if (name == NULL)
    {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR,
	     "Mk_Eval_Message: No target conversion ...reverting transparent mode");
#endif
      MkMess_Root->dotunnel = TRUE;
      return (0);
    }
  sprintf(path, "set appLibrary %s", PathMk);

    result = Tcl_Eval(interp, path);
    if (result != TCL_OK) {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR,
	     "Mk_appLibrary: TCL failure: %s ...reverting to transparent mode",
	     interp->result);
#endif
      MkMess_Root->dotunnel = TRUE;
      return(0);
    }
  sprintf(path, "source %s/run.tcl", PathMk);

    result = Tcl_Eval(interp, path);
    if (result != TCL_OK) {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR,
	     "Mk_Run: run.tcl TCL failure: %s ...reverting to transparent mode", 
	     interp->result);
#endif
      MkMess_Root->dotunnel = TRUE;
      return(0);
    } 
  MkMess_Curr = MkMess_Root;
  MkMess_Buf = MkMess_Curr->buf;
  sprintf(path, "source %s/convert/%s.tcl", PathMk, name);
#ifdef EMILDEBUG
  Mk_Log(LOG_DEBUG, "Converting message by %s script", name);
#endif
    result = Tcl_Eval(interp, path);
    if (result != TCL_OK) {
#ifdef EMILDEBUG
      Mk_Log(LOG_ERR,
     "Mk_Convert: convert/%s.tcl: TCL failure: %s ...reverting to transparent mode", 
	     name, interp->result);
#endif
      MkMess_Root->dotunnel = TRUE;
    }
  MkMess_Curr = MkMess_Root;
  MkMess_Buf = MkMess_Curr->buf;
}

  
int
checkbound(inbuf)
char *inbuf;
{
  char *s;
  int i;
  BoundStruct *boundary;

  boundary = MkMess_Root->bound;

  if (boundary && *inbuf)
    {
      if (boundary->lend && !strncmp(inbuf, boundary->lend, boundary->endlen))
	{
#ifdef EMILDEBUG
	  Mk_Log(LOG_DEBUG, "Seen End Boundary...");
#endif
	  if (!MkMess_Curr->end_line) {
	    MkMess_Curr->end_line = linecount - 1;
#ifdef EMILDEBUG
	    Mk_Log(LOG_DEBUG, "Setting end_line %i", linecount -1);
#endif
	  }
	  rmlastbound();
	  return(1);
	}
      else
      if (boundary->name && !strncmp(inbuf, boundary->name, boundary->namelen))
	{
#ifdef EMILDEBUG
	  Mk_Log(LOG_DEBUG, "Seen Boundary...");
#endif
	  if (!MkMess_Curr->end_line) {
	    MkMess_Curr->end_line = linecount - 1;
#ifdef EMILDEBUG
	    Mk_Log(LOG_DEBUG, "Setting end_line %i", linecount -1);
#endif
	  }
	  mkaddmessnode();
	  return(2);
	}
    }

  if (checkuuencode && *inbuf)
    if (!strncasecmp(inbuf, "begin ", 6))
      {
	s = inbuf;
	s += 6;
	for (i = 0; i < 3; i++, s++) 
	  if (*s == '\0' || !isdigit(*s))
	    return(0);
	if (*s == '\0' || *s++ != ' ')
	  return(0);
	if (*s == '\0')
	  return(0);
	if (index(s, ' ') || index(s, '\t'))
	      return(0);
	if (MkMess_Curr->start_line && MkMess_Curr->start_line != linecount) {
	  if (!MkMess_Curr->end_line) {
	    MkMess_Curr->end_line = linecount - 1;
#ifdef EMILDEBUG
	    Mk_Log(LOG_DEBUG, "Setting end_line %i", linecount -1);
#endif
	  }
	  mkaddmessnode();
	}
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Suspect uuencode...Trying it out");
#endif
	MkMess_Curr->enc = ENCUUENCODE;
	MkMess_Buf->fconv = fromuuencode;
	MkMess_Buf->name = newstr("fromuuencode");
	MkMess_Curr->start_line = linecount;
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Setting start_line %i", linecount);
#endif
	setfilename(s);
	return(1);
      }
  if (checkbinhex && *inbuf)
    if (!strncasecmp(inbuf, "(This File must be converted with BinHex", 40))
      {
	if (MkMess_Curr->start_line && MkMess_Curr->start_line != linecount) {
	  if (!MkMess_Curr->end_line) {
	    MkMess_Curr->end_line = linecount - 1;
#ifdef EMILDEBUG
	    Mk_Log(LOG_DEBUG, "Setting end_line %i", linecount -1);
#endif
	  }
	  mkaddmessnode();
	}
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Suspect BinHex...Trying it out");
#endif
	MkMess_Curr->enc = ENCBINHEX;
	MkMess_Buf->fconv = frombinhex;
	MkMess_Buf->name = newstr("frombinhex");
	MkMess_Curr->start_line = linecount;
#ifdef EMILDEBUG
	Mk_Log(LOG_DEBUG, "Setting start_line %i", linecount);
#endif
	return(1);
      }
  return(0);
      
}

Mk_Put_Headers(mfile, m, doroot)
FILE *mfile;
MAILER *m;
bool doroot;
{
  char boundbuf[BUFSIZE];
  HDR *h;
  
  if (MkMess_Curr != MkMess_Root && MkMess_Root->dosubheaders) {
    /* Write Boundary */
    char *bbuf;
    bbuf = getbound();
    if (bbuf != NULL) {
      strcpy(boundbuf, bbuf);
#ifdef EMILDEBUG
      Mk_Log(LOG_DEBUG, "Writing boundary: %s", boundbuf);
#endif
      putline(boundbuf, mfile, m);
    }
  }
  if (MkMess_Curr->header) {
    if ((doroot && MkMess_Curr == MkMess_Root) || MkMess_Root->dosubheaders) {
      /* Write headers */
      for (h = MkMess_Curr->header; h != NULL && h->h_field != NULL; 
	   h = h->h_link)
	{
	  char *s, *p;
	  sprintf(boundbuf, "%s: %s", h->h_field, h->h_value);
	  p = boundbuf;
#ifdef EMILDEBUG
      Mk_Log(LOG_DEBUG, "Writing header: %s", p);
#endif
	  while ((s = (char *)index(p, '\n')) != NULL)
	    {
	      *s++ = '\0';
	      putline(p, mfile, m);
	      p = s;
	    }
	  if (*p != '\0')
	    putline(p, mfile, m);
	}
      putline("", mfile, m);
      if (ferror(mfile))
	{
	  Mk_Log(LOG_ERR, "Mk_Put_Headers %i: write error", 
	       linecount);
#ifdef SENDMAIL
	  ExitStat = EX_IOERR;
#endif
	  return(0);
	}
    }
  }
}

mkcollect(buf, bufsize, infile)
char *buf;
int bufsize;
FILE *infile;
{
#ifdef SENDMAIL
  static int hdrflag = 0;
#else
  static int hdrflag = 2;
#endif /* !SENDMAIL */


  if (!MkMess_Root->dotunnel)
    {
      linecount++;
      MkMess_Curr->investigate = TRUE;
      MkMess_Buf->in = buf;
      MkMess_Buf->inlen = strlen(buf);
      if (hdrflag == 2)
	{
	  char *p;
	  char c;
	  if (isheader(buf))
	    clearendlf(buf);
	  while ((c = getc(infile)) == ' ' || c == '\t')
	    {
	      p = &buf[strlen(buf)];
	      *p++ = '\n';
	      *p++ = c;
	      if (sfgets(p, bufsize - (p - buf), infile) == NULL)
		break;
	      clearendlf(p);
	    }
	  if (!feof(infile) && !ferror(infile))
	    (void) ungetc(c, infile);
	  hdrflag = Mk_Get_Header();
	}
      if (hdrflag == 0)
	if(Mk_Check_New())
	  MkMess_Buf->in = buf;
      if (hdrflag != 2)
	hdrflag = Mk_Get_Body();
    }
}


mkputbodypart(ifp, ofp, m)
FILE *ifp;
FILE *ofp;
MAILER *m;
{
  bool do_read, do_write, do_conv;
  BufStruct *mp;
  char *wpindex, *wprest;
  char buf[BUFSIZE];


  /* init start */
  do_read = TRUE;
  do_write = FALSE;
  do_conv = FALSE;
  MkMess_Buf = MkMess_Curr->buf;
  mp = MkMess_Buf;

  mp->in = buf;


#ifdef EMILDEBUG
  Mk_Log(LOG_DEBUG, 
	 "Processing message part: type %s and enc %s size: %i",
	 get_type(), get_encoding(), MkMess_Curr->totlen); 
  Mk_Log(LOG_DEBUG, "start_line: %i end_line: %i", MkMess_Curr->start_line,
	 MkMess_Curr->end_line);
  Mk_Log(LOG_DEBUG, "Specified conversions:");
  {
    BufStruct *mptmp;
    for (mptmp = MkMess_Buf; mptmp != NULL; mptmp = mptmp->next)
      if (mptmp->name)
	Mk_Log(LOG_DEBUG, "%s", mptmp->name);
  }
#endif
  if (ferror(ifp))
      {
	Mk_Log(LOG_ERR, "mkputbodypart %i: read error before mkputbodypart", 
	       linecount);
#ifdef SENDMAIL
	ExitStat = EX_IOERR;
#endif
	return(0);
      }
  if (ferror(ofp))
      {
	Mk_Log(LOG_ERR, "mkputbodypart %i: write error before mkputbodypart",
	       linecount);
#ifdef SENDMAIL
	ExitStat = EX_IOERR;
#endif
	return(0);
      }
  while (!ferror(ofp))
    {
      if (MkMess_Curr->end_line <= linecount)
	break;
      /* READ */
      if (do_read)
	{
	  mp = MkMess_Buf;
	  if (fgets(mp->in, BUFSIZE, ifp) != NULL)
	    {
	      mp->inlen = strlen(mp->in);
	      linecount++;
/*Mk_RLog(LOG_DEBUG, mp->in);  */
	    }
	  else 
	    {
	      /* Force process of anything left in bufs */
	      bzero(buf, BUFSIZE);
	      mksetlast(mp, TRUE);
	      do_read = FALSE;
	      /* Process and exit */
	    }
	}
      
      /* CONTROL */
      if (!linecount)
	{
	  do_read = TRUE;
	}
      if (do_read && linecount > MkMess_Curr->start_line)
	{
	  do_conv = TRUE;
	  mksetfirst(MkMess_Curr, FALSE);
	}
      if (do_read && linecount == MkMess_Curr->start_line)
	{
	  mksetfirst(MkMess_Curr, TRUE);
	  mksetlast(MkMess_Curr, FALSE);
	  do_conv = TRUE;
	}
      if (do_read && linecount == MkMess_Curr->end_line)
	{
	  mksetlast(MkMess_Curr, TRUE);
	  do_read = FALSE;
	}
      if (!do_read && !do_conv && !do_write)
	{
	  break;
	}
      
      /* CONVERSION */
      if (do_conv)
	{
	  for (; mp->next != NULL; mp = mp->next)
	    {
	      mp->inpos = 0;
	      mp->outlen = 0;
	      (mp->fconv)(MkMess_Curr, mp);
	      mp->next->inlen = mp->outlen;
	    }
	  /* Special case for 8bit char strip */
	  if (mp->fconv)
	    (mp->fconv)(MkMess_Curr, mp); 
	  mp->inpos = 0;
	  mp->outlen = 0;
	  do_conv = FALSE;
	  do_write = TRUE;
	}
      
      /* WRITE */
      if (do_write)
	{
	  do_write = FALSE;
	  mp->inpos = 0;
	  if (mk_init(MkMess_Curr, mp, &wpindex, &wprest))
	    {
	      if (mp->inlen > 1024) {
		*(wpindex + mp->inlen) = '\n';
		mp->inlen++;
	      }
	      *(wpindex + mp->inlen) = '\0';
	      while (((wprest = (char *)index(wpindex, '\n')) != NULL) || MkMess_Curr->last)
		/* Lines to write */
		{
		  if (wprest)
		    *wprest++ = '\0';
		  if (*wpindex == 'F' && 
#ifdef SENDMAIL
		      bitnset(M_ESCFROM, m->m_mflags) &&
#endif
		      strncmp(wpindex, "From ", 5) == 0)
		    (void) putc('>', ofp);
/*Mk_WLog(LOG_DEBUG, wpindex);*/
		  putline(wpindex, ofp, m); 
		  if (wprest)
		    {
		      mp->inpos += wprest - wpindex;
		      wpindex = wprest;
		    }
		  else
		    break;
		}
	    }
	  if (!MkMess_Curr->last)
	    {
	      if (mp->inpos < mp->inlen)
		{
		  bcopy(wpindex, mp->inrest,
			mp->inlen - mp->inpos);
		  mp->inrestlen = mp->inlen - mp->inpos;
		}
	    }
	}
    }
  if (ferror(ifp))
      {
	Mk_Log(LOG_ERR, "mkputbodypart %i: read error in mkputbodypart",
	       linecount);
#ifdef SENDMAIL
	ExitStat = EX_IOERR;
#endif
	return(0);
      }
  if (ferror(ofp))
      {
	Mk_Log(LOG_ERR, "mkputbodypart %i: write error in mkputbodypart",
	       linecount);
#ifdef SENDMAIL
	ExitStat = EX_IOERR;
#endif
	return(0);
      }
  if (MkMess_Curr->putendbound) {
    char *boundbuf;
    boundbuf = getendbound();
    if (boundbuf) {
      strcpy(buf, boundbuf);
      putline(buf, ofp, m);
    }
  }
}

int
storeheader(s, mk)
     register char *s;
     MessageStruct *mk;
{
  HDR *h, *hp;
  char *cpos;
  char *field, *value;

  if (! s || ! *s )
    return(-1);
  clearend(s);
  if ((cpos = (char *)index(s, ':')) != NULL)
    *cpos = '\0';
  else
    return(-1);

  field = s;
  cpos++;

  while (isspace(*cpos))
    cpos++;
  value = cpos;
  if (! value || ! *value)
    return(-1);
  hp = mk->header;
  for (h = hp; h != NULL ; h = h->h_link)
    {
/*      if (h->h_field != NULL) {
	if (strcasecmp(field,h->h_field) == 0) 
	  break;
      }
      else
	break;
*/
      hp = h;
    }
  if (h == NULL) {
    h = (HDR *)xalloc(sizeof(HDR));
  }
  h->h_field = newstr(field);
  h->h_value = newstr(value);
  if (hp == NULL) {
    mk->header = h;
  }
  else
    if (hp != h)
      hp->h_link = h;
  return(0);
}

int
storespecheader(name, mk)
     register char *name;
     MessageStruct *mk;
{
  SpecHeaderStruct *h, *hp;

  hp = mk->specheader;
  for (h = hp; h != NULL ; h = h->next)
    {
      if (h->name != NULL) {
	if (strcasecmp(name,h->name) == 0) 
	  break;
      }
      else
	break;
      hp = h;
    }
  if (!h)
    h = (SpecHeaderStruct *)xalloc(sizeof(SpecHeaderStruct));
  h->name = newstr(name);
  if (!hp)
    mk->specheader = h;
  else
    hp->next = h;
  return(0);
}



int
  removeheader(field, hp)
char *field;
HDR *hp;
{
  register HDR *h, *hh;

  h = NULL;
#ifdef EMILDEBUG
  Mk_Log(LOG_DEBUG, "Removing header %s...", field);
#endif
    for (hh = hp; hh != NULL; hh = hh->h_link)
      {
	if (hh->h_field && !strcasecmp(field, hh->h_field))
	  {
	    if (h)
	      h->h_link = hh->h_link;
	    else
	       MkMess_Curr->header = hh->h_link;
	    if (hh) {
	      if (hh->h_field)
		free(hh->h_field);
	      if (hh->h_value)
		free(hh->h_value);
	      free(hh);
#ifdef EMILDEBUG
	      Mk_Log(LOG_DEBUG, "Succeeded.");
#endif
	    }
	  }
	h = hh;
      }
}



int
testdecode()
{

  switch(MkMess_Curr->enc) {
    
  case ENCBASE64:
    MkMess_Buf->fconv = frombase64;
    MkMess_Buf->name = newstr("frombase64");
    break;
  case ENCQUOTEDPRINTABLE:
    MkMess_Buf->fconv = fromquotedp;
    MkMess_Buf->name = newstr("fromquotedp");
    break;
  case ENCUUENCODE:
    MkMess_Buf->fconv = fromuuencode;
    MkMess_Buf->name = newstr("fromuuencode");
    break;
  case ENCBINHEX:
    MkMess_Buf->fconv = frombinhex;
    MkMess_Buf->name = newstr("frombinhex");
    break;
  default:
    MkMess_Buf->fconv = NULL;
    MkMess_Buf->name = newstr("Write");
    break;
  }
}


int rmlastbound()
{
  MkMess_Root->bound = (BoundStruct *)rmlbnd(MkMess_Root->bound);
}

BoundStruct *
rmlbnd(bound)
BoundStruct *bound;
{
  if (bound == NULL)
    return(NULL);
  else
    return(bound->next);
}

char *
getbound()
{
  char b[71];
  if (MkMess_Curr->newbound)
    {
      BoundStruct *bound, *tbound;
      bound = (BoundStruct *)MkMess_Root->bound;
      tbound = NULL;
      while (bound != NULL) {
	if (bound == MkMess_Boundary)
	  break;
	else
	  tbound = bound;
	bound = bound->next;
      }
      MkMess_Boundary = tbound;
    }
  if (MkMess_Boundary) {
    sprintf(b, "\n%s", MkMess_Boundary->name);
    return(b);
  }
  else
    return(NULL);
}

char *
getendbound()
{
  char b[71];
  BoundStruct *bound, *tbound;
  bound = (BoundStruct *)MkMess_Root->bound;
  tbound = NULL;
  while (bound != NULL) {
    if (bound == MkMess_Boundary)
      break;
    else
      tbound = bound;
    bound = bound->next;
  }
  if (tbound)
    tbound->next = bound->next;
  else
    MkMess_Root->bound = bound->next;
  MkMess_Boundary = bound->next;
  if (bound->lend)
    {
      sprintf(b, "\n%s", bound->lend);
      return(b);
    }
  else
    return(NULL);

}


char *
mkhvalue(field, header)
     char *field;
     HDR *header;
{
  register HDR *h;
  if (field == NULL || *field == '\0')
    return(NULL);
  for (h = header; h != NULL && h->h_field != NULL; h = h->h_link)
    {
      if (!strcasecmp(h->h_field, field))
	return(h->h_value);
    }
  return(NULL);
}

char *
Tk_HFieldMerge(hdr)
     HDR *hdr;
{
#   define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr, argc;
    int numChars;
    char *result;
    register char *dst;
    int i;
    HDR *thdr;

    if ((thdr = hdr) == NULL)
      return("0");
    
    for(argc = 1; thdr->h_link != NULL; thdr = thdr->h_link, argc++)
      ;
    if (argc == 1 && thdr->h_field == NULL)
      return("0");

    /*
     * Pass 1: estimate space, gather flags.
     */

    if (argc <= LOCAL_SIZE) {
	flagPtr = localFlags;
    } else {
	flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));
    }
    numChars = 1;
    thdr = hdr;
    for (i = 0; i < argc; i++, thdr = thdr->h_link) {
	numChars += Tcl_ScanElement(thdr->h_field, &flagPtr[i]) + 1;
    }

    /*
     * Pass two: copy into the result area.
     */

    result = (char *) ckalloc((unsigned) numChars);
    dst = result;
    thdr = hdr;
    for (i = 0; i < argc; i++, thdr = thdr->h_link) {
	numChars = Tcl_ConvertElement(thdr->h_field, dst, flagPtr[i]);
	dst += numChars;
	*dst = ' ';
	dst++;
    }
    if (dst == result) {
	*dst = 0;
    } else {
	dst[-1] = 0;
    }

    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }
    return result;
}

mkdeleval(mtype)
char *mtype;
{
#ifdef SENDMAIL
  FILE *mfp;
  MkMess_Root = (MessageStruct *)mkmessalloc();
  MkMess_Root->header = CurEnv->e_header;
  CurEnv->e_mf = newstr(queuename(CurEnv, 'm'));
  if ((mfp = dfopen(CurEnv->e_mf, "r")) == NULL)
    {
      syserr("Mk: Cannot open %s ...using transparent mode",
	     CurEnv->e_mf);
      MkMess_Root->dotunnel = TRUE;
    }
  else
    {	
      mkreadqf(mfp, MkMess_Root);
      fclose(mfp);
    }
#endif
  MkMess_Boundary = NULL;
  if (!MkMess_Root->dotunnel)
    {
      MkMess_Curr = MkMess_Root;
      Mk_Eval_Message(mtype);
      MkMess_Curr = MkMess_Root;
    }
}

