Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 /** 3 * Custom XML parser for signed and/or encrypted XML Docs 4 * 5 * @author Donal McMullan donal@catalyst.net.nz 6 * @version 0.0.1 7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 8 * @package mnet 9 */ 10 11 /** 12 * Custom XML parser class for signed and/or encrypted XML Docs 13 */ 14 class mnet_encxml_parser { 15 16 /** @var resource|false|XMLParser — a resource handle for the new XML parser. */ 17 private $parser; 18 19 /** @var int unique ID for each tag. */ 20 private $tag_number; 21 22 /** @var string digest string. */ 23 private $digest; 24 25 /** @var string remote_timestamp string. */ 26 public $remote_timestamp; 27 28 /** @var string remote_wwwroot string. */ 29 public $remote_wwwroot; 30 31 /** @var string signature string. */ 32 public $signature; 33 34 /** @var string data_object string. */ 35 public $data_object; 36 37 /** @var string URI value inside the RETRIEVALMETHOD xml tag. */ 38 private $key_URI; 39 40 /** @var bool true if $chiper has a value, otherwise false. */ 41 public $payload_encrypted; 42 43 /** @var array the chiper string. */ 44 public $cipher = []; 45 46 /** @var array error information with code and string keys. */ 47 public $error = []; 48 49 /** @var string The remote error string, specified in the discard_data(). */ 50 public $remoteerror; 51 52 /** @var stdClass error started status. */ 53 private $errorstarted; 54 55 /** 56 * Constructor creates and initialises parser resource and calls initialise 57 * 58 * @return bool True 59 */ 60 public function __construct() { 61 return $this->initialise(); 62 } 63 64 /** 65 * Old syntax of class constructor. Deprecated in PHP7. 66 * 67 * @deprecated since Moodle 3.1 68 */ 69 public function mnet_encxml_parser() { 70 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 71 self::__construct(); 72 } 73 74 /** 75 * Set default element handlers and initialise properties to empty. 76 * 77 * @return bool True 78 */ 79 function initialise() { 80 $this->parser = xml_parser_create(); 81 xml_set_object($this->parser, $this); 82 83 xml_set_element_handler($this->parser, "start_element", "end_element"); 84 xml_set_character_data_handler($this->parser, "discard_data"); 85 86 $this->tag_number = 0; // Just a unique ID for each tag 87 $this->digest = ''; 88 $this->remote_timestamp = ''; 89 $this->remote_wwwroot = ''; 90 $this->signature = ''; 91 $this->data_object = ''; 92 $this->key_URI = ''; 93 $this->payload_encrypted = false; 94 $this->cipher = array(); 95 $this->error = array(); 96 $this->remoteerror = null; 97 $this->errorstarted = false; 98 return true; 99 } 100 101 /** 102 * Parse a block of XML text 103 * 104 * The XML Text will be an XML-RPC request which is wrapped in an XML doc 105 * with a signature from the sender. This envelope may be encrypted and 106 * delivered within another XML envelope with a symmetric key. The parser 107 * should first decrypt this XML, and then place the XML-RPC request into 108 * the data_object property, and the signature into the signature property. 109 * 110 * See the W3C's {@link http://www.w3.org/TR/xmlenc-core/ XML Encryption Syntax and Processing} 111 * and {@link http://www.w3.org/TR/2001/PR-xmldsig-core-20010820/ XML-Signature Syntax and Processing} 112 * guidelines for more detail on the XML. 113 * 114 * -----XML-Envelope--------------------------------- 115 * | | 116 * | Symmetric-key-------------------------- | 117 * | |_____________________________________| | 118 * | | 119 * | Encrypted data------------------------- | 120 * | | | | 121 * | | -XML-Envelope------------------ | | 122 * | | | | | | 123 * | | | --Signature------------- | | | 124 * | | | |______________________| | | | 125 * | | | | | | 126 * | | | --Signed-Payload-------- | | | 127 * | | | | | | | | 128 * | | | | XML-RPC Request | | | | 129 * | | | |______________________| | | | 130 * | | | | | | 131 * | | |_____________________________| | | 132 * | |_____________________________________| | 133 * | | 134 * |________________________________________________| 135 * 136 * @param string $data The XML that you want to parse 137 * @return bool True on success - false on failure 138 */ 139 function parse($data) { 140 $p = xml_parse($this->parser, $data); 141 142 if ($p == 0) { 143 // Parse failed 144 $errcode = xml_get_error_code($this->parser); 145 $errstring = xml_error_string($errcode); 146 $lineno = xml_get_current_line_number($this->parser); 147 if ($lineno !== false) { 148 $error = array('lineno' => $lineno); 149 $lineno--; // Line numbering starts at 1. 150 while ($lineno > 0) { 151 $data = strstr($data, "\n"); 152 $lineno--; 153 } 154 $data .= "\n"; // In case there's only one line (no newline) 155 $line = substr($data, 0, strpos($data, "\n")); 156 $error['code'] = $errcode; 157 $error['string'] = $errstring; 158 $error['line'] = $line; 159 $this->error[] = $error; 160 } else { 161 $this->error[] = array('code' => $errcode, 'string' => $errstring); 162 } 163 } 164 165 if (!empty($this->remoteerror)) { 166 return false; 167 } 168 169 if (count($this->cipher) > 0) { 170 $this->cipher = array_values($this->cipher); 171 $this->payload_encrypted = true; 172 } 173 174 return (bool)$p; 175 } 176 177 /** 178 * Destroy the parser and free up any related resource. 179 */ 180 function free_resource() { 181 $free = xml_parser_free($this->parser); 182 } 183 184 /** 185 * Set the character-data handler to the right function for each element 186 * 187 * For each tag (element) name, this function switches the character-data 188 * handler to the function that handles that element. Note that character 189 * data is referred to the handler in blocks of 1024 bytes. 190 * 191 * @param mixed $parser The XML parser 192 * @param string $name The name of the tag, e.g. method_call 193 * @param array $attrs The tag's attributes (if any exist). 194 * @return bool True 195 */ 196 function start_element($parser, $name, $attrs) { 197 $this->tag_number++; 198 $handler = 'discard_data'; 199 switch(strtoupper($name)) { 200 case 'DIGESTVALUE': 201 $handler = 'parse_digest'; 202 break; 203 case 'SIGNATUREVALUE': 204 $handler = 'parse_signature'; 205 break; 206 case 'OBJECT': 207 $handler = 'parse_object'; 208 break; 209 case 'RETRIEVALMETHOD': 210 $this->key_URI = $attrs['URI']; 211 break; 212 case 'TIMESTAMP': 213 $handler = 'parse_timestamp'; 214 break; 215 case 'WWWROOT': 216 $handler = 'parse_wwwroot'; 217 break; 218 case 'CIPHERVALUE': 219 $this->cipher[$this->tag_number] = ''; 220 $handler = 'parse_cipher'; 221 break; 222 case 'FAULT': 223 $handler = 'parse_fault'; 224 default: 225 break; 226 } 227 xml_set_character_data_handler($this->parser, $handler); 228 return true; 229 } 230 231 /** 232 * Add the next chunk of character data to the remote_timestamp string 233 * 234 * @param mixed $parser The XML parser 235 * @param string $data The content of the current tag (1024 byte chunk) 236 * @return bool True 237 */ 238 function parse_timestamp($parser, $data) { 239 $this->remote_timestamp .= $data; 240 return true; 241 } 242 243 /** 244 * Add the next chunk of character data to the cipher string for that tag 245 * 246 * The XML parser calls the character-data handler with 1024-character 247 * chunks of data. This means that the handler may be called several times 248 * for a single tag, so we use the concatenate operator (.) to build the 249 * tag content into a string. 250 * We should not encounter more than one of each tag type, except for the 251 * cipher tag. We will often see two of those. We prevent the content of 252 * these two tags being concatenated together by counting each tag, and 253 * using its 'number' as the key to an array of ciphers. 254 * 255 * @param mixed $parser The XML parser 256 * @param string $data The content of the current tag (1024 byte chunk) 257 * @return bool True 258 */ 259 function parse_cipher($parser, $data) { 260 $this->cipher[$this->tag_number] .= $data; 261 return true; 262 } 263 264 /** 265 * Add the next chunk of character data to the remote_wwwroot string 266 * 267 * @param mixed $parser The XML parser 268 * @param string $data The content of the current tag (1024 byte chunk) 269 * @return bool True 270 */ 271 function parse_wwwroot($parser, $data) { 272 $this->remote_wwwroot .= $data; 273 return true; 274 } 275 276 /** 277 * Add the next chunk of character data to the digest string 278 * 279 * @param mixed $parser The XML parser 280 * @param string $data The content of the current tag (1024 byte chunk) 281 * @return bool True 282 */ 283 function parse_digest($parser, $data) { 284 $this->digest .= $data; 285 return true; 286 } 287 288 /** 289 * Add the next chunk of character data to the signature string 290 * 291 * @param mixed $parser The XML parser 292 * @param string $data The content of the current tag (1024 byte chunk) 293 * @return bool True 294 */ 295 function parse_signature($parser, $data) { 296 $this->signature .= $data; 297 return true; 298 } 299 300 /** 301 * Add the next chunk of character data to the data_object string 302 * 303 * @param mixed $parser The XML parser 304 * @param string $data The content of the current tag (1024 byte chunk) 305 * @return bool True 306 */ 307 function parse_object($parser, $data) { 308 $this->data_object .= $data; 309 return true; 310 } 311 312 /** 313 * Discard the next chunk of character data 314 * 315 * This is used for tags that we're not interested in. 316 * 317 * @param mixed $parser The XML parser 318 * @param string $data The content of the current tag (1024 byte chunk) 319 * @return bool True 320 */ 321 function discard_data($parser, $data) { 322 if (!$this->errorstarted) { 323 // Not interested 324 return true; 325 } 326 $data = trim($data); 327 if (isset($this->errorstarted->faultstringstarted) && !empty($data)) { 328 $this->remoteerror .= ', message: ' . $data; 329 } else if (isset($this->errorstarted->faultcodestarted)) { 330 $this->remoteerror = 'code: ' . $data; 331 unset($this->errorstarted->faultcodestarted); 332 } else if ($data == 'faultCode') { 333 $this->errorstarted->faultcodestarted = true; 334 } else if ($data == 'faultString') { 335 $this->errorstarted->faultstringstarted = true; 336 } 337 return true; 338 339 } 340 341 function parse_fault($parser, $data) { 342 $this->errorstarted = new StdClass; 343 return true; 344 } 345 346 /** 347 * Switch the character-data handler to ignore the next chunk of data 348 * 349 * @param mixed $parser The XML parser 350 * @param string $name The name of the tag, e.g. method_call 351 * @return bool True 352 */ 353 function end_element($parser, $name) { 354 $ok = xml_set_character_data_handler($this->parser, "discard_data"); 355 return true; 356 } 357 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body