summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/ICQ-WIM/ICQ-WIM.vcxproj1
-rw-r--r--protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters3
-rw-r--r--protocols/ICQ-WIM/src/mra.cpp141
-rw-r--r--protocols/ICQ-WIM/src/proto.cpp2
-rw-r--r--protocols/ICQ-WIM/src/proto.h8
-rw-r--r--protocols/ICQ-WIM/src/server.cpp12
-rw-r--r--protocols/ICQ-WIM/src/version.h2
-rw-r--r--src/mir_app/src/netlib_http.cpp4
8 files changed, 169 insertions, 4 deletions
diff --git a/protocols/ICQ-WIM/ICQ-WIM.vcxproj b/protocols/ICQ-WIM/ICQ-WIM.vcxproj
index 67b5fc5fb6..8ee9249589 100644
--- a/protocols/ICQ-WIM/ICQ-WIM.vcxproj
+++ b/protocols/ICQ-WIM/ICQ-WIM.vcxproj
@@ -30,6 +30,7 @@
<ClCompile Include="src\http.cpp" />
<ClCompile Include="src\ignore.cpp" />
<ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\mra.cpp" />
<ClCompile Include="src\options.cpp" />
<ClCompile Include="src\poll.cpp" />
<ClCompile Include="src\proto.cpp" />
diff --git a/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters b/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters
index 1536ee0c0d..52a8649bb9 100644
--- a/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters
+++ b/protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters
@@ -35,6 +35,9 @@
<ClCompile Include="src\utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\mra.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\http.h">
diff --git a/protocols/ICQ-WIM/src/mra.cpp b/protocols/ICQ-WIM/src/mra.cpp
new file mode 100644
index 0000000000..8b1da60c15
--- /dev/null
+++ b/protocols/ICQ-WIM/src/mra.cpp
@@ -0,0 +1,141 @@
+/*
+Copyright (C) 2012-21 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 CIcqProto::SendMrimLogin(NETLIBHTTPREQUEST *pReply)
+{
+ if (pReply) {
+ for (int i=0; i < pReply->headersCount; i++) {
+ if (!mir_strcmpi(pReply->headers[i].szName, "Set-Cookie")) {
+ char *p = strchr(pReply->headers[i].szValue, ';');
+ if (p) *p = 0;
+ if (!m_szMraCookie.IsEmpty())
+ m_szMraCookie.Append("; ");
+
+ m_szMraCookie.Append(pReply->headers[i].szValue);
+ }
+ }
+ }
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://icqapilogin.mail.ru/auth/mrimLogin", &CIcqProto::OnCheckMrimLogin);
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ if (!m_szMraCookie.IsEmpty())
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq << CHAR_PARAM("clientName", "webagent") << INT_PARAM("clientVersion", 711) << CHAR_PARAM("devId", MRA_APP_ID)
+ << CHAR_PARAM("r", "91640-1626423568") << CHAR_PARAM("f", "json");
+#ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+#endif
+ Push(pReq);
+}
+
+void CIcqProto::OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ case 302:
+ break;
+
+ case 460: // no cookies at all, obtain them via MRA auth site
+ {
+ CMStringW uin(m_szOwnId);
+
+ int iStart = 0;
+ CMStringW wszLogin = uin.Tokenize(L"@", iStart);
+ CMStringW wszDomain = uin.Tokenize(L"@", iStart);
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/cgi-bin/auth?from=splash", &CIcqProto::OnCheckMraAuth);
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ if (!m_szMraCookie.IsEmpty())
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq << WCHAR_PARAM("Domain", wszDomain) << WCHAR_PARAM("Login", wszLogin) << CHAR_PARAM("Password", m_szPassword)
+ << INT_PARAM("new_auth_form", 1) << INT_PARAM("saveauth", 1);
+#ifndef _DEBUG
+ pReq->flags |= NLHRF_NODUMPSEND;
+#endif
+ Push(pReq);
+ }
+ return;
+
+ case 462: // bad cookies, refresh them
+ if (!m_bError462) {
+ m_bError462 = true;
+
+ auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://auth.mail.ru/sdc?JSONP_call=jscb_tmp_c85825&from=https%3A%2F%2Ficqapilogin.mail.ru%2Fauth%2FmrimLogin", &CIcqProto::OnCheckMraAuthFinal);
+ pReq->flags |= NLHRF_REDIRECT;
+ pReq->AddHeader("Cookie", m_szMraCookie);
+ pReq->AddHeader("Referer", "https://webagent.mail.ru/");
+ pReq->AddHeader("User-Agent", NETLIB_USER_AGENT);
+ Push(pReq);
+ return;
+ }
+
+ m_bError462 = false;
+ __fallthrough;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_szAToken = data["token"]["a"].as_mstring();
+ mir_urlDecode(m_szAToken.GetBuffer());
+ setString(DB_KEY_ATOKEN, m_szAToken);
+
+ m_szSessionKey = data["sessionKey"].as_mstring();
+
+ CMStringW szUin = data["loginId"].as_mstring();
+ if (szUin)
+ m_szOwnId = szUin;
+
+ int srvTS = data["hostTime"].as_int();
+ m_iTimeShift = (srvTS) ? time(0) - srvTS : 0;
+
+ StartSession();
+}
+
+void CIcqProto::OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ case 302:
+ m_szMraCookie.Empty();
+ SendMrimLogin(pReply);
+ break;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, root.error());
+ }
+}
+
+void CIcqProto::OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ switch (pReply->resultCode) {
+ case 200:
+ case 302:
+ // accumulate sdcs cookie and resend request
+ SendMrimLogin(pReply);
+ break;
+
+ default:
+ ConnectionFailed(LOGINERR_WRONGPROTOCOL, 500);
+ }
+}
diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp
index d7bf399154..6504dba8cb 100644
--- a/protocols/ICQ-WIM/src/proto.cpp
+++ b/protocols/ICQ-WIM/src/proto.cpp
@@ -60,6 +60,8 @@ CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) :
db_set_resident(m_szModuleName, "IdleTS");
db_set_resident(m_szModuleName, "OnlineTS");
+ m_isMra = !stricmp(Proto_GetAccount(m_szModuleName)->szProtoName, "MRA");
+
// services
CreateProtoService(PS_CREATEACCMGRUI, &CIcqProto::CreateAccMgrUI);
diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h
index 3a5666b9f2..c6f0ff9502 100644
--- a/protocols/ICQ-WIM/src/proto.h
+++ b/protocols/ICQ-WIM/src/proto.h
@@ -33,6 +33,7 @@
#include "m_system.h"
#include "m_protoint.h"
+#define MRA_APP_ID "ic1pzYNtEU6dDnEQ"
#define ICQ_APP_ID "ic1nmMjqg7Yu-0hL"
#define ICQ_API_SERVER "https://u.icq.net/api/v17/wim"
#define ICQ_FILE_SERVER "https://u.icq.net/files/api/v1.1"
@@ -247,7 +248,7 @@ class CIcqProto : public PROTO<CIcqProto>
friend AsyncHttpRequest* operator <<(AsyncHttpRequest*, const AIMSID&);
- bool m_bOnline, m_bTerminated, m_bFirstBos;
+ bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462;
int m_iTimeShift;
MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove);
@@ -261,6 +262,7 @@ class CIcqProto : public PROTO<CIcqProto>
bool RetrievePassword();
void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead);
void RetrieveUserInfo(MCONTACT = INVALID_CONTACT_ID);
+ void SendMrimLogin(NETLIBHTTPREQUEST *pReply);
void SetServerStatus(int iNewStatus);
void ShutdownSession(void);
void StartSession(void);
@@ -287,6 +289,9 @@ class CIcqProto : public PROTO<CIcqProto>
void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
@@ -329,6 +334,7 @@ class CIcqProto : public PROTO<CIcqProto>
CMStringA m_szRToken;
CMStringA m_fetchBaseURL;
CMStringA m_aimsid;
+ CMStringA m_szMraCookie;
LONG m_msgId = 1;
int m_iRClientId;
HGENMENU m_hUploadGroups;
diff --git a/protocols/ICQ-WIM/src/server.cpp b/protocols/ICQ-WIM/src/server.cpp
index 2525482ec7..c650fc2224 100644
--- a/protocols/ICQ-WIM/src/server.cpp
+++ b/protocols/ICQ-WIM/src/server.cpp
@@ -89,7 +89,16 @@ void CIcqProto::CheckPassword()
m_szAToken = getMStringA(DB_KEY_ATOKEN);
m_iRClientId = getDword(DB_KEY_RCLIENTID);
m_szSessionKey = getMStringA(DB_KEY_SESSIONKEY);
- if (m_szAToken.IsEmpty() || m_szSessionKey.IsEmpty()) {
+ if (!m_szAToken.IsEmpty() && !m_szSessionKey.IsEmpty()) {
+ StartSession();
+ return;
+ }
+
+ if (m_isMra) {
+ m_bError462 = false;
+ SendMrimLogin(nullptr);
+ }
+ else {
auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "https://api.login.icq.net/auth/clientLogin", &CIcqProto::OnCheckPassword);
pReq << CHAR_PARAM("clientName", "Miranda NG") << CHAR_PARAM("clientVersion", mirVer) << CHAR_PARAM("devId", ICQ_APP_ID)
<< CHAR_PARAM("f", "json") << CHAR_PARAM("tokenType", "longTerm") << WCHAR_PARAM("s", m_szOwnId) << CHAR_PARAM("pwd", m_szPassword);
@@ -98,7 +107,6 @@ void CIcqProto::CheckPassword()
#endif
Push(pReq);
}
- else StartSession();
}
IcqFileInfo* CIcqProto::CheckFile(MCONTACT hContact, CMStringW &wszText, bool &bIsFile)
diff --git a/protocols/ICQ-WIM/src/version.h b/protocols/ICQ-WIM/src/version.h
index 07aa40c64b..a8e270e799 100644
--- a/protocols/ICQ-WIM/src/version.h
+++ b/protocols/ICQ-WIM/src/version.h
@@ -1,7 +1,7 @@
#define __MAJOR_VERSION 0
#define __MINOR_VERSION 96
#define __RELEASE_NUM 1
-#define __BUILD_NUM 1
+#define __BUILD_NUM 2
#include <stdver.h>
diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp
index 9db81ae182..da18e4bf53 100644
--- a/src/mir_app/src/netlib_http.cpp
+++ b/src/mir_app/src/netlib_http.cpp
@@ -929,6 +929,8 @@ char* gzip_decode(char *gzip_data, int *len_ptr, int window)
do {
output_data = (char*)mir_realloc(output_data, gzip_len+1);
+ if (output_data == nullptr)
+ break;
zstr.next_in = (Bytef*)gzip_data;
zstr.avail_in = *len_ptr;
@@ -944,6 +946,8 @@ char* gzip_decode(char *gzip_data, int *len_ptr, int window)
inflateEnd(&zstr);
gzip_len *= 2;
+ if (gzip_len > 10000000)
+ break;
} while (gzip_err == Z_BUF_ERROR);
gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;