summaryrefslogtreecommitdiff
path: root/protocols/Discord/src
diff options
context:
space:
mode:
authorGeorge Hazan <george.hazan@gmail.com>2024-01-14 18:43:24 +0300
committerGeorge Hazan <george.hazan@gmail.com>2024-01-14 18:43:24 +0300
commita8c9c34bead54d469f126db194b571e2fe90b078 (patch)
treee4348568eb8a966f4d87abf50beb98b2808a78b2 /protocols/Discord/src
parenta76d633fcee305009b2c8798d577e16f8dd0d931 (diff)
Discord: add MFA support
Diffstat (limited to 'protocols/Discord/src')
-rw-r--r--protocols/Discord/src/http.cpp2
-rw-r--r--protocols/Discord/src/mfa.cpp112
-rw-r--r--protocols/Discord/src/options.cpp4
-rw-r--r--protocols/Discord/src/proto.h10
-rw-r--r--protocols/Discord/src/resource.h10
-rw-r--r--protocols/Discord/src/server.cpp17
6 files changed, 143 insertions, 12 deletions
diff --git a/protocols/Discord/src/http.cpp b/protocols/Discord/src/http.cpp
index 82e8d495d0..70cfe9db80 100644
--- a/protocols/Discord/src/http.cpp
+++ b/protocols/Discord/src/http.cpp
@@ -41,7 +41,7 @@ static LONG g_reqNum = 0;
AsyncHttpRequest::AsyncHttpRequest(CDiscordProto *ppro, int iRequestType, LPCSTR _url, MTHttpRequestHandler pFunc, JSONNode *pRoot)
{
if (*_url == '/') { // relative url leads to a site
- m_szUrl = "https://discord.com/api/v8";
+ m_szUrl = "https://discord.com/api/v9";
m_szUrl += _url;
m_bMainSite = true;
}
diff --git a/protocols/Discord/src/mfa.cpp b/protocols/Discord/src/mfa.cpp
new file mode 100644
index 0000000000..9de79283fa
--- /dev/null
+++ b/protocols/Discord/src/mfa.cpp
@@ -0,0 +1,112 @@
+/*
+Copyright © 2016-22 Miranda NG team
+
+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, either version 2 of the License, or
+(at your option) any later version.
+
+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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CMfaDialog : public CDiscordDlgBase
+{
+ bool m_bHasSms, m_bHasTotp, m_bUseTotp = true;
+ CMStringA m_szTicket;
+
+ CCtrlBase m_label;
+ CCtrlEdit edtCode;
+ CCtrlButton btnCancel, btnAnother;
+
+public:
+ CMfaDialog(CDiscordProto *ppro, const JSONNode &pRoot) :
+ CDiscordDlgBase(ppro, IDD_MFA),
+ m_label(this, IDC_LABEL),
+ edtCode(this, IDC_CODE),
+ btnCancel(this, IDCANCEL),
+ btnAnother(this, IDC_ANOTHER)
+ {
+ m_bHasSms = pRoot["sms"].as_bool();
+ m_bHasTotp = pRoot["totp"].as_bool();
+ m_szTicket = pRoot["ticket"].as_mstring();
+ }
+
+ bool OnInitDialog() override
+ {
+ if (m_bUseTotp)
+ m_label.SetText(TranslateT("Enter Discord verification code:"));
+ else
+ m_label.SetText(TranslateT("Enter SMS code"));
+
+ // if (!m_bHasSms)
+ btnAnother.Disable();
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ ptrW wszCode(edtCode.GetText());
+
+ if (m_bUseTotp) {
+ JSONNode root;
+ root << WCHAR_PARAM("code", wszCode) << CHAR_PARAM("ticket", m_szTicket);
+
+ auto *pReq = new AsyncHttpRequest(m_proto, REQUEST_POST, "/auth/mfa/totp", &CDiscordProto::OnSendTotp, &root);
+ pReq->pUserInfo = this;
+ m_proto->Push(pReq);
+ }
+ return false;
+ }
+
+ void onClick_Cancel()
+ {
+ m_proto->ConnectionFailed(LOGINERR_WRONGPASSWORD);
+ }
+
+ void WrongCode()
+ {
+ edtCode.SetText(L"");
+ Beep(470, 200);
+ }
+};
+
+static void CALLBACK LaunchDialog(void *param)
+{
+ ((CMfaDialog *)param)->Show();
+}
+
+void CDiscordProto::ShowMfaDialog(const JSONNode &pRoot)
+{
+ CallFunctionAsync(LaunchDialog, new CMfaDialog(this, pRoot));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CDiscordProto::OnSendTotp(MHttpResponse *pReply, struct AsyncHttpRequest *pReq)
+{
+ auto *pDlg = (CMfaDialog *)pReq->pUserInfo;
+
+ JsonReply root(pReply);
+ if (!root) {
+ pDlg->WrongCode();
+ return;
+ }
+
+ pDlg->Close();
+
+ auto &data = root.data();
+ CMStringA szToken = data["token"].as_mstring();
+ m_szAccessToken = szToken.Detach();
+ setString("AccessToken", m_szAccessToken);
+ RetrieveMyInfo();
+}
diff --git a/protocols/Discord/src/options.cpp b/protocols/Discord/src/options.cpp
index 4716584b96..dc4a6d60b5 100644
--- a/protocols/Discord/src/options.cpp
+++ b/protocols/Discord/src/options.cpp
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////
-class CDiscardAccountOptions : public CProtoDlgBase<CDiscordProto>
+class CDiscardAccountOptions : public CDiscordDlgBase
{
CCtrlCheck chkUseChats, chkHideChats, chkUseGroups, chkDeleteMsgs;
CCtrlEdit m_edGroup, m_edUserName, m_edPassword;
@@ -27,7 +27,7 @@ class CDiscardAccountOptions : public CProtoDlgBase<CDiscordProto>
public:
CDiscardAccountOptions(CDiscordProto *ppro, int iDlgID, bool bFullDlg) :
- CProtoDlgBase<CDiscordProto>(ppro, iDlgID),
+ CDiscordDlgBase(ppro, iDlgID),
m_edGroup(this, IDC_GROUP),
m_edUserName(this, IDC_USERNAME),
m_edPassword(this, IDC_PASSWORD),
diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h
index fdb5e6a33b..bb99d2efbc 100644
--- a/protocols/Discord/src/proto.h
+++ b/protocols/Discord/src/proto.h
@@ -227,6 +227,7 @@ class CDiscordProto : public PROTO<CDiscordProto>
{
friend struct AsyncHttpRequest;
friend class CDiscardAccountOptions;
+ friend class CMfaDialog;
class CDiscordProtoImpl
{
@@ -395,6 +396,13 @@ class CDiscordProto : public PROTO<CDiscordProto>
void ParseSpecialChars(SESSION_INFO *si, CMStringW &str);
//////////////////////////////////////////////////////////////////////////////////////
+ // two-factor auth
+
+ void ShowMfaDialog(const JSONNode &pRoot);
+
+ void OnSendTotp(MHttpResponse *, struct AsyncHttpRequest *);
+
+ //////////////////////////////////////////////////////////////////////////////////////
// misc methods
SnowFlake getId(const char *szName);
@@ -526,6 +534,8 @@ public:
void CheckAvatarChange(MCONTACT hContact, const CMStringW &wszNewHash);
};
+typedef CProtoDlgBase<CDiscordProto> CDiscordDlgBase;
+
/////////////////////////////////////////////////////////////////////////////////////////
struct CMPlugin : public ACCPROTOPLUGIN<CDiscordProto>
diff --git a/protocols/Discord/src/resource.h b/protocols/Discord/src/resource.h
index 099a4af3af..dc1c9706f1 100644
--- a/protocols/Discord/src/resource.h
+++ b/protocols/Discord/src/resource.h
@@ -1,6 +1,6 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
-// Used by w:\miranda-ng\protocols\Discord\res\discord.rc
+// Used by W:\miranda-ng\protocols\Discord\res\discord.rc
//
#define IDI_MAIN 101
#define IDI_GROUPCHAT 102
@@ -9,6 +9,7 @@
#define IDD_OPTIONS_ACCMGR 105
#define IDI_VOICE_CALL 106
#define IDI_VOICE_ENDED 107
+#define IDD_MFA 108
#define IDC_PASSWORD 1001
#define IDC_USERNAME 1002
#define IDC_GROUP 1003
@@ -16,15 +17,18 @@
#define IDC_HIDECHATS 1005
#define IDC_USEGROUPS 1006
#define IDC_USEGUILDS 1007
+#define IDC_CODE 1008
#define IDC_DELETE_MSGS 1009
+#define IDC_ANOTHER 1009
+#define IDC_LABEL 1010
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_RESOURCE_VALUE 108
#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1008
+#define _APS_NEXT_CONTROL_VALUE 1011
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp
index 104d45435d..75167a7a60 100644
--- a/protocols/Discord/src/server.cpp
+++ b/protocols/Discord/src/server.cpp
@@ -281,13 +281,18 @@ void CDiscordProto::OnReceiveToken(MHttpResponse *pReply, AsyncHttpRequest*)
}
auto &data = root.data();
- CMStringA szToken = data["token"].as_mstring();
- if (szToken.IsEmpty()) {
- debugLogA("Strange empty token received, exiting");
+ if (auto &token = data["token"]) {
+ CMStringA szToken = token.as_mstring();
+ m_szAccessToken = szToken.Detach();
+ setString("AccessToken", m_szAccessToken);
+ RetrieveMyInfo();
return;
}
- m_szAccessToken = szToken.Detach();
- setString("AccessToken", m_szAccessToken);
- RetrieveMyInfo();
+ if (data["mfa"].as_bool())
+ ShowMfaDialog(data);
+ else {
+ ConnectionFailed(LOGINERR_WRONGPASSWORD);
+ debugLogA("Strange empty token received, exiting");
+ }
}