summaryrefslogtreecommitdiff
path: root/plugins/AutoShutdown/watcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AutoShutdown/watcher.c')
-rw-r--r--plugins/AutoShutdown/watcher.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/plugins/AutoShutdown/watcher.c b/plugins/AutoShutdown/watcher.c
new file mode 100644
index 0000000000..75a2881e98
--- /dev/null
+++ b/plugins/AutoShutdown/watcher.c
@@ -0,0 +1,408 @@
+/*
+
+'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;
+/* Overheat Shutdown */
+static HANDLE hHookHddOverheat;
+/* Services */
+static HANDLE hServiceStartWatcher,hServiceStopWatcher,hServiceIsEnabled;
+static HANDLE hEventWatcherChanged;
+/* Misc */
+static HANDLE hHookModulesLoaded;
+
+/************************* Shared *************************************/
+
+static WORD currentWatcherType;
+
+static void __stdcall MainThreadMapping(HANDLE *phDoneEvent)
+{
+ 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;
+ hDoneEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
+ if(CallFunctionAsync(MainThreadMapping,&hDoneEvent))
+ if(hDoneEvent!=NULL) WaitForSingleObject(hDoneEvent,INFINITE);
+ if(hDoneEvent!=NULL) CloseHandle(hDoneEvent);
+}
+
+/************************* Msg Shutdown *******************************/
+
+// ppBlob might get reallocated, must have been allocated using mir_alloc()
+static TCHAR* GetMessageText(BYTE **ppBlob,DWORD *pcbBlob)
+{
+#if defined(_UNICODE)
+ DWORD cb;
+ (*ppBlob)[*pcbBlob]=0;
+ 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;
+ BYTE *buf;
+ len=MultiByteToWideChar(CP_ACP,0,(char*)*ppBlob,-1,NULL,0);
+ if(!len) return NULL;
+ 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;
+ }
+#else
+ (*ppBlob)[*pcbBlob]=0;
+ return (char*)*ppBlob;
+#endif
+}
+
+static int MsgEventAdded(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ if(currentWatcherType&SDWTF_MESSAGE) {
+ DBEVENTINFO dbe;
+ dbe.cbSize=sizeof(dbe);
+ dbe.cbBlob=(DWORD)CallService(MS_DB_EVENT_GETBLOBSIZE,(WPARAM)lParam,0);
+ dbe.pBlob=(BYTE*)mir_alloc(dbe.cbBlob+2); /* ensure term zero */
+ if(dbe.pBlob==NULL) return 0;
+ if(!CallService(MS_DB_EVENT_GET,(WPARAM)lParam,(LPARAM)&dbe))
+ if(dbe.eventType==EVENTTYPE_MESSAGE && !(dbe.flags&DBEF_SENT)) {
+ DBVARIANT dbv;
+ TCHAR *pszMsg;
+ if(!DBGetContactSettingTString(NULL,"AutoShutdown","Message",&dbv)) {
+ TrimString(dbv.ptszVal);
+ 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;
+ UNREFERENCED_PARAMETER(wParam);
+ if(ack->type==ACKTYPE_FILE)
+ switch(ack->result) {
+ case ACKRESULT_DATA:
+ { int i;
+ for(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:
+ { int i;
+ for(i=0;i<nTransfersCount;++i)
+ if(transfers[i]==ack->hProcess) {
+ /* remove from list */
+ HANDLE *buf;
+ if(i<(nTransfersCount-1))
+ MoveMemory(&transfers[i],&transfers[i+1],(nTransfersCount-i-1)*sizeof(HANDLE));
+ --nTransfersCount;
+ 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 wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ if(currentWatcherType&SDWTF_IDLE && lParam&IDF_ISIDLE)
+ ShutdownAndStopWatcher();
+ return 0;
+}
+
+/************************* Status Shutdown ****************************/
+
+static BOOL CheckAllContactsOffline(void)
+{
+ BOOL fSmartCheck,fAllOffline=TRUE; /* tentatively */
+ HANDLE hContact;
+ char *pszProto;
+ fSmartCheck=DBGetContactSettingByte(NULL,"AutoShutdown","SmartOfflineCheck",SETTING_SMARTOFFLINECHECK_DEFAULT);
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while(hContact!=NULL) {
+ pszProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if(pszProto!=NULL && CallProtoService(pszProto,PS_GETSTATUS,0,0)!=ID_STATUS_OFFLINE)
+ if(DBGetContactSettingByte(hContact,pszProto,"ChatRoom",0)) continue;
+ if(DBGetContactSettingWord(hContact,pszProto,"Status",0)!=ID_STATUS_OFFLINE) {
+ if(fSmartCheck) {
+ if(DBGetContactSettingByte(hContact,"CList","Hidden",0)) continue;
+ if(DBGetContactSettingByte(hContact,"CList","NotOnList",0)) continue;
+ }
+ fAllOffline=FALSE;
+ break;
+ }
+ hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
+ }
+ 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=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ 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=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0);
+ if((BOOL)lParam && pszProto!=NULL && CallProtoService(pszProto,PS_GETSTATUS,0,0)==THUNDER)
+ if(DBGetContactSettingByte(NULL,"AutoShutdown","WeatherShutdown",SETTING_WEATHERSHUTDOWN_DEFAULT))
+ ServiceShutdown(SDSDT_SHUTDOWN,TRUE);
+ return 0;
+}
+
+/************************* Overheat Shutdown **************************/
+
+static int HddOverheat(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ if(DBGetContactSettingByte(NULL,"AutoShutdown","HddOverheatShutdown",SETTING_HDDOVERHEATSHUTDOWN_DEFAULT))
+ ServiceShutdown(SDSDT_SHUTDOWN,TRUE);
+ return 0;
+}
+
+/************************* Services ***********************************/
+
+int ServiceStartWatcher(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+
+ /* passing watcherType as lParam is only to be used internally, undocumented */
+ if(lParam==0) lParam=(LPARAM)DBGetContactSettingWord(NULL,"AutoShutdown","WatcherFlags",0);
+
+ if(!(lParam&SDWTF_MASK)) return 1; /* invalid flags or empty? */
+ if(lParam&SDWTF_SPECIFICTIME && !(lParam&SDWTF_ST_MASK)) return 2; /* no specific time choice? */
+ 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 ServiceStopWatcher(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(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 ServiceIsWatcherEnabled(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ return currentWatcherType!=0;
+}
+
+/************************* Misc ***********************************/
+
+static int WatcherModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ /* Weather Shutdown */
+ if(ServiceExists(MS_WEATHER_UPDATE))
+ hHookWeatherUpdated=HookEvent(ME_WEATHER_UPDATED,WeatherUpdated);
+ /* Overheat Shutdown */
+ if(ServiceExists(MS_SYSINFO_HDDTEMP))
+ hHookHddOverheat=HookEvent(ME_SYSINFO_HDDOVERHEAT,HddOverheat);
+
+ /* restore watcher if it was running on last exit */
+ if(DBGetContactSettingByte(NULL,"AutoShutdown","RememberOnRestart",0)==SDROR_RUNNING) {
+ DBWriteContactSettingByte(NULL,"AutoShutdown","RememberOnRestart",1);
+ ServiceStartWatcher(0,0); /* after modules loaded */
+ }
+ return 0;
+}
+
+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;
+ /* Overheat Shutdown */
+ hHookHddOverheat=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);
+ /* Misc */
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,WatcherModulesLoaded);
+}
+
+void UninitWatcher(void)
+{
+ /* remember watcher if running */
+ if(!ServiceStopWatcher(0,0))
+ if(DBGetContactSettingByte(NULL,"AutoShutdown","RememberOnRestart",SETTING_REMEMBERONRESTART_DEFAULT))
+ DBWriteContactSettingByte(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 */
+ /* Overheat Shutdown */
+ UnhookEvent(hHookHddOverheat); /* does NULL check */
+ /* Services */
+ DestroyServiceFunction(hServiceStartWatcher);
+ DestroyServiceFunction(hServiceStopWatcher);
+ DestroyServiceFunction(hServiceIsEnabled);
+ DestroyHookableEvent(hEventWatcherChanged);
+ /* Misc */
+ UnhookEvent(hHookModulesLoaded);
+}