blob: 969f3fba78d626d6752bf9c6ca82d432a26222aa [file] [edit]
#include "utils/url.h"
#include <cctype>
#include <cstddef>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace milotic {
std::string UrlDecode(absl::string_view encoded) {
std::string decoded;
decoded.reserve(encoded.size());
for (size_t i = 0; i < encoded.size(); ++i) {
if (encoded[i] != '%' || i + 2 >= encoded.size()) {
decoded.push_back(encoded[i]);
continue;
}
char hex1 = encoded[i + 1];
char hex2 = encoded[i + 2];
if (std::isxdigit(static_cast<unsigned char>(hex1)) == 0 ||
std::isxdigit(static_cast<unsigned char>(hex2)) == 0) {
decoded.push_back(encoded[i]);
continue;
}
char val = 0;
if (hex1 >= '0' && hex1 <= '9') {
val += (hex1 - '0') * 16;
} else if (hex1 >= 'a' && hex1 <= 'f') {
val += (hex1 - 'a' + 10) * 16;
} else if (hex1 >= 'A' && hex1 <= 'F') {
val += (hex1 - 'A' + 10) * 16;
}
if (hex2 >= '0' && hex2 <= '9') {
val += (hex2 - '0');
} else if (hex2 >= 'a' && hex2 <= 'f') {
val += (hex2 - 'a' + 10);
} else if (hex2 >= 'A' && hex2 <= 'F') {
val += (hex2 - 'A' + 10);
}
decoded.push_back(val);
i += 2;
}
return decoded;
}
namespace {
std::string NormalizePath(absl::string_view path) {
std::vector<absl::string_view> segments;
size_t start = 0;
while (start < path.size()) {
size_t end = path.find('/', start);
if (end == absl::string_view::npos) {
end = path.size();
}
absl::string_view segment = path.substr(start, end - start);
if (!segment.empty() && segment != ".") {
if (segment == "..") {
if (!segments.empty()) {
segments.pop_back();
}
} else {
segments.push_back(segment);
}
}
start = end + 1;
}
bool has_trailing_slash = !path.empty() && path.back() == '/';
std::string normalized;
normalized.reserve(path.size());
for (absl::string_view segment : segments) {
normalized.push_back('/');
normalized.append(segment.data(), segment.size());
}
if (normalized.empty()) {
return "/";
}
if (has_trailing_slash) {
normalized.push_back('/');
}
return normalized;
}
} // namespace
absl::StatusOr<std::string> SanitizeUrl(absl::string_view redfish_id) {
size_t query_pos = redfish_id.find('?');
absl::string_view path_part = redfish_id.substr(0, query_pos);
absl::string_view query_part = (query_pos == absl::string_view::npos)
? ""
: redfish_id.substr(query_pos);
std::string decoded_path = UrlDecode(path_part);
if (absl::StrContains(decoded_path, '?') ||
absl::StrContains(decoded_path, '#') ||
absl::StrContains(decoded_path, '\0')) {
return absl::InvalidArgumentError("Path contains invalid characters");
}
return absl::StrCat(NormalizePath(decoded_path), query_part);
}
} // namespace milotic