Skip to content
Snippets Groups Projects
mailsrvr.c 97.7 KiB
Newer Older
				}
				if(msg.idx.attr&MSG_DELETE) {
					lprintf("%04d !POP3 ATTEMPT to retrieve DELETED message"
						,socket);
					sockprintf(socket,"-ERR message deleted");
					continue;
				}
				if((i=smb_lockmsghdr(&smb,&msg))!=0) {
					lprintf("%04d !POP3 ERROR %d (%s) locking message header #%lu"
						,socket, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR %d locking message header",i);
					continue; 
				}
				i=smb_getmsghdr(&smb,&msg);
				smb_unlockmsghdr(&smb,&msg);
				if(i!=0) {
					lprintf("%04d !POP3 ERROR %d (%s) getting message header #%lu"
						,socket, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR %d getting message header",i);
					continue;
				}

				if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_TAILS))==NULL) {
					smb_freemsgmem(&msg);
					lprintf("%04d !POP3 ERROR (%s) retrieving message %lu text"
						,socket, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR retrieving message text");
					continue;
				}

				sockprintf(socket,"+OK message follows");
				lprintf("%04d POP3 sending message text (%u bytes)"
					,socket,strlen(msgtxt));
				lines_sent=sockmsgtxt(socket,&msg,msgtxt,lines);
				/* if(startup->options&MAIL_OPT_DEBUG_POP3) */
				if(lines!=-1 && lines_sent<lines)	/* could send *more* lines */
					lprintf("%04d !POP3 ERROR sending message text (sent %ld of %ld lines)"
						,socket,lines_sent,lines);
					lprintf("%04d POP3 message transfer complete (%lu lines)"
						,socket,lines_sent);
					msg.hdr.attr|=MSG_READ;
					msg.idx.attr=msg.hdr.attr;
					msg.hdr.netattr|=MSG_SENT;
					if((i=smb_lockmsghdr(&smb,&msg))!=0) 
						lprintf("%04d !POP3 ERROR %d (%s) locking message header #%lu"
							,socket, i, smb.last_error, msg.hdr.number);
					if((i=smb_putmsg(&smb,&msg))!=0)
						lprintf("%04d !POP3 ERROR %d (%s) marking message #%lu as read"
							,socket, i, smb.last_error, msg.hdr.number);
					smb_unlockmsghdr(&smb,&msg);
				}
				smb_freemsgmem(&msg);
				smb_freemsgtxt(msgtxt);
				continue;
			}
			if(!strnicmp(buf, "DELE ",5)) {
				p=buf+5;
				while(*p && *p<=' ') p++;
				msgnum=atol(p);

				if(msgnum<1 || msgnum>msgs) {
					lprintf("%04d !POP3 %s ATTEMPTED to delete an INVALID message #%ld"
						,socket, user.alias, msgnum);
					sockprintf(socket,"-ERR no such message");
					continue;
				}
				msg.hdr.number=mail[msgnum-1].number;

				lprintf("%04d POP3 %s deleting message #%ld"
					,socket, user.alias, msg.hdr.number);

				if((i=smb_getmsgidx(&smb,&msg))!=0) {
					lprintf("%04d !POP3 ERROR %d (%s) getting message index"
						,socket, i, smb.last_error);
					sockprintf(socket,"-ERR %d getting message index",i);
					continue;
				}
				if((i=smb_lockmsghdr(&smb,&msg))!=0) {
					lprintf("%04d !POP3 ERROR %d (%s) locking message header #%lu"
						,socket, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR %d locking message header",i);
					continue; 
				}
				if((i=smb_getmsghdr(&smb,&msg))!=0) {
					smb_unlockmsghdr(&smb,&msg);
					lprintf("%04d !POP3 ERROR %d (%s) getting message header #%lu"
						,socket, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR %d getting message header",i);
					continue;
				}
				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 (%s) marking message as read"
						, socket, i, smb.last_error);
					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, %lu clients served)"
			,socket, thread_count, served);
