diff --git a/activitypub.php b/activitypub.php index b9e84b6..08ff918 100644 --- a/activitypub.php +++ b/activitypub.php @@ -23,15 +23,15 @@ function init() { require_once dirname( __FILE__ ) . '/includes/table/followers-list.php'; require_once dirname( __FILE__ ) . '/includes/class-signature.php'; - require_once dirname( __FILE__ ) . '/includes/class-activity.php'; - require_once dirname( __FILE__ ) . '/includes/db/class-followers.php'; + require_once dirname( __FILE__ ) . '/includes/peer/class-followers.php'; require_once dirname( __FILE__ ) . '/includes/functions.php'; require_once dirname( __FILE__ ) . '/includes/class-activity-dispatcher.php'; \Activitypub\Activity_Dispatcher::init(); - require_once dirname( __FILE__ ) . '/includes/class-post.php'; - \Activitypub\Post::init(); + require_once dirname( __FILE__ ) . '/includes/model/class-activity.php'; + require_once dirname( __FILE__ ) . '/includes/model/class-post.php'; + \Activitypub\Model\Post::init(); require_once dirname( __FILE__ ) . '/includes/class-activitypub.php'; \Activitypub\Activitypub::init(); diff --git a/includes/class-activity-dispatcher.php b/includes/class-activity-dispatcher.php index 6cd7280..7457db8 100644 --- a/includes/class-activity-dispatcher.php +++ b/includes/class-activity-dispatcher.php @@ -15,7 +15,7 @@ class Activity_Dispatcher { public static function init() { \add_action( 'activitypub_send_post_activity', array( '\Activitypub\Activity_Dispatcher', 'send_post_activity' ) ); \add_action( 'activitypub_send_update_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_activity' ) ); - //\add_action( 'activitypub_send_delete_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_activity' ) ); + \add_action( 'activitypub_send_delete_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_activity' ) ); } /** @@ -27,8 +27,8 @@ class Activity_Dispatcher { $post = \get_post( $post_id ); $user_id = $post->post_author; - $activitypub_post = new \Activitypub\Post( $post ); - $activitypub_activity = new \Activitypub\Activity( 'Create', \Activitypub\Activity::TYPE_FULL ); + $activitypub_post = new \Activitypub\Model\Post( $post ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL ); $activitypub_activity->from_post( $activitypub_post->to_array() ); foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) { @@ -48,8 +48,8 @@ class Activity_Dispatcher { $post = \get_post( $post_id ); $user_id = $post->post_author; - $activitypub_post = new \Activitypub\Post( $post ); - $activitypub_activity = new \Activitypub\Activity( 'Update', \Activitypub\Activity::TYPE_FULL ); + $activitypub_post = new \Activitypub\Model\Post( $post ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Update', \Activitypub\Model\Activity::TYPE_FULL ); $activitypub_activity->from_post( $activitypub_post->to_array() ); foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) { @@ -69,8 +69,8 @@ class Activity_Dispatcher { $post = \get_post( $post_id ); $user_id = $post->post_author; - $activitypub_post = new \Activitypub\Post( $post ); - $activitypub_activity = new \Activitypub\Activity( 'Delete', \Activitypub\Activity::TYPE_FULL ); + $activitypub_post = new \Activitypub\Model\Post( $post ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL ); $activitypub_activity->from_post( $activitypub_post->to_array() ); foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) { diff --git a/includes/functions.php b/includes/functions.php index d7a2264..4eeeb33 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -12,28 +12,8 @@ function get_context() { 'https://w3id.org/security/v1', array( 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', - 'sensitive' => 'as:sensitive', - 'movedTo' => array( - '@id' => 'as:movedTo', - '@type' => '@id', - ), - 'Hashtag' => 'as:Hashtag', - 'ostatus' => 'http://ostatus.org#', - 'atomUri' => 'ostatus:atomUri', - 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', - 'conversation' => 'ostatus:conversation', - 'toot' => 'http://joinmastodon.org/ns#', - 'Emoji' => 'toot:Emoji', - 'focalPoint' => array( - '@container' => '@list', - '@id' => 'toot:focalPoint', - ), - 'featured' => array( - '@id' => 'toot:featured', - '@type' => '@id', - ), - 'schema' => 'http://schema.org#', 'PropertyValue' => 'schema:PropertyValue', + 'schema' => 'http://schema.org#', 'value' => 'schema:value', ), ); @@ -185,7 +165,7 @@ function get_publickey_by_actor( $actor, $key_id ) { } function get_follower_inboxes( $user_id ) { - $followers = \Activitypub\Db\Followers::get_followers( $user_id ); + $followers = \Activitypub\Peer\Followers::get_followers( $user_id ); $inboxes = array(); foreach ( $followers as $follower ) { @@ -223,7 +203,7 @@ function get_identifier_settings( $user_id ) { } function get_followers( $user_id ) { - $followers = \Activitypub\Db\Followers::get_followers( $user_id ); + $followers = \Activitypub\Peer\Followers::get_followers( $user_id ); if ( ! $followers ) { return array(); @@ -237,3 +217,51 @@ function count_followers( $user_id ) { return \count( $followers ); } + +/** + * Examine a url and try to determine the author ID it represents. + * + * Checks are supposedly from the hosted site blog. + * + * @param string $url Permalink to check. + * + * @return int User ID, or 0 on failure. + */ +function url_to_authorid( $url ) { + global $wp_rewrite; + + // check if url hase the same host + if ( wp_parse_url( site_url(), PHP_URL_HOST ) !== wp_parse_url( $url, PHP_URL_HOST ) ) { + return 0; + } + + // first, check to see if there is a 'author=N' to match against + if ( preg_match( '/[?&]author=(\d+)/i', $url, $values ) ) { + $id = absint( $values[1] ); + if ( $id ) { + return $id; + } + } + + // check to see if we are using rewrite rules + $rewrite = $wp_rewrite->wp_rewrite_rules(); + + // not using rewrite rules, and 'author=N' method failed, so we're out of options + if ( empty( $rewrite ) ) { + return 0; + } + + // generate rewrite rule for the author url + $author_rewrite = $wp_rewrite->get_author_permastruct(); + $author_regexp = str_replace( '%author%', '', $author_rewrite ); + + // match the rewrite rule with the passed url + if ( preg_match( '/https?:\/\/(.+)' . preg_quote( $author_regexp, '/' ) . '([^\/]+)/i', $url, $match ) ) { + $user = get_user_by( 'slug', $match[2] ); + if ( $user ) { + return $user->ID; + } + } + + return 0; +} diff --git a/includes/class-activity.php b/includes/model/class-activity.php similarity index 98% rename from includes/class-activity.php rename to includes/model/class-activity.php index 4b4df84..0cfc1c6 100644 --- a/includes/class-activity.php +++ b/includes/model/class-activity.php @@ -1,5 +1,5 @@ partOf = \get_rest_url( null, "/activitypub/1.0/users/$user_id/followers" ); // phpcs:ignore $json->totalItems = \Activitypub\count_followers( $user_id ); // phpcs:ignore - $json->orderedItems = \Activitypub\Db\Followers::get_followers( $user_id ); // phpcs:ignore + $json->orderedItems = \Activitypub\Peer\Followers::get_followers( $user_id ); // phpcs:ignore $json->first = $json->partOf; // phpcs:ignore diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index fc1c3be..b9bafba 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -30,7 +30,7 @@ class Inbox { 'activitypub/1.0', '/inbox', array( array( 'methods' => \WP_REST_Server::EDITABLE, - 'callback' => array( '\Activitypub\Rest\Inbox', 'global_inbox' ), + 'callback' => array( '\Activitypub\Rest\Inbox', 'shared_inbox' ), ), ) ); @@ -87,7 +87,6 @@ class Inbox { */ public static function user_inbox( $request ) { $author_id = $request->get_param( 'id' ); - $author = \get_user_by( 'ID', $author_id ); $data = \json_decode( $request->get_body(), true ); @@ -113,9 +112,62 @@ class Inbox { * * @return WP_Error not yet implemented */ - public static function global_inbox( $request ) { - // Create the response object - return new \WP_Error( 'rest_not_implemented', \__( 'This method is not yet implemented', 'activitypub' ), array( 'status' => 501 ) ); + public static function shared_inbox( $request ) { + $data = \json_decode( $request->get_body(), true ); + + if ( empty( $data['to'] ) ) { + return new \WP_Error( 'rest_invalid_data', \__( 'No receiving actor set', 'activitypub' ), array( 'status' => 422 ) ); + } + + if ( \filter_var( $data['to'], \FILTER_VALIDATE_URL ) ) { + $author_id = \Activitypub\url_to_authorid( $data['to'] ); + + if ( ! $author_id ) { + return new \WP_Error( 'rest_invalid_data', \__( 'No matching user', 'activitypub' ), array( 'status' => 422 ) ); + } + } else { + // get the identifier at the left of the '@' + $parts = \explode( '@', $data['to'] ); + + if ( 3 === \count( $parts ) ) { + $username = $parts[1]; + $host = $parts[2]; + } elseif ( 2 === \count( $parts ) ) { + $username = $parts[0]; + $host = $parts[1]; + } + + if ( ! $username || ! $host ) { + return new \WP_Error( 'rest_invalid_data', \__( 'Invalid actor identifier', 'activitypub' ), array( 'status' => 422 ) ); + } + + // check domain + if ( ! \wp_parse_url( \home_url(), \PHP_URL_HOST ) !== $host ) { + return new \WP_Error( 'rest_invalid_data', \__( 'Invalid host', 'activitypub' ), array( 'status' => 422 ) ); + } + + $author = \get_user_by( 'login', $username ); + + if ( ! $author ) { + return new \WP_Error( 'rest_invalid_data', \__( 'No matching user', 'activitypub' ), array( 'status' => 422 ) ); + } + + $author_id = $author->ID; + } + + $type = 'create'; + if ( ! empty( $data['type'] ) ) { + $type = \strtolower( $data['type'] ); + } + + if ( ! \is_array( $data ) || ! \array_key_exists( 'type', $data ) ) { + return new \WP_Error( 'rest_invalid_data', \__( 'Invalid payload', 'activitypub' ), array( 'status' => 422 ) ); + } + + \do_action( 'activitypub_inbox', $data, $author_id, $type ); + \do_action( "activitypub_inbox_{$type}", $data, $author_id ); + + return new \WP_REST_Response( array(), 202 ); } /** @@ -150,13 +202,13 @@ class Inbox { } // save follower - \Activitypub\Db\Followers::add_follower( $object['actor'], $user_id ); + \Activitypub\Peer\Followers::add_follower( $object['actor'], $user_id ); // get inbox $inbox = \Activitypub\get_inbox_by_actor( $object['actor'] ); // send "Accept" activity - $activity = new \Activitypub\Activity( 'Accept', \Activitypub\Activity::TYPE_SIMPLE ); + $activity = new \Activitypub\Model\Activity( 'Accept', \Activitypub\Model\Activity::TYPE_SIMPLE ); $activity->set_object( $object ); $activity->set_actor( \get_author_posts_url( $user_id ) ); $activity->set_to( $object['actor'] ); @@ -178,7 +230,7 @@ class Inbox { return new \WP_Error( 'activitypub_no_actor', \__( 'No "Actor" found', 'activitypub' ) ); } - \Activitypub\Db\Followers::remove_follower( $object['actor'], $user_id ); + \Activitypub\Peer\Followers::remove_follower( $object['actor'], $user_id ); } /** diff --git a/includes/rest/class-outbox.php b/includes/rest/class-outbox.php index 5f1e41c..c7ddbcb 100644 --- a/includes/rest/class-outbox.php +++ b/includes/rest/class-outbox.php @@ -83,8 +83,8 @@ class Outbox { } foreach ( $posts as $post ) { - $activitypub_post = new \Activitypub\Post( $post ); - $activitypub_activity = new \Activitypub\Activity( 'Create', \Activitypub\Activity::TYPE_NONE ); + $activitypub_post = new \Activitypub\Model\Post( $post ); + $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_NONE ); $activitypub_activity->from_post( $activitypub_post->to_array() ); $json->orderedItems[] = $activitypub_activity->to_array(); // phpcs:ignore } diff --git a/includes/table/followers-list.php b/includes/table/followers-list.php index 6c60104..965cb90 100644 --- a/includes/table/followers-list.php +++ b/includes/table/followers-list.php @@ -25,7 +25,7 @@ class Followers_List extends \WP_List_Table { $this->items = array(); - foreach ( \Activitypub\Db\Followers::get_followers( \get_current_user_id() ) as $follower ) { + foreach ( \Activitypub\Peer\Followers::get_followers( \get_current_user_id() ) as $follower ) { $this->items[]['identifier'] = \esc_attr( $follower ); } } diff --git a/languages/activitypub.pot b/languages/activitypub.pot index 70471bc..a6c7d7b 100644 --- a/languages/activitypub.pot +++ b/languages/activitypub.pot @@ -5,7 +5,7 @@ msgstr "" "Project-Id-Version: ActivityPub 0.8.3\n" "Report-Msgid-Bugs-To: " "https://wordpress.org/support/plugin/wordpress-activitypub\n" -"POT-Creation-Date: 2019-09-30 06:07:59+00:00\n" +"POT-Creation-Date: 2019-11-18 19:51:49+00:00\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -88,35 +88,35 @@ msgstr "" msgid "Fediverse" msgstr "" -#: includes/db/class-followers.php:53 -msgid "Unknown Actor schema" -msgstr "" - -#: includes/functions.php:104 +#: includes/functions.php:84 msgid "The \"actor\" is no valid URL" msgstr "" -#: includes/functions.php:130 +#: includes/functions.php:110 msgid "No valid JSON data" msgstr "" -#: includes/functions.php:158 +#: includes/functions.php:138 msgid "No \"Inbox\" found" msgstr "" -#: includes/functions.php:184 +#: includes/functions.php:164 msgid "No \"Public-Key\" found" msgstr "" -#: includes/functions.php:212 +#: includes/functions.php:192 msgid "Profile identifier" msgstr "" -#: includes/functions.php:217 +#: includes/functions.php:197 #. translators: the webfinger resource msgid "Try to follow \"@%s\" in the Mastodon/Friendica search field." msgstr "" +#: includes/peer/class-followers.php:53 +msgid "Unknown Actor schema" +msgstr "" + #: includes/rest/class-followers.php:46 includes/rest/class-followers.php:49 #: includes/rest/class-following.php:46 includes/rest/class-following.php:49 #: includes/rest/class-outbox.php:45 includes/rest/class-outbox.php:48 @@ -124,16 +124,28 @@ msgstr "" msgid "User not found" msgstr "" -#: includes/rest/class-inbox.php:100 +#: includes/rest/class-inbox.php:99 includes/rest/class-inbox.php:164 msgid "Invalid payload" msgstr "" -#: includes/rest/class-inbox.php:118 -msgid "This method is not yet implemented" +#: includes/rest/class-inbox.php:119 +msgid "No receiving actor set" msgstr "" -#: includes/rest/class-inbox.php:149 includes/rest/class-inbox.php:178 -#: includes/rest/class-inbox.php:192 includes/rest/class-inbox.php:229 +#: includes/rest/class-inbox.php:126 includes/rest/class-inbox.php:152 +msgid "No matching user" +msgstr "" + +#: includes/rest/class-inbox.php:141 +msgid "Invalid actor identifier" +msgstr "" + +#: includes/rest/class-inbox.php:146 +msgid "Invalid host" +msgstr "" + +#: includes/rest/class-inbox.php:201 includes/rest/class-inbox.php:230 +#: includes/rest/class-inbox.php:244 includes/rest/class-inbox.php:281 msgid "No \"Actor\" found" msgstr "" diff --git a/templates/followers-list.php b/templates/followers-list.php index aa86c39..4776851 100644 --- a/templates/followers-list.php +++ b/templates/followers-list.php @@ -1,7 +1,7 @@

-

+

diff --git a/templates/json-author.php b/templates/json-author.php index 2228871..21d3003 100644 --- a/templates/json-author.php +++ b/templates/json-author.php @@ -75,9 +75,9 @@ if ( \get_the_author_meta( 'user_url', $author_id ) ) { ); } -//$json->endpoints = array( -// 'sharedInbox' => \get_rest_url( null, '/activitypub/1.0/inbox' ), -//); +$json->endpoints = array( + 'sharedInbox' => \get_rest_url( null, '/activitypub/1.0/inbox' ), +); // filter output $json = \apply_filters( 'activitypub_json_author_array', $json ); diff --git a/templates/json-post.php b/templates/json-post.php index 04f683f..ccd9fa9 100644 --- a/templates/json-post.php +++ b/templates/json-post.php @@ -1,7 +1,7 @@ \Activitypub\get_context() ), $activitypub_post->to_array() ); // filter output diff --git a/tests/test-class-db-activitypub-followers.php b/tests/test-class-db-activitypub-followers.php index 77e01e4..30c6cdb 100644 --- a/tests/test-class-db-activitypub-followers.php +++ b/tests/test-class-db-activitypub-followers.php @@ -9,7 +9,7 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { ); update_user_meta( 1, 'activitypub_followers', $followers ); - $db_followers = \Activitypub\Db\Followers::get_followers( 1 ); + $db_followers = \Activitypub\Peer\Followers::get_followers( 1 ); $this->assertEquals( 3, count( $db_followers ) ); @@ -18,9 +18,9 @@ class Test_Db_Activitypub_Followers extends WP_UnitTestCase { public function test_add_follower() { $follower = 'https://example.com/author/' . time(); - \Activitypub\Db\Followers::add_follower( $follower, 1 ); + \Activitypub\Peer\Followers::add_follower( $follower, 1 ); - $db_followers = \Activitypub\Db\Followers::get_followers( 1 ); + $db_followers = \Activitypub\Peer\Followers::get_followers( 1 ); $this->assertContains( $follower, $db_followers ); }