Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
1441 ariadna 82
        xml_set_element_handler($this->parser, [$this, "start_element"], [$this, "end_element"]);
83
        xml_set_character_data_handler($this->parser, [$this, "discard_data"]);
1 efrain 84
 
85
        $this->tag_number        = 0; // Just a unique ID for each tag
86
        $this->digest            = '';
87
        $this->remote_timestamp  = '';
88
        $this->remote_wwwroot    = '';
89
        $this->signature         = '';
90
        $this->data_object       = '';
91
        $this->key_URI           = '';
92
        $this->payload_encrypted = false;
93
        $this->cipher            = array();
94
        $this->error             = array();
95
        $this->remoteerror       = null;
96
        $this->errorstarted      = false;
97
        return true;
98
    }
99
 
100
    /**
101
     * Parse a block of XML text
102
     *
103
     * The XML Text will be an XML-RPC request which is wrapped in an XML doc
104
     * with a signature from the sender. This envelope may be encrypted and
105
     * delivered within another XML envelope with a symmetric key. The parser
106
     * should first decrypt this XML, and then place the XML-RPC request into
107
     * the data_object property, and the signature into the signature property.
108
     *
109
     * See the W3C's {@link http://www.w3.org/TR/xmlenc-core/ XML Encryption Syntax and Processing}
110
     * and {@link http://www.w3.org/TR/2001/PR-xmldsig-core-20010820/ XML-Signature Syntax and Processing}
111
     * guidelines for more detail on the XML.
112
     *
113
     * -----XML-Envelope---------------------------------
114
     * |                                                |
115
     * |    Symmetric-key--------------------------     |
116
     * |    |_____________________________________|     |
117
     * |                                                |
118
     * |    Encrypted data-------------------------     |
119
     * |    |                                     |     |
120
     * |    |  -XML-Envelope------------------    |     |
121
     * |    |  |                             |    |     |
122
     * |    |  |  --Signature-------------   |    |     |
123
     * |    |  |  |______________________|   |    |     |
124
     * |    |  |                             |    |     |
125
     * |    |  |  --Signed-Payload--------   |    |     |
126
     * |    |  |  |                      |   |    |     |
127
     * |    |  |  |   XML-RPC Request    |   |    |     |
128
     * |    |  |  |______________________|   |    |     |
129
     * |    |  |                             |    |     |
130
     * |    |  |_____________________________|    |     |
131
     * |    |_____________________________________|     |
132
     * |                                                |
133
     * |________________________________________________|
134
     *
135
     * @param   string  $data   The XML that you want to parse
136
     * @return  bool            True on success - false on failure
137
     */
138
    function parse($data) {
139
        $p = xml_parse($this->parser, $data);
140
 
141
        if ($p == 0) {
142
            // Parse failed
143
            $errcode = xml_get_error_code($this->parser);
144
            $errstring = xml_error_string($errcode);
145
            $lineno = xml_get_current_line_number($this->parser);
146
            if ($lineno !== false) {
147
                $error = array('lineno' => $lineno);
148
                $lineno--; // Line numbering starts at 1.
149
                while ($lineno > 0) {
150
                    $data = strstr($data, "\n");
151
                    $lineno--;
152
                }
153
                $data .= "\n"; // In case there's only one line (no newline)
154
                $line = substr($data, 0, strpos($data, "\n"));
155
                $error['code']   = $errcode;
156
                $error['string'] = $errstring;
157
                $error['line']   = $line;
158
                $this->error[] = $error;
159
            } else {
160
                $this->error[] = array('code' => $errcode, 'string' => $errstring);
161
            }
162
        }
163
 
164
        if (!empty($this->remoteerror)) {
165
            return false;
166
        }
167
 
168
        if (count($this->cipher) > 0) {
169
            $this->cipher = array_values($this->cipher);
170
            $this->payload_encrypted = true;
171
        }
172
 
173
        return (bool)$p;
174
    }
175
 
176
    /**
177
     * Destroy the parser and free up any related resource.
178
     */
179
    function free_resource() {
180
        $free = xml_parser_free($this->parser);
181
    }
182
 
183
    /**
184
     * Set the character-data handler to the right function for each element
185
     *
186
     * For each tag (element) name, this function switches the character-data
187
     * handler to the function that handles that element. Note that character
188
     * data is referred to the handler in blocks of 1024 bytes.
189
     *
190
     * @param   mixed   $parser The XML parser
191
     * @param   string  $name   The name of the tag, e.g. method_call
192
     * @param   array   $attrs  The tag's attributes (if any exist).
193
     * @return  bool            True
194
     */
