Skip to content
Snippets Groups Projects
mailsrvr.c 67.7 KiB
Newer Older

			if((i=smb_lockmsghdr(&smb,&msg))!=0) {
				lprintf("!SendMail: ERROR %d locking message header #%lu"
					,i,msg.idx.number);
				continue;
			}
			if((i=smb_getmsghdr(&smb,&msg))!=0) {
				smb_unlockmsghdr(&smb,&msg);
				lprintf("!SendMail: ERROR %d reading message header #%lu"
					,i,msg.idx.number);
				continue; 
			}
			smb_unlockmsghdr(&smb,&msg);

			if(msg.to_net.type!=NET_INTERNET) 
				continue;

			active_sendmail=1;
			update_clients();

			lprintf("SendMail: Message #%lu from %s to %s"
				,msg.hdr.number, msg.from, msg.to_net.addr);
			status("SendMail");
#ifdef _WIN32
			if(startup->outbound_sound[0] && !(startup->options&MAIL_OPT_MUTE)) 
				PlaySound(startup->outbound_sound, NULL, SND_ASYNC|SND_FILENAME);

			lprintf("SendMail: getting message text");
			if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_TAILS))==NULL) {
				lprintf("!SendMail: ERROR retrieving message text");
				continue;
			}

			if(startup->options&MAIL_OPT_RELAY_TX)  
				server=startup->relay_server;
			else {
				sprintf(to,"%.*s",(int)sizeof(to)-1,(char*)msg.to_net.addr);
				p=strrchr(to,'>');	/* Truncate '>' */
				if(p!=NULL) *p=0;

				p=strrchr(to,'@');
				if(p==NULL) {
					lprintf("!SendMail: INVALID destination address: %s", to);
					sprintf(err,"Invalid destination address: %s", to);
					bounce(&smb,&msg,err,TRUE);
					continue;
				}
				if((dns=resolve_ip(startup->dns_server))==0) 
					continue;
				p++;
				lprintf("SendMail: getting MX records for %s from %s",p,startup->dns_server);
				if((i=dns_getmx(p, mx, mx2, startup->interface_addr, dns
					,startup->options&MAIL_OPT_USE_TCP_DNS ? TRUE : FALSE))!=0) {
					lprintf("!SendMail: ERROR %d obtaining MX records for %s from %s"
						,i,p,startup->dns_server);
					sprintf(err,"Error %d obtaining MX record for %s",i,p);
					bounce(&smb,&msg,err,FALSE);
					continue;
				}
				server=mx;
			}


			lprintf("SendMail: opening socket");
			if((sock=open_socket(SOCK_STREAM))==INVALID_SOCKET) {
				lprintf("!SendMail: ERROR %d opening socket", ERROR_VALUE);
				continue;
			}

			lprintf("SendMail: socket opened: %d",sock);

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

			lprintf("SendMail: binding socket");
			if((i=bind(sock, (struct sockaddr *) &addr, sizeof (addr)))!=0) {
				lprintf("!SendMail: ERROR %d (%d) binding socket %d", i, ERROR_VALUE, sock);
				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("SendMail: resolving SMTP host name: %s", server);
				ip_addr=resolve_ip(server);
				if(!ip_addr)  {
					sprintf(err,"Failed to resolve SMTP host name: %s",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(IPPORT_SMTP);
				
				lprintf("SendMail: connecting to %s [%s]"
					,server,inet_ntoa(server_addr.sin_addr));
				if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) {
					lprintf("!SendMail: ERROR %d (%d) connecting to SMTP server: %s"
						,i,ERROR_VALUE, server);
					sprintf(err,"Error %ld connecting to SMTP server: %s"
						,ERROR_VALUE,server);
					continue;
				}
				success=TRUE;
			}
			if(!success) {	/* Failed to send, so bounce */
				bounce(&smb,&msg,err,FALSE);	
				continue;
			}

			lprintf("SendMail: connected to %s on socket %d",server,sock);

			/* HELO */
			if(!sockgetrsp(sock,"220",buf,sizeof(buf))) {
				sprintf(err,"%s replied with '%s' instead of 220",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			sockprintf(sock,"HELO %s",scfg.sys_inetaddr);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,"%s replied with '%s' instead of 250",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* MAIL */
			if(msg.from_net.type==NET_INTERNET)
				strcpy(fromaddr,msg.from_net.addr);
			else 
				usermailaddr(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))) {
				sprintf(err,"%s replied with '%s' instead of 250",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* RCPT */
			if(*((char*)msg.to_net.addr)=='<')
				sockprintf(sock,"RCPT TO: %s", (char*)msg.to_net.addr);
			else
				sockprintf(sock,"RCPT TO: <%s>", (char*)msg.to_net.addr);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,"%s replied with '%s' instead of 250",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* DATA */
			sockprintf(sock,"DATA");
			if(!sockgetrsp(sock,"354", buf, sizeof(buf))) {
				sprintf(err,"%s replied with '%s' instead of 354",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf("SendMail: sending message text");
			sockmsgtxt(sock,&msg,msgtxt,fromaddr,-1);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,"%s replied with '%s' instead of 250",server,buf);
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf("SendMail: transfer successful");

			msg.hdr.attr|=MSG_DELETE;
			msg.idx.attr=msg.hdr.attr;
			if((i=smb_lockmsghdr(&smb,&msg))!=0) 
				lprintf("!SendMail: ERROR %d locking message header #%lu"
					,i,msg.hdr.number);
			if((i=smb_putmsg(&smb,&msg))!=0)
				lprintf("!SendMail: ERROR %d deleting message #%lu"
					,i,msg.hdr.number);
			smb_unlockmsghdr(&smb,&msg);

			/* QUIT */
			sockprintf(sock,"QUIT");
			sockgetrsp(sock,"221", buf, sizeof(buf));
			close_socket(sock);
			sock=INVALID_SOCKET;
		}				
		status(STATUS_WFC);
	}
	if(sock!=INVALID_SOCKET)
		close_socket(sock);

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

	lprintf("SendMail thread terminated");

	sendmail_running=FALSE;

	thread_down();
}

void mail_terminate(void)
{
	if(server_socket!=INVALID_SOCKET) {
    	lprintf("MAIL Terminate: closing socket %d",server_socket);
		close_socket(server_socket);
	    server_socket=INVALID_SOCKET;
    }
}

static void cleanup(int code)
{
	if(server_socket!=INVALID_SOCKET) {
		close_socket(server_socket);
		server_socket=INVALID_SOCKET;
	}

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

	update_clients();

	if(WSACleanup()!=0) 
		lprintf("!WSACleanup ERROR %d",ERROR_VALUE);

    lprintf("Mail Server thread terminated");
	status("Down");
	if(startup!=NULL && startup->terminated!=NULL)
		startup->terminated(code);
	thread_down();
}

char* mail_ver(void)
{
	static char ver[256];
	char compiler[32];

#if defined(__BORLANDC__)
	sprintf(compiler,"BCC %X.%02X"
		,__BORLANDC__>>8
		,__BORLANDC__&0xff);
#elif defined(_MSC_VER)
	sprintf(compiler,"MSC %u", _MSC_VER);
#else
	strcpy(compiler,"UNKNOWN COMPILER");
#endif

	sprintf(ver,"Synchronet Mail Server v%s%s  SMBLIB v%s  "
		"Compiled %s %s with %s"
		,MAIL_VERSION
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,smb_lib_ver()
		,__DATE__, __TIME__, compiler
		);

	return(ver);
}

void mail_server(void* arg)
{
	char			compiler[32];
	SOCKADDR_IN		server_addr;
	SOCKADDR_IN		client_addr;
	int				client_addr_len;
	SOCKET			client_socket;
	int				i;
	int				result;
	ulong			l;
	time_t			t;
	time_t			start;
	LINGER			linger;
	fd_set			socket_set;
	pop3_t*			pop3;
	smtp_t*			smtp;

	startup=(mail_startup_t*)arg;

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

	thread_up();

	status("Initializing");

	lprintf("Synchronet Mail Server Version %s%s"
		,MAIL_VERSION
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		);

#if defined(__BORLANDC__)
	sprintf(compiler,"BCC %X.%02X"
		,__BORLANDC__>>8
		,__BORLANDC__&0xff);
#elif defined(_MSC_VER)
	sprintf(compiler,"MSC %u", _MSC_VER);
#else
	strcpy(compiler,"UNKNOWN COMPILER");
#endif

	lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);

	lprintf("SMBLIB v%s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);

	srand(time(NULL));

	if(PUTENV("TZ=UCT0"))
		lprintf("!putenv() FAILED");

	tzset();

	if((t=checktime())!=0) {   /* Check binary time */
		lprintf("!TIME PROBLEM (%08lx)",t);
		cleanup(1);
		return;
    }

	if(!winsock_startup()) {
		cleanup(1);
		return;
	}

	/* Initial configuration and load from CNF files */
    memset(&scfg, 0, sizeof(scfg));
    sprintf(scfg.ctrl_dir, "%.*s", (int)sizeof(scfg.ctrl_dir)-1
    	,startup->ctrl_dir);
    lprintf("Loading configuration files from %s", scfg.ctrl_dir);
	if(!load_cfg(&scfg, NULL)) {
		lprintf("!Failed to load configuration files");
		cleanup(1);
		return;
	}

	if(!startup->max_clients) {
		startup->max_clients=scfg.sys_nodes;
		if(startup->max_clients<10)
			startup->max_clients=10;
	}

	lprintf("Maximum clients: %u",startup->max_clients);

	if(!startup->max_inactivity) 
		startup->max_inactivity=120; /* seconds */

	lprintf("Maximum inactivity: %u seconds",startup->max_inactivity);

	active_clients=0;
	update_clients();

    /* open a socket and wait for a client */

    server_socket = open_socket(SOCK_STREAM);

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

    lprintf("SMTP socket %d opened",server_socket);

#if 0
	optlen = sizeof(i);
	result = getsockopt (server_socket, SOL_SOCKET, SO_SNDBUF
    	,(char *)&i, &optlen);

	if (result != 0) {
		lprintf("!ERROR %d (%d) getting socket options", result, ERROR_VALUE);
		cleanup(1);
		return;
	}

	lprintf("SO_SNDBUF size=%d",i);

	optlen=sizeof(i);
	result = getsockopt (server_socket, SOL_SOCKET, SO_RCVBUF
    	,(char *)&i, &optlen);

	if (result != 0) {
		lprintf("!ERROR %d (%d) getting socket options", result, ERROR_VALUE);
		cleanup(1);
		return;
	}

	lprintf("SO_RCVBUF size=%d",i);
#endif

#if 1
	linger.l_onoff=TRUE;
    linger.l_linger=5;	/* seconds */

	result = setsockopt (server_socket, SOL_SOCKET, SO_LINGER
    	,(char *)&linger, sizeof(linger));

	if (result != 0) {
		lprintf("!ERROR %d (%d) setting socket options", result, ERROR_VALUE);
		cleanup(1);
		return;
	}
#endif

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

    result = bind(server_socket, (struct sockaddr *) &server_addr
    	,sizeof (server_addr));

	if (result != 0) {
		lprintf("!ERROR %d (%d) binding SMTP socket to port %d"
			,result, ERROR_VALUE, startup->smtp_port);
		cleanup(1);
		return;
	}

    lprintf("SMTP socket bound to port %d",startup->smtp_port);

    result = listen (server_socket, 1);

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

	if(startup->options&MAIL_OPT_ALLOW_POP3) {

		/* open a socket and wait for a client */

		pop3_socket = open_socket(SOCK_STREAM);

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

		lprintf("POP3 socket %d 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);

		result = bind(pop3_socket, (struct sockaddr *) &server_addr
    		,sizeof (server_addr));

		if (result != 0) {
			lprintf("!ERROR %d (%d) binding POP3 socket to port %u"
				,result, ERROR_VALUE, startup->pop3_port);
			cleanup(1);
			return;
		}

	    lprintf("POP3 socket bound to port %u",startup->pop3_port);

		result = listen (pop3_socket, 1);

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

	/* signal caller that we've started up successfully */
    if(startup->started!=NULL)
    	startup->started();

	_beginthread (sendmail_thread, 0, NULL);

	lprintf("Mail Server thread started");
	status(STATUS_WFC);

	while (server_socket!=INVALID_SOCKET) {

		/* now wait for connection */

		FD_ZERO(&socket_set);
		FD_SET(server_socket,&socket_set);
		if(startup->options&MAIL_OPT_ALLOW_POP3)
			FD_SET(pop3_socket,&socket_set);

		if((i=select(0,&socket_set,NULL,NULL,NULL))<1) {
			if(!i) {
				lprintf("select returned zero");
				break;
			}
rswindell's avatar
rswindell committed
			if(ERROR_VALUE==EINTR)
				lprintf("Mail Server listening interrupted");
rswindell's avatar
rswindell committed
			else if(ERROR_VALUE == ENOTSOCK)
            	lprintf("Mail Server sockets closed");
			else
				lprintf("!ERROR %d selecting sockets",ERROR_VALUE);
			break;
		}


		if(FD_ISSET(server_socket,&socket_set)) {

			client_addr_len = sizeof (client_addr);
			client_socket = accept(server_socket, (struct sockaddr *)&client_addr
        		,&client_addr_len);

			if (client_socket == INVALID_SOCKET)
			{
rswindell's avatar
rswindell committed
				if(ERROR_VALUE == ENOTSOCK)
            		lprintf("SMTP Socket closed while listening");
				else
					lprintf("!ERROR %d accept failed", ERROR_VALUE);
				break;
			}
			if(startup->socket_open!=NULL)
				startup->socket_open(TRUE);
			sockets++;

			if(active_clients>=startup->max_clients) {
				lprintf("!MAXMIMUM CLIENTS (%u) reached, access denied",startup->max_clients);
				sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
				Sleep(3000);
				close_socket(client_socket);
				continue;
			}

			l=1;

			if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
				lprintf("!ERROR %d (%d) disabling blocking on socket %d"
					,i, ERROR_VALUE, client_socket);
				close_socket(client_socket);
				continue;
			}

			if((smtp=malloc(sizeof(smtp_t)))==NULL) {
				lprintf("!ERROR allocating %u bytes of memory for smtp_t"
					,sizeof(smtp_t));
				close_socket(client_socket);
				continue;
			}

			smtp->socket=client_socket;
			smtp->client_addr=client_addr;
			_beginthread (smtp_thread, 0, smtp);
		}

		if(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)
			{
rswindell's avatar
rswindell committed
				if(ERROR_VALUE == ENOTSOCK)
            		lprintf("POP3 Socket closed while listening");
				else
					lprintf("!ERROR %d accept failed", ERROR_VALUE);
				break;
			}
			if(startup->socket_open!=NULL)
				startup->socket_open(TRUE);
			sockets++;

			if(active_clients>=startup->max_clients) {
				lprintf("!MAXMIMUM CLIENTS (%u) reached, access denied",startup->max_clients);
				sockprintf(client_socket,"-ERR Maximum active clients reached, please try again later.");
				Sleep(3000);
				close_socket(client_socket);
				continue;
			}


			l=1;

			if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
				lprintf("!ERROR %d (%d) disabling blocking on socket %d"
					,i, ERROR_VALUE, client_socket);
				sockprintf(client_socket,"-ERR System error, please try again later.");
				Sleep(3000);
				close_socket(client_socket);
				continue;
			}

			if((pop3=malloc(sizeof(pop3_t)))==NULL) {
				lprintf("!ERROR allocating %u bytes of memory for pop3_t"
					,sizeof(pop3_t));
				sockprintf(client_socket,"-ERR System error, please try again later.");
				Sleep(3000);
				close_socket(client_socket);
				continue;
			}

			pop3->socket=client_socket;
			pop3->client_addr=client_addr;

			_beginthread (pop3_thread, 0, pop3);
		}
	}

	if(active_clients) {
		lprintf("Waiting for %d active clients to disconnect...", active_clients);
		start=time(NULL);
		while(active_clients) {
			if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
				lprintf("!TIMEOUT waiting for %u active clients ",active_clients);
				break;
			}
			Sleep(100);
		}
	}

	if(sendmail_running) {
		lprintf("Waiting for SendMail thread to terminate...");
		start=time(NULL);
		while(sendmail_running) {
			if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
				lprintf("!TIMEOUT waiting for sendmail thread to "
            		"terminate");
				break;
			}
			Sleep(100);
		}
	}

	cleanup(0);
}