summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c208
1 files changed, 147 insertions, 61 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 6a12ced..f15c9c0 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -1,5 +1,5 @@
1/* snac - A simple, minimalistic ActivityPub instance */ 1/* snac - A simple, minimalistic ActivityPub instance */
2/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ 2/* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
3 3
4#ifndef NO_MASTODON_API 4#ifndef NO_MASTODON_API
5 5
@@ -1171,6 +1171,10 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
1171 const char *content = xs_dict_get(msg, "content"); 1171 const char *content = xs_dict_get(msg, "content");
1172 const char *actor = xs_dict_get(msg, "actor"); 1172 const char *actor = xs_dict_get(msg, "actor");
1173 const xs_list *contentl = xs_dict_get(sfrl, content); 1173 const xs_list *contentl = xs_dict_get(sfrl, content);
1174
1175 if ((snac && is_muted(snac, actor)) || is_instance_blocked(actor))
1176 continue;
1177
1174 /* NOTE: idk when there are no actor, but i encountered that bug. 1178 /* NOTE: idk when there are no actor, but i encountered that bug.
1175 * Probably because of one of my previous attempts. 1179 * Probably because of one of my previous attempts.
1176 * Keeping this just in case, can remove later */ 1180 * Keeping this just in case, can remove later */
@@ -1455,6 +1459,7 @@ void credentials_get(char **body, char **ctype, int *status, snac snac)
1455 acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac.config, "published")); 1459 acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac.config, "published"));
1456 acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio")); 1460 acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio"));
1457 acct = xs_dict_append(acct, "url", snac.actor); 1461 acct = xs_dict_append(acct, "url", snac.actor);
1462 acct = xs_dict_append(acct, "uri", snac.actor);
1458 1463
1459 acct = xs_dict_append(acct, "locked", 1464 acct = xs_dict_append(acct, "locked",
1460 xs_stock(xs_is_true(xs_dict_get(snac.config, "approve_followers")) ? XSTYPE_TRUE : XSTYPE_FALSE)); 1465 xs_stock(xs_is_true(xs_dict_get(snac.config, "approve_followers")) ? XSTYPE_TRUE : XSTYPE_FALSE));
@@ -1805,6 +1810,37 @@ xs_list *mastoapi_account_lists(snac *user, const char *uid)
1805} 1810}
1806 1811
1807 1812
1813xs_list *build_childrens(const xs_dict *msg, snac *snac1) {
1814 xs_list *ret = xs_list_new();
1815 xs *children = object_children(xs_dict_get(msg, "id"));
1816 char *p = children;
1817 const xs_str *v;
1818
1819 while (xs_list_iter(&p, &v)) {
1820 xs *m2 = NULL;
1821
1822 if (valid_status(timeline_get_by_md5(snac1, v, &m2))) {
1823 if (xs_is_null(xs_dict_get(m2, "name"))) {
1824 xs *st = mastoapi_status(snac1, m2);
1825
1826 if (st) {
1827 /* childrens children */
1828 xs *childs = build_childrens(m2, snac1);
1829 ret = xs_list_append(ret, st);
1830 if (xs_list_len(childs)) {
1831 char *p2 = childs;
1832 while (xs_list_iter(&p2, &v))
1833 ret = xs_list_append(ret, v);
1834
1835 }
1836 }
1837 }
1838 }
1839 }
1840 return ret;
1841}
1842
1843
1808int mastoapi_get_handler(const xs_dict *req, const char *q_path, 1844int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1809 char **body, int *b_size, char **ctype, xs_str **link) 1845 char **body, int *b_size, char **ctype, xs_str **link)
1810{ 1846{
@@ -2612,19 +2648,33 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2612 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/ 2648 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/
2613 xs *emo = emojis(); 2649 xs *emo = emojis();
2614 xs *list = xs_list_new(); 2650 xs *list = xs_list_new();
2615 int c = 0;
2616 const xs_str *k; 2651 const xs_str *k;
2617 const xs_val *v; 2652 const xs_val *v;
2618 while(xs_dict_next(emo, &k, &v, &c)) { 2653 xs_dict_foreach(emo, k, v) {
2619 xs *current = xs_dict_new(); 2654 xs *current = xs_dict_new();
2620 if (xs_startswith(v, "https://") && xs_startswith((xs_mime_by_ext(v)), "image/")) { 2655 if ((xs_startswith(v, "https://") && xs_startswith((xs_mime_by_ext(v)), "image/")) || xs_type(v) == XSTYPE_DICT) {
2621 /* remove first and last colon */ 2656 /* remove first and last colon */
2622 xs *shortcode = xs_replace(k, ":", ""); 2657 if (xs_type(v) == XSTYPE_DICT) {
2623 current = xs_dict_append(current, "shortcode", shortcode); 2658 const char *v2;
2624 current = xs_dict_append(current, "url", v); 2659 const char *cat = k;
2625 current = xs_dict_append(current, "static_url", v); 2660 xs_dict_foreach(v, k, v2) {
2626 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE)); 2661 xs *shortcode = xs_replace(k, ":", "");
2627 list = xs_list_append(list, current); 2662 current = xs_dict_append(current, "shortcode", shortcode);
2663 current = xs_dict_append(current, "url", v2);
2664 current = xs_dict_append(current, "static_url", v2);
2665 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE));
2666 current = xs_dict_append(current, "category", cat);
2667 list = xs_list_append(list, current);
2668 }
2669 }
2670 else {
2671 xs *shortcode = xs_replace(k, ":", "");
2672 current = xs_dict_append(current, "shortcode", shortcode);
2673 current = xs_dict_append(current, "url", v);
2674 current = xs_dict_append(current, "static_url", v);
2675 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE));
2676 list = xs_list_append(list, current);
2677 }
2628 } 2678 }
2629 } 2679 }
2630 *body = xs_json_dumps(list, 0); 2680 *body = xs_json_dumps(list, 0);
@@ -2817,8 +2867,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2817 /* return ancestors and children */ 2867 /* return ancestors and children */
2818 xs *anc = xs_list_new(); 2868 xs *anc = xs_list_new();
2819 xs *des = xs_list_new(); 2869 xs *des = xs_list_new();
2820 xs_list *p;
2821 const xs_str *v;
2822 char pid[MD5_HEX_SIZE]; 2870 char pid[MD5_HEX_SIZE];
2823 2871
2824 /* build the [grand]parent list, moving up */ 2872 /* build the [grand]parent list, moving up */
@@ -2838,21 +2886,9 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2838 } 2886 }
2839 2887
2840 /* build the children list */ 2888 /* build the children list */
2841 xs *children = object_children(xs_dict_get(msg, "id")); 2889 xs *childs = build_childrens(msg, &snac1);
2842 p = children; 2890 if (xs_list_len(childs) > 0)
2843 2891 des = xs_list_cat(des, childs);
2844 while (xs_list_iter(&p, &v)) {
2845 xs *m2 = NULL;
2846
2847 if (valid_status(timeline_get_by_md5(&snac1, v, &m2))) {
2848 if (xs_is_null(xs_dict_get(m2, "name"))) {
2849 xs *st = mastoapi_status(&snac1, m2);
2850
2851 if (st)
2852 des = xs_list_append(des, st);
2853 }
2854 }
2855 }
2856 2892
2857 out = xs_dict_new(); 2893 out = xs_dict_new();
2858 out = xs_dict_append(out, "ancestors", anc); 2894 out = xs_dict_append(out, "ancestors", anc);
@@ -3204,6 +3240,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3204 app = xs_dict_append(app, "client_secret", csec); 3240 app = xs_dict_append(app, "client_secret", csec);
3205 app = xs_dict_append(app, "vapid_key", vkey); 3241 app = xs_dict_append(app, "vapid_key", vkey);
3206 app = xs_dict_append(app, "id", id); 3242 app = xs_dict_append(app, "id", id);
3243 app = xs_dict_append(app, "client_secret_expires_at", xs_stock(0));
3207 3244
3208 *body = xs_json_dumps(app, 4); 3245 *body = xs_json_dumps(app, 4);
3209 *ctype = "application/json"; 3246 *ctype = "application/json";
@@ -3223,12 +3260,16 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3223 if (strcmp(cmd, "/v1/statuses") == 0) { /** **/ 3260 if (strcmp(cmd, "/v1/statuses") == 0) { /** **/
3224 if (logged_in) { 3261 if (logged_in) {
3225 /* post a new Note */ 3262 /* post a new Note */
3226 const char *content = xs_dict_get(args, "status"); 3263 const char *content = xs_dict_get(args, "status");
3227 const char *mid = xs_dict_get(args, "in_reply_to_id"); 3264 const char *mid = xs_dict_get(args, "in_reply_to_id");
3228 const char *visibility = xs_dict_get(args, "visibility"); 3265 const char *visibility = xs_dict_get(args, "visibility");
3229 const char *summary = xs_dict_get(args, "spoiler_text"); 3266 const char *summary = xs_dict_get(args, "spoiler_text");
3230 const char *media_ids = xs_dict_get(args, "media_ids"); 3267 const char *poll_opts = xs_dict_get(args, "poll[options][]");
3231 const char *language = xs_dict_get(args, "language"); 3268 const char *poll_end_secs = xs_dict_get(args, "poll[expires_in]");
3269 const char *poll_multiple = xs_dict_get(args, "poll[multiple]");
3270 // const char *poll_hide_totals = xs_dict_get(args, "poll[hide_totals]");
3271 const char *media_ids = xs_dict_get(args, "media_ids");
3272 const char *language = xs_dict_get(args, "language");
3232 3273
3233 if (xs_is_null(media_ids)) 3274 if (xs_is_null(media_ids))
3234 media_ids = xs_dict_get(args, "media_ids[]"); 3275 media_ids = xs_dict_get(args, "media_ids[]");
@@ -3251,8 +3292,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3251 irt = xs_dup(xs_dict_get(r_msg, "id")); 3292 irt = xs_dup(xs_dict_get(r_msg, "id"));
3252 } 3293 }
3253 3294
3254 /* does it have attachments? */ 3295 /* does it have attachments (and no poll)? */
3255 if (!xs_is_null(media_ids)) { 3296 if (!xs_is_null(media_ids) && xs_is_null(poll_end_secs) && xs_is_null(poll_opts)) {
3256 xs *mi = NULL; 3297 xs *mi = NULL;
3257 3298
3258 if (xs_type(media_ids) == XSTYPE_LIST) 3299 if (xs_type(media_ids) == XSTYPE_LIST)
@@ -3278,6 +3319,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3278 } 3319 }
3279 3320
3280 /* prepare the message */ 3321 /* prepare the message */
3322 xs *msg = NULL;
3323
3281 int scope = SCOPE_MENTIONED; 3324 int scope = SCOPE_MENTIONED;
3282 if (strcmp(visibility, "unlisted") == 0) 3325 if (strcmp(visibility, "unlisted") == 0)
3283 scope = SCOPE_UNLISTED; 3326 scope = SCOPE_UNLISTED;
@@ -3288,7 +3331,29 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3288 if (strcmp(visibility, "private") == 0) 3331 if (strcmp(visibility, "private") == 0)
3289 scope = SCOPE_FOLLOWERS; 3332 scope = SCOPE_FOLLOWERS;
3290 3333
3291 xs *msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language, NULL); 3334 /* does it have a poll? */
3335 if (!xs_is_null(poll_opts) && !xs_is_null(poll_end_secs)) {
3336 xs *po = NULL;
3337 int end_secs = atoi(poll_end_secs);
3338 int multiple = 0;
3339
3340 if (xs_type(poll_opts) == XSTYPE_LIST)
3341 po = xs_dup(poll_opts);
3342 else {
3343 po = xs_list_new();
3344 po = xs_list_append(po, poll_opts);
3345 }
3346
3347 if (!xs_is_null(poll_multiple) && strcmp(poll_multiple, "true") == 0)
3348 multiple = 1;
3349
3350 msg = msg_question(&snac, content, attach_list,
3351 poll_opts, multiple, end_secs);
3352
3353 enqueue_close_question(&snac, xs_dict_get(msg, "id"), end_secs);
3354 }
3355 else
3356 msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language, NULL);
3292 3357
3293 if (!xs_is_null(summary) && *summary) { 3358 if (!xs_is_null(summary) && *summary) {
3294 msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); 3359 msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE));
@@ -3650,44 +3715,65 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3650 /* skip the 'fake' part of the id */ 3715 /* skip the 'fake' part of the id */
3651 mid = MID_TO_MD5(mid); 3716 mid = MID_TO_MD5(mid);
3652 3717
3653 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) { 3718 /* try timeline first, then global object store for remote posts */
3719 int found = valid_status(timeline_get_by_md5(&snac, mid, &msg));
3720 if (!found)
3721 found = valid_status(object_get_by_md5(mid, &msg));
3722
3723 if (found) {
3654 const char *id = xs_dict_get(msg, "id"); 3724 const char *id = xs_dict_get(msg, "id");
3655 const char *atto = get_atto(msg); 3725 const char *atto = get_atto(msg);
3726 int closed = 0;
3727 const char *f_closed = NULL;
3656 3728
3657 const xs_list *opts = xs_dict_get(msg, "oneOf"); 3729 if ((f_closed = xs_dict_get(msg, "closed")) != NULL) {
3658 if (opts == NULL) 3730 /* it has a closed date... but is it in the past? */
3659 opts = xs_dict_get(msg, "anyOf"); 3731 time_t t0 = time(NULL);
3732 time_t t1 = xs_parse_iso_date(f_closed, 0);
3660 3733
3661 if (op == NULL) { 3734 if (t1 < t0)
3735 closed = 1;
3662 } 3736 }
3663 else
3664 if (strcmp(op, "votes") == 0) {
3665 const xs_list *choices = xs_dict_get(args, "choices[]");
3666 3737
3667 if (xs_is_null(choices)) 3738 if (closed || was_question_voted(&snac, id))
3668 choices = xs_dict_get(args, "choices"); 3739 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
3740 else {
3741 const xs_list *opts = xs_dict_get(msg, "oneOf");
3742 if (opts == NULL)
3743 opts = xs_dict_get(msg, "anyOf");
3744
3745 if (op == NULL) {
3746 }
3747 else {
3748 if (strcmp(op, "votes") == 0) {
3749 const xs_list *choices = xs_dict_get(args, "choices[]");
3669 3750
3670 if (xs_type(choices) == XSTYPE_LIST) { 3751 if (xs_is_null(choices))
3671 const xs_str *v; 3752 choices = xs_dict_get(args, "choices");
3672 3753
3673 int c = 0; 3754 if (xs_type(choices) == XSTYPE_LIST) {
3674 while (xs_list_next(choices, &v, &c)) { 3755 const xs_str *v;
3675 int io = atoi(v);
3676 const xs_dict *o = xs_list_get(opts, io);
3677 3756
3678 if (o) { 3757 int c = 0;
3679 const char *name = xs_dict_get(o, "name"); 3758 while (xs_list_next(choices, &v, &c)) {
3759 int io = atoi(v);
3760 const xs_dict *o = xs_list_get(opts, io);
3680 3761
3681 xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL, NULL); 3762 if (o) {
3682 msg = xs_dict_append(msg, "name", name); 3763 const char *name = xs_dict_get(o, "name");
3683 3764
3684 xs *c_msg = msg_create(&snac, msg); 3765 xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL, NULL);
3685 enqueue_message(&snac, c_msg); 3766 msg = xs_dict_append(msg, "name", name);
3686 timeline_add(&snac, xs_dict_get(msg, "id"), msg); 3767
3768 xs *c_msg = msg_create(&snac, msg);
3769 enqueue_message(&snac, c_msg);
3770 timeline_add(&snac, xs_dict_get(msg, "id"), msg);
3771 }
3772 }
3773
3774 out = mastoapi_poll(&snac, msg);
3687 } 3775 }
3688 } 3776 }
3689
3690 out = mastoapi_poll(&snac, msg);
3691 } 3777 }
3692 } 3778 }
3693 } 3779 }