aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrunfink <grunfink@noreply.codeberg.org>2025-09-28 15:46:56 +0200
committergrunfink <grunfink@noreply.codeberg.org>2025-09-28 15:46:56 +0200
commitab698c2bd6cbe74b2cca0a17fe210e159a43a2ce (patch)
tree868fa6b77c7697f972cb7b9ea8d3ff49ab8652e1
parent65d868092cd8bddad703f9dad77867bb45ca3286 (diff)
parent5610ffb13bebe2739cb865f47ee73725512ec600 (diff)
Merge pull request 'implementing scopes' (#474) from byte/snac2:feature/visibility into master
Reviewed-on: https://codeberg.org/grunfink/snac2/pulls/474
-rw-r--r--activitypub.c106
-rw-r--r--data.c10
-rw-r--r--html.c144
-rw-r--r--main.c13
-rw-r--r--mastoapi.c21
-rw-r--r--snac.h8
6 files changed, 239 insertions, 63 deletions
diff --git a/activitypub.c b/activitypub.c
index 2d3d903..4ed2805 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -569,14 +569,21 @@ xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
}
while (xs_list_iter(&l, &v)) {
- if (expand_public && strcmp(v, public_address) == 0) {
- /* iterate the followers and add them */
- xs *fwers = follower_list(snac);
- const char *actor;
+ if (expand_public) {
+ if (strcmp(v, public_address) == 0 ||
+ /* check if it's a followers collection URL */
+ (xs_type(v) == XSTYPE_STRING &&
+ strcmp(v, xs_fmt("%s/followers", snac->actor)) == 0) ||
+ (xs_type(v) == XSTYPE_LIST &&
+ xs_list_in(v, xs_fmt("%s/followers", snac->actor)) != -1)) {
+ /* iterate the followers and add them */
+ xs *fwers = follower_list(snac);
+ const char *actor;
- char *p = fwers;
- while (xs_list_iter(&p, &actor))
- xs_set_add(&rcpts, actor);
+ char *p = fwers;
+ while (xs_list_iter(&p, &actor))
+ xs_set_add(&rcpts, actor);
+ }
}
else
xs_set_add(&rcpts, v);
@@ -1840,8 +1847,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
xs_list *p;
const xs_val *v;
- /* FIXME: implement scope 3 */
- int priv = scope == 1;
+ const int priv = (scope == SCOPE_MENTIONED || scope == SCOPE_FOLLOWERS);
if (rcpts == NULL)
to = xs_list_new();
@@ -1901,9 +1907,13 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
if ((v = xs_dict_get(p_msg, "conversation")))
msg = xs_dict_append(msg, "conversation", v);
- /* if this message is public, ours will also be */
- if (!priv && is_msg_public(p_msg) && xs_list_in(to, public_address) == -1)
+ /* if this message is public or unlisted, ours will also be */
+ const int orig_scope = get_msg_visibility(p_msg);
+ if (!priv && orig_scope == SCOPE_PUBLIC && xs_list_in(to, public_address) == -1)
to = xs_list_append(to, public_address);
+ else
+ if (!priv && orig_scope == SCOPE_UNLISTED && xs_list_in(cc, public_address) == -1)
+ cc = xs_list_append(cc, public_address);
}
irt = xs_dup(in_reply_to);
@@ -1947,28 +1957,50 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
if (ctxt == NULL)
ctxt = xs_fmt("%s#ctxt", id);
- /* add all mentions to the cc */
+ /* add all mentions to the appropriate field */
p = tag;
while (xs_list_iter(&p, &v)) {
if (xs_type(v) == XSTYPE_DICT) {
const char *t;
if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) {
- if (!xs_is_null(t = xs_dict_get(v, "href")))
- cc = xs_list_append(cc, t);
+ if (!xs_is_null(t = xs_dict_get(v, "href"))) {
+ if (scope == SCOPE_MENTIONED) {
+ /* for DMs, mentions go to 'to' */
+ to = xs_list_append(to, t);
+ } else {
+ /* for other visibility levels, mentions go to 'cc' */
+ cc = xs_list_append(cc, t);
+ }
+ }
}
}
}
- if (scope == 2) {
- /* Mastodon's "quiet public": add public address to cc */
+ if (scope == SCOPE_UNLISTED) {
+ /* Mastodon's "quiet public": remove from to, add public address to cc */
+ int idx;
+ if ((idx = xs_list_in(to, public_address)) != -1)
+ to = xs_list_del(to, idx);
if (xs_list_in(cc, public_address) == -1)
cc = xs_list_append(cc, public_address);
}
else
- /* no recipients? must be for everybody */
- if (!priv && xs_list_len(to) == 0)
- to = xs_list_append(to, public_address);
+ if (scope == SCOPE_FOLLOWERS) {
+ /* followers-only: add followers collection to to */
+ xs *followers_url = xs_fmt("%s/followers", snac->actor);
+ if (xs_list_in(to, followers_url) == -1)
+ to = xs_list_append(to, followers_url);
+ }
+ else
+ if (scope == SCOPE_PUBLIC) {
+ /* public: ensure public address is in to and not in cc */
+ int idx;
+ if ((idx = xs_list_in(cc, public_address)) != -1)
+ cc = xs_list_del(cc, idx);
+ if (xs_list_in(to, public_address) == -1)
+ to = xs_list_append(to, public_address);
+ }
/* delete all cc recipients that also are in the to */
p = to;
@@ -2113,6 +2145,42 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
return msg;
}
+int get_msg_visibility(const xs_dict *msg)
+/* determine visibility from message based on CC, TO and /followers mark */
+{
+ const xs_val *to = xs_dict_get(msg, "to");
+ const xs_val *cc = xs_dict_get(msg, "cc");
+
+ /* check if it's unlisted (public in cc but not in to) */
+ int pub_in_to = 0;
+ int pub_in_cc = 0;
+
+ if ((xs_type(to) == XSTYPE_STRING && strcmp(to, public_address) == 0) ||
+ (xs_type(to) == XSTYPE_LIST && xs_list_in(to, public_address) != -1))
+ pub_in_to = 1;
+
+ if ((xs_type(cc) == XSTYPE_STRING && strcmp(cc, public_address) == 0) ||
+ (xs_type(cc) == XSTYPE_LIST && xs_list_in(cc, public_address) != -1))
+ pub_in_cc = 1;
+
+ if (!pub_in_to && pub_in_cc) {
+ return SCOPE_UNLISTED;
+ }
+ if (pub_in_to && !pub_in_cc) {
+ return SCOPE_PUBLIC;
+ }
+
+ xs *followers_url = xs_fmt("%s/followers", xs_dict_get(msg, "attributedTo"));
+
+ if ((xs_type(to) == XSTYPE_STRING && strcmp(to, followers_url) == 0) ||
+ (xs_type(to) == XSTYPE_LIST && xs_list_in(to, followers_url) != -1))
+ return SCOPE_FOLLOWERS;
+ else
+ return SCOPE_MENTIONED;
+ /* should be unreachable, someone violated the protocol. */
+ /* better treat it as followers-only to make sure we don't leak information.*/
+ return SCOPE_FOLLOWERS;
+}
int update_question(snac *user, const char *id)
/* updates the poll counts */
diff --git a/data.c b/data.c
index 3b45665..53e9438 100644
--- a/data.c
+++ b/data.c
@@ -1456,15 +1456,16 @@ void timeline_update_indexes(snac *snac, const char *id)
xs *msg = NULL;
if (valid_status(object_get(id, &msg))) {
+ const int scope = get_msg_visibility(msg);
/* if its ours and is public, also store in public */
- if (is_msg_public(msg)) {
+ if (scope == SCOPE_PUBLIC) {
if (object_user_cache_add(snac, id, "public") >= 0) {
/* also add it to the instance public timeline */
xs *ipt = xs_fmt("%s/public.idx", srv_basedir);
index_add(ipt, id);
}
else
- srv_debug(1, xs_fmt("Not added to public instance index %s", id));
+ srv_debug(1, xs_fmt("Not added to public instance index %s, visibility %d", id, scope));
}
else
/* also add it to public, it will be discarded later */
@@ -2222,7 +2223,10 @@ void tag_index(const char *id, const xs_dict *obj)
const xs_list *tags = xs_dict_get(obj, "tag");
xs *md5_id = xs_md5_hex(id, strlen(id));
- if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) {
+ if (get_msg_visibility(obj) != SCOPE_PUBLIC)
+ return;
+
+ if (xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) {
xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir);
mkdirx(g_tag_dir);
diff --git a/html.c b/html.c
index ed9e739..2fca837 100644
--- a/html.c
+++ b/html.c
@@ -165,7 +165,7 @@ xs_str *format_text_with_emoji(snac *user, const char *text, int ems, const char
xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
- const char *udate, const char *url, int priv,
+ const char *udate, const char *url, int scope,
int in_people, const char *proxy, const char *lang,
const char *md5)
{
@@ -249,12 +249,35 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
xs_html_raw("&#129309;")));
}
- if (priv) {
- xs_html_add(actor_icon,
- xs_html_text(" "),
- xs_html_tag("span",
- xs_html_attr("title", "private"),
- xs_html_raw("&#128274;")));
+ if (scope != -1) {
+ if (scope == SCOPE_FOLLOWERS) {
+ xs_html_add(actor_icon,
+ xs_html_text(" "),
+ xs_html_tag("span",
+ xs_html_attr("title", "followers"),
+ xs_html_raw("&#128274;"))); // emoji of a lock
+ }
+ else if (scope == SCOPE_PUBLIC) {
+ xs_html_add(actor_icon,
+ xs_html_text(" "),
+ xs_html_tag("span",
+ xs_html_attr("title", "public"),
+ xs_html_raw("&#127760;"))); // emoji of a globe
+ }
+ else if (scope == SCOPE_UNLISTED) {
+ xs_html_add(actor_icon,
+ xs_html_text(" "),
+ xs_html_tag("span",
+ xs_html_attr("title", "unlisted"),
+ xs_html_raw("&#128275;"))); // emoji of an unlocked lock
+ }
+ else if (scope == SCOPE_MENTIONED) {
+ xs_html_add(actor_icon,
+ xs_html_text(" "),
+ xs_html_tag("span",
+ xs_html_attr("title", "mentioned"),
+ xs_html_raw("&#9993;&#65039;"))); // emoji of a mail
+ }
}
if (xs_is_null(date)) {
@@ -342,18 +365,17 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg,
const char *date = NULL;
const char *udate = NULL;
const char *url = NULL;
- int priv = 0;
const char *type = xs_dict_get(msg, "type");
+ const int scope = get_msg_visibility(msg);
if (xs_match(type, POSTLIKE_OBJECT_TYPE))
url = xs_dict_get(msg, "id");
- priv = !is_msg_public(msg);
date = xs_dict_get(msg, "published");
udate = xs_dict_get(msg, "updated");
- actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang, md5);
+ actor_icon = html_actor_icon(user, actor, date, udate, url, scope, 0, proxy, lang, md5);
}
return actor_icon;
@@ -365,7 +387,7 @@ xs_html *html_note(snac *user, const char *summary,
const char *ta_plh, const char *ta_content,
const char *edit_id, const char *actor_id,
const xs_val *cw_yn, const char *cw_text,
- const xs_val *mnt_only, const char *redir,
+ const int scope, const char *redir,
const char *in_reply_to, int poll,
const xs_list *att_files, const xs_list *att_alt_texts,
int is_draft, const char *published,
@@ -417,14 +439,44 @@ xs_html *html_note(snac *user, const char *summary,
xs_html_attr("name", "to"),
xs_html_attr("value", actor_id)));
else {
- /* no actor_id; ask for mentioned_only */
xs_html_add(form,
- xs_html_tag("p", NULL),
- xs_html_text(L("Only for mentioned people: ")),
- xs_html_sctag("input",
- xs_html_attr("type", "checkbox"),
- xs_html_attr("name", "mentioned_only"),
- xs_html_attr(xs_type(mnt_only) == XSTYPE_TRUE ? "checked" : "", NULL)));
+ xs_html_tag("p",
+ xs_html_text(L("Visibility: ")),
+ xs_html_tag("label",
+ xs_html_sctag("input",
+ xs_html_attr("type", "radio"),
+ xs_html_attr("name", "visibility"),
+ xs_html_attr("value", "public"),
+ xs_html_attr(scope == SCOPE_PUBLIC ? "checked" : "", NULL)),
+ xs_html_text(" "),
+ xs_html_text(L("Public"))),
+ xs_html_text(" "),
+ xs_html_tag("label",
+ xs_html_sctag("input",
+ xs_html_attr("type", "radio"),
+ xs_html_attr("name", "visibility"),
+ xs_html_attr("value", "unlisted"),
+ xs_html_attr(scope == SCOPE_UNLISTED ? "checked" : "", NULL)),
+ xs_html_text(" "),
+ xs_html_text(L("Unlisted"))),
+ xs_html_text(" "),
+ xs_html_tag("label",
+ xs_html_sctag("input",
+ xs_html_attr("type", "radio"),
+ xs_html_attr("name", "visibility"),
+ xs_html_attr("value", "followers"),
+ xs_html_attr(scope == SCOPE_FOLLOWERS ? "checked" : "", NULL)),
+ xs_html_text(" "),
+ xs_html_text(L("Followers-only"))),
+ xs_html_text(" "),
+ xs_html_tag("label",
+ xs_html_sctag("input",
+ xs_html_attr("type", "radio"),
+ xs_html_attr("name", "visibility"),
+ xs_html_attr("value", "mentioned"),
+ xs_html_attr(scope == SCOPE_MENTIONED ? "checked" : "", NULL)),
+ xs_html_text(" "),
+ xs_html_text(L("Direct Message")))));
}
if (redir)
@@ -1236,7 +1288,7 @@ xs_html *html_top_controls(snac *user)
L("What's on your mind?"), "",
NULL, NULL,
xs_stock(XSTYPE_FALSE), "",
- xs_stock(XSTYPE_FALSE), NULL,
+ SCOPE_PUBLIC, NULL,
NULL, 1, NULL, NULL, 0, NULL, NULL),
/** operations **/
@@ -1942,6 +1994,8 @@ xs_html *html_entry_controls(snac *user, const char *actor,
xs_dict_next(cmap, &note_lang, &dummy, &c);
}
+ const int edit_scope = get_msg_visibility(msg);
+
xs_html_add(controls, xs_html_tag("div",
xs_html_tag("p", NULL),
html_note(user, L("Edit..."),
@@ -1949,7 +2003,7 @@ xs_html *html_entry_controls(snac *user, const char *actor,
"", prev_src,
id, NULL,
xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"),
- xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir,
+ edit_scope, redir,
NULL, 0, att_files, att_alt_texts, is_draft(user, id),
xs_dict_get(msg, "published"), note_lang)),
xs_html_tag("p", NULL));
@@ -1962,6 +2016,8 @@ xs_html *html_entry_controls(snac *user, const char *actor,
xs *form_id = xs_fmt("%s_reply_form", md5);
xs *redir = xs_fmt("%s_entry", md5);
+ const int scope = get_msg_visibility(msg);
+
xs_html_add(controls, xs_html_tag("div",
xs_html_tag("p", NULL),
html_note(user, L("Reply..."),
@@ -1969,7 +2025,7 @@ xs_html *html_entry_controls(snac *user, const char *actor,
"", ct,
NULL, NULL,
xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"),
- xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir,
+ scope, redir,
id, 0, NULL, NULL, 0, NULL, NULL)),
xs_html_tag("p", NULL));
}
@@ -2061,6 +2117,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
xs_html_attr("name", s1)));
}
+ /* don't show followers-only notes from not followed users */
+ if (user && get_msg_visibility(msg) == SCOPE_FOLLOWERS &&
+ strcmp(user->actor, actor) != 0 && following_check(user, actor) == 0) {
+ return NULL;
+ }
+
if ((user == NULL || strcmp(actor, user->actor) != 0)
&& !valid_status(actor_get(actor, NULL))) {
@@ -2275,7 +2337,6 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
has_title = 1;
}
-
snac_content = xs_html_tag("div", NULL);
}
@@ -3089,8 +3150,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
xs_html_text(title)));
}
- xs_html_add(body,
- posts);
+ xs_html_add(body, posts);
int mark_shown = 0;
@@ -3130,8 +3190,9 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
if (user == NULL && is_msg_from_private_user(msg))
continue;
- /* is this message a non-public reply? */
- if (user != NULL && !is_msg_public(msg)) {
+ const int scope = get_msg_visibility(msg);
+ if (user != NULL && scope != SCOPE_PUBLIC){
+ /* is this message a non-public reply? */
const char *irt = get_in_reply_to(msg);
/* is it a reply to something not in the storage? */
@@ -3146,6 +3207,14 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
}
}
}
+ /* hide non-public posts from /instance view */
+ if (page != NULL && strcmp(page, "/instance") == 0 && scope != SCOPE_PUBLIC){
+ continue;
+ }
+ /* hide non-public posts viewed from outside */
+ if (read_only && scope != SCOPE_PUBLIC){
+ continue;
+ }
xs_html *entry = html_entry(user, msg, read_only, 0, v, (user && !hide_children) ? 0 : 1);
@@ -3256,7 +3325,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
xs_html_tag("div",
xs_html_attr("class", "snac-post-header"),
html_actor_icon(user, actor, xs_dict_get(actor, "published"),
- NULL, NULL, 0, 1, proxy, NULL, NULL)));
+ NULL, NULL, -1, 1, proxy, NULL, NULL)));
/* content (user bio) */
const char *c = xs_dict_get(actor, "summary");
@@ -3357,7 +3426,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
"", "",
NULL, actor_id,
xs_stock(XSTYPE_FALSE), "",
- xs_stock(XSTYPE_FALSE), NULL,
+ SCOPE_MENTIONED, NULL,
NULL, 0, NULL, NULL, 0, NULL, NULL),
xs_html_tag("p", NULL));
@@ -3564,7 +3633,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
xs_html_add(entry,
xs_html_tag("div",
xs_html_attr("class", "snac-post"),
- html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL)));
+ html_actor_icon(user, actor, NULL, NULL, NULL, -1, 0, proxy, NULL, NULL)));
}
else
if (strcmp(type, "Move") == 0) {
@@ -3578,7 +3647,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
xs_html_add(entry,
xs_html_tag("div",
xs_html_attr("class", "snac-post"),
- html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL)));
+ html_actor_icon(user, old_actor, NULL, NULL, NULL, -1, 0, proxy, NULL, NULL)));
}
}
}
@@ -4445,7 +4514,18 @@ int html_post_handler(const xs_dict *req, const char *q_path,
const char *post_date = xs_dict_get_def(p_vars, "post_date", "");
const char *post_time = xs_dict_get_def(p_vars, "post_time", "");
const char *post_lang = xs_dict_get(p_vars, "post_lang");
- int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only"));
+ const char *visibility = xs_dict_get(p_vars, "visibility");
+ int scope = SCOPE_PUBLIC; /* default to public */
+ if (!xs_is_null(visibility)) {
+ if (strcmp(visibility, "unlisted") == 0)
+ scope = SCOPE_UNLISTED;
+ else
+ if (strcmp(visibility, "followers") == 0)
+ scope = SCOPE_FOLLOWERS;
+ else
+ if (strcmp(visibility, "mentioned") == 0)
+ scope = SCOPE_MENTIONED;
+ }
int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft"));
xs *attach_list = xs_list_new();
@@ -4528,7 +4608,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, post_lang, NULL);
+ msg = msg_note(&snac, content_2, to, in_reply_to, attach_list, scope, post_lang, NULL);
if (sensitive != NULL) {
msg = xs_dict_set(msg, "sensitive", xs_stock(XSTYPE_TRUE));
diff --git a/main.c b/main.c
index f119197..46f88d2 100644
--- a/main.c
+++ b/main.c
@@ -45,6 +45,7 @@ int usage(const char *cmd)
"note {basedir} {uid} {text} [files...] Sends a note with optional attachments\n"
"note_unlisted {basedir} {uid} {text} [files...] Sends an unlisted note with optional attachments\n"
"note_mention {basedir} {uid} {text} [files...] Sends a note only to mentioned accounts\n"
+ "note_followers {basedir} {uid} {text} [files...] Sends a note only to followers\n"
"boost|announce {basedir} {uid} {url} Boosts (announces) a post\n"
"unboost {basedir} {uid} {url} Unboosts a post\n"
"resetpwd {basedir} {uid} Resets the password of a user\n"
@@ -800,7 +801,8 @@ int main(int argc, char *argv[])
if (strcmp(cmd, "note") == 0 || /** **/
strcmp(cmd, "note_unlisted") == 0 || /** **/
- strcmp(cmd, "note_mention") == 0) { /** **/
+ strcmp(cmd, "note_mention") == 0 || /** **/
+ strcmp(cmd, "note_followers") == 0) { /** **/
xs *content = NULL;
xs *msg = NULL;
xs *c_msg = NULL;
@@ -909,12 +911,15 @@ int main(int argc, char *argv[])
return 1;
}
- int scope = 0;
+ int scope = SCOPE_PUBLIC;
if (strcmp(cmd, "note_mention") == 0)
- scope = 1;
+ scope = SCOPE_MENTIONED;
else
if (strcmp(cmd, "note_unlisted") == 0)
- scope = 2;
+ scope = SCOPE_UNLISTED;
+ else
+ if (strcmp(cmd, "note_followers") == 0)
+ scope = SCOPE_FOLLOWERS;
msg = msg_note(&snac, content, NULL, in_reply_to, attl, scope, getenv("LANG"), post_date);
diff --git a/mastoapi.c b/mastoapi.c
index 8cbc189..26af7d2 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -884,8 +884,16 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
st = xs_dict_append(st, "content", s1);
}
- st = xs_dict_append(st, "visibility",
- is_msg_public(msg) ? "public" : "private");
+ /* determine visibility: https://docs.joinmastodon.org/entities/Status/#visibility */
+ const int scope = get_msg_visibility(msg);
+ if (scope == SCOPE_PUBLIC)
+ st = xs_dict_append(st, "visibility", "public");
+ else if (scope == SCOPE_FOLLOWERS)
+ st = xs_dict_append(st, "visibility", "private");
+ else if (scope == SCOPE_MENTIONED)
+ st = xs_dict_append(st, "visibility", "direct");
+ else if (scope == SCOPE_UNLISTED)
+ st = xs_dict_append(st, "visibility", "unlisted");
tmp = xs_dict_get(msg, "sensitive");
if (xs_is_null(tmp))
@@ -2856,12 +2864,15 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
}
/* prepare the message */
- int scope = 1;
+ int scope = SCOPE_MENTIONED;
if (strcmp(visibility, "unlisted") == 0)
- scope = 2;
+ scope = SCOPE_UNLISTED;
else
if (strcmp(visibility, "public") == 0)
- scope = 0;
+ scope = SCOPE_PUBLIC;
+ else
+ if (strcmp(visibility, "private") == 0)
+ scope = SCOPE_FOLLOWERS;
xs *msg = msg_note(&snac, content, NULL, irt, attach_list, scope, language, NULL);
diff --git a/snac.h b/snac.h
index aaa5558..5ed054a 100644
--- a/snac.h
+++ b/snac.h
@@ -78,6 +78,13 @@ typedef struct {
extern srv_state *p_state;
+enum {
+ SCOPE_PUBLIC = 0,
+ SCOPE_MENTIONED = 1,
+ SCOPE_UNLISTED = 2,
+ SCOPE_FOLLOWERS = 3,
+};
+
void snac_log(snac *user, xs_str *str);
#define snac_debug(user, level, str) do { if (dbglevel >= (level)) \
{ snac_log((user), (str)); } } while (0)
@@ -367,6 +374,7 @@ xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to);
xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
const xs_list *opts, int multiple, int end_secs);
xs_dict *msg_replies(snac *user, const char *id, int fill);
+int get_msg_visibility(const xs_dict *msg);
int activitypub_request(snac *snac, const char *url, xs_dict **data);
int actor_request(snac *user, const char *actor, xs_dict **data);