Newer
Older
/* mailsrvr.c */
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <io.h> /* open/close */
#include <share.h> /* share open flags */
#include <process.h> /* _beginthread */
#include <windows.h> /* required for mmsystem.h */
#include <mmsystem.h> /* SND_ASYNC */
#include <stdlib.h> /* ltoa in GNU C lib */
#include <stdarg.h> /* va_list */
#include <string.h> /* strrchr */
#include <ctype.h> /* isdigit */
#include <fcntl.h> /* Open flags */
#include <errno.h> /* errno */
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "mime.h"
#include "md5.h"
#include "base64.h"
#include "ini_file.h"
#define FORWARD "forward:"
#define NO_FORWARD "local:"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
static char* ok_rsp = "250 OK";
static char* auth_ok = "235 User Authenticated";
static char* sys_error = "421 System error";
static char* sys_unavail= "421 System unavailable, try again later";
static char* badarg_rsp = "501 Bad argument";
static char* badseq_rsp = "503 Bad sequence of commands";
static char* badauth_rsp= "535 Authentication failure";
static char* badrsp_err = "%s replied with:\r\n\"%s\"\r\n"
"instead of the expected reply:\r\n\"%s ...\"";
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define DNSBL_THROTTLE_VALUE 5000 /* Milliseconds */
#define STATUS_WFC "Listening"
static mail_startup_t* startup=NULL;
static scfg_t scfg;
static SOCKET server_socket=INVALID_SOCKET;
static SOCKET pop3_socket=INVALID_SOCKET;
static DWORD active_clients=0;
static DWORD thread_count=0;
static BOOL sendmail_running=FALSE;
static DWORD sockets=0;
static DWORD served=0;
static BOOL terminate_server=FALSE;
static BOOL terminate_sendmail=FALSE;
static sem_t sendmail_wakeup_sem;
static char revision[16];
static time_t uptime;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static int mailproc_count;
struct mailproc {
char cmdline[INI_MAX_VALUE_LEN];
str_list_t to;
BOOL passthru;
BOOL native;
} *mailproc_list;
typedef struct {
SOCKET socket;
SOCKADDR_IN client_addr;
} smtp_t,pop3_t;
static int lprintf(int level, char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
if(startup==NULL || startup->lputs==NULL)
return(0);
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
return(startup->lputs(startup->cbdata,level,sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define SOCKLIB_DESC NULL
#endif
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,active_clients+active_sendmail);
static void client_on(SOCKET sock, client_t* client, BOOL update)
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
thread_count++;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE,setuid);
}
static void thread_down(void)
{
if(thread_count>0)
thread_count--;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);

rswindell
committed
SOCKET mail_open_socket(int type)
char error[256];
SOCKET sock;
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
if(set_socket_options(&scfg, sock,error))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
sockets++;
#if 0 /*def _DEBUG */
lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,sockets);
#endif
}
return(sock);
}

rswindell
committed
int mail_close_socket(SOCKET sock)
if(sock==INVALID_SOCKET)
return(-1);
shutdown(sock,SHUT_RDWR); /* required on Unix */
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_ERR,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,sockets);
#endif
return(result);
}
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
int sockprintf(SOCKET sock, char *fmt, ...)
int maxlen;
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
va_end(argptr);
if(len<0 || len > maxlen) /* format error or output truncated */
len=maxlen;
if(startup->options&MAIL_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d TX: %.*s", sock, len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
return(0);
}
/* Check socket for writability (using select) */
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
lprintf(LOG_NOTICE,"%04d !TIMEOUT selecting socket for send"
lprintf(LOG_NOTICE,"%04d !ERROR %d selecting socket for send"
,sock, ERROR_VALUE);
return(0);
}
while((result=sendsocket(sock,sbuf,len))!=len) {
YIELD();
lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
lprintf(LOG_NOTICE,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
lprintf(LOG_WARNING,"%04d !ERROR: short send on socket: %d instead of %d",sock,result,len);
}
return(len);
}
static time_t checktime(void)
{
struct tm tm;
memset(&tm,0,sizeof(tm));
tm.tm_year=94;
tm.tm_mday=1;

rswindell
committed
return(mktime(&tm)-0x2D24BD00L);

rswindell
committed
static void recverror(SOCKET socket, int rd, int line)
lprintf(LOG_NOTICE,"%04d Socket closed by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %d)"

rswindell
committed
,socket, line);
lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %d)"

rswindell
committed
,socket, ERROR_VALUE, line);
lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %d)"

rswindell
committed
,socket, rd, line);

