/* "Spam Filter"-Plugin for Miranda IM Copyright 2003-2006 Heiko Herkenrath 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 ("SpamFilter-License.txt"); if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // -- Includes #include "common.h" // -- Defines #define CACHE_MODE_DISABLED (-1) // -- Variables STRINGLIST** apslCache = NULL; CRITICAL_SECTION csCacheAccess; int iCacheMode = CACHE_MODE_DISABLED; HANDLE hDefinitionPathChange = INVALID_HANDLE_VALUE; HANDLE hCustomPathChange = INVALID_HANDLE_VALUE; // -- Variables: Events/Hooks HANDLE hHookCustomFoldersChanged = NULL; // -- Prototypes void ToggleFileUpdateWatcher(BOOL bEnable); // HELPER // ----------------------------------------- BOOL GetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID) { if (!psl || (uSpamDefID > SDID_MAX)) return FALSE; // Return cached item if available if (iCacheMode != CACHE_MODE_DISABLED) { EnterCriticalSection(&csCacheAccess); if (apslCache[uSpamDefID]) if (SLMergeList(psl, apslCache[uSpamDefID])) { LeaveCriticalSection(&csCacheAccess); return TRUE; } LeaveCriticalSection(&csCacheAccess); } { WCHAR szFileName[MAX_PATH]; BOOL bReturn; switch (uSpamDefID) { case SDID_CUSTOM_BADWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw")); break; } case SDID_CUSTOM_GOODWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw")); break; } case SDID_CUSTOM_DISLIKEDWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw")); break; } case SDID_BADWORDS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\BadWords\\*.sfw")); break; } case SDID_HOAXTEXTS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\HoaxTexts\\*.sft")); break; } case SDID_TEASERTEXTS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Teasers\\*.sft")); break; } case SDID_URLTLDS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\URLs\\*.sfc")); break; } case SDID_PHONENUMBERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\PhoneNumbers\\*.sfc")); break; } case SDID_SPECIALCHARS_REPLACE_TEXT: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatText\\*.sfc")); break; } case SDID_SPECIALCHARS_REPLACE_NUMBERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatNumbers\\*.sfc")); break; } case SDID_SPECIALCHARS_SEPARATORS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatSeparators\\*.sfc")); break; } case SDID_SPAMMERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Spammers\\*.sfc")); break; } default: bReturn = FALSE; } if (!bReturn) return FALSE; bReturn = SLLoadFromFile(psl, szFileName); // Add item to cache if (iCacheMode != CACHE_MODE_DISABLED) { EnterCriticalSection(&csCacheAccess); // Insert new item if (!apslCache[uSpamDefID]) apslCache[uSpamDefID] = SLNewList(); // Update cache item SLClearList(apslCache[uSpamDefID]); if (!SLMergeList(apslCache[uSpamDefID], psl)) { SLFreeList(apslCache[uSpamDefID]); apslCache[uSpamDefID] = NULL; } LeaveCriticalSection(&csCacheAccess); } return bReturn; } } BOOL SetSpamDefinitionData(STRINGLIST* psl, UINT uSpamDefID, HWND hwndDlg) { WCHAR szFileName[MAX_PATH]; BOOL bReturn; switch (uSpamDefID) { case SDID_CUSTOM_BADWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw")); break; } case SDID_CUSTOM_GOODWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw")); break; } case SDID_CUSTOM_DISLIKEDWORDS: { bReturn = GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw")); break; } // Default data can't be saved default: bReturn = FALSE; } if (!bReturn) return FALSE; // Make sure the directory exists PPreparePathForWrite(hwndDlg, szFileName, FALSE, TRUE); // Temporaryly disable file change watcher if (iCacheMode != CACHE_MODE_DISABLED) iCacheMode++; bReturn = SLSaveToFile(psl, szFileName); OUT("re-enabled ignored event"); // Re-enable file change watcher if (iCacheMode != CACHE_MODE_DISABLED) iCacheMode--; // Update item in cache if (iCacheMode != CACHE_MODE_DISABLED) { EnterCriticalSection(&csCacheAccess); // Insert new item if (!apslCache[uSpamDefID]) apslCache[uSpamDefID] = SLNewList(); // Update cache SLClearList(apslCache[uSpamDefID]); if (!SLMergeList(apslCache[uSpamDefID], psl)) { SLFreeList(apslCache[uSpamDefID]); apslCache[uSpamDefID] = NULL; } LeaveCriticalSection(&csCacheAccess); } return bReturn; } BOOL IsSpamDefinitionDataPresent(UINT uSpamDefID) { switch (uSpamDefID) { // Custom data can also be empty case SDID_CUSTOM_BADWORDS: { WCHAR szFileName[MAX_PATH]; GetDefinitionsPath(szFileName, TRUE, _T("own-badwords.sfw")); return PathFileExists(szFileName); } case SDID_CUSTOM_GOODWORDS: { WCHAR szFileName[MAX_PATH]; GetDefinitionsPath(szFileName, TRUE, _T("own-goodwords.sfw")); return PathFileExists(szFileName); } case SDID_CUSTOM_DISLIKEDWORDS: { WCHAR szFileName[MAX_PATH]; GetDefinitionsPath(szFileName, TRUE, _T("own-dislikedwords.sfw")); return PathFileExists(szFileName); } // Default data is not allowed to be empty default: { STRINGLIST* psl = SLNewList(); BOOL bReturn; GetSpamDefinitionData(psl, uSpamDefID); bReturn = (SLGetItemCount(psl) > 0); SLFreeList(psl); return bReturn; } } } // ----------------------------------------- BOOL EnumSpamDefinitions(UINT uSpamDefID, BOOL bAtLeastOneTime, SPAMDEFINITIONS_ENUMPROC psdenump, WPARAM wParam, LPARAM lParam) { BOOL bReturn; WCHAR szFileName[MAX_PATH]; WCHAR szFullPath[MAX_PATH]; WIN32_FIND_DATA wfd; HANDLE hFind; SYSTEMTIME stDate; if (!psdenump) return FALSE; switch (uSpamDefID) { case SDID_BADWORDS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\BadWords\\*.sfw")); break; } case SDID_HOAXTEXTS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\HoaxTexts\\*.sft")); break; } case SDID_TEASERTEXTS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Teasers\\*.sft")); break; } case SDID_URLTLDS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\URLs\\*.sfc")); break; } case SDID_PHONENUMBERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\PhoneNumbers\\*.sfc")); break; } case SDID_SPECIALCHARS_REPLACE_TEXT: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatText\\*.sfc")); break; } case SDID_SPECIALCHARS_REPLACE_NUMBERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatNumbers\\*.sfc")); break; } case SDID_SPECIALCHARS_SEPARATORS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\FormatSeparators\\*.sfc")); break; } case SDID_SPAMMERS: { bReturn = GetDefinitionsPath(szFileName, FALSE, _T("\\Spammers\\*.sfc")); break; } /* case SDID_CUSTOM_BADWORDS: case SDID_CUSTOM_GOODWORDS: case SDID_CUSTOM_DISLIKEDWORDS: */ default: bReturn = FALSE; } if (bReturn) { bReturn = FALSE; hFind = FindFirstFile(szFileName, &wfd); PathRemoveFileSpec(szFileName); if (hFind != INVALID_HANDLE_VALUE) { do { if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue; if (FileTimeToSystemTime(&wfd.ftLastWriteTime, &stDate)) { // Construct full path StrCpy(szFullPath, szFileName); PathAppend(szFullPath, wfd.cFileName); // Extract name only PathRemoveExtension(wfd.cFileName); // Ignore "Empty" placeholders if (StrCmpI(wfd.cFileName, _T("Empty")) == 0) continue; if (psdenump(uSpamDefID, szFullPath, wfd.cFileName, &stDate, wParam, lParam)) bReturn = TRUE; else break; } } while (FindNextFile(hFind, &wfd)); FindClose(hFind); } } if (bAtLeastOneTime && !bReturn) psdenump(uSpamDefID, NULL, NULL, NULL, wParam, lParam); return bReturn; } // ----------------------------------------- static void ThreadSpamDefinitionCheck(void* arg) { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); // Give miranda some time to load SleepEx(30000, TRUE); OutputDebugString(_T("Spam Filter: Validating Spam Definitions...\r\n")); if (!Miranda_Terminated()) { BOOL bChecked = FALSE; STRINGLIST* pslCheck = SLNewList(); if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER)) { bChecked = TRUE; GetSpamDefinitionData(pslCheck, SDID_BADWORDS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_TEASERTEXTS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_URLTLDS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_PHONENUMBERS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_SPAMMERS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_REPLACE_TEXT); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_REPLACE_NUMBERS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; GetSpamDefinitionData(pslCheck, SDID_SPECIALCHARS_SEPARATORS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; } if (DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER)) { bChecked = TRUE; GetSpamDefinitionData(pslCheck, SDID_HOAXTEXTS); if (SLGetItemCount(pslCheck) > 0) goto CheckResultOk; } // Show warning if no spam definitions are present if (bChecked) ShowInfoMessage(NIIF_WARNING, TranslateT("Spam Filter Warning"), TranslateT("You did not install any Spam Definition files! Spam Filter can not work as powerful as it could.\r\n\r\nPlease install at least one language of the Spam Definitions."), 0); CheckResultOk: SLFreeList(pslCheck); } } // ----------------------------------------- static int CustomFoldersChanged(WPARAM wParam, LPARAM lParam) { if ((hDefinitionPathChange != INVALID_HANDLE_VALUE) || (hCustomPathChange != INVALID_HANDLE_VALUE)) { ToggleFileUpdateWatcher(FALSE); ToggleFileUpdateWatcher(TRUE); } return 0; } static int InternalServiceWaitOnDataPathChange(WPARAM wParam, LPARAM lParam) // HELPER { int i; OutputDebugString(_T("Spam Filter: Clearing Spam Definitions cache (file was modified).\r\n")); EnterCriticalSection(&csCacheAccess); for (i=0; i<=SDID_MAX; i++) { // Skip custom file cache items when waited separately if (i <= SDID_CUSTOMMAX) if (ServiceExists("SpamFilter/Wait/CustomPathChange")) continue; // Reset cache item SLFreeList(apslCache[i]); apslCache[i] = NULL; } LeaveCriticalSection(&csCacheAccess); // Refresh windows if (IsWindow(hwndSpamDefinitionsInfo)) PostMessage(hwndSpamDefinitionsInfo, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)TRUE, (LPARAM)0); if (IsWindow(hwndAdvertismentFilter)) PostMessage(hwndAdvertismentFilter, SFM_RELOAD_DEFAULTBADWORDS, 0, 0); // Register for next notification if (!FindNextChangeNotification((HANDLE)wParam)) ToggleFileUpdateWatcher(FALSE); return 0; } static int InternalServiceWaitOnCustomPathChange(WPARAM wParam, LPARAM lParam) // HELPER { int i; OutputDebugString(_T("Spam Filter: Clearing Custom Definitions cache (file was modified).\r\n")); // Reset custom cache EnterCriticalSection(&csCacheAccess); for (i=0; i<=SDID_CUSTOMMAX; i++) { // Reset CUSTOM cache item SLFreeList(apslCache[i]); apslCache[i] = NULL; } LeaveCriticalSection(&csCacheAccess); // Register for next notification return (FindNextChangeNotification((HANDLE)wParam) ? 0 : 1); } void ToggleFileUpdateWatcher(BOOL bEnable) // HELPER { static HANDLE hDefinitionPathService; static HANDLE hCustomPathService; // * START WATCHER // Get watch handles and tell Miranda to wait for those handles if (bEnable) { WCHAR szDataPath[MAX_PATH]; WCHAR szCustomPath[MAX_PATH]; char* pszService; BOOL bStarted = FALSE; const DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE; // Already started? if (hDefinitionPathChange != INVALID_HANDLE_VALUE) return; // Watch the data directory "Spam Filter" for any changes GetDefinitionsPath(szDataPath, FALSE, NULL); hDefinitionPathChange = FindFirstChangeNotification(szDataPath, TRUE, dwNotifyFilter); if (hDefinitionPathChange != INVALID_HANDLE_VALUE) { pszService = "SpamFilter/Wait/DataPathChange"; hDefinitionPathService = CreateServiceFunction(pszService, InternalServiceWaitOnDataPathChange); if (hDefinitionPathService) if (CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hDefinitionPathChange, (LPARAM)pszService) == 0) bStarted = TRUE; } // Watch the profile sub-directory "Spam Filter" (custom files) for any changes if (bStarted) { GetDefinitionsPath(szCustomPath, TRUE, NULL); if (StrCmp(szDataPath, szCustomPath) != 0) { hCustomPathChange = FindFirstChangeNotification(szCustomPath, FALSE, dwNotifyFilter); if (hCustomPathChange != INVALID_HANDLE_VALUE) { pszService = "SpamFilter/Wait/CustomPathChange"; hCustomPathService = CreateServiceFunction(pszService, InternalServiceWaitOnCustomPathChange); if (hCustomPathService) if (CallService(MS_SYSTEM_WAITONHANDLE, (WPARAM)hCustomPathChange, (LPARAM)pszService) != 0) { DestroyServiceFunction(hDefinitionPathService); hDefinitionPathService = NULL; } } } if (IsCustomFoldersAvailable()) hHookCustomFoldersChanged = HookEvent(ME_FOLDERS_PATH_CHANGED, CustomFoldersChanged); OutputDebugString(_T("Spam Filter: Spam Definitions file watcher started.\r\n")); return; // SUCCESS! } // else fall through to cleanup } // * STOP WATCHER // Stop data path wait if (hDefinitionPathChange != INVALID_HANDLE_VALUE) { CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hDefinitionPathChange, 0); Sleep(0); // give rest of time slice to miranda (maybe currently waiting) ~10-15ms if (hDefinitionPathService) DestroyServiceFunction(hDefinitionPathService); FindCloseChangeNotification(hDefinitionPathChange); hDefinitionPathChange = INVALID_HANDLE_VALUE; // Stop custom path wait if (hCustomPathChange != INVALID_HANDLE_VALUE) { CallService(MS_SYSTEM_REMOVEWAIT, (WPARAM)hCustomPathChange, 0); Sleep(0); // give rest of time slice to miranda (maybe currently waiting) ~10-15ms if (hCustomPathService) DestroyServiceFunction(hCustomPathService); FindCloseChangeNotification(hCustomPathChange); hCustomPathChange = INVALID_HANDLE_VALUE; } // Custom Folders Plugin support if (hHookCustomFoldersChanged) UnhookEvent(hHookCustomFoldersChanged); OutputDebugString(_T("Spam Filter: Spam Definitions file watcher stopped.\r\n")); } } /* static void ThreadFileUpdateWatcher(void* arg) { static bThreadRunning = FALSE; DWORD dwWaitStatus; BOOL bResetCache; HANDLE ahChangeHandles[2]; // Allow thread only run once at a time if (bThreadRunning) return; bThreadRunning = TRUE; OutputDebugString(_T("Spam Filter: Data file watcher started\r\n")); // Get watch handles { TCHAR szDataPath[MAX_PATH]; TCHAR szCustomPath[MAX_PATH]; // FIXME!: // Custom Folder Plugin: not reloaded when user changed folder // Watch the data directory "Spam Filter" for any changes GetDefinitionsPath(szDataPath, FALSE, NULL); ahChangeHandles[0] = FindFirstChangeNotification(szDataPath, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE); if (ahChangeHandles[0] == INVALID_HANDLE_VALUE) { ahChangeHandles[1] = INVALID_HANDLE_VALUE; goto CleanupChangeHandles; } // Watch the profile sub-directory "Spam Filter" (custom files) for any changes GetDefinitionsPath(szCustomPath, TRUE, NULL); if (StrCmp(szDataPath, szCustomPath) != 0) ahChangeHandles[1] = FindFirstChangeNotification(szCustomPath, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE); else ahChangeHandles[1] = INVALID_HANDLE_VALUE; } // Change notification is set: // Now wait on both notification handles and refresh accordingly. while (!Miranda_Terminated() && (iCacheMode != CACHE_MODE_DISABLED)) { if (ahChangeHandles[1] == INVALID_HANDLE_VALUE) dwWaitStatus = WaitForSingleObjectEx(ahChangeHandles[0], INFINITE, TRUE); else dwWaitStatus = WaitForMultipleObjectsEx(ARRAYSIZE(ahChangeHandles), ahChangeHandles, FALSE, INFINITE, TRUE); bResetCache = (iCacheMode == 0); if ((iCacheMode == CACHE_MODE_DISABLED) || Miranda_Terminated()) break; switch (dwWaitStatus) { // Something in data directory was changed (-> reset data cache) case WAIT_OBJECT_0: { // Check if file watcher is temporaryly disabled if (bResetCache) { int i; OutputDebugString(_T("Spam Filter: File change event, Spam Definition file cache reset\r\n")); EnterCriticalSection(&csCacheAccess); for (i=0; i<=SDID_MAX; i++) { if (i <= SDID_CUSTOMMAX) if (ahChangeHandles[1] != INVALID_HANDLE_VALUE) continue; // Reset cache item SLFreeList(apslCache[i]); apslCache[i] = NULL; } LeaveCriticalSection(&csCacheAccess); if (IsWindow(hwndSpamDefinitionsInfo)) PostMessage(hwndSpamDefinitionsInfo, SFM_REFRESH_SPAMDEFINITIONS, (WPARAM)TRUE, (LPARAM)0); if (IsWindow(hwndAdvertismentFilter)) PostMessage(hwndAdvertismentFilter, SFM_RELOAD_DEFAULTBADWORDS, 0, 0); } if (!FindNextChangeNotification(ahChangeHandles[0])) goto CleanupChangeHandles; break; } // Custom files changed (-> reset custom cache) case WAIT_OBJECT_0+1: { // Check if file watcher is temporaryly disabled if (bResetCache) { OutputDebugString(_T("Spam Filter: File change event, custom data file cache reset\r\n")); // Reset custom cache EnterCriticalSection(&csCacheAccess); SLFreeList(apslCache[SDID_CUSTOM_BADWORDS]); apslCache[SDID_CUSTOM_BADWORDS] = NULL; SLFreeList(apslCache[SDID_CUSTOM_GOODWORDS]); apslCache[SDID_CUSTOM_GOODWORDS] = NULL; SLFreeList(apslCache[SDID_CUSTOM_DISLIKEDWORDS]); apslCache[SDID_CUSTOM_DISLIKEDWORDS] = NULL; LeaveCriticalSection(&csCacheAccess); } // Register for next notification FindNextChangeNotification(ahChangeHandles[1]); break; } } } CleanupChangeHandles: bThreadRunning = FALSE; // Close change notification handles if (ahChangeHandles[0] != INVALID_HANDLE_VALUE) FindCloseChangeNotification(ahChangeHandles[0]); if (ahChangeHandles[1] != INVALID_HANDLE_VALUE) FindCloseChangeNotification(ahChangeHandles[1]); OutputDebugString(_T("Spam Filter: Data file watcher closed\r\n")); } */ // ----------------------------------------- void ToggleSpamDefinitionsCache(BOOL bEnable) { if ((iCacheMode != CACHE_MODE_DISABLED) == bEnable) return; if (bEnable) { // Show warning if no spam definitions are present if (bEnable) forkthread(ThreadSpamDefinitionCheck, 0, NULL); OutputDebugString(_T("Spam Filter: Starting Spam Definitions cache...\r\n")); apslCache = (STRINGLIST**)mir_alloc(sizeof(STRINGLIST*) * (SDID_MAX+1)); if (apslCache) { ZeroMemory(apslCache, sizeof(STRINGLIST*) * (SDID_MAX+1)); InitializeCriticalSection(&csCacheAccess); iCacheMode = 0; // Start file change watcher // Replaced with Miranda's wait service //forkthread(ThreadFileUpdateWatcher, 0, NULL); ToggleFileUpdateWatcher(TRUE); } } else { int i; ToggleFileUpdateWatcher(FALSE); // Clear cache iCacheMode = CACHE_MODE_DISABLED; // Release memory EnterCriticalSection(&csCacheAccess); for (i=0; i<=SDID_MAX; i++) { SLFreeList(apslCache[i]); apslCache[i] = NULL; } LeaveCriticalSection(&csCacheAccess); mir_free(apslCache); apslCache = NULL; DeleteCriticalSection(&csCacheAccess); OutputDebugString(_T("Spam Filter: Spam Definitions cache stopped.\r\n")); } } // ----------------------------------------- void InitSpamDefinitions(void) { // Only start cache if Disliked Messages or Advertisment Filter is enabled (saving memory) if ( DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_ADVERTISMENTFILTER, (BYTE)DEFAULT_SETTING_ADVERTISMENTFILTER) || DBGetContactSettingByte(NULL, DB_MODULE_NAME, DB_SETTING_DISLIKEDMESSAGEFILTER, (BYTE)DEFAULT_SETTING_DISLIKEDMESSAGEFILTER) ) { ToggleSpamDefinitionsCache(TRUE); } } void UninitSpamDefinitions(void) { ToggleSpamDefinitionsCache(FALSE); }