summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/WhatsAppWeb/src/avatars.cpp83
-rw-r--r--protocols/WhatsAppWeb/src/db.h2
-rw-r--r--protocols/WhatsAppWeb/src/main.cpp9
-rw-r--r--protocols/WhatsAppWeb/src/proto.cpp7
-rw-r--r--protocols/WhatsAppWeb/src/proto.h18
-rw-r--r--protocols/WhatsAppWeb/src/server.cpp11
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);
}