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:
parent
6c4a110679
commit
24cc33e704
17 changed files with 530 additions and 37 deletions
|
@ -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
113
src/util/colorize.cpp
Normal 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
34
src/util/colorize.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue