Newer
Older
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
if (*session != -1) {
cryptDestroySession(*session);
*session = -1;
}
if (!sendmail_open_socket(&sock, smb, msg))
continue;
success=FALSE;
for(i=0;i<2 && !success;i++) {
if(i) {
if(startup->options&MAIL_OPT_RELAY_TX || !mx2[0])
break;
lprintf(LOG_DEBUG,"%04d SEND reverting to second MX: %s", sock, mx2);
server=mx2; /* Give second mx record a try */
}
lprintf(LOG_DEBUG,"%04d SEND resolving SMTP hostname: %s", sock, server);
ip_addr=resolve_ip(server);
if(ip_addr==INADDR_NONE) {
SAFEPRINTF(err, "Error resolving hostname %s", server);
lprintf(LOG_WARNING,"%04d !SEND failure resolving hostname: %s", sock, server);
continue;
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.in.sin_addr.s_addr = ip_addr;
server_addr.in.sin_family = AF_INET;
server_addr.in.sin_port = htons(port);
inet_addrtop(&server_addr,server_ip,sizeof(server_ip));
if((node=listFindNode(failed_server_list,&server_addr,sizeof(server_addr))) != NULL) {
SAFEPRINTF4(err, "Error %ld connecting to port %u on %s [%s]", node->tag, inet_addrport(&server_addr), server, server_ip);
lprintf(LOG_INFO,"%04d SEND skipping failed SMTP server: %s", sock, err);
continue;
}
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);
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
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:
/* We NEVER bounce() because of TLS errors, so we don't need to set err */
if ((!tls_retry) && get_ssl_cert(&scfg, NULL, 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) {
GCESH(status, "SMTP", sock, server, CRYPT_UNUSED, "creating TLS session");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(status, "SMTP", sock, server, *session, "creating TLS session");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_OBLIVIOUS)) != CRYPT_OK) {
GCESH(status, "SMTP", sock, server, *session, "setting certificate compliance level");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
GCESH(status, "SMTP", sock, server, *session, "setting private key");
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) {
GCESH(status, "SMTP", sock, server, *session, "setting network socket");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
GCESH(status, "SMTP", sock, server, *session, "setting network socket");
continue;
}
if (startup->max_inactivity) {
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCESH(status, "SMTP", sock, server, *session, "setting read timeout");
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,"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 Transfer Agent", mail_open_socket, startup->seteuid, "smtp"))
if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) {
xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces
, startup->submission_port, "SMTP Submission Agent", mail_open_socket, startup->seteuid, "submission");
}
if(startup->options&MAIL_OPT_TLS_SUBMISSION) {
xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submissions_port
, "SMTPS Submission Agent", mail_open_socket, startup->seteuid, "submissions");
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, "POP3S Server", mail_open_socket, startup->seteuid, "pop3s"))
lprintf(LOG_INFO,"POP3S No extra interfaces 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) {