rswindell
committed
static int sockreadline(SOCKET socket, char* buf, int len)
{
char ch;
int i,rd=0;

rswindell
committed
fd_set socket_set;
struct timeval tv;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockreadline");
return(-1);

rswindell
committed
if(server_socket==INVALID_SOCKET || terminate_server) {
lprintf(LOG_WARNING,"%04d !ABORTING sockreadline",socket);
tv.tv_sec=startup->max_inactivity;

rswindell
committed
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(socket,&socket_set);
i=select(socket+1,&socket_set,NULL,NULL,&tv);

rswindell
committed
if(i==0) {
if((time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d !SOCKET INACTIVE",socket);
return(-1);

rswindell
committed
recverror(socket,i,__LINE__);
return(-1);

rswindell
committed
}
i=recv(socket, &ch, 1, 0);
if(i<1) {
recverror(socket,i,__LINE__);
return(-1);
if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
break;
}
buf[rd++]=ch;
}
if(rd>0 && buf[rd-1]=='\r')
rd--;
buf[rd]=0;
return(rd);
}
static BOOL sockgetrsp(SOCKET socket, char* rsp, char *buf, int len)
{
int rd;
while(1) {
rd = sockreadline(socket, buf, len);
if(rd<1)
return(FALSE);
if(buf[3]=='-') { /* Multi-line response */
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d !INVALID RESPONSE: '%s' Expected: '%s'", socket, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
return(TRUE);
}
#define MAX_LINE_LEN 1000
static ulong sockmsgtxt(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxlines)
char toaddr[256]="";
char fromaddr[256]="";
char fromhost[256];
char filepath[MAX_PATH+1]="";
char* boundary=NULL;
char* content_type=NULL;
/* HEADERS (in recommended order per RFC822 4.1) */
if(!sockprintf(socket,"Date: %s",msgdate(msg->hdr.when_written,date)))
return(0);
if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
s=sockprintf(socket,"From: %s",p); /* use original RFC822 header field */
else {
if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
SAFEPRINTF2(fromaddr,"%s!%s"
,(char*)msg->from_net.addr
,usermailaddr(&scfg,fromhost,msg->from));
else if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL)
SAFECOPY(fromaddr,smb_faddrtoa((faddr_t *)msg->from_net.addr,NULL));
else if(msg->from_net.type!=NET_NONE && msg->from_net.addr!=NULL)
SAFECOPY(fromaddr,(char*)msg->from_net.addr);
else
usermailaddr(&scfg,fromaddr,msg->from);
if(fromaddr[0]=='<')
s=sockprintf(socket,"From: \"%s\" %s",msg->from,fromaddr);
else
s=sockprintf(socket,"From: \"%s\" <%s>",msg->from,fromaddr);
}
if(!s)
return(0);
if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
if(!sockprintf(socket,"Organization: %s"
,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
return(0);
if(!sockprintf(socket,"Subject: %s",msg->subj))
return(0);
if((p=smb_get_hfield(msg,RFC822TO,NULL))!=NULL)
s=sockprintf(socket,"To: %s",p); /* use original RFC822 header field */
else {
if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
s=sockprintf(socket,"To: %s",msg->to); /* Avoid double-@ */
else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
if(strchr((char*)msg->to_net.addr,'<')!=NULL)
s=sockprintf(socket,"To: %s",(char*)msg->to_net.addr);
else
s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
} else {
usermailaddr(&scfg,toaddr,msg->to);
s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(!s)
return(0);
if((p=smb_get_hfield(msg,RFC822REPLYTO,NULL))==NULL) {
if(msg->replyto_net.type==NET_INTERNET)
else if(msg->replyto!=NULL)
p=msg->replyto;
}
if(p!=NULL)
s=sockprintf(socket,"Reply-To: %s",p); /* use original RFC822 header field */
if(!s)
return(0);
if(!sockprintf(socket,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg)))
if(msg->reply_id!=NULL)
if(!sockprintf(socket,"In-Reply-To: %s",msg->reply_id))
return(0);
for(i=0;i<msg->total_hfields;i++) {
if(msg->hfield[i].type==RFC822HEADER) {
if(strnicmp((char*)msg->hfield_dat[i],"Content-Type:",13)==0)
content_type=msg->hfield_dat[i];
if(!sockprintf(socket,"%s",(char*)msg->hfield_dat[i]))
return(0);
} else if(msg->hdr.auxattr&MSG_FILEATTACH && msg->hfield[i].type==FILEATTACH)
strncpy(filepath,(char*)msg->hfield_dat[i],sizeof(filepath)-1);
}
/* Default MIME Content-Type for non-Internet messages */
if(msg->from_net.type!=NET_INTERNET && content_type==NULL) {
/* No content-type specified, so assume IBM code-page 437 (full ex-ASCII) */
sockprintf(socket,"Content-Type: text/plain; charset=IBM437");
sockprintf(socket,"Content-Transfer-Encoding: 8bit");
}
if(msg->hdr.auxattr&MSG_FILEATTACH) {
if(filepath[0]==0) { /* filename stored in subject */
if(msg->idx.to!=0)
snprintf(filepath,sizeof(filepath)-1,"%sfile/%04u.in/%s"
,scfg.data_dir,msg->idx.to,msg->subj);
else
snprintf(filepath,sizeof(filepath)-1,"%sfile/%04u.out/%s"
,scfg.data_dir,msg->idx.from,msg->subj);
}
boundary=mimegetboundary();
mimeheaders(socket,boundary);
sockprintf(socket,"");
mimeblurb(socket,boundary);
sockprintf(socket,"");
mimetextpartheader(socket,boundary);
}
if(!sockprintf(socket,"")) /* Header Terminator */
return(0);
#if 0 /* This is now handled in smb_getmsgtxt() */
for(i=0;i<msg->total_hfields;i++) { /* delivery failure notification? */
if(msg->hfield[i].type==SMTPSYSMSG || msg->hfield[i].type==SMB_COMMENT) {
if(!sockprintf(socket,"%s",(char*)msg->hfield_dat[i]))
return(0);
lines++;
}
}
#endif
while(*p && lines<maxlines) {
tp=strchr(p,'\n');
if(tp) {
if(tp-p>MAX_LINE_LEN)
tp=p+MAX_LINE_LEN;
*tp=0;
}
truncsp(p); /* Takes care of '\r' or spaces */
if(*p=='.')
i=sockprintf(socket,".%.*s",MAX_LINE_LEN,p);
else
i=sockprintf(socket,"%.*s",MAX_LINE_LEN,p);
if(!i)
break;
if(tp==NULL)
break;
p=tp+1;
/* release time-slices every x lines */
if(startup->lines_per_yield
&& !(lines%startup->lines_per_yield))
YIELD();
if(msg->hdr.auxattr&MSG_FILEATTACH) {
sockprintf(socket,"");
lprintf(LOG_INFO,"%04u MIME Encoding and sending %s",socket,filepath);
if(!mimeattach(socket,boundary,filepath))
lprintf(LOG_ERR,"%04u !ERROR opening/encoding/sending %s",socket,filepath);
else {
endmime(socket,boundary);
if(msg->hdr.auxattr&MSG_KILLFILE)
if(remove(filepath)!=0)
lprintf(LOG_WARNING,"%04u !ERROR %d removing %s",socket,errno,filepath);
}
sockprintf(socket,"."); /* End of text */
if(boundary) FREE(boundary);
static u_long resolve_ip(char *inaddr)
char* p;
char* addr;
char buf[128];
HOSTENT* host;
SAFECOPY(buf,inaddr);
addr=buf;
if(*addr=='[' && *(p=lastchar(addr))==']') { /* Support [ip_address] notation */
addr++;
*p=0;
}
if(*addr==0)
return((u_long)INADDR_NONE);
for(p=addr;*p;p++)
if(*p!='.' && !isdigit(*p))
break;
if(!(*p))
if((host=gethostbyname(inaddr))==NULL) {
lprintf(LOG_WARNING,"0000 !ERROR resolving hostname: %s",inaddr);
return((u_long)INADDR_NONE);
}
return(*((ulong*)host->h_addr_list[0]));
}
static void pop3_thread(void* arg)
{
char* p;
char str[128];
char buf[512];
char host_name[128];
char host_ip[64];
char username[LEN_ALIAS+1];
char password[LEN_PASS+1];
char challenge[256];
char digest[MD5_DIGEST_SIZE];
char* response="";
char* msgtxt;
int i;
int rd;
BOOL activity=FALSE;
long l;
long msgs,msgnum;
ulong bytes;
SOCKET socket;
HOSTENT* host;
smb_t smb;
smbmsg_t msg;
user_t user;
client_t client;
mail_t* mail;
pop3_t pop3=*(pop3_t*)arg;
thread_up(TRUE /* setuid */);
free(arg);
socket=pop3.socket;
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 session thread started", socket);
if(startup->pop3_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME);
SAFECOPY(host_ip,inet_ntoa(pop3.client_addr.sin_addr));
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 connection accepted from: %s port %u"
,socket, host_ip, ntohs(pop3.client_addr.sin_port));
if(startup->options&MAIL_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr((char *)&pop3.client_addr.sin_addr
,sizeof(pop3.client_addr.sin_addr),AF_INET);
if(host!=NULL && host->h_name!=NULL)
else
strcpy(host_name,"<no name>");
if(startup->options&MAIL_OPT_DEBUG_POP3
&& !(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name);
for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
lprintf(LOG_INFO,"%04d POP3 HostAlias: %s", socket, host->h_aliases[i]);
if(trashcan(&scfg,host_ip,"ip")) {
lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT IP ADDRESS: %s"
,socket, host_ip);
sockprintf(socket,"-ERR Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT HOSTNAME: %s"
,socket, host_name);
sockprintf(socket,"-ERR Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
active_clients++, update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.port=ntohs(pop3.client_addr.sin_port);
client.protocol="POP3";
client.user="<unknown>";
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF(str,"POP3: %s", host_ip);
do {
memset(&smb,0,sizeof(smb));
memset(&msg,0,sizeof(msg));

rswindell
committed
memset(&user,0,sizeof(user));
password[0]=0;
srand(time(NULL)); /* seed random number generator */
rand(); /* throw-away first result */
safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>"
,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
sockprintf(socket,"+OK Synchronet POP3 Server %s-%s Ready %s"
,revision,PLATFORM_DESC,challenge);
/* Requires USER command first */
for(i=3;i;i--) {
if(!sockgetrsp(socket,NULL,buf,sizeof(buf)))
break;
if(!strnicmp(buf,"USER ",5))
break;
if(!strnicmp(buf,"APOP ",5)) {
apop=TRUE;
break;
}
sockprintf(socket,"-ERR USER or APOP command expected");
if(!i || buf[0]==0) /* no USER or APOP command received */
break;
SKIP_WHITESPACE(p);
if(apop) {
if((response=strrchr(p,' '))!=NULL)
*(response++)=0;
else
response=p;
}
if(!apop) {
sockprintf(socket,"+OK");
if(!sockgetrsp(socket,"PASS ",buf,sizeof(buf))) {
sockprintf(socket,"-ERR PASS command expected");
break;
}
p=buf+5;
SKIP_WHITESPACE(p);
user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/);

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s (password: %s)"

rswindell
committed
,socket, username, password);
else
lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s"

rswindell
committed
,socket, username);
break;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d getting data on user (%s)"
break;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !POP3 DELETED or INACTIVE user #%u (%s)"
,socket, user.number, username);
if(apop) {
strlwr(user.pass); /* this is case-sensitive, so convert to lowercase */
strcat(challenge,user.pass);
MD5_calc(digest,challenge,strlen(challenge));
MD5_hex(str,digest);
if(strcmp(str,response)) {
lprintf(LOG_WARNING,"%04d !POP3 %s FAILED APOP authentication"
lprintf(LOG_DEBUG,"%04d !POP3 digest data: %s",socket,challenge);
lprintf(LOG_DEBUG,"%04d !POP3 calc digest: %s",socket,str);
lprintf(LOG_DEBUG,"%04d !POP3 resp digest: %s",socket,response);
#endif
sockprintf(socket,pop_err);
break;
}
} else if(stricmp(password,user.pass)) {

rswindell
committed
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s: '%s' expected '%s'"

rswindell
committed
,socket, username, password, user.pass);
else
lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s"

rswindell
committed
,socket, username);
putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
/* Update client display */
client.user=user.alias;
client_on(socket,&client,TRUE /* update */);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 %s logged in %s", socket, user.alias, apop ? "via APOP":"");
SAFEPRINTF(str,"POP3: %s",user.alias);
sprintf(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d !POP3 MAIL BASE LOCKED: %s",socket,smb.last_error);
sockprintf(socket,"-ERR database locked, try again later");
break;
}
smb.retry_time=scfg.smb_retry_time;
smb.subnum=INVALID_SUB;
if((i=smb_open(&smb))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) opening %s",socket,i,smb.last_error,smb.file);
sockprintf(socket,"-ERR %d opening %s",i,smb.file);
break;
}
mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,0);
for(l=bytes=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
bytes+=smb_getmsgtxtlen(&msg);
if(l<msgs) {
sockprintf(socket,"-ERR message #%d: %d (%s)"
,mail[l].number,i,smb.last_error);
break;
}
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
while(1) { /* TRANSACTION STATE */
rd = sockreadline(socket, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 RX: %s", socket, buf);
if(!stricmp(buf, "NOOP")) {
sockprintf(socket,"+OK");
continue;
}
if(!stricmp(buf, "QUIT")) {
sockprintf(socket,"+OK");
break;
}
if(!stricmp(buf, "STAT")) {
sockprintf(socket,"+OK %lu %lu",msgs,bytes);
continue;
}
if(!stricmp(buf, "RSET")) {
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
msg.hdr.attr=mail[l].attr;
msg.idx.attr=msg.hdr.attr;
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) updating message index"
,socket, i, smb.last_error);
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
}
if(l<msgs)
sockprintf(socket,"-ERR %d messages reset (ERROR: %d)",l,i);
else
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
continue;
}
if(!strnicmp(buf, "LIST",4) || !strnicmp(buf,"UIDL",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(isdigit(*p)) {
msgnum=atol(p);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_WARNING,"%04d !POP3 INVALID message #%ld"
,socket, msgnum);
sockprintf(socket,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
sockprintf(socket,"-ERR %d getting message index",i);
break;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_WARNING,"%04d !POP3 ATTEMPT to list DELETED message"
,socket);
sockprintf(socket,"-ERR message deleted");
continue;
}
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d locking message header",i);
continue;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d getting message header",i);
continue;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,"+OK %lu %lu",msgnum,smb_getmsgtxtlen(&msg));
} else /* UIDL */
sockprintf(socket,"+OK %lu %lu",msgnum,msg.hdr.number);
smb_freemsgmem(&msg);
continue;
}
/* List ALL messages */
sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
for(l=0;l<msgs;l++) {
msg.hdr.number=mail[l].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
break;
}
if(msg.idx.attr&MSG_DELETE)
continue;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
break;
}
if(!strnicmp(buf, "LIST",4)) {
sockprintf(socket,"%lu %lu",l+1,smb_getmsgtxtlen(&msg));
} else /* UIDL */
sockprintf(socket,"%lu %lu",l+1,msg.hdr.number);
smb_freemsgmem(&msg);
}
sockprintf(socket,".");
continue;
}
activity=TRUE;
if(!strnicmp(buf, "RETR ",5) || !strnicmp(buf,"TOP ",4)) {
SAFEPRINTF(str,"POP3: %s", user.alias);
status(str);
lines=-1;
p=buf+4;
SKIP_WHITESPACE(p);
msgnum=atol(p);
if(!strnicmp(buf,"TOP ",4)) {
SKIP_DIGIT(p);
SKIP_WHITESPACE(p);
lines=atol(p);
}
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_WARNING,"%04d !POP3 %s ATTEMPTED to retrieve an INVALID message #%ld"
,socket, user.alias, msgnum);
sockprintf(socket,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d POP3 %s retrieving message #%ld"
,socket, user.alias, msg.hdr.number);
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
sockprintf(socket,"-ERR %d getting message index",i);
continue;
}
if(msg.idx.attr&MSG_DELETE) {
lprintf(LOG_WARNING,"%04d !POP3 ATTEMPT to retrieve DELETED message"
,socket);
sockprintf(socket,"-ERR message deleted");
continue;
}
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d locking message header",i);
continue;
}
i=smb_getmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
if(i!=0) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d getting message header",i);
continue;
}
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
lprintf(LOG_ERR,"%04d !POP3 ERROR (%s) retrieving message %lu text"
,socket, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR retrieving message text");
continue;
}
sockprintf(socket,"+OK message follows");
lprintf(LOG_DEBUG,"%04d POP3 sending message text (%u bytes)"
lines_sent=sockmsgtxt(socket,&msg,msgtxt,lines);
/* if(startup->options&MAIL_OPT_DEBUG_POP3) */
if(lines!=-1 && lines_sent<lines) /* could send *more* lines */
lprintf(LOG_ERR,"%04d !POP3 ERROR sending message text (sent %ld of %ld lines)"
,socket,lines_sent,lines);
lprintf(LOG_DEBUG,"%04d POP3 message transfer complete (%lu lines)"
,socket,lines_sent);
msg.hdr.attr|=MSG_READ;
msg.idx.attr=msg.hdr.attr;
msg.hdr.netattr|=MSG_SENT;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) marking message #%lu as read"
,socket, i, smb.last_error, msg.hdr.number);
smb_unlockmsghdr(&smb,&msg);
}
smb_freemsgmem(&msg);
smb_freemsgtxt(msgtxt);
continue;
}
if(!strnicmp(buf, "DELE ",5)) {
p=buf+5;
SKIP_WHITESPACE(p);
msgnum=atol(p);
if(msgnum<1 || msgnum>msgs) {
lprintf(LOG_WARNING,"%04d !POP3 %s ATTEMPTED to delete an INVALID message #%ld"
,socket, user.alias, msgnum);
sockprintf(socket,"-ERR no such message");
continue;
}
msg.hdr.number=mail[msgnum-1].number;
lprintf(LOG_INFO,"%04d POP3 %s deleting message #%ld"
,socket, user.alias, msg.hdr.number);
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
,socket, i, smb.last_error);
sockprintf(socket,"-ERR %d getting message index",i);
continue;
}
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d locking message header",i);
continue;
}
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
,socket, i, smb.last_error, msg.hdr.number);
sockprintf(socket,"-ERR %d getting message header",i);
continue;
}
msg.hdr.attr|=MSG_DELETE;
msg.idx.attr=msg.hdr.attr;
if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) marking message as read"
, socket, i, smb.last_error);
sockprintf(socket,"-ERR %d marking message for deletion",i);
continue;
}
if(msg.hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,&msg);
smb_unlockmsghdr(&smb,&msg);
smb_freemsgmem(&msg);
sockprintf(socket,"+OK");
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 message deleted", socket);
lprintf(LOG_WARNING,"%04d !POP3 UNSUPPORTED COMMAND from %s: '%s'"
sockprintf(socket,"-ERR UNSUPPORTED COMMAND: %s",buf);
}
if(user.number)

