/*
 *	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:
 *
 *  - DATA
 *  - BDAT
 *
 */

#ifdef USE_TRANSLATION
#include <libtrans.h>
#endif /* USE_TRANSLATION */
#include "smtpserver.h"

static int mvdata __((SmtpState *, char *));
static int mvbdata __((SmtpState *, char *, long));

int
smtp_data(SS,buf,cp)
	SmtpState *SS;
	char *buf, *cp;
{
	int filsiz;
	struct stat stbuf;
	long tell;
	time_t mtime;
	int ino;
	char msg[2048];

	if (SS->state != RecipientOrData) {
	  switch (SS->state) {
	    case Hello:
		cp = "Waiting for HELO command";
		break;
	    case Mail:
	    case MailOrHello:
		cp = "Waiting for MAIL command";
		break;
	    case Recipient:
		cp = "Waiting for RCPT command";
		break;
	    case BData:
		cp = "Must not intermix BDAT and DATA in same transaction!";
		break;
	    default:
		cp = NULL;
		break;
	  }
	  type(SS,503, m552, cp);
	  typeflush(SS);
	  return 0;
	}
	if (ferror(SS->mfp)) {
	  type(SS,452,m430,(char *)NULL);
	  typeflush(SS);
	  clearerr(SS->mfp);
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  reporterr(SS, tell, "message file error");
	  return 0;
	}
	if (SS->sender_ok == 0) {
	  type(SS,550, "5.1.7", "No valid sender, rejecting all recipients");
	  typeflush(SS);
	  SS->state = MailOrHello;
	  return 0;
	}
	if (SS->rcpt_count == 0) {
	  /* No valid recipients! */
	  type(SS,550, "5.1.3", "No valid recipients at RCPT addresses, or no RCPT addresses at all");
	  typeflush(SS);
	  SS->state = MailOrHello;
	  return 0;
	}
	if ((SS->from_box != 0) && (SS->rcpt_count > MaxErrorRecipients)) {
	  /* Too many recipients for a  "MAIL FROM:<>" */
	  type(SS,550, "5.1.7", "SPAM trap -- too many recipients for an empty source address!");
	  typeflush(SS);
	  SS->state = MailOrHello;
	  return 0;
	}

	SS->rcpt_count = 0; /* now we can zero it.. */
	type(SS, 354, NULL, (char *)NULL);
	fputs("env-end\n",SS->mfp);
	/* We set alarm()s inside the mvdata() */
	*msg = 0;
	filsiz = mvdata(SS, msg);
	alarm(0);		/* cancel the alarm() */
	tell = ftell(SS->mfp);

	if (*msg != 0) {
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  type(SS,452,m430,"%s",msg);
	} else if (s_feof(SS)) {
	  /* [mea@utu.fi] says this can happen */
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  reporterr(SS, tell, "premature EOF on DATA input");
	  typeflush(SS);
	  return -1;
	} else if (ferror(SS->mfp)) {
	  type(SS,452,m430,(char *)NULL);
	  clearerr(SS->mfp);
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  reporterr(SS, tell, "message file error");
	} else if (maxsize > 0 && filsiz > maxsize) {
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  type(SS,552, "5.3.4", "Message size exceeded fixed maximum size of %ld chars for acceptable email", maxsize);
	} else if (_mail_close_(SS->mfp, &ino, &mtime) == EOF) {
	  type(SS,452,m430,(char *)NULL);
	  SS->mfp = NULL;
	  reporterr(SS,tell, "message file close failed");
	} else {
	  /* Ok, build responce with proper "spoolid" */
	  char fnam[20], taspid[30];
	  sprintf(fnam,"%d",ino);
	  taspoolid(taspid,sizeof(taspid),mtime,fnam);

	  SS->mfp = NULL;
	  type(SS,250, "2.6.0", "%s message accepted", taspid);
#if 0
	  zsyslog((LOG_INFO,
		   "accepted id %s (%ldc) from %s/%d", taspid, tell,
		   SS->rhostname, SS->rport));
#endif
	  if (logfp != NULL) {
	    fprintf(logfp, "%d#\t%s: %ld bytes\n", pid, taspid, tell);
	    fflush(logfp);
	  }
	}
	SS->state = MailOrHello;
	typeflush(SS);
	return 0;
}

