summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/Teams/Teams.vcxproj2
-rw-r--r--protocols/Teams/Teams.vcxproj.filters6
-rw-r--r--protocols/Teams/src/proto.h43
-rw-r--r--protocols/Teams/src/teams_http.cpp138
-rw-r--r--protocols/Teams/src/teams_login.cpp58
-rw-r--r--protocols/Teams/src/teams_proto.cpp52
6 files changed, 296 insertions, 3 deletions
diff --git a/protocols/Teams/Teams.vcxproj b/protocols/Teams/Teams.vcxproj
index f5faf4cd02..00464f85ee 100644
--- a/protocols/Teams/Teams.vcxproj
+++ b/protocols/Teams/Teams.vcxproj
@@ -30,6 +30,8 @@
<ClCompile Include="src\stdafx.cxx">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\teams_http.cpp" />
+ <ClCompile Include="src\teams_login.cpp" />
<ClCompile Include="src\teams_options.cpp" />
<ClCompile Include="src\teams_proto.cpp" />
<ClInclude Include="src\proto.h" />
diff --git a/protocols/Teams/Teams.vcxproj.filters b/protocols/Teams/Teams.vcxproj.filters
index 9f0401d3cc..b30f82a50a 100644
--- a/protocols/Teams/Teams.vcxproj.filters
+++ b/protocols/Teams/Teams.vcxproj.filters
@@ -13,6 +13,12 @@
<ClCompile Include="src\teams_options.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\teams_login.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\teams_http.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\resource.h">
diff --git a/protocols/Teams/src/proto.h b/protocols/Teams/src/proto.h
index d1c3fd7f4f..de6a543bd9 100644
--- a/protocols/Teams/src/proto.h
+++ b/protocols/Teams/src/proto.h
@@ -1,16 +1,50 @@
-struct HttpRequest : public MTHttpRequest<CTeamsProto>
+enum HostType {
+ HOST_OTHER = 0,
+ HOST_LOGIN = 1,
+};
+
+struct AsyncHttpRequest : public MTHttpRequest<CTeamsProto>
{
- HttpRequest(int iRequestType, const char *pszUrl);
+ AsyncHttpRequest(int iRequestType, HostType host, const char *pszUrl, MTHttpRequestHandler pFunc = 0);
+
+ HostType m_host;
};
class CTeamsProto : public PROTO<CTeamsProto>
{
friend class COptionsMainDlg;
+ CMStringA GetTenant();
+
+ // http queue
+ bool m_isTerminated = true;
+ mir_cs m_requestQueueLock;
+ LIST<AsyncHttpRequest> m_requests;
+ MEventHandle m_hRequestQueueEvent;
+ HANDLE m_hRequestQueueThread;
+
+ void __cdecl WorkerThread(void *);
+
+ void StartQueue();
+ void StopQueue();
+
+ MHttpResponse* DoSend(AsyncHttpRequest *request);
+
+ void Execute(AsyncHttpRequest *request);
+ void PushRequest(AsyncHttpRequest *request);
+
+ // login
+ void Login();
+ void RefreshToken(const char *pszScope, AsyncHttpRequest::MTHttpRequestHandler pFunc);
+
+ void OnRefreshAccessToken(MHttpResponse *response, AsyncHttpRequest *pRequest);
+ void OnRefreshSubstrate(MHttpResponse *response, AsyncHttpRequest *pRequest);
+
// options
int __cdecl OnOptionsInit(WPARAM, LPARAM);
- CMOption<wchar_t*> m_wstrCListGroup;
+ // settings
+ CMOption<wchar_t *> m_wstrCListGroup;
public:
// constructor
@@ -18,6 +52,9 @@ public:
~CTeamsProto();
MWindow OnCreateAccMgrUI(MWindow) override;
+
+ INT_PTR GetCaps(int type, MCONTACT) override;
+ int SetStatus(int iNewStatus) override;
};
typedef CProtoDlgBase<CTeamsProto> CTeamsDlgBase;
diff --git a/protocols/Teams/src/teams_http.cpp b/protocols/Teams/src/teams_http.cpp
new file mode 100644
index 0000000000..67bbd78bd4
--- /dev/null
+++ b/protocols/Teams/src/teams_http.cpp
@@ -0,0 +1,138 @@
+/*
+Copyright (C) 2012-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"
+
+AsyncHttpRequest::AsyncHttpRequest(int type, HostType host, LPCSTR url, MTHttpRequestHandler pFunc) :
+ m_host(host)
+{
+ switch (host) {
+ case HOST_LOGIN: m_szUrl = "login.microsoftonline.com"; break;
+ }
+
+ AddHeader("User-Agent", NETLIB_USER_AGENT);
+
+ if (url)
+ m_szUrl.Append(url);
+ m_pFunc = pFunc;
+ flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_DUMPASTEXT;
+ requestType = type;
+}
+/*
+void AsyncHttpRequest::AddAuthentication(CTeamsProto *ppro)
+{
+ AddHeader("Authentication", CMStringA("skypetoken=") + ppro->m_szApiToken);
+}
+
+void AsyncHttpRequest::AddRegister(CTeamsProto *ppro)
+{
+ AddHeader("RegistrationToken", CMStringA("registrationToken=") + ppro->m_szToken);
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTeamsProto::StartQueue()
+{
+ if (!m_isTerminated)
+ return;
+
+ m_isTerminated = false;
+ if (m_hRequestQueueThread == nullptr)
+ ForkThread(&CTeamsProto::WorkerThread);
+}
+
+void CTeamsProto::StopQueue()
+{
+ m_isTerminated = true;
+
+ if (m_hRequestQueueThread)
+ m_hRequestQueueEvent.Set();
+}
+
+void CTeamsProto::PushRequest(AsyncHttpRequest *request)
+{
+ if (m_isTerminated)
+ return;
+ {
+ mir_cslock lock(m_requestQueueLock);
+ m_requests.insert(request);
+ }
+ m_hRequestQueueEvent.Set();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MHttpResponse* CTeamsProto::DoSend(AsyncHttpRequest *pReq)
+{
+ if (pReq->m_host != HOST_OTHER)
+ pReq->m_szUrl.Insert(0, ((pReq->flags & NLHRF_SSL) ? "https://" : "http://"));
+
+ if (!pReq->m_szParam.IsEmpty()) {
+ switch (pReq->requestType) {
+ case REQUEST_PUT:
+ case REQUEST_POST:
+ if (!pReq->FindHeader("Content-Type")) {
+ if (pReq->m_szParam[0] == '[' || pReq->m_szParam[0] == '{')
+ pReq->AddHeader("Content-Type", "application/json; charset=UTF-8");
+ else
+ pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded");
+ }
+ }
+ }
+
+ debugLogA("Send request to %s", pReq->m_szUrl.c_str());
+
+ return Netlib_HttpTransaction(m_hNetlibUser, pReq);
+}
+
+void CTeamsProto::Execute(AsyncHttpRequest *item)
+{
+ NLHR_PTR response(DoSend(item));
+ if (item->m_pFunc != nullptr)
+ (this->*item->m_pFunc)(response, item);
+ m_requests.remove(item);
+ delete item;
+}
+
+void CTeamsProto::WorkerThread(void*)
+{
+ m_hRequestQueueThread = GetCurrentThread();
+
+ while (true) {
+ m_hRequestQueueEvent.Wait();
+ if (m_isTerminated)
+ break;
+
+ while (true) {
+ AsyncHttpRequest *item = nullptr;
+ {
+ mir_cslock lock(m_requestQueueLock);
+
+ if (m_requests.getCount() == 0)
+ break;
+
+ item = m_requests[0];
+ m_requests.remove(0);
+ }
+ if (item != nullptr)
+ Execute(item);
+ }
+ }
+
+ m_hRequestQueueThread = nullptr;
+}
diff --git a/protocols/Teams/src/teams_login.cpp b/protocols/Teams/src/teams_login.cpp
new file mode 100644
index 0000000000..9be1df5de8
--- /dev/null
+++ b/protocols/Teams/src/teams_login.cpp
@@ -0,0 +1,58 @@
+/*
+Copyright (C) 2012-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"
+
+CMStringA CTeamsProto::GetTenant()
+{
+ CMStringA ret(getMStringA("Tenant"));
+ return (ret.IsEmpty()) ? "consumers" : ret;
+}
+
+void CTeamsProto::Login()
+{
+ CMStringA szLogin(getMStringA("Login")), szPassword(getMStringA("Password"));
+ if (szLogin.IsEmpty() || szPassword.IsEmpty()) {
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, 1001);
+ return;
+ }
+
+ // login
+ int oldStatus = m_iStatus;
+ m_iStatus = ID_STATUS_CONNECTING;
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
+
+ StartQueue();
+
+ RefreshToken("service::api.fl.teams.microsoft.com::MBI_SSL openid profile offline_access", &CTeamsProto::OnRefreshAccessToken);
+ // RefreshToken("service::api.fl.spaces.skype.com::MBI_SSL openid profile offline_access", &CTeamsProto::OnRefreshAccessToken);
+ // RefreshToken("https://substrate.office.com/M365.Access openid profile offline_access", &CTeamsProto::OnRefreshSubstrate);
+}
+
+void CTeamsProto::RefreshToken(const char *pszScope, AsyncHttpRequest::MTHttpRequestHandler pFunc)
+{
+ auto *pReq = new AsyncHttpRequest(REQUEST_POST, HOST_LOGIN, "/" + GetTenant() + "/oauth2/v2.0/token", pFunc);
+ pReq << CHAR_PARAM("scope", pszScope) << CHAR_PARAM("client_id", "8ec6bc83-69c8-4392-8f08-b3c986009232")
+ << CHAR_PARAM("grant_type", "refresh_token") << CHAR_PARAM("refresh_token", getMStringA("Password"));
+ PushRequest(pReq);
+}
+
+void CTeamsProto::OnRefreshAccessToken(MHttpResponse *response, AsyncHttpRequest *pRequest)
+{}
+
+void CTeamsProto::OnRefreshSubstrate(MHttpResponse *response, AsyncHttpRequest *pRequest)
+{}
diff --git a/protocols/Teams/src/teams_proto.cpp b/protocols/Teams/src/teams_proto.cpp
index b96ebd2593..0cf9fb0e1c 100644
--- a/protocols/Teams/src/teams_proto.cpp
+++ b/protocols/Teams/src/teams_proto.cpp
@@ -2,6 +2,7 @@
CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
PROTO<CTeamsProto>(protoName, userName),
+ m_requests(10),
m_wstrCListGroup(this, "DefaultGroup", L"Teams")
{
HookProtoEvent(ME_OPT_INITIALISE, &CTeamsProto::OnOptionsInit);
@@ -17,3 +18,54 @@ CTeamsProto::CTeamsProto(const char *protoName, const wchar_t *userName) :
CTeamsProto::~CTeamsProto()
{
}
+
+INT_PTR CTeamsProto::GetCaps(int type, MCONTACT)
+{
+ switch (type) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_AUTHREQ | PF1_CHAT | PF1_BASICSEARCH | PF1_MODEMSG | PF1_FILE | PF1_SERVERCLIST;
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND;
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_HEAVYDND;
+ case PFLAGNUM_4:
+ return PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SERVERMSGID | PF4_SERVERFORMATTING;
+ case PFLAG_UNIQUEIDTEXT:
+ return (INT_PTR)TranslateT("Teams ID");
+ }
+ return 0;
+}
+
+int CTeamsProto::SetStatus(int iNewStatus)
+{
+ if (iNewStatus == m_iDesiredStatus)
+ return 0;
+
+ switch (iNewStatus) {
+ case ID_STATUS_FREECHAT: iNewStatus = ID_STATUS_ONLINE; break;
+ case ID_STATUS_NA: iNewStatus = ID_STATUS_AWAY; break;
+ case ID_STATUS_OCCUPIED: iNewStatus = ID_STATUS_DND; break;
+ }
+
+ debugLogA(__FUNCTION__ ": changing status from %i to %i", m_iStatus, iNewStatus);
+
+ int old_status = m_iStatus;
+ m_iDesiredStatus = iNewStatus;
+
+ if (iNewStatus == ID_STATUS_OFFLINE) {
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
+ StopQueue();
+
+ ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, ID_STATUS_OFFLINE);
+
+ if (!Miranda_IsTerminated())
+ setAllContactStatuses(ID_STATUS_OFFLINE, false);
+ return 0;
+ }
+
+ if (m_iStatus == ID_STATUS_OFFLINE)
+ Login();
+ // else
+ // PushRequest(new SetStatusRequest(MirandaToSkypeStatus(m_iDesiredStatus)));
+ return 0;
+}