~ubuntu-branches/ubuntu/natty/moodle/natty

« back to all changes in this revision

Viewing changes to lib/htmlpurifier/HTMLPurifier/Encoder.php

  • Committer: Bazaar Package Importer
  • Author(s): Tomasz Muras
  • Date: 2010-10-30 12:19:28 UTC
  • mfrom: (1.1.12 upstream) (3.1.10 squeeze)
  • Revision ID: james.westby@ubuntu.com-20101030121928-qzobi6mctpnk4dif
Tags: 1.9.9.dfsg2-2
* Added Romanian translation
* Updated Japanese translation (closes: #596820)
* Backporting security fixes from Moodle 1.9.10 (closes: #601384)
   - Updated embedded CAS to 1.1.3
   - Added patch for MDL-24523:
     clean_text() not filtering text in markdown format
   - Added patch for MDL-24810 and upgraded customized HTML Purifier to 4.2.0 
   - Added patch for MDL-24258:
     students can delete their forum posts later than $CFG->maxeditingtime 
     under certain conditions
   - Added patch for MDL-23377:
     Can't delete quiz attempts in course without enrolled students

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
2
 
3
 
HTMLPurifier_ConfigSchema::define(
4
 
    'Core', 'Encoding', 'utf-8', 'istring', 
5
 
    'If for some reason you are unable to convert all webpages to UTF-8, '. 
6
 
    'you can use this directive as a stop-gap compatibility change to '. 
7
 
    'let HTML Purifier deal with non UTF-8 input.  This technique has '. 
8
 
    'notable deficiencies: absolutely no characters outside of the selected '. 
9
 
    'character encoding will be preserved, not even the ones that have '. 
10
 
    'been ampersand escaped (this is due to a UTF-8 specific <em>feature</em> '.
11
 
    'that automatically resolves all entities), making it pretty useless '.
12
 
    'for anything except the most I18N-blind applications, although '.
13
 
    '%Core.EscapeNonASCIICharacters offers fixes this trouble with '.
14
 
    'another tradeoff. This directive '.
15
 
    'only accepts ISO-8859-1 if iconv is not enabled.'
16
 
);
17
 
 
18
 
HTMLPurifier_ConfigSchema::define(
19
 
    'Core', 'EscapeNonASCIICharacters', false, 'bool',
20
 
    'This directive overcomes a deficiency in %Core.Encoding by blindly '.
21
 
    'converting all non-ASCII characters into decimal numeric entities before '.
22
 
    'converting it to its native encoding. This means that even '.
23
 
    'characters that can be expressed in the non-UTF-8 encoding will '.
24
 
    'be entity-ized, which can be a real downer for encodings like Big5. '.
25
 
    'It also assumes that the ASCII repetoire is available, although '.
26
 
    'this is the case for almost all encodings. Anyway, use UTF-8! This '.
27
 
    'directive has been available since 1.4.0.'
28
 
);
29
 
 
30
 
if ( !function_exists('iconv') ) {
31
 
    // only encodings with native PHP support
32
 
    HTMLPurifier_ConfigSchema::defineAllowedValues(
33
 
        'Core', 'Encoding', array(
34
 
            'utf-8',
35
 
            'iso-8859-1'
36
 
        )
37
 
    );
38
 
    HTMLPurifier_ConfigSchema::defineValueAliases(
39
 
        'Core', 'Encoding', array(
40
 
            'iso8859-1' => 'iso-8859-1'
41
 
        )
42
 
    );
43
 
}
44
 
 
45
 
HTMLPurifier_ConfigSchema::define(
46
 
    'Test', 'ForceNoIconv', false, 'bool', 
47
 
    'When set to true, HTMLPurifier_Encoder will act as if iconv does not '.
48
 
    'exist and use only pure PHP implementations.'
49
 
);
50
 
 
51
3
/**
52
4
 * A UTF-8 specific character encoder that handles cleaning and transforming.
53
5
 * @note All functions in this class should be static.
54
6
 */
55
7
class HTMLPurifier_Encoder
56
8
{
57
 
    
 
9
 
58
10
    /**
59
11
     * Constructor throws fatal error if you attempt to instantiate class
60
12
     */
61
 
    function HTMLPurifier_Encoder() {
 
13
    private function __construct() {
62
14
        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
63
15
    }
64
 
    
 
16
 
65
17
    /**
66
18
     * Error-handler that mutes errors, alternative to shut-up operator.
67
19
     */
68
 
    function muteErrorHandler() {}
69
 
    
70
 
    /**
 
20
    public static function muteErrorHandler() {}
 
21
 
71
22
    /**
72
23
     * Cleans a UTF-8 string for well-formedness and SGML validity
73
 
     * 
 
24
     *
74
25
     * It will parse according to UTF-8 and return a valid UTF8 string, with
75
26
     * non-SGML codepoints excluded.
76
 
     * 
77
 
     * @static
 
27
     *
78
28
     * @note Just for reference, the non-SGML code points are 0 to 31 and
79
29
     *       127 to 159, inclusive.  However, we allow code points 9, 10
80
30
     *       and 13, which are the tab, line feed and carriage return
81
31
     *       respectively. 128 and above the code points map to multibyte
82
32
     *       UTF-8 representations.
83
 
     * 
 
33
     *
84
34
     * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
85
35
     *       hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
86
36
     *       LGPL license.  Notes on what changed are inside, but in general,
94
44
     *       would need that, and I'm probably not going to implement them.
95
45
     *       Once again, PHP 6 should solve all our problems.
96
46
     */
97
 
    function cleanUTF8($str, $force_php = false) {
98
 
        
 
47
    public static function cleanUTF8($str, $force_php = false) {
 
48
 
99
49
        // UTF-8 validity is checked since PHP 4.3.5
100
50
        // This is an optimization: if the string is already valid UTF-8, no
101
51
        // need to do PHP stuff. 99% of the time, this will be the case.
104
54
        if (preg_match('/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', $str)) {
105
55
            return $str;
106
56
        }
107
 
        
 
57
 
108
58
        $mState = 0; // cached expected number of octets after the current octet
109
59
                     // until the beginning of the next UTF8 character sequence
110
60
        $mUcs4  = 0; // cached Unicode character
111
61
        $mBytes = 1; // cached expected number of octets in the current sequence
112
 
        
 
62
 
113
63
        // original code involved an $out that was an array of Unicode
114
64
        // codepoints.  Instead of having to convert back into UTF-8, we've
115
65
        // decided to directly append valid UTF-8 characters onto a string
116
66
        // $out once they're done.  $char accumulates raw bytes, while $mUcs4
117
67
        // turns into the Unicode code point, so there's some redundancy.
118
 
        
 
68
 
119
69
        $out = '';
120
70
        $char = '';
121
 
        
 
71
 
122
72
        $len = strlen($str);
123
73
        for($i = 0; $i < $len; $i++) {
124
74
            $in = ord($str{$i});
125
75
            $char .= $str[$i]; // append byte to char
126
76
            if (0 == $mState) {
127
 
                // When mState is zero we expect either a US-ASCII character 
 
77
                // When mState is zero we expect either a US-ASCII character
128
78
                // or a multi-octet sequence.
129
79
                if (0 == (0x80 & ($in))) {
130
80
                    // US-ASCII, pass straight through.
131
 
                    if (($in <= 31 || $in == 127) && 
 
81
                    if (($in <= 31 || $in == 127) &&
132
82
                        !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
133
83
                    ) {
134
84
                        // control characters, remove
158
108
                    $mBytes = 4;
159
109
                } elseif (0xF8 == (0xFC & ($in))) {
160
110
                    // First octet of 5 octet sequence.
161
 
                    // 
162
 
                    // This is illegal because the encoded codepoint must be 
 
111
                    //
 
112
                    // This is illegal because the encoded codepoint must be
163
113
                    // either:
164
114
                    // (a) not the shortest form or
165
115
                    // (b) outside the Unicode range of 0-0x10FFFF.
166
 
                    // Rather than trying to resynchronize, we will carry on 
 
116
                    // Rather than trying to resynchronize, we will carry on
167
117
                    // until the end of the sequence and let the later error
168
118
                    // handling code catch it.
169
119
                    $mUcs4 = ($in);
178
128
                    $mState = 5;
179
129
                    $mBytes = 6;
180
130
                } else {
181
 
                    // Current octet is neither in the US-ASCII range nor a 
 
131
                    // Current octet is neither in the US-ASCII range nor a
182
132
                    // legal first octet of a multi-octet sequence.
183
133
                    $mState = 0;
184
134
                    $mUcs4  = 0;
194
144
                    $tmp = $in;
195
145
                    $tmp = ($tmp & 0x0000003F) << $shift;
196
146
                    $mUcs4 |= $tmp;
197
 
                    
 
147
 
198
148
                    if (0 == --$mState) {
199
149
                        // End of the multi-octet sequence. mUcs4 now contains
200
150
                        // the final Unicode codepoint to be output
201
 
                        
 
151
 
202
152
                        // Check for illegal sequences and codepoints.
203
 
                        
 
153
 
204
154
                        // From Unicode 3.1, non-shortest form is illegal
205
155
                        if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
206
156
                            ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
211
161
                            // Codepoints outside the Unicode range are illegal
212
162
                            ($mUcs4 > 0x10FFFF)
213
163
                        ) {
214
 
                            
 
164
 
215
165
                        } elseif (0xFEFF != $mUcs4 && // omit BOM
216
166
                            // check for valid Char unicode codepoints
217
167
                            (
246
196
        }
247
197
        return $out;
248
198
    }
249
 
    
 
199
 
250
200
    /**
251
201
     * Translates a Unicode codepoint into its corresponding UTF-8 character.
252
 
     * @static
253
202
     * @note Based on Feyd's function at
254
203
     *       <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
255
204
     *       which is in public domain.
260
209
     *       maintenance/generate-entity-file.php (although this is superior,
261
210
     *       due to its sanity checks).
262
211
     */
263
 
    
 
212
 
264
213
    // +----------+----------+----------+----------+
265
214
    // | 33222222 | 22221111 | 111111   |          |
266
215
    // | 10987654 | 32109876 | 54321098 | 76543210 | bit
272
221
    // +----------+----------+----------+----------+
273
222
    // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
274
223
    // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
275
 
    // +----------+----------+----------+----------+ 
276
 
    
277
 
    function unichr($code) {
 
224
    // +----------+----------+----------+----------+
 
225
 
 
226
    public static function unichr($code) {
278
227
        if($code > 1114111 or $code < 0 or
279
228
          ($code >= 55296 and $code <= 57343) ) {
280
229
            // bits are set outside the "valid" range as defined
281
 
            // by UNICODE 4.1.0 
 
230
            // by UNICODE 4.1.0
282
231
            return '';
283
232
        }
284
 
        
285
 
        $x = $y = $z = $w = 0; 
 
233
 
 
234
        $x = $y = $z = $w = 0;
286
235
        if ($code < 128) {
287
236
            // regular ASCII character
288
237
            $x = $code;
299
248
                    $z = (($code >> 12) & 63) | 128;
300
249
                    $w = (($code >> 18) & 7)  | 240;
301
250
                }
302
 
            } 
 
251
            }
303
252
        }
304
253
        // set up the actual character
305
254
        $ret = '';
306
255
        if($w) $ret .= chr($w);
307
256
        if($z) $ret .= chr($z);
308
257
        if($y) $ret .= chr($y);
309
 
        $ret .= chr($x); 
310
 
        
 
258
        $ret .= chr($x);
 
259
 
311
260
        return $ret;
312
261
    }
313
 
    
 
262
 
314
263
    /**
315
264
     * Converts a string to UTF-8 based on configuration.
316
 
     * @static
317
265
     */
318
 
    function convertToUTF8($str, $config, &$context) {
319
 
        $encoding = $config->get('Core', 'Encoding');
 
266
    public static function convertToUTF8($str, $config, $context) {
 
267
        $encoding = $config->get('Core.Encoding');
320
268
        if ($encoding === 'utf-8') return $str;
321
269
        static $iconv = null;
322
270
        if ($iconv === null) $iconv = function_exists('iconv');
323
271
        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
324
 
        if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
 
272
        if ($iconv && !$config->get('Test.ForceNoIconv')) {
325
273
            $str = iconv($encoding, 'utf-8//IGNORE', $str);
 
274
            if ($str === false) {
 
275
                // $encoding is not a valid encoding
 
276
                restore_error_handler();
 
277
                trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
 
278
                return '';
 
279
            }
326
280
            // If the string is bjorked by Shift_JIS or a similar encoding
327
281
            // that doesn't support all of ASCII, convert the naughty
328
282
            // characters to their true byte-wise ASCII/UTF-8 equivalents.
334
288
            restore_error_handler();
335
289
            return $str;
336
290
        }
337
 
        trigger_error('Encoding not supported', E_USER_ERROR);
 
291
        trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
338
292
    }
339
 
    
 
293
 
340
294
    /**
341
295
     * Converts a string from UTF-8 based on configuration.
342
 
     * @static
343
296
     * @note Currently, this is a lossy conversion, with unexpressable
344
297
     *       characters being omitted.
345
298
     */
346
 
    function convertFromUTF8($str, $config, &$context) {
347
 
        $encoding = $config->get('Core', 'Encoding');
 
299
    public static function convertFromUTF8($str, $config, $context) {
 
300
        $encoding = $config->get('Core.Encoding');
348
301
        if ($encoding === 'utf-8') return $str;
349
302
        static $iconv = null;
350
303
        if ($iconv === null) $iconv = function_exists('iconv');
351
 
        if ($escape = $config->get('Core', 'EscapeNonASCIICharacters')) {
 
304
        if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
352
305
            $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
353
306
        }
354
307
        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
355
 
        if ($iconv && !$config->get('Test', 'ForceNoIconv')) {
 
308
        if ($iconv && !$config->get('Test.ForceNoIconv')) {
356
309
            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
357
310
            $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding);
358
311
            if (!$escape && !empty($ascii_fix)) {
372
325
        }
373
326
        trigger_error('Encoding not supported', E_USER_ERROR);
374
327
    }
375
 
    
 
328
 
376
329
    /**
377
330
     * Lossless (character-wise) conversion of HTML to ASCII
378
 
     * @static
379
331
     * @param $str UTF-8 string to be converted to ASCII
380
332
     * @returns ASCII encoded string with non-ASCII character entity-ized
381
333
     * @warning Adapted from MediaWiki, claiming fair use: this is a common
390
342
     * @note Sort of with cleanUTF8() but it assumes that $str is
391
343
     *       well-formed UTF-8
392
344
     */
393
 
    function convertToASCIIDumbLossless($str) {
 
345
    public static function convertToASCIIDumbLossless($str) {
394
346
        $bytesleft = 0;
395
347
        $result = '';
396
348
        $working = 0;
420
372
        }
421
373
        return $result;
422
374
    }
423
 
    
 
375
 
424
376
    /**
425
377
     * This expensive function tests whether or not a given character
426
378
     * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will
427
379
     * fail this test, and require special processing. Variable width
428
380
     * encodings shouldn't ever fail.
429
 
     * 
 
381
     *
430
382
     * @param string $encoding Encoding name to test, as per iconv format
431
383
     * @param bool $bypass Whether or not to bypass the precompiled arrays.
432
384
     * @return Array of UTF-8 characters to their corresponding ASCII,
433
385
     *      which can be used to "undo" any overzealous iconv action.
434
386
     */
435
 
    function testEncodingSupportsASCII($encoding, $bypass = false) {
 
387
    public static function testEncodingSupportsASCII($encoding, $bypass = false) {
436
388
        static $encodings = array();
437
389
        if (!$bypass) {
438
390
            if (isset($encodings[$encoding])) return $encodings[$encoding];
449
401
        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
450
402
        if (iconv('UTF-8', $encoding, 'a') === false) return false;
451
403
        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
452
 
            $c = chr($i);
453
 
            if (iconv('UTF-8', "$encoding//IGNORE", $c) === '') {
 
404
            $c = chr($i); // UTF-8 char
 
405
            $r = iconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
 
406
            if (
 
407
                $r === '' ||
 
408
                // This line is needed for iconv implementations that do not
 
409
                // omit characters that do not exist in the target character set
 
410
                ($r === $c && iconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
 
411
            ) {
454
412
                // Reverse engineer: what's the UTF-8 equiv of this byte
455
413
                // sequence? This assumes that there's no variable width
456
414
                // encoding that doesn't support ASCII.
461
419
        $encodings[$encoding] = $ret;
462
420
        return $ret;
463
421
    }
464
 
    
465
 
    
 
422
 
 
423
 
466
424
}
467
425
 
 
426
// vim: et sw=4 sts=4