From 67c6f233d7e60ad59918e734fe565f5f5dfd28d5 Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sun, 20 May 2012 18:33:39 +0000 Subject: projects renames and fixes git-svn-id: http://svn.miranda-ng.org/main/trunk@105 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AVS/poll.cpp | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 plugins/AVS/poll.cpp (limited to 'plugins/AVS/poll.cpp') diff --git a/plugins/AVS/poll.cpp b/plugins/AVS/poll.cpp new file mode 100644 index 0000000000..32553a9c33 --- /dev/null +++ b/plugins/AVS/poll.cpp @@ -0,0 +1,319 @@ +/* +Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish + +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 "commonheaders.h" + +/* +It has 1 queue: +A queue to request items. One request is done at a time, REQUEST_WAIT_TIME miliseconts after it has beeing fired + ACKRESULT_STATUS. This thread only requests the avatar (and maybe add it to the cache queue) +*/ + +#define REQUEST_WAIT_TIME 3000 + +// Time to wait before re-requesting an avatar that failed +#define REQUEST_FAIL_WAIT_TIME (3 * 60 * 60 * 1000) + +// Time to wait before re-requesting an avatar that received an wait for +#define REQUEST_WAITFOR_WAIT_TIME (30 * 60 * 1000) + +// Number of mileseconds the threads wait until take a look if it is time to request another item +#define POOL_DELAY 1000 + +// Number of mileseconds the threads wait after a GAIR_WAITFOR is returned +#define REQUEST_DELAY 18000 + + +// Prototypes /////////////////////////////////////////////////////////////////////////// + +static void RequestThread(void *vParam); + +extern HANDLE hShutdownEvent; +extern char *g_szMetaName; +extern int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0); +extern int DeleteAvatar(HANDLE hContact); +extern void MakePathRelative(HANDLE hContact, TCHAR *path); +int Proto_GetDelayAfterFail(const char *proto); +BOOL Proto_IsFetchingAlwaysAllowed(const char *proto); + +struct CacheNode *FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny = FALSE); + +extern HANDLE hEventContactAvatarChanged; +extern BOOL g_AvatarHistoryAvail; +extern FI_INTERFACE *fei; + +#ifdef _DEBUG +int _DebugTrace(const char *fmt, ...); +int _DebugTrace(HANDLE hContact, const char *fmt, ...); +#endif + +// Functions //////////////////////////////////////////////////////////////////////////// + +// Items with higher priority at end +static int QueueSortItems( const QueueItem* i1, const QueueItem* i2) +{ + return i2->check_time - i1->check_time; +} + +static OBJLIST queue( 20, QueueSortItems ); +static CRITICAL_SECTION cs; +static int waitTime; + +void InitPolls() +{ + waitTime = REQUEST_WAIT_TIME; + InitializeCriticalSection( &cs ); + + // Init request queue + mir_forkthread(RequestThread, NULL); +} + +void FreePolls() +{ +} + +// Return true if this protocol can have avatar requested +static BOOL PollProtocolCanHaveAvatar(const char *szProto) +{ + int pCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0); + int status = CallProtoService(szProto, PS_GETSTATUS, 0, 0); + return (pCaps & PF4_AVATARS) + && (g_szMetaName == NULL || strcmp(g_szMetaName, szProto)) + && ((status > ID_STATUS_OFFLINE && status != ID_STATUS_INVISIBLE) || Proto_IsFetchingAlwaysAllowed(szProto)); +} + +// Return true if this protocol has to be checked +static BOOL PollCheckProtocol(const char *szProto) +{ + return DBGetContactSettingByte(NULL, AVS_MODULE, szProto, 1); +} + +// Return true if this contact can have avatar requested +static BOOL PollContactCanHaveAvatar(HANDLE hContact, const char *szProto) +{ + int status = DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE); + return (Proto_IsFetchingAlwaysAllowed(szProto) || status != ID_STATUS_OFFLINE) + && !DBGetContactSettingByte(hContact, "CList", "NotOnList", 0) + && DBGetContactSettingByte(hContact, "CList", "ApparentMode", 0) != ID_STATUS_OFFLINE; +} + +// Return true if this contact has to be checked +static BOOL PollCheckContact(HANDLE hContact, const char *szProto) +{ + return !DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0) + && FindAvatarInCache(hContact, FALSE, TRUE) != NULL; +} + +static void QueueRemove(HANDLE hContact) +{ + EnterCriticalSection(&cs); + + for (int i = queue.getCount()-1 ; i >= 0 ; i-- ) { + QueueItem& item = queue[i]; + if (item.hContact == hContact) + queue.remove(i); + } + + LeaveCriticalSection(&cs); +} + +static void QueueAdd(HANDLE hContact, int waitTime) +{ + if(fei == NULL) + return; + + EnterCriticalSection(&cs); + + // Only add if not exists yet + int i; + for (i = queue.getCount()-1; i >= 0; i--) + if ( queue[i].hContact == hContact) + break; + + if (i < 0) { + QueueItem *item = new QueueItem; + if (item != NULL) { + item->hContact = hContact; + item->check_time = GetTickCount() + waitTime; + queue.insert(item); + } } + + LeaveCriticalSection(&cs); +} + +// Add an contact to a queue +void QueueAdd(HANDLE hContact) +{ + QueueAdd(hContact, waitTime); +} + +void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai, const char *szProto) +{ + QueueRemove(hContact); + + if (type == GAIR_SUCCESS) + { + if (pai == NULL) + return; + + // Fix settings in DB + DBDeleteContactSetting(hContact, "ContactPhoto", "NeedUpdate"); + DBDeleteContactSetting(hContact, "ContactPhoto", "RFile"); + if (!DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0)) + DBDeleteContactSetting(hContact, "ContactPhoto", "Backup"); + DBWriteContactSettingTString(hContact, "ContactPhoto", "File", pai->filename); + DBWriteContactSettingWord(hContact, "ContactPhoto", "Format", pai->format); + + if (pai->format == PA_FORMAT_PNG || pai->format == PA_FORMAT_JPEG + || pai->format == PA_FORMAT_ICON || pai->format == PA_FORMAT_BMP + || pai->format == PA_FORMAT_GIF) + { + // We can load it! + MakePathRelative(hContact, pai->filename); + ChangeAvatar(hContact, TRUE, TRUE, pai->format); + } + else + { + // As we can't load it, notify but don't load + ChangeAvatar(hContact, FALSE, TRUE, pai->format); + } + } + else if (type == GAIR_NOAVATAR) + { + DBDeleteContactSetting(hContact, "ContactPhoto", "NeedUpdate"); + + if (DBGetContactSettingByte(NULL, AVS_MODULE, "RemoveAvatarWhenContactRemoves", 1)) + { + // Delete settings + DBDeleteContactSetting(hContact, "ContactPhoto", "RFile"); + if (!DBGetContactSettingByte(hContact, "ContactPhoto", "Locked", 0)) + DBDeleteContactSetting(hContact, "ContactPhoto", "Backup"); + DBDeleteContactSetting(hContact, "ContactPhoto", "File"); + DBDeleteContactSetting(hContact, "ContactPhoto", "Format"); + + // Fix cache + ChangeAvatar(hContact, FALSE, TRUE, 0); + } + } + else if (type == GAIR_FAILED) + { + int wait = Proto_GetDelayAfterFail(szProto); + if (wait > 0) + { + // Reschedule to request after needed time (and avoid requests before that) + EnterCriticalSection(&cs); + QueueRemove(hContact); + QueueAdd(hContact, wait); + LeaveCriticalSection(&cs); + } + } +} + +int FetchAvatarFor(HANDLE hContact, char *szProto = NULL) +{ + int result = GAIR_NOAVATAR; + + if (szProto == NULL) + szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + + if (szProto != NULL && PollProtocolCanHaveAvatar(szProto) && PollContactCanHaveAvatar(hContact, szProto)) + { + // Can have avatar, but must request it? + if ( + (g_AvatarHistoryAvail && CallService(MS_AVATARHISTORY_ENABLED, (WPARAM) hContact, 0)) + || (PollCheckProtocol(szProto) && PollCheckContact(hContact, szProto)) + ) + { + // Request it + PROTO_AVATAR_INFORMATIONT pai_s = {0}; + pai_s.cbSize = sizeof(pai_s); + pai_s.hContact = hContact; + //_DebugTrace(hContact, "schedule request"); + INT_PTR res = CallProtoService(szProto, PS_GETAVATARINFOT, GAIF_FORCE, (LPARAM)&pai_s); +#ifdef _UNICODE + if (res == CALLSERVICE_NOTFOUND) + { + PROTO_AVATAR_INFORMATION pai = {0}; + pai.cbSize = sizeof(pai); + pai.hContact = hContact; + res = CallProtoService(szProto, PS_GETAVATARINFO, GAIF_FORCE, (LPARAM)&pai); + MultiByteToWideChar( CP_ACP, 0, pai.filename, -1, pai_s.filename, SIZEOF(pai_s.filename)); + pai_s.format = pai.format; + } +#endif + if (res != CALLSERVICE_NOTFOUND) result = res; + ProcessAvatarInfo(pai_s.hContact, result, &pai_s, szProto); + } + } + + return result; +} + +static void RequestThread(void *vParam) +{ + while (!g_shutDown) + { + EnterCriticalSection(&cs); + + if ( queue.getCount() == 0 ) + { + // No items, so supend thread + LeaveCriticalSection(&cs); + + mir_sleep(POOL_DELAY); + } + else + { + // Take a look at first item + QueueItem& qi = queue[ queue.getCount()-1 ]; + + if (qi.check_time > GetTickCount()) + { + // Not time to request yet, wait... + LeaveCriticalSection(&cs); + mir_sleep(POOL_DELAY); + } + else + { + // Will request this item + HANDLE hContact = qi.hContact; + queue.remove( queue.getCount()-1 ); + + QueueRemove(hContact); + + LeaveCriticalSection(&cs); + + if (FetchAvatarFor(hContact) == GAIR_WAITFOR) + { + // Mark to not request this contact avatar for more 30 min + EnterCriticalSection(&cs); + QueueRemove(hContact); + QueueAdd(hContact, REQUEST_WAITFOR_WAIT_TIME); + LeaveCriticalSection(&cs); + + // Wait a little until requesting again + mir_sleep(REQUEST_DELAY); + } + } + } + } + + DeleteCriticalSection(&cs); + queue.destroy(); +} -- cgit v1.2.3