Newer
Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
continue;
if((i=smb_locksmbhdr(&smb))!=0)
continue;
if((i=smb_getstatus(&smb))!=0) {
smb_unlocksmbhdr(&smb);
continue;
}
smb_unlocksmbhdr(&smb);
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(sock!=INVALID_SOCKET) {

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

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

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

rswindell
committed
if(startup->options&MAIL_OPT_RELAY_TX)
server_addr.sin_port = htons(startup->relay_port);
else
server_addr.sin_port = htons(IPPORT_SMTP);
lprintf("%04d SEND connecting to port %u on %s [%s]"

rswindell
committed
,ntohs(server_addr.sin_port)
,server,inet_ntoa(server_addr.sin_addr));
if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) {
lprintf("%04d !SEND ERROR %d (%d) connecting to SMTP server: %s"
sprintf(err,"Error %d connecting to SMTP server: %s"
,(int)ERROR_VALUE,server);
continue;
}
success=TRUE;
}
if(!success) { /* Failed to send, so bounce */
bounce(&smb,&msg,err,FALSE);
continue;
}
lprintf("%04d SEND connected to %s",sock,server);
/* HELO */
if(!sockgetrsp(sock,"220",buf,sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 220",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
sockprintf(sock,"HELO %s",scfg.sys_inetaddr);
if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 250",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
/* MAIL */
if(msg.from_net.type==NET_INTERNET)
strcpy(fromaddr,msg.from_net.addr);
else
usermailaddr(&scfg,fromaddr,msg.from);
if(fromaddr[0]=='<')
sockprintf(sock,"MAIL FROM: %s",fromaddr);
else
sockprintf(sock,"MAIL FROM: <%s>",fromaddr);
if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 250",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
/* RCPT */
if(*((char*)msg.to_net.addr)=='<')
sockprintf(sock,"RCPT TO: %s", (char*)msg.to_net.addr);
else
sockprintf(sock,"RCPT TO: <%s>", (char*)msg.to_net.addr);
if(!sockgetrsp(sock,"25", buf, sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 25*",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
/* DATA */
sockprintf(sock,"DATA");
if(!sockgetrsp(sock,"354", buf, sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 354",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
lprintf("%04d SEND sending message text (%u bytes)"
,sock, strlen(msgtxt));
sockmsgtxt(sock,&msg,msgtxt,fromaddr,-1);
if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
sprintf(err,"%s replied with '%s' instead of 250",server,buf);
bounce(&smb,&msg,err,buf[0]=='5');
continue;
}
lprintf("%04d SEND message transfer complete", sock);
msg.hdr.attr|=MSG_DELETE;
msg.idx.attr=msg.hdr.attr;
if((i=smb_lockmsghdr(&smb,&msg))!=0)
lprintf("%04d !SEND ERROR %d locking message header #%lu"
,i,msg.hdr.number);
if((i=smb_putmsg(&smb,&msg))!=0)
lprintf("%04d !SEND ERROR %d deleting message #%lu"
,i,msg.hdr.number);
smb_unlockmsghdr(&smb,&msg);
/* QUIT */
sockprintf(sock,"QUIT");
sockgetrsp(sock,"221", buf, sizeof(buf));

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
status(STATUS_WFC);
}
if(sock!=INVALID_SOCKET)

rswindell
committed
mail_close_socket(sock);
smb_freemsgtxt(msgtxt);
smb_freemsgmem(&msg);
smb_close(&smb);
lprintf("0000 SendMail thread terminated");
sendmail_running=FALSE;
thread_down();
}
void DLLCALL mail_terminate(void)
{
if(server_socket!=INVALID_SOCKET) {
lprintf("%04d MAIL Terminate: closing socket",server_socket);

rswindell
committed
mail_close_socket(server_socket);
server_socket=INVALID_SOCKET;
}
}
static void cleanup(int code)
{
if(server_socket!=INVALID_SOCKET) {

rswindell
committed
mail_close_socket(server_socket);
server_socket=INVALID_SOCKET;
}
if(pop3_socket!=INVALID_SOCKET) {

rswindell
committed
mail_close_socket(pop3_socket);
pop3_socket=INVALID_SOCKET;
}
update_clients();
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
lprintf("#### Mail Server thread terminated");
status("Down");
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(code);
thread_down();
}
const char* DLLCALL mail_ver(void)
{
static char ver[256];
char compiler[32];

rswindell
committed
COMPILER_DESC(compiler);
sprintf(ver,"Synchronet Mail Server v%s%s SMBLIB v%s "
"Compiled %s %s with %s"
,MAIL_VERSION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,smb_lib_ver()
,__DATE__, __TIME__, compiler
);
return(ver);
}
void DLLCALL mail_server(void* arg)
{
char compiler[32];
SOCKADDR_IN server_addr;
SOCKADDR_IN client_addr;
int client_addr_len;
SOCKET client_socket;
int i;
int result;
ulong l;
time_t t;
time_t start;
LINGER linger;
fd_set socket_set;
SOCKET high_socket_set;
pop3_t* pop3;
smtp_t* smtp;

rswindell
committed
struct timeval tv;
startup=(mail_startup_t*)arg;
if(startup==NULL) {
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(mail_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
/* Setup intelligent defaults */
if(startup->relay_port==0) startup->relay_port=IPPORT_SMTP;
if(startup->smtp_port==0) startup->smtp_port=IPPORT_SMTP;
if(startup->pop3_port==0) startup->pop3_port=110;
if(startup->rescan_frequency==0) startup->rescan_frequency=3600; /* 60 minutes */
if(startup->max_delivery_attempts==0) startup->max_delivery_attempts=50;
if(startup->max_inactivity==0) startup->max_inactivity=120; /* seconds */

rswindell
committed
thread_up();
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
#ifdef __unix__ /* Ignore "Broken Pipe" signal */
signal(SIGPIPE,SIG_IGN);
#endif
lprintf("Synchronet Mail Server Version %s%s"
,MAIL_VERSION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
);

rswindell
committed
COMPILER_DESC(compiler);
lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
lprintf("SMBLIB v%s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
srand(time(NULL));

rswindell
committed
if(!(startup->options&MAIL_OPT_LOCAL_TIMEZONE)) {
if(PUTENV("TZ=UCT0"))
lprintf("!putenv() FAILED");
tzset();
if((t=checktime())!=0) { /* Check binary time */
lprintf("!TIME PROBLEM (%ld)",t);
cleanup(1);
return;
}
}
if(!winsock_startup()) {
cleanup(1);
return;
}
lprintf("Initializing on %.24s with options: %lx"
,ctime(&t),startup->options);
/* Initial configuration and load from CNF files */
sprintf(scfg.ctrl_dir, "%.*s", (int)sizeof(scfg.ctrl_dir)-1
,startup->ctrl_dir);
lprintf("Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
if(!load_cfg(&scfg, NULL, TRUE)) {
lprintf("!Failed to load configuration files");
cleanup(1);
return;
}
if(startup->max_clients==0) {
startup->max_clients=scfg.sys_nodes;
if(startup->max_clients<10)
startup->max_clients=10;
}
lprintf("Maximum clients: %u",startup->max_clients);
lprintf("Maximum inactivity: %u seconds",startup->max_inactivity);
active_clients=0;
update_clients();
/* open a socket and wait for a client */

rswindell
committed
server_socket = mail_open_socket(SOCK_STREAM);
if (server_socket == INVALID_SOCKET) {
lprintf("!ERROR %d opening socket", ERROR_VALUE);
cleanup(1);
return;
}
lprintf("%04d SMTP socket opened",server_socket);
#if 1
linger.l_onoff=TRUE;
linger.l_linger=5; /* seconds */
result = setsockopt (server_socket, SOL_SOCKET, SO_LINGER
,(char *)&linger, sizeof(linger));
if (result != 0) {
lprintf("%04d !ERROR %d (%d) setting socket options"
,server_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
#endif
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->smtp_port);
result = bind(server_socket, (struct sockaddr *) &server_addr
,sizeof (server_addr));
if (result != 0) {
lprintf("%04d !ERROR %d (%d) binding SMTP socket to port %u"
,server_socket, result, ERROR_VALUE, startup->smtp_port);
lprintf("!Another service may be using this port");
cleanup(1);
return;
}
lprintf("%04d SMTP socket bound to port %u"
,server_socket, startup->smtp_port);
result = listen (server_socket, 1);
if (result != 0) {
lprintf("%04d !ERROR %d (%d) listening on socket"
,server_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
if(startup->options&MAIL_OPT_ALLOW_POP3) {
/* open a socket and wait for a client */

rswindell
committed
pop3_socket = mail_open_socket(SOCK_STREAM);
if (pop3_socket == INVALID_SOCKET) {
lprintf("!ERROR %d opening POP3 socket", ERROR_VALUE);
cleanup(1);
return;
}
lprintf("%04d POP3 socket opened",pop3_socket);
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->pop3_port);
result = bind(pop3_socket, (struct sockaddr *) &server_addr
,sizeof (server_addr));
if (result != 0) {
lprintf("%04d !ERROR %d (%d) binding POP3 socket to port %u"
,pop3_socket, result, ERROR_VALUE, startup->pop3_port);
lprintf("!Another service may be using this port");
cleanup(1);
return;
}
lprintf("%04d POP3 socket bound to port %u"
,pop3_socket, startup->pop3_port);
result = listen (pop3_socket, 1);
if (result != 0) {
lprintf("%04d !ERROR %d (%d) listening on POP3 socket"
,pop3_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
}
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started();
if(!(startup->options&MAIL_OPT_NO_SENDMAIL))
_beginthread (sendmail_thread, 0, NULL);
lprintf("%04d Mail Server thread started",server_socket);
status(STATUS_WFC);
while (server_socket!=INVALID_SOCKET) {
/* now wait for connection */
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);
high_socket_set=server_socket+1;
if(startup->options&MAIL_OPT_ALLOW_POP3) {
FD_SET(pop3_socket,&socket_set);
if(pop3_socket+1>high_socket_set)
high_socket_set=pop3_socket+1;
}

rswindell
committed
tv.tv_sec=2;
tv.tv_usec=0;
if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
if(i==0) {
mswait(1);
continue;
lprintf("0000 Mail Server listening interrupted");
lprintf("0000 Mail Server sockets closed");
lprintf("0000 !ERROR %d selecting sockets",ERROR_VALUE);
break;
}
if(FD_ISSET(server_socket,&socket_set)) {
client_addr_len = sizeof (client_addr);
client_socket = accept(server_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
if (client_socket == INVALID_SOCKET)
{
lprintf("%04d SMTP socket closed while listening",server_socket);
lprintf("%04d !ERROR %d accept failed", server_socket, ERROR_VALUE);
break;
}
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
sockets++;
if(active_clients>=startup->max_clients) {
lprintf("%04d !MAXMIMUM CLIENTS (%u) reached, access denied"
,client_socket, startup->max_clients);
sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");

rswindell
committed
mail_close_socket(client_socket);
continue;
}
l=1;
if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
lprintf("%04d !ERROR %d (%d) disabling blocking on socket"
,client_socket, i, ERROR_VALUE);

rswindell
committed
mail_close_socket(client_socket);
continue;
}
if((smtp=malloc(sizeof(smtp_t)))==NULL) {
lprintf("%04d !ERROR allocating %u bytes of memory for smtp_t"
,client_socket, sizeof(smtp_t));

rswindell
committed
mail_close_socket(client_socket);
continue;
}
smtp->socket=client_socket;
smtp->client_addr=client_addr;
_beginthread (smtp_thread, 0, smtp);
}
if(FD_ISSET(pop3_socket,&socket_set)) {
client_addr_len = sizeof (client_addr);
client_socket = accept(pop3_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
if (client_socket == INVALID_SOCKET)
{
lprintf("%04d POP3 socket closed while listening",pop3_socket);
lprintf("%04d !ERROR %d accept failed", pop3_socket, ERROR_VALUE);
break;
}
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
sockets++;
if(active_clients>=startup->max_clients) {
lprintf("%04d !MAXMIMUM CLIENTS (%u) reached, access denied"
,client_socket, startup->max_clients);
sockprintf(client_socket,"-ERR Maximum active clients reached, please try again later.");

rswindell
committed
mail_close_socket(client_socket);
continue;
}
l=1;
if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
lprintf("%04d !ERROR %d (%d) disabling blocking on socket"
,client_socket, i, ERROR_VALUE);
sockprintf(client_socket,"-ERR System error, please try again later.");

rswindell
committed
mail_close_socket(client_socket);
continue;
}
if((pop3=malloc(sizeof(pop3_t)))==NULL) {
lprintf("%04d !ERROR allocating %u bytes of memory for pop3_t"
,client_socket,sizeof(pop3_t));
sockprintf(client_socket,"-ERR System error, please try again later.");

rswindell
committed
mail_close_socket(client_socket);
continue;
}
pop3->socket=client_socket;
pop3->client_addr=client_addr;
_beginthread (pop3_thread, 0, pop3);
}
}
if(active_clients) {
lprintf("0000 Waiting for %d active clients to disconnect...", active_clients);
start=time(NULL);
while(active_clients) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf("!TIMEOUT waiting for %u active clients ",active_clients);
break;
}
}
}
if(sendmail_running) {
lprintf("0000 Waiting for SendMail thread to terminate...");
start=time(NULL);
while(sendmail_running) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf("!TIMEOUT waiting for sendmail thread to "
"terminate");
break;
}