/* 'AutoShutdown'-Plugin for Miranda IM Copyright 2004-2007 H. 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 (Shutdown-License.txt); if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdafx.h" /* Msg Shutdown */ static HANDLE hHookEventAdded; /* Transfer Shutdown */ static HANDLE hHookProtoAck; /* Idle Shutdown */ static HANDLE hHookIdleChanged; /* Status Shutdown */ static HANDLE hHookSettingChanged; /* Weather Shutdown */ static HANDLE hHookWeatherUpdated; /* Services */ static HANDLE hEventWatcherChanged; /* Misc */ static HANDLE hHookModulesLoaded; /************************* Shared *************************************/ static uint16_t currentWatcherType; static void __stdcall MainThreadMapping(void *param) { HANDLE *phDoneEvent = (HANDLE*)param; ServiceShutdown(0, TRUE); /* ensure main thread (for cpu usage shutdown) */ ServiceStopWatcher(0, 0); if (*phDoneEvent != nullptr) SetEvent(*phDoneEvent); } static void __inline ShutdownAndStopWatcher(void) { HANDLE hDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); CallFunctionAsync(MainThreadMapping, &hDoneEvent); if (hDoneEvent != nullptr) { WaitForSingleObject(hDoneEvent, INFINITE); CloseHandle(hDoneEvent); } } /************************* Msg Shutdown *******************************/ static int MsgEventAdded(WPARAM, LPARAM hDbEvent) { if (currentWatcherType & SDWTF_MESSAGE) { DB::EventInfo dbei(hDbEvent); if (!dbei) return 0; if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { DBVARIANT dbv; if (!g_plugin.getWString("Message", &dbv)) { ltrimw(rtrimw(dbv.pwszVal)); ptrW wszMsg(dbei.getText()); if (wszMsg != nullptr && wcsstr(wszMsg, dbv.pwszVal) != nullptr) ShutdownAndStopWatcher(); /* msg with specified text recvd */ mir_free(dbv.pwszVal); /* does NULL check */ } } } return 0; } /************************* Transfer Shutdown **************************/ static HANDLE *transfers; static int nTransfersCount; static int ProtoAck(WPARAM, LPARAM lParam) { ACKDATA *ack = (ACKDATA*)lParam; if (ack->type != ACKTYPE_FILE) return 0; switch (ack->result) { case ACKRESULT_DATA: { for (int i = 0; i < nTransfersCount; ++i) if (transfers[i] == ack->hProcess) break; /* already in list */ /* insert into list */ HANDLE *buf = (HANDLE*)mir_realloc(transfers, (nTransfersCount + 1)*sizeof(HANDLE)); if (buf != nullptr) { transfers = buf; transfers[nTransfersCount] = ack->hProcess; ++nTransfersCount; } break; } case ACKRESULT_SUCCESS: case ACKRESULT_FAILED: case ACKRESULT_DENIED: for (int i = 0; i < nTransfersCount; ++i) { if (transfers[i] == ack->hProcess) { /* remove from list */ if (i < (nTransfersCount - 1)) memmove(&transfers[i], &transfers[i + 1], (nTransfersCount - i - 1)*sizeof(HANDLE)); --nTransfersCount; HANDLE *buf = (HANDLE*)mir_realloc(transfers, nTransfersCount*sizeof(HANDLE)); if (buf != nullptr) transfers = buf; else if (!nTransfersCount) transfers = nullptr; /* stop watcher */ if (!nTransfersCount && (currentWatcherType&SDWTF_FILETRANSFER)) ShutdownAndStopWatcher(); break; } } break; } return 0; } /************************* Idle Shutdown ******************************/ static int IdleChanged(WPARAM, LPARAM lParam) { if (currentWatcherType&SDWTF_IDLE && lParam&IDF_ISIDLE) ShutdownAndStopWatcher(); return 0; } /************************* Status Shutdown ****************************/ static BOOL CheckAllContactsOffline(void) { BOOL fSmartCheck, fAllOffline = TRUE; /* tentatively */ fSmartCheck = g_plugin.getByte("SmartOfflineCheck", SETTING_SMARTOFFLINECHECK_DEFAULT); for (auto &hContact : Contacts()) { char *pszProto = Proto_GetBaseAccountName(hContact); if (pszProto != nullptr && Proto_GetStatus(pszProto) != ID_STATUS_OFFLINE) { if (Contact::IsGroupChat(hContact, pszProto)) continue; if (db_get_w(hContact, pszProto, "Status", 0) != ID_STATUS_OFFLINE) { if (fSmartCheck) { if (Contact::IsHidden(hContact)) continue; if (!Contact::OnList(hContact)) continue; } fAllOffline = FALSE; break; } } } return fAllOffline; } static int StatusSettingChanged(WPARAM wParam, LPARAM lParam) { if (currentWatcherType&SDWTF_STATUS) { DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING*)lParam; if ((HANDLE)wParam != nullptr && dbcws->value.wVal == ID_STATUS_OFFLINE && !strcmp(dbcws->szSetting, "Status")) { char *pszProto = Proto_GetBaseAccountName(wParam); if (pszProto != nullptr && !strcmp(dbcws->szModule, pszProto)) if (CheckAllContactsOffline()) ShutdownAndStopWatcher(); } } return 0; } /************************* Cpu Shutdown *******************************/ static uint32_t idCpuUsageThread; static BOOL CALLBACK CpuUsageWatcherProc(uint8_t nCpuUsage, LPARAM lParam) { static uint8_t nTimesBelow = 0; /* only one watcher thread */ /* terminated? */ if (idCpuUsageThread != GetCurrentThreadId()) { nTimesBelow = 0; return FALSE; /* stop poll thread */ } /* ignore random peaks */ if (nCpuUsage < (uint8_t)lParam) ++nTimesBelow; else nTimesBelow = 0; if (nTimesBelow == 3) { nTimesBelow = 0; ShutdownAndStopWatcher(); return FALSE; /* stop poll thread */ } return TRUE; } /************************* Weather Shutdown ***************************/ static int WeatherUpdated(WPARAM wParam, LPARAM lParam) { char *pszProto = Proto_GetBaseAccountName(wParam); if ((BOOL)lParam && pszProto != nullptr && Proto_GetStatus(pszProto) == ID_STATUS_INVISIBLE) if (g_plugin.getByte("WeatherShutdown", SETTING_WEATHERSHUTDOWN_DEFAULT)) ServiceShutdown(SDSDT_SHUTDOWN, TRUE); return 0; } /************************* Services ***********************************/ INT_PTR ServiceStartWatcher(WPARAM, LPARAM lParam) { /* passing watcherType as lParam is only to be used internally, undocumented */ if (lParam == 0) lParam = (LPARAM)g_plugin.getWord("WatcherFlags", 0); /* invalid flags or empty? */ if (!(lParam&SDWTF_MASK)) return 1; /* no specific time choice? */ if (lParam&SDWTF_SPECIFICTIME && !(lParam&SDWTF_ST_MASK)) return 2; if (currentWatcherType == (uint16_t)lParam) return 3; if (currentWatcherType != 0) { /* Time Shutdown */ CloseCountdownFrame(); /* fails if not opened */ /* Cpu Shutdown */ idCpuUsageThread = 0; } SetShutdownMenuItem(true); SetShutdownToolbarButton(true); currentWatcherType = (uint16_t)lParam; NotifyEventHooks(hEventWatcherChanged, TRUE, 0); /* Time Shutdown */ if (currentWatcherType&SDWTF_SPECIFICTIME) ShowCountdownFrame(currentWatcherType); /* after modules loaded */ /* Cpu Shutdown */ if (currentWatcherType&SDWTF_CPUUSAGE) idCpuUsageThread = PollCpuUsage(CpuUsageWatcherProc, (LPARAM)DBGetContactSettingRangedByte(0, MODULENAME, "CpuUsageThreshold", SETTING_CPUUSAGETHRESHOLD_DEFAULT, 1, 100), 1500); /* Transfer Shutdown */ if (currentWatcherType&SDWTF_FILETRANSFER && !nTransfersCount) ShutdownAndStopWatcher(); /* Status Shutdown */ if (currentWatcherType&SDWTF_STATUS && CheckAllContactsOffline()) ShutdownAndStopWatcher(); return 0; } INT_PTR ServiceStopWatcher(WPARAM, LPARAM) { if (currentWatcherType == 0) return 1; /* Time Shutdown */ if (currentWatcherType&SDWTF_SPECIFICTIME) CloseCountdownFrame(); /* Cpu Shutdown */ idCpuUsageThread = 0; currentWatcherType = 0; SetShutdownMenuItem(FALSE); SetShutdownToolbarButton(FALSE); NotifyEventHooks(hEventWatcherChanged, FALSE, 0); return 0; } INT_PTR ServiceIsWatcherEnabled(WPARAM, LPARAM) { return currentWatcherType != 0; } /************************* Misc ***********************************/ void WatcherModulesLoaded(void) { /* Weather Shutdown */ if (ServiceExists(MS_WEATHER_UPDATE)) hHookWeatherUpdated = HookEvent(ME_WEATHER_UPDATED, WeatherUpdated); /* restore watcher if it was running on last exit */ if (g_plugin.getByte("RememberOnRestart", 0) == SDROR_RUNNING) { g_plugin.setByte("RememberOnRestart", 1); ServiceStartWatcher(0, 0); /* after modules loaded */ } } void InitWatcher(void) { /* Shared */ currentWatcherType = 0; /* Message Shutdown */ hHookEventAdded = HookEvent(ME_DB_EVENT_ADDED, MsgEventAdded); /* Status Shutdown*/ hHookSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, StatusSettingChanged); /* Idle Shutdown */ hHookIdleChanged = HookEvent(ME_IDLE_CHANGED, IdleChanged); /* Transfer Shutdown */ transfers = nullptr; nTransfersCount = 0; hHookProtoAck = HookEvent(ME_PROTO_ACK, ProtoAck); /* Weather Shutdown */ hHookWeatherUpdated = nullptr; /* Services */ hEventWatcherChanged = CreateHookableEvent(ME_AUTOSHUTDOWN_WATCHERCHANGED); CreateServiceFunction(MS_AUTOSHUTDOWN_STARTWATCHER, ServiceStartWatcher); CreateServiceFunction(MS_AUTOSHUTDOWN_STOPWATCHER, ServiceStopWatcher); CreateServiceFunction(MS_AUTOSHUTDOWN_ISWATCHERENABLED, ServiceIsWatcherEnabled); } void UninitWatcher(void) { /* remember watcher if running */ if (!ServiceStopWatcher(0, 0)) if (g_plugin.getByte("RememberOnRestart", SETTING_REMEMBERONRESTART_DEFAULT)) g_plugin.setByte("RememberOnRestart", SDROR_RUNNING); /* Message Shutdown */ UnhookEvent(hHookEventAdded); /* Status Shutdown*/ UnhookEvent(hHookSettingChanged); /* Idle Shutdown */ UnhookEvent(hHookIdleChanged); /* Transfer Shutdown */ UnhookEvent(hHookProtoAck); mir_free(transfers); /* does NULL check */ /* Weather Shutdown */ UnhookEvent(hHookWeatherUpdated); /* does NULL check */ /* Services */ DestroyHookableEvent(hEventWatcherChanged); /* Misc */ UnhookEvent(hHookModulesLoaded); }