Newer
Older
if((server==mx || server==mx2)
&& ((ip_addr&0xff)==127 || ip_addr==0)) {
SAFEPRINTF2(err,"Bad IP address (%s) for MX server: %s"
,server_ip,server);
lprintf(LOG_INFO, "%04d SEND %s", err);
continue;
}
lprintf(LOG_INFO,"%04d SEND connecting to port %u on %s [%s]"
,sock
,inet_addrport(&server_addr)
,server,server_ip);
if((i=nonblocking_connect(sock, (struct sockaddr *)&server_addr, xp_sockaddr_len(&server_addr), startup->connect_timeout))!=0) {
SAFEPRINTF2(err,"ERROR %d connecting to SMTP server: %s"
,i, server);
lprintf(LOG_INFO,"%04d !SEND %s" ,sock, err);
listAddNodeData(failed_server_list,&server_addr,sizeof(server_addr),i,NULL);
continue;
}
lprintf(LOG_DEBUG,"%04d SEND connected to %s",sock,server);
/* HELO */
if(!sockgetrsp(sock,*session,"220",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"220");
lprintf(LOG_INFO, "%04d SEND %s", sock, err);
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
continue;
}
success=TRUE;
}
if(!success) { /* Failed to connect to an MX, so bounce */
remove_msg_intransit(smb,msg);
bounce(sock /* Should be zero? */, smb,msg,err,/* immediate: */FALSE);
mail_close_socket(sock);
return INVALID_SOCKET;
}
sockprintf(sock,*session,"EHLO %s",startup->host_name);
switch (sockgetrsp_opt(sock,*session,"250", "STARTTLS", buf, sizeof(buf))) {
case -1:
if(startup->options&MAIL_OPT_RELAY_TX
&& (startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=0) { /* Requires ESMTP */
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */buf[0]=='5');
mail_close_socket(sock);
return INVALID_SOCKET;
}
sockprintf(sock,*session,"HELO %s",startup->host_name);
if(!sockgetrsp(sock,*session,"250",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */buf[0]=='5');
mail_close_socket(sock);
return INVALID_SOCKET;
}
return sock;
case 0:
return sock;
case 1:
if ((!tls_retry) && get_ssl_cert(&scfg, NULL) != -1) {
sockprintf(sock, *session, "STARTTLS");
if (sockgetrsp(sock, *session, "220", buf, sizeof(buf))) {
if ((status=cryptCreateSession(session, CRYPT_UNUSED, CRYPT_SESSION_SSL)) != CRYPT_OK) {
SAFEPRINTF2(err, "ERROR %d creating TLS session to SMTP server: %s", status, server);
lprintf(LOG_INFO,"%04d !SEND %s", sock, err);
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
SAFEPRINTF2(err, "ERROR %d disabling certificate validation with SMTP server: %s", status, server);
lprintf(LOG_INFO,"%04d !SEND %s" ,sock, err);
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_OBLIVIOUS)) != CRYPT_OK) {
SAFEPRINTF2(err, "ERROR %d setting oblivious certificate compliance level with SMTP server: %s", status, server);
lprintf(LOG_INFO,"%04d !SEND %s" ,sock, err);
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
SAFEPRINTF2(err, "ERROR %d setting private key with SMTP server: %s", status, server);
lprintf(LOG_INFO,"%04d !SEND %s", sock, err);
continue;
}
nodelay = TRUE;
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(sock,FIONBIO,&nb);
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_NETWORKSOCKET, sock)) != CRYPT_OK) {
SAFEPRINTF2(err, "ERROR %d setting network socket with SMTP server: %s", status, server);
lprintf(LOG_INFO,"%04d !SEND %s", sock, err);
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
estr = get_crypt_error(*session);
SAFEPRINTF3(err, "ERROR %d (%s) activating TLS session with SMTP server: %s", status, estr ? estr : "<unknown>", server);
if (estr)
free_crypt_attrstr(estr);
lprintf(LOG_INFO,"%04d !SEND %s", sock, err);
continue;
}
if (startup->max_inactivity) {
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
estr = get_crypt_error(*session);
SAFEPRINTF3(err, "ERROR %d (%s) setting max inactivity with SMTP server: %s", status, estr ? estr : "<unknown>", server);
if (estr)
free_crypt_attrstr(estr);
lprintf(LOG_INFO,"%04d !SEND %s", sock, err);
continue;
}
}
sockprintf(sock,*session,"EHLO %s",startup->host_name);
if(!sockgetrsp(sock,*session,"250",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"220");
lprintf(LOG_INFO, "%04d SEND %s", sock, err);
continue;
}
}
}
return sock;
}
}
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */FALSE);
if (*session != -1) {
cryptDestroySession(*session);
*session = -1;
}
mail_close_socket(sock);
return INVALID_SOCKET;
}
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
int i;
char to[128];
char mx[128];
char mx2[128];
char str[128];
char resp[512];
char toaddr[256];
char fromext[128];
char challenge[256];
char secret[64];
char md5_data[384];
uchar digest[MD5_DIGEST_SIZE];
char numeric_ip[16];
char domain_list[MAX_PATH+1];
char dns_server[16];
char* server;
char* msgtxt=NULL;
char* p;
char* tp;
ushort port;
ulong last_msg=0;
ulong dns;
ulong bytes;
BOOL first_cycle=TRUE;
SOCKET sock=INVALID_SOCKET;
time_t last_scan=0;
smb_t smb;
smbmsg_t msg;
uint32_t msgs;
uint32_t u;
size_t len;
BOOL sending_locally=FALSE;
link_list_t failed_server_list;
SetThreadName("sbbs/sendMail");
thread_up(TRUE /* setuid */);
terminate_sendmail=FALSE;
lprintf(LOG_INFO,"0000 SendMail thread started");
memset(&msg,0,sizeof(msg));
memset(&smb,0,sizeof(smb));
listInit(&failed_server_list, /* flags: */0);
YIELD();
if(startup->options&MAIL_OPT_NO_SENDMAIL) {
sem_trywait_block(&sendmail_wakeup_sem,1000);
continue;
}
if(active_sendmail!=0)
active_sendmail=0, update_clients();
listFreeNodes(&failed_server_list);
if (session != -1) {
cryptDestroySession(session);
session = -1;
}

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);
SAFEPRINTF(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;
lprintf(LOG_DEBUG, "0000 SEND last_msg=%u, smb.status.last_msg=%u, elapsed=%u"
,last_msg, smb.status.last_msg, time(NULL)-last_scan);
last_msg=smb.status.last_msg;
last_scan=time(NULL);
mail=loadmail(&smb,&msgs,/* to network */0,MAIL_YOUR,0);
for(u=0; u<msgs; u++) {
if(active_sendmail!=0)
active_sendmail=0, update_clients();
if (session != -1) {
cryptDestroySession(session);
session = -1;
}

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);
msg.hdr.number=mail[u].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) getting message index #%lu"
,i, smb.last_error, mail[u].number);
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);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) line %u, msg #%lu"
,i, smb.last_error, __LINE__, msg.idx.number);
if(msg.hdr.attr&MSG_DELETE || 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_NOTICE,"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();
fromext[0]=0;
if(msg.from_ext)
SAFEPRINTF(fromext," #%s", msg.from_ext);
if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL)
SAFECOPY(fromaddr,msg.reverse_path);
else
usermailaddr(&scfg,fromaddr,msg.from);
truncstr(fromaddr," ");
lprintf(LOG_INFO,"0000 SEND Message #%lu (%u of %u) from %s%s %s to %s [%s]"
,msg.hdr.number, u+1, msgs, msg.from, fromext, fromaddr
,msg.to, msg.to_net.addr);
SAFEPRINTF2(str,"Sending (%u of %u)", u+1, msgs);
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");
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR (%s) retrieving message text",smb.last_error);
remove_ctrl_a(msgtxt, msgtxt);
port=0;
mx2[0]=0;
sending_locally=FALSE;
/* Check if this is a local email ToDo */
SAFECOPY(to,(char*)msg.to_net.addr);
truncstr(to,"> ");
p=strrchr(to,'@');
if(p==NULL) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_WARNING,"0000 !SEND INVALID destination address: %s", to);
SAFEPRINTF(err,"Invalid destination address: %s", to);
bounce(0, &smb,&msg,err, /* immediate: */TRUE);
continue;
}
p++;
SAFEPRINTF(domain_list,"%sdomains.cfg",scfg.ctrl_dir);
if(stricmp(p,scfg.sys_inetaddr)==0
|| stricmp(p,startup->host_name)==0
|| findstr(p,domain_list)) {
/* This is a local message... no need to send to remote */
port = startup->smtp_port;
server="127.0.0.1";
else {
SAFEPRINTF4(numeric_ip, "%u.%u.%u.%u"
, startup->outgoing4.s_addr >> 24
, (startup->outgoing4.s_addr >> 16) & 0xff
, (startup->outgoing4.s_addr >> 8) & 0xff
, startup->outgoing4.s_addr & 0xff);
server = numeric_ip;
sending_locally=TRUE;
}
else {
if(startup->options&MAIL_OPT_RELAY_TX) {
server=startup->relay_server;
port=startup->relay_port;
} else {
server=p;
tp=strrchr(p,':'); /* non-standard SMTP port */
if(tp!=NULL) {
*tp=0;
port=atoi(tp+1);
if(port==0) { /* No port specified, use MX look-up */
get_dns_server(dns_server,sizeof(dns_server));
if((dns=resolve_ip(dns_server))==INADDR_NONE) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_WARNING,"0000 !SEND INVALID DNS server address: %s"
,dns_server);
continue;
}
lprintf(LOG_DEBUG,"0000 SEND getting MX records for %s from %s",p,dns_server);
if((i=dns_getmx(p, mx, mx2, INADDR_ANY, dns
,startup->options&MAIL_OPT_USE_TCP_DNS ? TRUE : FALSE
,TIMEOUT_THREAD_WAIT/2))!=0) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_WARNING,"0000 !SEND ERROR %d obtaining MX records for %s from %s"
,i,p,dns_server);
SAFEPRINTF2(err,"Error %d obtaining MX record for %s",i,p);
bounce(0, &smb,&msg,err, /* immediate: */FALSE);
continue;
}
server=mx;
if(!port)
port=IPPORT_SMTP;
sock = sendmail_negotiate(&session, &smb, &msg, mx, mx2, server, &failed_server_list, port);
if (sock == INVALID_SOCKET)
/* AUTH */
if(startup->options&MAIL_OPT_RELAY_TX
&& (startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=0 && !sending_locally) {
if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)==MAIL_OPT_RELAY_AUTH_PLAIN) {
/* Build the buffer: <username>\0<user-id>\0<password */
len=safe_snprintf(buf,sizeof(buf),"%s%c%s%c%s"
,startup->relay_user
,0
,startup->relay_user
,0
,startup->relay_pass);
b64_encode(resp,sizeof(resp),buf,len);
sockprintf(sock,session,"AUTH PLAIN %s",resp);
} else {
switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) {
case MAIL_OPT_RELAY_AUTH_LOGIN:
p="LOGIN";
break;
case MAIL_OPT_RELAY_AUTH_CRAM_MD5:
p="CRAM-MD5";
break;
default:
p="<unknown>";
break;
}
sockprintf(sock,session,"AUTH %s",p);
if(!sockgetrsp(sock,session,"334",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"334 Username/Challenge");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) {
case MAIL_OPT_RELAY_AUTH_LOGIN:
b64_encode(p=resp,sizeof(resp),startup->relay_user,0);
break;
case MAIL_OPT_RELAY_AUTH_CRAM_MD5:
p=buf;
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
b64_decode(challenge,sizeof(challenge),p,0);
/* Calculate response */
memset(secret,0,sizeof(secret));
SAFECOPY(secret,startup->relay_pass);
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));
safe_snprintf(buf,sizeof(buf),"%s %s",startup->relay_user,MD5_hex((BYTE*)str,digest));
b64_encode(p=resp,sizeof(resp),buf,0);
break;
default:
p="<unknown>";
break;
}
if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) {
if(!sockgetrsp(sock,session,"334",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"334 Password");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) {
case MAIL_OPT_RELAY_AUTH_LOGIN:
b64_encode(p=buf,sizeof(buf),startup->relay_pass,0);
break;
default:
p="<unknown>";
break;
}
}
if(!sockgetrsp(sock,session,"235",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"235");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
}
/* MAIL */
if(fromaddr[0]=='<')
sockprintf(sock,session,"MAIL FROM: %s",fromaddr);
sockprintf(sock,session,"MAIL FROM: <%s>",fromaddr);
if(!sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
/* RCPT */
if(msg.forward_path!=NULL) {
SAFECOPY(toaddr,msg.forward_path);
} else {
if((p=strrchr((char*)msg.to_net.addr,'<'))!=NULL)
p++;
else
p=(char*)msg.to_net.addr;
SAFECOPY(toaddr,p);
truncstr(toaddr,"> ");
if((p=strrchr(toaddr,'@'))!=NULL && (tp=strrchr(toaddr,':'))!=NULL
&& tp > p)
*tp=0; /* Remove ":port" designation from envelope */
}
sockprintf(sock,session,"RCPT TO: <%s>", toaddr);
if(!sockgetrsp(sock,session,"25", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"25*");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
/* DATA */
sockprintf(sock,session,"DATA");
if(!sockgetrsp(sock,session,"354", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"354");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
bytes=strlen(msgtxt);
lprintf(LOG_DEBUG,"%04d SEND sending message text (%u bytes) begin"
,sock, bytes);
lines=sockmsgtxt(sock,session,&msg,msgtxt,session);
lprintf(LOG_DEBUG,"%04d SEND send of message text (%u bytes, %u lines) complete, waiting for acknowledgment (250)"
,sock, bytes, lines);
if(!sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
/* Wait doublely-long for the acknowledgment */
if(buf[0] || !sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
lprintf(LOG_INFO,"%04d SEND message transfer complete (%u bytes, %lu lines)", sock, bytes, lines);
/* Now lets mark this message for deletion without corrupting the index */
msg.hdr.attr|=MSG_DELETE;
msg.hdr.netattr&=~MSG_INTRANSIT;
if((i=smb_updatemsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !SEND ERROR %d (%s) deleting message #%lu"
,sock, i, smb.last_error, msg.hdr.number);
if(msg.hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,&msg);
if(msg.from_agent==AGENT_PERSON && !(startup->options&MAIL_OPT_NO_AUTO_EXEMPT))
exempt_email_addr("SEND Auto-exempting",msg.from,fromext,fromaddr,toaddr);
sockprintf(sock,session,"QUIT");
sockgetrsp(sock,session,"221", buf, sizeof(buf));
if (session != -1) {
cryptDestroySession(session);
session = -1;
}

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
status(STATUS_WFC);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
if (session != -1)
cryptDestroySession(session);

rswindell
committed
mail_close_socket(sock);
listFree(&failed_server_list);
smb_freemsgtxt(msgtxt);
smb_freemsgmem(&msg);
smb_close(&smb);
if(active_sendmail!=0)
active_sendmail=0, update_clients();
{
int32_t remain = thread_down();
lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", remain);
}
sendmail_running=FALSE;
}
void DLLCALL mail_terminate(void)
terminate_server=TRUE;
}
static void cleanup(int code)
{
int i;
if(protected_uint32_value(thread_count) > 1) {
lprintf(LOG_DEBUG,"#### Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
while(protected_uint32_value(thread_count) > 1) {
mswait(100);
}
}
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(mailproc_list!=NULL) {
for(i=0;i<mailproc_count;i++) {
if(mailproc_list[i].ar!=NULL && mailproc_list[i].ar!=nular)
free(mailproc_list[i].ar);
strListFree(&mailproc_list[i].to);
strListFree(&mailproc_list[i].from);
}
FREE_AND_NULL(mailproc_list);
}
/* Check a if(mail_set!=NULL) check be performed here? */
xpms_destroy(mail_set, mail_close_socket_cb, NULL);
mail_set=NULL;
terminated=TRUE;
update_clients(); /* active_clients is destroyed below */
if(protected_uint32_value(active_clients))
lprintf(LOG_WARNING,"#### !Mail Server terminating with %ld active clients", protected_uint32_value(active_clients));
else
protected_uint32_destroy(active_clients);
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
thread_down();
if(terminate_server || code) {
char str[1024];
sprintf(str,"%lu connections served", stats.connections_served);
if(stats.connections_refused)
sprintf(str+strlen(str),", %lu refused", stats.connections_refused);
if(stats.connections_ignored)
sprintf(str+strlen(str),", %lu ignored", stats.connections_refused);
if(stats.sessions_refused)
sprintf(str+strlen(str),", %lu sessions refused", stats.sessions_refused);
sprintf(str+strlen(str),", %lu messages received", stats.msgs_received);
if(stats.msgs_refused)
sprintf(str+strlen(str),", %lu refused", stats.msgs_refused);
if(stats.msgs_ignored)
sprintf(str+strlen(str),", %lu ignored", stats.msgs_ignored);
if(stats.errors)
sprintf(str+strlen(str),", %lu errors", stats.errors);
if(stats.crit_errors)
sprintf(str+strlen(str),", %lu critcal", stats.crit_errors);
lprintf(LOG_INFO,"#### Mail Server thread terminated (%s)",str);
}
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(startup->cbdata,code);
const char* DLLCALL mail_ver(void)
{
static char ver[256];
char compiler[32];
sscanf("$Revision$", "%*s %s", revision);
sprintf(ver,"%s %s%s SMBLIB %s "
,server_name
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,smb_lib_ver()
,__DATE__, __TIME__, compiler
);
return(ver);
}
void DLLCALL mail_server(void* arg)
char* p;
char path[MAX_PATH+1];
char mailproc_ini[MAX_PATH+1];
char str[256];
char error[256];
socklen_t client_addr_len;
SOCKET client_socket;
int i;
ulong l;
time_t t;
time_t start;
time_t initialized=0;
pop3_t* pop3;
smtp_t* smtp;
FILE* fp;
str_list_t sec_list;
startup=(mail_startup_t*)arg;
#ifdef _THREAD_SUID_BROKEN
if(thread_suid_broken)
startup->seteuid(TRUE);
#endif
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;
}
ZERO_VAR(js_server_props);
SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision);
js_server_props.version_detail=mail_ver();
js_server_props.clients=&active_clients.value;
js_server_props.options=&startup->options;
js_server_props.interfaces=&startup->interfaces;
uptime=0;
memset(&stats,0,sizeof(stats));
startup->recycle_now=FALSE;
startup->shutdown_now=FALSE;
terminate_server=FALSE;
SetThreadName("sbbs/mailServer");
protected_uint32_init(&thread_count, 0);
/* Setup intelligent defaults */
if(startup->relay_port==0) startup->relay_port=IPPORT_SMTP;
if(startup->submission_port==0) startup->submission_port=IPPORT_SUBMISSION;
if(startup->submissions_port==0) startup->submissions_port=IPPORT_SUBMISSIONS;
if(startup->smtp_port==0) startup->smtp_port=IPPORT_SMTP;
if(startup->pop3_port==0) startup->pop3_port=IPPORT_POP3;
if(startup->pop3s_port==0) startup->pop3s_port=IPPORT_POP3S;
if(startup->rescan_frequency==0) startup->rescan_frequency=MAIL_DEFAULT_RESCAN_FREQUENCY;
if(startup->max_delivery_attempts==0) startup->max_delivery_attempts=MAIL_DEFAULT_MAX_DELIVERY_ATTEMPTS;
if(startup->max_inactivity==0) startup->max_inactivity=MAIL_DEFAULT_MAX_INACTIVITY; /* seconds */
if(startup->sem_chk_freq==0) startup->sem_chk_freq=DEFAULT_SEM_CHK_FREQ;
if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
protected_uint32_adjust(&thread_count,1);
thread_up(FALSE /* setuid */);
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
lprintf(LOG_INFO,"%s Revision %s%s"
,server_name
," Debug"
lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
sbbs_srand();
if(!winsock_startup()) {

rswindell
committed
cleanup(1);
return;
}
t=time(NULL);
lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
,ctime_r(&t,str),startup->options);
if(chdir(startup->ctrl_dir)!=0)
lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
/* Initial configuration and load from CNF files */
SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
SAFECOPY(error,UNKNOWN_LOAD_ERROR);
if(!load_cfg(&scfg, NULL, TRUE, error)) {
lprintf(LOG_CRIT,"!ERROR %s",error);
lprintf(LOG_CRIT,"!Failed to load configuration files");
cleanup(1);
return;
}
if(startup->temp_dir[0])
SAFECOPY(scfg.temp_dir,startup->temp_dir);
else
SAFECOPY(scfg.temp_dir,"../temp");
prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
MKDIR(scfg.temp_dir);
lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
if(!isdir(scfg.temp_dir)) {
lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
/* Parse the mailproc[.host].ini */
mailproc_list=NULL;
mailproc_count=0;
iniFileName(mailproc_ini,sizeof(mailproc_ini),scfg.ctrl_dir,"mailproc.ini");
if((fp=iniOpenFile(mailproc_ini, /* create? */FALSE))!=NULL) {
lprintf(LOG_DEBUG,"Reading %s",mailproc_ini);
sec_list = iniReadSectionList(fp,/* prefix */NULL);
if((mailproc_count=strListCount(sec_list))!=0
&& (mailproc_list=malloc(mailproc_count*sizeof(struct mailproc)))!=NULL) {
char buf[INI_MAX_VALUE_LEN+1];
for(i=0;i<mailproc_count;i++) {
memset(&mailproc_list[i],0,sizeof(struct mailproc));
SAFECOPY(mailproc_list[i].name,sec_list[i]);
SAFECOPY(mailproc_list[i].cmdline,
iniReadString(fp,sec_list[i],"Command",sec_list[i],buf));
SAFECOPY(mailproc_list[i].eval,
iniReadString(fp,sec_list[i],"Eval","",buf));
mailproc_list[i].to =
mailproc_list[i].from =
mailproc_list[i].passthru =
mailproc_list[i].native =
mailproc_list[i].disabled =
mailproc_list[i].ignore_on_error =
iniReadBool(fp,sec_list[i],"IgnoreOnError",FALSE);
mailproc_list[i].process_spam =
iniReadBool(fp,sec_list[i],"ProcessSPAM",TRUE);
mailproc_list[i].process_dnsbl =
iniReadBool(fp,sec_list[i],"ProcessDNSBL",TRUE);
mailproc_list[i].ar =
arstr(NULL,iniReadString(fp,sec_list[i],"AccessRequirements","",buf),&scfg);
}
}
iniFreeStringList(sec_list);
iniCloseFile(fp);
}
if(startup->host_name[0]==0)
SAFECOPY(startup->host_name,scfg.sys_inetaddr);
if((t=checktime())!=0) { /* Check binary time */
lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
}
if(uptime==0)
uptime=time(NULL); /* this must be done *after* setting the timezone */
if(startup->max_clients==0) {
startup->max_clients=scfg.sys_nodes;
if(startup->max_clients<10)
startup->max_clients=10;
}
lprintf(LOG_DEBUG,"Maximum clients: %u",startup->max_clients);
lprintf(LOG_DEBUG,"Maximum inactivity: %u seconds",startup->max_inactivity);
protected_uint32_init(&active_clients, 0);
update_clients();
/* open a socket and wait for a client */
mail_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf);
if(mail_set == NULL) {
lprintf(LOG_CRIT,"!ERROR creating mail server socket set", ERROR_VALUE);
cleanup(1);
return;
}
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->smtp_port, "SMTP Server", mail_open_socket, startup->seteuid, "smtp"))
lprintf(LOG_INFO,"SMTP No extra interfaces listening");
lprintf(LOG_INFO,"SMTP Server listening");
if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) {
if(xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submission_port, "SMTP Submission Agent", mail_open_socket, startup->seteuid, "submission"))
lprintf(LOG_INFO,"SUBMISSION Server listening");
}
if(startup->options&MAIL_OPT_TLS_SUBMISSION) {
if(xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submissions_port, "TLS/SMTP Submission Agent", mail_open_socket, startup->seteuid, "submissions"))
lprintf(LOG_INFO,"SUBMISSIONS Server listening");
}
if(startup->options&MAIL_OPT_ALLOW_POP3) {
/* open a socket and wait for a client */
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->pop3_interfaces, startup->pop3_port, "POP3 Server", mail_open_socket, startup->seteuid, "pop3"))
lprintf(LOG_INFO,"POP3 No extra interfaces listening");
if(startup->options&MAIL_OPT_TLS_POP3) {
/* open a socket and wait for a client */
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->pop3_interfaces, startup->pop3s_port, "TLS/POP3 Server", mail_open_socket, startup->seteuid, "pop3s"))
lprintf(LOG_INFO,"POP3S No extra interfaces listening");
lprintf(LOG_INFO,"POP3S Server listening");
}
sem_init(&sendmail_wakeup_sem,0,0);
if(!(startup->options&MAIL_OPT_NO_SENDMAIL)) {
sendmail_running=TRUE;
protected_uint32_adjust(&thread_count,1);
_beginthread(sendmail_thread, 0, NULL);
}
status(STATUS_WFC);
/* Setup recycle/shutdown semaphore file lists */
shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","mail");
recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","mail");
semfile_list_add(&recycle_semfiles,startup->ini_fname);
SAFEPRINTF(path,"%smailsrvr.rec",scfg.ctrl_dir); /* legacy */
semfile_list_add(&recycle_semfiles,path);
semfile_list_add(&recycle_semfiles,mailproc_ini);
if(!initialized) {
semfile_list_check(&initialized,recycle_semfiles);
semfile_list_check(&initialized,shutdown_semfiles);
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started(startup->cbdata);
YIELD();
if(protected_uint32_value(thread_count) <= (unsigned int)(1+(sendmail_running?1:0))) {
if(!(startup->options&MAIL_OPT_NO_RECYCLE)) {
if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
break;
}
if(startup->recycle_now==TRUE) {
startup->recycle_now=FALSE;
break;
}
if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
&& lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected",p))
|| (startup->shutdown_now==TRUE
startup->shutdown_now=FALSE;
terminate_server=TRUE;
break;
}
/* now wait for connection */
client_addr_len = sizeof(client_addr);
client_socket = xpms_accept(mail_set,&client_addr, &client_addr_len, startup->sem_chk_freq*1000, &cbdata);
if(client_socket != INVALID_SOCKET) {
if(startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);