summaryrefslogtreecommitdiff
path: root/activitypub.c
diff options
context:
space:
mode:
Diffstat (limited to 'activitypub.c')
-rw-r--r--activitypub.c220
1 files changed, 126 insertions, 94 deletions
diff --git a/activitypub.c b/activitypub.c
index a209abd..aa679a0 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -329,6 +329,52 @@ xs_list *get_attachments(const xs_dict *msg)
329} 329}
330 330
331 331
332int hashtag_in_msg(const xs_list *hashtags, const xs_dict *msg)
333/* returns 1 if the message contains any of the list of hashtags */
334{
335 if (xs_is_list(hashtags) && xs_is_dict(msg)) {
336 const xs_list *tags_in_msg = xs_dict_get(msg, "tag");
337
338 if (xs_is_list(tags_in_msg)) {
339 const xs_dict *te;
340
341 /* iterate the tags in the message */
342 xs_list_foreach(tags_in_msg, te) {
343 if (xs_is_dict(te)) {
344 const char *type = xs_dict_get(te, "type");
345 const char *name = xs_dict_get(te, "name");
346
347 if (xs_is_string(type) && xs_is_string(name)) {
348 if (strcmp(type, "Hashtag") == 0) {
349 xs *lc_name = xs_utf8_to_lower(name);
350
351 if (xs_list_in(hashtags, lc_name) != -1)
352 return 1;
353 }
354 }
355 }
356 }
357 }
358 }
359
360 return 0;
361}
362
363
364int followed_hashtag_check(snac *user, const xs_dict *msg)
365/* returns 1 if this message contains a hashtag followed by me */
366{
367 return hashtag_in_msg(xs_dict_get(user->config, "followed_hashtags"), msg);
368}
369
370
371int blocked_hashtag_check(snac *user, const xs_dict *msg)
372/* returns 1 if this message contains a hashtag blocked by me */
373{
374 return hashtag_in_msg(xs_dict_get(user->config, "blocked_hashtags"), msg);
375}
376
377
332int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) 378int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
333/* ensures that an entry and its ancestors are in the timeline */ 379/* ensures that an entry and its ancestors are in the timeline */
334{ 380{
@@ -344,68 +390,71 @@ int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
344 } 390 }
345 391
346 /* is the object already there? */ 392 /* is the object already there? */
347 if (!valid_status(object_get(*id, &msg))) { 393 if (!valid_status((status = object_get(*id, &msg)))) {
348 /* no; download it */ 394 /* no; download it */
349 status = activitypub_request(snac, *id, &msg); 395 status = activitypub_request(snac, *id, &msg);
396 }
350 397
351 if (valid_status(status)) { 398 if (valid_status(status)) {
352 const xs_dict *object = msg; 399 const xs_dict *object = msg;
353 const char *type = xs_dict_get(object, "type"); 400 const char *type = xs_dict_get(object, "type");
354 401
355 /* get the id again from the object, as it may be different */ 402 /* get the id again from the object, as it may be different */
356 const char *nid = xs_dict_get(object, "id"); 403 const char *nid = xs_dict_get(object, "id");
357 404
358 if (xs_type(nid) != XSTYPE_STRING) 405 if (xs_type(nid) != XSTYPE_STRING)
359 return 0; 406 return 0;
360 407
361 if (wrk && strcmp(nid, *id) != 0) { 408 if (wrk && strcmp(nid, *id) != 0) {
362 snac_debug(snac, 1, 409 snac_debug(snac, 1,
363 xs_fmt("timeline_request canonical id for %s is %s", *id, nid)); 410 xs_fmt("timeline_request canonical id for %s is %s", *id, nid));
364 411
365 *wrk = xs_dup(nid); 412 *wrk = xs_dup(nid);
366 *id = *wrk; 413 *id = *wrk;
367 } 414 }
368 415
369 if (xs_is_null(type)) 416 if (xs_is_null(type))
370 type = "(null)"; 417 type = "(null)";
371 418
372 srv_debug(2, xs_fmt("timeline_request type %s '%s'", nid, type)); 419 srv_debug(2, xs_fmt("timeline_request type %s '%s'", nid, type));
373 420
374 if (strcmp(type, "Create") == 0) { 421 if (strcmp(type, "Create") == 0) {
375 /* some software like lemmy nest Announce + Create + Note */ 422 /* some software like lemmy nest Announce + Create + Note */
376 if (!xs_is_null(object = xs_dict_get(object, "object"))) { 423 if (!xs_is_null(object = xs_dict_get(object, "object"))) {
377 type = xs_dict_get(object, "type"); 424 type = xs_dict_get(object, "type");
378 nid = xs_dict_get(object, "id"); 425 nid = xs_dict_get(object, "id");
379 }
380 else
381 type = "(null)";
382 } 426 }
427 else
428 type = "(null)";
429 }
383 430
384 if (xs_match(type, POSTLIKE_OBJECT_TYPE)) { 431 if (xs_match(type, POSTLIKE_OBJECT_TYPE)) {
385 if (content_match("filter_reject.txt", object)) 432 if (content_match("filter_reject.txt", object))
386 snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid)); 433 snac_log(snac, xs_fmt("timeline_request rejected by content %s", nid));
387 else { 434 else
388 const char *actor = get_atto(object); 435 if (blocked_hashtag_check(snac, object))
389 436 snac_log(snac, xs_fmt("timeline_request rejected by hashtag %s", nid));
390 if (!xs_is_null(actor)) { 437 else {
391 /* request (and drop) the actor for this entry */ 438 const char *actor = get_atto(object);
392 if (!valid_status(actor_request(snac, actor, NULL))) {
393 /* failed? retry later */
394 enqueue_actor_refresh(snac, actor, 60);
395 }
396 439
397 /* does it have an ancestor? */ 440 if (!xs_is_null(actor)) {
398 const char *in_reply_to = get_in_reply_to(object); 441 /* request (and drop) the actor for this entry */
442 if (!valid_status(actor_request(snac, actor, NULL))) {
443 /* failed? retry later */
444 enqueue_actor_refresh(snac, actor, 60);
445 }
399 446
400 /* store */ 447 /* does it have an ancestor? */
401 timeline_add(snac, nid, object); 448 const char *in_reply_to = get_in_reply_to(object);
402 449
403 /* redistribute to lists for this user */ 450 /* store */
404 list_distribute(snac, actor, object); 451 timeline_add(snac, nid, object);
405 452
406 /* recurse! */ 453 /* redistribute to lists for this user */
407 timeline_request(snac, &in_reply_to, NULL, level + 1); 454 list_distribute(snac, actor, object);
408 } 455
456 /* recurse! */
457 timeline_request(snac, &in_reply_to, NULL, level + 1);
409 } 458 }
410 } 459 }
411 } 460 }
@@ -587,40 +636,6 @@ int is_msg_from_private_user(const xs_dict *msg)
587} 636}
588 637
589 638
590int followed_hashtag_check(snac *user, const xs_dict *msg)
591/* returns true if this message contains a hashtag followed by me */
592{
593 const xs_list *fw_tags = xs_dict_get(user->config, "followed_hashtags");
594
595 if (xs_is_list(fw_tags)) {
596 const xs_list *tags_in_msg = xs_dict_get(msg, "tag");
597
598 if (xs_is_list(tags_in_msg)) {
599 const xs_dict *te;
600
601 /* iterate the tags in the message */
602 xs_list_foreach(tags_in_msg, te) {
603 if (xs_is_dict(te)) {
604 const char *type = xs_dict_get(te, "type");
605 const char *name = xs_dict_get(te, "name");
606
607 if (xs_is_string(type) && xs_is_string(name)) {
608 if (strcmp(type, "Hashtag") == 0) {
609 xs *lc_name = xs_utf8_to_lower(name);
610
611 if (xs_list_in(fw_tags, lc_name) != -1)
612 return 1;
613 }
614 }
615 }
616 }
617 }
618 }
619
620 return 0;
621}
622
623
624void followed_hashtag_distribute(const xs_dict *msg) 639void followed_hashtag_distribute(const xs_dict *msg)
625/* distribute this post to all users following the included hashtags */ 640/* distribute this post to all users following the included hashtags */
626{ 641{
@@ -665,31 +680,36 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
665 680
666 if (xs_match(type, "Like|Announce|EmojiReact")) { 681 if (xs_match(type, "Like|Announce|EmojiReact")) {
667 const char *object = xs_dict_get(c_msg, "object"); 682 const char *object = xs_dict_get(c_msg, "object");
683 xs *obj = NULL;
668 684
669 if (xs_is_dict(object)) 685 if (xs_is_dict(object)) {
686 obj = xs_dup(object);
670 object = xs_dict_get(object, "id"); 687 object = xs_dict_get(object, "id");
688 }
671 689
672 /* bad object id? reject */ 690 /* bad object id? reject */
673 if (!xs_is_string(object)) 691 if (!xs_is_string(object))
674 return 0; 692 return 0;
675 693
694 /* try to get the object */
695 if (!xs_is_dict(obj))
696 object_get(object, &obj);
697
676 /* if it's about one of our posts, accept it */ 698 /* if it's about one of our posts, accept it */
677 if (xs_startswith(object, snac->actor)) 699 if (xs_startswith(object, snac->actor))
678 return 2; 700 return 2;
679 701
702 /* blocked by hashtag? */
703 if (blocked_hashtag_check(snac, obj))
704 return 0;
705
680 /* if it's by someone we follow, accept it */ 706 /* if it's by someone we follow, accept it */
681 if (following_check(snac, actor)) 707 if (following_check(snac, actor))
682 return 1; 708 return 1;
683 709
684 /* do we follow any hashtag? */ 710 /* do we follow any hashtag? */
685 if (xs_is_list(xs_dict_get(snac->config, "followed_hashtags"))) { 711 if (followed_hashtag_check(snac, obj))
686 xs *obj = NULL; 712 return 7;
687
688 /* if the admired object contains any followed hashtag, accept it */
689 if (valid_status(object_get(object, &obj)) &&
690 followed_hashtag_check(snac, obj))
691 return 7;
692 }
693 713
694 return 0; 714 return 0;
695 } 715 }
@@ -721,13 +741,20 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
721 return 1; 741 return 1;
722 } 742 }
723 743
744 const xs_dict *msg = xs_dict_get(c_msg, "object");
745
746 /* any blocked hashtag? reject */
747 if (blocked_hashtag_check(snac, msg)) {
748 snac_debug(snac, 1, xs_fmt("blocked by hashtag %s", xs_dict_get(msg, "id")));
749 return 0;
750 }
751
724 int pub_msg = is_msg_public(c_msg); 752 int pub_msg = is_msg_public(c_msg);
725 753
726 /* if this message is public and we follow the actor of this post, allow */ 754 /* if this message is public and we follow the actor of this post, allow */
727 if (pub_msg && following_check(snac, actor)) 755 if (pub_msg && following_check(snac, actor))
728 return 1; 756 return 1;
729 757
730 const xs_dict *msg = xs_dict_get(c_msg, "object");
731 xs *rcpts = recipient_list(snac, msg, 0); 758 xs *rcpts = recipient_list(snac, msg, 0);
732 xs_list *p = rcpts; 759 xs_list *p = rcpts;
733 const xs_str *v; 760 const xs_str *v;
@@ -1531,7 +1558,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
1531 1558
1532xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, 1559xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1533 const xs_str *in_reply_to, const xs_list *attach, 1560 const xs_str *in_reply_to, const xs_list *attach,
1534 int scope, const char *lang_str) 1561 int scope, const char *lang_str, const char *msg_date)
1535/* creates a 'Note' message */ 1562/* creates a 'Note' message */
1536/* scope: 0, public; 1, private (mentioned only); 2, "quiet public"; 3, followers only */ 1563/* scope: 0, public; 1, private (mentioned only); 2, "quiet public"; 3, followers only */
1537{ 1564{
@@ -1545,7 +1572,12 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1545 xs *irt = NULL; 1572 xs *irt = NULL;
1546 xs *tag = xs_list_new(); 1573 xs *tag = xs_list_new();
1547 xs *atls = xs_list_new(); 1574 xs *atls = xs_list_new();
1548 xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL); 1575
1576 /* discard non-parseable dates */
1577 if (!xs_is_string(msg_date) || xs_parse_iso_date(msg_date, 0) == 0)
1578 msg_date = NULL;
1579
1580 xs_dict *msg = msg_base(snac, "Note", id, NULL, xs_or(msg_date, "@now"), NULL);
1549 xs_list *p; 1581 xs_list *p;
1550 const xs_val *v; 1582 const xs_val *v;
1551 1583
@@ -1758,7 +1790,7 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
1758 const xs_list *opts, int multiple, int end_secs) 1790 const xs_list *opts, int multiple, int end_secs)
1759/* creates a Question message */ 1791/* creates a Question message */
1760{ 1792{
1761 xs_dict *msg = msg_note(user, content, NULL, NULL, attach, 0, NULL); 1793 xs_dict *msg = msg_note(user, content, NULL, NULL, attach, 0, NULL, NULL);
1762 int max = 8; 1794 int max = 8;
1763 xs_set seen; 1795 xs_set seen;
1764 1796
@@ -2336,7 +2368,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2336 xs *this_relay = xs_fmt("%s/relay", srv_baseurl); 2368 xs *this_relay = xs_fmt("%s/relay", srv_baseurl);
2337 2369
2338 if (strcmp(actor, this_relay) != 0) { 2370 if (strcmp(actor, this_relay) != 0) {
2339 if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED) 2371 if (valid_status(timeline_admire(snac, object, actor, 0)))
2340 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); 2372 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
2341 else 2373 else
2342 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", 2374 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",