diff options
-rw-r--r-- | protocols/WhatsAppWeb/src/avatars.cpp | 83 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/db.h | 2 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/main.cpp | 9 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/proto.cpp | 7 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/proto.h | 18 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/server.cpp | 11 |
6 files changed, 109 insertions, 21 deletions
diff --git a/protocols/WhatsAppWeb/src/avatars.cpp b/protocols/WhatsAppWeb/src/avatars.cpp index fb51e03c79..fe84739d2e 100644 --- a/protocols/WhatsAppWeb/src/avatars.cpp +++ b/protocols/WhatsAppWeb/src/avatars.cpp @@ -7,6 +7,39 @@ Copyright © 2019-21 George Hazan #include "stdafx.h" +void WhatsAppProto::OnGetAvatarInfo(const JSONNode &root, void *pUserInfo) +{ + if (!root) return; + + MCONTACT hContact = (UINT_PTR)pUserInfo; + + PROTO_AVATAR_INFORMATION ai = {}; + ai.hContact = hContact; + ai.format = PA_FORMAT_JPEG; + wcsncpy_s(ai.filename, GetAvatarFileName(hContact), _TRUNCATE); + + DWORD dwLastChangeTime = _wtoi(root["tag"].as_mstring()); + + CMStringA szUrl(root["eurl"].as_mstring()); + if (szUrl.IsEmpty()) { + setDword(hContact, DBKEY_AVATAR_TAG, 0); // avatar doesn't exist, don't check it later + +LBL_Error: + ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai)); + return; + } + + // if avatar was changed or not present at all, download it + if (dwLastChangeTime > getDword(hContact, DBKEY_AVATAR_TAG)) { + if (!g_plugin.SaveFile(szUrl, ai)) + goto LBL_Error; + + // set timestamp of avatar being saved + setDword(hContact, DBKEY_AVATAR_TAG, dwLastChangeTime); + } + ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai)); +} + INT_PTR WhatsAppProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam) { PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION*)lParam; @@ -19,10 +52,10 @@ INT_PTR WhatsAppProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam) wcsncpy_s(pai->filename, tszFileName.c_str(), _TRUNCATE); pai->format = PA_FORMAT_JPEG; - ptrA szAvatarId(getStringA(pai->hContact, DBKEY_AVATAR_ID)); - if (szAvatarId == NULL || (wParam & GAIF_FORCE) != 0) + DWORD dwTag = getDword(pai->hContact, DBKEY_AVATAR_TAG, -1); + if (dwTag == -1 || (wParam & GAIF_FORCE) != 0) if (pai->hContact != NULL && isOnline()) { - // m_pConnection->sendGetPicture(id, "image"); + WSSend(CMStringA(FORMAT, "[\"query\",\"ProfilePicThumb\",\"%s\"]", id.get()), &WhatsAppProto::OnGetAvatarInfo, (void*)pai->hContact); return GAIR_WAITFOR; } @@ -30,6 +63,8 @@ INT_PTR WhatsAppProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam) return GAIR_NOAVATAR; } +///////////////////////////////////////////////////////////////////////////////////////// + INT_PTR WhatsAppProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam) { switch (wParam) { @@ -79,3 +114,45 @@ INT_PTR WhatsAppProto::SetMyAvatar(WPARAM, LPARAM) { return 0; } + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CMPlugin::SaveFile(const char *pszUrl, PROTO_AVATAR_INFORMATION &ai) +{ + NETLIBHTTPREQUEST req = {}; + req.cbSize = sizeof(req); + req.flags = NLHRF_NODUMP | NLHRF_PERSISTENT | NLHRF_SSL | NLHRF_HTTP11 | NLHRF_REDIRECT; + req.requestType = REQUEST_GET; + req.szUrl = (char*)pszUrl; + req.nlc = hAvatarConn; + + NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(hAvatarUser, &req); + if (pReply == nullptr) { + hAvatarConn = nullptr; + debugLogA("Failed to retrieve avatar from url: %s", pszUrl); + return false; + } + + hAvatarConn = pReply->nlc; + + bool bSuccess = false; + if (pReply->resultCode == 200 && pReply->pData && pReply->dataLength) { + if (auto *pszHdr = Netlib_GetHeader(pReply, "Content-Type")) + ai.format = ProtoGetAvatarFormatByMimeType(pszHdr); + + if (ai.format != PA_FORMAT_UNKNOWN) { + FILE *fout = _wfopen(ai.filename, L"wb"); + if (fout) { + fwrite(pReply->pData, 1, pReply->dataLength, fout); + fclose(fout); + bSuccess = true; + } + else debugLogA("Error saving avatar to file %S", ai.filename); + } + else debugLogA("unknown avatar mime type"); + } + else debugLogA("Error %d reading avatar from url: %s", pReply->resultCode, pszUrl); + + Netlib_FreeHttpRequest(pReply); + return bSuccess; +} diff --git a/protocols/WhatsAppWeb/src/db.h b/protocols/WhatsAppWeb/src/db.h index 90f91429f3..553375df9a 100644 --- a/protocols/WhatsAppWeb/src/db.h +++ b/protocols/WhatsAppWeb/src/db.h @@ -26,7 +26,7 @@ Copyright © 2019-21 George Hazan #define DBKEY_NICK "Nick" #define DBKEY_DEF_GROUP "DefaultGroup" #define DBKEY_AUTORUNCHATS "AutoRunChats" -#define DBKEY_AVATAR_ID "AvatarId" +#define DBKEY_AVATAR_TAG "AvatarTag" #define DBKEY_EVENT_CLIENT_COLBACK "PopupClientColorBack" #define DBKEY_EVENT_CLIENT_COLTEXT "PopupClientColorText" diff --git a/protocols/WhatsAppWeb/src/main.cpp b/protocols/WhatsAppWeb/src/main.cpp index 852e55b3af..4cbdf78ccb 100644 --- a/protocols/WhatsAppWeb/src/main.cpp +++ b/protocols/WhatsAppWeb/src/main.cpp @@ -79,6 +79,12 @@ int CMPlugin::Load() // InitIcons(); // InitContactMenus(); + NETLIBUSER nlu = {}; + nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; + nlu.szSettingsModule = "WhatsApp"; + nlu.szDescriptiveName.w = TranslateT("WhatsApp HTTP connection"); + hAvatarUser = Netlib_RegisterUser(&nlu); + ////////////////////////////////////////////////////////////////////////////////////// signal_context_create(&pCtx, nullptr); @@ -99,6 +105,9 @@ int CMPlugin::Load() int CMPlugin::Unload() { + Netlib_CloseHandle(hAvatarConn); + Netlib_CloseHandle(hAvatarUser); + signal_context_destroy(pCtx); return 0; } diff --git a/protocols/WhatsAppWeb/src/proto.cpp b/protocols/WhatsAppWeb/src/proto.cpp index df5993592a..34ba75a243 100644 --- a/protocols/WhatsAppWeb/src/proto.cpp +++ b/protocols/WhatsAppWeb/src/proto.cpp @@ -72,11 +72,6 @@ WhatsAppProto::WhatsAppProto(const char *proto_name, const wchar_t *username) : nlu.szSettingsModule = m_szModuleName; nlu.szDescriptiveName.w = descr; m_hNetlibUser = Netlib_RegisterUser(&nlu); - if (m_hNetlibUser == nullptr) { - wchar_t error[200]; - mir_snwprintf(error, TranslateT("Unable to initialize Netlib for %s."), m_tszUserName); - MessageBox(nullptr, error, L"Miranda NG", MB_OK | MB_ICONERROR); - } m_tszAvatarFolder = CMStringW(VARSW(L"%miranda_avatarcache%")) + L"\\" + m_tszUserName; DWORD dwAttributes = GetFileAttributes(m_tszAvatarFolder.c_str()); @@ -204,7 +199,7 @@ int WhatsAppProto::SetStatus(int new_status) ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnSendMessage(const JSONNode &node) +void WhatsAppProto::OnSendMessage(const JSONNode &node, void*) { CMStringA szPrefix= node["$id$"].as_mstring(); diff --git a/protocols/WhatsAppWeb/src/proto.h b/protocols/WhatsAppWeb/src/proto.h index a615d4a8d2..f3e574b804 100644 --- a/protocols/WhatsAppWeb/src/proto.h +++ b/protocols/WhatsAppWeb/src/proto.h @@ -9,7 +9,7 @@ Copyright © 2019-21 George Hazan #define PROTO_H class WhatsAppProto; -typedef void (WhatsAppProto:: *WA_PKT_HANDLER)(const JSONNode &node); +typedef void (WhatsAppProto:: *WA_PKT_HANDLER)(const JSONNode &node, void*); enum class WAMetric { @@ -68,6 +68,7 @@ struct WARequest { CMStringA szPrefix; WA_PKT_HANDLER pHandler; + void *pUserInfo; }; struct WAUser @@ -164,7 +165,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto> OBJLIST<WARequest> m_arPacketQueue; bool WSReadPacket(const WSHeader &hdr, MBinBuffer &buf); - int WSSend(const CMStringA &str, WA_PKT_HANDLER = nullptr); + int WSSend(const CMStringA &str, WA_PKT_HANDLER = nullptr, void *pUserIndo = nullptr); int WSSendNode(const char *pszPrefix, WAMetric, int flags, WANode &node, WA_PKT_HANDLER = nullptr); void OnLoggedIn(void); @@ -181,10 +182,11 @@ class WhatsAppProto : public PROTO<WhatsAppProto> /// Request handlers /////////////////////////////////////////////////////////////////// - void OnRestoreSession1(const JSONNode &node); - void OnRestoreSession2(const JSONNode &node); - void OnSendMessage(const JSONNode &node); - void OnStartSession(const JSONNode &node); + void OnGetAvatarInfo(const JSONNode &node, void*); + void OnRestoreSession1(const JSONNode &node, void*); + void OnRestoreSession2(const JSONNode &node, void*); + void OnSendMessage(const JSONNode &node, void*); + void OnStartSession(const JSONNode &node, void*); // binary packets void ProcessBinaryPacket(const void *pData, size_t cbLen); @@ -251,6 +253,10 @@ public: struct CMPlugin : public ACCPROTOPLUGIN<WhatsAppProto> { signal_context *pCtx; + + HNETLIBUSER hAvatarUser = nullptr; + HNETLIBCONN hAvatarConn = nullptr; + bool SaveFile(const char *pszUrl, PROTO_AVATAR_INFORMATION &ai); CMPlugin(); diff --git a/protocols/WhatsAppWeb/src/server.cpp b/protocols/WhatsAppWeb/src/server.cpp index 13a08e9478..c771d72220 100644 --- a/protocols/WhatsAppWeb/src/server.cpp +++ b/protocols/WhatsAppWeb/src/server.cpp @@ -10,7 +10,7 @@ Copyright © 2019-21 George Hazan ///////////////////////////////////////////////////////////////////////////////////////// // sends a piece of JSON to a server via a websocket, masked -int WhatsAppProto::WSSend(const CMStringA &str, WA_PKT_HANDLER pHandler) +int WhatsAppProto::WSSend(const CMStringA &str, WA_PKT_HANDLER pHandler, void *pUserInfo) { if (m_hServerConn == nullptr) return -1; @@ -24,6 +24,7 @@ int WhatsAppProto::WSSend(const CMStringA &str, WA_PKT_HANDLER pHandler) auto *pReq = new WARequest; pReq->pHandler = pHandler; pReq->szPrefix = buf; + pReq->pUserInfo = pUserInfo; mir_cslock lck(m_csPacketQueue); m_arPacketQueue.insert(pReq); @@ -261,7 +262,7 @@ bool WhatsAppProto::ProcessSecret(const CMStringA &szSecret) ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnRestoreSession1(const JSONNode&) +void WhatsAppProto::OnRestoreSession1(const JSONNode&, void*) { ptrA szClient(getStringA(DBKEY_CLIENT_TOKEN)), szServer(getStringA(DBKEY_SERVER_TOKEN)); if (szClient == nullptr || szServer == nullptr) { @@ -273,7 +274,7 @@ void WhatsAppProto::OnRestoreSession1(const JSONNode&) WSSend(payload, &WhatsAppProto::OnRestoreSession2); } -void WhatsAppProto::OnRestoreSession2(const JSONNode &root) +void WhatsAppProto::OnRestoreSession2(const JSONNode &root, void*) { int status = root["status"].as_int(); if (status != 200) { @@ -311,7 +312,7 @@ void WhatsAppProto::ShutdownSession() ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnStartSession(const JSONNode &root) +void WhatsAppProto::OnStartSession(const JSONNode &root, void*) { int status = root["status"].as_int(); if (status != 200) { @@ -455,7 +456,7 @@ bool WhatsAppProto::ServerThreadWorker() auto *pReq = m_arPacketQueue.find((WARequest *)&szPrefix); if (pReq != nullptr) { root << CHAR_PARAM("$id$", szPrefix); - (this->*pReq->pHandler)(root); + (this->*pReq->pHandler)(root, pReq->pUserInfo); } else ProcessPacket(root); } |