Merge branch 'master' into Comments

This commit is contained in:
Django Doucet 2023-10-27 07:28:21 -06:00
commit 8a0e02b39f
20 changed files with 167 additions and 99 deletions

View file

@ -3,7 +3,7 @@
**Tags:** OStatus, fediverse, activitypub, activitystream **Tags:** OStatus, fediverse, activitypub, activitystream
**Requires at least:** 4.7 **Requires at least:** 4.7
**Tested up to:** 6.3 **Tested up to:** 6.3
**Stable tag:** 1.0.7 **Stable tag:** 1.0.10
**Requires PHP:** 5.6 **Requires PHP:** 5.6
**License:** MIT **License:** MIT
**License URI:** http://opensource.org/licenses/MIT **License URI:** http://opensource.org/licenses/MIT
@ -105,6 +105,26 @@ 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). 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
### 1.0.8 ###
* 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"
* Updated: more consistent usage of the `application/activity+json` Content-Type
* Removed: featured tags endpoint
### 1.0.7 ### ### 1.0.7 ###
* Fixed: broken function call * Fixed: broken function call

View file

@ -3,7 +3,7 @@
* Plugin Name: ActivityPub * Plugin Name: ActivityPub
* Plugin URI: https://github.com/pfefferle/wordpress-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. * 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.10
* Author: Matthias Pfefferle & Automattic * Author: Matthias Pfefferle & Automattic
* Author URI: https://automattic.com/ * Author URI: https://automattic.com/
* License: MIT * License: MIT
@ -69,7 +69,6 @@ function plugin_init() {
\add_action( 'init', array( __NAMESPACE__ . '\Collection\Followers', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Collection\Followers', 'init' ) );
\add_action( 'init', array( __NAMESPACE__ . '\Admin', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Admin', 'init' ) );
\add_action( 'init', array( __NAMESPACE__ . '\Hashtag', '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__ . '\Mention', 'init' ) );
\add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Health_Check', 'init' ) );
\add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) ); \add_action( 'init', array( __NAMESPACE__ . '\Scheduler', 'init' ) );

View file

@ -5,14 +5,9 @@ use function Activitypub\esc_hashtag;
class Shortcodes { class Shortcodes {
/** /**
* Class constructor, registering WordPress then Shortcodes * Register the shortcodes
*/ */
public static function init() { public static function register() {
// do not load on admin pages
if ( is_admin() ) {
return;
}
foreach ( get_class_methods( self::class ) as $shortcode ) { foreach ( get_class_methods( self::class ) as $shortcode ) {
if ( 'init' !== $shortcode ) { if ( 'init' !== $shortcode ) {
add_shortcode( 'ap_' . $shortcode, array( self::class, $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 * Generates output for the 'ap_hashtags' shortcode
* *

View file

@ -4,6 +4,7 @@ namespace Activitypub;
use WP_Error; use WP_Error;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
use WP_REST_Request;
use Activitypub\Collection\Users; use Activitypub\Collection\Users;
/** /**
@ -226,7 +227,7 @@ class Signature {
/** /**
* Verifies the http signatures * 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. * @return mixed A boolean or WP_Error.
*/ */
@ -259,7 +260,7 @@ class Signature {
} }
if ( ! isset( $headers['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 ) ) { if ( array_key_exists( 'signature', $headers ) ) {
@ -269,7 +270,7 @@ class Signature {
} }
if ( ! isset( $signature_block ) || ! $signature_block ) { 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']; $signed_headers = $signature_block['headers'];
@ -279,12 +280,12 @@ class Signature {
$signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers );
if ( ! $signed_data ) { 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 ); $algorithm = self::get_signature_algorithm( $signature_block );
if ( ! $algorithm ) { 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 ) ) { if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) {
@ -300,7 +301,7 @@ class Signature {
} }
if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore 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 +314,7 @@ class Signature {
$verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; $verified = \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0;
if ( ! $verified ) { 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; return $verified;
} }
@ -323,17 +324,25 @@ class Signature {
* *
* @param string $key_id The URL to the public key. * @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 public static function get_remote_key( $key_id ) { // phpcs:ignore
$actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore $actor = get_remote_metadata_by_actor( strip_fragment_from_url( $key_id ) ); // phpcs:ignore
if ( \is_wp_error( $actor ) ) { 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'] ) ) { if ( isset( $actor['publicKey']['publicKeyPem'] ) ) {
return \rtrim( $actor['publicKey']['publicKeyPem'] ); // phpcs:ignore 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 )
);
} }
/** /**

View file

@ -173,8 +173,6 @@ class Followers {
return new WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) ); return new WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) );
} }
$error = null;
$follower = new Follower(); $follower = new Follower();
$follower->from_array( $meta ); $follower->from_array( $meta );
@ -184,14 +182,10 @@ class Followers {
return $id; return $id;
} }
$meta = get_post_meta( $id, 'activitypub_user_id' ); $post_meta = get_post_meta( $id, 'activitypub_user_id' );
if ( $error ) {
self::add_error( $id, $error );
}
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict // 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 ); add_post_meta( $id, 'activitypub_user_id', $user_id );
wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
} }

View file

@ -35,3 +35,15 @@ if ( ! function_exists( 'get_self_link' ) ) {
return esc_url( apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . $path ) ) ); 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;
}
}

View file

@ -42,7 +42,7 @@ function get_webfinger_resource( $user_id ) {
* @param string $actor The Actor URL. * @param string $actor The Actor URL.
* @param bool $cached If the result should be cached. * @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 ) { function get_remote_metadata_by_actor( $actor, $cached = true ) {
$pre = apply_filters( 'pre_get_remote_metadata_by_actor', false, $actor ); $pre = apply_filters( 'pre_get_remote_metadata_by_actor', false, $actor );
@ -74,32 +74,25 @@ function get_remote_metadata_by_actor( $actor, $cached = true ) {
if ( ! \wp_http_validate_url( $actor ) ) { 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 ) ); $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; return $metadata;
} }
$short_timeout = function() {
return 10;
};
add_filter( 'activitypub_remote_get_timeout', $short_timeout );
$response = Http::get( $actor ); $response = Http::get( $actor );
remove_filter( 'activitypub_remote_get_timeout', $short_timeout );
if ( \is_wp_error( $response ) ) { if ( \is_wp_error( $response ) ) {
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $response; return $response;
} }
$metadata = \wp_remote_retrieve_body( $response ); $metadata = \wp_remote_retrieve_body( $response );
$metadata = \json_decode( $metadata, true ); $metadata = \json_decode( $metadata, true );
\set_transient( $transient_key, $metadata, WEEK_IN_SECONDS );
if ( ! $metadata ) { if ( ! $metadata ) {
$metadata = new WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), array( 'status' => 400, 'actor' => $actor ) ); $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; return $metadata;
} }
\set_transient( $transient_key, $metadata, WEEK_IN_SECONDS );
return $metadata; return $metadata;
} }

View file

@ -58,10 +58,6 @@ class Application_User extends Blog_User {
return null; return null;
} }
public function get_featured_tags() {
return null;
}
public function get_featured() { public function get_featured() {
return null; return null;
} }
@ -69,4 +65,8 @@ class Application_User extends Blog_User {
public function get_moderators() { public function get_moderators() {
return null; return null;
} }
public function get_indexable() {
return false;
}
} }

View file

@ -18,15 +18,6 @@ class User extends Actor {
*/ */
protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore 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. * 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() ) ); 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. * Extend the User-Output with Attachments.
* *

View file

@ -105,7 +105,7 @@ class Collection {
'@context' => Activity::CONTEXT, '@context' => Activity::CONTEXT,
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ), 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ),
'type' => 'Collection', 'type' => 'Collection',
'totalItems' => count( $tags ), 'totalItems' => is_countable( $tags ) ? count( $tags ) : 0,
'items' => array(), 'items' => array(),
); );
@ -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;
} }
/** /**
@ -160,7 +163,7 @@ class Collection {
'@context' => Activity::CONTEXT, '@context' => Activity::CONTEXT,
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ), 'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ),
'type' => 'OrderedCollection', 'type' => 'OrderedCollection',
'totalItems' => count( $posts ), 'totalItems' => is_countable( $posts ) ? count( $posts ) : 0,
'orderedItems' => array(), 'orderedItems' => array(),
); );
@ -168,7 +171,10 @@ class Collection {
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array(); $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(); $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;
} }
/** /**

View file

@ -103,10 +103,10 @@ class Followers {
$data['followers'] $data['followers']
); );
$response = new WP_REST_Response( $json, 200 ); $rest_response = new WP_REST_Response( $json, 200 );
$response->header( 'Content-Type', 'application/activity+json' ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
return $response; return $rest_response;
} }
/** /**

View file

@ -1,6 +1,7 @@
<?php <?php
namespace Activitypub\Rest; namespace Activitypub\Rest;
use WP_REST_Response;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
use function Activitypub\is_single_user; use function Activitypub\is_single_user;
@ -74,15 +75,15 @@ class Following {
$items = apply_filters( 'activitypub_rest_following', array(), $user ); // phpcs:ignore $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->orderedItems = $items; // phpcs:ignore
$json->first = $json->partOf; // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore
$response = new \WP_REST_Response( $json, 200 ); $rest_response = new WP_REST_Response( $json, 200 );
$response->header( 'Content-Type', 'application/activity+json' ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
return $response; return $rest_response;
} }
/** /**

View file

@ -109,11 +109,10 @@ class Inbox {
*/ */
\do_action( 'activitypub_inbox_post' ); \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 $rest_response;
return $response;
} }
/** /**
@ -138,7 +137,10 @@ class Inbox {
\do_action( 'activitypub_inbox', $data, $user->get__id(), $type ); \do_action( 'activitypub_inbox', $data, $user->get__id(), $type );
\do_action( "activitypub_inbox_{$type}", $data, $user->get__id() ); \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;
} }
/** /**
@ -158,7 +160,7 @@ class Inbox {
'rest_invalid_param', 'rest_invalid_param',
\__( 'No recipients found', 'activitypub' ), \__( 'No recipients found', 'activitypub' ),
array( array(
'status' => 404, 'status' => 400,
'params' => array( 'params' => array(
'to' => \__( 'Please check/validate "to" field', 'activitypub' ), 'to' => \__( 'Please check/validate "to" field', 'activitypub' ),
'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ), 'bto' => \__( 'Please check/validate "bto" field', 'activitypub' ),
@ -183,7 +185,10 @@ class Inbox {
\do_action( "activitypub_inbox_{$type}", $data, $user->ID ); \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;
} }
/** /**
@ -523,7 +528,7 @@ class Inbox {
$recipient_items = array_merge( $recipient_items, $recipient ); $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 ] ) ) { if ( is_array( $data['object'][ $i ] ) ) {
$recipient = $data['object'][ $i ]; $recipient = $data['object'][ $i ];
} else { } else {

View file

@ -88,7 +88,7 @@ class Nodeinfo {
) )
); );
if ( is_array( $users ) ) { if ( is_countable( $users ) ) {
$users = count( $users ); $users = count( $users );
} else { } else {
$users = 1; $users = 1;
@ -145,7 +145,7 @@ class Nodeinfo {
) )
); );
if ( is_array( $users ) ) { if ( is_countable( $users ) ) {
$users = count( $users ); $users = count( $users );
} else { } else {
$users = 1; $users = 1;

View file

@ -123,11 +123,10 @@ class Outbox {
*/ */
\do_action( 'activitypub_outbox_post' ); \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 $rest_response;
return $response;
} }
/** /**

View file

@ -2,6 +2,7 @@
namespace Activitypub\Rest; namespace Activitypub\Rest;
use stdClass; use stdClass;
use WP_Error;
use WP_REST_Response; use WP_REST_Response;
use Activitypub\Signature; use Activitypub\Signature;
use Activitypub\Model\Application_User; use Activitypub\Model\Application_User;
@ -54,11 +55,10 @@ class Server {
$json = $user->to_array(); $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 $rest_response;
return $response;
} }
/** /**
@ -74,6 +74,10 @@ class Server {
* @return mixed|WP_Error The response, error, or modified response. * @return mixed|WP_Error The response, error, or modified response.
*/ */
public static function authorize_activitypub_requests( $response, $handler, $request ) { public static function authorize_activitypub_requests( $response, $handler, $request ) {
if ( 'HEAD' === $request->get_method() ) {
return $response;
}
$route = $request->get_route(); $route = $request->get_route();
// check if it is an activitypub request and exclude webfinger and nodeinfo endpoints // check if it is an activitypub request and exclude webfinger and nodeinfo endpoints
@ -86,16 +90,16 @@ class Server {
} }
// POST-Requets are always signed // POST-Requets are always signed
if ( 'get' !== \strtolower( $request->get_method() ) ) { if ( 'GET' !== $request->get_method() ) {
$verified_request = Signature::verify_http_signature( $request ); $verified_request = Signature::verify_http_signature( $request );
if ( \is_wp_error( $verified_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' === \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 ) { if ( ACTIVITYPUB_AUTHORIZED_FETCH ) {
$verified_request = Signature::verify_http_signature( $request ); $verified_request = Signature::verify_http_signature( $request );
if ( \is_wp_error( $verified_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 ) );
} }
} }
} }

View file

@ -95,10 +95,10 @@ class Users {
$json = $user->to_array(); $json = $user->to_array();
$response = new WP_REST_Response( $json, 200 ); $rest_response = new WP_REST_Response( $json, 200 );
$response->header( 'Content-Type', 'application/activity+json' ); $rest_response->header( 'Content-Type', 'application/activity+json; charset=' . get_option( 'blog_charset' ) );
return $response; return $rest_response;
} }

View file

@ -5,6 +5,7 @@ use WP_Post;
use Activitypub\Collection\Users; use Activitypub\Collection\Users;
use Activitypub\Model\Blog_User; use Activitypub\Model\Blog_User;
use Activitypub\Activity\Base_Object; use Activitypub\Activity\Base_Object;
use Activitypub\Shortcodes;
use function Activitypub\esc_hashtag; use function Activitypub\esc_hashtag;
use function Activitypub\is_single_user; use function Activitypub\is_single_user;
@ -334,6 +335,8 @@ class Post {
return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) ); return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) );
} }
// Default to Article.
$object_type = 'Article';
$post_type = \get_post_type( $this->wp_post ); $post_type = \get_post_type( $this->wp_post );
switch ( $post_type ) { switch ( $post_type ) {
case 'post': case 'post':
@ -466,6 +469,8 @@ class Post {
$post = $this->wp_post; $post = $this->wp_post;
$content = $this->get_post_content_template(); $content = $this->get_post_content_template();
// Register our shortcodes just in time.
Shortcodes::register();
// Fill in the shortcodes. // Fill in the shortcodes.
setup_postdata( $post ); setup_postdata( $post );
$content = do_shortcode( $content ); $content = do_shortcode( $content );
@ -477,6 +482,9 @@ class Post {
$content = \apply_filters( 'activitypub_the_content', $content, $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; return $content;
} }

View file

@ -3,7 +3,7 @@ Contributors: automattic, pfefferle, mediaformat, mattwiebe, akirk, jeherve, nur
Tags: OStatus, fediverse, activitypub, activitystream Tags: OStatus, fediverse, activitypub, activitystream
Requires at least: 4.7 Requires at least: 4.7
Tested up to: 6.3 Tested up to: 6.3
Stable tag: 1.0.7 Stable tag: 1.0.10
Requires PHP: 5.6 Requires PHP: 5.6
License: MIT License: MIT
License URI: http://opensource.org/licenses/MIT License URI: http://opensource.org/licenses/MIT
@ -105,6 +105,26 @@ 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). 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
= 1.0.8 =
* 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"
* Updated: more consistent usage of the `application/activity+json` Content-Type
* Removed: featured tags endpoint
= 1.0.7 = = 1.0.7 =
* Fixed: broken function call * Fixed: broken function call

View file

@ -1,6 +1,10 @@
<?php <?php
use Activitypub\Shortcodes;
class Test_Activitypub_Shortcodes extends WP_UnitTestCase { class Test_Activitypub_Shortcodes extends WP_UnitTestCase {
public function test_content() { public function test_content() {
Shortcodes::register();
global $post; global $post;
$post_id = -99; // negative ID, to avoid clash with a valid post $post_id = -99; // negative ID, to avoid clash with a valid post
@ -26,9 +30,11 @@ class Test_Activitypub_Shortcodes extends WP_UnitTestCase {
wp_reset_postdata(); wp_reset_postdata();
$this->assertEquals( '<p>hallo</p>', $content ); $this->assertEquals( '<p>hallo</p>', $content );
Shortcodes::unregister();
} }
public function test_password_protected_content() { public function test_password_protected_content() {
Shortcodes::register();
global $post; global $post;
$post_id = -98; // negative ID, to avoid clash with a valid 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(); wp_reset_postdata();
$this->assertEquals( '', $content ); $this->assertEquals( '', $content );
Shortcodes::unregister();
} }
} }