Skip to content
Snippets Groups Projects
mailsrvr.c 165 KiB
Newer Older
		for(argc=0;*p;argc++) {
			SAFECOPY(arg,p);
			truncstr(arg," \t");
rswindell's avatar
rswindell committed
			val=STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,arg));
			if(!JS_SetElement(*js_cx, argv, argc, &val))
				break;
			FIND_WHITESPACE(p);
			SKIP_WHITESPACE(p);
		}
rswindell's avatar
rswindell committed
		JS_DefineProperty(*js_cx, js_scope, "argc", INT_TO_JSVAL(argc)
			,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

deuce's avatar
deuce committed
		if(*mailproc->eval!=0) {
			lprintf(LOG_DEBUG,"%04d %s Evaluating: %s"
rswindell's avatar
rswindell committed
				,sock, log_prefix, mailproc->eval);
			js_script=JS_CompileScript(*js_cx, js_scope, mailproc->eval, strlen(mailproc->eval), NULL, 1);
		} else {
			lprintf(LOG_DEBUG,"%04d %s Executing: %s"
rswindell's avatar
rswindell committed
				,sock, log_prefix, cmdline);
			if((js_script=JS_CompileFile(*js_cx, js_scope, path)) != NULL)
				js_PrepareToExecute(*js_cx, js_scope, path, /* startup_dir: */NULL, js_scope);
rswindell's avatar
rswindell committed
		}
		if(js_script==NULL)
			break;
		/* ToDo: Set operational callback */
rswindell's avatar
rswindell committed
		success=JS_ExecuteScript(*js_cx, js_scope, js_script, &rval);
		JS_GetProperty(*js_cx, js_scope, "exit_code", &rval);

		if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval))
			JS_ValueToInt32(*js_cx,rval,result);
		js_EvalOnExit(*js_cx, js_scope, &js_callback);
		JS_ReportPendingException(*js_cx);
rswindell's avatar
rswindell committed
		JS_ClearScope(*js_cx, js_scope);
rswindell's avatar
rswindell committed
		JS_GC(*js_cx);
rswindell's avatar
rswindell committed

