diff options
Diffstat (limited to 'mastoapi.c')
| -rw-r--r-- | mastoapi.c | 208 |
1 files changed, 147 insertions, 61 deletions
| @@ -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 | ||
| 1813 | xs_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 | |||
| 1808 | int mastoapi_get_handler(const xs_dict *req, const char *q_path, | 1844 | int 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 | } |