From 33b61ca2b97ad0e7e5afd8c647be81855b3332fa Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Thu, 19 Oct 2023 14:46:31 -0500 Subject: [PATCH 01/14] Shortcodes: only register when needed (#526) --- activitypub.php | 1 - includes/class-shortcodes.php | 20 +++++++++++++------- includes/transformer/class-post.php | 6 ++++++ tests/test-class-activitypub-shortcodes.php | 7 +++++++ 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/activitypub.php b/activitypub.php index 521a379..f232c57 100644 --- a/activitypub.php +++ b/activitypub.php @@ -69,7 +69,6 @@ function plugin_init() { \add_action( 'init', array( __NAMESPACE__ . '\Collection\Followers', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Admin', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Hashtag', 'init' ) ); - \add_action( 'init', array( __NAMESPACE__ . '\Shortcodes', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Mention', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) ); diff --git a/includes/class-shortcodes.php b/includes/class-shortcodes.php index 43be17b..708aa61 100644 --- a/includes/class-shortcodes.php +++ b/includes/class-shortcodes.php @@ -5,14 +5,9 @@ use function Activitypub\esc_hashtag; class Shortcodes { /** - * Class constructor, registering WordPress then Shortcodes + * Register the shortcodes */ - public static function init() { - // do not load on admin pages - if ( is_admin() ) { - return; - } - + public static function register() { foreach ( get_class_methods( self::class ) as $shortcode ) { if ( 'init' !== $shortcode ) { add_shortcode( 'ap_' . $shortcode, array( self::class, $shortcode ) ); @@ -20,6 +15,17 @@ class Shortcodes { } } + /** + * Unregister the shortcodes + */ + public static function unregister() { + foreach ( get_class_methods( self::class ) as $shortcode ) { + if ( 'init' !== $shortcode ) { + remove_shortcode( 'ap_' . $shortcode ); + } + } + } + /** * Generates output for the 'ap_hashtags' shortcode * diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 6e2f0aa..9250afe 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -5,6 +5,7 @@ use WP_Post; use Activitypub\Collection\Users; use Activitypub\Model\Blog_User; use Activitypub\Activity\Base_Object; +use Activitypub\Shortcodes; use function Activitypub\esc_hashtag; use function Activitypub\is_single_user; @@ -466,6 +467,8 @@ class Post { $post = $this->wp_post; $content = $this->get_post_content_template(); + // Register our shortcodes just in time. + Shortcodes::register(); // Fill in the shortcodes. setup_postdata( $post ); $content = do_shortcode( $content ); @@ -477,6 +480,9 @@ class Post { $content = \apply_filters( 'activitypub_the_content', $content, $post ); + // Don't need these any more, should never appear in a post. + Shortcodes::unregister(); + return $content; } diff --git a/tests/test-class-activitypub-shortcodes.php b/tests/test-class-activitypub-shortcodes.php index 8637a61..b1413e8 100644 --- a/tests/test-class-activitypub-shortcodes.php +++ b/tests/test-class-activitypub-shortcodes.php @@ -1,6 +1,10 @@ assertEquals( '

hallo

', $content ); + Shortcodes::unregister(); } public function test_password_protected_content() { + Shortcodes::register(); global $post; $post_id = -98; // negative ID, to avoid clash with a valid post @@ -54,5 +60,6 @@ class Test_Activitypub_Shortcodes extends WP_UnitTestCase { wp_reset_postdata(); $this->assertEquals( '', $content ); + Shortcodes::unregister(); } } From a40bd8408a9115307a540741e41f65052ab962f7 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Sat, 21 Oct 2023 11:23:05 +0200 Subject: [PATCH 02/14] Various improvements (#527) * remove unused code * check if `$data['object']` is a sting * do not index application user * this fixes GoToSocial errors * do not cache errors * re-added the fragment See https://github.com/superseriousbusiness/gotosocial/issues/2280 * Fix coding standards * do not verify signature on head request --- includes/collection/class-followers.php | 10 ++-------- includes/functions.php | 13 +++---------- includes/model/class-application-user.php | 4 ++++ includes/rest/class-inbox.php | 2 +- includes/rest/class-server.php | 4 ++++ 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index a9fe298..c2ad01f 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -173,8 +173,6 @@ class Followers { return new WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) ); } - $error = null; - $follower = new Follower(); $follower->from_array( $meta ); @@ -184,14 +182,10 @@ class Followers { return $id; } - $meta = get_post_meta( $id, 'activitypub_user_id' ); - - if ( $error ) { - self::add_error( $id, $error ); - } + $post_meta = get_post_meta( $id, 'activitypub_user_id' ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict - if ( is_array( $meta ) && ! in_array( $user_id, $meta ) ) { + if ( is_array( $post_meta ) && ! in_array( $user_id, $post_meta ) ) { add_post_meta( $id, 'activitypub_user_id', $user_id ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); } diff --git a/includes/functions.php b/includes/functions.php index 883cd3f..99b433f 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -74,32 +74,25 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) { if ( ! \wp_http_validate_url( $actor ) ) { $metadata = new WP_Error( 'activitypub_no_valid_actor_url', \__( 'The "actor" is no valid URL', 'activitypub' ), array( 'status' => 400, 'actor' => $actor ) ); - \set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $metadata; } - $short_timeout = function() { - return 10; - }; - add_filter( 'activitypub_remote_get_timeout', $short_timeout ); $response = Http::get( $actor ); - remove_filter( 'activitypub_remote_get_timeout', $short_timeout ); + if ( \is_wp_error( $response ) ) { - \set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $response; } $metadata = \wp_remote_retrieve_body( $response ); $metadata = \json_decode( $metadata, true ); - \set_transient( $transient_key, $metadata, WEEK_IN_SECONDS ); - if ( ! $metadata ) { $metadata = new WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), array( 'status' => 400, 'actor' => $actor ) ); - \set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period. return $metadata; } + \set_transient( $transient_key, $metadata, WEEK_IN_SECONDS ); + return $metadata; } diff --git a/includes/model/class-application-user.php b/includes/model/class-application-user.php index 1cfcec0..ae8b641 100644 --- a/includes/model/class-application-user.php +++ b/includes/model/class-application-user.php @@ -69,4 +69,8 @@ class Application_User extends Blog_User { public function get_moderators() { return null; } + + public function get_indexable() { + return false; + } } diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 5d65b9b..9c5961a 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -416,7 +416,7 @@ class Inbox { $recipient_items = array_merge( $recipient_items, $recipient ); } - if ( array_key_exists( $i, $data['object'] ) ) { + if ( is_array( $data['object'] ) && array_key_exists( $i, $data['object'] ) ) { if ( is_array( $data['object'][ $i ] ) ) { $recipient = $data['object'][ $i ]; } else { diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index e1a1037..19d35b2 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -74,6 +74,10 @@ class Server { * @return mixed|WP_Error The response, error, or modified response. */ public static function authorize_activitypub_requests( $response, $handler, $request ) { + if ( 'HEAD' === $request->get_method() ) { + return $response; + } + $route = $request->get_route(); // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints From 247899312acf5e88d920062eb3ee0c526c5966dc Mon Sep 17 00:00:00 2001 From: Chaitanya110703 <116812461+Chaitanya110703@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:56:17 +0530 Subject: [PATCH 03/14] doc(README): remove typo (#528) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93f3e85..f1c45e0 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Use shortcodes instead of custom templates, to setup the Activity Post-Content ([#250](https://github.com/pfefferle/wordpress-activitypub/pull/250)) props [@toolstack](https://github.com/toolstack) * Remove custom REST Server, because the needed changes are now merged into Core. * Fix hashtags ([#261](https://github.com/pfefferle/wordpress-activitypub/pull/261)) props [@akirk](https://github.com/akirk) -* Change priorites, to maybe fix the hashtag issue +* Change priorities, to maybe fix the hashtag issue ### 0.15.0 ### From 0ab8df539e6b300efd2956ab0bc8e6b28ec62b63 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 08:28:25 +0200 Subject: [PATCH 04/14] simplify check --- includes/rest/class-server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index 19d35b2..a8706ea 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -90,12 +90,12 @@ class Server { } // POST-Requets are always signed - if ( 'get' !== \strtolower( $request->get_method() ) ) { + if ( 'GET' !== $request->get_method() ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { return $verified_request; } - } elseif ( 'get' === \strtolower( $request->get_method() ) ) { // GET-Requests are only signed in secure mode + } elseif ( 'GET' === $request->get_method() ) { // GET-Requests are only signed in secure mode if ( ACTIVITYPUB_AUTHORIZED_FETCH ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { From acc632f05c04279ca45090a17f8b58ea4dba8a4f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 09:03:15 +0200 Subject: [PATCH 05/14] prepare v1.0.8 --- README.md | 13 +++++++++++-- activitypub.php | 2 +- readme.txt | 11 ++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f1c45e0..f320c00 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Tags:** OStatus, fediverse, activitypub, activitystream **Requires at least:** 4.7 **Tested up to:** 6.3 -**Stable tag:** 1.0.7 +**Stable tag:** 1.0.8 **Requires PHP:** 5.6 **License:** MIT **License URI:** http://opensource.org/licenses/MIT @@ -105,6 +105,15 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). +### 1.0.8 ### + +* Fixed: blocking of HEAD requests +* Fixed: PHP fatal error +* Fixed: several typos +* Improved: loading of shortcodes +* Updated: caching of followers +* Updated: Application-User is no longer "indexable" + ### 1.0.7 ### * Fixed: broken function call @@ -210,7 +219,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Use shortcodes instead of custom templates, to setup the Activity Post-Content ([#250](https://github.com/pfefferle/wordpress-activitypub/pull/250)) props [@toolstack](https://github.com/toolstack) * Remove custom REST Server, because the needed changes are now merged into Core. * Fix hashtags ([#261](https://github.com/pfefferle/wordpress-activitypub/pull/261)) props [@akirk](https://github.com/akirk) -* Change priorities, to maybe fix the hashtag issue +* Change priorites, to maybe fix the hashtag issue ### 0.15.0 ### diff --git a/activitypub.php b/activitypub.php index f232c57..62556b4 100644 --- a/activitypub.php +++ b/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 1.0.7 + * Version: 1.0.8 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT diff --git a/readme.txt b/readme.txt index 03e9b39..5cc8a6f 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nur Tags: OStatus, fediverse, activitypub, activitystream Requires at least: 4.7 Tested up to: 6.3 -Stable tag: 1.0.7 +Stable tag: 1.0.8 Requires PHP: 5.6 License: MIT License URI: http://opensource.org/licenses/MIT @@ -105,6 +105,15 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). += 1.0.8 = + +* Fixed: blocking of HEAD requests +* Fixed: PHP fatal error +* Fixed: several typos +* Improved: loading of shortcodes +* Updated: caching of followers +* Updated: Application-User is no longer "indexable" + = 1.0.7 = * Fixed: broken function call From b55c5d1666df6ad38ebb1e99da1643c5ce1325bb Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 14:54:40 +0200 Subject: [PATCH 06/14] use 401 instead of 403 --- includes/class-signature.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index 1789dd6..0b102b3 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -259,7 +259,7 @@ class Signature { } if ( ! isset( $headers['signature'] ) ) { - return new WP_Error( 'activitypub_signature', __( 'Request not signed', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Request not signed', 'activitypub' ), array( 'status' => 401 ) ); } if ( array_key_exists( 'signature', $headers ) ) { @@ -269,7 +269,7 @@ class Signature { } if ( ! isset( $signature_block ) || ! $signature_block ) { - return new WP_Error( 'activitypub_signature', __( 'Incompatible request signature. keyId and signature are required', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Incompatible request signature. keyId and signature are required', 'activitypub' ), array( 'status' => 401 ) ); } $signed_headers = $signature_block['headers']; @@ -279,12 +279,12 @@ class Signature { $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); if ( ! $signed_data ) { - return new WP_Error( 'activitypub_signature', __( 'Signed request date outside acceptable time window', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Signed request date outside acceptable time window', 'activitypub' ), array( 'status' => 401 ) ); } $algorithm = self::get_signature_algorithm( $signature_block ); if ( ! $algorithm ) { - return new WP_Error( 'activitypub_signature', __( 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', 'activitypub' ), array( 'status' => 401 ) ); } if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) { @@ -300,7 +300,7 @@ class Signature { } if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore - return new WP_Error( 'activitypub_signature', __( 'Invalid Digest header', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Invalid Digest header', 'activitypub' ), array( 'status' => 401 ) ); } } @@ -313,7 +313,7 @@ class Signature { $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; if ( ! $verified ) { - return new WP_Error( 'activitypub_signature', __( 'Invalid signature', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_signature', __( 'Invalid signature', 'activitypub' ), array( 'status' => 401 ) ); } return $verified; } @@ -333,7 +333,7 @@ class Signature { if ( isset( $actor['publicKey']['publicKeyPem'] ) ) { return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore } - return new WP_Error( 'activitypub_no_remote_key_found', __( 'No Public-Key found', 'activitypub' ), array( 'status' => 403 ) ); + return new WP_Error( 'activitypub_no_remote_key_found', __( 'No Public-Key found', 'activitypub' ), array( 'status' => 401 ) ); } /** From 9ac5d84f5c72dca6b1f32fe6497497ecc5960e4e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 14:56:37 +0200 Subject: [PATCH 07/14] updated readme --- README.md | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index f320c00..1f0c6f2 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Fixed: blocking of HEAD requests * Fixed: PHP fatal error * Fixed: several typos +* Fixed: error codes * Improved: loading of shortcodes * Updated: caching of followers * Updated: Application-User is no longer "indexable" diff --git a/readme.txt b/readme.txt index 5cc8a6f..9b0b1b9 100644 --- a/readme.txt +++ b/readme.txt @@ -110,6 +110,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Fixed: blocking of HEAD requests * Fixed: PHP fatal error * Fixed: several typos +* Fixed: error codes * Improved: loading of shortcodes * Updated: caching of followers * Updated: Application-User is no longer "indexable" From b946ef3de1a211dc792bed6808091b78086f83a0 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 14:57:58 +0200 Subject: [PATCH 08/14] more consistent use of response content type (#529) * more consistent use of response content type * update readme * fix typo --- README.md | 1 + includes/rest/class-collection.php | 15 ++++++++++++--- includes/rest/class-followers.php | 6 +++--- includes/rest/class-following.php | 6 +++--- includes/rest/class-inbox.php | 17 +++++++++++------ includes/rest/class-outbox.php | 7 +++---- includes/rest/class-server.php | 7 +++---- includes/rest/class-users.php | 6 +++--- readme.txt | 1 + 9 files changed, 40 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 1f0c6f2..18b5742 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Improved: loading of shortcodes * Updated: caching of followers * Updated: Application-User is no longer "indexable" +* Updated: more consistent usage of the `application/activity+json` Content-Type ### 1.0.7 ### diff --git a/includes/rest/class-collection.php b/includes/rest/class-collection.php index 5fa585d..2e6522e 100644 --- a/includes/rest/class-collection.php +++ b/includes/rest/class-collection.php @@ -117,7 +117,10 @@ class Collection { ); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -168,7 +171,10 @@ class Collection { $response['orderedItems'][] = Post::transform( $post )->to_object()->to_array(); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -192,7 +198,10 @@ class Collection { $response['orderedItems'][] = $user->get_url(); } - return new WP_REST_Response( $response, 200 ); + $rest_response = new WP_REST_Response( $response, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** diff --git a/includes/rest/class-followers.php b/includes/rest/class-followers.php index b7be9d0..71e4840 100644 --- a/includes/rest/class-followers.php +++ b/includes/rest/class-followers.php @@ -103,10 +103,10 @@ class Followers { $data['followers'] ); - $response = new WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $rest_response; } /** diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 33bdf65..b591ab6 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -79,10 +79,10 @@ class Following { $json->first = $json->partOf; // phpcs:ignore - $response = new \WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $rest_response; } /** diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 9c5961a..747290e 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -109,11 +109,10 @@ class Inbox { */ \do_action( 'activitypub_inbox_post' ); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** @@ -138,7 +137,10 @@ class Inbox { \do_action( 'activitypub_inbox', $data, $user->get__id(), $type ); \do_action( "activitypub_inbox_{$type}", $data, $user->get__id() ); - return new WP_REST_Response( array(), 202 ); + $rest_response = new WP_REST_Response( array(), 202 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** @@ -183,7 +185,10 @@ class Inbox { \do_action( "activitypub_inbox_{$type}", $data, $user->ID ); } - return new WP_REST_Response( array(), 202 ); + $rest_response = new WP_REST_Response( array(), 202 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); + + return $rest_response; } /** diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index eb35e86..d640d17 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -123,11 +123,10 @@ class Outbox { */ \do_action( 'activitypub_outbox_post' ); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index a8706ea..8239168 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -54,11 +54,10 @@ class Server { $json = $user->to_array(); - $response = new WP_REST_Response( $json, 200 ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - $response->header( 'Content-Type', 'application/activity+json' ); - - return $response; + return $rest_response; } /** diff --git a/includes/rest/class-users.php b/includes/rest/class-users.php index 31a92dd..9fb10ba 100644 --- a/includes/rest/class-users.php +++ b/includes/rest/class-users.php @@ -95,10 +95,10 @@ class Users { $json = $user->to_array(); - $response = new WP_REST_Response( $json, 200 ); - $response->header( 'Content-Type', 'application/activity+json' ); + $rest_response = new WP_REST_Response( $json, 200 ); + $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) ); - return $response; + return $rest_response; } diff --git a/readme.txt b/readme.txt index 9b0b1b9..848a213 100644 --- a/readme.txt +++ b/readme.txt @@ -114,6 +114,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Improved: loading of shortcodes * Updated: caching of followers * Updated: Application-User is no longer "indexable" +* Updated: more consistent usage of the `application/activity+json` Content-Type = 1.0.7 = From 4d7c0594cdadf294e56e739c22423b356192d61e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 16:16:24 +0200 Subject: [PATCH 09/14] remove featured tags endpoint --- includes/model/class-application-user.php | 4 ---- includes/model/class-user.php | 18 ------------------ 2 files changed, 22 deletions(-) diff --git a/includes/model/class-application-user.php b/includes/model/class-application-user.php index ae8b641..cf4d9cc 100644 --- a/includes/model/class-application-user.php +++ b/includes/model/class-application-user.php @@ -58,10 +58,6 @@ class Application_User extends Blog_User { return null; } - public function get_featured_tags() { - return null; - } - public function get_featured() { return null; } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index f62772d..95c83d7 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -18,15 +18,6 @@ class User extends Actor { */ protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore - /** - * The Featured-Tags. - * - * @see https://docs.joinmastodon.org/spec/activitypub/#featuredTags - * - * @var string - */ - protected $featured_tags; - /** * The Featured-Posts. * @@ -235,15 +226,6 @@ class User extends Actor { return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) ); } - /** - * Returns the Featured-Tags-API-Endpoint. - * - * @return string The Featured-Tags-Endpoint. - */ - public function get_featured_tags() { - return get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $this->get__id() ) ); - } - /** * Extend the User-Output with Attachments. * From 2664ae807ca149122e4b4c270d817f5061fad8d4 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 23 Oct 2023 16:18:28 +0200 Subject: [PATCH 10/14] update readme --- README.md | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 18b5742..40c5398 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Updated: caching of followers * Updated: Application-User is no longer "indexable" * Updated: more consistent usage of the `application/activity+json` Content-Type +* Removed: featured tags endpoint ### 1.0.7 ### diff --git a/readme.txt b/readme.txt index 848a213..b04a987 100644 --- a/readme.txt +++ b/readme.txt @@ -115,6 +115,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu * Updated: caching of followers * Updated: Application-User is no longer "indexable" * Updated: more consistent usage of the `application/activity+json` Content-Type +* Removed: featured tags endpoint = 1.0.7 = From e91334e4d7c0d32bdaa49f965c9c3eb3a1e03435 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 24 Oct 2023 12:45:46 +0200 Subject: [PATCH 11/14] fix following endpoint (#531) * fix following endpoint * version bump --- README.md | 6 +++++- activitypub.php | 2 +- includes/rest/class-following.php | 1 + readme.txt | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40c5398..8156292 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Tags:** OStatus, fediverse, activitypub, activitystream **Requires at least:** 4.7 **Tested up to:** 6.3 -**Stable tag:** 1.0.8 +**Stable tag:** 1.0.9 **Requires PHP:** 5.6 **License:** MIT **License URI:** http://opensource.org/licenses/MIT @@ -105,6 +105,10 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). +### 1.0.9 ### + +* Fixed: broken following endpoint + ### 1.0.8 ### * Fixed: blocking of HEAD requests diff --git a/activitypub.php b/activitypub.php index 62556b4..91786e5 100644 --- a/activitypub.php +++ b/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 1.0.8 + * Version: 1.0.9 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index b591ab6..22c9d46 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -1,6 +1,7 @@ Date: Tue, 24 Oct 2023 13:00:22 +0200 Subject: [PATCH 12/14] improve error messages and codes (#532) * improve error messages and codes * version bump --- README.md | 6 +++++- activitypub.php | 2 +- includes/class-signature.php | 2 +- readme.txt | 6 +++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8156292..dcc91f2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Tags:** OStatus, fediverse, activitypub, activitystream **Requires at least:** 4.7 **Tested up to:** 6.3 -**Stable tag:** 1.0.9 +**Stable tag:** 1.0.10 **Requires PHP:** 5.6 **License:** MIT **License URI:** http://opensource.org/licenses/MIT @@ -105,6 +105,10 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). +### 1.0.10 ### + +* Improved: better error messages if remote profile is not accessible + ### 1.0.9 ### * Fixed: broken following endpoint diff --git a/activitypub.php b/activitypub.php index 91786e5..97f6067 100644 --- a/activitypub.php +++ b/activitypub.php @@ -3,7 +3,7 @@ * Plugin Name: ActivityPub * Plugin URI: https://github.com/pfefferle/wordpress-activitypub/ * Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format. - * Version: 1.0.9 + * Version: 1.0.10 * Author: Matthias Pfefferle & Automattic * Author URI: https://automattic.com/ * License: MIT diff --git a/includes/class-signature.php b/includes/class-signature.php index 0b102b3..e66aa4b 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -328,7 +328,7 @@ class Signature { public static function get_remote_key( $key_id ) { // phpcs:ignore $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore if ( \is_wp_error( $actor ) ) { - return $actor; + return new WP_Error( 'activitypub_no_remote_profile_found', __( 'No Profile found or Profile not accessible', 'activitypub' ), array( 'status' => 401 ) ); } if ( isset( $actor['publicKey']['publicKeyPem'] ) ) { return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore diff --git a/readme.txt b/readme.txt index bb562c6..7e48fc1 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nur Tags: OStatus, fediverse, activitypub, activitystream Requires at least: 4.7 Tested up to: 6.3 -Stable tag: 1.0.9 +Stable tag: 1.0.10 Requires PHP: 5.6 License: MIT License URI: http://opensource.org/licenses/MIT @@ -105,6 +105,10 @@ Where 'blog' is the path to the subdirectory at which your blog resides. Project maintained on GitHub at [automattic/wordpress-activitypub](https://github.com/automattic/wordpress-activitypub). += 1.0.10 = + +* Improved: better error messages if remote profile is not accessible + = 1.0.9 = * Fixed: broken following endpoint From 8078512b8cd1d7ba1d3a7565b3fd9a9e04baac21 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 24 Oct 2023 14:54:03 +0200 Subject: [PATCH 13/14] small improvements --- includes/class-signature.php | 17 +++++++++++++---- includes/functions.php | 2 +- includes/rest/class-inbox.php | 2 +- includes/rest/class-server.php | 5 +++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/includes/class-signature.php b/includes/class-signature.php index e66aa4b..d021cf0 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -4,6 +4,7 @@ namespace Activitypub; use WP_Error; use DateTime; use DateTimeZone; +use WP_REST_Request; use Activitypub\Collection\Users; /** @@ -226,7 +227,7 @@ class Signature { /** * Verifies the http signatures * - * @param WP_REQUEST|array $request The request object or $_SERVER array. + * @param WP_REST_Request|array $request The request object or $_SERVER array. * * @return mixed A boolean or WP_Error. */ @@ -323,17 +324,25 @@ class Signature { * * @param string $key_id The URL to the public key. * - * @return WP_Error|string The public key. + * @return WP_Error|string The public key or WP_Error. */ public static function get_remote_key( $key_id ) { // phpcs:ignore $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore if ( \is_wp_error( $actor ) ) { - return new WP_Error( 'activitypub_no_remote_profile_found', __( 'No Profile found or Profile not accessible', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_no_remote_profile_found', + __( 'No Profile found or Profile not accessible', 'activitypub' ), + array( 'status' => 401 ) + ); } if ( isset( $actor['publicKey']['publicKeyPem'] ) ) { return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore } - return new WP_Error( 'activitypub_no_remote_key_found', __( 'No Public-Key found', 'activitypub' ), array( 'status' => 401 ) ); + return new WP_Error( + 'activitypub_no_remote_key_found', + __( 'No Public-Key found', 'activitypub' ), + array( 'status' => 401 ) + ); } /** diff --git a/includes/functions.php b/includes/functions.php index 99b433f..b2972c0 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -42,7 +42,7 @@ function get_webfinger_resource( $user_id ) { * @param string $actor The Actor URL. * @param bool $cached If the result should be cached. * - * @return array The Actor profile as array + * @return array|WP_Error The Actor profile as array or WP_Error on failure. */ function get_remote_metadata_by_actor( $actor, $cached = true ) { $pre = apply_filters( 'pre_get_remote_metadata_by_actor', false, $actor ); diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index 747290e..9088993 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -160,7 +160,7 @@ class Inbox { 'rest_invalid_param', \__( 'No recipients found', 'activitypub' ), array( - 'status' => 404, + 'status' => 400, 'params' => array( 'to' => \__( 'Please check/validate "to" field', 'activitypub' ), 'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ), diff --git a/includes/rest/class-server.php b/includes/rest/class-server.php index 8239168..0e6e4cc 100644 --- a/includes/rest/class-server.php +++ b/includes/rest/class-server.php @@ -2,6 +2,7 @@ namespace Activitypub\Rest; use stdClass; +use WP_Error; use WP_REST_Response; use Activitypub\Signature; use Activitypub\Model\Application_User; @@ -92,13 +93,13 @@ class Server { if ( 'GET' !== $request->get_method() ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { - return $verified_request; + return new WP_Error( 'activitypub_signature_verification', $verified_request->get_error_message(), array( 'status' => 401 ) ); } } elseif ( 'GET' === $request->get_method() ) { // GET-Requests are only signed in secure mode if ( ACTIVITYPUB_AUTHORIZED_FETCH ) { $verified_request = Signature::verify_http_signature( $request ); if ( \is_wp_error( $verified_request ) ) { - return $verified_request; + return new WP_Error( 'activitypub_signature_verification', $verified_request->get_error_message(), array( 'status' => 401 ) ); } } } From 53adfe6b80b372ae907e85566907cabd4b3fe478 Mon Sep 17 00:00:00 2001 From: Matt Wiebe Date: Wed, 25 Oct 2023 01:44:04 -0500 Subject: [PATCH 14/14] PHP 8.1 compatibility (#533) * PHP 8.1 compatibility * Update compat.php --------- Co-authored-by: Matthias Pfefferle --- includes/compat.php | 12 ++++++++++++ includes/rest/class-collection.php | 4 ++-- includes/rest/class-following.php | 2 +- includes/rest/class-nodeinfo.php | 4 ++-- includes/transformer/class-post.php | 2 ++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/includes/compat.php b/includes/compat.php index 4bee640..3dd405c 100644 --- a/includes/compat.php +++ b/includes/compat.php @@ -35,3 +35,15 @@ if ( ! function_exists( 'get_self_link' ) ) { return esc_url( apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . $path ) ) ); } } + +if ( ! function_exists( 'is_countable' ) ) { + /** + * Polyfill for `is_countable()` function added in PHP 7.3. + * + * @param mixed $value The value to check. + * @return bool True if `$value` is countable, otherwise false. + */ + function is_countable( $value ) { + return is_array( $value ) || $value instanceof \Countable; + } +} diff --git a/includes/rest/class-collection.php b/includes/rest/class-collection.php index 2e6522e..365641c 100644 --- a/includes/rest/class-collection.php +++ b/includes/rest/class-collection.php @@ -105,7 +105,7 @@ class Collection { '@context' => Activity::CONTEXT, 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ), 'type' => 'Collection', - 'totalItems' => count( $tags ), + 'totalItems' => is_countable( $tags ) ? count( $tags ) : 0, 'items' => array(), ); @@ -163,7 +163,7 @@ class Collection { '@context' => Activity::CONTEXT, 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ), 'type' => 'OrderedCollection', - 'totalItems' => count( $posts ), + 'totalItems' => is_countable( $posts ) ? count( $posts ) : 0, 'orderedItems' => array(), ); diff --git a/includes/rest/class-following.php b/includes/rest/class-following.php index 22c9d46..58e4375 100644 --- a/includes/rest/class-following.php +++ b/includes/rest/class-following.php @@ -75,7 +75,7 @@ class Following { $items = apply_filters( 'activitypub_rest_following', array(), $user ); // phpcs:ignore - $json->totalItems = count( $items ); // phpcs:ignore + $json->totalItems = is_countable( $items ) ? count( $items ) : 0; // phpcs:ignore $json->orderedItems = $items; // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore diff --git a/includes/rest/class-nodeinfo.php b/includes/rest/class-nodeinfo.php index 1f6277a..4829e75 100644 --- a/includes/rest/class-nodeinfo.php +++ b/includes/rest/class-nodeinfo.php @@ -88,7 +88,7 @@ class Nodeinfo { ) ); - if ( is_array( $users ) ) { + if ( is_countable( $users ) ) { $users = count( $users ); } else { $users = 1; @@ -145,7 +145,7 @@ class Nodeinfo { ) ); - if ( is_array( $users ) ) { + if ( is_countable( $users ) ) { $users = count( $users ); } else { $users = 1; diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 9250afe..36d357e 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -335,6 +335,8 @@ class Post { return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) ); } + // Default to Article. + $object_type = 'Article'; $post_type = \get_post_type( $this->wp_post ); switch ( $post_type ) { case 'post':