summaryrefslogtreecommitdiff
path: root/activitypub.c
diff options
context:
space:
mode:
authorGravatar Louis Brauer2024-05-25 08:05:36 +0000
committerGravatar Louis Brauer2024-05-25 08:05:36 +0000
commit84a767dd0878013194ed7551b5ae6ef715e841a6 (patch)
tree9fb1b2b89e0bfbb4b8bf1e85d840c8653e646bb7 /activitypub.c
parentPrevent some browsers from caching servers basic auth request (diff)
parentBackport from xs (fix regex.h compilation with tcc). (diff)
downloadsnac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.gz
snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.xz
snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.zip
Merge pull request 'master' (#1) from grunfink/snac2:master into master
Reviewed-on: https://codeberg.org/louis77/snac2/pulls/1
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)
67 xs *response = NULL; 67 xs *response = NULL;
68 xs *payload = NULL; 68 xs *payload = NULL;
69 int p_size; 69 int p_size;
70 char *ctype; 70 const char *ctype;
71 71
72 *data = NULL; 72 *data = NULL;
73 73
@@ -154,20 +154,21 @@ int actor_request(snac *user, const char *actor, xs_dict **data)
154} 154}
155 155
156 156
157char *get_atto(const xs_dict *msg) 157const char *get_atto(const xs_dict *msg)
158/* gets the attributedTo field (an actor) */ 158/* gets the attributedTo field (an actor) */
159{ 159{
160 char *actor = xs_dict_get(msg, "attributedTo"); 160 const xs_val *actor = xs_dict_get(msg, "attributedTo");
161 161
162 /* if the actor is a list of objects (like on Peertube videos), pick the Person */ 162 /* if the actor is a list of objects (like on Peertube videos), pick the Person */
163 if (xs_type(actor) == XSTYPE_LIST) { 163 if (xs_type(actor) == XSTYPE_LIST) {
164 xs_list *p = actor; 164 const xs_list *p = actor;
165 xs_dict *v; 165 int c = 0;
166 const xs_dict *v;
166 actor = NULL; 167 actor = NULL;
167 168
168 while (actor == NULL && xs_list_iter(&p, &v)) { 169 while (actor == NULL && xs_list_next(p, &v, &c)) {
169 if (xs_type(v) == XSTYPE_DICT) { 170 if (xs_type(v) == XSTYPE_DICT) {
170 char *type = xs_dict_get(v, "type"); 171 const char *type = xs_dict_get(v, "type");
171 if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) { 172 if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) {
172 actor = xs_dict_get(v, "id"); 173 actor = xs_dict_get(v, "id");
173 174
@@ -186,12 +187,12 @@ xs_list *get_attachments(const xs_dict *msg)
186/* unify the garbage fire that are the attachments */ 187/* unify the garbage fire that are the attachments */
187{ 188{
188 xs_list *l = xs_list_new(); 189 xs_list *l = xs_list_new();
189 xs_list *p; 190 const xs_list *p;
190 191
191 /* try first the attachments list */ 192 /* try first the attachments list */
192 if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) { 193 if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) {
193 xs *attach = NULL; 194 xs *attach = NULL;
194 xs_val *v; 195 const xs_val *v;
195 196
196 /* ensure it's a list */ 197 /* ensure it's a list */
197 if (xs_type(p) == XSTYPE_DICT) { 198 if (xs_type(p) == XSTYPE_DICT) {
@@ -203,23 +204,24 @@ xs_list *get_attachments(const xs_dict *msg)
203 204
204 if (xs_type(attach) == XSTYPE_LIST) { 205 if (xs_type(attach) == XSTYPE_LIST) {
205 /* does the message have an image? */ 206 /* does the message have an image? */
206 if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) { 207 const xs_dict *d = xs_dict_get(msg, "image");
208 if (xs_type(d) == XSTYPE_DICT) {
207 /* add it to the attachment list */ 209 /* add it to the attachment list */
208 attach = xs_list_append(attach, v); 210 attach = xs_list_append(attach, d);
209 } 211 }
210 } 212 }
211 213
212 /* now iterate the list */ 214 /* now iterate the list */
213 p = attach; 215 int c = 0;
214 while (xs_list_iter(&p, &v)) { 216 while (xs_list_next(attach, &v, &c)) {
215 char *type = xs_dict_get(v, "mediaType"); 217 const char *type = xs_dict_get(v, "mediaType");
216 if (xs_is_null(type)) 218 if (xs_is_null(type))
217 type = xs_dict_get(v, "type"); 219 type = xs_dict_get(v, "type");
218 220
219 if (xs_is_null(type)) 221 if (xs_is_null(type))
220 continue; 222 continue;
221 223
222 char *href = xs_dict_get(v, "url"); 224 const char *href = xs_dict_get(v, "url");
223 if (xs_is_null(href)) 225 if (xs_is_null(href))
224 href = xs_dict_get(v, "href"); 226 href = xs_dict_get(v, "href");
225 if (xs_is_null(href)) 227 if (xs_is_null(href))
@@ -233,7 +235,7 @@ xs_list *get_attachments(const xs_dict *msg)
233 type = mt; 235 type = mt;
234 } 236 }
235 237
236 char *name = xs_dict_get(v, "name"); 238 const char *name = xs_dict_get(v, "name");
237 if (xs_is_null(name)) 239 if (xs_is_null(name))
238 name = xs_dict_get(msg, "name"); 240 name = xs_dict_get(msg, "name");
239 if (xs_is_null(name)) 241 if (xs_is_null(name))
@@ -252,29 +254,31 @@ xs_list *get_attachments(const xs_dict *msg)
252 p = xs_dict_get(msg, "url"); 254 p = xs_dict_get(msg, "url");
253 255
254 if (xs_type(p) == XSTYPE_LIST) { 256 if (xs_type(p) == XSTYPE_LIST) {
255 char *href = NULL; 257 const char *href = NULL;
256 char *type = NULL; 258 const char *type = NULL;
257 xs_val *v; 259 int c = 0;
260 const xs_val *v;
258 261
259 while (href == NULL && xs_list_iter(&p, &v)) { 262 while (href == NULL && xs_list_next(p, &v, &c)) {
260 if (xs_type(v) == XSTYPE_DICT) { 263 if (xs_type(v) == XSTYPE_DICT) {
261 char *mtype = xs_dict_get(v, "type"); 264 const char *mtype = xs_dict_get(v, "type");
262 265
263 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) { 266 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) {
264 mtype = xs_dict_get(v, "mediaType"); 267 mtype = xs_dict_get(v, "mediaType");
265 xs_list *tag = xs_dict_get(v, "tag"); 268 const xs_list *tag = xs_dict_get(v, "tag");
266 269
267 if (xs_type(mtype) == XSTYPE_STRING && 270 if (xs_type(mtype) == XSTYPE_STRING &&
268 strcmp(mtype, "application/x-mpegURL") == 0 && 271 strcmp(mtype, "application/x-mpegURL") == 0 &&
269 xs_type(tag) == XSTYPE_LIST) { 272 xs_type(tag) == XSTYPE_LIST) {
270 /* now iterate the tag list, looking for a video URL */ 273 /* now iterate the tag list, looking for a video URL */
271 xs_dict *d; 274 const xs_dict *d;
275 int c = 0;
272 276
273 while (href == NULL && xs_list_iter(&tag, &d)) { 277 while (href == NULL && xs_list_next(tag, &d, &c)) {
274 if (xs_type(d) == XSTYPE_DICT) { 278 if (xs_type(d) == XSTYPE_DICT) {
275 if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING && 279 if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING &&
276 xs_startswith(mtype, "video/")) { 280 xs_startswith(mtype, "video/")) {
277 char *h = xs_dict_get(d, "href"); 281 const char *h = xs_dict_get(d, "href");
278 282
279 /* this is probably it */ 283 /* this is probably it */
280 if (xs_type(h) == XSTYPE_STRING) { 284 if (xs_type(h) == XSTYPE_STRING) {
@@ -303,7 +307,7 @@ xs_list *get_attachments(const xs_dict *msg)
303} 307}
304 308
305 309
306int timeline_request(snac *snac, char **id, xs_str **wrk, int level) 310int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
307/* ensures that an entry and its ancestors are in the timeline */ 311/* ensures that an entry and its ancestors are in the timeline */
308{ 312{
309 int status = 0; 313 int status = 0;
@@ -323,7 +327,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
323 status = activitypub_request(snac, *id, &msg); 327 status = activitypub_request(snac, *id, &msg);
324 328
325 if (valid_status(status)) { 329 if (valid_status(status)) {
326 xs_dict *object = msg; 330 const xs_dict *object = msg;
327 const char *type = xs_dict_get(object, "type"); 331 const char *type = xs_dict_get(object, "type");
328 332
329 /* get the id again from the object, as it may be different */ 333 /* 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)
355 type = "(null)"; 359 type = "(null)";
356 } 360 }
357 361
358 if (xs_match(type, "Note|Page|Article|Video")) { 362 if (xs_match(type, POSTLIKE_OBJECT_TYPE)) {
359 const char *actor = get_atto(object); 363 if (content_match("filter_reject.txt", object))
360
361 if (content_check("filter_reject.txt", object))
362 snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); 364 snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid));
363 else { 365 else {
364 /* request (and drop) the actor for this entry */ 366 const char *actor = get_atto(object);
365 if (!xs_is_null(actor))
366 actor_request(snac, actor, NULL);
367
368 /* does it have an ancestor? */
369 char *in_reply_to = xs_dict_get(object, "inReplyTo");
370
371 /* store */
372 timeline_add(snac, nid, object);
373
374 /* recurse! */
375 timeline_request(snac, &in_reply_to, NULL, level + 1);
376 }
377 }
378 }
379 }
380
381 enqueue_request_replies(snac, *id);
382 }
383
384 return status;
385}
386 367
368 if (!xs_is_null(actor)) {
369 /* request (and drop) the actor for this entry */
370 if (!valid_status(actor_request(snac, actor, NULL))) {
371 /* failed? retry later */
372 enqueue_actor_refresh(snac, actor, 60);
373 }
387 374
388void timeline_request_replies(snac *user, const char *id) 375 /* does it have an ancestor? */
389/* requests all replies of a message */ 376 const char *in_reply_to = xs_dict_get(object, "inReplyTo");
390/* FIXME: experimental -- needs more testing */
391{
392 /* FIXME: TEMPORARILY DISABLED */
393 /* Reason: I've found that many of the posts in the 'replies' Collection
394 do not have an inReplyTo field (why??? aren't they 'replies'???).
395 For this reason, these requested objects are not stored as children
396 of the original post and they are shown as out-of-context, top level posts.
397 This process is disabled until I find an elegant way of providing a parent
398 for these 'stray' children. */
399 return;
400
401 xs *msg = NULL;
402
403 if (!valid_status(object_get(id, &msg)))
404 return;
405
406 /* does it have a replies collection? */
407 const xs_dict *replies = xs_dict_get(msg, "replies");
408
409 if (!xs_is_null(replies)) {
410 const char *type = xs_dict_get(replies, "type");
411 const char *first = xs_dict_get(replies, "first");
412
413 if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) {
414 const char *next = xs_dict_get(first, "next");
415
416 if (!xs_is_null(next)) {
417 xs *rpls = NULL;
418 int status = activitypub_request(user, next, &rpls);
419
420 /* request the Collection of replies */
421 if (valid_status(status)) {
422 xs_list *items = xs_dict_get(rpls, "items");
423
424 if (xs_type(items) == XSTYPE_LIST) {
425 xs_val *v;
426
427 /* request them all */
428 while (xs_list_iter(&items, &v)) {
429 if (xs_type(v) == XSTYPE_DICT) {
430 /* not an id, but the object itself (!) */
431 const char *c_id = xs_dict_get(v, "id");
432
433 if (!xs_is_null(id)) {
434 snac_debug(user, 0, xs_fmt("embedded reply %s", c_id));
435 377
436 object_add(c_id, v); 378 /* store */
379 timeline_add(snac, nid, object);
437 380
438 /* get its own children */ 381 /* recurse! */
439 timeline_request_replies(user, v); 382 timeline_request(snac, &in_reply_to, NULL, level + 1);
440 }
441 }
442 else {
443 snac_debug(user, 0, xs_fmt("request reply %s", v));
444 timeline_request(user, &v, NULL, 0);
445 }
446 } 383 }
447 } 384 }
448 } 385 }
449 else
450 snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status));
451 } 386 }
452 } 387 }
453 } 388 }
389
390 return status;
454} 391}
455 392
456 393
@@ -476,7 +413,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
476 xs_val **payload, int *p_size, int timeout) 413 xs_val **payload, int *p_size, int timeout)
477/* sends a message to an Inbox */ 414/* sends a message to an Inbox */
478{ 415{
479 char *seckey = xs_dict_get(snac->key, "secret"); 416 const char *seckey = xs_dict_get(snac->key, "secret");
480 417
481 return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout); 418 return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout);
482} 419}
@@ -486,7 +423,7 @@ xs_str *get_actor_inbox(const char *actor)
486/* gets an actor's inbox */ 423/* gets an actor's inbox */
487{ 424{
488 xs *data = NULL; 425 xs *data = NULL;
489 char *v = NULL; 426 const char *v = NULL;
490 427
491 if (valid_status(actor_request(NULL, actor, &data))) { 428 if (valid_status(actor_request(NULL, actor, &data))) {
492 /* try first endpoints/sharedInbox */ 429 /* try first endpoints/sharedInbox */
@@ -535,17 +472,17 @@ void post_message(snac *snac, const char *actor, const xs_dict *msg)
535xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public) 472xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
536/* returns the list of recipients for a message */ 473/* returns the list of recipients for a message */
537{ 474{
538 char *to = xs_dict_get(msg, "to"); 475 const xs_val *to = xs_dict_get(msg, "to");
539 char *cc = xs_dict_get(msg, "cc"); 476 const xs_val *cc = xs_dict_get(msg, "cc");
540 xs_set rcpts; 477 xs_set rcpts;
541 int n; 478 int n;
542 479
543 xs_set_init(&rcpts); 480 xs_set_init(&rcpts);
544 481
545 char *lists[] = { to, cc, NULL }; 482 const xs_list *lists[] = { to, cc, NULL };
546 for (n = 0; lists[n]; n++) { 483 for (n = 0; lists[n]; n++) {
547 char *l = lists[n]; 484 xs_list *l = (xs_list *)lists[n];
548 char *v; 485 const char *v;
549 xs *tl = NULL; 486 xs *tl = NULL;
550 487
551 /* if it's a string, create a list with only one element */ 488 /* 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)
560 if (expand_public && strcmp(v, public_address) == 0) { 497 if (expand_public && strcmp(v, public_address) == 0) {
561 /* iterate the followers and add them */ 498 /* iterate the followers and add them */
562 xs *fwers = follower_list(snac); 499 xs *fwers = follower_list(snac);
563 char *actor; 500 const char *actor;
564 501
565 char *p = fwers; 502 char *p = fwers;
566 while (xs_list_iter(&p, &actor)) 503 while (xs_list_iter(&p, &actor))
@@ -667,13 +604,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
667 604
668 /* if it's a Follow, it must be explicitly for us */ 605 /* if it's a Follow, it must be explicitly for us */
669 if (xs_match(type, "Follow")) { 606 if (xs_match(type, "Follow")) {
670 char *object = xs_dict_get(c_msg, "object"); 607 const char *object = xs_dict_get(c_msg, "object");
671 return !xs_is_null(object) && strcmp(snac->actor, object) == 0; 608 return !xs_is_null(object) && strcmp(snac->actor, object) == 0;
672 } 609 }
673 610
674 /* only accept Ping directed to us */ 611 /* only accept Ping directed to us */
675 if (xs_match(type, "Ping")) { 612 if (xs_match(type, "Ping")) {
676 char *dest = xs_dict_get(c_msg, "to"); 613 const char *dest = xs_dict_get(c_msg, "to");
677 return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0; 614 return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0;
678 } 615 }
679 616
@@ -688,10 +625,10 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
688 if (pub_msg && following_check(snac, actor)) 625 if (pub_msg && following_check(snac, actor))
689 return 1; 626 return 1;
690 627
691 xs_dict *msg = xs_dict_get(c_msg, "object"); 628 const xs_dict *msg = xs_dict_get(c_msg, "object");
692 xs *rcpts = recipient_list(snac, msg, 0); 629 xs *rcpts = recipient_list(snac, msg, 0);
693 xs_list *p = rcpts; 630 xs_list *p = rcpts;
694 xs_str *v; 631 const xs_str *v;
695 632
696 xs *actor_followers = NULL; 633 xs *actor_followers = NULL;
697 634
@@ -700,8 +637,9 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
700 xs *actor_obj = NULL; 637 xs *actor_obj = NULL;
701 638
702 if (valid_status(object_get(actor, &actor_obj))) { 639 if (valid_status(object_get(actor, &actor_obj))) {
703 if ((v = xs_dict_get(actor_obj, "followers"))) 640 const xs_val *fw = xs_dict_get(actor_obj, "followers");
704 actor_followers = xs_dup(v); 641 if (fw)
642 actor_followers = xs_dup(fw);
705 } 643 }
706 } 644 }
707 645
@@ -724,13 +662,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
724 } 662 }
725 663
726 /* accept if it's by someone we follow */ 664 /* accept if it's by someone we follow */
727 char *atto = get_atto(msg); 665 const char *atto = get_atto(msg);
728 666
729 if (pub_msg && !xs_is_null(atto) && following_check(snac, atto)) 667 if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
730 return 3; 668 return 3;
731 669
732 /* is this message a reply to another? */ 670 /* is this message a reply to another? */
733 char *irt = xs_dict_get(msg, "inReplyTo"); 671 const char *irt = xs_dict_get(msg, "inReplyTo");
734 if (!xs_is_null(irt)) { 672 if (!xs_is_null(irt)) {
735 xs *r_msg = NULL; 673 xs *r_msg = NULL;
736 674
@@ -755,7 +693,7 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag)
755 xs_list *tl = *tag; 693 xs_list *tl = *tag;
756 xs *split; 694 xs *split;
757 xs_list *p; 695 xs_list *p;
758 xs_val *v; 696 const xs_val *v;
759 int n = 0; 697 int n = 0;
760 698
761 /* create a default server for incomplete mentions */ 699 /* create a default server for incomplete mentions */
@@ -983,8 +921,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
983 921
984 /* telegram */ 922 /* telegram */
985 923
986 char *bot = xs_dict_get(snac->config, "telegram_bot"); 924 const char *bot = xs_dict_get(snac->config, "telegram_bot");
987 char *chat_id = xs_dict_get(snac->config, "telegram_chat_id"); 925 const char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
988 926
989 if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id) 927 if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id)
990 enqueue_telegram(body, bot, chat_id); 928 enqueue_telegram(body, bot, chat_id);
@@ -997,8 +935,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
997 objid = actor; 935 objid = actor;
998 936
999 /* ntfy */ 937 /* ntfy */
1000 char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); 938 const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
1001 char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); 939 const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
1002 940
1003 if (!xs_is_null(ntfy_server) && *ntfy_server) 941 if (!xs_is_null(ntfy_server) && *ntfy_server)
1004 enqueue_ntfy(body, ntfy_server, ntfy_token); 942 enqueue_ntfy(body, ntfy_server, ntfy_token);
@@ -1084,7 +1022,7 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id,
1084} 1022}
1085 1023
1086 1024
1087xs_dict *msg_collection(snac *snac, char *id) 1025xs_dict *msg_collection(snac *snac, const char *id)
1088/* creates an empty OrderedCollection message */ 1026/* creates an empty OrderedCollection message */
1089{ 1027{
1090 xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL); 1028 xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL);
@@ -1098,7 +1036,7 @@ xs_dict *msg_collection(snac *snac, char *id)
1098} 1036}
1099 1037
1100 1038
1101xs_dict *msg_accept(snac *snac, char *object, char *to) 1039xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to)
1102/* creates an Accept message (as a response to a Follow) */ 1040/* creates an Accept message (as a response to a Follow) */
1103{ 1041{
1104 xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object); 1042 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)
1109} 1047}
1110 1048
1111 1049
1112xs_dict *msg_update(snac *snac, xs_dict *object) 1050xs_dict *msg_update(snac *snac, const xs_dict *object)
1113/* creates an Update message */ 1051/* creates an Update message */
1114{ 1052{
1115 xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object); 1053 xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object);
1116 1054
1117 char *type = xs_dict_get(object, "type"); 1055 const char *type = xs_dict_get(object, "type");
1118 1056
1119 if (strcmp(type, "Note") == 0) { 1057 if (strcmp(type, "Note") == 0) {
1120 msg = xs_dict_append(msg, "to", xs_dict_get(object, "to")); 1058 msg = xs_dict_append(msg, "to", xs_dict_get(object, "to"));
@@ -1137,7 +1075,7 @@ xs_dict *msg_update(snac *snac, xs_dict *object)
1137} 1075}
1138 1076
1139 1077
1140xs_dict *msg_admiration(snac *snac, char *object, char *type) 1078xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
1141/* creates a Like or Announce message */ 1079/* creates a Like or Announce message */
1142{ 1080{
1143 xs *a_msg = NULL; 1081 xs *a_msg = NULL;
@@ -1168,7 +1106,7 @@ xs_dict *msg_admiration(snac *snac, char *object, char *type)
1168} 1106}
1169 1107
1170 1108
1171xs_dict *msg_repulsion(snac *user, char *id, char *type) 1109xs_dict *msg_repulsion(snac *user, const char *id, const char *type)
1172/* creates an Undo + admiration message */ 1110/* creates an Undo + admiration message */
1173{ 1111{
1174 xs *a_msg = NULL; 1112 xs *a_msg = NULL;
@@ -1206,7 +1144,7 @@ xs_dict *msg_actor(snac *snac)
1206 xs *kid = NULL; 1144 xs *kid = NULL;
1207 xs *f_bio = NULL; 1145 xs *f_bio = NULL;
1208 xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL); 1146 xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL);
1209 char *p; 1147 const char *p;
1210 int n; 1148 int n;
1211 1149
1212 /* change the @context (is this really necessary?) */ 1150 /* change the @context (is this really necessary?) */
@@ -1264,11 +1202,11 @@ xs_dict *msg_actor(snac *snac)
1264 } 1202 }
1265 1203
1266 /* add the metadata as attachments of PropertyValue */ 1204 /* add the metadata as attachments of PropertyValue */
1267 xs_dict *metadata = xs_dict_get(snac->config, "metadata"); 1205 const xs_dict *metadata = xs_dict_get(snac->config, "metadata");
1268 if (xs_type(metadata) == XSTYPE_DICT) { 1206 if (xs_type(metadata) == XSTYPE_DICT) {
1269 xs *attach = xs_list_new(); 1207 xs *attach = xs_list_new();
1270 xs_str *k; 1208 const xs_str *k;
1271 xs_str *v; 1209 const xs_str *v;
1272 1210
1273 int c = 0; 1211 int c = 0;
1274 while (xs_dict_next(metadata, &k, &v, &c)) { 1212 while (xs_dict_next(metadata, &k, &v, &c)) {
@@ -1277,7 +1215,7 @@ xs_dict *msg_actor(snac *snac)
1277 xs *k2 = encode_html(k); 1215 xs *k2 = encode_html(k);
1278 xs *v2 = NULL; 1216 xs *v2 = NULL;
1279 1217
1280 if (xs_startswith(v, "https:")) { 1218 if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) {
1281 xs *t = encode_html(v); 1219 xs *t = encode_html(v);
1282 v2 = xs_fmt("<a href=\"%s\" rel=\"me\">%s</a>", t, t); 1220 v2 = xs_fmt("<a href=\"%s\" rel=\"me\">%s</a>", t, t);
1283 } 1221 }
@@ -1310,7 +1248,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
1310/* creates a 'Create' message */ 1248/* creates a 'Create' message */
1311{ 1249{
1312 xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object); 1250 xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object);
1313 xs_val *v; 1251 const xs_val *v;
1314 1252
1315 if ((v = get_atto(object))) 1253 if ((v = get_atto(object)))
1316 msg = xs_dict_append(msg, "attributedTo", v); 1254 msg = xs_dict_append(msg, "attributedTo", v);
@@ -1327,7 +1265,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
1327} 1265}
1328 1266
1329 1267
1330xs_dict *msg_undo(snac *snac, char *object) 1268xs_dict *msg_undo(snac *snac, const xs_val *object)
1331/* creates an 'Undo' message */ 1269/* creates an 'Undo' message */
1332{ 1270{
1333 xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object); 1271 xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object);
@@ -1340,7 +1278,7 @@ xs_dict *msg_undo(snac *snac, char *object)
1340} 1278}
1341 1279
1342 1280
1343xs_dict *msg_delete(snac *snac, char *id) 1281xs_dict *msg_delete(snac *snac, const char *id)
1344/* creates a 'Delete' + 'Tombstone' for a local entry */ 1282/* creates a 'Delete' + 'Tombstone' for a local entry */
1345{ 1283{
1346 xs *tomb = xs_dict_new(); 1284 xs *tomb = xs_dict_new();
@@ -1369,7 +1307,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
1369 1307
1370 xs *url_or_uid = xs_strip_i(xs_str_new(q)); 1308 xs *url_or_uid = xs_strip_i(xs_str_new(q));
1371 1309
1372 if (xs_startswith(url_or_uid, "https:/")) 1310 if (xs_startswith(url_or_uid, "https:/") || xs_startswith(url_or_uid, "http:/"))
1373 actor = xs_dup(url_or_uid); 1311 actor = xs_dup(url_or_uid);
1374 else 1312 else
1375 if (!valid_status(webfinger_request(url_or_uid, &actor, NULL)) || actor == NULL) { 1313 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)
1382 1320
1383 if (valid_status(status)) { 1321 if (valid_status(status)) {
1384 /* check if the actor is an alias */ 1322 /* check if the actor is an alias */
1385 char *r_actor = xs_dict_get(actor_o, "id"); 1323 const char *r_actor = xs_dict_get(actor_o, "id");
1386 1324
1387 if (r_actor && strcmp(actor, r_actor) != 0) { 1325 if (r_actor && strcmp(actor, r_actor) != 0) {
1388 snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor)); 1326 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)
1398 1336
1399 1337
1400xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, 1338xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1401 xs_str *in_reply_to, xs_list *attach, int priv) 1339 const xs_str *in_reply_to, const xs_list *attach, int priv)
1402/* creates a 'Note' message */ 1340/* creates a 'Note' message */
1403{ 1341{
1404 xs *ntid = tid(0); 1342 xs *ntid = tid(0);
@@ -1413,7 +1351,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1413 xs *atls = xs_list_new(); 1351 xs *atls = xs_list_new();
1414 xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL); 1352 xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL);
1415 xs_list *p; 1353 xs_list *p;
1416 xs_val *v; 1354 const xs_val *v;
1417 1355
1418 if (rcpts == NULL) 1356 if (rcpts == NULL)
1419 to = xs_list_new(); 1357 to = xs_list_new();
@@ -1438,7 +1376,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1438 1376
1439 if (valid_status(object_get(in_reply_to, &p_msg))) { 1377 if (valid_status(object_get(in_reply_to, &p_msg))) {
1440 /* add this author as recipient */ 1378 /* add this author as recipient */
1441 char *a, *v; 1379 const char *a, *v;
1442 1380
1443 if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1) 1381 if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1)
1444 to = xs_list_append(to, a); 1382 to = xs_list_append(to, a);
@@ -1449,7 +1387,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1449 xs *actor_o = NULL; 1387 xs *actor_o = NULL;
1450 1388
1451 if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) { 1389 if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) {
1452 char *uname = xs_dict_get(actor_o, "preferredUsername"); 1390 const char *uname = xs_dict_get(actor_o, "preferredUsername");
1453 1391
1454 if (!xs_is_null(uname) && *uname) { 1392 if (!xs_is_null(uname) && *uname) {
1455 xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2)); 1393 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,
1488 1426
1489 /* create the attachment list, if there are any */ 1427 /* create the attachment list, if there are any */
1490 if (!xs_is_null(attach)) { 1428 if (!xs_is_null(attach)) {
1491 while (xs_list_iter(&attach, &v)) { 1429 int c = 0;
1430 while (xs_list_next(attach, &v, &c)) {
1492 xs *d = xs_dict_new(); 1431 xs *d = xs_dict_new();
1493 const char *url = xs_list_get(v, 0); 1432 const char *url = xs_list_get(v, 0);
1494 const char *alt = xs_list_get(v, 1); 1433 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,
1511 p = tag; 1450 p = tag;
1512 while (xs_list_iter(&p, &v)) { 1451 while (xs_list_iter(&p, &v)) {
1513 if (xs_type(v) == XSTYPE_DICT) { 1452 if (xs_type(v) == XSTYPE_DICT) {
1514 char *t; 1453 const char *t;
1515 1454
1516 if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) { 1455 if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) {
1517 if (!xs_is_null(t = xs_dict_get(v, "href"))) 1456 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,
1589 1528
1590 xs *o = xs_list_new(); 1529 xs *o = xs_list_new();
1591 xs_list *p = (xs_list *)opts; 1530 xs_list *p = (xs_list *)opts;
1592 xs_str *v; 1531 const xs_str *v;
1593 xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}"); 1532 xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}");
1594 1533
1595 xs_set_init(&seen); 1534 xs_set_init(&seen);
@@ -1635,9 +1574,9 @@ int update_question(snac *user, const char *id)
1635 xs *msg = NULL; 1574 xs *msg = NULL;
1636 xs *rcnt = xs_dict_new(); 1575 xs *rcnt = xs_dict_new();
1637 xs *lopts = xs_list_new(); 1576 xs *lopts = xs_list_new();
1638 xs_list *opts; 1577 const xs_list *opts;
1639 xs_list *p; 1578 xs_list *p;
1640 xs_val *v; 1579 const xs_val *v;
1641 1580
1642 /* get the object */ 1581 /* get the object */
1643 if (!valid_status(object_get(id, &msg))) 1582 if (!valid_status(object_get(id, &msg)))
@@ -1653,8 +1592,8 @@ int update_question(snac *user, const char *id)
1653 return -3; 1592 return -3;
1654 1593
1655 /* fill the initial count */ 1594 /* fill the initial count */
1656 p = opts; 1595 int c = 0;
1657 while (xs_list_iter(&p, &v)) { 1596 while (xs_list_next(opts, &v, &c)) {
1658 const char *name = xs_dict_get(v, "name"); 1597 const char *name = xs_dict_get(v, "name");
1659 if (name) { 1598 if (name) {
1660 lopts = xs_list_append(lopts, name); 1599 lopts = xs_list_append(lopts, name);
@@ -1760,13 +1699,13 @@ int update_question(snac *user, const char *id)
1760 1699
1761/** queues **/ 1700/** queues **/
1762 1701
1763int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) 1702int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
1764/* processes an ActivityPub message from the input queue */ 1703/* processes an ActivityPub message from the input queue */
1765/* return values: -1, fatal error; 0, transient error, retry; 1704/* return values: -1, fatal error; 0, transient error, retry;
1766 1, processed and done; 2, propagate to users (only when no user is set) */ 1705 1, processed and done; 2, propagate to users (only when no user is set) */
1767{ 1706{
1768 char *actor = xs_dict_get(msg, "actor"); 1707 const char *actor = xs_dict_get(msg, "actor");
1769 char *type = xs_dict_get(msg, "type"); 1708 const char *type = xs_dict_get(msg, "type");
1770 xs *actor_o = NULL; 1709 xs *actor_o = NULL;
1771 int a_status; 1710 int a_status;
1772 int do_notify = 0; 1711 int do_notify = 0;
@@ -1786,7 +1725,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1786 return -1; 1725 return -1;
1787 } 1726 }
1788 1727
1789 char *object, *utype; 1728 const char *object, *utype;
1790 1729
1791 object = xs_dict_get(msg, "object"); 1730 object = xs_dict_get(msg, "object");
1792 if (object != NULL && xs_type(object) == XSTYPE_DICT) 1731 if (object != NULL && xs_type(object) == XSTYPE_DICT)
@@ -1809,7 +1748,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1809 } 1748 }
1810 1749
1811 /* also discard if the object to be deleted is not here */ 1750 /* also discard if the object to be deleted is not here */
1812 char *obj_id = object; 1751 const char *obj_id = object;
1813 if (xs_type(obj_id) == XSTYPE_DICT) 1752 if (xs_type(obj_id) == XSTYPE_DICT)
1814 obj_id = xs_dict_get(obj_id, "id"); 1753 obj_id = xs_dict_get(obj_id, "id");
1815 1754
@@ -1881,7 +1820,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1881 int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age")); 1820 int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age"));
1882 1821
1883 if (min_account_age > 0) { 1822 if (min_account_age > 0) {
1884 char *actor_date = xs_dict_get(actor_o, "published"); 1823 const char *actor_date = xs_dict_get(actor_o, "published");
1885 if (!xs_is_null(actor_date)) { 1824 if (!xs_is_null(actor_date)) {
1886 time_t actor_t = xs_parse_iso_date(actor_date, 0); 1825 time_t actor_t = xs_parse_iso_date(actor_date, 0);
1887 1826
@@ -1941,16 +1880,39 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1941 } 1880 }
1942 else 1881 else
1943 if (strcmp(type, "Undo") == 0) { /** **/ 1882 if (strcmp(type, "Undo") == 0) { /** **/
1883 const char *id = xs_dict_get(object, "object");
1884
1944 if (xs_type(object) != XSTYPE_DICT) 1885 if (xs_type(object) != XSTYPE_DICT)
1945 utype = "Follow"; 1886 utype = "Follow";
1946 1887
1947 if (strcmp(utype, "Follow") == 0) { /** **/ 1888 if (strcmp(utype, "Follow") == 0) { /** **/
1948 if (valid_status(follower_del(snac, actor))) { 1889 if (id && strcmp(id, snac->actor) != 0)
1949 snac_log(snac, xs_fmt("no longer following us %s", actor)); 1890 snac_debug(snac, 1, xs_fmt("Undo + Follow from %s not for us (%s)", actor, id));
1950 do_notify = 1; 1891 else {
1892 if (valid_status(follower_del(snac, actor))) {
1893 snac_log(snac, xs_fmt("no longer following us %s", actor));
1894 do_notify = 1;
1895 }
1896 else
1897 snac_log(snac, xs_fmt("error deleting follower %s", actor));
1951 } 1898 }
1952 else 1899 }
1953 snac_log(snac, xs_fmt("error deleting follower %s", actor)); 1900 else
1901 if (strcmp(utype, "Like") == 0) { /** **/
1902 int status = object_unadmire(id, actor, 1);
1903
1904 snac_log(snac, xs_fmt("Unlike for %s %d", id, status));
1905 }
1906 else
1907 if (strcmp(utype, "Announce") == 0) { /** **/
1908 int status = 200;
1909
1910 /* commented out: if a followed user boosts something that
1911 is requested and then unboosts, the post remains here,
1912 but with no apparent reason, and that is confusing */
1913 //status = object_unadmire(id, actor, 0);
1914
1915 snac_log(snac, xs_fmt("Unboost for %s %d", id, status));
1954 } 1916 }
1955 else 1917 else
1956 snac_debug(snac, 1, xs_fmt("ignored 'Undo' for object type '%s'", utype)); 1918 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)
1963 } 1925 }
1964 1926
1965 if (xs_match(utype, "Note|Article")) { /** **/ 1927 if (xs_match(utype, "Note|Article")) { /** **/
1966 char *id = xs_dict_get(object, "id"); 1928 const char *id = xs_dict_get(object, "id");
1967 char *in_reply_to = xs_dict_get(object, "inReplyTo"); 1929 const char *in_reply_to = xs_dict_get(object, "inReplyTo");
1930 const char *atto = get_atto(object);
1968 xs *wrk = NULL; 1931 xs *wrk = NULL;
1969 1932
1933 if (xs_is_null(id))
1934 snac_log(snac, xs_fmt("malformed message: no 'id' field"));
1935 else
1936 if (xs_is_null(atto))
1937 snac_log(snac, xs_fmt("malformed message: no 'attributedTo' field"));
1938 else
1970 if (!xs_is_null(in_reply_to) && is_hidden(snac, in_reply_to)) { 1939 if (!xs_is_null(in_reply_to) && is_hidden(snac, in_reply_to)) {
1971 snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to)); 1940 snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to));
1972 } 1941 }
1973 else { 1942 else {
1974 if (content_check("filter_reject.txt", object)) { 1943 if (content_match("filter_reject.txt", object)) {
1975 snac_log(snac, xs_fmt("rejected by content %s", id)); 1944 snac_log(snac, xs_fmt("rejected by content %s", id));
1976 return 1; 1945 return 1;
1977 } 1946 }
1978 1947
1948 if (strcmp(actor, atto) != 0)
1949 snac_log(snac, xs_fmt("SUSPICIOUS: actor != atto (%s != %s)", actor, atto));
1950
1979 timeline_request(snac, &in_reply_to, &wrk, 0); 1951 timeline_request(snac, &in_reply_to, &wrk, 0);
1980 1952
1981 if (timeline_add(snac, id, object)) { 1953 if (timeline_add(snac, id, object)) {
@@ -1992,14 +1964,14 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1992 } 1964 }
1993 else 1965 else
1994 if (strcmp(utype, "Question") == 0) { /** **/ 1966 if (strcmp(utype, "Question") == 0) { /** **/
1995 char *id = xs_dict_get(object, "id"); 1967 const char *id = xs_dict_get(object, "id");
1996 1968
1997 if (timeline_add(snac, id, object)) 1969 if (timeline_add(snac, id, object))
1998 snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id)); 1970 snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id));
1999 } 1971 }
2000 else 1972 else
2001 if (strcmp(utype, "Video") == 0) { /** **/ 1973 if (strcmp(utype, "Video") == 0) { /** **/
2002 char *id = xs_dict_get(object, "id"); 1974 const char *id = xs_dict_get(object, "id");
2003 1975
2004 if (timeline_add(snac, id, object)) 1976 if (timeline_add(snac, id, object))
2005 snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id)); 1977 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)
2080 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", 2052 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",
2081 actor, object)); 2053 actor, object));
2082 2054
2055 /* distribute the post with the actor as 'proxy' */
2056 list_distribute(snac, actor, a_msg);
2057
2083 do_notify = 1; 2058 do_notify = 1;
2084 } 2059 }
2085 else 2060 else
@@ -2172,7 +2147,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
2172} 2147}
2173 2148
2174 2149
2175int send_email(char *msg) 2150int send_email(const char *msg)
2176/* invoke sendmail with email headers and body in msg */ 2151/* invoke sendmail with email headers and body in msg */
2177{ 2152{
2178 FILE *f; 2153 FILE *f;
@@ -2204,18 +2179,18 @@ int send_email(char *msg)
2204void process_user_queue_item(snac *snac, xs_dict *q_item) 2179void process_user_queue_item(snac *snac, xs_dict *q_item)
2205/* processes an item from the user queue */ 2180/* processes an item from the user queue */
2206{ 2181{
2207 char *type; 2182 const char *type;
2208 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); 2183 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
2209 2184
2210 if ((type = xs_dict_get(q_item, "type")) == NULL) 2185 if ((type = xs_dict_get(q_item, "type")) == NULL)
2211 type = "output"; 2186 type = "output";
2212 2187
2213 if (strcmp(type, "message") == 0) { 2188 if (strcmp(type, "message") == 0) {
2214 xs_dict *msg = xs_dict_get(q_item, "message"); 2189 const xs_dict *msg = xs_dict_get(q_item, "message");
2215 xs *rcpts = recipient_list(snac, msg, 1); 2190 xs *rcpts = recipient_list(snac, msg, 1);
2216 xs_set inboxes; 2191 xs_set inboxes;
2217 xs_list *p; 2192 xs_list *p;
2218 xs_str *actor; 2193 const xs_str *actor;
2219 2194
2220 xs_set_init(&inboxes); 2195 xs_set_init(&inboxes);
2221 2196
@@ -2237,7 +2212,7 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
2237 if (is_msg_public(msg)) { 2212 if (is_msg_public(msg)) {
2238 if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) { 2213 if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) {
2239 xs *shibx = inbox_list(); 2214 xs *shibx = inbox_list();
2240 xs_str *inbox; 2215 const xs_str *inbox;
2241 2216
2242 p = shibx; 2217 p = shibx;
2243 while (xs_list_iter(&p, &inbox)) { 2218 while (xs_list_iter(&p, &inbox)) {
@@ -2252,8 +2227,8 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
2252 else 2227 else
2253 if (strcmp(type, "input") == 0) { 2228 if (strcmp(type, "input") == 0) {
2254 /* process the message */ 2229 /* process the message */
2255 xs_dict *msg = xs_dict_get(q_item, "message"); 2230 const xs_dict *msg = xs_dict_get(q_item, "message");
2256 xs_dict *req = xs_dict_get(q_item, "req"); 2231 const xs_dict *req = xs_dict_get(q_item, "req");
2257 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2232 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2258 2233
2259 if (xs_is_null(msg)) 2234 if (xs_is_null(msg))
@@ -2280,11 +2255,20 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
2280 update_question(snac, id); 2255 update_question(snac, id);
2281 } 2256 }
2282 else 2257 else
2283 if (strcmp(type, "request_replies") == 0) { 2258 if (strcmp(type, "object_request") == 0) {
2284 const char *id = xs_dict_get(q_item, "message"); 2259 const char *id = xs_dict_get(q_item, "message");
2285 2260
2286 if (!xs_is_null(id)) 2261 if (!xs_is_null(id)) {
2287 timeline_request_replies(snac, id); 2262 int status;
2263 xs *data = NULL;
2264
2265 status = activitypub_request(snac, id, &data);
2266
2267 if (valid_status(status))
2268 object_add_ow(id, data);
2269
2270 snac_debug(snac, 1, xs_fmt("object_request %s %d", id, status));
2271 }
2288 } 2272 }
2289 else 2273 else
2290 if (strcmp(type, "verify_links") == 0) { 2274 if (strcmp(type, "verify_links") == 0) {
@@ -2320,7 +2304,7 @@ int process_user_queue(snac *snac)
2320 xs *list = user_queue(snac); 2304 xs *list = user_queue(snac);
2321 2305
2322 xs_list *p = list; 2306 xs_list *p = list;
2323 xs_str *fn; 2307 const xs_str *fn;
2324 2308
2325 while (xs_list_iter(&p, &fn)) { 2309 while (xs_list_iter(&p, &fn)) {
2326 xs *q_item = dequeue(fn); 2310 xs *q_item = dequeue(fn);
@@ -2339,19 +2323,20 @@ int process_user_queue(snac *snac)
2339void process_queue_item(xs_dict *q_item) 2323void process_queue_item(xs_dict *q_item)
2340/* processes an item from the global queue */ 2324/* processes an item from the global queue */
2341{ 2325{
2342 char *type = xs_dict_get(q_item, "type"); 2326 const char *type = xs_dict_get(q_item, "type");
2343 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); 2327 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
2344 2328
2345 if (strcmp(type, "output") == 0) { 2329 if (strcmp(type, "output") == 0) {
2346 int status; 2330 int status;
2347 xs_str *inbox = xs_dict_get(q_item, "inbox"); 2331 const xs_str *inbox = xs_dict_get(q_item, "inbox");
2348 xs_str *keyid = xs_dict_get(q_item, "keyid"); 2332 const xs_str *keyid = xs_dict_get(q_item, "keyid");
2349 xs_str *seckey = xs_dict_get(q_item, "seckey"); 2333 const xs_str *seckey = xs_dict_get(q_item, "seckey");
2350 xs_dict *msg = xs_dict_get(q_item, "message"); 2334 const xs_dict *msg = xs_dict_get(q_item, "message");
2351 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2335 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2352 int p_status = xs_number_get(xs_dict_get(q_item, "p_status")); 2336 int p_status = xs_number_get(xs_dict_get(q_item, "p_status"));
2353 xs *payload = NULL; 2337 xs *payload = NULL;
2354 int p_size = 0; 2338 int p_size = 0;
2339 int timeout = 0;
2355 2340
2356 if (xs_is_null(inbox) || xs_is_null(msg) || xs_is_null(keyid) || xs_is_null(seckey)) { 2341 if (xs_is_null(inbox) || xs_is_null(msg) || xs_is_null(keyid) || xs_is_null(seckey)) {
2357 srv_log(xs_fmt("output message error: missing fields")); 2342 srv_log(xs_fmt("output message error: missing fields"));
@@ -2364,8 +2349,15 @@ void process_queue_item(xs_dict *q_item)
2364 } 2349 }
2365 2350
2366 /* deliver (if previous error status was a timeout, try now longer) */ 2351 /* deliver (if previous error status was a timeout, try now longer) */
2367 status = send_to_inbox_raw(keyid, seckey, inbox, msg, 2352 if (p_status == 599)
2368 &payload, &p_size, p_status == 599 ? 8 : 6); 2353 timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout_2", "8"));
2354 else
2355 timeout = xs_number_get(xs_dict_get_def(srv_config, "queue_timeout", "6"));
2356
2357 if (timeout == 0)
2358 timeout = 6;
2359
2360 status = send_to_inbox_raw(keyid, seckey, inbox, msg, &payload, &p_size, timeout);
2369 2361
2370 if (payload) { 2362 if (payload) {
2371 if (p_size > 64) { 2363 if (p_size > 64) {
@@ -2411,7 +2403,7 @@ void process_queue_item(xs_dict *q_item)
2411 else 2403 else
2412 if (strcmp(type, "email") == 0) { 2404 if (strcmp(type, "email") == 0) {
2413 /* send this email */ 2405 /* send this email */
2414 xs_str *msg = xs_dict_get(q_item, "message"); 2406 const xs_str *msg = xs_dict_get(q_item, "message");
2415 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2407 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2416 2408
2417 if (!send_email(msg)) 2409 if (!send_email(msg))
@@ -2433,8 +2425,8 @@ void process_queue_item(xs_dict *q_item)
2433 else 2425 else
2434 if (strcmp(type, "telegram") == 0) { 2426 if (strcmp(type, "telegram") == 0) {
2435 /* send this via telegram */ 2427 /* send this via telegram */
2436 char *bot = xs_dict_get(q_item, "bot"); 2428 const char *bot = xs_dict_get(q_item, "bot");
2437 char *msg = xs_dict_get(q_item, "message"); 2429 const char *msg = xs_dict_get(q_item, "message");
2438 xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id")); 2430 xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id"));
2439 int status = 0; 2431 int status = 0;
2440 2432
@@ -2457,9 +2449,9 @@ void process_queue_item(xs_dict *q_item)
2457 else 2449 else
2458 if (strcmp(type, "ntfy") == 0) { 2450 if (strcmp(type, "ntfy") == 0) {
2459 /* send this via ntfy */ 2451 /* send this via ntfy */
2460 char *ntfy_server = xs_dict_get(q_item, "ntfy_server"); 2452 const char *ntfy_server = xs_dict_get(q_item, "ntfy_server");
2461 char *msg = xs_dict_get(q_item, "message"); 2453 const char *msg = xs_dict_get(q_item, "message");
2462 char *ntfy_token = xs_dict_get(q_item, "ntfy_token"); 2454 const char *ntfy_token = xs_dict_get(q_item, "ntfy_token");
2463 int status = 0; 2455 int status = 0;
2464 2456
2465 xs *url = xs_fmt("%s", ntfy_server); 2457 xs *url = xs_fmt("%s", ntfy_server);
@@ -2488,8 +2480,8 @@ void process_queue_item(xs_dict *q_item)
2488 } 2480 }
2489 else 2481 else
2490 if (strcmp(type, "input") == 0) { 2482 if (strcmp(type, "input") == 0) {
2491 xs_dict *msg = xs_dict_get(q_item, "message"); 2483 const xs_dict *msg = xs_dict_get(q_item, "message");
2492 xs_dict *req = xs_dict_get(q_item, "req"); 2484 const xs_dict *req = xs_dict_get(q_item, "req");
2493 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2485 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2494 2486
2495 /* do some instance-level checks */ 2487 /* do some instance-level checks */
@@ -2497,8 +2489,6 @@ void process_queue_item(xs_dict *q_item)
2497 2489
2498 if (r == 0) { 2490 if (r == 0) {
2499 /* transient error? retry */ 2491 /* transient error? retry */
2500 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
2501
2502 if (retries > queue_retry_max) 2492 if (retries > queue_retry_max)
2503 srv_log(xs_fmt("shared input giving up")); 2493 srv_log(xs_fmt("shared input giving up"));
2504 else { 2494 else {
@@ -2510,7 +2500,7 @@ void process_queue_item(xs_dict *q_item)
2510 else 2500 else
2511 if (r == 2) { 2501 if (r == 2) {
2512 /* redistribute the input message to all users */ 2502 /* redistribute the input message to all users */
2513 char *ntid = xs_dict_get(q_item, "ntid"); 2503 const char *ntid = xs_dict_get(q_item, "ntid");
2514 xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid); 2504 xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid);
2515 FILE *f; 2505 FILE *f;
2516 2506
@@ -2521,7 +2511,7 @@ void process_queue_item(xs_dict *q_item)
2521 2511
2522 xs *users = user_list(); 2512 xs *users = user_list();
2523 xs_list *p = users; 2513 xs_list *p = users;
2524 char *v; 2514 const char *v;
2525 int cnt = 0; 2515 int cnt = 0;
2526 2516
2527 while (xs_list_iter(&p, &v)) { 2517 while (xs_list_iter(&p, &v)) {
@@ -2564,7 +2554,7 @@ int process_queue(void)
2564 xs *list = queue(); 2554 xs *list = queue();
2565 2555
2566 xs_list *p = list; 2556 xs_list *p = list;
2567 xs_str *fn; 2557 const xs_str *fn;
2568 2558
2569 while (xs_list_iter(&p, &fn)) { 2559 while (xs_list_iter(&p, &fn)) {
2570 xs *q_item = dequeue(fn); 2560 xs *q_item = dequeue(fn);
@@ -2585,7 +2575,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2585 char **body, int *b_size, char **ctype) 2575 char **body, int *b_size, char **ctype)
2586{ 2576{
2587 int status = 200; 2577 int status = 200;
2588 char *accept = xs_dict_get(req, "accept"); 2578 const char *accept = xs_dict_get(req, "accept");
2589 snac snac; 2579 snac snac;
2590 xs *msg = NULL; 2580 xs *msg = NULL;
2591 2581
@@ -2597,7 +2587,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2597 return 0; 2587 return 0;
2598 2588
2599 xs *l = xs_split_n(q_path, "/", 2); 2589 xs *l = xs_split_n(q_path, "/", 2);
2600 char *uid, *p_path; 2590 const char *uid;
2591 const char *p_path;
2601 2592
2602 uid = xs_list_get(l, 1); 2593 uid = xs_list_get(l, 1);
2603 if (!user_open(&snac, uid)) { 2594 if (!user_open(&snac, uid)) {
@@ -2615,7 +2606,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2615 msg = msg_actor(&snac); 2606 msg = msg_actor(&snac);
2616 *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; 2607 *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
2617 2608
2618 char *ua = xs_dict_get(req, "user-agent"); 2609 const char *ua = xs_dict_get(req, "user-agent");
2619 2610
2620 snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA")); 2611 snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA"));
2621 } 2612 }
@@ -2625,15 +2616,16 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2625 xs *elems = timeline_simple_list(&snac, "public", 0, 20); 2616 xs *elems = timeline_simple_list(&snac, "public", 0, 20);
2626 xs *list = xs_list_new(); 2617 xs *list = xs_list_new();
2627 msg = msg_collection(&snac, id); 2618 msg = msg_collection(&snac, id);
2628 char *p, *v; 2619 char *p;
2620 const char *v;
2629 2621
2630 p = elems; 2622 p = elems;
2631 while (xs_list_iter(&p, &v)) { 2623 while (xs_list_iter(&p, &v)) {
2632 xs *i = NULL; 2624 xs *i = NULL;
2633 2625
2634 if (valid_status(object_get_by_md5(v, &i))) { 2626 if (valid_status(object_get_by_md5(v, &i))) {
2635 char *type = xs_dict_get(i, "type"); 2627 const char *type = xs_dict_get(i, "type");
2636 char *id = xs_dict_get(i, "id"); 2628 const char *id = xs_dict_get(i, "id");
2637 2629
2638 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) { 2630 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) {
2639 xs *c_msg = msg_create(&snac, i); 2631 xs *c_msg = msg_create(&snac, i);
@@ -2686,9 +2678,9 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
2686 (void)b_size; 2678 (void)b_size;
2687 2679
2688 int status = 202; /* accepted */ 2680 int status = 202; /* accepted */
2689 char *i_ctype = xs_dict_get(req, "content-type"); 2681 const char *i_ctype = xs_dict_get(req, "content-type");
2690 snac snac; 2682 snac snac;
2691 char *v; 2683 const char *v;
2692 2684
2693 if (i_ctype == NULL) { 2685 if (i_ctype == NULL) {
2694 *body = xs_str_new("no content-type"); 2686 *body = xs_str_new("no content-type");