Skip to content
Snippets Groups Projects
mailsrvr.c 144 KiB
Newer Older
					server="127.0.0.1";
				else {
					sprintf(numeric_ip, "%u.%u.%u.%u"
							, startup->interface_addr >> 24
							, (startup->interface_addr >> 16) & 0xff
							, (startup->interface_addr >> 8) & 0xff
							, startup->interface_addr & 0xff);
					server = numeric_ip;
			}
			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(&smb,&msg,err,FALSE);
							continue;
						}
						server=mx;
			if((sock=mail_open_socket(SOCK_STREAM,"smtp|sendmail"))==INVALID_SOCKET) {
				remove_msg_intransit(&smb,&msg);
				lprintf(LOG_ERR,"0000 !SEND ERROR %d opening socket", ERROR_VALUE);
				continue;
			}

			memset(&addr,0,sizeof(addr));
			addr.sin_addr.s_addr = htonl(startup->interface_addr);
			addr.sin_family = AF_INET;

			/* Not needed.  Port is zero
			if(startup->seteuid!=NULL)
				startup->seteuid(FALSE); */
			i=bind(sock,(struct sockaddr *)&addr, sizeof(addr));
			/* Not needed.  Port is zero
			if(startup->seteuid!=NULL)
				startup->seteuid(TRUE); */
			if(i!=0) {
				remove_msg_intransit(&smb,&msg);
				lprintf(LOG_ERR,"%04d !SEND ERROR %d (%d) binding socket", sock, i, ERROR_VALUE);
				continue;
			}

			strcpy(err,"UNKNOWN ERROR");
			success=FALSE;
			for(j=0;j<2 && !success;j++) {
				if(j) {
					if(startup->options&MAIL_OPT_RELAY_TX || !mx2[0])
						break;
					server=mx2;	/* Give second mx record a try */
				}
				
				lprintf(LOG_DEBUG,"%04d SEND resolving SMTP hostname: %s", sock, server);
				ip_addr=resolve_ip(server);
				if(ip_addr==INADDR_NONE) {
					SAFEPRINTF(err,"Failed to resolve SMTP hostname: %s",server);
					lprintf(LOG_WARNING,"%04d !SEND failure resolving hostname: %s", sock, server);
					continue;
				}

				memset(&server_addr,0,sizeof(server_addr));
				server_addr.sin_addr.s_addr = ip_addr;
				server_addr.sin_family = AF_INET;
				server_addr.sin_port = htons(port);
				if((server==mx || server==mx2) 
					&& ((ip_addr&0xff)==127 || ip_addr==0)) {
					SAFEPRINTF2(err,"Bad IP address (%s) for MX server: %s"
						,inet_ntoa(server_addr.sin_addr),server);
					continue;
				}
				lprintf(LOG_INFO,"%04d SEND connecting to port %u on %s [%s]"
					,server,inet_ntoa(server_addr.sin_addr));
				if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) {
					lprintf(LOG_WARNING,"%04d !SEND ERROR %d connecting to SMTP server: %s"
					SAFEPRINTF2(err,"Error %d connecting to SMTP server: %s"
					continue;
				}
				success=TRUE;
			}
			if(!success) {	/* Failed to send, so bounce */
				remove_msg_intransit(&smb,&msg);
				bounce(&smb,&msg,err,FALSE);	
				continue;
			}

			lprintf(LOG_DEBUG,"%04d SEND connected to %s",sock,server);

			/* HELO */
			if(!sockgetrsp(sock,"220",buf,sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"220");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			if(startup->options&MAIL_OPT_RELAY_TX 
				&& (startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=0)	/* Requires ESMTP */
				sockprintf(sock,"EHLO %s",startup->host_name);
			else
				sockprintf(sock,"HELO %s",startup->host_name);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}

			/* 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,"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,"AUTH %s",p);
					if(!sockgetrsp(sock,"334",buf,sizeof(buf))) {
						SAFEPRINTF3(err,badrsp_err,server,buf,"334 Username/Challenge");
						bounce(&smb,&msg,err,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(str,digest));
							b64_encode(p=resp,sizeof(resp),buf,0);
							break;
						default:
							p="<unknown>";
							break;
					}
					sockprintf(sock,"%s",p);
					if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) {
						if(!sockgetrsp(sock,"334",buf,sizeof(buf))) {
							SAFEPRINTF3(err,badrsp_err,server,buf,"334 Password");
							bounce(&smb,&msg,err,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,"%s",p);
					}
				}
				if(!sockgetrsp(sock,"235",buf,sizeof(buf))) {
					SAFEPRINTF3(err,badrsp_err,server,buf,"235");
					bounce(&smb,&msg,err,buf[0]=='5');
					continue;
				}
			}

			if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL)
				SAFECOPY(fromaddr,msg.reverse_path);
				usermailaddr(&scfg,fromaddr,msg.from);
			if(fromaddr[0]=='<')
				sockprintf(sock,"MAIL FROM: %s",fromaddr);
			else
				sockprintf(sock,"MAIL FROM: <%s>",fromaddr);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,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,"RCPT TO: <%s>", toaddr);
			if(!sockgetrsp(sock,"25", buf, sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"25*");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* DATA */
			sockprintf(sock,"DATA");
			if(!sockgetrsp(sock,"354", buf, sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"354");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf(LOG_DEBUG,"%04d SEND sending message text (%u bytes)"
				,sock, strlen(msgtxt));
			lines=sockmsgtxt(sock,&msg,msgtxt,-1);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				remove_msg_intransit(&smb,&msg);
				SAFEPRINTF3(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf(LOG_DEBUG,"%04d SEND message transfer complete (%lu lines)", sock, 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,fromaddr,toaddr);
			/* QUIT */
			sockprintf(sock,"QUIT");
			sockgetrsp(sock,"221", buf, sizeof(buf));
			sock=INVALID_SOCKET;
		}				
		status(STATUS_WFC);
		/* Free up resources here */
		if(mail!=NULL)
			freemail(mail);
	}
	if(sock!=INVALID_SOCKET)

	smb_freemsgtxt(msgtxt);
	smb_freemsgmem(&msg);
	smb_close(&smb);

		active_sendmail=0, update_clients();
	lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", thread_count);
  	lprintf(LOG_INFO,"%04d Mail Server terminate",server_socket);
}

static void cleanup(int code)
{
	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);
	if(server_socket!=INVALID_SOCKET) {
		server_socket=INVALID_SOCKET;
	}

	if(submission_socket!=INVALID_SOCKET) {
		mail_close_socket(submission_socket);
		submission_socket=INVALID_SOCKET;
	}

	if(pop3_socket!=INVALID_SOCKET) {
		pop3_socket=INVALID_SOCKET;
	}

	update_clients();

	if(WSAInitialized && WSACleanup()!=0) 
		lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
	status("Down");
	if(terminate_server || code)
		lprintf(LOG_INFO,"#### Mail Server thread terminated (%u threads remain, %lu clients served)"
			,thread_count, served);
	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];
	SOCKADDR_IN		server_addr;
	SOCKADDR_IN		client_addr;
	socklen_t		client_addr_len;
	SOCKET			client_socket;
	int				i;
	int				result;
	ulong			l;
	time_t			t;
	time_t			start;
	fd_set			socket_set;
	pop3_t*			pop3;
	smtp_t*			smtp;
	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=3600;	/* 60 minutes */
	if(startup->max_delivery_attempts==0)	startup->max_delivery_attempts=50;
	if(startup->max_inactivity==0) 			startup->max_inactivity=120; /* seconds */
	if(startup->max_recipients==0) 			startup->max_recipients=100;
	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;
	js_server_props.options=&startup->options;
	js_server_props.interface_addr=&startup->interface_addr;

	startup->recycle_now=FALSE;
	SetThreadName("Mail Server");

		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);

		/* 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].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);
		active_clients=0,update_clients();
		/* open a socket and wait for a client */
		server_socket = mail_open_socket(SOCK_STREAM,"smtp");
		if(server_socket == INVALID_SOCKET) {
			lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE);
		lprintf(LOG_DEBUG,"%04d SMTP socket opened",server_socket);

		/*****************************/
		/* Listen for incoming calls */
		/*****************************/
		memset(&server_addr, 0, sizeof(server_addr));

		server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
		server_addr.sin_family = AF_INET;
		server_addr.sin_port   = htons(startup->smtp_port);
		if(startup->smtp_port < IPPORT_RESERVED) {
			if(startup->seteuid!=NULL)
				startup->seteuid(FALSE);
		}
		result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
			,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Server",lprintf);
		if(startup->smtp_port < IPPORT_RESERVED) {
			if(startup->seteuid!=NULL)
				startup->seteuid(TRUE);
		}
		if(result != 0) {
			lprintf(LOG_CRIT,"%04d %s",server_socket, BIND_FAILURE_HELP);
		if(result != 0) {
			lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SMTP socket"
				,server_socket, result, ERROR_VALUE);
		lprintf(LOG_INFO,"%04d SMTP Server listening on port %u"
			,server_socket, startup->smtp_port);

		if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) {

			submission_socket = mail_open_socket(SOCK_STREAM,"submission");

			if(submission_socket == INVALID_SOCKET) {
				lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE);
				cleanup(1);
				return;
			}

			lprintf(LOG_DEBUG,"%04d SUBMISSION socket opened",submission_socket);

			/*****************************/
			/* Listen for incoming calls */
			/*****************************/
			memset(&server_addr, 0, sizeof(server_addr));

			server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
			server_addr.sin_family = AF_INET;
			server_addr.sin_port   = htons(startup->submission_port);

			if(startup->submission_port < IPPORT_RESERVED) {
				if(startup->seteuid!=NULL)
					startup->seteuid(FALSE);
			}
			result = retry_bind(submission_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
				,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Submission Agent",lprintf);
			if(startup->submission_port < IPPORT_RESERVED) {
				if(startup->seteuid!=NULL)
					startup->seteuid(TRUE);
			}
			if(result != 0) {
				lprintf(LOG_CRIT,"%04d %s",submission_socket, BIND_FAILURE_HELP);
				cleanup(1);
				return;
			}

			result = listen(submission_socket, 1);

			if(result != 0) {
				lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SUBMISSION socket"
					,submission_socket, result, ERROR_VALUE);
				cleanup(1);
				return;
			}

			lprintf(LOG_INFO,"%04d SUBMISSION Server listening on port %u"
				,submission_socket, startup->submission_port);
		if(startup->options&MAIL_OPT_ALLOW_POP3) {
			/* open a socket and wait for a client */
			pop3_socket = mail_open_socket(SOCK_STREAM,"pop3");
			if(pop3_socket == INVALID_SOCKET) {
				lprintf(LOG_CRIT,"!ERROR %d opening POP3 socket", ERROR_VALUE);
			lprintf(LOG_DEBUG,"%04d POP3 socket opened",pop3_socket);
			/*****************************/
			/* Listen for incoming calls */
			/*****************************/
			memset(&server_addr, 0, sizeof(server_addr));
			server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
			server_addr.sin_family = AF_INET;
			server_addr.sin_port   = htons(startup->pop3_port);
			if(startup->pop3_port < IPPORT_RESERVED) {
				if(startup->seteuid!=NULL)
					startup->seteuid(FALSE);
			}
			result = retry_bind(pop3_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
				,startup->bind_retry_count,startup->bind_retry_delay,"POP3 Server",lprintf);
			if(startup->pop3_port < IPPORT_RESERVED) {
				if(startup->seteuid!=NULL)
					startup->seteuid(FALSE);
			}
			if(result != 0) {
				lprintf(LOG_CRIT,"%04d %s",pop3_socket,BIND_FAILURE_HELP);
			if(result != 0) {
				lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on POP3 socket"
					,pop3_socket, result, ERROR_VALUE);
				cleanup(1);
				return;

			lprintf(LOG_INFO,"%04d POP3 Server listening on port %u"
				,pop3_socket, startup->pop3_port);
		sem_init(&sendmail_wakeup_sem,0,0);

		if(!(startup->options&MAIL_OPT_NO_SENDMAIL))
			_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)
rswindell's avatar
rswindell committed
		lprintf(LOG_INFO,"%04d Mail Server thread started",server_socket);

		while(server_socket!=INVALID_SOCKET && !terminate_server) {
			if(active_clients==0) {
				if(!(startup->options&MAIL_OPT_NO_RECYCLE)) {
					if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
						lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
							,server_socket,p);
						break;
					}
					if(startup->recycle_now==TRUE) {
						lprintf(LOG_NOTICE,"%04d Recycle semaphore signaled", server_socket);
				if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
						&& lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
						,server_socket,p))
					|| (startup->shutdown_now==TRUE
						&& lprintf(LOG_INFO,"%04d Shutdown semaphore signaled",server_socket))) {
					startup->shutdown_now=FALSE;
					terminate_server=TRUE;
			/* now wait for connection */

			FD_ZERO(&socket_set);
			FD_SET(server_socket,&socket_set);
			high_socket_set=server_socket+1;
			if(startup->options&MAIL_OPT_ALLOW_POP3 
				&& pop3_socket!=INVALID_SOCKET) {
				FD_SET(pop3_socket,&socket_set);
				if(pop3_socket+1>high_socket_set)
					high_socket_set=pop3_socket+1;
			if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT 
				&& submission_socket!=INVALID_SOCKET) {
				FD_SET(submission_socket,&socket_set);
				if(submission_socket+1>high_socket_set)
					high_socket_set=submission_socket+1;
			}
			tv.tv_sec=startup->sem_chk_freq;
			if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
					lprintf(LOG_DEBUG,"%04d Mail Server listening interrupted",server_socket);
				else if(ERROR_VALUE == ENOTSOCK)
            		lprintf(LOG_NOTICE,"%04d Mail Server sockets closed",server_socket);
					lprintf(LOG_WARNING,"%04d !ERROR %d selecting sockets",server_socket,ERROR_VALUE);
			if(server_socket!=INVALID_SOCKET && !terminate_server
				&& (FD_ISSET(server_socket,&socket_set) 
					|| (startup->options&MAIL_OPT_USE_SUBMISSION_PORT
						&& FD_ISSET(submission_socket,&socket_set)))) {
				client_addr_len = sizeof(client_addr);
				client_socket = accept(
					FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket
					,(struct sockaddr *)&client_addr
				if(client_socket == INVALID_SOCKET)
#if 0	/* is this necessary still? */
					if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) {
            			lprintf(LOG_NOTICE,"%04d SMTP socket closed while listening"
							,server_socket);
						break;
					}
#endif
					lprintf(LOG_WARNING,"%04d SMTP !ERROR %d accepting connection"
						,FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket
						,ERROR_VALUE);
#ifdef _WIN32
					if(WSAGetLastError()==WSAENOBUFS)	/* recycle (re-init WinSock) on this error */
						break;
#endif
					continue;
					startup->socket_open(startup->cbdata,TRUE);
				if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) {
					mail_close_socket(client_socket);
					continue;
				}

				if(active_clients>=startup->max_clients) {
runderwo's avatar
 
runderwo committed
					lprintf(LOG_WARNING,"%04d SMTP !MAXIMUM CLIENTS (%u) reached, access denied"
						,client_socket, startup->max_clients);
					sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}
				if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
					lprintf(LOG_ERR,"%04d SMTP !ERROR %d (%d) disabling blocking on socket"
						,client_socket, i, ERROR_VALUE);
					mail_close_socket(client_socket);
					continue;
				}
				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;
				smtp->client_addr=client_addr;
			if(pop3_socket!=INVALID_SOCKET
				&& FD_ISSET(pop3_socket,&socket_set)) {
				client_addr_len = sizeof(client_addr);
				client_socket = accept(pop3_socket, (struct sockaddr *)&client_addr
        			,&client_addr_len);
				if(client_socket == INVALID_SOCKET)
#if 0	/* is this necessary still? */
					if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL) {
            			lprintf(LOG_NOTICE,"%04d POP3 socket closed while listening",pop3_socket);
						break;
					}
#endif
					lprintf(LOG_WARNING,"%04d POP3 !ERROR %d accepting connection"
						,pop3_socket, ERROR_VALUE);
#ifdef _WIN32
					if(WSAGetLastError()==WSAENOBUFS)	/* recycle (re-init WinSock) on this error */
						break;
#endif
					continue;
					startup->socket_open(startup->cbdata,TRUE);
				if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) {
					mail_close_socket(client_socket);
					continue;
				}

				if(active_clients>=startup->max_clients) {
runderwo's avatar
 
runderwo committed
					lprintf(LOG_WARNING,"%04d POP3 !MAXIMUM CLIENTS (%u) reached, access denied"
						,client_socket, startup->max_clients);
					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) {
					lprintf(LOG_ERR,"%04d POP3 !ERROR %d (%d) disabling blocking on socket"
						,client_socket, i, ERROR_VALUE);
					sockprintf(client_socket,"-ERR System error, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}

				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;
				}
				pop3->socket=client_socket;
				pop3->client_addr=client_addr;

			lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
				,server_socket, active_clients);
			start=time(NULL);
			while(active_clients) {
				if(time(NULL)-start>startup->max_inactivity) {
					lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients"
						,server_socket, active_clients);
			sem_post(&sendmail_wakeup_sem);
			mswait(100);
			lprintf(LOG_DEBUG,"%04d Waiting for SendMail thread to terminate..."
				,server_socket);
			start=time(NULL);
			while(sendmail_running) {