diff options
-rw-r--r-- | protocols/ICQ-WIM/ICQ-WIM.vcxproj | 1 | ||||
-rw-r--r-- | protocols/ICQ-WIM/ICQ-WIM.vcxproj.filters | 3 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/mra.cpp | 141 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/proto.cpp | 2 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/proto.h | 8 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/server.cpp | 12 | ||||
-rw-r--r-- | protocols/ICQ-WIM/src/version.h | 2 | ||||
-rw-r--r-- | src/mir_app/src/netlib_http.cpp | 4 |
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; |