Newer
Older
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);

rswindell
committed
sprintf(smb.file,"%smail",scfg.data_dir);
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
if((i=smb_open(&smb))!=0)
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);
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
/* 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(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;
}