From 049818a9042ce1da23e6fe3c41f776b1470e7cc4 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 29 Apr 2023 07:36:18 +0200 Subject: Public posts are also added to an instance public timeline index. --- data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index c2d29c1..db42ece 100644 --- a/data.c +++ b/data.c @@ -969,8 +969,13 @@ void timeline_update_indexes(snac *snac, const char *id) if (valid_status(object_get(id, &msg))) { /* if its ours and is public, also store in public */ - if (is_msg_public(snac, msg)) + if (is_msg_public(snac, msg)) { object_user_cache_add(snac, id, "public"); + + /* also add it to the instance public timeline */ + xs *ipt = xs_fmt("%s/public.idx", srv_basedir); + index_add(ipt, id); + } } } } -- cgit v1.2.3 From 6b632e1ee9450f12c51442264e402753f8dfe325 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 06:01:08 +0200 Subject: Bumped version. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index fe7adfb..a17d33f 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2023 grunfink / MIT license */ -#define VERSION "2.29" +#define VERSION "2.30-dev" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3 From ede4d6f2dc8f862337724054dcfeb31cbaa89bcc Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 06:39:55 +0200 Subject: Some instance timeline work. --- data.c | 13 +++++++++++-- mastoapi.c | 52 +++++++++++++++++++++++----------------------------- snac.h | 8 ++++---- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/data.c b/data.c index db42ece..60c4b26 100644 --- a/data.c +++ b/data.c @@ -1046,7 +1046,7 @@ xs_list *timeline_top_level(snac *snac, xs_list *list) } -d_char *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show) +xs_list *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show) /* returns a timeline (with all entries) */ { int c_max; @@ -1064,7 +1064,7 @@ d_char *timeline_simple_list(snac *snac, const char *idx_name, int skip, int sho } -d_char *timeline_list(snac *snac, const char *idx_name, int skip, int show) +xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show) /* returns a timeline (only top level entries) */ { xs *list = timeline_simple_list(snac, idx_name, skip, show); @@ -1073,6 +1073,15 @@ d_char *timeline_list(snac *snac, const char *idx_name, int skip, int show) } +xs_list *timeline_instance_list(int skip, int show) +/* returns the timeline for the full instance */ +{ + xs *idx = xs_fmt("%s/public.idx", srv_basedir); + + return index_list_desc(idx, skip, show); +} + + /** following **/ /* this needs special treatment and cannot use the object db as is, diff --git a/mastoapi.c b/mastoapi.c index 001c0bc..92acd41 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1038,9 +1038,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (strcmp(cmd, "/v1/timelines/public") == 0) { /* the public timeline (public timelines for all users) */ - /* this is an ugly kludge: first users in the list get all the fame */ - - const char *limit_s = xs_dict_get(args, "limit"); + const char *limit_s = xs_dict_get(args, "limit"); int limit = 0; int cnt = 0; @@ -1050,44 +1048,40 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (limit == 0) limit = 20; - xs *out = xs_list_new(); - xs *users = user_list(); - xs_list *p = users; - xs_str *uid; - - while (xs_list_iter(&p, &uid) && cnt < limit) { - snac user; + xs *timeline = timeline_instance_list(0, limit); + xs *out = xs_list_new(); + xs_list *p = timeline; + xs_str *md5; - if (user_open(&user, uid)) { - xs *timeline = timeline_simple_list(&user, "public", 0, 4); - xs_list *p2 = timeline; - xs_str *v; + while (xs_list_iter(&p, &md5) && cnt < limit) { + xs *msg = NULL; - while (xs_list_iter(&p2, &v) && cnt < limit) { - xs *msg = NULL; + /* get the entry */ + if (!valid_status(object_get_by_md5(md5, &msg))) + continue; - /* get the entry */ - if (!valid_status(timeline_get_by_md5(&user, v, &msg))) - continue; + /* discard non-Notes */ + if (strcmp(xs_dict_get(msg, "type"), "Note") != 0) + continue; - /* discard non-Notes */ - if (strcmp(xs_dict_get(msg, "type"), "Note") != 0) - continue; + /* get the uid */ + xs *l = xs_split(xs_dict_get(msg, "attributedTo"), "/"); + const char *uid = xs_list_get(l, -1); - /* discard entries not by this user */ - if (!xs_startswith(xs_dict_get(msg, "id"), user.actor)) - continue; + if (!xs_is_null(uid)) { + snac user; + if (user_open(&user, uid)) { /* convert the Note into a Mastodon status */ xs *st = mastoapi_status(&user, msg); - if (st != NULL) { + if (st != NULL) out = xs_list_append(out, st); - cnt++; - } + + user_free(&user); } - user_free(&user); + cnt++; } } diff --git a/snac.h b/snac.h index a17d33f..5478933 100644 --- a/snac.h +++ b/snac.h @@ -105,14 +105,14 @@ 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); -d_char *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show); -d_char *timeline_list(snac *snac, const char *idx_name, int skip, int show); +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, char *id, char *o_msg); void timeline_admire(snac *snac, char *id, char *admirer, int like); xs_list *timeline_top_level(snac *snac, xs_list *list); - -d_char *local_list(snac *snac, int max); +xs_list *local_list(snac *snac, int max); +xs_list *timeline_instance_list(int skip, int show); int following_add(snac *snac, const char *actor, const xs_dict *msg); int following_del(snac *snac, const char *actor); -- cgit v1.2.3 From e31c4810a83c42c0298d4832fb4f2291dec0e84d Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 06:49:51 +0200 Subject: actor_get() returns an actor message for local users. --- data.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/data.c b/data.c index 60c4b26..187d9b9 100644 --- a/data.c +++ b/data.c @@ -1287,27 +1287,43 @@ int is_hidden(snac *snac, const char *id) } -int actor_add(snac *snac, const char *actor, d_char *msg) +int actor_add(snac *snac, const char *actor, xs_dict *msg) /* adds an actor */ { return object_add_ow(actor, msg); } -int actor_get(snac *snac, const char *actor, d_char **data) +int actor_get(snac *snac1, const char *actor, xs_dict **data) /* returns an already downloaded actor */ { int status = 200; - d_char *d; + xs_dict *d; - if (strcmp(actor, snac->actor) == 0) { + if (strcmp(actor, snac1->actor) == 0) { /* this actor */ if (data) - *data = msg_actor(snac); + *data = msg_actor(snac1); return status; } + if (xs_startswith(actor, srv_baseurl)) { + /* it's a (possible) local user */ + xs *l = xs_split(actor, "/"); + const char *uid = xs_list_get(l, -1); + snac user; + + if (!xs_is_null(uid) && user_open(&user, uid)) { + if (data) + *data = msg_actor(&user); + + user_free(&user); + } + else + return 404; + } + /* read the object */ if (!valid_status(status = object_get(actor, &d))) return status; -- cgit v1.2.3 From cfa0df3ac5a8b62539a3d1830414ec3d81d24f5d Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 07:00:49 +0200 Subject: The instance timeline now works. --- mastoapi.c | 29 +++++++++++------------------ snac.h | 4 ++-- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 92acd41..5c4e27f 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1036,7 +1036,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, } else if (strcmp(cmd, "/v1/timelines/public") == 0) { - /* the public timeline (public timelines for all users) */ + /* the instance public timeline (public timelines for all users) */ + + /* NOTE: this api call needs no authorization; but, + I need a logged-in user in mastoapi_status() for + is_msg_public() and the liked/boosted flags, + so it will silently fail for pure public access */ const char *limit_s = xs_dict_get(args, "limit"); int limit = 0; @@ -1053,7 +1058,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs_list *p = timeline; xs_str *md5; - while (xs_list_iter(&p, &md5) && cnt < limit) { + while (logged_in && xs_list_iter(&p, &md5) && cnt < limit) { xs *msg = NULL; /* get the entry */ @@ -1064,23 +1069,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, if (strcmp(xs_dict_get(msg, "type"), "Note") != 0) continue; - /* get the uid */ - xs *l = xs_split(xs_dict_get(msg, "attributedTo"), "/"); - const char *uid = xs_list_get(l, -1); - - if (!xs_is_null(uid)) { - snac user; - - if (user_open(&user, uid)) { - /* convert the Note into a Mastodon status */ - xs *st = mastoapi_status(&user, msg); - - if (st != NULL) - out = xs_list_append(out, st); - - user_free(&user); - } + /* convert the Note into a Mastodon status */ + xs *st = mastoapi_status(&snac1, msg); + if (st != NULL) { + out = xs_list_append(out, st); cnt++; } } diff --git a/snac.h b/snac.h index 5478933..10e8c4c 100644 --- a/snac.h +++ b/snac.h @@ -127,8 +127,8 @@ int is_muted(snac *snac, const char *actor); void hide(snac *snac, const char *id); int is_hidden(snac *snac, const char *id); -int actor_add(snac *snac, const char *actor, d_char *msg); -int actor_get(snac *snac, const char *actor, d_char **data); +int actor_add(snac *snac, const char *actor, xs_dict *msg); +int actor_get(snac *snac, const char *actor, xs_dict **data); int static_get(snac *snac, const char *id, d_char **data, int *size); void static_put(snac *snac, const char *id, const char *data, int size); -- cgit v1.2.3 From 6917f0ab2684a126986dc4169b56bbdac985aa15 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 07:01:44 +0200 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8a153e9..033916f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,9 @@ # Release Notes +## 2.30 + +New Mastodon API features: the instance public timeline is now a real one. + ## 2.29 New Mastodon API features: account search, relationships (so the Follow/Unfollow buttons now appear for each account), follow and unfollow accounts, an instance-level timeline (very kludgy), custom emojis for accounts and statuses, many bug fixes (sadly, the Mastodon official app still does not work). -- cgit v1.2.3 From 21a2a01937211543216add23a8a4eaca20bf5f67 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 30 Apr 2023 07:37:39 +0200 Subject: Return immediately from actor_get() if it's a local user. --- data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/data.c b/data.c index 187d9b9..ee36103 100644 --- a/data.c +++ b/data.c @@ -1319,6 +1319,7 @@ int actor_get(snac *snac1, const char *actor, xs_dict **data) *data = msg_actor(&user); user_free(&user); + return 200; } else return 404; -- cgit v1.2.3 From a7d4513f776f831c36058dfb9b86e80438d16d26 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 1 May 2023 07:35:26 +0200 Subject: In /api/v1/statuses, get the object from the storage instead of from the timeline. This was affecting clicking on posts from the instance timeline, that were not in the logged-in user timeline. --- mastoapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastoapi.c b/mastoapi.c index 5c4e27f..3d0a939 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1313,7 +1313,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, /* skip the 'fake' part of the id */ id = MID_TO_MD5(id); - if (valid_status(timeline_get_by_md5(&snac1, id, &msg))) { + if (valid_status(object_get_by_md5(id, &msg))) { if (op == NULL) { if (!is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) { /* return the status itself */ -- cgit v1.2.3 From 84902d8dcca0ff06e55698432cca69e542be685f Mon Sep 17 00:00:00 2001 From: default Date: Mon, 1 May 2023 08:32:48 +0200 Subject: Purge / gc the instance timeline index. --- data.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data.c b/data.c index ee36103..50d88eb 100644 --- a/data.c +++ b/data.c @@ -2051,7 +2051,11 @@ void purge_server(void) xs *ib_dir = xs_fmt("%s/inbox", srv_basedir); _purge_dir(ib_dir, 7); - srv_debug(1, xs_fmt("purge: global (obj: %d, idx: %d)", cnt, icnt)); + /* purge the instance timeline */ + xs *itl_fn = xs_fmt("%s/public.idx", srv_basedir); + int itl_gc = index_gc(itl_fn); + + srv_debug(1, xs_fmt("purge: global (obj: %d, idx: %d, itl: %d)", cnt, icnt, itl_gc)); } -- cgit v1.2.3 From c21bbd5f7ca814d65a3759d429c1d673bee5081b Mon Sep 17 00:00:00 2001 From: default Date: Mon, 1 May 2023 17:02:44 +0200 Subject: New functions index_del_md5(), index_del() and object_unadmire(). --- data.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/data.c b/data.c index 50d88eb..e038f81 100644 --- a/data.c +++ b/data.c @@ -328,6 +328,51 @@ int index_add(const char *fn, const char *id) } +int index_del_md5(const char *fn, const char *md5) +/* deletes an md5 from an index */ +{ + int status = 404; + FILE *f; + + pthread_mutex_lock(&data_mutex); + + if ((f = fopen(fn, "r+")) != NULL) { + char line[256]; + + while (fgets(line, sizeof(line), f) != NULL) { + line[32] = '\0'; + + if (strcmp(line, md5) == 0) { + /* found! just rewind, overwrite it with garbage + and an eventual call to index_gc() will clean it + [yes: this breaks index_len()] */ + fseek(f, -33, SEEK_CUR); + fwrite("-", 1, 1, f); + status = 200; + + break; + } + } + + fclose(f); + } + else + status = 500; + + pthread_mutex_unlock(&data_mutex); + + return status; +} + + +int index_del(const char *fn, const char *id) +/* deletes an id from an index */ +{ + xs *md5 = xs_md5_hex(id, strlen(id)); + return index_del_md5(fn, md5); +} + + int index_gc(const char *fn) /* garbage-collects an index, deleting objects that are not here */ { @@ -772,6 +817,23 @@ int object_admire(const char *id, const char *actor, int like) } +int object_unadmire(const char *id, const char *actor, int like) +/* actor no longer likes or announces this object */ +{ + int status; + xs *fn = _object_fn(id); + + fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx"); + + status = index_del(fn, actor); + + srv_debug(1, + xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); + + return status; +} + + int _object_user_cache(snac *snac, const char *id, const char *cachedir, int del) /* adds or deletes from a user cache */ { -- cgit v1.2.3 From 4595a3685992a8f31b86cca0ecf10e286dec52eb Mon Sep 17 00:00:00 2001 From: default Date: Mon, 1 May 2023 17:20:49 +0200 Subject: Partial support for mastoapi unfavourite / unreblog. --- data.c | 2 +- mastoapi.c | 9 +++++++-- snac.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/data.c b/data.c index e038f81..67f5751 100644 --- a/data.c +++ b/data.c @@ -827,7 +827,7 @@ int object_unadmire(const char *id, const char *actor, int like) status = index_del(fn, actor); - srv_debug(1, + srv_debug(0, xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); return status; diff --git a/mastoapi.c b/mastoapi.c index 3d0a939..72d9579 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1670,7 +1670,11 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, } else if (strcmp(op, "unfavourite") == 0) { - /* snac does not support Undo+Like */ + /* partial support: as the original Like message + is not stored anywhere here, it's not possible + to send an Undo + Like; the only thing done here + is to delete the actor from the list of likes */ + object_unadmire(id, snac.actor, 1); } else if (strcmp(op, "reblog") == 0) { @@ -1685,7 +1689,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, } else if (strcmp(op, "unreblog") == 0) { - /* snac does not support Undo+Announce */ + /* partial support: see comment in 'unfavourite' */ + object_unadmire(id, snac.actor, 0); } else if (strcmp(op, "bookmark") == 0) { diff --git a/snac.h b/snac.h index 10e8c4c..a162618 100644 --- a/snac.h +++ b/snac.h @@ -83,6 +83,7 @@ int object_del_if_unref(const char *id); double object_ctime_by_md5(const char *md5); double object_ctime(const char *id); int object_admire(const char *id, const char *actor, int like); +int object_unadmire(const char *id, const char *actor, int like); int object_likes_len(const char *id); int object_announces_len(const char *id); -- cgit v1.2.3 From f1756a53d1c2f3308fefda669ae4d372daf88f8d Mon Sep 17 00:00:00 2001 From: default Date: Mon, 1 May 2023 17:21:56 +0200 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 033916f..78a3603 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,7 +2,7 @@ ## 2.30 -New Mastodon API features: the instance public timeline is now a real one. +New Mastodon API features: the instance public timeline is now a real one, unfavourite / unreblog is supported (somewhat). ## 2.29 -- cgit v1.2.3 From 29815a38736666e2226454d96ba444843853dca5 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 2 May 2023 06:41:45 +0200 Subject: Backport from xs. --- xs.h | 12 ++-- xs_encdec.h | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- xs_version.h | 2 +- 3 files changed, 220 insertions(+), 15 deletions(-) diff --git a/xs.h b/xs.h index fef91b7..c4c961b 100644 --- a/xs.h +++ b/xs.h @@ -65,8 +65,10 @@ xs_str *xs_str_new(const char *str); xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix); #define xs_str_prepend_i(str, prefix) xs_str_wrap_i(prefix, str, NULL) #define xs_str_cat(str, suffix) xs_str_wrap_i(NULL, str, suffix) -xs_str *xs_replace_i(xs_str *str, const char *sfrom, const char *sto); -#define xs_replace(str, sfrom, sto) xs_replace_i(xs_dup(str), sfrom, sto) +xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times); +#define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL) +#define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL) +#define xs_replace_n(str, sfrom, sto, times) xs_replace_in(xs_dup(str), sfrom, sto, times) xs_str *xs_fmt(const char *fmt, ...); int xs_str_in(const char *haystack, const char *needle); int _xs_startsorends(const char *str, const char *xfix, int ends); @@ -416,7 +418,7 @@ xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix) } -xs_str *xs_replace_i(xs_str *str, const char *sfrom, const char *sto) +xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times) /* replaces inline all sfrom with sto */ { XS_ASSERT_TYPE(str, XSTYPE_STRING); @@ -426,7 +428,7 @@ xs_str *xs_replace_i(xs_str *str, const char *sfrom, const char *sto) char *ss; int offset = 0; - while ((ss = strstr(str + offset, sfrom)) != NULL) { + while (times > 0 && (ss = strstr(str + offset, sfrom)) != NULL) { int n_offset = ss - str; str = xs_collapse(str, n_offset, sfsz); @@ -434,6 +436,8 @@ xs_str *xs_replace_i(xs_str *str, const char *sfrom, const char *sto) memcpy(str + n_offset, sto, stsz); offset = n_offset + stsz; + + times--; } return str; diff --git a/xs_encdec.h b/xs_encdec.h index 12f40ef..2502520 100644 --- a/xs_encdec.h +++ b/xs_encdec.h @@ -7,13 +7,20 @@ xs_str *xs_hex_enc(const xs_val *data, int size); xs_val *xs_hex_dec(const xs_str *hex, int *size); int xs_is_hex(const char *str); + xs_str *xs_base32_enc(const xs_val *data, int sz); + xs_str *xs_base32hex_enc(const xs_val *data, int sz); + xs_val *xs_base32_dec(const xs_str *data, int *size); + xs_val *xs_base32hex_dec(const xs_str *data, int *size); xs_str *xs_base64_enc(const xs_val *data, int sz); xs_val *xs_base64_dec(const xs_str *data, int *size); + int xs_is_base64(const char *str); xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint); #ifdef XS_IMPLEMENTATION +/** hex **/ + xs_str *xs_hex_enc(const xs_val *data, int size) /* returns an hexdump of data */ { @@ -78,16 +85,178 @@ int xs_is_hex(const char *str) } -xs_str *xs_base64_enc(const xs_val *data, int sz) -/* encodes data to base64 */ +/** base32 */ + +static char *xs_b32_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "234567="; + +static char *xs_b32hex_tbl = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUV="; + +/* + 00000|00011|11111|12222|22223|33333|33444|44444 +*/ + +xs_str *xs_base32_enc_tbl(const xs_val *data, int sz, const char *b32_tbl) +/* encodes data to base32 using a table */ +{ + xs_str *s = xs_str_new(NULL); + unsigned char *p; + int n; + + p = (unsigned char *)data; + + for (n = 0; n < sz; n += 5) { + int l = sz - n; + char enc[9] = "========"; + + enc[0] = b32_tbl[(p[n] >> 3) & 0x1f]; + + if (l > 1) { + enc[1] = b32_tbl[(p[n] << 2 | p[n + 1] >> 6) & 0x1f]; + enc[2] = b32_tbl[(p[n + 1] >> 1) & 0x1f]; + + if (l > 2) { + enc[3] = b32_tbl[(p[n + 1] << 4 | p[n + 2] >> 4) & 0x1f]; + + if (l > 3) { + enc[4] = b32_tbl[(p[n + 2] << 1 | p[n + 3] >> 7) & 0x1f]; + enc[5] = b32_tbl[(p[n + 3] >> 2) & 0x1f]; + + if (l > 4) { + enc[6] = b32_tbl[(p[n + 3] << 3 | p[n + 4] >> 5) & 0x1f]; + enc[7] = b32_tbl[(p[n + 4]) & 0x1f]; + } + else + enc[6] = b32_tbl[(p[n + 3] << 3) & 0x1f]; + } + else + enc[4] = b32_tbl[(p[n + 2] << 1) & 0x1f]; + } + else + enc[3] = b32_tbl[(p[n + 1] << 4) & 0x1f]; + } + else + enc[1] = b32_tbl[(p[n] << 2) & 0x1f]; + + s = xs_str_cat(s, enc); + } + + return s; +} + + +xs_str *xs_base32_enc(const xs_val *data, int sz) +/* encodes data to base32 */ +{ + return xs_base32_enc_tbl(data, sz, xs_b32_tbl); +} + + +xs_str *xs_base32hex_enc(const xs_val *data, int sz) +/* encodes data to base32 with HEX alphabet (RFC4648) */ +{ + return xs_base32_enc_tbl(data, sz, xs_b32hex_tbl); +} + + +xs_val *xs_base32_dec_tbl(const xs_str *data, int *size, const char *b32_tbl) +/* decodes data from base32 using a table */ +{ + xs_val *s = NULL; + int sz = 0; + char *p; + + p = (char *)data; + + /* size of data must be a multiple of 8 */ + if (strlen(p) % 8) + return NULL; + + for (p = (char *)data; *p; p += 8) { + int cs[8]; + int n; + unsigned char tmp[5]; + + for (n = 0; n < 8; n++) { + char *ss = strchr(b32_tbl, p[n]); + + if (ss == NULL) { + /* not a base32 char */ + return xs_free(s); + } + + cs[n] = ss - b32_tbl; + } + + n = 0; + + /* #0 byte */ + tmp[n++] = cs[0] << 3 | cs[1] >> 2; + + if (cs[2] != 32) { + /* #1 byte */ + tmp[n++] = (cs[1] & 0x3) << 6 | cs[2] << 1 | (cs[3] & 0x10) >> 4; + + if (cs[4] != 32) { + /* #2 byte */ + tmp[n++] = (cs[3] & 0xf) << 4 | cs[4] >> 1; + + if (cs[5] != 32) { + /* #3 byte */ + tmp[n++] = (cs[4] & 0x1) << 7 | cs[5] << 2 | cs[6] >> 3; + + if (cs[7] != 32) { + /* #4 byte */ + tmp[n++] = (cs[6] & 0x7) << 5 | cs[7]; + } + } + } + } + + /* must be done manually because data can be pure binary */ + s = xs_realloc(s, _xs_blk_size(sz + n)); + memcpy(s + sz, tmp, n); + sz += n; + } + + /* asciiz it to use it as a string */ + s = xs_realloc(s, _xs_blk_size(sz + 1)); + s[sz] = '\0'; + + *size = sz; + + return s; +} + + +xs_val *xs_base32_dec(const xs_str *data, int *size) +/* decodes data from base32 */ +{ + return xs_base32_dec_tbl(data, size, xs_b32_tbl); +} + + +xs_val *xs_base32hex_dec(const xs_str *data, int *size) +/* decodes data from base32 with HEX alphabet (RFC4648) */ +{ + return xs_base32_dec_tbl(data, size, xs_b32hex_tbl); +} + + +/** base64 */ + +static char *xs_b64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/="; + +xs_str *xs_base64_enc_tbl(const xs_val *data, int sz, const char *b64_tbl) +/* encodes data to base64 using a table */ { xs_str *s; unsigned char *p; char *i; int bsz, n; - static char *b64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; bsz = ((sz + 3 - 1) / 3) * 4; i = s = xs_realloc(NULL, _xs_blk_size(bsz + 1)); @@ -123,15 +292,19 @@ xs_str *xs_base64_enc(const xs_val *data, int sz) } -xs_val *xs_base64_dec(const xs_str *data, int *size) -/* decodes data from base64 */ +xs_str *xs_base64_enc(const xs_val *data, int sz) +/* encodes data to base64 */ +{ + return xs_base64_enc_tbl(data, sz, xs_b64_tbl); +} + + +xs_val *xs_base64_dec_tbl(const xs_str *data, int *size, const char *b64_tbl) +/* decodes data from base64 using a table */ { xs_val *s = NULL; int sz = 0; char *p; - static char *b64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/="; p = (char *)data; @@ -184,6 +357,34 @@ xs_val *xs_base64_dec(const xs_str *data, int *size) } +xs_val *xs_base64_dec(const xs_str *data, int *size) +/* decodes data from base64 */ +{ + return xs_base64_dec_tbl(data, size, xs_b64_tbl); +} + + +int xs_is_base64_tbl(const char *str, const char *b64_tbl) +/* returns 1 if str is a base64 string, with table */ +{ + while (*str) { + if (strchr(b64_tbl, *str++) == NULL) + return 0; + } + + return 1; +} + + +int xs_is_base64(const char *str) +/* returns 1 if str is a base64 string */ +{ + return xs_is_base64_tbl(str, xs_b64_tbl); +} + + +/** utf-8 **/ + xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint) /* encodes an Unicode codepoint to utf8 */ { diff --git a/xs_version.h b/xs_version.h index 1d59f5e..6117ce9 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* a885c7cc4c8e6384ae23125ed05f434471ccc6fb */ +/* dfdd729248d7169b80cb6a7462fe6c0ba6efeb16 */ -- cgit v1.2.3 From be5f08e6c3d605fb2beb1fdd1c2f10818b1e1812 Mon Sep 17 00:00:00 2001 From: default Date: Tue, 2 May 2023 06:49:00 +0200 Subject: Use xs_replace_n() where it suits. --- http.c | 2 +- mastoapi.c | 14 +++++++------- webfinger.c | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/http.c b/http.c index ae46001..58d188c 100644 --- a/http.c +++ b/http.c @@ -33,7 +33,7 @@ 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(url, "https:/" "/", ""); + xs *s = xs_replace_n(url, "https:/" "/", "", 1); l1 = xs_split_n(s, "/", 1); } diff --git a/mastoapi.c b/mastoapi.c index 72d9579..f7e101d 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -185,7 +185,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, int status = 404; xs_dict *msg = xs_dict_get(req, "q_vars"); - xs *cmd = xs_replace(q_path, "/oauth", ""); + xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); srv_debug(1, xs_fmt("oauth_get_handler %s", q_path)); @@ -245,7 +245,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, else args = xs_dup(xs_dict_get(req, "p_vars")); - xs *cmd = xs_replace(q_path, "/oauth", ""); + xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); srv_debug(1, xs_fmt("oauth_post_handler %s", q_path)); @@ -328,7 +328,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, const char *auhdr = xs_dict_get(req, "authorization"); if (!xs_is_null(auhdr) && xs_startswith(auhdr, "Basic ")) { - xs *s1 = xs_replace(auhdr, "Basic ", ""); + xs *s1 = xs_replace_n(auhdr, "Basic ", "", 1); int size; xs *s2 = xs_base64_dec(s1, &size); @@ -787,7 +787,7 @@ int process_auth_token(snac *snac, const xs_dict *req) /* if there is an authorization field, try to validate it */ if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { - xs *tokid = xs_replace(v, "Bearer ", ""); + xs *tokid = xs_replace_n(v, "Bearer ", "", 1); xs *token = token_get(tokid); if (token != NULL) { @@ -826,7 +826,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, int status = 404; xs_dict *args = xs_dict_get(req, "q_vars"); - xs *cmd = xs_replace(q_path, "/api", ""); + xs *cmd = xs_replace_n(q_path, "/api", "", 1); snac snac1 = {0}; int logged_in = process_auth_token(&snac1, req); @@ -1500,7 +1500,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, printf("%s\n", j); }*/ - xs *cmd = xs_replace(q_path, "/api", ""); + xs *cmd = xs_replace_n(q_path, "/api", "", 1); snac snac = {0}; int logged_in = process_auth_token(&snac, req); @@ -1916,7 +1916,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path, if (args == NULL) return 400; - xs *cmd = xs_replace(q_path, "/api", ""); + xs *cmd = xs_replace_n(q_path, "/api", "", 1); snac snac = {0}; int logged_in = process_auth_token(&snac, req); diff --git a/webfinger.c b/webfinger.c index eb6b2ad..f56b6f2 100644 --- a/webfinger.c +++ b/webfinger.c @@ -21,7 +21,7 @@ int webfinger_request(const char *qs, char **actor, char **user) if (xs_startswith(qs, "https:/" "/")) { /* actor query: pick the host */ - xs *s = xs_replace(qs, "https:/" "/", ""); + xs *s = xs_replace_n(qs, "https:/" "/", "", 1); l = xs_split_n(s, "/", 1); @@ -74,7 +74,7 @@ int webfinger_request(const char *qs, char **actor, char **user) char *subject = xs_dict_get(obj, "subject"); if (subject) - *user = xs_replace(subject, "acct:", ""); + *user = xs_replace_n(subject, "acct:", "", 1); } if (actor != NULL) { @@ -136,7 +136,7 @@ int webfinger_get_handler(d_char *req, char *q_path, else if (xs_startswith(resource, "acct:")) { /* it's an account name */ - xs *an = xs_replace(resource, "acct:", ""); + xs *an = xs_replace_n(resource, "acct:", "", 1); xs *l = NULL; /* strip a possible leading @ */ -- cgit v1.2.3 From 0bd609f5be9aa24bced629273ff2428058388ac3 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 3 May 2023 07:57:10 +0200 Subject: Fixed missing notifications in certain circunstancies. --- html.c | 2 +- mastoapi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/html.c b/html.c index 5f57262..9d6fbc6 100644 --- a/html.c +++ b/html.c @@ -1226,7 +1226,7 @@ xs_str *html_notifications(snac *snac) const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; - if (!valid_status(object_get(actor_id, &actor))) + if (!valid_status(actor_get(snac, actor_id, &actor))) continue; xs *a_name = actor_name(actor); diff --git a/mastoapi.c b/mastoapi.c index f7e101d..7324bed 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1108,7 +1108,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, xs *actor = NULL; xs *entry = NULL; - if (!valid_status(object_get(xs_dict_get(noti, "actor"), &actor))) + if (!valid_status(actor_get(&snac1, xs_dict_get(noti, "actor"), &actor))) continue; if (objid != NULL && !valid_status(object_get(objid, &entry))) -- cgit v1.2.3 From 18f799a58fc7516012751abb0b371ba5420ac12b Mon Sep 17 00:00:00 2001 From: default Date: Wed, 3 May 2023 07:58:08 +0200 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 78a3603..7453ec3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,8 @@ ## 2.30 +Fixed a bug that made some notifications to be missed. + New Mastodon API features: the instance public timeline is now a real one, unfavourite / unreblog is supported (somewhat). ## 2.29 -- cgit v1.2.3 From cf2b334d86e8b4ccd0525fbb741fbbaf86a2c618 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 3 May 2023 08:15:38 +0200 Subject: Ensure no actor data is left in actor_get(). --- data.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data.c b/data.c index 67f5751..edeb676 100644 --- a/data.c +++ b/data.c @@ -1360,7 +1360,7 @@ int actor_get(snac *snac1, const char *actor, xs_dict **data) /* returns an already downloaded actor */ { int status = 200; - xs_dict *d; + xs_dict *d = NULL; if (strcmp(actor, snac1->actor) == 0) { /* this actor */ @@ -1388,8 +1388,10 @@ int actor_get(snac *snac1, const char *actor, xs_dict **data) } /* read the object */ - if (!valid_status(status = object_get(actor, &d))) + if (!valid_status(status = object_get(actor, &d))) { + d = xs_free(d); return status; + } if (data) *data = d; -- cgit v1.2.3 From cf8d91147b1a0233ba5d3d98a8806bd2a87c222d Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 05:52:35 +0200 Subject: Updated documentation. --- doc/snac.1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/snac.1 b/doc/snac.1 index ec8df64..91d7e85 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -220,6 +220,20 @@ Please take note that they will show your timeline in a 'Mastodon fashion' post display with the most active threads at the top that the web interface of .Nm provides. +.Ss Implementing post bots +.Nm +makes very easy to post messages in a non-interactive manner. This example +posts a string: +.Bd -literal -offset indent +uptime | snac note $SNAC_BASEDIR $SNAC_USER - +.Ed +.Pp +You can setup a line like this from a +.Xr crontab 5 +or similar. Take note that you need a) command-line access to the same machine +that hosts the +.Nm +instance, and b) write permissions to the storage directories and files. .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From a9f0f2f695d653a2ca58057837fae6b8bc4dc13f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 06:27:13 +0200 Subject: Avoid crash in optional mastoapi argument. --- mastoapi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mastoapi.c b/mastoapi.c index 7324bed..c51c933 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1569,6 +1569,9 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (xs_is_null(media_ids)) media_ids = xs_dict_get(args, "media_ids[]"); + if (xs_is_null(visibility)) + visibility = "public"; + xs *attach_list = xs_list_new(); xs *irt = NULL; -- cgit v1.2.3 From dd290c8e226c7d78dc74695d26e28ec8bf4eab26 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 06:38:24 +0200 Subject: Updated documentation. --- doc/snac.1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/snac.1 b/doc/snac.1 index 91d7e85..c52eba9 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -234,6 +234,17 @@ or similar. Take note that you need a) command-line access to the same machine that hosts the .Nm instance, and b) write permissions to the storage directories and files. +.Pp +You can also post non-interactively using the Mastodon API and a command-line +http tool like +.Xr curl 1 +or similar. This has the advantage that you can do it remotely from any host, +anywhere; the only thing you need is an API Token. This is an example: +.Bd -literal -offset indent +curl -X POST https://snac.example.com/api/v1/statuses \ +--header "Authorization: Bearer ${TOKEN}" -d "status=$(uptime)" +.Ed +.Pp .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From d46bf8837c1c6654868170a293352d12f6d84962 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 06:40:51 +0200 Subject: Updated documentation. --- doc/snac.1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/snac.1 b/doc/snac.1 index c52eba9..0d36f5d 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -241,10 +241,11 @@ http tool like or similar. This has the advantage that you can do it remotely from any host, anywhere; the only thing you need is an API Token. This is an example: .Bd -literal -offset indent -curl -X POST https://snac.example.com/api/v1/statuses \ +curl -X POST https://snac.example.com/api/v1/statuses \\ --header "Authorization: Bearer ${TOKEN}" -d "status=$(uptime)" .Ed .Pp +You can obtain an API Token from a mobile app. .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From cd19efcae29d64171e9f2eb852799cc0fe5642ea Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 09:17:50 +0200 Subject: Updated dependencies. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c0e4170..710d61f 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ httpd.o: httpd.c xs.h xs_io.h xs_encdec.h xs_json.h xs_socket.h \ xs_httpd.h xs_mime.h snac.h main.o: main.c xs.h xs_io.h xs_encdec.h xs_json.h snac.h mastoapi.o: mastoapi.c xs.h xs_encdec.h xs_openssl.h xs_json.h xs_io.h \ - xs_time.h snac.h + xs_time.h xs_glob.h snac.h snac.o: snac.c xs.h xs_io.h xs_encdec.h xs_json.h xs_curl.h xs_openssl.h \ xs_socket.h xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h \ snac.h -- cgit v1.2.3 From 511f5062b7df26c47409c88649f24d68bbd43ccb Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 09:19:26 +0200 Subject: Deleted real unused parameters. --- activitypub.c | 4 ++-- data.c | 2 +- html.c | 15 +++++++-------- snac.h | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/activitypub.c b/activitypub.c index b20ae0f..13b1ce2 100644 --- a/activitypub.c +++ b/activitypub.c @@ -109,7 +109,7 @@ int actor_request(snac *snac, const char *actor, xs_dict **data) if (valid_status(status2)) { /* renew data */ - status = actor_add(snac, actor, payload); + status = actor_add(actor, payload); if (data != NULL) { *data = payload; @@ -1121,7 +1121,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) else if (strcmp(type, "Update") == 0) { if (strcmp(utype, "Person") == 0) { - actor_add(snac, actor, xs_dict_get(msg, "object")); + actor_add(actor, xs_dict_get(msg, "object")); snac_log(snac, xs_fmt("updated actor %s", actor)); } diff --git a/data.c b/data.c index edeb676..f03fd71 100644 --- a/data.c +++ b/data.c @@ -1349,7 +1349,7 @@ int is_hidden(snac *snac, const char *id) } -int actor_add(snac *snac, const char *actor, xs_dict *msg) +int actor_add(const char *actor, xs_dict *msg) /* adds an actor */ { return object_add_ow(actor, msg); diff --git a/html.c b/html.c index 9d6fbc6..2aa6514 100644 --- a/html.c +++ b/html.c @@ -79,7 +79,7 @@ xs_str *actor_name(xs_dict *actor) } -d_char *html_actor_icon(snac *snac, d_char *os, char *actor, +xs_str *html_actor_icon(xs_str *os, char *actor, const char *date, const char *udate, const char *url, int priv) { xs *s = xs_str_new(NULL); @@ -168,7 +168,7 @@ d_char *html_msg_icon(snac *snac, d_char *os, char *msg) date = xs_dict_get(msg, "published"); udate = xs_dict_get(msg, "updated"); - os = html_actor_icon(snac, os, actor, date, udate, url, priv); + os = html_actor_icon(os, actor, date, udate, url, priv); } return os; @@ -983,7 +983,7 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, int local, } -d_char *html_user_footer(snac *snac, d_char *s) +xs_str *html_user_footer(xs_str *s) { xs *s1 = xs_fmt( "
\n" @@ -1064,7 +1064,7 @@ d_char *html_timeline(snac *snac, char *list, int local, int skip, int show, int s = xs_str_cat(s, s1); } - s = html_user_footer(snac, s); + s = html_user_footer(s); s = xs_str_cat(s, "\n\n"); @@ -1088,8 +1088,7 @@ d_char *html_people_list(snac *snac, d_char *os, d_char *list, const char *heade if (valid_status(actor_get(snac, actor_id, &actor))) { s = xs_str_cat(s, "
\n"); - s = html_actor_icon(snac, s, actor, xs_dict_get(actor, "published"), NULL, NULL, 0); - + s = html_actor_icon(s, actor, xs_dict_get(actor, "published"), NULL, NULL, 0); /* content (user bio) */ char *c = xs_dict_get(actor, "summary"); @@ -1182,7 +1181,7 @@ d_char *html_people(snac *snac) s = html_people_list(snac, s, wers, L("People that follows you"), "e"); - s = html_user_footer(snac, s); + s = html_user_footer(s); s = xs_str_cat(s, "\n\n"); @@ -1277,7 +1276,7 @@ xs_str *html_notifications(snac *snac) s = xs_str_cat(s, s1); } - s = html_user_footer(snac, s); + s = html_user_footer(s); s = xs_str_cat(s, "\n\n"); diff --git a/snac.h b/snac.h index a162618..54c8e51 100644 --- a/snac.h +++ b/snac.h @@ -128,7 +128,7 @@ int is_muted(snac *snac, const char *actor); void hide(snac *snac, const char *id); int is_hidden(snac *snac, const char *id); -int actor_add(snac *snac, const char *actor, xs_dict *msg); +int actor_add(const char *actor, xs_dict *msg); int actor_get(snac *snac, const char *actor, xs_dict **data); int static_get(snac *snac, const char *id, d_char **data, int *size); -- cgit v1.2.3 From 753eadfd1775545c5bfb4110ad2ba7cb61df9588 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 09:25:09 +0200 Subject: Added some const here and there. --- activitypub.c | 6 +++--- data.c | 2 +- html.c | 6 ++++-- httpd.c | 2 +- snac.h | 14 ++++++++------ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/activitypub.c b/activitypub.c index 13b1ce2..86d33df 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1426,7 +1426,7 @@ int process_queue(void) /** HTTP handlers */ -int activitypub_get_handler(d_char *req, char *q_path, +int activitypub_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) { int status = 200; @@ -1519,8 +1519,8 @@ int activitypub_get_handler(d_char *req, char *q_path, } -int activitypub_post_handler(d_char *req, char *q_path, - d_char *payload, int p_size, +int activitypub_post_handler(const xs_dict *req, const char *q_path, + char *payload, int p_size, char **body, int *b_size, char **ctype) /* processes an input message */ { diff --git a/data.c b/data.c index f03fd71..72b63f8 100644 --- a/data.c +++ b/data.c @@ -1814,7 +1814,7 @@ static xs_dict *_new_qmsg(const char *type, const xs_val *msg, int retries) } -void enqueue_input(snac *snac, xs_dict *msg, xs_dict *req, int retries) +void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retries) /* enqueues an input message */ { xs *qmsg = _new_qmsg("input", msg, retries); diff --git a/html.c b/html.c index 2aa6514..3ba7930 100644 --- a/html.c +++ b/html.c @@ -1289,7 +1289,8 @@ xs_str *html_notifications(snac *snac) } -int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char **ctype) +int html_get_handler(const xs_dict *req, const char *q_path, + char **body, int *b_size, char **ctype) { char *accept = xs_dict_get(req, "accept"); int status = 404; @@ -1546,7 +1547,8 @@ int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char * } -int html_post_handler(d_char *req, char *q_path, d_char *payload, int p_size, +int html_post_handler(const xs_dict *req, const char *q_path, + char *payload, int p_size, char **body, int *b_size, char **ctype) { int status = 0; diff --git a/httpd.c b/httpd.c index 70083a1..5520457 100644 --- a/httpd.c +++ b/httpd.c @@ -43,7 +43,7 @@ d_char *nodeinfo_2_0(void) } -int server_get_handler(d_char *req, char *q_path, +int server_get_handler(xs_dict *req, char *q_path, char **body, int *b_size, char **ctype) /* basic server services */ { diff --git a/snac.h b/snac.h index 54c8e51..5279453 100644 --- a/snac.h +++ b/snac.h @@ -155,7 +155,7 @@ void inbox_add(const char *inbox); void inbox_add_by_actor(const xs_dict *actor); xs_list *inbox_list(void); -void enqueue_input(snac *snac, xs_dict *msg, xs_dict *req, int retries); +void enqueue_input(snac *snac, 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); void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries); @@ -187,7 +187,7 @@ int check_signature(snac *snac, xs_dict *req, xs_str **err); void httpd(void); int webfinger_request(const char *qs, char **actor, char **user); -int webfinger_get_handler(d_char *req, char *q_path, +int webfinger_get_handler(xs_dict *req, char *q_path, char **body, int *b_size, char **ctype); const char *default_avatar_base64(void); @@ -220,17 +220,19 @@ int process_user_queue(snac *snac); void process_queue_item(xs_dict *q_item); int process_queue(void); -int activitypub_get_handler(d_char *req, char *q_path, +int activitypub_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype); -int activitypub_post_handler(d_char *req, char *q_path, +int activitypub_post_handler(const xs_dict *req, const char *q_path, char *payload, int p_size, char **body, int *b_size, char **ctype); d_char *not_really_markdown(const char *content); d_char *sanitize(const char *str); -int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char **ctype); -int html_post_handler(d_char *req, char *q_path, d_char *payload, int p_size, +int html_get_handler(const xs_dict *req, const char *q_path, + char **body, int *b_size, char **ctype); +int html_post_handler(const xs_dict *req, const char *q_path, + char *payload, int p_size, char **body, int *b_size, char **ctype); int snac_init(const char *_basedir); -- cgit v1.2.3 From 185aac23876f9ae5d9b613f8b9abd517c3ab3b4d Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 09:28:36 +0200 Subject: Added -Wextra to C flags. --- Makefile | 2 +- activitypub.c | 2 ++ html.c | 3 +++ httpd.c | 6 ++++++ mastoapi.c | 13 +++++++++++++ webfinger.c | 2 ++ 6 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 710d61f..7dbd9e7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PREFIX=/usr/local PREFIX_MAN=$(PREFIX)/man -CFLAGS?=-g -Wall +CFLAGS?=-g -Wall -Wextra all: snac diff --git a/activitypub.c b/activitypub.c index 86d33df..c25b733 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1524,6 +1524,8 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) /* processes an input message */ { + (void)b_size; + int status = 202; /* accepted */ char *i_ctype = xs_dict_get(req, "content-type"); snac snac; diff --git a/html.c b/html.c index 3ba7930..3ebe70d 100644 --- a/html.c +++ b/html.c @@ -1551,6 +1551,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, char *payload, int p_size, char **body, int *b_size, char **ctype) { + (void)p_size; + (void)ctype; + int status = 0; snac snac; char *uid, *p_path; diff --git a/httpd.c b/httpd.c index 5520457..fe93727 100644 --- a/httpd.c +++ b/httpd.c @@ -49,6 +49,8 @@ int server_get_handler(xs_dict *req, char *q_path, { int status = 0; + (void)req; + /* is it the server root? */ if (*q_path == '\0') { /* try to open greeting.html */ @@ -285,6 +287,8 @@ static jmp_buf on_break; void term_handler(int s) { + (void)s; + longjmp(on_break, 1); } @@ -401,6 +405,8 @@ static void *background_thread(void *arg) { time_t purge_time; + (void)arg; + /* first purge time */ purge_time = time(NULL) + 10 * 60; diff --git a/mastoapi.c b/mastoapi.c index c51c933..2413edb 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -175,6 +175,8 @@ const char *login_page = "" int oauth_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) { + (void)b_size; + if (!xs_startswith(q_path, "/oauth/")) return 0; @@ -227,6 +229,9 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, const char *payload, int p_size, char **body, int *b_size, char **ctype) { + (void)p_size; + (void)b_size; + if (!xs_startswith(q_path, "/oauth/")) return 0; @@ -815,6 +820,8 @@ int process_auth_token(snac *snac, const xs_dict *req) int mastoapi_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype) { + (void)b_size; + if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; @@ -1474,6 +1481,9 @@ 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) { + (void)p_size; + (void)b_size; + if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; @@ -1898,6 +1908,9 @@ 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) { + (void)p_size; + (void)b_size; + if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) return 0; diff --git a/webfinger.c b/webfinger.c index f56b6f2..765c469 100644 --- a/webfinger.c +++ b/webfinger.c @@ -104,6 +104,8 @@ int webfinger_get_handler(d_char *req, char *q_path, { int status; + (void)b_size; + if (strcmp(q_path, "/.well-known/webfinger") != 0) return 0; -- cgit v1.2.3 From 980a8d524fae11d17366e7aea56206d13f278e31 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 09:34:33 +0200 Subject: Fixed more warnings. --- activitypub.c | 3 ++- html.c | 1 + mastoapi.c | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index c25b733..0d5439d 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1384,8 +1384,9 @@ void process_queue_item(xs_dict *q_item) xs *headers = xs_dict_new(); headers = xs_dict_append(headers, "content-type", "application/json"); - xs *rsp = xs_http_request("POST", url, headers, + xs *rsp = xs_http_request("POST", url, headers, body, strlen(body), &status, NULL, NULL, 0); + rsp = xs_free(rsp); srv_debug(0, xs_fmt("telegram post %d", status)); } diff --git a/html.c b/html.c index 3ebe70d..8d4203d 100644 --- a/html.c +++ b/html.c @@ -1282,6 +1282,7 @@ xs_str *html_notifications(snac *snac) /* set the check time to now */ xs *dummy = notify_check_time(snac, 1); + dummy = xs_free(dummy); timeline_touch(snac); diff --git a/mastoapi.c b/mastoapi.c index 2413edb..c333573 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -542,7 +542,6 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) xs *f = xs_val_new(XSTYPE_FALSE); xs *t = xs_val_new(XSTYPE_TRUE); xs *n = xs_val_new(XSTYPE_NULL); - xs *el = xs_list_new(); xs *idx = NULL; xs *ixc = NULL; -- cgit v1.2.3 From f6ef275fa3dfd0e74093a5eb74a5167f7be4ece0 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 11:08:35 +0200 Subject: Made the post action configurable in login_page. --- mastoapi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index c333573..4396155 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -162,7 +162,7 @@ const char *login_page = "" "\n" "

