aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee/src
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/epee/src')
-rw-r--r--contrib/epee/src/CMakeLists.txt16
-rw-r--r--contrib/epee/src/byte_slice.cpp24
-rw-r--r--contrib/epee/src/file_io_utils.cpp231
-rw-r--r--contrib/epee/src/levin_base.cpp65
-rw-r--r--contrib/epee/src/misc_language.cpp44
-rw-r--r--contrib/epee/src/net_ssl.cpp47
-rw-r--r--contrib/epee/src/portable_storage.cpp222
7 files changed, 613 insertions, 36 deletions
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index 5e101a86a..132fed355 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -26,10 +26,21 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+set(EPEE_INCLUDE_DIR_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../include")
+
+# Adding headers to the file list, to be able to search for them in IDEs.
+file(GLOB EPEE_HEADERS_PUBLIC
+ "${EPEE_INCLUDE_DIR_BASE}/*.h*" # h* will include hpps as well.
+ "${EPEE_INCLUDE_DIR_BASE}/**/*.h*" # Any number of subdirs will be included.
+)
add_library(epee STATIC byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp
wipeable_string.cpp levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp
- int-util.cpp portable_storage.cpp)
+ int-util.cpp portable_storage.cpp
+ misc_language.cpp
+ file_io_utils.cpp
+ ${EPEE_HEADERS_PUBLIC}
+ )
if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW)))
add_library(epee_readline STATIC readline_buffer.cpp)
@@ -71,3 +82,6 @@ if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW)))
PRIVATE
${GNU_READLINE_LIBRARY})
endif()
+
+target_include_directories(epee PUBLIC "${EPEE_INCLUDE_DIR_BASE}")
+
diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp
index faf7689be..453b63a4c 100644
--- a/contrib/epee/src/byte_slice.cpp
+++ b/contrib/epee/src/byte_slice.cpp
@@ -36,6 +36,11 @@
#include "byte_slice.h"
#include "byte_stream.h"
+namespace
+{
+ const std::size_t page_size = 4096;
+}
+
namespace epee
{
struct byte_slice_data
@@ -173,16 +178,27 @@ namespace epee
: byte_slice(adapt_buffer{}, std::move(buffer))
{}
- byte_slice::byte_slice(byte_stream&& stream) noexcept
+ byte_slice::byte_slice(byte_stream&& stream, const bool shrink)
: storage_(nullptr), portion_(stream.data(), stream.size())
{
- if (stream.size())
+ if (portion_.size())
{
- std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice);
+ byte_buffer buf;
+ if (shrink && page_size <= stream.available())
+ {
+ buf = byte_buffer_resize(stream.take_buffer(), portion_.size());
+ if (!buf)
+ throw std::bad_alloc{};
+ portion_ = {buf.get(), portion_.size()};
+ }
+ else // no need to shrink buffer
+ buf = stream.take_buffer();
+
+ std::uint8_t* const data = buf.release() - sizeof(raw_byte_slice);
new (data) raw_byte_slice{};
storage_.reset(reinterpret_cast<raw_byte_slice*>(data));
}
- else
+ else // empty stream
portion_ = nullptr;
}
diff --git a/contrib/epee/src/file_io_utils.cpp b/contrib/epee/src/file_io_utils.cpp
new file mode 100644
index 000000000..5072adcbc
--- /dev/null
+++ b/contrib/epee/src/file_io_utils.cpp
@@ -0,0 +1,231 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "file_io_utils.h"
+
+#include <fstream>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+#ifdef WIN32
+#include <windows.h>
+#include "string_tools.h"
+#endif
+
+// On Windows there is a problem with non-ASCII characters in path and file names
+// as far as support by the standard components used is concerned:
+
+// The various file stream classes, e.g. std::ifstream and std::ofstream, are
+// part of the GNU C++ Library / libstdc++. On the most basic level they use the
+// fopen() call as defined / made accessible to programs compiled within MSYS2
+// by the stdio.h header file maintained by the MinGW project.
+
+// The critical point: The implementation of fopen() is part of MSVCRT, the
+// Microsoft Visual C/C++ Runtime Library, and this method does NOT offer any
+// Unicode support.
+
+// Monero code that would want to continue to use the normal file stream classes
+// but WITH Unicode support could therefore not solve this problem on its own,
+// but 2 different projects from 2 different maintaining groups would need changes
+// in this particular direction - something probably difficult to achieve and
+// with a long time to wait until all new versions / releases arrive.
+
+// Implemented solution approach: Circumvent the problem by stopping to use std
+// file stream classes on Windows and directly use Unicode-capable WIN32 API
+// calls. Most of the code doing so is concentrated in this header file here.
+
+namespace epee
+{
+namespace file_io_utils
+{
+
+ bool is_file_exist(const std::string& path)
+ {
+ boost::filesystem::path p(path);
+ return boost::filesystem::exists(p);
+ }
+
+
+ bool save_string_to_file(const std::string& path_to_file, const std::string& str)
+ {
+#ifdef WIN32
+ std::wstring wide_path;
+ try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
+ HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return false;
+ DWORD bytes_written;
+ DWORD bytes_to_write = (DWORD)str.size();
+ BOOL result = WriteFile(file_handle, str.data(), bytes_to_write, &bytes_written, NULL);
+ CloseHandle(file_handle);
+ if (bytes_written != bytes_to_write)
+ result = FALSE;
+ return result;
+#else
+ try
+ {
+ std::ofstream fstream;
+ fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ fstream << str;
+ fstream.close();
+ return true;
+ }
+
+ catch(...)
+ {
+ return false;
+ }
+#endif
+ }
+
+
+ bool get_file_time(const std::string& path_to_file, time_t& ft)
+ {
+ boost::system::error_code ec;
+ ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec);
+ if(!ec)
+ return true;
+ else
+ return false;
+ }
+
+
+ bool set_file_time(const std::string& path_to_file, const time_t& ft)
+ {
+ boost::system::error_code ec;
+ boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec);
+ if(!ec)
+ return true;
+ else
+ return false;
+ }
+
+
+
+ bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size)
+ {
+#ifdef WIN32
+ std::wstring wide_path;
+ try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
+ HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return false;
+ DWORD file_size = GetFileSize(file_handle, NULL);
+ if ((file_size == INVALID_FILE_SIZE) || (uint64_t)file_size > (uint64_t)max_size) {
+ CloseHandle(file_handle);
+ return false;
+ }
+ target_str.resize(file_size);
+ DWORD bytes_read;
+ BOOL result = ReadFile(file_handle, &target_str[0], file_size, &bytes_read, NULL);
+ CloseHandle(file_handle);
+ if (bytes_read != file_size)
+ result = FALSE;
+ return result;
+#else
+ try
+ {
+ std::ifstream fstream;
+ fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate);
+
+ std::ifstream::pos_type file_size = fstream.tellg();
+
+ if((uint64_t)file_size > (uint64_t)max_size) // ensure a large domain for comparison, and negative -> too large
+ return false;//don't go crazy
+ size_t file_size_t = static_cast<size_t>(file_size);
+
+ target_str.resize(file_size_t);
+
+ fstream.seekg (0, std::ios::beg);
+ fstream.read((char*)target_str.data(), target_str.size());
+ fstream.close();
+ return true;
+ }
+
+ catch(...)
+ {
+ return false;
+ }
+#endif
+ }
+
+
+ bool append_string_to_file(const std::string& path_to_file, const std::string& str)
+ {
+ // No special Windows implementation because so far not used in Monero code
+ try
+ {
+ std::ofstream fstream;
+ fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app);
+ fstream << str;
+ fstream.close();
+ return true;
+ }
+
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+
+ bool get_file_size(const std::string& path_to_file, uint64_t &size)
+ {
+#ifdef WIN32
+ std::wstring wide_path;
+ try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
+ HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return false;
+ LARGE_INTEGER file_size;
+ BOOL result = GetFileSizeEx(file_handle, &file_size);
+ CloseHandle(file_handle);
+ if (result) {
+ size = file_size.QuadPart;
+ }
+ return size;
+#else
+ try
+ {
+ std::ifstream fstream;
+ fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate);
+ size = fstream.tellg();
+ fstream.close();
+ return true;
+ }
+
+ catch(...)
+ {
+ return false;
+ }
+#endif
+ }
+
+}
+}
diff --git a/contrib/epee/src/levin_base.cpp b/contrib/epee/src/levin_base.cpp
index 5ec86b3d6..7c5cd5a78 100644
--- a/contrib/epee/src/levin_base.cpp
+++ b/contrib/epee/src/levin_base.cpp
@@ -34,6 +34,25 @@ namespace epee
{
namespace levin
{
+ message_writer::message_writer(const std::size_t reserve)
+ : buffer()
+ {
+ buffer.reserve(reserve);
+ buffer.put_n(0, sizeof(header));
+ }
+
+ byte_slice message_writer::finalize(const uint32_t command, const uint32_t flags, const uint32_t return_code, const bool expect_response)
+ {
+ if (buffer.size() < sizeof(header))
+ throw std::runtime_error{"levin_writer::finalize already called"};
+
+ header head = make_header(command, payload_size(), flags, expect_response);
+ head.m_return_code = SWAP32LE(return_code);
+
+ std::memcpy(buffer.tellp() - buffer.size(), std::addressof(head), sizeof(head));
+ return byte_slice{std::move(buffer)};
+ }
+
bucket_head2 make_header(uint32_t command, uint64_t msg_size, uint32_t flags, bool expect_response) noexcept
{
bucket_head2 head = {0};
@@ -47,12 +66,6 @@ namespace levin
return head;
}
- byte_slice make_notify(int command, epee::span<const std::uint8_t> payload)
- {
- const bucket_head2 head = make_header(command, payload.size(), LEVIN_PACKET_REQUEST, false);
- return byte_slice{epee::as_byte_span(head), payload};
- }
-
byte_slice make_noise_notify(const std::size_t noise_bytes)
{
static constexpr const std::uint32_t flags =
@@ -68,46 +81,40 @@ namespace levin
return byte_slice{std::move(buffer)};
}
- byte_slice make_fragmented_notify(const byte_slice& noise_message, int command, epee::span<const std::uint8_t> payload)
+ byte_slice make_fragmented_notify(const std::size_t noise_size, const int command, message_writer message)
{
- const size_t noise_size = noise_message.size();
if (noise_size < sizeof(bucket_head2) * 2)
return nullptr;
- if (payload.size() <= noise_size - sizeof(bucket_head2))
+ if (message.buffer.size() <= noise_size)
{
/* The entire message can be sent at once, and the levin binary parser
will ignore extra bytes. So just pad with zeroes and otherwise send
a "normal", not fragmented message. */
- const size_t padding = noise_size - sizeof(bucket_head2) - payload.size();
- const span<const uint8_t> padding_bytes{noise_message.end() - padding, padding};
- const bucket_head2 head = make_header(command, noise_size - sizeof(bucket_head2), LEVIN_PACKET_REQUEST, false);
- return byte_slice{as_byte_span(head), payload, padding_bytes};
+ message.buffer.put_n(0, noise_size - message.buffer.size());
+ return message.finalize_notify(command);
}
// fragment message
+ const byte_slice payload_bytes = message.finalize_notify(command);
+ span<const std::uint8_t> payload = to_span(payload_bytes);
+
const size_t payload_space = noise_size - sizeof(bucket_head2);
const size_t expected_fragments = ((payload.size() - 2) / payload_space) + 1;
- std::string buffer{};
- buffer.reserve((expected_fragments + 1) * noise_size); // +1 here overselects for internal bucket_head2 value
+ byte_stream buffer{};
+ buffer.reserve(expected_fragments * noise_size);
- bucket_head2 head = make_header(0, noise_size - sizeof(bucket_head2), LEVIN_PACKET_BEGIN, false);
- buffer.append(reinterpret_cast<const char*>(&head), sizeof(head));
+ bucket_head2 head = make_header(0, payload_space, LEVIN_PACKET_BEGIN, false);
+ buffer.write(as_byte_span(head));
- head.m_command = command;
- head.m_flags = LEVIN_PACKET_REQUEST;
- head.m_cb = payload.size();
- buffer.append(reinterpret_cast<const char*>(&head), sizeof(head));
+ // internal levin header is in payload already
- size_t copy_size = payload.remove_prefix(payload_space - sizeof(bucket_head2));
- buffer.append(reinterpret_cast<const char*>(payload.data()) - copy_size, copy_size);
+ size_t copy_size = payload.remove_prefix(payload_space);
+ buffer.write(payload.data() - copy_size, copy_size);
- head.m_command = 0;
head.m_flags = 0;
- head.m_cb = noise_size - sizeof(bucket_head2);
-
while (!payload.empty())
{
copy_size = payload.remove_prefix(payload_space);
@@ -115,12 +122,12 @@ namespace levin
if (payload.empty())
head.m_flags = LEVIN_PACKET_END;
- buffer.append(reinterpret_cast<const char*>(&head), sizeof(head));
- buffer.append(reinterpret_cast<const char*>(payload.data()) - copy_size, copy_size);
+ buffer.write(as_byte_span(head));
+ buffer.write(payload.data() - copy_size, copy_size);
}
const size_t padding = noise_size - copy_size - sizeof(bucket_head2);
- buffer.append(reinterpret_cast<const char*>(noise_message.end()) - padding, padding);
+ buffer.put_n(0, padding);
return byte_slice{std::move(buffer)};
}
diff --git a/contrib/epee/src/misc_language.cpp b/contrib/epee/src/misc_language.cpp
new file mode 100644
index 000000000..6e8f2daef
--- /dev/null
+++ b/contrib/epee/src/misc_language.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "misc_language.h"
+
+#include <boost/thread.hpp>
+
+namespace epee
+{
+namespace misc_utils
+{
+ bool sleep_no_w(long ms )
+ {
+ boost::this_thread::sleep(
+ boost::get_system_time() +
+ boost::posix_time::milliseconds( std::max<long>(ms,0) ) );
+
+ return true;
+ }
+}
+}
diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp
index 6ed27efa9..765dadce3 100644
--- a/contrib/epee/src/net_ssl.cpp
+++ b/contrib/epee/src/net_ssl.cpp
@@ -29,6 +29,8 @@
#include <string.h>
#include <thread>
#include <boost/asio/ssl.hpp>
+#include <boost/cerrno.hpp>
+#include <boost/filesystem/operations.hpp>
#include <boost/lambda/lambda.hpp>
#include <openssl/ssl.h>
#include <openssl/pem.h>
@@ -567,6 +569,51 @@ bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
return true;
}
+boost::system::error_code store_ssl_keys(boost::asio::ssl::context& ssl, const boost::filesystem::path& base)
+{
+ EVP_PKEY* ssl_key = nullptr;
+ X509* ssl_cert = nullptr;
+ const auto ctx = ssl.native_handle();
+ CHECK_AND_ASSERT_MES(ctx, boost::system::error_code(EINVAL, boost::system::system_category()), "Context is null");
+ CHECK_AND_ASSERT_MES(base.has_filename(), boost::system::error_code(EINVAL, boost::system::system_category()), "Need filename");
+ if (!(ssl_key = SSL_CTX_get0_privatekey(ctx)) || !(ssl_cert = SSL_CTX_get0_certificate(ctx)))
+ return {EINVAL, boost::system::system_category()};
+
+ using file_closer = int(std::FILE*);
+ boost::system::error_code error{};
+ std::unique_ptr<std::FILE, file_closer*> file{nullptr, std::fclose};
+
+ // write key file unencrypted
+ {
+ const boost::filesystem::path key_file{base.string() + ".key"};
+ file.reset(std::fopen(key_file.string().c_str(), "wb"));
+ if (!file)
+ return {errno, boost::system::system_category()};
+ boost::filesystem::permissions(key_file, boost::filesystem::owner_read, error);
+ if (error)
+ return error;
+ if (!PEM_write_PrivateKey(file.get(), ssl_key, nullptr, nullptr, 0, nullptr, nullptr))
+ return boost::asio::error::ssl_errors(ERR_get_error());
+ if (std::fclose(file.release()) != 0)
+ return {errno, boost::system::system_category()};
+ }
+
+ // write certificate file in standard SSL X.509 unencrypted
+ const boost::filesystem::path cert_file{base.string() + ".crt"};
+ file.reset(std::fopen(cert_file.string().c_str(), "wb"));
+ if (!file)
+ return {errno, boost::system::system_category()};
+ const auto cert_perms = (boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read);
+ boost::filesystem::permissions(cert_file, cert_perms, error);
+ if (error)
+ return error;
+ if (!PEM_write_X509(file.get(), ssl_cert))
+ return boost::asio::error::ssl_errors(ERR_get_error());
+ if (std::fclose(file.release()) != 0)
+ return {errno, boost::system::system_category()};
+ return error;
+}
+
} // namespace
} // namespace
diff --git a/contrib/epee/src/portable_storage.cpp b/contrib/epee/src/portable_storage.cpp
index 4534deff3..b922cc9e3 100644
--- a/contrib/epee/src/portable_storage.cpp
+++ b/contrib/epee/src/portable_storage.cpp
@@ -1,10 +1,43 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
#include "byte_slice.h"
#include "byte_stream.h"
#include "misc_log_ex.h"
#include "span.h"
#include "storages/portable_storage.h"
+#include "storages/portable_storage_to_json.h"
+#include "storages/portable_storage_from_json.h"
#include "storages/portable_storage_to_bin.h"
+#include "storages/portable_storage_from_bin.h"
+
+#include <boost/utility/string_ref.hpp>
+
+#include <string>
+#include <sstream>
namespace epee
{
@@ -15,15 +48,200 @@ namespace serialization
TRY_ENTRY();
byte_stream ss;
ss.reserve(initial_buffer_size);
+ store_to_binary(ss);
+ target = epee::byte_slice{std::move(ss)};
+ return true;
+ CATCH_ENTRY("portable_storage::store_to_binary", false);
+ }
+
+ bool portable_storage::store_to_binary(byte_stream& ss)
+ {
+ TRY_ENTRY();
storage_block_header sbh{};
sbh.m_signature_a = SWAP32LE(PORTABLE_STORAGE_SIGNATUREA);
sbh.m_signature_b = SWAP32LE(PORTABLE_STORAGE_SIGNATUREB);
sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER;
ss.write(epee::as_byte_span(sbh));
pack_entry_to_buff(ss, m_root);
- target = epee::byte_slice{std::move(ss)};
return true;
- CATCH_ENTRY("portable_storage::store_to_binary", false)
+ CATCH_ENTRY("portable_storage::store_to_binary", false);
}
+
+ bool portable_storage::dump_as_json(std::string& buff, size_t indent, bool insert_newlines)
+ {
+ TRY_ENTRY();
+ std::stringstream ss;
+ epee::serialization::dump_as_json(ss, m_root, indent, insert_newlines);
+ buff = ss.str();
+ return true;
+ CATCH_ENTRY("portable_storage::dump_as_json", false)
+ }
+
+ bool portable_storage::load_from_json(const std::string& source)
+ {
+ TRY_ENTRY();
+ return json::load_from_json(source, *this);
+ CATCH_ENTRY("portable_storage::load_from_json", false)
+ }
+
+ bool portable_storage::load_from_binary(const epee::span<const uint8_t> source, const limits_t *limits)
+ {
+ m_root.m_entries.clear();
+ if(source.size() < sizeof(storage_block_header))
+ {
+ LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header));
+ return false;
+ }
+ storage_block_header* pbuff = (storage_block_header*)source.data();
+ if(pbuff->m_signature_a != SWAP32LE(PORTABLE_STORAGE_SIGNATUREA) ||
+ pbuff->m_signature_b != SWAP32LE(PORTABLE_STORAGE_SIGNATUREB)
+ )
+ {
+ LOG_ERROR("portable_storage: wrong binary format - signature mismatch");
+ return false;
+ }
+ if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER)
+ {
+ LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver);
+ return false;
+ }
+ TRY_ENTRY();
+ throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header));
+ if (limits)
+ buf_reader.set_limits(limits->n_objects, limits->n_fields, limits->n_strings);
+ buf_reader.read(m_root);
+ return true;//TODO:
+ CATCH_ENTRY("portable_storage::load_from_binary", false);
+ }
+
+ hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist)
+ {
+ TRY_ENTRY();
+ hparent_section = hparent_section ? hparent_section:&m_root;
+ storage_entry* pentry = find_storage_entry(section_name, hparent_section);
+ if(!pentry)
+ {
+ if(!create_if_notexist)
+ return nullptr;
+ return insert_new_section(section_name, hparent_section);
+ }
+ CHECK_AND_ASSERT(pentry , nullptr);
+ //check that section_entry we find is real "CSSection"
+ if(pentry->type() != typeid(section))
+ {
+ if(create_if_notexist)
+ *pentry = storage_entry(section());//replace
+ else
+ return nullptr;
+ }
+ return &boost::get<section>(*pentry);
+ CATCH_ENTRY("portable_storage::open_section", nullptr);
+ }
+
+ bool portable_storage::get_value(const std::string& value_name, storage_entry& val, hsection hparent_section)
+ {
+ //TRY_ENTRY();
+ if(!hparent_section) hparent_section = &m_root;
+ storage_entry* pentry = find_storage_entry(value_name, hparent_section);
+ if(!pentry)
+ return false;
+
+ val = *pentry;
+ return true;
+ //CATCH_ENTRY("portable_storage::template<>get_value", false);
+ }
+
+ storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection)
+ {
+ TRY_ENTRY();
+ CHECK_AND_ASSERT(psection, nullptr);
+ auto it = psection->m_entries.find(pentry_name);
+ if(it == psection->m_entries.end())
+ return nullptr;
+
+ return &it->second;
+ CATCH_ENTRY("portable_storage::find_storage_entry", nullptr);
+ }
+
+ hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection)
+ {
+ TRY_ENTRY();
+ storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section());
+ if(!pse) return nullptr;
+ return &boost::get<section>(*pse);
+ CATCH_ENTRY("portable_storage::insert_new_section", nullptr);
+ }
+
+ harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section)
+ {
+ TRY_ENTRY();
+ if(!hparent_section) hparent_section = &m_root;
+ storage_entry* pentry = find_storage_entry(sec_name, hparent_section);
+ if(!pentry)
+ return nullptr;
+ if(pentry->type() != typeid(array_entry))
+ return nullptr;
+ array_entry& ar_entry = boost::get<array_entry>(*pentry);
+ if(ar_entry.type() != typeid(array_entry_t<section>))
+ return nullptr;
+ array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry);
+ section* psec = sec_array.get_first_val();
+ if(!psec)
+ return nullptr;
+ h_child_section = psec;
+ return &ar_entry;
+ CATCH_ENTRY("portable_storage::get_first_section", nullptr);
+ }
+
+ bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section)
+ {
+ TRY_ENTRY();
+ CHECK_AND_ASSERT(hsec_array, false);
+ if(hsec_array->type() != typeid(array_entry_t<section>))
+ return false;
+ array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array);
+ h_child_section = sec_array.get_next_val();
+ if(!h_child_section)
+ return false;
+ return true;
+ CATCH_ENTRY("portable_storage::get_next_section", false);
+ }
+
+ harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section)
+ {
+ TRY_ENTRY();
+ if(!hparent_section) hparent_section = &m_root;
+ storage_entry* pentry = find_storage_entry(sec_name, hparent_section);
+ if(!pentry)
+ {
+ pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t<section>()));
+ if(!pentry)
+ return nullptr;
+ }
+ if(pentry->type() != typeid(array_entry))
+ *pentry = storage_entry(array_entry(array_entry_t<section>()));
+
+ array_entry& ar_entry = boost::get<array_entry>(*pentry);
+ if(ar_entry.type() != typeid(array_entry_t<section>))
+ ar_entry = array_entry(array_entry_t<section>());
+
+ array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry);
+ hinserted_childsection = &sec_array.insert_first_val(section());
+ return &ar_entry;
+ CATCH_ENTRY("portable_storage::insert_first_section", nullptr);
+ }
+
+ bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection)
+ {
+ TRY_ENTRY();
+ CHECK_AND_ASSERT(hsec_array, false);
+ CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t<section>),
+ false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name());
+
+ array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array);
+ hinserted_childsection = &sec_array.insert_next_value(section());
+ return true;
+ CATCH_ENTRY("portable_storage::insert_next_section", false);
+ }
}
}