static ulong rblchk(SOCKET sock, DWORD mail_addr_n, const 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(startup->options&MAIL_OPT_DNSBL_DEBUG)
		lprintf("%04d SMTP DNSBL query: %s",sock,name);

	if((host=gethostbyname(name))==NULL)
		return(0);
	dnsbl_result.s_addr = *((ulong*)host->h_addr_list[0]);
	lprintf("%04d SMTP DNSBL query: %s resolved to: %s"
		,sock,name,inet_ntoa(dnsbl_result));

	return(dnsbl_result.s_addr);
static ulong dns_blacklisted(SOCKET sock, IN_ADDR addr, char* host_name, char* list)
{
	char	fname[MAX_PATH+1];
	char	str[256];
	char*	p;
	char*	tp;
	FILE*	fp;
	sprintf(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
	if(findstr(inet_ntoa(addr),fname))
		return(FALSE);
	if(findstr(host_name,fname))
		return(FALSE);
	sprintf(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir);
	if((fp=fopen(fname,"r"))==NULL)
		return(FALSE);

	while(!feof(fp) && !found) {
		if(fgets(str,sizeof(str),fp)==NULL)
			break;
		truncsp(str);

		p=str;
		while(*p && *p<=' ') p++;
		if(*p==';' || *p==0) /* comment or blank line */
			continue;

		sprintf(list,"%.100s",p);

		/* terminate */
		tp = p;
		while(*tp && *tp>' ') tp++;
		*tp=0;	

		found = rblchk(sock, addr.s_addr, p);
static BOOL chk_email_addr(SOCKET socket, char* p, char* host_name, char* host_ip
						   ,char* to, char* from)
{
	char	addr[64];
	char	tmp[128];

	while(*p && *p<=' ') p++;
	if(*p=='<') p++;		/* Skip '<' */
	SAFECOPY(addr,p);

	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", "REFUSED", tmp, host_name, host_ip, to, from);
	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)
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments             */
/*****************************************************************************/
static char* cmdstr(char* instr, char* msgpath, char* lstpath, char* errpath, char* cmd)
{
	char	str[256];
    int		i,j,len;

    len=strlen(instr);
    for(i=j=0;i<len;i++) {
        if(instr[i]=='%') {
            i++;
            cmd[j]=0;
            switch(toupper(instr[i])) {
				case 'E':
					strcat(cmd,errpath);
					break;
                case 'G':   /* Temp directory */
                    strcat(cmd,scfg.temp_dir);
                    break;
                case 'J':
                    strcat(cmd,scfg.data_dir);
                    break;
                case 'K':
                    strcat(cmd,scfg.ctrl_dir);
                    break;
				case 'L':
					strcat(cmd,lstpath);
					break;
				case 'F':
				case 'M':
					strcat(cmd,msgpath);
					break;
                case 'O':   /* SysOp */
                    strcat(cmd,scfg.sys_op);
                    break;
                case 'Q':   /* QWK ID */
                    strcat(cmd,scfg.sys_id);
                    break;
                case 'V':   /* Synchronet Version */
                    sprintf(str,"%s%c",VERSION,REVISION);
                    break;
                case 'Z':
                    strcat(cmd,scfg.text_dir);
                    break;
                case '!':   /* EXEC Directory */
                    strcat(cmd,scfg.exec_dir);
                    break;
                case '%':   /* %% for percent sign */
                    strcat(cmd,"%");
                    break;
				case '?':	/* Platform */
#ifdef __OS2__
					SAFECOPY(str,"OS2");
#else
					SAFECOPY(str,PLATFORM_DESC);
#endif
					strlwr(str);
					strcat(cmd,str);
					break;
                default:    /* unknown specification */
                    break; 
			}
            j=strlen(cmd); 
		}
        else
            cmd[j++]=instr[i]; 
	}
    cmd[j]=0;

    return(cmd);
}


static int parse_header_field(char* buf, smbmsg_t* msg)
{
	char*	p;
	ushort	nettype;

	if(!strnicmp(buf, "TO:",3)) {
		p=buf+3;
		while(*p && *p<=' ') p++;
		truncsp(p);
		return smb_hfield(msg, RFC822TO, (ushort)strlen(p), p);
	}
	if(!strnicmp(buf, "REPLY-TO:",9)) {
		p=buf+9;
		while(*p && *p<=' ') p++;
		truncsp(p);
		smb_hfield(msg, RFC822REPLYTO, (ushort)strlen(p), p);
		if(*p=='<')  {
			p++;
			truncstr(p,">");
		}
		nettype=NET_INTERNET;
		smb_hfield(msg, REPLYTONETTYPE, sizeof(nettype), &nettype);
		return smb_hfield(msg, REPLYTONETADDR, (ushort)strlen(p), p);
	}
	if(!strnicmp(buf, "FROM:", 5)) {
		p=buf+5;
		while(*p && *p<=' ') p++;
		truncsp(p);
		return smb_hfield(msg, RFC822FROM, (ushort)strlen(p), p);
	}
	if(!strnicmp(buf, "ORGANIZATION:",13)) {
		p=buf+13;
		while(*p && *p<=' ') p++;
		return smb_hfield(msg, SENDERORG, (ushort)strlen(p), p);
	}
	if(!strnicmp(buf, "DATE:",5)) {
		p=buf+5;
		msg->hdr.when_written=rfc822date(p);
	}
	if(!strnicmp(buf, "MESSAGE-ID:",11)) {
		p=buf+11;
		while(*p && *p<=' ') p++;
		return smb_hfield(msg, RFC822MSGID, (ushort)strlen(p), p);
	}
	if(!strnicmp(buf, "IN-REPLY-TO:",12)) {
		p=buf+12;
		while(*p && *p<=' ') p++;
		return smb_hfield(msg, RFC822REPLYID, (ushort)strlen(p), p);
	return smb_hfield(msg, RFC822HEADER, (ushort)strlen(buf), buf);
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		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;
	socklen_t	addr_len;
	BOOL		esmtp=FALSE;
	BOOL		forward=FALSE;
	BOOL		no_forward=FALSE;
	FILE*		msgtxt=NULL;
	FILE*		rcptlst;
	FILE*		proc_cfg;
	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;
	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;

	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));
	SAFECOPY(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)
		SAFECOPY(host_name,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);
		for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
			lprintf("%04d SMTP HostAlias: %s", socket, host->h_aliases[i]);
	}
		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) */
	dnsbl_result.s_addr = dns_blacklisted(socket,smtp.client_addr.sin_addr,host_name,dnsbl);
	if(dnsbl_result.s_addr) {
		lprintf("%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
			,socket, dnsbl, host_name, host_ip, inet_ntoa(dnsbl_result));
		if(startup->options&MAIL_OPT_DNSBL_REFUSE) {
			sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
			spamlog(&scfg, "SMTP", "SESSION REFUSED", str, host_name, host_ip, NULL, NULL);
			sockprintf(socket
				,"550 Mail from %s refused due to listing at %s"
				,host_ip, dnsbl);
			mail_close_socket(socket);
			lprintf("%04d !SMTP REFUSED SESSION from blacklisted server"
			thread_down();
			active_clients--,update_clients();
	srand(time(NULL));	/* Seed random number generator */
	xp_random(10);		/* Throw away first number */
	sprintf(session_id,"%d.%lx.%x",socket,clock()&0xffff,xp_random(0x10000));

	sprintf(rcptlst_fname,"%sSMTP.%s.lst", scfg.data_dir, session_id);
	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");
		thread_down();
		active_clients--,update_clients();
	if(trashcan(&scfg,host_name,"smtpspy") 
		|| trashcan(&scfg,host_ip,"smtpspy")) {
		sprintf(str,"%sSMTPSPY.TXT", scfg.data_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 */);

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

	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(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 (body: %lu lines, %lu bytes, header: %lu lines, %lu bytes)"
					, socket, lines, ftell(msgtxt)-hdr_len, hdr_lines, hdr_len);

				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 */
						sprintf(str,"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s"
							,head,sender_addr,host_ip,host_name,tail);
					else
						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");
						continue; 
					}
					strcpy(telegram_buf,str);	/* can't use SAFECOPY here */
					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 */
					rcpt_count=0;
					while(!feof(rcptlst)  && rcpt_count<startup->max_recipients) {
						if(fgets(str,sizeof(str),rcptlst)==NULL)
							break;
						usernum=atoi(str);
						if(fgets(rcpt_name,sizeof(rcpt_name),rcptlst)==NULL)
							break;
						truncsp(rcpt_name);
						if(fgets(rcpt_addr,sizeof(rcpt_addr),rcptlst)==NULL)
							break;
						truncsp(rcpt_addr);
						putsmsg(&scfg,usernum,telegram_buf);
						lprintf("%04d SMTP Created telegram (%ld/%u bytes) from %s to %s <%s>"
							,socket, length, strlen(telegram_buf), sender_addr, rcpt_name, rcpt_addr);
						rcpt_count++;
					}
					free(telegram_buf);
					sockprintf(socket,ok_rsp);
					telegram=FALSE;
					continue;
				}

				fclose(msgtxt);
				fclose(rcptlst);

				/* External Mail Processing here */
				if(startup->proc_cfg_file[0] 
					&& (proc_cfg=fopen(startup->proc_cfg_file,"r"))!=NULL) {
					sprintf(proc_err_fname,"%sSMTP.%s.err", scfg.data_dir, session_id);
					remove(proc_err_fname);

					while(!feof(proc_cfg)) {
						if(!fgets(tmp,sizeof(tmp),proc_cfg))
							break;
						truncsp(tmp);
						p=tmp;
						while(*p && *p<=' ') p++;
						if(*p==';' || *p==0)	/* comment or blank line */
							continue;
						lprintf("%04d SMTP executing external process: %s", socket, p);
						system(cmdstr(p, msgtxt_fname, rcptlst_fname, proc_err_fname, str));
						if(flength(proc_err_fname)>0)
							break;
						if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname))
							break;
					}
					fclose(proc_cfg);
					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("%04d !SMTP external process error: %s", socket, str);
							i=atoi(str);
							if(i>=100 && i<1000)
								sockprintf(socket,"%s", str);
							else
								sockprintf(socket,"554%c%s"
									,ftell(proc_err)<filelength(fileno(proc_err)) ? '-' : ' '
									,str);
						}
						fclose(proc_err);
						remove(proc_err_fname);
						continue;
					}
					if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
						lprintf("%04d SMTP external process removed %s"
							,socket, fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list");
						sockprintf(socket,ok_rsp);
						continue;
					}
				}

				/* Re-open files */
				if((rcptlst=fopen(rcptlst_fname,"r"))==NULL) {
					lprintf("%04d !SMTP ERROR %d re-opening recipient list: %s"
						,socket, errno, rcptlst_fname);
					sockprintf(socket,"421 Error %d opening %s", errno, rcptlst_fname);
					continue;
				}
			
				if((msgtxt=fopen(msgtxt_fname,"rb"))==NULL) {
					lprintf("%04d !SMTP ERROR %d re-opening message file: %s"
						,socket, errno, msgtxt_fname);
					sockprintf(socket, "421 Error %d opening %s", errno, msgtxt_fname);
					continue;
				}

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

				/* Parse message header here */
				smb_error=0; /* 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;
						while(*p && *p<=' ') p++;
						if(dnsbl_result.s_addr && startup->dnsbl_tag[0]
							&& !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
							sprintf(str,"%.*s: %.*s"
								,(int)sizeof(str)/2, startup->dnsbl_tag
								,(int)sizeof(str)/2, p);
							p=str;
							lprintf("%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
								,socket, startup->dnsbl_tag);
						}
						smb_hfield(&msg, SUBJECT, (ushort)strlen(p), p);
						msg.idx.subj=subject_crc(p);
						continue;
					}
					if(!strnicmp(buf, "FROM:", 5)
						&& !chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr,reverse_path))
						break;
					if((smb_error=parse_header_field(buf,&msg))!=0) {
						if(smb_error==SMB_ERR_HDR_LEN)
							lprintf("%04d !SMTP MESSAGE HEADER EXCEEDS %u BYTES"
								,socket, SMB_MAX_HDR_LEN);
						else
							lprintf("%04d !SMTP ERROR %d adding header field: %s"
								,socket, smb_error, buf);
						break;
					}
				}
				if(smb_error!=0) {	/* SMB Error */
					sockprintf(socket, "452 Insufficient system storage");
					continue;
				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) {
					/* Get the sender's address */
					if((tp=strchr(p,'<'))!=NULL)
						tp++;
					else
						tp=p;
					while(*tp && *tp<=' ') tp++;
					SAFECOPY(sender_addr,tp);
					truncstr(sender_addr,">( ");

					SAFECOPY(tmp,p);
					p=tmp;
					/* Get the sender's "name" (if possible) */
					if((tp=strchr(p,'('))!=NULL) {			/* name in parenthesis? */
						p=tp+1;
						tp=strchr(p,')');
					} else if((tp=strchr(p,'"'))!=NULL) {	/* name in quotes? */
						p=tp+1;
						tp=strchr(p,'"');
					} else if(*p=='<') {					/* address in brackets? */
						p++;
						tp=strchr(p,'>');
					} else									/* name, then address in brackets */
						tp=strchr(p,'<');
					if(tp) *tp=0;
					SAFECOPY(sender,p);
					truncsp(sender);
				}

				/* SPAM Filtering/Logging */
				if(msg.subj!=NULL && trashcan(&scfg,msg.subj,"subject")) {
					lprintf("%04d !SMTP BLOCKED SUBJECT (%s) from: %s"
						,socket, msg.subj, reverse_path);
					sprintf(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;
				}
				if(dnsbl_result.s_addr) {
					if(startup->options&MAIL_OPT_DNSBL_IGNORE) {
						lprintf("%04d !SMTP IGNORED MAIL from blacklisted server"
						sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
						spamlog(&scfg, "SMTP", "IGNORED"
							,str, host_name, host_ip, rcpt_addr, reverse_path);
						/* pretend we received it */
					if(startup->dnsbl_hdr[0]) {
						sprintf(str,"%s: %s is listed on %s as %s"
							,startup->dnsbl_hdr, host_ip
							,dnsbl, inet_ntoa(dnsbl_result));
						smb_hfield(&msg,RFC822HEADER,strlen(str),str);
						lprintf("%04d !SMTP TAGGED MAIL HEADER from blacklisted server with: %s"
							,socket, startup->dnsbl_hdr);
					}
					if(startup->dnsbl_hdr[0] || startup->dnsbl_tag[0]) {
						sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
						spamlog(&scfg, "SMTP", "TAGGED", str, host_name, host_ip, rcpt_addr, reverse_path);
				if(sender[0]==0) {
					lprintf("%04d !SMTP MISSING mail header 'FROM' field", socket);
					sockprintf(socket, "554 Mail header missing 'FROM' field");
				smb_hfield(&msg, SMTPREVERSEPATH, (ushort)strlen(reverse_path), reverse_path);
				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);
				length=filelength(fileno(msgtxt))-ftell(msgtxt);

				if(startup->max_msg_size && length>startup->max_msg_size) {
					lprintf("%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("%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 */

				/* Do external JavaScript processing here? */

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

					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);
					}
					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);
						lprintf("%04d !SMTP ERROR %d (%s) creating %s"
							,socket, i, smb.last_error, smb.file);
						sockprintf(socket, "452 Insufficient system storage");
					lprintf("%04d !SMTP ERROR %d (%s) locking %s"
						,socket, i, smb.last_error, 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);
						lprintf("%04d !SMTP ERROR %d ADDING MESSAGE: %s"
							, socket, i, smb.last_error);