www.gusucode.com > VC++版的邮件服务器源程序源码程序 > VC++版的邮件服务器源程序源码程序\code\Filter.cpp

    //Download by http://www.NewXing.com
/*
 *  XMail by Davide Libenzi ( Intranet and Internet mail server )
 *  Copyright (C) 1999,..,2004  Davide Libenzi
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Davide Libenzi <davidel@xmailserver.org>
 *
 */

#include "SysInclude.h"
#include "SysDep.h"
#include "SvrDefines.h"
#include "SList.h"
#include "ShBlocks.h"
#include "StrUtils.h"
#include "BuffSock.h"
#include "MiscUtils.h"
#include "ResLocks.h"
#include "BuffSock.h"
#include "SvrUtils.h"
#include "UsrUtils.h"
#include "MessQueue.h"
#include "SMAILUtils.h"
#include "QueueUtils.h"
#include "MailDomains.h"
#include "SMTPUtils.h"
#include "ExtAliases.h"
#include "UsrMailList.h"
#include "Filter.h"
#include "MailConfig.h"
#include "AppDefines.h"
#include "MailSvr.h"

#define FILTER_STORAGE_DIR          "filters"
#define FILTER_SELECT_MAX           128
#define FILTER_DB_LINE_MAX          512
#define FILTER_LINE_MAX             1024

struct FilterMsgInfo {
	char szSender[MAX_ADDR_NAME];
	char szRecipient[MAX_ADDR_NAME];
	SYS_INET_ADDR LocalAddr;
	SYS_INET_ADDR RemoteAddr;
	char szSpoolFile[SYS_MAX_PATH];
};

static int FilLoadMsgInfo(SPLF_HANDLE hFSpool, FilterMsgInfo & FMI);
static void FilFreeMsgInfo(FilterMsgInfo & FMI);
static int FilGetFilePath(char const *pszMode, char *pszFilePath, int iMaxPath);
static int FilAddFilter(char **ppszFilters, int &iNumFilters, char const *pszFilterName);
static int FilSelectFilters(char const *pszFilterFilePath, char const *pszMode,
			    FilterMsgInfo const &FMI, char **ppszFilters, int iMaxFilters);
static void FilFreeFilters(char **ppszFilters, int iNumFilters);
static int FilGetFilterPath(char const *pszFileName, char *pszFilePath, int iMaxPath);
static int FilApplyFilter(char const *pszFilterPath, SPLF_HANDLE hFSpool,
			  QUEUE_HANDLE hQueue, QMSG_HANDLE hMessage, FilterMsgInfo const &FMI);
static int FilFilterMacroSubstitutes(char **ppszCmdTokens, SPLF_HANDLE hFSpool,
				     FilterMsgInfo const &FMI);

static int FilLoadMsgInfo(SPLF_HANDLE hFSpool, FilterMsgInfo & FMI)
{

	UserInfo *pUI;
	char const *const *ppszInfo = USmlGetInfo(hFSpool);
	char const *const *ppszFrom = USmlGetMailFrom(hFSpool);
	char const *const *ppszRcpt = USmlGetRcptTo(hFSpool);
	char const *pszSpoolFile = USmlGetSpoolFilePath(hFSpool);
	int iFromDomains = StrStringsCount(ppszFrom);
	int iRcptDomains = StrStringsCount(ppszRcpt);
	char szUser[MAX_ADDR_NAME] = "";
	char szDomain[MAX_ADDR_NAME] = "";

	ZeroData(FMI);

	if ((iFromDomains > 0) &&
	    (USmtpSplitEmailAddr(ppszFrom[iFromDomains - 1], szUser, szDomain) == 0)) {
		if ((pUI = UsrGetUserByNameOrAlias(szDomain, szUser)) != NULL) {
			UsrGetAddress(pUI, FMI.szSender);

			UsrFreeUserInfo(pUI);
		} else
			StrSNCpy(FMI.szSender, ppszFrom[iFromDomains - 1]);
	} else
		SetEmptyString(FMI.szSender);

	if ((iRcptDomains > 0) &&
	    (USmtpSplitEmailAddr(ppszRcpt[iRcptDomains - 1], szUser, szDomain) == 0)) {
		if ((pUI = UsrGetUserByNameOrAlias(szDomain, szUser)) != NULL) {
			UsrGetAddress(pUI, FMI.szRecipient);

			UsrFreeUserInfo(pUI);
		} else
			StrSNCpy(FMI.szRecipient, ppszRcpt[iRcptDomains - 1]);
	} else
		SetEmptyString(FMI.szRecipient);

	if ((MscGetServerAddress(ppszInfo[smiServerAddr], FMI.LocalAddr) < 0) ||
	    (MscGetServerAddress(ppszInfo[smiClientAddr], FMI.RemoteAddr) < 0))
		return (ErrGetErrorCode());

	StrSNCpy(FMI.szSpoolFile, pszSpoolFile);

	return (0);

}

