PluginProbe
ActivityPub / 5.7.0
ActivityPub v5.7.0
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/class-http.php +89 -100 8.2.15.7.0 View file →
@@ -6,8 +6,9 @@
6 6 */
7 7
8 8 namespace Activitypub;
9 9
10 +use WP_Error;
10 11 use Activitypub\Collection\Actors;
11 12
12 13 /**
13 14 * ActivityPub HTTP Class
@@ -21,9 +22,9 @@
21 22 * @param string $url The URL endpoint.
22 23 * @param string $body The Post Body.
23 24 * @param int $user_id The WordPress User-ID.
24 25 *
25 - * @return array|\WP_Error The POST Response or an WP_Error.
26 + * @return array|WP_Error The POST Response or an WP_Error.
26 27 */
27 28 public static function post( $url, $body, $user_id ) {
28 29 /**
29 30 * Fires before an HTTP POST request is made.
@@ -33,25 +34,22 @@
33 34 * @param int $user_id The WordPress User ID.
34 35 */
35 36 \do_action( 'activitypub_pre_http_post', $url, $body, $user_id );
36 37
38 + $date = \gmdate( 'D, d M Y H:i:s T' );
39 + $digest = Signature::generate_digest( $body );
40 + $signature = Signature::generate_signature( $user_id, 'post', $url, $date, $digest );
41 +
42 + $wp_version = get_masked_wp_version();
43 +
37 44 /**
38 45 * Filters the HTTP headers user agent string.
39 46 *
40 47 * @param string $user_agent The user agent string.
41 - * @param string $url The request URL.
42 48 */
43 - $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . get_masked_wp_version() . '; ' . \get_bloginfo( 'url' ), $url );
44 -
45 - /**
46 - * Filters the timeout duration for remote POST requests in ActivityPub.
47 - *
48 - * @param int $timeout The timeout value in seconds. Default 10 seconds.
49 - */
50 - $timeout = \apply_filters( 'activitypub_remote_post_timeout', 10 );
51 -
52 - $args = array(
53 - 'timeout' => $timeout,
49 + $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
50 + $args = array(
51 + 'timeout' => 100,
54 52 'limit_response_size' => 1048576,
55 53 'redirection' => 3,
56 54 'user-agent' => "$user_agent; ActivityPub",
57 55 'headers' => array(
@@ -56,14 +54,13 @@
56 54 'user-agent' => "$user_agent; ActivityPub",
57 55 'headers' => array(
58 56 'Accept' => 'application/activity+json',
59 57 'Content-Type' => 'application/activity+json',
60 - 'Date' => \gmdate( 'D, d M Y H:i:s T' ),
58 + 'Digest' => $digest,
59 + 'Signature' => $signature,
60 + 'Date' => $date,
61 61 ),
62 62 'body' => $body,
63 - 'key_id' => \json_decode( $body )->actor . '#main-key',
64 - 'private_key' => Actors::get_private_key( $user_id ),
65 - 'user_id' => $user_id,
66 63 );
67 64
68 65 $response = \wp_safe_remote_post( $url, $args );
69 66 $code = \wp_remote_retrieve_response_code( $response );
@@ -68,9 +65,9 @@
68 65 $response = \wp_safe_remote_post( $url, $args );
69 66 $code = \wp_remote_retrieve_response_code( $response );
70 67
71 68 if ( $code >= 400 ) {
72 - $response = new \WP_Error(
69 + $response = new WP_Error(
73 70 $code,
74 71 __( 'Failed HTTP Request', 'activitypub' ),
75 72 array(
76 73 'status' => $code,
@@ -81,12 +78,12 @@
81 78
82 79 /**
83 80 * Action to save the response of the remote POST request.
84 81 *
85 - * @param array|\WP_Error $response The response of the remote POST request.
86 - * @param string $url The URL endpoint.
87 - * @param string $body The Post Body.
88 - * @param int $user_id The WordPress User-ID.
82 + * @param array|WP_Error $response The response of the remote POST request.
83 + * @param string $url The URL endpoint.
84 + * @param string $body The Post Body.
85 + * @param int $user_id The WordPress User-ID.
89 86 */
90 87 \do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id );
91 88
92 89 return $response;
@@ -95,26 +92,13 @@
95 92 /**
96 93 * Send a GET Request with the needed HTTP Headers.
97 94 *
98 95 * @param string $url The URL endpoint.
99 - * @param array $args Optional. Additional arguments to customize the request.
100 - * - 'headers': Array of headers to override defaults.
101 - * @param bool|int $cached Optional. Whether to return cached results, or cache duration. Default false.
96 + * @param bool|int $cached Optional. Whether the result should be cached, or its duration. Default false.
102 97 *
103 - * @return array|\WP_Error The GET Response or a WP_Error.
98 + * @return array|WP_Error The GET Response or a WP_Error.
104 99 */
105 - public static function get( $url, $args = array(), $cached = false ) {
106 - // Backward compatibility: if $args is boolean/int, it's the old $cached parameter.
107 - if ( ! \is_array( $args ) ) {
108 - \_deprecated_argument(
109 - __METHOD__,
110 - '7.9.0',
111 - \esc_html__( 'The $cached parameter should now be passed as the third argument.', 'activitypub' )
112 - );
113 - $cached = $args;
114 - $args = array();
115 - }
116 -
100 + public static function get( $url, $cached = false ) {
117 101 /**
118 102 * Fires before an HTTP GET request is made.
119 103 *
120 104 * @param string $url The URL endpoint.
@@ -120,12 +104,11 @@
120 104 * @param string $url The URL endpoint.
121 105 */
122 106 \do_action( 'activitypub_pre_http_get', $url );
123 107
124 - $transient_key = self::generate_cache_key( $url );
108 + if ( $cached ) {
109 + $transient_key = self::generate_cache_key( $url );
125 110
126 - // Check cache only if caching is requested.
127 - if ( $cached ) {
128 111 $response = \get_transient( $transient_key );
129 112
130 113 if ( $response ) {
131 114 /**
@@ -130,10 +113,10 @@
130 113 if ( $response ) {
131 114 /**
132 115 * Action to save the response of the remote GET request.
133 116 *
134 - * @param array|\WP_Error $response The response of the remote GET request.
135 - * @param string $url The URL endpoint.
117 + * @param array|WP_Error $response The response of the remote GET request.
118 + * @param string $url The URL endpoint.
136 119 */
137 120 \do_action( 'activitypub_safe_remote_get_response', $response, $url );
138 121
139 122 return $response;
@@ -139,24 +122,31 @@
139 122 return $response;
140 123 }
141 124 }
142 125
126 + $date = \gmdate( 'D, d M Y H:i:s T' );
127 + $signature = Signature::generate_signature( Actors::APPLICATION_USER_ID, 'get', $url, $date );
128 +
129 + $wp_version = get_masked_wp_version();
130 +
143 131 /**
144 132 * Filters the HTTP headers user agent string.
145 133 *
134 + * This filter allows developers to modify the user agent string that is
135 + * sent with HTTP requests.
136 + *
146 137 * @param string $user_agent The user agent string.
147 - * @param string $url The request URL.
148 138 */
149 - $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . get_masked_wp_version() . '; ' . \get_bloginfo( 'url' ), $url );
139 + $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
150 140
151 141 /**
152 142 * Filters the timeout duration for remote GET requests in ActivityPub.
153 143 *
154 - * @param int $timeout The timeout value in seconds. Default 10 seconds.
144 + * @param int $timeout The timeout value in seconds. Default 100 seconds.
155 145 */
156 - $timeout = \apply_filters( 'activitypub_remote_get_timeout', 10 );
146 + $timeout = \apply_filters( 'activitypub_remote_get_timeout', 100 );
157 147
158 - $defaults = array(
148 + $args = array(
159 149 'timeout' => $timeout,
160 150 'limit_response_size' => 1048576,
161 151 'redirection' => 3,
162 152 'user-agent' => "$user_agent; ActivityPub",
@@ -162,56 +152,35 @@
162 152 'user-agent' => "$user_agent; ActivityPub",
163 153 'headers' => array(
164 154 'Accept' => 'application/activity+json',
165 155 'Content-Type' => 'application/activity+json',
166 - 'Date' => \gmdate( 'D, d M Y H:i:s T' ),
156 + 'Signature' => $signature,
157 + 'Date' => $date,
167 158 ),
168 - 'key_id' => Actors::get_by_id( Actors::APPLICATION_USER_ID )->get_id() . '#main-key',
169 - 'private_key' => Actors::get_private_key( Actors::APPLICATION_USER_ID ),
170 159 );
171 160
172 - $args = \wp_parse_args( $args, $defaults );
173 - $args['headers'] = \wp_parse_args( $args['headers'], $defaults['headers'] );
174 -
175 161 $response = \wp_safe_remote_get( $url, $args );
176 162 $code = \wp_remote_retrieve_response_code( $response );
177 163
178 - if ( \is_wp_error( $response ) || $code >= 400 ) {
179 - if ( ! $code ) {
180 - $code = 0;
181 - }
182 - $response = new \WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) );
183 -
184 - /*
185 - * Always cache errors to prevent repeated timeout waits.
186 - * - Retriable errors (timeouts, 5xx): 1 minute (server may recover quickly).
187 - * - Other errors (4xx): 15 minutes (client errors are more permanent).
188 - */
189 - if ( \in_array( $code, ACTIVITYPUB_RETRY_ERROR_CODES, true ) || 0 === $code ) {
190 - $cache_duration = MINUTE_IN_SECONDS;
191 - } else {
192 - $cache_duration = 15 * MINUTE_IN_SECONDS;
193 - }
194 -
195 - \set_transient( $transient_key, $response, $cache_duration );
196 -
197 - return $response;
164 + if ( $code >= 400 ) {
165 + $response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) );
198 166 }
199 167
200 168 /**
201 169 * Action to save the response of the remote GET request.
202 170 *
203 - * @param array|\WP_Error $response The response of the remote GET request.
204 - * @param string $url The URL endpoint.
171 + * @param array|WP_Error $response The response of the remote GET request.
172 + * @param string $url The URL endpoint.
205 173 */
206 174 \do_action( 'activitypub_safe_remote_get_response', $response, $url );
207 175
208 - // Always cache successful responses.
209 - $cache_duration = $cached;
210 - if ( ! is_int( $cache_duration ) ) {
211 - $cache_duration = HOUR_IN_SECONDS;
176 + if ( $cached ) {
177 + $cache_duration = $cached;
178 + if ( ! is_int( $cache_duration ) ) {
179 + $cache_duration = HOUR_IN_SECONDS;
180 + }
181 + \set_transient( $transient_key, $response, $cache_duration );
212 182 }
213 - \set_transient( $transient_key, $response, $cache_duration );
214 183
215 184 return $response;
216 185 }
217 186
@@ -222,11 +191,29 @@
222 191 *
223 192 * @return bool True if the URL is a tombstone.
224 193 */
225 194 public static function is_tombstone( $url ) {
226 - _deprecated_function( __METHOD__, '7.3.0', 'Activitypub\Tombstone::exists_remote' );
195 + /**
196 + * Fires before checking if the URL is a tombstone.
197 + *
198 + * @param string $url The URL to check.
199 + */
200 + \do_action( 'activitypub_pre_http_is_tombstone', $url );
227 201
228 - return Tombstone::exists_remote( $url );
202 + $response = \wp_safe_remote_get( $url, array( 'headers' => array( 'Accept' => 'application/activity+json' ) ) );
203 + $code = \wp_remote_retrieve_response_code( $response );
204 +
205 + if ( in_array( (int) $code, array( 404, 410 ), true ) ) {
206 + return true;
207 + }
208 +
209 + $data = \wp_remote_retrieve_body( $response );
210 + $data = \json_decode( $data, true );
211 + if ( $data && isset( $data['type'] ) && 'Tombstone' === $data['type'] ) {
212 + return true;
213 + }
214 +
215 + return false;
229 216 }
230 217
231 218 /**
232 219 * Generate a cache key for the URL.
@@ -244,22 +231,11 @@
244 231 *
245 232 * @param array|string $url_or_object The Object or the Object URL.
246 233 * @param bool $cached Optional. Whether the result should be cached. Default true.
247 234 *
248 - * @return array|\WP_Error The Object data as array or WP_Error on failure.
235 + * @return array|WP_Error The Object data as array or WP_Error on failure.
249 236 */
250 237 public static function get_remote_object( $url_or_object, $cached = true ) {
251 - /**
252 - * Filters the preemptive return value of a remote object request.
253 - *
254 - * @param array|string|null $response The response.
255 - * @param array|string|null $url_or_object The Object or the Object URL.
256 - */
257 - $response = apply_filters( 'activitypub_pre_http_get_remote_object', null, $url_or_object );
258 - if ( null !== $response ) {
259 - return $response;
260 - }
261 -
262 238 $url = object_to_uri( $url_or_object );
263 239
264 240 if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $url ) ) {
265 241 $url = Webfinger::resolve( $url );
@@ -265,9 +241,9 @@
265 241 $url = Webfinger::resolve( $url );
266 242 }
267 243
268 244 if ( ! $url ) {
269 - return new \WP_Error(
245 + return new WP_Error(
270 246 'activitypub_no_valid_actor_identifier',
271 247 \__( 'The "actor" identifier is not valid', 'activitypub' ),
272 248 array(
273 249 'status' => 404,
@@ -275,14 +251,25 @@
275 251 )
276 252 );
277 253 }
278 254
279 - if ( \is_wp_error( $url ) ) {
255 + if ( is_wp_error( $url ) ) {
280 256 return $url;
281 257 }
282 258
259 + $transient_key = self::generate_cache_key( $url );
260 +
261 + // Only check the cache if needed.
262 + if ( $cached ) {
263 + $data = \get_transient( $transient_key );
264 +
265 + if ( $data ) {
266 + return $data;
267 + }
268 + }
269 +
283 270 if ( ! \wp_http_validate_url( $url ) ) {
284 - return new \WP_Error(
271 + return new WP_Error(
285 272 'activitypub_no_valid_object_url',
286 273 \__( 'The "object" is/has no valid URL', 'activitypub' ),
287 274 array(
288 275 'status' => 400,
@@ -290,9 +277,9 @@
290 277 )
291 278 );
292 279 }
293 280
294 - $response = self::get( $url, array(), $cached );
281 + $response = self::get( $url );
295 282
296 283 if ( \is_wp_error( $response ) ) {
297 284 return $response;
298 285 }
@@ -300,9 +287,9 @@
300 287 $data = \wp_remote_retrieve_body( $response );
301 288 $data = \json_decode( $data, true );
302 289
303 290 if ( ! $data ) {
304 - return new \WP_Error(
291 + return new WP_Error(
305 292 'activitypub_invalid_json',
306 293 \__( 'No valid JSON data', 'activitypub' ),
307 294 array(
308 295 'status' => 400,
@@ -309,8 +296,10 @@
309 296 'object' => $url,
310 297 )
311 298 );
312 299 }
300 +
301 + \set_transient( $transient_key, $data, WEEK_IN_SECONDS );
313 302
314 303 return $data;
315 304 }
316 305 }