From e21c6ce7f7083eac7d49050e18787669c835cd08 Mon Sep 17 00:00:00 2001 From: grunfink Date: Mon, 20 Jul 2026 16:44:36 +0200 Subject: In build_mentions(), don't add the original poster as a CC. --- html.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 5ba00ba..4810601 100644 --- a/html.c +++ b/html.c @@ -2045,6 +2045,9 @@ xs_str *build_mentions(snac *user, const xs_dict *msg) } } +#if 0 + /* disabled by now */ + /* also add the author of this post as a mention */ const char *atto = get_atto(msg); @@ -2065,6 +2068,7 @@ xs_str *build_mentions(snac *user, const xs_dict *msg) } } } +#endif if (*s) { xs *s1 = s; -- cgit From 4d120bccd8a031b4092db82ffbc0843fb15f9f10 Mon Sep 17 00:00:00 2001 From: voidq Date: Thu, 23 Jul 2026 19:47:05 +0800 Subject: renamed snac-btn-like to snac-btn-clear-all by mistake clear_all btn was given class snac-btn-like --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 4810601..bc4dd63 100644 --- a/html.c +++ b/html.c @@ -4337,7 +4337,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("id", "clear"), xs_html_sctag("input", xs_html_attr("type", "submit"), - xs_html_attr("class", "snac-btn-like"), + xs_html_attr("class", "snac-btn-clear-all"), xs_html_attr("value", L("Clear all"))))); xs_html *noti_new = NULL; -- cgit From 86852a0c2d95bbee5105294dff1f1998f93dbfed Mon Sep 17 00:00:00 2001 From: grunfink Date: Thu, 30 Jul 2026 10:21:29 +0200 Subject: Don't skip unlisted replies to unknown posts. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index bc4dd63..48aecde 100644 --- a/html.c +++ b/html.c @@ -3700,7 +3700,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, continue; const int scope = get_msg_visibility(msg); - if (user != NULL && scope != SCOPE_PUBLIC && !is_msg_mine(user, xs_dict_get(msg, "id"))) { + if (user != NULL && scope != SCOPE_PUBLIC && scope != SCOPE_UNLISTED && !is_msg_mine(user, xs_dict_get(msg, "id"))) { /* is this message a non-public reply? */ const char *irt = get_in_reply_to(msg); -- cgit From 3391e38d9482d8a6a26cb61707adc70af986db9d Mon Sep 17 00:00:00 2001 From: grunfink Date: Thu, 6 Aug 2026 07:07:48 +0200 Subject: Don't try (and fail) to parse non-UTC dates in posts. I'll provide a real fix eventually. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 48aecde..3ea9513 100644 --- a/html.c +++ b/html.c @@ -216,7 +216,7 @@ xs_str *html_date_label(snac *user, const char *date) /* check if a user has actually set a timezone */ if (user != NULL && xs_dict_get(user->config, "tz") != NULL && - (t = xs_parse_iso_date(date, 0)) != 0) { + xs_endswith(date, "Z") && (t = xs_parse_iso_date(date, 0)) != 0) { t += xs_tz_offset(user->tz); time_t today = time(NULL); -- cgit From 52adad8882d1504e8a8422ddb128b4976b853113 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 18 Aug 2026 17:56:13 +0200 Subject: Don't propagate 4xx error statuses from proxied media. --- html.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 3ea9513..ee68327 100644 --- a/html.c +++ b/html.c @@ -5328,6 +5328,9 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } } + else + if (status >= 400 && status <= 499) + status = HTTP_STATUS_NOT_FOUND; snac_debug(&snac, 1, xs_fmt("Proxy for %s %d", url, status)); } -- cgit From ed9571af3b970a624f22e5158616d1dde545beb4 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 18 Aug 2026 18:44:29 +0200 Subject: Moved proxy status change after the log message. --- html.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index ee68327..a93435e 100644 --- a/html.c +++ b/html.c @@ -5328,11 +5328,11 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } } - else - if (status >= 400 && status <= 499) - status = HTTP_STATUS_NOT_FOUND; snac_debug(&snac, 1, xs_fmt("Proxy for %s %d", url, status)); + + if (status >= 400 && status <= 499) + status = HTTP_STATUS_NOT_FOUND; } } else -- cgit From 6fed0f6823bd6815d01698a1c682c2759160262b Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 18 Aug 2026 19:23:56 +0200 Subject: More proxy test fixes. --- html.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index a93435e..8cb2370 100644 --- a/html.c +++ b/html.c @@ -5293,6 +5293,10 @@ int html_get_handler(const xs_dict *req, const char *q_path, /* pick the raw path (including optional ? arguments) */ const char *raw_path = xs_dict_get(req, "raw_path"); + /* skip the prefix and user part */ + xs *s = xs_fmt("%s/%s/", xs_dict_get(srv_config, "prefix"), snac.uid); + raw_path += strlen(s); + /* skip to where the proxy/ string starts */ raw_path += xs_str_in(raw_path, proxy_prefix); -- cgit From 6ae12b03d5566b9119c78d79a96df7ac12b2c636 Mon Sep 17 00:00:00 2001 From: grunfink Date: Wed, 26 Aug 2026 07:24:25 +0200 Subject: In the people page, strip absurdly long location coordinates due to floating point inaccuracies. --- html.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 8cb2370..3f35040 100644 --- a/html.c +++ b/html.c @@ -3899,7 +3899,10 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c const char *lo = xs_is_string(longitude) ? longitude : xs_number_str(longitude); if (xs_is_string(la) && xs_is_string(lo)) { - xs *label = xs_fmt("%s,%s", la, lo); + xs *sla = xs_utf8_crop_i(xs_dup(la), 0, 7); + xs *slo = xs_utf8_crop_i(xs_dup(lo), 0, 7); + + xs *label = xs_fmt("%s,%s", sla, slo); xs *url = xs_fmt("https://openstreetmap.org/search?query=%s,%s", la, lo); xs_html_add(snac_post, -- cgit From 71a83e6578f910c3baa251e4128208b201ce9e45 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 26 Aug 2026 07:59:23 +0200 Subject: Use snac_op in actor_failure(). --- activitypub.c | 10 +++++----- data.c | 11 +++++++---- html.c | 2 +- snac.h | 10 +++++++++- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'html.c') diff --git a/activitypub.c b/activitypub.c index 92eb7f5..4de0be7 100644 --- a/activitypub.c +++ b/activitypub.c @@ -3054,7 +3054,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) followed_hashtag_distribute(a_msg); /* actor is [back] alive */ - actor_failure(who, 2); + actor_failure(who, OP_DEL); do_notify = 1; } @@ -3461,7 +3461,7 @@ void process_user_queue_item(snac *user, xs_dict *q_item) const char *actor = xs_dict_get(q_item, "actor"); double mtime = object_mtime(actor); - if (actor_failure(actor, 0) == -1) { + if (actor_failure(actor, OP_CHECK) == -1) { /* actor is broken beyond repair */ snac_debug(user, 1, xs_fmt("actor_refresh skipped broken actor %s", actor)); } @@ -3478,16 +3478,16 @@ void process_user_queue_item(snac *user, xs_dict *q_item) actor_add(actor, actor_o); /* mark actor and instance as working */ - actor_failure(actor, 2); + actor_failure(actor, OP_DEL); instance_failure(actor, 2); } else { if (status == HTTP_STATUS_GONE || status == HTTP_STATUS_NOT_FOUND) { - actor_failure(actor, 1); + actor_failure(actor, OP_ADD); snac_log(user, xs_fmt("actor_refresh marking actor %s as broken %d", actor, status)); } else { - actor_failure(actor, 2); + actor_failure(actor, OP_DEL); object_touch(actor); } } diff --git a/data.c b/data.c index 806938c..7a8f884 100644 --- a/data.c +++ b/data.c @@ -3233,7 +3233,7 @@ xs_list *content_search(snac *user, const char *regex, } -int actor_failure(const char *actor, int op) +int actor_failure(const char *actor, snac_op op) /* actor failure maintenance */ { int ret = 0; @@ -3242,13 +3242,13 @@ int actor_failure(const char *actor, int op) xs *fn = xs_fmt("%s/failure/%s", srv_basedir, md5); switch (op) { - case 0: /** check **/ + case OP_CHECK: /** check **/ if (mtime(fn)) ret = -1; break; - case 1: /** register a failure **/ + case OP_ADD: /** register a failure **/ if (mtime(fn) == 0.0) { FILE *f; @@ -3261,11 +3261,14 @@ int actor_failure(const char *actor, int op) break; - case 2: /** clear a failure **/ + case OP_DEL: /** clear a failure **/ /* called whenever a message comes from this instance */ unlink(fn); break; + + default: + break; } return ret; diff --git a/html.c b/html.c index 3f35040..f18e233 100644 --- a/html.c +++ b/html.c @@ -362,7 +362,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, xs_html_raw("💔"))); } - if (actor_failure(actor_id, 0) == -1) { + if (actor_failure(actor_id, OP_CHECK) == -1) { xs_html_add(actor_icon, xs_html_text(" "), xs_html_tag("span", diff --git a/snac.h b/snac.h index 1644315..602fa0c 100644 --- a/snac.h +++ b/snac.h @@ -79,6 +79,14 @@ typedef struct { enum { THST_STOP, THST_WAIT, THST_IN, THST_QUEUE } th_state[MAX_THREADS]; } srv_state; +typedef enum { + OP_CHECK = 0, + OP_ADD = 1, + OP_DEL = 2, + OP_LIST = 3, + OP_FIND = 4 +} snac_op; + extern srv_state *p_state; enum { @@ -300,7 +308,7 @@ int content_match(const char *file, const xs_dict *msg); xs_list *content_search(snac *user, const char *regex, int priv, int skip, int show, int max_secs, int *timeout); -int actor_failure(const char *actor, int op); +int actor_failure(const char *actor, snac_op op); int instance_failure(const char *url, int op); int grave(const char *objid, int op); -- cgit From d7fc3d950d4654a3291066fed667f1b7d48e70bb Mon Sep 17 00:00:00 2001 From: default Date: Wed, 26 Aug 2026 08:05:16 +0200 Subject: Use snac_op in grave(). --- activitypub.c | 4 ++-- data.c | 11 +++++++---- html.c | 2 +- snac.h | 2 +- utils.c | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) (limited to 'html.c') diff --git a/activitypub.c b/activitypub.c index 8155fe4..df319f0 100644 --- a/activitypub.c +++ b/activitypub.c @@ -3951,7 +3951,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, uid = xs_list_get(l, 1); if (!user_open(&snac, uid)) { /* invalid user */ - status = grave(uid, 0) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; + status = grave(uid, OP_CHECK) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; srv_debug(1, xs_fmt("activitypub_get_handler bad user %s %d", uid, status)); return status; } @@ -4151,7 +4151,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path, const char *uid = xs_list_get(l, 1); if (!user_open(&snac, uid)) { /* invalid user */ - status = grave(uid, 0) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; + status = grave(uid, OP_CHECK) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; srv_debug(1, xs_fmt("activitypub_post_handler bad user %s %d", uid, status)); return status; } diff --git a/data.c b/data.c index 3736bc9..333ed31 100644 --- a/data.c +++ b/data.c @@ -3329,7 +3329,7 @@ int instance_failure(const char *url, snac_op op) } -int grave(const char *objid, int op) +int grave(const char *objid, snac_op op) /* the graveyeard of deleted objects */ { int ret = 0; @@ -3339,11 +3339,11 @@ int grave(const char *objid, int op) FILE *f; switch (op) { - case 0: /** check **/ + case OP_CHECK: /** check **/ ret = mtime(fn) > 0.0 ? 1 : 0; break; - case 1: /** add **/ + case OP_ADD: /** add **/ mkdirx(dir); if ((f = fopen(fn, "w")) != NULL) { @@ -3353,9 +3353,12 @@ int grave(const char *objid, int op) break; - case 2: /** del **/ + case OP_DEL: /** del **/ unlink(fn); break; + + default: + break; } return ret; diff --git a/html.c b/html.c index f18e233..8f177cc 100644 --- a/html.c +++ b/html.c @@ -4717,7 +4717,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (!uid || !user_open(&snac, uid)) { /* invalid user */ - status = grave(uid, 0) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; + status = grave(uid, OP_CHECK) ? HTTP_STATUS_GONE : HTTP_STATUS_NOT_FOUND; srv_debug(2, xs_fmt("html_get_handler bad user '%s' %d", xs_or(uid, "(null)"), status)); return status; } diff --git a/snac.h b/snac.h index d21c7ad..84223e6 100644 --- a/snac.h +++ b/snac.h @@ -311,7 +311,7 @@ xs_list *content_search(snac *user, const char *regex, int actor_failure(const char *actor, snac_op op); int instance_failure(const char *url, snac_op op); -int grave(const char *objid, int op); +int grave(const char *objid, snac_op op); void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retries); void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries); diff --git a/utils.c b/utils.c index 6941081..c1855b2 100644 --- a/utils.c +++ b/utils.c @@ -372,7 +372,7 @@ int adduser(const char *uid) } /* remove the grave */ - grave(uid, 2); + grave(uid, OP_DEL); printf("\nUser password is %s\n", pwd); @@ -467,7 +467,7 @@ int deluser(snac *user) } } - grave(user->uid, 1); + grave(user->uid, OP_ADD); rm_rf(user->basedir); -- cgit From 319d5cae0e632e1891f442194afe5e36aa48e6c4 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 26 Aug 2026 08:17:03 +0200 Subject: Use snac_op in list_maint(). --- data.c | 19 +++++++++++-------- html.c | 4 ++-- main.c | 16 ++++++++-------- mastoapi.c | 8 ++++---- snac.h | 5 +++-- utils.c | 4 ++-- 6 files changed, 30 insertions(+), 26 deletions(-) (limited to 'html.c') diff --git a/data.c b/data.c index 8be649d..6273800 100644 --- a/data.c +++ b/data.c @@ -2474,13 +2474,13 @@ xs_list *tag_search(const char *tag, int skip, int show) /** lists **/ -xs_val *list_maint(snac *user, const char *list, int op) +xs_val *list_maint(snac *user, const char *list, snac_op op) /* list maintenance */ { xs_val *l = NULL; switch (op) { - case 0: /** list of lists **/ + case OP_LIST: /** list of lists **/ { FILE *f; xs *spec = xs_fmt("%s/list/" "*.id", user->basedir); @@ -2509,9 +2509,9 @@ xs_val *list_maint(snac *user, const char *list, int op) break; - case 1: /** create new list (list is the name) **/ + case OP_ADD: /** create new list (list is the name) **/ { - xs *lol = list_maint(user, NULL, 0); + xs *lol = list_maint(user, NULL, OP_LIST); int c = 0; const xs_list *v; int add = 1; @@ -2546,7 +2546,7 @@ xs_val *list_maint(snac *user, const char *list, int op) break; - case 2: /** delete list (list is the id) **/ + case OP_DEL: /** delete list (list is the id) **/ { if (xs_is_hex(list)) { xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); @@ -2565,7 +2565,7 @@ xs_val *list_maint(snac *user, const char *list, int op) break; - case 3: /** get list name **/ + case OP_ID: /** get list name **/ if (xs_is_hex(list)) { FILE *f; xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); @@ -2578,9 +2578,9 @@ xs_val *list_maint(snac *user, const char *list, int op) break; - case 4: /** find list id by name **/ + case OP_FIND: /** find list id by name **/ if (xs_is_string(list)) { - xs *lol = list_maint(user, NULL, 0); + xs *lol = list_maint(user, NULL, OP_LIST); const xs_list *li; xs_list_foreach(lol, li) { @@ -2590,6 +2590,9 @@ xs_val *list_maint(snac *user, const char *list, int op) } } } + + default: + break; } return l; diff --git a/html.c b/html.c index 8f177cc..503539c 100644 --- a/html.c +++ b/html.c @@ -3534,7 +3534,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_attr("class", "snac-list-of-lists")); xs_html_add(body, lol); - xs *lists = list_maint(user, NULL, 0); /* get list of lists */ + xs *lists = list_maint(user, NULL, OP_LIST); /* get list of lists */ int ct = 0; const char *v; @@ -5179,7 +5179,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *ttl = timeline_top_level(&snac, list); xs *base = xs_fmt("/list/%s", lid); - xs *name = list_maint(&snac, lid, 3); + xs *name = list_maint(&snac, lid, OP_ID); xs *title = xs_fmt(L("Showing timeline for list '%s'"), name); *body = html_timeline(&snac, ttl, 0, skip, show, diff --git a/main.c b/main.c index dd224de..433bfc2 100644 --- a/main.c +++ b/main.c @@ -332,7 +332,7 @@ int main(int argc, char *argv[]) } if (strcmp(cmd, "lists") == 0) { /** **/ - xs *lol = list_maint(&snac, NULL, 0); + xs *lol = list_maint(&snac, NULL, OP_LIST); const xs_list *l; xs_list_foreach(lol, l) { @@ -389,7 +389,7 @@ int main(int argc, char *argv[]) return usage(cmd); if (strcmp(cmd, "list_members") == 0) { /** **/ - xs *lid = list_maint(&snac, url, 4); + xs *lid = list_maint(&snac, url, OP_FIND); if (lid != NULL) { xs *lcont = list_members(&snac, lid, NULL, OP_LIST); @@ -410,10 +410,10 @@ int main(int argc, char *argv[]) } if (strcmp(cmd, "list_create") == 0) { /** **/ - xs *lid = list_maint(&snac, url, 4); + xs *lid = list_maint(&snac, url, OP_FIND); if (lid == NULL) { - xs *n_lid = list_maint(&snac, url, 1); + xs *n_lid = list_maint(&snac, url, OP_ADD); printf("New list named '%s' created (%s)\n", url, n_lid); } else @@ -423,10 +423,10 @@ int main(int argc, char *argv[]) } if (strcmp(cmd, "list_remove") == 0) { /** **/ - xs *lid = list_maint(&snac, url, 4); + xs *lid = list_maint(&snac, url, OP_FIND); if (lid != NULL) { - list_maint(&snac, lid, 2); + list_maint(&snac, lid, OP_DEL); printf("List '%s' (%s) deleted\n", url, lid); } else @@ -439,7 +439,7 @@ int main(int argc, char *argv[]) const char *account = GET_ARGV(); if (account != NULL) { - xs *lid = list_maint(&snac, url, 4); + xs *lid = list_maint(&snac, url, OP_FIND); if (lid != NULL) { xs *actor = NULL; @@ -466,7 +466,7 @@ int main(int argc, char *argv[]) const char *account = GET_ARGV(); if (account != NULL) { - xs *lid = list_maint(&snac, url, 4); + xs *lid = list_maint(&snac, url, OP_FIND); if (lid != NULL) { xs *md5 = xs_md5_hex(account, strlen(account)); diff --git a/mastoapi.c b/mastoapi.c index 571d7cb..81c9bf0 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1781,7 +1781,7 @@ xs_list *mastoapi_account_lists(snac *user, const char *uid) { xs_list *out = xs_list_new(); xs *actor_md5 = NULL; - xs *lol = list_maint(user, NULL, 0); + xs *lol = list_maint(user, NULL, OP_LIST); if (uid) { if (!xs_is_hex(uid)) @@ -2549,7 +2549,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, else if (xs_is_hex(p)) { xs *out = xs_list_new(); - xs *lol = list_maint(&snac1, NULL, 0); + xs *lol = list_maint(&snac1, NULL, OP_LIST); int c = 0; const xs_list *v; @@ -3803,7 +3803,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (xs_type(title) == XSTYPE_STRING) { /* add the list */ xs *out = xs_dict_new(); - xs *lid = list_maint(&snac, title, 1); + xs *lid = list_maint(&snac, title, OP_ADD); if (!xs_is_null(lid)) { out = xs_dict_append(out, "id", lid); @@ -4058,7 +4058,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, else { /* delete list */ if (xs_is_hex(p)) { - list_maint(&snac, p, 2); + list_maint(&snac, p, OP_DEL); } } } diff --git a/snac.h b/snac.h index 0406a2e..a8dad16 100644 --- a/snac.h +++ b/snac.h @@ -84,7 +84,8 @@ typedef enum { OP_ADD = 1, OP_DEL = 2, OP_LIST = 3, - OP_FIND = 4 + OP_FIND = 4, + OP_ID = 5 } snac_op; extern srv_state *p_state; @@ -259,7 +260,7 @@ void tag_index(const char *id, const xs_dict *obj); xs_str *tag_fn(const char *tag); xs_list *tag_search(const char *tag, int skip, int show); -xs_val *list_maint(snac *user, const char *list, int op); +xs_val *list_maint(snac *user, const char *list, snac_op op); xs_str *list_timeline_fn(snac *user, const char *list); xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_members(snac *user, const char *list_id, const char *actor_md5, snac_op op); diff --git a/utils.c b/utils.c index 6d202d8..8780270 100644 --- a/utils.c +++ b/utils.c @@ -698,7 +698,7 @@ void export_csv(snac *user) if ((f = fopen(fn, "w")) != NULL) { snac_log(user, xs_fmt("Creating %s...", fn)); - xs *lol = list_maint(user, NULL, 0); + xs *lol = list_maint(user, NULL, OP_LIST); const xs_list *li; xs_list_foreach(lol, li) { @@ -927,7 +927,7 @@ void import_list_csv(snac *user, const char *ifn) if (lname && acct) { /* create the list */ - xs *list_id = list_maint(user, lname, 1); + xs *list_id = list_maint(user, lname, OP_ADD); xs *url = NULL; xs *uid = NULL; -- cgit From 5107778280bfe66c51ab0654babaf622580beb9e Mon Sep 17 00:00:00 2001 From: grunfink Date: Wed, 26 Aug 2026 10:21:23 +0200 Subject: Fixed ugly link label in 'Link' attachments when proxy media is on. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 503539c..e5c84fb 100644 --- a/html.c +++ b/html.c @@ -3135,7 +3135,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_tag("p", xs_html_tag("a", xs_html_attr("href", o_href), - xs_html_text(href)))); + xs_html_text(o_href)))); /* do not generate an Alt... */ name = NULL; -- cgit From 748b578c96bb5d6c3b372d533bc374e2fb918348 Mon Sep 17 00:00:00 2001 From: grunfink Date: Sun, 30 Aug 2026 07:43:18 +0200 Subject: Fixed EmojiReact code to allow any emoticon in emojis.json, not only those with colon-wrapped ids. --- activitypub.c | 6 +++--- html.c | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'html.c') diff --git a/activitypub.c b/activitypub.c index df319f0..bb6de13 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1607,7 +1607,7 @@ xs_dict *msg_emoji_init(snac *snac, const char *mid, const char *eid_o) { xs_dict *n_msg = msg_admiration(snac, mid, "EmojiReact"); - xs *eid = xs_strip_chars_i(xs_dup(eid_o), ":"); + xs *eid = xs_dup(eid_o); xs *content = NULL; xs *tag = xs_list_new(); xs *dict = xs_dict_new(); @@ -1623,14 +1623,14 @@ xs_dict *msg_emoji_init(snac *snac, const char *mid, const char *eid_o) content = xs_dup(eid); else if (*eid == '%') { - content = xs_url_dec_emoji(xs_dup(eid)); + content = xs_url_dec_emoji(eid); if (content == NULL) { xs_free(n_msg); return NULL; } } else { - content = xs_fmt(":%s:", eid); + content = xs_dup(eid); const char *emo = xs_dict_get(emjs, content); if (emo == NULL) { diff --git a/html.c b/html.c index e5c84fb..134cc87 100644 --- a/html.c +++ b/html.c @@ -5727,8 +5727,6 @@ int html_post_handler(const xs_dict *req, const char *q_path, if (strcmp(action, L("EmojiReact")) == 0) { /** **/ xs *eid = xs_dup(xs_dict_get(p_vars, "eid")); - eid = xs_strip_chars_i(eid, ":"); - xs *ret = msg_emoji_init(&snac, id, eid); /* fails if either invalid or already reacted */ if (!ret) { -- cgit From 61ef7a86183d7d8f5f6a5fcd6d945b91dcb68526 Mon Sep 17 00:00:00 2001 From: grunfink Date: Sun, 30 Aug 2026 16:38:58 +0200 Subject: Fixed error in notification filtering. --- data.c | 14 ++++++++++++-- html.c | 7 ++++--- snac.h | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'html.c') diff --git a/data.c b/data.c index 04b9e6e..fd6c91f 100644 --- a/data.c +++ b/data.c @@ -3576,7 +3576,7 @@ xs_list *notify_list(snac *snac, int skip, int show) } -xs_list *notify_filter_list(snac *snac, xs_list *notifs) +xs_list *notify_filter_list(snac *snac, xs_list *notifs, int skip, int show) /* apply user-defined notification filter to IDs */ { const xs_dict *n_filter = xs_dict_get(snac->config, "notify_filter"); @@ -3633,7 +3633,17 @@ xs_list *notify_filter_list(snac *snac, xs_list *notifs) continue; if (strcmp(type, "Announce") == 0 && !n_ann_on) continue; + + if (skip) { + skip--; + continue; + } + flt = xs_list_append(flt, v); + show--; + + if (show == 0) + break; } return flt; } @@ -3644,7 +3654,7 @@ int notify_new_num(snac *snac) { xs *t = notify_check_time(snac, 0); xs *lst_unfilt = notify_list(snac, 0, XS_ALL); - xs *lst = notify_filter_list(snac, lst_unfilt); + xs *lst = notify_filter_list(snac, lst_unfilt, 0, XS_ALL); int cnt = 0; xs_list *p = lst; diff --git a/html.c b/html.c index 134cc87..b26c252 100644 --- a/html.c +++ b/html.c @@ -4267,7 +4267,7 @@ xs_str *html_notifications(snac *user, int skip, int show) if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) proxy = user->actor; - xs *n_list_unfilt = notify_list(user, skip, show); + xs *n_list_unfilt = notify_list(user, 0, XS_ALL); xs *n_time = notify_check_time(user, 0); xs_html *body = html_user_body(user, 0); @@ -4278,7 +4278,7 @@ xs_str *html_notifications(snac *user, int skip, int show) user->tz = xs_dict_get_def(user->config, "tz", "UTC"); // previous line invalidates user->tz n_filter = xs_dict_get(user->config, "notify_filter"); } - xs *n_list = notify_filter_list(user, n_list_unfilt); + xs *n_list = notify_filter_list(user, n_list_unfilt, skip, show); /* all filters are true by default */ const xs_val *n_def = xs_stock( XSTYPE_TRUE ); int n_likes_on = xs_is_true(xs_dict_get_def(n_filter, "likes", n_def)); @@ -4607,7 +4607,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_text(L("None")))); /* add the navigation footer */ - xs *next_p = notify_list(user, skip + show, 1); + xs *next_p = notify_filter_list(user, n_list_unfilt, skip + show, 1); + if (xs_list_len(next_p)) { xs *url = xs_fmt("%s/notifications?skip=%d&show=%d", user->actor, skip + show, show); diff --git a/snac.h b/snac.h index 2acb1ec..80d2429 100644 --- a/snac.h +++ b/snac.h @@ -291,7 +291,7 @@ void notify_add(snac *snac, const char *type, const char *utype, xs_dict *notify_get(snac *snac, const char *id); int notify_new_num(snac *snac); xs_list *notify_list(snac *snac, int skip, int show); -xs_list *notify_filter_list(snac *snac, xs_list *ids); +xs_list *notify_filter_list(snac *snac, xs_list *ids, int skip, int show); void notify_clear(snac *snac); xs_dict *markers_get(snac *snac, const xs_list *markers); -- cgit From 8e521cce1f980edb6ce0bfd490e93e8ab5baa93a Mon Sep 17 00:00:00 2001 From: grunfink Date: Sun, 30 Aug 2026 16:46:38 +0200 Subject: Added notification filter for Webmentions. --- data.c | 3 +++ html.c | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'html.c') diff --git a/data.c b/data.c index fd6c91f..c38a0b3 100644 --- a/data.c +++ b/data.c @@ -3593,6 +3593,7 @@ xs_list *notify_filter_list(snac *snac, xs_list *notifs, int skip, int show) int n_folreq_on = xs_is_true(xs_dict_get_def(n_filter, "folreqs", n_def)); int n_blocks_on = xs_is_true(xs_dict_get_def(n_filter, "blocks", n_def)); int n_polls_on = xs_is_true(xs_dict_get_def(n_filter, "polls", n_def)); + int n_webmen_on = xs_is_true(xs_dict_get_def(n_filter, "webmentions", n_def)); const xs_str *v; xs_list *flt = xs_list_new(); @@ -3633,6 +3634,8 @@ xs_list *notify_filter_list(snac *snac, xs_list *notifs, int skip, int show) continue; if (strcmp(type, "Announce") == 0 && !n_ann_on) continue; + if (strcmp(type, "Webmention") == 0 && !n_webmen_on) + continue; if (skip) { skip--; diff --git a/html.c b/html.c index b26c252..3ba48d7 100644 --- a/html.c +++ b/html.c @@ -4246,6 +4246,7 @@ void notify_filter(snac *user, const xs_dict *p_vars) int folreq_on = (v = xs_dict_get(p_vars, "folreqs_on")) ? strcmp(v, "on") == 0 : 0; int blocks_on = (v = xs_dict_get(p_vars, "blocks_on")) ? strcmp(v, "on") == 0 : 0; int polls_on = (v = xs_dict_get(p_vars, "polls_on")) ? strcmp(v, "on") == 0 : 0; + int webmen_on = (v = xs_dict_get(p_vars, "webmentions_on")) ? strcmp(v, "on") == 0 : 0; xs *filter = xs_dict_new(); filter = xs_dict_set(filter, "likes", xs_stock(likes_on ? XSTYPE_TRUE : XSTYPE_FALSE)); filter = xs_dict_set(filter, "reacts", xs_stock(reacts_on ? XSTYPE_TRUE : XSTYPE_FALSE)); @@ -4256,6 +4257,7 @@ void notify_filter(snac *user, const xs_dict *p_vars) filter = xs_dict_set(filter, "folreqs", xs_stock(folreq_on ? XSTYPE_TRUE : XSTYPE_FALSE)); filter = xs_dict_set(filter, "blocks", xs_stock(blocks_on ? XSTYPE_TRUE : XSTYPE_FALSE)); filter = xs_dict_set(filter, "polls", xs_stock(polls_on ? XSTYPE_TRUE : XSTYPE_FALSE)); + filter = xs_dict_set(filter, "webmentions", xs_stock(webmen_on ? XSTYPE_TRUE : XSTYPE_FALSE)); user->config = xs_dict_set(user->config, "notify_filter", filter); user->tz = xs_dict_get_def(user->config, "tz", "UTC"); // previous line invalidates user->tz } @@ -4290,6 +4292,7 @@ xs_str *html_notifications(snac *user, int skip, int show) int n_folreq_on = xs_is_true(xs_dict_get_def(n_filter, "folreqs", n_def)); int n_blocks_on = xs_is_true(xs_dict_get_def(n_filter, "blocks", n_def)); int n_polls_on = xs_is_true(xs_dict_get_def(n_filter, "polls", n_def)); + int n_webmen_on = xs_is_true(xs_dict_get_def(n_filter, "webmentions", n_def)); xs_html *html = xs_html_tag("html", html_user_head(user, NULL, NULL), @@ -4316,6 +4319,7 @@ xs_str *html_notifications(snac *user, int skip, int show) html_checkbox("folreqs_on", L("Follow requests"), n_folreq_on), html_checkbox("blocks_on", L("Blocks"), n_blocks_on), html_checkbox("polls_on", L("Polls"), n_polls_on), + html_checkbox("webmentions_on", L("Webmentions"), n_webmen_on), xs_html_sctag("input", xs_html_attr("type", "submit"), xs_html_attr("class", "button"), -- cgit From 73015e2a6f46739f1d75ae32c915552aa58cbb19 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 08:39:27 +0200 Subject: Minor HTML tweak. --- html.c | 1 + 1 file changed, 1 insertion(+) (limited to 'html.c') diff --git a/html.c b/html.c index 3ba48d7..bcdeb62 100644 --- a/html.c +++ b/html.c @@ -1537,6 +1537,7 @@ xs_html *html_top_controls(snac *user) xs_html_attr("value", L("Like"))), xs_html_text(" "), xs_html_text(L("(by URL)"))), + xs_html_tag("p", NULL), xs_html_tag("form", xs_html_attr("autocomplete", "off"), xs_html_attr("method", "post"), -- cgit From c38ad28d5daa44b592a04192c7eeebed644593d4 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 14:25:12 +0200 Subject: New function text_browser_friendly() (still unused). --- html.c | 37 +++++++++++++++++++++++++++++++++++++ snac.c | 2 +- snac.h | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index bcdeb62..89034ec 100644 --- a/html.c +++ b/html.c @@ -4651,6 +4651,40 @@ void set_user_lang(snac *user) } +int text_browser_friendly(const snac *user, const char *user_agent) +/* returns true if the web UI must be adapted to text browsers */ +/* basically, this means to avoid details/summary as much as possible */ +{ + if (user != NULL && xs_is_string(user_agent)) { + snac_debug(user, 1, xs_fmt("user-agent: %s", user_agent)); + + const char *text_browser_uas = xs_dict_get(user->config, "text_browser_uas"); + + if (xs_is_string(text_browser_uas)) { + xs *l = xs_split(text_browser_uas, "\n"); + const char *v; + + xs_list_foreach(l, v) { + if (*v == '\0') + continue; + + xs *v2 = xs_strip_i(xs_dup(v)); + + /* a * means "all browsers", then yes */ + if (strcmp(v2, "*") == 0) + return 1; + + /* if this string is inside user_agent, then yes */ + if (xs_str_in(user_agent, v2) != -1) + return 1; + } + } + } + + return 0; +} + + int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag, xs_str **last_modified) @@ -4665,6 +4699,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int save = 1; int proxy = 0; int terse = 0; + int tb_friendly = 0; const char *v; const xs_dict *q_vars = xs_dict_get(req, "q_vars"); @@ -4734,6 +4769,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) proxy = 1; + tb_friendly = text_browser_friendly(user, xs_dict_get(req, "user-agent")); + /* return the RSS if requested by Accept header */ if (accept != NULL) { if (xs_str_in(accept, "text/xml") != -1 || diff --git a/snac.c b/snac.c index 8afe1f8..d2dbc7d 100644 --- a/snac.c +++ b/snac.c @@ -127,7 +127,7 @@ void srv_log(xs_str *str) } -void snac_log(snac *snac, xs_str *str) +void snac_log(const snac *snac, xs_str *str) /* prints a user debugging information */ { xs *o_str = str; diff --git a/snac.h b/snac.h index 80d2429..1c505ec 100644 --- a/snac.h +++ b/snac.h @@ -97,7 +97,7 @@ enum { SCOPE_FOLLOWERS = 3, }; -void snac_log(snac *user, xs_str *str); +void snac_log(const snac *user, xs_str *str); #define snac_debug(user, level, str) do { if (dbglevel >= (level)) \ { snac_log((user), (str)); } } while (0) -- cgit From 2e4a9a94208d6ecdd4b70aff7d7c7d6dddefdfaa Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 15:01:08 +0200 Subject: More text browser friendly stuff. --- html.c | 71 +++++++++++++++++++++++++++++++++++++++++------------------------ httpd.c | 4 ++-- snac.h | 3 ++- 3 files changed, 49 insertions(+), 29 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index 89034ec..5cbc096 100644 --- a/html.c +++ b/html.c @@ -2081,7 +2081,7 @@ xs_str *build_mentions(snac *user, const xs_dict *msg) xs_html *html_entry_controls(snac *user, const char *actor, - const xs_dict *msg, const char *md5) + const xs_dict *msg, const char *md5, int tb_friendly) { const char *id = xs_dict_get(msg, "id"); const char *group = xs_dict_get(msg, "audience"); @@ -2193,6 +2193,21 @@ xs_html *html_entry_controls(snac *user, const char *actor, xs_html_add(form, html_button("hide", L("Hide"), L("Hide this post and its children"))); + if (tb_friendly) { + /* generate a link a More... text pointing to the one-post-only timeline */ + xs *url = xs_fmt("%s/admin/p/%s#%s_entry", user->actor, md5, md5); + + xs_html_add(controls, + xs_html_tag("p", NULL), + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_text(L("More...")))); + + /* and DONE */ + + return controls; + } + const char *prev_src = xs_dict_get(msg, "sourceContent"); if (!xs_is_null(prev_src) && strcmp(actor, user->actor) == 0) { /** edit **/ @@ -2309,7 +2324,7 @@ static const xs_str* words_in_content(const xs_list *words, const xs_val *conten xs_html *html_entry(snac *user, xs_dict *msg, int read_only, - int level, const char *md5, int hide_children) + int level, const char *md5, int hide_children, int tb_friendly) { const char *id = xs_dict_get(msg, "id"); const char *type = xs_dict_get(msg, "type"); @@ -2872,7 +2887,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_add(snac_content, xs_html_tag("blockquote", xs_html_attr("class", "snac-quoted-post"), - html_entry(user, quoted_post, 1, level + 1, md5, 1))); + html_entry(user, quoted_post, 1, level + 1, md5, 1, tb_friendly))); } else if (user) @@ -3320,7 +3335,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (!read_only && user) { xs_html_add(entry, - html_entry_controls(user, actor, msg, md5)); + html_entry_controls(user, actor, msg, md5, tb_friendly)); } /** children **/ @@ -3382,7 +3397,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, so that it appears unindented just before the parent like a fucking Twitter-like thread */ xs_html_add(fch_container, - html_entry(user, f_chd, read_only, level + 1, cmd5, hide_children)); + html_entry(user, f_chd, read_only, level + 1, cmd5, hide_children, tb_friendly)); cnt++; f_cnt++; @@ -3403,7 +3418,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (chd != NULL) { if (xs_is_null(xs_dict_get(chd, "name"))) { xs_html *che = html_entry(user, chd, read_only, - level + 1, cmd5, hide_children); + level + 1, cmd5, hide_children, tb_friendly); if (che != NULL) { if (left > 3) { @@ -3470,7 +3485,7 @@ xs_html *html_footer(const snac *user) xs_str *html_timeline(snac *user, const xs_list *list, int read_only, int skip, int show, int show_more, const char *title, const char *page, - int utl, const char *error, int terse) + int utl, const char *error, int terse, int tb_friendly) /* returns the HTML for the timeline */ { const char *v; @@ -3537,10 +3552,9 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs *lists = list_maint(user, NULL, OP_LIST); /* get list of lists */ - int ct = 0; const char *v; - while (xs_list_next(lists, &v, &ct)) { + xs_list_foreach(lists, v) { const char *lname = xs_list_get(v, 1); xs *url = xs_fmt("%s/list/%s", user->actor, xs_list_get(v, 0)); xs *ttl = xs_fmt(L("Timeline for list '%s'"), lname); @@ -3664,6 +3678,11 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, int show_unlisted = user ? xs_is_true(xs_dict_get(user->config, "show_unlisted")) : 0; + /* disable text-browser friendliness if it's just one post, + to force the details/summary controls to appear */ + if (xs_list_len(list) == 1) + tb_friendly = 0; + xs_list_foreach(list, v) { xs *msg = NULL; int status; @@ -3730,7 +3749,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, continue; } - xs_html *entry = html_entry(user, msg, read_only, 0, v, (user && !hide_children) ? 0 : 1); + xs_html *entry = html_entry(user, msg, read_only, 0, v, (user && !hide_children) ? 0 : 1, tb_friendly); if (entry != NULL) xs_html_add(posts, @@ -4182,7 +4201,7 @@ xs_str *html_people_one(snac *user, const char *actor, const xs_list *list, xs_list_in((reacts = object_get_emoji_reacts(id)), actor_md5) == -1))) continue; - xs_html *entry = html_entry(user, msg, 0, 0, v, 1); + xs_html *entry = html_entry(user, msg, 0, 0, v, 1, 0); if (entry != NULL) xs_html_add(lists, @@ -4554,7 +4573,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs *md5 = xs_md5_hex(id, strlen(id)); xs *ctxt = xs_fmt("%s/admin/p/%s#%s_entry", user->actor, md5, md5); - xs_html *h = html_entry(user, obj, 0, 0, md5, 1); + xs_html *h = html_entry(user, obj, 0, 0, md5, 1, 0); if (h != NULL) { xs_html_add(entry, @@ -4822,7 +4841,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE) { /** empty public timeline for private users **/ - *body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL, "", 1, error, terse); + *body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL, "", 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -4845,7 +4864,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *pins = pinned_list(&snac); pins = xs_list_cat(pins, list); - *body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error, terse); + *body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; @@ -4973,7 +4992,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *title = xs_fmt(xs_list_len(tl) ? L("Search results for tag %s") : L("Nothing found for tag %s"), q); - *body = html_timeline(&snac, tl, 0, skip, show, more, title, page, 0, error, terse); + *body = html_timeline(&snac, tl, 0, skip, show, more, title, page, 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -4998,7 +5017,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, title = xs_fmt(L("Nothing found for '%s'"), q); *body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, - title, page, 0, error, terse); + title, page, 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5025,7 +5044,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = timeline_list(&snac, "private", skip, show, &more); *body = html_timeline(&snac, list, 0, skip, show, - more, NULL, "/admin", 1, error, terse); + more, NULL, "/admin", 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; @@ -5052,7 +5071,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list0 = xs_list_append(xs_list_new(), md5); xs *list = timeline_top_level(&snac, list0); - *body = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1, error, terse); + *body = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5124,7 +5143,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *next = timeline_instance_list(skip + show, 1); *body = html_timeline(&snac, list, 0, skip, show, - xs_list_len(next), L("Showing instance timeline"), "/instance", 0, error, terse); + xs_list_len(next), L("Showing instance timeline"), "/instance", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5139,7 +5158,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = pinned_list(&snac); *body = html_timeline(&snac, list, 0, skip, show, - 0, L("Pinned posts"), "", 0, error, terse); + 0, L("Pinned posts"), "", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5155,7 +5174,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = timeline_list(&snac, "admire", skip, show, &more); *body = html_timeline(&snac, list, 0, skip, show, - more, L("Liked, boosted or reacted posts"), "/admirations", 0, error, terse); + more, L("Liked, boosted or reacted posts"), "/admirations", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5170,7 +5189,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = bookmark_list(&snac); *body = html_timeline(&snac, list, 0, skip, show, - 0, L("Bookmarked posts"), "", 0, error, terse); + 0, L("Bookmarked posts"), "", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5185,7 +5204,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = draft_list(&snac); *body = html_timeline(&snac, list, 0, skip, show, - 0, L("Post drafts"), "", 0, error, terse); + 0, L("Post drafts"), "", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5200,7 +5219,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = scheduled_list(&snac); *body = html_timeline(&snac, list, 0, skip, show, - 0, L("Scheduled posts"), "", 0, error, terse); + 0, L("Scheduled posts"), "", 0, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5226,7 +5245,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *title = xs_fmt(L("Showing timeline for list '%s'"), name); *body = html_timeline(&snac, ttl, 0, skip, show, - xs_list_len(next), title, base, 1, error, terse); + xs_list_len(next), title, base, 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } @@ -5246,7 +5265,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, list = xs_list_append(list, md5); - *body = html_timeline(&snac, list, 1, 0, 0, 0, NULL, "", 1, error, terse); + *body = html_timeline(&snac, list, 1, 0, 0, 0, NULL, "", 1, error, terse, tb_friendly); *b_size = strlen(*body); status = HTTP_STATUS_OK; } diff --git a/httpd.c b/httpd.c index 5b503b9..3504f3e 100644 --- a/httpd.c +++ b/httpd.c @@ -258,7 +258,7 @@ int server_get_handler(xs_dict *req, const char *q_path, else { xs *page = xs_fmt("?t=%s", t); xs *title = xs_fmt(L("Search results for tag #%s"), t); - *body = html_timeline(NULL, tl, 0, skip, show, more, title, page, 0, NULL, 0); + *body = html_timeline(NULL, tl, 0, skip, show, more, title, page, 0, NULL, 0, 0); } } else @@ -266,7 +266,7 @@ int server_get_handler(xs_dict *req, const char *q_path, /** instance timeline **/ xs *tl = timeline_instance_list(0, 30); *body = html_timeline(NULL, tl, 0, 0, 0, 0, - L("Recent posts by users in this instance"), NULL, 0, NULL, 0); + L("Recent posts by users in this instance"), NULL, 0, NULL, 0, 0); } else *body = greeting_html(); diff --git a/snac.h b/snac.h index 1c505ec..2d3c7d0 100644 --- a/snac.h +++ b/snac.h @@ -438,7 +438,8 @@ xs_str *encode_html(const char *str); xs_str *html_timeline(snac *user, const xs_list *list, int read_only, int skip, int show, int show_more, - const char *title, const char *page, int utl, const char *error, int terse); + const char *title, const char *page, + int utl, const char *error, int terse, int tb_friendly); int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, -- cgit From 3b19a508d292e52cca82b2c1d157f1018674af1b Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 15:18:06 +0200 Subject: New user page /operations. --- html.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'html.c') diff --git a/html.c b/html.c index 5cbc096..1181644 100644 --- a/html.c +++ b/html.c @@ -4874,6 +4874,28 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else + if (strcmp(p_path, "operations") == 0) { /** operations and user settings **/ + if (!login(&snac, req)) { + *body = xs_dup(uid); + status = HTTP_STATUS_UNAUTHORIZED; + } + else { + xs_html *h_body = html_user_body(&snac, 0); + + xs_html *html = xs_html_tag("html", + html_user_head(&snac, NULL, NULL), + h_body); + + xs_html_add(h_body, + html_top_controls(&snac), + html_footer(&snac)); + + *body = xs_html_render_s(html, "\n"); + *b_size = strlen(*body); + status = HTTP_STATUS_OK; + } + } + else if (strcmp(p_path, "admin") == 0) { /** private timeline **/ if (!login(&snac, req)) { *body = xs_dup(uid); -- cgit From 32953e4d2d23e0ded2a8cbd96d1b73f6d91b8953 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 15:26:02 +0200 Subject: More text browser friendly things. --- html.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'html.c') diff --git a/html.c b/html.c index 1181644..2b7698f 100644 --- a/html.c +++ b/html.c @@ -1465,9 +1465,18 @@ xs_html *html_checkbox(const char *form_name, const char *label, int flag) } -xs_html *html_top_controls(snac *user) +xs_html *html_top_controls(snac *user, int tb_friendly) /* generates the top controls */ { + if (tb_friendly) { + /* generate only a link to the /operations entrypoint */ + xs *url = xs_fmt("%s/operations", user->actor); + + return xs_html_tag("a", + xs_html_attr("href", url), + xs_html_text(L("Operations..."))); + } + xs *ops_action = xs_fmt("%s/admin/action", user->actor); xs_html *top_controls = xs_html_tag("div", @@ -3529,7 +3538,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, if (user && !read_only) xs_html_add(body, - html_top_controls(user)); + html_top_controls(user, tb_friendly)); if (error != NULL) { xs_html_add(body, @@ -4887,7 +4896,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, h_body); xs_html_add(h_body, - html_top_controls(&snac), + html_top_controls(&snac, 0), html_footer(&snac)); *body = xs_html_render_s(html, "\n"); -- cgit From 7a62d550de0fe0f76fe5c2c789fe331ea4fe5046 Mon Sep 17 00:00:00 2001 From: grunfink Date: Tue, 1 Sep 2026 16:19:37 +0200 Subject: Added web UI for setting the 'text_browser_uas' user config field. --- html.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index 2b7698f..a639269 100644 --- a/html.c +++ b/html.c @@ -1624,6 +1624,7 @@ xs_html *html_top_controls(snac *user, int tb_friendly) const char *webhook = xs_dict_get_def(user->config, "notify_webhook", ""); const char *post_langs = xs_dict_get_def(user->config, "post_langs", ""); const char *excluded_langs = xs_dict_get_def(user->config, "excluded_langs", ""); + const char *text_browser_uas = xs_dict_get_def(user->config, "text_browser_uas", ""); xs *metadata = NULL; const xs_dict *md = xs_dict_get(user->config, "metadata"); @@ -1870,6 +1871,16 @@ xs_html *html_top_controls(snac *user, int tb_friendly) xs_html_attr("value", excluded_langs), xs_html_attr("placeholder", L("en fr es de_AT")))), + xs_html_tag("p", + xs_html_text(L("Show a simpler web UI for browsers with these user agents (one per line):")), + xs_html_sctag("br", NULL), + xs_html_tag("textarea", + xs_html_attr("name", "text_browser_uas"), + xs_html_attr("cols", "40"), + xs_html_attr("rows", "4"), + xs_html_attr("placeholder", "Links\nLynx\nNetSurf"), + xs_html_text(text_browser_uas))), + xs_html_tag("p", xs_html_text(L("New password:")), xs_html_sctag("br", NULL), @@ -4698,7 +4709,7 @@ int text_browser_friendly(const snac *user, const char *user_agent) xs *v2 = xs_strip_i(xs_dup(v)); - /* a * means "all browsers", then yes */ + /* an asterisk alone means "all browsers", so yes */ if (strcmp(v2, "*") == 0) return 1; @@ -6119,6 +6130,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, if ((v = xs_dict_get(p_vars, "metadata")) != NULL) snac.config = xs_dict_set(snac.config, "metadata", v); + snac.config = xs_dict_set(snac.config, "text_browser_uas", xs_dict_get_def(p_vars, "text_browser_uas", "")); + /* uploads */ const char *uploads[] = { "avatar", "header", NULL }; int n; -- cgit From 3f5f9d0d0d5191a09ed04c8db970a6e5e077dcc1 Mon Sep 17 00:00:00 2001 From: grunfink Date: Wed, 2 Sep 2026 10:30:17 +0200 Subject: Increased user-agent debug level. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'html.c') diff --git a/html.c b/html.c index a639269..1ec4296 100644 --- a/html.c +++ b/html.c @@ -4695,7 +4695,7 @@ int text_browser_friendly(const snac *user, const char *user_agent) /* basically, this means to avoid details/summary as much as possible */ { if (user != NULL && xs_is_string(user_agent)) { - snac_debug(user, 1, xs_fmt("user-agent: %s", user_agent)); + snac_debug(user, 2, xs_fmt("user-agent: %s", user_agent)); const char *text_browser_uas = xs_dict_get(user->config, "text_browser_uas"); -- cgit