. * * @author niel * @copyright 2016 nZEDb */ namespace App\Extensions\util; /** * Class Php. */ class PhpYenc { /** * @param $text * @param bool $ignore * @return bool|string */ public static function decode(&$text, $ignore = false): bool|string { $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, $trailerSize] = $encoded; } 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.'; 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.'; 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.'; 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. */ public static function decodeIgnore(string &$text): string { 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; } /** * @return bool */ public static function enabled(): bool { return true; } /** * @param $data * @param $filename * @param int $lineLength * @param bool $crc32 * @return string */ public static function encode($data, $filename, int $lineLength = 128, bool $crc32 = true): string { // 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.'; throw new \RuntimeException($message); } $encoded = ''; $stringLength = \strlen($data); // Encode each character of the string one at a time. foreach ($data as $i => $iValue) { $value = ((\ord($iValue) + 42) % 256); // Escape NULL, TAB, LF, CR, space, . and = characters. $encoded .= match ($value) { 0, 10, 13, 61 => ('='.\chr(($value + 64) % 256)), default => \chr($value), }; } $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; } }