Newer
Older
lprintf("%04d !SMTP ERROR %d ADDING MESSAGE: %s"
, socket, i, smb.last_error);

rswindell
committed
sockprintf(socket, "554 Duplicate Message");
continue;
}
}

rswindell
committed
msg.hdr.offset=offset;

rswindell
committed
smb_dfield(&msg,TEXT_BODY,length);

rswindell
committed
smb_unlocksmbhdr(&smb);
#else /* new way */
smb.subnum=INVALID_SUB;
i=savemsg(&scfg, &smb, &msg, msgbuf);
free(msgbuf);
if(i!=0) {
lprintf("%04d !SMTP ERROR %d (%s) saving message"
,socket,i,smb.last_error);
sockprintf(socket, "452 ERROR %d (%s) saving message"
,i,smb.last_error);
continue;
}
#endif
rcpt_count=0;
while(!feof(rcptlst) && rcpt_count<startup->max_recipients) {

rswindell
committed
if((i=smb_copymsgmem(&newmsg,&msg))!=0) {
lprintf("%04d !SMTP ERROR %d (%s) copying message"
,socket, i, smb.last_error);

rswindell
committed
break;
}
if(fgets(str,sizeof(str),rcptlst)==NULL)

rswindell
committed
break;
usernum=atoi(str);
if(fgets(rcpt_name,sizeof(rcpt_name),rcptlst)==NULL)

rswindell
committed
break;
truncsp(rcpt_name);
if(fgets(rcpt_addr,sizeof(rcpt_addr),rcptlst)==NULL)
break;

rswindell
committed
truncsp(rcpt_addr);
snprintf(hdrfield,sizeof(hdrfield),
"Received: from %s (%s [%s])\r\n"
" by %s [%s] (Synchronet Mail Server %s-%s) with %s\r\n"

rswindell
committed
" for %s; %s"
,host_name,hello_name,host_ip
,startup->host_name,inet_ntoa(server_addr.sin_addr)
,revision,PLATFORM_DESC

rswindell
committed
,esmtp ? "ESMTP" : "SMTP"
,rcpt_name,msgdate(msg.hdr.when_imported,date));

rswindell
committed
smb_hfield(&newmsg, RFC822HEADER, (ushort)strlen(hdrfield), hdrfield);
smb_hfield(&newmsg, RECIPIENT, (ushort)strlen(rcpt_name), rcpt_name);
if(rcpt_addr[0]=='#') { /* Local destination */
newmsg.idx.to=atoi(rcpt_addr+1);
smb_hfield(&newmsg, RECIPIENTEXT
,(ushort)strlen(rcpt_addr+1), rcpt_addr+1);
} else {
newmsg.idx.to=0;
nettype=NET_INTERNET;
smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(nettype), &nettype);

rswindell
committed
smb_hfield(&newmsg, RECIPIENTNETADDR
,(ushort)strlen(rcpt_addr), rcpt_addr);
}

