summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
authorGravatar Louis Brauer2024-05-25 08:05:36 +0000
committerGravatar Louis Brauer2024-05-25 08:05:36 +0000
commit84a767dd0878013194ed7551b5ae6ef715e841a6 (patch)
tree9fb1b2b89e0bfbb4b8bf1e85d840c8653e646bb7 /mastoapi.c
parentPrevent some browsers from caching servers basic auth request (diff)
parentBackport from xs (fix regex.h compilation with tcc). (diff)
downloadsnac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.gz
snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.xz
snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.zip
Merge pull request 'master' (#1) from grunfink/snac2:master into master
Reviewed-on: https://codeberg.org/louis77/snac2/pulls/1
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c495
1 files changed, 404 insertions, 91 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 4d80f69..3936c2a 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -156,7 +156,7 @@ const char *login_page = ""
156"</head>\n" 156"</head>\n"
157"<body><h1>%s OAuth identify</h1>\n" 157"<body><h1>%s OAuth identify</h1>\n"
158"<div style=\"background-color: red; color: white\">%s</div>\n" 158"<div style=\"background-color: red; color: white\">%s</div>\n"
159"<form method=\"post\" action=\"https:/" "/%s/%s\">\n" 159"<form method=\"post\" action=\"%s:/" "/%s/%s\">\n"
160"<p>Login: <input type=\"text\" name=\"login\"></p>\n" 160"<p>Login: <input type=\"text\" name=\"login\"></p>\n"
161"<p>Password: <input type=\"password\" name=\"passwd\"></p>\n" 161"<p>Password: <input type=\"password\" name=\"passwd\"></p>\n"
162"<input type=\"hidden\" name=\"redir\" value=\"%s\">\n" 162"<input type=\"hidden\" name=\"redir\" value=\"%s\">\n"
@@ -175,7 +175,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
175 return 0; 175 return 0;
176 176
177 int status = 404; 177 int status = 404;
178 xs_dict *msg = xs_dict_get(req, "q_vars"); 178 const xs_dict *msg = xs_dict_get(req, "q_vars");
179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); 179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
180 180
181 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path)); 181 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path));
@@ -193,11 +193,12 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
193 193
194 if (app != NULL) { 194 if (app != NULL) {
195 const char *host = xs_dict_get(srv_config, "host"); 195 const char *host = xs_dict_get(srv_config, "host");
196 const char *proto = xs_dict_get_def(srv_config, "protocol", "https");
196 197
197 if (xs_is_null(state)) 198 if (xs_is_null(state))
198 state = ""; 199 state = "";
199 200
200 *body = xs_fmt(login_page, host, host, "", host, "oauth/x-snac-login", 201 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-login",
201 ruri, cid, state, USER_AGENT); 202 ruri, cid, state, USER_AGENT);
202 *ctype = "text/html"; 203 *ctype = "text/html";
203 status = 200; 204 status = 200;
@@ -213,8 +214,9 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
213 else 214 else
214 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/ 215 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/
215 const char *host = xs_dict_get(srv_config, "host"); 216 const char *host = xs_dict_get(srv_config, "host");
217 const char *proto = xs_dict_get_def(srv_config, "protocol", "https");
216 218
217 *body = xs_fmt(login_page, host, host, "", host, "oauth/x-snac-get-token", 219 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-get-token",
218 "", "", "", USER_AGENT); 220 "", "", "", USER_AGENT);
219 *ctype = "text/html"; 221 *ctype = "text/html";
220 status = 200; 222 status = 200;
@@ -237,7 +239,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
237 239
238 int status = 404; 240 int status = 404;
239 241
240 char *i_ctype = xs_dict_get(req, "content-type"); 242 const char *i_ctype = xs_dict_get(req, "content-type");
241 xs *args = NULL; 243 xs *args = NULL;
242 244
243 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 245 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
@@ -265,11 +267,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
265 const char *redir = xs_dict_get(args, "redir"); 267 const char *redir = xs_dict_get(args, "redir");
266 const char *cid = xs_dict_get(args, "cid"); 268 const char *cid = xs_dict_get(args, "cid");
267 const char *state = xs_dict_get(args, "state"); 269 const char *state = xs_dict_get(args, "state");
268 270 const char *host = xs_dict_get(srv_config, "host");
269 const char *host = xs_dict_get(srv_config, "host"); 271 const char *proto = xs_dict_get_def(srv_config, "protocol", "https");
270 272
271 /* by default, generate another login form with an error */ 273 /* by default, generate another login form with an error */
272 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", host, "oauth/x-snac-login", 274 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-login",
273 redir, cid, state, USER_AGENT); 275 redir, cid, state, USER_AGENT);
274 *ctype = "text/html"; 276 *ctype = "text/html";
275 status = 200; 277 status = 200;
@@ -289,7 +291,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
289 *body = xs_dup(code); 291 *body = xs_dup(code);
290 } 292 }
291 else { 293 else {
292 *body = xs_fmt("%s?code=%s", redir, code); 294 if (xs_str_in(redir, "?") != -1)
295 *body = xs_fmt("%s&code=%s", redir, code);
296 else
297 *body = xs_fmt("%s?code=%s", redir, code);
298
293 status = 303; 299 status = 303;
294 } 300 }
295 301
@@ -446,11 +452,11 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
446 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/ 452 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/
447 const char *login = xs_dict_get(args, "login"); 453 const char *login = xs_dict_get(args, "login");
448 const char *passwd = xs_dict_get(args, "passwd"); 454 const char *passwd = xs_dict_get(args, "passwd");
449 455 const char *host = xs_dict_get(srv_config, "host");
450 const char *host = xs_dict_get(srv_config, "host"); 456 const char *proto = xs_dict_get_def(srv_config, "protocol", "https");
451 457
452 /* by default, generate another login form with an error */ 458 /* by default, generate another login form with an error */
453 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", host, "oauth/x-snac-get-token", 459 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-get-token",
454 "", "", "", USER_AGENT); 460 "", "", "", USER_AGENT);
455 *ctype = "text/html"; 461 *ctype = "text/html";
456 status = 200; 462 status = 200;
@@ -544,6 +550,9 @@ xs_dict *mastoapi_account(const xs_dict *actor)
544 acct = xs_dict_append(acct, "created_at", date); 550 acct = xs_dict_append(acct, "created_at", date);
545 } 551 }
546 552
553 xs *last_status_at = xs_str_utctime(0, "%Y-%m-%d");
554 acct = xs_dict_append(acct, "last_status_at", last_status_at);
555
547 const char *note = xs_dict_get(actor, "summary"); 556 const char *note = xs_dict_get(actor, "summary");
548 if (xs_is_null(note)) 557 if (xs_is_null(note))
549 note = ""; 558 note = "";
@@ -559,10 +568,10 @@ xs_dict *mastoapi_account(const xs_dict *actor)
559 acct = xs_dict_append(acct, "uri", id); 568 acct = xs_dict_append(acct, "uri", id);
560 569
561 xs *avatar = NULL; 570 xs *avatar = NULL;
562 xs_dict *av = xs_dict_get(actor, "icon"); 571 const xs_dict *av = xs_dict_get(actor, "icon");
563 572
564 if (xs_type(av) == XSTYPE_DICT) { 573 if (xs_type(av) == XSTYPE_DICT) {
565 char *url = xs_dict_get(av, "url"); 574 const char *url = xs_dict_get(av, "url");
566 575
567 if (url != NULL) 576 if (url != NULL)
568 avatar = xs_dup(url); 577 avatar = xs_dup(url);
@@ -575,7 +584,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
575 acct = xs_dict_append(acct, "avatar_static", avatar); 584 acct = xs_dict_append(acct, "avatar_static", avatar);
576 585
577 xs *header = NULL; 586 xs *header = NULL;
578 xs_dict *hd = xs_dict_get(actor, "image"); 587 const xs_dict *hd = xs_dict_get(actor, "image");
579 588
580 if (xs_type(hd) == XSTYPE_DICT) 589 if (xs_type(hd) == XSTYPE_DICT)
581 header = xs_dup(xs_dict_get(hd, "url")); 590 header = xs_dup(xs_dict_get(hd, "url"));
@@ -587,12 +596,13 @@ xs_dict *mastoapi_account(const xs_dict *actor)
587 acct = xs_dict_append(acct, "header_static", header); 596 acct = xs_dict_append(acct, "header_static", header);
588 597
589 /* emojis */ 598 /* emojis */
590 xs_list *p; 599 const xs_list *p;
591 if (!xs_is_null(p = xs_dict_get(actor, "tag"))) { 600 if (!xs_is_null(p = xs_dict_get(actor, "tag"))) {
592 xs *eml = xs_list_new(); 601 xs *eml = xs_list_new();
593 xs_dict *v; 602 const xs_dict *v;
603 int c = 0;
594 604
595 while (xs_list_iter(&p, &v)) { 605 while (xs_list_next(p, &v, &c)) {
596 const char *type = xs_dict_get(v, "type"); 606 const char *type = xs_dict_get(v, "type");
597 607
598 if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) { 608 if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) {
@@ -627,11 +637,11 @@ xs_dict *mastoapi_account(const xs_dict *actor)
627 637
628 xs *fields = xs_list_new(); 638 xs *fields = xs_list_new();
629 p = xs_dict_get(actor, "attachment"); 639 p = xs_dict_get(actor, "attachment");
630 xs_dict *v; 640 const xs_dict *v;
631 641
632 /* dict of validated links */ 642 /* dict of validated links */
633 xs_dict *val_links = NULL; 643 xs_dict *val_links = NULL;
634 xs_dict *metadata = xs_stock(XSTYPE_DICT); 644 const xs_dict *metadata = xs_stock(XSTYPE_DICT);
635 snac user = {0}; 645 snac user = {0};
636 646
637 if (xs_startswith(id, srv_baseurl)) { 647 if (xs_startswith(id, srv_baseurl)) {
@@ -645,19 +655,20 @@ xs_dict *mastoapi_account(const xs_dict *actor)
645 if (xs_is_null(val_links)) 655 if (xs_is_null(val_links))
646 val_links = xs_stock(XSTYPE_DICT); 656 val_links = xs_stock(XSTYPE_DICT);
647 657
648 while (xs_list_iter(&p, &v)) { 658 int c = 0;
649 char *type = xs_dict_get(v, "type"); 659 while (xs_list_next(p, &v, &c)) {
650 char *name = xs_dict_get(v, "name"); 660 const char *type = xs_dict_get(v, "type");
651 char *value = xs_dict_get(v, "value"); 661 const char *name = xs_dict_get(v, "name");
662 const char *value = xs_dict_get(v, "value");
652 663
653 if (!xs_is_null(type) && !xs_is_null(name) && 664 if (!xs_is_null(type) && !xs_is_null(name) &&
654 !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) { 665 !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) {
655 xs *val_date = NULL; 666 xs *val_date = NULL;
656 667
657 char *url = xs_dict_get(metadata, name); 668 const char *url = xs_dict_get(metadata, name);
658 669
659 if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) { 670 if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) {
660 xs_number *verified_time = xs_dict_get(val_links, url); 671 const xs_number *verified_time = xs_dict_get(val_links, url);
661 if (xs_type(verified_time) == XSTYPE_NUMBER) { 672 if (xs_type(verified_time) == XSTYPE_NUMBER) {
662 time_t t = xs_number_get(verified_time); 673 time_t t = xs_number_get(verified_time);
663 674
@@ -686,7 +697,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
686} 697}
687 698
688 699
689xs_str *mastoapi_date(char *date) 700xs_str *mastoapi_date(const char *date)
690/* converts an ISO 8601 date to whatever format Mastodon uses */ 701/* converts an ISO 8601 date to whatever format Mastodon uses */
691{ 702{
692 xs_str *s = xs_crop_i(xs_dup(date), 0, 19); 703 xs_str *s = xs_crop_i(xs_dup(date), 0, 19);
@@ -701,16 +712,29 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
701{ 712{
702 xs_dict *poll = xs_dict_new(); 713 xs_dict *poll = xs_dict_new();
703 xs *mid = mastoapi_id(msg); 714 xs *mid = mastoapi_id(msg);
704 xs_list *opts = NULL; 715 const xs_list *opts = NULL;
705 xs_val *v; 716 const xs_val *v;
706 int num_votes = 0; 717 int num_votes = 0;
707 xs *options = xs_list_new(); 718 xs *options = xs_list_new();
708 719
709 poll = xs_dict_append(poll, "id", mid); 720 poll = xs_dict_append(poll, "id", mid);
710 xs *fd = mastoapi_date(xs_dict_get(msg, "endTime")); 721 const char *date = xs_dict_get(msg, "endTime");
722 if (date == NULL)
723 date = xs_dict_get(msg, "closed");
724 if (date == NULL)
725 return NULL;
726
727 xs *fd = mastoapi_date(date);
711 poll = xs_dict_append(poll, "expires_at", fd); 728 poll = xs_dict_append(poll, "expires_at", fd);
729
730 date = xs_dict_get(msg, "closed");
731 time_t t = 0;
732
733 if (date != NULL)
734 t = xs_parse_iso_date(date, 0);
735
712 poll = xs_dict_append(poll, "expired", 736 poll = xs_dict_append(poll, "expired",
713 xs_dict_get(msg, "closed") != NULL ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE)); 737 t < time(NULL) ? xs_stock(XSTYPE_FALSE) : xs_stock(XSTYPE_TRUE));
714 738
715 if ((opts = xs_dict_get(msg, "oneOf")) != NULL) 739 if ((opts = xs_dict_get(msg, "oneOf")) != NULL)
716 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_FALSE)); 740 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_FALSE));
@@ -719,7 +743,8 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
719 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE)); 743 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE));
720 } 744 }
721 745
722 while (xs_list_iter(&opts, &v)) { 746 int c = 0;
747 while (xs_list_next(opts, &v, &c)) {
723 const char *title = xs_dict_get(v, "name"); 748 const char *title = xs_dict_get(v, "name");
724 const char *replies = xs_dict_get(v, "replies"); 749 const char *replies = xs_dict_get(v, "replies");
725 750
@@ -772,7 +797,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
772 797
773 xs *idx = NULL; 798 xs *idx = NULL;
774 xs *ixc = NULL; 799 xs *ixc = NULL;
775 char *tmp; 800 const char *tmp;
776 xs *mid = mastoapi_id(msg); 801 xs *mid = mastoapi_id(msg);
777 802
778 xs_dict *st = xs_dict_new(); 803 xs_dict *st = xs_dict_new();
@@ -824,14 +849,14 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
824 849
825 { 850 {
826 xs_list *p = attach; 851 xs_list *p = attach;
827 xs_dict *v; 852 const xs_dict *v;
828 853
829 xs *matt = xs_list_new(); 854 xs *matt = xs_list_new();
830 855
831 while (xs_list_iter(&p, &v)) { 856 while (xs_list_iter(&p, &v)) {
832 char *type = xs_dict_get(v, "type"); 857 const char *type = xs_dict_get(v, "type");
833 char *href = xs_dict_get(v, "href"); 858 const char *href = xs_dict_get(v, "href");
834 char *name = xs_dict_get(v, "name"); 859 const char *name = xs_dict_get(v, "name");
835 860
836 if (xs_match(type, "image/*|video/*|Image|Video")) { /* */ 861 if (xs_match(type, "image/*|video/*|Image|Video")) { /* */
837 xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt)); 862 xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt));
@@ -857,7 +882,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
857 xs *ml = xs_list_new(); 882 xs *ml = xs_list_new();
858 xs *htl = xs_list_new(); 883 xs *htl = xs_list_new();
859 xs *eml = xs_list_new(); 884 xs *eml = xs_list_new();
860 xs_list *tag = xs_dict_get(msg, "tag"); 885 const xs_list *tag = xs_dict_get(msg, "tag");
861 int n = 0; 886 int n = 0;
862 887
863 xs *tag_list = NULL; 888 xs *tag_list = NULL;
@@ -873,9 +898,10 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
873 tag_list = xs_list_new(); 898 tag_list = xs_list_new();
874 899
875 tag = tag_list; 900 tag = tag_list;
876 xs_dict *v; 901 const xs_dict *v;
877 902
878 while (xs_list_iter(&tag, &v)) { 903 int c = 0;
904 while (xs_list_next(tag, &v, &c)) {
879 const char *type = xs_dict_get(v, "type"); 905 const char *type = xs_dict_get(v, "type");
880 906
881 if (xs_is_null(type)) 907 if (xs_is_null(type))
@@ -984,7 +1010,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
984 xs *irt_mid = mastoapi_id(irto); 1010 xs *irt_mid = mastoapi_id(irto);
985 st = xs_dict_set(st, "in_reply_to_id", irt_mid); 1011 st = xs_dict_set(st, "in_reply_to_id", irt_mid);
986 1012
987 char *at = NULL; 1013 const char *at = NULL;
988 if (!xs_is_null(at = get_atto(irto))) { 1014 if (!xs_is_null(at = get_atto(irto))) {
989 xs *at_md5 = xs_md5_hex(at, strlen(at)); 1015 xs *at_md5 = xs_md5_hex(at, strlen(at));
990 st = xs_dict_set(st, "in_reply_to_account_id", at_md5); 1016 st = xs_dict_set(st, "in_reply_to_account_id", at_md5);
@@ -994,7 +1020,10 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
994 1020
995 st = xs_dict_append(st, "reblog", xs_stock(XSTYPE_NULL)); 1021 st = xs_dict_append(st, "reblog", xs_stock(XSTYPE_NULL));
996 st = xs_dict_append(st, "card", xs_stock(XSTYPE_NULL)); 1022 st = xs_dict_append(st, "card", xs_stock(XSTYPE_NULL));
997 st = xs_dict_append(st, "language", xs_stock(XSTYPE_NULL)); 1023 st = xs_dict_append(st, "language", "en");
1024
1025 st = xs_dict_append(st, "filtered", xs_stock(XSTYPE_LIST));
1026 st = xs_dict_append(st, "muted", xs_stock(XSTYPE_FALSE));
998 1027
999 tmp = xs_dict_get(msg, "sourceContent"); 1028 tmp = xs_dict_get(msg, "sourceContent");
1000 if (xs_is_null(tmp)) 1029 if (xs_is_null(tmp))
@@ -1093,7 +1122,7 @@ int process_auth_token(snac *snac, const xs_dict *req)
1093/* processes an authorization token, if there is one */ 1122/* processes an authorization token, if there is one */
1094{ 1123{
1095 int logged_in = 0; 1124 int logged_in = 0;
1096 char *v; 1125 const char *v;
1097 1126
1098 /* if there is an authorization field, try to validate it */ 1127 /* if there is an authorization field, try to validate it */
1099 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { 1128 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) {
@@ -1131,7 +1160,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1131 return 0; 1160 return 0;
1132 1161
1133 int status = 404; 1162 int status = 404;
1134 xs_dict *args = xs_dict_get(req, "q_vars"); 1163 const xs_dict *args = xs_dict_get(req, "q_vars");
1135 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 1164 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
1136 1165
1137 snac snac1 = {0}; 1166 snac snac1 = {0};
@@ -1157,7 +1186,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1157 acct = xs_dict_append(acct, "source", src); 1186 acct = xs_dict_append(acct, "source", src);
1158 1187
1159 xs *avatar = NULL; 1188 xs *avatar = NULL;
1160 char *av = xs_dict_get(snac1.config, "avatar"); 1189 const char *av = xs_dict_get(snac1.config, "avatar");
1161 1190
1162 if (xs_is_null(av) || *av == '\0') 1191 if (xs_is_null(av) || *av == '\0')
1163 avatar = xs_fmt("%s/susie.png", srv_baseurl); 1192 avatar = xs_fmt("%s/susie.png", srv_baseurl);
@@ -1168,7 +1197,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1168 acct = xs_dict_append(acct, "avatar_static", avatar); 1197 acct = xs_dict_append(acct, "avatar_static", avatar);
1169 1198
1170 xs *header = NULL; 1199 xs *header = NULL;
1171 char *hd = xs_dict_get(snac1.config, "header"); 1200 const char *hd = xs_dict_get(snac1.config, "header");
1172 1201
1173 if (!xs_is_null(hd)) 1202 if (!xs_is_null(hd))
1174 header = xs_dup(hd); 1203 header = xs_dup(hd);
@@ -1178,11 +1207,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1178 acct = xs_dict_append(acct, "header", header); 1207 acct = xs_dict_append(acct, "header", header);
1179 acct = xs_dict_append(acct, "header_static", header); 1208 acct = xs_dict_append(acct, "header_static", header);
1180 1209
1181 xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); 1210 const xs_dict *metadata = xs_dict_get(snac1.config, "metadata");
1182 if (xs_type(metadata) == XSTYPE_DICT) { 1211 if (xs_type(metadata) == XSTYPE_DICT) {
1183 xs *fields = xs_list_new(); 1212 xs *fields = xs_list_new();
1184 xs_str *k; 1213 const xs_str *k;
1185 xs_str *v; 1214 const xs_str *v;
1186 1215
1187 xs_dict *val_links = snac1.links; 1216 xs_dict *val_links = snac1.links;
1188 if (xs_is_null(val_links)) 1217 if (xs_is_null(val_links))
@@ -1192,7 +1221,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1192 while (xs_dict_next(metadata, &k, &v, &c)) { 1221 while (xs_dict_next(metadata, &k, &v, &c)) {
1193 xs *val_date = NULL; 1222 xs *val_date = NULL;
1194 1223
1195 xs_number *verified_time = xs_dict_get(val_links, v); 1224 const xs_number *verified_time = xs_dict_get(val_links, v);
1196 if (xs_type(verified_time) == XSTYPE_NUMBER) { 1225 if (xs_type(verified_time) == XSTYPE_NUMBER) {
1197 time_t t = xs_number_get(verified_time); 1226 time_t t = xs_number_get(verified_time);
1198 1227
@@ -1258,13 +1287,13 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1258 else 1287 else
1259 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/ 1288 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/
1260 /* lookup an account */ 1289 /* lookup an account */
1261 char *acct = xs_dict_get(args, "acct"); 1290 const char *acct = xs_dict_get(args, "acct");
1262 1291
1263 if (!xs_is_null(acct)) { 1292 if (!xs_is_null(acct)) {
1264 xs *s = xs_strip_chars_i(xs_dup(acct), "@"); 1293 xs *s = xs_strip_chars_i(xs_dup(acct), "@");
1265 xs *l = xs_split_n(s, "@", 1); 1294 xs *l = xs_split_n(s, "@", 1);
1266 char *uid = xs_list_get(l, 0); 1295 const char *uid = xs_list_get(l, 0);
1267 char *host = xs_list_get(l, 1); 1296 const char *host = xs_list_get(l, 1);
1268 1297
1269 if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) { 1298 if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) {
1270 snac user; 1299 snac user;
@@ -1305,7 +1334,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1305 xs *wers = follower_list(&snac1); 1334 xs *wers = follower_list(&snac1);
1306 xs *ulst = user_list(); 1335 xs *ulst = user_list();
1307 xs_list *p; 1336 xs_list *p;
1308 xs_str *v; 1337 const xs_str *v;
1309 xs_set seen; 1338 xs_set seen;
1310 1339
1311 xs_set_init(&seen); 1340 xs_set_init(&seen);
@@ -1381,7 +1410,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1381 /* the public list of posts of a user */ 1410 /* the public list of posts of a user */
1382 xs *timeline = timeline_simple_list(&snac2, "public", 0, 256); 1411 xs *timeline = timeline_simple_list(&snac2, "public", 0, 256);
1383 xs_list *p = timeline; 1412 xs_list *p = timeline;
1384 xs_str *v; 1413 const xs_str *v;
1385 1414
1386 out = xs_list_new(); 1415 out = xs_list_new();
1387 1416
@@ -1446,7 +1475,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1446 1475
1447 xs *out = xs_list_new(); 1476 xs *out = xs_list_new();
1448 xs_list *p = timeline; 1477 xs_list *p = timeline;
1449 xs_str *v; 1478 const xs_str *v;
1450 1479
1451 while (xs_list_iter(&p, &v) && cnt < limit) { 1480 while (xs_list_iter(&p, &v) && cnt < limit) {
1452 xs *msg = NULL; 1481 xs *msg = NULL;
@@ -1479,7 +1508,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1479 /* discard non-Notes */ 1508 /* discard non-Notes */
1480 const char *id = xs_dict_get(msg, "id"); 1509 const char *id = xs_dict_get(msg, "id");
1481 const char *type = xs_dict_get(msg, "type"); 1510 const char *type = xs_dict_get(msg, "type");
1482 if (!xs_match(type, "Note|Question|Page|Article|Video")) 1511 if (!xs_match(type, POSTLIKE_OBJECT_TYPE))
1483 continue; 1512 continue;
1484 1513
1485 const char *from = NULL; 1514 const char *from = NULL;
@@ -1550,7 +1579,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1550 xs *timeline = timeline_instance_list(0, limit); 1579 xs *timeline = timeline_instance_list(0, limit);
1551 xs *out = xs_list_new(); 1580 xs *out = xs_list_new();
1552 xs_list *p = timeline; 1581 xs_list *p = timeline;
1553 xs_str *md5; 1582 const xs_str *md5;
1554 1583
1555 snac *user = NULL; 1584 snac *user = NULL;
1556 if (logged_in) 1585 if (logged_in)
@@ -1599,12 +1628,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1599 1628
1600 /* get the tag */ 1629 /* get the tag */
1601 xs *l = xs_split(cmd, "/"); 1630 xs *l = xs_split(cmd, "/");
1602 char *tag = xs_list_get(l, -1); 1631 const char *tag = xs_list_get(l, -1);
1603 1632
1604 xs *timeline = tag_search(tag, 0, limit); 1633 xs *timeline = tag_search(tag, 0, limit);
1605 xs *out = xs_list_new(); 1634 xs *out = xs_list_new();
1606 xs_list *p = timeline; 1635 xs_list *p = timeline;
1607 xs_str *md5; 1636 const xs_str *md5;
1608 1637
1609 while (xs_list_iter(&p, &md5) && cnt < limit) { 1638 while (xs_list_iter(&p, &md5) && cnt < limit) {
1610 xs *msg = NULL; 1639 xs *msg = NULL;
@@ -1635,6 +1664,77 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1635 status = 200; 1664 status = 200;
1636 } 1665 }
1637 else 1666 else
1667 if (xs_startswith(cmd, "/v1/timelines/list/")) { /** **/
1668 /* get the list id */
1669 if (logged_in) {
1670 xs *l = xs_split(cmd, "/");
1671 const char *list = xs_list_get(l, -1);
1672
1673 xs *timeline = list_timeline(&snac1, list, 0, 2048);
1674 xs *out = xs_list_new();
1675 int c = 0;
1676 const char *md5;
1677
1678 while (xs_list_next(timeline, &md5, &c)) {
1679 xs *msg = NULL;
1680
1681 /* get the entry */
1682 if (!valid_status(timeline_get_by_md5(&snac1, md5, &msg)))
1683 continue;
1684
1685 /* discard non-Notes */
1686 const char *id = xs_dict_get(msg, "id");
1687 const char *type = xs_dict_get(msg, "type");
1688 if (!xs_match(type, POSTLIKE_OBJECT_TYPE))
1689 continue;
1690
1691 const char *from = NULL;
1692 if (strcmp(type, "Page") == 0)
1693 from = xs_dict_get(msg, "audience");
1694
1695 if (from == NULL)
1696 from = get_atto(msg);
1697
1698 if (from == NULL)
1699 continue;
1700
1701 /* is this message from a person we don't follow? */
1702 if (strcmp(from, snac1.actor) && !following_check(&snac1, from)) {
1703 /* discard if it was not boosted */
1704 xs *idx = object_announces(id);
1705
1706 if (xs_list_len(idx) == 0)
1707 continue;
1708 }
1709
1710 /* discard notes from muted morons */
1711 if (is_muted(&snac1, from))
1712 continue;
1713
1714 /* discard hidden notes */
1715 if (is_hidden(&snac1, id))
1716 continue;
1717
1718 /* if it has a name and it's not a Page or a Video,
1719 it's a poll vote, so discard it */
1720 if (!xs_is_null(xs_dict_get(msg, "name")) && !xs_match(type, "Page|Video"))
1721 continue;
1722
1723 /* convert the Note into a Mastodon status */
1724 xs *st = mastoapi_status(&snac1, msg);
1725
1726 if (st != NULL)
1727 out = xs_list_append(out, st);
1728 }
1729
1730 *body = xs_json_dumps(out, 4);
1731 *ctype = "application/json";
1732 status = 200;
1733 }
1734 else
1735 status = 421;
1736 }
1737 else
1638 if (strcmp(cmd, "/v1/conversations") == 0) { /** **/ 1738 if (strcmp(cmd, "/v1/conversations") == 0) { /** **/
1639 /* TBD */ 1739 /* TBD */
1640 *body = xs_dup("[]"); 1740 *body = xs_dup("[]");
@@ -1647,8 +1747,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1647 xs *l = notify_list(&snac1, 0, 64); 1747 xs *l = notify_list(&snac1, 0, 64);
1648 xs *out = xs_list_new(); 1748 xs *out = xs_list_new();
1649 xs_list *p = l; 1749 xs_list *p = l;
1650 xs_dict *v; 1750 const xs_dict *v;
1651 xs_list *excl = xs_dict_get(args, "exclude_types[]"); 1751 const xs_list *excl = xs_dict_get(args, "exclude_types[]");
1652 1752
1653 while (xs_list_iter(&p, &v)) { 1753 while (xs_list_iter(&p, &v)) {
1654 xs *noti = notify_get(&snac1, v); 1754 xs *noti = notify_get(&snac1, v);
@@ -1753,11 +1853,88 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1753 status = 200; 1853 status = 200;
1754 } 1854 }
1755 else 1855 else
1756 if (strcmp(cmd, "/v1/lists") == 0) { /** **/ 1856 if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/
1757 /* snac does not support lists */ 1857 if (logged_in) {
1758 *body = xs_dup("[]"); 1858 xs *lol = list_maint(&snac1, NULL, 0);
1759 *ctype = "application/json"; 1859 xs *l = xs_list_new();
1760 status = 200; 1860 int c = 0;
1861 const xs_list *li;
1862
1863 while (xs_list_next(lol, &li, &c)) {
1864 xs *d = xs_dict_new();
1865
1866 d = xs_dict_append(d, "id", xs_list_get(li, 0));
1867 d = xs_dict_append(d, "title", xs_list_get(li, 1));
1868 d = xs_dict_append(d, "replies_policy", "list");
1869 d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE));
1870
1871 l = xs_list_append(l, d);
1872 }
1873
1874 *body = xs_json_dumps(l, 4);
1875 *ctype = "application/json";
1876 status = 200;
1877 }
1878 }
1879 else
1880 if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/
1881 if (logged_in) {
1882 xs *l = xs_split(cmd, "/");
1883 const char *p = xs_list_get(l, -1);
1884
1885 if (p) {
1886 if (strcmp(p, "accounts") == 0) {
1887 p = xs_list_get(l, -2);
1888
1889 if (p && xs_is_hex(p)) {
1890 xs *actors = list_content(&snac1, p, NULL, 0);
1891 xs *out = xs_list_new();
1892 int c = 0;
1893 const char *v;
1894
1895 while (xs_list_next(actors, &v, &c)) {
1896 xs *actor = NULL;
1897
1898 if (valid_status(object_get_by_md5(v, &actor))) {
1899 xs *acct = mastoapi_account(actor);
1900 out = xs_list_append(out, acct);
1901 }
1902 }
1903
1904 *body = xs_json_dumps(out, 4);
1905 *ctype = "application/json";
1906 status = 200;
1907 }
1908 }
1909 else
1910 if (xs_is_hex(p)) {
1911 xs *out = xs_list_new();
1912 xs *lol = list_maint(&snac1, NULL, 0);
1913 int c = 0;
1914 const xs_list *v;
1915
1916 while (xs_list_next(lol, &v, &c)) {
1917 const char *id = xs_list_get(v, 0);
1918
1919 if (id && strcmp(id, p) == 0) {
1920 xs *d = xs_dict_new();
1921
1922 d = xs_dict_append(d, "id", p);
1923 d = xs_dict_append(d, "title", xs_list_get(v, 1));
1924 d = xs_dict_append(d, "replies_policy", "list");
1925 d = xs_dict_append(d, "exclusive", xs_stock(XSTYPE_FALSE));
1926
1927 out = xs_list_append(out, d);
1928 break;
1929 }
1930 }
1931
1932 *body = xs_json_dumps(out, 4);
1933 *ctype = "application/json";
1934 status = 200;
1935 }
1936 }
1937 }
1761 } 1938 }
1762 else 1939 else
1763 if (strcmp(cmd, "/v1/scheduled_statuses") == 0) { /** **/ 1940 if (strcmp(cmd, "/v1/scheduled_statuses") == 0) { /** **/
@@ -1928,7 +2105,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1928 xs *anc = xs_list_new(); 2105 xs *anc = xs_list_new();
1929 xs *des = xs_list_new(); 2106 xs *des = xs_list_new();
1930 xs_list *p; 2107 xs_list *p;
1931 xs_str *v; 2108 const xs_str *v;
1932 char pid[64]; 2109 char pid[64];
1933 2110
1934 /* build the [grand]parent list, moving up */ 2111 /* build the [grand]parent list, moving up */
@@ -1982,7 +2159,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1982 l = object_likes(xs_dict_get(msg, "id")); 2159 l = object_likes(xs_dict_get(msg, "id"));
1983 2160
1984 xs_list *p = l; 2161 xs_list *p = l;
1985 xs_str *v; 2162 const xs_str *v;
1986 2163
1987 while (xs_list_iter(&p, &v)) { 2164 while (xs_list_iter(&p, &v)) {
1988 xs *actor2 = NULL; 2165 xs *actor2 = NULL;
@@ -2052,8 +2229,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2052 /* reply something only for offset 0; otherwise, 2229 /* reply something only for offset 0; otherwise,
2053 apps like Tusky keep asking again and again */ 2230 apps like Tusky keep asking again and again */
2054 2231
2055 if (!xs_is_null(q) && !xs_is_null(type)) { 2232 if (!xs_is_null(q)) {
2056 if (strcmp(type, "accounts") == 0) { 2233 if (xs_is_null(type) || strcmp(type, "accounts") == 0) {
2057 /* do a webfinger query */ 2234 /* do a webfinger query */
2058 char *actor = NULL; 2235 char *actor = NULL;
2059 char *user = NULL; 2236 char *user = NULL;
@@ -2068,8 +2245,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2068 } 2245 }
2069 } 2246 }
2070 } 2247 }
2071 else 2248
2072 if (strcmp(type, "hashtags") == 0) { 2249 if (xs_is_null(type) || strcmp(type, "hashtags") == 0) {
2073 /* search this tag */ 2250 /* search this tag */
2074 xs *tl = tag_search((char *)q, 0, 1); 2251 xs *tl = tag_search((char *)q, 0, 1);
2075 2252
@@ -2084,6 +2261,26 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2084 htl = xs_list_append(htl, d); 2261 htl = xs_list_append(htl, d);
2085 } 2262 }
2086 } 2263 }
2264
2265 if (xs_is_null(type) || strcmp(type, "statuses") == 0) {
2266 int to = 0;
2267 int cnt = 40;
2268 xs *tl = content_search(&snac1, q, 1, 0, cnt, 0, &to);
2269 int c = 0;
2270 const char *v;
2271
2272 while (xs_list_next(tl, &v, &c) && --cnt) {
2273 xs *post = NULL;
2274
2275 if (!valid_status(timeline_get_by_md5(&snac1, v, &post)))
2276 continue;
2277
2278 xs *s = mastoapi_status(&snac1, post);
2279
2280 if (!xs_is_null(s))
2281 stl = xs_list_append(stl, s);
2282 }
2283 }
2087 } 2284 }
2088 } 2285 }
2089 2286
@@ -2119,11 +2316,9 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2119 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 2316 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
2120 return 0; 2317 return 0;
2121 2318
2122 srv_debug(1, xs_fmt("mastoapi_post_handler %s", q_path));
2123
2124 int status = 404; 2319 int status = 404;
2125 xs *args = NULL; 2320 xs *args = NULL;
2126 char *i_ctype = xs_dict_get(req, "content-type"); 2321 const char *i_ctype = xs_dict_get(req, "content-type");
2127 2322
2128 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 2323 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2129 if (!xs_is_null(payload)) 2324 if (!xs_is_null(payload))
@@ -2238,7 +2433,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2238 } 2433 }
2239 2434
2240 xs_list *p = mi; 2435 xs_list *p = mi;
2241 xs_str *v; 2436 const xs_str *v;
2242 2437
2243 while (xs_list_iter(&p, &v)) { 2438 while (xs_list_iter(&p, &v)) {
2244 xs *l = xs_list_new(); 2439 xs *l = xs_list_new();
@@ -2296,7 +2491,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2296 mid = MID_TO_MD5(mid); 2491 mid = MID_TO_MD5(mid);
2297 2492
2298 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) { 2493 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) {
2299 char *id = xs_dict_get(msg, "id"); 2494 const char *id = xs_dict_get(msg, "id");
2300 2495
2301 if (op == NULL) { 2496 if (op == NULL) {
2302 /* no operation (?) */ 2497 /* no operation (?) */
@@ -2402,7 +2597,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2402 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/ 2597 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/
2403 /* I don't know what I'm doing */ 2598 /* I don't know what I'm doing */
2404 if (logged_in) { 2599 if (logged_in) {
2405 char *v; 2600 const char *v;
2406 2601
2407 xs *wpush = xs_dict_new(); 2602 xs *wpush = xs_dict_new();
2408 2603
@@ -2574,7 +2769,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2574 const char *id = xs_dict_get(msg, "id"); 2769 const char *id = xs_dict_get(msg, "id");
2575 const char *atto = get_atto(msg); 2770 const char *atto = get_atto(msg);
2576 2771
2577 xs_list *opts = xs_dict_get(msg, "oneOf"); 2772 const xs_list *opts = xs_dict_get(msg, "oneOf");
2578 if (opts == NULL) 2773 if (opts == NULL)
2579 opts = xs_dict_get(msg, "anyOf"); 2774 opts = xs_dict_get(msg, "anyOf");
2580 2775
@@ -2582,15 +2777,16 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2582 } 2777 }
2583 else 2778 else
2584 if (strcmp(op, "votes") == 0) { 2779 if (strcmp(op, "votes") == 0) {
2585 xs_list *choices = xs_dict_get(args, "choices[]"); 2780 const xs_list *choices = xs_dict_get(args, "choices[]");
2586 2781
2587 if (xs_is_null(choices)) 2782 if (xs_is_null(choices))
2588 choices = xs_dict_get(args, "choices"); 2783 choices = xs_dict_get(args, "choices");
2589 2784
2590 if (xs_type(choices) == XSTYPE_LIST) { 2785 if (xs_type(choices) == XSTYPE_LIST) {
2591 xs_str *v; 2786 const xs_str *v;
2592 2787
2593 while (xs_list_iter(&choices, &v)) { 2788 int c = 0;
2789 while (xs_list_next(choices, &v, &c)) {
2594 int io = atoi(v); 2790 int io = atoi(v);
2595 const xs_dict *o = xs_list_get(opts, io); 2791 const xs_dict *o = xs_list_get(opts, io);
2596 2792
@@ -2621,19 +2817,73 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2621 else 2817 else
2622 status = 401; 2818 status = 401;
2623 } 2819 }
2820 else
2821 if (strcmp(cmd, "/v1/lists") == 0) {
2822 if (logged_in) {
2823 const char *title = xs_dict_get(args, "title");
2824
2825 if (xs_type(title) == XSTYPE_STRING) {
2826 /* add the list */
2827 xs *out = xs_dict_new();
2828
2829 if (xs_type(list_maint(&snac, title, 1)) == XSTYPE_TRUE) {
2830 out = xs_dict_append(out, "title", title);
2831 out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list"));
2832 out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE));
2833
2834 status = 200;
2835 }
2836 else {
2837 out = xs_dict_append(out, "error", "cannot create list");
2838 status = 422;
2839 }
2840
2841 *body = xs_json_dumps(out, 4);
2842 *ctype = "application/json";
2843 }
2844 else
2845 status = 422;
2846 }
2847 }
2848 if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/
2849 if (logged_in) {
2850 xs *l = xs_split(cmd, "/");
2851 const char *op = xs_list_get(l, -1);
2852 const char *id = xs_list_get(l, -2);
2853
2854 if (op && id && xs_is_hex(id)) {
2855 if (strcmp(op, "accounts") == 0) {
2856 const xs_list *accts = xs_dict_get(args, "account_ids[]");
2857 int c = 0;
2858 const char *v;
2859
2860 while (xs_list_next(accts, &v, &c)) {
2861 list_content(&snac, id, v, 1);
2862 }
2863
2864 status = 200;
2865 }
2866 }
2867 }
2868 else
2869 status = 422;
2870 }
2624 2871
2625 /* user cleanup */ 2872 /* user cleanup */
2626 if (logged_in) 2873 if (logged_in)
2627 user_free(&snac); 2874 user_free(&snac);
2628 2875
2876 srv_debug(1, xs_fmt("mastoapi_post_handler %s %d", q_path, status));
2877
2629 return status; 2878 return status;
2630} 2879}
2631 2880
2632 2881
2633int mastoapi_delete_handler(const xs_dict *req, const char *q_path, 2882int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2634 char **body, int *b_size, char **ctype) { 2883 const char *payload, int p_size,
2635 2884 char **body, int *b_size, char **ctype)
2636 (void)req; 2885{
2886 (void)p_size;
2637 (void)body; 2887 (void)body;
2638 (void)b_size; 2888 (void)b_size;
2639 (void)ctype; 2889 (void)ctype;
@@ -2641,13 +2891,76 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2641 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 2891 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
2642 return 0; 2892 return 0;
2643 2893
2644 srv_debug(1, xs_fmt("mastoapi_delete_handler %s", q_path)); 2894 int status = 404;
2895 xs *args = NULL;
2896 const char *i_ctype = xs_dict_get(req, "content-type");
2897
2898 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2899 if (!xs_is_null(payload))
2900 args = xs_json_loads(payload);
2901 }
2902 else if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded"))
2903 {
2904 // Some apps send form data instead of json so we should cater for those
2905 if (!xs_is_null(payload)) {
2906 xs *upl = xs_url_dec(payload);
2907 args = xs_url_vars(upl);
2908 }
2909 }
2910 else
2911 args = xs_dup(xs_dict_get(req, "p_vars"));
2912
2913 if (args == NULL)
2914 return 400;
2915
2916 snac snac = {0};
2917 int logged_in = process_auth_token(&snac, req);
2918
2645 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 2919 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
2920
2646 if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/ 2921 if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/
2647 // pretend we deleted it, since it doesn't exist anyway 2922 // pretend we deleted it, since it doesn't exist anyway
2648 return 200; 2923 status = 200;
2924 }
2925 else
2926 if (xs_startswith(cmd, "/v1/lists/")) {
2927 if (logged_in) {
2928 xs *l = xs_split(cmd, "/");
2929 const char *p = xs_list_get(l, -1);
2930
2931 if (p) {
2932 if (strcmp(p, "accounts") == 0) {
2933 /* delete account from list */
2934 p = xs_list_get(l, -2);
2935 const xs_list *accts = xs_dict_get(args, "account_ids[]");
2936 int c = 0;
2937 const char *v;
2938
2939 while (xs_list_next(accts, &v, &c)) {
2940 list_content(&snac, p, v, 2);
2941 }
2942 }
2943 else {
2944 /* delete list */
2945 if (xs_is_hex(p)) {
2946 list_maint(&snac, p, 2);
2947 }
2948 }
2949 }
2950
2951 status = 200;
2952 }
2953 else
2954 status = 401;
2649 } 2955 }
2650 return 0; 2956
2957 /* user cleanup */
2958 if (logged_in)
2959 user_free(&snac);
2960
2961 srv_debug(1, xs_fmt("mastoapi_delete_handler %s %d", q_path, status));
2962
2963 return status;
2651} 2964}
2652 2965
2653 2966
@@ -2663,7 +2976,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
2663 2976
2664 int status = 404; 2977 int status = 404;
2665 xs *args = NULL; 2978 xs *args = NULL;
2666 char *i_ctype = xs_dict_get(req, "content-type"); 2979 const char *i_ctype = xs_dict_get(req, "content-type");
2667 2980
2668 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 2981 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2669 if (!xs_is_null(payload)) 2982 if (!xs_is_null(payload))
@@ -2770,7 +3083,7 @@ void mastoapi_purge(void)
2770 xs *spec = xs_fmt("%s/app/" "*.json", srv_basedir); 3083 xs *spec = xs_fmt("%s/app/" "*.json", srv_basedir);
2771 xs *files = xs_glob(spec, 1, 0); 3084 xs *files = xs_glob(spec, 1, 0);
2772 xs_list *p = files; 3085 xs_list *p = files;
2773 xs_str *v; 3086 const xs_str *v;
2774 3087
2775 time_t mt = time(NULL) - 3600; 3088 time_t mt = time(NULL) - 3600;
2776 3089