/*
Copyright (c) 2013-15 Miranda NG project (http://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
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "stdafx.h"
void CVkProto::RetrievePollingInfo()
{
debugLogA("CVkProto::RetrievePollingInfo");
if (!IsOnline())
return;
Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/messages.getLongPollServer.json", true, &CVkProto::OnReceivePollingInfo, AsyncHttpRequest::rpHigh)
<< INT_PARAM("use_ssl", 1)
<< VER_API);
}
void CVkProto::OnReceivePollingInfo(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq)
{
debugLogA("CVkProto::OnReceivePollingInfo %d", reply->resultCode);
if (reply->resultCode != 200)
return;
JSONROOT pRoot;
JSONNODE *pResponse = CheckJsonResponse(pReq, reply, pRoot);
if (pResponse == NULL)
return;
m_pollingTs = mir_t2a(ptrT(json_as_string(json_get(pResponse, "ts"))));
m_pollingKey = mir_t2a(ptrT(json_as_string(json_get(pResponse, "key"))));
m_pollingServer = mir_t2a(ptrT(json_as_string(json_get(pResponse, "server"))));
if (!m_hPollingThread) {
debugLogA("CVkProto::OnReceivePollingInfo m_hPollingThread is NULL");
debugLogA("CVkProto::OnReceivePollingInfo m_pollingTs = \'%s' m_pollingKey = \'%s\' m_pollingServer = \'%s\'",
m_pollingTs ? m_pollingTs : "",
m_pollingKey ? m_pollingKey : "",
m_pollingServer ? m_pollingServer : "");
if (m_pollingTs != NULL && m_pollingKey != NULL && m_pollingServer != NULL) {
debugLogA("CVkProto::OnReceivePollingInfo PollingThread starting...");
m_hPollingThread = ForkThreadEx(&CVkProto::PollingThread, NULL, NULL);
}
else {
debugLogA("CVkProto::OnReceivePollingInfo PollingThread not start");
m_pollingConn = NULL;
ShutdownSession();
return;
}
}
else
debugLogA("CVkProto::OnReceivePollingInfo m_hPollingThread is not NULL");
}
void CVkProto::PollUpdates(JSONNODE *pUpdates)
{
debugLogA("CVkProto::PollUpdates");
CMStringA mids;
int msgid, uid, flags, platform;
MCONTACT hContact;
JSONNODE *pChild;
for (int i = 0; (pChild = json_at(pUpdates, i)) != NULL; i++) {
switch (json_as_int(json_at(pChild, 0))) {
case VKPOLL_MSG_DELFLAGS:
msgid = json_as_int(json_at(pChild, 1));
flags = json_as_int(json_at(pChild, 2));
uid = json_as_int(json_at(pChild, 3));
hContact = FindUser(uid);
if (hContact != NULL && (flags & VKFLAG_MSGUNREAD) && !CheckMid(m_incIds, msgid)) {
setDword(hContact, "LastMsgReadTime", time(NULL));
SetSrmmReadStatus(hContact);
if (m_bUserForceOnlineOnActivity)
SetInvisible(hContact);
}
break;
case VKPOLL_MSG_ADDED: // new message
msgid = json_as_int(json_at(pChild, 1));
// skip outgoing messages sent from a client
flags = json_as_int(json_at(pChild, 2));
if (flags & VKFLAG_MSGOUTBOX && !(flags & VKFLAG_MSGCHAT))
if (CheckMid(m_sendIds, msgid))
break;
if (!mids.IsEmpty())
mids.AppendChar(',');
mids.AppendFormat("%d", msgid);
break;
case VKPOLL_READ_ALL_OUT:
uid = json_as_int(json_at(pChild, 1));
hContact = FindUser(uid);
if (hContact != NULL) {
setDword(hContact, "LastMsgReadTime", time(NULL));
SetSrmmReadStatus(hContact);
if (m_bUserForceOnlineOnActivity)
SetInvisible(hContact);
}
break;
case VKPOLL_USR_ONLINE:
uid = -json_as_int(json_at(pChild, 1));
if ((hContact = FindUser(uid)) != NULL) {
setWord(hContact, "Status", ID_STATUS_ONLINE);
platform = json_as_int(json_at(pChild, 2));
SetMirVer(hContact, platform);
}
break;
case VKPOLL_USR_OFFLINE:
uid = -json_as_int(json_at(pChild, 1));
if ((hContact = FindUser(uid)) != NULL) {
setWord(hContact, "Status", ID_STATUS_OFFLINE);
db_unset(hContact, m_szModuleName, "ListeningTo");
SetMirVer(hContact, -1);
}
break;
case VKPOLL_USR_UTN:
uid = json_as_int(json_at(pChild, 1));
hContact = FindUser(uid);
if (hContact != NULL) {
ForkThread(&CVkProto::ContactTypingThread, (void *)hContact);
if (m_bUserForceOnlineOnActivity)
SetInvisible(hContact);
}
break;
case VKPOLL_CHAT_CHANGED:
int chat_id = json_as_int(json_at(pChild, 1));
CVkChatInfo *cc = m_chats.find((CVkChatInfo*)&chat_id);
if (cc)
RetrieveChatInfo(cc);
break;
}
}
RetrieveMessagesByIds(mids);
}
int CVkProto::PollServer()
{
debugLogA("CVkProto::PollServer");
if (!IsOnline()) {
debugLogA("CVkProto::PollServer is dead (not online)");
m_pollingConn = NULL;
ShutdownSession();
return 0;
}
debugLogA("CVkProto::PollServer (online)");
int iPollConnRetry = MAX_RETRIES;
NETLIBHTTPREQUEST *reply;
CMStringA szReqUrl;
szReqUrl.AppendFormat("https://%s?act=a_check&key=%s&ts=%s&wait=25&access_token=%s&mode=%d", m_pollingServer, m_pollingKey, m_pollingTs, m_szAccessToken, 106);
// see mode parametr description on https://vk.com/dev/using_longpoll (Russian version)
NETLIBHTTPREQUEST req = { sizeof(req) };
req.requestType = REQUEST_GET;
req.szUrl = mir_strdup(szReqUrl.GetBuffer());
req.flags = VK_NODUMPHEADERS | NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL;
req.timeout = 30000;
req.nlc = m_pollingConn;
while ((reply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)&req)) == NULL) {
debugLogA("CVkProto::PollServer is dead");
m_pollingConn = NULL;
if (iPollConnRetry && !m_bTerminated) {
iPollConnRetry--;
debugLogA("CVkProto::PollServer restarting %d", MAX_RETRIES - iPollConnRetry);
Sleep(1000);
}
else {
debugLogA("CVkProto::PollServer => ShutdownSession");
mir_free(req.szUrl);
ShutdownSession();
return 0;
}
}
mir_free(req.szUrl);
int retVal = 0;
if (reply->resultCode == 200) {
JSONROOT pRoot(reply->pData);
JSONNODE *pFailed = json_get(pRoot, "failed");
if (pFailed != NULL && json_as_int(pFailed) > 1) {
RetrievePollingInfo();
retVal = -1;
debugLogA("Polling key expired, restarting polling thread");
}
else if (CheckJsonResult(NULL, pRoot)) {
m_pollingTs = mir_t2a(ptrT(json_as_string(json_get(pRoot, "ts"))));
JSONNODE *pUpdates = json_get(pRoot, "updates");
if (pUpdates != NULL)
PollUpdates(pUpdates);
retVal = 1;
}
}
else if ((reply->resultCode >= 400 && reply->resultCode <= 417)
|| (reply->resultCode >= 500 && reply->resultCode <= 509)) {
debugLogA("CVkProto::PollServer is dead. Error code - %d", reply->resultCode);
m_pollingConn = NULL;
CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)reply);
ShutdownSession();
return 0;
}
m_pollingConn = reply->nlc;
CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)reply);
debugLogA("CVkProto::PollServer return %d", retVal);
return retVal;
}
void CVkProto::PollingThread(void*)
{
debugLogA("CVkProto::PollingThread: entering");
while (!m_bTerminated)
if (PollServer() == -1)
break;
m_hPollingThread = NULL;
m_pollingConn = NULL;
debugLogA("CVkProto::PollingThread: leaving");
}