From b2bd4e18b9d7691cfae7be3e5e08a0c00ff4ac0e Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Jan 2025 03:28:53 +0100 Subject: Updated documentation. --- doc/snac.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snac.1 b/doc/snac.1 index 71dcf4a..7611600 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -256,7 +256,7 @@ it's - (a lonely hyphen), the post content will be read from stdin. The rest of command line arguments are treated as media files to be attached to the post. .It Cm note_unlisted Ar basedir Ar uid Ar text Op file file ... -Like the previous one, but creates an "unlisted" (or "quiet public") one. +Like the previous one, but creates an "unlisted" (or "quiet public") post. .It Cm block Ar basedir Ar instance_url Blocks a full instance, given its URL or domain name. All subsequent incoming activities with identifiers from that instance will be immediately -- cgit v1.2.3 From 64f2d3d746d8536c460b88fc852fdca01c5c813c Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Jan 2025 08:08:51 +0100 Subject: Minor semantic tweak. --- activitypub.c | 6 +++++- snac.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index 1ae5ad9..0740d3d 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1423,8 +1423,9 @@ xs_dict *msg_follow(snac *snac, const char *q) xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, const xs_str *in_reply_to, const xs_list *attach, - int priv, const char *lang_str) + int scope, const char *lang_str) /* creates a 'Note' message */ +/* scope: 0, public; 1, private (mentioned only); 2, "quiet public"; 3, followers only */ { xs *ntid = tid(0); xs *id = xs_fmt("%s/p/%s", snac->actor, ntid); @@ -1440,6 +1441,9 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, xs_list *p; const xs_val *v; + /* FIXME: implement scopes 2 and 3 */ + int priv = scope == 1; + if (rcpts == NULL) to = xs_list_new(); else { diff --git a/snac.h b/snac.h index ec3ee5c..75db4a9 100644 --- a/snac.h +++ b/snac.h @@ -317,7 +317,7 @@ xs_dict *msg_follow(snac *snac, const char *actor); xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, const xs_str *in_reply_to, const xs_list *attach, - int priv, const char *lang); + int scope, const char *lang); xs_dict *msg_undo(snac *snac, const xs_val *object); xs_dict *msg_delete(snac *snac, const char *id); -- cgit v1.2.3 From cdfa5ee8df661d4745e0949d07a3b2e2fa848bd8 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Jan 2025 10:19:48 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 247b53b..31165ac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # Release Notes -## UNRELEASED +## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 1039c2dff51173b46278c6b89555af7b273a9eae Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Jan 2025 10:20:07 +0100 Subject: Version 2.68 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 75db4a9..5ee0390 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ -#define VERSION "2.68-dev" +#define VERSION "2.68" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From fc9140bf1a94ea927c10db9c6bfb70afe5438bef Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 Jan 2025 19:01:46 +0100 Subject: Added a tweak to follow LitePub (Pleroma-style, https://litepub.social/) relays. Usage: create a magic user named 'relay' and make it follow a relay actor (examples: https://relay.c.im/actor or https://relay.ie9.org/actor). Unfollowing returns a 400 status and posts keep arriving (at least with ie9.org), so this is a problem at this moment. --- activitypub.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/activitypub.c b/activitypub.c index 0740d3d..fe3fd41 100644 --- a/activitypub.c +++ b/activitypub.c @@ -675,7 +675,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) if (pub_msg) { /* a public message for someone we follow? (probably cc'ed) accept */ - if (following_check(snac, v)) + if (strcmp(v, public_address) != 0 && following_check(snac, v)) return 5; } else @@ -1242,6 +1242,10 @@ xs_dict *msg_actor(snac *snac) if (xs_type(xs_dict_get(snac->config, "bot")) == XSTYPE_TRUE) msg = xs_dict_set(msg, "type", "Service"); + /* if it's named "relay", then identify as an "Application" */ + if (strcmp(snac->uid, "relay") == 0) + msg = xs_dict_set(msg, "type", "Application"); + /* add the header image, if there is one defined */ const char *header = xs_dict_get(snac->config, "header"); if (!xs_is_null(header)) { @@ -2773,11 +2777,12 @@ void process_queue_item(xs_dict *q_item) snac user; if (user_open(&user, v)) { - if (is_msg_for_me(&user, msg)) { + int rsn = is_msg_for_me(&user, msg); + if (rsn) { xs *fn = xs_fmt("%s/queue/%s.json", user.basedir, ntid); snac_debug(&user, 1, - xs_fmt("enqueue_input (from shared inbox) %s", xs_dict_get(msg, "id"))); + xs_fmt("enqueue_input (from shared inbox) %s [%d]", xs_dict_get(msg, "id"), rsn)); if (link(tmpfn, fn) < 0) srv_log(xs_fmt("link(%s, %s) error", tmpfn, fn)); -- cgit v1.2.3 From 6af6194f55e751157f74a891738697e635c9c64e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 16:51:30 +0100 Subject: Show hashtags that are not already linked from the post content. --- html.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/html.c b/html.c index e8803ab..d1844c2 100644 --- a/html.c +++ b/html.c @@ -2189,6 +2189,39 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, au_tag); } + /* show all hashtags that has not been shown previously in the content */ + const xs_list *tags = xs_dict_get(msg, "tag"); + if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { + const char *content = xs_dict_get(msg, "content"); + const xs_dict *tag; + + xs_html *add_hashtags = xs_html_tag("ul", + xs_html_attr("class", "snac-more-hashtags")); + + xs_list_foreach(tags, tag) { + const char *type = xs_dict_get(tag, "type"); + + if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Hashtag") == 0) { + const char *href = xs_dict_get(tag, "href"); + + if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { + /* not in the content: add here */ + const char *name = xs_dict_get(tag, "name"); + + xs_html_add(add_hashtags, + xs_html_tag("li", + xs_html_tag("a", + xs_html_attr("href", href), + xs_html_text(name), + xs_html_text(" ")))); + } + } + } + + xs_html_add(snac_content_wrap, + add_hashtags); + } + /** controls **/ if (!read_only && user) { -- cgit v1.2.3 From 932227bbac7fe740bff1e38bf92b58edef19e32f Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 16:59:14 +0100 Subject: Bumped copyright year. --- LICENSE | 2 +- activitypub.c | 2 +- data.c | 2 +- format.c | 2 +- html.c | 2 +- http.c | 2 +- httpd.c | 2 +- main.c | 4 ++-- mastoapi.c | 2 +- snac.c | 2 +- snac.h | 2 +- upgrade.c | 2 +- utils.c | 2 +- webfinger.c | 2 +- xs.h | 2 +- xs_curl.h | 2 +- xs_fcgi.h | 2 +- xs_glob.h | 2 +- xs_hex.h | 2 +- xs_html.h | 2 +- xs_httpd.h | 2 +- xs_io.h | 2 +- xs_json.h | 2 +- xs_match.h | 2 +- xs_mime.h | 2 +- xs_openssl.h | 2 +- xs_random.h | 2 +- xs_regex.h | 2 +- xs_set.h | 2 +- xs_socket.h | 2 +- xs_time.h | 2 +- xs_unicode.h | 2 +- xs_unix_socket.h | 2 +- xs_url.h | 25 ++++++++++++++++++++++++- xs_version.h | 2 +- 35 files changed, 59 insertions(+), 36 deletions(-) diff --git a/LICENSE b/LICENSE index cdb4e0b..702b487 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 - 2024 grunfink et al. (Fediverse: @grunfink@comam.es) +Copyright (c) 2022 - 2025 grunfink et al. (Fediverse: @grunfink@comam.es) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/activitypub.c b/activitypub.c index fe3fd41..24954e6 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_json.h" diff --git a/data.c b/data.c index 0fd3528..576bc40 100644 --- a/data.c +++ b/data.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_hex.h" diff --git a/format.c b/format.c index 41e4162..52ed3ec 100644 --- a/format.c +++ b/format.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_regex.h" diff --git a/html.c b/html.c index d1844c2..5a62d16 100644 --- a/html.c +++ b/html.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" diff --git a/http.c b/http.c index b21f1dc..611de4f 100644 --- a/http.c +++ b/http.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" diff --git a/httpd.c b/httpd.c index 163f98a..dda40b9 100644 --- a/httpd.c +++ b/httpd.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" diff --git a/main.c b/main.c index 7d23c84..5b1dd61 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" @@ -14,7 +14,7 @@ int usage(void) { printf("snac " VERSION " - A simple, minimalistic ActivityPub instance\n"); - printf("Copyright (c) 2022 - 2024 grunfink et al. / MIT license\n"); + printf("Copyright (c) 2022 - 2025 grunfink et al. / MIT license\n"); printf("\n"); printf("Commands:\n"); printf("\n"); diff --git a/mastoapi.c b/mastoapi.c index 62108ad..4b89d12 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef NO_MASTODON_API diff --git a/snac.c b/snac.c index 1ce563b..80c7349 100644 --- a/snac.c +++ b/snac.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #define XS_IMPLEMENTATION diff --git a/snac.h b/snac.h index 5ee0390..23ec93a 100644 --- a/snac.h +++ b/snac.h @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #define VERSION "2.68" diff --git a/upgrade.c b/upgrade.c index 847c62e..87ddfc8 100644 --- a/upgrade.c +++ b/upgrade.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" diff --git a/utils.c b/utils.c index 0740d4d..336ae2d 100644 --- a/utils.c +++ b/utils.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_io.h" diff --git a/webfinger.c b/webfinger.c index 85123bc..5db9a97 100644 --- a/webfinger.c +++ b/webfinger.c @@ -1,5 +1,5 @@ /* snac - A simple, minimalistic ActivityPub instance */ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #include "xs.h" #include "xs_json.h" diff --git a/xs.h b/xs.h index 39b3b64..a6e40d5 100644 --- a/xs.h +++ b/xs.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_H diff --git a/xs_curl.h b/xs_curl.h index 9b4c229..f0cfd98 100644 --- a/xs_curl.h +++ b/xs_curl.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_CURL_H diff --git a/xs_fcgi.h b/xs_fcgi.h index c6ffe1f..870e3e8 100644 --- a/xs_fcgi.h +++ b/xs_fcgi.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ /* This is an intentionally-dead-simple FastCGI implementation; diff --git a/xs_glob.h b/xs_glob.h index 15cb49a..6d87ca1 100644 --- a/xs_glob.h +++ b/xs_glob.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_GLOB_H diff --git a/xs_hex.h b/xs_hex.h index 745ac93..2507d5d 100644 --- a/xs_hex.h +++ b/xs_hex.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_HEX_H diff --git a/xs_html.h b/xs_html.h index a95d45a..8331f93 100644 --- a/xs_html.h +++ b/xs_html.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_HTML_H diff --git a/xs_httpd.h b/xs_httpd.h index 860ae05..cf0d811 100644 --- a/xs_httpd.h +++ b/xs_httpd.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_HTTPD_H diff --git a/xs_io.h b/xs_io.h index f63ae5b..110b0eb 100644 --- a/xs_io.h +++ b/xs_io.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_IO_H diff --git a/xs_json.h b/xs_json.h index 69249b1..660c277 100644 --- a/xs_json.h +++ b/xs_json.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_JSON_H diff --git a/xs_match.h b/xs_match.h index 668429a..0b89ac8 100644 --- a/xs_match.h +++ b/xs_match.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_MATCH_H diff --git a/xs_mime.h b/xs_mime.h index 5c0a6fd..6f65382 100644 --- a/xs_mime.h +++ b/xs_mime.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_MIME_H diff --git a/xs_openssl.h b/xs_openssl.h index f339ac6..9388691 100644 --- a/xs_openssl.h +++ b/xs_openssl.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_OPENSSL_H diff --git a/xs_random.h b/xs_random.h index 78de32a..f936099 100644 --- a/xs_random.h +++ b/xs_random.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_RANDOM_H diff --git a/xs_regex.h b/xs_regex.h index 3ba504b..a4db447 100644 --- a/xs_regex.h +++ b/xs_regex.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_REGEX_H diff --git a/xs_set.h b/xs_set.h index f6b65aa..3eaefdf 100644 --- a/xs_set.h +++ b/xs_set.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_SET_H diff --git a/xs_socket.h b/xs_socket.h index fb67b9d..6e618ba 100644 --- a/xs_socket.h +++ b/xs_socket.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_SOCKET_H diff --git a/xs_time.h b/xs_time.h index 1df2fef..0e004dc 100644 --- a/xs_time.h +++ b/xs_time.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_TIME_H diff --git a/xs_unicode.h b/xs_unicode.h index cfcd8ff..ef18fea 100644 --- a/xs_unicode.h +++ b/xs_unicode.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_UNICODE_H diff --git a/xs_unix_socket.h b/xs_unix_socket.h index 3eb2307..462a5b3 100644 --- a/xs_unix_socket.h +++ b/xs_unix_socket.h @@ -1,4 +1,4 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_UNIX_SOCKET_H diff --git a/xs_url.h b/xs_url.h index ac43585..3c24736 100644 --- a/xs_url.h +++ b/xs_url.h @@ -1,10 +1,11 @@ -/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ +/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ #ifndef _XS_URL_H #define _XS_URL_H xs_str *xs_url_dec(const char *str); +xs_str *xs_url_enc(const char *str); xs_dict *xs_url_vars(const char *str); xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *header); @@ -39,6 +40,28 @@ xs_str *xs_url_dec(const char *str) } +xs_str *xs_url_enc(const char *str) +/* URL-encodes a string (RFC 3986) */ +{ + xs_str *s = xs_str_new(NULL); + + while (*str) { + if (isalnum(*str) || strchr("-._~", *str)) { + s = xs_append_m(s, str, 1); + } + else { + char tmp[8]; + snprintf(tmp, sizeof(tmp), "%%%02X", (unsigned char)*str); + s = xs_append_m(s, tmp, 3); + } + + str++; + } + + return s; +} + + xs_dict *xs_url_vars(const char *str) /* parse url variables */ { diff --git a/xs_version.h b/xs_version.h index 770366a..fc3b755 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 297f71e198be7819213e9122e1e78c3b963111bc 2024-11-24T18:48:42+01:00 */ +/* 9e8f5cf300ffbf453031f2ec923cef0822a41b41 2025-01-08T16:57:26+01:00 */ -- cgit v1.2.3 From b51085831e40dbcbd5ff80e033c1b59702cbc920 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 17:20:53 +0100 Subject: Also check for the lowecase URL of the hashtags. --- Makefile | 3 ++- Makefile.NetBSD | 3 ++- html.c | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8aa2ca9..75d9562 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,8 @@ data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h xs_match.h snac.h http_codes.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ - xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h snac.h http_codes.h + xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ + http_codes.h http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ snac.h http_codes.h httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ diff --git a/Makefile.NetBSD b/Makefile.NetBSD index 20cf28b..f14eb6e 100644 --- a/Makefile.NetBSD +++ b/Makefile.NetBSD @@ -44,7 +44,8 @@ data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h xs_match.h snac.h http_codes.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ - xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h snac.h http_codes.h + xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ + http_codes.h http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ snac.h http_codes.h httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ diff --git a/html.c b/html.c index 5a62d16..bade62a 100644 --- a/html.c +++ b/html.c @@ -12,6 +12,7 @@ #include "xs_match.h" #include "xs_html.h" #include "xs_curl.h" +#include "xs_unicode.h" #include "snac.h" @@ -2203,8 +2204,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Hashtag") == 0) { const char *href = xs_dict_get(tag, "href"); + xs *lc_href = xs_utf8_to_lower(href); - if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { + if (xs_type(href) == XSTYPE_STRING && + xs_str_in(content, href) == -1 && + xs_str_in(content, lc_href) == -1) { /* not in the content: add here */ const char *name = xs_dict_get(tag, "name"); -- cgit v1.2.3 From a6dce9953889156b485405c7de2bd954587b2ddc Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 17:24:41 +0100 Subject: Do a lower case search of hashtags in the content. --- html.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/html.c b/html.c index bade62a..7c46c76 100644 --- a/html.c +++ b/html.c @@ -2193,7 +2193,8 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, /* show all hashtags that has not been shown previously in the content */ const xs_list *tags = xs_dict_get(msg, "tag"); if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { - const char *content = xs_dict_get(msg, "content"); + const char *o_content = xs_dict_get(msg, "content"); + xs *content = xs_utf8_to_lower(o_content); const xs_dict *tag; xs_html *add_hashtags = xs_html_tag("ul", @@ -2203,12 +2204,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, const char *type = xs_dict_get(tag, "type"); if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Hashtag") == 0) { - const char *href = xs_dict_get(tag, "href"); - xs *lc_href = xs_utf8_to_lower(href); + const char *o_href = xs_dict_get(tag, "href"); + xs *href = xs_utf8_to_lower(o_href); - if (xs_type(href) == XSTYPE_STRING && - xs_str_in(content, href) == -1 && - xs_str_in(content, lc_href) == -1) { + if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { /* not in the content: add here */ const char *name = xs_dict_get(tag, "name"); -- cgit v1.2.3 From 935a0a5a7d9f50427fe15b0a8b947d51db177cdc Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 19:56:15 +0100 Subject: Fixed crash on NULL hashtag href. --- html.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/html.c b/html.c index 7c46c76..294faa3 100644 --- a/html.c +++ b/html.c @@ -2205,18 +2205,21 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Hashtag") == 0) { const char *o_href = xs_dict_get(tag, "href"); - xs *href = xs_utf8_to_lower(o_href); - if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { - /* not in the content: add here */ - const char *name = xs_dict_get(tag, "name"); + if (xs_type(o_href) == XSTYPE_STRING) { + xs *href = xs_utf8_to_lower(o_href); - xs_html_add(add_hashtags, - xs_html_tag("li", - xs_html_tag("a", - xs_html_attr("href", href), - xs_html_text(name), - xs_html_text(" ")))); + if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { + /* not in the content: add here */ + const char *name = xs_dict_get(tag, "name"); + + xs_html_add(add_hashtags, + xs_html_tag("li", + xs_html_tag("a", + xs_html_attr("href", href), + xs_html_text(name), + xs_html_text(" ")))); + } } } } -- cgit v1.2.3 From 74cd356be8ba270c76202c7c55b5b07ab4bfd22c Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 Jan 2025 20:06:17 +0100 Subject: Deleted useless check. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 294faa3..7e3d281 100644 --- a/html.c +++ b/html.c @@ -2209,7 +2209,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (xs_type(o_href) == XSTYPE_STRING) { xs *href = xs_utf8_to_lower(o_href); - if (xs_type(href) == XSTYPE_STRING && xs_str_in(content, href) == -1) { + if (xs_str_in(content, href) == -1) { /* not in the content: add here */ const char *name = xs_dict_get(tag, "name"); -- cgit v1.2.3 From 25b22d8a94ddfd3f281e0b0c3405bf7dd6e6feac Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 Jan 2025 09:10:21 +0100 Subject: Some fixes to receive and update 'Event' objects correctly. --- activitypub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activitypub.c b/activitypub.c index 24954e6..944dd69 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2126,14 +2126,14 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id)); } else - if (strcmp(utype, "Video") == 0) { /** **/ + if (xs_match(utype, "Audio|Video|Event")) { /** **/ const char *id = xs_dict_get(object, "id"); if (xs_is_null(id)) snac_log(snac, xs_fmt("malformed message: no 'id' field")); else if (timeline_add(snac, id, object)) - snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id)); + snac_log(snac, xs_fmt("new '%s' %s %s", utype, actor, id)); } else snac_debug(snac, 1, xs_fmt("ignored 'Create' for object type '%s'", utype)); @@ -2241,7 +2241,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) snac_log(snac, xs_fmt("updated actor %s", actor)); } else - if (xs_match(utype, "Note|Page|Article|Video")) { /** **/ + if (xs_match(utype, "Note|Page|Article|Video|Audio|Event")) { /** **/ const char *id = xs_dict_get(object, "id"); if (xs_is_null(id)) -- cgit v1.2.3 From c25360468c56286a50f52128f548bfd9e8cb7b6b Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 Jan 2025 18:37:09 +0100 Subject: Added a way to archive ignored messages. --- activitypub.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activitypub.c b/activitypub.c index 24954e6..9f7e9a6 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1853,6 +1853,17 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) /* reject uninteresting messages right now */ if (xs_match(type, "Add|View|Reject|Read|Remove")) { srv_debug(0, xs_fmt("Ignored message of type '%s'", type)); + + /* archive the ignored activity */ + xs *ntid = tid(0); + xs *fn = xs_fmt("%s/ignored/%s.json", srv_basedir, ntid); + FILE *f; + + if ((f = fopen(fn, "w")) != NULL) { + xs_json_dump(msg, 4, f); + fclose(f); + } + return -1; } -- cgit v1.2.3 From 41cb63c936e1520def842f381334397a572bc1cd Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 Jan 2025 19:21:06 +0100 Subject: Added some actor object caching. --- activitypub.c | 16 +++++++++++++++- data.c | 4 ++++ snac.h | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index 81f7708..6f3aefb 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1194,10 +1194,20 @@ xs_dict *msg_actor(snac *snac) xs *avtr = NULL; xs *kid = NULL; xs *f_bio = NULL; - xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL); + xs_dict *msg = NULL; const char *p; int n; + /* everybody loves some caching */ + if (time(NULL) - object_mtime(snac->actor) < 6 * 3600 && + valid_status(object_get(snac->actor, &msg))) { + snac_debug(snac, 1, xs_fmt("Returning cached actor %s", snac->actor)); + + return msg; + } + + msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL); + /* change the @context (is this really necessary?) */ ctxt = xs_list_append(ctxt, "https:/" "/www.w3.org/ns/activitystreams"); ctxt = xs_list_append(ctxt, "https:/" "/w3id.org/security/v1"); @@ -1330,6 +1340,10 @@ xs_dict *msg_actor(snac *snac) msg = xs_dict_set(msg, "manuallyApprovesFollowers", xs_stock(xs_is_true(manually) ? XSTYPE_TRUE : XSTYPE_FALSE)); + /* cache it */ + snac_debug(snac, 1, xs_fmt("Caching actor %s", snac->actor)); + object_add_ow(snac->actor, msg); + return msg; } diff --git a/data.c b/data.c index 576bc40..3463727 100644 --- a/data.c +++ b/data.c @@ -336,6 +336,10 @@ int user_persist(snac *snac, int publish) if (!nw) publish = 0; + else { + /* uncache the actor object */ + object_del(snac->actor); + } } } } diff --git a/snac.h b/snac.h index 23ec93a..5729a22 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.68" +#define VERSION "2.69-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From f97fbe374429833319cea78b1f149a3111cea633 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 Jan 2025 19:32:40 +0100 Subject: Bumped debug level of actor cache hits. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 6f3aefb..d31a888 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1201,7 +1201,7 @@ xs_dict *msg_actor(snac *snac) /* everybody loves some caching */ if (time(NULL) - object_mtime(snac->actor) < 6 * 3600 && valid_status(object_get(snac->actor, &msg))) { - snac_debug(snac, 1, xs_fmt("Returning cached actor %s", snac->actor)); + snac_debug(snac, 2, xs_fmt("Returning cached actor %s", snac->actor)); return msg; } -- cgit v1.2.3 From 361f6b693a0a6eee886fc6e5bacf42f359e7f793 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 06:41:31 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 31165ac..ae68d50 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,13 @@ # Release Notes +## UNRELEASED + +Some fixes to accept `Event` objects properly (like those coming from implementations like https://gancio.org/). + +Added some caching for local `Actor` objects. + +Hashtags that are not explicitly linked in a post's content are shown below. + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 131fbc105a9b82988869fe84c03f2e0b0135e168 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 06:45:44 +0100 Subject: Always return a shared inbox for the 'relay' user. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index d31a888..77d8e34 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1321,7 +1321,7 @@ xs_dict *msg_actor(snac *snac) } /* use shared inboxes? */ - if (xs_type(xs_dict_get(srv_config, "shared_inboxes")) == XSTYPE_TRUE) { + if (xs_is_true(xs_dict_get(srv_config, "shared_inboxes")) || strcmp(snac->uid, "relay") == 0) { xs *d = xs_dict_new(); xs *si = xs_fmt("%s/shared-inbox", srv_baseurl); d = xs_dict_append(d, "sharedInbox", si); -- cgit v1.2.3 From aab32743c150a62648cd620da05d4388747c6253 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 06:47:58 +0100 Subject: Actor caching lowered to 3 hours. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 77d8e34..ecc0533 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1199,7 +1199,7 @@ xs_dict *msg_actor(snac *snac) int n; /* everybody loves some caching */ - if (time(NULL) - object_mtime(snac->actor) < 6 * 3600 && + if (time(NULL) - object_mtime(snac->actor) < 3 * 3600 && valid_status(object_get(snac->actor, &msg))) { snac_debug(snac, 2, xs_fmt("Returning cached actor %s", snac->actor)); -- cgit v1.2.3 From ed729878a6d411038b6dabfc81980c3a193efb97 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 08:00:18 +0100 Subject: New user settings latitude and longitude. --- data.c | 3 ++- html.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 3463727..ca63084 100644 --- a/data.c +++ b/data.c @@ -319,7 +319,8 @@ int user_persist(snac *snac, int publish) if (old != NULL) { int nw = 0; - const char *fields[] = { "header", "avatar", "name", "bio", "metadata", NULL }; + const char *fields[] = { "header", "avatar", "name", "bio", + "metadata", "latitude", "longitude", NULL }; for (int n = 0; fields[n]; n++) { const char *of = xs_dict_get(old, fields[n]); diff --git a/html.c b/html.c index 7e3d281..559d139 100644 --- a/html.c +++ b/html.c @@ -1101,6 +1101,8 @@ xs_html *html_top_controls(snac *snac) const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); const xs_val *pending = xs_dict_get(snac->config, "approve_followers"); const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics"); + const char *latitude = xs_dict_get_def(snac->config, "latitude", ""); + const char *longitude = xs_dict_get_def(snac->config, "longitude", ""); xs *metadata = NULL; const xs_dict *md = xs_dict_get(snac->config, "metadata"); @@ -1290,6 +1292,20 @@ xs_html *html_top_controls(snac *snac) xs_html_tag("label", xs_html_attr("for", "show_contact_metrics"), xs_html_text(L("Publish follower and following metrics")))), + xs_html_tag("p", + xs_html_text(L("Home location:")), + xs_html_sctag("br", NULL), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", "latitude"), + xs_html_attr("value", latitude), + xs_html_attr("placeholder", "latitude")), + xs_html_text(" "), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", "longitude"), + xs_html_attr("value", longitude), + xs_html_attr("placeholder", "longitude"))), xs_html_tag("p", xs_html_text(L("Profile metadata (key=value pairs in each line):")), xs_html_sctag("br", NULL), @@ -4053,6 +4069,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, else snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); + snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); + snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); + if ((v = xs_dict_get(p_vars, "metadata")) != NULL) snac.config = xs_dict_set(snac.config, "metadata", v); -- cgit v1.2.3 From ea2eb3299dbb13699cffadb7be445ae87cae00bc Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 08:12:38 +0100 Subject: Show the location in the public page. --- html.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/html.c b/html.c index 559d139..eb4ab1f 100644 --- a/html.c +++ b/html.c @@ -924,6 +924,7 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_raw("✔ "), xs_html_tag("a", xs_html_attr("rel", "me"), + xs_html_attr("target", "_blank"), xs_html_attr("href", v), xs_html_text(v))); } @@ -960,6 +961,23 @@ static xs_html *html_user_body(snac *user, int read_only) snac_metadata); } + const char *latitude = xs_dict_get_def(user->config, "latitude", ""); + const char *longitude = xs_dict_get_def(user->config, "longitude", ""); + + if (*latitude && *longitude) { + xs *label = xs_fmt(L("%s,%s"), latitude, longitude); + xs *url = xs_fmt(L("https://openstreetmap.org/search?query=%s,%s"), + latitude, longitude); + + xs_html_add(top_user, + xs_html_tag("p", + xs_html_text(L("Location: ")), + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("target", "_blank"), + xs_html_text(label)))); + } + if (xs_is_true(xs_dict_get(user->config, "show_contact_metrics"))) { xs *fwers = follower_list(user); xs *fwing = following_list(user); -- cgit v1.2.3 From 538f6970f86323dca734436861fe19193fae87e5 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 08:29:57 +0100 Subject: If a location is defined, user actors include a 'Place' object. --- activitypub.c | 27 +++++++++++++++++++++++++++ html.c | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index ecc0533..3bd6220 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1184,6 +1184,28 @@ xs_dict *msg_repulsion(snac *user, const char *id, const char *type) } +xs_dict *msg_place(snac *user, const char *label) +/* creates a Place object, if the user has a location defined */ +{ + xs *place = NULL; + const char *latitude = xs_dict_get_def(user->config, "latitude", ""); + const char *longitude = xs_dict_get_def(user->config, "longitude", ""); + + if (*latitude && *longitude) { + xs *d_la = xs_number_new(atof(latitude)); + xs *d_lo = xs_number_new(atof(longitude)); + + place = msg_base(user, "Place", NULL, user->actor, NULL, NULL); + + place = xs_dict_set(place, "name", label); + place = xs_dict_set(place, "latitude", d_la); + place = xs_dict_set(place, "longitude", d_lo); + } + + return place; +} + + xs_dict *msg_actor(snac *snac) /* create a Person message for this actor */ { @@ -1340,6 +1362,11 @@ xs_dict *msg_actor(snac *snac) msg = xs_dict_set(msg, "manuallyApprovesFollowers", xs_stock(xs_is_true(manually) ? XSTYPE_TRUE : XSTYPE_FALSE)); + /* if there are location coords, create a Place object */ + xs *location = msg_place(snac, "Home"); + if (xs_type(location) == XSTYPE_DICT) + msg = xs_dict_set(msg, "location", location); + /* cache it */ snac_debug(snac, 1, xs_fmt("Caching actor %s", snac->actor)); object_add_ow(snac->actor, msg); diff --git a/html.c b/html.c index eb4ab1f..3498b93 100644 --- a/html.c +++ b/html.c @@ -1311,7 +1311,7 @@ xs_html *html_top_controls(snac *snac) xs_html_attr("for", "show_contact_metrics"), xs_html_text(L("Publish follower and following metrics")))), xs_html_tag("p", - xs_html_text(L("Home location:")), + xs_html_text(L("Current location:")), xs_html_sctag("br", NULL), xs_html_sctag("input", xs_html_attr("type", "text"), -- cgit v1.2.3 From ef7c257c5c2fb8e696ea3bcdd279d46e09e5b20d Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 08:34:48 +0100 Subject: Fixed crash in msg_place() because I'm a moron. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 3bd6220..4966482 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1187,7 +1187,7 @@ xs_dict *msg_repulsion(snac *user, const char *id, const char *type) xs_dict *msg_place(snac *user, const char *label) /* creates a Place object, if the user has a location defined */ { - xs *place = NULL; + xs_dict *place = NULL; const char *latitude = xs_dict_get_def(user->config, "latitude", ""); const char *longitude = xs_dict_get_def(user->config, "longitude", ""); -- cgit v1.2.3 From 9fec8fc5ca8b3a45510183b839f15375c2a66695 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 08:47:01 +0100 Subject: Fixed crash. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 3498b93..5ad55a3 100644 --- a/html.c +++ b/html.c @@ -2227,7 +2227,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, /* show all hashtags that has not been shown previously in the content */ const xs_list *tags = xs_dict_get(msg, "tag"); if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { - const char *o_content = xs_dict_get(msg, "content"); + const char *o_content = xs_dict_get_def(msg, "content", ""); xs *content = xs_utf8_to_lower(o_content); const xs_dict *tag; -- cgit v1.2.3 From 6a5ea9783aac7947fed8b40cc78f67e11bb9a99c Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 Jan 2025 09:52:20 +0100 Subject: If a post has a location, show it. --- html.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/html.c b/html.c index 5ad55a3..fbaa5d1 100644 --- a/html.c +++ b/html.c @@ -2224,6 +2224,43 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, au_tag); } + /* does it have a location? */ + const xs_dict *location = xs_dict_get(msg, "location"); + if (xs_type(location) == XSTYPE_DICT) { + const xs_number *latitude = xs_dict_get(location, "latitude"); + const xs_number *longitude = xs_dict_get(location, "longitude"); + const char *name = xs_dict_get(location, "name"); + const char *address = xs_dict_get(location, "address"); + xs *label_list = xs_list_new(); + + if (xs_type(name) == XSTYPE_STRING) + label_list = xs_list_append(label_list, name); + if (xs_type(address) == XSTYPE_STRING) + label_list = xs_list_append(label_list, address); + + if (xs_list_len(label_list)) { + xs *label = xs_join(label_list, ", "); + + if (!xs_is_null(latitude) && !xs_is_null(longitude)) { + xs *url = xs_fmt("https://openstreetmap.org/search/?query=%s,%s", + xs_number_str(latitude), xs_number_str(longitude)); + + xs_html_add(snac_content_wrap, + xs_html_tag("p", + xs_html_text(L("Location: ")), + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("target", "_blank"), + xs_html_text(label)))); + } + else + xs_html_add(snac_content_wrap, + xs_html_tag("p", + xs_html_text(L("Location: ")), + xs_html_text(label))); + } + } + /* show all hashtags that has not been shown previously in the content */ const xs_list *tags = xs_dict_get(msg, "tag"); if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { -- cgit v1.2.3 From ff44142b30ceded6ed7db24d033296b0966edb27 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 Jan 2025 01:39:37 +0100 Subject: Moved creation of "quiet public" posts to msg_note(), where it belongs. --- activitypub.c | 8 +++++++- main.c | 11 ++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/activitypub.c b/activitypub.c index 4966482..369423d 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1486,7 +1486,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, xs_list *p; const xs_val *v; - /* FIXME: implement scopes 2 and 3 */ + /* FIXME: implement scope 3 */ int priv = scope == 1; if (rcpts == NULL) @@ -1606,6 +1606,12 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, } } + if (scope == 2) { + /* Mastodon's "quiet public": add public address to cc */ + if (xs_list_in(cc, public_address) == -1) + cc = xs_list_append(cc, public_address); + } + else /* no recipients? must be for everybody */ if (!priv && xs_list_len(to) == 0) to = xs_list_append(to, public_address); diff --git a/main.c b/main.c index 5b1dd61..83b73af 100644 --- a/main.c +++ b/main.c @@ -668,15 +668,8 @@ int main(int argc, char *argv[]) else content = xs_dup(url); - msg = msg_note(&snac, content, NULL, NULL, attl, 0, getenv("LANG")); - - if (strcmp(cmd, "note_unlisted") == 0) { - /* according to Mastodon, "unlisted" posts (now called "quiet public") - has the public address as a cc instead of to, so toggle it */ - xs *to = xs_dup(xs_dict_get(msg, "to")); - msg = xs_dict_set(msg, "cc", to); - msg = xs_dict_set(msg, "to", xs_stock(XSTYPE_LIST)); - } + msg = msg_note(&snac, content, NULL, NULL, attl, + strcmp(cmd, "note_unlisted") == 0 ? 2 : 0, getenv("LANG")); c_msg = msg_create(&snac, msg); -- cgit v1.2.3 From 9d4d740220c79e13d2c1f95ba91405f24c90b287 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 Jan 2025 01:48:19 +0100 Subject: mastoapi: obey the "unlisted" visibility set by apps. --- mastoapi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 4b89d12..16065ec 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2650,8 +2650,14 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, } /* prepare the message */ - xs *msg = msg_note(&snac, content, NULL, irt, attach_list, - strcmp(visibility, "public") == 0 ? 0 : 1, language); + int scope = 1; + if (strcmp(visibility, "unlisted") == 0) + scope = 2; + else + if (strcmp(visibility, "public") == 0) + scope = 0; + + xs *msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language); if (!xs_is_null(summary) && *summary) { msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); -- cgit v1.2.3 From 9d650bd2c94eb324530aa40aa65001da0c64c94c Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 04:27:07 +0000 Subject: Added sandbox.o dependency to Makefile.NetBSD. --- Makefile.NetBSD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.NetBSD b/Makefile.NetBSD index f14eb6e..93222b2 100644 --- a/Makefile.NetBSD +++ b/Makefile.NetBSD @@ -5,7 +5,7 @@ LDFLAGS=-lrt all: snac -snac: snac.o main.o data.o http.o httpd.o webfinger.o \ +snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \ activitypub.o html.o utils.o format.o upgrade.o mastoapi.o $(CC) $(CFLAGS) -L/usr/pkg/lib *.o -lcurl -lcrypto -pthread $(LDFLAGS) -Wl,-rpath,/usr/lib -Wl,-rpath,/usr/pkg/lib -o $@ -- cgit v1.2.3 From 1ac3f65e0a0ef2aebf2b88dacfd202b4b649ac65 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 05:29:09 +0100 Subject: Renamed msg_place() to msg_actor_place(). --- activitypub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index 369423d..862f943 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1184,7 +1184,7 @@ xs_dict *msg_repulsion(snac *user, const char *id, const char *type) } -xs_dict *msg_place(snac *user, const char *label) +xs_dict *msg_actor_place(snac *user, const char *label) /* creates a Place object, if the user has a location defined */ { xs_dict *place = NULL; @@ -1363,7 +1363,7 @@ xs_dict *msg_actor(snac *snac) xs_stock(xs_is_true(manually) ? XSTYPE_TRUE : XSTYPE_FALSE)); /* if there are location coords, create a Place object */ - xs *location = msg_place(snac, "Home"); + xs *location = msg_actor_place(snac, "Home"); if (xs_type(location) == XSTYPE_DICT) msg = xs_dict_set(msg, "location", location); -- cgit v1.2.3 From 9fd234d05edd9b6690fd9f14546f93b29e8379cf Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 06:59:16 +0100 Subject: Backport from xs. --- xs.h | 16 ++++++++++++---- xs_version.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/xs.h b/xs.h index a6e40d5..2a90467 100644 --- a/xs.h +++ b/xs.h @@ -1061,14 +1061,15 @@ xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *va typedef struct { int value_offset; /* offset to value (from dict start) */ - int next; /* next node in sequential search */ + int next; /* next node in sequential scanning */ int child[4]; /* child nodes in hashed search */ char key[]; /* C string key */ } ditem_hdr; typedef struct { int size; /* size of full dict (_XS_TYPE_SIZE) */ - int first; /* first node for sequential search */ + int first; /* first node for sequential scanning */ + int last; /* last node for sequential scanning */ int root; /* root node for hashed search */ /* a bunch of ditem_hdr and value follows */ } dict_hdr; @@ -1153,8 +1154,15 @@ xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value) memcpy(dict + di->value_offset, value, vsz); /* chain to the sequential list */ - di->next = dh->first; - dh->first = end; + if (dh->first == 0) + dh->first = end; + else { + /* chain this new element to the last one */ + ditem_hdr *dil = (ditem_hdr *)(dict + dh->last); + dil->next = end; + } + + dh->last = end; } else { /* ditem already exists */ diff --git a/xs_version.h b/xs_version.h index fc3b755..ded4335 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 9e8f5cf300ffbf453031f2ec923cef0822a41b41 2025-01-08T16:57:26+01:00 */ +/* c317231894f28c39ba45a46f493f124d12a12f3a 2025-01-12T06:56:21+01:00 */ -- cgit v1.2.3 From a0fd51eec8a513a5c3ff9b4cf36dbdfc9d908339 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 07:22:08 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ae68d50..d68824d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -8,6 +8,8 @@ Added some caching for local `Actor` objects. Hashtags that are not explicitly linked in a post's content are shown below. +Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 8ab30e736535859547559c8f0d4fbff638e71dff Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 08:06:55 +0100 Subject: Updated TODO. --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 8ca0b14..999ea1c 100644 --- a/TODO.md +++ b/TODO.md @@ -22,7 +22,7 @@ Mastoapi: implement /v1/conversations. Implement following of hashtags (this is not trivial). -Track 'Event' data types standardization; how to add plan-to-attend and similar activities (more info: https://event-federation.eu/) +Track 'Event' data types standardization; how to add plan-to-attend and similar activities (more info: https://event-federation.eu/). Friendica interacts with events via activities `Accept` (will go), `TentativeAccept` (will try to go) or `Reject` (cannot go) (`object` field as id, not object). `Undo` for any of these activities cancel (`object` as an object, not id). Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md -- cgit v1.2.3 From 5d2a5f160adc0992178812bcaa72df9cd5f072a7 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 09:32:09 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d68824d..28bcf56 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,8 @@ Hashtags that are not explicitly linked in a post's content are shown below. Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). +The user profile can now include longitude and latitude data for your current location. + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From f88a32dbe0805fceb3fb75b01de61616d7953f5e Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 11:30:16 +0100 Subject: mastoapi: fixed Events not being shown. --- main.c | 16 ++++++++++++++++ mastoapi.c | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 83b73af..b4e7fa1 100644 --- a/main.c +++ b/main.c @@ -351,6 +351,22 @@ int main(int argc, char *argv[]) return 0; } + + if (strcmp(cmd, "assist") == 0) { /** **/ + /* undocumented: experimental (do not use) */ + xs *msg = msg_admiration(&snac, url, "Accept"); + + if (msg != NULL) { + enqueue_message(&snac, msg); + + if (dbglevel) { + xs_json_dump(msg, 4, stdout); + } + } + + return 0; + } + if (strcmp(cmd, "unboost") == 0) { /** **/ xs *msg = msg_repulsion(&snac, url, "Announce"); diff --git a/mastoapi.c b/mastoapi.c index 16065ec..21e2a78 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1428,9 +1428,9 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn continue; } - /* if it has a name and it's not a Page or a Video, + /* if it has a name and it's not an object that may have one, it's a poll vote, so discard it */ - if (!xs_is_null(xs_dict_get(msg, "name")) && !xs_match(type, "Page|Video")) + if (!xs_is_null(xs_dict_get(msg, "name")) && !xs_match(type, "Page|Video|Audio|Event")) continue; /* convert the Note into a Mastodon status */ -- cgit v1.2.3 From 6154ce75826ccb4ade18a45b838d159cd26d81f8 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 11:57:30 +0100 Subject: If an Event has an URL, use it. --- html.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/html.c b/html.c index fbaa5d1..1e890e3 100644 --- a/html.c +++ b/html.c @@ -2239,8 +2239,19 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, label_list = xs_list_append(label_list, address); if (xs_list_len(label_list)) { + const char *url = xs_dict_get(location, "url"); xs *label = xs_join(label_list, ", "); + if (xs_type(url) == XSTYPE_STRING) { + xs_html_add(snac_content_wrap, + xs_html_tag("p", + xs_html_text(L("Location: ")), + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("target", "_blank"), + xs_html_text(label)))); + } + else if (!xs_is_null(latitude) && !xs_is_null(longitude)) { xs *url = xs_fmt("https://openstreetmap.org/search/?query=%s,%s", xs_number_str(latitude), xs_number_str(longitude)); -- cgit v1.2.3 From 6d0b0cdaab829c8c0d98b0f3cdc5d3526f1c5662 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 14:31:45 +0100 Subject: Show event start / end times. This code is a ugly as fuck. --- html.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/html.c b/html.c index 1e890e3..6a5aa10 100644 --- a/html.c +++ b/html.c @@ -2272,6 +2272,49 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } } + if (strcmp(type, "Event") == 0) { /** Event start and end times **/ + const char *s_time = xs_dict_get(msg, "startTime"); + + if (xs_type(s_time) == XSTYPE_STRING) { + const char *e_time = xs_dict_get(msg, "endTime"); + const char *tz = xs_dict_get(msg, "timezone"); + + xs *s = xs_replace_i(xs_dup(s_time), "T", " "); + xs *e = NULL; + + if (xs_type(e_time) == XSTYPE_STRING) + e = xs_replace_i(xs_dup(e_time), "T", " "); + + /* if the event has a timezone, crop the offsets */ + if (xs_type(tz) == XSTYPE_STRING) { + s = xs_crop_i(s, 0, 19); + + if (e) + e = xs_crop_i(e, 0, 19); + } + else + tz = ""; + + /* if start and end share the same day, crop it from the end */ + if (e && memcmp(s, e, 11) == 0) + e = xs_crop_i(e, 11, strlen(e)); + + if (e) + s = xs_str_cat(s, " / ", e); + + if (*tz) + s = xs_str_cat(s, " (", tz, ")"); + + /* replace ugly decimals */ + s = xs_replace_i(s, ".000", ""); + + xs_html_add(snac_content_wrap, + xs_html_tag("p", + xs_html_text(L("Time: ")), + xs_html_text(s))); + } + } + /* show all hashtags that has not been shown previously in the content */ const xs_list *tags = xs_dict_get(msg, "tag"); if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { -- cgit v1.2.3 From 5aaa6192ef08de7ab6c63e85708a1c825e9ff5f1 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 14:59:28 +0100 Subject: Better dangling hashtag checking. --- html.c | 14 +++++++------- xs.h | 3 +++ xs_version.h | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/html.c b/html.c index 6a5aa10..322bafa 100644 --- a/html.c +++ b/html.c @@ -2317,8 +2317,9 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, /* show all hashtags that has not been shown previously in the content */ const xs_list *tags = xs_dict_get(msg, "tag"); - if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags)) { - const char *o_content = xs_dict_get_def(msg, "content", ""); + const char *o_content = xs_dict_get_def(msg, "content", ""); + + if (xs_is_string(o_content) && xs_is_list(tags) && xs_list_len(tags)) { xs *content = xs_utf8_to_lower(o_content); const xs_dict *tag; @@ -2328,16 +2329,15 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_list_foreach(tags, tag) { const char *type = xs_dict_get(tag, "type"); - if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Hashtag") == 0) { + if (xs_is_string(type) && strcmp(type, "Hashtag") == 0) { const char *o_href = xs_dict_get(tag, "href"); + const char *name = xs_dict_get(tag, "name"); - if (xs_type(o_href) == XSTYPE_STRING) { + if (xs_is_string(o_href) && xs_is_string(name)) { xs *href = xs_utf8_to_lower(o_href); - if (xs_str_in(content, href) == -1) { + if (xs_str_in(content, href) == -1 && xs_str_in(content, name) == -1) { /* not in the content: add here */ - const char *name = xs_dict_get(tag, "name"); - xs_html_add(add_hashtags, xs_html_tag("li", xs_html_tag("a", diff --git a/xs.h b/xs.h index 2a90467..45952c9 100644 --- a/xs.h +++ b/xs.h @@ -157,6 +157,9 @@ unsigned int xs_hash_func(const char *data, int size); #define xs_is_true(v) (xs_type((v)) == XSTYPE_TRUE) #define xs_is_false(v) (xs_type((v)) == XSTYPE_FALSE) #define xs_not(v) xs_stock(xs_is_true((v)) ? XSTYPE_FALSE : XSTYPE_TRUE) +#define xs_is_string(v) (xs_type((v)) == XSTYPE_STRING) +#define xs_is_list(v) (xs_type((v)) == XSTYPE_LIST) +#define xs_is_dict(v) (xs_type((v)) == XSTYPE_DICT) #define xs_list_foreach(l, v) for (int ct_##__LINE__ = 0; xs_list_next(l, &v, &ct_##__LINE__); ) #define xs_dict_foreach(l, k, v) for (int ct_##__LINE__ = 0; xs_dict_next(l, &k, &v, &ct_##__LINE__); ) diff --git a/xs_version.h b/xs_version.h index ded4335..ce3d06b 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* c317231894f28c39ba45a46f493f124d12a12f3a 2025-01-12T06:56:21+01:00 */ +/* cebb5663f26bc91b80c787525f5953b97839e0f7 2025-01-12T14:57:12+01:00 */ -- cgit v1.2.3 From ddc8781b3a93d77836b61f3efa884b3c23a1fd35 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 Jan 2025 16:21:20 +0100 Subject: Minor improvements to start / end event time showing. --- html.c | 8 ++++---- xs.h | 5 ++--- xs_version.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/html.c b/html.c index 322bafa..c55937d 100644 --- a/html.c +++ b/html.c @@ -2275,18 +2275,18 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (strcmp(type, "Event") == 0) { /** Event start and end times **/ const char *s_time = xs_dict_get(msg, "startTime"); - if (xs_type(s_time) == XSTYPE_STRING) { + if (xs_is_string(s_time) && strlen(s_time) > 20) { const char *e_time = xs_dict_get(msg, "endTime"); const char *tz = xs_dict_get(msg, "timezone"); xs *s = xs_replace_i(xs_dup(s_time), "T", " "); xs *e = NULL; - if (xs_type(e_time) == XSTYPE_STRING) + if (xs_is_string(e_time) && strlen(e_time) > 20) e = xs_replace_i(xs_dup(e_time), "T", " "); /* if the event has a timezone, crop the offsets */ - if (xs_type(tz) == XSTYPE_STRING) { + if (xs_is_string(tz)) { s = xs_crop_i(s, 0, 19); if (e) @@ -2297,7 +2297,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, /* if start and end share the same day, crop it from the end */ if (e && memcmp(s, e, 11) == 0) - e = xs_crop_i(e, 11, strlen(e)); + e = xs_crop_i(e, 11, 0); if (e) s = xs_str_cat(s, " / ", e); diff --git a/xs.h b/xs.h index 45952c9..05d84f5 100644 --- a/xs.h +++ b/xs.h @@ -626,15 +626,14 @@ int xs_between(const char *prefix, const char *str, const char *suffix) xs_str *xs_crop_i(xs_str *str, int start, int end) /* crops the string to be only from start to end */ { - XS_ASSERT_TYPE(str, XSTYPE_STRING); - int sz = strlen(str); if (end <= 0) end = sz + end; /* crop from the top */ - str[end] = '\0'; + if (end > 0 && end < sz) + str[end] = '\0'; /* crop from the bottom */ str = xs_collapse(str, 0, start); diff --git a/xs_version.h b/xs_version.h index ce3d06b..12f713a 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* cebb5663f26bc91b80c787525f5953b97839e0f7 2025-01-12T14:57:12+01:00 */ +/* b865e89769aedfdbc61251e94451e9d37579f52e 2025-01-12T16:17:47+01:00 */ -- cgit v1.2.3 From a016045a8fb80e4accad618c60b987bad708f45e Mon Sep 17 00:00:00 2001 From: default Date: Mon, 13 Jan 2025 06:17:30 +0100 Subject: Don't describe as 'fatal' what are just non-retriable connection errors. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 862f943..f0d9336 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2706,7 +2706,7 @@ void process_queue_item(xs_dict *q_item) || status == HTTP_STATUS_UNPROCESSABLE_CONTENT || status < 0) /* explicit error: discard */ - srv_log(xs_fmt("output message: fatal error %s %d", inbox, status)); + srv_log(xs_fmt("output message: error %s %d", inbox, status)); else if (retries > queue_retry_max) srv_log(xs_fmt("output message: giving up %s %d", inbox, status)); -- cgit v1.2.3 From 9686ccd916464a1280082a4abba7a5fa3574dd0c Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Tue, 14 Jan 2025 22:17:46 +0000 Subject: Implement limit= on notification fetches --- mastoapi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mastoapi.c b/mastoapi.c index 21e2a78..3e823ed 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1816,6 +1816,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, const xs_list *excl = xs_dict_get(args, "exclude_types[]"); const char *min_id = xs_dict_get(args, "min_id"); const char *max_id = xs_dict_get(args, "max_id"); + const char *limit = xs_dict_get(args, "limit"); + int limit_count = 0; + if (!xs_is_null(limit)) { + limit_count = atoi(limit); + } if (dbglevel) { xs *js = xs_json_dumps(args, 0); @@ -1903,6 +1908,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, } out = xs_list_append(out, mn); + if (!xs_is_null(limit)) { + if (--limit_count <= 0) + break; + } } srv_debug(1, xs_fmt("mastoapi_notifications count %d", xs_list_len(out))); -- cgit v1.2.3 From b5c5c5cb9e1c728a83596dcc7c4191d012fe1332 Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Wed, 15 Jan 2025 00:40:32 +0000 Subject: Implement faster min_id handling --- data.c | 31 +++++++++++++++++++++++++++++++ mastoapi.c | 46 ++++++++++++++++++++++++++++------------------ snac.h | 2 ++ 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/data.c b/data.c index ca63084..33947ff 100644 --- a/data.c +++ b/data.c @@ -679,6 +679,37 @@ int index_desc_first(FILE *f, char md5[MD5_HEX_SIZE], int skip) return 1; } +int index_asc_first(FILE *f,char md5[MD5_HEX_SIZE], const char *seek_md5) +/* reads the first entry of an ascending index, starting from a given md5 */ +{ + fseek(f, SEEK_SET, 0); + while (fread(md5, MD5_HEX_SIZE, 1, f)) { + md5[MD5_HEX_SIZE - 1] = '\0'; + if (strcmp(md5,seek_md5) == 0) { + return index_asc_next(f, md5); + } + } + return 0; +} + +int index_asc_next(FILE *f, char md5[MD5_HEX_SIZE]) +/* reads the next entry of an ascending index */ +{ + for (;;) { + /* read an md5 */ + if (!fread(md5, MD5_HEX_SIZE, 1, f)) + return 0; + + /* deleted, skip */ + if (md5[0] != '-') + break; + } + + md5[MD5_HEX_SIZE - 1] = '\0'; + + return 1; +} + xs_list *index_list_desc(const char *fn, int skip, int show) /* returns an index as a list, in reverse order */ diff --git a/mastoapi.c b/mastoapi.c index 21e2a78..c25f78b 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1339,6 +1339,9 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn const char *since_id = xs_dict_get(args, "since_id"); const char *min_id = xs_dict_get(args, "min_id"); /* unsupported old-to-new navigation */ const char *limit_s = xs_dict_get(args, "limit"); + int (*iterator)(FILE *, char *); + int initial_status = 0; + int ascending = 0; int limit = 0; int cnt = 0; @@ -1348,27 +1351,40 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn if (limit == 0) limit = 20; - if (index_desc_first(f, md5, 0)) { + if (min_id) { + iterator = &index_asc_next; + initial_status = index_asc_first(f, md5, MID_TO_MD5(min_id)); + ascending = 1; + } + else { + iterator = &index_desc_next; + initial_status = index_desc_first(f, md5, 0); + } + + if (initial_status) { do { xs *msg = NULL; /* only return entries older that max_id */ if (max_id) { - if (strcmp(md5, MID_TO_MD5(max_id)) == 0) + if (strcmp(md5, MID_TO_MD5(max_id)) == 0) { max_id = NULL; - - continue; + if (ascending) + break; + } + if (!ascending) + continue; } /* only returns entries newer than since_id */ if (since_id) { - if (strcmp(md5, MID_TO_MD5(since_id)) == 0) - break; - } - - if (min_id) { - if (strcmp(md5, MID_TO_MD5(min_id)) == 0) - break; + if (strcmp(md5, MID_TO_MD5(since_id)) == 0) { + if (!ascending) + break; + since_id = NULL; + } + if (ascending) + continue; } /* get the entry */ @@ -1440,14 +1456,8 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn out = xs_list_append(out, st); cnt++; } - if (min_id) { - while (cnt > limit) { - out = xs_list_del(out, 0); - cnt--; - } - } - } while ((min_id || (cnt < limit)) && index_desc_next(f, md5)); + } while ((cnt < limit) && (*iterator)(f, md5)); } int more = index_desc_next(f, md5); diff --git a/snac.h b/snac.h index 5729a22..65ece5d 100644 --- a/snac.h +++ b/snac.h @@ -108,6 +108,8 @@ int index_len(const char *fn); xs_list *index_list(const char *fn, int max); int index_desc_next(FILE *f, char md5[MD5_HEX_SIZE]); int index_desc_first(FILE *f, char md5[MD5_HEX_SIZE], int skip); +int index_asc_next(FILE *f, char md5[MD5_HEX_SIZE]); +int index_asc_first(FILE *f, char md5[MD5_HEX_SIZE], const char *seek_md5); xs_list *index_list_desc(const char *fn, int skip, int show); int object_add(const char *id, const xs_dict *obj); -- cgit v1.2.3 From f4a2add31008a5d3a42fe8b7780f3ee6716d7940 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jan 2025 06:45:39 +0100 Subject: Reduced RSA key size to 2048. --- utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.c b/utils.c index 336ae2d..8422a9a 100644 --- a/utils.c +++ b/utils.c @@ -330,7 +330,7 @@ int adduser(const char *uid) } printf("\nCreating RSA key...\n"); - key = xs_evp_genkey(4096); + key = xs_evp_genkey(2048); printf("Done.\n"); xs *kfn = xs_fmt("%s/key.json", basedir); -- cgit v1.2.3 From 061fa60171f157c064bceb990a9fbc9ae472b535 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jan 2025 06:47:56 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 28bcf56..2e20bc7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,8 @@ Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). The user profile can now include longitude and latitude data for your current location. +Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 0078f9db6892f984434cc34dbbae1ec5c31d1a90 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jan 2025 06:54:04 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2e20bc7..37a8f97 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,8 @@ Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). The user profile can now include longitude and latitude data for your current location. +Mastodon API: implemented limit= on notification fetches (contributed by nowster). + Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. ## 2.68 -- cgit v1.2.3 From ad5455f5556b2057b6ddfce8e0656c04112737de Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jan 2025 06:58:20 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 37a8f97..ae96c92 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,7 +12,7 @@ Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). The user profile can now include longitude and latitude data for your current location. -Mastodon API: implemented limit= on notification fetches (contributed by nowster). +Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster). Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. -- cgit v1.2.3 From ba4c3508f76466e79e9f3b5f665d72f0e425c4aa Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Jan 2025 09:29:18 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ae96c92..f99f3e0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,17 +2,17 @@ ## UNRELEASED -Some fixes to accept `Event` objects properly (like those coming from implementations like https://gancio.org/). +Some fixes to accept `Event` objects properly (like those coming from implementations like https://gancio.org/ or https://mobilizon.fr). Added some caching for local `Actor` objects. -Hashtags that are not explicitly linked in a post's content are shown below. +Hashtags that are not explicitly linked in a post's content are shown below it. Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). The user profile can now include longitude and latitude data for your current location. -Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster). +Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster), obey the quiet public visibility set for posts. Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. -- cgit v1.2.3 From b993e26346f885586ff0533a3b309ed7d1e910cf Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Jan 2025 14:20:07 +0100 Subject: Implemented Mastodon-like /authorize_interaction. --- html.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ httpd.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/html.c b/html.c index c55937d..aad2f31 100644 --- a/html.c +++ b/html.c @@ -3124,6 +3124,21 @@ int html_get_handler(const xs_dict *req, const char *q_path, else return HTTP_STATUS_NOT_FOUND; } + else + if (strcmp(v, "auth-int-bridge") == 0) { + const char *login = xs_dict_get(q_vars, "login"); + const char *id = xs_dict_get(q_vars, "id"); + const char *action = xs_dict_get(q_vars, "action"); + + if (xs_is_string(login) && xs_is_string(id) && xs_is_string(action)) { + *body = xs_fmt("%s/%s/authorize_interaction?action=%s&id=%s", + srv_baseurl, login, action, id); + + return HTTP_STATUS_SEE_OTHER; + } + else + return HTTP_STATUS_NOT_FOUND; + } uid = xs_dup(v); @@ -3695,6 +3710,52 @@ int html_get_handler(const xs_dict *req, const char *q_path, status = HTTP_STATUS_SEE_OTHER; } } + else + if (strcmp(p_path, "authorize_interaction") == 0) { /** follow, like or boost from Mastodon **/ + if (!login(&snac, req)) { + *body = xs_dup(uid); + status = HTTP_STATUS_UNAUTHORIZED; + } + else { + status = HTTP_STATUS_NOT_FOUND; + + const char *id = xs_dict_get(q_vars, "id"); + const char *action = xs_dict_get(q_vars, "action"); + + if (xs_is_string(id) && xs_is_string(action)) { + if (strcmp(action, "Follow") == 0) { + xs *msg = msg_follow(&snac, id); + + if (msg != NULL) { + const char *actor = xs_dict_get(msg, "object"); + + following_add(&snac, actor, msg); + + enqueue_output_by_actor(&snac, msg, actor, 0); + + status = HTTP_STATUS_SEE_OTHER; + } + } + else + if (xs_match(action, "Like|Boost|Announce")) { + /* bring the post */ + xs *msg = msg_admiration(&snac, id, *action == 'L' ? "Like" : "Announce"); + + if (msg != NULL) { + enqueue_message(&snac, msg); + timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, *action == 'L' ? 1 : 0); + + status = HTTP_STATUS_SEE_OTHER; + } + } + } + + if (status == HTTP_STATUS_SEE_OTHER) { + *body = xs_fmt("%s/admin", snac.actor); + *b_size = strlen(*body); + } + } + } else status = HTTP_STATUS_NOT_FOUND; diff --git a/httpd.c b/httpd.c index dda40b9..74628e3 100644 --- a/httpd.c +++ b/httpd.c @@ -182,6 +182,29 @@ const char *share_page = "" ""; +const char *authorize_interaction_page = "" +"\n" +"\n" +"\n" +"%s - snac\n" +"\n" +"\n" +"\n" +"\n" +"

%s authorize interaction

\n" +"
\n" +" %s\n" +"\n" +"

Login:

\n" +"\n" +"

%s

\n" +""; + + int server_get_handler(xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) /* basic server services */ @@ -318,6 +341,25 @@ int server_get_handler(xs_dict *req, const char *q_path, USER_AGENT ); } + else + if (strcmp(q_path, "/authorize_interaction") == 0) { + const xs_dict *q_vars = xs_dict_get(req, "q_vars"); + const char *uri = xs_dict_get(q_vars, "uri"); + + if (xs_is_string(uri)) { + status = HTTP_STATUS_OK; + *ctype = "text/html; charset=utf-8"; + *body = xs_fmt(authorize_interaction_page, + xs_dict_get(srv_config, "host"), + srv_baseurl, + xs_dict_get(srv_config, "host"), + srv_baseurl, + uri, + uri, + USER_AGENT + ); + } + } if (status != 0) srv_debug(1, xs_fmt("server_get_handler serving '%s' %d", q_path, status)); -- cgit v1.2.3 From 9de9295837b6b7536be4d9b621d41fd1c22bd750 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Jan 2025 14:21:54 +0100 Subject: Updated documentation. --- doc/snac.8 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/snac.8 b/doc/snac.8 index 3a4bfcf..3c36f7b 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -683,6 +683,12 @@ location /share { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $remote_addr; } +# optional (Mastodon-like "authorize interaction" entrypoint) +location /authorize_interaction { + proxy_pass http://localhost:8001; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $remote_addr; +} .Ed .Pp Restart the nginx daemon and connect to @@ -736,6 +742,11 @@ ProxyPreserveHost On ProxyPass http://127.0.0.1:8001/share + +# optional (Mastodon-like "authorize interaction" entrypoint) + + ProxyPass http://127.0.0.1:8001/share + .Ed .Pp Since version 2.43, @@ -795,6 +806,10 @@ location "/.well-known/host-meta" { location "/share" { fastcgi socket tcp "127.0.0.1" 8001 } + +location "/authorize_interaction" { + fastcgi socket tcp "127.0.0.1" 8001 +} .Ed .Sh SEE ALSO .Xr snac 1 , -- cgit v1.2.3 From 8b1ee84788bd0e7ede7904cc5653ec8310e37c2b Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Jan 2025 14:24:37 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f99f3e0..6776410 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,8 @@ ## UNRELEASED +Added support for a Mastodon-like `/authorize_interaction` webpoint entry, that allows following, liking and boosting from another account's Mastodon public web interface. To be able to use it, you must reconfigure your https proxy to redirect `/authorize_interaction` to snac (see `snac(8)`). + Some fixes to accept `Event` objects properly (like those coming from implementations like https://gancio.org/ or https://mobilizon.fr). Added some caching for local `Actor` objects. -- cgit v1.2.3 From 18b2228205f1ccf16415cdb4694787c90ad05a07 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Jan 2025 14:46:12 +0100 Subject: Updated TODO. --- TODO.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 999ea1c..86c706a 100644 --- a/TODO.md +++ b/TODO.md @@ -32,8 +32,6 @@ Integrate "Added handling for International Domain Names" PR https://codeberg.or Do something about Akkoma and Misskey's quoted replies (they use the `quoteUrl` field instead of `inReplyTo`). -Add support for /authorize_interaction (whatever it is). - Add a list of hashtags to drop. Take a look at crashes in the brittle Mastodon official app (crashes when hitting the reply button, crashes or 'ownVotes is null' errors when trying to show polls). @@ -365,3 +363,5 @@ Unfollowing lemmy groups gets rejected with an http status of 400 (it seems to w CSV import/export does not work with OpenBSD security on; document it or fix it (2025-01-04T19:35:09+0100). Add support for /share?text=tt&website=url (whatever it is, see https://mastodonshare.com/ for details) (2025-01-06T18:43:52+0100). + +Add support for /authorize_interaction (whatever it is) (2025-01-16T14:45:28+0100). -- cgit v1.2.3 From 04803b628b44acb773ff972a4c12071cbcf49b23 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Jan 2025 18:19:19 +0100 Subject: Changed Boost/Like order in the /authorize_interaction page. --- httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpd.c b/httpd.c index 74628e3..7222893 100644 --- a/httpd.c +++ b/httpd.c @@ -195,8 +195,8 @@ const char *authorize_interaction_page = "" "
\n" " %s\n" "\n" "

Login:

\n" -- cgit v1.2.3 From f3c9bd1ae5cbc82d2a4e81f8aac90f91c2045b45 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 17 Jan 2025 10:14:15 +0100 Subject: Fixed the greeting.html template to have a closing head tag. --- utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.c b/utils.c index 8422a9a..a5b1124 100644 --- a/utils.c +++ b/utils.c @@ -97,7 +97,7 @@ static const char *greeting_html = "\n" "\n" "\n" - "Welcome to %host%\n" + "Welcome to %host%\n\n" "\n" "%blurb%" "

The following users are part of this community:

\n" -- cgit v1.2.3 From 2ab9db4175cde75889bb43d0805d96759d6215bc Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 16:38:13 +0100 Subject: Added controls for the followed hashtags. --- html.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/html.c b/html.c index aad2f31..3ad330d 100644 --- a/html.c +++ b/html.c @@ -1355,6 +1355,38 @@ xs_html *html_top_controls(snac *snac) xs_html_attr("class", "button"), xs_html_attr("value", L("Update user info"))))))); + xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", snac->actor); + xs *followed_hashtags = xs_join(xs_dict_get_def(snac->config, + "followed_hashtags", xs_stock(XSTYPE_LIST)), "\n"); + + xs_html_add(top_controls, + xs_html_tag("details", + xs_html_tag("summary", + xs_html_text(L("Followed hashtags..."))), + xs_html_tag("p", + xs_html_text(L("One hashtag per line"))), + xs_html_tag("div", + xs_html_attr("class", "snac-followed-hashtags"), + xs_html_tag("form", + xs_html_attr("autocomplete", "off"), + xs_html_attr("method", "post"), + xs_html_attr("action", followed_hashtags_action), + xs_html_attr("enctype", "multipart/form-data"), + + xs_html_tag("textarea", + xs_html_attr("name", "hashtags"), + xs_html_attr("cols", "40"), + xs_html_attr("rows", "4"), + xs_html_attr("placeholder", "#cats\n#windowfriday\n#classicalmusic"), + xs_html_text(followed_hashtags)), + + xs_html_tag("br", NULL), + + xs_html_sctag("input", + xs_html_attr("type", "submit"), + xs_html_attr("class", "button"), + xs_html_attr("value", L("Update hashtags"))))))); + return top_controls; } -- cgit v1.2.3 From ba5d978bf1385647d0f696f4d4cb3e3dd1cc544d Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 17:02:43 +0100 Subject: Update user config from admin/followed-hashtags. --- html.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 3ad330d..09dd202 100644 --- a/html.c +++ b/html.c @@ -1374,7 +1374,7 @@ xs_html *html_top_controls(snac *snac) xs_html_attr("enctype", "multipart/form-data"), xs_html_tag("textarea", - xs_html_attr("name", "hashtags"), + xs_html_attr("name", "followed_hashtags"), xs_html_attr("cols", "40"), xs_html_attr("rows", "4"), xs_html_attr("placeholder", "#cats\n#windowfriday\n#classicalmusic"), @@ -4390,6 +4390,35 @@ int html_post_handler(const xs_dict *req, const char *q_path, status = HTTP_STATUS_SEE_OTHER; } + else + if (p_path && strcmp(p_path, "admin/followed-hashtags") == 0) { /** **/ + const char *followed_hashtags = xs_dict_get(p_vars, "followed_hashtags"); + + if (xs_is_string(followed_hashtags)) { + xs *new_hashtags = xs_list_new(); + xs *l = xs_split(followed_hashtags, "\n"); + const char *v; + + xs_list_foreach(l, v) { + xs *s1 = xs_strip_i(xs_dup(v)); + s1 = xs_replace_i(s1, " ", ""); + + if (*s1 == '\0') + continue; + + xs *s2 = xs_utf8_to_lower(s1); + if (*s2 != '#') + s2 = xs_str_prepend_i(s2, "#"); + + new_hashtags = xs_list_append(new_hashtags, s2); + } + + snac.config = xs_dict_set(snac.config, "followed_hashtags", new_hashtags); + user_persist(&snac, 0); + } + + status = HTTP_STATUS_SEE_OTHER; + } if (status == HTTP_STATUS_SEE_OTHER) { const char *redir = xs_dict_get(p_vars, "redir"); -- cgit v1.2.3 From 9be32a3061f20add0f159e2e9c805754aa4ecaf5 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 17:13:23 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6776410..b99fadf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,10 @@ ## UNRELEASED +Added support for subscribing to LitePub (Pleroma-style) Fediverse Relays like e.g. https://fedi-relay.gyptazy.com to improve federation. See `snac(8)` (the Administrator Manual) for more information on how to use this feature. + +Added support for following hashtags. This is only useful if your instance is subscribed to relays (see above). + Added support for a Mastodon-like `/authorize_interaction` webpoint entry, that allows following, liking and boosting from another account's Mastodon public web interface. To be able to use it, you must reconfigure your https proxy to redirect `/authorize_interaction` to snac (see `snac(8)`). Some fixes to accept `Event` objects properly (like those coming from implementations like https://gancio.org/ or https://mobilizon.fr). -- cgit v1.2.3 From a80b15e478e303feaddb5d357502896f3d53dff8 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 17:32:24 +0100 Subject: Updated documentation. --- doc/snac.8 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/snac.8 b/doc/snac.8 index 3c36f7b..141fa27 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -583,6 +583,27 @@ to pass the remote connection address in the .Ic X-Forwarded-For HTTP header (unless you use the FastCGI interface; if that's the case, you don't have to do anything). +.Pp +.Ss Subscribing to Fediverse Relays +Since version 2.69, a +.Nm +instance can subscribe to LitePub (Pleroma-style) Fediverse Relays. Doing this improves +visibility and allows following hashtags. To do this, you must create a special user named +relay and, from it, follow the relay actor(s) like you do with regular actor URLs. This +special user will start receiving boosts from the relay server of posts from other instances +also following it. It any other user of the same +.Nm +instance follows any of the hashtags included in these boosted posts coming from the relay, +they will received as if they were for them. +.Pp +Example: +.Bd -literal -offset indent +snac adduser $SNAC_BASEDIR relay # only needed once +snac follow $SNAC_BASEDIR relay https://fedi-relay.gyptazy.com/actor +.Ed +.Pp +Lowering the data retention time for this special user is recommended, as the amount of +received posts can be big. .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From 4127ec4100ee6f87f42afacba185486ade670966 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 18:20:27 +0100 Subject: Also relay notes to the relays that follow us. --- activitypub.c | 58 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/activitypub.c b/activitypub.c index f0d9336..18b3c86 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2485,7 +2485,7 @@ int send_email(const char *msg) } -void process_user_queue_item(snac *snac, xs_dict *q_item) +void process_user_queue_item(snac *user, xs_dict *q_item) /* processes an item from the user queue */ { const char *type; @@ -2496,7 +2496,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) if (strcmp(type, "message") == 0) { const xs_dict *msg = xs_dict_get(q_item, "message"); - xs *rcpts = recipient_list(snac, msg, 1); + xs *rcpts = recipient_list(user, msg, 1); xs_set inboxes; const xs_str *actor; @@ -2505,7 +2505,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) /* add this shared inbox first */ xs *this_shared_inbox = xs_fmt("%s/shared-inbox", srv_baseurl); xs_set_add(&inboxes, this_shared_inbox); - enqueue_output(snac, msg, this_shared_inbox, 0, 0); + enqueue_output(user, msg, this_shared_inbox, 0, 0); /* iterate the recipients */ xs_list_foreach(rcpts, actor) { @@ -2516,10 +2516,10 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) if (inbox != NULL) { /* add to the set and, if it's not there, send message */ if (xs_set_add(&inboxes, inbox) == 1) - enqueue_output(snac, msg, inbox, 0, 0); + enqueue_output(user, msg, inbox, 0, 0); } else - snac_log(snac, xs_fmt("cannot find inbox for %s", actor)); + snac_log(user, xs_fmt("cannot find inbox for %s", actor)); } } @@ -2531,12 +2531,36 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) xs_list_foreach(shibx, inbox) { if (xs_set_add(&inboxes, inbox) == 1) - enqueue_output(snac, msg, inbox, 0, 0); + enqueue_output(user, msg, inbox, 0, 0); } } } xs_set_free(&inboxes); + + /* relay this note */ + if (strcmp(user->uid, "relay") != 0) { /* avoid loops */ + snac relay; + if (user_open(&relay, "relay")) { + /* a 'relay' user exists */ + const char *type = xs_dict_get(msg, "type"); + + if (xs_is_string(type) && strcmp(type, "Create") == 0) { + const xs_val *object = xs_dict_get(msg, "object"); + + if (xs_is_dict(object)) { + object = xs_dict_get(object, "id"); + + snac_debug(&relay, 1, xs_fmt("relaying message %s", object)); + + xs *boost = msg_admiration(&relay, object, "Announce"); + enqueue_message(&relay, boost); + } + } + + user_free(&relay); + } + } } else if (strcmp(type, "input") == 0) { @@ -2548,13 +2572,13 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) if (xs_is_null(msg)) return; - if (!process_input_message(snac, msg, req)) { + if (!process_input_message(user, msg, req)) { if (retries > queue_retry_max) - snac_log(snac, xs_fmt("input giving up")); + snac_log(user, xs_fmt("input giving up")); else { /* reenqueue */ - enqueue_input(snac, msg, req, retries + 1); - snac_log(snac, xs_fmt("input requeue #%d", retries + 1)); + enqueue_input(user, msg, req, retries + 1); + snac_log(user, xs_fmt("input requeue #%d", retries + 1)); } } } @@ -2564,7 +2588,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) const char *id = xs_dict_get(q_item, "message"); if (!xs_is_null(id)) - update_question(snac, id); + update_question(user, id); } else if (strcmp(type, "object_request") == 0) { @@ -2574,17 +2598,17 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) int status; xs *data = NULL; - status = activitypub_request(snac, id, &data); + status = activitypub_request(user, id, &data); if (valid_status(status)) object_add_ow(id, data); - snac_debug(snac, 1, xs_fmt("object_request %s %d", id, status)); + snac_debug(user, 1, xs_fmt("object_request %s %d", id, status)); } } else if (strcmp(type, "verify_links") == 0) { - verify_links(snac); + verify_links(user); } else if (strcmp(type, "actor_refresh") == 0) { @@ -2596,16 +2620,16 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) xs *actor_o = NULL; int status; - if (valid_status((status = activitypub_request(snac, actor, &actor_o)))) + if (valid_status((status = activitypub_request(user, actor, &actor_o)))) actor_add(actor, actor_o); else object_touch(actor); - snac_log(snac, xs_fmt("actor_refresh %s %d", actor, status)); + snac_log(user, xs_fmt("actor_refresh %s %d", actor, status)); } } else - snac_log(snac, xs_fmt("unexpected user q_item type '%s'", type)); + snac_log(user, xs_fmt("unexpected user q_item type '%s'", type)); } -- cgit v1.2.3 From e42cf9b5c055dd0fb0a9de139f0273cf7e9d7084 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 18:27:59 +0100 Subject: New command 'note_mention'. --- main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index b4e7fa1..6eefbf4 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,7 @@ #include "xs_json.h" #include "xs_time.h" #include "xs_openssl.h" +#include "xs_match.h" #include "snac.h" @@ -34,6 +35,7 @@ int usage(void) printf("actor {basedir} [{uid}] {url} Requests an actor\n"); printf("note {basedir} {uid} {text} [files...] Sends a note with optional attachments\n"); printf("note_unlisted {basedir} {uid} {text} [files...] Sends an unlisted note with optional attachments\n"); + printf("note_mention {basedir} {uid} {text} [files...] Sends a note only to mentioned accounts\n"); printf("boost|announce {basedir} {uid} {url} Boosts (announces) a post\n"); printf("unboost {basedir} {uid} {url} Unboosts a post\n"); printf("resetpwd {basedir} {uid} Resets the password of a user\n"); @@ -620,7 +622,7 @@ int main(int argc, char *argv[]) return 0; } - if (strcmp(cmd, "note") == 0 || strcmp(cmd, "note_unlisted") == 0) { /** **/ + if (xs_match(cmd, "note|note_unlisted|note_mention")) { /** **/ xs *content = NULL; xs *msg = NULL; xs *c_msg = NULL; @@ -684,8 +686,14 @@ int main(int argc, char *argv[]) else content = xs_dup(url); - msg = msg_note(&snac, content, NULL, NULL, attl, - strcmp(cmd, "note_unlisted") == 0 ? 2 : 0, getenv("LANG")); + int scope = 0; + if (strcmp(cmd, "note_mention") == 0) + scope = 1; + else + if (strcmp(cmd, "note_unlisted") == 0) + scope = 2; + + msg = msg_note(&snac, content, NULL, NULL, attl, scope, getenv("LANG")); c_msg = msg_create(&snac, msg); -- cgit v1.2.3 From b3ca9a50e7fe365a7757d39b7a3c192827b0b680 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 18:39:07 +0100 Subject: changed note* commands checking. --- main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 6eefbf4..5678af4 100644 --- a/main.c +++ b/main.c @@ -622,7 +622,9 @@ int main(int argc, char *argv[]) return 0; } - if (xs_match(cmd, "note|note_unlisted|note_mention")) { /** **/ + if (strcmp(cmd, "note") == 0 || /** **/ + strcmp(cmd, "note_unlisted") == 0 || /** **/ + strcmp(cmd, "note_mention") == 0) { /** **/ xs *content = NULL; xs *msg = NULL; xs *c_msg = NULL; -- cgit v1.2.3 From 5b7279b6f7b0bff59aba029b12b05c989a3b0059 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 18:39:29 +0100 Subject: Only relay public posts. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 18b3c86..73145d3 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2539,7 +2539,7 @@ void process_user_queue_item(snac *user, xs_dict *q_item) xs_set_free(&inboxes); /* relay this note */ - if (strcmp(user->uid, "relay") != 0) { /* avoid loops */ + if (is_msg_public(msg) && strcmp(user->uid, "relay") != 0) { /* avoid loops */ snac relay; if (user_open(&relay, "relay")) { /* a 'relay' user exists */ -- cgit v1.2.3 From f7ae2521ba76dfa403b182221001c38bf383d0e3 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 19 Jan 2025 18:54:02 +0100 Subject: Don't notify us for our own posts from the relay. --- activitypub.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activitypub.c b/activitypub.c index 73145d3..8b44dc8 100644 --- a/activitypub.c +++ b/activitypub.c @@ -889,6 +889,11 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor, /* if it's not an admiration about something by us, done */ if (xs_is_null(objid) || !xs_startswith(objid, snac->actor)) return; + + /* if it's an announce by our own relay, done */ + xs *relay_id = xs_fmt("%s/relay", srv_baseurl); + if (xs_startswith(id, relay_id)) + return; } /* updated poll? */ -- cgit v1.2.3 From 202d99c81529694ac3c6b524af9627404a26dd2f Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 08:39:06 +0100 Subject: Added the USER_AGENT to HTTP errors. --- httpd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/httpd.c b/httpd.c index 7222893..eb72c6f 100644 --- a/httpd.c +++ b/httpd.c @@ -501,13 +501,13 @@ void httpd_connection(FILE *f) } if (status == HTTP_STATUS_FORBIDDEN) - body = xs_str_new("

403 Forbidden

"); + body = xs_str_new("

403 Forbidden (" USER_AGENT ")

"); if (status == HTTP_STATUS_NOT_FOUND) - body = xs_str_new("

404 Not Found

"); + body = xs_str_new("

404 Not Found (" USER_AGENT ")

"); if (status == HTTP_STATUS_BAD_REQUEST && body != NULL) - body = xs_str_new("

400 Bad Request

"); + body = xs_str_new("

400 Bad Request (" USER_AGENT ")

"); if (status == HTTP_STATUS_SEE_OTHER) headers = xs_dict_append(headers, "location", body); -- cgit v1.2.3 From b4add138e10410fc4aeedea50a6de7ebead2f710 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 09:17:57 +0100 Subject: Show the post language under the date 'title'. --- html.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/html.c b/html.c index 09dd202..3ece3b2 100644 --- a/html.c +++ b/html.c @@ -114,7 +114,8 @@ xs_str *actor_name(xs_dict *actor, const char *proxy) xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, - const char *udate, const char *url, int priv, int in_people, const char *proxy) + const char *udate, const char *url, int priv, + int in_people, const char *proxy, const char *lang) { xs_html *actor_icon = xs_html_tag("p", NULL); @@ -220,6 +221,9 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, date_title = xs_str_cat(date_title, " / ", udate); } + if (xs_is_string(lang)) + date_title = xs_str_cat(date_title, " (", lang, ")"); + xs_html_add(actor_icon, xs_html_text(" "), xs_html_tag("time", @@ -266,6 +270,7 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con const char *date = NULL; const char *udate = NULL; const char *url = NULL; + const char *lang = NULL; int priv = 0; const char *type = xs_dict_get(msg, "type"); @@ -277,7 +282,17 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con date = xs_dict_get(msg, "published"); udate = xs_dict_get(msg, "updated"); - actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy); + lang = xs_dict_get(msg, "contentMap"); + if (xs_is_dict(lang)) { + const char *v; + int c = 0; + + xs_dict_next(lang, &lang, &v, &c); + } + else + lang = NULL; + + actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang); } return actor_icon; @@ -2769,7 +2784,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons xs_html_tag("div", xs_html_attr("class", "snac-post-header"), html_actor_icon(snac, actor, xs_dict_get(actor, "published"), - NULL, NULL, 0, 1, proxy))); + NULL, NULL, 0, 1, proxy, NULL))); /* content (user bio) */ const char *c = xs_dict_get(actor, "summary"); @@ -3022,7 +3037,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_add(entry, xs_html_tag("div", xs_html_attr("class", "snac-post"), - html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy))); + html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL))); } else if (strcmp(type, "Move") == 0) { @@ -3036,7 +3051,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_add(entry, xs_html_tag("div", xs_html_attr("class", "snac-post"), - html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy))); + html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy, NULL))); } } } -- cgit v1.2.3 From e56e30f0c64f12ea12413aaf4229975543e1bdbf Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 10:13:06 +0100 Subject: Updated documentation. --- doc/snac.8 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/snac.8 b/doc/snac.8 index 141fa27..c0e1af5 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -602,8 +602,9 @@ snac adduser $SNAC_BASEDIR relay # only needed once snac follow $SNAC_BASEDIR relay https://fedi-relay.gyptazy.com/actor .Ed .Pp -Lowering the data retention time for this special user is recommended, as the amount of -received posts can be big. +Please take note that subscribing to relays can increase the traffic towards your instance +significantly. In any case, lowering the "Maximum days to keep posts" value for the relay +special user is recommended (e.g. setting to just 1 day). .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From 5b21c9546b40cc0c52d5f39273747c551926984c Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 10:40:43 +0100 Subject: Added support for the SNAC_BASEDIR environment variable. If it's set, the basedir argument in command-line commands must not be given. --- main.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/main.c b/main.c index 5678af4..db26252 100644 --- a/main.c +++ b/main.c @@ -96,19 +96,6 @@ int main(int argc, char *argv[]) return snac_init(basedir); } - if (strcmp(cmd, "upgrade") == 0) { /** **/ - int ret; - - /* upgrade */ - if ((basedir = GET_ARGV()) == NULL) - return usage(); - - if ((ret = srv_open(basedir, 1)) == 1) - srv_log(xs_dup("OK")); - - return ret; - } - if (strcmp(cmd, "markdown") == 0) { /** **/ /* undocumented, for testing only */ xs *c = xs_readall(stdin); @@ -118,8 +105,20 @@ int main(int argc, char *argv[]) return 0; } - if ((basedir = GET_ARGV()) == NULL) - return usage(); + if ((basedir = getenv("SNAC_BASEDIR")) == NULL) { + if ((basedir = GET_ARGV()) == NULL) + return usage(); + } + + if (strcmp(cmd, "upgrade") == 0) { /** **/ + int ret; + + /* upgrade */ + if ((ret = srv_open(basedir, 1)) == 1) + srv_log(xs_dup("OK")); + + return ret; + } if (!srv_open(basedir, 0)) { srv_log(xs_fmt("error opening data storage at %s", basedir)); -- cgit v1.2.3 From fbc5015dc4f7b8fbc5901022b3252b5edbcf6ebf Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 10:47:23 +0100 Subject: Documented the SNAC_BASEDIR environment variable. --- doc/snac.1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/snac.1 b/doc/snac.1 index 7611600..d33bccb 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -377,6 +377,13 @@ https://$SNAC_HOST/oauth/x-snac-get-token .Pp .Sh ENVIRONMENT .Bl -tag -width Ds +.It SNAC_BASEDIR +This optional environment variable can be set to the base directory of +your installation; if set, you don't have to add the base directory as an +argument to command-line operations. This may prove useful if you only +have one +.Nm +instance in you system (which is probably your case). .It Ev DEBUG Overrides the debugging level from the server 'dbglevel' configuration variable. Set it to an integer value. The higher, the deeper in meaningless -- cgit v1.2.3 From fab45bcd213f3a22ab8f115dacdbb5668a5a63b0 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 10:48:51 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b99fadf..97cf0e4 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -22,6 +22,8 @@ Mastodon API: implemented limit= on notification fetches (contributed by nowster Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. +If the `SNAC_BASEDIR` environment variable is defined and set to the base directory of your installation, you don't have to include the base directory in the command line. + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 2ee79f565da10b243b2362bc731686753839c2eb Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 16:45:14 +0100 Subject: Fixed usage text. --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index db26252..a57adb5 100644 --- a/main.c +++ b/main.c @@ -51,10 +51,10 @@ int usage(void) printf("unlimit {basedir} {uid} {actor} Unlimits an actor\n"); printf("verify_links {basedir} {uid} Verifies a user's links (in the metadata)\n"); printf("search {basedir} {uid} {regex} Searches posts by content\n"); - printf("export_csv {basedir} {uid} Exports data as CSV files into current directory\n"); + printf("export_csv {basedir} {uid} Exports data as CSV files\n"); printf("alias {basedir} {uid} {account} Sets account (@user@host or actor url) as an alias\n"); printf("migrate {basedir} {uid} Migrates to the account defined as the alias\n"); - printf("import_csv {basedir} {uid} Imports data from CSV files in the current directory\n"); + printf("import_csv {basedir} {uid} Imports data from CSV files\n"); printf("import_list {basedir} {uid} {file} Imports a Mastodon CSV list file\n"); printf("import_block_list {basedir} {uid} {file} Imports a Mastodon CSV block list file\n"); -- cgit v1.2.3 From 9e283af12cf8a7e99c9ba13165135e3eff8aba01 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 17:00:59 +0100 Subject: Minor HTML tweak. --- html.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 3ece3b2..2a02d18 100644 --- a/html.c +++ b/html.c @@ -1368,7 +1368,9 @@ xs_html *html_top_controls(snac *snac) xs_html_sctag("input", xs_html_attr("type", "submit"), xs_html_attr("class", "button"), - xs_html_attr("value", L("Update user info"))))))); + xs_html_attr("value", L("Update user info"))), + + xs_html_tag("p", NULL))))); xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", snac->actor); xs *followed_hashtags = xs_join(xs_dict_get_def(snac->config, -- cgit v1.2.3 From 4416e3a28a32a848d99c0b9621256be18a60325c Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 18:37:21 +0100 Subject: Fixed check for the basedir (contributed by an-im-dugud). --- httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpd.c b/httpd.c index eb72c6f..c13402b 100644 --- a/httpd.c +++ b/httpd.c @@ -212,7 +212,7 @@ int server_get_handler(xs_dict *req, const char *q_path, int status = 0; /* is it the server root? */ - if (*q_path == '\0') { + if (*q_path == '\0' || strcmp(q_path, "/") == 0) { const xs_dict *q_vars = xs_dict_get(req, "q_vars"); const char *t = NULL; -- cgit v1.2.3 From 7add876a9334e87138be7edf72db35832755e118 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 20 Jan 2025 18:38:25 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 97cf0e4..cc6866a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -24,6 +24,8 @@ Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to If the `SNAC_BASEDIR` environment variable is defined and set to the base directory of your installation, you don't have to include the base directory in the command line. +Fixed a bug in the generation of the top page (contributed by an-im-dugud). + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 3d677f67d845cf6e74271681e76d3cb806036483 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 Jan 2025 14:33:21 +0100 Subject: Added more Markdown (contributed by an-im-dugud). --- format.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 52ed3ec..e5934b8 100644 --- a/format.c +++ b/format.c @@ -92,6 +92,8 @@ static xs_str *format_line(const char *line, xs_list **attach) "`[^`]+`" "|" "~~[^~]+~~" "|" "\\*\\*?\\*?[^\\*]+\\*?\\*?\\*" "|" + "_[^_]+_" "|" //anzu + "__[^_]+__" "|" //anzu "!\\[[^]]+\\]\\([^\\)]+\\)" "|" "\\[[^]]+\\]\\([^\\)]+\\)" "|" "[a-z]+:/" "/[^[:space:]]+" "|" @@ -127,6 +129,20 @@ static xs_str *format_line(const char *line, xs_list **attach) xs *s2 = xs_fmt("%s", s1); s = xs_str_cat(s, s2); } + //anzu - begin + else + if (xs_startswith(v, "__")) { + xs *s1 = xs_strip_chars_i(xs_dup(v), "_"); + xs *s2 = xs_fmt("%s", s1); + s = xs_str_cat(s, s2); + } + else + if (xs_startswith(v, "_")) { + xs *s1 = xs_strip_chars_i(xs_dup(v), "_"); + xs *s2 = xs_fmt("%s", s1); + s = xs_str_cat(s, s2); + } + //anzu - end else if (xs_startswith(v, "~~")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "~"); @@ -303,6 +319,31 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag continue; } + //anzu - begin + // h1 reserved for snac? + if (xs_startswith(ss, "# ")) { + ss = xs_strip_i(xs_crop_i(ss, 2, 0)); + s = xs_str_cat(s, "

"); + s = xs_str_cat(s, ss); + s = xs_str_cat(s, "

"); + continue; + } + if (xs_startswith(ss, "## ")) { + ss = xs_strip_i(xs_crop_i(ss, 3, 0)); + s = xs_str_cat(s, "

"); + s = xs_str_cat(s, ss); + s = xs_str_cat(s, "

"); + continue; + } + if (xs_startswith(ss, "### ")) { + ss = xs_strip_i(xs_crop_i(ss, 4, 0)); + s = xs_str_cat(s, "

"); + s = xs_str_cat(s, ss); + s = xs_str_cat(s, "

"); + continue; + } + //anzu - end + if (xs_startswith(ss, ">")) { /* delete the > and subsequent spaces */ ss = xs_strip_i(xs_crop_i(ss, 1, 0)); @@ -336,6 +377,8 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag s = xs_replace_i(s, "

", "
"); s = xs_replace_i(s, "

", "
"); s = xs_replace_i(s, "
", ""); + s = xs_replace_i(s, "
", ""); //anzu ??? + s = xs_replace_i(s, "
", ""); //anzu ??? { /* traditional emoticons */ @@ -378,7 +421,9 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag const char *valid_tags[] = { "a", "p", "br", "br/", "blockquote", "ul", "ol", "li", "cite", "small", - "span", "i", "b", "u", "s", "pre", "code", "em", "strong", "hr", "img", "del", "bdi", NULL + "span", "i", "b", "u", "s", "pre", "code", "em", "strong", "hr", "img", "del", "bdi", + "h2","h3", //anzu + NULL }; xs_str *sanitize(const char *content) -- cgit v1.2.3 From ece1b9ed165f524d50c3be20e477a7c9b25e19a4 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 Jan 2025 14:34:29 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index cc6866a..1e8c221 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -26,6 +26,8 @@ If the `SNAC_BASEDIR` environment variable is defined and set to the base direct Fixed a bug in the generation of the top page (contributed by an-im-dugud). +Added support for Markdown headers and underlining (contributed by an-im-dugud). + ## 2.68 Fixed regression in link verification code (contributed by nowster). -- cgit v1.2.3 From 468b7a38885152cb2d61537abfdd9a4cda9c847a Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 Jan 2025 14:39:49 +0100 Subject: Updated documentation. --- doc/snac.5 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/snac.5 b/doc/snac.5 index be7bfd7..3550995 100644 --- a/doc/snac.5 +++ b/doc/snac.5 @@ -24,9 +24,11 @@ A special subset of Markdown is allowed, including: .It bold **text between two pairs of asterisks** .It italic -*text between a pair of asterisks* +*text between a pair of asterisks* or _between a pair of underscores_ .It strikethrough text ~~text between a pair of tildes~~ +.It underlined text +__text between two pairs of underscores__ .It code Text `between backticks` is formatted as code. .Bd -literal @@ -53,6 +55,9 @@ Horizonal rules can be inserted by typing three minus symbols alone in a line. .It quoted text Lines starting with >. +.It headers +One, two or three # at the beginning of a line plus a space plus +some text are converted to HTML headers. .It user mentions Strings in the format @user@host are requested using the Webfinger protocol and converted to links and mentions if something reasonable -- cgit v1.2.3 From 2611c816b01d3999ffcf3b59c0d3592908054950 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 Jan 2025 20:25:50 +0100 Subject: Hide the 'relay' user from the greeting list. --- httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpd.c b/httpd.c index c13402b..e0a36b6 100644 --- a/httpd.c +++ b/httpd.c @@ -138,7 +138,7 @@ static xs_str *greeting_html(void) while (xs_list_iter(&p, &uid)) { snac user; - if (user_open(&user, uid)) { + if (strcmp(uid, "relay") && user_open(&user, uid)) { xs_html_add(ul, xs_html_tag("li", xs_html_tag("a", -- cgit v1.2.3 From e6baf0d45c9b55f0b1532a0069b59ca06ac8fbc1 Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Tue, 21 Jan 2025 23:26:50 +0000 Subject: When reading timeline in ascending order, return results in descending order --- mastoapi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index 3250a20..54b4333 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1453,7 +1453,10 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn xs *st = mastoapi_status(user, msg); if (st != NULL) { - out = xs_list_append(out, st); + if (ascending) + out = xs_list_insert(out, 0, st); + else + out = xs_list_append(out, st); cnt++; } -- cgit v1.2.3 From 7cd62dd25b92780ef4e1ad42c1d2e9ba69c931e9 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 05:29:25 +0100 Subject: Don't account as such announces by our own relay. --- activitypub.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/activitypub.c b/activitypub.c index 8b44dc8..5517bf4 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2274,11 +2274,16 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) xs *who_o = NULL; if (valid_status(actor_request(snac, who, &who_o))) { - if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) - snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); - else - snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", - actor, object)); + /* don't account as such announces by our own relay */ + xs *this_relay = xs_fmt("%s/relay", srv_baseurl); + + if (strcmp(actor, this_relay) != 0) { + if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) + snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); + else + snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", + actor, object)); + } /* distribute the post with the actor as 'proxy' */ list_distribute(snac, actor, a_msg); -- cgit v1.2.3 From 070a9db3d72e0961dca45c7e65aa9fc4532aba73 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 05:59:15 +0100 Subject: Also accept 'Update' activities for 'Application' objects. --- activitypub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index 5517bf4..8fa29c9 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2302,7 +2302,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) } else if (strcmp(type, "Update") == 0) { /** **/ - if (xs_match(utype, "Person|Service")) { /** **/ + if (xs_match(utype, "Person|Service|Application")) { /** **/ actor_add(actor, xs_dict_get(msg, "object")); timeline_touch(snac); -- cgit v1.2.3 From f5e75f90058665f413e1535e8001ec5b4e0a7a19 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 06:28:46 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1e8c221..eb5c3b2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -18,7 +18,7 @@ Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). The user profile can now include longitude and latitude data for your current location. -Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster), obey the quiet public visibility set for posts. +Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster), obey the quiet public visibility set for posts, other timeline improvements (contributed by nowster). Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. -- cgit v1.2.3 From ac663a6b4dd5eee1fb3c676524b8e141eb2529ed Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 10:07:39 +0100 Subject: Moved followed hashtag check to its own function. --- activitypub.c | 60 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/activitypub.c b/activitypub.c index 8fa29c9..b505fbb 100644 --- a/activitypub.c +++ b/activitypub.c @@ -587,6 +587,40 @@ int is_msg_from_private_user(const xs_dict *msg) } +int followed_hashtag_check(snac *user, const xs_dict *msg) +/* returns true if this message contains a hashtag followed by me */ +{ + const xs_list *fw_tags = xs_dict_get(user->config, "followed_hashtags"); + + if (xs_is_list(fw_tags)) { + const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); + + if (xs_is_list(tags_in_msg)) { + const xs_dict *te; + + /* iterate the tags in the message */ + xs_list_foreach(tags_in_msg, te) { + if (xs_is_dict(te)) { + const char *type = xs_dict_get(te, "type"); + const char *name = xs_dict_get(te, "name"); + + if (xs_is_string(type) && xs_is_string(name)) { + if (strcmp(type, "Hashtag") == 0) { + xs *lc_name = xs_utf8_to_lower(name); + + if (xs_list_in(fw_tags, lc_name) != -1) + return 1; + } + } + } + } + } + } + + return 0; +} + + int is_msg_for_me(snac *snac, const xs_dict *c_msg) /* checks if this message is for me */ { @@ -708,30 +742,8 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) } /* does this message contain a tag we are following? */ - const xs_list *fw_tags = xs_dict_get(snac->config, "followed_hashtags"); - if (pub_msg && xs_type(fw_tags) == XSTYPE_LIST) { - const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); - if (xs_type(tags_in_msg) == XSTYPE_LIST) { - const xs_dict *te; - - /* iterate the tags in the message */ - xs_list_foreach(tags_in_msg, te) { - if (xs_type(te) == XSTYPE_DICT) { - const char *type = xs_dict_get(te, "type"); - const char *name = xs_dict_get(te, "name"); - - if (xs_type(type) == XSTYPE_STRING && xs_type(name) == XSTYPE_STRING) { - if (strcmp(type, "Hashtag") == 0) { - xs *lc_name = xs_utf8_to_lower(name); - - if (xs_list_in(fw_tags, lc_name) != -1) - return 7; - } - } - } - } - } - } + if (pub_msg && followed_hashtag_check(snac, msg)) + return 7; return 0; } -- cgit v1.2.3 From a51c996efe0e41334523602271740b6e6b2501c0 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 10:38:01 +0100 Subject: More tweaks to hashtag following code. --- activitypub.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/activitypub.c b/activitypub.c index b505fbb..86599ee 100644 --- a/activitypub.c +++ b/activitypub.c @@ -636,19 +636,32 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) if (xs_match(type, "Like|Announce|EmojiReact")) { const char *object = xs_dict_get(c_msg, "object"); - if (xs_type(object) == XSTYPE_DICT) + if (xs_is_dict(object)) object = xs_dict_get(object, "id"); /* bad object id? reject */ - if (xs_type(object) != XSTYPE_STRING) + if (!xs_is_string(object)) return 0; /* if it's about one of our posts, accept it */ if (xs_startswith(object, snac->actor)) return 2; - /* if it's by someone we don't follow, reject */ - return following_check(snac, actor); + /* if it's by someone we follow, accept it */ + if (following_check(snac, actor)) + return 1; + + /* do we follow any hashtag? */ + if (xs_is_list(xs_dict_get(snac->config, "followed_hashtags"))) { + xs *obj = NULL; + + /* if the admired object contains any followed hashtag, accept it */ + if (valid_status(object_get(object, &obj)) && + followed_hashtag_check(snac, obj)) + return 7; + } + + return 0; } /* if it's an Undo, it must be from someone related to us */ -- cgit v1.2.3 From 0cae9a520c7f3d72ecdabf1fac066f1da943605c Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 15:23:37 +0100 Subject: Minor visual tweak. --- html.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/html.c b/html.c index 2a02d18..fcf2904 100644 --- a/html.c +++ b/html.c @@ -1855,22 +1855,20 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } } } - else + if (strcmp(type, "Note") == 0) { - if (level == 0) { - /* is the parent not here? */ - const char *parent = get_in_reply_to(msg); + /* is the parent not here? */ + const char *parent = get_in_reply_to(msg); - if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { - xs_html_add(post_header, - xs_html_tag("div", - xs_html_attr("class", "snac-origin"), - xs_html_text(L("in reply to")), - xs_html_text(" "), - xs_html_tag("a", - xs_html_attr("href", parent), - xs_html_text("»")))); - } + if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { + xs_html_add(post_header, + xs_html_tag("div", + xs_html_attr("class", "snac-origin"), + xs_html_text(L("in reply to")), + xs_html_text(" "), + xs_html_tag("a", + xs_html_attr("href", parent), + xs_html_text("»")))); } } -- cgit v1.2.3 From ced28f931928fac1ded3e435868c3ca204b9280b Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 15:31:20 +0100 Subject: Updated documentation. --- doc/snac.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snac.8 b/doc/snac.8 index c0e1af5..1ca5d4b 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -599,7 +599,7 @@ they will received as if they were for them. Example: .Bd -literal -offset indent snac adduser $SNAC_BASEDIR relay # only needed once -snac follow $SNAC_BASEDIR relay https://fedi-relay.gyptazy.com/actor +snac follow $SNAC_BASEDIR relay https://relay.example.com/actor .Ed .Pp Please take note that subscribing to relays can increase the traffic towards your instance -- cgit v1.2.3 From 0f0e33f5f6f49b9b4055b24761559b11c80da45e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 Jan 2025 15:47:21 +0100 Subject: Fixed bug in 'in reply to' checking. --- html.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/html.c b/html.c index fcf2904..a15bd06 100644 --- a/html.c +++ b/html.c @@ -1856,19 +1856,23 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } } - if (strcmp(type, "Note") == 0) { + if (user && strcmp(type, "Note") == 0) { /* is the parent not here? */ const char *parent = get_in_reply_to(msg); - if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { - xs_html_add(post_header, - xs_html_tag("div", - xs_html_attr("class", "snac-origin"), - xs_html_text(L("in reply to")), - xs_html_text(" "), - xs_html_tag("a", - xs_html_attr("href", parent), - xs_html_text("»")))); + if (!xs_is_null(parent) && *parent) { + xs *md5 = xs_md5_hex(parent, strlen(parent)); + + if (!timeline_here(user, md5)) { + xs_html_add(post_header, + xs_html_tag("div", + xs_html_attr("class", "snac-origin"), + xs_html_text(L("in reply to")), + xs_html_text(" "), + xs_html_tag("a", + xs_html_attr("href", parent), + xs_html_text("»")))); + } } } -- cgit v1.2.3 From fda3057dc86d859a0935d2f5fce890d114db5861 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 23 Jan 2025 14:39:59 +0100 Subject: More hashtag following tweaks. --- activitypub.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/activitypub.c b/activitypub.c index 86599ee..cade0d9 100644 --- a/activitypub.c +++ b/activitypub.c @@ -621,6 +621,36 @@ int followed_hashtag_check(snac *user, const xs_dict *msg) } +void followed_hashtag_distribute(const xs_dict *msg) +/* distribute this post to all users following the included hashtags */ +{ + const char *id = xs_dict_get(msg, "id"); + const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); + + if (!xs_is_string(id) || !xs_is_list(tags_in_msg) || xs_list_len(tags_in_msg) == 0) + return; + + srv_debug(1, xs_fmt("followed_hashtag_distribute check for %s", id)); + + xs *users = user_list(); + const char *uid; + + xs_list_foreach(users, uid) { + snac user; + + if (user_open(&user, uid)) { + if (followed_hashtag_check(&user, msg)) { + timeline_add(&user, id, msg); + + snac_log(&user, xs_fmt("followed hashtag in %s", id)); + } + + user_free(&user); + } + } +} + + int is_msg_for_me(snac *snac, const xs_dict *c_msg) /* checks if this message is for me */ { @@ -2313,6 +2343,9 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) /* distribute the post with the actor as 'proxy' */ list_distribute(snac, actor, a_msg); + /* distribute the post to users following these hashtags */ + followed_hashtag_distribute(a_msg); + do_notify = 1; } else -- cgit v1.2.3 From 0394f835e45975b9a82b0b009de2471e55a6cb7a Mon Sep 17 00:00:00 2001 From: default Date: Thu, 23 Jan 2025 20:09:22 +0100 Subject: Added some code to supress repeated notifications. --- html.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index a15bd06..d325063 100644 --- a/html.c +++ b/html.c @@ -2967,9 +2967,12 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("class", "snac-posts")); xs_html_add(body, posts); - xs_list *p = n_list; + xs_set rep; + xs_set_init(&rep); + const xs_str *v; - while (xs_list_iter(&p, &v)) { + + xs_list_foreach(n_list, v) { xs *noti = notify_get(user, v); if (noti == NULL) @@ -2990,6 +2993,9 @@ xs_str *html_notifications(snac *user, int skip, int show) object_get(id, &obj); + if (xs_set_add(&rep, xs_dict_get(obj, "id")) != 1) + continue; + const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; @@ -3103,6 +3109,8 @@ xs_str *html_notifications(snac *user, int skip, int show) } } + xs_set_free(&rep); + if (noti_new == NULL && noti_seen == NULL) xs_html_add(body, xs_html_tag("h2", -- cgit v1.2.3 From cc1d4258e5dea493605c3f09b8279e28dd61e727 Mon Sep 17 00:00:00 2001 From: shtrophic Date: Thu, 23 Jan 2025 20:15:23 +0100 Subject: Sandbox fixes - allow reading `/dev/urandom` as it is shown as a failed syscall when tracing - resolve `/etc/ssl/cert.pem` in case it is a symlink --- sandbox.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sandbox.c b/sandbox.c index cbe0043..0fc48ad 100644 --- a/sandbox.c +++ b/sandbox.c @@ -71,15 +71,22 @@ LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smail) LANDLOCK_ACCESS_FS_REFER_COMPAT, s = LANDLOCK_ACCESS_FS_MAKE_SOCK, x = LANDLOCK_ACCESS_FS_EXECUTE; + char *resolved_path = NULL; LL_PATH(basedir, rf|rd|w|c); LL_PATH("/tmp", rf|rd|w|c); #ifndef WITHOUT_SHM LL_PATH("/dev/shm", rf|w|c ); #endif + LL_PATH("/dev/urandom", rf ); LL_PATH("/etc/resolv.conf", rf ); LL_PATH("/etc/hosts", rf ); - LL_PATH("/etc/ssl", rf ); + LL_PATH("/etc/ssl", rf|rd ); + if ((resolved_path = realpath("/etc/ssl/cert.pem", NULL))) { + /* some distros like cert.pem to be a symlink */ + LL_PATH(resolved_path, rf ); + free(resolved_path); + } LL_PATH("/usr/share/zoneinfo", rf ); if (mtime("/etc/pki") > 0) -- cgit v1.2.3 From 95d32cbe7cfbb0321ac37b57ada23b6c268815e2 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 23 Jan 2025 20:17:38 +0100 Subject: Improved notify repetition code. --- html.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index d325063..3fd1e4b 100644 --- a/html.c +++ b/html.c @@ -2993,7 +2993,9 @@ xs_str *html_notifications(snac *user, int skip, int show) object_get(id, &obj); - if (xs_set_add(&rep, xs_dict_get(obj, "id")) != 1) + const char *msg_id = NULL; + + if (xs_is_dict(obj) && (msg_id = xs_dict_get(obj, "id")) && xs_set_add(&rep, msg_id) != 1) continue; const char *actor_id = xs_dict_get(noti, "actor"); -- cgit v1.2.3 From 3f3419d8dbc810fe3ba8c8b1b33b7cb529900c61 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 08:12:57 +0100 Subject: Updated documentation. --- doc/snac.8 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/snac.8 b/doc/snac.8 index 1ca5d4b..e228495 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -591,10 +591,10 @@ instance can subscribe to LitePub (Pleroma-style) Fediverse Relays. Doing this i visibility and allows following hashtags. To do this, you must create a special user named relay and, from it, follow the relay actor(s) like you do with regular actor URLs. This special user will start receiving boosts from the relay server of posts from other instances -also following it. It any other user of the same +also following it. If any other user of the same .Nm instance follows any of the hashtags included in these boosted posts coming from the relay, -they will received as if they were for them. +they will be received as if they were for them. .Pp Example: .Bd -literal -offset indent @@ -602,6 +602,9 @@ snac adduser $SNAC_BASEDIR relay # only needed once snac follow $SNAC_BASEDIR relay https://relay.example.com/actor .Ed .Pp +Users on your instance do NOT need to follow the local relay user to benefit from following +hashtags. +.Pp Please take note that subscribing to relays can increase the traffic towards your instance significantly. In any case, lowering the "Maximum days to keep posts" value for the relay special user is recommended (e.g. setting to just 1 day). -- cgit v1.2.3 From a72d09ae56b927b872540fec5c85881496c23e27 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 08:23:18 +0100 Subject: Set value 'purge_days' to 1 by default when creating the relay user. --- utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils.c b/utils.c index a5b1124..fd00e99 100644 --- a/utils.c +++ b/utils.c @@ -318,6 +318,10 @@ int adduser(const char *uid) mkdirx(d); } + /* add a specially short data retention time for the relay */ + if (strcmp(uid, "relay") == 0) + config = xs_dict_set(config, "purge_days", xs_stock(1)); + xs *cfn = xs_fmt("%s/user.json", basedir); if ((f = fopen(cfn, "w")) == NULL) { -- cgit v1.2.3 From 4140d46faf6571f729da807ca857733d1b72a397 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 10:14:57 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index eb5c3b2..23a2a17 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # Release Notes -## UNRELEASED +## 2.69 "Yin/Yang of Love" Added support for subscribing to LitePub (Pleroma-style) Fediverse Relays like e.g. https://fedi-relay.gyptazy.com to improve federation. See `snac(8)` (the Administrator Manual) for more information on how to use this feature. -- cgit v1.2.3 From 1cb7b37ae6adb01444ea1466d8884aacca10ebd8 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 10:15:17 +0100 Subject: Version 2.69 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 65ece5d..c0e512d 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.69-dev" +#define VERSION "2.69" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From 5c303d539b48cdec5cc9cca77472faaa7c5c8d71 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 14:54:55 +0100 Subject: Improved notification messages. --- html.c | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/html.c b/html.c index 3fd1e4b..e1f21bb 100644 --- a/html.c +++ b/html.c @@ -2967,8 +2967,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("class", "snac-posts")); xs_html_add(body, posts); - xs_set rep; - xs_set_init(&rep); + /* dict to store previous notification labels */ + xs *admiration_labels = xs_dict_new(); const xs_str *v; @@ -2995,8 +2995,8 @@ xs_str *html_notifications(snac *user, int skip, int show) const char *msg_id = NULL; - if (xs_is_dict(obj) && (msg_id = xs_dict_get(obj, "id")) && xs_set_add(&rep, msg_id) != 1) - continue; + if (xs_is_dict(obj)) + msg_id = xs_dict_get(obj, "id"); const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; @@ -3030,9 +3030,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs *s_date = xs_crop_i(xs_dup(date), 0, 10); - xs_html *entry = xs_html_tag("div", - xs_html_attr("class", "snac-post-with-desc"), - xs_html_tag("p", + xs_html *this_html_label = xs_html_container( xs_html_tag("b", xs_html_text(label), xs_html_text(" by "), @@ -3043,7 +3041,37 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_tag("time", xs_html_attr("class", "dt-published snac-pubdate"), xs_html_attr("title", date), - xs_html_text(s_date)))); + xs_html_text(s_date))); + + xs_html *html_label = NULL; + + if (xs_is_string(msg_id)) { + const xs_val *prev_label = xs_dict_get(admiration_labels, msg_id); + + if (xs_type(prev_label) == XSTYPE_DATA) { + /* there is a previous list of admiration labels! */ + xs_data_get(&html_label, prev_label); + + xs_html_add(html_label, + xs_html_sctag("br", NULL), + this_html_label); + + continue; + } + } + + xs_html *entry = NULL; + + html_label = xs_html_tag("p", + this_html_label); + + /* store in the admiration labels dict */ + xs *pl = xs_data_new(&html_label, sizeof(html_label)); + admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); + + entry = xs_html_tag("div", + xs_html_attr("class", "snac-post-with-desc"), + html_label); if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { xs_html_add(entry, @@ -3111,8 +3139,6 @@ xs_str *html_notifications(snac *user, int skip, int show) } } - xs_set_free(&rep); - if (noti_new == NULL && noti_seen == NULL) xs_html_add(body, xs_html_tag("h2", -- cgit v1.2.3 From 71537bdaf8fa3e752b7178efdf53e5c1c1402973 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 05:29:01 +0100 Subject: Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index c0e512d..344dbaa 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.69" +#define VERSION "2.70-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From 896a36837c8da070d85abe92d62d12c4009fca1c Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 21:45:14 +0100 Subject: Another tweak to fix repeated notifications. --- html.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/html.c b/html.c index e1f21bb..31d8d97 100644 --- a/html.c +++ b/html.c @@ -2967,6 +2967,9 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("class", "snac-posts")); xs_html_add(body, posts); + xs_set rep; + xs_set_init(&rep); + /* dict to store previous notification labels */ xs *admiration_labels = xs_dict_new(); @@ -2983,6 +2986,7 @@ xs_str *html_notifications(snac *user, int skip, int show) const char *utype = xs_dict_get(noti, "utype"); const char *id = xs_dict_get(noti, "objid"); const char *date = xs_dict_get(noti, "date"); + const char *id2 = xs_dict_get_path(noti, "msg.id"); xs *wrk = NULL; if (xs_is_null(id)) @@ -2991,6 +2995,9 @@ xs_str *html_notifications(snac *user, int skip, int show) if (is_hidden(user, id)) continue; + if (xs_is_string(id2) && xs_set_add(&rep, id2) != 1) + continue; + object_get(id, &obj); const char *msg_id = NULL; @@ -3158,6 +3165,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_text(L("More..."))))); } + xs_set_free(&rep); + xs_html_add(body, html_footer()); -- cgit v1.2.3 From b2f5f95c7b8c8793f82d394009b0908888de5bdc Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 23:06:08 +0100 Subject: Fixed repeated entries in the instance public timeline. --- data.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/data.c b/data.c index 33947ff..40382d2 100644 --- a/data.c +++ b/data.c @@ -1399,11 +1399,13 @@ void timeline_update_indexes(snac *snac, const char *id) if (valid_status(object_get(id, &msg))) { /* if its ours and is public, also store in public */ if (is_msg_public(msg)) { - object_user_cache_add(snac, id, "public"); - - /* also add it to the instance public timeline */ - xs *ipt = xs_fmt("%s/public.idx", srv_basedir); - index_add(ipt, id); + if (object_user_cache_add(snac, id, "public") >= 0) { + /* also add it to the instance public timeline */ + xs *ipt = xs_fmt("%s/public.idx", srv_basedir); + index_add(ipt, id); + } + else + srv_debug(1, xs_fmt("Not added to public instance index %s", id)); } } } @@ -1524,8 +1526,17 @@ xs_list *timeline_instance_list(int skip, int show) /* returns the timeline for the full instance */ { xs *idx = instance_index_fn(); + xs *lst = index_list_desc(idx, skip, show); - return index_list_desc(idx, skip, show); + /* make the list unique */ + xs_set rep; + xs_set_init(&rep); + const char *md5; + + xs_list_foreach(lst, md5) + xs_set_add(&rep, md5); + + return xs_set_result(&rep); } -- cgit v1.2.3 From 5d267968cb0d2670f8a4bd7587e505f812dfa3bc Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 23:13:59 +0100 Subject: Fixed crash in the notification area after deleting a post. --- html.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 31d8d97..3b63cfc 100644 --- a/html.c +++ b/html.c @@ -3074,7 +3074,9 @@ xs_str *html_notifications(snac *user, int skip, int show) /* store in the admiration labels dict */ xs *pl = xs_data_new(&html_label, sizeof(html_label)); - admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); + + if (xs_is_string(msg_id)) + admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); entry = xs_html_tag("div", xs_html_attr("class", "snac-post-with-desc"), -- cgit v1.2.3