Try to fetch account information from posts without one
Example of one with: https://dlx.pink/notice/AbtdJkjioOo8ZSdDhw Example of one without: https://dlx.pink/notice/AbD2kgNviafFEsebqq
This commit is contained in:
parent
8462aa21bb
commit
8635127ec3
31
models.cpp
31
models.cpp
|
@ -7,6 +7,10 @@
|
||||||
#include "models.h"
|
#include "models.h"
|
||||||
#include "numberhelper.h"
|
#include "numberhelper.h"
|
||||||
|
|
||||||
|
#define DOMAIN_RE "(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}"
|
||||||
|
// https://docs.joinmastodon.org/methods/accounts/#422-unprocessable-entity
|
||||||
|
#define USERNAME_RE "[a-zA-Z0-9_]+"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
static time_t parse_rfc3339(const std::string& str);
|
static time_t parse_rfc3339(const std::string& str);
|
||||||
|
|
||||||
|
@ -28,15 +32,10 @@ void from_json(const json& j, AccountField& field) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::regex host_regex(R"EOF(https?://([a-z0-9-.]+)/.+)EOF", std::regex::ECMAScript | std::regex::icase);
|
static std::regex host_regex("https?://(" DOMAIN_RE ")/.+", std::regex::ECMAScript | std::regex::icase);
|
||||||
void from_json(const json& j, Account& account) {
|
void from_json(const json& j, Account& account) {
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
// https://dlx.pink/notice/AbtdJkjioOo8ZSdDhw
|
|
||||||
if (j.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
j.at("id").get_to(account.id);
|
j.at("id").get_to(account.id);
|
||||||
j.at("username").get_to(account.username);
|
j.at("username").get_to(account.username);
|
||||||
j.at("display_name").get_to(account.display_name);
|
j.at("display_name").get_to(account.display_name);
|
||||||
|
@ -117,6 +116,7 @@ void from_json(const json& j, Poll& poll) {
|
||||||
j.at("emojis").get_to(poll.emojis);
|
j.at("emojis").get_to(poll.emojis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::regex akkoma_status_url_regex("https?://(" DOMAIN_RE ")/(?:@|users/)(" USERNAME_RE ")/.+");
|
||||||
void from_json(const json& j, Post& post) {
|
void from_json(const json& j, Post& post) {
|
||||||
j.at("id").get_to(post.id);
|
j.at("id").get_to(post.id);
|
||||||
post.created_at = parse_rfc3339(j.at("created_at").get_ref<const std::string&>());
|
post.created_at = parse_rfc3339(j.at("created_at").get_ref<const std::string&>());
|
||||||
|
@ -142,13 +142,30 @@ void from_json(const json& j, Post& post) {
|
||||||
post.reblog = std::make_unique<Post>();
|
post.reblog = std::make_unique<Post>();
|
||||||
from_json(j["reblog"].get<json>(), *post.reblog.get());
|
from_json(j["reblog"].get<json>(), *post.reblog.get());
|
||||||
}
|
}
|
||||||
j.at("account").get_to(post.account);
|
|
||||||
j.at("media_attachments").get_to(post.media_attachments);
|
j.at("media_attachments").get_to(post.media_attachments);
|
||||||
j.at("emojis").get_to(post.emojis);
|
j.at("emojis").get_to(post.emojis);
|
||||||
// https://social.kernel.org/@monsieuricon/Ac6oYwtLhess6uil1c
|
// https://social.kernel.org/@monsieuricon/Ac6oYwtLhess6uil1c
|
||||||
if (j.contains("poll") && !j["poll"].is_null()) {
|
if (j.contains("poll") && !j["poll"].is_null()) {
|
||||||
post.poll = j["poll"].get<Poll>();
|
post.poll = j["poll"].get<Poll>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// empty account with username accessible: https://dlx.pink/notice/AbtdJkjioOo8ZSdDhw
|
||||||
|
// empty account with username inaccesible: https://dlx.pink/notice/AbD2kgNviafFEsebqq
|
||||||
|
if (j.at("account").size()) {
|
||||||
|
j["account"].get_to(post.account);
|
||||||
|
} else {
|
||||||
|
std::smatch sm;
|
||||||
|
const std::string& url = j.at("url").get_ref<const std::string&>();
|
||||||
|
if (!std::regex_match(url, sm, akkoma_status_url_regex)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
post.account = {
|
||||||
|
.username = sm.str(2),
|
||||||
|
.server = sm.str(1),
|
||||||
|
.display_name = sm.str(2),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(const json& j, PostContext& context) {
|
void from_json(const json& j, PostContext& context) {
|
||||||
|
|
|
@ -105,7 +105,12 @@ svg {
|
||||||
.post-header span, .post-header time {
|
.post-header span, .post-header time {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
margin-left: 0.5em;
|
}
|
||||||
|
.post-header img {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
.post-header time {
|
||||||
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
.post-time_header {
|
.post-time_header {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
|
@ -33,6 +33,7 @@ void status_route(const httplib::Request& req, httplib::Response& res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dlx.pink/notice/AbtdJkjioOo8ZSdDhw
|
// https://dlx.pink/notice/AbtdJkjioOo8ZSdDhw
|
||||||
|
// https://dlx.pink/notice/AbD2kgNviafFEsebqq
|
||||||
if (post->reblog && !post->reblog->account.id.empty()) {
|
if (post->reblog && !post->reblog->account.id.empty()) {
|
||||||
serve_redirect(req, res, get_origin(req) + '/' + server + "/@" + post->reblog->account.acct(false) + '/' + post->reblog->id, true);
|
serve_redirect(req, res, get_origin(req) + '/' + server + "/@" + post->reblog->account.acct(false) + '/' + post->reblog->id, true);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct PostStatus {
|
||||||
const char* icon_html;
|
const char* icon_html;
|
||||||
Node info_node;
|
Node info_node;
|
||||||
};
|
};
|
||||||
static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, bool main_post, const std::optional<PostStatus>& post_status);
|
static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, bool main_post, const std::optional<PostStatus>& post_status, const Post* reblogged = nullptr);
|
||||||
static inline Element serialize_media(const Media& media);
|
static inline Element serialize_media(const Media& media);
|
||||||
static inline Element serialize_poll(const httplib::Request& req, const Poll& poll);
|
static inline Element serialize_poll(const httplib::Request& req, const Poll& poll);
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ Element serialize_post(const httplib::Request& req, const std::string& server, c
|
||||||
fa_retweet,
|
fa_retweet,
|
||||||
preprocess_html(req, post.account.emojis, post.account.display_name + " boosted"),
|
preprocess_html(req, post.account.emojis, post.account.display_name + " boosted"),
|
||||||
};
|
};
|
||||||
return serialize_post(req, server, *post.reblog, main_post, post_status);
|
return serialize_post(req, server, *post.reblog, main_post, post_status, &post);
|
||||||
} else if (pinned) {
|
} else if (pinned) {
|
||||||
PostStatus post_status = {
|
PostStatus post_status = {
|
||||||
fa_thumbtack,
|
fa_thumbtack,
|
||||||
|
@ -437,9 +437,17 @@ static inline std::vector<lxb_dom_node_t*> emojify(lxb_dom_document_t* document,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, bool main_post, const std::optional<PostStatus>& post_status) {
|
static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, bool main_post, const std::optional<PostStatus>& post_status, const Post* reblogged) {
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
bool user_known = !post.account.id.empty();
|
||||||
|
bool user_ref_known = !post.account.username.empty() && !post.account.server.empty();
|
||||||
|
// `reblogged == nullptr` since a malicious server could take down the frontend
|
||||||
|
// by sending a post that is not a reblog with no account information
|
||||||
|
std::string post_url = user_known || reblogged == nullptr
|
||||||
|
? get_origin(req) + '/' + server + "/@" + post.account.acct(false) + '/' + post.id + "#m"
|
||||||
|
: get_origin(req) + '/' + server + "/@" + reblogged->account.acct(false) + '/' + reblogged->id + "#m";
|
||||||
|
|
||||||
std::string time_title = post.edited_at < 0
|
std::string time_title = post.edited_at < 0
|
||||||
? full_time(post.created_at)
|
? full_time(post.created_at)
|
||||||
: "Created: "s + full_time(post.created_at) + "\nEdited: " + full_time(post.edited_at);
|
: "Created: "s + full_time(post.created_at) + "\nEdited: " + full_time(post.edited_at);
|
||||||
|
@ -478,14 +486,16 @@ static Element serialize_post(const httplib::Request& req, const std::string& se
|
||||||
|
|
||||||
Element div("div", {{"class", "post"}}, {
|
Element div("div", {{"class", "post"}}, {
|
||||||
Element("div", {{"class", "post-header"}}, {
|
Element("div", {{"class", "post-header"}}, {
|
||||||
!post.account.id.empty() ? Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false)}}, {
|
user_ref_known ? Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false)}}, {
|
||||||
Element("img", {{"class", "post-avatar"}, {"alt", "User profile picture"}, {"loading", "lazy"}, {"src", post.account.avatar_static}}, {}),
|
!post.account.avatar_static.empty()
|
||||||
|
? Element("img", {{"class", "post-avatar"}, {"alt", "User profile picture"}, {"loading", "lazy"}, {"src", post.account.avatar_static}}, {})
|
||||||
|
: Node(""),
|
||||||
Element("span", {
|
Element("span", {
|
||||||
Element("b", {preprocess_html(req, post.account.emojis, post.account.display_name)}),
|
Element("b", {preprocess_html(req, post.account.emojis, post.account.display_name)}),
|
||||||
Element("br"), "@", post.account.acct(),
|
Element("br"), "@", post.account.acct(),
|
||||||
}),
|
}),
|
||||||
}) : Element("b", {"Unknown user"}),
|
}) : Element("b", {"Unknown user"}),
|
||||||
Element("a", {{"class", "post-time_header"}, {"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false) + '/' + post.id + "#m"}, {"title", time_title}}, {
|
Element("a", {{"class", "post-time_header"}, {"href", std::move(post_url)}, {"title", time_title}}, {
|
||||||
Element("time", {{"datetime", to_rfc3339(post.created_at)}}, {relative_time(post.created_at, current_time()), time_badge}),
|
Element("time", {{"datetime", to_rfc3339(post.created_at)}}, {relative_time(post.created_at, current_time()), time_badge}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Reference in New Issue