/* * Smart Auto Replier (SAR) - auto replier plugin for Miranda IM * * Copyright (C) 2004 - 2012 by Volodymyr M. Shcherbyna * * This file is part of SAR. * * SAR 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 3 of the License, or * (at your option) any later version. * * SAR 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 SAR. If not, see . */ #include "stdafx.h" #include "messageshandler.h" #include "SarLuaScript.h" #include extern CCrushLog CRUSHLOGOBJ; /// init static members... CMessagesHandler * CMessagesHandler::m_pThis = NULL; /// thread that is checking /// users that sent messages, /// analize sent time and if /// reply delay is greater then /// difference between current time and /// sent time thread deletes item. INT WINAPI ContactsFetcherThread(LPVOID lp) { BEGIN_PROTECT_AND_LOG_CODE #define SLEEP_TIME 50 CMessagesHandler *pHolder = CMessagesHandler::GetObject(); if (pHolder) { while (pHolder->m_bShouldWork) { WaitForSingleObject(pHolder->m_hEnableEvent, INFINITE); if (!pHolder->m_bShouldWork) break; EnterCriticalSection(&pHolder->m_critSect); if (pHolder->m_contacts.size() == 0) { LeaveCriticalSection(&pHolder->m_critSect); Sleep (SLEEP_TIME); continue; } SYSTEMTIME sysTime = {0}; FILETIME ft = {0}; LARGE_INTEGER liNow = {0}; GetLocalTime (&sysTime); SystemTimeToFileTime(&sysTime, &ft); memcpy(&liNow, &ft, sizeof(liNow)); ContactsMap::iterator it; CONTACT_METAINFO rawm = {0}; LARGE_INTEGER liDiff = {0}; for (it = pHolder->m_contacts.begin(); it != pHolder->m_contacts.end(); it++) { if (pHolder->m_contacts.size() == 0) { break; } rawm = it->second; liDiff.QuadPart = liNow.QuadPart - rawm.StartTime.QuadPart; if (liDiff.QuadPart >= rawm.DiffTime.QuadPart) { if (pHolder->m_contacts.size() == 1) { pHolder->m_contacts.clear(); break; } else pHolder->m_contacts.erase(it); } } LeaveCriticalSection(&pHolder->m_critSect); Sleep (SLEEP_TIME); } } END_PROTECT_AND_LOG_CODE return FALSE; } /// ctor of main plugin manager CMessagesHandler::CMessagesHandler(void) : IMSingeltone(this), m_hFetcherThread(NULL), m_hEnableEvent(NULL), m_bShouldWork(true), m_bInited(false) { BEGIN_PROTECT_AND_LOG_CODE m_settings.Init(); END_PROTECT_AND_LOG_CODE } /// dtor of main plugin manager CMessagesHandler::~CMessagesHandler(void) { BEGIN_PROTECT_AND_LOG_CODE m_settings.DeInit(); m_bShouldWork = false; CMessagesHandler::m_pThis = NULL; END_PROTECT_AND_LOG_CODE } /// enables or disables feature void CMessagesHandler::MakeAction(bool bEnable) { BEGIN_PROTECT_AND_LOG_CODE REPLYER_SETTINGS & s = m_settings.getSettings(); s.bEnabled = bEnable; m_settings.setSettings(s); if (bEnable) { HookEvents(); SetEvent (m_hEnableEvent); } else UnHookEvents(); END_PROTECT_AND_LOG_CODE } bool CMessagesHandler::AllowReply(HANDLE hContact) { BEGIN_PROTECT_AND_LOG_CODE if (!m_settings.getSettings().bEnabled) return FALSE; ContactsMap::iterator it; bool bRetVal = false; EnterCriticalSection(&m_critSect); it = m_contacts.find(hContact); SYSTEMTIME sysTime = {0}; FILETIME ft = {0}; GetLocalTime (&sysTime); SystemTimeToFileTime(&sysTime, &ft); if (it == m_contacts.end()) { /// no such contact... CONTACT_METAINFO cm = {0}; memcpy(&cm.StartTime, &ft, sizeof(cm.StartTime)); cm.DiffTime.QuadPart = m_settings.getSettings().ReplayDelay * nano100SecInSec; ContactsPair p(hContact, cm); m_contacts.insert(p); bRetVal = true; } else { /// there is one... memcpy(&it->second.StartTime, &ft, sizeof(it->second.StartTime)); } LeaveCriticalSection(&m_critSect); return bRetVal; END_PROTECT_AND_LOG_CODE return false; } void CMessagesHandler::HookEvents(void) { BEGIN_PROTECT_AND_LOG_CODE #ifdef _DEBUG #endif InitializeCriticalSection(&m_critSect); m_settings.Init(); #ifdef _DEBUG bool bEnabled = m_settings.getSettings().bEnabled; #endif m_hEnableEvent = CreateEvent(NULL, TRUE, m_settings.getSettings().bEnabled, NULL); DWORD dw = 0; m_bShouldWork = true; m_hFetcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ContactsFetcherThread, NULL, NULL, &dw); m_hEvents[0] = HookEvent(ME_DB_EVENT_ADDED, CMessagesHandler::EventAddHook); m_bInited = true; END_PROTECT_AND_LOG_CODE } void CMessagesHandler::UnHookEvents(void) { BEGIN_PROTECT_AND_LOG_CODE m_bShouldWork = false; if (!m_bInited) return; SetEvent(m_hEnableEvent); for (size_t i = 0; i < HOOKS_NUM; i++) UnhookEvent(m_hEvents[i]); WaitForSingleObject(m_hFetcherThread, INFINITE); DeleteCriticalSection(&m_critSect); CloseHandle(m_hEnableEvent); //m_settings.DeInit(); m_hEnableEvent = NULL; m_hFetcherThread = NULL; END_PROTECT_AND_LOG_CODE } LPTSTR CMessagesHandler::GetContactName(HANDLE hContact) { LPARAM lFlags = GCDNF_NOMYHANDLE; #ifdef _UNICODE lFlags |= GCDNF_UNICODE; #endif BEGIN_PROTECT_AND_LOG_CODE return reinterpret_cast(CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, lFlags)); END_PROTECT_AND_LOG_CODE return NULL; } int CMessagesHandler::SendProtoMess(HANDLE hContact, LPCSTR szMess) { BEGIN_PROTECT_AND_LOG_CODE return CallContactService(hContact, PSS_MESSAGE, 0, reinterpret_cast(szMess)); END_PROTECT_AND_LOG_CODE return 0; } /** Convert unicode to ansi (utf16 to utf8) * @return ansi string or null */ char* Utf16toUtf8(LPCWSTR lpwszStrIn, UINT & nSize) { LPSTR pszOut = NULL; if (lpwszStrIn != NULL) { #define SAFE_DELTA 2 int nInputStrLen = (int)wcslen(lpwszStrIn); int nOutputStrLen = WideCharToMultiByte(CP_UTF8, 0, lpwszStrIn, nInputStrLen, NULL, 0, 0, 0); if (nOutputStrLen == 0) { return NULL; } nOutputStrLen += SAFE_DELTA; pszOut = (LPSTR)malloc(nOutputStrLen); if (pszOut) { nSize = nOutputStrLen - SAFE_DELTA; memset(pszOut, 0x00, nOutputStrLen); WideCharToMultiByte(CP_UTF8, 0, lpwszStrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0); } } return pszOut; } /** Convert unicode to ansi (utf16 to utf8) * @return ansi string or null */ wchar_t* Utf8toUtf16(CHAR * szStrIn, UINT & nSize) { wchar_t * pszOut = NULL; if (szStrIn != NULL) { #define SAFE_DELTA 2 int nInputStrLen = (int)nSize; int nOutputStrLen = MultiByteToWideChar(CP_UTF8, 0, szStrIn, nInputStrLen, NULL, 0); if (nOutputStrLen == 0) { return NULL; } nOutputStrLen += SAFE_DELTA; pszOut = (wchar_t*)malloc(nOutputStrLen * sizeof(wchar_t)); if (pszOut) { nSize = nOutputStrLen - SAFE_DELTA; memset(pszOut, 0x00, nOutputStrLen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, szStrIn, nInputStrLen, pszOut, nOutputStrLen); } } return pszOut; } /// this handle is invoked wnen /// event is added to db int CMessagesHandler::EventAddHook(WPARAM wp, LPARAM lp) { BEGIN_PROTECT_AND_LOG_CODE DWORD dwOffset = static_cast (lp); HANDLE hContact = reinterpret_cast(wp); DBEVENTINFO dbei; if (!hContact || !dwOffset) { return FALSE; /// unspecifyed error } ZeroMemory(&dbei, sizeof(dbei)); dbei.cbSize = sizeof(dbei); dbei.cbBlob = 0; CallService(MS_DB_EVENT_GET, lp, (LPARAM)&dbei); /// detect size of msg if ((dbei.eventType != EVENTTYPE_MESSAGE) || (dbei.flags == DBEF_READ) || (dbei.flags == DBEF_SENT) ) { return FALSE; /// we need EVENTTYPE_MESSAGE event.. } else { /// needed event has occured.. if (!dbei.cbBlob) /// invalid size { return FALSE; } dbei.pBlob = new BYTE[dbei.cbBlob]; if (dbei.pBlob) { LPARAM lParam = GCDNF_NOMYHANDLE; #ifdef _UNICODE lParam |= GCDNF_UNICODE; #endif CallService(MS_DB_EVENT_GET, lp, reinterpret_cast(&dbei)); TCHAR * szMessage = DbGetEventTextT(&dbei, 0); TCHAR * szContactName = reinterpret_cast(CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wp, lParam)); CMessagesHandler *ptrHolder = NULL; ptrHolder = CMessagesHandler::GetObject(); if (!szContactName || !ptrHolder) { return FALSE; } if (!ptrHolder->AllowReply(hContact)) { return FALSE; } TCHAR * lpMessage = NULL; { RULE_METAINFO inf = {0}; inf.ContactName = szContactName; /// analizes all rules and formes autoreply /// message - it's stored in lpMessage /// then - just sent it to user CSettingsHandler & settingsManager = ptrHolder->getSettings(); settingsManager.getStorage().IsRuleMatch(inf, lpMessage, szMessage); { /// do the lua trick CLuaBridge luaBridge; CSarLuaScript script(luaBridge); UINT nReplyScriptLength = 0; CHAR * szReplyScript = NULL; szReplyScript = Utf16toUtf8(lpMessage, nReplyScriptLength); if (szReplyScript != NULL) { UINT nUserMessage = 0; CHAR * szUserMessage = Utf16toUtf8(szMessage, nUserMessage); UINT nUserName = 0; CHAR * szUserName = Utf16toUtf8(szContactName, nUserName); script.CompileScript(szReplyScript, nReplyScriptLength); script.SelectScriptFunction("SAR"); script.AddParam((int)hContact); script.AddParam(szUserMessage); script.AddParam(szUserName); script.AddParam((char*)dbei.szModule); script.Run(); free(szReplyScript); free(szUserMessage); free(szUserName); } } mir_free(szMessage); delete lpMessage; delete dbei.pBlob; return FALSE; } } } END_PROTECT_AND_LOG_CODE return FALSE; } void CMessagesHandler::WriteToHistory(LPTSTR lpMsg, HANDLE hContact) { LPTSTR lp1 = TranslateTS(TEXT("Generated autoreply:\r\n")); LPTSTR lpszMess = new TCHAR[_tcslen(lpMsg) + _tcslen(lp1)]; if (lpszMess == NULL) return; memset(lpszMess, 0, (_tcslen(lpMsg) + _tcslen(lp1)) * sizeof(TCHAR)); _tcscpy(lpszMess, lp1); _tcscat(lpszMess, lpMsg); DBEVENTINFO dbei = {0}; dbei.cbSize = sizeof(dbei); dbei.eventType = EVENTTYPE_MESSAGE; dbei.flags = DBEF_READ; dbei.szModule = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact, 0); dbei.timestamp = time(NULL); dbei.cbBlob = _tcslen(lpszMess) + 1; dbei.pBlob = (PBYTE)lpszMess; CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei); delete lpszMess; } /// getting settings.. CSettingsHandler & CMessagesHandler::getSettings(void) { BEGIN_PROTECT_AND_LOG_CODE return m_settings; END_PROTECT_AND_LOG_CODE }