+
+/**
+ * Returns a contextual HTTP error code for authorization failure.
+ *
+ * @since 4.7.0
+ *
+ * @return integer 401 if the user is not logged in, 403 if the user is logged in.
+ */
+function rest_authorization_required_code() {
+ return is_user_logged_in() ? 403 : 401;
+}
+
+/**
+ * Validate a request argument based on details registered to the route.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $value
+ * @param WP_REST_Request $request
+ * @param string $param
+ * @return WP_Error|boolean
+ */
+function rest_validate_request_arg( $value, $request, $param ) {
+ $attributes = $request->get_attributes();
+ if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
+ return true;
+ }
+ $args = $attributes['args'][ $param ];
+
+ return rest_validate_value_from_schema( $value, $args, $param );
+}
+
+/**
+ * Sanitize a request argument based on details registered to the route.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $value
+ * @param WP_REST_Request $request
+ * @param string $param
+ * @return mixed
+ */
+function rest_sanitize_request_arg( $value, $request, $param ) {
+ $attributes = $request->get_attributes();
+ if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
+ return $value;
+ }
+ $args = $attributes['args'][ $param ];
+
+ return rest_sanitize_value_from_schema( $value, $args );
+}
+
+/**
+ * Parse a request argument based on details registered to the route.
+ *
+ * Runs a validation check and sanitizes the value, primarily to be used via
+ * the `sanitize_callback` arguments in the endpoint args registration.
+ *
+ * @since 4.7.0
+ *
+ * @param mixed $value
+ * @param WP_REST_Request $request
+ * @param string $param
+ * @return mixed
+ */
+function rest_parse_request_arg( $value, $request, $param ) {
+ $is_valid = rest_validate_request_arg( $value, $request, $param );
+
+ if ( is_wp_error( $is_valid ) ) {
+ return $is_valid;
+ }
+
+ $value = rest_sanitize_request_arg( $value, $request, $param );
+
+ return $value;
+}
+
+/**
+ * Determines if an IP address is valid.
+ *
+ * Handles both IPv4 and IPv6 addresses.
+ *
+ * @since 4.7.0
+ *
+ * @param string $ip IP address.
+ * @return string|false The valid IP address, otherwise false.
+ */
+function rest_is_ip_address( $ip ) {
+ $ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
+
+ if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) {
+ return false;
+ }
+
+ return $ip;
+}
+
+/**
+ * Changes a boolean-like value into the proper boolean value.
+ *
+ * @since 4.7.0
+ *
+ * @param bool|string|int $value The value being evaluated.
+ * @return boolean Returns the proper associated boolean value.
+ */
+function rest_sanitize_boolean( $value ) {
+ // String values are translated to `true`; make sure 'false' is false.
+ if ( is_string( $value ) ) {
+ $value = strtolower( $value );
+ if ( in_array( $value, array( 'false', '0' ), true ) ) {
+ $value = false;
+ }
+ }
+
+ // Everything else will map nicely to boolean.
+ return (boolean) $value;
+}
+
+/**
+ * Determines if a given value is boolean-like.
+ *
+ * @since 4.7.0
+ *
+ * @param bool|string $maybe_bool The value being evaluated.
+ * @return boolean True if a boolean, otherwise false.
+ */
+function rest_is_boolean( $maybe_bool ) {
+ if ( is_bool( $maybe_bool ) ) {
+ return true;
+ }
+
+ if ( is_string( $maybe_bool ) ) {
+ $maybe_bool = strtolower( $maybe_bool );
+
+ $valid_boolean_values = array(
+ 'false',
+ 'true',
+ '0',
+ '1',
+ );
+
+ return in_array( $maybe_bool, $valid_boolean_values, true );
+ }
+
+ if ( is_int( $maybe_bool ) ) {
+ return in_array( $maybe_bool, array( 0, 1 ), true );
+ }
+
+ return false;
+}
+
+/**
+ * Retrieves the avatar urls in various sizes based on a given email address.
+ *
+ * @since 4.7.0
+ *
+ * @see get_avatar_url()
+ *
+ * @param string $email Email address.
+ * @return array $urls Gravatar url for each size.
+ */
+function rest_get_avatar_urls( $email ) {
+ $avatar_sizes = rest_get_avatar_sizes();
+
+ $urls = array();
+ foreach ( $avatar_sizes as $size ) {
+ $urls[ $size ] = get_avatar_url( $email, array( 'size' => $size ) );
+ }
+
+ return $urls;
+}
+
+/**
+ * Retrieves the pixel sizes for avatars.
+ *
+ * @since 4.7.0
+ *
+ * @return array List of pixel sizes for avatars. Default `[ 24, 48, 96 ]`.
+ */
+function rest_get_avatar_sizes() {
+ /**
+ * Filter the REST avatar sizes.
+ *
+ * Use this filter to adjust the array of sizes returned by the
+ * `rest_get_avatar_sizes` function.
+ *
+ * @since 4.4.0
+ *
+ * @param array $sizes An array of int values that are the pixel sizes for avatars.
+ * Default `[ 24, 48, 96 ]`.
+ */
+ return apply_filters( 'rest_avatar_sizes', array( 24, 48, 96 ) );
+}
+
+/**
+ * Validate a value based on a schema.
+ *
+ * @param mixed $value The value to validate.
+ * @param array $args Schema array to use for validation.
+ * @param string $param The parameter name, used in error messages.
+ * @return true|WP_Error
+ */
+function rest_validate_value_from_schema( $value, $args, $param = '' ) {
+ if ( 'array' === $args['type'] ) {
+ if ( ! is_array( $value ) ) {
+ $value = preg_split( '/[\s,]+/', $value );
+ }
+ if ( ! wp_is_numeric_array( $value ) ) {
+ /* translators: 1: parameter, 2: type name */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ) );
+ }
+ foreach ( $value as $index => $v ) {
+ $is_valid = rest_validate_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' );
+ if ( is_wp_error( $is_valid ) ) {
+ return $is_valid;
+ }
+ }
+ }
+ if ( ! empty( $args['enum'] ) ) {
+ if ( ! in_array( $value, $args['enum'], true ) ) {
+ /* translators: 1: parameter, 2: list of valid values */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) );
+ }
+ }
+
+ if ( in_array( $args['type'], array( 'integer', 'number' ) ) && ! is_numeric( $value ) ) {
+ /* translators: 1: parameter, 2: type name */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, $args['type'] ) );
+ }
+
+ if ( 'integer' === $args['type'] && round( floatval( $value ) ) !== floatval( $value ) ) {
+ /* translators: 1: parameter, 2: type name */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'integer' ) );
+ }
+
+ if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) {
+ /* translators: 1: parameter, 2: type name */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $value, 'boolean' ) );
+ }
+
+ if ( 'string' === $args['type'] && ! is_string( $value ) ) {
+ /* translators: 1: parameter, 2: type name */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'string' ) );
+ }
+
+ if ( isset( $args['format'] ) ) {
+ switch ( $args['format'] ) {
+ case 'date-time' :
+ if ( ! rest_parse_date( $value ) ) {
+ return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) );
+ }
+ break;
+
+ case 'email' :
+ // is_email() checks for 3 characters (a@b), but
+ // wp_handle_comment_submission() requires 6 characters (a@b.co)
+ //
+ // https://core.trac.wordpress.org/ticket/38506
+ if ( ! is_email( $value ) || strlen( $value ) < 6 ) {
+ return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) );
+ }
+ break;
+ case 'ip' :
+ if ( ! rest_is_ip_address( $value ) ) {
+ /* translators: %s: IP address */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) );
+ }
+ break;
+ }
+ }
+
+ if ( in_array( $args['type'], array( 'number', 'integer' ), true ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) {
+ if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) {
+ if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) );
+ } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) );
+ }
+ } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) {
+ if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) {
+ /* translators: 1: parameter, 2: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) );
+ } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) {
+ /* translators: 1: parameter, 2: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) );
+ }
+ } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) {
+ if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
+ if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number, 3: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
+ }
+ } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
+ if ( $value >= $args['maximum'] || $value < $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number, 3: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
+ }
+ } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
+ if ( $value > $args['maximum'] || $value <= $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number, 3: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
+ }
+ } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
+ if ( $value > $args['maximum'] || $value < $args['minimum'] ) {
+ /* translators: 1: parameter, 2: minimum number, 3: maximum number */
+ return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Sanitize a value based on a schema.
+ *
+ * @param mixed $value The value to sanitize.
+ * @param array $args Schema array to use for sanitization.
+ * @return true|WP_Error
+ */
+function rest_sanitize_value_from_schema( $value, $args ) {
+ if ( 'array' === $args['type'] ) {
+ if ( empty( $args['items'] ) ) {
+ return (array) $value;
+ }
+ if ( ! is_array( $value ) ) {
+ $value = preg_split( '/[\s,]+/', $value );
+ }
+ foreach ( $value as $index => $v ) {
+ $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'] );
+ }
+ // Normalize to numeric array so nothing unexpected
+ // is in the keys.
+ $value = array_values( $value );
+ return $value;
+ }
+ if ( 'integer' === $args['type'] ) {
+ return (int) $value;
+ }
+
+ if ( 'number' === $args['type'] ) {
+ return (float) $value;
+ }
+
+ if ( 'boolean' === $args['type'] ) {
+ return rest_sanitize_boolean( $value );
+ }
+
+ if ( isset( $args['format'] ) ) {
+ switch ( $args['format'] ) {
+ case 'date-time' :
+ return sanitize_text_field( $value );
+
+ case 'email' :
+ /*
+ * sanitize_email() validates, which would be unexpected.
+ */
+ return sanitize_text_field( $value );
+
+ case 'uri' :
+ return esc_url_raw( $value );
+
+ case 'ip' :
+ return sanitize_text_field( $value );
+ }
+ }
+
+ if ( 'string' === $args['type'] ) {
+ return strval( $value );
+ }
+
+ return $value;
+}