/* Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) 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 "stdafx.h" void mir_sleep(int time) { if (!g_shutDown) WaitForSingleObject(hShutdownEvent, time); } ///////////////////////////////////////////////////////////////////////////////////////// // substitutes variables and passes our own data path as base void MyPathToAbsolute(const wchar_t *ptszPath, wchar_t *ptszDest) { PathToAbsoluteW(VARSW(ptszPath), ptszDest, g_szDataPath); } ///////////////////////////////////////////////////////////////////////////////////////// // convert the avatar image path to a relative one... // given: contact handle, path to image void MakePathRelative(MCONTACT hContact, wchar_t *path) { wchar_t szFinalPath[MAX_PATH]; szFinalPath[0] = '\0'; size_t result = PathToRelativeW(path, szFinalPath, g_szDataPath); if (result && szFinalPath[0] != '\0') { db_set_ws(hContact, "ContactPhoto", "RFile", szFinalPath); if (!db_get_b(hContact, "ContactPhoto", "Locked", 0)) db_set_ws(hContact, "ContactPhoto", "Backup", szFinalPath); } } ///////////////////////////////////////////////////////////////////////////////////////// // convert the avatar image path to a relative one... // given: contact handle void MakePathRelative(MCONTACT hContact) { ptrW tszPath(db_get_wsa(hContact, "ContactPhoto", "File")); if (tszPath) MakePathRelative(hContact, tszPath); } ///////////////////////////////////////////////////////////////////////////////////////// // 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(MCONTACT hContact, AVATARCACHEENTRY *ace, const char *szProto) { ptrW tszValue; wchar_t tszFilename[MAX_PATH]; tszFilename[0] = 0; ace->hbmPic = nullptr; ace->dwFlags = 0; ace->bmHeight = 0; ace->bmWidth = 0; ace->lpDIBSection = nullptr; ace->szFilename[0] = 0; if (szProto == nullptr) { char *proto = Proto_GetBaseAccountName(hContact); if (proto == nullptr || !g_plugin.getByte(proto, 1)) return -1; if (db_get_b(hContact, "ContactPhoto", "Locked", 0) && (tszValue = db_get_wsa(hContact, "ContactPhoto", "Backup"))) MyPathToAbsolute(tszValue, tszFilename); else if (tszValue = db_get_wsa(hContact, "ContactPhoto", "RFile")) MyPathToAbsolute(tszValue, tszFilename); else if (tszValue = db_get_wsa(hContact, "ContactPhoto", "File")) MyPathToAbsolute(tszValue, tszFilename); else return -2; } else { if (hContact == 0) { // create a protocol picture in the proto picture cache if (tszValue = db_get_wsa(0, PPICT_MODULE, szProto)) MyPathToAbsolute(tszValue, tszFilename); else if (mir_strcmp(szProto, AVS_DEFAULT)) { if (tszValue = db_get_wsa(0, PPICT_MODULE, AVS_DEFAULT)) MyPathToAbsolute(tszValue, tszFilename); if (!strstr(szProto, "Global avatar for")) { PROTOACCOUNT *pdescr = Proto_GetAccount(szProto); if (pdescr == nullptr) return -1; char key[MAX_PATH]; mir_snprintf(key, "Global avatar for %s accounts", pdescr->szProtoName); if (tszValue = db_get_wsa(0, PPICT_MODULE, key)) MyPathToAbsolute(tszValue, tszFilename); } } } else if (hContact == INVALID_CONTACT_ID) { // 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 (tszValue = g_plugin.getWStringA("GlobalUserAvatarFile")) MyPathToAbsolute(tszValue, tszFilename); else return -10; } else if (ProtoServiceExists(szProto, PS_GETMYAVATAR)) { if (CallProtoService(szProto, PS_GETMYAVATAR, (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, _countof(tszFilename)); } else if (tszValue = db_get_wsa(0, szProto, "AvatarFile")) MyPathToAbsolute(tszValue, tszFilename); else return -1; } } if (mir_wstrlen(tszFilename) < 4) return -1; wcsncpy_s(tszFilename, VARSW(tszFilename), _TRUNCATE); if (_waccess(tszFilename, 4) == -1) return -2; BOOL isTransparentImage = 0; ace->hbmPic = BmpFilterLoadBitmap(&isTransparentImage, tszFilename); ace->dwFlags = 0; ace->bmHeight = 0; ace->bmWidth = 0; ace->lpDIBSection = nullptr; wcsncpy(ace->szFilename, tszFilename, MAX_PATH); ace->szFilename[MAX_PATH - 1] = 0; if (ace->hbmPic == nullptr) return -1; BITMAP bminfo; GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); 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 = g_plugin.getByte("RemoveAllTransparency", 0); // Calc image hash if (hContact != 0 && hContact != INVALID_CONTACT_ID) { // Have to reset settings? -> do it if image changed uint32_t 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", g_plugin.getByte("MakeTransparentBkg", 0))) { if (MakeTransparentBkg(hContact, &ace->hbmPic)) { ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); isTransparentImage = TRUE; } } } else if (hContact == INVALID_CONTACT_ID) { // My avatars if (!noTransparency && !isTransparentImage && g_plugin.getByte("MakeTransparentBkg", 0) && g_plugin.getByte("MakeMyAvatarsTransparent", 0)) { if (MakeTransparentBkg(0, &ace->hbmPic)) { ace->dwFlags |= AVS_CUSTOMTRANSPBKG | AVS_HASTRANSPARENCY; GetObject(ace->hbmPic, sizeof(bminfo), &bminfo); isTransparentImage = TRUE; } } } if (g_plugin.getByte("MakeGrayscale", 0)) ace->hbmPic = MakeGrayscale(ace->hbmPic); if (noTransparency) { FreeImage_CorrectBitmap32Alpha(ace->hbmPic, TRUE); isTransparentImage = FALSE; } if (bminfo.bmBitsPixel == 32 && isTransparentImage) { if (FreeImage_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 == INVALID_CONTACT_ID) pAce->dwFlags |= AVS_OWNAVATAR; } return 1; } ///////////////////////////////////////////////////////////////////////////////////////// #define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */ #define TOPBIT (1 << (WIDTH - 1)) /* MSB */ #define WIDTH 32 int GetFileHash(wchar_t *filename) { HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); 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, nullptr); /* 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(int _type, const char *_szName) : cacheType(_type), szProtoname(mir_strdup(_szName)) { } protoPicCacheEntry::~protoPicCacheEntry() { mir_free(szProtoname); if (hbmPic != nullptr) DeleteObject(hbmPic); } void protoPicCacheEntry::clear() { if (hbmPic != nullptr) 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 = { 300, 300 }; CallProtoService(proto, PS_GETAVATARCAPS, AF_MAXSIZE, (LPARAM)&maxSize); *width = maxSize.x; *height = maxSize.y; } 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)) return CallProtoService(proto, PS_GETAVATARCAPS, AF_DONTNEEDDELAYS, 0) <= 0; 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_IsFetchingWhenProtoNotVisibleAllowed(const char *proto) { if (ProtoServiceExists(proto, PS_GETAVATARCAPS)) return CallProtoService(proto, PS_GETAVATARCAPS, AF_FETCHIFPROTONOTVISIBLE, 0); return FALSE; } BOOL Proto_IsFetchingWhenContactOfflineAllowed(const char *proto) { if (ProtoServiceExists(proto, PS_GETAVATARCAPS)) return CallProtoService(proto, PS_GETAVATARCAPS, AF_FETCHIFCONTACTOFFLINE, 0); return FALSE; } ///////////////////////////////////////////////////////////////////////////////////////// protoPicCacheEntry *GetProtoDefaultAvatar(MCONTACT hContact) { char *szProto = Proto_GetBaseAccountName(hContact); if (szProto) for (auto &p : g_ProtoPictures) if (!mir_strcmp(p->szProtoname, szProto) && p->hbmPic != nullptr) return p; return nullptr; } MCONTACT GetContactThatHaveTheAvatar(MCONTACT hContact, int locked) { if (db_mc_isMeta(hContact)) { if (locked == -1) locked = db_get_b(hContact, "ContactPhoto", "Locked", 0); if (!locked) hContact = db_mc_getMostOnline(hContact); } return hContact; } int ChangeAvatar(MCONTACT 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 == nullptr) return 0; if (fNotifyHist) node->bNotify = true; node->pa_format = pa_format; if (fLoad) { PushAvatarRequest(node); SetEvent(hLoaderEvent); } else node->wipeInfo(); return 0; } void DeleteGlobalUserAvatar() { ptrW wszPath(g_plugin.getWStringA("GlobalUserAvatarFile")); if (!wszPath) return; wchar_t szFilename[MAX_PATH]; MyPathToAbsolute(wszPath, szFilename); DeleteFile(szFilename); g_plugin.delSetting("GlobalUserAvatarFile"); } void SetIgnoreNotify(char *protocol, BOOL ignore) { for (auto &it : g_MyAvatars) { if (protocol == nullptr || !mir_strcmp(it->szProtoname, protocol)) { if (ignore) it->dwFlags |= AVS_IGNORENOTIFY; else it->dwFlags &= ~AVS_IGNORENOTIFY; } } } /////////////////////////////////////////////////////////////////////////////////////////////////////// uint32_t GetFileSize(wchar_t *szFilename) { struct _stat info; return (_wstat(szFilename, &info) == -1) ? 0 : info.st_size; }