From 8b1290c895e7cc64efa2cac05fc2f9bd93d3427c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 8 Jan 2023 22:21:31 -0600 Subject: [PATCH] build: remove explicit iconv dependency in libtransmission (#4565) --- CMakeLists.txt | 6 -- Transmission.xcodeproj/project.pbxproj | 3 - libtransmission/CMakeLists.txt | 2 - libtransmission/torrent-metainfo.cc | 8 +- libtransmission/utils.cc | 102 +------------------------ libtransmission/utils.h | 2 +- tests/libtransmission/utils-test.cc | 32 ++++---- 7 files changed, 23 insertions(+), 132 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2220b2034..d9a5ea58b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,12 +206,6 @@ if(NOT TARGET CURL::libcurl) target_include_directories(CURL::libcurl INTERFACE ${CURL_INCLUDE_DIRS}) endif() -if(NOT WIN32) - find_package(Iconv) -else() - set(Iconv_FOUND OFF) -endif() - set(CRYPTO_PKG "") if(WITH_CRYPTO STREQUAL "AUTO" OR WITH_CRYPTO STREQUAL "ccrypto") tr_get_required_flag(WITH_CRYPTO CCRYPTO_IS_REQUIRED) diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj index 1a65d01bd..ed6920c80 100644 --- a/Transmission.xcodeproj/project.pbxproj +++ b/Transmission.xcodeproj/project.pbxproj @@ -3728,7 +3728,6 @@ "-DWIDE_INTEGER_DISABLE_IOSTREAM", "-DHAVE_FLOCK", "-DHAVE_STRLCPY", - "-DHAVE_ICONV", ); PRODUCT_NAME = transmission; SYSTEM_HEADER_SEARCH_PATHS = ( @@ -3973,7 +3972,6 @@ "-DWIDE_INTEGER_DISABLE_IOSTREAM", "-DHAVE_FLOCK", "-DHAVE_STRLCPY", - "-DHAVE_ICONV", ); PRODUCT_NAME = transmission; SYSTEM_HEADER_SEARCH_PATHS = ( @@ -4290,7 +4288,6 @@ "-DWIDE_INTEGER_DISABLE_IOSTREAM", "-DHAVE_FLOCK", "-DHAVE_STRLCPY", - "-DHAVE_ICONV", ); PRODUCT_NAME = transmission; SYSTEM_HEADER_SEARCH_PATHS = ( diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index 9d462362e..93dbe9b86 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -220,7 +220,6 @@ target_compile_definitions(${TR_NAME} $<$:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}> # API version macro was only added in 1.7 $<$:USE_SYSTEM_B64> $<$:HAVE_SO_REUSEPORT=1> - $<$:HAVE_ICONV> PUBLIC $<$>:DISABLE_GETTEXT>) @@ -285,7 +284,6 @@ target_link_libraries(${TR_NAME} utf8::cpp wildmat WideInteger::WideInteger - $<$:Iconv::Iconv> $<$:crypt32> $<$:shlwapi> "$<$:-framework Foundation>" diff --git a/libtransmission/torrent-metainfo.cc b/libtransmission/torrent-metainfo.cc index 74eb60706..beac560ba 100644 --- a/libtransmission/torrent-metainfo.cc +++ b/libtransmission/torrent-metainfo.cc @@ -321,11 +321,11 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler #include #include -#include #include #ifdef _WIN32 @@ -33,10 +32,6 @@ #include // mode_t #endif -#ifdef HAVE_ICONV -#include -#endif - #define UTF_CPP_CPLUSPLUS 201703L #include @@ -303,106 +298,13 @@ double tr_getRatio(uint64_t numerator, uint64_t denominator) **** ***/ -namespace -{ -namespace tr_strvUtf8Clean_impl -{ - -template -struct ArgTypeImpl; - -template -struct ArgTypeImpl : std::tuple_element<1, std::tuple> -{ -}; - -template -using ArgType = typename ArgTypeImpl::type; - -bool validateUtf8(std::string_view sv, char const** good_end) -{ - auto const* begin = std::data(sv); - auto const* const end = begin + std::size(sv); - auto const* walk = begin; - auto all_good = false; - - try - { - while (walk < end) - { - utf8::next(walk, end); - } - - all_good = true; - } - catch (utf8::exception const&) - { - all_good = false; - } - - if (good_end != nullptr) - { - *good_end = walk; - } - - return all_good; -} - -std::string strip_non_utf8(std::string_view sv) +std::string tr_strv_replace_invalid(std::string_view sv, uint32_t replacement) { auto out = std::string{}; - utf8::unchecked::replace_invalid(std::data(sv), std::data(sv) + std::size(sv), std::back_inserter(out), '?'); + utf8::unchecked::replace_invalid(std::data(sv), std::data(sv) + std::size(sv), std::back_inserter(out), replacement); return out; } -std::string to_utf8(std::string_view sv) -{ -#ifdef HAVE_ICONV - size_t const buflen = std::size(sv) * 4 + 10; - auto buf = std::vector{}; - buf.resize(buflen); - - auto constexpr Encodings = std::array{ "CURRENT", "ISO-8859-15" }; - for (auto const* test_encoding : Encodings) - { - iconv_t cd = iconv_open("UTF-8", test_encoding); - if (cd == (iconv_t)-1) // NOLINT(performance-no-int-to-ptr) - { - continue; - } - - auto const* inbuf = std::data(sv); - size_t inbytesleft = std::size(sv); - char* out = std::data(buf); - size_t outbytesleft = std::size(buf); - auto const rv = iconv(cd, const_cast>(&inbuf), &inbytesleft, &out, &outbytesleft); - iconv_close(cd); - if (rv != size_t(-1)) - { - return std::string{ std::data(buf), buflen - outbytesleft }; - } - } - -#endif - - return strip_non_utf8(sv); -} - -} // namespace tr_strvUtf8Clean_impl -} // namespace - -std::string tr_strvUtf8Clean(std::string_view cleanme) -{ - using namespace tr_strvUtf8Clean_impl; - - if (validateUtf8(cleanme, nullptr)) - { - return std::string{ cleanme }; - } - - return to_utf8(cleanme); -} - #ifdef _WIN32 std::string tr_win32_native_to_utf8(std::wstring_view in) diff --git a/libtransmission/utils.h b/libtransmission/utils.h index e735f8cf0..554b6ba69 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -204,7 +204,7 @@ constexpr bool tr_strvSep(std::string_view* sv, std::string_view* token, char de [[nodiscard]] std::string_view tr_strvStrip(std::string_view str); -[[nodiscard]] std::string tr_strvUtf8Clean(std::string_view cleanme); +[[nodiscard]] std::string tr_strv_replace_invalid(std::string_view cleanme, uint32_t replacement = 0xFFFD /*�*/); /** * @brief copies `src` into `buf`. diff --git a/tests/libtransmission/utils-test.cc b/tests/libtransmission/utils-test.cc index ca6e0f6ae..1f7097f33 100644 --- a/tests/libtransmission/utils-test.cc +++ b/tests/libtransmission/utils-test.cc @@ -113,53 +113,53 @@ TEST_F(UtilsTest, trStrvStrip) EXPECT_EQ("test"sv, tr_strvStrip("test"sv)); } -TEST_F(UtilsTest, trStrvUtf8Clean) +TEST_F(UtilsTest, strvReplaceInvalid) { auto in = "hello world"sv; - auto out = tr_strvUtf8Clean(in); + auto out = tr_strv_replace_invalid(in); EXPECT_EQ(in, out); in = "hello world"sv; - out = tr_strvUtf8Clean(in.substr(0, 5)); + out = tr_strv_replace_invalid(in.substr(0, 5)); EXPECT_EQ("hello"sv, out); // this version is not utf-8 (but cp866) in = "\x92\xE0\xE3\xA4\xAD\xAE \xA1\xEB\xE2\xEC \x81\xAE\xA3\xAE\xAC"sv; - out = tr_strvUtf8Clean(in); - EXPECT_TRUE(std::size(out) == 17 || std::size(out) == 33); - EXPECT_EQ(out, tr_strvUtf8Clean(out)); + out = tr_strv_replace_invalid(in, '?'); + EXPECT_EQ(17U, std::size(out)); + EXPECT_EQ(out, tr_strv_replace_invalid(out)); // same string, but utf-8 clean in = "Трудно быть Богом"sv; - out = tr_strvUtf8Clean(in); + out = tr_strv_replace_invalid(in); EXPECT_NE(0U, std::size(out)); - EXPECT_EQ(out, tr_strvUtf8Clean(out)); + EXPECT_EQ(out, tr_strv_replace_invalid(out)); EXPECT_EQ(in, out); // https://trac.transmissionbt.com/ticket/6064 // This was a fuzzer-generated string that crashed Transmission. // Even invalid strings shouldn't cause a crash. in = "\xF4\x00\x81\x82"sv; - out = tr_strvUtf8Clean(in); + out = tr_strv_replace_invalid(in); EXPECT_NE(0U, std::size(out)); - EXPECT_EQ(out, tr_strvUtf8Clean(out)); + EXPECT_EQ(out, tr_strv_replace_invalid(out)); in = "\xF4\x33\x81\x82"sv; - out = tr_strvUtf8Clean(in); + out = tr_strv_replace_invalid(in, '?'); EXPECT_NE(nullptr, out.data()); - EXPECT_TRUE(out.size() == 4 || out.size() == 7); - EXPECT_EQ(out, tr_strvUtf8Clean(out)); + EXPECT_EQ(4U, std::size(out)); + EXPECT_EQ(out, tr_strv_replace_invalid(out)); } -TEST_F(UtilsTest, trStrvUtf8CleanFuzz) +TEST_F(UtilsTest, strvReplaceInvalidFuzz) { auto buf = std::vector{}; for (size_t i = 0; i < 1000; ++i) { buf.resize(tr_rand_int(4096U)); tr_rand_buffer(std::data(buf), std::size(buf)); - auto const out = tr_strvUtf8Clean({ std::data(buf), std::size(buf) }); - EXPECT_EQ(out, tr_strvUtf8Clean(out)); + auto const out = tr_strv_replace_invalid({ std::data(buf), std::size(buf) }); + EXPECT_EQ(out, tr_strv_replace_invalid(out)); } }