1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Goetas\Mail\ToSwiftMailParser\Mime; |
4
|
|
|
|
5
|
|
|
class HeaderDecoder |
6
|
|
|
{ |
7
|
|
|
private $decodeWindows1252 = false; |
8
|
|
|
|
9
|
7 |
|
public function __construct(bool $decodeWindows1252 = false) |
10
|
|
|
{ |
11
|
7 |
|
$this->decodeWindows1252 = $decodeWindows1252; |
12
|
7 |
|
} |
13
|
|
|
|
14
|
7 |
|
public function decode(string $string): string |
15
|
|
|
{ |
16
|
|
|
/* Take out any spaces between multiple encoded words. */ |
17
|
7 |
|
$string = preg_replace('|\?=\s+=\?|', '?==?', $string); |
18
|
|
|
|
19
|
7 |
|
$out = ''; |
20
|
7 |
|
$old_pos = 0; |
21
|
|
|
|
22
|
7 |
|
while (($pos = strpos($string, '=?', $old_pos)) !== false) { |
23
|
|
|
/* Save any preceding text. */ |
24
|
4 |
|
$out .= substr($string, $old_pos, $pos - $old_pos); |
25
|
|
|
|
26
|
|
|
/* Search for first delimiting question mark (charset). */ |
27
|
4 |
|
if (($d1 = strpos($string, '?', $pos + 2)) === false) { |
28
|
|
|
break; |
29
|
|
|
} |
30
|
|
|
|
31
|
4 |
|
$orig_charset = substr($string, $pos + 2, $d1 - $pos - 2); |
32
|
4 |
|
if ($this->decodeWindows1252 && mb_strtolower($orig_charset) == 'iso-8859-1') { |
33
|
|
|
$orig_charset = 'windows-1252'; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/* Search for second delimiting question mark (encoding). */ |
37
|
4 |
|
if (($d2 = strpos($string, '?', $d1 + 1)) === false) { |
38
|
|
|
break; |
39
|
|
|
} |
40
|
|
|
|
41
|
4 |
|
$encoding = substr($string, $d1 + 1, $d2 - $d1 - 1); |
42
|
|
|
|
43
|
|
|
/* Search for end of encoded data. */ |
44
|
4 |
|
if (($end = strpos($string, '?=', $d2 + 1)) === false) { |
45
|
|
|
break; |
46
|
|
|
} |
47
|
|
|
|
48
|
4 |
|
$encoded_text = substr($string, $d2 + 1, $end - $d2 - 1); |
49
|
|
|
|
50
|
|
|
switch ($encoding) { |
51
|
4 |
|
case 'Q': |
52
|
3 |
|
case 'q': |
53
|
1 |
|
$out .= self::convertCharset(preg_replace_callback('/=([0-9a-f]{2})/i', function ($ord) { |
54
|
1 |
|
return chr(hexdec($ord [1])); |
55
|
1 |
|
}, str_replace('_', ' ', $encoded_text)), $orig_charset, mb_internal_encoding()); |
56
|
1 |
|
break; |
57
|
|
|
|
58
|
3 |
|
case 'B': |
59
|
|
|
case 'b': |
60
|
3 |
|
$out .= self::convertCharset(base64_decode($encoded_text), $orig_charset, mb_internal_encoding()); |
61
|
3 |
|
break; |
62
|
|
|
|
63
|
|
|
default: |
64
|
|
|
// Ignore unknown encoding. |
65
|
|
|
break; |
66
|
|
|
} |
67
|
|
|
|
68
|
4 |
|
$old_pos = $end + 2; |
69
|
|
|
} |
70
|
|
|
|
71
|
7 |
|
return $out . substr($string, $old_pos); |
72
|
|
|
} |
73
|
|
|
|
74
|
4 |
|
private static function convertCharset(string $str, string $orig, string $to) |
75
|
|
|
{ |
76
|
4 |
|
return mb_convert_encoding($str, $to, $orig); |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
|