Skip to content
Snippets Groups Projects
mailsrvr.c 106 KiB
Newer Older
					for(i=0;!dnsbl_result.s_addr && i<msg.total_hfields;i++)  {
						if(msg.hfield[i].type == RFC822HEADER
							&& strnicmp(msg.hfield_dat[i],"Received:",9)==0)  {
							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("%04d !SMTP IGNORED MAIL from blacklisted server"
						sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
							,str, host_name, dnsbl_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, dnsbl_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, 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 */
				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 */
				smb.subnum=INVALID_SUB;
				i=savemsg(&scfg, &smb, &msg, msgbuf);
				free(msgbuf);
				if(i!=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);
					continue;
				}

				while(!feof(rcptlst) && rcpt_count<startup->max_recipients) {
					if((i=smb_copymsgmem(&smb,&newmsg,&msg))!=0) {
						lprintf("%04d !SMTP ERROR %d (%s) copying message"
							,socket, i, smb.last_error);
					if(fgets(str,sizeof(str),rcptlst)==NULL)
					if(fgets(rcpt_name,sizeof(rcpt_name),rcptlst)==NULL)
					if(fgets(rcpt_addr,sizeof(rcpt_addr),rcptlst)==NULL)
						break;
					truncsp(rcpt_addr);

					snprintf(hdrfield,sizeof(hdrfield),
						"Received: from %s (%s [%s])\r\n"
						"          by %s [%s] (Synchronet Mail Server %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));
					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, sizeof(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 (%s) adding message header"
							,socket, i, smb.last_error);
					lprintf("%04d SMTP Created message #%ld from %s to %s <%s>"
						,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
					if(!(startup->options&MAIL_OPT_NO_NOTIFY) && 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);
					smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS);
					sockprintf(socket, "452 Insufficient system storage");
				}
						smb_incmsg_dfields(&smb,&msg,(ushort)(rcpt_count-1));
				}
				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 */
				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("%04d SMTP %s",socket, buf);
			if(msgtxt!=NULL) 
				fprintf(msgtxt, "%s\r\n", buf);
		lprintf("%04d SMTP RX: %s", socket, buf);
		if(!strnicmp(buf,"HELO",4)) {
			p=buf+4;
			while(*p && *p<=' ') p++;
			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;
			while(*p && *p<=' ') p++;
			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 
			|| stricmp(buf,"AUTH PLAIN")==0) {
			if(auth_login)
				sockprintf(socket,"334 VXNlcm5hbWU6");	/* Base64-encoded "Username:" */
			else
				sockprintf(socket,"334 Username:");
			if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
				sockprintf(socket,badarg_rsp);
			if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
rswindell's avatar
rswindell committed
				lprintf("%04d RX: %s",socket,buf);
			if(auth_login) {
				if(b64_decode(user_name,sizeof(user_name),buf,rd)<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
			} else
				SAFECOPY(user_name,buf);

			if(auth_login)
				sockprintf(socket,"334 UGFzc3dvcmQ6");	/* Base64-encoded "Password:" */
			else
				sockprintf(socket,"334 Password:");
			if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
				sockprintf(socket,badarg_rsp);
			if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
				lprintf("%04d RX: %s",socket,buf);
			if(auth_login) {
				if(b64_decode(user_pass,sizeof(user_pass),buf,rd)<1) {
					sockprintf(socket,badarg_rsp);
					continue;
				}
			} else
				SAFECOPY(user_pass,buf);
			if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
				if(scfg.sys_misc&SM_ECHO_PW)
					lprintf("%04d !SMTP UNKNOWN USER: %s (password: %s)"
						,socket, user_name, user_pass);
				else
					lprintf("%04d !SMTP UNKNOWN USER: %s"
						,socket, user_name);
				sockprintf(socket,badauth_rsp);
				continue;
			}
			if((i=getuserdat(&scfg, &relay_user))!=0) {
				lprintf("%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("%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("%04d !SMTP FAILED Password attempt for user %s: '%s' expected '%s'"
						,socket, user_name, user_pass, relay_user.pass);
				else
					lprintf("%04d !SMTP FAILED Password attempt for user %s"
						,socket, user_name);
				sockprintf(socket,badauth_rsp);
			lprintf("%04d SMTP %s authenticated using %s authentication"
				,socket,relay_user.alias,auth_login ? "LOGIN" : "PLAIN");
			sockprintf(socket,auth_ok);
			continue;
		}
		if(!stricmp(buf,"AUTH CRAM-MD5")) {
			sprintf(challenge,"<%x%x%lx%lx@%s>"
				,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
			lprintf("%04d SMTP CRAM-MD5 challenge: %s"
				,socket,challenge);
			b64_encode(str,sizeof(str),challenge,0);
			sockprintf(socket,"334 %s",str);
			if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
				sockprintf(socket,badarg_rsp);
			if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
				lprintf("%04d RX: %s",socket,buf);

			if(b64_decode(response,sizeof(response),buf,rd)<1) {
				sockprintf(socket,badarg_rsp);
				continue;
			}
#if 0
			lprintf("%04d SMTP CRAM-MD5 response: %s"
				,socket,response);
			if((p=strrchr(response,' '))!=NULL)
				*(p++)=0;
			else
				p=response;
			SAFECOPY(user_name,response);
			if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
				lprintf("%04d !SMTP UNKNOWN USER: %s"
					,socket, user_name);
				sockprintf(socket,badauth_rsp);
				continue;
			}
			if((i=getuserdat(&scfg, &relay_user))!=0) {
				lprintf("%04d !SMTP ERROR %d getting data on user (%s)"
					,socket, i, user_name);
				sockprintf(socket,badauth_rsp);
				relay_user.number=0;
				continue;
			}
			if(relay_user.misc&(DELETED|INACTIVE)) {
				lprintf("%04d !SMTP DELETED or INACTIVE user #%u (%s)"
					,socket, relay_user.number, user_name);
				sockprintf(socket,badauth_rsp);
				relay_user.number=0;
				continue;
			}
			/* Calculate correct response */
			memset(secret,0,sizeof(secret));
			SAFECOPY(secret,relay_user.pass);
			strlwr(secret);	/* this is case sensitive, so convert to lowercase first */
			for(i=0;i<sizeof(secret);i++)
				md5_data[i]=secret[i]^0x36;	/* ipad */
			strcpy(md5_data+i,challenge);
			MD5_calc(digest,md5_data,sizeof(secret)+strlen(challenge));
			for(i=0;i<sizeof(secret);i++)
				md5_data[i]=secret[i]^0x5c;	/* opad */
			memcpy(md5_data+i,digest,sizeof(digest));
			MD5_calc(digest,md5_data,sizeof(secret)+sizeof(digest));
			MD5_hex(str,digest);
			if(strcmp(p,str)) {
				lprintf("%04d !SMTP %s FAILED CRAM-MD5 authentication"
					,socket,relay_user.alias);
				lprintf("%04d !SMTP calc digest: %s"
					,socket,str);
				lprintf("%04d !SMTP resp digest: %s"
					,socket,p);
				sockprintf(socket,badauth_rsp);
				relay_user.number=0;
				continue;
			}
			lprintf("%04d SMTP %s authenticated using CRAM-MD5 authentication"
			sockprintf(socket,auth_ok);
		if(!strnicmp(buf,"AUTH",4)) {
			sockprintf(socket,"504 Unrecognized authentication type.");
			continue;
		}
		if(!stricmp(buf,"QUIT")) {
			sockprintf(socket,"221 %s Service closing transmission channel",startup->host_name);
			break;
		} 
		if(!stricmp(buf,"NOOP")) {
			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);
			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;
			state=SMTP_STATE_HELO;
rswindell's avatar
rswindell committed
			cmd=SMTP_CMD_NONE;
			if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
				lprintf("%04d !SMTP ERROR %d re-opening %s"
					,socket, errno, rcptlst_fname);
				sockprintf(socket,sys_error);
				break;
			}
			lprintf("%04d SMTP session reset",socket);
		if(!strnicmp(buf,"MAIL FROM:",10)
			|| !strnicmp(buf,"SEND FROM:",10)	/* Send a Message (Telegram) to a local ONLINE user */
			|| !strnicmp(buf,"SOML FROM:",10)	/* Send OR Mail a Message to a local user */
			|| !strnicmp(buf,"SAML FROM:",10)	/* Send AND Mail a Message to a local user */
			) {
			if(!chk_email_addr(socket,p,host_name,host_ip,NULL,NULL))
			while(*p && *p<=' ') p++;
			SAFECOPY(reverse_path,p);

			/* Update client display */
			client.user=reverse_path;
			client_on(socket,&client,TRUE /* update */);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_MAIL_FROM;
			if(!strnicmp(buf,"MAIL FROM:",10))
				cmd=SMTP_CMD_MAIL;
			else if(!strnicmp(buf,"SEND FROM:",10))
				cmd=SMTP_CMD_SEND;
			else if(!strnicmp(buf,"SOML FROM:",10))
				cmd=SMTP_CMD_SOML;
			else if(!strnicmp(buf,"SAML FROM:",10))
				cmd=SMTP_CMD_SAML;

			/* reset recipient list */
			/* reset recipient list */
			if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
				lprintf("%04d !SMTP ERROR %d re-opening %s"
					,socket, errno, rcptlst_fname);
				sockprintf(socket,sys_error);
				break;
			}
#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

		/* Add to Recipient list */
		if(!strnicmp(buf,"RCPT TO:",8)) {

rswindell's avatar
rswindell committed
			if(state<SMTP_STATE_MAIL_FROM) {
				lprintf("%04d !SMTP MISSING 'MAIL' command",socket);
			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++;
			SAFECOPY(str,p);
			truncstr(str,">");	/* was truncating at space too */
			forward=FALSE;
			no_forward=FALSE;
			if(!strnicmp(p,FORWARD,strlen(FORWARD))) {
				forward=TRUE;		/* force forward to user's netmail address */
				p+=strlen(FORWARD);
			}
			if(!strnicmp(p,NO_FORWARD,strlen(NO_FORWARD))) {
				no_forward=TRUE;	/* do not forward to user's netmail address */
				p+=strlen(NO_FORWARD);
			}

			SAFECOPY(rcpt_addr,p);
			/* Check recipient counter */
			if(rcpt_count>=startup->max_recipients) {
				lprintf("%04d !SMTP MAXIMUM RECIPIENTS (%d) REACHED"
					,socket, startup->max_recipients);
				sprintf(tmp,"Maximum recipient count (%d)",startup->max_recipients);
				spamlog(&scfg, "SMTP", "REFUSED", tmp
					,host_name, host_ip, rcpt_addr, reverse_path);
				sockprintf(socket, "552 Too many recipients");
				continue;
			}

			/* Check for SPAM bait recipient */
			if(findstr(rcpt_addr,spam_bait)) {
				sprintf(str,"SPAM BAIT (%s) taken", rcpt_addr);
				lprintf("%04d !SMTP %s by: %s"
					,socket, str, reverse_path);
				strcpy(tmp,"REFUSED");
				if(dnsbl_result.s_addr==0)	{ /* Don't double-filter */
					lprintf("%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block);
					filter_ip(&scfg, "SMTP", str, host_name, host_ip, reverse_path, spam_block);
				spamlog(&scfg, "SMTP", tmp, "Attempted recipient in SPAM BAIT list"
					,host_name, host_ip, rcpt_addr, reverse_path);
			/* Check for blocked recipients */
			if(trashcan(&scfg,rcpt_addr,"email")) {
				lprintf("%04d !SMTP BLOCKED RECIPIENT (%s) from: %s"
					,socket, rcpt_addr, reverse_path);
				spamlog(&scfg, "SMTP", "REFUSED", "Blocked recipient e-mail address"
					,host_name, host_ip, rcpt_addr, reverse_path);
				sockprintf(socket, "550 Unknown User:%s", buf+8);
				continue;
			}

			if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_BADUSER) {
				lprintf("%04d !SMTP REFUSED MAIL from blacklisted server"
					,socket);
				sprintf(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
				spamlog(&scfg, "SMTP", "REFUSED", str, host_name, host_ip, rcpt_addr, reverse_path);
				sockprintf(socket
					,"550 Mail from %s refused due to listing at %s"
					,host_ip, dnsbl);
				continue;
			}

			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) {
				SAFECOPY(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
						&& stricmp(dest_host,startup->host_name)!=0
						&& resolve_ip(dest_host)!=server_addr.sin_addr.s_addr
						&& findstr(dest_host,domain_list)==FALSE)
					|| dest_port!=server_addr.sin_port) {

					sprintf(relay_list,"%srelay.cfg",scfg.ctrl_dir);
					if(relay_user.number==0 /* not authenticated, search for IP */
						&& startup->options&MAIL_OPT_SMTP_AUTH_VIA_IP) { 
						relay_user.number=userdatdupe(&scfg, 0, U_NOTE, LEN_NOTE, host_ip, FALSE);
						if(relay_user.number) {
							getuserdat(&scfg,&relay_user);
							if(relay_user.laston < time(NULL)-(60*60))	/* logon in past hour? */
								relay_user.number=0;
						}
					} else
						getuserdat(&scfg,&relay_user);
					if(p!=alias_buf /* forced relay by alias */ &&
						(!(startup->options&MAIL_OPT_ALLOW_RELAY)
							|| relay_user.number==0
							|| relay_user.rest&(FLAG('G')|FLAG('M'))) &&
						!findstr(host_name,relay_list) && 
						!findstr(host_ip,relay_list)) {
						lprintf("%04d !SMTP ILLEGAL RELAY ATTEMPT from %s [%s] to %s"
							,socket, reverse_path, host_ip, p);
						sprintf(tmp,"Relay attempt to: %s", p);
						spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
						if(startup->options&MAIL_OPT_ALLOW_RELAY)
							sockprintf(socket, "553 Relaying through this server "
							"requires authentication.  "
							"Please authenticate before sending.");
						else
							sockprintf(socket, "550 Relay not allowed.");
					if(relay_user.number==0)
						SAFECOPY(relay_user.alias,"Unknown User");

					lprintf("%04d SMTP %s relaying to external mail service: %s"
						,socket, relay_user.alias, tp+1);

					fprintf(rcptlst,"0\n%.*s\n%.*s\n"
rswindell's avatar
rswindell committed
						,(int)sizeof(rcpt_name)-1,rcpt_addr
						,(int)sizeof(rcpt_addr)-1,p);
rswindell's avatar
rswindell committed
					state=SMTP_STATE_RCPT_TO;
rswindell's avatar
rswindell committed
			if(tp!=NULL)
				*tp=0;	/* truncate at '@' */
			tp=strchr(p,'!');	/* Routed QWKnet mail in <qwkid!user@host> format */
			if(tp!=NULL) {
				*(tp++)=0;
				while(*tp && *tp=='"') tp++;	/* Skip '"' */
				truncstr(tp,"\"");				/* Strip '"' */
				SAFECOPY(rcpt_addr,tp);
			while(*p && !isalnum(*p)) p++;	/* Skip '<' or '"' */
			p=alias(&scfg,p,name_alias_buf);
				lprintf("%04d SMTP NAME ALIAS: %s",socket,p);
		
			if(!strnicmp(p,"sub:",4)) {		/* Post on a sub-board */
				p+=4;
				for(i=0;i<scfg.total_subs;i++)
					if(!stricmp(p,scfg.sub[i]->code))
						break;
				if(i>=scfg.total_subs) {
					lprintf("%04d !SMTP UNKNOWN SUB-BOARD: %s", socket, p);
					sockprintf(socket, "550 Unknown sub-board: %s", p);
					continue;
				}
				subnum=i;
			usernum=0;	/* unknown user at this point */
			if(routed) {
				/* Search QWKnet hub-IDs for route destination */
				for(i=0;i<scfg.total_qhubs;i++) {
					if(!stricmp(p,scfg.qhub[i]->id))
						break;
				}
				if(i<scfg.total_qhubs) {	/* found matching QWKnet hub */

				}
			}

			if(startup->options&MAIL_OPT_ALLOW_RX_BY_NUMBER 
				&& isdigit(*p)) {
				usernum=atoi(p);			/* RX by user number */
				/* verify usernum */
				username(&scfg,usernum,str);
				if(!str[0] || !stricmp(str,"DELETED USER"))
					usernum=0;
				p=str;
			} else {
				/* RX by "user alias", "user.alias" or "user_alias" */
				usernum=matchuser(&scfg,p,TRUE /* sysop_alias */);	

				if(!usernum) { /* RX by "real name", "real.name", or "sysop.alias" */
					
					/* convert "user.name" to "user name" */
					SAFECOPY(rcpt_name,p);
					for(tp=rcpt_name;*tp;tp++)	
						if(*tp=='.') *tp=' ';

					if(!stricmp(p,scfg.sys_op) || !stricmp(rcpt_name,scfg.sys_op))
					if(!usernum && scfg.msg_misc&MM_REALNAME)	/* RX by "real name" */
						usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, p, FALSE);

					if(!usernum && scfg.msg_misc&MM_REALNAME)	/* RX by "real.name" */
						usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, rcpt_name, FALSE);
			if(!usernum && startup->default_user[0]) {
				usernum=matchuser(&scfg,startup->default_user,TRUE /* sysop_alias */);
				if(usernum)
					lprintf("%04d SMTP Forwarding mail for UNKNOWN USER to default user: %s"
						,socket,startup->default_user);
				else
					lprintf("%04d !SMTP UNKNOWN DEFAULT USER: %s"
						,socket,startup->default_user);
			}

			if(!usernum) {
				lprintf("%04d !SMTP UNKNOWN USER:%s", socket, buf+8);
				sockprintf(socket, "550 Unknown User:%s", buf+8);
				continue;
			}
			user.number=usernum;
			if((i=getuserdat(&scfg, &user))!=0) {
				lprintf("%04d !SMTP ERROR %d getting data on user #%u (%s)"
					,socket, i, usernum, p);
				sockprintf(socket, "550 Unknown User:%s", buf+8);
				continue;
			}
			if(user.misc&(DELETED|INACTIVE)) {
				lprintf("%04d !SMTP DELETED or INACTIVE user #%u (%s)"
					,socket, usernum, p);
				sockprintf(socket, "550 Unknown User:%s", buf+8);
rswindell's avatar
rswindell committed
			if(cmd==SMTP_CMD_SEND) { /* Check if user online */
				for(i=0;i<scfg.sys_nodes;i++) {
					getnodedat(&scfg, i+1, &node, 0);
					if(node.status==NODE_INUSE && node.useron==user.number
						&& !(node.misc&NODE_POFF))
						break;
				}
				if(i>=scfg.sys_nodes) {
					lprintf("%04d !Attempt to send telegram to unavailable user #%u (%s)"
						,socket, user.number, user.alias);
					sockprintf(socket,"450 User unavailable");
					continue;
				}
			}
rswindell's avatar
rswindell committed
			if(cmd==SMTP_CMD_MAIL) {
#if 0	/* implement later */
				if(useron.etoday>=cfg.level_emailperday[useron.level]
					&& !(useron.rest&FLAG('Q'))) {
					bputs(text[TooManyEmailsToday]);
					continue; 
				}
rswindell's avatar
rswindell committed
			fprintf(rcptlst,"%u\n%.*s\n"
				,user.number,(int)sizeof(rcpt_name)-1,rcpt_addr);

			/* Forward to Internet */
			tp=strrchr(user.netmail,'@');
				&& tp && strchr(tp,'.') && !strchr(tp,'/') 
				&& !strstr(tp,scfg.sys_inetaddr)) {
				lprintf("%04d SMTP Forwarding to: %s"
					,socket, user.netmail);
				fprintf(rcptlst,"%s\n",user.netmail);
				sockprintf(socket,"251 User not local; will forward to %s", user.netmail);
			} else { /* Local (no-forward) */
				fprintf(rcptlst,"#%u\n",usernum);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_RCPT_TO;
		/* Message Data (header and body) */
		if(!strnicmp(buf,"DATA",4)) {
rswindell's avatar
rswindell committed
			if(state<SMTP_STATE_RCPT_TO) {
				lprintf("%04d !SMTP MISSING 'RCPT TO' command", socket);
				fclose(msgtxt), msgtxt=NULL;
				if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
					unlink(msgtxt_fname);
			}
			sprintf(msgtxt_fname,"%sSMTP.%s.msg", scfg.data_dir, session_id);
			if((msgtxt=fopen(msgtxt_fname,"w+b"))==NULL) {
				lprintf("%04d !SMTP ERROR %d opening %s"
					,socket, errno, msgtxt_fname);
				sockprintf(socket, "452 Insufficient system storage");
				continue;
			}
			/* These vars are potentially over-written by parsing an RFC822 header */
			/* get sender_addr */
			p=strchr(reverse_path,'<');
			if(p==NULL)	
				p=reverse_path;
			else 
				p++;
			truncstr(sender_addr,">");
			if(truncstr(sender,"@")==NULL)
			sockprintf(socket, "354 send the mail data, end with <CRLF>.<CRLF>");
			if(telegram)
				state=SMTP_STATE_DATA_BODY;	/* No RFC headers in Telegrams */
			else
				state=SMTP_STATE_DATA_HEADER;
			continue;
		}
		sockprintf(socket,"500 Syntax error");
		lprintf("%04d !SMTP UNSUPPORTED COMMAND: '%s'", socket, buf);
			lprintf("%04d !TOO MANY INVALID COMMANDS (%u)",socket,badcmds);
			break;
		}
	}

	/* Free up resources here */
	smb_freemsgmem(&msg);

	if(msgtxt!=NULL) {
		fclose(msgtxt);
		if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
			unlink(msgtxt_fname);
	}
	if(rcptlst!=NULL) {
		fclose(rcptlst);
		unlink(rcptlst_fname);
	}
	if(spy!=NULL)
		fclose(spy);

	status(STATUS_WFC);

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

	thread_down();
	lprintf("%04d SMTP RX Session thread terminated (%u threads remain, %lu clients served)"
		,socket, thread_count, served);
}

BOOL bounce(smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate)
{
	smbmsg_t	newmsg;

	if((i=smb_lockmsghdr(smb,msg))!=0) {
		lprintf("0000 !BOUNCE ERROR %d (%s) locking message header #%lu"
			,i, smb->last_error, msg->hdr.number);
		return(FALSE);
	}

	msg->hdr.delivery_attempts++;
	if((i=smb_putmsg(smb,msg))!=0) {
		lprintf("0000 !BOUNCE ERROR %d (%s) incrementing delivery attempt counter"
			,i, smb->last_error);
		smb_unlockmsghdr(smb,msg);
		return(FALSE);
	}

	lprintf("0000 !Delivery attempt #%u FAILED for message #%lu from %s to %s"
		,msg->hdr.delivery_attempts, msg->hdr.number
		,msg->from, msg->to_net.addr);

	if(!immediate && msg->hdr.delivery_attempts<startup->max_delivery_attempts) {
		smb_unlockmsghdr(smb,msg);
		return(TRUE);
	}

	newmsg=*msg;
	/* Mark original message as deleted */
	msg->hdr.attr|=MSG_DELETE;
	msg->idx.attr=msg->hdr.attr;
	if((i=smb_putmsg(smb,msg))!=0) {
		lprintf("0000 !BOUNCE ERROR %d (%s) deleting message"
			,i, smb->last_error);
		smb_unlockmsghdr(smb,msg);
		return(FALSE);
	}
	if(msg->hdr.auxattr&MSG_FILEATTACH)
		delfattach(&scfg,msg);
	smb_unlockmsghdr(smb,msg);

	if(msg->from_agent!=AGENT_PERSON	/* don't bounce 'bounce messages' */
		|| (msg->idx.from==0 && msg->from_net.type==NET_NONE)
		|| (msg->reverse_path!=NULL && *msg->reverse_path==0)) {
		lprintf("0000 !Deleted undeliverable message from %s", msg->from);
		return(TRUE);
	}
	
	lprintf("0000 !Bouncing message back to %s", msg->from);

	newmsg.hfield=NULL;
	newmsg.hfield_dat=NULL;
	newmsg.total_hfields=0;
	newmsg.idx.to=newmsg.idx.from;
	newmsg.idx.from=0;
	sprintf(str,"Delivery failure: %.100s",newmsg.subj);
	smb_hfield(&newmsg, SUBJECT, (ushort)strlen(str), str);
	smb_hfield(&newmsg, RECIPIENT, (ushort)strlen(newmsg.from), newmsg.from);
	if(newmsg.idx.to) {
		sprintf(str,"%u",newmsg.idx.to);
		smb_hfield(&newmsg, RECIPIENTEXT, (ushort)strlen(str), str);
	}
	if(newmsg.from_net.type!=NET_NONE && newmsg.from_net.type!=NET_FIDO
		&& newmsg.reverse_path!=NULL) {
		smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(newmsg.from_net.type)
			,&newmsg.from_net.type);
		smb_hfield(&newmsg, RECIPIENTNETADDR, (ushort)strlen(newmsg.reverse_path)
			,newmsg.reverse_path);
	}
	strcpy(str,"Mail Delivery Subsystem");
	smb_hfield(&newmsg, SENDER, (ushort)strlen(str), str);
	smb_hfield(&newmsg, SENDERAGENT, sizeof(agent), &agent);
	
	/* Put error message in subject for now */
		sprintf(attempts,"after %u attempts", msg->hdr.delivery_attempts);
	sprintf(str,"%s reporting delivery failure of message %s"
		,startup->host_name, attempts);
	smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
		,msg->reverse_path==NULL ? msg->from : msg->reverse_path
		,(char*)msg->to_net.addr);
	smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
	smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);
	smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(err), err);
	sprintf(str,"\r\nOriginal message text follows:\r\n");
	smb_hfield(&newmsg, SMB_COMMENT, (ushort)strlen(str), str);

	if((i=smb_addmsghdr(smb,&newmsg,SMB_SELFPACK))!=0)
		lprintf("0000 !BOUNCE ERROR %d (%s) adding message header"
			,i,smb->last_error);
		lprintf("0000 !Delivery failure notification (message #%ld) created for %s"
			,newmsg.hdr.number, newmsg.from);
		if((i=smb_incmsg_dfields(smb,&newmsg,1))!=0)
			lprintf("0000 !BOUNCE ERROR %d (%s) incrementing data allocation units"
				,i,smb->last_error);
	}