/*
Copyright (c) 2015-23 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
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 CSkypeProto::PollingThread(void *)
{
debugLogA(__FUNCTION__ ": entering");
while (true) {
m_hPollingEvent.Wait();
if (m_bThreadsTerminated)
break;
int nErrors = 0;
m_iPollingId = -1;
while ((nErrors < POLLING_ERRORS_LIMIT) && m_iStatus != ID_STATUS_OFFLINE) {
std::unique_ptr request(new PollRequest(this));
NLHR_PTR response(DoSend(request.get()));
if (response == nullptr) {
nErrors++;
continue;
}
if (response->resultCode == 200) {
nErrors = 0;
if (response->pData)
ParsePollData(response->pData);
}
else {
nErrors++;
if (response->pData) {
JSONNode root = JSONNode::parse(response->pData);
const JSONNode &error = root["errorCode"];
if (error && error.as_int() == 729)
break;
}
}
}
if (m_iStatus != ID_STATUS_OFFLINE) {
debugLogA(__FUNCTION__ ": unexpected termination; switching protocol to offline");
SetStatus(ID_STATUS_OFFLINE);
}
}
m_hPollingThread = nullptr;
debugLogA(__FUNCTION__ ": leaving");
}
void CSkypeProto::ParsePollData(const char *szData)
{
debugLogA(__FUNCTION__);
JSONNode data = JSONNode::parse(szData);
if (!data)
return;
for (auto &message : data["eventMessages"]) {
int eventId = message["id"].as_int();
if (eventId > m_iPollingId)
m_iPollingId = eventId;
const JSONNode &resType = message["resourceType"];
const JSONNode &resource = message["resource"];
std::string resourceType = resType.as_string();
if (resourceType == "NewMessage") {
ProcessNewMessage(resource);
}
else if (resourceType == "UserPresence") {
ProcessUserPresence(resource);
}
else if (resourceType == "EndpointPresence") {
ProcessEndpointPresence(resource);
}
else if (resourceType == "ConversationUpdate") {
ProcessConversationUpdate(resource);
}
else if (resourceType == "ThreadUpdate") {
ProcessThreadUpdate(resource);
}
}
}
void CSkypeProto::ProcessEndpointPresence(const JSONNode &node)
{
debugLogA(__FUNCTION__);
std::string selfLink = node["selfLink"].as_string();
CMStringA skypename(UrlToSkypeId(selfLink.c_str()));
MCONTACT hContact = FindContact(skypename);
if (hContact == NULL)
return;
const JSONNode &publicInfo = node["publicInfo"];
const JSONNode &privateInfo = node["privateInfo"];
CMStringA MirVer;
if (publicInfo) {
std::string skypeNameVersion = publicInfo["skypeNameVersion"].as_string();
std::string version = publicInfo["version"].as_string();
std::string typ = publicInfo["typ"].as_string();
int iTyp = atoi(typ.c_str());
switch (iTyp) {
case 0:
case 1:
MirVer.Append("Skype (Web) " + ParseUrl(version.c_str(), "/"));
break;
case 10:
MirVer.Append("Skype (XBOX) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 17:
MirVer.Append("Skype (Android) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 16:
MirVer.Append("Skype (iOS) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 12:
MirVer.Append("Skype (WinRT) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 15:
MirVer.Append("Skype (WP) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 13:
MirVer.Append("Skype (OSX) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 11:
MirVer.Append("Skype (Windows) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 14:
MirVer.Append("Skype (Linux) " + ParseUrl(skypeNameVersion.c_str(), "/"));
break;
case 125:
MirVer.AppendFormat("Miranda NG Skype %s", version.c_str());
break;
default:
MirVer.Append("Skype (Unknown)");
}
}
if (privateInfo != NULL) {
std::string epname = privateInfo["epname"].as_string();
if (!epname.empty()) {
MirVer.AppendFormat(" [%s]", epname.c_str());
}
}
setString(hContact, "MirVer", MirVer);
}
void CSkypeProto::ProcessUserPresence(const JSONNode &node)
{
debugLogA(__FUNCTION__);
std::string selfLink = node["selfLink"].as_string();
std::string status = node["status"].as_string();
CMStringA skypename = UrlToSkypeId(selfLink.c_str());
if (!skypename.IsEmpty()) {
if (IsMe(skypename)) {
int iNewStatus = SkypeToMirandaStatus(status.c_str());
if (iNewStatus == ID_STATUS_OFFLINE) return;
int old_status = m_iStatus;
m_iDesiredStatus = iNewStatus;
m_iStatus = iNewStatus;
if (old_status != iNewStatus)
ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, iNewStatus);
}
else {
MCONTACT hContact = FindContact(skypename);
if (hContact != NULL)
SetContactStatus(hContact, SkypeToMirandaStatus(status.c_str()));
}
}
}
void CSkypeProto::ProcessNewMessage(const JSONNode &node)
{
debugLogA(__FUNCTION__);
std::string conversationLink = node["conversationLink"].as_string();
int iUserType;
UrlToSkypeId(conversationLink.c_str(), &iUserType);
if (iUserType == 2 || iUserType == 8)
OnPrivateMessageEvent(node);
else if (iUserType == 19)
OnChatEvent(node);
}
void CSkypeProto::ProcessConversationUpdate(const JSONNode &) {}
void CSkypeProto::ProcessThreadUpdate(const JSONNode &) {}