diff --git a/SiaDrive.Api/SiaApi.cpp b/SiaDrive.Api/SiaApi.cpp index 8269687..4f67939 100644 --- a/SiaDrive.Api/SiaApi.cpp +++ b/SiaDrive.Api/SiaApi.cpp @@ -4,9 +4,9 @@ using namespace Sia::Api; CSiaApi::CSiaApi(const SiaHostConfig& hostConfig) : + _siaCurl(hostConfig), _wallet(new CSiaWallet(_siaCurl)) { - _siaCurl.SetHostConfig(hostConfig); } CSiaApi::~CSiaApi() diff --git a/SiaDrive.Api/SiaApi.h b/SiaDrive.Api/SiaApi.h index c8667a3..ebf2f87 100644 --- a/SiaDrive.Api/SiaApi.h +++ b/SiaDrive.Api/SiaApi.h @@ -12,6 +12,8 @@ public: { Success, NotImplemented, + RequestError, + WalletExists, WalletLocked, WalletUnlocked, WalletNotCreated @@ -42,6 +44,7 @@ public: public: _SiaApiError Create(const _SiaSeedLanguage& seedLanguage, String& seed); + bool Refresh(); _SiaApiError Restore(const String& seed); _SiaApiError Lock(); _SiaApiError Unlock(); diff --git a/SiaDrive.Api/SiaCurl.cpp b/SiaDrive.Api/SiaCurl.cpp index 01c2222..7f3482f 100644 --- a/SiaDrive.Api/SiaCurl.cpp +++ b/SiaDrive.Api/SiaCurl.cpp @@ -7,7 +7,12 @@ CSiaCurl::CSiaCurl() : _curlHandle(curl_easy_init()) { SetHostConfig({ L"localhost", 9980, L""}); - curl_easy_setopt(_curlHandle, CURLOPT_USERAGENT, "Sia-Agent"); +} + +CSiaCurl::CSiaCurl(const SiaHostConfig& hostConfig) : + _curlHandle(curl_easy_init()) +{ + SetHostConfig(hostConfig); } CSiaCurl::~CSiaCurl() @@ -43,32 +48,34 @@ std::string CSiaCurl::ConstructPath(const String& relativePath) const SiaCurlError CSiaCurl::_Get(const String& path, json& response) const { - std::string result; - SiaCurlError ret = SiaCurlError::Success; - if (API_SUCCESS(SiaCurlError, ret)) + curl_easy_reset(_curlHandle); + + curl_easy_setopt(_curlHandle, CURLOPT_USERAGENT, "Sia-Agent"); + 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 { - 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; + })); + + std::string result; + curl_easy_setopt(_curlHandle, CURLOPT_WRITEDATA, &result); + const CURLcode res = curl_easy_perform(_curlHandle); + + SiaCurlError ret; + if (res == CURLE_OK) + { + ret = CheckApiError((response = json::parse(result.c_str()))); + } + else + { + if ((res == CURLE_COULDNT_RESOLVE_HOST) || (res == CURLE_COULDNT_CONNECT)) { - (*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 = CheckApiError((response = json::parse(result.c_str()))); + ret = SiaCurlError::NoResponse; } else { - if ((res == CURLE_COULDNT_RESOLVE_HOST) || (res == CURLE_COULDNT_CONNECT)) - { - ret = SiaCurlError::NoResponse; - } - else - { - ret = SiaCurlError::UnknownFailure; - } + ret = SiaCurlError::UnknownFailure; } } @@ -104,13 +111,62 @@ String CSiaCurl::GetServerVersion() const SiaCurlError CSiaCurl::Get(const String& path, json& response) const { - std::string result; - SiaCurlError ret; if (CheckVersion(ret)) { ret = _Get(path, response); } + return ret; +} + +SiaCurlError CSiaCurl::Post(const String& path, const PostParameters& parameters, json& response) const +{ + SiaCurlError ret; + if (CheckVersion(ret)) + { + curl_easy_reset(_curlHandle); + + curl_easy_setopt(_curlHandle, CURLOPT_USERAGENT, "Sia-Agent"); + 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; + })); + + std::string fields; + for (const auto& param : parameters) + { + if (fields.length()) + { + fields += "&"; + } + + fields += (std::string(CW2A(param.first.c_str())) + "=" + std::string(CW2A(param.second.c_str()))); + } + curl_easy_setopt(_curlHandle, CURLOPT_POSTFIELDS, fields.c_str()); + + std::string result; + curl_easy_setopt(_curlHandle, CURLOPT_WRITEDATA, &result); + const CURLcode res = curl_easy_perform(_curlHandle); + + if (res == CURLE_OK) + { + ret = CheckApiError((response = json::parse(result.c_str()))); + } + else + { + if ((res == CURLE_COULDNT_RESOLVE_HOST) || (res == CURLE_COULDNT_CONNECT)) + { + ret = SiaCurlError::NoResponse; + } + else + { + ret = SiaCurlError::UnknownFailure; + } + } + } + return ret; } \ No newline at end of file diff --git a/SiaDrive.Api/SiaCurl.h b/SiaDrive.Api/SiaCurl.h index 1250467..73bf59b 100644 --- a/SiaDrive.Api/SiaCurl.h +++ b/SiaDrive.Api/SiaCurl.h @@ -1,7 +1,6 @@ #pragma once #include - -// Forward curl +#include NS_BEGIN(Sia) NS_BEGIN(Api) @@ -19,9 +18,13 @@ public: UnknownFailure }; + typedef std::unordered_map _PostParameters; + public: CSiaCurl(); + CSiaCurl(const SiaHostConfig& hostConfig); + public: ~CSiaCurl(); @@ -41,9 +44,11 @@ private: public: String GetServerVersion() const; _SiaCurlError Get(const String& path, json& result) const; + _SiaCurlError Post(const String& path, const _PostParameters& parameters, json& response) const; }; typedef CSiaCurl::_SiaCurlError SiaCurlError; +typedef CSiaCurl::_PostParameters PostParameters; NS_END(2) diff --git a/SiaDrive.Api/SiaWallet.cpp b/SiaDrive.Api/SiaWallet.cpp index abac212..6d05830 100644 --- a/SiaDrive.Api/SiaWallet.cpp +++ b/SiaDrive.Api/SiaWallet.cpp @@ -3,12 +3,30 @@ using namespace Sia::Api; +static String SeedLangToString(const SiaSeedLanguage& lang) +{ + switch (lang) + { + case SiaSeedLanguage::English: + return L"english"; + + case SiaSeedLanguage::German: + return L"german"; + + case SiaSeedLanguage::Japanese: + return L"japanese"; + + default: + throw std::exception("Seed language not implemented"); + } +} + CSiaApi::_CSiaWallet::_CSiaWallet(CSiaCurl& siaCurl) : _siaCurl(siaCurl), _Created(false), _Locked(false) { - + Refresh(); } CSiaApi::_CSiaWallet::~_CSiaWallet() @@ -16,15 +34,48 @@ CSiaApi::_CSiaWallet::~_CSiaWallet() } +bool CSiaApi::_CSiaWallet::Refresh() +{ + json result; + SiaCurlError error = _siaCurl.Get(L"/wallet", result); + if (API_SUCCESS(SiaCurlError, error)) + { + SetCreated(result["encrypted"].get()); + SetLocked(result["unlocked"].get()); + + return true; + } + + return false; +} + SiaApiError CSiaApi::_CSiaWallet::Create(const SiaSeedLanguage& seedLanguage, String& seed) { - SiaApiError error = SiaApiError::NotImplemented; + SiaApiError error = SiaApiError::RequestError; + if (Refresh()) + { + error = SiaApiError::WalletExists; + if (!GetCreated()) + { + error = SiaApiError::RequestError; + json result; + SiaCurlError cerror = _siaCurl.Post(L"/wallet/init", { {L"dictionary", SeedLangToString(seedLanguage)} }, result); + if (API_SUCCESS(SiaCurlError, cerror)) + { + error = SiaApiError::Success; + seed = CA2W(result["primaryseed"].get().c_str()); + Refresh(); + } + } + } + return error; } SiaApiError CSiaApi::_CSiaWallet::Restore(const String& seed) { SiaApiError error = SiaApiError::NotImplemented; + // TODO Future enhancement return error; } diff --git a/UnitTests/SiaWalletApiTests.cpp b/UnitTests/SiaWalletApiTests.cpp index cfeb102..e23d0ab 100644 --- a/UnitTests/SiaWalletApiTests.cpp +++ b/UnitTests/SiaWalletApiTests.cpp @@ -29,6 +29,7 @@ namespace UnitTests String seed; Assert::IsTrue(API_SUCCESS(SiaApiError, wallet->Create(SiaSeedLanguage::English, seed))); Assert::IsTrue(wallet->GetCreated()); + Assert::IsFalse(wallet->GetLocked()); } }; } \ No newline at end of file