MDL-16094 File storage conversion Quiz and Questions
[moodle.git] / mnet / xmlrpc / xmlparser.php
CommitLineData
71558f85 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 */
14class mnet_encxml_parser {
15 /**
16 * Constructor creates and initialises parser resource and calls initialise
17 *
18 * @return bool True
19 */
20 function mnet_encxml_parser() {
21 return $this->initialise();
22 }
23
24 /**
25 * Set default element handlers and initialise properties to empty.
26 *
27 * @return bool True
28 */
29 function initialise() {
30 $this->parser = xml_parser_create();
31 xml_set_object($this->parser, $this);
32
33 xml_set_element_handler($this->parser, "start_element", "end_element");
34 xml_set_character_data_handler($this->parser, "discard_data");
35
66ebd55b 36 $this->tag_number = 0; // Just a unique ID for each tag
37 $this->digest = '';
f0e4c270 38 $this->remote_timestamp = '';
66ebd55b 39 $this->remote_wwwroot = '';
40 $this->signature = '';
41 $this->data_object = '';
42 $this->key_URI = '';
71558f85 43 $this->payload_encrypted = false;
66ebd55b 44 $this->cipher = array();
fd7756e4 45 $this->error = array();
a5248bee 46 $this->remoteerror = null;
47 $this->errorstarted = false;
71558f85 48 return true;
49 }
50
51 /**
52 * Parse a block of XML text
53 *
54 * The XML Text will be an XML-RPC request which is wrapped in an XML doc
55 * with a signature from the sender. This envelope may be encrypted and
56 * delivered within another XML envelope with a symmetric key. The parser
57 * should first decrypt this XML, and then place the XML-RPC request into
58 * the data_object property, and the signature into the signature property.
59 *
60 * See the W3C's {@link http://www.w3.org/TR/xmlenc-core/ XML Encryption Syntax and Processing}
61 * and {@link http://www.w3.org/TR/2001/PR-xmldsig-core-20010820/ XML-Signature Syntax and Processing}
62 * guidelines for more detail on the XML.
63 *
64 * -----XML-Envelope---------------------------------
65 * | |
66 * | Symmetric-key-------------------------- |
67 * | |_____________________________________| |
68 * | |
69 * | Encrypted data------------------------- |
70 * | | | |
71 * | | -XML-Envelope------------------ | |
72 * | | | | | |
73 * | | | --Signature------------- | | |
74 * | | | |______________________| | | |
75 * | | | | | |
76 * | | | --Signed-Payload-------- | | |
77 * | | | | | | | |
78 * | | | | XML-RPC Request | | | |
79 * | | | |______________________| | | |
80 * | | | | | |
81 * | | |_____________________________| | |
82 * | |_____________________________________| |
83 * | |
84 * |________________________________________________|
85 *
71558f85 86 * @param string $data The XML that you want to parse
87 * @return bool True on success - false on failure
88 */
89 function parse($data) {
71558f85 90 $p = xml_parse($this->parser, $data);
91
fd7756e4 92 if ($p == 0) {
93 // Parse failed
94 $errcode = xml_get_error_code($this->parser);
95 $errstring = xml_error_string($errcode);
d60a9e40 96 $lineno = xml_get_current_line_number($this->parser);
97 if ($lineno !== false) {
98 $error = array('lineno' => $lineno);
99 $lineno--; // Line numbering starts at 1.
100 while ($lineno > 0) {
101 $data = strstr($data, "\n");
102 $lineno--;
103 }
104 $data .= "\n"; // In case there's only one line (no newline)
105 $line = substr($data, 0, strpos($data, "\n"));
106 $error['code'] = $errcode;
107 $error['string'] = $errstring;
108 $error['line'] = $line;
109 $this->error[] = $error;
110 } else {
111 $this->error[] = array('code' => $errcode, 'string' => $errstring);
112 }
fd7756e4 113 }
114
a5248bee 115 if (!empty($this->remoteerror)) {
116 return false;
117 }
118
71558f85 119 if (count($this->cipher) > 0) {
82b29ea1 120 $this->cipher = array_values($this->cipher);
71558f85 121 $this->payload_encrypted = true;
122 }
123
124 return (bool)$p;
125 }
126
127 /**
128 * Destroy the parser and free up any related resource.
129 */
130 function free_resource() {
131 $free = xml_parser_free($this->parser);
132 }
133
134 /**
135 * Set the character-data handler to the right function for each element
136 *
137 * For each tag (element) name, this function switches the character-data
138 * handler to the function that handles that element. Note that character
139 * data is referred to the handler in blocks of 1024 bytes.
140 *
141 * @param mixed $parser The XML parser
142 * @param string $name The name of the tag, e.g. method_call
143 * @param array $attrs The tag's attributes (if any exist).
144 * @return bool True
145 */
146 function start_element($parser, $name, $attrs) {
147 $this->tag_number++;
148 $handler = 'discard_data';
149 switch(strtoupper($name)) {
150 case 'DIGESTVALUE':
151 $handler = 'parse_digest';
152 break;
153 case 'SIGNATUREVALUE':
154 $handler = 'parse_signature';
155 break;
156 case 'OBJECT':
157 $handler = 'parse_object';
158 break;
159 case 'RETRIEVALMETHOD':
160 $this->key_URI = $attrs['URI'];
161 break;
f0e4c270 162 case 'TIMESTAMP':
163 $handler = 'parse_timestamp';
164 break;
71558f85 165 case 'WWWROOT':
166 $handler = 'parse_wwwroot';
167 break;
168 case 'CIPHERVALUE':
169 $this->cipher[$this->tag_number] = '';
170 $handler = 'parse_cipher';
171 break;
a5248bee 172 case 'FAULT':
173 $handler = 'parse_fault';
71558f85 174 default:
175 break;
176 }
177 xml_set_character_data_handler($this->parser, $handler);
178 return true;
179 }
180
f0e4c270 181 /**
182 * Add the next chunk of character data to the remote_timestamp string
183 *
184 * @param mixed $parser The XML parser
185 * @param string $data The content of the current tag (1024 byte chunk)
186 * @return bool True
187 */
188 function parse_timestamp($parser, $data) {
189 $this->remote_timestamp .= $data;
190 return true;
191 }
192
71558f85 193 /**
194 * Add the next chunk of character data to the cipher string for that tag
195 *
196 * The XML parser calls the character-data handler with 1024-character
197 * chunks of data. This means that the handler may be called several times
198 * for a single tag, so we use the concatenate operator (.) to build the
199 * tag content into a string.
200 * We should not encounter more than one of each tag type, except for the
201 * cipher tag. We will often see two of those. We prevent the content of
202 * these two tags being concatenated together by counting each tag, and
203 * using its 'number' as the key to an array of ciphers.
204 *
205 * @param mixed $parser The XML parser
206 * @param string $data The content of the current tag (1024 byte chunk)
207 * @return bool True
208 */
209 function parse_cipher($parser, $data) {
210 $this->cipher[$this->tag_number] .= $data;
211 return true;
212 }
213
214 /**
215 * Add the next chunk of character data to the remote_wwwroot string
216 *
217 * @param mixed $parser The XML parser
218 * @param string $data The content of the current tag (1024 byte chunk)
219 * @return bool True
220 */
221 function parse_wwwroot($parser, $data) {
222 $this->remote_wwwroot .= $data;
223 return true;
224 }
225
226 /**
227 * Add the next chunk of character data to the digest string
228 *
229 * @param mixed $parser The XML parser
230 * @param string $data The content of the current tag (1024 byte chunk)
231 * @return bool True
232 */
233 function parse_digest($parser, $data) {
234 $this->digest .= $data;
235 return true;
236 }
237
238 /**
239 * Add the next chunk of character data to the signature string
240 *
241 * @param mixed $parser The XML parser
242 * @param string $data The content of the current tag (1024 byte chunk)
243 * @return bool True
244 */
245 function parse_signature($parser, $data) {
246 $this->signature .= $data;
247 return true;
248 }
249
250 /**
251 * Add the next chunk of character data to the data_object string
252 *
253 * @param mixed $parser The XML parser
254 * @param string $data The content of the current tag (1024 byte chunk)
255 * @return bool True
256 */
257 function parse_object($parser, $data) {
258 $this->data_object .= $data;
259 return true;
260 }
261
262 /**
263 * Discard the next chunk of character data
264 *
265 * This is used for tags that we're not interested in.
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 discard_data($parser, $data) {
a5248bee 272 if (!$this->errorstarted) {
273 // Not interested
274 return true;
275 }
276 $data = trim($data);
277 if (isset($this->errorstarted->faultstringstarted) && !empty($data)) {
278 $this->remoteerror .= ', message: ' . $data;
279 } else if (isset($this->errorstarted->faultcodestarted)) {
280 $this->remoteerror = 'code: ' . $data;
281 unset($this->errorstarted->faultcodestarted);
282 } else if ($data == 'faultCode') {
283 $this->errorstarted->faultcodestarted = true;
284 } else if ($data == 'faultString') {
285 $this->errorstarted->faultstringstarted = true;
286 }
287 return true;
288
289 }
290
291 function parse_fault($parser, $data) {
292 $this->errorstarted = new StdClass;
71558f85 293 return true;
294 }
295
296 /**
297 * Switch the character-data handler to ignore the next chunk of data
298 *
299 * @param mixed $parser The XML parser
300 * @param string $name The name of the tag, e.g. method_call
301 * @return bool True
302 */
303 function end_element($parser, $name) {
304 $ok = xml_set_character_data_handler($this->parser, "discard_data");
305 return true;
306 }
307}