Skip to content

Commit 7cd5ecd

Browse files
committed
XML-RPC: Set HTTP status code in accordance with the spec.
When the XML-RPC endpoint is enabled, always return a HTTP `200 OK` status code in accordance with the XML-RPC specification. Continue to return an HTTP `405 Method Not Allowed` status code when the endpoint is disabled. Props ariskataoka, johnbillion. Fixes #52958. git-svn-id: https://develop.svn.wordpress.org/trunk@50954 602fd350-edb4-49c9-b593-d223f7449a82
1 parent c4fd562 commit 7cd5ecd

3 files changed

Lines changed: 89 additions & 50 deletions

File tree

src/wp-includes/IXR/class-IXR-server.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,6 @@ function error($error, $message = false)
130130
$error = new IXR_Error($error, $message);
131131
}
132132

133-
if ( function_exists( 'status_header' ) ) {
134-
status_header( $error->code );
135-
}
136-
137133
$this->output($error->getXml());
138134
}
139135

src/wp-includes/class-wp-xmlrpc-server.php

Lines changed: 78 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* options, etc.
1515
*
1616
* As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled
17-
* via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::login().
17+
* via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::set_is_enabled().
1818
*
1919
* @since 1.5.0
2020
*
@@ -49,6 +49,13 @@ class wp_xmlrpc_server extends IXR_Server {
4949
*/
5050
protected $auth_failed = false;
5151

52+
/**
53+
* Flags that XML-RPC is enabled
54+
*
55+
* @var bool
56+
*/
57+
private $is_enabled;
58+
5259
/**
5360
* Registers all of the XMLRPC methods that XMLRPC server understands.
5461
*
@@ -164,6 +171,51 @@ public function __construct() {
164171
* @param string[] $methods An array of XML-RPC methods, keyed by their methodName.
165172
*/
166173
$this->methods = apply_filters( 'xmlrpc_methods', $this->methods );
174+
175+
$this->set_is_enabled();
176+
}
177+
178+
/**
179+
* Set wp_xmlrpc_server::$is_enabled property.
180+
*
181+
* Determine whether the xmlrpc server is enabled on this WordPress install
182+
* and set the is_enabled property accordingly.
183+
*
184+
* @since 5.7.3
185+
*/
186+
private function set_is_enabled() {
187+
/*
188+
* Respect old get_option() filters left for back-compat when the 'enable_xmlrpc'
189+
* option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead.
190+
*/
191+
$is_enabled = apply_filters( 'pre_option_enable_xmlrpc', false );
192+
if ( false === $is_enabled ) {
193+
$is_enabled = apply_filters( 'option_enable_xmlrpc', true );
194+
}
195+
196+
/**
197+
* Filters whether XML-RPC methods requiring authentication are enabled.
198+
*
199+
* Contrary to the way it's named, this filter does not control whether XML-RPC is *fully*
200+
* enabled, rather, it only controls whether XML-RPC methods requiring authentication - such
201+
* as for publishing purposes - are enabled.
202+
*
203+
* Further, the filter does not control whether pingbacks or other custom endpoints that don't
204+
* require authentication are enabled. This behavior is expected, and due to how parity was matched
205+
* with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5.
206+
*
207+
* To disable XML-RPC methods that require authentication, use:
208+
*
209+
* add_filter( 'xmlrpc_enabled', '__return_false' );
210+
*
211+
* For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'}
212+
* and {@see 'xmlrpc_element_limit'} hooks.
213+
*
214+
* @since 3.5.0
215+
*
216+
* @param bool $is_enabled Whether XML-RPC is enabled. Default true.
217+
*/
218+
$this->is_enabled = apply_filters( 'xmlrpc_enabled', $is_enabled );
167219
}
168220