rswindell
committed
logoutuserdat(&scfg,&user,time(NULL),client.time);
if(activity)
lprintf(LOG_INFO,"%04d POP3 %s logged out from port %u on %s [%s]"
,socket, user.alias, ntohs(pop3.client_addr.sin_port), host_name, host_ip);
status(STATUS_WFC);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
smb_freemsgmem(&msg);
smb_close(&smb);
if(active_clients)
active_clients--, update_clients();
client_off(socket);
thread_down();
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 session thread terminated (%u threads remain, %lu clients served)"
,socket, thread_count, served);
/* Must be last */
mail_close_socket(socket);
static ulong rblchk(SOCKET sock, DWORD mail_addr_n, const char* rbl_addr)
{
char name[256];
DWORD mail_addr;
HOSTENT* host;
struct in_addr dnsbl_result;
mail_addr=ntohl(mail_addr_n);
safe_snprintf(name,sizeof(name),"%ld.%ld.%ld.%ld.%.128s"
,mail_addr&0xff
,(mail_addr>>8)&0xff
,(mail_addr>>16)&0xff
,(mail_addr>>24)&0xff
,rbl_addr
);
if(startup->options&MAIL_OPT_DNSBL_DEBUG)
lprintf(LOG_DEBUG,"%04d SMTP DNSBL Query: %s",sock,name);
if((host=gethostbyname(name))==NULL)
return(0);
dnsbl_result.s_addr = *((ulong*)host->h_addr_list[0]);
lprintf(LOG_INFO,"%04d SMTP DNSBL Query: %s resolved to: %s"
,sock,name,inet_ntoa(dnsbl_result));
return(dnsbl_result.s_addr);
static ulong dns_blacklisted(SOCKET sock, IN_ADDR addr, char* host_name, char* list, char* dnsbl_ip)
{
char fname[MAX_PATH+1];
char str[256];
char* p;
char* tp;
FILE* fp;
ulong found=0;
sprintf(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
if(findstr(inet_ntoa(addr),fname))
return(FALSE);
if(findstr(host_name,fname))
return(FALSE);
sprintf(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir);
if((fp=fopen(fname,"r"))==NULL)
return(FALSE);
while(!feof(fp) && !found) {
if(fgets(str,sizeof(str),fp)==NULL)
break;
truncsp(str);
p=str;
SKIP_WHITESPACE(p);
if(*p==';' || *p==0) /* comment or blank line */
continue;
sprintf(list,"%.100s",p);
/* terminate */
tp = p;
FIND_WHITESPACE(tp);
*tp=0;
found = rblchk(sock, addr.s_addr, p);
}
fclose(fp);
if(found)
strcpy(dnsbl_ip, inet_ntoa(addr));
return(found);
}
static BOOL chk_email_addr(SOCKET socket, char* p, char* host_name, char* host_ip
,char* to, char* from)
{
char addr[64];
char tmp[128];
SKIP_WHITESPACE(p);
truncstr(addr,">( ");
if(!trashcan(&scfg,addr,"email"))
return(TRUE);
lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SOURCE: %s"
SAFEPRINTF(tmp,"Blocked source e-mail address: %s", addr);
spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, to, from);
sockprintf(socket, "554 Sender not allowed.");
return(FALSE);
}
{
int file;
if(scfg.smtpmail_sem[0]==0)
return; /* do nothing */
if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC))!=-1)
close(file);
}
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments */
/*****************************************************************************/
static char* mailcmdstr(char* instr, char* msgpath, char* lstpath, char* errpath
,char* host, char* ip, uint usernum
,char* sender, char* sender_addr, char* reverse_path, char* cmd)
{
char str[1024];
int i,j,len;
len=strlen(instr);
for(i=j=0;i<len;i++) {
if(instr[i]=='%') {
i++;
cmd[j]=0;
switch(toupper(instr[i])) {
case 'E':
strcat(cmd,errpath);
break;
case 'H':
strcat(cmd,host);
break;
case 'I':
strcat(cmd,ip);
break;
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
case 'G': /* Temp directory */
strcat(cmd,scfg.temp_dir);
break;
case 'J':
strcat(cmd,scfg.data_dir);
break;
case 'K':
strcat(cmd,scfg.ctrl_dir);
break;
case 'L':
strcat(cmd,lstpath);
break;
case 'F':
case 'M':
strcat(cmd,msgpath);
break;
case 'O': /* SysOp */
strcat(cmd,scfg.sys_op);
break;
case 'Q': /* QWK ID */
strcat(cmd,scfg.sys_id);
break;
case 'R': /* reverse path */
strcat(cmd,reverse_path);
break;
case 'S': /* sender name */
strcat(cmd,sender);
break;
case 'A': /* sender address */
strcat(cmd,sender_addr);
break;
case 'V': /* Synchronet Version */
sprintf(str,"%s%c",VERSION,REVISION);
strcat(cmd,str);
break;
case 'Z':
strcat(cmd,scfg.text_dir);
break;
case '!': /* EXEC Directory */
strcat(cmd,scfg.exec_dir);
break;
case '%': /* %% for percent sign */
strcat(cmd,"%");
break;
case '?': /* Platform */
#ifdef __OS2__
SAFECOPY(str,"OS2");
#else
SAFECOPY(str,PLATFORM_DESC);
#endif
strlwr(str);
strcat(cmd,str);
break;
case 'U': /* User number */
sprintf(str,"%u",usernum);
strcat(cmd,str);
break;
default: /* unknown specification */
break;
}
j=strlen(cmd);
}
else
cmd[j++]=instr[i];
}
cmd[j]=0;
return(cmd);
}
#ifdef JAVASCRIPT
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
char line[64];
char file[MAX_PATH+1];
char* warning;
SOCKET* sock;
if((sock=(SOCKET*)JS_GetContextPrivate(cx))==NULL)
return;
if(report==NULL) {
lprintf(LOG_ERR,"!JavaScript: %s", message);
return;
}
if(report->filename)
SAFEPRINTF(file," %s",report->filename);
else
file[0]=0;
if(report->lineno)
SAFEPRINTF(line," line %u",report->lineno);
else
line[0]=0;
if(JSREPORT_IS_WARNING(report->flags)) {
if(JSREPORT_IS_STRICT(report->flags))
warning="strict warning";
else
warning="warning";
} else
warning="";
lprintf(LOG_ERR,"%04d !JavaScript %s%s%s: %s"
,*sock, warning ,file, line, message);
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i=0;
int32 level=LOG_INFO;
JSString* str=NULL;
SOCKET* sock;
if((sock=(SOCKET*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(JSVAL_IS_NUMBER(argv[i]))
JS_ValueToInt32(cx,argv[i++],&level);
for(; i<argc; i++) {
if((str=JS_ValueToString(cx, argv[i]))==NULL)
return(JS_FALSE);
lprintf(level,"%04d JavaScript: %s",*sock,JS_GetStringBytes(str));
}
if(str==NULL)
*rval = JSVAL_VOID;
else
*rval = STRING_TO_JSVAL(str);
return(JS_TRUE);
}
static JSFunctionSpec js_global_functions[] = {
{"write", js_log, 0},
{"writeln", js_log, 0},
{"print", js_log, 0},
{"log", js_log, 0},
{0}
};
static BOOL
js_mailproc(SOCKET sock, client_t* client, user_t* user
,char* cmdline
,char* msgtxt_fname, char* rcptlst_fname, char* proc_err_fname
,char* sender, char* sender_addr, char* reverse_path)
{
char* p;
char fname[MAX_PATH+1];
char path[MAX_PATH+1];
char arg[MAX_PATH+1];
BOOL success=FALSE;
JSRuntime* js_runtime=NULL;
JSContext* js_cx=NULL;
JSObject* js_glob;
JSObject* argv;
jsuint argc;
JSScript* js_script;
js_branch_t js_branch;
jsval val;
jsval rval=JSVAL_VOID;
SAFECOPY(fname,cmdline);
truncstr(fname," \t");
if(getfext(fname)==NULL) /* No extension specified, assume '.js' */
strcat(fname,".js");
SAFECOPY(path,fname);
if(getfname(path)==path) /* No path specified, assume exec_dir */
sprintf(path,"%s%s",scfg.exec_dir,fname);
do {
lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes\n"
,sock, startup->js.max_bytes);
if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL)
break;
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)\n"
,sock, startup->js.cx_stack);
if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
break;
JS_SetErrorReporter(js_cx, js_ErrorReporter);
JS_SetContextPrivate(js_cx, &sock);
/* Global Object */
if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL)
break;
if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
break;
/* Internal JS Object */
if(js_CreateInternalJsObject(js_cx, js_glob, &js_branch)==NULL)
break;
/* Client Object */
if(js_CreateClientObject(js_cx, js_glob, "client", client, sock)==NULL)
break;
/* System Object */
if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL)
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
break;
/* Socket Class */
if(js_CreateSocketClass(js_cx, js_glob)==NULL)
break;
/* MsgBase Class */
if(js_CreateMsgBaseClass(js_cx, js_glob, &scfg)==NULL)
break;
/* File Class */
if(js_CreateFileClass(js_cx, js_glob)==NULL)
break;
/* User class */
if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
break;
/* Area and "user" Objects */
if(!js_CreateUserObjects(js_cx, js_glob, &scfg, user, NULL, NULL))
break;
/* Convert command-line to argv/argc */
argv=JS_NewArrayObject(js_cx, 0, NULL);
JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
p=cmdline;
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
for(argc=0;*p;argc++) {
SAFECOPY(arg,p);
truncstr(arg," \t");
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,arg));
if(!JS_SetElement(js_cx, argv, argc, &val))
break;
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
}
JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
/* Mailproc "API" filenames */
JS_DefineProperty(js_cx, js_glob, "message_text_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,msgtxt_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(js_cx, js_glob, "recipient_list_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,rcptlst_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(js_cx, js_glob, "processing_error_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,proc_err_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(js_cx, js_glob, "sender_name"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,sender))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(js_cx, js_glob, "sender_address"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,sender_addr))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(js_cx, js_glob, "reverse_path"
,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,reverse_path))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
if((js_script=JS_CompileFile(js_cx, js_glob, path))==NULL)
break;
success=JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
} while(0);
if(js_cx!=NULL)
JS_DestroyContext(js_cx);
if(js_runtime!=NULL)
JS_DestroyRuntime(js_runtime);
return(success);
}
#endif
static int parse_header_field(uchar* buf, smbmsg_t* msg, ushort* type)
{
char* p;
char field[128];
int len;
ushort nettype;
if(buf[0]<=' ' && *type!=UNKNOWN) { /* folded header, append to previous */
p=buf;
truncsp(p);
smb_hfield_append_str(msg,*type,"\r\n");
return smb_hfield_append_str(msg, *type, p);
}
if((p=strchr(buf,':'))==NULL)
return smb_hfield_str(msg, *type=RFC822HEADER, buf);
len=(ulong)p-(ulong)buf;
if(len>sizeof(field)-1)
len=sizeof(field)-1;
sprintf(field,"%.*s",len,buf);
truncsp(field);
p++; /* skip colon */
SKIP_WHITESPACE(p);
truncsp(p);
if(!stricmp(field, "TO"))
return smb_hfield_str(msg, *type=RFC822TO, p);
if(!stricmp(field, "REPLY-TO")) {
smb_hfield_str(msg, *type=RFC822REPLYTO, p);
if(*p=='<') {
p++;
truncstr(p,">");
}
nettype=NET_INTERNET;
smb_hfield(msg, REPLYTONETTYPE, sizeof(nettype), &nettype);
return smb_hfield_str(msg, *type=REPLYTONETADDR, p);
}
if(!stricmp(field, "FROM"))
return smb_hfield_str(msg, *type=RFC822FROM, p);
if(!stricmp(field, "ORGANIZATION"))
return smb_hfield_str(msg, *type=SENDERORG, p);
if(!stricmp(field, "DATE")) {
msg->hdr.when_written=rfc822date(p);
*type=UNKNOWN;
}
if(!stricmp(field, "MESSAGE-ID"))
return smb_hfield_str(msg, *type=RFC822MSGID, p);
if(!stricmp(field, "IN-REPLY-TO"))
return smb_hfield_str(msg, *type=RFC822REPLYID, p);
/* Fall-through */
return smb_hfield_str(msg, *type=RFC822HEADER, buf);
}
static int chk_received_hdr(SOCKET socket,const char *buf,IN_ADDR *dnsbl_result, char *dnsbl, char *dnsbl_ip)
{
char host_name[128];
IN_ADDR check_addr;
char *fromstr;
char ip[16];
char *p;
char *p2;
fromstr=(char *)MALLOC(strlen(buf)+1);
if(fromstr==NULL)
return(0);
strcpy(fromstr,buf);
strlwr(fromstr);
do {
p=strstr(fromstr,"from ");
if(p==NULL)
break;
p+=4;
SKIP_WHITESPACE(p);
if(*p==0)
break;
p2=host_name;
for(;*p && !isspace(*p) && p2<host_name+126;p++) {
*p2++=*p;
}
*p2=0;
p=strtok(fromstr,"[");
if(p==NULL)
break;
p=strtok(NULL,"]");
if(p==NULL)
break;
strncpy(ip,p,16);
ip[15]=0;
check_addr.s_addr = inet_addr(ip);
if(startup->options&MAIL_OPT_DNSBL_DEBUG)
lprintf(LOG_DEBUG,"%04d DEBUG checking %s (%s)",socket,host_name,ip);
if((dnsbl_result->s_addr=dns_blacklisted(socket,check_addr,host_name,dnsbl,dnsbl_ip))!=0)
lprintf(LOG_WARNING,"%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
,socket, dnsbl, host_name, ip, inet_ntoa(*dnsbl_result));
} while(0);
free(fromstr);
return(dnsbl_result->s_addr);
}
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
static void parse_mail_address(char* p
,char* name, size_t name_len
,char* addr, size_t addr_len)
{
char* tp;
char tmp[128];
SKIP_WHITESPACE(p);
/* Get the address */
if((tp=strchr(p,'<'))!=NULL)
tp++;
else
tp=p;
SKIP_WHITESPACE(tp);
sprintf(addr,"%.*s",addr_len,tp);
truncstr(addr,">( ");
SAFECOPY(tmp,p);
p=tmp;
/* Get the "name" (if possible) */
if((tp=strchr(p,'('))!=NULL) { /* name in parenthesis? */
p=tp+1;
tp=strchr(p,')');
} else if((tp=strchr(p,'"'))!=NULL) { /* name in quotes? */
p=tp+1;
tp=strchr(p,'"');
} else if(*p=='<') { /* address in brackets? */
p++;
tp=strchr(p,'>');
} else /* name, then address in brackets */
tp=strchr(p,'<');
if(tp) *tp=0;
sprintf(name,"%.*s",name_len,p);
truncsp(name);
}
static void smtp_thread(void* arg)
{
int i,j;
char value[INI_MAX_VALUE_LEN];
char** sec_list;
char* section;
char buf[1024],*p,*tp,*cp;
char hdrfield[512];
char alias_buf[128];
char reverse_path[128];
char date[64];
char qwkid[32];
char rcpt_name[128];
char rcpt_addr[128];

rswindell
committed
char sender[128];
char sender_addr[128];
char user_name[LEN_ALIAS+1];
char user_pass[LEN_PASS+1];
char relay_list[MAX_PATH+1];
char domain_list[MAX_PATH+1];
char spam_bait[MAX_PATH+1];
char spam_block[MAX_PATH+1];
char host_name[128];
char host_ip[64];
char dnsbl_ip[64];

rswindell
committed
char* telegram_buf;
char* msgbuf;
char challenge[256];
char response[128];
char secret[64];
char md5_data[384];
char digest[MD5_DIGEST_SIZE];
char dest_host[128];
ushort dest_port;
ushort hfield_type;
ushort agent;
ulong hdr_lines=0;
ulong hdr_len=0;
ulong badcmds=0;

rswindell
committed
BOOL telegram=FALSE;
BOOL forward=FALSE;
BOOL no_forward=FALSE;
BOOL auth_login;
BOOL routed=FALSE;
BOOL dnsbl_recvhdr;
BOOL msg_handled;
uint subnum=INVALID_SUB;
char msgtxt_fname[MAX_PATH+1];
char rcptlst_fname[MAX_PATH+1];
ushort rcpt_count=0;
FILE* proc_err;
char proc_err_fname[MAX_PATH+1];
char session_id[MAX_PATH+1];
FILE* spy=NULL;
SOCKET socket;
HOSTENT* host;
int smb_error;
smb_t smb;
smbmsg_t msg;
smbmsg_t newmsg;
user_t user;
user_t relay_user;

rswindell
committed
node_t node;
client_t client;
smtp_t smtp=*(smtp_t*)arg;
SOCKADDR_IN server_addr;
IN_ADDR dnsbl_result;
BOOL* mailproc_match;
enum {
SMTP_STATE_INITIAL
,SMTP_STATE_HELO
,SMTP_STATE_DATA_HEADER
,SMTP_STATE_DATA_BODY
} state = SMTP_STATE_INITIAL;
enum {
SMTP_CMD_NONE
,SMTP_CMD_MAIL
,SMTP_CMD_SEND
,SMTP_CMD_SOML
,SMTP_CMD_SAML
} cmd = SMTP_CMD_NONE;
thread_up(TRUE /* setuid */);
free(arg);
socket=smtp.socket;
lprintf(LOG_DEBUG,"%04d SMTP RX Session thread started", socket);
if(startup->inbound_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->inbound_sound, NULL, SND_ASYNC|SND_FILENAME);
addr_len=sizeof(server_addr);
if((i=getsockname(socket, (struct sockaddr *)&server_addr,&addr_len))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%d) getting address/port"
sockprintf(socket,sys_error);
mail_close_socket(socket);
thread_down();
if((mailproc_match=alloca(sizeof(BOOL)*mailproc_count))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR allocating memory for mailproc_match", socket);
sockprintf(socket,sys_error);
mail_close_socket(socket);
thread_down();
return;
}
memset(mailproc_match,FALSE,sizeof(BOOL)*mailproc_count);
memset(&smb,0,sizeof(smb));

rswindell
committed
memset(&msg,0,sizeof(msg));
memset(&user,0,sizeof(user));
memset(&relay_user,0,sizeof(relay_user));
SAFECOPY(host_ip,inet_ntoa(smtp.client_addr.sin_addr));
lprintf(LOG_INFO,"%04d SMTP Connection accepted from: %s port %u"
, socket, host_ip, ntohs(smtp.client_addr.sin_port));
if(startup->options&MAIL_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr ((char *)&smtp.client_addr.sin_addr
,sizeof(smtp.client_addr.sin_addr),AF_INET);
if(host!=NULL && host->h_name!=NULL)
else
strcpy(host_name,"<no name>");
if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
lprintf(LOG_INFO,"%04d SMTP Hostname: %s", socket, host_name);
for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
lprintf(LOG_INFO,"%04d SMTP HostAlias: %s", socket, host->h_aliases[i]);
#if 0
if(host!=NULL) {
ip=resolve_ip(host_name);
if(ip!=smtp.client_addr.sin_addr.s_addr) {
smtp.client_addr.sin_addr.s_addr=ip;
lprintf(LOG_WARNING,"%04d !SMTP DNS/IP ADDRESS MISMATCH: %s vs %s"
,socket, inet_ntoa(smtp.client_addr.sin_addr), host_ip);
sockprintf(socket,"550 DNS and IP address mismatch");
mail_close_socket(socket);
thread_down();
return;
}
}
#endif
SAFECOPY(hello_name,host_name);
sprintf(spam_bait,"%sspambait.cfg",scfg.ctrl_dir);
sprintf(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);
if(trashcan(&scfg,host_ip,"ip") || findstr(host_ip,spam_block)) {
lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SERVER IP ADDRESS: %s"
,socket, host_ip);
sockprintf(socket,"550 Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host") || findstr(host_name,spam_block)) {
lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SERVER HOSTNAME: %s"
,socket, host_name);
sockprintf(socket,"550 Access denied.");

rswindell
committed
mail_close_socket(socket);
thread_down();
return;
}
active_clients++, update_clients();
/* SPAM Filters (mail-abuse.org) */
dnsbl_result.s_addr = dns_blacklisted(socket,smtp.client_addr.sin_addr,host_name,dnsbl,dnsbl_ip);
if(dnsbl_result.s_addr) {
lprintf(LOG_WARNING,"%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
,socket, dnsbl, host_name, dnsbl_ip, inet_ntoa(dnsbl_result));
if(startup->options&MAIL_OPT_DNSBL_REFUSE) {
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "SESSION REFUSED", str, host_name, dnsbl_ip, NULL, NULL);
sockprintf(socket
,"550 Mail from %s refused due to listing at %s"
,dnsbl_ip, dnsbl);
mail_close_socket(socket);
lprintf(LOG_WARNING,"%04d !SMTP REFUSED SESSION from blacklisted server"
thread_down();
if(active_clients)
active_clients--, update_clients();
sprintf(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d !SMTP MAIL BASE LOCKED: %s"
,socket, smb.last_error);
sockprintf(socket,sys_unavail);
mail_close_socket(socket);
thread_down();
if(active_clients)
active_clients--, update_clients();
return;
}
srand(time(NULL)); /* seed random number generator */
rand(); /* throw-away first result */
SAFEPRINTF3(session_id,"%x%x%lx",socket,rand(),clock());
SAFEPRINTF2(rcptlst_fname,"%sSBBS_SMTP.%s.lst", scfg.temp_dir, session_id);
rcptlst=fopen(rcptlst_fname,"w+");
if(rcptlst==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating recipient list: %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);

rswindell
committed
mail_close_socket(socket);
if(active_clients)
active_clients--, update_clients();
if(trashcan(&scfg,host_name,"smtpspy")
|| trashcan(&scfg,host_ip,"smtpspy")) {
sprintf(str,"%ssmtpspy.txt", scfg.logs_dir);
spy=fopen(str,"a");
}
/* Initialize client display */
client.size=sizeof(client);
client.time=time(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.port=ntohs(smtp.client_addr.sin_port);
client.protocol="SMTP";
client.user="<unknown>";
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF(str,"SMTP: %s",host_ip);
sockprintf(socket,"220 %s Synchronet SMTP Server %s-%s Ready"
,startup->host_name,revision,PLATFORM_DESC);
while(1) {
rd = sockreadline(socket, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(spy!=NULL)
fprintf(spy,"%s\n",buf);
if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_THROTTLE)
mswait(DNSBL_THROTTLE_VALUE);
if(state>=SMTP_STATE_DATA_HEADER) {
if(!strcmp(buf,".")) {

rswindell
committed
state=SMTP_STATE_HELO; /* RESET state machine here in case of error */

rswindell
committed
if(msgtxt==NULL) {
lprintf(LOG_ERR,"%04d !SMTP NO MESSAGE TEXT FILE POINTER?", socket);
sockprintf(socket,"554 No message text");
continue;
}
if(ftell(msgtxt)<1) {
lprintf(LOG_ERR,"%04d !SMTP INVALID MESSAGE LENGTH: %ld (%lu lines)"
, socket, ftell(msgtxt), lines);
sockprintf(socket,"554 No message text");
lprintf(LOG_INFO,"%04d SMTP End of message (body: %lu lines, %lu bytes, header: %lu lines, %lu bytes)"
, socket, lines, ftell(msgtxt)-hdr_len, hdr_lines, hdr_len);
if(telegram==TRUE) { /* Telegram */
const char* head="\1n\1h\1cInstant Message\1n from \1h\1y";
const char* tail="\1n:\r\n\1h";
rewind(msgtxt);
length=filelength(fileno(msgtxt));
p=strchr(sender_addr,'@');
if(p==NULL || resolve_ip(p+1)!=smtp.client_addr.sin_addr.s_addr)
/* Append real IP and hostname if different */
safe_snprintf(str,sizeof(str),"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s"
,head,sender_addr,host_ip,host_name,tail);
else
safe_snprintf(str,sizeof(str),"%s%s%s",head,sender_addr,tail);
if((telegram_buf=(char*)malloc(length+strlen(str)+1))==NULL) {
lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %lu bytes of memory for telegram from %s"
,socket,length+strlen(str)+1,sender_addr);
sockprintf(socket, "452 Insufficient system storage");
continue;
}
strcpy(telegram_buf,str); /* can't use SAFECOPY here */
if(fread(telegram_buf+strlen(str),1,length,msgtxt)!=length) {
lprintf(LOG_ERR,"%04d !SMTP ERROR reading %lu bytes from telegram file"
,socket,length);
sockprintf(socket, "452 Insufficient system storage");
free(telegram_buf);
continue;
}
telegram_buf[length+strlen(str)]=0; /* Need ASCIIZ */
/* Send telegram to users */
sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */
for(rcpt_count=0; sec_list!=NULL
&& sec_list[rcpt_count]!=NULL
&& rcpt_count<startup->max_recipients; rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value));
usernum=iniReadInteger(rcptlst,section ,smb_hfieldtype(RECIPIENTEXT),0);
SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),rcpt_name,value));
if((i=putsmsg(&scfg,usernum,telegram_buf))==0)
lprintf(LOG_INFO,"%04d SMTP Created telegram (%ld/%u bytes) from %s to %s <%s>"
,socket, length, strlen(telegram_buf), sender_addr, rcpt_name, rcpt_addr);
else
lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating telegram from %s to %s <%s>"
,socket, i, sender_addr, rcpt_name, rcpt_addr);
}
iniFreeStringList(sec_list);
free(telegram_buf);
sockprintf(socket,ok_rsp);
telegram=FALSE;
continue;
}
fclose(msgtxt), msgtxt=NULL;
fclose(rcptlst), rcptlst=NULL;
/* External Mail Processing here */
msg_handled=FALSE;
if(mailproc_count) {
SAFEPRINTF2(proc_err_fname,"%sSBBS_SMTP.%s.err", scfg.temp_dir, session_id);
remove(proc_err_fname);
for(i=0;i<mailproc_count;i++) {
/* This processor is for specific recipients only and did not match */
if(strListCount(mailproc_list[i].to) && !mailproc_match[i])
continue;
if(!mailproc_list[i].passthru)
msg_handled=TRUE;
mailcmdstr(mailproc_list[i].cmdline
,msgtxt_fname, rcptlst_fname, proc_err_fname
,host_name, host_ip, relay_user.number
,sender, sender_addr, reverse_path, str);
lprintf(LOG_DEBUG,"%04d SMTP Executing external process: %s"
,socket, str);
if(mailproc_list[i].native) {
if((j=system(str))!=0)
lprintf(LOG_WARNING,"%04d !SMTP system(%s) returned %d (errno: %d)"
,socket, str, j, errno);
} else /* JavaScript */
js_mailproc(socket, &client, &relay_user, str /* cmdline */
,msgtxt_fname, rcptlst_fname, proc_err_fname
,sender, sender_addr, reverse_path);
if(flength(proc_err_fname)>0)
break;
if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname))
break;
}
if(flength(proc_err_fname)>0
&& (proc_err=fopen(proc_err_fname,"r"))!=NULL) {
while(!feof(proc_err)) {
if(!fgets(str,sizeof(str),proc_err))
break;
truncsp(str);
lprintf(LOG_WARNING,"%04d !SMTP external process error: %s", socket, str);
i=atoi(str);
if(i>=100 && i<1000)
sockprintf(socket,"%s", str);
else
sockprintf(socket,"554%c%s"
,ftell(proc_err)<filelength(fileno(proc_err)) ? '-' : ' '
,str);
}
fclose(proc_err);
msg_handled=TRUE;
}
else if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
lprintf(LOG_WARNING,"%04d SMTP External process removed %s file"
,socket, fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list");
sockprintf(socket,ok_rsp);
msg_handled=TRUE;
}
else if(msg_handled)
sockprintf(socket,ok_rsp);
remove(proc_err_fname); /* Remove error file here */
}
/* Re-open files */
/* We must do this before continuing for handled msgs */
/* to prevent freopen(NULL) and orphaned temp files */
if((rcptlst=fopen(rcptlst_fname,fexist(rcptlst_fname) ? "r":"w+"))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening recipient list: %s"
,socket, errno, rcptlst_fname);
if(!msg_handled)
sockprintf(socket,sys_error);
continue;
}
if(msg_handled) {
lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor"
,socket);
continue;
}
if((msgtxt=fopen(msgtxt_fname,"rb"))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening message file: %s"
,socket, errno, msgtxt_fname);
sockprintf(socket,sys_error);
continue;
}
/* Initialize message header */
smb_freemsgmem(&msg);
memset(&msg,0,sizeof(smbmsg_t));
/* Parse message header here */
hfield_type=UNKNOWN;
smb_error=SMB_SUCCESS; /* no SMB error */
while(!feof(msgtxt)) {
if(!fgets(buf,sizeof(buf),msgtxt))
break;
truncsp(buf);
if(buf[0]==0) /* blank line marks end of header */
break;
if(!strnicmp(buf, "SUBJECT:",8)) {
p=buf+8;
SKIP_WHITESPACE(p);
if(relay_user.number==0 && dnsbl_result.s_addr && startup->dnsbl_tag[0]
&& !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
safe_snprintf(str,sizeof(str),"%.*s: %.*s"
,(int)sizeof(str)/2, startup->dnsbl_tag
,(int)sizeof(str)/2, p);
p=str;
lprintf(LOG_WARNING,"%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
,socket, startup->dnsbl_tag);
}
smb_hfield_str(&msg, SUBJECT, p);
continue;
}
if(!strnicmp(buf, "FROM:", 5)
&& !chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr,reverse_path))
break;
if((smb_error=parse_header_field(buf,&msg,&hfield_type))!=SMB_SUCCESS) {
if(smb_error==SMB_ERR_HDR_LEN)
lprintf(LOG_WARNING,"%04d !SMTP MESSAGE HEADER EXCEEDS %u BYTES"
,socket, SMB_MAX_HDR_LEN);
else
lprintf(LOG_ERR,"%04d !SMTP ERROR %d adding header field: %s"
,socket, smb_error, buf);
break;
}
}
if(smb_error!=SMB_SUCCESS) { /* SMB Error */
sockprintf(socket, "452 Insufficient system storage");
continue;
}
if((p=smb_get_hfield(&msg, RFC822TO, NULL))!=NULL) {
if(*p=='<') p++;
SAFECOPY(rcpt_name,p);
truncstr(rcpt_name,">");
}
if((p=smb_get_hfield(&msg, RFC822FROM, NULL))!=NULL) {
parse_mail_address(p
,sender ,sizeof(sender)-1
,sender_addr,sizeof(sender_addr)-1);
}
/* SPAM Filtering/Logging */
if(msg.subj!=NULL && trashcan(&scfg,msg.subj,"subject")) {
lprintf(LOG_WARNING,"%04d !SMTP BLOCKED SUBJECT (%s) from: %s"
,socket, msg.subj, reverse_path);
SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s"
,msg.subj, reverse_path);
spamlog(&scfg, "SMTP", "REFUSED"
,tmp, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "554 Subject not allowed.");
continue;
}
dnsbl_recvhdr=FALSE;
if(startup->options&MAIL_OPT_DNSBL_CHKRECVHDRS) {
for(i=0;!dnsbl_result.s_addr && i<msg.total_hfields;i++) {
if(msg.hfield[i].type == RFC822HEADER
&& strnicmp(msg.hfield_dat[i],"Received:",9)==0) {
if(chk_received_hdr(socket,msg.hfield_dat[i],&dnsbl_result,dnsbl,dnsbl_ip)) {
dnsbl_recvhdr=TRUE;
break;
}
}
}
if(relay_user.number==0 && dnsbl_result.s_addr) {
if(startup->options&MAIL_OPT_DNSBL_IGNORE) {
lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL from blacklisted server"
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "IGNORED"
,str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
/* pretend we received it */
sockprintf(socket,ok_rsp);
/* tag message as spam */
if(startup->dnsbl_hdr[0]) {
safe_snprintf(str,sizeof(str),"%s: %s is listed on %s as %s"
,startup->dnsbl_hdr, dnsbl_ip
,dnsbl, inet_ntoa(dnsbl_result));
smb_hfield_str(&msg, RFC822HEADER, str);
lprintf(LOG_WARNING,"%04d !SMTP TAGGED MAIL HEADER from blacklisted server with: %s"
,socket, startup->dnsbl_hdr);
}
if(startup->dnsbl_hdr[0] || startup->dnsbl_tag[0]) {
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "TAGGED", str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
if(dnsbl_recvhdr) /* DNSBL-listed IP found in Received header? */
dnsbl_result.s_addr=0; /* Reset DNSBL look-up result between messages */

rswindell
committed
if(sender[0]==0) {
lprintf(LOG_WARNING,"%04d !SMTP MISSING mail header 'FROM' field", socket);

rswindell
committed
sockprintf(socket, "554 Mail header missing 'FROM' field");
subnum=INVALID_SUB;

rswindell
committed
continue;
}
nettype=NET_INTERNET;
smb_hfield_str(&msg, SMTPREVERSEPATH, reverse_path);
smb_hfield_str(&msg, SENDER, sender);

rswindell
committed
smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype);
smb_hfield_str(&msg, SENDERNETADDR, sender_addr);
if(msg.subj==NULL)
smb_hfield(&msg, SUBJECT, 0, NULL);

rswindell
committed
length=filelength(fileno(msgtxt))-ftell(msgtxt);
if(startup->max_msg_size && length>startup->max_msg_size) {
lprintf(LOG_WARNING,"%04d !SMTP message size (%lu) exceeds maximum: %lu bytes"
,socket,length,startup->max_msg_size);
sockprintf(socket, "552 Message size (%lu) exceeds maximum: %lu bytes"
,length,startup->max_msg_size);
subnum=INVALID_SUB;
continue;
}
if((msgbuf=(char*)malloc(length+1))==NULL) {
lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %d bytes of memory"
,socket,length+1);
sockprintf(socket, "452 Insufficient system storage");
subnum=INVALID_SUB;
continue;
}
fread(msgbuf,length,1,msgtxt);
msgbuf[length]=0; /* ASCIIZ */
/* Do external JavaScript processing here? */
if(subnum!=INVALID_SUB) { /* Message Base */
if(relay_user.number==0)
memset(&relay_user,0,sizeof(relay_user));
if(!chk_ar(&scfg,scfg.grp[scfg.sub[subnum]->grp]->ar, &relay_user)
|| !chk_ar(&scfg,scfg.sub[subnum]->ar, &relay_user)
|| !chk_ar(&scfg,scfg.sub[subnum]->post_ar, &relay_user)) {
lprintf(LOG_WARNING,"%04d !SMTP %s has insufficient access to post on %s"
,socket, sender_addr, scfg.sub[subnum]->sname);
sockprintf(socket,"550 Insufficient access");
subnum=INVALID_SUB;
continue;
}
if(rcpt_name[0]==0)
strcpy(rcpt_name,"All");
smb_hfield_str(&msg, RECIPIENT, rcpt_name);
smb.subnum=subnum;
if((i=savemsg(&scfg, &smb, &msg, &client, msgbuf))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !SMTP ERROR %d (%s) saving message"
,socket,i,smb.last_error);
sockprintf(socket, "452 ERROR %d (%s) saving message"
,i,smb.last_error);
} else {
lprintf(LOG_INFO,"%04d SMTP %s posted a message on %s"
,socket, sender_addr, scfg.sub[subnum]->sname);
sockprintf(socket,ok_rsp);
signal_smtp_sem();
}
free(msgbuf);
smb_close(&smb);
subnum=INVALID_SUB;
continue;
}
/* E-mail */
smb.subnum=INVALID_SUB;
i=savemsg(&scfg, &smb, &msg, &client, msgbuf);
free(msgbuf);
if(i!=SMB_SUCCESS) {
smb_close(&smb);
lprintf(LOG_ERR,"%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;
}
sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */
for(rcpt_count=0; sec_list!=NULL
&& sec_list[rcpt_count]!=NULL
&& rcpt_count<startup->max_recipients; rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value));
usernum=iniReadInteger(rcptlst,section ,smb_hfieldtype(RECIPIENTEXT),0);
agent=iniReadShortInt(rcptlst,section ,smb_hfieldtype(RECIPIENTAGENT),AGENT_PERSON);
nettype=iniReadShortInt(rcptlst,section ,smb_hfieldtype(RECIPIENTNETTYPE),NET_NONE);
sprintf(str,"#%u",usernum);
SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),str,value));

