Code refactoring.
This commit is contained in:
990
src/app/Common.cpp
Normal file
990
src/app/Common.cpp
Normal file
@@ -0,0 +1,990 @@
|
|||||||
|
/*++
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
Common.cpp
|
||||||
|
|
||||||
|
Notices:
|
||||||
|
|
||||||
|
Dial Module common.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Copyright (c) Prepodobny Alen
|
||||||
|
|
||||||
|
mailto: alienufo@inbox.ru
|
||||||
|
mailto: ufocomp@gmail.com
|
||||||
|
|
||||||
|
--*/
|
||||||
|
|
||||||
|
#include "Core.hpp"
|
||||||
|
#include "Common.hpp"
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C++" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
namespace Apostol {
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-- CCustomModule ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CCustomModule::CCustomModule(CModuleProcess *AProcess, const CString& ModuleName, const CString& SectionName):
|
||||||
|
CApostolModule(AProcess, ModuleName, SectionName) {
|
||||||
|
|
||||||
|
m_SyncPeriod = BPS_DEFAULT_SYNC_PERIOD;
|
||||||
|
m_Status = psStopped;
|
||||||
|
|
||||||
|
CCustomModule::InitMethods();
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::InitMethods() {
|
||||||
|
#if defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 9)
|
||||||
|
m_pMethods->AddObject(_T("GET") , (CObject *) new CMethodHandler(true , [this](auto && Connection) { DoGet(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("POST") , (CObject *) new CMethodHandler(true , [this](auto && Connection) { DoPost(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("HEAD") , (CObject *) new CMethodHandler(true , [this](auto && Connection) { DoHead(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("OPTIONS"), (CObject *) new CMethodHandler(true , [this](auto && Connection) { DoOptions(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("PUT") , (CObject *) new CMethodHandler(false, [this](auto && Connection) { MethodNotAllowed(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("DELETE") , (CObject *) new CMethodHandler(false, [this](auto && Connection) { MethodNotAllowed(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("TRACE") , (CObject *) new CMethodHandler(false, [this](auto && Connection) { MethodNotAllowed(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("PATCH") , (CObject *) new CMethodHandler(false, [this](auto && Connection) { MethodNotAllowed(Connection); }));
|
||||||
|
m_pMethods->AddObject(_T("CONNECT"), (CObject *) new CMethodHandler(false, [this](auto && Connection) { MethodNotAllowed(Connection); }));
|
||||||
|
#else
|
||||||
|
m_pMethods->AddObject(_T("GET"), (CObject *) new CMethodHandler(true, std::bind(&CCustomModule::DoGet, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("POST"), (CObject *) new CMethodHandler(true, std::bind(&CCustomModule::DoPost, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("HEAD"), (CObject *) new CMethodHandler(true, std::bind(&CCustomModule::DoHead, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("OPTIONS"), (CObject *) new CMethodHandler(true, std::bind(&CCustomModule::DoOptions, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("PUT"), (CObject *) new CMethodHandler(false, std::bind(&CCustomModule::MethodNotAllowed, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("DELETE"), (CObject *) new CMethodHandler(false, std::bind(&CCustomModule::MethodNotAllowed, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("TRACE"), (CObject *) new CMethodHandler(false, std::bind(&CCustomModule::MethodNotAllowed, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("PATCH"), (CObject *) new CMethodHandler(false, std::bind(&CCustomModule::MethodNotAllowed, this, _1)));
|
||||||
|
m_pMethods->AddObject(_T("CONNECT"), (CObject *) new CMethodHandler(false, std::bind(&CCustomModule::MethodNotAllowed, this, _1)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::Initialization(CModuleProcess *AProcess) {
|
||||||
|
CApostolModule::Initialization(AProcess);
|
||||||
|
|
||||||
|
if (Enabled()) {
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool CCustomModule::Enabled() {
|
||||||
|
if (m_ModuleStatus == msUnknown)
|
||||||
|
m_ModuleStatus = Config()->IniFile().ReadBool(SectionName().c_str(), "enable", true) ? msEnabled : msDisabled;
|
||||||
|
return m_ModuleStatus == msEnabled;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CString CCustomModule::ToString(unsigned long Value) {
|
||||||
|
TCHAR szString[_INT_T_LEN + 1] = {0};
|
||||||
|
sprintf(szString, "%lu", Value);
|
||||||
|
return { szString };
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::JsonStringToKey(const CString &String, CString& Key) {
|
||||||
|
const CJSON Json(String);
|
||||||
|
|
||||||
|
if (Json.HasOwnProperty("error")) {
|
||||||
|
const auto &error = Json["error"];
|
||||||
|
if (error.ValueType() == jvtObject)
|
||||||
|
throw Delphi::Exception::Exception(error["message"].AsString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Json.HasOwnProperty("data")) {
|
||||||
|
Key = Json["data"].AsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::CheckKeyForNull(LPCTSTR Key, LPCTSTR Value) {
|
||||||
|
if (Value == nullptr)
|
||||||
|
throw ExceptionFrm("Invalid format: key \"%s\" cannot be empty.", Key);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool CCustomModule::FindURLInLine(const CString &Line, CStringList &List) {
|
||||||
|
CString URL;
|
||||||
|
|
||||||
|
TCHAR ch;
|
||||||
|
int length = 0;
|
||||||
|
size_t startPos, pos;
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
while ((startPos = Line.Find(HTTP_PREFIX, pos)) != CString::npos) {
|
||||||
|
|
||||||
|
URL.Clear();
|
||||||
|
|
||||||
|
pos = startPos + HTTP_PREFIX_SIZE;
|
||||||
|
if (Line.Length() < 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
URL.Append(HTTP_PREFIX);
|
||||||
|
ch = Line.at(pos);
|
||||||
|
if (ch == 's') {
|
||||||
|
URL.Append(ch);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Line.Length() < 7 || Line.at(pos++) != ':' || Line.at(pos++) != '/' || Line.at(pos++) != '/')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
URL.Append("://");
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
ch = Line.at(pos);
|
||||||
|
while (ch != 0 && (IsChar(ch) || IsNumeral(ch) || ch == ':' || ch == '.' || ch == '-')) {
|
||||||
|
URL.Append(ch);
|
||||||
|
length++;
|
||||||
|
ch = Line.at(++pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startPos == 0) {
|
||||||
|
List.Add(URL);
|
||||||
|
} else {
|
||||||
|
ch = Line.at(startPos - 1);
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
case ',':
|
||||||
|
case ';':
|
||||||
|
List.Add(URL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int CCustomModule::CheckFee(const CString &Fee) {
|
||||||
|
if (!Fee.IsEmpty()) {
|
||||||
|
|
||||||
|
if (Fee.Length() >= 10)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t numbers = 0;
|
||||||
|
size_t delimiter = 0;
|
||||||
|
size_t percent = 0;
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
TCHAR ch;
|
||||||
|
|
||||||
|
ch = Fee.at(pos);
|
||||||
|
while (ch != 0) {
|
||||||
|
if (IsNumeral(ch))
|
||||||
|
numbers++;
|
||||||
|
if (ch == '.')
|
||||||
|
delimiter++;
|
||||||
|
if (ch == '%')
|
||||||
|
percent++;
|
||||||
|
ch = Fee.at(++pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numbers == 0 || delimiter > 1 || percent > 1 || ((numbers + percent + delimiter) != Fee.Length()))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool CCustomModule::CheckVerifyPGPSignature(int VerifyPGPSignature, CString &Message) {
|
||||||
|
if (VerifyPGPSignature == -2) {
|
||||||
|
Message = "PGP public key is not meaningful.";
|
||||||
|
} else if (VerifyPGPSignature == -1) {
|
||||||
|
Message = "PGP signature not valid.";
|
||||||
|
} else if (VerifyPGPSignature == 0) {
|
||||||
|
Message = "PGP signature not found.";
|
||||||
|
} else if (VerifyPGPSignature > 1) {
|
||||||
|
Message = "PGP signature status: Unknown.";
|
||||||
|
}
|
||||||
|
return VerifyPGPSignature == 1;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int CCustomModule::VerifyPGPSignature(const CString &ClearText, const CString &Key, CString &Message) {
|
||||||
|
const OpenPGP::Key signer(Key.c_str());
|
||||||
|
const OpenPGP::CleartextSignature cleartext(ClearText.c_str());
|
||||||
|
|
||||||
|
if (!cleartext.meaningful())
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
const int verified = OpenPGP::Verify::cleartext_signature(signer, cleartext);
|
||||||
|
|
||||||
|
if (verified == 1) {
|
||||||
|
Message = cleartext.get_message().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::ParsePGPKey(const CString &Key, CStringPairs& ServerList, CStringList& BTCKeys) {
|
||||||
|
if (Key.IsEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Apostol::PGP::Key pgp(Key.c_str());
|
||||||
|
if (!pgp.meaningful())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CStringList Data;
|
||||||
|
CPGPUserIdList List;
|
||||||
|
CStringList KeyList;
|
||||||
|
|
||||||
|
pgp.ExportUID(List);
|
||||||
|
|
||||||
|
for (int i = 0; i < List.Count(); i++) {
|
||||||
|
|
||||||
|
const auto &uid = List[i];
|
||||||
|
|
||||||
|
DebugMessage("%s (%s)\n", uid.Name.c_str(), uid.Desc.c_str());
|
||||||
|
|
||||||
|
const auto &name = uid.Name.Lower();
|
||||||
|
const auto &data = uid.Desc.Lower();
|
||||||
|
|
||||||
|
if (name == "technical_data") {
|
||||||
|
SplitColumns(data, Data, ';');
|
||||||
|
|
||||||
|
if (Data.Count() == 3) {
|
||||||
|
m_SyncPeriod = StrToIntDef(Data[1].Trim().c_str(), BPS_DEFAULT_SYNC_PERIOD);
|
||||||
|
} else if (Data.Count() == 2) {
|
||||||
|
m_SyncPeriod = StrToIntDef(Data[0].Trim().c_str(), BPS_DEFAULT_SYNC_PERIOD);
|
||||||
|
} else if (Data.Count() == 1) {
|
||||||
|
m_SyncPeriod = StrToIntDef(Data[0].Trim().c_str(), BPS_DEFAULT_SYNC_PERIOD);
|
||||||
|
}
|
||||||
|
} if (uid.Name.Length() >= 35 && uid.Name.SubString(0, 3) == BM_PREFIX) {
|
||||||
|
CStringList urlList;
|
||||||
|
if (FindURLInLine(uid.Desc, urlList)) {
|
||||||
|
for (int l = 0; l < urlList.Count(); l++) {
|
||||||
|
ServerList.AddPair(uid.Name, urlList[l]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (name.Find("bitcoin_key") != CString::npos) {
|
||||||
|
const auto &key = wallet::ec_public(data.c_str());
|
||||||
|
if (verify(key))
|
||||||
|
KeyList.AddPair(name, key.encoded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CString Name;
|
||||||
|
for (int i = 1; i <= KeyList.Count(); i++) {
|
||||||
|
Name = "bitcoin_key";
|
||||||
|
Name << i;
|
||||||
|
const auto &key = KeyList[Name];
|
||||||
|
if (!key.IsEmpty()) {
|
||||||
|
BTCKeys.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2) {
|
||||||
|
if (OAuth2["type"].AsString() == "service_account") {
|
||||||
|
UpdateProviders(Context.Providers(), OAuth2);
|
||||||
|
|
||||||
|
Context.SetCheckDate(0);
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::UpdateProviders(CProviders &Providers, const CJSONObject &Data) {
|
||||||
|
const auto &caProviderName = CString(SYSTEM_PROVIDER_NAME);
|
||||||
|
const auto &caApplicationName = CString(SERVICE_APPLICATION_NAME);
|
||||||
|
|
||||||
|
int index = Providers.IndexOfName(caProviderName);
|
||||||
|
if (index == -1)
|
||||||
|
index = Providers.AddPair(caProviderName, CProvider(caProviderName));
|
||||||
|
auto& Provider = Providers[index].Value();
|
||||||
|
Provider.Applications().AddPair(caApplicationName, Data);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::CreateAccessToken(const CProvider &Provider, const CString &Application, CContext &Context) {
|
||||||
|
|
||||||
|
auto OnDone = [this, &Context](CTCPConnection *Sender) {
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (Sender);
|
||||||
|
auto pReply = pConnection->Reply();
|
||||||
|
|
||||||
|
DebugReply(pReply);
|
||||||
|
|
||||||
|
if (pReply->Status == CHTTPReply::ok) {
|
||||||
|
const CJSON Json(pReply->Content);
|
||||||
|
|
||||||
|
Context.Session() = Json["session"].AsString();
|
||||||
|
Context.Secret() = Json["secret"].AsString();
|
||||||
|
|
||||||
|
Context.Tokens().Values("access_token", Json["access_token"].AsString());
|
||||||
|
|
||||||
|
ModuleStatus(Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnFail = [&Context](CTCPConnection *Sender, const Delphi::Exception::Exception &E) {
|
||||||
|
Context.SetStatus(csInitialized);
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (Sender);
|
||||||
|
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
|
||||||
|
|
||||||
|
DebugReply(pConnection->Reply());
|
||||||
|
|
||||||
|
Log()->Error(APP_LOG_ERR, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnHTTPClient = [this](const CLocation &URI) {
|
||||||
|
return GetClient(URI.hostname, URI.port);
|
||||||
|
};
|
||||||
|
|
||||||
|
CString server_uri(Context.URL().Origin());
|
||||||
|
|
||||||
|
const auto &token_uri = Provider.TokenURI(Application);
|
||||||
|
const auto &service_token = CToken::CreateToken(Provider, Application);
|
||||||
|
|
||||||
|
Context.Tokens().Values("service_token", service_token);
|
||||||
|
|
||||||
|
if (!token_uri.IsEmpty()) {
|
||||||
|
CToken::FetchAccessToken(token_uri.front() == '/' ? server_uri + token_uri : token_uri, service_token, OnHTTPClient, OnDone, OnFail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::FetchProviders(CDateTime Now, CContext &Context) {
|
||||||
|
for (int i = 0; i < Context.Providers().Count(); i++) {
|
||||||
|
auto &Provider = Context.Providers()[i].Value();
|
||||||
|
for (int j = 0; j < Provider.Applications().Count(); ++j) {
|
||||||
|
const auto &app = Provider.Applications().Members(j);
|
||||||
|
if (app["type"].AsString() == "service_account") {
|
||||||
|
if (Provider.KeyStatus() == ksUnknown) {
|
||||||
|
CreateAccessToken(Provider, app.String(), Context);
|
||||||
|
Provider.KeyStatusTime(Now);
|
||||||
|
Provider.KeyStatus(ksSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::CheckProviders(CDateTime Now, CContext &Context) {
|
||||||
|
for (int i = 0; i < Context.Providers().Count(); i++) {
|
||||||
|
auto& Provider = Context.Providers()[i].Value();
|
||||||
|
if (Provider.KeyStatus() != ksUnknown) {
|
||||||
|
Provider.KeyStatusTime(Now);
|
||||||
|
Provider.KeyStatus(ksUnknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::CheckDeal(const CDeal &Deal) {
|
||||||
|
|
||||||
|
const auto &Data = Deal.Data();
|
||||||
|
|
||||||
|
const auto DateTime = UTC();
|
||||||
|
const auto Date = StringToDate(Data.Date);
|
||||||
|
const auto Sum = BTCToDouble(Data.Payment.Sum);
|
||||||
|
|
||||||
|
if (Data.Order == doCreate) {
|
||||||
|
if (DateTime < Date)
|
||||||
|
throw ExceptionFrm("Invalid deal date.");
|
||||||
|
|
||||||
|
if ((DateTime - Date) > (CDateTime) 180 / SecsPerDay)
|
||||||
|
throw ExceptionFrm("Deal date expired.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Data.Order == doComplete) {
|
||||||
|
const CDateTime LeaveBefore = StringToDate(Data.FeedBack.LeaveBefore);
|
||||||
|
if (DateTime > LeaveBefore)
|
||||||
|
throw ExceptionFrm("Date feedback expired.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Odd(int(Data.Order)) || Data.Order == doExecute || Data.Order == doDelete)
|
||||||
|
throw ExceptionFrm("Invalid \"order\" value for deal module.");
|
||||||
|
|
||||||
|
if (Data.Order == doCancel) {
|
||||||
|
const CDateTime Until = StringToDate(Data.Payment.Until);
|
||||||
|
if (DateTime > Until)
|
||||||
|
throw ExceptionFrm("Deal cancellation expired.");
|
||||||
|
|
||||||
|
CString message(Data.Payment.Address);
|
||||||
|
if (!Data.FeedBack.Comments.IsEmpty()) {
|
||||||
|
message += LINEFEED;
|
||||||
|
message += Data.FeedBack.Comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Data.Seller.Signature.IsEmpty() || !VerifyMessage(message, Data.Seller.Address, Data.Seller.Signature))
|
||||||
|
throw ExceptionFrm("The deal is not signed by the seller.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Data.Order == doFeedback) {
|
||||||
|
CString message(Data.Payment.Address);
|
||||||
|
if (!Data.FeedBack.Comments.IsEmpty()) {
|
||||||
|
message += LINEFEED;
|
||||||
|
message += Data.FeedBack.Comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Data.Customer.Signature.IsEmpty() || !VerifyMessage(message, Data.Customer.Address, Data.Customer.Signature))
|
||||||
|
throw ExceptionFrm("The deal is not signed by the customer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid_address(Data.Seller.Address))
|
||||||
|
throw ExceptionFrm("Invalid Seller address: %s.", Data.Seller.Address.c_str());
|
||||||
|
|
||||||
|
if (!valid_address(Data.Customer.Address))
|
||||||
|
throw ExceptionFrm("Invalid Customer address: %s.", Data.Customer.Address.c_str());
|
||||||
|
|
||||||
|
if (!valid_address(Data.Payment.Address))
|
||||||
|
throw ExceptionFrm("Invalid Payment address: %s.", Data.Payment.Address.c_str());
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::DoGet(CHTTPServerConnection *AConnection) {
|
||||||
|
|
||||||
|
auto pRequest = AConnection->Request();
|
||||||
|
|
||||||
|
CString sPath(pRequest->Location.pathname);
|
||||||
|
|
||||||
|
// Request sPath must be absolute and not contain "..".
|
||||||
|
if (sPath.empty() || sPath.front() != '/' || sPath.find(_T("..")) != CString::npos) {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::bad_request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sPath.SubString(0, 5) == "/api/") {
|
||||||
|
DoAPI(AConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendResource(AConnection, sPath);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::DoPost(CHTTPServerConnection *AConnection) {
|
||||||
|
|
||||||
|
auto pRequest = AConnection->Request();
|
||||||
|
auto pReply = AConnection->Reply();
|
||||||
|
|
||||||
|
pReply->ContentType = CHTTPReply::json;
|
||||||
|
|
||||||
|
CStringList slRouts;
|
||||||
|
SplitColumns(pRequest->Location.pathname, slRouts, '/');
|
||||||
|
|
||||||
|
if (slRouts.Count() < 3) {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &caService = slRouts[0].Lower();
|
||||||
|
const auto &caVersion = slRouts[1].Lower();
|
||||||
|
const auto &caCommand = slRouts[2].Lower();
|
||||||
|
const auto &caAction = slRouts.Count() == 4 ? slRouts[3].Lower() : "";
|
||||||
|
|
||||||
|
if (caService != "api") {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caVersion != "v1") {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString sRoute;
|
||||||
|
for (int i = 0; i < slRouts.Count(); ++i) {
|
||||||
|
sRoute.Append('/');
|
||||||
|
sRoute.Append(slRouts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (caCommand == "account") {
|
||||||
|
DoAccount(AConnection, "POST", sRoute);
|
||||||
|
} else if (caCommand == "deal") {
|
||||||
|
DoDeal(AConnection, "POST", sRoute, caAction);
|
||||||
|
} else if (caCommand == "signature") {
|
||||||
|
DoSignature(AConnection);
|
||||||
|
} else {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
ReplyError(AConnection, CHTTPReply::bad_request, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::DoAPI(CHTTPServerConnection *AConnection) {
|
||||||
|
|
||||||
|
auto pRequest = AConnection->Request();
|
||||||
|
auto pReply = AConnection->Reply();
|
||||||
|
|
||||||
|
pReply->ContentType = CHTTPReply::json;
|
||||||
|
|
||||||
|
CStringList slRouts;
|
||||||
|
SplitColumns(pRequest->Location.pathname, slRouts, '/');
|
||||||
|
|
||||||
|
if (slRouts.Count() < 3) {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &caService = slRouts[0].Lower();
|
||||||
|
const auto &caVersion = slRouts[1].Lower();
|
||||||
|
const auto &caCommand = slRouts[2].Lower();
|
||||||
|
const auto &caAction = slRouts.Count() == 4 ? slRouts[3].Lower() : "";
|
||||||
|
|
||||||
|
if (caService != "api") {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caVersion != "v1") {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString sRoute;
|
||||||
|
for (int i = 0; i < slRouts.Count(); ++i) {
|
||||||
|
sRoute.Append('/');
|
||||||
|
sRoute.Append(slRouts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (caCommand == "ping") {
|
||||||
|
|
||||||
|
AConnection->SendStockReply(CHTTPReply::ok);
|
||||||
|
|
||||||
|
} else if (caCommand == "time") {
|
||||||
|
|
||||||
|
pReply->Content << "{\"serverTime\": " << ToString(MsEpoch()) << "}";
|
||||||
|
|
||||||
|
AConnection->SendReply(CHTTPReply::ok);
|
||||||
|
|
||||||
|
} else if (caCommand == "help") {
|
||||||
|
|
||||||
|
pRequest->Content.Clear();
|
||||||
|
|
||||||
|
DoAccount(AConnection, "GET", sRoute);
|
||||||
|
|
||||||
|
} else if (caCommand == "account" && caAction == "status") {
|
||||||
|
|
||||||
|
pRequest->Content.Clear();
|
||||||
|
|
||||||
|
DoAccount(AConnection, "GET", sRoute);
|
||||||
|
|
||||||
|
} else if (caCommand == "deal" && caAction == "status") {
|
||||||
|
|
||||||
|
pRequest->Content.Clear();
|
||||||
|
|
||||||
|
DoDeal(AConnection, "GET", sRoute, caAction);
|
||||||
|
|
||||||
|
} else if (caCommand == "bc" && caAction == "history") {
|
||||||
|
|
||||||
|
const auto &caAccount = pRequest->Params["account"];
|
||||||
|
|
||||||
|
if (caAccount.IsEmpty()) {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::bad_request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wallet::payment_address address(std::string(caAccount.c_str()));
|
||||||
|
|
||||||
|
CJSON history;
|
||||||
|
fetch_history(address, history);
|
||||||
|
|
||||||
|
pReply->Content = history.ToString();
|
||||||
|
} catch (Delphi::Exception::Exception &E) {
|
||||||
|
ExceptionToJson(CHTTPReply::bad_request, E, pReply->Content);
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, E.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AConnection->SendReply(CHTTPReply::ok, nullptr, true);
|
||||||
|
|
||||||
|
} else if (caCommand == "bc" && caAction == "header") {
|
||||||
|
|
||||||
|
const auto &caHeight = pRequest->Params["height"];
|
||||||
|
const auto &caHash = pRequest->Params["hash"];
|
||||||
|
|
||||||
|
if (caHeight.IsEmpty() && caHash.IsEmpty()) {
|
||||||
|
AConnection->SendStockReply(CHTTPReply::bad_request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
CJSON header;
|
||||||
|
|
||||||
|
if (!caHash.IsEmpty()) {
|
||||||
|
fetch_header(hash256(std::string(caHash.c_str())), header);
|
||||||
|
} else {
|
||||||
|
uint32_t height = StrToInt(caHeight.c_str());
|
||||||
|
fetch_header(height, header);
|
||||||
|
}
|
||||||
|
|
||||||
|
pReply->Content = header.ToString();
|
||||||
|
} catch (Delphi::Exception::Exception &E) {
|
||||||
|
ExceptionToJson(CHTTPReply::bad_request, E, pReply->Content);
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, E.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
AConnection->SendReply(CHTTPReply::ok, nullptr, true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
AConnection->SendStockReply(CHTTPReply::not_found);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
ReplyError(AConnection, CHTTPReply::bad_request, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::ModuleService(CContext &Context) {
|
||||||
|
|
||||||
|
Log()->Notice("[%s] [%s] Trying to fetch a OAuth2 configuration file.", Context.Name().c_str(), Context.URL().Origin().c_str());
|
||||||
|
|
||||||
|
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csPreparing);
|
||||||
|
|
||||||
|
ARequest->ContentType = CHTTPRequest::text;
|
||||||
|
|
||||||
|
Apostol::PGP::CleartextSignature(
|
||||||
|
m_pgpModuleKey,
|
||||||
|
m_pgpPassphrase,
|
||||||
|
BPS_PGP_HASH,
|
||||||
|
Context.Name(),
|
||||||
|
ARequest->Content);
|
||||||
|
|
||||||
|
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/service");
|
||||||
|
|
||||||
|
const auto &caModuleAddress = m_Module["address"];
|
||||||
|
if (!caModuleAddress.IsEmpty())
|
||||||
|
ARequest->AddHeader("Module-Address", caModuleAddress);
|
||||||
|
|
||||||
|
DebugRequest(ARequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnExecute = [&Context](CTCPConnection *AConnection) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialization);
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pReply = pConnection->Reply();
|
||||||
|
|
||||||
|
DebugReply(pReply);
|
||||||
|
|
||||||
|
if (pReply->Status == CHTTPReply::ok) {
|
||||||
|
const CJSON OAuth2(pReply->Content);
|
||||||
|
UpdateOAuth2(Context, OAuth2.Object());
|
||||||
|
}
|
||||||
|
|
||||||
|
pConnection->CloseConnection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
|
||||||
|
if (pClient != nullptr) {
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
|
||||||
|
}
|
||||||
|
DebugReply(pConnection->Reply());
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialization);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
|
||||||
|
|
||||||
|
pClient->OnRequest(OnRequest);
|
||||||
|
pClient->OnExecute(OnExecute);
|
||||||
|
pClient->OnException(OnException);
|
||||||
|
|
||||||
|
pClient->AutoFree(true);
|
||||||
|
pClient->Active(true);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::ModuleStatus(CContext &Context) {
|
||||||
|
|
||||||
|
Log()->Notice("[%s] [%s] Trying get module status.", Context.Name().c_str(), Context.URL().Origin().c_str());
|
||||||
|
|
||||||
|
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInProgress);
|
||||||
|
|
||||||
|
const auto &caModuleAddress = m_Module["address"];
|
||||||
|
|
||||||
|
ARequest->ContentType = CHTTPRequest::text;
|
||||||
|
|
||||||
|
ARequest->Content.Format(R"({"address": "%s"})", caModuleAddress.c_str());
|
||||||
|
|
||||||
|
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/status");
|
||||||
|
|
||||||
|
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
|
||||||
|
|
||||||
|
if (!caModuleAddress.IsEmpty())
|
||||||
|
ARequest->AddHeader("Module-Address", caModuleAddress);
|
||||||
|
|
||||||
|
DebugRequest(ARequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pReply = pConnection->Reply();
|
||||||
|
|
||||||
|
DebugReply(pReply);
|
||||||
|
|
||||||
|
if (pReply->Status == CHTTPReply::ok) {
|
||||||
|
ModuleAuthorize(Context);
|
||||||
|
} else {
|
||||||
|
ModuleNew(Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pConnection->CloseConnection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
|
||||||
|
if (pClient != nullptr) {
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
|
||||||
|
}
|
||||||
|
DebugReply(pConnection->Reply());
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
|
||||||
|
|
||||||
|
pClient->OnRequest(OnRequest);
|
||||||
|
pClient->OnExecute(OnExecute);
|
||||||
|
pClient->OnException(OnException);
|
||||||
|
|
||||||
|
pClient->AutoFree(true);
|
||||||
|
pClient->Active(true);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::ModuleNew(CContext &Context) {
|
||||||
|
|
||||||
|
Log()->Notice("[%s] [%s] Trying create new module.", Context.Name().c_str(), Context.URL().Origin().c_str());
|
||||||
|
|
||||||
|
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInProgress);
|
||||||
|
|
||||||
|
ARequest->ContentType = CHTTPRequest::json;
|
||||||
|
|
||||||
|
const auto &caModuleAddress = m_Module["address"];
|
||||||
|
|
||||||
|
CJSON Json(jvtObject);
|
||||||
|
|
||||||
|
Json.Object().AddPair("address", caModuleAddress);
|
||||||
|
Json.Object().AddPair("bitmessage", Context.Name());
|
||||||
|
|
||||||
|
const OpenPGP::SecretKey pgpSecret(m_pgpModuleKey.c_str());
|
||||||
|
const auto &public_key = pgpSecret.get_public();
|
||||||
|
|
||||||
|
Json.Object().AddPair("pgp", public_key.write(OpenPGP::PGP::Armored::YES));
|
||||||
|
|
||||||
|
ARequest->Content = Json.ToString();
|
||||||
|
|
||||||
|
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/client/new");
|
||||||
|
|
||||||
|
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
|
||||||
|
|
||||||
|
if (!caModuleAddress.IsEmpty())
|
||||||
|
ARequest->AddHeader("Module-Address", caModuleAddress);
|
||||||
|
|
||||||
|
DebugRequest(ARequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnExecute = [this, &Context](CTCPConnection *AConnection) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pReply = pConnection->Reply();
|
||||||
|
|
||||||
|
DebugReply(pReply);
|
||||||
|
|
||||||
|
if (pReply->Status == CHTTPReply::ok) {
|
||||||
|
const CJSON Json(pReply->Content);
|
||||||
|
|
||||||
|
if (Json.HasOwnProperty("result")) {
|
||||||
|
const auto &caResult = Json["result"];
|
||||||
|
if (caResult.HasOwnProperty("success")) {
|
||||||
|
if (caResult["success"].AsBoolean()) {
|
||||||
|
ModuleAuthorize(Context);
|
||||||
|
} else {
|
||||||
|
if (caResult.HasOwnProperty("message")) {
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, "[%s] [%s] %s", Context.Name().c_str(),
|
||||||
|
Context.URL().Origin().c_str(), caResult["message"].AsString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pConnection->CloseConnection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
|
||||||
|
if (pClient != nullptr) {
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
|
||||||
|
}
|
||||||
|
DebugReply(pConnection->Reply());
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
|
||||||
|
|
||||||
|
pClient->OnRequest(OnRequest);
|
||||||
|
pClient->OnExecute(OnExecute);
|
||||||
|
pClient->OnException(OnException);
|
||||||
|
|
||||||
|
pClient->AutoFree(true);
|
||||||
|
pClient->Active(true);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CCustomModule::ModuleAuthorize(CContext &Context) {
|
||||||
|
|
||||||
|
Log()->Notice("[%s] [%s] Trying authorize module.", Context.Name().c_str(), Context.URL().Origin().c_str());
|
||||||
|
|
||||||
|
auto OnRequest = [this, &Context](CHTTPClient *Sender, CHTTPRequest *ARequest) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csAuthorization);
|
||||||
|
|
||||||
|
ARequest->ContentType = CHTTPRequest::text;
|
||||||
|
|
||||||
|
Apostol::PGP::CleartextSignature(
|
||||||
|
m_pgpModuleKey,
|
||||||
|
m_pgpPassphrase,
|
||||||
|
BPS_PGP_HASH,
|
||||||
|
Context.Name(),
|
||||||
|
ARequest->Content);
|
||||||
|
|
||||||
|
CHTTPRequest::Prepare(ARequest, "POST", "/api/v1/dm/authorize");
|
||||||
|
|
||||||
|
ARequest->AddHeader("Authorization", "Bearer " + Context.Tokens()["access_token"]);
|
||||||
|
|
||||||
|
const auto &caModuleAddress = m_Module["address"];
|
||||||
|
if (!caModuleAddress.IsEmpty())
|
||||||
|
ARequest->AddHeader("Module-Address", caModuleAddress);
|
||||||
|
|
||||||
|
DebugRequest(ARequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnExecute = [&Context](CTCPConnection *AConnection) {
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pReply = pConnection->Reply();
|
||||||
|
|
||||||
|
DebugReply(pReply);
|
||||||
|
|
||||||
|
if (pReply->Status == CHTTPReply::ok) {
|
||||||
|
const CJSON Json(pReply->Content);
|
||||||
|
|
||||||
|
Context.Session() = Json["session"].AsString();
|
||||||
|
Context.Secret() = Json["secret"].AsString();
|
||||||
|
|
||||||
|
Context.Tokens().Values("access_token", Json["access_token"].AsString());
|
||||||
|
|
||||||
|
Context.SetFixedDate(0);
|
||||||
|
Context.SetCheckDate(Now() + (CDateTime) 55 / MinsPerDay); // 55 min
|
||||||
|
|
||||||
|
Context.SetStatus(csAuthorized);
|
||||||
|
} else {
|
||||||
|
Context.SetCheckDate(Now() + (CDateTime) 5 / MinsPerDay); // 5 min
|
||||||
|
}
|
||||||
|
|
||||||
|
pConnection->CloseConnection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto OnException = [&Context](CTCPConnection *AConnection, const Delphi::Exception::Exception &E) {
|
||||||
|
auto pConnection = dynamic_cast<CHTTPClientConnection *> (AConnection);
|
||||||
|
if (pConnection != nullptr) {
|
||||||
|
auto pClient = dynamic_cast<CHTTPClient *> (pConnection->Client());
|
||||||
|
if (pClient != nullptr) {
|
||||||
|
Log()->Error(APP_LOG_EMERG, 0, "[%s:%d] %s", pClient->Host().c_str(), pClient->Port(), E.what());
|
||||||
|
}
|
||||||
|
DebugReply(pConnection->Reply());
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.SetStatus(Context::csInitialized);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pClient = GetClient(Context.URL().hostname, Context.URL().port == 0 ? BPS_SERVER_PORT : Context.URL().port);
|
||||||
|
|
||||||
|
pClient->OnRequest(OnRequest);
|
||||||
|
pClient->OnExecute(OnExecute);
|
||||||
|
pClient->OnException(OnException);
|
||||||
|
|
||||||
|
pClient->AutoFree(true);
|
||||||
|
pClient->Active(true);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
|
|
||||||
|
} // namespace Apostol
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
154
src/app/Common.hpp
Normal file
154
src/app/Common.hpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/*++
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
Common.hpp
|
||||||
|
|
||||||
|
Notices:
|
||||||
|
|
||||||
|
Dial Module common.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Copyright (c) Prepodobny Alen
|
||||||
|
|
||||||
|
mailto: alienufo@inbox.ru
|
||||||
|
mailto: ufocomp@gmail.com
|
||||||
|
|
||||||
|
--*/
|
||||||
|
|
||||||
|
#ifndef APOSTOL_DM_COMMON_HPP
|
||||||
|
#define APOSTOL_DM_COMMON_HPP
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define BPS_PGP_HASH "SHA512"
|
||||||
|
#define BM_PREFIX "BM-"
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C++" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
namespace Apostol {
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-- CCustomModule ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class CCustomModule: public CApostolModule {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
CStringList m_Module;
|
||||||
|
|
||||||
|
CJSON m_OAuth2;
|
||||||
|
|
||||||
|
CString m_pgpModuleKey;
|
||||||
|
CString m_pgpPublicKey;
|
||||||
|
CString m_pgpPassphrase;
|
||||||
|
|
||||||
|
CProcessStatus m_Status;
|
||||||
|
|
||||||
|
int m_SyncPeriod;
|
||||||
|
|
||||||
|
void InitMethods() override;
|
||||||
|
|
||||||
|
template<class ClassName>
|
||||||
|
void UpdateServerList(TPairs<ClassName> &ClassList, const CString &Key) {
|
||||||
|
CStringPairs ServerList;
|
||||||
|
CStringList Keys;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
ParsePGPKey(Key, ServerList, Keys);
|
||||||
|
|
||||||
|
if (ServerList.Count() != 0) {
|
||||||
|
CStringPairs::ConstEnumerator em(ServerList);
|
||||||
|
while (em.MoveNext()) {
|
||||||
|
const auto &caCurrent = em.Current();
|
||||||
|
#ifndef _DEBUG
|
||||||
|
if (caCurrent.Name() == BPS_BM_DEBUG_ADDRESS)
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
index = ClassList.IndexOfName(caCurrent.Name());
|
||||||
|
if (index == -1) {
|
||||||
|
index = ClassList.AddPair(caCurrent.Name(), ClassName(caCurrent.Name(), CLocation(caCurrent.Value())));
|
||||||
|
auto &Context = ClassList[index].Value();
|
||||||
|
Context.PGP().Add(CKeyContext(CString("PUBLIC"), Key));
|
||||||
|
Context.PGP().Add(CKeyContext(caCurrent.Name(), CString()));
|
||||||
|
} else {
|
||||||
|
auto &Context = ClassList[index].Value();
|
||||||
|
Context.URL() = caCurrent.Value();
|
||||||
|
Context.PGP().First().Key = Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &Context = ClassList[index].Value();
|
||||||
|
Context.BTCKeys() = Keys;
|
||||||
|
|
||||||
|
if (Context.Status() == Context::csInitialization) {
|
||||||
|
UpdateOAuth2(Context, m_OAuth2.Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void FetchProviders(CDateTime Now, CContext &Context);
|
||||||
|
static void CheckProviders(CDateTime Now, CContext &Context);
|
||||||
|
|
||||||
|
void CreateAccessToken(const CProvider &Provider, const CString &Application, CContext &Context);
|
||||||
|
void ParsePGPKey(const CString &Key, CStringPairs &ServerList, CStringList &BTCKeys);
|
||||||
|
|
||||||
|
void ModuleService(CContext &Context);
|
||||||
|
void ModuleStatus(CContext &Context);
|
||||||
|
void ModuleNew(CContext &Context);
|
||||||
|
void ModuleAuthorize(CContext &Context);
|
||||||
|
|
||||||
|
void DoGet(CHTTPServerConnection *AConnection) override;
|
||||||
|
|
||||||
|
virtual void DoPost(CHTTPServerConnection *AConnection);
|
||||||
|
virtual void DoAPI(CHTTPServerConnection *AConnection);
|
||||||
|
|
||||||
|
virtual void DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI) abstract;
|
||||||
|
virtual void DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action) abstract;
|
||||||
|
virtual void DoSignature(CHTTPServerConnection *AConnection) abstract;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit CCustomModule(CModuleProcess *AProcess, const CString &ModuleName, const CString &SectionName);
|
||||||
|
|
||||||
|
~CCustomModule() override = default;
|
||||||
|
|
||||||
|
void Initialization(CModuleProcess *AProcess) override;
|
||||||
|
bool Enabled() override;
|
||||||
|
|
||||||
|
virtual void Reload() abstract;
|
||||||
|
|
||||||
|
static CString ToString(unsigned long Value);
|
||||||
|
static void JsonStringToKey(const CString &String, CString &Key);
|
||||||
|
|
||||||
|
static void CheckKeyForNull(LPCTSTR Key, LPCTSTR Value);
|
||||||
|
|
||||||
|
static void CheckDeal(const CDeal &Deal);
|
||||||
|
static int CheckFee(const CString &Fee);
|
||||||
|
|
||||||
|
static bool CheckVerifyPGPSignature(int VerifyPGPSignature, CString &Message);
|
||||||
|
static int VerifyPGPSignature(const CString &ClearText, const CString &Key, CString &Message);
|
||||||
|
|
||||||
|
static bool FindURLInLine(const CString &Line, CStringList &List);
|
||||||
|
static void UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2);
|
||||||
|
static void UpdateProviders(CProviders &Providers, const CJSONObject &Data);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Apostol::Common;
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif //APOSTOL_DM_COMMON_HPP
|
||||||
@@ -29,9 +29,6 @@ Author:
|
|||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
//----------------------------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
//using namespace std;
|
|
||||||
//----------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "delphi.hpp"
|
#include "delphi.hpp"
|
||||||
//----------------------------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,9 @@ Author:
|
|||||||
#define DM_WEB_SERVICE_MODULE_HPP
|
#define DM_WEB_SERVICE_MODULE_HPP
|
||||||
//----------------------------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "Common.hpp"
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
extern "C++" {
|
extern "C++" {
|
||||||
|
|
||||||
namespace Apostol {
|
namespace Apostol {
|
||||||
@@ -37,26 +40,14 @@ namespace Apostol {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class CWebService: public CApostolModule {
|
class CWebService: public CCustomModule {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
CStringList m_Module;
|
|
||||||
|
|
||||||
CJSON m_OAuth2;
|
|
||||||
|
|
||||||
CString m_pgpModuleKey;
|
|
||||||
CString m_pgpPublicKey;
|
|
||||||
CString m_pgpPassphrase;
|
|
||||||
|
|
||||||
CProcessStatus m_Status;
|
|
||||||
|
|
||||||
CContextPair m_DefaultServer { BPS_BM_SERVER_ADDRESS, CContext(BPS_BM_SERVER_ADDRESS, CLocation(BPS_SERVER_URL)) };
|
CContextPair m_DefaultServer { BPS_BM_SERVER_ADDRESS, CContext(BPS_BM_SERVER_ADDRESS, CLocation(BPS_SERVER_URL)) };
|
||||||
CContextList m_Servers {};
|
CContextList m_Servers {};
|
||||||
|
|
||||||
CContext m_CurrentServer {};
|
CContext m_CurrentServer {};
|
||||||
|
|
||||||
int m_SyncPeriod;
|
|
||||||
|
|
||||||
CDateTime m_NexServerTime;
|
CDateTime m_NexServerTime;
|
||||||
|
|
||||||
CHTTPProxyManager m_ProxyManager;
|
CHTTPProxyManager m_ProxyManager;
|
||||||
@@ -64,39 +55,16 @@ namespace Apostol {
|
|||||||
void InitMethods() override;
|
void InitMethods() override;
|
||||||
|
|
||||||
void InitServerList();
|
void InitServerList();
|
||||||
void UpdateServerList(const CString &Key);
|
|
||||||
|
|
||||||
void FetchProviders(CDateTime Now, CContext &Context);
|
|
||||||
void CheckProviders(CDateTime Now, CContext &Context);
|
|
||||||
|
|
||||||
void CreateAccessToken(const CProvider &Provider, const CString &Application, CContext &Context);
|
|
||||||
void ParsePGPKey(const CString& Key, CStringPairs& ServerList, CStringList& BTCKeys);
|
|
||||||
void FetchPGP(CContext &Context);
|
void FetchPGP(CContext &Context);
|
||||||
|
|
||||||
void ModuleService(CContext &Context);
|
|
||||||
void ModuleStatus(CContext &Context);
|
|
||||||
void ModuleNew(CContext &Context);
|
|
||||||
void ModuleAuthorize(CContext &Context);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
CHTTPProxy *GetProxy(CHTTPServerConnection *AConnection);
|
CHTTPProxy *GetProxy(CHTTPServerConnection *AConnection);
|
||||||
|
|
||||||
const CContext &CurrentServer() const;
|
void DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI) override;
|
||||||
|
void DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action) override;
|
||||||
static int CheckFee(const CString& Fee);
|
void DoSignature(CHTTPServerConnection *AConnection) override;
|
||||||
|
|
||||||
static void CheckKeyForNull(LPCTSTR Key, LPCTSTR Value);
|
|
||||||
|
|
||||||
void DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI);
|
|
||||||
void DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action);
|
|
||||||
|
|
||||||
void DoSignature(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
void DoAPI(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
void DoGet(CHTTPServerConnection *AConnection) override;
|
|
||||||
void DoPost(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
bool DoProxyExecute(CTCPConnection *AConnection);
|
bool DoProxyExecute(CTCPConnection *AConnection);
|
||||||
void DoProxyException(CTCPConnection *AConnection, const Delphi::Exception::Exception &E);
|
void DoProxyException(CTCPConnection *AConnection, const Delphi::Exception::Exception &E);
|
||||||
@@ -107,33 +75,17 @@ namespace Apostol {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit CWebService(CModuleProcess *AProcess);
|
explicit CWebService(CModuleProcess *AProcess, const CString &ModuleName, const CString &SectionName);
|
||||||
|
|
||||||
~CWebService() override = default;
|
~CWebService() override = default;
|
||||||
|
|
||||||
static class CWebService *CreateModule(CModuleProcess *AProcess) {
|
static class CWebService *CreateModule(CModuleProcess *AProcess) {
|
||||||
return new CWebService(AProcess);
|
return new CWebService(AProcess, "web service", "module/WebService");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heartbeat(CDateTime Now) override;
|
void Heartbeat(CDateTime Now) override;
|
||||||
|
|
||||||
void Initialization(CModuleProcess *AProcess) override;
|
void Reload() override;
|
||||||
bool Enabled() override;
|
|
||||||
|
|
||||||
void Reload();
|
|
||||||
|
|
||||||
static CString ToString(unsigned long Value);
|
|
||||||
|
|
||||||
static void JsonStringToKey(const CString& jsonString, CString& Key);
|
|
||||||
|
|
||||||
static void CheckDeal(const CDeal& Deal);
|
|
||||||
|
|
||||||
static bool CheckVerifyPGPSignature(int VerifyPGPSignature, CString &Message);
|
|
||||||
static int VerifyPGPSignature(const CString &ClearText, const CString &Key, CString &Message);
|
|
||||||
|
|
||||||
static bool FindURLInLine(const CString &Line, CStringList &List);
|
|
||||||
static void UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2);
|
|
||||||
static void UpdateProviders(CProviders &Providers, const CJSONObject &Data);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,9 @@ Author:
|
|||||||
#define DM_WEB_SOCKET_MODULE_HPP
|
#define DM_WEB_SOCKET_MODULE_HPP
|
||||||
//----------------------------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "Common.hpp"
|
||||||
|
//----------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
extern "C++" {
|
extern "C++" {
|
||||||
|
|
||||||
namespace Apostol {
|
namespace Apostol {
|
||||||
@@ -64,42 +67,18 @@ namespace Apostol {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class CWebSocketModule: public CApostolModule {
|
class CWebSocketModule: public CCustomModule {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
CStringList m_Module;
|
|
||||||
|
|
||||||
CJSON m_OAuth2;
|
|
||||||
|
|
||||||
CString m_pgpModuleKey;
|
|
||||||
CString m_pgpPublicKey;
|
|
||||||
CString m_pgpPassphrase;
|
|
||||||
|
|
||||||
int m_SyncPeriod;
|
|
||||||
|
|
||||||
CClientContextPair m_DefaultServer { BPS_BM_SERVER_ADDRESS, CClientContext(BPS_BM_SERVER_ADDRESS, CLocation(BPS_SERVER_URL)) };
|
CClientContextPair m_DefaultServer { BPS_BM_SERVER_ADDRESS, CClientContext(BPS_BM_SERVER_ADDRESS, CLocation(BPS_SERVER_URL)) };
|
||||||
|
|
||||||
CClientContextList m_Servers {};
|
CClientContextList m_Servers {};
|
||||||
|
|
||||||
void InitMethods() override;
|
|
||||||
|
|
||||||
void InitServerList();
|
void InitServerList();
|
||||||
void UpdateServerList(const CString &Key);
|
|
||||||
|
|
||||||
void FetchProviders(CDateTime Now, CClientContext &Context);
|
|
||||||
void CheckProviders(CDateTime Now, CClientContext &Context);
|
|
||||||
|
|
||||||
void CreateAccessToken(const CProvider &Provider, const CString &Application, CClientContext &Context);
|
|
||||||
void ParsePGPKey(const CString& Key, CStringPairs& ServerList, CStringList& BTCKeys);
|
|
||||||
|
|
||||||
CWebSocketClient *GetWebSocketClient(CClientContext &Context);
|
CWebSocketClient *GetWebSocketClient(CClientContext &Context);
|
||||||
void CreateWebSocketClient(CClientContext &Context);
|
void CreateWebSocketClient(CClientContext &Context);
|
||||||
|
|
||||||
void ModuleService(CContext &Context);
|
|
||||||
void ModuleStatus(CContext &Context);
|
|
||||||
void ModuleNew(CContext &Context);
|
|
||||||
void ModuleAuthorize(CContext &Context);
|
|
||||||
|
|
||||||
static CWebSocketClient *GetConnectedClient(const CClientContext &Context);
|
static CWebSocketClient *GetConnectedClient(const CClientContext &Context);
|
||||||
|
|
||||||
int CurrentContextIndex(const CString &Params);
|
int CurrentContextIndex(const CString &Params);
|
||||||
@@ -107,17 +86,9 @@ namespace Apostol {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void Heartbeat(CDateTime Now) override;
|
void DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI) override;
|
||||||
|
void DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action) override;
|
||||||
void DoAccount(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI);
|
void DoSignature(CHTTPServerConnection *AConnection) override;
|
||||||
void DoDeal(CHTTPServerConnection *AConnection, const CString &Method, const CString &URI, const CString &Action);
|
|
||||||
|
|
||||||
void DoSignature(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
void DoAPI(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
void DoGet(CHTTPServerConnection *AConnection) override;
|
|
||||||
void DoPost(CHTTPServerConnection *AConnection);
|
|
||||||
|
|
||||||
void DoWebSocketError(CTCPConnection *AConnection);
|
void DoWebSocketError(CTCPConnection *AConnection);
|
||||||
void DoClientConnected(CObject *Sender);
|
void DoClientConnected(CObject *Sender);
|
||||||
@@ -137,31 +108,18 @@ namespace Apostol {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit CWebSocketModule(CModuleProcess *AProcess);
|
explicit CWebSocketModule(CModuleProcess *AProcess, const CString &ModuleName, const CString &SectionName);
|
||||||
|
|
||||||
~CWebSocketModule() override = default;
|
~CWebSocketModule() override = default;
|
||||||
|
|
||||||
static class CWebSocketModule *CreateModule(CModuleProcess *AProcess) {
|
static class CWebSocketModule *CreateModule(CModuleProcess *AProcess) {
|
||||||
return new CWebSocketModule(AProcess);
|
return new CWebSocketModule(AProcess, "web socket", "module/WebSocket");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialization(CModuleProcess *AProcess) override;
|
void Heartbeat(CDateTime Now) override;
|
||||||
bool Enabled() override;
|
|
||||||
|
|
||||||
void Reload();
|
void Reload() override;
|
||||||
|
|
||||||
static CString ToString(unsigned long Value);
|
|
||||||
|
|
||||||
static int CheckFee(const CString& Fee);
|
|
||||||
static void CheckDeal(const CDeal& Deal);
|
|
||||||
static void CheckKeyForNull(LPCTSTR key, const CString& Value);
|
|
||||||
|
|
||||||
static bool CheckVerifyPGPSignature(int VerifyPGPSignature, CString &Message);
|
|
||||||
static int VerifyPGPSignature(const CString &Text, const CString &Key, CString &Message);
|
|
||||||
|
|
||||||
static bool FindURLInLine(const CString &Line, CStringList &List);
|
|
||||||
static void UpdateOAuth2(CContext &Context, const CJSONObject &OAuth2);
|
|
||||||
static void UpdateProviders(CProviders &Providers, const CJSONObject &Data);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user