void js_cleanup(JSRuntime* js_runtime, JSContext* js_cx, JSObject** js_glob)
rswindell's avatar
rswindell committed
{
	if(js_cx!=NULL) {
		JS_BEGINREQUEST(js_cx);
		JS_RemoveObjectRoot(js_cx, js_glob);
		JS_ENDREQUEST(js_cx);
rswindell's avatar
rswindell committed
		JS_DestroyContext(js_cx);
rswindell's avatar
rswindell committed
	if(js_runtime!=NULL)
		jsrt_Release(js_runtime);
}
static char* get_header_field(char* buf, char* name, size_t maxlen)
	size_t	len;

	if(buf[0]<=' ')	/* folded header */
		return NULL;

	if((p=strchr(buf,':'))==NULL)
		return NULL;

	len = p-buf;
deuce's avatar
deuce committed
	sprintf(name,"%.*s",(int)len,buf);
	truncsp(name);

	p++;	/* skip colon */
	SKIP_WHITESPACE(p);
	return p;
}
static int parse_header_field(char* buf, smbmsg_t* msg, ushort* type)
	if(buf[0]<=' ' && *type!=UNKNOWN) {	/* folded header, append to previous */
		p=buf;
		truncsp(p);
		if(*type==RFC822HEADER || *type==SMTPRECEIVED)
			smb_hfield_append_str(msg,*type,"\r\n");
		else { /* Unfold other common header field types (e.g. Subject, From, To) */
			smb_hfield_append_str(msg,*type," ");
		return smb_hfield_append_str(msg, *type, p);
	if((p=strchr(buf,':'))==NULL)
		return smb_hfield_str(msg, *type=RFC822HEADER, buf);

	len=(ulong)p-(ulong)buf;
	if(len>sizeof(field)-1)
		len=sizeof(field)-1;
	sprintf(field,"%.*s",len,buf);
	truncsp(field);

	p++;	/* skip colon */
	SKIP_WHITESPACE(p);
	truncsp(p);

	if(!stricmp(field, "TO"))
		return smb_hfield_str(msg, *type=RFC822TO, p);
		smb_hfield_str(msg, *type=RFC822REPLYTO, p);
		if((tp=strrchr(p,'<'))!=NULL)  {
			tp++;
			truncstr(tp,">");
			p=tp;
		}
		nettype=NET_INTERNET;
		smb_hfield(msg, REPLYTONETTYPE, sizeof(nettype), &nettype);
		return smb_hfield_str(msg, *type=REPLYTONETADDR, p);
		return smb_hfield_str(msg, *type=RFC822FROM, p);
		return smb_hfield_str(msg, *type=SENDERORG, p);
		msg->hdr.when_written=rfc822date(p);
		return smb_hfield_str(msg, *type=RFC822MSGID, p);
		return smb_hfield_str(msg, *type=RFC822REPLYID, p);
	if(!stricmp(field, "CC"))
		return smb_hfield_str(msg, *type=SMB_CARBONCOPY, p);

	if(!stricmp(field, "RECEIVED"))
		return smb_hfield_str(msg, *type=SMTPRECEIVED, p);

	if(!stricmp(field, "RETURN-PATH")) {
		*type=UNKNOWN;
		return SMB_SUCCESS;	/* Ignore existing "Return-Path" header fields */
	}

	return smb_hfield_str(msg, *type=RFC822HEADER, buf);
static int chk_received_hdr(SOCKET socket,const char *buf,IN_ADDR *dnsbl_result, char *dnsbl, char *dnsbl_ip)
{
	char		host_name[128];
	char		*fromstr;
	char		ip[16];
	char		*p;
	char		*p2;
deuce's avatar
deuce committed
	union xp_sockaddr addr;
	struct addrinfo ai,*res;
deuce's avatar
deuce committed
	fromstr=strdup(buf);
	if(fromstr==NULL)
		return(0);
	strlwr(fromstr);
	do {
		p=strstr(fromstr,"from ");
		if(p==NULL)
			break;
		p+=4;
		for(;*p && !isspace((unsigned char)*p) && p2<host_name+126;p++)  {
		p=strtok_r(fromstr,"[",&last);
		p=strtok_r(NULL,"]",&last);
deuce's avatar
deuce committed
		if(strnicmp("IPv6:", p, 5)) {
			p+=5;
			SKIP_WHITESPACE(p);
			memset(&ai, 0, sizeof(ai));
			ai.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV|AI_PASSIVE;
			if(getaddrinfo(p, NULL, &ai, &res)!=0)
				break;
			if(res->ai_family == AF_INET6)
				memcpy(&addr, res->ai_addr, res->ai_addrlen);
			else
				break;
			freeaddrinfo(res);
		}
		else {
			strncpy(ip,p,16);
			ip[15]=0;
			addr.in.sin_family=AF_INET;
			addr.in.sin_addr.s_addr=inet_addr(ip);
			lprintf(LOG_DEBUG,"%04d SMTP DNSBL checking received header address %s [%s]",socket,host_name,ip);
		}

		if((dnsbl_result->s_addr=dns_blacklisted(socket,&addr,host_name,dnsbl,dnsbl_ip))!=0)
				lprintf(LOG_NOTICE,"%04d SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
					,socket, dnsbl, host_name, ip, inet_ntoa(*dnsbl_result));
	} while(0);
	free(fromstr);
	return(dnsbl_result->s_addr);
}

rswindell's avatar
rswindell committed
static void strip_char(char* str, char ch)
{
	char* src;
	char* p;
	char* tmp = strdup(str);

	if(tmp == NULL)
		return;
	p=tmp;
	for(src = str; *src; src++) {
		if(*src != ch)
			*(p++) = *src;
	}
	*p=0;
	strcpy(str, tmp);
	free(tmp);
}

static void parse_mail_address(char* p
							   ,char* name, size_t name_len
							   ,char* addr, size_t addr_len)
{
	char*	tp;
	char	tmp[128];

	SKIP_WHITESPACE(p);

	/* Get the address */
deuce's avatar
deuce committed
	sprintf(addr,"%.*s",(int)addr_len,tp);
	truncstr(addr,">( ");

	SAFECOPY(tmp,p);
	p=tmp;
	/* Get the "name" (if possible) */
rswindell's avatar
rswindell committed
	if((tp=strchr(p,'"'))!=NULL) {	/* name in quotes? */
rswindell's avatar
rswindell committed
		tp=strrchr(p,'"');
	} else if((tp=strchr(p,'('))!=NULL) {	/* name in parenthesis? */
rswindell's avatar
rswindell committed
		tp=strrchr(p,')');
	} else if(*p=='<') {					/* address in brackets? */
		p++;
rswindell's avatar
rswindell committed
		tp=strrchr(p,'>');
	} else									/* name, then address in brackets */
		tp=strchr(p,'<');
	if(tp) *tp=0;
deuce's avatar
deuce committed
	sprintf(name,"%.*s",(int)name_len,p);
rswindell's avatar
rswindell committed
	strip_char(name, '\\');
/* Decode quoted-printable content-transfer-encoded text */
/* Ignores (strips) unsupported ctrl chars and non-ASCII chars */
/* Does not enforce 76 char line length limit */
static char* qp_decode(char* buf)
	uchar*	p=(uchar*)buf;
	uchar*	dest=p;

	for(;;p++) {
		if(*p==0) {
			*dest++='\r';
			*dest++='\n';
			break;
		}
		if(*p==' ' || (*p>='!' && *p<='~' && *p!='=') || *p=='\t')
			*dest++=*p;
		else if(*p=='=') {
			p++;
			if(*p==0) 	/* soft link break */
				break;
			if(isxdigit(*p) && isxdigit(*(p+1))) {
				char hex[3];
				hex[0]=*p;
				hex[1]=*(p+1);
				hex[2]=0;
				/* ToDo: what about encoded NULs and the like? */
				*dest++=(uchar)strtoul(hex,NULL,16);
				p++;
			} else {	/* bad encoding */
				*dest++='=';
				*dest++=*p;
			}
		}
	}
	*dest=0;
	return buf;
}

deuce's avatar
deuce committed
static BOOL checktag(scfg_t *scfg, char *tag, uint usernum)
{
	char	fname[MAX_PATH+1];

	if(tag==NULL)
		return(FALSE);
	SAFEPRINTF2(fname,"%suser/%04d.smtpblock",scfg->data_dir,usernum);
deuce's avatar
deuce committed
	return(findstr(tag, fname));
}

static BOOL smtp_splittag(char *in, char **name, char **tag)
{
	char	*last;

	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, /* del */FALSE, /* next */FALSE);
deuce's avatar
deuce committed
	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		relay_list[MAX_PATH+1];
	char		domain_list[MAX_PATH+1];
	char		spam_bait[MAX_PATH+1];
	BOOL		spam_bait_result=FALSE;
rswindell's avatar
rswindell committed
	char		spam_block_exemptions[MAX_PATH+1];
	BOOL		spam_block_exempt=FALSE;
	char		host_name[128];
deuce's avatar
deuce committed
	char		host_ip[INET6_ADDRSTRLEN];
	char		server_ip[INET6_ADDRSTRLEN];
deuce's avatar
deuce committed
	char		dnsbl_ip[INET6_ADDRSTRLEN];
	char		challenge[256];
	char		response[128];
	char		secret[64];
	char		md5_data[384];
	uchar		digest[MD5_DIGEST_SIZE];
	socklen_t	addr_len;
	ulong		login_attempts;
rswindell's avatar
rswindell committed
	ulong		waiting;
	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;
	smbmsg_t	msg;
	smbmsg_t	newmsg;
	user_t		user;
	client_t	client;
	smtp_t		smtp=*(smtp_t*)arg;
deuce's avatar
deuce committed
	union xp_sockaddr	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;
	login_attempt_t attempted;
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;

	enum {
			 ENCODING_NONE
			,ENCODING_BASE64
			,ENCODING_QUOTED_PRINTABLE
	} content_encoding = ENCODING_NONE;

	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);
deuce's avatar
deuce committed
	if((i=getsockname(socket, &server_addr.addr, &addr_len))!=0) {
		lprintf(LOG_CRIT,"%04d !SMTP ERROR %d (%d) getting address/port"
			,socket, i, ERROR_VALUE);
		mail_close_socket(socket);
		thread_down();
deuce's avatar
deuce committed
	}
deuce's avatar
deuce committed
	if((mailproc_to_match=malloc(sizeof(BOOL)*mailproc_count))==NULL) {
		lprintf(LOG_CRIT,"%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(&spam,0,sizeof(spam));
	memset(&relay_user,0,sizeof(relay_user));
deuce's avatar
deuce committed
	inet_addrtop(&smtp.client_addr,host_ip,sizeof(host_ip));
	lprintf(LOG_INFO,"%04d SMTP Connection accepted on port %u from: %s port %u"
deuce's avatar
deuce committed
		,socket, inet_addrport(&server_addr), host_ip, inet_addrport(&smtp.client_addr));
deuce's avatar
deuce committed
	if(getnameinfo(&smtp.client_addr.addr, smtp.client_addr_len, host_name, sizeof(host_name), NULL, 0, (startup->options&MAIL_OPT_NO_HOST_LOOKUP)?NI_NUMERICHOST:0)!=0)
		SAFECOPY(host_name, "<no name>");
	if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP))
		lprintf(LOG_INFO,"%04d SMTP Hostname: %s", socket, host_name);

	protected_uint32_adjust(&active_clients, 1);
	SAFEPRINTF(spam_bait,"%sspambait.cfg",scfg.ctrl_dir);
	SAFEPRINTF(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);
rswindell's avatar
rswindell committed
	SAFEPRINTF(spam_block_exemptions,"%sspamblock_exempt.cfg",scfg.ctrl_dir);
deuce's avatar
deuce committed
	inet_addrtop(&server_addr,server_ip,sizeof(server_ip));

	if(strcmp(server_ip, host_ip)==0) {
		/* local connection */
		dnsbl_result.s_addr=0;
	} else {
		ulong banned = loginBanned(&scfg, startup->login_attempt_list, socket, host_name, startup->login_attempt, &attempted);
rswindell's avatar
rswindell committed
		if(banned) {
			char ban_duration[128];
			lprintf(LOG_NOTICE, "%04d !TEMPORARY BAN of %s (%u login attempts, last: %s) - remaining: %s"
				,socket, host_ip, attempted.count-attempted.dupes, attempted.user, seconds_to_str(banned, ban_duration));
rswindell's avatar
rswindell committed
			mail_close_socket(socket);
			thread_down();
			protected_uint32_adjust(&active_clients, -1);
			update_clients();
			free(mailproc_to_match);
			return;
		}

		spam_block_exempt = findstr(host_ip,spam_block_exemptions) || findstr(host_name,spam_block_exemptions);
		if(trashcan(&scfg,host_ip,"ip") 
rswindell's avatar
rswindell committed
			|| (findstr(host_ip,spam_block) && !spam_block_exempt)) {
			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT IP ADDRESS BLOCKED: %s (%u total)"
				,socket, host_ip, ++stats.sessions_refused);
			sockprintf(socket,"550 CLIENT IP ADDRESS BLOCKED: %s", host_ip);
			protected_uint32_adjust(&active_clients, -1);
deuce's avatar
deuce committed
			free(mailproc_to_match);
		if(trashcan(&scfg,host_name,"host") 
rswindell's avatar
rswindell committed
			|| (findstr(host_name,spam_block) && !spam_block_exempt)) {
			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT HOSTNAME BLOCKED: %s (%u total)"
				,socket, host_name, ++stats.sessions_refused);
			sockprintf(socket,"550 CLIENT HOSTNAME BLOCKED: %s", host_name);
			mail_close_socket(socket);
			protected_uint32_adjust(&active_clients, -1);
deuce's avatar
deuce committed
			free(mailproc_to_match);
deuce's avatar
deuce committed
		dnsbl_result.s_addr = dns_blacklisted(socket,&smtp.client_addr,host_name,dnsbl,dnsbl_ip);
			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 (%u total)"
					,socket, ++stats.sessions_refused);
				protected_uint32_adjust(&active_clients, -1);
deuce's avatar
deuce committed
				free(mailproc_to_match);
	SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
		lprintf(LOG_CRIT,"%04d !SMTP MAIL BASE LOCKED: %s"
			,socket, smb.last_error);
		sockprintf(socket,sys_unavail);
		mail_close_socket(socket);
		thread_down();
		protected_uint32_adjust(&active_clients, -1);
deuce's avatar
deuce committed
		free(mailproc_to_match);
	SAFEPRINTF(spam.file,"%sspam",scfg.data_dir);
	spam.retry_time=scfg.smb_retry_time;
	spam.subnum=INVALID_SUB;
	srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId()));	/* seed random number generator */
	rand();	/* throw-away first result */
	SAFEPRINTF4(session_id,"%x%x%x%lx",getpid(),socket,rand(),clock());
	lprintf(LOG_DEBUG,"%04d SMTP Session ID=%s", socket, session_id);
	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_CRIT,"%04d !SMTP ERROR %d creating recipient list: %s"
			,socket, errno, rcptlst_fname);
		thread_down();
		protected_uint32_adjust(&active_clients, -1);
