summaryrefslogtreecommitdiff
path: root/spamfilter/spamdefinitions.c
diff options
context:
space:
mode:
Diffstat (limited to 'spamfilter/spamdefinitions.c')
-rw-r--r--spamfilter/spamdefinitions.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/spamfilter/spamdefinitions.c b/spamfilter/spamdefinitions.c
new file mode 100644
index 0000000..5cfe40b
--- /dev/null
+++ b/spamfilter/spamdefinitions.c
@@ -0,0 +1,850 @@
+/*
+
+"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);
+} \ No newline at end of file