From 960336573d3420785094dd46fe4a3edb4cb152a8 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sat, 10 Aug 2013 21:34:29 +0000 Subject: divide et impera git-svn-id: http://svn.miranda-ng.org/main/trunk@5639 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AVS/avs_10.vcxproj | 3 + plugins/AVS/avs_10.vcxproj.filters | 9 + plugins/AVS/avs_11.vcxproj | 3 + plugins/AVS/avs_11.vcxproj.filters | 9 + plugins/AVS/src/acc.cpp | 7 - plugins/AVS/src/cache.cpp | 360 ++++++ plugins/AVS/src/commonheaders.h | 75 +- plugins/AVS/src/main.cpp | 2158 ++---------------------------------- plugins/AVS/src/options.cpp | 7 - plugins/AVS/src/poll.cpp | 8 +- plugins/AVS/src/services.cpp | 997 +++++++++++++++++ plugins/AVS/src/utils.cpp | 634 +++++++++++ plugins/AVS/src/version.h | 4 +- 13 files changed, 2200 insertions(+), 2074 deletions(-) create mode 100644 plugins/AVS/src/cache.cpp create mode 100644 plugins/AVS/src/services.cpp create mode 100644 plugins/AVS/src/utils.cpp (limited to 'plugins/AVS') diff --git a/plugins/AVS/avs_10.vcxproj b/plugins/AVS/avs_10.vcxproj index cdf4553179..b486cc998a 100644 --- a/plugins/AVS/avs_10.vcxproj +++ b/plugins/AVS/avs_10.vcxproj @@ -207,13 +207,16 @@ + + Create + diff --git a/plugins/AVS/avs_10.vcxproj.filters b/plugins/AVS/avs_10.vcxproj.filters index 05a3b22fa3..338e0d11a4 100644 --- a/plugins/AVS/avs_10.vcxproj.filters +++ b/plugins/AVS/avs_10.vcxproj.filters @@ -33,6 +33,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/plugins/AVS/avs_11.vcxproj b/plugins/AVS/avs_11.vcxproj index 9d4563fec1..ab0939273a 100644 --- a/plugins/AVS/avs_11.vcxproj +++ b/plugins/AVS/avs_11.vcxproj @@ -210,13 +210,16 @@ + + Create + diff --git a/plugins/AVS/avs_11.vcxproj.filters b/plugins/AVS/avs_11.vcxproj.filters index 05a3b22fa3..1c2c19c978 100644 --- a/plugins/AVS/avs_11.vcxproj.filters +++ b/plugins/AVS/avs_11.vcxproj.filters @@ -33,6 +33,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/plugins/AVS/src/acc.cpp b/plugins/AVS/src/acc.cpp index 5e2512c82c..971b29005f 100644 --- a/plugins/AVS/src/acc.cpp +++ b/plugins/AVS/src/acc.cpp @@ -23,13 +23,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "commonheaders.h" -int GetImageFormat(TCHAR *filename); -INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam); -INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam); -INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam); -void InternalDrawAvatar(AVATARDRAWREQUEST *r, HBITMAP hbm, LONG bmWidth, LONG bmHeight, DWORD dwFlags); - - #define DM_AVATARCHANGED (WM_USER + 20) #define DM_MYAVATARCHANGED (WM_USER + 21) diff --git a/plugins/AVS/src/cache.cpp b/plugins/AVS/src/cache.cpp new file mode 100644 index 0000000000..42caa1734c --- /dev/null +++ b/plugins/AVS/src/cache.cpp @@ -0,0 +1,360 @@ +/* +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" + +static int g_maxBlock = 0, g_curBlock = 0; +static CacheNode **g_cacheBlocks = NULL; + +static CacheNode *g_Cache = 0; +static CRITICAL_SECTION cachecs, alloccs; + +/* + * allocate a cache block and add it to the list of blocks + * does not link the new block with the old block(s) - caller needs to do this + */ + +static CacheNode *AllocCacheBlock() +{ + CacheNode *allocedBlock = NULL; + + allocedBlock = (CacheNode *)malloc(CACHE_BLOCKSIZE * sizeof(struct CacheNode)); + ZeroMemory((void *)allocedBlock, sizeof(struct CacheNode) * CACHE_BLOCKSIZE); + + for(int i = 0; i < CACHE_BLOCKSIZE - 1; i++) + allocedBlock[i].pNextNode = &allocedBlock[i + 1]; // pre-link the alloced block + + if (g_Cache == NULL) // first time only... + g_Cache = allocedBlock; + + // add it to the list of blocks + + if (g_curBlock == g_maxBlock) { + g_maxBlock += 10; + g_cacheBlocks = (CacheNode **)realloc(g_cacheBlocks, g_maxBlock * sizeof(CacheNode *)); + } + g_cacheBlocks[g_curBlock++] = allocedBlock; + + return(allocedBlock); +} + +void InitCache(void) +{ + InitializeCriticalSection(&cachecs); + InitializeCriticalSection(&alloccs); + AllocCacheBlock(); +} + +void UnloadCache(void) +{ + CacheNode *pNode = g_Cache; + while(pNode) { + if (pNode->ace.hbmPic != 0) + DeleteObject(pNode->ace.hbmPic); + pNode = pNode->pNextNode; + } + + for(int i = 0; i < g_curBlock; i++) + free(g_cacheBlocks[i]); + free(g_cacheBlocks); + + DeleteCriticalSection(&alloccs); + DeleteCriticalSection(&cachecs); +} + +/* + * link a new cache block with the already existing chain of blocks + */ + +static CacheNode *AddToList(CacheNode *node) +{ + CacheNode *pCurrent = g_Cache; + + while(pCurrent->pNextNode != 0) + pCurrent = pCurrent->pNextNode; + + pCurrent->pNextNode = node; + return pCurrent; +} + +CacheNode *FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny) +{ + if (g_shutDown) + return NULL; + + char *szProto = GetContactProto(hContact); + if (szProto == NULL || !db_get_b(NULL, AVS_MODULE, szProto, 1)) + return NULL; + + mir_cslock lck(cachecs); + + CacheNode *ρρ = g_Cache, *foundNode = NULL; + while(ρρ) { + if (ρρ->ace.hContact == hContact) { + ρρ->ace.t_lastAccess = time(NULL); + foundNode = ρρ->loaded || findAny ? ρρ : NULL; + return foundNode; + } + + // found an empty and usable node + if (foundNode == NULL && ρρ->ace.hContact == 0) + foundNode = ρρ; + + ρρ = ρρ->pNextNode; + } + + // not found + if (!add) + return NULL; + + if (foundNode == NULL) { // no free entry found, create a new and append it to the list + mir_cslock all(alloccs); // protect memory block allocation + CacheNode *newNode = AllocCacheBlock(); + AddToList(newNode); + foundNode = newNode; + } + + foundNode->ace.hContact = hContact; + if (g_MetaAvail) + foundNode->dwFlags |= (db_get_b(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0); + foundNode->loaded = FALSE; + foundNode->mustLoad = 1; // pic loader will watch this and load images + SetEvent(hLoaderEvent); // wake him up + return NULL; +} + +/* + * output a notification message. + * may accept a hContact to include the contacts nickname in the notification message... + * the actual message is using printf() rules for formatting and passing the arguments... + * + * can display the message either as systray notification (baloon popup) or using the + * popup plugin. + */ + +void NotifyMetaAware(HANDLE hContact, CacheNode *node = NULL, AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)-1) +{ + if (g_shutDown) + return; + + if (ace == (AVATARCACHEENTRY *)-1) + ace = &node->ace; + + NotifyEventHooks(hEventChanged, (WPARAM)hContact, (LPARAM)ace); + + if (g_MetaAvail && (node->dwFlags & MC_ISSUBCONTACT) && db_get_b(NULL, g_szMetaName, "Enabled", 0)) { + HANDLE hMasterContact = (HANDLE)db_get_dw(hContact, g_szMetaName, "Handle", 0); + if (hMasterContact && (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMasterContact, 0) == hContact && + !db_get_b(hMasterContact, "ContactPhoto", "Locked", 0)) + NotifyEventHooks(hEventChanged, (WPARAM)hMasterContact, (LPARAM)ace); + } + + if (node->dwFlags & AVH_MUSTNOTIFY) { + // Fire the event for avatar history + node->dwFlags &= ~AVH_MUSTNOTIFY; + if (node->ace.szFilename[0] != '\0') { + CONTACTAVATARCHANGEDNOTIFICATION cacn = {0}; + cacn.cbSize = sizeof(CONTACTAVATARCHANGEDNOTIFICATION); + cacn.hContact = hContact; + cacn.format = node->pa_format; + _tcsncpy(cacn.filename, node->ace.szFilename, MAX_PATH); + cacn.filename[MAX_PATH - 1] = 0; + + // Get hash + char *szProto = GetContactProto(hContact); + if (szProto != NULL) { + DBVARIANT dbv = {0}; + if ( !db_get_s(hContact, szProto, "AvatarHash", &dbv)) { + if (dbv.type == DBVT_TCHAR) + _tcsncpy_s(cacn.hash, SIZEOF(cacn.hash), dbv.ptszVal, _TRUNCATE); + else if (dbv.type == DBVT_BLOB) { + // Lets use base64 encode + char *tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i; + for(i = 0; i < dbv.cpbVal / 3 && i*4+3 < sizeof(cacn.hash)-1; i++) { + BYTE a = dbv.pbVal[i*3]; + BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0; + BYTE c = i*3 + 2 < dbv.cpbVal ? dbv.pbVal[i*3 + 2] : 0; + + cacn.hash[i*4] = tab[(a & 0xFC) >> 2]; + cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)]; + cacn.hash[i*4+2] = tab[((b & 0xF) << 2) + ((c & 0xC0) >> 6)]; + cacn.hash[i*4+3] = tab[c & 0x3F]; + } + if (dbv.cpbVal % 3 != 0 && i*4+3 < sizeof(cacn.hash)-1) { + BYTE a = dbv.pbVal[i*3]; + BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0; + + cacn.hash[i*4] = tab[(a & 0xFC) >> 2]; + cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)]; + if (i + 1 < dbv.cpbVal) + cacn.hash[i*4+2] = tab[((b & 0xF) << 4)]; + else + cacn.hash[i*4+2] = '='; + cacn.hash[i*4+3] = '='; + } + } + db_free(&dbv); + } + } + + // Default value + if (cacn.hash[0] == '\0') + mir_sntprintf(cacn.hash, SIZEOF(cacn.hash), _T("AVS-HASH-%x"), GetFileHash(cacn.filename)); + + NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)cacn.hContact, (LPARAM)&cacn); + } + else NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)hContact, NULL); + } +} + +/* + * this thread scans the cache and handles nodes which have mustLoad set to > 0 (must be loaded/reloaded) or + * nodes where mustLoad is < 0 (must be deleted). + * its waken up by the event and tries to lock the cache only when absolutely necessary. + */ + +void PicLoader(LPVOID param) +{ + DWORD dwDelay = db_get_dw(NULL, AVS_MODULE, "picloader_sleeptime", 80); + + if (dwDelay < 30) + dwDelay = 30; + else if (dwDelay > 100) + dwDelay = 100; + + while(!g_shutDown) { + CacheNode *node = g_Cache; + + while(!g_shutDown && node) { + if (node->mustLoad > 0 && node->ace.hContact) { + node->mustLoad = 0; + AVATARCACHEENTRY ace_temp; + + if (db_get_b(node->ace.hContact, "ContactPhoto", "NeedUpdate", 0)) + QueueAdd(node->ace.hContact); + + CopyMemory(&ace_temp, &node->ace, sizeof(AVATARCACHEENTRY)); + ace_temp.hbmPic = 0; + + int result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL); + + if (result == -2) { + char *szProto = GetContactProto(node->ace.hContact); + if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto)) + QueueAdd(node->ace.hContact); + else if (FetchAvatarFor(node->ace.hContact, szProto) == GAIR_SUCCESS) // Try yo create again + result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL); + } + + if (result == 1 && ace_temp.hbmPic != 0) { // Loaded + HBITMAP oldPic = node->ace.hbmPic; + + EnterCriticalSection(&cachecs); + CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY)); + node->loaded = TRUE; + LeaveCriticalSection(&cachecs); + if (oldPic) + DeleteObject(oldPic); + NotifyMetaAware(node->ace.hContact, node); + } + else if (result == 0 || result == -3) { // Has no avatar + HBITMAP oldPic = node->ace.hbmPic; + + EnterCriticalSection(&cachecs); + CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY)); + node->loaded = FALSE; + node->mustLoad = 0; + LeaveCriticalSection(&cachecs); + if (oldPic) + DeleteObject(oldPic); + NotifyMetaAware(node->ace.hContact, node); + } + + mir_sleep(dwDelay); + } + else if (node->mustLoad < 0 && node->ace.hContact) { // delete this picture + HANDLE hContact = node->ace.hContact; + EnterCriticalSection(&cachecs); + node->mustLoad = 0; + node->loaded = 0; + if (node->ace.hbmPic) + DeleteObject(node->ace.hbmPic); + ZeroMemory(&node->ace, sizeof(AVATARCACHEENTRY)); + if (node->dwFlags & AVS_DELETENODEFOREVER) + node->dwFlags &= ~AVS_DELETENODEFOREVER; + else + node->ace.hContact = hContact; + + LeaveCriticalSection(&cachecs); + NotifyMetaAware(hContact, node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact)); + } + // protect this by changes from the cache block allocator as it can cause inconsistencies while working + // on allocating a new block. + EnterCriticalSection(&alloccs); + node = node->pNextNode; + LeaveCriticalSection(&alloccs); + } + WaitForSingleObject(hLoaderEvent, INFINITE); + //_DebugTrace(0, "pic loader awake..."); + ResetEvent(hLoaderEvent); + } +} + +// Just delete an avatar from cache +// An cache entry is never deleted. What is deleted is the image handle inside it +// This is done this way to keep track of which avatars avs have to keep track +void DeleteAvatarFromCache(HANDLE hContact, BOOL forever) +{ + hContact = GetContactThatHaveTheAvatar(hContact); + + CacheNode *node = FindAvatarInCache(hContact, FALSE); + if (node == NULL) { + struct CacheNode temp_node = {0}; + if (g_MetaAvail) + temp_node.dwFlags |= (db_get_b(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0); + NotifyMetaAware(hContact, &temp_node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact)); + return; + } + node->mustLoad = -1; // mark for deletion + if (forever) + node->dwFlags |= AVS_DELETENODEFOREVER; + SetEvent(hLoaderEvent); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode) +{ + CacheNode *ρρ = g_Cache; + + while(ρρ) { + if (ρρ->ace.hContact == hContact) { + DWORD dwFlags = ρρ->ace.dwFlags; + + ρρ->ace.dwFlags = mode ? (ρρ->ace.dwFlags | attrib) : (ρρ->ace.dwFlags & ~attrib); + if (ρρ->ace.dwFlags != dwFlags) + NotifyMetaAware(hContact, ρρ); + break; + } + ρρ = ρρ->pNextNode; + } + return 0; +} + diff --git a/plugins/AVS/src/commonheaders.h b/plugins/AVS/src/commonheaders.h index 0541a7e53a..0cab3148b9 100644 --- a/plugins/AVS/src/commonheaders.h +++ b/plugins/AVS/src/commonheaders.h @@ -50,9 +50,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "poll.h" #include "acc.h" - -// shared vars -//extern HINSTANCE g_hInst; +#ifndef SHVIEW_THUMBNAIL +#define SHVIEW_THUMBNAIL 0x702D +#endif /* most free()'s are invalid when the code is executed from a dll, so this changes all the bad free()'s to good ones, however it's still incorrect code. The reasons for not @@ -89,10 +89,16 @@ struct protoPicCacheEntry : public avatarCacheEntry, public MZeroedObject extern OBJLIST g_ProtoPictures, g_MyAvatars; -extern FI_INTERFACE *fei; +struct SetMyAvatarHookData +{ + char *protocol; + BOOL square; + BOOL grow; -int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode); -void DeleteAvatarFromCache(HANDLE, BOOL); + BOOL thumbnail; +}; + +extern FI_INTERFACE *fei; #define GAIR_FAILED 1000 @@ -101,4 +107,59 @@ void DeleteAvatarFromCache(HANDLE, BOOL); #define AVS_DEFAULT "Global avatar" void mir_sleep(int time); -extern bool g_shutDown; \ No newline at end of file +extern bool g_shutDown; +extern char *g_szMetaName; +extern TCHAR g_szDataPath[]; // user datae path (read at startup only) +extern BOOL g_MetaAvail, g_AvatarHistoryAvail; +extern HWND hwndSetMyAvatar; + +extern HINSTANCE g_hInst; + +extern HANDLE hMyAvatarsFolder; +extern HANDLE hGlobalAvatarFolder; +extern HANDLE hLoaderEvent, hShutdownEvent; +extern HANDLE hEventChanged, hEventContactAvatarChanged, hMyAvatarChanged; + +int GetFileHash(TCHAR* filename); +DWORD GetFileSize(TCHAR *szFilename); +const TCHAR *GetFormatExtension(int format); +int GetImageFormat(TCHAR *filename); +void MakePathRelative(HANDLE hContact); +void MakePathRelative(HANDLE hContact, TCHAR *dest); + +HBITMAP LoadPNG(struct avatarCacheEntry *ace, char *szFilename); + +void InitCache(void); +void UnloadCache(void); +int CreateAvatarInCache(HANDLE hContact, avatarCacheEntry *ace, char *szProto); +void DeleteAvatarFromCache(HANDLE, BOOL); +void PicLoader(LPVOID param); + +void InternalDrawAvatar(AVATARDRAWREQUEST *r, HBITMAP hbm, LONG bmWidth, LONG bmHeight, DWORD dwFlags); + +int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0); +void DeleteGlobalUserAvatar(); +int FetchAvatarFor(HANDLE hContact, char *szProto = NULL); +CacheNode* FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny = FALSE); +int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode); +void SetIgnoreNotify(char *protocol, BOOL ignore); + +INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam); +INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam); +INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam); +INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam); +INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam); + +HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked = -1); + +void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai, const char *szProto); + +int Proto_GetDelayAfterFail(const char *proto); +BOOL Proto_NeedDelaysForAvatars(const char *proto); +BOOL Proto_IsAvatarsEnabled(const char *proto); +BOOL Proto_IsAvatarFormatSupported(const char *proto, int format); +int Proto_AvatarImageProportion(const char *proto); +void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height); +int Proto_GetAvatarMaxFileSize(const char *proto); + +protoPicCacheEntry* GetProtoDefaultAvatar(HANDLE hContact); \ No newline at end of file diff --git a/plugins/AVS/src/main.cpp b/plugins/AVS/src/main.cpp index bd54f660e6..83a36ea251 100644 --- a/plugins/AVS/src/main.cpp +++ b/plugins/AVS/src/main.cpp @@ -29,1794 +29,103 @@ bool g_shutDown = false; int hLangpack; -static TCHAR g_szDataPath[MAX_PATH]; // user datae path (read at startup only) -static BOOL g_MetaAvail = FALSE; -BOOL g_AvatarHistoryAvail = FALSE; -static long hwndSetMyAvatar = 0; +TCHAR g_szDataPath[MAX_PATH]; // user datae path (read at startup only) +BOOL g_MetaAvail = FALSE; +BOOL g_AvatarHistoryAvail = FALSE; +HWND hwndSetMyAvatar = 0; -static HANDLE hMyAvatarsFolder; -static HANDLE hGlobalAvatarFolder; -static HANDLE hLoaderEvent, hShutdownEvent; -static HANDLE hEventContactAvatarChanged, hMyAvatarChanged; -HANDLE hEventChanged; +HANDLE hMyAvatarsFolder; +HANDLE hGlobalAvatarFolder; +HANDLE hLoaderEvent, hShutdownEvent; +HANDLE hEventChanged, hEventContactAvatarChanged, hMyAvatarChanged; -BOOL (WINAPI *AvsAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION) = NULL; +void InitServices(); -static struct CacheNode *g_Cache = 0; -static CRITICAL_SECTION cachecs, alloccs; +BOOL (WINAPI *AvsAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION) = NULL; static int ComparePicture( const protoPicCacheEntry* p1, const protoPicCacheEntry* p2 ) { if ((lstrcmpA(p1->szProtoname, "Global avatar") == 0) || strstr(p1->szProtoname, "Global avatar")) - return -1; - if ((lstrcmpA(p2->szProtoname, "Global avatar") == 0) || strstr(p1->szProtoname, "Global avatar")) - return 1; - return lstrcmpA( p1->szProtoname, p2->szProtoname ); -} - -OBJLIST - g_ProtoPictures( 10, ComparePicture ), - g_MyAvatars( 10, ComparePicture ); - -char* g_szMetaName = NULL; - -#ifndef SHVIEW_THUMBNAIL -#define SHVIEW_THUMBNAIL 0x702D -#endif - -// Stores the id of the dialog - -int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0); - -int OnDetailsInit(WPARAM wParam, LPARAM lParam); -int OptInit(WPARAM wParam, LPARAM lParam); - -static int ShutdownProc(WPARAM wParam, LPARAM lParam); -static int OkToExitProc(WPARAM wParam, LPARAM lParam); -static int GetFileHash(TCHAR* filename); -static DWORD GetFileSize(TCHAR *szFilename); - -void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai, const char *szProto); -int FetchAvatarFor(HANDLE hContact, char *szProto = NULL); -static INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam); - -BOOL Proto_IsAvatarsEnabled(const char *proto); -BOOL Proto_IsAvatarFormatSupported(const char *proto, int format); -void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height); -int Proto_AvatarImageProportion(const char *proto); -BOOL Proto_NeedDelaysForAvatars(const char *proto); -int Proto_GetAvatarMaxFileSize(const char *proto); -int Proto_GetDelayAfterFail(const char *proto); - -FI_INTERFACE *fei = 0; - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __AUTHOREMAIL, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {E00F1643-263C-4599-B84B-053E5C511D29} - {0xe00f1643, 0x263c, 0x4599, {0xb8, 0x4b, 0x5, 0x3e, 0x5c, 0x51, 0x1d, 0x29}} -}; - -extern INT_PTR CALLBACK DlgProcAvatarOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); - -static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int format, BOOL square, BOOL grow); - -/* - * output a notification message. - * may accept a hContact to include the contacts nickname in the notification message... - * the actual message is using printf() rules for formatting and passing the arguments... - * - * can display the message either as systray notification (baloon popup) or using the - * popup plugin. - */ - -#ifdef _DEBUG - -int _DebugTrace(const char *fmt, ...) -{ - char debug[2048]; - int ibsize = 2047; - va_list va; - va_start(va, fmt); - - mir_snprintf(debug, SIZEOF(debug) - 10, " ***** AVS [%08d] [ID:%04x]: ", GetTickCount(), GetCurrentThreadId()); - OutputDebugStringA(debug); - mir_vsnprintf(debug, ibsize, fmt, va); - OutputDebugStringA(debug); - OutputDebugStringA(" ***** \n"); - - return 0; -} - -int _DebugTrace(HANDLE hContact, const char *fmt, ...) -{ - char text[1024]; - size_t len; - va_list va; - - char *name = NULL; - char *proto = NULL; - if (hContact != NULL) - { - name = (char*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); - proto = GetContactProto(hContact); - } - - mir_snprintf(text, SIZEOF(text) - 10, " ***** AVS [%08d] [ID:%04x]: [%08d - %s - %s] ", - GetTickCount(), GetCurrentThreadId(), hContact, proto == NULL ? "" : proto, name == NULL ? "" : name); - len = strlen(text); - - va_start(va, fmt); - mir_vsnprintf(&text[len], SIZEOF(text) - len, fmt, va); - va_end(va); - - OutputDebugStringA(text); - OutputDebugStringA(" ***** \n"); - - return 0; -} - -#endif - -void mir_sleep(int time) -{ - if (!g_shutDown) - WaitForSingleObject(hShutdownEvent, time); -} - -/* - * path utilities (make avatar paths relative to *PROFILE* directory, not miranda directory. - * taken and modified from core services - */ - -int AVS_pathIsAbsolute(const TCHAR *path) -{ - if (!path || !(lstrlen(path) > 2)) - return 0; - if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\')) return 1; - return 0; -} - -size_t AVS_pathToRelative(const TCHAR *pSrc, TCHAR *pOut) -{ - if (!pSrc || !*pSrc || _tcslen(pSrc) > MAX_PATH) return 0; - if (!AVS_pathIsAbsolute( pSrc )) - lstrcpyn(pOut, pSrc, MAX_PATH); - else { - TCHAR szTmp[MAX_PATH]; - mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s"), pSrc); - _tcslwr(szTmp); - if (_tcsstr(szTmp, g_szDataPath)) - lstrcpyn(pOut, pSrc + _tcslen(g_szDataPath) + 1, MAX_PATH); - else - lstrcpyn(pOut, pSrc, MAX_PATH); - } - return _tcslen(pOut); -} - -size_t AVS_pathToAbsolute(const TCHAR *pSrc, TCHAR *pOut) -{ - if (!pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH) - return 0; - - if (AVS_pathIsAbsolute(pSrc) || !_istalnum(pSrc[0])) - lstrcpyn(pOut, pSrc, MAX_PATH); - else - mir_sntprintf(pOut, MAX_PATH, _T("%s\\%s"), g_szDataPath, pSrc, MAX_PATH); - return lstrlen(pOut); -} - -static void NotifyMetaAware(HANDLE hContact, struct CacheNode *node = NULL, AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)-1) -{ - if (g_shutDown) - return; - - if (ace == (AVATARCACHEENTRY *)-1) - ace = &node->ace; - - NotifyEventHooks(hEventChanged, (WPARAM)hContact, (LPARAM)ace); - - if (g_MetaAvail && (node->dwFlags & MC_ISSUBCONTACT) && db_get_b(NULL, g_szMetaName, "Enabled", 0)) { - HANDLE hMasterContact = (HANDLE)db_get_dw(hContact, g_szMetaName, "Handle", 0); - if (hMasterContact && (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMasterContact, 0) == hContact && - !db_get_b(hMasterContact, "ContactPhoto", "Locked", 0)) - NotifyEventHooks(hEventChanged, (WPARAM)hMasterContact, (LPARAM)ace); - } - - if (node->dwFlags & AVH_MUSTNOTIFY) { - // Fire the event for avatar history - node->dwFlags &= ~AVH_MUSTNOTIFY; - if (node->ace.szFilename[0] != '\0') { - CONTACTAVATARCHANGEDNOTIFICATION cacn = {0}; - cacn.cbSize = sizeof(CONTACTAVATARCHANGEDNOTIFICATION); - cacn.hContact = hContact; - cacn.format = node->pa_format; - _tcsncpy(cacn.filename, node->ace.szFilename, MAX_PATH); - cacn.filename[MAX_PATH - 1] = 0; - - // Get hash - char *szProto = GetContactProto(hContact); - if (szProto != NULL) { - DBVARIANT dbv = {0}; - if ( !db_get_s(hContact, szProto, "AvatarHash", &dbv)) { - if (dbv.type == DBVT_TCHAR) - _tcsncpy_s(cacn.hash, SIZEOF(cacn.hash), dbv.ptszVal, _TRUNCATE); - else if (dbv.type == DBVT_BLOB) { - // Lets use base64 encode - char *tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i; - for(i = 0; i < dbv.cpbVal / 3 && i*4+3 < sizeof(cacn.hash)-1; i++) { - BYTE a = dbv.pbVal[i*3]; - BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0; - BYTE c = i*3 + 2 < dbv.cpbVal ? dbv.pbVal[i*3 + 2] : 0; - - cacn.hash[i*4] = tab[(a & 0xFC) >> 2]; - cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)]; - cacn.hash[i*4+2] = tab[((b & 0xF) << 2) + ((c & 0xC0) >> 6)]; - cacn.hash[i*4+3] = tab[c & 0x3F]; - } - if (dbv.cpbVal % 3 != 0 && i*4+3 < sizeof(cacn.hash)-1) { - BYTE a = dbv.pbVal[i*3]; - BYTE b = i*3 + 1 < dbv.cpbVal ? dbv.pbVal[i*3 + 1] : 0; - - cacn.hash[i*4] = tab[(a & 0xFC) >> 2]; - cacn.hash[i*4+1] = tab[((a & 0x3) << 4) + ((b & 0xF0) >> 4)]; - if (i + 1 < dbv.cpbVal) - cacn.hash[i*4+2] = tab[((b & 0xF) << 4)]; - else - cacn.hash[i*4+2] = '='; - cacn.hash[i*4+3] = '='; - } - } - db_free(&dbv); - } - } - - // Default value - if (cacn.hash[0] == '\0') - mir_sntprintf(cacn.hash, SIZEOF(cacn.hash), _T("AVS-HASH-%x"), GetFileHash(cacn.filename)); - - NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)cacn.hContact, (LPARAM)&cacn); - } - else NotifyEventHooks(hEventContactAvatarChanged, (WPARAM)hContact, NULL); - } -} - -static int g_maxBlock = 0, g_curBlock = 0; -static struct CacheNode **g_cacheBlocks = NULL; - -/* - * allocate a cache block and add it to the list of blocks - * does not link the new block with the old block(s) - caller needs to do this - */ - -static struct CacheNode *AllocCacheBlock() -{ - struct CacheNode *allocedBlock = NULL; - - allocedBlock = (struct CacheNode *)malloc(CACHE_BLOCKSIZE * sizeof(struct CacheNode)); - ZeroMemory((void *)allocedBlock, sizeof(struct CacheNode) * CACHE_BLOCKSIZE); - - for(int i = 0; i < CACHE_BLOCKSIZE - 1; i++) - allocedBlock[i].pNextNode = &allocedBlock[i + 1]; // pre-link the alloced block - - if (g_Cache == NULL) // first time only... - g_Cache = allocedBlock; - - // add it to the list of blocks - - if (g_curBlock == g_maxBlock) { - g_maxBlock += 10; - g_cacheBlocks = (struct CacheNode **)realloc(g_cacheBlocks, g_maxBlock * sizeof(struct CacheNode *)); - } - g_cacheBlocks[g_curBlock++] = allocedBlock; - - return(allocedBlock); -} - -int SetAvatarAttribute(HANDLE hContact, DWORD attrib, int mode) -{ - struct CacheNode *ρρ = g_Cache; - - while(ρρ) { - if (ρρ->ace.hContact == hContact) { - DWORD dwFlags = ρρ->ace.dwFlags; - - ρρ->ace.dwFlags = mode ? (ρρ->ace.dwFlags | attrib) : (ρρ->ace.dwFlags & ~attrib); - if (ρρ->ace.dwFlags != dwFlags) - NotifyMetaAware(hContact, ρρ); - break; - } - ρρ = ρρ->pNextNode; - } - return 0; -} - -/* - * convert the avatar image path to a relative one... - * given: contact handle, path to image - */ -void MakePathRelative(HANDLE hContact, TCHAR *path) -{ - TCHAR szFinalPath[MAX_PATH]; - szFinalPath[0] = '\0'; - - size_t result = AVS_pathToRelative(path, szFinalPath); - if (result && lstrlen(szFinalPath) > 0) { - db_set_ts(hContact, "ContactPhoto", "RFile", szFinalPath); - if (!db_get_b(hContact, "ContactPhoto", "Locked", 0)) - db_set_ts(hContact, "ContactPhoto", "Backup", szFinalPath); - } -} - -/* - * convert the avatar image path to a relative one... - * given: contact handle - */ - -static void MakePathRelative(HANDLE hContact) -{ - DBVARIANT dbv; - if ( !db_get_ts(hContact, "ContactPhoto", "File", &dbv)) { - MakePathRelative(hContact, dbv.ptszVal); - db_free(&dbv); - } -} - -static void ResetTranspSettings(HANDLE hContact) -{ - db_unset(hContact, "ContactPhoto", "MakeTransparentBkg"); - db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints"); - db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff"); -} - -static TCHAR *getJGMailID(char *szProto) -{ - static TCHAR szJID[MAX_PATH+1]; szJID[0] = '\0'; - - DBVARIANT dbva, dbvb; - if ( db_get_ts(NULL, szProto, "LoginName", &dbva)) - return szJID; - - if ( db_get_ts(NULL, szProto, "LoginServer", &dbvb)) { - db_free(&dbva); - return szJID; - } - - mir_sntprintf(szJID, SIZEOF(szJID), _T("%s@%s"), dbva.ptszVal, dbvb.ptszVal); - db_free(&dbva); - db_free(&dbvb); - return szJID; -} - -// create the avatar in cache -// returns 0 if not created (no avatar), iIndex otherwise, -2 if has to request avatar, -3 if avatar too big -int CreateAvatarInCache(HANDLE hContact, avatarCacheEntry *ace, char *szProto) -{ - DBVARIANT dbv = {0}; - char *szExt = NULL; - TCHAR tszFilename[MAX_PATH]; - HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD dwFileSizeHigh = 0, dwFileSize = 0, sizeLimit = 0; - - tszFilename[0] = 0; - - ace->hbmPic = 0; - ace->dwFlags = 0; - ace->bmHeight = 0; - ace->bmWidth = 0; - ace->lpDIBSection = NULL; - ace->szFilename[0] = 0; - - if (szProto == NULL) { - char *proto = GetContactProto(hContact); - if (proto == NULL || !db_get_b(NULL, AVS_MODULE, proto, 1)) - return -1; - - if (db_get_b(hContact, "ContactPhoto", "Locked", 0) - && !db_get_ts(hContact, "ContactPhoto", "Backup", &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else if ( !db_get_ts(hContact, "ContactPhoto", "RFile", &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else if ( !db_get_ts(hContact, "ContactPhoto", "File", &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else return -2; - } - else { - if (hContact == 0) { // create a protocol picture in the proto picture cache - if ( !db_get_ts(NULL, PPICT_MODULE, szProto, &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else { - if (lstrcmpA(szProto, AVS_DEFAULT)) { - if ( !db_get_ts(NULL, PPICT_MODULE, AVS_DEFAULT, &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - - if (!strstr(szProto, "Global avatar for")) { - PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)szProto); - if (pdescr == NULL) - return -1; - char key[MAX_PATH]; - mir_snprintf(key, SIZEOF(key), "Global avatar for %s accounts", pdescr->szProtoName); - if ( !db_get_ts(NULL, PPICT_MODULE, key, &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - } - } - } - } - else if (hContact == (HANDLE)-1) { // create own picture - note, own avatars are not on demand, they are loaded once at - // startup and everytime they are changed. - if (szProto[0] == '\0') { - // Global avatar - if ( db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) - return -10; - - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else if ( ProtoServiceExists(szProto, PS_GETMYAVATART)) { - if (CallProtoService(szProto, PS_GETMYAVATART, (WPARAM)tszFilename, (LPARAM)MAX_PATH)) - tszFilename[0] = '\0'; - } - else if ( ProtoServiceExists(szProto, PS_GETMYAVATAR)) { - char szFileName[ MAX_PATH ]; - if (CallProtoService(szProto, PS_GETMYAVATAR, (WPARAM)szFileName, (LPARAM)MAX_PATH)) - tszFilename[0] = '\0'; - else - MultiByteToWideChar( CP_ACP, 0, szFileName, -1, tszFilename, SIZEOF(tszFilename)); - } - else if ( !db_get_ts(NULL, szProto, "AvatarFile", &dbv)) { - AVS_pathToAbsolute(dbv.ptszVal, tszFilename); - db_free(&dbv); - } - else return -1; - } - } - - if ( lstrlen(tszFilename) < 4) - return -1; - - _tcsncpy_s(tszFilename, SIZEOF(tszFilename), VARST(tszFilename), _TRUNCATE); - if ((hFile = CreateFile(tszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) - return -2; - - CloseHandle(hFile); - WPARAM isTransparentImage = 0; - - ace->hbmPic = (HBITMAP) BmpFilterLoadBitmap32((WPARAM)&isTransparentImage, (LPARAM)tszFilename); - ace->dwFlags = 0; - ace->bmHeight = 0; - ace->bmWidth = 0; - ace->lpDIBSection = NULL; - _tcsncpy(ace->szFilename, tszFilename, MAX_PATH); - ace->szFilename[MAX_PATH - 1] = 0; - if (ace->hbmPic != 0) { - BITMAP bminfo; - - GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); - - ace->cbSize = sizeof(avatarCacheEntry); - ace->dwFlags = AVS_BITMAP_VALID; - if (hContact != NULL && db_get_b(hContact, "ContactPhoto", "Hidden", 0)) - ace->dwFlags |= AVS_HIDEONCLIST; - ace->hContact = hContact; - ace->bmHeight = bminfo.bmHeight; - ace->bmWidth = bminfo.bmWidth; - - BOOL noTransparency = db_get_b(0, AVS_MODULE, "RemoveAllTransparency", 0); - - // Calc image hash - if (hContact != 0 && hContact != (HANDLE)-1) { - // Have to reset settings? -> do it if image changed - DWORD imgHash = GetImgHash(ace->hbmPic); - if (imgHash != db_get_dw(hContact, "ContactPhoto", "ImageHash", 0)) { - ResetTranspSettings(hContact); - db_set_dw(hContact, "ContactPhoto", "ImageHash", imgHash); - } - - // Make transparent? - if (!noTransparency && !isTransparentImage - && db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", - db_get_b(0, AVS_MODULE, "MakeTransparentBkg", 0))) - { - if (MakeTransparentBkg(hContact, &ace->hbmPic)) { - ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; - GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); - isTransparentImage = TRUE; - } - } - } - else if (hContact == (HANDLE)-1) { // My avatars - if (!noTransparency && !isTransparentImage - && db_get_b(0, AVS_MODULE, "MakeTransparentBkg", 0) - && db_get_b(0, AVS_MODULE, "MakeMyAvatarsTransparent", 0)) - { - if (MakeTransparentBkg(0, &ace->hbmPic)) { - ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; - GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); - isTransparentImage = TRUE; - } - } - } - - if (db_get_b(0, AVS_MODULE, "MakeGrayscale", 0)) - ace->hbmPic = MakeGrayscale(hContact, ace->hbmPic); - - if (noTransparency) { - fei->FI_CorrectBitmap32Alpha(ace->hbmPic, TRUE); - isTransparentImage = FALSE; - } - - if (bminfo.bmBitsPixel == 32 && isTransparentImage) { - if (fei->FI_Premultiply(ace->hbmPic)) - ace->dwFlags |= AVS_HASTRANSPARENCY; - - ace->dwFlags |= AVS_PREMULTIPLIED; - } - - if (szProto) { - protoPicCacheEntry *pAce = (protoPicCacheEntry *)ace; - if (hContact == 0) - pAce->dwFlags |= AVS_PROTOPIC; - else if (hContact == (HANDLE)-1) - pAce->dwFlags |= AVS_OWNAVATAR; - } - - return 1; - } - return -1; -} - -/* - * link a new cache block with the already existing chain of blocks - */ - -static struct CacheNode *AddToList(struct CacheNode *node) -{ - struct CacheNode *pCurrent = g_Cache; - - while(pCurrent->pNextNode != 0) - pCurrent = pCurrent->pNextNode; - - pCurrent->pNextNode = node; - return pCurrent; -} - -struct CacheNode *FindAvatarInCache(HANDLE hContact, BOOL add, BOOL findAny = FALSE) -{ - if (g_shutDown) - return NULL; - - char *szProto = GetContactProto(hContact); - if (szProto == NULL || !db_get_b(NULL, AVS_MODULE, szProto, 1)) - return NULL; - - mir_cslock lck(cachecs); - - struct CacheNode *ρρ = g_Cache, *foundNode = NULL; - while(ρρ) { - if (ρρ->ace.hContact == hContact) { - ρρ->ace.t_lastAccess = time(NULL); - foundNode = ρρ->loaded || findAny ? ρρ : NULL; - return foundNode; - } - - // found an empty and usable node - if (foundNode == NULL && ρρ->ace.hContact == 0) - foundNode = ρρ; - - ρρ = ρρ->pNextNode; - } - - // not found - if (!add) - return NULL; - - if (foundNode == NULL) { // no free entry found, create a new and append it to the list - mir_cslock all(alloccs); // protect memory block allocation - struct CacheNode *newNode = AllocCacheBlock(); - AddToList(newNode); - foundNode = newNode; - } - - foundNode->ace.hContact = hContact; - if (g_MetaAvail) - foundNode->dwFlags |= (db_get_b(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0); - foundNode->loaded = FALSE; - foundNode->mustLoad = 1; // pic loader will watch this and load images - SetEvent(hLoaderEvent); // wake him up - return NULL; -} - -#define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */ -#define TOPBIT (1 << (WIDTH - 1)) /* MSB */ -#define WIDTH 32 - -static int GetFileHash(TCHAR* filename) -{ - HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) - return 0; - - int remainder = 0; - char data[1024]; - DWORD dwRead; - do - { - // Read file chunk - dwRead = 0; - ReadFile(hFile, data, 1024, &dwRead, NULL); - - /* loop through each byte of data */ - for (int byte = 0; byte < (int) dwRead; ++byte) { - /* store the next byte into the remainder */ - remainder ^= (data[byte] << (WIDTH - 8)); - /* calculate for all 8 bits in the byte */ - for (int bit = 8; bit > 0; --bit) { - /* check if MSB of remainder is a one */ - if (remainder & TOPBIT) - remainder = (remainder << 1) ^ POLYNOMIAL; - else - remainder = (remainder << 1); - } - } - } - while(dwRead == 1024); - - CloseHandle(hFile); - - return remainder; -} - -static int ProtocolAck(WPARAM wParam, LPARAM lParam) -{ - ACKDATA *ack = (ACKDATA *) lParam; - - if (ack != NULL && ack->type == ACKTYPE_AVATAR && ack->hContact != 0 && (!g_MetaAvail || strcmp(ack->szModule, g_szMetaName))) { - if (ack->result == ACKRESULT_SUCCESS) { - if (ack->hProcess == NULL) - ProcessAvatarInfo(ack->hContact, GAIR_NOAVATAR, NULL, ack->szModule); - else - ProcessAvatarInfo(ack->hContact, GAIR_SUCCESS, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule); - } - else if (ack->result == ACKRESULT_FAILED) { - ProcessAvatarInfo(ack->hContact, GAIR_FAILED, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule); - } - else if (ack->result == ACKRESULT_STATUS) { - char *szProto = GetContactProto(ack->hContact); - if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto)) { - // Queue - db_set_b(ack->hContact, "ContactPhoto", "NeedUpdate", 1); - QueueAdd(ack->hContact); - } - else { - // Fetch it now - FetchAvatarFor(ack->hContact, szProto); - } - } - } - return 0; -} - -INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE)wParam; - BYTE was_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); - - if (fei == NULL || was_locked == (BYTE)lParam) // no need for redundant lockings... - return 0; - - if (hContact) { - if (!was_locked) - MakePathRelative(hContact); - db_set_b(hContact, "ContactPhoto", "Locked", lParam ? 1 : 0); - if (lParam == 0) - MakePathRelative(hContact); - ChangeAvatar(hContact, TRUE); - } - return 0; -} - -/* - * for subclassing the open file dialog... - */ -struct OpenFileSubclassData { - BYTE *locking_request; - BYTE setView; -}; - -static BOOL CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch(msg) { - case WM_INITDIALOG: - { - OPENFILENAME *ofn = (OPENFILENAME *)lParam; - - data = (OpenFileSubclassData *) malloc(sizeof(OpenFileSubclassData)); - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data); - data->locking_request = (BYTE *)ofn->lCustData; - data->setView = TRUE; - - TranslateDialogDefault(hwnd); - CheckDlgButton(hwnd, IDC_PROTECTAVATAR, *(data->locking_request)); - } - break; - - case WM_COMMAND: - if (LOWORD(wParam) == IDC_PROTECTAVATAR) - *(data->locking_request) = IsDlgButtonChecked(hwnd, IDC_PROTECTAVATAR) ? TRUE : FALSE; - break; - - case WM_NOTIFY: - if (data->setView) { - HWND hwndParent = GetParent(hwnd); - HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ; - if (hwndLv != NULL) { - SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0); - data->setView = FALSE; - } - } - break; - - case WM_NCDESTROY: - free((OpenFileSubclassData *)data); - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); - break; - } - - return FALSE; -} - -/* - * set an avatar (service function) - * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL - * image filename (will be checked for existance, though) - */ - -INT_PTR avSetAvatar(HANDLE hContact, TCHAR* tszPath) -{ - BYTE is_locked = 0; - TCHAR FileName[MAX_PATH], szBackupName[MAX_PATH]; - TCHAR *szFinalName = NULL; - HANDLE hFile = 0; - BYTE locking_request; - - if (hContact == NULL || fei == NULL) - return 0; - - is_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); - - if ( tszPath == NULL ) { - OPENFILENAME ofn = {0}; - TCHAR filter[256]; - - filter[0] = '\0'; - CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF(filter), ( LPARAM )filter); - - if (IsWinVer2000Plus()) - ofn.lStructSize = sizeof(ofn); - else - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = 0; - ofn.lpstrFile = FileName; - ofn.lpstrFilter = filter; - ofn.nMaxFile = MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK; - ofn.lpstrInitialDir = _T("."); - *FileName = '\0'; - ofn.lpstrDefExt = _T(""); - ofn.hInstance = g_hInst; - ofn.lpTemplateName = MAKEINTRESOURCE(IDD_OPENSUBCLASS); - ofn.lpfnHook = (LPOFNHOOKPROC)OpenFileSubclass; - locking_request = is_locked; - ofn.lCustData = (LPARAM)&locking_request; - if (GetOpenFileName(&ofn)) { - szFinalName = FileName; - is_locked = locking_request ? 1 : is_locked; - } - else - return 0; - } - else - szFinalName = tszPath; - - /* - * filename is now set, check it and perform all needed action - */ - - if ((hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) - return 0; - - // file exists... - - CloseHandle(hFile); - - AVS_pathToRelative(szFinalName, szBackupName); - db_set_ts(hContact, "ContactPhoto", "Backup", szBackupName); - - db_set_b(hContact, "ContactPhoto", "Locked", is_locked); - db_set_ts(hContact, "ContactPhoto", "File", szFinalName); - MakePathRelative(hContact, szFinalName); - // Fix cache - ChangeAvatar(hContact, TRUE); - - return 0; -} - -INT_PTR SetAvatar(WPARAM wParam, LPARAM lParam) -{ - return avSetAvatar(( HANDLE )wParam, _A2T(( const char* )lParam )); -} - -INT_PTR SetAvatarW(WPARAM wParam, LPARAM lParam) -{ - return avSetAvatar(( HANDLE )wParam, ( TCHAR* )lParam ); -} - -/* - * see if is possible to set the avatar for the expecified protocol - */ -static INT_PTR CanSetMyAvatar(WPARAM wParam, LPARAM lParam) -{ - char *protocol = (char *) wParam; - if (protocol == NULL || fei == NULL) - return 0; - - return ProtoServiceExists(protocol, PS_SETMYAVATAR); -} - -struct SetMyAvatarHookData { - char *protocol; - BOOL square; - BOOL grow; - - BOOL thumbnail; -}; - -/* - * Callback to set thumbnaill view to open dialog - */ -static UINT_PTR CALLBACK SetMyAvatarHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch(msg) { - case WM_INITDIALOG: - { - InterlockedExchange(&hwndSetMyAvatar, (LONG) hwnd); - - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)lParam); - OPENFILENAME *ofn = (OPENFILENAME *)lParam; - SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; - data->thumbnail = TRUE; - - SetWindowText(GetDlgItem(hwnd, IDC_MAKE_SQUARE), TranslateT("Make the avatar square")); - SetWindowText(GetDlgItem(hwnd, IDC_GROW), TranslateT("Grow avatar to fit max allowed protocol size")); - - CheckDlgButton(hwnd, IDC_MAKE_SQUARE, data->square ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwnd, IDC_GROW, data->grow ? BST_CHECKED : BST_UNCHECKED); - - if (data->protocol != NULL && (Proto_AvatarImageProportion(data->protocol) & PIP_SQUARE)) - EnableWindow(GetDlgItem(hwnd, IDC_MAKE_SQUARE), FALSE); - } - break; - - case WM_NOTIFY: - { - OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; - if (data->thumbnail) - { - HWND hwndParent = GetParent(hwnd); - HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ; - if (hwndLv != NULL) - { - SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0); - data->thumbnail = FALSE; - } - } - break; - } - case WM_DESTROY: - { - OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; - data->square = IsDlgButtonChecked(hwnd, IDC_MAKE_SQUARE); - data->grow = IsDlgButtonChecked(hwnd, IDC_GROW); - - InterlockedExchange(&hwndSetMyAvatar, 0); - break; - } - } - - return 0; -} - -const TCHAR *GetFormatExtension(int format) -{ - if (format == PA_FORMAT_PNG) - return _T(".png"); - if (format == PA_FORMAT_JPEG) - return _T(".jpg"); - if (format == PA_FORMAT_ICON) - return _T(".ico"); - if (format == PA_FORMAT_BMP) - return _T(".bmp"); - if (format == PA_FORMAT_GIF) - return _T(".gif"); - if (format == PA_FORMAT_SWF) - return _T(".swf"); - if (format == PA_FORMAT_XML) - return _T(".xml"); - - return NULL; -} - -int GetImageFormat(TCHAR *filename) -{ - size_t len = lstrlen(filename); - - if (len < 5) - return PA_FORMAT_UNKNOWN; - - if (_tcsicmp(_T(".png"), &filename[len-4]) == 0) - return PA_FORMAT_PNG; - - if (_tcsicmp(_T(".jpg"), &filename[len-4]) == 0 || _tcsicmp(_T(".jpeg"), &filename[len-4]) == 0) - return PA_FORMAT_JPEG; - - if (_tcsicmp(_T(".ico"), &filename[len-4]) == 0) - return PA_FORMAT_ICON; - - if (_tcsicmp(_T(".bmp"), &filename[len-4]) == 0 || _tcsicmp(_T(".rle"), &filename[len-4]) == 0) - return PA_FORMAT_BMP; - - if (_tcsicmp(_T(".gif"), &filename[len-4]) == 0) - return PA_FORMAT_GIF; - - if (_tcsicmp(_T(".swf"), &filename[len-4]) == 0) - return PA_FORMAT_SWF; - - if (_tcsicmp(_T(".xml"), &filename[len-4]) == 0) - return PA_FORMAT_XML; - - return PA_FORMAT_UNKNOWN; -} - -static void FilterGetStrings(TCHAR *filter, int bytesLeft, BOOL xml, BOOL swf) -{ - TCHAR *pfilter; - int wParam = bytesLeft; - - lstrcpyn(filter, TranslateT("All Files"), bytesLeft); bytesLeft-=lstrlen(filter); - _tcsncat(filter, _T(" (*.bmp;*.jpg;*.gif;*.png"), bytesLeft); - if (swf) _tcscat(filter, _T(";*.swf")); - if (xml) _tcscat(filter, _T(";*.xml")); - _tcscat(filter, _T(")")); - pfilter=filter+lstrlen(filter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"), bytesLeft); - if (swf) _tcscat(pfilter, _T(";*.SWF")); - if (xml) _tcscat(pfilter, _T(";*.XML")); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - - lstrcpyn(pfilter, TranslateT("Windows Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.bmp;*.rle)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.BMP;*.RLE"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - - lstrcpyn(pfilter,TranslateT("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.jpg;*.jpeg)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.JPG;*.JPEG"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - - lstrcpyn(pfilter,TranslateT("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.gif)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.GIF"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - - lstrcpyn(pfilter,TranslateT("PNG Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.png)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.PNG"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - - if (swf) - { - lstrcpyn(pfilter,TranslateT("Flash Animations"), bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.swf)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.SWF"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - } - - if (xml) - { - lstrcpyn(pfilter, TranslateT("XML Files"), bytesLeft); bytesLeft-=lstrlen(pfilter); - _tcsncat(pfilter, _T(" (*.xml)"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - lstrcpyn(pfilter, _T("*.XML"), bytesLeft); - pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); - } - - if (bytesLeft) *pfilter='\0'; -} - -static void DeleteGlobalUserAvatar() -{ - DBVARIANT dbv = {0}; - if (db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) - return; - - TCHAR szFilename[MAX_PATH]; - AVS_pathToAbsolute(dbv.ptszVal, szFilename); - db_free(&dbv); - - DeleteFile(szFilename); - db_unset(NULL, AVS_MODULE, "GlobalUserAvatarFile"); -} - -static void SetIgnoreNotify(char *protocol, BOOL ignore) -{ - for(int i = 0; i < g_MyAvatars.getCount(); i++) { - if (protocol == NULL || !lstrcmpA(g_MyAvatars[i].szProtoname, protocol)) { - if (ignore) - g_MyAvatars[i].dwFlags |= AVS_IGNORENOTIFY; - else - g_MyAvatars[i].dwFlags &= ~AVS_IGNORENOTIFY; - } - } -} - -static int InternalRemoveMyAvatar(char *protocol) -{ - SetIgnoreNotify(protocol, TRUE); - - // Remove avatar - int ret = 0; - if (protocol != NULL) - { - if ( ProtoServiceExists(protocol, PS_SETMYAVATAR)) - ret = SaveAvatar(protocol, NULL); - else - ret = -3; - - if (ret == 0) - { - // Has global avatar? - DBVARIANT dbv = {0}; - if ( !db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) { - db_free(&dbv); - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); - DeleteGlobalUserAvatar(); - } - } - } - else - { - PROTOACCOUNT **accs; - int i,count; - - ProtoEnumAccounts( &count, &accs ); - for (i = 0; i < count; i++) - { - if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) - continue; - - if (!Proto_IsAvatarsEnabled( accs[i]->szModuleName )) - continue; - - // Found a protocol - int retTmp = SaveAvatar( accs[i]->szModuleName, NULL); - if (retTmp != 0) - ret = retTmp; - } - - DeleteGlobalUserAvatar(); - - if (ret) - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); - else - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0); - } - - SetIgnoreNotify(protocol, FALSE); - - ReportMyAvatarChanged((WPARAM)(( protocol == NULL ) ? "" : protocol ), 0); - return ret; -} - -static int InternalSetMyAvatar(char *protocol, TCHAR *szFinalName, SetMyAvatarHookData &data, BOOL allAcceptXML, BOOL allAcceptSWF) -{ - HANDLE hFile = 0; - - int format = GetImageFormat(szFinalName); - if (format == PA_FORMAT_UNKNOWN || (hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) - return -3; - - CloseHandle(hFile); - - // file exists... - - HBITMAP hBmp = NULL; - - if (format == PA_FORMAT_SWF) - { - if (!allAcceptSWF) - return -4; - } - else if (format == PA_FORMAT_XML) - { - if (!allAcceptXML) - return -4; - } - else - { - // Try to open if is not a flash or XML - hBmp = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFinalName, IMGL_TCHAR); - if (hBmp == NULL) - return -4; - } - - SetIgnoreNotify(protocol, TRUE); - - int ret = 0; - if (protocol != NULL) - { - ret = SetProtoMyAvatar(protocol, hBmp, szFinalName, format, data.square, data.grow); - - if (ret == 0) - { - DeleteGlobalUserAvatar(); - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); - } - } - else - { - PROTOACCOUNT **accs; - int i,count; - - ProtoEnumAccounts( &count, &accs ); - for (i = 0; i < count; i++) - { - if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) - continue; - - if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName )) - continue; - - int retTmp = SetProtoMyAvatar( accs[i]->szModuleName, hBmp, szFinalName, format, data.square, data.grow); - if (retTmp != 0) - ret = retTmp; - } - - DeleteGlobalUserAvatar(); - - if (ret) - { - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); - } - else - { - // Copy avatar file to store as global one - TCHAR globalFile[1024]; - BOOL saved = TRUE; - if (FoldersGetCustomPathT(hGlobalAvatarFolder, globalFile, SIZEOF(globalFile), _T(""))) - { - mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\%s"), g_szDataPath, _T("GlobalAvatar")); - CreateDirectory(globalFile, NULL); - } - - TCHAR *ext = _tcsrchr(szFinalName, _T('.')); // Can't be NULL here - if (format == PA_FORMAT_XML || format == PA_FORMAT_SWF) - { - mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext); - CopyFile(szFinalName, globalFile, FALSE); - } - else - { - // Resize (to avoid too big avatars) - ResizeBitmap rb = {0}; - rb.size = sizeof(ResizeBitmap); - rb.hBmp = hBmp; - rb.max_height = 300; - rb.max_width = 300; - rb.fit = (data.grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW) - | (data.square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS); - - HBITMAP hBmpTmp = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0); - - // Check if need to resize - if (hBmpTmp == hBmp || hBmpTmp == NULL) - { - // Use original image - mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext); - CopyFile(szFinalName, globalFile, FALSE); - } - else - { - // Save as PNG - mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar.png"), globalFile); - if (BmpFilterSaveBitmap((WPARAM) hBmpTmp, (LPARAM) globalFile)) - saved = FALSE; - - DeleteObject(hBmpTmp); - } - } - - if (saved) - { - TCHAR relFile[1024]; - if (AVS_pathToRelative(globalFile, relFile)) - db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", relFile); - else - db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", globalFile); - - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0); - } - else - { - db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); - } - } - } - - DeleteObject(hBmp); - - SetIgnoreNotify(protocol, FALSE); - - ReportMyAvatarChanged((WPARAM)(( protocol == NULL ) ? "" : protocol ), 0); - return ret; -} - -/* - * set an avatar for a protocol (service function) - * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL - * image filename (will be checked for existance, though) - */ - -INT_PTR avSetMyAvatar( char* protocol, TCHAR* tszPath ) -{ - TCHAR FileName[MAX_PATH]; - TCHAR *szFinalName = NULL; - BOOL allAcceptXML; - BOOL allAcceptSWF; - - // Protocol allow seting of avatar? - if (protocol != NULL && !CanSetMyAvatar((WPARAM) protocol, 0)) - return -1; - - if (tszPath == NULL && hwndSetMyAvatar != 0) - { - SetForegroundWindow((HWND) hwndSetMyAvatar); - SetFocus((HWND) hwndSetMyAvatar); - ShowWindow((HWND) hwndSetMyAvatar, SW_SHOW); - return -2; - } - - SetMyAvatarHookData data = { 0 }; - - // Check for XML and SWF - if (protocol == NULL) - { - allAcceptXML = TRUE; - allAcceptSWF = TRUE; - - PROTOACCOUNT **accs; - int i,count; - - ProtoEnumAccounts( &count, &accs ); - for (i = 0; i < count; i++) - { - if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) - continue; - - if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName )) - continue; - - allAcceptXML = allAcceptXML && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_XML); - allAcceptSWF = allAcceptSWF && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_SWF); - } - - data.square = db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0); - } - else - { - allAcceptXML = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML); - allAcceptSWF = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF); - - data.protocol = protocol; - data.square = (Proto_AvatarImageProportion(protocol) & PIP_SQUARE) - || db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0); - } - - if (tszPath == NULL) { - OPENFILENAME ofn = {0}; - TCHAR filter[512]; - TCHAR inipath[1024]; - - data.protocol = protocol; - - filter[0] = '\0'; - FilterGetStrings(filter, SIZEOF(filter), allAcceptXML, allAcceptSWF); - - FoldersGetCustomPathT(hMyAvatarsFolder, inipath, SIZEOF(inipath), _T(".")); - - if (IsWinVer2000Plus()) - ofn.lStructSize = sizeof(ofn); - else - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = 0; - ofn.lpstrFile = FileName; - ofn.lpstrFilter = filter; - ofn.nMaxFile = MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK; - ofn.lpstrInitialDir = inipath; - ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SET_OWN_SUBCLASS); - ofn.lpfnHook = SetMyAvatarHookProc; - ofn.lCustData = (LPARAM) &data; - - *FileName = '\0'; - ofn.lpstrDefExt = _T(""); - ofn.hInstance = g_hInst; - - TCHAR title[256]; - if (protocol == NULL) - mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar")); - else - { - TCHAR* prototmp = mir_a2t(protocol); - mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar for %s"), prototmp); - mir_free(prototmp); - } - ofn.lpstrTitle = title; - - if (GetOpenFileName(&ofn)) - szFinalName = FileName; - else - return 1; - } - else - szFinalName = (TCHAR *)tszPath; - - /* - * filename is now set, check it and perform all needed action - */ - - if (szFinalName[0] == '\0') - return InternalRemoveMyAvatar(protocol); - - return InternalSetMyAvatar(protocol, szFinalName, data, allAcceptXML, allAcceptSWF); -} - -static INT_PTR SetMyAvatar( WPARAM wParam, LPARAM lParam ) -{ - return avSetMyAvatar(( char* )wParam, _A2T(( const char* )lParam )); -} - -static INT_PTR SetMyAvatarW( WPARAM wParam, LPARAM lParam ) -{ - return avSetMyAvatar(( char* )wParam, ( TCHAR* )lParam ); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -static DWORD GetFileSize(TCHAR *szFilename) -{ - HANDLE hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) - return 0; - - DWORD low = GetFileSize(hFile, NULL); - - CloseHandle(hFile); - - if (low == INVALID_FILE_SIZE) - return 0; - - return low; -} - -struct SaveProtocolData { - DWORD max_size; - TCHAR image_file_name[MAX_PATH]; - BOOL saved; - BOOL need_smaller_size; - int width; - int height; - TCHAR temp_file[MAX_PATH]; - HBITMAP hBmpProto; -}; - -static void SaveImage(SaveProtocolData &d, char *protocol, int format) -{ - if (Proto_IsAvatarFormatSupported(protocol, format)) - { - mir_sntprintf(d.image_file_name, SIZEOF(d.image_file_name), _T("%s%s"), d.temp_file, GetFormatExtension(format)); - if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, format == PA_FORMAT_JPEG ? JPEG_QUALITYSUPERB : 0)) - { - if (d.max_size != 0 && GetFileSize(d.image_file_name) > d.max_size) - { - DeleteFile(d.image_file_name); - - if (format == PA_FORMAT_JPEG) - { - // Try with lower quality - if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, JPEG_QUALITYGOOD)) - { - if (GetFileSize(d.image_file_name) > d.max_size) - { - DeleteFile(d.image_file_name); - d.need_smaller_size = TRUE; - } - else - d.saved = TRUE; - } - } - else - d.need_smaller_size = TRUE; - } - else - d.saved = TRUE; - } - } -} - -static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int originalFormat, BOOL square, BOOL grow) -{ - if (!ProtoServiceExists(protocol, PS_SETMYAVATAR)) - return -1; - - // If is swf or xml, just set it - - if (originalFormat == PA_FORMAT_SWF) - { - if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF)) - return -1; - - return SaveAvatar(protocol, originalFilename); - } - - if (originalFormat == PA_FORMAT_XML) - { - if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML)) - return -1; - - return SaveAvatar(protocol, originalFilename); - } - - // Get protocol info - SaveProtocolData d = {0}; - - d.max_size = (DWORD) Proto_GetAvatarMaxFileSize(protocol); - - Proto_GetAvatarMaxSize(protocol, &d.width, &d.height); - int orig_width = d.width; - int orig_height = d.height; - - if (Proto_AvatarImageProportion(protocol) & PIP_SQUARE) - square = TRUE; - - // Try to save until a valid image is found or we give up - int num_tries = 0; - do { - // Lets do it - ResizeBitmap rb; - rb.size = sizeof(ResizeBitmap); - rb.hBmp = hBmp; - rb.max_height = d.height; - rb.max_width = d.width; - rb.fit = (grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW) - | (square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS); - - d.hBmpProto = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0); - - if (d.hBmpProto == NULL) - { - if (d.temp_file[0] != '\0') - DeleteFile(d.temp_file); - return -1; - } - - // Check if can use original image - if (d.hBmpProto == hBmp - && Proto_IsAvatarFormatSupported(protocol, originalFormat) - && (d.max_size == 0 || GetFileSize(originalFilename) < d.max_size)) - { - if (d.temp_file[0] != '\0') - DeleteFile(d.temp_file); - - // Use original image - return SaveAvatar(protocol, originalFilename); - } - - // Create a temporary file (if was not created already) - if (d.temp_file[0] == '\0') - { - d.temp_file[0] = '\0'; - if (GetTempPath(MAX_PATH, d.temp_file) == 0 - || GetTempFileName(d.temp_file, _T("mir_av_"), 0, d.temp_file) == 0) - { - DeleteObject(d.hBmpProto); - return -1; - } - } - - // Which format? - - // First try to use original format - if (originalFormat != PA_FORMAT_BMP) - SaveImage(d, protocol, originalFormat); - - if (!d.saved && originalFormat != PA_FORMAT_PNG) - SaveImage(d, protocol, PA_FORMAT_PNG); - - if (!d.saved && originalFormat != PA_FORMAT_JPEG) - SaveImage(d, protocol, PA_FORMAT_JPEG); - - if (!d.saved && originalFormat != PA_FORMAT_GIF) - SaveImage(d, protocol, PA_FORMAT_GIF); - - if (!d.saved) - SaveImage(d, protocol, PA_FORMAT_BMP); - - num_tries++; - if (!d.saved && d.need_smaller_size && num_tries < 4) - { - // Cleanup - if (d.hBmpProto != hBmp) - DeleteObject(d.hBmpProto); - - // use a smaller size - d.width = orig_width * (4 - num_tries) / 4; - d.height = orig_height * (4 - num_tries) / 4; - } - - } while(!d.saved && d.need_smaller_size && num_tries < 4); - - int ret; - - if (d.saved) - { - // Call proto service - ret = SaveAvatar(protocol, d.image_file_name); - DeleteFile(d.image_file_name); - } - else - { - ret = -1; - } - - if (d.temp_file[0] != '\0') - DeleteFile(d.temp_file); - - if (d.hBmpProto != hBmp) - DeleteObject(d.hBmpProto); - - return ret; -} - -static INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam) -{ - if (wParam) - CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_AVATAROPTIONS), 0, DlgProcAvatarOptions, (LPARAM)wParam); - return 0; -} - -INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam) -{ - if (wParam || g_shutDown || fei == NULL) - return 0; - - char *szProto = (char *)lParam; - if (lParam == 0 || IsBadReadPtr(szProto, 4)) - return 0; - - for(int i = 0; i < g_MyAvatars.getCount(); i++) - if (!lstrcmpA(szProto, g_MyAvatars[i].szProtoname) && g_MyAvatars[i].hbmPic != 0) - return (INT_PTR)&g_MyAvatars[i]; - - return 0; -} - -static protoPicCacheEntry *GetProtoDefaultAvatar(HANDLE hContact) -{ - char *szProto = GetContactProto(hContact); - if (szProto) { - for(int i = 0; i < g_ProtoPictures.getCount(); i++) { - protoPicCacheEntry& p = g_ProtoPictures[i]; - if ( !lstrcmpA(p.szProtoname, szProto) && p.hbmPic != NULL) - return &g_ProtoPictures[i]; - } - } - return NULL; + return -1; + if ((lstrcmpA(p2->szProtoname, "Global avatar") == 0) || strstr(p1->szProtoname, "Global avatar")) + return 1; + return lstrcmpA( p1->szProtoname, p2->szProtoname ); } -HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked = -1) -{ - if (g_MetaAvail && db_get_b(NULL, g_szMetaName, "Enabled", 0)) { - if (db_get_dw(hContact, g_szMetaName, "NumContacts", 0) >= 1) { - if (locked == -1) - locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); +OBJLIST + g_ProtoPictures( 10, ComparePicture ), + g_MyAvatars( 10, ComparePicture ); - if (!locked) - hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); - } - } - return hContact; -} +char* g_szMetaName = NULL; -INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam) -{ - if (wParam == 0 || g_shutDown || fei == NULL) - return 0; +// Stores the id of the dialog - HANDLE hContact = (HANDLE) wParam; - hContact = GetContactThatHaveTheAvatar(hContact); +int OnDetailsInit(WPARAM wParam, LPARAM lParam); +int OptInit(WPARAM wParam, LPARAM lParam); - // Get the node - struct CacheNode *node = FindAvatarInCache(hContact, TRUE); - if (node == NULL || !node->loaded) - return (INT_PTR) GetProtoDefaultAvatar(hContact); - else - return (INT_PTR) &node->ace; -} +FI_INTERFACE *fei = 0; -// Just delete an avatar from cache -// An cache entry is never deleted. What is deleted is the image handle inside it -// This is done this way to keep track of which avatars avs have to keep track -void DeleteAvatarFromCache(HANDLE hContact, BOOL forever) -{ - hContact = GetContactThatHaveTheAvatar(hContact); - - struct CacheNode *node = FindAvatarInCache(hContact, FALSE); - if (node == NULL) { - struct CacheNode temp_node = {0}; - if (g_MetaAvail) - temp_node.dwFlags |= (db_get_b(hContact, g_szMetaName, "IsSubcontact", 0) ? MC_ISSUBCONTACT : 0); - NotifyMetaAware(hContact, &temp_node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact)); - return; - } - node->mustLoad = -1; // mark for deletion - if (forever) - node->dwFlags |= AVS_DELETENODEFOREVER; - SetEvent(hLoaderEvent); -} +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __AUTHOREMAIL, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {E00F1643-263C-4599-B84B-053E5C511D29} + {0xe00f1643, 0x263c, 0x4599, {0xb8, 0x4b, 0x5, 0x3e, 0x5c, 0x51, 0x1d, 0x29}} +}; -int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist, int pa_format) +static TCHAR* getJGMailID(char *szProto) { - if (g_shutDown) - return 0; - - hContact = GetContactThatHaveTheAvatar(hContact); + static TCHAR szJID[MAX_PATH+1]; szJID[0] = '\0'; - // Get the node - struct CacheNode *node = FindAvatarInCache(hContact, g_AvatarHistoryAvail && fNotifyHist, TRUE); - if (node == NULL) - return 0; + DBVARIANT dbva, dbvb; + if ( db_get_ts(NULL, szProto, "LoginName", &dbva)) + return szJID; - if (fNotifyHist) - node->dwFlags |= AVH_MUSTNOTIFY; + if ( db_get_ts(NULL, szProto, "LoginServer", &dbvb)) { + db_free(&dbva); + return szJID; + } - node->mustLoad = fLoad ? 1 : -1; - node->pa_format = pa_format; - SetEvent(hLoaderEvent); - return 0; + mir_sntprintf(szJID, SIZEOF(szJID), _T("%s@%s"), dbva.ptszVal, dbvb.ptszVal); + db_free(&dbva); + db_free(&dbvb); + return szJID; } -/* - * this thread scans the cache and handles nodes which have mustLoad set to > 0 (must be loaded/reloaded) or - * nodes where mustLoad is < 0 (must be deleted). - * its waken up by the event and tries to lock the cache only when absolutely necessary. - */ - -static void PicLoader(LPVOID param) +static int ProtocolAck(WPARAM wParam, LPARAM lParam) { - DWORD dwDelay = db_get_dw(NULL, AVS_MODULE, "picloader_sleeptime", 80); - - if (dwDelay < 30) - dwDelay = 30; - else if (dwDelay > 100) - dwDelay = 100; - - while(!g_shutDown) { - struct CacheNode *node = g_Cache; - - while(!g_shutDown && node) { - if (node->mustLoad > 0 && node->ace.hContact) { - node->mustLoad = 0; - AVATARCACHEENTRY ace_temp; - - if (db_get_b(node->ace.hContact, "ContactPhoto", "NeedUpdate", 0)) - QueueAdd(node->ace.hContact); - - CopyMemory(&ace_temp, &node->ace, sizeof(AVATARCACHEENTRY)); - ace_temp.hbmPic = 0; - - int result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL); - - if (result == -2) { - char *szProto = GetContactProto(node->ace.hContact); - if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto)) - QueueAdd(node->ace.hContact); - else if (FetchAvatarFor(node->ace.hContact, szProto) == GAIR_SUCCESS) // Try yo create again - result = CreateAvatarInCache(node->ace.hContact, &ace_temp, NULL); - } - - if (result == 1 && ace_temp.hbmPic != 0) { // Loaded - HBITMAP oldPic = node->ace.hbmPic; - - EnterCriticalSection(&cachecs); - CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY)); - node->loaded = TRUE; - LeaveCriticalSection(&cachecs); - if (oldPic) - DeleteObject(oldPic); - NotifyMetaAware(node->ace.hContact, node); - } - else if (result == 0 || result == -3) { // Has no avatar - HBITMAP oldPic = node->ace.hbmPic; - - EnterCriticalSection(&cachecs); - CopyMemory(&node->ace, &ace_temp, sizeof(AVATARCACHEENTRY)); - node->loaded = FALSE; - node->mustLoad = 0; - LeaveCriticalSection(&cachecs); - if (oldPic) - DeleteObject(oldPic); - NotifyMetaAware(node->ace.hContact, node); - } - - mir_sleep(dwDelay); + ACKDATA *ack = (ACKDATA *) lParam; + + if (ack != NULL && ack->type == ACKTYPE_AVATAR && ack->hContact != 0 && (!g_MetaAvail || strcmp(ack->szModule, g_szMetaName))) { + if (ack->result == ACKRESULT_SUCCESS) { + if (ack->hProcess == NULL) + ProcessAvatarInfo(ack->hContact, GAIR_NOAVATAR, NULL, ack->szModule); + else + ProcessAvatarInfo(ack->hContact, GAIR_SUCCESS, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule); + } + else if (ack->result == ACKRESULT_FAILED) { + ProcessAvatarInfo(ack->hContact, GAIR_FAILED, (PROTO_AVATAR_INFORMATIONT *) ack->hProcess, ack->szModule); + } + else if (ack->result == ACKRESULT_STATUS) { + char *szProto = GetContactProto(ack->hContact); + if (szProto == NULL || Proto_NeedDelaysForAvatars(szProto)) { + // Queue + db_set_b(ack->hContact, "ContactPhoto", "NeedUpdate", 1); + QueueAdd(ack->hContact); } - else if (node->mustLoad < 0 && node->ace.hContact) { // delete this picture - HANDLE hContact = node->ace.hContact; - EnterCriticalSection(&cachecs); - node->mustLoad = 0; - node->loaded = 0; - if (node->ace.hbmPic) - DeleteObject(node->ace.hbmPic); - ZeroMemory(&node->ace, sizeof(AVATARCACHEENTRY)); - if (node->dwFlags & AVS_DELETENODEFOREVER) - node->dwFlags &= ~AVS_DELETENODEFOREVER; - else - node->ace.hContact = hContact; - - LeaveCriticalSection(&cachecs); - NotifyMetaAware(hContact, node, (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hContact)); + else { + // Fetch it now + FetchAvatarFor(ack->hContact, szProto); } - // protect this by changes from the cache block allocator as it can cause inconsistencies while working - // on allocating a new block. - EnterCriticalSection(&alloccs); - node = node->pNextNode; - LeaveCriticalSection(&alloccs); } - WaitForSingleObject(hLoaderEvent, INFINITE); - //_DebugTrace(0, "pic loader awake..."); - ResetEvent(hLoaderEvent); } + return 0; } static int MetaChanged(WPARAM wParam, LPARAM lParam) @@ -1830,7 +139,7 @@ static int MetaChanged(WPARAM wParam, LPARAM lParam) HANDLE hSubContact = GetContactThatHaveTheAvatar(hContact); // Get the node - struct CacheNode *node = FindAvatarInCache(hSubContact, TRUE); + CacheNode *node = FindAvatarInCache(hSubContact, TRUE); if (node == NULL || !node->loaded) { ace = (AVATARCACHEENTRY *)GetProtoDefaultAvatar(hSubContact); QueueAdd(hSubContact); @@ -1852,7 +161,7 @@ static void LoadDefaultInfo() g_ProtoPictures.insert(pce); } -static void LoadProtoInfo(PROTOCOLDESCRIPTOR* proto) +static void LoadProtoInfo(PROTOCOLDESCRIPTOR *proto) { if (proto->type != PROTOTYPE_PROTOCOL || proto->cbSize != sizeof(*proto)) return; @@ -1871,7 +180,7 @@ static void LoadProtoInfo(PROTOCOLDESCRIPTOR* proto) g_ProtoPictures.insert(pce); } -static void LoadAccountInfo( PROTOACCOUNT* acc ) +static void LoadAccountInfo(PROTOACCOUNT *acc) { protoPicCacheEntry *pce = new protoPicCacheEntry; if ( CreateAvatarInCache(0, pce, acc->szModuleName) != 1) @@ -1913,112 +222,6 @@ static int OnAccChanged(WPARAM wParam, LPARAM lParam) return 0; } -static int ModulesLoaded(WPARAM wParam, LPARAM lParam) -{ - int i; - DBVARIANT dbv = {0}; - TCHAR szEventName[100]; - int result = 0; - - mir_sntprintf(szEventName, 100, _T("avs_loaderthread_%d"), GetCurrentThreadId()); - hLoaderEvent = CreateEvent(NULL, TRUE, FALSE, szEventName); - SetThreadPriority( mir_forkthread(PicLoader, 0), THREAD_PRIORITY_IDLE); - - // Folders plugin support - hMyAvatarsFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("My Avatars"), MIRANDA_USERDATAT _T("\\Avatars")); - hGlobalAvatarFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("My Global Avatar Cache"), MIRANDA_USERDATAT _T("\\Avatars")); - - g_AvatarHistoryAvail = ServiceExists(MS_AVATARHISTORY_ENABLED); - - g_MetaAvail = ServiceExists(MS_MC_GETPROTOCOLNAME) ? TRUE : FALSE; - if (g_MetaAvail) { - g_szMetaName = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); - if (g_szMetaName == NULL) - g_MetaAvail = FALSE; - } - - PROTOACCOUNT **accs = NULL; - int accCount; - ProtoEnumAccounts(&accCount, &accs); - - if (fei != NULL) { - LoadDefaultInfo(); - PROTOCOLDESCRIPTOR** proto; - int protoCount; - CallService(MS_PROTO_ENUMPROTOS, (WPARAM)&protoCount, (LPARAM)&proto); - for (i=0; i < protoCount; i++ ) - LoadProtoInfo( proto[i] ); - for (i=0; i < accCount; i++) - LoadAccountInfo( accs[i] ); - } - - // Load global avatar - protoPicCacheEntry *pce = new protoPicCacheEntry; - CreateAvatarInCache((HANDLE)-1, pce, ""); - pce->szProtoname = mir_strdup(""); - g_MyAvatars.insert( pce ); - - HookEvent(ME_PROTO_ACCLISTCHANGED, OnAccChanged); - HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownProc); - HookEvent(ME_USERINFO_INITIALISE, OnDetailsInit); - return 0; -} - -static void ReloadMyAvatar(LPVOID lpParam) -{ - char *szProto = (char *)lpParam; - - mir_sleep(500); - for(int i = 0; !g_shutDown && i < g_MyAvatars.getCount(); i++) { - char *myAvatarProto = g_MyAvatars[i].szProtoname; - - if (szProto[0] == 0) { - // Notify to all possibles - if (lstrcmpA(myAvatarProto, szProto)) { - if (!ProtoServiceExists( myAvatarProto, PS_SETMYAVATAR)) - continue; - if (!Proto_IsAvatarsEnabled( myAvatarProto )) - continue; - } - - } - else if (lstrcmpA(myAvatarProto, szProto)) - continue; - - if (g_MyAvatars[i].hbmPic) - DeleteObject(g_MyAvatars[i].hbmPic); - - if (CreateAvatarInCache((HANDLE)-1, &g_MyAvatars[i], myAvatarProto) != -1) - NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, (LPARAM)&g_MyAvatars[i]); - else - NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, 0); - } - - free(lpParam); -} - -static INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam) -{ - if (wParam == NULL) - return -1; - - char *proto = (char *) wParam; - - for(int i = 0; i < g_MyAvatars.getCount(); i++) { - if (g_MyAvatars[i].dwFlags & AVS_IGNORENOTIFY) - continue; - - if ( !lstrcmpA(g_MyAvatars[i].szProtoname, proto)) { - LPVOID lpParam = (void *)malloc(lstrlenA(g_MyAvatars[i].szProtoname) + 2); - strcpy((char *)lpParam, g_MyAvatars[i].szProtoname); - mir_forkthread(ReloadMyAvatar, lpParam); - return 0; - } - } - - return -2; -} - static int ContactSettingChanged(WPARAM wParam, LPARAM lParam) { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; @@ -2158,59 +361,59 @@ void InternalDrawAvatar(AVATARDRAWREQUEST *r, HBITMAP hbm, LONG bmWidth, LONG bm } } -INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam) +static int ModulesLoaded(WPARAM wParam, LPARAM lParam) { - AVATARDRAWREQUEST *r = (AVATARDRAWREQUEST *)lParam; - AVATARCACHEENTRY *ace = NULL; + int i; + DBVARIANT dbv = {0}; + TCHAR szEventName[100]; + int result = 0; - if (fei == NULL || r == NULL || IsBadReadPtr((void *)r, sizeof(AVATARDRAWREQUEST))) - return 0; + mir_sntprintf(szEventName, 100, _T("avs_loaderthread_%d"), GetCurrentThreadId()); + hLoaderEvent = CreateEvent(NULL, TRUE, FALSE, szEventName); + SetThreadPriority( mir_forkthread(PicLoader, 0), THREAD_PRIORITY_IDLE); - if (r->cbSize != sizeof(AVATARDRAWREQUEST)) - return 0; + // Folders plugin support + hMyAvatarsFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("My Avatars"), MIRANDA_USERDATAT _T("\\Avatars")); + hGlobalAvatarFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("My Global Avatar Cache"), MIRANDA_USERDATAT _T("\\Avatars")); - if (r->dwFlags & AVDRQ_PROTOPICT) { - if (r->szProto == NULL) - return 0; + g_AvatarHistoryAvail = ServiceExists(MS_AVATARHISTORY_ENABLED); - for(int i = 0; i < g_ProtoPictures.getCount(); i++) { - protoPicCacheEntry& p = g_ProtoPictures[i]; - if ( !lstrcmpA(p.szProtoname, r->szProto) && lstrlenA(r->szProto) == lstrlenA(p.szProtoname) && p.hbmPic != 0) { - ace = (AVATARCACHEENTRY *)&g_ProtoPictures[i]; - break; - } - } + g_MetaAvail = ServiceExists(MS_MC_GETPROTOCOLNAME) ? TRUE : FALSE; + if (g_MetaAvail) { + g_szMetaName = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); + if (g_szMetaName == NULL) + g_MetaAvail = FALSE; } - else if (r->dwFlags & AVDRQ_OWNPIC) { - if (r->szProto == NULL) - return 0; - if (r->szProto[0] == '\0' && db_get_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1)) - return -1; + PROTOACCOUNT **accs = NULL; + int accCount; + ProtoEnumAccounts(&accCount, &accs); - ace = (AVATARCACHEENTRY *)GetMyAvatar(0, (LPARAM)r->szProto); + if (fei != NULL) { + LoadDefaultInfo(); + PROTOCOLDESCRIPTOR** proto; + int protoCount; + CallService(MS_PROTO_ENUMPROTOS, (WPARAM)&protoCount, (LPARAM)&proto); + for (i=0; i < protoCount; i++ ) + LoadProtoInfo( proto[i] ); + for (i=0; i < accCount; i++) + LoadAccountInfo( accs[i] ); } - else - ace = (AVATARCACHEENTRY *)GetAvatarBitmap((WPARAM)r->hContact, 0); - - if (ace && (!(r->dwFlags & AVDRQ_RESPECTHIDDEN) || !(ace->dwFlags & AVS_HIDEONCLIST))) { - ace->t_lastAccess = time(NULL); - if (ace->bmHeight == 0 || ace->bmWidth == 0 || ace->hbmPic == 0) - return 0; - - InternalDrawAvatar(r, ace->hbmPic, ace->bmWidth, ace->bmHeight, ace->dwFlags); - return 1; - } + // Load global avatar + protoPicCacheEntry *pce = new protoPicCacheEntry; + CreateAvatarInCache((HANDLE)-1, pce, ""); + pce->szProtoname = mir_strdup(""); + g_MyAvatars.insert(pce); + HookEvent(ME_PROTO_ACCLISTCHANGED, OnAccChanged); + HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownProc); + HookEvent(ME_USERINFO_INITIALISE, OnDetailsInit); return 0; } static int LoadAvatarModule() { - InitializeCriticalSection(&cachecs); - InitializeCriticalSection(&alloccs); - hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL); HookEvent(ME_OPT_INITIALISE, OptInit); @@ -2219,27 +422,12 @@ static int LoadAvatarModule() HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted); HookEvent(ME_PROTO_ACK, ProtocolAck); - CreateServiceFunction(MS_AV_GETAVATARBITMAP, GetAvatarBitmap); - CreateServiceFunction(MS_AV_PROTECTAVATAR, ProtectAvatar); - CreateServiceFunction(MS_AV_SETAVATAR, SetAvatar); - CreateServiceFunction(MS_AV_SETMYAVATAR, SetMyAvatar); - CreateServiceFunction(MS_AV_CANSETMYAVATAR, CanSetMyAvatar); - CreateServiceFunction(MS_AV_CONTACTOPTIONS, ContactOptions); - CreateServiceFunction(MS_AV_DRAWAVATAR, DrawAvatarPicture); - CreateServiceFunction(MS_AV_GETMYAVATAR, GetMyAvatar); - CreateServiceFunction(MS_AV_REPORTMYAVATARCHANGED, ReportMyAvatarChanged); - CreateServiceFunction(MS_AV_LOADBITMAP32, BmpFilterLoadBitmap32); - CreateServiceFunction(MS_AV_SAVEBITMAP, BmpFilterSaveBitmap); - CreateServiceFunction(MS_AV_CANSAVEBITMAP, BmpFilterCanSaveBitmap); - CreateServiceFunction(MS_AV_RESIZEBITMAP, BmpFilterResizeBitmap); - CreateServiceFunction(MS_AV_SETAVATARW, SetAvatarW); - CreateServiceFunction(MS_AV_SETMYAVATARW, SetMyAvatarW); - hEventChanged = CreateHookableEvent(ME_AV_AVATARCHANGED); hEventContactAvatarChanged = CreateHookableEvent(ME_AV_CONTACTAVATARCHANGED); hMyAvatarChanged = CreateHookableEvent(ME_AV_MYAVATARCHANGED); - AllocCacheBlock(); + InitServices(); + InitCache(); InitPolls(); HMODULE hDll; @@ -2284,17 +472,7 @@ extern "C" int __declspec(dllexport) Load(void) extern "C" int __declspec(dllexport) Unload(void) { UninitPolls(); - - struct CacheNode *pNode = g_Cache; - while(pNode) { - if (pNode->ace.hbmPic != 0) - DeleteObject(pNode->ace.hbmPic); - pNode = pNode->pNextNode; - } - - for(int i = 0; i < g_curBlock; i++) - free(g_cacheBlocks[i]); - free(g_cacheBlocks); + UnloadCache(); g_ProtoPictures.destroy(); g_MyAvatars.destroy(); @@ -2304,113 +482,5 @@ extern "C" int __declspec(dllexport) Unload(void) DestroyHookableEvent(hMyAvatarChanged); CloseHandle(hLoaderEvent); - DeleteCriticalSection(&alloccs); - DeleteCriticalSection(&cachecs); - return 0; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -protoPicCacheEntry::~protoPicCacheEntry() -{ - if (hbmPic != 0) - DeleteObject(hbmPic); - mir_free(szProtoname); - mir_free(tszAccName); -} - -void protoPicCacheEntry::clear() -{ - if (hbmPic != 0) - DeleteObject(hbmPic); - - memset(this, 0, sizeof(avatarCacheEntry)); -} - -BOOL Proto_IsAvatarsEnabled(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_ENABLED, 0); - - return TRUE; -} - -BOOL Proto_IsAvatarFormatSupported(const char *proto, int format) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_FORMATSUPPORTED, format); - - if (format >= PA_FORMAT_SWF) - return FALSE; - - return TRUE; -} - -int Proto_AvatarImageProportion(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_PROPORTION, 0); - - return 0; -} - -void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) { - POINT maxSize; - CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXSIZE, (LPARAM) &maxSize); - *width = maxSize.y; - *height = maxSize.x; - } - else { - *width = 300; - *height = 300; - } - - if (*width < 0) - *width = 0; - else if (*width > 300) - *width = 300; - - if (*height < 0) - *height = 0; - else if (*height > 300) - *height = 300; -} - -BOOL Proto_NeedDelaysForAvatars(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) { - int ret = CallProtoService(proto, PS_GETAVATARCAPS, AF_DONTNEEDDELAYS, 0); - if (ret > 0) - return FALSE; - else - return TRUE; - } - - return TRUE; -} - -int Proto_GetAvatarMaxFileSize(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXFILESIZE, 0); - - return 0; -} - -int Proto_GetDelayAfterFail(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_DELAYAFTERFAIL, 0); - return 0; } - -BOOL Proto_IsFetchingAlwaysAllowed(const char *proto) -{ - if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) - return CallProtoService(proto, PS_GETAVATARCAPS, AF_FETCHALWAYS, 0); - - return FALSE; -} diff --git a/plugins/AVS/src/options.cpp b/plugins/AVS/src/options.cpp index 8e7556629d..193de068db 100644 --- a/plugins/AVS/src/options.cpp +++ b/plugins/AVS/src/options.cpp @@ -35,13 +35,6 @@ extern HANDLE hEventChanged; extern HINSTANCE g_hInst; extern HICON g_hIcon; -extern int CreateAvatarInCache(HANDLE hContact, struct avatarCacheEntry *ace, char *szProto); -extern INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam); -extern int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist = FALSE, int pa_format = 0); -extern HBITMAP LoadPNG(struct avatarCacheEntry *ace, char *szFilename); -extern HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked = -1); - -extern BOOL Proto_IsAvatarsEnabled(const char *proto); extern BOOL ScreenToClient(HWND hWnd, LPRECT lpRect); static BOOL dialoginit = TRUE; diff --git a/plugins/AVS/src/poll.cpp b/plugins/AVS/src/poll.cpp index 340919be0d..22968fe5a0 100644 --- a/plugins/AVS/src/poll.cpp +++ b/plugins/AVS/src/poll.cpp @@ -45,17 +45,11 @@ A queue to request items. One request is done at a time, REQUEST_WAIT_TIME milis 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 BOOL g_AvatarHistoryAvail; - #ifdef _DEBUG int _DebugTrace(const char *fmt, ...); int _DebugTrace(HANDLE hContact, const char *fmt, ...); @@ -209,7 +203,7 @@ void ProcessAvatarInfo(HANDLE hContact, int type, PROTO_AVATAR_INFORMATIONT *pai } } -int FetchAvatarFor(HANDLE hContact, char *szProto = NULL) +int FetchAvatarFor(HANDLE hContact, char *szProto) { int result = GAIR_NOAVATAR; diff --git a/plugins/AVS/src/services.cpp b/plugins/AVS/src/services.cpp new file mode 100644 index 0000000000..bb1014d70a --- /dev/null +++ b/plugins/AVS/src/services.cpp @@ -0,0 +1,997 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2004 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR GetAvatarBitmap(WPARAM wParam, LPARAM lParam) +{ + if (wParam == 0 || g_shutDown || fei == NULL) + return 0; + + HANDLE hContact = (HANDLE) wParam; + hContact = GetContactThatHaveTheAvatar(hContact); + + // Get the node + CacheNode *node = FindAvatarInCache(hContact, TRUE); + if (node == NULL || !node->loaded) + return (INT_PTR) GetProtoDefaultAvatar(hContact); + else + return (INT_PTR) &node->ace; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR ProtectAvatar(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + BYTE was_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); + + if (fei == NULL || was_locked == (BYTE)lParam) // no need for redundant lockings... + return 0; + + if (hContact) { + if (!was_locked) + MakePathRelative(hContact); + db_set_b(hContact, "ContactPhoto", "Locked", lParam ? 1 : 0); + if (lParam == 0) + MakePathRelative(hContact); + ChangeAvatar(hContact, TRUE); + } + return 0; +} + +/* + * set an avatar (service function) + * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL + * image filename (will be checked for existance, though) + */ + +struct OpenFileSubclassData { + BYTE *locking_request; + BYTE setView; +}; + +BOOL CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + OpenFileSubclassData *data= (OpenFileSubclassData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + switch(msg) { + case WM_INITDIALOG: + { + OPENFILENAME *ofn = (OPENFILENAME *)lParam; + + data = (OpenFileSubclassData *) malloc(sizeof(OpenFileSubclassData)); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data); + data->locking_request = (BYTE *)ofn->lCustData; + data->setView = TRUE; + + TranslateDialogDefault(hwnd); + CheckDlgButton(hwnd, IDC_PROTECTAVATAR, *(data->locking_request)); + } + break; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_PROTECTAVATAR) + *(data->locking_request) = IsDlgButtonChecked(hwnd, IDC_PROTECTAVATAR) ? TRUE : FALSE; + break; + + case WM_NOTIFY: + if (data->setView) { + HWND hwndParent = GetParent(hwnd); + HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ; + if (hwndLv != NULL) { + SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0); + data->setView = FALSE; + } + } + break; + + case WM_NCDESTROY: + free((OpenFileSubclassData *)data); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); + break; + } + + return FALSE; +} + +static INT_PTR avSetAvatar(HANDLE hContact, TCHAR *tszPath) +{ + BYTE is_locked = 0; + TCHAR FileName[MAX_PATH], szBackupName[MAX_PATH]; + TCHAR *szFinalName = NULL; + HANDLE hFile = 0; + BYTE locking_request; + + if (hContact == NULL || fei == NULL) + return 0; + + is_locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); + + if ( tszPath == NULL ) { + OPENFILENAME ofn = {0}; + TCHAR filter[256]; + + filter[0] = '\0'; + CallService(MS_UTILS_GETBITMAPFILTERSTRINGST, SIZEOF(filter), ( LPARAM )filter); + + if (IsWinVer2000Plus()) + ofn.lStructSize = sizeof(ofn); + else + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = 0; + ofn.lpstrFile = FileName; + ofn.lpstrFilter = filter; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK; + ofn.lpstrInitialDir = _T("."); + *FileName = '\0'; + ofn.lpstrDefExt = _T(""); + ofn.hInstance = g_hInst; + ofn.lpTemplateName = MAKEINTRESOURCE(IDD_OPENSUBCLASS); + ofn.lpfnHook = (LPOFNHOOKPROC)OpenFileSubclass; + locking_request = is_locked; + ofn.lCustData = (LPARAM)&locking_request; + if (GetOpenFileName(&ofn)) { + szFinalName = FileName; + is_locked = locking_request ? 1 : is_locked; + } + else + return 0; + } + else + szFinalName = tszPath; + + /* + * filename is now set, check it and perform all needed action + */ + + if ((hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) + return 0; + + // file exists... + + CloseHandle(hFile); + + AVS_pathToRelative(szFinalName, szBackupName); + db_set_ts(hContact, "ContactPhoto", "Backup", szBackupName); + + db_set_b(hContact, "ContactPhoto", "Locked", is_locked); + db_set_ts(hContact, "ContactPhoto", "File", szFinalName); + MakePathRelative(hContact, szFinalName); + // Fix cache + ChangeAvatar(hContact, TRUE); + + return 0; +} + +INT_PTR SetAvatar(WPARAM wParam, LPARAM lParam) +{ + return avSetAvatar((HANDLE)wParam, _A2T((const char*)lParam )); +} + +INT_PTR SetAvatarW(WPARAM wParam, LPARAM lParam) +{ + return avSetAvatar((HANDLE)wParam, (TCHAR*)lParam ); +} + +/* + * see if is possible to set the avatar for the expecified protocol + */ + +static INT_PTR CanSetMyAvatar(WPARAM wParam, LPARAM lParam) +{ + char *protocol = (char *) wParam; + if (protocol == NULL || fei == NULL) + return 0; + + return ProtoServiceExists(protocol, PS_SETMYAVATAR); +} + +/* + * set an avatar for a protocol (service function) + * if lParam == NULL, a open file dialog will be opened, otherwise, lParam is taken as a FULL + * image filename (will be checked for existance, though) + */ + +static int InternalRemoveMyAvatar(char *protocol) +{ + SetIgnoreNotify(protocol, TRUE); + + // Remove avatar + int ret = 0; + if (protocol != NULL) + { + if ( ProtoServiceExists(protocol, PS_SETMYAVATAR)) + ret = SaveAvatar(protocol, NULL); + else + ret = -3; + + if (ret == 0) + { + // Has global avatar? + DBVARIANT dbv = {0}; + if ( !db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) { + db_free(&dbv); + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); + DeleteGlobalUserAvatar(); + } + } + } + else + { + PROTOACCOUNT **accs; + int i,count; + + ProtoEnumAccounts( &count, &accs ); + for (i = 0; i < count; i++) + { + if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) + continue; + + if (!Proto_IsAvatarsEnabled( accs[i]->szModuleName )) + continue; + + // Found a protocol + int retTmp = SaveAvatar( accs[i]->szModuleName, NULL); + if (retTmp != 0) + ret = retTmp; + } + + DeleteGlobalUserAvatar(); + + if (ret) + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); + else + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0); + } + + SetIgnoreNotify(protocol, FALSE); + + ReportMyAvatarChanged(WPARAM((protocol == NULL ) ? "" : protocol), 0); + return ret; +} + +static void FilterGetStrings(TCHAR *filter, int bytesLeft, BOOL xml, BOOL swf) +{ + TCHAR *pfilter; + int wParam = bytesLeft; + + lstrcpyn(filter, TranslateT("All Files"), bytesLeft); bytesLeft-=lstrlen(filter); + _tcsncat(filter, _T(" (*.bmp;*.jpg;*.gif;*.png"), bytesLeft); + if (swf) _tcscat(filter, _T(";*.swf")); + if (xml) _tcscat(filter, _T(";*.xml")); + _tcscat(filter, _T(")")); + pfilter=filter+lstrlen(filter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG"), bytesLeft); + if (swf) _tcscat(pfilter, _T(";*.SWF")); + if (xml) _tcscat(pfilter, _T(";*.XML")); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + + lstrcpyn(pfilter, TranslateT("Windows Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.bmp;*.rle)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.BMP;*.RLE"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + + lstrcpyn(pfilter,TranslateT("JPEG Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.jpg;*.jpeg)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.JPG;*.JPEG"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + + lstrcpyn(pfilter,TranslateT("GIF Bitmaps"),bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.gif)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.GIF"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + + lstrcpyn(pfilter,TranslateT("PNG Bitmaps"), bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.png)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.PNG"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + + if (swf) + { + lstrcpyn(pfilter,TranslateT("Flash Animations"), bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.swf)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.SWF"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + } + + if (xml) + { + lstrcpyn(pfilter, TranslateT("XML Files"), bytesLeft); bytesLeft-=lstrlen(pfilter); + _tcsncat(pfilter, _T(" (*.xml)"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + lstrcpyn(pfilter, _T("*.XML"), bytesLeft); + pfilter+=lstrlen(pfilter)+1; bytesLeft=wParam-(pfilter-filter); + } + + if (bytesLeft) *pfilter='\0'; +} + +/* + * Callback to set thumbnaill view to open dialog + */ +static UINT_PTR CALLBACK SetMyAvatarHookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) { + case WM_INITDIALOG: + { + hwndSetMyAvatar = hwnd; + + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)lParam); + OPENFILENAME *ofn = (OPENFILENAME *)lParam; + SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; + data->thumbnail = TRUE; + + SetWindowText(GetDlgItem(hwnd, IDC_MAKE_SQUARE), TranslateT("Make the avatar square")); + SetWindowText(GetDlgItem(hwnd, IDC_GROW), TranslateT("Grow avatar to fit max allowed protocol size")); + + CheckDlgButton(hwnd, IDC_MAKE_SQUARE, data->square ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwnd, IDC_GROW, data->grow ? BST_CHECKED : BST_UNCHECKED); + + if (data->protocol != NULL && (Proto_AvatarImageProportion(data->protocol) & PIP_SQUARE)) + EnableWindow(GetDlgItem(hwnd, IDC_MAKE_SQUARE), FALSE); + } + break; + + case WM_NOTIFY: + { + OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; + if (data->thumbnail) + { + HWND hwndParent = GetParent(hwnd); + HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ; + if (hwndLv != NULL) + { + SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0); + data->thumbnail = FALSE; + } + } + break; + } + case WM_DESTROY: + { + OPENFILENAME *ofn = (OPENFILENAME *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + SetMyAvatarHookData *data = (SetMyAvatarHookData *) ofn->lCustData; + data->square = IsDlgButtonChecked(hwnd, IDC_MAKE_SQUARE); + data->grow = IsDlgButtonChecked(hwnd, IDC_GROW); + + hwndSetMyAvatar = NULL; + break; + } + } + + return 0; +} + +struct SaveProtocolData { + DWORD max_size; + TCHAR image_file_name[MAX_PATH]; + BOOL saved; + BOOL need_smaller_size; + int width; + int height; + TCHAR temp_file[MAX_PATH]; + HBITMAP hBmpProto; +}; + +void SaveImage(SaveProtocolData &d, char *protocol, int format) +{ + if (Proto_IsAvatarFormatSupported(protocol, format)) + { + mir_sntprintf(d.image_file_name, SIZEOF(d.image_file_name), _T("%s%s"), d.temp_file, GetFormatExtension(format)); + if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, format == PA_FORMAT_JPEG ? JPEG_QUALITYSUPERB : 0)) + { + if (d.max_size != 0 && GetFileSize(d.image_file_name) > d.max_size) + { + DeleteFile(d.image_file_name); + + if (format == PA_FORMAT_JPEG) + { + // Try with lower quality + if (!BmpFilterSaveBitmapT(d.hBmpProto, d.image_file_name, JPEG_QUALITYGOOD)) + { + if (GetFileSize(d.image_file_name) > d.max_size) + { + DeleteFile(d.image_file_name); + d.need_smaller_size = TRUE; + } + else + d.saved = TRUE; + } + } + else + d.need_smaller_size = TRUE; + } + else + d.saved = TRUE; + } + } +} + +static int SetProtoMyAvatar(char *protocol, HBITMAP hBmp, TCHAR *originalFilename, int originalFormat, BOOL square, BOOL grow) +{ + if (!ProtoServiceExists(protocol, PS_SETMYAVATAR)) + return -1; + + // If is swf or xml, just set it + + if (originalFormat == PA_FORMAT_SWF) + { + if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF)) + return -1; + + return SaveAvatar(protocol, originalFilename); + } + + if (originalFormat == PA_FORMAT_XML) + { + if (!Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML)) + return -1; + + return SaveAvatar(protocol, originalFilename); + } + + // Get protocol info + SaveProtocolData d = {0}; + + d.max_size = (DWORD) Proto_GetAvatarMaxFileSize(protocol); + + Proto_GetAvatarMaxSize(protocol, &d.width, &d.height); + int orig_width = d.width; + int orig_height = d.height; + + if (Proto_AvatarImageProportion(protocol) & PIP_SQUARE) + square = TRUE; + + // Try to save until a valid image is found or we give up + int num_tries = 0; + do { + // Lets do it + ResizeBitmap rb; + rb.size = sizeof(ResizeBitmap); + rb.hBmp = hBmp; + rb.max_height = d.height; + rb.max_width = d.width; + rb.fit = (grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW) + | (square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS); + + d.hBmpProto = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0); + + if (d.hBmpProto == NULL) + { + if (d.temp_file[0] != '\0') + DeleteFile(d.temp_file); + return -1; + } + + // Check if can use original image + if (d.hBmpProto == hBmp + && Proto_IsAvatarFormatSupported(protocol, originalFormat) + && (d.max_size == 0 || GetFileSize(originalFilename) < d.max_size)) + { + if (d.temp_file[0] != '\0') + DeleteFile(d.temp_file); + + // Use original image + return SaveAvatar(protocol, originalFilename); + } + + // Create a temporary file (if was not created already) + if (d.temp_file[0] == '\0') + { + d.temp_file[0] = '\0'; + if (GetTempPath(MAX_PATH, d.temp_file) == 0 + || GetTempFileName(d.temp_file, _T("mir_av_"), 0, d.temp_file) == 0) + { + DeleteObject(d.hBmpProto); + return -1; + } + } + + // Which format? + + // First try to use original format + if (originalFormat != PA_FORMAT_BMP) + SaveImage(d, protocol, originalFormat); + + if (!d.saved && originalFormat != PA_FORMAT_PNG) + SaveImage(d, protocol, PA_FORMAT_PNG); + + if (!d.saved && originalFormat != PA_FORMAT_JPEG) + SaveImage(d, protocol, PA_FORMAT_JPEG); + + if (!d.saved && originalFormat != PA_FORMAT_GIF) + SaveImage(d, protocol, PA_FORMAT_GIF); + + if (!d.saved) + SaveImage(d, protocol, PA_FORMAT_BMP); + + num_tries++; + if (!d.saved && d.need_smaller_size && num_tries < 4) + { + // Cleanup + if (d.hBmpProto != hBmp) + DeleteObject(d.hBmpProto); + + // use a smaller size + d.width = orig_width * (4 - num_tries) / 4; + d.height = orig_height * (4 - num_tries) / 4; + } + + } while(!d.saved && d.need_smaller_size && num_tries < 4); + + int ret; + + if (d.saved) + { + // Call proto service + ret = SaveAvatar(protocol, d.image_file_name); + DeleteFile(d.image_file_name); + } + else + { + ret = -1; + } + + if (d.temp_file[0] != '\0') + DeleteFile(d.temp_file); + + if (d.hBmpProto != hBmp) + DeleteObject(d.hBmpProto); + + return ret; +} + +static int InternalSetMyAvatar(char *protocol, TCHAR *szFinalName, SetMyAvatarHookData &data, BOOL allAcceptXML, BOOL allAcceptSWF) +{ + HANDLE hFile = 0; + + int format = GetImageFormat(szFinalName); + if (format == PA_FORMAT_UNKNOWN || (hFile = CreateFile(szFinalName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) + return -3; + + CloseHandle(hFile); + + // file exists... + + HBITMAP hBmp = NULL; + + if (format == PA_FORMAT_SWF) + { + if (!allAcceptSWF) + return -4; + } + else if (format == PA_FORMAT_XML) + { + if (!allAcceptXML) + return -4; + } + else + { + // Try to open if is not a flash or XML + hBmp = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFinalName, IMGL_TCHAR); + if (hBmp == NULL) + return -4; + } + + SetIgnoreNotify(protocol, TRUE); + + int ret = 0; + if (protocol != NULL) + { + ret = SetProtoMyAvatar(protocol, hBmp, szFinalName, format, data.square, data.grow); + + if (ret == 0) + { + DeleteGlobalUserAvatar(); + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); + } + } + else + { + PROTOACCOUNT **accs; + int i,count; + + ProtoEnumAccounts( &count, &accs ); + for (i = 0; i < count; i++) + { + if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) + continue; + + if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName )) + continue; + + int retTmp = SetProtoMyAvatar( accs[i]->szModuleName, hBmp, szFinalName, format, data.square, data.grow); + if (retTmp != 0) + ret = retTmp; + } + + DeleteGlobalUserAvatar(); + + if (ret) + { + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); + } + else + { + // Copy avatar file to store as global one + TCHAR globalFile[1024]; + BOOL saved = TRUE; + if (FoldersGetCustomPathT(hGlobalAvatarFolder, globalFile, SIZEOF(globalFile), _T(""))) + { + mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\%s"), g_szDataPath, _T("GlobalAvatar")); + CreateDirectory(globalFile, NULL); + } + + TCHAR *ext = _tcsrchr(szFinalName, _T('.')); // Can't be NULL here + if (format == PA_FORMAT_XML || format == PA_FORMAT_SWF) + { + mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext); + CopyFile(szFinalName, globalFile, FALSE); + } + else + { + // Resize (to avoid too big avatars) + ResizeBitmap rb = {0}; + rb.size = sizeof(ResizeBitmap); + rb.hBmp = hBmp; + rb.max_height = 300; + rb.max_width = 300; + rb.fit = (data.grow ? 0 : RESIZEBITMAP_FLAG_DONT_GROW) + | (data.square ? RESIZEBITMAP_MAKE_SQUARE : RESIZEBITMAP_KEEP_PROPORTIONS); + + HBITMAP hBmpTmp = (HBITMAP) BmpFilterResizeBitmap((WPARAM)&rb, 0); + + // Check if need to resize + if (hBmpTmp == hBmp || hBmpTmp == NULL) + { + // Use original image + mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar%s"), globalFile, ext); + CopyFile(szFinalName, globalFile, FALSE); + } + else + { + // Save as PNG + mir_sntprintf(globalFile, SIZEOF(globalFile), _T("%s\\my_global_avatar.png"), globalFile); + if (BmpFilterSaveBitmap((WPARAM) hBmpTmp, (LPARAM) globalFile)) + saved = FALSE; + + DeleteObject(hBmpTmp); + } + } + + if (saved) + { + TCHAR relFile[1024]; + if (AVS_pathToRelative(globalFile, relFile)) + db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", relFile); + else + db_set_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", globalFile); + + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 0); + } + else + { + db_set_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1); + } + } + } + + DeleteObject(hBmp); + + SetIgnoreNotify(protocol, FALSE); + + ReportMyAvatarChanged(WPARAM((protocol == NULL) ? "" : protocol), 0); + return ret; +} + +INT_PTR avSetMyAvatar( char* protocol, TCHAR* tszPath ) +{ + TCHAR FileName[MAX_PATH]; + TCHAR *szFinalName = NULL; + BOOL allAcceptXML; + BOOL allAcceptSWF; + + // Protocol allow seting of avatar? + if (protocol != NULL && !CanSetMyAvatar((WPARAM)protocol, 0)) + return -1; + + if (tszPath == NULL && hwndSetMyAvatar != 0) { + SetForegroundWindow(hwndSetMyAvatar); + SetFocus(hwndSetMyAvatar); + ShowWindow(hwndSetMyAvatar, SW_SHOW); + return -2; + } + + SetMyAvatarHookData data = { 0 }; + + // Check for XML and SWF + if (protocol == NULL) + { + allAcceptXML = TRUE; + allAcceptSWF = TRUE; + + PROTOACCOUNT **accs; + int i,count; + + ProtoEnumAccounts( &count, &accs ); + for (i = 0; i < count; i++) + { + if ( !ProtoServiceExists( accs[i]->szModuleName, PS_SETMYAVATAR)) + continue; + + if ( !Proto_IsAvatarsEnabled( accs[i]->szModuleName )) + continue; + + allAcceptXML = allAcceptXML && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_XML); + allAcceptSWF = allAcceptSWF && Proto_IsAvatarFormatSupported( accs[i]->szModuleName, PA_FORMAT_SWF); + } + + data.square = db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0); + } + else + { + allAcceptXML = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_XML); + allAcceptSWF = Proto_IsAvatarFormatSupported(protocol, PA_FORMAT_SWF); + + data.protocol = protocol; + data.square = (Proto_AvatarImageProportion(protocol) & PIP_SQUARE) + || db_get_b(0, AVS_MODULE, "SetAllwaysMakeSquare", 0); + } + + if (tszPath == NULL) { + OPENFILENAME ofn = {0}; + TCHAR filter[512]; + TCHAR inipath[1024]; + + data.protocol = protocol; + + filter[0] = '\0'; + FilterGetStrings(filter, SIZEOF(filter), allAcceptXML, allAcceptSWF); + + FoldersGetCustomPathT(hMyAvatarsFolder, inipath, SIZEOF(inipath), _T(".")); + + if (IsWinVer2000Plus()) + ofn.lStructSize = sizeof(ofn); + else + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = 0; + ofn.lpstrFile = FileName; + ofn.lpstrFilter = filter; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_ENABLETEMPLATE | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK; + ofn.lpstrInitialDir = inipath; + ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SET_OWN_SUBCLASS); + ofn.lpfnHook = SetMyAvatarHookProc; + ofn.lCustData = (LPARAM) &data; + + *FileName = '\0'; + ofn.lpstrDefExt = _T(""); + ofn.hInstance = g_hInst; + + TCHAR title[256]; + if (protocol == NULL) + mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar")); + else + { + TCHAR* prototmp = mir_a2t(protocol); + mir_sntprintf(title, SIZEOF(title), TranslateT("Set My Avatar for %s"), prototmp); + mir_free(prototmp); + } + ofn.lpstrTitle = title; + + if (GetOpenFileName(&ofn)) + szFinalName = FileName; + else + return 1; + } + else + szFinalName = (TCHAR *)tszPath; + + /* + * filename is now set, check it and perform all needed action + */ + + if (szFinalName[0] == '\0') + return InternalRemoveMyAvatar(protocol); + + return InternalSetMyAvatar(protocol, szFinalName, data, allAcceptXML, allAcceptSWF); +} + +static INT_PTR SetMyAvatar( WPARAM wParam, LPARAM lParam ) +{ + return avSetMyAvatar(( char* )wParam, _A2T(( const char* )lParam )); +} + +static INT_PTR SetMyAvatarW( WPARAM wParam, LPARAM lParam ) +{ + return avSetMyAvatar(( char* )wParam, ( TCHAR* )lParam ); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CALLBACK DlgProcAvatarOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +static INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam) +{ + if (wParam) + CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_AVATAROPTIONS), 0, DlgProcAvatarOptions, (LPARAM)wParam); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR DrawAvatarPicture(WPARAM wParam, LPARAM lParam) +{ + AVATARDRAWREQUEST *r = (AVATARDRAWREQUEST *)lParam; + AVATARCACHEENTRY *ace = NULL; + + if (fei == NULL || r == NULL || IsBadReadPtr((void *)r, sizeof(AVATARDRAWREQUEST))) + return 0; + + if (r->cbSize != sizeof(AVATARDRAWREQUEST)) + return 0; + + if (r->dwFlags & AVDRQ_PROTOPICT) { + if (r->szProto == NULL) + return 0; + + for(int i = 0; i < g_ProtoPictures.getCount(); i++) { + protoPicCacheEntry& p = g_ProtoPictures[i]; + if ( !lstrcmpA(p.szProtoname, r->szProto) && lstrlenA(r->szProto) == lstrlenA(p.szProtoname) && p.hbmPic != 0) { + ace = (AVATARCACHEENTRY *)&g_ProtoPictures[i]; + break; + } + } + } + else if (r->dwFlags & AVDRQ_OWNPIC) { + if (r->szProto == NULL) + return 0; + + if (r->szProto[0] == '\0' && db_get_b(NULL, AVS_MODULE, "GlobalUserAvatarNotConsistent", 1)) + return -1; + + ace = (AVATARCACHEENTRY *)GetMyAvatar(0, (LPARAM)r->szProto); + } + else + ace = (AVATARCACHEENTRY *)GetAvatarBitmap((WPARAM)r->hContact, 0); + + if (ace && (!(r->dwFlags & AVDRQ_RESPECTHIDDEN) || !(ace->dwFlags & AVS_HIDEONCLIST))) { + ace->t_lastAccess = time(NULL); + + if (ace->bmHeight == 0 || ace->bmWidth == 0 || ace->hbmPic == 0) + return 0; + + InternalDrawAvatar(r, ace->hbmPic, ace->bmWidth, ace->bmHeight, ace->dwFlags); + return 1; + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR GetMyAvatar(WPARAM wParam, LPARAM lParam) +{ + if (wParam || g_shutDown || fei == NULL) + return 0; + + char *szProto = (char *)lParam; + if (lParam == 0 || IsBadReadPtr(szProto, 4)) + return 0; + + for(int i = 0; i < g_MyAvatars.getCount(); i++) + if (!lstrcmpA(szProto, g_MyAvatars[i].szProtoname) && g_MyAvatars[i].hbmPic != 0) + return (INT_PTR)&g_MyAvatars[i]; + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void ReloadMyAvatar(LPVOID lpParam) +{ + char *szProto = (char *)lpParam; + + mir_sleep(500); + for(int i = 0; !g_shutDown && i < g_MyAvatars.getCount(); i++) { + char *myAvatarProto = g_MyAvatars[i].szProtoname; + + if (szProto[0] == 0) { + // Notify to all possibles + if (lstrcmpA(myAvatarProto, szProto)) { + if (!ProtoServiceExists( myAvatarProto, PS_SETMYAVATAR)) + continue; + if (!Proto_IsAvatarsEnabled( myAvatarProto )) + continue; + } + + } + else if (lstrcmpA(myAvatarProto, szProto)) + continue; + + if (g_MyAvatars[i].hbmPic) + DeleteObject(g_MyAvatars[i].hbmPic); + + if (CreateAvatarInCache((HANDLE)-1, &g_MyAvatars[i], myAvatarProto) != -1) + NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, (LPARAM)&g_MyAvatars[i]); + else + NotifyEventHooks(hMyAvatarChanged, (WPARAM)myAvatarProto, 0); + } + + free(lpParam); +} + +INT_PTR ReportMyAvatarChanged(WPARAM wParam, LPARAM lParam) +{ + const char *proto = (const char*)wParam; + if (proto == NULL) + return -1; + + for(int i = 0; i < g_MyAvatars.getCount(); i++) { + if (g_MyAvatars[i].dwFlags & AVS_IGNORENOTIFY) + continue; + + if ( !lstrcmpA(g_MyAvatars[i].szProtoname, proto)) { + LPVOID lpParam = (void *)malloc(lstrlenA(g_MyAvatars[i].szProtoname) + 2); + strcpy((char *)lpParam, g_MyAvatars[i].szProtoname); + mir_forkthread(ReloadMyAvatar, lpParam); + return 0; + } + } + + return -2; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void InitServices() +{ + CreateServiceFunction(MS_AV_GETAVATARBITMAP, GetAvatarBitmap); + CreateServiceFunction(MS_AV_PROTECTAVATAR, ProtectAvatar); + CreateServiceFunction(MS_AV_SETAVATAR, SetAvatar); + CreateServiceFunction(MS_AV_SETAVATARW, SetAvatarW); + CreateServiceFunction(MS_AV_SETMYAVATAR, SetMyAvatar); + CreateServiceFunction(MS_AV_SETMYAVATARW, SetMyAvatarW); + CreateServiceFunction(MS_AV_CANSETMYAVATAR, CanSetMyAvatar); + CreateServiceFunction(MS_AV_CONTACTOPTIONS, ContactOptions); + CreateServiceFunction(MS_AV_DRAWAVATAR, DrawAvatarPicture); + CreateServiceFunction(MS_AV_GETMYAVATAR, GetMyAvatar); + CreateServiceFunction(MS_AV_REPORTMYAVATARCHANGED, ReportMyAvatarChanged); + + CreateServiceFunction(MS_AV_LOADBITMAP32, BmpFilterLoadBitmap32); + CreateServiceFunction(MS_AV_SAVEBITMAP, BmpFilterSaveBitmap); + CreateServiceFunction(MS_AV_CANSAVEBITMAP, BmpFilterCanSaveBitmap); + CreateServiceFunction(MS_AV_RESIZEBITMAP, BmpFilterResizeBitmap); +} diff --git a/plugins/AVS/src/utils.cpp b/plugins/AVS/src/utils.cpp new file mode 100644 index 0000000000..1d0494e258 --- /dev/null +++ b/plugins/AVS/src/utils.cpp @@ -0,0 +1,634 @@ +/* +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" + +#ifdef _DEBUG + +int _DebugTrace(const char *fmt, ...) +{ + char debug[2048]; + int ibsize = 2047; + va_list va; + va_start(va, fmt); + + mir_snprintf(debug, SIZEOF(debug) - 10, " ***** AVS [%08d] [ID:%04x]: ", GetTickCount(), GetCurrentThreadId()); + OutputDebugStringA(debug); + mir_vsnprintf(debug, ibsize, fmt, va); + OutputDebugStringA(debug); + OutputDebugStringA(" ***** \n"); + + return 0; +} + +int _DebugTrace(HANDLE hContact, const char *fmt, ...) +{ + char text[1024]; + size_t len; + va_list va; + + char *name = NULL; + char *proto = NULL; + if (hContact != NULL) + { + name = (char*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + proto = GetContactProto(hContact); + } + + mir_snprintf(text, SIZEOF(text) - 10, " ***** AVS [%08d] [ID:%04x]: [%08d - %s - %s] ", + GetTickCount(), GetCurrentThreadId(), hContact, proto == NULL ? "" : proto, name == NULL ? "" : name); + len = strlen(text); + + va_start(va, fmt); + mir_vsnprintf(&text[len], SIZEOF(text) - len, fmt, va); + va_end(va); + + OutputDebugStringA(text); + OutputDebugStringA(" ***** \n"); + + return 0; +} + +#endif + +void mir_sleep(int time) +{ + if (!g_shutDown) + WaitForSingleObject(hShutdownEvent, time); +} + +/* + * path utilities (make avatar paths relative to *PROFILE* directory, not miranda directory. + * taken and modified from core services + */ + +int AVS_pathIsAbsolute(const TCHAR *path) +{ + if (!path || !(lstrlen(path) > 2)) + return 0; + if ((path[1]==':'&&path[2]=='\\')||(path[0]=='\\'&&path[1]=='\\')) return 1; + return 0; +} + +size_t AVS_pathToRelative(const TCHAR *pSrc, TCHAR *pOut) +{ + if (!pSrc || !*pSrc || _tcslen(pSrc) > MAX_PATH) return 0; + if (!AVS_pathIsAbsolute( pSrc )) + lstrcpyn(pOut, pSrc, MAX_PATH); + else { + TCHAR szTmp[MAX_PATH]; + mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s"), pSrc); + _tcslwr(szTmp); + if (_tcsstr(szTmp, g_szDataPath)) + lstrcpyn(pOut, pSrc + _tcslen(g_szDataPath) + 1, MAX_PATH); + else + lstrcpyn(pOut, pSrc, MAX_PATH); + } + return _tcslen(pOut); +} + +size_t AVS_pathToAbsolute(const TCHAR *pSrc, TCHAR *pOut) +{ + if (!pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH) + return 0; + + if (AVS_pathIsAbsolute(pSrc) || !_istalnum(pSrc[0])) + lstrcpyn(pOut, pSrc, MAX_PATH); + else + mir_sntprintf(pOut, MAX_PATH, _T("%s\\%s"), g_szDataPath, pSrc, MAX_PATH); + return lstrlen(pOut); +} + +/* + * convert the avatar image path to a relative one... + * given: contact handle, path to image + */ +void MakePathRelative(HANDLE hContact, TCHAR *path) +{ + TCHAR szFinalPath[MAX_PATH]; + szFinalPath[0] = '\0'; + + size_t result = AVS_pathToRelative(path, szFinalPath); + if (result && lstrlen(szFinalPath) > 0) { + db_set_ts(hContact, "ContactPhoto", "RFile", szFinalPath); + if (!db_get_b(hContact, "ContactPhoto", "Locked", 0)) + db_set_ts(hContact, "ContactPhoto", "Backup", szFinalPath); + } +} + +/* + * convert the avatar image path to a relative one... + * given: contact handle + */ + +void MakePathRelative(HANDLE hContact) +{ + DBVARIANT dbv; + if ( !db_get_ts(hContact, "ContactPhoto", "File", &dbv)) { + MakePathRelative(hContact, dbv.ptszVal); + db_free(&dbv); + } +} + +// create the avatar in cache +// returns 0 if not created (no avatar), iIndex otherwise, -2 if has to request avatar, -3 if avatar too big +int CreateAvatarInCache(HANDLE hContact, avatarCacheEntry *ace, char *szProto) +{ + DBVARIANT dbv = {0}; + char *szExt = NULL; + TCHAR tszFilename[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD dwFileSizeHigh = 0, dwFileSize = 0, sizeLimit = 0; + + tszFilename[0] = 0; + + ace->hbmPic = 0; + ace->dwFlags = 0; + ace->bmHeight = 0; + ace->bmWidth = 0; + ace->lpDIBSection = NULL; + ace->szFilename[0] = 0; + + if (szProto == NULL) { + char *proto = GetContactProto(hContact); + if (proto == NULL || !db_get_b(NULL, AVS_MODULE, proto, 1)) + return -1; + + if (db_get_b(hContact, "ContactPhoto", "Locked", 0) + && !db_get_ts(hContact, "ContactPhoto", "Backup", &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else if ( !db_get_ts(hContact, "ContactPhoto", "RFile", &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else if ( !db_get_ts(hContact, "ContactPhoto", "File", &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else return -2; + } + else { + if (hContact == 0) { // create a protocol picture in the proto picture cache + if ( !db_get_ts(NULL, PPICT_MODULE, szProto, &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else { + if (lstrcmpA(szProto, AVS_DEFAULT)) { + if ( !db_get_ts(NULL, PPICT_MODULE, AVS_DEFAULT, &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + + if (!strstr(szProto, "Global avatar for")) { + PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)szProto); + if (pdescr == NULL) + return -1; + char key[MAX_PATH]; + mir_snprintf(key, SIZEOF(key), "Global avatar for %s accounts", pdescr->szProtoName); + if ( !db_get_ts(NULL, PPICT_MODULE, key, &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + } + } + } + } + else if (hContact == (HANDLE)-1) { // create own picture - note, own avatars are not on demand, they are loaded once at + // startup and everytime they are changed. + if (szProto[0] == '\0') { + // Global avatar + if ( db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) + return -10; + + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else if ( ProtoServiceExists(szProto, PS_GETMYAVATART)) { + if (CallProtoService(szProto, PS_GETMYAVATART, (WPARAM)tszFilename, (LPARAM)MAX_PATH)) + tszFilename[0] = '\0'; + } + else if ( ProtoServiceExists(szProto, PS_GETMYAVATAR)) { + char szFileName[ MAX_PATH ]; + if (CallProtoService(szProto, PS_GETMYAVATAR, (WPARAM)szFileName, (LPARAM)MAX_PATH)) + tszFilename[0] = '\0'; + else + MultiByteToWideChar( CP_ACP, 0, szFileName, -1, tszFilename, SIZEOF(tszFilename)); + } + else if ( !db_get_ts(NULL, szProto, "AvatarFile", &dbv)) { + AVS_pathToAbsolute(dbv.ptszVal, tszFilename); + db_free(&dbv); + } + else return -1; + } + } + + if ( lstrlen(tszFilename) < 4) + return -1; + + _tcsncpy_s(tszFilename, SIZEOF(tszFilename), VARST(tszFilename), _TRUNCATE); + if ((hFile = CreateFile(tszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) + return -2; + + CloseHandle(hFile); + WPARAM isTransparentImage = 0; + + ace->hbmPic = (HBITMAP) BmpFilterLoadBitmap32((WPARAM)&isTransparentImage, (LPARAM)tszFilename); + ace->dwFlags = 0; + ace->bmHeight = 0; + ace->bmWidth = 0; + ace->lpDIBSection = NULL; + _tcsncpy(ace->szFilename, tszFilename, MAX_PATH); + ace->szFilename[MAX_PATH - 1] = 0; + if (ace->hbmPic != 0) { + BITMAP bminfo; + + GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); + + ace->cbSize = sizeof(avatarCacheEntry); + ace->dwFlags = AVS_BITMAP_VALID; + if (hContact != NULL && db_get_b(hContact, "ContactPhoto", "Hidden", 0)) + ace->dwFlags |= AVS_HIDEONCLIST; + ace->hContact = hContact; + ace->bmHeight = bminfo.bmHeight; + ace->bmWidth = bminfo.bmWidth; + + BOOL noTransparency = db_get_b(0, AVS_MODULE, "RemoveAllTransparency", 0); + + // Calc image hash + if (hContact != 0 && hContact != (HANDLE)-1) { + // Have to reset settings? -> do it if image changed + DWORD imgHash = GetImgHash(ace->hbmPic); + if (imgHash != db_get_dw(hContact, "ContactPhoto", "ImageHash", 0)) { + db_unset(hContact, "ContactPhoto", "MakeTransparentBkg"); + db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints"); + db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff"); + + db_set_dw(hContact, "ContactPhoto", "ImageHash", imgHash); + } + + // Make transparent? + if (!noTransparency && !isTransparentImage + && db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", + db_get_b(0, AVS_MODULE, "MakeTransparentBkg", 0))) + { + if (MakeTransparentBkg(hContact, &ace->hbmPic)) { + ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; + GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); + isTransparentImage = TRUE; + } + } + } + else if (hContact == (HANDLE)-1) { // My avatars + if (!noTransparency && !isTransparentImage + && db_get_b(0, AVS_MODULE, "MakeTransparentBkg", 0) + && db_get_b(0, AVS_MODULE, "MakeMyAvatarsTransparent", 0)) + { + if (MakeTransparentBkg(0, &ace->hbmPic)) { + ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; + GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); + isTransparentImage = TRUE; + } + } + } + + if (db_get_b(0, AVS_MODULE, "MakeGrayscale", 0)) + ace->hbmPic = MakeGrayscale(hContact, ace->hbmPic); + + if (noTransparency) { + fei->FI_CorrectBitmap32Alpha(ace->hbmPic, TRUE); + isTransparentImage = FALSE; + } + + if (bminfo.bmBitsPixel == 32 && isTransparentImage) { + if (fei->FI_Premultiply(ace->hbmPic)) + ace->dwFlags |= AVS_HASTRANSPARENCY; + + ace->dwFlags |= AVS_PREMULTIPLIED; + } + + if (szProto) { + protoPicCacheEntry *pAce = (protoPicCacheEntry *)ace; + if (hContact == 0) + pAce->dwFlags |= AVS_PROTOPIC; + else if (hContact == (HANDLE)-1) + pAce->dwFlags |= AVS_OWNAVATAR; + } + + return 1; + } + return -1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */ +#define TOPBIT (1 << (WIDTH - 1)) /* MSB */ +#define WIDTH 32 + +int GetFileHash(TCHAR* filename) +{ + HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + int remainder = 0; + char data[1024]; + DWORD dwRead; + do + { + // Read file chunk + dwRead = 0; + ReadFile(hFile, data, 1024, &dwRead, NULL); + + /* loop through each byte of data */ + for (int byte = 0; byte < (int) dwRead; ++byte) { + /* store the next byte into the remainder */ + remainder ^= (data[byte] << (WIDTH - 8)); + /* calculate for all 8 bits in the byte */ + for (int bit = 8; bit > 0; --bit) { + /* check if MSB of remainder is a one */ + if (remainder & TOPBIT) + remainder = (remainder << 1) ^ POLYNOMIAL; + else + remainder = (remainder << 1); + } + } + } + while(dwRead == 1024); + + CloseHandle(hFile); + + return remainder; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +protoPicCacheEntry::~protoPicCacheEntry() +{ + if (hbmPic != 0) + DeleteObject(hbmPic); + mir_free(szProtoname); + mir_free(tszAccName); +} + +void protoPicCacheEntry::clear() +{ + if (hbmPic != 0) + DeleteObject(hbmPic); + + memset(this, 0, sizeof(avatarCacheEntry)); +} + +BOOL Proto_IsAvatarsEnabled(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_ENABLED, 0); + + return TRUE; +} + +BOOL Proto_IsAvatarFormatSupported(const char *proto, int format) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_FORMATSUPPORTED, format); + + if (format >= PA_FORMAT_SWF) + return FALSE; + + return TRUE; +} + +int Proto_AvatarImageProportion(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_PROPORTION, 0); + + return 0; +} + +void Proto_GetAvatarMaxSize(const char *proto, int *width, int *height) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) { + POINT maxSize; + CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXSIZE, (LPARAM) &maxSize); + *width = maxSize.y; + *height = maxSize.x; + } + else { + *width = 300; + *height = 300; + } + + if (*width < 0) + *width = 0; + else if (*width > 300) + *width = 300; + + if (*height < 0) + *height = 0; + else if (*height > 300) + *height = 300; +} + +BOOL Proto_NeedDelaysForAvatars(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) { + int ret = CallProtoService(proto, PS_GETAVATARCAPS, AF_DONTNEEDDELAYS, 0); + if (ret > 0) + return FALSE; + else + return TRUE; + } + + return TRUE; +} + +int Proto_GetAvatarMaxFileSize(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXFILESIZE, 0); + + return 0; +} + +int Proto_GetDelayAfterFail(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_DELAYAFTERFAIL, 0); + + return 0; +} + +BOOL Proto_IsFetchingAlwaysAllowed(const char *proto) +{ + if ( ProtoServiceExists(proto, PS_GETAVATARCAPS)) + return CallProtoService(proto, PS_GETAVATARCAPS, AF_FETCHALWAYS, 0); + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +protoPicCacheEntry *GetProtoDefaultAvatar(HANDLE hContact) +{ + char *szProto = GetContactProto(hContact); + if (szProto) { + for(int i = 0; i < g_ProtoPictures.getCount(); i++) { + protoPicCacheEntry& p = g_ProtoPictures[i]; + if ( !lstrcmpA(p.szProtoname, szProto) && p.hbmPic != NULL) + return &g_ProtoPictures[i]; + } + } + return NULL; +} + +HANDLE GetContactThatHaveTheAvatar(HANDLE hContact, int locked) +{ + if (g_MetaAvail && db_get_b(NULL, g_szMetaName, "Enabled", 0)) { + if (db_get_dw(hContact, g_szMetaName, "NumContacts", 0) >= 1) { + if (locked == -1) + locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); + + if (!locked) + hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + } + } + return hContact; +} + +int ChangeAvatar(HANDLE hContact, BOOL fLoad, BOOL fNotifyHist, int pa_format) +{ + if (g_shutDown) + return 0; + + hContact = GetContactThatHaveTheAvatar(hContact); + + // Get the node + CacheNode *node = FindAvatarInCache(hContact, g_AvatarHistoryAvail && fNotifyHist, TRUE); + if (node == NULL) + return 0; + + if (fNotifyHist) + node->dwFlags |= AVH_MUSTNOTIFY; + + node->mustLoad = fLoad ? 1 : -1; + node->pa_format = pa_format; + SetEvent(hLoaderEvent); + return 0; +} + +void DeleteGlobalUserAvatar() +{ + DBVARIANT dbv = {0}; + if (db_get_ts(NULL, AVS_MODULE, "GlobalUserAvatarFile", &dbv)) + return; + + TCHAR szFilename[MAX_PATH]; + AVS_pathToAbsolute(dbv.ptszVal, szFilename); + db_free(&dbv); + + DeleteFile(szFilename); + db_unset(NULL, AVS_MODULE, "GlobalUserAvatarFile"); +} + +void SetIgnoreNotify(char *protocol, BOOL ignore) +{ + for(int i = 0; i < g_MyAvatars.getCount(); i++) { + if (protocol == NULL || !lstrcmpA(g_MyAvatars[i].szProtoname, protocol)) { + if (ignore) + g_MyAvatars[i].dwFlags |= AVS_IGNORENOTIFY; + else + g_MyAvatars[i].dwFlags &= ~AVS_IGNORENOTIFY; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +const TCHAR *GetFormatExtension(int format) +{ + if (format == PA_FORMAT_PNG) + return _T(".png"); + if (format == PA_FORMAT_JPEG) + return _T(".jpg"); + if (format == PA_FORMAT_ICON) + return _T(".ico"); + if (format == PA_FORMAT_BMP) + return _T(".bmp"); + if (format == PA_FORMAT_GIF) + return _T(".gif"); + if (format == PA_FORMAT_SWF) + return _T(".swf"); + if (format == PA_FORMAT_XML) + return _T(".xml"); + + return NULL; +} + +int GetImageFormat(TCHAR *filename) +{ + size_t len = lstrlen(filename); + + if (len < 5) + return PA_FORMAT_UNKNOWN; + + if (_tcsicmp(_T(".png"), &filename[len-4]) == 0) + return PA_FORMAT_PNG; + + if (_tcsicmp(_T(".jpg"), &filename[len-4]) == 0 || _tcsicmp(_T(".jpeg"), &filename[len-4]) == 0) + return PA_FORMAT_JPEG; + + if (_tcsicmp(_T(".ico"), &filename[len-4]) == 0) + return PA_FORMAT_ICON; + + if (_tcsicmp(_T(".bmp"), &filename[len-4]) == 0 || _tcsicmp(_T(".rle"), &filename[len-4]) == 0) + return PA_FORMAT_BMP; + + if (_tcsicmp(_T(".gif"), &filename[len-4]) == 0) + return PA_FORMAT_GIF; + + if (_tcsicmp(_T(".swf"), &filename[len-4]) == 0) + return PA_FORMAT_SWF; + + if (_tcsicmp(_T(".xml"), &filename[len-4]) == 0) + return PA_FORMAT_XML; + + return PA_FORMAT_UNKNOWN; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +DWORD GetFileSize(TCHAR *szFilename) +{ + HANDLE hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + + DWORD low = GetFileSize(hFile, NULL); + + CloseHandle(hFile); + + if (low == INVALID_FILE_SIZE) + return 0; + + return low; +} diff --git a/plugins/AVS/src/version.h b/plugins/AVS/src/version.h index 70435c2294..7292c874f5 100644 --- a/plugins/AVS/src/version.h +++ b/plugins/AVS/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0 #define __MINOR_VERSION 11 -#define __RELEASE_NUM 0 -#define __BUILD_NUM 2 +#define __RELEASE_NUM 1 +#define __BUILD_NUM 1 #define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM -- cgit v1.2.3