diff options
| author | 2025-01-24 20:38:26 +0100 | |
|---|---|---|
| committer | 2025-01-24 20:38:26 +0100 | |
| commit | 85be7f36e12507cff7607df22ca14f8bfc00f6e2 (patch) | |
| tree | e41bedab3e3b011c16d2ea6180926470cc8586aa | |
| parent | fix memory leak (diff) | |
| parent | Version 2.69 RELEASED. (diff) | |
| download | penes-snac2-85be7f36e12507cff7607df22ca14f8bfc00f6e2.tar.gz penes-snac2-85be7f36e12507cff7607df22ca14f8bfc00f6e2.tar.xz penes-snac2-85be7f36e12507cff7607df22ca14f8bfc00f6e2.zip | |
Merge remote-tracking branch 'upstream/master' into curl-smtp
| -rw-r--r-- | RELEASE_NOTES.md | 6 | ||||
| -rw-r--r-- | activitypub.c | 131 | ||||
| -rw-r--r-- | doc/snac.5 | 7 | ||||
| -rw-r--r-- | doc/snac.8 | 9 | ||||
| -rw-r--r-- | format.c | 47 | ||||
| -rw-r--r-- | html.c | 28 | ||||
| -rw-r--r-- | httpd.c | 2 | ||||
| -rw-r--r-- | mastoapi.c | 5 | ||||
| -rw-r--r-- | sandbox.c | 9 | ||||
| -rw-r--r-- | snac.h | 2 | ||||
| -rw-r--r-- | utils.c | 4 |
11 files changed, 197 insertions, 53 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index cc6866a..23a2a17 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | # Release Notes | 1 | # Release Notes |
| 2 | 2 | ||
| 3 | ## UNRELEASED | 3 | ## 2.69 "Yin/Yang of Love" |
| 4 | 4 | ||
| 5 | Added support for subscribing to LitePub (Pleroma-style) Fediverse Relays like e.g. https://fedi-relay.gyptazy.com to improve federation. See `snac(8)` (the Administrator Manual) for more information on how to use this feature. | 5 | Added support for subscribing to LitePub (Pleroma-style) Fediverse Relays like e.g. https://fedi-relay.gyptazy.com to improve federation. See `snac(8)` (the Administrator Manual) for more information on how to use this feature. |
| 6 | 6 | ||
| @@ -18,7 +18,7 @@ Fixed broken NetBSD build (missing dependency in Makefile.NetBSD). | |||
| 18 | 18 | ||
| 19 | The user profile can now include longitude and latitude data for your current location. | 19 | The user profile can now include longitude and latitude data for your current location. |
| 20 | 20 | ||
| 21 | Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster), obey the quiet public visibility set for posts. | 21 | Mastodon API: implemented limit= on notification fetches (contributed by nowster), implemented faster min_id handling (contributed by nowster), obey the quiet public visibility set for posts, other timeline improvements (contributed by nowster). |
| 22 | 22 | ||
| 23 | Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. | 23 | Reduced RSA key size for new users from 4096 to 2048. This will be friendlier to smaller machines, and everybody else out there is using 2048. |
| 24 | 24 | ||
| @@ -26,6 +26,8 @@ If the `SNAC_BASEDIR` environment variable is defined and set to the base direct | |||
| 26 | 26 | ||
| 27 | Fixed a bug in the generation of the top page (contributed by an-im-dugud). | 27 | Fixed a bug in the generation of the top page (contributed by an-im-dugud). |
| 28 | 28 | ||
| 29 | Added support for Markdown headers and underlining (contributed by an-im-dugud). | ||
| 30 | |||
| 29 | ## 2.68 | 31 | ## 2.68 |
| 30 | 32 | ||
| 31 | Fixed regression in link verification code (contributed by nowster). | 33 | Fixed regression in link verification code (contributed by nowster). |
diff --git a/activitypub.c b/activitypub.c index 2b10435..4cb779a 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -587,6 +587,70 @@ int is_msg_from_private_user(const xs_dict *msg) | |||
| 587 | } | 587 | } |
| 588 | 588 | ||
| 589 | 589 | ||
| 590 | int followed_hashtag_check(snac *user, const xs_dict *msg) | ||
| 591 | /* returns true if this message contains a hashtag followed by me */ | ||
| 592 | { | ||
| 593 | const xs_list *fw_tags = xs_dict_get(user->config, "followed_hashtags"); | ||
| 594 | |||
| 595 | if (xs_is_list(fw_tags)) { | ||
| 596 | const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); | ||
| 597 | |||
| 598 | if (xs_is_list(tags_in_msg)) { | ||
| 599 | const xs_dict *te; | ||
| 600 | |||
| 601 | /* iterate the tags in the message */ | ||
| 602 | xs_list_foreach(tags_in_msg, te) { | ||
| 603 | if (xs_is_dict(te)) { | ||
| 604 | const char *type = xs_dict_get(te, "type"); | ||
| 605 | const char *name = xs_dict_get(te, "name"); | ||
| 606 | |||
| 607 | if (xs_is_string(type) && xs_is_string(name)) { | ||
| 608 | if (strcmp(type, "Hashtag") == 0) { | ||
| 609 | xs *lc_name = xs_utf8_to_lower(name); | ||
| 610 | |||
| 611 | if (xs_list_in(fw_tags, lc_name) != -1) | ||
| 612 | return 1; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | } | ||
| 616 | } | ||
| 617 | } | ||
| 618 | } | ||
| 619 | |||
| 620 | return 0; | ||
| 621 | } | ||
| 622 | |||
| 623 | |||
| 624 | void followed_hashtag_distribute(const xs_dict *msg) | ||
| 625 | /* distribute this post to all users following the included hashtags */ | ||
| 626 | { | ||
| 627 | const char *id = xs_dict_get(msg, "id"); | ||
| 628 | const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); | ||
| 629 | |||
| 630 | if (!xs_is_string(id) || !xs_is_list(tags_in_msg) || xs_list_len(tags_in_msg) == 0) | ||
| 631 | return; | ||
| 632 | |||
| 633 | srv_debug(1, xs_fmt("followed_hashtag_distribute check for %s", id)); | ||
| 634 | |||
| 635 | xs *users = user_list(); | ||
| 636 | const char *uid; | ||
| 637 | |||
| 638 | xs_list_foreach(users, uid) { | ||
| 639 | snac user; | ||
| 640 | |||
| 641 | if (user_open(&user, uid)) { | ||
| 642 | if (followed_hashtag_check(&user, msg)) { | ||
| 643 | timeline_add(&user, id, msg); | ||
| 644 | |||
| 645 | snac_log(&user, xs_fmt("followed hashtag in %s", id)); | ||
| 646 | } | ||
| 647 | |||
| 648 | user_free(&user); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | |||
| 590 | int is_msg_for_me(snac *snac, const xs_dict *c_msg) | 654 | int is_msg_for_me(snac *snac, const xs_dict *c_msg) |
| 591 | /* checks if this message is for me */ | 655 | /* checks if this message is for me */ |
| 592 | { | 656 | { |
| @@ -602,19 +666,32 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) | |||
| 602 | if (xs_match(type, "Like|Announce|EmojiReact")) { | 666 | if (xs_match(type, "Like|Announce|EmojiReact")) { |
| 603 | const char *object = xs_dict_get(c_msg, "object"); | 667 | const char *object = xs_dict_get(c_msg, "object"); |
| 604 | 668 | ||
| 605 | if (xs_type(object) == XSTYPE_DICT) | 669 | if (xs_is_dict(object)) |
| 606 | object = xs_dict_get(object, "id"); | 670 | object = xs_dict_get(object, "id"); |
| 607 | 671 | ||
| 608 | /* bad object id? reject */ | 672 | /* bad object id? reject */ |
| 609 | if (xs_type(object) != XSTYPE_STRING) | 673 | if (!xs_is_string(object)) |
| 610 | return 0; | 674 | return 0; |
| 611 | 675 | ||
| 612 | /* if it's about one of our posts, accept it */ | 676 | /* if it's about one of our posts, accept it */ |
| 613 | if (xs_startswith(object, snac->actor)) | 677 | if (xs_startswith(object, snac->actor)) |
| 614 | return 2; | 678 | return 2; |
| 615 | 679 | ||
| 616 | /* if it's by someone we don't follow, reject */ | 680 | /* if it's by someone we follow, accept it */ |
| 617 | return following_check(snac, actor); | 681 | if (following_check(snac, actor)) |
| 682 | return 1; | ||
| 683 | |||
| 684 | /* do we follow any hashtag? */ | ||
| 685 | if (xs_is_list(xs_dict_get(snac->config, "followed_hashtags"))) { | ||
| 686 | xs *obj = NULL; | ||
| 687 | |||
| 688 | /* if the admired object contains any followed hashtag, accept it */ | ||
| 689 | if (valid_status(object_get(object, &obj)) && | ||
| 690 | followed_hashtag_check(snac, obj)) | ||
| 691 | return 7; | ||
| 692 | } | ||
| 693 | |||
| 694 | return 0; | ||
| 618 | } | 695 | } |
| 619 | 696 | ||
| 620 | /* if it's an Undo, it must be from someone related to us */ | 697 | /* if it's an Undo, it must be from someone related to us */ |
| @@ -708,30 +785,8 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) | |||
| 708 | } | 785 | } |
| 709 | 786 | ||
| 710 | /* does this message contain a tag we are following? */ | 787 | /* does this message contain a tag we are following? */ |
| 711 | const xs_list *fw_tags = xs_dict_get(snac->config, "followed_hashtags"); | 788 | if (pub_msg && followed_hashtag_check(snac, msg)) |
| 712 | if (pub_msg && xs_type(fw_tags) == XSTYPE_LIST) { | 789 | return 7; |
| 713 | const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); | ||
| 714 | if (xs_type(tags_in_msg) == XSTYPE_LIST) { | ||
| 715 | const xs_dict *te; | ||
| 716 | |||
| 717 | /* iterate the tags in the message */ | ||
| 718 | xs_list_foreach(tags_in_msg, te) { | ||
| 719 | if (xs_type(te) == XSTYPE_DICT) { | ||
| 720 | const char *type = xs_dict_get(te, "type"); | ||
| 721 | const char *name = xs_dict_get(te, "name"); | ||
| 722 | |||
| 723 | if (xs_type(type) == XSTYPE_STRING && xs_type(name) == XSTYPE_STRING) { | ||
| 724 | if (strcmp(type, "Hashtag") == 0) { | ||
| 725 | xs *lc_name = xs_utf8_to_lower(name); | ||
| 726 | |||
| 727 | if (xs_list_in(fw_tags, lc_name) != -1) | ||
| 728 | return 7; | ||
| 729 | } | ||
| 730 | } | ||
| 731 | } | ||
| 732 | } | ||
| 733 | } | ||
| 734 | } | ||
| 735 | 790 | ||
| 736 | return 0; | 791 | return 0; |
| 737 | } | 792 | } |
| @@ -2277,15 +2332,23 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 2277 | xs *who_o = NULL; | 2332 | xs *who_o = NULL; |
| 2278 | 2333 | ||
| 2279 | if (valid_status(actor_request(snac, who, &who_o))) { | 2334 | if (valid_status(actor_request(snac, who, &who_o))) { |
| 2280 | if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) | 2335 | /* don't account as such announces by our own relay */ |
| 2281 | snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); | 2336 | xs *this_relay = xs_fmt("%s/relay", srv_baseurl); |
| 2282 | else | 2337 | |
| 2283 | snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", | 2338 | if (strcmp(actor, this_relay) != 0) { |
| 2284 | actor, object)); | 2339 | if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) |
| 2340 | snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); | ||
| 2341 | else | ||
| 2342 | snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", | ||
| 2343 | actor, object)); | ||
| 2344 | } | ||
| 2285 | 2345 | ||
| 2286 | /* distribute the post with the actor as 'proxy' */ | 2346 | /* distribute the post with the actor as 'proxy' */ |
| 2287 | list_distribute(snac, actor, a_msg); | 2347 | list_distribute(snac, actor, a_msg); |
| 2288 | 2348 | ||
| 2349 | /* distribute the post to users following these hashtags */ | ||
| 2350 | followed_hashtag_distribute(a_msg); | ||
| 2351 | |||
| 2289 | do_notify = 1; | 2352 | do_notify = 1; |
| 2290 | } | 2353 | } |
| 2291 | else | 2354 | else |
| @@ -2300,7 +2363,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 2300 | } | 2363 | } |
| 2301 | else | 2364 | else |
| 2302 | if (strcmp(type, "Update") == 0) { /** **/ | 2365 | if (strcmp(type, "Update") == 0) { /** **/ |
| 2303 | if (xs_match(utype, "Person|Service")) { /** **/ | 2366 | if (xs_match(utype, "Person|Service|Application")) { /** **/ |
| 2304 | actor_add(actor, xs_dict_get(msg, "object")); | 2367 | actor_add(actor, xs_dict_get(msg, "object")); |
| 2305 | timeline_touch(snac); | 2368 | timeline_touch(snac); |
| 2306 | 2369 | ||
| @@ -24,9 +24,11 @@ A special subset of Markdown is allowed, including: | |||
| 24 | .It bold | 24 | .It bold |
| 25 | **text between two pairs of asterisks** | 25 | **text between two pairs of asterisks** |
| 26 | .It italic | 26 | .It italic |
| 27 | *text between a pair of asterisks* | 27 | *text between a pair of asterisks* or _between a pair of underscores_ |
| 28 | .It strikethrough text | 28 | .It strikethrough text |
| 29 | ~~text between a pair of tildes~~ | 29 | ~~text between a pair of tildes~~ |
| 30 | .It underlined text | ||
| 31 | __text between two pairs of underscores__ | ||
| 30 | .It code | 32 | .It code |
| 31 | Text `between backticks` is formatted as code. | 33 | Text `between backticks` is formatted as code. |
| 32 | .Bd -literal | 34 | .Bd -literal |
| @@ -53,6 +55,9 @@ Horizonal rules can be inserted by typing three minus symbols | |||
| 53 | alone in a line. | 55 | alone in a line. |
| 54 | .It quoted text | 56 | .It quoted text |
| 55 | Lines starting with >. | 57 | Lines starting with >. |
| 58 | .It headers | ||
| 59 | One, two or three # at the beginning of a line plus a space plus | ||
| 60 | some text are converted to HTML headers. | ||
| 56 | .It user mentions | 61 | .It user mentions |
| 57 | Strings in the format @user@host are requested using the Webfinger | 62 | Strings in the format @user@host are requested using the Webfinger |
| 58 | protocol and converted to links and mentions if something reasonable | 63 | protocol and converted to links and mentions if something reasonable |
| @@ -591,17 +591,20 @@ instance can subscribe to LitePub (Pleroma-style) Fediverse Relays. Doing this i | |||
| 591 | visibility and allows following hashtags. To do this, you must create a special user named | 591 | visibility and allows following hashtags. To do this, you must create a special user named |
| 592 | relay and, from it, follow the relay actor(s) like you do with regular actor URLs. This | 592 | relay and, from it, follow the relay actor(s) like you do with regular actor URLs. This |
| 593 | special user will start receiving boosts from the relay server of posts from other instances | 593 | special user will start receiving boosts from the relay server of posts from other instances |
| 594 | also following it. It any other user of the same | 594 | also following it. If any other user of the same |
| 595 | .Nm | 595 | .Nm |
| 596 | instance follows any of the hashtags included in these boosted posts coming from the relay, | 596 | instance follows any of the hashtags included in these boosted posts coming from the relay, |
| 597 | they will received as if they were for them. | 597 | they will be received as if they were for them. |
| 598 | .Pp | 598 | .Pp |
| 599 | Example: | 599 | Example: |
| 600 | .Bd -literal -offset indent | 600 | .Bd -literal -offset indent |
| 601 | snac adduser $SNAC_BASEDIR relay # only needed once | 601 | snac adduser $SNAC_BASEDIR relay # only needed once |
| 602 | snac follow $SNAC_BASEDIR relay https://fedi-relay.gyptazy.com/actor | 602 | snac follow $SNAC_BASEDIR relay https://relay.example.com/actor |
| 603 | .Ed | 603 | .Ed |
| 604 | .Pp | 604 | .Pp |
| 605 | Users on your instance do NOT need to follow the local relay user to benefit from following | ||
| 606 | hashtags. | ||
| 607 | .Pp | ||
| 605 | Please take note that subscribing to relays can increase the traffic towards your instance | 608 | Please take note that subscribing to relays can increase the traffic towards your instance |
| 606 | significantly. In any case, lowering the "Maximum days to keep posts" value for the relay | 609 | significantly. In any case, lowering the "Maximum days to keep posts" value for the relay |
| 607 | special user is recommended (e.g. setting to just 1 day). | 610 | special user is recommended (e.g. setting to just 1 day). |
| @@ -92,6 +92,8 @@ static xs_str *format_line(const char *line, xs_list **attach) | |||
| 92 | "`[^`]+`" "|" | 92 | "`[^`]+`" "|" |
| 93 | "~~[^~]+~~" "|" | 93 | "~~[^~]+~~" "|" |
| 94 | "\\*\\*?\\*?[^\\*]+\\*?\\*?\\*" "|" | 94 | "\\*\\*?\\*?[^\\*]+\\*?\\*?\\*" "|" |
| 95 | "_[^_]+_" "|" //anzu | ||
| 96 | "__[^_]+__" "|" //anzu | ||
| 95 | "!\\[[^]]+\\]\\([^\\)]+\\)" "|" | 97 | "!\\[[^]]+\\]\\([^\\)]+\\)" "|" |
| 96 | "\\[[^]]+\\]\\([^\\)]+\\)" "|" | 98 | "\\[[^]]+\\]\\([^\\)]+\\)" "|" |
| 97 | "[a-z]+:/" "/[^[:space:]]+" "|" | 99 | "[a-z]+:/" "/[^[:space:]]+" "|" |
| @@ -127,6 +129,20 @@ static xs_str *format_line(const char *line, xs_list **attach) | |||
| 127 | xs *s2 = xs_fmt("<i>%s</i>", s1); | 129 | xs *s2 = xs_fmt("<i>%s</i>", s1); |
| 128 | s = xs_str_cat(s, s2); | 130 | s = xs_str_cat(s, s2); |
| 129 | } | 131 | } |
| 132 | //anzu - begin | ||
| 133 | else | ||
| 134 | if (xs_startswith(v, "__")) { | ||
| 135 | xs *s1 = xs_strip_chars_i(xs_dup(v), "_"); | ||
| 136 | xs *s2 = xs_fmt("<u>%s</u>", s1); | ||
| 137 | s = xs_str_cat(s, s2); | ||
| 138 | } | ||
| 139 | else | ||
| 140 | if (xs_startswith(v, "_")) { | ||
| 141 | xs *s1 = xs_strip_chars_i(xs_dup(v), "_"); | ||
| 142 | xs *s2 = xs_fmt("<i>%s</i>", s1); | ||
| 143 | s = xs_str_cat(s, s2); | ||
| 144 | } | ||
| 145 | //anzu - end | ||
| 130 | else | 146 | else |
| 131 | if (xs_startswith(v, "~~")) { | 147 | if (xs_startswith(v, "~~")) { |
| 132 | xs *s1 = xs_strip_chars_i(xs_dup(v), "~"); | 148 | xs *s1 = xs_strip_chars_i(xs_dup(v), "~"); |
| @@ -303,6 +319,31 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag | |||
| 303 | continue; | 319 | continue; |
| 304 | } | 320 | } |
| 305 | 321 | ||
| 322 | //anzu - begin | ||
| 323 | // h1 reserved for snac? | ||
| 324 | if (xs_startswith(ss, "# ")) { | ||
| 325 | ss = xs_strip_i(xs_crop_i(ss, 2, 0)); | ||
| 326 | s = xs_str_cat(s, "<h2>"); | ||
| 327 | s = xs_str_cat(s, ss); | ||
| 328 | s = xs_str_cat(s, "</h2>"); | ||
| 329 | continue; | ||
| 330 | } | ||
| 331 | if (xs_startswith(ss, "## ")) { | ||
| 332 | ss = xs_strip_i(xs_crop_i(ss, 3, 0)); | ||
| 333 | s = xs_str_cat(s, "<h2>"); | ||
| 334 | s = xs_str_cat(s, ss); | ||
| 335 | s = xs_str_cat(s, "</h2>"); | ||
| 336 | continue; | ||
| 337 | } | ||
| 338 | if (xs_startswith(ss, "### ")) { | ||
| 339 | ss = xs_strip_i(xs_crop_i(ss, 4, 0)); | ||
| 340 | s = xs_str_cat(s, "<h3>"); | ||
| 341 | s = xs_str_cat(s, ss); | ||
| 342 | s = xs_str_cat(s, "</h3>"); | ||
| 343 | continue; | ||
| 344 | } | ||
| 345 | //anzu - end | ||
| 346 | |||
| 306 | if (xs_startswith(ss, ">")) { | 347 | if (xs_startswith(ss, ">")) { |
| 307 | /* delete the > and subsequent spaces */ | 348 | /* delete the > and subsequent spaces */ |
| 308 | ss = xs_strip_i(xs_crop_i(ss, 1, 0)); | 349 | ss = xs_strip_i(xs_crop_i(ss, 1, 0)); |
| @@ -336,6 +377,8 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag | |||
| 336 | s = xs_replace_i(s, "<br><br><blockquote>", "<br><blockquote>"); | 377 | s = xs_replace_i(s, "<br><br><blockquote>", "<br><blockquote>"); |
| 337 | s = xs_replace_i(s, "</blockquote><br>", "</blockquote>"); | 378 | s = xs_replace_i(s, "</blockquote><br>", "</blockquote>"); |
| 338 | s = xs_replace_i(s, "</pre><br>", "</pre>"); | 379 | s = xs_replace_i(s, "</pre><br>", "</pre>"); |
| 380 | s = xs_replace_i(s, "</h2><br>", "</h2>"); //anzu ??? | ||
| 381 | s = xs_replace_i(s, "</h3><br>", "</h3>"); //anzu ??? | ||
| 339 | 382 | ||
| 340 | { | 383 | { |
| 341 | /* traditional emoticons */ | 384 | /* traditional emoticons */ |
| @@ -378,7 +421,9 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag | |||
| 378 | 421 | ||
| 379 | const char *valid_tags[] = { | 422 | const char *valid_tags[] = { |
| 380 | "a", "p", "br", "br/", "blockquote", "ul", "ol", "li", "cite", "small", | 423 | "a", "p", "br", "br/", "blockquote", "ul", "ol", "li", "cite", "small", |
| 381 | "span", "i", "b", "u", "s", "pre", "code", "em", "strong", "hr", "img", "del", "bdi", NULL | 424 | "span", "i", "b", "u", "s", "pre", "code", "em", "strong", "hr", "img", "del", "bdi", |
| 425 | "h2","h3", //anzu | ||
| 426 | NULL | ||
| 382 | }; | 427 | }; |
| 383 | 428 | ||
| 384 | xs_str *sanitize(const char *content) | 429 | xs_str *sanitize(const char *content) |
| @@ -1855,13 +1855,15 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1855 | } | 1855 | } |
| 1856 | } | 1856 | } |
| 1857 | } | 1857 | } |
| 1858 | else | ||
| 1859 | if (strcmp(type, "Note") == 0) { | ||
| 1860 | if (level == 0) { | ||
| 1861 | /* is the parent not here? */ | ||
| 1862 | const char *parent = get_in_reply_to(msg); | ||
| 1863 | 1858 | ||
| 1864 | if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { | 1859 | if (user && strcmp(type, "Note") == 0) { |
| 1860 | /* is the parent not here? */ | ||
| 1861 | const char *parent = get_in_reply_to(msg); | ||
| 1862 | |||
| 1863 | if (!xs_is_null(parent) && *parent) { | ||
| 1864 | xs *md5 = xs_md5_hex(parent, strlen(parent)); | ||
| 1865 | |||
| 1866 | if (!timeline_here(user, md5)) { | ||
| 1865 | xs_html_add(post_header, | 1867 | xs_html_add(post_header, |
| 1866 | xs_html_tag("div", | 1868 | xs_html_tag("div", |
| 1867 | xs_html_attr("class", "snac-origin"), | 1869 | xs_html_attr("class", "snac-origin"), |
| @@ -2965,9 +2967,12 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 2965 | xs_html_attr("class", "snac-posts")); | 2967 | xs_html_attr("class", "snac-posts")); |
| 2966 | xs_html_add(body, posts); | 2968 | xs_html_add(body, posts); |
| 2967 | 2969 | ||
| 2968 | xs_list *p = n_list; | 2970 | xs_set rep; |
| 2971 | xs_set_init(&rep); | ||
| 2972 | |||
| 2969 | const xs_str *v; | 2973 | const xs_str *v; |
| 2970 | while (xs_list_iter(&p, &v)) { | 2974 | |
| 2975 | xs_list_foreach(n_list, v) { | ||
| 2971 | xs *noti = notify_get(user, v); | 2976 | xs *noti = notify_get(user, v); |
| 2972 | 2977 | ||
| 2973 | if (noti == NULL) | 2978 | if (noti == NULL) |
| @@ -2988,6 +2993,11 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 2988 | 2993 | ||
| 2989 | object_get(id, &obj); | 2994 | object_get(id, &obj); |
| 2990 | 2995 | ||
| 2996 | const char *msg_id = NULL; | ||
| 2997 | |||
| 2998 | if (xs_is_dict(obj) && (msg_id = xs_dict_get(obj, "id")) && xs_set_add(&rep, msg_id) != 1) | ||
| 2999 | continue; | ||
| 3000 | |||
| 2991 | const char *actor_id = xs_dict_get(noti, "actor"); | 3001 | const char *actor_id = xs_dict_get(noti, "actor"); |
| 2992 | xs *actor = NULL; | 3002 | xs *actor = NULL; |
| 2993 | 3003 | ||
| @@ -3101,6 +3111,8 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3101 | } | 3111 | } |
| 3102 | } | 3112 | } |
| 3103 | 3113 | ||
| 3114 | xs_set_free(&rep); | ||
| 3115 | |||
| 3104 | if (noti_new == NULL && noti_seen == NULL) | 3116 | if (noti_new == NULL && noti_seen == NULL) |
| 3105 | xs_html_add(body, | 3117 | xs_html_add(body, |
| 3106 | xs_html_tag("h2", | 3118 | xs_html_tag("h2", |
| @@ -138,7 +138,7 @@ static xs_str *greeting_html(void) | |||
| 138 | while (xs_list_iter(&p, &uid)) { | 138 | while (xs_list_iter(&p, &uid)) { |
| 139 | snac user; | 139 | snac user; |
| 140 | 140 | ||
| 141 | if (user_open(&user, uid)) { | 141 | if (strcmp(uid, "relay") && user_open(&user, uid)) { |
| 142 | xs_html_add(ul, | 142 | xs_html_add(ul, |
| 143 | xs_html_tag("li", | 143 | xs_html_tag("li", |
| 144 | xs_html_tag("a", | 144 | xs_html_tag("a", |
| @@ -1453,7 +1453,10 @@ xs_list *mastoapi_timeline(snac *user, const xs_dict *args, const char *index_fn | |||
| 1453 | xs *st = mastoapi_status(user, msg); | 1453 | xs *st = mastoapi_status(user, msg); |
| 1454 | 1454 | ||
| 1455 | if (st != NULL) { | 1455 | if (st != NULL) { |
| 1456 | out = xs_list_append(out, st); | 1456 | if (ascending) |
| 1457 | out = xs_list_insert(out, 0, st); | ||
| 1458 | else | ||
| 1459 | out = xs_list_append(out, st); | ||
| 1457 | cnt++; | 1460 | cnt++; |
| 1458 | } | 1461 | } |
| 1459 | 1462 | ||
| @@ -63,15 +63,22 @@ LL_BEGIN(sbox_enter_linux_, const char* basedir, const char *address, int smtp_p | |||
| 63 | LANDLOCK_ACCESS_FS_REFER_COMPAT, | 63 | LANDLOCK_ACCESS_FS_REFER_COMPAT, |
| 64 | s = LANDLOCK_ACCESS_FS_MAKE_SOCK, | 64 | s = LANDLOCK_ACCESS_FS_MAKE_SOCK, |
| 65 | x = LANDLOCK_ACCESS_FS_EXECUTE; | 65 | x = LANDLOCK_ACCESS_FS_EXECUTE; |
| 66 | char *resolved_path = NULL; | ||
| 66 | 67 | ||
| 67 | LL_PATH(basedir, rf|rd|w|c); | 68 | LL_PATH(basedir, rf|rd|w|c); |
| 68 | LL_PATH("/tmp", rf|rd|w|c); | 69 | LL_PATH("/tmp", rf|rd|w|c); |
| 69 | #ifndef WITHOUT_SHM | 70 | #ifndef WITHOUT_SHM |
| 70 | LL_PATH("/dev/shm", rf|w|c ); | 71 | LL_PATH("/dev/shm", rf|w|c ); |
| 71 | #endif | 72 | #endif |
| 73 | LL_PATH("/dev/urandom", rf ); | ||
| 72 | LL_PATH("/etc/resolv.conf", rf ); | 74 | LL_PATH("/etc/resolv.conf", rf ); |
| 73 | LL_PATH("/etc/hosts", rf ); | 75 | LL_PATH("/etc/hosts", rf ); |
| 74 | LL_PATH("/etc/ssl", rf ); | 76 | LL_PATH("/etc/ssl", rf|rd ); |
| 77 | if ((resolved_path = realpath("/etc/ssl/cert.pem", NULL))) { | ||
| 78 | /* some distros like cert.pem to be a symlink */ | ||
| 79 | LL_PATH(resolved_path, rf ); | ||
| 80 | free(resolved_path); | ||
| 81 | } | ||
| 75 | LL_PATH("/usr/share/zoneinfo", rf ); | 82 | LL_PATH("/usr/share/zoneinfo", rf ); |
| 76 | 83 | ||
| 77 | if (mtime("/etc/pki") > 0) | 84 | if (mtime("/etc/pki") > 0) |
| @@ -1,7 +1,7 @@ | |||
| 1 | /* snac - A simple, minimalistic ActivityPub instance */ | 1 | /* snac - A simple, minimalistic ActivityPub instance */ |
| 2 | /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ | 2 | /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ |
| 3 | 3 | ||
| 4 | #define VERSION "2.69-dev" | 4 | #define VERSION "2.69" |
| 5 | 5 | ||
| 6 | #define USER_AGENT "snac/" VERSION | 6 | #define USER_AGENT "snac/" VERSION |
| 7 | 7 | ||
| @@ -318,6 +318,10 @@ int adduser(const char *uid) | |||
| 318 | mkdirx(d); | 318 | mkdirx(d); |
| 319 | } | 319 | } |
| 320 | 320 | ||
| 321 | /* add a specially short data retention time for the relay */ | ||
| 322 | if (strcmp(uid, "relay") == 0) | ||
| 323 | config = xs_dict_set(config, "purge_days", xs_stock(1)); | ||
| 324 | |||
| 321 | xs *cfn = xs_fmt("%s/user.json", basedir); | 325 | xs *cfn = xs_fmt("%s/user.json", basedir); |
| 322 | 326 | ||
| 323 | if ((f = fopen(cfn, "w")) == NULL) { | 327 | if ((f = fopen(cfn, "w")) == NULL) { |