static void FilFreeMsgInfo(FilterMsgInfo & FMI)
{

}

char *FilGetFilterRejMessage(char const *pszSpoolFile)
{

	FILE *pFile;
	char szRejFilePath[SYS_MAX_PATH] = "";
	char szRejMsg[512] = "";

	SysSNPrintf(szRejFilePath, sizeof(szRejFilePath) - 1, "%s.rej", pszSpoolFile);
	if ((pFile = fopen(szRejFilePath, "rb")) == NULL)
		return NULL;

	MscFGets(szRejMsg, sizeof(szRejMsg) - 1, pFile);

	fclose(pFile);
	SysRemove(szRejFilePath);

	return (SysStrDup(szRejMsg));

}

static int FilGetFilePath(char const *pszMode, char *pszFilePath, int iMaxPath)
{

	char szMailRootPath[SYS_MAX_PATH] = "";

	CfgGetRootPath(szMailRootPath, sizeof(szMailRootPath));

	SysSNPrintf(pszFilePath, iMaxPath - 1, "%sfilters.%s.tab", szMailRootPath, pszMode);

	return (0);

}

static int FilAddFilter(char **ppszFilters, int &iNumFilters, char const *pszFilterName)
{

	for (int ii = 0; ii < iNumFilters; ii++)
		if (strcmp(ppszFilters[ii], pszFilterName) == 0)
			return (0);

	if ((ppszFilters[iNumFilters] = SysStrDup(pszFilterName)) == NULL)
		return (ErrGetErrorCode());

	iNumFilters++;

	return (0);

}

static int FilSelectFilters(char const *pszFilterFilePath, char const *pszMode,
			    FilterMsgInfo const &FMI, char **ppszFilters, int iMaxFilters)
{
///////////////////////////////////////////////////////////////////////////////
//  Share lock the filter table file
///////////////////////////////////////////////////////////////////////////////
	char szResLock[SYS_MAX_PATH] = "";
	RLCK_HANDLE hResLock = RLckLockSH(CfgGetBasedPath(pszFilterFilePath, szResLock,
							  sizeof(szResLock)));

	if (hResLock == INVALID_RLCK_HANDLE)
		return (ErrGetErrorCode());

///////////////////////////////////////////////////////////////////////////////
//  Get local and remote IP addresses
///////////////////////////////////////////////////////////////////////////////
	NET_ADDRESS LocalAddr;
	NET_ADDRESS RemoteAddr;

	SysGetAddrAddress(FMI.LocalAddr, LocalAddr);
	SysGetAddrAddress(FMI.RemoteAddr, RemoteAddr);

///////////////////////////////////////////////////////////////////////////////
//  Open the filter database. Fail smootly if the file does not exist
///////////////////////////////////////////////////////////////////////////////
	FILE *pFile = fopen(pszFilterFilePath, "rt");

	if (pFile == NULL) {
		RLckUnlockSH(hResLock);
		return (0);
	}

	int iNumFilters = 0;
	char szLine[FILTER_DB_LINE_MAX] = "";

	while ((iNumFilters < iMaxFilters) &&
	       (MscGetConfigLine(szLine, sizeof(szLine) - 1, pFile) != NULL)) {
		char **ppszTokens = StrGetTabLineStrings(szLine);

		if (ppszTokens == NULL)
			continue;

		int iFieldsCount = StrStringsCount(ppszTokens);

		if ((iFieldsCount >= filMax) &&
		    StrIWildMatch(FMI.szSender, ppszTokens[filSender]) &&
		    StrIWildMatch(FMI.szRecipient, ppszTokens[filRecipient])) {
			AddressFilter AFRemote;
			AddressFilter AFLocal;

			if ((MscLoadAddressFilter(&ppszTokens[filRemoteAddr], 1, AFRemote) == 0)
			    && MscAddressMatch(AFRemote, RemoteAddr) &&
			    (MscLoadAddressFilter(&ppszTokens[filLocalAddr], 1, AFLocal) == 0) &&
			    MscAddressMatch(AFLocal, LocalAddr)) {

				FilAddFilter(ppszFilters, iNumFilters, ppszTokens[filFileName]);

			}
		}

		StrFreeStrings(ppszTokens);
	}

	fclose(pFile);

	RLckUnlockSH(hResLock);

	return (iNumFilters);

}

