/*$Id: copy.c,v 1.10 1999/08/07 19:28:16 lindberg Exp $*/
/*$Name: ezmlm-idx-040 $*/

/* Copies a file relative the current directory and substitutes    */
/* !A at the beginning of a line for the target,                   */
/* !R at the beginning of a line for the confirm reply address,    */
/* The following substitutions are also made. If not set, ?????    */
/* will be printed:  <#l#> outlocal                                */
/* will be printed:  <#h#> outhost                                 */
/* will be printed:  <#n#> outmsgnum                               */
/* Other tags are killed, e.g. removed. A missing file is a        */
/* permanent error so owner finds out ASAP. May not have access to */
/* maillog. Content transfer encoding is done for 'B' and 'Q'. For */
/* 'H' no content transfer encoding is done, but blank lines are   */
/* suppressed. Behavior for other codes is undefined. This includes*/
/* lower case 'q'/'b'! If code is 'H' substitution of target and   */
/* verptarget is prevented as it may create illegal headers.       */

#include "stralloc.h"
#include "substdio.h"
#include "strerr.h"
#include "str.h"
#include "getln.h"
#include "case.h"
#include "readwrite.h"
#include "qmail.h"
#include "errtxt.h"
#include "error.h"
#include "quote.h"
#include "copy.h"
#include "mime.h"
			/* for public setup functions only */
#define FATAL "copy: fatal: "

static stralloc line = {0};
static stralloc outline = {0};
static stralloc qline = {0};
static stralloc outlocal = {0};
static stralloc outhost = {0};
static substdio sstext;
static char textbuf[256];
static char *target = "?????";
static char *verptarget = "?????";
static char *confirm = "?????";
static char *szmsgnum = "?????";

void set_cpoutlocal(ln)
stralloc *ln;
{	/* must be quoted for safety. Note that substitutions that use */
	/* outlocal within an atom may create illegal addresses */
  if (!quote(&outlocal,ln))
        strerr_die2x(111,FATAL,ERR_NOMEM);
}

void set_cpouthost(ln)
stralloc *ln;
{
  if (!stralloc_copy(&outhost,ln))
        strerr_die2x(111,FATAL,ERR_NOMEM);
}

void set_cptarget(tg)
char *tg;
{
  target = tg;
}

void set_cpverptarget(tg)
char *tg;
{
  verptarget = tg;
}

void set_cpconfirm(cf)
char *cf;
{
  confirm = cf;
}

void set_cpnum(cf)
char *cf;
{
  szmsgnum = cf;
}

static struct qmail *qq;

static void codeput(l,n,code,fatal)
char *l;
unsigned int n;
char code;
char *fatal;

{
  if (!code || code == 'H')
    qmail_put(qq,l,n);
  else {
    if (code == 'Q')
      encodeQ(l,n,&qline,fatal);
    else
      encodeB(l,n,&qline,0,fatal);
    qmail_put(qq,qline.s,qline.len);
  }
}

static void codeputs(l,code,fatal)
char *l;
char code;
char *fatal;
{
  codeput(l,str_len(l),code,fatal);
}

void copy(qqp,fn,q,fatal)
struct qmail *qqp;
char *fn;		/* text file name */
char q;			/* = '\0' for regular output, 'B' for base64, */
			/* 'Q' for quoted printable,'H' for header    */
char *fatal;		/* FATAL error string */

{
  int fd;
  int match, done;
  unsigned int pos,nextpos;

  qq = qqp;
  if ((fd = open_read(fn)) == -1)
    if (errno != error_noent)
      strerr_die4sys(111,fatal,ERR_OPEN,fn,": ");
    else
      strerr_die4sys(100,fatal,ERR_OPEN,fn,": ");
  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
  for (;;) {
    if (getln(&sstext,&line,&match,'\n') == -1)
      strerr_die4sys(111,fatal,ERR_READ,fn,": ");
    if (match) {	/* suppress blank line for 'H'eader mode */
      if (line.len == 1 && q == 'H') continue;
      if (line.s[0] == '!') {
	if (line.s[1] == 'R') {
	  codeput("   ",3,q,fatal);
	  codeputs(confirm,q,fatal);
	  codeput("\n",1,q,fatal);
	  continue;
	}
	if (line.s[1] == 'A') {
	  codeput("   ",3,q,fatal);
	  codeputs(target,q,fatal);
	  codeput("\n",1,q,fatal);
	  continue;
	}
      }
		/* Find tags <#x#>. Replace with for x=R confirm, for x=A */
		/* target, x=l outlocal, x=h outhost. For others, just    */
		/* skip tag. If outlocal/outhost are not set, the tags are*/
		/* skipped. If confirm/taget are not set, the tags are    */
		/* replaced by "???????" */
      pos = 0;
      nextpos = 0;
      done = 0;
      outline.len = 0;			/* zap outline */
      while ((pos += byte_chr(line.s+pos,line.len-pos,'<')) != line.len) {
        if (pos + 4 < line.len &&
            line.s[pos+1] == '#' &&
            line.s[pos+3] == '#' &&
            line.s[pos+4] == '>') {	/* tag. Copy first part of line */
          done = 1;				/* did something */
          if (!stralloc_catb(&outline,line.s+nextpos,pos-nextpos))
			 die_nomem(fatal);
          switch(line.s[pos+2]) {
            case 'A':
	      if (q == 'H') strerr_die(111,ERR_SUBST_UNSAFE);
              if (!stralloc_cats(&outline,target)) die_nomem(fatal);
              break;
            case 'R':
              if (!stralloc_cats(&outline,confirm)) die_nomem(fatal);
              break;
            case 'l':
              if (!stralloc_cat(&outline,&outlocal)) die_nomem(fatal);
              break;
            case 'h':
              if (!stralloc_cat(&outline,&outhost)) die_nomem(fatal);
              break;
            case 't':
	      if (q == 'H') strerr_die(111,ERR_SUBST_UNSAFE);
              if (!stralloc_cats(&outline,verptarget)) die_nomem(fatal);
              break;
            case 'n':
              if (!stralloc_cats(&outline,szmsgnum)) die_nomem(fatal);
              break;
            default:
              break;			/* unknown tags killed */
          }
          pos += 5;
          nextpos = pos;
        } else
          ++pos;				/* try next position */
      }
      if (!done)
        codeput(line.s,line.len,q,fatal);
      else {
        if (!stralloc_catb(&outline,line.s+nextpos,line.len-nextpos))
		die_nomem(fatal);		/* remainder */
        codeput(outline.s,outline.len,q,fatal);
      }

    } else
      break;
  }
  close(fd);
}

