diff options
-rw-r--r-- | protocols/ICQ-WIM/src/proto.cpp | 31 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/proto.h | 22 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/server.cpp | 90 |
3 files changed, 109 insertions, 34 deletions
diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index e50442fd8b..0e5cb32fb0 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -393,6 +393,37 @@ int CIcqProto::AuthRequest(MCONTACT hContact, const wchar_t* szMessage) } //////////////////////////////////////////////////////////////////////////////////////// +// 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(); + + 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, 0); + + auto *ft = (IcqFileTransfer *)hTransfer; + delete ft; + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// // GetCaps - return protocol capabilities bits INT_PTR CIcqProto::GetCaps(int type, MCONTACT) diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index 95ea7b72bb..28b54f202a 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -127,6 +127,21 @@ struct IcqConn struct IcqFileTransfer : public MZeroedObject { + // create an object for receiving + 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)); + const wchar_t *p = wcsrchr(pwszFileName, '/'); + m_wszFileName = (p == nullptr) ? pwszFileName : p + 1; + m_wszShortName = m_wszShortName; + } + + // create an object for sending IcqFileTransfer(MCONTACT hContact, const wchar_t *pwszFileName) : m_wszFileName(pwszFileName) { @@ -229,7 +244,6 @@ class CIcqProto : public PROTO<CIcqProto> void SetServerStatus(int iNewStatus); void ShutdownSession(void); void StartSession(void); - void TryFetchFileInfo(CMStringW &wszText); void CheckAvatarChange(MCONTACT hContact, const JSONNode&); void CheckLastId(MCONTACT hContact, const JSONNode&); @@ -258,6 +272,7 @@ class CIcqProto : public PROTO<CIcqProto> void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); @@ -387,7 +402,10 @@ class CIcqProto : public PROTO<CIcqProto> int GetInfo(MCONTACT hContact, int infoType) override; 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; + HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override; int SendMsg(MCONTACT hContact, int flags, const char *msg) override; diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp index 265b6ba632..2f67f4b0d9 100644 --- a/protocols/ICQ-WIM/src/server.cpp +++ b/protocols/ICQ-WIM/src/server.cpp @@ -368,7 +368,7 @@ void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNo } else { wszText = it["text"].as_mstring(); - TryFetchFileInfo(wszText); + wszText.TrimRight(); } int iMsgTime = (bFromHistory) ? it["time"].as_int() : time(0); @@ -401,6 +401,15 @@ void CIcqProto::ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNo return; bool bIsOutgoing = it["outgoing"].as_bool(); + if (!bIsOutgoing && wszText.Left(26) == L"https://files.icq.net/get/") { + CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszText.Mid(26).c_str()); + auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo); + pReq->hContact = hContact; + pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "600"); + Push(pReq); + return; + } + ptrA szUtf(mir_utf8encodeW(wszText)); PROTORECVEVENT pre = {}; @@ -467,19 +476,6 @@ void CIcqProto::RetrieveUserInfo(MCONTACT hContact) Push(pReq); } -void CIcqProto::TryFetchFileInfo(CMStringW &wszText) -{ - wszText.TrimRight(); - - if (wszText.Left(26) == L"https://files.icq.net/get/") { - CMStringA szUrl(FORMAT, ICQ_FILE_SERVER "/info/%S/", wszText.Mid(26).c_str()); - auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnFileInfo); - pReq << CHAR_PARAM("aimsid", m_aimsid) << CHAR_PARAM("previews", "600"); - pReq->pUserInfo = &wszText; - ExecuteRequest(pReq); - } -} - AsyncHttpRequest* CIcqProto::UserInfoRequest(MCONTACT hContact) { auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/presence/get", &CIcqProto::OnGetUserInfo); @@ -738,9 +734,6 @@ void CIcqProto::OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld << CHAR_PARAM("offlineIM", "true") << WCHAR_PARAM("parts", wszParts) << WCHAR_PARAM("t", GetUserId(pTransfer->pfts.hContact)) << INT_PARAM("ts", TS()); Push(pReq); - // Send the same message to myself - TryFetchFileInfo(wszUrl); - T2Utf msgText(wszUrl); PROTORECVEVENT recv = {}; recv.flags = PREF_CREATEREAD | PREF_SENT; @@ -798,34 +791,67 @@ void CIcqProto::OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pOld) ProtoBroadcastAck(pTransfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, pTransfer, (LPARAM)&pTransfer->pfts); } +///////////////////////////////////////////////////////////////////////////////////////// + void CIcqProto::OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) { - CMStringW *wszText = (CMStringW *)pReq->pUserInfo; - RobustReply root(pReply); if (root.error() != 200) return; - wszText->Empty(); auto &data = root.result(); - CMStringW tmp(data["extra"]["file_type"].as_mstring()); - wszText->AppendFormat(L"%s\r\n", (tmp == "image") ? TranslateT("Image received:") : TranslateT("File received")); + std::string szUrl(data["info"]["dlink"].as_string()); + if (szUrl.empty()) + return; - tmp = data["info"]["file_name"].as_mstring(); - if (!tmp.IsEmpty()) - wszText->AppendFormat(L"%s: %s\r\n", TranslateT("File name"), tmp.c_str()); + mir_urlDecode(&*szUrl.begin()); + auto *ft = new IcqFileTransfer(pReq->hContact, szUrl.c_str()); + ft->pfts.totalBytes = ft->pfts.currentFileSize = 100; + ft->pfts.szCurrentFile.w = ft->m_wszFileName.GetBuffer(); + + PROTORECVFILE pre = { 0 }; + pre.dwFlags = PRFF_UNICODE; + pre.fileCount = 1; + pre.timestamp = time(0); + pre.files.w = &ft->m_wszShortName; + pre.lParam = (LPARAM)ft; + ProtoChainRecvFile(pReq->hContact, &pre); +} + +void CIcqProto::OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ + auto *ft = (IcqFileTransfer*)pReq->pUserInfo; + + if (pReply->resultCode != 200) { +LBL_Error: + FileCancel(pReq->hContact, ft); + return; + } - tmp = data["info"]["dlink"].as_mstring(); - if (!tmp.IsEmpty()) - wszText->AppendFormat(L"%s: %s\r\n", TranslateT("URL"), tmp.c_str()); + ft->pfts.totalProgress += 100; + ft->pfts.currentFileProgress += 100; + ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->pfts); - if (data["info"]["has_previews"].as_bool()) { - tmp = data["previews"]["600"].as_mstring(); - if (!tmp.IsEmpty()) - wszText->AppendFormat(L"%s: %s\r\n", TranslateT("Preview"), tmp.c_str()); + 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; } + + ProtoBroadcastAck(ft->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0); + delete ft; } +///////////////////////////////////////////////////////////////////////////////////////// + void CIcqProto::OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) { RobustReply root(pReply); |