/* * This code implements miscellaneous usefull functions * * (c) majvan 2002-2004 */ #include "m_yamn.h" #include "m_protoplugin.h" #include "m_messages.h" #include "m_synchro.h" #include "main.h" #include "yamn.h" #ifdef DEBUG_SYNCHRO #include <stdio.h> #endif //- imported --------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- extern PYAMN_PROTOPLUGINQUEUE FirstProtoPlugin; extern YAMN_VARIABLES YAMNVar; extern char *ProtoName; extern HANDLE hTTButton; //TopToolBar button extern DWORD WriteAccountsToFile(); extern DWORD WINAPI SWMRGWaitToRead(PSWMRG,DWORD); extern void WINAPI SWMRGDoneReading(PSWMRG); extern DWORD WINAPI WaitToReadFcn(PSWMRG); extern void WINAPI ReadDoneFcn(PSWMRG); //From protoplugin.cpp extern struct CExportedFunctions ProtoPluginExportedFcn[1]; extern struct CExportedServices ProtoPluginExportedSvc[5]; //From filterplugin.cpp extern struct CExportedFunctions FilterPluginExportedFcn[1]; extern struct CExportedServices FilterPluginExportedSvc[2]; //From synchro.cpp extern struct CExportedFunctions SynchroExportedFcn[7]; //From account.cpp extern struct CExportedFunctions AccountExportedFcn[2]; extern struct CExportedServices AccountExportedSvc[9]; //From mails.cpp (MIME) extern struct CExportedFunctions MailExportedFcn[8]; extern struct CExportedServices MailExportedSvc[5]; //-------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- //MessageWndCS //We want to send messages to all windows in the queue //When we send messages, no other window can register itself to the queue for receiving messages LPCRITICAL_SECTION MessageWndCS; //Plugin registration CS //Used if we add (register) plugin to YAMN plugins and when we browse through registered plugins LPCRITICAL_SECTION PluginRegCS; //AccountWriterCS //We want to store number of writers of Accounts (number of Accounts used for writing) //If we want to read all accounts (for saving to file) immidiatelly, we have to wait until no account is changing (no thread writing to account) SCOUNTER *AccountWriterSO; //NoExitEV //Event that is signaled when there's a request to exit, so no new pop3 check should be performed HANDLE ExitEV; //WriteToFileEV //If this is signaled, write accounts to file is performed. Set this event if you want to actualize your accounts and messages HANDLE WriteToFileEV; //Returns pointer to YAMN exported function INT_PTR GetFcnPtrSvc(WPARAM wParam,LPARAM lParam); //Returns pointer to YAMN variables INT_PTR GetVariablesSvc(WPARAM wParam,LPARAM); // Thread running only to catch hotkeys DWORD WINAPI YAMNHotKeyThread(LPVOID Param); // Function every seconds decrements account counter of seconds and checks if they are 0 // If yes, creates a POP3 thread to check account void CALLBACK TimerProc(HWND,UINT,UINT,DWORD); // Function called to check all accounts immidialtelly // no params INT_PTR ForceCheckSvc(WPARAM,LPARAM); //thread is running all the time //waits for WriteToFileEV and then writes all accounts to file //DWORD WINAPI FileWritingThread(PVOID); // Function is called when Miranda notifies plugin that it is about to exit // Ensures succesfull end of POP3 checking, sets event that no next checking should be performed // If there's no writer to account (POP3 thread), saves the results to the file //not used now, perhaps in the future //int ExitProc(WPARAM wParam,LPARAM lParam); //-------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- INT_PTR GetFcnPtrSvc(WPARAM wParam,LPARAM lParam) { register int i; for(i=0;i<sizeof(ProtoPluginExportedFcn)/sizeof(ProtoPluginExportedFcn[0]);i++) if(0==lstrcmp((char *)wParam,ProtoPluginExportedFcn[i].ID)) return (INT_PTR)ProtoPluginExportedFcn[i].Ptr; for(i=0;i<sizeof(ProtoPluginExportedSvc)/sizeof(ProtoPluginExportedSvc[0]);i++) if(0==lstrcmp((char *)wParam,ProtoPluginExportedSvc[i].ID)) return (INT_PTR)ProtoPluginExportedSvc[i].Ptr; for(i=0;i<sizeof(SynchroExportedFcn)/sizeof(SynchroExportedFcn[0]);i++) if(0==lstrcmp((char *)wParam,SynchroExportedFcn[i].ID)) return (INT_PTR)SynchroExportedFcn[i].Ptr; for(i=0;i<sizeof(AccountExportedFcn)/sizeof(AccountExportedFcn[0]);i++) if(0==lstrcmp((char *)wParam,AccountExportedFcn[i].ID)) return (INT_PTR)AccountExportedFcn[i].Ptr; for(i=0;i<sizeof(AccountExportedSvc)/sizeof(AccountExportedSvc[0]);i++) if(0==lstrcmp((char *)wParam,AccountExportedSvc[i].ID)) return (INT_PTR)AccountExportedSvc[i].Ptr; for(i=0;i<sizeof(MailExportedFcn)/sizeof(MailExportedFcn[0]);i++) if(0==lstrcmp((char *)wParam,MailExportedFcn[i].ID)) return (INT_PTR)MailExportedFcn[i].Ptr; for(i=0;i<sizeof(MailExportedSvc)/sizeof(MailExportedSvc[0]);i++) if(0==lstrcmp((char *)wParam,MailExportedSvc[i].ID)) return (INT_PTR)MailExportedSvc[i].Ptr; for(i=0;i<sizeof(FilterPluginExportedFcn)/sizeof(FilterPluginExportedFcn[0]);i++) if(0==lstrcmp((char *)wParam,FilterPluginExportedFcn[i].ID)) return (INT_PTR)FilterPluginExportedFcn[i].Ptr; for(i=0;i<sizeof(FilterPluginExportedSvc)/sizeof(FilterPluginExportedSvc[0]);i++) if(0==lstrcmp((char *)wParam,FilterPluginExportedSvc[i].ID)) return (INT_PTR)FilterPluginExportedSvc[i].Ptr; return (INT_PTR)NULL; } INT_PTR GetVariablesSvc(WPARAM wParam,LPARAM) { return wParam==YAMN_VARIABLESVERSION ? (INT_PTR)&YAMNVar : (INT_PTR)NULL; } DWORD WINAPI YAMNHotKeyThread(LPVOID Param) { MSG WinMessage; WORD HotKey = LOWORD(Param); int HotKeyID; // register hotkey for main YAMN thread first if(!(HotKeyID=RegisterHotKey(NULL,(int)GlobalAddAtom(YAMN_HKCHECKMAIL),HIBYTE(HotKey),LOBYTE(HotKey)))) return 0; while(1) { GetMessage(&WinMessage,NULL,WM_HOTKEY,WM_YAMN_CHANGEHOTKEY); // if we want to close miranda, we get event and do not run pop3 checking anymore if(WAIT_OBJECT_0==WaitForSingleObject(ExitEV,0)) break; switch(WinMessage.message) { // user pressed hotkey case WM_HOTKEY: ForceCheckSvc((WPARAM)0,(LPARAM)0); break; // hotkey changed case WM_YAMN_CHANGEHOTKEY: UnregisterHotKey(NULL,HotKeyID); HotKeyID=RegisterHotKey(NULL,(int)GlobalAddAtom(YAMN_HKCHECKMAIL),WinMessage.wParam,WinMessage.lParam); break; } } return 1; } void CALLBACK TimerProc(HWND,UINT,UINT,DWORD) { PYAMN_PROTOPLUGINQUEUE ActualPlugin; HACCOUNT ActualAccount; HANDLE ThreadRunningEV; DWORD Status,tid; // we use event to signal, that running thread has all needed stack parameters copied if(NULL==(ThreadRunningEV=CreateEvent(NULL,FALSE,FALSE,NULL))) return; // if we want to close miranda, we get event and do not run checking anymore if(WAIT_OBJECT_0==WaitForSingleObject(ExitEV,0)) return; // Get actual status of current user in Miranda Status=CallService(MS_CLIST_GETSTATUSMODE,0,0); EnterCriticalSection(PluginRegCS); for(ActualPlugin=FirstProtoPlugin;ActualPlugin!=NULL;ActualPlugin=ActualPlugin->Next) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:AccountBrowserSO-read wait\n"); #endif if(WAIT_OBJECT_0!=SWMRGWaitToRead(ActualPlugin->Plugin->AccountBrowserSO,0)) //we want to access accounts immiadtelly { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:AccountBrowserSO-read enter failed\n"); #endif LeaveCriticalSection(PluginRegCS); return; } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:AccountBrowserSO-read enter\n"); #endif for(ActualAccount=ActualPlugin->Plugin->FirstAccount;ActualAccount!=NULL;ActualAccount=ActualAccount->Next) { if(ActualAccount->Plugin==NULL || ActualAccount->Plugin->Fcn==NULL) //account not inited continue; #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read wait\n"); #endif if(WAIT_OBJECT_0!=SWMRGWaitToRead(ActualAccount->AccountAccessSO,0)) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read wait failed\n"); #endif continue; } #ifdef DEBUG_SYNCHRO switch(Status) { case ID_STATUS_OFFLINE: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status offline\n"); break; case ID_STATUS_ONLINE: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status online\n"); break; case ID_STATUS_AWAY: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status away\n"); break; case ID_STATUS_DND: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status dnd\n"); break; case ID_STATUS_NA: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status na\n"); break; case ID_STATUS_OCCUPIED: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status occupied\n"); break; case ID_STATUS_FREECHAT: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status freechat\n"); break; case ID_STATUS_INVISIBLE: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status invisible\n"); break; case ID_STATUS_ONTHEPHONE: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status onthephone\n"); break; case ID_STATUS_OUTTOLUNCH: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status outtolunch\n"); break; default: DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read enter status unknown\n"); break; } #endif BOOL isAccountCounting = 0; if( (ActualAccount->Flags & YAMN_ACC_ENA) && (((ActualAccount->StatusFlags & YAMN_ACC_ST0) && (Status<=ID_STATUS_OFFLINE)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST1) && (Status==ID_STATUS_ONLINE)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST2) && (Status==ID_STATUS_AWAY)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST3) && (Status==ID_STATUS_DND)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST4) && (Status==ID_STATUS_NA)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST5) && (Status==ID_STATUS_OCCUPIED)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST6) && (Status==ID_STATUS_FREECHAT)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST7) && (Status==ID_STATUS_INVISIBLE)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST8) && (Status==ID_STATUS_ONTHEPHONE)) || ((ActualAccount->StatusFlags & YAMN_ACC_ST9) && (Status==ID_STATUS_OUTTOLUNCH)))) { if((!ActualAccount->Interval && !ActualAccount->TimeLeft) || ActualAccount->Plugin->Fcn->TimeoutFcnPtr==NULL) { goto ChangeIsCountingStatusLabel; } if(ActualAccount->TimeLeft){ ActualAccount->TimeLeft--; isAccountCounting = TRUE; } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:time left : %i\n",ActualAccount->TimeLeft); #endif WindowList_BroadcastAsync(YAMNVar.MessageWnds,WM_YAMN_CHANGETIME,(WPARAM)ActualAccount,(LPARAM)ActualAccount->TimeLeft); if(!ActualAccount->TimeLeft) { struct CheckParam ParamToPlugin={YAMN_CHECKVERSION,ThreadRunningEV,ActualAccount,YAMN_NORMALCHECK,(void *)0,NULL}; HANDLE NewThread; ActualAccount->TimeLeft=ActualAccount->Interval; if(NULL==(NewThread=CreateThread(NULL,0,(YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->TimeoutFcnPtr,&ParamToPlugin,0,&tid))) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read done\n"); #endif ReadDoneFcn(ActualAccount->AccountAccessSO); continue; } else { WaitForSingleObject(ThreadRunningEV,INFINITE); CloseHandle(NewThread); } } } ChangeIsCountingStatusLabel: #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:ActualAccountSO-read done\n"); #endif if (((ActualAccount->isCounting)!=0)!=isAccountCounting){ ActualAccount->isCounting=isAccountCounting; WORD cStatus = DBGetContactSettingWord(ActualAccount->hContact,ProtoName,"Status",0); switch (cStatus){ case ID_STATUS_ONLINE: case ID_STATUS_OFFLINE: DBWriteContactSettingWord(ActualAccount->hContact, ProtoName, "Status", isAccountCounting?ID_STATUS_ONLINE:ID_STATUS_OFFLINE); default: break; } } ReadDoneFcn(ActualAccount->AccountAccessSO); } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"TimerProc:AccountBrowserSO-read done\n"); #endif SWMRGDoneReading(ActualPlugin->Plugin->AccountBrowserSO); } LeaveCriticalSection(PluginRegCS); CloseHandle(ThreadRunningEV); return; } INT_PTR ForceCheckSvc(WPARAM,LPARAM) { PYAMN_PROTOPLUGINQUEUE ActualPlugin; HACCOUNT ActualAccount; HANDLE ThreadRunningEV; DWORD tid; //we use event to signal, that running thread has all needed stack parameters copied if(NULL==(ThreadRunningEV=CreateEvent(NULL,FALSE,FALSE,NULL))) return 0; //if we want to close miranda, we get event and do not run pop3 checking anymore if(WAIT_OBJECT_0==WaitForSingleObject(ExitEV,0)) return 0; EnterCriticalSection(PluginRegCS); for(ActualPlugin=FirstProtoPlugin;ActualPlugin!=NULL;ActualPlugin=ActualPlugin->Next) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:AccountBrowserSO-read wait\n"); #endif SWMRGWaitToRead(ActualPlugin->Plugin->AccountBrowserSO,INFINITE); #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:AccountBrowserSO-read enter\n"); #endif for(ActualAccount=ActualPlugin->Plugin->FirstAccount;ActualAccount!=NULL;ActualAccount=ActualAccount->Next) { if(ActualAccount->Plugin->Fcn==NULL) //account not inited continue; #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:ActualAccountSO-read wait\n"); #endif if(WAIT_OBJECT_0!=WaitToReadFcn(ActualAccount->AccountAccessSO)) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:ActualAccountSO-read wait failed\n"); #endif continue; } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:ActualAccountSO-read enter\n"); #endif if((ActualAccount->Flags & YAMN_ACC_ENA) && (ActualAccount->StatusFlags & YAMN_ACC_FORCE)) //account cannot be forced to check { if(ActualAccount->Plugin->Fcn->ForceCheckFcnPtr==NULL) { ReadDoneFcn(ActualAccount->AccountAccessSO); continue; } struct CheckParam ParamToPlugin={YAMN_CHECKVERSION,ThreadRunningEV,ActualAccount,YAMN_FORCECHECK,(void *)0,NULL}; if(NULL==CreateThread(NULL,0,(YAMN_STANDARDFCN)ActualAccount->Plugin->Fcn->ForceCheckFcnPtr,&ParamToPlugin,0,&tid)) { ReadDoneFcn(ActualAccount->AccountAccessSO); continue; } else WaitForSingleObject(ThreadRunningEV,INFINITE); } ReadDoneFcn(ActualAccount->AccountAccessSO); } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ForceCheck:AccountBrowserSO-read done\n"); #endif SWMRGDoneReading(ActualPlugin->Plugin->AccountBrowserSO); } LeaveCriticalSection(PluginRegCS); CloseHandle(ThreadRunningEV); CallService(MS_TTB_SETBUTTONSTATE,(WPARAM)hTTButton,(LPARAM)TTBST_RELEASED); return 1; } /* int ExitProc(WPARAM wParam,LPARAM lParam) { THIS WILL BE IMPLEMENTED LATER // First, no thread must add or delete accounts. This is achieved by entering browsing through accounts // If any thread want to delete or add, it waits for write-access to browse accounts (so it waits infinite time) #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ExitProc:AccountBrowserSO-wait to obtain read access\n")); #endif if(WAIT_TIMEOUT==SWMRGWaitToRead(AccountBrowserSO,0)) { #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ExitProc:AccountBrowserSO-read access obtain failed, I'll try later\n")); #endif return 1; } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ExitProc:AccountBrowserSO-read access obtained\n")); #endif #ifdef DEBUG_SYNCHRO TCHAR Debug[100]; _stprintf(Debug,_T("ExitProc:Writers: %d\n"),AccountWriterSO->GetNumber()); DEBUG_SYNCHRO2F(Debug); DebugLog(SynchroFile,"ExitProc:NoWriterEV-test\n")); #endif // next, threads must not write to any account. This works like hFinishEV event in AccountAccessSO and MessagesAccessSO. // When hFinishEV is set, any beginning with reading and writing to account (messages) is failed. // This is similar, but the difference is, that we can finish the whole work (we can decide: if ExitEV is set, should we // end immidialtelly or should we continue (to end operation successfully)? // E.g. I decided that once we started checking account, we get all new mails and then we can end. // The second and more significant difference is, that ExitEV is signal to all accounts and messages, not only to one account. SetEvent(ExitEV); if(WAIT_TIMEOUT==WaitForSingleObject(AccountWriterSO->Event,0)) { // There exists a thread writing to account, so we ca try later to write accounts to file, if no thread is writting #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ExitProc:NoWriterEV-writer(s) exists, I'll try later\n")); #endif SWMRGDoneReading(AccountBrowserSO); return 1; } #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"ExitProc:NoWriterEV-no writer, going to save!\n")); #endif // Save to file WriteAccountsToFile(); SWMRGDoneReading(AccountBrowserSO); // Now, all is saved, we can safe exit from Miranda return 0; } */ /* DWORD WINAPI FileWritingThread(PVOID) { HACCOUNT ActualAccount=FirstAccount; while(1) { WaitForSingleObject(WriteToFileEV,INFINITE); #ifdef DEBUG_SYNCHRO DebugLog(SynchroFile,"FileWriting:WriteToFileEV-signaled\n")); #endif // now, write accounts and messages if it is possible. If it is not possible e.g. to read messages from one account, // function will wait until messages are not used and then writes messages WriteAccountsToFile(); } return 0; } */