rswindell
committed
i=smb_addmsghdr(&smb,&newmsg,SMB_SELFPACK);
smb_freemsgmem(&newmsg);
if(i!=0) {
lprintf("%04d !SMTP ERROR %d (%s) adding message header"
,socket, i, smb.last_error);

rswindell
committed
break;

rswindell
committed
lprintf("%04d SMTP Created message #%ld from %s to %s <%s>"
,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
if(usernum) {
sprintf(str,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: "
"\1h%s\1n\r\n"
,timestr(&scfg,(time_t*)&newmsg.hdr.when_imported.time,tmp)

rswindell
committed
,sender,sender_addr);
if(!newmsg.idx.to) { /* Forwarding */
strcat(str,"\1mand it was automatically forwaded to: \1h");
strcat(str,user.netmail);
strcat(str,"\1n\r\n");
}
putsmsg(&scfg, usernum, str);
rcpt_count++;
if(rcpt_count<1) {
smb_freemsgdat(&smb,msg.hdr.offset,length,0);

rswindell
committed
sockprintf(socket, "452 Insufficient system storage");
}
else {
if(rcpt_count>1)
smb_incdat(&smb,msg.hdr.offset,length,(ushort)(rcpt_count-1));
sockprintf(socket,ok_rsp);
signal_smtp_sem();
}
smb_close_da(&smb);
smb_close(&smb);
continue;
}
if(buf[0]==0 && state==SMTP_STATE_DATA_HEADER) {
state=SMTP_STATE_DATA_BODY; /* Null line separates header and body */
if(msgtxt!=NULL) {
fprintf(msgtxt, "\r\n");
hdr_len=ftell(msgtxt);
}
continue;
}
if(state==SMTP_STATE_DATA_BODY) {
p=buf;
if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", p);
/* release time-slices every x lines */
if(startup->lines_per_yield &&
!(lines%startup->lines_per_yield))
continue;
}
/* RFC822 Header parsing */
if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
lprintf("%04d SMTP %s",socket, buf);
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", buf);
hdr_lines++;
#if 0 /* moved */
if(!strnicmp(buf, "SUBJECT:",8)) {
p=buf+8;
while(*p && *p<=' ') p++;
if(dnsbl_result.s_addr && startup->dnsbl_tag[0]
&& !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
sprintf(str,"%.*s: %.*s"
,(int)sizeof(str)/2, startup->dnsbl_tag
,(int)sizeof(str)/2, p);
lprintf("%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
,socket, startup->dnsbl_tag);
smb_hfield(&msg, SUBJECT, (ushort)strlen(p), p);
msg.idx.subj=subject_crc(p);
if(!strnicmp(buf, "FROM:", 5)
&& !chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr,reverse_path))
break;
parse_header_field(buf,&msg);
#endif
lprintf("%04d SMTP RX: %s", socket, buf);
if(!strnicmp(buf,"HELO",4)) {
p=buf+4;
while(*p && *p<=' ') p++;
sockprintf(socket,"250 %s",startup->host_name);
esmtp=FALSE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
continue;
}
if(!strnicmp(buf,"EHLO",4)) {
p=buf+4;
while(*p && *p<=' ') p++;
sockprintf(socket,"250 %s",startup->host_name);
esmtp=TRUE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
continue;
}
if(!stricmp(buf,"QUIT")) {
sockprintf(socket,"221 %s Service closing transmission channel",startup->host_name);
break;
}
if(!stricmp(buf,"NOOP")) {
sockprintf(socket, ok_rsp);
badcmds=0;
continue;
}
if(state<SMTP_STATE_HELO) {
/* RFC 821 4.1.1 "The first command in a session must be the HELO command." */
lprintf("%04d !SMTP MISSING 'HELO' command",socket);
sockprintf(socket, badseq_rsp);
continue;
}
if(!stricmp(buf,"TURN")) {
sockprintf(socket,"502 command not supported");
badcmds=0;
continue;
}
if(!stricmp(buf,"RSET")) {
smb_freemsgmem(&msg);
memset(&msg,0,sizeof(smbmsg_t)); /* Initialize message header */
reverse_path[0]=0;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
/* reset recipient list */
if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
lprintf("%04d !SMTP ERROR %d re-opening %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);
break;
}
rcpt_count=0;
sockprintf(socket,ok_rsp);
badcmds=0;
lprintf("%04d SMTP session reset",socket);
if(!strnicmp(buf,"MAIL FROM:",10)
|| !strnicmp(buf,"SEND FROM:",10) /* Send a Message (Telegram) to a local ONLINE user */
|| !strnicmp(buf,"SOML FROM:",10) /* Send OR Mail a Message to a local user */
|| !strnicmp(buf,"SAML FROM:",10) /* Send AND Mail a Message to a local user */
) {
if(!chk_email_addr(socket,p,host_name,host_ip,NULL,NULL))
/* Update client display */
client.user=reverse_path;
client_on(socket,&client,TRUE /* update */);
/* Setup state */
if(!strnicmp(buf,"MAIL FROM:",10))
cmd=SMTP_CMD_MAIL;
else if(!strnicmp(buf,"SEND FROM:",10))
cmd=SMTP_CMD_SEND;
else if(!strnicmp(buf,"SOML FROM:",10))
cmd=SMTP_CMD_SOML;
else if(!strnicmp(buf,"SAML FROM:",10))
cmd=SMTP_CMD_SAML;
/* reset recipient list */
/* reset recipient list */
if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
lprintf("%04d !SMTP ERROR %d re-opening %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);
break;
}
rcpt_count=0;
#if 0 /* moved */
/* Initialize message header */
memset(&msg,0,sizeof(smbmsg_t));
msg.hdr.version=smb_ver();
msg.hdr.when_imported.time=time(NULL);
msg.hdr.when_imported.zone=scfg.sys_timezone;
#endif
sockprintf(socket,ok_rsp);
badcmds=0;

rswindell
committed
continue;
}
#if 0 /* No one uses this command */
if(!strnicmp(buf,"VRFY",4)) {
p=buf+4;
while(*p && *p<=' ') p++;
if(*p==0) {
sockprintf(socket,"550 No user specified.");
continue;
}
#endif
/* Add to Recipient list */
if(!strnicmp(buf,"RCPT TO:",8)) {
lprintf("%04d !SMTP MISSING 'MAIL' command",socket);
sockprintf(socket, badseq_rsp);

rswindell
committed
if(spy==NULL && trashcan(&scfg,reverse_path,"smtpspy")) {
sprintf(str,"%sSMTPSPY.TXT", scfg.data_dir);
spy=fopen(str,"a");
}
p=strrchr(str,'<');
if(p==NULL)
p=str;
else
p++;
truncstr(str,">"); /* was truncating at space too */
forward=FALSE;
no_forward=FALSE;
if(!strnicmp(p,FORWARD,strlen(FORWARD))) {
forward=TRUE; /* force forward to user's netmail address */
p+=strlen(FORWARD);
}
if(!strnicmp(p,NO_FORWARD,strlen(NO_FORWARD))) {
no_forward=TRUE; /* do not forward to user's netmail address */
p+=strlen(NO_FORWARD);
}
rcpt_name[0]=0;
/* Check recipient counter */
if(rcpt_count>=startup->max_recipients) {
lprintf("%04d !SMTP MAXIMUM RECIPIENTS (%d) REACHED"
,socket, startup->max_recipients);
sprintf(tmp,"Maximum recipient count (%d)",startup->max_recipients);
spamlog(&scfg, "SMTP", "REFUSED", tmp
,host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "552 Too many recipients");
continue;
}
/* Check for blocked recipients */
if(trashcan(&scfg,rcpt_addr,"email")) {
lprintf("%04d !SMTP BLOCKED RECIPIENT (%s) from: %s"
,socket, rcpt_addr, reverse_path);
spamlog(&scfg, "SMTP", "REFUSED", "Blocked recipient e-mail address"
,host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "550 Unknown User:%s", buf+8);
continue;
}
if(dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_BADUSER) {
lprintf("%04d !SMTP REFUSED MAIL from blacklisted server"
,socket);
sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "REFUSED", str, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket
,"550 Mail from %s refused due to listing at %s"
,host_ip, dnsbl);
continue;
}
/* Check for full address aliases */
p=alias(&scfg,p,alias_buf);
if(p==alias_buf)
lprintf("%04d SMTP ADDRESS ALIAS: %s",socket,p);
tp=strrchr(p,'@');
if(cmd==SMTP_CMD_MAIL && tp!=NULL) {
dest_port=server_addr.sin_port;
cp=strrchr(dest_host,':');
if(cp!=NULL) {
*cp=0;
dest_port=atoi(cp+1);
}
sprintf(domain_list,"%sdomains.cfg",scfg.ctrl_dir);
if((stricmp(dest_host,scfg.sys_inetaddr)!=0
&& stricmp(dest_host,startup->host_name)!=0
&& resolve_ip(dest_host)!=server_addr.sin_addr.s_addr
&& findstr(dest_host,domain_list)==FALSE)
|| dest_port!=server_addr.sin_port) {
sprintf(relay_list,"%srelay.cfg",scfg.ctrl_dir);
relay_user.number=userdatdupe(&scfg, 0, U_NOTE, LEN_NOTE, host_ip, FALSE);
if(relay_user.number!=0)
getuserdat(&scfg,&relay_user);
if(p!=alias_buf /* forced relay by alias */ &&
(!(startup->options&MAIL_OPT_ALLOW_RELAY)
|| relay_user.number==0
|| relay_user.laston < time(NULL)-(60*60)
|| relay_user.rest&(FLAG('G')|FLAG('M'))) &&
!findstr(host_name,relay_list) &&
!findstr(host_ip,relay_list)) {
lprintf("%04d !SMTP ILLEGAL RELAY ATTEMPT from %s [%s] to %s"
,socket, reverse_path, host_ip, p);
sprintf(tmp,"Relay attempt to: %s", p);
spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
if(startup->options&MAIL_OPT_ALLOW_RELAY)
sockprintf(socket, "553 Relaying through this server "
"requires authentication. "
"Please authenticate with POP3 before sending.");
else
sockprintf(socket, "550 Relay not allowed.");
if(relay_user.number==0)
SAFECOPY(relay_user.alias,"Unknown User");
lprintf("%04d SMTP %s relaying to external mail service: %s"
,socket, relay_user.alias, tp+1);
fprintf(rcptlst,"0\n%.*s\n%.*s\n"
sockprintf(socket,ok_rsp);
rcpt_count++;
while(*p && !isalnum(*p)) p++; /* Skip '<' or '"' */
truncstr(p,"\"");
p=alias(&scfg,p,name_alias_buf);
if(p==name_alias_buf)
lprintf("%04d SMTP NAME ALIAS: %s",socket,p);
if(!strnicmp(p,"sub:",4)) { /* Post on a sub-board */
p+=4;
for(i=0;i<scfg.total_subs;i++)
if(!stricmp(p,scfg.sub[i]->code))
break;
if(i>=scfg.total_subs) {
lprintf("%04d !SMTP UNKNOWN SUB-BOARD: %s", socket, p);
sockprintf(socket, "550 Unknown sub-board: %s", p);
continue;
}
subnum=i;
sockprintf(socket,ok_rsp);
state=SMTP_STATE_RCPT_TO;
rcpt_count++;
continue;
}
usernum=0; /* unknown user at this point */
if(startup->options&MAIL_OPT_ALLOW_RX_BY_NUMBER
usernum=atoi(p); /* RX by user number */
/* verify usernum */
username(&scfg,usernum,str);
if(!str[0] || !stricmp(str,"DELETED USER"))
usernum=0;
p=str;
} else {
/* RX by "user alias", "user.alias" or "user_alias" */
usernum=matchuser(&scfg,p,TRUE /* sysop_alias */);
if(!usernum) { /* RX by "real name", "real.name", or "sysop.alias" */
/* convert "user.name" to "user name" */
for(tp=rcpt_name;*tp;tp++)
if(*tp=='.') *tp=' ';
if(!stricmp(p,scfg.sys_op) || !stricmp(rcpt_name,scfg.sys_op))
usernum=1; /* RX by "sysop.alias" */
if(!usernum && scfg.msg_misc&MM_REALNAME) /* RX by "real name" */
usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, p, FALSE);
if(!usernum && scfg.msg_misc&MM_REALNAME) /* RX by "real.name" */
usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, rcpt_name, FALSE);
if(!usernum && startup->default_user[0]) {
usernum=matchuser(&scfg,startup->default_user,TRUE /* sysop_alias */);
if(usernum)
lprintf("%04d SMTP Forwarding mail for UNKNOWN USER to default user: %s"
,socket,startup->default_user);
else
lprintf("%04d !SMTP UNKNOWN DEFAULT USER: %s"
,socket,startup->default_user);
}
lprintf("%04d !SMTP UNKNOWN USER:%s", socket, buf+8);
sockprintf(socket, "550 Unknown User:%s", buf+8);
continue;
}
user.number=usernum;
if((i=getuserdat(&scfg, &user))!=0) {

rswindell
committed
lprintf("%04d !SMTP ERROR %d getting data on user #%u (%s)"
sockprintf(socket, "550 Unknown User:%s", buf+8);
continue;
}
if(user.misc&(DELETED|INACTIVE)) {

rswindell
committed
lprintf("%04d !SMTP DELETED or INACTIVE user #%u (%s)"
sockprintf(socket, "550 Unknown User:%s", buf+8);
if(cmd==SMTP_CMD_SEND) { /* Check if user online */

rswindell
committed
for(i=0;i<scfg.sys_nodes;i++) {
getnodedat(&scfg, i+1, &node, 0);
if(node.status==NODE_INUSE && node.useron==user.number
&& !(node.misc&NODE_POFF))
break;
}
if(i>=scfg.sys_nodes) {
lprintf("%04d !Attempt to send telegram to unavailable user #%u (%s)"
,socket, user.number, user.alias);
sockprintf(socket,"450 User unavailable");
continue;
}
}

rswindell
committed
if(useron.etoday>=cfg.level_emailperday[useron.level]
&& !(useron.rest&FLAG('Q'))) {
bputs(text[TooManyEmailsToday]);
continue;
}

rswindell
committed
} else
telegram=TRUE;
fprintf(rcptlst,"%u\n%.*s\n"
,user.number,(int)sizeof(rcpt_name)-1,rcpt_addr);
/* Forward to Internet */
tp=strrchr(user.netmail,'@');

rswindell
committed
if(!telegram
&& !no_forward

rswindell
committed
&& scfg.sys_misc&SM_FWDTONET
&& (user.misc&NETMAIL || forward)
&& tp && strchr(tp,'.') && !strchr(tp,'/')
&& !strstr(tp,scfg.sys_inetaddr)) {
lprintf("%04d SMTP Forwarding to: %s"
,socket, user.netmail);
fprintf(rcptlst,"%s\n",user.netmail);
sockprintf(socket,"251 User not local; will forward to %s", user.netmail);
} else { /* Local (no-forward) */
fprintf(rcptlst,"#%u\n",usernum);
sockprintf(socket,ok_rsp);
rcpt_count++;
/* Message Data (header and body) */
if(!strnicmp(buf,"DATA",4)) {
lprintf("%04d !SMTP MISSING 'RCPT TO' command", socket);
sockprintf(socket, badseq_rsp);
if(msgtxt!=NULL) {
if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
unlink(msgtxt_fname);
}
sprintf(msgtxt_fname,"%sSMTP.%s.msg", scfg.data_dir, session_id);
if((msgtxt=fopen(msgtxt_fname,"w+b"))==NULL) {
,socket, errno, msgtxt_fname);
sockprintf(socket, "452 Insufficient system storage");
continue;
}

rswindell
committed
/* These vars are potentially over-written by parsing an RFC822 header */
/* get sender_addr */
p=strchr(reverse_path,'<');
if(p==NULL)
p=reverse_path;
else
p++;
SAFECOPY(sender_addr,p);
truncstr(sender_addr,">");

rswindell
committed
/* get sender */
SAFECOPY(sender,sender_addr);
if(truncstr(sender,"@")==NULL)

rswindell
committed
sender[0]=0;
sockprintf(socket, "354 send the mail data, end with <CRLF>.<CRLF>");

rswindell
committed
if(telegram)
state=SMTP_STATE_DATA_BODY; /* No RFC headers in Telegrams */
else
state=SMTP_STATE_DATA_HEADER;
hdr_lines=0;
continue;
}
sockprintf(socket,"500 Syntax error");
lprintf("%04d !SMTP UNSUPPORTED COMMAND: '%s'", socket, buf);
lprintf("%04d !TOO MANY INVALID COMMANDS (%u)",socket,badcmds);
break;
}
}
/* Free up resources here */
smb_freemsgmem(&msg);
if(msgtxt!=NULL) {
fclose(msgtxt);
if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
unlink(msgtxt_fname);
}
if(rcptlst!=NULL) {
fclose(rcptlst);
unlink(rcptlst_fname);
}
if(spy!=NULL)
fclose(spy);
status(STATUS_WFC);
active_clients--,update_clients();
client_off(socket);
thread_down();
lprintf("%04d SMTP RX Session thread terminated (%u threads remain, %lu clients served)"
,socket, thread_count, served);
/* Must be last */
mail_close_socket(socket);
}
BOOL bounce(smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate)
{
char str[128];
char attempts[64];
ushort agent=AGENT_SMTPSYSMSG;
smbmsg_t newmsg;
if((i=smb_lockmsghdr(smb,msg))!=0) {
lprintf("0000 !BOUNCE ERROR %d (%s) locking message header #%lu"
,i, smb->last_error, msg->hdr.number);
return(FALSE);
}
msg->hdr.delivery_attempts++;
if((i=smb_putmsg(smb,msg))!=0) {
lprintf("0000 !BOUNCE ERROR %d (%s) incrementing delivery attempt counter"
,i, smb->last_error);
smb_unlockmsghdr(smb,msg);
return(FALSE);
}
lprintf("0000 !Delivery attempt #%u FAILED for message #%lu from %s to %s"
,msg->hdr.delivery_attempts, msg->hdr.number
,msg->from, msg->to_net.addr);
if(!immediate && msg->hdr.delivery_attempts<startup->max_delivery_attempts) {
smb_unlockmsghdr(smb,msg);
return(TRUE);
}
newmsg=*msg;
/* Mark original message as deleted */
msg->hdr.attr|=MSG_DELETE;
msg->idx.attr=msg->hdr.attr;
if((i=smb_putmsg(smb,msg))!=0) {
lprintf("0000 !BOUNCE ERROR %d (%s) deleting message"
,i, smb->last_error);
smb_unlockmsghdr(smb,msg);
return(FALSE);
}
if(msg->hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,msg);
smb_unlockmsghdr(smb,msg);
if(msg->from_agent!=AGENT_PERSON /* don't bounce 'bounce messages' */
|| (msg->idx.from==0 && msg->from_net.type==NET_NONE)
|| (msg->reverse_path!=NULL && *msg->reverse_path==0)) {
lprintf("0000 !Deleted undeliverable message from %s", msg->from);
#if 0 /* this is actually wrong... whoever deletes the index will free the data fields */
i=smb_freemsgdat(smb,msg->hdr.offset,smb_getmsgdatlen(msg),1);
if(i!=SMB_SUCCESS)
lprintf("0000 !ERROR %d (%s) freeing data blocks for undeliverable message"
,i,smb->last_error);
#endif
return(TRUE);
}
lprintf("0000 !Bouncing message back to %s", msg->from);
newmsg.hfield=NULL;
newmsg.hfield_dat=NULL;
newmsg.total_hfields=0;
newmsg.idx.to=newmsg.idx.from;
newmsg.idx.from=0;
newmsg.hdr.delivery_attempts=0;
sprintf(str,"Delivery failure: %.100s",newmsg.subj);
smb_hfield(&newmsg, SUBJECT, (ushort)strlen(str), str);
smb_hfield(&newmsg, RECIPIENT, (ushort)strlen(newmsg.from), newmsg.from);
if(newmsg.idx.to) {
sprintf(str,"%u",newmsg.idx.to);
smb_hfield(&newmsg, RECIPIENTEXT, (ushort)strlen(str), str);
}
if(newmsg.from_net.type!=NET_NONE && newmsg.from_net.type!=NET_FIDO
&& newmsg.reverse_path!=NULL) {
smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(newmsg.from_net.type)
,&newmsg.from_net.type);
smb_hfield(&newmsg, RECIPIENTNETADDR, (ushort)strlen(newmsg.reverse_path)
,newmsg.reverse_path);
}
strcpy(str,"Mail Delivery Subsystem");
smb_hfield(&newmsg, SENDER, (ushort)strlen(str), str);
smb_hfield(&newmsg, SENDERAGENT, sizeof(agent), &agent);
/* Put error message in subject for now */
if(msg->hdr.delivery_attempts>1)
sprintf(attempts,"after %u attempts", msg->hdr.delivery_attempts);
else
attempts[0]=0;
sprintf(str,"%s reporting delivery failure of message %s"
,startup->host_name, attempts);
smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
sprintf(str,"from %s to %s\r\n"
,msg->reverse_path==NULL ? msg->from : msg->reverse_path
,(char*)msg->to_net.addr);
smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
strcpy(str,"Reason:");
smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(err), err);
sprintf(str,"\r\nOriginal message text follows:\r\n");
smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
if((i=smb_addmsghdr(smb,&newmsg,SMB_SELFPACK))!=0)
lprintf("0000 !BOUNCE ERROR %d (%s) adding message header"
,i,smb->last_error);
else {
lprintf("0000 !Delivery failure notification (message #%ld) created for %s"
,newmsg.hdr.number, newmsg.from);
if((i=smb_incmsg(smb,&newmsg))!=0)
lprintf("0000 !BOUNCE ERROR %d (%s) incrementing data allocation units"
,i,smb->last_error);
}
newmsg.dfield=NULL; /* Don't double-free the data fields */
newmsg.hdr.total_dfields=0;
smb_freemsgmem(&newmsg);
return(TRUE);
}
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
int i,j;
char to[128];
char mx[128];
char mx2[128];
char err[128];
char buf[512];
char toaddr[256];
char fromaddr[256];
char* server;
char* msgtxt=NULL;
char* p;
ushort port;
ulong offset;
ulong last_msg=0;
ulong total_msgs;
ulong ip_addr;
ulong dns;
BOOL first_cycle=TRUE;
SOCKET sock=INVALID_SOCKET;
SOCKADDR_IN addr;
SOCKADDR_IN server_addr;
time_t last_scan=0;
smb_t smb;
smbmsg_t msg;
sendmail_running=TRUE;
thread_up(TRUE /* setuid */);
lprintf("0000 SendMail thread started");
memset(&msg,0,sizeof(msg));
memset(&smb,0,sizeof(smb));
while(server_socket!=INVALID_SOCKET) {
if(startup->options&MAIL_OPT_NO_SENDMAIL) {
mswait(1000);
continue;
}
if(active_sendmail!=0)
active_sendmail=0,update_clients();
smb_close(&smb);
if(sock!=INVALID_SOCKET) {

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);
/* Don't delay on first loop */
if(first_cycle)
first_cycle=FALSE;
else
mswait(3000);

rswindell
committed
sprintf(smb.file,"%smail",scfg.data_dir);
smb.retry_time=scfg.smb_retry_time;
smb.subnum=INVALID_SUB;
if((i=smb_open(&smb))!=0)
continue;
if((i=smb_locksmbhdr(&smb))!=0)
continue;
i=smb_getstatus(&smb);
if(i!=0)
continue;
if(smb.status.last_msg==last_msg && time(NULL)-last_scan<startup->rescan_frequency)
continue;
last_msg=smb.status.last_msg;
last_scan=time(NULL);
total_msgs=smb.status.total_msgs;
smb_rewind(smb.sid_fp);
for(offset=0;offset<total_msgs;offset++) {
if(active_sendmail!=0)
active_sendmail=0,update_clients();
if(server_socket==INVALID_SOCKET) /* server stopped */
break;

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);
smb_fseek(smb.sid_fp, offset*sizeof(msg.idx), SEEK_SET);
if(smb_fread(&msg.idx, sizeof(msg.idx), smb.sid_fp) != sizeof(msg.idx))
break;
if(msg.idx.attr&MSG_DELETE) /* Marked for deletion */
continue;
if(msg.idx.to) /* Local */
continue;
msg.offset=offset;
if((i=smb_lockmsghdr(&smb,&msg))!=0) {
lprintf("0000 !SEND ERROR %d (%s) locking message header #%lu"
,i, smb.last_error, msg.idx.number);
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf("0000 !SEND ERROR %d (%s) reading message header #%lu"
,i, smb.last_error, msg.idx.number);
if(msg.to_net.type!=NET_INTERNET || msg.to_net.addr==NULL)
active_sendmail=1,update_clients();
lprintf("0000 SEND Message #%lu from %s to %s"
,msg.hdr.number, msg.from, msg.to_net.addr);
if(startup->outbound_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->outbound_sound, NULL, SND_ASYNC|SND_FILENAME);
lprintf("0000 SEND getting message text");
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_TAILS))==NULL) {
lprintf("0000 !SEND ERROR (%s) retrieving message text",smb.last_error);
port=0;
if(startup->options&MAIL_OPT_RELAY_TX) {
server=startup->relay_server;
port=startup->relay_port;
} else {
p=strrchr((char*)msg.to_net.addr,':'); /* non-standard SMTP port */
if(p!=NULL) {
*p=0;
port=atoi(p+1);
}
SAFECOPY(to,(char*)msg.to_net.addr);
truncstr(to,"> ");
p=strrchr(to,'@');
if(p==NULL) {
lprintf("0000 !SEND INVALID destination address: %s", to);
sprintf(err,"Invalid destination address: %s", to);
bounce(&smb,&msg,err,TRUE);
continue;
}
if((dns=resolve_ip(startup->dns_server))==INADDR_NONE) {
lprintf("0000 !SEND INVALID DNS server address: %s"
,startup->dns_server);
lprintf("0000 SEND getting MX records for %s from %s",p,startup->dns_server);
if((i=dns_getmx(p, mx, mx2, startup->interface_addr, dns

rswindell
committed
,startup->options&MAIL_OPT_USE_TCP_DNS ? TRUE : FALSE
,TIMEOUT_THREAD_WAIT/2))!=0) {
lprintf("0000 !SEND ERROR %d obtaining MX records for %s from %s"
,i,p,startup->dns_server);
sprintf(err,"Error %d obtaining MX record for %s",i,p);
bounce(&smb,&msg,err,FALSE);
continue;
}
server=mx;
}
if(!port)
port=IPPORT_SMTP;

rswindell
committed
if((sock=mail_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
lprintf("0000 !SEND ERROR %d opening socket", ERROR_VALUE);
continue;
}
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = htonl(startup->interface_addr);
addr.sin_family = AF_INET;
if(startup->seteuid!=NULL)
startup->seteuid(FALSE);
i=bind(sock,(struct sockaddr *)&addr, sizeof(addr));
if(startup->seteuid!=NULL)
startup->seteuid(TRUE);
lprintf("%04d !SEND ERROR %d (%d) binding socket", sock, i, ERROR_VALUE);
continue;
}
strcpy(err,"UNKNOWN ERROR");
success=FALSE;
for(j=0;j<2 && !success;j++) {
if(j) {
if(startup->options&MAIL_OPT_RELAY_TX || !mx2[0])
break;
server=mx2; /* Give second mx record a try */
}
lprintf("%04d SEND resolving SMTP hostname: %s", sock, server);
ip_addr=resolve_ip(server);
if(ip_addr==INADDR_NONE) {
sprintf(err,"Failed to resolve SMTP hostname: %s",server);
continue;
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
lprintf("%04d SEND connecting to port %u on %s [%s]"

rswindell
committed
,ntohs(server_addr.sin_port)
,server,inet_ntoa(server_addr.sin_addr));
if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) {
i=ERROR_VALUE;
lprintf("%04d !SEND ERROR %d connecting to SMTP server: %s"
sprintf(err,"Error %d connecting to SMTP server: %s"