From d9cdbf7eba8b496b7325c736fdc7dbd2afc55a29 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 10 Jul 2023 20:11:11 +0300 Subject: fixes #3567 (ICQ: implement offline files download) --- protocols/ICQ-WIM/src/file.cpp | 17 ------ protocols/ICQ-WIM/src/proto.cpp | 115 +++++++++++++++++++---------------- protocols/ICQ-WIM/src/proto.h | 16 ++--- protocols/ICQ-WIM/src/server.cpp | 125 ++++++++++++++------------------------- 4 files changed, 116 insertions(+), 157 deletions(-) (limited to 'protocols') diff --git a/protocols/ICQ-WIM/src/file.cpp b/protocols/ICQ-WIM/src/file.cpp index 76b55026f9..1dc58ba06b 100644 --- a/protocols/ICQ-WIM/src/file.cpp +++ b/protocols/ICQ-WIM/src/file.cpp @@ -17,23 +17,6 @@ along with this program. If not, see . #include "stdafx.h" -// create an object for receiving -IcqFileTransfer::IcqFileTransfer(MCONTACT hContact, const char *pszUrl) : - m_szHost(pszUrl) -{ - pfts.hContact = hContact; - pfts.totalFiles = 1; - pfts.flags = PFTS_UNICODE | PFTS_RECEIVING; - - ptrW pwszFileName(mir_utf8decodeW(pszUrl)); - if (pwszFileName == nullptr) - pwszFileName = mir_a2u(pszUrl); - - const wchar_t *p = wcsrchr(pwszFileName, '/'); - m_wszFileName = (p == nullptr) ? pwszFileName : p + 1; - m_wszShortName = m_wszFileName; -} - // create an object for sending IcqFileTransfer::IcqFileTransfer(MCONTACT hContact, const wchar_t *pwszFileName) : m_wszFileName(pwszFileName) diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index 9cade4f8d9..09496b46c4 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -74,6 +74,9 @@ CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) : CreateProtoService(PS_GETUNREADEMAILCOUNT, &CIcqProto::GetEmailCount); CreateProtoService(PS_GOTO_INBOX, &CIcqProto::GotoInbox); + // offline file transfer + CreateProtoService(PS_OFFLINEFILE, &CIcqProto::SvcOfflineFile); + // events HookProtoEvent(ME_CLIST_GROUPCHANGE, &CIcqProto::OnGroupChange); HookProtoEvent(ME_GC_EVENT, &CIcqProto::GroupchatEventHook); @@ -165,11 +168,73 @@ void CIcqProto::OnContactDeleted(MCONTACT hContact) << AIMSID(this) << WCHAR_PARAM("buddy", szId) << INT_PARAM("allGroups", 1)); } +void CIcqProto::OnCreateOfflineFile(DB::FILE_BLOB &blob, void *hTransfer) +{ + if (auto *pFileInfo = (IcqFileInfo *)hTransfer) { + blob.setUrl(pFileInfo->szUrl); + blob.setSize(pFileInfo->dwFileSize); + } +} + void CIcqProto::OnEventEdited(MCONTACT, MEVENT) { } +///////////////////////////////////////////////////////////////////////////////////////// + +void CIcqProto::OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ + if (pReply->resultCode != 200) + return; + + auto *ofd = (OFDTHREAD *)pReq->pUserInfo; + debugLogW(L"Saving to [%s]", ofd->wszPath.c_str()); + int fileId = _wopen(ofd->wszPath, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); + if (fileId == -1) { + debugLogW(L"Cannot open [%s] for writing", ofd->wszPath.c_str()); + return; + } + + int result = _write(fileId, pReply->pData, pReply->dataLength); + _close(fileId); + if (result != pReply->dataLength) { + debugLogW(L"Error writing data into [%s]", ofd->wszPath.c_str()); + return; + } + + delete ofd; +} + +void __cdecl CIcqProto::OfflineFileThread(void *pParam) +{ + auto *ofd = (OFDTHREAD *)pParam; + + DB::EventInfo dbei(ofd->hDbEvent); + 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); + return; + } + } + + delete ofd; +} + +INT_PTR __cdecl CIcqProto::SvcOfflineFile(WPARAM param, LPARAM) +{ + ForkThread((MyThreadFunc)&CIcqProto::OfflineFileThread, (void *)param); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + INT_PTR CIcqProto::OnMenuLoadHistory(WPARAM hContact, LPARAM) { delSetting(hContact, DB_KEY_LASTMSGID); @@ -340,56 +405,6 @@ int CIcqProto::AuthRequest(MCONTACT hContact, const wchar_t* szMessage) return 0; } -//////////////////////////////////////////////////////////////////////////////////////// -// File operations - -HANDLE CIcqProto::FileAllow(MCONTACT, HANDLE hTransfer, const wchar_t *pwszSavePath) -{ - if (!m_bOnline) - return nullptr; - - auto *ft = (IcqFileTransfer *)hTransfer; - ft->m_wszFileName.Insert(0, pwszSavePath); - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); - ft->Acquire(); - - auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_GET, ft->m_szHost, &CIcqProto::OnFileRecv); - pReq->pUserInfo = ft; - pReq->AddHeader("Sec-Fetch-User", "?1"); - pReq->AddHeader("Sec-Fetch-Site", "cross-site"); - pReq->AddHeader("Sec-Fetch-Mode", "navigate"); - Push(pReq); - - return hTransfer; -} - -int CIcqProto::FileCancel(MCONTACT hContact, HANDLE hTransfer) -{ - ProtoBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, hTransfer); - - auto *ft = (IcqFileTransfer *)hTransfer; - if (ft->pfts.currentFileTime != 0) - ft->m_bCanceled = true; - else - ft->Release(); - return 0; -} - -int CIcqProto::FileResume(HANDLE hTransfer, int, const wchar_t *szFilename) -{ - auto *ft = (IcqFileTransfer *)hTransfer; - if (!m_bOnline || ft == nullptr) - return 1; - - if (szFilename != nullptr) { - ft->m_wszFileName = szFilename; - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); - } - - ::SetEvent(ft->hWaitEvent); - return 0; -} - //////////////////////////////////////////////////////////////////////////////////////// // GetCaps - return protocol capabilities bits diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index b7cac8f5c4..018a4e1399 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -144,7 +144,7 @@ struct IcqConn int lastTs, timeout; }; -struct IcqFileTransfer : public MZeroedObject, public MShareable +struct IcqFileTransfer : public MZeroedObject { bool m_bCanceled = false, m_bStarted = false; int m_fileId = -1; @@ -152,15 +152,11 @@ struct IcqFileTransfer : public MZeroedObject, public MShareable CMStringW m_wszFileName, m_wszDescr; const wchar_t *m_wszShortName; PROTOFILETRANSFERSTATUS pfts; - HANDLE hWaitEvent; - - // create an object for receiving - IcqFileTransfer(MCONTACT hContact, const char *pszUrl); // create an object for sending IcqFileTransfer(MCONTACT hContact, const wchar_t *pwszFileName); - ~IcqFileTransfer() override; + ~IcqFileTransfer(); void FillHeaders(AsyncHttpRequest *pReq); }; @@ -353,6 +349,7 @@ class CIcqProto : public PROTO HANDLE m_hWorkerThread; void __cdecl ServerThread(void*); void __cdecl PollThread(void*); + void __cdecl OfflineFileThread(void*); //////////////////////////////////////////////////////////////////////////////////////// // services @@ -362,6 +359,8 @@ class CIcqProto : public PROTO INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM); INT_PTR __cdecl SetAvatar(WPARAM, LPARAM); + INT_PTR __cdecl SvcOfflineFile(WPARAM, LPARAM); + INT_PTR __cdecl EditGroups(WPARAM, LPARAM); INT_PTR __cdecl EditProfile(WPARAM, LPARAM); INT_PTR __cdecl GetEmailCount(WPARAM, LPARAM); @@ -390,10 +389,6 @@ class CIcqProto : public PROTO HANDLE SearchBasic(const wchar_t *id) override; - HANDLE FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath) override; - int FileCancel(MCONTACT hContact, HANDLE hTransfer) override; - int FileResume(HANDLE hTransfer, int action, const wchar_t *szFilename) override; - HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override; int SendMsg(MCONTACT hContact, int flags, const char *msg) override; @@ -406,6 +401,7 @@ class CIcqProto : public PROTO void OnContactAdded(MCONTACT) override; void OnContactDeleted(MCONTACT) override; MWindow OnCreateAccMgrUI(MWindow) override; + void OnCreateOfflineFile(DB::FILE_BLOB &blob, void *ft) override; void OnEventEdited(MCONTACT, MEVENT) override; void OnMarkRead(MCONTACT, MEVENT) override; void OnModulesLoaded() override; diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp index e8f360144c..6a1eb0cbef 100644 --- a/protocols/ICQ-WIM/src/server.cpp +++ b/protocols/ICQ-WIM/src/server.cpp @@ -124,6 +124,42 @@ void CIcqProto::CheckPassword() } } +///////////////////////////////////////////////////////////////////////////////////////// + +void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ + IcqFileInfo **res = (IcqFileInfo **)pReq->pUserInfo; + *res = nullptr; + + RobustReply root(pReply); + if (root.error() != 200) + return; + + auto &pData = root.result(); + auto &pInfo = pData["info"]; + std::string szUrl(pInfo["dlink"].as_string()); + if (szUrl.empty()) + return; + + OnMarkRead(pReq->hContact, 0); + + bool bIsSticker; + CMStringW wszDescr(pInfo["file_name"].as_mstring()); + if (!mir_wstrncmp(wszDescr, L"dnld", 4)) { + bIsSticker = true; + + std::string szPreview = pData["previews"]["192"].as_string(); + if (!szPreview.empty()) + szUrl = szPreview; + } + else bIsSticker = false; + + mir_urlDecode(&*szUrl.begin()); + + *res = new IcqFileInfo(szUrl, wszDescr, pInfo["file_size"].as_int()); + res[0]->bIsSticker = bIsSticker; +} + IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile) { CMStringW wszUrl(wszText.Mid(26)); @@ -522,17 +558,20 @@ void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNo // convert a file info into Miranda's file transfer if (bIsFileTransfer) { - auto *ft = new IcqFileTransfer(hContact, pFileInfo->szUrl); - ft->pfts.totalBytes = ft->pfts.currentFileSize = pFileInfo->dwFileSize; - ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); + ptrW pwszFileName(mir_utf8decodeW(pFileInfo->szUrl)); + if (pwszFileName == nullptr) + pwszFileName = mir_a2u(pFileInfo->szUrl); + + const wchar_t *p = wcsrchr(pwszFileName, '/'); + const wchar_t *m_wszShortName = (p == nullptr) ? pwszFileName : p + 1; PROTORECVFILE pre = {}; - pre.dwFlags = PRFF_UNICODE; + pre.dwFlags = PRFF_UNICODE | PRFF_SILENT; pre.fileCount = 1; pre.timestamp = iMsgTime; - pre.files.w = &ft->m_wszShortName; + pre.files.w = &m_wszShortName; pre.descr.w = pFileInfo->wszDescr; - pre.lParam = (LPARAM)ft; + pre.lParam = (LPARAM)pFileInfo; ProtoChainRecvFile(hContact, &pre); delete pFileInfo; @@ -943,80 +982,6 @@ void CIcqProto::OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, LPARAM(&cont)); } -///////////////////////////////////////////////////////////////////////////////////////// -// File info request - -void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - IcqFileInfo **res = (IcqFileInfo **)pReq->pUserInfo; - *res = nullptr; - - RobustReply root(pReply); - if (root.error() != 200) - return; - - auto &pData = root.result(); - auto &pInfo = pData["info"] ; - std::string szUrl(pInfo["dlink"].as_string()); - if (szUrl.empty()) - return; - - OnMarkRead(pReq->hContact, 0); - - bool bIsSticker; - CMStringW wszDescr(pInfo["file_name"].as_mstring()); - if (!mir_wstrncmp(wszDescr, L"dnld", 4)) { - bIsSticker = true; - - std::string szPreview = pData["previews"]["192"].as_string(); - if (!szPreview.empty()) - szUrl = szPreview; - } - else bIsSticker = false; - - mir_urlDecode(&*szUrl.begin()); - - *res = new IcqFileInfo(szUrl, wszDescr, pInfo["file_size"].as_int()); - res[0]->bIsSticker = bIsSticker; -} - -void CIcqProto::OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - auto *ft = (IcqFileTransfer*)pReq->pUserInfo; - - if (pReply->resultCode != 200) { -LBL_Error: - FileCancel(pReq->hContact, ft); - return; - } - - ft->hWaitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&ft->pfts)) - WaitForSingleObject(ft->hWaitEvent, INFINITE); - CloseHandle(ft->hWaitEvent); - - debugLogW(L"Saving to [%s]", ft->pfts.szCurrentFile.w); - int fileId = _wopen(ft->pfts.szCurrentFile.w, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); - if (fileId == -1) { - debugLogW(L"Cannot open [%s] for writing", ft->pfts.szCurrentFile.w); - goto LBL_Error; - } - - int result = _write(fileId, pReply->pData, pReply->dataLength); - _close(fileId); - if (result != pReply->dataLength) { - debugLogW(L"Error writing data into [%s]", ft->pfts.szCurrentFile.w); - goto LBL_Error; - } - - ft->pfts.totalProgress += pReply->dataLength; - ft->pfts.currentFileProgress += pReply->dataLength; - ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->pfts); - - ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft); - delete ft; -} - ///////////////////////////////////////////////////////////////////////////////////////// void CIcqProto::OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) -- cgit v1.2.3