/*
* 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-2003.
*/
/*
* Zmailer SMTP-server divided into bits
*
* The basic commands:
*
* - DATA (RFC 821)
* - BDAT (RFC 1830)
*
*/
/* XX: for anti-spam hack */
/* #define USE_ANTISPAM_HACKS */
/* #define USE_STRICT_MSGID_FREEZING */
#define FREEZE__X_ADVERTISEMENT_FOUND 951
#ifdef USE_ANTISPAM_HACKS
#define FREEZE__X_UIDL_FOUND 952
#define FREEZE__IMPROBABLE_RECEIVED_HEADER_FOUND 952
#define FREEZE__MALFORMED_MESSAGE_ID_HEADER 953
#endif
#include "smtpserver.h"
#ifdef USE_TRANSLATION
#include <libtrans.h>
#endif /* USE_TRANSLATION */
#define SKIPSPACE(Y) while (*Y == ' ' || *Y == '\t') ++Y
#define SKIPDIGIT(Y) while ('0' <= *Y && *Y <= '9') ++Y
#define SKIPTEXT(Y) while (*Y && *Y != ' ' && *Y != '\t') ++Y
static int mvdata __((SmtpState *, char *));
static int mvbdata __((SmtpState *, char *, long));
static int parsestatcode __((const char **ss, const char **statcode));
static int parsestatcode(ssp, statcodep)
const char **ssp;
const char **statcodep;
{
int code = -1;
const char *ss = *ssp;
static char statcodebuf[6];
*statcodep = NULL;
for (;'0' <= *ss && *ss <= '9'; ++ss) {
if (code < 0) code = 0;
code = code * 10 + (*ss - '0');
}
SKIPSPACE(ss);
if (isdigit(ss[0]) && ss[1] == '.' &&
isdigit(ss[2]) && ss[3] == '.' &&
isdigit(ss[4])) {
memcpy(statcodebuf, ss, 5);
statcodebuf[5] = 0;
*statcodep = statcodebuf;
ss += 5;
}
SKIPSPACE(ss);
*ssp = ss;
if (code < 200 || code > 599) code = 0;
return code;
}
int smtp_data(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
int filsiz;
long tell = 0;
int i, j;
char msg[2048];
struct stat stbuf;
char *fname;
char taspid[30];
MIBMtaEntry->ss.ReceivedMessagesSs += 1;
MIBMtaEntry->ss.ReceivedRecipientsSs += SS->ok_rcpt_count;
MIBMtaEntry->ss.IncomingSMTP_DATA += 1;
while (!strict_protocol && (*cp == ' ' || *cp == '\t')) ++cp;
if (strict_protocol && *cp != 0) {
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
type(SS, 501, m554, "Extra junk after 'DATA' verb");
return 0;
}
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;
}
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
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);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
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;
if (SS->mfp)
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
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;
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
return 0;
}
if ((SS->from_box != 0) && (SS->rcpt_count > MaxErrorRecipients)) {
/* Too many recipients for a "MAIL FROM:<>" */
type(SS, 550, "5.7.1", "SPAM trap -- too many recipients for an empty source address!");
typeflush(SS);
SS->state = MailOrHello;
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
return 0;
}
SS->rcpt_count = 0; /* now we can zero it.. */
type(SS, 354, NULL, (char *) NULL);
typeflush(SS);
fputs("env-end\n", SS->mfp);
if (msa_mode && SS->authuser != NULL ) {
fprintf(SS->mfp, "X-Comment: RFC 2476 MSA function at %s logged sender identity as: %s\n", SS->myhostname, SS->authuser);
}
/* We set alarm()s inside the mvdata() */
*msg = 0;
filsiz = mvdata(SS, msg);
SS->messagesize = filsiz;
fflush(SS->mfp);
fname = mail_fname(SS->mfp);
fstat(FILENO(SS->mfp), &stbuf);
taspoolid(taspid, stbuf.st_mtime, stbuf.st_ino);
tell = stbuf.st_size,
report(SS, "Got '.'; tell=%ld", tell);
availspace = used_fd_statfs(FILENO(SS->mfp));
if (availspace >= 0)
MIBMtaEntry->sys.SpoolUsedSpace = availspace;
availspace = free_fd_statfs(FILENO(SS->mfp));
if (availspace < 0)
availspace = LONG_MAX / 1024; /* Over 2G ? */
if (availspace >= 0)
MIBMtaEntry->sys.SpoolFreeSpace = availspace;
availspace -= minimum_availspace;
if (availspace > (LONG_MAX / 1024))
availspace = LONG_MAX / 1024;
availspace *= 1024;
if (*msg != 0) {
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
type(SS, 452, m430, "%s", msg);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, "%s", msg);
typeflush(SS);
} else if (s_feof(SS)) {
if (STYLE(SS->cfinfo,'D')) {
/* Says: DON'T DISCARD -- aka DEBUG ERRORS! */
mail_close_alternate(SS->mfp,"public",".DATA-EOF");
} else
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
reporterr(SS, tell, "premature EOF on DATA input");
typeflush(SS);
return -1;
} else if (availspace < 0 || ferror(SS->mfp)) {
type(SS, 452, m430, NULL); /* insufficient system storage */
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, NULL); /* insufficient system storage */
typeflush(SS);
reporterr(SS, tell, ferror(SS->mfp) ? "write to spool file failed" : "system free storage under limit");
clearerr(SS->mfp);
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
} else if (maxsize > 0 && filsiz > maxsize) {
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
type(SS, 552, "5.3.4", "Size of this message exceeds the fixed maximum size of %ld chars for received email ", maxsize);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 552, "5.3.4", "Size of this message exceeds the fixed maximum size of %ld chars for received email ", maxsize);
typeflush(SS);
} else {
/* Things have been good thus far, now we store
the resulting file into router spool area;
pending a few things we do at first.. */
const char *statcode = NULL, *ss, *ss0;
int code = 0;
const char *sslines[20];
int sslinecnt = 0;
/* Lets see what the content-policy will tell now ? */
if (debug) typeflush(SS);
SS->policyresult = contentpolicy(policydb, &SS->policystate, fname);
ss0 = ss = policymsg(policydb, &SS->policystate);
if (ss)
type(NULL,0,NULL,
"Content-policy analysis ordered message %s. (code=%d); msg='%s'",
(SS->policyresult < 0 ? "rejection" :
(SS->policyresult > 0 ? "freezing" : "acceptance")),
SS->policyresult, ss);
if (ss) {
char *p, *s;
code = parsestatcode(&ss,&statcode);
s = (char *)ss;
p = strchr(s,'\r');
sslinecnt = 0;
if (p) {
/* Multiline! CRs in message text... */
while (p && sslinecnt < 17) { /* Arbitrary fixed limit.. */
*p++ = '\0';
sslines[sslinecnt++] = s;
sslines[sslinecnt+0] = p;
sslines[sslinecnt+1] = NULL;
s = p;
p = strchr(s,'\r');
}
}
}
if (!ss || *ss == 0) {
if (SS->policyresult < 0)
sslines[0] = ss = "rejected, no further explanations";
else if (SS->policyresult == 0)
sslines[0] = ss = "accepted";
else
sslines[0] = ss = "accepted into freezer, no explanations";
sslines[1] = NULL;
}
if (SS->policyresult < 0) {
if (!statcode) statcode = m571;
if (!code) code = 552;
type(SS, -code, statcode, "Content-Policy msg: %s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "msg: %s", sslines[j]);
type(SS, code, statcode, "Content-Policy analysis rejected this message");
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
type(SS, -code, statcode, "Content-Policy msg: %s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "msg: %s", sslines[j]);
type(SS, code, statcode, "Content-Policy analysis rejected this message");
}
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
SS->mfp = NULL;
} else if (SS->policyresult > 0) {
char polbuf[20];
runasrootuser();
sprintf(polbuf,"policy-%d",SS->policyresult);
if (mail_close_alternate(SS->mfp, FREEZERDIR, polbuf) != 0) {
type(NULL,0,NULL,
"mail_close_alternate(..'FREEZER','%s') failed, errno=%d (%s)",
polbuf, errno, strerror(errno));
type(SS, 452, m430, "Message file disposition failed; %s", taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, "Message file disposition failed; %s", taspid);
typeflush(SS);
SS->mfp = NULL;
reporterr(SS, tell, "message file close failed");
} else {
smtp_tarpit(SS);
if (!statcode) statcode = "2.7.1";
if (!code) code = 250;
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
type(SS, code, statcode, "Content-Policy accepted this message into freezer-%d; %s", SS->policyresult, taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
type(SS, code, statcode, "Content-Policy accepted this message into freezer-%d; %s", SS->policyresult, taspid);
}
typeflush(SS);
SS->mfp = NULL;
zsyslog((LOG_INFO, "accepted %s (%ldc) from %s/%d into freeze[%d]",
taspid, tell, SS->rhostname, SS->rport, SS->policyresult));
}
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
runastrusteduser();
} else {
/* Ok, we didn't have smtp-policy defined freezer action,
lets see if we do it some other way.. */
if (mail_close(SS->mfp) == EOF) {
type(SS, 452, m430, (char *) NULL);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, (char *) NULL);
typeflush(SS);
SS->mfp = NULL;
reporterr(SS, tell, "message file close failed");
MIBMtaEntry->ss.IncomingSMTP_DATA_bad += 1;
} else {
/* Ok, build response with proper "spoolid" */
SS->mfp = NULL;
if (!ss || *ss == 0) {
type(SS, 250, "2.0.0", "Message accepted; %s", taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 250, "2.0.0", "Message accepted; %s", taspid);
} else {
if (!statcode) statcode = "2.0.0";
if (!code) code = 250;
if (sslinecnt < 1)
type(SS, code, statcode, "%s; %s", ss, taspid);
else
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
if (sslinecnt >= 1)
type(SS, code, statcode, "Content-Policy accepted this message; %s", taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
if (sslinecnt < 1)
type(SS, code, statcode, "%s; %s", ss, taspid);
else
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
if (sslinecnt >= 1)
type(SS, code, statcode, "Content-Policy accepted this message; %s", taspid);
}
}
typeflush(SS);
if (smtp_syslog)
zsyslog((LOG_INFO,
"%s: (%ldc) accepted from %s/%d", taspid, tell,
SS->rhostname, SS->rport));
MIBMtaEntry->ss.IncomingSMTP_DATA_ok += 1;
MIBMtaEntry->ss.TransmittedMessagesSs += 1;
MIBMtaEntry->ss.TransmittedRecipientsSs += SS->ok_rcpt_count;
MIBMtaEntry->ss.IncomingSMTP_DATA_KBYTES += (SS->messagesize+1023)/1024;
MIBMtaEntry->ss.IncomingSMTP_spool_KBYTES += (tell + 1023)/1024;
type(NULL,0,NULL,"%s: %ld bytes", taspid, tell);
if (logfp)
fflush(logfp);
}
}
}
SS->state = MailOrHello;
typeflush(SS);
return 0;
}
int smtp_bdata(SS, buf, cp)
SmtpState *SS;
const char *buf, *cp;
{
int filsiz, rc;
long tell;
char msg[2048];
long bdata_chunksize;
int bdata_last, i, j;
struct stat stbuf;
char *fname;
char taspid[30];
MIBMtaEntry->ss.ReceivedMessagesSs += 1;
MIBMtaEntry->ss.ReceivedRecipientsSs += SS->ok_rcpt_count;
MIBMtaEntry->ss.IncomingSMTP_BDAT += 1;
if (SS->state == RecipientOrData) {
SS->state = BData;
SS->bdata_blocknum = 0;
SS->mvbstate = -1;
}
*msg = 0;
rc = sscanf(cp, "%ld %7s %7s", &bdata_chunksize, msg, msg + 20);
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);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
return 0;
}
if (SS->bdata_blocknum == 1 && SS->mfp) {
fputs("env-end\n", SS->mfp);
if (msa_mode && SS->authuser != NULL ) {
fprintf(SS->mfp, "X-Comment: RFC 2476 MSA function at %s logged sender identity as: %s\n", SS->myhostname, SS->authuser);
}
}
/* We set alarm()s inside the mvbdata() */
*msg = 0;
filsiz = mvbdata(SS, msg, bdata_chunksize);
SS->messagesize += filsiz;
tell = 0;
if (SS->mfp) {
fflush(SS->mfp);
fname = mail_fname(SS->mfp);
fstat(FILENO(SS->mfp), &stbuf);
taspoolid(taspid, stbuf.st_mtime, stbuf.st_ino);
tell = stbuf.st_size;
} else {
tell = 0;
fname = "<NIL>";
strcpy(taspid, "<NIL>");
}
report(SS, "BDAT %ld%s; tell=%ld", bdata_chunksize,
bdata_last ? " LAST":"", tell);
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);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 503, m552, cp);
typeflush(SS);
if (SS->mfp)
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
return 0;
}
if (SS->bdata_blocknum == 1) {
if (SS->sender_ok == 0 || SS->rcpt_count == 0) {
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);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 550, "5.1.3", cp);
typeflush(SS);
SS->state = MailOrHello;
if (SS->mfp)
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
return 0;
}
if ((SS->from_box != 0) && (SS->rcpt_count > MaxErrorRecipients)) {
/* Too many recipients for a "MAIL FROM:<>" */
type(SS, 550, "5.7.1", "SPAM trap -- too many recipients for an empty source address!");
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 550, "5.7.1", "SPAM trap -- too many recipients for an empty source address!");
typeflush(SS);
SS->state = MailOrHello;
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
return 0;
}
SS->rcpt_count = 0; /* now we can zero them.. */
SS->sender_ok = 0;
}
availspace = used_fd_statfs(FILENO(SS->mfp));
if (availspace >= 0)
MIBMtaEntry->sys.SpoolUsedSpace = availspace;
availspace = free_fd_statfs(FILENO(SS->mfp));
if (availspace < 0)
availspace = LONG_MAX / 1024; /* Over 2G ? */
if (availspace >= 0)
MIBMtaEntry->sys.SpoolFreeSpace = availspace;
availspace -= minimum_availspace;
if (availspace > (LONG_MAX / 1024))
availspace = LONG_MAX / 1024;
availspace *= 1024;
/* The common typeflush() is at the end... */
if (SS->mfp == NULL) {
type(SS, 452, m430, "BDAT block discarded due to earlier error");
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, "BDAT block discarded due to earlier error");
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
} else if (*msg != 0) {
mail_abort(SS->mfp);
SS->mfp = NULL;
type(SS, 452, "%s", msg);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, "%s", msg);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
} else if (s_feof(SS)) {
/* [mea@utu.fi] says this can happen */
if (STYLE(SS->cfinfo,'D')) {
/* Says: DON'T DISCARD -- aka DEBUG ERRORS! */
mail_close_alternate(SS->mfp,"public",".BDAT-EOF");
} else
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
reporterr(SS, tell, "premature EOF on BDAT input");
typeflush(SS); /* Pointless ?? */
return -1;
} else if (availspace < 0 || ferror(SS->mfp)) {
type(SS, 452, m400, (char *) NULL);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m400, (char *) NULL);
reporterr(SS, tell,
ferror(SS->mfp) ? "write to spool file failed" :
"system free storage under limit");
clearerr(SS->mfp);
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
} else if (maxsize > 0 && tell > maxsize) {
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
type(SS, 552, "5.3.4", "Size of this message exceeds the fixed maximum size of %ld chars for received email ", maxsize);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 552, "5.3.4", "Size of this message exceeds the fixed maximum size of %ld chars for received email ", maxsize);
} else if (bdata_last) {
/* Things have been good thus far, now we store
the resulting file into router spool area;
pending a few things we do at first.. */
const char *statcode = NULL, *ss, *ss0;
int code = 0;
const char *sslines[20];
int sslinecnt = 0;
/* Lets see what the content-policy will tell now ? */
if (debug) typeflush(SS);
SS->policyresult = contentpolicy(policydb, &SS->policystate, fname);
ss0 = ss = policymsg(policydb, &SS->policystate);
if (ss)
type(NULL,0,NULL,
"Content-policy analysis ordered message %s. (code=%d); msg='%s'",
(SS->policyresult < 0 ? "rejection" :
(SS->policyresult > 0 ? "freezing" : "acceptance")),
SS->policyresult, ss);
if (ss) {
char *p, *s;
code = parsestatcode(&ss,&statcode);
s = (char *) ss;
p = strchr(s,'\r');
sslinecnt = 0;
if (p) {
/* Multiline! CRs in message text... */
while (p && sslinecnt < 17) { /* Arbitrary fixed limit.. */
*p++ = '\0';
sslines[sslinecnt++] = s;
sslines[sslinecnt+0] = p;
sslines[sslinecnt+1] = NULL;
s = p;
p = strchr(s,'\r');
}
}
}
if (!ss || *ss == 0) {
if (SS->policyresult < 0)
sslines[0] = ss = "rejected, no further explanations";
else if (SS->policyresult == 0)
sslines[0] = ss = "accepted";
else
sslines[0] = ss = "accepted into freezer, no explanations";
sslines[1] = NULL;
}
if (SS->policyresult < 0) {
if (!statcode) statcode = m571;
if (!code) code = 552;
type(SS, -code, statcode, "Content-Policy msg: %s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "msg: %s", sslines[j]);
type(SS, code, statcode, "Content-Policy analysis rejected this message");
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
type(SS, -code, statcode, "Content-Policy msg: %s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "msg: %s", sslines[j]);
type(SS, code, statcode, "Content-Policy analysis rejected this message");
}
mail_abort(SS->mfp);
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
SS->mfp = NULL;
} else if (SS->policyresult > 0) {
runasrootuser();
if (mail_close_alternate(SS->mfp, FREEZERDIR, "policy") != 0) {
type(NULL,0,NULL,
"mail_close_alternate(..'FREEZER','%s') failed, errno=%d (%s)",
"policy", errno, strerror(errno));
if (logfp)
fflush(logfp);
type(SS, 452, m430, "Message file disposition failed; %s", taspid);
if (lmtp_mode) for(i = 0; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m430, "Message file disposition failed; %s",taspid);
SS->mfp = NULL;
reporterr(SS, tell, "message file close failed");
} else {
smtp_tarpit(SS);
if (!statcode) statcode = "2.7.1";
if (!code) code = 250;
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
type(SS, code, statcode, "Content-Policy accepted this message into freezer-%d; %s", SS->policyresult, taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
type(SS, code, statcode, "Content-Policy accepted this message into freezer-%d; %s", SS->policyresult, taspid);
}
typeflush(SS);
SS->mfp = NULL;
zsyslog((LOG_INFO, "accepted %s (%ldc) from %s/%d into freeze[%d]",
taspid, tell, SS->rhostname, SS->rport, SS->policyresult));
}
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
runastrusteduser();
} else if (mail_close(SS->mfp) == EOF) {
type(SS, 452, m400, (char *) NULL);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 452, m400, (char *) NULL);
SS->mfp = NULL;
reporterr(SS, tell, "message file close failed");
MIBMtaEntry->ss.IncomingSMTP_BDAT_bad += 1;
} else {
/* Ok, build response with proper "spoolid" */
SS->mfp = NULL;
#if 1
type(SS, 250, "2.0.0", "%s Roger, got %ld bytes in the last chunk, stored %ld bytes into spool",
taspid, bdata_chunksize, (long) tell);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 250, "2.0.0", "%s Roger, got %ld bytes in the last chunk, stored %ld bytes into spool",
taspid, bdata_chunksize, (long) tell);
type(NULL,0,NULL,"-- pipeline input: %d bytes",s_hasinput(SS));
#else
if (!statcode) statcode = "2.0.0";
if (!code) code = 250;
if (sslinecnt < 1)
type(SS, code, statcode, "%s; %s", ss, taspid);
else
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
if (sslinecnt >= 1)
type(SS, code, statcode, "Content-Policy accepted this message; %s", taspid);
if (lmtp_mode) for(i = 1; i < SS->ok_rcpt_count; ++i) {
if (sslinecnt < 1)
type(SS, code, statcode, "%s; %s", ss, taspid);
else
type(SS, -code, statcode, "%s; %s", ss, taspid);
for (j= 1; j <= sslinecnt; ++j)
type(SS, -code, statcode, "%s", sslines[j]);
if (sslinecnt >= 1)
type(SS, code, statcode, "Content-Policy accepted this message; %s", taspid);
}
#endif
MIBMtaEntry->ss.IncomingSMTP_BDAT_ok += 1;
MIBMtaEntry->ss.TransmittedMessagesSs += 1;
MIBMtaEntry->ss.TransmittedRecipientsSs += SS->ok_rcpt_count;
MIBMtaEntry->ss.IncomingSMTP_BDAT_KBYTES += (SS->messagesize+1023)/1024;
MIBMtaEntry->ss.IncomingSMTP_spool_KBYTES += (tell + 1023)/1024;
if (smtp_syslog)
zsyslog((LOG_INFO,
"%s: (%ldc) accepted from %s/%d", taspid, tell,
SS->rhostname, SS->rport));
type(NULL,0,NULL,"%s: %ld bytes", taspid, tell);
if (logfp)
fflush(logfp);
}
} else { /* Not last chunk! */
type(SS, 250, "2.0.0", "Received %ld bytes", bdata_chunksize);
if (lmtp_mode && bdata_last) for(i = 1; i < SS->ok_rcpt_count; ++i)
type(SS, 250, "2.0.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
};
/*
* BASE64 DECODER index table, and ENCODER map array...
*/
static int base64decode_index[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 decodechar64(c) (((c) < 0 || (c) > 127) ? -1 : base64decode_index[(c)])
static char base64encode_array[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int decodebase64string(instr,inlen,outstr,outspc,inleftover)
const char * instr;
char * outstr;
const char ** inleftover;
int inlen, outspc;
{
int b64decoding = 1, outlen = 0;
int b64eod = 0, b64i = 0, i, c;
char b64c[4];
while (inlen > 0 && outlen < outspc && !b64eod) {
c = *instr++; --inlen;
if (c == '=' || !b64decoding) {
b64eod = 1;
continue;
}
i = decodechar64(c);
if (i < 0) continue;
b64c[b64i++] = i;
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;
}
outstr[outlen] = c;
++outlen;
}
if (outlen < outspc)
outstr[outlen] = 0;
while (*instr == '=') ++instr;
if (inleftover)
*inleftover = instr;
return outlen;
}
int encodebase64string(instr,inlen,outstr,outspc)
const char * instr;
char *outstr;
int inlen, outspc; /* Always positive values .. */
{
/* Build groups of 3 bytes, encode them in 4 chars; if some byte
is not filled, mark the incomplete byte with '=' */
u_char b64out[4];
int b64i, outlen = 0, b64val;
while (inlen > 0 && outlen < outspc) {
b64i = inlen > 3 ? 3 : inlen;
b64val = ((unsigned char) instr[0]) << 16;
if (b64i > 1)
b64val |= ((unsigned char) instr[1]) << 8;
if (b64i > 2)
b64val |= ((unsigned char) instr[2]);
b64out[3] = base64encode_array[ b64val & 63 ]; b64val >>= 6;
b64out[2] = base64encode_array[ b64val & 63 ]; b64val >>= 6;
b64out[1] = base64encode_array[ b64val & 63 ]; b64val >>= 6;
b64out[0] = base64encode_array[ b64val & 63 ];
switch(b64i) {
case 1:
b64out[2] = '=';
case 2:
b64out[3] = '=';
}
instr += b64i;
inlen -= b64i;
b64i = (b64i == 1) ? 3 : 4;
if ((outlen + b64i) < outspc) { /* Can fit in .. */
memcpy(outstr+outlen, b64out, b64i);
outlen += b64i;
} else { /* Can't fit in :-( */
memcpy(outstr+outlen, b64out, outspc-outlen);
outlen = outspc;
}
}
return outlen;
}
/*
* Copy bytes from stdin to out, obeying sensible SMTP DATA input heuristics.
*
* Rayan back in 1988:
* "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."
* (with 68030 CPU running at 33 MHz -- about 10-15 MIPS)
*/
static int /* count of bytes */ mvdata(SS, msg)
SmtpState *SS;
char *msg;
{
register int c, state, *sts, endstate, cnt;
register char *idxnum;
#ifdef NO_INCOMING_HEADER_PROCESSING
idxnum = indexnum + 1;
state = 10;
endstate = X_;
sts = states;
cnt = 0;
SS->read_alarm_ival = 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;
#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];
#endif /* USE_TRANSLATION */
typeflush(SS);
idxnum = indexnum + 1;
state = 10;
endstate = X_;
sts = states;
cnt = 0;
col = 0;
SS->read_alarm_ival = 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, 1);
/* An EOF in here is an error! */
#if EOF != -1
if (c == EOF)
return EOF;
#else
if (c < 0)
return EOF;
#endif
++cnt;
state = sts[state + idxnum[c]];
if (state & ~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)
fputc(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 = state & 0xFF;
continue;
}
if (col < (sizeof(linebuf) - 1))
linebuf[col++] = (state >> 8);
state = state & 0xFF;
}
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;
type(NULL,0,NULL,"(8bit decode: %s, translate: %s) [%s%s,%s]",
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++)
fputc(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;
}
}
/* XX: The anti-spam hacks. To differentiate between the auto-freeze of
* allegedly-spam messages and freezes resulting from user-specified
* smtp-policy.src, we do not use 1 to freeze the messages.
*/
if (CISTREQN(linebuf, "X-Advertisement:",16)) {
/* Gee... Only SPAMmers (Cyberpromo!) use this .. (I hope..) */
SS->policyresult = FREEZE__X_ADVERTISEMENT_FOUND;
type(NULL,0,NULL,"Found X-Advertisement header");
}
if (CISTREQN(linebuf, "X-Advertisment:",15)) {
/* Gee... Only SPAMmers (Cyberpromo!) use this .. (I hope..) */
SS->policyresult = FREEZE__X_ADVERTISEMENT_FOUND;
type(NULL,0,NULL,"Found X-Advertisment header");
}
#ifdef USE_ANTISPAM_HACKS
if (strncmp(linebuf, "X-UIDL:", 7)==0) {
/* Sigh... SPAMmers use this .. but it is valid AOL too.. */
SS->policyresult = FREEZE__X_UIDL_FOUND;
}
#endif
#ifdef USE_ANTISPAM_HACKS
/* This test probably doesn't work */
if (CISTREQN(linebuf, "Received:", 9)) {
/* Scan for "(really ". Anything with this string in the Received
* header is highly likely to have a forged Received header
* characteristic of spams. Unfortunately this string may
* also be a result of pure coincidence.
*/
s = linebuf + 11;
while (*s) {
if (CISTREQN(s, "(really ", 8)) {
SS->policyresult = FREEZE__IMPROBABLE_RECEIVED_HEADER_FOUND;
type(NULL,0,NULL,"Improbable Received: header");
break;
}
++s;
}
}
#endif
#ifdef USE_ANTISPAM_HACKS
if (CISTREQN(linebuf, "Message-ID:", 11)) {
/* Freeze any mail with no message id in the message-id header,
* or a message id with obvious syntax errors, or message id
* with junk after it. These are highly likely to be spam, though
* they might only be a result of buggy software. (MS Exchange?)
*/
s = linebuf + 11;
while (*s == ' ' || *s == '\t')
++s;
if (*s != '<') {
SS->policyresult = FREEZE__MALFORMED_MESSAGE_ID_HEADER;
type(NULL,0,NULL,"No <> around Message-Id");
} else if (s[1] == '@') {
SS->policyresult = FREEZE__MALFORMED_MESSAGE_ID_HEADER;
type(NULL,0,NULL,"Source route in Message-Id:");
} else if (s[1] == '>') {
SS->policyresult = FREEZE__MALFORMED_MESSAGE_ID_HEADER;
type(NULL,0,NULL,"Empty Message-Id:");
} else {
const char *t = rfc821_path(s, 1);
if (s == t) { /* error */
#ifdef USE_STRICT_MSGID_FREEZING
SS->policyresult = FREEZE__MALFORMED_MESSAGE_ID_HEADER;
type(NULL,0,NULL,"Message-Id: syntax error");
#endif
} else {
while (*t == ' ' || *t == '\t' || *t == '\r' || *t == '\n')
++t;
if (*t) {
SS->policyresult = FREEZE__MALFORMED_MESSAGE_ID_HEADER;
type(NULL,0,NULL,"Spurious junk after Message-Id:");
}
}
}
}
#endif
if (CISTREQN(linebuf, "Precedence:", 11)) {
s = linebuf + 11;
while (*s == ' ' || *s == '\t')
++s;
if ((eol - s) < 4)
continue; /* Hmm.. */
if (CISTREQN("high", s, 4))
mail_priority = _MAILPRIO_HIGH;
else if (CISTREQN("junk", s, 4))
mail_priority = _MAILPRIO_JUNK;
else if (CISTREQN("bulk", s, 4))
mail_priority = _MAILPRIO_BULK;
else if (((eol - s) >= 6) && CISTREQN("normal", s, 6))
mail_priority = _MAILPRIO_NORMAL;
}
#if 0 /* Nice in theory - impractical in reality */
if (msa_mode && CISTREQN(linebuf, "Sender:", 7)) {
if ( SS->authuser != NULL ) {
fprintf(SS->mfp, "Sender: %s@%s\n", SS->authuser, SS->myhostname);
fprintf(SS->mfp, "Old-");
}
}
#endif
#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.. */
/* Must ALWAYS check for C-T: TEXT/any ! */
/* #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)
fputc(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 (verbose)
type(NULL,0,NULL,"(mail_priority=%d)", mail_priority);
#endif
/* ================ Normal email BODY input.. ================ */
for (;;) {
c = s_getc(SS, 1);
#if EOF != -1
if (c == EOF) /* a little slower... */
break;
#endif
++cnt;
state = sts[state + idxnum[c]];
if (state & ~0xff) {
if (state & O_) {
if (state == endstate)
break;
state = (char) state;
continue;
}
if (!ferror(SS->mfp))
fputc((state >> 8), SS->mfp);
state = state & 0xFF;
}
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++] = decodechar64(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)
fputc(TR_IN(c), SS->mfp);
else
#endif /* USE_TRANSLATION */
fputc(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;
cnt = 0;
/* XX: header processing REMOVED from BDAT processing */
SS->read_alarm_ival = SMTP_DATA_TIME_PER_LINE;
/* ================ Normal email BODY input.. ================ */
for (; incount > 0; --incount) {
c = s_getc(SS, 1);
if (c == EOF)
break;
++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))
fputc(SS->mvbstate, SS->mfp);
if (!ferror(SS->mfp))
fputc(c, SS->mfp);
}
} else {
/* CR + LF -- forget the CR */
if (SS->mfp && !ferror(SS->mfp))
fputc(c, SS->mfp);
}
} else {
/* Anything else, just output it! */
if (SS->mfp && !ferror(SS->mfp))
fputc(c, SS->mfp);
}
SS->mvbstate = c;
}
return cnt;
}
syntax highlighted by Code2HTML, v. 0.9.1