Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions include/boost/mqtt5/detail/utf8_mqtt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ enum class validation_result : uint8_t {
invalid
};

inline bool is_continuation_byte(char c) {
return (c & 0xC0) == 0x80;
}

inline int pop_front_unichar(std::string_view& s) {
// assuming that s.length() is > 0

Expand All @@ -31,26 +35,44 @@ inline int pop_front_unichar(std::string_view& s) {
ch = s[0];
s.remove_prefix(1);
}
else if ((n == 0xC0 || n == 0xD0) && s.size() > 1) {
ch = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
s.remove_prefix(2);
else if (
(n == 0xC0 || n == 0xD0) && s.size() > 1 &&
is_continuation_byte(s[1])
) {
int decoded = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
if (decoded >= 0x80) {
ch = decoded;
s.remove_prefix(2);
}
}
else if ((n == 0xE0) && s.size() > 2) {
ch = ((s[0] & 0x1F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
s.remove_prefix(3);
else if (
(n == 0xE0) && s.size() > 2 &&
is_continuation_byte(s[1]) && is_continuation_byte(s[2])
) {
int decoded = ((s[0] & 0x1F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
if (decoded >= 0x800) {
ch = decoded;
s.remove_prefix(3);
}
}
else if ((n == 0xF0) && s.size() > 3) {
ch = ((s[0] & 0x1F) << 18) | ((s[1] & 0x3F) << 12) |
else if (
(n == 0xF0) && s.size() > 3 &&
is_continuation_byte(s[1]) && is_continuation_byte(s[2]) &&
is_continuation_byte(s[3])
) {
int decoded = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) |
((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
s.remove_prefix(4);
if (decoded >= 0x10000) {
ch = decoded;
s.remove_prefix(4);
}
}

return ch;
}

inline validation_result validate_mqtt_utf8_char(int c) {
constexpr int fe_flag = 0xFE;
constexpr int ff_flag = 0xFF;
constexpr int noncharacter_flag = 0xFFFE;

constexpr int multi_lvl_wildcard = '#';
constexpr int single_lvl_wildcard = '+';
Expand All @@ -62,8 +84,7 @@ inline validation_result validate_mqtt_utf8_char(int c) {
(c < 0x007F || c > 0x009F) && // U+007F...0+009F control characters
(c < 0xD800 || c > 0xDFFF) && // U+D800...U+DFFF surrogates
(c < 0xFDD0 || c > 0xFDEF) && // U+FDD0...U+FDEF non-characters
(c & fe_flag) != fe_flag && // non-characters
(c & ff_flag) != ff_flag
(c & noncharacter_flag) != noncharacter_flag // non-characters
)
return validation_result::valid;

Expand Down
12 changes: 12 additions & 0 deletions test/unit/string_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ BOOST_AUTO_TEST_CASE(utf8_string_validation) {
BOOST_CHECK(validate_mqtt_utf8(to_str(0xFDF0)) == validation_result::valid);
BOOST_CHECK(validate_mqtt_utf8(to_str(0x1FFFE)) == validation_result::invalid);
BOOST_CHECK(validate_mqtt_utf8(to_str(0x1FFFF)) == validation_result::invalid);

BOOST_CHECK(validate_mqtt_utf8(to_str(0xFE)) == validation_result::valid);
BOOST_CHECK(validate_mqtt_utf8(to_str(0xFF)) == validation_result::valid);
BOOST_CHECK(validate_mqtt_utf8(to_str(0x1F5FE)) == validation_result::valid);

BOOST_CHECK(validate_mqtt_utf8("\xC3z") == validation_result::invalid);
BOOST_CHECK(validate_mqtt_utf8("\xE2\x28\xA1") == validation_result::invalid);
BOOST_CHECK(validate_mqtt_utf8("\xF0\x28\x8C\xBC") == validation_result::invalid);

BOOST_CHECK(validate_mqtt_utf8("\xC1\x81") == validation_result::invalid);
BOOST_CHECK(validate_mqtt_utf8("\xE0\x81\x81") == validation_result::invalid);
BOOST_CHECK(validate_mqtt_utf8("\xF0\x80\x81\x81") == validation_result::invalid);
}

BOOST_AUTO_TEST_CASE(topic_filter_validation) {
Expand Down
Loading