int
smtp_bdata(SS,buf,cp)
	SmtpState *SS;
	char *buf, *cp;
{
	int filsiz, rc;
	struct stat stbuf;
	long tell;
	char msg[2048];
	long bdata_chunksize;
	int bdata_last;

	if (SS->state == RecipientOrData) {
	  SS->state = BData;
	  SS->bdata_blocknum = 0;
	  SS->mvbstate = -1;
	}

	*msg = 0;
	rc = sscanf(cp,"%ld %s %s",&bdata_chunksize,msg, msg+8);
	SS->bdata_blocknum += 1;
	bdata_last = CISTREQ(msg,"LAST");
	if (!(bdata_chunksize > 0L
	      && (rc == 1 || (rc == 2 && bdata_last)))) {
	  type(SS,501,m552,NULL);
	  typeflush(SS);
	  return 0;
	}
	
	if (SS->bdata_blocknum == 1) {
	  fputs("env-end\n",SS->mfp);
	}

	/* We set alarm()s inside the mvbdata() */
	*msg = 0;
	filsiz = mvbdata(SS, msg, bdata_chunksize);
	alarm(0);		/* cancel the alarm() */
	if (SS->mfp)
	  tell = ftell(SS->mfp);
	else
	  tell = 0;

	if (SS->state != BData) {
	  switch (SS->state) {
	    case Hello:
		cp = "Waiting for HELO command";
		break;
	    case Mail:
	    case MailOrHello:
		cp = "Waiting for MAIL command";
		break;
	    case Recipient:
		cp = "Waiting for RCPT command";
		break;
	    default:
		cp = NULL;
		break;
	  }
	  type(SS,503, m552,cp);
	  typeflush(SS);
	  if (SS->mfp) mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  return 0;
	}

	if (SS->bdata_blocknum == 1) {
	  if (SS->sender_ok == 0 || SS->rcpt_count == 0) {
	    char *cp = "No valid sender, rejecting all recipients";
	    if (SS->sender_ok != 0)
	      cp = "No valid recipient at RCPT addresses, or no RCPT addresses at all";
	    type(SS,550, "5.1.3", cp);
	    typeflush(SS);
	    SS->state = MailOrHello;
	    if (SS->mfp)
	      mail_abort(SS->mfp);
	    SS->mfp = NULL;
	    return 0;
	  }

	  SS->rcpt_count = 0; /* now we can zero them.. */
	  SS->sender_ok = 0;
	}

	if (SS->mfp == NULL) {
	  type(SS,503,m552,"BDAT block discarded due to earlier error");
	} else if (*msg != 0) {
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  type(SS,452,"%s",msg);
	} else if (s_feof(SS)) {
	  /* [mea@utu.fi] says this can happen */
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  reporterr(SS, tell, "premature EOF on BDAT input");
	  typeflush(SS);
	  return -1;
	} else if (ferror(SS->mfp)) {
	  type(SS,452,m400,(char *)NULL);
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  reporterr(SS, tell, "write to spool file failed");
	} else if (maxsize > 0 && tell > maxsize) {
	  mail_abort(SS->mfp);
	  SS->mfp = NULL;
	  type(SS,552, "5.3.4", "Message size exceeded fixed maximum size of %ld chars for acceptable email", maxsize);
	} else if (bdata_last) {
	  time_t mtime;
	  int inum;

	  if (fstat(FILENO(SS->mfp), &stbuf) < 0)
	    inum = 0;
	  else
	    inum = stbuf.st_ino;
	  if (_mail_close_(SS->mfp, &inum, &mtime) == EOF) {
	    type(SS,452,m400,(char *)NULL);
	    SS->mfp = NULL;
	    reporterr(SS, tell, "message file close failed");
	  } else {
	    /* Ok, build responce with proper "spoolid" */
	    char fnam[20], taspid[30];
	    sprintf(fnam,"%d",inum);
	    taspoolid(taspid,sizeof(taspid),mtime,fnam);

	    SS->mfp = NULL;
	    type(SS,250, "2.6.0", "%s Roger, stored %ld bytes into spool",
		 taspid, tell);
#if 0
	    zsyslog((LOG_INFO, "accepted id %s (%ldc) from %s/%d", taspid,
		     tell, SS->rhostname, SS->rport));
#endif
	    if (logfp != NULL) {
	      fprintf(logfp, "%d#\t%s: %ld bytes\n", pid, taspid, tell);
	      fflush(logfp);
	    }
	  }
	} else { /* Not last chunk! */
	  type(SS,250,"2.6.0","Received %ld bytes",bdata_chunksize);
	}
	if (bdata_last) {
	  SS->state = MailOrHello;
	}
	typeflush(SS);
	return 0;
}


