summaryrefslogtreecommitdiff
path: root/protocols/VKontakte/src
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/VKontakte/src')
-rw-r--r--protocols/VKontakte/src/main.cpp2
-rw-r--r--protocols/VKontakte/src/misc.cpp256
-rw-r--r--protocols/VKontakte/src/resource.h14
-rw-r--r--protocols/VKontakte/src/stdafx.cxx2
-rw-r--r--protocols/VKontakte/src/stdafx.h2
-rw-r--r--protocols/VKontakte/src/version.h6
-rw-r--r--protocols/VKontakte/src/vk.h4
-rw-r--r--protocols/VKontakte/src/vk_avatars.cpp2
-rw-r--r--protocols/VKontakte/src/vk_captcha.cpp31
-rw-r--r--protocols/VKontakte/src/vk_chats.cpp9
-rw-r--r--protocols/VKontakte/src/vk_dialogs.cpp64
-rw-r--r--protocols/VKontakte/src/vk_dialogs.h27
-rw-r--r--protocols/VKontakte/src/vk_feed.cpp6
-rw-r--r--protocols/VKontakte/src/vk_files.cpp10
-rw-r--r--protocols/VKontakte/src/vk_history.cpp12
-rw-r--r--protocols/VKontakte/src/vk_messages.cpp29
-rw-r--r--protocols/VKontakte/src/vk_options.cpp68
-rw-r--r--protocols/VKontakte/src/vk_options.h9
-rw-r--r--protocols/VKontakte/src/vk_pollserver.cpp9
-rw-r--r--protocols/VKontakte/src/vk_proto.cpp15
-rw-r--r--protocols/VKontakte/src/vk_proto.h29
-rw-r--r--protocols/VKontakte/src/vk_queue.cpp165
-rw-r--r--protocols/VKontakte/src/vk_search.cpp2
-rw-r--r--protocols/VKontakte/src/vk_status.cpp9
-rw-r--r--protocols/VKontakte/src/vk_struct.cpp23
-rw-r--r--protocols/VKontakte/src/vk_struct.h10
-rw-r--r--protocols/VKontakte/src/vk_thread.cpp408
-rw-r--r--protocols/VKontakte/src/vk_wallpost.cpp2
-rw-r--r--protocols/VKontakte/src/vkjs.js50
29 files changed, 646 insertions, 629 deletions
diff --git a/protocols/VKontakte/src/main.cpp b/protocols/VKontakte/src/main.cpp
index 036d1f5f21..f11f9f5db7 100644
--- a/protocols/VKontakte/src/main.cpp
+++ b/protocols/VKontakte/src/main.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/misc.cpp b/protocols/VKontakte/src/misc.cpp
index a0e49f8cce..95870c2a7a 100644
--- a/protocols/VKontakte/src/misc.cpp
+++ b/protocols/VKontakte/src/misc.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -146,6 +146,12 @@ void CVkProto::CheckUpdate()
bIint64IDCompatibility = true;
}
+ if (getDword("LastAccessTokenTime", 0) < 1740009600)
+ ClearAccessToken();
+
+ delSetting("Login");
+ delSetting("Password");
+
}
//////////////////////// bIint64IDCompatibility /////////////////////////////////////////
@@ -231,13 +237,6 @@ void CVkProto::ClearAccessToken()
ShutdownSession();
}
-wchar_t* CVkProto::GetUserStoredPassword()
-{
- debugLogA("CVkProto::GetUserStoredPassword");
- ptrA szRawPass(getStringA("Password"));
- return (szRawPass != nullptr) ? mir_utf8decodeW(szRawPass) : nullptr;
-}
-
void CVkProto::SetAllContactStatuses(int iStatus)
{
debugLogA("CVkProto::SetAllContactStatuses (%d)", iStatus);
@@ -390,16 +389,24 @@ bool CVkProto::CheckJsonResult(AsyncHttpRequest *pReq, const JSONNode &jnNode)
MsgPopup(TranslateT("Access denied! Data will not be sent or received."), TranslateT("Error"), true);
break;
case VKERR_CAPTCHA_NEEDED:
- ApplyCaptcha(pReq, jnError);
+ if (!pReq)
+ return false;
+
+ if (!ApplyCaptcha(pReq, jnError))
+ if(!pReq->m_iRetry) {
+ CMStringW wszMsg(FORMAT, TranslateT("Error %d. Data will not be sent or received."), iErrorCode);
+ wszMsg += "\n";
+ wszMsg += TranslateT("Captcha processing error.");
+ MsgPopup(wszMsg, TranslateT("Error"), true);
+ debugLogA("CVkProto::CheckJsonResult Captcha processing error");
+ }
+
break;
case VKERR_VALIDATION_REQUIRED: // Validation Required
MsgPopup(TranslateT("You have to validate your account before you can use VK in Miranda NG"), TranslateT("Error"), true);
if (jnRedirectUri) {
T2Utf szRedirectUri(jnRedirectUri.as_mstring());
- AsyncHttpRequest *pRedirectReq = new AsyncHttpRequest(this, REQUEST_GET, szRedirectUri, false, &CVkProto::OnOAuthAuthorize);
- pRedirectReq->m_bApiReq = false;
- pRedirectReq->bIsMainConn = true;
- Push(pRedirectReq);
+ LogIn(szRedirectUri);
}
break;
case VKERR_FLOOD_CONTROL:
@@ -408,12 +415,13 @@ bool CVkProto::CheckJsonResult(AsyncHttpRequest *pReq, const JSONNode &jnNode)
case VKERR_UNKNOWN:
case VKERR_TOO_MANY_REQ_PER_SEC:
+ if (pReq->m_priority == AsyncHttpRequest::rpCaptcha)
+ break;
+ __fallthrough;
case VKERR_INTERNAL_SERVER_ERR:
if (pReq->m_iRetry > 0) {
pReq->bNeedsRestart = true;
- Sleep(1000); //Pause for fix err
- debugLogA("CVkProto::CheckJsonResult Retry = %d", pReq->m_iRetry);
- pReq->m_iRetry--;
+ debugLogA("CVkProto::CheckJsonResult Retry = %d", (MAX_RETRIES - pReq->m_iRetry + 1));
}
else {
CMStringW wszMsg(FORMAT, TranslateT("Error %d. Data will not be sent or received."), iErrorCode);
@@ -468,115 +476,6 @@ void CVkProto::OnReceiveSmth(MHttpResponse *reply, AsyncHttpRequest *pReq)
/////////////////////////////////////////////////////////////////////////////////////////
// Quick & dirty form parser
-static CMStringA getAttr(char *szSrc, LPCSTR szAttrName)
-{
- char *pEnd = strchr(szSrc, '>');
- if (pEnd == nullptr)
- return "";
-
- *pEnd = 0;
-
- char *p1 = strstr(szSrc, szAttrName);
- if (p1 == nullptr) {
- *pEnd = '>';
- return "";
- }
-
- p1 += mir_strlen(szAttrName);
- if (p1[0] != '=' || p1[1] != '\"') {
- *pEnd = '>';
- return "";
- }
-
- p1 += 2;
- char *p2 = strchr(p1, '\"');
- *pEnd = '>';
- if (p2 == nullptr)
- return "";
-
- return CMStringA(p1, (int)(p2 - p1));
-}
-
-bool CVkProto::AutoFillForm(char *pBody, CMStringA &szAction, CMStringA& szResult)
-{
- debugLogA("CVkProto::AutoFillForm");
- szResult.Empty();
-
- char *pFormBeg = strstr(pBody, "<form ");
- if (pFormBeg == nullptr)
- return false;
-
- char *pFormEnd = strstr(pFormBeg, "</form>");
- if (pFormEnd == nullptr)
- return false;
-
- *pFormEnd = 0;
-
- szAction = getAttr(pFormBeg, "action");
-
- CMStringA result;
- char *pFieldBeg = pFormBeg;
- while (true) {
- if ((pFieldBeg = strstr(pFieldBeg + 1, "<input ")) == nullptr)
- break;
-
- CMStringA type = getAttr(pFieldBeg, "type");
- if (type != "submit") {
- CMStringA name = getAttr(pFieldBeg, "name");
- CMStringA value = getAttr(pFieldBeg, "value");
- if (name == "email")
- value = (char*)T2Utf(ptrW(getWStringA("Login")));
- else if (name == "pass")
- value = (char*)T2Utf(ptrW(GetUserStoredPassword()));
- else if (name == "captcha_key") {
- char *pCaptchaBeg = strstr(pFormBeg, "<img id=\"captcha\"");
- if (!pCaptchaBeg)
- pCaptchaBeg = strstr(pFormBeg, "<img src=\"/captcha.php");
-
- if (pCaptchaBeg)
- if (!RunCaptchaForm(getAttr(pCaptchaBeg, "src"), value))
- return false;
- }
- else if (name == "code") {
- char szPrefixTel[10], szSufixTel[10];
- CMStringW wszTitle;
- char *pPhonePref = strstr(pFormBeg, "<span class=\"field_prefix\">");
- if (pPhonePref && sscanf(pPhonePref, "<span class=\"field_prefix\">%[^<]", szPrefixTel) == 1) {
- pPhonePref = strstr(pPhonePref + 1, "<span class=\"field_prefix\">&nbsp;");
- if (pPhonePref && sscanf(pPhonePref, "<span class=\"field_prefix\">&nbsp;%[^<]", szSufixTel) == 1) {
- wszTitle.Format(TranslateT("Enter the missing digits between %s and %s of the phone number linked to your account"),
- ptrW(mir_a2u(szPrefixTel)).get(), ptrW(mir_a2u(szSufixTel)).get());
- MessageBoxW(nullptr, wszTitle, TranslateT("Attention!"), MB_ICONWARNING | MB_OK);
- }
- }
-
- value = RunConfirmationCode(wszTitle);
- if (value.IsEmpty())
- return false;
- }
-
- if (!result.IsEmpty())
- result.AppendChar('&');
- result += name + "=";
- result += mir_urlEncode(value);
- }
- }
-
- szResult = result;
- debugLogA("CVkProto::AutoFillForm result = \"%s\"", szResult.c_str());
- return true;
-}
-
-CMStringW CVkProto::RunConfirmationCode(LPCWSTR pwszTitle)
-{
- ENTER_STRING pForm = {};
- pForm.type = 0;
- CMStringW wszTitle(FORMAT, L"%s: %s", m_tszUserName, IsEmpty(pwszTitle) ? TranslateT("Enter confirmation code") : pwszTitle);
- pForm.caption = wszTitle;
- pForm.szModuleName = m_szModuleName;
- pForm.szDataPrefix = "confirmcode_";
- return (!EnterString(&pForm)) ? CMStringW() : CMStringW(ptrW(pForm.ptszResult));
-}
CMStringW CVkProto::RunRenameNick(LPCWSTR pwszOldName)
{
@@ -593,13 +492,20 @@ CMStringW CVkProto::RunRenameNick(LPCWSTR pwszOldName)
void CVkProto::GrabCookies(MHttpResponse *nhr, CMStringA szDefDomain)
{
- debugLogA("CVkProto::GrabCookies");
+ if (!nhr)
+ return;
for (auto &hdr : *nhr) {
if (_stricmp(hdr->szName, "Set-cookie"))
continue;
CMStringA szValue = hdr->szValue, szCookieName, szCookieVal, szDomain;
+
+ CMStringA szLogValue(szValue);
+ if (!IsEmpty(m_szAccessToken))
+ szLogValue.Replace(m_szAccessToken, "*secret*");
+ debugLogA("CVkProto::GrabCookies: %s", szLogValue.c_str());
+
int iStart = 0;
while (true) {
bool bFirstToken = (iStart == 0);
@@ -624,20 +530,23 @@ void CVkProto::GrabCookies(MHttpResponse *nhr, CMStringA szDefDomain)
for (auto &it : m_cookies)
if (it->m_name == szCookieName) {
bFound = true;
- if (szCookieVal == "DELETED")
+ if (CMStringA(szCookieVal).MakeUpper() == "DELETED")
m_cookies.remove(it);
else
it->m_value = szCookieVal;
break;
}
- if (!bFound && szCookieVal != "DELETED")
+ if (!bFound && CMStringA(szCookieVal).MakeUpper() != "DELETED")
m_cookies.insert(new CVkCookie(szCookieName, szCookieVal, szDomain));
+
}
}
+
+ SaveCookies();
}
-void CVkProto::ApplyCookies(AsyncHttpRequest *pReq)
+void CVkProto::ApplyCookies(MHttpRequest *pReq)
{
debugLogA("CVkProto::ApplyCookies");
CMStringA szCookie;
@@ -653,10 +562,68 @@ void CVkProto::ApplyCookies(AsyncHttpRequest *pReq)
szCookie.Append(it->m_value);
}
- if (!szCookie.IsEmpty())
+ if (!szCookie.IsEmpty()) {
pReq->AddHeader("Cookie", szCookie);
+ }
}
+void CVkProto::SaveCookies()
+{
+ CMStringA szCookie;
+
+ for (auto& it : m_cookies) {
+ if (!szCookie.IsEmpty())
+ szCookie.Append("; ");
+ szCookie.Append(it->m_name);
+ szCookie.AppendChar('=');
+ szCookie.Append(it->m_value);
+ szCookie.AppendChar('=');
+ szCookie.Append(it->m_domain);
+ }
+
+ if (!szCookie.IsEmpty())
+ setString("Cookie", szCookie);
+
+ if (!IsEmpty(m_szAccessToken))
+ szCookie.Replace(m_szAccessToken, "*secret*");
+
+ debugLogA("CVkProto::SaveCookies: %s", szCookie.c_str());
+}
+
+void CVkProto::LoadCookies()
+{
+ debugLogA("CVkProto::LoadCookies");
+ CMStringA szCookies = getStringA("Cookie");
+ if (szCookies.IsEmpty())
+ return;
+
+ CMStringA szCookieName, szCookieDomain, szCookieValue;
+ szCookies.AppendChar(';');
+
+ int iStart = 0;
+ while (true) {
+ CMStringA szToken = szCookies.Tokenize(";", iStart).Trim();
+ if (iStart == -1)
+ break;
+
+ int iStart2 = 0;
+
+ szCookieName = szToken.Tokenize("=", iStart2);
+ if (iStart2 == -1)
+ continue;
+
+ szCookieValue = szToken.Tokenize("=", iStart2);
+ if (iStart2 == -1)
+ continue;
+
+ szCookieDomain = szToken.Tokenize("=", iStart2);
+ if (iStart2 == -1)
+ continue;
+
+ m_cookies.insert(new CVkCookie(szCookieName, szCookieValue, szCookieDomain));
+ }
+
+}
/////////////////////////////////////////////////////////////////////////////////////////
bool CVkProto::IsAuthContactLater(MCONTACT hContact)
@@ -719,7 +686,7 @@ void CVkProto::DBAddAuthRequest(const MCONTACT hContact, bool added)
DBEVENTINFO dbei = {};
dbei.szModule = m_szModuleName;
- dbei.timestamp = (uint32_t)time(0);
+ dbei.iTimestamp = (uint32_t)time(0);
dbei.flags = DBEF_UTF;
dbei.eventType = added ? EVENTTYPE_ADDED : EVENTTYPE_AUTHREQUEST;
dbei.cbBlob = blob.size();
@@ -1378,10 +1345,14 @@ CMStringW CVkProto::GetAttachmentDescr(const JSONNode &jnAttachments, BBCSupport
continue;
CMStringW wszUrl(jnGraffiti["url"].as_mstring());
+ CMStringW wszImgUrl(wszUrl);
+
+ if (m_vkOptions.bBBCNewStorySupport)
+ wszUrl = GetVkFileItem(wszImgUrl, hContact, iMessageId);
res.AppendFormat(L"%s\n\t%s",
SetBBCString(TranslateT("Graffiti"), iBBC, vkbbcUrl, wszUrl).c_str(),
- SetBBCString(wszUrl, bbcBasic, vkbbcImg).c_str()
+ SetBBCString(wszImgUrl, iBBC, vkbbcImg).c_str()
);
}
else if (wszType == L"video") {
@@ -1575,8 +1546,11 @@ CMStringW CVkProto::GetAttachmentDescr(const JSONNode &jnAttachments, BBCSupport
wszUrl = wszLink.IsEmpty() ? (wszLink128.IsEmpty() ? wszLinkLast : wszLink128) : wszLink;
- if (!m_vkOptions.bStikersAsSmileys)
+ if (!m_vkOptions.bStikersAsSmileys) {
+ if(m_vkOptions.bBBCNewStorySupport)
+ wszUrl = GetVkFileItem(wszUrl, hContact, iMessageId);
res += SetBBCString(wszUrl, iBBC, vkbbcImg);
+ }
else if (m_vkOptions.bUseStikersAsStaticSmileys)
res.AppendFormat(L"[sticker:%d]", iStickerId);
else {
@@ -1649,10 +1623,13 @@ CMStringW CVkProto::GetAttachmentDescr(const JSONNode &jnAttachments, BBCSupport
SetBBCString(TranslateT("Product"), iBBC, vkbbcB).c_str(),
SetBBCString(wszTitle.IsEmpty() ? TranslateT("Link") : wszTitle, iBBC, vkbbcUrl, wszUrl).c_str());
- if (!wszPhoto.IsEmpty())
+ if (!wszPhoto.IsEmpty()) {
+ if (m_vkOptions.bBBCNewStorySupport)
+ wszPhoto = GetVkFileItem(wszPhoto, hContact, iMessageId);
res.AppendFormat(L"\n\t%s: %s",
SetBBCString(TranslateT("Photo"), iBBC, vkbbcB).c_str(),
SetBBCString(wszPhoto, iBBC, vkbbcImg).c_str());
+ }
if (jnMarket["price"] && jnMarket["price"]["text"])
res.AppendFormat(L"\n\t%s: %s",
@@ -1679,8 +1656,11 @@ CMStringW CVkProto::GetAttachmentDescr(const JSONNode &jnAttachments, BBCSupport
continue;
res += SetBBCString(TranslateT("Gift"), iBBC, vkbbcUrl, wszLink);
- if (m_vkOptions.iIMGBBCSupport && iBBC != bbcNo)
+ if (m_vkOptions.iIMGBBCSupport && iBBC != bbcNo) {
+ if (m_vkOptions.bBBCNewStorySupport)
+ wszLink = GetVkFileItem(wszLink, hContact, iMessageId);
res.AppendFormat(L"\n\t%s", SetBBCString(wszLink, iBBC, vkbbcImg).c_str());
+ }
}
else {
res.AppendFormat(TranslateT("Unsupported or unknown attachment type: %s"), SetBBCString(wszType, iBBC, vkbbcB).c_str());
@@ -1949,7 +1929,7 @@ void CVkProto::AddVkDeactivateEvent(MCONTACT hContact, CMStringW& wszType)
DBEVENTINFO dbei = {};
dbei.szModule = m_szModuleName;
- dbei.timestamp = time(0);
+ dbei.iTimestamp = time(0);
dbei.eventType = VK_USER_DEACTIVATE_ACTION;
ptrA pszDescription(mir_utf8encode(vkDeactivateEvent[iDEIdx].szDescription));
dbei.cbBlob = (uint32_t)mir_strlen(pszDescription) + 1;
@@ -1982,7 +1962,7 @@ MEVENT CVkProto::GetMessageFromDb(const char *szMessageId, time_t& tTimeStamp, C
DB::EventInfo dbei(hDbEvent);
wszMsg = ptrW(mir_utf8decodeW((char*)dbei.pBlob));
- tTimeStamp = dbei.timestamp;
+ tTimeStamp = dbei.getUnixtime();
return hDbEvent;
}
@@ -2010,7 +1990,7 @@ bool CVkProto::IsMessageExist(VKMessageID_t iMessageId, VKMesType vkType)
if(db_event_get(hDbEvent, &dbei))
return false;
- return ((vkType == vkOUT) == (bool)(dbei.flags & DBEF_SENT));
+ return ((vkType == vkOUT) == dbei.bSent);
}
CMStringW CVkProto::UserProfileUrl(VKUserID_t iUserId)
diff --git a/protocols/VKontakte/src/resource.h b/protocols/VKontakte/src/resource.h
index 4f3d4fa917..309d06f3e4 100644
--- a/protocols/VKontakte/src/resource.h
+++ b/protocols/VKontakte/src/resource.h
@@ -1,6 +1,6 @@
//{{NO_DEPENDENCIES}}
// Включаемый файл, созданный в Microsoft Visual C++.
-// Используется e:\github\miranda-ng\protocols\VKontakte\res\resource.rc
+// Используется E:\github\miranda-ng\protocols\VKontakte\res\resource.rc
//
#define IDOPENBROWSER 3
#define IDD_ACCMGRUI 101
@@ -27,8 +27,7 @@
#define IDD_VKUSERFORM 124
#define IDI_FORWARD 126
#define IDI_RELOADMESSAGE 130
-#define IDC_LOGIN 1001
-#define IDC_PASSWORD 1002
+#define IDD_TOKENFORM 131
#define IDC_URL 1003
#define IDC_VALUE 1004
#define IDC_WHITERECT 1005
@@ -37,7 +36,6 @@
#define IDC_FRAME1 1008
#define IDC_FRAME2 1009
#define IDC_SUBMIT 1010
-#define IDC_DELIVERY 1011
#define IDC_GROUPNAME 1012
#define IDC_HIDECHATS 1014
#define IDC_CONTACT 1015
@@ -131,18 +129,18 @@
#define IDC_MESSAGE 1117
#define IDC_STATIC_MARKCONTAKTS 1118
#define IDC_STATIC_MESSAGE 1120
-#define IDC_ADD_MES_LINK_MES_ATT 1121
#define IDC_ADD_ORIG_VER_IN_EDITED_MES 1122
#define IDC_IMG_300 1123
-
+#define IDC_TOKENVAL 1124
+#define IDC_TOKENREQ 1125
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
-#define _APS_NEXT_RESOURCE_VALUE 131
+#define _APS_NEXT_RESOURCE_VALUE 132
#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1124
+#define _APS_NEXT_CONTROL_VALUE 1126
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/protocols/VKontakte/src/stdafx.cxx b/protocols/VKontakte/src/stdafx.cxx
index 2f80390225..ce308be858 100644
--- a/protocols/VKontakte/src/stdafx.cxx
+++ b/protocols/VKontakte/src/stdafx.cxx
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/stdafx.h b/protocols/VKontakte/src/stdafx.h
index 727c31a6c3..4f1835b97c 100644
--- a/protocols/VKontakte/src/stdafx.h
+++ b/protocols/VKontakte/src/stdafx.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/version.h b/protocols/VKontakte/src/version.h
index 20c56b9a6a..6c2d7c50f6 100644
--- a/protocols/VKontakte/src/version.h
+++ b/protocols/VKontakte/src/version.h
@@ -1,7 +1,7 @@
#define __MAJOR_VERSION 0
#define __MINOR_VERSION 1
-#define __RELEASE_NUM 17
-#define __BUILD_NUM 1
+#define __RELEASE_NUM 19
+#define __BUILD_NUM 2
#include <stdver.h>
@@ -10,4 +10,4 @@
#define __DESCRIPTION "VKontakte protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/VKontakte"
-#define __COPYRIGHT "В© 2013-24 Miranda NG team"
+#define __COPYRIGHT "В© 2013-25 Miranda NG team"
diff --git a/protocols/VKontakte/src/vk.h b/protocols/VKontakte/src/vk.h
index 36a1800d0a..9a9eb2e580 100644
--- a/protocols/VKontakte/src/vk.h
+++ b/protocols/VKontakte/src/vk.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -88,7 +88,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VK_USER_DEACTIVATE_ACTION 9321
-#define VK_API_VER "5.236"
+#define VK_API_VER "5.199"
#define VER_API CHAR_PARAM("v", VK_API_VER)
#define VK_FEED_USER 2147483647L
diff --git a/protocols/VKontakte/src/vk_avatars.cpp b/protocols/VKontakte/src/vk_avatars.cpp
index 0073be6d8d..9bd0ae782c 100644
--- a/protocols/VKontakte/src/vk_avatars.cpp
+++ b/protocols/VKontakte/src/vk_avatars.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/vk_captcha.cpp b/protocols/VKontakte/src/vk_captcha.cpp
index 3969850fe8..58d212fd8c 100644
--- a/protocols/VKontakte/src/vk_captcha.cpp
+++ b/protocols/VKontakte/src/vk_captcha.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -69,20 +69,39 @@ bool CVkProto::RunCaptchaForm(LPCSTR szUrl, CMStringA &result)
bool CVkProto::ApplyCaptcha(AsyncHttpRequest *pReq, const JSONNode &jnErrorNode)
{
debugLogA("CVkProto::ApplyCaptcha");
- if (!IsOnline())
- return false;
+ if (m_bCaptchaReqInProgress)
+ return true;
+
+ m_bCaptchaReqInProgress = true;
CMStringA szUrl(jnErrorNode["captcha_img"].as_mstring());
CMStringA szSid(jnErrorNode["captcha_sid"].as_mstring());
- if (szUrl.IsEmpty() || szSid.IsEmpty())
+ if (szUrl.IsEmpty() || szSid.IsEmpty()) {
+ m_bCaptchaReqInProgress = false;
return false;
+ }
CMStringA userReply;
- if (!RunCaptchaForm(szUrl, userReply))
+ if (!RunCaptchaForm(szUrl, userReply)) {
+ m_bCaptchaReqInProgress = false;
return false;
+ }
+
+ Push(new AsyncHttpRequest(this,
+ REQUEST_GET,
+ (m_iStatus == ID_STATUS_INVISIBLE) ? "/method/account.setOffline" : "/method/account.setOnline",
+ true,
+ &CVkProto::OnReceiveSmth,
+ AsyncHttpRequest::rpCaptcha
+ )
+ << CHAR_PARAM("captcha_sid", szSid)
+ << CHAR_PARAM("captcha_key", userReply.GetString())
+ );
- pReq << CHAR_PARAM("captcha_sid", szSid) << CHAR_PARAM("captcha_key", userReply.GetString());
pReq->bNeedsRestart = true;
+ debugLogA("CVkProto::ApplyCaptcha for %s", pReq->m_szUrl.c_str());
+ m_bCaptchaReqInProgress = false;
+
return true;
} \ No newline at end of file
diff --git a/protocols/VKontakte/src/vk_chats.cpp b/protocols/VKontakte/src/vk_chats.cpp
index 68ada0350f..979624a17d 100644
--- a/protocols/VKontakte/src/vk_chats.cpp
+++ b/protocols/VKontakte/src/vk_chats.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -442,11 +442,6 @@ void CVkProto::AppendChatConversationMessage(VKUserID_t iChatId, const JSONNode&
}
}
- if (m_vkOptions.bAddMessageLinkToMesWAtt && ((jnAttachments && !jnAttachments.empty()) || (jnFwdMessages && !jnFwdMessages.empty()) || (jnReplyMessages && !jnReplyMessages.empty() && m_vkOptions.bShowReplyInMessage)))
- wszBody += SetBBCString(TranslateT("Message link"), m_vkOptions.bBBCNewStorySupport ? bbcAdvanced : bbcNo, vkbbcUrl,
- CMStringW(FORMAT, L"https://vk.com/im?sel=c%d&msgid=%d", vkChatInfo->m_iChatId, iMessageId));
-
-
if (jnMsg["action"] && jnMsg["action"]["type"]) {
bIsAction = true;
CMStringW wszAction = jnMsg["action"]["type"].as_mstring();
@@ -582,7 +577,7 @@ void CVkProto::AppendChatMessage(CVkChatInfo* vkChatInfo, VKMessageID_t iMessage
DB::EventInfo dbei;
dbei.szId = szMid;
- dbei.timestamp = tMsgTime;
+ dbei.iTimestamp = tMsgTime;
dbei.pBlob = pszBody;
if (iUserId == m_iMyUserId)
dbei.flags |= DBEF_SENT;
diff --git a/protocols/VKontakte/src/vk_dialogs.cpp b/protocols/VKontakte/src/vk_dialogs.cpp
index 85590484c0..fc8576410d 100644
--- a/protocols/VKontakte/src/vk_dialogs.cpp
+++ b/protocols/VKontakte/src/vk_dialogs.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -98,8 +98,70 @@ void CVkCaptchaForm::On_btnOpenInBrowser_Click(CCtrlButton*)
void CVkCaptchaForm::On_edtValue_Change(CCtrlEdit*)
{
m_btnOk.Enable(!IsEmpty(ptrA(m_edtValue.GetTextA())));
+
+}
+
+////////////////////////////////// IDD_TOKENFORM //////////////////////////////////////////
+CVkTokenForm::CVkTokenForm(CVkProto* proto, const char* _szTokenReq) :
+ CVkDlgBase(proto, IDD_TOKENFORM),
+ m_instruction(this, IDC_INSTRUCTION),
+ m_edtValue(this, IDC_TOKENVAL),
+ m_btnTokenReq(this, IDC_TOKENREQ),
+ m_btnOk(this, IDOK),
+ m_TokenReq(_szTokenReq)
+{
+ m_btnTokenReq.OnClick = Callback(this, &CVkTokenForm::On_btnTokenReq_Click);
+ m_edtValue.OnChange = Callback(this, &CVkTokenForm::On_edtValue_Change);
+ m_szAccName = proto->m_szModuleName;
}
+bool CVkTokenForm::OnInitDialog()
+{
+ Window_SetIcon_IcoLib(m_hwnd, Skin_GetIconHandle(SKINICON_OTHER_KEYS));
+
+ CMStringW wszCaption(Translate("Logon for "));
+ wszCaption += m_proto->m_tszUserName;
+
+ SetCaption(wszCaption.c_str());
+
+ m_btnOk.Disable();
+ if (!m_TokenReq.IsEmpty()) {
+ m_instruction.SetText(TranslateT("To log into the VKontakte protocol, you need to authorize in the browser and copy the received address from its address bar into the field below.\nDo not change anything when copying!\nYou will have to ignore the anti-copy message that you will see in your browser. Unfortunately, Miranda NG cannot authorize you in any other way right now."));
+ Utils_OpenUrl(m_TokenReq.c_str());
+ return true;
+ }
+
+ return false;
+}
+
+void CVkTokenForm::OnDestroy()
+{
+ Window_FreeIcon_IcoLib(m_hwnd);
+}
+
+bool CVkTokenForm::OnApply()
+{
+ m_edtValue.GetTextA(Result, _countof(Result));
+ return true;
+}
+
+void CVkTokenForm::On_edtValue_Change(CCtrlEdit*)
+{
+ extern char szVKTokenBeg[];
+ ptrA pszUrlSring(m_edtValue.GetTextA());
+
+ if (!IsEmpty(pszUrlSring) && strstr(pszUrlSring, szVKTokenBeg))
+ m_btnOk.Enable();
+ else
+ m_btnOk.Disable();
+}
+
+void CVkTokenForm::On_btnTokenReq_Click(CCtrlButton*)
+{
+ Utils_OpenUrl(m_TokenReq.c_str());
+}
+
+
////////////////////////////////// IDD_WALLPOST ///////////////////////////////////////////
CVkWallPostForm::CVkWallPostForm(CVkProto *proto, WALLPOST_FORM_PARAMS *param) :
diff --git a/protocols/VKontakte/src/vk_dialogs.h b/protocols/VKontakte/src/vk_dialogs.h
index e921e3ad0d..9a333ec430 100644
--- a/protocols/VKontakte/src/vk_dialogs.h
+++ b/protocols/VKontakte/src/vk_dialogs.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -51,6 +51,31 @@ public:
};
+////////////////////////////////// IDD_TOKENFORM ////////////////////////////////////////
+
+class CVkTokenForm : public CVkDlgBase
+{
+ CCtrlData m_instruction;
+ CCtrlEdit m_edtValue;
+ CCtrlButton m_btnTokenReq;
+ CCtrlButton m_btnOk;
+ CMStringA m_TokenReq;
+ CMStringA m_szAccName;
+
+public:
+ CVkTokenForm(CVkProto* proto, const char* _szTokenReq);
+
+ bool OnInitDialog() override;
+ bool OnApply() override;
+ void OnDestroy() override;
+
+ void On_btnTokenReq_Click(CCtrlButton*);
+ void On_edtValue_Change(CCtrlEdit*);
+
+ char Result[4096];
+
+};
+
////////////////////////////////// IDD_WALLPOST ///////////////////////////////////////////
struct WALLPOST_FORM_PARAMS
diff --git a/protocols/VKontakte/src/vk_feed.cpp b/protocols/VKontakte/src/vk_feed.cpp
index f1646c6a78..be05063ca0 100644
--- a/protocols/VKontakte/src/vk_feed.cpp
+++ b/protocols/VKontakte/src/vk_feed.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -56,7 +56,7 @@ void CVkProto::AddFeedEvent(CVKNewsItem& vkNewsItem)
T2Utf pszBody(vkNewsItem.wszText);
DB::EventInfo dbei;
- dbei.timestamp = vkNewsItem.tDate;
+ dbei.iTimestamp = vkNewsItem.tDate;
dbei.pBlob = pszBody;
if (m_vkOptions.bUseNonStandardNotifications) {
@@ -868,7 +868,7 @@ void CVkProto::NewsClearHistory()
while (MEVENT hDbEvent = pCursor.FetchNext()) {
DBEVENTINFO dbei = {};
db_event_get(hDbEvent, &dbei);
- if (dbei.timestamp < tTime)
+ if (dbei.getUnixtime() < tTime)
pCursor.DeleteEvent();
}
} \ No newline at end of file
diff --git a/protocols/VKontakte/src/vk_files.cpp b/protocols/VKontakte/src/vk_files.cpp
index 19b8619881..24b7c943e1 100644
--- a/protocols/VKontakte/src/vk_files.cpp
+++ b/protocols/VKontakte/src/vk_files.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -494,8 +494,6 @@ void CVkProto::OnReciveUploadFile(MHttpResponse *reply, AsyncHttpRequest *pReq)
}
pMsgReq << INT_PARAM("chat_id", cc->m_iChatId);
- pMsgReq->pUserInfo = pReq->pUserInfo;
-
}
else {
VKUserID_t iUserId = ReadVKUserID(fup->hContact);
@@ -505,15 +503,15 @@ void CVkProto::OnReciveUploadFile(MHttpResponse *reply, AsyncHttpRequest *pReq)
}
pMsgReq << INT_PARAM("peer_id", iUserId);
- pMsgReq->pUserInfo = new CVkSendMsgParam(fup->hContact, fup);
}
-
+
ULONG uMsgId = ::InterlockedIncrement(&m_iMsgId);
pMsgReq
<< WCHAR_PARAM("message", fup->wszDesc)
<< WCHAR_PARAM("attachment", Attachment)
<< INT_PARAM("random_id", ((long)time(0)) * 100 + uMsgId % 100);
-
pMsgReq->AddHeader("Content-Type", "application/x-www-form-urlencoded");
+ pMsgReq->pUserInfo = new CVkSendMsgParam(fup->hContact, fup);
+
Push(pMsgReq);
} \ No newline at end of file
diff --git a/protocols/VKontakte/src/vk_history.cpp b/protocols/VKontakte/src/vk_history.cpp
index 93a1fcb25f..63774a9423 100644
--- a/protocols/VKontakte/src/vk_history.cpp
+++ b/protocols/VKontakte/src/vk_history.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -116,7 +116,7 @@ void CVkProto::GetServerHistoryLastNDay(MCONTACT hContact, int iNDay)
while (MEVENT hDbEvent = pCursor.FetchNext()) {
DBEVENTINFO dbei = {};
db_event_get(hDbEvent, &dbei);
- if (dbei.timestamp > tTime && dbei.eventType != VK_USER_DEACTIVATE_ACTION)
+ if (dbei.getUnixtime() > tTime && dbei.eventType != VK_USER_DEACTIVATE_ACTION)
pCursor.DeleteEvent();
}
@@ -145,7 +145,7 @@ void CVkProto::GetServerHistory(MCONTACT hContact, int iOffset, int iCount, time
if (VK_INVALID_USER == iUserId || iUserId == VK_FEED_USER)
return;
- Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/execute.GetServerConversationHistory", true, &CVkProto::OnReceiveHistoryMessages, AsyncHttpRequest::rpLow)
+ Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/execute.GetServerConversationHistory", true, &CVkProto::OnReceiveHistoryMessages, AsyncHttpRequest::rpLowCListEvents)
<< INT_PARAM("reqcount", iCount)
<< INT_PARAM("offset", iOffset)
<< INT_PARAM("userid", iUserId)
@@ -297,10 +297,6 @@ void CVkProto::OnReceiveHistoryMessages(MHttpResponse *reply, AsyncHttpRequest *
wszBody += wszAttachmentDescr;
}
- if (m_vkOptions.bAddMessageLinkToMesWAtt && ((jnAttachments && !jnAttachments.empty()) || (jnFwdMessages && !jnFwdMessages.empty()) || (jnReplyMessages && !jnReplyMessages.empty() && m_vkOptions.bShowReplyInMessage)))
- wszBody += SetBBCString(TranslateT("Message link"), m_vkOptions.BBCForAttachments(), vkbbcUrl,
- CMStringW(FORMAT, L"https://vk.com/im?sel=%d&msgid=%d", iUserId, iMessageId));
-
DB::EventInfo dbei;
if (bIsRead)
dbei.flags |= DBEF_READ;
@@ -326,7 +322,7 @@ void CVkProto::OnReceiveHistoryMessages(MHttpResponse *reply, AsyncHttpRequest *
T2Utf pszBody(wszBody);
- dbei.timestamp = tDateTime;
+ dbei.iTimestamp = tDateTime;
dbei.pBlob = pszBody;
dbei.szId = szMid;
diff --git a/protocols/VKontakte/src/vk_messages.cpp b/protocols/VKontakte/src/vk_messages.cpp
index 7d14a6305c..57cdb27a9a 100644
--- a/protocols/VKontakte/src/vk_messages.cpp
+++ b/protocols/VKontakte/src/vk_messages.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -60,10 +60,10 @@ int CVkProto::ForwardMsg(MCONTACT hContact, std::vector<MEVENT>& vForvardEvents,
continue;
if (!Proto_IsProtoOnContact(dbei.hContact, m_szModuleName)) {
- CMStringW wszContactName = (dbei.flags & DBEF_SENT) ? getWStringA(0, "Nick", TranslateT("Me")) : Clist_GetContactDisplayName(dbei.hContact);
+ CMStringW wszContactName = (dbei.bSent) ? getWStringA(0, "Nick", TranslateT("Me")) : Clist_GetContactDisplayName(dbei.hContact);
wchar_t ttime[64];
- time_t tTimestamp(dbei.timestamp);
+ time_t tTimestamp(dbei.getUnixtime());
_locale_t locale = _create_locale(LC_ALL, "");
_wcsftime_l(ttime, _countof(ttime), TranslateT("%x at %X"), localtime(&tTimestamp), locale);
_free_locale(locale);
@@ -129,9 +129,6 @@ int CVkProto::ForwardMsg(MCONTACT hContact, std::vector<MEVENT>& vForvardEvents,
pReq->pUserInfo = new CVkSendMsgParam(hContact, uMsgId);
Push(pReq);
- if (!m_vkOptions.bServerDelivery)
- ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)uMsgId);
-
if (vForvardEvents.size() > VK_MAX_FORWARD_MESSAGES) {
std::vector vNextForvardEvents(vForvardEvents.begin() + VK_MAX_FORWARD_MESSAGES, vForvardEvents.end());
ForwardMsg(hContact, vNextForvardEvents, "");
@@ -210,9 +207,6 @@ int CVkProto::SendMsg(MCONTACT hContact, MEVENT hReplyEvent, const char *szMsg)
pReq->pUserInfo = new CVkSendMsgParam(hContact, uMsgId);
Push(pReq);
- if (!m_vkOptions.bServerDelivery)
- ProtoBroadcastAsync(hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)uMsgId);
-
if (!IsEmpty(pszRetMsg))
SendMsg(hContact, 0, pszRetMsg);
else if (m_iStatus == ID_STATUS_INVISIBLE)
@@ -272,7 +266,7 @@ void CVkProto::OnSendMessage(MHttpResponse *reply, AsyncHttpRequest *pReq)
if (!pReq->bNeedsRestart || m_bTerminated)
delete param->pFUP;
}
- else if (m_vkOptions.bServerDelivery)
+ else
ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, iResult, (HANDLE)(param->iMsgID), (LPARAM)szMid);
@@ -372,7 +366,12 @@ void CVkProto::OnReceiveMessages(MHttpResponse *reply, AsyncHttpRequest *pReq)
CMStringW wszPeerType(jnPeer["type"].as_mstring());
VKUserID_t iUserId = jnPeer["id"].as_int();
- MCONTACT hContact = (wszPeerType == L"chat") ? FindChat(iUserId % VK_CHAT_MIN) : FindUser(iUserId, true);
+ MCONTACT hContact = (wszPeerType == L"chat") ? FindChat(iUserId % VK_CHAT_MIN) : FindUser(iUserId);
+ if (!hContact) {
+ hContact = FindUser(iUserId, true);
+ RetrieveUserInfo(iUserId);
+ }
+
WriteQSWord(hContact, "in_read", jnItem["in_read"].as_int());
WriteQSWord(hContact, "out_read", jnItem["out_read"].as_int());
if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnReceive)
@@ -460,10 +459,6 @@ void CVkProto::OnReceiveMessages(MHttpResponse *reply, AsyncHttpRequest *pReq)
wszBody += wszAttachmentDescr;
}
- if (m_vkOptions.bAddMessageLinkToMesWAtt && ((jnAttachments && !jnAttachments.empty()) || (jnFwdMessages && !jnFwdMessages.empty()) || (jnReplyMessages && !jnReplyMessages.empty() && m_vkOptions.bShowReplyInMessage)))
- wszBody += SetBBCString(TranslateT("Message link"), m_vkOptions.BBCForAttachments(), vkbbcUrl,
- CMStringW(FORMAT, L"https://vk.com/im?sel=%d&msgid=%d", iUserId, iMessageId));
-
VKMessageID_t iReadMsg = ReadQSWord(hContact, "in_read", 0);
bool bIsRead = (iMessageId <= iReadMsg);
@@ -499,7 +494,7 @@ void CVkProto::OnReceiveMessages(MHttpResponse *reply, AsyncHttpRequest *pReq)
SetInvisible(hContact);
T2Utf pszBody(wszBody);
- dbei.timestamp = bEdited ? tDateTime : (m_vkOptions.bUseLocalTime ? time(0) : tDateTime);
+ dbei.iTimestamp = bEdited ? tDateTime : (m_vkOptions.bUseLocalTime ? time(0) : tDateTime);
dbei.pBlob = pszBody;
if (!m_vkOptions.bShowReplyInMessage && szReplyId) {
@@ -528,7 +523,7 @@ void CVkProto::OnReceiveMessages(MHttpResponse *reply, AsyncHttpRequest *pReq)
debugLogA("CVkProto::OnReceiveMessages add attachments");
T2Utf pszAttach(wszAttachmentDescr);
- dbei.timestamp = isOut ? time(0) : tDateTime;
+ dbei.iTimestamp = isOut ? time(0) : tDateTime;
dbei.pBlob = pszAttach;
dbei.szId = strcat(szMid, "_");
ProtoChainRecvMsg(hContact, dbei);
diff --git a/protocols/VKontakte/src/vk_options.cpp b/protocols/VKontakte/src/vk_options.cpp
index 3f993f2b64..b82babbaf1 100644
--- a/protocols/VKontakte/src/vk_options.cpp
+++ b/protocols/VKontakte/src/vk_options.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -22,56 +22,34 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class CVkAccMgrForm : public CVkDlgBase
{
typedef CVkDlgBase CSuper;
-
- CCtrlEdit m_edtLogin;
- CCtrlEdit m_edtPassword;
CCtrlHyperlink m_hlLink;
-
- pass_ptrW m_pwszOldPass;
- ptrW m_pwszOldLogin;
+ CCtrlButton m_btnTokenReq;
public:
CVkAccMgrForm(CVkProto *proto, HWND hwndParent) :
CVkDlgBase(proto, IDD_ACCMGRUI),
- m_edtLogin(this, IDC_LOGIN),
- m_edtPassword(this, IDC_PASSWORD),
+ m_btnTokenReq(this, IDC_TOKENREQ),
m_hlLink(this, IDC_URL, "https://vk.com/")
{
SetParent(hwndParent);
-
- CreateLink(m_edtLogin, "Login", L"");
+ m_btnTokenReq.OnClick = Callback(this, &CVkAccMgrForm::On_btnTokenReq_Click);
}
bool OnInitDialog() override
{
CSuper::OnInitDialog();
-
- m_pwszOldLogin = m_edtLogin.GetText();
- m_edtLogin.SendMsg(EM_LIMITTEXT, 1024, 0);
-
- m_pwszOldPass = m_proto->GetUserStoredPassword();
- m_edtPassword.SetText(m_pwszOldPass);
- m_edtPassword.SendMsg(EM_LIMITTEXT, 1024, 0);
return true;
}
bool OnApply() override
{
- pass_ptrW pwszNewPass(m_edtPassword.GetText());
- bool bPassChanged = mir_wstrcmp(m_pwszOldPass, pwszNewPass) != 0;
- if (bPassChanged) {
- T2Utf szRawPasswd(pwszNewPass);
- m_proto->setString("Password", szRawPasswd);
- pass_ptrA pszPass(szRawPasswd.detach());
- m_pwszOldPass = pwszNewPass.detach();
- }
-
- ptrW pwszNewLogin(m_edtLogin.GetText());
- if (bPassChanged || mir_wstrcmpi(m_pwszOldLogin, pwszNewLogin))
- m_proto->ClearAccessToken();
- m_pwszOldLogin = pwszNewLogin.detach();
return true;
}
+
+ void On_btnTokenReq_Click(CCtrlButton*)
+ {
+ m_proto->LogIn();
+ }
};
MWindow CVkProto::OnCreateAccMgrUI(MWindow hwndParent)
@@ -160,21 +138,15 @@ static vkMarkMsgAsReadMethods[] =
CVkOptionAccountForm::CVkOptionAccountForm(CVkProto *proto) :
CVkDlgBase(proto, IDD_OPT_MAIN),
- m_edtLogin(this, IDC_LOGIN),
- m_edtPassword(this, IDC_PASSWORD),
- m_hlLink(this, IDC_URL, "https://vk.com/"),
m_edtGroupName(this, IDC_GROUPNAME),
m_cbxVKLang(this, IDC_COMBO_LANGUAGE),
- m_cbDelivery(this, IDC_DELIVERY),
m_cbLoadLastMessageOnMsgWindowsOpen(this, IDC_LASTHISTORYLOAD),
m_cbUseLocalTime(this, IDC_USE_LOCAL_TIME),
m_cbLoadOnlyFriends(this, IDC_LOADONLYFRIENDS),
m_cbxMarkAsRead(this, IDC_COMBO_MARKASREAD),
m_cbxSyncHistory(this, IDC_COMBO_SYNCHISTORY)
{
- CreateLink(m_edtLogin, "Login", L"");
CreateLink(m_edtGroupName, m_proto->m_vkOptions.pwszDefaultGroup);
- CreateLink(m_cbDelivery, m_proto->m_vkOptions.bServerDelivery);
CreateLink(m_cbLoadLastMessageOnMsgWindowsOpen, m_proto->m_vkOptions.bLoadLastMessageOnMsgWindowsOpen);
CreateLink(m_cbUseLocalTime, m_proto->m_vkOptions.bUseLocalTime);
CreateLink(m_cbLoadOnlyFriends, m_proto->m_vkOptions.bLoadOnlyFriends);
@@ -182,13 +154,6 @@ CVkOptionAccountForm::CVkOptionAccountForm(CVkProto *proto) :
bool CVkOptionAccountForm::OnInitDialog()
{
- m_pwszOldLogin = m_edtLogin.GetText();
- m_edtLogin.SendMsg(EM_LIMITTEXT, 1024, 0);
-
- m_pwszOldPass = m_proto->GetUserStoredPassword();
- m_edtPassword.SetText(m_pwszOldPass);
- m_edtPassword.SendMsg(EM_LIMITTEXT, 1024, 0);
-
m_pwszOldGroup = m_edtGroupName.GetText();
int iListIndex = MarkMsgReadOn::markOnRead;
@@ -229,19 +194,6 @@ bool CVkOptionAccountForm::OnApply()
m_pwszOldGroup = pwszGroupName;
}
- pass_ptrW pwszNewPass(m_edtPassword.GetText());
- bool bPassChanged = mir_wstrcmp(m_pwszOldPass, pwszNewPass) != 0;
- if (bPassChanged) {
- T2Utf szRawPasswd(pwszNewPass);
- m_proto->setString("Password", szRawPasswd);
- pass_ptrA pszPass(szRawPasswd.detach());
- m_pwszOldPass = pwszNewPass;
- }
-
- ptrW pwszNewLogin(m_edtLogin.GetText());
- if (bPassChanged || mir_wstrcmpi(m_pwszOldLogin, pwszNewLogin))
- m_proto->ClearAccessToken();
- m_pwszOldLogin = pwszNewLogin;
return true;
}
@@ -448,7 +400,6 @@ CVkOptionViewForm::CVkOptionViewForm(CVkProto *proto) :
m_cbBBCForAttachmentsAdvanced(this, IDC_ATTBBC_ADV),
m_cbStikersAsSmyles(this, IDC_STICKERS_AS_SMYLES),
m_cbShortenLinksForAudio(this, IDC_SHOTEN_LINKS_FOR_AUDIO),
- m_cbAddMessageLinkToMesWAtt(this, IDC_ADD_MES_LINK_MES_ATT),
m_cbUseNonStandardNotifications(this, IDC_USENOSTDPOPUPS),
m_cbShowBeforeEditedPostVersion(this, IDC_ADD_ORIG_VER_IN_EDITED_MES)
{
@@ -456,7 +407,6 @@ CVkOptionViewForm::CVkOptionViewForm(CVkProto *proto) :
CreateLink(m_cbUseBBCOnAttacmentsAsNews, m_proto->m_vkOptions.bUseBBCOnAttacmentsAsNews);
CreateLink(m_cbStikersAsSmyles, m_proto->m_vkOptions.bStikersAsSmileys);
CreateLink(m_cbShortenLinksForAudio, m_proto->m_vkOptions.bShortenLinksForAudio);
- CreateLink(m_cbAddMessageLinkToMesWAtt, m_proto->m_vkOptions.bAddMessageLinkToMesWAtt);
CreateLink(m_cbUseNonStandardNotifications, m_proto->m_vkOptions.bUseNonStandardNotifications);
CreateLink(m_cbShowBeforeEditedPostVersion, m_proto->m_vkOptions.bShowBeforeEditedPostVersion);
diff --git a/protocols/VKontakte/src/vk_options.h b/protocols/VKontakte/src/vk_options.h
index 7ffb0c1be0..3deab3f976 100644
--- a/protocols/VKontakte/src/vk_options.h
+++ b/protocols/VKontakte/src/vk_options.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -24,20 +24,14 @@ typedef CProtoDlgBase<CVkProto> CVkDlgBase;
class CVkOptionAccountForm : public CVkDlgBase
{
- CCtrlEdit m_edtLogin;
- CCtrlEdit m_edtPassword;
- CCtrlHyperlink m_hlLink;
CCtrlEdit m_edtGroupName;
CCtrlCombo m_cbxVKLang;
- CCtrlCheck m_cbDelivery;
CCtrlCheck m_cbLoadLastMessageOnMsgWindowsOpen;
CCtrlCheck m_cbUseLocalTime;
CCtrlCheck m_cbLoadOnlyFriends;
CCtrlCombo m_cbxSyncHistory;
CCtrlCombo m_cbxMarkAsRead;
- pass_ptrW m_pwszOldPass;
- ptrW m_pwszOldLogin;
ptrW m_pwszOldGroup;
public:
@@ -149,7 +143,6 @@ class CVkOptionViewForm : public CVkDlgBase
CCtrlCheck m_cbStikersAsSmyles;
CCtrlCheck m_cbShortenLinksForAudio;
- CCtrlCheck m_cbAddMessageLinkToMesWAtt;
CCtrlCheck m_cbUseNonStandardNotifications;
CCtrlCheck m_cbShowBeforeEditedPostVersion;
diff --git a/protocols/VKontakte/src/vk_pollserver.cpp b/protocols/VKontakte/src/vk_pollserver.cpp
index d5916d8e47..eb54472188 100644
--- a/protocols/VKontakte/src/vk_pollserver.cpp
+++ b/protocols/VKontakte/src/vk_pollserver.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -143,7 +143,7 @@ void CVkProto::PollUpdates(const JSONNode &jnUpdates)
_itoa(iMessageId, szMid, 10);
T2Utf pszMsg(wszMsg);
- dbei.timestamp = tDateTime;
+ dbei.iTimestamp = tDateTime;
dbei.pBlob = pszMsg;
dbei.szId = szMid;
ProtoChainRecvMsg(hContact, dbei);
@@ -240,16 +240,19 @@ int CVkProto::PollServer()
return 0;
}
+ extern char szVKUserAgent[];
+
debugLogA("CVkProto::PollServer (online)");
int iPollConnRetry = MAX_RETRIES;
- CMStringA szReqUrl(FORMAT, "https://%s?act=a_check&key=%s&ts=%s&wait=25&access_token=%s&mode=%d&version=%d", m_szPollingServer, m_szPollingKey, m_szPollingTs, m_szAccessToken, 106, 2);
+ CMStringA szReqUrl(FORMAT, "https://%s?act=a_check&key=%s&ts=%s&wait=25&access_token=%s&mode=%d&version=%d", m_szPollingServer, m_szPollingKey, m_szPollingTs, m_szAccessToken, 106, 3);
// see mode parametr description on https://vk.com/dev/using_longpoll (Russian version)
MHttpRequest req(REQUEST_GET);
req.m_szUrl = szReqUrl.GetBuffer();
req.flags = VK_NODUMPHEADERS | NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL;
req.timeout = 30000;
req.nlc = m_hPollingConn;
+ req.AddHeader("User-Agent", szVKUserAgent);
time_t tLocalPoolThreadTimer;
{
mir_cslock lck(m_csPoolThreadTimer);
diff --git a/protocols/VKontakte/src/vk_proto.cpp b/protocols/VKontakte/src/vk_proto.cpp
index 885c2c1205..b9aa9b1c5f 100644
--- a/protocols/VKontakte/src/vk_proto.cpp
+++ b/protocols/VKontakte/src/vk_proto.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -34,6 +34,7 @@ CVkProto::CVkProto(const char *szModuleName, const wchar_t *pwszUserName) :
m_chats(1, NumericKeySortT),
m_ChatsTyping(1, NumericKeySortT),
m_iLoadHistoryTask(0),
+ m_iLoadCListIntervalCounter(0),
m_bNotifyForEndLoadingHistory(false),
m_bNotifyForEndLoadingHistoryAllContact(false),
m_hAPIConnection(nullptr),
@@ -41,6 +42,7 @@ CVkProto::CVkProto(const char *szModuleName, const wchar_t *pwszUserName) :
m_bSetBroadcast(false),
m_bNeedSendOnline(false),
m_bErr404Return(false),
+ m_bCaptchaReqInProgress(false),
m_vkOptions(this)
{
bIint64IDCompatibility = getBool("Iint64IDCompatibility");
@@ -88,6 +90,8 @@ CVkProto::CVkProto(const char *szModuleName, const wchar_t *pwszUserName) :
CreateProtoService(PS_JOINCHAT, &CVkProto::OnJoinChat);
HookProtoEvent(ME_GC_EVENT, &CVkProto::OnChatEvent);
HookProtoEvent(ME_GC_BUILDMENU, &CVkProto::OnGcMenuHook);
+
+ LoadCookies();
}
CVkProto::~CVkProto()
@@ -239,6 +243,7 @@ void CVkProto::InitMenus()
CreateProtoService(PS_GETALLSERVERHISTORYFORCONTACT, &CVkProto::SvcGetAllServerHistoryForContact);
CreateProtoService(PS_GETALLSERVERHISTORY, &CVkProto::SvcGetAllServerHistory);
CreateProtoService(PS_VISITPROFILE, &CVkProto::SvcVisitProfile);
+ CreateProtoService(PS_GOTOSITEIM, &CVkProto::SvcGoToSiteIM);
CreateProtoService(PS_CREATECHAT, &CVkProto::SvcCreateChat);
CreateProtoService(PS_ADDASFRIEND, &CVkProto::SvcAddAsFriend);
CreateProtoService(PS_DELETEFRIEND, &CVkProto::SvcDeleteFriend);
@@ -265,6 +270,13 @@ void CVkProto::InitMenus()
SET_UID(mi, 0x828cc50e, 0x398d, 0x43a2, 0xbf, 0xd3, 0xa9, 0x96, 0x47, 0x9d, 0x52, 0xff);
m_hContactMenuItems[CMI_VISITPROFILE] = Menu_AddContactMenuItem(&mi, m_szModuleName);
+ mi.pszService = PS_GOTOSITEIM;
+ mi.position = -200001000 + CMI_GOTOSITEIM;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_FORWARD);
+ mi.name.w = LPGENW("Go to site IM");
+ SET_UID(mi, 0x681c7e6f, 0x34fa, 0x4184, 0x81, 0x6f, 0x43, 0x00, 0x51, 0xe7, 0xad, 0x42);
+ m_hContactMenuItems[CMI_GOTOSITEIM] = Menu_AddContactMenuItem(&mi, m_szModuleName);
+
mi.pszService = PS_MARKMESSAGESASREAD;
mi.position = -200001000 + CMI_MARKMESSAGESASREAD;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_MARKMESSAGESASREAD);
@@ -402,6 +414,7 @@ int CVkProto::OnPreBuildContactMenu(WPARAM hContact, LPARAM)
bool bisBroadcast = !(IsEmpty(ptrW(db_get_wsa(hContact, m_szModuleName, "AudioUrl"))));
bool bIsGroup = IsGroupUser(hContact);
Menu_ShowItem(m_hContactMenuItems[CMI_VISITPROFILE], iUserId != VK_FEED_USER);
+ Menu_ShowItem(m_hContactMenuItems[CMI_GOTOSITEIM], iUserId != VK_FEED_USER);
Menu_ShowItem(m_hContactMenuItems[CMI_MARKMESSAGESASREAD], iUserId != VK_FEED_USER);
Menu_ShowItem(m_hContactMenuItems[CMI_WALLPOST], !isChatRoom(hContact));
Menu_ShowItem(m_hContactMenuItems[CMI_ADDASFRIEND], !bisFriend && !isChatRoom(hContact) && iUserId != VK_FEED_USER && !bIsGroup);
diff --git a/protocols/VKontakte/src/vk_proto.h b/protocols/VKontakte/src/vk_proto.h
index 368735e235..605a446a5e 100644
--- a/protocols/VKontakte/src/vk_proto.h
+++ b/protocols/VKontakte/src/vk_proto.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -29,6 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define PS_GETALLSERVERHISTORYFORCONTACT "/GetAllServerHystoryForContact"
#define PS_GETALLSERVERHISTORY "/GetAllServerHystory"
#define PS_VISITPROFILE "/VisitProfile"
+#define PS_GOTOSITEIM "/GoToSiteIM"
#define PS_ADDASFRIEND "/AddAsFriend"
#define PS_DELETEFRIEND "/DeleteFriend"
#define PS_BANUSER "/BanUser"
@@ -46,7 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MAXHISTORYMIDSPERONE 100
#define MAX_RETRIES 10
-#define MAX_CONTACTS_PER_REQUEST 530
+#define MAX_CONTACTS_PER_REQUEST 100
struct CVkProto : public PROTO<CVkProto>
{
@@ -81,7 +82,6 @@ struct CVkProto : public PROTO<CVkProto>
//==== Events ========================================================================
int __cdecl OnOptionsInit(WPARAM, LPARAM);
- void OnOAuthAuthorize(MHttpResponse*, AsyncHttpRequest*);
void OnReceiveAvatar(MHttpResponse*, AsyncHttpRequest*);
//==== Services ======================================================================
@@ -94,6 +94,7 @@ struct CVkProto : public PROTO<CVkProto>
//==== Menus ==========================================================================
INT_PTR __cdecl SvcVisitProfile(WPARAM hContact, LPARAM);
+ INT_PTR __cdecl SvcGoToSiteIM(WPARAM hContact, LPARAM);
INT_PTR __cdecl SvcAddAsFriend(WPARAM hContact, LPARAM);
INT_PTR __cdecl SvcWipeNonFriendContacts(WPARAM, LPARAM);
INT_PTR __cdecl SvcDeleteFriend(WPARAM hContact, LPARAM);
@@ -131,7 +132,6 @@ struct CVkProto : public PROTO<CVkProto>
bool CheckHealthThreads();
void OnTimerTic();
void ClearAccessToken();
- wchar_t* GetUserStoredPassword(void);
void ShowCaptchaInBrowser(HBITMAP hBitmap);
static mir_cs m_csTimer;
@@ -145,6 +145,8 @@ struct CVkProto : public PROTO<CVkProto>
int ForwardMsg(MCONTACT hContact, std::vector<MEVENT> &vForvardEvents, const char* szMsg);
uint8_t GetContactType(MCONTACT hContact);
+ void LogIn(LPCSTR pszUrl = nullptr);
+
private:
VKPeerType GetVKPeerType(VKUserID_t iPeerId);
@@ -159,6 +161,7 @@ private:
CMI_CHATCHANGETOPIC,
CMI_CHATINVITEUSER,
CMI_VISITPROFILE,
+ CMI_GOTOSITEIM,
CMI_MARKMESSAGESASREAD,
CMI_WALLPOST,
CMI_ADDASFRIEND,
@@ -190,6 +193,7 @@ private:
PMI_GETALLSERVERHISTORY,
PMI_WIPENONFRIENDS,
PMI_VISITPROFILE,
+ PMI_GOTOSITEIM,
PMI_COUNT
};
@@ -209,7 +213,8 @@ private:
m_bNotifyForEndLoadingHistory,
m_bNotifyForEndLoadingHistoryAllContact,
m_bTerminated,
- m_bErr404Return;
+ m_bErr404Return,
+ m_bCaptchaReqInProgress;
VKUserID_t m_iMyUserId;
@@ -247,6 +252,7 @@ private:
m_tPoolThreadTimer;
int m_iLoadHistoryTask;
+ int m_iLoadCListIntervalCounter;
OBJLIST<CVkChatInfo> m_chats;
OBJLIST<CVKChatContactTypingParam> m_ChatsTyping;
@@ -328,7 +334,8 @@ private:
//====================================================================================
void SetServerStatus(int);
- void RetrieveUsersInfo(bool bFlag = false, bool bRepeat = false);
+ void RetrieveUsersFrameInfo(CMStringA& userIDs, bool bFreeOffline, bool bSendOnline, bool bRepeat);
+ void RetrieveUsersInfo(bool bFreeOffline = false);
void RetrieveStatusMsg(const CMStringW &StatusMsg);
void RetrieveStatusMusic(const CMStringW &StatusMsg);
void OnReceiveStatus(MHttpResponse*, AsyncHttpRequest*);
@@ -340,7 +347,7 @@ private:
void RetrieveUserInfo(VKUserID_t iUserId);
void RetrieveGroupInfo(VKUserID_t iGroupID);
void RetrieveGroupInfo(CMStringA & groupIDs);
- void OnReceiveUserInfo(MHttpResponse*, AsyncHttpRequest*);
+ void OnReceiveUserFrameInfo(MHttpResponse* reply, AsyncHttpRequest* pReq);
void OnReceiveGroupInfo(MHttpResponse * reply, AsyncHttpRequest * pReq);
void RetrieveFriends(bool bCleanNonFriendContacts = false);
void OnReceiveFriends(MHttpResponse*, AsyncHttpRequest*);
@@ -372,11 +379,11 @@ private:
JSONNode& CheckJsonResponse(AsyncHttpRequest *pReq, MHttpResponse *reply, JSONNode &root);
bool CheckJsonResult(AsyncHttpRequest *pReq, const JSONNode &Node);
void OnReceiveSmth(MHttpResponse*, AsyncHttpRequest*);
- bool AutoFillForm(char*, CMStringA&, CMStringA&);
- CMStringW RunConfirmationCode(LPCWSTR pwszTitle);
CMStringW RunRenameNick(LPCWSTR pwszOldName);
void GrabCookies(MHttpResponse *nhr, CMStringA szDefDomain = "");
- void ApplyCookies(AsyncHttpRequest*);
+ void ApplyCookies(MHttpRequest*);
+ void SaveCookies();
+ void LoadCookies();
bool IsAuthContactLater(MCONTACT hContact);
bool AddAuthContactLater(MCONTACT hContact);
void __cdecl DBAddAuthRequestThread(void *p);
@@ -411,6 +418,7 @@ private:
void InitQueue();
void UninitQueue();
bool ExecuteRequest(AsyncHttpRequest*);
+ bool RestartRequest(AsyncHttpRequest*);
void __cdecl WorkerThread(void*);
AsyncHttpRequest* Push(MHttpRequest *pReq, int iTimeout = 10000);
bool RunCaptchaForm(LPCSTR szUrl, CMStringA&);
@@ -421,6 +429,7 @@ private:
void CloseAPIConnection(bool bShutdown = false);
void OnLoggedOut();
void ShutdownSession();
+ bool LoadToken(LPCSTR pszUrlSring);
void SetAvatarUrl(MCONTACT hContact, CMStringW &wszUrl);
void GetAvatarFileName(MCONTACT hContact, wchar_t *pwszDest, size_t cbLen);
void ReloadAvatarInfo(MCONTACT hContact);
diff --git a/protocols/VKontakte/src/vk_queue.cpp b/protocols/VKontakte/src/vk_queue.cpp
index fde2bc0294..6ae7873b62 100644
--- a/protocols/VKontakte/src/vk_queue.cpp
+++ b/protocols/VKontakte/src/vk_queue.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -34,64 +34,88 @@ void CVkProto::UninitQueue()
bool CVkProto::ExecuteRequest(AsyncHttpRequest *pReq)
{
- do {
- pReq->bNeedsRestart = false;
- pReq->m_iErrorCode = 0;
- pReq->m_szUrl = pReq->m_szUrl.GetBuffer();
-
- if (pReq->m_bApiReq) {
- pReq->flags |= NLHRF_PERSISTENT;
- pReq->nlc = m_hAPIConnection;
- }
+ pReq->bNeedsRestart = false;
+ pReq->m_iErrorCode = 0;
- if (m_bTerminated)
- break;
+ if (pReq->m_bApiReq) {
+ pReq->flags |= NLHRF_PERSISTENT;
+ pReq->nlc = m_hAPIConnection;
+ }
- time_t tLocalWorkThreadTimer = 0;
- {
- mir_cslock lck(m_csWorkThreadTimer);
- tLocalWorkThreadTimer = m_tWorkThreadTimer = time(0);
- }
+ if (m_bTerminated) {
+ delete pReq;
+ return false;
+ }
- debugLogA("CVkProto::ExecuteRequest \n====\n%s\n====\n", pReq->m_szUrl.c_str());
- NLHR_PTR reply(Netlib_HttpTransaction(m_hNetlibUser, pReq));
- {
- mir_cslock lck(m_csWorkThreadTimer);
- if (tLocalWorkThreadTimer != m_tWorkThreadTimer) {
- debugLogA("CVkProto::WorkerThread is living Dead => return");
- delete pReq;
- return false;
- }
- }
+ time_t tLocalWorkThreadTimer = 0;
+ {
+ mir_cslock lck(m_csWorkThreadTimer);
+ tLocalWorkThreadTimer = m_tWorkThreadTimer = time(0);
+ if (pReq->m_bApiReq)
+ ApplyCookies(pReq);
+ }
- if (reply != nullptr) {
- if (pReq->m_pFunc != nullptr)
- (this->*(pReq->m_pFunc))(reply, pReq); // may be set pReq->bNeedsRestart
+ CMStringA szParam(pReq->m_szParam);
+ if (!IsEmpty(m_szAccessToken))
+ szParam.Replace(m_szAccessToken, "*secret*");
+ debugLogA("CVkProto::ExecuteRequest \n====\n%s\n%s\n====\n", pReq->m_szUrl.c_str(), szParam.c_str());
- if (pReq->m_bApiReq)
- m_hAPIConnection = reply->nlc;
+ NLHR_PTR reply(Netlib_HttpTransaction(m_hNetlibUser, pReq));
+ {
+ mir_cslock lck(m_csWorkThreadTimer);
+ if (pReq->m_bApiReq)
+ GrabCookies(reply, "api.vk.com");
+
+ if (tLocalWorkThreadTimer != m_tWorkThreadTimer) {
+ debugLogA("CVkProto::WorkerThread is living Dead => return");
+ delete pReq;
+ return false;
}
- else if (pReq->bIsMainConn) {
- if (IsStatusConnecting(m_iStatus))
- ConnectionFailed(LOGINERR_NONETWORK);
- else if (pReq->m_iRetry && !m_bTerminated) {
- pReq->bNeedsRestart = true;
- Sleep(1000); //Pause for fix err
- pReq->m_iRetry--;
- debugLogA("CVkProto::ExecuteRequest restarting (retry = %d)", MAX_RETRIES - pReq->m_iRetry);
- }
- else {
- debugLogA("CVkProto::ExecuteRequest ShutdownSession");
- ShutdownSession();
- }
+ }
+
+ if (reply != nullptr) {
+ if (pReq->m_pFunc != nullptr)
+ (this->*(pReq->m_pFunc))(reply, pReq); // may be set pReq->bNeedsRestart
+
+ if (pReq->m_bApiReq)
+ m_hAPIConnection = reply->nlc;
+ }
+ else if (pReq->bIsMainConn) {
+ if (IsStatusConnecting(m_iStatus))
+ ConnectionFailed(LOGINERR_NONETWORK);
+ else if (pReq->m_iRetry && !m_bTerminated)
+ pReq->bNeedsRestart = true;
+ else {
+ debugLogA("CVkProto::ExecuteRequest ShutdownSession");
+ ShutdownSession();
+ return false;
}
- debugLogA("CVkProto::ExecuteRequest pReq->bNeedsRestart = %d", (int)pReq->bNeedsRestart);
+ }
+
+ if (pReq->bNeedsRestart)
+ RestartRequest(pReq);
+ else
+ delete pReq;
+
+ return true;
+}
+
+bool CVkProto::RestartRequest(AsyncHttpRequest* pReq)
+{
+ debugLogA("CVkProto::RestartRequest %d %s", (MAX_RETRIES - pReq->m_iRetry + 1), pReq->m_szUrl.c_str());
+
+ if (m_bTerminated || !pReq->m_iRetry) {
+ ShutdownSession();
+ delete pReq;
+ pReq = nullptr;
+ return false;
+ }
- if (!reply && pReq->m_bApiReq)
- CloseAPIConnection();
+ pReq->bNeedsRestart = false;
+ pReq->m_priority = AsyncHttpRequest::RequestPriority::rpRestart;
+ pReq->m_iRetry--;
- } while (pReq->bNeedsRestart && !m_bTerminated);
- delete pReq;
+ Push(pReq);
return true;
}
@@ -103,7 +127,8 @@ AsyncHttpRequest* CVkProto::Push(MHttpRequest *p, int iTimeout)
debugLogA("CVkProto::Push");
pReq->timeout = iTimeout;
- if (pReq->m_bApiReq) {
+
+ if (pReq->m_bApiReq && (pReq->m_iRetry == MAX_RETRIES)) {
pReq << VER_API;
if (!IsEmpty(m_vkOptions.pwszVKLang))
pReq << WCHAR_PARAM("lang", m_vkOptions.pwszVKLang);
@@ -137,40 +162,8 @@ void CVkProto::WorkerThread(void*)
if (m_szAccessToken != nullptr)
// try to receive a response from server
RetrieveMyInfo();
- else {
- // Initialize new OAuth session
- extern char szBlankUrl[];
- extern char szVKUserAgent[];
- extern char szVKUserAgentCH[];
-
- AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_GET, "https://oauth.vk.com/authorize", false, &CVkProto::OnOAuthAuthorize);
- pReq
- << INT_PARAM("client_id", VK_APP_ID)
- << CHAR_PARAM("scope", szScore)
- << CHAR_PARAM("redirect_uri", szBlankUrl)
- << CHAR_PARAM("display", "mobile")
- << CHAR_PARAM("response_type", "token")
- << VER_API;
-
- // Headers
- pReq->AddHeader("User-agent", szVKUserAgent);
- pReq->AddHeader("dht", "1");
- pReq->AddHeader("origin", "https://oauth.vk.com");
- pReq->AddHeader("referer", "https://oauth.vk.com/");
- pReq->AddHeader("sec-ch-ua", szVKUserAgentCH);
- pReq->AddHeader("sec-ch-ua-mobile", "?0");
- pReq->AddHeader("sec-ch-ua-platform", "Windows");
- pReq->AddHeader("sec-fetch-dest", "document");
- pReq->AddHeader("sec-fetch-mode", "navigate");
- pReq->AddHeader("sec-fetch-site", "same-site");
- pReq->AddHeader("sec-fetch-user", "?1");
- pReq->AddHeader("upgrade-insecure-requests", "1");
- //Headers
-
- pReq->m_bApiReq = false;
- pReq->bIsMainConn = true;
- Push(pReq);
- }
+ else
+ LogIn();
CloseAPIConnection();
@@ -197,6 +190,8 @@ void CVkProto::WorkerThread(void*)
if (!(pReq->m_bApiReq))
lWaitingTime = 0;
+ else if (pReq->m_priority == AsyncHttpRequest::rpLowCListEvents) // for 'error 9' fix
+ lWaitingTime += m_vkOptions.iTimeoutForUsersGet > 60000 ? 5000 : m_vkOptions.iTimeoutForUsersGet;
}
if (m_bTerminated)
diff --git a/protocols/VKontakte/src/vk_search.cpp b/protocols/VKontakte/src/vk_search.cpp
index 8a3b85ef58..fbc0f3b565 100644
--- a/protocols/VKontakte/src/vk_search.cpp
+++ b/protocols/VKontakte/src/vk_search.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/vk_status.cpp b/protocols/VKontakte/src/vk_status.cpp
index c6b74160c8..4aae624dbf 100644
--- a/protocols/VKontakte/src/vk_status.cpp
+++ b/protocols/VKontakte/src/vk_status.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -31,11 +31,10 @@ int CVkProto::SetStatus(int iNewStatus)
m_iDesiredStatus = iNewStatus;
if (iNewStatus == ID_STATUS_OFFLINE) {
- if (IsOnline()) {
+ if (IsOnline())
SetServerStatus(ID_STATUS_OFFLINE);
- debugLogA("CVkProto::SetStatus ShutdownSession");
- ShutdownSession();
- }
+ debugLogA("CVkProto::SetStatus ShutdownSession");
+ ShutdownSession();
m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
diff --git a/protocols/VKontakte/src/vk_struct.cpp b/protocols/VKontakte/src/vk_struct.cpp
index a17eef2d4b..6c134010df 100644
--- a/protocols/VKontakte/src/vk_struct.cpp
+++ b/protocols/VKontakte/src/vk_struct.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -20,11 +20,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////
ULONG AsyncHttpRequest::m_uReqCount = 0;
+char szVKUserAgent[] = "Miranda NG/0.96";
AsyncHttpRequest::AsyncHttpRequest()
{
m_bApiReq = true;
AddHeader("Connection", "keep-alive");
+ AddHeader("User-Agent", szVKUserAgent);
+
pUserInfo = nullptr;
m_iRetry = MAX_RETRIES;
m_iErrorCode = 0;
@@ -40,6 +43,7 @@ AsyncHttpRequest::AsyncHttpRequest(CVkProto *ppro, int iRequestType, LPCSTR _url
m_bApiReq = true;
bIsMainConn = false;
AddHeader("Connection", "keep-alive");
+ AddHeader("User-Agent", szVKUserAgent);
if (ppro->bIint64IDCompatibility)
AddHeader("X-Owner", "long");
@@ -53,7 +57,9 @@ AsyncHttpRequest::AsyncHttpRequest(CVkProto *ppro, int iRequestType, LPCSTR _url
else
m_szUrl = _url;
- flags = VK_NODUMPHEADERS | NLHRF_DUMPASTEXT | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ flags |= VK_NODUMPHEADERS;
+
if (bSecure) {
flags |= NLHRF_SSL;
this << CHAR_PARAM("access_token", ppro->m_szAccessToken);
@@ -160,7 +166,6 @@ CVkChatUser* CVkChatInfo::GetUserById(VKUserID_t iUserId)
CVKOptions::CVKOptions(PROTO_INTERFACE* proto) :
bLoadLastMessageOnMsgWindowsOpen(proto, "LoadLastMessageOnMsgWindowsOpen", true),
bLoadOnlyFriends(proto, "LoadOnlyFriends", false),
- bServerDelivery(proto, "BsDirect", true),
bHideChats(proto, "HideChats", true),
bMesAsUnread(proto, "MesAsUnread", false),
bUseLocalTime(proto, "UseLocalTime", false),
@@ -201,7 +206,6 @@ CVKOptions::CVKOptions(PROTO_INTERFACE* proto) :
bLoadSentAttachments(proto, "LoadSentAttachments", bSendVKLinksAsAttachments),
bUseNonStandardNotifications(proto, "UseNonStandardNotifications", false),
bShortenLinksForAudio(proto, "ShortenLinksForAudio", true),
- bAddMessageLinkToMesWAtt(proto, "AddMessageLinkToMesWAtt", true),
bShowReplyInMessage(proto, "ShowReplyInMessage", false),
bSplitFormatFwdMsg(proto, "SplitFormatFwdMsg", false),
bSyncReadMessageStatusFromServer(proto, "SyncReadMessageStatusFromServer", false),
@@ -209,6 +213,7 @@ CVKOptions::CVKOptions(PROTO_INTERFACE* proto) :
bShowVkDeactivateEvents(proto, "ShowVkDeactivateEvents", true),
bStickerBackground(proto, "StickerBackground", false),
bFilterAudioMessages(proto, "FilterAudioMessages", false),
+ bRepeatRequestAfterEmptyFrame(proto, "RepeatRequestAfterEmptyFrame", false),
bShowProtoMenuItem0(proto, "ShowProtoMenuItem0", true),
bShowProtoMenuItem1(proto, "ShowProtoMenuItem1", true),
@@ -236,17 +241,11 @@ CVKOptions::CVKOptions(PROTO_INTERFACE* proto) :
iInvisibleInterval(proto, "InvisibleInterval", 10),
iMaxFriendsCount(proto, "MaxFriendsCount", 1000),
iStickerSize(proto, "StickerSize", 128),
+ iTimeoutForUsersGet(proto, "TimeoutForUsersGet", 0),
+ iLoadCListInterval(proto, "LoadCListInterval", 1),
pwszDefaultGroup(proto, "ProtoGroup", L"VKontakte"),
pwszVKLang(proto, "VKLang", nullptr)
{
- // Note: Delete this code after next stable build
- int iStikersAsSmyles = db_get_b(0, proto->m_szModuleName, "StikersAsSmyles", -1);
- if (iStikersAsSmyles != -1) {
- bStikersAsSmileys = iStikersAsSmyles == 1;
- db_set_b(0, proto->m_szModuleName, "StikersAsSmileys", (uint8_t)iStikersAsSmyles);
- db_unset(0, proto->m_szModuleName, "StikersAsSmyles");
- }
- // Note
} \ No newline at end of file
diff --git a/protocols/VKontakte/src/vk_struct.h b/protocols/VKontakte/src/vk_struct.h
index 8dce08e013..aa1014dca2 100644
--- a/protocols/VKontakte/src/vk_struct.h
+++ b/protocols/VKontakte/src/vk_struct.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
struct AsyncHttpRequest : public MTHttpRequest<CVkProto>
{
- enum RequestPriority { rpLowLow, rpLow, rpMedium, rpHigh };
+ enum RequestPriority { rpLowLow, rpLowCListEvents, rpLow, rpMedium, rpHigh, rpRestart, rpCaptcha };
AsyncHttpRequest();
AsyncHttpRequest(CVkProto*, int iRequestType, LPCSTR szUrl, bool bSecure, MTHttpRequestHandler pFunc, RequestPriority rpPriority = rpMedium);
@@ -254,7 +254,6 @@ struct CVkCookie
struct CVKOptions {
CMOption<uint8_t> bLoadLastMessageOnMsgWindowsOpen;
CMOption<uint8_t> bLoadOnlyFriends;
- CMOption<uint8_t> bServerDelivery;
CMOption<uint8_t> bHideChats;
CMOption<uint8_t> bMesAsUnread;
CMOption<uint8_t> bUseLocalTime;
@@ -302,7 +301,6 @@ struct CVKOptions {
CMOption<uint8_t> bNotificationFilterAcceptedFriends;
CMOption<uint8_t> bUseNonStandardNotifications;
CMOption<uint8_t> bShortenLinksForAudio;
- CMOption<uint8_t> bAddMessageLinkToMesWAtt;
CMOption<uint8_t> bShowReplyInMessage;
CMOption<uint8_t> bSplitFormatFwdMsg;
CMOption<uint8_t> bSyncReadMessageStatusFromServer;
@@ -312,6 +310,7 @@ struct CVKOptions {
CMOption<uint8_t> bShowVkDeactivateEvents;
CMOption<uint8_t> bStickerBackground;
CMOption<uint8_t> bFilterAudioMessages;
+ CMOption<uint8_t> bRepeatRequestAfterEmptyFrame;
CMOption<uint8_t> bShowProtoMenuItem0;
CMOption<uint8_t> bShowProtoMenuItem1;
@@ -329,6 +328,9 @@ struct CVKOptions {
CMOption<uint32_t> iMaxFriendsCount;
CMOption<uint32_t> iStickerSize;
+ CMOption<uint32_t> iTimeoutForUsersGet;
+ CMOption<uint32_t> iLoadCListInterval;
+
CMOption<wchar_t*> pwszDefaultGroup;
CMOption<wchar_t*> pwszVKLang;
diff --git a/protocols/VKontakte/src/vk_thread.cpp b/protocols/VKontakte/src/vk_thread.cpp
index 178809ddbe..87820273ad 100644
--- a/protocols/VKontakte/src/vk_thread.cpp
+++ b/protocols/VKontakte/src/vk_thread.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
@@ -22,12 +22,11 @@ mir_cs CVkProto::m_csTimer;
char szBlankUrl[] = "https://oauth.vk.com/blank.html";
char szScore[] = "friends,photos,audio,docs,video,wall,messages,offline,status,notifications,groups";
-char szVKUserAgent[] = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
-char szVKUserAgentCH[] = "\"Microsoft Edge\";v =\"95\", \"Chromium\";v =\"95\", \";Not A Brand\";v = \"99\"";
+char szVKTokenBeg[] = "access_token=";
+char szVKCookieDomain[] = ".vk.com";
+
+
-static char szVKTokenBeg[] = "access_token=";
-static char szVKLoginDomain[] = "https://m.vk.com";
-static char szVKCookieDomain[] = ".vk.com";
static char szFieldsName[] = "id, first_name, last_name, photo_100, bdate, sex, timezone, "
"contacts, last_seen, online, status, country, city, relation, interests, activities, "
"music, movies, tv, books, games, quotes, about, domain, can_write_private_message";
@@ -119,7 +118,12 @@ static void CALLBACK VKUnsetTimer(void*)
void CVkProto::OnTimerTic()
{
- RetrieveUsersInfo(true);
+
+ if (++m_iLoadCListIntervalCounter >= m_vkOptions.iLoadCListInterval) {
+ RetrieveUsersInfo(true);
+ m_iLoadCListIntervalCounter = 0;
+ }
+
RetrieveUnreadEvents();
SetServerStatus(m_iDesiredStatus);
}
@@ -204,169 +208,46 @@ void CVkProto::OnLoggedOut()
/////////////////////////////////////////////////////////////////////////////////////////
-void CVkProto::OnOAuthAuthorize(MHttpResponse *reply, AsyncHttpRequest*)
+bool CVkProto::LoadToken(LPCSTR pszUrlSring)
{
- debugLogA("CVkProto::OnOAuthAuthorize %d", reply->resultCode);
- GrabCookies(reply, szVKCookieDomain);
-
- if (reply->resultCode == 404 && !m_bErr404Return) {
- m_bErr404Return = true;
- setString("AccessScore", szScore);
- AsyncHttpRequest* pReq = new AsyncHttpRequest(this, REQUEST_GET, "https://oauth.vk.com/authorize", false, &CVkProto::OnOAuthAuthorize);
- pReq
- << INT_PARAM("client_id", VK_APP_ID)
- << CHAR_PARAM("scope", szScore)
- << CHAR_PARAM("redirect_uri", szBlankUrl)
- << CHAR_PARAM("display", "mobile")
- << CHAR_PARAM("response_type", "token")
- << VER_API;
- pReq->m_bApiReq = false;
- pReq->bIsMainConn = true;
- ApplyCookies(pReq);
- pReq->AddHeader("User-agent", szVKUserAgent);
- Push(pReq);
- return;
- }
-
- if (reply->resultCode == 302) { // manual redirect
- LPCSTR pszLocation = reply->FindHeader("Location");
- if (pszLocation) {
- if (!_strnicmp(pszLocation, szBlankUrl, sizeof(szBlankUrl) - 1)) {
- m_szAccessToken = nullptr;
- LPCSTR p = strstr(pszLocation, szVKTokenBeg);
- if (p) {
- p += sizeof(szVKTokenBeg) - 1;
- for (LPCSTR q = p + 1; *q; q++) {
- if (*q == '&' || *q == '=' || *q == '\"') {
- m_szAccessToken = mir_strndup(p, q - p);
- break;
- }
- }
- if (m_szAccessToken == nullptr)
- m_szAccessToken = mir_strdup(p);
- setString("AccessToken", m_szAccessToken);
- RetrieveMyInfo();
- }
- else
- ConnectionFailed(LOGINERR_NOSERVER);
- }
- else {
- AsyncHttpRequest *pRedirectReq = new AsyncHttpRequest();
- pRedirectReq->requestType = REQUEST_GET;
- pRedirectReq->flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11;
- pRedirectReq->m_pFunc = &CVkProto::OnOAuthAuthorize;
- pRedirectReq->AddHeader("Referer", m_szPrevUrl);
- pRedirectReq->Redirect(reply);
- if (!pRedirectReq->m_szUrl.IsEmpty()) {
- if (pRedirectReq->m_szUrl[0] == '/')
- pRedirectReq->m_szUrl = szVKLoginDomain + pRedirectReq->m_szUrl;
- ApplyCookies(pRedirectReq);
- m_szPrevUrl = pRedirectReq->m_szUrl;
- }
-
- pRedirectReq->m_bApiReq = false;
- pRedirectReq->bIsMainConn = true;
- // Headers
- pRedirectReq->AddHeader("User-agent", szVKUserAgent);
- pRedirectReq->AddHeader("dht", "1");
- pRedirectReq->AddHeader("sec-ch-ua", szVKUserAgentCH);
- pRedirectReq->AddHeader("sec-ch-ua-mobile", "?0");
- pRedirectReq->AddHeader("sec-ch-ua-platform", "Windows");
- pRedirectReq->AddHeader("sec-fetch-dest", "document");
- pRedirectReq->AddHeader("sec-fetch-mode", "navigate");
- pRedirectReq->AddHeader("sec-fetch-site", "same-site");
- pRedirectReq->AddHeader("sec-fetch-user", "?1");
- pRedirectReq->AddHeader("upgrade-insecure-requests", "1");
- //Headers
-
- Push(pRedirectReq);
- }
- }
- else
- ConnectionFailed(LOGINERR_NOSERVER);
- return;
- }
-
- if (reply->resultCode != 200 || reply->body.IsEmpty() || (!(strstr(reply->body, "method=\"post\"") || strstr(reply->body, "method=\"POST\"")) && !strstr(reply->body, "meta http-equiv=\"refresh\""))) { // something went wrong
- ConnectionFailed(LOGINERR_NOSERVER);
- return;
- }
-
- LPCSTR pBlankUrl = strstr(reply->body, szBlankUrl);
- if (pBlankUrl) {
- debugLogA("CVkProto::OnOAuthAuthorize blank ulr found");
- m_szAccessToken = nullptr;
- LPCSTR p = strstr(pBlankUrl, szVKTokenBeg);
- if (p) {
- p += sizeof(szVKTokenBeg) - 1;
- for (LPCSTR q = p + 1; *q; q++) {
- if (*q == '&' || *q == '=' || *q == '\"') {
- m_szAccessToken = mir_strndup(p, q - p);
- break;
- }
- }
- setString("AccessToken", m_szAccessToken);
- RetrieveMyInfo();
- }
- else {
- debugLogA("CVkProto::OnOAuthAuthorize blank ulr found, access_token not found");
- ConnectionFailed(LOGINERR_NOSERVER);
- }
- return;
+ LPCSTR p = strstr(pszUrlSring, szVKTokenBeg);
+ if (!p) {
+ debugLogA("CVkProto::LoadToken error");
+ return false;
}
-
- auto *pMsgWarning = strstr(reply->body, "service_msg_warning");
- if (pMsgWarning) {
- auto *p1 = strchr(pMsgWarning, '>');
- auto *p2 = strchr(pMsgWarning, '<');
- if (p1 && p2 && (p1 + 1 < p2)) {
- CMStringA szMsg(p1 + 1, (int)(p2 - p1 - 1));
- MsgPopup(ptrW(mir_utf8decodeW(szMsg)), TranslateT("Service message"), true);
- debugLogA("CVkProto::OnOAuthAuthorize %s", szMsg.c_str());
+
+ p += sizeof(szVKTokenBeg) - 1;
+ for (LPCSTR q = p + 1; *q; q++) {
+ if (*q == '&' || *q == '=' || *q == '\"') {
+ m_szAccessToken = mir_strndup(p, q - p);
+ break;
}
- ConnectionFailed(LOGINERR_WRONGPASSWORD);
- return;
}
+ if (m_szAccessToken == nullptr)
+ m_szAccessToken = mir_strdup(p);
+ setString("AccessToken", m_szAccessToken);
+ setDword("LastAccessTokenTime", time(0));
+ debugLogA("CVkProto::LoadToken OK");
+ return true;
+}
- CMStringA szAction, szBody;
- bool bSuccess = AutoFillForm(reply->body.GetBuffer(), szAction, szBody);
- if (!bSuccess || szAction.IsEmpty() || szBody.IsEmpty()) {
- if (m_bPrevError) {
- ConnectionFailed(LOGINERR_NOSERVER);
- return;
- }
- m_bPrevError = true;
- }
+void CVkProto::LogIn(LPCSTR pszUrl)
+{
+ debugLogA("CVkProto::LogIn %s", pszUrl ? pszUrl : " ");
+ CMStringA szTokenReq(
+ FORMAT,
+ "https://oauth.vk.com/authorize?client_id=%d&scope=%s&redirect_uri=%s&display=mobile&response_type=token&v=%s",
+ VK_APP_ID,
+ mir_urlEncode(szScore).c_str(),
+ mir_urlEncode(szBlankUrl).c_str(),
+ VER_API
+ );
- AsyncHttpRequest *pReq = new AsyncHttpRequest();
- pReq->requestType = REQUEST_POST;
- pReq->flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11;
- pReq->m_szParam = szBody;
- pReq->m_szUrl = szAction;
- if (!pReq->m_szUrl.IsEmpty() && pReq->m_szUrl[0] == '/')
- pReq->m_szUrl = szVKLoginDomain + pReq->m_szUrl;
- m_szPrevUrl = pReq->m_szUrl;
- pReq->m_pFunc = &CVkProto::OnOAuthAuthorize;
- pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded");
- pReq->Redirect(reply);
- ApplyCookies(pReq);
- // Headers
- pReq->AddHeader("User-agent", szVKUserAgent);
- pReq->AddHeader("dht", "1");
- pReq->AddHeader("origin", "https://oauth.vk.com");
- pReq->AddHeader("referer", "https://oauth.vk.com/");
- pReq->AddHeader("sec-ch-ua", szVKUserAgentCH);
- pReq->AddHeader("sec-ch-ua-mobile", "?0");
- pReq->AddHeader("sec-ch-ua-platform", "Windows");
- pReq->AddHeader("sec-fetch-dest", "document");
- pReq->AddHeader("sec-fetch-mode", "navigate");
- pReq->AddHeader("sec-fetch-site", "same-site");
- pReq->AddHeader("sec-fetch-user", "?1");
- pReq->AddHeader("upgrade-insecure-requests", "1");
- //Headers
- pReq->m_bApiReq = false;
- pReq->bIsMainConn = true;
- Push(pReq);
+ CVkTokenForm dlg(this, pszUrl ? pszUrl : szTokenReq.c_str());
+ if (dlg.DoModal() && LoadToken(dlg.Result))
+ RetrieveMyInfo();
+ else
+ ConnectionFailed(LOGINERR_NOSERVER);
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -395,8 +276,10 @@ void CVkProto::OnReceiveMyInfo(MHttpResponse *reply, AsyncHttpRequest *pReq)
JSONNode jnRoot;
const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot);
- if (!jnResponse)
+ if (!jnResponse) {
+ ConnectionFailed(LOGINERR_NOSERVER);
return;
+ }
const JSONNode &jnUser = *(jnResponse.begin());
@@ -405,11 +288,11 @@ void CVkProto::OnReceiveMyInfo(MHttpResponse *reply, AsyncHttpRequest *pReq)
WriteVKUserID(0, m_iMyUserId);
OnLoggedIn();
- RetrieveUserInfo(m_iMyUserId);
TrackVisitor();
RetrieveUnreadMessages();
RetrieveFriends(m_vkOptions.bLoadOnlyFriends);
RetrievePollingInfo();
+ RetrieveUserInfo(m_iMyUserId);
}
MCONTACT CVkProto::SetContactInfo(const JSONNode &jnItem, bool bFlag, VKContactType vkContactType)
@@ -492,12 +375,8 @@ MCONTACT CVkProto::SetContactInfo(const JSONNode &jnItem, bool bFlag, VKContactT
if (!wszValue.IsEmpty()) {
int d, m, y, iReadCount;
iReadCount = swscanf(wszValue, L"%d.%d.%d", &d, &m, &y);
- if (iReadCount > 1) {
- if (iReadCount == 3)
- setWord(hContact, "BirthYear", y);
- setByte(hContact, "BirthDay", d);
- setByte(hContact, "BirthMonth", m);
- }
+ if (iReadCount > 1)
+ Contact::SetBirthday(hContact, d, m, (iReadCount == 3) ? y : 0);
}
wszValue = jnItem["photo_100"].as_mstring();
@@ -518,6 +397,8 @@ MCONTACT CVkProto::SetContactInfo(const JSONNode &jnItem, bool bFlag, VKContactT
int iNewStatus = (jnItem["online"].as_int() == 0) ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE;
setWord(hContact, "Status", iNewStatus);
+
+ db_unset(hContact, m_szModuleName, "EmptyFrameOffline");
if (iNewStatus == ID_STATUS_ONLINE) {
db_set_dw(hContact, "BuddyExpectator", "LastSeen", (uint32_t)time(0));
@@ -665,11 +546,20 @@ void CVkProto::RetrieveUserInfo(VKUserID_t iUserId)
return;
}
- Push(new AsyncHttpRequest(this, REQUEST_POST, "/method/execute.RetrieveUserInfo", true, &CVkProto::OnReceiveUserInfo)
+ CMStringA szUserId(FORMAT, "%d", iUserId);
+
+ Push(
+ new AsyncHttpRequest(
+ this,
+ REQUEST_POST,
+ "/method/execute.RetrieveUserInfo",
+ true,
+ &CVkProto::OnReceiveUserFrameInfo,
+ AsyncHttpRequest::rpLowCListEvents
+ )
<< INT_PARAM("userid", iUserId)
<< CHAR_PARAM("fields", szFieldsName)
- );
-
+ )->pUserInfo = mir_strdup(szUserId.c_str());
}
void CVkProto::RetrieveGroupInfo(VKUserID_t iGroupId)
@@ -694,101 +584,113 @@ void CVkProto::RetrieveGroupInfo(CMStringA& groupIDs)
<< CHAR_PARAM("group_ids", groupIDs));
}
-void CVkProto::RetrieveUsersInfo(bool bFreeOffline, bool bRepeat)
+void CVkProto::RetrieveUsersFrameInfo(CMStringA& szUserIds, bool bFreeOffline, bool bSendOnline, bool bRepeat)
{
- debugLogA("CVkProto::RetrieveUsersInfo");
- if (!IsOnline())
+ debugLogA("CVkProto::RetrieveUsersFrameInfo %s", szUserIds);
+ if (!IsOnline() || szUserIds.IsEmpty())
return;
-
- CMStringA userIDs;
- int i = 0;
- for (auto &hContact : AccContacts()) {
- VKUserID_t iUserId = ReadVKUserID(hContact);
- if (iUserId == VK_INVALID_USER || iUserId == VK_FEED_USER || iUserId < 0 || isChatRoom(hContact))
- continue;
-
- bool bIsFriend = !getBool(hContact, "Auth", true);
- if (bFreeOffline && !m_vkOptions.bLoadFullCList && bIsFriend)
- continue;
-
- if (!userIDs.IsEmpty())
- userIDs.AppendChar(',');
- userIDs.AppendFormat("%i", iUserId);
-
- if (i == MAX_CONTACTS_PER_REQUEST)
- break;
- i++;
- }
-
- Push(new AsyncHttpRequest(this, REQUEST_POST, "/method/execute.RetrieveUsersInfo", true, &CVkProto::OnReceiveUserInfo)
- << CHAR_PARAM("userids", userIDs)
+
+ Push(
+ new AsyncHttpRequest(
+ this,
+ REQUEST_POST,
+ "/method/execute.RetrieveUsersFrameInfo",
+ true,
+ &CVkProto::OnReceiveUserFrameInfo,
+ AsyncHttpRequest::rpLowCListEvents
+ )
+ << CHAR_PARAM("userids", szUserIds)
<< CHAR_PARAM("fields", (bFreeOffline ? "online,status,can_write_private_message" : szFieldsName))
<< INT_PARAM("norepeat", (int)bRepeat)
- << INT_PARAM("setonline", (int)m_bNeedSendOnline)
+ << INT_PARAM("setonline", (int)(m_bNeedSendOnline && bSendOnline))
<< INT_PARAM("func_v", (bFreeOffline && !m_vkOptions.bLoadFullCList) ? 1 : 2)
- );
-
+ )->pUserInfo = mir_strdup(szUserIds.c_str());
}
-void CVkProto::OnReceiveUserInfo(MHttpResponse *reply, AsyncHttpRequest *pReq)
+void CVkProto::OnReceiveUserFrameInfo(MHttpResponse* reply, AsyncHttpRequest* pReq)
{
- debugLogA("CVkProto::OnReceiveUserInfo %d", reply->resultCode);
+ debugLogA("CVkProto::OnReceiveUserFrameInfo %d", reply->resultCode);
if (reply->resultCode != 200 || !IsOnline())
return;
JSONNode jnRoot;
- const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot);
+ const JSONNode& jnResponse = CheckJsonResponse(pReq, reply, jnRoot);
if (!jnResponse)
return;
- const JSONNode &jnUsers = jnResponse["users"];
+ const JSONNode& jnUsers = jnResponse["users"];
if (!jnUsers)
return;
- if (!jnResponse["norepeat"].as_bool() && jnResponse["usercount"].as_int() == 0) {
- RetrieveUsersInfo(true, true);
- return;
+ CMStringA szUserIds;
+ if (pReq->pUserInfo) {
+ szUserIds = ((const char*)pReq->pUserInfo);
+ mir_free(pReq->pUserInfo);
}
- LIST<void> arContacts(10, PtrKeySortT);
+ bool bEmptyFrame = !jnResponse["norepeat"].as_bool() && jnResponse["usercount"].as_int() == 0;
- for (auto &hContact : AccContacts())
- if (!isChatRoom(hContact) && !IsGroupUser(hContact))
- arContacts.insert((HANDLE)hContact);
- for (auto &it : jnUsers) {
+
+ if (bEmptyFrame && m_vkOptions.bRepeatRequestAfterEmptyFrame) {
+ Sleep(5000);
+ RetrieveUsersFrameInfo(szUserIds, true, false, true);
+ return;
+ }
+
+ LIST<void> arContacts(100, PtrKeySortT);
+
+ for (auto& hContact : AccContacts())
+ if (!isChatRoom(hContact) && !IsGroupUser(hContact)) {
+ if (szUserIds.IsEmpty()) {
+ if (!getBool(hContact, "Auth", true))
+ arContacts.insert((HANDLE)hContact);
+ }
+ else {
+ VKUserID_t iUserId = ReadVKUserID(hContact);
+ char szId[40];
+ ltoa(iUserId, szId, 10);
+ if (szUserIds.Find(szId) >= 0)
+ arContacts.insert((HANDLE)hContact);
+ }
+ }
+
+ for (auto& it : jnUsers) {
MCONTACT hContact = SetContactInfo(it);
if (hContact)
arContacts.remove((HANDLE)hContact);
}
if (jnResponse["freeoffline"].as_bool())
- for (auto &it : arContacts) {
+ for (auto& it : arContacts) {
MCONTACT cc = (UINT_PTR)it;
VKUserID_t iUserId = ReadVKUserID(cc);
if (iUserId == m_iMyUserId || iUserId == VK_FEED_USER)
continue;
- int iContactStatus = getWord(cc, "Status", ID_STATUS_OFFLINE);
-
- if ((iContactStatus == ID_STATUS_ONLINE)
- || (iContactStatus == ID_STATUS_INVISIBLE && time(0) - getDword(cc, "InvisibleTS", 0) >= m_vkOptions.iInvisibleInterval * 60LL)) {
- setWord(cc, "Status", ID_STATUS_OFFLINE);
- SetMirVer(cc, -1);
- db_unset(cc, m_szModuleName, "ListeningTo");
+ if (getBool(cc, "EmptyFrameOffline")) {
+ int iContactStatus = getWord(cc, "Status", ID_STATUS_OFFLINE);
+ if ((iContactStatus == ID_STATUS_ONLINE)
+ || (iContactStatus == ID_STATUS_INVISIBLE && time(0) - getDword(cc, "InvisibleTS", 0) >= m_vkOptions.iInvisibleInterval * 60LL)) {
+ setWord(cc, "Status", ID_STATUS_OFFLINE);
+ SetMirVer(cc, -1);
+ db_unset(cc, m_szModuleName, "ListeningTo");
+ }
+ db_unset(cc, m_szModuleName, "EmptyFrameOffline");
}
+ else
+ setByte(cc, "EmptyFrameOffline", 1);
}
arContacts.destroy();
- AddFeedSpecialUser();
- const JSONNode &jnRequests = jnResponse["requests"];
+ const JSONNode& jnRequests = jnResponse["requests"];
if (!jnRequests)
return;
int iCount = jnRequests["count"].as_int();
- const JSONNode &jnItems = jnRequests["items"];
+ const JSONNode& jnItems = jnRequests["items"];
if (!iCount || !jnItems)
return;
@@ -802,12 +704,47 @@ void CVkProto::OnReceiveUserInfo(MHttpResponse *reply, AsyncHttpRequest *pReq)
if (!IsAuthContactLater(hContact)) {
RetrieveUserInfo(iUserId);
AddAuthContactLater(hContact);
- CVkDBAddAuthRequestThreadParam *param = new CVkDBAddAuthRequestThreadParam(hContact, false);
- ForkThread(&CVkProto::DBAddAuthRequestThread, (void *)param);
+ CVkDBAddAuthRequestThreadParam* param = new CVkDBAddAuthRequestThreadParam(hContact, false);
+ ForkThread(&CVkProto::DBAddAuthRequestThread, (void*)param);
}
}
}
+void CVkProto::RetrieveUsersInfo(bool bFreeOffline)
+{
+ debugLogA("CVkProto::RetrieveUsersInformation");
+ if (!IsOnline())
+ return;
+
+ CMStringA szUserIds;
+ int i = 0;
+ bool bSendOnline = true;
+
+ for (auto& hContact : AccContacts()) {
+ VKUserID_t iUserId = ReadVKUserID(hContact);
+ if (iUserId == VK_INVALID_USER || iUserId == VK_FEED_USER || iUserId < 0 || isChatRoom(hContact))
+ continue;
+
+ if (!szUserIds.IsEmpty())
+ szUserIds.AppendChar(',');
+ szUserIds.AppendFormat("%i", iUserId);
+
+ if (i < (MAX_CONTACTS_PER_REQUEST - 1))
+ i++;
+ else {
+ RetrieveUsersFrameInfo(szUserIds, bFreeOffline, bSendOnline, false);
+ i = 0;
+ szUserIds.Empty();
+ bSendOnline = false;
+ }
+ }
+
+ if(!szUserIds.IsEmpty())
+ RetrieveUsersFrameInfo(szUserIds, bFreeOffline, bSendOnline, false);
+
+ AddFeedSpecialUser();
+}
+
void CVkProto::OnReceiveGroupInfo(MHttpResponse *reply, AsyncHttpRequest *pReq)
{
debugLogA("CVkProto::OnReceiveUserInfo %d", reply->resultCode);
@@ -1142,4 +1079,17 @@ INT_PTR __cdecl CVkProto::SvcVisitProfile(WPARAM hContact, LPARAM)
Utils_OpenUrlW(wszUrl);
return 0;
+}
+
+INT_PTR __cdecl CVkProto::SvcGoToSiteIM(WPARAM hContact, LPARAM)
+{
+ debugLogA("CVkProto::SvcGoToSiteIM");
+
+ VKUserID_t iUserId = ReadVKUserID(hContact);
+ if (isChatRoom(hContact))
+ iUserId += VK_CHAT_MIN;
+ CMStringW wszUrl(FORMAT, L"https://vk.com/im/convo/%d?entrypoint=list_all", iUserId);
+
+ Utils_OpenUrlW(wszUrl);
+ return 0;
} \ No newline at end of file
diff --git a/protocols/VKontakte/src/vk_wallpost.cpp b/protocols/VKontakte/src/vk_wallpost.cpp
index 5a7b76fc7e..562abde590 100644
--- a/protocols/VKontakte/src/vk_wallpost.cpp
+++ b/protocols/VKontakte/src/vk_wallpost.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2013-25 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
diff --git a/protocols/VKontakte/src/vkjs.js b/protocols/VKontakte/src/vkjs.js
index 7feb127504..f40a515a40 100644
--- a/protocols/VKontakte/src/vkjs.js
+++ b/protocols/VKontakte/src/vkjs.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-24 Miranda NG team (https://miranda-ng.org)
+// Copyright (c) 2013-25 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
@@ -17,6 +17,37 @@
// 4. Create additional version(s) stored procedure (if required)
+// Stored procedure name: RetrieveUsersFrameInfo = Begin
+// Arguments:
+// Args.userids
+// Args.fields
+// Args.norepeat
+// Args.setonline
+
+// ver 1
+var Req =[];
+if (Args.setonline == 1) {
+ API.account.setOnline();
+ Req = API.friends.getRequests({ "extended": 0, "need_mutual": 0, "out": 0 });
+};
+var res = [];
+var US = API.users.get({ "user_ids": Args.userids, "fields": Args.fields, "name_case": "nom" });
+var index = US.length;
+while (index > 0) {
+ index = index - 1;
+ if (US[index].online != 0) {
+ res.push(US[index]);
+ };
+};
+return { "freeoffline": 1, "norepeat": parseInt(Args.norepeat), "usercount": res.length, "users": res , "requests": Req};
+
+// ver 2
+if (Args.setonline == 1)
+ API.account.setOnline();
+var res = API.users.get({ "user_ids": Args.userids, "fields": Args.fields, "name_case": "nom" });
+return { "freeoffline": 0, "norepeat": parseInt(Args.norepeat), "usercount": res.length, "users": res, "requests": API.friends.getRequests({ "extended": 0, "need_mutual": 0, "out": 0 })};
+// Stored procedure name: RetrieveUsersFrameInfo = End
+
// Stored procedure name: RetrieveUserInfo = Begin
// Arguments:
// Args.userid
@@ -38,7 +69,7 @@ if (Args.setonline == 1)
API.account.setOnline();
var US = [];
var res = API.users.get({ "user_ids": API.friends.getOnline(), "fields": Args.fields, "name_case": "nom" });
-var t = 10;
+var t = 3;
while (t > 0) {
US = API.users.get({ "user_ids": Args.userids, "fields": Args.fields, "name_case": "nom" });
var index = US.length;
@@ -141,7 +172,8 @@ if (index > 0) {
};
Idx = Idx + 1;
};
- var FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
+ var FUsers = [];
+ if (Uids.length > 0) FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
return { "count": index, "datetime": parseInt(Args.time), "items": ret, "fwd_users": FUsers, "once": parseInt(Args.once), "rcount": parseInt(Args.reqcount) };
} else {
return{"count":0,"datetime":parseInt(Args.time),"items":[],"fwd_users":[],"once":parseInt(Args.once),"rcount":parseInt(Args.reqcount)};
@@ -187,7 +219,8 @@ while (Idx < FMsgs.length) {
};
Idx = Idx + 1;
};
-var FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
+var FUsers = [];
+if (Uids.length > 0) FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
var Conv = API.messages.getConversationsById({"peer_ids": ConvIds});
return { "Msgs": Msgs, "fwd_users": FUsers, "conv":Conv };
// Stored procedure name: RetrieveMessagesConversationByIds = End
@@ -329,7 +362,8 @@ while (Idx < FMsgs.length) {
};
Idx = Idx + 1;
};
-var FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
+var FUsers = [];
+if (Uids.length > 0) FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
var MsgUsers = API.users.get({ "user_ids": ChatMsg.items@.user_id, "fields":"id,first_name,last_name"});
return { "info": Info, "users": ChatUsers, "msgs": ChatMsg, "fwd_users": FUsers, "msgs_users": MsgUsers };
@@ -363,7 +397,8 @@ while (Idx < FMsgs.length) {
};
Idx = Idx + 1;
};
-var FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
+var FUsers = [];
+if (Uids.length > 0) FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
var GUsers = [];
if(GUids.length>0){
GUsers = API.groups.getById({ "group_ids": GUids });
@@ -402,7 +437,8 @@ while (Idx < FMsgs.length) {
};
Idx = Idx + 1;
};
-var FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
+var FUsers = [];
+if (Uids.length > 0) FUsers = API.users.get({ "user_ids": Uids, "name_case": "gen" });
var GUsers = [];
if(GUids.length>0){
GUsers = API.groups.getById({ "group_ids": GUids });