MDL-34284 library: Import ZF2012-01 security patch for Zend
[moodle.git] / lib / zend / Zend / XmlRpc / Request.php
1 <?php
2 /**
3  * Zend Framework
4  *
5  * LICENSE
6  *
7  * This source file is subject to the new BSD license that is bundled
8  * with this package in the file LICENSE.txt.
9  * It is also available through the world-wide-web at this URL:
10  * http://framework.zend.com/license/new-bsd
11  * If you did not receive a copy of the license and are unable to
12  * obtain it through the world-wide-web, please send an email
13  * to license@zend.com so we can send you a copy immediately.
14  *
15  * @category   Zend
16  * @package    Zend_Controller
17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
19  */
21 /**
22  * Zend_XmlRpc_Value
23  */
24 require_once 'Zend/XmlRpc/Value.php';
26 /**
27  * Zend_XmlRpc_Fault
28  */
29 require_once 'Zend/XmlRpc/Fault.php';
31 /**
32  * XmlRpc Request object
33  *
34  * Encapsulates an XmlRpc request, holding the method call and all parameters.
35  * Provides accessors for these, as well as the ability to load from XML and to
36  * create the XML request string.
37  *
38  * Additionally, if errors occur setting the method or parsing XML, a fault is
39  * generated and stored in {@link $_fault}; developers may check for it using
40  * {@link isFault()} and {@link getFault()}.
41  *
42  * @category Zend
43  * @package  Zend_XmlRpc
44  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
45  * @license    http://framework.zend.com/license/new-bsd     New BSD License
46  * @version $Id$
47  */
48 class Zend_XmlRpc_Request
49 {
50     /**
51      * Request character encoding
52      * @var string
53      */
54     protected $_encoding = 'UTF-8';
56     /**
57      * Method to call
58      * @var string
59      */
60     protected $_method;
62     /**
63      * XML request
64      * @var string
65      */
66     protected $_xml;
68     /**
69      * Method parameters
70      * @var array
71      */
72     protected $_params = array();
74     /**
75      * Fault object, if any
76      * @var Zend_XmlRpc_Fault
77      */
78     protected $_fault = null;
80     /**
81      * XML-RPC type for each param
82      * @var array
83      */
84     protected $_types = array();
86     /**
87      * XML-RPC request params
88      * @var array
89      */
90     protected $_xmlRpcParams = array();
92     /**
93      * Create a new XML-RPC request
94      *
95      * @param string $method (optional)
96      * @param array $params  (optional)
97      */
98     public function __construct($method = null, $params = null)
99     {
100         if ($method !== null) {
101             $this->setMethod($method);
102         }
104         if ($params !== null) {
105             $this->setParams($params);
106         }
107     }
110     /**
111      * Set encoding to use in request
112      *
113      * @param string $encoding
114      * @return Zend_XmlRpc_Request
115      */
116     public function setEncoding($encoding)
117     {
118         $this->_encoding = $encoding;
119         Zend_XmlRpc_Value::setEncoding($encoding);
120         return $this;
121     }
123     /**
124      * Retrieve current request encoding
125      *
126      * @return string
127      */
128     public function getEncoding()
129     {
130         return $this->_encoding;
131     }
133     /**
134      * Set method to call
135      *
136      * @param string $method
137      * @return boolean Returns true on success, false if method name is invalid
138      */
139     public function setMethod($method)
140     {
141         if (!is_string($method) || !preg_match('/^[a-z0-9_.:\/]+$/i', $method)) {
142             $this->_fault = new Zend_XmlRpc_Fault(634, 'Invalid method name ("' . $method . '")');
143             $this->_fault->setEncoding($this->getEncoding());
144             return false;
145         }
147         $this->_method = $method;
148         return true;
149     }
151     /**
152      * Retrieve call method
153      *
154      * @return string
155      */
156     public function getMethod()
157     {
158         return $this->_method;
159     }
161     /**
162      * Add a parameter to the parameter stack
163      *
164      * Adds a parameter to the parameter stack, associating it with the type
165      * $type if provided
166      *
167      * @param mixed $value
168      * @param string $type Optional; type hinting
169      * @return void
170      */
171     public function addParam($value, $type = null)
172     {
173         $this->_params[] = $value;
174         if (null === $type) {
175             // Detect type if not provided explicitly
176             if ($value instanceof Zend_XmlRpc_Value) {
177                 $type = $value->getType();
178             } else {
179                 $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($value);
180                 $type        = $xmlRpcValue->getType();
181             }
182         }
183         $this->_types[]  = $type;
184         $this->_xmlRpcParams[] = array('value' => $value, 'type' => $type);
185     }
187     /**
188      * Set the parameters array
189      *
190      * If called with a single, array value, that array is used to set the
191      * parameters stack. If called with multiple values or a single non-array
192      * value, the arguments are used to set the parameters stack.
193      *
194      * Best is to call with array of the format, in order to allow type hinting
195      * when creating the XMLRPC values for each parameter:
196      * <code>
197      * $array = array(
198      *     array(
199      *         'value' => $value,
200      *         'type'  => $type
201      *     )[, ... ]
202      * );
203      * </code>
204      *
205      * @access public
206      * @return void
207      */
208     public function setParams()
209     {
210         $argc = func_num_args();
211         $argv = func_get_args();
212         if (0 == $argc) {
213             return;
214         }
216         if ((1 == $argc) && is_array($argv[0])) {
217             $params     = array();
218             $types      = array();
219             $wellFormed = true;
220             foreach ($argv[0] as $arg) {
221                 if (!is_array($arg) || !isset($arg['value'])) {
222                     $wellFormed = false;
223                     break;
224                 }
225                 $params[] = $arg['value'];
227                 if (!isset($arg['type'])) {
228                     $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg['value']);
229                     $arg['type'] = $xmlRpcValue->getType();
230                 }
231                 $types[] = $arg['type'];
232             }
233             if ($wellFormed) {
234                 $this->_xmlRpcParams = $argv[0];
235                 $this->_params = $params;
236                 $this->_types  = $types;
237             } else {
238                 $this->_params = $argv[0];
239                 $this->_types  = array();
240                 $xmlRpcParams  = array();
241                 foreach ($argv[0] as $arg) {
242                     if ($arg instanceof Zend_XmlRpc_Value) {
243                         $type = $arg->getType();
244                     } else {
245                         $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg);
246                         $type        = $xmlRpcValue->getType();
247                     }
248                     $xmlRpcParams[] = array('value' => $arg, 'type' => $type);
249                     $this->_types[] = $type;
250                 }
251                 $this->_xmlRpcParams = $xmlRpcParams;
252             }
253             return;
254         }
256         $this->_params = $argv;
257         $this->_types  = array();
258         $xmlRpcParams  = array();
259         foreach ($argv as $arg) {
260             if ($arg instanceof Zend_XmlRpc_Value) {
261                 $type = $arg->getType();
262             } else {
263                 $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg);
264                 $type        = $xmlRpcValue->getType();
265             }
266             $xmlRpcParams[] = array('value' => $arg, 'type' => $type);
267             $this->_types[] = $type;
268         }
269         $this->_xmlRpcParams = $xmlRpcParams;
270     }
272     /**
273      * Retrieve the array of parameters
274      *
275      * @return array
276      */
277     public function getParams()
278     {
279         return $this->_params;
280     }
282     /**
283      * Return parameter types
284      *
285      * @return array
286      */
287     public function getTypes()
288     {
289         return $this->_types;
290     }
292     /**
293      * Load XML and parse into request components
294      *
295      * @param string $request
296      * @return boolean True on success, false if an error occurred.
297      */
298     public function loadXml($request)
299     {
300         if (!is_string($request)) {
301             $this->_fault = new Zend_XmlRpc_Fault(635);
302             $this->_fault->setEncoding($this->getEncoding());
303             return false;
304         }
306         // @see ZF-12293 - disable external entities for security purposes
307         $loadEntities = libxml_disable_entity_loader(true);
308         try {
309             $xml = new SimpleXMLElement($request);
310         } catch (Exception $e) {
311             // Not valid XML
312             $this->_fault = new Zend_XmlRpc_Fault(631);
313             $this->_fault->setEncoding($this->getEncoding());
314             libxml_disable_entity_loader($loadEntities);
315             return false;
316         }
318         // Check for method name
319         if (empty($xml->methodName)) {
320             // Missing method name
321             $this->_fault = new Zend_XmlRpc_Fault(632);
322             $this->_fault->setEncoding($this->getEncoding());
323             libxml_disable_entity_loader($loadEntities);
324             return false;
325         }
327         $this->_method = (string) $xml->methodName;
329         // Check for parameters
330         if (!empty($xml->params)) {
331             $types = array();
332             $argv  = array();
333             foreach ($xml->params->children() as $param) {
334                 if (!isset($param->value)) {
335                     $this->_fault = new Zend_XmlRpc_Fault(633);
336                     $this->_fault->setEncoding($this->getEncoding());
337                     libxml_disable_entity_loader($loadEntities);
338                     return false;
339                 }
341                 try {
342                     $param   = Zend_XmlRpc_Value::getXmlRpcValue($param->value, Zend_XmlRpc_Value::XML_STRING);
343                     $types[] = $param->getType();
344                     $argv[]  = $param->getValue();
345                 } catch (Exception $e) {
346                     $this->_fault = new Zend_XmlRpc_Fault(636);
347                     $this->_fault->setEncoding($this->getEncoding());
348                     libxml_disable_entity_loader($loadEntities);
349                     return false;
350                 }
351             }
353             $this->_types  = $types;
354             $this->_params = $argv;
355         }
357         libxml_disable_entity_loader($loadEntities);
358         $this->_xml = $request;
360         return true;
361     }
363     /**
364      * Does the current request contain errors and should it return a fault
365      * response?
366      *
367      * @return boolean
368      */
369     public function isFault()
370     {
371         return $this->_fault instanceof Zend_XmlRpc_Fault;
372     }
374     /**
375      * Retrieve the fault response, if any
376      *
377      * @return null|Zend_XmlRpc_Fault
378      */
379     public function getFault()
380     {
381         return $this->_fault;
382     }
384     /**
385      * Retrieve method parameters as XMLRPC values
386      *
387      * @return array
388      */
389     protected function _getXmlRpcParams()
390     {
391         $params = array();
392         if (is_array($this->_xmlRpcParams)) {
393             foreach ($this->_xmlRpcParams as $param) {
394                 $value = $param['value'];
395                 $type  = isset($param['type']) ? $param['type'] : Zend_XmlRpc_Value::AUTO_DETECT_TYPE;
397                 if (!$value instanceof Zend_XmlRpc_Value) {
398                     $value = Zend_XmlRpc_Value::getXmlRpcValue($value, $type);
399                 }
400                 $params[] = $value;
401             }
402         }
404         return $params;
405     }
407     /**
408      * Create XML request
409      *
410      * @return string
411      */
412     public function saveXml()
413     {
414         $args   = $this->_getXmlRpcParams();
415         $method = $this->getMethod();
417         $generator = Zend_XmlRpc_Value::getGenerator();
418         $generator->openElement('methodCall')
419                   ->openElement('methodName', $method)
420                   ->closeElement('methodName');
422         if (is_array($args) && count($args)) {
423             $generator->openElement('params');
425             foreach ($args as $arg) {
426                 $generator->openElement('param');
427                 $arg->generateXml();
428                 $generator->closeElement('param');
429             }
430             $generator->closeElement('params');
431         }
432         $generator->closeElement('methodCall');
434         return $generator->flush();
435     }
437     /**
438      * Return XML request
439      *
440      * @return string
441      */
442     public function __toString()
443     {
444         return $this->saveXML();
445     }