static void FilFreeFilters(char **ppszFilters, int iNumFilters)
{

	for (iNumFilters--; iNumFilters >= 0; iNumFilters--)
		SysFree(ppszFilters[iNumFilters]);

}

static int FilGetFilterPath(char const *pszFileName, char *pszFilePath, int iMaxPath)
{

	char szMailRootPath[SYS_MAX_PATH] = "";

	CfgGetRootPath(szMailRootPath, sizeof(szMailRootPath));

	SysSNPrintf(pszFilePath, iMaxPath - 1, "%s%s%s%s",
		    szMailRootPath, FILTER_STORAGE_DIR, SYS_SLASH_STR, pszFileName);

	return (0);

}

static int FilApplyFilter(char const *pszFilterPath, SPLF_HANDLE hFSpool,
			  QUEUE_HANDLE hQueue, QMSG_HANDLE hMessage, FilterMsgInfo const &FMI)
{
///////////////////////////////////////////////////////////////////////////////
//  Share lock the filter file
///////////////////////////////////////////////////////////////////////////////
	char szResLock[SYS_MAX_PATH] = "";
	RLCK_HANDLE hResLock = RLckLockSH(CfgGetBasedPath(pszFilterPath, szResLock,
							  sizeof(szResLock)));

	if (hResLock == INVALID_RLCK_HANDLE)
		return (ErrGetErrorCode());

///////////////////////////////////////////////////////////////////////////////
//  This should not happen but if it happens we let the message pass through
///////////////////////////////////////////////////////////////////////////////
	FILE *pFiltFile = fopen(pszFilterPath, "rt");

	if (pFiltFile == NULL) {
		RLckUnlockSH(hResLock);
		return (0);
	}
///////////////////////////////////////////////////////////////////////////////
//  Filter this message
///////////////////////////////////////////////////////////////////////////////
	char szFiltLine[FILTER_LINE_MAX] = "";

	while (MscGetConfigLine(szFiltLine, sizeof(szFiltLine) - 1, pFiltFile) != NULL) {
		char **ppszCmdTokens = StrGetTabLineStrings(szFiltLine);

		if (ppszCmdTokens == NULL)
			continue;

		int iFieldsCount = StrStringsCount(ppszCmdTokens);
		int iExitFlags = 0;

		if (iFieldsCount > 0) {
///////////////////////////////////////////////////////////////////////////////
//  Do filter line macro substitution
///////////////////////////////////////////////////////////////////////////////
			FilFilterMacroSubstitutes(ppszCmdTokens, hFSpool, FMI);

			int iExitCode = 0;
			int iExecResult = SysExec(ppszCmdTokens[0], &ppszCmdTokens[0],
						  iFilterTimeout, FILTER_PRIORITY, &iExitCode);

			if (iExecResult == 0) {
				SysLogMessage(LOG_LEV_MESSAGE,
					      "Filter run: Sender = \"%s\" Recipient = \"%s\" Filter = \"%s\" Retcode = %d\n",
					      FMI.szSender, FMI.szRecipient, ppszCmdTokens[0],
					      iExitCode);

///////////////////////////////////////////////////////////////////////////////
//  Separate code from flags
///////////////////////////////////////////////////////////////////////////////
				iExitFlags = iExitCode & FILTER_FLAGS_MASK;
				iExitCode &= ~FILTER_FLAGS_MASK;

				if ((iExitCode == FILTER_OUT_EXITCODE) ||
				    (iExitCode == FILTER_OUT_NN_EXITCODE) ||
				    (iExitCode == FILTER_OUT_NNF_EXITCODE)) {
					StrFreeStrings(ppszCmdTokens);
					fclose(pFiltFile);
					RLckUnlockSH(hResLock);

///////////////////////////////////////////////////////////////////////////////
//  Filter out message
///////////////////////////////////////////////////////////////////////////////
					char *pszRejMsg = FilGetFilterRejMessage(FMI.szSpoolFile);

					if (iExitCode == FILTER_OUT_EXITCODE)
						QueUtNotifyPermErrDelivery(hQueue, hMessage, NULL,
									   (pszRejMsg !=
									    NULL) ? pszRejMsg :
									   ErrGetErrorString
									   (ERR_FILTERED_MESSAGE),
									   NULL, true);
					else if (iExitCode == FILTER_OUT_NN_EXITCODE)
						QueCleanupMessage(hQueue, hMessage,
								  !QueUtRemoveSpoolErrors());
					else
						QueCleanupMessage(hQueue, hMessage, false);

					if (pszRejMsg != NULL)
						SysFree(pszRejMsg);

					ErrSetErrorCode(ERR_FILTERED_MESSAGE);
					return (ERR_FILTERED_MESSAGE);
				} else if (iExitCode == FILTER_MODIFY_EXITCODE) {
///////////////////////////////////////////////////////////////////////////////
//  Filter modified the message, we need to reload the spool handle
///////////////////////////////////////////////////////////////////////////////
					if (USmlReloadHandle(hFSpool) < 0) {
						ErrorPush();
						StrFreeStrings(ppszCmdTokens);
						fclose(pFiltFile);
						RLckUnlockSH(hResLock);

						SysLogMessage(LOG_LEV_MESSAGE,
							      "Filter error [ Modified message corrupted ]: Sender = \"%s\" Recipient = \"%s\" (%s)\n",
							      FMI.szSender, FMI.szRecipient,
							      ppszCmdTokens[0]);

						QueUtErrLogMessage(hQueue, hMessage,
								   "Filter error [ Modified message corrupted ]: Sender = \"%s\" Recipient = \"%s\" (%s)\n",
								   FMI.szSender, FMI.szRecipient,
								   ppszCmdTokens[0]);

						QueCleanupMessage(hQueue, hMessage, true);

						return (ErrorPop());
					}
				}
			} else {
				SysLogMessage(LOG_LEV_ERROR,
					      "Filter error (%d): Sender = \"%s\" Recipient = \"%s\" Filter = \"%s\"\n",
					      iExecResult, FMI.szSender, FMI.szRecipient,
					      ppszCmdTokens[0]);

				QueUtErrLogMessage(hQueue, hMessage,
						   "Filter error (%d): Sender = \"%s\" Recipient = \"%s\" Filter = \"%s\"\n",
						   iExecResult, FMI.szSender, FMI.szRecipient,
						   ppszCmdTokens[0]);
			}
		}

		StrFreeStrings(ppszCmdTokens);

///////////////////////////////////////////////////////////////////////////////
//  Filter list processing break required ?
///////////////////////////////////////////////////////////////////////////////
		if (iExitFlags & FILTER_FLAGS_BREAK) {
			fclose(pFiltFile);
			RLckUnlockSH(hResLock);
			return (1);
		}
	}

	fclose(pFiltFile);
	RLckUnlockSH(hResLock);

	return (0);

}

