// ---------------------------------------------------------------------------80 // ICQ plugin for Miranda Instant Messenger // ________________________________________ // // Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede // Copyright © 2001-2002 Jon Keating, Richard Hughes // Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater // Copyright © 2004-2010 Joe Kucera // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // ----------------------------------------------------------------------------- // DESCRIPTION: // // Background thread for automatic update of user details // // ----------------------------------------------------------------------------- #include "icqoscar.h" // Retrieve users' info void CIcqProto::icq_InitInfoUpdate(void) { // Create wait objects hInfoQueueEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (hInfoQueueEvent) { // Init mutexes infoUpdateMutex = new icq_critical_section(); // Init list for (int i = 0; iEnter(); // Check if in list for (i = 0; (iLeave(); return TRUE; } return FALSE; } void CIcqProto::icq_DequeueUser(DWORD dwUin) { if (nInfoUserCount > 0) { int nChecked = 0; // Check if in list infoUpdateMutex->Enter(); for (int i = 0; (i < LISTSIZE && nChecked < nInfoUserCount); i++) { if (m_infoUpdateList[i].dwUin) { nChecked++; // Remove from list if (m_infoUpdateList[i].dwUin == dwUin) { #ifdef _DEBUG debugLogA("Dequeued user %u", m_infoUpdateList[i].dwUin); #endif m_infoUpdateList[i].dwUin = 0; m_infoUpdateList[i].hContact = NULL; m_infoUpdateList[i].queued = 0; nInfoUserCount--; break; } } } infoUpdateMutex->Leave(); } } void CIcqProto::icq_RescanInfoUpdate() { bInfoPendingUsers = 0; /* This is here, cause we do not want to emit large number of reuqest at once, fill queue, and let thread deal with it */ bInfoUpdateEnabled = 0; // freeze thread // Queue all outdated users HANDLE hContact = FindFirstContact(); while (hContact != NULL) { if (IsMetaInfoChanged(hContact)) { // Queue user if (!icq_QueueUser(hContact)) { // The queue is full, pause queuing contacts bInfoPendingUsers = 1; break; } } hContact = FindNextContact(hContact); } bInfoUpdateEnabled = TRUE; } void CIcqProto::icq_EnableUserLookup(BOOL bEnable) { bInfoUpdateEnabled = bEnable; // Notify worker thread if (bInfoUpdateEnabled && hInfoQueueEvent) SetEvent(hInfoQueueEvent); } void __cdecl CIcqProto::InfoUpdateThread( void* ) { int i; DWORD dwWait = WAIT_OBJECT_0; debugLogA("%s thread starting.", "Info-Update"); bInfoUpdateRunning = TRUE; while (bInfoUpdateRunning) { if (!nInfoUserCount && bInfoPendingUsers) // whole queue processed, check if more users needs updating icq_RescanInfoUpdate(); if (!nInfoUserCount || !bInfoUpdateEnabled || !icqOnline()) { dwWait = WAIT_TIMEOUT; while (bInfoUpdateRunning && dwWait == WAIT_TIMEOUT) { // wait for new work or until we should end dwWait = ICQWaitForSingleObject(hInfoQueueEvent, 10000); } } if (!bInfoUpdateRunning) break; switch (dwWait) { case WAIT_IO_COMPLETION: // Possible shutdown in progress break; case WAIT_OBJECT_0: case WAIT_TIMEOUT: // Time to check for new users if (!bInfoUpdateEnabled) continue; // we can't send requests now if (nInfoUserCount && icqOnline()) { time_t now = time(NULL); BOOL bNotReady = FALSE, bTimeOuted = FALSE; // Check the list, take only users that were there for at least 5sec // wait if any user is there shorter than 5sec and not a single user is there longer than 20sec infoUpdateMutex->Enter(); for (i = 0; i= now) bNotReady = TRUE; } } infoUpdateMutex->Leave(); if (!bTimeOuted && bNotReady) { SleepEx(1000, TRUE); if (!bInfoUpdateRunning) { // need to end as fast as possible debugLogA("%s thread ended.", "Info-Update"); goto LBL_Exit; } continue; } if (FindCookie(dwInfoActiveRequest, NULL, NULL)) { // only send another request, when the previous is completed #ifdef _DEBUG debugLogA("Info-Update: Request 0x%x still in progress.", dwInfoActiveRequest); #endif SleepEx(1000, TRUE); if (!bInfoUpdateRunning) { // need to end as fast as possible debugLogA("%s thread ended.", "Info-Update"); goto LBL_Exit; } continue; } #ifdef _DEBUG debugLogA("Info-Update: Users %u in queue.", nInfoUserCount); #endif // Either some user is waiting long enough, or all users are ready (waited at least the minimum time) m_ratesMutex->Enter(); if (!m_rates) { // we cannot send info request - icq is offline m_ratesMutex->Leave(); break; } WORD wGroup = m_rates->getGroupFromSNAC(ICQ_EXTENSIONS_FAMILY, ICQ_META_CLI_REQUEST); while (m_rates->getNextRateLevel(wGroup) < m_rates->getLimitLevel(wGroup, RML_IDLE_30)) { // we are over rate, need to wait before sending int nDelay = m_rates->getDelayToLimitLevel(wGroup, RML_IDLE_50); m_ratesMutex->Leave(); #ifdef _DEBUG debugLogA("Rates: InfoUpdate delayed %dms", nDelay); #endif SleepEx(nDelay, TRUE); // do not keep things locked during sleep if (!bInfoUpdateRunning) { // need to end as fast as possible debugLogA("%s thread ended.", "Info-Update"); goto LBL_Exit; } m_ratesMutex->Enter(); if (!m_rates) // we lost connection when we slept, go away break; } if (!m_rates) { // we cannot send info request - icq is offline m_ratesMutex->Leave(); break; } m_ratesMutex->Leave(); userinfo *hContactList[LISTSIZE]; int nListIndex = 0; BYTE *pRequestData = NULL; int nRequestSize = 0; infoUpdateMutex->Enter(); for (i = 0; iLeave(); break; } if (!(dwInfoActiveRequest = sendUserInfoMultiRequest(pRequestData, nRequestSize, nListIndex))) { // sending data packet failed SAFE_FREE((void**)&pRequestData); infoUpdateMutex->Leave(); break; } SAFE_FREE((void**)&pRequestData); for (i = 0; idwUin = 0; hContactList[i]->hContact = NULL; hContactList[i]->queued = 0; nInfoUserCount--; } infoUpdateMutex->Leave(); } break; default: // Something strange happened. Exit bInfoUpdateRunning = FALSE; break; } } debugLogA("%s thread ended.", "Info-Update"); LBL_Exit: SAFE_DELETE(&infoUpdateMutex); CloseHandle(hInfoQueueEvent); } // Clean up before exit void CIcqProto::icq_InfoUpdateCleanup(void) { debugLogA("%s must die.", "Info-Update"); bInfoUpdateRunning = FALSE; if (hInfoQueueEvent) SetEvent(hInfoQueueEvent); // break queue loop }