195
    function start_element($parser, $name, $attrs) {
196
        $this->tag_number++;
197
        $handler = 'discard_data';
198
        switch(strtoupper($name)) {
199
            case 'DIGESTVALUE':
200
                $handler = 'parse_digest';
201
                break;
202
            case 'SIGNATUREVALUE':
203
                $handler = 'parse_signature';
204
                break;
205
            case 'OBJECT':
206
                $handler = 'parse_object';
207
                break;
208
            case 'RETRIEVALMETHOD':
209
                $this->key_URI = $attrs['URI'];
210
                break;
211
            case 'TIMESTAMP':
212
                $handler = 'parse_timestamp';
213
                break;
214
            case 'WWWROOT':
215
                $handler = 'parse_wwwroot';
216
                break;
217
            case 'CIPHERVALUE':
218
                $this->cipher[$this->tag_number] = '';
219
                $handler = 'parse_cipher';
220
                break;
221
            case 'FAULT':
222
                $handler = 'parse_fault';
223
            default:
224
                break;
225
        }
1441 ariadna 226
        xml_set_character_data_handler($this->parser, [$this, $handler]);
1 efrain 227
        return true;
228
    }
229
 
230
    /**
231
     * Add the next chunk of character data to the remote_timestamp string
232
     *
233
     * @param   mixed   $parser The XML parser
234
     * @param   string  $data   The content of the current tag (1024 byte chunk)
235
     * @return  bool            True
236
     */
237
    function parse_timestamp($parser, $data) {
238
        $this->remote_timestamp .= $data;
239
        return true;
240
    }
241
 
242
    /**
243
     * Add the next chunk of character data to the cipher string for that tag
244
     *
245
     * The XML parser calls the character-data handler with 1024-character
246
     * chunks of data. This means that the handler may be called several times
247
     * for a single tag, so we use the concatenate operator (.) to build the
248
     * tag content into a string.
249
     * We should not encounter more than one of each tag type, except for the
250
     * cipher tag. We will often see two of those. We prevent the content of
251
     * these two tags being concatenated together by counting each tag, and
252
     * using its 'number' as the key to an array of ciphers.
253
     *
254
     * @param   mixed   $parser The XML parser
255
     * @param   string  $data   The content of the current tag (1024 byte chunk)
256
     * @return  bool            True
257
     */
258
    function parse_cipher($parser, $data) {
259
        $this->cipher[$this->tag_number] .= $data;
260
        return true;
261
    }
262
 
263
    /**
264
     * Add the next chunk of character data to the remote_wwwroot string
265
     *
266
     * @param   mixed   $parser The XML parser
267
     * @param   string  $data   The content of the current tag (1024 byte chunk)
268
     * @return  bool            True
269
     */
270
    function parse_wwwroot($parser, $data) {
271
        $this->remote_wwwroot .= $data;
272
        return true;
273
    }
274
 
275
    /**
276
     * Add the next chunk of character data to the digest string
277
     *
278
     * @param   mixed   $parser The XML parser
279
     * @param   string  $data   The content of the current tag (1024 byte chunk)
280
     * @return  bool            True
281
     */
282
    function parse_digest($parser, $data) {
283
        $this->digest .= $data;
284
        return true;
285
    }
286
 
287
    /**
288
     * Add the next chunk of character data to the signature string
289
     *
290
     * @param   mixed   $parser The XML parser
291
     * @param   string  $data   The content of the current tag (1024 byte chunk)
292
     * @return  bool            True
293
     */
294
    function parse_signature($parser, $data) {
295
        $this->signature .= $data;
296
        return true;
297
    }
298
 
299
    /**
300
     * Add the next chunk of character data to the data_object string
301
     *
302
     * @param   mixed   $parser The XML parser
303
     * @param   string  $data   The content of the current tag (1024 byte chunk)
304
     * @return  bool            True
305
     */
306
    function parse_object($parser, $data) {
307
        $this->data_object .= $data;
308
        return true;
309
    }
310
 
311
    /**
312
     * Discard the next chunk of character data
313
     *
314
     * This is used for tags that we're not interested in.
315
     *
316
     * @param   mixed   $parser The XML parser
317
     * @param   string  $data   The content of the current tag (1024 byte chunk)
318
     * @return  bool            True
319
     */
320
    function discard_data($parser, $data) {
321
        if (!$this->errorstarted) {
322
            // Not interested
323
            return true;
324
        }
325
        $data = trim($data);
326
        if (isset($this->errorstarted->faultstringstarted) && !empty($data)) {
327
            $this->remoteerror .= ', message: ' . $data;
328
        } else if (isset($this->errorstarted->faultcodestarted)) {
329
            $this->remoteerror = 'code: ' . $data;
330
            unset($this->errorstarted->faultcodestarted);
331
        } else if ($data == 'faultCode') {
332
            $this->errorstarted->faultcodestarted = true;
333
        } else if ($data == 'faultString') {
334
            $this->errorstarted->faultstringstarted = true;
335
        }
336
        return true;
337
 
338
    }
339
 
340
    function parse_fault($parser, $data) {
341
        $this->errorstarted = new StdClass;
342
        return true;
343
    }
344
 
345
    /**
346
     * Switch the character-data handler to ignore the next chunk of data
347
     *
348
     * @param   mixed   $parser The XML parser
349
     * @param   string  $name   The name of the tag, e.g. method_call
350
     * @return  bool            True
351
     */
352
    function end_element($parser, $name) {
1441 ariadna 353
        $ok = xml_set_character_data_handler($this->parser, [$this, "discard_data"]);
1 efrain 354
        return true;
355
    }
356
}