41 TRACE(
"Executing is_ipv4_addr()")
42 std::regex ipv4_pattern(
43 R
"(^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$)");
44 return std::regex_match(ip, ipv4_pattern);
63 TRACE(
"Executing is_dns_name()")
64 std::regex pattern(
"^((?!-)[A-Za-z0-9-]{1,63}\\.)+[A-Za-z]{2,6}$");
65 return (std::regex_match(label, pattern) && !label.contains(
"-.") &&
66 !label.contains(
"--") && !label.ends_with(
'-')) ||
76 TRACE(
"Executing UPnPsdk::remove_dot_segments(\"" + a_path +
"\")")
78 std::string_view path_sv{a_path};
81 while (!path_sv.empty()) {
83 if (path_sv.substr(0, 3) ==
"../") {
84 path_sv.remove_prefix(3);
85 }
else if (path_sv.substr(0, 2) ==
"./") {
86 path_sv.remove_prefix(2);
89 else if (path_sv.substr(0, 3) ==
"/./") {
90 path_sv.remove_prefix(2);
91 }
else if (path_sv ==
"/.") {
92 path_sv.remove_suffix(1);
95 else if (path_sv.substr(0, 4) ==
"/../") {
96 path_sv.remove_prefix(3);
97 if (!output.empty()) {
99 output.erase((pos = output.find_last_of(
'/')) ==
104 }
else if (path_sv ==
"/..") {
105 path_sv.remove_suffix(2);
106 if (!output.empty()) {
108 output.erase((pos = output.find_last_of(
'/')) ==
115 else if (path_sv ==
"." || path_sv ==
"..") {
120 size_t start = path_sv.front() ==
'/' ? 1 : 0;
121 size_t end = path_sv.find_first_of(
'/', start);
122 if (end == std::string_view::npos)
123 end = path_sv.size();
124 output += path_sv.substr(0, end);
125 path_sv.remove_prefix(end);
134 TRACE(
"Executing decode_esc_chars()")
139 auto it{a_encoded.begin()};
140 auto it_end{a_encoded.end()};
144 if (!a_encoded.empty() &&
145 ( (a_encoded.size() < 3 && *it ==
'%') ||
146 (a_encoded.size() >= 3 && (*(it_end - 2) ==
'%' || *(it_end - 1) ==
'%'))))
150 for (; it < it_end; it++) {
151 if (
static_cast<unsigned char>(*it) ==
'%') {
153 if (!std::isxdigit(
static_cast<unsigned char>(*(it + 1))) ||
154 !std::isxdigit(
static_cast<unsigned char>(*(it + 2))))
158 stoi(a_encoded.substr(
159 static_cast<size_t>(it - a_encoded.begin()) + 1, 2),
161 decoded.push_back(
static_cast<char>(value));
164 decoded.push_back(*it);
172 throw std::invalid_argument(
174 "MSG1159")
"URI with invalid percent encoding, failed URI=\"" +
175 a_encoded +
"\".\n");
182 TRACE2(
this,
" Executing CComponent::state()")
187 TRACE2(
this,
" Executing CComponent::str()")
207 TRACE(
"Executing get_scheme(a_uri_sv)")
210 if ((pos = a_uri_sv.find_first_of(
':')) == std::string_view::npos)
219 return std::string_view(a_uri_sv.data(), 1);
223 a_uri_sv.remove_suffix(a_uri_sv.size() - pos - 1);
226 if (!std::isalpha(
static_cast<unsigned char>(a_uri_sv.front()))) {
231 for (
auto it{a_uri_sv.begin()}; it < a_uri_sv.end() - 1; it++) {
232 ch =
static_cast<unsigned char>(*it);
233 if (!(std::isalnum(ch) || (ch ==
'-') || (ch ==
'+') ||
249 TRACE2(
this,
" Construct CScheme(a_uri_sv)")
251 std::string_view scheme_sv = get_scheme(a_uri_sv);
253 if (scheme_sv.empty())
256 if (scheme_sv ==
":") {
263 scheme_sv.remove_suffix(1);
266 *it =
static_cast<char>(std::tolower(
static_cast<unsigned char>(*it)));
297 TRACE(
"Executing get_authority(a_uriref_sv)")
299 auto& npos = std::string_view::npos;
301 if ((pos = a_uriref_sv.find(
"//")) != npos) {
303 a_uriref_sv.remove_prefix(pos);
305 if ((pos = a_uriref_sv.find_first_of(
"/?#", 2)) != npos)
306 a_uriref_sv.remove_suffix(a_uriref_sv.size() - pos);
342 TRACE(
"Executing get_userinfo(a_uriref_sv)")
346 if (authority_sv.empty())
350 authority_sv.remove_prefix(2);
352 auto& npos = std::string_view::npos;
355 if ((pos = authority_sv.find_first_of(
'@')) == npos)
359 if (pos == 0 || authority_sv[0] ==
':') {
363 return std::string_view(authority_sv.data(), 1);
367 authority_sv.remove_suffix(authority_sv.size() - pos - 1);
370 if ((pos = authority_sv.find_first_of(
':')) != npos) {
373 authority_sv.remove_suffix(authority_sv.size() - pos - 1);
376 authority_sv.remove_suffix(1);
385 TRACE2(
this,
" Construct CUserinfo(a_uriref_sv)")
387 std::string_view userinfo_sv = get_userinfo(a_uriref_sv);
389 if (userinfo_sv.empty())
392 if (userinfo_sv ==
"@" || userinfo_sv ==
":") {
424 TRACE(
"Executing get_host(a_uriref_sv)")
428 if (authority_sv.empty())
434 std::string_view end_separator = authority_sv.substr(0, 1);
435 authority_sv.remove_prefix(2);
437 auto& npos = std::string_view::npos;
440 if ((pos = authority_sv.find_first_of(
'@')) != npos)
441 authority_sv.remove_prefix(pos + 1);
445 if ((pos = authority_sv.find_last_of(
"]:")) != npos &&
446 authority_sv[pos] ==
':')
447 authority_sv.remove_suffix(authority_sv.size() - pos);
450 if (authority_sv.empty()) {
454 return end_separator;
459 if (authority_sv.front() ==
'[') {
462 saObj = authority_sv;
463 }
catch (
const std::exception&) {
475 std::string host_str{authority_sv};
484 throw std::invalid_argument(
486 "MSG1160")
"invalid host address or host name on URI=\"" +
487 std::string(a_uriref_sv) +
"\".");
495 TRACE2(
this,
" Construct CHost(a_uriref_sv)")
497 std::string_view host_sv = get_host(a_uriref_sv);
502 if (host_sv.size() == 1 &&
503 host_sv.find_first_of(
":/?#") != std::string_view::npos) {
511 *it =
static_cast<char>(std::tolower(
static_cast<unsigned char>(*it)));
540 TRACE(
"Executing get_port(a_uriref_sv)")
544 if (authority_sv.empty())
548 authority_sv.remove_prefix(2);
550 auto& npos = std::string_view::npos;
554 if ((pos = authority_sv.find_first_of(
'@')) != npos)
555 authority_sv.remove_prefix(pos + 1);
559 if ((pos = authority_sv.find_last_of(
"]:")) == npos ||
560 authority_sv[pos] !=
':')
565 authority_sv.remove_prefix(pos);
569 if (authority_sv ==
":") {
573 authority_sv.remove_prefix(1);
576 if (
to_port(authority_sv) != 0)
577 throw std::invalid_argument(
578 UPnPsdk_LOGEXCEPT(
"MSG1164")
"Invalid port number. Failed URI=\"" +
579 std::string(a_uriref_sv) +
"\".\n");
589 TRACE2(
this,
" Construct CPort(a_uriref_sv)")
591 std::string_view port_sv = get_port(a_uriref_sv);
596 if (port_sv ==
":") {
604 if (port_sv ==
"80") {
605 if (get_scheme(a_uriref_sv) ==
"http:") {
609 }
else if (port_sv ==
"443") {
610 if (get_scheme(a_uriref_sv) ==
"https:") {
624 : userinfo(a_uri_sv), host(a_uri_sv), port(a_uri_sv) {
625 TRACE2(
this,
" Construct CAuthority(a_uri_sv)");
629 TRACE2(
this,
" Executing CAuthority::state()")
630 if (this->
host.
state() == STATE::avail ||
631 this->userinfo.state() == STATE::avail ||
632 this->port.state() == STATE::avail)
635 if (this->
host.
state() == STATE::undef &&
636 this->userinfo.state() == STATE::undef &&
637 this->port.state() == STATE::undef)
644 TRACE2(
this,
" Executing CAuthority::str()")
649 (this->
port.
state() == STATE::avail ?
":" :
"") +
682 TRACE(
"Executing get_path(a_uriref_sv)")
684 auto& npos = std::string_view::npos;
689 if ((pos = a_uriref_sv.find_first_of(
"?#")) != npos)
690 a_uriref_sv.remove_suffix(a_uriref_sv.size() - pos);
693 if ((pos =
get_scheme(a_uriref_sv).size()) != 0)
694 a_uriref_sv.remove_prefix(pos);
698 a_uriref_sv.remove_prefix(pos);
711 TRACE2(
this,
" Construct CPath(a_uriref_sv)")
713 std::string_view path_sv = get_path(a_uriref_sv);
716 if (path_sv.empty()) {
754 TRACE(
"Executing get_query(a_uriref_sv)")
757 auto& npos = std::string_view::npos;
759 if ((pos = a_uriref_sv.find_first_of(
"?#")) == npos ||
760 a_uriref_sv[pos] ==
'#')
765 a_uriref_sv.remove_prefix(pos);
767 if ((pos = a_uriref_sv.find_first_of(
'#')) != npos)
768 a_uriref_sv.remove_suffix(a_uriref_sv.size() - pos);
770 if (a_uriref_sv ==
"?")
775 a_uriref_sv.remove_prefix(1);
784 TRACE2(
this,
" Construct CQuery(a_uriref_sv)")
786 std::string_view query_sv = get_query(a_uriref_sv);
788 if (query_sv.empty())
791 if (query_sv ==
"?") {
820 TRACE(
"Executing get_fragment(a_uriref_sv)")
823 auto& npos = std::string_view::npos;
825 if ((pos = a_uriref_sv.find_first_of(
'#')) == npos)
830 a_uriref_sv.remove_prefix(pos);
832 if (a_uriref_sv ==
"#")
837 a_uriref_sv.remove_prefix(1);
846 TRACE2(
this,
" Construct CFragment(a_uriref_sv)")
848 std::string_view fragment_sv = get_fragment(a_uriref_sv);
850 if (fragment_sv.empty())
853 if (fragment_sv ==
"#") {
867 TRACE2(
this,
" Construct CPrepUriStr(a_uriref_str)")
869 auto it{a_uriref_str.begin()};
870 auto it_end{a_uriref_str.end()};
873 if (!a_uriref_str.empty() &&
874 ( (a_uriref_str.size() < 3 && *it ==
'%') ||
875 (a_uriref_str.size() >= 3 && (*(it_end - 2) ==
'%' || *(it_end - 1) ==
'%'))))
880 for (; it < it_end; it++) {
881 if (
static_cast<unsigned char>(*it) ==
'%') {
883 if (!std::isxdigit(
static_cast<unsigned char>(*it)))
885 *it =
static_cast<char>(
886 std::toupper(
static_cast<unsigned char>(*it)));
888 if (!std::isxdigit(
static_cast<unsigned char>(*it)))
890 *it =
static_cast<char>(
891 std::toupper(
static_cast<unsigned char>(*it)));
897 throw std::invalid_argument(
899 "MSG1165")
"URI with invalid percent encoding, failed URI=\"" +
900 a_uriref_str +
"\".\n");
907 : prepare_uri_str(a_uriref_str), scheme(a_uriref_str),
908 authority(a_uriref_str), path(a_uriref_str), query(a_uriref_str),
909 fragment(a_uriref_str) {
910 TRACE2(
this,
" Construct CUriRef(a_uriref_str)");
916 if (this->
scheme.
str() ==
"http" || this->scheme.str() ==
"https") {
918 throw std::invalid_argument(
919 UPnPsdk_LOGEXCEPT(
"MSG1168")
"Scheme \"" + this->
scheme.
str() +
920 "\" must have a host. Invalid URI=\"" + a_uriref_str +
"\"\n");
922 }
else if (this->
scheme.
str() ==
"file") {
936 this->authority.port.state() == STATE::undef &&
937 this->path.state() != STATE::empty &&
938 this->query.state() == STATE::undef &&
939 this->fragment.state() == STATE::undef) {
943 throw std::invalid_argument(
944 UPnPsdk_LOGEXCEPT(
"MSG1169")
"Invalid URI=\"" +
945 std::string(a_uriref_str) +
"\"\n");
951 TRACE2(
this,
" Executing CUriRf::state()")
954 this->authority.state() == STATE::avail ||
955 this->path.state() == STATE::avail ||
956 this->query.state() == STATE::avail ||
957 this->fragment.state() == STATE::avail)
961 this->authority.state() == STATE::undef &&
962 this->path.state() == STATE::undef &&
963 this->query.state() == STATE::undef &&
964 this->fragment.state() == STATE::undef)
971 TRACE2(
this,
" Executing CUriRef::str()")
1007 std::string path_str;
1010 path_str =
'/' + a_rel.
path.
str();
1013 if ((pos = a_base.
path.
str().find_last_of(
'/')) == std::string::npos)
1016 path_str = a_base.
path.
str().substr(0, pos);
1017 path_str +=
'/' + a_rel.
path.
str();
1019 a_path =
CPath(path_str);
1024CUri::CUri(std::string a_uriref_str) : base(a_uriref_str), target(
"") {
1025 TRACE2(
this,
" Construct CUri(a_uriref_str)")
1030 throw std::invalid_argument(
1032 "MSG1170")
"Only base URI accepted. Invalid URI=\"" +
1033 a_uriref_str +
"\"\n");
1038 TRACE2(
this,
" Executing CUri::operator=(a_relref_str)")
1044 throw std::invalid_argument(
1046 "MSG1171")
"Only relative reference accepted. Invalid URI=\"" +
1047 a_relref_str +
"\"\n");
1053 auto& baseObj = this->
base;
1054 auto& targetObj = this->
target;
1059 targetObj.path = relObj.
path;
1061 targetObj.query = relObj.
query;
1065 targetObj.path = relObj.
path;
1067 targetObj.query = relObj.
query;
1070 targetObj.path = baseObj.path;
1071 targetObj.path.remove_dot_segments();
1073 targetObj.query = relObj.
query;
1075 targetObj.query = baseObj.query;
1077 if (relObj.
path.
str().front() ==
'/') {
1078 targetObj.path = relObj.
path;
1081 merge_paths(targetObj.path, baseObj, relObj);
1082 targetObj.path.remove_dot_segments();
1084 targetObj.query = relObj.
query;
1086 targetObj.authority = baseObj.authority;
1088 targetObj.scheme = baseObj.scheme;
1090 targetObj.fragment = relObj.
fragment;
1116 std::string_view uriref_sv = std::string_view(in, max);
1123 std::cerr <<
"DEBUG: scheme=\"" << uriObj.
scheme.
str() <<
"\"\n";
1124 std::cerr <<
"DEBUG: authority=\"" << uriObj.authority.str() <<
"\"\n";
1125 std::cerr <<
"DEBUG: path=\"" << uriObj.path.str() <<
"\"\n";
1126 std::cerr <<
"DEBUG: query=\"" << uriObj.query.str() <<
"\"\n";
1127 std::cerr <<
"DEBUG: fragment=\"" << uriObj.fragment.str() <<
"\"\n";
1129 ::memset(out, 0,
sizeof(*out));
1132 if (uriObj.scheme.state() == STATE::avail) {
1140 if (uriObj.path.state() == STATE::avail &&
1141 uriObj.path.str().front() ==
'/')
1145 if (uriObj.scheme.state() == STATE::avail) {
1146 std::string_view scheme_sv = UPnPsdk::get_scheme(uriref_sv);
1147 scheme_sv.remove_suffix(1);
1153 if (uriObj.authority.host.state() == STATE::avail) {
1156 std::string_view host_sv = UPnPsdk::get_host(uriref_sv);
1163 if (uriObj.authority.port.state() == STATE::avail)
1166 uriObj.authority.port.str().size();
1174 std::string port_str;
1175 if (uriObj.authority.port.state() == STATE::avail) {
1176 port_str = uriObj.authority.port.str();
1178 if (uriObj.scheme.state() == STATE::avail) {
1179 if (uriObj.scheme.str() ==
"https")
1181 else if (uriObj.scheme.str() ==
"http")
1188 throw std::invalid_argument(
1190 "MSG1155")
"Host not found. Failed URI=\"" +
1191 std::string(uriref_sv) +
"\"\n");
1194 *
reinterpret_cast<sockaddr_storage*
>(ai->ai_addr);
1198 if (uriObj.path.state() == STATE::avail) {
1199 std::string_view path_sv = UPnPsdk::get_path(uriref_sv);
1201 if (uriObj.query.state() == STATE::avail) {
1202 std::string_view query_sv = UPnPsdk::get_query(uriref_sv);
1210 if (uriObj.fragment.state() == STATE::avail) {
1211 std::string_view fragment_sv = UPnPsdk::get_fragment(uriref_sv);
1218 }
catch (
const std::invalid_argument& ex) {
1219 UPnPsdk_LOGCATCH(
"MSG1046")
"Catched next line...\n"
1220 << ex.what() <<
'\n';
Manage Uniform Resource Identifier (URI) as specified with RFC 3986.
token fragment
Member variable.
token scheme
Member variable.
@ Relative
The URI is relative, means it hasn't a 'scheme' component.
@ Absolute
The URI is absolute, means it has a 'scheme' component.
hostport_type hostport
Member variable.
size_t size
Size of the buffer.
@ ABS_PATH
The 'path' component begins with a '/'.
@ REL_PATH
The 'path' component doesn't begin with a '/'.
pathType path_type
Member variable.
token pathquery
Member variable.
sockaddr_storage IPaddress
Network socket address.
constexpr int HTTP_SUCCESS
Yet another success code.
uriType type
Member variable.
token text
Pointing to the full host:port string representation.
Represents a URI used in parse_uri and elsewhere.
Declaration of the Addrinfo class.
Get information from the operating system about an internet address.
bool get_first()
Get the first entry of an address info from the operating system.
std::string str() const
Get the string of the authority component.
CAuthority(std::string_view a_uri_sv)
Initialize the authority component.
CComponent::STATE state() const
Get state of the authority.
std::string m_component
Name of the component.
STATE state() const
Get state of the component.
STATE
Defines the possible states of a URI component.
@ avail
Component string is available, means it has a valid content.
@ empty
Component is defined but empty, component string is empty.
@ undef
component is undefined, component string is empty.
STATE m_state
Current state of the component.
const std::string & str() const
Get the string of the component.
CFragment(std::string_view a_uri_sv)
Initialize the fragment component.
CHost(std::string_view a_uri_sv)
Initialize the host subcomponent.
Path component of a URI reference.
CPath(std::string_view a_uri_sv)
Initialize the path component.
void remove_dot_segments()
UPnPsdk::remove_dot_segments() from path component.
CPort(std::string_view a_uri_sv)
Initialize the port subcomponent.
CQuery(std::string_view a_uri_sv)
Initialize the query component.
CScheme(std::string_view a_uri_sv)
Initialize the scheme component.
CComponent::STATE state() const
Get state of the URI reference.
std::string str() const
Get URI reference string.
CUriRef(std::string a_uriref_str)
Initialize the URI reference.
CUriRef target
Resulting URI of merged relative reference to the base URI.
void operator=(std::string a_relref_str)
Set a relative resource reference.
CUri(std::string a_uriref_str)
Initialize with the base URI.
CComponent::STATE state() const
Get state of the URI.
std::string str() const
Get the resulting URI string merged with the relative reference.
CUserinfo(std::string_view a_uri_sv)
Initialize the userinfo subcomponent.
int to_port(std::string_view a_port_str, in_port_t *const a_port_num=nullptr) noexcept
Free function to check if a string represents a valid port number.
int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 3986 (Uniform Resource Identifier).
UPnPsdk_VIS void remove_dot_segments(std::string &a_path)
Remove dot segments from a path.
std::string_view get_authority(std::string_view a_uriref_sv)
Separates the authority component from a URI reference.
CPrepUriStr(std::string &a_uriref_str)
Initialize the helper class.
std::string_view get_path(std::string_view a_uriref_sv)
Separates the path component from a URI reference.
std::string_view get_scheme(std::string_view a_uri_sv)
Separates the scheme component from a URI.
std::string_view get_port(std::string_view a_uriref_sv)
Separates the authority port subcomponent from a URI reference.
bool is_ipv4_addr(const std::string &ip)
Check if string is a valid IPv4 address.
std::string_view get_query(std::string_view a_uriref_sv)
Separates the query component from a URI reference.
std::string_view get_fragment(std::string_view a_uriref_sv)
Separates the fragment component from a URI reference.
std::string_view get_userinfo(std::string_view a_uriref_sv)
Separates the authority userinfo subcomponent from a URI reference.
std::string_view get_host(std::string_view a_uriref_sv)
Separates the authority host subcomponent from a URI reference.
void merge_paths(CPath &a_path, const CUriRef &a_base, const CUriRef &a_rel)
Merge a relative reference to a base URI.
bool is_dns_name(const std::string &label)
Check if a string conforms to a DNS name.
Definition of the UPNP_E_* error messages.
#define UPNP_E_INVALID_URL
An URL passed into the function is invalid.
Reengineered Object Oriented UPnP+ program code.
UPnPsdk_VIS void decode_esc_chars(std::string &a_encoded)
Replaces http percent encoded characters with their character representation.
Declaration of the Sockaddr class and some free helper functions.
Trivial ::sockaddr structures enhanced with methods.
Define macro for synced logging to the console for detailed info and debug.