2023-10-22 22:20:53 +00:00
|
|
|
<?php
|
2024-10-09 12:44:17 +00:00
|
|
|
/**
|
|
|
|
* Users collection file.
|
|
|
|
*
|
|
|
|
* @package Activitypub
|
|
|
|
*/
|
|
|
|
|
2023-10-22 22:20:53 +00:00
|
|
|
namespace Activitypub\Collection;
|
|
|
|
|
|
|
|
use WP_Error;
|
|
|
|
use WP_User_Query;
|
|
|
|
use Activitypub\Model\User;
|
2024-06-27 12:10:38 +00:00
|
|
|
use Activitypub\Model\Blog;
|
|
|
|
use Activitypub\Model\Application;
|
2023-10-22 22:20:53 +00:00
|
|
|
|
2024-05-09 15:26:55 +00:00
|
|
|
use function Activitypub\object_to_uri;
|
2024-07-19 19:46:05 +00:00
|
|
|
use function Activitypub\normalize_url;
|
|
|
|
use function Activitypub\normalize_host;
|
2024-02-08 12:31:25 +00:00
|
|
|
use function Activitypub\url_to_authorid;
|
2023-10-22 22:20:53 +00:00
|
|
|
use function Activitypub\is_user_disabled;
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
/**
|
|
|
|
* Users collection.
|
|
|
|
*/
|
2023-10-22 22:20:53 +00:00
|
|
|
class Users {
|
|
|
|
/**
|
2024-10-09 12:44:17 +00:00
|
|
|
* The ID of the Blog User.
|
2023-10-22 22:20:53 +00:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
const BLOG_USER_ID = 0;
|
|
|
|
|
|
|
|
/**
|
2024-10-09 12:44:17 +00:00
|
|
|
* The ID of the Application User.
|
2023-10-22 22:20:53 +00:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
const APPLICATION_USER_ID = -1;
|
|
|
|
|
|
|
|
/**
|
2024-10-09 12:44:17 +00:00
|
|
|
* Get the User by ID.
|
2023-10-22 22:20:53 +00:00
|
|
|
*
|
|
|
|
* @param int $user_id The User-ID.
|
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @return User|Blog|Application|WP_Error The User or WP_Error if user not found.
|
2023-10-22 22:20:53 +00:00
|
|
|
*/
|
|
|
|
public static function get_by_id( $user_id ) {
|
|
|
|
if ( is_string( $user_id ) || is_numeric( $user_id ) ) {
|
|
|
|
$user_id = (int) $user_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( is_user_disabled( $user_id ) ) {
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_user_not_found',
|
|
|
|
\__( 'User not found', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( self::BLOG_USER_ID === $user_id ) {
|
2024-06-27 12:10:38 +00:00
|
|
|
return new Blog();
|
2023-10-22 22:20:53 +00:00
|
|
|
} elseif ( self::APPLICATION_USER_ID === $user_id ) {
|
2024-06-27 12:10:38 +00:00
|
|
|
return new Application();
|
2023-10-22 22:20:53 +00:00
|
|
|
} elseif ( $user_id > 0 ) {
|
|
|
|
return User::from_wp_user( $user_id );
|
|
|
|
}
|
|
|
|
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_user_not_found',
|
|
|
|
\__( 'User not found', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the User by username.
|
|
|
|
*
|
|
|
|
* @param string $username The User-Name.
|
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @return User|Blog|Application|WP_Error The User or WP_Error if user not found.
|
2023-10-22 22:20:53 +00:00
|
|
|
*/
|
|
|
|
public static function get_by_username( $username ) {
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for blog user.
|
2024-06-27 12:10:38 +00:00
|
|
|
if ( Blog::get_default_username() === $username ) {
|
|
|
|
return new Blog();
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
if ( get_option( 'activitypub_blog_identifier' ) === $username ) {
|
2024-06-27 12:10:38 +00:00
|
|
|
return new Blog();
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for application user.
|
2023-10-22 22:20:53 +00:00
|
|
|
if ( 'application' === $username ) {
|
2024-07-19 19:46:05 +00:00
|
|
|
return new Application();
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for 'activitypub_username' meta.
|
2023-10-22 22:20:53 +00:00
|
|
|
$user = new WP_User_Query(
|
|
|
|
array(
|
2024-10-09 12:44:17 +00:00
|
|
|
'count_total' => false,
|
|
|
|
'number' => 1,
|
|
|
|
'hide_empty' => true,
|
|
|
|
'fields' => 'ID',
|
2023-10-22 22:20:53 +00:00
|
|
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
2024-10-09 12:44:17 +00:00
|
|
|
'meta_query' => array(
|
2023-10-22 22:20:53 +00:00
|
|
|
'relation' => 'OR',
|
|
|
|
array(
|
|
|
|
'key' => 'activitypub_user_identifier',
|
|
|
|
'value' => $username,
|
|
|
|
'compare' => 'LIKE',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( $user->results ) {
|
|
|
|
return self::get_by_id( $user->results[0] );
|
|
|
|
}
|
|
|
|
|
2024-02-08 12:31:25 +00:00
|
|
|
$username = str_replace( array( '*', '%' ), '', $username );
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for login or nicename.
|
2023-10-22 22:20:53 +00:00
|
|
|
$user = new WP_User_Query(
|
|
|
|
array(
|
2024-07-19 19:46:05 +00:00
|
|
|
'count_total' => false,
|
2023-10-22 22:20:53 +00:00
|
|
|
'search' => $username,
|
|
|
|
'search_columns' => array( 'user_login', 'user_nicename' ),
|
|
|
|
'number' => 1,
|
|
|
|
'hide_empty' => true,
|
|
|
|
'fields' => 'ID',
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( $user->results ) {
|
|
|
|
return self::get_by_id( $user->results[0] );
|
|
|
|
}
|
|
|
|
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_user_not_found',
|
|
|
|
\__( 'User not found', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the User by resource.
|
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @param string $uri The User-Resource.
|
2023-10-22 22:20:53 +00:00
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @return User|WP_Error The User or WP_Error if user not found.
|
2023-10-22 22:20:53 +00:00
|
|
|
*/
|
2024-10-09 12:44:17 +00:00
|
|
|
public static function get_by_resource( $uri ) {
|
|
|
|
$uri = object_to_uri( $uri );
|
2024-05-09 15:26:55 +00:00
|
|
|
|
2024-02-08 12:31:25 +00:00
|
|
|
$scheme = 'acct';
|
2024-10-09 12:44:17 +00:00
|
|
|
$match = array();
|
|
|
|
// Try to extract the scheme and the host.
|
|
|
|
if ( preg_match( '/^([a-zA-Z^:]+):(.*)$/i', $uri, $match ) ) {
|
|
|
|
// Extract the scheme.
|
2024-06-27 12:10:38 +00:00
|
|
|
$scheme = \esc_attr( $match[1] );
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-02-08 12:31:25 +00:00
|
|
|
switch ( $scheme ) {
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for http(s) URIs.
|
2024-02-08 12:31:25 +00:00
|
|
|
case 'http':
|
|
|
|
case 'https':
|
2024-10-09 12:44:17 +00:00
|
|
|
$resource_path = \wp_parse_url( $uri, PHP_URL_PATH );
|
2023-10-22 22:20:53 +00:00
|
|
|
|
2024-06-27 12:10:38 +00:00
|
|
|
if ( $resource_path ) {
|
|
|
|
$blog_path = \wp_parse_url( \home_url(), PHP_URL_PATH );
|
|
|
|
|
|
|
|
if ( $blog_path ) {
|
|
|
|
$resource_path = \str_replace( $blog_path, '', $resource_path );
|
|
|
|
}
|
|
|
|
|
|
|
|
$resource_path = \trim( $resource_path, '/' );
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for http(s)://blog.example.com/@username.
|
2024-06-27 12:10:38 +00:00
|
|
|
if ( str_starts_with( $resource_path, '@' ) ) {
|
|
|
|
$identifier = \str_replace( '@', '', $resource_path );
|
|
|
|
$identifier = \trim( $identifier, '/' );
|
2023-10-22 22:20:53 +00:00
|
|
|
|
2024-06-27 12:10:38 +00:00
|
|
|
return self::get_by_username( $identifier );
|
|
|
|
}
|
2024-02-08 12:31:25 +00:00
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for http(s)://blog.example.com/author/username.
|
|
|
|
$user_id = url_to_authorid( $uri );
|
2024-02-08 12:31:25 +00:00
|
|
|
|
|
|
|
if ( $user_id ) {
|
|
|
|
return self::get_by_id( $user_id );
|
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for http(s)://blog.example.com/.
|
2024-02-08 12:31:25 +00:00
|
|
|
if (
|
2024-10-09 12:44:17 +00:00
|
|
|
normalize_url( site_url() ) === normalize_url( $uri ) ||
|
|
|
|
normalize_url( home_url() ) === normalize_url( $uri )
|
2024-02-08 12:31:25 +00:00
|
|
|
) {
|
|
|
|
return self::get_by_id( self::BLOG_USER_ID );
|
|
|
|
}
|
|
|
|
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_no_user_found',
|
|
|
|
\__( 'User not found', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
2024-10-09 12:44:17 +00:00
|
|
|
// Check for acct URIs.
|
2024-02-08 12:31:25 +00:00
|
|
|
case 'acct':
|
2024-10-09 12:44:17 +00:00
|
|
|
$uri = \str_replace( 'acct:', '', $uri );
|
|
|
|
$identifier = \substr( $uri, 0, \strrpos( $uri, '@' ) );
|
|
|
|
$host = normalize_host( \substr( \strrchr( $uri, '@' ), 1 ) );
|
2024-07-19 19:46:05 +00:00
|
|
|
$blog_host = normalize_host( \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) );
|
2023-10-22 22:20:53 +00:00
|
|
|
|
2024-02-08 12:31:25 +00:00
|
|
|
if ( $blog_host !== $host ) {
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_wrong_host',
|
|
|
|
\__( 'Resource host does not match blog host', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-10-09 12:44:17 +00:00
|
|
|
// Prepare wildcards https://github.com/mastodon/mastodon/issues/22213.
|
2024-02-08 12:31:25 +00:00
|
|
|
if ( in_array( $identifier, array( '_', '*', '' ), true ) ) {
|
|
|
|
return self::get_by_id( self::BLOG_USER_ID );
|
|
|
|
}
|
|
|
|
|
|
|
|
return self::get_by_username( $identifier );
|
|
|
|
default:
|
|
|
|
return new WP_Error(
|
|
|
|
'activitypub_wrong_scheme',
|
|
|
|
\__( 'Wrong scheme', 'activitypub' ),
|
|
|
|
array( 'status' => 404 )
|
|
|
|
);
|
|
|
|
}
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the User by resource.
|
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @param string $id The User-Resource.
|
2023-10-22 22:20:53 +00:00
|
|
|
*
|
2024-10-09 12:44:17 +00:00
|
|
|
* @return User|Blog|Application|WP_Error The User or WP_Error if user not found.
|
2023-10-22 22:20:53 +00:00
|
|
|
*/
|
|
|
|
public static function get_by_various( $id ) {
|
2024-06-27 12:10:38 +00:00
|
|
|
$user = null;
|
|
|
|
|
2023-10-22 22:20:53 +00:00
|
|
|
if ( is_numeric( $id ) ) {
|
2024-06-27 12:10:38 +00:00
|
|
|
$user = self::get_by_id( $id );
|
2024-02-08 12:31:25 +00:00
|
|
|
} elseif (
|
2024-10-09 12:44:17 +00:00
|
|
|
// Is URL.
|
2024-02-08 12:31:25 +00:00
|
|
|
filter_var( $id, FILTER_VALIDATE_URL ) ||
|
2024-10-09 12:44:17 +00:00
|
|
|
// Is acct.
|
2024-06-27 12:10:38 +00:00
|
|
|
str_starts_with( $id, 'acct:' ) ||
|
2024-10-09 12:44:17 +00:00
|
|
|
// Is email.
|
2024-06-27 12:10:38 +00:00
|
|
|
filter_var( $id, FILTER_VALIDATE_EMAIL )
|
2024-02-08 12:31:25 +00:00
|
|
|
) {
|
2024-06-27 12:10:38 +00:00
|
|
|
$user = self::get_by_resource( $id );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $user && ! is_wp_error( $user ) ) {
|
|
|
|
return $user;
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
2024-06-27 12:10:38 +00:00
|
|
|
|
|
|
|
return self::get_by_username( $id );
|
2023-10-22 22:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the User collection.
|
|
|
|
*
|
|
|
|
* @return array The User collection.
|
|
|
|
*/
|
|
|
|
public static function get_collection() {
|
|
|
|
$users = \get_users(
|
|
|
|
array(
|
2024-04-19 10:49:31 +00:00
|
|
|
'capability__in' => array( 'activitypub' ),
|
2023-10-22 22:20:53 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
$return = array();
|
|
|
|
|
|
|
|
foreach ( $users as $user ) {
|
|
|
|
$return[] = User::from_wp_user( $user->ID );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $return;
|
|
|
|
}
|
|
|
|
}
|