/* Implement SMTP DATA filter */

/*
 * The state table is indexed by the current character (across), and
 * the current state (down). Column 0 is for any character that is not
 * a '\r', '\n', or '.'.  The table entries encode the next state, and
 * what to output. An entry of EOF means exit. The next state is encoded
 * in the l.s.byte, and what to output is encoded in the next-l.s.byte.
 * If the next-l.s.byte is null, the current input character is output,
 * if the high bit is set, nothing is output, else the entire byte is
 * output followed by the current character.
 */

#define	O_	(0200 << 8)		/* don't print anything flag */
#define N_	('\r' << 8)		/* print '\r' then current input */
#define X_	~0			/* exit. must have (X_&O_) != 0 */

static int states[] = {
/*	  current input character	*/
/*	*	'\r'	'\n'	'.'	EOF	   states */
	0,   O_|15,      10,	0,	X_,	/* 0: during line */
	0,   O_|20,	 X_,	0,	X_,	/* 5: "^." */
	0,   O_|15,      10,	O_|5,	X_,	/* 10: "^" (start state) */
     N_|0,	15,	 10,	N_|0,	X_,	/* 15: seen a \r */
     N_|0,	15,	 X_,	N_|0,	X_,	/* 20: "^.\r" */
};

/*
 * Quick way of getting the column number of the state table,
 * that corresponds to the input character.
 */

