summaryrefslogtreecommitdiff
path: root/activitypub.c
diff options
context:
space:
mode:
Diffstat (limited to 'activitypub.c')
-rw-r--r--activitypub.c420
1 files changed, 206 insertions, 214 deletions
diff --git a/activitypub.c b/activitypub.c
index 9a23e14..6e40a88 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -67,7 +67,7 @@ int activitypub_request(snac *user, const char *url, xs_dict **data)
xs *response = NULL;
xs *payload = NULL;
int p_size;
- char *ctype;
+ const char *ctype;
*data = NULL;
@@ -154,20 +154,21 @@ int actor_request(snac *user, const char *actor, xs_dict **data)
}
-char *get_atto(const xs_dict *msg)
+const char *get_atto(const xs_dict *msg)
/* gets the attributedTo field (an actor) */
{
- char *actor = xs_dict_get(msg, "attributedTo");
+ const xs_val *actor = xs_dict_get(msg, "attributedTo");
/* if the actor is a list of objects (like on Peertube videos), pick the Person */
if (xs_type(actor) == XSTYPE_LIST) {
- xs_list *p = actor;
- xs_dict *v;
+ const xs_list *p = actor;
+ int c = 0;
+ const xs_dict *v;
actor = NULL;
- while (actor == NULL && xs_list_iter(&p, &v)) {
+ while (actor == NULL && xs_list_next(p, &v, &c)) {
if (xs_type(v) == XSTYPE_DICT) {
- char *type = xs_dict_get(v, "type");
+ const char *type = xs_dict_get(v, "type");
if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) {
actor = xs_dict_get(v, "id");
@@ -186,12 +187,12 @@ xs_list *get_attachments(const xs_dict *msg)
/* unify the garbage fire that are the attachments */
{
xs_list *l = xs_list_new();
- xs_list *p;
+ const xs_list *p;
/* try first the attachments list */
if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) {
xs *attach = NULL;
- xs_val *v;
+ const xs_val *v;
/* ensure it's a list */
if (xs_type(p) == XSTYPE_DICT) {
@@ -203,23 +204,24 @@ xs_list *get_attachments(const xs_dict *msg)
if (xs_type(attach) == XSTYPE_LIST) {
/* does the message have an image? */
- if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) {
+ const xs_dict *d = xs_dict_get(msg, "image");
+ if (xs_type(d) == XSTYPE_DICT) {
/* add it to the attachment list */
- attach = xs_list_append(attach, v);
+ attach = xs_list_append(attach, d);
}
}
/* now iterate the list */
- p = attach;
- while (xs_list_iter(&p, &v)) {
- char *type = xs_dict_get(v, "mediaType");
+ int c = 0;
+ while (xs_list_next(attach, &v, &c)) {
+ const char *type = xs_dict_get(v, "mediaType");
if (xs_is_null(type))
type = xs_dict_get(v, "type");
if (xs_is_null(type))
continue;
- char *href = xs_dict_get(v, "url");
+ const char *href = xs_dict_get(v, "url");
if (xs_is_null(href))
href = xs_dict_get(v, "href");
if (xs_is_null(href))
@@ -233,7 +235,7 @@ xs_list *get_attachments(const xs_dict *msg)
type = mt;
}
- char *name = xs_dict_get(v, "name");
+ const char *name = xs_dict_get(v, "name");
if (xs_is_null(name))
name = xs_dict_get(msg, "name");
if (xs_is_null(name))
@@ -252,29 +254,31 @@ xs_list *get_attachments(const xs_dict *msg)
p = xs_dict_get(msg, "url");
if (xs_type(p) == XSTYPE_LIST) {
- char *href = NULL;
- char *type = NULL;
- xs_val *v;
+ const char *href = NULL;
+ const char *type = NULL;
+ int c = 0;
+ const xs_val *v;
- while (href == NULL && xs_list_iter(&p, &v)) {
+ while (href == NULL && xs_list_next(p, &v, &c)) {
if (xs_type(v) == XSTYPE_DICT) {
- char *mtype = xs_dict_get(v, "type");
+ const char *mtype = xs_dict_get(v, "type");
if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) {
mtype = xs_dict_get(v, "mediaType");
- xs_list *tag = xs_dict_get(v, "tag");
+ const xs_list *tag = xs_dict_get(v, "tag");
if (xs_type(mtype) == XSTYPE_STRING &&
strcmp(mtype, "application/x-mpegURL") == 0 &&
xs_type(tag) == XSTYPE_LIST) {
/* now iterate the tag list, looking for a video URL */
- xs_dict *d;
+ const xs_dict *d;
+ int c = 0;
- while (href == NULL && xs_list_iter(&tag, &d)) {
+ while (href == NULL && xs_list_next(tag, &d, &c)) {
if (xs_type(d) == XSTYPE_DICT) {
if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING &&
xs_startswith(mtype, "video/")) {
- char *h = xs_dict_get(d, "href");
+ const char *h = xs_dict_get(d, "href");
/* this is probably it */
if (xs_type(h) == XSTYPE_STRING) {
@@ -303,7 +307,7 @@ xs_list *get_attachments(const xs_dict *msg)
}
-int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
+int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
/* ensures that an entry and its ancestors are in the timeline */
{
int status = 0;
@@ -323,7 +327,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
status = activitypub_request(snac, *id, &msg);
if (valid_status(status)) {
- xs_dict *object = msg;
+ 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 */
@@ -355,102 +359,35 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
type = "(null)";
}
- if (xs_match(type, "Note|Page|Article|Video")) {
- const char *actor = get_atto(object);
-
- if (content_check("filter_reject.txt", object))
+ 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 {
- /* request (and drop) the actor for this entry */
- if (!xs_is_null(actor))
- actor_request(snac, actor, NULL);
-
- /* does it have an ancestor? */
- char *in_reply_to = xs_dict_get(object, "inReplyTo");
-
- /* store */
- timeline_add(snac, nid, object);
-
- /* recurse! */
- timeline_request(snac, &in_reply_to, NULL, level + 1);
- }
- }
- }
- }
-
- enqueue_request_replies(snac, *id);
- }
-
- return status;
-}
+ 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);
+ }
-void timeline_request_replies(snac *user, const char *id)
-/* requests all replies of a message */
-/* FIXME: experimental -- needs more testing */
-{
- /* FIXME: TEMPORARILY DISABLED */
- /* Reason: I've found that many of the posts in the 'replies' Collection
- do not have an inReplyTo field (why??? aren't they 'replies'???).
- For this reason, these requested objects are not stored as children
- of the original post and they are shown as out-of-context, top level posts.
- This process is disabled until I find an elegant way of providing a parent
- for these 'stray' children. */
- return;
-
- xs *msg = NULL;
-
- if (!valid_status(object_get(id, &msg)))
- return;
-
- /* does it have a replies collection? */
- const xs_dict *replies = xs_dict_get(msg, "replies");
-
- if (!xs_is_null(replies)) {
- const char *type = xs_dict_get(replies, "type");
- const char *first = xs_dict_get(replies, "first");
-
- if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) {
- const char *next = xs_dict_get(first, "next");
-
- if (!xs_is_null(next)) {
- xs *rpls = NULL;
- int status = activitypub_request(user, next, &rpls);
-
- /* request the Collection of replies */
- if (valid_status(status)) {
- xs_list *items = xs_dict_get(rpls, "items");
-
- if (xs_type(items) == XSTYPE_LIST) {
- xs_val *v;
-
- /* request them all */
- while (xs_list_iter(&items, &v)) {
- if (xs_type(v) == XSTYPE_DICT) {
- /* not an id, but the object itself (!) */
- const char *c_id = xs_dict_get(v, "id");
-
- if (!xs_is_null(id)) {
- snac_debug(user, 0, xs_fmt("embedded reply %s", c_id));
+ /* does it have an ancestor? */
+ const char *in_reply_to = xs_dict_get(object, "inReplyTo");
- object_add(c_id, v);
+ /* store */
+ timeline_add(snac, nid, object);
- /* get its own children */
- timeline_request_replies(user, v);
- }
- }
- else {
- snac_debug(user, 0, xs_fmt("request reply %s", v));
- timeline_request(user, &v, NULL, 0);
- }
+ /* recurse! */
+ timeline_request(snac, &in_reply_to, NULL, level + 1);
}
}
}
- else
- snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status));
}
}
}
+
+ return status;
}
@@ -476,7 +413,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
xs_val **payload, int *p_size, int timeout)
/* sends a message to an Inbox */
{
- char *seckey = xs_dict_get(snac->key, "secret");
+ const char *seckey = xs_dict_get(snac->key, "secret");
return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout);
}
@@ -486,7 +423,7 @@ xs_str *get_actor_inbox(const char *actor)
/* gets an actor's inbox */
{
xs *data = NULL;
- char *v = NULL;
+ const char *v = NULL;
if (valid_status(actor_request(NULL, actor, &data))) {
/* try first endpoints/sharedInbox */
@@ -535,17 +472,17 @@ void post_message(snac *snac, const char *actor, const xs_dict *msg)
xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
/* returns the list of recipients for a message */
{
- char *to = xs_dict_get(msg, "to");
- char *cc = xs_dict_get(msg, "cc");
+ const xs_val *to = xs_dict_get(msg, "to");
+ const xs_val *cc = xs_dict_get(msg, "cc");
xs_set rcpts;
int n;
xs_set_init(&rcpts);
- char *lists[] = { to, cc, NULL };
+ const xs_list *lists[] = { to, cc, NULL };
for (n = 0; lists[n]; n++) {
- char *l = lists[n];
- char *v;
+ xs_list *l = (xs_list *)lists[n];
+ const char *v;
xs *tl = NULL;
/* if it's a string, create a list with only one element */
@@ -560,7 +497,7 @@ xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
if (expand_public && strcmp(v, public_address) == 0) {
/* iterate the followers and add them */
xs *fwers = follower_list(snac);
- char *actor;
+ const char *actor;
char *p = fwers;
while (xs_list_iter(&p, &actor))
@@ -667,13 +604,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
/* if it's a Follow, it must be explicitly for us */
if (xs_match(type, "Follow")) {
- char *object = xs_dict_get(c_msg, "object");
+ const char *object = xs_dict_get(c_msg, "object");
return !xs_is_null(object) && strcmp(snac->actor, object) == 0;
}
/* only accept Ping directed to us */
if (xs_match(type, "Ping")) {
- char *dest = xs_dict_get(c_msg, "to");
+ const char *dest = xs_dict_get(c_msg, "to");
return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0;
}
@@ -688,10 +625,10 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
if (pub_msg && following_check(snac, actor))
return 1;
- xs_dict *msg = xs_dict_get(c_msg, "object");
+ const xs_dict *msg = xs_dict_get(c_msg, "object");
xs *rcpts = recipient_list(snac, msg, 0);
xs_list *p = rcpts;
- xs_str *v;
+ const xs_str *v;
xs *actor_followers = NULL;
@@ -700,8 +637,9 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
xs *actor_obj = NULL;
if (valid_status(object_get(actor, &actor_obj))) {
- if ((v = xs_dict_get(actor_obj, "followers")))
- actor_followers = xs_dup(v);
+ const xs_val *fw = xs_dict_get(actor_obj, "followers");
+ if (fw)
+ actor_followers = xs_dup(fw);
}
}
@@ -724,13 +662,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
}
/* accept if it's by someone we follow */
- char *atto = get_atto(msg);
+ const char *atto = get_atto(msg);
if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
return 3;
/* is this message a reply to another? */
- char *irt = xs_dict_get(msg, "inReplyTo");
+ const char *irt = xs_dict_get(msg, "inReplyTo");
if (!xs_is_null(irt)) {
xs *r_msg = NULL;
@@ -755,7 +693,7 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag)
xs_list *tl = *tag;
xs *split;
xs_list *p;
- xs_val *v;
+ const xs_val *v;
int n = 0;
/* create a default server for incomplete mentions */
@@ -983,8 +921,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
/* telegram */
- char *bot = xs_dict_get(snac->config, "telegram_bot");
- char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
+ const char *bot = xs_dict_get(snac->config, "telegram_bot");
+ const char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id)
enqueue_telegram(body, bot, chat_id);
@@ -997,8 +935,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
objid = actor;
/* ntfy */
- char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
- char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
+ const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
+ const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
if (!xs_is_null(ntfy_server) && *ntfy_server)
enqueue_ntfy(body, ntfy_server, ntfy_token);
@@ -1084,7 +1022,7 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id,
}
-xs_dict *msg_collection(snac *snac, char *id)
+xs_dict *msg_collection(snac *snac, const char *id)
/* creates an empty OrderedCollection message */
{
xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL);
@@ -1098,7 +1036,7 @@ xs_dict *msg_collection(snac *snac, char *id)
}
-xs_dict *msg_accept(snac *snac, char *object, char *to)
+xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to)
/* creates an Accept message (as a response to a Follow) */
{
xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object);
@@ -1109,12 +1047,12 @@ xs_dict *msg_accept(snac *snac, char *object, char *to)
}
-xs_dict *msg_update(snac *snac, xs_dict *object)
+xs_dict *msg_update(snac *snac, const xs_dict *object)
/* creates an Update message */
{
xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object);
- char *type = xs_dict_get(object, "type");
+ const char *type = xs_dict_get(object, "type");
if (strcmp(type, "Note") == 0) {
msg = xs_dict_append(msg, "to", xs_dict_get(object, "to"));
@@ -1137,7 +1075,7 @@ xs_dict *msg_update(snac *snac, xs_dict *object)
}
-xs_dict *msg_admiration(snac *snac, char *object, char *type)
+xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
/* creates a Like or Announce message */
{
xs *a_msg = NULL;
@@ -1168,7 +1106,7 @@ xs_dict *msg_admiration(snac *snac, char *object, char *type)
}
-xs_dict *msg_repulsion(snac *user, char *id, char *type)
+xs_dict *msg_repulsion(snac *user, const char *id, const char *type)
/* creates an Undo + admiration message */
{
xs *a_msg = NULL;
@@ -1206,7 +1144,7 @@ xs_dict *msg_actor(snac *snac)
xs *kid = NULL;
xs *f_bio = NULL;
xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL);
- char *p;
+ const char *p;
int n;
/* change the @context (is this really necessary?) */
@@ -1264,11 +1202,11 @@ xs_dict *msg_actor(snac *snac)
}
/* add the metadata as attachments of PropertyValue */
- xs_dict *metadata = xs_dict_get(snac->config, "metadata");
+ const xs_dict *metadata = xs_dict_get(snac->config, "metadata");
if (xs_type(metadata) == XSTYPE_DICT) {
xs *attach = xs_list_new();
- xs_str *k;
- xs_str *v;
+ const xs_str *k;
+ const xs_str *v;
int c = 0;
while (xs_dict_next(metadata, &k, &v, &c)) {
@@ -1277,7 +1215,7 @@ xs_dict *msg_actor(snac *snac)
xs *k2 = encode_html(k);
xs *v2 = NULL;
- if (xs_startswith(v, "https:")) {
+ if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) {
xs *t = encode_html(v);
v2 = xs_fmt("<a href=\"%s\" rel=\"me\">%s</a>", t, t);
}
@@ -1310,7 +1248,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
/* creates a 'Create' message */
{
xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object);
- xs_val *v;
+ const xs_val *v;
if ((v = get_atto(object)))
msg = xs_dict_append(msg, "attributedTo", v);
@@ -1327,7 +1265,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
}
-xs_dict *msg_undo(snac *snac, char *object)
+xs_dict *msg_undo(snac *snac, const xs_val *object)
/* creates an 'Undo' message */
{
xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object);
@@ -1340,7 +1278,7 @@ xs_dict *msg_undo(snac *snac, char *object)
}
-xs_dict *msg_delete(snac *snac, char *id)
+xs_dict *msg_delete(snac *snac, const char *id)
/* creates a 'Delete' + 'Tombstone' for a local entry */
{
xs *tomb = xs_dict_new();
@@ -1369,7 +1307,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
xs *url_or_uid = xs_strip_i(xs_str_new(q));
- if (xs_startswith(url_or_uid, "https:/"))
+ if (xs_startswith(url_or_uid, "https:/") || xs_startswith(url_or_uid, "http:/"))
actor = xs_dup(url_or_uid);
else
if (!valid_status(webfinger_request(url_or_uid, &actor, NULL)) || actor == NULL) {
@@ -1382,7 +1320,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
if (valid_status(status)) {
/* check if the actor is an alias */
- char *r_actor = xs_dict_get(actor_o, "id");
+ const char *r_actor = xs_dict_get(actor_o, "id");
if (r_actor && strcmp(actor, r_actor) != 0) {
snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor));
@@ -1398,7 +1336,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
- xs_str *in_reply_to, xs_list *attach, int priv)
+ const xs_str *in_reply_to, const xs_list *attach, int priv)
/* creates a 'Note' message */
{
xs *ntid = tid(0);
@@ -1413,7 +1351,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
xs *atls = xs_list_new();
xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL);
xs_list *p;
- xs_val *v;
+ const xs_val *v;
if (rcpts == NULL)
to = xs_list_new();
@@ -1438,7 +1376,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
if (valid_status(object_get(in_reply_to, &p_msg))) {
/* add this author as recipient */
- char *a, *v;
+ const char *a, *v;
if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1)
to = xs_list_append(to, a);
@@ -1449,7 +1387,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
xs *actor_o = NULL;
if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) {
- char *uname = xs_dict_get(actor_o, "preferredUsername");
+ const char *uname = xs_dict_get(actor_o, "preferredUsername");
if (!xs_is_null(uname) && *uname) {
xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2));
@@ -1488,7 +1426,8 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
/* create the attachment list, if there are any */
if (!xs_is_null(attach)) {
- while (xs_list_iter(&attach, &v)) {
+ int c = 0;
+ while (xs_list_next(attach, &v, &c)) {
xs *d = xs_dict_new();
const char *url = xs_list_get(v, 0);
const char *alt = xs_list_get(v, 1);
@@ -1511,7 +1450,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
p = tag;
while (xs_list_iter(&p, &v)) {
if (xs_type(v) == XSTYPE_DICT) {
- char *t;
+ 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")))
@@ -1589,7 +1528,7 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
xs *o = xs_list_new();
xs_list *p = (xs_list *)opts;
- xs_str *v;
+ const xs_str *v;
xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}");
xs_set_init(&seen);
@@ -1635,9 +1574,9 @@ int update_question(snac *user, const char *id)
xs *msg = NULL;
xs *rcnt = xs_dict_new();
xs *lopts = xs_list_new();
- xs_list *opts;
+ const xs_list *opts;
xs_list *p;
- xs_val *v;
+ const xs_val *v;
/* get the object */
if (!valid_status(object_get(id, &msg)))
@@ -1653,8 +1592,8 @@ int update_question(snac *user, const char *id)
return -3;
/* fill the initial count */
- p = opts;
- while (xs_list_iter(&p, &v)) {
+ int c = 0;
+ while (xs_list_next(opts, &v, &c)) {
const char *name = xs_dict_get(v, "name");
if (name) {
lopts = xs_list_append(lopts, name);
@@ -1760,13 +1699,13 @@ int update_question(snac *user, const char *id)
/** queues **/
-int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
+int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
/* processes an ActivityPub message from the input queue */
/* return values: -1, fatal error; 0, transient error, retry;
1, processed and done; 2, propagate to users (only when no user is set) */
{
- char *actor = xs_dict_get(msg, "actor");
- char *type = xs_dict_get(msg, "type");
+ const char *actor = xs_dict_get(msg, "actor");
+ const char *type = xs_dict_get(msg, "type");
xs *actor_o = NULL;
int a_status;
int do_notify = 0;
@@ -1786,7 +1725,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
return -1;
}
- char *object, *utype;
+ const char *object, *utype;
object = xs_dict_get(msg, "object");
if (object != NULL && xs_type(object) == XSTYPE_DICT)
@@ -1809,7 +1748,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
}
/* also discard if the object to be deleted is not here */
- char *obj_id = object;
+ const char *obj_id = object;
if (xs_type(obj_id) == XSTYPE_DICT)
obj_id = xs_dict_get(obj_id, "id");
@@ -1881,7 +1820,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age"));
if (min_account_age > 0) {
- char *actor_date = xs_dict_get(actor_o, "published");
+ const char *actor_date = xs_dict_get(actor_o, "published");
if (!xs_is_null(actor_date)) {
time_t actor_t = xs_parse_iso_date(actor_date, 0);
@@ -1941,16 +1880,39 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
}
else
if (strcmp(type, "Undo") == 0) { /** **/
+ const char *id = xs_dict_get(object, "object");
+
if (xs_type(object) != XSTYPE_DICT)
utype = "Follow";
if (strcmp(utype, "Follow") == 0) { /** **/
- if (valid_status(follower_del(snac, actor))) {
- snac_log(snac, xs_fmt("no longer following us %s", actor));
- do_notify = 1;
+ if (id && strcmp(id, snac->actor) != 0)
+ snac_debug(snac, 1, xs_fmt("Undo + Follow from %s not for us (%s)", actor, id));
+ else {
+ if (valid_status(follower_del(snac, actor))) {
+ snac_log(snac, xs_fmt("no longer following us %s", actor));
+ do_notify = 1;
+ }
+ else
+ snac_log(snac, xs_fmt("error deleting follower %s", actor));
}
- else
- snac_log(snac, xs_fmt("error deleting follower %s", actor));
+ }
+ else
+ if (strcmp(utype, "Like") == 0) { /** **/
+ int status = object_unadmire(id, actor, 1);
+
+ snac_log(snac, xs_fmt("Unlike for %s %d", id, status));
+ }
+ else
+ if (strcmp(utype, "Announce") == 0) { /** **/
+ int status = 200;
+
+ /* commented out: if a followed user boosts something that
+ is requested and then unboosts, the post remains here,
+ but with no apparent reason, and that is confusing */
+ //status = object_unadmire(id, actor, 0);
+
+ snac_log(snac, xs_fmt("Unboost for %s %d", id, status));
}
else
snac_debug(snac, 1, xs_fmt("ignored 'Undo' for object type '%s'", utype));
@@ -1963,19 +1925,29 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
}
if (xs_match(utype, "Note|Article")) { /** **/
- char *id = xs_dict_get(object, "id");
- char *in_reply_to = xs_dict_get(object, "inReplyTo");
+ const char *id = xs_dict_get(object, "id");
+ const char *in_reply_to = xs_dict_get(object, "inReplyTo");
+ const char *atto = get_atto(object);
xs *wrk = NULL;
+ if (xs_is_null(id))
+ snac_log(snac, xs_fmt("malformed message: no 'id' field"));
+ else
+ if (xs_is_null(atto))
+ snac_log(snac, xs_fmt("malformed message: no 'attributedTo' field"));
+ else
if (!xs_is_null(in_reply_to) && is_hidden(snac, in_reply_to)) {
snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to));
}
else {
- if (content_check("filter_reject.txt", object)) {
+ if (content_match("filter_reject.txt", object)) {
snac_log(snac, xs_fmt("rejected by content %s", id));
return 1;
}
+ if (strcmp(actor, atto) != 0)
+ snac_log(snac, xs_fmt("SUSPICIOUS: actor != atto (%s != %s)", actor, atto));
+
timeline_request(snac, &in_reply_to, &wrk, 0);
if (timeline_add(snac, id, object)) {
@@ -1992,14 +1964,14 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
}
else
if (strcmp(utype, "Question") == 0) { /** **/
- char *id = xs_dict_get(object, "id");
+ const char *id = xs_dict_get(object, "id");
if (timeline_add(snac, id, object))
snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id));
}
else
if (strcmp(utype, "Video") == 0) { /** **/
- char *id = xs_dict_get(object, "id");
+ const char *id = xs_dict_get(object, "id");
if (timeline_add(snac, id, object))
snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id));
@@ -2080,6 +2052,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",
actor, object));
+ /* distribute the post with the actor as 'proxy' */
+ list_distribute(snac, actor, a_msg);
+
do_notify = 1;
}
else
@@ -2172,7 +2147,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
}
-int send_email(char *msg)
+int send_email(const char *msg)
/* invoke sendmail with email headers and body in msg */
{
FILE *f;
@@ -2204,18 +2179,18 @@ int send_email(char *msg)
void process_user_queue_item(snac *snac, xs_dict *q_item)
/* processes an item from the user queue */
{
- char *type;
+ const char *type;
int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
if ((type = xs_dict_get(q_item, "type")) == NULL)
type = "output";
if (strcmp(type, "message") == 0) {
- xs_dict *msg = xs_dict_get(q_item, "message");
+ const xs_dict *msg = xs_dict_get(q_item, "message");
xs *rcpts = recipient_list(snac, msg, 1);
xs_set inboxes;
xs_list *p;
- xs_str *actor;
+ const xs_str *actor;
xs_set_init(&inboxes);
@@ -2237,7 +2212,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
if (is_msg_public(msg)) {
if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) {
xs *shibx = inbox_list();
- xs_str *inbox;
+ const xs_str *inbox;
p = shibx;
while (xs_list_iter(&p, &inbox)) {
@@ -2252,8 +2227,8 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
else
if (strcmp(type, "input") == 0) {
/* process the message */
- xs_dict *msg = xs_dict_get(q_item, "message");
- xs_dict *req = xs_dict_get(q_item, "req");
+ const xs_dict *msg = xs_dict_get(q_item, "message");
+ const xs_dict *req = xs_dict_get(q_item, "req");
int retries = xs_number_get(xs_dict_get(q_item, "retries"));
if (xs_is_null(msg))
@@ -2280,11 +2255,20 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
update_question(snac, id);
}
else
- if (strcmp(type, "request_replies") == 0) {
+ if (strcmp(type, "object_request") == 0) {
const char *id = xs_dict_get(q_item, "message");
- if (!xs_is_null(id))
- timeline_request_replies(snac, id);
+ if (!xs_is_null(id)) {
+ int status;
+ xs *data = NULL;
+
+ status = activitypub_request(snac, id, &data);
+
+ if (valid_status(status))
+ object_add_ow(id, data);
+
+ snac_debug(snac, 1, xs_fmt("object_request %s %d", id, status));
+ }
}
else
if (strcmp(type, "verify_links") == 0) {
@@ -2320,7 +2304,7 @@ int process_user_queue(snac *snac)
xs *list = user_queue(snac);
xs_list *p = list;
- xs_str *fn;
+ const xs_str *fn;
while (xs_list_iter(&p, &fn)) {
xs *q_item = dequeue(fn);
@@ -2339,19 +2323,20 @@ int process_user_queue(snac *snac)
void process_queue_item(xs_dict *q_item)
/* processes an item from the global queue */
{
- char *type = xs_dict_get(q_item, "type");
+ const char *type = xs_dict_get(q_item, "type");
int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
if (strcmp(type, "output") == 0) {
int status;
- xs_str *inbox = xs_dict_get(q_item, "inbox");
- xs_str *keyid = xs_dict_get(q_item, "keyid");
- xs_str *seckey = xs_dict_get(q_item, "seckey");
- xs_dict *msg = xs_dict_get(q_item, "message");
+ const xs_str *inbox = xs_dict_get(q_item, "inbox");
+ const xs_str *keyid = xs_dict_get(q_item, "keyid");
+ const xs_str *seckey = xs_dict_get(q_item, "seckey");
+ const xs_dict *msg = xs_dict_get(q_item, "message");
int retries = xs_number_get(xs_dict_get(q_item, "retries"));
int p_status = xs_number_get(xs_dict_get(q_item, "p_status"));
xs *payload = NULL;
int p_size = 0;
+ int timeout = 0;
if (xs_is_null(inbox) || xs_is_null(msg) || xs_is_null(keyid) || xs_is_null(seckey)) {
srv_log(xs_fmt("output message error: missing fields"));
@@ -2364,8 +2349,15 @@ void process_queue_item(xs_dict *q_item)
}
/* deliver (if previous error status was a timeout, try now longer) */
- status = send_to_inbox_raw(keyid, seckey, inbox, msg,
- &payload, &p_size, p_status == 599 ? 8 : 6);
+ if (p_status == 599)
+ timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout_2", "8"));
+ else
+ timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout", "6"));
+
+ if (timeout == 0)
+ timeout = 6;
+
+ status = send_to_inbox_raw(keyid, seckey, inbox, msg, &payload, &p_size, timeout);
if (payload) {
if (p_size > 64) {
@@ -2411,7 +2403,7 @@ void process_queue_item(xs_dict *q_item)
else
if (strcmp(type, "email") == 0) {
/* send this email */
- xs_str *msg = xs_dict_get(q_item, "message");
+ const xs_str *msg = xs_dict_get(q_item, "message");
int retries = xs_number_get(xs_dict_get(q_item, "retries"));
if (!send_email(msg))
@@ -2433,8 +2425,8 @@ void process_queue_item(xs_dict *q_item)
else
if (strcmp(type, "telegram") == 0) {
/* send this via telegram */
- char *bot = xs_dict_get(q_item, "bot");
- char *msg = xs_dict_get(q_item, "message");
+ const char *bot = xs_dict_get(q_item, "bot");
+ const char *msg = xs_dict_get(q_item, "message");
xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id"));
int status = 0;
@@ -2457,9 +2449,9 @@ void process_queue_item(xs_dict *q_item)
else
if (strcmp(type, "ntfy") == 0) {
/* send this via ntfy */
- char *ntfy_server = xs_dict_get(q_item, "ntfy_server");
- char *msg = xs_dict_get(q_item, "message");
- char *ntfy_token = xs_dict_get(q_item, "ntfy_token");
+ const char *ntfy_server = xs_dict_get(q_item, "ntfy_server");
+ const char *msg = xs_dict_get(q_item, "message");
+ const char *ntfy_token = xs_dict_get(q_item, "ntfy_token");
int status = 0;
xs *url = xs_fmt("%s", ntfy_server);
@@ -2488,8 +2480,8 @@ void process_queue_item(xs_dict *q_item)
}
else
if (strcmp(type, "input") == 0) {
- xs_dict *msg = xs_dict_get(q_item, "message");
- xs_dict *req = xs_dict_get(q_item, "req");
+ const xs_dict *msg = xs_dict_get(q_item, "message");
+ const xs_dict *req = xs_dict_get(q_item, "req");
int retries = xs_number_get(xs_dict_get(q_item, "retries"));
/* do some instance-level checks */
@@ -2497,8 +2489,6 @@ void process_queue_item(xs_dict *q_item)
if (r == 0) {
/* transient error? retry */
- int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
-
if (retries > queue_retry_max)
srv_log(xs_fmt("shared input giving up"));
else {
@@ -2510,7 +2500,7 @@ void process_queue_item(xs_dict *q_item)
else
if (r == 2) {
/* redistribute the input message to all users */
- char *ntid = xs_dict_get(q_item, "ntid");
+ const char *ntid = xs_dict_get(q_item, "ntid");
xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid);
FILE *f;
@@ -2521,7 +2511,7 @@ void process_queue_item(xs_dict *q_item)
xs *users = user_list();
xs_list *p = users;
- char *v;
+ const char *v;
int cnt = 0;
while (xs_list_iter(&p, &v)) {
@@ -2564,7 +2554,7 @@ int process_queue(void)
xs *list = queue();
xs_list *p = list;
- xs_str *fn;
+ const xs_str *fn;
while (xs_list_iter(&p, &fn)) {
xs *q_item = dequeue(fn);
@@ -2585,7 +2575,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
char **body, int *b_size, char **ctype)
{
int status = 200;
- char *accept = xs_dict_get(req, "accept");
+ const char *accept = xs_dict_get(req, "accept");
snac snac;
xs *msg = NULL;
@@ -2597,7 +2587,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
return 0;
xs *l = xs_split_n(q_path, "/", 2);
- char *uid, *p_path;
+ const char *uid;
+ const char *p_path;
uid = xs_list_get(l, 1);
if (!user_open(&snac, uid)) {
@@ -2615,7 +2606,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
msg = msg_actor(&snac);
*ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
- char *ua = xs_dict_get(req, "user-agent");
+ const char *ua = xs_dict_get(req, "user-agent");
snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA"));
}
@@ -2625,15 +2616,16 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
xs *elems = timeline_simple_list(&snac, "public", 0, 20);
xs *list = xs_list_new();
msg = msg_collection(&snac, id);
- char *p, *v;
+ char *p;
+ const char *v;
p = elems;
while (xs_list_iter(&p, &v)) {
xs *i = NULL;
if (valid_status(object_get_by_md5(v, &i))) {
- char *type = xs_dict_get(i, "type");
- char *id = xs_dict_get(i, "id");
+ const char *type = xs_dict_get(i, "type");
+ const char *id = xs_dict_get(i, "id");
if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) {
xs *c_msg = msg_create(&snac, i);
@@ -2686,9 +2678,9 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
(void)b_size;
int status = 202; /* accepted */
- char *i_ctype = xs_dict_get(req, "content-type");
+ const char *i_ctype = xs_dict_get(req, "content-type");
snac snac;
- char *v;
+ const char *v;
if (i_ctype == NULL) {
*body = xs_str_new("no content-type");