Skip to content
Snippets Groups Projects
mailsrvr.c 81.7 KiB
Newer Older
				msg.hdr.attr|=MSG_DELETE;
				msg.idx.attr=msg.hdr.attr;
				if((i=smb_putmsg(&smb,&msg))!=0) {
					smb_unlockmsghdr(&smb,&msg);
					smb_freemsgmem(&msg);
					lprintf("%04d !POP3 ERROR %d marking message as read", socket, i);
					sockprintf(socket,"-ERR %d marking message for deletion",i);
					continue;
				}
				if(msg.hdr.auxattr&MSG_FILEATTACH)
					delfattach(&scfg,&msg);
				smb_unlockmsghdr(&smb,&msg);
				smb_freemsgmem(&msg);
				sockprintf(socket,"+OK");
				if(startup->options&MAIL_OPT_DEBUG_POP3)
					lprintf("%04d POP3 message deleted", socket);
			lprintf("%04d !POP3 UNSUPPORTED COMMAND from %s: '%s'"
				,socket, user.alias, buf);
			sockprintf(socket,"-ERR UNSUPPORTED COMMAND: %s",buf);
		}
		if(user.number)
			logoutuserdat(&scfg,&user,time(NULL),client.time);
	if(activity) 
		lprintf("%04d POP3 %s logged out from port %u on %s [%s]"
			,socket, user.alias, ntohs(pop3.client_addr.sin_port), host_name, host_ip);

	status(STATUS_WFC);

	/* Free up resources here */
	if(mail!=NULL)
		freemail(mail);

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

	active_clients--;
	update_clients();
	client_off(socket);

	thread_down();
	if(startup->options&MAIL_OPT_DEBUG_POP3)
		lprintf("%04d POP3 session thread terminated (%u threads remain)"
			,socket, thread_count);

	/* Must be last */
	mail_close_socket(socket);
static BOOL rblchk(DWORD mail_addr_n, char* rbl_addr)
{
	char		name[256];
	DWORD		mail_addr;

	mail_addr=ntohl(mail_addr_n);
	sprintf(name,"%ld.%ld.%ld.%ld.%s"
		,mail_addr&0xff
		,(mail_addr>>8)&0xff
		,(mail_addr>>16)&0xff
		,(mail_addr>>24)&0xff
		,rbl_addr
		);

	if(gethostbyname(name)==NULL)
		return(FALSE);

	return(TRUE);
}


static BOOL chk_email_addr(SOCKET socket, char* p, char* host_name, char* host_ip, char* to)
{
	char	addr[64];
	char	tmp[128];
	char*	tp;

	while(*p && *p<=' ') p++;
	if(*p=='<') p++;		/* Skip '<' */
	sprintf(addr,"%.*s",sizeof(addr)-1,p);
	tp=strchr(addr,'>');
	if(tp!=NULL) *tp=0;

	if(!trashcan(&scfg,addr,"email"))
		return(TRUE);

	lprintf("%04d !SMTP BLOCKED SOURCE: %s"
		,socket, addr);
	sprintf(tmp,"Blocked source e-mail address: %s", addr);
	spamlog(&scfg, "SMTP", tmp, host_name, host_ip, to);
	sockprintf(socket, "554 Sender not allowed.");

	return(FALSE);
}

static void signal_smtp_sem(void)
	if(scfg.smtpmail_sem[0]==0) 
	if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC))!=-1)
