forked from boostorg/mqtt5
Summary: related to T13767 T13767 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Differential Revision: https://repo.mireo.local/D29686
119 lines
3.1 KiB
C++
119 lines
3.1 KiB
C++
//
|
|
// Copyright (c) 2023-2024 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
#ifndef ASYNC_MQTT5_TOPIC_VALIDATION_HPP
|
|
#define ASYNC_MQTT5_TOPIC_VALIDATION_HPP
|
|
|
|
#include <string>
|
|
|
|
#include <async_mqtt5/detail/utf8_mqtt.hpp>
|
|
|
|
namespace async_mqtt5::detail {
|
|
|
|
static constexpr uint32_t min_subscription_identifier = 1;
|
|
static constexpr uint32_t max_subscription_identifier = 268'435'455;
|
|
|
|
static constexpr std::string_view shared_sub_prefix = "$share/";
|
|
|
|
inline bool is_utf8_no_wildcard(validation_result result) {
|
|
return result == validation_result::valid;
|
|
}
|
|
|
|
inline bool is_not_empty(size_t sz) {
|
|
return sz != 0;
|
|
}
|
|
|
|
inline bool is_valid_topic_size(size_t sz) {
|
|
return is_not_empty(sz) && is_valid_string_size(sz);
|
|
}
|
|
|
|
inline validation_result validate_topic_name(std::string_view str) {
|
|
return validate_impl(str, is_valid_topic_size, is_utf8_no_wildcard);
|
|
}
|
|
|
|
inline validation_result validate_topic_alias_name(std::string_view str) {
|
|
return validate_impl(str, is_valid_string_size, is_utf8_no_wildcard);
|
|
}
|
|
|
|
inline validation_result validate_shared_topic_name(std::string_view str) {
|
|
return validate_impl(str, is_not_empty, is_utf8_no_wildcard);
|
|
}
|
|
|
|
inline validation_result validate_topic_filter(std::string_view str) {
|
|
if (!is_valid_topic_size(str.size()))
|
|
return validation_result::invalid;
|
|
|
|
constexpr int multi_lvl_wildcard = '#';
|
|
constexpr int single_lvl_wildcard = '+';
|
|
|
|
// must be the last character preceded by '/' or stand alone
|
|
// #, .../#
|
|
if (str.back() == multi_lvl_wildcard) {
|
|
str.remove_suffix(1);
|
|
|
|
if (!str.empty() && str.back() != '/')
|
|
return validation_result::invalid;
|
|
}
|
|
|
|
int last_c = -1;
|
|
validation_result result;
|
|
while (!str.empty()) {
|
|
int c = pop_front_unichar(str);
|
|
|
|
// can be used at any level, but must occupy an entire level
|
|
// +, +/..., .../+/..., .../+
|
|
bool is_valid_single_lvl = (c == single_lvl_wildcard) &&
|
|
(str.empty() || str.front() == '/') &&
|
|
(last_c == -1 || last_c == '/');
|
|
|
|
result = validate_mqtt_utf8_char(c);
|
|
if (
|
|
result == validation_result::valid ||
|
|
is_valid_single_lvl
|
|
) {
|
|
last_c = c;
|
|
continue;
|
|
}
|
|
|
|
return validation_result::invalid;
|
|
}
|
|
|
|
return validation_result::valid;
|
|
}
|
|
|
|
inline validation_result validate_shared_topic_filter(
|
|
std::string_view str, bool wildcard_allowed = true
|
|
) {
|
|
if (!is_valid_topic_size(str.size()))
|
|
return validation_result::invalid;
|
|
|
|
if (str.compare(0, shared_sub_prefix.size(), shared_sub_prefix) != 0)
|
|
return validation_result::invalid;
|
|
|
|
str.remove_prefix(shared_sub_prefix.size());
|
|
|
|
size_t share_name_end = str.find_first_of('/');
|
|
if (share_name_end == std::string::npos)
|
|
return validation_result::invalid;
|
|
|
|
validation_result result;
|
|
result = validate_shared_topic_name(str.substr(0, share_name_end));
|
|
|
|
if (result != validation_result::valid)
|
|
return validation_result::invalid;
|
|
|
|
auto topic_filter = str.substr(share_name_end + 1);
|
|
return wildcard_allowed ?
|
|
validate_topic_filter(topic_filter) :
|
|
validate_topic_name(topic_filter)
|
|
;
|
|
}
|
|
|
|
} // end namespace async_mqtt5::detail
|
|
|
|
#endif //ASYNC_MQTT5_TOPIC_VALIDATION_HPP
|