diff options
| author | 2023-06-11 07:05:07 +0000 | |
|---|---|---|
| committer | 2023-06-11 07:05:07 +0000 | |
| commit | 422b3148aecc5e3fdd94f5a9655de191766e10f6 (patch) | |
| tree | 70b00c4361a45f223dc2c7a2acfa2697e446c0a4 | |
| parent | Updated TODO. (diff) | |
| parent | Only allow logged-in user to use statuses API (diff) | |
| download | snac2-422b3148aecc5e3fdd94f5a9655de191766e10f6.tar.gz snac2-422b3148aecc5e3fdd94f5a9655de191766e10f6.tar.xz snac2-422b3148aecc5e3fdd94f5a9655de191766e10f6.zip | |
Merge pull request 'Mastodon API private timeline fixes' (#44) from poesty/snac2:master into master
Reviewed-on: https://codeberg.org/grunfink/snac2/pulls/44
| -rw-r--r-- | mastoapi.c | 211 |
1 files changed, 112 insertions, 99 deletions
| @@ -1172,7 +1172,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1172 | 1172 | ||
| 1173 | /* only return entries older that max_id */ | 1173 | /* only return entries older that max_id */ |
| 1174 | if (max_id) { | 1174 | if (max_id) { |
| 1175 | if (strcmp(v, max_id) == 0) | 1175 | if (strcmp(v, MID_TO_MD5(max_id)) == 0) |
| 1176 | max_id = NULL; | 1176 | max_id = NULL; |
| 1177 | 1177 | ||
| 1178 | continue; | 1178 | continue; |
| @@ -1180,14 +1180,14 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1180 | 1180 | ||
| 1181 | /* only returns entries newer than since_id */ | 1181 | /* only returns entries newer than since_id */ |
| 1182 | if (since_id) { | 1182 | if (since_id) { |
| 1183 | if (strcmp(v, since_id) == 0) | 1183 | if (strcmp(v, MID_TO_MD5(since_id)) == 0) |
| 1184 | break; | 1184 | break; |
| 1185 | } | 1185 | } |
| 1186 | 1186 | ||
| 1187 | /* only returns entries newer than min_id */ | 1187 | /* only returns entries newer than min_id */ |
| 1188 | /* what does really "Return results immediately newer than ID" mean? */ | 1188 | /* what does really "Return results immediately newer than ID" mean? */ |
| 1189 | if (min_id) { | 1189 | if (min_id) { |
| 1190 | if (strcmp(v, min_id) == 0) | 1190 | if (strcmp(v, MID_TO_MD5(min_id)) == 0) |
| 1191 | break; | 1191 | break; |
| 1192 | } | 1192 | } |
| 1193 | 1193 | ||
| @@ -1200,6 +1200,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1200 | if (strcmp(type, "Note") != 0 && strcmp(type, "Question") != 0) | 1200 | if (strcmp(type, "Note") != 0 && strcmp(type, "Question") != 0) |
| 1201 | continue; | 1201 | continue; |
| 1202 | 1202 | ||
| 1203 | /* discard notes from people we don't follow with no boosts */ | ||
| 1204 | if (!following_check(&snac1, xs_dict_get(msg, "attributedTo")) && | ||
| 1205 | object_announces_len(xs_dict_get(msg, "id")) == 0) | ||
| 1206 | continue; | ||
| 1207 | |||
| 1203 | /* discard notes from muted morons */ | 1208 | /* discard notes from muted morons */ |
| 1204 | if (is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) | 1209 | if (is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) |
| 1205 | continue; | 1210 | continue; |
| @@ -1505,102 +1510,106 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1505 | else | 1510 | else |
| 1506 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ | 1511 | if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ |
| 1507 | /* information about a status */ | 1512 | /* information about a status */ |
| 1508 | xs *l = xs_split(cmd, "/"); | 1513 | if (logged_in) { |
| 1509 | const char *id = xs_list_get(l, 3); | 1514 | xs *l = xs_split(cmd, "/"); |
| 1510 | const char *op = xs_list_get(l, 4); | 1515 | const char *id = xs_list_get(l, 3); |
| 1516 | const char *op = xs_list_get(l, 4); | ||
| 1511 | 1517 | ||
| 1512 | if (!xs_is_null(id)) { | 1518 | if (!xs_is_null(id)) { |
| 1513 | xs *msg = NULL; | 1519 | xs *msg = NULL; |
| 1514 | xs *out = NULL; | 1520 | xs *out = NULL; |
| 1515 | 1521 | ||
| 1516 | /* skip the 'fake' part of the id */ | 1522 | /* skip the 'fake' part of the id */ |
| 1517 | id = MID_TO_MD5(id); | 1523 | id = MID_TO_MD5(id); |
| 1518 | 1524 | ||
| 1519 | if (valid_status(object_get_by_md5(id, &msg))) { | 1525 | if (valid_status(object_get_by_md5(id, &msg))) { |
| 1520 | if (op == NULL) { | 1526 | if (op == NULL) { |
| 1521 | if (!is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) { | 1527 | if (!is_muted(&snac1, xs_dict_get(msg, "attributedTo"))) { |
| 1522 | /* return the status itself */ | 1528 | /* return the status itself */ |
| 1523 | out = mastoapi_status(&snac1, msg); | 1529 | out = mastoapi_status(&snac1, msg); |
| 1530 | } | ||
| 1524 | } | 1531 | } |
| 1525 | } | 1532 | else |
| 1526 | else | 1533 | if (strcmp(op, "context") == 0) { /** **/ |
| 1527 | if (strcmp(op, "context") == 0) { /** **/ | 1534 | /* return ancestors and children */ |
| 1528 | /* return ancestors and children */ | 1535 | xs *anc = xs_list_new(); |
| 1529 | xs *anc = xs_list_new(); | 1536 | xs *des = xs_list_new(); |
| 1530 | xs *des = xs_list_new(); | 1537 | xs_list *p; |
| 1531 | xs_list *p; | 1538 | xs_str *v; |
| 1532 | xs_str *v; | 1539 | char pid[64]; |
| 1533 | char pid[64]; | ||
| 1534 | 1540 | ||
| 1535 | /* build the [grand]parent list, moving up */ | 1541 | /* build the [grand]parent list, moving up */ |
| 1536 | strncpy(pid, id, sizeof(pid)); | 1542 | strncpy(pid, id, sizeof(pid)); |
| 1537 | 1543 | ||
| 1538 | while (object_parent(pid, pid, sizeof(pid))) { | 1544 | while (object_parent(pid, pid, sizeof(pid))) { |
| 1539 | xs *m2 = NULL; | 1545 | xs *m2 = NULL; |
| 1540 | 1546 | ||
| 1541 | if (valid_status(timeline_get_by_md5(&snac1, pid, &m2))) { | 1547 | if (valid_status(timeline_get_by_md5(&snac1, pid, &m2))) { |
| 1542 | xs *st = mastoapi_status(&snac1, m2); | 1548 | xs *st = mastoapi_status(&snac1, m2); |
| 1543 | anc = xs_list_insert(anc, 0, st); | 1549 | anc = xs_list_insert(anc, 0, st); |
| 1550 | } | ||
| 1551 | else | ||
| 1552 | break; | ||
| 1544 | } | 1553 | } |
| 1545 | else | ||
| 1546 | break; | ||
| 1547 | } | ||
| 1548 | 1554 | ||
| 1549 | /* build the children list */ | 1555 | /* build the children list */ |
| 1550 | xs *children = object_children(xs_dict_get(msg, "id")); | 1556 | xs *children = object_children(xs_dict_get(msg, "id")); |
| 1551 | p = children; | 1557 | p = children; |
| 1552 | 1558 | ||
| 1553 | while (xs_list_iter(&p, &v)) { | 1559 | while (xs_list_iter(&p, &v)) { |
| 1554 | xs *m2 = NULL; | 1560 | xs *m2 = NULL; |
| 1555 | 1561 | ||
| 1556 | if (valid_status(timeline_get_by_md5(&snac1, v, &m2))) { | 1562 | if (valid_status(timeline_get_by_md5(&snac1, v, &m2))) { |
| 1557 | if (xs_is_null(xs_dict_get(m2, "name"))) { | 1563 | if (xs_is_null(xs_dict_get(m2, "name"))) { |
| 1558 | xs *st = mastoapi_status(&snac1, m2); | 1564 | xs *st = mastoapi_status(&snac1, m2); |
| 1559 | des = xs_list_append(des, st); | 1565 | des = xs_list_append(des, st); |
| 1566 | } | ||
| 1560 | } | 1567 | } |
| 1561 | } | 1568 | } |
| 1562 | } | ||
| 1563 | 1569 | ||
| 1564 | out = xs_dict_new(); | 1570 | out = xs_dict_new(); |
| 1565 | out = xs_dict_append(out, "ancestors", anc); | 1571 | out = xs_dict_append(out, "ancestors", anc); |
| 1566 | out = xs_dict_append(out, "descendants", des); | 1572 | out = xs_dict_append(out, "descendants", des); |
| 1567 | } | 1573 | } |
| 1568 | else | 1574 | else |
| 1569 | if (strcmp(op, "reblogged_by") == 0 || /** **/ | 1575 | if (strcmp(op, "reblogged_by") == 0 || /** **/ |
| 1570 | strcmp(op, "favourited_by") == 0) { /** **/ | 1576 | strcmp(op, "favourited_by") == 0) { /** **/ |
| 1571 | /* return the list of people who liked or boosted this */ | 1577 | /* return the list of people who liked or boosted this */ |
| 1572 | out = xs_list_new(); | 1578 | out = xs_list_new(); |
| 1573 | 1579 | ||
| 1574 | xs *l = NULL; | 1580 | xs *l = NULL; |
| 1575 | 1581 | ||
| 1576 | if (op[0] == 'r') | 1582 | if (op[0] == 'r') |
| 1577 | l = object_announces(xs_dict_get(msg, "id")); | 1583 | l = object_announces(xs_dict_get(msg, "id")); |
| 1578 | else | 1584 | else |
| 1579 | l = object_likes(xs_dict_get(msg, "id")); | 1585 | l = object_likes(xs_dict_get(msg, "id")); |
| 1580 | 1586 | ||
| 1581 | xs_list *p = l; | 1587 | xs_list *p = l; |
| 1582 | xs_str *v; | 1588 | xs_str *v; |
| 1583 | 1589 | ||
| 1584 | while (xs_list_iter(&p, &v)) { | 1590 | while (xs_list_iter(&p, &v)) { |
| 1585 | xs *actor2 = NULL; | 1591 | xs *actor2 = NULL; |
| 1586 | 1592 | ||
| 1587 | if (valid_status(object_get_by_md5(v, &actor2))) { | 1593 | if (valid_status(object_get_by_md5(v, &actor2))) { |
| 1588 | xs *acct2 = mastoapi_account(actor2); | 1594 | xs *acct2 = mastoapi_account(actor2); |
| 1589 | 1595 | ||
| 1590 | out = xs_list_append(out, acct2); | 1596 | out = xs_list_append(out, acct2); |
| 1597 | } | ||
| 1591 | } | 1598 | } |
| 1592 | } | 1599 | } |
| 1593 | } | 1600 | } |
| 1594 | } | 1601 | else |
| 1595 | else | 1602 | srv_debug(1, xs_fmt("mastoapi status: bad id %s", id)); |
| 1596 | srv_debug(1, xs_fmt("mastoapi status: bad id %s", id)); | ||
| 1597 | 1603 | ||
| 1598 | if (out != NULL) { | 1604 | if (out != NULL) { |
| 1599 | *body = xs_json_dumps_pp(out, 4); | 1605 | *body = xs_json_dumps_pp(out, 4); |
| 1600 | *ctype = "application/json"; | 1606 | *ctype = "application/json"; |
| 1601 | status = 200; | 1607 | status = 200; |
| 1608 | } | ||
| 1602 | } | 1609 | } |
| 1603 | } | 1610 | } |
| 1611 | else | ||
| 1612 | status = 401; | ||
| 1604 | } | 1613 | } |
| 1605 | else | 1614 | else |
| 1606 | if (strcmp(cmd, "/v1/preferences") == 0) { /** **/ | 1615 | if (strcmp(cmd, "/v1/preferences") == 0) { /** **/ |
| @@ -1622,43 +1631,47 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1622 | } | 1631 | } |
| 1623 | else | 1632 | else |
| 1624 | if (strcmp(cmd, "/v2/search") == 0) { /** **/ | 1633 | if (strcmp(cmd, "/v2/search") == 0) { /** **/ |
| 1625 | const char *q = xs_dict_get(args, "q"); | 1634 | if (logged_in) { |
| 1626 | const char *type = xs_dict_get(args, "type"); | 1635 | const char *q = xs_dict_get(args, "q"); |
| 1627 | const char *offset = xs_dict_get(args, "offset"); | 1636 | const char *type = xs_dict_get(args, "type"); |
| 1637 | const char *offset = xs_dict_get(args, "offset"); | ||
| 1628 | 1638 | ||
| 1629 | xs *acl = xs_list_new(); | 1639 | xs *acl = xs_list_new(); |
| 1630 | xs *stl = xs_list_new(); | 1640 | xs *stl = xs_list_new(); |
| 1631 | xs *htl = xs_list_new(); | 1641 | xs *htl = xs_list_new(); |
| 1632 | xs *res = xs_dict_new(); | 1642 | xs *res = xs_dict_new(); |
| 1633 | 1643 | ||
| 1634 | if (xs_is_null(offset) || strcmp(offset, "0") == 0) { | 1644 | if (xs_is_null(offset) || strcmp(offset, "0") == 0) { |
| 1635 | /* reply something only for offset 0; otherwise, | 1645 | /* reply something only for offset 0; otherwise, |
| 1636 | apps like Tusky keep asking again and again */ | 1646 | apps like Tusky keep asking again and again */ |
| 1637 | 1647 | ||
| 1638 | if (!xs_is_null(q) && !xs_is_null(type) && strcmp(type, "accounts") == 0) { | 1648 | if (!xs_is_null(q) && !xs_is_null(type) && strcmp(type, "accounts") == 0) { |
| 1639 | /* do a webfinger query */ | 1649 | /* do a webfinger query */ |
| 1640 | char *actor = NULL; | 1650 | char *actor = NULL; |
| 1641 | char *user = NULL; | 1651 | char *user = NULL; |
| 1642 | 1652 | ||
| 1643 | if (valid_status(webfinger_request(q, &actor, &user))) { | 1653 | if (valid_status(webfinger_request(q, &actor, &user))) { |
| 1644 | xs *actor_o = NULL; | 1654 | xs *actor_o = NULL; |
| 1645 | 1655 | ||
| 1646 | if (valid_status(actor_request(&snac1, actor, &actor_o))) { | 1656 | if (valid_status(actor_request(&snac1, actor, &actor_o))) { |
| 1647 | xs *acct = mastoapi_account(actor_o); | 1657 | xs *acct = mastoapi_account(actor_o); |
| 1648 | 1658 | ||
| 1649 | acl = xs_list_append(acl, acct); | 1659 | acl = xs_list_append(acl, acct); |
| 1660 | } | ||
| 1650 | } | 1661 | } |
| 1651 | } | 1662 | } |
| 1652 | } | 1663 | } |
| 1653 | } | ||
| 1654 | 1664 | ||
| 1655 | res = xs_dict_append(res, "accounts", acl); | 1665 | res = xs_dict_append(res, "accounts", acl); |
| 1656 | res = xs_dict_append(res, "statuses", stl); | 1666 | res = xs_dict_append(res, "statuses", stl); |
| 1657 | res = xs_dict_append(res, "hashtags", htl); | 1667 | res = xs_dict_append(res, "hashtags", htl); |
| 1658 | 1668 | ||
| 1659 | *body = xs_json_dumps_pp(res, 4); | 1669 | *body = xs_json_dumps_pp(res, 4); |
| 1660 | *ctype = "application/json"; | 1670 | *ctype = "application/json"; |
| 1661 | status = 200; | 1671 | status = 200; |
| 1672 | } | ||
| 1673 | else | ||
| 1674 | status = 401; | ||
| 1662 | } | 1675 | } |
| 1663 | 1676 | ||
| 1664 | /* user cleanup */ | 1677 | /* user cleanup */ |