From 3b818f557ae3c1073f1464fe0a7319a0e025ba86 Mon Sep 17 00:00:00 2001 From: Eirik Oeverby Date: Mon, 6 Jan 2025 20:03:00 +0100 Subject: Add short_description_raw option --- doc/snac.8 | 2 ++ html.c | 30 ++++++++++++++++++++---------- utils.c | 1 + 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/doc/snac.8 b/doc/snac.8 index 3a4bfcf..db47ae5 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -205,6 +205,8 @@ The email address of the instance administrator (optional). The user name of the instance administrator (optional). .It Ic short_description A textual short description about the instance (optional). +.It Ic short_description_raw +Whether to interpret short_descript as raw string or convert to HTML (optional) .It Ic fastcgi If set to true, .Nm diff --git a/html.c b/html.c index e8803ab..b54556d 100644 --- a/html.c +++ b/html.c @@ -537,10 +537,11 @@ xs_html *html_instance_head(void) static xs_html *html_instance_body(void) { - const char *host = xs_dict_get(srv_config, "host"); - const char *sdesc = xs_dict_get(srv_config, "short_description"); - const char *email = xs_dict_get(srv_config, "admin_email"); - const char *acct = xs_dict_get(srv_config, "admin_account"); + const char *host = xs_dict_get(srv_config, "host"); + const char *sdesc = xs_dict_get(srv_config, "short_description"); + const char *sdescraw = xs_dict_get(srv_config, "short_description_raw"); + const char *email = xs_dict_get(srv_config, "admin_email"); + const char *acct = xs_dict_get(srv_config, "admin_account"); xs *blurb = xs_replace(snac_blurb, "%host%", host); @@ -553,12 +554,21 @@ static xs_html *html_instance_body(void) dl = xs_html_tag("dl", NULL))); if (sdesc && *sdesc) { - xs_html_add(dl, - xs_html_tag("di", - xs_html_tag("dt", - xs_html_text(L("Site description"))), - xs_html_tag("dd", - xs_html_text(sdesc)))); + if (!xs_is_null(sdescraw) && xs_type(sdescraw) == XSTYPE_TRUE) { + xs_html_add(dl, + xs_html_tag("di", + xs_html_tag("dt", + xs_html_text(L("Site description"))), + xs_html_tag("dd", + xs_html_raw(sdesc)))); + } else { + xs_html_add(dl, + xs_html_tag("di", + xs_html_tag("dt", + xs_html_text(L("Site description"))), + xs_html_tag("dd", + xs_html_text(sdesc)))); + } } if (email && *email) { xs *mailto = xs_fmt("mailto:%s", email); diff --git a/utils.c b/utils.c index 0740d4d..0f97a2f 100644 --- a/utils.c +++ b/utils.c @@ -36,6 +36,7 @@ static const char *default_srv_config = "{" "\"admin_account\": \"\"," "\"title\": \"\"," "\"short_description\": \"\"," + "\"short_description_raw\": false," "\"protocol\": \"https\"," "\"fastcgi\": false" "}"; -- cgit v1.2.3 From 4528029dabb7d79cddefaa5677ae314e16ab51f4 Mon Sep 17 00:00:00 2001 From: Eirik Oeverby Date: Mon, 6 Jan 2025 20:03:28 +0100 Subject: Add short_description_raw option --- doc/snac.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snac.8 b/doc/snac.8 index db47ae5..c44a891 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -206,7 +206,7 @@ The user name of the instance administrator (optional). .It Ic short_description A textual short description about the instance (optional). .It Ic short_description_raw -Whether to interpret short_descript as raw string or convert to HTML (optional) +Whether to interpret short_descript as raw string or convert to HTML (optional). .It Ic fastcgi If set to true, .Nm -- cgit v1.2.3 From 5c303d539b48cdec5cc9cca77472faaa7c5c8d71 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 Jan 2025 14:54:55 +0100 Subject: Improved notification messages. --- html.c | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/html.c b/html.c index 3fd1e4b..e1f21bb 100644 --- a/html.c +++ b/html.c @@ -2967,8 +2967,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("class", "snac-posts")); xs_html_add(body, posts); - xs_set rep; - xs_set_init(&rep); + /* dict to store previous notification labels */ + xs *admiration_labels = xs_dict_new(); const xs_str *v; @@ -2995,8 +2995,8 @@ xs_str *html_notifications(snac *user, int skip, int show) const char *msg_id = NULL; - if (xs_is_dict(obj) && (msg_id = xs_dict_get(obj, "id")) && xs_set_add(&rep, msg_id) != 1) - continue; + if (xs_is_dict(obj)) + msg_id = xs_dict_get(obj, "id"); const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; @@ -3030,9 +3030,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs *s_date = xs_crop_i(xs_dup(date), 0, 10); - xs_html *entry = xs_html_tag("div", - xs_html_attr("class", "snac-post-with-desc"), - xs_html_tag("p", + xs_html *this_html_label = xs_html_container( xs_html_tag("b", xs_html_text(label), xs_html_text(" by "), @@ -3043,7 +3041,37 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_tag("time", xs_html_attr("class", "dt-published snac-pubdate"), xs_html_attr("title", date), - xs_html_text(s_date)))); + xs_html_text(s_date))); + + xs_html *html_label = NULL; + + if (xs_is_string(msg_id)) { + const xs_val *prev_label = xs_dict_get(admiration_labels, msg_id); + + if (xs_type(prev_label) == XSTYPE_DATA) { + /* there is a previous list of admiration labels! */ + xs_data_get(&html_label, prev_label); + + xs_html_add(html_label, + xs_html_sctag("br", NULL), + this_html_label); + + continue; + } + } + + xs_html *entry = NULL; + + html_label = xs_html_tag("p", + this_html_label); + + /* store in the admiration labels dict */ + xs *pl = xs_data_new(&html_label, sizeof(html_label)); + admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); + + entry = xs_html_tag("div", + xs_html_attr("class", "snac-post-with-desc"), + html_label); if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { xs_html_add(entry, @@ -3111,8 +3139,6 @@ xs_str *html_notifications(snac *user, int skip, int show) } } - xs_set_free(&rep); - if (noti_new == NULL && noti_seen == NULL) xs_html_add(body, xs_html_tag("h2", -- cgit v1.2.3 From 71537bdaf8fa3e752b7178efdf53e5c1c1402973 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 05:29:01 +0100 Subject: Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index c0e512d..344dbaa 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.69" +#define VERSION "2.70-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From 896a36837c8da070d85abe92d62d12c4009fca1c Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 21:45:14 +0100 Subject: Another tweak to fix repeated notifications. --- html.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/html.c b/html.c index e1f21bb..31d8d97 100644 --- a/html.c +++ b/html.c @@ -2967,6 +2967,9 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_attr("class", "snac-posts")); xs_html_add(body, posts); + xs_set rep; + xs_set_init(&rep); + /* dict to store previous notification labels */ xs *admiration_labels = xs_dict_new(); @@ -2983,6 +2986,7 @@ xs_str *html_notifications(snac *user, int skip, int show) const char *utype = xs_dict_get(noti, "utype"); const char *id = xs_dict_get(noti, "objid"); const char *date = xs_dict_get(noti, "date"); + const char *id2 = xs_dict_get_path(noti, "msg.id"); xs *wrk = NULL; if (xs_is_null(id)) @@ -2991,6 +2995,9 @@ xs_str *html_notifications(snac *user, int skip, int show) if (is_hidden(user, id)) continue; + if (xs_is_string(id2) && xs_set_add(&rep, id2) != 1) + continue; + object_get(id, &obj); const char *msg_id = NULL; @@ -3158,6 +3165,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_text(L("More..."))))); } + xs_set_free(&rep); + xs_html_add(body, html_footer()); -- cgit v1.2.3 From b2f5f95c7b8c8793f82d394009b0908888de5bdc Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 23:06:08 +0100 Subject: Fixed repeated entries in the instance public timeline. --- data.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/data.c b/data.c index 33947ff..40382d2 100644 --- a/data.c +++ b/data.c @@ -1399,11 +1399,13 @@ void timeline_update_indexes(snac *snac, const char *id) if (valid_status(object_get(id, &msg))) { /* if its ours and is public, also store in public */ if (is_msg_public(msg)) { - object_user_cache_add(snac, id, "public"); - - /* also add it to the instance public timeline */ - xs *ipt = xs_fmt("%s/public.idx", srv_basedir); - index_add(ipt, id); + if (object_user_cache_add(snac, id, "public") >= 0) { + /* also add it to the instance public timeline */ + xs *ipt = xs_fmt("%s/public.idx", srv_basedir); + index_add(ipt, id); + } + else + srv_debug(1, xs_fmt("Not added to public instance index %s", id)); } } } @@ -1524,8 +1526,17 @@ xs_list *timeline_instance_list(int skip, int show) /* returns the timeline for the full instance */ { xs *idx = instance_index_fn(); + xs *lst = index_list_desc(idx, skip, show); - return index_list_desc(idx, skip, show); + /* make the list unique */ + xs_set rep; + xs_set_init(&rep); + const char *md5; + + xs_list_foreach(lst, md5) + xs_set_add(&rep, md5); + + return xs_set_result(&rep); } -- cgit v1.2.3 From 5d267968cb0d2670f8a4bd7587e505f812dfa3bc Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 Jan 2025 23:13:59 +0100 Subject: Fixed crash in the notification area after deleting a post. --- html.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 31d8d97..3b63cfc 100644 --- a/html.c +++ b/html.c @@ -3074,7 +3074,9 @@ xs_str *html_notifications(snac *user, int skip, int show) /* store in the admiration labels dict */ xs *pl = xs_data_new(&html_label, sizeof(html_label)); - admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); + + if (xs_is_string(msg_id)) + admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); entry = xs_html_tag("div", xs_html_attr("class", "snac-post-with-desc"), -- cgit v1.2.3 From 6097ba55cf96444aac7fb9b74295722e8e5c3810 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 26 Jan 2025 17:55:36 +0100 Subject: New command-line option 'unmute'. --- main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/main.c b/main.c index a57adb5..347c495 100644 --- a/main.c +++ b/main.c @@ -49,6 +49,7 @@ int usage(void) printf("unblock {basedir} {instance_url} Unblocks a full instance\n"); printf("limit {basedir} {uid} {actor} Limits an actor (drops their announces)\n"); printf("unlimit {basedir} {uid} {actor} Unlimits an actor\n"); + printf("unmute {basedir} {uid} {actor} Unmutes a previously muted actor\n"); printf("verify_links {basedir} {uid} Verifies a user's links (in the metadata)\n"); printf("search {basedir} {uid} {regex} Searches posts by content\n"); printf("export_csv {basedir} {uid} Exports data as CSV files\n"); @@ -446,6 +447,18 @@ int main(int argc, char *argv[]) return 0; } + if (strcmp(cmd, "unmute") == 0) { /** **/ + if (is_muted(&snac, url)) { + unmute(&snac, url); + + printf("%s unmuted\n", url); + } + else + printf("%s actor is not muted\n", url); + + return 0; + } + if (strcmp(cmd, "search") == 0) { /** **/ int to; -- cgit v1.2.3 From 82bcc4b465f73a5d1f2eebcf3813452bc1c37fbd Mon Sep 17 00:00:00 2001 From: default Date: Mon, 27 Jan 2025 16:59:08 +0100 Subject: Minor optimization in timeline retrieving. Functions now receive an optional int *more, set to 1 if there are more than the 'show' requested. --- activitypub.c | 2 +- data.c | 24 ++++++++++++++++++------ html.c | 25 +++++++++++-------------- mastoapi.c | 2 +- snac.h | 4 ++-- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/activitypub.c b/activitypub.c index cade0d9..bcb733a 100644 --- a/activitypub.c +++ b/activitypub.c @@ -3081,7 +3081,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, int cnt = xs_number_get(xs_dict_get_def(srv_config, "max_public_entries", "20")); /* get the public outbox or the pinned list */ - xs *elems = *p_path == 'o' ? timeline_simple_list(&snac, "public", 0, cnt) : pinned_list(&snac); + xs *elems = *p_path == 'o' ? timeline_simple_list(&snac, "public", 0, cnt, NULL) : pinned_list(&snac); xs_list_foreach(elems, v) { xs *i = NULL; diff --git a/data.c b/data.c index 40382d2..550f7cf 100644 --- a/data.c +++ b/data.c @@ -1489,16 +1489,28 @@ xs_str *user_index_fn(snac *user, const char *idx_name) } -xs_list *timeline_simple_list(snac *user, const char *idx_name, int skip, int show) +xs_list *timeline_simple_list(snac *user, const char *idx_name, int skip, int show, int *more) /* returns a timeline (with all entries) */ { xs *idx = user_index_fn(user, idx_name); - return index_list_desc(idx, skip, show); + /* if a more flag is sent, request one more */ + xs_list *lst = index_list_desc(idx, skip, show + (more != NULL ? 1 : 0)); + + if (more != NULL) { + if (xs_list_len(lst) > show) { + *more = 1; + lst = xs_list_del(lst, -1); + } + else + *more = 0; + } + + return lst; } -xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show) +xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show, int *more) /* returns a timeline (only top level entries) */ { int c_max; @@ -1510,7 +1522,7 @@ xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show) if (show > c_max) show = c_max; - xs *list = timeline_simple_list(snac, idx_name, skip, show); + xs *list = timeline_simple_list(snac, idx_name, skip, show, more); return timeline_top_level(snac, list); } @@ -2709,9 +2721,9 @@ xs_list *content_search(snac *user, const char *regex, const char *md5s[3] = {0}; int c[3] = {0}; - tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* public */ + tls[0] = timeline_simple_list(user, "public", 0, XS_ALL, NULL); /* public */ tls[1] = timeline_instance_list(0, XS_ALL); /* instance */ - tls[2] = priv ? timeline_simple_list(user, "private", 0, XS_ALL) : xs_list_new(); /* private or none */ + tls[2] = priv ? timeline_simple_list(user, "private", 0, XS_ALL, NULL) : xs_list_new(); /* private or none */ /* first positioning */ for (int n = 0; n < 3; n++) diff --git a/html.c b/html.c index 3b63cfc..bd94e9f 100644 --- a/html.c +++ b/html.c @@ -3314,21 +3314,17 @@ int html_get_handler(const xs_dict *req, const char *q_path, } else { xs *list = NULL; - xs *next = NULL; + int more = 0; - if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) { - list = timeline_simple_list(&snac, "public", skip, show); - next = timeline_simple_list(&snac, "public", skip + show, 1); - } - else { - list = timeline_list(&snac, "public", skip, show); - next = timeline_list(&snac, "public", skip + show, 1); - } + if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) + list = timeline_simple_list(&snac, "public", skip, show, &more); + else + list = timeline_list(&snac, "public", skip, show, &more); xs *pins = pinned_list(&snac); pins = xs_list_cat(pins, list); - *body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL, "", 1, error); + *body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error); *b_size = strlen(*body); status = HTTP_STATUS_OK; @@ -3490,13 +3486,14 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs_dict_get(req, "if-none-match"), etag); } else { + int more = 0; + snac_debug(&snac, 1, xs_fmt("building timeline")); - xs *list = timeline_list(&snac, "private", skip, show); - xs *next = timeline_list(&snac, "private", skip + show, 1); + xs *list = timeline_list(&snac, "private", skip, show, &more); *body = html_timeline(&snac, list, 0, skip, show, - xs_list_len(next), NULL, "/admin", 1, error); + more, NULL, "/admin", 1, error); *b_size = strlen(*body); status = HTTP_STATUS_OK; @@ -3702,7 +3699,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int cnt = xs_number_get(xs_dict_get_def(srv_config, "max_public_entries", "20")); - xs *elems = timeline_simple_list(&snac, "public", 0, cnt); + xs *elems = timeline_simple_list(&snac, "public", 0, cnt, NULL); xs *bio = xs_dup(xs_dict_get(snac.config, "bio")); xs *rss_title = xs_fmt("%s (@%s@%s)", diff --git a/mastoapi.c b/mastoapi.c index 54b4333..3c445c2 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1676,7 +1676,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, else if (strcmp(opt, "statuses") == 0) { /** **/ /* the public list of posts of a user */ - xs *timeline = timeline_simple_list(&snac2, "public", 0, 256); + xs *timeline = timeline_simple_list(&snac2, "public", 0, 256, NULL); xs_list *p = timeline; const xs_str *v; diff --git a/snac.h b/snac.h index 344dbaa..278e798 100644 --- a/snac.h +++ b/snac.h @@ -157,8 +157,8 @@ int timeline_here(snac *snac, const char *md5); int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg); int timeline_del(snac *snac, const char *id); xs_str *user_index_fn(snac *user, const char *idx_name); -xs_list *timeline_simple_list(snac *user, const char *idx_name, int skip, int show); -xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show); +xs_list *timeline_simple_list(snac *user, const char *idx_name, int skip, int show, int *more); +xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show, int *more); int timeline_add(snac *snac, const char *id, const xs_dict *o_msg); int timeline_admire(snac *snac, const char *id, const char *admirer, int like); -- cgit v1.2.3 From 1766d6bf92eb433841c03ccb096c636a4c5dc968 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 27 Jan 2025 20:20:40 +0100 Subject: Added a 'No more unseen posts' mark. --- data.c | 8 ++++++++ html.c | 24 ++++++++++++++++++++++++ snac.h | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/data.c b/data.c index 550f7cf..480a35b 100644 --- a/data.c +++ b/data.c @@ -1528,6 +1528,14 @@ xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show, int } +void timeline_add_mark(snac *user) +/* adds an "already seen" mark to the private timeline */ +{ + xs *fn = xs_fmt("%s/private.idx", user->basedir); + index_add_md5(fn, MD5_ALREADY_SEEN_MARK); +} + + xs_str *instance_index_fn(void) { return xs_fmt("%s/public.idx", srv_basedir); diff --git a/html.c b/html.c index bd94e9f..771d2af 100644 --- a/html.c +++ b/html.c @@ -2648,10 +2648,31 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, posts); + int mark_shown = 0; + while (xs_list_iter(&p, &v)) { xs *msg = NULL; int status; + /* "already seen" mark? */ + if (strcmp(v, MD5_ALREADY_SEEN_MARK) == 0) { + if (skip == 0 && !mark_shown) { + xs *s = xs_fmt("%s/admin#top", user->actor); + + xs_html_add(posts, + xs_html_text(L("No more unseen posts")), + xs_html_text(" - "), + xs_html_tag("a", + xs_html_attr("href", s), + xs_html_text(L("Back to top"))), + xs_html_sctag("hr", NULL)); + } + + mark_shown = 1; + + continue; + } + if (utl && user && !is_pinned_by_md5(user, v)) status = timeline_get_by_md5(user, v, &msg); else @@ -3473,6 +3494,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else { + /** the private timeline **/ double t = history_mtime(&snac, "timeline.html_"); /* if enabled by admin, return a cached page if its timestamp is: @@ -3492,6 +3514,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = timeline_list(&snac, "private", skip, show, &more); + timeline_add_mark(&snac); + *body = html_timeline(&snac, list, 0, skip, show, more, NULL, "/admin", 1, error); diff --git a/snac.h b/snac.h index 278e798..6c3ccda 100644 --- a/snac.h +++ b/snac.h @@ -22,6 +22,8 @@ #define MD5_HEX_SIZE 33 +#define MD5_ALREADY_SEEN_MARK "00000000000000000000000000000000" + extern double disk_layout; extern xs_str *srv_basedir; extern xs_dict *srv_config; @@ -163,6 +165,8 @@ int timeline_add(snac *snac, const char *id, const xs_dict *o_msg); int timeline_admire(snac *snac, const char *id, const char *admirer, int like); xs_list *timeline_top_level(snac *snac, const xs_list *list); +void timeline_add_mark(snac *user); + xs_list *local_list(snac *snac, int max); xs_str *instance_index_fn(void); xs_list *timeline_instance_list(int skip, int show); -- cgit v1.2.3 From 492b91e4e47c43d15bb828ecbe1e4195c47c31f8 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 07:42:08 +0100 Subject: Backport from xs. --- xs.h | 21 ++++++++++++++++++--- xs_match.h | 7 ++++++- xs_version.h | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/xs.h b/xs.h index 05d84f5..961425b 100644 --- a/xs.h +++ b/xs.h @@ -12,6 +12,7 @@ #include #include #include +#include typedef enum { XSTYPE_STRING = 0x02, /* C string (\0 delimited) (NOT STORED) */ @@ -142,6 +143,7 @@ void xs_data_get(void *data, const xs_data *value); void *xs_memmem(const char *haystack, int h_size, const char *needle, int n_size); unsigned int xs_hash_func(const char *data, int size); +uint64_t xs_hash64_func(const char *data, int size); #ifdef XS_ASSERT #include @@ -632,7 +634,7 @@ xs_str *xs_crop_i(xs_str *str, int start, int end) end = sz + end; /* crop from the top */ - if (end > 0 && end < sz) + if (end >= 0 && end < sz) str[end] = '\0'; /* crop from the bottom */ @@ -1487,9 +1489,8 @@ unsigned int xs_hash_func(const char *data, int size) /* a general purpose hashing function */ { unsigned int hash = 0x666; - int n; - for (n = 0; n < size; n++) { + for (int n = 0; n < size; n++) { hash ^= (unsigned char)data[n]; hash *= 111111111; } @@ -1498,6 +1499,20 @@ unsigned int xs_hash_func(const char *data, int size) } +uint64_t xs_hash64_func(const char *data, int size) +/* a general purpose hashing function (64 bit) */ +{ + uint64_t hash = 0x100; + + for (int n = 0; n < size; n++) { + hash ^= (unsigned char)data[n]; + hash *= 1111111111111111111; + } + + return hash; +} + + #endif /* XS_IMPLEMENTATION */ #endif /* _XS_H */ diff --git a/xs_match.h b/xs_match.h index 0b89ac8..76c1bf3 100644 --- a/xs_match.h +++ b/xs_match.h @@ -24,6 +24,7 @@ int xs_match(const char *str, const char *spec) retry: for (;;) { + const char *q = spec; char c = *str++; char p = *spec++; @@ -63,8 +64,12 @@ retry: spec = b_spec; str = ++b_str; } - else + else { + if (*q == '|') + spec = q; + break; + } } } } diff --git a/xs_version.h b/xs_version.h index 12f713a..7314133 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* b865e89769aedfdbc61251e94451e9d37579f52e 2025-01-12T16:17:47+01:00 */ +/* 2f43b93e9d2b63360c802e09f4c68adfef74c673 2025-01-28T07:40:50+01:00 */ -- cgit v1.2.3 From d9d2dedbc053b35aee02ba7f408e056ab03368c0 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 07:48:31 +0100 Subject: Set the CSS class snac-no-more-unseen-posts. --- html.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/html.c b/html.c index 771d2af..c27ab99 100644 --- a/html.c +++ b/html.c @@ -2660,12 +2660,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs *s = xs_fmt("%s/admin#top", user->actor); xs_html_add(posts, - xs_html_text(L("No more unseen posts")), - xs_html_text(" - "), - xs_html_tag("a", - xs_html_attr("href", s), - xs_html_text(L("Back to top"))), - xs_html_sctag("hr", NULL)); + xs_html_tag("div", + xs_html_attr("class", "snac-no-more-unseen-posts"), + xs_html_text(L("No more unseen posts")), + xs_html_text(" - "), + xs_html_tag("a", + xs_html_attr("href", s), + xs_html_text(L("Back to top"))))); } mark_shown = 1; -- cgit v1.2.3 From bf07718f02081c88b48ea18c0bd38e49e971d398 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 08:04:57 +0100 Subject: Added some default styling to CSS class snac-no-more-unseen-posts. --- doc/style.css | 1 + utils.c | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/style.css b/doc/style.css index 13cef97..546cfb3 100644 --- a/doc/style.css +++ b/doc/style.css @@ -29,6 +29,7 @@ pre { overflow-x: scroll; } .snac-list-of-lists { padding-left: 0; } .snac-list-of-lists li { display: inline; border: 1px solid #a0a0a0; border-radius: 25px; margin-right: 0.5em; padding-left: 0.5em; padding-right: 0.5em; } +.snac-no-more-unseen-posts { border-top: 1px solid #000; border-bottom: 1px solid #000; padding: 0.5em 0; margin: 1em 0; } @media (prefers-color-scheme: dark) { body, input, textarea { background-color: #000; color: #fff; } a { color: #7799dd } diff --git a/utils.c b/utils.c index fd00e99..eb83884 100644 --- a/utils.c +++ b/utils.c @@ -72,6 +72,7 @@ static const char *default_css = ".snac-list-of-lists { padding-left: 0; }\n" ".snac-list-of-lists li { display: inline; border: 1px solid #a0a0a0; border-radius: 25px;\n" " margin-right: 0.5em; padding-left: 0.5em; padding-right: 0.5em; }\n" + ".snac-no-more-unseen-posts { border-top: 1px solid #000; border-bottom: 1px solid #000; padding: 0.5em 0; margin: 1em 0; }\n" "@media (prefers-color-scheme: dark) { \n" " body, input, textarea { background-color: #000; color: #fff; }\n" " a { color: #7799dd }\n" -- cgit v1.2.3 From ca9ff0538ef4e4bc6c23d1d30d83b0879c4d5351 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 08:07:43 +0100 Subject: Some CSS tweaks. --- doc/style.css | 2 +- utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/style.css b/doc/style.css index 546cfb3..87c5598 100644 --- a/doc/style.css +++ b/doc/style.css @@ -29,7 +29,7 @@ pre { overflow-x: scroll; } .snac-list-of-lists { padding-left: 0; } .snac-list-of-lists li { display: inline; border: 1px solid #a0a0a0; border-radius: 25px; margin-right: 0.5em; padding-left: 0.5em; padding-right: 0.5em; } -.snac-no-more-unseen-posts { border-top: 1px solid #000; border-bottom: 1px solid #000; padding: 0.5em 0; margin: 1em 0; } +.snac-no-more-unseen-posts { border-top: 1px solid #a0a0a0; border-bottom: 1px solid #a0a0a0; padding: 0.5em 0; margin: 1em 0; } @media (prefers-color-scheme: dark) { body, input, textarea { background-color: #000; color: #fff; } a { color: #7799dd } diff --git a/utils.c b/utils.c index eb83884..946897f 100644 --- a/utils.c +++ b/utils.c @@ -72,7 +72,7 @@ static const char *default_css = ".snac-list-of-lists { padding-left: 0; }\n" ".snac-list-of-lists li { display: inline; border: 1px solid #a0a0a0; border-radius: 25px;\n" " margin-right: 0.5em; padding-left: 0.5em; padding-right: 0.5em; }\n" - ".snac-no-more-unseen-posts { border-top: 1px solid #000; border-bottom: 1px solid #000; padding: 0.5em 0; margin: 1em 0; }\n" + ".snac-no-more-unseen-posts { border-top: 1px solid #a0a0a0; border-bottom: 1px solid #a0a0a0; padding: 0.5em 0; margin: 1em 0; }\n" "@media (prefers-color-scheme: dark) { \n" " body, input, textarea { background-color: #000; color: #fff; }\n" " a { color: #7799dd }\n" -- cgit v1.2.3 From 8156e4f03fd730facdee8c5910b8ec810dc5835c Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 08:18:11 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 23a2a17..b9f953d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,13 @@ # Release Notes +## UNRELEASED + +Notifications are now shown in a more compact way (i.e. all reactions are shown just above your post, instead of repeating the post *ad nauseam* for every reaction). + +New command-line option `unmute` to, well, no-longer-mute an actor. + +The private timeline now includes a mark between new posts and "already seen" ones. + ## 2.69 "Yin/Yang of Love" Added support for subscribing to LitePub (Pleroma-style) Fediverse Relays like e.g. https://fedi-relay.gyptazy.com to improve federation. See `snac(8)` (the Administrator Manual) for more information on how to use this feature. -- cgit v1.2.3 From 06ba1174ca7084a3d691aa8c2b552b708d04b8db Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 10:04:23 +0100 Subject: Some tweaks to the already seen mark code. --- data.c | 18 +++++++++++++++++- html.c | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/data.c b/data.c index 480a35b..7139cdf 100644 --- a/data.c +++ b/data.c @@ -1532,7 +1532,23 @@ void timeline_add_mark(snac *user) /* adds an "already seen" mark to the private timeline */ { xs *fn = xs_fmt("%s/private.idx", user->basedir); - index_add_md5(fn, MD5_ALREADY_SEEN_MARK); + char last_entry[MD5_HEX_SIZE] = ""; + FILE *f; + + /* get the last entry in the index */ + if ((f = fopen(fn, "r")) != NULL) { + index_desc_first(f, last_entry, 0); + fclose(f); + } + + /* is the last entry *not* a mark? */ + if (strcmp(last_entry, MD5_ALREADY_SEEN_MARK) != 0) { + /* add it */ + index_add_md5(fn, MD5_ALREADY_SEEN_MARK); + + /* mark as new */ + timeline_touch(user); + } } diff --git a/html.c b/html.c index c27ab99..8f8b524 100644 --- a/html.c +++ b/html.c @@ -2657,7 +2657,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, /* "already seen" mark? */ if (strcmp(v, MD5_ALREADY_SEEN_MARK) == 0) { if (skip == 0 && !mark_shown) { - xs *s = xs_fmt("%s/admin#top", user->actor); + xs *s = xs_fmt("%s/admin", user->actor); xs_html_add(posts, xs_html_tag("div", @@ -2665,7 +2665,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_text(L("No more unseen posts")), xs_html_text(" - "), xs_html_tag("a", - xs_html_attr("href", s), + xs_html_attr("href", s), xs_html_text(L("Back to top"))))); } -- cgit v1.2.3 From aec643838f304468af61ca2ad4e00a9e7125e53b Mon Sep 17 00:00:00 2001 From: default Date: Tue, 28 Jan 2025 20:36:37 +0100 Subject: Deleted useless code. --- data.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/data.c b/data.c index 7139cdf..50c6121 100644 --- a/data.c +++ b/data.c @@ -1545,9 +1545,6 @@ void timeline_add_mark(snac *user) if (strcmp(last_entry, MD5_ALREADY_SEEN_MARK) != 0) { /* add it */ index_add_md5(fn, MD5_ALREADY_SEEN_MARK); - - /* mark as new */ - timeline_touch(user); } } -- cgit v1.2.3 From 4acb3f65db21b1be6c16cf017aae890bb6dd4eea Mon Sep 17 00:00:00 2001 From: default Date: Wed, 29 Jan 2025 09:24:09 +0100 Subject: Call timeline_add_mark() after history_add() for better timestamp checks. --- html.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index 8f8b524..6365afb 100644 --- a/html.c +++ b/html.c @@ -3515,8 +3515,6 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *list = timeline_list(&snac, "private", skip, show, &more); - timeline_add_mark(&snac); - *body = html_timeline(&snac, list, 0, skip, show, more, NULL, "/admin", 1, error); @@ -3525,6 +3523,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (save) history_add(&snac, "timeline.html_", *body, *b_size, etag); + + timeline_add_mark(&snac); } } } -- cgit v1.2.3 From ebb3921772834bb91cf9ccacf9d34bd050efbe5e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 29 Jan 2025 09:44:34 +0100 Subject: Updated README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index de11b98..8d5a59d 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ This will: - [How to install snac on OpenBSD without relayd (by @antics@mastodon.nu)](https://chai.guru/pub/openbsd/snac.html). - [Setting up Snac in OpenBSD (by Yonle)](https://wiki.ircnow.org/index.php?n=Openbsd.Snac). - [How to run your own social network with snac (by Giacomo Tesio)](https://encrypted.tesio.it/2024/12/18/how-to-run-your-own-social-network.html). Includes information on how to run snac as a CGI. +- [Improving snac Performance with Nginx Proxy Cache (by Stefano Marinelli)](https://it-notes.dragas.net/2025/01/29/improving-snac-performance-with-nginx-proxy-cache/). ## Incredibly awesome CSS themes for snac -- cgit v1.2.3 From 3799a6aa60df749624a5ad09aa5c60754987a49f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 30 Jan 2025 16:13:22 +0100 Subject: Updated TODO. --- TODO.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index 86c706a..045b4cc 100644 --- a/TODO.md +++ b/TODO.md @@ -14,14 +14,10 @@ Important: deleting a follower should do more that just delete the object, see h ## Wishlist -Add support for subscribing and posting to relays (see https://codeberg.org/grunfink/snac2/issues/216 for more information). - The instance timeline should also show boosts from users. Mastoapi: implement /v1/conversations. -Implement following of hashtags (this is not trivial). - Track 'Event' data types standardization; how to add plan-to-attend and similar activities (more info: https://event-federation.eu/). Friendica interacts with events via activities `Accept` (will go), `TentativeAccept` (will try to go) or `Reject` (cannot go) (`object` field as id, not object). `Undo` for any of these activities cancel (`object` as an object, not id). Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md @@ -365,3 +361,7 @@ CSV import/export does not work with OpenBSD security on; document it or fix it Add support for /share?text=tt&website=url (whatever it is, see https://mastodonshare.com/ for details) (2025-01-06T18:43:52+0100). Add support for /authorize_interaction (whatever it is) (2025-01-16T14:45:28+0100). + +Implement following of hashtags (this is not trivial) (2025-01-30T16:12:16+0100). + +Add support for subscribing and posting to relays (see https://codeberg.org/grunfink/snac2/issues/216 for more information) (2025-01-30T16:12:34+0100). -- cgit v1.2.3 From e5d1fe433cdeaf4d65e13d660a7a7db696c763a9 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 30 Jan 2025 16:15:22 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b9f953d..b895801 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,7 +6,9 @@ Notifications are now shown in a more compact way (i.e. all reactions are shown New command-line option `unmute` to, well, no-longer-mute an actor. -The private timeline now includes a mark between new posts and "already seen" ones. +The private timeline now includes an approximate mark between new posts and "already seen" ones. + +Fixed a spurious 404 error in the instance root URL for some configurations. ## 2.69 "Yin/Yang of Love" -- cgit v1.2.3 From 317106fc69f8aeca023788f0e0f6768387a2cd2a Mon Sep 17 00:00:00 2001 From: default Date: Thu, 30 Jan 2025 16:32:37 +0100 Subject: Updated documentation. --- doc/snac.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snac.5 b/doc/snac.5 index 3550995..d873908 100644 --- a/doc/snac.5 +++ b/doc/snac.5 @@ -78,7 +78,7 @@ converted to related emojis: .Ss Accepted HTML All HTML tags in entries are neutered except the following ones: .Bd -literal -a p br blockquote ul ol li cite small +a p br blockquote ul ol li cite small h2 h3 span i b u s pre code em strong hr img del .Ed .Pp -- cgit v1.2.3 From d0c81d35cefd5ce9ba6b2c6fd0bf3091011959b5 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 31 Jan 2025 09:10:16 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b895801..337a29a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # Release Notes -## UNRELEASED +## 2.70 Notifications are now shown in a more compact way (i.e. all reactions are shown just above your post, instead of repeating the post *ad nauseam* for every reaction). -- cgit v1.2.3 From ed6c6328a14ae486ba87a7bad273a4bd3a4056c4 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 31 Jan 2025 09:11:34 +0100 Subject: Version 2.70 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 6c3ccda..0cd6113 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.70-dev" +#define VERSION "2.70" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From 35ce3d1ecb26f7fb76858f015312b69cb9817bfb Mon Sep 17 00:00:00 2001 From: default Date: Sat, 1 Feb 2025 08:21:05 +0100 Subject: Updated documentation. --- doc/snac.1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/snac.1 b/doc/snac.1 index d33bccb..8cc032d 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -234,6 +234,8 @@ Purges old data from the timeline of all users. .It Cm adduser Ar basedir Op uid Adds a new user to the server. This is an interactive command; necessary information will be prompted for. +.It Cm deluser Ar basedir Ar uid +Deletes a user, unfollowing all accounts first. .It Cm resetpwd Ar basedir Ar uid Resets a user's password to a new, random one. .It Cm queue Ar basedir Ar uid -- cgit v1.2.3 From a3d4575d91680580c69855c6732c6fe8a4db5fef Mon Sep 17 00:00:00 2001 From: default Date: Sun, 2 Feb 2025 09:54:33 +0100 Subject: Updated documentation. --- doc/snac.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/snac.1 b/doc/snac.1 index 8cc032d..327e071 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -259,6 +259,9 @@ The rest of command line arguments are treated as media files to be attached to the post. .It Cm note_unlisted Ar basedir Ar uid Ar text Op file file ... Like the previous one, but creates an "unlisted" (or "quiet public") post. +.It Cm note_mention Ar basedir Ar uid Ar text Op file file ... +Like the previous one, but creates a post only for accounts mentioned +in the post body. .It Cm block Ar basedir Ar instance_url Blocks a full instance, given its URL or domain name. All subsequent incoming activities with identifiers from that instance will be immediately -- cgit v1.2.3 From 11d1adca7a2f3e398722f3160ac6a8061f18c31d Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Tue, 4 Feb 2025 13:09:49 +0200 Subject: Fix memory leaks Fix a memory leak on each login; the allocated OpenSSL BIOs were only freed partially. Also add a missing deallocation during server socket creation. --- xs_openssl.h | 2 +- xs_socket.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/xs_openssl.h b/xs_openssl.h index 9388691..f215bcc 100644 --- a/xs_openssl.h +++ b/xs_openssl.h @@ -83,7 +83,7 @@ xs_val *xs_base64_dec(const xs_str *data, int *size) s = xs_realloc(s, _xs_blk_size(*size + 1)); s[*size] = '\0'; - BIO_free_all(mem); + BIO_free_all(b64); return s; } diff --git a/xs_socket.h b/xs_socket.h index 6e618ba..7bf5298 100644 --- a/xs_socket.h +++ b/xs_socket.h @@ -85,6 +85,8 @@ int xs_socket_server(const char *addr, const char *serv) listen(rs, SOMAXCONN); } + freeaddrinfo(res); + #else /* WITHOUT_GETADDRINFO */ struct sockaddr_in host; -- cgit v1.2.3 From 51dc5aad0fabaa35df27bc5b112d21bc470cc212 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 13:37:47 +0100 Subject: Fixed crash. --- xs_url.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs_url.h b/xs_url.h index 3c24736..81dd668 100644 --- a/xs_url.h +++ b/xs_url.h @@ -69,7 +69,7 @@ xs_dict *xs_url_vars(const char *str) vars = xs_dict_new(); - if (str != NULL) { + if (xs_is_string(str)) { /* split by arguments */ xs *args = xs_split(str, "&"); -- cgit v1.2.3 From e1e4c0ca1ade524eb6d2585215c0b028ffaf5a8b Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 13:56:08 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 337a29a..0623f23 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,11 @@ # Release Notes +## UNRELEASED + +Fixed memory leak (contributed by inz). + +Fixed crash. + ## 2.70 Notifications are now shown in a more compact way (i.e. all reactions are shown just above your post, instead of repeating the post *ad nauseam* for every reaction). -- cgit v1.2.3 From f942acf3e987989b4898ee43b00685092127399e Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 13:57:19 +0100 Subject: Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 0cd6113..b4852f3 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.70" +#define VERSION "2.71-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From 6f2afe1ecb01ff3ff8d6f9e297ed02e0d42f8b93 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 15:07:17 +0100 Subject: Version 2.71 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index b4852f3..4618b87 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.71-dev" +#define VERSION "2.71" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From d80985746719d54ef0a63bc51c05adedb537b394 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 15:09:13 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0623f23..f39a912 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # Release Notes -## UNRELEASED +## 2.71 Fixed memory leak (contributed by inz). -- cgit v1.2.3 From a33cdf29070a7bd0e46fc1484c98508ca959c1fa Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 16:10:52 +0100 Subject: Another crash fix. --- xs_url.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs_url.h b/xs_url.h index 81dd668..fd320a4 100644 --- a/xs_url.h +++ b/xs_url.h @@ -23,6 +23,9 @@ xs_str *xs_url_dec(const char *str) if (sscanf(str + 1, "%02x", &i) == 1) { unsigned char uc = i; + if (!xs_is_string((char *)&uc)) + break; + s = xs_append_m(s, (char *)&uc, 1); str += 2; } -- cgit v1.2.3 From 636d026e133d874daf39a84a7702253454644926 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 16:24:21 +0100 Subject: More xs_url_dec() fixes. --- xs_url.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs_url.h b/xs_url.h index fd320a4..8e2e243 100644 --- a/xs_url.h +++ b/xs_url.h @@ -17,6 +17,9 @@ xs_str *xs_url_dec(const char *str) xs_str *s = xs_str_new(NULL); while (*str) { + if (!xs_is_string(str)) + break; + if (*str == '%') { unsigned int i; -- cgit v1.2.3 From be9104783e75548195ec9f9661d4a3365d300f85 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 16:56:12 +0100 Subject: Don't append non-strings in xs_readline(). --- xs_io.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs_io.h b/xs_io.h index 110b0eb..8fc5d6f 100644 --- a/xs_io.h +++ b/xs_io.h @@ -27,7 +27,8 @@ xs_str *xs_readline(FILE *f) while ((c = fgetc(f)) != EOF) { unsigned char rc = c; - s = xs_append_m(s, (char *)&rc, 1); + if (xs_is_string((char *)&rc)) + s = xs_append_m(s, (char *)&rc, 1); if (c == '\n') break; -- cgit v1.2.3 From d1d9e1397e6d7ee182316bbbb1dd0b1b9788b36f Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 17:11:43 +0100 Subject: Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 4618b87..7b5c54c 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.71" +#define VERSION "2.72-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From a2dde5dedaddb7fe91c061997e1314ba569c2edf Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 17:26:47 +0100 Subject: Only split real strings in xs_split_n(). --- xs.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/xs.h b/xs.h index 961425b..b53885e 100644 --- a/xs.h +++ b/xs.h @@ -991,16 +991,20 @@ xs_str *xs_join(const xs_list *list, const char *sep) xs_list *xs_split_n(const char *str, const char *sep, int times) /* splits a string into a list upto n times */ { + xs_list *list = xs_list_new(); + + if (!xs_is_string(str) || !xs_is_string(sep)) + return list; + int sz = strlen(sep); char *ss; - xs_list *list; - - list = xs_list_new(); while (times > 0 && (ss = strstr(str, sep)) != NULL) { /* create a new string with this slice and add it to the list */ xs *s = xs_str_new_sz(str, ss - str); - list = xs_list_append(list, s); + + if (xs_is_string(s)) + list = xs_list_append(list, s); /* skip past the separator */ str = ss + sz; @@ -1009,7 +1013,8 @@ xs_list *xs_split_n(const char *str, const char *sep, int times) } /* add the rest of the string */ - list = xs_list_append(list, str); + if (xs_is_string(str)) + list = xs_list_append(list, str); return list; } -- cgit v1.2.3 From 2c10e230a290113c665a31a013812d9bf721f215 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 4 Feb 2025 18:32:55 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f39a912..d196dac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,9 @@ # Release Notes +## UNRELEASED + +Fixed more crashes (thank you very much to inz for helping me debugging this). + ## 2.71 Fixed memory leak (contributed by inz). -- cgit v1.2.3 From 879fcd830d172b89f8e8f8300af469ae5727fb7d Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 09:04:47 +0100 Subject: Updated TODO. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 045b4cc..bdb860c 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,8 @@ Important: deleting a follower should do more that just delete the object, see h ## Wishlist +Each notification should show a link to the full thread, to see it in context. + The instance timeline should also show boosts from users. Mastoapi: implement /v1/conversations. -- cgit v1.2.3 From 4df4f0c5518cfe8ae90d098612e9d131f4a384f5 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 09:47:38 +0100 Subject: Fixed bug in search. --- data.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 50c6121..2f1a52b 100644 --- a/data.c +++ b/data.c @@ -2766,7 +2766,17 @@ xs_list *content_search(snac *user, const char *regex, for (int n = 0; n < 3; n++) { if (md5s[n] != NULL) { xs *fn = _object_fn_by_md5(md5s[n], "content_search"); - double mt = mtime(fn); + double mt; + + while ((mt = mtime(fn)) == 0 && md5s[n] != NULL) { + /* object is not here: move to the next one */ + if (xs_list_next(tls[n], &md5s[n], &c[n])) { + xs_free(fn); + fn = _object_fn_by_md5(md5s[n], "content_search_2"); + } + else + md5s[n] = NULL; + } if (mt > mtime) { newest = n; -- cgit v1.2.3 From 8f76c4d8f6ac9d4fa81ab9fd13d8f5e8e65ea8b8 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 09:48:04 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d196dac..719c89a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,8 @@ ## UNRELEASED +Fixed a search bug. + Fixed more crashes (thank you very much to inz for helping me debugging this). ## 2.71 -- cgit v1.2.3 From 52c45718208f076ba1e4a30d7d54655cb4dcc78c Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Fri, 31 Jan 2025 13:58:10 +0200 Subject: Fix link detection. Fixes: #290 --- format.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index e5934b8..4c8ed29 100644 --- a/format.c +++ b/format.c @@ -78,6 +78,8 @@ xs_dict *emojis(void) return d; } +/* Non-whitespace without trailing comma, period or closing paren */ +#define NOSPACE "([^[:space:],.)]+|[,.)]+[^[:space:],.)])+" static xs_str *format_line(const char *line, xs_list **attach) /* formats a line */ @@ -96,8 +98,8 @@ static xs_str *format_line(const char *line, xs_list **attach) "__[^_]+__" "|" //anzu "!\\[[^]]+\\]\\([^\\)]+\\)" "|" "\\[[^]]+\\]\\([^\\)]+\\)" "|" - "[a-z]+:/" "/[^[:space:]]+" "|" - "(mailto|xmpp):[^@[:space:]]+@[^[:space:]]+" + "[a-z]+:/" "/" NOSPACE "|" + "(mailto|xmpp):[^@[:space:]]+@" NOSPACE ")"); int n = 0; -- cgit v1.2.3 From a356ca255acf2119f10acbe577c3f8008ad8e160 Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Sat, 1 Feb 2025 14:31:18 +0200 Subject: Allow multiple editors at the same time Instead of a hard-coded message file, use mkstemp() to have an unique temp file for each invocation. --- main.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/main.c b/main.c index 347c495..58a46de 100644 --- a/main.c +++ b/main.c @@ -676,19 +676,25 @@ int main(int argc, char *argv[]) if (strcmp(url, "-e") == 0) { /* get the content from an editor */ +#define EDITOR "$EDITOR " + char cmd[] = EDITOR "/tmp/snac-XXXXXX"; FILE *f; - - unlink("/tmp/snac-edit.txt"); - system("$EDITOR /tmp/snac-edit.txt"); - - if ((f = fopen("/tmp/snac-edit.txt", "r")) != NULL) { - content = xs_readall(f); - fclose(f); - - unlink("/tmp/snac-edit.txt"); - } - else { - printf("Nothing to send\n"); + int fd = mkstemp(cmd + strlen(EDITOR)); + + if (fd >= 0) { + int status = system(cmd); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0 && (f = fdopen(fd, "r")) != NULL) { + content = xs_readall(f); + fclose(f); + unlink(cmd + strlen(EDITOR)); + } else { + printf("Nothing to send\n"); + close(fd); + return 1; + } + } else { + fprintf(stderr, "Temp file creation failed\n"); return 1; } } @@ -700,6 +706,11 @@ int main(int argc, char *argv[]) else content = xs_dup(url); + if (!content || !*content) { + printf("Nothing to send\n"); + return 1; + } + int scope = 0; if (strcmp(cmd, "note_mention") == 0) scope = 1; -- cgit v1.2.3 From 1d37e33e405975ce740be89fc02968cc7ec09b0f Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 10:14:42 +0100 Subject: Added a 'Context' link to each notification. --- html.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 6365afb..500272b 100644 --- a/html.c +++ b/html.c @@ -3129,11 +3129,16 @@ xs_str *html_notifications(snac *user, int skip, int show) else if (obj != NULL) { 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); if (h != NULL) { xs_html_add(entry, + xs_html_tag("p", + xs_html_tag("a", + xs_html_attr("href", ctxt), + xs_html_text(L("Context")))), h); } } @@ -3540,7 +3545,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, const char *md5 = xs_list_get(l, -1); if (md5 && *md5 && timeline_here(&snac, md5)) { - xs *list = xs_list_append(xs_list_new(), md5); + 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); *b_size = strlen(*body); -- cgit v1.2.3 From 4842abbfc679495efb5984bfc712ce9d812ed1b5 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 10:25:45 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 719c89a..7a23b8b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,8 +4,14 @@ Fixed a search bug. +Each notification includes a link labelled `Context`, that leads to a page with the full conversation tree the post is a part of. + Fixed more crashes (thank you very much to inz for helping me debugging this). +Fixed link detection in posts (contributed by inz). + +Allow multiple editors for command-line posts (contributed by inz). + ## 2.71 Fixed memory leak (contributed by inz). -- cgit v1.2.3 From 0da936901283db8c5de5b6470ffd911ac8b300fe Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 10:51:11 +0100 Subject: Fixed crash in xs_fcgi_request(). --- xs_fcgi.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs_fcgi.h b/xs_fcgi.h index 870e3e8..0b53dac 100644 --- a/xs_fcgi.h +++ b/xs_fcgi.h @@ -173,6 +173,9 @@ xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id) xs *v = xs_str_new_sz((char *)&buf[offset], vsz); offset += vsz; + if (!xs_is_string(k) || !xs_is_string(v)) + continue; + cgi_vars = xs_dict_append(cgi_vars, k, v); if (strcmp(k, "REQUEST_METHOD") == 0) -- cgit v1.2.3 From 3318f1596bbb2853a8305194204084b32790fbd7 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 11:00:30 +0100 Subject: Fixed compilation failure in FreeBSD. --- main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main.c b/main.c index 58a46de..1cd6580 100644 --- a/main.c +++ b/main.c @@ -11,6 +11,7 @@ #include "snac.h" #include +#include int usage(void) { -- cgit v1.2.3 From bec483653384e22c989f6a08921a5617284e5e7e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 11:04:28 +0100 Subject: Fixed JSON string parsing. --- xs_json.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xs_json.h b/xs_json.h index 660c277..d1b18e4 100644 --- a/xs_json.h +++ b/xs_json.h @@ -280,6 +280,12 @@ static xs_val *_xs_json_load_lexer(FILE *f, js_type *t) else { char cc = c; v = xs_insert_m(v, offset, &cc, 1); + + if (!xs_is_string(v)) { + *t = JS_ERROR; + break; + } + offset++; } } -- cgit v1.2.3 From a5aea195c2676a5c17a66f6c98f5654ce79a3794 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 12:58:10 +0100 Subject: Minor HTML tweak. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 5f1cd6c..b3a18d9 100644 --- a/html.c +++ b/html.c @@ -3145,7 +3145,7 @@ xs_str *html_notifications(snac *user, int skip, int show) if (h != NULL) { xs_html_add(entry, - xs_html_tag("p", + xs_html_tag("div", xs_html_tag("a", xs_html_attr("href", ctxt), xs_html_text(L("Context")))), -- cgit v1.2.3 From de8fd8305396abd1e7e7208f443be4a4891219cd Mon Sep 17 00:00:00 2001 From: default Date: Wed, 5 Feb 2025 13:11:14 +0100 Subject: More HTML tweaks. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index b3a18d9..5f1cd6c 100644 --- a/html.c +++ b/html.c @@ -3145,7 +3145,7 @@ xs_str *html_notifications(snac *user, int skip, int show) if (h != NULL) { xs_html_add(entry, - xs_html_tag("div", + xs_html_tag("p", xs_html_tag("a", xs_html_attr("href", ctxt), xs_html_text(L("Context")))), -- cgit v1.2.3 From 0155ccfa5280b8e95aca8f085bd3114213d506a6 Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Thu, 6 Feb 2025 00:47:23 +0200 Subject: Rewrite query string decoding --- xs_url.h | 103 ++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/xs_url.h b/xs_url.h index 8e2e243..bb31779 100644 --- a/xs_url.h +++ b/xs_url.h @@ -11,6 +11,39 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea #ifdef XS_IMPLEMENTATION +char *xs_url_dec_in(char *str, int qs) +{ + char *w = str; + char *r; + + for (r = str; *r != '\0'; r++) { + switch (*r) { + case '%': { + unsigned hex; + if (!r[1] || !r[2]) + return NULL; + if (sscanf(r + 1, "%2x", &hex) != 1) + return NULL; + *w++ = hex; + r += 2; + break; + } + + case '+': + if (qs) { + *w++ = ' '; + break; + } + /* fall-through */ + default: + *w++ = *r; + } + } + + *w++ = '\0'; + return str; +} + xs_str *xs_url_dec(const char *str) /* decodes an URL */ { @@ -76,42 +109,44 @@ xs_dict *xs_url_vars(const char *str) vars = xs_dict_new(); if (xs_is_string(str)) { - /* split by arguments */ - xs *args = xs_split(str, "&"); - - const xs_val *v; - - xs_list_foreach(args, v) { - xs *dv = xs_url_dec(v); - xs *kv = xs_split_n(dv, "=", 1); - - if (xs_list_len(kv) == 2) { - const char *key = xs_list_get(kv, 0); - const char *pv = xs_dict_get(vars, key); - - if (!xs_is_null(pv)) { - /* there is a previous value: convert to a list and append */ - xs *vlist = NULL; - if (xs_type(pv) == XSTYPE_LIST) - vlist = xs_dup(pv); - else { - vlist = xs_list_new(); - vlist = xs_list_append(vlist, pv); - } - - vlist = xs_list_append(vlist, xs_list_get(kv, 1)); - vars = xs_dict_set(vars, key, vlist); - } + xs *dup = xs_dup(str); + char *k; + char *saveptr; + for (k = strtok_r(dup, "&", &saveptr); + k; + k = strtok_r(NULL, "&", &saveptr)) { + char *v = strchr(k, '='); + if (!v) + continue; + *v++ = '\0'; + k = xs_url_dec_in(k, 1); + v = xs_url_dec_in(v, 1); + if (!xs_is_string(k) || !xs_is_string(v)) + continue; + + const char *pv = xs_dict_get(vars, k); + if (!xs_is_null(pv)) { + /* there is a previous value: convert to a list and append */ + xs *vlist = NULL; + if (xs_type(pv) == XSTYPE_LIST) + vlist = xs_dup(pv); else { - /* ends with []? force to always be a list */ - if (xs_endswith(key, "[]")) { - xs *vlist = xs_list_new(); - vlist = xs_list_append(vlist, xs_list_get(kv, 1)); - vars = xs_dict_append(vars, key, vlist); - } - else - vars = xs_dict_append(vars, key, xs_list_get(kv, 1)); + vlist = xs_list_new(); + vlist = xs_list_append(vlist, pv); + } + + vlist = xs_list_append(vlist, v); + vars = xs_dict_set(vars, k, vlist); + } + else { + /* ends with []? force to always be a list */ + if (xs_endswith(k, "[]")) { + xs *vlist = xs_list_new(); + vlist = xs_list_append(vlist, v); + vars = xs_dict_append(vars, k, vlist); } + else + vars = xs_dict_append(vars, k, v); } } } -- cgit v1.2.3 From 1089b24171934bc742c2e438f17ec1774b90c728 Mon Sep 17 00:00:00 2001 From: Santtu Lakkala Date: Thu, 6 Feb 2025 00:47:49 +0200 Subject: Rewrite http header parsing --- xs_httpd.h | 54 ++++++++++++++++++++++++++++++++++-------------------- xs_io.h | 4 +--- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/xs_httpd.h b/xs_httpd.h index cf0d811..8aec996 100644 --- a/xs_httpd.h +++ b/xs_httpd.h @@ -15,41 +15,48 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size) { xs *q_vars = NULL; xs *p_vars = NULL; - xs *l1, *l2; + xs *l1; const char *v; + char *saveptr; xs_socket_timeout(fileno(f), 2.0, 0.0); /* read the first line and split it */ l1 = xs_strip_i(xs_readline(f)); - l2 = xs_split(l1, " "); + char *raw_path; + const char *mtd; + const char *proto; + + if (!(mtd = strtok_r(l1, " ", &saveptr)) || + !(raw_path = strtok_r(NULL, " ", &saveptr)) || + !(proto = strtok_r(NULL, " ", &saveptr)) || + strtok_r(NULL, " ", &saveptr)) + return NULL; - if (xs_list_len(l2) != 3) { - /* error or timeout */ + if (!xs_is_string(mtd) || !xs_is_string(raw_path) || !xs_is_string(proto)) return NULL; - } xs_dict *req = xs_dict_new(); - req = xs_dict_append(req, "method", xs_list_get(l2, 0)); - req = xs_dict_append(req, "raw_path", xs_list_get(l2, 1)); - req = xs_dict_append(req, "proto", xs_list_get(l2, 2)); + req = xs_dict_append(req, "method", mtd); + req = xs_dict_append(req, "raw_path", raw_path); + req = xs_dict_append(req, "proto", proto); { - /* split the path with its optional variables */ - const xs_val *udp = xs_list_get(l2, 1); - xs *pnv = xs_split_n(udp, "?", 1); - - /* store the path */ - req = xs_dict_append(req, "path", xs_list_get(pnv, 0)); + char *q = strchr(raw_path, '?'); /* get the variables */ - q_vars = xs_url_vars(xs_list_get(pnv, 1)); + if (q) { + *q++ = '\0'; + q_vars = xs_url_vars(q); + } + /* store the path */ + req = xs_dict_append(req, "path", raw_path); } /* read the headers */ for (;;) { - xs *l, *p = NULL; + xs *l; l = xs_strip_i(xs_readline(f)); @@ -58,11 +65,18 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size) break; /* split header and content */ - p = xs_split_n(l, ": ", 1); + char *cnt = strchr(l, ':'); + if (!cnt) + continue; + + *cnt++ = '\0'; + cnt += strspn(cnt, " \r\n\t\v\f"); + xs_rstrip_chars_i(l, " \r\n\t\v\f"); + + if (!xs_is_string(cnt)) + continue; - if (xs_list_len(p) == 2) - req = xs_dict_append(req, xs_tolower_i( - (xs_str *)xs_list_get(p, 0)), xs_list_get(p, 1)); + req = xs_dict_append(req, xs_tolower_i(l), cnt); } xs_socket_timeout(fileno(f), 5.0, 0.0); diff --git a/xs_io.h b/xs_io.h index 8fc5d6f..9c5018e 100644 --- a/xs_io.h +++ b/xs_io.h @@ -14,7 +14,7 @@ xs_val *xs_readall(FILE *f); xs_str *xs_readline(FILE *f) /* reads a line from a file */ { - xs_str *s = NULL; + xs_str *s = xs_str_new(NULL); errno = 0; @@ -22,8 +22,6 @@ xs_str *xs_readline(FILE *f) if (!feof(f)) { int c; - s = xs_str_new(NULL); - while ((c = fgetc(f)) != EOF) { unsigned char rc = c; -- cgit v1.2.3 From 2b28fe814271eaa0b4a29457b364abd446d728c2 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 08:53:52 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7a23b8b..78f5694 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,12 +6,14 @@ Fixed a search bug. Each notification includes a link labelled `Context`, that leads to a page with the full conversation tree the post is a part of. -Fixed more crashes (thank you very much to inz for helping me debugging this). +Fixed more crashes (contributed by inz). Fixed link detection in posts (contributed by inz). Allow multiple editors for command-line posts (contributed by inz). +Special thanks to fellow developer inz for bringing my attention to code places where I should have been more careful. + ## 2.71 Fixed memory leak (contributed by inz). -- cgit v1.2.3 From 8e1a2c5ff92d01789e02980f2e36c8808c8ea033 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Thu, 6 Feb 2025 09:04:36 +0100 Subject: Merged 53ad5cdf0371fc068fa03bf287fc94be. --- RELEASE_NOTES.md | 2 ++ doc/snac.8 | 2 ++ html.c | 2 +- httpd.c | 2 +- utils.c | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 78f5694..8e95766 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,8 @@ Fixed link detection in posts (contributed by inz). Allow multiple editors for command-line posts (contributed by inz). +Separated maximum and default timeline entry count, allowing larger timelines to be requested without having to increase the default (contributed by lxo). + Special thanks to fellow developer inz for bringing my attention to code places where I should have been more careful. ## 2.71 diff --git a/doc/snac.8 b/doc/snac.8 index e9b33dd..aac9f70 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -154,6 +154,8 @@ to those servers that went timeout in the previous retry. If you want to give slow servers a chance to receive your messages, you can increase this value (but also take into account that processing the queue will take longer while waiting for these molasses to respond). +.It Ic def_timeline_entries +This is the default timeline entries shown in the web interface. .It Ic max_timeline_entries This is the maximum timeline entries shown in the web interface. .It Ic timeline_purge_days diff --git a/html.c b/html.c index 5f1cd6c..82d2e55 100644 --- a/html.c +++ b/html.c @@ -3306,7 +3306,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, cache = 0; int skip = 0; - int def_show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); + int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", "50")); int show = def_show; if ((v = xs_dict_get(q_vars, "skip")) != NULL) diff --git a/httpd.c b/httpd.c index e0a36b6..b856b7d 100644 --- a/httpd.c +++ b/httpd.c @@ -219,7 +219,7 @@ int server_get_handler(xs_dict *req, const char *q_path, if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) { /** search by tag **/ int skip = 0; - int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); + int show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", "50")); const char *v; if ((v = xs_dict_get(q_vars, "skip")) != NULL) diff --git a/utils.c b/utils.c index ae1c1b6..b799813 100644 --- a/utils.c +++ b/utils.c @@ -28,6 +28,7 @@ static const char *default_srv_config = "{" "\"queue_timeout\": 6," "\"queue_timeout_2\": 8," "\"cssurls\": [\"\"]," + "\"def_timeline_entries\": 50," "\"max_timeline_entries\": 50," "\"timeline_purge_days\": 120," "\"local_purge_days\": 0," -- cgit v1.2.3 From f9796044c6c1fda786bd608987c1f41d5244e528 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Thu, 6 Feb 2025 09:24:27 +0100 Subject: Merged 383bf2e2eeadcacfa519be7a56c9a301. --- RELEASE_NOTES.md | 2 ++ html.c | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8e95766..c6d439d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,6 +14,8 @@ Allow multiple editors for command-line posts (contributed by inz). Separated maximum and default timeline entry count, allowing larger timelines to be requested without having to increase the default (contributed by lxo). +Turned message date into a link to the local post, so that it can be loaded into a separate tab for interacting with (contributed by lxo). + Special thanks to fellow developer inz for bringing my attention to code places where I should have been more careful. ## 2.71 diff --git a/html.c b/html.c index 82d2e55..8ad79de 100644 --- a/html.c +++ b/html.c @@ -13,6 +13,7 @@ #include "xs_html.h" #include "xs_curl.h" #include "xs_unicode.h" +#include "xs_url.h" #include "snac.h" @@ -115,7 +116,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 *lang) + int in_people, const char *proxy, const char *lang, + const char *md5) { xs_html *actor_icon = xs_html_tag("p", NULL); @@ -224,12 +226,29 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, if (xs_is_string(lang)) date_title = xs_str_cat(date_title, " (", lang, ")"); + xs_html *date_text = xs_html_text(date_label); + + if (user && md5) { + xs *lpost_url = xs_fmt("%s/admin/p/%s", + user->actor, md5); + date_text = xs_html_tag("a", + xs_html_attr("href", lpost_url), + date_text); + } + else if (user && url) { + xs *lpost_url = xs_fmt("%s/admin?q=%s", + user->actor, xs_url_enc(url)); + date_text = xs_html_tag("a", + xs_html_attr("href", lpost_url), + date_text); + } + xs_html_add(actor_icon, xs_html_text(" "), xs_html_tag("time", xs_html_attr("class", "dt-published snac-pubdate"), xs_html_attr("title", date_title), - xs_html_text(date_label))); + date_text)); } { @@ -261,7 +280,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, } -xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy) +xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy, const char *md5) { xs *actor = NULL; xs_html *actor_icon = NULL; @@ -292,7 +311,7 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con else lang = NULL; - actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang); + actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang, md5); } return actor_icon; @@ -1706,7 +1725,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_tag("div", xs_html_attr("class", "snac-origin"), xs_html_text(L("follows you"))), - html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy))); + html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy, NULL))); } else if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { @@ -1887,7 +1906,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } xs_html_add(post_header, - html_msg_icon(read_only ? NULL : user, actor, msg, proxy)); + html_msg_icon(read_only ? NULL : user, actor, msg, proxy, md5)); /** post content **/ @@ -2820,7 +2839,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, NULL, 0, 1, proxy, NULL, NULL))); /* content (user bio) */ const char *c = xs_dict_get(actor, "summary"); @@ -3118,7 +3137,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, NULL))); + html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); } else if (strcmp(type, "Move") == 0) { @@ -3132,7 +3151,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, NULL))); + html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); } } } -- cgit v1.2.3 From 4ed3a01474dbb99597ce219ad5418df7557f6459 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 09:25:53 +0100 Subject: Added an anchor to the post link in the date. --- html.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index 8ad79de..13615b9 100644 --- a/html.c +++ b/html.c @@ -229,10 +229,11 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, xs_html *date_text = xs_html_text(date_label); if (user && md5) { - xs *lpost_url = xs_fmt("%s/admin/p/%s", - user->actor, md5); + xs *lpost_url = xs_fmt("%s/admin/p/%s#%s_entry", + user->actor, md5, md5); date_text = xs_html_tag("a", xs_html_attr("href", lpost_url), + xs_html_attr("class", "snac-pubdate"), date_text); } else if (user && url) { @@ -240,6 +241,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, user->actor, xs_url_enc(url)); date_text = xs_html_tag("a", xs_html_attr("href", lpost_url), + xs_html_attr("class", "snac-pubdate"), date_text); } -- cgit v1.2.3 From 2bfbe6215f849c1b8af3df88d33e34dcc572844c Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 10:48:15 +0100 Subject: Fixed bug in xs_httpd_request(). --- xs_httpd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs_httpd.h b/xs_httpd.h index 8aec996..4cc8263 100644 --- a/xs_httpd.h +++ b/xs_httpd.h @@ -71,7 +71,7 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size) *cnt++ = '\0'; cnt += strspn(cnt, " \r\n\t\v\f"); - xs_rstrip_chars_i(l, " \r\n\t\v\f"); + l = xs_rstrip_chars_i(l, " \r\n\t\v\f"); if (!xs_is_string(cnt)) continue; -- cgit v1.2.3 From 22a23dbdd1fca4695c012c31d5e5502f38212ed8 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 10:56:21 +0100 Subject: Minor tweak to inbox_list(). --- data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data.c b/data.c index 2f1a52b..9fc54a0 100644 --- a/data.c +++ b/data.c @@ -2601,7 +2601,7 @@ xs_list *inbox_list(void) if ((f = fopen(v, "r")) != NULL) { xs *line = xs_readline(f); - if (line) { + if (line && *line) { line = xs_strip_i(line); ibl = xs_list_append(ibl, line); } -- cgit v1.2.3 From f4fee170ce4d02182081f084975c3cd7d9ba4e93 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 19:16:49 +0100 Subject: If 'def_timeline_entries' is not set, 'max_timeline_entries' is used (instead of the default). --- html.c | 3 ++- httpd.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index 13615b9..c12aea4 100644 --- a/html.c +++ b/html.c @@ -3327,7 +3327,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, cache = 0; int skip = 0; - int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", "50")); + int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", + xs_dict_get_def(srv_config, "max_timeline_entries", "50"))); int show = def_show; if ((v = xs_dict_get(q_vars, "skip")) != NULL) diff --git a/httpd.c b/httpd.c index b856b7d..5a38aff 100644 --- a/httpd.c +++ b/httpd.c @@ -219,7 +219,8 @@ int server_get_handler(xs_dict *req, const char *q_path, if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) { /** search by tag **/ int skip = 0; - int show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", "50")); + int show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", + xs_dict_get_def(srv_config, "max_timeline_entries", "50"))); const char *v; if ((v = xs_dict_get(q_vars, "skip")) != NULL) -- cgit v1.2.3 From 3099d179be9ffb38753e412480e23e5391913751 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 6 Feb 2025 20:10:52 +0100 Subject: Fixed a crash in xs_multipart_form_data(). --- xs_url.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xs_url.h b/xs_url.h index bb31779..37d2391 100644 --- a/xs_url.h +++ b/xs_url.h @@ -274,7 +274,8 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea l1 = xs_list_append(l1, vpo); l1 = xs_list_append(l1, vps); - p_vars = xs_dict_append(p_vars, vn, l1); + if (xs_is_string(vn)) + p_vars = xs_dict_append(p_vars, vn, l1); } else { /* regular variable; just copy */ @@ -282,7 +283,8 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea memcpy(vc, payload + po, ps); vc[ps] = '\0'; - p_vars = xs_dict_append(p_vars, vn, vc); + if (xs_is_string(vn) && xs_is_string(vc)) + p_vars = xs_dict_append(p_vars, vn, vc); } /* move on */ -- cgit v1.2.3 From 5de52cb753abaab03e9405441542e73803c3fe08 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 7 Feb 2025 09:26:54 +0100 Subject: Added support for multiple attachments in the same post. --- html.c | 168 ++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 59 deletions(-) diff --git a/html.c b/html.c index c12aea4..1d6acfc 100644 --- a/html.c +++ b/html.c @@ -327,7 +327,7 @@ xs_html *html_note(snac *user, const char *summary, const xs_val *cw_yn, const char *cw_text, const xs_val *mnt_only, const char *redir, const char *in_reply_to, int poll, - const char *att_file, const char *att_alt_text, + const xs_list *att_files, const xs_list *att_alt_texts, int is_draft) /* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */ { @@ -432,30 +432,71 @@ xs_html *html_note(snac *user, const char *summary, xs_html_tag("p", NULL), att = xs_html_tag("details", xs_html_tag("summary", - xs_html_text(L("Attachment..."))), + xs_html_text(L("Attachments..."))), xs_html_tag("p", NULL))); - if (att_file && *att_file) + int max_attachments = xs_number_get(xs_dict_get_def(srv_config, "max_attachments", "4")); + int att_n = 0; + + /* fields for the currently existing attachments */ + if (xs_is_list(att_files) && xs_is_list(att_alt_texts)) { + while (att_n < max_attachments) { + const char *att_file = xs_list_get(att_files, att_n); + const char *att_alt_text = xs_list_get(att_alt_texts, att_n); + + if (!xs_is_string(att_file) || !xs_is_string(att_alt_text)) + break; + + xs *att_lbl = xs_fmt("attach_url_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); + + if (att_n) + xs_html_add(att, + xs_html_sctag("br", NULL)); + + xs_html_add(att, + xs_html_text(L("File:")), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", att_lbl), + xs_html_attr("title", L("Clear this field to delete the attachment")), + xs_html_attr("value", att_file))); + + xs_html_add(att, + xs_html_text(" "), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", alt_lbl), + xs_html_attr("value", att_alt_text), + xs_html_attr("placeholder", L("Attachment description")))); + + att_n++; + } + } + + /* the rest of possible attachments */ + while (att_n < max_attachments) { + xs *att_lbl = xs_fmt("attach_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); + + if (att_n) + xs_html_add(att, + xs_html_sctag("br", NULL)); + xs_html_add(att, - xs_html_text(L("File:")), xs_html_sctag("input", - xs_html_attr("type", "text"), - xs_html_attr("name", "attach_url"), - xs_html_attr("title", L("Clear this field to delete the attachment")), - xs_html_attr("value", att_file))); - else + xs_html_attr("type", "file"), + xs_html_attr("name", att_lbl))); + xs_html_add(att, + xs_html_text(" "), xs_html_sctag("input", - xs_html_attr("type", "file"), - xs_html_attr("name", "attach"))); + xs_html_attr("type", "text"), + xs_html_attr("name", alt_lbl), + xs_html_attr("placeholder", L("Attachment description")))); - xs_html_add(att, - xs_html_text(" "), - xs_html_sctag("input", - xs_html_attr("type", "text"), - xs_html_attr("name", "alt_text"), - xs_html_attr("value", att_alt_text), - xs_html_attr("placeholder", L("Attachment description")))); + att_n++; + } /* add poll controls */ if (poll) { @@ -1059,7 +1100,7 @@ xs_html *html_top_controls(snac *snac) NULL, NULL, xs_stock(XSTYPE_FALSE), "", xs_stock(XSTYPE_FALSE), NULL, - NULL, 1, "", "", 0), + NULL, 1, NULL, NULL, 0), /** operations **/ xs_html_tag("details", @@ -1631,17 +1672,22 @@ xs_html *html_entry_controls(snac *snac, const char *actor, xs *form_id = xs_fmt("%s_edit_form", md5); xs *redir = xs_fmt("%s_entry", md5); - const char *att_file = ""; - const char *att_alt_text = ""; + xs *att_files = xs_list_new(); + xs *att_alt_texts = xs_list_new(); + const xs_list *att_list = xs_dict_get(msg, "attachment"); - /* does it have an attachment? */ - if (xs_type(att_list) == XSTYPE_LIST && xs_list_len(att_list)) { - const xs_dict *d = xs_list_get(att_list, 0); + if (xs_is_list(att_list)) { + const xs_dict *d; - if (xs_type(d) == XSTYPE_DICT) { - att_file = xs_dict_get_def(d, "url", ""); - att_alt_text = xs_dict_get_def(d, "name", ""); + xs_list_foreach(att_list, d) { + const char *att_file = xs_dict_get(d, "url"); + const char *att_alt_text = xs_dict_get(d, "name"); + + if (xs_is_string(att_file) && xs_is_string(att_alt_text)) { + att_files = xs_list_append(att_files, att_file); + att_alt_texts = xs_list_append(att_alt_texts, att_alt_text); + } } } @@ -1653,7 +1699,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, id, NULL, xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, - NULL, 0, att_file, att_alt_text, is_draft(snac, id))), + NULL, 0, att_files, att_alt_texts, is_draft(snac, id))), xs_html_tag("p", NULL)); } @@ -1672,7 +1718,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, NULL, NULL, xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, - id, 0, "", "", 0)), + id, 0, NULL, NULL, 0)), xs_html_tag("p", NULL)); } @@ -2938,7 +2984,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons NULL, actor_id, xs_stock(XSTYPE_FALSE), "", xs_stock(XSTYPE_FALSE), NULL, - NULL, 0, "", "", 0), + NULL, 0, NULL, NULL, 0), xs_html_tag("p", NULL)); xs_html_add(snac_post, snac_controls); @@ -3966,52 +4012,56 @@ int html_post_handler(const xs_dict *req, const char *q_path, /* post note */ const xs_str *content = xs_dict_get(p_vars, "content"); const xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); - const xs_str *attach_url = xs_dict_get(p_vars, "attach_url"); - const xs_list *attach_file = xs_dict_get(p_vars, "attach"); const xs_str *to = xs_dict_get(p_vars, "to"); const xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); const xs_str *summary = xs_dict_get(p_vars, "summary"); const xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); - const xs_str *alt_text = xs_dict_get(p_vars, "alt_text"); int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft")); xs *attach_list = xs_list_new(); - /* default alt text */ - if (xs_is_null(alt_text)) - alt_text = ""; + /* iterate the attachments */ + int max_attachments = xs_number_get(xs_dict_get_def(srv_config, "max_attachments", "4")); - /* is attach_url set? */ - if (!xs_is_null(attach_url) && *attach_url != '\0') { - xs *l = xs_list_new(); + for (int att_n = 0; att_n < max_attachments; att_n++) { + xs *url_lbl = xs_fmt("attach_url_%d", att_n); + xs *att_lbl = xs_fmt("attach_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); - l = xs_list_append(l, attach_url); - l = xs_list_append(l, alt_text); + const char *attach_url = xs_dict_get(p_vars, url_lbl); + const xs_list *attach_file = xs_dict_get(p_vars, att_lbl); + const char *alt_text = xs_dict_get_def(p_vars, alt_lbl, ""); - attach_list = xs_list_append(attach_list, l); - } + if (xs_is_string(attach_url) && *attach_url != '\0') { + xs *l = xs_list_new(); - /* is attach_file set? */ - if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) { - const char *fn = xs_list_get(attach_file, 0); + l = xs_list_append(l, attach_url); + l = xs_list_append(l, alt_text); - if (*fn != '\0') { - char *ext = strrchr(fn, '.'); - xs *hash = xs_md5_hex(fn, strlen(fn)); - xs *id = xs_fmt("%s%s", hash, ext); - xs *url = xs_fmt("%s/s/%s", snac.actor, id); - int fo = xs_number_get(xs_list_get(attach_file, 1)); - int fs = xs_number_get(xs_list_get(attach_file, 2)); + attach_list = xs_list_append(attach_list, l); + } + else + if (xs_is_list(attach_file)) { + const char *fn = xs_list_get(attach_file, 0); - /* store */ - static_put(&snac, id, payload + fo, fs); + if (xs_is_string(fn) && *fn != '\0') { + char *ext = strrchr(fn, '.'); + xs *hash = xs_md5_hex(fn, strlen(fn)); + xs *id = xs_fmt("%s%s", hash, ext); + xs *url = xs_fmt("%s/s/%s", snac.actor, id); + int fo = xs_number_get(xs_list_get(attach_file, 1)); + int fs = xs_number_get(xs_list_get(attach_file, 2)); - xs *l = xs_list_new(); + /* store */ + static_put(&snac, id, payload + fo, fs); - l = xs_list_append(l, url); - l = xs_list_append(l, alt_text); + xs *l = xs_list_new(); - attach_list = xs_list_append(attach_list, l); + l = xs_list_append(l, url); + l = xs_list_append(l, alt_text); + + attach_list = xs_list_append(attach_list, l); + } } } -- cgit v1.2.3 From 413862f404c65ef57f9d7da99b775c72c2da4e31 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 7 Feb 2025 09:29:33 +0100 Subject: Updated documentation. --- doc/snac.8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/snac.8 b/doc/snac.8 index aac9f70..7e3213b 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -260,6 +260,8 @@ need at least a Linux kernel version 5.13.0. .It Ic max_public_entries The maximum number of entries (posts) to be returned in user RSS feeds and outboxes (default: 20). +.It Ic max_attachments +The maximum number of attachments per post (default: 4). .El .Pp You must restart the server to make effective these changes. -- cgit v1.2.3 From cfe73e119da9783e0dab52860298e5dfe8ff63a6 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 7 Feb 2025 09:31:35 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c6d439d..b7c1c89 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,10 +2,12 @@ ## UNRELEASED -Fixed a search bug. +Each post can have more than one attachment from the web UI. The maximum number can be configured in `server.json` via the `max_attachments` value (default: 4). Each notification includes a link labelled `Context`, that leads to a page with the full conversation tree the post is a part of. +Fixed a search bug (some matches were missed). + Fixed more crashes (contributed by inz). Fixed link detection in posts (contributed by inz). -- cgit v1.2.3 From 1787102870161914fa7ba19d16803ebf7050b7dd Mon Sep 17 00:00:00 2001 From: default Date: Fri, 7 Feb 2025 09:41:31 +0100 Subject: mastoapi: notify the maximum configured attachments. --- mastoapi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index 3c445c2..797a4da 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2171,7 +2171,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, { xs *d11 = xs_json_loads("{\"characters_reserved_per_url\":32," - "\"max_characters\":100000,\"max_media_attachments\":8}"); + "\"max_characters\":100000,\"max_media_attachments\":4}"); + + const xs_number *max_attachments = xs_dict_get(srv_config, "max_attachments"); + if (xs_type(max_attachments) == XSTYPE_NUMBER) + d11 = xs_dict_set(d11, "max_media_attachments", max_attachments); + cfg = xs_dict_append(cfg, "statuses", d11); xs *d12 = xs_json_loads("{\"max_featured_tags\":0}"); -- cgit v1.2.3 From 54fe489c5f1582cbc8d627eccf2fe575dd38100c Mon Sep 17 00:00:00 2001 From: default Date: Sat, 8 Feb 2025 05:38:10 +0100 Subject: Show links to followed hashtags just below the list of lists. --- html.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/html.c b/html.c index 1d6acfc..f39bef4 100644 --- a/html.c +++ b/html.c @@ -2706,6 +2706,29 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_attr("title", L("Post drafts")), xs_html_text("drafts")))); } + + /* the list of followed hashtags */ + const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags"); + + if (xs_is_list(followed_hashtags) && xs_list_len(followed_hashtags)) { + xs_html *loht = xs_html_tag("ul", + xs_html_attr("class", "snac-list-of-lists")); + xs_html_add(body, loht); + + const char *ht; + + xs_list_foreach(followed_hashtags, ht) { + xs *url = xs_fmt("%s/admin?q=%s", user->actor, ht); + url = xs_replace_i(url, "#", "%23"); + + xs_html_add(loht, + xs_html_tag("li", + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("class", "snac-list-link"), + xs_html_text(ht)))); + } + } } xs_html_add(body, -- cgit v1.2.3 From 99d28c3d6c476a1d70b83a5685fbc53dcb74dc2f Mon Sep 17 00:00:00 2001 From: default Date: Sat, 8 Feb 2025 16:59:26 +0100 Subject: Fixed crash in incomplete poll post. --- html.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/html.c b/html.c index f39bef4..e742e94 100644 --- a/html.c +++ b/html.c @@ -2099,16 +2099,17 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, const char *name = xs_dict_get(v, "name"); const xs_dict *replies = xs_dict_get(v, "replies"); - if (name && replies) { - char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems")); + if (xs_is_string(name) && xs_is_dict(replies)) { + const char *ti = xs_number_str(xs_dict_get(replies, "totalItems")); - xs_html_add(poll_result, - xs_html_tag("tr", - xs_html_tag("td", - xs_html_text(name), - xs_html_text(":")), - xs_html_tag("td", - xs_html_text(ti)))); + if (xs_is_string(ti)) + xs_html_add(poll_result, + xs_html_tag("tr", + xs_html_tag("td", + xs_html_text(name), + xs_html_text(":")), + xs_html_tag("td", + xs_html_text(ti)))); } } -- cgit v1.2.3 From 3c5ccc9cefacab86fbff08feaa10e964f831a383 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 8 Feb 2025 17:05:55 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b7c1c89..b135649 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Each post can have more than one attachment from the web UI. The maximum number Each notification includes a link labelled `Context`, that leads to a page with the full conversation tree the post is a part of. +Each followed hashtag has now a directly accesible link. + Fixed a search bug (some matches were missed). Fixed more crashes (contributed by inz). -- cgit v1.2.3 From 699762ef4ca90806f8ee95c3f92b5345cedc77a5 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 8 Feb 2025 17:08:50 +0100 Subject: Updated README. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8d5a59d..b01308e 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ This will: - [Setting up Snac in OpenBSD (by Yonle)](https://wiki.ircnow.org/index.php?n=Openbsd.Snac). - [How to run your own social network with snac (by Giacomo Tesio)](https://encrypted.tesio.it/2024/12/18/how-to-run-your-own-social-network.html). Includes information on how to run snac as a CGI. - [Improving snac Performance with Nginx Proxy Cache (by Stefano Marinelli)](https://it-notes.dragas.net/2025/01/29/improving-snac-performance-with-nginx-proxy-cache/). +- [Caching Snac Proxied Media With Nginx (by Stefano Marinelli)](https://it-notes.dragas.net/2025/02/08/caching-snac-proxied-media-with-nginx/). ## Incredibly awesome CSS themes for snac -- cgit v1.2.3 From d9b6b7ce086e0372e33f855d913891d624024898 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 9 Feb 2025 07:18:17 +0100 Subject: Minor xs_html tweak. --- xs_html.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs_html.h b/xs_html.h index 8331f93..29886c6 100644 --- a/xs_html.h +++ b/xs_html.h @@ -114,7 +114,7 @@ xs_html *xs_html_text(const char *content) xs_html *a = XS_HTML_NEW(); a->type = XS_HTML_TEXT; - a->content = xs_html_encode(content); + a->content = xs_is_string(content) ? xs_html_encode(content) : xs_str_new(NULL); return a; } @@ -126,7 +126,7 @@ xs_html *xs_html_raw(const char *content) xs_html *a = XS_HTML_NEW(); a->type = XS_HTML_TEXT; - a->content = xs_dup(content); + a->content = xs_is_string(content) ? xs_dup(content) : xs_str_new(NULL); return a; } -- cgit v1.2.3 From 3db7d9f58abe4595fbc75fb9d08dc84eba742bdd Mon Sep 17 00:00:00 2001 From: default Date: Sun, 9 Feb 2025 18:12:58 +0100 Subject: Fixed function tid() for some architectures. --- snac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.c b/snac.c index 80c7349..9f5b50e 100644 --- a/snac.c +++ b/snac.c @@ -69,7 +69,7 @@ xs_str *tid(int offset) gettimeofday(&tv, NULL); - return xs_fmt("%10d.%06d", tv.tv_sec + offset, tv.tv_usec); + return xs_fmt("%010ld.%06ld", (long)tv.tv_sec + (long)offset, (long)tv.tv_usec); } -- cgit v1.2.3 From fa260625b57cbc6c11627f4b96734ebc4f9128d5 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 10 Feb 2025 09:46:46 +0100 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b135649..f8566a2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ # Release Notes -## UNRELEASED +## 2.72 Each post can have more than one attachment from the web UI. The maximum number can be configured in `server.json` via the `max_attachments` value (default: 4). -- cgit v1.2.3 From 3d705b22a67ee913b9bd4473277430bd481cef25 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 10 Feb 2025 09:47:15 +0100 Subject: Version 2.72 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 7b5c54c..2ff3235 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ -#define VERSION "2.72-dev" +#define VERSION "2.72" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3