summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c201
1 files changed, 121 insertions, 80 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 001c0bc..b82ecfa 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -162,7 +162,7 @@ const char *login_page = ""
162"<!DOCTYPE html>\n" 162"<!DOCTYPE html>\n"
163"<body><h1>%s OAuth identify</h1>\n" 163"<body><h1>%s OAuth identify</h1>\n"
164"<div style=\"background-color: red; color: white\">%s</div>\n" 164"<div style=\"background-color: red; color: white\">%s</div>\n"
165"<form method=\"post\" action=\"https:/" "/%s/oauth/x-snac-login\">\n" 165"<form method=\"post\" action=\"https:/" "/%s/%s\">\n"
166"<p>Login: <input type=\"text\" name=\"login\"></p>\n" 166"<p>Login: <input type=\"text\" name=\"login\"></p>\n"
167"<p>Password: <input type=\"password\" name=\"passwd\"></p>\n" 167"<p>Password: <input type=\"password\" name=\"passwd\"></p>\n"
168"<input type=\"hidden\" name=\"redir\" value=\"%s\">\n" 168"<input type=\"hidden\" name=\"redir\" value=\"%s\">\n"
@@ -175,6 +175,8 @@ const char *login_page = ""
175int oauth_get_handler(const xs_dict *req, const char *q_path, 175int oauth_get_handler(const xs_dict *req, const char *q_path,
176 char **body, int *b_size, char **ctype) 176 char **body, int *b_size, char **ctype)
177{ 177{
178 (void)b_size;
179
178 if (!xs_startswith(q_path, "/oauth/")) 180 if (!xs_startswith(q_path, "/oauth/"))
179 return 0; 181 return 0;
180 182
@@ -185,7 +187,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
185 187
186 int status = 404; 188 int status = 404;
187 xs_dict *msg = xs_dict_get(req, "q_vars"); 189 xs_dict *msg = xs_dict_get(req, "q_vars");
188 xs *cmd = xs_replace(q_path, "/oauth", ""); 190 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
189 191
190 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path)); 192 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path));
191 193
@@ -206,17 +208,28 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
206 if (xs_is_null(state)) 208 if (xs_is_null(state))
207 state = ""; 209 state = "";
208 210
209 *body = xs_fmt(login_page, host, "", host, ruri, cid, state, USER_AGENT); 211 *body = xs_fmt(login_page, host, "", host, "oauth/x-snac-login",
212 ruri, cid, state, USER_AGENT);
210 *ctype = "text/html"; 213 *ctype = "text/html";
211 status = 200; 214 status = 200;
212 215
213 srv_debug(0, xs_fmt("oauth authorize: generating login page")); 216 srv_debug(1, xs_fmt("oauth authorize: generating login page"));
214 } 217 }
215 else 218 else
216 srv_debug(0, xs_fmt("oauth authorize: bad client_id %s", cid)); 219 srv_debug(1, xs_fmt("oauth authorize: bad client_id %s", cid));
217 } 220 }
218 else 221 else
219 srv_debug(0, xs_fmt("oauth authorize: invalid or unset arguments")); 222 srv_debug(1, xs_fmt("oauth authorize: invalid or unset arguments"));
223 }
224 else
225 if (strcmp(cmd, "/x-snac-get-token") == 0) {
226 const char *host = xs_dict_get(srv_config, "host");
227
228 *body = xs_fmt(login_page, host, "", host, "oauth/x-snac-get-token",
229 "", "", "", USER_AGENT);
230 *ctype = "text/html";
231 status = 200;
232
220 } 233 }
221 234
222 return status; 235 return status;
@@ -227,6 +240,9 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
227 const char *payload, int p_size, 240 const char *payload, int p_size,
228 char **body, int *b_size, char **ctype) 241 char **body, int *b_size, char **ctype)
229{ 242{
243 (void)p_size;
244 (void)b_size;
245
230 if (!xs_startswith(q_path, "/oauth/")) 246 if (!xs_startswith(q_path, "/oauth/"))
231 return 0; 247 return 0;
232 248
@@ -245,7 +261,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
245 else 261 else
246 args = xs_dup(xs_dict_get(req, "p_vars")); 262 args = xs_dup(xs_dict_get(req, "p_vars"));
247 263
248 xs *cmd = xs_replace(q_path, "/oauth", ""); 264 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
249 265
250 srv_debug(1, xs_fmt("oauth_post_handler %s", q_path)); 266 srv_debug(1, xs_fmt("oauth_post_handler %s", q_path));
251 267
@@ -259,7 +275,8 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
259 const char *host = xs_dict_get(srv_config, "host"); 275 const char *host = xs_dict_get(srv_config, "host");
260 276
261 /* by default, generate another login form with an error */ 277 /* by default, generate another login form with an error */
262 *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, redir, cid, state, USER_AGENT); 278 *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, "oauth/x-snac-login",
279 redir, cid, state, USER_AGENT);
263 *ctype = "text/html"; 280 *ctype = "text/html";
264 status = 200; 281 status = 200;
265 282
@@ -268,8 +285,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
268 285
269 if (user_open(&snac, login)) { 286 if (user_open(&snac, login)) {
270 /* check the login + password */ 287 /* check the login + password */
271 if (check_password(login, passwd, 288 if (check_password(login, passwd, xs_dict_get(snac.config, "passwd"))) {
272 xs_dict_get(snac.config, "passwd"))) {
273 /* success! redirect to the desired uri */ 289 /* success! redirect to the desired uri */
274 xs *code = random_str(); 290 xs *code = random_str();
275 291
@@ -328,7 +344,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
328 const char *auhdr = xs_dict_get(req, "authorization"); 344 const char *auhdr = xs_dict_get(req, "authorization");
329 345
330 if (!xs_is_null(auhdr) && xs_startswith(auhdr, "Basic ")) { 346 if (!xs_is_null(auhdr) && xs_startswith(auhdr, "Basic ")) {
331 xs *s1 = xs_replace(auhdr, "Basic ", ""); 347 xs *s1 = xs_replace_n(auhdr, "Basic ", "", 1);
332 int size; 348 int size;
333 xs *s2 = xs_base64_dec(s1, &size); 349 xs *s2 = xs_base64_dec(s1, &size);
334 350
@@ -373,7 +389,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
373 389
374 const char *uid = xs_dict_get(app, "uid"); 390 const char *uid = xs_dict_get(app, "uid");
375 391
376 srv_debug(0, xs_fmt("oauth token: " 392 srv_debug(1, xs_fmt("oauth token: "
377 "successful login for %s, new token %s", uid, tokid)); 393 "successful login for %s, new token %s", uid, tokid));
378 394
379 xs *token = xs_dict_new(); 395 xs *token = xs_dict_new();
@@ -387,7 +403,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
387 } 403 }
388 } 404 }
389 else { 405 else {
390 srv_debug(0, xs_fmt("oauth token: invalid or unset arguments")); 406 srv_debug(1, xs_fmt("oauth token: invalid or unset arguments"));
391 status = 400; 407 status = 400;
392 } 408 }
393 } 409 }
@@ -409,7 +425,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
409 } 425 }
410 else { 426 else {
411 token_del(tokid); 427 token_del(tokid);
412 srv_debug(0, xs_fmt("oauth revoke: revoked token %s", tokid)); 428 srv_debug(1, xs_fmt("oauth revoke: revoked token %s", tokid));
413 status = 200; 429 status = 200;
414 430
415 /* also delete the app, as it serves no purpose from now on */ 431 /* also delete the app, as it serves no purpose from now on */
@@ -417,10 +433,52 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
417 } 433 }
418 } 434 }
419 else { 435 else {
420 srv_debug(0, xs_fmt("oauth revoke: invalid or unset arguments")); 436 srv_debug(1, xs_fmt("oauth revoke: invalid or unset arguments"));
421 status = 403; 437 status = 403;
422 } 438 }
423 } 439 }
440 if (strcmp(cmd, "/x-snac-get-token") == 0) {
441 const char *login = xs_dict_get(args, "login");
442 const char *passwd = xs_dict_get(args, "passwd");
443
444 const char *host = xs_dict_get(srv_config, "host");
445
446 /* by default, generate another login form with an error */
447 *body = xs_fmt(login_page, host, "LOGIN INCORRECT", host, "oauth/x-snac-get-token",
448 "", "", "", USER_AGENT);
449 *ctype = "text/html";
450 status = 200;
451
452 if (login && passwd) {
453 snac user;
454
455 if (user_open(&user, login)) {
456 /* check the login + password */
457 if (check_password(login, passwd, xs_dict_get(user.config, "passwd"))) {
458 /* success! create a new token */
459 xs *tokid = random_str();
460
461 srv_debug(1, xs_fmt("x-snac-new-token: "
462 "successful login for %s, new token %s", login, tokid));
463
464 xs *token = xs_dict_new();
465 token = xs_dict_append(token, "token", tokid);
466 token = xs_dict_append(token, "client_id", "snac-client");
467 token = xs_dict_append(token, "client_secret", "");
468 token = xs_dict_append(token, "uid", login);
469 token = xs_dict_append(token, "code", "");
470
471 token_add(tokid, token);
472
473 *ctype = "text/plain";
474 xs_free(*body);
475 *body = xs_dup(tokid);
476 }
477
478 user_free(&user);
479 }
480 }
481 }
424 482
425 return status; 483 return status;
426} 484}
@@ -537,7 +595,6 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
537 xs *f = xs_val_new(XSTYPE_FALSE); 595 xs *f = xs_val_new(XSTYPE_FALSE);
538 xs *t = xs_val_new(XSTYPE_TRUE); 596 xs *t = xs_val_new(XSTYPE_TRUE);
539 xs *n = xs_val_new(XSTYPE_NULL); 597 xs *n = xs_val_new(XSTYPE_NULL);
540 xs *el = xs_list_new();
541 xs *idx = NULL; 598 xs *idx = NULL;
542 xs *ixc = NULL; 599 xs *ixc = NULL;
543 600
@@ -787,7 +844,7 @@ int process_auth_token(snac *snac, const xs_dict *req)
787 844
788 /* if there is an authorization field, try to validate it */ 845 /* if there is an authorization field, try to validate it */
789 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { 846 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) {
790 xs *tokid = xs_replace(v, "Bearer ", ""); 847 xs *tokid = xs_replace_n(v, "Bearer ", "", 1);
791 xs *token = token_get(tokid); 848 xs *token = token_get(tokid);
792 849
793 if (token != NULL) { 850 if (token != NULL) {
@@ -815,6 +872,8 @@ int process_auth_token(snac *snac, const xs_dict *req)
815int mastoapi_get_handler(const xs_dict *req, const char *q_path, 872int mastoapi_get_handler(const xs_dict *req, const char *q_path,
816 char **body, int *b_size, char **ctype) 873 char **body, int *b_size, char **ctype)
817{ 874{
875 (void)b_size;
876
818 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 877 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
819 return 0; 878 return 0;
820 879
@@ -826,7 +885,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
826 885
827 int status = 404; 886 int status = 404;
828 xs_dict *args = xs_dict_get(req, "q_vars"); 887 xs_dict *args = xs_dict_get(req, "q_vars");
829 xs *cmd = xs_replace(q_path, "/api", ""); 888 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
830 889
831 snac snac1 = {0}; 890 snac snac1 = {0};
832 int logged_in = process_auth_token(&snac1, req); 891 int logged_in = process_auth_token(&snac1, req);
@@ -1036,11 +1095,14 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1036 } 1095 }
1037 else 1096 else
1038 if (strcmp(cmd, "/v1/timelines/public") == 0) { 1097 if (strcmp(cmd, "/v1/timelines/public") == 0) {
1039 /* the public timeline (public timelines for all users) */ 1098 /* the instance public timeline (public timelines for all users) */
1040 1099
1041 /* this is an ugly kludge: first users in the list get all the fame */ 1100 /* NOTE: this api call needs no authorization; but,
1101 I need a logged-in user in mastoapi_status() for
1102 is_msg_public() and the liked/boosted flags,
1103 so it will silently fail for pure public access */
1042 1104
1043 const char *limit_s = xs_dict_get(args, "limit"); 1105 const char *limit_s = xs_dict_get(args, "limit");
1044 int limit = 0; 1106 int limit = 0;
1045 int cnt = 0; 1107 int cnt = 0;
1046 1108
@@ -1050,44 +1112,28 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1050 if (limit == 0) 1112 if (limit == 0)
1051 limit = 20; 1113 limit = 20;
1052 1114
1053 xs *out = xs_list_new(); 1115 xs *timeline = timeline_instance_list(0, limit);
1054 xs *users = user_list(); 1116 xs *out = xs_list_new();
1055 xs_list *p = users; 1117 xs_list *p = timeline;
1056 xs_str *uid; 1118 xs_str *md5;
1057
1058 while (xs_list_iter(&p, &uid) && cnt < limit) {
1059 snac user;
1060
1061 if (user_open(&user, uid)) {
1062 xs *timeline = timeline_simple_list(&user, "public", 0, 4);
1063 xs_list *p2 = timeline;
1064 xs_str *v;
1065
1066 while (xs_list_iter(&p2, &v) && cnt < limit) {
1067 xs *msg = NULL;
1068 1119
1069 /* get the entry */ 1120 while (logged_in && xs_list_iter(&p, &md5) && cnt < limit) {
1070 if (!valid_status(timeline_get_by_md5(&user, v, &msg))) 1121 xs *msg = NULL;
1071 continue;
1072
1073 /* discard non-Notes */
1074 if (strcmp(xs_dict_get(msg, "type"), "Note") != 0)
1075 continue;
1076 1122
1077 /* discard entries not by this user */ 1123 /* get the entry */
1078 if (!xs_startswith(xs_dict_get(msg, "id"), user.actor)) 1124 if (!valid_status(object_get_by_md5(md5, &msg)))
1079 continue; 1125 continue;
1080 1126
1081 /* convert the Note into a Mastodon status */ 1127 /* discard non-Notes */
1082 xs *st = mastoapi_status(&user, msg); 1128 if (strcmp(xs_dict_get(msg, "type"), "Note") != 0)
1129 continue;
1083 1130
1084 if (st != NULL) { 1131 /* convert the Note into a Mastodon status */
1085 out = xs_list_append(out, st); 1132 xs *st = mastoapi_status(&snac1, msg);
1086 cnt++;
1087 }
1088 }
1089 1133
1090 user_free(&user); 1134 if (st != NULL) {
1135 out = xs_list_append(out, st);
1136 cnt++;
1091 } 1137 }
1092 } 1138 }
1093 1139
@@ -1121,7 +1167,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1121 xs *actor = NULL; 1167 xs *actor = NULL;
1122 xs *entry = NULL; 1168 xs *entry = NULL;
1123 1169
1124 if (!valid_status(object_get(xs_dict_get(noti, "actor"), &actor))) 1170 if (!valid_status(actor_get(&snac1, xs_dict_get(noti, "actor"), &actor)))
1125 continue; 1171 continue;
1126 1172
1127 if (objid != NULL && !valid_status(object_get(objid, &entry))) 1173 if (objid != NULL && !valid_status(object_get(objid, &entry)))
@@ -1287,25 +1333,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1287 cfg = xs_dict_append(cfg, "statuses", d11); 1333 cfg = xs_dict_append(cfg, "statuses", d11);
1288 } 1334 }
1289 1335
1290 {
1291 xs *d11 = xs_dict_new();
1292 xs *mt = xs_list_new();
1293
1294 mt = xs_list_append(mt, "image/jpeg");
1295 mt = xs_list_append(mt, "image/png");
1296 mt = xs_list_append(mt, "image/gif");
1297
1298 d11 = xs_dict_append(d11, "supported_mime_types", mt);
1299
1300 d11 = xs_dict_append(d11, "image_size_limit", z);
1301 d11 = xs_dict_append(d11, "image_matrix_limit", z);
1302 d11 = xs_dict_append(d11, "video_size_limit", z);
1303 d11 = xs_dict_append(d11, "video_matrix_limit", z);
1304 d11 = xs_dict_append(d11, "video_frame_rate_limit", z);
1305
1306 cfg = xs_dict_append(cfg, "media_attachments", d11);
1307 }
1308
1309 ins = xs_dict_append(ins, "configuration", cfg); 1336 ins = xs_dict_append(ins, "configuration", cfg);
1310 1337
1311 *body = xs_json_dumps_pp(ins, 4); 1338 *body = xs_json_dumps_pp(ins, 4);
@@ -1326,7 +1353,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1326 /* skip the 'fake' part of the id */ 1353 /* skip the 'fake' part of the id */
1327 id = MID_TO_MD5(id); 1354 id = MID_TO_MD5(id);
1328 1355
1329 if (valid_status(timeline_get_by_md5(&snac1, id, &msg))) { 1356 if (valid_status(object_get_by_md5(id, &msg))) {
1330 if (op == NULL) { 1357 if (op == NULL) {
1331 if (!is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) { 1358 if (!is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) {
1332 /* return the status itself */ 1359 /* return the status itself */
@@ -1487,6 +1514,9 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1487 const char *payload, int p_size, 1514 const char *payload, int p_size,
1488 char **body, int *b_size, char **ctype) 1515 char **body, int *b_size, char **ctype)
1489{ 1516{
1517 (void)p_size;
1518 (void)b_size;
1519
1490 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 1520 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
1491 return 0; 1521 return 0;
1492 1522
@@ -1513,7 +1543,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1513 printf("%s\n", j); 1543 printf("%s\n", j);
1514 }*/ 1544 }*/
1515 1545
1516 xs *cmd = xs_replace(q_path, "/api", ""); 1546 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
1517 1547
1518 snac snac = {0}; 1548 snac snac = {0};
1519 int logged_in = process_auth_token(&snac, req); 1549 int logged_in = process_auth_token(&snac, req);
@@ -1562,7 +1592,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1562 1592
1563 app_add(cid, app); 1593 app_add(cid, app);
1564 1594
1565 srv_debug(0, xs_fmt("mastoapi apps: new app %s", cid)); 1595 srv_debug(1, xs_fmt("mastoapi apps: new app %s", cid));
1566 } 1596 }
1567 } 1597 }
1568 else 1598 else
@@ -1582,6 +1612,9 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1582 if (xs_is_null(media_ids)) 1612 if (xs_is_null(media_ids))
1583 media_ids = xs_dict_get(args, "media_ids[]"); 1613 media_ids = xs_dict_get(args, "media_ids[]");
1584 1614
1615 if (xs_is_null(visibility))
1616 visibility = "public";
1617
1585 xs *attach_list = xs_list_new(); 1618 xs *attach_list = xs_list_new();
1586 xs *irt = NULL; 1619 xs *irt = NULL;
1587 1620
@@ -1683,7 +1716,11 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1683 } 1716 }
1684 else 1717 else
1685 if (strcmp(op, "unfavourite") == 0) { 1718 if (strcmp(op, "unfavourite") == 0) {
1686 /* snac does not support Undo+Like */ 1719 /* partial support: as the original Like message
1720 is not stored anywhere here, it's not possible
1721 to send an Undo + Like; the only thing done here
1722 is to delete the actor from the list of likes */
1723 object_unadmire(id, snac.actor, 1);
1687 } 1724 }
1688 else 1725 else
1689 if (strcmp(op, "reblog") == 0) { 1726 if (strcmp(op, "reblog") == 0) {
@@ -1698,7 +1735,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
1698 } 1735 }
1699 else 1736 else
1700 if (strcmp(op, "unreblog") == 0) { 1737 if (strcmp(op, "unreblog") == 0) {
1701 /* snac does not support Undo+Announce */ 1738 /* partial support: see comment in 'unfavourite' */
1739 object_unadmire(id, snac.actor, 0);
1702 } 1740 }
1703 else 1741 else
1704 if (strcmp(op, "bookmark") == 0) { 1742 if (strcmp(op, "bookmark") == 0) {
@@ -1903,6 +1941,9 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
1903 const char *payload, int p_size, 1941 const char *payload, int p_size,
1904 char **body, int *b_size, char **ctype) 1942 char **body, int *b_size, char **ctype)
1905{ 1943{
1944 (void)p_size;
1945 (void)b_size;
1946
1906 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 1947 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
1907 return 0; 1948 return 0;
1908 1949
@@ -1924,7 +1965,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
1924 if (args == NULL) 1965 if (args == NULL)
1925 return 400; 1966 return 400;
1926 1967
1927 xs *cmd = xs_replace(q_path, "/api", ""); 1968 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
1928 1969
1929 snac snac = {0}; 1970 snac snac = {0};
1930 int logged_in = process_auth_token(&snac, req); 1971 int logged_in = process_auth_token(&snac, req);