Skip to content
Snippets Groups Projects
mailsrvr.c 166 KiB
Newer Older
			loginSuccess(startup->login_attempt_list, &pop3.client_addr);
		putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
deuce's avatar
deuce committed
		putuserrec(&scfg,user.number,U_IPADDR,LEN_IPADDR,host_ip);

		/* Update client display */
		client.user=user.alias;
		client_on(socket,&client,TRUE /* update */);

		if(startup->options&MAIL_OPT_DEBUG_POP3)		
			lprintf(LOG_INFO,"%04d POP3 %s logged in %s", socket, user.alias, apop ? "via APOP":"");
		SAFEPRINTF(str,"POP3: %s",user.alias);
		SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
			lprintf(LOG_WARNING,"%04d !POP3 MAIL BASE LOCKED: %s",socket,smb.last_error);
			sockprintf(socket,"-ERR database locked, try again later");
			break;
		}
		smb.retry_time=scfg.smb_retry_time;
		smb.subnum=INVALID_SUB;
		if((i=smb_open(&smb))!=SMB_SUCCESS) {
			lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) opening %s",socket,i,smb.last_error,smb.file);
			sockprintf(socket,"-ERR %d opening %s",i,smb.file);
			break;
		}

		mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,0);

		for(l=bytes=0;l<msgs;l++) {
			msg.hdr.number=mail[l].number;
			if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
				lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
					,socket, i, smb.last_error);
			if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
				lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
					,socket, i, smb.last_error, msg.hdr.number);
				break; 
			}
			i=smb_getmsghdr(&smb,&msg);
			smb_unlockmsghdr(&smb,&msg);
			if(i!=0) {
				lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
					,socket, i, smb.last_error, msg.hdr.number);
			smb_freemsgmem(&msg);
		if(l<msgs) {
			sockprintf(socket,"-ERR message #%d: %d (%s)"
				,mail[l].number,i,smb.last_error);
			break;
		}

		sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);

		while(1) {	/* TRANSACTION STATE */
			rd = sockreadline(socket, buf, sizeof(buf));
			if(startup->options&MAIL_OPT_DEBUG_POP3)
				lprintf(LOG_DEBUG,"%04d POP3 RX: %s", socket, buf);
			if(!stricmp(buf, "NOOP")) {
				sockprintf(socket,"+OK");
				continue;
			}
			if(!stricmp(buf, "QUIT")) {
				sockprintf(socket,"+OK");
				break;
			}
			if(!stricmp(buf, "STAT")) {
				sockprintf(socket,"+OK %lu %lu",msgs,bytes);
				continue;
			}
			if(!stricmp(buf, "RSET")) {
				if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) locking message base"
						,socket, i, smb.last_error);
					sockprintf(socket,"-ERR %d locking message base",i);
					continue;
				}
				for(l=0;l<msgs;l++) {
					msg.hdr.number=mail[l].number;
					if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
							,socket, i, smb.last_error);
					if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
							,socket, i, smb.last_error, msg.hdr.number);
					if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
							,socket, i, smb.last_error, msg.hdr.number);
						break;
					}
					msg.hdr.attr=mail[l].attr;
					if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) updating message index"
							,socket, i, smb.last_error);
					smb_unlockmsghdr(&smb,&msg);
					smb_freemsgmem(&msg);
				}
				if(l<msgs)
					sockprintf(socket,"-ERR %d messages reset (ERROR: %d)",l,i);
				else
					sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
				continue;
			}
			if(!strnicmp(buf, "LIST",4) || !strnicmp(buf,"UIDL",4)) {
				p=buf+4;
				if(isdigit((uchar)*p)) {
					msgnum=strtoul(p, NULL, 10);
					if(msgnum<1 || msgnum>msgs) {
						lprintf(LOG_NOTICE,"%04d !POP3 INVALID message #%" PRIu32
							,socket, msgnum);
						sockprintf(socket,"-ERR no such message");
						continue;
					}
					msg.hdr.number=mail[msgnum-1].number;
					if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
							,socket, i, smb.last_error);
						sockprintf(socket,"-ERR %d getting message index",i);
						break;
					}
					if(msg.idx.attr&MSG_DELETE) {
						lprintf(LOG_NOTICE,"%04d !POP3 ATTEMPT to list DELETED message"
							,socket);
						sockprintf(socket,"-ERR message deleted");
						continue;
					}
					if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_WARNING,"%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) {
						smb_freemsgmem(&msg);
						lprintf(LOG_ERR,"%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(!strnicmp(buf, "LIST",4)) {
						sockprintf(socket,"+OK %" PRIu32 " %lu",msgnum,smb_getmsgtxtlen(&msg));
					} else /* UIDL */
						sockprintf(socket,"+OK %" PRIu32 " %lu",msgnum,msg.hdr.number);

					smb_freemsgmem(&msg);
					continue;
				}
				/* List ALL messages */
				sockprintf(socket,"+OK %lu messages (%lu bytes)",msgs,bytes);
				for(l=0;l<msgs;l++) {
					msg.hdr.number=mail[l].number;
					if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
							,socket, i, smb.last_error);
						break;
					}
					if(msg.idx.attr&MSG_DELETE) 
						continue;
					if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
						lprintf(LOG_WARNING,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
							,socket, i, smb.last_error, msg.hdr.number);
						break; 
					}
					i=smb_getmsghdr(&smb,&msg);
					smb_unlockmsghdr(&smb,&msg);
					if(i!=0) {
						smb_freemsgmem(&msg);
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message header #%lu"
							,socket, i, smb.last_error, msg.hdr.number);
						break;
					}
					if(!strnicmp(buf, "LIST",4)) {
						sockprintf(socket,"%lu %lu",l+1,smb_getmsgtxtlen(&msg));
					} else /* UIDL */
						sockprintf(socket,"%lu %lu",l+1,msg.hdr.number);

					smb_freemsgmem(&msg);
				}			
				sockprintf(socket,".");
				continue;
			}
			if(!strnicmp(buf, "RETR ",5) || !strnicmp(buf,"TOP ",4)) {
				SAFEPRINTF(str,"POP3: %s", user.alias);
				status(str);

				lines=-1;
				p=buf+4;
				msgnum=strtoul(p, NULL, 10);

				if(!strnicmp(buf,"TOP ",4)) {
					lines=atol(p);
				}
				if(msgnum<1 || msgnum>msgs) {
					lprintf(LOG_NOTICE,"%04d !POP3 %s ATTEMPTED to retrieve an INVALID message #%" PRIu32
						,socket, user.alias, msgnum);
					sockprintf(socket,"-ERR no such message");
					continue;
				}
				msg.hdr.number=mail[msgnum-1].number;

rswindell's avatar
rswindell committed
				lprintf(LOG_INFO,"%04d POP3 %s retrieving message #%ld with command: %s"
					,socket, user.alias, msg.hdr.number, buf);
				if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
						,socket, i, smb.last_error);
					sockprintf(socket,"-ERR %d getting message index",i);
					continue;
				}
				if(msg.idx.attr&MSG_DELETE) {
					lprintf(LOG_NOTICE,"%04d !POP3 ATTEMPT to retrieve DELETED message"
						,socket);
					sockprintf(socket,"-ERR message deleted");
					continue;
				}
				if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_WARNING,"%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(LOG_ERR,"%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_ALL))==NULL) {
					smb_freemsgmem(&msg);
					lprintf(LOG_ERR,"%04d !POP3 ERROR (%s) retrieving message %lu text"
						,socket, smb.last_error, msg.hdr.number);
					sockprintf(socket,"-ERR retrieving message text");
					continue;
				}

