Skip to content
Snippets Groups Projects
mailsrvr.c 188 KiB
Newer Older
		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);
				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 */
deuce's avatar
deuce committed
				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;
}

deuce's avatar
deuce committed
/* TODO: IPv6 etc. */
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
	char		to[128];
	char		mx[128];
	char		mx2[128];
	char		buf[512];
	char		fromaddr[256];
	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*		server;
	char*		msgtxt=NULL;
	char*		p;
	ulong		last_msg=0;
	ulong		dns;
	SOCKET		sock=INVALID_SOCKET;
	time_t		last_scan=0;
	smb_t		smb;
	smbmsg_t	msg;
	BOOL		sending_locally=FALSE;
	CRYPT_SESSION session = -1;
	lprintf(LOG_INFO,"SendMail thread started");

	memset(&msg,0,sizeof(msg));
	memset(&smb,0,sizeof(smb));

	listInit(&failed_server_list, /* flags: */0);

deuce's avatar
deuce committed
	while((!terminated) && !terminate_sendmail) {
		if(startup->options&MAIL_OPT_NO_SENDMAIL) {
			sem_trywait_block(&sendmail_wakeup_sem,1000);
			active_sendmail=0, update_clients();
		if (session != -1) {
			cryptDestroySession(session);
			session = -1;
		}
		if(sock!=INVALID_SOCKET) {
			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)
		smb_unlocksmbhdr(&smb);
		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);
				active_sendmail=0, update_clients();
deuce's avatar
deuce committed
			if(terminated || terminate_sendmail)	/* server stopped */
			if (session != -1) {
				cryptDestroySession(session);
				session = -1;
			}
			if(sock!=INVALID_SOCKET) {
				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) {
				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) {
			if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) {
rswindell's avatar
rswindell committed
				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
			SAFEPRINTF2(str,"Sending (%u of %u)", u+1, msgs);
#ifdef _WIN32
			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);
			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);
			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;
deuce's avatar
deuce committed
				/* TODO: IPv6 */
				if(startup->outgoing4.s_addr==0)
					SAFEPRINTF4(numeric_ip, "%u.%u.%u.%u"
deuce's avatar
deuce committed
							, startup->outgoing4.s_addr >> 24
							, (startup->outgoing4.s_addr >> 16) & 0xff
							, (startup->outgoing4.s_addr >> 8) & 0xff
							, startup->outgoing4.s_addr & 0xff);
			}
			else {
				if(startup->options&MAIL_OPT_RELAY_TX) { 
					server=startup->relay_server;
					port=startup->relay_port;
				} else {
					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"
							SAFEPRINTF2(err,"Error %d obtaining MX record for %s",i,p);
							bounce(0, &smb,&msg,err, /* immediate: */FALSE);
			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);
					sockprintf(sock,session,"%s",p);
					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;
						}
						sockprintf(sock,session,"%s",p);
				if(!sockgetrsp(sock,session,"235",buf,sizeof(buf))) {
					SAFEPRINTF3(err,badrsp_err,server,buf,"235");
					bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
			/* 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');
			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');
			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)"
			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;
			}
			sock=INVALID_SOCKET;
		}				
		status(STATUS_WFC);
		/* Free up resources here */
		if(mail!=NULL)
			freemail(mail);
	if (session != -1)
		cryptDestroySession(session);
	if(sock!=INVALID_SOCKET)
	smb_freemsgtxt(msgtxt);
	smb_freemsgmem(&msg);
	smb_close(&smb);

		active_sendmail=0, update_clients();
	{
		int32_t remain = thread_down();
		lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", remain);
	}
deuce's avatar
deuce committed
  	lprintf(LOG_INFO,"Mail Server terminate");
}

