/* "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" // ----------------------------------------- BOOL WriteToLogFile(const char* pszMsgTypeSection, const char* pszMsgTypeName, const WCHAR* pszUserName, const WCHAR* pszMsgContent, STRINGLIST* pslRecognition, DWORD dwResult) { BOOL bReturn; HANDLE hFile; DWORD dwWritten; // For Windows 95/98/ME compatibility for WriteFile (parameter needed) STRINGLIST* pslVariablesFromTo; WCHAR szLogFile[MAX_PATH]; int iBuf; WCHAR* pszBuf; // Get file to log to { DBVARIANT dbv; if (DBGetContactSettingTString(NULL, DB_MODULE_NAME, DB_SETTING_LOGFILE, &dbv) == 0) { mir_sntprintf(szLogFile, ARRAYSIZE(szLogFile), _T("%s"), dbv.pszVal); DBFreeVariant(&dbv); PMakePathUsable(szLogFile); } else { szLogFile[0] = _T('\0'); } } // Make sure the directory exists: SILENT! PPreparePathForWrite(NULL, szLogFile, FALSE, TRUE); // Open file hFile = CreateFile(szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); bReturn = (hFile != INVALID_HANDLE_VALUE); if (!bReturn) { WCHAR* pszLastError = GetLastErrorDescription(GetLastError()); ShowInfoMessage(NIIF_ERROR, TranslateT("Spam Filter Error"), TranslateT("The log file \"%s\"\r\ncan not be written.\r\n\r\nProblem:\r\n%s\r\nPlease check the settings."), lstrlen(szLogFile)+lstrlen(pszLastError), (lstrlen(PathFindFileName(szLogFile))>0)?PathFindFileName(szLogFile):szLogFile, pszLastError?pszLastError:_T("")); if (pszLastError) mir_free(pszLastError); return FALSE; } // Variables list (From-To) pslVariablesFromTo = SLNewList(); // %log_time% iBuf = GetTimeFormat(GetThreadLocale(), 0, NULL, NULL, NULL, 0); // or: LOCALE_USER_DEFAULT pszBuf = (WCHAR*)mir_alloc((iBuf+1)*sizeof(WCHAR)); GetTimeFormat(GetThreadLocale(), 0, NULL, NULL, pszBuf, iBuf); // or: LOCALE_USER_DEFAULT SLAddItemPair(pslVariablesFromTo, _T("%log_time%"), pszBuf); if (pszBuf) mir_free(pszBuf); // %log_date% iBuf = GetDateFormat(GetThreadLocale(), DATE_SHORTDATE, NULL, NULL, NULL, 0); // or: LOCALE_USER_DEFAULT pszBuf = (WCHAR*)mir_alloc((iBuf+1)*sizeof(WCHAR)); GetDateFormat(GetThreadLocale(), DATE_SHORTDATE, NULL, NULL, pszBuf, iBuf); // or: LOCALE_USER_DEFAULT SLAddItemPair(pslVariablesFromTo, _T("%log_date%"), pszBuf); if (pszBuf) mir_free(pszBuf); // %log_user% SLAddItemPair(pslVariablesFromTo, _T("%log_user%"), pszUserName); // %log_type% EnterCriticalSection(&csMsgTypes); // thread safety iBuf = GetMsgTypeID(pszMsgTypeSection, pszMsgTypeName); pszBuf = (iBuf >= 0) ? pamtdMsgTypes[iBuf].ptszDescription : NULL; SLAddItemPair(pslVariablesFromTo, _T("%log_type%"), pszBuf); LeaveCriticalSection(&csMsgTypes); // thread safety // %log_recognition% pszBuf = SLConvertToString(pslRecognition, TranslateT(", "), FALSE, FALSE, NULL); SLAddItemPair(pslVariablesFromTo, _T("%log_recognition%"), pszBuf); if (pszBuf) SLFreeReturn(pszBuf); // %log_result% if (dwResult&SFF_MARKREAD) pszBuf = TranslateT("Message marked read"); else if (dwResult&SFF_IGNORE) pszBuf = TranslateT("Message deleted"); else pszBuf = TranslateT("User ignored"); SLAddItemPair(pslVariablesFromTo, _T("%log_result%"), pszBuf); // %log_message% SLAddItemPair(pslVariablesFromTo, _T("%log_message%"), pszMsgContent); // ##### XML format ##### if (PathMatchSpec(szLogFile, _T("*.xml"))) { HANDLE hXSLFile; WCHAR szXSLFile[MAX_PATH]; STRINGLIST* pslSpecialCharsFromTo; char* pszUtf8; WCHAR* pszBuf2; // Create XSL file name mir_sntprintf(szXSLFile, ARRAYSIZE(szXSLFile), _T("%s"), szLogFile); PathRenameExtension(szXSLFile, _T(".xsl")); // Construct XML special char replace filter pslSpecialCharsFromTo = SLNewList(); SLAddItemPair(pslSpecialCharsFromTo, _T("&"), _T("&")); SLAddItemPair(pslSpecialCharsFromTo, _T("<"), _T("<")); SLAddItemPair(pslSpecialCharsFromTo, _T(">"), _T(">")); SLAddItemPair(pslSpecialCharsFromTo, _T("'"), _T("'")); SLAddItemPair(pslSpecialCharsFromTo, _T("\""), _T(""")); SLAddItemPair(pslSpecialCharsFromTo, _T(" "), _T("  "));// Only one single space is allowed (multiple must be replaced) // Replace special chars in replacement variables for (iBuf=SL_MIN_POS; iBuf<=SLGetMaxPos(pslVariablesFromTo); iBuf+=2) { pszBuf = ReplaceSubStringWithStringMultiple(SLGetItem(pslVariablesFromTo, iBuf), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLChangeItem(pslVariablesFromTo, iBuf, pszBuf, FALSE); if (pszBuf) mir_free(pszBuf); } // Write XSL stylesheet for XML (XSL-File) hXSLFile = CreateFile(szXSLFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (hXSLFile != INVALID_HANDLE_VALUE) // If file does not already exist { STRINGLIST* pslXSLFromTo = SLNewList(); // Translate file contents // ", " pszBuf = ReplaceSubStringWithStringMultiple(TranslateT(", "), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T(", "), pszBuf); if (pszBuf) mir_free(pszBuf); // "Received Spam" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Received Spam"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Received Spam"), pszBuf); if (pszBuf) mir_free(pszBuf); // "Logged since" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Logged since"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Logged since"), pszBuf); if (pszBuf) mir_free(pszBuf); // "Date/Time:" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Date/Time:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Date/Time:"), pszBuf); if (pszBuf) mir_free(pszBuf); // "User:" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("User:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("User:"), pszBuf); if (pszBuf) mir_free(pszBuf); // Type: pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Type:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Type:"), pszBuf); if (pszBuf) mir_free(pszBuf); // "Recognition:" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Recognition:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Recognition:"), pszBuf); if (pszBuf) mir_free(pszBuf); // "Result:" pszBuf = ReplaceSubStringWithStringMultiple(TranslateT("Result:"), pslSpecialCharsFromTo, TRUE, FALSE, NULL); SLAddItemPair(pslXSLFromTo, _T("Result:"), pszBuf); if (pszBuf) mir_free(pszBuf); mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XSLLAYOUTFILETEMPLATE), _T("TEXT")))),&pszBuf2); if (pszBuf2) { pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslXSLFromTo, TRUE, FALSE, NULL); mir_free(pszBuf2); if (pszBuf) { pszUtf8 = mir_utf8encodeW(pszBuf); mir_free(pszBuf); if (pszUtf8) { WriteFile(hXSLFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL); //SetEndOfFile(hXSLFile); mir_free(pszUtf8); } } } SLFreeList(pslXSLFromTo); CloseHandle(hXSLFile); } // Write XML header/footer (XML-File) if (GetFileSize(hFile, NULL) == 0) // If file is empty { // Replace spacial variables in header // %log_xslfile% SLAddItemPair(pslVariablesFromTo, _T("%log_xslfile%"), PathFindFileName(szXSLFile)); mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XMLFILETEMPLATE), _T("TEXT")))),&pszBuf2); if (pszBuf2) { pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslVariablesFromTo, TRUE, FALSE, NULL); mir_free(pszBuf2); if (pszBuf) { pszUtf8 = mir_utf8encodeW(pszBuf); mir_free(pszBuf); if (pszUtf8) { WriteFile(hFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL); //SetEndOfFile(hFile); mir_free(pszUtf8); } } } } // Inject new XML item (XML-File) mir_utf8decode(LockResource(LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_XMLITEMTEMPLATE), _T("TEXT")))),&pszBuf2); if (pszBuf2) { pszBuf = ReplaceSubStringWithStringMultiple(pszBuf2, pslVariablesFromTo, TRUE, FALSE, NULL); mir_free(pszBuf2); if (pszBuf) { pszUtf8 = mir_utf8encodeW(pszBuf); mir_free(pszBuf); if (pszUtf8) { SetFilePointer(hFile, -12, NULL, FILE_END); bReturn = WriteFile(hFile, (PBYTE)pszUtf8, lstrlenA(pszUtf8)*sizeof(char), &dwWritten, NULL); //SetEndOfFile(hFile); mir_free(pszUtf8); } } } SLFreeList(pslSpecialCharsFromTo); // ##### Character Separated Values (CSV) format ##### } else if (PathMatchSpec(szLogFile, _T("*.csv"))) { BOOL bIsUnicodeFile; // Replace special chars in replacement variables for (iBuf=SL_MIN_POS; iBuf<=SLGetMaxPos(pslVariablesFromTo); iBuf+=2) { pszBuf = ReplaceSubStringWithString(SLGetItem(pslVariablesFromTo, iBuf), _T("\""), _T("\"\""), TRUE, FALSE, NULL); SLChangeItem(pslVariablesFromTo, iBuf, pszBuf, FALSE); if (pszBuf) mir_free(pszBuf); } // Unicode and CSV header (Byte-order Mark) if (GetFileSize(hFile, NULL) == 0) // If file is empty { #if defined(UNICODE) // Write Unicode BOM UTF-16LE (Notepad) // FF FE = UTF-16, little-endian // -> indicating Unicode text file if (IsWinVer2000Plus()) { // Notepad doesn't support Unicode on Win9x/ME -> no viewer available WCHAR chBOM = 0xFFFE; bIsUnicodeFile = WriteFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL); } else { bIsUnicodeFile = FALSE; } #else bIsUnicodeFile = FALSE; #endif pszBuf = TranslateT("\"Date\";\"Time\";\"User\";\"Type\";\"Recognition\";\"Result\";\"Message\"\r\n"); if (pszBuf) { #if defined(UNICODE) if (!bIsUnicodeFile) { WCHAR* pszAnsi; mir_utf8decode((char*)pszBuf,&pszAnsi); if (pszAnsi) { bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL); mir_free(pszAnsi); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlenW(pszBuf)*sizeof(WCHAR), &dwWritten, NULL); } #else if (bIsUnicodeFile) { WCHAR* pszUnicode = mir_utf8encodeW(pszBuf); if (pszUnicode) { bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL); mir_free(pszUnicode); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlenA(pszBuf)*sizeof(char), &dwWritten, NULL); } #endif } } else { WCHAR chBOM; // Read BOM header (Is file UTF-16LE?) if (ReadFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL)) bIsUnicodeFile = (chBOM == 0xFFFE); else bIsUnicodeFile = FALSE; } pszBuf = TranslateT("\"%log_date%\";\"%log_time%\";\"%log_user%\";\"%log_type%\";\"%log_recognition%\";\"%log_result%\";\"%log_message%\"\r\n"); pszBuf = ReplaceSubStringWithStringMultiple(pszBuf, pslVariablesFromTo, TRUE, FALSE, NULL); if (pszBuf) { SetFilePointer(hFile, 0, NULL, FILE_END); // Append Unicode/ANSI CSV item #if defined(UNICODE) if (!bIsUnicodeFile) { WCHAR* pszAnsi; mir_utf8decode((char*)pszBuf,&pszAnsi); if (pszAnsi) { bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL); mir_free(pszAnsi); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL); } #else if (bIsUnicodeFile) { WCHAR* pszUnicode = mir_utf8encodeW(pszBuf); if (pszUnicode) { bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL); mir_free(pszUnicode); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL); } #endif //SetEndOfFile(hFile); mir_free(pszBuf); } // ##### Plain-Text format (TXT/LOG) ##### } else { BOOL bIsUnicodeFile; // Unicode header (Byte-order Mark) if (GetFileSize(hFile, NULL) == 0) // If file is empty { #if defined(UNICODE) // Write Unicode BOM UTF-16LE (Notepad) // FF FE = UTF-16, little-endian // -> indicating Unicode text file if (IsWinVer2000Plus()) {// Notepad doesn't support Unicode on Win9x/ME -> no viewer available WCHAR chBOM = 0xFFFE; bIsUnicodeFile = WriteFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL); } else { bIsUnicodeFile = FALSE; } #else bIsUnicodeFile = FALSE; #endif } else { WCHAR chBOM; bIsUnicodeFile = FALSE; // Read BOM header (Is file UTF-16LE?) if (ReadFile(hFile, (PBYTE)&chBOM, sizeof(WCHAR), &dwWritten, NULL)) { bIsUnicodeFile = (chBOM == 0xFFFE); } else { bIsUnicodeFile = FALSE; } } pszBuf = TranslateT("[%log_date% %log_time%, User: %log_user%, Type: %log_type%]\r\nRecognition: %log_recognition%\t\r\nResult: %log_result%\r\n%log_message%\r\n\r\n"); pszBuf = ReplaceSubStringWithStringMultiple(pszBuf, pslVariablesFromTo, TRUE, FALSE, NULL); if (pszBuf) { SetFilePointer(hFile, 0, NULL, FILE_END); // Append Unicode/ANSI log item #if defined(UNICODE) if (!bIsUnicodeFile) { WCHAR* pszAnsi; mir_utf8decode((char*)pszBuf,&pszAnsi); if (pszAnsi) { bReturn = WriteFile(hFile, (PBYTE)pszAnsi, lstrlenW(pszAnsi)*sizeof(char), &dwWritten, NULL); mir_free(pszAnsi); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(WCHAR), &dwWritten, NULL); } #else if (bIsUnicodeFile) { WCHAR* pszUnicode = mir_utf8encodeW(pszBuf); if (pszUnicode) { bReturn = WriteFile(hFile, (PBYTE)pszUnicode, lstrlenW(pszUnicode)*sizeof(WCHAR), &dwWritten, NULL); mir_free(pszUnicode); } } else { bReturn = WriteFile(hFile, (PBYTE)pszBuf, lstrlen(pszBuf)*sizeof(TCHAR), &dwWritten, NULL); } #endif //SetEndOfFile(hFile); mir_free(pszBuf); } } // ##### format end ##### CloseHandle(hFile); SLFreeList(pslVariablesFromTo); // Enable "show log" button if log was created while window open if (IsWindow(hwndSpamFilterOpt)) PostMessage(hwndSpamFilterOpt, SFM_VALIDATE_LOGFILENAME, 0, 0); return bReturn; }