Index: src/wp-comments-post.php
===================================================================
--- src/wp-comments-post.php	(revision 34666)
+++ src/wp-comments-post.php	(working copy)
@@ -17,129 +17,17 @@
 
 nocache_headers();
 
-$comment_post_ID = isset($_POST['comment_post_ID']) ? (int) $_POST['comment_post_ID'] : 0;
-
-$post = get_post($comment_post_ID);
-
-if ( empty( $post->comment_status ) ) {
-	/**
-	 * Fires when a comment is attempted on a post that does not exist.
-	 *
-	 * @since 1.5.0
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'comment_id_not_found', $comment_post_ID );
-	exit;
-}
-
-// get_post_status() will get the parent status for attachments.
-$status = get_post_status($post);
-
-$status_obj = get_post_status_object($status);
-
-if ( ! comments_open( $comment_post_ID ) ) {
-	/**
-	 * Fires when a comment is attempted on a post that has comments closed.
-	 *
-	 * @since 1.5.0
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'comment_closed', $comment_post_ID );
-	wp_die( __( 'Sorry, comments are closed for this item.' ), 403 );
-} elseif ( 'trash' == $status ) {
-	/**
-	 * Fires when a comment is attempted on a trashed post.
-	 *
-	 * @since 2.9.0
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'comment_on_trash', $comment_post_ID );
-	exit;
-} elseif ( ! $status_obj->public && ! $status_obj->private ) {
-	/**
-	 * Fires when a comment is attempted on a post in draft mode.
-	 *
-	 * @since 1.5.1
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'comment_on_draft', $comment_post_ID );
-	exit;
-} elseif ( post_password_required( $comment_post_ID ) ) {
-	/**
-	 * Fires when a comment is attempted on a password-protected post.
-	 *
-	 * @since 2.9.0
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'comment_on_password_protected', $comment_post_ID );
-	exit;
-} else {
-	/**
-	 * Fires before a comment is posted.
-	 *
-	 * @since 2.8.0
-	 *
-	 * @param int $comment_post_ID Post ID.
-	 */
-	do_action( 'pre_comment_on_post', $comment_post_ID );
-}
-
-$comment_author       = ( isset( $_POST['author'] ) && is_string( $_POST['author'] ) ) ? trim( strip_tags( $_POST['author'] ) ) : null;
-$comment_author_email = ( isset( $_POST['email'] ) && is_email( $_POST['email'] ) ) ? trim( $_POST['email'] ) : null;
-$comment_author_url   = ( isset( $_POST['url'] ) && is_string( $_POST['url'] ) ) ? trim( $_POST['url'] ) : null;
-$comment_content      = ( isset( $_POST['comment'] ) && is_string( $_POST['comment'] ) ) ? trim( $_POST['comment'] ) : null;
-
-// If the user is logged in
-$user = wp_get_current_user();
-if ( $user->exists() ) {
-	if ( empty( $user->display_name ) )
-		$user->display_name=$user->user_login;
-	$comment_author       = wp_slash( $user->display_name );
-	$comment_author_email = wp_slash( $user->user_email );
-	$comment_author_url   = wp_slash( $user->user_url );
-	if ( current_user_can( 'unfiltered_html' ) ) {
-		if ( ! isset( $_POST['_wp_unfiltered_html_comment'] )
-			|| ! wp_verify_nonce( $_POST['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID )
-		) {
-			kses_remove_filters(); // start with a clean slate
-			kses_init_filters(); // set up the filters
-		}
-	}
-} else {
-	if ( get_option( 'comment_registration' ) || 'private' == $status ) {
-		wp_die( __( 'Sorry, you must be logged in to post a comment.' ), 403 );
+$comment = wp_handle_comment_post( wp_unslash( $_POST ) );
+if ( is_wp_error( $comment ) ) {
+	$data = $comment->get_error_data();
+	if ( ! empty( $data ) ) {
+		wp_die( $comment->get_error_message(), $data );
+	} else {
+		exit;
 	}
 }
 
-$comment_type = '';
-
-if ( get_option('require_name_email') && !$user->exists() ) {
-	if ( 6 > strlen( $comment_author_email ) || '' == $comment_author ) {
-		wp_die( __( '<strong>ERROR</strong>: please fill the required fields (name, email).' ), 200 );
-	} elseif ( ! is_email( $comment_author_email ) ) {
-		wp_die( __( '<strong>ERROR</strong>: please enter a valid email address.' ), 200 );
-	}
-}
-
-if ( '' == $comment_content ) {
-	wp_die( __( '<strong>ERROR</strong>: please type a comment.' ), 200 );
-}
-
-$comment_parent = isset($_POST['comment_parent']) ? absint($_POST['comment_parent']) : 0;
-
-$commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
-
-$comment_id = wp_new_comment( $commentdata );
-if ( ! $comment_id ) {
-	wp_die( __( "<strong>ERROR</strong>: The comment could not be saved. Please try again later." ) );
-}
-
-$comment = get_comment( $comment_id );
+$user = wp_get_current_user();
 
 /**
  * Perform other actions when comment cookies are set.
@@ -151,7 +39,7 @@
  */
 do_action( 'set_comment_cookies', $comment, $user );
 
-$location = empty($_POST['redirect_to']) ? get_comment_link( $comment ) : $_POST['redirect_to'] . '#comment-' . $comment_id;
+$location = empty( $_POST['redirect_to'] ) ? get_comment_link( $comment ) : $_POST['redirect_to'] . '#comment-' . $comment->comment_ID;
 
 /**
  * Filter the location URI to send the commenter after posting.
Index: src/wp-includes/comment-functions.php
===================================================================
--- src/wp-includes/comment-functions.php	(revision 34666)
+++ src/wp-includes/comment-functions.php	(working copy)
@@ -2550,3 +2550,187 @@
 
 	return $open;
 }
+
+/**
+ * Handles the submission of a comment, usually posted to wp-comments-post.php via a comment form.
+ *
+ * @param array $comment_data {
+ *     Comment data.
+ *
+ *     @type string|int $comment_post_ID             The ID of the post that relates to the comment.
+ *     @type string     $author                      The name of the comment author.
+ *     @type string     $email                       The comment author email address.
+ *     @type string     $url                         The comment author URL.
+ *     @type string     $comment                     The content of the comment.
+ *     @type string|int $comment_parent              The ID of this comment's parent, if any. Default 0.
+ *     @type string     $_wp_unfiltered_html_comment The nonce value for allowing unfiltered HTML.
+ * }
+ * @return WP_Comment|WP_Error A WP_Comment object on success, a WP_Error object on failure.
+ */
+function wp_handle_comment_post( $comment_data ) {
+
+	$comment_post_ID = isset( $comment_data['comment_post_ID'] )
+		? (int) $comment_data['comment_post_ID']
+		: 0;
+	$comment_author = ( isset( $comment_data['author'] ) && is_string( $comment_data['author'] ) )
+		? trim( strip_tags( $comment_data['author'] ) )
+		: null;
+	$comment_author_email = ( isset( $comment_data['email'] ) && is_email( $comment_data['email'] ) )
+		? trim( $comment_data['email'] )
+		: null;
+	$comment_author_url = ( isset( $comment_data['url'] ) && is_string( $comment_data['url'] ) )
+		? trim( $comment_data['url'] )
+		: null;
+	$comment_content = ( isset( $comment_data['comment'] ) && is_string( $comment_data['comment'] ) )
+		? trim( $comment_data['comment'] )
+		: null;
+	$comment_parent = isset( $comment_data['comment_parent'] )
+		? absint( $comment_data['comment_parent'] )
+		: 0;
+	$_wp_unfiltered_html_comment = ( isset( $comment_data['_wp_unfiltered_html_comment'] ) && is_string( $comment_data['_wp_unfiltered_html_comment'] ) )
+		? trim( $comment_data['_wp_unfiltered_html_comment'] )
+		: null;
+
+	$post = get_post( $comment_post_ID );
+
+	if ( empty( $post->comment_status ) ) {
+
+		/**
+		 * Fires when a comment is attempted on a post that does not exist.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'comment_id_not_found', $comment_post_ID );
+
+		return new WP_Error( 'comment_id_not_found' );
+
+	}
+
+	// get_post_status() will get the parent status for attachments.
+	$status = get_post_status( $post );
+
+	$status_obj = get_post_status_object( $status );
+
+	if ( ! comments_open( $comment_post_ID ) ) {
+
+		/**
+		 * Fires when a comment is attempted on a post that has comments closed.
+		 *
+		 * @since 1.5.0
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'comment_closed', $comment_post_ID );
+
+		return new WP_Error( 'comment_closed', __( 'Sorry, comments are closed for this item.' ), 403 );
+
+	} elseif ( 'trash' == $status ) {
+
+		/**
+		 * Fires when a comment is attempted on a trashed post.
+		 *
+		 * @since 2.9.0
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'comment_on_trash', $comment_post_ID );
+
+		return new WP_Error( 'comment_on_trash' );
+
+	} elseif ( ! $status_obj->public && ! $status_obj->private ) {
+
+		/**
+		 * Fires when a comment is attempted on a post in draft mode.
+		 *
+		 * @since 1.5.1
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'comment_on_draft', $comment_post_ID );
+
+		return new WP_Error( 'comment_on_draft' );
+
+	} elseif ( post_password_required( $comment_post_ID ) ) {
+
+		/**
+		 * Fires when a comment is attempted on a password-protected post.
+		 *
+		 * @since 2.9.0
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'comment_on_password_protected', $comment_post_ID );
+
+		return new WP_Error( 'comment_on_password_protected' );
+
+	} else {
+
+		/**
+		 * Fires before a comment is posted.
+		 *
+		 * @since 2.8.0
+		 *
+		 * @param int $comment_post_ID Post ID.
+		 */
+		do_action( 'pre_comment_on_post', $comment_post_ID );
+
+	}
+
+	// If the user is logged in
+	$user = wp_get_current_user();
+	if ( $user->exists() ) {
+		if ( empty( $user->display_name ) ) {
+			$user->display_name=$user->user_login;
+		}
+		$comment_author       = $user->display_name;
+		$comment_author_email = $user->user_email;
+		$comment_author_url   = $user->user_url;
+		if ( current_user_can( 'unfiltered_html' ) ) {
+			if ( ! isset( $comment_data['_wp_unfiltered_html_comment'] )
+				|| ! wp_verify_nonce( $comment_data['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID )
+			) {
+				kses_remove_filters(); // start with a clean slate
+				kses_init_filters(); // set up the filters
+			}
+		}
+	} else {
+		if ( get_option( 'comment_registration' ) || 'private' == $status ) {
+			return new WP_Error( 'not_logged_in', __( 'Sorry, you must be logged in to post a comment.' ), 403 );
+		}
+	}
+
+	$comment_type = '';
+
+	if ( get_option( 'require_name_email' ) && ! $user->exists() ) {
+		if ( 6 > strlen( $comment_author_email ) || '' == $comment_author ) {
+			return new WP_Error( 'require_name_email', __( '<strong>ERROR</strong>: please fill the required fields (name, email).' ), 200 );
+		} elseif ( ! is_email( $comment_author_email ) ) {
+			return new WP_Error( 'require_valid_email', __( '<strong>ERROR</strong>: please enter a valid email address.' ), 200 );
+		}
+	}
+
+	if ( '' == $comment_content ) {
+		return new WP_Error( 'require_valid_comment', __( '<strong>ERROR</strong>: please type a comment.' ), 200 );
+	}
+
+	$commentdata = compact(
+		'comment_post_ID',
+		'comment_author',
+		'comment_author_email',
+		'comment_author_url',
+		'comment_content',
+		'comment_type',
+		'comment_parent',
+		'user_ID'
+	);
+
+	$comment_id = wp_new_comment( wp_slash( $commentdata ) );
+	if ( ! $comment_id ) {
+		return new WP_Error( 'comment_save_error', __( '<strong>ERROR</strong>: The comment could not be saved. Please try again later.' ), 500 );
+	}
+
+	return get_comment( $comment_id );
+
+}
