Skip to content
Snippets Groups Projects
mailsrvr.c 188 KiB
Newer Older
		if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
			GCESH(stat, "POP3", socket, host_ip, CRYPT_UNUSED, "creating session");
			mail_close_socket(socket);
			thread_down();
			return;
		}
		if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
			GCESH(stat, "POP3", socket, host_ip, session, "disabling certificate verification");
			mail_close_socket(socket);
			thread_down();
			return;
		}
		if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
			GCESH(stat, "POP3", socket, host_ip, session, "setting private key");
			mail_close_socket(socket);
			thread_down();
			return;
		}
		nodelay = TRUE;
		setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
		nb=0;
		ioctlsocket(socket,FIONBIO,&nb);
		if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
			GCESH(stat, "POP3", socket, host_ip, session, "setting session socket");
			mail_close_socket(socket);
			thread_down();
			return;
		}
		if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
			GCESH(stat, "POP3", socket, host_ip, session, "setting session active");
			mail_close_socket(socket);
			thread_down();
			return;
		}
		if (startup->max_inactivity) {
			if (cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
				GCESH(stat, "POP3", socket, host_ip, session, "setting read timeout");
	ulong banned = loginBanned(&scfg, startup->login_attempt_list, socket, host_name, startup->login_attempt, &attempted);
	if(banned || trashcan(&scfg,host_ip,"ip")) {
		if(banned) {
			char ban_duration[128];
			lprintf(LOG_NOTICE, "%04d !POP3 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));
		}
		else
			lprintf(LOG_NOTICE,"%04d !POP3 CLIENT IP ADDRESS BLOCKED: %s",socket, host_ip);
		sockprintf(socket,session,"-ERR Access denied.");
		if (session != -1)
			cryptDestroySession(session);
	if(trashcan(&scfg,host_name,"host")) {
		lprintf(LOG_NOTICE,"%04d !POP3 CLIENT HOSTNAME BLOCKED: %s"
		sockprintf(socket,session,"-ERR Access denied.");
		if (session != -1)
			cryptDestroySession(session);
	protected_uint32_adjust(&active_clients, 1);

	/* 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(&pop3.client_addr);
	client.protocol="POP3";
	client_on(socket,&client,FALSE /* update */);
	SAFEPRINTF(str,"POP3: %s", host_ip);
rswindell's avatar
rswindell committed
	if(startup->login_attempt.throttle
		&& (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) {
		lprintf(LOG_DEBUG,"%04d POP3 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);
rswindell's avatar
rswindell committed
	mail=NULL;

	do {
		memset(&smb,0,sizeof(smb));
		memset(&msg,0,sizeof(msg));
		srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId()));	/* seed random number generator */
		rand();	/* throw-away first result */
		safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>"
			,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
		sockprintf(socket,session,"+OK Synchronet POP3 Server %s-%s Ready %s"
			,revision,PLATFORM_DESC,challenge);
		/* Requires USER or APOP command first */
		for(i=5;i;i--) {
			if(!sockgetrsp(socket,session,NULL,buf,sizeof(buf)))
				break;
			if(!strnicmp(buf,"USER ",5))
				break;
			else if(!strnicmp(buf,"APOP ",5)) {
			else if (!stricmp(buf, "CAPA")) {
				// Capabilities
				sockprintf(socket,session, "+OK Capability list follows");
deuce's avatar
deuce committed
				sockprintf(socket,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s-%s\r\n%s.", revision, PLATFORM_DESC, (session != -1 || get_ssl_cert(&scfg, NULL, NULL) == -1) ? "" : "STLS\r\n");
				i++;
			}
			else if (!stricmp(buf, "STLS")) {
deuce's avatar
deuce committed
				if (get_ssl_cert(&scfg, NULL, NULL) == -1) {
					sockprintf(socket,session,"-ERR STLS command not supported");
					continue;
				}
				sockprintf(socket,session,"+OK Begin TLS negotiation");
				if ((stat=cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
					GCESH(stat, "POP3", socket, host_ip, CRYPT_UNUSED, "creating session");
				if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
deuce's avatar
deuce committed
					GCESH(stat, "POP3", socket, host_ip, session, "disabling certificate verification");
				if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
					GCESH(stat, "POP3", socket, host_ip, session, "setting private key");
					buf[0] = 0;
					break;
				}
				nodelay = TRUE;
				setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
				nb=0;
				ioctlsocket(socket,FIONBIO,&nb);
				if ((stat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
					GCESH(stat, "POP3", socket, host_ip, session, "setting network socket");
				if ((stat=cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
					GCESH(stat, "POP3", socket, host_ip, session, "setting session active");
					buf[0] = 0;
					break;
				}
				if (startup->max_inactivity) {
					if ((stat=cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
						GCESH(stat, "POP3", socket, host_ip, session, "setting read timeout");
						buf[0] = 0;
						break;
					}
				}
				i++;
			}
			else
				sockprintf(socket,session,"-ERR USER, APOP, CAPA, or STLS command expected");
		if(!i || buf[0]==0)	/* no USER or APOP command received */
		if(apop) {
			if((response=strrchr(p,' '))!=NULL)
				*(response++)=0;
			else
				response=p;
		}
		SAFECOPY(username,p);
		if((p = strstr(username, NO_SPAM)) != NULL) {
			*p = 0;
			lm_mode = LM_NOSPAM;
		} else
			lm_mode = 0;
			sockprintf(socket,session,"+OK");
			if(!sockgetrsp(socket,session,"PASS ",buf,sizeof(buf))) {
				sockprintf(socket,session,"-ERR PASS command expected");
			SAFECOPY(password,p);
		user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/);
		if(!user.number) {
				lprintf(LOG_NOTICE,"%04d !POP3 [%s] UNKNOWN USER: '%s' (password: %s)"
					,socket, host_ip, username, password);
				lprintf(LOG_NOTICE,"%04d !POP3 [%s] UNKNOWN USER: '%s'"
					,socket, host_ip, username);
			badlogin(socket, session, client.protocol, pop_err, username, password, host_name, &pop3.client_addr);
			break;
		}
		if((i=getuserdat(&scfg, &user))!=0) {
			lprintf(LOG_ERR,"%04d !POP3 [%s] ERROR %d getting data on user (%s)"
				,socket, host_ip, i, username);
			badlogin(socket, session, client.protocol, pop_err, NULL, NULL, NULL, NULL);
			break;
		}
		if(user.misc&(DELETED|INACTIVE)) {
			lprintf(LOG_NOTICE,"%04d !POP3 [%s] DELETED or INACTIVE user #%u (%s)"
				,socket, host_ip, user.number, username);
			badlogin(socket, session, client.protocol, pop_err, NULL, NULL, NULL, NULL);
		if(apop) {
			strlwr(user.pass);	/* this is case-sensitive, so convert to lowercase */
			strcat(challenge,user.pass);
			MD5_calc(digest,challenge,strlen(challenge));
			MD5_hex((BYTE*)str,digest);
			if(strcmp(str,response)) {
				lprintf(LOG_NOTICE,"%04d !POP3 [%s] FAILED APOP authentication: %s"
					,socket, host_ip, username);
				lprintf(LOG_DEBUG,"%04d !POP3 digest data: %s",socket,challenge);
				lprintf(LOG_DEBUG,"%04d !POP3 calc digest: %s",socket,str);
				lprintf(LOG_DEBUG,"%04d !POP3 resp digest: %s",socket,response);
				badlogin(socket, session, client.protocol, pop_err, username, response, host_name, &pop3.client_addr);
				break;
			}
		} else if(stricmp(password,user.pass)) {
				lprintf(LOG_NOTICE,"%04d !POP3 [%s] FAILED Password attempt for user %s: '%s' expected '%s'"
					,socket, host_ip, username, password, user.pass);
				lprintf(LOG_NOTICE,"%04d !POP3 [%s] FAILED Password attempt for user %s"
					,socket, host_ip, username);
			badlogin(socket, session, client.protocol, pop_err, username, password, host_name, &pop3.client_addr);
			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] %s logged in %s", socket, host_ip, user.alias, apop ? "via APOP":"");
		SAFEPRINTF(str,"POP3: %s",user.alias);
		SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
			lprintf(LOG_WARNING,"%04d !POP3 <%s> MAIL BASE LOCKED: %s",socket, user.alias, smb.last_error);
			sockprintf(socket,session,"-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 <%s> ERROR %d (%s) opening %s", socket, user.alias, i, smb.last_error,smb.file);
			sockprintf(socket,session,"-ERR %d opening %s",i,smb.file);
		mail=loadmail(&smb,&msgs,user.number,MAIL_YOUR,lm_mode);

		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 <%s> ERROR %d (%s) getting message index"
					,socket, user.alias, i, smb.last_error);
			if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
				lprintf(LOG_WARNING,"%04d !POP3 <%s> ERROR %d (%s) locking message header #%lu"
					,socket, user.alias, 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 <%s> ERROR %d (%s) line %u, msg #%lu"
					,socket, user.alias, i, smb.last_error, __LINE__, msg.hdr.number);
			smb_freemsgmem(&msg);
			sockprintf(socket,session,"-ERR message #%d: %d (%s)"
				,mail[l].number,i,smb.last_error);
			break;
		}

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

					smb_freemsgmem(&msg);
					continue;
				}
				/* List ALL messages */
				sockprintf(socket,session,"+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 <%s> ERROR %d (%s) getting message index"
							,socket, user.alias, 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 <%s> ERROR %d (%s) locking message header #%lu"
							,socket, user.alias, 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 <%s> ERROR %d (%s) line %u, msg #%lu"
							,socket, user.alias, i, smb.last_error, __LINE__, msg.hdr.number);
						break;
					}
					if(!strnicmp(buf, "LIST",4)) {
						sockprintf(socket,session,"%lu %lu",l+1,smb_getmsgtxtlen(&msg));
					} else /* UIDL */
						sockprintf(socket,session,"%lu %lu",l+1,msg.hdr.number);
				sockprintf(socket,session,".");
			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,session,"-ERR no such message");
					continue;
				}
				msg.hdr.number=mail[msgnum-1].number;

				lprintf(LOG_INFO,"%04d POP3 <%s> retrieving message #%ld with command: %s"
rswindell's avatar
rswindell committed
					,socket, user.alias, msg.hdr.number, buf);
				if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) getting message index"
						,socket, user.alias, i, smb.last_error);
					sockprintf(socket,session,"-ERR %d getting message index",i);
					continue;
				}
				if(msg.idx.attr&MSG_DELETE) {
					lprintf(LOG_NOTICE,"%04d !POP3 <%s> ATTEMPT to retrieve DELETED message"
						,socket, user.alias);
					sockprintf(socket,session,"-ERR message deleted");
				if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_WARNING,"%04d !POP3 <%s> ERROR %d (%s) locking message header #%lu"
						,socket, user.alias, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,session,"-ERR %d locking message header",i);
				i=smb_getmsghdr(&smb,&msg);
				smb_unlockmsghdr(&smb,&msg);
				if(i!=0) {
					lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) line %u, msg #%lu"
						,socket, user.alias, i, smb.last_error, __LINE__, msg.hdr.number);
					sockprintf(socket,session,"-ERR %d getting message header",i);
				if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
					smb_freemsgmem(&msg);
					lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR (%s) retrieving message %lu text"
						,socket, user.alias, smb.last_error, msg.hdr.number);
					sockprintf(socket,session,"-ERR retrieving message text");
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,session,"+OK message follows");
				lprintf(LOG_DEBUG,"%04d POP3 <%s> sending message text (%u bytes)"
					,socket, user.alias, strlen(msgtxt));
				lines_sent=sockmsgtxt(socket,session,&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 <%s> ERROR sending message text (sent %ld of %ld lines)"
						,socket, user.alias, lines_sent, lines);
					lprintf(LOG_DEBUG,"%04d POP3 <%s> message transfer complete (%lu lines)"
						,socket, user.alias, lines_sent);
					if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
						lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) locking message base"
							,socket, user.alias, i, smb.last_error);
					} else {
						if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
							lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) getting message index"
								,socket, user.alias, 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 <%s> ERROR %d (%s) locking message header #%lu"
									,socket, user.alias, i, smb.last_error, msg.hdr.number);
							if((i=smb_putmsg(&smb,&msg))!=SMB_SUCCESS)
								lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) marking message #%lu as read"
									,socket, user.alias, 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,session,"-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 <%s> ERROR %d (%s) locking message base"
						,socket, user.alias, i, smb.last_error);
					sockprintf(socket,session,"-ERR %d locking message base",i);
				if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) getting message index"
						,socket, user.alias, i, smb.last_error);
					sockprintf(socket,session,"-ERR %d getting message index",i);
				if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
					lprintf(LOG_WARNING,"%04d !POP3 <%s> ERROR %d (%s) locking message header #%lu"
						,socket, user.alias, i, smb.last_error, msg.hdr.number);
					sockprintf(socket,session,"-ERR %d locking message header",i);
				if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
					smb_unlockmsghdr(&smb,&msg);
					lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR %d (%s) line %u, msg #%lu"
						,socket, user.alias, i, smb.last_error, __LINE__, msg.hdr.number);
					sockprintf(socket,session,"-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 <%s> ERROR %d (%s) marking message as read"
						, socket, user.alias, i, smb.last_error);
					sockprintf(socket,session,"-ERR %d marking message for deletion",i);
				sockprintf(socket,session,"+OK");
				if(startup->options&MAIL_OPT_DEBUG_POP3)
					lprintf(LOG_INFO,"%04d POP3 <%s> message deleted", socket, user.alias);
			lprintf(LOG_NOTICE,"%04d !POP3 <%s> UNSUPPORTED COMMAND: '%s'"
				,socket, user.alias, buf);
			sockprintf(socket,session,"-ERR UNSUPPORTED COMMAND: %s",buf);
		if(user.number) {
			if(!logoutuserdat(&scfg,&user,time(NULL),client.time))
				lprintf(LOG_ERR,"%04d !POP3 <%s> ERROR in logoutuserdat", socket, user.alias);
	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);
	}
	if (session != -1)
		cryptDestroySession(session);
	/* Must be last */
	mail_close_socket(socket);
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 replacements            */
/*****************************************************************************/
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)