PageRenderTime 218ms CodeModel.GetById 188ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/JsHttpRequest.php

https://bitbucket.org/boamaod/frontaccounting
PHP | 548 lines | 428 code | 21 blank | 99 comment | 13 complexity | 73632b9bf098b755b1a9654aa71b4188 MD5 | raw file
  1<?php
  2/**
  3 * JsHttpRequest: PHP backend for JavaScript DHTML loader.
  4 * (C) Dmitry Koterov, http://en.dklab.ru
  5 *
  6 * This library is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU Lesser General Public
  8 * License as published by the Free Software Foundation; either
  9 * version 2.1 of the License, or (at your option) any later version.
 10 * See http://www.gnu.org/copyleft/lesser.html
 11 *
 12 * Do not remove this comment if you want to use the script!
 13 * Ќе удал€йте данный комментарий, если вы хотите использовать скрипт!
 14 *
 15 * This backend library also supports POST requests additionally to GET.
 16 *
 17 * @author Dmitry Koterov 
 18 * @version 5.x
 19 */
 20
 21class JsHttpRequest
 22{
 23    var $SCRIPT_ENCODING = "windows-1251";
 24    var $SCRIPT_DECODE_MODE = '';
 25    var $LOADER = null;
 26    var $ID = null;    
 27    var $RESULT = null;
 28    
 29    // Internal; uniq value.
 30    var $_uniqHash;
 31    // Magic number for display_error checking.
 32    var $_magic = 14623;
 33    // Previous display_errors value.
 34    var $_prevDisplayErrors = null;    
 35    // Internal: response content-type depending on loader type.
 36    var $_contentTypes = array(
 37        "script" => "text/javascript",
 38        "xml"    => "text/plain", // In XMLHttpRequest mode we must return text/plain - stupid Opera 8.0. :(
 39        "form"   => "text/html",
 40        ""       => "text/plain", // for unknown loader
 41    );
 42    // Internal: conversion to UTF-8 JSON cancelled because of non-ascii key.
 43    var $_toUtfFailed = false;
 44    // Internal: list of characters 128...255 (for strpbrk() ASCII check).
 45    var $_nonAsciiChars = '';
 46    // Which Unicode conversion function is available?
 47    var $_unicodeConvMethod = null;
 48    // Emergency memory buffer to be freed on memory_limit error.
 49    var $_emergBuffer = null;
 50
 51    
 52    /**
 53     * Constructor.
 54     * 
 55     * Create new JsHttpRequest backend object and attach it
 56     * to script output buffer. As a result - script will always return
 57     * correct JavaScript code, even in case of fatal errors.
 58     *
 59     * QUERY_STRING is in form of: PHPSESSID=<sid>&a=aaa&b=bbb&JsHttpRequest=<id>-<loader>
 60     * where <id> is a request ID, <loader> is a loader name, <sid> - a session ID (if present), 
 61     * PHPSESSID - session parameter name (by default = "PHPSESSID").
 62     * 
 63     * If an object is created WITHOUT an active AJAX query, it is simply marked as
 64     * non-active. Use statuc method isActive() to check.
 65     */
 66    function JsHttpRequest($enc)
 67    {
 68        global $JsHttpRequest_Active;
 69        
 70        // To be on a safe side - do not allow to drop reference counter on ob processing.
 71        $GLOBALS['_RESULT'] =& $this->RESULT; 
 72        
 73        // Parse QUERY_STRING.
 74        if (preg_match('/^(.*)(?:&|^)JsHttpRequest=(?:(\d+)-)?([^&]+)((?:&|$).*)$/s', @$_SERVER['QUERY_STRING'], $m)) {
 75            $this->ID = $m[2];
 76            $this->LOADER = strtolower($m[3]);
 77            $_SERVER['QUERY_STRING'] = preg_replace('/^&+|&+$/s', '', preg_replace('/(^|&)'.session_name().'=[^&]*&?/s', '&', $m[1] . $m[4]));
 78            unset(
 79                $_GET['JsHttpRequest'],
 80                $_REQUEST['JsHttpRequest'],
 81                $_GET[session_name()],
 82                $_POST[session_name()],
 83                $_REQUEST[session_name()]
 84            );
 85            // Detect Unicode conversion method.
 86            $this->_unicodeConvMethod = function_exists('mb_convert_encoding')? 'mb' : (function_exists('iconv')? 'iconv' : null);
 87    
 88            // Fill an emergency buffer. We erase it at the first line of OB processor
 89            // to free some memory. This memory may be used on memory_limit error.
 90            $this->_emergBuffer = str_repeat('a', 1024 * 200);
 91
 92            // Intercept fatal errors via display_errors (seems it is the only way).     
 93            $this->_uniqHash = md5('JsHttpRequest' . microtime() . getmypid());
 94            $this->_prevDisplayErrors = ini_get('display_errors');
 95            ini_set('display_errors', $this->_magic); //
 96            ini_set('error_prepend_string', $this->_uniqHash . ini_get('error_prepend_string'));
 97            ini_set('error_append_string',  ini_get('error_append_string') . $this->_uniqHash);
 98            if (function_exists('xdebug_disable')) xdebug_disable(); // else Fatal errors are not catched
 99
100            // Start OB handling early.
101            ob_start(array(&$this, "_obHandler"));
102            $JsHttpRequest_Active = true;
103    
104            // Set up the encoding.
105            $this->setEncoding($enc);
106    
107            // Check if headers are already sent (see Content-Type library usage).
108            // If true - generate a debug message and exit.
109            $file = $line = null;
110            $headersSent = version_compare(PHP_VERSION, "4.3.0") < 0? headers_sent() : headers_sent($file, $line);
111            if ($headersSent) {
112                trigger_error(
113                    "HTTP headers are already sent" . ($line !== null? " in $file on line $line" : " somewhere in the script") . ". "
114                    . "Possibly you have an extra space (or a newline) before the first line of the script or any library. "
115                    . "Please note that JsHttpRequest uses its own Content-Type header and fails if "
116                    . "this header cannot be set. See header() function documentation for more details",
117                    E_USER_ERROR
118                );
119                exit();
120            }
121        } else {
122            $this->ID = 0;
123            $this->LOADER = 'unknown';
124            $JsHttpRequest_Active = false;
125        }
126    }
127    
128
129    /**
130     * Static function.
131     * Returns true if JsHttpRequest output processor is currently active.
132     * 
133     * @return boolean    True if the library is active, false otherwise.
134     */
135    function isActive()
136    {
137        return !empty($GLOBALS['JsHttpRequest_Active']);
138    }
139    
140
141    /**
142     * string getJsCode()
143     * 
144     * Return JavaScript part of the library.
145     */
146    function getJsCode()
147    {
148        return file_get_contents(dirname(__FILE__) . '/JsHttpRequest.js');
149    }
150
151
152    /**
153     * void setEncoding(string $encoding)
154     * 
155     * Set an active script encoding & correct QUERY_STRING according to it.
156     * Examples:
157     *   "windows-1251"          - set plain encoding (non-windows characters, 
158     *                             e.g. hieroglyphs, are totally ignored)
159     *   "windows-1251 entities" - set windows encoding, BUT additionally replace:
160     *                             "&"         ->  "&amp;" 
161     *                             hieroglyph  ->  &#XXXX; entity
162     */
163    function setEncoding($enc)
164    {
165        // Parse an encoding.
166        preg_match('/^(\S*)(?:\s+(\S*))$/', $enc, $p);
167        $this->SCRIPT_ENCODING    = strtolower(!empty($p[1])? $p[1] : $enc);
168        $this->SCRIPT_DECODE_MODE = !empty($p[2])? $p[2] : '';
169        // Manually parse QUERY_STRING because of damned Unicode's %uXXXX.
170        $this->_correctSuperglobals();
171    }
172
173    
174    /**
175     * string quoteInput(string $input)
176     * 
177     * Quote a string according to the input decoding mode.
178     * If entities are used (see setEncoding()), no '&' character is quoted,
179     * only '"', '>' and '<' (we presume that '&' is already quoted by
180     * an input reader function).
181     *
182     * Use this function INSTEAD of htmlspecialchars() for $_GET data 
183     * in your scripts.
184     */
185    function quoteInput($s)
186    {
187        if ($this->SCRIPT_DECODE_MODE == 'entities')
188            return str_replace(array('"', '<', '>'), array('&quot;', '&lt;', '&gt;'), $s);
189        else
190            return htmlspecialchars($s);
191    }
192    
193
194    /**
195     * Convert a PHP scalar, array or hash to JS scalar/array/hash. This function is 
196     * an analog of json_encode(), but it can work with a non-UTF8 input and does not 
197     * analyze the passed data. Output format must be fully JSON compatible.
198     * 
199     * @param mixed $a   Any structure to convert to JS.
200     * @return string    JavaScript equivalent structure.
201     */
202    function php2js($a=false)
203    {
204        if (is_null($a)) return 'null';
205        if ($a === false) return 'false';
206        if ($a === true) return 'true';
207        if (is_scalar($a)) {
208            if (is_float($a)) {
209                // Always use "." for floats.
210                $a = str_replace(",", ".", strval($a));
211            }
212            // All scalars are converted to strings to avoid indeterminism.
213            // PHP's "1" and 1 are equal for all PHP operators, but 
214            // JS's "1" and 1 are not. So if we pass "1" or 1 from the PHP backend,
215            // we should get the same result in the JS frontend (string).
216            // Character replacements for JSON.
217            static $jsonReplaces = array(
218                array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'),
219                array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"')
220            );
221            return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';
222        }
223        $isList = true;
224        for ($i = 0, reset($a); $i < count($a); $i++, next($a)) {
225            if (key($a) !== $i) { 
226                $isList = false; 
227                break; 
228            }
229        }
230        $result = array();
231        if ($isList) {
232            foreach ($a as $v) {
233                $result[] = $this->php2js($v);
234            }
235            return '[ ' . join(', ', $result) . ' ]';
236        } else {
237            foreach ($a as $k => $v) {
238                $result[] = $this->php2js($k) . ': ' . $this->php2js($v);
239            }
240            return '{ ' . join(', ', $result) . ' }';
241        }
242    }
243    
244        
245    /**
246     * Internal methods.
247     */
248
249    /**
250     * Parse & decode QUERY_STRING.
251     */
252    function _correctSuperglobals()
253    {
254        // In case of FORM loader we may go to nirvana, everything is already parsed by PHP.
255        if ($this->LOADER == 'form') return;
256        
257        // ATTENTION!!!
258        // HTTP_RAW_POST_DATA is only accessible when Content-Type of POST request
259        // is NOT default "application/x-www-form-urlencoded"!!!
260        // Library frontend sets "application/octet-stream" for that purpose,
261        // see JavaScript code. In PHP 5.2.2.HTTP_RAW_POST_DATA is not set sometimes; 
262        // in such cases - read the POST data manually from the STDIN stream.
263        $rawPost = strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') == 0? (isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : @file_get_contents("php://input")) : null;
264        $source = array(
265            '_GET' => !empty($_SERVER['QUERY_STRING'])? $_SERVER['QUERY_STRING'] : null, 
266            '_POST'=> $rawPost,
267        );
268        foreach ($source as $dst=>$src) {
269            // First correct all 2-byte entities.
270            $s = preg_replace('/%(?!5B)(?!5D)([0-9a-f]{2})/si', '%u00\\1', $src);
271            // Now we can use standard parse_str() with no worry!
272            $data = null;
273            parse_str($s, $data);
274            $GLOBALS[$dst] = $this->_ucs2EntitiesDecode($data);
275        }
276        $GLOBALS['HTTP_GET_VARS'] = $_GET; // deprecated vars
277        $GLOBALS['HTTP_POST_VARS'] = $_POST;
278        $_REQUEST = 
279            (isset($_COOKIE)? $_COOKIE : array()) + 
280            (isset($_POST)? $_POST : array()) + 
281            (isset($_GET)? $_GET : array());
282        if (ini_get('register_globals')) {
283            // TODO?
284        }
285    }
286
287
288    /**
289     * Called in case of error too!
290     */
291    function _obHandler($text)
292    {
293        unset($this->_emergBuffer); // free a piece of memory for memory_limit error
294        unset($GLOBALS['JsHttpRequest_Active']);
295        
296        // Check for error & fetch a resulting data.
297        $wasFatalError = false;
298        if (preg_match_all("/{$this->_uniqHash}(.*?){$this->_uniqHash}/sx", $text, $m)) {
299            // Display_errors:
300            // 1. disabled manually after the library initialization, or
301            // 2. was initially disabled and is not changed
302            $needRemoveErrorMessages = !ini_get('display_errors') || (!$this->_prevDisplayErrors && ini_get('display_errors') == $this->_magic);
303            foreach ($m[0] as $error) {
304                if (preg_match('/\bFatal error(<.*?>)?:/i', $error)) {
305                    $wasFatalError = true;
306                }
307                if ($needRemoveErrorMessages) {
308                    $text = str_replace($error, '', $text); // strip the whole error message
309                } else {
310                    $text = str_replace($this->_uniqHash, '', $text);
311                }
312            }
313        }
314        if ($wasFatalError) {
315            // On fatal errors - force "null" result. This is needed, because $_RESULT
316            // may not be fully completed at the moment of the error.
317            $this->RESULT = null;
318        } else {
319            // Read the result from globals if not set directly.
320            if (!isset($this->RESULT)) {
321                global $_RESULT;
322                $this->RESULT = $_RESULT;
323            }
324            // Avoid manual NULLs in the result (very important!).
325            if ($this->RESULT === null) {
326                $this->RESULT = false;
327            }
328        }
329        
330        // Note that 500 error is generated when a PHP error occurred.
331        $status = $this->RESULT === null? 500 : 200;
332        $result = array(
333            'id'   => $this->ID,
334            'js'   => $this->RESULT,  // null always means a fatal error...
335            'text' => $text,          // ...independent on $text!!!
336        );
337        $encoding = $this->SCRIPT_ENCODING;
338        $text = null; // to be on a safe side
339        
340        // Try to use very fast json_encode: 3-4 times faster than a manual encoding.
341        if (function_exists('array_walk_recursive') && function_exists('json_encode') && $this->_unicodeConvMethod) {
342            $this->_nonAsciiChars = join("", array_map('chr', range(128, 255)));
343            $this->_toUtfFailed = false;
344            $resultUtf8 = $result;
345            array_walk_recursive($resultUtf8, array(&$this, '_toUtf8_callback'), $this->SCRIPT_ENCODING);
346            if (!$this->_toUtfFailed) {
347                // If some key contains non-ASCII character, convert everything manually.
348                $text = json_encode($resultUtf8);
349                $encoding = "UTF-8";
350            }
351        }
352        
353        // On failure, use manual encoding.
354        if ($text === null) {
355            $text = $this->php2js($result);
356        }
357
358        if ($this->LOADER != "xml") {
359            // In non-XML mode we cannot use plain JSON. So - wrap with JS function call.
360            // If top.JsHttpRequestGlobal is not defined, loading is aborted and 
361            // iframe is removed, so - do not call dataReady().
362            $text = "" 
363                . ($this->LOADER == "form"? 'top && top.JsHttpRequestGlobal && top.JsHttpRequestGlobal' : 'JsHttpRequest') 
364                . ".dataReady(" . $text . ")\n"
365                . "";
366            if ($this->LOADER == "form") {
367                $text = '<script type="text/javascript" language="JavaScript"><!--' . "\n$text" . '//--></script>';
368            }
369            
370            // Always return 200 code in non-XML mode (else SCRIPT does not work in FF).
371            // For XML mode, 500 code is okay.
372            $status = 200;
373        }
374
375        // Status header. To be safe, display it only in error mode. In case of success 
376        // termination, do not modify the status (""HTTP/1.1 ..." header seems to be not
377        // too cross-platform).
378        if ($this->RESULT === null) {
379            if (php_sapi_name() == "cgi") {
380                header("Status: $status");
381            } else {
382                header("HTTP/1.1 $status");
383            }
384        }
385
386        // In XMLHttpRequest mode we must return text/plain - damned stupid Opera 8.0. :(
387        $ctype = !empty($this->_contentTypes[$this->LOADER])? $this->_contentTypes[$this->LOADER] : $this->_contentTypes[''];
388        header("Content-type: $ctype; charset=$encoding");
389
390        return $text;
391    }
392
393
394    /**
395     * Internal function, used in array_walk_recursive() before json_encode() call.
396     * If a key contains non-ASCII characters, this function sets $this->_toUtfFailed = true,
397     * becaues array_walk_recursive() cannot modify array keys.
398     */
399    function _toUtf8_callback(&$v, $k, $fromEnc)
400    {
401        if ($v === null || is_bool($v)) return;
402        if ($this->_toUtfFailed || !is_scalar($v) || strpbrk($k, $this->_nonAsciiChars) !== false) {
403            $this->_toUtfFailed = true;
404        } else {
405            $v = $this->_unicodeConv($fromEnc, 'UTF-8', $v);
406        }
407    }
408    
409
410    /**
411     * Decode all %uXXXX entities in string or array (recurrent).
412     * String must not contain %XX entities - they are ignored!
413     */
414    function _ucs2EntitiesDecode($data)
415    {
416        if (is_array($data)) {
417            $d = array();
418            foreach ($data as $k=>$v) {
419                $d[$this->_ucs2EntitiesDecode($k)] = $this->_ucs2EntitiesDecode($v);
420            }
421            return $d;
422        } else {
423            if (strpos($data, '%u') !== false) { // improve speed
424                $data = preg_replace_callback('/%u([0-9A-F]{1,4})/si', array(&$this, '_ucs2EntitiesDecodeCallback'), $data);
425            }
426            return $data;
427        }
428    }
429
430
431    /**
432     * Decode one %uXXXX entity (RE callback).
433     */
434    function _ucs2EntitiesDecodeCallback($p)
435    {
436        $hex = $p[1];
437        $dec = hexdec($hex);
438        if ($dec === "38" && $this->SCRIPT_DECODE_MODE == 'entities') {
439            // Process "&" separately in "entities" decode mode.
440            $c = "&amp;";
441        } else {
442            if ($this->_unicodeConvMethod) {
443                $c = @$this->_unicodeConv('UCS-2BE', $this->SCRIPT_ENCODING, pack('n', $dec));
444            } else {
445                $c = $this->_decUcs2Decode($dec, $this->SCRIPT_ENCODING);
446            }
447            if (!strlen($c)) {
448                if ($this->SCRIPT_DECODE_MODE == 'entities') {
449                    $c = '&#' . $dec . ';';
450                } else {
451                    $c = '?';
452                }
453            }
454        }
455        return $c;
456    }
457
458
459    /**
460     * Wrapper for iconv() or mb_convert_encoding() functions.
461     * This function will generate fatal error if none of these functons available!
462     * 
463     * @see iconv()
464     */
465    function _unicodeConv($fromEnc, $toEnc, $v)
466    {
467        if ($this->_unicodeConvMethod == 'iconv') {
468            return iconv($fromEnc, $toEnc, $v);
469        } 
470        return mb_convert_encoding($v, $toEnc, $fromEnc);
471    }
472
473
474    /**
475     * If there is no ICONV, try to decode 1-byte characters and UTF-8 manually
476     * (for most popular charsets only).
477     */
478     
479    /**
480     * Convert from UCS-2BE decimal to $toEnc.
481     */
482    function _decUcs2Decode($code, $toEnc)
483    {
484        // Little speedup by using array_flip($this->_encTables) and later hash access.
485        static $flippedTable = null;
486        if ($code < 128) return chr($code);
487        
488        if (isset($this->_encTables[$toEnc])) {
489            if (!$flippedTable) $flippedTable = array_flip($this->_encTables[$toEnc]);
490            if (isset($flippedTable[$code])) return chr(128 + $flippedTable[$code]);
491        } else if ($toEnc == 'utf-8' || $toEnc == 'utf8') {
492            // UTF-8 conversion rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html
493            if ($code < 0x800) {
494                return chr(0xC0 + ($code >> 6)) . 
495                       chr(0x80 + ($code & 0x3F));
496            } else { // if ($code <= 0xFFFF) -- it is almost always so for UCS2-BE
497                return chr(0xE0 + ($code >> 12)) .
498                       chr(0x80 + (0x3F & ($code >> 6))) .
499                       chr(0x80 + ($code & 0x3F));
500            }
501        }
502        
503        return "";
504    }
505    
506
507    /**
508     * UCS-2BE -> 1-byte encodings (from #128).
509     */
510    var $_encTables = array(
511        'windows-1251' => array(
512            0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
513            0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
514            0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
515            0x0098, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
516            0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
517            0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
518            0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
519            0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
520            0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
521            0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
522            0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
523            0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
524            0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
525            0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
526            0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
527            0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
528        ),
529        'koi8-r' => array(
530            0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
531            0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
532            0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248,
533            0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
534            0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,
535            0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255d, 0x255E,
536            0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,
537            0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9,
538            0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
539            0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043d, 0x043E,
540            0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
541            0x044C, 0x044B, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044A,
542            0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
543            0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041d, 0x041E,
544            0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
545            0x042C, 0x042B, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042A      
546        ),
547    );
548}