diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 8587015e877..5238c056f9a 100644 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -1151,8 +1151,7 @@ public function parseResponse($fp) //------------------------------------- $parser = xml_parser_create($this->xmlrpc_defencoding); - $pname = (string) $parser; - $this->xh[$pname] = array( + $this->xh = array( 'isf' => 0, 'ac' => '', 'headers' => array(), @@ -1175,7 +1174,7 @@ public function parseResponse($fp) { break; } - $this->xh[$pname]['headers'][] = $line; + $this->xh['headers'][] = $line; } $data = implode("\r\n", $lines); @@ -1193,18 +1192,18 @@ public function parseResponse($fp) xml_parser_free($parser); // Got ourselves some badness, it seems - if ($this->xh[$pname]['isf'] > 1) + if ($this->xh['isf'] > 1) { if ($this->debug === TRUE) { - echo "---Invalid Return---\n".$this->xh[$pname]['isf_reason']."---Invalid Return---\n\n"; + echo "---Invalid Return---\n".$this->xh['isf_reason']."---Invalid Return---\n\n"; } - return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']); + return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh['isf_reason']); } - elseif ( ! is_object($this->xh[$pname]['value'])) + elseif ( ! is_object($this->xh['value'])) { - return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']); + return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh['isf_reason']); } // Display XML content for debugging @@ -1212,10 +1211,10 @@ public function parseResponse($fp) { echo '
';
 
-			if (count($this->xh[$pname]['headers']) > 0)
+			if (count($this->xh['headers']) > 0)
 			{
 				echo "---HEADERS---\n";
-				foreach ($this->xh[$pname]['headers'] as $header)
+				foreach ($this->xh['headers'] as $header)
 				{
 					echo $header."\n";
 				}
@@ -1223,13 +1222,13 @@ public function parseResponse($fp)
 			}
 
 			echo "---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n---PARSED---\n";
-			var_dump($this->xh[$pname]['value']);
+			var_dump($this->xh['value']);
 			echo "\n---END PARSED---
