diff options
| -rw-r--r-- | TODO.md | 14 | ||||
| -rw-r--r-- | activitypub.c | 20 | ||||
| -rw-r--r-- | data.c | 2 | ||||
| -rw-r--r-- | doc/snac.5 | 2 | ||||
| -rw-r--r-- | html.c | 4 | ||||
| -rw-r--r-- | httpd.c | 14 | ||||
| -rw-r--r-- | main.c | 6 | ||||
| -rw-r--r-- | mastoapi.c | 24 | ||||
| -rw-r--r-- | snac.h | 1 |
9 files changed, 69 insertions, 18 deletions
| @@ -16,10 +16,6 @@ Important: deleting a follower should do more that just delete the object, see h | |||
| 16 | 16 | ||
| 17 | ## Wishlist | 17 | ## Wishlist |
| 18 | 18 | ||
| 19 | Implement Proxying for Media Links to Enhance User Privacy (see https://codeberg.org/grunfink/snac2/issues/219 for more information). | ||
| 20 | |||
| 21 | Consider showing only posts by the account owner (not full trees) (see https://codeberg.org/grunfink/snac2/issues/217 for more information). | ||
| 22 | |||
| 23 | Add support for subscribing and posting to relays (see https://codeberg.org/grunfink/snac2/issues/216 for more information). | 19 | Add support for subscribing and posting to relays (see https://codeberg.org/grunfink/snac2/issues/216 for more information). |
| 24 | 20 | ||
| 25 | The instance timeline should also show boosts from users. | 21 | The instance timeline should also show boosts from users. |
| @@ -357,3 +353,13 @@ Fix a crash when posting from the links browser (2.63, 2024-11-08T15:57:25+0100) | |||
| 357 | Fix some repeated images in Lemmy posts (2.63, 2024-11-08T15:57:25+0100). | 353 | Fix some repeated images in Lemmy posts (2.63, 2024-11-08T15:57:25+0100). |
| 358 | 354 | ||
| 359 | Fix a crash when posting an image from the tooot mobile app (2.63, 2024-11-11T19:42:11+0100). | 355 | Fix a crash when posting an image from the tooot mobile app (2.63, 2024-11-11T19:42:11+0100). |
| 356 | |||
| 357 | Fix some URL proxying (2.64, 2024-11-16T07:26:23+0100). | ||
| 358 | |||
| 359 | Allow underscores in hashtags (2.64, 2024-11-16T07:26:23+0100). | ||
| 360 | |||
| 361 | Add a pidfile (2.64, 2024-11-17T10:21:29+0100). | ||
| 362 | |||
| 363 | Implement Proxying for Media Links to Enhance User Privacy (see https://codeberg.org/grunfink/snac2/issues/219 for more information) (2024-11-18T20:36:39+0100). | ||
| 364 | |||
| 365 | Consider showing only posts by the account owner (not full trees) (see https://codeberg.org/grunfink/snac2/issues/217 for more information) (2024-11-18T20:36:39+0100). | ||
diff --git a/activitypub.c b/activitypub.c index 0b2fc6a..473675d 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -183,6 +183,18 @@ const char *get_atto(const xs_dict *msg) | |||
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | 185 | ||
| 186 | const char *get_in_reply_to(const xs_dict *msg) | ||
| 187 | /* gets the inReplyTo id */ | ||
| 188 | { | ||
| 189 | const xs_val *in_reply_to = xs_dict_get(msg, "inReplyTo"); | ||
| 190 | |||
| 191 | if (xs_type(in_reply_to) == XSTYPE_DICT) | ||
| 192 | in_reply_to = xs_dict_get(in_reply_to, "id"); | ||
| 193 | |||
| 194 | return in_reply_to; | ||
| 195 | } | ||
| 196 | |||
| 197 | |||
| 186 | xs_list *get_attachments(const xs_dict *msg) | 198 | xs_list *get_attachments(const xs_dict *msg) |
| 187 | /* unify the garbage fire that are the attachments */ | 199 | /* unify the garbage fire that are the attachments */ |
| 188 | { | 200 | { |
| @@ -373,7 +385,7 @@ int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) | |||
| 373 | } | 385 | } |
| 374 | 386 | ||
| 375 | /* does it have an ancestor? */ | 387 | /* does it have an ancestor? */ |
| 376 | const char *in_reply_to = xs_dict_get(object, "inReplyTo"); | 388 | const char *in_reply_to = get_in_reply_to(object); |
| 377 | 389 | ||
| 378 | /* store */ | 390 | /* store */ |
| 379 | timeline_add(snac, nid, object); | 391 | timeline_add(snac, nid, object); |
| @@ -671,7 +683,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) | |||
| 671 | return 3; | 683 | return 3; |
| 672 | 684 | ||
| 673 | /* is this message a reply to another? */ | 685 | /* is this message a reply to another? */ |
| 674 | const char *irt = xs_dict_get(msg, "inReplyTo"); | 686 | const char *irt = get_in_reply_to(msg); |
| 675 | if (!xs_is_null(irt)) { | 687 | if (!xs_is_null(irt)) { |
| 676 | xs *r_msg = NULL; | 688 | xs *r_msg = NULL; |
| 677 | 689 | ||
| @@ -724,7 +736,7 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag) | |||
| 724 | /* use this same server */ | 736 | /* use this same server */ |
| 725 | def_srv = xs_dup(xs_dict_get(srv_config, "host")); | 737 | def_srv = xs_dup(xs_dict_get(srv_config, "host")); |
| 726 | 738 | ||
| 727 | split = xs_regex_split(content, "(@[A-Za-z0-9_]+(@[A-Za-z0-9\\.-]+)?|&#[0-9]+;|#[^[:punct:][:space:]]+)"); | 739 | split = xs_regex_split(content, "(@[A-Za-z0-9_]+(@[A-Za-z0-9\\.-]+)?|&#[0-9]+;|#(_|[^[:punct:][:space:]])+)"); |
| 728 | 740 | ||
| 729 | p = split; | 741 | p = split; |
| 730 | while (xs_list_iter(&p, &v)) { | 742 | while (xs_list_iter(&p, &v)) { |
| @@ -1957,7 +1969,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 1957 | 1969 | ||
| 1958 | if (xs_match(utype, "Note|Article")) { /** **/ | 1970 | if (xs_match(utype, "Note|Article")) { /** **/ |
| 1959 | const char *id = xs_dict_get(object, "id"); | 1971 | const char *id = xs_dict_get(object, "id"); |
| 1960 | const char *in_reply_to = xs_dict_get(object, "inReplyTo"); | 1972 | const char *in_reply_to = get_in_reply_to(object); |
| 1961 | const char *atto = get_atto(object); | 1973 | const char *atto = get_atto(object); |
| 1962 | xs *wrk = NULL; | 1974 | xs *wrk = NULL; |
| 1963 | 1975 | ||
| @@ -762,7 +762,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow) | |||
| 762 | fclose(f); | 762 | fclose(f); |
| 763 | 763 | ||
| 764 | /* does this object has a parent? */ | 764 | /* does this object has a parent? */ |
| 765 | const char *in_reply_to = xs_dict_get(obj, "inReplyTo"); | 765 | const char *in_reply_to = get_in_reply_to(obj); |
| 766 | 766 | ||
| 767 | if (!xs_is_null(in_reply_to) && *in_reply_to) { | 767 | if (!xs_is_null(in_reply_to) && *in_reply_to) { |
| 768 | /* update the children index of the parent */ | 768 | /* update the children index of the parent */ |
| @@ -209,6 +209,8 @@ web interface. | |||
| 209 | .It Pa history/ | 209 | .It Pa history/ |
| 210 | This directory contains generated HTML files. They may be snapshots of the | 210 | This directory contains generated HTML files. They may be snapshots of the |
| 211 | local timeline in previous months or other cached data. | 211 | local timeline in previous months or other cached data. |
| 212 | .It Pa server.pid | ||
| 213 | This file stores the server PID in a single text line. | ||
| 212 | .El | 214 | .El |
| 213 | .Sh SEE ALSO | 215 | .Sh SEE ALSO |
| 214 | .Xr snac 1 , | 216 | .Xr snac 1 , |
| @@ -1670,7 +1670,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1670 | if (strcmp(type, "Note") == 0) { | 1670 | if (strcmp(type, "Note") == 0) { |
| 1671 | if (level == 0) { | 1671 | if (level == 0) { |
| 1672 | /* is the parent not here? */ | 1672 | /* is the parent not here? */ |
| 1673 | const char *parent = xs_dict_get(msg, "inReplyTo"); | 1673 | const char *parent = get_in_reply_to(msg); |
| 1674 | 1674 | ||
| 1675 | if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { | 1675 | if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { |
| 1676 | xs_html_add(post_header, | 1676 | xs_html_add(post_header, |
| @@ -2329,7 +2329,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 2329 | 2329 | ||
| 2330 | /* is this message a non-public reply? */ | 2330 | /* is this message a non-public reply? */ |
| 2331 | if (user != NULL && !is_msg_public(msg)) { | 2331 | if (user != NULL && !is_msg_public(msg)) { |
| 2332 | const char *irt = xs_dict_get(msg, "inReplyTo"); | 2332 | const char *irt = get_in_reply_to(msg); |
| 2333 | 2333 | ||
| 2334 | /* is it a reply to something not in the storage? */ | 2334 | /* is it a reply to something not in the storage? */ |
| 2335 | if (!xs_is_null(irt) && !object_here(irt)) { | 2335 | if (!xs_is_null(irt) && !object_here(irt)) { |
| @@ -774,6 +774,7 @@ void httpd(void) | |||
| 774 | xs *sem_name = NULL; | 774 | xs *sem_name = NULL; |
| 775 | xs *shm_name = NULL; | 775 | xs *shm_name = NULL; |
| 776 | sem_t anon_job_sem; | 776 | sem_t anon_job_sem; |
| 777 | xs *pidfile = xs_fmt("%s/server.pid", srv_basedir); | ||
| 777 | 778 | ||
| 778 | address = xs_dict_get(srv_config, "address"); | 779 | address = xs_dict_get(srv_config, "address"); |
| 779 | 780 | ||
| @@ -809,6 +810,17 @@ void httpd(void) | |||
| 809 | srv_log(xs_fmt("httpd%s start %s %s", p_state->use_fcgi ? " (FastCGI)" : "", | 810 | srv_log(xs_fmt("httpd%s start %s %s", p_state->use_fcgi ? " (FastCGI)" : "", |
| 810 | full_address, USER_AGENT)); | 811 | full_address, USER_AGENT)); |
| 811 | 812 | ||
| 813 | { | ||
| 814 | FILE *f; | ||
| 815 | |||
| 816 | if ((f = fopen(pidfile, "w")) != NULL) { | ||
| 817 | fprintf(f, "%d\n", getpid()); | ||
| 818 | fclose(f); | ||
| 819 | } | ||
| 820 | else | ||
| 821 | srv_log(xs_fmt("Cannot create %s: %s", pidfile, strerror(errno))); | ||
| 822 | } | ||
| 823 | |||
| 812 | /* show the number of usable file descriptors */ | 824 | /* show the number of usable file descriptors */ |
| 813 | struct rlimit r; | 825 | struct rlimit r; |
| 814 | getrlimit(RLIMIT_NOFILE, &r); | 826 | getrlimit(RLIMIT_NOFILE, &r); |
| @@ -894,4 +906,6 @@ void httpd(void) | |||
| 894 | srv_log(xs_fmt("httpd%s stop %s (run time: %s)", | 906 | srv_log(xs_fmt("httpd%s stop %s (run time: %s)", |
| 895 | p_state->use_fcgi ? " (FastCGI)" : "", | 907 | p_state->use_fcgi ? " (FastCGI)" : "", |
| 896 | full_address, uptime)); | 908 | full_address, uptime)); |
| 909 | |||
| 910 | unlink(pidfile); | ||
| 897 | } | 911 | } |
| @@ -558,7 +558,11 @@ int main(int argc, char *argv[]) | |||
| 558 | if (data != NULL) { | 558 | if (data != NULL) { |
| 559 | xs_json_dump(data, 4, stdout); | 559 | xs_json_dump(data, 4, stdout); |
| 560 | enqueue_actor_refresh(&snac, xs_dict_get(data, "attributedTo"), 0); | 560 | enqueue_actor_refresh(&snac, xs_dict_get(data, "attributedTo"), 0); |
| 561 | timeline_add(&snac, url, data); | 561 | |
| 562 | if (!timeline_here(&snac, url)) | ||
| 563 | timeline_add(&snac, url, data); | ||
| 564 | else | ||
| 565 | printf("Post %s already here\n", url); | ||
| 562 | } | 566 | } |
| 563 | 567 | ||
| 564 | return 0; | 568 | return 0; |
| @@ -171,7 +171,7 @@ const char *login_page = "" | |||
| 171 | "<body><h1>%s OAuth identify</h1>\n" | 171 | "<body><h1>%s OAuth identify</h1>\n" |
| 172 | "<div style=\"background-color: red; color: white\">%s</div>\n" | 172 | "<div style=\"background-color: red; color: white\">%s</div>\n" |
| 173 | "<form method=\"post\" action=\"%s:/" "/%s/%s\">\n" | 173 | "<form method=\"post\" action=\"%s:/" "/%s/%s\">\n" |
| 174 | "<p>Login: <input type=\"text\" name=\"login\"></p>\n" | 174 | "<p>Login: <input type=\"text\" name=\"login\" autocapitalize=\"off\"></p>\n" |
| 175 | "<p>Password: <input type=\"password\" name=\"passwd\"></p>\n" | 175 | "<p>Password: <input type=\"password\" name=\"passwd\"></p>\n" |
| 176 | "<input type=\"hidden\" name=\"redir\" value=\"%s\">\n" | 176 | "<input type=\"hidden\" name=\"redir\" value=\"%s\">\n" |
| 177 | "<input type=\"hidden\" name=\"cid\" value=\"%s\">\n" | 177 | "<input type=\"hidden\" name=\"cid\" value=\"%s\">\n" |
| @@ -1024,7 +1024,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg) | |||
| 1024 | st = xs_dict_append(st, "in_reply_to_id", xs_stock(XSTYPE_NULL)); | 1024 | st = xs_dict_append(st, "in_reply_to_id", xs_stock(XSTYPE_NULL)); |
| 1025 | st = xs_dict_append(st, "in_reply_to_account_id", xs_stock(XSTYPE_NULL)); | 1025 | st = xs_dict_append(st, "in_reply_to_account_id", xs_stock(XSTYPE_NULL)); |
| 1026 | 1026 | ||
| 1027 | tmp = xs_dict_get(msg, "inReplyTo"); | 1027 | tmp = get_in_reply_to(msg); |
| 1028 | if (!xs_is_null(tmp)) { | 1028 | if (!xs_is_null(tmp)) { |
| 1029 | xs *irto = NULL; | 1029 | xs *irto = NULL; |
| 1030 | 1030 | ||
| @@ -1727,11 +1727,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1727 | if (logged_in) { | 1727 | if (logged_in) { |
| 1728 | xs *l = notify_list(&snac1, 0, 64); | 1728 | xs *l = notify_list(&snac1, 0, 64); |
| 1729 | xs *out = xs_list_new(); | 1729 | xs *out = xs_list_new(); |
| 1730 | xs_list *p = l; | ||
| 1731 | const xs_dict *v; | 1730 | const xs_dict *v; |
| 1732 | const xs_list *excl = xs_dict_get(args, "exclude_types[]"); | 1731 | const xs_list *excl = xs_dict_get(args, "exclude_types[]"); |
| 1732 | const char *max_id = xs_dict_get(args, "max_id"); | ||
| 1733 | 1733 | ||
| 1734 | while (xs_list_iter(&p, &v)) { | 1734 | xs_list_foreach(l, v) { |
| 1735 | xs *noti = notify_get(&snac1, v); | 1735 | xs *noti = notify_get(&snac1, v); |
| 1736 | 1736 | ||
| 1737 | if (noti == NULL) | 1737 | if (noti == NULL) |
| @@ -1740,6 +1740,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1740 | const char *type = xs_dict_get(noti, "type"); | 1740 | const char *type = xs_dict_get(noti, "type"); |
| 1741 | const char *utype = xs_dict_get(noti, "utype"); | 1741 | const char *utype = xs_dict_get(noti, "utype"); |
| 1742 | const char *objid = xs_dict_get(noti, "objid"); | 1742 | const char *objid = xs_dict_get(noti, "objid"); |
| 1743 | const char *id = xs_dict_get(noti, "id"); | ||
| 1744 | xs *fid = xs_replace(id, ".", ""); | ||
| 1743 | xs *actor = NULL; | 1745 | xs *actor = NULL; |
| 1744 | xs *entry = NULL; | 1746 | xs *entry = NULL; |
| 1745 | 1747 | ||
| @@ -1752,6 +1754,13 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1752 | if (is_hidden(&snac1, objid)) | 1754 | if (is_hidden(&snac1, objid)) |
| 1753 | continue; | 1755 | continue; |
| 1754 | 1756 | ||
| 1757 | if (max_id) { | ||
| 1758 | if (strcmp(fid, max_id) == 0) | ||
| 1759 | max_id = NULL; | ||
| 1760 | |||
| 1761 | continue; | ||
| 1762 | } | ||
| 1763 | |||
| 1755 | /* convert the type */ | 1764 | /* convert the type */ |
| 1756 | if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0) | 1765 | if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0) |
| 1757 | type = "favourite"; | 1766 | type = "favourite"; |
| @@ -1778,12 +1787,15 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1778 | 1787 | ||
| 1779 | mn = xs_dict_append(mn, "type", type); | 1788 | mn = xs_dict_append(mn, "type", type); |
| 1780 | 1789 | ||
| 1781 | xs *id = xs_replace(xs_dict_get(noti, "id"), ".", ""); | 1790 | mn = xs_dict_append(mn, "id", fid); |
| 1782 | mn = xs_dict_append(mn, "id", id); | ||
| 1783 | 1791 | ||
| 1784 | mn = xs_dict_append(mn, "created_at", xs_dict_get(noti, "date")); | 1792 | mn = xs_dict_append(mn, "created_at", xs_dict_get(noti, "date")); |
| 1785 | 1793 | ||
| 1786 | xs *acct = mastoapi_account(&snac1, actor); | 1794 | xs *acct = mastoapi_account(&snac1, actor); |
| 1795 | |||
| 1796 | if (acct == NULL) | ||
| 1797 | continue; | ||
| 1798 | |||
| 1787 | mn = xs_dict_append(mn, "account", acct); | 1799 | mn = xs_dict_append(mn, "account", acct); |
| 1788 | 1800 | ||
| 1789 | if (strcmp(type, "follow") != 0 && !xs_is_null(objid)) { | 1801 | if (strcmp(type, "follow") != 0 && !xs_is_null(objid)) { |
| @@ -298,6 +298,7 @@ const char *default_avatar_base64(void); | |||
| 298 | xs_str *process_tags(snac *snac, const char *content, xs_list **tag); | 298 | xs_str *process_tags(snac *snac, const char *content, xs_list **tag); |
| 299 | 299 | ||
| 300 | const char *get_atto(const xs_dict *msg); | 300 | const char *get_atto(const xs_dict *msg); |
| 301 | const char *get_in_reply_to(const xs_dict *msg); | ||
| 301 | xs_list *get_attachments(const xs_dict *msg); | 302 | xs_list *get_attachments(const xs_dict *msg); |
| 302 | 303 | ||
| 303 | xs_dict *msg_admiration(snac *snac, const char *object, const char *type); | 304 | xs_dict *msg_admiration(snac *snac, const char *object, const char *type); |