int FilFilterMessage(SPLF_HANDLE hFSpool, QUEUE_HANDLE hQueue,
		     QMSG_HANDLE hMessage, char const *pszMode)
{
///////////////////////////////////////////////////////////////////////////////
//  Get filter file path and returns immediately if no file is defined
///////////////////////////////////////////////////////////////////////////////
	char szFilterFilePath[SYS_MAX_PATH] = "";

	FilGetFilePath(pszMode, szFilterFilePath, sizeof(szFilterFilePath));

	if (!SysExistFile(szFilterFilePath))
		return (0);

///////////////////////////////////////////////////////////////////////////////
//  Load the message info
///////////////////////////////////////////////////////////////////////////////
	FilterMsgInfo FMI;

	if (FilLoadMsgInfo(hFSpool, FMI) < 0)
		return (ErrGetErrorCode());

///////////////////////////////////////////////////////////////////////////////
//  Select applicable filters
///////////////////////////////////////////////////////////////////////////////
	int iNumFilters;
	char *pszFilters[FILTER_SELECT_MAX];

	if ((iNumFilters = FilSelectFilters(szFilterFilePath, pszMode, FMI, pszFilters,
					    CountOf(pszFilters))) < 0) {
		ErrorPush();
		FilFreeMsgInfo(FMI);
		return (ErrorPop());
	}
///////////////////////////////////////////////////////////////////////////////
//  Sequentially apply each selected filter
///////////////////////////////////////////////////////////////////////////////
	for (int ii = 0; ii < iNumFilters; ii++) {
		int iFilterResult;
		char szFilterPath[SYS_MAX_PATH] = "";

		FilGetFilterPath(pszFilters[ii], szFilterPath, sizeof(szFilterPath));

		if ((iFilterResult =
		     FilApplyFilter(szFilterPath, hFSpool, hQueue, hMessage, FMI)) < 0) {
			ErrorPush();
			FilFreeFilters(pszFilters, iNumFilters);
			FilFreeMsgInfo(FMI);
			return (ErrorPop());
		}
///////////////////////////////////////////////////////////////////////////////
//  A return code greater than zero means exit filter processing loop soon
///////////////////////////////////////////////////////////////////////////////
		if (iFilterResult > 0)
			break;
	}

	FilFreeFilters(pszFilters, iNumFilters);
	FilFreeMsgInfo(FMI);

	return (0);

}

