Skip to content
Snippets Groups Projects
mailsrvr.c 143 KiB
Newer Older
deuce's avatar
deuce committed
	if(in==NULL)
		return(FALSE);

	*name=strtok_r(in, "#", &last);
deuce's avatar
deuce committed
	if(*name) {
		*tag=strtok_r(NULL, "", &last);
		return(TRUE);
	}
	return(FALSE);
}

static uint smtp_matchuser(scfg_t *scfg, char *str, BOOL aliases, BOOL datdupe)
{
	char	*user=strdup(str);
	char	*name;
	char	*tag=NULL;
	uint	usernum=0;

	if(!user)
		return(0);

	if(!smtp_splittag(user, &name, &tag))
		goto end;

	if(datdupe)
		usernum=userdatdupe(scfg, 0, U_NAME, LEN_NAME, name, FALSE);
	else
		usernum=matchuser(scfg, name, aliases);

	if(!usernum)
		goto end;

	if(checktag(scfg, tag, usernum))
		usernum=UINT_MAX;

end:
	free(user);
	return(usernum);
}

static void smtp_thread(void* arg)
{
	int			rd;
	char		str[512];
	char		buf[1024],*p,*tp,*cp;
	char		hdrfield[512];
	char		alias_buf[128];
rswindell's avatar
rswindell committed
	char		name_alias_buf[128];
	char		reverse_path[128];
	char		date[64];
	char		rcpt_name[128];
	char		rcpt_addr[128];
	char		hello_name[128];
	char		user_name[LEN_ALIAS+1];
	char		user_pass[LEN_PASS+1];
	char		relay_list[MAX_PATH+1];
	char		domain_list[MAX_PATH+1];
	char		spam_bait[MAX_PATH+1];
	char		host_name[128];
	char		host_ip[64];
	char		challenge[256];
	char		response[128];
	char		secret[64];
	char		md5_data[384];
	char		digest[MD5_DIGEST_SIZE];
	socklen_t	addr_len;
	BOOL		esmtp=FALSE;
	BOOL		forward=FALSE;
	BOOL		no_forward=FALSE;
	BOOL		routed=FALSE;
	BOOL		dnsbl_recvhdr;
	FILE*		msgtxt=NULL;
	char		newtxt_fname[MAX_PATH+1];
	char		logtxt_fname[MAX_PATH+1];
	FILE*		rcptlst;
	FILE*		proc_err;
	char		proc_err_fname[MAX_PATH+1];
	char		session_id[MAX_PATH+1];
	FILE*		spy=NULL;
	SOCKET		socket;
	HOSTENT*	host;
	smb_t		smb;
	smbmsg_t	msg;
	smbmsg_t	newmsg;
	user_t		user;
	client_t	client;
	smtp_t		smtp=*(smtp_t*)arg;
	SOCKADDR_IN server_addr;
	IN_ADDR		dnsbl_result;
rswindell's avatar
rswindell committed
	BOOL*		mailproc_to_match;
rswindell's avatar
rswindell committed
	JSRuntime*	js_runtime=NULL;
	JSContext*	js_cx=NULL;
	JSObject*	js_glob=NULL;
	int32		js_result;
rswindell's avatar
rswindell committed

	enum {
			 SMTP_STATE_INITIAL
			,SMTP_STATE_HELO
rswindell's avatar
rswindell committed
			,SMTP_STATE_MAIL_FROM
			,SMTP_STATE_RCPT_TO
			,SMTP_STATE_DATA_HEADER
			,SMTP_STATE_DATA_BODY

	} state = SMTP_STATE_INITIAL;

rswindell's avatar
rswindell committed
	enum {
			 SMTP_CMD_NONE
			,SMTP_CMD_MAIL
			,SMTP_CMD_SEND
			,SMTP_CMD_SOML
			,SMTP_CMD_SAML

	} cmd = SMTP_CMD_NONE;

	SetThreadName("SMTP");
	lprintf(LOG_DEBUG,"%04d SMTP Session thread started", socket);
#ifdef _WIN32
	if(startup->inbound_sound[0] && !(startup->options&MAIL_OPT_MUTE)) 
		PlaySound(startup->inbound_sound, NULL, SND_ASYNC|SND_FILENAME);

	addr_len=sizeof(server_addr);
	if((i=getsockname(socket, (struct sockaddr *)&server_addr,&addr_len))!=0) {
		lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%d) getting address/port"
			,socket, i, ERROR_VALUE);
		mail_close_socket(socket);
		thread_down();
