1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-30 19:22:14 +00:00

Add button_url[] and hypertext element to allow mods to open web pages (#13825)

Fixes #12500
This commit is contained in:
rubenwardy 2024-03-24 17:19:23 +00:00 committed by GitHub
parent 6c4a110679
commit 24cc33e704
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 530 additions and 37 deletions

View file

@ -1,6 +1,7 @@
set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp

113
src/util/colorize.cpp Normal file
View file

@ -0,0 +1,113 @@
/*
Part of Minetest
Copyright (C) 2024 rubenwardy
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "colorize.h"
#ifdef USE_CURL
#include <curl/urlapi.h>
#include "log.h"
#include "string.h"
#include <sstream>
std::string colorize_url(const std::string &url)
{
// Forbid escape codes in URL
if (url.find('\x1b') != std::string::npos) {
throw std::runtime_error("Unable to open URL as it contains escape codes");
}
auto urlHandleRAII = std::unique_ptr<CURLU, decltype(&curl_url_cleanup)>(
curl_url(), curl_url_cleanup);
CURLU *urlHandle = urlHandleRAII.get();
auto rc = curl_url_set(urlHandle, CURLUPART_URL, url.c_str(), 0);
if (rc != CURLUE_OK) {
throw std::runtime_error("Unable to open URL as it is not valid");
}
auto url_get = [&] (CURLUPart what) -> std::string {
char *tmp = nullptr;
curl_url_get(urlHandle, what, &tmp, 0);
std::string ret(tmp ? tmp : "");
curl_free(tmp);
return ret;
};
auto scheme = url_get(CURLUPART_SCHEME);
auto user = url_get(CURLUPART_USER);
auto password = url_get(CURLUPART_PASSWORD);
auto host = url_get(CURLUPART_HOST);
auto port = url_get(CURLUPART_PORT);
auto path = url_get(CURLUPART_PATH);
auto query = url_get(CURLUPART_QUERY);
auto fragment = url_get(CURLUPART_FRAGMENT);
auto zoneid = url_get(CURLUPART_ZONEID);
std::ostringstream os;
std::string_view red = COLOR_CODE("#faa");
std::string_view white = COLOR_CODE("#fff");
std::string_view grey = COLOR_CODE("#aaa");
os << grey << scheme << "://";
if (!user.empty())
os << user;
if (!password.empty())
os << ":" << password;
if (!(user.empty() && password.empty()))
os << "@";
// Print hostname, escaping unsafe characters
os << white;
bool was_alphanum = true;
std::string host_s = host;
for (size_t i = 0; i < host_s.size(); i++) {
char c = host_s[i];
bool is_alphanum = isalnum(c) || ispunct(c);
if (is_alphanum == was_alphanum) {
// skip
} else if (is_alphanum) {
os << white;
} else {
os << red;
}
was_alphanum = is_alphanum;
if (is_alphanum) {
os << c;
} else {
os << "%" << std::setfill('0') << std::setw(2) << std::hex
<< (static_cast<unsigned int>(c) & 0xff);
}
}
os << grey;
if (!zoneid.empty())
os << "%" << zoneid;
if (!port.empty())
os << ":" << port;
os << path;
if (!query.empty())
os << "?" << query;
if (!fragment.empty())
os << "#" << fragment;
return os.str();
}
#endif

34
src/util/colorize.h Normal file
View file

@ -0,0 +1,34 @@
/*
Part of Minetest
Copyright (C) 2024 rubenwardy
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include "config.h"
#define COLOR_CODE(color) "\x1b(c@" color ")"
#ifdef USE_CURL
/**
* Colorize URL to highlight the hostname and any unsafe characters
*
* Throws an exception if the url is invalid
*/
std::string colorize_url(const std::string &url);
#endif

View file

@ -593,6 +593,40 @@ void str_replace(std::string &str, char from, char to)
std::replace(str.begin(), str.end(), from, to);
}
std::string wrap_rows(std::string_view from, unsigned row_len, bool has_color_codes)
{
std::string to;
to.reserve(from.size());
std::string last_color_code;
unsigned character_idx = 0;
bool inside_colorize = false;
for (size_t i = 0; i < from.size(); i++) {
if (!IS_UTF8_MULTB_INNER(from[i])) {
if (inside_colorize) {
last_color_code += from[i];
if (from[i] == ')') {
inside_colorize = false;
} else {
// keep reading
}
} else if (has_color_codes && from[i] == '\x1b') {
inside_colorize = true;
last_color_code = "\x1b";
} else {
// Wrap string after last inner byte of char
if (character_idx > 0 && character_idx % row_len == 0) {
to += '\n' + last_color_code;
}
character_idx++;
}
}
to += from[i];
}
return to;
}
/* Translated strings have the following format:
* \x1bT marks the beginning of a translated string
* \x1bE marks its end

View file

@ -512,7 +512,7 @@ inline bool string_allowed_blacklist(std::string_view str,
* Create a string based on \p from where a newline is forcefully inserted
* every \p row_len characters.
*
* @note This function does not honour word wraps and blindy inserts a newline
* @note This function does not honour word wraps and blindly inserts a newline
* every \p row_len characters whether it breaks a word or not. It is
* intended to be used for, for example, showing paths in the GUI.
*
@ -521,26 +521,10 @@ inline bool string_allowed_blacklist(std::string_view str,
*
* @param from The (utf-8) string to be wrapped into rows.
* @param row_len The row length (in characters).
* @param has_color_codes Whether the source string has colorize codes.
* @return A new string with the wrapping applied.
*/
inline std::string wrap_rows(std::string_view from, unsigned row_len)
{
std::string to;
to.reserve(from.size());
unsigned character_idx = 0;
for (size_t i = 0; i < from.size(); i++) {
if (!IS_UTF8_MULTB_INNER(from[i])) {
// Wrap string after last inner byte of char
if (character_idx > 0 && character_idx % row_len == 0)
to += '\n';
character_idx++;
}
to += from[i];
}
return to;
}
std::string wrap_rows(std::string_view from, unsigned row_len, bool has_color_codes = false);
/**