PluginProbe
ActivityPub / 3.2.3
ActivityPub v3.2.3
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 3.2.3, at includes/class-http.php

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