deuce's avatar
deuce committed
		free(mailproc_to_match);
	if(trashcan(&scfg,host_name,"smtpspy") 
		|| trashcan(&scfg,host_ip,"smtpspy")) {
		SAFEPRINTF(str,"%ssmtpspy.txt", scfg.logs_dir);
		spy=fopen(str,"a");
	}

	/* Initialize client display */
	client.size=sizeof(client);
	SAFECOPY(client.addr,host_ip);
	SAFECOPY(client.host,host_name);
deuce's avatar
deuce committed
	client.port=inet_addrport(&smtp.client_addr);
	client.protocol="SMTP";
	client.user="<unknown>";
	client_on(socket,&client,FALSE /* update */);
	SAFEPRINTF(str,"SMTP: %s",host_ip);
rswindell's avatar
rswindell committed
	if(startup->login_attempt.throttle
		&& (login_attempts=loginAttempts(startup->login_attempt_list, &smtp.client_addr)) > 1) {
		lprintf(LOG_DEBUG,"%04d SMTP Throttling suspicious connection from: %s (%u login attempts)"
deuce's avatar
deuce committed
			,socket, host_ip, login_attempts);
rswindell's avatar
rswindell committed
		mswait(login_attempts*startup->login_attempt.throttle);
	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);

				if(!socket_check(socket, NULL, NULL, 0)) {
					lprintf(LOG_WARNING,"%04d !SMTP sender disconnected (premature evacuation)", socket);
				/* Twit-listing (sender's name and e-mail addresses) here */
				SAFEPRINTF(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> (%u total)"
						,socket, sender, sender_addr, ++stats.msgs_refused);
					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";
deuce's avatar
deuce committed
					struct addrinfo ai;
					struct addrinfo *res,*cur;
					BOOL matched=FALSE;

					rewind(msgtxt);
					length=filelength(fileno(msgtxt));
					
					p=strchr(sender_addr,'@');
deuce's avatar
deuce committed
					memset(&ai, 0, sizeof(ai));
					ai.ai_flags = AI_PASSIVE;
					ai.ai_family = smtp.client_addr.addr.sa_family;
					if(getaddrinfo(p+1, NULL, &ai, &res) != 0)
						p=NULL;
					else {
						for(cur=res; cur; cur=cur->ai_next) {
							char cur_ip[INET6_ADDRSTRLEN];

							if(inet_addrtop((void *)cur->ai_addr, cur_ip, sizeof(cur_ip))) {
								if(strcmp(host_ip, cur_ip)==0)
									matched=TRUE;
							}
						}
						freeaddrinfo(res);
						if(!matched)
							p=NULL;
					}
					if(p==NULL)
						/* 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 
rswindell's avatar
rswindell committed
						&& (startup->max_recipients==0 || rcpt_count<startup->max_recipients); rcpt_count++) {
						SAFECOPY(rcpt_to,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_to,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_to, rcpt_addr);
						else
							lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating telegram from %s to %s <%s>"
								,socket, i, sender_addr, rcpt_to, 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);
					for(i=0;i<mailproc_count && !msg_handled;i++) {
						struct mailproc* mp=&mailproc_list[i];
						if(mp->disabled)
rswindell's avatar
rswindell committed
							continue;
						if(!mp->process_dnsbl && dnsbl_result.s_addr)
						if(!mp->process_spam && spam_bait_result)
						if(!chk_ar(&scfg,mp->ar,&relay_user,&client))
rswindell's avatar
rswindell committed
							continue;

						if(mp->to!=NULL && !mailproc_to_match[i])
						if(mp->from!=NULL 
							&& !findstr_in_list(sender_addr, mp->from))
							,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"
rswindell's avatar
rswindell committed
							lprintf(LOG_DEBUG,"%04d SMTP Executing external command: %s"
								,socket, str);
								lprintf(LOG_NOTICE,"%04d SMTP system(%s) returned %d (errno: %d)"
								if(mp->ignore_on_error) {
									lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) error: %d"
rswindell's avatar
rswindell committed
							if(!js_mailproc(socket, &client, &relay_user
rswindell's avatar
rswindell committed
								,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);
									lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) failure"
						if(!mp->passthru || flength(proc_err_fname)>0 || !fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
							mailproc=mp;
							msg_handled=TRUE;
					}
					if(flength(proc_err_fname)>0 
						&& (proc_err=fopen(proc_err_fname,"r"))!=NULL) {
						lprintf(LOG_WARNING,"%04d !SMTP External mail processor (%s) created: %s"
								,socket, mailproc->name, proc_err_fname);
							if(!fgets(str,sizeof(str),proc_err))
								break;
							truncsp(str);
							lprintf(LOG_WARNING,"%04d !SMTP External mail processor (%s) error: %s"
								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
							,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);
				if(!msg_handled && subnum==INVALID_SUB && iniReadSectionCount(rcptlst,NULL) < 1) {
					lprintf(LOG_DEBUG,"%04d SMTP No recipients in recipient list file (message handled by external mail processor?)"
						,socket);
					sockprintf(socket,ok_rsp);
					msg_handled=TRUE;
				}
				if(msg_handled) {
					if(mailproc!=NULL)
						lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor (%s, %u total)"
							,socket, mailproc->name, ++mailproc->handled);
				/* 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 */
					if(!fgets(buf,sizeof(buf),msgtxt))
						break;
					truncsp(buf);
					if(buf[0]==0)	/* blank line marks end of header */
						break;

					if((p=get_header_field(buf, field, sizeof(field)))!=NULL) {
						if(stricmp(field, "SUBJECT")==0) {
							/* SPAM Filtering/Logging */
							if(relay_user.number==0) {
								if(trashcan(&scfg,p,"subject")) {
									lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SUBJECT (%s) from: %s (%u total)"
										,socket, p, reverse_path, ++stats.msgs_refused);
									SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s"
										,p, reverse_path);