| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212 |
- <?php
- namespace WGSSWITCHEPP\IDNA;
- // {{{ license
- /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
- //
- // +----------------------------------------------------------------------+
- // | This library is free software; you can redistribute it and/or modify |
- // | it under the terms of the GNU Lesser General Public License as |
- // | published by the Free Software Foundation; either version 2.1 of the |
- // | License, or (at your option) any later version. |
- // | |
- // | This library is distributed in the hope that it will be useful, but |
- // | WITHOUT ANY WARRANTY; without even the implied warranty of |
- // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
- // | Lesser General Public License for more details. |
- // | |
- // | You should have received a copy of the GNU Lesser General Public |
- // | License along with this library; if not, write to the Free Software |
- // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
- // | USA. |
- // +----------------------------------------------------------------------+
- //
- // }}}
- /**
- * Encode/decode Internationalized Domain Names.
- *
- * The class allows to convert internationalized domain names
- * (see RFC 3490 for details) as they can be used with various registries worldwide
- * to be translated between their original (localized) form and their encoded form
- * as it will be used in the DNS (Domain Name System).
- *
- * The class provides two public methods, encode() and decode(), which do exactly
- * what you would expect them to do. You are allowed to use complete domain names,
- * simple strings and complete email addresses as well. That means, that you might
- * use any of the following notations:
- *
- * - www.nörgler.com
- * - xn--nrgler-wxa
- * - xn--brse-5qa.xn--knrz-1ra.info
- *
- * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4
- * array. Unicode output is available in the same formats.
- * You can select your preferred format via {@link set_paramter()}.
- *
- * ACE input and output is always expected to be ASCII.
- *
- * @author Matthias Sommerfeld <mso@phlylabs.de>
- * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de
- * @version 0.5.1
- *
- */
- class idna_convert {
- /**
- * Holds all relevant mapping tables, loaded from a seperate file on construct
- * See RFC3454 for details
- *
- * @var array
- * @access private
- */
- var $NP = array();
- // Internal settings, do not mess with them
- var $_punycode_prefix = 'xn--';
- var $_invalid_ucs = 0x80000000;
- var $_max_ucs = 0x10FFFF;
- var $_base = 36;
- var $_tmin = 1;
- var $_tmax = 26;
- var $_skew = 38;
- var $_damp = 700;
- var $_initial_bias = 72;
- var $_initial_n = 0x80;
- var $_sbase = 0xAC00;
- var $_lbase = 0x1100;
- var $_vbase = 0x1161;
- var $_tbase = 0x11A7;
- var $_lcount = 19;
- var $_vcount = 21;
- var $_tcount = 28;
- var $_ncount = 588; // _vcount * _tcount
- var $_scount = 11172; // _lcount * _tcount * _vcount
- var $_error = false;
- // See {@link set_paramter()} for details of how to change the following
- // settings from within your script / application
- var $_api_encoding = 'utf8'; // Default input charset is UTF-8
- var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden
- var $_strict_mode = false; // Behave strict or not
- // The constructor
- function idna_convert($options = false) {
- $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
- if (function_exists('file_get_contents')) {
- $this->NP = unserialize(file_get_contents(dirname(__FILE__) . '/npdata.ser'));
- } else {
- $this->NP = unserialize(join('', file(dirname(__FILE__) . '/npdata.ser')));
- }
- // If parameters are given, pass these to the respective method
- if (is_array($options)) {
- return $this->set_parameter($options);
- }
- return true;
- }
- /**
- * Sets a new option value. Available options and values:
- * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
- * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
- * [overlong - Unicode does not allow unnecessarily long encodings of chars,
- * to allow this, set this parameter to true, else to false;
- * default is false.]
- * [strict - true: strict mode, good for registration purposes - Causes errors
- * on failures; false: loose mode, ideal for "wildlife" applications
- * by silently ignoring errors and returning the original input instead
- *
- * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs)
- * @param string Value to use (if parameter 1 is a string)
- * @return boolean true on success, false otherwise
- * @access public
- */
- function set_parameter($option, $value = false) {
- if (!is_array($option)) {
- $option = array($option => $value);
- }
- foreach ($option as $k => $v) {
- switch ($k) {
- case 'encoding':
- switch ($v) {
- case 'utf8':
- case 'ucs4_string':
- case 'ucs4_array':
- $this->_api_encoding = $v;
- break;
- default:
- $this->_error('Set Parameter: Unknown parameter ' . $v . ' for option ' . $k);
- return false;
- }
- break;
- case 'overlong':
- $this->_allow_overlong = ($v) ? true : false;
- break;
- case 'strict':
- $this->_strict_mode = ($v) ? true : false;
- break;
- default:
- $this->_error('Set Parameter: Unknown option ' . $k);
- return false;
- }
- }
- return true;
- }
- /**
- * Decode a given ACE domain name
- * @param string Domain name (ACE string)
- * [@param string Desired output encoding, see {@link set_parameter}]
- * @return string Decoded Domain name (UTF-8 or UCS-4)
- * @access public
- */
- function decode($input, $one_time_encoding = false) {
- // Optionally set
- if ($one_time_encoding) {
- switch ($one_time_encoding) {
- case 'utf8':
- case 'ucs4_string':
- case 'ucs4_array':
- break;
- default:
- $this->_error('Unknown encoding ' . $one_time_encoding);
- return false;
- }
- }
- // Make sure to drop any newline characters around
- $input = trim($input);
- // Negotiate input and try to determine, whether it is a plain string,
- // an email address or something like a complete URL
- if (strpos($input, '@')) { // Maybe it is an email address
- // No no in strict mode
- if ($this->_strict_mode) {
- $this->_error('Only simple domain name parts can be handled in strict mode');
- return false;
- }
- list ($email_pref, $input) = explode('@', $input, 2);
- $arr = explode('.', $input);
- foreach ($arr as $k => $v) {
- if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) {
- $conv = $this->_decode($v);
- if ($conv)
- $arr[$k] = $conv;
- }
- }
- $input = join('.', $arr);
- $arr = explode('.', $email_pref);
- foreach ($arr as $k => $v) {
- if (preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $v)) {
- $conv = $this->_decode($v);
- if ($conv)
- $arr[$k] = $conv;
- }
- }
- $email_pref = join('.', $arr);
- $return = $email_pref . '@' . $input;
- } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters)
- // No no in strict mode
- if ($this->_strict_mode) {
- $this->_error('Only simple domain name parts can be handled in strict mode');
- return false;
- }
- $parsed = parse_url($input);
- if (isset($parsed['host'])) {
- $arr = explode('.', $parsed['host']);
- foreach ($arr as $k => $v) {
- $conv = $this->_decode($v);
- if ($conv)
- $arr[$k] = $conv;
- }
- $parsed['host'] = join('.', $arr);
- $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'] . (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
- . (empty($parsed['user']) ? '' : $parsed['user'] . (empty($parsed['pass']) ? '' : ':' . $parsed['pass']) . '@')
- . $parsed['host']
- . (empty($parsed['port']) ? '' : ':' . $parsed['port'])
- . (empty($parsed['path']) ? '' : $parsed['path'])
- . (empty($parsed['query']) ? '' : '?' . $parsed['query'])
- . (empty($parsed['fragment']) ? '' : '#' . $parsed['fragment']);
- } else { // parse_url seems to have failed, try without it
- $arr = explode('.', $input);
- foreach ($arr as $k => $v) {
- $conv = $this->_decode($v);
- $arr[$k] = ($conv) ? $conv : $v;
- }
- $return = join('.', $arr);
- }
- } else { // Otherwise we consider it being a pure domain name string
- $return = $this->_decode($input);
- if (!$return)
- $return = $input;
- }
- // The output is UTF-8 by default, other output formats need conversion here
- // If one time encoding is given, use this, else the objects property
- switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
- case 'utf8':
- return $return;
- break;
- case 'ucs4_string':
- return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
- break;
- case 'ucs4_array':
- return $this->_utf8_to_ucs4($return);
- break;
- default:
- $this->_error('Unsupported output format');
- return false;
- }
- }
- /**
- * Encode a given UTF-8 domain name
- * @param string Domain name (UTF-8 or UCS-4)
- * [@param string Desired input encoding, see {@link set_parameter}]
- * @return string Encoded Domain name (ACE string)
- * @access public
- */
- function encode($decoded, $one_time_encoding = false) {
- // Forcing conversion of input to UCS4 array
- // If one time encoding is given, use this, else the objects property
- switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) {
- case 'utf8':
- $decoded = $this->_utf8_to_ucs4($decoded);
- break;
- case 'ucs4_string':
- $decoded = $this->_ucs4_string_to_ucs4($decoded);
- case 'ucs4_array':
- break;
- default:
- $this->_error('Unsupported input format: ' . ($one_time_encoding ? $one_time_encoding : $this->_api_encoding));
- return false;
- }
- // No input, no output, what else did you expect?
- if (empty($decoded))
- return '';
- // Anchors for iteration
- $last_begin = 0;
- // Output string
- $output = '';
- foreach ($decoded as $k => $v) {
- // Make sure to use just the plain dot
- switch ($v) {
- case 0x3002:
- case 0xFF0E:
- case 0xFF61:
- $decoded[$k] = 0x2E;
- // Right, no break here, the above are converted to dots anyway
- // Stumbling across an anchoring character
- case 0x2E:
- case 0x2F:
- case 0x3A:
- case 0x3F:
- case 0x40:
- // Neither email addresses nor URLs allowed in strict mode
- if ($this->_strict_mode) {
- $this->_error('Neither email addresses nor URLs are allowed in strict mode.');
- return false;
- } else {
- // Skip first char
- if ($k) {
- $encoded = '';
- $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k) - $last_begin)));
- if ($encoded) {
- $output .= $encoded;
- } else {
- $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k) - $last_begin)));
- }
- $output .= chr($decoded[$k]);
- }
- $last_begin = $k + 1;
- }
- }
- }
- // Catch the rest of the string
- if ($last_begin) {
- $inp_len = sizeof($decoded);
- $encoded = '';
- $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len) - $last_begin)));
- if ($encoded) {
- $output .= $encoded;
- } else {
- $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len) - $last_begin)));
- }
- return $output;
- } else {
- if ($output = $this->_encode($decoded)) {
- return $output;
- } else {
- return $this->_ucs4_to_utf8($decoded);
- }
- }
- }
- /**
- * Use this method to get the last error ocurred
- * @param void
- * @return string The last error, that occured
- * @access public
- */
- function get_last_error() {
- return $this->_error;
- }
- /**
- * The actual decoding algorithm
- * @access private
- */
- function _decode($encoded) {
- // We do need to find the Punycode prefix
- if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) {
- $this->_error('This is not a punycode string');
- return false;
- }
- $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded);
- // If nothing left after removing the prefix, it is hopeless
- if (!$encode_test) {
- $this->_error('The given encoded string was empty');
- return false;
- }
- // Find last occurence of the delimiter
- $delim_pos = strrpos($encoded, '-');
- if ($delim_pos > strlen($this->_punycode_prefix)) {
- for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) {
- $decoded[] = ord($encoded[$k]);
- }
- } else {
- $decoded = array();
- }
- $deco_len = count($decoded);
- $enco_len = strlen($encoded);
- // Wandering through the strings; init
- $is_first = true;
- $bias = $this->_initial_bias;
- $idx = 0;
- $char = $this->_initial_n;
- for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
- for ($old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base) {
- $digit = $this->_decode_digit($encoded[$enco_idx++]);
- $idx += $digit * $w;
- $t = ($k <= $bias) ? $this->_tmin :
- (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
- if ($digit < $t)
- break;
- $w = (int) ($w * ($this->_base - $t));
- }
- $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first);
- $is_first = false;
- $char += (int) ($idx / ($deco_len + 1));
- $idx %= ($deco_len + 1);
- if ($deco_len > 0) {
- // Make room for the decoded char
- for ($i = $deco_len; $i > $idx; $i--) {
- $decoded[$i] = $decoded[($i - 1)];
- }
- }
- $decoded[$idx++] = $char;
- }
- return $this->_ucs4_to_utf8($decoded);
- }
- /**
- * The actual encoding algorithm
- * @access private
- */
- function _encode($decoded) {
- // We cannot encode a domain name containing the Punycode prefix
- $extract = strlen($this->_punycode_prefix);
- $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix);
- $check_deco = array_slice($decoded, 0, $extract);
- if ($check_pref == $check_deco) {
- $this->_error('This is already a punycode string');
- return false;
- }
- // We will not try to encode strings consisting of basic code points only
- $encodable = false;
- foreach ($decoded as $k => $v) {
- if ($v > 0x7a) {
- $encodable = true;
- break;
- }
- }
- if (!$encodable) {
- $this->_error('The given string does not contain encodable chars');
- return false;
- }
- // Do NAMEPREP
- $decoded = $this->_nameprep($decoded);
- if (!$decoded || !is_array($decoded))
- return false; // NAMEPREP failed
- $deco_len = count($decoded);
- if (!$deco_len)
- return false; // Empty array
- $codecount = 0; // How many chars have been consumed
- $encoded = '';
- // Copy all basic code points to output
- for ($i = 0; $i < $deco_len; ++$i) {
- $test = $decoded[$i];
- // Will match [-0-9a-zA-Z]
- if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) {
- $encoded .= chr($decoded[$i]);
- $codecount++;
- }
- }
- if ($codecount == $deco_len)
- return $encoded; // All codepoints were basic ones
-
- // Start with the prefix; copy it to output
- $encoded = $this->_punycode_prefix . $encoded;
- // If we have basic code points in output, add an hyphen to the end
- if ($codecount)
- $encoded .= '-';
- // Now find and encode all non-basic code points
- $is_first = true;
- $cur_code = $this->_initial_n;
- $bias = $this->_initial_bias;
- $delta = 0;
- while ($codecount < $deco_len) {
- // Find the smallest code point >= the current code point and
- // remember the last ouccrence of it in the input
- for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) {
- if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) {
- $next_code = $decoded[$i];
- }
- }
- $delta += ($next_code - $cur_code) * ($codecount + 1);
- $cur_code = $next_code;
- // Scan input again and encode all characters whose code point is $cur_code
- for ($i = 0; $i < $deco_len; $i++) {
- if ($decoded[$i] < $cur_code) {
- $delta++;
- } elseif ($decoded[$i] == $cur_code) {
- for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) {
- $t = ($k <= $bias) ? $this->_tmin :
- (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias);
- if ($q < $t)
- break;
- $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval()
- $q = (int) (($q - $t) / ($this->_base - $t));
- }
- $encoded .= $this->_encode_digit($q);
- $bias = $this->_adapt($delta, $codecount + 1, $is_first);
- $codecount++;
- $delta = 0;
- $is_first = false;
- }
- }
- $delta++;
- $cur_code++;
- }
- return $encoded;
- }
- /**
- * Adapt the bias according to the current code point and position
- * @access private
- */
- function _adapt($delta, $npoints, $is_first) {
- $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2));
- $delta += intval($delta / $npoints);
- for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) {
- $delta = intval($delta / ($this->_base - $this->_tmin));
- }
- return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew));
- }
- /**
- * Encoding a certain digit
- * @access private
- */
- function _encode_digit($d) {
- return chr($d + 22 + 75 * ($d < 26));
- }
- /**
- * Decode a certain digit
- * @access private
- */
- function _decode_digit($cp) {
- $cp = ord($cp);
- return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base));
- }
- /**
- * Internal error handling method
- * @access private
- */
- function _error($error = '') {
- $this->_error = $error;
- }
- /**
- * Do Nameprep according to RFC3491 and RFC3454
- * @param array Unicode Characters
- * @return string Unicode Characters, Nameprep'd
- * @access private
- */
- function _nameprep($input) {
- $output = array();
- $error = false;
- //
- // Mapping
- // Walking through the input array, performing the required steps on each of
- // the input chars and putting the result into the output array
- // While mapping required chars we apply the cannonical ordering
- foreach ($input as $v) {
- // Map to nothing == skip that code point
- if (in_array($v, $this->NP['map_nothing']))
- continue;
- // Try to find prohibited input
- if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) {
- $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v));
- return false;
- }
- foreach ($this->NP['prohibit_ranges'] as $range) {
- if ($range[0] <= $v && $v <= $range[1]) {
- $this->_error('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v));
- return false;
- }
- }
- //
- // Hangul syllable decomposition
- if (0xAC00 <= $v && $v <= 0xD7AF) {
- foreach ($this->_hangul_decompose($v) as $out) {
- $output[] = (int) $out;
- }
- // There's a decomposition mapping for that code point
- } elseif (isset($this->NP['replacemaps'][$v])) {
- foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) {
- $output[] = (int) $out;
- }
- } else {
- $output[] = (int) $v;
- }
- }
- // Before applying any Combining, try to rearrange any Hangul syllables
- $output = $this->_hangul_compose($output);
- //
- // Combine code points
- //
- $last_class = 0;
- $last_starter = 0;
- $out_len = count($output);
- for ($i = 0; $i < $out_len; ++$i) {
- $class = $this->_get_combining_class($output[$i]);
- if ((!$last_class || $last_class > $class) && $class) {
- // Try to match
- $seq_len = $i - $last_starter;
- $out = $this->_combine(array_slice($output, $last_starter, $seq_len));
- // On match: Replace the last starter with the composed character and remove
- // the now redundant non-starter(s)
- if ($out) {
- $output[$last_starter] = $out;
- if (count($out) != $seq_len) {
- for ($j = $i + 1; $j < $out_len; ++$j) {
- $output[$j - 1] = $output[$j];
- }
- unset($output[$out_len]);
- }
- // Rewind the for loop by one, since there can be more possible compositions
- $i--;
- $out_len--;
- $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i - 1]);
- continue;
- }
- }
- // The current class is 0
- if (!$class)
- $last_starter = $i;
- $last_class = $class;
- }
- return $output;
- }
- /**
- * Decomposes a Hangul syllable
- * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
- * @param integer 32bit UCS4 code point
- * @return array Either Hangul Syllable decomposed or original 32bit value as one value array
- * @access private
- */
- function _hangul_decompose($char) {
- $sindex = (int) $char - $this->_sbase;
- if ($sindex < 0 || $sindex >= $this->_scount) {
- return array($char);
- }
- $result = array();
- $result[] = (int) $this->_lbase + $sindex / $this->_ncount;
- $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount;
- $T = intval($this->_tbase + $sindex % $this->_tcount);
- if ($T != $this->_tbase)
- $result[] = $T;
- return $result;
- }
- /**
- * Ccomposes a Hangul syllable
- * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
- * @param array Decomposed UCS4 sequence
- * @return array UCS4 sequence with syllables composed
- * @access private
- */
- function _hangul_compose($input) {
- $inp_len = count($input);
- if (!$inp_len)
- return array();
- $result = array();
- $last = (int) $input[0];
- $result[] = $last; // copy first char from input to output
- for ($i = 1; $i < $inp_len; ++$i) {
- $char = (int) $input[$i];
- $sindex = $last - $this->_sbase;
- $lindex = $last - $this->_lbase;
- $vindex = $char - $this->_vbase;
- $tindex = $char - $this->_tbase;
- // Find out, whether two current characters are LV and T
- if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) && 0 <= $tindex && $tindex <= $this->_tcount) {
- // create syllable of form LVT
- $last += $tindex;
- $result[(count($result) - 1)] = $last; // reset last
- continue; // discard char
- }
- // Find out, whether two current characters form L and V
- if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) {
- // create syllable of form LV
- $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount;
- $result[(count($result) - 1)] = $last; // reset last
- continue; // discard char
- }
- // if neither case was true, just add the character
- $last = $char;
- $result[] = $char;
- }
- return $result;
- }
- /**
- * Returns the combining class of a certain wide char
- * @param integer Wide char to check (32bit integer)
- * @return integer Combining class if found, else 0
- * @access private
- */
- function _get_combining_class($char) {
- return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0;
- }
- /**
- * Apllies the cannonical ordering of a decomposed UCS4 sequence
- * @param array Decomposed UCS4 sequence
- * @return array Ordered USC4 sequence
- * @access private
- */
- function _apply_cannonical_ordering($input) {
- $swap = true;
- $size = count($input);
- while ($swap) {
- $swap = false;
- $last = $this->_get_combining_class(intval($input[0]));
- for ($i = 0; $i < $size - 1; ++$i) {
- $next = $this->_get_combining_class(intval($input[$i + 1]));
- if ($next != 0 && $last > $next) {
- // Move item leftward until it fits
- for ($j = $i + 1; $j > 0; --$j) {
- if ($this->_get_combining_class(intval($input[$j - 1])) <= $next)
- break;
- $t = intval($input[$j]);
- $input[$j] = intval($input[$j - 1]);
- $input[$j - 1] = $t;
- $swap = true;
- }
- // Reentering the loop looking at the old character again
- $next = $last;
- }
- $last = $next;
- }
- }
- return $input;
- }
- /**
- * Do composition of a sequence of starter and non-starter
- * @param array UCS4 Decomposed sequence
- * @return array Ordered USC4 sequence
- * @access private
- */
- function _combine($input) {
- $inp_len = count($input);
- foreach ($this->NP['replacemaps'] as $np_src => $np_target) {
- if ($np_target[0] != $input[0])
- continue;
- if (count($np_target) != $inp_len)
- continue;
- $hit = false;
- foreach ($input as $k2 => $v2) {
- if ($v2 == $np_target[$k2]) {
- $hit = true;
- } else {
- $hit = false;
- break;
- }
- }
- if ($hit)
- return $np_src;
- }
- return false;
- }
- /**
- * This converts an UTF-8 encoded string to its UCS-4 representation
- * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
- * each of the "chars". This is due to PHP not being able to handle strings with
- * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
- * The following UTF-8 encodings are supported:
- * bytes bits representation
- * 1 7 0xxxxxxx
- * 2 11 110xxxxx 10xxxxxx
- * 3 16 1110xxxx 10xxxxxx 10xxxxxx
- * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * Each x represents a bit that can be used to store character data.
- * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
- * @access private
- */
- function _utf8_to_ucs4($input) {
- $output = array();
- $out_len = 0;
- $inp_len = strlen($input);
- $mode = 'next';
- $test = 'none';
- for ($k = 0; $k < $inp_len; ++$k) {
- $v = ord($input[$k]); // Extract byte from input string
- if ($v < 128) { // We found an ASCII char - put into stirng as is
- $output[$out_len] = $v;
- ++$out_len;
- if ('add' == $mode) {
- $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k);
- return false;
- }
- continue;
- }
- if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
- $start_byte = $v;
- $mode = 'add';
- $test = 'range';
- if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
- $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
- $v = ($v - 192) << 6;
- } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
- $next_byte = 1;
- $v = ($v - 224) << 12;
- } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- $next_byte = 2;
- $v = ($v - 240) << 18;
- } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- $next_byte = 3;
- $v = ($v - 248) << 24;
- } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- $next_byte = 4;
- $v = ($v - 252) << 30;
- } else {
- $this->_error('This might be UTF-8, but I don\'t understand it at byte ' . $k);
- return false;
- }
- if ('add' == $mode) {
- $output[$out_len] = (int) $v;
- ++$out_len;
- continue;
- }
- }
- if ('add' == $mode) {
- if (!$this->_allow_overlong && $test == 'range') {
- $test = 'none';
- if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
- $this->_error('Bogus UTF-8 character detected (out of legal range) at byte ' . $k);
- return false;
- }
- }
- if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
- $v = ($v - 128) << ($next_byte * 6);
- $output[($out_len - 1)] += $v;
- --$next_byte;
- } else {
- $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k);
- return false;
- }
- if ($next_byte < 0) {
- $mode = 'next';
- }
- }
- } // for
- return $output;
- }
- /**
- * Convert UCS-4 string into UTF-8 string
- * See _utf8_to_ucs4() for details
- * @access private
- */
- function _ucs4_to_utf8($input) {
- $output = '';
- $k = 0;
- foreach ($input as $v) {
- ++$k;
- // $v = ord($v);
- if ($v < 128) { // 7bit are transferred literally
- $output .= chr($v);
- } elseif ($v < (1 << 11)) { // 2 bytes
- $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63));
- } elseif ($v < (1 << 16)) { // 3 bytes
- $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
- } elseif ($v < (1 << 21)) { // 4 bytes
- $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63))
- . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
- } elseif ($v < (1 << 26)) { // 5 bytes
- $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63))
- . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63))
- . chr(128 + ($v & 63));
- } elseif ($v < (1 << 31)) { // 6 bytes
- $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63))
- . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63))
- . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
- } else {
- $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k);
- return false;
- }
- }
- return $output;
- }
- /**
- * Convert UCS-4 array into UCS-4 string
- *
- * @access private
- */
- function _ucs4_to_ucs4_string($input) {
- $output = '';
- // Take array values and split output to 4 bytes per value
- // The bit mask is 255, which reads &11111111
- foreach ($input as $v) {
- $output .= chr(($v >> 24) & 255) . chr(($v >> 16) & 255) . chr(($v >> 8) & 255) . chr($v & 255);
- }
- return $output;
- }
- /**
- * Convert UCS-4 strin into UCS-4 garray
- *
- * @access private
- */
- function _ucs4_string_to_ucs4($input) {
- $output = array();
- $inp_len = strlen($input);
- // Input length must be dividable by 4
- if ($inp_len % 4) {
- $this->_error('Input UCS4 string is broken');
- return false;
- }
- // Empty input - return empty output
- if (!$inp_len)
- return $output;
- for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
- // Increment output position every 4 input bytes
- if (!($i % 4)) {
- $out_len++;
- $output[$out_len] = 0;
- }
- $output[$out_len] += ord($input[$i]) << (8 * (3 - ($i % 4) ) );
- }
- return $output;
- }
- }
- /**
- * Adapter class for aligning the API of idna_convert with that of Net_IDNA
- * @author Matthias Sommerfeld <mso@phlylabs.de>
- */
- class Net_IDNA_php4 extends idna_convert {
- /**
- * Sets a new option value. Available options and values:
- * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
- * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
- * [overlong - Unicode does not allow unnecessarily long encodings of chars,
- * to allow this, set this parameter to true, else to false;
- * default is false.]
- * [strict - true: strict mode, good for registration purposes - Causes errors
- * on failures; false: loose mode, ideal for "wildlife" applications
- * by silently ignoring errors and returning the original input instead
- *
- * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs)
- * @param string Value to use (if parameter 1 is a string)
- * @return boolean true on success, false otherwise
- * @access public
- */
- function setParams($option, $param = false) {
- return $this->IC->set_parameters($option, $param);
- }
- }
- class owncloud_Encoding {
- const ICONV_TRANSLIT = "TRANSLIT";
- const ICONV_IGNORE = "IGNORE";
- const WITHOUT_ICONV = "";
- protected static $win1252ToUtf8 = array(
- 128 => "\xe2\x82\xac",
- 130 => "\xe2\x80\x9a",
- 131 => "\xc6\x92",
- 132 => "\xe2\x80\x9e",
- 133 => "\xe2\x80\xa6",
- 134 => "\xe2\x80\xa0",
- 135 => "\xe2\x80\xa1",
- 136 => "\xcb\x86",
- 137 => "\xe2\x80\xb0",
- 138 => "\xc5\xa0",
- 139 => "\xe2\x80\xb9",
- 140 => "\xc5\x92",
- 142 => "\xc5\xbd",
- 145 => "\xe2\x80\x98",
- 146 => "\xe2\x80\x99",
- 147 => "\xe2\x80\x9c",
- 148 => "\xe2\x80\x9d",
- 149 => "\xe2\x80\xa2",
- 150 => "\xe2\x80\x93",
- 151 => "\xe2\x80\x94",
- 152 => "\xcb\x9c",
- 153 => "\xe2\x84\xa2",
- 154 => "\xc5\xa1",
- 155 => "\xe2\x80\xba",
- 156 => "\xc5\x93",
- 158 => "\xc5\xbe",
- 159 => "\xc5\xb8"
- );
- protected static $brokenUtf8ToUtf8 = array(
- "\xc2\x80" => "\xe2\x82\xac",
- "\xc2\x82" => "\xe2\x80\x9a",
- "\xc2\x83" => "\xc6\x92",
- "\xc2\x84" => "\xe2\x80\x9e",
- "\xc2\x85" => "\xe2\x80\xa6",
- "\xc2\x86" => "\xe2\x80\xa0",
- "\xc2\x87" => "\xe2\x80\xa1",
- "\xc2\x88" => "\xcb\x86",
- "\xc2\x89" => "\xe2\x80\xb0",
- "\xc2\x8a" => "\xc5\xa0",
- "\xc2\x8b" => "\xe2\x80\xb9",
- "\xc2\x8c" => "\xc5\x92",
- "\xc2\x8e" => "\xc5\xbd",
- "\xc2\x91" => "\xe2\x80\x98",
- "\xc2\x92" => "\xe2\x80\x99",
- "\xc2\x93" => "\xe2\x80\x9c",
- "\xc2\x94" => "\xe2\x80\x9d",
- "\xc2\x95" => "\xe2\x80\xa2",
- "\xc2\x96" => "\xe2\x80\x93",
- "\xc2\x97" => "\xe2\x80\x94",
- "\xc2\x98" => "\xcb\x9c",
- "\xc2\x99" => "\xe2\x84\xa2",
- "\xc2\x9a" => "\xc5\xa1",
- "\xc2\x9b" => "\xe2\x80\xba",
- "\xc2\x9c" => "\xc5\x93",
- "\xc2\x9e" => "\xc5\xbe",
- "\xc2\x9f" => "\xc5\xb8"
- );
- protected static $utf8ToWin1252 = array(
- "\xe2\x82\xac" => "\x80",
- "\xe2\x80\x9a" => "\x82",
- "\xc6\x92" => "\x83",
- "\xe2\x80\x9e" => "\x84",
- "\xe2\x80\xa6" => "\x85",
- "\xe2\x80\xa0" => "\x86",
- "\xe2\x80\xa1" => "\x87",
- "\xcb\x86" => "\x88",
- "\xe2\x80\xb0" => "\x89",
- "\xc5\xa0" => "\x8a",
- "\xe2\x80\xb9" => "\x8b",
- "\xc5\x92" => "\x8c",
- "\xc5\xbd" => "\x8e",
- "\xe2\x80\x98" => "\x91",
- "\xe2\x80\x99" => "\x92",
- "\xe2\x80\x9c" => "\x93",
- "\xe2\x80\x9d" => "\x94",
- "\xe2\x80\xa2" => "\x95",
- "\xe2\x80\x93" => "\x96",
- "\xe2\x80\x94" => "\x97",
- "\xcb\x9c" => "\x98",
- "\xe2\x84\xa2" => "\x99",
- "\xc5\xa1" => "\x9a",
- "\xe2\x80\xba" => "\x9b",
- "\xc5\x93" => "\x9c",
- "\xc5\xbe" => "\x9e",
- "\xc5\xb8" => "\x9f"
- );
- static function toUTF8($text) {
- if (is_array($text)) {
- foreach ($text as $k => $v) {
- $text[$k] = self::toUTF8($v);
- }
- return $text;
- } elseif (is_string($text)) {
- if (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload')) & 2) {
- $max = mb_strlen($text, '8bit');
- } else {
- $max = strlen($text);
- }
- $buf = "";
- for ($i = 0; $i < $max; $i++) {
- $c1 = $text[$i];
- if ($c1 >= "\xc0") { //Should be converted to UTF8, if it's not UTF8 already
- $c2 = $i + 1 >= $max ? "\x00" : $text[$i + 1];
- $c3 = $i + 2 >= $max ? "\x00" : $text[$i + 2];
- $c4 = $i + 3 >= $max ? "\x00" : $text[$i + 3];
- if ($c1 >= "\xc0" & $c1 <= "\xdf") { //looks like 2 bytes UTF8
- if ($c2 >= "\x80" && $c2 <= "\xbf") { //yeah, almost sure it's UTF8 already
- $buf .= $c1 . $c2;
- $i++;
- } else { //not valid UTF8. Convert it.
- $cc1 = (chr(ord($c1) / 64) | "\xc0");
- $cc2 = ($c1 & "\x3f") | "\x80";
- $buf .= $cc1 . $cc2;
- }
- } elseif ($c1 >= "\xe0" & $c1 <= "\xef") { //looks like 3 bytes UTF8
- if ($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf") { //yeah, almost sure it's UTF8 already
- $buf .= $c1 . $c2 . $c3;
- $i = $i + 2;
- } else { //not valid UTF8. Convert it.
- $cc1 = (chr(ord($c1) / 64) | "\xc0");
- $cc2 = ($c1 & "\x3f") | "\x80";
- $buf .= $cc1 . $cc2;
- }
- } elseif ($c1 >= "\xf0" & $c1 <= "\xf7") { //looks like 4 bytes UTF8
- if ($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf") { //yeah, almost sure it's UTF8 already
- $buf .= $c1 . $c2 . $c3 . $c4;
- $i = $i + 3;
- } else { //not valid UTF8. Convert it.
- $cc1 = (chr(ord($c1) / 64) | "\xc0");
- $cc2 = ($c1 & "\x3f") | "\x80";
- $buf .= $cc1 . $cc2;
- }
- } else { //doesn't look like UTF8, but should be converted
- $cc1 = (chr(ord($c1) / 64) | "\xc0");
- $cc2 = (($c1 & "\x3f") | "\x80");
- $buf .= $cc1 . $cc2;
- }
- } elseif (($c1 & "\xc0") == "\x80") { // needs conversion
- if (isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
- $buf .= self::$win1252ToUtf8[ord($c1)];
- } else {
- $cc1 = (chr(ord($c1) / 64) | "\xc0");
- $cc2 = (($c1 & "\x3f") | "\x80");
- $buf .= $cc1 . $cc2;
- }
- } else { // it doesn't need conversion
- $buf .= $c1;
- }
- }
- return $buf;
- } else {
- return $text;
- }
- }
- static function toWin1252($text, $option = self::WITHOUT_ICONV) {
- if (is_array($text)) {
- foreach ($text as $k => $v) {
- $text[$k] = self::toWin1252($v, $option);
- }
- return $text;
- } elseif (is_string($text)) {
- return static::utf8_decode($text, $option);
- } else {
- return $text;
- }
- }
- static function toISO8859($text) {
- return self::toWin1252($text);
- }
- static function toLatin1($text) {
- return self::toWin1252($text);
- }
- static function fixUTF8($text, $option = self::WITHOUT_ICONV) {
- if (is_array($text)) {
- foreach ($text as $k => $v) {
- $text[$k] = self::fixUTF8($v, $option);
- }
- return $text;
- }
- $last = "";
- while ($last <> $text) {
- $last = $text;
- $text = self::toUTF8(static::utf8_decode($text, $option));
- }
- $text = self::toUTF8(static::utf8_decode($text, $option));
- return $text;
- }
- static function UTF8FixWin1252Chars($text) {
- // If you received an UTF-8 string that was converted from Windows-1252 as it was ISO8859-1
- // (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it.
- // See: http://en.wikipedia.org/wiki/Windows-1252
- return str_replace(array_keys(self::$brokenUtf8ToUtf8), array_values(self::$brokenUtf8ToUtf8), $text);
- }
- static function removeBOM($str = "") {
- if (substr($str, 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
- $str = substr($str, 3);
- }
- return $str;
- }
- public static function normalizeEncoding($encodingLabel) {
- $encoding = strtoupper($encodingLabel);
- $encoding = preg_replace('/[^a-zA-Z0-9\s]/', '', $encoding);
- $equivalences = array(
- 'ISO88591' => 'ISO-8859-1',
- 'ISO8859' => 'ISO-8859-1',
- 'ISO' => 'ISO-8859-1',
- 'LATIN1' => 'ISO-8859-1',
- 'LATIN' => 'ISO-8859-1',
- 'UTF8' => 'UTF-8',
- 'UTF' => 'UTF-8',
- 'WIN1252' => 'ISO-8859-1',
- 'WINDOWS1252' => 'ISO-8859-1'
- );
- if (empty($equivalences[$encoding])) {
- return 'UTF-8';
- }
- return $equivalences[$encoding];
- }
- public static function encode($encodingLabel, $text) {
- $encodingLabel = self::normalizeEncoding($encodingLabel);
- if ($encodingLabel == 'UTF-8')
- return Encoding::toUTF8($text);
- if ($encodingLabel == 'ISO-8859-1')
- return Encoding::toLatin1($text);
- }
- protected static function utf8_decode($text, $option) {
- if ($option == self::WITHOUT_ICONV || !function_exists('iconv')) {
- $o = mb_convert_encoding(
- str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), self::toUTF8($text)), 'ISO-8859-1', 'UTF-8');
- } else {
- $o = iconv("UTF-8", "Windows-1252" . ($option == self::ICONV_TRANSLIT ? '//TRANSLIT' : ($option == self::ICONV_IGNORE ? '//IGNORE' : '')), $text);
- }
- return $o;
- }
- }
- ?>
|