rswindell's avatar
rswindell committed
				if(lines > 0					/* Works around BlackBerry mail server */
					&& lines >= strlen(msgtxt))	/* which requests the number of bytes (instead of lines) using TOP */
					lines=-1;					

				sockprintf(socket,"+OK message follows");
				lprintf(LOG_DEBUG,"%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(LOG_WARNING,"%04d !POP3 ERROR sending message text (sent %ld of %ld lines)"
					lprintf(LOG_DEBUG,"%04d POP3 message transfer complete (%lu lines)"
					if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) locking message base"
							,socket, i, smb.last_error);
					} else {
						if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
							lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index"
								,socket, i, smb.last_error);
						} else {
							msg.hdr.attr|=MSG_READ;
							msg.hdr.netattr|=MSG_SENT;

							if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) 
								lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) locking message header #%lu"
									,socket, i, smb.last_error, msg.hdr.number);
							if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
								lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) marking message #%lu as read"
									,socket, i, smb.last_error, msg.hdr.number);
							smb_unlockmsghdr(&smb,&msg);
						}
						smb_unlocksmbhdr(&smb);
					}
				smb_freemsgmem(&msg);
				smb_freemsgtxt(msgtxt);
				continue;
			}
			if(!strnicmp(buf, "DELE ",5)) {
				p=buf+5;
				msgnum=strtoul(p, NULL, 10);

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

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

				if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) locking message base"
						,socket, i, smb.last_error);
					sockprintf(socket,"-ERR %d locking message base",i);
					continue;
				}
				if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%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))!=SMB_SUCCESS) {
					lprintf(LOG_WARNING,"%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))!=SMB_SUCCESS) {
					smb_unlockmsghdr(&smb,&msg);
					lprintf(LOG_ERR,"%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;

				if((i=smb_putmsg(&smb,&msg))==SMB_SUCCESS && msg.hdr.auxattr&MSG_FILEATTACH)
					delfattach(&scfg,&msg);
				smb_unlockmsghdr(&smb,&msg);
				smb_unlocksmbhdr(&smb);
				smb_freemsgmem(&msg);
				if(i!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) marking message as read"
						, socket, i, smb.last_error);
					sockprintf(socket,"-ERR %d marking message for deletion",i);
					continue;
				}
				sockprintf(socket,"+OK");
				if(startup->options&MAIL_OPT_DEBUG_POP3)
					lprintf(LOG_INFO,"%04d POP3 message deleted", socket);
			lprintf(LOG_NOTICE,"%04d !POP3 UNSUPPORTED COMMAND from %s: '%s'"
				,socket, user.alias, buf);
			sockprintf(socket,"-ERR UNSUPPORTED COMMAND: %s",buf);
		}
		if(user.number) {
			if(!logoutuserdat(&scfg,&user,time(NULL),client.time))
				lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat", socket);
		}
	if(activity) {
		if(user.number)
			lprintf(LOG_INFO,"%04d POP3 %s logged out from port %u on %s [%s]"
deuce's avatar
deuce committed
				,socket, user.alias, inet_addrport(&pop3.client_addr), host_name, host_ip);
		else
			lprintf(LOG_INFO,"%04d POP3 client disconnected from port %u on %s [%s]"
deuce's avatar
deuce committed
				,socket, inet_addrport(&pop3.client_addr), host_name, host_ip);
	status(STATUS_WFC);

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

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

	protected_uint32_adjust(&active_clients, -1);
	client_off(socket);

	{ 
		int32_t remain = thread_down();
		if(startup->options&MAIL_OPT_DEBUG_POP3)
			lprintf(LOG_DEBUG,"%04d POP3 session thread terminated (%u threads remain, %lu clients served)"
				,socket, remain, ++stats.pop3_served);
	}
deuce's avatar
deuce committed
static ulong rblchk(SOCKET sock, union xp_sockaddr *addr, const char* rbl_addr)
{
	char		name[256];
	DWORD		mail_addr;
deuce's avatar
deuce committed
	unsigned char	*addr6;

	switch(addr->addr.sa_family) {
		case AF_INET:
			mail_addr=ntohl(addr->in.sin_addr.s_addr);
			safe_snprintf(name,sizeof(name),"%ld.%ld.%ld.%ld.%.128s"
				,mail_addr&0xff
				,(mail_addr>>8)&0xff
				,(mail_addr>>16)&0xff
				,(mail_addr>>24)&0xff
				,rbl_addr
				);
			break;
		case AF_INET6:
			addr6 = (unsigned char *)&addr->in6.sin6_addr;
			safe_snprintf(name,sizeof(name),"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
											"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
											"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
											"%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%.128s"
				,addr6[15]&0x0f
				,addr6[15]>>4
				,addr6[14]&0x0f
				,addr6[14]>>4
				,addr6[13]&0x0f
				,addr6[13]>>4
				,addr6[12]&0x0f
				,addr6[12]>>4
				,addr6[11]&0x0f
				,addr6[11]>>4
				,addr6[10]&0x0f
				,addr6[10]>>4
				,addr6[9]&0x0f
				,addr6[9]>>4
				,addr6[8]&0x0f
				,addr6[8]>>4
				,addr6[7]&0x0f
				,addr6[7]>>4
				,addr6[6]&0x0f
				,addr6[6]>>4
				,addr6[5]&0x0f
				,addr6[5]>>4
				,addr6[4]&0x0f
				,addr6[4]>>4
				,addr6[3]&0x0f
				,addr6[3]>>4
				,addr6[2]&0x0f
				,addr6[2]>>4
				,addr6[1]&0x0f
				,addr6[1]>>4
				,addr6[0]&0x0f
				,addr6[0]>>4
				,rbl_addr
				);
			break;
		default:
			return 0;
	}
	lprintf(LOG_DEBUG,"%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(LOG_INFO,"%04d SMTP DNSBL Query: %s resolved to: %s"
		,sock,name,inet_ntoa(dnsbl_result));

	return(dnsbl_result.s_addr);
deuce's avatar
deuce committed
static ulong dns_blacklisted(SOCKET sock, union xp_sockaddr *addr, char* host_name, char* list, char* dnsbl_ip)
{
	char	fname[MAX_PATH+1];
	char	str[256];
	char*	p;
	char*	tp;
	FILE*	fp;
deuce's avatar
deuce committed
	char	ip[INET6_ADDRSTRLEN];
	SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
deuce's avatar
deuce committed
	inet_addrtop(addr, ip, sizeof(ip));
	if(findstr(ip,fname))
	if(findstr(host_name,fname))
		return(FALSE);
	SAFEPRINTF(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)
		if(*p==';' || *p==0) /* comment or blank line */
			continue;

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

		/* terminate */
		tp = p;
deuce's avatar
deuce committed
		found = rblchk(sock, addr, p);
deuce's avatar
deuce committed
		strcpy(dnsbl_ip, ip);
static BOOL chk_email_addr(SOCKET socket, char* p, char* host_name, char* host_ip
						   ,char* to, char* from, char* source)
{
	char	addr[64];
	char	tmp[128];

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

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

	lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED %s e-mail address: %s"
		,socket, source, addr);
	SAFEPRINTF2(tmp,"Blocked %s e-mail address: %s", source, addr);
	spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, to, from);
static BOOL email_addr_is_exempt(const char* addr)
{
	char fname[MAX_PATH+1];
	char netmail[128];
	char* p;

	if(*addr==0 || strcmp(addr,"<>")==0)
		return FALSE;
	SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
	if(findstr((char*)addr,fname))
		return TRUE;
	SAFECOPY(netmail, addr);
	if(*(p=netmail)=='<')
		p++;
	truncstr(p,">");
	return userdatdupe(&scfg, 0, U_NETMAIL, LEN_NETMAIL, p, /* del */FALSE, /* next */FALSE, NULL, NULL);
static void exempt_email_addr(const char* comment
							  ,const char* fromname, const char* fromext, const char* fromaddr
	char	fname[MAX_PATH+1];
	char	to[128];
	char	tmp[128];
	FILE*	fp;
	if(!email_addr_is_exempt(to)) {
		SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir);
		if((fp=fopen(fname,"a"))==NULL)
			lprintf(LOG_ERR,"0000 !Error opening file: %s", fname);
		else {
			lprintf(LOG_INFO,"0000 %s: %s", comment, to);
				fprintf(fp,"%s",fromext);
			fprintf(fp," %s on %s\n%s\n"
				,fromaddr, timestr(&scfg,time32(NULL),tmp), to);
static void signal_smtp_sem(void)
	if(scfg.smtpmail_sem[0]==0) 
	if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC,DEFFILEMODE))!=-1)
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments             */
/*****************************************************************************/
static char* mailcmdstr(char* instr, char* msgpath, char* newpath, char* logpath
						,char* lstpath, char* errpath
						,char* host, char* ip, uint usernum
						,char* sender, char* sender_addr, char* reverse_path, char* cmd)
    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 'H':
					strcat(cmd,host);
					break;
				case 'I':
					strcat(cmd,ip);
					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 'R':	/* reverse path */
					strcat(cmd,reverse_path);
					break;
				case 'S':	/* sender name */
				case 'T':	/* recipient */
					strcat(cmd,rcpt_addr);
					break;
				case 'A':	/* sender address */
					strcat(cmd,sender_addr);
					break;
                    SAFEPRINTF2(str,"%s%c",VERSION,REVISION);
                    break;
                case 'Z':
                    strcat(cmd,scfg.text_dir);
                    break;
                case '!':   /* EXEC Directory */
                    strcat(cmd,scfg.exec_dir);
                    break;
                case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
                    strcat(cmd,scfg.exec_dir);
#endif
                    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;
					SAFEPRINTF(str,"%u",usernum);
                default:    /* unknown specification */
                    break; 
			}
            j=strlen(cmd); 
		}
        else
            cmd[j++]=instr[i]; 
	}
    cmd[j]=0;

    return(cmd);
}
rswindell's avatar
rswindell committed
typedef struct {
	SOCKET		sock;
	const char*	log_prefix;
	const char*	proc_name;
} private_t;

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	char*	warning;
rswindell's avatar
rswindell committed
	private_t*	p;
