PluginProbe
ActivityPub / 4.3.0
ActivityPub v4.3.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
activitypub / includes / class-http.php

class-http.php in ActivityPub 4.3.0, at includes/class-http.php

288 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ActivityPub HTTP Class.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub;
9
10 use WP_Error;
11 use Activitypub\Collection\Actors;
12
13 /**
14 * ActivityPub HTTP Class
15 *
16 * @author Matthias Pfefferle
17 */
18 class Http {
19 /**
20 * Send a POST Request with the needed HTTP Headers
21 *
22 * @param string $url The URL endpoint.
23 * @param string $body The Post Body.
24 * @param int $user_id The WordPress User-ID.
25 *
26 * @return array|WP_Error The POST Response or an WP_Error.
27 */
28 public static function post( $url, $body, $user_id ) {
29 \do_action( 'activitypub_pre_http_post', $url, $body, $user_id );
30
31 $date = \gmdate( 'D, d M Y H:i:s T' );
32 $digest = Signature::generate_digest( $body );
33 $signature = Signature::generate_signature( $user_id, 'post', $url, $date, $digest );
34
35 $wp_version = get_masked_wp_version();
36
37 /**
38 * Filter the HTTP headers user agent.
39 *
40 * @param string $user_agent The user agent string.
41 */
42 $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
43 $args = array(
44 'timeout' => 100,
45 'limit_response_size' => 1048576,
46 'redirection' => 3,
47 'user-agent' => "$user_agent; ActivityPub",
48 'headers' => array(
49 'Accept' => 'application/activity+json',
50 'Content-Type' => 'application/activity+json',
51 'Digest' => $digest,
52 'Signature' => $signature,
53 'Date' => $date,
54 ),
55 'body' => $body,
56 );
57
58 $response = \wp_safe_remote_post( $url, $args );
59 $code = \wp_remote_retrieve_response_code( $response );
60
61 if ( $code >= 400 ) {
62 $response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) );
63 }
64
65 /**
66 * Action to save the response of the remote POST request.
67 *
68 * @param array|WP_Error $response The response of the remote POST request.
69 * @param string $url The URL endpoint.
70 * @param string $body The Post Body.
71 * @param int $user_id The WordPress User-ID.
72 */
73 \do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id );
74
75 return $response;
76 }
77
78 /**
79 * Send a GET Request with the needed HTTP Headers.
80 *
81 * @param string $url The URL endpoint.
82 * @param bool|int $cached Optional. Whether the result should be cached, or its duration. Default false.
83 *
84 * @return array|WP_Error The GET Response or a WP_Error.
85 */
86 public static function get( $url, $cached = false ) {
87 \do_action( 'activitypub_pre_http_get', $url );
88
89 if ( $cached ) {
90 $transient_key = self::generate_cache_key( $url );
91
92 $response = \get_transient( $transient_key );
93
94 if ( $response ) {
95 /**
96 * Action to save the response of the remote GET request.
97 *
98 * @param array|WP_Error $response The response of the remote GET request.
99 * @param string $url The URL endpoint.
100 */
101 \do_action( 'activitypub_safe_remote_get_response', $response, $url );
102
103 return $response;
104 }
105 }
106
107 $date = \gmdate( 'D, d M Y H:i:s T' );
108 $signature = Signature::generate_signature( Actors::APPLICATION_USER_ID, 'get', $url, $date );
109
110 $wp_version = get_masked_wp_version();
111
112 /**
113 * Filter the HTTP headers user agent.
114 *
115 * @param string $user_agent The user agent string.
116 */
117 $user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
118
119 $args = array(
120 'timeout' => apply_filters( 'activitypub_remote_get_timeout', 100 ),
121 'limit_response_size' => 1048576,
122 'redirection' => 3,
123 'user-agent' => "$user_agent; ActivityPub",
124 'headers' => array(
125 'Accept' => 'application/activity+json',
126 'Content-Type' => 'application/activity+json',
127 'Signature' => $signature,
128 'Date' => $date,
129 ),
130 );
131
132 $response = \wp_safe_remote_get( $url, $args );
133 $code = \wp_remote_retrieve_response_code( $response );
134
135 if ( $code >= 400 ) {
136 $response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ), array( 'status' => $code ) );
137 }
138
139 /**
140 * Action to save the response of the remote GET request.
141 *
142 * @param array|WP_Error $response The response of the remote GET request.
143 * @param string $url The URL endpoint.
144 */
145 \do_action( 'activitypub_safe_remote_get_response', $response, $url );
146
147 if ( $cached ) {
148 $cache_duration = $cached;
149 if ( ! is_int( $cache_duration ) ) {
150 $cache_duration = HOUR_IN_SECONDS;
151 }
152 \set_transient( $transient_key, $response, $cache_duration );
153 }
154
155 return $response;
156 }
157
158 /**
159 * Check for URL for Tombstone.
160 *
161 * @param string $url The URL to check.
162 *
163 * @return bool True if the URL is a tombstone.
164 */
165 public static function is_tombstone( $url ) {
166 /**
167 * Action before checking if the URL is a tombstone.
168 *
169 * @param string $url The URL to check.
170 */
171 \do_action( 'activitypub_pre_http_is_tombstone', $url );
172
173 $response = \wp_safe_remote_get( $url );
174 $code = \wp_remote_retrieve_response_code( $response );
175
176 if ( in_array( (int) $code, array( 404, 410 ), true ) ) {
177 return true;
178 }
179
180 return false;
181 }
182
183 /**
184 * Generate a cache key for the URL.
185 *
186 * @param string $url The URL to generate the cache key for.
187 *
188 * @return string The cache key.
189 */
190 public static function generate_cache_key( $url ) {
191 return 'activitypub_http_' . \md5( $url );
192 }
193
194 /**
195 * Requests the Data from the Object-URL or Object-Array.
196 *
197 * @param array|string $url_or_object The Object or the Object URL.
198 * @param bool $cached Optional. Whether the result should be cached. Default true.
199 *
200 * @return array|WP_Error The Object data as array or WP_Error on failure.
201 */
202 public static function get_remote_object( $url_or_object, $cached = true ) {
203 if ( is_array( $url_or_object ) ) {
204 if ( array_key_exists( 'id', $url_or_object ) ) {
205 $url = $url_or_object['id'];
206 } elseif ( array_key_exists( 'url', $url_or_object ) ) {
207 $url = $url_or_object['url'];
208 } else {
209 return new WP_Error(
210 'activitypub_no_valid_actor_identifier',
211 \__( 'The "actor" identifier is not valid', 'activitypub' ),
212 array(
213 'status' => 404,
214 'object' => $url_or_object,
215 )
216 );
217 }
218 } else {
219 $url = $url_or_object;
220 }
221
222 if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $url ) ) {
223 $url = Webfinger::resolve( $url );
224 }
225
226 if ( ! $url ) {
227 return new WP_Error(
228 'activitypub_no_valid_actor_identifier',
229 \__( 'The "actor" identifier is not valid', 'activitypub' ),
230 array(
231 'status' => 404,
232 'object' => $url,
233 )
234 );
235 }
236
237 if ( is_wp_error( $url ) ) {
238 return $url;
239 }
240
241 $transient_key = self::generate_cache_key( $url );
242
243 // Only check the cache if needed.
244 if ( $cached ) {
245 $data = \get_transient( $transient_key );
246
247 if ( $data ) {
248 return $data;
249 }
250 }
251
252 if ( ! \wp_http_validate_url( $url ) ) {
253 return new WP_Error(
254 'activitypub_no_valid_object_url',
255 \__( 'The "object" is/has no valid URL', 'activitypub' ),
256 array(
257 'status' => 400,
258 'object' => $url,
259 )
260 );
261 }
262
263 $response = self::get( $url );
264
265 if ( \is_wp_error( $response ) ) {
266 return $response;
267 }
268
269 $data = \wp_remote_retrieve_body( $response );
270 $data = \json_decode( $data, true );
271
272 if ( ! $data ) {
273 return new WP_Error(
274 'activitypub_invalid_json',
275 \__( 'No valid JSON data', 'activitypub' ),
276 array(
277 'status' => 400,
278 'object' => $url,
279 )
280 );
281 }
282
283 \set_transient( $transient_key, $data, WEEK_IN_SECONDS );
284
285 return $data;
286 }
287 }
288