1191 lines
53 KiB
C++
1191 lines
53 KiB
C++
/*++
|
|
|
|
Program name:
|
|
|
|
dm
|
|
|
|
Module Name:
|
|
|
|
WebSocket.cpp
|
|
|
|
Notices:
|
|
|
|
Module: WebSocket
|
|
|
|
Author:
|
|
|
|
Copyright (c) Prepodobny Alen
|
|
|
|
mailto: alienufo@inbox.ru
|
|
mailto: ufocomp@gmail.com
|
|
|
|
--*/
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
#include "Core.hpp"
|
|
#include "WebSocket.hpp"
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
#include <random>
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
#define SYSTEM_PROVIDER_NAME "system"
|
|
#define SERVICE_APPLICATION_NAME "service"
|
|
#define CONFIG_SECTION_NAME "module"
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
#define NOT_FOUND_ACTIVE_CONNECTION "Not found active connection. Try again later."
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
extern "C++" {
|
|
|
|
namespace Apostol {
|
|
|
|
namespace Module {
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
//-- CWebSocketModule ------------------------------------------------------------------------------------------
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
CWebSocketModule::CWebSocketModule(CModuleProcess *AProcess, const CString &ModuleName, const CString &SectionName):
|
|
CCustomModule(AProcess, ModuleName, SectionName) {
|
|
|
|
m_SyncPeriod = BPS_DEFAULT_SYNC_PERIOD;
|
|
m_Headers.Add("Authorization");
|
|
|
|
CWebSocketModule::InitMethods();
|
|
|
|
InitServerList();
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::InitServerList() {
|
|
m_DefaultServer.Value().PGP().Add(CKeyContext(CString("PUBLIC"), CString()));
|
|
m_DefaultServer.Value().PGP().Add(CKeyContext(m_DefaultServer.Name(), CString()));
|
|
|
|
if (m_Servers.Count() == 0) {
|
|
#ifdef _DEBUG
|
|
const int index = m_Servers.AddPair(BPS_DEBUG_SERVER_URL, CClientContext(BPS_BM_DEBUG_ADDRESS, CLocation(BPS_DEBUG_SERVER_URL)));
|
|
auto &Keys = m_Servers[index].Value().PGP();
|
|
Keys.Add(CKeyContext(CString("PUBLIC"), CString()));
|
|
Keys.Add(CKeyContext(CString(BPS_BM_DEBUG_ADDRESS), CString()));
|
|
#endif
|
|
m_Servers.Add(m_DefaultServer);
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
CWebSocketClient *CWebSocketModule::GetWebSocketClient(CClientContext &Context) {
|
|
const auto pClient = Context.ClientManager().Add(&Context, CLocation(Context.URL().Origin() + "/session/" + Context.Session()));
|
|
|
|
pClient->Session() = Context.Session();
|
|
|
|
pClient->ClientName() = GApplication->Title();
|
|
pClient->AutoConnect(false);
|
|
pClient->AllocateEventHandlers(Server());
|
|
|
|
#if defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 9)
|
|
//pClient->OnVerbose([this](auto && Sender, auto && AFormat, auto && args) { DoVerbose(Sender, AFormat, args); });
|
|
pClient->OnException([this](auto && AConnection, auto && AException) { DoException(AConnection, AException); });
|
|
pClient->OnEventHandlerException([this](auto && AHandler, auto && AException) { DoEventHandlerException(AHandler, AException); });
|
|
pClient->OnNoCommandHandler([this](auto && Sender, auto && AData, auto && AConnection) { DoNoCommandHandler(Sender, AData, AConnection); });
|
|
pClient->OnWebSocketError([this](auto && AConnection) { DoWebSocketError(AConnection); });
|
|
pClient->OnConnected([this](auto && Sender) { DoClientConnected(Sender); });
|
|
pClient->OnDisconnected([this](auto && Sender) { DoClientDisconnected(Sender); });
|
|
pClient->OnMessage([this](auto && Sender, auto && Message) { DoClientMessage(Sender, Message); });
|
|
pClient->OnError([this](auto && Sender, int Code, auto && Message) { DoClientError(Sender, Code, Message); });
|
|
pClient->OnHeartbeat([this](auto && Sender) { DoClientHeartbeat(Sender); });
|
|
pClient->OnTimeOut([this](auto && Sender) { DoClientTimeOut(Sender); });
|
|
pClient->OnAuthorize([this](auto && Sender, auto && Request, auto && Response) { DoAuthorizeEvent(Sender, Request, Response); });
|
|
pClient->OnSubscribe([this](auto && Sender, auto && Request, auto && Response) { DoSubscribeEvent(Sender, Request, Response); });
|
|
pClient->OnKey([this](auto && Sender, auto && Request, auto && Response) { DoKeyEvent(Sender, Request, Response); });
|
|
pClient->OnWebSocketEvent([this](auto && Sender, auto && Request, auto && Response) { DoWebSocketClientEvent(Sender, Request, Response); });
|
|
#else
|
|
//pClient->OnVerbose(std::bind(&CWebSocketModule::DoVerbose, this, _1, _2, _3));
|
|
pClient->OnException(std::bind(&CWebSocketModule::DoException, this, _1, _2));
|
|
pClient->OnEventHandlerException(std::bind(&CWebSocketModule::DoEventHandlerException, this, _1, _2));
|
|
pClient->OnNoCommandHandler(std::bind(&CWebSocketModule::DoNoCommandHandler, this, _1, _2, _3));
|
|
pClient->OnWebSocketError(std::bind(&CWebSocketModule::DoWebSocketError, this, _1));
|
|
pClient->OnConnected(std::bind(&CWebSocketModule::DoClientConnected, this, _1));
|
|
pClient->OnDisconnected(std::bind(&CWebSocketModule::DoClientDisconnected, this, _1));
|
|
pClient->OnMessage(std::bind(&CWebSocketModule::DoClientMessage, this, _1, _2));
|
|
pClient->OnError(std::bind(&CWebSocketModule::DoClientError, this, _1, _2, _3));
|
|
pClient->OnHeartbeat(std::bind(&CWebSocketModule::DoClientHeartbeat, this, _1));
|
|
pClient->OnTimeOut(std::bind(&CWebSocketModule::DoClientTimeOut, this, _1));
|
|
pClient->OnAuthorize(std::bind(&CWebSocketModule::DoAuthorizeEvent, this, _1, _2, _3));
|
|
pClient->OnSubscribe(std::bind(&CWebSocketModule::DoSubscribeEvent, this, _1, _2, _3));
|
|
pClient->OnKey(std::bind(&CWebSocketModule::DoKeyEvent, this, _1, _2, _3));
|
|
pClient->OnWebSocketEvent(std::bind(&CWebSocketModule::DoWebSocketClientEvent, this, _1, _2, _3));
|
|
#endif
|
|
return pClient;
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::CreateWebSocketClient(CClientContext &Context) {
|
|
auto pClient = GetWebSocketClient(Context);
|
|
try {
|
|
pClient->Active(true);
|
|
|
|
Context.SetFixedDate(0);
|
|
Context.SetStatus(csRunning);
|
|
} catch (std::exception &e) {
|
|
Log()->Error(APP_LOG_ERR, 0, e.what());
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
CWebSocketClient *CWebSocketModule::GetConnectedClient(const CClientContext &Context) {
|
|
for (int i = 0; i < Context.ClientManager().Count(); i++) {
|
|
const auto pClient = Context.ClientManager()[i];
|
|
if (pClient->Connected())
|
|
return pClient;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
int CWebSocketModule::CurrentContextIndex(const CString &Params) {
|
|
|
|
int index = 0;
|
|
while (index < m_Servers.Count()) {
|
|
const auto &caContext = m_Servers[index].Value();
|
|
if (caContext.Status() == Context::csRunning) {
|
|
if (Params.IsEmpty() || Params == caContext.Name() || Params == caContext.URL().Origin()) {
|
|
const auto pClient = GetConnectedClient(caContext);
|
|
if (pClient != nullptr)
|
|
return index;
|
|
}
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
CWebSocketClient *CWebSocketModule::GetConnectedClient(const CString &Params) {
|
|
|
|
int index = 0;
|
|
while (index < m_Servers.Count()) {
|
|
const auto &caContext = m_Servers[index].Value();
|
|
if (caContext.Status() == Context::csRunning) {
|
|
if (Params.IsEmpty() || Params == caContext.URL().Origin()) {
|
|
auto pClient = GetConnectedClient(caContext);
|
|
if (pClient != nullptr)
|
|
return pClient;
|
|
}
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientConnected(CObject *Sender) {
|
|
auto pConnection = dynamic_cast<CWebSocketClientConnection *>(Sender);
|
|
if (pConnection != nullptr) {
|
|
auto pBinding = pConnection->Socket()->Binding();
|
|
if (pBinding != nullptr) {
|
|
Log()->Notice(_T("[%s:%d] [%s] WebSocket client connected."),
|
|
pConnection->Socket()->Binding()->IP(),
|
|
pConnection->Socket()->Binding()->Port(),
|
|
pConnection->Session().c_str());
|
|
} else {
|
|
Log()->Notice(_T("[%s] WebSocket client connected."),
|
|
pConnection->Session().c_str());
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientDisconnected(CObject *Sender) {
|
|
auto pConnection = dynamic_cast<CWebSocketClientConnection *>(Sender);
|
|
if (pConnection != nullptr) {
|
|
auto pBinding = pConnection->Socket()->Binding();
|
|
if (pBinding != nullptr) {
|
|
Log()->Notice(_T("[%s:%d] [%s] WebSocket client disconnected."),
|
|
pConnection->Socket()->Binding()->IP(),
|
|
pConnection->Socket()->Binding()->Port(),
|
|
pConnection->Session().c_str());
|
|
} else {
|
|
Log()->Notice(_T("[%s] WebSocket client disconnected."),
|
|
pConnection->Session().c_str());
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientHeartbeat(CObject *Sender) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientTimeOut(CObject *Sender) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
pClient->SwitchConnection(nullptr);
|
|
pClient->Reload();
|
|
pContext->SetFixedDate(0);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientMessage(CObject *Sender, const CWSMessage &Message) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
Log()->Message("[%s] [%s] [%s] [%s] %s", pClient->Session().c_str(),
|
|
Message.UniqueId.c_str(),
|
|
Message.Action.IsEmpty() ? "Unknown" : Message.Action.c_str(),
|
|
CWSMessage::MessageTypeIdToString(Message.MessageTypeId).c_str(),
|
|
Message.Payload.IsNull() ? "{}" : Message.Payload.ToString().c_str());
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoClientError(CObject *Sender, int Code, const CString &Message) {
|
|
Log()->Error(APP_LOG_ERR, 0, "[%d] %s", Code, Message.c_str());
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoAuthorizeEvent(CObject *Sender, const CWSMessage &Request, const CWSMessage &Response) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoSubscribeEvent(CObject *Sender, const CWSMessage &Request, const CWSMessage &Response) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoKeyEvent(CObject *Sender, const CWSMessage &Request, const CWSMessage &Response) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
|
|
auto &Keys = pContext->PGP();
|
|
|
|
for (int i = 0; i < Keys.Count(); i++ ) {
|
|
auto &Key = Keys[i];
|
|
|
|
Key.StatusTime = Now();
|
|
Key.Status = CKeyContext::ksSuccess;
|
|
Key.RunTime = GetRandomDate(10 * 60, m_SyncPeriod * 60, Key.StatusTime); // 10..m_SyncPeriod min
|
|
|
|
if (Response.Payload.HasOwnProperty("data")) {
|
|
if (Response.Payload.HasOwnProperty("code")) {
|
|
if (Response.Payload["code"].AsString() == Key.Name) {
|
|
if (Key.Name == "PUBLIC") {
|
|
UpdateServerList(m_Servers, Response.Payload["data"].AsString());
|
|
} else {
|
|
Key.Key = Response.Payload["data"].AsString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoWebSocketClientEvent(CObject *Sender, const CWSMessage &Request, const CWSMessage &Response) {
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (Sender);
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoWebSocketError(CTCPConnection *AConnection) {
|
|
auto pConnection = dynamic_cast<CWebSocketClientConnection *> (AConnection);
|
|
auto pClient = dynamic_cast<CWebSocketClient *> (pConnection->Client());
|
|
chASSERT(pClient);
|
|
auto pContext = pClient->Context();
|
|
chASSERT(pContext);
|
|
const auto &Reply = pConnection->Reply();
|
|
|
|
if (Reply.Status == CHTTPReply::moved_permanently || Reply.Status == CHTTPReply::moved_temporarily) {
|
|
const auto &caLocation = Reply.Headers["Location"];
|
|
if (!caLocation.IsEmpty()) {
|
|
pClient->SetURI(CLocation(caLocation));
|
|
Log()->Notice(_T("[%s] Redirect to %s."), pClient->Session().c_str(), pClient->URI().href().c_str());
|
|
}
|
|
pContext->SetFixedDate(0);
|
|
} else {
|
|
auto pBinding = pConnection->Socket()->Binding();
|
|
if (pBinding != nullptr) {
|
|
Log()->Warning(_T("[%s:%d] [%s] WebSocket client failed to establish connection"),
|
|
pConnection->Socket()->Binding()->IP(),
|
|
pConnection->Socket()->Binding()->Port(),
|
|
pConnection->Session().c_str());
|
|
} else {
|
|
Log()->Warning(_T("[%s] WebSocket client failed to establish connection."),
|
|
pConnection->Session().c_str());
|
|
}
|
|
pContext->SetFixedDate(Now() + (CDateTime) 1 / MinsPerDay); // 1 min
|
|
}
|
|
|
|
pConnection->CloseConnection(true);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI) {
|
|
|
|
auto OnRequest = [AConnection](CWebSocketMessageHandler *AHandler, CWebSocketConnection *AWSConnection) {
|
|
auto &Reply = AConnection->Reply();
|
|
const auto &wsMessage = CCustomWebSocketClient::RequestToMessage(AWSConnection);
|
|
Reply.ContentType = CHTTPReply::json;
|
|
if (wsMessage.MessageTypeId == mtCallResult) {
|
|
Reply.Content = wsMessage.Payload.ToString();
|
|
AConnection->SendReply(CHTTPReply::ok, nullptr, true);
|
|
} else {
|
|
ReplyError(AConnection, CHTTPReply::bad_request, wsMessage.ErrorMessage);
|
|
}
|
|
};
|
|
|
|
const auto &caServerRequest = AConnection->Request();
|
|
|
|
const auto &caModuleAddress = m_Module["address"];
|
|
const auto &caHost = caServerRequest.Headers["host"];
|
|
const auto &caOrigin = caServerRequest.Headers["origin"];
|
|
|
|
const auto &caUserAddress = GetParam(caServerRequest, "address");
|
|
const auto &pgpValue = GetParam(caServerRequest, "pgp");
|
|
const auto &caServerParam = GetParam(caServerRequest, "server");
|
|
|
|
CStringList caClearText;
|
|
CString sPayload;
|
|
|
|
if (!caServerRequest.Content.IsEmpty()) {
|
|
|
|
const auto &ContentType = caServerRequest.Headers.Values(_T("content-type"));
|
|
|
|
if (ContentType.Find("application/x-www-form-urlencoded") == 0) {
|
|
|
|
const CStringList &FormData = caServerRequest.FormData;
|
|
|
|
const auto &formDate = FormData["date"];
|
|
const auto &formAddress = FormData["address"];
|
|
const auto &formBitmessage = FormData["bitmessage"];
|
|
const auto &formKey = FormData["key"];
|
|
const auto &formPGP = FormData["pgp"];
|
|
const auto &formURL = FormData["url"];
|
|
const auto &formFlags = FormData["flags"];
|
|
const auto &formSign = FormData["sign"];
|
|
|
|
if (!formDate.IsEmpty()) {
|
|
caClearText << formDate;
|
|
}
|
|
|
|
if (!formAddress.IsEmpty()) {
|
|
caClearText << formAddress;
|
|
}
|
|
|
|
if (!formFlags.IsEmpty()) {
|
|
caClearText << formFlags;
|
|
}
|
|
|
|
if (!formBitmessage.IsEmpty()) {
|
|
caClearText << formBitmessage;
|
|
}
|
|
|
|
if (!formKey.IsEmpty()) {
|
|
caClearText << formKey;
|
|
}
|
|
|
|
if (!formPGP.IsEmpty()) {
|
|
caClearText << formPGP;
|
|
}
|
|
|
|
if (!formURL.IsEmpty()) {
|
|
caClearText << formURL;
|
|
}
|
|
|
|
if (!formSign.IsEmpty()) {
|
|
caClearText << formSign;
|
|
}
|
|
|
|
} else if (ContentType.Find("multipart/form-data") == 0) {
|
|
|
|
CFormData FormData;
|
|
CHTTPRequestParser::ParseFormData(caServerRequest, FormData);
|
|
|
|
const auto &formDate = FormData.Data("date");
|
|
const auto &formAddress = FormData.Data("address");
|
|
const auto &formBitmessage = FormData.Data("bitmessage");
|
|
const auto &formKey = FormData.Data("key");
|
|
const auto &formPGP = FormData.Data("pgp");
|
|
const auto &formURL = FormData.Data("url");
|
|
const auto &formFlags = FormData.Data("flags");
|
|
const auto &formSign = FormData.Data("sign");
|
|
|
|
if (!formDate.IsEmpty()) {
|
|
caClearText << formDate;
|
|
}
|
|
|
|
if (!formAddress.IsEmpty()) {
|
|
caClearText << formAddress;
|
|
}
|
|
|
|
if (!formFlags.IsEmpty()) {
|
|
caClearText << formFlags;
|
|
}
|
|
|
|
if (!formBitmessage.IsEmpty()) {
|
|
caClearText << formBitmessage;
|
|
}
|
|
|
|
if (!formKey.IsEmpty()) {
|
|
caClearText << formKey;
|
|
}
|
|
|
|
if (!formPGP.IsEmpty()) {
|
|
caClearText << formPGP;
|
|
}
|
|
|
|
if (!formURL.IsEmpty()) {
|
|
caClearText << formURL;
|
|
}
|
|
|
|
if (!formSign.IsEmpty()) {
|
|
caClearText << formSign;
|
|
}
|
|
|
|
} else if (ContentType.Find("application/json") == 0) {
|
|
|
|
const CJSON contextJson(caServerRequest.Content);
|
|
|
|
const auto &jsonDate = contextJson["date"].AsString();
|
|
const auto &jsonAddress = contextJson["address"].AsString();
|
|
const auto &jsonBitmessage = contextJson["bitmessage"].AsString();
|
|
const auto &jsonKey = contextJson["key"].AsString();
|
|
const auto &jsonPGP = contextJson["pgp"].AsString();
|
|
const auto &jsonFlags = contextJson["flags"].AsString();
|
|
const auto &jsonSign = contextJson["sign"].AsString();
|
|
|
|
if (!jsonDate.IsEmpty()) {
|
|
caClearText << jsonDate;
|
|
}
|
|
|
|
if (!jsonAddress.IsEmpty()) {
|
|
caClearText << jsonAddress;
|
|
}
|
|
|
|
if (!jsonFlags.IsEmpty()) {
|
|
caClearText << jsonFlags;
|
|
}
|
|
|
|
if (!jsonBitmessage.IsEmpty()) {
|
|
caClearText << jsonBitmessage;
|
|
}
|
|
|
|
if (!jsonKey.IsEmpty()) {
|
|
caClearText << jsonKey;
|
|
}
|
|
|
|
if (!jsonPGP.IsEmpty()) {
|
|
caClearText << jsonPGP;
|
|
}
|
|
|
|
const CJSONValue &jsonURL = contextJson["url"];
|
|
if (jsonURL.IsArray()) {
|
|
const CJSONArray &arrayURL = jsonURL.Array();
|
|
for (int i = 0; i < arrayURL.Count(); i++) {
|
|
caClearText << arrayURL[i].AsString();
|
|
}
|
|
}
|
|
|
|
if (!jsonSign.IsEmpty()) {
|
|
caClearText << jsonSign;
|
|
}
|
|
|
|
} else {
|
|
caClearText = caServerRequest.Content;
|
|
}
|
|
|
|
if (pgpValue == "off" || pgpValue == "false") {
|
|
sPayload = caClearText.Text();
|
|
} else {
|
|
Apostol::PGP::CleartextSignature(
|
|
m_pgpModuleKey,
|
|
m_pgpPassphrase,
|
|
BPS_PGP_HASH,
|
|
caClearText.Text(),
|
|
sPayload);
|
|
}
|
|
}
|
|
|
|
CJSON Json(jvtObject);
|
|
CJSONValue Module(jvtObject);
|
|
|
|
Module.Object().AddPair("address", caModuleAddress);
|
|
|
|
Json.Object().AddPair("id", GetUID(16).Lower());
|
|
Json.Object().AddPair("host", caHost);
|
|
|
|
if (!caOrigin.IsEmpty()) {
|
|
Json.Object().AddPair("origin", caOrigin);
|
|
}
|
|
|
|
Json.Object().AddPair("module", Module);
|
|
Json.Object().AddPair("address", caUserAddress.IsEmpty() ? caModuleAddress : caUserAddress);
|
|
|
|
if (!sPayload.IsEmpty())
|
|
Json.Object().AddPair("payload", base64_encode(sPayload));
|
|
|
|
auto pClient = GetConnectedClient(caServerParam);
|
|
|
|
if (pClient == nullptr) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
pClient->Send(URI, Json, OnRequest);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action) {
|
|
|
|
auto OnRequest = [AConnection](CWebSocketMessageHandler *AHandler, CWebSocketConnection *AWSConnection) {
|
|
auto &Reply = AConnection->Reply();
|
|
const auto &wsMessage = CCustomWebSocketClient::RequestToMessage(AWSConnection);
|
|
Reply.ContentType = CHTTPReply::json;
|
|
if (wsMessage.MessageTypeId == mtCallResult) {
|
|
Reply.Content = wsMessage.Payload.ToString();
|
|
AConnection->SendReply(CHTTPReply::ok, nullptr, true);
|
|
} else {
|
|
ReplyError(AConnection, CHTTPReply::bad_request, wsMessage.ErrorMessage);
|
|
}
|
|
};
|
|
|
|
const auto &caServerRequest = AConnection->Request();
|
|
|
|
const auto &caModuleAddress = m_Module["address"];
|
|
const auto &caModuleFee = m_Module["fee"];
|
|
|
|
const auto checkFee = CheckFee(caModuleFee);
|
|
if (checkFee == -1)
|
|
throw ExceptionFrm("Invalid module fee value: %s", caModuleFee.c_str());
|
|
|
|
const auto &caHost = caServerRequest.Headers["host"];
|
|
const auto &caOrigin = caServerRequest.Headers["origin"];
|
|
|
|
const auto &code = GetParam(caServerRequest, "code");
|
|
const auto &address = GetParam(caServerRequest, "address");
|
|
const auto &pgpValue = GetParam(caServerRequest, "pgp");
|
|
const auto &caServerParam = GetParam(caServerRequest, "server");
|
|
|
|
const auto &caUserAddress = address.length() == 40 ? CString() : address;
|
|
const auto &caDealCode = !code.empty() ? code : address.length() == 40 ? address : CString();
|
|
|
|
const auto index = CurrentContextIndex(caServerParam);
|
|
if (index == -1) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
const auto &caContext = m_Servers[index].Value();
|
|
|
|
YAML::Node Node;
|
|
|
|
CString sPayload;
|
|
|
|
if (!caServerRequest.Content.IsEmpty() && Action != "status") {
|
|
|
|
const auto utc = UTC();
|
|
const auto &ContentType = caServerRequest.Headers.Values(_T("content-type"));
|
|
|
|
if (ContentType.Find("application/x-www-form-urlencoded") == 0) {
|
|
|
|
const CStringList &FormData = caServerRequest.FormData;
|
|
|
|
const auto &formType = FormData["type"];
|
|
const auto &formAt = FormData["at"];
|
|
const auto &formDate = FormData["date"];
|
|
const auto &formSalt = FormData["salt"];
|
|
const auto &formSellerAddress = FormData["seller_address"];
|
|
const auto &formSellerRating = FormData["seller_rating"];
|
|
const auto &formSellerSignature = FormData["seller_signature"];
|
|
const auto &formCustomerAddress = FormData["customer_address"];
|
|
const auto &formCustomerRating = FormData["customer_rating"];
|
|
const auto &formCustomerSignature = FormData["customer_signature"];
|
|
const auto &formPaymentAddress = FormData["payment_address"];
|
|
const auto &formPaymentUntil = FormData["payment_until"];
|
|
const auto &formPaymentSum = FormData["payment_sum"];
|
|
const auto &formFeedbackLeaveBefore = FormData["feedback_leave_before"];
|
|
const auto &formFeedbackStatus = FormData["feedback_status"];
|
|
const auto &formFeedbackComments = FormData["feedback_comments"];
|
|
|
|
CheckKeyForNull("order", Action.c_str());
|
|
CheckKeyForNull("type", formType.c_str());
|
|
CheckKeyForNull("at", formAt.c_str());
|
|
CheckKeyForNull("seller_address", formSellerAddress.c_str());
|
|
CheckKeyForNull("customer_address", formCustomerAddress.c_str());
|
|
CheckKeyForNull("payment_sum", formPaymentSum.c_str());
|
|
|
|
if (Action == "cancel") {
|
|
CheckKeyForNull("seller_signature", formSellerSignature.c_str());
|
|
}
|
|
|
|
if (Action == "feedback") {
|
|
CheckKeyForNull("customer_signature", formCustomerSignature.c_str());
|
|
}
|
|
|
|
YAML::Node Deal = Node["deal"];
|
|
|
|
Deal["order"] = Action.c_str();
|
|
Deal["type"] = formType.c_str();
|
|
|
|
Deal["at"] = formAt.c_str();
|
|
Deal["date"] = formDate.IsEmpty() ? UTCFormat(DateToString(utc)).c_str() : formDate.c_str();
|
|
Deal["salt"] = formSalt.IsEmpty() ? GenSalt().c_str() : formSalt.c_str();
|
|
|
|
YAML::Node Seller = Deal["seller"];
|
|
|
|
Seller["address"] = formSellerAddress.c_str();
|
|
|
|
if (!formSellerRating.IsEmpty())
|
|
Seller["rating"] = formSellerRating.c_str();
|
|
|
|
if (!formSellerSignature.IsEmpty())
|
|
Seller["signature"] = formSellerSignature.c_str();
|
|
|
|
YAML::Node Customer = Deal["customer"];
|
|
|
|
Customer["address"] = formCustomerAddress.c_str();
|
|
|
|
if (!formCustomerRating.IsEmpty())
|
|
Customer["rating"] = formCustomerRating.c_str();
|
|
|
|
if (!formCustomerSignature.IsEmpty())
|
|
Customer["signature"] = formCustomerSignature.c_str();
|
|
|
|
YAML::Node Payment = Deal["payment"];
|
|
|
|
if (!formPaymentAddress.IsEmpty())
|
|
Payment["address"] = formPaymentAddress.c_str();
|
|
|
|
if (!formPaymentUntil.IsEmpty())
|
|
Payment["until"] = formPaymentUntil.c_str();
|
|
|
|
Payment["sum"] = formPaymentSum.c_str();
|
|
|
|
if (!formFeedbackLeaveBefore.IsEmpty()) {
|
|
YAML::Node Feedback = Deal["feedback"];
|
|
|
|
Feedback["leave-before"] = formFeedbackLeaveBefore.c_str();
|
|
|
|
if (!formFeedbackStatus.IsEmpty())
|
|
Feedback["status"] = formFeedbackStatus.c_str();
|
|
|
|
if (!formFeedbackComments.IsEmpty())
|
|
Feedback["comments"] = formFeedbackComments.c_str();
|
|
}
|
|
|
|
} else if (ContentType.Find("multipart/form-data") == 0) {
|
|
|
|
CFormData FormData;
|
|
CHTTPRequestParser::ParseFormData(caServerRequest, FormData);
|
|
|
|
const auto &formType = FormData.Data("type");
|
|
const auto &formAt = FormData.Data("at");
|
|
const auto &formDate = FormData.Data("date");
|
|
const auto &formSalt = FormData.Data("salt");
|
|
const auto &formSellerAddress = FormData.Data("seller_address");
|
|
const auto &formSellerRating = FormData.Data("seller_rating");
|
|
const auto &formSellerSignature = FormData.Data("seller_signature");
|
|
const auto &formCustomerAddress = FormData.Data("customer_address");
|
|
const auto &formCustomerRating = FormData.Data("customer_rating");
|
|
const auto &formCustomerSignature = FormData.Data("customer_signature");
|
|
const auto &formPaymentAddress = FormData.Data("payment_address");
|
|
const auto &formPaymentUntil = FormData.Data("payment_until");
|
|
const auto &formPaymentSum = FormData.Data("payment_sum");
|
|
const auto &formFeedbackLeaveBefore = FormData.Data("feedback_leave_before");
|
|
const auto &formFeedbackStatus = FormData.Data("feedback_status");
|
|
const auto &formFeedbackComments = FormData.Data("feedback_comments");
|
|
|
|
CheckKeyForNull("order", Action.c_str());
|
|
CheckKeyForNull("type", formType.c_str());
|
|
CheckKeyForNull("at", formAt.c_str());
|
|
CheckKeyForNull("seller_address", formSellerAddress.c_str());
|
|
CheckKeyForNull("customer_address", formCustomerAddress.c_str());
|
|
CheckKeyForNull("payment_sum", formPaymentSum.c_str());
|
|
|
|
if (Action == "cancel") {
|
|
CheckKeyForNull("seller_signature", formSellerSignature.c_str());
|
|
}
|
|
|
|
if (Action == "feedback") {
|
|
CheckKeyForNull("customer_signature", formCustomerSignature.c_str());
|
|
}
|
|
|
|
YAML::Node Deal = Node["deal"];
|
|
|
|
Deal["order"] = Action.c_str();
|
|
Deal["type"] = formType.c_str();
|
|
|
|
Deal["at"] = formAt.c_str();
|
|
Deal["date"] = formDate.IsEmpty() ? UTCFormat(DateToString(utc)).c_str() : formDate.c_str();
|
|
Deal["salt"] = formSalt.IsEmpty() ? GenSalt().c_str() : formSalt.c_str();
|
|
|
|
YAML::Node Seller = Deal["seller"];
|
|
|
|
Seller["address"] = formSellerAddress.c_str();
|
|
|
|
if (!formSellerRating.IsEmpty())
|
|
Seller["rating"] = formSellerRating.c_str();
|
|
|
|
if (!formSellerSignature.IsEmpty())
|
|
Seller["signature"] = formSellerSignature.c_str();
|
|
|
|
YAML::Node Customer = Deal["customer"];
|
|
|
|
Customer["address"] = formCustomerAddress.c_str();
|
|
|
|
if (!formCustomerRating.IsEmpty())
|
|
Customer["rating"] = formCustomerRating.c_str();
|
|
|
|
if (!formCustomerSignature.IsEmpty())
|
|
Customer["signature"] = formCustomerSignature.c_str();
|
|
|
|
YAML::Node Payment = Deal["payment"];
|
|
|
|
if (!formPaymentAddress.IsEmpty())
|
|
Payment["address"] = formPaymentAddress.c_str();
|
|
|
|
if (!formPaymentUntil.IsEmpty())
|
|
Payment["until"] = formPaymentUntil.c_str();
|
|
|
|
Payment["sum"] = formPaymentSum.c_str();
|
|
|
|
if (!formFeedbackLeaveBefore.IsEmpty()) {
|
|
YAML::Node Feedback = Deal["feedback"];
|
|
|
|
Feedback["leave-before"] = formFeedbackLeaveBefore.c_str();
|
|
|
|
if (!formFeedbackStatus.IsEmpty())
|
|
Feedback["status"] = formFeedbackStatus.c_str();
|
|
|
|
if (!formFeedbackComments.IsEmpty())
|
|
Feedback["comments"] = formFeedbackComments.c_str();
|
|
}
|
|
|
|
} else if (ContentType.Find("application/json") == 0) {
|
|
|
|
const CJSON jsonData(caServerRequest.Content);
|
|
|
|
const auto &formOrder = jsonData["order"].AsString().Lower();
|
|
const auto &formType = jsonData["type"].AsString();
|
|
|
|
const auto &formAt = jsonData["at"].AsString();
|
|
const auto &formDate = jsonData["date"].AsString();
|
|
const auto &formSalt = jsonData["salt"].AsString();
|
|
|
|
const CJSONValue &jsonSeller = jsonData["seller"];
|
|
|
|
const auto &formSellerAddress = jsonSeller["address"].AsString();
|
|
const auto &formSellerRating = jsonSeller["rating"].AsString();
|
|
const auto &formSellerSignature = jsonSeller["signature"].AsString();
|
|
|
|
const CJSONValue &jsonCustomer = jsonData["customer"];
|
|
|
|
const auto &formCustomerAddress = jsonCustomer["address"].AsString();
|
|
const auto &formCustomerRating = jsonCustomer["rating"].AsString();
|
|
const auto &formCustomerSignature = jsonCustomer["signature"].AsString();
|
|
|
|
const CJSONValue &jsonPayment = jsonData["payment"];
|
|
|
|
const auto &formPaymentAddress = jsonPayment["address"].AsString();
|
|
const auto &formPaymentUntil = jsonPayment["until"].AsString();
|
|
const auto &formPaymentSum = jsonPayment["sum"].AsString();
|
|
|
|
const CJSONValue &jsonFeedback = jsonData["feedback"];
|
|
|
|
CString Key("leave_before");
|
|
if (!jsonFeedback.HasOwnProperty(Key))
|
|
Key = "leave-before";
|
|
|
|
const auto &formFeedbackLeaveBefore = jsonFeedback[Key].AsString();
|
|
const auto &formFeedbackStatus = jsonFeedback["status"].AsString();
|
|
const auto &formFeedbackComments = jsonFeedback["comments"].AsString();
|
|
|
|
const auto &action = Action.IsEmpty() ? formOrder : Action;
|
|
|
|
CheckKeyForNull("order", action.c_str());
|
|
CheckKeyForNull("type", formType.c_str());
|
|
CheckKeyForNull("at", formAt.c_str());
|
|
CheckKeyForNull("seller.address", formSellerAddress.c_str());
|
|
CheckKeyForNull("customer.address", formCustomerAddress.c_str());
|
|
CheckKeyForNull("payment.sum", formPaymentSum.c_str());
|
|
|
|
if (action == "cancel") {
|
|
CheckKeyForNull("seller.signature", formSellerSignature.c_str());
|
|
}
|
|
|
|
if (action == "feedback") {
|
|
CheckKeyForNull("customer.signature", formCustomerSignature.c_str());
|
|
}
|
|
|
|
YAML::Node Deal = Node["deal"];
|
|
|
|
Deal["order"] = action.c_str();
|
|
Deal["type"] = formType.c_str();
|
|
|
|
Deal["at"] = formAt.c_str();
|
|
Deal["date"] = formDate.IsEmpty() ? UTCFormat(DateToString(utc)).c_str() : formDate.c_str();
|
|
Deal["salt"] = formSalt.IsEmpty() ? GenSalt().c_str() : formSalt.c_str();
|
|
|
|
YAML::Node Seller = Deal["seller"];
|
|
|
|
Seller["address"] = formSellerAddress.c_str();
|
|
|
|
if (!formSellerRating.IsEmpty())
|
|
Seller["rating"] = formSellerRating.c_str();
|
|
|
|
if (!formSellerSignature.IsEmpty())
|
|
Seller["signature"] = formSellerSignature.c_str();
|
|
|
|
YAML::Node Customer = Deal["customer"];
|
|
|
|
Customer["address"] = formCustomerAddress.c_str();
|
|
|
|
if (!formCustomerRating.IsEmpty())
|
|
Customer["rating"] = formCustomerRating.c_str();
|
|
|
|
if (!formCustomerSignature.IsEmpty())
|
|
Customer["signature"] = formCustomerSignature.c_str();
|
|
|
|
YAML::Node Payment = Deal["payment"];
|
|
|
|
if (!formPaymentAddress.IsEmpty())
|
|
Payment["address"] = formPaymentAddress.c_str();
|
|
|
|
if (!formPaymentUntil.IsEmpty())
|
|
Payment["until"] = formPaymentUntil.c_str();
|
|
|
|
Payment["sum"] = formPaymentSum.c_str();
|
|
|
|
if (!formFeedbackLeaveBefore.IsEmpty()) {
|
|
YAML::Node Feedback = Deal["feedback"];
|
|
|
|
Feedback["leave-before"] = formFeedbackLeaveBefore.c_str();
|
|
|
|
if (!formFeedbackStatus.IsEmpty())
|
|
Feedback["status"] = formFeedbackStatus.c_str();
|
|
|
|
if (!formFeedbackComments.IsEmpty())
|
|
Feedback["comments"] = formFeedbackComments.c_str();
|
|
}
|
|
|
|
} else {
|
|
Node = YAML::Load(caServerRequest.Content.c_str());
|
|
}
|
|
|
|
const auto &BTCKeys = caContext.BTCKeys();
|
|
|
|
if (BTCKeys.Count() < 2)
|
|
throw ExceptionFrm("Bitcoin keys cannot be empty.");
|
|
|
|
for (int i = 0; i < BTCKeys.Count(); ++i) {
|
|
if (BTCKeys[i].IsEmpty())
|
|
throw ExceptionFrm("Bitcoin KEY%d cannot be empty.", i);
|
|
}
|
|
|
|
CDeal Deal(Node);
|
|
|
|
auto& Data = Deal.Data();
|
|
|
|
if (Data.Order == doCreate) {
|
|
const auto isSegWit = IsSegWitAddress(Data.Seller.Address) && IsSegWitAddress(Data.Customer.Address);
|
|
|
|
Data.Payment.Address = isSegWit ?
|
|
Deal.GetPaymentSW(BTCKeys.Names(0), BTCKeys.Names(1), Deal.Data().Transaction.Key, BitcoinConfig.version_hd) :
|
|
Deal.GetPaymentHD(BTCKeys.Names(0), BTCKeys.Names(1), Deal.Data().Transaction.Key, BitcoinConfig.version_hd, BitcoinConfig.version_script);
|
|
|
|
Node["deal"]["date"] = Data.Date.c_str();
|
|
|
|
YAML::Node Payment = Node["deal"]["payment"];
|
|
Payment.remove("sum");
|
|
|
|
Payment["address"] = Data.Payment.Address.c_str();
|
|
Payment["until"] = Data.Payment.Until.c_str();
|
|
Payment["sum"] = Data.Payment.Sum.c_str();
|
|
|
|
Node["deal"]["feedback"]["leave-before"] = Data.FeedBack.LeaveBefore.c_str();
|
|
}
|
|
|
|
CheckDeal(Deal);
|
|
|
|
const CString caClearText(YAML::Dump(Node));
|
|
|
|
if (pgpValue == "off" || pgpValue == "false") {
|
|
sPayload = caClearText;
|
|
} else {
|
|
Apostol::PGP::CleartextSignature(
|
|
m_pgpModuleKey,
|
|
m_pgpPassphrase,
|
|
BPS_PGP_HASH,
|
|
caClearText,
|
|
sPayload);
|
|
}
|
|
}
|
|
|
|
CJSON Json(jvtObject);
|
|
CJSONValue Module(jvtObject);
|
|
|
|
Module.Object().AddPair("address", caModuleAddress);
|
|
Module.Object().AddPair("fee", caModuleFee);
|
|
|
|
Json.Object().AddPair("id", GetUID(16).Lower());
|
|
Json.Object().AddPair("host", caHost);
|
|
|
|
if (!caOrigin.IsEmpty()) {
|
|
Json.Object().AddPair("origin", caOrigin);
|
|
}
|
|
|
|
Json.Object().AddPair("module", Module);
|
|
|
|
if (!caDealCode.IsEmpty()) {
|
|
CJSONValue Deal(jvtObject);
|
|
Deal.Object().AddPair("code", caDealCode);
|
|
Json.Object().AddPair("deal", Deal);
|
|
} else {
|
|
Json.Object().AddPair("address", caUserAddress.IsEmpty() ? caModuleAddress : caUserAddress);
|
|
}
|
|
|
|
if (!sPayload.IsEmpty())
|
|
Json.Object().AddPair("payload", base64_encode(sPayload));
|
|
|
|
auto pClient = GetConnectedClient(caContext);
|
|
|
|
if (pClient == nullptr) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
pClient->Send(URI, Json, OnRequest);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoProxy(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI) {
|
|
|
|
auto OnRequest = [AConnection](CWebSocketMessageHandler *AHandler, CWebSocketConnection *AWSConnection) {
|
|
auto &Reply = AConnection->Reply();
|
|
const auto &wsMessage = CCustomWebSocketClient::RequestToMessage(AWSConnection);
|
|
Reply.ContentType = CHTTPReply::json;
|
|
if (wsMessage.MessageTypeId == mtCallResult) {
|
|
Reply.Content = wsMessage.Payload.ToString();
|
|
AConnection->SendReply(CHTTPReply::ok, nullptr, true);
|
|
} else {
|
|
ReplyError(AConnection, CHTTPReply::bad_request, wsMessage.ErrorMessage);
|
|
}
|
|
};
|
|
|
|
const auto &caServerRequest = AConnection->Request();
|
|
|
|
const auto &caModuleAddress = m_Module["address"];
|
|
const auto &caModuleFee = m_Module["fee"];
|
|
|
|
const auto checkFee = CheckFee(caModuleFee);
|
|
if (checkFee == -1)
|
|
throw ExceptionFrm("Invalid module fee value: %s", caModuleFee.c_str());
|
|
|
|
const auto &caHost = caServerRequest.Headers["host"];
|
|
const auto &caOrigin = caServerRequest.Headers["origin"];
|
|
|
|
const auto &address = caServerRequest.Params["address"];
|
|
const auto &code = caServerRequest.Params["code"];
|
|
|
|
const auto &caUserAddress = address.length() == 40 ? CString() : address;
|
|
const auto &caDealCode = !code.empty() ? code : address.length() == 40 ? address : CString();
|
|
|
|
const auto &pgpValue = caServerRequest.Params["pgp"];
|
|
const auto &caServerParam = caServerRequest.Params["server"];
|
|
|
|
const auto index = CurrentContextIndex(caServerParam);
|
|
if (index == -1) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
const auto &caContext = m_Servers[index].Value();
|
|
|
|
auto pClient = GetConnectedClient(caContext);
|
|
|
|
if (pClient == nullptr) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
const CJSON Json(caServerRequest.Content);
|
|
|
|
pClient->Send(URI, Json, OnRequest);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::DoSignature(CHTTPServerConnection *AConnection) {
|
|
const auto &caRequest = AConnection->Request();
|
|
auto &Reply = AConnection->Reply();
|
|
|
|
if (caRequest.Content.IsEmpty()) {
|
|
AConnection->SendStockReply(CHTTPReply::no_content);
|
|
return;
|
|
}
|
|
|
|
const auto &caServerParam = caRequest.Params["server"];
|
|
|
|
int index = CurrentContextIndex(caServerParam);
|
|
if (index == -1) {
|
|
throw Delphi::Exception::Exception(NOT_FOUND_ACTIVE_CONNECTION);
|
|
}
|
|
|
|
const auto &caContext = m_Servers[index].Value();
|
|
auto &Keys = caContext.PGP();
|
|
|
|
index = 0;
|
|
while (index < Keys.Count() && Keys[index].Name != caContext.Name()) {
|
|
index++;
|
|
}
|
|
|
|
if (index == Keys.Count())
|
|
throw ExceptionFrm("PGP key not found.");
|
|
|
|
const auto& caServerKey = Keys[index].Key;
|
|
|
|
if (caServerKey.IsEmpty())
|
|
throw ExceptionFrm("Server PGP key not added.");
|
|
|
|
CString message;
|
|
CJSON Json(jvtObject);
|
|
|
|
const auto& caContentType = caRequest.Headers["content-type"];
|
|
|
|
if (caContentType.Find("application/x-www-form-urlencoded") == 0) {
|
|
const CStringList &FormData = caRequest.FormData;
|
|
|
|
const auto& caClearText = FormData["message"];
|
|
CheckKeyForNull("message", caClearText.c_str());
|
|
|
|
const auto bVerified = CheckVerifyPGPSignature(VerifyPGPSignature(caClearText, caServerKey, message), message);
|
|
Json.Object().AddPair("verified", bVerified);
|
|
} else if (caContentType.Find("multipart/form-data") == 0) {
|
|
CFormData FormData;
|
|
CHTTPRequestParser::ParseFormData(caRequest, FormData);
|
|
|
|
const auto& caClearText = FormData.Data("message");
|
|
CheckKeyForNull("message", caClearText.c_str());
|
|
|
|
const auto bVerified = CheckVerifyPGPSignature(VerifyPGPSignature(caClearText, caServerKey, message), message);
|
|
Json.Object().AddPair("verified", bVerified);
|
|
} else if (caContentType.Find("application/json") == 0) {
|
|
const CJSON jsonData(caRequest.Content);
|
|
|
|
const auto& caClearText = jsonData["message"].AsString();
|
|
CheckKeyForNull("message", caClearText.c_str());
|
|
|
|
const auto bVerified = CheckVerifyPGPSignature(VerifyPGPSignature(caClearText, caServerKey, message), message);
|
|
Json.Object().AddPair("verified", bVerified);
|
|
} else {
|
|
const auto& caClearText = caRequest.Content;
|
|
const auto bVerified = CheckVerifyPGPSignature(VerifyPGPSignature(caClearText, caServerKey, message), message);
|
|
Json.Object().AddPair("verified", bVerified);
|
|
}
|
|
|
|
Json.Object().AddPair("message", message);
|
|
|
|
AConnection->CloseConnection(true);
|
|
Reply.Content << Json;
|
|
|
|
AConnection->SendReply(CHTTPReply::ok);
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::Heartbeat(CDateTime Now) {
|
|
for (int i = 0; i < m_Servers.Count(); i++) {
|
|
auto &Context = m_Servers[i].Value();
|
|
|
|
if (Now >= Context.CheckDate()) {
|
|
Context.SetCheckDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
|
|
|
|
if (Context.Status() == Context::csInitialization) {
|
|
ModuleService(Context);
|
|
}
|
|
|
|
if (Context.Status() >= Context::csInitialized) {
|
|
CheckProviders(Context, Now);
|
|
}
|
|
}
|
|
|
|
if (Now >= Context.FixedDate()) {
|
|
Context.SetFixedDate(Now + (CDateTime) 30 / SecsPerDay); // 30 sec
|
|
|
|
if (Context.Status() == Context::csAuthorized) {
|
|
CreateWebSocketClient(Context);
|
|
}
|
|
|
|
if (Context.Status() == Context::csRunning) {
|
|
for (int j = 0; j < Context.ClientManager().Count(); ++j) {
|
|
const auto pClient = Context.ClientManager()[j];
|
|
|
|
if (!pClient->Active())
|
|
pClient->Active(true);
|
|
|
|
if (!pClient->Connected() && !pClient->Session().IsEmpty()) {
|
|
Log()->Notice(_T("[%s] [%s] [%s] Trying to establish a connection."),
|
|
Context.Name().c_str(),
|
|
Context.URL().Origin().c_str(),
|
|
pClient->Session().c_str());
|
|
pClient->ConnectStart();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
void CWebSocketModule::Reload() {
|
|
Config()->IniFile().ReadSectionValues("module", &m_Module);
|
|
|
|
CString FileName;
|
|
|
|
FileName = Config()->IniFile().ReadString(CONFIG_SECTION_NAME, "oauth2", "oauth2/service.json");
|
|
if (!path_separator(FileName.front())) {
|
|
FileName = Config()->Prefix() + FileName;
|
|
}
|
|
|
|
if (FileExists(FileName.c_str())) {
|
|
m_OAuth2.LoadFromFile(FileName.c_str());
|
|
}
|
|
|
|
const auto &caPrivateKey = Config()->IniFile().ReadString("pgp", "private", "module.sec");
|
|
const auto &caPublicKey = Config()->IniFile().ReadString("pgp", "public", "dm.pub");
|
|
|
|
m_pgpPassphrase = Config()->IniFile().ReadString("pgp", "passphrase", "");
|
|
|
|
if (FileExists(caPrivateKey.c_str())) {
|
|
m_pgpModuleKey.LoadFromFile(caPrivateKey.c_str());
|
|
|
|
if (FileExists(caPublicKey.c_str())) {
|
|
m_pgpPublicKey.LoadFromFile(caPublicKey.c_str());
|
|
UpdateServerList(m_Servers, m_pgpPublicKey);
|
|
} else {
|
|
Log()->Error(APP_LOG_WARN, 0, APP_FILE_NOT_FOUND, caPublicKey.c_str());
|
|
}
|
|
} else {
|
|
Log()->Error(APP_LOG_WARN, 0, APP_FILE_NOT_FOUND, caPrivateKey.c_str());
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
}
|
|
}
|
|
} |