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

	/* 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->smtp_port==0)				startup->smtp_port=IPPORT_SMTP;
	if(startup->pop3_port==0)				startup->pop3_port=IPPORT_POP3;
	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=2;
	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;
	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("Mail Server");
	protected_uint32_init(&thread_count, 0);
		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 Server", mail_open_socket, startup->seteuid, "smtp"))
deuce's avatar
deuce committed
			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"))
deuce's avatar
deuce committed
				lprintf(LOG_INFO,"SUBMISSION Server listening on port %u"
					,startup->submission_port);
		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"))
deuce's avatar
deuce committed
				lprintf(LOG_INFO,"SMTP No extra interfaces listening");
			lprintf(LOG_INFO,"POP3 Server 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");
		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) <= 1+sendmail_running) {
				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) {
deuce's avatar
deuce committed
						lprintf(LOG_NOTICE,"Recycle semaphore signaled");
				if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
deuce's avatar
deuce committed
						&& lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected",p))
					|| (startup->shutdown_now==TRUE
deuce's avatar
deuce committed
						&& lprintf(LOG_INFO,"Shutdown semaphore signaled"))) {
					startup->shutdown_now=FALSE;
					terminate_server=TRUE;
deuce's avatar
deuce committed
			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);
deuce's avatar
deuce committed
				inet_addrtop(&client_addr, host_ip, sizeof(host_ip));
				if(trashcan(&scfg,host_ip,"ip-silent")) {
					mail_close_socket(client_socket);
				if(protected_uint32_value(active_clients)>=startup->max_clients) {
deuce's avatar
deuce committed
					lprintf(LOG_WARNING,"%04d %s !MAXIMUM CLIENTS (%u) reached, access denied (%u total)"
						,client_socket, (char *)cbdata, startup->max_clients, ++stats.connections_refused);
					sockprintf(client_socket,"-ERR Maximum active clients reached, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}
				l=1;

				if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
deuce's avatar
deuce committed
					lprintf(LOG_CRIT,"%04d %s !ERROR %d (%d) disabling blocking on socket"
						,client_socket, (char *)cbdata, i, ERROR_VALUE);
					sockprintf(client_socket,"-ERR System error, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}

deuce's avatar
deuce committed
				if(strcmp((char *)cbdata, "pop3")) { /* Not POP3 */
					if((smtp=malloc(sizeof(smtp_t)))==NULL) {
						lprintf(LOG_CRIT,"%04d SMTP !ERROR allocating %u bytes of memory for smtp_t"
							,client_socket, sizeof(smtp_t));
						mail_close_socket(client_socket);
						continue;
					}

					smtp->socket=client_socket;
					memcpy(&smtp->client_addr, &client_addr, client_addr_len);
					smtp->client_addr_len=client_addr_len;
					protected_uint32_adjust(&thread_count,1);
					_beginthread(smtp_thread, 0, smtp);
					stats.connections_served++;
deuce's avatar
deuce committed
				else {
					if((pop3=malloc(sizeof(pop3_t)))==NULL) {
						lprintf(LOG_CRIT,"%04d POP3 !ERROR allocating %u bytes of memory for pop3_t"
							,client_socket,sizeof(pop3_t));
						sockprintf(client_socket,"-ERR System error, please try again later.");
						mswait(3000);
						mail_close_socket(client_socket);
						continue;
					}
deuce's avatar
deuce committed
					pop3->socket=client_socket;
					memcpy(&pop3->client_addr, &client_addr, client_addr_len);
					pop3->client_addr_len=client_addr_len;
					protected_uint32_adjust(&thread_count,1);
					_beginthread(pop3_thread, 0, pop3);
					stats.connections_served++;
				}
		if(protected_uint32_value(active_clients)) {
deuce's avatar
deuce committed
			lprintf(LOG_DEBUG,"Waiting for %d active clients to disconnect..."
				, protected_uint32_value(active_clients));
			while(protected_uint32_value(active_clients)) {
				if(startup->max_inactivity && time(NULL)-start>startup->max_inactivity) {
deuce's avatar
deuce committed
					lprintf(LOG_WARNING,"!TIMEOUT (%u seconds) waiting for %d active clients"
						, startup->max_inactivity, protected_uint32_value(active_clients));
			sem_post(&sendmail_wakeup_sem);
			mswait(100);
deuce's avatar
deuce committed
			lprintf(LOG_DEBUG,"Waiting for SendMail thread to terminate...");
			start=time(NULL);
			while(sendmail_running) {
				if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
deuce's avatar
deuce committed
					lprintf(LOG_WARNING,"!TIMEOUT waiting for sendmail thread to terminate");
deuce's avatar
deuce committed
		if(!sendmail_running) {
			while(sem_destroy(&sendmail_wakeup_sem)==-1 && errno==EBUSY) {
				mswait(1);
deuce's avatar
deuce committed
				sem_post(&sendmail_wakeup_sem);
deuce's avatar
deuce committed
			}
		}
			lprintf(LOG_INFO,"Recycling server...");
			mswait(2000);
			if(startup->recycle!=NULL)
				startup->recycle(startup->cbdata);
	protected_uint32_destroy(thread_count);