169221
/**
@@ -231,40 +283,7 @@ public function addTwoNumbers( $args ) {
231283
* @return WP_User|false WP_User object if authentication passed, false otherwise
232284
*/
233285
public function login( $username, $password ) {
234-
/*
235-
* Respect old get_option() filters left for back-compat when the 'enable_xmlrpc'
236-
* option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead.
237-
*/
238-
$enabled = apply_filters( 'pre_option_enable_xmlrpc', false );
239-
if ( false === $enabled ) {
240-
$enabled = apply_filters( 'option_enable_xmlrpc', true );
241-
}
242-
243-
/**
244-
* Filters whether XML-RPC methods requiring authentication are enabled.
245-
*
246-
* Contrary to the way it's named, this filter does not control whether XML-RPC is *fully*
247-
* enabled, rather, it only controls whether XML-RPC methods requiring authentication - such
248-
* as for publishing purposes - are enabled.
249-
*
250-
* Further, the filter does not control whether pingbacks or other custom endpoints that don't
251-
* require authentication are enabled. This behavior is expected, and due to how parity was matched
252-
* with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5.
253-
*
254-
* To disable XML-RPC methods that require authentication, use:
255-
*
256-
* add_filter( 'xmlrpc_enabled', '__return_false' );
257-
*
258-
* For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'}
259-
* and {@see 'xmlrpc_element_limit'} hooks.
260-
*
261-
* @since 3.5.0
262-
*
263-
* @param bool $enabled Whether XML-RPC is enabled. Default true.
264-
*/
265-
$enabled = apply_filters( 'xmlrpc_enabled', $enabled );
266-
267-
if ( ! $enabled ) {
286+
if ( ! $this->is_enabled ) {
268287
$this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) );
269288
return false;
270289
}
@@ -335,6 +354,30 @@ public function escape( &$data ) {
335354
}
336355
}
337356

357+
/**
358+
* Send error response to client.
359+
*
360+
* Send an XML error response to the client. If the endpoint is enabled
361+
* an HTTP 200 response is always sent per the XML-RPC specification.
362+
*
363+
* @since 5.7.3
364+
*
365+
* @param IXR_Error|string $error Error code or an error object.
366+
* @param false $message Error message. Optional.
367+
*/
368+
public function error( $error, $message = false ) {
369+
// Accepts either an error object or an error code and message
370+
if ( $message && ! is_object( $error ) ) {
371+
$error = new IXR_Error( $error, $message );
372+
}
373+
374+
if ( ! $this->is_enabled ) {
375+
status_header( $error->code );
376+
}
377+
378+
$this->output( $error->getXml() );
379+
}
380+
338381
/**
339382
* Retrieve custom fields for post.
340383
*

tests/phpunit/tests/xmlrpc/basic.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,15 @@ function test_enabled() {
1616
$this->assertSame( 403, $result->code );
1717
}
1818

19-
function test_disabled() {
20-
add_filter( 'xmlrpc_enabled', '__return_false' );
21-
22-
$result = $this->myxmlrpcserver->wp_getOptions( array( 1, 'username', 'password' ) );
23-
24-
$this->assertIXRError( $result );
25-
$this->assertSame( 405, $result->code );
26-
}
27-
2819
function test_login_pass_ok() {
29-
$user_id = $this->make_user_by_role( 'subscriber' );
20+
$this->make_user_by_role( 'subscriber' );
3021

3122
$this->assertTrue( $this->myxmlrpcserver->login_pass_ok( 'subscriber', 'subscriber' ) );
3223
$this->assertInstanceOf( 'WP_User', $this->myxmlrpcserver->login( 'subscriber', 'subscriber' ) );
3324
}
3425

3526
function test_login_pass_bad() {
36-
$user_id = $this->make_user_by_role( 'subscriber' );
27+
$this->make_user_by_role( 'subscriber' );
3728

3829
$this->assertFalse( $this->myxmlrpcserver->login_pass_ok( 'username', 'password' ) );
3930
$this->assertFalse( $this->myxmlrpcserver->login( 'username', 'password' ) );
@@ -117,4 +108,13 @@ function test_isStruct_on_non_numerically_indexed_array() {
117108

118109
$this->assertXmlStringEqualsXmlString( $return, $value->getXML() );
119110
}
111+
112+
function test_disabled() {
113+
add_filter( 'xmlrpc_enabled', '__return_false' );
114+
$testcase_xmlrpc_server = new wp_xmlrpc_server();
115+
$result = $testcase_xmlrpc_server->wp_getOptions( array( 1, 'username', 'password' ) );
116+
117+
$this->assertIXRError( $result );
118+
$this->assertSame( 405, $result->code );
119+
}
120120
}

0 commit comments

Comments
 (0)