build: remove explicit iconv dependency in libtransmission (#4565)

This commit is contained in:
Charles Kerr 2023-01-08 22:21:31 -06:00 committed by GitHub
parent 72b6ae0378
commit 8b1290c895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 23 additions and 132 deletions

View File

@ -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)

View File

@ -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 = (

View File

@ -220,7 +220,6 @@ target_compile_definitions(${TR_NAME}
$<$<VERSION_LESS:${MINIUPNPC_VERSION},1.7>:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}> # API version macro was only added in 1.7
$<$<BOOL:${USE_SYSTEM_B64}>:USE_SYSTEM_B64>
$<$<BOOL:${HAVE_SO_REUSEPORT}>:HAVE_SO_REUSEPORT=1>
$<$<BOOL:${Iconv_FOUND}>:HAVE_ICONV>
PUBLIC
$<$<NOT:$<BOOL:${ENABLE_NLS}>>:DISABLE_GETTEXT>)
@ -285,7 +284,6 @@ target_link_libraries(${TR_NAME}
utf8::cpp
wildmat
WideInteger::WideInteger
$<$<BOOL:${ICONV_FOUND}>:Iconv::Iconv>
$<$<BOOL:${WIN32}>:crypt32>
$<$<BOOL:${WIN32}>:shlwapi>
"$<$<BOOL:${APPLE}>:-framework Foundation>"

View File

@ -321,11 +321,11 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
}
else if (pathIs(CommentKey) || pathIs(CommentUtf8Key))
{
tm_.comment_ = tr_strvUtf8Clean(value);
tm_.comment_ = tr_strv_replace_invalid(value);
}
else if (pathIs(CreatedByKey) || pathIs(CreatedByUtf8Key))
{
tm_.creator_ = tr_strvUtf8Clean(value);
tm_.creator_ = tr_strv_replace_invalid(value);
}
else if (pathIs(SourceKey) || pathIs(InfoKey, SourceKey) || pathIs(PublisherKey) || pathIs(InfoKey, PublisherKey))
{
@ -333,7 +333,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
// to have the same use as the 'source' key
// http://wiki.bitcomet.com/inside_bitcomet
tm_.source_ = tr_strvUtf8Clean(value);
tm_.source_ = tr_strv_replace_invalid(value);
}
else if (pathIs(AnnounceKey))
{
@ -349,7 +349,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
}
else if (pathIs(InfoKey, NameKey) || pathIs(InfoKey, NameUtf8Key))
{
tm_.name_ = tr_strvUtf8Clean(value);
tm_.name_ = tr_strv_replace_invalid(value);
}
else if (pathIs(InfoKey, PiecesKey))
{

View File

@ -19,7 +19,6 @@
#include <set>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#ifdef _WIN32
@ -33,10 +32,6 @@
#include <sys/stat.h> // mode_t
#endif
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#define UTF_CPP_CPLUSPLUS 201703L
#include <utf8.h>
@ -303,106 +298,13 @@ double tr_getRatio(uint64_t numerator, uint64_t denominator)
****
***/
namespace
{
namespace tr_strvUtf8Clean_impl
{
template<std::size_t N, typename F>
struct ArgTypeImpl;
template<std::size_t N, typename R, typename... ArgTs>
struct ArgTypeImpl<N, R (*)(ArgTs...)> : std::tuple_element<1, std::tuple<ArgTs...>>
{
};
template<std::size_t N, typename F>
using ArgType = typename ArgTypeImpl<N, F>::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<char>{};
buf.resize(buflen);
auto constexpr Encodings = std::array<char const*, 2>{ "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<ArgType<1, decltype(&iconv)>>(&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)

View File

@ -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 /*<2A>*/);
/**
* @brief copies `src` into `buf`.

View File

@ -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<char>{};
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));
}
}