summaryrefslogtreecommitdiff
path: root/protocols/Teams/src
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Teams/src')
-rw-r--r--protocols/Teams/src/main.cpp17
-rw-r--r--protocols/Teams/src/requests/capabilities.h41
-rw-r--r--protocols/Teams/src/requests/contacts.h89
-rw-r--r--protocols/Teams/src/requests/poll.h38
-rw-r--r--protocols/Teams/src/stdafx.cxx2
-rw-r--r--protocols/Teams/src/stdafx.h7
-rw-r--r--protocols/Teams/src/teams_avatars.cpp4
-rw-r--r--protocols/Teams/src/teams_chatrooms.cpp6
-rw-r--r--protocols/Teams/src/teams_contacts.cpp53
-rw-r--r--protocols/Teams/src/teams_endpoint.cpp71
-rw-r--r--protocols/Teams/src/teams_files.cpp19
-rw-r--r--protocols/Teams/src/teams_history.cpp6
-rw-r--r--protocols/Teams/src/teams_http.cpp4
-rw-r--r--protocols/Teams/src/teams_login.cpp4
-rw-r--r--protocols/Teams/src/teams_menus.cpp2
-rw-r--r--protocols/Teams/src/teams_mood.cpp2
-rw-r--r--protocols/Teams/src/teams_options.cpp2
-rw-r--r--protocols/Teams/src/teams_polling.cpp39
-rw-r--r--protocols/Teams/src/teams_popups.cpp17
-rw-r--r--protocols/Teams/src/teams_profile.cpp4
-rw-r--r--protocols/Teams/src/teams_proto.cpp40
-rw-r--r--protocols/Teams/src/teams_proto.h70
-rw-r--r--protocols/Teams/src/teams_search.cpp4
-rw-r--r--protocols/Teams/src/teams_trouter.cpp367
-rw-r--r--protocols/Teams/src/teams_utils.cpp2
-rw-r--r--protocols/Teams/src/teams_utils.h4
-rw-r--r--protocols/Teams/src/version.h2
27 files changed, 592 insertions, 324 deletions
diff --git a/protocols/Teams/src/main.cpp b/protocols/Teams/src/main.cpp
index d2ac241040..07778b2405 100644
--- a/protocols/Teams/src/main.cpp
+++ b/protocols/Teams/src/main.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
CMPlugin g_plugin;
diff --git a/protocols/Teams/src/requests/capabilities.h b/protocols/Teams/src/requests/capabilities.h
deleted file mode 100644
index 566d946e3e..0000000000
--- a/protocols/Teams/src/requests/capabilities.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-Copyright (c) 2015-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
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_REQUEST_CAPS_H_
-#define _SKYPE_REQUEST_CAPS_H_
-
-struct SendCapabilitiesRequest : public AsyncHttpRequest
-{
- SendCapabilitiesRequest(const char *hostname, CTeamsProto *ppro) :
- AsyncHttpRequest(REQUEST_PUT, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(ppro->m_szId) + "/presenceDocs/messagingService", &CTeamsProto::OnCapabilitiesSended)
- {
- JSONNode privateInfo; privateInfo.set_name("privateInfo");
- privateInfo << CHAR_PARAM("epname", hostname);
-
- JSONNode publicInfo; publicInfo.set_name("publicInfo");
- publicInfo << CHAR_PARAM("capabilities", "Audio|Video") << INT_PARAM("typ", 125)
- << CHAR_PARAM("skypeNameVersion", "Miranda NG Skype") << CHAR_PARAM("nodeInfo", "xx") << CHAR_PARAM("version", g_szMirVer);
-
- JSONNode node;
- node << CHAR_PARAM("id", "messagingService") << CHAR_PARAM("type", "EndpointPresenceDoc")
- << CHAR_PARAM("selfLink", "uri") << privateInfo << publicInfo;
-
- m_szParam = node.write().c_str();
- }
-};
-
-#endif //_SKYPE_REQUEST_CAPS_H_
diff --git a/protocols/Teams/src/requests/contacts.h b/protocols/Teams/src/requests/contacts.h
deleted file mode 100644
index f0614f10b6..0000000000
--- a/protocols/Teams/src/requests/contacts.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright (c) 2015-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
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_REQUEST_CONTACTS_H_
-#define _SKYPE_REQUEST_CONTACTS_H_
-
-struct GetContactListRequest : public AsyncHttpRequest
-{
- GetContactListRequest() :
- AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/contacts", &CTeamsProto::LoadContactList)
- {
- }
-};
-
-struct GetContactsAuthRequest : public AsyncHttpRequest
-{
- GetContactsAuthRequest() :
- AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/invites", &CTeamsProto::LoadContactsAuth)
- {
- }
-};
-
-struct AddContactRequest : public AsyncHttpRequest
-{
- AddContactRequest(const char *who, const char *greeting = "") :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts")
- {
- JSONNode node;
- node << CHAR_PARAM("mri", who) << CHAR_PARAM("greeting", greeting);
- m_szParam = node.write().c_str();
- }
-};
-
-struct AuthAcceptRequest : public AsyncHttpRequest
-{
- AuthAcceptRequest(const char *who) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS)
- {
- m_szUrl.AppendFormat("/users/SELF/invites/%s/accept", mir_urlEncode(who).c_str());
- }
-};
-
-struct AuthDeclineRequest : public AsyncHttpRequest
-{
- AuthDeclineRequest(const char *who) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS)
- {
- m_szUrl.AppendFormat("/users/SELF/invites/%s/decline", mir_urlEncode(who).c_str());
- }
-};
-
-struct BlockContactRequest : public AsyncHttpRequest
-{
- BlockContactRequest(CTeamsProto *ppro, MCONTACT hContact) :
- AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + ppro->getId(hContact), &CTeamsProto::OnBlockContact)
- {
- m_szParam = "{\"report_abuse\":\"false\",\"ui_version\":\"skype.com\"}";
- pUserInfo = (void *)hContact;
- }
-};
-
-struct UnblockContactRequest : public AsyncHttpRequest
-{
- UnblockContactRequest(CTeamsProto *ppro, MCONTACT hContact) :
- AsyncHttpRequest(REQUEST_DELETE, HOST_CONTACTS, 0, &CTeamsProto::OnUnblockContact)
- {
- m_szUrl.AppendFormat("/users/SELF/contacts/blocklist/%s", ppro->getId(hContact).c_str());
- pUserInfo = (void *)hContact;
-
- // TODO: user ip address
- this << CHAR_PARAM("reporterIp", "123.123.123.123") << CHAR_PARAM("uiVersion", g_szMirVer);
- }
-};
-
-#endif //_SKYPE_REQUEST_CONTACTS_H_ \ No newline at end of file
diff --git a/protocols/Teams/src/requests/poll.h b/protocols/Teams/src/requests/poll.h
deleted file mode 100644
index b2573a43e2..0000000000
--- a/protocols/Teams/src/requests/poll.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright (c) 2015-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
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _SKYPE_POLL_H_
-#define _SKYPE_POLL_H_
-
-struct PollRequest : public AsyncHttpRequest
-{
- PollRequest(CTeamsProto *ppro) :
- AsyncHttpRequest(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(ppro->m_szId) + "/subscriptions/0/poll")
- {
- flags |= NLHRF_PERSISTENT;
- timeout = 120000;
-
- if (ppro->m_iPollingId != -1)
- m_szUrl.AppendFormat("?ackId=%d", ppro->m_iPollingId);
-
- AddHeader("Referer", "https://web.skype.com/main");
- AddHeader("ClientInfo", "os=Windows; osVer=8.1; proc=Win32; lcid=en-us; deviceType=1; country=n/a; clientName=swx-skype.com; clientVer=908/1.85.0.29");
- AddHeader("Accept", "application/json");
- AddHeader("Accept-Language", "en, C");
- }
-};
-#endif //_SKYPE_POLL_H_ \ No newline at end of file
diff --git a/protocols/Teams/src/stdafx.cxx b/protocols/Teams/src/stdafx.cxx
index 4a1e1f0e70..b64bcca703 100644
--- a/protocols/Teams/src/stdafx.cxx
+++ b/protocols/Teams/src/stdafx.cxx
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2025 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/Teams/src/stdafx.h b/protocols/Teams/src/stdafx.h
index de676dace4..7cbbec2676 100644
--- a/protocols/Teams/src/stdafx.h
+++ b/protocols/Teams/src/stdafx.h
@@ -88,16 +88,11 @@ struct AsyncHttpRequest : public MTHttpRequest<CTeamsProto>
#include "teams_proto.h"
-#include "requests/capabilities.h"
#include "requests/chatrooms.h"
-#include "requests/contacts.h"
#include "requests/history.h"
-#include "requests/poll.h"
#include "requests/profile.h"
#include "requests/search.h"
#include "requests/status.h"
#include "requests/subscriptions.h"
-#define POLLING_ERRORS_LIMIT 3
-
-#endif //_COMMON_H_ \ No newline at end of file
+#endif //_COMMON_H_
diff --git a/protocols/Teams/src/teams_avatars.cpp b/protocols/Teams/src/teams_avatars.cpp
index 6bbac21d04..533aa62d08 100644
--- a/protocols/Teams/src/teams_avatars.cpp
+++ b/protocols/Teams/src/teams_avatars.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -191,7 +191,7 @@ struct SetAvatarRequest : public AsyncHttpRequest
void CTeamsProto::OnSentAvatar(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply root(response);
+ TeamsReply root(response);
if (root.error())
return;
}
diff --git a/protocols/Teams/src/teams_chatrooms.cpp b/protocols/Teams/src/teams_chatrooms.cpp
index 42c5f269ce..e4012e376f 100644
--- a/protocols/Teams/src/teams_chatrooms.cpp
+++ b/protocols/Teams/src/teams_chatrooms.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -353,7 +353,7 @@ void CTeamsProto::SendChatMessage(SESSION_INFO *si, const wchar_t *tszMessage)
void CTeamsProto::OnGetChatMembers(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -375,7 +375,7 @@ void CTeamsProto::OnGetChatMembers(MHttpResponse *response, AsyncHttpRequest *pR
void CTeamsProto::OnGetChatInfo(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
diff --git a/protocols/Teams/src/teams_contacts.cpp b/protocols/Teams/src/teams_contacts.cpp
index b8f94be72d..8579aed3c3 100644
--- a/protocols/Teams/src/teams_contacts.cpp
+++ b/protocols/Teams/src/teams_contacts.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -100,9 +100,11 @@ MCONTACT CTeamsProto::AddContact(const char *skypeId, const char *nick, bool isT
return hContact;
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::LoadContactsAuth(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -140,7 +142,7 @@ void CTeamsProto::LoadContactsAuth(MHttpResponse *response, AsyncHttpRequest*)
void CTeamsProto::LoadContactList(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -220,16 +222,14 @@ void CTeamsProto::LoadContactList(MHttpResponse *response, AsyncHttpRequest*)
}
}
- PushRequest(new GetContactsAuthRequest());
+ PushRequest(new AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/invites", &CTeamsProto::LoadContactsAuth));
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
INT_PTR CTeamsProto::OnRequestAuth(WPARAM hContact, LPARAM)
{
- if (hContact == INVALID_CONTACT_ID)
- return 1;
-
- PushRequest(new AddContactRequest(getId(hContact)));
- return 0;
+ return AuthRequest(hContact, 0);
}
INT_PTR CTeamsProto::OnGrantAuth(WPARAM hContact, LPARAM)
@@ -237,7 +237,7 @@ INT_PTR CTeamsProto::OnGrantAuth(WPARAM hContact, LPARAM)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthAcceptRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/accept"));
return 0;
}
@@ -257,15 +257,6 @@ bool CTeamsProto::OnContactDeleted(MCONTACT hContact, uint32_t flags)
/////////////////////////////////////////////////////////////////////////////////////////
-INT_PTR CTeamsProto::BlockContact(WPARAM hContact, LPARAM)
-{
- if (!IsOnline()) return 1;
-
- if (IDYES == MessageBox(NULL, TranslateT("Are you sure?"), TranslateT("Warning"), MB_YESNO | MB_ICONQUESTION))
- PushRequest(new BlockContactRequest(this, hContact));
- return 0;
-}
-
void CTeamsProto::OnBlockContact(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
MCONTACT hContact = (DWORD_PTR)pRequest->pUserInfo;
@@ -273,12 +264,21 @@ void CTeamsProto::OnBlockContact(MHttpResponse *response, AsyncHttpRequest *pReq
Contact::Hide(hContact);
}
-INT_PTR CTeamsProto::UnblockContact(WPARAM hContact, LPARAM)
+INT_PTR CTeamsProto::BlockContact(WPARAM hContact, LPARAM)
{
- PushRequest(new UnblockContactRequest(this, hContact));
+ if (!IsOnline()) return 1;
+
+ if (IDYES == MessageBox(NULL, TranslateT("Are you sure?"), TranslateT("Warning"), MB_YESNO | MB_ICONQUESTION)) {
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + mir_urlEncode(getId(hContact)), &CTeamsProto::OnBlockContact);
+ pReq->m_szParam = "{\"report_abuse\":\"false\",\"ui_version\":\"skype.com\"}";
+ pReq->pUserInfo = (void *)hContact;
+ PushRequest(pReq);
+ }
return 0;
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::OnUnblockContact(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
if (response == nullptr)
@@ -288,3 +288,14 @@ void CTeamsProto::OnUnblockContact(MHttpResponse *response, AsyncHttpRequest *pR
Contact::Hide(hContact, false);
delSetting(hContact, "IsBlocked");
}
+
+INT_PTR CTeamsProto::UnblockContact(WPARAM hContact, LPARAM)
+{
+ if (!IsOnline()) return 1;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_DELETE, HOST_CONTACTS, "/users/SELF/contacts/blocklist/" + mir_urlEncode(getId(hContact)), &CTeamsProto::OnUnblockContact);
+ pReq->pUserInfo = (void *)hContact;
+ pReq << CHAR_PARAM("reporterIp", "123.123.123.123") << CHAR_PARAM("uiVersion", g_szMirVer); // TODO: user ip address
+ PushRequest(pReq);
+ return 0;
+}
diff --git a/protocols/Teams/src/teams_endpoint.cpp b/protocols/Teams/src/teams_endpoint.cpp
index 39b6958abb..733008ee75 100644
--- a/protocols/Teams/src/teams_endpoint.cpp
+++ b/protocols/Teams/src/teams_endpoint.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -17,15 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-void CTeamsProto::ProcessTimer()
-{
- if (!IsOnline())
- return;
-
- PushRequest(new GetContactListRequest());
- SendPresence();
-}
-
void CTeamsProto::SendCreateEndpoint()
{
auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_DEFAULT, "/users/ME/endpoints", &CTeamsProto::OnEndpointCreated);
@@ -83,19 +74,17 @@ void CTeamsProto::OnEndpointCreated(MHttpResponse *response, AsyncHttpRequest*)
if (name == "registrationToken")
m_szToken = val.Detach();
else if (name == "endpointId")
- m_szId = val.Detach();
+ m_szEndpoint = val.Detach();
}
}
- if (m_szId && m_hPollingThread == nullptr)
- ForkThread(&CTeamsProto::PollingThread);
-
+ StartTrouter();
PushRequest(new CreateSubscriptionsRequest());
}
void CTeamsProto::OnEndpointDeleted(MHttpResponse *, AsyncHttpRequest *)
{
- m_szId = nullptr;
+ m_szEndpoint = nullptr;
m_szToken = nullptr;
}
@@ -111,23 +100,9 @@ void CTeamsProto::OnSubscriptionsCreated(MHttpResponse *response, AsyncHttpReque
SendPresence();
}
-void CTeamsProto::SendPresence()
-{
- ptrA epname;
-
- if (!m_bUseHostnameAsPlace && m_wstrPlace && *m_wstrPlace)
- epname = mir_utf8encodeW(m_wstrPlace);
- else {
- wchar_t compName[MAX_COMPUTERNAME_LENGTH + 1];
- DWORD size = _countof(compName);
- GetComputerName(compName, &size);
- epname = mir_utf8encodeW(compName);
- }
-
- PushRequest(new SendCapabilitiesRequest(epname, this));
-}
+/////////////////////////////////////////////////////////////////////////////////////////
-void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest*)
+void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest *)
{
if (response == nullptr || response->body.IsEmpty()) {
ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
@@ -147,7 +122,7 @@ void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest
skypenames.destroy();
ReceiveAvatar(0);
- PushRequest(new GetContactListRequest());
+ PushRequest(new AsyncHttpRequest(REQUEST_GET, HOST_CONTACTS, "/users/SELF/contacts", &CTeamsProto::LoadContactList));
PushRequest(new SyncConversations());
JSONNode root = JSONNode::parse(response->body);
@@ -157,6 +132,38 @@ void CTeamsProto::OnCapabilitiesSended(MHttpResponse *response, AsyncHttpRequest
PushRequest(new GetProfileRequest(this, 0));
}
+void CTeamsProto::SendPresence()
+{
+ ptrA epname;
+
+ if (!m_bUseHostnameAsPlace && m_wstrPlace && *m_wstrPlace)
+ epname = mir_utf8encodeW(m_wstrPlace);
+ else {
+ wchar_t compName[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD size = _countof(compName);
+ GetComputerName(compName, &size);
+ epname = mir_utf8encodeW(compName);
+ }
+
+ JSONNode privateInfo; privateInfo.set_name("privateInfo");
+ privateInfo << CHAR_PARAM("epname", epname);
+
+ JSONNode publicInfo; publicInfo.set_name("publicInfo");
+ publicInfo << CHAR_PARAM("capabilities", "Audio|Video") << INT_PARAM("typ", 125)
+ << CHAR_PARAM("skypeNameVersion", "Miranda NG Skype") << CHAR_PARAM("nodeInfo", "xx") << CHAR_PARAM("version", g_szMirVer);
+
+ JSONNode node;
+ node << CHAR_PARAM("id", "messagingService") << CHAR_PARAM("type", "EndpointPresenceDoc")
+ << CHAR_PARAM("selfLink", "uri") << privateInfo << publicInfo;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_DEFAULT, "/users/ME/endpoints/" + mir_urlEncode(m_szEndpoint) + "/presenceDocs/messagingService",
+ &CTeamsProto::OnCapabilitiesSended);
+ pReq->m_szParam = node.write().c_str();
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTeamsProto::OnStatusChanged(MHttpResponse *response, AsyncHttpRequest*)
{
if (response == nullptr || response->body.IsEmpty()) {
diff --git a/protocols/Teams/src/teams_files.cpp b/protocols/Teams/src/teams_files.cpp
index a6e7d07087..4efb89a662 100644
--- a/protocols/Teams/src/teams_files.cpp
+++ b/protocols/Teams/src/teams_files.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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 <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
////////////////////////////////////////////////////////////////////////////////////////
@@ -48,7 +65,7 @@ void CTeamsProto::ReceiveFileThread(void *param)
nlhr.AddHeader("Cookie", szCookie);
NLHR_PTR response(Netlib_HttpTransaction(m_hNetlibUser, &nlhr));
if (response) {
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (!reply.error()) {
auto &root = reply.data();
if (root["content_state"].as_string() == "ready")
diff --git a/protocols/Teams/src/teams_history.cpp b/protocols/Teams/src/teams_history.cpp
index eb6a9ca39f..a2502a0eb3 100644
--- a/protocols/Teams/src/teams_history.cpp
+++ b/protocols/Teams/src/teams_history.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
void CTeamsProto::OnGetServerHistory(MHttpResponse *response, AsyncHttpRequest *pRequest)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
@@ -96,7 +96,7 @@ INT_PTR CTeamsProto::SvcLoadHistory(WPARAM hContact, LPARAM)
void CTeamsProto::OnSyncConversations(MHttpResponse *response, AsyncHttpRequest*)
{
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error())
return;
diff --git a/protocols/Teams/src/teams_http.cpp b/protocols/Teams/src/teams_http.cpp
index 18e3067119..a24b44bfbc 100644
--- a/protocols/Teams/src/teams_http.cpp
+++ b/protocols/Teams/src/teams_http.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -23,7 +23,7 @@ AsyncHttpRequest::AsyncHttpRequest(int type, SkypeHost host, LPCSTR url, MTHttpR
switch (host) {
case HOST_API: m_szUrl = "api.skype.com"; break;
case HOST_PEOPLE: m_szUrl = "people.skype.com/v2"; break;
- case HOST_CONTACTS: m_szUrl = "edge.skype.com/pcs/contacts/v2"; break;
+ case HOST_CONTACTS: m_szUrl = "contacts.skype.com/contacts/v2"; break;
case HOST_GRAPH: m_szUrl = "skypegraph.skype.com"; break;
case HOST_LOGIN: m_szUrl = "login.microsoftonline.com"; break;
case HOST_TEAMS: m_szUrl = "teams.live.com"; break;
diff --git a/protocols/Teams/src/teams_login.cpp b/protocols/Teams/src/teams_login.cpp
index 9e4f2a966a..b3c800d13a 100644
--- a/protocols/Teams/src/teams_login.cpp
+++ b/protocols/Teams/src/teams_login.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -40,8 +40,6 @@ void CTeamsProto::LoggedIn()
m_iStatus = m_iDesiredStatus;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
- m_impl.m_heartBeat.StartSafe(600 * 1000);
-
SendCreateEndpoint();
}
diff --git a/protocols/Teams/src/teams_menus.cpp b/protocols/Teams/src/teams_menus.cpp
index 9238c33946..8605e30a52 100644
--- a/protocols/Teams/src/teams_menus.cpp
+++ b/protocols/Teams/src/teams_menus.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_mood.cpp b/protocols/Teams/src/teams_mood.cpp
index 66a2b15444..fb366ac97a 100644
--- a/protocols/Teams/src/teams_mood.cpp
+++ b/protocols/Teams/src/teams_mood.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_options.cpp b/protocols/Teams/src/teams_options.cpp
index 040583ad85..693af22fb1 100644
--- a/protocols/Teams/src/teams_options.cpp
+++ b/protocols/Teams/src/teams_options.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_polling.cpp b/protocols/Teams/src/teams_polling.cpp
index ca59ac8f6a..11a918034e 100644
--- a/protocols/Teams/src/teams_polling.cpp
+++ b/protocols/Teams/src/teams_polling.cpp
@@ -17,42 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-void CTeamsProto::PollingThread(void *)
-{
- debugLogA(__FUNCTION__ ": entering");
-
- m_iPollingId = -1;
-
- while (true) {
- if (m_isTerminated || m_szId == nullptr)
- break;
-
- std::unique_ptr<PollRequest> request(new PollRequest(this));
- request->nlc = m_hPollingConn;
- NLHR_PTR response(DoSend(request.get()));
- if (m_isTerminated || m_szId == nullptr)
- break;
-
- if (response == nullptr || response->resultCode != 200) {
- m_hPollingConn = nullptr;
- continue;
- }
-
- m_hPollingConn = response->nlc;
- if (!response->body.IsEmpty())
- ParsePollData(response->body);
- }
-
- if (!m_isTerminated) {
- debugLogA(__FUNCTION__ ": unexpected termination; switching protocol to offline");
- SetStatus(ID_STATUS_OFFLINE);
- }
-
- m_hPollingConn = nullptr;
- m_hPollingThread = nullptr;
- debugLogA(__FUNCTION__ ": leaving");
-}
-
void CTeamsProto::ParsePollData(const char *szData)
{
debugLogA(__FUNCTION__);
@@ -171,6 +135,3 @@ void CTeamsProto::ProcessUserPresence(const JSONNode &node)
}
}
}
-
-void CTeamsProto::ProcessConversationUpdate(const JSONNode &) {}
-void CTeamsProto::ProcessThreadUpdate(const JSONNode &) {}
diff --git a/protocols/Teams/src/teams_popups.cpp b/protocols/Teams/src/teams_popups.cpp
index efac8c26f9..59cca9e937 100644
--- a/protocols/Teams/src/teams_popups.cpp
+++ b/protocols/Teams/src/teams_popups.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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 <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
void CTeamsProto::InitPopups()
diff --git a/protocols/Teams/src/teams_profile.cpp b/protocols/Teams/src/teams_profile.cpp
index a26d88b1fe..29a04937c6 100644
--- a/protocols/Teams/src/teams_profile.cpp
+++ b/protocols/Teams/src/teams_profile.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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
@@ -105,7 +105,7 @@ void CTeamsProto::LoadProfile(MHttpResponse *response, AsyncHttpRequest *pReques
{
MCONTACT hContact = (DWORD_PTR)pRequest->pUserInfo;
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error()) {
ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, 0);
return;
diff --git a/protocols/Teams/src/teams_proto.cpp b/protocols/Teams/src/teams_proto.cpp
index 28dcf637ad..9999e41c2e 100644
--- a/protocols/Teams/src/teams_proto.cpp
+++ b/protocols/Teams/src/teams_proto.cpp
@@ -1,3 +1,20 @@
+/*
+Copyright (c) 2025 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 <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
@@ -22,6 +39,13 @@ CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
nlu.szSettingsModule = m_szModuleName;
m_hNetlibUser = Netlib_RegisterUser(&nlu);
+ CMStringA module(FORMAT, "%s.TRouter", m_szModuleName);
+ CMStringW descr(FORMAT, TranslateT("%s websocket connection"), m_tszUserName);
+ nlu.szSettingsModule = module.GetBuffer();
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_UNICODE;
+ nlu.szDescriptiveName.w = descr.GetBuffer();
+ m_hTrouterNetlibUser = Netlib_RegisterUser(&nlu);
+
CreateProtoService(PS_GETAVATARINFO, &CTeamsProto::SvcGetAvatarInfo);
CreateProtoService(PS_GETAVATARCAPS, &CTeamsProto::SvcGetAvatarCaps);
CreateProtoService(PS_GETMYAVATAR, &CTeamsProto::SvcGetMyAvatar);
@@ -83,6 +107,7 @@ void CTeamsProto::OnModulesLoaded()
void CTeamsProto::OnShutdown()
{
StopQueue();
+ StopTrouter();
}
INT_PTR CTeamsProto::GetCaps(int type, MCONTACT)
@@ -142,7 +167,7 @@ int CTeamsProto::Authorize(MEVENT hDbEvent)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthAcceptRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/accept"));
return 0;
}
@@ -152,7 +177,7 @@ int CTeamsProto::AuthDeny(MEVENT hDbEvent, const wchar_t *)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AuthDeclineRequest(getId(hContact)));
+ PushRequest(new AsyncHttpRequest(REQUEST_POST, HOST_CONTACTS, "/users/SELF/invites/" + mir_urlEncode(getId(hContact)) + "/decline"));
return 0;
}
@@ -166,7 +191,15 @@ int CTeamsProto::AuthRequest(MCONTACT hContact, const wchar_t *szMessage)
if (hContact == INVALID_CONTACT_ID)
return 1;
- PushRequest(new AddContactRequest(getId(hContact), T2Utf(szMessage)));
+ auto *pReq = new AsyncHttpRequest(REQUEST_PUT, HOST_CONTACTS, "/users/SELF/contacts");
+
+ JSONNode node;
+ node << CHAR_PARAM("mri", getId(hContact));
+ if (mir_wstrlen(szMessage))
+ node << WCHAR_PARAM("greeting", szMessage);
+ pReq->m_szParam = node.write().c_str();
+
+ PushRequest(pReq);
return 0;
}
@@ -203,6 +236,7 @@ int CTeamsProto::SetStatus(int iNewStatus)
if (iNewStatus == ID_STATUS_OFFLINE) {
m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
StopQueue();
+ StopTrouter();
ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, ID_STATUS_OFFLINE);
diff --git a/protocols/Teams/src/teams_proto.h b/protocols/Teams/src/teams_proto.h
index 850aae8808..c6ad876e86 100644
--- a/protocols/Teams/src/teams_proto.h
+++ b/protocols/Teams/src/teams_proto.h
@@ -1,4 +1,8 @@
#define TEAMS_CLIENT_ID "8ec6bc83-69c8-4392-8f08-b3c986009232"
+#define TEAMS_CLIENTINFO_NAME "skypeteams"
+#define TEAMS_CLIENTINFO_VERSION "49/24062722442"
+
+#define TEAMS_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0 Teams/24165.1410.2974.6689/49"
#define DBKEY_ID "id"
#define DBKEY_GROUP "DefaultGroup"
@@ -39,7 +43,7 @@ class CTeamsProto : public PROTO<CTeamsProto>
CTimer m_heartBeat, m_loginPoll;
void OnHeartBeat(CTimer *)
{
- m_proto.ProcessTimer();
+ m_proto.TRouterSendJson("ping");
}
void OnLoginPoll(CTimer *)
{
@@ -141,12 +145,6 @@ public:
void __cdecl SearchBasicThread(void *param);
//////////////////////////////////////////////////////////////////////////////////////
- // services
-
- static INT_PTR __cdecl SvcEventGetIcon(WPARAM, LPARAM);
- static INT_PTR __cdecl SvcGetEventText(WPARAM, LPARAM);
-
- //////////////////////////////////////////////////////////////////////////////////////
// settings
CMOption<bool> m_bAutoHistorySync;
@@ -164,7 +162,7 @@ public:
// other data
int m_iPollingId, m_iMessageId = 1;
- ptrA m_szToken, m_szId, m_szOwnSkypeId;
+ ptrA m_szToken, m_szEndpoint, m_szOwnSkypeId;
CMStringA m_szSkypename, m_szMyname, m_szSkypeToken;
MCONTACT m_hMyContact;
@@ -182,12 +180,6 @@ public:
void OnEndpointCreated(MHttpResponse *response, AsyncHttpRequest *pRequest);
void OnEndpointDeleted(MHttpResponse *response, AsyncHttpRequest *pRequest);
- // oauth
- void OnOAuthStart(MHttpResponse *response, AsyncHttpRequest *pRequest);
- void OnOAuthConfirm(MHttpResponse* response, AsyncHttpRequest* pRequest);
- void OnOAuthAuthorize(MHttpResponse* response, AsyncHttpRequest* pRequest);
- void OnOAuthEnd(MHttpResponse *response, AsyncHttpRequest *pRequest);
-
void OnASMObjectCreated(MHttpResponse *response, AsyncHttpRequest *pRequest);
void OnASMObjectUploaded(MHttpResponse *response, AsyncHttpRequest *pRequest);
@@ -211,6 +203,7 @@ public:
void LoadProfile(MHttpResponse *response, AsyncHttpRequest *pRequest);
static INT_PTR __cdecl GlobalParseSkypeUriService(WPARAM, LPARAM lParam);
+
private:
bool m_bHistorySynced;
@@ -224,9 +217,6 @@ private:
mir_cs messageSyncLock;
mir_cs m_StatusLock;
- HANDLE m_hPollingThread;
- HNETLIBCONN m_hPollingConn;
-
// avatars
void SetAvatarUrl(MCONTACT hContact, const CMStringW &tszUrl);
bool ReceiveAvatar(MCONTACT hContact);
@@ -303,17 +293,7 @@ private:
void SetChatStatus(MCONTACT hContact, int iStatus);
- // polling
- void __cdecl PollingThread(void*);
-
bool ParseMessage(const JSONNode &node, DB::EventInfo &dbei);
- void ParsePollData(const char*);
-
- void ProcessNewMessage(const JSONNode &node);
- void ProcessUserPresence(const JSONNode &node);
- void ProcessThreadUpdate(const JSONNode &node);
- void ProcessEndpointPresence(const JSONNode &node);
- void ProcessConversationUpdate(const JSONNode &node);
// utils
template <typename T>
@@ -346,8 +326,6 @@ private:
static LRESULT CALLBACK PopupDlgProcCall(HWND hPopup, UINT uMsg, WPARAM wParam, LPARAM lParam);
- void ProcessTimer();
-
void SetString(MCONTACT hContact, const char *pszSetting, const JSONNode &node);
CMStringW ChangeTopicForm();
@@ -369,6 +347,40 @@ private:
auto *proto = CMPlugin::getInstance((MCONTACT)wParam);
return proto ? (proto->*Service)(wParam, lParam) : 0;
}
+
+ // trouter
+public:
+ void TRouterProcess(const char *str);
+
+private:
+ HNETLIBUSER m_hTrouterNetlibUser;
+ CMStringA m_szTrouterUrl, m_szTrouterSurl;
+ WebSocket<CTeamsProto> *m_ws;
+ MHttpHeaders m_connectParams;
+ int iCommandId;
+
+ void ProcessNewMessage(const JSONNode &node);
+ void ProcessUserPresence(const JSONNode &node);
+ void ProcessThreadUpdate(const JSONNode &node);
+ void ProcessServerMessage(const std::string &szName, const JSONNode &args);
+ void ProcessEndpointPresence(const JSONNode &node);
+ void ProcessConversationUpdate(const JSONNode &node);
+
+ void __cdecl GatewayThread(void *);
+
+ void TRouterSendJson(const char *szName, const JSONNode *node = 0);
+ void TRouterSendJson(const JSONNode &node);
+
+ void TRouterSendAuthentication();
+ void TRouterSendActive(bool);
+ void TRouterRegister();
+ void TRouterRegister(const char *pszAppId, const char *pszKey, const char *pszPath);
+
+ void StartTrouter();
+ void StopTrouter();
+
+ void OnTrouterInfo(MHttpResponse *response, AsyncHttpRequest *pRequest);
+ void OnTrouterSession(MHttpResponse *response, AsyncHttpRequest *pRequest);
};
typedef CProtoDlgBase<CTeamsProto> CTeamsDlgBase;
diff --git a/protocols/Teams/src/teams_search.cpp b/protocols/Teams/src/teams_search.cpp
index a88daaae47..5cceab3c3f 100644
--- a/protocols/Teams/src/teams_search.cpp
+++ b/protocols/Teams/src/teams_search.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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,7 +34,7 @@ void CTeamsProto::OnSearch(MHttpResponse *response, AsyncHttpRequest*)
{
debugLogA(__FUNCTION__);
- SkypeReply reply(response);
+ TeamsReply reply(response);
if (reply.error()) {
ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0);
return;
diff --git a/protocols/Teams/src/teams_trouter.cpp b/protocols/Teams/src/teams_trouter.cpp
new file mode 100644
index 0000000000..e602724c68
--- /dev/null
+++ b/protocols/Teams/src/teams_trouter.cpp
@@ -0,0 +1,367 @@
+/*
+Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define TEAMS_TROUTER_TTL 86400
+#define TEAMS_TROUTER_TCCV "2024.23.01.2"
+
+void CTeamsProto::OnTrouterSession(MHttpResponse *response, AsyncHttpRequest *pRequest)
+{
+ if (response->resultCode != 200) {
+ LoginError();
+ return;
+ }
+
+ int iStart = 0;
+ CMStringA szId = response->body.Tokenize(":", iStart);
+ m_szTrouterUrl = pRequest->m_szUrl;
+ m_szTrouterUrl.Replace("socket.io/1/", "socket.io/1/websocket/" + szId + "/");
+ ForkThread(&CTeamsProto::GatewayThread);
+}
+
+void CTeamsProto::OnTrouterInfo(MHttpResponse *response, AsyncHttpRequest *)
+{
+ TeamsReply reply(response);
+ if (reply.error()) {
+ LoginError();
+ return;
+ }
+
+ auto &root = reply.data();
+ m_szTrouterSurl = root["surl"].as_mstring();
+ CMStringA ccid = root["ccid"].as_mstring();
+ CMStringA szUrl = root["socketio"].as_mstring();
+ szUrl += "socket.io/1/";
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_GET, HOST_OTHER, szUrl, &CTeamsProto::OnTrouterSession);
+ pReq << CHAR_PARAM("v", "v4");
+
+ m_connectParams.destroy();
+ for (auto &it : root["connectparams"]) {
+ m_connectParams.AddHeader(it.name(), it.as_string().c_str());
+ pReq << CHAR_PARAM(it.name(), it.as_string().c_str());
+ }
+
+ pReq << CHAR_PARAM("tc", "{\"cv\":\"" TEAMS_TROUTER_TCCV "\",\"ua\":\"TeamsCDL\",\"hr\":\"\",\"v\":\"" TEAMS_CLIENTINFO_VERSION "\"}")
+ << CHAR_PARAM("con_num", "1234567890123_1") << CHAR_PARAM("epid", m_szEndpoint) << BOOL_PARAM("auth", true) << INT_PARAM("timeout", 40);
+ if (!ccid.IsEmpty())
+ pReq << CHAR_PARAM("ccid", ccid);
+ PushRequest(pReq);
+}
+
+void CTeamsProto::StartTrouter()
+{
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://go.trouter.teams.microsoft.com/v4/a", &CTeamsProto::OnTrouterInfo);
+ pReq->m_szUrl.AppendFormat("?epid=%s", m_szEndpoint.get());
+ pReq->AddHeader("x-skypetoken", m_szSkypeToken);
+ pReq->flags |= NLHRF_NODUMPHEADERS;
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTeamsProto::StopTrouter()
+{
+ m_impl.m_heartBeat.StopSafe();
+
+ if (m_ws) {
+ m_ws->terminate();
+ m_ws = nullptr;
+ }
+}
+
+void CTeamsProto::GatewayThread(void *)
+{
+ m_ws = nullptr;
+
+ MHttpHeaders headers;
+ headers.AddHeader("x-skypetoken", m_szSkypeToken);
+ headers.AddHeader("User-Agent", TEAMS_USER_AGENT);
+
+ WebSocket<CTeamsProto> ws(this);
+ NLHR_PTR pReply(ws.connect(m_hTrouterNetlibUser, m_szTrouterUrl, &headers));
+ if (pReply) {
+ if (pReply->resultCode == 101) {
+ m_ws = &ws;
+
+ iCommandId = 1;
+ m_impl.m_heartBeat.StartSafe(30000);
+
+ debugLogA("Websocket connection succeeded");
+ ws.run();
+ }
+ else debugLogA("websocket connection failed: %d", pReply->resultCode);
+ }
+ else debugLogA("websocket connection failed");
+
+ StopTrouter();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// TRouter send
+
+void CTeamsProto::TRouterSendJson(const JSONNode &node)
+{
+ std::string szJson = "5:::" + node.write();
+ if (m_ws) {
+ m_ws->sendText(szJson.c_str());
+ }
+}
+
+void CTeamsProto::TRouterSendJson(const char *szName, const JSONNode *node)
+{
+ JSONNode payload, args(JSON_ARRAY);
+ payload << CHAR_PARAM("name", szName);
+ if (node) {
+ args.set_name("args");
+ args << *node;
+ payload << args;
+ }
+
+ std::string szJson = payload.write();
+ if (m_ws)
+ m_ws->sendText(szJson.c_str());
+}
+
+void CTeamsProto::TRouterSendAuthentication()
+{
+ JSONNode headers, params, payload;
+
+ headers.set_name("headers");
+ headers << CHAR_PARAM("X-Ms-Test-User", "False") << CHAR_PARAM("Authorization", "Bearer " + m_szAccessToken)
+ << CHAR_PARAM("X-MS-Migration", "True");
+
+ params.set_name("connectparams");
+ for (auto &it : m_connectParams)
+ params << CHAR_PARAM(it->szName, it->szValue);
+
+ payload << headers << params;
+ TRouterSendJson(payload);
+}
+
+static char szSuffix[4] = { 'A', 'g', 'Q', 'w' };
+
+void CTeamsProto::TRouterSendActive(bool bActive)
+{
+ CMStringA cv;
+ srand(time(0));
+ for (int i = 0; i < 21; i++)
+ cv.AppendChar('a' + rand() % 26);
+ cv.AppendChar(szSuffix[rand() % 4]);
+ cv += ".0.1";
+
+ JSONNode payload;
+ payload << CHAR_PARAM("state", bActive ? "active" : "inactive") << CHAR_PARAM("cv", cv);
+ TRouterSendJson("user.activity", &payload);
+}
+
+void CTeamsProto::TRouterRegister()
+{
+ TRouterRegister("NextGenCalling", "DesktopNgc_2.3:SkypeNgc", m_szTrouterSurl + "NGCallManagerWin");
+ TRouterRegister("SkypeSpacesWeb", "SkypeSpacesWeb_2.3", m_szTrouterSurl + "SkypeSpacesWeb");
+ TRouterRegister("TeamsCDLWebWorker", "TeamsCDLWebWorker_1.9", m_szTrouterSurl);
+}
+
+void CTeamsProto::TRouterRegister(const char *pszAppId, const char *pszKey, const char *pszPath)
+{
+ JSONNode descr, reg, obj, trouter(JSON_ARRAY), transports;
+ descr.set_name("clientDescription");
+ descr << CHAR_PARAM("appId", pszAppId) << CHAR_PARAM("aesKey", "") << CHAR_PARAM("languageId", "en-US")
+ << CHAR_PARAM("platform", "edge") << CHAR_PARAM("templateKey", pszKey) << CHAR_PARAM("platformUIVersion", TEAMS_CLIENTINFO_VERSION)
+ << CHAR_PARAM("productContext", "TFL");
+
+ obj << CHAR_PARAM("context", "") << CHAR_PARAM("path", pszPath) << INT_PARAM("ttl", TEAMS_TROUTER_TTL);
+ trouter.set_name("TROUTER"); trouter << obj;
+ transports.set_name("transports"); transports << trouter;
+
+ reg.set_name("registration");
+ reg << descr << CHAR_PARAM("registrationId", m_szEndpoint) << CHAR_PARAM("nodeId", "") << transports;
+
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_OTHER, "https://edge.skype.com/registrar/prod/v2/registrations");
+ pReq->flags |= NLHRF_NODUMPHEADERS;
+ pReq->AddHeader("Content-Type", "application/json");
+ pReq->AddHeader("X-Skypetoken", m_szSkypeToken);
+ pReq->AddHeader("Authorization", "Bearer " + m_szAccessToken);
+ pReq->m_szParam = reg.write().c_str();
+ PushRequest(pReq);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// TRouter receive
+
+void WebSocket<CTeamsProto>::process(const uint8_t *buf, size_t cbLen)
+{
+ Netlib_Dump(getConn(), buf, cbLen, false, 0);
+
+ CMStringA payload((const char *)buf, (int)cbLen);
+ p->TRouterProcess(payload);
+}
+
+static const char* skip3colons(const char *str)
+{
+ int nColons = 3;
+ for (const char *p = str; *p; p++) {
+ if (*p == ':') {
+ if (--nColons == 0)
+ return p + 1;
+ }
+ }
+ return str;
+}
+
+void CTeamsProto::TRouterProcess(const char *str)
+{
+ switch (*str) {
+ case '1':
+ TRouterSendAuthentication();
+ TRouterSendActive(true);
+ TRouterRegister();
+ break;
+
+ case '3':
+ if (auto packet = JSONNode::parse(skip3colons(str))) {
+ std::string szBody(packet["body"].as_string());
+ auto message = JSONNode::parse(szBody.c_str());
+ if (message) {
+ Netlib_Logf(m_hTrouterNetlibUser, "Got event:\n%s", message.write_formatted().c_str());
+
+ const JSONNode &resource = message["resource"];
+
+ std::string resourceType = message["resourceType"].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);
+ }
+ }
+ break;
+
+ case '5':
+ if (auto root = JSONNode::parse(skip3colons(str))) {
+ std::string szName(root["name"].as_string());
+ ProcessServerMessage(szName, root["args"]);
+ }
+ break;
+ }
+}
+
+void CTeamsProto::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 CTeamsProto::ProcessUserPresence(const JSONNode &node)
+{
+ debugLogA(__FUNCTION__);
+
+ std::string selfLink = node["selfLink"].as_string();
+ std::string status = node["availability"].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 CTeamsProto::ProcessServerMessage(const std::string &szName, const JSONNode&)
+{
+ if (szName == "trouter.message_loss") {
+ TRouterRegister("TeamsCDLWebWorker", "TeamsCDLWebWorker_1.9", m_szTrouterSurl);
+ }
+}
+
+void CTeamsProto::ProcessConversationUpdate(const JSONNode &) {}
+void CTeamsProto::ProcessThreadUpdate(const JSONNode &) {}
diff --git a/protocols/Teams/src/teams_utils.cpp b/protocols/Teams/src/teams_utils.cpp
index 59beeaa894..3d23351468 100644
--- a/protocols/Teams/src/teams_utils.cpp
+++ b/protocols/Teams/src/teams_utils.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2015-25 Miranda NG team (https://miranda-ng.org)
+Copyright (c) 2025 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/Teams/src/teams_utils.h b/protocols/Teams/src/teams_utils.h
index f7d205da61..58255e53c1 100644
--- a/protocols/Teams/src/teams_utils.h
+++ b/protocols/Teams/src/teams_utils.h
@@ -60,9 +60,9 @@ struct CFileUploadParam : public MZeroedObject
}
};
-struct SkypeReply : public JsonReply
+struct TeamsReply : public JsonReply
{
- SkypeReply(MHttpResponse *response) :
+ TeamsReply(MHttpResponse *response) :
JsonReply(response)
{
if (m_root)
diff --git a/protocols/Teams/src/version.h b/protocols/Teams/src/version.h
index 711a86277e..3b23908bca 100644
--- a/protocols/Teams/src/version.h
+++ b/protocols/Teams/src/version.h
@@ -7,7 +7,7 @@
#define __PLUGIN_NAME "Teams protocol"
#define __FILENAME "Teams.dll"
-#define __DESCRIPTION "Teams protocol support for Miranda NG."
+#define __DESCRIPTION "Microsoft Teams protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Teams"
#define __COPYRIGHT "© 2025 Miranda NG team"