new_build_system (#18)
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				BlockStorage/repertory/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	BlockStorage/repertory/pipeline/head This commit looks good
				
			Reviewed-on: #18
This commit is contained in:
		
							
								
								
									
										2
									
								
								support/3rd_party/boost_1_85_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								support/3rd_party/boost_1_85_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | ||||
| be0d91732d5b0cc6fbb275c7939974457e79b54d6f07ce2e3dfdd68bef883b0b  ./boost_1_85_0.tar.gz | ||||
| be0d91732d5b0cc6fbb275c7939974457e79b54d6f07ce2e3dfdd68bef883b0b  boost_1_85_0.tar.gz | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.16.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.16.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +0,0 @@ | ||||
| c125022eb85eaa12235518dc4638be93b62c3216d0f87b655af7b17b71b38851 *cpp-httplib-0.16.0.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.16.3.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.16.3.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/cpp-httplib-0.16.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/cpp-httplib-0.16.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| c1742fc7179aaae2a67ad9bba0740b7e9ffaf4f5e62feef53101ecdef1478716  cpp-httplib-0.16.3.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.8.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.8.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/curl-8.8.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/curl-8.8.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| 77c0e1cd35ab5b45b659645a93b46d660224d0024f1185e8a95cdb27ae3d787d *curl-8.8.0.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.9.1.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.9.1.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/curl-8.9.1.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/curl-8.9.1.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| d714818f6ac41ae9154850158fed44b7a87650a6d52f83d3bcb9aa527be354d7  curl-8.9.1.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +0,0 @@ | ||||
| 7315acb6bf10e99f332c8a43f00d5fbb1ee6ca48c52f6b936991b216c586aaad *googletest-1.15.0.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.2.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.2.tar.gz
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/googletest-1.15.2.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/googletest-1.15.2.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926  googletest-1.15.2.tar.gz | ||||
							
								
								
									
										2
									
								
								support/3rd_party/json-3.11.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								support/3rd_party/json-3.11.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | ||||
| 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406 *json-3.11.3.tar.gz | ||||
| 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406  json-3.11.3.tar.gz | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/libsodium-1.0.20.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/libsodium-1.0.20.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +1 @@ | ||||
| ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19 *libsodium-1.0.20.tar.gz | ||||
| 8e5aeca07a723a27bbecc3beef14b0068d37e7fc0e97f51b3f1c82d2a58005c1  libsodium-1.0.20.tar.gz | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| ae9a5789e23459e59606e6714723f2d3ffc31c03174191ef0d015bdf06007450 *binutils-2.41.tar.xz | ||||
| ae9a5789e23459e59606e6714723f2d3ffc31c03174191ef0d015bdf06007450  binutils-2.41.tar.xz | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/expat-2.6.2.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/expat-2.6.2.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +1 @@ | ||||
| d4cf38d26e21a56654ffe4acd9cd5481164619626802328506a2869afab29ab3  expat-2.6.2.tar.gz | ||||
| fbd032683370d761ba68dba2566d3280a154f5290634172d60a79b24d366d9dc  expat-2.6.2.tar.gz | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| 8cb4be3796651976f94b9356fa08d833524f62420d6292c5033a9a26af315078  gcc-13.2.0.tar.gz | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/mingw64/gcc-14.2.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/mingw64/gcc-14.2.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 7d376d445f93126dc545e2c0086d0f647c3094aae081cdb78f42ce2bc25e7293  gcc-14.2.0.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/mingw-w64-11.0.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/mingw-w64-11.0.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +0,0 @@ | ||||
| 9c82a58713786c95b0594443a1c6190cd986401ed2b03677a90acc470140af28 *mingw-w64-11.0.1.tar.gz | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/mingw-w64-v11.0.1.tar.bz2
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/mingw-w64-v11.0.1.tar.bz2
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/mingw64/mingw-w64-v11.0.1.tar.bz2.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/mingw64/mingw-w64-v11.0.1.tar.bz2.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 3f66bce069ee8bed7439a1a13da7cb91a5e67ea6170f21317ac7f5794625ee10  mingw-w64-v11.0.1.tar.bz2 | ||||
| @@ -1 +1 @@ | ||||
| 6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591 *pkg-config-0.29.2.tar.gz | ||||
| 6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591  pkg-config-0.29.2.tar.gz | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/zlib-1.3.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/mingw64/zlib-1.3.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +1 @@ | ||||
| 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23  zlib-1.3.1.tar.gz | ||||
| 17e88863f3600672ab49182f217281b6fc4d3c762bde361935e436a95214d05c  zlib-1.3.1.tar.gz | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 777cd596284c883375a2a7a11bf5d2786fc5413255efab20c50d6ffe6d020b7e *openssl-3.3.1.tar.gz | ||||
| 777cd596284c883375a2a7a11bf5d2786fc5413255efab20c50d6ffe6d020b7e  openssl-3.3.1.tar.gz | ||||
|   | ||||
							
								
								
									
										2
									
								
								support/3rd_party/pugixml-1.14.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								support/3rd_party/pugixml-1.14.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1 @@ | ||||
| 2f10e276870c64b1db6809050a75e11a897a8d7456c4be5c6b2e35a11168a015  ./pugixml-1.14.tar.gz | ||||
| 2f10e276870c64b1db6809050a75e11a897a8d7456c4be5c6b2e35a11168a015  pugixml-1.14.tar.gz | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b *spdlog-1.14.1.tar.gz | ||||
| 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b  spdlog-1.14.1.tar.gz | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/sqlite-amalgamation-3460000.zip
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/sqlite-amalgamation-3460000.zip
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1 +0,0 @@ | ||||
| 712a7d09d2a22652fb06a49af516e051979a3984adb067da86760e60ed51a7f5 *sqlite-amalgamation-3460000.zip | ||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/sqlite-amalgamation-3460100.zip
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/sqlite-amalgamation-3460100.zip
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/sqlite-amalgamation-3460100.zip.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/sqlite-amalgamation-3460100.zip.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784  sqlite-amalgamation-3460100.zip | ||||
| @@ -1 +1 @@ | ||||
| b1176597e789531c38481acbbed2a6894ad419aab0979c10410d59eb0ebf40d3 *stduuid-1.2.3.tar.gz | ||||
| b1176597e789531c38481acbbed2a6894ad419aab0979c10410d59eb0ebf40d3  stduuid-1.2.3.tar.gz | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -24,13 +24,19 @@ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/base64.hpp" | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/com_init_wrapper.hpp" | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/encrypting_reader.hpp" | ||||
| #include "utils/encryption.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/file.hpp" | ||||
| #include "utils/hash.hpp" | ||||
| #include "utils/path.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #include "utils/time.hpp" | ||||
| #include "utils/unix.hpp" | ||||
| #include "utils/windows.hpp" | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_ALL_HPP_ | ||||
| #endif // REPERTORY_INCLUDE_UTILS_ALL_HPP_ | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/string.hpp" | ||||
|  | ||||
| namespace repertory::utils::collection { | ||||
| @@ -69,24 +70,75 @@ inline auto includes(const col_t &collection, | ||||
|          collection.end(); | ||||
| } | ||||
|  | ||||
| template <typename val_t, typename string_t> | ||||
| [[nodiscard]] inline auto | ||||
| from_hex_string_t(std::basic_string_view<typename string_t::value_type> str, | ||||
|                   val_t &val) -> bool { | ||||
| template <typename val_t> | ||||
| [[nodiscard]] inline auto from_hex_string_t(std::string_view str, | ||||
|                                             val_t &val) -> bool { | ||||
|   static constexpr const auto base16{16}; | ||||
|  | ||||
|   val.clear(); | ||||
|   if (not(str.length() % 2U)) { | ||||
|     for (std::size_t i = 0U; i < str.length(); i += 2U) { | ||||
|       val.emplace_back(static_cast<typename val_t::value_type>( | ||||
|           std::strtol(string_t{str.substr(i, 2U)}.c_str(), nullptr, base16))); | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     val.clear(); | ||||
|  | ||||
|     std::string fmt_val{str}; | ||||
|     utils::string::trim(fmt_val); | ||||
|     if (fmt_val.empty()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     fmt_val = utils::string::to_lower(fmt_val); | ||||
|     if (utils::string::begins_with(fmt_val, "0x")) { | ||||
|       fmt_val = fmt_val.substr(2U); | ||||
|     } | ||||
|  | ||||
|     if (fmt_val.empty()) { | ||||
|       throw std::runtime_error("hex string is invalid|" + std::string{str}); | ||||
|     } | ||||
|  | ||||
|     if (fmt_val.length() % 2U) { | ||||
|       fmt_val = '0' + fmt_val; | ||||
|     } | ||||
|  | ||||
|     auto iter = std::find_if_not( | ||||
|         fmt_val.begin(), fmt_val.end(), [](auto cur_char) -> bool { | ||||
|           auto check = static_cast<std::uint32_t>(cur_char); | ||||
|           return ((check >= 48U && check <= 57U) || | ||||
|                   (check >= 97U && check <= 102U)); | ||||
|         }); | ||||
|     if (iter != fmt_val.end()) { | ||||
|       auto invalid_idx{std::distance(fmt_val.begin(), iter)}; | ||||
|       throw std::range_error( | ||||
|           "invalid character in hex string|" + std::to_string(invalid_idx) + | ||||
|           '|' + std::string(1U, str.at(invalid_idx)) + '|' + std::string{str}); | ||||
|     } | ||||
|  | ||||
|     val.resize(fmt_val.length() / 2U); | ||||
|     for (std::size_t idx = 0U; idx < fmt_val.length(); idx += 2U) { | ||||
|       val.at(idx / 2U) = static_cast<typename val_t::value_type>( | ||||
|           std::strtoul(fmt_val.substr(idx, 2U).c_str(), nullptr, base16)); | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   val.clear(); | ||||
|   return false; | ||||
| } | ||||
| template <typename val_t> | ||||
| inline auto from_hex_string(std::string_view str, val_t &val) -> bool { | ||||
|   return from_hex_string_t<val_t>(str, val); | ||||
| } | ||||
|  | ||||
| template <typename val_t> | ||||
| inline auto from_hex_string(std::wstring_view str, val_t &val) -> bool { | ||||
|   return from_hex_string_t<val_t>(utils::string::to_utf8(str), val); | ||||
| } | ||||
|  | ||||
| template <typename col_t> | ||||
| inline auto remove_element(col_t &collection, | ||||
| @@ -96,16 +148,6 @@ inline auto remove_element(col_t &collection, | ||||
|   return collection; | ||||
| } | ||||
|  | ||||
| template <typename val_t> | ||||
| inline auto from_hex_string(std::string_view str, val_t &val) -> bool { | ||||
|   return from_hex_string_t<val_t, std::string>(str, val); | ||||
| } | ||||
|  | ||||
| template <typename val_t> | ||||
| inline auto from_hex_string(std::wstring_view str, val_t &val) -> bool { | ||||
|   return from_hex_string_t<val_t, std::wstring>(str, val); | ||||
| } | ||||
|  | ||||
| template <typename col_t> | ||||
| inline auto to_hex_string(const col_t &collection) -> std::string { | ||||
|   static_assert(sizeof(typename col_t::value_type) == 1U, | ||||
|   | ||||
| @@ -33,6 +33,8 @@ struct result final { | ||||
|   [[nodiscard]] operator bool() const { return ok; } | ||||
| }; | ||||
|  | ||||
| using retryable_action_t = std::function<bool()>; | ||||
|  | ||||
| [[nodiscard]] inline constexpr auto | ||||
| calculate_read_size(std::uint64_t total_size, std::size_t read_size, | ||||
|                     std::uint64_t offset) -> std::size_t { | ||||
| @@ -60,22 +62,20 @@ template <typename result_t, typename data_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| divide_with_ceiling(result_t numerator, data_t denominator) -> result_t; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| template <typename data_t> | ||||
| [[nodiscard]] inline auto generate_random() -> data_t; | ||||
|  | ||||
| template <typename data_t> | ||||
| [[nodiscard]] inline auto generate_random(std::size_t size) -> data_t; | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| template <typename data_t> | ||||
| [[nodiscard]] inline auto generate_random_between(data_t begin, | ||||
|                                                   data_t end) -> data_t; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| [[nodiscard]] auto generate_random_string(std::size_t length) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto generate_random_wstring(std::size_t length) -> std::wstring; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| template <typename data_t> | ||||
| [[nodiscard]] inline auto generate_secure_random() -> data_t; | ||||
|  | ||||
| template <typename data_t> | ||||
| [[nodiscard]] inline auto generate_secure_random(std::size_t size) -> data_t; | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| @@ -90,6 +90,11 @@ get_next_available_port(std::uint16_t first_port, | ||||
|                         std::uint16_t &available_port) -> bool; | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| [[nodiscard]] auto retry_action(retryable_action_t action, | ||||
|                                 std::size_t retry_count = 200U, | ||||
|                                 std::chrono::milliseconds retry_wait = | ||||
|                                     std::chrono::milliseconds(10)) -> bool; | ||||
|  | ||||
| template <typename result_t, typename data_t> | ||||
| inline constexpr auto divide_with_ceiling(result_t numerator, | ||||
|                                           data_t denominator) -> result_t { | ||||
| @@ -101,26 +106,6 @@ inline constexpr auto divide_with_ceiling(result_t numerator, | ||||
|              : (numerator / denominator) + (numerator % denominator != 0); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| template <typename data_t> inline auto generate_random() -> data_t { | ||||
|   static_assert(!is_collection<std::decay_t<data_t>>::value, | ||||
|                 "data_t is a vector or collection"); | ||||
|   data_t ret{}; | ||||
|   randombytes_buf(&ret, sizeof(ret)); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| template <typename data_t> | ||||
| inline auto generate_random(std::size_t size) -> data_t { | ||||
|   static_assert(is_collection<std::decay_t<data_t>>::value, | ||||
|                 "data_t is not a vector or collection"); | ||||
|   data_t ret; | ||||
|   ret.resize(size); | ||||
|   randombytes_buf(ret.data(), ret.size() * sizeof(typename data_t::value_type)); | ||||
|   return ret; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| template <typename data_t> | ||||
| inline auto generate_random_between(data_t begin, data_t end) -> data_t { | ||||
|   static_assert(std::is_integral_v<std::remove_cv_t<data_t>>, | ||||
| @@ -129,10 +114,31 @@ inline auto generate_random_between(data_t begin, data_t end) -> data_t { | ||||
|     throw std::range_error("end must be greater than begin"); | ||||
|   } | ||||
|  | ||||
|   static std::mt19937 gen(std::random_device{}()); | ||||
|   thread_local std::mt19937 gen( | ||||
|       static_cast<unsigned long>(std::time(nullptr) ^ std::random_device{}())); | ||||
|   std::uniform_int_distribution<data_t> dis(begin, end); | ||||
|   return dis(gen); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| template <typename data_t> inline auto generate_secure_random() -> data_t { | ||||
|   static_assert(!is_collection<std::decay_t<data_t>>::value, | ||||
|                 "data_t is a vector or collection"); | ||||
|   data_t ret{}; | ||||
|   randombytes_buf(&ret, sizeof(ret)); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| template <typename data_t> | ||||
| inline auto generate_secure_random(std::size_t size) -> data_t { | ||||
|   static_assert(is_collection<std::decay_t<data_t>>::value, | ||||
|                 "data_t is not a vector or collection"); | ||||
|   data_t ret; | ||||
|   ret.resize(size); | ||||
|   randombytes_buf(ret.data(), ret.size() * sizeof(typename data_t::value_type)); | ||||
|   return ret; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| } // namespace repertory::utils | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_COMMON_HPP_ | ||||
|   | ||||
| @@ -22,6 +22,8 @@ | ||||
| #ifndef REPERTORY_INCLUDE_UTILS_CONFIG_HPP_ | ||||
| #define REPERTORY_INCLUDE_UTILS_CONFIG_HPP_ | ||||
|  | ||||
| #define NOMINMAX | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| #define WINVER 0x0602 | ||||
| #define _WIN32_WINNT WINVER | ||||
| @@ -163,6 +165,50 @@ extern "C" { | ||||
| #endif // defined(__cplusplus) | ||||
| #endif // defined(PROJECT_ENABLE_FZF) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| #if defined(__cplusplus) | ||||
| extern "C" { | ||||
| #endif // defined(__cplusplus) | ||||
| #include "bdsm/bdsm.h" | ||||
| #if defined(__cplusplus) | ||||
| } | ||||
|  | ||||
| struct netbios_ns_deleter final { | ||||
|   void operator()(netbios_ns *ns) { | ||||
|     if (ns != nullptr) { | ||||
|       netbios_ns_destroy(ns); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using netbios_ns_t = std::unique_ptr<netbios_ns, netbios_ns_deleter>; | ||||
|  | ||||
| inline const auto smb_session_deleter = [](smb_session *session) { | ||||
|   if (session != nullptr) { | ||||
|     smb_session_destroy(session); | ||||
|   } | ||||
| }; | ||||
| using smb_session_t = std::shared_ptr<smb_session>; | ||||
|  | ||||
| struct smb_stat_deleter final { | ||||
|   void operator()(smb_stat st) { | ||||
|     if (st != nullptr) { | ||||
|       smb_stat_destroy(st); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using smb_stat_t = std::unique_ptr<smb_file, smb_stat_deleter>; | ||||
|  | ||||
| struct smb_stat_list_deleter final { | ||||
|   void operator()(smb_file *list) { | ||||
|     if (list != nullptr) { | ||||
|       smb_stat_list_destroy(list); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using smb_stat_list_t = std::unique_ptr<smb_file, smb_stat_list_deleter>; | ||||
| #endif // defined(__cplusplus) | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBEVENT) | ||||
| #include "event2/buffer.h" | ||||
| #include "event2/bufferevent.h" | ||||
| @@ -171,20 +217,68 @@ extern "C" { | ||||
| #include "event2/util.h" | ||||
| #endif // defined(PROJECT_ENABLE_LIBEVENT) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| #include "sodium.h" | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_SDL) | ||||
| #include "SDL.h" | ||||
| #include "SDL_gamecontroller.h" | ||||
| #include "SDL_joystick.h" | ||||
| #endif // defined(PROJECT_ENABLE_SDL) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| #include "sodium.h" | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_SQLITE) | ||||
| #include "sqlite3.h" | ||||
| #endif // defined(PROJECT_ENABLE_SQLITE) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_VLC) | ||||
| #include <vlc/vlc.h> | ||||
|  | ||||
| #if defined(__cplusplus) | ||||
| [[nodiscard]] inline auto get_libvlc_error_msg() -> std::string { | ||||
|   const auto *msg = libvlc_errmsg(); | ||||
|   return msg == nullptr ? "none" : msg; | ||||
| } | ||||
|  | ||||
| struct vlc_deleter final { | ||||
|   void operator()(libvlc_instance_t *inst) { | ||||
|     if (inst != nullptr) { | ||||
|       libvlc_release(inst); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using vlc_t = std::unique_ptr<libvlc_instance_t, vlc_deleter>; | ||||
|  | ||||
| struct vlc_media_deleter final { | ||||
|   void operator()(libvlc_media_t *media) { | ||||
|     if (media != nullptr) { | ||||
|       libvlc_media_release(media); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using vlc_media_t = std::unique_ptr<libvlc_media_t, vlc_media_deleter>; | ||||
|  | ||||
| struct vlc_media_list_deleter final { | ||||
|   void operator()(libvlc_media_list_t *media_list) { | ||||
|     if (media_list != nullptr) { | ||||
|       libvlc_media_list_release(media_list); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using vlc_media_list_t = | ||||
|     std::unique_ptr<libvlc_media_list_t, vlc_media_list_deleter>; | ||||
|  | ||||
| struct vlc_string_deleter final { | ||||
|   void operator()(char *str) { | ||||
|     if (str != nullptr) { | ||||
|       libvlc_free(str); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| using vlc_string_t = std::unique_ptr<char, vlc_string_deleter>; | ||||
| #endif // defined(__cplusplus) | ||||
| #endif // defined(PROJECT_ENABLE_VLC) | ||||
|  | ||||
| #if !defined(fstat64) | ||||
| #define fstat64 fstat | ||||
| #endif // !defined(fstat64) | ||||
| @@ -231,6 +325,14 @@ extern "C" { | ||||
| #include "boost/serialization/vector.hpp" | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_CLI11) | ||||
| #if defined(PROJECT_IS_MINGW) && !defined(PROJECT_IS_MINGW_UNIX) | ||||
| #include "CLI/CLI.hpp" | ||||
| #else // !defined(PROJECT_IS_MINGW) || defined(PROJECT_IS_MINGW_UNIX) | ||||
| #include "CLI11.hpp" | ||||
| #endif // defined(PROJECT_IS_MINGW) && !defined(PROJECT_IS_MINGW_UNIX) | ||||
| #endif // defined(PROJECT_ENABLE_CLI11) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_CPP_HTTPLIB) | ||||
| #include "httplib.h" | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
| @@ -300,9 +402,26 @@ namespace repertory { | ||||
| using data_buffer = std::vector<unsigned char>; | ||||
| using mutex_lock = std::lock_guard<std::mutex>; | ||||
| using recur_mutex_lock = std::lock_guard<std::recursive_mutex>; | ||||
| using stop_type = std::atomic_bool; | ||||
| using unique_mutex_lock = std::unique_lock<std::mutex>; | ||||
| using unique_recur_mutex_lock = std::unique_lock<std::recursive_mutex>; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| #if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
| inline constexpr const auto max_path_length = std::size_t{32767U}; | ||||
| #else  // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
| inline constexpr const auto max_path_length = std::size_t{MAX_PATH}; | ||||
| #endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|  | ||||
| using native_handle = HANDLE; | ||||
| #else // !defined(_WIN32) | ||||
| inline constexpr const auto max_path_length = std::size_t{PATH_MAX}; | ||||
| using native_handle = int; | ||||
| #if !defined(INVALID_HANDLE_VALUE) | ||||
| #define INVALID_HANDLE_VALUE (-1) | ||||
| #endif // !defined(INVALID_HANDLE_VALUE) | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| template <class... Ts> struct overloaded : Ts... { | ||||
|   using Ts::operator()...; | ||||
| }; | ||||
| @@ -329,6 +448,17 @@ struct file_deleter final { | ||||
|   } | ||||
| }; | ||||
| using file_t = std::unique_ptr<FILE, file_deleter>; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_CURL) | ||||
| struct http_range final { | ||||
|   std::uint64_t begin{}; | ||||
|   std::uint64_t end{}; | ||||
| }; | ||||
|  | ||||
| using http_headers = std::unordered_map<std::string, std::string>; | ||||
| using http_query_parameters = std::map<std::string, std::string>; | ||||
| using http_ranges = std::vector<http_range>; | ||||
| #endif // defined(PROJECT_ENABLE_CURL) | ||||
| } // namespace repertory | ||||
| #endif // defined(__cplusplus) | ||||
|  | ||||
|   | ||||
							
								
								
									
										145
									
								
								support/include/utils/encrypting_reader.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								support/include/utils/encrypting_reader.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #ifndef REPERTORY_INCLUDE_UTILS_ENCRYPTING_READER_HPP_ | ||||
| #define REPERTORY_INCLUDE_UTILS_ENCRYPTING_READER_HPP_ | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/hash.hpp" | ||||
| #include "utils/types/file/i_file.hpp" | ||||
|  | ||||
| namespace repertory::utils::encryption { | ||||
| class encrypting_reader final { | ||||
| public: | ||||
|   encrypting_reader(std::string_view file_name, std::string_view source_path, | ||||
|                     stop_type &stop_requested, std::string_view token, | ||||
|                     std::optional<std::string> relative_parent_path, | ||||
|                     std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, stop_type &stop_requested, | ||||
|                     std::string_view token, std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader( | ||||
|       std::string_view encrypted_file_path, std::string_view source_path, | ||||
|       stop_type &stop_requested, std::string_view token, | ||||
|       std::vector<std::array<unsigned char, | ||||
|                              crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|           iv_list, | ||||
|       std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(const encrypting_reader &reader); | ||||
|   encrypting_reader(encrypting_reader &&) = delete; | ||||
|  | ||||
|   auto operator=(const encrypting_reader &) -> encrypting_reader & = delete; | ||||
|   auto operator=(encrypting_reader &&) -> encrypting_reader & = delete; | ||||
|  | ||||
|   ~encrypting_reader() noexcept = default; | ||||
|  | ||||
| public: | ||||
|   using iostream = std::basic_iostream<char, std::char_traits<char>>; | ||||
|   using streambuf = std::basic_streambuf<char, std::char_traits<char>>; | ||||
|  | ||||
| private: | ||||
|   utils::encryption::hash_256_t key_; | ||||
|   stop_type &stop_requested_; | ||||
|   size_t error_return_; | ||||
|   std::unique_ptr<utils::file::i_file> source_file_; | ||||
|  | ||||
| private: | ||||
|   std::unordered_map<std::size_t, data_buffer> chunk_buffers_; | ||||
|   std::string encrypted_file_name_; | ||||
|   std::string encrypted_file_path_; | ||||
|   std::vector< | ||||
|       std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|       iv_list_; | ||||
|   std::size_t last_data_chunk_{}; | ||||
|   std::size_t last_data_chunk_size_{}; | ||||
|   std::uint64_t read_offset_{}; | ||||
|   std::uint64_t total_size_{}; | ||||
|  | ||||
| private: | ||||
|   static const std::size_t header_size_; | ||||
|   static const std::size_t data_chunk_size_; | ||||
|   static const std::size_t encrypted_chunk_size_; | ||||
|  | ||||
| private: | ||||
|   auto reader_function(char *buffer, size_t size, size_t nitems) -> size_t; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] static auto | ||||
|   calculate_decrypted_size(std::uint64_t total_size) -> std::uint64_t; | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   calculate_encrypted_size(std::string_view source_path) -> std::uint64_t; | ||||
|  | ||||
|   [[nodiscard]] auto create_iostream() const -> std::shared_ptr<iostream>; | ||||
|  | ||||
|   [[nodiscard]] static constexpr auto | ||||
|   get_encrypted_chunk_size() -> std::size_t { | ||||
|     return encrypted_chunk_size_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] static constexpr auto get_data_chunk_size() -> std::size_t { | ||||
|     return data_chunk_size_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_encrypted_file_name() const -> std::string { | ||||
|     return encrypted_file_name_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_encrypted_file_path() const -> std::string { | ||||
|     return encrypted_file_path_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_error_return() const -> std::size_t { | ||||
|     return error_return_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_iv_list() | ||||
|       -> std::vector<std::array<unsigned char, | ||||
|                                 crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> { | ||||
|     return iv_list_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_stop_requested() const -> bool { | ||||
|     return stop_requested_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_total_size() const -> std::uint64_t { | ||||
|     return total_size_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] static auto reader_function(char *buffer, size_t size, | ||||
|                                             size_t nitems, | ||||
|                                             void *instream) -> size_t { | ||||
|     return reinterpret_cast<encrypting_reader *>(instream)->reader_function( | ||||
|         buffer, size, nitems); | ||||
|   } | ||||
|  | ||||
|   void set_read_position(std::uint64_t position) { read_offset_ = position; } | ||||
| }; | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| #endif // REPERTORY_INCLUDE_UTILS_ENCRYPTING_READER_HPP_ | ||||
| @@ -33,38 +33,30 @@ inline constexpr const std::uint32_t encryption_header_size{ | ||||
|         crypto_aead_xchacha20poly1305_IETF_ABYTES, | ||||
| }; | ||||
|  | ||||
| template <typename hash_t> | ||||
| [[nodiscard]] inline auto default_create_hash(std::string_view) -> hash_t; | ||||
|  | ||||
| template <typename hash_t> | ||||
| [[nodiscard]] inline auto default_create_hash(std::wstring_view) -> hash_t; | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline auto generate_key( | ||||
|     std::string_view password, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) -> hash_t; | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) -> hash_t; | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline auto generate_key( | ||||
|     std::wstring_view password, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) -> hash_t; | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) -> hash_t; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_BOOST) | ||||
| [[nodiscard]] auto decrypt_data(std::string_view password, | ||||
|                                 std::string_view data) -> data_buffer; | ||||
| [[nodiscard]] auto decrypt_file_name(std::string_view encryption_token, | ||||
|                                      std::string &file_name) -> bool; | ||||
|  | ||||
| [[nodiscard]] auto encrypt_data(std::string_view password, | ||||
|                                 std::string_view data) -> data_buffer; | ||||
| [[nodiscard]] auto decrypt_file_path(std::string_view encryption_token, | ||||
|                                      std::string &file_path) -> bool; | ||||
|  | ||||
| template <typename result, typename arr_t, std::size_t arr_size> | ||||
| template <typename result_t, typename arr_t, std::size_t arr_size> | ||||
| [[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key, | ||||
|                                        const unsigned char *buffer, | ||||
|                                        std::size_t buffer_size, | ||||
|                                        result &res) -> bool { | ||||
|                                        result_t &res) -> bool { | ||||
|   if (buffer_size > encryption_header_size) { | ||||
|     const std::uint32_t size = | ||||
|         boost::endian::native_to_big(static_cast<std::uint32_t>(buffer_size)); | ||||
| @@ -80,43 +72,42 @@ template <typename result, typename arr_t, std::size_t arr_size> | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| template <typename buffer, typename result, typename arr_t, | ||||
| template <typename buffer_t, typename result_t, typename arr_t, | ||||
|           std::size_t arr_size> | ||||
| [[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key, | ||||
|                                        const buffer &buf, result &res) -> bool { | ||||
|   return decrypt_data<result>( | ||||
|                                        const buffer_t &buf, | ||||
|                                        result_t &res) -> bool { | ||||
|   return decrypt_data<result_t>( | ||||
|       key, reinterpret_cast<const unsigned char *>(buf.data()), buf.size(), | ||||
|       res); | ||||
| } | ||||
|  | ||||
| template <typename buffer, typename result, typename hash_t = hash_256_t> | ||||
| template <typename buffer_t, typename result_t, typename hash_t = hash_256_t> | ||||
| [[nodiscard]] inline auto decrypt_data( | ||||
|     std::string_view password, const buffer &buf, result &res, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) -> bool { | ||||
|   return decrypt_data<buffer, result>(generate_key(password, hasher), buf, res); | ||||
|     std::string_view password, const buffer_t &buf, result_t &res, | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) -> bool { | ||||
|   return decrypt_data<buffer_t, result_t>(generate_key(password, hasher), buf, | ||||
|                                           res); | ||||
| } | ||||
|  | ||||
| template <typename result, typename hash_t = hash_256_t> | ||||
| template <typename result_t, typename hash_t = hash_256_t> | ||||
| [[nodiscard]] inline auto decrypt_data( | ||||
|     std::string_view password, const unsigned char *buffer, | ||||
|     std::size_t buffer_size, result &res, | ||||
|  | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) -> bool { | ||||
|   return decrypt_data<result>(generate_key(password, hasher), buffer, | ||||
|                               buffer_size, res); | ||||
|     std::size_t buffer_size, result_t &res, | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) -> bool { | ||||
|   return decrypt_data<result_t>(generate_key(password, hasher), buffer, | ||||
|                                 buffer_size, res); | ||||
| } | ||||
|  | ||||
| template <typename result, typename arr_t, std::size_t arr_size> | ||||
| template <typename result_t, typename arr_t, std::size_t arr_size> | ||||
| inline void | ||||
| encrypt_data(const std::array<unsigned char, | ||||
|                               crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> &iv, | ||||
|              const std::array<arr_t, arr_size> &key, | ||||
|              const unsigned char *buffer, std::size_t buffer_size, | ||||
|              result &res) { | ||||
|              result_t &res) { | ||||
|   std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_ABYTES> mac{}; | ||||
|  | ||||
|   const std::uint32_t size = boost::endian::native_to_big( | ||||
| @@ -137,115 +128,86 @@ encrypt_data(const std::array<unsigned char, | ||||
|   std::memcpy(&res[iv.size()], mac.data(), mac.size()); | ||||
| } | ||||
|  | ||||
| template <typename result, typename s, std::size_t t> | ||||
| inline void encrypt_data(const std::array<s, t> &key, | ||||
| template <typename result_t, typename arr_t, std::size_t arr_size> | ||||
| inline void encrypt_data(const std::array<arr_t, arr_size> &key, | ||||
|                          const unsigned char *buffer, std::size_t buffer_size, | ||||
|                          result &res) { | ||||
|                          result_t &res) { | ||||
|   std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> iv{}; | ||||
|   randombytes_buf(iv.data(), iv.size()); | ||||
|  | ||||
|   encrypt_data<result>(iv, key, buffer, buffer_size, res); | ||||
|   encrypt_data<result_t>(iv, key, buffer, buffer_size, res); | ||||
| } | ||||
|  | ||||
| template <typename result, typename hash_t = hash_256_t> | ||||
| template <typename result_t, typename hash_t = hash_256_t> | ||||
| inline void encrypt_data( | ||||
|     std::string_view password, const unsigned char *buffer, | ||||
|     std::size_t buffer_size, result &res, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) { | ||||
|   encrypt_data<result>(generate_key(password, hasher), buffer, buffer_size, | ||||
|                        res); | ||||
|     std::size_t buffer_size, result_t &res, | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) { | ||||
|   encrypt_data<result_t>(generate_key(password, hasher), buffer, buffer_size, | ||||
|                          res); | ||||
| } | ||||
|  | ||||
| template <typename buffer, typename result, typename hash_t = hash_256_t> | ||||
| template <typename buffer_t, typename result_t, typename hash_t = hash_256_t> | ||||
| inline void encrypt_data( | ||||
|     std::string_view password, const buffer &buf, result &res, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher = std::nullopt) { | ||||
|   encrypt_data<result>(generate_key(password, hasher), | ||||
|                        reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                        buf.size(), res); | ||||
|     std::string_view password, const buffer_t &buf, result_t &res, | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher = | ||||
|         default_create_hash<hash_t>()) { | ||||
|   encrypt_data<result_t>(generate_key(password, hasher), | ||||
|                          reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                          buf.size(), res); | ||||
| } | ||||
|  | ||||
| template <typename buffer, typename result, typename s, std::size_t t> | ||||
| inline void encrypt_data(const std::array<s, t> &key, const buffer &buf, | ||||
|                          result &res) { | ||||
|   encrypt_data<result>(key, reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                        buf.size(), res); | ||||
| template <typename buffer_t, typename result_t, typename arr_t, | ||||
|           std::size_t arr_size> | ||||
| inline void encrypt_data(const std::array<arr_t, arr_size> &key, | ||||
|                          const buffer_t &buf, result_t &res) { | ||||
|   encrypt_data<result_t>(key, | ||||
|                          reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                          buf.size(), res); | ||||
| } | ||||
|  | ||||
| template <typename buffer, typename result, typename s, std::size_t t> | ||||
| template <typename buffer_t, typename result_t, typename arr_t, | ||||
|           std::size_t arr_size> | ||||
| inline void | ||||
| encrypt_data(const std::array<unsigned char, | ||||
|                               crypto_aead_xchacha20poly1305_IETF_NPUBBYTES> &iv, | ||||
|              const std::array<s, t> &key, const buffer &buf, result &res) { | ||||
|   encrypt_data<result>(iv, key, | ||||
|                        reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                        buf.size(), res); | ||||
|              const std::array<arr_t, arr_size> &key, const buffer_t &buf, | ||||
|              result_t &res) { | ||||
|   encrypt_data<result_t>(iv, key, | ||||
|                          reinterpret_cast<const unsigned char *>(buf.data()), | ||||
|                          buf.size(), res); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_CURL) | ||||
| using reader_func_t = | ||||
|     std::function<bool(data_buffer &cypher_text, std::uint64_t start_offset, | ||||
|                        std::uint64_t end_offset)>; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| read_encrypted_range(const http_range &range, | ||||
|                      const utils::encryption::hash_256_t &key, | ||||
|                      reader_func_t reader_func, std::uint64_t total_size, | ||||
|                      data_buffer &data) -> bool; | ||||
| #endif // defined(PROJECT_ENABLE_CURL) | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_256_t>(std::string_view data) -> hash_256_t { | ||||
|   return create_hash_sha256(data); | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_256_t>(std::wstring_view data) -> hash_256_t { | ||||
|   return create_hash_sha256(data); | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_384_t>(std::string_view data) -> hash_384_t { | ||||
|   return create_hash_blake2b_384(data); | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_384_t>(std::wstring_view data) -> hash_384_t { | ||||
|   return create_hash_blake2b_384(data); | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_512_t>(std::string_view data) -> hash_512_t { | ||||
|   return create_hash_sha512(data); | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto | ||||
| default_create_hash<hash_512_t>(std::wstring_view data) -> hash_512_t { | ||||
|   return create_hash_sha512(data); | ||||
| } | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline auto generate_key( | ||||
|     std::string_view password, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher) -> hash_t { | ||||
|   return hasher.has_value() ? (*hasher)(reinterpret_cast<const unsigned char *>( | ||||
|                                             password.data()), | ||||
|                                         password.size()) | ||||
|                             : default_create_hash<hash_t>(password); | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher) | ||||
|     -> hash_t { | ||||
|   return hasher(reinterpret_cast<const unsigned char *>(password.data()), | ||||
|                 password.size()); | ||||
| } | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline auto generate_key( | ||||
|     std::wstring_view password, | ||||
|     std::optional< | ||||
|         std::function<hash_t(const unsigned char *data, std::size_t size)>> | ||||
|         hasher) -> hash_t { | ||||
|   return hasher.has_value() | ||||
|              ? (*hasher)( | ||||
|                    reinterpret_cast<const unsigned char *>(password.data()), | ||||
|                    password.size() * sizeof(std::wstring_view::value_type)) | ||||
|              : default_create_hash<hash_t>(password); | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> hasher) | ||||
|     -> hash_t { | ||||
|   return hasher(reinterpret_cast<const unsigned char *>(password.data()), | ||||
|                 password.size() * sizeof(wchar_t)); | ||||
| } | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
|   | ||||
| @@ -42,11 +42,33 @@ protected: | ||||
|   i_exception_handler() = default; | ||||
| }; | ||||
|  | ||||
| struct iostream_exception_handler final : i_exception_handler { | ||||
|   void handle_exception(std::string_view function_name) const override { | ||||
|     std::cerr << function_name << "|exception|unknown" << std::endl; | ||||
|   } | ||||
|  | ||||
|   void handle_exception(std::string_view function_name, | ||||
|                         const std::exception &ex) const override { | ||||
|     std::cerr << function_name << "|exception|" | ||||
|               << (ex.what() == nullptr ? "unknown" : ex.what()) << std::endl; | ||||
|   } | ||||
| }; | ||||
| inline const iostream_exception_handler default_exception_handler{}; | ||||
|  | ||||
| extern std::atomic<const i_exception_handler *> exception_handler; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_TESTING) | ||||
| [[nodiscard]] inline auto | ||||
| get_exception_handler() -> const i_exception_handler * { | ||||
|   return exception_handler; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_TESTING) | ||||
|  | ||||
| void handle_exception(std::string_view function_name); | ||||
|  | ||||
| void handle_exception(std::string_view function_name, const std::exception &ex); | ||||
|  | ||||
| void set_exception_handler(i_exception_handler *handler); | ||||
| void set_exception_handler(const i_exception_handler *handler); | ||||
| } // namespace repertory::utils::error | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_ERROR_HPP_ | ||||
|   | ||||
| @@ -24,128 +24,833 @@ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/path.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #include "utils/types/file/i_directory.hpp" | ||||
| #include "utils/types/file/i_file.hpp" | ||||
| #include "utils/types/file/i_fs_item.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| class file final { | ||||
| [[nodiscard]] auto change_to_process_directory() -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto create_temp_name(std::string_view file_part) -> std::string; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| create_temp_name(std::wstring_view file_part) -> std::wstring; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] inline auto | ||||
| directory_exists_in_path(std::string_view path, | ||||
|                          std::string_view sub_directory) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] inline auto | ||||
| directory_exists_in_path(std::wstring_view path, | ||||
|                          std::wstring_view sub_directory) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] inline auto | ||||
| file_exists_in_path(std::string_view path, std::string_view file_name) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] inline auto | ||||
| file_exists_in_path(std::wstring_view path, | ||||
|                     std::wstring_view file_name) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_free_drive_space(std::string_view path) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_free_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto get_time(std::string_view path, | ||||
|                             time_type type) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto get_time(std::wstring_view path, | ||||
|                             time_type type) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_times(std::string_view path) -> std::optional<file_times>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_times(std::wstring_view path) -> std::optional<file_times>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_total_drive_space(std::string_view path) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| get_total_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| [[nodiscard]] auto | ||||
| smb_create_and_validate_relative_path(std::string_view smb_path, | ||||
|                                       std::string_view rel_path) -> std::string; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| smb_create_relative_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| smb_create_search_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| smb_create_smb_path(std::string_view smb_path, | ||||
|                     std::string_view rel_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| smb_get_parent_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto smb_get_root_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto smb_get_unc_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto smb_get_uri_path(std::string_view smb_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto smb_get_uri_path(std::string_view smb_path, | ||||
|                                     std::string_view user, | ||||
|                                     std::string_view password) -> std::string; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto smb_parent_is_same(std::string_view smb_path1, | ||||
|                                       std::string_view smb_path2) -> bool; | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
|  | ||||
| class file final : public i_file { | ||||
| public: | ||||
|   [[nodiscard]] static auto open_file(std::filesystem::path path) -> file; | ||||
|   // [[nodiscard]] static auto | ||||
|   // attach_file(native_handle handle, | ||||
|   //             bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   // INFO: has test | ||||
|   [[nodiscard]] static auto open_file(std::string_view path, | ||||
|                                       bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto open_file(std::wstring_view path, | ||||
|                                       bool read_only = false) -> fs_file_t { | ||||
|     return open_file(utils::string::to_utf8(path), read_only); | ||||
|   } | ||||
|  | ||||
|   // INFO: has test | ||||
|   [[nodiscard]] static auto | ||||
|   open_or_create_file(std::string_view path, | ||||
|                       bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   open_or_create_file(std::filesystem::path path) -> file; | ||||
|   open_or_create_file(std::wstring_view path, | ||||
|                       bool read_only = false) -> fs_file_t { | ||||
|     return open_or_create_file(utils::string::to_utf8(path), read_only); | ||||
|   } | ||||
|  | ||||
| protected: | ||||
|   file(std::fstream stream, std::filesystem::path path) | ||||
|       : path_(std::move(path)), stream_(std::move(stream)) {} | ||||
|   file() = default; | ||||
| public: | ||||
|   file() noexcept = default; | ||||
|  | ||||
|   file(std::string_view path) | ||||
|       : file_(nullptr), path_(utils::path::absolute(path)) {} | ||||
|  | ||||
|   file(std::wstring_view path) | ||||
|       : file_(nullptr), | ||||
|         path_(utils::path::absolute(utils::string::to_utf8(path))) {} | ||||
|  | ||||
| private: | ||||
|   file(file_t file_ptr, std::string_view path, bool read_only) | ||||
|       : file_(std::move(file_ptr)), path_(path), read_only_(read_only) {} | ||||
|  | ||||
| public: | ||||
|   file(const file &) = delete; | ||||
|   file(file &&file_) noexcept = default; | ||||
|  | ||||
|   ~file() { close(); } | ||||
|   file(file &&move_file) noexcept | ||||
|       : file_(std::move(move_file.file_)), | ||||
|         path_(std::move(move_file.path_)), | ||||
|         read_only_(move_file.read_only_) {} | ||||
|  | ||||
|   auto operator=(const file &) noexcept -> file & = delete; | ||||
|   auto operator=(file &&file_) noexcept -> file & = default; | ||||
|   ~file() override { close(); } | ||||
|  | ||||
| private: | ||||
|   std::error_code error_{}; | ||||
|   std::filesystem::path path_; | ||||
|   std::fstream stream_; | ||||
|   file_t file_; | ||||
|   std::string path_; | ||||
|   bool read_only_{false}; | ||||
|  | ||||
| private: | ||||
|   std::atomic_uint32_t read_buffer_size{65536U}; | ||||
|  | ||||
| private: | ||||
|   void open(); | ||||
|  | ||||
| public: | ||||
|   void close(); | ||||
|   void close() override; | ||||
|  | ||||
|   [[nodiscard]] auto get_error_code() const -> std::error_code { | ||||
|     return error_; | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override; | ||||
|  | ||||
|   void flush() const override; | ||||
|  | ||||
|   [[nodiscard]] auto get_handle() const -> native_handle override; | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { return path_; } | ||||
|  | ||||
|   [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { | ||||
|     return read_buffer_size; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::filesystem::path { return path_; } | ||||
|  | ||||
|   [[nodiscard]] auto move_to(std::filesystem::path new_path) -> bool; | ||||
|  | ||||
|   [[nodiscard]] auto read(data_buffer &data, std::uint64_t offset, | ||||
|                           std::size_t *total_read = nullptr) -> bool { | ||||
|     return read_(reinterpret_cast<unsigned char *>(data.data()), data.size(), | ||||
|                  offset, total_read); | ||||
|   [[nodiscard]] auto is_read_only() const -> bool override { | ||||
|     return read_only_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool; | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate() -> bool { return truncate(0U); } | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate(std::size_t size) -> bool; | ||||
|   [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, | ||||
|                           std::uint64_t offset, | ||||
|                           std::size_t *total_read = nullptr) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto write(const data_buffer &data, std::uint64_t offset, | ||||
|                            std::size_t *total_written = nullptr) -> bool { | ||||
|     return write_(reinterpret_cast<const unsigned char *>(data.data()), | ||||
|                   data.size(), offset, total_written); | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { | ||||
|     read_buffer_size = size; | ||||
|     return read_buffer_size; | ||||
|   } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_JSON) | ||||
|   [[nodiscard]] auto write_json(const nlohmann::json &data, | ||||
|                                 std::size_t *total_written = nullptr) -> bool { | ||||
|     auto str_data = data.dump(); | ||||
|     return write_(reinterpret_cast<const unsigned char *>(str_data.c_str()), | ||||
|                   str_data.size(), 0U, total_written); | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|   [[nodiscard]] auto sha256() -> std::optional<std::string>; | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
|   [[nodiscard]] auto size() const -> std::optional<std::uint64_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate(std::size_t size) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   write(const unsigned char *data, std::size_t to_write, std::size_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool override; | ||||
|  | ||||
| public: | ||||
|   auto operator=(const file &) noexcept -> file & = delete; | ||||
|  | ||||
|   auto operator=(file &&move_file) noexcept -> file & { | ||||
|     if (&move_file != this) { | ||||
|       file_ = std::move(move_file.file_); | ||||
|       path_ = std::move(move_file.path_); | ||||
|       read_only_ = move_file.read_only_; | ||||
|     } | ||||
|  | ||||
|     return *this; | ||||
|   } | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
|   [[nodiscard]] operator bool() const { return stream_.is_open(); } | ||||
|  | ||||
| private: | ||||
|   [[nodiscard]] auto read_(unsigned char *data, std::size_t to_read, | ||||
|                            std::uint64_t offset, | ||||
|                            std::size_t *total_read) -> bool; | ||||
|  | ||||
|   [[nodiscard]] auto write_(const unsigned char *data, std::size_t to_write, | ||||
|                             std::size_t offset, | ||||
|                             std::size_t *total_written) -> bool; | ||||
|   [[nodiscard]] operator bool() const override { return file_ != nullptr; } | ||||
| }; | ||||
|  | ||||
| [[nodiscard]] auto get_file_size(std::string_view path, | ||||
|                                  std::uint64_t &file_size) -> bool; | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| class enc_file final : public i_file { | ||||
| public: | ||||
|   [[nodiscard]] static auto attach_file(fs_file_t file) -> fs_file_t; | ||||
|  | ||||
| [[nodiscard]] auto get_file_size(std::wstring_view path, | ||||
|                                  std::uint64_t &file_size) -> bool; | ||||
| public: | ||||
|   enc_file() noexcept = default; | ||||
|  | ||||
| [[nodiscard]] auto is_directory(std::string_view path) -> bool; | ||||
| protected: | ||||
|   enc_file(fs_file_t file); | ||||
|  | ||||
| [[nodiscard]] auto is_directory(std::wstring_view path) -> bool; | ||||
| public: | ||||
|   enc_file(const enc_file &) = delete; | ||||
|  | ||||
| [[nodiscard]] auto is_file(std::string_view path) -> bool; | ||||
|   enc_file(enc_file &&move_file) noexcept : file_(std::move(move_file.file_)) {} | ||||
|  | ||||
| [[nodiscard]] auto is_file(std::wstring_view path) -> bool; | ||||
|   ~enc_file() override { close(); } | ||||
|  | ||||
| private: | ||||
|   fs_file_t file_; | ||||
|  | ||||
| public: | ||||
|   void close() override; | ||||
|  | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override { return file_->exists(); } | ||||
|  | ||||
|   void flush() const override; | ||||
|  | ||||
|   [[nodiscard]] auto get_handle() const -> native_handle override { | ||||
|     return file_->get_handle(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { | ||||
|     return file_->get_path(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { | ||||
|     return file_->get_read_buffer_size(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_time(time_type type) const -> std::optional<std::uint64_t> override { | ||||
|     return file_->get_time(type); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_read_only() const -> bool override { | ||||
|     return file_->is_read_only(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override { | ||||
|     return file_->is_symlink(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, | ||||
|                           std::uint64_t offset, | ||||
|                           std::size_t *total_read = nullptr) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { | ||||
|     return file_->set_read_buffer_size(size); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto size() const -> std::optional<std::uint64_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate(std::size_t size) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   write(const unsigned char *data, std::size_t to_write, std::size_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool override; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] operator bool() const override { | ||||
|     return static_cast<bool>(*file_); | ||||
|   } | ||||
|  | ||||
|   auto operator=(const file &) noexcept -> enc_file & = delete; | ||||
|  | ||||
|   auto operator=(enc_file &&move_file) noexcept -> enc_file & { | ||||
|     if (&move_file != this) { | ||||
|       file_ = std::move(move_file.file_); | ||||
|     } | ||||
|  | ||||
|     return *this; | ||||
|   } | ||||
| }; | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| class thread_file final : public i_file { | ||||
| public: | ||||
|   // [[nodiscard]] static auto | ||||
|   // attach_file(native_handle handle, | ||||
|   //             bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto attach_file(fs_file_t file) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto open_file(std::string_view path, | ||||
|                                       bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto open_file(std::wstring_view path, | ||||
|                                       bool read_only = false) -> fs_file_t { | ||||
|     return open_file(utils::string::to_utf8(path), read_only); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   open_or_create_file(std::string_view path, | ||||
|                       bool read_only = false) -> fs_file_t; | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   open_or_create_file(std::wstring_view path, | ||||
|                       bool read_only = false) -> fs_file_t { | ||||
|     return open_or_create_file(utils::string::to_utf8(path), read_only); | ||||
|   } | ||||
|  | ||||
| public: | ||||
|   thread_file() noexcept = default; | ||||
|  | ||||
|   thread_file(std::string_view path) : file_(new file(path)) {} | ||||
|  | ||||
|   thread_file(std::wstring_view path) | ||||
|       : file_(new file(utils::string::to_utf8(path))) {} | ||||
|  | ||||
| protected: | ||||
|   thread_file(fs_file_t file); | ||||
|  | ||||
| public: | ||||
|   thread_file(const thread_file &) = delete; | ||||
|  | ||||
|   thread_file(thread_file &&move_file) noexcept | ||||
|       : file_(std::move(move_file.file_)) {} | ||||
|  | ||||
|   ~thread_file() override { close(); } | ||||
|  | ||||
| private: | ||||
|   fs_file_t file_; | ||||
|  | ||||
| public: | ||||
|   void close() override; | ||||
|  | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override { return file_->exists(); } | ||||
|  | ||||
|   void flush() const override; | ||||
|  | ||||
|   [[nodiscard]] auto get_handle() const -> native_handle override { | ||||
|     return file_->get_handle(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { | ||||
|     return file_->get_path(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { | ||||
|     return file_->get_read_buffer_size(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_time(time_type type) const -> std::optional<std::uint64_t> override { | ||||
|     return file_->get_time(type); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_read_only() const -> bool override { | ||||
|     return file_->is_read_only(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override { | ||||
|     return file_->is_symlink(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, | ||||
|                           std::uint64_t offset, | ||||
|                           std::size_t *total_read = nullptr) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { | ||||
|     return file_->set_read_buffer_size(size); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto size() const -> std::optional<std::uint64_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate(std::size_t size) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   write(const unsigned char *data, std::size_t to_write, std::size_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool override; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] operator bool() const override { | ||||
|     return static_cast<bool>(*file_); | ||||
|   } | ||||
|  | ||||
|   auto operator=(const file &) noexcept -> thread_file & = delete; | ||||
|  | ||||
|   auto operator=(thread_file &&move_file) noexcept -> thread_file & { | ||||
|     if (&move_file != this) { | ||||
|       file_ = std::move(move_file.file_); | ||||
|     } | ||||
|  | ||||
|     return *this; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class directory final : public i_directory { | ||||
| public: | ||||
|   using directory_t = std::unique_ptr<directory>; | ||||
|  | ||||
|   directory() noexcept = default; | ||||
|  | ||||
|   directory(std::string_view path) : path_(utils::path::absolute(path)) {} | ||||
|  | ||||
|   directory(std::wstring_view path) | ||||
|       : path_(utils::path::absolute(utils::string::to_utf8(path))) {} | ||||
|  | ||||
|   directory(const directory &) noexcept = delete; | ||||
|  | ||||
|   directory(directory &&move_dir) noexcept = default; | ||||
|  | ||||
|   ~directory() override = default; | ||||
|  | ||||
| private: | ||||
|   std::string path_; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   count(bool recursive = false) const -> std::uint64_t override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   create_directory(std::string_view path = "") const -> fs_directory_t override; | ||||
|  | ||||
|   [[nodiscard]] auto create_file(std::string_view file_name, | ||||
|                                  bool read_only) const -> fs_file_t override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_directory(std::string_view path) const -> fs_directory_t override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_directories() const -> std::vector<fs_directory_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_file(std::string_view path) const -> fs_file_t override; | ||||
|  | ||||
|   [[nodiscard]] auto get_files() const -> std::vector<fs_file_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto get_items() const -> std::vector<fs_item_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { return path_; } | ||||
|  | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove_recursively() -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   size(bool recursive = false) const -> std::uint64_t override; | ||||
|  | ||||
| public: | ||||
|   auto operator=(const directory &) noexcept -> directory & = delete; | ||||
|  | ||||
|   auto operator=(directory &&move_dir) noexcept -> directory & = default; | ||||
|  | ||||
|   [[nodiscard]] operator bool() const override { return exists(); } | ||||
| }; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| #define SMB_MOD_RW2                                                            \ | ||||
|   (SMB_MOD_READ | SMB_MOD_WRITE | SMB_MOD_READ_EXT | SMB_MOD_WRITE_EXT |       \ | ||||
|    SMB_MOD_READ_ATTR | SMB_MOD_WRITE_ATTR | SMB_MOD_READ_CTL) | ||||
|  | ||||
| class smb_file final : public i_file { | ||||
| public: | ||||
|   smb_file() = default; | ||||
|  | ||||
|   smb_file(std::optional<smb_fd> fd, std::string path, smb_session_t session, | ||||
|            std::string_view share_name, smb_tid tid) | ||||
|       : fd_(std::move(fd)), | ||||
|         path_(std::move(path)), | ||||
|         session_(std::move(session)), | ||||
|         share_name_(share_name), | ||||
|         tid_(tid) {} | ||||
|  | ||||
|   smb_file(const smb_file &) = delete; | ||||
|  | ||||
|   smb_file(smb_file &&f) noexcept | ||||
|       : fd_(std::move(f.fd_)), | ||||
|         path_(std::move(f.path_)), | ||||
|         read_buffer_size(f.get_read_buffer_size()), | ||||
|         read_only_(f.read_only_), | ||||
|         session_(std::move(f.session_)), | ||||
|         share_name_(std::move(f.share_name_)), | ||||
|         tid_(f.tid_) {} | ||||
|  | ||||
|   ~smb_file() override { close(); } | ||||
|  | ||||
| private: | ||||
|   std::optional<smb_fd> fd_; | ||||
|   std::string path_; | ||||
|   std::atomic_uint32_t read_buffer_size{65536U}; | ||||
|   bool read_only_; | ||||
|   smb_session_t session_; | ||||
|   std::string share_name_; | ||||
|   smb_tid tid_; | ||||
|  | ||||
| public: | ||||
|   void close() override; | ||||
|  | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override; | ||||
|  | ||||
|   void flush() const override; | ||||
|  | ||||
|   [[nodiscard]] auto get_handle() const -> native_handle override { | ||||
|     return INVALID_HANDLE_VALUE; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { return path_; } | ||||
|  | ||||
|   [[nodiscard]] auto get_read_buffer_size() const -> std::uint32_t override { | ||||
|     return read_buffer_size; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   get_time(smb_session *session, smb_tid tid, std::string path, | ||||
|            time_type type) -> std::optional<std::uint64_t>; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_time(time_type type) const -> std::optional<std::uint64_t> override { | ||||
|     return get_time(session_.get(), tid_, path_, type); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_unc_path() const -> std::string { | ||||
|     return smb_get_unc_path(path_); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_uri_path() const -> std::string { | ||||
|     return smb_get_uri_path(path_); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_uri_path(std::string_view user, | ||||
|                std::string_view password) const -> std::string { | ||||
|     return smb_get_uri_path(path_, user, password); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_read_only() const -> bool override { | ||||
|     return read_only_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override; | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto open(bool read_only) -> bool; | ||||
|  | ||||
|   [[nodiscard]] auto read(unsigned char *data, std::size_t to_read, | ||||
|                           std::uint64_t offset, | ||||
|                           std::size_t *total_read = nullptr) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t override { | ||||
|     read_buffer_size = size; | ||||
|     return read_buffer_size; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto size() const -> std::optional<std::uint64_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto truncate(std::size_t size) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   write(const unsigned char *data, std::size_t to_write, std::size_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool override; | ||||
|  | ||||
| public: | ||||
|   auto operator=(const smb_file &) noexcept -> smb_file & = delete; | ||||
|  | ||||
|   auto operator=(smb_file &&move_file) noexcept -> smb_file & { | ||||
|     if (this != &move_file) { | ||||
|       fd_ = std::move(move_file.fd_); | ||||
|       path_ = std::move(move_file.path_); | ||||
|       read_buffer_size = move_file.get_read_buffer_size(); | ||||
|       read_only_ = move_file.read_only_; | ||||
|       session_ = std::move(move_file.session_); | ||||
|       share_name_ = std::move(move_file.share_name_); | ||||
|       tid_ = move_file.tid_; | ||||
|     } | ||||
|  | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] operator bool() const override { return fd_.has_value(); } | ||||
| }; | ||||
|  | ||||
| class smb_directory final : public i_directory { | ||||
| public: | ||||
|   using smb_directory_t = std::unique_ptr<smb_directory>; | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   open(std::string_view host, std::string_view user, std::string_view password, | ||||
|        std::string_view share_name) -> smb_directory_t; | ||||
|  | ||||
|   [[nodiscard]] static auto | ||||
|   open(std::wstring_view host, std::wstring_view user, | ||||
|        std::wstring_view password, | ||||
|        std::wstring_view share_name) -> smb_directory_t; | ||||
|  | ||||
| public: | ||||
|   smb_directory() noexcept = default; | ||||
|  | ||||
|   smb_directory(const smb_directory &) noexcept = delete; | ||||
|  | ||||
|   smb_directory(smb_directory &&) noexcept = default; | ||||
|  | ||||
|   ~smb_directory() override = default; | ||||
|  | ||||
| private: | ||||
|   smb_directory(std::string path, smb_session_t session, | ||||
|                 std::string_view share_name, smb_tid tid) | ||||
|       : path_(std::move(path)), | ||||
|         session_(std::move(session)), | ||||
|         share_name_(share_name), | ||||
|         tid_(tid) {} | ||||
|  | ||||
| private: | ||||
|   std::string path_{}; | ||||
|   smb_session_t session_{}; | ||||
|   std::string share_name_{}; | ||||
|   smb_tid tid_{}; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] auto | ||||
|   count(bool recursive = false) const -> std::uint64_t override; | ||||
|  | ||||
|   [[nodiscard]] auto copy_to(std::string_view new_path, | ||||
|                              bool overwrite) const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   create_directory(std::string_view path = "") const -> fs_directory_t override; | ||||
|  | ||||
|   [[nodiscard]] auto create_file(std::string_view file_name, | ||||
|                                  bool read_only) const -> fs_file_t override; | ||||
|  | ||||
|   [[nodiscard]] auto exists() const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_directory(std::string_view path) const -> fs_directory_t override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_directories() const -> std::vector<fs_directory_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_file(std::string_view path) const -> fs_file_t override; | ||||
|  | ||||
|   [[nodiscard]] auto get_files() const -> std::vector<fs_file_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto get_items() const -> std::vector<fs_item_t> override; | ||||
|  | ||||
|   [[nodiscard]] auto get_path() const -> std::string override { return path_; } | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_time(time_type type) const -> std::optional<std::uint64_t> override { | ||||
|     return smb_file::get_time(session_.get(), tid_, path_, type); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_unc_path() const -> std::string { | ||||
|     return smb_get_unc_path(path_); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_uri_path() const -> std::string { | ||||
|     return smb_get_uri_path(path_); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   get_uri_path(std::string_view user, | ||||
|                std::string_view password) const -> std::string { | ||||
|     return smb_get_uri_path(path_, user, password); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto is_symlink() const -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto move_to(std::string_view new_path) -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove() -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto remove_recursively() -> bool override; | ||||
|  | ||||
|   [[nodiscard]] auto | ||||
|   size(bool recursive = false) const -> std::uint64_t override; | ||||
|  | ||||
| public: | ||||
|   auto operator=(const smb_directory &) noexcept -> smb_directory & = delete; | ||||
|  | ||||
|   auto | ||||
|   operator=(smb_directory &&move_dir) noexcept -> smb_directory & = default; | ||||
|  | ||||
|   [[nodiscard]] operator bool() const override { return session_ != nullptr; } | ||||
| }; | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_JSON) | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto | ||||
| read_json_file(std::string_view path, nlohmann::json &data, | ||||
|                std::optional<std::string_view> password = std::nullopt) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto read_json_file( | ||||
|     std::wstring_view path, nlohmann::json &data, | ||||
|     std::optional<std::wstring_view> password = std::nullopt) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto write_json_file( | ||||
|     std::string_view path, const nlohmann::json &data, | ||||
|     std::optional<std::string_view> password = std::nullopt) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto write_json_file( | ||||
|     std::wstring_view path, const nlohmann::json &data, | ||||
|     std::optional<std::wstring_view> password = std::nullopt) -> bool; | ||||
| #else  // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto read_json_file(std::string_view path, | ||||
|                                   nlohmann::json &data) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto read_json_file(std::wstring_view path, | ||||
|                                   nlohmann::json &data) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto write_json_file(std::string_view path, | ||||
|                                    const nlohmann::json &data) -> bool; | ||||
|  | ||||
| // INFO: has test | ||||
| [[nodiscard]] auto write_json_file(std::wstring_view path, | ||||
|                                    const nlohmann::json &data) -> bool; | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
| // INFO: has test | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto directory_exists_in_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path, | ||||
|     std::basic_string_view<typename string_t::value_type> sub_directory) | ||||
|     -> bool { | ||||
|   return directory(utils::path::combine(path, {sub_directory})).exists(); | ||||
| } | ||||
|  | ||||
| // INFO: has test | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto file_exists_in_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path, | ||||
|     std::basic_string_view<typename string_t::value_type> file_name) -> bool { | ||||
|   return file(utils::path::combine(path, {file_name})).exists(); | ||||
| } | ||||
|  | ||||
| // INFO: has test | ||||
| inline auto directory_exists_in_path(std::string_view path, | ||||
|                                      std::string_view sub_directory) -> bool { | ||||
|   return directory_exists_in_path_t<std::string>(path, sub_directory); | ||||
| } | ||||
|  | ||||
| // INFO: has test | ||||
| inline auto directory_exists_in_path(std::wstring_view path, | ||||
|                                      std::wstring_view sub_directory) -> bool { | ||||
|   return directory_exists_in_path_t<std::wstring>(path, sub_directory); | ||||
| } | ||||
|  | ||||
| // INFO: has test | ||||
| inline auto file_exists_in_path(std::string_view path, | ||||
|                                 std::string_view file_name) -> bool { | ||||
|   return file_exists_in_path_t<std::string>(path, file_name); | ||||
| } | ||||
|  | ||||
| // INFO: has test | ||||
| inline auto file_exists_in_path(std::wstring_view path, | ||||
|                                 std::wstring_view file_name) -> bool { | ||||
|   return file_exists_in_path_t<std::wstring>(path, file_name); | ||||
| } | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_FILE_HPP_ | ||||
|   | ||||
| @@ -35,219 +35,132 @@ using hash_512_t = std::array<unsigned char, 64U>; | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_256(std::wstring_view data) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_256(const data_buffer &data) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_blake2b_384(std::string_view data) -> hash_384_t; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_384(std::wstring_view data) -> hash_384_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_blake2b_512(std::string_view data) -> hash_512_t; | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_384(const data_buffer &data) -> hash_384_t; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_512(std::wstring_view data) -> hash_512_t; | ||||
|  | ||||
| template <typename char_t, typename hash_t> | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_t(std::basic_string_view<char_t> data) -> hash_t; | ||||
| create_hash_blake2b_512(const data_buffer &data) -> hash_512_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_blake2b_512(std::string_view data) -> hash_512_t; | ||||
|  | ||||
| template <typename hash_t> | ||||
| [[nodiscard]] auto create_hash_blake2b_t(const data_buffer &data) -> hash_t; | ||||
|  | ||||
| template <typename char_t, typename hash_t> | ||||
| [[nodiscard]] auto create_hash_blake2b_t( | ||||
|     const std::vector<std::basic_string<char_t>> &data) -> hash_t; | ||||
|  | ||||
| template <typename arr_t, std::size_t arr_size> | ||||
| [[nodiscard]] auto | ||||
| create_hash_blake2b_t(const std::vector<std::array<arr_t, arr_size>> &data) | ||||
|     -> std::array<arr_t, arr_size>; | ||||
| [[nodiscard]] auto create_hash_blake2b_t(const unsigned char *data, | ||||
|                                          std::size_t data_size) -> hash_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha256(std::string_view data) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha256(std::wstring_view data) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha256(const data_buffer &data) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha256(const unsigned char *data, | ||||
|                                       std::size_t data_size) -> hash_256_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha512(std::string_view data) -> hash_512_t; | ||||
|  | ||||
| [[nodiscard]] auto create_hash_sha512(std::wstring_view data) -> hash_512_t; | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] auto | ||||
| create_hash_sha256_t(std::basic_string_view<char_t> data) -> hash_256_t; | ||||
| [[nodiscard]] auto create_hash_sha512(const data_buffer &data) -> hash_512_t; | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] auto create_hash_sha512_t(std::basic_string_view<char_t> data) | ||||
|     -> repertory::utils::encryption::hash_512_t; | ||||
| [[nodiscard]] auto create_hash_sha512(const unsigned char *data, | ||||
|                                       std::size_t data_size) -> hash_512_t; | ||||
|  | ||||
| template <typename hash_t> | ||||
| auto create_hash_blake2b_t(const data_buffer &data) -> hash_t { | ||||
| [[nodiscard]] inline auto default_create_hash() -> const | ||||
|     std::function<hash_t(const unsigned char *data, std::size_t size)> &; | ||||
|  | ||||
| template <typename hash_t> | ||||
| auto create_hash_blake2b_t(const unsigned char *data, | ||||
|                            std::size_t data_size) -> hash_t { | ||||
|   hash_t hash{}; | ||||
|  | ||||
|   crypto_generichash_blake2b_state state{}; | ||||
|   auto res = crypto_generichash_blake2b_init(&state, nullptr, 0U, hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize blake2b|" + | ||||
|     throw std::runtime_error("failed to initialize blake2b-" + | ||||
|                              std::to_string(hash.size() * 8U) + "|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_generichash_blake2b_update( | ||||
|       &state, reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
|   res = crypto_generichash_blake2b_update(&state, data, data_size); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update blake2b|" + std::to_string(res)); | ||||
|     throw std::runtime_error("failed to update blake2b-" + | ||||
|                              std::to_string(hash.size() * 8U) + "|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_generichash_blake2b_final(&state, hash.data(), hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize blake2b|" + | ||||
|     throw std::runtime_error("failed to finalize blake2b-" + | ||||
|                              std::to_string(hash.size() * 8U) + "|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| } | ||||
|  | ||||
| template <typename arr_t, std::size_t arr_size> | ||||
| auto create_hash_blake2b_t(const std::vector<std::array<arr_t, arr_size>> &data) | ||||
|     -> std::array<arr_t, arr_size> { | ||||
|   using hash_t = std::array<arr_t, arr_size>; | ||||
|   hash_t hash{}; | ||||
| inline const std::function<hash_256_t(const unsigned char *data, | ||||
|                                       std::size_t size)> | ||||
|     blake2b_256_hasher = | ||||
|         [](const unsigned char *data, std::size_t data_size) -> hash_256_t { | ||||
|   return create_hash_blake2b_t<hash_256_t>(data, data_size); | ||||
| }; | ||||
|  | ||||
|   crypto_generichash_blake2b_state state{}; | ||||
|   auto res = crypto_generichash_blake2b_init(&state, nullptr, 0U, hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
| inline const std::function<hash_384_t(const unsigned char *data, | ||||
|                                       std::size_t size)> | ||||
|     blake2b_384_hasher = | ||||
|         [](const unsigned char *data, std::size_t data_size) -> hash_384_t { | ||||
|   return create_hash_blake2b_t<hash_384_t>(data, data_size); | ||||
| }; | ||||
|  | ||||
|   for (const auto &item : data) { | ||||
|     res = crypto_generichash_blake2b_update( | ||||
|         &state, reinterpret_cast<const unsigned char *>(item.data()), | ||||
|         item.size()); | ||||
|     if (res != 0) { | ||||
|       throw std::runtime_error("failed to update blake2b|" + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|   } | ||||
| inline const std::function<hash_512_t(const unsigned char *data, | ||||
|                                       std::size_t size)> | ||||
|     blake2b_512_hasher = | ||||
|         [](const unsigned char *data, std::size_t data_size) -> hash_512_t { | ||||
|   return create_hash_blake2b_t<hash_512_t>(data, data_size); | ||||
| }; | ||||
|  | ||||
|   res = crypto_generichash_blake2b_final(&state, hash.data(), hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
| inline const std::function<hash_256_t(const unsigned char *data, | ||||
|                                       std::size_t size)> | ||||
|     sha256_hasher = | ||||
|         [](const unsigned char *data, std::size_t data_size) -> hash_256_t { | ||||
|   return create_hash_sha256(data, data_size); | ||||
| }; | ||||
|  | ||||
|   return hash; | ||||
| inline const std::function<hash_512_t(const unsigned char *data, | ||||
|                                       std::size_t size)> | ||||
|     sha512_hasher = | ||||
|         [](const unsigned char *data, std::size_t data_size) -> hash_512_t { | ||||
|   return create_hash_sha512(data, data_size); | ||||
| }; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline auto default_create_hash<hash_256_t>() -> const | ||||
|     std::function<hash_256_t(const unsigned char *data, std::size_t size)> & { | ||||
|   return blake2b_256_hasher; | ||||
| } | ||||
|  | ||||
| template <typename char_t, typename hash_t> | ||||
| auto create_hash_blake2b_t(const std::vector<std::basic_string<char_t>> &data) | ||||
|     -> hash_t { | ||||
|   hash_t hash{}; | ||||
|  | ||||
|   crypto_generichash_blake2b_state state{}; | ||||
|   auto res = crypto_generichash_blake2b_init(&state, nullptr, 0U, hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   for (const auto &item : data) { | ||||
|     res = crypto_generichash_blake2b_update( | ||||
|         &state, reinterpret_cast<const unsigned char *>(item.data()), | ||||
|         item.size() * sizeof(char_t)); | ||||
|     if (res != 0) { | ||||
|       throw std::runtime_error("failed to update blake2b|" + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   res = crypto_generichash_blake2b_final(&state, hash.data(), hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| template <> | ||||
| [[nodiscard]] inline auto default_create_hash<hash_384_t>() -> const | ||||
|     std::function<hash_384_t(const unsigned char *data, std::size_t size)> & { | ||||
|   return blake2b_384_hasher; | ||||
| } | ||||
|  | ||||
| template <typename char_t, typename hash_t> | ||||
| auto create_hash_blake2b_t(std::basic_string_view<char_t> data) -> hash_t { | ||||
|   hash_t hash{}; | ||||
|  | ||||
|   crypto_generichash_blake2b_state state{}; | ||||
|   auto res = crypto_generichash_blake2b_init(&state, nullptr, 0U, hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_generichash_blake2b_update( | ||||
|       &state, reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(char_t)); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update blake2b|" + std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_generichash_blake2b_final(&state, hash.data(), hash.size()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize blake2b|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| auto create_hash_sha256_t(std::basic_string_view<char_t> data) | ||||
|     -> repertory::utils::encryption::hash_256_t { | ||||
|   repertory::utils::encryption::hash_256_t hash{}; | ||||
|  | ||||
|   crypto_hash_sha256_state state{}; | ||||
|   auto res = crypto_hash_sha256_init(&state); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize sha256|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha256_update( | ||||
|       &state, reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(char_t)); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update sha256|" + std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha256_final(&state, hash.data()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize sha256|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| auto create_hash_sha512_t(std::basic_string_view<char_t> data) | ||||
|     -> repertory::utils::encryption::hash_512_t { | ||||
|   repertory::utils::encryption::hash_512_t hash{}; | ||||
|  | ||||
|   crypto_hash_sha512_state state{}; | ||||
|   auto res = crypto_hash_sha512_init(&state); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize sha512|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha512_update( | ||||
|       &state, reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(char_t)); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update sha512|" + std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha512_final(&state, hash.data()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize sha512|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| template <> | ||||
| [[nodiscard]] inline auto default_create_hash<hash_512_t>() -> const | ||||
|     std::function<hash_512_t(const unsigned char *data, std::size_t size)> & { | ||||
|   return blake2b_512_hasher; | ||||
| } | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
|   | ||||
| @@ -30,8 +30,12 @@ inline constexpr const std::string_view backslash{"\\"}; | ||||
| inline constexpr const std::wstring_view backslash_w{L"\\"}; | ||||
| inline constexpr const std::string_view dot{"."}; | ||||
| inline constexpr const std::wstring_view dot_w{L"."}; | ||||
| inline constexpr const std::string_view dot_backslash{".\\"}; | ||||
| inline constexpr const std::wstring_view dot_backslash_w{L".\\"}; | ||||
| inline constexpr const std::string_view dot_slash{"./"}; | ||||
| inline constexpr const std::wstring_view dot_slash_w{L"./"}; | ||||
| inline constexpr const std::string_view long_notation{"\\\\?\\"}; | ||||
| inline constexpr const std::wstring_view long_notation_w{L"\\\\?\\"}; | ||||
| inline constexpr const std::string_view slash{"/"}; | ||||
| inline constexpr const std::wstring_view slash_w{L"/"}; | ||||
| #if defined(_WIN32) | ||||
| @@ -39,6 +43,8 @@ inline constexpr const std::string_view directory_seperator{backslash}; | ||||
| inline constexpr const std::wstring_view directory_seperator_w{backslash_w}; | ||||
| inline constexpr const std::string_view not_directory_seperator{slash}; | ||||
| inline constexpr const std::wstring_view not_directory_seperator_w{slash_w}; | ||||
| inline constexpr const std::string_view unc_notation{"\\\\"}; | ||||
| inline constexpr const std::wstring_view unc_notation_w{L"\\\\"}; | ||||
| #else  // !defined(_WIN32) | ||||
| inline constexpr const std::string_view directory_seperator{slash}; | ||||
| inline constexpr const std::wstring_view directory_seperator_w{slash_w}; | ||||
| @@ -62,53 +68,6 @@ get_backslash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return backslash_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto get_dot() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot<char>() -> std::basic_string_view<char> { | ||||
|   return dot; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return dot_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash<char>() -> std::basic_string_view<char> { | ||||
|   return dot_slash; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return dot_slash_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash<char>() -> std::basic_string_view<char> { | ||||
|   return slash; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return slash_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_directory_seperator() -> std::basic_string_view<char_t>; | ||||
| @@ -141,37 +100,130 @@ get_not_directory_seperator<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return not_directory_seperator_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto get_dot() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot<char>() -> std::basic_string_view<char> { | ||||
|   return dot; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return dot_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_backslash() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_backslash<char>() -> std::basic_string_view<char> { | ||||
|   return dot_backslash; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_backslash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return dot_backslash_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash<char>() -> std::basic_string_view<char> { | ||||
|   return dot_slash; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_dot_slash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return dot_slash_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_long_notation() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_long_notation<char>() -> std::basic_string_view<char> { | ||||
|   return long_notation; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_long_notation<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return long_notation_w; | ||||
| } | ||||
|  | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash<char>() -> std::basic_string_view<char> { | ||||
|   return slash; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_slash<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return slash_w; | ||||
| } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| template <typename char_t> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_unc_notation() -> std::basic_string_view<char_t>; | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_unc_notation<char>() -> std::basic_string_view<char> { | ||||
|   return unc_notation; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline constexpr auto | ||||
| get_unc_notation<wchar_t>() -> std::basic_string_view<wchar_t> { | ||||
|   return unc_notation_w; | ||||
| } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto get_current_path() -> string_t; | ||||
|  | ||||
| [[nodiscard]] auto absolute(std::string_view path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto absolute(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| combine(std::string path, | ||||
| combine(std::string_view path, | ||||
|         const std::vector<std::string_view> &paths) -> std::string; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| combine(std::wstring path, | ||||
| combine(std::wstring_view path, | ||||
|         const std::vector<std::wstring_view> &paths) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto contains_trash_directory(std::string_view path) -> bool; | ||||
|  | ||||
| [[nodiscard]] auto contains_trash_directory(std::wstring_view path) -> bool; | ||||
|  | ||||
| [[nodiscard]] auto inline create_api_path(std::string_view path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto inline create_api_path(std::wstring_view path) | ||||
|     -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| directory_exists_in_path(std::string_view path, | ||||
|                          std::string_view sub_directory) -> bool; | ||||
| [[nodiscard]] auto exists(std::string_view path) -> bool; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| directory_exists_in_path(std::wstring_view path, | ||||
|                          std::wstring_view sub_directory) -> bool; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| file_exists_in_path(std::string_view path, std::string_view file_name) -> bool; | ||||
|  | ||||
| [[nodiscard]] inline auto | ||||
| file_exists_in_path(std::wstring_view path, | ||||
|                     std::wstring_view file_name) -> bool; | ||||
| [[nodiscard]] auto exists(std::wstring_view path) -> bool; | ||||
|  | ||||
| [[nodiscard]] inline auto finalize(std::string_view path) -> std::string; | ||||
|  | ||||
| @@ -196,22 +248,27 @@ get_parent_api_path(std::string_view path) -> std::string; | ||||
| [[nodiscard]] inline auto | ||||
| get_parent_api_path(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto get_parent_directory(std::string_view path) -> std::string; | ||||
| [[nodiscard]] auto get_parent_path(std::string_view path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto get_parent_directory(std::wstring_view path) -> std::wstring; | ||||
| [[nodiscard]] auto get_parent_path(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto is_trash_directory(std::string_view path) -> bool; | ||||
| [[nodiscard]] inline auto | ||||
| get_parts(std::string_view path) -> std::vector<std::string>; | ||||
|  | ||||
| [[nodiscard]] auto is_trash_directory(std::wstring_view path) -> bool; | ||||
| [[nodiscard]] inline auto | ||||
| get_parts_w(std::wstring_view path) -> std::vector<std::wstring>; | ||||
|  | ||||
| [[nodiscard]] auto get_relative_path(std::string_view path, | ||||
|                                      std::string_view root_path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| get_relative_path(std::wstring_view path, | ||||
|                   std::wstring_view root_path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto make_file_uri(std::string_view path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto make_file_uri(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto remove_file_name(std::string_view path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto remove_file_name(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| [[nodiscard]] auto strip_to_file_name(std::string path) -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto strip_to_file_name(std::wstring path) -> std::wstring; | ||||
| @@ -222,31 +279,30 @@ get_parent_api_path(std::wstring_view path) -> std::wstring; | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto combine_t( | ||||
|     string_t path, | ||||
|     std::basic_string_view<typename string_t::value_type> path, | ||||
|     const std::vector<std::basic_string_view<typename string_t::value_type>> | ||||
|         &paths) -> string_t { | ||||
|   path = std::accumulate( | ||||
|       paths.begin(), paths.end(), path, [](auto next_path, auto &&path_part) { | ||||
|         if (next_path.empty()) { | ||||
|           return string_t{path_part}; | ||||
|         } | ||||
|   auto dir_sep_t = | ||||
|       string_t{get_directory_seperator<typename string_t::value_type>()}; | ||||
|   return absolute( | ||||
|       std::accumulate(paths.begin(), paths.end(), | ||||
|                       std::basic_string<typename string_t::value_type>{path}, | ||||
|                       [&dir_sep_t](auto next_path, auto &&path_part) { | ||||
|                         if (next_path.empty()) { | ||||
|                           return string_t{path_part}; | ||||
|                         } | ||||
|  | ||||
|         return next_path + | ||||
|                string_t{ | ||||
|                    get_directory_seperator<typename string_t::value_type>()} + | ||||
|                string_t{path_part}; | ||||
|       }); | ||||
|  | ||||
|   return absolute(path); | ||||
|                         return next_path + dir_sep_t + string_t{path_part}; | ||||
|                       })); | ||||
| } | ||||
|  | ||||
| inline auto combine(std::string path, | ||||
| inline auto combine(std::string_view path, | ||||
|                     const std::vector<std::string_view> &paths) -> std::string { | ||||
|   return combine_t<std::string>(path, paths); | ||||
| } | ||||
|  | ||||
| inline auto | ||||
| combine(std::wstring path, | ||||
| combine(std::wstring_view path, | ||||
|         const std::vector<std::wstring_view> &paths) -> std::wstring { | ||||
|   return combine_t<std::wstring>(path, paths); | ||||
| } | ||||
| @@ -255,12 +311,20 @@ template <typename string_t> | ||||
| [[nodiscard]] inline auto create_api_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path) -> string_t { | ||||
|   auto backslash_t = get_backslash<typename string_t::value_type>(); | ||||
|   auto dot_t = get_dot<typename string_t::value_type>(); | ||||
|   auto dot_backslash_t = get_dot_backslash<typename string_t::value_type>(); | ||||
|   auto dot_slash_t = get_dot_slash<typename string_t::value_type>(); | ||||
|   auto dot_t = get_dot<typename string_t::value_type>(); | ||||
|   auto slash_t = get_slash<typename string_t::value_type>(); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   auto long_notation_t = get_long_notation<typename string_t::value_type>(); | ||||
|   if (utils::string::begins_with(path, long_notation_t)) { | ||||
|     path = path.substr(long_notation_t.size()); | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   if (path.empty() || path == backslash_t || path == dot_t || | ||||
|       path == dot_slash_t || path == slash_t) { | ||||
|       path == dot_slash_t || path == slash_t || path == dot_backslash_t) { | ||||
|     return string_t{slash_t}; | ||||
|   } | ||||
|  | ||||
| @@ -271,16 +335,12 @@ template <typename string_t> | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   format_path(api_path, slash_t, backslash_t); | ||||
|  | ||||
|   while (utils::string::begins_with(api_path, dot_slash_t)) { | ||||
|     api_path = api_path.substr(dot_slash_t.size()); | ||||
|   } | ||||
|  | ||||
|   while (utils::string::begins_with(api_path, dot_t)) { | ||||
|     api_path = api_path.substr(dot_t.size()); | ||||
|   } | ||||
|  | ||||
|   format_path(api_path, slash_t, backslash_t); | ||||
|  | ||||
|   if (api_path.at(0U) != slash_t.at(0U)) { | ||||
|     return string_t{slash_t} + api_path; | ||||
|   } | ||||
| @@ -296,50 +356,57 @@ inline auto create_api_path(std::wstring_view path) -> std::wstring { | ||||
|   return create_api_path_t<std::wstring>(path); | ||||
| } | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto directory_exists_in_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path, | ||||
|     std::basic_string_view<typename string_t::value_type> sub_directory) | ||||
|     -> bool { | ||||
|   return std::filesystem::is_directory( | ||||
|       std::filesystem::path(path).append(sub_directory)); | ||||
| } | ||||
|  | ||||
| inline auto directory_exists_in_path(std::string_view path, | ||||
|                                      std::string_view sub_directory) -> bool { | ||||
|   return directory_exists_in_path_t<std::string>(path, sub_directory); | ||||
| } | ||||
|  | ||||
| inline auto directory_exists_in_path(std::wstring_view path, | ||||
|                                      std::wstring_view sub_directory) -> bool { | ||||
|   return directory_exists_in_path_t<std::wstring>(path, sub_directory); | ||||
| } | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto file_exists_in_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path, | ||||
|     std::basic_string_view<typename string_t::value_type> file_name) -> bool { | ||||
|   return std::filesystem::is_regular_file( | ||||
|       std::filesystem::path(path).append(file_name)); | ||||
| } | ||||
|  | ||||
| inline auto file_exists_in_path(std::string_view path, | ||||
|                                 std::string_view file_name) -> bool { | ||||
|   return file_exists_in_path_t<std::string>(path, file_name); | ||||
| } | ||||
|  | ||||
| inline auto file_exists_in_path(std::wstring_view path, | ||||
|                                 std::wstring_view file_name) -> bool { | ||||
|   return file_exists_in_path_t<std::wstring>(path, file_name); | ||||
| } | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto finalize_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path) -> string_t { | ||||
|   string_t dir_sep_t{get_directory_seperator<typename string_t::value_type>()}; | ||||
|   string_t fmt_path{path}; | ||||
|   format_path(fmt_path, | ||||
|               get_directory_seperator<typename string_t::value_type>(), | ||||
|   if (fmt_path.empty()) { | ||||
|     return fmt_path; | ||||
|   } | ||||
|  | ||||
|   format_path(fmt_path, dir_sep_t, | ||||
|               get_not_directory_seperator<typename string_t::value_type>()); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   auto unc_notation_t = get_unc_notation<typename string_t::value_type>(); | ||||
|   if (utils::string::begins_with(fmt_path, unc_notation_t)) { | ||||
|     return fmt_path; | ||||
|   } | ||||
|  | ||||
|   auto dot_t = get_dot<typename string_t::value_type>(); | ||||
|   auto dot_sep_t = string_t{dot_t} + dir_sep_t; | ||||
|   if (fmt_path == dot_t || fmt_path == dot_sep_t) { | ||||
|     return get_current_path<string_t>(); | ||||
|   } | ||||
|  | ||||
|   if (fmt_path == dir_sep_t) { | ||||
| #if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|     return get_current_path<string_t>().substr(0U, long_notation.size() + 2U); | ||||
| #else  // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|     return get_current_path<string_t>().substr(0U, 2U); | ||||
| #endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|   } | ||||
|  | ||||
|   if (utils::string::begins_with(fmt_path, dir_sep_t)) { | ||||
| #if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|     return get_current_path<string_t>().substr(0U, long_notation.size() + 2U) + | ||||
|            fmt_path; | ||||
| #else  // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|     return get_current_path<string_t>().substr(0U, 2U) + fmt_path; | ||||
| #endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|   } | ||||
|  | ||||
|   if (utils::string::begins_with(fmt_path, dot_sep_t)) { | ||||
|     return get_current_path<string_t>() + dir_sep_t + fmt_path.substr(2U); | ||||
|   } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|   return string_t{get_long_notation<typename string_t::value_type>()} + | ||||
|          fmt_path; | ||||
| #endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return fmt_path; | ||||
| } | ||||
|  | ||||
| @@ -359,6 +426,19 @@ format_path(string_t &path, | ||||
|     -> string_t & { | ||||
|   utils::string::replace(path, not_sep, sep); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   auto is_unc{false}; | ||||
|   auto long_notation_t = get_long_notation<typename string_t::value_type>(); | ||||
|   auto unc_notation_t = get_unc_notation<typename string_t::value_type>(); | ||||
|   if (utils::string::begins_with(path, long_notation_t)) { | ||||
|     path = path.substr(long_notation_t.size()); | ||||
|   } else if (utils::string::begins_with(path, unc_notation_t)) { | ||||
|     path = path.substr(unc_notation_t.size()); | ||||
|     utils::string::left_trim(path, sep.at(0U)); | ||||
|     is_unc = true; | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   string_t double_sep(2U, sep.at(0U)); | ||||
|   while (utils::string::contains(path, double_sep)) { | ||||
|     utils::string::replace(path, double_sep, sep); | ||||
| @@ -369,7 +449,9 @@ format_path(string_t &path, | ||||
|   } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   if ((path.size() >= 2U) && (path.at(1U) == ':')) { | ||||
|   if (is_unc) { | ||||
|     path = string_t{unc_notation_t} + path; | ||||
|   } else if ((path.size() >= 2U) && (path.at(1U) == ':')) { | ||||
|     path[0U] = utils::string::to_lower(string_t(1U, path.at(0U))).at(0U); | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
| @@ -377,6 +459,24 @@ format_path(string_t &path, | ||||
|   return path; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline auto get_current_path<std::string>() -> std::string { | ||||
| #if defined(_WIN32) | ||||
|   std::string path; | ||||
|   path.resize(repertory::max_path_length + 1); | ||||
|   ::GetCurrentDirectoryA(static_cast<DWORD>(path.size()), path.data()); | ||||
|   path = path.c_str(); | ||||
|   return finalize(path); | ||||
| #else  // !defined(_WIN32) | ||||
|   return finalize(std::filesystem::current_path().string()); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| template <> | ||||
| [[nodiscard]] inline auto get_current_path<std::wstring>() -> std::wstring { | ||||
|   return utils::string::from_utf8(get_current_path<std::string>()); | ||||
| } | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto get_parent_api_path_t( | ||||
|     std::basic_string_view<typename string_t::value_type> path) -> string_t { | ||||
| @@ -387,12 +487,12 @@ template <typename string_t> | ||||
|     return ret; | ||||
|   } | ||||
|  | ||||
|   ret = path.substr(0, path.rfind('/') + 1); | ||||
|   ret = path.substr(0, path.rfind(slash_t) + 1); | ||||
|   if (ret == slash_t) { | ||||
|     return ret; | ||||
|   } | ||||
|  | ||||
|   return utils::string::right_trim(ret, '/'); | ||||
|   return utils::string::right_trim(ret, slash_t.at(0U)); | ||||
| } | ||||
|  | ||||
| inline auto get_parent_api_path(std::string_view path) -> std::string { | ||||
| @@ -402,6 +502,23 @@ inline auto get_parent_api_path(std::string_view path) -> std::string { | ||||
| inline auto get_parent_api_path(std::wstring_view path) -> std::wstring { | ||||
|   return get_parent_api_path_t<std::wstring>(path); | ||||
| } | ||||
|  | ||||
| template <typename string_t> | ||||
| [[nodiscard]] inline auto | ||||
| get_parts_t(std::basic_string_view<typename string_t::value_type> path) | ||||
|     -> std::vector<string_t> { | ||||
|   return utils::string::split( | ||||
|       path, get_directory_seperator<typename string_t::value_type>().at(0U), | ||||
|       false); | ||||
| } | ||||
|  | ||||
| inline auto get_parts(std::string_view path) -> std::vector<std::string> { | ||||
|   return get_parts_t<std::string>(path); | ||||
| } | ||||
|  | ||||
| inline auto get_parts_w(std::wstring_view path) -> std::vector<std::wstring> { | ||||
|   return get_parts_t<std::wstring>(path); | ||||
| } | ||||
| } // namespace repertory::utils::path | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_PATH_HPP_ | ||||
|   | ||||
| @@ -25,7 +25,9 @@ | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| namespace repertory::utils::time { | ||||
| inline constexpr const auto NANOS_PER_SECOND = 1000000000L; | ||||
| inline constexpr const auto NANOS_PER_SECOND{1000000000ULL}; | ||||
| inline constexpr const auto WIN32_TIME_CONVERSION{116444736000000000ULL}; | ||||
| inline constexpr const auto WIN32_TIME_NANOS_PER_TICK{100ULL}; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT) | ||||
| [[nodiscard]] inline auto convert_to_utc(time_t time) -> std::time_t { | ||||
| @@ -34,11 +36,6 @@ inline constexpr const auto NANOS_PER_SECOND = 1000000000L; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT) | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| [[nodiscard]] auto | ||||
| filetime_to_unix_time(const FILETIME &file_time) -> std::uint64_t; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT) | ||||
| [[nodiscard]] inline auto get_current_time_utc() -> std::time_t { | ||||
|   auto calendar_time = fmt::gmtime(std::time(nullptr)); | ||||
| @@ -46,8 +43,6 @@ filetime_to_unix_time(const FILETIME &file_time) -> std::uint64_t; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_SPDLOG) || defined(PROJECT_ENABLE_FMT) | ||||
|  | ||||
| [[nodiscard]] auto get_file_time_now() -> std::uint64_t; | ||||
|  | ||||
| void get_local_time_now(struct tm &local_time); | ||||
|  | ||||
| [[nodiscard]] auto get_time_now() -> std::uint64_t; | ||||
| @@ -55,10 +50,20 @@ void get_local_time_now(struct tm &local_time); | ||||
| #if defined(_WIN32) | ||||
| auto strptime(const char *s, const char *f, struct tm *tm) -> const char *; | ||||
|  | ||||
| [[nodiscard]] auto time64_to_unix_time(const __time64_t &time) -> std::uint64_t; | ||||
|  | ||||
| [[nodiscard]] auto unix_time_to_filetime(std::uint64_t unix_time) -> FILETIME; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| windows_file_time_to_unix_time(FILETIME win_time) -> std::uint64_t; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| windows_time_t_to_unix_time(__time64_t win_time) -> std::uint64_t; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| unix_time_to_windows_time(std::uint64_t unix_time) -> std::uint64_t; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| windows_time_to_unix_time(std::uint64_t win_time) -> std::uint64_t; | ||||
| } // namespace repertory::utils::time | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_UTILS_TIME_HPP_ | ||||
|   | ||||
							
								
								
									
										79
									
								
								support/include/utils/types/file/i_directory.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								support/include/utils/types/file/i_directory.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #ifndef REPERTORY_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_ | ||||
| #define REPERTORY_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/types/file/i_file.hpp" | ||||
| #include "utils/types/file/i_fs_item.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| struct i_directory : public i_fs_item { | ||||
|   using fs_directory_t = std::unique_ptr<i_directory>; | ||||
|   using fs_file_t = i_file::fs_file_t; | ||||
|  | ||||
|   virtual ~i_directory() = default; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   count(bool recursive = false) const -> std::uint64_t = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   create_directory(std::string_view path = "") const -> fs_directory_t = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto create_file(std::string_view file_name, | ||||
|                                          bool read_only) const -> fs_file_t = 0; | ||||
|  | ||||
|   [[nodiscard]] auto is_directory_item() const -> bool override { return true; } | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   get_directory(std::string_view path) const -> fs_directory_t = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   get_directories() const -> std::vector<fs_directory_t> = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   get_file(std::string_view path) const -> fs_file_t = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto get_files() const -> std::vector<fs_file_t> = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto get_items() const -> std::vector<fs_item_t> = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto remove_recursively() -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   size(bool recursive = false) const -> std::uint64_t = 0; | ||||
|  | ||||
| protected: | ||||
|   i_directory() noexcept = default; | ||||
|  | ||||
|   i_directory(const i_directory &) noexcept = default; | ||||
|  | ||||
|   i_directory(i_directory &&) noexcept = default; | ||||
|  | ||||
|   auto operator=(i_directory &&) noexcept -> i_directory & = default; | ||||
|  | ||||
|   auto operator=(const i_directory &) noexcept -> i_directory & = default; | ||||
| }; | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_TYPES_FILE_I_DIRECTORY_HPP_ | ||||
							
								
								
									
										93
									
								
								support/include/utils/types/file/i_file.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								support/include/utils/types/file/i_file.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #ifndef REPERTORY_INCLUDE_TYPES_FILE_I_FILE_HPP_ | ||||
| #define REPERTORY_INCLUDE_TYPES_FILE_I_FILE_HPP_ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/types/file/i_fs_item.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| struct i_file : public i_fs_item { | ||||
|   using fs_file_t = std::unique_ptr<i_file>; | ||||
|  | ||||
|   virtual ~i_file() = default; | ||||
|  | ||||
|   virtual void close() = 0; | ||||
|  | ||||
|   virtual void flush() const = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto get_handle() const -> native_handle = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto get_read_buffer_size() const -> std::uint32_t = 0; | ||||
|  | ||||
|   [[nodiscard]] auto is_directory_item() const -> bool override { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto is_read_only() const -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto read(data_buffer &data, std::uint64_t offset, | ||||
|                                   std::size_t *total_read = nullptr) -> bool { | ||||
|     return read(data.data(), data.size(), offset, total_read); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   read(unsigned char *data, std::size_t to_read, std::uint64_t offset, | ||||
|        std::size_t *total_read = nullptr) -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   read_all(data_buffer &data, std::uint64_t offset, | ||||
|            std::size_t *total_read = nullptr) -> bool; | ||||
|  | ||||
|   virtual auto set_read_buffer_size(std::uint32_t size) -> std::uint32_t = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto size() const -> std::optional<std::uint64_t> = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto truncate() -> bool { return truncate(0U); } | ||||
|  | ||||
|   [[nodiscard]] virtual auto truncate(std::size_t size) -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   write(const data_buffer &data, std::uint64_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool { | ||||
|     return write(data.data(), data.size(), offset, total_written); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   write(const unsigned char *data, std::size_t to_write, std::size_t offset, | ||||
|         std::size_t *total_written = nullptr) -> bool = 0; | ||||
|  | ||||
| protected: | ||||
|   i_file() noexcept = default; | ||||
|  | ||||
|   i_file(const i_file &) noexcept = default; | ||||
|  | ||||
|   i_file(i_file &&) noexcept = default; | ||||
|  | ||||
|   auto operator=(i_file &&) noexcept -> i_file & = default; | ||||
|  | ||||
|   auto operator=(const i_file &) noexcept -> i_file & = default; | ||||
| }; | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_TYPES_FILE_I_FILE_HPP_ | ||||
							
								
								
									
										111
									
								
								support/include/utils/types/file/i_fs_item.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								support/include/utils/types/file/i_fs_item.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #ifndef REPERTORY_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_ | ||||
| #define REPERTORY_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #include "utils/string.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| enum class time_type { | ||||
|   accessed, | ||||
|   created, | ||||
|   modified, | ||||
|   written, | ||||
| }; | ||||
|  | ||||
| struct file_times final { | ||||
|   std::uint64_t accessed{}; | ||||
|   std::uint64_t created{}; | ||||
|   std::uint64_t modified{}; | ||||
|   std::uint64_t written{}; | ||||
|  | ||||
|   [[nodiscard]] auto get(time_type type) const -> std::uint64_t { | ||||
|     switch (type) { | ||||
|     case time_type::accessed: | ||||
|       return accessed; | ||||
|     case time_type::created: | ||||
|       return created; | ||||
|     case time_type::modified: | ||||
|       return modified; | ||||
|     case time_type::written: | ||||
|       return written; | ||||
|     } | ||||
|  | ||||
|     throw std::runtime_error("type_type not supported"); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| struct i_fs_item { | ||||
|   using fs_item_t = std::unique_ptr<i_fs_item>; | ||||
|  | ||||
|   virtual ~i_fs_item() = default; | ||||
|  | ||||
|   [[nodiscard]] virtual auto copy_to(std::string_view to_path, | ||||
|                                      bool overwrite) const -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto copy_to(std::wstring_view new_path, | ||||
|                                      bool overwrite) -> bool { | ||||
|     return copy_to(utils::string::to_utf8(new_path), overwrite); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto exists() const -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto get_path() const -> std::string = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto | ||||
|   get_time(time_type type) const -> std::optional<std::uint64_t>; | ||||
|  | ||||
|   [[nodiscard]] virtual auto is_directory_item() const -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] auto is_file_item() const -> bool { | ||||
|     return not is_directory_item(); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto is_symlink() const -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto move_to(std::string_view new_path) -> bool = 0; | ||||
|  | ||||
|   [[nodiscard]] virtual auto move_to(std::wstring_view new_path) -> bool { | ||||
|     return move_to(utils::string::to_utf8(new_path)); | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] virtual auto remove() -> bool = 0; | ||||
|  | ||||
| public: | ||||
|   [[nodiscard]] virtual operator bool() const = 0; | ||||
|  | ||||
| protected: | ||||
|   i_fs_item() noexcept = default; | ||||
|  | ||||
|   i_fs_item(const i_fs_item &) noexcept = default; | ||||
|  | ||||
|   i_fs_item(i_fs_item &&) noexcept = default; | ||||
|  | ||||
|   auto operator=(i_fs_item &&) noexcept -> i_fs_item & = default; | ||||
|  | ||||
|   auto operator=(const i_fs_item &) noexcept -> i_fs_item & = default; | ||||
| }; | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // REPERTORY_INCLUDE_TYPES_FILE_I_FS_ITEM_HPP_ | ||||
| @@ -1,45 +0,0 @@ | ||||
| // Pick your poison. | ||||
| // | ||||
| // On GNU/Linux, you have few choices to get the most out of your stack trace. | ||||
| // | ||||
| // By default you get: | ||||
| //	- object filename | ||||
| //	- function name | ||||
| // | ||||
| // In order to add: | ||||
| //	- source filename | ||||
| //	- line and column numbers | ||||
| //	- source code snippet (assuming the file is accessible) | ||||
|  | ||||
| // Install one of the following libraries then uncomment one of the macro (or | ||||
| // better, add the detection of the lib and the macro definition in your build | ||||
| // system) | ||||
|  | ||||
| // - apt-get install libdw-dev ... | ||||
| // - g++/clang++ -ldw ... | ||||
| // #define BACKWARD_HAS_DW 1 | ||||
|  | ||||
| // - apt-get install binutils-dev ... | ||||
| // - g++/clang++ -lbfd ... | ||||
| // #define BACKWARD_HAS_BFD 1 | ||||
|  | ||||
| // - apt-get install libdwarf-dev ... | ||||
| // - g++/clang++ -ldwarf ... | ||||
| // #define BACKWARD_HAS_DWARF 1 | ||||
|  | ||||
| // Regardless of the library you choose to read the debug information, | ||||
| // for potentially more detailed stack traces you can use libunwind | ||||
| // - apt-get install libunwind-dev | ||||
| // - g++/clang++ -lunwind | ||||
| // #define BACKWARD_HAS_LIBUNWIND 1 | ||||
| #include "backward.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_BACKWARD_CPP) | ||||
|  | ||||
| namespace backward { | ||||
|  | ||||
| backward::SignalHandling sh; | ||||
|  | ||||
| } // namespace backward | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_BACKWARD_CPP) | ||||
| @@ -21,7 +21,7 @@ | ||||
| */ | ||||
| #include "utils/common.hpp" | ||||
|  | ||||
| #include "utils/path.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/string.hpp" | ||||
|  | ||||
| namespace repertory::utils { | ||||
| @@ -67,7 +67,7 @@ auto compare_version_strings(std::wstring_view version1, | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_STDUUID) | ||||
| auto create_uuid_string() -> std::string { | ||||
|   std::random_device random_device; | ||||
|   std::random_device random_device{}; | ||||
|   auto seed_data = std::array<int, std::mt19937::state_size>{}; | ||||
|   std::generate(std::begin(seed_data), std::end(seed_data), | ||||
|                 std::ref(random_device)); | ||||
| @@ -83,7 +83,6 @@ auto create_uuid_wstring() -> std::wstring { | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_STDUUID) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| auto generate_random_string(std::size_t length) -> std::string { | ||||
|   std::string ret; | ||||
|   if (length == 0U) { | ||||
| @@ -98,9 +97,7 @@ auto generate_random_string(std::size_t length) -> std::string { | ||||
|         generate_random_between(97U, 255U), | ||||
|     }; | ||||
|     ch = static_cast<char>( | ||||
|         random_list.at(repertory::utils::generate_random_between(0U, 2U)) % | ||||
|             74U + | ||||
|         48U); | ||||
|         random_list.at(generate_random_between(0U, 2U)) % 74U + 48U); | ||||
|   } | ||||
|  | ||||
|   return ret; | ||||
| @@ -109,7 +106,6 @@ auto generate_random_string(std::size_t length) -> std::string { | ||||
| auto generate_random_wstring(std::size_t length) -> std::wstring { | ||||
|   return utils::string::from_utf8(generate_random_string(length)); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| auto get_environment_variable(std::string_view variable) -> std::string { | ||||
|   static std::mutex mtx{}; | ||||
| @@ -154,4 +150,27 @@ auto get_next_available_port(std::uint16_t first_port, | ||||
|   return not error_code; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| auto retry_action(retryable_action_t action, std::size_t retry_count, | ||||
|                   std::chrono::milliseconds retry_wait) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     for (std::size_t idx = 0U; idx < retry_count; ++idx) { | ||||
|       if (action()) { | ||||
|         return true; | ||||
|       } | ||||
|  | ||||
|       std::this_thread::sleep_for(retry_wait); | ||||
|     } | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
| } // namespace repertory::utils | ||||
|   | ||||
							
								
								
									
										415
									
								
								support/src/utils/encrypting_reader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								support/src/utils/encrypting_reader.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,415 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/encrypting_reader.hpp" | ||||
|  | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/encryption.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/file.hpp" | ||||
| #include "utils/unix.hpp" | ||||
| #include "utils/windows.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| #if !defined(CURL_READFUNC_ABORT) | ||||
| #define CURL_READFUNC_ABORT (-1) | ||||
| #endif // !defined(CURL_READFUNC_ABORT) | ||||
|  | ||||
| namespace repertory::utils::encryption { | ||||
| class encrypting_streambuf final : public encrypting_reader::streambuf { | ||||
| public: | ||||
|   encrypting_streambuf(const encrypting_streambuf &) = default; | ||||
|   encrypting_streambuf(encrypting_streambuf &&) = delete; | ||||
|   auto | ||||
|   operator=(const encrypting_streambuf &) -> encrypting_streambuf & = delete; | ||||
|   auto operator=(encrypting_streambuf &&) -> encrypting_streambuf & = delete; | ||||
|  | ||||
|   explicit encrypting_streambuf(const encrypting_reader &reader) | ||||
|       : reader_(reader) { | ||||
|     setg(reinterpret_cast<char *>(0), reinterpret_cast<char *>(0), | ||||
|          reinterpret_cast<char *>(reader_.get_total_size())); | ||||
|   } | ||||
|  | ||||
|   ~encrypting_streambuf() override = default; | ||||
|  | ||||
| private: | ||||
|   encrypting_reader reader_; | ||||
|  | ||||
| protected: | ||||
|   auto seekoff(off_type off, std::ios_base::seekdir dir, | ||||
|                std::ios_base::openmode which = std::ios_base::out | | ||||
|                                                std::ios_base::in) | ||||
|       -> pos_type override { | ||||
|     if ((which & std::ios_base::in) != std::ios_base::in) { | ||||
|       throw std::runtime_error("output is not supported"); | ||||
|     } | ||||
|  | ||||
|     const auto set_position = [this](char *next) -> pos_type { | ||||
|       if ((next <= egptr()) && (next >= eback())) { | ||||
|         setg(eback(), next, reinterpret_cast<char *>(reader_.get_total_size())); | ||||
|         return static_cast<std::streamoff>( | ||||
|             reinterpret_cast<std::uintptr_t>(gptr())); | ||||
|       } | ||||
|  | ||||
|       return {traits_type::eof()}; | ||||
|     }; | ||||
|  | ||||
|     switch (dir) { | ||||
|     case std::ios_base::beg: | ||||
|       return set_position(eback() + off); | ||||
|  | ||||
|     case std::ios_base::cur: | ||||
|       return set_position(gptr() + off); | ||||
|  | ||||
|     case std::ios_base::end: | ||||
|       return set_position(egptr() + off); | ||||
|     } | ||||
|  | ||||
|     return encrypting_reader::streambuf::seekoff(off, dir, which); | ||||
|   } | ||||
|  | ||||
|   auto seekpos(pos_type pos, std::ios_base::openmode which = | ||||
|                                  std::ios_base::out | | ||||
|                                  std::ios_base::in) -> pos_type override { | ||||
|     return seekoff(pos, std::ios_base::beg, which); | ||||
|   } | ||||
|  | ||||
|   auto uflow() -> int_type override { | ||||
|     auto ret = underflow(); | ||||
|     if (ret == traits_type::eof()) { | ||||
|       return ret; | ||||
|     } | ||||
|  | ||||
|     gbump(1); | ||||
|  | ||||
|     return ret; | ||||
|   } | ||||
|  | ||||
|   auto underflow() -> int_type override { | ||||
|     if (gptr() == egptr()) { | ||||
|       return traits_type::eof(); | ||||
|     } | ||||
|  | ||||
|     reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr())); | ||||
|  | ||||
|     char c{}; | ||||
|     const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_); | ||||
|     if (res != 1) { | ||||
|       return traits_type::eof(); | ||||
|     } | ||||
|  | ||||
|     return c; | ||||
|   } | ||||
|  | ||||
|   auto xsgetn(char *ptr, std::streamsize count) -> std::streamsize override { | ||||
|     if (gptr() == egptr()) { | ||||
|       return traits_type::eof(); | ||||
|     } | ||||
|  | ||||
|     reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr())); | ||||
|  | ||||
|     auto res = encrypting_reader::reader_function( | ||||
|         ptr, 1U, static_cast<std::size_t>(count), &reader_); | ||||
|     if ((res == reader_.get_error_return()) || | ||||
|         (reader_.get_stop_requested() && | ||||
|          (res == static_cast<std::size_t>(CURL_READFUNC_ABORT)))) { | ||||
|       return traits_type::eof(); | ||||
|     } | ||||
|  | ||||
|     setg(eback(), gptr() + res, | ||||
|          reinterpret_cast<char *>(reader_.get_total_size())); | ||||
|     return static_cast<std::streamsize>(res); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class encrypting_reader_iostream final : public encrypting_reader::iostream { | ||||
| public: | ||||
|   encrypting_reader_iostream(const encrypting_reader_iostream &) = delete; | ||||
|   encrypting_reader_iostream(encrypting_reader_iostream &&) = delete; | ||||
|  | ||||
|   auto operator=(const encrypting_reader_iostream &) | ||||
|       -> encrypting_reader_iostream & = delete; | ||||
|   auto operator=(encrypting_reader_iostream &&) | ||||
|       -> encrypting_reader_iostream & = delete; | ||||
|  | ||||
|   explicit encrypting_reader_iostream( | ||||
|       std::unique_ptr<encrypting_streambuf> buffer) | ||||
|       : encrypting_reader::iostream(buffer.get()), buffer_(std::move(buffer)) {} | ||||
|  | ||||
|   ~encrypting_reader_iostream() override = default; | ||||
|  | ||||
| private: | ||||
|   std::unique_ptr<encrypting_streambuf> buffer_; | ||||
| }; | ||||
|  | ||||
| const std::size_t encrypting_reader::header_size_ = ([]() { | ||||
|   return crypto_aead_xchacha20poly1305_IETF_NPUBBYTES + | ||||
|          crypto_aead_xchacha20poly1305_IETF_ABYTES; | ||||
| })(); | ||||
| const std::size_t encrypting_reader::data_chunk_size_ = (8UL * 1024UL * 1024UL); | ||||
| const std::size_t encrypting_reader::encrypted_chunk_size_ = | ||||
|     data_chunk_size_ + header_size_; | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     std::string_view file_name, std::string_view source_path, | ||||
|     stop_type &stop_requested, std::string_view token, | ||||
|     std::optional<std::string> relative_parent_path, std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|           token)), | ||||
|       stop_requested_(stop_requested), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
|   if (not *source_file_) { | ||||
|     throw std::runtime_error("file open failed|src|" + | ||||
|                              std::string{source_path}); | ||||
|   } | ||||
|  | ||||
|   data_buffer result; | ||||
|   utils::encryption::encrypt_data( | ||||
|       key_, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       file_name.size(), result); | ||||
|   encrypted_file_name_ = utils::collection::to_hex_string(result); | ||||
|  | ||||
|   if (relative_parent_path.has_value()) { | ||||
|     for (auto &&part : | ||||
|          utils::string::split(relative_parent_path.value(), | ||||
|                               utils::path::directory_seperator, false)) { | ||||
|       utils::encryption::encrypt_data( | ||||
|           key_, reinterpret_cast<const unsigned char *>(part.c_str()), | ||||
|           strnlen(part.c_str(), part.size()), result); | ||||
|       encrypted_file_path_ += '/' + utils::collection::to_hex_string(result); | ||||
|     } | ||||
|     encrypted_file_path_ += '/' + encrypted_file_name_; | ||||
|   } | ||||
|  | ||||
|   auto opt_size = source_file_->size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw std::runtime_error("failed to get file size|" + | ||||
|                              source_file_->get_path()); | ||||
|   } | ||||
|   auto file_size = opt_size.value(); | ||||
|  | ||||
|   const auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   total_size_ = file_size + (total_chunks * encryption_header_size); | ||||
|   last_data_chunk_ = total_chunks - 1U; | ||||
|   last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size | ||||
|                           : (file_size % data_chunk_size_) == 0U | ||||
|                               ? data_chunk_size_ | ||||
|                               : file_size % data_chunk_size_; | ||||
|   iv_list_.resize(total_chunks); | ||||
|   for (auto &iv : iv_list_) { | ||||
|     randombytes_buf(iv.data(), iv.size()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(std::string_view encrypted_file_path, | ||||
|                                      std::string_view source_path, | ||||
|                                      stop_type &stop_requested, | ||||
|                                      std::string_view token, | ||||
|                                      std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|           token)), | ||||
|       stop_requested_(stop_requested), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
|   if (not *source_file_) { | ||||
|     throw std::runtime_error("file open failed|src|" + | ||||
|                              std::string{source_path}); | ||||
|   } | ||||
|  | ||||
|   encrypted_file_path_ = encrypted_file_path; | ||||
|   encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_); | ||||
|  | ||||
|   auto opt_size = source_file_->size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw std::runtime_error("failed to get file size|" + | ||||
|                              source_file_->get_path()); | ||||
|   } | ||||
|   auto file_size = opt_size.value(); | ||||
|  | ||||
|   const auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   total_size_ = file_size + (total_chunks * encryption_header_size); | ||||
|   last_data_chunk_ = total_chunks - 1U; | ||||
|   last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size | ||||
|                           : (file_size % data_chunk_size_) == 0U | ||||
|                               ? data_chunk_size_ | ||||
|                               : file_size % data_chunk_size_; | ||||
|   iv_list_.resize(total_chunks); | ||||
|   for (auto &iv : iv_list_) { | ||||
|     randombytes_buf(iv.data(), iv.size()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     std::string_view encrypted_file_path, std::string_view source_path, | ||||
|     stop_type &stop_requested, std::string_view token, | ||||
|     std::vector< | ||||
|         std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|         iv_list, | ||||
|     std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|           token)), | ||||
|       stop_requested_(stop_requested), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
|   if (not *source_file_) { | ||||
|     throw std::runtime_error("file open failed|src|" + | ||||
|                              std::string{source_path}); | ||||
|   } | ||||
|  | ||||
|   encrypted_file_path_ = encrypted_file_path; | ||||
|   encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_path_); | ||||
|  | ||||
|   auto opt_size = source_file_->size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw std::runtime_error("get file size failed|src|" + | ||||
|                              source_file_->get_path() + '|' + | ||||
|                              std::to_string(utils::get_last_error_code())); | ||||
|   } | ||||
|   auto file_size{opt_size.value()}; | ||||
|  | ||||
|   const auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   total_size_ = file_size + (total_chunks * encryption_header_size); | ||||
|   last_data_chunk_ = total_chunks - 1U; | ||||
|   last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size | ||||
|                           : (file_size % data_chunk_size_) == 0U | ||||
|                               ? data_chunk_size_ | ||||
|                               : file_size % data_chunk_size_; | ||||
|   iv_list_ = std::move(iv_list); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(const encrypting_reader &reader) | ||||
|     : key_(reader.key_), | ||||
|       stop_requested_(reader.stop_requested_), | ||||
|       error_return_(reader.error_return_), | ||||
|       source_file_( | ||||
|           utils::file::file::open_file(reader.source_file_->get_path(), true)), | ||||
|       chunk_buffers_(reader.chunk_buffers_), | ||||
|       encrypted_file_name_(reader.encrypted_file_name_), | ||||
|       encrypted_file_path_(reader.encrypted_file_path_), | ||||
|       iv_list_(reader.iv_list_), | ||||
|       last_data_chunk_(reader.last_data_chunk_), | ||||
|       last_data_chunk_size_(reader.last_data_chunk_size_), | ||||
|       read_offset_(reader.read_offset_), | ||||
|       total_size_(reader.total_size_) { | ||||
|   if (not *source_file_) { | ||||
|     throw std::runtime_error("file open failed|src|" + | ||||
|                              source_file_->get_path()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size) | ||||
|     -> std::uint64_t { | ||||
|   return total_size - (utils::divide_with_ceiling( | ||||
|                            total_size, static_cast<std::uint64_t>( | ||||
|                                            get_encrypted_chunk_size())) * | ||||
|                        encryption_header_size); | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::calculate_encrypted_size(std::string_view source_path) | ||||
|     -> std::uint64_t { | ||||
|   auto opt_size = utils::file::file{source_path}.size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw std::runtime_error("get file size failed|src|" + | ||||
|                              std::string{source_path} + '|' + | ||||
|                              std::to_string(utils::get_last_error_code())); | ||||
|   } | ||||
|   auto file_size{opt_size.value()}; | ||||
|  | ||||
|   const auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   return file_size + (total_chunks * encryption_header_size); | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::create_iostream() const | ||||
|     -> std::shared_ptr<encrypting_reader::iostream> { | ||||
|   return std::make_shared<encrypting_reader_iostream>( | ||||
|       std::make_unique<encrypting_streambuf>(*this)); | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|                                         size_t nitems) -> size_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   const auto read_size = static_cast<std::size_t>(std::min( | ||||
|       static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_)); | ||||
|  | ||||
|   auto chunk = read_offset_ / encrypted_chunk_size_; | ||||
|   auto chunk_offset = read_offset_ % encrypted_chunk_size_; | ||||
|   std::size_t total_read{}; | ||||
|  | ||||
|   auto ret = false; | ||||
|   if (read_offset_ < total_size_) { | ||||
|     try { | ||||
|       ret = true; | ||||
|       auto remain = read_size; | ||||
|       while (not stop_requested_ && ret && (remain != 0U)) { | ||||
|         if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) { | ||||
|           auto &chunk_buffer = chunk_buffers_[chunk]; | ||||
|           data_buffer file_data(chunk == last_data_chunk_ | ||||
|                                     ? last_data_chunk_size_ | ||||
|                                     : data_chunk_size_); | ||||
|           chunk_buffer.resize(file_data.size() + encryption_header_size); | ||||
|  | ||||
|           std::size_t bytes_read{}; | ||||
|           if ((ret = source_file_->read(file_data, chunk * data_chunk_size_, | ||||
|                                         &bytes_read))) { | ||||
|             utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data, | ||||
|                                             chunk_buffer); | ||||
|           } | ||||
|         } else if (chunk) { | ||||
|           chunk_buffers_.erase(chunk - 1u); | ||||
|         } | ||||
|  | ||||
|         auto &chunk_buffer = chunk_buffers_[chunk]; | ||||
|         const auto to_read = std::min( | ||||
|             static_cast<std::size_t>(chunk_buffer.size() - chunk_offset), | ||||
|             remain); | ||||
|         std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read); | ||||
|         total_read += to_read; | ||||
|         remain -= to_read; | ||||
|         chunk_offset = 0u; | ||||
|         chunk++; | ||||
|         read_offset_ += to_read; | ||||
|       } | ||||
|     } catch (const std::exception &e) { | ||||
|       utils::error::handle_exception(function_name, e); | ||||
|       ret = false; | ||||
|     } catch (...) { | ||||
|       utils::error::handle_exception(function_name); | ||||
|       ret = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return stop_requested_ ? static_cast<std::size_t>(CURL_READFUNC_ABORT) | ||||
|          : ret           ? total_read | ||||
|                          : error_return_; | ||||
| } | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| @@ -21,34 +21,97 @@ | ||||
| */ | ||||
| #include "utils/encryption.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| namespace repertory::utils::encryption { | ||||
| #if defined(PROJECT_ENABLE_BOOST) | ||||
| auto decrypt_data(std::string_view password, | ||||
|                   std::string_view data) -> data_buffer { | ||||
|   auto key = generate_key<hash_256_t>(password); | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/encrypting_reader.hpp" | ||||
|  | ||||
|   data_buffer buf{}; | ||||
|   if (not decrypt_data(key, | ||||
|                        reinterpret_cast<const unsigned char *>(data.data()), | ||||
|                        data.size(), buf)) { | ||||
|     throw std::runtime_error("decryption failed"); | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| namespace repertory::utils::encryption { | ||||
| auto decrypt_file_path(std::string_view encryption_token, | ||||
|                        std::string &file_path) -> bool { | ||||
|   std::string decrypted_file_path{}; | ||||
|   for (const auto &part : std::filesystem::path(file_path)) { | ||||
|     auto file_name = part.string(); | ||||
|     if (file_name == "/") { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     auto res = decrypt_file_name(encryption_token, file_name); | ||||
|     if (not res) { | ||||
|       return res; | ||||
|     } | ||||
|  | ||||
|     decrypted_file_path += '/' + file_name; | ||||
|   } | ||||
|  | ||||
|   return buf; | ||||
|   file_path = decrypted_file_path; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| auto encrypt_data(std::string_view password, | ||||
|                   std::string_view data) -> data_buffer { | ||||
|   auto key = generate_key<hash_256_t>(password); | ||||
| auto decrypt_file_name(std::string_view encryption_token, | ||||
|                        std::string &file_name) -> bool { | ||||
|   data_buffer buffer; | ||||
|   if (not utils::collection::from_hex_string(file_name, buffer)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   data_buffer buf{}; | ||||
|   encrypt_data(key, reinterpret_cast<const unsigned char *>(data.data()), | ||||
|                data.size(), buf); | ||||
|   file_name.clear(); | ||||
|   if (not utils::encryption::decrypt_data(encryption_token, buffer, | ||||
|                                           file_name)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return buf; | ||||
|   return true; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_CURL) | ||||
| auto read_encrypted_range(const http_range &range, | ||||
|                           const utils::encryption::hash_256_t &key, | ||||
|                           reader_func_t reader_func, std::uint64_t total_size, | ||||
|                           data_buffer &data) -> bool { | ||||
|   const auto encrypted_chunk_size = | ||||
|       utils::encryption::encrypting_reader::get_encrypted_chunk_size(); | ||||
|   const auto data_chunk_size = | ||||
|       utils::encryption::encrypting_reader::get_data_chunk_size(); | ||||
|  | ||||
|   const auto start_chunk = | ||||
|       static_cast<std::size_t>(range.begin / data_chunk_size); | ||||
|   const auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size); | ||||
|   auto remain = range.end - range.begin + 1U; | ||||
|   auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size); | ||||
|  | ||||
|   for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) { | ||||
|     data_buffer cypher; | ||||
|     const auto start_offset = chunk * encrypted_chunk_size; | ||||
|     const auto end_offset = std::min( | ||||
|         start_offset + (total_size - (chunk * data_chunk_size)) + | ||||
|             encryption_header_size - 1U, | ||||
|         static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U)); | ||||
|  | ||||
|     if (not reader_func(cypher, start_offset, end_offset)) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     data_buffer source_buffer; | ||||
|     if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) { | ||||
|       return false; | ||||
|     } | ||||
|     cypher.clear(); | ||||
|  | ||||
|     const auto data_size = static_cast<std::size_t>(std::min( | ||||
|         remain, static_cast<std::uint64_t>(data_chunk_size - source_offset))); | ||||
|     std::copy(std::next(source_buffer.begin(), | ||||
|                         static_cast<std::int64_t>(source_offset)), | ||||
|               std::next(source_buffer.begin(), | ||||
|                         static_cast<std::int64_t>(source_offset + data_size)), | ||||
|               std::back_inserter(data)); | ||||
|     remain -= data_size; | ||||
|     source_offset = 0U; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_CURL) | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined (PROJECT_ENABLE_BOOST) | ||||
|   | ||||
| @@ -21,51 +21,32 @@ | ||||
| */ | ||||
| #include "utils/error.hpp" | ||||
|  | ||||
| namespace { | ||||
| struct default_exception_handler final | ||||
|     : repertory::utils::error::i_exception_handler { | ||||
|   void handle_exception(std::string_view function_name) const override { | ||||
|     std::cerr << function_name << "|exception|unknown" << std::endl; | ||||
|   } | ||||
|  | ||||
|   void handle_exception(std::string_view function_name, | ||||
|                         const std::exception &ex) const override { | ||||
|     std::cerr << function_name << "|exception|" | ||||
|               << (ex.what() == nullptr ? "unknown" : ex.what()) << std::endl; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| static default_exception_handler default_handler{}; | ||||
|  | ||||
| static std::atomic<repertory::utils::error::i_exception_handler *> | ||||
|     exception_handler{ | ||||
|         &default_handler, | ||||
|     }; | ||||
| } // namespace | ||||
|  | ||||
| namespace repertory::utils::error { | ||||
| std::atomic<const i_exception_handler *> exception_handler{ | ||||
|     &default_exception_handler}; | ||||
|  | ||||
| void handle_exception(std::string_view function_name) { | ||||
|   i_exception_handler *handler{exception_handler}; | ||||
|   const i_exception_handler *handler{exception_handler}; | ||||
|   if (handler != nullptr) { | ||||
|     handler->handle_exception(function_name); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   default_handler.handle_exception(function_name); | ||||
|   default_exception_handler.handle_exception(function_name); | ||||
| } | ||||
|  | ||||
| void handle_exception(std::string_view function_name, | ||||
|                       const std::exception &ex) { | ||||
|   i_exception_handler *handler{exception_handler}; | ||||
|   const i_exception_handler *handler{exception_handler}; | ||||
|   if (handler != nullptr) { | ||||
|     handler->handle_exception(function_name, ex); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   default_handler.handle_exception(function_name, ex); | ||||
|   default_exception_handler.handle_exception(function_name, ex); | ||||
| } | ||||
|  | ||||
| void set_exception_handler(i_exception_handler *handler) { | ||||
| void set_exception_handler(const i_exception_handler *handler) { | ||||
|   exception_handler = handler; | ||||
| } | ||||
| } // namespace repertory::utils::error | ||||
|   | ||||
| @@ -21,227 +21,309 @@ | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/encryption.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/path.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #include "utils/time.hpp" | ||||
| #include "utils/unix.hpp" | ||||
| #include "utils/windows.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| auto file::open_file(std::filesystem::path path) -> file { | ||||
|   path = utils::path::absolute(path.string()); | ||||
|   if (not is_file(path.string())) { | ||||
|     throw std::runtime_error("file not found: " + path.string()); | ||||
|   } | ||||
|  | ||||
|   auto stream = std::fstream{ | ||||
|       path, | ||||
|       std::ios_base::binary | std::ios_base::in | std::ios_base::out, | ||||
|   }; | ||||
|   return { | ||||
|       std::move(stream), | ||||
|       path, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| auto file::open_or_create_file(std::filesystem::path path) -> file { | ||||
|   path = utils::path::absolute(path.string()); | ||||
|   auto stream = std::fstream{ | ||||
|       path.string().c_str(), | ||||
|       std::ios_base::binary | std::ios_base::trunc | std::ios_base::in | | ||||
|           std::ios_base::out, | ||||
|   }; | ||||
|  | ||||
|   return { | ||||
|       std::move(stream), | ||||
|       path, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| void file::close() { | ||||
|   if (stream_.is_open()) { | ||||
|     stream_.close(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto file::move_to(std::filesystem::path new_path) -> bool { | ||||
|   new_path = utils::path::absolute(new_path.string()); | ||||
|  | ||||
|   auto reopen{false}; | ||||
|   if (stream_.is_open()) { | ||||
|     reopen = true; | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   std::filesystem::rename(path_, new_path, error_); | ||||
|   if (not error_) { | ||||
|     path_ = new_path; | ||||
|     if (reopen) { | ||||
|       *this = open_file(path_); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   if (reopen) { | ||||
|     *this = open_file(path_); | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::read_(unsigned char *data, std::size_t to_read, std::uint64_t offset, | ||||
|                  std::size_t *total_read) -> bool { | ||||
| auto change_to_process_directory() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   if (total_read != nullptr) { | ||||
|     (*total_read) = 0U; | ||||
|   try { | ||||
| #if defined(_WIN32) | ||||
|     std::string file_name; | ||||
|     file_name.resize(MAX_PATH + 1U); | ||||
|  | ||||
|     ::GetModuleFileNameA(nullptr, file_name.data(), | ||||
|                          static_cast<DWORD>(file_name.size() - 1U)); | ||||
|     auto path = utils::path::strip_to_file_name(file_name.c_str()); | ||||
|     ::SetCurrentDirectoryA(path.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|     std::string path; | ||||
|     path.resize(PATH_MAX + 1); | ||||
| #if defined(__APPLE__) | ||||
|     proc_pidpath(getpid(), path.c_str(), path.size()); | ||||
| #else  // !defined(__APPLE__) | ||||
|     auto res = readlink("/proc/self/exe", path.data(), path.size()); | ||||
|     if (res == -1) { | ||||
|       throw std::runtime_error("failed to readlink|" + path + '|' + | ||||
|                                std::to_string(errno)); | ||||
|     } | ||||
| #endif // defined(__APPLE__) | ||||
|     path = utils::path::get_parent_path(path); | ||||
|     res = chdir(path.c_str()); | ||||
|     if (res != 0) { | ||||
|       throw std::runtime_error("failed to chdir|" + path + '|' + | ||||
|                                std::to_string(errno)); | ||||
|     } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|     stream_.seekg(static_cast<std::streamoff>(offset)); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
|     auto before = stream_.tellg(); | ||||
|     if (before == -1) { | ||||
|       throw std::runtime_error("failed to tellg() before read"); | ||||
| auto create_temp_name(std::string_view file_part) -> std::string { | ||||
|   std::array<std::uint8_t, 8U> data{ | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|       utils::generate_random_between<std::uint8_t>(0U, 9U), | ||||
|   }; | ||||
|  | ||||
|   return std::accumulate(data.begin(), data.end(), std::string{file_part} + '_', | ||||
|                          [](auto &&name, auto &&val) -> auto { | ||||
|                            return name + std::to_string(val); | ||||
|                          }); | ||||
| } | ||||
|  | ||||
| auto create_temp_name(std::wstring_view file_part) -> std::wstring { | ||||
|   return utils::string::from_utf8( | ||||
|       create_temp_name(utils::string::to_utf8(file_part))); | ||||
| } | ||||
|  | ||||
| auto get_free_drive_space(std::string_view path) | ||||
|     -> std::optional<std::uint64_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
| #if defined(_WIN32) | ||||
|     ULARGE_INTEGER li{}; | ||||
|     if (not ::GetDiskFreeSpaceEx(std::string{path}.c_str(), &li, nullptr, | ||||
|                                  nullptr)) { | ||||
|       throw std::runtime_error("failed to get free disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     stream_.read(reinterpret_cast<char *>(data), | ||||
|                  static_cast<std::streamoff>(to_read)); | ||||
|     return li.QuadPart; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| #if defined(__linux__) | ||||
|     struct statfs64 st {}; | ||||
|     if (statfs64(std::string{path}.c_str(), &st) != 0) { | ||||
|       throw std::runtime_error("failed to get free disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     return st.f_bfree * static_cast<std::uint64_t>(st.f_bsize); | ||||
| #endif // defined(__linux__) | ||||
|  | ||||
| #if defined(__APPLE__) | ||||
|     struct statvfs st {}; | ||||
|     if (statvfs(path.c_str(), &st) != 0) { | ||||
|       throw std::runtime_error("failed to get free disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     return st.f_bfree * static_cast<std::uint64_t>(st.f_frsize); | ||||
| #endif // defined(__APPLE__) | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto get_free_drive_space(std::wstring_view path) | ||||
|     -> std::optional<std::uint64_t> { | ||||
|   return get_free_drive_space(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto get_time(std::string_view path, | ||||
|               time_type type) -> std::optional<std::uint64_t> { | ||||
|   auto times = get_times(path); | ||||
|   if (times.has_value()) { | ||||
|     return times->get(type); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto get_time(std::wstring_view path, | ||||
|               time_type type) -> std::optional<std::uint64_t> { | ||||
|   return get_time(utils::string::to_utf8(path), type); | ||||
| } | ||||
|  | ||||
| auto get_times(std::string_view path) -> std::optional<file_times> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     file_times ret{}; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     auto file_handle = | ||||
|         ::CreateFileA(std::string{path}.c_str(), GENERIC_READ, | ||||
|                       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, | ||||
|                       nullptr, OPEN_EXISTING, 0U, nullptr); | ||||
|     if (file_handle == INVALID_HANDLE_VALUE) { | ||||
|       throw std::runtime_error("failed to get file times|" + std::string{path} + | ||||
|                                '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     std::array<FILETIME, 3U> times{}; | ||||
|     auto res = | ||||
|         ::GetFileTime(file_handle, ×.at(0U), ×.at(1U), ×.at(2U)); | ||||
|     ::CloseHandle(file_handle); | ||||
|  | ||||
|     if (not res) { | ||||
|       throw std::runtime_error("failed to get file times|" + std::string{path} + | ||||
|                                '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     ret.accessed = utils::time::windows_file_time_to_unix_time(times.at(1U)); | ||||
|     ret.created = utils::time::windows_file_time_to_unix_time(times.at(0U)); | ||||
|     ret.modified = utils::time::windows_file_time_to_unix_time(times.at(2U)); | ||||
|     ret.written = utils::time::windows_file_time_to_unix_time(times.at(2U)); | ||||
| #else  // !defined(_WIN32) | ||||
|     struct stat64 st {}; | ||||
|     if (stat64(std::string{path}.c_str(), &st) != 0) { | ||||
|       throw std::runtime_error("failed to get file times|" + std::string{path} + | ||||
|                                '|' + std::to_string(errno)); | ||||
|     } | ||||
|  | ||||
|     ret.accessed = static_cast<std::uint64_t>(st.st_atim.tv_nsec) + | ||||
|                    static_cast<std::uint64_t>(st.st_atim.tv_sec) * | ||||
|                        utils::time::NANOS_PER_SECOND; | ||||
|     ret.created = static_cast<std::uint64_t>(st.st_ctim.tv_nsec) + | ||||
|                   static_cast<std::uint64_t>(st.st_ctim.tv_sec) * | ||||
|                       utils::time::NANOS_PER_SECOND; | ||||
|     ret.modified = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) + | ||||
|                    static_cast<std::uint64_t>(st.st_mtim.tv_sec) * | ||||
|                        utils::time::NANOS_PER_SECOND; | ||||
|     ret.written = static_cast<std::uint64_t>(st.st_mtim.tv_nsec) + | ||||
|                   static_cast<std::uint64_t>(st.st_mtim.tv_sec) * | ||||
|                       utils::time::NANOS_PER_SECOND; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto get_times(std::wstring_view path) -> std::optional<file_times> { | ||||
|   return get_times(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto get_total_drive_space(std::string_view path) | ||||
|     -> std::optional<std::uint64_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
| #if defined(_WIN32) | ||||
|     ULARGE_INTEGER li{}; | ||||
|     if (not ::GetDiskFreeSpaceEx(std::string{path}.c_str(), nullptr, &li, | ||||
|                                  nullptr)) { | ||||
|       throw std::runtime_error("failed to get total disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     return li.QuadPart; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| #if defined(__linux__) | ||||
|     struct statfs64 st {}; | ||||
|     if (statfs64(std::string{path}.c_str(), &st) != 0) { | ||||
|       throw std::runtime_error("failed to get total disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     return st.f_blocks * static_cast<std::uint64_t>(st.f_bsize); | ||||
| #endif // defined(__linux__) | ||||
|  | ||||
| #if defined(__APPLE__) | ||||
|     struct statvfs st {}; | ||||
|     if (statvfs(path.c_str(), &st) != 0) { | ||||
|       throw std::runtime_error("failed to get total disk space|" + | ||||
|                                std::string{path} + '|' + | ||||
|                                std::to_string(utils::get_last_error_code())); | ||||
|     } | ||||
|  | ||||
|     return st.f_blocks * static_cast<std::uint64_t>(st.f_frsize); | ||||
| #endif // defined(__APPLE__) | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto get_total_drive_space(std::wstring_view path) | ||||
|     -> std::optional<std::uint64_t> { | ||||
|   return get_total_drive_space(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto i_fs_item::get_time(time_type type) const -> std::optional<std::uint64_t> { | ||||
|   return utils::file::get_time(get_path(), type); | ||||
| } | ||||
|  | ||||
| auto i_file::read_all(data_buffer &data, std::uint64_t offset, | ||||
|                       std::size_t *total_read) -> bool { | ||||
|   data_buffer buffer; | ||||
|   buffer.resize(get_read_buffer_size()); | ||||
|  | ||||
|   std::size_t current_read{}; | ||||
|   while (read(reinterpret_cast<unsigned char *>(buffer.data()), | ||||
|               buffer.size() * sizeof(data_buffer::value_type), offset, | ||||
|               ¤t_read)) { | ||||
|     if (total_read != nullptr) { | ||||
|       auto after = stream_.tellg(); | ||||
|       if (after >= 0) { | ||||
|         (*total_read) = static_cast<std::size_t>(after - before); | ||||
|       } else if (after == -1 && not stream_.eof()) { | ||||
|         throw std::runtime_error("failed to tellg() after read"); | ||||
|       } | ||||
|       *total_read += current_read; | ||||
|     } | ||||
|  | ||||
|     if (current_read != 0U) { | ||||
|       offset += current_read; | ||||
|  | ||||
|       data.insert( | ||||
|           data.end(), buffer.begin(), | ||||
|           std::next(buffer.begin(), | ||||
|                     static_cast<std::int64_t>( | ||||
|                         current_read / sizeof(data_buffer::value_type)))); | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::remove() -> bool { | ||||
|   close(); | ||||
|   return std::filesystem::remove(path_, error_); | ||||
| } | ||||
|  | ||||
| auto file::truncate(std::size_t size) -> bool { | ||||
|   auto reopen{false}; | ||||
|   if (stream_.is_open()) { | ||||
|     reopen = true; | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   std::filesystem::resize_file(path_, size, error_); | ||||
|   if (reopen) { | ||||
|     *this = open_file(path_); | ||||
|   } | ||||
|  | ||||
|   return not error_; | ||||
| } | ||||
|  | ||||
| auto file::write_(const unsigned char *data, std::size_t to_write, | ||||
|                   std::size_t offset, std::size_t *total_written) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   if (total_written != nullptr) { | ||||
|     (*total_written) = 0U; | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|     stream_.seekp(static_cast<std::streamoff>(offset)); | ||||
|     auto before = stream_.tellp(); | ||||
|     if (before == -1) { | ||||
|       throw std::runtime_error("failed to tellp() before write"); | ||||
|     } | ||||
|  | ||||
|     stream_.write(reinterpret_cast<const char *>(data), | ||||
|                   static_cast<std::streamoff>(to_write)); | ||||
|  | ||||
|     auto after = stream_.tellp(); | ||||
|     if (after == -1) { | ||||
|       throw std::runtime_error("failed to tellp() after write"); | ||||
|     } | ||||
|  | ||||
|     if (total_written != nullptr) { | ||||
|       (*total_written) = static_cast<std::size_t>(after - before); | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto get_file_size(std::string_view path, std::uint64_t &file_size) -> bool { | ||||
|   auto abs_path = utils::path::absolute(path); | ||||
|  | ||||
|   file_size = 0U; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   struct _stat64 st {}; | ||||
|   if (_stat64(abs_path.c_str(), &st) != 0) { | ||||
| #else  // !defined(_WIN32) | ||||
|   struct stat st {}; | ||||
|   if (stat(abs_path.c_str(), &st) != 0) { | ||||
| #endif // defined(_WIN32) | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (st.st_size >= 0) { | ||||
|     file_size = static_cast<std::uint64_t>(st.st_size); | ||||
|   } | ||||
|  | ||||
|   return (st.st_size >= 0); | ||||
| } | ||||
|  | ||||
| auto get_file_size(std::wstring_view path, std::uint64_t &file_size) -> bool { | ||||
|   return get_file_size(utils::string::to_utf8(path), file_size); | ||||
| } | ||||
|  | ||||
| auto is_directory(std::string_view path) -> bool { | ||||
|   auto abs_path = utils::path::absolute(path); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   return ::PathIsDirectoryA(abs_path.c_str()) != 0; | ||||
| #else  // !defined(_WIN32) | ||||
|   struct stat st {}; | ||||
|   return (stat(abs_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| auto is_directory(std::wstring_view path) -> bool { | ||||
|   return is_directory(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto is_file(std::string_view path) -> bool { | ||||
|   auto abs_path = utils::path::absolute(path); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   return (::PathFileExistsA(abs_path.c_str()) && | ||||
|           not ::PathIsDirectoryA(abs_path.c_str())); | ||||
| #else  // !defined(_WIN32) | ||||
|   struct stat st {}; | ||||
|   return (stat(abs_path.c_str(), &st) == 0 && not S_ISDIR(st.st_mode)); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| auto is_file(std::wstring_view path) -> bool { | ||||
|   return is_file(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_JSON) | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| auto read_json_file(std::string_view path, nlohmann::json &data, | ||||
| @@ -255,44 +337,42 @@ auto read_json_file(std::string_view path, nlohmann::json &data) -> bool { | ||||
|  | ||||
|   try { | ||||
|     auto abs_path = utils::path::absolute(path); | ||||
|     if (not is_file(abs_path)) { | ||||
|       return true; | ||||
|     auto file = file::open_file(abs_path); | ||||
|     if (not *file) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     std::ifstream file_stream{ | ||||
|         abs_path.c_str(), | ||||
|         std::ios_base::binary | std::ios::in, | ||||
|     }; | ||||
|     if (not file_stream.is_open()) { | ||||
|       throw std::runtime_error("failed to open file: " + abs_path); | ||||
|     } | ||||
|  | ||||
|     auto ret{true}; | ||||
|     try { | ||||
|       std::stringstream stream; | ||||
|       stream << file_stream.rdbuf(); | ||||
|       data_buffer buffer{}; | ||||
|       if (not file->read_all(buffer, 0U)) { | ||||
|         return false; | ||||
|       } | ||||
|  | ||||
|       auto json_text = stream.str(); | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|       if (password.has_value()) { | ||||
|         auto decrypted_data = | ||||
|             utils::encryption::decrypt_data(*password, json_text); | ||||
|         json_text = {decrypted_data.begin(), decrypted_data.end()}; | ||||
|         data_buffer decrypted_data{}; | ||||
|         if (not utils::encryption::decrypt_data(*password, buffer, | ||||
|                                                 decrypted_data)) { | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         buffer = decrypted_data; | ||||
|       } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|       if (not json_text.empty()) { | ||||
|         data = nlohmann::json::parse(json_text.c_str()); | ||||
|  | ||||
|       std::string json_str(buffer.begin(), buffer.end()); | ||||
|       if (not json_str.empty()) { | ||||
|         data = nlohmann::json::parse(json_str); | ||||
|       } | ||||
|     } catch (const std::exception &e) { | ||||
|       utils::error::handle_exception(function_name, e); | ||||
|       ret = false; | ||||
|       return false; | ||||
|     } catch (...) { | ||||
|       utils::error::handle_exception(function_name); | ||||
|       ret = false; | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     file_stream.close(); | ||||
|     return ret; | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
| @@ -315,19 +395,26 @@ auto write_json_file(std::string_view path, | ||||
|  | ||||
|   try { | ||||
|     auto file = file::open_or_create_file(path); | ||||
|     if (not file.truncate()) { | ||||
|       throw std::runtime_error("failed to truncate file: " + | ||||
|                                file.get_error_code().message()); | ||||
|     if (not file->truncate()) { | ||||
|       throw std::runtime_error("failed to truncate file"); | ||||
|     } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|     if (password.has_value()) { | ||||
|       return file.write(utils::encryption::encrypt_data(*password, data.dump()), | ||||
|                         0U); | ||||
|       const auto str_data = data.dump(); | ||||
|  | ||||
|       data_buffer encrypted_data{}; | ||||
|       utils::encryption::encrypt_data( | ||||
|           *password, reinterpret_cast<const unsigned char *>(str_data.c_str()), | ||||
|           str_data.size(), encrypted_data); | ||||
|       return file->write(encrypted_data, 0U); | ||||
|     } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
|     return file.write(data, 0U); | ||||
|     auto json_str = data.dump(); | ||||
|     return file->write( | ||||
|         reinterpret_cast<const unsigned char *>(json_str.c_str()), | ||||
|         json_str.size(), 0U); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
| @@ -368,4 +455,170 @@ auto write_json_file(std::wstring_view path, | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| static constexpr const auto validate_smb_path = | ||||
|     [](std::string_view path) -> bool { | ||||
|   return (not utils::string::begins_with(path, "///") && | ||||
|           utils::string::begins_with(path, "//") && | ||||
|           not utils::string::contains(path, " ") && | ||||
|           std::count(path.begin(), path.end(), '/') >= 3U); | ||||
| }; | ||||
|  | ||||
| auto smb_create_smb_path(std::string_view smb_path, | ||||
|                          std::string_view rel_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   std::string path{rel_path}; | ||||
|   utils::path::format_path(path, "/", "\\"); | ||||
|   utils::string::left_trim(path, '/'); | ||||
|  | ||||
|   auto old_parts = | ||||
|       repertory::utils::string::split(smb_path.substr(2U), '/', false); | ||||
|   old_parts.erase(std::next(old_parts.begin(), 2U), old_parts.end()); | ||||
|  | ||||
|   auto new_parts = repertory::utils::string::split(path, '/', false); | ||||
|   old_parts.insert(old_parts.end(), new_parts.begin(), new_parts.end()); | ||||
|  | ||||
|   path = utils::string::join(old_parts, '/'); | ||||
|   path = "//" + utils::path::format_path(path, "/", "\\"); | ||||
|  | ||||
|   if (not validate_smb_path(path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{path}); | ||||
|   } | ||||
|  | ||||
|   return path; | ||||
| } | ||||
|  | ||||
| auto smb_create_and_validate_relative_path( | ||||
|     std::string_view smb_path, std::string_view path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   std::string dir_path; | ||||
|   if (utils::string::begins_with(path, "//")) { | ||||
|     if (not utils::file::smb_parent_is_same(smb_path, path)) { | ||||
|       throw std::runtime_error("failed to validate path|" + | ||||
|                                std::string{smb_path} + '|' + std::string{path} + | ||||
|                                "|parent paths are not the same"); | ||||
|     } | ||||
|  | ||||
|     return utils::file::smb_create_relative_path(path); | ||||
|   } | ||||
|  | ||||
|   return utils::file::smb_create_relative_path(std::string{smb_path} + '/' + | ||||
|                                                std::string{path}); | ||||
| } | ||||
|  | ||||
| auto smb_create_relative_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   std::string path{smb_path}; | ||||
|   utils::path::format_path(path, "\\", "/"); | ||||
|   utils::string::left_trim(path, '\\'); | ||||
|  | ||||
|   auto parts = repertory::utils::string::split(path, '\\', false); | ||||
|   parts.erase(parts.begin(), std::next(parts.begin(), 2U)); | ||||
|   return "\\" + utils::string::join(parts, '\\'); | ||||
| } | ||||
|  | ||||
| auto smb_create_search_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   std::string path{smb_path}; | ||||
|   utils::string::left_trim(path, '/'); | ||||
|  | ||||
|   auto parts = repertory::utils::string::split(path, '/', false); | ||||
|   parts.erase(parts.begin(), std::next(parts.begin(), 2U)); | ||||
|  | ||||
|   auto search_path = repertory::utils::string::join(parts, '\\'); | ||||
|   return search_path.empty() ? "\\*" : "\\" + search_path + "\\*"; | ||||
| } | ||||
|  | ||||
| auto smb_get_parent_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   auto parts = repertory::utils::string::split(smb_path.substr(2U), '/', false); | ||||
|   if (parts.size() > 2U) { | ||||
|     parts.erase(std::prev(parts.end()), parts.end()); | ||||
|   } | ||||
|  | ||||
|   auto parent_smb_path = "//" + utils::string::join(parts, '/'); | ||||
|   if (not validate_smb_path(parent_smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + parent_smb_path); | ||||
|   } | ||||
|  | ||||
|   return parent_smb_path; | ||||
| } | ||||
|  | ||||
| auto smb_get_root_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   auto parts = repertory::utils::string::split(smb_path.substr(2U), '/', false); | ||||
|   if (parts.size() > 2U) { | ||||
|     parts.erase(std::next(parts.begin(), 2U), parts.end()); | ||||
|   } | ||||
|  | ||||
|   return "//" + utils::string::join(parts, '/'); | ||||
| } | ||||
|  | ||||
| auto smb_get_unc_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   std::string unc_path{smb_path}; | ||||
|   utils::path::format_path(unc_path, "\\", "/"); | ||||
|   return '\\' + unc_path; | ||||
| } | ||||
|  | ||||
| auto smb_get_uri_path(std::string_view smb_path) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   return "smb:" + std::string{smb_path}; | ||||
| } | ||||
|  | ||||
| auto smb_get_uri_path(std::string_view smb_path, std::string_view user, | ||||
|                       std::string_view password) -> std::string { | ||||
|   if (not validate_smb_path(smb_path)) { | ||||
|     throw std::runtime_error("invalid smb path|" + std::string{smb_path}); | ||||
|   } | ||||
|  | ||||
|   return "smb://" + std::string{user} + ':' + std::string{password} + '@' + | ||||
|          std::string{smb_path.substr(2U)}; | ||||
| } | ||||
|  | ||||
| auto smb_parent_is_same(std::string_view smb_path1, | ||||
|                         std::string_view smb_path2) -> bool { | ||||
|   if (not(validate_smb_path(smb_path1) && validate_smb_path(smb_path2))) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   auto parts1 = utils::string::split(smb_path1.substr(2U), "/", false); | ||||
|   auto parts2 = utils::string::split(smb_path2.substr(2U), "/", false); | ||||
|   if (parts1.size() < 2U || parts2.size() < 2U) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (parts2.at(1U).empty() || parts1.at(1U).empty()) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return std::equal(parts1.begin(), std::next(parts1.begin(), 2U), | ||||
|                     parts2.begin()); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
| } // namespace repertory::utils::file | ||||
|   | ||||
							
								
								
									
										466
									
								
								support/src/utils/file_directory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										466
									
								
								support/src/utils/file_directory.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,466 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/unix.hpp" | ||||
| #include "utils/windows.hpp" | ||||
|  | ||||
| namespace { | ||||
| auto traverse_directory( | ||||
|     std::string_view path, | ||||
|     std::function<bool(repertory::utils::file::directory)> directory_action, | ||||
|     std::function<bool(repertory::utils::file::file)> file_action) -> bool { | ||||
|   auto res{true}; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   WIN32_FIND_DATAA fd{}; | ||||
|   auto search = repertory::utils::path::combine(path, {"*.*"}); | ||||
|   auto find = ::FindFirstFileA(search.c_str(), &fd); | ||||
|   if (find == INVALID_HANDLE_VALUE) { | ||||
|     throw std::runtime_error( | ||||
|         "failed to open directory|" + std::string{path} + '|' + | ||||
|         std::to_string(repertory::utils::get_last_error_code())); | ||||
|   } | ||||
|  | ||||
|   do { | ||||
|     if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | ||||
|       if ((std::string_view(fd.cFileName) != ".") && | ||||
|           (std::string_view(fd.cFileName) != "..")) { | ||||
|         res = directory_action(repertory::utils::file::directory{ | ||||
|             repertory::utils::path::combine(path, {fd.cFileName})}); | ||||
|       } | ||||
|     } else { | ||||
|       res = file_action(repertory::utils::file::file( | ||||
|           repertory::utils::path::combine(path, {fd.cFileName}))); | ||||
|     } | ||||
|   } while (res && (::FindNextFileA(find, &fd) != 0)); | ||||
|  | ||||
|   ::FindClose(find); | ||||
| #else  // !defined(_WIN32) | ||||
|   auto *root = opendir(std::string{path}.c_str()); | ||||
|   if (root == nullptr) { | ||||
|     throw std::runtime_error( | ||||
|         "failed to open directory|" + std::string{path} + '|' + | ||||
|         std::to_string(repertory::utils::get_last_error_code())); | ||||
|   } | ||||
|  | ||||
|   struct dirent *de{nullptr}; | ||||
|   while (res && (de = readdir(root))) { | ||||
|     if (de->d_type == DT_DIR) { | ||||
|       if ((std::string_view(de->d_name) != ".") && | ||||
|           (std::string_view(de->d_name) != "..")) { | ||||
|         res = directory_action(repertory::utils::file::directory( | ||||
|             repertory::utils::path::combine(path, {de->d_name}))); | ||||
|       } | ||||
|     } else { | ||||
|       res = file_action(repertory::utils::file::file( | ||||
|           repertory::utils::path::combine(path, {de->d_name}))); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   closedir(root); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return res; | ||||
| } | ||||
| } // namespace | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| auto directory::copy_to(std::string_view new_path, | ||||
|                         bool overwrite) const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     throw std::runtime_error("failed to copy directory|" + path_ + '|' + | ||||
|                              std::string{new_path} + '+' + | ||||
|                              std::to_string(overwrite) + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto directory::count(bool recursive) const -> std::uint64_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     std::uint64_t ret{0U}; | ||||
|  | ||||
|     traverse_directory( | ||||
|         path_, | ||||
|         [&ret, &recursive](auto dir_item) -> bool { | ||||
|           if (recursive) { | ||||
|             ret += dir_item.count(true); | ||||
|           } | ||||
|  | ||||
|           ++ret; | ||||
|           return true; | ||||
|         }, | ||||
|         [&ret](auto /* file_item */) -> bool { | ||||
|           ++ret; | ||||
|           return true; | ||||
|         }); | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return 0U; | ||||
| } | ||||
|  | ||||
| auto directory::create_directory(std::string_view path) const | ||||
|     -> fs_directory_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     auto abs_path = utils::path::combine(path_, {path}); | ||||
|     if (directory{abs_path}.exists()) { | ||||
|       return std::make_unique<directory>(abs_path); | ||||
|     } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     auto res = ::SHCreateDirectory(nullptr, | ||||
|                                    utils::string::from_utf8(abs_path).c_str()); | ||||
|     if (res != ERROR_SUCCESS) { | ||||
|       throw std::runtime_error("failed to create directory|" + | ||||
|                                std::string{abs_path} + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
| #else // !defined(_WIN32) | ||||
|     auto ret{true}; | ||||
|     auto paths = | ||||
|         utils::string::split(abs_path, utils::path::directory_seperator, false); | ||||
|  | ||||
|     std::string current_path; | ||||
|     for (std::size_t idx = 0U; ret && (idx < paths.size()); ++idx) { | ||||
|       if (paths.at(idx).empty()) { | ||||
|         current_path = utils::path::directory_seperator; | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       current_path = utils::path::combine(current_path, {paths.at(idx)}); | ||||
|       auto status = mkdir(current_path.c_str(), S_IRWXU); | ||||
|       ret = ((status == 0) || (errno == EEXIST)); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     return std::make_unique<directory>(abs_path); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto directory::exists() const -> bool { | ||||
| #if defined(_WIN32) | ||||
|   return ::PathIsDirectoryA(path_.c_str()) != 0; | ||||
| #else  // !defined(_WIN32) | ||||
|   struct stat64 st {}; | ||||
|   return (stat64(path_.c_str(), &st) == 0 && S_ISDIR(st.st_mode)); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto directory::get_directory(std::string_view path) const -> fs_directory_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     auto dir_path = utils::path::combine(path_, {path}); | ||||
|     return fs_directory_t{ | ||||
|         directory{dir_path}.exists() ? new directory{dir_path} : nullptr, | ||||
|     }; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto directory::get_directories() const -> std::vector<fs_directory_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     std::vector<fs_directory_t> ret{}; | ||||
|  | ||||
|     traverse_directory( | ||||
|         path_, | ||||
|         [&ret](auto dir_item) -> bool { | ||||
|           ret.emplace_back(fs_directory_t{ | ||||
|               new directory(dir_item.get_path()), | ||||
|           }); | ||||
|  | ||||
|           return true; | ||||
|         }, | ||||
|         [](auto /* file_item */) -> bool { return true; }); | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto directory::create_file(std::string_view file_name, | ||||
|                             bool read_only) const -> fs_file_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     auto file_path = utils::path::combine(path_, {file_name}); | ||||
|     return file::open_or_create_file(file_path, read_only); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto directory::get_file(std::string_view path) const -> fs_file_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     auto file_path = utils::path::combine(path_, {path}); | ||||
|     return fs_file_t{ | ||||
|         file{file_path}.exists() ? new file(file_path) : nullptr, | ||||
|     }; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto directory::get_files() const -> std::vector<fs_file_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     std::vector<fs_file_t> ret{}; | ||||
|  | ||||
|     traverse_directory( | ||||
|         path_, [](auto /* dir_item */) -> bool { return true; }, | ||||
|         [&ret](auto file_item) -> bool { | ||||
|           ret.emplace_back(fs_file_t{ | ||||
|               new file(file_item.get_path()), | ||||
|           }); | ||||
|           return true; | ||||
|         }); | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto directory::get_items() const -> std::vector<fs_item_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     std::vector<fs_item_t> ret{}; | ||||
|  | ||||
|     traverse_directory( | ||||
|         path_, | ||||
|         [&ret](auto dir_item) -> bool { | ||||
|           ret.emplace_back(fs_item_t{ | ||||
|               new directory(dir_item.get_path()), | ||||
|           }); | ||||
|           return true; | ||||
|         }, | ||||
|         [&ret](auto file_item) -> bool { | ||||
|           ret.emplace_back(fs_item_t{ | ||||
|               new file(file_item.get_path()), | ||||
|           }); | ||||
|           return true; | ||||
|         }); | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto directory::is_symlink() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     return std::filesystem::is_symlink(path_); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto directory::move_to(std::string_view new_path) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     throw std::runtime_error("failed to move directory|" + path_ + '|' + | ||||
|                              std::string{new_path} + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto directory::remove() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   if (not exists()) { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   return utils::retry_action([this]() -> bool { | ||||
|     try { | ||||
| #if defined(_WIN32) | ||||
|       return ::RemoveDirectoryA(path_.c_str()); | ||||
| #else  // !defined(_WIN32) | ||||
|       return (rmdir(path_.c_str()) == 0); | ||||
| #endif // defined(_WIN32) | ||||
|     } catch (const std::exception &e) { | ||||
|       utils::error::handle_exception(function_name, e); | ||||
|     } catch (...) { | ||||
|       utils::error::handle_exception(function_name); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| auto directory::remove_recursively() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not exists()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     if (not traverse_directory( | ||||
|             path_, | ||||
|             [](auto dir_item) -> bool { return dir_item.remove_recursively(); }, | ||||
|             [](auto file_item) -> bool { return file_item.remove(); })) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return remove(); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto directory::size(bool recursive) const -> std::uint64_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     std::uint64_t ret{0U}; | ||||
|  | ||||
|     traverse_directory( | ||||
|         path_, | ||||
|         [&ret, &recursive](auto dir_item) -> bool { | ||||
|           if (recursive) { | ||||
|             ret += dir_item.size(true); | ||||
|           } | ||||
|  | ||||
|           return true; | ||||
|         }, | ||||
|         [&ret](auto file_item) -> bool { | ||||
|           auto cur_size = file_item.size(); | ||||
|           if (cur_size.has_value()) { | ||||
|             ret += cur_size.value(); | ||||
|           } | ||||
|           return true; | ||||
|         }); | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return 0U; | ||||
| } | ||||
| } // namespace repertory::utils::file | ||||
							
								
								
									
										53
									
								
								support/src/utils/file_enc_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								support/src/utils/file_enc_file.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| auto enc_file::attach_file(fs_file_t file) -> fs_file_t {} | ||||
|  | ||||
| enc_file::enc_file(fs_file_t file) : file_(std::move(file)) {} | ||||
|  | ||||
| void enc_file::close() {} | ||||
|  | ||||
| auto enc_file::copy_to(std::string_view new_path, | ||||
|                        bool overwrite) const -> bool {} | ||||
|  | ||||
| void enc_file::flush() const {} | ||||
|  | ||||
| auto enc_file::move_to(std::string_view path) -> bool {} | ||||
|  | ||||
| auto enc_file::read(unsigned char *data, std::size_t to_read, | ||||
|                     std::uint64_t offset, std::size_t *total_read) -> bool {} | ||||
|  | ||||
| auto enc_file::remove() -> bool {} | ||||
|  | ||||
| auto enc_file::truncate(std::size_t size) -> bool {} | ||||
|  | ||||
| auto enc_file::write(const unsigned char *data, std::size_t to_write, | ||||
|                      std::size_t offset, std::size_t *total_written) -> bool {} | ||||
|  | ||||
| auto enc_file::size() const -> std::optional<std::uint64_t> {} | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
							
								
								
									
										539
									
								
								support/src/utils/file_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										539
									
								
								support/src/utils/file_file.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,539 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/path.hpp" | ||||
|  | ||||
| namespace { | ||||
| [[nodiscard]] auto get_file_size(std::string_view path, | ||||
|                                  std::uint64_t &file_size) -> bool { | ||||
|   auto abs_path = repertory::utils::path::absolute(path); | ||||
|   file_size = 0U; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   struct _stat64 st {}; | ||||
|   auto res = _stat64(std::string{path}.c_str(), &st); | ||||
|   if (res != 0) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   file_size = static_cast<std::uint64_t>(st.st_size); | ||||
|   return true; | ||||
| #else  // !defined(_WIN32) | ||||
|   std::error_code ec{}; | ||||
|   file_size = std::filesystem::file_size(abs_path, ec); | ||||
|   return (ec.value() == 0); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| [[nodiscard]] auto is_file(std::string_view path) -> bool { | ||||
|   auto abs_path = repertory::utils::path::absolute(path); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   return (::PathFileExistsA(abs_path.c_str()) && | ||||
|           not ::PathIsDirectoryA(abs_path.c_str())); | ||||
| #else  // !defined(_WIN32) | ||||
|   struct stat64 st {}; | ||||
|   return (stat64(abs_path.c_str(), &st) == 0 && not S_ISDIR(st.st_mode)); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
| } // namespace | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| // auto file::attach_file(native_handle handle, | ||||
| //                        bool read_only) -> fs_file_t { | ||||
| //   static constexpr const std::string_view function_name{ | ||||
| //       static_cast<const char *>(__FUNCTION__), | ||||
| //   }; | ||||
| // | ||||
| //   try { | ||||
| //     std::string path; | ||||
| // | ||||
| // #if defined(_WIN32) | ||||
| //     path.resize(repertory::max_path_length + 1); | ||||
| //     ::GetFinalPathNameByHandleA(handle, path.data(), | ||||
| //                                 static_cast<DWORD>(path.size()), | ||||
| //                                 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); | ||||
| // #else // !defined(_WIN32) | ||||
| //     path.resize(repertory::max_path_length + 1); | ||||
| // | ||||
| // #if defined(__APPLE__) | ||||
| //     fcntl(handle, F_GETPATH, source_path.data()); | ||||
| // #else  // !defined(__APPLE__) | ||||
| //     readlink(("/proc/self/fd/" + std::to_string(handle)).c_str(), | ||||
| //     path.data(), | ||||
| //              path.size()); | ||||
| // #endif // defined(__APPLE__) | ||||
| // #endif // defined(_WIN32) | ||||
| // | ||||
| //     path = path.c_str(); | ||||
| // | ||||
| // #if defined(_WIN32) | ||||
| //     auto *ptr = _fdopen( | ||||
| //         static_cast<int>(_open_osfhandle(reinterpret_cast<intptr_t>(handle), | ||||
| //                                          read_only ? _O_RDONLY : _O_RDWR)), | ||||
| //         read_only ? "rb" : "rb+"); | ||||
| // #else  // !defined(_WIN32) | ||||
| //     auto *ptr = fdopen(handle, read_only ? "rb" : "rb+"); | ||||
| // #endif // defined(_WIN32) | ||||
| // | ||||
| //     return fs_file_t(new file{ | ||||
| //         file_t{ptr}, | ||||
| //         utils::path::absolute(path), | ||||
| //         read_only, | ||||
| //     }); | ||||
| //   } catch (const std::exception &e) { | ||||
| //     utils::error::handle_exception(function_name, e); | ||||
| //   } catch (...) { | ||||
| //     utils::error::handle_exception(function_name); | ||||
| //   } | ||||
| // | ||||
| //   return nullptr; | ||||
| // } | ||||
|  | ||||
| void file::open() { | ||||
|   if (not is_file(path_)) { | ||||
|     throw std::runtime_error("file not found: " + path_); | ||||
|   } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   file_.reset(_fsopen(path_.c_str(), read_only_ ? "rb" : "rb+", _SH_DENYNO)); | ||||
| #else  // !defined(_WIN32) | ||||
|   file_.reset(fopen(path_.c_str(), read_only_ ? "rb" : "rb+")); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| auto file::open_file(std::string_view path, bool read_only) -> fs_file_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   auto *ptr = new file{ | ||||
|       nullptr, | ||||
|       utils::path::absolute(path), | ||||
|       read_only, | ||||
|   }; | ||||
|   auto new_file = fs_file_t(ptr); | ||||
|  | ||||
|   try { | ||||
|     ptr->open(); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return new_file; | ||||
| } | ||||
|  | ||||
| auto file::open_or_create_file(std::string_view path, | ||||
|                                bool read_only) -> fs_file_t { | ||||
|   auto abs_path = utils::path::absolute(path); | ||||
|   if (not is_file(abs_path)) { | ||||
| #if defined(_WIN32) | ||||
|     int old_mode{}; | ||||
|     _umask_s(077, &old_mode); | ||||
| #else  // !defined(_WIN32) | ||||
|     auto old_mode = umask(077); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     auto *ptr = _fsopen(abs_path.c_str(), "ab+", _SH_DENYNO); | ||||
| #else  // !defined(_WIN32) | ||||
|     auto *ptr = fopen(abs_path.c_str(), "ab+"); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|     if (ptr != nullptr) { | ||||
|       fclose(ptr); | ||||
|     } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     _umask_s(old_mode, nullptr); | ||||
| #else  // !defined(_WIN32) | ||||
|     umask(old_mode); | ||||
| #endif // defined(_WIN32) | ||||
|   } | ||||
|  | ||||
|   return open_file(abs_path, read_only); | ||||
| } | ||||
|  | ||||
| void file::close() { file_.reset(); } | ||||
|  | ||||
| auto file::copy_to(std::string_view new_path, bool overwrite) const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     auto to_path = utils::path::absolute(new_path); | ||||
|     if (directory(to_path).exists()) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     return ::CopyFileA(path_.c_str(), to_path.c_str(), | ||||
|                        overwrite ? TRUE : FALSE); | ||||
| #else  // !defined(_WIN32) | ||||
|     return std::filesystem::copy_file( | ||||
|         path_, to_path, | ||||
|         overwrite ? std::filesystem::copy_options::overwrite_existing | ||||
|                   : std::filesystem::copy_options::skip_existing); | ||||
| #endif // defined(_WIN32) | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::exists() const -> bool { return is_file(path_); } | ||||
|  | ||||
| void file::flush() const { | ||||
|   if (file_) { | ||||
|     fflush(file_.get()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto file::get_handle() const -> native_handle { | ||||
|   if (file_) { | ||||
| #if defined(_WIN32) | ||||
|     return reinterpret_cast<native_handle>( | ||||
|         _get_osfhandle(_fileno(file_.get()))); | ||||
| #else  // !defined(_WIN32) | ||||
|     return fileno(file_.get()); | ||||
| #endif // defined(_WIN32) | ||||
|   } | ||||
|  | ||||
|   return INVALID_HANDLE_VALUE; | ||||
| } | ||||
|  | ||||
| auto file::is_symlink() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     return std::filesystem::is_symlink(path_); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::move_to(std::string_view path) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   auto abs_path = utils::path::absolute(path); | ||||
|  | ||||
|   auto reopen{false}; | ||||
|   if (file_) { | ||||
|     reopen = true; | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   auto success{false}; | ||||
| #if defined(_WIN32) | ||||
|   success = !!::MoveFileExA(path_.c_str(), abs_path.c_str(), | ||||
|                             MOVEFILE_REPLACE_EXISTING); | ||||
| #else  // !// defined(_WIN32) | ||||
|   std::error_code ec{}; | ||||
|   std::filesystem::rename(path_, abs_path, ec); | ||||
|   success = ec.value() == 0; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   if (success) { | ||||
|     path_ = abs_path; | ||||
|   } | ||||
|  | ||||
|   if (reopen) { | ||||
|     try { | ||||
|       open(); | ||||
|       return success; | ||||
|     } catch (const std::exception &e) { | ||||
|       utils::error::handle_exception(function_name, e); | ||||
|     } catch (...) { | ||||
|       utils::error::handle_exception(function_name); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::read(unsigned char *data, std::size_t to_read, std::uint64_t offset, | ||||
|                 std::size_t *total_read) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   if (total_read != nullptr) { | ||||
|     (*total_read) = 0U; | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|     if (not file_) { | ||||
|       throw std::runtime_error("file is not open for reading"); | ||||
|     } | ||||
|  | ||||
|     if (fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET) == | ||||
|         -1) { | ||||
|       throw std::runtime_error("failed to seek before read"); | ||||
|     } | ||||
|  | ||||
|     std::size_t bytes_read{0U}; | ||||
|     while (bytes_read != to_read) { | ||||
|       auto res = | ||||
|           fread(&data[bytes_read], 1U, to_read - bytes_read, file_.get()); | ||||
|       if (not feof(file_.get()) && ferror(file_.get())) { | ||||
|         throw std::runtime_error("failed to read file bytes"); | ||||
|       } | ||||
|  | ||||
|       if (res == 0) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       bytes_read += static_cast<std::size_t>(res); | ||||
|     } | ||||
|  | ||||
|     if (total_read != nullptr) { | ||||
|       (*total_read) = bytes_read; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| auto file::sha256() -> std::optional<std::string> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   auto should_close{false}; | ||||
|   auto read_only{read_only_}; | ||||
|   std::optional<std::string> ret; | ||||
|  | ||||
|   try { | ||||
|     if (file_ == nullptr) { | ||||
|       should_close = true; | ||||
|       read_only_ = true; | ||||
|       this->open(); | ||||
|     } | ||||
|  | ||||
|     crypto_hash_sha256_state state{}; | ||||
|     auto res = crypto_hash_sha256_init(&state); | ||||
|     if (res != 0) { | ||||
|       throw std::runtime_error("failed to initialize sha256|" + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     { | ||||
|       data_buffer buffer(get_read_buffer_size()); | ||||
|       std::uint64_t read_offset{0U}; | ||||
|       std::size_t bytes_read{0U}; | ||||
|       while (i_file::read(buffer, read_offset, &bytes_read)) { | ||||
|         if (not bytes_read) { | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|         read_offset += bytes_read; | ||||
|         res = crypto_hash_sha256_update( | ||||
|             &state, reinterpret_cast<const unsigned char *>(buffer.data()), | ||||
|             bytes_read); | ||||
|         if (res != 0) { | ||||
|           throw std::runtime_error("failed to update sha256|" + | ||||
|                                    std::to_string(res)); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     std::array<unsigned char, crypto_hash_sha256_BYTES> out{}; | ||||
|     res = crypto_hash_sha256_final(&state, out.data()); | ||||
|     if (res != 0) { | ||||
|       throw std::runtime_error("failed to finalize sha256|" + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     ret = utils::collection::to_hex_string(out); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   if (should_close) { | ||||
|     read_only_ = read_only; | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| auto file::remove() -> bool { | ||||
|   if (not exists()) { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   close(); | ||||
|  | ||||
|   return utils::retry_action([this]() -> bool { | ||||
| #if defined(_WIN32) | ||||
|     return ::DeleteFileA(path_.c_str()); | ||||
| #else  // !defined(_WIN32) | ||||
|     std::error_code ec{}; | ||||
|     return std::filesystem::remove(path_, ec); | ||||
| #endif // defined(_WIN32) | ||||
|   }); | ||||
| } | ||||
|  | ||||
| auto file::truncate(std::size_t size) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   auto reopen{false}; | ||||
|   if (file_) { | ||||
|     reopen = true; | ||||
|     close(); | ||||
|   } | ||||
|  | ||||
|   std::error_code ec{}; | ||||
|   std::filesystem::resize_file(path_, size, ec); | ||||
|  | ||||
|   auto success{ec.value() == 0}; | ||||
|  | ||||
|   if (reopen) { | ||||
|     try { | ||||
|       open(); | ||||
|     } catch (const std::exception &e) { | ||||
|       utils::error::handle_exception(function_name, e); | ||||
|       success = false; | ||||
|     } catch (...) { | ||||
|       utils::error::handle_exception(function_name); | ||||
|       success = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| auto file::write(const unsigned char *data, std::size_t to_write, | ||||
|                  std::size_t offset, std::size_t *total_written) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   if (total_written != nullptr) { | ||||
|     (*total_written) = 0U; | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|     if (not file_) { | ||||
|       throw std::runtime_error("file is not open for writing"); | ||||
|     } | ||||
|  | ||||
|     auto res = fseeko(file_.get(), static_cast<std::int64_t>(offset), SEEK_SET); | ||||
|     if (res == -1) { | ||||
|       throw std::runtime_error("failed to seek before write"); | ||||
|     } | ||||
|  | ||||
|     std::size_t bytes_written{0U}; | ||||
|     while (bytes_written != to_write) { | ||||
|       res = fwrite(reinterpret_cast<const char *>(&data[bytes_written]), 1U, | ||||
|                    to_write - bytes_written, file_.get()); | ||||
|       if (not feof(file_.get()) && ferror(file_.get())) { | ||||
|         throw std::runtime_error("failed to write file bytes"); | ||||
|       } | ||||
|  | ||||
|       if (res == 0) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       bytes_written += static_cast<std::size_t>(res); | ||||
|     } | ||||
|  | ||||
|     flush(); | ||||
|  | ||||
|     if (total_written != nullptr) { | ||||
|       (*total_written) = bytes_written; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto file::size() const -> std::optional<std::uint64_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (file_) { | ||||
|       if (fseeko(file_.get(), 0, SEEK_END) == -1) { | ||||
|         throw std::runtime_error("failed to seek"); | ||||
|       } | ||||
|  | ||||
|       auto size = ftello(file_.get()); | ||||
|       if (size == -1) { | ||||
|         throw std::runtime_error("failed to get position"); | ||||
|       } | ||||
|  | ||||
|       return static_cast<std::uint64_t>(size); | ||||
|     } | ||||
|  | ||||
|     std::uint64_t size{}; | ||||
|     if (not get_file_size(path_, size)) { | ||||
|       throw std::runtime_error("failed to get file size"); | ||||
|     } | ||||
|  | ||||
|     return size; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
| } // namespace repertory::utils::file | ||||
							
								
								
									
										615
									
								
								support/src/utils/file_smb_directory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										615
									
								
								support/src/utils/file_smb_directory.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,615 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/error.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| namespace repertory::utils::file { | ||||
| auto smb_directory::open(std::string_view host, std::string_view user, | ||||
|                          std::string_view password, | ||||
|                          std::string_view share_name) -> smb_directory_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     smb_session_t session{smb_session_new(), smb_session_deleter}; | ||||
|     netbios_ns_t ns{netbios_ns_new()}; | ||||
|  | ||||
|     sockaddr_in addr{}; | ||||
|  | ||||
|     auto res = netbios_ns_resolve( | ||||
|         ns.get(), std::string{host}.c_str(), NETBIOS_FILESERVER, | ||||
|         reinterpret_cast<std::uint32_t *>(&addr.sin_addr.s_addr)); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       res = inet_pton(AF_INET, std::string{host}.c_str(), &addr.sin_addr); | ||||
|       if (res != 1) { | ||||
|         throw std::runtime_error("failed to resolve host|" + std::string{host} + | ||||
|                                  '|' + std::to_string(errno)); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     res = smb_session_connect(session.get(), std::string{host}.c_str(), | ||||
|                               static_cast<std::uint32_t>(addr.sin_addr.s_addr), | ||||
|                               SMB_TRANSPORT_TCP); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to connect to host|" + | ||||
|                                std::string{host} + '|' + std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     smb_session_set_creds(session.get(), std::string{host}.c_str(), | ||||
|                           std::string{user}.c_str(), | ||||
|                           std::string{password}.c_str()); | ||||
|     res = smb_session_login(session.get()); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to logon to host|" + std::string{host} + | ||||
|                                '|' + std::string{user} + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     smb_tid tid{}; | ||||
|     res = | ||||
|         smb_tree_connect(session.get(), std::string{share_name}.c_str(), &tid); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to connect to share|" + | ||||
|                                std::string{share_name} + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     return smb_directory_t{ | ||||
|         new smb_directory{ | ||||
|             "//" + std::string{host} + "/" + std::string{share_name}, | ||||
|             session, | ||||
|             share_name, | ||||
|             tid, | ||||
|         }, | ||||
|     }; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto smb_directory::open(std::wstring_view host, std::wstring_view user, | ||||
|                          std::wstring_view password, | ||||
|                          std::wstring_view share_name) -> smb_directory_t { | ||||
|   return open(utils::string::to_utf8(host), utils::string::to_utf8(user), | ||||
|               utils::string::to_utf8(password), | ||||
|               utils::string::to_utf8(share_name)); | ||||
| } | ||||
|  | ||||
| auto smb_directory::copy_to(std::string_view new_path, | ||||
|                             bool overwrite) const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     // auto to_path = utils::path::absolute(new_path); | ||||
|  | ||||
|     throw std::runtime_error("failed to copy directory|" + path_ + '|' + | ||||
|                              std::string{new_path} + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::count(bool recursive) const -> std::uint64_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_list_t list{ | ||||
|         smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; | ||||
|     auto count = smb_stat_list_count(list.get()); | ||||
|  | ||||
|     if (not recursive) { | ||||
|       return count; | ||||
|     } | ||||
|  | ||||
|     throw std::runtime_error("failed to get directory count recursively|" + | ||||
|                              path_ + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return 0U; | ||||
| } | ||||
|  | ||||
| auto smb_directory::create_directory(std::string_view path) const | ||||
|     -> fs_directory_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     auto dir = get_directory(path); | ||||
|     if (dir) { | ||||
|       return dir; | ||||
|     } | ||||
|  | ||||
|     auto res = smb_directory_create( | ||||
|         session_.get(), tid_, | ||||
|         smb_create_and_validate_relative_path(path_, path).c_str()); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to create directory|" + path_ + '/' + | ||||
|                                std::string{path} + '|' + std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     return get_directory(path); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto smb_directory::create_file(std::string_view file_name, | ||||
|                                 bool read_only) const -> fs_file_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     auto fs_file = get_file(file_name); | ||||
|     if (fs_file) { | ||||
|       if (not dynamic_cast<smb_file *>(fs_file.get())->open(read_only)) { | ||||
|         throw std::runtime_error("failed to open existing file|" + | ||||
|                                  std::string{file_name}); | ||||
|       } | ||||
|  | ||||
|       return fs_file; | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_and_validate_relative_path(path_, file_name); | ||||
|  | ||||
|     smb_fd fd{}; | ||||
|     auto res = | ||||
|         smb_fopen(session_.get(), tid_, rel_path.c_str(), SMB_MOD_RW, &fd); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       return nullptr; | ||||
|     } | ||||
|     smb_fclose(session_.get(), fd); | ||||
|  | ||||
|     res = smb_fopen(session_.get(), tid_, rel_path.c_str(), | ||||
|                     read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       return nullptr; | ||||
|     } | ||||
|  | ||||
|     return std::make_unique<smb_file>( | ||||
|         fd, smb_create_smb_path(path_, std::string{rel_path}), session_, | ||||
|         share_name_, tid_); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
| auto smb_directory::exists() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_t st{smb_fstat(session_.get(), tid_, | ||||
|                             smb_create_relative_path(path_).c_str())}; | ||||
|     if (not st) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::get_directory(std::string_view path) const | ||||
|     -> fs_directory_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_and_validate_relative_path(path_, path); | ||||
|     smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())}; | ||||
|     if (not st) { | ||||
|       throw std::runtime_error("failed to stat directory|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U}; | ||||
|     if (not is_dir) { | ||||
|       throw std::runtime_error("path is not a directory|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     return smb_directory_t{ | ||||
|         new smb_directory{ | ||||
|             smb_create_smb_path(path_, rel_path), | ||||
|             session_, | ||||
|             share_name_, | ||||
|             tid_, | ||||
|         }, | ||||
|     }; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto smb_directory::get_directories() const -> std::vector<fs_directory_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_list_t list{ | ||||
|         smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; | ||||
|     if (not list) { | ||||
|       throw std::runtime_error("failed to get directory list|" + path_); | ||||
|     } | ||||
|  | ||||
|     std::vector<fs_directory_t> ret{}; | ||||
|  | ||||
|     auto count = smb_stat_list_count(list.get()); | ||||
|     for (std::size_t idx = 0U; idx < count; ++idx) { | ||||
|       auto st = smb_stat_list_at(list.get(), idx); | ||||
|  | ||||
|       bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; | ||||
|       if (not is_dir) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       std::string name{smb_stat_name(st)}; | ||||
|       if (name == "." || name == "..") { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       ret.emplace_back(smb_directory_t{ | ||||
|           new smb_directory{ | ||||
|               smb_create_smb_path(path_, name), | ||||
|               session_, | ||||
|               share_name_, | ||||
|               tid_, | ||||
|           }, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto smb_directory::get_file(std::string_view path) const -> fs_file_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_and_validate_relative_path(path_, path); | ||||
|     smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())}; | ||||
|     if (not st) { | ||||
|       throw std::runtime_error("failed to stat file|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     bool is_dir{smb_stat_get(st.get(), SMB_STAT_ISDIR) != 0U}; | ||||
|     if (is_dir) { | ||||
|       throw std::runtime_error("path is not a file|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     return std::make_unique<smb_file>( | ||||
|         std::nullopt, smb_create_smb_path(path_, std::string{rel_path}), | ||||
|         session_, share_name_, tid_); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| auto smb_directory::get_files() const -> std::vector<fs_file_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_list_t list{ | ||||
|         smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; | ||||
|     if (not list) { | ||||
|       throw std::runtime_error("failed to get file list|" + path_); | ||||
|     } | ||||
|  | ||||
|     std::vector<fs_file_t> ret{}; | ||||
|  | ||||
|     auto count = smb_stat_list_count(list.get()); | ||||
|     for (std::size_t idx = 0U; idx < count; ++idx) { | ||||
|       auto st = smb_stat_list_at(list.get(), idx); | ||||
|       bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; | ||||
|       if (is_dir) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       std::string name{smb_stat_name(st)}; | ||||
|       ret.emplace_back(std::make_unique<smb_file>( | ||||
|           std::nullopt, smb_create_smb_path(path_, name), session_, share_name_, | ||||
|           tid_)); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto smb_directory::get_items() const -> std::vector<fs_item_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_list_t list{ | ||||
|         smb_find(session_.get(), tid_, smb_create_search_path(path_).c_str())}; | ||||
|     if (not list) { | ||||
|       throw std::runtime_error("failed to get item list|" + path_); | ||||
|     } | ||||
|     std::vector<fs_item_t> ret{}; | ||||
|  | ||||
|     auto count = smb_stat_list_count(list.get()); | ||||
|     for (std::size_t idx = 0U; idx < count; ++idx) { | ||||
|       auto st = smb_stat_list_at(list.get(), idx); | ||||
|  | ||||
|       bool is_dir{smb_stat_get(st, SMB_STAT_ISDIR) != 0U}; | ||||
|       std::string name{smb_stat_name(st)}; | ||||
|  | ||||
|       if (is_dir) { | ||||
|         if (name == "." || name == "..") { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         ret.emplace_back(smb_directory_t{ | ||||
|             new smb_directory{ | ||||
|                 path_ + '/' + name, | ||||
|                 session_, | ||||
|                 share_name_, | ||||
|                 tid_, | ||||
|             }, | ||||
|         }); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       ret.emplace_back(std::make_unique<smb_file>( | ||||
|           std::nullopt, smb_create_smb_path(path_, name), session_, share_name_, | ||||
|           tid_)); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| auto smb_directory::is_symlink() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::move_to(std::string_view new_path) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     throw std::runtime_error("failed to move directory|" + path_ + '|' + | ||||
|                              std::string{new_path} + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::remove() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     if (not exists()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     return utils::retry_action([this]() -> bool { | ||||
|       try { | ||||
|         auto res = smb_directory_rm(session_.get(), tid_, | ||||
|                                     smb_create_relative_path(path_).c_str()); | ||||
|         if (res != DSM_SUCCESS) { | ||||
|           throw std::runtime_error("failed to remove directory|" + path_ + '|' + | ||||
|                                    std::to_string(res)); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|       } catch (const std::exception &e) { | ||||
|         utils::error::handle_exception(function_name, e); | ||||
|       } catch (...) { | ||||
|         utils::error::handle_exception(function_name); | ||||
|       } | ||||
|  | ||||
|       return false; | ||||
|     }); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::remove_recursively() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     if (not exists()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     throw std::runtime_error("failed to remove directory recursively|" + path_ + | ||||
|                              "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_directory::size(bool /* recursive */) const -> std::uint64_t { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     throw std::runtime_error("failed to get directory size|" + path_ + | ||||
|                              "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
							
								
								
									
										460
									
								
								support/src/utils/file_smb_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								support/src/utils/file_smb_file.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,460 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/string.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| void smb_file::close() { | ||||
|   if (fd_.has_value()) { | ||||
|     smb_fclose(session_.get(), *fd_); | ||||
|     fd_.reset(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto smb_file::copy_to(std::string_view new_path, | ||||
|                        bool overwrite) const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     // auto to_path = utils::path::absolute(new_path); | ||||
|  | ||||
|     throw std::runtime_error("failed to copy file|" + path_ + '|' + | ||||
|                              std::string{new_path} + '|' + | ||||
|                              std::to_string(overwrite) + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::exists() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     smb_stat_t st{smb_fstat(session_.get(), tid_, | ||||
|                             smb_create_relative_path(path_).c_str())}; | ||||
|     if (not st) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     return smb_stat_get(st.get(), SMB_STAT_ISDIR) == 0U; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| void smb_file::flush() const { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     throw std::runtime_error("failed to flush file|" + path_ + | ||||
|                              "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto smb_file::get_time(smb_session *session, smb_tid tid, std::string path, | ||||
|                         time_type type) -> std::optional<std::uint64_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (session == nullptr) { | ||||
|       throw std::runtime_error("session not found|" + path); | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_relative_path(path); | ||||
|     smb_stat_t st{smb_fstat(session, tid, rel_path.c_str())}; | ||||
|     if (not st) { | ||||
|       throw std::runtime_error("failed to stat directory|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     switch (type) { | ||||
|     case time_type::accessed: | ||||
|       return smb_stat_get(st.get(), SMB_STAT_ATIME); | ||||
|  | ||||
|     case time_type::created: | ||||
|       return smb_stat_get(st.get(), SMB_STAT_CTIME); | ||||
|  | ||||
|     case time_type::modified: | ||||
|       return smb_stat_get(st.get(), SMB_STAT_MTIME); | ||||
|  | ||||
|     case time_type::written: | ||||
|       return smb_stat_get(st.get(), SMB_STAT_WTIME); | ||||
|     } | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto smb_file::is_symlink() const -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::move_to(std::string_view new_path) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (utils::string::begins_with(new_path, "//")) { | ||||
|       throw std::runtime_error("failed to move file|" + path_ + '|' + | ||||
|                                std::string{new_path} + | ||||
|                                "|new path must be in same share"); | ||||
|     } | ||||
|  | ||||
|     auto from_path = smb_create_relative_path(path_); | ||||
|     auto to_path = smb_create_and_validate_relative_path( | ||||
|         utils::string::begins_with(new_path, "/") ? smb_get_root_path(path_) | ||||
|                                                   : smb_get_parent_path(path_), | ||||
|         new_path); | ||||
|  | ||||
|     auto was_open{false}; | ||||
|     if (fd_.has_value()) { | ||||
|       close(); | ||||
|       was_open = true; | ||||
|     } | ||||
|  | ||||
|     auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to connect to share|" + share_name_ + | ||||
|                                '|' + std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     res = smb_file_mv(session_.get(), tid_, from_path.c_str(), to_path.c_str()); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to move file|" + path_ + '|' + | ||||
|                                from_path + '|' + to_path + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     path_ = smb_create_smb_path(path_, to_path); | ||||
|     if (was_open) { | ||||
|       return open(read_only_); | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::open(bool read_only) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (fd_.has_value()) { | ||||
|       if (read_only == read_only_) { | ||||
|         return true; | ||||
|       } | ||||
|  | ||||
|       close(); | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_relative_path(path_); | ||||
|  | ||||
|     auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to connect to share|" + share_name_ + | ||||
|                                '|' + std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     smb_fd fd{}; | ||||
|     res = smb_fopen(session_.get(), tid_, rel_path.c_str(), | ||||
|                     read_only ? SMB_MOD_RO : SMB_MOD_RW2, &fd); | ||||
|     if (res != DSM_SUCCESS) { | ||||
|       throw std::runtime_error("failed to open file|" + path_ + '|' + rel_path + | ||||
|                                '|' + utils::string::from_bool(read_only) + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     fd_ = fd; | ||||
|     read_only_ = read_only; | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::read(unsigned char *data, std::size_t to_read, | ||||
|                     std::uint64_t offset, std::size_t *total_read) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (total_read != nullptr) { | ||||
|       (*total_read) = 0U; | ||||
|     } | ||||
|  | ||||
|     if (not fd_.has_value()) { | ||||
|       throw std::runtime_error("failed to read file|" + path_ + | ||||
|                                "|file not open"); | ||||
|     } | ||||
|  | ||||
|     auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset), | ||||
|                          SMB_SEEK_SET); | ||||
|     if (res == -1) { | ||||
|       throw std::runtime_error("failed to seek file|" + path_ + '|' + | ||||
|                                std::to_string(offset) + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     std::size_t bytes_read{0U}; | ||||
|     while (bytes_read != to_read) { | ||||
|       res = smb_fread(session_.get(), *fd_, &data[bytes_read], | ||||
|                       to_read - bytes_read); | ||||
|       if (res == -1) { | ||||
|         throw std::runtime_error("failed to read file|" + path_ + '|' + | ||||
|                                  std::to_string(to_read) + '|' + | ||||
|                                  std::to_string(res)); | ||||
|       } | ||||
|  | ||||
|       if (res == 0) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       bytes_read += static_cast<std::size_t>(res); | ||||
|     } | ||||
|  | ||||
|     if (total_read != nullptr) { | ||||
|       (*total_read) = bytes_read; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::remove() -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not exists()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     close(); | ||||
|  | ||||
|     return utils::retry_action([this]() -> bool { | ||||
|       try { | ||||
|         auto res = smb_tree_connect(session_.get(), share_name_.c_str(), &tid_); | ||||
|         if (res != DSM_SUCCESS) { | ||||
|           throw std::runtime_error("failed to connect to share|" + share_name_ + | ||||
|                                    '|' + std::to_string(res)); | ||||
|         } | ||||
|  | ||||
|         auto rel_path = smb_create_relative_path(path_); | ||||
|         res = smb_file_rm(session_.get(), tid_, rel_path.c_str()); | ||||
|         if (res != DSM_SUCCESS) { | ||||
|           throw std::runtime_error( | ||||
|               "failed to remove file|" + path_ + '|' + rel_path + '|' + | ||||
|               std::to_string(res) + '|' + | ||||
|               std::to_string(smb_session_get_nt_status(session_.get()))); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|       } catch (const std::exception &e) { | ||||
|         utils::error::handle_exception(function_name, e); | ||||
|       } catch (...) { | ||||
|         utils::error::handle_exception(function_name); | ||||
|       } | ||||
|  | ||||
|       return false; | ||||
|     }); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::size() const -> std::optional<std::uint64_t> { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (not session_) { | ||||
|       throw std::runtime_error("session not found|" + path_); | ||||
|     } | ||||
|  | ||||
|     auto rel_path = smb_create_relative_path(path_); | ||||
|     smb_stat_t st{smb_fstat(session_.get(), tid_, rel_path.c_str())}; | ||||
|     if (not st) { | ||||
|       throw std::runtime_error("failed to stat directory|" + rel_path); | ||||
|     } | ||||
|  | ||||
|     return smb_stat_get(st.get(), SMB_STAT_SIZE); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return std::nullopt; | ||||
| } | ||||
|  | ||||
| auto smb_file::truncate(std::size_t size) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     throw std::runtime_error("failed to truncate file|" + path_ + '|' + | ||||
|                              std::to_string(size) + "|not implemented"); | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| auto smb_file::write(const unsigned char *data, std::size_t to_write, | ||||
|                      std::size_t offset, std::size_t *total_written) -> bool { | ||||
|   static constexpr const std::string_view function_name{ | ||||
|       static_cast<const char *>(__FUNCTION__), | ||||
|   }; | ||||
|  | ||||
|   try { | ||||
|     if (total_written != nullptr) { | ||||
|       (*total_written) = 0U; | ||||
|     } | ||||
|  | ||||
|     if (not fd_.has_value()) { | ||||
|       throw std::runtime_error("failed to write file|" + path_ + | ||||
|                                "|file not open"); | ||||
|     } | ||||
|  | ||||
|     auto res = smb_fseek(session_.get(), *fd_, static_cast<off_t>(offset), | ||||
|                          SMB_SEEK_SET); | ||||
|     if (res == -1) { | ||||
|       throw std::runtime_error("failed to seek file|" + path_ + '|' + | ||||
|                                std::to_string(offset) + '|' + | ||||
|                                std::to_string(res)); | ||||
|     } | ||||
|  | ||||
|     std::size_t bytes_written{0U}; | ||||
|     while (bytes_written != to_write) { | ||||
|       res = smb_fwrite(session_.get(), *fd_, | ||||
|                        const_cast<unsigned char *>(&data[bytes_written]), | ||||
|                        to_write - bytes_written); | ||||
|       if (res == -1) { | ||||
|         throw std::runtime_error("failed to write file|" + path_ + '|' + | ||||
|                                  std::to_string(to_write) + '|' + | ||||
|                                  std::to_string(res)); | ||||
|       } | ||||
|  | ||||
|       if (res == 0) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       bytes_written += static_cast<std::size_t>(res); | ||||
|     } | ||||
|  | ||||
|     if (total_written != nullptr) { | ||||
|       (*total_written) = bytes_written; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|   } catch (const std::exception &e) { | ||||
|     utils::error::handle_exception(function_name, e); | ||||
|   } catch (...) { | ||||
|     utils::error::handle_exception(function_name); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
| } // namespace repertory::utils::file | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
							
								
								
									
										59
									
								
								support/src/utils/file_thread_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								support/src/utils/file_thread_file.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "utils/file.hpp" | ||||
|  | ||||
| namespace repertory::utils::file { | ||||
| // auto thread_file::attach_file(native_handle handle, | ||||
| //                               bool read_only) -> fs_file_t {} | ||||
|  | ||||
| auto thread_file::attach_file(fs_file_t file) -> fs_file_t {} | ||||
|  | ||||
| auto thread_file::open_file(std::string_view path, | ||||
|                             bool read_only) -> fs_file_t {} | ||||
|  | ||||
| auto thread_file::open_or_create_file(std::string_view path, | ||||
|                                       bool read_only) -> fs_file_t {} | ||||
|  | ||||
| thread_file::thread_file(fs_file_t file) : file_(std::move(file)) {} | ||||
|  | ||||
| void thread_file::close() {} | ||||
|  | ||||
| auto thread_file::copy_to(std::string_view new_path, | ||||
|                           bool overwrite) const -> bool {} | ||||
|  | ||||
| void thread_file::flush() const {} | ||||
|  | ||||
| auto thread_file::move_to(std::string_view path) -> bool {} | ||||
|  | ||||
| auto thread_file::read(unsigned char *data, std::size_t to_read, | ||||
|                        std::uint64_t offset, std::size_t *total_read) -> bool {} | ||||
|  | ||||
| auto thread_file::remove() -> bool {} | ||||
|  | ||||
| auto thread_file::truncate(std::size_t size) -> bool {} | ||||
|  | ||||
| auto thread_file::write(const unsigned char *data, std::size_t to_write, | ||||
|                         std::size_t offset, | ||||
|                         std::size_t *total_written) -> bool {} | ||||
|  | ||||
| auto thread_file::size() const -> std::optional<std::uint64_t> {} | ||||
| } // namespace repertory::utils::file | ||||
| @@ -23,47 +23,140 @@ | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| namespace {} // namespace | ||||
|  | ||||
| namespace repertory::utils::encryption { | ||||
| auto create_hash_blake2b_256(std::string_view data) -> hash_256_t { | ||||
|   return create_hash_blake2b_t<char, hash_256_t>(data); | ||||
|   return create_hash_blake2b_t<hash_256_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), data.size()); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_256(std::wstring_view data) -> hash_256_t { | ||||
|   return create_hash_blake2b_t<wchar_t, hash_256_t>(data); | ||||
|   return create_hash_blake2b_t<hash_256_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(wchar_t)); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_256(const data_buffer &data) -> hash_256_t { | ||||
|   return create_hash_blake2b_t<hash_256_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_384(std::string_view data) -> hash_384_t { | ||||
|   return create_hash_blake2b_t<char, hash_384_t>(data); | ||||
|   return create_hash_blake2b_t<hash_384_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), data.size()); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_384(std::wstring_view data) -> hash_384_t { | ||||
|   return create_hash_blake2b_t<wchar_t, hash_384_t>(data); | ||||
|   return create_hash_blake2b_t<hash_384_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(wchar_t)); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_384(const data_buffer &data) -> hash_384_t { | ||||
|   return create_hash_blake2b_t<hash_384_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_512(std::string_view data) -> hash_512_t { | ||||
|   return create_hash_blake2b_t<char, hash_512_t>(data); | ||||
|   return create_hash_blake2b_t<hash_512_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), data.size()); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_512(std::wstring_view data) -> hash_512_t { | ||||
|   return create_hash_blake2b_t<wchar_t, hash_512_t>(data); | ||||
|   return create_hash_blake2b_t<hash_512_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(wchar_t)); | ||||
| } | ||||
|  | ||||
| auto create_hash_blake2b_512(const data_buffer &data) -> hash_512_t { | ||||
|   return create_hash_blake2b_t<hash_512_t>( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha256(std::string_view data) -> hash_256_t { | ||||
|   return create_hash_sha256_t<char>(data); | ||||
|   return create_hash_sha256( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), data.size()); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha256(std::wstring_view data) -> hash_256_t { | ||||
|   return create_hash_sha256_t<wchar_t>(data); | ||||
|   return create_hash_sha256( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(wchar_t)); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha256(const data_buffer &data) -> hash_256_t { | ||||
|   return create_hash_sha256( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha512(std::string_view data) -> hash_512_t { | ||||
|   return create_hash_sha512_t<char>(data); | ||||
|   return create_hash_sha512( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), data.size()); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha512(std::wstring_view data) -> hash_512_t { | ||||
|   return create_hash_sha512_t<wchar_t>(data); | ||||
|   return create_hash_sha512( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(wchar_t)); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha512(const data_buffer &data) -> hash_512_t { | ||||
|   return create_hash_sha512( | ||||
|       reinterpret_cast<const unsigned char *>(data.data()), | ||||
|       data.size() * sizeof(data_buffer::value_type)); | ||||
| } | ||||
|  | ||||
| auto create_hash_sha512(const unsigned char *data, | ||||
|                         std::size_t data_size) -> hash_512_t { | ||||
|   hash_512_t hash{}; | ||||
|  | ||||
|   crypto_hash_sha512_state state{}; | ||||
|   auto res = crypto_hash_sha512_init(&state); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize sha-512|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha512_update(&state, data, data_size); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update sha-512|" + std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha512_final(&state, hash.data()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize sha-512|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| } | ||||
|  | ||||
| auto create_hash_sha256(const unsigned char *data, | ||||
|                         std::size_t data_size) -> hash_256_t { | ||||
|   hash_256_t hash{}; | ||||
|  | ||||
|   crypto_hash_sha256_state state{}; | ||||
|   auto res = crypto_hash_sha256_init(&state); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to initialize sha-256|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha256_update(&state, data, data_size); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to update sha-256|" + std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   res = crypto_hash_sha256_final(&state, hash.data()); | ||||
|   if (res != 0) { | ||||
|     throw std::runtime_error("failed to finalize sha-256|" + | ||||
|                              std::to_string(res)); | ||||
|   } | ||||
|  | ||||
|   return hash; | ||||
| } | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
|   | ||||
| @@ -23,18 +23,11 @@ | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/file.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #include "utils/unix.hpp" | ||||
|  | ||||
| namespace { | ||||
| static const std::string directory_seperator_str{ | ||||
|     repertory::utils::path::directory_seperator, | ||||
| }; | ||||
|  | ||||
| static const std::wstring directory_seperator_str_w{ | ||||
|     repertory::utils::path::directory_seperator_w, | ||||
| }; | ||||
|  | ||||
| [[nodiscard]] auto resolve(std::string path) -> std::string { | ||||
| #if defined(_WIN32) | ||||
|   if (repertory::utils::string::contains(path, "~") || | ||||
| @@ -52,12 +45,17 @@ static const std::wstring directory_seperator_str_w{ | ||||
| #else  // !defined (_WIN32) | ||||
|   if (repertory::utils::string::contains(path, "~")) { | ||||
|     std::string home{}; | ||||
|     repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) { | ||||
|       home = (pw->pw_dir ? pw->pw_dir : ""); | ||||
|       if (home.empty() || ((home == "/") && (getuid() != 0))) { | ||||
|         home = repertory::utils::path::combine("/home", {pw->pw_name}); | ||||
|       } | ||||
|     }); | ||||
|     auto res = | ||||
|         repertory::utils::use_getpwuid(getuid(), [&home](struct passwd *pw) { | ||||
|           home = (pw->pw_dir ? pw->pw_dir : ""); | ||||
|           if (home.empty() || | ||||
|               ((home == repertory::utils::path::slash) && (getuid() != 0))) { | ||||
|             home = repertory::utils::path::combine("/home", {pw->pw_name}); | ||||
|           } | ||||
|         }); | ||||
|     if (res) { | ||||
|       throw std::runtime_error("failed to getpwuid: " + res.reason); | ||||
|     } | ||||
|  | ||||
|     return repertory::utils::string::replace(path, "~", home); | ||||
|   } | ||||
| @@ -70,42 +68,59 @@ static const std::wstring directory_seperator_str_w{ | ||||
| namespace repertory::utils::path { | ||||
| auto absolute(std::string_view path) -> std::string { | ||||
|   std::string abs_path{path}; | ||||
|   abs_path = resolve(abs_path); | ||||
|   format_path(abs_path, directory_seperator, not_directory_seperator); | ||||
|   if (abs_path.empty()) { | ||||
|     return abs_path; | ||||
|   } | ||||
|  | ||||
|   abs_path = finalize(resolve(abs_path)); | ||||
| #if defined(_WIN32) | ||||
|   if (not abs_path.empty() && ::PathIsRelativeA(abs_path.c_str())) { | ||||
|     std::string temp; | ||||
|     temp.resize(MAX_PATH + 1); | ||||
|     abs_path = _fullpath(temp.data(), abs_path.c_str(), MAX_PATH); | ||||
|   if (not utils::string::contains(abs_path, dot)) { | ||||
|     return abs_path; | ||||
|   } | ||||
|  | ||||
|   std::string temp; | ||||
|   temp.resize(repertory::max_path_length + 1); | ||||
|   ::GetFullPathNameA(abs_path.c_str(), static_cast<DWORD>(temp.size()), | ||||
|                      temp.data(), nullptr); | ||||
| #else  // !defined(_WIN32) | ||||
|   if (not abs_path.empty() && (abs_path.at(0U) != '/')) { | ||||
|     auto found{false}; | ||||
|     std::string tmp{abs_path}; | ||||
|     do { | ||||
|       auto *res = realpath(tmp.c_str(), nullptr); | ||||
|       if (res != nullptr) { | ||||
|         abs_path = res + std::string{directory_seperator} + | ||||
|                    abs_path.substr(tmp.size()); | ||||
|         free(res); | ||||
|         found = true; | ||||
|       } else if (tmp == ".") { | ||||
|         found = true; | ||||
|       } else { | ||||
|         tmp = dirname(tmp.data()); | ||||
|       } | ||||
|     } while (not found); | ||||
|   if (not utils::string::contains(abs_path, dot) || | ||||
|       utils::string::begins_with(abs_path, slash)) { | ||||
|     return abs_path; | ||||
|   } | ||||
|  | ||||
|   auto found{false}; | ||||
|   std::string tmp{abs_path}; | ||||
|   do { | ||||
|     auto *res = realpath(tmp.c_str(), nullptr); | ||||
|     if (res != nullptr) { | ||||
|       abs_path = | ||||
|           res + std::string{directory_seperator} + abs_path.substr(tmp.size()); | ||||
|       free(res); | ||||
|       found = true; | ||||
|     } else if (tmp == dot) { | ||||
|       found = true; | ||||
|     } else { | ||||
|       tmp = dirname(tmp.data()); | ||||
|     } | ||||
|   } while (not found); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return format_path(abs_path, directory_seperator, not_directory_seperator); | ||||
|   return finalize(abs_path); | ||||
| } | ||||
|  | ||||
| auto absolute(std::wstring_view path) -> std::wstring { | ||||
|   return utils::string::from_utf8(absolute(utils::string::to_utf8(path))); | ||||
| } | ||||
|  | ||||
| auto exists(std::string_view path) -> bool { | ||||
|   return utils::file::file{path}.exists() || | ||||
|          utils::file::directory{path}.exists(); | ||||
| } | ||||
|  | ||||
| auto exists(std::wstring_view path) -> bool { | ||||
|   return exists(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto find_program_in_path(const std::string &name_without_extension) | ||||
|     -> std::string { | ||||
|   static std::mutex mtx{}; | ||||
| @@ -137,12 +152,12 @@ auto find_program_in_path(const std::string &name_without_extension) | ||||
|   static constexpr const auto split_char = ':'; | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   const auto search_path_list = utils::string::split(path, split_char, false); | ||||
|   auto search_path_list = utils::string::split(path, split_char, false); | ||||
|   for (auto &&search_path : search_path_list) { | ||||
|     for (auto &&extension : extension_list) { | ||||
|       auto exec_path = combine( | ||||
|           search_path, {name_without_extension + std::string{extension}}); | ||||
|       if (std::filesystem::exists(exec_path)) { | ||||
|       if (utils::file::file(exec_path).exists()) { | ||||
|         found_items[name_without_extension] = exec_path; | ||||
|         return exec_path; | ||||
|       } | ||||
| @@ -158,41 +173,66 @@ find_program_in_path(std::wstring_view name_without_extension) -> std::wstring { | ||||
|       find_program_in_path(utils::string::to_utf8(name_without_extension))); | ||||
| } | ||||
|  | ||||
| auto get_parent_directory(std::string_view path) -> std::string { | ||||
|   auto ret = std::filesystem::path{path}.parent_path().string(); | ||||
| #if !defined(_WIN32) | ||||
|   if (ret == ".") { | ||||
|     ret = "/"; | ||||
|   } | ||||
| #endif // !defined(_WIN32) | ||||
| auto get_parent_path(std::string_view path) -> std::string { | ||||
|   auto abs_path = absolute(path); | ||||
|  | ||||
|   return absolute(ret); | ||||
| #if defined(_WIN32) | ||||
|   ::PathRemoveFileSpecA(abs_path.data()); | ||||
|   abs_path = abs_path.c_str(); | ||||
| #else  // !defined(_WIN32) | ||||
|   abs_path = std::filesystem::path{abs_path}.parent_path().string(); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return finalize(abs_path); | ||||
| } | ||||
|  | ||||
| auto get_parent_directory(std::wstring_view path) -> std::wstring { | ||||
| auto get_parent_path(std::wstring_view path) -> std::wstring { | ||||
|   return utils::string::from_utf8( | ||||
|       get_parent_directory(utils::string::to_utf8(path))); | ||||
|       get_parent_path(utils::string::to_utf8(path))); | ||||
| } | ||||
|  | ||||
| auto is_trash_directory(std::string_view path) -> bool { | ||||
|   auto trash_path = utils::string::to_lower(absolute(path)); | ||||
|   return utils::string::begins_with(trash_path, | ||||
|                                     directory_seperator_str + ".trash-") || | ||||
|          utils::string::begins_with(trash_path, | ||||
|                                     directory_seperator_str + ".trashes") || | ||||
|          utils::string::begins_with(trash_path, | ||||
|                                     directory_seperator_str + "$recycle.bin"); | ||||
| auto get_relative_path(std::string_view path, | ||||
|                        std::string_view root_path) -> std::string { | ||||
|   auto abs_path = absolute(path); | ||||
|   auto abs_root_path = | ||||
|       absolute(root_path) + std::string{get_directory_seperator<char>()}; | ||||
| #if defined(_WIN32) | ||||
|   if (utils::string::to_lower(abs_path).starts_with( | ||||
|           utils::string::to_lower(abs_root_path))) { | ||||
| #else  // !defined(_WIN32) | ||||
|   if (abs_path.starts_with(abs_root_path)) { | ||||
| #endif // defined(_WIN32) | ||||
|     return abs_path.substr(abs_root_path.size()); | ||||
|   } | ||||
|  | ||||
|   return abs_path; | ||||
| } | ||||
|  | ||||
| auto is_trash_directory(std::wstring_view path) -> bool { | ||||
|   return is_trash_directory(utils::string::to_utf8(path)); | ||||
| auto get_relative_path(std::wstring_view path, | ||||
|                        std::wstring_view root_path) -> std::wstring { | ||||
|   return utils::string::from_utf8(get_relative_path( | ||||
|       utils::string::to_utf8(path), utils::string::to_utf8(root_path))); | ||||
| } | ||||
|  | ||||
| auto contains_trash_directory(std::string_view path) -> bool { | ||||
|   auto parts = utils::string::split(utils::string::to_lower(absolute(path)), | ||||
|                                     get_directory_seperator<char>(), false); | ||||
|  | ||||
|   return std::find_if(parts.begin(), parts.end(), [](auto &&part) -> bool { | ||||
|            return utils::string::begins_with(part, ".trash-") || | ||||
|                   part == ".trashes" || part == "$recycle.bin"; | ||||
|          }) != parts.end(); | ||||
| } | ||||
|  | ||||
| auto contains_trash_directory(std::wstring_view path) -> bool { | ||||
|   return contains_trash_directory(utils::string::to_utf8(path)); | ||||
| } | ||||
|  | ||||
| auto make_file_uri(std::string_view path) -> std::string { | ||||
|   auto abs_path = absolute(path); | ||||
| #if defined(_WIN32) | ||||
|   utils::string::replace(abs_path, '\\', '/'); | ||||
|   abs_path = '/' + abs_path; | ||||
|   utils::string::replace(abs_path, backslash, slash); | ||||
|   abs_path = std::string{slash} + abs_path; | ||||
| #endif // defined(_WIN32) | ||||
|   return "file://" + abs_path; | ||||
| } | ||||
| @@ -201,31 +241,11 @@ auto make_file_uri(std::wstring_view path) -> std::wstring { | ||||
|   return utils::string::from_utf8(make_file_uri(utils::string::to_utf8(path))); | ||||
| } | ||||
|  | ||||
| auto remove_file_name(std::string_view path) -> std::string { | ||||
|   auto abs_path = absolute(path); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   ::PathRemoveFileSpecA(abs_path.data()); | ||||
|   abs_path = abs_path.c_str(); | ||||
| #else  // !defined(_WIN32) | ||||
|   if (abs_path != "/") { | ||||
|     auto idx{abs_path.size() - 1U}; | ||||
|     while ((idx != 0U) && (abs_path.at(idx) != '/')) { | ||||
|       idx--; | ||||
|     } | ||||
|  | ||||
|     abs_path = (idx > 0U) ? absolute(abs_path.substr(0U, idx)) : "/"; | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   return abs_path; | ||||
| } | ||||
|  | ||||
| auto strip_to_file_name(std::string path) -> std::string { | ||||
| #if defined(_WIN32) | ||||
|   return ::PathFindFileNameA(path.c_str()); | ||||
| #else  // !defined(_WIN32) | ||||
|   return utils::string::contains(path, "/") ? basename(path.data()) : path; | ||||
|   return utils::string::contains(path, slash) ? basename(path.data()) : path; | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,32 +22,6 @@ | ||||
| #include "utils/time.hpp" | ||||
|  | ||||
| namespace repertory::utils::time { | ||||
| #if defined(_WIN32) | ||||
| // https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ | ||||
| auto filetime_to_unix_time(const FILETIME &file_time) -> std::uint64_t { | ||||
|   LARGE_INTEGER date{}; | ||||
|   date.HighPart = static_cast<LONG>(file_time.dwHighDateTime); | ||||
|   date.LowPart = file_time.dwLowDateTime; | ||||
|   date.QuadPart -= 116444736000000000LL; | ||||
|  | ||||
|   return static_cast<std::uint64_t>(date.QuadPart) * 100ULL; | ||||
| } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| auto get_file_time_now() -> std::uint64_t { | ||||
| #if defined(_WIN32) | ||||
|   SYSTEMTIME sys_time{}; | ||||
|   ::GetSystemTime(&sys_time); | ||||
|  | ||||
|   FILETIME file_time{}; | ||||
|   ::SystemTimeToFileTime(&sys_time, &file_time); | ||||
|   return static_cast<std::uint64_t>( | ||||
|       (reinterpret_cast<LARGE_INTEGER *>(&file_time))->QuadPart); | ||||
| #else  // !defined(_WIN32) | ||||
|   return get_time_now(); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| void get_local_time_now(struct tm &local_time) { | ||||
|   std::memset(&local_time, 0, sizeof(local_time)); | ||||
|  | ||||
| @@ -61,21 +35,10 @@ void get_local_time_now(struct tm &local_time) { | ||||
| } | ||||
|  | ||||
| auto get_time_now() -> std::uint64_t { | ||||
| #if defined(_WIN32) | ||||
|   return static_cast<std::uint64_t>( | ||||
|       std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| #if defined(__APPLE__) | ||||
|   return std::chrono::nanoseconds( | ||||
|              std::chrono::system_clock::now().time_since_epoch()) | ||||
|       .count(); | ||||
| #else  // !defined(__APPLE__) | ||||
|   return static_cast<std::uint64_t>( | ||||
|       std::chrono::nanoseconds( | ||||
|           std::chrono::high_resolution_clock::now().time_since_epoch()) | ||||
|       std::chrono::duration_cast<std::chrono::nanoseconds>( | ||||
|           std::chrono::system_clock::now().time_since_epoch()) | ||||
|           .count()); | ||||
| #endif // defined(__APPLE__) | ||||
| } | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| @@ -91,17 +54,35 @@ auto strptime(const char *s, const char *f, struct tm *tm) -> const char * { | ||||
|   return reinterpret_cast<const char *>(s + input.tellg()); | ||||
| } | ||||
|  | ||||
| auto time64_to_unix_time(const __time64_t &time) -> std::uint64_t { | ||||
|   return static_cast<std::uint64_t>(time * NANOS_PER_SECOND); | ||||
| } | ||||
|  | ||||
| // https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ | ||||
| auto unix_time_to_filetime(std::uint64_t unix_time) -> FILETIME { | ||||
|   const auto win_time = (unix_time / 100ULL) + 116444736000000000ULL; | ||||
|   auto win_time = unix_time_to_windows_time(unix_time); | ||||
|  | ||||
|   FILETIME file_time{}; | ||||
|   file_time.dwHighDateTime = static_cast<DWORD>(win_time >> 32U); | ||||
|   file_time.dwLowDateTime = win_time & 0xFFFFFFFF; | ||||
|   return file_time; | ||||
| } | ||||
|  | ||||
| auto windows_file_time_to_unix_time(FILETIME win_time) -> std::uint64_t { | ||||
|   return windows_time_to_unix_time( | ||||
|       (static_cast<std::uint64_t>(win_time.dwHighDateTime) << 32ULL) | | ||||
|       static_cast<std::uint64_t>(win_time.dwLowDateTime)); | ||||
| } | ||||
|  | ||||
| auto windows_time_t_to_unix_time(__time64_t win_time) -> std::uint64_t { | ||||
|   return static_cast<std::uint64_t>( | ||||
|       std::chrono::duration_cast<std::chrono::nanoseconds>( | ||||
|           std::chrono::system_clock::from_time_t(win_time).time_since_epoch()) | ||||
|           .count()); | ||||
| } | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| auto unix_time_to_windows_time(std::uint64_t unix_time) -> std::uint64_t { | ||||
|   return (unix_time / WIN32_TIME_NANOS_PER_TICK) + WIN32_TIME_CONVERSION; | ||||
| } | ||||
|  | ||||
| auto windows_time_to_unix_time(std::uint64_t win_time) -> std::uint64_t { | ||||
|   return (win_time * WIN32_TIME_NANOS_PER_TICK) - WIN32_TIME_CONVERSION; | ||||
| } | ||||
| } // namespace repertory::utils::time | ||||
|   | ||||
| @@ -76,7 +76,7 @@ auto run_process_elevated(std::vector<const char *> args) -> int { | ||||
|   } | ||||
|  | ||||
|   std::string full_path; | ||||
|   full_path.resize(MAX_PATH + 1); | ||||
|   full_path.resize(repertory::max_path_length + 1); | ||||
|  | ||||
|   if (::GetModuleFileNameA(nullptr, full_path.data(), MAX_PATH)) { | ||||
|     SHELLEXECUTEINFOA sei{}; | ||||
|   | ||||
							
								
								
									
										61
									
								
								support/test/include/test.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								support/test/include/test.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #ifndef REPERTORY_TEST_INCLUDE_TEST_HPP_ | ||||
| #define REPERTORY_TEST_INCLUDE_TEST_HPP_ | ||||
|  | ||||
| #if defined(U) | ||||
| #undef U | ||||
| #endif // defined(U) | ||||
|  | ||||
| #include "gmock/gmock.h" | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| using ::testing::_; | ||||
| using namespace ::testing; | ||||
|  | ||||
| #define COMMA , | ||||
|  | ||||
| #include "utils/all.hpp" | ||||
|  | ||||
| namespace repertory::test { | ||||
| [[nodiscard]] auto | ||||
| create_random_file(std::size_t size) -> utils::file::i_file &; | ||||
|  | ||||
| [[nodiscard]] auto | ||||
| generate_test_file_name(std::string_view file_name_no_extension) -> std::string; | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| template <typename buffer_t, typename result_t> | ||||
| static void decrypt_and_verify(const buffer_t &buffer, std::string_view token, | ||||
|                                result_t &result) { | ||||
|   EXPECT_TRUE(utils::encryption::decrypt_data(token, buffer, result)); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| auto generate_test_directory() -> utils::file::i_directory &; | ||||
|  | ||||
| [[nodiscard]] auto get_test_input_dir() -> std::string; | ||||
|  | ||||
| [[nodiscard]] auto get_test_output_dir() -> std::string; | ||||
| } // namespace repertory::test | ||||
|  | ||||
| #endif // REPERTORY_TEST_INCLUDE_TEST_HPP_ | ||||
							
								
								
									
										142
									
								
								support/test/src/test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								support/test/src/test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace { | ||||
| static std::recursive_mutex file_mtx{}; | ||||
|  | ||||
| static std::vector<std::unique_ptr<repertory::utils::file::i_fs_item>> | ||||
|     generated_files{}; | ||||
|  | ||||
| static void delete_generated_files() { | ||||
|   repertory::recur_mutex_lock lock{file_mtx}; | ||||
|   std::optional<std::string> parent_path; | ||||
|   for (auto &&path : generated_files) { | ||||
|     if (parent_path->empty()) { | ||||
|       parent_path = repertory::utils::path::get_parent_path(path->get_path()); | ||||
|     } | ||||
|  | ||||
|     [[maybe_unused]] auto removed = path->remove(); | ||||
|   } | ||||
|   generated_files.clear(); | ||||
|  | ||||
|   if (parent_path.has_value()) { | ||||
|     EXPECT_TRUE( | ||||
|         repertory::utils::file::directory(*parent_path).remove_recursively()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| struct file_deleter final { | ||||
|   ~file_deleter() { delete_generated_files(); } | ||||
| }; | ||||
|  | ||||
| static auto deleter{std::make_unique<file_deleter>()}; | ||||
| } // namespace | ||||
|  | ||||
| namespace repertory::test { | ||||
| auto create_random_file(std::size_t size) -> utils::file::i_file & { | ||||
|   auto path = generate_test_file_name("random"); | ||||
|   auto file = utils::file::file::open_or_create_file(path); | ||||
|   EXPECT_TRUE(*file); | ||||
|   if (*file) { | ||||
|     data_buffer buf(size); | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|     randombytes_buf(buf.data(), buf.size()); | ||||
| #else  // !defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|     thread_local std::mt19937 gen(static_cast<unsigned long>( | ||||
|         std::time(nullptr) ^ std::random_device{}())); | ||||
|     std::uniform_int_distribution<std::uint8_t> dis(0U, 255U); | ||||
|     std::generate(buf.begin(), buf.end(), [&]() -> auto { return dis(gen); }); | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
|     std::size_t bytes_written{}; | ||||
|     EXPECT_TRUE(file->write(buf, 0U, &bytes_written)); | ||||
|     EXPECT_EQ(size, bytes_written); | ||||
|  | ||||
|     EXPECT_EQ(size, file->size()); | ||||
|   } | ||||
|  | ||||
|   recur_mutex_lock lock{file_mtx}; | ||||
|   generated_files.emplace_back(std::move(file)); | ||||
|   return *dynamic_cast<utils::file::i_file *>(generated_files.back().get()); | ||||
| } | ||||
|  | ||||
| auto generate_test_directory() -> utils::file::i_directory & { | ||||
|   auto path = utils::path::combine( | ||||
|       get_test_output_dir(), | ||||
|       { | ||||
|           std::string{"test_dir"} + std::to_string(generated_files.size()), | ||||
|       }); | ||||
|  | ||||
|   recur_mutex_lock lock{file_mtx}; | ||||
|   generated_files.emplace_back(std::unique_ptr<utils::file::i_fs_item>( | ||||
|       new utils::file::directory{path})); | ||||
|  | ||||
|   auto &ret = | ||||
|       *dynamic_cast<utils::file::i_directory *>(generated_files.back().get()); | ||||
|   EXPECT_TRUE(ret.create_directory()); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| auto generate_test_file_name(std::string_view file_name_no_extension) | ||||
|     -> std::string { | ||||
|   auto path = utils::path::combine( | ||||
|       get_test_output_dir(), { | ||||
|                                  std::string{file_name_no_extension} + | ||||
|                                      std::to_string(generated_files.size()), | ||||
|                              }); | ||||
|  | ||||
|   recur_mutex_lock lock{file_mtx}; | ||||
|   generated_files.emplace_back( | ||||
|       std::unique_ptr<utils::file::i_file>(new utils::file::file{path})); | ||||
|   return generated_files.back()->get_path(); | ||||
| } | ||||
|  | ||||
| auto get_test_input_dir() -> std::string { | ||||
|   static auto test_path = ([]() -> std::string { | ||||
|     auto dir = utils::get_environment_variable("PROJECT_TEST_DIR"); | ||||
|     return utils::path::combine(dir.empty() ? "." : dir, {"test_config"}); | ||||
|   })(); | ||||
|  | ||||
|   return test_path; | ||||
| } | ||||
|  | ||||
| auto get_test_output_dir() -> std::string { | ||||
|   static auto test_path = ([]() -> std::string { | ||||
|     auto temp = utils::file::create_temp_name("project_test"); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|     auto path = utils::path::combine("%TEMP%", {temp}); | ||||
| #else  // !defined(_WIN32) | ||||
|     auto path = utils::path::combine("/tmp", {temp}); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|     if (not utils::file::directory(path).exists()) { | ||||
|       EXPECT_TRUE(utils::file::directory{path}.create_directory()); | ||||
|     } | ||||
|  | ||||
|     return path; | ||||
|   })(); | ||||
|  | ||||
|   return test_path; | ||||
| } | ||||
| } // namespace repertory::test | ||||
							
								
								
									
										250
									
								
								support/test/src/utils/collection_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								support/test/src/utils/collection_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,250 @@ | ||||
| /* | ||||
|   Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|   of this software and associated documentation files (the "Software"), to deal | ||||
|   in the Software without restriction, including without limitation the rights | ||||
|   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|   copies of the Software, and to permit persons to whom the Software is | ||||
|   furnished to do so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|   copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_collection, excludes) { | ||||
|   auto data = {"cow", "moose", "dog", "chicken"}; | ||||
|   EXPECT_FALSE(utils::collection::excludes(data, "chicken")); | ||||
|   EXPECT_FALSE(utils::collection::excludes(data, "cow")); | ||||
|   EXPECT_FALSE(utils::collection::excludes(data, "dog")); | ||||
|   EXPECT_FALSE(utils::collection::excludes(data, "moose")); | ||||
|   EXPECT_TRUE(utils::collection::excludes(data, "mouse")); | ||||
| } | ||||
|  | ||||
| TEST(utils_collection, includes) { | ||||
|   auto data = {"cow", "moose", "dog", "chicken"}; | ||||
|   EXPECT_FALSE(utils::collection::includes(data, "mice")); | ||||
|   EXPECT_TRUE(utils::collection::includes(data, "chicken")); | ||||
|   EXPECT_TRUE(utils::collection::includes(data, "cow")); | ||||
|   EXPECT_TRUE(utils::collection::includes(data, "dog")); | ||||
|   EXPECT_TRUE(utils::collection::includes(data, "moose")); | ||||
| } | ||||
|  | ||||
| TEST(utils_collection, from_hex_string) { | ||||
|   { | ||||
|     auto data = "0xABCDEF10"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = " 0xABCDEF10 "; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = "ABCDEF10"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = "ACDEF"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(3U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = " ACDEF "; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(3U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = ""; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"0xABCDEF10"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L" 0xABCDEF10 "; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"ABCDEF10"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(4U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"ACDEF"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(3U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L" ACDEF "; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_EQ(3U, val.size()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L""; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_TRUE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_collection, from_hex_string_fails) { | ||||
|   { | ||||
|     auto data = "ABCDEF1Z"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = "ABC DEF1"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = "0x"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = " 0x "; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"ABCDEF1Z"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"ABC DEF1"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L"0x"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto data = L" 0x"; | ||||
|     std::vector<std::uint8_t> val{}; | ||||
|     EXPECT_FALSE(utils::collection::from_hex_string(data, val)); | ||||
|     EXPECT_TRUE(val.empty()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_collection, to_hex_string) { | ||||
|   { | ||||
|     std::array<std::int8_t, 2U> col{ | ||||
|         static_cast<std::int8_t>(0xFF), | ||||
|         static_cast<std::int8_t>(0xEE), | ||||
|     }; | ||||
|  | ||||
|     auto str = utils::collection::to_hex_string(col); | ||||
|     EXPECT_STREQ("ffee", str.c_str()); | ||||
|  | ||||
|     auto w_str = utils::collection::to_hex_wstring(col); | ||||
|     EXPECT_STREQ(L"ffee", w_str.c_str()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     std::array<std::uint8_t, 2U> col{ | ||||
|         static_cast<std::uint8_t>(0xFF), | ||||
|         static_cast<std::uint8_t>(0xEE), | ||||
|     }; | ||||
|  | ||||
|     auto str = utils::collection::to_hex_string(col); | ||||
|     EXPECT_STREQ("ffee", str.c_str()); | ||||
|  | ||||
|     auto w_str = utils::collection::to_hex_wstring(col); | ||||
|     EXPECT_STREQ(L"ffee", w_str.c_str()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_collection, remove_element) { | ||||
|   { | ||||
|     std::vector<std::uint8_t> col{ | ||||
|         static_cast<std::uint8_t>(0xFF), | ||||
|         static_cast<std::uint8_t>(0xEE), | ||||
|     }; | ||||
|  | ||||
|     utils::collection::remove_element(col, 0xFF); | ||||
|     EXPECT_EQ(1U, col.size()); | ||||
|     EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(0U)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     std::vector<std::uint8_t> col{ | ||||
|         static_cast<std::uint8_t>(0xFF), | ||||
|         static_cast<std::uint8_t>(0xEE), | ||||
|     }; | ||||
|  | ||||
|     utils::collection::remove_element(col, 0xEE); | ||||
|     EXPECT_EQ(1U, col.size()); | ||||
|     EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     std::vector<std::uint8_t> col{ | ||||
|         static_cast<std::uint8_t>(0xFF), | ||||
|         static_cast<std::uint8_t>(0xEE), | ||||
|     }; | ||||
|  | ||||
|     utils::collection::remove_element(col, 0xEF); | ||||
|     EXPECT_EQ(2U, col.size()); | ||||
|     EXPECT_EQ(static_cast<std::uint8_t>(0xFF), col.at(0U)); | ||||
|     EXPECT_EQ(static_cast<std::uint8_t>(0xEE), col.at(1U)); | ||||
|   } | ||||
| } | ||||
| } // namespace repertory | ||||
| @@ -19,12 +19,7 @@ | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
|  | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_common, calculate_read_size) { | ||||
| @@ -129,40 +124,40 @@ TEST(utils_common, create_uuid_string) { | ||||
| #endif // defined(PROJECT_ENABLE_STDUUID) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| TEST(utils_common, generate_random) { | ||||
| TEST(utils_common, generate_secure_random) { | ||||
|   { | ||||
|     auto r1 = utils::generate_random<std::size_t>(); | ||||
|     auto r2 = utils::generate_random<std::size_t>(); | ||||
|     auto r1 = utils::generate_secure_random<std::size_t>(); | ||||
|     auto r2 = utils::generate_secure_random<std::size_t>(); | ||||
|     EXPECT_NE(r1, r2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto r1 = utils::generate_random<std::vector<std::uint8_t>>(6U); | ||||
|     auto r2 = utils::generate_random<std::vector<std::uint8_t>>(6U); | ||||
|     auto r1 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U); | ||||
|     auto r2 = utils::generate_secure_random<std::vector<std::uint8_t>>(6U); | ||||
|     EXPECT_EQ(6U, r1.size()); | ||||
|     EXPECT_EQ(r1.size(), r2.size()); | ||||
|     EXPECT_NE(r1, r2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto r1 = utils::generate_random<std::array<std::uint8_t, 4U>>(); | ||||
|     auto r2 = utils::generate_random<std::array<std::uint8_t, 4U>>(); | ||||
|     auto r1 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>(); | ||||
|     auto r2 = utils::generate_secure_random<std::array<std::uint8_t, 4U>>(); | ||||
|     EXPECT_EQ(4U, r1.size()); | ||||
|     EXPECT_EQ(r1.size(), r2.size()); | ||||
|     EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size())); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto r1 = utils::generate_random<std::string>(6U); | ||||
|     auto r2 = utils::generate_random<std::string>(6U); | ||||
|     auto r1 = utils::generate_secure_random<std::string>(6U); | ||||
|     auto r2 = utils::generate_secure_random<std::string>(6U); | ||||
|     EXPECT_EQ(6U, r1.size()); | ||||
|     EXPECT_EQ(r1.size(), r2.size()); | ||||
|     EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size())); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto r1 = utils::generate_random<std::wstring>(6U); | ||||
|     auto r2 = utils::generate_random<std::wstring>(6U); | ||||
|     auto r1 = utils::generate_secure_random<std::wstring>(6U); | ||||
|     auto r2 = utils::generate_secure_random<std::wstring>(6U); | ||||
|     EXPECT_EQ(6U, r1.size()); | ||||
|     EXPECT_EQ(r1.size(), r2.size()); | ||||
|     EXPECT_NE(0, std::memcmp(r1.data(), r2.data(), r1.size())); | ||||
| @@ -243,7 +238,6 @@ TEST(utils_common, generate_random_between_throws_error_on_invalid_range) { | ||||
|       std::range_error); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| TEST(utils_common, generate_random_string) { | ||||
|   static constexpr const auto max_iterations{10000L}; | ||||
|  | ||||
| @@ -271,14 +265,13 @@ TEST(utils_common, generate_random_string_for_zero_length) { | ||||
|   EXPECT_TRUE(utils::generate_random_string(0U).empty()); | ||||
|   EXPECT_TRUE(utils::generate_random_wstring(0U).empty()); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| TEST(utils_common, get_environment_variable) { | ||||
|   static constexpr const std::string path_env{"PATH"}; | ||||
|   std::string path; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   path.resize(MAX_PATH + 1U); | ||||
|   path.resize(repertory::max_path_length + 1U); | ||||
|   auto size = ::GetEnvironmentVariableA(path_env.c_str(), path.data(), 0U); | ||||
|  | ||||
|   path.resize(size); | ||||
| @@ -298,7 +291,7 @@ TEST(utils_common, get_environment_variable) { | ||||
| #if defined(PROJECT_ENABLE_BOOST) | ||||
| TEST(utils_common, get_next_available_port) { | ||||
|   std::uint16_t available_port{}; | ||||
|   for (std::uint16_t port = 1U; port < 65535; ++port) { | ||||
|   for (std::uint16_t port = 1025U; port < 1030U; ++port) { | ||||
|     EXPECT_TRUE(utils::get_next_available_port(port, available_port)); | ||||
|     EXPECT_GE(available_port, port); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										225
									
								
								support/test/src/utils/encrypting_reader_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								support/test/src/utils/encrypting_reader_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| /* | ||||
|  Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  of this software and associated documentation files (the "Software"), to deal | ||||
|  in the Software without restriction, including without limitation the rights to | ||||
|  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
|  of the Software, and to permit persons to whom the Software is furnished to do | ||||
|  so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|  copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_encrypting_reader, read_file_data) { | ||||
|   const auto token = std::string("moose"); | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size()); | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         ASSERT_EQ( | ||||
|             buffer.size() / 2U, | ||||
|             utils::encryption::encrypting_reader::reader_function( | ||||
|                 reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]), | ||||
|                 buffer.size() / 2U, 1U, &reader)); | ||||
|       } | ||||
|  | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE( | ||||
|           utils::encryption::decrypt_data(token, buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
|  | ||||
|       std::size_t bytes_read{}; | ||||
|       data_buffer file_data(decrypted_data.size()); | ||||
|       EXPECT_TRUE(source_file.read( | ||||
|           file_data, | ||||
|           utils::encryption::encrypting_reader::get_data_chunk_size() * i, | ||||
|           &bytes_read)); | ||||
|       EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                file_data.size())); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) { | ||||
|   const auto token = std::string("moose"); | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size() * | ||||
|           2U); | ||||
|       EXPECT_EQ(buffer.size(), | ||||
|                 utils::encryption::encrypting_reader::reader_function( | ||||
|                     reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U, | ||||
|                     &reader)); | ||||
|  | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             token, | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
|                                               offset + (buffer.size() / 2U)))), | ||||
|             decrypted_data)); | ||||
|  | ||||
|         EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                   decrypted_data.size()); | ||||
|  | ||||
|         std::size_t bytes_read{}; | ||||
|         data_buffer file_data(decrypted_data.size()); | ||||
|         EXPECT_TRUE(source_file.read( | ||||
|             file_data, | ||||
|             (utils::encryption::encrypting_reader::get_data_chunk_size() * i) + | ||||
|                 (j * | ||||
|                  utils::encryption::encrypting_reader::get_data_chunk_size()), | ||||
|             &bytes_read)); | ||||
|         EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                  file_data.size())); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, read_file_data_as_stream) { | ||||
|   const auto token = std::string("moose"); | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|     EXPECT_EQ(reader.get_total_size(), | ||||
|               static_cast<std::uint64_t>(io_stream->tellg())); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size()); | ||||
|       EXPECT_FALSE( | ||||
|           io_stream->seekg(static_cast<std::streamoff>(i * buffer.size())) | ||||
|               .fail()); | ||||
|       EXPECT_TRUE(io_stream->good()); | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         EXPECT_FALSE( | ||||
|             io_stream | ||||
|                 ->read( | ||||
|                     reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]), | ||||
|                     static_cast<std::streamsize>(buffer.size()) / 2U) | ||||
|                 .fail()); | ||||
|         EXPECT_TRUE(io_stream->good()); | ||||
|       } | ||||
|  | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE( | ||||
|           utils::encryption::decrypt_data(token, buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
|  | ||||
|       std::size_t bytes_read{}; | ||||
|       data_buffer file_data(decrypted_data.size()); | ||||
|       EXPECT_TRUE(source_file.read( | ||||
|           file_data, | ||||
|           utils::encryption::encrypting_reader::get_data_chunk_size() * i, | ||||
|           &bytes_read)); | ||||
|       EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                file_data.size())); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) { | ||||
|   const auto token = std::string("moose"); | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8u * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|     EXPECT_EQ(reader.get_total_size(), | ||||
|               static_cast<std::uint64_t>(io_stream->tellg())); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size() * | ||||
|           2U); | ||||
|       EXPECT_FALSE(io_stream | ||||
|                        ->read(reinterpret_cast<char *>(buffer.data()), | ||||
|                               static_cast<std::streamsize>(buffer.size())) | ||||
|                        .fail()); | ||||
|       EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             token, | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
|                                               offset + (buffer.size() / 2U)))), | ||||
|             decrypted_data)); | ||||
|  | ||||
|         EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                   decrypted_data.size()); | ||||
|  | ||||
|         std::size_t bytes_read{}; | ||||
|         data_buffer file_data(decrypted_data.size()); | ||||
|         EXPECT_TRUE(source_file.read( | ||||
|             file_data, | ||||
|             (utils::encryption::encrypting_reader::get_data_chunk_size() * i) + | ||||
|                 (j * | ||||
|                  utils::encryption::encrypting_reader::get_data_chunk_size()), | ||||
|             &bytes_read)); | ||||
|         EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                  file_data.size())); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| } // namespace repertory | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| @@ -19,22 +19,19 @@ | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| #include "utils/collection.hpp" | ||||
| #include "utils/encryption.hpp" | ||||
|  | ||||
| namespace repertory { | ||||
| static const std::string token{"moose"}; | ||||
| static const std::wstring token_w{L"moose"}; | ||||
| static constexpr const std::string_view token{"moose"}; | ||||
| static constexpr const std::wstring_view token_w{L"moose"}; | ||||
|  | ||||
| TEST(utils_encryption, generate_key) { | ||||
|   auto key1 = | ||||
|       utils::encryption::generate_key<utils::encryption::hash_256_t>(token); | ||||
|   EXPECT_STREQ( | ||||
|       "182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481", | ||||
|       "ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96", | ||||
|       utils::collection::to_hex_string(key1).c_str()); | ||||
|  | ||||
|   auto key2 = | ||||
| @@ -50,9 +47,15 @@ TEST(utils_encryption, generate_key) { | ||||
|   auto key1_w = | ||||
|       utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w); | ||||
|   EXPECT_NE(key1, key1_w); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"590ac70125bec4501172937f6a2cbdeb22a87b5e40d5595eccd06b2b20548d8f", | ||||
|       L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe", | ||||
|       utils::collection::to_hex_wstring(key1_w).c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a", | ||||
|       utils::collection::to_hex_wstring(key1_w).c_str()); | ||||
| #endif | ||||
|  | ||||
|   auto key2_w = | ||||
|       utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose"); | ||||
| @@ -68,12 +71,12 @@ TEST(utils_encryption, generate_key) { | ||||
|   EXPECT_NE(key4_w, key4); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption, generate_key_default_hasher_is_sha256) { | ||||
| TEST(utils_encryption, generate_key_default_hasher_is_blake2b_256) { | ||||
|   auto key1 = | ||||
|       utils::encryption::generate_key<utils::encryption::hash_256_t>(token); | ||||
|   auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_sha256( | ||||
|         return utils::encryption::create_hash_blake2b_256( | ||||
|             std::string_view(reinterpret_cast<const char *>(data), size)); | ||||
|       }); | ||||
|   EXPECT_EQ(key1, key2); | ||||
| @@ -82,7 +85,7 @@ TEST(utils_encryption, generate_key_default_hasher_is_sha256) { | ||||
|       utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w); | ||||
|   auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token_w, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_sha256(std::wstring_view( | ||||
|         return utils::encryption::create_hash_blake2b_256(std::wstring_view( | ||||
|             reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t))); | ||||
|       }); | ||||
|   EXPECT_EQ(key1_w, key2_w); | ||||
| @@ -93,44 +96,44 @@ TEST(utils_encryption, generate_key_default_hasher_is_sha256) { | ||||
|  | ||||
| TEST(utils_encryption, generate_key_with_hasher) { | ||||
|   auto key1 = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_sha256( | ||||
|             std::string_view(reinterpret_cast<const char *>(data), size)); | ||||
|       }); | ||||
|       token, utils::encryption::blake2b_256_hasher); | ||||
|   EXPECT_STREQ( | ||||
|       "182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481", | ||||
|       "ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96", | ||||
|       utils::collection::to_hex_string(key1).c_str()); | ||||
|  | ||||
|   auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_blake2b_256( | ||||
|             std::string_view(reinterpret_cast<const char *>(data), size)); | ||||
|       }); | ||||
|       token, utils::encryption::sha256_hasher); | ||||
|   EXPECT_NE(key1, key2); | ||||
|  | ||||
|   EXPECT_STREQ( | ||||
|       "ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96", | ||||
|       "182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481", | ||||
|       utils::collection::to_hex_string(key2).c_str()); | ||||
|  | ||||
|   auto key1_w = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token_w, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_sha256(std::wstring_view( | ||||
|             reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t))); | ||||
|       }); | ||||
|       token_w, utils::encryption::blake2b_256_hasher); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"590ac70125bec4501172937f6a2cbdeb22a87b5e40d5595eccd06b2b20548d8f", | ||||
|       L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe", | ||||
|       utils::collection::to_hex_wstring(key1_w).c_str()); | ||||
|  | ||||
|   auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token_w, [](auto &&data, auto &&size) -> auto { | ||||
|         return utils::encryption::create_hash_blake2b_256(std::wstring_view( | ||||
|             reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t))); | ||||
|       }); | ||||
|   EXPECT_NE(key1_w, key2_w); | ||||
|  | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"0392d95ed3eee9772fbb9af68fedf829a8eb0adbe8575d9691cc9a752196766a", | ||||
|       utils::collection::to_hex_wstring(key1_w).c_str()); | ||||
| #endif | ||||
|  | ||||
|   auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>( | ||||
|       token_w, utils::encryption::sha256_hasher); | ||||
|   EXPECT_NE(key1_w, key2_w); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"918e4c6d39bb373f139b5fac8ec0548a9770da399b2835608974ffeac7fab6c4", | ||||
|       utils::collection::to_hex_wstring(key2_w).c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       L"590ac70125bec4501172937f6a2cbdeb22a87b5e40d5595eccd06b2b20548d8f", | ||||
|       utils::collection::to_hex_wstring(key2_w).c_str()); | ||||
| #endif | ||||
|  | ||||
|   EXPECT_NE(key1_w, key1); | ||||
|   EXPECT_NE(key2_w, key2); | ||||
| @@ -241,6 +244,39 @@ TEST(utils_encryption, decryption_failure) { | ||||
|   std::string data; | ||||
|   EXPECT_FALSE(utils::encryption::decrypt_data(key, result, data)); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption, decrypt_file_name) { | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token, | ||||
|         std::nullopt); | ||||
|  | ||||
|     auto file_name = reader.get_encrypted_file_name(); | ||||
|  | ||||
|     EXPECT_EQ(true, utils::encryption::decrypt_file_name(token, file_name)); | ||||
|     EXPECT_STREQ("test.dat", file_name.c_str()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption, decrypt_file_path) { | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     stop_type stop_requested{false}; | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), stop_requested, token, "moose/cow"); | ||||
|  | ||||
|     auto file_path = reader.get_encrypted_file_path(); | ||||
|  | ||||
|     EXPECT_EQ(true, utils::encryption::decrypt_file_path(token, file_path)); | ||||
|     EXPECT_STREQ("/moose/cow/test.dat", file_path.c_str()); | ||||
|   } | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
| } // namespace repertory | ||||
|  | ||||
|   | ||||
							
								
								
									
										66
									
								
								support/test/src/utils/error_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								support/test/src/utils/error_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| /* | ||||
|  Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  of this software and associated documentation files (the "Software"), to deal | ||||
|  in the Software without restriction, including without limitation the rights to | ||||
|  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
|  of the Software, and to permit persons to whom the Software is furnished to do | ||||
|  so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|  copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace repertory { | ||||
| template <typename T, typename U> | ||||
| constexpr bool is_decay_equ = std::is_same_v<std::decay_t<T>, U>; | ||||
|  | ||||
| TEST(utils_error, check_default_exception_handler) { | ||||
|   EXPECT_TRUE(utils::error::get_exception_handler() != nullptr); | ||||
|   if (&utils::error::default_exception_handler == | ||||
|       utils::error::get_exception_handler()) { | ||||
|     auto default_handler_is_iostream = | ||||
|         is_decay_equ<decltype(utils::error::default_exception_handler), | ||||
|                      utils::error::iostream_exception_handler>; | ||||
|     EXPECT_TRUE(default_handler_is_iostream); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_error, can_override_exception_handler) { | ||||
|   struct my_exc_handler : public utils::error::i_exception_handler { | ||||
|     MOCK_METHOD(void, handle_exception, (std::string_view function_name), | ||||
|                 (const, override)); | ||||
|  | ||||
|     MOCK_METHOD(void, handle_exception, | ||||
|                 (std::string_view function_name, const std::exception &ex), | ||||
|                 (const, override)); | ||||
|   }; | ||||
|  | ||||
|   my_exc_handler handler{}; | ||||
|   utils::error::set_exception_handler(&handler); | ||||
|  | ||||
|   EXPECT_CALL(handler, handle_exception("test_func")).WillOnce(Return()); | ||||
|   utils::error::handle_exception("test_func"); | ||||
|  | ||||
|   auto ex = std::runtime_error("moose"); | ||||
|   EXPECT_CALL(handler, handle_exception(_, _)) | ||||
|       .WillOnce( | ||||
|           [&ex](std::string_view function_name, const std::exception &ex2) { | ||||
|             EXPECT_EQ("test_func_ex", function_name); | ||||
|             EXPECT_STREQ(ex.what(), ex2.what()); | ||||
|           }); | ||||
|   utils::error::handle_exception("test_func_ex", ex); | ||||
|  | ||||
|   utils::error::set_exception_handler(&utils::error::default_exception_handler); | ||||
| } | ||||
| } // namespace repertory | ||||
							
								
								
									
										517
									
								
								support/test/src/utils/file_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										517
									
								
								support/test/src/utils/file_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,517 @@ | ||||
| /* | ||||
|  Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  of this software and associated documentation files (the "Software"), to deal | ||||
|  in the Software without restriction, including without limitation the rights to | ||||
|  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
|  of the Software, and to permit persons to whom the Software is furnished to do | ||||
|  so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|  copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace { | ||||
| static constexpr const auto file_type_count{1U}; | ||||
| } | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_file, can_create_file) { | ||||
|   for (auto idx = 0U; idx < file_type_count; ++idx) { | ||||
|     auto path = test::generate_test_file_name("utils_file"); | ||||
|     EXPECT_FALSE(utils::file::file(path).exists() || | ||||
|                  utils::file::directory(path).exists()); | ||||
|  | ||||
|     auto file = idx == 0U ? utils::file::file::open_or_create_file(path) | ||||
|                           : utils::file::thread_file::open_or_create_file(path); | ||||
|     EXPECT_TRUE(*file); | ||||
|  | ||||
|     EXPECT_TRUE(utils::file::file(path).exists()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, can_open_file) { | ||||
|   for (auto idx = 0U; idx < file_type_count; ++idx) { | ||||
|     auto path = test::generate_test_file_name("utils_file"); | ||||
|  | ||||
|     { | ||||
|       auto file = idx == 0U | ||||
|                       ? utils::file::file::open_or_create_file(path) | ||||
|                       : utils::file::thread_file::open_or_create_file(path); | ||||
|       EXPECT_TRUE(*file); | ||||
|     } | ||||
|  | ||||
|     { | ||||
|       auto file = idx == 0U ? utils::file::file::open_file(path) | ||||
|                             : utils::file::thread_file::open_file(path); | ||||
|       EXPECT_TRUE(*file); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, open_file_fails_if_not_found) { | ||||
|   for (auto idx = 0U; idx < file_type_count; ++idx) { | ||||
|     auto path = test::generate_test_file_name("utils_file"); | ||||
|  | ||||
|     auto file = idx == 0U ? utils::file::file::open_file(path) | ||||
|                           : utils::file::thread_file::open_file(path); | ||||
|     EXPECT_FALSE(*file); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, write_fails_for_read_only_file) { | ||||
|   for (auto idx = 0U; idx < file_type_count; ++idx) { | ||||
|     auto path = test::generate_test_file_name("utils_file"); | ||||
|  | ||||
|     auto file = idx == 0U | ||||
|                     ? utils::file::file::open_or_create_file(path, true) | ||||
|                     : utils::file::thread_file::open_or_create_file(path, true); | ||||
|     EXPECT_TRUE(utils::file::file(path).exists()); | ||||
|     EXPECT_TRUE(*file); | ||||
|     std::size_t bytes_written{}; | ||||
|     EXPECT_FALSE(file->write(reinterpret_cast<const unsigned char *>("0"), 1U, | ||||
|                              0U, &bytes_written)); | ||||
|     EXPECT_EQ(0U, bytes_written); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // TEST(utils_file, can_attach_file) { | ||||
| //   for (auto idx = 0U; idx < file_type_count; ++idx) { | ||||
| //     auto path = test::generate_test_file_name("utils_file"); | ||||
| //     auto file = idx == 0U ? utils::file::file::open_or_create_file(path) | ||||
| //                           : | ||||
| //                           utils::file::thread_file::open_or_create_file(path); | ||||
| //     auto file2 = | ||||
| //         idx == 0U ? utils::file::file::attach_file(file->get_handle()) | ||||
| //                   : | ||||
| //                   utils::file::thread_file::attach_file(file->get_handle()); | ||||
| //     EXPECT_TRUE(*file); | ||||
| //     EXPECT_TRUE(*file2); | ||||
| //     EXPECT_EQ(file->get_path(), file2->get_path()); | ||||
| //   } | ||||
| // } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_JSON) | ||||
| TEST(utils_file, read_and_write_json_file) { | ||||
|   auto path = test::generate_test_file_name("utils_file"); | ||||
|  | ||||
|   auto json_data = nlohmann::json({{"moose", "cow"}}); | ||||
|   EXPECT_TRUE(utils::file::write_json_file(path, json_data)); | ||||
|  | ||||
|   { | ||||
|     nlohmann::json result_data{}; | ||||
|     EXPECT_TRUE(utils::file::read_json_file(path, result_data)); | ||||
|     EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     nlohmann::json result_data{}; | ||||
|     EXPECT_TRUE(utils::file::read_json_file(utils::string::from_utf8(path), | ||||
|                                             result_data)); | ||||
|     EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| TEST(utils_file, read_and_write_json_file_encrypted) { | ||||
|   { | ||||
|     auto path = test::generate_test_file_name("utils_file"); | ||||
|  | ||||
|     auto json_data = nlohmann::json({{"moose", "cow"}}); | ||||
|     EXPECT_TRUE(utils::file::write_json_file(path, json_data, "moose")); | ||||
|  | ||||
|     nlohmann::json result_data{}; | ||||
|     EXPECT_TRUE(utils::file::read_json_file(path, result_data, "moose")); | ||||
|     EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str()); | ||||
|  | ||||
|     { | ||||
|       auto file = utils::file::file::open_file(path); | ||||
|       data_buffer encrypted_data{}; | ||||
|       EXPECT_TRUE(file->read_all(encrypted_data, 0U)); | ||||
|  | ||||
|       data_buffer decrypted_data{}; | ||||
|       EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data, | ||||
|                                                   decrypted_data)); | ||||
|       EXPECT_STREQ(json_data.dump().c_str(), | ||||
|                    nlohmann::json::parse(std::string(decrypted_data.begin(), | ||||
|                                                      decrypted_data.end())) | ||||
|                        .dump() | ||||
|                        .c_str()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto path = | ||||
|         utils::string::from_utf8(test::generate_test_file_name("utils_file")); | ||||
|  | ||||
|     auto json_data = nlohmann::json({{"moose", "cow"}}); | ||||
|     EXPECT_TRUE(utils::file::write_json_file(path, json_data, L"moose")); | ||||
|  | ||||
|     nlohmann::json result_data{}; | ||||
|     EXPECT_TRUE(utils::file::read_json_file(path, result_data, L"moose")); | ||||
|     EXPECT_STREQ(json_data.dump().c_str(), result_data.dump().c_str()); | ||||
|  | ||||
|     { | ||||
|       auto file = utils::file::file::open_file(path); | ||||
|       data_buffer encrypted_data{}; | ||||
|       EXPECT_TRUE(file->read_all(encrypted_data, 0U)); | ||||
|  | ||||
|       data_buffer decrypted_data{}; | ||||
|       EXPECT_TRUE(utils::encryption::decrypt_data("moose", encrypted_data, | ||||
|                                                   decrypted_data)); | ||||
|       EXPECT_STREQ(json_data.dump().c_str(), | ||||
|                    nlohmann::json::parse(std::string(decrypted_data.begin(), | ||||
|                                                      decrypted_data.end())) | ||||
|                        .dump() | ||||
|                        .c_str()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBDSM) | ||||
| TEST(utils_file, smb_create_smb_path) { | ||||
|   auto path = "//server/share"; | ||||
|   auto rel_path = "test/test.txt"; | ||||
|   auto smb_path = utils::file::smb_create_smb_path(path, rel_path); | ||||
|   EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str()); | ||||
|  | ||||
|   rel_path = "/test/test.txt"; | ||||
|   smb_path = utils::file::smb_create_smb_path(path, rel_path); | ||||
|   EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str()); | ||||
|  | ||||
|   rel_path = "test\\test.txt"; | ||||
|   smb_path = utils::file::smb_create_smb_path(path, rel_path); | ||||
|   EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str()); | ||||
|  | ||||
|   rel_path = "\\test\\test.txt"; | ||||
|   smb_path = utils::file::smb_create_smb_path(path, rel_path); | ||||
|   EXPECT_STREQ("//server/share/test/test.txt", smb_path.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, smb_create_relative_path) { | ||||
|   auto path = "//server/share/test.txt"; | ||||
|   auto rel_path = utils::file::smb_create_relative_path(path); | ||||
|   EXPECT_STREQ("\\test.txt", rel_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/test"; | ||||
|   rel_path = utils::file::smb_create_relative_path(path); | ||||
|   EXPECT_STREQ("\\test", rel_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/test/"; | ||||
|   rel_path = utils::file::smb_create_relative_path(path); | ||||
|   EXPECT_STREQ("\\test", rel_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/test/"; | ||||
|   rel_path = utils::file::smb_create_relative_path(path); | ||||
|   EXPECT_STREQ("\\test", rel_path.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, smb_create_search_path) { | ||||
|   auto path = "//server/share"; | ||||
|   auto search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\*", search_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/"; | ||||
|   search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\*", search_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/folder"; | ||||
|   search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\folder\\*", search_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/folder/"; | ||||
|   search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\folder\\*", search_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/folder/next"; | ||||
|   search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\folder\\next\\*", search_path.c_str()); | ||||
|  | ||||
|   path = "//server/share/folder/next/"; | ||||
|   search_path = utils::file::smb_create_search_path(path); | ||||
|   EXPECT_STREQ("\\folder\\next\\*", search_path.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, smb_parent_is_same) { | ||||
|   auto path1 = "//server/share"; | ||||
|   auto path2 = "//server/share"; | ||||
|   EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/share/"; | ||||
|   path2 = "//server/share/"; | ||||
|   EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/share/one"; | ||||
|   path2 = "//server/share/two"; | ||||
|   EXPECT_TRUE(utils::file::smb_parent_is_same(path1, path2)); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, smb_parent_is_not_same) { | ||||
|   auto path1 = "server/share"; | ||||
|   auto path2 = "//server/share"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "server/share/"; | ||||
|   path2 = "server/share/"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server1/share/one"; | ||||
|   path2 = "//server/share/two"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/share"; | ||||
|   path2 = "//server/share2"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/share/"; | ||||
|   path2 = "//server/share2/"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/share/one"; | ||||
|   path2 = "//server/share2/two"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server"; | ||||
|   path2 = "//server/share/two"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server/"; | ||||
|   path2 = "//server/"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "//server"; | ||||
|   path2 = "//server"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
|  | ||||
|   path1 = "// server/cow"; | ||||
|   path2 = "// server/cow"; | ||||
|   EXPECT_FALSE(utils::file::smb_parent_is_same(path1, path2)); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_LIBDSM) | ||||
|  | ||||
| TEST(utils_file, directory_exists_in_path) { | ||||
|   auto &test_dir = test::generate_test_directory(); | ||||
|   EXPECT_FALSE( | ||||
|       utils::file::directory_exists_in_path(test_dir.get_path(), "moose")); | ||||
|  | ||||
|   EXPECT_FALSE(utils::file::directory_exists_in_path( | ||||
|       utils::string::from_utf8(test_dir.get_path()), L"moose")); | ||||
|  | ||||
|   EXPECT_FALSE(utils::file::file_exists_in_path(test_dir.get_path(), "moose")); | ||||
|  | ||||
|   EXPECT_FALSE(utils::file::file_exists_in_path( | ||||
|       utils::string::from_utf8(test_dir.get_path()), L"moose")); | ||||
|  | ||||
|   auto sub_dir = test_dir.create_directory("moose"); | ||||
|   EXPECT_TRUE(sub_dir != nullptr); | ||||
|   if (sub_dir) { | ||||
|     EXPECT_TRUE( | ||||
|         utils::file::directory_exists_in_path(test_dir.get_path(), "moose")); | ||||
|  | ||||
|     EXPECT_FALSE( | ||||
|         utils::file::file_exists_in_path(test_dir.get_path(), "moose")); | ||||
|  | ||||
|     EXPECT_TRUE(utils::file::directory_exists_in_path( | ||||
|         utils::string::from_utf8(test_dir.get_path()), L"moose")); | ||||
|  | ||||
|     EXPECT_FALSE(utils::file::file_exists_in_path( | ||||
|         utils::string::from_utf8(test_dir.get_path()), L"moose")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, file_exists_in_path) { | ||||
|   auto &test_dir = test::generate_test_directory(); | ||||
|   EXPECT_FALSE( | ||||
|       utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt")); | ||||
|  | ||||
|   EXPECT_FALSE(utils::file::file_exists_in_path( | ||||
|       utils::string::from_utf8(test_dir.get_path()), L"moose.txt")); | ||||
|  | ||||
|   EXPECT_FALSE( | ||||
|       utils::file::directory_exists_in_path(test_dir.get_path(), "moose.txt")); | ||||
|  | ||||
|   EXPECT_FALSE(utils::file::directory_exists_in_path( | ||||
|       utils::string::from_utf8(test_dir.get_path()), L"moose.txt")); | ||||
|  | ||||
|   auto sub_file = test_dir.create_file("moose.txt", false); | ||||
|   EXPECT_TRUE(sub_file != nullptr); | ||||
|   if (sub_file) { | ||||
|     EXPECT_TRUE( | ||||
|         utils::file::file_exists_in_path(test_dir.get_path(), "moose.txt")); | ||||
|  | ||||
|     EXPECT_FALSE(utils::file::directory_exists_in_path(test_dir.get_path(), | ||||
|                                                        "moose.txt")); | ||||
|  | ||||
|     EXPECT_TRUE(utils::file::file_exists_in_path( | ||||
|         utils::string::from_utf8(test_dir.get_path()), L"moose.txt")); | ||||
|  | ||||
|     EXPECT_FALSE(utils::file::directory_exists_in_path( | ||||
|         utils::string::from_utf8(test_dir.get_path()), L"moose.txt")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_free_drive_space) { | ||||
| #if defined(_WIN32) | ||||
|   auto space = utils::file::get_free_drive_space("C:"); | ||||
|   auto space2 = utils::file::get_free_drive_space(L"C:"); | ||||
| #else  // defined(_WIN32) | ||||
|   auto space = utils::file::get_free_drive_space("/"); | ||||
|   auto space2 = utils::file::get_free_drive_space(L"/"); | ||||
| #endif // !defined(_WIN32) | ||||
|  | ||||
|   EXPECT_TRUE(space.has_value()); | ||||
|   EXPECT_LT(0U, space.value()); | ||||
|  | ||||
|   EXPECT_TRUE(space2.has_value()); | ||||
|   EXPECT_EQ(space.value(), space2.value()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_free_drive_space_fails_for_bad_path) { | ||||
|   std::string name{"free_drive_space_test_XXXXXX"}; | ||||
|   auto temp = utils::file::create_temp_name("free_drive_space_test"); | ||||
|  | ||||
|   auto space = utils::file::get_free_drive_space(temp); | ||||
|   EXPECT_FALSE(space.has_value()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_total_drive_space) { | ||||
| #if defined(_WIN32) | ||||
|   auto space = utils::file::get_total_drive_space("C:"); | ||||
|   auto space2 = utils::file::get_total_drive_space(L"C:"); | ||||
| #else  // defined(_WIN32) | ||||
|   auto space = utils::file::get_total_drive_space("/"); | ||||
|   auto space2 = utils::file::get_total_drive_space(L"/"); | ||||
| #endif // !defined(_WIN32) | ||||
|  | ||||
|   EXPECT_TRUE(space.has_value()); | ||||
|   EXPECT_LT(0U, space.value()); | ||||
|  | ||||
|   EXPECT_TRUE(space2.has_value()); | ||||
|   EXPECT_EQ(space.value(), space2.value()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, create_temp_name) { | ||||
|   { | ||||
|     auto temp = utils::file::create_temp_name("test_temp"); | ||||
|     EXPECT_EQ(18U, temp.size()); | ||||
|  | ||||
|     auto temp2 = utils::file::create_temp_name("test_temp"); | ||||
|     EXPECT_STRNE(temp.c_str(), temp2.c_str()); | ||||
|  | ||||
|     EXPECT_TRUE(utils::string::begins_with(temp, "test_temp_")); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto temp = utils::file::create_temp_name(L"test_temp"); | ||||
|     EXPECT_EQ(18U, temp.size()); | ||||
|  | ||||
|     auto temp2 = utils::file::create_temp_name(L"test_temp"); | ||||
|     EXPECT_STRNE(temp.c_str(), temp2.c_str()); | ||||
|  | ||||
|     EXPECT_TRUE(utils::string::begins_with(temp, L"test_temp_")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_total_drive_space_fails_for_bad_path) { | ||||
|   auto temp = utils::file::create_temp_name("total_drive_space_test"); | ||||
|   auto space = utils::file::get_total_drive_space(temp); | ||||
|   EXPECT_FALSE(space.has_value()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_times) { | ||||
|   { | ||||
|     auto times = | ||||
|         utils::file::get_times(test::create_random_file(1U).get_path()); | ||||
|     EXPECT_TRUE(times.has_value()); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::accessed)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::created)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::modified)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::written)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto times = utils::file::get_times( | ||||
|         utils::string::from_utf8(test::create_random_file(1U).get_path())); | ||||
|     EXPECT_TRUE(times.has_value()); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::accessed)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::created)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::modified)); | ||||
|     EXPECT_LT(0U, times->get(utils::file::time_type::written)); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_times_fails_if_not_found) { | ||||
|   auto temp = utils::path::combine(".", {"get_times_test"}); | ||||
|   auto times = utils::file::get_times(temp); | ||||
|   EXPECT_FALSE(times.has_value()); | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_time) { | ||||
|   { | ||||
|     auto file_path = test::create_random_file(1U).get_path(); | ||||
|     auto file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::accessed); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::created); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::modified); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::written); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto file_path = | ||||
|         utils::string::from_utf8(test::create_random_file(1U).get_path()); | ||||
|  | ||||
|     auto file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::accessed); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::created); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::modified); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|  | ||||
|     file_time = | ||||
|         utils::file::get_time(file_path, utils::file::time_type::written); | ||||
|     EXPECT_TRUE(file_time.has_value()); | ||||
|     EXPECT_LT(0U, file_time.value()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_file, get_time_fails_if_not_found) { | ||||
|   auto temp = utils::path::combine(".", {"get_times_test"}); | ||||
|   auto file_time = | ||||
|       utils::file::get_time(temp, utils::file::time_type::accessed); | ||||
|   EXPECT_FALSE(file_time.has_value()); | ||||
| } | ||||
| } // namespace repertory | ||||
							
								
								
									
										186
									
								
								support/test/src/utils/hash_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								support/test/src/utils/hash_test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| /* | ||||
|  Copyright <2018-2024> <scott.e.graves@protonmail.com> | ||||
|  | ||||
|   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  of this software and associated documentation files (the "Software"), to deal | ||||
|  in the Software without restriction, including without limitation the rights to | ||||
|  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||||
|  of the Software, and to permit persons to whom the Software is furnished to do | ||||
|  so, subject to the following conditions: | ||||
|  | ||||
|   The above copyright notice and this permission notice shall be included in all | ||||
|  copies or substantial portions of the Software. | ||||
|  | ||||
|   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
| */ | ||||
| #include "test.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_LIBSODIUM) | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_hash, hash_type_sizes) { | ||||
|   EXPECT_EQ(32U, utils::encryption::hash_256_t{}.size()); | ||||
|   EXPECT_EQ(48U, utils::encryption::hash_384_t{}.size()); | ||||
|   EXPECT_EQ(64U, utils::encryption::hash_512_t{}.size()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, default_hasher_is_blake2b) { | ||||
|   EXPECT_EQ( | ||||
|       &utils::encryption::blake2b_256_hasher, | ||||
|       &utils::encryption::default_create_hash<utils::encryption::hash_256_t>()); | ||||
|  | ||||
|   EXPECT_EQ( | ||||
|       &utils::encryption::blake2b_384_hasher, | ||||
|       &utils::encryption::default_create_hash<utils::encryption::hash_384_t>()); | ||||
|  | ||||
|   EXPECT_EQ( | ||||
|       &utils::encryption::blake2b_512_hasher, | ||||
|       &utils::encryption::default_create_hash<utils::encryption::hash_512_t>()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, blake2b_256) { | ||||
|   auto hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_256("a")); | ||||
|   EXPECT_STREQ( | ||||
|       "8928aae63c84d87ea098564d1e03ad813f107add474e56aedd286349c0c03ea4", | ||||
|       hash.c_str()); | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_256(L"a")); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "d2373b17cd8a8e19e39f52fa4905a274f93805fbb8bb4c7f3cb4b2cd6708ec8a", | ||||
|       hash.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "9fdf5757d7eea386f0d34d2c0e202527986febf1ebb4315fcf7fff40776fa41d", | ||||
|       hash.c_str()); | ||||
| #endif | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_256({1U})); | ||||
|   EXPECT_STREQ( | ||||
|       "ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25", | ||||
|       hash.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, blake2b_384) { | ||||
|   auto hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_384("a")); | ||||
|   EXPECT_STREQ("7d40de16ff771d4595bf70cbda0c4ea0a066a6046fa73d34471cd4d93d827d7" | ||||
|                "c94c29399c50de86983af1ec61d5dcef0", | ||||
|                hash.c_str()); | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_384(L"a")); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ("637fe31d1e955760ef31043d525d9321826a778ddbe82fcde45a98394241380" | ||||
|                "96675e2f87e36b53ab223a7fd254198fd", | ||||
|                hash.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ("9d469bd9dab9d4b48b8688de7c22704a8de1b081294f9be294100dfa9f05c92" | ||||
|                "e8d3616476e46cd14f9e613fed80fd157", | ||||
|                hash.c_str()); | ||||
| #endif | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_384({1U})); | ||||
|   EXPECT_STREQ("42cfe875d08d816538103b906bb0b05202e0b09c4e981680c1110684fc7845b" | ||||
|                "c91c178fa167afcc445490644b2bf5f5b", | ||||
|                hash.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, blake2b_512) { | ||||
|   auto hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_512("a")); | ||||
|   EXPECT_STREQ( | ||||
|       "333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34d5abe2c6" | ||||
|       "b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c", | ||||
|       hash.c_str()); | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_512(L"a")); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "05970b95468b0b1941066ff189091493e73859ce41cde5ad08118e93ea1d81a57a144296" | ||||
|       "a26a9fe7781481bde97b886725e36e30b305d8bd5cce1ae36bf1564a", | ||||
|       hash.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "bbc187c6e4d8525655d0ada62d16eed59f3db3ab07e04fb0483fd4ae21d88b984774add9" | ||||
|       "b3fbcff56f9638091013994f8e2d4646fdbbcb4879e2b5160bbb755d", | ||||
|       hash.c_str()); | ||||
| #endif | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_blake2b_512({1U})); | ||||
|   EXPECT_STREQ( | ||||
|       "9545ba37b230d8a2e716c4707586542780815b7c4088edcb9af6a9452d50f32474d5ba9a" | ||||
|       "ab52a67aca864ef2696981c2eadf49020416136afd838fb048d21653", | ||||
|       hash.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, sha256) { | ||||
|   auto hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha256("a")); | ||||
|   EXPECT_STREQ( | ||||
|       "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", | ||||
|       hash.c_str()); | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha256(L"a")); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "ffe9aaeaa2a2d5048174df0b80599ef0197ec024c4b051bc9860cff58ef7f9f3", | ||||
|       hash.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "a2d398922901344d08180dc41d3e9d73d8c148c7f6e092835bbb28e02dbcf184", | ||||
|       hash.c_str()); | ||||
| #endif | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha256({1U})); | ||||
|   EXPECT_STREQ( | ||||
|       "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", | ||||
|       hash.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_hash, sha512) { | ||||
|   auto hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha512("a")); | ||||
|   EXPECT_STREQ( | ||||
|       "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c65" | ||||
|       "2bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", | ||||
|       hash.c_str()); | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha512(L"a")); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "5c2ca3d50f46ece6066c53bd1a490cbe5f72d2738ae9417332e91e5c3f75205c639d71a9" | ||||
|       "a41d67d965fa137dddf439e0ab9443a6ea44915e90d8b5b566d1c076", | ||||
|       hash.c_str()); | ||||
| #else // !defined(_WIN32) | ||||
|   EXPECT_STREQ( | ||||
|       "a93498d992e81915075144cb304d2bdf040b336283f888252244882d8366dd3a6e2d9749" | ||||
|       "077114dda1a9aa1a7b69d33f7a781f003ccd12e599a6341014f29aaf", | ||||
|       hash.c_str()); | ||||
| #endif | ||||
|  | ||||
|   hash = utils::collection::to_hex_string( | ||||
|       utils::encryption::create_hash_sha512({1U})); | ||||
|   EXPECT_STREQ( | ||||
|       "7b54b66836c1fbdd13d2441d9e1434dc62ca677fb68f5fe66a464baadecdbd00576f8d6b" | ||||
|       "5ac3bcc80844b7d50b1cc6603444bbe7cfcf8fc0aa1ee3c636d9e339", | ||||
|       hash.c_str()); | ||||
| } | ||||
| } // namespace repertory | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| @@ -19,11 +19,31 @@ | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "gtest/gtest.h" | ||||
| #include "test.hpp" | ||||
|  | ||||
| #include "utils/common.hpp" | ||||
| #include "utils/path.hpp" | ||||
| #include "utils/string.hpp" | ||||
| #if defined(_WIN32) | ||||
| namespace { | ||||
| static const auto test_path = [](std::string str) -> std::string { | ||||
| #if defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|   if (repertory::utils::string::begins_with(str, "\\")) { | ||||
|     str = repertory::utils::string::to_lower( | ||||
|               std::filesystem::current_path().string().substr(0U, 2U)) + | ||||
|           str; | ||||
|   } | ||||
|  | ||||
|   str = std::string{repertory::utils::path::long_notation} + str; | ||||
| #else  // !defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|   if (repertory::utils::string::begins_with(str, "\\")) { | ||||
|     str = repertory::utils::string::to_lower( | ||||
|               std::filesystem::current_path().string().substr(0U, 2U)) + | ||||
|           str; | ||||
|   } | ||||
| #endif // defined(PROJECT_ENABLE_WIN32_LONG_PATH_NAMES) | ||||
|  | ||||
|   return repertory::utils::string::right_trim(str, '\\'); | ||||
| }; | ||||
| } // namespace | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_path, constants) { | ||||
| @@ -31,10 +51,17 @@ TEST(utils_path, constants) { | ||||
|   EXPECT_EQ(std::wstring_view{L"\\"}, utils::path::backslash_w); | ||||
|   EXPECT_EQ(std::string_view{"."}, utils::path::dot); | ||||
|   EXPECT_EQ(std::wstring_view{L"."}, utils::path::dot_w); | ||||
|   EXPECT_EQ(std::string_view{".\\"}, utils::path::dot_backslash); | ||||
|   EXPECT_EQ(std::wstring_view{L".\\"}, utils::path::dot_backslash_w); | ||||
|   EXPECT_EQ(std::string_view{"./"}, utils::path::dot_slash); | ||||
|   EXPECT_EQ(std::wstring_view{L"./"}, utils::path::dot_slash_w); | ||||
|   EXPECT_EQ(std::string_view{"/"}, utils::path::slash); | ||||
|   EXPECT_EQ(std::wstring_view{L"/"}, utils::path::slash_w); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_EQ(std::string_view{"\\\\"}, utils::path::unc_notation); | ||||
|   EXPECT_EQ(std::wstring_view{L"\\\\"}, utils::path::unc_notation_w); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| TEST(utils_path, directory_seperator) { | ||||
| @@ -86,6 +113,12 @@ TEST(utils_path, get_dot) { | ||||
|   EXPECT_EQ(utils::path::dot_w, utils::path::get_dot<wchar_t>()); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, get_dot_backslash) { | ||||
|   EXPECT_EQ(utils::path::dot_backslash, utils::path::get_dot_backslash<char>()); | ||||
|   EXPECT_EQ(utils::path::dot_backslash_w, | ||||
|             utils::path::get_dot_backslash<wchar_t>()); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, get_dot_slash) { | ||||
|   EXPECT_EQ(utils::path::dot_slash, utils::path::get_dot_slash<char>()); | ||||
|   EXPECT_EQ(utils::path::dot_slash_w, utils::path::get_dot_slash<wchar_t>()); | ||||
| @@ -96,40 +129,56 @@ TEST(utils_path, get_slash) { | ||||
|   EXPECT_EQ(utils::path::slash_w, utils::path::get_slash<wchar_t>()); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, get_long_notation) { | ||||
|   EXPECT_EQ(utils::path::long_notation, utils::path::get_long_notation<char>()); | ||||
|   EXPECT_EQ(utils::path::long_notation_w, | ||||
|             utils::path::get_long_notation<wchar_t>()); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, combine) { | ||||
|   auto s = utils::path::combine(R"(\test\path)", {}); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\test\path)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/test/path", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::combine(R"(\test)", {R"(\path)"}); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\test\path)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\test\path)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/test/path", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::combine(R"(\test)", {R"(\path)", R"(\again\)"}); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\test\path\again)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\test\path\again)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/test/path/again", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::combine("/home/test/.dest", {".state"}); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(test_path(R"(\home\test\.dest\.state)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/home/test/.dest/.state", s.c_str()); | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   s = utils::path::combine(R"(R:\test)", {R"(\path)", R"(\again\)"}); | ||||
|   EXPECT_STREQ(R"(r:\test\path\again)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(r:\test\path\again)").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::combine("R:", {R"(\path)", R"(\again\)"}); | ||||
|   EXPECT_STREQ(R"(r:\path\again)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(r:\path\again)").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::combine("R:", {}); | ||||
|   EXPECT_STREQ("r:", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("r:").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::combine("R:", {"\\"}); | ||||
|   EXPECT_STREQ("r:", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("r:").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::combine("\\\\moose", {"cow"}); | ||||
|   EXPECT_STREQ("\\\\moose\\cow", s.c_str()); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| @@ -204,6 +253,15 @@ TEST(utils_path, create_api_path) { | ||||
|  | ||||
|   s = utils::path::create_api_path("/cow\\\\/moose\\\\/\\dog\\chicken\\"); | ||||
|   EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); | ||||
|  | ||||
|   s = utils::path::create_api_path(".state"); | ||||
|   EXPECT_STREQ("/.state", s.c_str()); | ||||
|  | ||||
|   s = utils::path::create_api_path("/.state/.local"); | ||||
|   EXPECT_STREQ("/.state/.local", s.c_str()); | ||||
|  | ||||
|   s = utils::path::create_api_path("./.state/.local"); | ||||
|   EXPECT_STREQ("/.state/.local", s.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, finalize) { | ||||
| @@ -212,104 +270,137 @@ TEST(utils_path, finalize) { | ||||
|  | ||||
|   s = utils::path::finalize(R"(\)"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize("/"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize(R"(\\)"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\)", s.c_str()); | ||||
|   EXPECT_STREQ("\\\\", s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize("//"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\)", s.c_str()); | ||||
|   EXPECT_STREQ("\\\\", s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize("/cow///moose/////dog/chicken"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize("\\cow\\\\\\moose\\\\\\\\dog\\chicken/"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); | ||||
| #endif | ||||
|  | ||||
|   s = utils::path::finalize("/cow\\\\/moose\\\\/\\dog\\chicken\\"); | ||||
| #if defined(_WIN32) | ||||
|   EXPECT_STREQ(R"(\cow\moose\dog\chicken)", s.c_str()); | ||||
|   EXPECT_STREQ(test_path(R"(\cow\moose\dog\chicken)").c_str(), s.c_str()); | ||||
| #else | ||||
|   EXPECT_STREQ("/cow/moose/dog/chicken", s.c_str()); | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   s = utils::path::finalize("D:"); | ||||
|   EXPECT_STREQ("d:", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:\\"); | ||||
|   EXPECT_STREQ("d:", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:\\moose"); | ||||
|   EXPECT_STREQ("d:\\moose", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:\\moose\\"); | ||||
|   EXPECT_STREQ("d:\\moose", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:/"); | ||||
|   EXPECT_STREQ("d:", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:/moose"); | ||||
|   EXPECT_STREQ("d:\\moose", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("D:/moose/"); | ||||
|   EXPECT_STREQ("d:\\moose", s.c_str()); | ||||
|   EXPECT_STREQ(test_path("d:\\moose").c_str(), s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("\\\\moose\\cow"); | ||||
|   EXPECT_STREQ("\\\\moose\\cow", s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("//moose/cow"); | ||||
|   EXPECT_STREQ("\\\\moose\\cow", s.c_str()); | ||||
| #else  // !defined(_WIN32) | ||||
|   s = utils::path::finalize("\\\\moose\\cow"); | ||||
|   EXPECT_STREQ("/moose/cow", s.c_str()); | ||||
|  | ||||
|   s = utils::path::finalize("//moose/cow"); | ||||
|   EXPECT_STREQ("/moose/cow", s.c_str()); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| TEST(utils_path, absolute) { | ||||
|   auto dir = utils::path::absolute(std::filesystem::current_path().string()); | ||||
|   auto dir = utils::path::get_current_path<std::string>(); | ||||
|   auto path = utils::path::absolute("."); | ||||
|   EXPECT_STREQ(dir.c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute("./"); | ||||
|   EXPECT_STREQ(dir.c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(.\)"); | ||||
|   EXPECT_STREQ(dir.c_str(), path.c_str()); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   path = utils::path::absolute(R"(.\moose)"); | ||||
|   EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(./moose)"); | ||||
|   EXPECT_STREQ((dir + R"(\moose)").c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(\\server\share)"); | ||||
|   EXPECT_STREQ(R"(\\server\share)", path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(//server/share)"); | ||||
|   EXPECT_STREQ(R"(\\server\share)", path.c_str()); | ||||
|  | ||||
|   auto home_env = utils::get_environment_variable("USERPROFILE"); | ||||
| #else  // !defined(_WIN32) | ||||
|   path = utils::path::absolute(R"(.\moose)"); | ||||
|   EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(./moose)"); | ||||
|   EXPECT_STREQ((dir + R"(/moose)").c_str(), path.c_str()); | ||||
|  | ||||
|   path = utils::path::absolute(R"(\\server\share)"); | ||||
|   EXPECT_STREQ(R"(/server/share)", path.c_str()); | ||||
|  | ||||
|   auto home_env = utils::get_environment_variable("HOME"); | ||||
| #endif // defined(_WIN32) | ||||
|  | ||||
|   auto home = utils::path::absolute(home_env); | ||||
|  | ||||
|   path = utils::path::absolute("~"); | ||||
|   EXPECT_STREQ(home.c_str(), path.c_str()); | ||||
|  | ||||
|   // path = utils::path::absolute("~/.local"); | ||||
| } | ||||
|  | ||||
| TEST(utils_path, absolute_can_resolve_path_variables) { | ||||
|   std::string home{}; | ||||
|  | ||||
| #if defined(_WIN32) | ||||
|   home.resize(MAX_PATH + 1U); | ||||
|   home.resize(repertory::max_path_length + 1U); | ||||
|   auto size = ::GetEnvironmentVariableA("USERPROFILE", home.data(), 0U); | ||||
|  | ||||
|   home.resize(size); | ||||
| @@ -331,4 +422,128 @@ TEST(utils_path, absolute_can_resolve_path_variables) { | ||||
|   EXPECT_STREQ(home.c_str(), expanded_str.c_str()); | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| TEST(utils_path, get_parent_path) { | ||||
| #if defined(_WIN32) | ||||
|   { | ||||
|     auto dir = R"(c:\test)"; | ||||
|     auto parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ("c:", parent.c_str()); | ||||
|  | ||||
|     dir = R"(c:\test\file.txt)"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(R"(c:\test)", parent.c_str()); | ||||
|  | ||||
|     dir = "c:"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ("c:", parent.c_str()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = LR"(c:\test)"; | ||||
|     auto parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(L"c:", parent.c_str()); | ||||
|  | ||||
|     dir = LR"(c:\test\file.txt)"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(LR"(c:\test)", parent.c_str()); | ||||
|  | ||||
|     dir = L"c:"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(L"c:", parent.c_str()); | ||||
|   } | ||||
| #else  // !defined(_WIN32) | ||||
|   { | ||||
|     auto dir = "/test"; | ||||
|     auto parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ("/", parent.c_str()); | ||||
|  | ||||
|     dir = "/test/test"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ("/test", parent.c_str()); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = L"/test"; | ||||
|     auto parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(L"/", parent.c_str()); | ||||
|  | ||||
|     dir = L"/test/test"; | ||||
|     parent = utils::path::get_parent_path(dir); | ||||
|     EXPECT_STREQ(L"/test", parent.c_str()); | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| TEST(utils_path, contains_trash_directory) { | ||||
| #if defined(_WIN32) | ||||
|   { | ||||
|     auto dir = R"(c:\$recycle.bin)"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = R"(c:\$recycle.bin\moose.txt)"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = LR"(c:\$recycle.bin)"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = LR"(c:\$recycle.bin\moose.txt)"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
| #else  // !defined(_WIN32) | ||||
|   { | ||||
|     auto dir = "/$recycle.bin"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = "/$recycle.bin/moose.txt"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = L"/$recycle.bin"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = L"/$recycle.bin/moose.txt"; | ||||
|     EXPECT_TRUE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
|  | ||||
| TEST(utils_path, does_not_contain_trash_directory) { | ||||
| #if defined(_WIN32) | ||||
|   { | ||||
|     auto dir = R"(c:\recycle.bin)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = R"(c:\recycle.bin\moose.txt)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = LR"(c:\recycle.bin)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = LR"(c:\recycle.bin\moose.txt)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
| #else  // !defined(_WIN32) | ||||
|   { | ||||
|     auto dir = "/recycle.bin"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = "/recycle.bin/moose.txt)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     auto dir = L"/recycle.bin"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|  | ||||
|     dir = L"/recycle.bin/moose.txt)"; | ||||
|     EXPECT_FALSE(utils::path::contains_trash_directory(dir)); | ||||
|   } | ||||
| #endif // defined(_WIN32) | ||||
| } | ||||
| } // namespace repertory | ||||
|   | ||||
| @@ -19,9 +19,7 @@ | ||||
|   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|   SOFTWARE. | ||||
| */ | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| #include "utils/string.hpp" | ||||
| #include "test.hpp" | ||||
|  | ||||
| namespace repertory { | ||||
| TEST(utils_string, begins_with) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user