rswindell's avatar
rswindell committed
	if((p=(private_t*)JS_GetContextPrivate(cx))==NULL)
rswindell's avatar
rswindell committed
		lprintf(LOG_ERR,"%04d %s %s !JavaScript: %s"
			, p->sock, p->log_prefix, p->proc_name, message);
		SAFEPRINTF(file," %s",report->filename);
		SAFEPRINTF(line," line %u",report->lineno);
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
	lprintf(log_level,"%04d %s %s !JavaScript %s%s%s: %s"
rswindell's avatar
rswindell committed
		,p->sock, p->log_prefix, p->proc_name
		,warning ,file, line, message);
js_log(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	private_t*	p;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

rswindell's avatar
rswindell committed
	if((p=(private_t*)JS_GetContextPrivate(cx))==NULL)
	if(JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
		JSVALUE_TO_RASTRING(cx, argv[i], lstr, &lstr_sz, NULL);
		HANDLE_PENDING(cx);
deuce's avatar
deuce committed
		if(lstr==NULL)
		lprintf(level,"%04d %s %s %s"
deuce's avatar
deuce committed
			,p->sock,p->log_prefix,p->proc_name,lstr);
		JS_SET_RVAL(cx, arglist, argv[i]);
	if(lstr)
		free(lstr);
static JSBool
js_alert(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	private_t*	p;
	jsrefcount	rc;
	char		*line;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
	if(line==NULL)
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
	lprintf(LOG_ERR,"%04d %s %s %s"
		,p->sock, p->log_prefix, p->proc_name, line);
	free(line);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, argv[0]);

    return(JS_TRUE);
}


static JSFunctionSpec js_global_functions[] = {
	{"write",			js_log,				0},
	{"writeln",			js_log,				0},
	{"print",			js_log,				0},
	{"log",				js_log,				0},
rswindell's avatar
rswindell committed
js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailproc
			,char* msgtxt_fname, char* newtxt_fname, char* logtxt_fname
			,char* rcptlst_fname, char* proc_err_fname
			,char* sender, char* sender_addr, char* reverse_path, char* hello_name
rswindell's avatar
rswindell committed
			,JSRuntime**	js_runtime
			,JSContext**	js_cx
			,JSObject**		js_glob
			,const char*	log_prefix
)
	char		path[MAX_PATH+1];
	char		arg[MAX_PATH+1];
	BOOL		success=FALSE;
rswindell's avatar
rswindell committed
	JSObject*	js_scope=NULL;
rswindell's avatar
rswindell committed
	private_t	priv;

	SAFECOPY(fname,cmdline);
	truncstr(fname," \t");
	if(getfext(fname)==NULL) /* No extension specified, assume '.js' */
		strcat(fname,".js");

	SAFECOPY(path,fname);
	if(getfname(path)==path) { /* No path specified, assume mods or exec dir */
		SAFEPRINTF2(path,"%s%s",scfg.mods_dir,fname);
		if(scfg.mods_dir[0]==0 || !fexist(path))
			SAFEPRINTF2(path,"%s%s",scfg.exec_dir,fname);
rswindell's avatar
rswindell committed
		if(*js_runtime==NULL) {
			lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes\n"
				,sock, log_prefix, startup->js.max_bytes);
rswindell's avatar
rswindell committed
			if((*js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
rswindell's avatar
rswindell committed
		}
rswindell's avatar
rswindell committed
		if(*js_cx==NULL) {
			lprintf(LOG_DEBUG,"%04d %s JavaScript: Initializing context (stack: %lu bytes)\n"
				,sock, log_prefix, startup->js.cx_stack);
rswindell's avatar
rswindell committed
			if((*js_cx = JS_NewContext(*js_runtime, startup->js.cx_stack))==NULL)
rswindell's avatar
rswindell committed
		}
		JS_BEGINREQUEST(*js_cx);

		JS_SetErrorReporter(*js_cx, js_ErrorReporter);

		priv.sock=sock;
		priv.log_prefix=log_prefix;
		priv.proc_name=mailproc->name;
		JS_SetContextPrivate(*js_cx, &priv);

		if(*js_glob==NULL) {
			/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
			if(!js_CreateCommonObjects(*js_cx, &scfg, &scfg, NULL
rswindell's avatar
rswindell committed
						,uptime, startup->host_name, SOCKLIB_DESC	/* system */
						,&startup->js
rswindell's avatar
rswindell committed
						,&js_server_props							/* server */
rswindell's avatar
rswindell committed
				break;
rswindell's avatar
rswindell committed
			if(!JS_DefineFunctions(*js_cx, *js_glob, js_global_functions))
				break;
rswindell's avatar
rswindell committed
			/* Area and "user" Objects */
rswindell's avatar
rswindell committed
			if(!js_CreateUserObjects(*js_cx, *js_glob, &scfg, user, client, NULL, NULL)) 
rswindell's avatar
rswindell committed
				break;
rswindell's avatar
rswindell committed
			/* Mailproc "API" filenames */
			JS_DefineProperty(*js_cx, *js_glob, "message_text_filename"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,msgtxt_fname))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
rswindell's avatar
rswindell committed
			JS_DefineProperty(*js_cx, *js_glob, "new_message_text_filename"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,newtxt_fname))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
rswindell's avatar
rswindell committed
			JS_DefineProperty(*js_cx, *js_glob, "log_text_filename"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,logtxt_fname))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

			JS_DefineProperty(*js_cx, *js_glob, "recipient_address"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcpt_addr))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

rswindell's avatar
rswindell committed
			JS_DefineProperty(*js_cx, *js_glob, "recipient_list_filename"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcptlst_fname))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

			JS_DefineProperty(*js_cx, *js_glob, "processing_error_filename"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,proc_err_fname))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

			JS_DefineProperty(*js_cx, *js_glob, "sender_name"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

			JS_DefineProperty(*js_cx, *js_glob, "sender_address"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender_addr))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
rswindell's avatar
rswindell committed
			JS_DefineProperty(*js_cx, *js_glob, "reverse_path"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,reverse_path))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

			JS_DefineProperty(*js_cx, *js_glob, "hello_name"
				,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,hello_name))
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

		}

		if((js_scope=JS_NewObject(*js_cx, NULL, NULL, *js_glob))==NULL)
			break;

		/* Convert command-line to argv/argc */
rswindell's avatar
rswindell committed
		argv=JS_NewArrayObject(*js_cx, 0, NULL);
		JS_DefineProperty(*js_cx, js_scope, "argv", OBJECT_TO_JSVAL(argv)
			,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

		p=cmdline;
		FIND_WHITESPACE(p); 
		SKIP_WHITESPACE(p);