diff options
author | George Hazan <ghazan@miranda.im> | 2019-01-16 18:06:52 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-01-16 18:06:52 +0300 |
commit | aa514a9f95c8cd95c01952250f7cdb670a66af9a (patch) | |
tree | ecb84e10fc3af008e1a8ac9027f1b526634c8d65 /protocols/ICQ-WIM/src/http.cpp | |
parent | 4f0a4ae4724fdfe65f63ee41dc1790ccbb0668f7 (diff) |
fixes #1761 (Rename Icq10 folder and ICQ/2018 in version.h to ICQ-WIM)
Diffstat (limited to 'protocols/ICQ-WIM/src/http.cpp')
-rw-r--r-- | protocols/ICQ-WIM/src/http.cpp | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/protocols/ICQ-WIM/src/http.cpp b/protocols/ICQ-WIM/src/http.cpp new file mode 100644 index 0000000000..e0bd34d17a --- /dev/null +++ b/protocols/ICQ-WIM/src/http.cpp @@ -0,0 +1,289 @@ +// ----------------------------------------------------------------------------- +// ICQ plugin for Miranda NG +// ----------------------------------------------------------------------------- +// Copyright © 2018-19 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// ----------------------------------------------------------------------------- + +#include "stdafx.h" + +#pragma comment(lib, "Rpcrt4.lib") + +void __cdecl CIcqProto::ServerThread(void*) +{ + memset(&m_ConnPool, 0, sizeof(m_ConnPool)); + m_bTerminated = false; + + debugLogA("CIcqProto::WorkerThread: %s", "entering"); + + while (true) { + WaitForSingleObject(m_evRequestsQueue, 1000); + if (m_bTerminated) + break; + + while (true) { + bool bNeedSleep = false; + AsyncHttpRequest *pReq; + { + mir_cslock lck(m_csHttpQueue); + if (m_arHttpQueue.getCount() == 0) + break; + + pReq = m_arHttpQueue[0]; + m_arHttpQueue.remove(0); + bNeedSleep = (m_arHttpQueue.getCount() > 1); + } + if (m_bTerminated) + break; + + ExecuteRequest(pReq); + if (bNeedSleep) + Sleep(200); + } + + int ts = time(0); + for (auto &it : m_ConnPool) { + int idx = int(&it - m_ConnPool); + if (idx == CONN_FETCH) + continue; + + if (it.s && it.lastTs + it.timeout < ts) { + debugLogA("Socket #1 (%p) expired", idx, it.s); + Netlib_CloseHandle(it.s); + it.s = nullptr; + it.lastTs = 0; + } + } + } + + m_hWorkerThread = nullptr; + for (auto &it : m_ConnPool) { + if (it.s) + Netlib_CloseHandle(it.s); + it.s = nullptr; + it.lastTs = it.timeout = 0; + } + + debugLogA("CIcqProto::WorkerThread: %s", "leaving"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +AsyncHttpRequest::AsyncHttpRequest(IcqConnection conn, int iType, const char *szUrl, MTHttpRequestHandler pFunc) : + m_conn(conn) +{ + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_DUMPASTEXT; + requestType = iType; + m_szUrl = szUrl; + m_pFunc = pFunc; + + GUID packetId; + UuidCreate(&packetId); + + RPC_CSTR szId; + UuidToStringA(&packetId, &szId); + strncpy_s(m_reqId, (char*)szId, _TRUNCATE); + RpcStringFreeA(&szId); + + if (iType == REQUEST_POST) { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + + dataLength = m_szParam.GetLength(); + pData = m_szParam.Detach(); + } +} + +void AsyncHttpRequest::ReplaceJsonParam(const char *paramName, const char *newValue) +{ + CMStringA szOldValue(FORMAT, "\"%s\":\"", paramName); + int idx = m_szParam.Find(szOldValue); + if (idx == -1) + return; + + idx += szOldValue.GetLength(); + int iEnd = m_szParam.Find("\"", idx); + if (iEnd == -1) + return; + + szOldValue += m_szParam.Mid(idx, iEnd + 1 - idx); + + CMStringA szNewValue(FORMAT, "\"%s\":\"%s\"", paramName, newValue); + m_szParam.Replace(szOldValue, szNewValue); + + replaceStr(pData, nullptr); + dataLength = 0; +} + +void CIcqProto::ExecuteRequest(AsyncHttpRequest *pReq) +{ + CMStringA str; + + pReq->szUrl = pReq->m_szUrl.GetBuffer(); + if (!pReq->m_szParam.IsEmpty()) { + if (pReq->requestType == REQUEST_GET) { + str.Format("%s?%s", pReq->m_szUrl.c_str(), pReq->m_szParam.c_str()); + pReq->szUrl = str.GetBuffer(); + } + else { + pReq->dataLength = pReq->m_szParam.GetLength(); + pReq->pData = mir_strdup(pReq->m_szParam); + } + } + + if (pReq->m_conn == CONN_RAPI) { + CMStringA szAgent(FORMAT, "%d Mail.ru Windows ICQ (version 10.0.1999)", DWORD(m_dwUin)); + pReq->AddHeader("User-Agent", szAgent); + + if (m_szRToken.IsEmpty()) { + if (!RefreshRobustToken()) { + delete pReq; + return; + } + + pReq->ReplaceJsonParam("authToken", m_szRToken); + pReq->dataLength = pReq->m_szParam.GetLength(); + pReq->pData = mir_strdup(pReq->m_szParam); + } + } + + debugLogA("Executing request %s:\n%s", pReq->m_reqId, pReq->szUrl); + + if (pReq->m_conn != CONN_NONE) { + pReq->flags |= NLHRF_PERSISTENT; + pReq->nlc = m_ConnPool[pReq->m_conn].s; + m_ConnPool[pReq->m_conn].lastTs = time(0); + } + + NETLIBHTTPREQUEST *reply = Netlib_HttpTransaction(m_hNetlibUser, pReq); + if (reply != nullptr) { + if (pReq->m_conn != CONN_NONE) { + auto &conn = m_ConnPool[pReq->m_conn]; + conn.s = reply->nlc; + conn.timeout = 0; + for (int i = 0; i < reply->headersCount; i++) { + if (!mir_strcmp(reply->headers[i].szName, "Keep-Alive")) { + int timeout; + if (1 == sscanf(reply->headers[i].szValue, "timeout=%d", &timeout)) + conn.timeout = timeout; + break; + } + } + } + + if (pReq->m_conn == CONN_RAPI && reply->pData && strstr(reply->pData, "\"code\": 40201")) { + RobustReply r(reply); + if (r.error() == 40201) { // robust token expired + m_szRToken.Empty(); + + // if token refresh succeeded, replace it in the query and push request back + if (RefreshRobustToken()) { + pReq->ReplaceJsonParam("authToken", m_szRToken); + Push(pReq); + } + else delete pReq; + return; + } + } + + if (pReq->m_pFunc != nullptr) + (this->*(pReq->m_pFunc))(reply, pReq); + + Netlib_FreeHttpRequest(reply); + } + else { + debugLogA("Request %s failed", pReq->m_reqId); + + if (pReq->m_conn != CONN_NONE) { + if (IsStatusConnecting(m_iStatus)) + ConnectionFailed(LOGINERR_NONETWORK); + m_ConnPool[pReq->m_conn].s = nullptr; + } + } + + delete pReq; +} + +void CIcqProto::Push(MHttpRequest *p) +{ + AsyncHttpRequest *pReq = (AsyncHttpRequest*)p; + + pReq->timeout = 10000; + { + mir_cslock lck(m_csHttpQueue); + m_arHttpQueue.insert(pReq); + } + + SetEvent(m_evRequestsQueue); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +JsonReply::JsonReply(NETLIBHTTPREQUEST *pReply) +{ + if (pReply == nullptr) { + m_errorCode = 500; + return; + } + + m_errorCode = pReply->resultCode; + if (m_errorCode != 200) + return; + + m_root = json_parse(pReply->pData); + if (m_root == nullptr) { + m_errorCode = 500; + return; + } + + JSONNode &response = (*m_root)["response"]; + m_errorCode = response["statusCode"].as_int(); + m_requestId = response["requestId"].as_mstring(); + m_detailCode = response["statusDetailCode"].as_int(); + m_data = &response["data"]; +} + +JsonReply::~JsonReply() +{ + json_delete(m_root); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RobustReply::RobustReply(NETLIBHTTPREQUEST *pReply) +{ + if (pReply == nullptr) { + m_errorCode = 500; + return; + } + + m_errorCode = pReply->resultCode; + if (m_errorCode != 200) + return; + + m_root = json_parse(pReply->pData); + if (m_root == nullptr) { + m_errorCode = 500; + return; + } + + m_errorCode = (*m_root)["status"]["code"].as_int(); + m_results = &(*m_root)["results"]; +} + +RobustReply::~RobustReply() +{ + json_delete(m_root); +} |