static void cleanup(int code)
{
	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);

		for(i=0;i<mailproc_count;i++) {
			if(mailproc_list[i].ar!=NULL && mailproc_list[i].ar!=nular)
				free(mailproc_list[i].ar);
	/* Check a if(mail_set!=NULL) check be performed here? */
deuce's avatar
deuce committed
	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));
		protected_uint32_destroy(active_clients);
	if(WSAInitialized && WSACleanup()!=0) 
		lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
	status("Down");
	if(terminate_server || code) {
		char str[1024];
deuce's avatar
deuce committed
		sprintf(str,"%lu connections served", stats.connections_served);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu refused", stats.connections_refused);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu ignored", stats.connections_refused);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu sessions refused", stats.sessions_refused);
		sprintf(str+strlen(str),", %lu messages received", stats.msgs_received);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu refused", stats.msgs_refused);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu ignored", stats.msgs_ignored);
deuce's avatar
deuce committed
			sprintf(str+strlen(str),", %lu errors", stats.errors);
deuce's avatar
deuce committed
			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];

	DESCRIBE_COMPILER(compiler);
	sscanf("$Revision$", "%*s %s", revision);
		"Compiled %s %s with %s"
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,smb_lib_ver()
		,__DATE__, __TIME__, compiler
		);

	return(ver);
}

void DLLCALL mail_server(void* arg)
	char			mailproc_ini[MAX_PATH+1];
	char			compiler[32];
deuce's avatar
deuce committed
	union xp_sockaddr	client_addr;
	socklen_t		client_addr_len;
deuce's avatar
deuce committed
	char			host_ip[INET6_ADDRSTRLEN];
	SOCKET			client_socket;
	int				i;
	ulong			l;
	time_t			t;
	time_t			start;
	pop3_t*			pop3;
	smtp_t*			smtp;
deuce's avatar
deuce committed
	void			*cbdata;
	CRYPT_SESSION	session = -1;
	startup=(mail_startup_t*)arg;

	if(thread_suid_broken)
		startup->seteuid(TRUE);
    if(startup==NULL) {
    	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;
	startup->recycle_now=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);
		memset(&scfg, 0, sizeof(scfg));
		lprintf(LOG_INFO,"%s Revision %s%s"
			,server_name
		DESCRIBE_COMPILER(compiler);
		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);
		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);
		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");
		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) {
				for(i=0;i<mailproc_count;i++) {
					memset(&mailproc_list[i],0,sizeof(struct mailproc));
rswindell's avatar
rswindell committed
					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));
rswindell's avatar
rswindell committed
						iniReadStringList(fp,sec_list[i],"To",",",NULL);
rswindell's avatar
rswindell committed
						iniReadStringList(fp,sec_list[i],"From",",",NULL);
rswindell's avatar
rswindell committed
						iniReadBool(fp,sec_list[i],"PassThru",TRUE);
rswindell's avatar
rswindell committed
						iniReadBool(fp,sec_list[i],"Native",FALSE);
rswindell's avatar
rswindell committed
						iniReadBool(fp,sec_list[i],"Disabled",FALSE);
						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);
			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);
		/* open a socket and wait for a client */
deuce's avatar
deuce committed
		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);
		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"))
deuce's avatar
deuce committed
			lprintf(LOG_INFO,"SMTP No extra interfaces listening");
		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");
		}

		if(!(startup->options&MAIL_OPT_NO_SENDMAIL)) {
			sendmail_running=TRUE;
			protected_uint32_adjust(&thread_count,1);
			_beginthread(sendmail_thread, 0, NULL);
		/* 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);
			semfile_list_check(&initialized,recycle_semfiles);
			semfile_list_check(&initialized,shutdown_semfiles);
		/* signal caller that we've started up successfully */
		if(startup->started!=NULL)
deuce's avatar
deuce committed
		lprintf(LOG_INFO,"Mail Server thread started");
deuce's avatar
deuce committed
		while(!terminated && !terminate_server) {
			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) {
deuce's avatar
deuce committed
						lprintf(LOG_INFO,"Recycle semaphore file (%s) detected",p);
						break;
					}
					if(startup->recycle_now==TRUE) {