/* '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 "common.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 hServiceStartWatcher,hServiceStopWatcher,hServiceIsEnabled; static HANDLE hEventWatcherChanged; /* Misc */ static HANDLE hHookModulesLoaded; /************************* Shared *************************************/ static WORD 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 != NULL) SetEvent(*phDoneEvent); } static void __inline ShutdownAndStopWatcher(void) { HANDLE hDoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL); CallFunctionAsync(MainThreadMapping, &hDoneEvent); if (hDoneEvent != NULL) { WaitForSingleObject(hDoneEvent,INFINITE); CloseHandle(hDoneEvent); } } /************************* Msg Shutdown *******************************/ // ppBlob might get reallocated, must have been allocated using mir_alloc() static TCHAR* GetMessageText(BYTE **ppBlob,DWORD *pcbBlob) { (*ppBlob)[*pcbBlob]=0; DWORD cb = lstrlenA((char*)*ppBlob); /* use Unicode data if present */ if (*pcbBlob>(cb+3)) { (*ppBlob)[*pcbBlob-1]=0; return (WCHAR*)&(*ppBlob)[cb]; } /* no Unicode data present, convert from ANSI */ int len = MultiByteToWideChar(CP_ACP,0,(char*)*ppBlob,-1,NULL,0); if (!len) return NULL; BYTE *buf=(BYTE*)mir_realloc(*ppBlob,(*pcbBlob)+(len*sizeof(WCHAR))); if (buf == NULL) return NULL; *pcbBlob += len*sizeof(WCHAR); *ppBlob = buf; buf = &(*ppBlob)[cb]; MultiByteToWideChar(CP_ACP,0,(char*)*ppBlob,-1,(WCHAR*)buf,len); ((WCHAR*)buf)[len-1] = 0; return (WCHAR*)buf; } static int MsgEventAdded(WPARAM wParam,LPARAM lParam) { HANDLE hDbEvent = (HANDLE)lParam; if (currentWatcherType & SDWTF_MESSAGE) { DBEVENTINFO dbe = { sizeof(dbe) }; dbe.cbBlob = db_event_getBlobSize(hDbEvent); dbe.pBlob = (BYTE*)mir_alloc(dbe.cbBlob+2); /* ensure term zero */ if (dbe.pBlob == NULL) return 0; if (!db_event_get(hDbEvent, &dbe)) if (dbe.eventType == EVENTTYPE_MESSAGE && !(dbe.flags & DBEF_SENT)) { DBVARIANT dbv; if (!db_get_ts(NULL,"AutoShutdown","Message",&dbv)) { TrimString(dbv.ptszVal); TCHAR *pszMsg = GetMessageText(&dbe.pBlob,&dbe.cbBlob); if (pszMsg != NULL && _tcsstr(pszMsg,dbv.ptszVal) != NULL) ShutdownAndStopWatcher(); /* msg with specified text recvd */ mir_free(dbv.ptszVal); /* does NULL check */ } } mir_free(dbe.pBlob); } return 0; } /************************* Transfer Shutdown **************************/ static HANDLE *transfers; static int nTransfersCount; static int ProtoAck(WPARAM 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 != NULL) { transfers = buf; transfers[nTransfersCount] = ack->hProcess; ++nTransfersCount; } break; } case ACKRESULT_SUCCESS: case ACKRESULT_FAILED: case ACKRESULT_DENIED: for(int i=0;ihProcess) { /* remove from list */ if (i<(nTransfersCount-1)) MoveMemory(&transfers[i],&transfers[i+1],(nTransfersCount-i-1)*sizeof(HANDLE)); --nTransfersCount; HANDLE *buf = (HANDLE*)mir_realloc(transfers,nTransfersCount*sizeof(HANDLE)); if (buf != NULL) transfers=buf; else if (!nTransfersCount) transfers=NULL; /* 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=db_get_b(NULL,"AutoShutdown","SmartOfflineCheck",SETTING_SMARTOFFLINECHECK_DEFAULT); for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { char *pszProto = GetContactProto(hContact); if (pszProto != NULL && CallProtoService(pszProto,PS_GETSTATUS,0,0) != ID_STATUS_OFFLINE) { if (db_get_b(hContact,pszProto,"ChatRoom",0)) continue; if (db_get_w(hContact,pszProto,"Status",0) != ID_STATUS_OFFLINE) { if (fSmartCheck) { if (db_get_b(hContact,"CList","Hidden",0)) continue; if (db_get_b(hContact,"CList","NotOnList",0)) continue; } fAllOffline=FALSE; break; } } } return fAllOffline; } static int StatusSettingChanged(WPARAM wParam,LPARAM lParam) { if (currentWatcherType&SDWTF_STATUS) { DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam; if ((HANDLE)wParam != NULL && dbcws->value.wVal==ID_STATUS_OFFLINE && !lstrcmpA(dbcws->szSetting,"Status")) { char *pszProto = GetContactProto(wParam); if (pszProto != NULL && !lstrcmpA(dbcws->szModule,pszProto)) if (CheckAllContactsOffline()) ShutdownAndStopWatcher(); } } return 0; } /************************* Cpu Shutdown *******************************/ static DWORD idCpuUsageThread; static BOOL CALLBACK CpuUsageWatcherProc(BYTE nCpuUsage,LPARAM lParam) { static BYTE nTimesBelow=0; /* only one watcher thread */ /* terminated? */ if (idCpuUsageThread != GetCurrentThreadId()) { nTimesBelow=0; return FALSE; /* stop poll thread */ } /* ignore random peaks */ if (nCpuUsage<(BYTE)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 = GetContactProto(wParam); if ((BOOL)lParam && pszProto != NULL && CallProtoService(pszProto,PS_GETSTATUS,0,0)==THUNDER) if (db_get_b(NULL,"AutoShutdown","WeatherShutdown",SETTING_WEATHERSHUTDOWN_DEFAULT)) ServiceShutdown(SDSDT_SHUTDOWN,TRUE); return 0; } /************************* Services ***********************************/ INT_PTR ServiceStartWatcher(WPARAM wParam,LPARAM lParam) { /* passing watcherType as lParam is only to be used internally, undocumented */ if (lParam==0) lParam=(LPARAM)db_get_w(NULL,"AutoShutdown","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==(WORD)lParam) return 3; if (currentWatcherType != 0) { /* Time Shutdown */ CloseCountdownFrame(); /* fails if not opened */ /* Cpu Shutdown */ idCpuUsageThread=0; } SetShutdownMenuItem(true); SetShutdownToolbarButton(true); currentWatcherType=(WORD)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(NULL,"AutoShutdown","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 (db_get_b(NULL,"AutoShutdown","RememberOnRestart",0)==SDROR_RUNNING) { db_set_b(NULL,"AutoShutdown","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=NULL; nTransfersCount=0; hHookProtoAck=HookEvent(ME_PROTO_ACK,ProtoAck); /* Weather Shutdown */ hHookWeatherUpdated=NULL; /* Services */ hEventWatcherChanged=CreateHookableEvent(ME_AUTOSHUTDOWN_WATCHERCHANGED); hServiceStartWatcher = CreateServiceFunction(MS_AUTOSHUTDOWN_STARTWATCHER, ServiceStartWatcher); hServiceStopWatcher = CreateServiceFunction(MS_AUTOSHUTDOWN_STOPWATCHER, ServiceStopWatcher); hServiceIsEnabled = CreateServiceFunction(MS_AUTOSHUTDOWN_ISWATCHERENABLED, ServiceIsWatcherEnabled); } void UninitWatcher(void) { /* remember watcher if running */ if (!ServiceStopWatcher(0,0)) if (db_get_b(NULL,"AutoShutdown","RememberOnRestart",SETTING_REMEMBERONRESTART_DEFAULT)) db_set_b(NULL,"AutoShutdown","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 */ DestroyServiceFunction(hServiceStartWatcher); DestroyServiceFunction(hServiceStopWatcher); DestroyServiceFunction(hServiceIsEnabled); DestroyHookableEvent(hEventWatcherChanged); /* Misc */ UnhookEvent(hHookModulesLoaded); }