static void smtp_thread(void* arg)
{
	int			i,j,x;
	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		month[16];
	char		rcpt_name[128];
	char		rcpt_addr[128];
	char		hello_name[128];
	char		relay_list[MAX_PATH+1];
	char		domain_list[MAX_PATH+1];
	char		host_name[128];
	char		host_ip[64];
	char		dest_host[128];
	ushort		dest_port;
	int			addr_len;
	ushort		xlat;
	ushort		nettype=NET_NONE;
	uint		usernum;
	ulong		crc;
	ulong		length;
	ulong		offset;
	BOOL		esmtp=FALSE;
	FILE*		msgtxt=NULL;
	FILE*		rcptlst;
	FILE*		spy=NULL;
	SOCKET		socket;
	HOSTENT*	host;
	smb_t		smb;
	smbmsg_t	msg;
	smbmsg_t	newmsg;
	user_t		user;
	client_t	client;
	struct tm	tm;
	smtp_t		smtp=*(smtp_t*)arg;
	SOCKADDR_IN server_addr;
	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;

	thread_up();

	free(arg);

	socket=smtp.socket;

	lprintf("%04d SMTP RX 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("%04d !SMTP ERROR %d (%d) getting address/port"
			,socket, i, ERROR_VALUE);
		sockprintf(socket,"421 System error");
		mail_close_socket(socket);
		thread_down();
	memset(&smb,0,sizeof(smb));
	memset(&msg,0,sizeof(msg));
	memset(&user,0,sizeof(user));

	strcpy(host_ip,inet_ntoa(smtp.client_addr.sin_addr));

	lprintf("%04d SMTP connection accepted from: %s port %u"
		, socket, 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
			,sizeof(smtp.client_addr.sin_addr),AF_INET);

	if(host!=NULL && host->h_name!=NULL)
		sprintf(host_name,"%.*s",(int)sizeof(host_name)-1,host->h_name);
	else
		strcpy(host_name,"<no name>");

	if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP))
		lprintf("%04d SMTP hostname: %s", socket, host_name);

	strcpy(hello_name,host_name);

		lprintf("%04d !SMTP BLOCKED SERVER IP ADDRESS: %s"
		sockprintf(socket,"550 Access denied.");
	if(trashcan(&scfg,host_name,"host")) {
		lprintf("%04d !SMTP BLOCKED SERVER HOSTNAME: %s"
		sockprintf(socket,"550 Access denied.");
	/*  SPAM Filters (mail-abuse.org) */
	if(startup->options&MAIL_OPT_USE_RBL
		&& rblchk(smtp.client_addr.sin_addr.s_addr
		lprintf("%04d !SMTP SPAM server filtered (RBL): %s [%s]"
			,socket, host_name, host_ip);
		spamlog(&scfg, "SMTP", "Listed on RBL (http://mail-abuse.org/rbl)"
			,host_name, host_ip, NULL);
		sockprintf(socket
			,"571 Mail from %s refused, see http://mail-abuse.org/rbl"
		thread_down();
		return;
	}
	if(startup->options&MAIL_OPT_USE_DUL
		&& rblchk(smtp.client_addr.sin_addr.s_addr
		lprintf("%04d !SMTP SPAM server filtered (DUL): %s [%s]"
			,socket, host_name, host_ip);
		spamlog(&scfg, "SMTP", "Listed on DUL (http://mail-abuse.org/dul)"
			,host_name, host_ip, NULL);
		sockprintf(socket
			,"571 Mail from %s refused, see http://mail-abuse.org/dul"
		thread_down();
		return;
	}
	if(startup->options&MAIL_OPT_USE_RSS
		&& rblchk(smtp.client_addr.sin_addr.s_addr
			,"relays.mail-abuse.org"
			)==TRUE) {
		lprintf("%04d !SMTP SPAM server filtered (RSS): %s [%s]"
			,socket, host_name, host_ip);
		spamlog(&scfg, "SMTP", "Listed on RSS (http://mail-abuse.org/rss)"
			,host_name, host_ip, NULL);
		sockprintf(socket
			,"571 Mail from %s refused, see http://mail-abuse.org/rss"
		thread_down();
		return;
	}

	sprintf(rcptlst_fname,"%sSMTP%d.LST", scfg.data_dir, socket);
	rcptlst=fopen(rcptlst_fname,"w+");
	if(rcptlst==NULL) {
		lprintf("%04d !SMTP ERROR %d creating recipient list: %s"
			,socket, errno, rcptlst_fname);
		sockprintf(socket,"421 System error");
	if(trashcan(&scfg,host_name,"smtpspy") 
		|| trashcan(&scfg,host_ip,"smtpspy")) {
		sprintf(str,"%sSMTPSPY.TXT", scfg.data_dir);
		spy=fopen(str,"a");
	}

	active_clients++;
	update_clients();

	/* Initialize client display */
	client.size=sizeof(client);
	client.time=time(NULL);
	sprintf(client.addr,"%.*s",(int)sizeof(client.addr)-1,host_ip);
	sprintf(client.host,"%.*s",(int)sizeof(client.host)-1,host_name);
	client.port=ntohs(smtp.client_addr.sin_port);
	client.protocol="SMTP";
	client.user="<unknown>";
	client_on(socket,&client);

	sprintf(str,"SMTP: %s",host_ip);
	status(str);

	sockprintf(socket,"220 %s Synchronet SMTP Server for %s v%s Ready"
		,scfg.sys_inetaddr,PLATFORM_DESC,MAIL_VERSION);
	while(1) {
		rd = sockreadline(socket, buf, sizeof(buf));
		if(rd<1) 
			break;
		if(spy!=NULL)
			fprintf(spy,"%s\n",buf);
		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;

				if(msgtxt==NULL) {
					lprintf("%04d !SMTP NO MESSAGE TEXT FILE POINTER?", socket);
					sockprintf(socket,"554 No message text");
					continue;
				}

				if(ftell(msgtxt)<1) {
					lprintf("%04d !SMTP INVALID MESSAGE LENGTH: %ld (%lu lines)"
						, socket, ftell(msgtxt), lines);
					sockprintf(socket,"554 No message text");
				lprintf("%04d SMTP End of message (%lu lines, %lu bytes)"
					, socket, lines, ftell(msgtxt));
				if(telegram==TRUE) {		/* Telegram */
					const char* head="\1n\1h\1cInstant Message\1n from \1h\1y";
					const char* tail="\1n:\r\n\1h";
					rewind(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 */
						sprintf(str,"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s"
							,head,sender_addr,host_ip,host_name,tail);
						sprintf(str,"%s%s%s",head,sender_addr,tail);
					if((telegram_buf=(char*)malloc(length+strlen(str)+1))==NULL) {
						lprintf("%04d !SMTP ERROR allocating %lu bytes of memory for telegram from %s"
							,socket,length+strlen(str)+1,sender_addr);
						sockprintf(socket, "452 Insufficient system storage");
					strcpy(telegram_buf,str);
					if(fread(telegram_buf+strlen(str),1,length,msgtxt)!=length) {
						lprintf("%04d !SMTP ERROR reading %lu bytes from telegram file"
							,socket,length);
						sockprintf(socket, "452 Insufficient system storage");
						free(telegram_buf);
						continue; 
					}
					telegram_buf[length+strlen(str)]=0;	/* Need ASCIIZ */
					/* Send telegram to users */
					rewind(rcptlst);
					while(!feof(rcptlst)) {
						if(fgets(str,sizeof(str)-1,rcptlst)==NULL)
							break;
						usernum=atoi(str);
						if(fgets(rcpt_name,sizeof(rcpt_name)-1,rcptlst)==NULL)
							break;
						if(fgets(rcpt_addr,sizeof(rcpt_addr)-1,rcptlst)==NULL)
							break;
						truncsp(rcpt_addr);
						putsmsg(&scfg,usernum,telegram_buf);
						lprintf("%04d SMTP Created telegram from %s to %s <%s>"
							,socket, sender_addr, rcpt_name, rcpt_addr);
					free(telegram_buf);
					sockprintf(socket,SMTP_OK);
					telegram=FALSE;
					continue;
				}

				if(sender[0]==0) {
					lprintf("%04d !SMTP MISSING mail header 'FROM' field", socket);
					sockprintf(socket, "554 Mail header missing 'FROM' field");
					continue;
				}
				nettype=NET_INTERNET;
				smb_hfield(&msg, SENDER, (ushort)strlen(sender), sender);
				smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype);
				smb_hfield(&msg, SENDERNETADDR, (ushort)strlen(sender_addr), sender_addr);
				if(msg.idx.subj==0) {
					p="";
					smb_hfield(&msg, SUBJECT, 0, p);
					msg.idx.subj=crc16(p);
				}
				length=filelength(fileno(msgtxt));

				if(subnum!=INVALID_SUB) {	/* Message Base */
					if(rcpt_name[0]==0)
						strcpy(rcpt_name,"All");
					smb_hfield(&msg, RECIPIENT, (ushort)strlen(rcpt_name), rcpt_name);

					if((msgbuf=(char*)malloc(length+1))==NULL) {
						lprintf("%04d !SMTP ERROR allocating %d bytes of memory"
							,socket,length+1);
						sockprintf(socket, "452 Insufficient system storage");
						subnum=INVALID_SUB;
						continue;
					}
					fread(msgbuf,length,1,msgtxt);
					msgbuf[length]=0;	/* ASCIIZ */
					smb.subnum=subnum;
					if((i=savemsg(&scfg, &smb, &msg, msgbuf))!=0) {
						lprintf("%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("%04d SMTP %s posted a message on %s"
							,socket, sender_addr, scfg.sub[subnum]->sname);
						sockprintf(socket,SMTP_OK);
					}
					free(msgbuf);
					smb_close(&smb);
					subnum=INVALID_SUB;
					continue;
				}

				/* E-mail */
				sprintf(smb.file,"%smail", scfg.data_dir);
				smb.retry_time=scfg.smb_retry_time;
				if((i=smb_open(&smb))!=0) {
					lprintf("%04d !SMTP ERROR %d (%s) opening %s"
						,socket, i, smb.last_error, smb.file);
					sockprintf(socket, "452 Insufficient system storage");
					continue;
				}
				if(smb_fgetlength(smb.shd_fp)<1) {	 /* Create it if it doesn't exist */
					smb.status.max_crcs=scfg.mail_maxcrcs;
					smb.status.max_age=scfg.mail_maxage;
					smb.status.max_msgs=MAX_SYSMAIL;
					smb.status.attr=SMB_EMAIL;
					if((i=smb_create(&smb))!=0) {
						smb_close(&smb);
							,socket, i, smb.file);
						sockprintf(socket, "452 Insufficient system storage");
				if((i=smb_locksmbhdr(&smb))!=0) {
					smb_close(&smb);
					lprintf("%04d !SMTP ERROR %d locking %s"
						,socket, i, smb.file);
					sockprintf(socket, "452 Insufficient system storage");
					continue; 
				}
				length+=sizeof(xlat);	 /* +2 for translation string */

				if((i=smb_open_da(&smb))!=0) {
					smb_unlocksmbhdr(&smb);
					smb_close(&smb);
					lprintf("%04d !SMTP ERROR %d (%s) opening %s.sda"
						,socket, i, smb.last_error, smb.file);
					sockprintf(socket, "452 Insufficient system storage");
					continue; 
				}
				if(scfg.sys_misc&SM_FASTMAIL)
					offset=smb_fallocdat(&smb,length,1);
				else
					offset=smb_allocdat(&smb,length,1);

				smb_fseek(smb.sdt_fp,offset,SEEK_SET);
				xlat=XLAT_NONE;
				smb_fwrite(&xlat,2,smb.sdt_fp);
				x=SDT_BLOCK_LEN-2;				/* Don't read/write more than 255 */
				while(!feof(msgtxt)) {
					memset(buf,0,x);
					j=fread(buf,1,x,msgtxt);
					if(j<1)
						break;
					if(j>1 && (j!=x || feof(msgtxt)) && buf[j-1]=='\n' && buf[j-2]=='\r')
					if(scfg.mail_maxcrcs) {
					smb_fwrite(buf,j,smb.sdt_fp);
					x=SDT_BLOCK_LEN; 
				}
				smb_fflush(smb.sdt_fp);
				crc=~crc;
				if(scfg.mail_maxcrcs) {
					i=smb_addcrc(&smb,crc);
					if(i) {
						smb_freemsgdat(&smb,offset,length,1);
						smb_unlocksmbhdr(&smb);
						smb_close(&smb);
						lprintf("%04d !SMTP DUPLICATE MESSAGE", socket);
						sockprintf(socket, "554 Duplicate Message");
						continue; 
					} 
				}
				rcpt_count=0;
				while(!feof(rcptlst) && rcpt_count<MAX_RECIPIENTS) {
					if((i=smb_copymsgmem(&newmsg,&msg))!=0) {
						lprintf("%04d !SMTP ERROR %d copying message"
							,socket, i);
						break;
					}
					if(fgets(str,sizeof(str)-1,rcptlst)==NULL)
						break;
					usernum=atoi(str);
					fgets(rcpt_name,sizeof(rcpt_name)-1,rcptlst);
					if(fgets(rcpt_addr,sizeof(rcpt_addr)-1,rcptlst)==NULL)
						break;
					truncsp(rcpt_name);
					truncsp(rcpt_addr);

					snprintf(hdrfield,sizeof(hdrfield),
						"Received: from %s (%s [%s])\r\n"
						"          by %s [%s] (Synchronet Mail Server for %s v%s) with %s\r\n"
						"          for %s; %s"
						,host_name,hello_name,host_ip
						,scfg.sys_inetaddr,inet_ntoa(server_addr.sin_addr)
						,PLATFORM_DESC,MAIL_VERSION
						,esmtp ? "ESMTP" : "SMTP"
						,rcpt_name,msgdate(msg.hdr.when_imported,date));
					smb_hfield(&newmsg, RFC822HEADER, (ushort)strlen(hdrfield), hdrfield);

					smb_hfield(&newmsg, RECIPIENT, (ushort)strlen(rcpt_name), rcpt_name);

					if(rcpt_addr[0]=='#') {	/* Local destination */
						newmsg.idx.to=atoi(rcpt_addr+1);
						smb_hfield(&newmsg, RECIPIENTEXT
							,(ushort)strlen(rcpt_addr+1), rcpt_addr+1);
					} else {
						newmsg.idx.to=0;
						nettype=NET_INTERNET;
						smb_hfield(&newmsg, RECIPIENTNETTYPE, nettype, &nettype);
						smb_hfield(&newmsg, RECIPIENTNETADDR
							,(ushort)strlen(rcpt_addr), rcpt_addr);
					}
					i=smb_addmsghdr(&smb,&newmsg,SMB_SELFPACK);
					smb_freemsgmem(&newmsg);
					if(i!=0) {
						lprintf("%04d !SMTP ERROR %d adding message header"
							,socket, i);
						break;
					lprintf("%04d SMTP Created message #%ld from %s to %s <%s>"
						,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
					if(usernum) {
						sprintf(str,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: "
							"\1h%s\1n\r\n"
							,timestr(&scfg,(time_t*)&newmsg.hdr.when_imported.time,tmp)
							,sender,sender_addr);
						if(!newmsg.idx.to) {	/* Forwarding */
							strcat(str,"\1mand it was automatically forwaded to: \1h");
							strcat(str,user.netmail);
							strcat(str,"\1n\r\n");
						}
						putsmsg(&scfg, usernum, str);
				if(rcpt_count<1) {
					smb_freemsgdat(&smb,offset,length,0);
					sockprintf(socket, "452 Insufficient system storage");
				}
				else {
					if(rcpt_count>1)
						smb_incdat(&smb,offset,length,(ushort)(rcpt_count-1));
					sockprintf(socket,SMTP_OK);
				}
				smb_close_da(&smb);
				smb_close(&smb);
				continue;
			}
			if(buf[0]==0 && state==SMTP_STATE_DATA_HEADER) {	
				state=SMTP_STATE_DATA_BODY;	/* Null line separates header and body */
				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);
				lines++;
				if(!(lines%LINES_PER_YIELD))		/* release time-slices every x lines */
					mswait(1);
				continue;
			}
			/* RFC822 Header parsing */
			if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
				lprintf("%04d SMTP %s",socket, buf);

			if(!strnicmp(buf, "SUBJECT:",8)) {
				p=buf+8;
				while(*p && *p<=' ') p++;
				if(trashcan(&scfg,p,"subject")) {
					lprintf("%04d !SMTP BLOCKED SUBJECT (%s) from: %s"
						,socket, p, reverse_path);
					sprintf(tmp,"Blocked subject (%s) from: %s"
						,p, reverse_path);
					spamlog(&scfg, "SMTP", tmp, host_name, host_ip, rcpt_addr);
					sockprintf(socket, "554 Subject not allowed.");
					break;
				}
				smb_hfield(&msg, SUBJECT, (ushort)strlen(p), p);
				strlwr(p);
				msg.idx.subj=crc16(p);
				continue;
			}
			if(!strnicmp(buf, "TO:",3)) {
				p=buf+3;
				while(*p && *p<=' ') p++;
				if(*p=='<')  {
					p++;
					tp=strrchr(p,'>');
					if(tp) *tp=0;
					sprintf(rcpt_name,"%.*s",sizeof(rcpt_name)-1,p);
				}
				continue;
			}
			if(!strnicmp(buf, "REPLY-TO:",9)) {
				p=buf+9;
				while(*p && *p<=' ') p++;
				if(*p=='<')  {
					p++;
					tp=strchr(p,'>');
					if(tp) *tp=0;
				}
				nettype=NET_INTERNET;
				smb_hfield(&msg, REPLYTONETTYPE, nettype, &nettype);
				smb_hfield(&msg, REPLYTONETADDR, (ushort)strlen(p), p);
				continue;
			}
			if(!strnicmp(buf, "FROM:", 5)) {
				if(!chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr))
				p=strchr(buf+5,'<');
				if(p) {
					p++;
					tp=strchr(p,'>');
					if(tp) *tp=0;
					sprintf(sender_addr,"%.*s",(int)sizeof(sender_addr)-1,p);
				} else {
					p=buf+5;
					while(*p && *p<=' ') p++;
					sprintf(sender_addr,"%.*s",(int)sizeof(sender_addr)-1,p);
				}
			
				p=buf+5;
				while(*p && *p<=' ') p++;
				if(*p=='"') { 
					p++;
					tp=strchr(p,'"');
				} else if(*p=='<') {
					p++;
					tp=strchr(p,'>');
				} else
					tp=strchr(p,'<');
				if(tp) *tp=0;
				truncsp(p);
				sprintf(sender,"%.*s",(int)sizeof(sender)-1,p);
				continue;
			}
			if(!strnicmp(buf, "DATE:",5)) {
				p=buf+5;
				while(*p && *p<=' ') p++;
/*				lprintf("Sent: %s",p); */
				memset(&tm,0,sizeof(tm));
				while(*p && !isdigit(*p)) p++;
				/* DAY */
				tm.tm_mday=atoi(p);
				while(*p && isdigit(*p)) p++;
				/* MONTH */
				while(*p && *p<=' ') p++;
				sprintf(month,"%3.3s",p);
				if(!stricmp(month,"jan"))
					tm.tm_mon=0;
				else if(!stricmp(month,"feb"))
					tm.tm_mon=1;
				else if(!stricmp(month,"mar"))
					tm.tm_mon=2;
				else if(!stricmp(month,"apr"))
					tm.tm_mon=3;
				else if(!stricmp(month,"may"))
					tm.tm_mon=4;
				else if(!stricmp(month,"jun"))
					tm.tm_mon=5;
				else if(!stricmp(month,"jul"))
					tm.tm_mon=6;
				else if(!stricmp(month,"aug"))
					tm.tm_mon=7;
				else if(!stricmp(month,"sep"))
					tm.tm_mon=8;
				else if(!stricmp(month,"oct"))
					tm.tm_mon=9;
				else if(!stricmp(month,"nov"))
					tm.tm_mon=10;
				else
					tm.tm_mon=11;
				p+=4;
				/* YEAR */
				tm.tm_year=atoi(p);
				if(tm.tm_year>1900)
					tm.tm_year-=1900;
				while(*p && isdigit(*p)) p++;
				/* HOUR */
				while(*p && *p<=' ') p++;
				tm.tm_hour=atoi(p);
				while(*p && isdigit(*p)) p++;
				/* MINUTE */
				if(*p) p++;
				tm.tm_min=atoi(p);
				while(*p && isdigit(*p)) p++;
				/* SECONDS */
				if(*p) p++;
				tm.tm_sec=atoi(p);
				while(*p && isdigit(*p)) p++;
				/* TIME ZONE */
				while(*p && *p<=' ') p++;
				if(*p && (isdigit(*p) || *p=='-')) { /* HHMM or -HHMM format */
					sprintf(str,"%.*s",*p=='-'? 3:2,p);
					msg.hdr.when_written.zone=atoi(str)*60;
					p+=(*p=='-') ? 3:2;
					msg.hdr.when_written.zone+=atoi(p);
				}
				else if(!strnicmp(p,"PDT",3))
					msg.hdr.when_written.zone=(short)PDT;
				else if(!strnicmp(p,"MDT",3))
					msg.hdr.when_written.zone=(short)MDT;
				else if(!strnicmp(p,"CDT",3))
					msg.hdr.when_written.zone=(short)CDT;
				else if(!strnicmp(p,"EDT",3))
					msg.hdr.when_written.zone=(short)EDT;
				else if(!strnicmp(p,"PST",3))
					msg.hdr.when_written.zone=(short)PST;
				else if(!strnicmp(p,"MST",3))
					msg.hdr.when_written.zone=(short)MST;
				else if(!strnicmp(p,"CST",3))
					msg.hdr.when_written.zone=(short)CST;
				else if(!strnicmp(p,"EST",3))
					msg.hdr.when_written.zone=(short)EST;
				msg.hdr.when_written.time=mktime(&tm);
				continue;
			}
			if(!strnicmp(buf, "MESSAGE-ID:",11)) {
				p=buf+11;
				while(*p && *p<=' ') p++;
				smb_hfield(&msg, RFC822MSGID, (ushort)strlen(p), p);
				continue;
			}
			if(!strnicmp(buf, "IN-REPLY-TO:",12)) {
				p=buf+12;
				while(*p && *p<=' ') p++;
				smb_hfield(&msg, RFC822REPLYID, (ushort)strlen(p), p);
				continue;
			}
			/* Fall-through */
			smb_hfield(&msg, RFC822HEADER, (ushort)strlen(buf), buf);
			continue;
		}
		lprintf("%04d SMTP RX: %s", socket, buf);
		if(!strnicmp(buf,"HELO",4)) {
			p=buf+4;
			while(*p && *p<=' ') p++;
			sprintf(hello_name,"%.*s",(int)sizeof(hello_name)-1,p);
			sockprintf(socket,"250 %s",scfg.sys_inetaddr);
			esmtp=FALSE;
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
			continue;
		}
		if(!strnicmp(buf,"EHLO",4)) {
			p=buf+4;
			while(*p && *p<=' ') p++;
			sprintf(hello_name,"%.*s",(int)sizeof(hello_name)-1,p);
			sockprintf(socket,"250 %s",scfg.sys_inetaddr);
			esmtp=TRUE;
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
			continue;
		}
		if(!stricmp(buf,"QUIT")) {
			sockprintf(socket,"221 %s Service closing transmission channel",scfg.sys_inetaddr);
			break;
		} 
		if(!stricmp(buf,"NOOP")) {
			sockprintf(socket, SMTP_OK);
			continue;
		}
		if(state<SMTP_STATE_HELO) {
			/* RFC 821 4.1.1 "The first command in a session must be the HELO command." */
			lprintf("%04d !SMTP MISSING 'HELO' command",socket);
			sockprintf(socket, SMTP_BADSEQ);
			continue;
		}
		if(!stricmp(buf,"TURN")) {
			sockprintf(socket,"502 command not supported");
			continue;
		}
		if(!stricmp(buf,"RSET")) {
			smb_freemsgmem(&msg);
			memset(&msg,0,sizeof(smbmsg_t));		/* Initialize message header */
			reverse_path[0]=0;
			sockprintf(socket,SMTP_OK);
			rewind(rcptlst);
			chsize(fileno(rcptlst),0);
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
			continue;
		}
		if(!strnicmp(buf,"MAIL FROM:",10)) {
			p=buf+10;
			if(!chk_email_addr(socket,p,host_name,host_ip,NULL))
			while(*p && *p<=' ') p++;
			sprintf(reverse_path,"%.*s",(int)sizeof(reverse_path)-1,p);

			/* Update client display */
			client.user=reverse_path;
			client_on(socket,&client);

			sockprintf(socket,SMTP_OK);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_MAIL_FROM;
			cmd=SMTP_CMD_MAIL;

			smb_freemsgmem(&msg);
			memset(&msg,0,sizeof(smbmsg_t));		/* Initialize message header */
			memcpy(msg.hdr.id,"SHD\x1a",4);
			msg.hdr.version=smb_ver();
			msg.hdr.when_imported.time=time(NULL);
			msg.hdr.when_imported.zone=scfg.sys_timezone;

		/* Send a Message (Telegram) to a local ONLINE user */
		if(!strnicmp(buf,"SEND FROM:",10)) {
			p=buf+10;
			if(!chk_email_addr(socket,p,host_name,host_ip,NULL))
			while(*p && *p<=' ') p++;
			sprintf(reverse_path,"%.*s",(int)sizeof(reverse_path)-1,p);
			sockprintf(socket,SMTP_OK);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_MAIL_FROM;
			cmd=SMTP_CMD_SEND;
			continue;
		}
		/* Send OR Mail a Message to a local user */
		if(!strnicmp(buf,"SOML FROM:",10)) {
			p=buf+10;
			if(!chk_email_addr(socket,p,host_name,host_ip,NULL))
			while(*p && *p<=' ') p++;
			sprintf(reverse_path,"%.*s",(int)sizeof(reverse_path)-1,p);
			sockprintf(socket,SMTP_OK);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_MAIL_FROM;
			cmd=SMTP_CMD_SOML;
			continue;
		}
		/* Send AND Mail a Message to a local user */
		if(!strnicmp(buf,"SAML FROM:",10)) {
			p=buf+10;
			if(!chk_email_addr(socket,p,host_name,host_ip,NULL))
			while(*p && *p<=' ') p++;
			sprintf(reverse_path,"%.*s",(int)sizeof(reverse_path)-1,p);
			sockprintf(socket,SMTP_OK);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_MAIL_FROM;
			cmd=SMTP_CMD_SAML;
#if 0	/* No one uses this command */
		if(!strnicmp(buf,"VRFY",4)) {
			p=buf+4;
			while(*p && *p<=' ') p++;
			if(*p==0) {
				sockprintf(socket,"550 No user specified.");
				continue;
			}
#endif



		if(!strnicmp(buf,"RCPT TO:",8)) {

rswindell's avatar
rswindell committed
			if(state<SMTP_STATE_MAIL_FROM) {
				lprintf("%04d !SMTP MISSING 'MAIL' command",socket);
				sockprintf(socket, SMTP_BADSEQ);
				continue;
			}

			if(spy==NULL && trashcan(&scfg,reverse_path,"smtpspy")) {
				sprintf(str,"%sSMTPSPY.TXT", scfg.data_dir);
				spy=fopen(str,"a");
			}

rswindell's avatar
rswindell committed
			p=buf+8;
			while(*p && *p<=' ') p++;
rswindell's avatar
rswindell committed
			sprintf(str,"%.*s",(int)sizeof(str)-1,p);
			p=str;

			if(*p=='<') p++;				/* Skip '<' */
			tp=strchr(str,'>');				/* Truncate '>' */
			if(tp!=NULL) *tp=0;

rswindell's avatar
rswindell committed
			sprintf(rcpt_addr,"%.*s",sizeof(rcpt_addr)-1,p);

			/* Check for blocked recipients */
			if(trashcan(&scfg,rcpt_addr,"email")) {
				lprintf("%04d !SMTP BLOCKED RECIPIENT (%s) from: %s"
					,socket, rcpt_addr, reverse_path);
				sprintf(str,"Blocked recipient e-mail address from: %s",reverse_path);
				spamlog(&scfg, "SMTP", str, host_name, host_ip, rcpt_addr);
				sockprintf(socket, "550 Unknown User:%s", buf+8);
				continue;
			}

			/* Check for full address aliases */
			p=alias(&scfg,p,alias_buf);
			if(p==alias_buf) 
				lprintf("%04d SMTP ADDRESS ALIAS: %s",socket,p);
rswindell's avatar
rswindell committed

			tp=strrchr(p,'@');
			if(cmd==SMTP_CMD_MAIL && tp!=NULL) {
				sprintf(dest_host,"%.*s",sizeof(dest_host),tp+1);
				cp=strrchr(dest_host,':');
				if(cp!=NULL) {
					*cp=0;
					dest_port=atoi(cp+1);
				}
				sprintf(domain_list,"%sdomains.cfg",scfg.ctrl_dir);
				if((stricmp(dest_host,scfg.sys_inetaddr)!=0
						&& resolve_ip(dest_host)!=server_addr.sin_addr.s_addr
						&& findstr(&scfg,dest_host,domain_list)==FALSE)