From 4eba326a2e8356a02a03ecd3f07c773132a3bd0e Mon Sep 17 00:00:00 2001 From: George Hazan Date: Thu, 20 Jul 2023 21:25:40 +0300 Subject: =?UTF-8?q?fixes=20#3579=20(ICQ:=20=D1=83=D1=81=D1=82=D0=B0=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=B0=D1=8E=D1=82=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=B0=20=D0=BE=D1=84=D1=84=D0=BB=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- protocols/ICQ-WIM/ICQ-WIM.vcxproj | 1 + protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters | 3 + protocols/ICQ-WIM/src/avatars.cpp | 134 ++++++++++++++++++++++++++++++ protocols/ICQ-WIM/src/proto.cpp | 17 ++-- protocols/ICQ-WIM/src/proto.h | 3 +- protocols/ICQ-WIM/src/server.cpp | 82 +++++++++--------- protocols/ICQ-WIM/src/stdafx.h | 4 + protocols/ICQ-WIM/src/utils.cpp | 127 +++------------------------- protocols/ICQ-WIM/src/version.h | 2 +- 9 files changed, 209 insertions(+), 164 deletions(-) create mode 100644 protocols/ICQ-WIM/src/avatars.cpp (limited to 'protocols') diff --git a/protocols/ICQ-WIM/ICQ-WIM.vcxproj b/protocols/ICQ-WIM/ICQ-WIM.vcxproj index 53677eedfa..43a019b110 100644 --- a/protocols/ICQ-WIM/ICQ-WIM.vcxproj +++ b/protocols/ICQ-WIM/ICQ-WIM.vcxproj @@ -26,6 +26,7 @@ + diff --git a/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters b/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters index d52897b648..39587e6cee 100644 --- a/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters +++ b/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters @@ -44,6 +44,9 @@ Source Files + + Source Files + diff --git a/protocols/ICQ-WIM/src/avatars.cpp b/protocols/ICQ-WIM/src/avatars.cpp new file mode 100644 index 0000000000..c7576ca60c --- /dev/null +++ b/protocols/ICQ-WIM/src/avatars.cpp @@ -0,0 +1,134 @@ +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +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 version 2 +of the License. + +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, see . +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// Avatars + +void CIcqProto::GetAvatarFileName(MCONTACT hContact, wchar_t *pszDest, size_t cbLen) +{ + CMStringW wszPath(GetAvatarPath()); + wszPath += '\\'; + + CMStringW wszFileName(getMStringW(hContact, "IconId")); + const wchar_t *szFileType = ProtoGetAvatarExtension(getByte(hContact, "AvatarType", PA_FORMAT_PNG)); + wszPath.AppendFormat(L"%s%s", wszFileName.c_str(), szFileType); + + wcsncpy_s(pszDest, cbLen, wszPath, _TRUNCATE); +} + +INT_PTR __cdecl CIcqProto::GetAvatar(WPARAM wParam, LPARAM lParam) +{ + wchar_t *buf = (wchar_t *)wParam; + int size = (int)lParam; + if (buf == nullptr || size <= 0) + return -1; + + GetAvatarFileName(0, buf, size); + return 0; +} + +INT_PTR __cdecl CIcqProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) +{ + switch (wParam) { + case AF_MAXSIZE: + ((POINT *)lParam)->x = -1; + ((POINT *)lParam)->y = -1; + return 0; + + case AF_FORMATSUPPORTED: // nobody + return 1; + + case AF_DELAYAFTERFAIL: + return 10 * 60 * 1000; + + case AF_ENABLED: + case AF_FETCHIFPROTONOTVISIBLE: + case AF_FETCHIFCONTACTOFFLINE: + return 1; + } + return 0; +} + +INT_PTR __cdecl CIcqProto::GetAvatarInfo(WPARAM, LPARAM lParam) +{ + PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam; + + ptrW szIconId(getWStringA(pai->hContact, "IconId")); + if (szIconId == nullptr) { + debugLogA("No avatar"); + return GAIR_NOAVATAR; + } + + GetAvatarFileName(pai->hContact, pai->filename, _countof(pai->filename)); + pai->format = getByte(pai->hContact, "AvatarType", 0); + + if (::_waccess(pai->filename, 0) == 0) + return GAIR_SUCCESS; + + debugLogA("No avatar"); + return GAIR_NOAVATAR; +} + +INT_PTR __cdecl CIcqProto::SetAvatar(WPARAM, LPARAM lParam) +{ + wchar_t *pwszFileName = (wchar_t *)lParam; + + wchar_t wszOldName[MAX_PATH]; + GetAvatarFileName(0, wszOldName, _countof(wszOldName)); + _wremove(wszOldName); + + auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/expressions/upload"); + pReq->m_szUrl.AppendFormat("?f=json&aimsid=%s&r=%s&type=largeBuddyIcon", mir_urlEncode(m_aimsid.c_str()).c_str(), pReq->m_reqId); + + if (pwszFileName == nullptr) + delSetting("AvatarHash"); + else { + int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD); + if (fileId < 0) { + delete pReq; + return 1; + } + + unsigned dwSize = (unsigned)_filelengthi64(fileId); + char *pData = (char *)mir_alloc(dwSize); + if (pData == nullptr) { + _close(fileId); + delete pReq; + return 2; + } + + _read(fileId, pData, dwSize); + _close(fileId); + + pReq->pData = pData; + pReq->dataLength = dwSize; + + int iAvatarType = ProtoGetBufferFormat(pData); + if (iAvatarType == PA_FORMAT_UNKNOWN) { + delete pReq; + delete pData; + return 3; + } + + pReq->AddHeader("Content-Type", ProtoGetAvatarMimeType(iAvatarType)); + } + Push(pReq); + + return 0; // TODO +} diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index a4b3207367..91db22a652 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -172,7 +172,7 @@ void CIcqProto::OnContactDeleted(MCONTACT hContact) void CIcqProto::OnCreateOfflineFile(DB::FILE_BLOB &blob, void *hTransfer) { if (auto *pFileInfo = (IcqFileInfo *)hTransfer) { - blob.setUrl(pFileInfo->szUrl); + blob.setUrl(pFileInfo->szOrigUrl); blob.setSize(pFileInfo->dwFileSize); } } @@ -216,12 +216,15 @@ void __cdecl CIcqProto::OfflineFileThread(void *pParam) if (dbei && !strcmp(dbei.szModule, m_szModuleName) && dbei.eventType == EVENTTYPE_FILE) { JSONNode root = JSONNode::parse((const char *)dbei.pBlob); if (m_bOnline && root) { - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, root["u"].as_string().c_str(), &CIcqProto::OnFileRecv); - pReq->pUserInfo = ofd; - pReq->AddHeader("Sec-Fetch-User", "?1"); - pReq->AddHeader("Sec-Fetch-Site", "cross-site"); - pReq->AddHeader("Sec-Fetch-Mode", "navigate"); - Push(pReq); + MCONTACT hContact = db_event_getContact(ofd->hDbEvent); + if (auto *pFileInfo = RetrieveFileInfo(hContact, fileText2url(root["u"].as_mstring()))) { + auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, pFileInfo->szUrl, &CIcqProto::OnFileRecv); + pReq->pUserInfo = ofd; + pReq->AddHeader("Sec-Fetch-User", "?1"); + pReq->AddHeader("Sec-Fetch-Site", "cross-site"); + pReq->AddHeader("Sec-Fetch-Mode", "navigate"); + Push(pReq); + } return; } } diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index a10f27c065..edac303ac1 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -78,7 +78,7 @@ struct IcqFileInfo dwFileSize(dwSize) {} - CMStringA szUrl; + CMStringA szUrl, szOrigUrl; CMStringW wszDescr; uint32_t dwFileSize; bool bIsSticker = false; @@ -232,6 +232,7 @@ class CIcqProto : public PROTO void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false); void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime); + IcqFileInfo* RetrieveFileInfo(MCONTACT hContact, const CMStringW &wszUrl); int StatusFromPresence(const JSONNode &presence, MCONTACT hContact); void ProcessStatus(IcqUser *pUser, int iStatus); diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp index 6a1eb0cbef..625aba8612 100644 --- a/protocols/ICQ-WIM/src/server.cpp +++ b/protocols/ICQ-WIM/src/server.cpp @@ -160,50 +160,56 @@ void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) res[0]->bIsSticker = bIsSticker; } -IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile) +IcqFileInfo *CIcqProto::RetrieveFileInfo(MCONTACT hContact, const CMStringW &wszUrl) { - CMStringW wszUrl(wszText.Mid(26)); - int idx = wszUrl.Find(' '); - if (idx != -1) - wszUrl.Truncate(idx); + IcqFileInfo *pFileInfo = nullptr; + CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszUrl.c_str()); + auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo); + pReq->hContact = hContact; + pReq->pUserInfo = &pFileInfo; + pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "192,600,xlarge"); + if (!ExecuteRequest(pReq)) + return nullptr; + return pFileInfo; +} + +IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile) +{ bIsFile = false; - IcqFileInfo *pFileInfo = nullptr; + CMStringW wszUrl(fileText2url(wszText)); // is it already downloaded sticker? CMStringW wszLoadedPath(FORMAT, L"%s\\%S\\Stickers\\STK{%s}.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName, wszUrl.c_str()); if (!_waccess(wszLoadedPath, 0)) { - pFileInfo = (IcqFileInfo *)this; wszText.Format(L"STK{%s}", wszUrl.c_str()); + return (IcqFileInfo *)this; } - else { - // download file info - CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszUrl.c_str()); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo); - pReq->hContact = hContact; - pReq->pUserInfo = &pFileInfo; - pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "192,600,xlarge"); - if (!ExecuteRequest(pReq)) - return nullptr; - - // is it a sticker? - if (pFileInfo && pFileInfo->bIsSticker) { - if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) { - auto *pNew = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, pFileInfo->szUrl, &CIcqProto::OnGetSticker); - pNew->flags |= NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; - pNew->pUserInfo = wszUrl.GetBuffer(); - pNew->AddHeader("Sec-Fetch-User", "?1"); - pNew->AddHeader("Sec-Fetch-Site", "cross-site"); - pNew->AddHeader("Sec-Fetch-Mode", "navigate"); - if (!ExecuteRequest(pNew)) - return nullptr; - - wszText.Format(L"STK{%s}", wszUrl.c_str()); - delete pFileInfo; - } - else wszText = TranslateT("SmileyAdd plugin required to support stickers"); + + // download file info + auto *pFileInfo = RetrieveFileInfo(hContact, wszUrl); + if (!pFileInfo) + return nullptr; + + // is it a sticker? + if (pFileInfo->bIsSticker) { + if (ServiceExists(MS_SMILEYADD_LOADCONTACTSMILEYS)) { + auto *pNew = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, pFileInfo->szUrl, &CIcqProto::OnGetSticker); + pNew->flags |= NLHRF_NODUMP | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; + pNew->pUserInfo = wszUrl.GetBuffer(); + pNew->AddHeader("Sec-Fetch-User", "?1"); + pNew->AddHeader("Sec-Fetch-Site", "cross-site"); + pNew->AddHeader("Sec-Fetch-Mode", "navigate"); + if (!ExecuteRequest(pNew)) + return nullptr; + + wszText.Format(L"STK{%s}", wszUrl.c_str()); } - else bIsFile = true; + else wszText = TranslateT("SmileyAdd plugin required to support stickers"); + } + else { + pFileInfo->szOrigUrl = wszText; + bIsFile = true; } return pFileInfo; @@ -522,10 +528,10 @@ void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNo } bool bIsOutgoing = it["outgoing"].as_bool(), bIsFileTransfer = false; - IcqFileInfo *pFileInfo = nullptr; + std::unique_ptr pFileInfo = nullptr; if (!bCreateRead && !bIsOutgoing && wszText.Left(26) == L"https://files.icq.net/get/") { - pFileInfo = CheckFile(hContact, wszText, bIsFileTransfer); + pFileInfo.reset(CheckFile(hContact, wszText, bIsFileTransfer)); if (!pFileInfo) { debugLogA("Some shit happened, report this case to developers"); return; @@ -571,10 +577,8 @@ void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNo pre.timestamp = iMsgTime; pre.files.w = &m_wszShortName; pre.descr.w = pFileInfo->wszDescr; - pre.lParam = (LPARAM)pFileInfo; + pre.lParam = (LPARAM)pFileInfo.release(); ProtoChainRecvFile(hContact, &pre); - - delete pFileInfo; return; } diff --git a/protocols/ICQ-WIM/src/stdafx.h b/protocols/ICQ-WIM/src/stdafx.h index b45810a867..e6b9d0020b 100644 --- a/protocols/ICQ-WIM/src/stdafx.h +++ b/protocols/ICQ-WIM/src/stdafx.h @@ -32,6 +32,8 @@ // Windows includes #include +#include + // Standard includes #include #include @@ -106,4 +108,6 @@ bool IsValidType(const JSONNode &aimid); void RefreshGroups(void); wchar_t* time2text(time_t time); +CMStringW fileText2url(const CMStringW &wszText); + extern bool g_bSecureIM, g_bMessageState; diff --git a/protocols/ICQ-WIM/src/utils.cpp b/protocols/ICQ-WIM/src/utils.cpp index 5adde04e81..fbe99c6ad6 100644 --- a/protocols/ICQ-WIM/src/utils.cpp +++ b/protocols/ICQ-WIM/src/utils.cpp @@ -147,122 +147,6 @@ void CIcqProto::Json2string(MCONTACT hContact, const JSONNode &node, const char delSetting(hContact, szSetting); } -///////////////////////////////////////////////////////////////////////////////////////// -// Avatars - -void CIcqProto::GetAvatarFileName(MCONTACT hContact, wchar_t* pszDest, size_t cbLen) -{ - CMStringW wszPath(GetAvatarPath()); - wszPath += '\\'; - - CMStringW wszFileName(getMStringW(hContact, "IconId")); - const wchar_t* szFileType = ProtoGetAvatarExtension(getByte(hContact, "AvatarType", PA_FORMAT_PNG)); - wszPath.AppendFormat(L"%s%s", wszFileName.c_str(), szFileType); - - wcsncpy_s(pszDest, cbLen, wszPath, _TRUNCATE); -} - -INT_PTR __cdecl CIcqProto::GetAvatar(WPARAM wParam, LPARAM lParam) -{ - wchar_t *buf = (wchar_t*)wParam; - int size = (int)lParam; - if (buf == nullptr || size <= 0) - return -1; - - GetAvatarFileName(0, buf, size); - return 0; -} - -INT_PTR __cdecl CIcqProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case AF_MAXSIZE: - ((POINT*)lParam)->x = -1; - ((POINT*)lParam)->y = -1; - return 0; - - case AF_FORMATSUPPORTED: // nobody - return 1; - - case AF_DELAYAFTERFAIL: - return 10 * 60 * 1000; - - case AF_ENABLED: - case AF_FETCHIFPROTONOTVISIBLE: - case AF_FETCHIFCONTACTOFFLINE: - return 1; - } - return 0; -} - -INT_PTR __cdecl CIcqProto::GetAvatarInfo(WPARAM, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATION* pai = (PROTO_AVATAR_INFORMATION*)lParam; - - ptrW szIconId(getWStringA(pai->hContact, "IconId")); - if (szIconId == nullptr) { - debugLogA("No avatar"); - return GAIR_NOAVATAR; - } - - GetAvatarFileName(pai->hContact, pai->filename, _countof(pai->filename)); - pai->format = getByte(pai->hContact, "AvatarType", 0); - - if (::_waccess(pai->filename, 0) == 0) - return GAIR_SUCCESS; - - debugLogA("No avatar"); - return GAIR_NOAVATAR; -} - -INT_PTR __cdecl CIcqProto::SetAvatar(WPARAM, LPARAM lParam) -{ - wchar_t* pwszFileName = (wchar_t*)lParam; - - wchar_t wszOldName[MAX_PATH]; - GetAvatarFileName(0, wszOldName, _countof(wszOldName)); - _wremove(wszOldName); - - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, ICQ_API_SERVER "/expressions/upload"); - pReq->m_szUrl.AppendFormat("?f=json&aimsid=%s&r=%s&type=largeBuddyIcon", mir_urlEncode(m_aimsid.c_str()).c_str(), pReq->m_reqId); - - if (pwszFileName == nullptr) - delSetting("AvatarHash"); - else { - int fileId = _wopen(pwszFileName, _O_RDONLY | _O_BINARY, _S_IREAD); - if (fileId < 0) { - delete pReq; - return 1; - } - - unsigned dwSize = (unsigned)_filelengthi64(fileId); - char* pData = (char*)mir_alloc(dwSize); - if (pData == nullptr) { - _close(fileId); - delete pReq; - return 2; - } - - _read(fileId, pData, dwSize); - _close(fileId); - - pReq->pData = pData; - pReq->dataLength = dwSize; - - int iAvatarType = ProtoGetBufferFormat(pData); - if (iAvatarType == PA_FORMAT_UNKNOWN) { - delete pReq; - delete pData; - return 3; - } - - pReq->AddHeader("Content-Type", ProtoGetAvatarMimeType(iAvatarType)); - } - Push(pReq); - - return 0; // TODO -} - ///////////////////////////////////////////////////////////////////////////////////////// CMStringW CIcqProto::GetUserId(MCONTACT hContact) @@ -361,6 +245,17 @@ void CIcqProto::setId(MCONTACT hContact, const char *szSetting, __int64 iValue) ///////////////////////////////////////////////////////////////////////////////////////// +CMStringW fileText2url(const CMStringW &wszText) +{ + CMStringW wszUrl(wszText.Mid(26)); + int idx = wszUrl.Find(' '); + if (idx != -1) + wszUrl.Truncate(idx); + return wszUrl; +} + +///////////////////////////////////////////////////////////////////////////////////////// + wchar_t* time2text(time_t ts) { if (ts == 0) diff --git a/protocols/ICQ-WIM/src/version.h b/protocols/ICQ-WIM/src/version.h index 13d762b563..bc77648b0b 100644 --- a/protocols/ICQ-WIM/src/version.h +++ b/protocols/ICQ-WIM/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0 #define __MINOR_VERSION 96 #define __RELEASE_NUM 3 -#define __BUILD_NUM 3 +#define __BUILD_NUM 4 #include -- cgit v1.2.3