summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--RELEASE_NOTES.md12
-rw-r--r--activitypub.c31
-rw-r--r--data.c22
-rw-r--r--doc/snac.54
-rw-r--r--html.c153
-rw-r--r--mastoapi.c4
-rw-r--r--po/cs.po32
-rw-r--r--rss.c2
-rw-r--r--snac.h3
-rw-r--r--upgrade.c2
-rw-r--r--webfinger.c4
11 files changed, 224 insertions, 45 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 7dfb332..505d080 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,17 @@
1# Release Notes 1# Release Notes
2 2
3## UNRELEASED
4
5Quoted posts are now shown.
6
7Added metadata to remote users in the people page (contributed by dandelions).
8
9Fixed memory leak (contributed by dandelions).
10
11Fixed user matching (contributed by rakoo).
12
13Fixed typo in man page (contributed by spky).
14
3## 2.84 15## 2.84
4 16
5Implemented more scopes to match other ActivityPub implementations (public, unlisted, followers-only and direct message) (contributed by byte). 17Implemented more scopes to match other ActivityPub implementations (public, unlisted, followers-only and direct message) (contributed by byte).
diff --git a/activitypub.c b/activitypub.c
index 0368ac8..90230d8 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -779,7 +779,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
779 object_get(object, &obj); 779 object_get(object, &obj);
780 780
781 /* if it's about one of our posts, accept it */ 781 /* if it's about one of our posts, accept it */
782 if (xs_startswith(object, snac->actor)) 782 if (is_msg_mine(snac, object))
783 return 2; 783 return 2;
784 784
785 /* blocked by hashtag? */ 785 /* blocked by hashtag? */
@@ -1242,7 +1242,7 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
1242 1242
1243 if (xs_match(type, "Like|Announce|EmojiReact")) { 1243 if (xs_match(type, "Like|Announce|EmojiReact")) {
1244 /* if it's not an admiration about something by us, done */ 1244 /* if it's not an admiration about something by us, done */
1245 if (xs_is_null(objid) || !xs_startswith(objid, snac->actor)) 1245 if (xs_is_null(objid) || !is_msg_mine(snac, objid))
1246 return; 1246 return;
1247 1247
1248 /* if it's an announce by our own relay, done */ 1248 /* if it's an announce by our own relay, done */
@@ -1267,7 +1267,7 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
1267 return; 1267 return;
1268 1268
1269 /* if it's not ours and we didn't vote, discard */ 1269 /* if it's not ours and we didn't vote, discard */
1270 if (!xs_startswith(poll_id, snac->actor) && !was_question_voted(snac, poll_id)) 1270 if (!is_msg_mine(snac, poll_id) && !was_question_voted(snac, poll_id))
1271 return; 1271 return;
1272 } 1272 }
1273 1273
@@ -2686,6 +2686,20 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2686 2686
2687 timeline_request(snac, &in_reply_to, &wrk, 0); 2687 timeline_request(snac, &in_reply_to, &wrk, 0);
2688 2688
2689 const char *quoted_id = xs_or(xs_dict_get(object, "quoteUri"), xs_dict_get(object, "quoteUrl"));
2690
2691 if (xs_is_string(quoted_id) && xs_match(quoted_id, "https://*|http://*")) { /** **/
2692 xs *quoted_post = NULL;
2693 int status;
2694
2695 if (valid_status(status = activitypub_request(snac, quoted_id, &quoted_post))) {
2696 /* got quoted post */
2697 object_add(quoted_id, quoted_post);
2698 }
2699
2700 snac_debug(snac, 1, xs_fmt("retrieving quoted post %s %d", quoted_id, status));
2701 }
2702
2689 if (timeline_add(snac, id, object)) { 2703 if (timeline_add(snac, id, object)) {
2690 snac_log(snac, xs_fmt("new '%s' %s %s", utype, actor, id)); 2704 snac_log(snac, xs_fmt("new '%s' %s %s", utype, actor, id));
2691 do_notify = 1; 2705 do_notify = 1;
@@ -2778,10 +2792,10 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2778 if (xs_is_null(object)) 2792 if (xs_is_null(object))
2779 snac_log(snac, xs_fmt("malformed message: no 'id' field")); 2793 snac_log(snac, xs_fmt("malformed message: no 'id' field"));
2780 else 2794 else
2781 if (is_muted(snac, actor) && !xs_startswith(object, snac->actor)) 2795 if (is_muted(snac, actor) && !is_msg_mine(snac, object))
2782 snac_log(snac, xs_fmt("dropped 'Announce' from muted actor %s", actor)); 2796 snac_log(snac, xs_fmt("dropped 'Announce' from muted actor %s", actor));
2783 else 2797 else
2784 if (is_limited(snac, actor) && !xs_startswith(object, snac->actor)) 2798 if (is_limited(snac, actor) && !is_msg_mine(snac, object))
2785 snac_log(snac, xs_fmt("dropped 'Announce' from limited actor %s", actor)); 2799 snac_log(snac, xs_fmt("dropped 'Announce' from limited actor %s", actor));
2786 else { 2800 else {
2787 xs *a_msg = NULL; 2801 xs *a_msg = NULL;
@@ -2792,6 +2806,9 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2792 if (valid_status(object_get(object, &a_msg))) { 2806 if (valid_status(object_get(object, &a_msg))) {
2793 const char *who = get_atto(a_msg); 2807 const char *who = get_atto(a_msg);
2794 2808
2809 /* got the admired object: instance is [back] online */
2810 instance_failure(object, 2);
2811
2795 if (who && !is_muted(snac, who)) { 2812 if (who && !is_muted(snac, who)) {
2796 /* bring the actor */ 2813 /* bring the actor */
2797 xs *who_o = NULL; 2814 xs *who_o = NULL;
@@ -2886,7 +2903,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2886 snac_log(snac, xs_fmt("malformed message: no 'id' field")); 2903 snac_log(snac, xs_fmt("malformed message: no 'id' field"));
2887 else 2904 else
2888 if (object_here(object)) { 2905 if (object_here(object)) {
2889 if (xs_startswith(object, srv_baseurl) && !xs_startswith(object, actor)) 2906 if (xs_startswith(object, srv_baseurl) && !is_msg_mine(snac, object))
2890 snac_log(snac, xs_fmt("ignored incorrect 'Delete' %s %s", actor, object)); 2907 snac_log(snac, xs_fmt("ignored incorrect 'Delete' %s %s", actor, object));
2891 else { 2908 else {
2892 timeline_del(snac, object); 2909 timeline_del(snac, object);
@@ -3699,7 +3716,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
3699 const char *type = xs_dict_get(i, "type"); 3716 const char *type = xs_dict_get(i, "type");
3700 const char *id = xs_dict_get(i, "id"); 3717 const char *id = xs_dict_get(i, "id");
3701 3718
3702 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) { 3719 if (type && id && strcmp(type, "Note") == 0 && is_msg_mine(&snac, id)) {
3703 if (is_msg_public(i)) { 3720 if (is_msg_public(i)) {
3704 xs *c_msg = msg_create(&snac, i); 3721 xs *c_msg = msg_create(&snac, i);
3705 list = xs_list_append(list, c_msg); 3722 list = xs_list_append(list, c_msg);
diff --git a/data.c b/data.c
index d291ba7..688d2e3 100644
--- a/data.c
+++ b/data.c
@@ -1358,6 +1358,20 @@ int pending_count(snac *user)
1358} 1358}
1359 1359
1360 1360
1361int is_msg_mine(snac *user, const char *id)
1362/* returns true if a post id is by the given user */
1363{
1364 int ret = 0;
1365
1366 if (xs_is_string(id)) {
1367 xs *s1 = xs_fmt("%s/", user->actor);
1368 ret = xs_startswith(id, s1);
1369 }
1370
1371 return ret;
1372}
1373
1374
1361/** timeline **/ 1375/** timeline **/
1362 1376
1363double timeline_mtime(snac *snac) 1377double timeline_mtime(snac *snac)
@@ -1453,7 +1467,7 @@ void timeline_update_indexes(snac *snac, const char *id)
1453{ 1467{
1454 object_user_cache_add(snac, id, "private"); 1468 object_user_cache_add(snac, id, "private");
1455 1469
1456 if (xs_startswith(id, snac->actor)) { 1470 if (is_msg_mine(snac, id)) {
1457 xs *msg = NULL; 1471 xs *msg = NULL;
1458 1472
1459 if (valid_status(object_get(id, &msg))) { 1473 if (valid_status(object_get(id, &msg))) {
@@ -1913,7 +1927,7 @@ int pin(snac *user, const char *id)
1913{ 1927{
1914 int ret = -2; 1928 int ret = -2;
1915 1929
1916 if (xs_startswith(id, user->actor)) { 1930 if (is_msg_mine(user, id)) {
1917 if (is_pinned(user, id)) 1931 if (is_pinned(user, id))
1918 ret = -3; 1932 ret = -3;
1919 else 1933 else
@@ -3513,7 +3527,7 @@ void enqueue_output(snac *snac, const xs_dict *msg,
3513 const xs_str *inbox, int retries, int p_status) 3527 const xs_str *inbox, int retries, int p_status)
3514/* enqueues an output message to an inbox */ 3528/* enqueues an output message to an inbox */
3515{ 3529{
3516 if (xs_startswith(inbox, snac->actor)) { 3530 if (is_msg_mine(snac, inbox)) {
3517 snac_debug(snac, 1, xs_str_new("refusing enqueue to myself")); 3531 snac_debug(snac, 1, xs_str_new("refusing enqueue to myself"));
3518 return; 3532 return;
3519 } 3533 }
@@ -4041,7 +4055,7 @@ void delete_purged_posts(snac *user, int days)
4041 if (xs_is_dict(msg)) { 4055 if (xs_is_dict(msg)) {
4042 const char *id = xs_dict_get(msg, "id"); 4056 const char *id = xs_dict_get(msg, "id");
4043 4057
4044 if (xs_is_string(id) && xs_startswith(id, user->actor)) { 4058 if (xs_is_string(id) && is_msg_mine(user, id)) {
4045 xs *d_msg = msg_delete(user, id); 4059 xs *d_msg = msg_delete(user, id);
4046 4060
4047 enqueue_message(user, d_msg); 4061 enqueue_message(user, d_msg);
diff --git a/doc/snac.5 b/doc/snac.5
index 89c01bd..cf6ee0c 100644
--- a/doc/snac.5
+++ b/doc/snac.5
@@ -143,6 +143,8 @@ in the "last_announcement" field of the
143.Pa user.json 143.Pa user.json
144file. When the file is modified, the announcement will then reappear. It can 144file. When the file is modified, the announcement will then reappear. It can
145contain only text and will be ignored if it has more than 2048 bytes. 145contain only text and will be ignored if it has more than 2048 bytes.
146.It Pa server.pid
147This file stores the server PID in a single text line.
146.El 148.El
147.Pp 149.Pp
148Each user directory is a subdirectory of 150Each user directory is a subdirectory of
@@ -222,8 +224,6 @@ after executing the 'export_csv' command-line operation.
222.It Pa import/ 224.It Pa import/
223Mastodon-compatible CSV files must be copied into this directory to use 225Mastodon-compatible CSV files must be copied into this directory to use
224any of the importing functions. 226any of the importing functions.
225.It Pa server.pid
226This file stores the server PID in a single text line.
227.El 227.El
228.Sh SEE ALSO 228.Sh SEE ALSO
229.Xr snac 1 , 229.Xr snac 1 ,
diff --git a/html.c b/html.c
index 84636e9..9ac57f8 100644
--- a/html.c
+++ b/html.c
@@ -1688,7 +1688,7 @@ xs_html *html_top_controls(snac *user)
1688 xs_html_text(L("Languages you usually post in:")), 1688 xs_html_text(L("Languages you usually post in:")),
1689 xs_html_sctag("br", NULL), 1689 xs_html_sctag("br", NULL),
1690 xs_html_sctag("input", 1690 xs_html_sctag("input",
1691 xs_html_attr("type", "next"), 1691 xs_html_attr("type", "text"),
1692 xs_html_attr("name", "post_langs"), 1692 xs_html_attr("name", "post_langs"),
1693 xs_html_attr("value", post_langs), 1693 xs_html_attr("value", post_langs),
1694 xs_html_attr("placeholder", L("en fr es de_AT")))), 1694 xs_html_attr("placeholder", L("en fr es de_AT")))),
@@ -1891,7 +1891,7 @@ xs_html *html_entry_controls(snac *user, const char *actor,
1891 xs_html_attr("name", "redir"), 1891 xs_html_attr("name", "redir"),
1892 xs_html_attr("value", redir)))); 1892 xs_html_attr("value", redir))));
1893 1893
1894 if (!xs_startswith(id, user->actor)) { 1894 if (!is_msg_mine(user, id)) {
1895 if (xs_list_in(likes, user->md5) == -1) { 1895 if (xs_list_in(likes, user->md5) == -1) {
1896 /* not already liked; add button */ 1896 /* not already liked; add button */
1897 xs_html_add(form, 1897 xs_html_add(form,
@@ -2387,6 +2387,23 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2387 /* c contains sanitized HTML */ 2387 /* c contains sanitized HTML */
2388 xs_html_add(snac_content, 2388 xs_html_add(snac_content,
2389 xs_html_raw(c)); 2389 xs_html_raw(c));
2390
2391 /* quoted post */
2392 const char *quoted_id = xs_or(xs_dict_get(msg, "quoteUri"), xs_dict_get(msg, "quoteUrl"));
2393
2394 if (xs_is_string(quoted_id) && xs_match(quoted_id, "https://*|http://*")) { /** **/
2395 xs *quoted_post = NULL;
2396
2397 if (valid_status(object_get(quoted_id, &quoted_post))) {
2398 xs_html_add(snac_content,
2399 xs_html_tag("blockquote",
2400 xs_html_attr("class", "snac-quoted-post"),
2401 html_entry(user, quoted_post, 1, 1, NULL, 1)));
2402 }
2403 else
2404 if (user)
2405 enqueue_object_request(user, quoted_id, 0);
2406 }
2390 } 2407 }
2391 2408
2392 if (strcmp(type, "Question") == 0) { /** question content **/ 2409 if (strcmp(type, "Question") == 0) { /** question content **/
@@ -2402,7 +2419,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2402 if (read_only) 2419 if (read_only)
2403 closed = 1; /* non-identified page; show as closed */ 2420 closed = 1; /* non-identified page; show as closed */
2404 else 2421 else
2405 if (user && xs_startswith(id, user->actor)) 2422 if (user && is_msg_mine(user, id))
2406 closed = 1; /* we questioned; closed for us */ 2423 closed = 1; /* we questioned; closed for us */
2407 else 2424 else
2408 if (user && was_question_voted(user, id)) 2425 if (user && was_question_voted(user, id))
@@ -2610,7 +2627,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2610 xs_html_attr("title", name)))); 2627 xs_html_attr("title", name))));
2611 } 2628 }
2612 else 2629 else
2613 if (xs_startswith(type, "video/")) { 2630 if (xs_startswith(type, "video/") || strcmp(type, "Video") == 0) {
2614 xs_html_add(content_attachments, 2631 xs_html_add(content_attachments,
2615 xs_html_tag("video", 2632 xs_html_tag("video",
2616 xs_html_attr("preload", "none"), 2633 xs_html_attr("preload", "none"),
@@ -3214,7 +3231,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
3214 continue; 3231 continue;
3215 } 3232 }
3216 /* hide non-public posts viewed from outside */ 3233 /* hide non-public posts viewed from outside */
3217 if (read_only && scope != SCOPE_PUBLIC){ 3234 if (read_only && (scope != SCOPE_PUBLIC && scope != SCOPE_UNLISTED)) {
3218 continue; 3235 continue;
3219 } 3236 }
3220 3237
@@ -3331,10 +3348,11 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
3331 3348
3332 /* content (user bio) */ 3349 /* content (user bio) */
3333 const char *c = xs_dict_get(actor, "summary"); 3350 const char *c = xs_dict_get(actor, "summary");
3351 const xs_val *tag = xs_dict_get(actor, "tag");
3334 3352
3335 if (!xs_is_null(c)) { 3353 if (!xs_is_null(c)) {
3336 xs *sc = sanitize(c); 3354 xs *sc = sanitize(c);
3337 sc = replace_shortnames(sc, xs_dict_get(actor, "tag"), 2, proxy); 3355 sc = replace_shortnames(sc, tag, 2, proxy);
3338 3356
3339 xs_html *snac_content = xs_html_tag("div", 3357 xs_html *snac_content = xs_html_tag("div",
3340 xs_html_attr("class", "snac-content")); 3358 xs_html_attr("class", "snac-content"));
@@ -3350,6 +3368,93 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
3350 xs_html_add(snac_post, snac_content); 3368 xs_html_add(snac_post, snac_content);
3351 } 3369 }
3352 3370
3371 /* add user metadata */
3372 xs_html *snac_metadata = xs_html_tag("div",
3373 xs_html_attr("class", "snac-metadata"));
3374
3375 int count = 0;
3376 const xs_val *address = xs_dict_get(actor, "vcard:Address");
3377 if (xs_is_string(address)) {
3378 xs_html_add(snac_metadata,
3379 xs_html_tag("span",
3380 xs_html_attr("class", "snac-property-name"),
3381 xs_html_raw("📍 Location")),
3382 xs_html_text(":"),
3383 xs_html_raw(" "),
3384 xs_html_tag("span",
3385 xs_html_attr("class", "snac-property-value p-adr"),
3386 xs_html_text(address)),
3387 xs_html_sctag("br", NULL));
3388
3389 count++;
3390 }
3391
3392 const xs_val *birthday = xs_dict_get(actor, "vcard:bday");
3393 if (xs_is_string(birthday)) {
3394 xs_html_add(snac_metadata,
3395 xs_html_tag("span",
3396 xs_html_attr("class", "snac-property-name"),
3397 xs_html_raw("🎂 Birthday")),
3398 xs_html_text(":"),
3399 xs_html_raw(" "),
3400 xs_html_tag("time",
3401 xs_html_attr("class", "snac-property-value dt-bday"),
3402 xs_html_text(birthday)),
3403 xs_html_sctag("br", NULL));
3404
3405 count++;
3406 }
3407
3408 const xs_list *attachment = xs_dict_get(actor, "attachment");
3409 if (count > 0 && xs_list_len(attachment) > 0) {
3410 xs_html_add(snac_metadata,
3411 xs_html_sctag("hr",
3412 xs_html_attr("class", "snac-property-divider")));
3413 }
3414
3415 const xs_val *v;
3416 xs_list_foreach(attachment, v) {
3417 const char *type = xs_dict_get(v, "type");
3418 const char *name = xs_dict_get(v, "name");
3419 const char *value = xs_dict_get(v, "value");
3420
3421 if (!xs_is_null(type) && !xs_is_null(name) &&
3422 !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) {
3423 /* both the name and the value can contain emoji */
3424 xs *nam = sanitize(name);
3425 nam = replace_shortnames(nam, tag, 1, proxy);
3426
3427 /* todo: sometimes the value is transmitted as markdown and not html ._. */
3428 xs *val = sanitize(value);
3429 val = replace_shortnames(val, tag, 1, proxy);
3430
3431 /* delete <p> tags, because some software sends them */
3432 val = xs_replace_i(val, "<p>", "");
3433 val = xs_replace_i(val, "</p>", "");
3434
3435 xs_html_add(snac_metadata,
3436 xs_html_tag("span",
3437 xs_html_attr("class", "snac-property-name"),
3438 xs_html_raw(nam)),
3439 xs_html_text(":"),
3440 xs_html_raw("&nbsp;"),
3441 xs_html_tag("span",
3442 xs_html_attr("class", "snac-property-value"),
3443 xs_html_raw(val)),
3444 xs_html_sctag("br", NULL));
3445
3446 count++;
3447 }
3448 }
3449
3450 if (count > 0) {
3451 xs_html_add(snac_post, snac_metadata);
3452 }
3453 else {
3454 /* free the html, by rendering it... */
3455 xs_free(xs_html_render(snac_metadata));
3456 }
3457
3353 /* buttons */ 3458 /* buttons */
3354 xs *btn_form_action = xs_fmt("%s/admin/action", user->actor); 3459 xs *btn_form_action = xs_fmt("%s/admin/action", user->actor);
3355 3460
@@ -3555,9 +3660,13 @@ xs_str *html_notifications(snac *user, int skip, int show)
3555 3660
3556 if (valid_status(actor_get(actor_id, &actor))) 3661 if (valid_status(actor_get(actor_id, &actor)))
3557 a_name = actor_name(actor, proxy); 3662 a_name = actor_name(actor, proxy);
3558 else 3663 else {
3559 a_name = xs_dup(actor_id); 3664 a_name = xs_dup(actor_id);
3560 3665
3666 /* actor not here: request it */
3667 enqueue_actor_refresh(user, actor_id, 0);
3668 }
3669
3561 xs *label_sanitized = sanitize(type); 3670 xs *label_sanitized = sanitize(type);
3562 const char *label = label_sanitized; 3671 const char *label = label_sanitized;
3563 3672
@@ -3630,7 +3739,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
3630 xs_html_attr("class", "snac-post-with-desc"), 3739 xs_html_attr("class", "snac-post-with-desc"),
3631 html_label); 3740 html_label);
3632 3741
3633 if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { 3742 if (strcmp(type, "Block") == 0) {
3634 if (actor) 3743 if (actor)
3635 xs_html_add(entry, 3744 xs_html_add(entry,
3636 xs_html_tag("div", 3745 xs_html_tag("div",
@@ -3638,6 +3747,32 @@ xs_str *html_notifications(snac *user, int skip, int show)
3638 html_actor_icon(user, actor, NULL, NULL, NULL, -1, 0, proxy, NULL, NULL))); 3747 html_actor_icon(user, actor, NULL, NULL, NULL, -1, 0, proxy, NULL, NULL)));
3639 } 3748 }
3640 else 3749 else
3750 if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0) {
3751 if (actor) {
3752 xs *action = xs_fmt("%s/admin/action", user->actor);
3753 xs_html *button = NULL;
3754
3755 if (following_check(user, actor_id))
3756 button = html_button("unfollow", L("Unfollow"), L("Stop following this user's activity"));
3757 else
3758 button = html_button("follow", L("Follow"), L("Start following this user's activity"));
3759
3760 xs_html_add(entry,
3761 xs_html_tag("div",
3762 xs_html_attr("class", "snac-post"),
3763 html_actor_icon(user, actor, NULL, NULL, NULL, -1, 0, proxy, NULL, NULL),
3764 xs_html_tag("form",
3765 xs_html_attr("method", "post"),
3766 xs_html_attr("action", action),
3767 xs_html_sctag("input",
3768 xs_html_attr("type", "hidden"),
3769 xs_html_attr("name", "actor"),
3770 xs_html_attr("value", actor_id)),
3771 button,
3772 xs_html_sctag("br", NULL))));
3773 }
3774 }
3775 else
3641 if (strcmp(type, "Move") == 0) { 3776 if (strcmp(type, "Move") == 0) {
3642 const xs_dict *o_msg = xs_dict_get(noti, "msg"); 3777 const xs_dict *o_msg = xs_dict_get(noti, "msg");
3643 const char *target; 3778 const char *target;
@@ -4880,7 +5015,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4880 } 5015 }
4881 else { 5016 else {
4882 /* delete an entry */ 5017 /* delete an entry */
4883 if (xs_startswith(id, snac.actor) && !is_draft(&snac, id)) { 5018 if (is_msg_mine(&snac, id) && !is_draft(&snac, id)) {
4884 /* it's a post by us: generate a delete */ 5019 /* it's a post by us: generate a delete */
4885 xs *msg = msg_delete(&snac, id); 5020 xs *msg = msg_delete(&snac, id);
4886 5021
diff --git a/mastoapi.c b/mastoapi.c
index 94912f1..acb95a0 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -1919,7 +1919,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1919 /* add only posts by the author */ 1919 /* add only posts by the author */
1920 if (!xs_is_null(msg_id) && 1920 if (!xs_is_null(msg_id) &&
1921 strcmp(xs_dict_get(msg, "type"), "Note") == 0 && 1921 strcmp(xs_dict_get(msg, "type"), "Note") == 0 &&
1922 xs_startswith(xs_dict_get(msg, "id"), snac2.actor) && is_msg_public(msg)) { 1922 is_msg_mine(&snac2, xs_dict_get(msg, "id")) && is_msg_public(msg)) {
1923 1923
1924 /* if max_id is set, skip entries until we find it */ 1924 /* if max_id is set, skip entries until we find it */
1925 if (skip_until_max) { 1925 if (skip_until_max) {
@@ -3824,7 +3824,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
3824 if (valid_status(object_get_by_md5(p, &obj))) { 3824 if (valid_status(object_get_by_md5(p, &obj))) {
3825 const char *id = xs_dict_get(obj, "id"); 3825 const char *id = xs_dict_get(obj, "id");
3826 3826
3827 if (xs_is_string(id) && xs_startswith(id, snac.actor)) { 3827 if (xs_is_string(id) && is_msg_mine(&snac, id)) {
3828 xs *out = mastoapi_status(&snac, obj); 3828 xs *out = mastoapi_status(&snac, obj);
3829 3829
3830 xs *msg = msg_delete(&snac, id); 3830 xs *msg = msg_delete(&snac, id);
diff --git a/po/cs.po b/po/cs.po
index d855e79..b987137 100644
--- a/po/cs.po
+++ b/po/cs.po
@@ -14,7 +14,7 @@ msgstr "Citlivý obsah: "
14 14
15#: html.c:448 15#: html.c:448
16msgid "Sensitive content description" 16msgid "Sensitive content description"
17msgstr "Varování k citlivému obsahu" 17msgstr "Varování o citlivém obsahu"
18 18
19msgid "Only for mentioned people: " 19msgid "Only for mentioned people: "
20msgstr "Pouze pro zmíněné osoby:" 20msgstr "Pouze pro zmíněné osoby:"
@@ -185,11 +185,11 @@ msgstr "Vaše jméno"
185 185
186#: html.c:1507 186#: html.c:1507
187msgid "Avatar: " 187msgid "Avatar: "
188msgstr "Avatar: " 188msgstr "Profilový obrázek: "
189 189
190#: html.c:1515 190#: html.c:1515
191msgid "Delete current avatar" 191msgid "Delete current avatar"
192msgstr "Smazat současný avatar" 192msgstr "Smazat profilový obrázek"
193 193
194#: html.c:1517 194#: html.c:1517
195msgid "Header image (banner): " 195msgid "Header image (banner): "
@@ -197,7 +197,7 @@ msgstr "Obrázek v záhlaví profilu: "
197 197
198#: html.c:1525 198#: html.c:1525
199msgid "Delete current header image" 199msgid "Delete current header image"
200msgstr "Smazat současný obrázek v záhlaví" 200msgstr "Smazat obrázek v záhlaví"
201 201
202#: html.c:1527 202#: html.c:1527
203msgid "Bio:" 203msgid "Bio:"
@@ -205,11 +205,11 @@ msgstr "Bio:"
205 205
206#: html.c:1533 206#: html.c:1533
207msgid "Write about yourself here..." 207msgid "Write about yourself here..."
208msgstr "Napište sem něco o sobě..." 208msgstr "Napište něco o sobě..."
209 209
210#: html.c:1542 210#: html.c:1542
211msgid "Always show sensitive content" 211msgid "Always show sensitive content"
212msgstr "Vždy zobrazit příspěvky s varováním o citlivém obsahu" 212msgstr "Rozbalit citlivé příspěvky"
213 213
214#: html.c:1544 214#: html.c:1544
215msgid "Email address for notifications:" 215msgid "Email address for notifications:"
@@ -233,11 +233,11 @@ msgstr "Zahodit soukromé zprávy od lidí, které nesledujete"
233 233
234#: html.c:1611 234#: html.c:1611
235msgid "This account is a bot" 235msgid "This account is a bot"
236msgstr "Tenhle účet je robot" 236msgstr "Tento účet je robotem"
237 237
238#: html.c:1620 238#: html.c:1620
239msgid "Auto-boost all mentions to this account" 239msgid "Auto-boost all mentions to this account"
240msgstr "Automaticky boostovat všechny zmíňky o tomto účtu" 240msgstr "Automaticky boostit všechna zmínění tohoto účtu"
241 241
242#: html.c:1629 242#: html.c:1629
243msgid "This account is private (posts are not shown through the web)" 243msgid "This account is private (posts are not shown through the web)"
@@ -246,11 +246,11 @@ msgstr ""
246 246
247#: html.c:1639 247#: html.c:1639
248msgid "Collapse top threads by default" 248msgid "Collapse top threads by default"
249msgstr "Zobrazovat vlákna složená" 249msgstr "Složit vlákna"
250 250
251#: html.c:1648 251#: html.c:1648
252msgid "Follow requests must be approved" 252msgid "Follow requests must be approved"
253msgstr "Žádosti o sledování je nutno manuálně potvrdit" 253msgstr "Žádosti o sledování je nutné manuálně potvrdit"
254 254
255#: html.c:1657 255#: html.c:1657
256msgid "Publish follower and following metrics" 256msgid "Publish follower and following metrics"
@@ -274,7 +274,7 @@ msgstr "Nové heslo:"
274 274
275#: html.c:1710 275#: html.c:1710
276msgid "Repeat new password:" 276msgid "Repeat new password:"
277msgstr "Zopakujte nové heslo:" 277msgstr "Nové heslo znovu:"
278 278
279#: html.c:1720 279#: html.c:1720
280msgid "Update user info" 280msgid "Update user info"
@@ -330,7 +330,7 @@ msgstr "Odboostit"
330 330
331#: html.c:1930 331#: html.c:1930
332msgid "I regret I boosted this" 332msgid "I regret I boosted this"
333msgstr "Boostit to byl blbej nápad" 333msgstr "Boostit to byl špatný nápad"
334 334
335#: html.c:1936 html.c:4922 335#: html.c:1936 html.c:4922
336msgid "Unbookmark" 336msgid "Unbookmark"
@@ -438,7 +438,7 @@ msgstr "Událost"
438 438
439#: html.c:2246 html.c:2275 439#: html.c:2246 html.c:2275
440msgid "boosted" 440msgid "boosted"
441msgstr "boostuje" 441msgstr "boostí"
442 442
443#: html.c:2289 443#: html.c:2289
444msgid "in reply to" 444msgid "in reply to"
@@ -478,7 +478,7 @@ msgstr "Popisek..."
478 478
479#: html.c:2697 479#: html.c:2697
480msgid "Source channel or community" 480msgid "Source channel or community"
481msgstr "" 481msgstr "Původní kanál nebo komunita"
482 482
483#: html.c:2791 483#: html.c:2791
484msgid "Time: " 484msgid "Time: "
@@ -519,7 +519,7 @@ msgstr "Rozepsané příspěvky"
519 519
520#: html.c:3177 520#: html.c:3177
521msgid "No more unseen posts" 521msgid "No more unseen posts"
522msgstr "Nic víc nového" 522msgstr "Nic nového"
523 523
524#: html.c:3181 html.c:3290 524#: html.c:3181 html.c:3290
525msgid "Back to top" 525msgid "Back to top"
@@ -623,7 +623,7 @@ msgstr "Nové"
623 623
624#: html.c:3707 624#: html.c:3707
625msgid "Already seen" 625msgid "Already seen"
626msgstr "Zobrazeno díve" 626msgstr "Ji viděno"
627 627
628#: html.c:3722 628#: html.c:3722
629msgid "None" 629msgid "None"
diff --git a/rss.c b/rss.c
index 6e77205..6124e7a 100644
--- a/rss.c
+++ b/rss.c
@@ -59,7 +59,7 @@ xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
59 const char *content = xs_dict_get(msg, "content"); 59 const char *content = xs_dict_get(msg, "content");
60 const char *published = xs_dict_get(msg, "published"); 60 const char *published = xs_dict_get(msg, "published");
61 61
62 if (user && !xs_startswith(id, user->actor)) 62 if (user && !is_msg_mine(user, id))
63 continue; 63 continue;
64 64
65 if (!id || !content || !published) 65 if (!id || !content || !published)
diff --git a/snac.h b/snac.h
index 4c3100a..4d387a6 100644
--- a/snac.h
+++ b/snac.h
@@ -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.84" 4#define VERSION "2.85-dev"
5 5
6#define USER_AGENT "snac/" VERSION 6#define USER_AGENT "snac/" VERSION
7 7
@@ -388,6 +388,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
388xs_str *get_actor_inbox(const char *actor, int shared); 388xs_str *get_actor_inbox(const char *actor, int shared);
389int send_to_actor(snac *snac, const char *actor, const xs_dict *msg, 389int send_to_actor(snac *snac, const char *actor, const xs_dict *msg,
390 xs_val **payload, int *p_size, int timeout); 390 xs_val **payload, int *p_size, int timeout);
391int is_msg_mine(snac *user, const char *id);
391int is_msg_public(const xs_dict *msg); 392int is_msg_public(const xs_dict *msg);
392int is_msg_from_private_user(const xs_dict *msg); 393int is_msg_from_private_user(const xs_dict *msg);
393int is_msg_for_me(snac *snac, const xs_dict *msg); 394int is_msg_for_me(snac *snac, const xs_dict *msg);
diff --git a/upgrade.c b/upgrade.c
index 87ddfc8..9e0ae6e 100644
--- a/upgrade.c
+++ b/upgrade.c
@@ -213,7 +213,7 @@ int snac_upgrade(xs_str **error)
213 object_add_ow(id, o); 213 object_add_ow(id, o);
214 214
215 /* if it's from us, add to public */ 215 /* if it's from us, add to public */
216 if (xs_startswith(id, snac.actor)) { 216 if (is_msg_mine(&snac, id)) {
217 const xs_list *p; 217 const xs_list *p;
218 const char *v; 218 const char *v;
219 int c; 219 int c;
diff --git a/webfinger.c b/webfinger.c
index 12ec42c..1ce5e76 100644
--- a/webfinger.c
+++ b/webfinger.c
@@ -76,9 +76,9 @@ int webfinger_request_signed(snac *snac, const char *qs, xs_str **actor, xs_str
76 xs *url = xs_fmt("%s:/" "/%s/.well-known/webfinger?resource=%s", proto, host, resource); 76 xs *url = xs_fmt("%s:/" "/%s/.well-known/webfinger?resource=%s", proto, host, resource);
77 77
78 if (snac == NULL) 78 if (snac == NULL)
79 xs_http_request("GET", url, headers, NULL, 0, &status, &payload, &p_size, 0); 79 xs_free(xs_http_request("GET", url, headers, NULL, 0, &status, &payload, &p_size, 0));
80 else 80 else
81 http_signed_request(snac, "GET", url, headers, NULL, 0, &status, &payload, &p_size, 0); 81 xs_free(http_signed_request(snac, "GET", url, headers, NULL, 0, &status, &payload, &p_size, 0));
82 } 82 }
83 83
84 if (obj == NULL && valid_status(status) && payload) { 84 if (obj == NULL && valid_status(status) && payload) {