"; } // Send response - $v = $this->xh[$pname]['value']; - if ($this->xh[$pname]['isf']) + $v = $this->xh['value']; + if ($this->xh['isf']) { $errno_v = $v->me['struct']['faultCode']; $errstr_v = $v->me['struct']['faultString']; @@ -1248,7 +1247,7 @@ public function parseResponse($fp) $r = new XML_RPC_Response($v); } - $r->headers = $this->xh[$pname]['headers']; + $r->headers = $this->xh['headers']; return $r; } @@ -1279,26 +1278,24 @@ public function parseResponse($fp) */ public function open_tag($the_parser, $name) { - $the_parser = (string) $the_parser; - // If invalid nesting, then return - if ($this->xh[$the_parser]['isf'] > 1) return; + if ($this->xh['isf'] > 1) return; // Evaluate and check for correct nesting of XML elements - if (count($this->xh[$the_parser]['stack']) === 0) + if (count($this->xh['stack']) === 0) { if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL') { - $this->xh[$the_parser]['isf'] = 2; - $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing'; + $this->xh['isf'] = 2; + $this->xh['isf_reason'] = 'Top level XML-RPC element is missing'; return; } } // not top level element: see if parent is OK - elseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE)) + elseif ( ! in_array($this->xh['stack'][0], $this->valid_parents[$name], TRUE)) { - $this->xh[$the_parser]['isf'] = 2; - $this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0]; + $this->xh['isf'] = 2; + $this->xh['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh['stack'][0]; return; } @@ -1308,22 +1305,22 @@ public function open_tag($the_parser, $name) case 'ARRAY': // Creates array for child elements $cur_val = array('value' => array(), 'type' => $name); - array_unshift($this->xh[$the_parser]['valuestack'], $cur_val); + array_unshift($this->xh['valuestack'], $cur_val); break; case 'METHODNAME': case 'NAME': - $this->xh[$the_parser]['ac'] = ''; + $this->xh['ac'] = ''; break; case 'FAULT': - $this->xh[$the_parser]['isf'] = 1; + $this->xh['isf'] = 1; break; case 'PARAM': - $this->xh[$the_parser]['value'] = NULL; + $this->xh['value'] = NULL; break; case 'VALUE': - $this->xh[$the_parser]['vt'] = 'value'; - $this->xh[$the_parser]['ac'] = ''; - $this->xh[$the_parser]['lv'] = 1; + $this->xh['vt'] = 'value'; + $this->xh['ac'] = ''; + $this->xh['lv'] = 1; break; case 'I4': case 'INT': @@ -1332,23 +1329,23 @@ public function open_tag($the_parser, $name) case 'DOUBLE': case 'DATETIME.ISO8601': case 'BASE64': - if ($this->xh[$the_parser]['vt'] !== 'value') + if ($this->xh['vt'] !== 'value') { //two data elements inside a value: an error occurred! - $this->xh[$the_parser]['isf'] = 2; - $this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a ' - .$this->xh[$the_parser]['vt'].' element inside a single value'; + $this->xh['isf'] = 2; + $this->xh['isf_reason'] = 'There is a '.$name.' element following a ' + .$this->xh['vt'].' element inside a single value'; return; } - $this->xh[$the_parser]['ac'] = ''; + $this->xh['ac'] = ''; break; case 'MEMBER': // Set name of to nothing to prevent errors later if no is found - $this->xh[$the_parser]['valuestack'][0]['name'] = ''; + $this->xh['valuestack'][0]['name'] = ''; // Set NULL value to check to see if value passed for this param/member - $this->xh[$the_parser]['value'] = NULL; + $this->xh['value'] = NULL; break; case 'DATA': case 'METHODCALL': @@ -1358,15 +1355,15 @@ public function open_tag($the_parser, $name) break; default: /// An Invalid Element is Found, so we have trouble - $this->xh[$the_parser]['isf'] = 2; - $this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name; + $this->xh['isf'] = 2; + $this->xh['isf_reason'] = 'Invalid XML-RPC element found: '.$name; break; } // Add current element name to stack, to allow validation of nesting - array_unshift($this->xh[$the_parser]['stack'], $name); + array_unshift($this->xh['stack'], $name); - $name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0; + $name === 'VALUE' OR $this->xh['lv'] = 0; } // -------------------------------------------------------------------- @@ -1380,27 +1377,25 @@ public function open_tag($the_parser, $name) */ public function closing_tag($the_parser, $name) { - $the_parser = (string) $the_parser; - - if ($this->xh[$the_parser]['isf'] > 1) return; + if ($this->xh['isf'] > 1) return; // Remove current element from stack and set variable // NOTE: If the XML validates, then we do not have to worry about // the opening and closing of elements. Nesting is checked on the opening // tag so we be safe there as well. - $curr_elem = array_shift($this->xh[$the_parser]['stack']); + $curr_elem = array_shift($this->xh['stack']); switch ($name) { case 'STRUCT': case 'ARRAY': - $cur_val = array_shift($this->xh[$the_parser]['valuestack']); - $this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array(); - $this->xh[$the_parser]['vt'] = strtolower($name); + $cur_val = array_shift($this->xh['valuestack']); + $this->xh['value'] = isset($cur_val['values']) ? $cur_val['values'] : array(); + $this->xh['vt'] = strtolower($name); break; case 'NAME': - $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac']; + $this->xh['valuestack'][0]['name'] = $this->xh['ac']; break; case 'BOOLEAN': case 'I4': @@ -1409,87 +1404,87 @@ public function closing_tag($the_parser, $name) case 'DOUBLE': case 'DATETIME.ISO8601': case 'BASE64': - $this->xh[$the_parser]['vt'] = strtolower($name); + $this->xh['vt'] = strtolower($name); if ($name === 'STRING') { - $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; + $this->xh['value'] = $this->xh['ac']; } elseif ($name === 'DATETIME.ISO8601') { - $this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime; - $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; + $this->xh['vt'] = $this->xmlrpcDateTime; + $this->xh['value'] = $this->xh['ac']; } elseif ($name === 'BASE64') { - $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']); + $this->xh['value'] = base64_decode($this->xh['ac']); } elseif ($name === 'BOOLEAN') { // Translated BOOLEAN values to TRUE AND FALSE - $this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac']; + $this->xh['value'] = (bool) $this->xh['ac']; } elseif ($name=='DOUBLE') { // we have a DOUBLE // we must check that only 0123456789-. are characters here - $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']) - ? (float) $this->xh[$the_parser]['ac'] + $this->xh['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh['ac']) + ? (float) $this->xh['ac'] : 'ERROR_NON_NUMERIC_FOUND'; } else { // we have an I4/INT // we must check that only 0123456789- are characters here - $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']) - ? (int) $this->xh[$the_parser]['ac'] + $this->xh['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh['ac']) + ? (int) $this->xh['ac'] : 'ERROR_NON_NUMERIC_FOUND'; } - $this->xh[$the_parser]['ac'] = ''; - $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value + $this->xh['ac'] = ''; + $this->xh['lv'] = 3; // indicate we've found a value break; case 'VALUE': // This if() detects if no scalar was inside - if ($this->xh[$the_parser]['vt'] == 'value') + if ($this->xh['vt'] == 'value') { - $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; - $this->xh[$the_parser]['vt'] = $this->xmlrpcString; + $this->xh['value'] = $this->xh['ac']; + $this->xh['vt'] = $this->xmlrpcString; } // build the XML-RPC value out of the data received, and substitute it - $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']); + $temp = new XML_RPC_Values($this->xh['value'], $this->xh['vt']); - if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY') + if (count($this->xh['valuestack']) && $this->xh['valuestack'][0]['type'] === 'ARRAY') { // Array - $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp; + $this->xh['valuestack'][0]['values'][] = $temp; } else { // Struct - $this->xh[$the_parser]['value'] = $temp; + $this->xh['value'] = $temp; } break; case 'MEMBER': - $this->xh[$the_parser]['ac'] = ''; + $this->xh['ac'] = ''; // If value add to array in the stack for the last element built - if ($this->xh[$the_parser]['value']) + if ($this->xh['value']) { - $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value']; + $this->xh['valuestack'][0]['values'][$this->xh['valuestack'][0]['name']] = $this->xh['value']; } break; case 'DATA': - $this->xh[$the_parser]['ac'] = ''; + $this->xh['ac'] = ''; break; case 'PARAM': - if ($this->xh[$the_parser]['value']) + if ($this->xh['value']) { - $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value']; + $this->xh['params'][] = $this->xh['value']; } break; case 'METHODNAME': - $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']); + $this->xh['method'] = ltrim($this->xh['ac']); break; case 'PARAMS': case 'FAULT': @@ -1514,24 +1509,22 @@ public function closing_tag($the_parser, $name) */ public function character_data($the_parser, $data) { - $the_parser = (string) $the_parser; - - if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already + if ($this->xh['isf'] > 1) return; // XML Fault found already // If a value has not been found - if ($this->xh[$the_parser]['lv'] !== 3) + if ($this->xh['lv'] !== 3) { - if ($this->xh[$the_parser]['lv'] === 1) + if ($this->xh['lv'] === 1) { - $this->xh[$the_parser]['lv'] = 2; // Found a value + $this->xh['lv'] = 2; // Found a value } - if ( ! isset($this->xh[$the_parser]['ac'])) + if ( ! isset($this->xh['ac'])) { - $this->xh[$the_parser]['ac'] = ''; + $this->xh['ac'] = ''; } - $this->xh[$the_parser]['ac'] .= $data; + $this->xh['ac'] .= $data; } } diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index eb5a24c4917..481938b3400 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -234,9 +234,8 @@ public function parseRequest($data = '') $parser = xml_parser_create($this->xmlrpc_defencoding); $parser_object = new XML_RPC_Message('filler'); - $pname = (string) $parser; - $parser_object->xh[$pname] = array( + $parser_object->xh = array( 'isf' => 0, 'isf_reason' => '', 'params' => array(), @@ -265,7 +264,7 @@ public function parseRequest($data = '') xml_get_current_line_number($parser))); xml_parser_free($parser); } - elseif ($parser_object->xh[$pname]['isf']) + elseif ($parser_object->xh['isf']) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); } @@ -273,17 +272,17 @@ public function parseRequest($data = '') { xml_parser_free($parser); - $m = new XML_RPC_Message($parser_object->xh[$pname]['method']); + $m = new XML_RPC_Message($parser_object->xh['method']); $plist = ''; - for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++) + for ($i = 0, $c = count($parser_object->xh['params']); $i < $c; $i++) { if ($this->debug === TRUE) { - $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n"; + $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh['params'][$i]), TRUE).";\n"; } - $m->addParam($parser_object->xh[$pname]['params'][$i]); + $m->addParam($parser_object->xh['params'][$i]); } if ($this->debug === TRUE) diff --git a/tests/codeigniter/libraries/Xmlrpc_test.php b/tests/codeigniter/libraries/Xmlrpc_test.php new file mode 100644 index 00000000000..46d73bf6e3e --- /dev/null +++ b/tests/codeigniter/libraries/Xmlrpc_test.php @@ -0,0 +1,130 @@ +input = new CI_Input($security); + + $this->input_lib_raw_stream = new ReflectionProperty($this->input, '_raw_input_stream'); + $this->input_lib_raw_stream->setAccessible(TRUE); + + $this->ci_instance_var('input', $this->input); + $this->ci_instance_var('security', $security); + } + + // -------------------------------------------------------------------- + + public function test_xmlrpc_client() + { + $xmlrpc = new Mock_Libraries_Xmlrpc(); + $xmlrpc->server('http://rpc.test/'); + $xmlrpc->method('testcontroller.test'); + + $request = array('My Blog', 'http://www.myrpc.com/test/'); + $message = 'test'.time(); + $xml_response = $this->xml_response($message); + $xmlrpc->client->mock_response = "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\nContent-Length: ".strlen($xml_response)."\r\n\r\n$xml_response"; + + // Perform in the same request multiple calls + for ($attempt = 1; $attempt <= 2; $attempt++) + { + $xmlrpc->request($request); + + $this->assertTrue($xmlrpc->send_request()); + + $response = $xmlrpc->display_response(); + + $this->assertEquals('theuser', $response['name']); + $this->assertEquals(123435, $response['member_id']); + $this->assertEquals($message, $response['request']); + } + } + + // -------------------------------------------------------------------- + + public function test_xmlrpc_server() + { + $xmlrpcs = new Mock_Libraries_Xmlrpcs(); + + $config['functions']['Testmethod'] = array('function' => __CLASS__.'.mock_method_new_entry'); + $config['object'] = $this; + + $xmlrpcs->initialize($config); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->input_lib_raw_stream->setValue($this->input, $this->xml_request()); + + $xmlrpcs->serve(); + + $this->assertEquals('Test', $this->method_param); + } + + // -------------------------------------------------------------------- + + /** + * @param XML_RPC_Message $param + */ + public function mock_method_new_entry($param) + { + $this->method_param = $param->params[0]->scalarval(); + + return new XML_RPC_Response(new XML_RPC_Values(true, 'boolean')); + } + + // -------------------------------------------------------------------- + + private function xml_response($message) + { + return ' + + + + + + +name + +theuser + + + +member_id + +123435 + + + +request + +'.$message.' + + + + + +'; + } + + // -------------------------------------------------------------------- + + public function xml_request() + { + return ' + +Testmethod + + + +Test + + + +'; + } +} diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php index 4dd53d4af31..ce3454dc9ed 100644 --- a/tests/mocks/autoloader.php +++ b/tests/mocks/autoloader.php @@ -52,6 +52,7 @@ function autoload($class) 'Upload', 'User_agent', 'Xmlrpc', + 'Xmlrpcs', 'Zip' ); diff --git a/tests/mocks/libraries/xmlrpc.php b/tests/mocks/libraries/xmlrpc.php new file mode 100644 index 00000000000..32f5f3765d5 --- /dev/null +++ b/tests/mocks/libraries/xmlrpc.php @@ -0,0 +1,33 @@ +client = new Mock_Libraries_XML_RPC_Client('/', $url, $port, $proxy, $proxy_port); + } +} + +class Mock_Libraries_XML_RPC_Client extends XML_RPC_Client { + public $mock_response = ''; + + /** + * @param XML_RPC_Message $msg + */ + public function sendPayload($msg) + { + if (empty($msg->payload)) + { + $msg->createPayload(); + } + + $fp = fopen('php://memory', 'rw+'); + fwrite($fp, $this->mock_response); + fseek($fp, 0); + + $parsed = $msg->parseResponse($fp); + fclose($fp); + + return $parsed; + } +} + diff --git a/tests/mocks/libraries/xmlrpcs.php b/tests/mocks/libraries/xmlrpcs.php new file mode 100644 index 00000000000..d93396c0189 --- /dev/null +++ b/tests/mocks/libraries/xmlrpcs.php @@ -0,0 +1,23 @@ +parseRequest(); + + $payload = 'xmlrpc_defencoding.'"?'.'>'."\n".$this->debug_msg.$r->prepare_response(); + + $this->mock_payload = "HTTP/1.1 200 OK\r\n"; + $this->mock_payload .= "Content-Type: text/xml\r\n"; + $this->mock_payload .= 'Content-Length: '.strlen($payload)."\r\n"; + + $this->mock_payload .= "\r\n"; + + $this->mock_payload .= $payload; + } +}