From 771e987958fca3ad8cf310771256596dc38f4904 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 14:43:29 +0100 Subject: New function hashtag_in_msg(). --- activitypub.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 104110d..2813beb 100644 --- a/activitypub.c +++ b/activitypub.c @@ -587,12 +587,10 @@ int is_msg_from_private_user(const xs_dict *msg) } -int followed_hashtag_check(snac *user, const xs_dict *msg) -/* returns true if this message contains a hashtag followed by me */ +int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg) +/* returns 1 if the message contains any of the list of hashtags */ { - const xs_list *fw_tags = xs_dict_get(user->config, "followed_hashtags"); - - if (xs_is_list(fw_tags)) { + if (xs_is_list(hashtags)) { const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); if (xs_is_list(tags_in_msg)) { @@ -608,7 +606,7 @@ int followed_hashtag_check(snac *user, const xs_dict *msg) if (strcmp(type, "Hashtag") == 0) { xs *lc_name = xs_utf8_to_lower(name); - if (xs_list_in(fw_tags, lc_name) != -1) + if (xs_list_in(hashtags, lc_name) != -1) return 1; } } @@ -621,6 +619,13 @@ int followed_hashtag_check(snac *user, const xs_dict *msg) } +int followed_hashtag_check(snac *user, const xs_dict *msg) +/* returns true if this message contains a hashtag followed by me */ +{ + return hashtag_in_msg(xs_dict_get(user->config, "followed_hashtags"), msg); +} + + void followed_hashtag_distribute(const xs_dict *msg) /* distribute this post to all users following the included hashtags */ { -- cgit From 190d1d68db733fa71edc5e4ac4e1d23caaae5963 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 15:09:00 +0100 Subject: New function blocked_hashtag_check(). --- activitypub.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 2813beb..a7b6c0b 100644 --- a/activitypub.c +++ b/activitypub.c @@ -590,7 +590,7 @@ int is_msg_from_private_user(const xs_dict *msg) int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg) /* returns 1 if the message contains any of the list of hashtags */ { - if (xs_is_list(hashtags)) { + if (xs_is_list(hashtags) && xs_is_dict(msg)) { const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); if (xs_is_list(tags_in_msg)) { @@ -620,7 +620,7 @@ int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg) int followed_hashtag_check(snac *user, const xs_dict *msg) -/* returns true if this message contains a hashtag followed by me */ +/* returns 1 if this message contains a hashtag followed by me */ { return hashtag_in_msg(xs_dict_get(user->config, "followed_hashtags"), msg); } @@ -656,6 +656,13 @@ void followed_hashtag_distribute(const xs_dict *msg) } +int blocked_hashtag_check(snac *user, const xs_dict *msg) +/* returns 1 if this message contains a hashtag blocked by me */ +{ + return hashtag_in_msg(xs_dict_get(user->config, "blocked_hashtags"), msg); +} + + int is_msg_for_me(snac *snac, const xs_dict *c_msg) /* checks if this message is for me */ { @@ -678,23 +685,25 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) if (!xs_is_string(object)) return 0; + xs *obj = NULL; + if (!valid_status(object_get(object, &obj))) + return 0; + /* if it's about one of our posts, accept it */ if (xs_startswith(object, snac->actor)) return 2; + /* blocked by hashtag? */ + if (blocked_hashtag_check(snac, obj)) + return 0; + /* if it's by someone we follow, accept it */ if (following_check(snac, actor)) return 1; /* do we follow any hashtag? */ - if (xs_is_list(xs_dict_get(snac->config, "followed_hashtags"))) { - xs *obj = NULL; - - /* if the admired object contains any followed hashtag, accept it */ - if (valid_status(object_get(object, &obj)) && - followed_hashtag_check(snac, obj)) - return 7; - } + if (followed_hashtag_check(snac, obj)) + return 7; return 0; } @@ -726,13 +735,20 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) return 1; } + const xs_dict *msg = xs_dict_get(c_msg, "object"); + + /* any blocked hashtag? reject */ + if (blocked_hashtag_check(snac, msg)) { + snac_debug(snac, 1, xs_fmt("blocked by hashtag %s", xs_dict_get(msg, "id"))); + return 0; + } + int pub_msg = is_msg_public(c_msg); /* if this message is public and we follow the actor of this post, allow */ if (pub_msg && following_check(snac, actor)) return 1; - const xs_dict *msg = xs_dict_get(c_msg, "object"); xs *rcpts = recipient_list(snac, msg, 0); xs_list *p = rcpts; const xs_str *v; -- cgit From 995ea87a8f60f160c4846e4fbfce4e9ae60273de Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 17:27:37 +0100 Subject: More hashtag block tweaks. --- activitypub.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index a7b6c0b..5d79593 100644 --- a/activitypub.c +++ b/activitypub.c @@ -677,17 +677,20 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) if (xs_match(type, "Like|Announce|EmojiReact")) { const char *object = xs_dict_get(c_msg, "object"); + xs *obj = NULL; - if (xs_is_dict(object)) + if (xs_is_dict(object)) { + obj = xs_dup(object); object = xs_dict_get(object, "id"); + } /* bad object id? reject */ if (!xs_is_string(object)) return 0; - xs *obj = NULL; - if (!valid_status(object_get(object, &obj))) - return 0; + /* try to get the object */ + if (!xs_is_dict(obj)) + object_get(object, &obj); /* if it's about one of our posts, accept it */ if (xs_startswith(object, snac->actor)) -- cgit From 7cc984c8d49ea5ae6e1902d91fafed9880384f2a Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 17:47:06 +0100 Subject: More hashtag block tweaks. --- activitypub.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 5d79593..960a2a1 100644 --- a/activitypub.c +++ b/activitypub.c @@ -2352,6 +2352,9 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) /* bring the actor */ xs *who_o = NULL; + if (blocked_hashtag_check(snac, a_msg)) + snac_debug(snac, 1, xs_fmt("blocked by hashtag %s", object)); + else if (valid_status(actor_request(snac, who, &who_o))) { /* don't account as such announces by our own relay */ xs *this_relay = xs_fmt("%s/relay", srv_baseurl); -- cgit From 1cce6d86fa6b6828d395305c85b7d252ae53469f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 18:01:34 +0100 Subject: Also check for blocked hashtags in timeline_request(). --- activitypub.c | 98 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 960a2a1..1dedd17 100644 --- a/activitypub.c +++ b/activitypub.c @@ -329,6 +329,52 @@ xs_list *get_attachments(const xs_dict *msg) } +int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg) +/* returns 1 if the message contains any of the list of hashtags */ +{ + if (xs_is_list(hashtags) && xs_is_dict(msg)) { + const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); + + if (xs_is_list(tags_in_msg)) { + const xs_dict *te; + + /* iterate the tags in the message */ + xs_list_foreach(tags_in_msg, te) { + if (xs_is_dict(te)) { + const char *type = xs_dict_get(te, "type"); + const char *name = xs_dict_get(te, "name"); + + if (xs_is_string(type) && xs_is_string(name)) { + if (strcmp(type, "Hashtag") == 0) { + xs *lc_name = xs_utf8_to_lower(name); + + if (xs_list_in(hashtags, lc_name) != -1) + return 1; + } + } + } + } + } + } + + return 0; +} + + +int followed_hashtag_check(snac *user, const xs_dict *msg) +/* returns 1 if this message contains a hashtag followed by me */ +{ + return hashtag_in_msg(xs_dict_get(user->config, "followed_hashtags"), msg); +} + + +int blocked_hashtag_check(snac *user, const xs_dict *msg) +/* returns 1 if this message contains a hashtag blocked by me */ +{ + return hashtag_in_msg(xs_dict_get(user->config, "blocked_hashtags"), msg); +} + + int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) /* ensures that an entry and its ancestors are in the timeline */ { @@ -384,6 +430,9 @@ int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { if (content_match("filter_reject.txt", object)) snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); + else + if (blocked_hashtag_check(snac, object)) + snac_log(snac, xs_fmt("timeline_request rejected by hashtag %s", nid)); else { const char *actor = get_atto(object); @@ -587,45 +636,6 @@ int is_msg_from_private_user(const xs_dict *msg) } -int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg) -/* returns 1 if the message contains any of the list of hashtags */ -{ - if (xs_is_list(hashtags) && xs_is_dict(msg)) { - const xs_list *tags_in_msg = xs_dict_get(msg, "tag"); - - if (xs_is_list(tags_in_msg)) { - const xs_dict *te; - - /* iterate the tags in the message */ - xs_list_foreach(tags_in_msg, te) { - if (xs_is_dict(te)) { - const char *type = xs_dict_get(te, "type"); - const char *name = xs_dict_get(te, "name"); - - if (xs_is_string(type) && xs_is_string(name)) { - if (strcmp(type, "Hashtag") == 0) { - xs *lc_name = xs_utf8_to_lower(name); - - if (xs_list_in(hashtags, lc_name) != -1) - return 1; - } - } - } - } - } - } - - return 0; -} - - -int followed_hashtag_check(snac *user, const xs_dict *msg) -/* returns 1 if this message contains a hashtag followed by me */ -{ - return hashtag_in_msg(xs_dict_get(user->config, "followed_hashtags"), msg); -} - - void followed_hashtag_distribute(const xs_dict *msg) /* distribute this post to all users following the included hashtags */ { @@ -656,13 +666,6 @@ void followed_hashtag_distribute(const xs_dict *msg) } -int blocked_hashtag_check(snac *user, const xs_dict *msg) -/* returns 1 if this message contains a hashtag blocked by me */ -{ - return hashtag_in_msg(xs_dict_get(user->config, "blocked_hashtags"), msg); -} - - int is_msg_for_me(snac *snac, const xs_dict *c_msg) /* checks if this message is for me */ { @@ -2352,9 +2355,6 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) /* bring the actor */ xs *who_o = NULL; - if (blocked_hashtag_check(snac, a_msg)) - snac_debug(snac, 1, xs_fmt("blocked by hashtag %s", object)); - else if (valid_status(actor_request(snac, who, &who_o))) { /* don't account as such announces by our own relay */ xs *this_relay = xs_fmt("%s/relay", srv_baseurl); -- cgit From 6776a73712047185c41d9cd9c1316f621c47ed4f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 27 Feb 2025 19:03:01 +0100 Subject: Some tweaks to timeline_request(). --- activitypub.c | 100 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 1dedd17..5e1dd55 100644 --- a/activitypub.c +++ b/activitypub.c @@ -390,71 +390,71 @@ int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) } /* is the object already there? */ - if (!valid_status(object_get(*id, &msg))) { + if (!valid_status((status = object_get(*id, &msg)))) { /* no; download it */ status = activitypub_request(snac, *id, &msg); + } - if (valid_status(status)) { - const xs_dict *object = msg; - const char *type = xs_dict_get(object, "type"); + if (valid_status(status)) { + const xs_dict *object = msg; + const char *type = xs_dict_get(object, "type"); - /* get the id again from the object, as it may be different */ - const char *nid = xs_dict_get(object, "id"); + /* get the id again from the object, as it may be different */ + const char *nid = xs_dict_get(object, "id"); - if (xs_type(nid) != XSTYPE_STRING) - return 0; + if (xs_type(nid) != XSTYPE_STRING) + return 0; - if (wrk && strcmp(nid, *id) != 0) { - snac_debug(snac, 1, - xs_fmt("timeline_request canonical id for %s is %s", *id, nid)); + if (wrk && strcmp(nid, *id) != 0) { + snac_debug(snac, 1, + xs_fmt("timeline_request canonical id for %s is %s", *id, nid)); - *wrk = xs_dup(nid); - *id = *wrk; - } + *wrk = xs_dup(nid); + *id = *wrk; + } - if (xs_is_null(type)) - type = "(null)"; + if (xs_is_null(type)) + type = "(null)"; - srv_debug(2, xs_fmt("timeline_request type %s '%s'", nid, type)); + srv_debug(2, xs_fmt("timeline_request type %s '%s'", nid, type)); - if (strcmp(type, "Create") == 0) { - /* some software like lemmy nest Announce + Create + Note */ - if (!xs_is_null(object = xs_dict_get(object, "object"))) { - type = xs_dict_get(object, "type"); - nid = xs_dict_get(object, "id"); - } - else - type = "(null)"; + if (strcmp(type, "Create") == 0) { + /* some software like lemmy nest Announce + Create + Note */ + if (!xs_is_null(object = xs_dict_get(object, "object"))) { + type = xs_dict_get(object, "type"); + nid = xs_dict_get(object, "id"); } + else + type = "(null)"; + } - if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { - if (content_match("filter_reject.txt", object)) - snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); - else - if (blocked_hashtag_check(snac, object)) - snac_log(snac, xs_fmt("timeline_request rejected by hashtag %s", nid)); - else { - const char *actor = get_atto(object); - - if (!xs_is_null(actor)) { - /* request (and drop) the actor for this entry */ - if (!valid_status(actor_request(snac, actor, NULL))) { - /* failed? retry later */ - enqueue_actor_refresh(snac, actor, 60); - } + if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { + if (content_match("filter_reject.txt", object)) + snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); + else + if (blocked_hashtag_check(snac, object)) + snac_log(snac, xs_fmt("timeline_request rejected by hashtag %s", nid)); + else { + const char *actor = get_atto(object); + + if (!xs_is_null(actor)) { + /* request (and drop) the actor for this entry */ + if (!valid_status(actor_request(snac, actor, NULL))) { + /* failed? retry later */ + enqueue_actor_refresh(snac, actor, 60); + } - /* does it have an ancestor? */ - const char *in_reply_to = get_in_reply_to(object); + /* does it have an ancestor? */ + const char *in_reply_to = get_in_reply_to(object); - /* store */ - timeline_add(snac, nid, object); + /* store */ + timeline_add(snac, nid, object); - /* redistribute to lists for this user */ - list_distribute(snac, actor, object); + /* redistribute to lists for this user */ + list_distribute(snac, actor, object); - /* recurse! */ - timeline_request(snac, &in_reply_to, NULL, level + 1); - } + /* recurse! */ + timeline_request(snac, &in_reply_to, NULL, level + 1); } } } @@ -2360,7 +2360,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) xs *this_relay = xs_fmt("%s/relay", srv_baseurl); if (strcmp(actor, this_relay) != 0) { - if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) + if (valid_status(timeline_admire(snac, object, actor, 0))) snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); else snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", -- cgit From 8bd364bd49116b8b335272300ca05f25e1267df7 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 2 Mar 2025 06:04:52 +0100 Subject: msg_note() accepts a nullable post date. --- activitypub.c | 11 ++++++++--- html.c | 6 +++--- main.c | 2 +- mastoapi.c | 5 +++-- snac.h | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) (limited to 'activitypub.c') diff --git a/activitypub.c b/activitypub.c index 5e1dd55..c9f700f 100644 --- a/activitypub.c +++ b/activitypub.c @@ -1555,7 +1555,7 @@ xs_dict *msg_follow(snac *snac, const char *q) xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, const xs_str *in_reply_to, const xs_list *attach, - int scope, const char *lang_str) + int scope, const char *lang_str, const char *msg_date) /* creates a 'Note' message */ /* scope: 0, public; 1, private (mentioned only); 2, "quiet public"; 3, followers only */ { @@ -1569,7 +1569,12 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, xs *irt = NULL; xs *tag = xs_list_new(); xs *atls = xs_list_new(); - xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL); + + /* discard non-parseable dates */ + if (!xs_is_string(msg_date) || xs_parse_iso_date(msg_date, 0) == 0) + msg_date = NULL; + + xs_dict *msg = msg_base(snac, "Note", id, NULL, xs_or(msg_date, "@now"), NULL); xs_list *p; const xs_val *v; @@ -1782,7 +1787,7 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach, const xs_list *opts, int multiple, int end_secs) /* creates a Question message */ { - xs_dict *msg = msg_note(user, content, NULL, NULL, attach, 0, NULL); + xs_dict *msg = msg_note(user, content, NULL, NULL, attach, 0, NULL, NULL); int max = 8; xs_set seen; diff --git a/html.c b/html.c index cb30eec..8ffa2be 100644 --- a/html.c +++ b/html.c @@ -4020,7 +4020,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, const char *b64 = xs_dict_get(q_vars, "content"); int sz; xs *content = xs_base64_dec(b64, &sz); - xs *msg = msg_note(&snac, content, NULL, NULL, NULL, 0, NULL); + xs *msg = msg_note(&snac, content, NULL, NULL, NULL, 0, NULL, NULL); xs *c_msg = msg_create(&snac, msg); timeline_add(&snac, xs_dict_get(msg, "id"), msg); @@ -4220,7 +4220,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, enqueue_close_question(&snac, xs_dict_get(msg, "id"), end_secs); } else - msg = msg_note(&snac, content_2, to, in_reply_to, attach_list, priv, NULL); + msg = msg_note(&snac, content_2, to, in_reply_to, attach_list, priv, NULL, NULL); if (sensitive != NULL) { msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); @@ -4659,7 +4659,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, int c = 0; while (xs_list_next(ls, &v, &c)) { - xs *msg = msg_note(&snac, "", actor, irt, NULL, 1, NULL); + xs *msg = msg_note(&snac, "", actor, irt, NULL, 1, NULL, NULL); /* set the option */ msg = xs_dict_append(msg, "name", v); diff --git a/main.c b/main.c index a4ea961..3cd4524 100644 --- a/main.c +++ b/main.c @@ -710,7 +710,7 @@ int main(int argc, char *argv[]) if (strcmp(cmd, "note_unlisted") == 0) scope = 2; - msg = msg_note(&snac, content, NULL, NULL, attl, scope, getenv("LANG")); + msg = msg_note(&snac, content, NULL, NULL, attl, scope, getenv("LANG"), NULL); c_msg = msg_create(&snac, msg); diff --git a/mastoapi.c b/mastoapi.c index 797a4da..25b17e2 100644 --- a/mastoapi.c +++ b/mastoapi.c @@ -2628,6 +2628,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, const char *summary = xs_dict_get(args, "spoiler_text"); const char *media_ids = xs_dict_get(args, "media_ids"); const char *language = xs_dict_get(args, "language"); + const char *sched_date = xs_dict_get(args, "scheduled_at"); if (xs_is_null(media_ids)) media_ids = xs_dict_get(args, "media_ids[]"); @@ -2684,7 +2685,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (strcmp(visibility, "public") == 0) scope = 0; - xs *msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language); + xs *msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language, sched_date); if (!xs_is_null(summary) && *summary) { msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE)); @@ -3034,7 +3035,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, if (o) { const char *name = xs_dict_get(o, "name"); - xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL); + xs *msg = msg_note(&snac, "", atto, (char *)id, NULL, 1, NULL, NULL); msg = xs_dict_append(msg, "name", name); xs *c_msg = msg_create(&snac, msg); diff --git a/snac.h b/snac.h index ac1abcd..ac30c44 100644 --- a/snac.h +++ b/snac.h @@ -329,7 +329,7 @@ xs_dict *msg_follow(snac *snac, const char *actor); xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, const xs_str *in_reply_to, const xs_list *attach, - int scope, const char *lang); + int scope, const char *lang_str, const char *msg_date); xs_dict *msg_undo(snac *snac, const xs_val *object); xs_dict *msg_delete(snac *snac, const char *id); -- cgit