diff options
Diffstat (limited to 'mastoapi.c')
| -rw-r--r-- | mastoapi.c | 220 |
1 files changed, 178 insertions, 42 deletions
| @@ -680,10 +680,10 @@ xs_dict *mastoapi_account(snac *logged, const xs_dict *actor) | |||
| 680 | 680 | ||
| 681 | /* does this user want to publish their contact metrics? */ | 681 | /* does this user want to publish their contact metrics? */ |
| 682 | if (xs_is_true(xs_dict_get(user.config, "show_contact_metrics"))) { | 682 | if (xs_is_true(xs_dict_get(user.config, "show_contact_metrics"))) { |
| 683 | xs *fwing = following_list(&user); | 683 | int fwing = following_list_len(&user); |
| 684 | xs *fwers = follower_list(&user); | 684 | int fwers = follower_list_len(&user); |
| 685 | xs *ni = xs_number_new(xs_list_len(fwing)); | 685 | xs *ni = xs_number_new(fwing); |
| 686 | xs *ne = xs_number_new(xs_list_len(fwers)); | 686 | xs *ne = xs_number_new(fwers); |
| 687 | 687 | ||
| 688 | acct = xs_dict_append(acct, "followers_count", ne); | 688 | acct = xs_dict_append(acct, "followers_count", ne); |
| 689 | acct = xs_dict_append(acct, "following_count", ni); | 689 | acct = xs_dict_append(acct, "following_count", ni); |
| @@ -1223,7 +1223,10 @@ void credentials_get(char **body, char **ctype, int *status, snac snac) | |||
| 1223 | acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac.config, "published")); | 1223 | acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac.config, "published")); |
| 1224 | acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio")); | 1224 | acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio")); |
| 1225 | acct = xs_dict_append(acct, "url", snac.actor); | 1225 | acct = xs_dict_append(acct, "url", snac.actor); |
| 1226 | acct = xs_dict_append(acct, "locked", xs_stock(XSTYPE_FALSE)); | 1226 | |
| 1227 | acct = xs_dict_append(acct, "locked", | ||
| 1228 | xs_stock(xs_is_true(xs_dict_get(snac.config, "approve_followers")) ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 1229 | |||
| 1227 | acct = xs_dict_append(acct, "bot", xs_stock(xs_is_true(bot) ? XSTYPE_TRUE : XSTYPE_FALSE)); | 1230 | acct = xs_dict_append(acct, "bot", xs_stock(xs_is_true(bot) ? XSTYPE_TRUE : XSTYPE_FALSE)); |
| 1228 | acct = xs_dict_append(acct, "emojis", xs_stock(XSTYPE_LIST)); | 1231 | acct = xs_dict_append(acct, "emojis", xs_stock(XSTYPE_LIST)); |
| 1229 | 1232 | ||
| @@ -1306,10 +1309,10 @@ void credentials_get(char **body, char **ctype, int *status, snac snac) | |||
| 1306 | 1309 | ||
| 1307 | /* does this user want to publish their contact metrics? */ | 1310 | /* does this user want to publish their contact metrics? */ |
| 1308 | if (xs_is_true(xs_dict_get(snac.config, "show_contact_metrics"))) { | 1311 | if (xs_is_true(xs_dict_get(snac.config, "show_contact_metrics"))) { |
| 1309 | xs *fwing = following_list(&snac); | 1312 | int fwing = following_list_len(&snac); |
| 1310 | xs *fwers = follower_list(&snac); | 1313 | int fwers = follower_list_len(&snac); |
| 1311 | xs *ni = xs_number_new(xs_list_len(fwing)); | 1314 | xs *ni = xs_number_new(fwing); |
| 1312 | xs *ne = xs_number_new(xs_list_len(fwers)); | 1315 | xs *ne = xs_number_new(fwers); |
| 1313 | 1316 | ||
| 1314 | acct = xs_dict_append(acct, "followers_count", ne); | 1317 | acct = xs_dict_append(acct, "followers_count", ne); |
| 1315 | acct = xs_dict_append(acct, "following_count", ni); | 1318 | acct = xs_dict_append(acct, "following_count", ni); |
| @@ -1500,6 +1503,44 @@ xs_str *timeline_link_header(const char *endpoint, xs_list *timeline) | |||
| 1500 | } | 1503 | } |
| 1501 | 1504 | ||
| 1502 | 1505 | ||
| 1506 | xs_list *mastoapi_account_lists(snac *user, const char *uid) | ||
| 1507 | /* returns the list of list an user is in */ | ||
| 1508 | { | ||
| 1509 | xs_list *out = xs_list_new(); | ||
| 1510 | xs *actor_md5 = NULL; | ||
| 1511 | xs *lol = list_maint(user, NULL, 0); | ||
| 1512 | |||
| 1513 | if (uid) { | ||
| 1514 | if (!xs_is_hex(uid)) | ||
| 1515 | actor_md5 = xs_md5_hex(uid, strlen(uid)); | ||
| 1516 | else | ||
| 1517 | actor_md5 = xs_dup(uid); | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | const xs_list *li; | ||
| 1521 | xs_list_foreach(lol, li) { | ||
| 1522 | const char *list_id = xs_list_get(li, 0); | ||
| 1523 | const char *list_title = xs_list_get(li, 1); | ||
| 1524 | if (uid) { | ||
| 1525 | xs *users = list_content(user, list_id, NULL, 0); | ||
| 1526 | if (xs_list_in(users, actor_md5) == -1) | ||
| 1527 | continue; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | xs *d = xs_dict_new(); | ||
| 1531 | |||
| 1532 | d = xs_dict_append(d, "id", list_id); | ||
| 1533 | d = xs_dict_append(d, "title", list_title); | ||
| 1534 | d = xs_dict_append(d, "replies_policy", "list"); | ||
| 1535 | d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE)); | ||
| 1536 | |||
| 1537 | out = xs_list_append(out, d); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | return out; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | |||
| 1503 | int mastoapi_get_handler(const xs_dict *req, const char *q_path, | 1544 | int mastoapi_get_handler(const xs_dict *req, const char *q_path, |
| 1504 | char **body, int *b_size, char **ctype, xs_str **link) | 1545 | char **body, int *b_size, char **ctype, xs_str **link) |
| 1505 | { | 1546 | { |
| @@ -1723,6 +1764,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1723 | if (strcmp(opt, "followers") == 0) { | 1764 | if (strcmp(opt, "followers") == 0) { |
| 1724 | out = xs_list_new(); | 1765 | out = xs_list_new(); |
| 1725 | } | 1766 | } |
| 1767 | else | ||
| 1768 | if (strcmp(opt, "lists") == 0) { | ||
| 1769 | out = mastoapi_account_lists(&snac1, uid); | ||
| 1770 | } | ||
| 1726 | 1771 | ||
| 1727 | user_free(&snac2); | 1772 | user_free(&snac2); |
| 1728 | } | 1773 | } |
| @@ -1744,6 +1789,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1744 | /* implement empty response so apps like Tokodon don't show an error */ | 1789 | /* implement empty response so apps like Tokodon don't show an error */ |
| 1745 | out = xs_list_new(); | 1790 | out = xs_list_new(); |
| 1746 | } | 1791 | } |
| 1792 | else | ||
| 1793 | if (strcmp(opt, "lists") == 0) { | ||
| 1794 | out = mastoapi_account_lists(&snac1, uid); | ||
| 1795 | } | ||
| 1747 | } | 1796 | } |
| 1748 | } | 1797 | } |
| 1749 | 1798 | ||
| @@ -1825,13 +1874,14 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1825 | if (logged_in) { | 1874 | if (logged_in) { |
| 1826 | xs *l = notify_list(&snac1, 0, 64); | 1875 | xs *l = notify_list(&snac1, 0, 64); |
| 1827 | xs *out = xs_list_new(); | 1876 | xs *out = xs_list_new(); |
| 1828 | const xs_dict *v; | 1877 | const char *v; |
| 1829 | const xs_list *excl = xs_dict_get(args, "exclude_types[]"); | 1878 | const xs_list *excl = xs_dict_get(args, "exclude_types[]"); |
| 1879 | const xs_list *incl = xs_dict_get(args, "types[]"); | ||
| 1830 | const char *min_id = xs_dict_get(args, "min_id"); | 1880 | const char *min_id = xs_dict_get(args, "min_id"); |
| 1831 | const char *max_id = xs_dict_get(args, "max_id"); | 1881 | const char *max_id = xs_dict_get(args, "max_id"); |
| 1832 | const char *limit = xs_dict_get(args, "limit"); | 1882 | const char *limit = xs_dict_get(args, "limit"); |
| 1833 | int limit_count = 0; | 1883 | int limit_count = 0; |
| 1834 | if (!xs_is_null(limit)) { | 1884 | if (xs_is_string(limit)) { |
| 1835 | limit_count = atoi(limit); | 1885 | limit_count = atoi(limit); |
| 1836 | } | 1886 | } |
| 1837 | 1887 | ||
| @@ -1850,11 +1900,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1850 | const char *utype = xs_dict_get(noti, "utype"); | 1900 | const char *utype = xs_dict_get(noti, "utype"); |
| 1851 | const char *objid = xs_dict_get(noti, "objid"); | 1901 | const char *objid = xs_dict_get(noti, "objid"); |
| 1852 | const char *id = xs_dict_get(noti, "id"); | 1902 | const char *id = xs_dict_get(noti, "id"); |
| 1903 | const char *actid = xs_dict_get(noti, "actor"); | ||
| 1853 | xs *fid = xs_replace(id, ".", ""); | 1904 | xs *fid = xs_replace(id, ".", ""); |
| 1854 | xs *actor = NULL; | 1905 | xs *actor = NULL; |
| 1855 | xs *entry = NULL; | 1906 | xs *entry = NULL; |
| 1856 | 1907 | ||
| 1857 | if (!valid_status(actor_get(xs_dict_get(noti, "actor"), &actor))) | 1908 | if (!valid_status(actor_get(actid, &actor))) |
| 1858 | continue; | 1909 | continue; |
| 1859 | 1910 | ||
| 1860 | if (objid != NULL && !valid_status(object_get(objid, &entry))) | 1911 | if (objid != NULL && !valid_status(object_get(objid, &entry))) |
| @@ -1895,7 +1946,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1895 | continue; | 1946 | continue; |
| 1896 | 1947 | ||
| 1897 | /* excluded type? */ | 1948 | /* excluded type? */ |
| 1898 | if (!xs_is_null(excl) && xs_list_in(excl, type) != -1) | 1949 | if (xs_is_list(excl) && xs_list_in(excl, type) != -1) |
| 1950 | continue; | ||
| 1951 | |||
| 1952 | /* included type? */ | ||
| 1953 | if (xs_is_list(incl) && xs_list_in(incl, type) == -1) | ||
| 1899 | continue; | 1954 | continue; |
| 1900 | 1955 | ||
| 1901 | xs *mn = xs_dict_new(); | 1956 | xs *mn = xs_dict_new(); |
| @@ -1921,10 +1976,9 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1921 | } | 1976 | } |
| 1922 | 1977 | ||
| 1923 | out = xs_list_append(out, mn); | 1978 | out = xs_list_append(out, mn); |
| 1924 | if (!xs_is_null(limit)) { | 1979 | |
| 1925 | if (--limit_count <= 0) | 1980 | if (--limit_count <= 0) |
| 1926 | break; | 1981 | break; |
| 1927 | } | ||
| 1928 | } | 1982 | } |
| 1929 | 1983 | ||
| 1930 | srv_debug(1, xs_fmt("mastoapi_notifications count %d", xs_list_len(out))); | 1984 | srv_debug(1, xs_fmt("mastoapi_notifications count %d", xs_list_len(out))); |
| @@ -1975,21 +2029,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1975 | else | 2029 | else |
| 1976 | if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/ | 2030 | if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/ |
| 1977 | if (logged_in) { | 2031 | if (logged_in) { |
| 1978 | xs *lol = list_maint(&snac1, NULL, 0); | 2032 | xs *l = mastoapi_account_lists(&snac1, NULL); |
| 1979 | xs *l = xs_list_new(); | ||
| 1980 | int c = 0; | ||
| 1981 | const xs_list *li; | ||
| 1982 | |||
| 1983 | while (xs_list_next(lol, &li, &c)) { | ||
| 1984 | xs *d = xs_dict_new(); | ||
| 1985 | |||
| 1986 | d = xs_dict_append(d, "id", xs_list_get(li, 0)); | ||
| 1987 | d = xs_dict_append(d, "title", xs_list_get(li, 1)); | ||
| 1988 | d = xs_dict_append(d, "replies_policy", "list"); | ||
| 1989 | d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE)); | ||
| 1990 | |||
| 1991 | l = xs_list_append(l, d); | ||
| 1992 | } | ||
| 1993 | 2033 | ||
| 1994 | *body = xs_json_dumps(l, 4); | 2034 | *body = xs_json_dumps(l, 4); |
| 1995 | *ctype = "application/json"; | 2035 | *ctype = "application/json"; |
| @@ -2069,10 +2109,26 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2069 | } | 2109 | } |
| 2070 | else | 2110 | else |
| 2071 | if (strcmp(cmd, "/v1/follow_requests") == 0) { /** **/ | 2111 | if (strcmp(cmd, "/v1/follow_requests") == 0) { /** **/ |
| 2072 | /* snac does not support optional follow confirmations */ | 2112 | if (logged_in) { |
| 2073 | *body = xs_dup("[]"); | 2113 | xs *pend = pending_list(&snac1); |
| 2074 | *ctype = "application/json"; | 2114 | xs *resp = xs_list_new(); |
| 2075 | status = HTTP_STATUS_OK; | 2115 | const char *id; |
| 2116 | |||
| 2117 | xs_list_foreach(pend, id) { | ||
| 2118 | xs *actor = NULL; | ||
| 2119 | |||
| 2120 | if (valid_status(object_get(id, &actor))) { | ||
| 2121 | xs *acct = mastoapi_account(&snac1, actor); | ||
| 2122 | |||
| 2123 | if (acct) | ||
| 2124 | resp = xs_list_append(resp, acct); | ||
| 2125 | } | ||
| 2126 | } | ||
| 2127 | |||
| 2128 | *body = xs_json_dumps(resp, 4); | ||
| 2129 | *ctype = "application/json"; | ||
| 2130 | status = HTTP_STATUS_OK; | ||
| 2131 | } | ||
| 2076 | } | 2132 | } |
| 2077 | else | 2133 | else |
| 2078 | if (strcmp(cmd, "/v1/announcements") == 0) { /** **/ | 2134 | if (strcmp(cmd, "/v1/announcements") == 0) { /** **/ |
| @@ -2256,6 +2312,25 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2256 | status = HTTP_STATUS_OK; | 2312 | status = HTTP_STATUS_OK; |
| 2257 | } | 2313 | } |
| 2258 | else | 2314 | else |
| 2315 | if (strcmp(cmd, "/v1/instance/peers") == 0) { /** **/ | ||
| 2316 | /* get the collected inbox list as the instances "this domain is aware of" */ | ||
| 2317 | xs *list = inbox_list(); | ||
| 2318 | xs *peers = xs_list_new(); | ||
| 2319 | const char *inbox; | ||
| 2320 | |||
| 2321 | xs_list_foreach(list, inbox) { | ||
| 2322 | xs *l = xs_split(inbox, "/"); | ||
| 2323 | const char *domain = xs_list_get(l, 2); | ||
| 2324 | |||
| 2325 | if (xs_is_string(domain)) | ||
| 2326 | peers = xs_list_append(peers, domain); | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | *body = xs_json_dumps(peers, 4); | ||
| 2330 | *ctype = "application/json"; | ||
| 2331 | status = HTTP_STATUS_OK; | ||
| 2332 | } | ||
| 2333 | else | ||
| 2259 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ | 2334 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ |
| 2260 | /* information about a status */ | 2335 | /* information about a status */ |
| 2261 | if (logged_in) { | 2336 | if (logged_in) { |
| @@ -2707,14 +2782,24 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 2707 | msg = xs_dict_set(msg, "summary", summary); | 2782 | msg = xs_dict_set(msg, "summary", summary); |
| 2708 | } | 2783 | } |
| 2709 | 2784 | ||
| 2710 | /* store */ | 2785 | /* scheduled? */ |
| 2711 | timeline_add(&snac, xs_dict_get(msg, "id"), msg); | 2786 | const char *scheduled_at = xs_dict_get(args, "scheduled_at"); |
| 2712 | 2787 | ||
| 2713 | /* 'Create' message */ | 2788 | if (xs_is_string(scheduled_at) && *scheduled_at) { |
| 2714 | xs *c_msg = msg_create(&snac, msg); | 2789 | msg = xs_dict_set(msg, "published", scheduled_at); |
| 2715 | enqueue_message(&snac, c_msg); | ||
| 2716 | 2790 | ||
| 2717 | timeline_touch(&snac); | 2791 | schedule_add(&snac, xs_dict_get(msg, "id"), msg); |
| 2792 | } | ||
| 2793 | else { | ||
| 2794 | /* store */ | ||
| 2795 | timeline_add(&snac, xs_dict_get(msg, "id"), msg); | ||
| 2796 | |||
| 2797 | /* 'Create' message */ | ||
| 2798 | xs *c_msg = msg_create(&snac, msg); | ||
| 2799 | enqueue_message(&snac, c_msg); | ||
| 2800 | |||
| 2801 | timeline_touch(&snac); | ||
| 2802 | } | ||
| 2718 | 2803 | ||
| 2719 | /* convert to a mastodon status as a response code */ | 2804 | /* convert to a mastodon status as a response code */ |
| 2720 | xs *st = mastoapi_status(&snac, msg); | 2805 | xs *st = mastoapi_status(&snac, msg); |
| @@ -3160,6 +3245,57 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3160 | status = HTTP_STATUS_OK; | 3245 | status = HTTP_STATUS_OK; |
| 3161 | } | 3246 | } |
| 3162 | else | 3247 | else |
| 3248 | if (xs_startswith(cmd, "/v1/follow_requests")) { /** **/ | ||
| 3249 | if (logged_in) { | ||
| 3250 | /* "authorize" or "reject" */ | ||
| 3251 | xs *rel = NULL; | ||
| 3252 | xs *l = xs_split(cmd, "/"); | ||
| 3253 | const char *md5 = xs_list_get(l, -2); | ||
| 3254 | const char *s_cmd = xs_list_get(l, -1); | ||
| 3255 | |||
| 3256 | if (xs_is_string(md5) && xs_is_string(s_cmd)) { | ||
| 3257 | xs *actor = NULL; | ||
| 3258 | |||
| 3259 | if (valid_status(object_get_by_md5(md5, &actor))) { | ||
| 3260 | const char *actor_id = xs_dict_get(actor, "id"); | ||
| 3261 | |||
| 3262 | if (strcmp(s_cmd, "authorize") == 0) { | ||
| 3263 | xs *fwreq = pending_get(&snac, actor_id); | ||
| 3264 | |||
| 3265 | if (fwreq != NULL) { | ||
| 3266 | xs *reply = msg_accept(&snac, fwreq, actor_id); | ||
| 3267 | |||
| 3268 | enqueue_message(&snac, reply); | ||
| 3269 | |||
| 3270 | if (xs_is_null(xs_dict_get(fwreq, "published"))) { | ||
| 3271 | xs *date = xs_str_utctime(0, ISO_DATE_SPEC); | ||
| 3272 | fwreq = xs_dict_set(fwreq, "published", date); | ||
| 3273 | } | ||
| 3274 | |||
| 3275 | timeline_add(&snac, xs_dict_get(fwreq, "id"), fwreq); | ||
| 3276 | |||
| 3277 | follower_add(&snac, actor_id); | ||
| 3278 | |||
| 3279 | pending_del(&snac, actor_id); | ||
| 3280 | rel = mastoapi_relationship(&snac, md5); | ||
| 3281 | } | ||
| 3282 | } | ||
| 3283 | else | ||
| 3284 | if (strcmp(s_cmd, "reject") == 0) { | ||
| 3285 | pending_del(&snac, actor_id); | ||
| 3286 | rel = mastoapi_relationship(&snac, md5); | ||
| 3287 | } | ||
| 3288 | } | ||
| 3289 | } | ||
| 3290 | |||
| 3291 | if (rel != NULL) { | ||
| 3292 | *body = xs_json_dumps(rel, 4); | ||
| 3293 | *ctype = "application/json"; | ||
| 3294 | status = HTTP_STATUS_OK; | ||
| 3295 | } | ||
| 3296 | } | ||
| 3297 | } | ||
| 3298 | else | ||
| 3163 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; | 3299 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; |
| 3164 | 3300 | ||
| 3165 | /* user cleanup */ | 3301 | /* user cleanup */ |