diff options
Diffstat (limited to 'mastoapi.c')
| -rw-r--r-- | mastoapi.c | 147 |
1 files changed, 128 insertions, 19 deletions
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "xs_url.h" | 15 | #include "xs_url.h" |
| 16 | #include "xs_mime.h" | 16 | #include "xs_mime.h" |
| 17 | #include "xs_match.h" | 17 | #include "xs_match.h" |
| 18 | #include "xs_unicode.h" | ||
| 18 | 19 | ||
| 19 | #include "snac.h" | 20 | #include "snac.h" |
| 20 | 21 | ||
| @@ -381,7 +382,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 381 | } | 382 | } |
| 382 | } | 383 | } |
| 383 | 384 | ||
| 384 | /* no code? | 385 | /* no code? |
| 385 | I'm not sure of the impacts of this right now, but Subway Tooter does not | 386 | I'm not sure of the impacts of this right now, but Subway Tooter does not |
| 386 | provide a code so one must be generated */ | 387 | provide a code so one must be generated */ |
| 387 | if (xs_is_null(code)){ | 388 | if (xs_is_null(code)){ |
| @@ -1133,9 +1134,14 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) | |||
| 1133 | bst = xs_dict_set(bst, "content", ""); | 1134 | bst = xs_dict_set(bst, "content", ""); |
| 1134 | bst = xs_dict_set(bst, "reblog", st); | 1135 | bst = xs_dict_set(bst, "reblog", st); |
| 1135 | 1136 | ||
| 1137 | xs *b_id = xs_toupper_i(xs_dup(xs_dict_get(st, "id"))); | ||
| 1138 | bst = xs_dict_set(bst, "id", b_id); | ||
| 1139 | |||
| 1136 | xs_free(st); | 1140 | xs_free(st); |
| 1137 | st = bst; | 1141 | st = bst; |
| 1138 | } | 1142 | } |
| 1143 | else | ||
| 1144 | xs_free(bst); | ||
| 1139 | } | 1145 | } |
| 1140 | 1146 | ||
| 1141 | return st; | 1147 | return st; |
| @@ -1338,9 +1344,9 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn | |||
| 1338 | if ((f = fopen(index_fn, "r")) == NULL) | 1344 | if ((f = fopen(index_fn, "r")) == NULL) |
| 1339 | return out; | 1345 | return out; |
| 1340 | 1346 | ||
| 1341 | const char *max_id = xs_dict_get(args, "max_id"); | 1347 | const char *o_max_id = xs_dict_get(args, "max_id"); |
| 1342 | const char *since_id = xs_dict_get(args, "since_id"); | 1348 | const char *o_since_id = xs_dict_get(args, "since_id"); |
| 1343 | const char *min_id = xs_dict_get(args, "min_id"); /* unsupported old-to-new navigation */ | 1349 | const char *o_min_id = xs_dict_get(args, "min_id"); /* unsupported old-to-new navigation */ |
| 1344 | const char *limit_s = xs_dict_get(args, "limit"); | 1350 | const char *limit_s = xs_dict_get(args, "limit"); |
| 1345 | int (*iterator)(FILE *, char *); | 1351 | int (*iterator)(FILE *, char *); |
| 1346 | int initial_status = 0; | 1352 | int initial_status = 0; |
| @@ -1348,6 +1354,10 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn | |||
| 1348 | int limit = 0; | 1354 | int limit = 0; |
| 1349 | int cnt = 0; | 1355 | int cnt = 0; |
| 1350 | 1356 | ||
| 1357 | xs *max_id = o_max_id ? xs_tolower_i(xs_dup(o_max_id)) : NULL; | ||
| 1358 | xs *since_id = o_since_id ? xs_tolower_i(xs_dup(o_since_id)) : NULL; | ||
| 1359 | xs *min_id = o_min_id ? xs_tolower_i(xs_dup(o_min_id)) : NULL; | ||
| 1360 | |||
| 1351 | if (!xs_is_null(limit_s)) | 1361 | if (!xs_is_null(limit_s)) |
| 1352 | limit = atoi(limit_s); | 1362 | limit = atoi(limit_s); |
| 1353 | 1363 | ||
| @@ -1371,7 +1381,7 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn | |||
| 1371 | /* only return entries older that max_id */ | 1381 | /* only return entries older that max_id */ |
| 1372 | if (max_id) { | 1382 | if (max_id) { |
| 1373 | if (strcmp(md5, MID_TO_MD5(max_id)) == 0) { | 1383 | if (strcmp(md5, MID_TO_MD5(max_id)) == 0) { |
| 1374 | max_id = NULL; | 1384 | max_id = xs_free(max_id); |
| 1375 | if (ascending) | 1385 | if (ascending) |
| 1376 | break; | 1386 | break; |
| 1377 | } | 1387 | } |
| @@ -1384,7 +1394,7 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn | |||
| 1384 | if (strcmp(md5, MID_TO_MD5(since_id)) == 0) { | 1394 | if (strcmp(md5, MID_TO_MD5(since_id)) == 0) { |
| 1385 | if (!ascending) | 1395 | if (!ascending) |
| 1386 | break; | 1396 | break; |
| 1387 | since_id = NULL; | 1397 | since_id = xs_free(since_id); |
| 1388 | } | 1398 | } |
| 1389 | if (ascending) | 1399 | if (ascending) |
| 1390 | continue; | 1400 | continue; |
| @@ -1637,7 +1647,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1637 | const char *aq = xs_dict_get(args, "q"); | 1647 | const char *aq = xs_dict_get(args, "q"); |
| 1638 | 1648 | ||
| 1639 | if (!xs_is_null(aq)) { | 1649 | if (!xs_is_null(aq)) { |
| 1640 | xs *q = xs_tolower_i(xs_dup(aq)); | 1650 | xs *q = xs_utf8_to_lower(aq); |
| 1641 | out = xs_list_new(); | 1651 | out = xs_list_new(); |
| 1642 | xs *wing = following_list(&snac1); | 1652 | xs *wing = following_list(&snac1); |
| 1643 | xs *wers = follower_list(&snac1); | 1653 | xs *wers = follower_list(&snac1); |
| @@ -1780,7 +1790,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1780 | } | 1790 | } |
| 1781 | else | 1791 | else |
| 1782 | if (strcmp(opt, "statuses") == 0) { | 1792 | if (strcmp(opt, "statuses") == 0) { |
| 1783 | /* we don't serve statuses of others; return the empty list */ | 1793 | /* we don't serve statuses of others; return the empty list */ |
| 1784 | out = xs_list_new(); | 1794 | out = xs_list_new(); |
| 1785 | } | 1795 | } |
| 1786 | else | 1796 | else |
| @@ -1999,7 +2009,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1999 | } | 2009 | } |
| 2000 | else | 2010 | else |
| 2001 | if (strcmp(cmd, "/v2/filters") == 0) { /** **/ | 2011 | if (strcmp(cmd, "/v2/filters") == 0) { /** **/ |
| 2002 | /* snac will never have filters | 2012 | /* snac will never have filters |
| 2003 | * but still, without a v2 endpoint a short delay is introduced | 2013 | * but still, without a v2 endpoint a short delay is introduced |
| 2004 | * in some apps */ | 2014 | * in some apps */ |
| 2005 | *body = xs_dup("[]"); | 2015 | *body = xs_dup("[]"); |
| @@ -2331,19 +2341,36 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2331 | status = HTTP_STATUS_OK; | 2341 | status = HTTP_STATUS_OK; |
| 2332 | } | 2342 | } |
| 2333 | else | 2343 | else |
| 2344 | if (strcmp(cmd, "/v1/instance/extended_description") == 0) { /** **/ | ||
| 2345 | xs *d = xs_dict_new(); | ||
| 2346 | xs *greeting = xs_fmt("%s/greeting.html", srv_basedir); | ||
| 2347 | time_t t = mtime(greeting); | ||
| 2348 | xs *updated_at = xs_str_iso_date(t); | ||
| 2349 | xs *content = xs_replace(snac_blurb, "%host%", xs_dict_get(srv_config, "host")); | ||
| 2350 | |||
| 2351 | d = xs_dict_set(d, "updated_at", updated_at); | ||
| 2352 | d = xs_dict_set(d, "content", content); | ||
| 2353 | |||
| 2354 | *body = xs_json_dumps(d, 4); | ||
| 2355 | *ctype = "application/json"; | ||
| 2356 | status = HTTP_STATUS_OK; | ||
| 2357 | } | ||
| 2358 | else | ||
| 2334 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ | 2359 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ |
| 2335 | /* information about a status */ | 2360 | /* information about a status */ |
| 2336 | if (logged_in) { | 2361 | if (logged_in) { |
| 2337 | xs *l = xs_split(cmd, "/"); | 2362 | xs *l = xs_split(cmd, "/"); |
| 2338 | const char *id = xs_list_get(l, 3); | 2363 | const char *oid = xs_list_get(l, 3); |
| 2339 | const char *op = xs_list_get(l, 4); | 2364 | const char *op = xs_list_get(l, 4); |
| 2340 | 2365 | ||
| 2341 | if (!xs_is_null(id)) { | 2366 | if (!xs_is_null(oid)) { |
| 2342 | xs *msg = NULL; | 2367 | xs *msg = NULL; |
| 2343 | xs *out = NULL; | 2368 | xs *out = NULL; |
| 2344 | 2369 | ||
| 2345 | /* skip the 'fake' part of the id */ | 2370 | /* skip the 'fake' part of the id */ |
| 2346 | id = MID_TO_MD5(id); | 2371 | oid = MID_TO_MD5(oid); |
| 2372 | |||
| 2373 | xs *id = xs_tolower_i(xs_dup(oid)); | ||
| 2347 | 2374 | ||
| 2348 | if (valid_status(object_get_by_md5(id, &msg))) { | 2375 | if (valid_status(object_get_by_md5(id, &msg))) { |
| 2349 | if (op == NULL) { | 2376 | if (op == NULL) { |
| @@ -2459,7 +2486,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2459 | if (logged_in) { | 2486 | if (logged_in) { |
| 2460 | const xs_list *timeline = xs_dict_get(args, "timeline[]"); | 2487 | const xs_list *timeline = xs_dict_get(args, "timeline[]"); |
| 2461 | xs_str *json = NULL; | 2488 | xs_str *json = NULL; |
| 2462 | if (!xs_is_null(timeline)) | 2489 | if (!xs_is_null(timeline)) |
| 2463 | json = xs_json_dumps(markers_get(&snac1, timeline), 4); | 2490 | json = xs_json_dumps(markers_get(&snac1, timeline), 4); |
| 2464 | 2491 | ||
| 2465 | if (!xs_is_null(json)) | 2492 | if (!xs_is_null(json)) |
| @@ -2475,6 +2502,40 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2475 | } | 2502 | } |
| 2476 | else | 2503 | else |
| 2477 | if (strcmp(cmd, "/v1/followed_tags") == 0) { /** **/ | 2504 | if (strcmp(cmd, "/v1/followed_tags") == 0) { /** **/ |
| 2505 | if (logged_in) { | ||
| 2506 | xs *r = xs_list_new(); | ||
| 2507 | const xs_list *followed_hashtags = xs_dict_get_def(snac1.config, | ||
| 2508 | "followed_hashtags", xs_stock(XSTYPE_LIST)); | ||
| 2509 | const char *hashtag; | ||
| 2510 | |||
| 2511 | xs_list_foreach(followed_hashtags, hashtag) { | ||
| 2512 | if (*hashtag == '#') { | ||
| 2513 | xs *d = xs_dict_new(); | ||
| 2514 | xs *s = xs_fmt("%s?t=%s", srv_baseurl, hashtag + 1); | ||
| 2515 | |||
| 2516 | d = xs_dict_set(d, "name", hashtag + 1); | ||
| 2517 | d = xs_dict_set(d, "url", s); | ||
| 2518 | d = xs_dict_set(d, "history", xs_stock(XSTYPE_LIST)); | ||
| 2519 | |||
| 2520 | r = xs_list_append(r, d); | ||
| 2521 | } | ||
| 2522 | } | ||
| 2523 | |||
| 2524 | *body = xs_json_dumps(r, 4); | ||
| 2525 | *ctype = "application/json"; | ||
| 2526 | status = HTTP_STATUS_OK; | ||
| 2527 | } | ||
| 2528 | else | ||
| 2529 | status = HTTP_STATUS_UNAUTHORIZED; | ||
| 2530 | } | ||
| 2531 | else | ||
| 2532 | if (strcmp(cmd, "/v1/blocks") == 0) { /** **/ | ||
| 2533 | *body = xs_dup("[]"); | ||
| 2534 | *ctype = "application/json"; | ||
| 2535 | status = HTTP_STATUS_OK; | ||
| 2536 | } | ||
| 2537 | else | ||
| 2538 | if (strcmp(cmd, "/v1/mutes") == 0) { /** **/ | ||
| 2478 | *body = xs_dup("[]"); | 2539 | *body = xs_dup("[]"); |
| 2479 | *ctype = "application/json"; | 2540 | *ctype = "application/json"; |
| 2480 | status = HTTP_STATUS_OK; | 2541 | status = HTTP_STATUS_OK; |
| @@ -2507,9 +2568,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 2507 | /* reply something only for offset 0; otherwise, | 2568 | /* reply something only for offset 0; otherwise, |
| 2508 | apps like Tusky keep asking again and again */ | 2569 | apps like Tusky keep asking again and again */ |
| 2509 | if (xs_startswith(q, "https://")) { | 2570 | if (xs_startswith(q, "https://")) { |
| 2510 | xs *md5 = xs_md5_hex(q, strlen(q)); | 2571 | if (!timeline_here(&snac1, q)) { |
| 2511 | |||
| 2512 | if (!timeline_here(&snac1, md5)) { | ||
| 2513 | xs *object = NULL; | 2572 | xs *object = NULL; |
| 2514 | int status; | 2573 | int status; |
| 2515 | 2574 | ||
| @@ -2979,8 +3038,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 2979 | 3038 | ||
| 2980 | if (*fn != '\0') { | 3039 | if (*fn != '\0') { |
| 2981 | char *ext = strrchr(fn, '.'); | 3040 | char *ext = strrchr(fn, '.'); |
| 2982 | xs *hash = xs_md5_hex(fn, strlen(fn)); | 3041 | char rnd[32]; |
| 2983 | xs *id = xs_fmt("%s%s", hash, ext); | 3042 | xs_rnd_buf(rnd, sizeof(rnd)); |
| 3043 | xs *hash = xs_md5_hex(rnd, sizeof(rnd)); | ||
| 3044 | xs *id = xs_fmt("post-%s%s", hash, ext ? ext : ""); | ||
| 2984 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); | 3045 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); |
| 2985 | int fo = xs_number_get(xs_list_get(file, 1)); | 3046 | int fo = xs_number_get(xs_list_get(file, 1)); |
| 2986 | int fs = xs_number_get(xs_list_get(file, 2)); | 3047 | int fs = xs_number_get(xs_list_get(file, 2)); |
| @@ -3227,7 +3288,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3227 | if (!xs_is_null(home)) | 3288 | if (!xs_is_null(home)) |
| 3228 | home_marker = xs_dict_get(home, "last_read_id"); | 3289 | home_marker = xs_dict_get(home, "last_read_id"); |
| 3229 | } | 3290 | } |
| 3230 | 3291 | ||
| 3231 | const xs_str *notify_marker = xs_dict_get(args, "notifications[last_read_id]"); | 3292 | const xs_str *notify_marker = xs_dict_get(args, "notifications[last_read_id]"); |
| 3232 | if (xs_is_null(notify_marker)) { | 3293 | if (xs_is_null(notify_marker)) { |
| 3233 | const xs_dict *notify = xs_dict_get(args, "notifications"); | 3294 | const xs_dict *notify = xs_dict_get(args, "notifications"); |
| @@ -3296,6 +3357,54 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 3296 | } | 3357 | } |
| 3297 | } | 3358 | } |
| 3298 | else | 3359 | else |
| 3360 | if (xs_startswith(cmd, "/v1/tags/")) { /** **/ | ||
| 3361 | if (logged_in) { | ||
| 3362 | xs *l = xs_split(cmd, "/"); | ||
| 3363 | const char *i_tag = xs_list_get(l, 3); | ||
| 3364 | const char *cmd = xs_list_get(l, 4); | ||
| 3365 | |||
| 3366 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; | ||
| 3367 | |||
| 3368 | if (xs_is_string(i_tag) && xs_is_string(cmd)) { | ||
| 3369 | int ok = 0; | ||
| 3370 | |||
| 3371 | xs *tag = xs_fmt("#%s", i_tag); | ||
| 3372 | xs *followed_hashtags = xs_dup(xs_dict_get_def(snac.config, | ||
| 3373 | "followed_hashtags", xs_stock(XSTYPE_LIST))); | ||
| 3374 | |||
| 3375 | if (strcmp(cmd, "follow") == 0) { | ||
| 3376 | followed_hashtags = xs_list_append(followed_hashtags, tag); | ||
| 3377 | ok = 1; | ||
| 3378 | } | ||
| 3379 | else | ||
| 3380 | if (strcmp(cmd, "unfollow") == 0) { | ||
| 3381 | int off = xs_list_in(followed_hashtags, tag); | ||
| 3382 | |||
| 3383 | if (off != -1) | ||
| 3384 | followed_hashtags = xs_list_del(followed_hashtags, off); | ||
| 3385 | |||
| 3386 | ok = 1; | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | if (ok) { | ||
| 3390 | /* update */ | ||
| 3391 | xs_dict_set(snac.config, "followed_hashtags", followed_hashtags); | ||
| 3392 | user_persist(&snac, 0); | ||
| 3393 | |||
| 3394 | xs *d = xs_dict_new(); | ||
| 3395 | xs *s = xs_fmt("%s?t=%s", srv_baseurl, i_tag); | ||
| 3396 | d = xs_dict_set(d, "name", i_tag); | ||
| 3397 | d = xs_dict_set(d, "url", s); | ||
| 3398 | d = xs_dict_set(d, "history", xs_stock(XSTYPE_LIST)); | ||
| 3399 | |||
| 3400 | *body = xs_json_dumps(d, 4); | ||
| 3401 | *ctype = "application/json"; | ||
| 3402 | status = HTTP_STATUS_OK; | ||
| 3403 | } | ||
| 3404 | } | ||
| 3405 | } | ||
| 3406 | } | ||
| 3407 | else | ||
| 3299 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; | 3408 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; |
| 3300 | 3409 | ||
| 3301 | /* user cleanup */ | 3410 | /* user cleanup */ |