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
\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