summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
authorGravatar Oliver2025-07-16 20:21:20 +0200
committerGravatar Oliver2025-07-16 20:21:20 +0200
commit3554a73aa50526631961efcca78c6c8eb2a16911 (patch)
treefc4c21a65337ab8ecb4fb00aa551ee21997faf6f /mastoapi.c
parentpo/de_DE.po aktualisiert (diff)
parentUpdated RELEASE_NOTES. (diff)
downloadpenes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.tar.gz
penes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.tar.xz
penes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.zip
Merge pull request 'master' (#9) from grunfink/snac2:master into master
Reviewed-on: https://codeberg.org/zen/snac2/pulls/9
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c147
1 files changed, 128 insertions, 19 deletions
diff --git a/mastoapi.c b/mastoapi.c
index a7d9c34..7fb995a 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -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 */