diff --git a/SiaDrive.Api/SiaApi.cpp b/SiaDrive.Api/SiaApi.cpp index 6f368ce..8269687 100644 --- a/SiaDrive.Api/SiaApi.cpp +++ b/SiaDrive.Api/SiaApi.cpp @@ -3,9 +3,10 @@ using namespace Sia::Api; -CSiaApi::CSiaApi(const String& host, const std::uint16_t& port) : - _wallet(new CSiaWallet()) +CSiaApi::CSiaApi(const SiaHostConfig& hostConfig) : + _wallet(new CSiaWallet(_siaCurl)) { + _siaCurl.SetHostConfig(hostConfig); } CSiaApi::~CSiaApi() @@ -13,6 +14,11 @@ CSiaApi::~CSiaApi() _wallet->Unlock(); } +String CSiaApi::GetServerVersion() const +{ + return _siaCurl.GetServerVersion(); +} + CSiaWalletPtr CSiaApi::GetWallet() const { return _wallet; diff --git a/SiaDrive.Api/SiaApi.h b/SiaDrive.Api/SiaApi.h index 6caee42..c473607 100644 --- a/SiaDrive.Api/SiaApi.h +++ b/SiaDrive.Api/SiaApi.h @@ -1,5 +1,6 @@ #pragma once #include "SiaCommon.h" +#include "SiaCurl.h" NS_BEGIN(Sia) NS_BEGIN(Api) @@ -7,7 +8,8 @@ NS_BEGIN(Api) class AFX_EXT_CLASS CSiaApi { public: - enum class _SiaApiError { + enum class _SiaApiError + { Success, NotImplemented, WalletLocked, @@ -19,11 +21,14 @@ public: { friend CSiaApi; private: - _CSiaWallet(); + _CSiaWallet(CSiaCurl& siaCurl); public: ~_CSiaWallet(); + private: + CSiaCurl& _siaCurl; + // Properties Property(bool, Created, public, private) Property(bool, Locked, public, private) @@ -36,22 +41,23 @@ public: }; public: - CSiaApi(const String& host, const std::uint16_t& port); + CSiaApi(const SiaHostConfig& hostConfig); public: ~CSiaApi(); private: + CSiaCurl _siaCurl; std::shared_ptr<_CSiaWallet> _wallet; public: std::shared_ptr<_CSiaWallet> GetWallet() const; + String GetServerVersion() const; }; typedef CSiaApi::_SiaApiError SiaApiError; typedef CSiaApi::_CSiaWallet CSiaWallet; typedef std::shared_ptr CSiaWalletPtr; -#define API_SUCCESS(x) (x == SiaApiError::Success) NS_END(2) \ No newline at end of file diff --git a/SiaDrive.Api/SiaCommon.h b/SiaDrive.Api/SiaCommon.h index 3c077a3..3a79019 100644 --- a/SiaDrive.Api/SiaCommon.h +++ b/SiaDrive.Api/SiaCommon.h @@ -32,6 +32,9 @@ set_access:\ struct SiaHostConfig { - std::string HostName; + String HostName; std::uint16_t HostPort; -}; \ No newline at end of file + String RequiredVersion; +}; + +#define API_SUCCESS(t, x) (x == t::Success) \ No newline at end of file diff --git a/SiaDrive.Api/SiaCurl.cpp b/SiaDrive.Api/SiaCurl.cpp index 5da9bea..08b7aa5 100644 --- a/SiaDrive.Api/SiaCurl.cpp +++ b/SiaDrive.Api/SiaCurl.cpp @@ -1,12 +1,13 @@ #include "stdafx.h" #include "SiaCurl.h" -#include +#include using namespace Sia::Api; CSiaCurl::CSiaCurl() : _curlHandle(curl_easy_init()) { - SetHostConfig({ "localhost", 9980 }); + SetHostConfig({ L"localhost", 9980, L""}); + curl_easy_setopt(_curlHandle, CURLOPT_USERAGENT, "Sia-Agent"); } CSiaCurl::~CSiaCurl() @@ -14,31 +15,73 @@ CSiaCurl::~CSiaCurl() curl_easy_cleanup(_curlHandle); } -CStringA CSiaCurl::ConstructPath(const CString& relativePath) const +SiaCurlError CSiaCurl::_Get(const String& path, json& response) const { - const CStringA ret = CStringA("http://") + GetHostConfig().HostName.c_str() + ":" + std::to_string(GetHostConfig().HostPort).c_str() + CW2A(relativePath); + std::string result; + SiaCurlError ret = SiaCurlError::Success; + if (API_SUCCESS(SiaCurlError, ret)) + { + curl_easy_setopt(_curlHandle, CURLOPT_URL, ConstructPath(path).c_str()); + curl_easy_setopt(_curlHandle, CURLOPT_WRITEFUNCTION, static_cast([](char *buffer, size_t size, size_t nitems, void *outstream) -> size_t + { + (*reinterpret_cast(outstream)) += std::string(reinterpret_cast(buffer), size * nitems); + return size * nitems; + })); + curl_easy_setopt(_curlHandle, CURLOPT_WRITEDATA, &result); + const CURLcode res = curl_easy_perform(_curlHandle); + if (res != CURLE_OK) + { + ret = SiaCurlError::Failed; + } + + response = json::parse(result.c_str()); + } + return ret; } -SiaCurlError CSiaCurl::Get(const CString& path, json& response) +bool CSiaCurl::CheckVersion(SiaCurlError& error) const { - CStringA result; - SiaCurlError ret = SiaCurlError::Success; - curl_easy_setopt(_curlHandle, CURLOPT_USERAGENT, "Sia-Agent"); - curl_easy_setopt(_curlHandle, CURLOPT_URL, static_cast(ConstructPath(path))); - curl_easy_setopt(_curlHandle, CURLOPT_WRITEFUNCTION, static_cast([](char *buffer, size_t size, size_t nitems, void *outstream) -> size_t + error = SiaCurlError::InvalidRequiredVersion; + if (GetHostConfig().RequiredVersion.length()) { - (*reinterpret_cast(outstream)) += CString(reinterpret_cast(buffer), size * nitems); - return size * nitems; - })); - curl_easy_setopt(_curlHandle, CURLOPT_WRITEDATA, &result); - const CURLcode res = curl_easy_perform(_curlHandle); - if (res != CURLE_OK) - { - ret = SiaCurlError::Failed; + error = SiaCurlError::NoResponse; + const String serverVersion = GetServerVersion(); + if (serverVersion.length()) + { + error = (serverVersion == GetHostConfig().RequiredVersion) ? SiaCurlError::Success : SiaCurlError::ServerVersionMismatch; + } } - response = json::parse((LPCSTR)result); + return API_SUCCESS(SiaCurlError, error); +} + +std::string CSiaCurl::ConstructPath(const String& relativePath) const +{ + const std::string ret = "http://" + std::string(CW2A(GetHostConfig().HostName.c_str()))+ ":" + std::to_string(GetHostConfig().HostPort) + std::string(CW2A(relativePath.c_str())); + return ret; +} + +String CSiaCurl::GetServerVersion() const +{ + json response; + if (API_SUCCESS(SiaCurlError, _Get(L"/daemon/version", response))) + { + return String(CA2W(response["version"].get().c_str())); + } + + return L""; +} + +SiaCurlError CSiaCurl::Get(const String& path, json& response) const +{ + std::string result; + + SiaCurlError ret; + if (CheckVersion(ret)) + { + ret = _Get(path, response); + } return ret; } \ No newline at end of file diff --git a/SiaDrive.Api/SiaCurl.h b/SiaDrive.Api/SiaCurl.h index 3e412b1..a1d4fc4 100644 --- a/SiaDrive.Api/SiaCurl.h +++ b/SiaDrive.Api/SiaCurl.h @@ -12,6 +12,9 @@ public: enum class _SiaCurlError { Success, + ServerVersionMismatch, + InvalidRequiredVersion, + NoResponse, Failed }; @@ -27,10 +30,13 @@ private: Property(SiaHostConfig, HostConfig, public, public) private: - CStringA ConstructPath(const CString& relativePath) const; + std::string ConstructPath(const String& relativePath) const; + _SiaCurlError _Get(const String& path, json& response) const; + bool CheckVersion(_SiaCurlError& error) const; public: - _SiaCurlError Get(const CString& path, json& result); + String GetServerVersion() const; + _SiaCurlError Get(const String& path, json& result) const; }; typedef CSiaCurl::_SiaCurlError SiaCurlError; diff --git a/SiaDrive.Api/SiaWallet.cpp b/SiaDrive.Api/SiaWallet.cpp index 47c478f..78290e3 100644 --- a/SiaDrive.Api/SiaWallet.cpp +++ b/SiaDrive.Api/SiaWallet.cpp @@ -3,7 +3,10 @@ using namespace Sia::Api; -CSiaApi::_CSiaWallet::_CSiaWallet() +CSiaApi::_CSiaWallet::_CSiaWallet(CSiaCurl& siaCurl) : + _siaCurl(siaCurl), + _Created(false), + _Locked(false) { } @@ -28,7 +31,7 @@ SiaApiError CSiaApi::_CSiaWallet::Restore(const String& seed) SiaApiError CSiaApi::_CSiaWallet::Lock() { SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiError::WalletLocked : SiaApiError::Success) : SiaApiError::WalletNotCreated; - if (API_SUCCESS(error)) + if (API_SUCCESS(SiaApiError, error)) { } @@ -38,7 +41,7 @@ SiaApiError CSiaApi::_CSiaWallet::Lock() SiaApiError CSiaApi::_CSiaWallet::Unlock() { SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiError::Success : SiaApiError::WalletUnlocked) : SiaApiError::WalletNotCreated; - if (API_SUCCESS(error)) + if (API_SUCCESS(SiaApiError, error)) { } diff --git a/SiaDrive.Api/json.hpp b/SiaDrive.Api/json.hpp index 5fdd83d..fff8d6c 100644 --- a/SiaDrive.Api/json.hpp +++ b/SiaDrive.Api/json.hpp @@ -29,6 +29,12 @@ SOFTWARE. #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP +#ifdef _WIN32 +#ifdef max +#undef max +#endif +#endif + #include // all_of, for_each, transform #include // array #include // assert diff --git a/UnitTests/SiaCurlTests.cpp b/UnitTests/SiaCurlTests.cpp index 6fd6aa9..dad5d26 100644 --- a/UnitTests/SiaCurlTests.cpp +++ b/UnitTests/SiaCurlTests.cpp @@ -13,12 +13,31 @@ namespace UnitTests TEST_METHOD(GetBasicTest) { CSiaCurl s; - s.SetHostConfig({ "localhost", 9980 }); + s.SetHostConfig({ L"localhost", 9980, L"1.1.0" }); json result; SiaCurlError err = s.Get(L"/daemon/version", result); Assert::IsTrue(err == SiaCurlError::Success); - Assert::IsTrue(result["version"].is_string()); + } + + TEST_METHOD(EmptyHostConfigRequiredVersion) + { + CSiaCurl s; + s.SetHostConfig({ L"localhost", 9980, L"" }); + + json result; + SiaCurlError err = s.Get(L"/daemon/version", result); + Assert::IsTrue(err == SiaCurlError::InvalidRequiredVersion); + } + + TEST_METHOD(ServerVersionDoesNotMatchRequiredVersion) + { + CSiaCurl s; + s.SetHostConfig({ L"localhost", 9980, L"ouaoeuaoeuaoeu" }); + + json result; + SiaCurlError err = s.Get(L"/daemon/version", result); + Assert::IsTrue(err == SiaCurlError::ServerVersionMismatch); } }; diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj index 5780c35..22c0bc5 100644 --- a/UnitTests/UnitTests.vcxproj +++ b/UnitTests/UnitTests.vcxproj @@ -45,7 +45,7 @@ true v140 Unicode - false + Dynamic DynamicLibrary @@ -53,7 +53,7 @@ v140 true Unicode - false + Dynamic @@ -90,7 +90,7 @@ Use Level3 Disabled - ..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + ..\SiaDrive.Api;..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;%(PreprocessorDefinitions) true @@ -106,7 +106,7 @@ Use Level3 Disabled - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + ..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) _DEBUG;%(PreprocessorDefinitions) true @@ -122,7 +122,7 @@ MaxSpeed true true - ..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + ..\SiaDrive.Api;..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;%(PreprocessorDefinitions) true @@ -142,7 +142,7 @@ MaxSpeed true true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + ..\SiaDrive.Api;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) true