PluginProbe
ActivityPub / 3.2.5
ActivityPub v3.2.5
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
← All changes | includes/handler/class-announce.php +44 -128 9.2.1 → 3.2.5 View file →
@@ -1,181 +1,97 @@
1 1 <?php
2 -/**
3 - * Announce handler file.
4 - *
5 - * @package Activitypub
6 - */
7 -
8 2 namespace Activitypub\Handler;
9 3
10 -use Activitypub\Collection\Actors;
4 +use Activitypub\Http;
5 +use Activitypub\Comment;
11 6 use Activitypub\Collection\Interactions;
12 -use Activitypub\Comment;
13 -use Activitypub\Http;
14 7
15 -use function Activitypub\is_activity;
8 +use function Activitypub\object_to_uri;
16 9 use function Activitypub\is_activity_public;
17 -use function Activitypub\is_same_host;
18 -use function Activitypub\object_to_uri;
19 10
20 11 /**
21 - * Handle Create requests.
12 + * Handle Create requests
22 13 */
23 14 class Announce {
24 15 /**
25 - * Initialize the class, registering WordPress hooks.
16 + * Initialize the class, registering WordPress hooks
26 17 */
27 18 public static function init() {
28 - \add_action( 'activitypub_inbox_announce', array( self::class, 'handle_announce' ), 10, 3 );
19 + \add_action(
20 + 'activitypub_inbox_announce',
21 + array( self::class, 'handle_announce' ),
22 + 10,
23 + 3
24 + );
29 25 }
30 26
31 27 /**
32 - * Handles "Announce" requests.
28 + * Handles "Announce" requests
33 29 *
34 - * @param array $announcement The activity-object.
35 - * @param int|int[] $user_ids The id(s) of the local blog-user(s).
36 - * @param \Activitypub\Activity\Activity $activity The activity object.
30 + * @param array $array The activity-object
31 + * @param int $user_id The id of the local blog-user
32 + * @param Activitypub\Activity $activity The activity object
33 + *
34 + * @return void
37 35 */
38 - public static function handle_announce( $announcement, $user_ids, $activity = null ) {
39 - // Check if Activity is public or not.
40 - if ( ! is_activity_public( $announcement ) ) {
36 + public static function handle_announce( $array, $user_id, $activity = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.arrayFound
37 + // check if Activity is public or not
38 + if ( ! is_activity_public( $array ) ) {
41 39 // @todo maybe send email
42 40 return;
43 41 }
44 42
45 - // Ignore announces from the blog actor.
46 - if ( Actors::BLOG_USER_ID === Actors::get_id_by_resource( $announcement['actor'] ) ) {
47 - return;
43 + if ( ! ACTIVITYPUB_DISABLE_REACTIONS ) {
44 + self::maybe_save_announce( $array, $user_id, $activity );
48 45 }
49 46
50 - // Check if reposts are allowed.
51 - if ( ! Comment::is_comment_type_enabled( 'repost' ) ) {
52 - return;
47 + if ( is_string( $array['object'] ) ) {
48 + $object = Http::get_remote_object( $array['object'] );
49 + } else {
50 + $object = $array['object'];
53 51 }
54 52
55 - self::maybe_save_announce( $announcement, $user_ids );
56 -
57 - $object_url = object_to_uri( $announcement['object'] );
58 -
59 - // Force no redirects for this object's request only, so the requested host stays the authoritative origin.
60 - $no_redirects = static function ( $args, $url ) use ( $object_url ) {
61 - if ( $url === $object_url ) {
62 - $args['redirection'] = 0;
63 - }
64 - return $args;
65 - };
66 -
67 - /*
68 - * Fetch the activity from its own id rather than the inline copy the Announce
69 - * carries: that copy is the announcer's, who is not necessarily the activity's
70 - * author. Redirects are forbidden (above) and the cache is bypassed so the
71 - * requested host is the authoritative origin — otherwise a redirect, or a
72 - * response cached from an earlier redirect-following fetch, could resolve to
73 - * attacker content while the host check below still saw the trusted host.
74 - */
75 - \add_filter( 'http_request_args', $no_redirects, 10, 2 );
76 - $object = Http::get_remote_object( $object_url, false );
77 - \remove_filter( 'http_request_args', $no_redirects, 10 );
78 -
79 - if ( ! $object || \is_wp_error( $object ) || ! \is_array( $object ) ) {
53 + if ( ! $object || is_wp_error( $object ) ) {
80 54 return;
81 55 }
82 56
83 - if ( ! is_activity( $object ) ) {
57 + if ( ! isset( $object['type'] ) ) {
84 58 return;
85 59 }
86 60
87 - $origin_host = \strtolower( (string) \wp_parse_url( (string) $object_url, \PHP_URL_HOST ) );
88 - $actor_host = \strtolower( (string) \wp_parse_url( (string) object_to_uri( $object['actor'] ?? '' ), \PHP_URL_HOST ) );
89 -
90 - /*
91 - * Only an actor's own server may vouch for an activity attributed to it, so the
92 - * host it was fetched from must equal its actor's host — the same key-host ==
93 - * actor-host binding verify_key_id() enforces for signed requests, generalised
94 - * to every relayed activity type.
95 - */
96 - if ( '' === $origin_host || '' === $actor_host || $origin_host !== $actor_host ) {
97 - return;
98 - }
99 -
100 - /*
101 - * The requested URL is not always the host that answered: get_remote_object() re-fetches a
102 - * document from the id it declares when the two disagree, and returns the re-fetched copy.
103 - * Bind the actor to that id as well, which an authentic activity shares a host with.
104 - *
105 - * Only when the document declares one. The id is derived exactly as get_remote_object()
106 - * derives it, so the two cannot disagree about what counts as declared: whatever it treats
107 - * as id-less it returns as served, without re-fetching, and the origin check above is
108 - * already authoritative for those. Binding them here would drop relayed activities that
109 - * legitimately omit an id.
110 - */
111 - $declared_id = isset( $object['id'] ) && \is_string( $object['id'] ) ? $object['id'] : '';
112 -
113 - if ( '' !== $declared_id && ! is_same_host( $declared_id, $object['actor'] ?? '' ) ) {
114 - return;
115 - }
116 -
117 61 $type = \strtolower( $object['type'] );
118 62
119 - /**
120 - * Fires after an Announce has been received.
121 - *
122 - * @param array $object The object.
123 - * @param int[] $user_ids The ids of the local blog-users.
124 - * @param string $type The type of the activity.
125 - * @param \Activitypub\Activity\Activity|null $activity The activity object.
126 - */
127 - \do_action( 'activitypub_inbox', $object, (array) $user_ids, $type, $activity );
128 -
129 - /**
130 - * Fires after an Announce of a specific type has been received.
131 - *
132 - * @param array $object The object.
133 - * @param int[] $user_ids The ids of the local blog-users.
134 - * @param \Activitypub\Activity\Activity|null $activity The activity object.
135 - */
136 - \do_action( "activitypub_inbox_{$type}", $object, (array) $user_ids, $activity );
63 + \do_action( 'activitypub_inbox', $object, $user_id, $type, $activity );
64 + \do_action( "activitypub_inbox_{$type}", $object, $user_id, $activity );
137 65 }
138 66
139 67 /**
140 - * Try to save the Announce.
68 + * Try to save the Announce
141 69 *
142 - * @param array $activity The activity-object.
143 - * @param int|int[] $user_ids The id of the local blog-user.
70 + * @param array $array The activity-object
71 + * @param int $user_id The id of the local blog-user
72 + * @param Activitypub\Activity $activity The activity object
73 + *
74 + * @return void
144 75 */
145 - public static function maybe_save_announce( $activity, $user_ids ) {
146 - $url = object_to_uri( $activity );
76 + public static function maybe_save_announce( $array, $user_id, $activity ) { // phpcs:ignore
77 + $url = object_to_uri( $array['object'] );
147 78
148 79 if ( empty( $url ) ) {
149 80 return;
150 81 }
151 82
152 - // Match any status, so a repost that was marked as spam or trashed still counts as seen.
153 - $exists = Comment::object_id_to_comment( \esc_url_raw( $url ), array( 'status' => 'any' ) );
83 + $exists = Comment::object_id_to_comment( esc_url_raw( $url ) );
154 84 if ( $exists ) {
155 85 return;
156 86 }
157 87
158 - // If the object is a Create activity, extract the actual object from it.
159 - if ( isset( $activity['object']['type'] ) && 'Create' === $activity['object']['type'] ) {
160 - $activity['object'] = object_to_uri( $activity['object']['object'] );
161 - }
88 + $state = Interactions::add_reaction( $array );
89 + $reaction = null;
162 90
163 - $success = false;
164 - $result = Interactions::add_reaction( $activity );
165 -
166 - if ( $result && ! \is_wp_error( $result ) ) {
167 - $success = true;
168 - $result = \get_comment( $result );
91 + if ( $state && ! is_wp_error( $state ) ) {
92 + $reaction = get_comment( $state );
169 93 }
170 94
171 - /**
172 - * Fires after an ActivityPub Announce activity has been handled.
173 - *
174 - * @param array $activity The ActivityPub activity data.
175 - * @param int[] $user_ids The local user IDs.
176 - * @param bool $success True on success, false otherwise.
177 - * @param array|string|int|\WP_Error|false $result The WP_Comment object of the created announce/repost comment, or null if creation failed.
178 - */
179 - \do_action( 'activitypub_handled_announce', $activity, (array) $user_ids, $success, $result );
95 + do_action( 'activitypub_handled_announce', $array, $user_id, $state, $reaction );
180 96 }
181 97 }