summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c220
1 files changed, 178 insertions, 42 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 8d61681..a7d9c34 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -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
1506xs_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
1503int mastoapi_get_handler(const xs_dict *req, const char *q_path, 1544int 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 */