rswindell
committed
if(nettype==NET_NONE /* Local destination */ && usernum==0) {
lprintf(LOG_ERR,"%04d !SMTP can't deliver mail to user #0"
,socket);
break;
}
if((i=smb_copymsgmem(&smb,&newmsg,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) copying message"
,socket, i, smb.last_error);
break;
}

rswindell
committed
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));
smb_hfield_str(&newmsg, RFC822HEADER, hdrfield);

rswindell
committed
smb_hfield_str(&newmsg, RECIPIENT, rcpt_name);

rswindell
committed
newmsg.idx.to=usernum;
if(nettype==NET_NONE) { /* Local destination */
sprintf(str,"%u",usernum);
smb_hfield_str(&newmsg, RECIPIENTEXT, str);

rswindell
committed
} else {
if(nettype!=NET_QWK)
newmsg.idx.to=0;
smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(nettype), &nettype);
smb_hfield_str(&newmsg, RECIPIENTNETADDR, rcpt_addr);

rswindell
committed
}
if(agent!=newmsg.to_agent)
smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(agent), &agent);

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

rswindell
committed
break;
lprintf(LOG_INFO,"%04d SMTP Created message #%ld from %s to %s <%s>"

rswindell
committed
,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
if(!(startup->options&MAIL_OPT_NO_NOTIFY) && usernum) {
safe_snprintf(str,sizeof(str)
,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: "

rswindell
committed
"\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);
iniFreeStringList(sec_list);
if(rcpt_count<1) {
smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS);

rswindell
committed
sockprintf(socket, "452 Insufficient system storage");
}
else {
if(rcpt_count>1)
smb_incmsg_dfields(&smb,&msg,(ushort)(rcpt_count-1));
sockprintf(socket,ok_rsp);
signal_smtp_sem();
#if 0 /* This shouldn't be necessary here */
smb_close_da(&smb);
#endif
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))
YIELD();
continue;
}
/* RFC822 Header parsing */
if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
lprintf(LOG_DEBUG,"%04d SMTP %s",socket, buf);
if(!strnicmp(buf, "FROM:", 5))
parse_mail_address(buf+5
,sender, sizeof(sender)-1
,sender_addr, sizeof(sender_addr)-1);
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", buf);
hdr_lines++;
strip_ctrl(buf);
lprintf(LOG_DEBUG,"%04d SMTP RX: %s", socket, buf);
if(!strnicmp(buf,"HELO",4)) {
p=buf+4;
SKIP_WHITESPACE(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;
SKIP_WHITESPACE(p);
sockprintf(socket,"250-%s",startup->host_name);
sockprintf(socket,"250 AUTH PLAIN LOGIN CRAM-MD5");
esmtp=TRUE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
if((auth_login=(stricmp(buf,"AUTH LOGIN")==0))==TRUE
|| stricmp(buf,"AUTH PLAIN")==0) {
if(auth_login)
sockprintf(socket,"334 VXNlcm5hbWU6"); /* Base64-encoded "Username:" */
else
sockprintf(socket,"334 Username:");
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(auth_login) {
if(b64_decode(user_name,sizeof(user_name),buf,rd)<1) {
sockprintf(socket,badarg_rsp);
continue;
}
} else
SAFECOPY(user_name,buf);
if(auth_login)
sockprintf(socket,"334 UGFzc3dvcmQ6"); /* Base64-encoded "Password:" */
else
sockprintf(socket,"334 Password:");
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(auth_login) {
if(b64_decode(user_pass,sizeof(user_pass),buf,rd)<1) {
sockprintf(socket,badarg_rsp);
continue;
}
} else
SAFECOPY(user_pass,buf);
if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s (password: %s)"
,socket, user_name, user_pass);
else
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s"
,socket, user_name);
sockprintf(socket,badauth_rsp);
continue;
}
if((i=getuserdat(&scfg, &relay_user))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user (%s)"
,socket, i, user_name);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
continue;
}
if(relay_user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
,socket, relay_user.number, user_name);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
break;
}
if(stricmp(user_pass,relay_user.pass)) {
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s: '%s' expected '%s'"
,socket, user_name, user_pass, relay_user.pass);
else
lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s"
,socket, user_name);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
break;
}
/* Update client display */
client.user=relay_user.alias;
client_on(socket,&client,TRUE /* update */);
lprintf(LOG_INFO,"%04d SMTP %s authenticated using %s authentication"
,socket,relay_user.alias,auth_login ? "LOGIN" : "PLAIN");
sockprintf(socket,auth_ok);
continue;
}
if(!stricmp(buf,"AUTH CRAM-MD5")) {
safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%s>"
,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
lprintf(LOG_DEBUG,"%04d SMTP CRAM-MD5 challenge: %s"
,socket,challenge);
b64_encode(str,sizeof(str),challenge,0);
sockprintf(socket,"334 %s",str);
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(b64_decode(response,sizeof(response),buf,rd)<1) {
sockprintf(socket,badarg_rsp);
continue;
}
#if 0
lprintf(LOG_DEBUG,"%04d SMTP CRAM-MD5 response: %s"
,socket,response);
if((p=strrchr(response,' '))!=NULL)
*(p++)=0;
else
p=response;
SAFECOPY(user_name,response);
if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s"
,socket, user_name);
sockprintf(socket,badauth_rsp);
continue;
}
if((i=getuserdat(&scfg, &relay_user))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user (%s)"
,socket, i, user_name);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
continue;
}
if(relay_user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
,socket, relay_user.number, user_name);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
continue;
}
/* Calculate correct response */
memset(secret,0,sizeof(secret));
SAFECOPY(secret,relay_user.pass);
strlwr(secret); /* this is case sensitive, so convert to lowercase first */
for(i=0;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x36; /* ipad */
strcpy(md5_data+i,challenge);
MD5_calc(digest,md5_data,sizeof(secret)+strlen(challenge));
for(i=0;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x5c; /* opad */
memcpy(md5_data+i,digest,sizeof(digest));
MD5_calc(digest,md5_data,sizeof(secret)+sizeof(digest));
MD5_hex(str,digest);
if(strcmp(p,str)) {
lprintf(LOG_WARNING,"%04d !SMTP %s FAILED CRAM-MD5 authentication"
,socket,relay_user.alias);
lprintf(LOG_DEBUG,"%04d !SMTP calc digest: %s"
,socket,str);
lprintf(LOG_DEBUG,"%04d !SMTP resp digest: %s"
,socket,p);
sockprintf(socket,badauth_rsp);
relay_user.number=0;
continue;
}
/* Update client display */
client.user=relay_user.alias;
client_on(socket,&client,TRUE /* update */);
lprintf(LOG_INFO,"%04d SMTP %s authenticated using CRAM-MD5 authentication"
,socket,relay_user.alias);
sockprintf(socket,auth_ok);
continue;
}
if(!strnicmp(buf,"AUTH",4)) {
sockprintf(socket,"504 Unrecognized authentication type.");
continue;
}
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(LOG_WARNING,"%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(LOG_ERR,"%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(LOG_INFO,"%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))
SKIP_WHITESPACE(p);
/* Update client display */
if(relay_user.number==0) {
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 */
if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
lprintf(LOG_ERR,"%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;

rswindell
committed
continue;
}
#if 0 /* No one uses this command */
if(!strnicmp(buf,"VRFY",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(*p==0) {
sockprintf(socket,"550 No user specified.");
continue;
}
#endif
/* Add to Recipient list */
if(!strnicmp(buf,"RCPT TO:",8)) {
lprintf(LOG_WARNING,"%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.logs_dir);

rswindell
committed
spy=fopen(str,"a");
}
SKIP_WHITESPACE(p);
p=strrchr(str,'<');
if(p==NULL)
p=str;
else
p++;
truncstr(str,">"); /* was truncating at space too */
routed=FALSE;
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);
}
if(*p==0) {
lprintf(LOG_WARNING,"%04d !SMTP NO RECIPIENT SPECIFIED"
,socket);
sockprintf(socket, "500 No recipient specified");
continue;
}
rcpt_name[0]=0;
/* Check recipient counter */
if(rcpt_count>=startup->max_recipients) {
lprintf(LOG_WARNING,"%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 SPAM bait recipient */
if(findstr(rcpt_addr,spam_bait)) {
SAFEPRINTF(str,"SPAM BAIT (%s) taken", rcpt_addr);
lprintf(LOG_WARNING,"%04d !SMTP %s by: %s"
,socket, str, reverse_path);
strcpy(tmp,"REFUSED");
if(dnsbl_result.s_addr==0) { /* Don't double-filter */
lprintf(LOG_WARNING,"%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block);
filter_ip(&scfg, "SMTP", str, host_name, host_ip, reverse_path, spam_block);
strcat(tmp," and BLOCKED");
spamlog(&scfg, "SMTP", tmp, "Attempted recipient in SPAM BAIT list"
,host_name, host_ip, rcpt_addr, reverse_path);
}
/* Check for blocked recipients */
if(trashcan(&scfg,rcpt_addr,"email")) {
lprintf(LOG_NOTICE,"%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(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_BADUSER) {
lprintf(LOG_WARNING,"%04d !SMTP REFUSED MAIL from blacklisted server"
,socket);
SAFEPRINTF2(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(LOG_INFO,"%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);
if(relay_user.number==0 /* not authenticated, search for IP */
&& startup->options&MAIL_OPT_SMTP_AUTH_VIA_IP) {
relay_user.number=userdatdupe(&scfg, 0, U_NOTE, LEN_NOTE, host_ip, FALSE);
if(relay_user.number) {
getuserdat(&scfg,&relay_user);
if(relay_user.laston < time(NULL)-(60*60)) /* logon in past hour? */
relay_user.number=0;
}
} else
getuserdat(&scfg,&relay_user);
if(p!=alias_buf /* forced relay by alias */ &&
(!(startup->options&MAIL_OPT_ALLOW_RELAY)
|| relay_user.number==0
|| relay_user.rest&(FLAG('G')|FLAG('M'))) &&
!findstr(host_name,relay_list) &&
!findstr(host_ip,relay_list)) {
lprintf(LOG_WARNING,"%04d !SMTP ILLEGAL RELAY ATTEMPT from %s [%s] to %s"
,socket, reverse_path, host_ip, p);
SAFEPRINTF(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 before sending.");
else
sockprintf(socket, "550 Relay not allowed.");
if(relay_user.number==0)
SAFECOPY(relay_user.alias,"Unknown User");
lprintf(LOG_INFO,"%04d SMTP %s relaying to external mail service: %s"
,socket, relay_user.alias, tp+1);
fprintf(rcptlst,"[%u]\n",rcpt_count++);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_INTERNET);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),p);
sockprintf(socket,ok_rsp);
tp=strchr(p,'!'); /* Routed QWKnet mail in <qwkid!user@host> format */
if(tp!=NULL) {
*(tp++)=0;
SKIP_CHAR(tp,'"'); /* Skip '"' */
truncstr(tp,"\""); /* Strip '"' */
SAFECOPY(rcpt_addr,tp);
routed=TRUE;
FIND_ALPHANUMERIC(p); /* Skip '<' or '"' */
truncstr(p,"\"");
p=alias(&scfg,p,name_alias_buf);
if(p==name_alias_buf)
lprintf(LOG_INFO,"%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(LOG_WARNING,"%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;
}
memset(mailproc_match,FALSE,sizeof(BOOL)*mailproc_count);
for(i=0;i<mailproc_count;i++) {
if(mailproc_list[i].to!=NULL) {
for(j=0;mailproc_list[i].to[j]!=NULL;j++) {
if(stricmp(p,mailproc_list[i].to[j])==0) {
mailproc_match[i]=TRUE;
if(!mailproc_list[i].passthru)
break;
}
}
}
}
/* destined for an external mail processor */
if(i<mailproc_count) {
fprintf(rcptlst,"[%u]\n",rcpt_count++);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
#if 0 /* should we fall-through to the sysop account? */
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTEXT),1);
#endif
lprintf(LOG_INFO,"%04d SMTP Routing mail for %s to External Mail Processor: %s"
,socket, rcpt_addr, mailproc_list[i].cmdline);
sockprintf(socket,ok_rsp);
state=SMTP_STATE_RCPT_TO;
continue;
}
usernum=0; /* unknown user at this point */
if(routed) {
SAFECOPY(qwkid,p);
truncstr(qwkid,"/");
/* Search QWKnet hub-IDs for route destination */
for(i=0;i<scfg.total_qhubs;i++) {
if(!stricmp(qwkid,scfg.qhub[i]->id))
break;
}
if(i<scfg.total_qhubs) { /* found matching QWKnet Hub */
lprintf(LOG_INFO,"%04d SMTP Routing mail for %s <%s> to QWKnet Hub: %s"
,socket, rcpt_addr, p, scfg.qhub[i]->id);
fprintf(rcptlst,"[%u]\n",rcpt_count++);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_QWK);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),p);
sockprintf(socket,ok_rsp);
state=SMTP_STATE_RCPT_TO;
continue;
}
}
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 */);
lprintf(LOG_INFO,"%04d SMTP Forwarding mail for UNKNOWN USER to default user: %s"
,socket,startup->default_user);
else
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN DEFAULT USER: %s"
,socket,startup->default_user);
}
lprintf(LOG_WARNING,"%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) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user #%u (%s)"
sockprintf(socket, "550 Unknown User:%s", buf+8);
continue;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%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(LOG_WARNING,"%04d !Attempt to send telegram to unavailable user #%u (%s)"

rswindell
committed
,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",rcpt_count++);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTEXT),user.number);
/* 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!=NULL && smb_netaddr_type(user.netmail)==NET_INTERNET
&& !strstr(tp,scfg.sys_inetaddr)) {
lprintf(LOG_INFO,"%04d SMTP Forwarding to: %s"
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_INTERNET);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),user.netmail);
sockprintf(socket,"251 User not local; will forward to %s", user.netmail);
} else { /* Local (no-forward) */
if(routed) { /* QWKnet */
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_QWK);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),user.alias);
}
sockprintf(socket,ok_rsp);
}
/* Message Data (header and body) */
if(!strnicmp(buf,"DATA",4)) {
lprintf(LOG_WARNING,"%04d !SMTP MISSING 'RCPT TO' command", socket);
sockprintf(socket, badseq_rsp);
if(msgtxt!=NULL) {
fclose(msgtxt), msgtxt=NULL;
if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
unlink(msgtxt_fname);
}
SAFEPRINTF2(msgtxt_fname,"%sSBBS_SMTP.%s.msg", scfg.temp_dir, session_id);
if((msgtxt=fopen(msgtxt_fname,"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d opening %s"
,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(LOG_WARNING,"%04d !SMTP UNSUPPORTED COMMAND: '%s'", socket, buf);
lprintf(LOG_WARNING,"%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);
if(active_clients)
active_clients--, update_clients();
client_off(socket);
thread_down();
lprintf(LOG_DEBUG,"%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;
msg->hdr.delivery_attempts++;
lprintf(LOG_WARNING,"0000 !Delivery attempt #%u FAILED (%s) for message #%lu from %s to %s"
,msg->hdr.delivery_attempts
,err
,msg->hdr.number
,msg->from
,msg->to_net.addr);
if((i=smb_lockmsghdr(smb,msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"0000 !BOUNCE ERROR %d (%s) locking message header #%lu"
,i, smb->last_error, msg->hdr.number);
if((i=smb_putmsg(smb,msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) incrementing delivery attempt counter"
,i, smb->last_error);
smb_unlockmsghdr(smb,msg);
return(FALSE);
}
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))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"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(LOG_WARNING,"0000 !Deleted undeliverable message from %s", msg->from);
return(TRUE);
}
lprintf(LOG_WARNING,"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;
SAFEPRINTF(str,"Delivery failure: %s",newmsg.subj);
smb_hfield_str(&newmsg, SUBJECT, str);
smb_hfield_str(&newmsg, RECIPIENT, newmsg.from);
if(newmsg.idx.to) {
sprintf(str,"%u",newmsg.idx.to);
smb_hfield_str(&newmsg, RECIPIENTEXT, str);
if((newmsg.from_net.type==NET_QWK || newmsg.from_net.type==NET_INTERNET)
&& newmsg.reverse_path!=NULL) {
smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(newmsg.from_net.type), &newmsg.from_net.type);
smb_hfield_str(&newmsg, RECIPIENTNETADDR, newmsg.reverse_path);
}
strcpy(str,"Mail Delivery Subsystem");
smb_hfield_str(&newmsg, SENDER, 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;
SAFEPRINTF2(str,"%s reporting delivery failure of message %s"
,startup->host_name, attempts);
smb_hfield_str(&newmsg, SMB_COMMENT, str);
SAFEPRINTF2(str,"from %s to %s\r\n"
,msg->reverse_path==NULL ? msg->from : msg->reverse_path
,(char*)msg->to_net.addr);
smb_hfield_str(&newmsg, SMB_COMMENT, str);
strcpy(str,"Reason:");
smb_hfield_str(&newmsg, SMB_COMMENT, str);
smb_hfield_str(&newmsg, SMB_COMMENT, err);
smb_hfield_str(&newmsg, SMB_COMMENT, "\r\nOriginal message text follows:\r\n");
smb_init_idx(smb,&newmsg);
if((i=smb_addmsghdr(smb,&newmsg,SMB_SELFPACK))!=SMB_SUCCESS)
lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) adding message header"
,i,smb->last_error);
else {
lprintf(LOG_WARNING,"0000 !Delivery failure notification (message #%ld) created for %s"
,newmsg.hdr.number, newmsg.from);
if((i=smb_incmsg_dfields(smb,&newmsg,1))!=SMB_SUCCESS)
lprintf(LOG_ERR,"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);
}
static int remove_msg_intransit(smb_t* smb, smbmsg_t* msg)
{
int i;
if((i=smb_lockmsghdr(smb,msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"0000 !SEND ERROR %d (%s) locking message header #%lu"
,i, smb->last_error, msg->idx.number);
return(i);
}
msg->hdr.netattr&=~MSG_INTRANSIT;
i=smb_putmsghdr(smb,msg);
smb_unlockmsghdr(smb,msg);
if(i!=0)
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) writing message header #%lu"
,i, smb->last_error, msg->idx.number);
return(i);
}
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
int i,j;
char to[128];
char mx[128];
char mx2[128];
char str[128];
char resp[512];
char toaddr[256];
char challenge[256];
char secret[64];
char md5_data[384];
char digest[MD5_DIGEST_SIZE];
char numeric_ip[16];
char domain_list[MAX_PATH+1];
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;
thread_up(TRUE /* setuid */);
sendmail_running=TRUE;
terminate_sendmail=FALSE;
lprintf(LOG_DEBUG,"0000 SendMail thread started");
memset(&msg,0,sizeof(msg));
memset(&smb,0,sizeof(smb));
while(server_socket!=INVALID_SOCKET && !terminate_sendmail) {
if(startup->options&MAIL_OPT_NO_SENDMAIL) {
sem_trywait_block(&sendmail_wakeup_sem,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
sem_trywait_block(&sendmail_wakeup_sem,startup->sem_chk_freq*1000);

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))!=SMB_SUCCESS)
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS)
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 || terminate_sendmail) /* server stopped */

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(&smb, &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;
if(msg.idx.number==0) /* Invalid message number */
continue;
msg.offset=offset;
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"0000 !SEND ERROR %d (%s) locking message header #%lu (offset %lu)"
,i, smb.last_error, msg.idx.number, offset);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) reading message header #%lu (offset %lu)"
,i, smb.last_error, msg.idx.number, offset);
if(msg.to_net.type!=NET_INTERNET || msg.to_net.addr==NULL) {
smb_unlockmsghdr(&smb,&msg);
if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"0000 SEND Message #%lu from %s to %s - in transit"
,msg.hdr.number, msg.from, msg.to_net.addr);
continue;
}
msg.hdr.netattr|=MSG_INTRANSIT; /* Prevent another sendmail thread from sending this msg */
smb_putmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
active_sendmail=1, update_clients();
lprintf(LOG_INFO,"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(LOG_DEBUG,"0000 SEND getting message text");
Loading
Loading full blame...