%s OAuth identify

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

Login:

\n" "

Password:

\n" "\n" @@ -208,7 +208,8 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, if (xs_is_null(state)) state = ""; - *body = xs_fmt(login_page, host, "", host, ruri, cid, state, USER_AGENT); + *body = xs_fmt(login_page, host, "", host, "oauth/x-snac-login", + ruri, cid, state, USER_AGENT); *ctype = "text/html"; status = 200; @@ -264,7 +265,8 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, const char *host = xs_dict_get(srv_config, "host"); /* by default, generate another login form with an error */ - *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, redir, cid, state, USER_AGENT); + *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, "oauth/x-snac-login", + redir, cid, state, USER_AGENT); *ctype = "text/html"; status = 200; @@ -273,8 +275,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, if (user_open(&snac, login)) { /* check the login + password */ - if (check_password(login, passwd, - xs_dict_get(snac.config, "passwd"))) { + if (check_password(login, passwd, xs_dict_get(snac.config, "passwd"))) { /* success! redirect to the desired uri */ xs *code = random_str(); -- cgit v1.2.3 From ec6f94e27ed0bfb5f8dd029f372cf4fe060ca19f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 11:52:04 +0200 Subject: New url /oauth/x-snac-get-token. --- mastoapi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/mastoapi.c b/mastoapi.c index 4396155..6729cf7 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -221,6 +221,16 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, else srv_debug(0, xs_fmt("oauth authorize: invalid or unset arguments")); } + else + if (strcmp(cmd, "/x-snac-get-token") == 0) { + const char *host = xs_dict_get(srv_config, "host"); + + *body = xs_fmt(login_page, host, "", host, "oauth/x-snac-get-token", + "", "", "", USER_AGENT); + *ctype = "text/html"; + status = 200; + + } return status; } @@ -427,6 +437,48 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, status = 403; } } + 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"); + + /* by default, generate another login form with an error */ + *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, "oauth/x-snac-get-token", + "", "", "", USER_AGENT); + *ctype = "text/html"; + status = 200; + + if (login && passwd) { + snac user; + + if (user_open(&user, login)) { + /* check the login + password */ + if (check_password(login, passwd, xs_dict_get(user.config, "passwd"))) { + /* success! create a new token */ + xs *tokid = random_str(); + + srv_debug(1, xs_fmt("x-snac-new-token: " + "successful login for %s, new token %s", login, tokid)); + + xs *token = xs_dict_new(); + token = xs_dict_append(token, "token", tokid); + token = xs_dict_append(token, "client_id", "snac-client"); + token = xs_dict_append(token, "client_secret", ""); + token = xs_dict_append(token, "uid", login); + token = xs_dict_append(token, "code", ""); + + token_add(tokid, token); + + *ctype = "text/plain"; + xs_free(*body); + *body = xs_dup(tokid); + } + + user_free(&user); + } + } + } return status; } -- cgit v1.2.3 From 212d1350fe261dbc04eb8cc9a9462c5ce5747597 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 11:53:17 +0200 Subject: Fixed mastoapi debug levels. --- mastoapi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 6729cf7..3c4be5a 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -213,13 +213,13 @@ int oauth_get_handler(const xs_dict *req, const char *q_path, *ctype = "text/html"; status = 200; - srv_debug(0, xs_fmt("oauth authorize: generating login page")); + srv_debug(1, xs_fmt("oauth authorize: generating login page")); } else - srv_debug(0, xs_fmt("oauth authorize: bad client_id %s", cid)); + srv_debug(1, xs_fmt("oauth authorize: bad client_id %s", cid)); } else - srv_debug(0, xs_fmt("oauth authorize: invalid or unset arguments")); + srv_debug(1, xs_fmt("oauth authorize: invalid or unset arguments")); } else if (strcmp(cmd, "/x-snac-get-token") == 0) { @@ -389,7 +389,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, const char *uid = xs_dict_get(app, "uid"); - srv_debug(0, xs_fmt("oauth token: " + srv_debug(1, xs_fmt("oauth token: " "successful login for %s, new token %s", uid, tokid)); xs *token = xs_dict_new(); @@ -403,7 +403,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, } } else { - srv_debug(0, xs_fmt("oauth token: invalid or unset arguments")); + srv_debug(1, xs_fmt("oauth token: invalid or unset arguments")); status = 400; } } @@ -425,7 +425,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, } else { token_del(tokid); - srv_debug(0, xs_fmt("oauth revoke: revoked token %s", tokid)); + srv_debug(1, xs_fmt("oauth revoke: revoked token %s", tokid)); status = 200; /* also delete the app, as it serves no purpose from now on */ @@ -433,7 +433,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, } } else { - srv_debug(0, xs_fmt("oauth revoke: invalid or unset arguments")); + srv_debug(1, xs_fmt("oauth revoke: invalid or unset arguments")); status = 403; } } @@ -1611,7 +1611,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, app_add(cid, app); - srv_debug(0, xs_fmt("mastoapi apps: new app %s", cid)); + srv_debug(1, xs_fmt("mastoapi apps: new app %s", cid)); } } else -- cgit v1.2.3 From cd71bd08bffc58f01610f3c13ff00c7ec214fd6a Mon Sep 17 00:00:00 2001 From: default Date: Thu, 4 May 2023 11:59:18 +0200 Subject: Updated documentation. --- doc/snac.1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/snac.1 b/doc/snac.1 index 0d36f5d..ef84f3d 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -241,11 +241,15 @@ http tool like or similar. This has the advantage that you can do it remotely from any host, anywhere; the only thing you need is an API Token. This is an example: .Bd -literal -offset indent -curl -X POST https://snac.example.com/api/v1/statuses \\ +curl -X POST https://$SNAC_HOST/api/v1/statuses \\ --header "Authorization: Bearer ${TOKEN}" -d "status=$(uptime)" .Ed .Pp -You can obtain an API Token from a mobile app. +You can obtain an API Token by connecting to the following URL: +.Bd -literal -offset indent +https://$SNAC_HOST/oauth/x-snac-get-token +.Ed +.Pp .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev DEBUG -- cgit v1.2.3 From d3a36218a6ebfc951ff38c410ac1c29ec15c3c7f Mon Sep 17 00:00:00 2001 From: default Date: Fri, 5 May 2023 09:54:41 +0200 Subject: Implemented 'Ping' and 'Pong' activities. According to https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt --- activitypub.c | 41 ++++++++++++++++++++++++++++++++++++++--- main.c | 14 ++++++++++++++ snac.h | 2 ++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/activitypub.c b/activitypub.c index 0d5439d..6127dab 100644 --- a/activitypub.c +++ b/activitypub.c @@ -437,7 +437,8 @@ void process_tags(snac *snac, const char *content, d_char **n_content, d_char ** /** messages **/ -d_char *msg_base(snac *snac, char *type, char *id, char *actor, char *date, char *object) +xs_dict *msg_base(snac *snac, const char *type, const char *id, + const char *actor, const char *date, const char *object) /* creates a base ActivityPub message */ { xs *did = NULL; @@ -467,7 +468,7 @@ d_char *msg_base(snac *snac, char *type, char *id, char *actor, char *date, char } } - d_char *msg = xs_dict_new(); + xs_dict *msg = xs_dict_new(); msg = xs_dict_append(msg, "@context", "https:/" "/www.w3.org/ns/activitystreams"); msg = xs_dict_append(msg, "type", type); @@ -845,6 +846,28 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, } +xs_dict *msg_ping(snac *user, const char *rcpt) +/* creates a Ping message (https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt) */ +{ + xs_dict *msg = msg_base(user, "Ping", "@dummy", user->actor, NULL, NULL); + + msg = xs_dict_append(msg, "to", rcpt); + + return msg; +} + + +xs_dict *msg_pong(snac *user, const char *rcpt, const char *object) +/* creates a Pong message (https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt) */ +{ + xs_dict *msg = msg_base(user, "Pong", "@dummy", user->actor, NULL, object); + + msg = xs_dict_append(msg, "to", rcpt); + + return msg; +} + + void notify(snac *snac, xs_str *type, xs_str *utype, xs_str *actor, xs_dict *msg) /* notifies the user of relevant events */ { @@ -1147,7 +1170,19 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) snac_debug(snac, 1, xs_fmt("ignored 'Delete' for unknown object %s", object)); } else - snac_debug(snac, 1, xs_fmt("process_message type '%s' ignored", type)); + if (strcmp(type, "Pong") == 0) { + snac_log(snac, xs_fmt("'Pong' received from %s", actor)); + } + else + if (strcmp(type, "Ping") == 0) { + snac_log(snac, xs_fmt("'Ping' requested from %s", actor)); + + xs *rsp = msg_pong(snac, actor, xs_dict_get(msg, "id")); + + enqueue_output_by_actor(snac, rsp, actor, 0); + } + else + snac_debug(snac, 1, xs_fmt("process_input_message type '%s' ignored", type)); if (do_notify) { notify(snac, type, utype, actor, msg); diff --git a/main.c b/main.c index 6a0bb74..b8fc97b 100644 --- a/main.c +++ b/main.c @@ -30,6 +30,7 @@ int usage(void) printf("actor {basedir} {uid} {url} Requests an actor\n"); printf("note {basedir} {uid} {'text'} Sends a note to followers\n"); printf("resetpwd {basedir} {uid} Resets the password of a user\n"); + printf("ping {basedir} {uid} {actor} Pings an actor\n"); return 1; } @@ -228,6 +229,19 @@ int main(int argc, char *argv[]) return 0; } + if (strcmp(cmd, "ping") == 0) { + xs *msg = msg_ping(&snac, url); + + enqueue_output_by_actor(&snac, msg, url, 0); + + if (dbglevel) { + xs *j = xs_json_dumps_pp(msg, 4); + printf("%s\n", msg); + } + + return 0; + } + if (strcmp(cmd, "request") == 0) { int status; xs *data = NULL; diff --git a/snac.h b/snac.h index 5279453..7d4b8f4 100644 --- a/snac.h +++ b/snac.h @@ -203,6 +203,8 @@ d_char *msg_undo(snac *snac, char *object); d_char *msg_delete(snac *snac, char *id); d_char *msg_actor(snac *snac); xs_dict *msg_update(snac *snac, xs_dict *object); +xs_dict *msg_ping(snac *user, const char *rcpt); +xs_dict *msg_pong(snac *user, const char *rcpt, const char *object); int activitypub_request(snac *snac, const char *url, xs_dict **data); int actor_request(snac *snac, const char *actor, xs_dict **data); -- cgit v1.2.3 From c1772cbca6d2562dadbc686fe481b88960398173 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 5 May 2023 10:05:04 +0200 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7453ec3..63dcacd 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Fixed a bug that made some notifications to be missed. New Mastodon API features: the instance public timeline is now a real one, unfavourite / unreblog is supported (somewhat). +The non-standard `Ping` and `Pong` #ActivityPub activities have been implemented as proposed by @tedu@honk.tedunangst.com in the https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt document (with a minor diversion: retries are managed in the same way as the rest of #snac messages). + ## 2.29 New Mastodon API features: account search, relationships (so the Follow/Unfollow buttons now appear for each account), follow and unfollow accounts, an instance-level timeline (very kludgy), custom emojis for accounts and statuses, many bug fixes (sadly, the Mastodon official app still does not work). -- cgit v1.2.3 From 8cd79df272645da70cfe2980d6f9c0c33c13e452 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 5 May 2023 10:08:39 +0200 Subject: Fixed bug in ping cmdline debug output. --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index b8fc97b..c44b527 100644 --- a/main.c +++ b/main.c @@ -236,7 +236,7 @@ int main(int argc, char *argv[]) if (dbglevel) { xs *j = xs_json_dumps_pp(msg, 4); - printf("%s\n", msg); + printf("%s\n", j); } return 0; -- cgit v1.2.3 From daaf863d83acd8638a29b8bbf9d6bedb7fae40f2 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 5 May 2023 12:47:17 +0200 Subject: The 'ping' command-line requests the actor for better error info. --- main.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index c44b527..3f34673 100644 --- a/main.c +++ b/main.c @@ -230,13 +230,21 @@ int main(int argc, char *argv[]) } if (strcmp(cmd, "ping") == 0) { - xs *msg = msg_ping(&snac, url); + xs *actor_o = NULL; - enqueue_output_by_actor(&snac, msg, url, 0); + if (valid_status(actor_request(&snac, url, &actor_o))) { + xs *msg = msg_ping(&snac, url); - if (dbglevel) { - xs *j = xs_json_dumps_pp(msg, 4); - printf("%s\n", j); + enqueue_output_by_actor(&snac, msg, url, 0); + + if (dbglevel) { + xs *j = xs_json_dumps_pp(msg, 4); + printf("%s\n", j); + } + } + else { + srv_log(xs_fmt("Error getting actor %s", url)); + return 1; } return 0; -- cgit v1.2.3 From 5b7a22e5b01608d046a665bb1a16dd4b4785e507 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 5 May 2023 12:58:54 +0200 Subject: Updated examples/docket-entrypoint.sh. snac init now prompts for an optional admin email address. --- examples/docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docker-entrypoint.sh b/examples/docker-entrypoint.sh index 639b692..a6216b2 100755 --- a/examples/docker-entrypoint.sh +++ b/examples/docker-entrypoint.sh @@ -1,7 +1,7 @@ #! /bin/sh if [ ! -e /data/data/server.json ] then - echo -ne "0.0.0.0\r\n8001\r\nlocalhost\r\n\r\n" | snac init /data/data + echo -ne "0.0.0.0\r\n8001\r\nlocalhost\r\n\r\n\r\n" | snac init /data/data snac adduser /data/data testuser fi SSLKEYLOGFILE=/data/key snac httpd /data/data -- cgit v1.2.3 From eed24fde1a9c3577cc2d41dfdb25c13ea816dd42 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 6 May 2023 11:10:08 +0200 Subject: Fixed a bug in Tusky's image send. --- mastoapi.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mastoapi.c b/mastoapi.c index 3c4be5a..b82ecfa 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -1333,25 +1333,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, cfg = xs_dict_append(cfg, "statuses", d11); } - { - xs *d11 = xs_dict_new(); - xs *mt = xs_list_new(); - - mt = xs_list_append(mt, "image/jpeg"); - mt = xs_list_append(mt, "image/png"); - mt = xs_list_append(mt, "image/gif"); - - d11 = xs_dict_append(d11, "supported_mime_types", mt); - - d11 = xs_dict_append(d11, "image_size_limit", z); - d11 = xs_dict_append(d11, "image_matrix_limit", z); - d11 = xs_dict_append(d11, "video_size_limit", z); - d11 = xs_dict_append(d11, "video_matrix_limit", z); - d11 = xs_dict_append(d11, "video_frame_rate_limit", z); - - cfg = xs_dict_append(cfg, "media_attachments", d11); - } - ins = xs_dict_append(ins, "configuration", cfg); *body = xs_json_dumps_pp(ins, 4); -- cgit v1.2.3 From 51d225dde0d2f5242a781e7c3405cff8bb1c507c Mon Sep 17 00:00:00 2001 From: default Date: Sat, 6 May 2023 11:17:58 +0200 Subject: Updated RELEASE_NOTES. --- RELEASE_NOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 63dcacd..1af4b73 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,10 +4,12 @@ Fixed a bug that made some notifications to be missed. -New Mastodon API features: the instance public timeline is now a real one, unfavourite / unreblog is supported (somewhat). +New Mastodon API features: the instance public timeline is now a real one, unfavourite / unreblog is supported (somewhat). Some regression bugs regarding image posting were also fixed. The non-standard `Ping` and `Pong` #ActivityPub activities have been implemented as proposed by @tedu@honk.tedunangst.com in the https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt document (with a minor diversion: retries are managed in the same way as the rest of #snac messages). +The build process now includes the `-Wextra` flag. + ## 2.29 New Mastodon API features: account search, relationships (so the Follow/Unfollow buttons now appear for each account), follow and unfollow accounts, an instance-level timeline (very kludgy), custom emojis for accounts and statuses, many bug fixes (sadly, the Mastodon official app still does not work). -- cgit v1.2.3 From 6a1cc55676eaf18ecadb42b86474aaef73cf9805 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 7 May 2023 08:54:54 +0200 Subject: Version 2.30 RELEASED. --- snac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snac.h b/snac.h index 7d4b8f4..782ae51 100644 --- a/snac.h +++ b/snac.h @@ -1,7 +1,7 @@ /* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2023 grunfink / MIT license */ -#define VERSION "2.30-dev" +#define VERSION "2.30" #define USER_AGENT "snac/" VERSION -- cgit v1.2.3