Passed
Push — master ( 0980e4...0c25e1 )
by Darko
16:40
created

PhpYenc::encode()   A

Complexity

Conditions 5
Paths 10

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
dl 0
loc 44
rs 9.1928
c 1
b 0
f 0
cc 5
nc 10
nop 4
1
<?php
2
/**
3
 * This program is free software: you can redistribute it and/or modify
4
 * it under the terms of the GNU General Public License as published by
5
 * the Free Software Foundation, either version 3 of the License, or
6
 * (at your option) any later version.
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 * You should have received a copy of the GNU General Public License
12
 * along with this program (see LICENSE.txt in the base directory.  If
13
 * not, see:.
14
 *
15
 * @link      <http://www.gnu.org/licenses/>.
16
 *
17
 * @author    niel
18
 * @copyright 2016 nZEDb
19
 */
20
21
namespace App\Extensions\util;
22
23
/**
24
 * Class Php.
25
 */
26
class PhpYenc
27
{
28
    public static function decode(&$text, bool $ignore = false): bool|string
0 ignored issues
show
Unused Code introduced by
The parameter $ignore is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

28
    public static function decode(&$text, /** @scrutinizer ignore-unused */ bool $ignore = false): bool|string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
29
    {
30
        $crc = '';
31
        // Extract the yEnc string itself.
32
        if (preg_match(
33
            '/=ybegin.*size=([^ $]+).*\\r\\n(.*)\\r\\n=yend.*size=([^ $\\r\\n]+)(.*)/ims',
34
            $text,
35
            $encoded
36
        )) {
37
            if (preg_match('/crc32=([^ $\\r\\n]+)/ims', $encoded[4], $trailer)) {
38
                $crc = trim($trailer[1]);
39
            }
40
41
            [$headerSize, $encoded, $trailerSize] = $encoded;
42
        } else {
43
            return false;
44
        }
45
46
        // Remove line breaks from the string.
47
        $encoded = trim(str_replace("\r\n", '', $encoded));
48
49
        // Make sure the header and trailer file sizes match up.
50
        if ($headerSize !== $trailerSize) {
51
            $message = 'Header and trailer file sizes do not match. This is a violation of the yEnc specification.';
52
            throw new \RuntimeException($message);
53
        }
54
55
        // Decode.
56
        $decoded = '';
57
        $encodedLength = \strlen($encoded);
58
        for ($chr = 0; $chr < $encodedLength; $chr++) {
59
            $decoded .= (
60
                $encoded[$chr] === '=' ?
61
                    \chr((\ord($encoded[$chr]) - 42) % 256) :
62
                    \chr((((\ord($encoded[++$chr]) - 64) % 256) - 42) % 256)
63
            );
64
        }
65
66
        // Make sure the decoded file size is the same as the size specified in the header.
67
        if (\strlen($decoded) !== $headerSize) {
68
            $message = 'Header file size ('.$headerSize.') and actual file size ('.\strlen($decoded).') do not match. The file is probably corrupt.';
69
70
            throw new \RuntimeException($message);
71
        }
72
73
        // Check the CRC value
74
        if ($crc !== '' && (strtolower($crc) !== strtolower(sprintf('%04X', crc32($decoded))))) {
75
            $message = 'CRC32 checksums do not match. The file is probably corrupt.';
76
77
            throw new \RuntimeException($message);
78
        }
79
80
        return $decoded;
81
    }
82
83
    /**
84
     * Decode a string of text encoded with yEnc. Ignores all errors.
85
     *
86
     * @param  string  $text  The encoded text to decode.
87
     * @return string The decoded yEnc string, or the input string, if it's not yEnc.
88
     */
89
    public static function decodeIgnore(string &$text): string
90
    {
91
        if (preg_match('/^(=yBegin.*=yEnd[^$]*)$/ims', $text, $input)) {
92
            $text = '';
93
            $input =
94
                trim(
95
                    preg_replace(
96
                        '/\r\n/im',
97
                        '',
98
                        preg_replace(
99
                            '/(^=yEnd.*)/im',
100
                            '',
101
                            preg_replace(
102
                                '/(^=yPart.*\\r\\n)/im',
103
                                '',
104
                                preg_replace('/(^=yBegin.*\\r\\n)/im', '', $input[1], 1),
105
                                1
106
                            ),
107
                            1
108
                        )
109
                    )
110
                );
111
112
            $length = \strlen($input);
113
            for ($chr = 0; $chr < $length; $chr++) {
114
                $text .= (
115
                    $input[$chr] === '=' ?
116
                        \chr((((\ord($input[++$chr]) - 64) % 256) - 42) % 256) :
117
                        \chr((\ord($input[$chr]) - 42) % 256)
118
                );
119
            }
120
        }
121
122
        return $text;
123
    }
124
125
    public static function enabled(): bool
126
    {
127
        return true;
128
    }
129
130
    public static function encode($data, $filename, int $lineLength = 128, bool $crc32 = true): string
131
    {
132
        // yEnc 1.3 draft doesn't allow line lengths of more than 254 bytes.
133
        if ($lineLength > 254) {
134
            $lineLength = 254;
135
        }
136
137
        if ($lineLength < 1) {
138
            $message = $lineLength.' is not a valid line length.';
139
140
            throw new \RuntimeException($message);
141
        }
142
143
        $encoded = '';
144
        $stringLength = \strlen($data);
145
        // Encode each character of the string one at a time.
146
        foreach ($data as $i => $iValue) {
147
            $value = ((\ord($iValue) + 42) % 256);
148
149
            // Escape NULL, TAB, LF, CR, space, . and = characters.
150
            $encoded .= match ($value) {
151
                0, 10, 13, 61 => ('='.\chr(($value + 64) % 256)),
152
                default => \chr($value),
153
            };
154
        }
155
156
        $encoded =
157
            '=ybegin line='.
158
            $lineLength.
159
            ' size='.
160
            $stringLength.
161
            ' name='.
162
            trim($filename).
163
            "\r\n".
164
            trim(chunk_split($encoded, $lineLength)).
165
            "\r\n=yend size=".
166
            $stringLength;
167
168
        // Add a CRC32 checksum if desired.
169
        if ($crc32 === true) {
170
            $encoded .= ' crc32='.strtolower(sprintf('%X', crc32($data)));
171
        }
172
173
        return $encoded;
174
    }
175
}
176