rswindell's avatar
rswindell committed
	if((mailproc_to_match=alloca(sizeof(BOOL)*mailproc_count))==NULL) {
		lprintf(LOG_ERR,"%04d !SMTP ERROR allocating memory for mailproc_to_match", socket);
		sockprintf(socket,sys_error);
		mail_close_socket(socket);
		thread_down();
		return;
	} 
rswindell's avatar
rswindell committed
	memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
	memset(&smb,0,sizeof(smb));
	memset(&msg,0,sizeof(msg));
	memset(&user,0,sizeof(user));
	memset(&relay_user,0,sizeof(relay_user));
	SAFECOPY(host_ip,inet_ntoa(smtp.client_addr.sin_addr));
	lprintf(LOG_INFO,"%04d SMTP Connection accepted on port %u from: %s port %u"
		,socket, BE_INT16(server_addr.sin_port), host_ip, ntohs(smtp.client_addr.sin_port));

	if(startup->options&MAIL_OPT_NO_HOST_LOOKUP)
		host=NULL;
	else
		host=gethostbyaddr ((char *)&smtp.client_addr.sin_addr
deuce's avatar
deuce committed
			,sizeof(smtp.client_addr.sin_addr),AF_INET);

	if(host!=NULL && host->h_name!=NULL)
		SAFECOPY(host_name,host->h_name);
	else
		strcpy(host_name,"<no name>");

	sprintf(spam_bait,"%sspambait.cfg",scfg.ctrl_dir);
	sprintf(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);

	if(smtp.client_addr.sin_addr.s_addr==server_addr.sin_addr.s_addr 
		|| smtp.client_addr.sin_addr.s_addr==htonl(IPv4_LOCALHOST)) {
		/* local connection */
		dnsbl_result.s_addr=0;
	} else {
		if(trashcan(&scfg,host_ip,"ip") || findstr(host_ip,spam_block)) {
			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT BLOCKED in ip.can: %s"
				,socket, host_ip);
			sockprintf(socket,"550 Access denied.");
			mail_close_socket(socket);
			thread_down();
			if(active_clients)
				active_clients--, update_clients();
			return;
		}
		if(trashcan(&scfg,host_name,"host") || findstr(host_name,spam_block)) {
			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT BLOCKED in host.can: %s"
				,socket, host_name);
			sockprintf(socket,"550 Access denied.");
			mail_close_socket(socket);
			if(active_clients)
				active_clients--, update_clients();

		/*  SPAM Filters (mail-abuse.org) */
		dnsbl_result.s_addr = dns_blacklisted(socket,smtp.client_addr.sin_addr,host_name,dnsbl,dnsbl_ip);
		if(dnsbl_result.s_addr) {
			lprintf(LOG_NOTICE,"%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
				,socket, dnsbl, host_name, dnsbl_ip, inet_ntoa(dnsbl_result));
			if(startup->options&MAIL_OPT_DNSBL_REFUSE) {
				SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
				spamlog(&scfg, "SMTP", "SESSION REFUSED", str, host_name, dnsbl_ip, NULL, NULL);
				sockprintf(socket
					,"550 Mail from %s refused due to listing at %s"
					,dnsbl_ip, dnsbl);
				mail_close_socket(socket);
				lprintf(LOG_NOTICE,"%04d !SMTP REFUSED SESSION from blacklisted server"
					,socket);
				thread_down();
				if(active_clients)
					active_clients--, update_clients();
				return;
			}
		}
	sprintf(smb.file,"%smail",scfg.data_dir);
	if(smb_islocked(&smb)) {
		lprintf(LOG_WARNING,"%04d !SMTP MAIL BASE LOCKED: %s"
			,socket, smb.last_error);
		sockprintf(socket,sys_unavail);
		mail_close_socket(socket);
		thread_down();
		if(active_clients)
			active_clients--, update_clients();
		return;
	}

	srand(time(NULL) ^ (DWORD)GetCurrentThreadId());	/* seed random number generator */
	rand();	/* throw-away first result */
	SAFEPRINTF4(session_id,"%x%x%x%lx",getpid(),socket,rand(),clock());
	SAFEPRINTF2(msgtxt_fname,"%sSBBS_SMTP.%s.msg", scfg.temp_dir, session_id);
	SAFEPRINTF2(newtxt_fname,"%sSBBS_SMTP.%s.new", scfg.temp_dir, session_id);
	SAFEPRINTF2(logtxt_fname,"%sSBBS_SMTP.%s.log", scfg.temp_dir, session_id);
	SAFEPRINTF2(rcptlst_fname,"%sSBBS_SMTP.%s.lst", scfg.temp_dir, session_id);
	rcptlst=fopen(rcptlst_fname,"w+");
	if(rcptlst==NULL) {
		lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating recipient list: %s"
			,socket, errno, rcptlst_fname);
		thread_down();
		if(active_clients)
			active_clients--, update_clients();
	if(trashcan(&scfg,host_name,"smtpspy") 
		|| trashcan(&scfg,host_ip,"smtpspy")) {
		sprintf(str,"%ssmtpspy.txt", scfg.logs_dir);
		spy=fopen(str,"a");
	}

	/* Initialize client display */
	client.size=sizeof(client);
	client.time=time(NULL);
	SAFECOPY(client.addr,host_ip);
	SAFECOPY(client.host,host_name);
	client.port=ntohs(smtp.client_addr.sin_port);
	client.protocol="SMTP";
	client.user="<unknown>";
	client_on(socket,&client,FALSE /* update */);
	SAFEPRINTF(str,"SMTP: %s",host_ip);
	sockprintf(socket,"220 %s Synchronet SMTP Server %s-%s Ready"
		,startup->host_name,revision,PLATFORM_DESC);
	while(1) {
		rd = sockreadline(socket, buf, sizeof(buf));
		if(spy!=NULL)
			fprintf(spy,"%s\n",buf);
		if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_THROTTLE)
			mswait(DNSBL_THROTTLE_VALUE);
		if(state>=SMTP_STATE_DATA_HEADER) {
			if(!strcmp(buf,".")) {

				state=SMTP_STATE_HELO;	/* RESET state machine here in case of error */
rswindell's avatar
rswindell committed
				cmd=SMTP_CMD_NONE;
					lprintf(LOG_ERR,"%04d !SMTP NO MESSAGE TEXT FILE POINTER?", socket);
					sockprintf(socket,"554 No message text");
					continue;
				}

				if(ftell(msgtxt)<1) {
					lprintf(LOG_ERR,"%04d !SMTP INVALID MESSAGE LENGTH: %ld (%lu lines)"
						, socket, ftell(msgtxt), lines);
					sockprintf(socket,"554 No message text");
				lprintf(LOG_INFO,"%04d SMTP End of message (body: %lu lines, %lu bytes, header: %lu lines, %lu bytes)"
					, socket, lines, ftell(msgtxt)-hdr_len, hdr_lines, hdr_len);

				/* Twit-listing (sender's name and e-mail addresses) here */
				sprintf(path,"%stwitlist.cfg",scfg.ctrl_dir);
				if(fexist(path) && (findstr(sender,path) || findstr(sender_addr,path))) {
					lprintf(LOG_NOTICE,"%04d !SMTP FILTERING TWIT-LISTED SENDER: %s <%s>"
						,socket, sender, sender_addr);
					SAFEPRINTF2(tmp,"Twit-listed sender: %s <%s>", sender, sender_addr);
					spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
					sockprintf(socket, "554 Sender not allowed.");
					continue;
				}

				if(telegram==TRUE) {		/* Telegram */
					const char* head="\1n\1h\1cInstant Message\1n from \1h\1y";
					const char* tail="\1n:\r\n\1h";
					rewind(msgtxt);
					length=filelength(fileno(msgtxt));
					
					p=strchr(sender_addr,'@');
					if(p==NULL || resolve_ip(p+1)!=smtp.client_addr.sin_addr.s_addr) 
						/* Append real IP and hostname if different */
						safe_snprintf(str,sizeof(str),"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s"
							,head,sender_addr,host_ip,host_name,tail);
					else
						safe_snprintf(str,sizeof(str),"%s%s%s",head,sender_addr,tail);
					
					if((telegram_buf=(char*)malloc(length+strlen(str)+1))==NULL) {
						lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %lu bytes of memory for telegram from %s"
							,socket,length+strlen(str)+1,sender_addr);
						sockprintf(socket, insuf_stor);
						continue; 
					}
					strcpy(telegram_buf,str);	/* can't use SAFECOPY here */
					if(fread(telegram_buf+strlen(str),1,length,msgtxt)!=length) {
						lprintf(LOG_ERR,"%04d !SMTP ERROR reading %lu bytes from telegram file"
						sockprintf(socket, insuf_stor);
						free(telegram_buf);
						continue; 
					}
					telegram_buf[length+strlen(str)]=0;	/* Need ASCIIZ */

					/* Send telegram to users */
					sec_list=iniReadSectionList(rcptlst,NULL);	/* Each section is a recipient */
					for(rcpt_count=0; sec_list!=NULL
						&& sec_list[rcpt_count]!=NULL 
						&& rcpt_count<startup->max_recipients; rcpt_count++) {

						section=sec_list[rcpt_count];

						SAFECOPY(rcpt_name,iniReadString(rcptlst,section	,smb_hfieldtype(RECIPIENT),"unknown",value));
						usernum=iniReadInteger(rcptlst,section				,smb_hfieldtype(RECIPIENTEXT),0);
						SAFECOPY(rcpt_addr,iniReadString(rcptlst,section	,smb_hfieldtype(RECIPIENTNETADDR),rcpt_name,value));

						if((i=putsmsg(&scfg,usernum,telegram_buf))==0)
							lprintf(LOG_INFO,"%04d SMTP Created telegram (%ld/%u bytes) from %s to %s <%s>"
								,socket, length, strlen(telegram_buf), sender_addr, rcpt_name, rcpt_addr);
						else
							lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating telegram from %s to %s <%s>"
								,socket, i, sender_addr, rcpt_name, rcpt_addr);
					free(telegram_buf);
					sockprintf(socket,ok_rsp);
					telegram=FALSE;
					continue;
				}

				fclose(msgtxt), msgtxt=NULL;
				fclose(rcptlst), rcptlst=NULL;
					SAFEPRINTF2(proc_err_fname,"%sSBBS_SMTP.%s.err", scfg.temp_dir, session_id);
rswindell's avatar
rswindell committed
	
						if(mailproc_list[i].disabled)
							continue;
rswindell's avatar
rswindell committed
						if(!chk_ar(&scfg,mailproc_list[i].ar,&relay_user,&client))
rswindell's avatar
rswindell committed
							continue;

						if(mailproc_list[i].to!=NULL && !mailproc_to_match[i])
						if(mailproc_list[i].from!=NULL 
							&& !findstr_in_list(sender_addr, mailproc_list[i].from))
							continue;

						if(!mailproc_list[i].passthru)
							msg_handled=TRUE;

							,msgtxt_fname, newtxt_fname, logtxt_fname
							,rcptlst_fname, proc_err_fname
							,host_name, host_ip, relay_user.number
							,sender, sender_addr, reverse_path, str);
rswindell's avatar
rswindell committed
						lprintf(LOG_INFO,"%04d SMTP Executing external mail processor: %s"
							,socket, mailproc_list[i].name);
rswindell's avatar
rswindell committed
							lprintf(LOG_DEBUG,"%04d SMTP Executing external command: %s"
								,socket, str);
							if((j=system(str))!=0) {
								lprintf(LOG_NOTICE,"%04d !SMTP system(%s) returned %d (errno: %d)"
								if(mailproc_list[i].ignore_on_error) {
									lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) error: %d"
										,socket, mailproc_list[i].name, j);
rswindell's avatar
rswindell committed
							if(!js_mailproc(socket, &client, &relay_user
								,&mailproc_list[i]
								,str /* cmdline */
								,msgtxt_fname, newtxt_fname, logtxt_fname
								,sender, sender_addr, reverse_path, hello_name, &js_result
rswindell's avatar
rswindell committed
								,&js_runtime, &js_cx, &js_glob
								,"SMTP") || js_result!=0) {
#if 0 /* calling exit() in a script causes js_mailproc to return FALSE */
								lprintf(LOG_NOTICE,"%04d !SMTP JavaScript mailproc command (%s) failed (returned: %d)"
									,socket, str, js_result);
								if(mailproc_list[i].ignore_on_error) {
									lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) failure"
										,socket, mailproc_list[i].name);
						if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname))
							break;
					}
					if(flength(proc_err_fname)>0 
						&& (proc_err=fopen(proc_err_fname,"r"))!=NULL) {
						while(!feof(proc_err)) {
							if(!fgets(str,sizeof(str),proc_err))
								break;
							truncsp(str);
							lprintf(LOG_WARNING,"%04d !SMTP External mail processor (%s) error: %s"
								,socket, mailproc_list[i].name, str);
								sockprintf(socket,"%s", str);
							else
								sockprintf(socket,"554%c%s"
									,ftell(proc_err)<filelength(fileno(proc_err)) ? '-' : ' '
									,str);
						}
						fclose(proc_err);
					else if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
						lprintf(LOG_NOTICE,"%04d SMTP External mail processor (%s) removed %s file"
							,socket
							,mailproc_list[i].name
							,fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list");
					else if(msg_handled)
						sockprintf(socket,ok_rsp);
					remove(proc_err_fname);	/* Remove error file here */
				/* We must do this before continuing for handled msgs */
				/* to prevent freopen(NULL) and orphaned temp files */
				if((rcptlst=fopen(rcptlst_fname,fexist(rcptlst_fname) ? "r":"w+"))==NULL) {
					lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening recipient list: %s"
						sockprintf(socket,sys_error);
					lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor (%s)"
						,socket, mailproc_list[i].name);
				/* If mailproc has written new message text to .new file, use that instead of .msg */
				if(flength(newtxt_fname) > 0) {
					remove(msgtxt_fname);
					SAFECOPY(msgtxt_fname, newtxt_fname);
				} else
					remove(newtxt_fname);
				if((msgtxt=fopen(msgtxt_fname,"rb"))==NULL) {
					lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening message file: %s"
						,socket, errno, msgtxt_fname);
					sockprintf(socket,sys_error);
					continue;
				}

				/* Initialize message header */
				smb_freemsgmem(&msg);
				memset(&msg,0,sizeof(smbmsg_t));		

				/* Parse message header here */
				smb_error=SMB_SUCCESS; /* no SMB error */
				while(!feof(msgtxt)) {
					if(!fgets(buf,sizeof(buf),msgtxt))
						break;
					truncsp(buf);
					if(buf[0]==0)	/* blank line marks end of header */
						break;

					if(!strnicmp(buf, "SUBJECT:",8)) {
						p=buf+8;
						if(relay_user.number==0	&& dnsbl_result.s_addr && startup->dnsbl_tag[0]
							&& !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
							safe_snprintf(str,sizeof(str),"%.*s: %.*s"
								,(int)sizeof(str)/2, startup->dnsbl_tag
								,(int)sizeof(str)/2, p);
							p=str;
							lprintf(LOG_NOTICE,"%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
						smb_hfield_str(&msg, SUBJECT, p);
						&& !chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr,reverse_path,"FROM")) {
						errmsg="554 Sender not allowed.";
						smb_error=SMB_FAILURE;
					}
					if(!strnicmp(buf, "TO:", 3)
						&& !chk_email_addr(socket,buf+3,host_name,host_ip,rcpt_addr,reverse_path,"TO")) {
						errmsg="550 Unknown user.";
						smb_error=SMB_FAILURE;
						break;
					}
					if((smb_error=parse_header_field(buf,&msg,&hfield_type))!=SMB_SUCCESS) {
						if(smb_error==SMB_ERR_HDR_LEN)
							lprintf(LOG_WARNING,"%04d !SMTP MESSAGE HEADER EXCEEDS %u BYTES"
								,socket, SMB_MAX_HDR_LEN);
						else
							lprintf(LOG_ERR,"%04d !SMTP ERROR %d adding header field: %s"
				if(smb_error!=SMB_SUCCESS) {	/* SMB Error */
				if((p=smb_get_hfield(&msg, RFC822TO, NULL))!=NULL) {
					if(*p=='<')	p++;
					SAFECOPY(rcpt_name,p);
					truncstr(rcpt_name,">");
				}
				if((p=smb_get_hfield(&msg, RFC822FROM, NULL))!=NULL) {
					parse_mail_address(p 
						,sender		,sizeof(sender)-1
						,sender_addr,sizeof(sender_addr)-1);
rswindell's avatar
rswindell committed
				if(relay_user.number==0 && msg.subj!=NULL && trashcan(&scfg,msg.subj,"subject")) {
					lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SUBJECT (%s) from: %s"
					SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s"
						,msg.subj, reverse_path);
					spamlog(&scfg, "SMTP", "REFUSED"
						,tmp, host_name, host_ip, rcpt_addr, reverse_path);
					sockprintf(socket, "554 Subject not allowed.");
					continue;
				}
				dnsbl_recvhdr=FALSE;
				if(startup->options&MAIL_OPT_DNSBL_CHKRECVHDRS)  {
					for(i=0;!dnsbl_result.s_addr && i<msg.total_hfields;i++)  {
						if(msg.hfield[i].type == SMTPRECEIVED)  {
							if(chk_received_hdr(socket,msg.hfield_dat[i],&dnsbl_result,dnsbl,dnsbl_ip)) {
								dnsbl_recvhdr=TRUE;
				if(relay_user.number==0 && dnsbl_result.s_addr) {
					if(startup->options&MAIL_OPT_DNSBL_IGNORE) {
						lprintf(LOG_NOTICE,"%04d !SMTP IGNORED MAIL from blacklisted server"
						SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
							,str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
						/* pretend we received it */
						safe_snprintf(str,sizeof(str),"%s: %s is listed on %s as %s"
							,startup->dnsbl_hdr, dnsbl_ip
							,dnsbl, inet_ntoa(dnsbl_result));
						smb_hfield_str(&msg, RFC822HEADER, str);
						lprintf(LOG_NOTICE,"%04d !SMTP TAGGED MAIL HEADER from blacklisted server with: %s"
							,socket, startup->dnsbl_hdr);
					}
					if(startup->dnsbl_hdr[0] || startup->dnsbl_tag[0]) {
						SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
						spamlog(&scfg, "SMTP", "TAGGED", str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
				if(dnsbl_recvhdr)			/* DNSBL-listed IP found in Received header? */
					dnsbl_result.s_addr=0;	/* Reset DNSBL look-up result between messages */
					lprintf(LOG_WARNING,"%04d !SMTP MISSING mail header 'FROM' field", socket);
					sockprintf(socket, "554 Mail header missing 'FROM' field");
				smb_hfield_str(&msg, SMTPREVERSEPATH, reverse_path);
				smb_hfield_str(&msg, SENDER, sender);
				smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype);
				smb_hfield_str(&msg, SENDERNETADDR, sender_addr);
				if(msg.subj==NULL)
					smb_hfield(&msg, SUBJECT, 0, NULL);
				length=filelength(fileno(msgtxt))-ftell(msgtxt);

				if(startup->max_msg_size && length>startup->max_msg_size) {
					lprintf(LOG_WARNING,"%04d !SMTP message size (%lu) exceeds maximum: %lu bytes"
						,socket,length,startup->max_msg_size);
					sockprintf(socket, "552 Message size (%lu) exceeds maximum: %lu bytes"
						,length,startup->max_msg_size);
					subnum=INVALID_SUB;
					continue;
				}

				if((msgbuf=(char*)malloc(length+1))==NULL) {
					lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %d bytes of memory"
					sockprintf(socket, insuf_stor);
					subnum=INVALID_SUB;
					continue;
				}
				fread(msgbuf,length,1,msgtxt);
				msgbuf[length]=0;	/* ASCIIZ */

				/* Do external JavaScript processing here? */

				if(subnum!=INVALID_SUB) {	/* Message Base */
					if(relay_user.number==0)
						memset(&relay_user,0,sizeof(relay_user));

rswindell's avatar
rswindell committed
					if(!can_user_post(&scfg,subnum,&relay_user,&client,&reason)) {
						lprintf(LOG_WARNING,"%04d !SMTP %s (user #%u) cannot post on %s (reason: %u)"
							,socket, sender_addr, relay_user.number
							,scfg.sub[subnum]->sname, reason);
						sockprintf(socket,"550 Insufficient access");
						subnum=INVALID_SUB;
						continue;
					}

					if(rcpt_name[0]==0)
						strcpy(rcpt_name,"All");
					smb_hfield_str(&msg, RECIPIENT, rcpt_name);
					if((i=savemsg(&scfg, &smb, &msg, &client, msgbuf))!=SMB_SUCCESS) {
						lprintf(LOG_WARNING,"%04d !SMTP ERROR %d (%s) saving message"
							,socket,i,smb.last_error);
						sockprintf(socket, "452 ERROR %d (%s) saving message"
							,i,smb.last_error);
					} else {
						lprintf(LOG_INFO,"%04d SMTP %s posted a message on %s"
							,socket, sender_addr, scfg.sub[subnum]->sname);
					}
					free(msgbuf);
					smb_close(&smb);
					subnum=INVALID_SUB;
					continue;
				}

				/* E-mail */
				/* creates message data, but no header or index records (since msg.to==NULL) */
				i=savemsg(&scfg, &smb, &msg, &client, msgbuf);
					lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) saving message"
						,socket,i,smb.last_error);
					sockprintf(socket, "452 ERROR %d (%s) saving message"
						,i,smb.last_error);
					continue;
				}

				sec_list=iniReadSectionList(rcptlst,NULL);	/* Each section is a recipient */
				for(rcpt_count=0; sec_list!=NULL
					&& sec_list[rcpt_count]!=NULL 
					&& rcpt_count<startup->max_recipients; rcpt_count++) {
					SAFECOPY(rcpt_name,iniReadString(rcptlst,section	,smb_hfieldtype(RECIPIENT),"unknown",value));
					usernum=iniReadInteger(rcptlst,section				,smb_hfieldtype(RECIPIENTEXT),0);
					agent=iniReadShortInt(rcptlst,section				,smb_hfieldtype(RECIPIENTAGENT),AGENT_PERSON);
					nettype=iniReadShortInt(rcptlst,section				,smb_hfieldtype(RECIPIENTNETTYPE),NET_NONE);
					SAFECOPY(rcpt_addr,iniReadString(rcptlst,section	,smb_hfieldtype(RECIPIENTNETADDR),str,value));
					if(nettype==NET_NONE /* Local destination */ && usernum==0) {
						lprintf(LOG_ERR,"%04d !SMTP can't deliver mail to user #0"
							,socket);
						break;
					}

					if((i=smb_copymsgmem(&smb,&newmsg,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) copying message"
							,socket, i, smb.last_error);
						break;
					}

						"          by %s [%s] (%s %s-%s) with %s\r\n"
						,startup->host_name,inet_ntoa(server_addr.sin_addr)
						,revision,PLATFORM_DESC
						,rcpt_name,msgdate(msg.hdr.when_imported,date)
						,reverse_path);
					smb_hfield_add_str(&newmsg, SMTPRECEIVED, hdrfield, /* insert: */TRUE);
					smb_hfield_str(&newmsg, RECIPIENT, rcpt_name);
					if(usernum && nettype!=NET_INTERNET) {	/* Local destination or QWKnet routed */
						/* This is required for fixsmb to be able to rebuild the index */
						smb_hfield_str(&newmsg, RECIPIENTEXT, str);
						smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(nettype), &nettype);
						smb_hfield_str(&newmsg, RECIPIENTNETADDR, rcpt_addr);
					if(agent!=newmsg.to_agent)
						smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(agent), &agent);
					i=smb_addmsghdr(&smb,&newmsg,SMB_SELFPACK);
					smb_freemsgmem(&newmsg);
						lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) adding message header"
							,socket, i, smb.last_error);
					lprintf(LOG_INFO,"%04d SMTP Created message #%ld from %s to %s <%s>"
						,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
rswindell's avatar
rswindell committed
					if(!(startup->options&MAIL_OPT_NO_NOTIFY) && usernum
						&& !dnsbl_recvhdr && !dnsbl_result.s_addr) {
						safe_snprintf(str,sizeof(str)
							,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: "
							,timestr(&scfg,newmsg.hdr.when_imported.time,tmp)
							,sender,sender_addr);
						if(!newmsg.idx.to) {	/* Forwarding */
							strcat(str,"\1mand it was automatically forwarded to: \1h");
					smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS);
					sockprintf(socket, insuf_stor);
						smb_incmsg_dfields(&smb,&msg,(ushort)(rcpt_count-1));
#if 0 /* This shouldn't be necessary here */
				continue;
			}
			if(buf[0]==0 && state==SMTP_STATE_DATA_HEADER) {	
				state=SMTP_STATE_DATA_BODY;	/* Null line separates header and body */
				if(msgtxt!=NULL) {
					fprintf(msgtxt, "\r\n");
				continue;
			}
			if(state==SMTP_STATE_DATA_BODY) {
				p=buf;
				if(*p=='.') p++;	/* Transparency (RFC821 4.5.2) */
				if(msgtxt!=NULL) 
					fprintf(msgtxt, "%s\r\n", p);
				/* release time-slices every x lines */
				if(startup->lines_per_yield &&
					!(lines%startup->lines_per_yield))	
				continue;
			}
			/* RFC822 Header parsing */
			if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
				lprintf(LOG_DEBUG,"%04d SMTP %s",socket, buf);
			if(!strnicmp(buf, "FROM:", 5))
				parse_mail_address(buf+5
					,sender,		sizeof(sender)-1
					,sender_addr,	sizeof(sender_addr)-1);

			if(msgtxt!=NULL) 
				fprintf(msgtxt, "%s\r\n", buf);
		lprintf(LOG_DEBUG,"%04d SMTP RX: %s", socket, buf);
		if(!strnicmp(buf,"HELO",4)) {
			p=buf+4;
			SAFECOPY(hello_name,p);
			sockprintf(socket,"250 %s",startup->host_name);
			esmtp=FALSE;
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
			continue;
		}
		if(!strnicmp(buf,"EHLO",4)) {
			p=buf+4;
			SAFECOPY(hello_name,p);
			sockprintf(socket,"250-%s",startup->host_name);
			sockprintf(socket,"250 AUTH PLAIN LOGIN CRAM-MD5");
			esmtp=TRUE;
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
		if((auth_login=(stricmp(buf,"AUTH LOGIN")==0))==TRUE 
			|| strnicmp(buf,"AUTH PLAIN",10)==0) {
				sockprintf(socket,"334 VXNlcm5hbWU6");	/* Base64-encoded "Username:" */
				if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
					lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
				if(b64_decode(user_name,sizeof(user_name),buf,rd)<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				sockprintf(socket,"334 UGFzc3dvcmQ6");	/* Base64-encoded "Password:" */
				if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
					lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
				if(b64_decode(user_pass,sizeof(user_pass),buf,rd)<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
			} else {	/* AUTH PLAIN b64(<username>\0<user-id>\0<password>) */
				p=buf+10;
				SKIP_WHITESPACE(p);
				if(*p==0) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				ZERO_VAR(tmp);
				if(b64_decode(tmp,sizeof(tmp),p,strlen(p))<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				p=tmp;
				while(*p) p++;	/* skip username */
				p++;			/* skip NULL */
				if(*p==0) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				SAFECOPY(user_name,p);
				while(*p) p++;	/* skip user-id */
				p++;			/* skip NULL */
				if(*p==0) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
				SAFECOPY(user_pass,p);
			}

			if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
				if(scfg.sys_misc&SM_ECHO_PW)
					lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s (password: %s)"
					lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s"
						,socket, user_name);
				sockprintf(socket,badauth_rsp);
				continue;
			}
			if((i=getuserdat(&scfg, &relay_user))!=0) {
				lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user (%s)"
					,socket, i, user_name);
				sockprintf(socket,badauth_rsp);
				continue;
			}
			if(relay_user.misc&(DELETED|INACTIVE)) {
				lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
					,socket, relay_user.number, user_name);
				sockprintf(socket,badauth_rsp);
				break;
			}
			if(stricmp(user_pass,relay_user.pass)) {
				if(scfg.sys_misc&SM_ECHO_PW)
					lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s: '%s' expected '%s'"
						,socket, user_name, user_pass, relay_user.pass);
				else
					lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s"
						,socket, user_name);
				sockprintf(socket,badauth_rsp);
			/* Update client display */
			client.user=relay_user.alias;