Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions inc/nopriv-write-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public static function init() {

// Make sure we can decode the Message
$message = json_decode( stripslashes( $_REQUEST['message'] ) );
if ( null === $message ) {
if ( ! is_object( $message ) ) {
self::die_failure( 'invalid_message', __( 'Message could not be decoded', 'o2' ) );
}

// We only support comment messages (for now)
if ( !in_array( $message->type, apply_filters( 'o2_supported_nopriv_message_types', array( 'comment' ) ) ) ) {
if ( ! isset( $message->type ) || !in_array( $message->type, apply_filters( 'o2_supported_nopriv_message_types', array( 'comment' ) ) ) ) {
self::die_failure( 'unknown_message_type', __( 'Unknown message type passed', 'o2' ) );
}

Expand Down Expand Up @@ -78,23 +78,20 @@ public static function create_comment( $message ) {
// Posting a new comment. We use the core-provided file to
// avoid re-implementing a bunch of logic

if ( function_exists( 'is_email_wp_emails' ) && is_email_wp_emails( $message->author->email ) ) {
// The client only sends a userLogin when it thinks the user is logged in,
// so the session expired (or they logged out elsewhere) since the page loaded.
if ( ! empty( $message->author->userLogin ) ) {
self::die_failure( 'logged_out', __( 'You have been logged out. Please log in again to comment.', 'o2' ), 403 );
}

$_POST = array_merge( $_POST, self::get_comment_fields( $message ) );

if ( function_exists( 'is_email_wp_emails' ) && is_email_wp_emails( $_POST['email'] ) ) {
self::die_failure( 'email_associated_with_account_error',
__( 'The email you specified is associated with a WordPress.com account. Please login first or use a different email address.', 'o2' )
);
}

// Re-map some incoming data to match the expected _POST elements
$remap = array(
'comment_post_ID' => $message->postID,
'comment' => $message->contentRaw,
'comment_parent' => $message->parentID,
'author' => $message->author->name,
'email' => $message->author->email,
'url' => $message->author->url
);
$_POST = array_merge( $_POST, $remap );

// Let the core comment handler take it from here
global $wpdb;
require_once ABSPATH . 'wp-comments-post.php';
Expand All @@ -104,6 +101,23 @@ public static function create_comment( $message ) {
self::die_failure( 'unknown_comment_error', __( 'Failed to post comment.', 'o2' ) );
}

/**
* Re-map the incoming message to the _POST elements wp-comments-post.php expects.
* Fields can be missing from hand-crafted requests, so default them.
*/
public static function get_comment_fields( $message ) {
$author = isset( $message->author ) && is_object( $message->author ) ? $message->author : new stdClass();

return array(
'comment_post_ID' => $message->postID ?? 0,
'comment' => $message->contentRaw ?? '',
'comment_parent' => $message->parentID ?? 0,
'author' => $author->name ?? '',
'email' => $author->email ?? '',
'url' => $author->url ?? '',
);
}

/**
* Called from the end of the core commenting process. If we get here
* then the comment was posted successfully, so just output it and die.
Expand Down
69 changes: 69 additions & 0 deletions tests/phpunit/test-nopriv-write-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

class NoPrivWriteAPITest extends WP_UnitTestCase {

function test_get_comment_fields_from_logged_out_client() {
$message = json_decode( '{"type":"comment","postID":5,"parentID":3,"contentRaw":"Hello","author":{"name":"Jane","email":"jane@example.com","url":"https://example.com"}}' );

$this->assertSame(
array(
'comment_post_ID' => 5,
'comment' => 'Hello',
'comment_parent' => 3,
'author' => 'Jane',
'email' => 'jane@example.com',
'url' => 'https://example.com',
),
o2_NoPriv_Write_API::get_comment_fields( $message )
);
}

function test_get_comment_fields_defaults_missing_fields() {
$message = json_decode( '{"type":"comment","postID":999999999,"contentRaw":"test"}' );

$this->assertSame(
array(
'comment_post_ID' => 999999999,
'comment' => 'test',
'comment_parent' => 0,
'author' => '',
'email' => '',
'url' => '',
),
o2_NoPriv_Write_API::get_comment_fields( $message )
);
}

function test_create_comment_from_logged_in_client_reports_logged_out() {
$message = json_decode( '{"type":"comment","postID":5,"parentID":0,"contentRaw":"Hello","author":{"userLogin":"jane","userNicename":"jane"}}' );

add_filter( 'wp_doing_ajax', '__return_true' );
add_filter( 'wp_die_ajax_handler', array( $this, 'get_die_handler' ) );

// If the guard regresses, wp-comments-post.php is reached, and it exits on anything but POST,
// silently ending the test run.
$request_method = $_SERVER['REQUEST_METHOD'] ?? null;
$_SERVER['REQUEST_METHOD'] = 'POST';

ob_start();
try {
o2_NoPriv_Write_API::create_comment( $message );
} catch ( WPDieException $e ) {
// Expected, die_failure() ends the request.
} finally {
$_SERVER['REQUEST_METHOD'] = $request_method;
}
$response = json_decode( ob_get_clean(), true );

$this->assertFalse( $response['success'] );
$this->assertSame( 'logged_out', $response['data']['error'] );
}

function get_die_handler() {
return array( $this, 'die_handler' );
}

function die_handler( $message ) {
throw new WPDieException( $message );
}
}
Loading