/* "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" // -- Variables: Events/Hooks/Services HANDLE hHookDbContactAdded = NULL; HANDLE hHookDbEventFilterAdd = NULL; // ----------------------------------------- static int SpamCheckProtoRecv(WPARAM wParam, LPARAM lParam) { CCSDATA* pccsd = (CCSDATA*)lParam; BOOL bNotOnList; SPAMCHECKDATA scd; DWORD dwSpamResult; int iReturn; ZeroMemory(&scd, sizeof(scd)); Netlib_Logf(NULL, "Spam Filter: Catching MS_PROTO_CHAINRECV"); // Message already marked as read (skipping spam check) if (((PROTORECVEVENT*)pccsd->lParam)->flags&PREF_CREATEREAD) { Netlib_Logf(NULL, "Spam Filter: Already marked as read (skipping spam check)"); return CallService(MS_PROTO_CHAINRECV, wParam, lParam); } if (StrCmpA(pccsd->szProtoService, PSR_URL) == 0) { PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam); scd.hContact = pccsd->hContact; // Interpret blob (ignore url itself, only description) scd.pszMsgText = ppre->szMessage; scd.pszMsgTypeName = SFMT_URL; } else if (StrCmpA(pccsd->szProtoService, PSR_AUTH) == 0) { PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam); scd.hContact = (*((PHANDLE)(ppre->szMessage+sizeof(DWORD)))); // Check if stored hContact is still valid if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0)) { Netlib_Logf(NULL, "Spam Filter: Contact invalid (skipping spam check)"); // Continue the chain return CallService(MS_PROTO_CHAINRECV, wParam, lParam); } // Interpret blob (only use reason text) scd.pszMsgText = (char*)(ppre->szMessage+sizeof(DWORD)+sizeof(HANDLE)); // uin, hcontact, nick scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // first scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // last scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // email scd.pszMsgText = scd.pszMsgText+(lstrlenA(scd.pszMsgText)+1)*sizeof(char); // reason // If no reason text is given deactivate the text checking if (strlen(scd.pszMsgText) <= 0) scd.pszMsgText = NULL; scd.pszMsgTypeName = SFMT_AUTHREQUEST; } else if (StrCmpA(pccsd->szProtoService, PSR_MESSAGE) == 0) { PROTORECVEVENT* ppre = ((PROTORECVEVENT*)pccsd->lParam); scd.hContact = pccsd->hContact; // Interpret blob (use all message) if (ppre->flags&PREF_UNICODE) { scd.dwFlags |= SCDF_UNICODE; scd.pszMsgText = ppre->szMessage+lstrlenA(ppre->szMessage)+1; } else { scd.pszMsgText = ppre->szMessage; } scd.pszMsgTypeName = SFMT_MESSAGE; } else if (StrCmpA(pccsd->szProtoService, PSR_FILE) == 0) { PROTORECVFILE* pprf = ((PROTORECVFILE*)pccsd->lParam); scd.hContact = pccsd->hContact; // Interpret blob (only use description text) scd.pszMsgText = pprf->szDescription; scd.pszMsgTypeName = SFMT_FILE; } else if (StrCmpA(pccsd->szProtoService, PSR_CONTACTS) == 0) { scd.hContact = pccsd->hContact; // Does not have any text to filter only contact names: nick, first, last, email scd.pszMsgText = NULL; scd.pszMsgTypeName = SFMT_CONTACTS; } else { Netlib_Logf(NULL, "Spam Filter: Format unknown (skipping spam check)"); // Continue the chain return CallService(MS_PROTO_CHAINRECV, wParam, lParam); } scd.cbSize = sizeof(scd); scd.pszMsgTypeSection = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)scd.hContact, 0); // If message is from "Unknown Contact" (very unlikely) -> MS_PROTO_GETCONTACTBASEPROTO returns NULL if (!scd.pszMsgTypeSection) { Netlib_Logf(NULL, "Spam Filter Result: No base protocol (skipping spam check)"); return CallService(MS_PROTO_CHAINRECV, wParam, lParam); } Netlib_Logf(NULL, "Spam Filter: Type = %s_%s", scd.pszMsgTypeSection, scd.pszMsgTypeName); // Result check bNotOnList = (DBGetContactSettingByte(scd.hContact, "CList", "NotOnList", 0) ? TRUE : FALSE); if (bNotOnList) { // If message is from test passed contact if(DBGetContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)FALSE)) { Netlib_Logf(NULL, "Spam Filter: Result = Already passed robot test (no spam)"); return CallService(MS_PROTO_CHAINRECV, wParam, lParam); } Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ADVERTISMENTCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_ADVERTISMENTCHECK, (WPARAM)&scd, 0); if (dwSpamResult&SFF_ISNORMAL) { Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ROBOTCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_ROBOTCHECK, (WPARAM)&scd, 0); } } else { Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_DISLIKEDMESSAGESCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_DISLIKEDMESSAGESCHECK, (WPARAM)&scd, 0); } if (dwSpamResult&SFF_ISNORMAL) { Netlib_Logf(NULL, "Spam Filter: Recognition = No-Spam"); } else { Netlib_Logf(NULL, "Spam Filter: Recognition = Spam!"); } // Message handling if (dwSpamResult&SFF_DELETE) { Netlib_Logf(NULL, "Spam Filter: Result = Event suppressed (stopping recv chain)"); // Cache chain (see reply messages) iReturn = 1; // Stop processing the chain } else if (dwSpamResult&SFF_MARKREAD) { (((PROTORECVEVENT*)pccsd->lParam)->flags) |= PREF_CREATEREAD; Netlib_Logf(NULL, "Spam Filter: Result = PREF_CREATEREAD flag set (continuing recv chain)"); // Cache chain (see reply messages) iReturn = CallService(MS_PROTO_CHAINRECV, wParam, lParam); } else { Netlib_Logf(NULL, "Spam Filter: Result = Event accepted (continuing recv chain)"); // Cache chain (see reply messages) iReturn = CallService(MS_PROTO_CHAINRECV, wParam, lParam); } // IMPORTANT!: The following is done *after* the call to MS_PROTO_CHAINRECV so that // the protocol has had the chance to add the recved event to the db. // The events to be sent are then ordered correctly in db. // The contact might have been deleted etc. by another filter protcol in recv chain // -> just a safety check if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0)) { Netlib_Logf(NULL, "Spam Filter: Contact deleted by other filter (stopping processing)"); return iReturn; } // Remember test passed (robot filter) if (dwSpamResult&SFF_TESTPASSED) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_TESTPASSED (restoring contact)"); // Mark contact as answered DBWriteContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)TRUE); DBDeleteContactSetting(scd.hContact, "CList", "Hidden"); // Show the old events again MarkAllContactEventsUnRead(scd.hContact); } // Contact handling if (dwSpamResult&SFF_IGNORE) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_IGNORE (ignoring contact)"); AddContactToIgnoreList(scd.hContact, (dwSpamResult&SFF_MARKREAD), TRUE, FALSE); } if (dwSpamResult&SFF_HIDE) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_HIDE (hiding contact)"); DBWriteContactSettingByte(scd.hContact, "CList", "Hidden", 1); } // Reply messages if (dwSpamResult&SFF_SENDMSG_NOTIFY) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_NOTIFY, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_NOTIFY); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_NOTIFY (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } if (dwSpamResult&SFF_SENDMSG_INSTRUCTION) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_INSTRUCTION, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_INSTRUCTION); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_INSTRUCTION (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } if (dwSpamResult&SFF_SENDMSG_CONFIRMATION) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_CONFIRMATION, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_CONFIRMATION); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_CONFIRMATION (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } // Contact handling if ((dwSpamResult&SFF_DELETE) && !(dwSpamResult&SFF_IGNORE)) { if (bNotOnList && !(dwSpamResult&SFF_TESTPASSED) && (CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)scd.hContact, 0) == 0) ) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_DELETE (deleting contact)"); CallService(MS_DB_CONTACT_DELETE, (WPARAM)scd.hContact, 0); } } // return queue value return iReturn; } static int DbEventFilterAdd(WPARAM wParam, LPARAM lParam) { DBEVENTINFO* pdbei = (DBEVENTINFO*)lParam; HANDLE hContact = (HANDLE)wParam; // Filtring events that don't have any protorecv service // -> hacky workaround: filtering db adds // Only filter incoming events if (!pdbei || !pdbei->pBlob || (pdbei->flags&DBEF_SENT) || (pdbei->flags&DBEF_READ) ) return 0; // continue adding the event // Only filter following events if ( (pdbei->eventType != EVENTTYPE_ADDED) && (pdbei->eventType != ICQEVENTTYPE_EMAILEXPRESS) && (pdbei->eventType != ICQEVENTTYPE_WEBPAGER) && (pdbei->eventType != ICQEVENTTYPE_SMS) ) return 0; { BOOL bNotOnList; BOOL bAlsoDoRobotCheck; SPAMCHECKDATA scd; DWORD dwSpamResult; Netlib_Logf(NULL, "Spam Filter: Catching ME_DB_EVENT_FILTER_ADD"); // Collecting data out of blob ZeroMemory(&scd, sizeof(scd)); switch (pdbei->eventType) { case EVENTTYPE_ADDED: { // Blob: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) if (hContact) scd.hContact = hContact; else scd.hContact = *((PHANDLE)(pdbei->pBlob+sizeof(DWORD))); // Check if stored hContact is still valid if (!CallService(MS_DB_CONTACT_IS, (WPARAM)scd.hContact, 0)) { Netlib_Logf(NULL, "Spam Filter: Contact invalid (skipping spam check)"); return 0; // continue adding the event } // No text to be checked available scd.pszMsgText = NULL; scd.pszMsgTypeName = SFMT_ADDED; bAlsoDoRobotCheck = TRUE; break; } case ICQEVENTTYPE_EMAILEXPRESS: // (ICQ only) { // Blob: text, usually "Sender IP: xxx.xxx.xxx.xxx\r\n%s"(ASCIIZ), name(ASCIIZ), email(ASCIIZ) // Interpret blob (get sender name) if (hContact) { scd.hContact = hContact; } else { scd.pszUserName = (char*)pdbei->pBlob; // text scd.pszUserName = scd.pszUserName+lstrlenA(scd.pszUserName)+1; // name scd.dwFlags = SCDF_NO_CONTACT|SCDF_NO_AUTOIGNORE; } // Interpret blob (only use message text) scd.pszMsgText = (char*)pdbei->pBlob; // text scd.pszMsgTypeName = "EmailExpress"; bAlsoDoRobotCheck = FALSE; break; } case ICQEVENTTYPE_WEBPAGER: // (ICQ only) { // Blob: text, usually "Subject: %s\r\n%s"(ASCIIZ), name(ASCIIZ), email(ASCIIZ) if (hContact) { scd.hContact = hContact; } else { // Interpret blob (get sender name) scd.pszUserName = (char*)pdbei->pBlob; // text scd.pszUserName = scd.pszUserName+lstrlenA(scd.pszUserName)+1; // name scd.dwFlags = SCDF_NO_CONTACT|SCDF_NO_AUTOIGNORE; } // Interpret blob (only use message text) scd.pszMsgText = (char*)pdbei->pBlob; // text scd.pszMsgTypeName = "WebPager"; bAlsoDoRobotCheck = FALSE; break; } default: return 0; } scd.pszMsgTypeSection = pdbei->szModule; if (!scd.pszMsgTypeSection) { Netlib_Logf(NULL, "Spam Filter: No base protocol (skipping spam check)"); return 0; // continue adding the event } scd.cbSize = sizeof(scd); Netlib_Logf(NULL, "Spam Filter: Type = %s_%s", scd.pszMsgTypeSection, scd.pszMsgTypeName); // Result check bNotOnList = !bAlsoDoRobotCheck || (DBGetContactSettingByte(scd.hContact, "CList", "NotOnList", 0) ? TRUE : FALSE); if (bNotOnList) { if (bAlsoDoRobotCheck) { // If message is from test passed contact if(DBGetContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)FALSE)) { Netlib_Logf(NULL, "Spam Filter: Result = Already passed robot test (no spam)"); return 0; // continue adding the event } } Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ADVERTISMENTCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_ADVERTISMENTCHECK, (WPARAM)&scd, 0); if (dwSpamResult&SFF_ISNORMAL) { if (bAlsoDoRobotCheck) { Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_ROBOTCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_ROBOTCHECK, (WPARAM)&scd, 0); } } } else { Netlib_Logf(NULL, "Spam Filter: Running MS_SPAMFILTER_DISLIKEDMESSAGESCHECK"); dwSpamResult = CallService(MS_SPAMFILTER_DISLIKEDMESSAGESCHECK, (WPARAM)&scd, 0); } if (dwSpamResult&SFF_ISNORMAL) { Netlib_Logf(NULL, "Spam Filter: Recognition = No-Spam"); } else { Netlib_Logf(NULL, "Spam Filter: Recognition = Spam!"); } // Remember test passed (robot filter) if (dwSpamResult&SFF_TESTPASSED) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_TESTPASSED (restoring contact)"); // Mark contact as answered DBWriteContactSettingByte(scd.hContact, DB_MODULE_NAME, DB_SETTING_ROBOT_TESTPASSED, (BYTE)TRUE); DBDeleteContactSetting(scd.hContact, "CList", "Hidden"); // Show the old events again MarkAllContactEventsUnRead(scd.hContact); } // Contact handling if (dwSpamResult&SFF_IGNORE) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_IGNORE (ignoring contact)"); AddContactToIgnoreList(scd.hContact, (dwSpamResult&SFF_MARKREAD), TRUE, FALSE); } if (dwSpamResult&SFF_HIDE) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_HIDE (hiding contact)"); DBWriteContactSettingByte(scd.hContact, "CList", "Hidden", 1); } // Reply messages if (dwSpamResult&SFF_SENDMSG_NOTIFY) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_NOTIFY, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_NOTIFY); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_NOTIFY (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } if (dwSpamResult&SFF_SENDMSG_INSTRUCTION) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_INSTRUCTION, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_INSTRUCTION); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_INSTRUCTION (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } if (dwSpamResult&SFF_SENDMSG_CONFIRMATION) { WCHAR* pszMsgText = (WCHAR*)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_MSGTEXT_CONFIRMATION, (LPARAM)&scd); BOOL bAddToHistory = (BOOL)CallService(MS_SPAMFILTER_GETSPAMCHECKINFO, (WPARAM)SFSCI_ADD_TO_HISTORY, (LPARAM)SFSCI_MSGTEXT_CONFIRMATION); Netlib_Logf(NULL, "Spam Filter: Action = SFF_SENDMSG_CONFIRMATION (sending reply)"); if (!SendContactAutomatedMessage(scd.hContact, pszMsgText, bAddToHistory)) CallService(MS_SPAMFILTER_SHOWERROR, (WPARAM)SFSE_SEND_FAILED, (LPARAM)&scd); if (pszMsgText) mir_free(pszMsgText); } // Contact handling if ((dwSpamResult&SFF_DELETE) && !(dwSpamResult&SFF_IGNORE)) { if (bNotOnList && !(dwSpamResult&SFF_TESTPASSED) && (CallService(MS_DB_EVENT_GETCOUNT, (WPARAM)scd.hContact, 0) == 0) ) { Netlib_Logf(NULL, "Spam Filter: Action = SFF_DELETE (deleting contact)"); CallService(MS_DB_CONTACT_DELETE, (WPARAM)scd.hContact, 0); } } // Message handling if (dwSpamResult&SFF_DELETE) { Netlib_Logf(NULL, "Spam Filter: Result = Event add prevented"); return 1; // Stop adding the event } else if (dwSpamResult&SFF_MARKREAD) { pdbei->flags |= DBEF_READ; Netlib_Logf(NULL, "Spam Filter: Result = DBEF_READ flag set (continuing add)"); return 0; } else { Netlib_Logf(NULL, "Spam Filter: Result = Event accepted (continuing add)"); return 0; } } } // ---------------------------------- static int SpamCheckProtoGetCaps(WPARAM wParam, LPARAM lParam) { // Non-network-access modules should return flags // to represent the things they actually actively use switch (wParam) { case PFLAGNUM_1: return PF1_IMSEND|PF1_IMRECV|PF1_URLRECV|PF1_FILERECV|PF1_CONTACTRECV|PF1_AUTHREQ|PF1_ADDED; default: return 0; } } #if defined(UNICODE) static int SpamCheckProtoGetNameW(WPARAM wParam, LPARAM lParam) { int cchName = (int)wParam; WCHAR* pszName = (WCHAR*)lParam; if (pszName && (cchName > 0)) { mir_sntprintf(pszName, cchName, _T("%s"), TranslateT("Spam Filter")); return 0; } return 1; } #endif static int SpamCheckProtoGetName(WPARAM wParam, LPARAM lParam) { int cchName = (int)wParam; char* pszName = (char*)lParam; if (pszName && (cchName > 0)) { mir_snprintf(pszName, cchName, "%s", Translate("Spam Filter")); return 0; } return 1; } static int DbContactAdded(WPARAM wParam, LPARAM lParam) { // Error here is very very unlikely if (CallService(MS_PROTO_ADDTOCONTACT, wParam, (LPARAM)DB_MODULE_NAME) == 0) Netlib_Logf(NULL, "Spam Filter: Protocol filter attached to contact \"%s\" (%i)", (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)wParam, (LPARAM)GCDNF_UNICODE), wParam); else CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0); return 0; } // ---------------------------------- void AssignAllContacts(void) { HANDLE hContact; // MS_PROTO_ADDTOCONTACT needs to be call in AllModulesLoaded! // Bug-Tracker #787: // If MS_PROTO_ADDTOCONTACT is called when some plugin mentioned // in modules chain isn't loaded, the chain will be sorted incorrectly, // because the call to Proto_IsProtocolLoaded in service Proto_AddToContact // returns NULL pointer to PROTOCOLDESCRIPTOR and the service can't determine the order. for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); hContact; hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) if (!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)DB_MODULE_NAME)) { if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)DB_MODULE_NAME) == 0) Netlib_Logf(NULL, "Spam Filter: Protocol filter attached to contact \"%s\" (%i)", (WCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, (LPARAM)GCDNF_UNICODE), hContact); else CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0); } } void InitSpamCheck(void) { BOOL bSuccess; PROTOCOLDESCRIPTOR pd; ZeroMemory(&pd, sizeof(pd)); pd.cbSize = sizeof(pd); pd.szName = DB_MODULE_NAME; pd.type = PROTOTYPE_IGNORE+100; // Ignore module = PROTOTYPE_IGNORE (old: PROTOTYPE_FILTER-100) if (CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd) == 0) { bSuccess = TRUE; // General protocol functions CreateProtoServiceFunction(pd.szName, PS_GETNAME, SpamCheckProtoGetName); CreateProtoServiceFunction(pd.szName, PS_GETCAPS, SpamCheckProtoGetCaps); #if defined(UNICODE) CreateProtoServiceFunction(pd.szName, PS_GETNAME"W", SpamCheckProtoGetNameW); #endif // Recv chain functions if (!CreateProtoServiceFunction(pd.szName, PSR_AUTH, SpamCheckProtoRecv)) bSuccess = FALSE; if (!CreateProtoServiceFunction(pd.szName, PSR_MESSAGE, SpamCheckProtoRecv)) bSuccess = FALSE; if (!CreateProtoServiceFunction(pd.szName, PSR_URL, SpamCheckProtoRecv)) bSuccess = FALSE; if (!CreateProtoServiceFunction(pd.szName, PSR_FILE, SpamCheckProtoRecv)) bSuccess = FALSE; if (!CreateProtoServiceFunction(pd.szName, PSR_CONTACTS, SpamCheckProtoRecv)) bSuccess = FALSE; // Register for events not added via recv chain hHookDbEventFilterAdd = HookEvent(ME_DB_EVENT_FILTER_ADD, DbEventFilterAdd); if (!hHookDbEventFilterAdd) bSuccess = FALSE; // Add filter protocol to all contacts hHookDbContactAdded = HookEvent(ME_DB_CONTACT_ADDED, DbContactAdded); if (!hHookDbContactAdded) bSuccess = FALSE; } else { bSuccess = FALSE; } // Error here is very very unlikely if (bSuccess) Netlib_Logf(NULL, "Spam Filter: Protocol filter initialized (%hs)", DB_MODULE_NAME); else CallService(MS_SPAMFILTER_SHOWERROR, SFSE_CRITICAL_ERROR, 0); } void UninitSpamCheck(void) { if (hHookDbContactAdded) UnhookEvent(hHookDbContactAdded); if (hHookDbEventFilterAdd) UnhookEvent(hHookDbEventFilterAdd); Netlib_Logf(NULL, "Spam Filter: Protocol filter uninitialized (%hs)", DB_MODULE_NAME); }