static char indexnum[256+1] = {
#if 0
	idxnum['\r'] = 1;
	idxnum['\n'] = 2;
	idxnum['.'] = 3;
#if EOF == -1
	idxnum[EOF] = 4;
#endif
#endif
	4, /* EOF */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, /* ...'\n'..'\r'.. */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* ... '.' .. */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/*
 * Copy bytes from stdin to out, obeying sensible SMTP DATA input heuristics.
 *
 * If you can improve on this heavily optimized routine, I'd like to see it.
 * This version goes at better than 100kB/cpu-sec on a Sun 3/180.
 */

static int /* count of bytes */
mvdata(SS, msg)
	SmtpState *SS;
	char *msg;
{
	register int c, state, *sts, endstate, cnt;
	register char *idxnum;
	int timercnt = 1024;

#ifdef NO_INCOMING_HEADER_PROCESSING
	idxnum = indexnum+1;

	state = 10;
	endstate = X_;
	sts = states;
	cnt = 0;

	alarm(SMTP_DATA_TIME_PER_LINE);
#else
	char linebuf[4000], *s, *eol;
	int col;
	int insubject = 0;
	int has8bit = 0; /* In headers */
	int has8bitsum = 0;
	int from__err = 0;
	int linecnt = 0;
	int was8bit = 0;
#ifdef USE_TRANSLATION
	int wi;
	char hdr_cte[4000],hdr_ct[4000];
	int delay_cte = 0, delay_ct = 0, append_hdr_ct = 0, append_hdr_cte = 0;
	int ct_is_text = 1;
#define CTE_8BIT 0
#define CTE_BASE64 1
#define CTE_QP 2
	int cte = CTE_8BIT;
	int do_decode=0,do_translate=0;
	int qp_chars=0,qp_hex=0;
	int b64decoding=1,b64eod=0,b64i=0;
	char b64c[4];
	static char index_64[128] = {
		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
		52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
		-1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
		15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
		-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
		41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
	};
#define char64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
#endif /* USE_TRANSLATION */

	typeflush(SS);

	idxnum = indexnum+1;

	state = 10;
	endstate = X_;
	sts = states;
	cnt = 0;
	col = 0;

	alarm(SMTP_DATA_TIME_PER_LINE);

	/* ================ Input the email headers ================ */
	/*	     and analyze them a bit (Precedence:)	     */
	/*	  ... that only "Subject:" has 8-bit chars ...	     */
	mail_priority = _MAILPRIO_NORMAL;
	for (;;) {
		c = s_getc(SS);
		/* An EOF in here is an error! */
#if EOF != -1
		if (c == EOF) return EOF;
#else
		if (c < 0) return EOF;
#endif
		if (--timercnt <= 0) {
		  /* Re-arm for every kilobyte */
		  alarm(SMTP_DATA_TIME_PER_LINE);
		  timercnt = 1024;
		}
		++cnt;
		if ((state = sts[state+idxnum[c]]) & ~0xff) {
		  if (state & O_) {
		    if (state == endstate) {
		      if (col > 0) {
#ifdef USE_TRANSLATION
			if (has8bit && !X_8bit)
			  header_to_mime(linebuf,&col,sizeof(linebuf));
			else
			  header_from_mime(linebuf,&col,sizeof(linebuf));				for (wi=0;wi<col;wi++)
			  putc(TR_IN(linebuf[wi]),SS->mfp);
#else /* USE_TRANSLATION */
			if (has8bit)
			  header_to_mime(linebuf,&col,sizeof(linebuf));
			fwrite(linebuf,1,col,SS->mfp);
#endif /* USE_TRANSLATION */
		      }
		      return cnt;
		    }
		    state = (char)state;
		    continue;
		  }
		  if (col < (sizeof(linebuf)-1))
		    linebuf[col++] = (state >> 8);
		  state = (char)state;
		}
		if (0x80 & c)
		  has8bit = 1;

		if (col < (sizeof(linebuf)-1))
		  linebuf[col++] = c;

		/* LF, or something else ?  Here the else.. */
		if (c != '\n')
		  continue;

		if (col < sizeof(linebuf))
		  linebuf[col] = 0;

		/* We have a string - header line - in ``col'' first
		   char positions of the ``linebuf'' array  */

		/* See what this header is about -- or if the body starts.. */
		if (col > sizeof(linebuf)-1) col = sizeof(linebuf)-1;

		eol = linebuf + col;
		*eol = 0;

		/* See if the line is all white-space: */
		for (s = linebuf; s < eol; ++s)
		  if (*s != ' ' && *s != '\t' &&
		      *s != '\r' && *s != '\n')
		    break;
		if (s == eol) {	/* All-blank line */
#ifdef USE_TRANSLATION
		  /* We have all info from the headers, time to make decision */
		  if (X_8bit && ct_is_text && cte)
		    do_decode=cte;
		  if (X_translation && X_8bit && ct_is_text && (X_settrrc == 0))
		    do_translate=1;
		  if (logfp)
		    fprintf(logfp,"%d#\t(8bit decode: %s, translate: %s) [%s%s,%s]\n",pid,
		      do_decode?"YES":"NO",do_translate?"YES":"NO",
		      X_translation?"-X ":"",
		      X_8bit?"-8":"",
		      ct_is_text?"text":"non-text");
		  /* write out content-type and content-transfer-encoding */
		  if (delay_ct) {
		    if (do_translate) {
		      /* Remove "charset=xxx".  It is mismatching anyway */
		      char *p,*q,*r;
		      p=hdr_ct;
		      while (*p && (*p != ';')) p++; /* skip to attrs */
		      q=p;
		      while (*p == ';') { /* check attributes */
			r=p+1;
			while (*r && ((*r == ' ') || (*r == '\t'))) r++;
			if (CISTREQN(r,"CHARSET=",8)) { /* skip it */
			  p++;
			  while (*p && (*p != ';') && (*p != '\n')) p++;
#ifdef USE_TRANSLATION
			  /* if forced charset specified, insert it. */
			  r="; CHARSET="; while(*r) *(q++)=*(r++);
			  r= USE_TRANSLATION; while(*r) *(q++)=*(r++);
#endif /* USE_TRANSLATION */
			} else { /* copy it */
			  *(q++)=*(p++);
			  while (*p && (*p != ';')) *(q++)=*(p++);
			}
		      }
		      while (*p) *(q++)=*(p++);
		      *q='\0';
		    }
		    fwrite(hdr_ct,1,strlen(hdr_ct),SS->mfp);
		  }
		  if (delay_cte) {
		    if (do_decode) {
		      /* Transfer-encoding changed to 8bit */
		      fputs("Content-Transfer-Encoding: 8bit\n",SS->mfp);
		    } else {
		      fwrite(hdr_cte,1,strlen(hdr_cte),SS->mfp);
		    }
		  }
#endif /* USE_TRANSLATION */
		  if (col > 0)
		    fwrite(linebuf,1,col,SS->mfp);
		  break;	/* Into the body processing */
		}
		++linecnt;
		if (*linebuf == ' ' || *linebuf == '\t') {
#ifdef USE_TRANSLATION
		  if (append_hdr_ct) {
		    strcat(hdr_ct,linebuf);
		    col = 0;
		    continue;
		  }
		  if (append_hdr_cte) {
		    strcat(hdr_cte,linebuf);
		    col = 0;
		    continue;
		  }
		  if (has8bit && !X_8bit)
		    header_to_mime(linebuf,&col,sizeof(linebuf));
		  else
		    header_from_mime(linebuf,&col,sizeof(linebuf));
		  if (col > 0)
		    for (wi=0;wi<col;wi++)
		      putc(TR_IN(linebuf[wi]),SS->mfp);
#else /* USE_TRANSLATION */
		  if (has8bit)
		    header_to_mime(linebuf,&col,sizeof(linebuf));
		  if (col > 0)
		    fwrite(linebuf,1,col,SS->mfp);
#endif /* USE_TRANSLATION */
		  has8bit = 0;
		  col = 0;
		  continue;	/* continuation line.. */
		}

#ifdef USE_TRANSLATION
		append_hdr_ct = 0;
		append_hdr_cte = 0;
#endif /* USE_TRANSLATION */

		/* ================ PROCESS THE HEADERS! ================ */
		if (CISTREQN(linebuf,"Subject:",8)) {
		  insubject = 1;
		} else {
		  if (!insubject) {
#if 0
		    if (has8bit)
		      sprintf(msg,"Header line \"%.200s\" contains illegal 8-bit chars", linebuf);
#endif
		    has8bitsum += has8bit;
		  }
		}

		if (CISTREQN(linebuf,"Precedence:",11)) {
		  s = linebuf+11;
		  while (*s == ' ' || *s == '\t') ++s;
		  if ((eol - s) < 4) continue; /* Hmm.. */
		  if (CISTREQN("junk",s,4)) {
		    mail_priority = _MAILPRIO_JUNK;
		  } else if (CISTREQN("bulk",s,4)) {
		    mail_priority = _MAILPRIO_BULK;
		  }
		}

#ifdef USE_TRANSLATION
		if (X_translation && (X_settrrc == 0)) {
		  if (CISTREQN(linebuf,"Content-Transfer-Encoding:",26)) {
		    if (1) {
		      strcpy(hdr_cte,linebuf);
		      delay_cte = 1;
		      append_hdr_cte = 1;
		      col=0;
		      has8bit = 0;
		      s = linebuf+26;
		      while (*s == ' ' || *s == '\t') ++s;
		      if ((eol - s) < 4) continue; /* Hmm.. */
		      if (CISTREQN("8bit",s,4)) {
			cte=CTE_8BIT;
		      } else if (CISTREQN("base64",s,6)) {
			cte=CTE_BASE64;
		      } else if (CISTREQN("quoted-printable",s,16)) {
			cte=CTE_QP;
		      }
		      continue; /* do not write out this one */
		    }
		  } else if (CISTREQN(linebuf,"Content-Type:",13)) {
		    if (1) {
		      strcpy(hdr_ct,linebuf);
		      delay_ct = 1;
		      append_hdr_ct = 1;
		      col=0;
		      has8bit = 0;
		      s = linebuf+13;
		      while (*s == ' ' || *s == '\t') ++s;
		      if ((eol - s) < 10) continue; /* Hmm.. */
#ifdef PARANOID_TRANSLATION
		      if (CISTREQN("text",s,4)) {
			ct_is_text=1;
		      } else {
			ct_is_text=0;
		      }
#endif /* PARANOID_TRANSLATION */
		      continue; /* do not write out this one */
		    }
		  }
		}
#endif /* USE_TRANSLATION */

		if (linecnt == 1 && (strncmp("From ",linebuf,5)==0 ||
				     strncmp(">From ",linebuf,6)==0)) {
#if 0
		  from__err = 1;
		  sprintf(msg,"Message starts with illegal \"%.200s\" line",linebuf);
#endif
		  /* DO NOT WRITE THIS LINE OUT! */
		  col = 0;
		  has8bit = 0;
		  continue;
		}
#ifdef USE_TRANSLATION
		if (has8bit && !X_8bit)
		  header_to_mime(linebuf,&col,sizeof(linebuf));
		else
		  header_from_mime(linebuf,&col,sizeof(linebuf));
		/* Write the line out */
		if (col > 0)
		  for (wi=0;wi<col;wi++)
		    putc(TR_IN(linebuf[wi]),SS->mfp);
#else /* USE_TRANSLATION */
		if (has8bit)
		  header_to_mime(linebuf,&col,sizeof(linebuf));
		/* Write the line out */
		if (col > 0)
		  fwrite(linebuf,1,col,SS->mfp);
#endif /* USE_TRANSLATION */
		has8bit = 0;
		col = 0;
	}
	if (logfp && verbose)
	  fprintf(logfp,"%d#\t(mail_priority=%d)\n",pid,mail_priority);
#endif

	/* ================ Normal email BODY input.. ================ */
	for (;;) {
		c = s_getc(SS);
#if EOF != -1
		if (c == EOF)		/* a little slower... */
			break;
#endif
		
		if (--timercnt <= 0) {
			/* Re-arm for every kilobyte */
			alarm(SMTP_DATA_TIME_PER_LINE);
			timercnt = 1024;
		}
		++cnt;
		if ((state = sts[state+idxnum[c]]) & ~0xff) {
			if (state & O_) {
				if (state == endstate)
					break;
				state = (char)state;
				continue;
			}
			if (!ferror(SS->mfp))
			  putc((state>>8), SS->mfp);
			state = (char)state;
		}
		if (!ferror(SS->mfp)) {
#ifdef USE_TRANSLATION
			if (do_decode == CTE_QP) {
				if (!qp_chars && c == '=') {
					qp_chars = 2;
					qp_hex = 0;
					continue;
				}
				if (qp_chars && c == '\n') {
					qp_chars = 0;
					continue;
				}
				if (qp_chars == 2 && (c == ' ' || c == '\t')) {
					continue;
				}
				if (qp_chars && ((c >= '0' && c <= '9') ||
						 (c >= 'A' && c <= 'F') ||
						 (c >= 'a' && c <= 'f'))) {
					qp_hex <<= 4;
					if (c >= '0' && c <= '9')
						qp_hex += (c - '0');
					if (c >= 'A' && c <= 'F')
						qp_hex += (c - 'A' + 10);
					if (c >= 'a' && c <= 'f')
						qp_hex += (c - 'a' + 10);
					--qp_chars;
					if (!qp_chars) c = qp_hex;
					else continue;
				} else if (qp_chars)
					qp_chars = 0;
			} else if (do_decode == CTE_BASE64) {
				if (b64decoding) {
					if ((c == ' ') || (c == '\t') ||
					    (c == '\n') || (c == '\r'))
						continue;
					b64c[b64i++]=char64(c);
					if (c == '=') {
						b64eod=1;
						continue;
					}
					if (b64i < 2) continue;
					if (b64i == 2) {
						c=(b64c[0]<<2) |
						  ((b64c[1]&0x30)>>4);
					} else if (b64i == 3) {
						c=((b64c[1]&0x0f)<<4) |
						  ((b64c[2]&0x3c)>>2);
					} else {
						c=(b64c[2]<<6) |
						  b64c[3];
						b64i=0;
						if (b64eod) b64decoding=0;
					}
				}
			}
			if (do_translate)
				putc(TR_IN(c), SS->mfp);
			else
#endif /* USE_TRANSLATION */
			putc(c, SS->mfp);
		}
	}
	typeflush(SS);
	return cnt;
}

/*
 *  BDAT -- For ESMTP CHUNKING extension (rfc 1830)
 */
static int /* count of bytes */
mvbdata(SS, msg, incount)
	SmtpState *SS;
	char *msg;
	register long incount;
{
	register int c, cnt;
	int timercnt = 1024;

	cnt = 0;

	/* XX: header processing REMOVED from BDAT processing */

	/* ================ Normal email BODY input.. ================ */
	for ( ; incount > 0; --incount) {
	  c = s_getc(SS);
	  if (c == EOF)		/* a little slower... */
	    break;
	  if (--timercnt <= 0) {
	    /* Re-arm for every kilobyte */
	    alarm(SMTP_DATA_TIME_PER_LINE);
	    timercnt = 1024;
	  }
	  ++cnt;
	  /* Canonize CR+LF --> LF (UNIX style) */
	  if (c == '\r') { /* Suspend sending, this is our 'mvbstate' */
	    /* do nothing, 'mvbstate = c' is done after this if-else */
	  } else if (SS->mvbstate == '\r') {
	    if (c != '\n') {
	      /* Suspended lone CR */
	      if (SS->mfp) { /* We just discard it, if no output stream */
		if (!ferror(SS->mfp))
		  putc(SS->mvbstate,SS->mfp);
		if (!ferror(SS->mfp))
		  putc(c,SS->mfp);
	      }
	    } else {
	      /* CR + LF -- forget the CR */
	      if (SS->mfp && !ferror(SS->mfp))
		putc(c,SS->mfp);
	    }
	  } else {
	    /* Anything else, just output it! */
	    if (SS->mfp && !ferror(SS->mfp))
	      putc(c,SS->mfp);
	  }
	  SS->mvbstate = c;
	}
	return cnt;
}
