| @@ -1,507 +1,507 @@ | ||
| 1 | -<?php | |
| 2 | - | |
| 3 | -namespace WPCoder\Optimization; | |
| 4 | - | |
| 5 | -defined( 'ABSPATH' ) || exit; | |
| 6 | - | |
| 7 | -class Obfuscator { | |
| 8 | - // constants | |
| 9 | - const IGNORE = '$1'; | |
| 10 | - | |
| 11 | - // validate parameters | |
| 12 | - private $_script = ''; | |
| 13 | - private $_encoding = 62; | |
| 14 | - private $_fastDecode = true; | |
| 15 | - private $_specialChars = false; | |
| 16 | - | |
| 17 | - private $LITERAL_ENCODING = array( | |
| 18 | - 'None' => 0, | |
| 19 | - 'Numeric' => 10, | |
| 20 | - 'Normal' => 62, | |
| 21 | - 'High ASCII' => 95 | |
| 22 | - ); | |
| 23 | - | |
| 24 | - public function __construct( $_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false ) { | |
| 25 | - $this->_script = $_script . "\n"; | |
| 26 | - if ( array_key_exists( $_encoding, $this->LITERAL_ENCODING ) ) { | |
| 27 | - $_encoding = $this->LITERAL_ENCODING[ $_encoding ]; | |
| 28 | - } | |
| 29 | - $this->_encoding = min( (int) $_encoding, 95 ); | |
| 30 | - $this->_fastDecode = $_fastDecode; | |
| 31 | - $this->_specialChars = $_specialChars; | |
| 32 | - } | |
| 33 | - | |
| 34 | - public function pack() { | |
| 35 | - $this->_addParser( '_basicCompression' ); | |
| 36 | - if ( $this->_specialChars ) { | |
| 37 | - $this->_addParser( '_encodeSpecialChars' ); | |
| 38 | - } | |
| 39 | - if ( $this->_encoding ) { | |
| 40 | - $this->_addParser( '_encodeKeywords' ); | |
| 41 | - } | |
| 42 | - | |
| 43 | - // go! | |
| 44 | - return $this->_pack( $this->_script ); | |
| 45 | - } | |
| 46 | - | |
| 47 | - // apply all parsing routines | |
| 48 | - private function _pack( $script ) { | |
| 49 | - for ( $i = 0; isset( $this->_parsers[ $i ] ); $i ++ ) { | |
| 50 | - $script = call_user_func( array( &$this, $this->_parsers[ $i ] ), $script ); | |
| 51 | - } | |
| 52 | - | |
| 53 | - return $script; | |
| 54 | - } | |
| 55 | - | |
| 56 | - // keep a list of parsing functions, they'll be executed all at once | |
| 57 | - private $_parsers = array(); | |
| 58 | - | |
| 59 | - private function _addParser( $parser ) { | |
| 60 | - $this->_parsers[] = $parser; | |
| 61 | - } | |
| 62 | - | |
| 63 | - // zero encoding - just removal of white space and comments | |
| 64 | - private function _basicCompression( $script ) { | |
| 65 | - $parser = new ParseMaster(); | |
| 66 | - // make safe | |
| 67 | - $parser->escapeChar = '\\'; | |
| 68 | - // protect strings | |
| 69 | - $parser->add( '/\'[^\'\\n\\r]*\'/', self::IGNORE ); | |
| 70 | - $parser->add( '/"[^"\\n\\r]*"/', self::IGNORE ); | |
| 71 | - // remove comments | |
| 72 | - $parser->add( '/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ' ); | |
| 73 | - $parser->add( '/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ' ); | |
| 74 | - // protect regular expressions | |
| 75 | - $parser->add( '/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2' ); // IGNORE | |
| 76 | - $parser->add( '/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE ); | |
| 77 | - // remove: ;;; doSomething(); | |
| 78 | - if ( $this->_specialChars ) { | |
| 79 | - $parser->add( '/;;;[^\\n\\r]+[\\n\\r]/' ); | |
| 80 | - } | |
| 81 | - // remove redundant semi-colons | |
| 82 | - $parser->add( '/\\(;;\\)/', self::IGNORE ); // protect for (;;) loops | |
| 83 | - $parser->add( '/;+\\s*([};])/', '$2' ); | |
| 84 | - // apply the above | |
| 85 | - $script = $parser->exec( $script ); | |
| 86 | - | |
| 87 | - // remove white-space | |
| 88 | - $parser->add( '/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3' ); | |
| 89 | - $parser->add( '/([+\\-])\\s+([+\\-])/', '$2 $3' ); | |
| 90 | - $parser->add( '/\\s+/', '' ); | |
| 91 | - | |
| 92 | - // done | |
| 93 | - return $parser->exec( $script ); | |
| 94 | - } | |
| 95 | - | |
| 96 | - private function _encodeSpecialChars( $script ) { | |
| 97 | - $parser = new ParseMaster(); | |
| 98 | - // replace: $name -> n, $$name -> na | |
| 99 | - $parser->add( | |
| 100 | - '/((\\x24+)([a-zA-Z$_]+))(\\d*)/', | |
| 101 | - array( 'fn' => '_replace_name' ) | |
| 102 | - ); | |
| 103 | - // replace: _name -> _0, double-underscore (__name) is ignored | |
| 104 | - $regexp = '/\\b_[A-Za-z\\d]\\w*/'; | |
| 105 | - // build the word list | |
| 106 | - $keywords = $this->_analyze( $script, $regexp, '_encodePrivate' ); | |
| 107 | - // quick ref | |
| 108 | - $encoded = $keywords['encoded']; | |
| 109 | - | |
| 110 | - $parser->add( | |
| 111 | - $regexp, | |
| 112 | - array( | |
| 113 | - 'fn' => '_replace_encoded', | |
| 114 | - 'data' => $encoded | |
| 115 | - ) | |
| 116 | - ); | |
| 117 | - | |
| 118 | - return $parser->exec( $script ); | |
| 119 | - } | |
| 120 | - | |
| 121 | - private function _encodeKeywords( $script ) { | |
| 122 | - // escape high-ascii values already in the script (i.e. in strings) | |
| 123 | - if ( $this->_encoding > 62 ) { | |
| 124 | - $script = $this->_escape95( $script ); | |
| 125 | - } | |
| 126 | - // create the parser | |
| 127 | - $parser = new ParseMaster(); | |
| 128 | - $encode = $this->_getEncoder( $this->_encoding ); | |
| 129 | - // for high-ascii, don't encode single character low-ascii | |
| 130 | - $regexp = ( $this->_encoding > 62 ) ? '/\\w\\w+/' : '/\\w+/'; | |
| 131 | - // build the word list | |
| 132 | - $keywords = $this->_analyze( $script, $regexp, $encode ); | |
| 133 | - $encoded = $keywords['encoded']; | |
| 134 | - | |
| 135 | - // encode | |
| 136 | - $parser->add( | |
| 137 | - $regexp, | |
| 138 | - array( | |
| 139 | - 'fn' => '_replace_encoded', | |
| 140 | - 'data' => $encoded | |
| 141 | - ) | |
| 142 | - ); | |
| 143 | - if ( empty( $script ) ) { | |
| 144 | - return $script; | |
| 145 | - } else { | |
| 146 | - //$res = $parser->exec($script); | |
| 147 | - //$res = $this->_bootStrap($res, $keywords); | |
| 148 | - //return $res; | |
| 149 | - return $this->_bootStrap( $parser->exec( $script ), $keywords ); | |
| 150 | - } | |
| 151 | - } | |
| 152 | - | |
| 153 | - private function _analyze( $script, $regexp, $encode ) { | |
| 154 | - // analyse | |
| 155 | - // retreive all words in the script | |
| 156 | - $all = array(); | |
| 157 | - preg_match_all( $regexp, $script, $all ); | |
| 158 | - $_sorted = array(); // list of words sorted by frequency | |
| 159 | - $_encoded = array(); // dictionary of word->encoding | |
| 160 | - $_protected = array(); // instances of "protected" words | |
| 161 | - $all = $all[0]; // simulate the javascript comportement of global match | |
| 162 | - if ( ! empty( $all ) ) { | |
| 163 | - $unsorted = array(); // same list, not sorted | |
| 164 | - $protected = array(); // "protected" words (dictionary of word->"word") | |
| 165 | - $value = array(); // dictionary of charCode->encoding (eg. 256->ff) | |
| 166 | - $this->_count = array(); // word->count | |
| 167 | - $i = count( $all ); | |
| 168 | - $j = 0; //$word = null; | |
| 169 | - // count the occurrences - used for sorting later | |
| 170 | - do { | |
| 171 | - -- $i; | |
| 172 | - $word = '$' . $all[ $i ]; | |
| 173 | - if ( ! isset( $this->_count[ $word ] ) ) { | |
| 174 | - $this->_count[ $word ] = 0; | |
| 175 | - $unsorted[ $j ] = $word; | |
| 176 | - // make a dictionary of all of the protected words in this script | |
| 177 | - // these are words that might be mistaken for encoding | |
| 178 | - //if (is_string($encode) && method_exists($this, $encode)) | |
| 179 | - $values[ $j ] = call_user_func( array( &$this, $encode ), $j ); | |
| 180 | - $protected[ '$' . $values[ $j ] ] = $j ++; | |
| 181 | - } | |
| 182 | - // increment the word counter | |
| 183 | - $this->_count[ $word ] ++; | |
| 184 | - } while ( $i > 0 ); | |
| 185 | - // prepare to sort the word list, first we must protect | |
| 186 | - // words that are also used as codes. we assign them a code | |
| 187 | - // equivalent to the word itself. | |
| 188 | - // e.g. if "do" falls within our encoding range | |
| 189 | - // then we store keywords["do"] = "do"; | |
| 190 | - // this avoids problems when decoding | |
| 191 | - $i = count( $unsorted ); | |
| 192 | - do { | |
| 193 | - $word = $unsorted[ -- $i ]; | |
| 194 | - if ( isset( $protected[ $word ] ) /*!= null*/ ) { | |
| 195 | - $_sorted[ $protected[ $word ] ] = substr( $word, 1 ); | |
| 196 | - $_protected[ $protected[ $word ] ] = true; | |
| 197 | - $this->_count[ $word ] = 0; | |
| 198 | - } | |
| 199 | - } while ( $i ); | |
| 200 | - | |
| 201 | - // sort the words by frequency | |
| 202 | - // Note: the javascript and php version of sort can be different : | |
| 203 | - // in php manual, usort : | |
| 204 | - // " If two members compare as equal, | |
| 205 | - // their order in the sorted array is undefined." | |
| 206 | - // so the final packed script is different of the Dean's javascript version | |
| 207 | - // but equivalent. | |
| 208 | - // the ECMAscript standard does not guarantee this behaviour, | |
| 209 | - // and thus not all browsers (e.g. Mozilla versions dating back to at | |
| 210 | - // least 2003) respect this. | |
| 211 | - usort( $unsorted, array( &$this, '_sortWords' ) ); | |
| 212 | - $j = 0; | |
| 213 | - // because there are "protected" words in the list | |
| 214 | - // we must add the sorted words around them | |
| 215 | - do { | |
| 216 | - if ( ! isset( $_sorted[ $i ] ) ) { | |
| 217 | - $_sorted[ $i ] = substr( $unsorted[ $j ++ ], 1 ); | |
| 218 | - } | |
| 219 | - $_encoded[ $_sorted[ $i ] ] = $values[ $i ]; | |
| 220 | - } while ( ++ $i < count( $unsorted ) ); | |
| 221 | - } | |
| 222 | - | |
| 223 | - return array( | |
| 224 | - 'sorted' => $_sorted, | |
| 225 | - 'encoded' => $_encoded, | |
| 226 | - 'protected' => $_protected | |
| 227 | - ); | |
| 228 | - } | |
| 229 | - | |
| 230 | - private $_count = array(); | |
| 231 | - | |
| 232 | - private function _sortWords( $match1, $match2 ) { | |
| 233 | - return $this->_count[ $match2 ] - $this->_count[ $match1 ]; | |
| 234 | - } | |
| 235 | - | |
| 236 | - // build the boot function used for loading and decoding | |
| 237 | - private function _bootStrap( $packed, $keywords ) { | |
| 238 | - $ENCODE = $this->_safeRegExp( '$encode\\($count\\)' ); | |
| 239 | - | |
| 240 | - // $packed: the packed script | |
| 241 | - $packed = "'" . $this->_escape( $packed ) . "'"; | |
| 242 | - | |
| 243 | - // $ascii: base for encoding | |
| 244 | - $ascii = min( count( $keywords['sorted'] ), $this->_encoding ); | |
| 245 | - if ( $ascii == 0 ) { | |
| 246 | - $ascii = 1; | |
| 247 | - } | |
| 248 | - | |
| 249 | - // $count: number of words contained in the script | |
| 250 | - $count = count( $keywords['sorted'] ); | |
| 251 | - | |
| 252 | - // $keywords: list of words contained in the script | |
| 253 | - foreach ( $keywords['protected'] as $i => $value ) { | |
| 254 | - $keywords['sorted'][ $i ] = ''; | |
| 255 | - } | |
| 256 | - // convert from a string to an array | |
| 257 | - ksort( $keywords['sorted'] ); | |
| 258 | - $keywords = "'" . implode( '|', $keywords['sorted'] ) . "'.split('|')"; | |
| 259 | - | |
| 260 | - $encode = ( $this->_encoding > 62 ) ? '_encode95' : $this->_getEncoder( $ascii ); | |
| 261 | - $encode = $this->_getJSFunction( $encode ); | |
| 262 | - $encode = preg_replace( '/_encoding/', '$ascii', $encode ); | |
| 263 | - $encode = preg_replace( '/arguments\\.callee/', '$encode', $encode ); | |
| 264 | - $inline = '\\$count' . ( $ascii > 10 ? '.toString(\\$ascii)' : '' ); | |
| 265 | - | |
| 266 | - // $decode: code snippet to speed up decoding | |
| 267 | - if ( $this->_fastDecode ) { | |
| 268 | - // create the decoder | |
| 269 | - $decode = $this->_getJSFunction( '_decodeBody' ); | |
| 270 | - if ( $this->_encoding > 62 ) { | |
| 271 | - $decode = preg_replace( '/\\\\w/', '[\\xa1-\\xff]', $decode ); | |
| 272 | - } // perform the encoding inline for lower ascii values | |
| 273 | - elseif ( $ascii < 36 ) { | |
| 274 | - $decode = preg_replace( $ENCODE, $inline, $decode ); | |
| 275 | - } | |
| 276 | - // special case: when $count==0 there are no keywords. I want to keep | |
| 277 | - // the basic shape of the unpacking funcion so i'll frig the code... | |
| 278 | - if ( $count == 0 ) { | |
| 279 | - $decode = preg_replace( $this->_safeRegExp( '($count)\\s*=\\s*1' ), '$1=0', $decode, 1 ); | |
| 280 | - } | |
| 281 | - } | |
| 282 | - | |
| 283 | - // boot function | |
| 284 | - $unpack = $this->_getJSFunction( '_unpack' ); | |
| 285 | - if ( $this->_fastDecode ) { | |
| 286 | - // insert the decoder | |
| 287 | - $this->buffer = $decode; | |
| 288 | - $unpack = preg_replace_callback( '/\\{/', array( &$this, '_insertFastDecode' ), $unpack, 1 ); | |
| 289 | - } | |
| 290 | - $unpack = preg_replace( '/"/', "'", $unpack ); | |
| 291 | - if ( $this->_encoding > 62 ) { // high-ascii | |
| 292 | - // get rid of the word-boundaries for regexp matches | |
| 293 | - $unpack = preg_replace( '/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack ); | |
| 294 | - } | |
| 295 | - if ( $ascii > 36 || $this->_encoding > 62 || $this->_fastDecode ) { | |
| 296 | - // insert the encode function | |
| 297 | - $this->buffer = $encode; | |
| 298 | - $unpack = preg_replace_callback( '/\\{/', array( &$this, '_insertFastEncode' ), $unpack, 1 ); | |
| 299 | - } else { | |
| 300 | - // perform the encoding inline | |
| 301 | - $unpack = preg_replace( $ENCODE, $inline, $unpack ); | |
| 302 | - } | |
| 303 | - // pack the boot function too | |
| 304 | - $unpackPacker = new Obfuscator( $unpack, 0, false, true ); | |
| 305 | - $unpack = $unpackPacker->pack(); | |
| 306 | - | |
| 307 | - // arguments | |
| 308 | - $params = array( $packed, $ascii, $count, $keywords ); | |
| 309 | - if ( $this->_fastDecode ) { | |
| 310 | - $params[] = 0; | |
| 311 | - $params[] = '{}'; | |
| 312 | - } | |
| 313 | - $params = implode( ',', $params ); | |
| 314 | - | |
| 315 | - // the whole thing | |
| 316 | - return 'eval(' . $unpack . '(' . $params . "))\n"; | |
| 317 | - } | |
| 318 | - | |
| 319 | - private $buffer; | |
| 320 | - | |
| 321 | - private function _insertFastDecode( $match ) { | |
| 322 | - return '{' . $this->buffer . ';'; | |
| 323 | - } | |
| 324 | - | |
| 325 | - private function _insertFastEncode( $match ) { | |
| 326 | - return '{$encode=' . $this->buffer . ';'; | |
| 327 | - } | |
| 328 | - | |
| 329 | - // mmm.. ..which one do i need ?? | |
| 330 | - private function _getEncoder( $ascii ) { | |
| 331 | - return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ? | |
| 332 | - '_encode95' : '_encode62' : '_encode36' : '_encode10'; | |
| 333 | - } | |
| 334 | - | |
| 335 | - // zero encoding | |
| 336 | - // characters: 0123456789 | |
| 337 | - private function _encode10( $charCode ) { | |
| 338 | - return $charCode; | |
| 339 | - } | |
| 340 | - | |
| 341 | - // inherent base36 support | |
| 342 | - // characters: 0123456789abcdefghijklmnopqrstuvwxyz | |
| 343 | - private function _encode36( $charCode ) { | |
| 344 | - return base_convert( $charCode, 10, 36 ); | |
| 345 | - } | |
| 346 | - | |
| 347 | - // hitch a ride on base36 and add the upper case alpha characters | |
| 348 | - // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | |
| 349 | - private function _encode62( $charCode ) { | |
| 350 | - $res = ''; | |
| 351 | - if ( $charCode >= $this->_encoding ) { | |
| 352 | - $res = $this->_encode62( (int) ( $charCode / $this->_encoding ) ); | |
| 353 | - } | |
| 354 | - $charCode = $charCode % $this->_encoding; | |
| 355 | - | |
| 356 | - if ( $charCode > 35 ) { | |
| 357 | - return $res . chr( $charCode + 29 ); | |
| 358 | - } else { | |
| 359 | - return $res . base_convert( $charCode, 10, 36 ); | |
| 360 | - } | |
| 361 | - } | |
| 362 | - | |
| 363 | - // use high-ascii values | |
| 364 | - // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ | |
| 365 | - private function _encode95( $charCode ) { | |
| 366 | - $res = ''; | |
| 367 | - if ( $charCode >= $this->_encoding ) { | |
| 368 | - $res = $this->_encode95( $charCode / $this->_encoding ); | |
| 369 | - } | |
| 370 | - | |
| 371 | - return $res . chr( ( $charCode % $this->_encoding ) + 161 ); | |
| 372 | - } | |
| 373 | - | |
| 374 | - private function _safeRegExp( $string ) { | |
| 375 | - return '/' . preg_replace( '/\$/', '\\\$', $string ) . '/'; | |
| 376 | - } | |
| 377 | - | |
| 378 | - private function _encodePrivate( $charCode ) { | |
| 379 | - return "_" . $charCode; | |
| 380 | - } | |
| 381 | - | |
| 382 | - // protect characters used by the parser | |
| 383 | - private function _escape( $script ) { | |
| 384 | - return preg_replace( '/([\\\\\'])/', '\\\$1', $script ); | |
| 385 | - } | |
| 386 | - | |
| 387 | - // protect high-ascii characters already in the script | |
| 388 | - private function _escape95( $script ) { | |
| 389 | - return preg_replace_callback( | |
| 390 | - '/[\\xa1-\\xff]/', | |
| 391 | - array( &$this, '_escape95Bis' ), | |
| 392 | - $script | |
| 393 | - ); | |
| 394 | - } | |
| 395 | - | |
| 396 | - private function _escape95Bis( $match ) { | |
| 397 | - return '\x' . ( (string) dechex( ord( $match ) ) ); | |
| 398 | - } | |
| 399 | - | |
| 400 | - | |
| 401 | - private function _getJSFunction( $aName ) { | |
| 402 | - if ( defined( 'self::JSFUNCTION' . $aName ) ) { | |
| 403 | - return constant( 'self::JSFUNCTION' . $aName ); | |
| 404 | - } else { | |
| 405 | - return ''; | |
| 406 | - } | |
| 407 | - } | |
| 408 | - | |
| 409 | - // JavaScript Functions used. | |
| 410 | - // Note : In Dean's version, these functions are converted | |
| 411 | - // with 'String(aFunctionName);'. | |
| 412 | - // This internal conversion complete the original code, ex : | |
| 413 | - // 'while (aBool) anAction();' is converted to | |
| 414 | - // 'while (aBool) { anAction(); }'. | |
| 415 | - // The JavaScript functions below are corrected. | |
| 416 | - | |
| 417 | - // unpacking function - this is the boot strap function | |
| 418 | - // data extracted from this packing routine is passed to | |
| 419 | - // this function when decoded in the target | |
| 420 | - // NOTE ! : without the ';' final. | |
| 421 | - const JSFUNCTION_unpack = | |
| 422 | - | |
| 423 | - 'function($packed, $ascii, $count, $keywords, $encode, $decode) { | |
| 424 | - while ($count--) { | |
| 425 | - if ($keywords[$count]) { | |
| 426 | - $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); | |
| 427 | - } | |
| 428 | - } | |
| 429 | - return $packed; | |
| 430 | - }'; | |
| 431 | - /* | |
| 432 | - 'function($packed, $ascii, $count, $keywords, $encode, $decode) { | |
| 433 | - while ($count--) | |
| 434 | - if ($keywords[$count]) | |
| 435 | - $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); | |
| 436 | - return $packed; | |
| 437 | - }'; | |
| 438 | - */ | |
| 439 | - | |
| 440 | - // code-snippet inserted into the unpacker to speed up decoding | |
| 441 | - const JSFUNCTION_decodeBody = | |
| 442 | -//_decode = function() { | |
| 443 | -// does the browser support String.replace where the | |
| 444 | -// replacement value is a function? | |
| 445 | - | |
| 446 | - ' if (!\'\'.replace(/^/, String)) { | |
| 447 | - // decode all the values we need | |
| 448 | - while ($count--) { | |
| 449 | - $decode[$encode($count)] = $keywords[$count] || $encode($count); | |
| 450 | - } | |
| 451 | - // global replacement function | |
| 452 | - $keywords = [function ($encoded) {return $decode[$encoded]}]; | |
| 453 | - // generic match | |
| 454 | - $encode = function () {return \'\\\\w+\'}; | |
| 455 | - // reset the loop counter - we are now doing a global replace | |
| 456 | - $count = 1; | |
| 457 | - } | |
| 458 | - '; | |
| 459 | -//}; | |
| 460 | - /* | |
| 461 | - ' if (!\'\'.replace(/^/, String)) { | |
| 462 | - // decode all the values we need | |
| 463 | - while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count); | |
| 464 | - // global replacement function | |
| 465 | - $keywords = [function ($encoded) {return $decode[$encoded]}]; | |
| 466 | - // generic match | |
| 467 | - $encode = function () {return\'\\\\w+\'}; | |
| 468 | - // reset the loop counter - we are now doing a global replace | |
| 469 | - $count = 1; | |
| 470 | - }'; | |
| 471 | - */ | |
| 472 | - | |
| 473 | - // zero encoding | |
| 474 | - // characters: 0123456789 | |
| 475 | - const JSFUNCTION_encode10 = | |
| 476 | - 'function($charCode) { | |
| 477 | - return $charCode; | |
| 478 | - }';//;'; | |
| 479 | - | |
| 480 | - // inherent base36 support | |
| 481 | - // characters: 0123456789abcdefghijklmnopqrstuvwxyz | |
| 482 | - const JSFUNCTION_encode36 = | |
| 483 | - 'function($charCode) { | |
| 484 | - return $charCode.toString(36); | |
| 485 | - }';//;'; | |
| 486 | - | |
| 487 | - // hitch a ride on base36 and add the upper case alpha characters | |
| 488 | - // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | |
| 489 | - const JSFUNCTION_encode62 = | |
| 490 | - 'function($charCode) { | |
| 491 | - return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) + | |
| 492 | - (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); | |
| 493 | - }'; | |
| 494 | - | |
| 495 | - // use high-ascii values | |
| 496 | - // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ | |
| 497 | - const JSFUNCTION_encode95 = | |
| 498 | - 'function($charCode) { | |
| 499 | - return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) + | |
| 500 | - String.fromCharCode($charCode % _encoding + 161); | |
| 501 | - }'; | |
| 502 | - | |
| 503 | -} | |
| 504 | - | |
| 505 | - | |
| 506 | - | |
| 507 | - | |
| 1 | +<?php | |
| 2 | + | |
| 3 | +namespace WPCoder\Optimization; | |
| 4 | + | |
| 5 | +defined( 'ABSPATH' ) || exit; | |
| 6 | + | |
| 7 | +class Obfuscator { | |
| 8 | + // constants | |
| 9 | + const IGNORE = '$1'; | |
| 10 | + | |
| 11 | + // validate parameters | |
| 12 | + private $_script = ''; | |
| 13 | + private $_encoding = 62; | |
| 14 | + private $_fastDecode = true; | |
| 15 | + private $_specialChars = false; | |
| 16 | + | |
| 17 | + private $LITERAL_ENCODING = array( | |
| 18 | + 'None' => 0, | |
| 19 | + 'Numeric' => 10, | |
| 20 | + 'Normal' => 62, | |
| 21 | + 'High ASCII' => 95 | |
| 22 | + ); | |
| 23 | + | |
| 24 | + public function __construct( $_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false ) { | |
| 25 | + $this->_script = $_script . "\n"; | |
| 26 | + if ( array_key_exists( $_encoding, $this->LITERAL_ENCODING ) ) { | |
| 27 | + $_encoding = $this->LITERAL_ENCODING[ $_encoding ]; | |
| 28 | + } | |
| 29 | + $this->_encoding = min( (int) $_encoding, 95 ); | |
| 30 | + $this->_fastDecode = $_fastDecode; | |
| 31 | + $this->_specialChars = $_specialChars; | |
| 32 | + } | |
| 33 | + | |
| 34 | + public function pack() { | |
| 35 | + $this->_addParser( '_basicCompression' ); | |
| 36 | + if ( $this->_specialChars ) { | |
| 37 | + $this->_addParser( '_encodeSpecialChars' ); | |
| 38 | + } | |
| 39 | + if ( $this->_encoding ) { | |
| 40 | + $this->_addParser( '_encodeKeywords' ); | |
| 41 | + } | |
| 42 | + | |
| 43 | + // go! | |
| 44 | + return $this->_pack( $this->_script ); | |
| 45 | + } | |
| 46 | + | |
| 47 | + // apply all parsing routines | |
| 48 | + private function _pack( $script ) { | |
| 49 | + for ( $i = 0; isset( $this->_parsers[ $i ] ); $i ++ ) { | |
| 50 | + $script = call_user_func( array( &$this, $this->_parsers[ $i ] ), $script ); | |
| 51 | + } | |
| 52 | + | |
| 53 | + return $script; | |
| 54 | + } | |
| 55 | + | |
| 56 | + // keep a list of parsing functions, they'll be executed all at once | |
| 57 | + private $_parsers = array(); | |
| 58 | + | |
| 59 | + private function _addParser( $parser ) { | |
| 60 | + $this->_parsers[] = $parser; | |
| 61 | + } | |
| 62 | + | |
| 63 | + // zero encoding - just removal of white space and comments | |
| 64 | + private function _basicCompression( $script ) { | |
| 65 | + $parser = new ParseMaster(); | |
| 66 | + // make safe | |
| 67 | + $parser->escapeChar = '\\'; | |
| 68 | + // protect strings | |
| 69 | + $parser->add( '/\'[^\'\\n\\r]*\'/', self::IGNORE ); | |
| 70 | + $parser->add( '/"[^"\\n\\r]*"/', self::IGNORE ); | |
| 71 | + // remove comments | |
| 72 | + $parser->add( '/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ' ); | |
| 73 | + $parser->add( '/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ' ); | |
| 74 | + // protect regular expressions | |
| 75 | + $parser->add( '/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2' ); // IGNORE | |
| 76 | + $parser->add( '/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE ); | |
| 77 | + // remove: ;;; doSomething(); | |
| 78 | + if ( $this->_specialChars ) { | |
| 79 | + $parser->add( '/;;;[^\\n\\r]+[\\n\\r]/' ); | |
| 80 | + } | |
| 81 | + // remove redundant semi-colons | |
| 82 | + $parser->add( '/\\(;;\\)/', self::IGNORE ); // protect for (;;) loops | |
| 83 | + $parser->add( '/;+\\s*([};])/', '$2' ); | |
| 84 | + // apply the above | |
| 85 | + $script = $parser->exec( $script ); | |
| 86 | + | |
| 87 | + // remove white-space | |
| 88 | + $parser->add( '/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3' ); | |
| 89 | + $parser->add( '/([+\\-])\\s+([+\\-])/', '$2 $3' ); | |
| 90 | + $parser->add( '/\\s+/', '' ); | |
| 91 | + | |
| 92 | + // done | |
| 93 | + return $parser->exec( $script ); | |
| 94 | + } | |
| 95 | + | |
| 96 | + private function _encodeSpecialChars( $script ) { | |
| 97 | + $parser = new ParseMaster(); | |
| 98 | + // replace: $name -> n, $$name -> na | |
| 99 | + $parser->add( | |
| 100 | + '/((\\x24+)([a-zA-Z$_]+))(\\d*)/', | |
| 101 | + array( 'fn' => '_replace_name' ) | |
| 102 | + ); | |
| 103 | + // replace: _name -> _0, double-underscore (__name) is ignored | |
| 104 | + $regexp = '/\\b_[A-Za-z\\d]\\w*/'; | |
| 105 | + // build the word list | |
| 106 | + $keywords = $this->_analyze( $script, $regexp, '_encodePrivate' ); | |
| 107 | + // quick ref | |
| 108 | + $encoded = $keywords['encoded']; | |
| 109 | + | |
| 110 | + $parser->add( | |
| 111 | + $regexp, | |
| 112 | + array( | |
| 113 | + 'fn' => '_replace_encoded', | |
| 114 | + 'data' => $encoded | |
| 115 | + ) | |
| 116 | + ); | |
| 117 | + | |
| 118 | + return $parser->exec( $script ); | |
| 119 | + } | |
| 120 | + | |
| 121 | + private function _encodeKeywords( $script ) { | |
| 122 | + // escape high-ascii values already in the script (i.e. in strings) | |
| 123 | + if ( $this->_encoding > 62 ) { | |
| 124 | + $script = $this->_escape95( $script ); | |
| 125 | + } | |
| 126 | + // create the parser | |
| 127 | + $parser = new ParseMaster(); | |
| 128 | + $encode = $this->_getEncoder( $this->_encoding ); | |
| 129 | + // for high-ascii, don't encode single character low-ascii | |
| 130 | + $regexp = ( $this->_encoding > 62 ) ? '/\\w\\w+/' : '/\\w+/'; | |
| 131 | + // build the word list | |
| 132 | + $keywords = $this->_analyze( $script, $regexp, $encode ); | |
| 133 | + $encoded = $keywords['encoded']; | |
| 134 | + | |
| 135 | + // encode | |
| 136 | + $parser->add( | |
| 137 | + $regexp, | |
| 138 | + array( | |
| 139 | + 'fn' => '_replace_encoded', | |
| 140 | + 'data' => $encoded | |
| 141 | + ) | |
| 142 | + ); | |
| 143 | + if ( empty( $script ) ) { | |
| 144 | + return $script; | |
| 145 | + } else { | |
| 146 | + //$res = $parser->exec($script); | |
| 147 | + //$res = $this->_bootStrap($res, $keywords); | |
| 148 | + //return $res; | |
| 149 | + return $this->_bootStrap( $parser->exec( $script ), $keywords ); | |
| 150 | + } | |
| 151 | + } | |
| 152 | + | |
| 153 | + private function _analyze( $script, $regexp, $encode ) { | |
| 154 | + // analyse | |
| 155 | + // retreive all words in the script | |
| 156 | + $all = array(); | |
| 157 | + preg_match_all( $regexp, $script, $all ); | |
| 158 | + $_sorted = array(); // list of words sorted by frequency | |
| 159 | + $_encoded = array(); // dictionary of word->encoding | |
| 160 | + $_protected = array(); // instances of "protected" words | |
| 161 | + $all = $all[0]; // simulate the javascript comportement of global match | |
| 162 | + if ( ! empty( $all ) ) { | |
| 163 | + $unsorted = array(); // same list, not sorted | |
| 164 | + $protected = array(); // "protected" words (dictionary of word->"word") | |
| 165 | + $value = array(); // dictionary of charCode->encoding (eg. 256->ff) | |
| 166 | + $this->_count = array(); // word->count | |
| 167 | + $i = count( $all ); | |
| 168 | + $j = 0; //$word = null; | |
| 169 | + // count the occurrences - used for sorting later | |
| 170 | + do { | |
| 171 | + -- $i; | |
| 172 | + $word = '$' . $all[ $i ]; | |
| 173 | + if ( ! isset( $this->_count[ $word ] ) ) { | |
| 174 | + $this->_count[ $word ] = 0; | |
| 175 | + $unsorted[ $j ] = $word; | |
| 176 | + // make a dictionary of all of the protected words in this script | |
| 177 | + // these are words that might be mistaken for encoding | |
| 178 | + //if (is_string($encode) && method_exists($this, $encode)) | |
| 179 | + $values[ $j ] = call_user_func( array( &$this, $encode ), $j ); | |
| 180 | + $protected[ '$' . $values[ $j ] ] = $j ++; | |
| 181 | + } | |
| 182 | + // increment the word counter | |
| 183 | + $this->_count[ $word ] ++; | |
| 184 | + } while ( $i > 0 ); | |
| 185 | + // prepare to sort the word list, first we must protect | |
| 186 | + // words that are also used as codes. we assign them a code | |
| 187 | + // equivalent to the word itself. | |
| 188 | + // e.g. if "do" falls within our encoding range | |
| 189 | + // then we store keywords["do"] = "do"; | |
| 190 | + // this avoids problems when decoding | |
| 191 | + $i = count( $unsorted ); | |
| 192 | + do { | |
| 193 | + $word = $unsorted[ -- $i ]; | |
| 194 | + if ( isset( $protected[ $word ] ) /*!= null*/ ) { | |
| 195 | + $_sorted[ $protected[ $word ] ] = substr( $word, 1 ); | |
| 196 | + $_protected[ $protected[ $word ] ] = true; | |
| 197 | + $this->_count[ $word ] = 0; | |
| 198 | + } | |
| 199 | + } while ( $i ); | |
| 200 | + | |
| 201 | + // sort the words by frequency | |
| 202 | + // Note: the javascript and php version of sort can be different : | |
| 203 | + // in php manual, usort : | |
| 204 | + // " If two members compare as equal, | |
| 205 | + // their order in the sorted array is undefined." | |
| 206 | + // so the final packed script is different of the Dean's javascript version | |
| 207 | + // but equivalent. | |
| 208 | + // the ECMAscript standard does not guarantee this behaviour, | |
| 209 | + // and thus not all browsers (e.g. Mozilla versions dating back to at | |
| 210 | + // least 2003) respect this. | |
| 211 | + usort( $unsorted, array( &$this, '_sortWords' ) ); | |
| 212 | + $j = 0; | |
| 213 | + // because there are "protected" words in the list | |
| 214 | + // we must add the sorted words around them | |
| 215 | + do { | |
| 216 | + if ( ! isset( $_sorted[ $i ] ) ) { | |
| 217 | + $_sorted[ $i ] = substr( $unsorted[ $j ++ ], 1 ); | |
| 218 | + } | |
| 219 | + $_encoded[ $_sorted[ $i ] ] = $values[ $i ]; | |
| 220 | + } while ( ++ $i < count( $unsorted ) ); | |
| 221 | + } | |
| 222 | + | |
| 223 | + return array( | |
| 224 | + 'sorted' => $_sorted, | |
| 225 | + 'encoded' => $_encoded, | |
| 226 | + 'protected' => $_protected | |
| 227 | + ); | |
| 228 | + } | |
| 229 | + | |
| 230 | + private $_count = array(); | |
| 231 | + | |
| 232 | + private function _sortWords( $match1, $match2 ) { | |
| 233 | + return $this->_count[ $match2 ] - $this->_count[ $match1 ]; | |
| 234 | + } | |
| 235 | + | |
| 236 | + // build the boot function used for loading and decoding | |
| 237 | + private function _bootStrap( $packed, $keywords ) { | |
| 238 | + $ENCODE = $this->_safeRegExp( '$encode\\($count\\)' ); | |
| 239 | + | |
| 240 | + // $packed: the packed script | |
| 241 | + $packed = "'" . $this->_escape( $packed ) . "'"; | |
| 242 | + | |
| 243 | + // $ascii: base for encoding | |
| 244 | + $ascii = min( count( $keywords['sorted'] ), $this->_encoding ); | |
| 245 | + if ( $ascii == 0 ) { | |
| 246 | + $ascii = 1; | |
| 247 | + } | |
| 248 | + | |
| 249 | + // $count: number of words contained in the script | |
| 250 | + $count = count( $keywords['sorted'] ); | |
| 251 | + | |
| 252 | + // $keywords: list of words contained in the script | |
| 253 | + foreach ( $keywords['protected'] as $i => $value ) { | |
| 254 | + $keywords['sorted'][ $i ] = ''; | |
| 255 | + } | |
| 256 | + // convert from a string to an array | |
| 257 | + ksort( $keywords['sorted'] ); | |
| 258 | + $keywords = "'" . implode( '|', $keywords['sorted'] ) . "'.split('|')"; | |
| 259 | + | |
| 260 | + $encode = ( $this->_encoding > 62 ) ? '_encode95' : $this->_getEncoder( $ascii ); | |
| 261 | + $encode = $this->_getJSFunction( $encode ); | |
| 262 | + $encode = preg_replace( '/_encoding/', '$ascii', $encode ); | |
| 263 | + $encode = preg_replace( '/arguments\\.callee/', '$encode', $encode ); | |
| 264 | + $inline = '\\$count' . ( $ascii > 10 ? '.toString(\\$ascii)' : '' ); | |
| 265 | + | |
| 266 | + // $decode: code snippet to speed up decoding | |
| 267 | + if ( $this->_fastDecode ) { | |
| 268 | + // create the decoder | |
| 269 | + $decode = $this->_getJSFunction( '_decodeBody' ); | |
| 270 | + if ( $this->_encoding > 62 ) { | |
| 271 | + $decode = preg_replace( '/\\\\w/', '[\\xa1-\\xff]', $decode ); | |
| 272 | + } // perform the encoding inline for lower ascii values | |
| 273 | + elseif ( $ascii < 36 ) { | |
| 274 | + $decode = preg_replace( $ENCODE, $inline, $decode ); | |
| 275 | + } | |
| 276 | + // special case: when $count==0 there are no keywords. I want to keep | |
| 277 | + // the basic shape of the unpacking funcion so i'll frig the code... | |
| 278 | + if ( $count == 0 ) { | |
| 279 | + $decode = preg_replace( $this->_safeRegExp( '($count)\\s*=\\s*1' ), '$1=0', $decode, 1 ); | |
| 280 | + } | |
| 281 | + } | |
| 282 | + | |
| 283 | + // boot function | |
| 284 | + $unpack = $this->_getJSFunction( '_unpack' ); | |
| 285 | + if ( $this->_fastDecode ) { | |
| 286 | + // insert the decoder | |
| 287 | + $this->buffer = $decode; | |
| 288 | + $unpack = preg_replace_callback( '/\\{/', array( &$this, '_insertFastDecode' ), $unpack, 1 ); | |
| 289 | + } | |
| 290 | + $unpack = preg_replace( '/"/', "'", $unpack ); | |
| 291 | + if ( $this->_encoding > 62 ) { // high-ascii | |
| 292 | + // get rid of the word-boundaries for regexp matches | |
| 293 | + $unpack = preg_replace( '/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack ); | |
| 294 | + } | |
| 295 | + if ( $ascii > 36 || $this->_encoding > 62 || $this->_fastDecode ) { | |
| 296 | + // insert the encode function | |
| 297 | + $this->buffer = $encode; | |
| 298 | + $unpack = preg_replace_callback( '/\\{/', array( &$this, '_insertFastEncode' ), $unpack, 1 ); | |
| 299 | + } else { | |
| 300 | + // perform the encoding inline | |
| 301 | + $unpack = preg_replace( $ENCODE, $inline, $unpack ); | |
| 302 | + } | |
| 303 | + // pack the boot function too | |
| 304 | + $unpackPacker = new Obfuscator( $unpack, 0, false, true ); | |
| 305 | + $unpack = $unpackPacker->pack(); | |
| 306 | + | |
| 307 | + // arguments | |
| 308 | + $params = array( $packed, $ascii, $count, $keywords ); | |
| 309 | + if ( $this->_fastDecode ) { | |
| 310 | + $params[] = 0; | |
| 311 | + $params[] = '{}'; | |
| 312 | + } | |
| 313 | + $params = implode( ',', $params ); | |
| 314 | + | |
| 315 | + // the whole thing | |
| 316 | + return 'eval(' . $unpack . '(' . $params . "))\n"; | |
| 317 | + } | |
| 318 | + | |
| 319 | + private $buffer; | |
| 320 | + | |
| 321 | + private function _insertFastDecode( $match ) { | |
| 322 | + return '{' . $this->buffer . ';'; | |
| 323 | + } | |
| 324 | + | |
| 325 | + private function _insertFastEncode( $match ) { | |
| 326 | + return '{$encode=' . $this->buffer . ';'; | |
| 327 | + } | |
| 328 | + | |
| 329 | + // mmm.. ..which one do i need ?? | |
| 330 | + private function _getEncoder( $ascii ) { | |
| 331 | + return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ? | |
| 332 | + '_encode95' : '_encode62' : '_encode36' : '_encode10'; | |
| 333 | + } | |
| 334 | + | |
| 335 | + // zero encoding | |
| 336 | + // characters: 0123456789 | |
| 337 | + private function _encode10( $charCode ) { | |
| 338 | + return $charCode; | |
| 339 | + } | |
| 340 | + | |
| 341 | + // inherent base36 support | |
| 342 | + // characters: 0123456789abcdefghijklmnopqrstuvwxyz | |
| 343 | + private function _encode36( $charCode ) { | |
| 344 | + return base_convert( $charCode, 10, 36 ); | |
| 345 | + } | |
| 346 | + | |
| 347 | + // hitch a ride on base36 and add the upper case alpha characters | |
| 348 | + // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | |
| 349 | + private function _encode62( $charCode ) { | |
| 350 | + $res = ''; | |
| 351 | + if ( $charCode >= $this->_encoding ) { | |
| 352 | + $res = $this->_encode62( (int) ( $charCode / $this->_encoding ) ); | |
| 353 | + } | |
| 354 | + $charCode = $charCode % $this->_encoding; | |
| 355 | + | |
| 356 | + if ( $charCode > 35 ) { | |
| 357 | + return $res . chr( $charCode + 29 ); | |
| 358 | + } else { | |
| 359 | + return $res . base_convert( $charCode, 10, 36 ); | |
| 360 | + } | |
| 361 | + } | |
| 362 | + | |
| 363 | + // use high-ascii values | |
| 364 | + // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ | |
| 365 | + private function _encode95( $charCode ) { | |
| 366 | + $res = ''; | |
| 367 | + if ( $charCode >= $this->_encoding ) { | |
| 368 | + $res = $this->_encode95( $charCode / $this->_encoding ); | |
| 369 | + } | |
| 370 | + | |
| 371 | + return $res . chr( ( $charCode % $this->_encoding ) + 161 ); | |
| 372 | + } | |
| 373 | + | |
| 374 | + private function _safeRegExp( $string ) { | |
| 375 | + return '/' . preg_replace( '/\$/', '\\\$', $string ) . '/'; | |
| 376 | + } | |
| 377 | + | |
| 378 | + private function _encodePrivate( $charCode ) { | |
| 379 | + return "_" . $charCode; | |
| 380 | + } | |
| 381 | + | |
| 382 | + // protect characters used by the parser | |
| 383 | + private function _escape( $script ) { | |
| 384 | + return preg_replace( '/([\\\\\'])/', '\\\$1', $script ); | |
| 385 | + } | |
| 386 | + | |
| 387 | + // protect high-ascii characters already in the script | |
| 388 | + private function _escape95( $script ) { | |
| 389 | + return preg_replace_callback( | |
| 390 | + '/[\\xa1-\\xff]/', | |
| 391 | + array( &$this, '_escape95Bis' ), | |
| 392 | + $script | |
| 393 | + ); | |
| 394 | + } | |
| 395 | + | |
| 396 | + private function _escape95Bis( $match ) { | |
| 397 | + return '\x' . ( (string) dechex( ord( $match ) ) ); | |
| 398 | + } | |
| 399 | + | |
| 400 | + | |
| 401 | + private function _getJSFunction( $aName ) { | |
| 402 | + if ( defined( 'self::JSFUNCTION' . $aName ) ) { | |
| 403 | + return constant( 'self::JSFUNCTION' . $aName ); | |
| 404 | + } else { | |
| 405 | + return ''; | |
| 406 | + } | |
| 407 | + } | |
| 408 | + | |
| 409 | + // JavaScript Functions used. | |
| 410 | + // Note : In Dean's version, these functions are converted | |
| 411 | + // with 'String(aFunctionName);'. | |
| 412 | + // This internal conversion complete the original code, ex : | |
| 413 | + // 'while (aBool) anAction();' is converted to | |
| 414 | + // 'while (aBool) { anAction(); }'. | |
| 415 | + // The JavaScript functions below are corrected. | |
| 416 | + | |
| 417 | + // unpacking function - this is the boot strap function | |
| 418 | + // data extracted from this packing routine is passed to | |
| 419 | + // this function when decoded in the target | |
| 420 | + // NOTE ! : without the ';' final. | |
| 421 | + const JSFUNCTION_unpack = | |
| 422 | + | |
| 423 | + 'function($packed, $ascii, $count, $keywords, $encode, $decode) { | |
| 424 | + while ($count--) { | |
| 425 | + if ($keywords[$count]) { | |
| 426 | + $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); | |
| 427 | + } | |
| 428 | + } | |
| 429 | + return $packed; | |
| 430 | + }'; | |
| 431 | + /* | |
| 432 | + 'function($packed, $ascii, $count, $keywords, $encode, $decode) { | |
| 433 | + while ($count--) | |
| 434 | + if ($keywords[$count]) | |
| 435 | + $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]); | |
| 436 | + return $packed; | |
| 437 | + }'; | |
| 438 | + */ | |
| 439 | + | |
| 440 | + // code-snippet inserted into the unpacker to speed up decoding | |
| 441 | + const JSFUNCTION_decodeBody = | |
| 442 | +//_decode = function() { | |
| 443 | +// does the browser support String.replace where the | |
| 444 | +// replacement value is a function? | |
| 445 | + | |
| 446 | + ' if (!\'\'.replace(/^/, String)) { | |
| 447 | + // decode all the values we need | |
| 448 | + while ($count--) { | |
| 449 | + $decode[$encode($count)] = $keywords[$count] || $encode($count); | |
| 450 | + } | |
| 451 | + // global replacement function | |
| 452 | + $keywords = [function ($encoded) {return $decode[$encoded]}]; | |
| 453 | + // generic match | |
| 454 | + $encode = function () {return \'\\\\w+\'}; | |
| 455 | + // reset the loop counter - we are now doing a global replace | |
| 456 | + $count = 1; | |
| 457 | + } | |
| 458 | + '; | |
| 459 | +//}; | |
| 460 | + /* | |
| 461 | + ' if (!\'\'.replace(/^/, String)) { | |
| 462 | + // decode all the values we need | |
| 463 | + while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count); | |
| 464 | + // global replacement function | |
| 465 | + $keywords = [function ($encoded) {return $decode[$encoded]}]; | |
| 466 | + // generic match | |
| 467 | + $encode = function () {return\'\\\\w+\'}; | |
| 468 | + // reset the loop counter - we are now doing a global replace | |
| 469 | + $count = 1; | |
| 470 | + }'; | |
| 471 | + */ | |
| 472 | + | |
| 473 | + // zero encoding | |
| 474 | + // characters: 0123456789 | |
| 475 | + const JSFUNCTION_encode10 = | |
| 476 | + 'function($charCode) { | |
| 477 | + return $charCode; | |
| 478 | + }';//;'; | |
| 479 | + | |
| 480 | + // inherent base36 support | |
| 481 | + // characters: 0123456789abcdefghijklmnopqrstuvwxyz | |
| 482 | + const JSFUNCTION_encode36 = | |
| 483 | + 'function($charCode) { | |
| 484 | + return $charCode.toString(36); | |
| 485 | + }';//;'; | |
| 486 | + | |
| 487 | + // hitch a ride on base36 and add the upper case alpha characters | |
| 488 | + // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ | |
| 489 | + const JSFUNCTION_encode62 = | |
| 490 | + 'function($charCode) { | |
| 491 | + return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) + | |
| 492 | + (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); | |
| 493 | + }'; | |
| 494 | + | |
| 495 | + // use high-ascii values | |
| 496 | + // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ | |
| 497 | + const JSFUNCTION_encode95 = | |
| 498 | + 'function($charCode) { | |
| 499 | + return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) + | |
| 500 | + String.fromCharCode($charCode % _encoding + 161); | |
| 501 | + }'; | |
| 502 | + | |
| 503 | +} | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |