PluginProbe
ActivityPub / 5.3.1
ActivityPub v5.3.1
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-webfinger.php +63 -128 8.2.1 → 5.3.1 View file →
@@ -6,11 +6,10 @@
6 6 */
7 7
8 8 namespace Activitypub;
9 9
10 -use Activitypub\Activity\Actor;
10 +use WP_Error;
11 11 use Activitypub\Collection\Actors;
12 -use Activitypub\Collection\Remote_Actors;
13 12
14 13 /**
15 14 * ActivityPub WebFinger Class.
16 15 *
@@ -39,9 +38,9 @@
39 38 * Resolve a WebFinger resource.
40 39 *
41 40 * @param string $uri The WebFinger Resource.
42 41 *
43 - * @return string|\WP_Error The URL or WP_Error.
42 + * @return string|WP_Error The URL or WP_Error.
44 43 */
45 44 public static function resolve( $uri ) {
46 45 $data = self::get_data( $uri );
47 46
@@ -49,9 +48,9 @@
49 48 return $data;
50 49 }
51 50
52 51 if ( ! is_array( $data ) || empty( $data['links'] ) ) {
53 - return new \WP_Error(
52 + return new WP_Error(
54 53 'webfinger_missing_links',
55 54 __( 'No valid Link elements found.', 'activitypub' ),
56 55 array(
57 56 'status' => 400,
@@ -62,9 +61,8 @@
62 61
63 62 foreach ( $data['links'] as $link ) {
64 63 if (
65 64 'self' === $link['rel'] &&
66 - isset( $link['type'] ) &&
67 65 (
68 66 'application/activity+json' === $link['type'] ||
69 67 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' === $link['type']
70 68 )
@@ -72,9 +70,9 @@
72 70 return $link['href'];
73 71 }
74 72 }
75 73
76 - return new \WP_Error(
74 + return new WP_Error(
77 75 'webfinger_url_no_activitypub',
78 76 __( 'The Site supports WebFinger but not ActivityPub', 'activitypub' ),
79 77 array(
80 78 'status' => 400,
@@ -85,13 +83,11 @@
85 83
86 84 /**
87 85 * Transform a URI to an acct <identifier>@<host>.
88 86 *
89 - * @see https://swicg.github.io/activitypub-webfinger/#reverse-discovery
90 - *
91 87 * @param string $uri The URI (acct:, mailto:, http:, https:).
92 88 *
93 - * @return string|\WP_Error Error or acct URI.
89 + * @return string|WP_Error Error or acct URI.
94 90 */
95 91 public static function uri_to_acct( $uri ) {
96 92 $data = self::get_data( $uri );
97 93
@@ -115,9 +111,9 @@
115 111 }
116 112 }
117 113 }
118 114
119 - return new \WP_Error(
115 + return new WP_Error(
120 116 'webfinger_url_no_acct',
121 117 __( 'No acct URI found.', 'activitypub' ),
122 118 array(
123 119 'status' => 400,
@@ -131,13 +127,13 @@
131 127 * Automatically adds acct: if it's missing.
132 128 *
133 129 * @param string $url The URI (acct:, mailto:, http:, https:).
134 130 *
135 - * @return \WP_Error|array Error reaction or array with identifier and host as values.
131 + * @return WP_Error|array Error reaction or array with identifier and host as values.
136 132 */
137 133 public static function get_identifier_and_host( $url ) {
138 134 if ( ! $url ) {
139 - return new \WP_Error(
135 + return new WP_Error(
140 136 'webfinger_invalid_identifier',
141 137 __( 'Invalid Identifier', 'activitypub' ),
142 138 array(
143 139 'status' => 400,
@@ -172,9 +168,9 @@
172 168 break;
173 169 }
174 170
175 171 if ( empty( $host ) ) {
176 - return new \WP_Error(
172 + return new WP_Error(
177 173 'webfinger_invalid_identifier',
178 174 __( 'Invalid Identifier', 'activitypub' ),
179 175 array(
180 176 'status' => 400,
@@ -190,9 +186,9 @@
190 186 * Get the WebFinger data for a given URI.
191 187 *
192 188 * @param string $uri The Identifier: <identifier>@<host> or URI.
193 189 *
194 - * @return \WP_Error|array Error reaction or array with identifier and host as values.
190 + * @return WP_Error|array Error reaction or array with identifier and host as values.
195 191 */
196 192 public static function get_data( $uri ) {
197 193 $identifier_and_host = self::get_identifier_and_host( $uri );
198 194
@@ -199,30 +195,47 @@
199 195 if ( is_wp_error( $identifier_and_host ) ) {
200 196 return $identifier_and_host;
201 197 }
202 198
199 + $transient_key = self::generate_cache_key( $uri );
200 +
203 201 list( $identifier, $host ) = $identifier_and_host;
204 202
203 + $data = \get_transient( $transient_key );
204 + if ( $data ) {
205 + return $data;
206 + }
207 +
205 208 $webfinger_url = sprintf(
206 209 'https://%s/.well-known/webfinger?resource=%s',
207 210 $host,
208 - \rawurlencode( $identifier )
211 + rawurlencode( $identifier )
209 212 );
210 213
211 - // Use Http::get() which handles all caching (success and errors).
212 - $response = Http::get(
214 + $response = wp_safe_remote_get(
213 215 $webfinger_url,
214 - array( 'headers' => array( 'Accept' => 'application/jrd+json' ) ),
215 - WEEK_IN_SECONDS
216 + array(
217 + 'headers' => array( 'Accept' => 'application/jrd+json' ),
218 + )
216 219 );
217 220
218 - if ( \is_wp_error( $response ) ) {
219 - return $response;
221 + if ( is_wp_error( $response ) ) {
222 + return new WP_Error(
223 + 'webfinger_url_not_accessible',
224 + __( 'The WebFinger Resource is not accessible.', 'activitypub' ),
225 + array(
226 + 'status' => 400,
227 + 'data' => $webfinger_url,
228 + )
229 + );
220 230 }
221 231
222 - $body = \wp_remote_retrieve_body( $response );
232 + $body = wp_remote_retrieve_body( $response );
233 + $data = json_decode( $body, true );
223 234
224 - return \json_decode( $body, true );
235 + \set_transient( $transient_key, $data, WEEK_IN_SECONDS );
236 +
237 + return $data;
225 238 }
226 239
227 240 /**
228 241 * Get the Remote-Follow endpoint for a given URI.
@@ -228,80 +241,21 @@
228 241 * Get the Remote-Follow endpoint for a given URI.
229 242 *
230 243 * @param string $uri The WebFinger Resource URI.
231 244 *
232 - * @return string|\WP_Error Error or the Remote-Follow endpoint URI.
245 + * @return string|WP_Error Error or the Remote-Follow endpoint URI.
233 246 */
234 247 public static function get_remote_follow_endpoint( $uri ) {
235 - return self::get_intent_endpoint( $uri, 'http://ostatus.org/schema/1.0/subscribe' );
236 - }
237 -
238 - /**
239 - * Generate a cache key for a given URI.
240 - *
241 - * @param string $uri A WebFinger Resource URI.
242 - *
243 - * @return string The cache key.
244 - */
245 - public static function generate_cache_key( $uri ) {
246 - $uri = ltrim( $uri, '@' );
247 -
248 - if ( filter_var( $uri, FILTER_VALIDATE_EMAIL ) ) {
249 - $uri = 'acct:' . $uri;
250 - }
251 -
252 - return 'webfinger_' . md5( $uri );
253 - }
254 -
255 - /**
256 - * Infer a shortname from the Actor ID or URL. Used only for fallbacks,
257 - * we will try to use what's supplied.
258 - *
259 - * @param Actor|string $actor_or_uri The Actor or URI.
260 - *
261 - * @return string Hopefully the name of the Follower.
262 - */
263 - public static function guess( $actor_or_uri ) {
264 - if ( ! $actor_or_uri instanceof Actor ) {
265 - $actor = Remote_Actors::fetch_by_uri( $actor_or_uri );
266 - if ( \is_wp_error( $actor ) ) {
267 - return extract_name_from_uri( $actor_or_uri ) . '@' . \wp_parse_url( $actor_or_uri, PHP_URL_HOST );
268 - }
269 -
270 - $actor_or_uri = $actor;
271 - }
272 -
273 - if ( $actor_or_uri->get_preferred_username() ) {
274 - return $actor_or_uri->get_preferred_username() . '@' . \wp_parse_url( $actor_or_uri->get_id(), PHP_URL_HOST );
275 - }
276 -
277 - return extract_name_from_uri( $actor_or_uri->get_id() ) . '@' . \wp_parse_url( $actor_or_uri->get_id(), PHP_URL_HOST );
278 - }
279 -
280 - /**
281 - * Get the Intent endpoint for a given URI and intent.
282 - *
283 - * @since 8.0.0
284 - *
285 - * @see https://codeberg.org/fediverse/fep/src/branch/main/fep/3b86/fep-3b86.md
286 - *
287 - * @param string $uri The WebFinger Resource URI.
288 - * @param string $intent The intent to look for.
289 - * @param bool $fallback Whether to fallback to the Remote-Follow endpoint.
290 - *
291 - * @return string|\WP_Error Error or the Intent endpoint URI (may contain `{uri}` placeholder).
292 - */
293 - public static function get_intent_endpoint( $uri, $intent, $fallback = false ) {
294 248 $data = self::get_data( $uri );
295 249
296 - if ( \is_wp_error( $data ) ) {
250 + if ( is_wp_error( $data ) ) {
297 251 return $data;
298 252 }
299 253
300 254 if ( empty( $data['links'] ) ) {
301 - return new \WP_Error(
255 + return new WP_Error(
302 256 'webfinger_missing_links',
303 - \__( 'No valid Link elements found.', 'activitypub' ),
257 + __( 'No valid Link elements found.', 'activitypub' ),
304 258 array(
305 259 'status' => 400,
306 260 'data' => $data,
307 261 )
@@ -307,56 +261,37 @@
307 261 )
308 262 );
309 263 }
310 264
311 - // Normalize the links with $rel as key.
312 - $links = array();
313 -
314 265 foreach ( $data['links'] as $link ) {
315 - if ( isset( $link['rel'] ) && isset( $link['template'] ) ) {
316 - $links[ \strtolower( $link['rel'] ) ] = $link['template'];
266 + if ( 'http://ostatus.org/schema/1.0/subscribe' === $link['rel'] ) {
267 + return $link['template'];
317 268 }
318 269 }
319 270
320 - $intent = \sanitize_text_field( $intent );
321 - $intent = \strtolower( $intent );
271 + return new WP_Error(
272 + 'webfinger_missing_remote_follow_endpoint',
273 + __( 'No valid Remote-Follow endpoint found.', 'activitypub' ),
274 + array(
275 + 'status' => 400,
276 + 'data' => $data,
277 + )
278 + );
279 + }
322 280
323 - if ( ! \filter_var( $intent, FILTER_VALIDATE_URL ) ) {
324 - $intent = 'https://w3id.org/fep/3b86/' . $intent;
325 - }
281 + /**
282 + * Generate a cache key for a given URI.
283 + *
284 + * @param string $uri A WebFinger Resource URI.
285 + *
286 + * @return string The cache key.
287 + */
288 + public static function generate_cache_key( $uri ) {
289 + $uri = ltrim( $uri, '@' );
326 290
327 - if ( isset( $links[ $intent ] ) ) {
328 - return $links[ $intent ];
291 + if ( filter_var( $uri, FILTER_VALIDATE_EMAIL ) ) {
292 + $uri = 'acct:' . $uri;
329 293 }
330 294
331 - if ( ! $fallback ) {
332 - return new \WP_Error(
333 - 'webfinger_missing_intent_endpoint',
334 - \__( 'No valid Intent endpoint found.', 'activitypub' ),
335 - array(
336 - 'status' => 400,
337 - 'data' => $data,
338 - )
339 - );
340 - }
341 -
342 - if ( isset( $links['http://ostatus.org/schema/1.0/subscribe'] ) ) {
343 - return $links['http://ostatus.org/schema/1.0/subscribe'];
344 - }
345 -
346 - // Last-resort: construct a Mastodon-compatible authorize_interaction URL.
347 - $identifier_and_host = self::get_identifier_and_host( $uri );
348 -
349 - if ( \is_wp_error( $identifier_and_host ) ) {
350 - return new \WP_Error(
351 - 'webfinger_missing_intent_endpoint',
352 - \__( 'No valid Intent endpoint found.', 'activitypub' ),
353 - array(
354 - 'status' => 400,
355 - 'data' => $data,
356 - )
357 - );
358 - }
359 -
360 - return 'https://' . $identifier_and_host[1] . '/authorize_interaction?uri={uri}';
295 + return 'webfinger_' . md5( $uri );
361 296 }
362 297 }