summaryrefslogtreecommitdiff
path: root/Plugins/smr/poll.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/smr/poll.cpp')
-rw-r--r--Plugins/smr/poll.cpp868
1 files changed, 868 insertions, 0 deletions
diff --git a/Plugins/smr/poll.cpp b/Plugins/smr/poll.cpp
new file mode 100644
index 0000000..f2fee48
--- /dev/null
+++ b/Plugins/smr/poll.cpp
@@ -0,0 +1,868 @@
+/*
+Copyright (C) 2006 Ricardo Pescuma Domenecci
+
+This is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this file; see the file license.txt. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+#include "poll.h"
+
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+static CRITICAL_SECTION update_cs;
+static CRITICAL_SECTION pause_cs;
+
+typedef struct
+{
+ SortedList *queue;
+ HANDLE hThread;
+ DWORD dwThreadID;
+ BOOL bThreadRunning;
+
+} ThreadQueue;
+
+ThreadQueue statusQueue;
+
+
+struct QueueItem
+{
+ HANDLE hContact;
+ DWORD check_time;
+ int type;
+};
+
+// Types
+#define STATUS_CHANGE 1
+#define STATUS_CHANGE_TIMER 2
+#define PROTOCOL_ONLINE 4
+#define POOL 8
+#define XSTATUS_CHANGE 16
+
+
+UINT_PTR hTimer = 0;
+
+void QueueAdd(HANDLE hContact, DWORD check_time, int type);
+void QueueRemove(HANDLE hContact);
+int QueueSortItems(void *i1, void *i2);
+
+
+VOID CALLBACK PollTimerAddContacts(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
+void PollAddAllContacts(int timer, const char *protocol, int type);
+
+DWORD WINAPI UpdateThread(LPVOID vParam);
+
+
+DWORD next_request_at = 0;
+void SetNextRequestAt(DWORD t);
+DWORD GetNextRequestAt(void);
+
+QueueItem *requested_item;
+
+
+// Functions ////////////////////////////////////////////////////////////////////////////
+
+
+void InitPoll()
+{
+ int queuesize = CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+
+ // Init queue
+ ZeroMemory(&statusQueue, sizeof(statusQueue));
+ statusQueue.queue = List_Create(0, queuesize + 10);
+ statusQueue.queue->sortFunc = QueueSortItems;
+ statusQueue.bThreadRunning = TRUE;
+ statusQueue.hThread = CreateThread(NULL, 16000, UpdateThread, NULL, 0, &statusQueue.dwThreadID);
+
+ InitializeCriticalSection(&update_cs);
+ InitializeCriticalSection(&pause_cs);
+
+ requested_item = NULL;
+}
+
+
+void FreePoll()
+{
+ DWORD dwExitcode;
+ int steps;
+
+ // Stop queue
+ steps = 0;
+ statusQueue.bThreadRunning = FALSE;
+ ResumeThread(statusQueue.hThread);
+ do {
+ Sleep(100);
+ GetExitCodeThread(statusQueue.hThread, &dwExitcode);
+ steps++;
+ } while ( dwExitcode == STILL_ACTIVE && steps < 20 );
+ if (statusQueue.hThread)
+ CloseHandle(statusQueue.hThread);
+
+ // Delete cs
+ DeleteCriticalSection(&update_cs);
+ DeleteCriticalSection(&pause_cs);
+
+ // Free lists
+ List_DestroyFreeContents(statusQueue.queue);
+ mir_free(statusQueue.queue);
+}
+
+int UpdateDelay()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "UpdateDelay", UPDATE_DELAY));
+
+ return delay;
+}
+
+int ErrorDelay()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "ErrorDelay", ERROR_DELAY));
+
+ return delay;
+}
+
+int WaitTime()
+{
+ int delay = max(50, DBGetContactSettingWord(NULL, MODULE_NAME, "WaitTime", WAIT_TIME));
+
+ return delay;
+}
+
+// Return true if this protocol has to be checked
+BOOL PollCheckProtocol(const char *protocol)
+{
+ if (protocol == NULL)
+ return FALSE;
+
+ if (!ProtoServiceExists(protocol, PS_GETSTATUS))
+ return FALSE;
+
+ if (CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ return FALSE;
+
+ if ((CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGRECV) == 0)
+ return FALSE;
+
+ char setting[256];
+ mir_snprintf(setting, sizeof(setting), OPT_PROTOCOL_GETMSG, protocol);
+
+ return (BOOL) DBGetContactSettingByte(NULL, MODULE_NAME, setting, FALSE);
+}
+
+
+// Return true if this contact has to be checked
+BOOL PollCheckContact(HANDLE hContact)
+{
+ return !DBGetContactSettingByte(hContact,"CList","Hidden",0) &&
+ !DBGetContactSettingByte(hContact,"CList","NotOnList",0) &&
+ DBGetContactSettingByte(hContact,"CList","ApparentMode",0) != ID_STATUS_OFFLINE &&
+ DBGetContactSettingByte(hContact, MODULE_NAME, OPT_CONTACT_GETMSG, TRUE);
+}
+
+
+// Add a contact to the poll when the status of the contact has changed
+void PollStatusChangeAddContact(HANDLE hContact)
+{
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (proto != NULL && PollCheckProtocol(proto))
+ {
+ // Delete some messages now (don't add to list...)
+ Check what = ProtocolStatusCheckMsg(hContact, proto);
+
+ if (what == Retrieve)
+ {
+ if (opts.poll_clear_on_status_change)
+ ClearStatusMessage(hContact);
+
+ if (opts.poll_check_on_status_change)
+ QueueAdd(hContact, GetTickCount(), STATUS_CHANGE);
+
+ if (opts.poll_check_on_status_change_timer)
+ QueueAdd(hContact, GetTickCount() + opts.poll_timer_status * 1000, STATUS_CHANGE_TIMER);
+ }
+ else if (what == UseXStatus || what == ClearXStatus)
+ {
+ PollXStatusChangeAddContact(hContact);
+ }
+ else
+ {
+ ProcessCheckNotToServer(what, hContact, proto);
+ }
+ }
+}
+
+// Check after some time, to allow proto to set all data about XStatus
+void PollXStatusChangeAddContact(HANDLE hContact)
+{
+ char *proto = (char*) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (proto != NULL && PollCheckProtocol(proto) && opts.when_xstatus != Normal)
+ {
+ QueueAdd(hContact, GetTickCount() + 200, XSTATUS_CHANGE);
+ }
+}
+
+void PollAddAllContactsTimer(int timer)
+{
+ PollAddAllContacts(timer, NULL, POOL);
+}
+
+void PollAddAllContactsProtoOnline(int timer, const char *protocol)
+{
+ PollAddAllContacts(timer, protocol, PROTOCOL_ONLINE);
+}
+
+void PollAddAllContacts(int timer, const char *protocol, int type)
+{
+ // Has to check?
+ if (protocol != NULL && !PollCheckProtocol(protocol))
+ return;
+
+ // Make list for next timer ...
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ char *proto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ if (proto != NULL && (protocol == NULL || strcmp(proto, protocol) == 0)
+ && (protocol != NULL || PollCheckProtocol(proto)))
+ {
+ if (type == PROTOCOL_ONLINE)
+ {
+ Check what = ProtocolStatusCheckMsg(hContact, proto);
+ ProcessCheckNotToServer(what, hContact, proto);
+ }
+
+ QueueAdd(hContact, GetTickCount() + timer, type);
+ }
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+}
+
+VOID CALLBACK PollTimerAddContacts(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+{
+ if (hTimer != NULL)
+ {
+ KillTimer(NULL, hTimer);
+ hTimer = NULL;
+ }
+
+ // Adds with half the timer because the real timer is the calling of this function.
+ // The timer passed here is the amout of time that it will wait to see if the user do not
+ // request the message by itself
+ PollAddAllContactsTimer(opts.poll_timer_check * 60000 / 3);
+
+ PollSetTimer();
+}
+
+
+void PollSetTimer(void)
+{
+ if (hTimer != NULL)
+ {
+ KillTimer(NULL, hTimer);
+ hTimer = NULL;
+ }
+
+ if (opts.poll_check_on_timer)
+ hTimer = SetTimer(NULL, 0, opts.poll_timer_check * 60000, PollTimerAddContacts);
+}
+
+
+// Remove a contact from the current poll
+void PollReceivedContactMessage(HANDLE hContact, BOOL from_network)
+{
+ QueueRemove(hContact);
+
+ if (from_network)
+ {
+ EnterCriticalSection(&update_cs);
+
+ if (requested_item != NULL && requested_item->hContact == hContact)
+ {
+ mir_free(requested_item);
+ requested_item = NULL;
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ SetNextRequestAt(GetTickCount() + UpdateDelay());
+ }
+}
+
+
+void QueueRemove(HANDLE hContact)
+{
+ EnterCriticalSection(&update_cs);
+
+ if (statusQueue.queue->items != NULL)
+ {
+ DWORD now = GetTickCount() + 5000; // 5secs error allowed
+
+ for (int i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *item = (QueueItem*) statusQueue.queue->items[i];
+
+ if (item->hContact == hContact)
+ {
+ // Remove old items and status changes
+ if ((item->type & ~STATUS_CHANGE_TIMER) || item->check_time <= now)
+ {
+ mir_free(item);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ }
+ }
+
+ LeaveCriticalSection(&update_cs);
+}
+
+
+// Test if has to add an item
+BOOL QueueTestAdd(QueueItem *item)
+{
+ BOOL add = TRUE;
+
+ switch(item->type)
+ {
+ case XSTATUS_CHANGE:
+ {
+ // Remove ALL
+ int i;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ break;
+ }
+ case STATUS_CHANGE:
+ {
+ // Remove all, exept PROTOCOL_ONLINE
+ int i;
+ int test = ~PROTOCOL_ONLINE;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ if (tmp->type & test)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ else
+ {
+ add = FALSE;
+ }
+ }
+ }
+
+ if (requested_item != NULL && requested_item->hContact == item->hContact)
+ {
+ add = FALSE;
+ }
+
+ break;
+ }
+ case STATUS_CHANGE_TIMER:
+ {
+ // Remove STATUS_CHANGE_TIMER and POOL
+ int i;
+ int test = STATUS_CHANGE_TIMER | POOL;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ if (tmp->type & test)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ else if (tmp->type & PROTOCOL_ONLINE)
+ {
+ add = FALSE;
+ }
+ }
+ }
+ break;
+ }
+ case PROTOCOL_ONLINE:
+ {
+ // Remove ALL
+ int i;
+ for ( i = statusQueue.queue->realCount - 1 ; i >= 0 ; i-- )
+ {
+ QueueItem *tmp = (QueueItem *) statusQueue.queue->items[i];
+ if (tmp->hContact == item->hContact)
+ {
+ mir_free(tmp);
+ List_Remove(statusQueue.queue, i);
+ }
+ }
+ break;
+ }
+ //case POOL:
+ //{
+ // // Dont remove, allways add
+ // break;
+ //}
+ }
+
+ return add;
+}
+
+// Add an contact to the poll queue
+void QueueAdd(HANDLE hContact, DWORD check_time, int type)
+{
+ // Add this to thread...
+ QueueItem *item = (QueueItem *) mir_alloc0(sizeof(QueueItem));
+
+ if (item == NULL)
+ return;
+
+ item->hContact = hContact;
+ item->check_time = check_time;
+ item->type = type;
+
+ EnterCriticalSection(&update_cs);
+
+ if (QueueTestAdd(item))
+ {
+ List_InsertOrdered(statusQueue.queue, item);
+ }
+ else
+ {
+ mir_free(item);
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ ResumeThread(statusQueue.hThread);
+}
+
+
+// Itens with higher priority at end
+int QueueSortItems(void *i1, void *i2)
+{
+ return ((QueueItem*)i2)->check_time - ((QueueItem*)i1)->check_time;
+}
+
+// Returns if has to check that status
+Check ProtocolStatusCheckMsg(HANDLE hContact, const char *protocol)
+{
+ BOOL check = PollCheckContact(hContact);
+ int status = CallProtoService(protocol, PS_GETSTATUS, 0, 0);
+
+ // Exclude offline, connecting and invisible
+ if (status >= ID_STATUS_ONLINE && status <= ID_STATUS_OUTTOLUNCH && status != ID_STATUS_INVISIBLE)
+ {
+ // Status
+ int contact_status = DBGetContactSettingWord(hContact, protocol, "Status", 0);
+ int contact_xstatus = DBGetContactSettingByte(hContact, protocol, "XStatusId", 0);
+
+ // Check
+ if (opts.when_xstatus != Normal && contact_xstatus != 0)
+ {
+ // Use XStatus
+
+ bool has_xstatus_name = false;
+ bool has_xstatus_message = false;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusName", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ has_xstatus_name = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusMsg", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ has_xstatus_message = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ if (opts.when_xstatus == Clear
+ || (opts.when_xstatus == ClearOnMessage && has_xstatus_message) )
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+ else if (opts.when_xstatus == SetToXStatusName
+ || opts.when_xstatus == SetToXStatusMessage
+ || opts.when_xstatus == SetToXStatusNameXStatusMessage)
+ {
+ return check ? UseXStatus : DoNothing;
+ }
+ }
+
+ // If get until this point, Use normal status message
+ DWORD protoStatusFlags = CallProtoService(protocol, PS_GETCAPS, PFLAGNUM_3, 0);
+ if (protoStatusFlags & Proto_Status2Flag(contact_status))
+ {
+ return check ? Retrieve : DoNothing;// here you know the proto named protocol supports status i
+ }
+ else
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+ }
+ else // Protocol in a status that do not have to check msgs
+ {
+ return (check || opts.always_clear) ? ClearMessage : DoNothing;
+ }
+}
+
+// This should be called from outside the critical session
+void ProcessCheckNotToServer(Check what, HANDLE hContact, const char *protocol)
+{
+ switch(what)
+ {
+ case ClearMessage:
+ {
+ ClearStatusMessage(hContact);
+ PollReceivedContactMessage(hContact, FALSE);
+ break;
+ }
+ case UseXStatus:
+ {
+ bool has_xstatus_name = false;
+ bool has_xstatus_message = false;
+ char name[256];
+ char msg[256];
+ name[0] = '\0';
+ msg[0] = '\0';
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusName", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ strncpy(name, dbv.pszVal, sizeof(name));
+ name[sizeof(name)-1] = '\0';
+ has_xstatus_name = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(hContact, protocol, "XStatusMsg", &dbv))
+ {
+ if (dbv.pszVal != NULL && dbv.pszVal[0] != '\0')
+ {
+ strncpy(msg, dbv.pszVal, sizeof(msg));
+ msg[sizeof(msg)-1] = '\0';
+ has_xstatus_message = true;
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ // SetToXStatusName
+ if (opts.when_xstatus == SetToXStatusName)
+ {
+ SetStatusMessage(hContact, name);
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+
+ // SetToXStatusMessage
+ else if (opts.when_xstatus == SetToXStatusMessage)
+ {
+ SetStatusMessage(hContact, msg);
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+
+ // SetToXStatusNameXStatusValue
+ else if (opts.when_xstatus == SetToXStatusNameXStatusMessage)
+ {
+ if (has_xstatus_name && has_xstatus_message)
+ {
+ if (strcmp(name, msg) == 0)
+ {
+ // Both are the same, use only one
+ SetStatusMessage(hContact, name);
+ }
+ else
+ {
+ char message[512];
+ mir_snprintf(message, sizeof(message), "%s: %s", name, msg);
+ SetStatusMessage(hContact, message);
+ }
+ }
+ else if (has_xstatus_name)
+ {
+ SetStatusMessage(hContact, name);
+ }
+ else if (has_xstatus_message)
+ {
+ SetStatusMessage(hContact, msg);
+ }
+ PollReceivedContactMessage(hContact, FALSE);
+ }
+ break;
+ }
+ }
+}
+
+
+DWORD WINAPI UpdateThread(LPVOID vParam)
+{
+ // Initial timer
+ Sleep(INITIAL_TIMER);
+
+ while (statusQueue.bThreadRunning)
+ {
+ // First remove all that are due and do not have to be pooled
+ EnterCriticalSection(&update_cs);
+
+ if (!List_HasItens(statusQueue.queue))
+ {
+ LeaveCriticalSection(&update_cs);
+ }
+ else
+ {
+ // Create a copy of the queue
+ SortedList *tmpQueue;
+ tmpQueue = List_Create(0, statusQueue.queue->realCount);
+ tmpQueue->sortFunc = QueueSortItems;
+
+ for (int i = statusQueue.queue->realCount - 1; i >= 0; i--)
+ {
+ QueueItem *qi = (QueueItem *) List_Pop(statusQueue.queue);
+
+ if (qi->check_time > GetTickCount())
+ {
+ List_Push(statusQueue.queue, qi);
+ break;
+ }
+ else
+ {
+ List_InsertOrdered(tmpQueue, qi);
+ }
+ }
+
+ LeaveCriticalSection(&update_cs);
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // See the itens that was copied
+ if (List_HasItens(tmpQueue))
+ {
+ for (int i = tmpQueue->realCount - 1; i >= 0; i--)
+ {
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ QueueItem *qi = (QueueItem *) tmpQueue->items[i];
+
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)qi->hContact, 0);
+
+ if (proto == NULL || !PollCheckProtocol(proto))
+ {
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ else
+ {
+ Check what = ProtocolStatusCheckMsg(qi->hContact, proto);
+
+ if (what != DoNothing && what != Retrieve)
+ {
+ ProcessCheckNotToServer(what, qi->hContact, proto);
+
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ else if (what == DoNothing)
+ {
+ List_Remove(tmpQueue, i);
+ mir_free(qi);
+ }
+ }
+ }
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // See if has itens to be copied back
+ if (List_HasItens(tmpQueue))
+ {
+ EnterCriticalSection(&update_cs);
+ for (int i = tmpQueue->realCount - 1; i >= 0; i--)
+ {
+ QueueItem *qi = (QueueItem *) List_Pop(tmpQueue);
+ List_InsertOrdered(statusQueue.queue, qi);
+ }
+ LeaveCriticalSection(&update_cs);
+ }
+ }
+ }
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ // Was to run yet?
+ DWORD test_timer = GetNextRequestAt();
+ if (test_timer > GetTickCount())
+ {
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ // Ok, lets check the message
+ EnterCriticalSection(&update_cs);
+
+ if (requested_item != NULL)
+ {
+ if (GetTickCount() < requested_item->check_time + WaitTime())
+ {
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ mir_free(requested_item);
+ requested_item = NULL;
+
+ LeaveCriticalSection(&update_cs);
+ }
+ }
+ else if (!List_HasItens(statusQueue.queue))
+ {
+ LeaveCriticalSection(&update_cs);
+
+ // Stop this one...
+ if (statusQueue.bThreadRunning)
+ SuspendThread(statusQueue.hThread);
+ }
+ else
+ {
+ // Get next job...
+
+ /*
+ * the thread is awake, processing the update queue one by one entry with a given delay
+ * of UPDATE_DELAY seconds. The delay ensures that no protocol will kick us because of
+ * flood protection(s)
+ */
+ QueueItem *qi = (QueueItem *) List_Peek(statusQueue.queue);
+
+ if (qi->check_time > GetTickCount())
+ {
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ qi = (QueueItem *) List_Pop(statusQueue.queue);
+
+ LeaveCriticalSection(&update_cs);
+
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)qi->hContact, 0);
+
+ if (proto == NULL || !PollCheckProtocol(proto))
+ {
+ mir_free(qi);
+ }
+ else
+ {
+ Check what = ProtocolStatusCheckMsg(qi->hContact, proto);
+
+ if (what == DoNothing)
+ {
+ mir_free(qi);
+ }
+ else if (what != Retrieve)
+ {
+ ProcessCheckNotToServer(what, qi->hContact, proto);
+ mir_free(qi);
+ }
+ else // if (what == Retrieve)
+ {
+ if (!statusQueue.bThreadRunning)
+ break;
+
+ int ret = CallContactService(qi->hContact,PSS_GETAWAYMSG,0,0);
+
+ if (ret != 0)
+ {
+ EnterCriticalSection(&update_cs);
+
+ requested_item = qi;
+ requested_item->check_time = GetTickCount();
+
+ LeaveCriticalSection(&update_cs);
+
+ if (statusQueue.bThreadRunning)
+ Sleep(POOL_DELAY);
+ }
+ else
+ {
+ EnterCriticalSection(&update_cs);
+
+ // Error, pause for a while
+ List_Push(statusQueue.queue, qi);
+
+ LeaveCriticalSection(&update_cs);
+
+ int delay = ErrorDelay();
+ SetNextRequestAt(GetTickCount() + delay);
+ if (statusQueue.bThreadRunning)
+ Sleep(delay);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+void SetNextRequestAt(DWORD t)
+{
+ EnterCriticalSection(&pause_cs);
+ next_request_at = t;
+ LeaveCriticalSection(&pause_cs);
+}
+
+DWORD GetNextRequestAt(void)
+{
+ EnterCriticalSection(&pause_cs);
+ DWORD ret = next_request_at;
+ LeaveCriticalSection(&pause_cs);
+
+ return ret;
+}