From 7312f4ce51100d4ed98988cb3af0ab06e4fba53c Mon Sep 17 00:00:00 2001 From: iwojim0 Date: Sat, 9 Mar 2024 14:37:54 +0000 Subject: [PATCH 001/120] Ability to federate with hidden networks #93 (update for v2.49) --- activitypub.c | 4 ++-- data.c | 5 +++-- html.c | 2 +- http.c | 3 ++- mastoapi.c | 2 +- webfinger.c | 9 +++++---- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/activitypub.c b/activitypub.c index 1976012..73fbbc6 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1228,7 +1228,7 @@ xs_dict *msg_actor(snac *snac) xs *k2 = encode_html(k); xs *v2 = NULL; - if (xs_startswith(v, "https:")) { + if (xs_startswith(v, "http")) { xs *t = encode_html(v); v2 = xs_fmt("%s", t, t); } @@ -1320,7 +1320,7 @@ xs_dict *msg_follow(snac *snac, const char *q) xs *url_or_uid = xs_strip_i(xs_str_new(q)); - if (xs_startswith(url_or_uid, "https:/")) + if (xs_startswith(url_or_uid, "http")) actor = xs_dup(url_or_uid); else if (!valid_status(webfinger_request(url_or_uid, &actor, NULL)) || actor == NULL) { diff --git a/data.c b/data.c index 4d9824a..f4cd6d6 100644 --- a/data.c +++ b/data.c @@ -67,7 +67,7 @@ int srv_open(char *basedir, int auto_upgrade) if (host == NULL || prefix == NULL) error = xs_str_new("ERROR: cannot get server data"); else { - srv_baseurl = xs_fmt("https://%s%s", host, prefix); + srv_baseurl = xs_fmt("http://%s%s", host, prefix); dbglevel = (int) xs_number_get(dbglvl); @@ -1944,7 +1944,8 @@ xs_list *inbox_list(void) xs_str *_instance_block_fn(const char *instance) { - xs *s1 = xs_replace(instance, "https:/" "/", ""); + xs *s = xs_replace(instance, "http:/" "/", ""); + xs *s1 = xs_replace(s, "https:/" "/", ""); xs *l = xs_split(s1, "/"); char *p = xs_list_get(l, 0); xs *md5 = xs_md5_hex(p, strlen(p)); diff --git a/html.c b/html.c index 59fd6c9..46ef735 100644 --- a/html.c +++ b/html.c @@ -783,7 +783,7 @@ static xs_html *html_user_body(snac *user, int read_only) while (xs_dict_next(metadata, &k, &v, &c)) { xs_html *value; - if (xs_startswith(v, "https:/" "/")) { + if (xs_startswith(v, "http")) { /* is this link validated? */ xs *verified_link = NULL; xs_number *val_time = xs_dict_get(val_links, v); diff --git a/http.c b/http.c index 1b3d590..f7ff9ba 100644 --- a/http.c +++ b/http.c @@ -32,7 +32,8 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, date = xs_str_utctime(0, "%a, %d %b %Y %H:%M:%S GMT"); { - xs *s = xs_replace_n(url, "https:/" "/", "", 1); + xs *s1 = xs_replace_n(url, "http:/" "/", "", 1); + xs *s = xs_replace_n(s1, "https:/" "/", "", 1); l1 = xs_split_n(s, "/", 1); } diff --git a/mastoapi.c b/mastoapi.c index 416f96c..d702c47 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -156,7 +156,7 @@ const char *login_page = "" "\n" "

%s OAuth identify

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

Login:

\n" "

Password:

\n" "\n" diff --git a/webfinger.c b/webfinger.c index a883d7f..331191b 100644 --- a/webfinger.c +++ b/webfinger.c @@ -19,9 +19,10 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us xs_str *host = NULL; xs *resource = NULL; - if (xs_startswith(qs, "https:/" "/")) { + if (xs_startswith(qs, "http")) { /* actor query: pick the host */ - xs *s = xs_replace_n(qs, "https:/" "/", "", 1); + xs *s1 = xs_replace_n(qs, "http:/" "/", "", 1); + xs *s = xs_replace_n(s1, "https:/" "/", "", 1); l = xs_split_n(s, "/", 1); @@ -69,7 +70,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us &payload, &p_size, &ctype); } else { - xs *url = xs_fmt("https:/" "/%s/.well-known/webfinger?resource=%s", host, resource); + xs *url = xs_fmt("http:/" "/%s/.well-known/webfinger?resource=%s", host, resource); if (snac == NULL) xs_http_request("GET", url, headers, NULL, 0, &status, &payload, &p_size, 0); @@ -139,7 +140,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, snac snac; int found = 0; - if (xs_startswith(resource, "https:/" "/")) { + if (xs_startswith(resource, "https")) { /* actor search: find a user with this actor */ xs *l = xs_split(resource, "/"); char *uid = xs_list_get(l, -1); From 46f2e324d8df49af914ba5781f13337dea7dd6aa Mon Sep 17 00:00:00 2001 From: default Date: Thu, 11 Apr 2024 19:29:30 +0200 Subject: [PATCH 002/120] Undo for Likes and Announces are no longer dropped. --- activitypub.c | 14 ++++++++++++++ data.c | 1 + 2 files changed, 15 insertions(+) diff --git a/activitypub.c b/activitypub.c index 9a23e14..d75bbdd 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1941,6 +1941,8 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } else if (strcmp(type, "Undo") == 0) { /** **/ + char *id = xs_dict_get(object, "object"); + if (xs_type(object) != XSTYPE_DICT) utype = "Follow"; @@ -1952,6 +1954,18 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) else snac_log(snac, xs_fmt("error deleting follower %s", actor)); } + else + if (strcmp(utype, "Like") == 0) { /** **/ + int status = object_unadmire(id, actor, 1); + + snac_log(snac, xs_fmt("Unlike for %s %d", id, status)); + } + else + if (strcmp(utype, "Announce") == 0) { /** **/ + int status = object_unadmire(id, actor, 0); + + snac_log(snac, xs_fmt("Unboost for %s %d", id, status)); + } else snac_debug(snac, 1, xs_fmt("ignored 'Undo' for object type '%s'", utype)); } diff --git a/data.c b/data.c index 7dd7d19..5cf62e7 100644 --- a/data.c +++ b/data.c @@ -916,6 +916,7 @@ int object_unadmire(const char *id, const char *actor, int like) fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx"); status = index_del(fn, actor); + index_gc(fn); srv_debug(0, xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); From e362dfc3563c2564b550a3274f7bf5c7ad9e92f8 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 11 Apr 2024 19:39:45 +0200 Subject: [PATCH 003/120] Also purge like and announce index backups. --- data.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/data.c b/data.c index 5cf62e7..c55f70f 100644 --- a/data.c +++ b/data.c @@ -2744,6 +2744,16 @@ void purge_server(void) } } } + + /* delete index backups */ + xs *specb = xs_fmt("%s/" "*.bak", v); + xs *bakfs = xs_glob(specb, 0, 0); + + p2 = bakfs; + while (xs_list_iter(&p2, &v2)) { + unlink(v2); + srv_debug(1, xs_fmt("purged %s", v2)); + } } } From a7bd9b63e4992018cade7c5c388bf30878389274 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 11 Apr 2024 19:55:45 +0200 Subject: [PATCH 004/120] In object_unadmire(), don't call index_gc() if index_del() did nothing. --- data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index c55f70f..688813f 100644 --- a/data.c +++ b/data.c @@ -916,7 +916,9 @@ int object_unadmire(const char *id, const char *actor, int like) fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx"); status = index_del(fn, actor); - index_gc(fn); + + if (valid_status(status)) + index_gc(fn); srv_debug(0, xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); From dba063066025d8feed0e18dde91037c9bb503d53 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 12 Apr 2024 05:28:34 +0200 Subject: [PATCH 005/120] Commented out received unboosts (see code for an explanation). --- activitypub.c | 7 ++++++- data.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index d75bbdd..526302a 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1962,7 +1962,12 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } else if (strcmp(utype, "Announce") == 0) { /** **/ - int status = object_unadmire(id, actor, 0); + int status = 200; + + /* commented out: if a followed user boosts something that + is requested and then unboosts, the post remains here, + but with no apparent reason, and that is confusing */ + //status = object_unadmire(id, actor, 0); snac_log(snac, xs_fmt("Unboost for %s %d", id, status)); } diff --git a/data.c b/data.c index 688813f..b6aa9cf 100644 --- a/data.c +++ b/data.c @@ -406,7 +406,7 @@ int index_del_md5(const char *fn, const char *md5) fclose(f); } else - status = 500; + status = 410; pthread_mutex_unlock(&data_mutex); From c9df6707abc8a8d2b7815f5210cf3667be6fb9b1 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 14 Apr 2024 18:31:53 +0200 Subject: [PATCH 006/120] Log status in mastoapi_post_handler(). --- mastoapi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 4d80f69..36f7238 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2119,8 +2119,6 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; - srv_debug(1, xs_fmt("mastoapi_post_handler %s", q_path)); - int status = 404; xs *args = NULL; char *i_ctype = xs_dict_get(req, "content-type"); @@ -2626,6 +2624,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (logged_in) user_free(&snac); + srv_debug(1, xs_fmt("mastoapi_post_handler %s %d", q_path, status)); + return status; } From 81cf1e21a646568323681df2763e66f933bee48f Mon Sep 17 00:00:00 2001 From: default Date: Sun, 14 Apr 2024 19:24:06 +0200 Subject: [PATCH 007/120] Better redirection URL building in oauth_post_handler(). --- mastoapi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index 36f7238..2b52972 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -289,7 +289,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, *body = xs_dup(code); } else { - *body = xs_fmt("%s?code=%s", redir, code); + if (xs_str_in(redir, "?")) + *body = xs_fmt("%s&code=%s", redir, code); + else + *body = xs_fmt("%s?code=%s", redir, code); + status = 303; } From 0275658a3602f74035ff776dac8d8f64b9a2794f Mon Sep 17 00:00:00 2001 From: default Date: Sun, 14 Apr 2024 19:26:49 +0200 Subject: [PATCH 008/120] Backport from xs. --- xs.h | 4 ++++ xs_json.h | 10 +++++----- xs_mime.h | 22 +++++++++++++--------- xs_unicode.h | 16 ++++++++-------- xs_url.h | 2 +- xs_version.h | 2 +- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/xs.h b/xs.h index d2de44a..bab315a 100644 --- a/xs.h +++ b/xs.h @@ -45,6 +45,10 @@ typedef char xs_data; /* not really all, just very much */ #define XS_ALL 0xfffffff +#ifndef xs_countof +#define xs_countof(a) (sizeof((a)) / sizeof((*a))) +#endif + void *xs_free(void *ptr); void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func); #define xs_realloc(ptr, size) _xs_realloc(ptr, size, __FILE__, __LINE__, __FUNCTION__) diff --git a/xs_json.h b/xs_json.h index 1494fe8..6706d7e 100644 --- a/xs_json.h +++ b/xs_json.h @@ -328,7 +328,7 @@ static xs_val *_xs_json_load_lexer(FILE *f, js_type *t) int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c) /* loads the next scalar value from the JSON stream */ -/* if the value ahead is complex, value is NULL and pt is filled */ +/* if the value ahead is compound, value is NULL and pt is set */ { js_type t; @@ -348,7 +348,7 @@ int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c) } if (*value == NULL) { - /* possible complex type ahead */ + /* possible compound type ahead */ if (t == JS_OBRACK) *pt = XSTYPE_LIST; else @@ -365,7 +365,7 @@ int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c) xs_list *xs_json_load_array(FILE *f) -/* loads a JSON array (after the initial OBRACK) */ +/* loads a full JSON array (after the initial OBRACK) */ { xstype t; xs_list *l = xs_list_new(); @@ -406,7 +406,7 @@ xs_list *xs_json_load_array(FILE *f) int xs_json_load_object_iter(FILE *f, xs_str **key, xs_val **value, xstype *pt, int *c) /* loads the next key and scalar value from the JSON stream */ -/* if the value ahead is complex, value is NULL and pt is filled */ +/* if the value ahead is compound, value is NULL and pt is set */ { js_type t; @@ -453,7 +453,7 @@ int xs_json_load_object_iter(FILE *f, xs_str **key, xs_val **value, xstype *pt, xs_dict *xs_json_load_object(FILE *f) -/* loads a JSON object (after the initial OCURLY) */ +/* loads a full JSON object (after the initial OCURLY) */ { xstype t; xs_dict *d = xs_dict_new(); diff --git a/xs_mime.h b/xs_mime.h index 84af49c..853b092 100644 --- a/xs_mime.h +++ b/xs_mime.h @@ -55,19 +55,23 @@ const char *xs_mime_by_ext(const char *file) const char *ext = strrchr(file, '.'); if (ext) { - const char **p = xs_mime_types; - xs *uext = xs_tolower_i(xs_dup(ext + 1)); + xs *uext = xs_tolower_i(xs_dup(ext + 1)); + int b = 0; + int t = xs_countof(xs_mime_types) / 2 - 2; - while (*p) { - int c; + while (t >= b) { + int n = (b + t) / 2; + const char *p = xs_mime_types[n * 2]; - if ((c = strcmp(*p, uext)) == 0) - return p[1]; + int c = strcmp(uext, p); + + if (c < 0) + t = n - 1; else if (c > 0) - break; - - p += 2; + b = n + 1; + else + return xs_mime_types[(n * 2) + 1]; } } diff --git a/xs_unicode.h b/xs_unicode.h index 47e1101..6654da4 100644 --- a/xs_unicode.h +++ b/xs_unicode.h @@ -27,8 +27,8 @@ #ifdef XS_IMPLEMENTATION -#ifndef countof -#define countof(a) (sizeof((a)) / sizeof((*a))) +#ifndef xs_countof +#define xs_countof(a) (sizeof((a)) / sizeof((*a))) #endif int _xs_utf8_enc(char buf[4], unsigned int cpoint) @@ -125,7 +125,7 @@ int xs_unicode_width(unsigned int cpoint) /* returns the width in columns of a Unicode codepoint (somewhat simplified) */ { int b = 0; - int t = countof(xs_unicode_width_table) / 3 - 1; + int t = xs_countof(xs_unicode_width_table) / 3 - 1; while (t >= b) { int n = (b + t) / 2; @@ -193,7 +193,7 @@ unsigned int *_xs_unicode_upper_search(unsigned int cpoint) /* searches for an uppercase codepoint in the case fold table */ { int b = 0; - int t = countof(xs_unicode_case_fold_table) / 2 + 1; + int t = xs_countof(xs_unicode_case_fold_table) / 2 + 1; while (t >= b) { int n = (b + t) / 2; @@ -216,7 +216,7 @@ unsigned int *_xs_unicode_lower_search(unsigned int cpoint) /* searches for a lowercase codepoint in the case fold table */ { unsigned int *p = xs_unicode_case_fold_table; - unsigned int *e = p + countof(xs_unicode_case_fold_table); + unsigned int *e = p + xs_countof(xs_unicode_case_fold_table); while (p < e) { if (cpoint == p[1]) @@ -251,7 +251,7 @@ int xs_unicode_nfd(unsigned int cpoint, unsigned int *base, unsigned int *diac) /* applies unicode Normalization Form D */ { int b = 0; - int t = countof(xs_unicode_nfd_table) / 3 - 1; + int t = xs_countof(xs_unicode_nfd_table) / 3 - 1; while (t >= b) { int n = (b + t) / 2; @@ -279,7 +279,7 @@ int xs_unicode_nfc(unsigned int base, unsigned int diac, unsigned int *cpoint) /* applies unicode Normalization Form C */ { unsigned int *p = xs_unicode_nfd_table; - unsigned int *e = p + countof(xs_unicode_nfd_table); + unsigned int *e = p + xs_countof(xs_unicode_nfd_table); while (p < e) { if (p[1] == base && p[2] == diac) { @@ -298,7 +298,7 @@ int xs_unicode_is_alpha(unsigned int cpoint) /* checks if a codepoint is an alpha (i.e. a letter) */ { int b = 0; - int t = countof(xs_unicode_alpha_table) / 2 - 1; + int t = xs_countof(xs_unicode_alpha_table) / 2 - 1; while (t >= b) { int n = (b + t) / 2; diff --git a/xs_url.h b/xs_url.h index f335709..6c9c8b5 100644 --- a/xs_url.h +++ b/xs_url.h @@ -56,7 +56,7 @@ xs_dict *xs_url_vars(const char *str) l = args; while (xs_list_iter(&l, &v)) { - xs *kv = xs_split_n(v, "=", 2); + xs *kv = xs_split_n(v, "=", 1); if (xs_list_len(kv) == 2) { const char *key = xs_list_get(kv, 0); diff --git a/xs_version.h b/xs_version.h index ef52120..f655735 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 0df383371d207b0adfda40912ee54b37e5b01152 2024-03-15T03:45:39+01:00 */ +/* f712d1336ef427c3b56305364b2687578537543f 2024-04-14T19:11:53+02:00 */ From 86f1d74214ff9de162b6abd0016d183b3f934d11 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 15 Apr 2024 06:32:38 +0200 Subject: [PATCH 009/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f82e722..f272f5c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,8 @@ Posts that were liked or boosted can now be unliked and unboosted. +Added a header to avoid over-zealous caching in some browsers (contributed by louis77). + ## 2.51 Support for custom Emojis has been added; they are no longer hardcoded, but read from the `emojis.json` file at the server base directory. Also, they are no longer limited to string substitutions, but images as external URLs are also supported (see `snac(8)` for more information). From 2e31253ba8de74e073fd104785bde78510c843c1 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 17 Apr 2024 06:45:19 +0200 Subject: [PATCH 010/120] Don't skip orphan private posts that are for us. --- html.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index f50fb7d..59ada61 100644 --- a/html.c +++ b/html.c @@ -2055,8 +2055,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, char *irt = xs_dict_get(msg, "inReplyTo"); if (!xs_is_null(irt) && !object_here(irt)) { - snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v)); - continue; + /* is it for me? */ + xs_list *to = xs_dict_get(msg, "to"); + + if (xs_type(to) == XSTYPE_LIST && xs_list_in(to, user->actor) == -1) { + snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v)); + continue; + } } } From 44d126e0f96748f7f5e0c8a95ed8b3f5f69cd0f0 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 17 Apr 2024 08:09:05 +0200 Subject: [PATCH 011/120] Improved non-public reply check. --- html.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index 59ada61..0fca601 100644 --- a/html.c +++ b/html.c @@ -2054,11 +2054,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, if (user != NULL && !is_msg_public(msg)) { char *irt = xs_dict_get(msg, "inReplyTo"); + /* is it a reply to something not in the storage? */ if (!xs_is_null(irt) && !object_here(irt)) { /* is it for me? */ - xs_list *to = xs_dict_get(msg, "to"); + xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST)); + xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST)); - if (xs_type(to) == XSTYPE_LIST && xs_list_in(to, user->actor) == -1) { + if (xs_list_in(to, user->actor) == -1 && xs_list_in(cc, user->actor) == -1) { snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v)); continue; } From 26840e0dc0e7322b2041c21b4e44ed9a34afff4b Mon Sep 17 00:00:00 2001 From: default Date: Thu, 18 Apr 2024 17:13:31 +0200 Subject: [PATCH 012/120] Some tweaks to previous patch. --- activitypub.c | 4 ++-- data.c | 6 ++++-- html.c | 2 +- mastoapi.c | 20 +++++++++++--------- webfinger.c | 8 +++++--- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/activitypub.c b/activitypub.c index 53f102e..c05e3f3 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1277,7 +1277,7 @@ xs_dict *msg_actor(snac *snac) xs *k2 = encode_html(k); xs *v2 = NULL; - if (xs_startswith(v, "http")) { + if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) { xs *t = encode_html(v); v2 = xs_fmt("%s", t, t); } @@ -1369,7 +1369,7 @@ xs_dict *msg_follow(snac *snac, const char *q) xs *url_or_uid = xs_strip_i(xs_str_new(q)); - if (xs_startswith(url_or_uid, "http")) + if (xs_startswith(url_or_uid, "https:/") || xs_startswith(url_or_uid, "http:/")) actor = xs_dup(url_or_uid); else if (!valid_status(webfinger_request(url_or_uid, &actor, NULL)) || actor == NULL) { diff --git a/data.c b/data.c index d3045f4..1e46395 100644 --- a/data.c +++ b/data.c @@ -60,15 +60,17 @@ int srv_open(char *basedir, int auto_upgrade) char *host; char *prefix; char *dbglvl; + char *proto; host = xs_dict_get(srv_config, "host"); prefix = xs_dict_get(srv_config, "prefix"); dbglvl = xs_dict_get(srv_config, "dbglevel"); + proto = xs_dict_get_def(srv_config, "protocol", "https"); if (host == NULL || prefix == NULL) error = xs_str_new("ERROR: cannot get server data"); else { - srv_baseurl = xs_fmt("http://%s%s", host, prefix); + srv_baseurl = xs_fmt("%s:/" "/%s%s", proto, host, prefix); dbglevel = (int) xs_number_get(dbglvl); @@ -1990,7 +1992,7 @@ xs_list *inbox_list(void) xs_str *_instance_block_fn(const char *instance) { - xs *s = xs_replace(instance, "http:/" "/", ""); + xs *s = xs_replace(instance, "http:/" "/", ""); xs *s1 = xs_replace(s, "https:/" "/", ""); xs *l = xs_split(s1, "/"); char *p = xs_list_get(l, 0); diff --git a/html.c b/html.c index a251e21..d52ef03 100644 --- a/html.c +++ b/html.c @@ -813,7 +813,7 @@ static xs_html *html_user_body(snac *user, int read_only) while (xs_dict_next(metadata, &k, &v, &c)) { xs_html *value; - if (xs_startswith(v, "http")) { + if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) { /* is this link validated? */ xs *verified_link = NULL; xs_number *val_time = xs_dict_get(val_links, v); diff --git a/mastoapi.c b/mastoapi.c index 78fd802..b49fbca 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -156,7 +156,7 @@ const char *login_page = "" "\n" "

%s OAuth identify

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

Login:

\n" "

Password:

\n" "\n" @@ -193,11 +193,12 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, if (app != NULL) { const char *host = xs_dict_get(srv_config, "host"); + const char *proto = xs_dict_get_def(srv_config, "protocol", "https"); if (xs_is_null(state)) state = ""; - *body = xs_fmt(login_page, host, host, "", host, "oauth/x-snac-login", + *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-login", ruri, cid, state, USER_AGENT); *ctype = "text/html"; status = 200; @@ -213,8 +214,9 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, else if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/ const char *host = xs_dict_get(srv_config, "host"); + const char *proto = xs_dict_get_def(srv_config, "protocol", "https"); - *body = xs_fmt(login_page, host, host, "", host, "oauth/x-snac-get-token", + *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-get-token", "", "", "", USER_AGENT); *ctype = "text/html"; status = 200; @@ -265,11 +267,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, const char *redir = xs_dict_get(args, "redir"); const char *cid = xs_dict_get(args, "cid"); const char *state = xs_dict_get(args, "state"); - - const char *host = xs_dict_get(srv_config, "host"); + const char *host = xs_dict_get(srv_config, "host"); + const char *proto = xs_dict_get_def(srv_config, "protocol", "https"); /* by default, generate another login form with an error */ - *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", host, "oauth/x-snac-login", + *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-login", redir, cid, state, USER_AGENT); *ctype = "text/html"; status = 200; @@ -450,11 +452,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/ const char *login = xs_dict_get(args, "login"); const char *passwd = xs_dict_get(args, "passwd"); - - const char *host = xs_dict_get(srv_config, "host"); + const char *host = xs_dict_get(srv_config, "host"); + const char *proto = xs_dict_get_def(srv_config, "protocol", "https"); /* by default, generate another login form with an error */ - *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", host, "oauth/x-snac-get-token", + *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-get-token", "", "", "", USER_AGENT); *ctype = "text/html"; status = 200; diff --git a/webfinger.c b/webfinger.c index 331191b..5b13d2f 100644 --- a/webfinger.c +++ b/webfinger.c @@ -19,7 +19,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us xs_str *host = NULL; xs *resource = NULL; - if (xs_startswith(qs, "http")) { + if (xs_startswith(qs, "https:/") || xs_startswith(qs, "http:/")) { /* actor query: pick the host */ xs *s1 = xs_replace_n(qs, "http:/" "/", "", 1); xs *s = xs_replace_n(s1, "https:/" "/", "", 1); @@ -70,7 +70,9 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us &payload, &p_size, &ctype); } else { - xs *url = xs_fmt("http:/" "/%s/.well-known/webfinger?resource=%s", host, resource); + const char *proto = xs_dict_get_def(srv_config, "protocol", "https"); + + xs *url = xs_fmt("%s:/" "/%s/.well-known/webfinger?resource=%s", proto, host, resource); if (snac == NULL) xs_http_request("GET", url, headers, NULL, 0, &status, &payload, &p_size, 0); @@ -140,7 +142,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, snac snac; int found = 0; - if (xs_startswith(resource, "https")) { + if (xs_startswith(resource, "https:/") || xs_startswith(resource, "http:/")) { /* actor search: find a user with this actor */ xs *l = xs_split(resource, "/"); char *uid = xs_list_get(l, -1); From 64adb173cc7006d914394f1380da3f8987b99956 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 19 Apr 2024 08:56:03 +0200 Subject: [PATCH 013/120] Only add Emoji tags when needed. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 92901bb..8d51351 100644 --- a/format.c +++ b/format.c @@ -241,7 +241,7 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag /* is it an URL to an image? */ if (xs_startswith(v, "https:/" "/") && xs_startswith((t = xs_mime_by_ext(v)), "image/")) { - if (tag) { + if (tag && xs_str_in(s, k) != -1) { /* add the emoji to the tag list */ xs *e = xs_dict_new(); xs *i = xs_dict_new(); From 922c94ce3c7d557ac51adc0af523451225a8fe8a Mon Sep 17 00:00:00 2001 From: default Date: Fri, 19 Apr 2024 09:09:46 +0200 Subject: [PATCH 014/120] Updated documentation. --- doc/snac.8 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/snac.8 b/doc/snac.8 index 4929a52..b25205b 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -209,6 +209,13 @@ with a large number of users. If this numeric value (in seconds) is set, any activity coming from an account that was created more recently than that will be rejected. This may be used to mitigate spam from automatically created accounts. +.It Ic protocol +This string value contains the protocol (schema) to be used in URLs. If not +set, it defaults to "https". If you run +.Nm +as part of a hidden network like Tor or I2P that doesn't have a TLS / +Certificate infrastructure, you need to set it to "http". Don't change it +unless you know what you are doing. .El .Pp You must restart the server to make effective these changes. From 040c43fb08fb4c9c00240c53515432f5f83454c9 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 19 Apr 2024 09:11:28 +0200 Subject: [PATCH 015/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f272f5c..e1528b7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Posts that were liked or boosted can now be unliked and unboosted. Added a header to avoid over-zealous caching in some browsers (contributed by louis77). +Added support for running and federating inside hidden networks like Tor, I2P or Loki (contributed by iwojima). + ## 2.51 Support for custom Emojis has been added; they are no longer hardcoded, but read from the `emojis.json` file at the server base directory. Also, they are no longer limited to string substitutions, but images as external URLs are also supported (see `snac(8)` for more information). From 5daab81d01e4524616e786b0dc63778fd4201dae Mon Sep 17 00:00:00 2001 From: default Date: Fri, 19 Apr 2024 09:26:34 +0200 Subject: [PATCH 016/120] Added the default 'protocol' field to 'server.json'. --- utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utils.c b/utils.c index 35caa6e..2b50d25 100644 --- a/utils.c +++ b/utils.c @@ -34,6 +34,7 @@ static const char *default_srv_config = "{" "\"admin_account\": \"\"," "\"title\": \"\"," "\"short_description\": \"\"," + "\"protocol\": \"https\"," "\"fastcgi\": false" "}"; From 62cc167c5f9205bf3a758a40cd7b22b9a54164be Mon Sep 17 00:00:00 2001 From: default Date: Sat, 20 Apr 2024 22:48:02 +0200 Subject: [PATCH 017/120] Updated TODO. --- TODO.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index bdb4f23..8d93fba 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 Implement `Group`-like accounts (i.e. an actor that boosts to their followers all posts that mention it). -Integrate "Ability to federate with hidden networks" see https://codeberg.org/grunfink/snac2/issues/93 - Integrate "Added handling for International Domain Names" PR https://codeberg.org/grunfink/snac2/pulls/104 Consider adding Mastodon import functionality (for following_accounts.csv and outbox.json). -Consider adding milter-like support to reject posts to mitigate spam. - Do something about Akkoma and Misskey's quoted replies (they use the `quoteUrl` field instead of `inReplyTo`). Add more CSS classes according to https://comam.es/snac/grunfink/p/1705598619.090050 @@ -311,3 +307,7 @@ Consider implementing the rejection of activities from recently-created accounts Consider discarding posts by content using string or regex to mitigate spam (2024-03-14T10:40:14+0100). Post edits should preserve the image and the image description somewhat (2024-03-22T09:57:18+0100). + +Integrate "Ability to federate with hidden networks" see https://codeberg.org/grunfink/snac2/issues/93 + +Consider adding milter-like support to reject posts to mitigate spam (discarded; 2024-04-20T22:46:35+0200). From ebf6a4bd8e97e434d1502ddead4690aca3dd6d33 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 05:46:56 +0200 Subject: [PATCH 018/120] URLs like {srv_baseurl}/{user}/admin/p/{md5} are valid. But only if {md5} is in the user's timeline. --- data.c | 14 +++++++++----- html.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/data.c b/data.c index 1e46395..2fb00eb 100644 --- a/data.c +++ b/data.c @@ -1065,14 +1065,18 @@ int timeline_touch(snac *snac) xs_str *timeline_fn_by_md5(snac *snac, const char *md5) /* get the filename of an entry by md5 from any timeline */ { - xs_str *fn = xs_fmt("%s/private/%s.json", snac->basedir, md5); + xs_str *fn = NULL; - if (mtime(fn) == 0.0) { - fn = xs_free(fn); - fn = xs_fmt("%s/public/%s.json", snac->basedir, md5); + if (xs_is_hex(md5) && strlen(md5) == 32) { + fn = xs_fmt("%s/private/%s.json", snac->basedir, md5); - if (mtime(fn) == 0.0) + if (mtime(fn) == 0.0) { fn = xs_free(fn); + fn = xs_fmt("%s/public/%s.json", snac->basedir, md5); + + if (mtime(fn) == 0.0) + fn = xs_free(fn); + } } return fn; diff --git a/html.c b/html.c index d52ef03..fdc0975 100644 --- a/html.c +++ b/html.c @@ -2586,6 +2586,25 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else + if (xs_startswith(p_path, "admin/p/")) { /** unique post by md5 **/ + if (!login(&snac, req)) { + *body = xs_dup(uid); + status = 401; + } + else { + xs *l = xs_split(p_path, "/"); + char *md5 = xs_list_get(l, -1); + + if (md5 && *md5 && timeline_here(&snac, md5)) { + xs *list = xs_list_append(xs_list_new(), md5); + + *body = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1); + *b_size = strlen(*body); + status = 200; + } + } + } + else if (strcmp(p_path, "people") == 0) { /** the list of people **/ if (!login(&snac, req)) { *body = xs_dup(uid); From 9cfce7a4bdbe934524c8d5d6ed4485f78c879e8f Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 07:23:27 +0200 Subject: [PATCH 019/120] Deleted useless recalculation of queue_retry_max. --- activitypub.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index c05e3f3..0ed764e 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2516,8 +2516,6 @@ void process_queue_item(xs_dict *q_item) if (r == 0) { /* transient error? retry */ - int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); - if (retries > queue_retry_max) srv_log(xs_fmt("shared input giving up")); else { From cf1c2b1ed82b54e5496275c2bab4c828afbb2b06 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 07:31:50 +0200 Subject: [PATCH 020/120] Backport from xs. --- xs.h | 44 ++++++++++++++++++++++++++++++++++++++++++-- xs_version.h | 2 +- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/xs.h b/xs.h index bab315a..48b0211 100644 --- a/xs.h +++ b/xs.h @@ -94,6 +94,7 @@ xs_list *xs_list_append_m(xs_list *list, const char *mem, int dsz); xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]); #define xs_list_append(list, ...) _xs_list_append(list, (const xs_val *[]){ __VA_ARGS__, NULL }) int xs_list_iter(xs_list **list, xs_val **value); +int xs_list_next(const xs_list *list, xs_val **value, int *ctxt); int xs_list_len(const xs_list *list); xs_val *xs_list_get(const xs_list *list, int num); xs_list *xs_list_del(xs_list *list, int num); @@ -752,6 +753,42 @@ int xs_list_iter(xs_list **list, xs_val **value) } +int xs_list_next(const xs_list *list, xs_val **value, int *ctxt) +/* iterates a list, with context */ +{ + if (xs_type(list) != XSTYPE_LIST) + return 0; + + int goon = 1; + + char *p = (char *)list; + + /* skip the start of the list */ + if (*ctxt == 0) + *ctxt = 1 + _XS_TYPE_SIZE; + + p += *ctxt; + + /* an element? */ + if (xs_type(p) == XSTYPE_LITEM) { + p++; + + *value = p; + + p += xs_size(*value); + } + else { + /* end of list */ + goon = 0; + } + + /* update the context */ + *ctxt = p - list; + + return goon; +} + + int xs_list_len(const xs_list *list) /* returns the number of elements in the list */ { @@ -1199,8 +1236,11 @@ double xs_number_get(const xs_number *v) { double f = 0.0; - if (v != NULL && v[0] == XSTYPE_NUMBER) + if (xs_type(v) == XSTYPE_NUMBER) f = atof(&v[1]); + else + if (xs_type(v) == XSTYPE_STRING) + f = atof(v); return f; } @@ -1211,7 +1251,7 @@ const char *xs_number_str(const xs_number *v) { const char *p = NULL; - if (v != NULL && v[0] == XSTYPE_NUMBER) + if (xs_type(v) == XSTYPE_NUMBER) p = &v[1]; return p; diff --git a/xs_version.h b/xs_version.h index f655735..f926e06 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* f712d1336ef427c3b56305364b2687578537543f 2024-04-14T19:11:53+02:00 */ +/* 0206a65508e86f66b6aa329418ddc8f6f8c1ecb2 2024-04-22T07:31:05+02:00 */ From 16a4a09e4fcc6f107649b3053747f2a2c68c0dea Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 07:53:40 +0200 Subject: [PATCH 021/120] New server.json knobs "queue_timeout" and "queue_timeout_2". --- activitypub.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index 0ed764e..94c0d30 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2371,6 +2371,7 @@ void process_queue_item(xs_dict *q_item) int p_status = xs_number_get(xs_dict_get(q_item, "p_status")); xs *payload = NULL; int p_size = 0; + int timeout = 0; if (xs_is_null(inbox) || xs_is_null(msg) || xs_is_null(keyid) || xs_is_null(seckey)) { srv_log(xs_fmt("output message error: missing fields")); @@ -2383,8 +2384,15 @@ void process_queue_item(xs_dict *q_item) } /* deliver (if previous error status was a timeout, try now longer) */ - status = send_to_inbox_raw(keyid, seckey, inbox, msg, - &payload, &p_size, p_status == 599 ? 8 : 6); + if (p_status == 599) + timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout_2", "8")); + else + timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout", "6")); + + if (timeout == 0) + timeout = 6; + + status = send_to_inbox_raw(keyid, seckey, inbox, msg, &payload, &p_size, timeout); if (payload) { if (p_size > 64) { From 6ed34913f0ad86b33dab0d11f061dbbb29e144e9 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 07:55:03 +0200 Subject: [PATCH 022/120] Added queue timeout tweaks to default server.json. --- utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils.c b/utils.c index 2b50d25..d00f513 100644 --- a/utils.c +++ b/utils.c @@ -25,6 +25,8 @@ static const char *default_srv_config = "{" "\"dbglevel\": 0," "\"queue_retry_minutes\": 2," "\"queue_retry_max\": 10," + "\"queue_timeout\": 6," + "\"queue_timeout_2\": 8," "\"cssurls\": [\"\"]," "\"max_timeline_entries\": 50," "\"timeline_purge_days\": 120," From 2d2c1bdfef027ef40767c673ce9a09090770b875 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 22 Apr 2024 08:03:33 +0200 Subject: [PATCH 023/120] Updated documentation. --- doc/snac.8 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/snac.8 b/doc/snac.8 index b25205b..7c35aeb 100644 --- a/doc/snac.8 +++ b/doc/snac.8 @@ -143,6 +143,14 @@ times the sending will be retried. The number of minutes to wait before the failed posting of a message is retried. This is not linear, but multipled by the number of retries already done. +.It Ic queue_timeout +The maximum number of seconds to wait when sending a message from the queue. +.It Ic queue_timeout_2 +The maximum number of seconds to wait when sending a message from the queue +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 max_timeline_entries This is the maximum timeline entries shown in the web interface. .It Ic timeline_purge_days From 1345409666f80bad0884dbee947fe4f558d06b1f Mon Sep 17 00:00:00 2001 From: default Date: Tue, 23 Apr 2024 19:48:14 +0200 Subject: [PATCH 024/120] Updated TODO. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 8d93fba..d00316d 100644 --- a/TODO.md +++ b/TODO.md @@ -10,6 +10,8 @@ Mastodon API: fix whatever the fuck is making the official app and Megalodon to Important: deleting a follower should do more that just delete the object, see https://codeberg.org/grunfink/snac2/issues/43#issuecomment-956721 +Editing / Updating a post does not index newly added hashtags. + ## Wishlist Implement `Group`-like accounts (i.e. an actor that boosts to their followers all posts that mention it). From be935b32c6a83de5ec39519bcb06e37edc373a2f Mon Sep 17 00:00:00 2001 From: default Date: Sat, 27 Apr 2024 08:02:47 +0200 Subject: [PATCH 025/120] mastoapi: minor tweaks. --- mastoapi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index b49fbca..fa01680 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -550,6 +550,9 @@ xs_dict *mastoapi_account(const xs_dict *actor) acct = xs_dict_append(acct, "created_at", date); } + xs *last_status_at = xs_str_utctime(0, "%Y-%m-%d"); + acct = xs_dict_append(acct, "last_status_at", last_status_at); + const char *note = xs_dict_get(actor, "summary"); if (xs_is_null(note)) note = ""; @@ -1000,7 +1003,10 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) st = xs_dict_append(st, "reblog", xs_stock(XSTYPE_NULL)); st = xs_dict_append(st, "card", xs_stock(XSTYPE_NULL)); - st = xs_dict_append(st, "language", xs_stock(XSTYPE_NULL)); + st = xs_dict_append(st, "language", "en"); + + st = xs_dict_append(st, "filtered", xs_stock(XSTYPE_LIST)); + st = xs_dict_append(st, "muted", xs_stock(XSTYPE_FALSE)); tmp = xs_dict_get(msg, "sourceContent"); if (xs_is_null(tmp)) From 6bcb248254b32f86e6f07a73ef2db632e79a01d7 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 27 Apr 2024 08:30:56 +0200 Subject: [PATCH 026/120] mastoapi: Fixed login bug. --- mastoapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index fa01680..7ffcbce 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -291,7 +291,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, *body = xs_dup(code); } else { - if (xs_str_in(redir, "?")) + if (xs_str_in(redir, "?") != -1) *body = xs_fmt("%s&code=%s", redir, code); else *body = xs_fmt("%s?code=%s", redir, code); From a3cf5d27c382524e45b602079e6a7187f438b0f9 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 27 Apr 2024 11:23:11 +0200 Subject: [PATCH 027/120] Updated TODO. --- TODO.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index d00316d..bdd7497 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,10 @@ Editing / Updating a post does not index newly added hashtags. ## Wishlist +Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md + +Track "FEP-ef61: Portable Objects" https://codeberg.org/fediverse/fep/src/branch/main/fep/ef61/fep-ef61.md + Implement `Group`-like accounts (i.e. an actor that boosts to their followers all posts that mention it). Integrate "Added handling for International Domain Names" PR https://codeberg.org/grunfink/snac2/pulls/104 @@ -22,22 +26,16 @@ Consider adding Mastodon import functionality (for following_accounts.csv and ou Do something about Akkoma and Misskey's quoted replies (they use the `quoteUrl` field instead of `inReplyTo`). -Add more CSS classes according to https://comam.es/snac/grunfink/p/1705598619.090050 - Add support for /share?text=tt&website=url (whatever it is, see https://mastodonshare.com/ for details). Add support for /authorize_interaction (whatever it is). Add a list of hashtags to drop. -Add domain/subdomain flexibility according to https://codeberg.org/grunfink/snac2/issues/3 - The 'history' pages are just monthly HTML snapshots of the local timeline. This is ok and cheap and easy, but is problematic if you e.g. intentionally delete a post because it will remain there in the history forever. If you activate local timeline purging, purged entries will remain in the history as 'ghosts', which may or may not be what the user wants. Implement bulleted lists. Mastodon is crap and won't show them, but other implementations (Friendica, Pleroma) will do. -User request: "will it be possible to click on a link and instead of opening the original instance, we'll be able only to see a list of the posts of this person here in comam?. Something like Mastodon does." - The actual storage system wastes too much disk space (lots of small files that really consume 4k of storage). Consider alternatives. ## Closed From a297b55dac6fac41136badf4b7af9c7a4f28a66f Mon Sep 17 00:00:00 2001 From: default Date: Sun, 28 Apr 2024 23:57:41 +0200 Subject: [PATCH 028/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e1528b7..021d191 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,8 @@ Posts that were liked or boosted can now be unliked and unboosted. +Outgoing message timeouts are no longer hardcoded and can be configured (see `snac(8)` for more information). + Added a header to avoid over-zealous caching in some browsers (contributed by louis77). Added support for running and federating inside hidden networks like Tor, I2P or Loki (contributed by iwojima). From 8275a5f4d8764ebc9f9f82a4db377bacfa9fbc75 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 07:43:01 +0200 Subject: [PATCH 029/120] Start of list support. --- data.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mastoapi.c | 47 ++++++++++++++++++++++++++++++++++++++--- snac.h | 2 ++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/data.c b/data.c index 2fb00eb..ab597e7 100644 --- a/data.c +++ b/data.c @@ -1729,6 +1729,68 @@ xs_list *tag_search(char *tag, int skip, int show) } +/** lists **/ + +xs_list *list_maint(snac *user, const char *list, int op) +{ + xs_list *l = NULL; + + switch (op) { + case 0: /** list of lists **/ + { + FILE *f; + xs *spec = xs_fmt("%s/list/" "*.id", user->basedir); + xs *ls = xs_glob(spec, 0, 0); + int c = 0; + char *v; + + l = xs_list_new(); + + while (xs_list_next(ls, &v, &c)) { + if ((f = fopen(v, "r")) != NULL) { + xs *title = xs_readline(f); + fclose(f); + + title = xs_strip_i(title); + xs *md5 = xs_md5_hex(title, strlen(title)); + + /* return [ list_id, list_title ] */ + l = xs_list_append(l, xs_list_append(xs_list_new(), md5, title)); + } + } + } + + break; + + case 1: /** create new list (list is the name) **/ + { + FILE *f; + xs *dir = xs_fmt("%s/list/", user->basedir); + xs *md5 = xs_md5_hex(list, strlen(list)); + + mkdirx(dir); + + xs *fn = xs_fmt("%s%s.id", dir, md5); + + if ((f = fopen(fn, "w")) != NULL) { + fprintf(f, "%s\n", list); + fclose(f); + } + } + + break; + + case 2: /** delete list (list is md5 id) **/ + break; + + case 3: /** list content (list is md5 id) **/ + break; + } + + return l; +} + + /** static data **/ static int _load_raw_file(const char *fn, xs_val **data, int *size, diff --git a/mastoapi.c b/mastoapi.c index 7ffcbce..20d6208 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1767,9 +1767,27 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, else if (strcmp(cmd, "/v1/lists") == 0) { /** **/ /* snac does not support lists */ - *body = xs_dup("[]"); - *ctype = "application/json"; - status = 200; + if (logged_in) { + xs *lol = list_maint(&snac1, NULL, 0); + xs *l = xs_list_new(); + int c = 0; + xs_list *li; + + while (xs_list_next(lol, &li, &c)) { + xs *d = xs_dict_new(); + + d = xs_dict_append(d, "id", xs_list_get(li, 0)); + d = xs_dict_append(d, "title", xs_list_get(li, 1)); + d = xs_dict_append(d, "replies_policy", "list"); + d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE)); + + l = xs_list_append(l, d); + } + + *body = xs_json_dumps(l, 4); + *ctype = "application/json"; + status = 200; + } } else if (strcmp(cmd, "/v1/scheduled_statuses") == 0) { /** **/ @@ -2631,6 +2649,29 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, else status = 401; } + else + if (strcmp(cmd, "/v1/lists") == 0) { + if (logged_in) { + const char *title = xs_dict_get(args, "title"); + + if (xs_type(title) == XSTYPE_STRING) { + /* add the list */ + list_maint(&snac, title, 1); + + xs *out = xs_dict_new(); + + out = xs_dict_append(out, "title", title); + out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list")); + out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE)); + + *body = xs_json_dumps(out, 4); + *ctype = "application/json"; + status = 200; + } + else + status = 422; + } + } /* user cleanup */ if (logged_in) diff --git a/snac.h b/snac.h index cac09a9..1cd8603 100644 --- a/snac.h +++ b/snac.h @@ -174,6 +174,8 @@ int is_hidden(snac *snac, const char *id); void tag_index(const char *id, const xs_dict *obj); xs_list *tag_search(char *tag, int skip, int show); +xs_list *list_maint(snac *user, const char *list, int op); + int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); int actor_get_refresh(snac *user, const char *actor, xs_dict **data); From 9a13e330f12aaf0fc1535e922efbf315308d252b Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 08:29:18 +0200 Subject: [PATCH 030/120] More work in lists. --- data.c | 66 +++++++++++++++++++++++++++++++++++++++++------------- mastoapi.c | 51 ++++++++++++++++++++++++++++++++--------- snac.h | 2 +- 3 files changed, 92 insertions(+), 27 deletions(-) diff --git a/data.c b/data.c index ab597e7..46d95b4 100644 --- a/data.c +++ b/data.c @@ -1731,9 +1731,9 @@ xs_list *tag_search(char *tag, int skip, int show) /** lists **/ -xs_list *list_maint(snac *user, const char *list, int op) +xs_val *list_maint(snac *user, const char *list, int op) { - xs_list *l = NULL; + xs_val *l = NULL; switch (op) { case 0: /** list of lists **/ @@ -1752,10 +1752,12 @@ xs_list *list_maint(snac *user, const char *list, int op) fclose(f); title = xs_strip_i(title); - xs *md5 = xs_md5_hex(title, strlen(title)); + + xs *v2 = xs_replace(v, ".id", ""); + xs *l2 = xs_split(v2, "/"); /* return [ list_id, list_title ] */ - l = xs_list_append(l, xs_list_append(xs_list_new(), md5, title)); + l = xs_list_append(l, xs_list_append(xs_list_new(), xs_list_get(l2, -1), title)); } } } @@ -1764,26 +1766,58 @@ xs_list *list_maint(snac *user, const char *list, int op) case 1: /** create new list (list is the name) **/ { - FILE *f; - xs *dir = xs_fmt("%s/list/", user->basedir); - xs *md5 = xs_md5_hex(list, strlen(list)); + xs *lol = list_maint(user, NULL, 0); + int c = 0; + xs_list *v; + int add = 1; - mkdirx(dir); + /* check if this list name already exists */ + while (xs_list_next(lol, &v, &c)) { + if (strcmp(xs_list_get(v, 1), list) == 0) { + add = 0; + break; + } + } - xs *fn = xs_fmt("%s%s.id", dir, md5); + if (add) { + FILE *f; + xs *dir = xs_fmt("%s/list/", user->basedir); + xs *id = xs_fmt("%010x", time(NULL)); - if ((f = fopen(fn, "w")) != NULL) { - fprintf(f, "%s\n", list); - fclose(f); + mkdirx(dir); + + xs *fn = xs_fmt("%s%s.id", dir, id); + + if ((f = fopen(fn, "w")) != NULL) { + fprintf(f, "%s\n", list); + fclose(f); + } + + l = xs_stock(XSTYPE_TRUE); + } + else + l = xs_stock(XSTYPE_FALSE); + } + + break; + + case 2: /** delete list (list is the id) **/ + { + if (xs_is_hex(list)) { + xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); + unlink(fn); + + fn = xs_replace_i(fn, ".id", ".lst"); + unlink(fn); + + fn = xs_replace_i(fn, ".list", ".idx"); + unlink(fn); } } break; - case 2: /** delete list (list is md5 id) **/ - break; - - case 3: /** list content (list is md5 id) **/ + case 3: /** list content (list is the id) **/ break; } diff --git a/mastoapi.c b/mastoapi.c index 20d6208..54d2777 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1766,7 +1766,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, } else if (strcmp(cmd, "/v1/lists") == 0) { /** **/ - /* snac does not support lists */ if (logged_in) { xs *lol = list_maint(&snac1, NULL, 0); xs *l = xs_list_new(); @@ -2656,17 +2655,22 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (xs_type(title) == XSTYPE_STRING) { /* add the list */ - list_maint(&snac, title, 1); - xs *out = xs_dict_new(); - out = xs_dict_append(out, "title", title); - out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list")); - out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE)); + if (xs_type(list_maint(&snac, title, 1)) == XSTYPE_TRUE) { + out = xs_dict_append(out, "title", title); + out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list")); + out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE)); + + status = 200; + } + else { + out = xs_dict_append(out, "error", "cannot create list"); + status = 422; + } *body = xs_json_dumps(out, 4); *ctype = "application/json"; - status = 200; } else status = 422; @@ -2691,16 +2695,43 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, (void)b_size; (void)ctype; + int status = 404; + if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; - srv_debug(1, xs_fmt("mastoapi_delete_handler %s", q_path)); + snac snac = {0}; + int logged_in = process_auth_token(&snac, req); + xs *cmd = xs_replace_n(q_path, "/api", "", 1); + if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/ // pretend we deleted it, since it doesn't exist anyway - return 200; + status = 200; } - return 0; + else + if (xs_startswith(cmd, "/v1/lists/")) { + if (logged_in) { + xs *l = xs_split(cmd, "/"); + char *id = xs_list_get(l, -1); + + if (xs_is_hex(id)) { + list_maint(&snac, id, 2); + } + + status = 200; + } + else + status = 401; + } + + /* user cleanup */ + if (logged_in) + user_free(&snac); + + srv_debug(1, xs_fmt("mastoapi_delete_handler %s %d", q_path, status)); + + return status; } diff --git a/snac.h b/snac.h index 1cd8603..f76f8e0 100644 --- a/snac.h +++ b/snac.h @@ -174,7 +174,7 @@ int is_hidden(snac *snac, const char *id); void tag_index(const char *id, const xs_dict *obj); xs_list *tag_search(char *tag, int skip, int show); -xs_list *list_maint(snac *user, const char *list, int op); +xs_val *list_maint(snac *user, const char *list, int op); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From 29fb43079761796e34209611ec93bd651b871cab Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 09:26:37 +0200 Subject: [PATCH 031/120] More work in lists. --- data.c | 38 ++++++++++++++++++- httpd.c | 2 +- mastoapi.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++----- snac.h | 6 ++- 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/data.c b/data.c index 46d95b4..ed65d32 100644 --- a/data.c +++ b/data.c @@ -1732,6 +1732,7 @@ xs_list *tag_search(char *tag, int skip, int show) /** lists **/ xs_val *list_maint(snac *user, const char *list, int op) +/* list maintenance */ { xs_val *l = NULL; @@ -1810,15 +1811,48 @@ xs_val *list_maint(snac *user, const char *list, int op) fn = xs_replace_i(fn, ".id", ".lst"); unlink(fn); - fn = xs_replace_i(fn, ".list", ".idx"); + fn = xs_replace_i(fn, ".lst", ".idx"); unlink(fn); } } break; + } + + return l; +} + + +xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op) +/* list content management */ +{ + xs_val *l = NULL; + + if (!xs_is_hex(list)) + return NULL; + + if (actor_md5 != NULL && !xs_is_hex(actor_md5)) + return NULL; + + xs *fn = xs_fmt("%s/list/%s.lst", user->basedir, list); + + switch (op) { + case 0: /** list content **/ + l = index_list(fn, XS_ALL); - case 3: /** list content (list is the id) **/ break; + + case 1: /** append actor to list **/ + if (actor_md5 != NULL) { + if (!index_in(fn, actor_md5)) + index_add_md5(fn, actor_md5); + } + + break; + + case 2: /** delete actor from list **/ + if (actor_md5 != NULL) + index_del_md5(fn, actor_md5); } return l; diff --git a/httpd.c b/httpd.c index e402e61..bda8159 100644 --- a/httpd.c +++ b/httpd.c @@ -360,7 +360,7 @@ void httpd_connection(FILE *f) #ifndef NO_MASTODON_API if (status == 0) status = mastoapi_delete_handler(req, q_path, - &body, &b_size, &ctype); + payload, p_size, &body, &b_size, &ctype); #endif } diff --git a/mastoapi.c b/mastoapi.c index 54d2777..8c41efb 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1765,7 +1765,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, status = 200; } else - if (strcmp(cmd, "/v1/lists") == 0) { /** **/ + if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/ if (logged_in) { xs *lol = list_maint(&snac1, NULL, 0); xs *l = xs_list_new(); @@ -1789,6 +1789,36 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, } } else + if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/ + if (logged_in) { + xs *l = xs_split(cmd, "/"); + char *op = xs_list_get(l, -1); + char *id = xs_list_get(l, -2); + + if (op && id && xs_is_hex(id)) { + if (strcmp(op, "accounts") == 0) { + xs *actors = list_content(&snac1, id, NULL, 0); + xs *out = xs_list_new(); + int c = 0; + char *v; + + while (xs_list_next(actors, &v, &c)) { + xs *actor = NULL; + + if (valid_status(object_get_by_md5(v, &actor))) { + xs *acct = mastoapi_account(actor); + out = xs_list_append(out, acct); + } + } + + *body = xs_json_dumps(out, 4); + *ctype = "application/json"; + status = 200; + } + } + } + } + else if (strcmp(cmd, "/v1/scheduled_statuses") == 0) { /** **/ /* snac does not schedule notes */ *body = xs_dup("[]"); @@ -2676,6 +2706,29 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, status = 422; } } + if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/ + if (logged_in) { + xs *l = xs_split(cmd, "/"); + char *op = xs_list_get(l, -1); + char *id = xs_list_get(l, -2); + + if (op && id && xs_is_hex(id)) { + if (strcmp(op, "accounts") == 0) { + xs_list *accts = xs_dict_get(args, "account_ids[]"); + int c = 0; + char *v; + + while (xs_list_next(accts, &v, &c)) { + list_content(&snac, id, v, 1); + } + + status = 200; + } + } + } + else + status = 422; + } /* user cleanup */ if (logged_in) @@ -2688,18 +2741,39 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, int mastoapi_delete_handler(const xs_dict *req, const char *q_path, - char **body, int *b_size, char **ctype) { - - (void)req; + const char *payload, int p_size, + char **body, int *b_size, char **ctype) +{ + (void)p_size; (void)body; (void)b_size; (void)ctype; - int status = 404; - if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; + int status = 404; + xs *args = NULL; + char *i_ctype = xs_dict_get(req, "content-type"); + + if (i_ctype && xs_startswith(i_ctype, "application/json")) { + if (!xs_is_null(payload)) + args = xs_json_loads(payload); + } + else if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded")) + { + // Some apps send form data instead of json so we should cater for those + if (!xs_is_null(payload)) { + xs *upl = xs_url_dec(payload); + args = xs_url_vars(upl); + } + } + else + args = xs_dup(xs_dict_get(req, "p_vars")); + + if (args == NULL) + return 400; + snac snac = {0}; int logged_in = process_auth_token(&snac, req); @@ -2713,10 +2787,26 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, if (xs_startswith(cmd, "/v1/lists/")) { if (logged_in) { xs *l = xs_split(cmd, "/"); - char *id = xs_list_get(l, -1); + char *p = xs_list_get(l, -1); - if (xs_is_hex(id)) { - list_maint(&snac, id, 2); + if (p) { + if (strcmp(p, "accounts") == 0) { + /* delete account from list */ + p = xs_list_get(l, -2); + xs_list *accts = xs_dict_get(args, "account_ids[]"); + int c = 0; + char *v; + + while (xs_list_next(accts, &v, &c)) { + list_content(&snac, p, v, 2); + } + } + else { + /* delete list */ + if (xs_is_hex(p)) { + list_maint(&snac, p, 2); + } + } } status = 200; diff --git a/snac.h b/snac.h index f76f8e0..da8b6f9 100644 --- a/snac.h +++ b/snac.h @@ -175,6 +175,7 @@ void tag_index(const char *id, const xs_dict *obj); xs_list *tag_search(char *tag, int skip, int show); xs_val *list_maint(snac *user, const char *list, int op); +xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); @@ -339,11 +340,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype); int mastoapi_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype); -int mastoapi_delete_handler(const xs_dict *req, const char *q_path, - char **body, int *b_size, char **ctype); int mastoapi_post_handler(const xs_dict *req, const char *q_path, const char *payload, int p_size, char **body, int *b_size, char **ctype); +int mastoapi_delete_handler(const xs_dict *req, const char *q_path, + const char *payload, int p_size, + char **body, int *b_size, char **ctype); int mastoapi_put_handler(const xs_dict *req, const char *q_path, const char *payload, int p_size, char **body, int *b_size, char **ctype); From 7f322302e5b5cf0345de110afe78767bfb03ae51 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 09:50:22 +0200 Subject: [PATCH 032/120] New function list_distribute(). --- data.c | 28 ++++++++++++++++++++++++++++ snac.h | 1 + 2 files changed, 29 insertions(+) diff --git a/data.c b/data.c index ed65d32..07b38ec 100644 --- a/data.c +++ b/data.c @@ -1154,6 +1154,8 @@ int timeline_add(snac *snac, const char *id, const xs_dict *o_msg) tag_index(id, o_msg); + list_distribute(snac, o_msg); + snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); return ret; @@ -1859,6 +1861,32 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op } +void list_distribute(snac *user, const xs_dict *post) +/* distributes the post to all appropriate lists */ +{ + char *atto = get_atto(post); + char *id = xs_dict_get(post, "id"); + + if (xs_type(atto) == XSTYPE_STRING && xs_type(id) == XSTYPE_STRING) { + xs *a_md5 = xs_md5_hex(atto, strlen(atto)); + xs *i_md5 = xs_md5_hex(id, strlen(id)); + xs *spec = xs_fmt("%s/list/" "*.lst", user->basedir); + xs *ls = xs_glob(spec, 0, 0); + int c = 0; + char *v; + + while (xs_list_next(ls, &v, &c)) { + /* is the actor in this list? */ + if (index_in_md5(v, a_md5)) { + /* it is; add post md5 to its timeline */ + xs *idx = xs_replace(v, ".lst", ".idx"); + index_add(idx, i_md5); + } + } + } +} + + /** static data **/ static int _load_raw_file(const char *fn, xs_val **data, int *size, diff --git a/snac.h b/snac.h index da8b6f9..b478c7e 100644 --- a/snac.h +++ b/snac.h @@ -176,6 +176,7 @@ xs_list *tag_search(char *tag, int skip, int show); xs_val *list_maint(snac *user, const char *list, int op); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); +void list_distribute(snac *user, const xs_dict *post); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From f1221808a858b0d205f3acdb028d58d362ef22e6 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 10:15:04 +0200 Subject: [PATCH 033/120] More work in lists. --- data.c | 13 +++++++++- mastoapi.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 07b38ec..be8edaa 100644 --- a/data.c +++ b/data.c @@ -1855,6 +1855,15 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op case 2: /** delete actor from list **/ if (actor_md5 != NULL) index_del_md5(fn, actor_md5); + + break; + + case 3: /** list timeline **/ + fn = xs_replace_i(fn, ".lst", ".idx"); + + l = index_list_desc(fn, 0, 2048); + + break; } return l; @@ -1880,7 +1889,9 @@ void list_distribute(snac *user, const xs_dict *post) if (index_in_md5(v, a_md5)) { /* it is; add post md5 to its timeline */ xs *idx = xs_replace(v, ".lst", ".idx"); - index_add(idx, i_md5); + index_add_md5(idx, i_md5); + + snac_debug(user, 1, xs_fmt("listed post %s in %s", id, idx)); } } } diff --git a/mastoapi.c b/mastoapi.c index 8c41efb..6316951 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1647,6 +1647,77 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, status = 200; } else + if (xs_startswith(cmd, "/v1/timelines/list/")) { /** **/ + /* get the list id */ + if (logged_in) { + xs *l = xs_split(cmd, "/"); + char *list = xs_list_get(l, -1); + + xs *timeline = list_content(&snac1, list, NULL, 3); + xs *out = xs_list_new(); + int c = 0; + char *md5; + + while (xs_list_next(timeline, &md5, &c)) { + xs *msg = NULL; + + /* get the entry */ + if (!valid_status(timeline_get_by_md5(&snac1, md5, &msg))) + continue; + + /* discard non-Notes */ + const char *id = xs_dict_get(msg, "id"); + const char *type = xs_dict_get(msg, "type"); + if (!xs_match(type, "Note|Question|Page|Article|Video")) + continue; + + const char *from = NULL; + if (strcmp(type, "Page") == 0) + from = xs_dict_get(msg, "audience"); + + if (from == NULL) + from = get_atto(msg); + + if (from == NULL) + continue; + + /* is this message from a person we don't follow? */ + if (strcmp(from, snac1.actor) && !following_check(&snac1, from)) { + /* discard if it was not boosted */ + xs *idx = object_announces(id); + + if (xs_list_len(idx) == 0) + continue; + } + + /* discard notes from muted morons */ + if (is_muted(&snac1, from)) + continue; + + /* discard hidden notes */ + if (is_hidden(&snac1, id)) + continue; + + /* if it has a name and it's not a Page or a Video, + it's a poll vote, so discard it */ + if (!xs_is_null(xs_dict_get(msg, "name")) && !xs_match(type, "Page|Video")) + continue; + + /* convert the Note into a Mastodon status */ + xs *st = mastoapi_status(&snac1, msg); + + if (st != NULL) + out = xs_list_append(out, st); + } + + *body = xs_json_dumps(out, 4); + *ctype = "application/json"; + status = 200; + } + else + status = 421; + } + else if (strcmp(cmd, "/v1/conversations") == 0) { /** **/ /* TBD */ *body = xs_dup("[]"); From da18efd288bc8e69a0f755e576e3ba14dfedbcb3 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 29 Apr 2024 10:48:48 +0200 Subject: [PATCH 034/120] More tweaks to mastoapi lists. --- mastoapi.c | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 6316951..0cdb4bf 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1863,22 +1863,52 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/ if (logged_in) { xs *l = xs_split(cmd, "/"); - char *op = xs_list_get(l, -1); - char *id = xs_list_get(l, -2); + char *p = xs_list_get(l, -1); - if (op && id && xs_is_hex(id)) { - if (strcmp(op, "accounts") == 0) { - xs *actors = list_content(&snac1, id, NULL, 0); + if (p) { + if (strcmp(p, "accounts") == 0) { + p = xs_list_get(l, -2); + + if (p && xs_is_hex(p)) { + xs *actors = list_content(&snac1, p, NULL, 0); + xs *out = xs_list_new(); + int c = 0; + char *v; + + while (xs_list_next(actors, &v, &c)) { + xs *actor = NULL; + + if (valid_status(object_get_by_md5(v, &actor))) { + xs *acct = mastoapi_account(actor); + out = xs_list_append(out, acct); + } + } + + *body = xs_json_dumps(out, 4); + *ctype = "application/json"; + status = 200; + } + } + else + if (xs_is_hex(p)) { xs *out = xs_list_new(); + xs *lol = list_maint(&snac1, NULL, 0); int c = 0; - char *v; + xs_list *v; - while (xs_list_next(actors, &v, &c)) { - xs *actor = NULL; + while (xs_list_next(lol, &v, &c)) { + char *id = xs_list_get(v, 0); - if (valid_status(object_get_by_md5(v, &actor))) { - xs *acct = mastoapi_account(actor); - out = xs_list_append(out, acct); + if (id && strcmp(id, p) == 0) { + xs *d = xs_dict_new(); + + d = xs_dict_append(d, "id", p); + d = xs_dict_append(d, "title", xs_list_get(v, 1)); + d = xs_dict_append(d, "replies_policy", "list"); + d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE)); + + out = xs_list_append(out, d); + break; } } From 96830967e15bd6b1a89686c8c729b096ae6c4550 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 30 Apr 2024 09:14:03 +0200 Subject: [PATCH 035/120] Also purge lists. --- data.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/data.c b/data.c index be8edaa..0d8aa8b 100644 --- a/data.c +++ b/data.c @@ -3012,6 +3012,19 @@ void purge_user(snac *snac) srv_debug(1, xs_fmt("purge: %s %d", idx, gc)); } + /* purge lists */ + { + xs *spec = xs_fmt("%s/list/" "*.idx", snac->basedir); + xs *lol = xs_glob(spec, 0, 0); + int c = 0; + char *v; + + while (xs_list_next(lol, &v, &c)) { + int gc = index_gc(v); + srv_debug(1, xs_fmt("purge: %s %d", v, gc)); + } + } + /* unrelated to purging, but it's a janitorial process, so what the hell */ verify_links(snac); } From 4e23570c215aaa2bc2611056b57d90e41621a001 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 30 Apr 2024 09:27:33 +0200 Subject: [PATCH 036/120] Added more internal integrity checks. index_add_md5() barfs if the md5 is not valid. --- data.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 0d8aa8b..82d0acc 100644 --- a/data.c +++ b/data.c @@ -340,6 +340,12 @@ double f_ctime(const char *fn) } +int is_md5_hex(const char *md5) +{ + return xs_is_hex(md5) && strlen(md5) == 32; +} + + /** database 2.1+ **/ /** indexes **/ @@ -351,6 +357,11 @@ int index_add_md5(const char *fn, const char *md5) int status = 201; /* Created */ FILE *f; + if (!is_md5_hex(md5)) { + srv_log(xs_fmt("index_add_md5: bad md5 %s %s", fn, md5)); + return 400; + } + pthread_mutex_lock(&data_mutex); if ((f = fopen(fn, "a")) != NULL) { @@ -606,7 +617,7 @@ static xs_str *_object_fn_by_md5(const char *md5, const char *func) if (md5[0] == '-') ok = 0; else - if (!xs_is_hex(md5) || strlen(md5) != 32) { + if (!is_md5_hex(md5)) { srv_log(xs_fmt("_object_fn_by_md5() [from %s()]: bad md5 '%s'", func, md5)); ok = 0; } From 6a77c634ad9938744bae6d718b209557fa37018c Mon Sep 17 00:00:00 2001 From: default Date: Tue, 30 Apr 2024 19:41:08 +0200 Subject: [PATCH 037/120] Also add announces to lists. --- activitypub.c | 3 +++ data.c | 15 +++++++++------ snac.h | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/activitypub.c b/activitypub.c index 94c0d30..2691a6a 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2099,6 +2099,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", actor, object)); + /* distribute the post with the actor as 'proxy' */ + list_distribute(snac, actor, a_msg); + do_notify = 1; } else diff --git a/data.c b/data.c index 82d0acc..db544f4 100644 --- a/data.c +++ b/data.c @@ -1165,7 +1165,7 @@ int timeline_add(snac *snac, const char *id, const xs_dict *o_msg) tag_index(id, o_msg); - list_distribute(snac, o_msg); + list_distribute(snac, NULL, o_msg); snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); @@ -1881,14 +1881,17 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op } -void list_distribute(snac *user, const xs_dict *post) +void list_distribute(snac *user, const char *who, const xs_dict *post) /* distributes the post to all appropriate lists */ { - char *atto = get_atto(post); - char *id = xs_dict_get(post, "id"); + char *id = xs_dict_get(post, "id"); - if (xs_type(atto) == XSTYPE_STRING && xs_type(id) == XSTYPE_STRING) { - xs *a_md5 = xs_md5_hex(atto, strlen(atto)); + /* if who is not set, use the attributedTo in the message */ + if (xs_is_null(who)) + who = get_atto(post); + + if (xs_type(who) == XSTYPE_STRING && xs_type(id) == XSTYPE_STRING) { + xs *a_md5 = xs_md5_hex(who, strlen(who)); xs *i_md5 = xs_md5_hex(id, strlen(id)); xs *spec = xs_fmt("%s/list/" "*.lst", user->basedir); xs *ls = xs_glob(spec, 0, 0); diff --git a/snac.h b/snac.h index b478c7e..74e58cd 100644 --- a/snac.h +++ b/snac.h @@ -176,7 +176,7 @@ xs_list *tag_search(char *tag, int skip, int show); xs_val *list_maint(snac *user, const char *list, int op); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); -void list_distribute(snac *user, const xs_dict *post); +void list_distribute(snac *user, const char *who, const xs_dict *post); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From d0d7c2ca029e7d10ab7c7a9a651dff38db3ed3c0 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 1 May 2024 08:01:50 +0200 Subject: [PATCH 038/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 021d191..5fdd27d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Posts that were liked or boosted can now be unliked and unboosted. Outgoing message timeouts are no longer hardcoded and can be configured (see `snac(8)` for more information). +Mastodon API: Added support for lists. + Added a header to avoid over-zealous caching in some browsers (contributed by louis77). Added support for running and federating inside hidden networks like Tor, I2P or Loki (contributed by iwojima). From 528f6463b2347fb5da3f41f8aac37c8c9168ea63 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 2 May 2024 07:25:22 +0200 Subject: [PATCH 039/120] Fixed confusion in Undo + Follow processing. --- activitypub.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/activitypub.c b/activitypub.c index 2691a6a..a8a7db7 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1947,12 +1947,16 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) utype = "Follow"; if (strcmp(utype, "Follow") == 0) { /** **/ - if (valid_status(follower_del(snac, actor))) { - snac_log(snac, xs_fmt("no longer following us %s", actor)); - do_notify = 1; + if (id && strcmp(id, snac->actor) != 0) + snac_debug(snac, 1, xs_fmt("Undo + Follow from %s not for us (%s)", actor, id)); + else { + if (valid_status(follower_del(snac, actor))) { + snac_log(snac, xs_fmt("no longer following us %s", actor)); + do_notify = 1; + } + else + snac_log(snac, xs_fmt("error deleting follower %s", actor)); } - else - snac_log(snac, xs_fmt("error deleting follower %s", actor)); } else if (strcmp(utype, "Like") == 0) { /** **/ From 82a9005ee398e5d8dea0d94dca98b2c685874449 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 2 May 2024 07:34:31 +0200 Subject: [PATCH 040/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5fdd27d..6455f5f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Posts that were liked or boosted can now be unliked and unboosted. Outgoing message timeouts are no longer hardcoded and can be configured (see `snac(8)` for more information). +Fixed a bug that caused some spurious unfollows under special conditions (with shared inboxes enabled and users from the same instance that follow each other, the internal message distributor was confused). + Mastodon API: Added support for lists. Added a header to avoid over-zealous caching in some browsers (contributed by louis77). From 48175857332080a19b2e1d9e785421ae234dbdb3 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 2 May 2024 07:41:01 +0200 Subject: [PATCH 041/120] 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 6455f5f..0473ece 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,7 +6,7 @@ Posts that were liked or boosted can now be unliked and unboosted. Outgoing message timeouts are no longer hardcoded and can be configured (see `snac(8)` for more information). -Fixed a bug that caused some spurious unfollows under special conditions (with shared inboxes enabled and users from the same instance that follow each other, the internal message distributor was confused). +Fixed a bug that caused some incorrect unfollows under special conditions (with shared inboxes enabled and users from the same instance that follow each other, the internal message distributor was confused). Mastodon API: Added support for lists. From 20bc82630032a6b8cdf9092651b7c50d7c581eee Mon Sep 17 00:00:00 2001 From: default Date: Fri, 3 May 2024 00:23:02 +0200 Subject: [PATCH 042/120] Backport from xs. --- xs.h | 48 ++++++------------------------------------------ xs_version.h | 2 +- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/xs.h b/xs.h index 48b0211..ad72207 100644 --- a/xs.h +++ b/xs.h @@ -110,11 +110,8 @@ xs_list *xs_split_n(const char *str, const char *sep, int times); xs_list *xs_list_cat(xs_list *l1, const xs_list *l2); xs_dict *xs_dict_new(void); -xs_dict *xs_dict_append_m(xs_dict *dict, const xs_str *key, const xs_val *mem, int dsz); -#define xs_dict_append(dict, key, data) xs_dict_append_m(dict, key, data, xs_size(data)) -xs_dict *xs_dict_prepend_m(xs_dict *dict, const xs_str *key, const xs_val *mem, int dsz); -#define xs_dict_prepend(dict, key, data) xs_dict_prepend_m(dict, key, data, xs_size(data)) -int xs_dict_iter(xs_dict **dict, xs_str **key, xs_val **value); +xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value); +xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value); int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt); xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def); #define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL) @@ -1042,50 +1039,17 @@ xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key, } -xs_dict *xs_dict_append_m(xs_dict *dict, const xs_str *key, const xs_val *mem, int dsz) +xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) /* appends a memory block to the dict */ { - return _xs_dict_write_ditem(dict, xs_size(dict) - 1, key, mem, dsz); + return _xs_dict_write_ditem(dict, xs_size(dict) - 1, key, value, xs_size(value)); } -xs_dict *xs_dict_prepend_m(xs_dict *dict, const xs_str *key, const xs_val *mem, int dsz) +xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) /* prepends a memory block to the dict */ { - return _xs_dict_write_ditem(dict, 4, key, mem, dsz); -} - - -int xs_dict_iter(xs_dict **dict, xs_str **key, xs_val **value) -/* iterates a dict value */ -{ - int goon = 1; - - xs_val *p = *dict; - - /* skip the start of the list */ - if (xs_type(p) == XSTYPE_DICT) - p += 1 + _XS_TYPE_SIZE; - - /* an element? */ - if (xs_type(p) == XSTYPE_DITEM) { - p++; - - *key = p; - p += xs_size(*key); - - *value = p; - p += xs_size(*value); - } - else { - /* end of list */ - goon = 0; - } - - /* store back the pointer */ - *dict = p; - - return goon; + return _xs_dict_write_ditem(dict, 4, key, value, xs_size(value)); } diff --git a/xs_version.h b/xs_version.h index f926e06..a672ef4 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 0206a65508e86f66b6aa329418ddc8f6f8c1ecb2 2024-04-22T07:31:05+02:00 */ +/* f3818ad611f09313008a2102a5e543c232e1d824 2024-05-02T23:45:38+02:00 */ From 2f499e9421bd67299d4fc1981f350f80efc2772b Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 01:06:34 +0200 Subject: [PATCH 043/120] Fixed Pleroma polls. --- html.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/html.c b/html.c index fdc0975..fa8253f 100644 --- a/html.c +++ b/html.c @@ -1653,20 +1653,27 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_list *p; xs_dict *v; int closed = 0; + char *f_closed = NULL; xs_html *poll = xs_html_tag("div", NULL); if (read_only) closed = 1; /* non-identified page; show as closed */ else - if (xs_dict_get(msg, "closed")) - closed = 2; - else if (user && xs_startswith(id, user->actor)) closed = 1; /* we questioned; closed for us */ else if (user && was_question_voted(user, id)) closed = 1; /* we already voted; closed for us */ + else + if ((f_closed = xs_dict_get(msg, "closed")) != NULL) { + /* it has a closed date... but is it in the past? */ + time_t t0 = time(NULL); + time_t t1 = xs_parse_iso_date(f_closed, 0); + + if (t1 < t0) + closed = 2; + } /* get the appropriate list of options */ p = oo != NULL ? oo : ao; @@ -1756,6 +1763,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, else { /* show when the poll closes */ char *end_time = xs_dict_get(msg, "endTime"); + + /* Pleroma does not have an endTime field; + it has a closed time in the future */ + if (xs_is_null(end_time)) + end_time = xs_dict_get(msg, "closed"); + if (!xs_is_null(end_time)) { time_t t0 = time(NULL); time_t t1 = xs_parse_iso_date(end_time, 0); From 7ea57485c70e9a55045f14bd81d7aea73fff4fb1 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 01:21:07 +0200 Subject: [PATCH 044/120] More Pleroma poll tweaks. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index fa8253f..45c665c 100644 --- a/html.c +++ b/html.c @@ -1665,7 +1665,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, else if (user && was_question_voted(user, id)) closed = 1; /* we already voted; closed for us */ - else + if ((f_closed = xs_dict_get(msg, "closed")) != NULL) { /* it has a closed date... but is it in the past? */ time_t t0 = time(NULL); From fc76ae4e9e721a1ec5fbcf3b509d5a47cf48cd61 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 01:38:22 +0200 Subject: [PATCH 045/120] New function enqueue_object_request(). --- activitypub.c | 16 ++++++++++++++++ data.c | 15 +++++++++++++++ main.c | 6 ++++++ snac.h | 1 + 4 files changed, 38 insertions(+) diff --git a/activitypub.c b/activitypub.c index a8a7db7..bb919ed 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2313,6 +2313,22 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) timeline_request_replies(snac, id); } else + if (strcmp(type, "object_request") == 0) { + const char *id = xs_dict_get(q_item, "message"); + + if (!xs_is_null(id)) { + int status; + xs *data = NULL; + + status = activitypub_request(snac, id, &data); + + if (valid_status(status)) + object_add_ow(id, data); + + snac_debug(snac, 1, xs_fmt("object_request %s %d", id, status)); + } + } + else if (strcmp(type, "verify_links") == 0) { verify_links(snac); } diff --git a/data.c b/data.c index db544f4..4d753a9 100644 --- a/data.c +++ b/data.c @@ -2651,6 +2651,21 @@ void enqueue_close_question(snac *user, const char *id, int end_secs) } +void enqueue_object_request(snac *user, const char *id, int forward_secs) +/* enqueues the request of an object in the future */ +{ + xs *qmsg = _new_qmsg("object_request", id, 0); + xs *ntid = tid(forward_secs); + xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); + + qmsg = xs_dict_set(qmsg, "ntid", ntid); + + qmsg = _enqueue_put(fn, qmsg); + + snac_debug(user, 0, xs_fmt("enqueue_object_request %s", id)); +} + + void enqueue_verify_links(snac *user) /* enqueues a link verification */ { diff --git a/main.c b/main.c index 06cae78..6a38412 100644 --- a/main.c +++ b/main.c @@ -458,6 +458,12 @@ int main(int argc, char *argv[]) return 0; } + if (strcmp(cmd, "request2") == 0) { /** **/ + enqueue_object_request(&snac, url, 2); + + return 0; + } + if (strcmp(cmd, "actor") == 0) { /** **/ int status; xs *data = NULL; diff --git a/snac.h b/snac.h index 74e58cd..721d088 100644 --- a/snac.h +++ b/snac.h @@ -226,6 +226,7 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id); void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token); void enqueue_message(snac *snac, const xs_dict *msg); void enqueue_close_question(snac *user, const char *id, int end_secs); +void enqueue_object_request(snac *user, const char *id, int forward_secs); void enqueue_verify_links(snac *user); void enqueue_actor_refresh(snac *user, const char *actor); void enqueue_request_replies(snac *user, const char *id); From be9cf2f5550f68ef4e86b547d7247a0bd498521d Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 01:58:27 +0200 Subject: [PATCH 046/120] Enqueue an object request for closed polls. It seems that Pleroma does not send poll updates on close (or never). --- data.c | 2 +- html.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 4d753a9..b9c1141 100644 --- a/data.c +++ b/data.c @@ -2662,7 +2662,7 @@ void enqueue_object_request(snac *user, const char *id, int forward_secs) qmsg = _enqueue_put(fn, qmsg); - snac_debug(user, 0, xs_fmt("enqueue_object_request %s", id)); + snac_debug(user, 0, xs_fmt("enqueue_object_request %s %d", id, forward_secs)); } diff --git a/html.c b/html.c index 45c665c..9de1cca 100644 --- a/html.c +++ b/html.c @@ -3300,6 +3300,25 @@ int html_post_handler(const xs_dict *req, const char *q_path, timeline_add(&snac, xs_dict_get(msg, "id"), msg); } + { + /* get the poll object */ + xs *poll = NULL; + + if (valid_status(object_get(irt, &poll))) { + char *date = xs_dict_get(poll, "endTime"); + if (xs_is_null(date)) + date = xs_dict_get(poll, "closed"); + + if (!xs_is_null(date)) { + time_t t = xs_parse_iso_date(date, 0) - time(NULL); + + /* request the poll when it's closed; + Pleroma does not send and update when the poll closes */ + enqueue_object_request(&snac, irt, t + 2); + } + } + } + status = 303; } From 79df266bfe68d9ab74378780aaf99546300a836d Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 08:23:12 +0200 Subject: [PATCH 047/120] Added some more logging. --- activitypub.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/activitypub.c b/activitypub.c index bb919ed..301fd4c 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1988,8 +1988,15 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) if (xs_match(utype, "Note|Article")) { /** **/ char *id = xs_dict_get(object, "id"); char *in_reply_to = xs_dict_get(object, "inReplyTo"); + char *atto = get_atto(object); xs *wrk = NULL; + if (xs_is_null(id)) + snac_log(snac, xs_fmt("malformed message: no 'id' field")); + else + if (xs_is_null(atto)) + snac_log(snac, xs_fmt("malformed message: no 'attributedTo' field")); + else if (!xs_is_null(in_reply_to) && is_hidden(snac, in_reply_to)) { snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to)); } @@ -1999,6 +2006,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) return 1; } + if (strcmp(actor, atto) != 0) + snac_log(snac, xs_fmt("SUSPICIOUS: actor != atto (%s != %s)", actor, atto)); + timeline_request(snac, &in_reply_to, &wrk, 0); if (timeline_add(snac, id, object)) { From b4293f3c38f5395d31ef3aef78a46e4c9148ea0a Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 09:30:24 +0200 Subject: [PATCH 048/120] More logging. --- webfinger.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webfinger.c b/webfinger.c index 5b13d2f..50600b0 100644 --- a/webfinger.c +++ b/webfinger.c @@ -219,5 +219,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, else status = 404; + srv_debug(1, xs_fmt("webfinger_get_handler: resource=%s", resource)); + return status; } From aa95a45a7f0e5d98f673b782821163081a491f43 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 09:34:14 +0200 Subject: [PATCH 049/120] More logging. --- webfinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfinger.c b/webfinger.c index 50600b0..7255ae2 100644 --- a/webfinger.c +++ b/webfinger.c @@ -219,7 +219,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, else status = 404; - srv_debug(1, xs_fmt("webfinger_get_handler: resource=%s", resource)); + srv_debug(1, xs_fmt("webfinger_get_handler resource=%s %d", resource, status)); return status; } From 580299bca54a0fe1d3d355e83e6244d92ff3e326 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 09:42:13 +0200 Subject: [PATCH 050/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0473ece..acb697d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,6 +14,8 @@ Added a header to avoid over-zealous caching in some browsers (contributed by lo Added support for running and federating inside hidden networks like Tor, I2P or Loki (contributed by iwojima). +Fixed an error processing polls coming from Pleroma instances. + ## 2.51 Support for custom Emojis has been added; they are no longer hardcoded, but read from the `emojis.json` file at the server base directory. Also, they are no longer limited to string substitutions, but images as external URLs are also supported (see `snac(8)` for more information). From 45357b8e6e36f973fc09c0a82a633e2d08b34eda Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 11:00:29 +0200 Subject: [PATCH 051/120] New function timeline_to_rss(). --- html.c | 111 ++++++++++++++++++++++++++++++++------------------------- snac.h | 1 + 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/html.c b/html.c index 9de1cca..fe070c1 100644 --- a/html.c +++ b/html.c @@ -2728,55 +2728,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs_dict_get(srv_config, "host")); xs *rss_link = xs_fmt("%s.rss", snac.actor); - xs_html *rss = xs_html_tag("rss", - xs_html_attr("version", "0.91")); - - xs_html *channel = xs_html_tag("channel", - xs_html_tag("title", - xs_html_text(rss_title)), - xs_html_tag("language", - xs_html_text("en")), - xs_html_tag("link", - xs_html_text(rss_link)), - xs_html_tag("description", - xs_html_text(bio))); - - xs_html_add(rss, channel); - - xs_list *p = elems; - char *v; - - while (xs_list_iter(&p, &v)) { - xs *msg = NULL; - - if (!valid_status(timeline_get_by_md5(&snac, v, &msg))) - continue; - - char *id = xs_dict_get(msg, "id"); - char *content = xs_dict_get(msg, "content"); - - if (!xs_startswith(id, snac.actor)) - continue; - - /* create a title with the first line of the content */ - xs *es_title = xs_replace(content, "
", "\n"); - xs *title = xs_str_new(NULL); - int i; - - for (i = 0; es_title[i] && es_title[i] != '\n' && es_title[i] != '&' && i < 50; i++) - title = xs_append_m(title, &es_title[i], 1); - - xs_html_add(channel, - xs_html_tag("item", - xs_html_tag("title", - xs_html_text(title)), - xs_html_tag("link", - xs_html_text(id)), - xs_html_tag("description", - xs_html_text(content)))); - } - - *body = xs_html_render_s(rss, "\n"); + *body = timeline_to_rss(&snac, elems, rss_title, rss_link, bio); *b_size = strlen(*body); *ctype = "application/rss+xml; charset=utf-8"; status = 200; @@ -3336,3 +3288,64 @@ int html_post_handler(const xs_dict *req, const char *q_path, return status; } + + +xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char *link, char *desc) +/* converts a timeline to rss */ +{ + xs_html *rss = xs_html_tag("rss", + xs_html_attr("version", "0.91")); + + xs_html *channel = xs_html_tag("channel", + xs_html_tag("title", + xs_html_text(title)), + xs_html_tag("language", + xs_html_text("en")), + xs_html_tag("link", + xs_html_text(link)), + xs_html_tag("description", + xs_html_text(desc))); + + xs_html_add(rss, channel); + + int c = 0; + char *v; + + while (xs_list_next(timeline, &v, &c)) { + xs *msg = NULL; + + if (user) { + if (!valid_status(timeline_get_by_md5(user, v, &msg))) + continue; + } + else { + if (!valid_status(object_get_by_md5(v, &msg))) + continue; + } + + char *id = xs_dict_get(msg, "id"); + char *content = xs_dict_get(msg, "content"); + + if (user && !xs_startswith(id, user->actor)) + continue; + + /* create a title with the first line of the content */ + xs *es_title = xs_replace(content, "
", "\n"); + xs *title = xs_str_new(NULL); + int i; + + for (i = 0; es_title[i] && es_title[i] != '\n' && es_title[i] != '&' && i < 50; i++) + title = xs_append_m(title, &es_title[i], 1); + + xs_html_add(channel, + xs_html_tag("item", + xs_html_tag("title", + xs_html_text(title)), + xs_html_tag("link", + xs_html_text(id)), + xs_html_tag("description", + xs_html_text(content)))); + } + + return xs_html_render_s(rss, "\n"); +} diff --git a/snac.h b/snac.h index 721d088..ae6e975 100644 --- a/snac.h +++ b/snac.h @@ -324,6 +324,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int html_post_handler(const xs_dict *req, const char *q_path, char *payload, int p_size, char **body, int *b_size, char **ctype); +xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char *link, char *desc); int snac_init(const char *_basedir); int adduser(const char *uid); From ddd2af73bd2b61b13e3b7d3928184d34d4729abd Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 11:11:32 +0200 Subject: [PATCH 052/120] Search by tag returns RSS if the appropriate header is defined. --- httpd.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/httpd.c b/httpd.c index bda8159..30367c8 100644 --- a/httpd.c +++ b/httpd.c @@ -169,8 +169,6 @@ int server_get_handler(xs_dict *req, const char *q_path, { int status = 0; - (void)req; - /* is it the server root? */ if (*q_path == '\0') { xs_dict *q_vars = xs_dict_get(req, "q_vars"); @@ -195,7 +193,15 @@ int server_get_handler(xs_dict *req, const char *q_path, more = 1; } - *body = html_timeline(NULL, tl, 0, skip, show, more, t, NULL, 0); + char *accept = xs_dict_get(req, "accept"); + if (!xs_is_null(accept) && strcmp(accept, "application/rss+xml") == 0) { + xs *link = xs_fmt("%s/?t=%s", srv_baseurl, t); + + *body = timeline_to_rss(NULL, tl, link, link, link); + *ctype = "application/rss+xml; charset=utf-8"; + } + else + *body = html_timeline(NULL, tl, 0, skip, show, more, t, NULL, 0); } else if (xs_type(xs_dict_get(srv_config, "show_instance_timeline")) == XSTYPE_TRUE) { From c31a4ae73e9759cebda2a9cf8a34b352dc246314 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 5 May 2024 11:27:24 +0200 Subject: [PATCH 053/120] Minor RSS title beautifying. --- html.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/html.c b/html.c index fe070c1..ee5c2cd 100644 --- a/html.c +++ b/html.c @@ -3330,12 +3330,19 @@ xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char * continue; /* create a title with the first line of the content */ - xs *es_title = xs_replace(content, "
", "\n"); - xs *title = xs_str_new(NULL); + xs *title = xs_replace(content, "
", "\n"); + title = xs_regex_replace_i(title, "<[^>]+>", " "); + title = xs_regex_replace_i(title, "&[^;]+;", " "); int i; - for (i = 0; es_title[i] && es_title[i] != '\n' && es_title[i] != '&' && i < 50; i++) - title = xs_append_m(title, &es_title[i], 1); + for (i = 0; title[i] && title[i] != '\n' && i < 50; i++); + + if (title[i] != '\0') { + title[i] = '\0'; + title = xs_str_cat(title, "..."); + } + + title = xs_strip_i(title); xs_html_add(channel, xs_html_tag("item", From c884f7081ab679014d05ebb6ad6add2315584e84 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 6 May 2024 05:54:46 +0200 Subject: [PATCH 054/120] mastoapi: also fix polls from Pleroma. --- mastoapi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 0cdb4bf..e435ce9 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -716,10 +716,23 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg) xs *options = xs_list_new(); poll = xs_dict_append(poll, "id", mid); - xs *fd = mastoapi_date(xs_dict_get(msg, "endTime")); + char *date = xs_dict_get(msg, "endTime"); + if (date == NULL) + date = xs_dict_get(msg, "closed"); + if (date == NULL) + return NULL; + + xs *fd = mastoapi_date(date); poll = xs_dict_append(poll, "expires_at", fd); + + date = xs_dict_get(msg, "closed"); + time_t t = 0; + + if (date != NULL) + t = xs_parse_iso_date(date, 0); + poll = xs_dict_append(poll, "expired", - xs_dict_get(msg, "closed") != NULL ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE)); + t < time(NULL) ? xs_stock(XSTYPE_FALSE) : xs_stock(XSTYPE_TRUE)); if ((opts = xs_dict_get(msg, "oneOf")) != NULL) poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_FALSE)); From f67c4cf7d95739fb5cfa7e6644565ba9fe4c925c Mon Sep 17 00:00:00 2001 From: default Date: Mon, 6 May 2024 06:40:07 +0200 Subject: [PATCH 055/120] More logging! --- mastoapi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mastoapi.c b/mastoapi.c index e435ce9..e9b3993 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1467,6 +1467,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (limit == 0) limit = 20; + snac_debug(&snac1, 1, + xs_fmt("/v1/timelines/home: max_id=%s since_id=%s min_id=%s limit=%s", + max_id, since_id, min_id, limit_s)); + xs *timeline = timeline_simple_list(&snac1, "private", 0, 2048); xs *out = xs_list_new(); From 7df69f8f1288290a8b948f33ac2368aa456b999e Mon Sep 17 00:00:00 2001 From: default Date: Mon, 6 May 2024 09:04:46 +0200 Subject: [PATCH 056/120] Logging, logging, logging everywhere. --- mastoapi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index e9b3993..1f7f0b2 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1467,9 +1467,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (limit == 0) limit = 20; - snac_debug(&snac1, 1, - xs_fmt("/v1/timelines/home: max_id=%s since_id=%s min_id=%s limit=%s", - max_id, since_id, min_id, limit_s)); + xs *ja = xs_json_dumps(args, 0); + snac_debug(&snac1, 1, xs_fmt("/v1/timelines/home: args='%s'", ja)); xs *timeline = timeline_simple_list(&snac1, "private", 0, 2048); From c5ae10af862d3c76c79e848fc4dbe69f80cfc214 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 10:24:38 +0200 Subject: [PATCH 057/120] Version 2.52 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index ae6e975..481a1ae 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ -#define VERSION "2.52-dev" +#define VERSION "2.52" #define USER_AGENT "snac/" VERSION From 6b1721c977294ee28f54150579c36514aa3ee62a Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 19:40:28 +0200 Subject: [PATCH 058/120] List timelines can now be (manually) navigated from the web UI. URLs are {srv_baseurl}/{user}/list/{list_id} (you must know the list id). --- data.c | 24 +++++++++++++++++++----- html.c | 23 +++++++++++++++++++++++ mastoapi.c | 2 +- snac.h | 1 + 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/data.c b/data.c index b9c1141..5b4936b 100644 --- a/data.c +++ b/data.c @@ -1836,6 +1836,23 @@ xs_val *list_maint(snac *user, const char *list, int op) } +xs_list *list_timeline(snac *user, const char *list, int skip, int show) +/* returns the timeline of a list */ +{ + xs_list *l = NULL; + + if (!xs_is_hex(list)) + return NULL; + + xs *fn = xs_fmt("%s/list/%s.idx", user->basedir, list); + + if (mtime(fn) > 0.0) + l = index_list_desc(fn, skip, show); + + return l; +} + + xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op) /* list content management */ { @@ -1869,11 +1886,8 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op break; - case 3: /** list timeline **/ - fn = xs_replace_i(fn, ".lst", ".idx"); - - l = index_list_desc(fn, 0, 2048); - + default: + srv_log(xs_fmt("ERROR: list_content: bad op %d", op)); break; } diff --git a/html.c b/html.c index ee5c2cd..b34fc81 100644 --- a/html.c +++ b/html.c @@ -2658,6 +2658,29 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else + if (xs_startswith(p_path, "list/")) { /** list timelines **/ + if (!login(&snac, req)) { + *body = xs_dup(uid); + status = 401; + } + else { + xs *l = xs_split(p_path, "/"); + char *lid = xs_list_get(l, -1); + + xs *list = list_timeline(&snac, lid, skip, show); + xs *next = list_timeline(&snac, lid, skip + show, 1); + + if (list != NULL) { + xs *base = xs_fmt("/list/%s", lid); + + *body = html_timeline(&snac, list, 0, skip, show, + xs_list_len(next), NULL, base, 0); + *b_size = strlen(*body); + status = 200; + } + } + } + else if (xs_startswith(p_path, "p/")) { /** a timeline with just one entry **/ if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE) return 403; diff --git a/mastoapi.c b/mastoapi.c index 1f7f0b2..d39a6f9 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1669,7 +1669,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *l = xs_split(cmd, "/"); char *list = xs_list_get(l, -1); - xs *timeline = list_content(&snac1, list, NULL, 3); + xs *timeline = list_timeline(&snac1, list, 0, 2048); xs *out = xs_list_new(); int c = 0; char *md5; diff --git a/snac.h b/snac.h index 481a1ae..ab8fc7c 100644 --- a/snac.h +++ b/snac.h @@ -175,6 +175,7 @@ void tag_index(const char *id, const xs_dict *obj); xs_list *tag_search(char *tag, int skip, int show); xs_val *list_maint(snac *user, const char *list, int op); +xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); From b57409a9590c737da66c0aaa679a47c2c947fbf8 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 19:45:10 +0200 Subject: [PATCH 059/120] Fixed typo. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index b34fc81..536879c 100644 --- a/html.c +++ b/html.c @@ -2674,7 +2674,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs *base = xs_fmt("/list/%s", lid); *body = html_timeline(&snac, list, 0, skip, show, - xs_list_len(next), NULL, base, 0); + xs_list_len(next), NULL, base, 1); *b_size = strlen(*body); status = 200; } From 82ec4ea95cb1040c5b9b983ec649dbde024d20f8 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 20:32:13 +0200 Subject: [PATCH 060/120] Minor "Back to top" "More..." link refactoring. --- html.c | 34 ++++++++++++++-------------------- httpd.c | 10 +++++++--- snac.h | 2 +- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/html.c b/html.c index 536879c..bd3179d 100644 --- a/html.c +++ b/html.c @@ -509,7 +509,7 @@ xs_html *html_instance_head(void) } -static xs_html *html_instance_body(char *tag) +static xs_html *html_instance_body(char *title) { char *host = xs_dict_get(srv_config, "host"); char *sdesc = xs_dict_get(srv_config, "short_description"); @@ -560,14 +560,11 @@ static xs_html *html_instance_body(char *tag) xs_html_text(handle))))); } - { - xs *l = tag ? xs_fmt(L("Search results for #%s"), tag) : - xs_dup(L("Recent posts by users in this instance")); - + if (title != NULL) { xs_html_add(body, xs_html_tag("h2", xs_html_attr("class", "snac-header"), - xs_html_text(l))); + xs_html_text(title))); } return body; @@ -1996,7 +1993,7 @@ xs_html *html_footer(void) xs_str *html_timeline(snac *user, const xs_list *list, int read_only, int skip, int show, int show_more, - char *tag, char *page, int utl) + char *title, char *page, int utl) /* returns the HTML for the timeline */ { xs_list *p = (xs_list *)list; @@ -2026,7 +2023,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, } else { head = html_instance_head(); - body = html_instance_body(tag); + body = html_instance_body(title); } xs_html *html = xs_html_tag("html", @@ -2126,25 +2123,22 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, } if (show_more) { - xs *t = NULL; xs *m = NULL; xs *ss = xs_fmt("skip=%d&show=%d", skip + show, show); - xs *url = page == NULL || user == NULL ? - xs_dup(srv_baseurl) : xs_fmt("%s%s", user->actor, page); + xs *url = xs_dup(user == NULL ? srv_baseurl : user->actor); - if (tag) { - t = xs_fmt("%s?t=%s", url, tag); - m = xs_fmt("%s&%s", t, ss); - } - else { - t = xs_dup(url); - m = xs_fmt("%s?%s", t, ss); - } + if (page != NULL) + url = xs_str_cat(url, page); + + if (xs_str_in(url, "?") != -1) + m = xs_fmt("%s&%s", url, ss); + else + m = xs_fmt("%s?%s", url, ss); xs_html *more_links = xs_html_tag("p", xs_html_tag("a", - xs_html_attr("href", t), + xs_html_attr("href", url), xs_html_attr("name", "snac-more"), xs_html_text(L("Back to top"))), xs_html_text(" - "), diff --git a/httpd.c b/httpd.c index 30367c8..81b0853 100644 --- a/httpd.c +++ b/httpd.c @@ -200,14 +200,18 @@ int server_get_handler(xs_dict *req, const char *q_path, *body = timeline_to_rss(NULL, tl, link, link, link); *ctype = "application/rss+xml; charset=utf-8"; } - else - *body = html_timeline(NULL, tl, 0, skip, show, more, t, NULL, 0); + else { + xs *page = xs_fmt("?t=%s", t); + xs *title = xs_fmt(L("Search results for #%s"), t); + *body = html_timeline(NULL, tl, 0, skip, show, more, title, page, 0); + } } else if (xs_type(xs_dict_get(srv_config, "show_instance_timeline")) == XSTYPE_TRUE) { /** instance timeline **/ xs *tl = timeline_instance_list(0, 30); - *body = html_timeline(NULL, tl, 0, 0, 0, 0, NULL, NULL, 0); + *body = html_timeline(NULL, tl, 0, 0, 0, 0, + L("Recent posts by users in this instance"), NULL, 0); } else *body = greeting_html(); diff --git a/snac.h b/snac.h index ab8fc7c..2a988f1 100644 --- a/snac.h +++ b/snac.h @@ -318,7 +318,7 @@ 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, - char *tag, char *page, int utl); + char *title, char *page, int utl); int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag); From 3355d5559dc03f01382282409f33ca6908e495fe Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 20:51:24 +0200 Subject: [PATCH 061/120] New op #3 to list_maint() (get list name). --- data.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/data.c b/data.c index 5b4936b..4e1682c 100644 --- a/data.c +++ b/data.c @@ -1829,6 +1829,19 @@ xs_val *list_maint(snac *user, const char *list, int op) } } + break; + + case 3: /** get list name **/ + if (xs_is_hex(list)) { + FILE *f; + xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); + + if ((f = fopen(fn, "r")) != NULL) { + l = xs_strip_i(xs_readline(f)); + fclose(f); + } + } + break; } From d3cdbf170251db70121b2162513a6cfa2addd8c4 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 20:51:43 +0200 Subject: [PATCH 062/120] Added a title to list timelines. --- html.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index bd3179d..86cb2f5 100644 --- a/html.c +++ b/html.c @@ -2034,6 +2034,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, html_top_controls(user)); + if (user && title) { + xs_html_add(body, + xs_html_tag("h2", + xs_html_attr("class", "snac-header"), + xs_html_text(title))); + } + xs_html_add(body, xs_html_tag("a", xs_html_attr("name", "snac-posts"))); @@ -2666,9 +2673,11 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (list != NULL) { xs *base = xs_fmt("/list/%s", lid); + xs *name = list_maint(&snac, lid, 3); + xs *title = xs_fmt(L("Showing timeline for list %s"), name); *body = html_timeline(&snac, list, 0, skip, show, - xs_list_len(next), NULL, base, 1); + xs_list_len(next), title, base, 1); *b_size = strlen(*body); status = 200; } From 2322de4900312653d04ec19e8ee2850e0f5d4ac4 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 7 May 2024 20:53:58 +0200 Subject: [PATCH 063/120] Added a title to the instance timeline. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 86cb2f5..5521a9f 100644 --- a/html.c +++ b/html.c @@ -2653,7 +2653,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), NULL, "/instance", 0); + xs_list_len(next), L("Showing instance timeline"), "/instance", 0); *b_size = strlen(*body); status = 200; } From fa8b40192757a2022da1cc945e4d0c3c1dc014b2 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 04:15:36 +0200 Subject: [PATCH 064/120] More HTML simplification. --- html.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/html.c b/html.c index 5521a9f..27fc713 100644 --- a/html.c +++ b/html.c @@ -509,7 +509,7 @@ xs_html *html_instance_head(void) } -static xs_html *html_instance_body(char *title) +static xs_html *html_instance_body(void) { char *host = xs_dict_get(srv_config, "host"); char *sdesc = xs_dict_get(srv_config, "short_description"); @@ -560,13 +560,6 @@ static xs_html *html_instance_body(char *title) xs_html_text(handle))))); } - if (title != NULL) { - xs_html_add(body, - xs_html_tag("h2", - xs_html_attr("class", "snac-header"), - xs_html_text(title))); - } - return body; } @@ -2023,7 +2016,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, } else { head = html_instance_head(); - body = html_instance_body(title); + body = html_instance_body(); } xs_html *html = xs_html_tag("html", @@ -2034,7 +2027,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, html_top_controls(user)); - if (user && title) { + if (title) { xs_html_add(body, xs_html_tag("h2", xs_html_attr("class", "snac-header"), From d27a60dfe7a440ad76540b826d9105a82711f70b Mon Sep 17 00:00:00 2001 From: khm Date: Tue, 7 May 2024 23:28:51 -0700 Subject: [PATCH 065/120] allow unboosting your own posts --- html.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index ee5c2cd..018cdc8 100644 --- a/html.c +++ b/html.c @@ -1265,8 +1265,8 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const } if (is_msg_public(msg)) { - if (strcmp(actor, snac->actor) == 0 || xs_list_in(boosts, snac->md5) == -1) { - /* not already boosted or us; add button */ + if (xs_list_in(boosts, snac->md5) == -1) { + /* not already boosted; add button */ xs_html_add(form, html_button("boost", L("Boost"), L("Announce this post to your followers"))); } From 3ab733cdf5a71b9a27399e8336e0c236c13d67fb Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 10:20:25 +0200 Subject: [PATCH 066/120] New function search_by_content(). --- data.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 18 ++++++++++++++++++ snac.h | 3 +++ 3 files changed, 79 insertions(+) diff --git a/data.c b/data.c index 4e1682c..bc9f979 100644 --- a/data.c +++ b/data.c @@ -2488,6 +2488,64 @@ void notify_clear(snac *snac) } +/** searches **/ + +xs_list *search_by_content(snac *user, const xs_list *timeline, + const char *regex, int timeout) +/* returns a list of posts which content matches the regex */ +{ + xs_list *r = xs_list_new(); + + if (timeout == 0) + timeout = 3; + + int c = 0; + char *v; + + time_t t = time(NULL) + timeout; + + while (xs_list_next(timeline, &v, &c)) { + xs *post = NULL; + + /* timeout? */ + if (time(NULL) > t) + break; + + int status; + + if (user) + status = timeline_get_by_md5(user, v, &post); + else + status = object_get_by_md5(v, &post); + + if (!valid_status(status)) + continue; + + /* must be a Note */ + if (strcmp(xs_dict_get_def(post, "type", ""), "Note")) + continue; + + char *content = xs_dict_get(post, "content"); + + if (xs_is_null(content)) + continue; + + /* strip HTML */ + xs *c = xs_regex_replace(content, "<[^>]+>", " "); + c = xs_regex_replace_i(c, " {2,}", " "); + c = xs_tolower_i(c); + + /* apply regex */ + xs *l = xs_regex_select_n(c, regex, 1); + + if (xs_list_len(l)) + r = xs_list_append(r, v); + } + + return r; +} + + /** the queue **/ static xs_dict *_enqueue_put(const char *fn, xs_dict *msg) diff --git a/main.c b/main.c index 6a38412..2e1a77c 100644 --- a/main.c +++ b/main.c @@ -44,6 +44,7 @@ int usage(void) printf("limit {basedir} {uid} {actor} Limits an actor (drops their announces)\n"); printf("unlimit {basedir} {uid} {actor} Unlimits an actor\n"); printf("verify_links {basedir} {uid} Verifies a user's links (in the metadata)\n"); + printf("search {basedir} {uid} {regex} Searches posts by content\n"); return 1; } @@ -374,6 +375,23 @@ int main(int argc, char *argv[]) return 0; } + if (strcmp(cmd, "search") == 0) { /** **/ + xs *tl = timeline_simple_list(&snac, "private", 0, XS_ALL); + + /* 'url' contains the regex */ + xs *r = search_by_content(&snac, tl, url, 10); + + int c = 0; + char *v; + + /* print results as standalone links */ + while (xs_list_next(r, &v, &c)) { + printf("%s/admin/p/%s\n", snac.actor, v); + } + + return 0; + } + if (strcmp(cmd, "ping") == 0) { /** **/ xs *actor_o = NULL; diff --git a/snac.h b/snac.h index 2a988f1..ae8fc59 100644 --- a/snac.h +++ b/snac.h @@ -179,6 +179,9 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); +xs_list *search_by_content(snac *user, const xs_list *timeline, + const char *regex, int timeout); + int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); int actor_get_refresh(snac *user, const char *actor, xs_dict **data); From dc74cac6c9c3f6e9edc848381422a1f124abac73 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 10:27:30 +0200 Subject: [PATCH 067/120] Added a timeout flag to search_by_content(). --- data.c | 13 ++++++++----- main.c | 3 ++- snac.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/data.c b/data.c index bc9f979..82f36a4 100644 --- a/data.c +++ b/data.c @@ -2491,25 +2491,28 @@ void notify_clear(snac *snac) /** searches **/ xs_list *search_by_content(snac *user, const xs_list *timeline, - const char *regex, int timeout) + const char *regex, int max_secs, int *timeout) /* returns a list of posts which content matches the regex */ { xs_list *r = xs_list_new(); - if (timeout == 0) - timeout = 3; + if (max_secs == 0) + max_secs = 3; int c = 0; char *v; - time_t t = time(NULL) + timeout; + time_t t = time(NULL) + max_secs; + *timeout = 0; while (xs_list_next(timeline, &v, &c)) { xs *post = NULL; /* timeout? */ - if (time(NULL) > t) + if (time(NULL) > t) { + *timeout = 1; break; + } int status; diff --git a/main.c b/main.c index 2e1a77c..9244b5a 100644 --- a/main.c +++ b/main.c @@ -377,9 +377,10 @@ int main(int argc, char *argv[]) if (strcmp(cmd, "search") == 0) { /** **/ xs *tl = timeline_simple_list(&snac, "private", 0, XS_ALL); + int to; /* 'url' contains the regex */ - xs *r = search_by_content(&snac, tl, url, 10); + xs *r = search_by_content(&snac, tl, url, 10, &to); int c = 0; char *v; diff --git a/snac.h b/snac.h index ae8fc59..7f069ab 100644 --- a/snac.h +++ b/snac.h @@ -180,7 +180,7 @@ xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int void list_distribute(snac *user, const char *who, const xs_dict *post); xs_list *search_by_content(snac *user, const xs_list *timeline, - const char *regex, int timeout); + const char *regex, int max_secs, int *timeout); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From 52ba7f030ed50f22b7fbf437e83c9415ed0e91ed Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 11:00:50 +0200 Subject: [PATCH 068/120] Renamed to content_search(). --- data.c | 2 +- main.c | 21 ++++++++++++++++++++- snac.h | 4 ++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/data.c b/data.c index 82f36a4..e3292c8 100644 --- a/data.c +++ b/data.c @@ -2490,7 +2490,7 @@ void notify_clear(snac *snac) /** searches **/ -xs_list *search_by_content(snac *user, const xs_list *timeline, +xs_list *content_search(snac *user, const xs_list *timeline, const char *regex, int max_secs, int *timeout) /* returns a list of posts which content matches the regex */ { diff --git a/main.c b/main.c index 9244b5a..240e02e 100644 --- a/main.c +++ b/main.c @@ -380,7 +380,26 @@ int main(int argc, char *argv[]) int to; /* 'url' contains the regex */ - xs *r = search_by_content(&snac, tl, url, 10, &to); + xs *r = content_search(&snac, tl, url, 10, &to); + + int c = 0; + char *v; + + /* print results as standalone links */ + while (xs_list_next(r, &v, &c)) { + printf("%s/admin/p/%s\n", snac.actor, v); + } + + return 0; + } + + if (strcmp(cmd, "search2") == 0) { /** **/ + /* undocumented (for testing only) */ + xs *tl = timeline_simple_list(&snac, "public", 0, XS_ALL); + int to; + + /* 'url' contains the regex */ + xs *r = content_search(&snac, tl, url, 10, &to); int c = 0; char *v; diff --git a/snac.h b/snac.h index 7f069ab..e3b76f6 100644 --- a/snac.h +++ b/snac.h @@ -179,8 +179,8 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -xs_list *search_by_content(snac *user, const xs_list *timeline, - const char *regex, int max_secs, int *timeout); +xs_list *content_search(snac *user, const xs_list *timeline, + const char *regex, int max_secs, int *timeout); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From 34c8968cfb0aaa14ed901115c1addd1886916b79 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 11:06:33 +0200 Subject: [PATCH 069/120] Minor tweaks to content_search(). --- data.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/data.c b/data.c index e3292c8..1421f17 100644 --- a/data.c +++ b/data.c @@ -2491,7 +2491,7 @@ void notify_clear(snac *snac) /** searches **/ xs_list *content_search(snac *user, const xs_list *timeline, - const char *regex, int max_secs, int *timeout) + const char *regex, int max_secs, int *timeout) /* returns a list of posts which content matches the regex */ { xs_list *r = xs_list_new(); @@ -2514,14 +2514,11 @@ xs_list *content_search(snac *user, const xs_list *timeline, break; } - int status; + /* if from a user, must be in any timeline */ + if (user && !timeline_here(user, v)) + continue; - if (user) - status = timeline_get_by_md5(user, v, &post); - else - status = object_get_by_md5(v, &post); - - if (!valid_status(status)) + if (!valid_status(object_get_by_md5(v, &post))) continue; /* must be a Note */ From 979f2ad1400111bcd36da980ba8f78f25bb5390e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 8 May 2024 16:43:02 +0200 Subject: [PATCH 070/120] Rewritten content_search() to read from both timelines. --- data.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++----------- main.c | 22 +---------------- snac.h | 3 +-- 3 files changed, 65 insertions(+), 37 deletions(-) diff --git a/data.c b/data.c index 1421f17..d9449d8 100644 --- a/data.c +++ b/data.c @@ -2490,23 +2490,35 @@ void notify_clear(snac *snac) /** searches **/ -xs_list *content_search(snac *user, const xs_list *timeline, - const char *regex, int max_secs, int *timeout) +xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int *timeout) /* returns a list of posts which content matches the regex */ { - xs_list *r = xs_list_new(); + xs_set seen; + + xs_set_init(&seen); if (max_secs == 0) max_secs = 3; - int c = 0; - char *v; - time_t t = time(NULL) + max_secs; *timeout = 0; - while (xs_list_next(timeline, &v, &c)) { - xs *post = NULL; + /* iterate both timelines simultaneously */ + xs *pub_tl = timeline_simple_list(user, "public", 0, XS_ALL); + int pub_c = 0; + char *pub_md5 = NULL; + + xs *priv_tl = priv ? timeline_simple_list(user, "private", 0, XS_ALL) : xs_list_new(); + int priv_c = 0; + char *priv_md5 = NULL; + + /* first positioning */ + xs_list_next(pub_tl, &pub_md5, &pub_c); + xs_list_next(priv_tl, &priv_md5, &priv_c); + + for (;;) { + char *md5 = NULL; + enum { NONE, PUBLIC, PRIVATE } from = NONE; /* timeout? */ if (time(NULL) > t) { @@ -2514,11 +2526,48 @@ xs_list *content_search(snac *user, const xs_list *timeline, break; } - /* if from a user, must be in any timeline */ - if (user && !timeline_here(user, v)) - continue; + if (pub_md5 == NULL) { + /* out of both lists? done */ + if (priv_md5 == NULL) + break; - if (!valid_status(object_get_by_md5(v, &post))) + /* out of public: take element from the private timeline and advance */ + from = PRIVATE; + } + else + if (priv_md5 == NULL) { + /* out of private: take element from the public timeline and advance */ + from = PUBLIC; + } + else { + /* candidates from both: choose one from the file dates */ + xs *pub_fn = xs_fmt("%s/public/%s.json", user->basedir, pub_md5); + xs *priv_fn = xs_fmt("%s/private/%s.json", user->basedir, priv_md5); + + if (mtime(pub_fn) < mtime(priv_fn)) + from = PRIVATE; + else + from = PUBLIC; + } + + if (from == PUBLIC) { /* public */ + md5 = pub_md5; + if (!xs_list_next(pub_tl, &pub_md5, &pub_c)) + pub_md5 = NULL; + } + else + if (from == PRIVATE) { /* private */ + md5 = priv_md5; + if (!xs_list_next(priv_tl, &priv_md5, &priv_c)) + priv_md5 = NULL; + } + + if (md5 == NULL) + break; + + xs *post = NULL; + + if (!valid_status(timeline_get_by_md5(user, md5, &post))) continue; /* must be a Note */ @@ -2539,10 +2588,10 @@ xs_list *content_search(snac *user, const xs_list *timeline, xs *l = xs_regex_select_n(c, regex, 1); if (xs_list_len(l)) - r = xs_list_append(r, v); + xs_set_add(&seen, md5); } - return r; + return xs_set_result(&seen); } diff --git a/main.c b/main.c index 240e02e..bd52b19 100644 --- a/main.c +++ b/main.c @@ -376,30 +376,10 @@ int main(int argc, char *argv[]) } if (strcmp(cmd, "search") == 0) { /** **/ - xs *tl = timeline_simple_list(&snac, "private", 0, XS_ALL); int to; /* 'url' contains the regex */ - xs *r = content_search(&snac, tl, url, 10, &to); - - int c = 0; - char *v; - - /* print results as standalone links */ - while (xs_list_next(r, &v, &c)) { - printf("%s/admin/p/%s\n", snac.actor, v); - } - - return 0; - } - - if (strcmp(cmd, "search2") == 0) { /** **/ - /* undocumented (for testing only) */ - xs *tl = timeline_simple_list(&snac, "public", 0, XS_ALL); - int to; - - /* 'url' contains the regex */ - xs *r = content_search(&snac, tl, url, 10, &to); + xs *r = content_search(&snac, url, 1, 10, &to); int c = 0; char *v; diff --git a/snac.h b/snac.h index e3b76f6..e04dc65 100644 --- a/snac.h +++ b/snac.h @@ -179,8 +179,7 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -xs_list *content_search(snac *user, const xs_list *timeline, - const char *regex, int max_secs, int *timeout); +xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int *timeout); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From be420cae77c6da69a976e15848989a5e52c56403 Mon Sep 17 00:00:00 2001 From: Kelson Vibber Date: Thu, 9 May 2024 04:27:58 +0000 Subject: [PATCH 071/120] Set avatars and custom emoji as square (fixes display in Dillo) The minimalist web browser Dillo has recently started up development again. Its CSS implementation only applies the explicitly stated width and height of an image, so avatars were displaying at 2.5 em high, and the full original width. https://dillo-browser.github.io/ --- html.c | 2 +- utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index ee5c2cd..8bc8cc9 100644 --- a/html.c +++ b/html.c @@ -55,7 +55,7 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems) tag_list = xs_dup(tag); } - xs *style = xs_fmt("height: %dem; vertical-align: middle;", ems); + xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); xs_list *p = tag_list; char *v; diff --git a/utils.c b/utils.c index d00f513..0523cac 100644 --- a/utils.c +++ b/utils.c @@ -49,7 +49,7 @@ static const char *default_css = ".snac-top-user { text-align: center; padding-bottom: 2em }\n" ".snac-top-user-name { font-size: 200% }\n" ".snac-top-user-id { font-size: 150% }\n" - ".snac-avatar { float: left; height: 2.5em; padding: 0.25em }\n" + ".snac-avatar { float: left; height: 2.5em; width: 2.5em; padding: 0.25em }\n" ".snac-author { font-size: 90%; text-decoration: none }\n" ".snac-author-tag { font-size: 80% }\n" ".snac-pubdate { color: #a0a0a0; font-size: 90% }\n" From 2c8d4ce6bd227377fbfa02d92abdbff8a65150c3 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 09:09:22 +0200 Subject: [PATCH 072/120] mastoapi: Added content search. --- mastoapi.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index d39a6f9..e4bc116 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2228,8 +2228,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* reply something only for offset 0; otherwise, apps like Tusky keep asking again and again */ - if (!xs_is_null(q) && !xs_is_null(type)) { - if (strcmp(type, "accounts") == 0) { + if (!xs_is_null(q)) { + if (xs_is_null(type) || strcmp(type, "accounts") == 0) { /* do a webfinger query */ char *actor = NULL; char *user = NULL; @@ -2244,8 +2244,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, } } } - else - if (strcmp(type, "hashtags") == 0) { + + if (xs_is_null(type) || strcmp(type, "hashtags") == 0) { /* search this tag */ xs *tl = tag_search((char *)q, 0, 1); @@ -2260,6 +2260,28 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, htl = xs_list_append(htl, d); } } + + if (xs_is_null(type) || strcmp(type, "statuses") == 0) { + int to = 0; + xs *tl = content_search(&snac1, q, 1, 0, &to); + int c = 0; + char *v; + + while (xs_list_next(tl, &v, &c)) { + xs *post = NULL; + + if (!valid_status(timeline_get_by_md5(&snac1, v, &post))) + continue; + + char *type = xs_dict_get_def(post, "type", "-"); + if (!xs_match(type, "Note|Article|Question|Page|Video")) + continue; + + xs *s = mastoapi_status(&snac1, post); + + stl = xs_list_append(stl, s); + } + } } } From 8de93b9cd72ef0cde8b5bb620f376e9b926354dd Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 09:24:39 +0200 Subject: [PATCH 073/120] mastoapi: some search by content tweaks. --- data.c | 4 ++-- mastoapi.c | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/data.c b/data.c index d9449d8..75149e2 100644 --- a/data.c +++ b/data.c @@ -10,6 +10,7 @@ #include "xs_set.h" #include "xs_time.h" #include "xs_regex.h" +#include "xs_match.h" #include "snac.h" @@ -2570,8 +2571,7 @@ xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, i if (!valid_status(timeline_get_by_md5(user, md5, &post))) continue; - /* must be a Note */ - if (strcmp(xs_dict_get_def(post, "type", ""), "Note")) + if (!xs_match(xs_dict_get_def(post, "type", "-"), "Note|Question|Page|Article|Video")) continue; char *content = xs_dict_get(post, "content"); diff --git a/mastoapi.c b/mastoapi.c index e4bc116..253021b 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1467,9 +1467,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (limit == 0) limit = 20; - xs *ja = xs_json_dumps(args, 0); - snac_debug(&snac1, 1, xs_fmt("/v1/timelines/home: args='%s'", ja)); - xs *timeline = timeline_simple_list(&snac1, "private", 0, 2048); xs *out = xs_list_new(); @@ -2266,20 +2263,18 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *tl = content_search(&snac1, q, 1, 0, &to); int c = 0; char *v; + int cnt = 40; - while (xs_list_next(tl, &v, &c)) { + while (xs_list_next(tl, &v, &c) && --cnt) { xs *post = NULL; if (!valid_status(timeline_get_by_md5(&snac1, v, &post))) continue; - char *type = xs_dict_get_def(post, "type", "-"); - if (!xs_match(type, "Note|Article|Question|Page|Video")) - continue; - xs *s = mastoapi_status(&snac1, post); - stl = xs_list_append(stl, s); + if (!xs_is_null(s)) + stl = xs_list_append(stl, s); } } } From cccc5454c259d0d4ca600b3e1165979c352be54f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 09:31:10 +0200 Subject: [PATCH 074/120] Added a maximum results argument to content_search(). --- data.c | 11 ++++++++--- main.c | 2 +- mastoapi.c | 4 ++-- snac.h | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/data.c b/data.c index 75149e2..78de21b 100644 --- a/data.c +++ b/data.c @@ -2491,9 +2491,12 @@ void notify_clear(snac *snac) /** searches **/ -xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int *timeout) +xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int max_res, int *timeout) /* returns a list of posts which content matches the regex */ { + if (regex == NULL || *regex == '\0') + return xs_list_new(); + xs_set seen; xs_set_init(&seen); @@ -2517,7 +2520,7 @@ xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, i xs_list_next(pub_tl, &pub_md5, &pub_c); xs_list_next(priv_tl, &priv_md5, &priv_c); - for (;;) { + while (max_res > 0) { char *md5 = NULL; enum { NONE, PUBLIC, PRIVATE } from = NONE; @@ -2587,8 +2590,10 @@ xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, i /* apply regex */ xs *l = xs_regex_select_n(c, regex, 1); - if (xs_list_len(l)) + if (xs_list_len(l)) { xs_set_add(&seen, md5); + max_res--; + } } return xs_set_result(&seen); diff --git a/main.c b/main.c index bd52b19..5e37340 100644 --- a/main.c +++ b/main.c @@ -379,7 +379,7 @@ int main(int argc, char *argv[]) int to; /* 'url' contains the regex */ - xs *r = content_search(&snac, url, 1, 10, &to); + xs *r = content_search(&snac, url, 1, 10, XS_ALL, &to); int c = 0; char *v; diff --git a/mastoapi.c b/mastoapi.c index 253021b..cbc965c 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2260,10 +2260,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (xs_is_null(type) || strcmp(type, "statuses") == 0) { int to = 0; - xs *tl = content_search(&snac1, q, 1, 0, &to); + int cnt = 40; + xs *tl = content_search(&snac1, q, 1, 0, cnt, &to); int c = 0; char *v; - int cnt = 40; while (xs_list_next(tl, &v, &c) && --cnt) { xs *post = NULL; diff --git a/snac.h b/snac.h index e04dc65..579d149 100644 --- a/snac.h +++ b/snac.h @@ -179,7 +179,7 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int *timeout); +xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int max_res, int *timeout); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From 7b0e9285af8ec0f4f4de48e43bf46e8bb1741ae9 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 09:32:43 +0200 Subject: [PATCH 075/120] Updated dependencies. --- Makefile | 2 +- Makefile.NetBSD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 39b44ef..1edf99e 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ uninstall: activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ - xs_set.h xs_time.h xs_regex.h snac.h + xs_set.h xs_time.h xs_regex.h xs_match.h snac.h format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h snac.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ diff --git a/Makefile.NetBSD b/Makefile.NetBSD index 67c77a5..bb2f1bc 100644 --- a/Makefile.NetBSD +++ b/Makefile.NetBSD @@ -38,7 +38,7 @@ uninstall: activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ - xs_set.h xs_time.h xs_regex.h snac.h + xs_set.h xs_time.h xs_regex.h xs_match.h snac.h format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h snac.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ From a5766a6c83662efeb9544ab4a14ea4bfa33c5344 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 10:04:45 +0200 Subject: [PATCH 076/120] The /admin page now accepts ?q=regex for a content search. --- html.c | 61 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/html.c b/html.c index 4109929..c56f4cb 100644 --- a/html.c +++ b/html.c @@ -2560,35 +2560,48 @@ int html_get_handler(const xs_dict *req, const char *q_path, status = 401; } else { - double t = history_mtime(&snac, "timeline.html_"); + char *q = xs_dict_get(q_vars, "q"); - /* if enabled by admin, return a cached page if its timestamp is: - a) newer than the timeline timestamp - b) newer than the start time of the server - */ - if (cache && t > timeline_mtime(&snac) && t > p_state->srv_start_time) { - snac_debug(&snac, 1, xs_fmt("serving cached timeline")); - - status = history_get(&snac, "timeline.html_", body, b_size, - xs_dict_get(req, "if-none-match"), etag); - } - else { - 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 *pins = pinned_list(&snac); - pins = xs_list_cat(pins, list); - - *body = html_timeline(&snac, pins, 0, skip, show, - xs_list_len(next), NULL, "/admin", 1); + if (q && *q) { + /* search by content */ + int to = 0; + xs *tl = content_search(&snac, q, 1, 0, show, &to); + *body = html_timeline(&snac, tl, 0, 0, show, 0, NULL, "/admin", 1); *b_size = strlen(*body); status = 200; + } + else { + double t = history_mtime(&snac, "timeline.html_"); - if (save) - history_add(&snac, "timeline.html_", *body, *b_size, etag); + /* if enabled by admin, return a cached page if its timestamp is: + a) newer than the timeline timestamp + b) newer than the start time of the server + */ + if (cache && t > timeline_mtime(&snac) && t > p_state->srv_start_time) { + snac_debug(&snac, 1, xs_fmt("serving cached timeline")); + + status = history_get(&snac, "timeline.html_", body, b_size, + xs_dict_get(req, "if-none-match"), etag); + } + else { + 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 *pins = pinned_list(&snac); + pins = xs_list_cat(pins, list); + + *body = html_timeline(&snac, pins, 0, skip, show, + xs_list_len(next), NULL, "/admin", 1); + + *b_size = strlen(*body); + status = 200; + + if (save) + history_add(&snac, "timeline.html_", *body, *b_size, etag); + } } } } From 430afe103b9b31c321c13d510250877b1361c965 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 9 May 2024 22:57:08 +0200 Subject: [PATCH 077/120] Added a title to search results. --- html.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index c56f4cb..505f41a 100644 --- a/html.c +++ b/html.c @@ -2566,8 +2566,14 @@ int html_get_handler(const xs_dict *req, const char *q_path, /* search by content */ int to = 0; xs *tl = content_search(&snac, q, 1, 0, show, &to); + xs *title = NULL; - *body = html_timeline(&snac, tl, 0, 0, show, 0, NULL, "/admin", 1); + if (xs_list_len(tl)) + title = xs_fmt(L("Search results for '%s'"), q); + else + title = xs_fmt(L("Nothing found for '%s'"), q); + + *body = html_timeline(&snac, tl, 0, 0, show, 0, title, "/admin", 1); *b_size = strlen(*body); status = 200; } From 2b27a0d772cd294cfe1db67bbfdb2b58df585130 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 11:02:59 +0200 Subject: [PATCH 078/120] Some tweaks to web ui search. --- html.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/html.c b/html.c index 505f41a..9eefeec 100644 --- a/html.c +++ b/html.c @@ -2565,15 +2565,18 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (q && *q) { /* search by content */ int to = 0; - xs *tl = content_search(&snac, q, 1, 0, show, &to); + xs *tl = content_search(&snac, q, 1, skip ? 10 : 0, skip + show, &to); xs *title = NULL; + xs *page = xs_fmt("/admin?q=%s", q); + int tl_len = xs_list_len(tl); - if (xs_list_len(tl)) + if (tl_len) title = xs_fmt(L("Search results for '%s'"), q); else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, 0, show, 0, title, "/admin", 1); + *body = html_timeline(&snac, tl, 0, 0, tl_len, + (to || tl_len == skip + show), title, page, 1); *b_size = strlen(*body); status = 200; } From 1531e81a310311656d06c9ee0d31da31493e6de2 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 11:29:18 +0200 Subject: [PATCH 079/120] Added a skip argument to content_search(). --- data.c | 13 +++++++++---- html.c | 10 ++++++---- main.c | 2 +- mastoapi.c | 2 +- snac.h | 3 ++- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/data.c b/data.c index 78de21b..e284696 100644 --- a/data.c +++ b/data.c @@ -2491,7 +2491,8 @@ void notify_clear(snac *snac) /** searches **/ -xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int max_res, int *timeout) +xs_list *content_search(snac *user, const char *regex, + int priv, int skip, int show, int max_secs, int *timeout) /* returns a list of posts which content matches the regex */ { if (regex == NULL || *regex == '\0') @@ -2520,7 +2521,7 @@ xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, i xs_list_next(pub_tl, &pub_md5, &pub_c); xs_list_next(priv_tl, &priv_md5, &priv_c); - while (max_res > 0) { + while (show > 0) { char *md5 = NULL; enum { NONE, PUBLIC, PRIVATE } from = NONE; @@ -2591,8 +2592,12 @@ xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, i xs *l = xs_regex_select_n(c, regex, 1); if (xs_list_len(l)) { - xs_set_add(&seen, md5); - max_res--; + if (skip) + skip--; + else { + xs_set_add(&seen, md5); + show--; + } } } diff --git a/html.c b/html.c index 9eefeec..d50333a 100644 --- a/html.c +++ b/html.c @@ -2563,20 +2563,22 @@ int html_get_handler(const xs_dict *req, const char *q_path, char *q = xs_dict_get(q_vars, "q"); if (q && *q) { - /* search by content */ + /** search by content **/ int to = 0; - xs *tl = content_search(&snac, q, 1, skip ? 10 : 0, skip + show, &to); + xs *tl = content_search(&snac, q, 1, skip, show, skip ? 10 : 0, &to); xs *title = NULL; xs *page = xs_fmt("/admin?q=%s", q); int tl_len = xs_list_len(tl); if (tl_len) title = xs_fmt(L("Search results for '%s'"), q); + else + if (skip) + title = xs_fmt(L("No more matches for '%s'"), q); else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, 0, tl_len, - (to || tl_len == skip + show), title, page, 1); + *body = html_timeline(&snac, tl, 0, skip, show, tl_len > 0, title, page, 1); *b_size = strlen(*body); status = 200; } diff --git a/main.c b/main.c index 5e37340..819922f 100644 --- a/main.c +++ b/main.c @@ -379,7 +379,7 @@ int main(int argc, char *argv[]) int to; /* 'url' contains the regex */ - xs *r = content_search(&snac, url, 1, 10, XS_ALL, &to); + xs *r = content_search(&snac, url, 1, 0, XS_ALL, 10, &to); int c = 0; char *v; diff --git a/mastoapi.c b/mastoapi.c index cbc965c..1071bfd 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2261,7 +2261,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (xs_is_null(type) || strcmp(type, "statuses") == 0) { int to = 0; int cnt = 40; - xs *tl = content_search(&snac1, q, 1, 0, cnt, &to); + xs *tl = content_search(&snac1, q, 1, 0, cnt, 0, &to); int c = 0; char *v; diff --git a/snac.h b/snac.h index 579d149..64d48f2 100644 --- a/snac.h +++ b/snac.h @@ -179,7 +179,8 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -xs_list *content_search(snac *user, const char *regex, int priv, int max_secs, int max_res, int *timeout); +xs_list *content_search(snac *user, const char *regex, + int priv, int skip, int show, int max_secs, int *timeout); int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); From 1632e009a429a45f374b5595d128da9db810b948 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 11:42:15 +0200 Subject: [PATCH 080/120] More content_search() tweaks. --- data.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data.c b/data.c index e284696..2e682b3 100644 --- a/data.c +++ b/data.c @@ -2592,12 +2592,11 @@ xs_list *content_search(snac *user, const char *regex, xs *l = xs_regex_select_n(c, regex, 1); if (xs_list_len(l)) { - if (skip) + if (skip > 0) skip--; - else { - xs_set_add(&seen, md5); + else + if (xs_set_add(&seen, md5) == 1) show--; - } } } From 5d1577af3ceaad8e3e89cf53a1c523e6a5c710a6 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 11:55:45 +0200 Subject: [PATCH 081/120] More web ui search tweaks baaaaah I'm boring. --- data.c | 17 ++++++++++++----- html.c | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/data.c b/data.c index 2e682b3..60eda25 100644 --- a/data.c +++ b/data.c @@ -2499,8 +2499,10 @@ xs_list *content_search(snac *user, const char *regex, return xs_list_new(); xs_set seen; + xs_set skipped; xs_set_init(&seen); + xs_set_init(&skipped); if (max_secs == 0) max_secs = 3; @@ -2592,14 +2594,19 @@ xs_list *content_search(snac *user, const char *regex, xs *l = xs_regex_select_n(c, regex, 1); if (xs_list_len(l)) { - if (skip > 0) - skip--; - else - if (xs_set_add(&seen, md5) == 1) - show--; + if (skip > 0) { + if (xs_set_add(&skipped, md5) == 1) + skip--; + } + else { + if (xs_set_add(&seen, md5) == 1) + show--; + } } } + xs_set_free(&skipped); + return xs_set_result(&seen); } diff --git a/html.c b/html.c index d50333a..8bcfab3 100644 --- a/html.c +++ b/html.c @@ -2578,7 +2578,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, skip, show, tl_len > 0, title, page, 1); + *body = html_timeline(&snac, tl, 0, skip, tl_len, tl_len > 0, title, page, 1); *b_size = strlen(*body); status = 200; } From d12ebcef82d274aef0930c94ba90b2b40d91828c Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 12:22:36 +0200 Subject: [PATCH 082/120] More search tweaks MUAHAHAHA. --- data.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/data.c b/data.c index 60eda25..d037af0 100644 --- a/data.c +++ b/data.c @@ -2499,10 +2499,8 @@ xs_list *content_search(snac *user, const char *regex, return xs_list_new(); xs_set seen; - xs_set skipped; xs_set_init(&seen); - xs_set_init(&skipped); if (max_secs == 0) max_secs = 3; @@ -2523,6 +2521,8 @@ xs_list *content_search(snac *user, const char *regex, xs_list_next(pub_tl, &pub_md5, &pub_c); xs_list_next(priv_tl, &priv_md5, &priv_c); + show += skip; + while (show > 0) { char *md5 = NULL; enum { NONE, PUBLIC, PRIVATE } from = NONE; @@ -2594,20 +2594,21 @@ xs_list *content_search(snac *user, const char *regex, xs *l = xs_regex_select_n(c, regex, 1); if (xs_list_len(l)) { - if (skip > 0) { - if (xs_set_add(&skipped, md5) == 1) - skip--; - } - else { - if (xs_set_add(&seen, md5) == 1) - show--; - } + if (xs_set_add(&seen, md5) == 1) + show--; } } - xs_set_free(&skipped); + xs_list *r = xs_set_result(&seen); - return xs_set_result(&seen); + if (skip) { + /* BAD */ + while (skip--) { + r = xs_list_del(r, 0); + } + } + + return r; } From c2c0f3b39b8d893b9041907df375fa20654ad3e2 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 12:44:26 +0200 Subject: [PATCH 083/120] More search tweaks la la la la. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 8bcfab3..8139d68 100644 --- a/html.c +++ b/html.c @@ -2578,7 +2578,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, skip, tl_len, tl_len > 0, title, page, 1); + *body = html_timeline(&snac, tl, 0, skip, tl_len, tl_len == show, title, page, 1); *b_size = strlen(*body); status = 200; } From 767c2d98d519c9bde43788336d269adf353e573f Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 12:49:03 +0200 Subject: [PATCH 084/120] Also add the 'More...' link if there was a search timeout. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 8139d68..3d378d1 100644 --- a/html.c +++ b/html.c @@ -2578,7 +2578,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, skip, tl_len, tl_len == show, title, page, 1); + *body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 1); *b_size = strlen(*body); status = 200; } From f2a33727e781a9f7338d8321b3d5b504ca4dab8e Mon Sep 17 00:00:00 2001 From: default Date: Fri, 10 May 2024 17:54:15 +0200 Subject: [PATCH 085/120] Added a search box. --- html.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index 3d378d1..a0a9cf7 100644 --- a/html.c +++ b/html.c @@ -739,7 +739,17 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_text(" - "), xs_html_tag("a", xs_html_attr("href", instance_url), - xs_html_text(L("instance")))); + xs_html_text(L("instance"))), + xs_html_text(" "), + xs_html_tag("form", + xs_html_attr("style", "display: inline!important"), + xs_html_attr("class", "snac-search-box"), + xs_html_attr("action", admin_url), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", "q"), + xs_html_attr("title", L("Search posts by content (regular expression)")), + xs_html_attr("placeholder", L("Content search"))))); } xs_html_add(body, From 1762e0f1459599524b54aabfd470e171e1d6e592 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 09:29:38 +0200 Subject: [PATCH 086/120] Added an incremental max seconds value to every content search page. --- html.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/html.c b/html.c index a0a9cf7..0cdb608 100644 --- a/html.c +++ b/html.c @@ -2525,7 +2525,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int skip = 0; int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); - char *q_vars = xs_dict_get(req, "q_vars"); + xs_dict *q_vars = xs_dict_get(req, "q_vars"); if ((v = xs_dict_get(q_vars, "skip")) != NULL) skip = atoi(v), cache = 0, save = 0; if ((v = xs_dict_get(q_vars, "show")) != NULL) @@ -2575,9 +2575,10 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (q && *q) { /** search by content **/ int to = 0; - xs *tl = content_search(&snac, q, 1, skip, show, skip ? 10 : 0, &to); + int msecs = atoi(xs_dict_get_def(q_vars, "msecs", "0")); + xs *tl = content_search(&snac, q, 1, skip, show, msecs, &to); xs *title = NULL; - xs *page = xs_fmt("/admin?q=%s", q); + xs *page = xs_fmt("/admin?q=%s&msecs=%d", q, msecs + 10); int tl_len = xs_list_len(tl); if (tl_len) From 32646aa786851ecaed7121f31afaf6df2d5eeb16 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 09:37:20 +0200 Subject: [PATCH 087/120] Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index acb697d..5720a63 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,11 @@ # Release Notes +## 2.53 + +New search feature (post content, using regular expressions). + +Minor fixes: Allow unboosting your own posts (contributed by khm), CSS fixes for the Dillo browser (contributed by kvibber). + ## 2.52 Posts that were liked or boosted can now be unliked and unboosted. From a0d98f3d2ad06571983caa155e1bbd8b2012fbb3 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 09:37:34 +0200 Subject: [PATCH 088/120] Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 64d48f2..c4e062b 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ -#define VERSION "2.52" +#define VERSION "2.53-dev" #define USER_AGENT "snac/" VERSION From 3954361785c1384272b0e9fc7262bb25441101de Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 18:38:18 +0200 Subject: [PATCH 089/120] Updated TODO. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index bdd7497..7411ba1 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,8 @@ Editing / Updating a post does not index newly added hashtags. ## Wishlist +Implement support for 'Event' data types. Example: https://fediversity.site/item/e9bdb383-eeb9-4d7d-b2f7-c6401267cae0 + Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md Track "FEP-ef61: Portable Objects" https://codeberg.org/fediverse/fep/src/branch/main/fep/ef61/fep-ef61.md From e1ce85debd5b192b5dc56a4e9810839cc52b0f8b Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 18:46:15 +0200 Subject: [PATCH 090/120] Unified post-like object type match. --- activitypub.c | 2 +- data.c | 2 +- html.c | 4 ++-- mastoapi.c | 4 ++-- snac.h | 2 ++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/activitypub.c b/activitypub.c index 301fd4c..4a04000 100644 --- a/activitypub.c +++ b/activitypub.c @@ -355,7 +355,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) type = "(null)"; } - if (xs_match(type, "Note|Page|Article|Video")) { + if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { const char *actor = get_atto(object); if (content_check("filter_reject.txt", object)) diff --git a/data.c b/data.c index d037af0..108354c 100644 --- a/data.c +++ b/data.c @@ -2577,7 +2577,7 @@ xs_list *content_search(snac *user, const char *regex, if (!valid_status(timeline_get_by_md5(user, md5, &post))) continue; - if (!xs_match(xs_dict_get_def(post, "type", "-"), "Note|Question|Page|Article|Video")) + if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) continue; char *content = xs_dict_get(post, "content"); diff --git a/html.c b/html.c index 0cdb608..12dba6b 100644 --- a/html.c +++ b/html.c @@ -256,7 +256,7 @@ xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg) int priv = 0; const char *type = xs_dict_get(msg, "type"); - if (xs_match(type, "Note|Question|Page|Article|Video")) + if (xs_match(type, POSTLIKE_OBJECT_TYPE)) url = xs_dict_get(msg, "id"); priv = !is_msg_public(msg); @@ -1405,7 +1405,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg))); } else - if (!xs_match(type, "Note|Question|Page|Article|Video")) { + if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { /* skip oddities */ return NULL; } diff --git a/mastoapi.c b/mastoapi.c index 1071bfd..2bf5fdc 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1504,7 +1504,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* discard non-Notes */ const char *id = xs_dict_get(msg, "id"); const char *type = xs_dict_get(msg, "type"); - if (!xs_match(type, "Note|Question|Page|Article|Video")) + if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) continue; const char *from = NULL; @@ -1681,7 +1681,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* discard non-Notes */ const char *id = xs_dict_get(msg, "id"); const char *type = xs_dict_get(msg, "type"); - if (!xs_match(type, "Note|Question|Page|Article|Video")) + if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) continue; const char *from = NULL; diff --git a/snac.h b/snac.h index c4e062b..c585e3f 100644 --- a/snac.h +++ b/snac.h @@ -29,6 +29,8 @@ extern int dbglevel; #define L(s) (s) +#define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video" + int mkdirx(const char *pathname); int valid_status(int status); From 6f3763f452ba55ee6ddd9f63f5bc523807e7288f Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 19:15:18 +0200 Subject: [PATCH 091/120] Show 'Event' object types as Notes. --- format.c | 5 ++--- html.c | 1 + snac.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 8d51351..631c11c 100644 --- a/format.c +++ b/format.c @@ -311,9 +311,8 @@ xs_str *sanitize(const char *content) s = xs_str_cat(s, s2); } else { - /* else? just show it with encoded code.. that's it. */ - xs *el = encode_html(v); - s = xs_str_cat(s, el); + if (strcmp(v, "")) + s = xs_str_cat(s, "

"); } } else { diff --git a/html.c b/html.c index 12dba6b..201456e 100644 --- a/html.c +++ b/html.c @@ -1407,6 +1407,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, else if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { /* skip oddities */ + snac_debug(user, 1, xs_fmt("html_entry: ignoring object type '%s' %s", type, id)); return NULL; } diff --git a/snac.h b/snac.h index c585e3f..239f27a 100644 --- a/snac.h +++ b/snac.h @@ -29,7 +29,7 @@ extern int dbglevel; #define L(s) (s) -#define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video" +#define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video|Event" int mkdirx(const char *pathname); From 4d53a7b6f7f45f524313980461f5d2a6ef965949 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 19:16:54 +0200 Subject: [PATCH 092/120] In timeline_request(), call enqueue_actor_request on actor errors. --- activitypub.c | 26 +++++++++++++++----------- data.c | 11 ++++++----- snac.h | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/activitypub.c b/activitypub.c index 301fd4c..280ac87 100644 --- a/activitypub.c +++ b/activitypub.c @@ -356,23 +356,27 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) } if (xs_match(type, "Note|Page|Article|Video")) { - const char *actor = get_atto(object); - if (content_check("filter_reject.txt", object)) snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); else { - /* request (and drop) the actor for this entry */ - if (!xs_is_null(actor)) - actor_request(snac, actor, NULL); + const char *actor = get_atto(object); - /* does it have an ancestor? */ - char *in_reply_to = xs_dict_get(object, "inReplyTo"); + if (!xs_is_null(actor)) { + /* request (and drop) the actor for this entry */ + if (!valid_status(actor_request(snac, actor, NULL))) { + /* failed? retry later */ + enqueue_actor_refresh(snac, actor, 60); + } - /* store */ - timeline_add(snac, nid, object); + /* does it have an ancestor? */ + char *in_reply_to = xs_dict_get(object, "inReplyTo"); - /* recurse! */ - timeline_request(snac, &in_reply_to, NULL, level + 1); + /* store */ + timeline_add(snac, nid, object); + + /* recurse! */ + timeline_request(snac, &in_reply_to, NULL, level + 1); + } } } } diff --git a/data.c b/data.c index d037af0..8016ce1 100644 --- a/data.c +++ b/data.c @@ -1632,7 +1632,7 @@ int actor_get_refresh(snac *user, const char *actor, xs_dict **data) int status = actor_get(actor, data); if (status == 205 && user && !xs_startswith(actor, srv_baseurl)) - enqueue_actor_refresh(user, actor); + enqueue_actor_refresh(user, actor, 0); return status; } @@ -2830,13 +2830,14 @@ void enqueue_verify_links(snac *user) } -void enqueue_actor_refresh(snac *user, const char *actor) +void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs) /* enqueues an actor refresh */ { - xs *qmsg = _new_qmsg("actor_refresh", "", 0); - char *ntid = xs_dict_get(qmsg, "ntid"); - xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); + xs *qmsg = _new_qmsg("actor_refresh", "", 0); + xs *ntid = tid(forward_secs); + xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); + qmsg = xs_dict_set(qmsg, "ntid", ntid); qmsg = xs_dict_append(qmsg, "actor", actor); qmsg = _enqueue_put(fn, qmsg); diff --git a/snac.h b/snac.h index c4e062b..de2ee1e 100644 --- a/snac.h +++ b/snac.h @@ -232,7 +232,7 @@ void enqueue_message(snac *snac, const xs_dict *msg); void enqueue_close_question(snac *user, const char *id, int end_secs); void enqueue_object_request(snac *user, const char *id, int forward_secs); void enqueue_verify_links(snac *user); -void enqueue_actor_refresh(snac *user, const char *actor); +void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs); void enqueue_request_replies(snac *user, const char *id); int was_question_voted(snac *user, const char *id); From 9acba489fb37f0d3daca45aad17df96a6360efe5 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 19:35:33 +0200 Subject: [PATCH 093/120] Add some special treatment to Event object display. --- format.c | 1 + html.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 631c11c..df3b5d9 100644 --- a/format.c +++ b/format.c @@ -311,6 +311,7 @@ xs_str *sanitize(const char *content) s = xs_str_cat(s, s2); } else { + /* treat end of divs as paragraph breaks */ if (strcmp(v, "")) s = xs_str_cat(s, "

"); } diff --git a/html.c b/html.c index 201456e..10b1427 100644 --- a/html.c +++ b/html.c @@ -1374,6 +1374,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, char *type = xs_dict_get(msg, "type"); char *actor; char *v; + int has_title = 0; /* do not show non-public messages in the public timeline */ if ((read_only || !user) && !is_msg_public(msg)) @@ -1484,6 +1485,14 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } } + if (strcmp(type, "Event") == 0) { + /* add the calendar emoji */ + xs_html_add(score, + xs_html_tag("span", + xs_html_attr("title", L("Event")), + xs_html_raw(" 📅 "))); + } + /* if it's a user from this same instance, add the score */ if (xs_startswith(id, srv_baseurl)) { int n_likes = object_likes_len(id); @@ -1575,11 +1584,13 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_add(entry, snac_content_wrap); - if (!xs_is_null(v = xs_dict_get(msg, "name"))) { + if (!has_title && !xs_is_null(v = xs_dict_get(msg, "name"))) { xs_html_add(snac_content_wrap, xs_html_tag("h3", xs_html_attr("class", "snac-entry-title"), xs_html_text(v))); + + has_title = 1; } xs_html *snac_content = NULL; @@ -1604,12 +1615,15 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } else { /* print the summary as a header (sites like e.g. Friendica can contain one) */ - if (!xs_is_null(v) && *v) + if (!has_title && !xs_is_null(v) && *v) { xs_html_add(snac_content_wrap, xs_html_tag("h3", xs_html_attr("class", "snac-entry-title"), xs_html_text(v))); + has_title = 1; + } + snac_content = xs_html_tag("div", NULL); } From ab9a1d4b1be5feddcdf00ec619ef4ef7d12faffd Mon Sep 17 00:00:00 2001 From: default Date: Sat, 11 May 2024 19:45:48 +0200 Subject: [PATCH 094/120] Updated TODO. --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 7411ba1..c921898 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,7 @@ Editing / Updating a post does not index newly added hashtags. ## Wishlist -Implement support for 'Event' data types. Example: https://fediversity.site/item/e9bdb383-eeb9-4d7d-b2f7-c6401267cae0 +Implement support for 'Event' data types. Example: https://fediversity.site/item/e9bdb383-eeb9-4d7d-b2f7-c6401267cae0 (more info: https://event-federation.eu/) Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md From 61b9ad8ee56ee66247c5e23d501e22c0fa07b626 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 May 2024 08:57:38 +0200 Subject: [PATCH 095/120] Updated TODO. --- TODO.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index c921898..db19a14 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,7 @@ Editing / Updating a post does not index newly added hashtags. ## Wishlist -Implement support for 'Event' data types. Example: https://fediversity.site/item/e9bdb383-eeb9-4d7d-b2f7-c6401267cae0 (more info: https://event-federation.eu/) +Track 'Event' data types standardization; how to add plan-to-attend and similar activities (more info: https://event-federation.eu/) Implement "FEP-3b86: Activity Intents" https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md @@ -313,3 +313,5 @@ Post edits should preserve the image and the image description somewhat (2024-03 Integrate "Ability to federate with hidden networks" see https://codeberg.org/grunfink/snac2/issues/93 Consider adding milter-like support to reject posts to mitigate spam (discarded; 2024-04-20T22:46:35+0200). + +Implement support for 'Event' data types. Example: https://fediversity.site/item/e9bdb383-eeb9-4d7d-b2f7-c6401267cae0 (2024-05-12T08:56:27+0200) From ba00443b84f3c7663d567e65ddfd7ea9257d6ce1 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 12 May 2024 09:02:38 +0200 Subject: [PATCH 096/120] 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 5720a63..de3759a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,7 +2,9 @@ ## 2.53 -New search feature (post content, using regular expressions). +New feature to search by post content (using regular expressions). + +Added some (partial) support for `Event` object types. Minor fixes: Allow unboosting your own posts (contributed by khm), CSS fixes for the Dillo browser (contributed by kvibber). From 281f934f74c662d3c2e58db556f3046a736a2f00 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 14 May 2024 05:17:33 +0200 Subject: [PATCH 097/120] content_search() also searches the instance timeline. --- data.c | 79 ++++++++++++++++++++++++---------------------------------- html.c | 2 +- 2 files changed, 34 insertions(+), 47 deletions(-) diff --git a/data.c b/data.c index 706e8e1..6df5f5c 100644 --- a/data.c +++ b/data.c @@ -2508,73 +2508,56 @@ xs_list *content_search(snac *user, const char *regex, time_t t = time(NULL) + max_secs; *timeout = 0; - /* iterate both timelines simultaneously */ - xs *pub_tl = timeline_simple_list(user, "public", 0, XS_ALL); - int pub_c = 0; - char *pub_md5 = NULL; + /* iterate all timelines simultaneously */ + xs_list *tls[3] = {0}; + char *md5s[3] = {0}; + int c[3] = {0}; - xs *priv_tl = priv ? timeline_simple_list(user, "private", 0, XS_ALL) : xs_list_new(); - int priv_c = 0; - char *priv_md5 = NULL; + tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* 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 */ /* first positioning */ - xs_list_next(pub_tl, &pub_md5, &pub_c); - xs_list_next(priv_tl, &priv_md5, &priv_c); + for (int n = 0; n < 3; n++) + xs_list_next(tls[n], &md5s[n], &c[n]); show += skip; while (show > 0) { - char *md5 = NULL; - enum { NONE, PUBLIC, PRIVATE } from = NONE; - /* timeout? */ if (time(NULL) > t) { *timeout = 1; break; } - if (pub_md5 == NULL) { - /* out of both lists? done */ - if (priv_md5 == NULL) - break; + /* find the newest post */ + int newest = -1; + double mtime = 0.0; - /* out of public: take element from the private timeline and advance */ - from = PRIVATE; - } - else - if (priv_md5 == NULL) { - /* out of private: take element from the public timeline and advance */ - from = PUBLIC; - } - else { - /* candidates from both: choose one from the file dates */ - xs *pub_fn = xs_fmt("%s/public/%s.json", user->basedir, pub_md5); - xs *priv_fn = xs_fmt("%s/private/%s.json", user->basedir, priv_md5); + 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); - if (mtime(pub_fn) < mtime(priv_fn)) - from = PRIVATE; - else - from = PUBLIC; + if (mt > mtime) { + newest = n; + mtime = mt; + } + } } - if (from == PUBLIC) { /* public */ - md5 = pub_md5; - if (!xs_list_next(pub_tl, &pub_md5, &pub_c)) - pub_md5 = NULL; - } - else - if (from == PRIVATE) { /* private */ - md5 = priv_md5; - if (!xs_list_next(priv_tl, &priv_md5, &priv_c)) - priv_md5 = NULL; - } - - if (md5 == NULL) + if (newest == -1) break; + char *md5 = md5s[newest]; + + /* advance the chosen timeline */ + if (!xs_list_next(tls[newest], &md5s[newest], &c[newest])) + md5s[newest] = NULL; + xs *post = NULL; - if (!valid_status(timeline_get_by_md5(user, md5, &post))) + if (!valid_status(object_get_by_md5(md5, &post))) continue; if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) @@ -2608,6 +2591,10 @@ xs_list *content_search(snac *user, const char *regex, } } + xs_free(tls[0]); + xs_free(tls[1]); + xs_free(tls[2]); + return r; } diff --git a/html.c b/html.c index 10b1427..2b0436e 100644 --- a/html.c +++ b/html.c @@ -2604,7 +2604,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, else title = xs_fmt(L("Nothing found for '%s'"), q); - *body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 1); + *body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 0); *b_size = strlen(*body); status = 200; } From 34311714ce725da9fae9fea3b29a4988977c79bd Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 05:57:21 +0200 Subject: [PATCH 098/120] User search can also be done by tag. --- html.c | 55 +++++++++++++++++++++++++++++++++++++------------------ httpd.c | 2 +- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/html.c b/html.c index 2b0436e..6351823 100644 --- a/html.c +++ b/html.c @@ -748,7 +748,7 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_sctag("input", xs_html_attr("type", "text"), xs_html_attr("name", "q"), - xs_html_attr("title", L("Search posts by content (regular expression)")), + xs_html_attr("title", L("Search posts by content (regular expression) or #tag")), xs_html_attr("placeholder", L("Content search"))))); } @@ -2588,25 +2588,44 @@ int html_get_handler(const xs_dict *req, const char *q_path, char *q = xs_dict_get(q_vars, "q"); if (q && *q) { - /** search by content **/ - int to = 0; - int msecs = atoi(xs_dict_get_def(q_vars, "msecs", "0")); - xs *tl = content_search(&snac, q, 1, skip, show, msecs, &to); - xs *title = NULL; - xs *page = xs_fmt("/admin?q=%s&msecs=%d", q, msecs + 10); - int tl_len = xs_list_len(tl); + if (*q == '#') { + /** search by tag **/ + xs *tl = tag_search(q, skip, show + 1); + int more = 0; + if (xs_list_len(tl) >= show + 1) { + /* drop the last one */ + tl = xs_list_del(tl, -1); + more = 1; + } - if (tl_len) - title = xs_fmt(L("Search results for '%s'"), q); - else - if (skip) - title = xs_fmt(L("No more matches for '%s'"), q); - else - title = xs_fmt(L("Nothing found for '%s'"), q); + xs *page = xs_fmt("/admin?q=%%23%s", q + 1); + xs *title = xs_fmt(L("Search results for tag %s"), q); - *body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 0); - *b_size = strlen(*body); - status = 200; + *body = html_timeline(&snac, tl, 0, skip, show, more, title, page, 0); + *b_size = strlen(*body); + status = 200; + } + else { + /** search by content **/ + int to = 0; + int msecs = atoi(xs_dict_get_def(q_vars, "msecs", "0")); + xs *tl = content_search(&snac, q, 1, skip, show, msecs, &to); + xs *title = NULL; + xs *page = xs_fmt("/admin?q=%s&msecs=%d", q, msecs + 10); + int tl_len = xs_list_len(tl); + + if (tl_len) + title = xs_fmt(L("Search results for '%s'"), q); + else + if (skip) + title = xs_fmt(L("No more matches for '%s'"), q); + else + 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); + *b_size = strlen(*body); + status = 200; + } } else { double t = history_mtime(&snac, "timeline.html_"); diff --git a/httpd.c b/httpd.c index 81b0853..71cce5e 100644 --- a/httpd.c +++ b/httpd.c @@ -202,7 +202,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 #%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); } } From 2ed0a08f9fbaac8adbf1d84aa149b9f7959a965b Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 06:07:33 +0200 Subject: [PATCH 099/120] Convert the regex to lowercase in content_search(). Is this a good idea? --- data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 6df5f5c..2410f5b 100644 --- a/data.c +++ b/data.c @@ -2498,6 +2498,8 @@ xs_list *content_search(snac *user, const char *regex, if (regex == NULL || *regex == '\0') return xs_list_new(); + xs *i_regex = xs_tolower_i(xs_dup(regex)); + xs_set seen; xs_set_init(&seen); @@ -2574,7 +2576,7 @@ xs_list *content_search(snac *user, const char *regex, c = xs_tolower_i(c); /* apply regex */ - xs *l = xs_regex_select_n(c, regex, 1); + xs *l = xs_regex_select_n(c, i_regex, 1); if (xs_list_len(l)) { if (xs_set_add(&seen, md5) == 1) From d5316497760bffd869ff2b80bebaa613798a6476 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 07:52:35 +0200 Subject: [PATCH 100/120] 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 de3759a..cea217a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,7 +2,7 @@ ## 2.53 -New feature to search by post content (using regular expressions). +New user feature to search by post content (using regular expressions) or tag. Added some (partial) support for `Event` object types. From ff8d49a8991977e07cf6bb50156b2ffda08d0c0e Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 13:27:23 +0200 Subject: [PATCH 101/120] Use xs_regex_match() where applicable. --- data.c | 14 ++++---------- xs.h | 2 +- xs_regex.h | 34 +++++++++++++++++++++++----------- xs_version.h | 2 +- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/data.c b/data.c index 2410f5b..65100de 100644 --- a/data.c +++ b/data.c @@ -2293,13 +2293,9 @@ int content_check(const char *file, const xs_dict *msg) while (!r && !feof(f)) { xs *rx = xs_strip_i(xs_readline(f)); - if (*rx) { - xs *l = xs_regex_select_n(c, rx, 1); - - if (xs_list_len(l)) { - srv_debug(1, xs_fmt("content_check: match for '%s'", rx)); - r = 1; - } + if (*rx && xs_regex_match(c, rx)) { + srv_debug(1, xs_fmt("content_check: match for '%s'", rx)); + r = 1; } } @@ -2576,9 +2572,7 @@ xs_list *content_search(snac *user, const char *regex, c = xs_tolower_i(c); /* apply regex */ - xs *l = xs_regex_select_n(c, i_regex, 1); - - if (xs_list_len(l)) { + if (xs_regex_match(c, i_regex)) { if (xs_set_add(&seen, md5) == 1) show--; } diff --git a/xs.h b/xs.h index ad72207..f5c87ef 100644 --- a/xs.h +++ b/xs.h @@ -1049,7 +1049,7 @@ xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) /* prepends a memory block to the dict */ { - return _xs_dict_write_ditem(dict, 4, key, value, xs_size(value)); + return _xs_dict_write_ditem(dict, 1 + _XS_TYPE_SIZE, key, value, xs_size(value)); } diff --git a/xs_regex.h b/xs_regex.h index 1adbcf8..cb73a01 100644 --- a/xs_regex.h +++ b/xs_regex.h @@ -4,6 +4,7 @@ #define _XS_REGEX_H +int xs_regex_match(const char *str, const char *rx); xs_list *xs_regex_split_n(const char *str, const char *rx, int count); #define xs_regex_split(str, rx) xs_regex_split_n(str, rx, XS_ALL) xs_list *xs_regex_select_n(const char *str, const char *rx, int count); @@ -18,18 +19,21 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c #include xs_list *xs_regex_split_n(const char *str, const char *rx, int count) -/* splits str by regex */ +/* splits str using regex as a separator, at most count times. + Always returns a list: + len == 0: regcomp error + len == 1: full string (no matches) + len == odd: first part [ separator / next part ]... +*/ { regex_t re; regmatch_t rm; int offset = 0; - xs_list *list = NULL; + xs_list *list = xs_list_new(); const char *p; if (regcomp(&re, rx, REG_EXTENDED)) - return NULL; - - list = xs_list_new(); + return list; while (count > 0 && !regexec(&re, (p = str + offset), 1, &rm, offset > 0 ? REG_NOTBOL : 0)) { /* add first the leading part of the string */ @@ -60,16 +64,15 @@ xs_list *xs_regex_select_n(const char *str, const char *rx, int count) { xs_list *list = xs_list_new(); xs *split = NULL; - xs_list *p; xs_val *v; int n = 0; + int c = 0; /* split */ split = xs_regex_split_n(str, rx, count); /* now iterate to get only the 'separators' (odd ones) */ - p = split; - while (xs_list_iter(&p, &v)) { + while (xs_list_next(split, &v, &c)) { if (n & 0x1) list = xs_list_append(list, v); @@ -86,13 +89,12 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c { xs_str *s = xs_str_new(NULL); xs *split = xs_regex_split_n(str, rx, count); - xs_list *p; xs_val *v; int n = 0; + int c = 0; int pholder = !!strchr(rep, '&'); - p = split; - while (xs_list_iter(&p, &v)) { + while (xs_list_next(split, &v, &c)) { if (n & 0x1) { if (pholder) { /* rep has a placeholder; process char by char */ @@ -128,6 +130,16 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c return s; } + +int xs_regex_match(const char *str, const char *rx) +/* returns if str matches the regex at least once */ +{ + xs *l = xs_regex_select_n(str, rx, 1); + + return xs_list_len(l) == 1; +} + + #endif /* XS_IMPLEMENTATION */ #endif /* XS_REGEX_H */ diff --git a/xs_version.h b/xs_version.h index a672ef4..16faf2b 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* f3818ad611f09313008a2102a5e543c232e1d824 2024-05-02T23:45:38+02:00 */ +/* 6e75e8736f7f1b6ea6c6774d4bd922b3ad56b771 2024-05-15T11:42:19+02:00 */ From 0a6cfba399cbd7510b6ffa9ddfde5a8ca31c4828 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 13:29:07 +0200 Subject: [PATCH 102/120] Moved functions around. --- data.c | 220 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 109 insertions(+), 111 deletions(-) diff --git a/data.c b/data.c index 65100de..61045c6 100644 --- a/data.c +++ b/data.c @@ -2270,7 +2270,7 @@ int instance_unblock(const char *instance) } -/** content filtering **/ +/** operations by content **/ int content_check(const char *file, const xs_dict *msg) /* checks if a message's content matches any of the regexes in file */ @@ -2307,6 +2307,114 @@ int content_check(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) +/* returns a list of posts which content matches the regex */ +{ + if (regex == NULL || *regex == '\0') + return xs_list_new(); + + xs *i_regex = xs_tolower_i(xs_dup(regex)); + + xs_set seen; + + xs_set_init(&seen); + + if (max_secs == 0) + max_secs = 3; + + time_t t = time(NULL) + max_secs; + *timeout = 0; + + /* iterate all timelines simultaneously */ + xs_list *tls[3] = {0}; + char *md5s[3] = {0}; + int c[3] = {0}; + + tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* 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 */ + + /* first positioning */ + for (int n = 0; n < 3; n++) + xs_list_next(tls[n], &md5s[n], &c[n]); + + show += skip; + + while (show > 0) { + /* timeout? */ + if (time(NULL) > t) { + *timeout = 1; + break; + } + + /* find the newest post */ + int newest = -1; + double mtime = 0.0; + + 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); + + if (mt > mtime) { + newest = n; + mtime = mt; + } + } + } + + if (newest == -1) + break; + + char *md5 = md5s[newest]; + + /* advance the chosen timeline */ + if (!xs_list_next(tls[newest], &md5s[newest], &c[newest])) + md5s[newest] = NULL; + + xs *post = NULL; + + if (!valid_status(object_get_by_md5(md5, &post))) + continue; + + if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) + continue; + + char *content = xs_dict_get(post, "content"); + + if (xs_is_null(content)) + continue; + + /* strip HTML */ + xs *c = xs_regex_replace(content, "<[^>]+>", " "); + c = xs_regex_replace_i(c, " {2,}", " "); + c = xs_tolower_i(c); + + /* apply regex */ + if (xs_regex_match(c, i_regex)) { + if (xs_set_add(&seen, md5) == 1) + show--; + } + } + + xs_list *r = xs_set_result(&seen); + + if (skip) { + /* BAD */ + while (skip--) { + r = xs_list_del(r, 0); + } + } + + xs_free(tls[0]); + xs_free(tls[1]); + xs_free(tls[2]); + + return r; +} + + /** notifications **/ xs_str *notify_check_time(snac *snac, int reset) @@ -2485,116 +2593,6 @@ void notify_clear(snac *snac) } -/** searches **/ - -xs_list *content_search(snac *user, const char *regex, - int priv, int skip, int show, int max_secs, int *timeout) -/* returns a list of posts which content matches the regex */ -{ - if (regex == NULL || *regex == '\0') - return xs_list_new(); - - xs *i_regex = xs_tolower_i(xs_dup(regex)); - - xs_set seen; - - xs_set_init(&seen); - - if (max_secs == 0) - max_secs = 3; - - time_t t = time(NULL) + max_secs; - *timeout = 0; - - /* iterate all timelines simultaneously */ - xs_list *tls[3] = {0}; - char *md5s[3] = {0}; - int c[3] = {0}; - - tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* 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 */ - - /* first positioning */ - for (int n = 0; n < 3; n++) - xs_list_next(tls[n], &md5s[n], &c[n]); - - show += skip; - - while (show > 0) { - /* timeout? */ - if (time(NULL) > t) { - *timeout = 1; - break; - } - - /* find the newest post */ - int newest = -1; - double mtime = 0.0; - - 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); - - if (mt > mtime) { - newest = n; - mtime = mt; - } - } - } - - if (newest == -1) - break; - - char *md5 = md5s[newest]; - - /* advance the chosen timeline */ - if (!xs_list_next(tls[newest], &md5s[newest], &c[newest])) - md5s[newest] = NULL; - - xs *post = NULL; - - if (!valid_status(object_get_by_md5(md5, &post))) - continue; - - if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) - continue; - - char *content = xs_dict_get(post, "content"); - - if (xs_is_null(content)) - continue; - - /* strip HTML */ - xs *c = xs_regex_replace(content, "<[^>]+>", " "); - c = xs_regex_replace_i(c, " {2,}", " "); - c = xs_tolower_i(c); - - /* apply regex */ - if (xs_regex_match(c, i_regex)) { - if (xs_set_add(&seen, md5) == 1) - show--; - } - } - - xs_list *r = xs_set_result(&seen); - - if (skip) { - /* BAD */ - while (skip--) { - r = xs_list_del(r, 0); - } - } - - xs_free(tls[0]); - xs_free(tls[1]); - xs_free(tls[2]); - - return r; -} - - /** the queue **/ static xs_dict *_enqueue_put(const char *fn, xs_dict *msg) From 0a6df8e504569be4caf39930c473f12b328e5b2a Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 13:31:53 +0200 Subject: [PATCH 103/120] Renamed content_check() to content_match(). --- activitypub.c | 4 ++-- data.c | 6 +++--- snac.h | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/activitypub.c b/activitypub.c index dee127d..231715b 100644 --- a/activitypub.c +++ b/activitypub.c @@ -356,7 +356,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) } if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { - if (content_check("filter_reject.txt", object)) + if (content_match("filter_reject.txt", object)) snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); else { const char *actor = get_atto(object); @@ -2005,7 +2005,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to)); } else { - if (content_check("filter_reject.txt", object)) { + if (content_match("filter_reject.txt", object)) { snac_log(snac, xs_fmt("rejected by content %s", id)); return 1; } diff --git a/data.c b/data.c index 61045c6..5abbeaf 100644 --- a/data.c +++ b/data.c @@ -2272,7 +2272,7 @@ int instance_unblock(const char *instance) /** operations by content **/ -int content_check(const char *file, const xs_dict *msg) +int content_match(const char *file, const xs_dict *msg) /* checks if a message's content matches any of the regexes in file */ /* file format: one regex per line */ { @@ -2283,7 +2283,7 @@ int content_check(const char *file, const xs_dict *msg) if (xs_type(v) == XSTYPE_STRING && *v) { if ((f = fopen(fn, "r")) != NULL) { - srv_debug(1, xs_fmt("content_check: loading regexes from %s", fn)); + srv_debug(1, xs_fmt("content_match: loading regexes from %s", fn)); /* massage content (strip HTML tags, etc.) */ xs *c = xs_regex_replace(v, "<[^>]+>", " "); @@ -2294,7 +2294,7 @@ int content_check(const char *file, const xs_dict *msg) xs *rx = xs_strip_i(xs_readline(f)); if (*rx && xs_regex_match(c, rx)) { - srv_debug(1, xs_fmt("content_check: match for '%s'", rx)); + srv_debug(1, xs_fmt("content_match: match for '%s'", rx)); r = 1; } } diff --git a/snac.h b/snac.h index 91033cf..e679d8c 100644 --- a/snac.h +++ b/snac.h @@ -181,9 +181,6 @@ xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -xs_list *content_search(snac *user, const char *regex, - int priv, int skip, int show, int max_secs, int *timeout); - int actor_add(const char *actor, xs_dict *msg); int actor_get(const char *actor, xs_dict **data); int actor_get_refresh(snac *user, const char *actor, xs_dict **data); @@ -219,7 +216,9 @@ int is_instance_blocked(const char *instance); int instance_block(const char *instance); int instance_unblock(const char *instance); -int content_check(const char *file, const xs_dict *msg); +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); 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); From e7fa6d992061b04038128320acef4c894ae69500 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 May 2024 13:36:59 +0200 Subject: [PATCH 104/120] Skip hidden posts in content_search(). --- data.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data.c b/data.c index 5abbeaf..0cceefd 100644 --- a/data.c +++ b/data.c @@ -2381,6 +2381,11 @@ xs_list *content_search(snac *user, const char *regex, if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) continue; + const char *id = xs_dict_get(post, "id"); + + if (id == NULL || is_hidden(user, id)) + continue; + char *content = xs_dict_get(post, "content"); if (xs_is_null(content)) From b95fbe4e438a2ab8a8625875e2eedac38dae572f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 May 2024 15:47:43 +0200 Subject: [PATCH 105/120] Version 2.53 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index e679d8c..b49fbe7 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ -#define VERSION "2.53-dev" +#define VERSION "2.53" #define USER_AGENT "snac/" VERSION From 4777fc86cb962917a8f34afb3bfa40f26290815d Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 May 2024 14:12:15 +0200 Subject: [PATCH 106/120] Added const everywhere. --- activitypub.c | 283 +++++++++++++++++++------------------------------- data.c | 130 +++++++++-------------- html.c | 278 +++++++++++++++++++++++++------------------------ http.c | 27 ++--- httpd.c | 16 +-- main.c | 2 +- mastoapi.c | 109 +++++++++---------- snac.h | 45 ++++---- upgrade.c | 40 ++++--- utils.c | 2 +- webfinger.c | 23 ++-- xs.h | 110 ++++++++++++++------ xs_curl.h | 2 +- xs_html.h | 34 +++--- xs_httpd.h | 5 +- xs_json.h | 9 +- xs_set.h | 2 +- xs_unicode.h | 6 +- xs_url.h | 4 +- xs_version.h | 2 +- 20 files changed, 549 insertions(+), 580 deletions(-) diff --git a/activitypub.c b/activitypub.c index 231715b..f4b4eac 100644 --- a/activitypub.c +++ b/activitypub.c @@ -67,7 +67,7 @@ int activitypub_request(snac *user, const char *url, xs_dict **data) xs *response = NULL; xs *payload = NULL; int p_size; - char *ctype; + const char *ctype; *data = NULL; @@ -154,20 +154,21 @@ int actor_request(snac *user, const char *actor, xs_dict **data) } -char *get_atto(const xs_dict *msg) +const char *get_atto(const xs_dict *msg) /* gets the attributedTo field (an actor) */ { - char *actor = xs_dict_get(msg, "attributedTo"); + const xs_val *actor = xs_dict_get(msg, "attributedTo"); /* if the actor is a list of objects (like on Peertube videos), pick the Person */ if (xs_type(actor) == XSTYPE_LIST) { - xs_list *p = actor; + const xs_list *p = actor; + int c = 0; xs_dict *v; actor = NULL; - while (actor == NULL && xs_list_iter(&p, &v)) { + while (actor == NULL && xs_list_next(p, &v, &c)) { if (xs_type(v) == XSTYPE_DICT) { - char *type = xs_dict_get(v, "type"); + const char *type = xs_dict_get(v, "type"); if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) { actor = xs_dict_get(v, "id"); @@ -186,7 +187,7 @@ xs_list *get_attachments(const xs_dict *msg) /* unify the garbage fire that are the attachments */ { xs_list *l = xs_list_new(); - xs_list *p; + const xs_list *p; /* try first the attachments list */ if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) { @@ -203,23 +204,24 @@ xs_list *get_attachments(const xs_dict *msg) if (xs_type(attach) == XSTYPE_LIST) { /* does the message have an image? */ - if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) { + const xs_dict *d = xs_dict_get(msg, "image"); + if (xs_type(d) == XSTYPE_DICT) { /* add it to the attachment list */ - attach = xs_list_append(attach, v); + attach = xs_list_append(attach, d); } } /* now iterate the list */ - p = attach; - while (xs_list_iter(&p, &v)) { - char *type = xs_dict_get(v, "mediaType"); + int c = 0; + while (xs_list_next(attach, &v, &c)) { + const char *type = xs_dict_get(v, "mediaType"); if (xs_is_null(type)) type = xs_dict_get(v, "type"); if (xs_is_null(type)) continue; - char *href = xs_dict_get(v, "url"); + const char *href = xs_dict_get(v, "url"); if (xs_is_null(href)) href = xs_dict_get(v, "href"); if (xs_is_null(href)) @@ -233,7 +235,7 @@ xs_list *get_attachments(const xs_dict *msg) type = mt; } - char *name = xs_dict_get(v, "name"); + const char *name = xs_dict_get(v, "name"); if (xs_is_null(name)) name = xs_dict_get(msg, "name"); if (xs_is_null(name)) @@ -252,29 +254,31 @@ xs_list *get_attachments(const xs_dict *msg) p = xs_dict_get(msg, "url"); if (xs_type(p) == XSTYPE_LIST) { - char *href = NULL; - char *type = NULL; + const char *href = NULL; + const char *type = NULL; + int c = 0; xs_val *v; - while (href == NULL && xs_list_iter(&p, &v)) { + while (href == NULL && xs_list_next(p, &v, &c)) { if (xs_type(v) == XSTYPE_DICT) { - char *mtype = xs_dict_get(v, "type"); + const char *mtype = xs_dict_get(v, "type"); if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) { mtype = xs_dict_get(v, "mediaType"); - xs_list *tag = xs_dict_get(v, "tag"); + const xs_list *tag = xs_dict_get(v, "tag"); if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "application/x-mpegURL") == 0 && xs_type(tag) == XSTYPE_LIST) { /* now iterate the tag list, looking for a video URL */ xs_dict *d; + int c = 0; - while (href == NULL && xs_list_iter(&tag, &d)) { + while (href == NULL && xs_list_next(tag, &d, &c)) { if (xs_type(d) == XSTYPE_DICT) { if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING && xs_startswith(mtype, "video/")) { - char *h = xs_dict_get(d, "href"); + const char *h = xs_dict_get(d, "href"); /* this is probably it */ if (xs_type(h) == XSTYPE_STRING) { @@ -303,7 +307,7 @@ xs_list *get_attachments(const xs_dict *msg) } -int timeline_request(snac *snac, char **id, xs_str **wrk, int level) +int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) /* ensures that an entry and its ancestors are in the timeline */ { int status = 0; @@ -323,7 +327,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) status = activitypub_request(snac, *id, &msg); if (valid_status(status)) { - xs_dict *object = msg; + const xs_dict *object = msg; const char *type = xs_dict_get(object, "type"); /* get the id again from the object, as it may be different */ @@ -369,7 +373,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) } /* does it have an ancestor? */ - char *in_reply_to = xs_dict_get(object, "inReplyTo"); + const char *in_reply_to = xs_dict_get(object, "inReplyTo"); /* store */ timeline_add(snac, nid, object); @@ -381,83 +385,12 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level) } } } - - enqueue_request_replies(snac, *id); } return status; } -void timeline_request_replies(snac *user, const char *id) -/* requests all replies of a message */ -/* FIXME: experimental -- needs more testing */ -{ - /* FIXME: TEMPORARILY DISABLED */ - /* Reason: I've found that many of the posts in the 'replies' Collection - do not have an inReplyTo field (why??? aren't they 'replies'???). - For this reason, these requested objects are not stored as children - of the original post and they are shown as out-of-context, top level posts. - This process is disabled until I find an elegant way of providing a parent - for these 'stray' children. */ - return; - - xs *msg = NULL; - - if (!valid_status(object_get(id, &msg))) - return; - - /* does it have a replies collection? */ - const xs_dict *replies = xs_dict_get(msg, "replies"); - - if (!xs_is_null(replies)) { - const char *type = xs_dict_get(replies, "type"); - const char *first = xs_dict_get(replies, "first"); - - if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) { - const char *next = xs_dict_get(first, "next"); - - if (!xs_is_null(next)) { - xs *rpls = NULL; - int status = activitypub_request(user, next, &rpls); - - /* request the Collection of replies */ - if (valid_status(status)) { - xs_list *items = xs_dict_get(rpls, "items"); - - if (xs_type(items) == XSTYPE_LIST) { - xs_val *v; - - /* request them all */ - while (xs_list_iter(&items, &v)) { - if (xs_type(v) == XSTYPE_DICT) { - /* not an id, but the object itself (!) */ - const char *c_id = xs_dict_get(v, "id"); - - if (!xs_is_null(id)) { - snac_debug(user, 0, xs_fmt("embedded reply %s", c_id)); - - object_add(c_id, v); - - /* get its own children */ - timeline_request_replies(user, v); - } - } - else { - snac_debug(user, 0, xs_fmt("request reply %s", v)); - timeline_request(user, &v, NULL, 0); - } - } - } - } - else - snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status)); - } - } - } -} - - int send_to_inbox_raw(const char *keyid, const char *seckey, const xs_str *inbox, const xs_dict *msg, xs_val **payload, int *p_size, int timeout) @@ -480,7 +413,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg, xs_val **payload, int *p_size, int timeout) /* sends a message to an Inbox */ { - char *seckey = xs_dict_get(snac->key, "secret"); + const char *seckey = xs_dict_get(snac->key, "secret"); return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout); } @@ -490,7 +423,7 @@ xs_str *get_actor_inbox(const char *actor) /* gets an actor's inbox */ { xs *data = NULL; - char *v = NULL; + const char *v = NULL; if (valid_status(actor_request(NULL, actor, &data))) { /* try first endpoints/sharedInbox */ @@ -539,16 +472,16 @@ void post_message(snac *snac, const char *actor, const xs_dict *msg) xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public) /* returns the list of recipients for a message */ { - char *to = xs_dict_get(msg, "to"); - char *cc = xs_dict_get(msg, "cc"); + const xs_val *to = xs_dict_get(msg, "to"); + const xs_val *cc = xs_dict_get(msg, "cc"); xs_set rcpts; int n; xs_set_init(&rcpts); - char *lists[] = { to, cc, NULL }; + const xs_list *lists[] = { to, cc, NULL }; for (n = 0; lists[n]; n++) { - char *l = lists[n]; + xs_list *l = (xs_list *)lists[n]; char *v; xs *tl = NULL; @@ -671,13 +604,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) /* if it's a Follow, it must be explicitly for us */ if (xs_match(type, "Follow")) { - char *object = xs_dict_get(c_msg, "object"); + const char *object = xs_dict_get(c_msg, "object"); return !xs_is_null(object) && strcmp(snac->actor, object) == 0; } /* only accept Ping directed to us */ if (xs_match(type, "Ping")) { - char *dest = xs_dict_get(c_msg, "to"); + const char *dest = xs_dict_get(c_msg, "to"); return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0; } @@ -692,7 +625,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) if (pub_msg && following_check(snac, actor)) return 1; - xs_dict *msg = xs_dict_get(c_msg, "object"); + const xs_dict *msg = xs_dict_get(c_msg, "object"); xs *rcpts = recipient_list(snac, msg, 0); xs_list *p = rcpts; xs_str *v; @@ -704,8 +637,9 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) xs *actor_obj = NULL; if (valid_status(object_get(actor, &actor_obj))) { - if ((v = xs_dict_get(actor_obj, "followers"))) - actor_followers = xs_dup(v); + const xs_val *fw = xs_dict_get(actor_obj, "followers"); + if (fw) + actor_followers = xs_dup(fw); } } @@ -728,13 +662,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) } /* accept if it's by someone we follow */ - char *atto = get_atto(msg); + const char *atto = get_atto(msg); if (pub_msg && !xs_is_null(atto) && following_check(snac, atto)) return 3; /* is this message a reply to another? */ - char *irt = xs_dict_get(msg, "inReplyTo"); + const char *irt = xs_dict_get(msg, "inReplyTo"); if (!xs_is_null(irt)) { xs *r_msg = NULL; @@ -987,8 +921,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor, /* telegram */ - char *bot = xs_dict_get(snac->config, "telegram_bot"); - char *chat_id = xs_dict_get(snac->config, "telegram_chat_id"); + const char *bot = xs_dict_get(snac->config, "telegram_bot"); + const char *chat_id = xs_dict_get(snac->config, "telegram_chat_id"); if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id) enqueue_telegram(body, bot, chat_id); @@ -1001,8 +935,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor, objid = actor; /* ntfy */ - char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); - char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); + const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); + const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); if (!xs_is_null(ntfy_server) && *ntfy_server) enqueue_ntfy(body, ntfy_server, ntfy_token); @@ -1088,7 +1022,7 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id, } -xs_dict *msg_collection(snac *snac, char *id) +xs_dict *msg_collection(snac *snac, const char *id) /* creates an empty OrderedCollection message */ { xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL); @@ -1102,7 +1036,7 @@ xs_dict *msg_collection(snac *snac, char *id) } -xs_dict *msg_accept(snac *snac, char *object, char *to) +xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to) /* creates an Accept message (as a response to a Follow) */ { xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object); @@ -1113,12 +1047,12 @@ xs_dict *msg_accept(snac *snac, char *object, char *to) } -xs_dict *msg_update(snac *snac, xs_dict *object) +xs_dict *msg_update(snac *snac, const xs_dict *object) /* creates an Update message */ { xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object); - char *type = xs_dict_get(object, "type"); + const char *type = xs_dict_get(object, "type"); if (strcmp(type, "Note") == 0) { msg = xs_dict_append(msg, "to", xs_dict_get(object, "to")); @@ -1141,7 +1075,7 @@ xs_dict *msg_update(snac *snac, xs_dict *object) } -xs_dict *msg_admiration(snac *snac, char *object, char *type) +xs_dict *msg_admiration(snac *snac, const char *object, const char *type) /* creates a Like or Announce message */ { xs *a_msg = NULL; @@ -1172,7 +1106,7 @@ xs_dict *msg_admiration(snac *snac, char *object, char *type) } -xs_dict *msg_repulsion(snac *user, char *id, char *type) +xs_dict *msg_repulsion(snac *user, const char *id, const char *type) /* creates an Undo + admiration message */ { xs *a_msg = NULL; @@ -1210,7 +1144,7 @@ xs_dict *msg_actor(snac *snac) xs *kid = NULL; xs *f_bio = NULL; xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL); - char *p; + const char *p; int n; /* change the @context (is this really necessary?) */ @@ -1268,7 +1202,7 @@ xs_dict *msg_actor(snac *snac) } /* add the metadata as attachments of PropertyValue */ - xs_dict *metadata = xs_dict_get(snac->config, "metadata"); + const xs_dict *metadata = xs_dict_get(snac->config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { xs *attach = xs_list_new(); xs_str *k; @@ -1314,7 +1248,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object) /* creates a 'Create' message */ { xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object); - xs_val *v; + const xs_val *v; if ((v = get_atto(object))) msg = xs_dict_append(msg, "attributedTo", v); @@ -1331,7 +1265,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object) } -xs_dict *msg_undo(snac *snac, char *object) +xs_dict *msg_undo(snac *snac, const xs_val *object) /* creates an 'Undo' message */ { xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object); @@ -1344,7 +1278,7 @@ xs_dict *msg_undo(snac *snac, char *object) } -xs_dict *msg_delete(snac *snac, char *id) +xs_dict *msg_delete(snac *snac, const char *id) /* creates a 'Delete' + 'Tombstone' for a local entry */ { xs *tomb = xs_dict_new(); @@ -1386,7 +1320,7 @@ xs_dict *msg_follow(snac *snac, const char *q) if (valid_status(status)) { /* check if the actor is an alias */ - char *r_actor = xs_dict_get(actor_o, "id"); + const char *r_actor = xs_dict_get(actor_o, "id"); if (r_actor && strcmp(actor, r_actor) != 0) { snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor)); @@ -1402,7 +1336,7 @@ xs_dict *msg_follow(snac *snac, const char *q) xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, - xs_str *in_reply_to, xs_list *attach, int priv) + const xs_str *in_reply_to, const xs_list *attach, int priv) /* creates a 'Note' message */ { xs *ntid = tid(0); @@ -1442,7 +1376,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, if (valid_status(object_get(in_reply_to, &p_msg))) { /* add this author as recipient */ - char *a, *v; + const char *a, *v; if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1) to = xs_list_append(to, a); @@ -1453,7 +1387,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, xs *actor_o = NULL; if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) { - char *uname = xs_dict_get(actor_o, "preferredUsername"); + const char *uname = xs_dict_get(actor_o, "preferredUsername"); if (!xs_is_null(uname) && *uname) { xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2)); @@ -1492,7 +1426,8 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, /* create the attachment list, if there are any */ if (!xs_is_null(attach)) { - while (xs_list_iter(&attach, &v)) { + int c = 0; + while (xs_list_next(attach, &v, &c)) { xs *d = xs_dict_new(); const char *url = xs_list_get(v, 0); const char *alt = xs_list_get(v, 1); @@ -1515,7 +1450,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, p = tag; while (xs_list_iter(&p, &v)) { if (xs_type(v) == XSTYPE_DICT) { - char *t; + const char *t; if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) { if (!xs_is_null(t = xs_dict_get(v, "href"))) @@ -1639,7 +1574,7 @@ int update_question(snac *user, const char *id) xs *msg = NULL; xs *rcnt = xs_dict_new(); xs *lopts = xs_list_new(); - xs_list *opts; + const xs_list *opts; xs_list *p; xs_val *v; @@ -1657,8 +1592,8 @@ int update_question(snac *user, const char *id) return -3; /* fill the initial count */ - p = opts; - while (xs_list_iter(&p, &v)) { + int c = 0; + while (xs_list_next(opts, &v, &c)) { const char *name = xs_dict_get(v, "name"); if (name) { lopts = xs_list_append(lopts, name); @@ -1764,13 +1699,13 @@ int update_question(snac *user, const char *id) /** queues **/ -int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) +int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) /* processes an ActivityPub message from the input queue */ /* return values: -1, fatal error; 0, transient error, retry; 1, processed and done; 2, propagate to users (only when no user is set) */ { - char *actor = xs_dict_get(msg, "actor"); - char *type = xs_dict_get(msg, "type"); + const char *actor = xs_dict_get(msg, "actor"); + const char *type = xs_dict_get(msg, "type"); xs *actor_o = NULL; int a_status; int do_notify = 0; @@ -1790,7 +1725,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) return -1; } - char *object, *utype; + const char *object, *utype; object = xs_dict_get(msg, "object"); if (object != NULL && xs_type(object) == XSTYPE_DICT) @@ -1813,7 +1748,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } /* also discard if the object to be deleted is not here */ - char *obj_id = object; + const char *obj_id = object; if (xs_type(obj_id) == XSTYPE_DICT) obj_id = xs_dict_get(obj_id, "id"); @@ -1885,7 +1820,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age")); if (min_account_age > 0) { - char *actor_date = xs_dict_get(actor_o, "published"); + const char *actor_date = xs_dict_get(actor_o, "published"); if (!xs_is_null(actor_date)) { time_t actor_t = xs_parse_iso_date(actor_date, 0); @@ -1945,7 +1880,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } else if (strcmp(type, "Undo") == 0) { /** **/ - char *id = xs_dict_get(object, "object"); + const char *id = xs_dict_get(object, "object"); if (xs_type(object) != XSTYPE_DICT) utype = "Follow"; @@ -1990,9 +1925,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } if (xs_match(utype, "Note|Article")) { /** **/ - char *id = xs_dict_get(object, "id"); - char *in_reply_to = xs_dict_get(object, "inReplyTo"); - char *atto = get_atto(object); + const char *id = xs_dict_get(object, "id"); + const char *in_reply_to = xs_dict_get(object, "inReplyTo"); + const char *atto = get_atto(object); xs *wrk = NULL; if (xs_is_null(id)) @@ -2029,14 +1964,14 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } else if (strcmp(utype, "Question") == 0) { /** **/ - char *id = xs_dict_get(object, "id"); + const char *id = xs_dict_get(object, "id"); if (timeline_add(snac, id, object)) snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id)); } else if (strcmp(utype, "Video") == 0) { /** **/ - char *id = xs_dict_get(object, "id"); + const char *id = xs_dict_get(object, "id"); if (timeline_add(snac, id, object)) snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id)); @@ -2212,7 +2147,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) } -int send_email(char *msg) +int send_email(const char *msg) /* invoke sendmail with email headers and body in msg */ { FILE *f; @@ -2244,14 +2179,14 @@ int send_email(char *msg) void process_user_queue_item(snac *snac, xs_dict *q_item) /* processes an item from the user queue */ { - char *type; + const char *type; int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); if ((type = xs_dict_get(q_item, "type")) == NULL) type = "output"; if (strcmp(type, "message") == 0) { - xs_dict *msg = xs_dict_get(q_item, "message"); + const xs_dict *msg = xs_dict_get(q_item, "message"); xs *rcpts = recipient_list(snac, msg, 1); xs_set inboxes; xs_list *p; @@ -2292,8 +2227,8 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) else if (strcmp(type, "input") == 0) { /* process the message */ - xs_dict *msg = xs_dict_get(q_item, "message"); - xs_dict *req = xs_dict_get(q_item, "req"); + const xs_dict *msg = xs_dict_get(q_item, "message"); + const xs_dict *req = xs_dict_get(q_item, "req"); int retries = xs_number_get(xs_dict_get(q_item, "retries")); if (xs_is_null(msg)) @@ -2320,13 +2255,6 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) update_question(snac, id); } else - if (strcmp(type, "request_replies") == 0) { - const char *id = xs_dict_get(q_item, "message"); - - if (!xs_is_null(id)) - timeline_request_replies(snac, id); - } - else if (strcmp(type, "object_request") == 0) { const char *id = xs_dict_get(q_item, "message"); @@ -2395,15 +2323,15 @@ int process_user_queue(snac *snac) void process_queue_item(xs_dict *q_item) /* processes an item from the global queue */ { - char *type = xs_dict_get(q_item, "type"); + const char *type = xs_dict_get(q_item, "type"); int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); if (strcmp(type, "output") == 0) { int status; - xs_str *inbox = xs_dict_get(q_item, "inbox"); - xs_str *keyid = xs_dict_get(q_item, "keyid"); - xs_str *seckey = xs_dict_get(q_item, "seckey"); - xs_dict *msg = xs_dict_get(q_item, "message"); + const xs_str *inbox = xs_dict_get(q_item, "inbox"); + const xs_str *keyid = xs_dict_get(q_item, "keyid"); + const xs_str *seckey = xs_dict_get(q_item, "seckey"); + const xs_dict *msg = xs_dict_get(q_item, "message"); int retries = xs_number_get(xs_dict_get(q_item, "retries")); int p_status = xs_number_get(xs_dict_get(q_item, "p_status")); xs *payload = NULL; @@ -2475,7 +2403,7 @@ void process_queue_item(xs_dict *q_item) else if (strcmp(type, "email") == 0) { /* send this email */ - xs_str *msg = xs_dict_get(q_item, "message"); + const xs_str *msg = xs_dict_get(q_item, "message"); int retries = xs_number_get(xs_dict_get(q_item, "retries")); if (!send_email(msg)) @@ -2497,8 +2425,8 @@ void process_queue_item(xs_dict *q_item) else if (strcmp(type, "telegram") == 0) { /* send this via telegram */ - char *bot = xs_dict_get(q_item, "bot"); - char *msg = xs_dict_get(q_item, "message"); + const char *bot = xs_dict_get(q_item, "bot"); + const char *msg = xs_dict_get(q_item, "message"); xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id")); int status = 0; @@ -2521,9 +2449,9 @@ void process_queue_item(xs_dict *q_item) else if (strcmp(type, "ntfy") == 0) { /* send this via ntfy */ - char *ntfy_server = xs_dict_get(q_item, "ntfy_server"); - char *msg = xs_dict_get(q_item, "message"); - char *ntfy_token = xs_dict_get(q_item, "ntfy_token"); + const char *ntfy_server = xs_dict_get(q_item, "ntfy_server"); + const char *msg = xs_dict_get(q_item, "message"); + const char *ntfy_token = xs_dict_get(q_item, "ntfy_token"); int status = 0; xs *url = xs_fmt("%s", ntfy_server); @@ -2552,8 +2480,8 @@ void process_queue_item(xs_dict *q_item) } else if (strcmp(type, "input") == 0) { - xs_dict *msg = xs_dict_get(q_item, "message"); - xs_dict *req = xs_dict_get(q_item, "req"); + const xs_dict *msg = xs_dict_get(q_item, "message"); + const xs_dict *req = xs_dict_get(q_item, "req"); int retries = xs_number_get(xs_dict_get(q_item, "retries")); /* do some instance-level checks */ @@ -2572,7 +2500,7 @@ void process_queue_item(xs_dict *q_item) else if (r == 2) { /* redistribute the input message to all users */ - char *ntid = xs_dict_get(q_item, "ntid"); + const char *ntid = xs_dict_get(q_item, "ntid"); xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid); FILE *f; @@ -2647,7 +2575,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) { int status = 200; - char *accept = xs_dict_get(req, "accept"); + const char *accept = xs_dict_get(req, "accept"); snac snac; xs *msg = NULL; @@ -2659,7 +2587,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, return 0; xs *l = xs_split_n(q_path, "/", 2); - char *uid, *p_path; + const char *uid; + const char *p_path; uid = xs_list_get(l, 1); if (!user_open(&snac, uid)) { @@ -2677,7 +2606,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, msg = msg_actor(&snac); *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; - char *ua = xs_dict_get(req, "user-agent"); + const char *ua = xs_dict_get(req, "user-agent"); snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA")); } @@ -2694,8 +2623,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, xs *i = NULL; if (valid_status(object_get_by_md5(v, &i))) { - char *type = xs_dict_get(i, "type"); - char *id = xs_dict_get(i, "id"); + const char *type = xs_dict_get(i, "type"); + const char *id = xs_dict_get(i, "id"); if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) { xs *c_msg = msg_create(&snac, i); @@ -2748,9 +2677,9 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path, (void)b_size; int status = 202; /* accepted */ - char *i_ctype = xs_dict_get(req, "content-type"); + const char *i_ctype = xs_dict_get(req, "content-type"); snac snac; - char *v; + const char *v; if (i_ctype == NULL) { *body = xs_str_new("no content-type"); diff --git a/data.c b/data.c index 0cceefd..3377f3e 100644 --- a/data.c +++ b/data.c @@ -29,7 +29,7 @@ pthread_mutex_t data_mutex = {0}; int snac_upgrade(xs_str **error); -int srv_open(char *basedir, int auto_upgrade) +int srv_open(const char *basedir, int auto_upgrade) /* opens a server */ { int ret = 0; @@ -58,10 +58,10 @@ int srv_open(char *basedir, int auto_upgrade) if (srv_config == NULL) error = xs_fmt("ERROR: cannot parse '%s'", cfg_file); else { - char *host; - char *prefix; - char *dbglvl; - char *proto; + const char *host; + const char *prefix; + const char *dbglvl; + const char *proto; host = xs_dict_get(srv_config, "host"); prefix = xs_dict_get(srv_config, "prefix"); @@ -710,7 +710,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow) fclose(f); /* does this object has a parent? */ - char *in_reply_to = xs_dict_get(obj, "inReplyTo"); + const char *in_reply_to = xs_dict_get(obj, "inReplyTo"); if (!xs_is_null(in_reply_to) && *in_reply_to) { /* update the children index of the parent */ @@ -1124,7 +1124,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg) } -int timeline_del(snac *snac, char *id) +int timeline_del(snac *snac, const char *id) /* deletes a message from the timeline */ { /* delete from the user's caches */ @@ -1192,17 +1192,16 @@ int timeline_admire(snac *snac, const char *id, const char *admirer, int like) } -xs_list *timeline_top_level(snac *snac, xs_list *list) +xs_list *timeline_top_level(snac *snac, const xs_list *list) /* returns the top level md5 entries from this index */ { xs_set seen; - xs_list *p; xs_str *v; xs_set_init(&seen); - p = list; - while (xs_list_iter(&p, &v)) { + int c = 0; + while (xs_list_next(list, &v, &c)) { char line[256] = ""; strncpy(line, v, sizeof(line)); @@ -1290,7 +1289,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg) /* object already exists; if it's of type Accept, the actor is already being followed and confirmed, so do nothing */ - char *type = xs_dict_get(p_object, "type"); + const char *type = xs_dict_get(p_object, "type"); if (!xs_is_null(type) && strcmp(type, "Accept") == 0) { snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor)); @@ -1546,8 +1545,9 @@ void hide(snac *snac, const char *id) /* resolve to get the id */ if (valid_status(object_get_by_md5(v, &co))) { - if ((v = xs_dict_get(co, "id")) != NULL) - hide(snac, v); + const char *id = xs_dict_get(co, "id"); + if (id != NULL) + hide(snac, id); } } } @@ -1563,7 +1563,7 @@ int is_hidden(snac *snac, const char *id) } -int actor_add(const char *actor, xs_dict *msg) +int actor_add(const char *actor, const xs_dict *msg) /* adds an actor */ { return object_add_ow(actor, msg); @@ -1687,7 +1687,7 @@ int limited(snac *user, const char *id, int cmd) void tag_index(const char *id, const xs_dict *obj) /* update the tag indexes for this object */ { - xs_list *tags = xs_dict_get(obj, "tag"); + const xs_list *tags = xs_dict_get(obj, "tag"); if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) { xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir); @@ -1695,9 +1695,10 @@ void tag_index(const char *id, const xs_dict *obj) mkdirx(g_tag_dir); xs_dict *v; - while (xs_list_iter(&tags, &v)) { - char *type = xs_dict_get(v, "type"); - char *name = xs_dict_get(v, "name"); + int ct = 0; + while (xs_list_next(tags, &v, &ct)) { + const char *type = xs_dict_get(v, "type"); + const char *name = xs_dict_get(v, "name"); if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) { while (*name == '#' || *name == '@') @@ -1706,7 +1707,7 @@ void tag_index(const char *id, const xs_dict *obj) if (*name == '\0') continue; - name = xs_tolower_i(name); + name = xs_tolower_i((xs_str *)name); xs *md5_tag = xs_md5_hex(name, strlen(name)); xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]); @@ -1729,7 +1730,7 @@ void tag_index(const char *id, const xs_dict *obj) } -xs_list *tag_search(char *tag, int skip, int show) +xs_list *tag_search(const char *tag, int skip, int show) /* returns the list of posts tagged with tag */ { if (*tag == '#') @@ -1912,7 +1913,7 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op void list_distribute(snac *user, const char *who, const xs_dict *post) /* distributes the post to all appropriate lists */ { - char *id = xs_dict_get(post, "id"); + const char *id = xs_dict_get(post, "id"); /* if who is not set, use the attributedTo in the message */ if (xs_is_null(who)) @@ -2164,7 +2165,7 @@ void inbox_add(const char *inbox) void inbox_add_by_actor(const xs_dict *actor) /* collects an actor's shared inbox, if it has one */ { - char *v; + const char *v; if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) && !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) { @@ -2210,7 +2211,7 @@ xs_str *_instance_block_fn(const char *instance) xs *s = xs_replace(instance, "http:/" "/", ""); xs *s1 = xs_replace(s, "https:/" "/", ""); xs *l = xs_split(s1, "/"); - char *p = xs_list_get(l, 0); + const char *p = xs_list_get(l, 0); xs *md5 = xs_md5_hex(p, strlen(p)); return xs_fmt("%s/block/%s", srv_basedir, md5); @@ -2279,7 +2280,7 @@ int content_match(const char *file, const xs_dict *msg) xs *fn = xs_fmt("%s/%s", srv_basedir, file); FILE *f; int r = 0; - char *v = xs_dict_get(msg, "content"); + const char *v = xs_dict_get(msg, "content"); if (xs_type(v) == XSTYPE_STRING && *v) { if ((f = fopen(fn, "r")) != NULL) { @@ -2386,7 +2387,7 @@ xs_list *content_search(snac *user, const char *regex, if (id == NULL || is_hidden(user, id)) continue; - char *content = xs_dict_get(post, "content"); + const char *content = xs_dict_get(post, "content"); if (xs_is_null(content)) continue; @@ -2639,7 +2640,7 @@ void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retri /* enqueues an input message */ { xs *qmsg = _new_qmsg("input", msg, retries); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); qmsg = xs_dict_append(qmsg, "req", req); @@ -2654,7 +2655,7 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries) /* enqueues an input message from the shared input */ { xs *qmsg = _new_qmsg("input", msg, retries); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); qmsg = xs_dict_append(qmsg, "req", req); @@ -2666,11 +2667,12 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries) void enqueue_output_raw(const char *keyid, const char *seckey, - xs_dict *msg, xs_str *inbox, int retries, int p_status) + const xs_dict *msg, const xs_str *inbox, + int retries, int p_status) /* enqueues an output message to an inbox */ { xs *qmsg = _new_qmsg("output", msg, retries); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); xs *ns = xs_number_new(p_status); @@ -2690,7 +2692,8 @@ void enqueue_output_raw(const char *keyid, const char *seckey, } -void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status) +void enqueue_output(snac *snac, const xs_dict *msg, + const xs_str *inbox, int retries, int p_status) /* enqueues an output message to an inbox */ { if (xs_startswith(inbox, snac->actor)) { @@ -2698,13 +2701,14 @@ void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_ return; } - char *seckey = xs_dict_get(snac->key, "secret"); + const char *seckey = xs_dict_get(snac->key, "secret"); enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status); } -void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries) +void enqueue_output_by_actor(snac *snac, const xs_dict *msg, + const xs_str *actor, int retries) /* enqueues an output message for an actor */ { xs *inbox = get_actor_inbox(actor); @@ -2716,11 +2720,11 @@ void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int } -void enqueue_email(xs_str *msg, int retries) +void enqueue_email(const xs_str *msg, int retries) /* enqueues an email message to be sent */ { xs *qmsg = _new_qmsg("email", msg, retries); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); qmsg = _enqueue_put(fn, qmsg); @@ -2733,7 +2737,7 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id) /* enqueues a message to be sent via Telegram */ { xs *qmsg = _new_qmsg("telegram", msg, 0); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); qmsg = xs_dict_append(qmsg, "bot", bot); @@ -2748,7 +2752,7 @@ void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_t /* enqueues a message to be sent via ntfy */ { xs *qmsg = _new_qmsg("ntfy", msg, 0); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server); @@ -2764,7 +2768,7 @@ void enqueue_message(snac *snac, const xs_dict *msg) /* enqueues an output message */ { xs *qmsg = _new_qmsg("message", msg, 0); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); qmsg = _enqueue_put(fn, qmsg); @@ -2807,7 +2811,7 @@ void enqueue_verify_links(snac *user) /* enqueues a link verification */ { xs *qmsg = _new_qmsg("verify_links", "", 0); - char *ntid = xs_dict_get(qmsg, "ntid"); + const char *ntid = xs_dict_get(qmsg, "ntid"); xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); qmsg = _enqueue_put(fn, qmsg); @@ -2832,42 +2836,6 @@ void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs) } -void enqueue_request_replies(snac *user, const char *id) -/* enqueues a request for the replies of a message */ -{ - /* test first if this precise request is already in the queue */ - xs *queue = user_queue(user); - xs_list *p = queue; - xs_str *v; - - while (xs_list_iter(&p, &v)) { - xs *q_item = queue_get(v); - - if (q_item != NULL) { - const char *type = xs_dict_get(q_item, "type"); - const char *msg = xs_dict_get(q_item, "message"); - - if (type && msg && strcmp(type, "request_replies") == 0 && strcmp(msg, id) == 0) { - /* don't requeue */ - snac_debug(user, 1, xs_fmt("enqueue_request_replies already here %s", id)); - return; - } - } - } - - /* not there; enqueue the request with a small delay */ - xs *qmsg = _new_qmsg("request_replies", id, 0); - xs *ntid = tid(10); - xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); - - qmsg = xs_dict_set(qmsg, "ntid", ntid); - - qmsg = _enqueue_put(fn, qmsg); - - snac_debug(user, 2, xs_fmt("enqueue_request_replies %s", id)); -} - - int was_question_voted(snac *user, const char *id) /* returns true if the user voted in this poll */ { @@ -2881,7 +2849,7 @@ int was_question_voted(snac *user, const char *id) xs *obj = NULL; if (valid_status(object_get_by_md5(md5, &obj))) { - char *atto = get_atto(obj); + const char *atto = get_atto(obj); if (atto && strcmp(atto, user->actor) == 0 && !xs_is_null(xs_dict_get(obj, "name"))) { voted = 1; @@ -3055,7 +3023,7 @@ void purge_server(void) if (mtime_nl(v2, &n_link) < mt && n_link < 2) { xs *s1 = xs_replace(v2, ".json", ""); xs *l = xs_split(s1, "/"); - char *md5 = xs_list_get(l, -1); + const char *md5 = xs_list_get(l, -1); object_del_by_md5(md5); cnt++; @@ -3147,7 +3115,7 @@ void purge_user(snac *snac) /* do the purge for this user */ { int priv_days, pub_days, user_days = 0; - char *v; + const char *v; int n; priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); @@ -3256,7 +3224,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req, if (p_size && payload) { xs *payload_fn = NULL; xs *payload_fn_raw = NULL; - char *v = xs_dict_get(req, "content-type"); + const char *v = xs_dict_get(req, "content-type"); if (v && xs_str_in(v, "json") != -1) { payload_fn = xs_fmt("%s/payload.json", dir); @@ -3287,7 +3255,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req, if (b_size && body) { xs *body_fn = NULL; - char *v = xs_dict_get(headers, "content-type"); + const char *v = xs_dict_get(headers, "content-type"); if (v && xs_str_in(v, "json") != -1) { body_fn = xs_fmt("%s/body.json", dir); @@ -3356,7 +3324,7 @@ void srv_archive_error(const char *prefix, const xs_str *err, } -void srv_archive_qitem(char *prefix, xs_dict *q_item) +void srv_archive_qitem(const char *prefix, xs_dict *q_item) /* archives a q_item in the error folder */ { xs *ntid = tid(0); diff --git a/html.c b/html.c index 6351823..6521726 100644 --- a/html.c +++ b/html.c @@ -41,7 +41,7 @@ int login(snac *snac, const xs_dict *headers) } -xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems) +xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems) /* replaces all the :shortnames: with the emojis in tag */ { if (!xs_is_null(tag)) { @@ -57,18 +57,18 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems) xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); - xs_list *p = tag_list; char *v; + int c = 0; - while (xs_list_iter(&p, &v)) { - char *t = xs_dict_get(v, "type"); + while (xs_list_next(tag_list, &v, &c)) { + const char *t = xs_dict_get(v, "type"); if (t && strcmp(t, "Emoji") == 0) { - char *n = xs_dict_get(v, "name"); - char *i = xs_dict_get(v, "icon"); + const char *n = xs_dict_get(v, "name"); + const char *i = xs_dict_get(v, "icon"); if (n && i) { - char *u = xs_dict_get(i, "url"); + const char *u = xs_dict_get(i, "url"); xs_html *img = xs_html_sctag("img", xs_html_attr("loading", "lazy"), xs_html_attr("src", u), @@ -88,7 +88,7 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems) xs_str *actor_name(xs_dict *actor) /* gets the actor name */ { - char *v; + const char *v; if (xs_is_null((v = xs_dict_get(actor, "name"))) || *v == '\0') { if (xs_is_null(v = xs_dict_get(actor, "preferredUsername")) || *v == '\0') { @@ -106,7 +106,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, xs_html *actor_icon = xs_html_tag("p", NULL); xs *avatar = NULL; - char *v; + const char *v; int fwing = 0; int fwer = 0; @@ -125,7 +125,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, if (avatar == NULL) avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64()); - char *actor_id = xs_dict_get(actor, "id"); + const char *actor_id = xs_dict_get(actor, "id"); xs *href = NULL; if (user) { @@ -216,7 +216,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, } { - char *username, *id; + const char *username, *id; if (xs_is_null(username = xs_dict_get(actor, "preferredUsername")) || *username == '\0') { /* This should never be reached */ @@ -244,15 +244,15 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, } -xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg) +xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg) { xs *actor = NULL; xs_html *actor_icon = NULL; if (actor_id && valid_status(actor_get_refresh(user, actor_id, &actor))) { - char *date = NULL; - char *udate = NULL; - char *url = NULL; + const char *date = NULL; + const char *udate = NULL; + const char *url = NULL; int priv = 0; const char *type = xs_dict_get(msg, "type"); @@ -271,14 +271,14 @@ xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg) } -xs_html *html_note(snac *user, char *summary, - char *div_id, char *form_id, - char *ta_plh, char *ta_content, - char *edit_id, char *actor_id, - xs_val *cw_yn, char *cw_text, - xs_val *mnt_only, char *redir, - char *in_reply_to, int poll, - char *att_file, char *att_alt_text) +xs_html *html_note(snac *user, const char *summary, + const char *div_id, const char *form_id, + const char *ta_plh, const char *ta_content, + const char *edit_id, const char *actor_id, + 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) { xs *action = xs_fmt("%s/admin/note", user->actor); @@ -460,9 +460,11 @@ static xs_html *html_base_head(void) /* add server CSS and favicon */ xs *f; f = xs_fmt("%s/favicon.ico", srv_baseurl); - xs_list *p = xs_dict_get(srv_config, "cssurls"); + const xs_list *p = xs_dict_get(srv_config, "cssurls"); char *v; - while (xs_list_iter(&p, &v)) { + int c = 0; + + while (xs_list_next(p, &v, &c)) { xs_html_add(head, xs_html_sctag("link", xs_html_attr("rel", "stylesheet"), @@ -498,8 +500,8 @@ xs_html *html_instance_head(void) } } - char *host = xs_dict_get(srv_config, "host"); - char *title = xs_dict_get(srv_config, "title"); + const char *host = xs_dict_get(srv_config, "host"); + const char *title = xs_dict_get(srv_config, "title"); xs_html_add(head, xs_html_tag("title", @@ -511,10 +513,10 @@ xs_html *html_instance_head(void) static xs_html *html_instance_body(void) { - char *host = xs_dict_get(srv_config, "host"); - char *sdesc = xs_dict_get(srv_config, "short_description"); - char *email = xs_dict_get(srv_config, "admin_email"); - 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 *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); @@ -760,7 +762,7 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_attr("class", "h-card snac-top-user")); if (read_only) { - char *header = xs_dict_get(user->config, "header"); + const char *header = xs_dict_get(user->config, "header"); if (header && *header) { xs_html_add(top_user, xs_html_tag("div", @@ -797,7 +799,7 @@ static xs_html *html_user_body(snac *user, int read_only) xs_html_add(top_user, top_user_bio); - xs_dict *metadata = xs_dict_get(user->config, "metadata"); + const xs_dict *metadata = xs_dict_get(user->config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { xs_str *k; xs_str *v; @@ -816,7 +818,7 @@ static xs_html *html_user_body(snac *user, int read_only) if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) { /* is this link validated? */ xs *verified_link = NULL; - xs_number *val_time = xs_dict_get(val_links, v); + const xs_number *val_time = xs_dict_get(val_links, v); if (xs_type(val_time) == XSTYPE_NUMBER) { time_t t = xs_number_get(val_time); @@ -928,7 +930,7 @@ xs_html *html_top_controls(snac *snac) /** user settings **/ - char *email = "[disabled by admin]"; + const char *email = "[disabled by admin]"; if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { email = xs_dict_get(snac->config_o, "email"); @@ -940,38 +942,38 @@ xs_html *html_top_controls(snac *snac) } } - char *cw = xs_dict_get(snac->config, "cw"); + const char *cw = xs_dict_get(snac->config, "cw"); if (xs_is_null(cw)) cw = ""; - char *telegram_bot = xs_dict_get(snac->config, "telegram_bot"); + const char *telegram_bot = xs_dict_get(snac->config, "telegram_bot"); if (xs_is_null(telegram_bot)) telegram_bot = ""; - char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id"); + const char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id"); if (xs_is_null(telegram_chat_id)) telegram_chat_id = ""; - char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); + const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); if (xs_is_null(ntfy_server)) ntfy_server = ""; - char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); + const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); if (xs_is_null(ntfy_token)) ntfy_token = ""; - char *purge_days = xs_dict_get(snac->config, "purge_days"); + const char *purge_days = xs_dict_get(snac->config, "purge_days"); if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) purge_days = (char *)xs_number_str(purge_days); else purge_days = "0"; - xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown"); - xs_val *bot = xs_dict_get(snac->config, "bot"); - xs_val *a_private = xs_dict_get(snac->config, "private"); + const xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown"); + const xs_val *bot = xs_dict_get(snac->config, "bot"); + const xs_val *a_private = xs_dict_get(snac->config, "private"); xs *metadata = xs_str_new(NULL); - xs_dict *md = xs_dict_get(snac->config, "metadata"); + const xs_dict *md = xs_dict_get(snac->config, "metadata"); xs_str *k; xs_str *v; @@ -1158,13 +1160,14 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) /* returns a string with the mentions in msg */ { xs_str *s = xs_str_new(NULL); - char *list = xs_dict_get(msg, "tag"); + const char *list = xs_dict_get(msg, "tag"); char *v; + int c = 0; - while (xs_list_iter(&list, &v)) { - char *type = xs_dict_get(v, "type"); - char *href = xs_dict_get(v, "href"); - char *name = xs_dict_get(v, "name"); + while (xs_list_next(list, &v, &c)) { + const char *type = xs_dict_get(v, "type"); + const char *href = xs_dict_get(v, "href"); + const char *name = xs_dict_get(v, "name"); if (type && strcmp(type, "Mention") == 0 && href && strcmp(href, snac->actor) != 0 && name) { @@ -1208,10 +1211,11 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) } -xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const char *md5) +xs_html *html_entry_controls(snac *snac, const char *actor, + const xs_dict *msg, const char *md5) { - char *id = xs_dict_get(msg, "id"); - char *group = xs_dict_get(msg, "audience"); + const char *id = xs_dict_get(msg, "id"); + const char *group = xs_dict_get(msg, "audience"); xs *likes = object_likes(id); xs *boosts = object_announces(id); @@ -1310,7 +1314,7 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const html_button("delete", L("Delete"), L("Delete this post")), html_button("hide", L("Hide"), L("Hide this post and its children"))); - char *prev_src = xs_dict_get(msg, "sourceContent"); + const char *prev_src = xs_dict_get(msg, "sourceContent"); if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/ /* post can be edited */ @@ -1318,13 +1322,13 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const xs *form_id = xs_fmt("%s_edit_form", md5); xs *redir = xs_fmt("%s_entry", md5); - char *att_file = ""; - char *att_alt_text = ""; - xs_list *att_list = xs_dict_get(msg, "attachment"); + const char *att_file = ""; + const char *att_alt_text = ""; + 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)) { - xs_dict *d = xs_list_get(att_list, 0); + const xs_dict *d = xs_list_get(att_list, 0); if (xs_type(d) == XSTYPE_DICT) { att_file = xs_dict_get_def(d, "url", ""); @@ -1370,10 +1374,10 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const xs_html *html_entry(snac *user, xs_dict *msg, int read_only, int level, char *md5, int hide_children) { - char *id = xs_dict_get(msg, "id"); - char *type = xs_dict_get(msg, "type"); - char *actor; - char *v; + const char *id = xs_dict_get(msg, "id"); + const char *type = xs_dict_get(msg, "type"); + const char *actor; + const char *v; int has_title = 0; /* do not show non-public messages in the public timeline */ @@ -1509,7 +1513,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (xs_list_len(boosts)) { /* if somebody boosted this, show as origin */ - char *p = xs_list_get(boosts, -1); + const char *p = xs_list_get(boosts, -1); xs *actor_r = NULL; if (user && xs_list_in(boosts, user->md5) != -1) { @@ -1529,7 +1533,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (!xs_is_null(name)) { xs *href = NULL; - char *id = xs_dict_get(actor_r, "id"); + const char *id = xs_dict_get(actor_r, "id"); int fwers = 0; int fwing = 0; @@ -1558,7 +1562,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, if (strcmp(type, "Note") == 0) { if (level == 0) { /* is the parent not here? */ - char *parent = xs_dict_get(msg, "inReplyTo"); + const char *parent = xs_dict_get(msg, "inReplyTo"); if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { xs_html_add(post_header, @@ -1603,7 +1607,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, v = "..."; /* only show it when not in the public timeline and the config setting is "open" */ - char *cw = xs_dict_get(user->config, "cw"); + const char *cw = xs_dict_get(user->config, "cw"); if (xs_is_null(cw) || read_only) cw = ""; @@ -1632,7 +1636,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, { /** build the content string **/ - char *content = xs_dict_get(msg, "content"); + const char *content = xs_dict_get(msg, "content"); xs *c = sanitize(xs_is_null(content) ? "" : content); @@ -1650,7 +1654,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2); /* Peertube videos content is in markdown */ - char *mtype = xs_dict_get(msg, "mediaType"); + const char *mtype = xs_dict_get(msg, "mediaType"); if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "text/markdown") == 0) { /* a full conversion could be better */ c = xs_replace_i(c, "\r", ""); @@ -1663,12 +1667,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } if (strcmp(type, "Question") == 0) { /** question content **/ - xs_list *oo = xs_dict_get(msg, "oneOf"); - xs_list *ao = xs_dict_get(msg, "anyOf"); - xs_list *p; + const xs_list *oo = xs_dict_get(msg, "oneOf"); + const xs_list *ao = xs_dict_get(msg, "anyOf"); + const xs_list *p; xs_dict *v; int closed = 0; - char *f_closed = NULL; + const char *f_closed = NULL; xs_html *poll = xs_html_tag("div", NULL); @@ -1697,10 +1701,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, /* closed poll */ xs_html *poll_result = xs_html_tag("table", xs_html_attr("class", "snac-poll-result")); + int c = 0; - while (xs_list_iter(&p, &v)) { - char *name = xs_dict_get(v, "name"); - xs_dict *replies = xs_dict_get(v, "replies"); + while (xs_list_next(p, &v, &c)) { + 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")); @@ -1737,9 +1742,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_attr("name", "irt"), xs_html_attr("value", id)))); - while (xs_list_iter(&p, &v)) { - char *name = xs_dict_get(v, "name"); - xs_dict *replies = xs_dict_get(v, "replies"); + int c = 0; + while (xs_list_next(p, &v, &c)) { + const char *name = xs_dict_get(v, "name"); + const xs_dict *replies = xs_dict_get(v, "replies"); if (name) { char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems")); @@ -1777,7 +1783,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } else { /* show when the poll closes */ - char *end_time = xs_dict_get(msg, "endTime"); + const char *end_time = xs_dict_get(msg, "endTime"); /* Pleroma does not have an endTime field; it has a closed time in the future */ @@ -1820,12 +1826,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_add(snac_content, content_attachments); - xs_list *p = attach; - - while (xs_list_iter(&p, &v)) { - char *type = xs_dict_get(v, "type"); - char *href = xs_dict_get(v, "href"); - char *name = xs_dict_get(v, "name"); + int c = 0; + xs_dict *a; + while (xs_list_next(attach, &a, &c)) { + const char *type = xs_dict_get(a, "type"); + const char *href = xs_dict_get(a, "href"); + const char *name = xs_dict_get(a, "name"); if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) { xs_html_add(content_attachments, @@ -1889,7 +1895,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } /* has this message an audience (i.e., comes from a channel or community)? */ - char *audience = xs_dict_get(msg, "audience"); + const char *audience = xs_dict_get(msg, "audience"); if (strcmp(type, "Page") == 0 && !xs_is_null(audience)) { xs_html *au_tag = xs_html_tag("p", xs_html_text("("), @@ -2023,11 +2029,12 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, if (xs_list_len(list) == 1) { /* only one element? pick the description from the source */ - char *id = xs_list_get(list, 0); + const char *id = xs_list_get(list, 0); xs *d = NULL; object_get_by_md5(id, &d); - if (d && (v = xs_dict_get(d, "sourceContent")) != NULL) - desc = xs_dup(v); + const char *sc = xs_dict_get(d, "sourceContent"); + if (d && sc != NULL) + desc = xs_dup(sc); alternate = xs_dup(xs_dict_get(d, "id")); } @@ -2087,13 +2094,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, /* is this message a non-public reply? */ if (user != NULL && !is_msg_public(msg)) { - char *irt = xs_dict_get(msg, "inReplyTo"); + const char *irt = xs_dict_get(msg, "inReplyTo"); /* is it a reply to something not in the storage? */ if (!xs_is_null(irt) && !object_here(irt)) { /* is it for me? */ - xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST)); - xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST)); + const xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST)); + const xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST)); if (xs_list_in(to, user->actor) == -1 && xs_list_in(cc, user->actor) == -1) { snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v)); @@ -2212,7 +2219,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t) html_actor_icon(snac, actor, xs_dict_get(actor, "published"), NULL, NULL, 0, 1))); /* content (user bio) */ - char *c = xs_dict_get(actor, "summary"); + const char *c = xs_dict_get(actor, "summary"); if (!xs_is_null(c)) { xs *sc = sanitize(c); @@ -2364,10 +2371,10 @@ xs_str *html_notifications(snac *user, int skip, int show) continue; xs *obj = NULL; - char *type = xs_dict_get(noti, "type"); - char *utype = xs_dict_get(noti, "utype"); - char *id = xs_dict_get(noti, "objid"); - char *date = xs_dict_get(noti, "date"); + const char *type = xs_dict_get(noti, "type"); + const char *utype = xs_dict_get(noti, "utype"); + const char *id = xs_dict_get(noti, "objid"); + const char *date = xs_dict_get(noti, "date"); if (xs_is_null(id) || !valid_status(object_get(id, &obj))) continue; @@ -2375,14 +2382,14 @@ xs_str *html_notifications(snac *user, int skip, int show) if (is_hidden(user, id)) continue; - char *actor_id = xs_dict_get(noti, "actor"); + const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; if (!valid_status(actor_get(actor_id, &actor))) continue; xs *a_name = actor_name(actor); - char *label = type; + const char *label = type; if (strcmp(type, "Create") == 0) label = L("Mention"); @@ -2494,14 +2501,14 @@ xs_str *html_notifications(snac *user, int skip, int show) int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag) { - char *accept = xs_dict_get(req, "accept"); + const char *accept = xs_dict_get(req, "accept"); int status = 404; snac snac; xs *uid = NULL; - char *p_path; + const char *p_path; int cache = 1; int save = 1; - char *v; + const char *v; xs *l = xs_split_n(q_path, "/", 2); v = xs_list_get(l, 1); @@ -2540,7 +2547,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int skip = 0; int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); - xs_dict *q_vars = xs_dict_get(req, "q_vars"); + const xs_dict *q_vars = xs_dict_get(req, "q_vars"); if ((v = xs_dict_get(q_vars, "skip")) != NULL) skip = atoi(v), cache = 0, save = 0; if ((v = xs_dict_get(q_vars, "show")) != NULL) @@ -2585,7 +2592,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, status = 401; } else { - char *q = xs_dict_get(q_vars, "q"); + const char *q = xs_dict_get(q_vars, "q"); if (q && *q) { if (*q == '#') { @@ -2669,7 +2676,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } else { xs *l = xs_split(p_path, "/"); - char *md5 = xs_list_get(l, -1); + const char *md5 = xs_list_get(l, -1); if (md5 && *md5 && timeline_here(&snac, md5)) { xs *list = xs_list_append(xs_list_new(), md5); @@ -2728,7 +2735,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } else { xs *l = xs_split(p_path, "/"); - char *lid = xs_list_get(l, -1); + const char *lid = xs_list_get(l, -1); xs *list = list_timeline(&snac, lid, skip, show); xs *next = list_timeline(&snac, lid, skip + show, 1); @@ -2767,7 +2774,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, else if (xs_startswith(p_path, "s/")) { /** a static file **/ xs *l = xs_split(p_path, "/"); - char *id = xs_list_get(l, 1); + const char *id = xs_list_get(l, 1); int sz; if (id && *id) { @@ -2788,8 +2795,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE) return 403; - xs *l = xs_split(p_path, "/"); - char *id = xs_list_get(l, 1); + xs *l = xs_split(p_path, "/"); + const char *id = xs_list_get(l, 1); if (id && *id) { if (xs_endswith(id, "timeline.html_")) { @@ -2845,8 +2852,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, int status = 0; snac snac; - char *uid, *p_path; - xs_dict *p_vars; + const char *uid; + const char *p_path; + const xs_dict *p_vars; xs *l = xs_split_n(q_path, "/", 2); @@ -2874,15 +2882,15 @@ int html_post_handler(const xs_dict *req, const char *q_path, if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ /* post note */ - xs_str *content = xs_dict_get(p_vars, "content"); - xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); - xs_str *attach_url = xs_dict_get(p_vars, "attach_url"); - xs_list *attach_file = xs_dict_get(p_vars, "attach"); - xs_str *to = xs_dict_get(p_vars, "to"); - xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); - xs_str *summary = xs_dict_get(p_vars, "summary"); - xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); - xs_str *alt_text = xs_dict_get(p_vars, "alt_text"); + 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")); xs *attach_list = xs_list_new(); @@ -2902,7 +2910,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, /* is attach_file set? */ if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) { - char *fn = xs_list_get(attach_file, 0); + const char *fn = xs_list_get(attach_file, 0); if (*fn != '\0') { char *ext = strrchr(fn, '.'); @@ -2978,7 +2986,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, int n; for (n = 0; fields[n]; n++) { - char *v = xs_dict_get(p_msg, fields[n]); + const char *v = xs_dict_get(p_msg, fields[n]); msg = xs_dict_set(msg, fields[n], v); } @@ -3007,10 +3015,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, else if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/ /* action on an entry */ - char *id = xs_dict_get(p_vars, "id"); - char *actor = xs_dict_get(p_vars, "actor"); - char *action = xs_dict_get(p_vars, "action"); - char *group = xs_dict_get(p_vars, "group"); + const char *id = xs_dict_get(p_vars, "id"); + const char *actor = xs_dict_get(p_vars, "actor"); + const char *action = xs_dict_get(p_vars, "action"); + const char *group = xs_dict_get(p_vars, "group"); if (action == NULL) return 404; @@ -3134,7 +3142,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, } else if (strcmp(action, L("Delete")) == 0) { /** **/ - char *actor_form = xs_dict_get(p_vars, "actor-form"); + const char *actor_form = xs_dict_get(p_vars, "actor-form"); if (actor_form != NULL) { /* delete follower */ if (valid_status(follower_del(&snac, actor))) @@ -3178,8 +3186,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, else if (p_path && strcmp(p_path, "admin/user-setup") == 0) { /** **/ /* change of user data */ - char *v; - char *p1, *p2; + const char *v; + const char *p1, *p2; if ((v = xs_dict_get(p_vars, "name")) != NULL) snac.config = xs_dict_set(snac.config, "name", v); @@ -3245,7 +3253,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, for (n = 0; uploads[n]; n++) { xs *var_name = xs_fmt("%s_file", uploads[n]); - xs_list *uploaded_file = xs_dict_get(p_vars, var_name); + const xs_list *uploaded_file = xs_dict_get(p_vars, var_name); if (xs_type(uploaded_file) == XSTYPE_LIST) { const char *fn = xs_list_get(uploaded_file, 0); @@ -3310,7 +3318,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, } else if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/ - char *irt = xs_dict_get(p_vars, "irt"); + const char *irt = xs_dict_get(p_vars, "irt"); const char *opt = xs_dict_get(p_vars, "question"); const char *actor = xs_dict_get(p_vars, "actor"); @@ -3345,7 +3353,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, xs *poll = NULL; if (valid_status(object_get(irt, &poll))) { - char *date = xs_dict_get(poll, "endTime"); + const char *date = xs_dict_get(poll, "endTime"); if (xs_is_null(date)) date = xs_dict_get(poll, "closed"); @@ -3363,7 +3371,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, } if (status == 303) { - char *redir = xs_dict_get(p_vars, "redir"); + const char *redir = xs_dict_get(p_vars, "redir"); if (xs_is_null(redir)) redir = "top"; @@ -3411,8 +3419,8 @@ xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char * continue; } - char *id = xs_dict_get(msg, "id"); - char *content = xs_dict_get(msg, "content"); + const char *id = xs_dict_get(msg, "id"); + const char *content = xs_dict_get(msg, "content"); if (user && !xs_startswith(id, user->actor)) continue; diff --git a/http.c b/http.c index f7ff9ba..4d85631 100644 --- a/http.c +++ b/http.c @@ -12,7 +12,7 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, const char *method, const char *url, - xs_dict *headers, + const xs_dict *headers, const char *body, int b_size, int *status, xs_str **payload, int *p_size, int timeout) @@ -24,8 +24,8 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, xs *s64 = NULL; xs *signature = NULL; xs *hdrs = NULL; - char *host; - char *target; + const char *host; + const char *target; char *k, *v; xs_dict *response; @@ -106,13 +106,13 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, xs_dict *http_signed_request(snac *snac, const char *method, const char *url, - xs_dict *headers, + const xs_dict *headers, const char *body, int b_size, int *status, xs_str **payload, int *p_size, int timeout) /* does a signed HTTP request */ { - char *seckey = xs_dict_get(snac->key, "secret"); + const char *seckey = xs_dict_get(snac->key, "secret"); xs_dict *response; response = http_signed_request_raw(snac->actor, seckey, method, url, @@ -122,17 +122,18 @@ xs_dict *http_signed_request(snac *snac, const char *method, const char *url, } -int check_signature(xs_dict *req, xs_str **err) +int check_signature(const xs_dict *req, xs_str **err) /* check the signature */ { - char *sig_hdr = xs_dict_get(req, "signature"); + const char *sig_hdr = xs_dict_get(req, "signature"); xs *keyId = NULL; xs *headers = NULL; xs *signature = NULL; xs *created = NULL; xs *expires = NULL; - char *pubkey; char *p; + const char *pubkey; + const char *k; if (xs_is_null(sig_hdr)) { *err = xs_fmt("missing 'signature' header"); @@ -142,10 +143,10 @@ int check_signature(xs_dict *req, xs_str **err) { /* extract the values */ xs *l = xs_split(sig_hdr, ","); - xs_list *p = l; + int c = 0; xs_val *v; - while (xs_list_iter(&p, &v)) { + while (xs_list_next(l, &v, &c)) { xs *kv = xs_split_n(v, "=", 1); if (xs_list_len(kv) != 2) @@ -192,8 +193,8 @@ int check_signature(xs_dict *req, xs_str **err) return 0; } - if ((p = xs_dict_get(actor, "publicKey")) == NULL || - ((pubkey = xs_dict_get(p, "publicKeyPem")) == NULL)) { + if ((k = xs_dict_get(actor, "publicKey")) == NULL || + ((pubkey = xs_dict_get(k, "publicKeyPem")) == NULL)) { *err = xs_fmt("cannot get pubkey from %s", keyId); return 0; } @@ -208,7 +209,7 @@ int check_signature(xs_dict *req, xs_str **err) p = l; while (xs_list_iter(&p, &v)) { - char *hc; + const char *hc; xs *ss = NULL; if (*sig_str != '\0') diff --git a/httpd.c b/httpd.c index 71cce5e..d63fa0f 100644 --- a/httpd.c +++ b/httpd.c @@ -125,7 +125,7 @@ static xs_str *greeting_html(void) /* does it have a %userlist% mark? */ if (xs_str_in(s, "%userlist%") != -1) { - char *host = xs_dict_get(srv_config, "host"); + const char *host = xs_dict_get(srv_config, "host"); xs *list = user_list(); xs_list *p = list; xs_str *uid; @@ -171,14 +171,14 @@ int server_get_handler(xs_dict *req, const char *q_path, /* is it the server root? */ if (*q_path == '\0') { - xs_dict *q_vars = xs_dict_get(req, "q_vars"); - char *t = NULL; + const xs_dict *q_vars = xs_dict_get(req, "q_vars"); + const char *t = NULL; 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")); - char *v; + const char *v; if ((v = xs_dict_get(q_vars, "skip")) != NULL) skip = atoi(v); @@ -193,7 +193,7 @@ int server_get_handler(xs_dict *req, const char *q_path, more = 1; } - char *accept = xs_dict_get(req, "accept"); + const char *accept = xs_dict_get(req, "accept"); if (!xs_is_null(accept) && strcmp(accept, "application/rss+xml") == 0) { xs *link = xs_fmt("%s/?t=%s", srv_baseurl, t); @@ -268,7 +268,7 @@ void httpd_connection(FILE *f) /* the connection processor */ { xs *req; - char *method; + const char *method; int status = 0; xs_str *body = NULL; int b_size = 0; @@ -278,7 +278,7 @@ void httpd_connection(FILE *f) xs *payload = NULL; xs *etag = NULL; int p_size = 0; - char *p; + const char *p; int fcgi_id; if (p_state->use_fcgi) @@ -411,7 +411,7 @@ void httpd_connection(FILE *f) headers = xs_dict_append(headers, "etag", etag); /* if there are any additional headers, add them */ - xs_dict *more_headers = xs_dict_get(srv_config, "http_headers"); + const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers"); if (xs_type(more_headers) == XSTYPE_DICT) { char *k, *v; int c = 0; diff --git a/main.c b/main.c index 819922f..9c906a6 100644 --- a/main.c +++ b/main.c @@ -315,7 +315,7 @@ int main(int argc, char *argv[]) xs *msg = msg_follow(&snac, url); if (msg != NULL) { - char *actor = xs_dict_get(msg, "object"); + const char *actor = xs_dict_get(msg, "object"); following_add(&snac, actor, msg); diff --git a/mastoapi.c b/mastoapi.c index 2bf5fdc..852713e 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -175,7 +175,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, return 0; int status = 404; - xs_dict *msg = xs_dict_get(req, "q_vars"); + const xs_dict *msg = xs_dict_get(req, "q_vars"); xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); srv_debug(1, xs_fmt("oauth_get_handler %s", q_path)); @@ -239,7 +239,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, int status = 404; - char *i_ctype = xs_dict_get(req, "content-type"); + const char *i_ctype = xs_dict_get(req, "content-type"); xs *args = NULL; if (i_ctype && xs_startswith(i_ctype, "application/json")) { @@ -568,10 +568,10 @@ xs_dict *mastoapi_account(const xs_dict *actor) acct = xs_dict_append(acct, "uri", id); xs *avatar = NULL; - xs_dict *av = xs_dict_get(actor, "icon"); + const xs_dict *av = xs_dict_get(actor, "icon"); if (xs_type(av) == XSTYPE_DICT) { - char *url = xs_dict_get(av, "url"); + const char *url = xs_dict_get(av, "url"); if (url != NULL) avatar = xs_dup(url); @@ -584,7 +584,7 @@ xs_dict *mastoapi_account(const xs_dict *actor) acct = xs_dict_append(acct, "avatar_static", avatar); xs *header = NULL; - xs_dict *hd = xs_dict_get(actor, "image"); + const xs_dict *hd = xs_dict_get(actor, "image"); if (xs_type(hd) == XSTYPE_DICT) header = xs_dup(xs_dict_get(hd, "url")); @@ -596,12 +596,13 @@ xs_dict *mastoapi_account(const xs_dict *actor) acct = xs_dict_append(acct, "header_static", header); /* emojis */ - xs_list *p; + const xs_list *p; if (!xs_is_null(p = xs_dict_get(actor, "tag"))) { xs *eml = xs_list_new(); xs_dict *v; + int c = 0; - while (xs_list_iter(&p, &v)) { + while (xs_list_next(p, &v, &c)) { const char *type = xs_dict_get(v, "type"); if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) { @@ -640,7 +641,7 @@ xs_dict *mastoapi_account(const xs_dict *actor) /* dict of validated links */ xs_dict *val_links = NULL; - xs_dict *metadata = xs_stock(XSTYPE_DICT); + const xs_dict *metadata = xs_stock(XSTYPE_DICT); snac user = {0}; if (xs_startswith(id, srv_baseurl)) { @@ -654,19 +655,20 @@ xs_dict *mastoapi_account(const xs_dict *actor) if (xs_is_null(val_links)) val_links = xs_stock(XSTYPE_DICT); - while (xs_list_iter(&p, &v)) { - char *type = xs_dict_get(v, "type"); - char *name = xs_dict_get(v, "name"); - char *value = xs_dict_get(v, "value"); + int c = 0; + while (xs_list_next(p, &v, &c)) { + const char *type = xs_dict_get(v, "type"); + const char *name = xs_dict_get(v, "name"); + const char *value = xs_dict_get(v, "value"); if (!xs_is_null(type) && !xs_is_null(name) && !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) { xs *val_date = NULL; - char *url = xs_dict_get(metadata, name); + const char *url = xs_dict_get(metadata, name); if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) { - xs_number *verified_time = xs_dict_get(val_links, url); + const xs_number *verified_time = xs_dict_get(val_links, url); if (xs_type(verified_time) == XSTYPE_NUMBER) { time_t t = xs_number_get(verified_time); @@ -695,7 +697,7 @@ xs_dict *mastoapi_account(const xs_dict *actor) } -xs_str *mastoapi_date(char *date) +xs_str *mastoapi_date(const char *date) /* converts an ISO 8601 date to whatever format Mastodon uses */ { xs_str *s = xs_crop_i(xs_dup(date), 0, 19); @@ -710,13 +712,13 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg) { xs_dict *poll = xs_dict_new(); xs *mid = mastoapi_id(msg); - xs_list *opts = NULL; + const xs_list *opts = NULL; xs_val *v; int num_votes = 0; xs *options = xs_list_new(); poll = xs_dict_append(poll, "id", mid); - char *date = xs_dict_get(msg, "endTime"); + const char *date = xs_dict_get(msg, "endTime"); if (date == NULL) date = xs_dict_get(msg, "closed"); if (date == NULL) @@ -741,7 +743,8 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg) poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE)); } - while (xs_list_iter(&opts, &v)) { + int c = 0; + while (xs_list_next(opts, &v, &c)) { const char *title = xs_dict_get(v, "name"); const char *replies = xs_dict_get(v, "replies"); @@ -794,7 +797,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) xs *idx = NULL; xs *ixc = NULL; - char *tmp; + const char *tmp; xs *mid = mastoapi_id(msg); xs_dict *st = xs_dict_new(); @@ -851,9 +854,9 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) xs *matt = xs_list_new(); while (xs_list_iter(&p, &v)) { - char *type = xs_dict_get(v, "type"); - char *href = xs_dict_get(v, "href"); - char *name = xs_dict_get(v, "name"); + const char *type = xs_dict_get(v, "type"); + const char *href = xs_dict_get(v, "href"); + const char *name = xs_dict_get(v, "name"); if (xs_match(type, "image/*|video/*|Image|Video")) { /* */ xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt)); @@ -879,7 +882,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) xs *ml = xs_list_new(); xs *htl = xs_list_new(); xs *eml = xs_list_new(); - xs_list *tag = xs_dict_get(msg, "tag"); + const xs_list *tag = xs_dict_get(msg, "tag"); int n = 0; xs *tag_list = NULL; @@ -897,7 +900,8 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) tag = tag_list; xs_dict *v; - while (xs_list_iter(&tag, &v)) { + int c = 0; + while (xs_list_next(tag, &v, &c)) { const char *type = xs_dict_get(v, "type"); if (xs_is_null(type)) @@ -1006,7 +1010,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) xs *irt_mid = mastoapi_id(irto); st = xs_dict_set(st, "in_reply_to_id", irt_mid); - char *at = NULL; + const char *at = NULL; if (!xs_is_null(at = get_atto(irto))) { xs *at_md5 = xs_md5_hex(at, strlen(at)); st = xs_dict_set(st, "in_reply_to_account_id", at_md5); @@ -1118,7 +1122,7 @@ int process_auth_token(snac *snac, const xs_dict *req) /* processes an authorization token, if there is one */ { int logged_in = 0; - char *v; + const char *v; /* if there is an authorization field, try to validate it */ if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { @@ -1156,7 +1160,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, return 0; int status = 404; - xs_dict *args = xs_dict_get(req, "q_vars"); + const xs_dict *args = xs_dict_get(req, "q_vars"); xs *cmd = xs_replace_n(q_path, "/api", "", 1); snac snac1 = {0}; @@ -1182,7 +1186,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, acct = xs_dict_append(acct, "source", src); xs *avatar = NULL; - char *av = xs_dict_get(snac1.config, "avatar"); + const char *av = xs_dict_get(snac1.config, "avatar"); if (xs_is_null(av) || *av == '\0') avatar = xs_fmt("%s/susie.png", srv_baseurl); @@ -1193,7 +1197,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, acct = xs_dict_append(acct, "avatar_static", avatar); xs *header = NULL; - char *hd = xs_dict_get(snac1.config, "header"); + const char *hd = xs_dict_get(snac1.config, "header"); if (!xs_is_null(hd)) header = xs_dup(hd); @@ -1203,7 +1207,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, acct = xs_dict_append(acct, "header", header); acct = xs_dict_append(acct, "header_static", header); - xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); + const xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { xs *fields = xs_list_new(); xs_str *k; @@ -1217,7 +1221,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, while (xs_dict_next(metadata, &k, &v, &c)) { xs *val_date = NULL; - xs_number *verified_time = xs_dict_get(val_links, v); + const xs_number *verified_time = xs_dict_get(val_links, v); if (xs_type(verified_time) == XSTYPE_NUMBER) { time_t t = xs_number_get(verified_time); @@ -1283,13 +1287,13 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, else if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/ /* lookup an account */ - char *acct = xs_dict_get(args, "acct"); + const char *acct = xs_dict_get(args, "acct"); if (!xs_is_null(acct)) { xs *s = xs_strip_chars_i(xs_dup(acct), "@"); xs *l = xs_split_n(s, "@", 1); - char *uid = xs_list_get(l, 0); - char *host = xs_list_get(l, 1); + const char *uid = xs_list_get(l, 0); + const char *host = xs_list_get(l, 1); if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) { snac user; @@ -1624,7 +1628,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* get the tag */ xs *l = xs_split(cmd, "/"); - char *tag = xs_list_get(l, -1); + const char *tag = xs_list_get(l, -1); xs *timeline = tag_search(tag, 0, limit); xs *out = xs_list_new(); @@ -1664,7 +1668,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* get the list id */ if (logged_in) { xs *l = xs_split(cmd, "/"); - char *list = xs_list_get(l, -1); + const char *list = xs_list_get(l, -1); xs *timeline = list_timeline(&snac1, list, 0, 2048); xs *out = xs_list_new(); @@ -1744,7 +1748,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *out = xs_list_new(); xs_list *p = l; xs_dict *v; - xs_list *excl = xs_dict_get(args, "exclude_types[]"); + const xs_list *excl = xs_dict_get(args, "exclude_types[]"); while (xs_list_iter(&p, &v)) { xs *noti = notify_get(&snac1, v); @@ -1876,7 +1880,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/ if (logged_in) { xs *l = xs_split(cmd, "/"); - char *p = xs_list_get(l, -1); + const char *p = xs_list_get(l, -1); if (p) { if (strcmp(p, "accounts") == 0) { @@ -1910,7 +1914,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs_list *v; while (xs_list_next(lol, &v, &c)) { - char *id = xs_list_get(v, 0); + const char *id = xs_list_get(v, 0); if (id && strcmp(id, p) == 0) { xs *d = xs_dict_new(); @@ -2314,7 +2318,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, int status = 404; xs *args = NULL; - char *i_ctype = xs_dict_get(req, "content-type"); + const char *i_ctype = xs_dict_get(req, "content-type"); if (i_ctype && xs_startswith(i_ctype, "application/json")) { if (!xs_is_null(payload)) @@ -2487,7 +2491,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, mid = MID_TO_MD5(mid); if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) { - char *id = xs_dict_get(msg, "id"); + const char *id = xs_dict_get(msg, "id"); if (op == NULL) { /* no operation (?) */ @@ -2593,7 +2597,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/ /* I don't know what I'm doing */ if (logged_in) { - char *v; + const char *v; xs *wpush = xs_dict_new(); @@ -2765,7 +2769,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, const char *id = xs_dict_get(msg, "id"); const char *atto = get_atto(msg); - xs_list *opts = xs_dict_get(msg, "oneOf"); + const xs_list *opts = xs_dict_get(msg, "oneOf"); if (opts == NULL) opts = xs_dict_get(msg, "anyOf"); @@ -2773,7 +2777,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, } else if (strcmp(op, "votes") == 0) { - xs_list *choices = xs_dict_get(args, "choices[]"); + const xs_list *choices = xs_dict_get(args, "choices[]"); if (xs_is_null(choices)) choices = xs_dict_get(args, "choices"); @@ -2781,7 +2785,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (xs_type(choices) == XSTYPE_LIST) { xs_str *v; - while (xs_list_iter(&choices, &v)) { + int c = 0; + while (xs_list_next(choices, &v, &c)) { int io = atoi(v); const xs_dict *o = xs_list_get(opts, io); @@ -2843,12 +2848,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/ if (logged_in) { xs *l = xs_split(cmd, "/"); - char *op = xs_list_get(l, -1); - char *id = xs_list_get(l, -2); + const char *op = xs_list_get(l, -1); + const char *id = xs_list_get(l, -2); if (op && id && xs_is_hex(id)) { if (strcmp(op, "accounts") == 0) { - xs_list *accts = xs_dict_get(args, "account_ids[]"); + const xs_list *accts = xs_dict_get(args, "account_ids[]"); int c = 0; char *v; @@ -2888,7 +2893,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, int status = 404; xs *args = NULL; - char *i_ctype = xs_dict_get(req, "content-type"); + const char *i_ctype = xs_dict_get(req, "content-type"); if (i_ctype && xs_startswith(i_ctype, "application/json")) { if (!xs_is_null(payload)) @@ -2921,13 +2926,13 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, if (xs_startswith(cmd, "/v1/lists/")) { if (logged_in) { xs *l = xs_split(cmd, "/"); - char *p = xs_list_get(l, -1); + const char *p = xs_list_get(l, -1); if (p) { if (strcmp(p, "accounts") == 0) { /* delete account from list */ p = xs_list_get(l, -2); - xs_list *accts = xs_dict_get(args, "account_ids[]"); + const xs_list *accts = xs_dict_get(args, "account_ids[]"); int c = 0; char *v; @@ -2971,7 +2976,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path, int status = 404; xs *args = NULL; - char *i_ctype = xs_dict_get(req, "content-type"); + const char *i_ctype = xs_dict_get(req, "content-type"); if (i_ctype && xs_startswith(i_ctype, "application/json")) { if (!xs_is_null(payload)) diff --git a/snac.h b/snac.h index b49fbe7..79e144a 100644 --- a/snac.h +++ b/snac.h @@ -69,7 +69,7 @@ void snac_log(snac *user, xs_str *str); #define snac_debug(user, level, str) do { if (dbglevel >= (level)) \ { snac_log((user), (str)); } } while (0) -int srv_open(char *basedir, int auto_upgrade); +int srv_open(const char *basedir, int auto_upgrade); void srv_free(void); int user_open(snac *snac, const char *uid); @@ -88,7 +88,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req, const char *body, int b_size); void srv_archive_error(const char *prefix, const xs_str *err, const xs_dict *req, const xs_val *data); -void srv_archive_qitem(char *prefix, xs_dict *q_item); +void srv_archive_qitem(const char *prefix, xs_dict *q_item); double mtime_nl(const char *fn, int *n_link); #define mtime(fn) mtime_nl(fn, NULL) @@ -139,13 +139,13 @@ double timeline_mtime(snac *snac); int timeline_touch(snac *snac); 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, char *id); +int timeline_del(snac *snac, const char *id); xs_list *timeline_simple_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 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, xs_list *list); +xs_list *timeline_top_level(snac *snac, const xs_list *list); xs_list *local_list(snac *snac, int max); xs_list *timeline_instance_list(int skip, int show); @@ -174,14 +174,14 @@ void hide(snac *snac, const char *id); int is_hidden(snac *snac, const char *id); void tag_index(const char *id, const xs_dict *obj); -xs_list *tag_search(char *tag, int skip, int show); +xs_list *tag_search(const char *tag, int skip, int show); xs_val *list_maint(snac *user, const char *list, int op); xs_list *list_timeline(snac *user, const char *list, int skip, int show); xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); void list_distribute(snac *user, const char *who, const xs_dict *post); -int actor_add(const char *actor, xs_dict *msg); +int actor_add(const char *actor, const xs_dict *msg); int actor_get(const char *actor, xs_dict **data); int actor_get_refresh(snac *user, const char *actor, xs_dict **data); @@ -223,10 +223,13 @@ xs_list *content_search(snac *user, const char *regex, 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); void enqueue_output_raw(const char *keyid, const char *seckey, - xs_dict *msg, xs_str *inbox, int retries, int p_status); -void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status); -void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries); -void enqueue_email(xs_str *msg, int retries); + const xs_dict *msg, const xs_str *inbox, + int retries, int p_status); +void enqueue_output(snac *snac, const xs_dict *msg, + const xs_str *inbox, int retries, int p_status); +void enqueue_output_by_actor(snac *snac, const xs_dict *msg, + const xs_str *actor, int retries); +void enqueue_email(const xs_str *msg, int retries); void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id); void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token); void enqueue_message(snac *snac, const xs_dict *msg); @@ -234,7 +237,6 @@ void enqueue_close_question(snac *user, const char *id, int end_secs); void enqueue_object_request(snac *user, const char *id, int forward_secs); void enqueue_verify_links(snac *user); void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs); -void enqueue_request_replies(snac *user, const char *id); int was_question_voted(snac *user, const char *id); xs_list *user_queue(snac *snac); @@ -247,16 +249,16 @@ void purge_all(void); xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, const char *method, const char *url, - xs_dict *headers, + const xs_dict *headers, const char *body, int b_size, int *status, xs_str **payload, int *p_size, int timeout); xs_dict *http_signed_request(snac *snac, const char *method, const char *url, - xs_dict *headers, + const xs_dict *headers, const char *body, int b_size, int *status, xs_str **payload, int *p_size, int timeout); -int check_signature(xs_dict *req, xs_str **err); +int check_signature(const xs_dict *req, xs_str **err); srv_state *srv_state_op(xs_str **fname, int op); void httpd(void); @@ -270,21 +272,21 @@ const char *default_avatar_base64(void); xs_str *process_tags(snac *snac, const char *content, xs_list **tag); -char *get_atto(const xs_dict *msg); +const char *get_atto(const xs_dict *msg); xs_list *get_attachments(const xs_dict *msg); -xs_dict *msg_admiration(snac *snac, char *object, char *type); -xs_dict *msg_repulsion(snac *user, char *id, char *type); +xs_dict *msg_admiration(snac *snac, const char *object, const char *type); +xs_dict *msg_repulsion(snac *user, const char *id, const char *type); xs_dict *msg_create(snac *snac, const xs_dict *object); xs_dict *msg_follow(snac *snac, const char *actor); xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, - xs_str *in_reply_to, xs_list *attach, int priv); + const xs_str *in_reply_to, const xs_list *attach, int priv); -xs_dict *msg_undo(snac *snac, char *object); -xs_dict *msg_delete(snac *snac, char *id); +xs_dict *msg_undo(snac *snac, const xs_val *object); +xs_dict *msg_delete(snac *snac, const char *id); xs_dict *msg_actor(snac *snac); -xs_dict *msg_update(snac *snac, xs_dict *object); +xs_dict *msg_update(snac *snac, const xs_dict *object); xs_dict *msg_ping(snac *user, const char *rcpt); xs_dict *msg_pong(snac *user, const char *rcpt, const char *object); xs_dict *msg_question(snac *user, const char *content, xs_list *attach, @@ -292,7 +294,6 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach, int activitypub_request(snac *snac, const char *url, xs_dict **data); int actor_request(snac *user, const char *actor, xs_dict **data); -void timeline_request_replies(snac *user, const char *id); int send_to_inbox_raw(const char *keyid, const char *seckey, const xs_str *inbox, const xs_dict *msg, xs_val **payload, int *p_size, int timeout); diff --git a/upgrade.c b/upgrade.c index 7510ac8..266a4be 100644 --- a/upgrade.c +++ b/upgrade.c @@ -18,7 +18,7 @@ int snac_upgrade(xs_str **error) double f = 0.0; for (;;) { - char *layout = xs_dict_get(srv_config, "layout"); + const char *layout = xs_dict_get(srv_config, "layout"); double nf; f = nf = xs_number_get(layout); @@ -56,8 +56,8 @@ int snac_upgrade(xs_str **error) g = list; while (xs_list_iter(&g, &fn)) { - xs *l = xs_split(fn, "/"); - char *b = xs_list_get(l, -1); + xs *l = xs_split(fn, "/"); + const char *b = xs_list_get(l, -1); xs *dir = xs_fmt("%s/object/%c%c", srv_basedir, b[0], b[1]); xs *nfn = xs_fmt("%s/%s", dir, b); @@ -152,12 +152,12 @@ int snac_upgrade(xs_str **error) xs *o = xs_json_loads(s); fclose(f); - char *type = xs_dict_get(o, "type"); + const char *type = xs_dict_get(o, "type"); if (!xs_is_null(type) && strcmp(type, "Follow") == 0) { unlink(v); - char *actor = xs_dict_get(o, "actor"); + const char *actor = xs_dict_get(o, "actor"); if (!xs_is_null(actor)) follower_add(&snac, actor); @@ -198,22 +198,29 @@ int snac_upgrade(xs_str **error) xs *meta = xs_dup(xs_dict_get(o, "_snac")); o = xs_dict_del(o, "_snac"); - char *id = xs_dict_get(o, "id"); + const char *id = xs_dict_get(o, "id"); /* store object */ object_add_ow(id, o); /* if it's from us, add to public */ if (xs_startswith(id, snac.actor)) { - char *p, *v; + const xs_list *p; + char *v; + int c; object_user_cache_add(&snac, id, "public"); p = xs_dict_get(meta, "announced_by"); - while (xs_list_iter(&p, &v)) + + c = 0; + while (xs_list_next(p, &v, &c)) object_admire(id, v, 0); + p = xs_dict_get(meta, "liked_by"); - while (xs_list_iter(&p, &v)) + + c = 0; + while (xs_list_next(p, &v, &c)) object_admire(id, v, 1); } @@ -257,21 +264,28 @@ int snac_upgrade(xs_str **error) xs *meta = xs_dup(xs_dict_get(o, "_snac")); o = xs_dict_del(o, "_snac"); - char *id = xs_dict_get(o, "id"); + const char *id = xs_dict_get(o, "id"); /* store object */ object_add_ow(id, o); { - char *p, *v; + const xs_list *p; + char *v; + int c = 0; object_user_cache_add(&snac, id, "private"); p = xs_dict_get(meta, "announced_by"); - while (xs_list_iter(&p, &v)) + + c = 0; + while (xs_list_next(p, &v, &c)) object_admire(id, v, 0); + p = xs_dict_get(meta, "liked_by"); - while (xs_list_iter(&p, &v)) + + c = 0; + while (xs_list_next(p, &v, &c)) object_admire(id, v, 1); } diff --git a/utils.c b/utils.c index 0523cac..daaa583 100644 --- a/utils.c +++ b/utils.c @@ -418,7 +418,7 @@ int deluser(snac *user) void verify_links(snac *user) /* verifies a user's links */ { - xs_dict *p = xs_dict_get(user->config, "metadata"); + const xs_dict *p = xs_dict_get(user->config, "metadata"); char *k, *v; int changed = 0; diff --git a/webfinger.c b/webfinger.c index 7255ae2..a12134d 100644 --- a/webfinger.c +++ b/webfinger.c @@ -16,7 +16,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us int p_size = 0; xs *headers = xs_dict_new(); xs *l = NULL; - xs_str *host = NULL; + const char *host = NULL; xs *resource = NULL; if (xs_startswith(qs, "https:/") || xs_startswith(qs, "http:/")) { @@ -87,19 +87,20 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us if (obj) { if (user != NULL) { - char *subject = xs_dict_get(obj, "subject"); + const char *subject = xs_dict_get(obj, "subject"); if (subject) *user = xs_replace_n(subject, "acct:", "", 1); } if (actor != NULL) { - char *list = xs_dict_get(obj, "links"); + const xs_list *list = xs_dict_get(obj, "links"); + int c = 0; char *v; - while (xs_list_iter(&list, &v)) { + while (xs_list_next(list, &v, &c)) { if (xs_type(v) == XSTYPE_DICT) { - char *type = xs_dict_get(v, "type"); + const char *type = xs_dict_get(v, "type"); if (type && (strcmp(type, "application/activity+json") == 0 || strcmp(type, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") == 0)) { @@ -133,8 +134,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path, if (strcmp(q_path, "/.well-known/webfinger") != 0) return 0; - char *q_vars = xs_dict_get(req, "q_vars"); - char *resource = xs_dict_get(q_vars, "resource"); + const char *q_vars = xs_dict_get(req, "q_vars"); + const char *resource = xs_dict_get(q_vars, "resource"); if (resource == NULL) return 400; @@ -145,7 +146,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, if (xs_startswith(resource, "https:/") || xs_startswith(resource, "http:/")) { /* actor search: find a user with this actor */ xs *l = xs_split(resource, "/"); - char *uid = xs_list_get(l, -1); + const char *uid = xs_list_get(l, -1); if (uid) found = user_open(&snac, uid); @@ -163,8 +164,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path, l = xs_split_n(an, "@", 1); if (xs_list_len(l) == 2) { - char *uid = xs_list_get(l, 0); - char *host = xs_list_get(l, 1); + const char *uid = xs_list_get(l, 0); + const char *host = xs_list_get(l, 1); if (strcmp(host, xs_dict_get(srv_config, "host")) == 0) found = user_open(&snac, uid); @@ -194,7 +195,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, links = xs_list_append(links, prof); - char *avatar = xs_dict_get(snac.config, "avatar"); + const char *avatar = xs_dict_get(snac.config, "avatar"); if (!xs_is_null(avatar) && *avatar) { xs *d = xs_dict_new(); diff --git a/xs.h b/xs.h index f5c87ef..b46f0e1 100644 --- a/xs.h +++ b/xs.h @@ -21,8 +21,8 @@ typedef enum { XSTYPE_FALSE = 0x15, /* Boolean */ XSTYPE_LIST = 0x1d, /* Sequence of LITEMs up to EOM (with size) */ XSTYPE_LITEM = 0x1f, /* Element of a list (any type) */ - XSTYPE_DICT = 0x1c, /* Sequence of DITEMs up to EOM (with size) */ - XSTYPE_DITEM = 0x1e, /* Element of a dict (STRING key + any type) */ + XSTYPE_DICT = 0x1c, /* Sequence of KEYVALs up to EOM (with size) */ + XSTYPE_KEYVAL = 0x1e, /* key + value (STRING key + any type) */ XSTYPE_EOM = 0x19, /* End of Multiple (LIST or DICT) */ XSTYPE_DATA = 0x10 /* A block of anonymous data */ } xstype; @@ -32,6 +32,7 @@ typedef enum { typedef char xs_val; typedef char xs_str; typedef char xs_list; +typedef char xs_keyval; typedef char xs_dict; typedef char xs_number; typedef char xs_data; @@ -96,7 +97,7 @@ xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]); int xs_list_iter(xs_list **list, xs_val **value); int xs_list_next(const xs_list *list, xs_val **value, int *ctxt); int xs_list_len(const xs_list *list); -xs_val *xs_list_get(const xs_list *list, int num); +const xs_val *xs_list_get(const xs_list *list, int num); xs_list *xs_list_del(xs_list *list, int num); xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data); xs_list *xs_list_set(xs_list *list, int num, const xs_val *data); @@ -109,14 +110,20 @@ xs_list *xs_split_n(const char *str, const char *sep, int times); #define xs_split(str, sep) xs_split_n(str, sep, XS_ALL) xs_list *xs_list_cat(xs_list *l1, const xs_list *l2); +int xs_keyval_size(const xs_str *key, const xs_val *value); +xs_str *xs_keyval_key(const xs_keyval *keyval); +xs_val *xs_keyval_value(const xs_keyval *keyval); +xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value); + xs_dict *xs_dict_new(void); xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value); xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value); int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt); -xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def); +const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def); #define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL) xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key); xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data); +xs_dict *xs_dict_gc(xs_dict *dict); xs_val *xs_val_new(xstype t); xs_number *xs_number_new(double f); @@ -244,7 +251,7 @@ xstype xs_type(const xs_val *data) case XSTYPE_LIST: case XSTYPE_LITEM: case XSTYPE_DICT: - case XSTYPE_DITEM: + case XSTYPE_KEYVAL: case XSTYPE_NUMBER: case XSTYPE_EOM: case XSTYPE_DATA: @@ -262,7 +269,7 @@ xstype xs_type(const xs_val *data) void _xs_put_size(xs_val *ptr, int i) /* must match _XS_TYPE_SIZE */ { - memcpy(ptr, &i, sizeof(i)); + memcpy(ptr + 1, &i, sizeof(i)); } @@ -296,7 +303,7 @@ int xs_size(const xs_val *data) break; - case XSTYPE_DITEM: + case XSTYPE_KEYVAL: /* calculate the size of the key and the value */ p = data + 1; p += xs_size(p); @@ -380,7 +387,7 @@ xs_val *xs_expand(xs_val *data, int offset, int size) if (xs_type(data) == XSTYPE_LIST || xs_type(data) == XSTYPE_DICT || xs_type(data) == XSTYPE_DATA) - _xs_put_size(data + 1, sz); + _xs_put_size(data, sz); return data; } @@ -405,7 +412,7 @@ xs_val *xs_collapse(xs_val *data, int offset, int size) if (xs_type(data) == XSTYPE_LIST || xs_type(data) == XSTYPE_DICT || xs_type(data) == XSTYPE_DATA) - _xs_put_size(data + 1, sz); + _xs_put_size(data, sz); return xs_realloc(data, _xs_blk_size(sz)); } @@ -666,10 +673,10 @@ xs_list *xs_list_new(void) { int sz = 1 + _XS_TYPE_SIZE + 1; xs_list *l = xs_realloc(NULL, sz); - memset(l, '\0', sz); + memset(l, XSTYPE_EOM, sz); l[0] = XSTYPE_LIST; - _xs_put_size(&l[1], sz); + _xs_put_size(l, sz); return l; } @@ -802,7 +809,7 @@ int xs_list_len(const xs_list *list) } -xs_val *xs_list_get(const xs_list *list, int num) +const xs_val *xs_list_get(const xs_list *list, int num) /* returns the element #num */ { XS_ASSERT_TYPE(list, XSTYPE_LIST); @@ -830,7 +837,7 @@ xs_list *xs_list_del(xs_list *list, int num) { XS_ASSERT_TYPE(list, XSTYPE_LIST); - xs_val *v; + const xs_val *v; if ((v = xs_list_get(list, num)) != NULL) list = xs_collapse(list, v - 1 - list, xs_size(v - 1)); @@ -844,7 +851,7 @@ xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data) { XS_ASSERT_TYPE(list, XSTYPE_LIST); - xs_val *v; + const xs_val *v; int offset; if ((v = xs_list_get(list, num)) != NULL) @@ -999,6 +1006,40 @@ xs_list *xs_list_cat(xs_list *l1, const xs_list *l2) } +/** keyvals **/ + +int xs_keyval_size(const xs_str *key, const xs_val *value) +/* returns the needed size for a keyval */ +{ + return 1 + xs_size(key) + xs_size(value); +} + + +xs_str *xs_keyval_key(const xs_keyval *keyval) +/* returns a pointer to the key of the keyval */ +{ + return (xs_str *)&keyval[1]; +} + + +xs_val *xs_keyval_value(const xs_keyval *keyval) +/* returns a pointer to the value of the keyval */ +{ + return (xs_val *)&keyval[1 + xs_size(xs_keyval_key(keyval))]; +} + + +xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value) +/* builds a keyval into mem (should have enough size) */ +{ + keyval[0] = XSTYPE_KEYVAL; + memcpy(xs_keyval_key(keyval), key, xs_size(key)); + memcpy(xs_keyval_value(keyval), value, xs_size(value)); + + return keyval; +} + + /** dicts **/ xs_dict *xs_dict_new(void) @@ -1006,34 +1047,27 @@ xs_dict *xs_dict_new(void) { int sz = 1 + _XS_TYPE_SIZE + 1; xs_dict *d = xs_realloc(NULL, sz); - memset(d, '\0', sz); + memset(d, XSTYPE_EOM, sz); d[0] = XSTYPE_DICT; - _xs_put_size(&d[1], sz); + _xs_put_size(d, sz); return d; } -xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key, - const xs_val *data, int dsz) -/* inserts a memory block into the dict */ +xs_dict *_xs_dict_write_keyval(xs_dict *dict, int offset, const xs_str *key, const xs_val *value) +/* adds a new keyval to the dict */ { XS_ASSERT_TYPE(dict, XSTYPE_DICT); XS_ASSERT_TYPE(key, XSTYPE_STRING); - if (data == NULL) { - data = xs_stock(XSTYPE_NULL); - dsz = xs_size(data); - } + if (value == NULL) + value = xs_stock(XSTYPE_NULL); - int ksz = xs_size(key); + dict = xs_expand(dict, offset, xs_keyval_size(key, value)); - dict = xs_expand(dict, offset, 1 + ksz + dsz); - - dict[offset] = XSTYPE_DITEM; - memcpy(&dict[offset + 1], key, ksz); - memcpy(&dict[offset + 1 + ksz], data, dsz); + xs_keyval_make(&dict[offset], key, value); return dict; } @@ -1042,14 +1076,14 @@ xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key, xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) /* appends a memory block to the dict */ { - return _xs_dict_write_ditem(dict, xs_size(dict) - 1, key, value, xs_size(value)); + return _xs_dict_write_keyval(dict, xs_size(dict) - 1, key, value); } xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) /* prepends a memory block to the dict */ { - return _xs_dict_write_ditem(dict, 1 + _XS_TYPE_SIZE, key, value, xs_size(value)); + return _xs_dict_write_keyval(dict, 1 + _XS_TYPE_SIZE, key, value); } @@ -1070,7 +1104,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt) p += *ctxt; /* an element? */ - if (xs_type(p) == XSTYPE_DITEM) { + if (xs_type(p) == XSTYPE_KEYVAL) { p++; *key = p; @@ -1091,7 +1125,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt) } -xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def) +const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def) /* returns the value directed by key, or the default value */ { XS_ASSERT_TYPE(dict, XSTYPE_DICT); @@ -1150,6 +1184,14 @@ xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data) } +xs_dict *xs_dict_gc(xs_dict *dict) +/* collects garbage (leaked values) inside a dict */ +{ + /* this kind of dicts does not get garbage */ + return dict; +} + + /** other values **/ xs_val *xs_val_new(xstype t) @@ -1235,7 +1277,7 @@ xs_data *xs_data_new(const void *data, int size) v = xs_realloc(NULL, _xs_blk_size(total_size)); v[0] = XSTYPE_DATA; - _xs_put_size(v + 1, total_size); + _xs_put_size(v, total_size); memcpy(&v[1 + _XS_TYPE_SIZE], data, size); diff --git a/xs_curl.h b/xs_curl.h index f7783b9..2628d91 100644 --- a/xs_curl.h +++ b/xs_curl.h @@ -28,7 +28,7 @@ static size_t _header_callback(char *buffer, size_t size, if (xs_str_in(l, ": ") != -1) { xs *knv = xs_split_n(l, ": ", 1); - xs_tolower_i(xs_list_get(knv, 0)); + xs_tolower_i((xs_str *)xs_list_get(knv, 0)); headers = xs_dict_set(headers, xs_list_get(knv, 0), xs_list_get(knv, 1)); } diff --git a/xs_html.h b/xs_html.h index 80ae652..a95d45a 100644 --- a/xs_html.h +++ b/xs_html.h @@ -6,26 +6,26 @@ typedef struct xs_html xs_html; -xs_str *xs_html_encode(char *str); +xs_str *xs_html_encode(const char *str); -xs_html *xs_html_attr(char *key, char *value); -xs_html *xs_html_text(char *content); -xs_html *xs_html_raw(char *content); +xs_html *xs_html_attr(const char *key, const char *value); +xs_html *xs_html_text(const char *content); +xs_html *xs_html_raw(const char *content); xs_html *_xs_html_add(xs_html *tag, xs_html *var[]); #define xs_html_add(tag, ...) _xs_html_add(tag, (xs_html *[]) { __VA_ARGS__, NULL }) -xs_html *_xs_html_tag(char *tag, xs_html *var[]); +xs_html *_xs_html_tag(const char *tag, xs_html *var[]); #define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) -xs_html *_xs_html_sctag(char *tag, xs_html *var[]); +xs_html *_xs_html_sctag(const char *tag, xs_html *var[]); #define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) xs_html *_xs_html_container(xs_html *var[]); #define xs_html_container(...) _xs_html_container((xs_html *[]) { __VA_ARGS__, NULL }) void xs_html_render_f(xs_html *h, FILE *f); -xs_str *xs_html_render_s(xs_html *tag, char *prefix); +xs_str *xs_html_render_s(xs_html *tag, const char *prefix); #define xs_html_render(tag) xs_html_render_s(tag, NULL) @@ -47,16 +47,16 @@ struct xs_html { xs_html *next; }; -xs_str *xs_html_encode(char *str) +xs_str *xs_html_encode(const char *str) /* encodes str using HTML entities */ { xs_str *s = xs_str_new(NULL); int o = 0; - char *e = str + strlen(str); + const char *e = str + strlen(str); for (;;) { char *ec = "<>\"'&"; /* characters to escape */ - char *q = e; + const char *q = e; int z; /* find the nearest happening of a char */ @@ -90,7 +90,7 @@ xs_str *xs_html_encode(char *str) #define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html)) -xs_html *xs_html_attr(char *key, char *value) +xs_html *xs_html_attr(const char *key, const char *value) /* creates an HTML block with an attribute */ { xs_html *a = XS_HTML_NEW(); @@ -108,7 +108,7 @@ xs_html *xs_html_attr(char *key, char *value) } -xs_html *xs_html_text(char *content) +xs_html *xs_html_text(const char *content) /* creates an HTML block of text, escaping it previously */ { xs_html *a = XS_HTML_NEW(); @@ -120,7 +120,7 @@ xs_html *xs_html_text(char *content) } -xs_html *xs_html_raw(char *content) +xs_html *xs_html_raw(const char *content) /* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */ { xs_html *a = XS_HTML_NEW(); @@ -152,7 +152,7 @@ xs_html *_xs_html_add(xs_html *tag, xs_html *var[]) } -static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[]) +static xs_html *_xs_html_tag_t(xs_html_type type, const char *tag, xs_html *var[]) /* creates a tag with a variable list of attributes and subtags */ { xs_html *a = XS_HTML_NEW(); @@ -169,13 +169,13 @@ static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[]) } -xs_html *_xs_html_tag(char *tag, xs_html *var[]) +xs_html *_xs_html_tag(const char *tag, xs_html *var[]) { return _xs_html_tag_t(XS_HTML_TAG, tag, var); } -xs_html *_xs_html_sctag(char *tag, xs_html *var[]) +xs_html *_xs_html_sctag(const char *tag, xs_html *var[]) { return _xs_html_tag_t(XS_HTML_SCTAG, tag, var); } @@ -239,7 +239,7 @@ void xs_html_render_f(xs_html *h, FILE *f) } -xs_str *xs_html_render_s(xs_html *tag, char *prefix) +xs_str *xs_html_render_s(xs_html *tag, const char *prefix) /* renders to a string */ { xs_str *s = NULL; diff --git a/xs_httpd.h b/xs_httpd.h index 4d006d7..60933c8 100644 --- a/xs_httpd.h +++ b/xs_httpd.h @@ -16,7 +16,7 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size) xs *q_vars = NULL; xs *p_vars = NULL; xs *l1, *l2; - char *v; + const char *v; xs_socket_timeout(fileno(f), 2.0, 0.0); @@ -60,7 +60,8 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size) p = xs_split_n(l, ": ", 1); if (xs_list_len(p) == 2) - req = xs_dict_append(req, xs_tolower_i(xs_list_get(p, 0)), xs_list_get(p, 1)); + req = xs_dict_append(req, xs_tolower_i( + (xs_str *)xs_list_get(p, 0)), xs_list_get(p, 1)); } xs_socket_timeout(fileno(f), 5.0, 0.0); diff --git a/xs_json.h b/xs_json.h index 6706d7e..3a7742d 100644 --- a/xs_json.h +++ b/xs_json.h @@ -71,12 +71,12 @@ static void _xs_json_indent(int level, int indent, FILE *f) } -static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f) +static void _xs_json_dump(const xs_val *data, int level, int indent, FILE *f) /* dumps partial data as JSON */ { int c = 0; + int ct = 0; xs_val *v; - xs_val *data = (xs_val *)s_data; switch (xs_type(data)) { case XSTYPE_NULL: @@ -98,7 +98,7 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f) case XSTYPE_LIST: fputc('[', f); - while (xs_list_iter(&data, &v)) { + while (xs_list_next(data, &v, &ct)) { if (c != 0) fputc(',', f); @@ -117,9 +117,8 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f) fputc('{', f); xs_str *k; - int ct = 0; - while (xs_dict_next(s_data, &k, &v, &ct)) { + while (xs_dict_next(data, &k, &v, &ct)) { if (c != 0) fputc(',', f); diff --git a/xs_set.h b/xs_set.h index 3a334e4..b7eb091 100644 --- a/xs_set.h +++ b/xs_set.h @@ -104,7 +104,7 @@ int xs_set_add(xs_set *s, const xs_val *data) /* if it's new, add the data */ if (ret) - s->list = xs_list_append_m(s->list, data, xs_size(data)); + s->list = xs_list_append(s->list, data); return ret; } diff --git a/xs_unicode.h b/xs_unicode.h index 6654da4..1799d89 100644 --- a/xs_unicode.h +++ b/xs_unicode.h @@ -6,7 +6,7 @@ int _xs_utf8_enc(char buf[4], unsigned int cpoint); int xs_is_utf8_cont_byte(char c); - unsigned int xs_utf8_dec(char **str); + unsigned int xs_utf8_dec(const char **str); int xs_unicode_width(unsigned int cpoint); int xs_is_surrogate(unsigned int cpoint); unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2); @@ -66,10 +66,10 @@ int xs_is_utf8_cont_byte(char c) } -unsigned int xs_utf8_dec(char **str) +unsigned int xs_utf8_dec(const char **str) /* decodes an utf-8 char inside str and updates the pointer */ { - char *p = *str; + const char *p = *str; unsigned int cpoint = 0; unsigned char c = *p++; int cb = 0; diff --git a/xs_url.h b/xs_url.h index 6c9c8b5..69313b6 100644 --- a/xs_url.h +++ b/xs_url.h @@ -119,8 +119,8 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea while ((p = xs_memmem(payload + offset, p_size - offset, boundary, bsz)) != NULL) { xs *s1 = NULL; xs *l1 = NULL; - char *vn = NULL; - char *fn = NULL; + const char *vn = NULL; + const char *fn = NULL; char *q; int po, ps; diff --git a/xs_version.h b/xs_version.h index 16faf2b..9ecf9b8 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 6e75e8736f7f1b6ea6c6774d4bd922b3ad56b771 2024-05-15T11:42:19+02:00 */ +/* 34850dcdec50b669a2c0bbe9f16f6d9c4b16eafd 2024-05-21T14:06:02+02:00 */ From cdfaf6dc57a03503cec46cad5fbfd6fec45b0f52 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 May 2024 18:57:13 +0200 Subject: [PATCH 107/120] New compilation variable WITHOUT_SHM, to disable shared memory functions. --- README.md | 6 ++++++ httpd.c | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/README.md b/README.md index 1e5c2e8..6c1e31b 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,12 @@ If your compilation process complains about undefined references to `shm_open()` make LDFLAGS=-lrt ``` +If it still doesn't work (because your system does not implement the shared memory functions) or you are just not interested, you can compile out with + +```sh +make CFLAGS=-DWITHOUT_SHM +``` + See the administrator manual on how to proceed from here. ## Testing via Docker diff --git a/httpd.c b/httpd.c index d63fa0f..993aa1c 100644 --- a/httpd.c +++ b/httpd.c @@ -653,6 +653,29 @@ void term_handler(int s) } +#ifdef WITHOUT_SHM + +/* dummy versions */ + +int shm_open(const char *name, int flags, mode_t mode) +{ + (void)name; + (void)flags; + (void)mode; + + errno = ENOTSUP; + return -1; +} + +int shm_unlink(const char *name) +{ + (void)name; + return -1; +} + + +#endif + srv_state *srv_state_op(xs_str **fname, int op) /* opens or deletes the shared memory object */ { From ed973241f44f8dd1b688bbab37018f33160027bf Mon Sep 17 00:00:00 2001 From: default Date: Tue, 21 May 2024 19:08:33 +0200 Subject: [PATCH 108/120] Another approach to disabling SHM. --- httpd.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/httpd.c b/httpd.c index 993aa1c..8a20c89 100644 --- a/httpd.c +++ b/httpd.c @@ -653,29 +653,6 @@ void term_handler(int s) } -#ifdef WITHOUT_SHM - -/* dummy versions */ - -int shm_open(const char *name, int flags, mode_t mode) -{ - (void)name; - (void)flags; - (void)mode; - - errno = ENOTSUP; - return -1; -} - -int shm_unlink(const char *name) -{ - (void)name; - return -1; -} - - -#endif - srv_state *srv_state_op(xs_str **fname, int op) /* opens or deletes the shared memory object */ { @@ -687,6 +664,13 @@ srv_state *srv_state_op(xs_str **fname, int op) switch (op) { case 0: /* open for writing */ + +#ifdef WITHOUT_SHM + + errno = ENOTSUP; + +#else + if ((fd = shm_open(*fname, O_CREAT | O_RDWR, 0666)) != -1) { ftruncate(fd, sizeof(*ss)); @@ -697,6 +681,8 @@ srv_state *srv_state_op(xs_str **fname, int op) close(fd); } +#endif + if (ss == NULL) { /* shared memory error: just create a plain structure */ srv_log(xs_fmt("warning: shm object error (%s)", strerror(errno))); @@ -710,6 +696,13 @@ srv_state *srv_state_op(xs_str **fname, int op) break; case 1: /* open for reading */ + +#ifdef WITHOUT_SHM + + errno = ENOTSUP; + +#else + if ((fd = shm_open(*fname, O_RDONLY, 0666)) != -1) { if ((ss = mmap(0, sizeof(*ss), PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) ss = NULL; @@ -717,6 +710,8 @@ srv_state *srv_state_op(xs_str **fname, int op) close(fd); } +#endif + if (ss == NULL) { /* shared memory error */ srv_log(xs_fmt("error: shm object error (%s) server not running?", strerror(errno))); @@ -734,9 +729,14 @@ srv_state *srv_state_op(xs_str **fname, int op) break; case 2: /* unlink */ + +#ifndef WITHOUT_SHM + if (*fname) shm_unlink(*fname); +#endif + break; } From c9c9b0bfdceefbded1250ad3947d6248232e080c Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 May 2024 09:37:44 +0200 Subject: [PATCH 109/120] Updated README. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6c1e31b..4cd3777 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ Run `make` and then `make install` as root. If you're compiling on NetBSD, you should use the specific provided Makefile and run `make -f Makefile.NetBSD` and then `make -f Makefile.NetBSD install` as root. - From version 2.27, `snac` includes support for the Mastodon API; if you are not interested on it, you can compile it out by running ```sh @@ -71,7 +70,7 @@ If your compilation process complains about undefined references to `shm_open()` make LDFLAGS=-lrt ``` -If it still doesn't work (because your system does not implement the shared memory functions) or you are just not interested, you can compile out with +If it still gives compilation errors (because your system does not implement the shared memory functions), you can fix it with ```sh make CFLAGS=-DWITHOUT_SHM From 59bdaca3a87189bd6f634716e4d7ccf85081ff89 Mon Sep 17 00:00:00 2001 From: Steve Bate Date: Wed, 22 May 2024 09:43:36 +0200 Subject: [PATCH 110/120] Change WebFinger content type to RFC-required type --- webfinger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfinger.c b/webfinger.c index 7255ae2..2e5ec29 100644 --- a/webfinger.c +++ b/webfinger.c @@ -214,7 +214,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path, status = 200; *body = j; - *ctype = "application/json"; + *ctype = "application/jrd+json"; } else status = 404; From 8fd070bb9a1a8e355f0899ef2f02c55ab90a09d8 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 May 2024 14:14:29 +0200 Subject: [PATCH 111/120] Fixed warning. --- data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data.c b/data.c index 3377f3e..6ac115d 100644 --- a/data.c +++ b/data.c @@ -114,7 +114,7 @@ int srv_open(const char *basedir, int auto_upgrade) #endif #ifdef __OpenBSD__ - char *v = xs_dict_get(srv_config, "disable_openbsd_security"); + const char *v = xs_dict_get(srv_config, "disable_openbsd_security"); if (v && xs_type(v) == XSTYPE_TRUE) { srv_debug(1, xs_dup("OpenBSD security disabled by admin")); From 63e0ca5abac0f00121f724927c25edaf822097a9 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 22 May 2024 16:14:58 +0200 Subject: [PATCH 112/120] Also return an application/ld+json object in webfinger. --- webfinger.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/webfinger.c b/webfinger.c index cad86e4..b1d85e9 100644 --- a/webfinger.c +++ b/webfinger.c @@ -103,7 +103,8 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us const char *type = xs_dict_get(v, "type"); if (type && (strcmp(type, "application/activity+json") == 0 || - strcmp(type, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") == 0)) { + strcmp(type, "application/ld+json; profile=\"https:/" + "/www.w3.org/ns/activitystreams\"") == 0)) { *actor = xs_dup(xs_dict_get(v, "href")); break; } @@ -189,6 +190,12 @@ int webfinger_get_handler(xs_dict *req, char *q_path, links = xs_list_append(links, aaj); + /* duplicate with the ld+json type */ + aaj = xs_dict_set(aaj, "type", "application/ld+json; profile=\"https:/" + "/www.w3.org/ns/activitystreams\""); + + links = xs_list_append(links, aaj); + prof = xs_dict_append(prof, "rel", "http://webfinger.net/rel/profile-page"); prof = xs_dict_append(prof, "type", "text/html"); prof = xs_dict_append(prof, "href", snac.actor); From 8cf7559a7e21c5757455b948814d61e6e96f08f1 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 23 May 2024 10:01:37 +0200 Subject: [PATCH 113/120] Added more const. --- activitypub.c | 39 +++++++++++++++++----------------- data.c | 58 +++++++++++++++++++++++++++------------------------ format.c | 11 ++++++---- html.c | 36 ++++++++++++++++---------------- http.c | 6 +++--- httpd.c | 9 ++++---- main.c | 2 +- mastoapi.c | 50 ++++++++++++++++++++++---------------------- upgrade.c | 37 ++++++++++++++++++++------------ utils.c | 10 ++++----- webfinger.c | 2 +- xs.h | 34 +++++++++++++++--------------- xs_curl.h | 4 ++-- xs_fcgi.h | 4 ++-- xs_httpd.h | 4 ++-- xs_json.h | 4 ++-- xs_regex.h | 4 ++-- xs_set.h | 3 ++- xs_url.h | 2 +- xs_version.h | 2 +- 20 files changed, 171 insertions(+), 150 deletions(-) diff --git a/activitypub.c b/activitypub.c index f4b4eac..6e40a88 100644 --- a/activitypub.c +++ b/activitypub.c @@ -163,7 +163,7 @@ const char *get_atto(const xs_dict *msg) if (xs_type(actor) == XSTYPE_LIST) { const xs_list *p = actor; int c = 0; - xs_dict *v; + const xs_dict *v; actor = NULL; while (actor == NULL && xs_list_next(p, &v, &c)) { @@ -192,7 +192,7 @@ xs_list *get_attachments(const xs_dict *msg) /* try first the attachments list */ if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) { xs *attach = NULL; - xs_val *v; + const xs_val *v; /* ensure it's a list */ if (xs_type(p) == XSTYPE_DICT) { @@ -257,7 +257,7 @@ xs_list *get_attachments(const xs_dict *msg) const char *href = NULL; const char *type = NULL; int c = 0; - xs_val *v; + const xs_val *v; while (href == NULL && xs_list_next(p, &v, &c)) { if (xs_type(v) == XSTYPE_DICT) { @@ -271,7 +271,7 @@ xs_list *get_attachments(const xs_dict *msg) strcmp(mtype, "application/x-mpegURL") == 0 && xs_type(tag) == XSTYPE_LIST) { /* now iterate the tag list, looking for a video URL */ - xs_dict *d; + const xs_dict *d; int c = 0; while (href == NULL && xs_list_next(tag, &d, &c)) { @@ -482,7 +482,7 @@ xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public) const xs_list *lists[] = { to, cc, NULL }; for (n = 0; lists[n]; n++) { xs_list *l = (xs_list *)lists[n]; - char *v; + const char *v; xs *tl = NULL; /* if it's a string, create a list with only one element */ @@ -497,7 +497,7 @@ xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public) if (expand_public && strcmp(v, public_address) == 0) { /* iterate the followers and add them */ xs *fwers = follower_list(snac); - char *actor; + const char *actor; char *p = fwers; while (xs_list_iter(&p, &actor)) @@ -628,7 +628,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) const xs_dict *msg = xs_dict_get(c_msg, "object"); xs *rcpts = recipient_list(snac, msg, 0); xs_list *p = rcpts; - xs_str *v; + const xs_str *v; xs *actor_followers = NULL; @@ -693,7 +693,7 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag) xs_list *tl = *tag; xs *split; xs_list *p; - xs_val *v; + const xs_val *v; int n = 0; /* create a default server for incomplete mentions */ @@ -1205,8 +1205,8 @@ xs_dict *msg_actor(snac *snac) const xs_dict *metadata = xs_dict_get(snac->config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { xs *attach = xs_list_new(); - xs_str *k; - xs_str *v; + const xs_str *k; + const xs_str *v; int c = 0; while (xs_dict_next(metadata, &k, &v, &c)) { @@ -1351,7 +1351,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, xs *atls = xs_list_new(); xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL); xs_list *p; - xs_val *v; + const xs_val *v; if (rcpts == NULL) to = xs_list_new(); @@ -1528,7 +1528,7 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach, xs *o = xs_list_new(); xs_list *p = (xs_list *)opts; - xs_str *v; + const xs_str *v; xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}"); xs_set_init(&seen); @@ -1576,7 +1576,7 @@ int update_question(snac *user, const char *id) xs *lopts = xs_list_new(); const xs_list *opts; xs_list *p; - xs_val *v; + const xs_val *v; /* get the object */ if (!valid_status(object_get(id, &msg))) @@ -2190,7 +2190,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) xs *rcpts = recipient_list(snac, msg, 1); xs_set inboxes; xs_list *p; - xs_str *actor; + const xs_str *actor; xs_set_init(&inboxes); @@ -2212,7 +2212,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item) if (is_msg_public(msg)) { if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) { xs *shibx = inbox_list(); - xs_str *inbox; + const xs_str *inbox; p = shibx; while (xs_list_iter(&p, &inbox)) { @@ -2304,7 +2304,7 @@ int process_user_queue(snac *snac) xs *list = user_queue(snac); xs_list *p = list; - xs_str *fn; + const xs_str *fn; while (xs_list_iter(&p, &fn)) { xs *q_item = dequeue(fn); @@ -2511,7 +2511,7 @@ void process_queue_item(xs_dict *q_item) xs *users = user_list(); xs_list *p = users; - char *v; + const char *v; int cnt = 0; while (xs_list_iter(&p, &v)) { @@ -2554,7 +2554,7 @@ int process_queue(void) xs *list = queue(); xs_list *p = list; - xs_str *fn; + const xs_str *fn; while (xs_list_iter(&p, &fn)) { xs *q_item = dequeue(fn); @@ -2616,7 +2616,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, xs *elems = timeline_simple_list(&snac, "public", 0, 20); xs *list = xs_list_new(); msg = msg_collection(&snac, id); - char *p, *v; + char *p; + const char *v; p = elems; while (xs_list_iter(&p, &v)) { diff --git a/data.c b/data.c index 6ac115d..f507078 100644 --- a/data.c +++ b/data.c @@ -193,7 +193,7 @@ int user_open(snac *user, const char *uid) xs *lcuid = xs_tolower_i(xs_dup(uid)); xs *ulist = user_list(); xs_list *p = ulist; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *v2 = xs_tolower_i(xs_dup(v)); @@ -289,7 +289,7 @@ int user_open_by_md5(snac *snac, const char *md5) { xs *ulist = user_list(); xs_list *p = ulist; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { user_open(snac, v); @@ -772,7 +772,8 @@ int object_del_by_md5(const char *md5) xs *spec = xs_dup(fn); spec = xs_replace_i(spec, ".json", "*.idx"); xs *files = xs_glob(spec, 0, 0); - char *p, *v; + char *p; + const char *v; p = files; while (xs_list_iter(&p, &v)) { @@ -1033,7 +1034,8 @@ xs_list *follower_list(snac *snac) { xs *list = object_user_cache_list(snac, "followers", XS_ALL, 0); xs_list *fwers = xs_list_new(); - char *p, *v; + char *p; + const char *v; /* resolve the list of md5 to be a list of actors */ p = list; @@ -1196,7 +1198,7 @@ xs_list *timeline_top_level(snac *snac, const xs_list *list) /* returns the top level md5 entries from this index */ { xs_set seen; - xs_str *v; + const xs_str *v; xs_set_init(&seen); @@ -1367,7 +1369,7 @@ xs_list *following_list(snac *snac) xs *spec = xs_fmt("%s/following/" "*.json", snac->basedir); xs *glist = xs_glob(spec, 0, 0); xs_list *p; - xs_str *v; + const xs_str *v; xs_list *list = xs_list_new(); /* iterate the list of files */ @@ -1537,7 +1539,8 @@ void hide(snac *snac, const char *id) /* hide all the children */ xs *chld = object_children(id); - char *p, *v; + char *p; + const char *v; p = chld; while (xs_list_iter(&p, &v)) { @@ -1694,7 +1697,7 @@ void tag_index(const char *id, const xs_dict *obj) mkdirx(g_tag_dir); - xs_dict *v; + const xs_dict *v; int ct = 0; while (xs_list_next(tags, &v, &ct)) { const char *type = xs_dict_get(v, "type"); @@ -1758,7 +1761,7 @@ xs_val *list_maint(snac *user, const char *list, int op) xs *spec = xs_fmt("%s/list/" "*.id", user->basedir); xs *ls = xs_glob(spec, 0, 0); int c = 0; - char *v; + const char *v; l = xs_list_new(); @@ -1784,7 +1787,7 @@ xs_val *list_maint(snac *user, const char *list, int op) { xs *lol = list_maint(user, NULL, 0); int c = 0; - xs_list *v; + const xs_list *v; int add = 1; /* check if this list name already exists */ @@ -1925,7 +1928,7 @@ void list_distribute(snac *user, const char *who, const xs_dict *post) xs *spec = xs_fmt("%s/list/" "*.lst", user->basedir); xs *ls = xs_glob(spec, 0, 0); int c = 0; - char *v; + const char *v; while (xs_list_next(ls, &v, &c)) { /* is the actor in this list? */ @@ -2183,7 +2186,7 @@ xs_list *inbox_list(void) xs *spec = xs_fmt("%s/inbox/" "*", srv_basedir); xs *files = xs_glob(spec, 0, 0); xs_list *p = files; - xs_val *v; + const xs_val *v; while (xs_list_iter(&p, &v)) { FILE *f; @@ -2329,8 +2332,8 @@ xs_list *content_search(snac *user, const char *regex, /* iterate all timelines simultaneously */ xs_list *tls[3] = {0}; - char *md5s[3] = {0}; - int c[3] = {0}; + const char *md5s[3] = {0}; + int c[3] = {0}; tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* public */ tls[1] = timeline_instance_list(0, XS_ALL); /* instance */ @@ -2368,7 +2371,7 @@ xs_list *content_search(snac *user, const char *regex, if (newest == -1) break; - char *md5 = md5s[newest]; + const char *md5 = md5s[newest]; /* advance the chosen timeline */ if (!xs_list_next(tls[newest], &md5s[newest], &c[newest])) @@ -2534,7 +2537,7 @@ xs_list *notify_list(snac *snac, int skip, int show) xs *spec = xs_fmt("%s/notify/" "*.json", snac->basedir); xs *lst = xs_glob(spec, 1, 0); xs_list *p = lst; - char *v; + const char *v; while (xs_list_iter(&p, &v)) { char *p = strrchr(v, '.'); @@ -2562,7 +2565,7 @@ int notify_new_num(snac *snac) int cnt = 0; xs_list *p = lst; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *id = xs_strip_i(xs_dup(v)); @@ -2584,7 +2587,7 @@ void notify_clear(snac *snac) xs *spec = xs_fmt("%s/notify/" "*", snac->basedir); xs *lst = xs_glob(spec, 0, 0); xs_list *p = lst; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) unlink(v); @@ -2842,7 +2845,7 @@ int was_question_voted(snac *user, const char *id) xs *children = object_children(id); int voted = 0; xs_list *p; - xs_str *md5; + const xs_str *md5; p = children; while (xs_list_iter(&p, &md5)) { @@ -2869,7 +2872,7 @@ xs_list *user_queue(snac *snac) xs_list *list = xs_list_new(); time_t t = time(NULL); xs_list *p; - xs_val *v; + const xs_val *v; xs *fns = xs_glob(spec, 0, 0); @@ -2898,7 +2901,7 @@ xs_list *queue(void) xs_list *list = xs_list_new(); time_t t = time(NULL); xs_list *p; - xs_val *v; + const xs_val *v; xs *fns = xs_glob(spec, 0, 0); @@ -2974,7 +2977,7 @@ static void _purge_dir(const char *dir, int days) xs *spec = xs_fmt("%s/" "*", dir); xs *list = xs_glob(spec, 0, 0); xs_list *p; - xs_str *v; + const xs_str *v; p = list; while (xs_list_iter(&p, &v)) @@ -3000,7 +3003,7 @@ void purge_server(void) xs *spec = xs_fmt("%s/object/??", srv_basedir); xs *dirs = xs_glob(spec, 0, 0); xs_list *p; - xs_str *v; + const xs_str *v; int cnt = 0; int icnt = 0; @@ -3009,7 +3012,7 @@ void purge_server(void) p = dirs; while (xs_list_iter(&p, &v)) { xs_list *p2; - xs_str *v2; + const xs_str *v2; { xs *spec2 = xs_fmt("%s/" "*.json", v); @@ -3088,7 +3091,7 @@ void purge_server(void) xs *spec2 = xs_fmt("%s/" "*.idx", v); xs *files = xs_glob(spec2, 0, 0); xs_list *p2; - xs_str *v2; + const xs_str *v2; p2 = files; while (xs_list_iter(&p2, &v2)) { @@ -3152,7 +3155,7 @@ void purge_user(snac *snac) xs *spec = xs_fmt("%s/list/" "*.idx", snac->basedir); xs *lol = xs_glob(spec, 0, 0); int c = 0; - char *v; + const char *v; while (xs_list_next(lol, &v, &c)) { int gc = index_gc(v); @@ -3170,7 +3173,8 @@ void purge_all(void) { snac snac; xs *list = user_list(); - char *p, *uid; + char *p; + const char *uid; p = list; while (xs_list_iter(&p, &uid)) { diff --git a/format.c b/format.c index df3b5d9..b021f55 100644 --- a/format.c +++ b/format.c @@ -82,7 +82,8 @@ static xs_str *format_line(const char *line, xs_list **attach) /* formats a line */ { xs_str *s = xs_str_new(NULL); - char *p, *v; + char *p; + const char *v; /* split by markup */ xs *sm = xs_regex_split(line, @@ -155,7 +156,8 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag int in_pre = 0; int in_blq = 0; xs *list; - char *p, *v; + char *p; + const char *v; /* work by lines */ list = xs_split(content, "\n"); @@ -234,7 +236,7 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag /* traditional emoticons */ xs *d = emojis(); int c = 0; - char *k, *v; + const char *k, *v; while (xs_dict_next(d, &k, &v, &c)) { const char *t = NULL; @@ -280,7 +282,8 @@ xs_str *sanitize(const char *content) xs_str *s = xs_str_new(NULL); xs *sl; int n = 0; - char *p, *v; + char *p; + const char *v; sl = xs_regex_split(content, "]+>"); diff --git a/html.c b/html.c index 6521726..11fe4bf 100644 --- a/html.c +++ b/html.c @@ -57,7 +57,7 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems) xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); - char *v; + const char *v; int c = 0; while (xs_list_next(tag_list, &v, &c)) { @@ -461,7 +461,7 @@ static xs_html *html_base_head(void) xs *f; f = xs_fmt("%s/favicon.ico", srv_baseurl); const xs_list *p = xs_dict_get(srv_config, "cssurls"); - char *v; + const char *v; int c = 0; while (xs_list_next(p, &v, &c)) { @@ -801,8 +801,8 @@ static xs_html *html_user_body(snac *user, int read_only) const xs_dict *metadata = xs_dict_get(user->config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { - xs_str *k; - xs_str *v; + const xs_str *k; + const xs_str *v; xs_dict *val_links = user->links; if (xs_is_null(val_links)) @@ -974,8 +974,8 @@ xs_html *html_top_controls(snac *snac) xs *metadata = xs_str_new(NULL); const xs_dict *md = xs_dict_get(snac->config, "metadata"); - xs_str *k; - xs_str *v; + const xs_str *k; + const xs_str *v; int c = 0; while (xs_dict_next(md, &k, &v, &c)) { @@ -1161,7 +1161,7 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) { xs_str *s = xs_str_new(NULL); const char *list = xs_dict_get(msg, "tag"); - char *v; + const char *v; int c = 0; while (xs_list_next(list, &v, &c)) { @@ -1372,7 +1372,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, xs_html *html_entry(snac *user, xs_dict *msg, int read_only, - int level, char *md5, int hide_children) + int level, const char *md5, int hide_children) { const char *id = xs_dict_get(msg, "id"); const char *type = xs_dict_get(msg, "type"); @@ -1670,7 +1670,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, const xs_list *oo = xs_dict_get(msg, "oneOf"); const xs_list *ao = xs_dict_get(msg, "anyOf"); const xs_list *p; - xs_dict *v; + const xs_dict *v; int closed = 0; const char *f_closed = NULL; @@ -1827,7 +1827,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, content_attachments); int c = 0; - xs_dict *a; + const xs_dict *a; while (xs_list_next(attach, &a, &c)) { const char *type = xs_dict_get(a, "type"); const char *href = xs_dict_get(a, "href"); @@ -1945,7 +1945,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } xs_list *p = children; - char *cmd5; + const char *cmd5; int cnt = 0; int o_cnt = 0; @@ -2021,7 +2021,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, /* returns the HTML for the timeline */ { xs_list *p = (xs_list *)list; - char *v; + const char *v; double t = ftime(); xs *desc = NULL; @@ -2130,7 +2130,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs *list = history_list(user); xs_list *p = list; - char *v; + const char *v; while (xs_list_iter(&p, &v)) { xs *fn = xs_replace(v, ".html", ""); @@ -2203,7 +2203,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t) xs_html_text("...")))); xs_list *p = list; - char *actor_id; + const char *actor_id; while (xs_list_iter(&p, &actor_id)) { xs *md5 = xs_md5_hex(actor_id, strlen(actor_id)); @@ -2363,7 +2363,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html *noti_seen = NULL; xs_list *p = n_list; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *noti = notify_get(user, v); @@ -3232,7 +3232,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, xs_dict *md = xs_dict_new(); xs *l = xs_split(v, "\n"); xs_list *p = l; - xs_str *kp; + const xs_str *kp; while (xs_list_iter(&p, &kp)) { xs *kpl = xs_split_n(kp, "=", 1); @@ -3333,7 +3333,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, } xs_list *p = ls; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *msg = msg_note(&snac, "", actor, irt, NULL, 1); @@ -3405,7 +3405,7 @@ xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char * xs_html_add(rss, channel); int c = 0; - char *v; + const char *v; while (xs_list_next(timeline, &v, &c)) { xs *msg = NULL; diff --git a/http.c b/http.c index 4d85631..b21f1dc 100644 --- a/http.c +++ b/http.c @@ -26,7 +26,7 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, xs *hdrs = NULL; const char *host; const char *target; - char *k, *v; + const char *k, *v; xs_dict *response; date = xs_str_utctime(0, "%a, %d %b %Y %H:%M:%S GMT"); @@ -144,7 +144,7 @@ int check_signature(const xs_dict *req, xs_str **err) /* extract the values */ xs *l = xs_split(sig_hdr, ","); int c = 0; - xs_val *v; + const xs_val *v; while (xs_list_next(l, &v, &c)) { xs *kv = xs_split_n(v, "=", 1); @@ -205,7 +205,7 @@ int check_signature(const xs_dict *req, xs_str **err) { xs *l = xs_split(headers, " "); xs_list *p; - xs_val *v; + const xs_val *v; p = l; while (xs_list_iter(&p, &v)) { diff --git a/httpd.c b/httpd.c index 8a20c89..a7396e8 100644 --- a/httpd.c +++ b/httpd.c @@ -75,7 +75,7 @@ xs_str *nodeinfo_2_0(void) int n_posts = 0; xs *users = user_list(); xs_list *p = users; - char *v; + const char *v; double now = (double)time(NULL); while (xs_list_iter(&p, &v)) { @@ -128,7 +128,7 @@ static xs_str *greeting_html(void) const char *host = xs_dict_get(srv_config, "host"); xs *list = user_list(); xs_list *p = list; - xs_str *uid; + const xs_str *uid; xs_html *ul = xs_html_tag("ul", xs_html_attr("class", "snac-user-list")); @@ -413,7 +413,7 @@ void httpd_connection(FILE *f) /* if there are any additional headers, add them */ const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers"); if (xs_type(more_headers) == XSTYPE_DICT) { - char *k, *v; + const char *k, *v; int c = 0; while (xs_dict_next(more_headers, &k, &v, &c)) headers = xs_dict_set(headers, k, v); @@ -590,7 +590,8 @@ static void *background_thread(void *arg) { xs *list = user_list(); - char *p, *uid; + char *p; + const char *uid; /* process queues for all users */ p = list; diff --git a/main.c b/main.c index 9c906a6..c88eebe 100644 --- a/main.c +++ b/main.c @@ -382,7 +382,7 @@ int main(int argc, char *argv[]) xs *r = content_search(&snac, url, 1, 0, XS_ALL, 10, &to); int c = 0; - char *v; + const char *v; /* print results as standalone links */ while (xs_list_next(r, &v, &c)) { diff --git a/mastoapi.c b/mastoapi.c index 852713e..3936c2a 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -599,7 +599,7 @@ xs_dict *mastoapi_account(const xs_dict *actor) const xs_list *p; if (!xs_is_null(p = xs_dict_get(actor, "tag"))) { xs *eml = xs_list_new(); - xs_dict *v; + const xs_dict *v; int c = 0; while (xs_list_next(p, &v, &c)) { @@ -637,7 +637,7 @@ xs_dict *mastoapi_account(const xs_dict *actor) xs *fields = xs_list_new(); p = xs_dict_get(actor, "attachment"); - xs_dict *v; + const xs_dict *v; /* dict of validated links */ xs_dict *val_links = NULL; @@ -713,7 +713,7 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg) xs_dict *poll = xs_dict_new(); xs *mid = mastoapi_id(msg); const xs_list *opts = NULL; - xs_val *v; + const xs_val *v; int num_votes = 0; xs *options = xs_list_new(); @@ -849,7 +849,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) { xs_list *p = attach; - xs_dict *v; + const xs_dict *v; xs *matt = xs_list_new(); @@ -898,7 +898,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) tag_list = xs_list_new(); tag = tag_list; - xs_dict *v; + const xs_dict *v; int c = 0; while (xs_list_next(tag, &v, &c)) { @@ -1210,8 +1210,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, const xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); if (xs_type(metadata) == XSTYPE_DICT) { xs *fields = xs_list_new(); - xs_str *k; - xs_str *v; + const xs_str *k; + const xs_str *v; xs_dict *val_links = snac1.links; if (xs_is_null(val_links)) @@ -1334,7 +1334,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *wers = follower_list(&snac1); xs *ulst = user_list(); xs_list *p; - xs_str *v; + const xs_str *v; xs_set seen; xs_set_init(&seen); @@ -1410,7 +1410,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* the public list of posts of a user */ xs *timeline = timeline_simple_list(&snac2, "public", 0, 256); xs_list *p = timeline; - xs_str *v; + const xs_str *v; out = xs_list_new(); @@ -1475,7 +1475,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *out = xs_list_new(); xs_list *p = timeline; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v) && cnt < limit) { xs *msg = NULL; @@ -1579,7 +1579,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *timeline = timeline_instance_list(0, limit); xs *out = xs_list_new(); xs_list *p = timeline; - xs_str *md5; + const xs_str *md5; snac *user = NULL; if (logged_in) @@ -1633,7 +1633,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *timeline = tag_search(tag, 0, limit); xs *out = xs_list_new(); xs_list *p = timeline; - xs_str *md5; + const xs_str *md5; while (xs_list_iter(&p, &md5) && cnt < limit) { xs *msg = NULL; @@ -1673,7 +1673,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *timeline = list_timeline(&snac1, list, 0, 2048); xs *out = xs_list_new(); int c = 0; - char *md5; + const char *md5; while (xs_list_next(timeline, &md5, &c)) { xs *msg = NULL; @@ -1747,7 +1747,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *l = notify_list(&snac1, 0, 64); xs *out = xs_list_new(); xs_list *p = l; - xs_dict *v; + const xs_dict *v; const xs_list *excl = xs_dict_get(args, "exclude_types[]"); while (xs_list_iter(&p, &v)) { @@ -1858,7 +1858,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *lol = list_maint(&snac1, NULL, 0); xs *l = xs_list_new(); int c = 0; - xs_list *li; + const xs_list *li; while (xs_list_next(lol, &li, &c)) { xs *d = xs_dict_new(); @@ -1890,7 +1890,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *actors = list_content(&snac1, p, NULL, 0); xs *out = xs_list_new(); int c = 0; - char *v; + const char *v; while (xs_list_next(actors, &v, &c)) { xs *actor = NULL; @@ -1911,7 +1911,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *out = xs_list_new(); xs *lol = list_maint(&snac1, NULL, 0); int c = 0; - xs_list *v; + const xs_list *v; while (xs_list_next(lol, &v, &c)) { const char *id = xs_list_get(v, 0); @@ -2105,7 +2105,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *anc = xs_list_new(); xs *des = xs_list_new(); xs_list *p; - xs_str *v; + const xs_str *v; char pid[64]; /* build the [grand]parent list, moving up */ @@ -2159,7 +2159,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, l = object_likes(xs_dict_get(msg, "id")); xs_list *p = l; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *actor2 = NULL; @@ -2267,7 +2267,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, int cnt = 40; xs *tl = content_search(&snac1, q, 1, 0, cnt, 0, &to); int c = 0; - char *v; + const char *v; while (xs_list_next(tl, &v, &c) && --cnt) { xs *post = NULL; @@ -2433,7 +2433,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, } xs_list *p = mi; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *l = xs_list_new(); @@ -2783,7 +2783,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, choices = xs_dict_get(args, "choices"); if (xs_type(choices) == XSTYPE_LIST) { - xs_str *v; + const xs_str *v; int c = 0; while (xs_list_next(choices, &v, &c)) { @@ -2855,7 +2855,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (strcmp(op, "accounts") == 0) { const xs_list *accts = xs_dict_get(args, "account_ids[]"); int c = 0; - char *v; + const char *v; while (xs_list_next(accts, &v, &c)) { list_content(&snac, id, v, 1); @@ -2934,7 +2934,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, p = xs_list_get(l, -2); const xs_list *accts = xs_dict_get(args, "account_ids[]"); int c = 0; - char *v; + const char *v; while (xs_list_next(accts, &v, &c)) { list_content(&snac, p, v, 2); @@ -3083,7 +3083,7 @@ void mastoapi_purge(void) xs *spec = xs_fmt("%s/app/" "*.json", srv_basedir); xs *files = xs_glob(spec, 1, 0); xs_list *p = files; - xs_str *v; + const xs_str *v; time_t mt = time(NULL) - 3600; diff --git a/upgrade.c b/upgrade.c index 266a4be..847c62e 100644 --- a/upgrade.c +++ b/upgrade.c @@ -43,7 +43,8 @@ int snac_upgrade(xs_str **error) else if (f < 2.2) { xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { @@ -52,7 +53,8 @@ int snac_upgrade(xs_str **error) if (user_open(&snac, v)) { xs *spec = xs_fmt("%s/actors/" "*.json", snac.basedir); xs *list = xs_glob(spec, 0, 0); - char *g, *fn; + char *g; + const char *fn; g = list; while (xs_list_iter(&g, &fn)) { @@ -77,14 +79,16 @@ int snac_upgrade(xs_str **error) else if (f < 2.3) { xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { snac snac; if (user_open(&snac, v)) { - char *p, *v; + char *p; + const char *v; xs *dir = xs_fmt("%s/hidden", snac.basedir); /* create the hidden directory */ @@ -109,7 +113,8 @@ int snac_upgrade(xs_str **error) else if (f < 2.4) { xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { @@ -132,7 +137,8 @@ int snac_upgrade(xs_str **error) if (f < 2.5) { /* upgrade followers */ xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { @@ -141,7 +147,8 @@ int snac_upgrade(xs_str **error) if (user_open(&snac, v)) { xs *spec = xs_fmt("%s/followers/" "*.json", snac.basedir); xs *dir = xs_glob(spec, 0, 0); - char *p, *v; + char *p; + const char *v; p = dir; while (xs_list_iter(&p, &v)) { @@ -175,7 +182,8 @@ int snac_upgrade(xs_str **error) if (f < 2.6) { /* upgrade local/ to public/ */ xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { @@ -184,7 +192,8 @@ int snac_upgrade(xs_str **error) if (user_open(&snac, v)) { xs *spec = xs_fmt("%s/local/" "*.json", snac.basedir); xs *dir = xs_glob(spec, 0, 0); - char *p, *v; + char *p; + const char *v; p = dir; while (xs_list_iter(&p, &v)) { @@ -206,7 +215,7 @@ int snac_upgrade(xs_str **error) /* if it's from us, add to public */ if (xs_startswith(id, snac.actor)) { const xs_list *p; - char *v; + const char *v; int c; object_user_cache_add(&snac, id, "public"); @@ -241,7 +250,8 @@ int snac_upgrade(xs_str **error) if (f < 2.7) { /* upgrade timeline/ to private/ */ xs *users = user_list(); - char *p, *v; + char *p; + const char *v; p = users; while (xs_list_iter(&p, &v)) { @@ -250,7 +260,8 @@ int snac_upgrade(xs_str **error) if (user_open(&snac, v)) { xs *spec = xs_fmt("%s/timeline/" "*.json", snac.basedir); xs *dir = xs_glob(spec, 0, 0); - char *p, *v; + char *p; + const char *v; p = dir; while (xs_list_iter(&p, &v)) { @@ -271,7 +282,7 @@ int snac_upgrade(xs_str **error) { const xs_list *p; - char *v; + const char *v; int c = 0; object_user_cache_add(&snac, id, "private"); diff --git a/utils.c b/utils.c index daaa583..0e8e3be 100644 --- a/utils.c +++ b/utils.c @@ -358,7 +358,7 @@ void rm_rf(const char *dir) xs *d = xs_str_cat(xs_dup(dir), "/" "*"); xs *l = xs_glob(d, 0, 0); xs_list *p = l; - xs_str *v; + const xs_str *v; if (dbglevel >= 1) printf("Deleting directory %s\n", dir); @@ -393,7 +393,7 @@ int deluser(snac *user) int ret = 0; xs *fwers = following_list(user); xs_list *p = fwers; - xs_str *v; + const xs_str *v; while (xs_list_iter(&p, &v)) { xs *object = NULL; @@ -419,7 +419,7 @@ void verify_links(snac *user) /* verifies a user's links */ { const xs_dict *p = xs_dict_get(user->config, "metadata"); - char *k, *v; + const char *k, *v; int changed = 0; xs *headers = xs_dict_new(); @@ -449,7 +449,7 @@ void verify_links(snac *user) xs *ls = xs_regex_select(payload, "< *(a|link) +[^>]+>"); xs_list *lp = ls; - char *ll; + const char *ll; int vfied = 0; while (!vfied && xs_list_iter(&lp, &ll)) { @@ -463,7 +463,7 @@ void verify_links(snac *user) xs *href = NULL; int is_rel_me = 0; xs_list *pr = r; - char *ar; + const char *ar; while (xs_list_iter(&pr, &ar)) { xs *nq = xs_dup(ar); diff --git a/webfinger.c b/webfinger.c index b1d85e9..c79fd44 100644 --- a/webfinger.c +++ b/webfinger.c @@ -96,7 +96,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us if (actor != NULL) { const xs_list *list = xs_dict_get(obj, "links"); int c = 0; - char *v; + const char *v; while (xs_list_next(list, &v, &c)) { if (xs_type(v) == XSTYPE_DICT) { diff --git a/xs.h b/xs.h index b46f0e1..f361830 100644 --- a/xs.h +++ b/xs.h @@ -94,8 +94,8 @@ xs_list *xs_list_new(void); xs_list *xs_list_append_m(xs_list *list, const char *mem, int dsz); xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]); #define xs_list_append(list, ...) _xs_list_append(list, (const xs_val *[]){ __VA_ARGS__, NULL }) -int xs_list_iter(xs_list **list, xs_val **value); -int xs_list_next(const xs_list *list, xs_val **value, int *ctxt); +int xs_list_iter(xs_list **list, const xs_val **value); +int xs_list_next(const xs_list *list, const xs_val **value, int *ctxt); int xs_list_len(const xs_list *list); const xs_val *xs_list_get(const xs_list *list, int num); xs_list *xs_list_del(xs_list *list, int num); @@ -118,7 +118,7 @@ xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *va xs_dict *xs_dict_new(void); xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value); xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value); -int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt); +int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt); const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def); #define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL) xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key); @@ -726,7 +726,7 @@ xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]) } -int xs_list_iter(xs_list **list, xs_val **value) +int xs_list_iter(xs_list **list, const xs_val **value) /* iterates a list value */ { int goon = 1; @@ -757,7 +757,7 @@ int xs_list_iter(xs_list **list, xs_val **value) } -int xs_list_next(const xs_list *list, xs_val **value, int *ctxt) +int xs_list_next(const xs_list *list, const xs_val **value, int *ctxt) /* iterates a list, with context */ { if (xs_type(list) != XSTYPE_LIST) @@ -765,7 +765,7 @@ int xs_list_next(const xs_list *list, xs_val **value, int *ctxt) int goon = 1; - char *p = (char *)list; + const char *p = list; /* skip the start of the list */ if (*ctxt == 0) @@ -800,7 +800,7 @@ int xs_list_len(const xs_list *list) int c = 0; xs_list *p = (xs_list *)list; - xs_val *v; + const xs_val *v; while (xs_list_iter(&p, &v)) c++; @@ -819,7 +819,7 @@ const xs_val *xs_list_get(const xs_list *list, int num) int c = 0; xs_list *p = (xs_list *)list; - xs_val *v; + const xs_val *v; while (xs_list_iter(&p, &v)) { if (c == num) @@ -881,7 +881,7 @@ xs_list *xs_list_dequeue(xs_list *list, xs_val **data, int last) XS_ASSERT_TYPE(list, XSTYPE_LIST); xs_list *p = list; - xs_val *v = NULL; + const xs_val *v = NULL; if (!last) { /* get the first */ @@ -910,7 +910,7 @@ int xs_list_in(const xs_list *list, const xs_val *val) int n = 0; xs_list *p = (xs_list *)list; - xs_val *v; + const xs_val *v; int sz = xs_size(val); while (xs_list_iter(&p, &v)) { @@ -931,7 +931,7 @@ xs_str *xs_join(const xs_list *list, const char *sep) xs_str *s = NULL; xs_list *p = (xs_list *)list; - xs_val *v; + const xs_val *v; int c = 0; int offset = 0; int ssz = strlen(sep); @@ -1087,7 +1087,7 @@ xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) } -int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt) +int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt) /* iterates a dict, with context */ { if (xs_type(dict) != XSTYPE_DICT) @@ -1131,8 +1131,8 @@ const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_v XS_ASSERT_TYPE(dict, XSTYPE_DICT); XS_ASSERT_TYPE(key, XSTYPE_STRING); - xs_str *k; - xs_val *v; + const xs_str *k; + const xs_val *v; int c = 0; while (xs_dict_next(dict, &k, &v, &c)) { @@ -1150,14 +1150,14 @@ xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key) XS_ASSERT_TYPE(dict, XSTYPE_DICT); XS_ASSERT_TYPE(key, XSTYPE_STRING); - xs_str *k; - xs_val *v; + const xs_str *k; + const xs_val *v; int c = 0; while (xs_dict_next(dict, &k, &v, &c)) { if (strcmp(k, key) == 0) { /* the address of the item is just behind the key */ - char *i = k - 1; + char *i = (char *)k - 1; dict = xs_collapse(dict, i - dict, xs_size(i)); break; diff --git a/xs_curl.h b/xs_curl.h index 2628d91..215db7f 100644 --- a/xs_curl.h +++ b/xs_curl.h @@ -93,8 +93,8 @@ xs_dict *xs_http_request(const char *method, const char *url, xs_dict *response; CURL *curl; struct curl_slist *list = NULL; - xs_str *k; - xs_val *v; + const xs_str *k; + const xs_val *v; long lstatus = 0; struct _payload_data pd; diff --git a/xs_fcgi.h b/xs_fcgi.h index 4727c5c..a1433a2 100644 --- a/xs_fcgi.h +++ b/xs_fcgi.h @@ -293,8 +293,8 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b struct fcgi_record_header hdr = {0}; struct fcgi_end_request ereq = {0}; xs *out = xs_str_new(NULL); - xs_str *k; - xs_str *v; + const xs_str *k; + const xs_str *v; /* no previous id? it's an error */ if (fcgi_id == -1) diff --git a/xs_httpd.h b/xs_httpd.h index 60933c8..4195b81 100644 --- a/xs_httpd.h +++ b/xs_httpd.h @@ -99,8 +99,8 @@ void xs_httpd_response(FILE *f, int status, xs_dict *headers, xs_str *body, int /* sends an httpd response */ { xs *proto; - xs_str *k; - xs_val *v; + const xs_str *k; + const xs_val *v; proto = xs_fmt("HTTP/1.1 %d %s", status, status / 100 == 2 ? "OK" : "ERROR"); fprintf(f, "%s\r\n", proto); diff --git a/xs_json.h b/xs_json.h index 3a7742d..b65e825 100644 --- a/xs_json.h +++ b/xs_json.h @@ -76,7 +76,7 @@ static void _xs_json_dump(const xs_val *data, int level, int indent, FILE *f) { int c = 0; int ct = 0; - xs_val *v; + const xs_val *v; switch (xs_type(data)) { case XSTYPE_NULL: @@ -116,7 +116,7 @@ static void _xs_json_dump(const xs_val *data, int level, int indent, FILE *f) case XSTYPE_DICT: fputc('{', f); - xs_str *k; + const xs_str *k; while (xs_dict_next(data, &k, &v, &ct)) { if (c != 0) diff --git a/xs_regex.h b/xs_regex.h index cb73a01..cdb2cf8 100644 --- a/xs_regex.h +++ b/xs_regex.h @@ -64,7 +64,7 @@ xs_list *xs_regex_select_n(const char *str, const char *rx, int count) { xs_list *list = xs_list_new(); xs *split = NULL; - xs_val *v; + const xs_val *v; int n = 0; int c = 0; @@ -89,7 +89,7 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c { xs_str *s = xs_str_new(NULL); xs *split = xs_regex_split_n(str, rx, count); - xs_val *v; + const xs_val *v; int n = 0; int c = 0; int pholder = !!strchr(rep, '&'); diff --git a/xs_set.h b/xs_set.h index b7eb091..12bac94 100644 --- a/xs_set.h +++ b/xs_set.h @@ -85,7 +85,8 @@ int xs_set_add(xs_set *s, const xs_val *data) { /* is it 'full'? */ if (s->used >= s->elems / 2) { - char *p, *v; + char *p; + const xs_val *v; /* expand! */ s->elems *= 2; diff --git a/xs_url.h b/xs_url.h index 69313b6..488a65f 100644 --- a/xs_url.h +++ b/xs_url.h @@ -52,7 +52,7 @@ xs_dict *xs_url_vars(const char *str) xs *args = xs_split(str, "&"); xs_list *l; - xs_val *v; + const xs_val *v; l = args; while (xs_list_iter(&l, &v)) { diff --git a/xs_version.h b/xs_version.h index 9ecf9b8..9fb70ef 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 34850dcdec50b669a2c0bbe9f16f6d9c4b16eafd 2024-05-21T14:06:02+02:00 */ +/* 65265483c102909393287bfb173d1a7ae9c3be00 2024-05-23T09:57:20+02:00 */ From fcead8ea9686d4707f95e9249d955284d22d4ec2 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 12:05:31 +0200 Subject: [PATCH 114/120] Also delete the list .idx.bak. --- data.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data.c b/data.c index f507078..1866d5e 100644 --- a/data.c +++ b/data.c @@ -1831,6 +1831,9 @@ xs_val *list_maint(snac *user, const char *list, int op) fn = xs_replace_i(fn, ".lst", ".idx"); unlink(fn); + + fn = xs_append(fn, ".bak"); + unlink(fn); } } From a787c818b2c52b5bf30d13bb8f3490a92dd8bdcb Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 12:12:03 +0200 Subject: [PATCH 115/120] Added links to the lists in html_timeline(). --- data.c | 2 +- html.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index 1866d5e..edbc64f 100644 --- a/data.c +++ b/data.c @@ -1832,7 +1832,7 @@ xs_val *list_maint(snac *user, const char *list, int op) fn = xs_replace_i(fn, ".lst", ".idx"); unlink(fn); - fn = xs_append(fn, ".bak"); + fn = xs_str_cat(fn, ".bak"); unlink(fn); } } diff --git a/html.c b/html.c index 11fe4bf..a9b82da 100644 --- a/html.c +++ b/html.c @@ -2066,6 +2066,31 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_text(title))); } + /* show links to the available lists */ + { + xs *lists = list_maint(user, NULL, 0); /* get list of lists */ + + if (xs_list_len(lists)) { + int ct = 0; + const char *v; + + xs_html *lol = xs_html_tag("ul", + xs_html_attr("class", "snac-list-of-lists")); + xs_html_add(body, lol); + + while (xs_list_next(lists, &v, &ct)) { + xs *url = xs_fmt("%s/list/%s", user->actor, xs_list_get(v, 0)); + + xs_html_add(lol, + xs_html_tag("li", + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("class", "snac-list-link"), + xs_html_text(xs_list_get(v, 1))))); + } + } + } + xs_html_add(body, xs_html_tag("a", xs_html_attr("name", "snac-posts"))); From 8ebad8d536dfdcec568e3a926e3c8796a0305ab5 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 12:24:23 +0200 Subject: [PATCH 116/120] Minor list show tweaks. --- html.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/html.c b/html.c index a9b82da..e77771f 100644 --- a/html.c +++ b/html.c @@ -2059,13 +2059,6 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, html_top_controls(user)); - if (title) { - xs_html_add(body, - xs_html_tag("h2", - xs_html_attr("class", "snac-header"), - xs_html_text(title))); - } - /* show links to the available lists */ { xs *lists = list_maint(user, NULL, 0); /* get list of lists */ @@ -2091,6 +2084,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, } } + if (title) { + xs_html_add(body, + xs_html_tag("h2", + xs_html_attr("class", "snac-header"), + xs_html_text(title))); + } + xs_html_add(body, xs_html_tag("a", xs_html_attr("name", "snac-posts"))); @@ -2768,7 +2768,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, if (list != NULL) { xs *base = xs_fmt("/list/%s", lid); xs *name = list_maint(&snac, lid, 3); - xs *title = xs_fmt(L("Showing timeline for list %s"), name); + xs *title = xs_fmt(L("Showing timeline for list '%s'"), name); *body = html_timeline(&snac, list, 0, skip, show, xs_list_len(next), title, base, 1); From bd4511e7c50cc92739d2762cd9d69ebc8d0b8e94 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 12:31:41 +0200 Subject: [PATCH 117/120] Show 'Nothing found for tag' message. --- html.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index e77771f..4e29bd7 100644 --- a/html.c +++ b/html.c @@ -2631,7 +2631,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, } xs *page = xs_fmt("/admin?q=%%23%s", q + 1); - xs *title = xs_fmt(L("Search results for tag %s"), q); + 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); *b_size = strlen(*body); From a727bb29f6a4b1e66a4ad0664e4557f69d1e2ac4 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 12:36:08 +0200 Subject: [PATCH 118/120] Don't show list links in read only timelines. --- html.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html.c b/html.c index 4e29bd7..e89fdfb 100644 --- a/html.c +++ b/html.c @@ -2060,7 +2060,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, html_top_controls(user)); /* show links to the available lists */ - { + if (user && !read_only) { xs *lists = list_maint(user, NULL, 0); /* get list of lists */ if (xs_list_len(lists)) { From f631fc5ed22cb49d9d9f6a481f4a5d227c180246 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 24 May 2024 19:06:39 +0200 Subject: [PATCH 119/120] Added a 'title' to each list timeline. --- html.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/html.c b/html.c index e89fdfb..f97c45d 100644 --- a/html.c +++ b/html.c @@ -2072,14 +2072,17 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, lol); while (xs_list_next(lists, &v, &ct)) { + 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); xs_html_add(lol, xs_html_tag("li", xs_html_tag("a", xs_html_attr("href", url), xs_html_attr("class", "snac-list-link"), - xs_html_text(xs_list_get(v, 1))))); + xs_html_attr("title", ttl), + xs_html_text(lname)))); } } } From a2920800007c291bdf2b5264622cbc713d4961ee Mon Sep 17 00:00:00 2001 From: default Date: Sat, 25 May 2024 08:24:08 +0200 Subject: [PATCH 120/120] Backport from xs (fix regex.h compilation with tcc). --- xs.h | 24 +++++++++++------------- xs_regex.h | 5 +++++ xs_set.h | 5 ++--- xs_url.h | 5 ++--- xs_version.h | 2 +- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/xs.h b/xs.h index f361830..972665c 100644 --- a/xs.h +++ b/xs.h @@ -798,11 +798,10 @@ int xs_list_len(const xs_list *list) { XS_ASSERT_TYPE_NULL(list, XSTYPE_LIST); - int c = 0; - xs_list *p = (xs_list *)list; + int c = 0, ct = 0; const xs_val *v; - while (xs_list_iter(&p, &v)) + while (xs_list_next(list, &v, &ct)) c++; return c; @@ -817,11 +816,10 @@ const xs_val *xs_list_get(const xs_list *list, int num) if (num < 0) num = xs_list_len(list) + num; - int c = 0; - xs_list *p = (xs_list *)list; + int c = 0, ct = 0; const xs_val *v; - while (xs_list_iter(&p, &v)) { + while (xs_list_next(list, &v, &ct)) { if (c == num) return v; @@ -880,16 +878,16 @@ xs_list *xs_list_dequeue(xs_list *list, xs_val **data, int last) { XS_ASSERT_TYPE(list, XSTYPE_LIST); - xs_list *p = list; + int ct = 0; const xs_val *v = NULL; if (!last) { /* get the first */ - xs_list_iter(&p, &v); + xs_list_next(list, &v, &ct); } else { /* iterate to the end */ - while (xs_list_iter(&p, &v)); + while (xs_list_next(list, &v, &ct)); } if (v != NULL) { @@ -909,11 +907,11 @@ int xs_list_in(const xs_list *list, const xs_val *val) XS_ASSERT_TYPE_NULL(list, XSTYPE_LIST); int n = 0; - xs_list *p = (xs_list *)list; + int ct = 0; const xs_val *v; int sz = xs_size(val); - while (xs_list_iter(&p, &v)) { + while (xs_list_next(list, &v, &ct)) { if (sz == xs_size(v) && memcmp(val, v, sz) == 0) return n; @@ -930,13 +928,13 @@ xs_str *xs_join(const xs_list *list, const char *sep) XS_ASSERT_TYPE(list, XSTYPE_LIST); xs_str *s = NULL; - xs_list *p = (xs_list *)list; const xs_val *v; int c = 0; + int ct = 0; int offset = 0; int ssz = strlen(sep); - while (xs_list_iter(&p, &v)) { + while (xs_list_next(list, &v, &ct)) { /* refuse to join non-string values */ if (xs_type(v) == XSTYPE_STRING) { int sz; diff --git a/xs_regex.h b/xs_regex.h index cdb2cf8..d8d2d1d 100644 --- a/xs_regex.h +++ b/xs_regex.h @@ -16,6 +16,11 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c #ifdef XS_IMPLEMENTATION +#ifdef __TINYC__ +/* fix a compilation error in tcc */ +#define _REGEX_NELTS(n) +#endif + #include xs_list *xs_regex_split_n(const char *str, const char *rx, int count) diff --git a/xs_set.h b/xs_set.h index 12bac94..d320d34 100644 --- a/xs_set.h +++ b/xs_set.h @@ -85,7 +85,6 @@ int xs_set_add(xs_set *s, const xs_val *data) { /* is it 'full'? */ if (s->used >= s->elems / 2) { - char *p; const xs_val *v; /* expand! */ @@ -96,8 +95,8 @@ int xs_set_add(xs_set *s, const xs_val *data) memset(s->hash, '\0', s->elems * sizeof(int)); /* add the list elements back */ - p = s->list; - while (xs_list_iter(&p, &v)) + int ct = 0; + while (xs_list_next(s->list, &v, &ct)) _store_hash(s, v, v - s->list); } diff --git a/xs_url.h b/xs_url.h index 488a65f..606c3e4 100644 --- a/xs_url.h +++ b/xs_url.h @@ -51,11 +51,10 @@ xs_dict *xs_url_vars(const char *str) /* split by arguments */ xs *args = xs_split(str, "&"); - xs_list *l; + int ct = 0; const xs_val *v; - l = args; - while (xs_list_iter(&l, &v)) { + while (xs_list_next(args, &v, &ct)) { xs *kv = xs_split_n(v, "=", 1); if (xs_list_len(kv) == 2) { diff --git a/xs_version.h b/xs_version.h index 9fb70ef..7a7ba53 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 65265483c102909393287bfb173d1a7ae9c3be00 2024-05-23T09:57:20+02:00 */ +/* 65769f25ed99b886a643522bef21628396cd118d 2024-05-25T08:18:51+02:00 */