Proyectos de Subversion Moodle

Rev

| 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
        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
}