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(+) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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 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(+) (limited to 'html.c') 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 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(-) (limited to 'html.c') 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