summaryrefslogtreecommitdiff
path: root/protocols/Teams/src/teams_endpoint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Teams/src/teams_endpoint.cpp')
-rw-r--r--protocols/Teams/src/teams_endpoint.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/protocols/Teams/src/teams_endpoint.cpp b/protocols/Teams/src/teams_endpoint.cpp
new file mode 100644
index 0000000000..d0431cd3b2
--- /dev/null
+++ b/protocols/Teams/src/teams_endpoint.cpp
@@ -0,0 +1,227 @@
+/*
+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/>.
+*/
+
+#include "stdafx.h"
+
+void CTeamsProto::CheckConvert()
+{
+ m_szSkypename = getMStringA(SKYPE_SETTINGS_ID);
+ if (m_szSkypename.IsEmpty()) {
+ m_szSkypename = getMStringA(SKYPE_SETTINGS_LOGIN);
+ if (!m_szSkypename.IsEmpty()) { // old settings format, need to update all settings
+ m_szSkypename.Insert(0, "8:");
+ setString(SKYPE_SETTINGS_ID, m_szSkypename);
+
+ for (auto &hContact : AccContacts()) {
+ CMStringA id(ptrA(getUStringA(hContact, "Skypename")));
+ if (!id.IsEmpty())
+ setString(hContact, SKYPE_SETTINGS_ID, (isChatRoom(hContact)) ? "19:" + id : "8:" + id);
+
+ ptrW wszNick(getWStringA(hContact, "Nick"));
+ if (wszNick == nullptr)
+ setUString(hContact, "Nick", id);
+
+ delSetting(hContact, "Skypename");
+ }
+ }
+ }
+}
+
+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);
+ pReq->m_szParam = "{\"endpointFeatures\":\"Agent,Presence2015,MessageProperties,CustomUserProperties,Casts,ModernBots,AutoIdleForWebApi,secureThreads,notificationStream,InviteFree,SupportsReadReceipts,ued\"}";
+ pReq->AddHeader("Origin", "https://web.skype.com");
+ pReq->AddHeader("Referer", "https://web.skype.com/");
+ pReq->AddAuthentication(this);
+
+ PushRequest(pReq);
+}
+
+void CTeamsProto::OnEndpointCreated(MHttpResponse *response, AsyncHttpRequest*)
+{
+ if (IsStatusConnecting(m_iStatus))
+ m_iStatus++;
+
+ if (response == nullptr) {
+ debugLogA(__FUNCTION__ ": failed to get create endpoint");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ switch (response->resultCode) {
+ case 200:
+ case 201: // okay, endpoint created
+ break;
+
+ case 301:
+ case 302: // redirect to the closest data center
+ if (auto *hdr = response->FindHeader("Location")) {
+ CMStringA szUrl(hdr+8);
+ int iEnd = szUrl.Find('/');
+ // g_plugin.szDefaultServer = (iEnd != -1) ? szUrl.Left(iEnd) : szUrl;
+ }
+ SendCreateEndpoint();
+ return;
+
+ case 401: // unauthorized
+ default:
+ delSetting("TokenExpiresIn");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ // Succeeded, decode the answer
+ int oldStatus = m_iStatus;
+ m_iStatus = m_iDesiredStatus;
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
+
+ if (auto *hdr = response->FindHeader("Set-RegistrationToken")) {
+ CMStringA szValue = hdr;
+ int iStart = 0;
+ while (true) {
+ CMStringA szToken = szValue.Tokenize(";", iStart).Trim();
+ if (iStart == -1)
+ break;
+
+ int iStart2 = 0;
+ CMStringA name = szToken.Tokenize("=", iStart2);
+ CMStringA val = szToken.Mid(iStart2);
+
+ if (name == "registrationToken")
+ m_szToken = val.Detach();
+ else if (name == "endpointId")
+ m_szId = val.Detach();
+ }
+ }
+
+ if (m_szId && m_hPollingThread == nullptr)
+ ForkThread(&CTeamsProto::PollingThread);
+
+ PushRequest(new CreateSubscriptionsRequest());
+}
+
+void CTeamsProto::OnEndpointDeleted(MHttpResponse *, AsyncHttpRequest *)
+{
+ m_szId = nullptr;
+ m_szToken = nullptr;
+}
+
+void CTeamsProto::OnSubscriptionsCreated(MHttpResponse *response, AsyncHttpRequest*)
+{
+ if (response == nullptr) {
+ debugLogA(__FUNCTION__ ": failed to create subscription");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ 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*)
+{
+ if (response == nullptr || response->body.IsEmpty()) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ PushRequest(new SetStatusRequest(MirandaToSkypeStatus(m_iDesiredStatus)));
+
+ LIST<char> skypenames(1);
+ for (auto &hContact : AccContacts())
+ if (!isChatRoom(hContact))
+ skypenames.insert(getId(hContact).Detach());
+
+ PushRequest(new CreateContactsSubscriptionRequest(skypenames));
+ FreeList(skypenames);
+ skypenames.destroy();
+
+ ReceiveAvatar(0);
+ PushRequest(new GetContactListRequest());
+ PushRequest(new SyncConversations());
+
+ JSONNode root = JSONNode::parse(response->body);
+ if (root)
+ m_szOwnSkypeId = UrlToSkypeId(root["selfLink"].as_string().c_str()).Detach();
+
+ PushRequest(new GetProfileRequest(this, 0));
+}
+
+void CTeamsProto::OnStatusChanged(MHttpResponse *response, AsyncHttpRequest*)
+{
+ if (response == nullptr || response->body.IsEmpty()) {
+ debugLogA(__FUNCTION__ ": failed to change status");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ JSONNode json = JSONNode::parse(response->body);
+ if (!json) {
+ debugLogA(__FUNCTION__ ": failed to change status");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ const JSONNode &nStatus = json["status"];
+ if (!nStatus) {
+ debugLogA(__FUNCTION__ ": result contains no valid status to switch to");
+ return;
+ }
+
+ int iNewStatus = SkypeToMirandaStatus(nStatus.as_string().c_str());
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ debugLogA(__FUNCTION__ ": failed to change status");
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ SetStatus(ID_STATUS_OFFLINE);
+ return;
+ }
+
+ int oldStatus = m_iStatus;
+ m_iStatus = m_iDesiredStatus = iNewStatus;
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
+}