diff --git a/app/Extensions/util/Yenc.php b/app/Extensions/util/Yenc.php index 41fdda85f..fb0820b07 100644 --- a/app/Extensions/util/Yenc.php +++ b/app/Extensions/util/Yenc.php @@ -19,41 +19,59 @@ namespace App\Extensions\util; -use Illuminate\Support\ServiceProvider; -use App\Extensions\util\yenc\adapter\NzedbYenc; +use App\Providers\YencServiceProvider; -class Yenc extends ServiceProvider +class Yenc extends YencServiceProvider { + /** - * @param $text yEncoded text to decode back to an 8 bit form. + * @param $text yEncoded text to decode back to an 8 bit form. * - * @return string 8 bit decoded version of $text. + * @param array $options + * + * @return string 8 bit decoded version of $text. */ - public static function decode(&$text) + public static function decode(&$text, array $options = []) { - return NzedbYenc::decode($text); + $options += [ + 'name' => 'default', + 'file' => true, + ]; + return static::config($options['name'])->decode($text); } /** - * @param $text + * @param $text + * + * @param array $options * * @return mixed */ - public static function decodeIgnore(&$text) + public static function decodeIgnore(&$text, array $options = []) { - return NzedbYenc::decodeIgnore($text); + $options += [ + 'name' => 'default', + 'file' => true, + ]; + + return static::config($options['name'])->decodeIgnore($text); } /** - * @param $data 8 bit data to convert to yEncoded text. - * @param string $filename Name of file to recreate as. - * @param int $line Maximum number of characters in each line. + * @param $data 8 bit data to convert to yEncoded text. + * @param string $filename Name of file to recreate as. + * @param int $line Maximum number of characters in each line. * to use. * - * @return string|\Exception The yEncoded version of $data. + * @param bool $crc32 + * @param array $options + * + * @return \Exception|string The yEncoded version of $data. */ - public static function encode(&$data, $filename, $line = 128) + public static function encode(&$data, $filename, $line = 128, $crc32 = true, array $options = []) { - return NzedbYenc::encode($data, $filename, $line); + $options += ['name' => 'default']; + + return static::config($options['name'])->encode($data, $filename, $line, $crc32); } } diff --git a/app/Extensions/util/yenc/adapter/NzedbYenc.php b/app/Extensions/util/yenc/adapter/NzedbYenc.php index 153b075d0..471f3679e 100644 --- a/app/Extensions/util/yenc/adapter/NzedbYenc.php +++ b/app/Extensions/util/yenc/adapter/NzedbYenc.php @@ -19,11 +19,10 @@ namespace App\Extensions\util\yenc\adapter; -use Illuminate\Support\ServiceProvider; use yenc\yenc; -class NzedbYenc extends ServiceProvider +class NzedbYenc extends \App\Extensions\util\Yenc { public static function decode(&$text, $ignore = false, array $options = []) { diff --git a/app/Extensions/util/yenc/adapter/Php.php b/app/Extensions/util/yenc/adapter/Php.php new file mode 100644 index 000000000..c0c187fc2 --- /dev/null +++ b/app/Extensions/util/yenc/adapter/Php.php @@ -0,0 +1,202 @@ +. + * @author niel + * @copyright 2016 nZEDb + */ + +namespace app\extensions\util\yenc\adapter; + +use App\Extensions\util\Yenc; +use nntmux\Logger; + + +/** + * Class Php + * + * @package app\extensions\util\yenc\adapter + */ +class Php extends Yenc +{ + public static function decode(&$text, $ignore = false) + { + $crc = ''; + // Extract the yEnc string itself. + if (preg_match("/=ybegin.*size=([^ $]+).*\\r\\n(.*)\\r\\n=yend.*size=([^ $\\r\\n]+)(.*)/ims", + $text, + $encoded)) { + if (preg_match('/crc32=([^ $\\r\\n]+)/ims', $encoded[4], $trailer)) { + $crc = trim($trailer[1]); + } + + $headerSize = $encoded[1]; + $trailerSize = $encoded[3]; + $encoded = $encoded[2]; + } else { + return false; + } + + // Remove line breaks from the string. + $encoded = trim(str_replace("\r\n", '', $encoded)); + + // Make sure the header and trailer file sizes match up. + if ($headerSize != $trailerSize) { + $message = 'Header and trailer file sizes do not match. This is a violation of the yEnc specification.'; + if (NN_LOGGING || NN_DEBUG) { + (new Logger())->log(__CLASS__, __FUNCTION__, $message, Logger::LOG_NOTICE); + } + + throw new \RuntimeException($message); + } + + // Decode. + $decoded = ''; + $encodedLength = strlen($encoded); + for ($chr = 0; $chr < $encodedLength; $chr++) { + $decoded .= ( + $encoded[$chr] == '=' ? + chr((ord($encoded[$chr]) - 42) % 256) : + chr((((ord($encoded[++$chr]) - 64) % 256) - 42) % 256) + ); + } + + // Make sure the decoded file size is the same as the size specified in the header. + if (strlen($decoded) != $headerSize) { + $message = 'Header file size (' . $headerSize . ') and actual file size (' . strlen($decoded) . ') do not match. The file is probably corrupt.'; + if (NN_LOGGING || NN_DEBUG) { + (new Logger())->log(__CLASS__, __FUNCTION__, $message, Logger::LOG_NOTICE); + } + + throw new \RuntimeException($message); + } + + // Check the CRC value + if ($crc !== '' && (strtolower($crc) !== strtolower(sprintf("%04X", crc32($decoded))))) { + $message = 'CRC32 checksums do not match. The file is probably corrupt.'; + if (NN_LOGGING || NN_DEBUG) { + (new Logger())->log(__CLASS__, __FUNCTION__, $message, Logger::LOG_NOTICE); + } + + throw new \RuntimeException($message); + } + + return $decoded; + } + + /** + * Decode a string of text encoded with yEnc. Ignores all errors. + * + * @param string $text The encoded text to decode. + * + * @return string The decoded yEnc string, or the input string, if it's not yEnc. + * @access protected + */ + public static function decodeIgnore(&$text) + { + if (preg_match('/^(=yBegin.*=yEnd[^$]*)$/ims', $text, $input)) { + $text = ''; + $input = + trim( + preg_replace( + '/\r\n/im', + '', + preg_replace( + '/(^=yEnd.*)/im', + '', + preg_replace( + '/(^=yPart.*\\r\\n)/im', + '', + preg_replace('/(^=yBegin.*\\r\\n)/im', '', $input[1], 1), + 1), + 1) + ) + ); + + $length = strlen($input); + for ($chr = 0; $chr < $length; $chr++) { + $text .= ( + $input[$chr] == '=' ? + chr((((ord($input[++$chr]) - 64) % 256) - 42) % 256) : + chr((ord($input[$chr]) - 42) % 256) + ); + } + } + + return $text; + } + + public static function enabled() + { + return true; + } + + public static function encode($data, $filename, $lineLength = 128, $crc32 = true) + { + // yEnc 1.3 draft doesn't allow line lengths of more than 254 bytes. + if ($lineLength > 254) { + $lineLength = 254; + } + + if ($lineLength < 1) { + $message = $lineLength . ' is not a valid line length.'; + if (NN_LOGGING || NN_DEBUG) { + (new Logger())->log(__CLASS__, __FUNCTION__, $message, Logger::LOG_NOTICE); + } + + throw new \RuntimeException($message); + } + + $encoded = ''; + $stringLength = strlen($data); + // Encode each character of the string one at a time. + for ($i = 0; $i < $stringLength; $i++) { + $value = ((ord($data[$i]) + 42) % 256); + + // Escape NULL, TAB, LF, CR, space, . and = characters. + switch ($value) { + case 0: + case 10: + case 13: + case 61: + $encoded .= ('=' . chr(($value + 64) % 256)); + break; + default: + $encoded .= chr($value); + break; + } + } + + $encoded = + '=ybegin line=' . + $lineLength . + ' size=' . + $stringLength . + ' name=' . + trim($filename) . + "\r\n" . + trim(chunk_split($encoded, $lineLength)) . + "\r\n=yend size=" . + $stringLength; + + // Add a CRC32 checksum if desired. + if ($crc32 === true) { + $encoded .= ' crc32=' . strtolower(sprintf("%X", crc32($data))); + } + + return $encoded; + } +} + +?> diff --git a/app/Providers/YencServiceProvider.php b/app/Providers/YencServiceProvider.php new file mode 100644 index 000000000..179cdf785 --- /dev/null +++ b/app/Providers/YencServiceProvider.php @@ -0,0 +1,55 @@ + + [ + 'default' => [ + 'adapter' => '' + ], + + 'nzedb' => [ + 'adapter' => 'NzedbYenc' + ], + + 'php' => [ + 'adapter' => 'Php' + ], + ] + ] + ]; + + $options += $defaults; + + return $options['name']; + } +} diff --git a/bootstrap/yenc.php b/bootstrap/yenc.php new file mode 100644 index 000000000..6e5181ada --- /dev/null +++ b/bootstrap/yenc.php @@ -0,0 +1,60 @@ +. + * @author niel + * @copyright 2016 nZEDb + */ + +use app\models\Settings; + + +if (defined('NN_INSTALLER') && NN_INSTALLER !== false) { + $adapter = 'Php'; +} else { + switch (true) { + case extension_loaded('yenc'): + if (method_exists('yenc\yEnc', 'version') && + version_compare( + yenc\yEnc::version(), + '1.3.0', + '>=' + ) + ) { + $adapter = 'NzedbYenc'; + break; + } else { + trigger_error('Your version of the php-yenc extension is out of date and will be + ignored. Please update it to use the extension.', E_USER_WARNING); + } + default: + $adapter = 'Php'; + } +} + +\App\Extensions\util\Yenc::config([ + 'default' => [ + 'adapter' => $adapter + ], + + 'nzedb' => [ + 'adapter' => 'NzedbYenc' + ], + + 'php' => [ + 'adapter' => 'Php' + ], +]); + +?> diff --git a/config/app.php b/config/app.php index 6df7cb473..46ccffb22 100644 --- a/config/app.php +++ b/config/app.php @@ -176,6 +176,7 @@ return [ // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, + App\Providers\YencServiceProvider::class, ],