static int FilFilterMacroSubstitutes(char **ppszCmdTokens, SPLF_HANDLE hFSpool,
				     FilterMsgInfo const &FMI)
{

	char const *const *ppszInfo = USmlGetInfo(hFSpool);
	char const *pszSMTPDomain = USmlGetSMTPDomain(hFSpool);
	char const *const *ppszFrom = USmlGetMailFrom(hFSpool);
	char const *const *ppszRcpt = USmlGetRcptTo(hFSpool);
	char const *pszSmtpMessageID = USmlGetSmtpMessageID(hFSpool);
	char const *pszMessageID = USmlGetSpoolFile(hFSpool);
	int iFromDomains = StrStringsCount(ppszFrom);
	int iRcptDomains = StrStringsCount(ppszRcpt);
	FileSection FS;

///////////////////////////////////////////////////////////////////////////////
//  This function retrieve the spool file message section and sync the content.
//  This is necessary before passing the file name to external programs
///////////////////////////////////////////////////////////////////////////////
	if (USmlGetMsgFileSection(hFSpool, FS) < 0)
		return (ErrGetErrorCode());

	for (int ii = 0; ppszCmdTokens[ii] != NULL; ii++) {
		if (strcmp(ppszCmdTokens[ii], "@@FROM") == 0) {
			char *pszNewValue =
			    SysStrDup((iFromDomains > 0) ? ppszFrom[iFromDomains - 1] : "");

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@RCPT") == 0) {
			char *pszNewValue =
			    SysStrDup((iRcptDomains > 0) ? ppszRcpt[iRcptDomains - 1] : "");

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@RFROM") == 0) {
			char *pszNewValue = SysStrDup(FMI.szSender);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@RRCPT") == 0) {
			char *pszNewValue = SysStrDup(FMI.szRecipient);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@FILE") == 0) {
			char *pszNewValue = SysStrDup(FS.szFilePath);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@MSGID") == 0) {
			char *pszNewValue = SysStrDup(pszMessageID);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@MSGREF") == 0) {
			char *pszNewValue = SysStrDup(pszSmtpMessageID);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@LOCALADDR") == 0) {
			char *pszNewValue = SysStrDup(ppszInfo[smiServerAddr]);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		} else if (strcmp(ppszCmdTokens[ii], "@@REMOTEADDR") == 0) {
			char *pszNewValue = SysStrDup(ppszInfo[smiClientAddr]);

			if (pszNewValue == NULL)
				return (ErrGetErrorCode());

			SysFree(ppszCmdTokens[ii]);

			ppszCmdTokens[ii] = pszNewValue;
		}

	}

	return (0);

}