diff options
Diffstat (limited to 'mastoapi.c')
| -rw-r--r-- | mastoapi.c | 202 |
1 files changed, 142 insertions, 60 deletions
| @@ -1459,6 +1459,7 @@ void credentials_get(char **body, char **ctype, int *status, snac snac) | |||
| 1459 | 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")); |
| 1460 | 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")); |
| 1461 | 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); | ||
| 1462 | 1463 | ||
| 1463 | acct = xs_dict_append(acct, "locked", | 1464 | acct = xs_dict_append(acct, "locked", |
| 1464 | 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)); |
| @@ -1809,6 +1810,37 @@ xs_list *mastoapi_account_lists(snac *user, const char *uid) | |||
| 1809 | } | 1810 | } |
| 1810 | 1811 | ||
| 1811 | 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 | |||
| 1812 | 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, |
| 1813 | char **body, int *b_size, char **ctype, xs_str **link) | 1845 | char **body, int *b_size, char **ctype, xs_str **link) |
| 1814 | { | 1846 | { |
| @@ -2616,19 +2648,33 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2616 | if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/ | 2648 | if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/ |
| 2617 | xs *emo = emojis(); | 2649 | xs *emo = emojis(); |
| 2618 | xs *list = xs_list_new(); | 2650 | xs *list = xs_list_new(); |
| 2619 | int c = 0; | ||
| 2620 | const xs_str *k; | 2651 | const xs_str *k; |
| 2621 | const xs_val *v; | 2652 | const xs_val *v; |
| 2622 | while(xs_dict_next(emo, &k, &v, &c)) { | 2653 | xs_dict_foreach(emo, k, v) { |
| 2623 | xs *current = xs_dict_new(); | 2654 | xs *current = xs_dict_new(); |
| 2624 | 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) { |
| 2625 | /* remove first and last colon */ | 2656 | /* remove first and last colon */ |
| 2626 | xs *shortcode = xs_replace(k, ":", ""); | 2657 | if (xs_type(v) == XSTYPE_DICT) { |
| 2627 | current = xs_dict_append(current, "shortcode", shortcode); | 2658 | const char *v2; |
| 2628 | current = xs_dict_append(current, "url", v); | 2659 | const char *cat = k; |
| 2629 | current = xs_dict_append(current, "static_url", v); | 2660 | xs_dict_foreach(v, k, v2) { |
| 2630 | current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE)); | 2661 | xs *shortcode = xs_replace(k, ":", ""); |
| 2631 | 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 | } | ||
| 2632 | } | 2678 | } |
| 2633 | } | 2679 | } |
| 2634 | *body = xs_json_dumps(list, 0); | 2680 | *body = xs_json_dumps(list, 0); |
| @@ -2821,8 +2867,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2821 | /* return ancestors and children */ | 2867 | /* return ancestors and children */ |
| 2822 | xs *anc = xs_list_new(); | 2868 | xs *anc = xs_list_new(); |
| 2823 | xs *des = xs_list_new(); | 2869 | xs *des = xs_list_new(); |
| 2824 | xs_list *p; | ||
| 2825 | const xs_str *v; | ||
| 2826 | char pid[MD5_HEX_SIZE]; | 2870 | char pid[MD5_HEX_SIZE]; |
| 2827 | 2871 | ||
| 2828 | /* build the [grand]parent list, moving up */ | 2872 | /* build the [grand]parent list, moving up */ |
| @@ -2842,21 +2886,9 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2842 | } | 2886 | } |
| 2843 | 2887 | ||
| 2844 | /* build the children list */ | 2888 | /* build the children list */ |
| 2845 | xs *children = object_children(xs_dict_get(msg, "id")); | 2889 | xs *childs = build_childrens(msg, &snac1); |
| 2846 | p = children; | 2890 | if (xs_list_len(childs) > 0) |
| 2847 | 2891 | des = xs_list_cat(des, childs); | |
| 2848 | while (xs_list_iter(&p, &v)) { | ||
| 2849 | xs *m2 = NULL; | ||
| 2850 | |||
| 2851 | if (valid_status(timeline_get_by_md5(&snac1, v, &m2))) { | ||
| 2852 | if (xs_is_null(xs_dict_get(m2, "name"))) { | ||
| 2853 | xs *st = mastoapi_status(&snac1, m2); | ||
| 2854 | |||
| 2855 | if (st) | ||
| 2856 | des = xs_list_append(des, st); | ||
| 2857 | } | ||
| 2858 | } | ||
| 2859 | } | ||
| 2860 | 2892 | ||
| 2861 | out = xs_dict_new(); | 2893 | out = xs_dict_new(); |
| 2862 | out = xs_dict_append(out, "ancestors", anc); | 2894 | out = xs_dict_append(out, "ancestors", anc); |
| @@ -3208,6 +3240,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3208 | app = xs_dict_append(app, "client_secret", csec); | 3240 | app = xs_dict_append(app, "client_secret", csec); |
| 3209 | app = xs_dict_append(app, "vapid_key", vkey); | 3241 | app = xs_dict_append(app, "vapid_key", vkey); |
| 3210 | 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)); | ||
| 3211 | 3244 | ||
| 3212 | *body = xs_json_dumps(app, 4); | 3245 | *body = xs_json_dumps(app, 4); |
| 3213 | *ctype = "application/json"; | 3246 | *ctype = "application/json"; |
| @@ -3227,12 +3260,16 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3227 | if (strcmp(cmd, "/v1/statuses") == 0) { /** **/ | 3260 | if (strcmp(cmd, "/v1/statuses") == 0) { /** **/ |
| 3228 | if (logged_in) { | 3261 | if (logged_in) { |
| 3229 | /* post a new Note */ | 3262 | /* post a new Note */ |
| 3230 | const char *content = xs_dict_get(args, "status"); | 3263 | const char *content = xs_dict_get(args, "status"); |
| 3231 | const char *mid = xs_dict_get(args, "in_reply_to_id"); | 3264 | const char *mid = xs_dict_get(args, "in_reply_to_id"); |
| 3232 | const char *visibility = xs_dict_get(args, "visibility"); | 3265 | const char *visibility = xs_dict_get(args, "visibility"); |
| 3233 | const char *summary = xs_dict_get(args, "spoiler_text"); | 3266 | const char *summary = xs_dict_get(args, "spoiler_text"); |
| 3234 | const char *media_ids = xs_dict_get(args, "media_ids"); | 3267 | const char *poll_opts = xs_dict_get(args, "poll[options][]"); |
| 3235 | 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"); | ||
| 3236 | 3273 | ||
| 3237 | if (xs_is_null(media_ids)) | 3274 | if (xs_is_null(media_ids)) |
| 3238 | media_ids = xs_dict_get(args, "media_ids[]"); | 3275 | media_ids = xs_dict_get(args, "media_ids[]"); |
| @@ -3255,8 +3292,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3255 | irt = xs_dup(xs_dict_get(r_msg, "id")); | 3292 | irt = xs_dup(xs_dict_get(r_msg, "id")); |
| 3256 | } | 3293 | } |
| 3257 | 3294 | ||
| 3258 | /* does it have attachments? */ | 3295 | /* does it have attachments (and no poll)? */ |
| 3259 | if (!xs_is_null(media_ids)) { | 3296 | if (!xs_is_null(media_ids) && xs_is_null(poll_end_secs) && xs_is_null(poll_opts)) { |
| 3260 | xs *mi = NULL; | 3297 | xs *mi = NULL; |
| 3261 | 3298 | ||
| 3262 | if (xs_type(media_ids) == XSTYPE_LIST) | 3299 | if (xs_type(media_ids) == XSTYPE_LIST) |
| @@ -3282,6 +3319,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3282 | } | 3319 | } |
| 3283 | 3320 | ||
| 3284 | /* prepare the message */ | 3321 | /* prepare the message */ |
| 3322 | xs *msg = NULL; | ||
| 3323 | |||
| 3285 | int scope = SCOPE_MENTIONED; | 3324 | int scope = SCOPE_MENTIONED; |
| 3286 | if (strcmp(visibility, "unlisted") == 0) | 3325 | if (strcmp(visibility, "unlisted") == 0) |
| 3287 | scope = SCOPE_UNLISTED; | 3326 | scope = SCOPE_UNLISTED; |
| @@ -3292,7 +3331,29 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3292 | if (strcmp(visibility, "private") == 0) | 3331 | if (strcmp(visibility, "private") == 0) |
| 3293 | scope = SCOPE_FOLLOWERS; | 3332 | scope = SCOPE_FOLLOWERS; |
| 3294 | 3333 | ||
| 3295 | 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); | ||
| 3296 | 3357 | ||
| 3297 | if (!xs_is_null(summary) && *summary) { | 3358 | if (!xs_is_null(summary) && *summary) { |
| 3298 | msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); | 3359 | msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); |
| @@ -3654,44 +3715,65 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3654 | /* skip the 'fake' part of the id */ | 3715 | /* skip the 'fake' part of the id */ |
| 3655 | mid = MID_TO_MD5(mid); | 3716 | mid = MID_TO_MD5(mid); |
| 3656 | 3717 | ||
| 3657 | 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) { | ||
| 3658 | const char *id = xs_dict_get(msg, "id"); | 3724 | const char *id = xs_dict_get(msg, "id"); |
| 3659 | const char *atto = get_atto(msg); | 3725 | const char *atto = get_atto(msg); |
| 3726 | int closed = 0; | ||
| 3727 | const char *f_closed = NULL; | ||
| 3660 | 3728 | ||
| 3661 | const xs_list *opts = xs_dict_get(msg, "oneOf"); | 3729 | if ((f_closed = xs_dict_get(msg, "closed")) != NULL) { |
| 3662 | if (opts == NULL) | 3730 | /* it has a closed date... but is it in the past? */ |
| 3663 | opts = xs_dict_get(msg, "anyOf"); | 3731 | time_t t0 = time(NULL); |
| 3732 | time_t t1 = xs_parse_iso_date(f_closed, 0); | ||
| 3664 | 3733 | ||
| 3665 | if (op == NULL) { | 3734 | if (t1 < t0) |
| 3735 | closed = 1; | ||
| 3666 | } | 3736 | } |
| 3667 | else | ||
| 3668 | if (strcmp(op, "votes") == 0) { | ||
| 3669 | const xs_list *choices = xs_dict_get(args, "choices[]"); | ||
| 3670 | 3737 | ||
| 3671 | if (xs_is_null(choices)) | 3738 | if (closed || was_question_voted(&snac, id)) |
| 3672 | 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"); | ||
| 3673 | 3744 | ||
| 3674 | if (xs_type(choices) == XSTYPE_LIST) { | 3745 | if (op == NULL) { |
| 3675 | const xs_str *v; | 3746 | } |
| 3747 | else { | ||
| 3748 | if (strcmp(op, "votes") == 0) { | ||
| 3749 | const xs_list *choices = xs_dict_get(args, "choices[]"); | ||
| 3676 | 3750 | ||
| 3677 | int c = 0; | 3751 | if (xs_is_null(choices)) |
| 3678 | while (xs_list_next(choices, &v, &c)) { | 3752 | choices = xs_dict_get(args, "choices"); |
| 3679 | int io = atoi(v); | ||
| 3680 | const xs_dict *o = xs_list_get(opts, io); | ||
| 3681 | 3753 | ||
| 3682 | if (o) { | 3754 | if (xs_type(choices) == XSTYPE_LIST) { |
| 3683 | const char *name = xs_dict_get(o, "name"); | 3755 | const xs_str *v; |
| 3684 | 3756 | ||
| 3685 | xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL, NULL); | 3757 | int c = 0; |
| 3686 | msg = xs_dict_append(msg, "name", name); | 3758 | while (xs_list_next(choices, &v, &c)) { |
| 3759 | int io = atoi(v); | ||
| 3760 | const xs_dict *o = xs_list_get(opts, io); | ||
| 3687 | 3761 | ||
| 3688 | xs *c_msg = msg_create(&snac, msg); | 3762 | if (o) { |
| 3689 | enqueue_message(&snac, c_msg); | 3763 | const char *name = xs_dict_get(o, "name"); |
| 3690 | timeline_add(&snac, xs_dict_get(msg, "id"), msg); | 3764 | |
| 3765 | xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL, NULL); | ||
| 3766 | msg = xs_dict_append(msg, "name", name); | ||
| 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); | ||
| 3691 | } | 3775 | } |
| 3692 | } | 3776 | } |
| 3693 | |||
| 3694 | out = mastoapi_poll(&snac, msg); | ||
| 3695 | } | 3777 | } |
| 3696 | } | 3778 | } |
| 3697 | } | 3779 | } |