Completed
Push — master ( c7cf7f...702f4a )
by Zaahid
02:37
created

UUEncodeStreamFilter::isEndLine()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 2
eloc 2
nc 2
nop 1
1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
namespace ZBateson\MailMimeParser;
8
9
use php_user_filter;
10
11
/**
12
 * Stream filter converts uuencoded text to its raw binary.
13
 *
14
 * @author Zaahid Bateson
15
 */
16
class UUEncodeStreamFilter extends php_user_filter
17
{
18
    /**
19
     * Name used when registering with stream_filter_register.
20
     */
21
    const STREAM_FILTER_NAME = 'mailmimeparser-uudecode';
22
    
23
    /**
24
     * @var string Leftovers from the last incomplete line that was parsed, to
25
     *      be prepended to the next line read.
26
     */
27
    private $leftover = '';
28
    
29
    /**
30
     * Returns an array of complete lines (including line endings) from the 
31
     * passed $bucket object.
32
     * 
33
     * If the last line on $bucket is incomplete, it's assigned to
34
     * $this->leftover and prepended to the first element of the first line in
35
     * the next call to getLines.
36
     * 
37
     * @param object $bucket
38
     * @return string[]
39
     */
40
    private function getLines($bucket)
41
    {
42
        $lines = preg_split(
43
            '/([^\r\n]+[\r\n]+)/',
44
            $bucket->data,
45
            -1,
46
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
47
        );
48
        if (!empty($this->leftover)) {
49
            $lines[0] = $this->leftover . $lines[0];
50
            $this->leftover = '';
51
        }
52
        $last = end($lines);
53
        if ($last[strlen($last) - 1] !== "\n") {
54
            $this->leftover = array_pop($lines);
55
        }
56
        return $lines;
57
    }
58
    
59
    /**
60
     * Returns true if the passed $line is empty or matches the beginning header
61
     * pattern for a uuencoded message.
62
     * 
63
     * @param string $line
64
     * @return bool
65
     */
66
    private function isEmptyOrStartLine($line)
67
    {
68
        return (empty($line) || preg_match('/^begin \d{3} .*$/', $line));
69
    }
70
    
71
    /**
72
     * Returns true if the passed $line is either a backtick character '`' or
73
     * the string 'end' signifying the end of the uuencoded message.
74
     * 
75
     * @param string $line
76
     * @return bool
77
     */
78
    private function isEndLine($line)
79
    {
80
        return ($line === '`' || $line === 'end');
81
    }
82
    
83
    /**
84
     * Filters a single line of encoded input.  Returns NULL if the end has been
85
     * reached.
86
     * 
87
     * @param string $line
88
     * @return string the decoded line
89
     */
90
    private function filterLine($line)
91
    {
92
        $cur = trim($line);
93
        if ($this->isEmptyOrStartLine($cur)) {
94
            return '';
95
        } elseif ($this->isEndLine($cur)) {
96
            return null;
97
        }
98
        return convert_uudecode($cur);
99
    }
100
    
101
    /**
102
     * Filters the lines in the passed $lines array, returning a concatenated
103
     * string of decoded lines.
104
     * 
105
     * @param array $lines
106
     * @param int $consumed
107
     * @return string
108
     */
109
    private function filterBucketLines(array $lines, &$consumed)
110
    {
111
        $data = '';
112
        foreach ($lines as $line) {
113
            $consumed += strlen($line);
114
            $filtered = $this->filterLine($line);
115
            if ($filtered === null) {
116
                break;
117
            }
118
            $data .= $filtered;
119
        }
120
        return $data;
121
    }
122
    
123
    /**
124
     * Filter implementation converts encoding before returning PSFS_PASS_ON.
125
     * 
126
     * @param resource $in
127
     * @param resource $out
128
     * @param int $consumed
129
     * @param bool $closing
130
     * @return int
131
     */
132
    public function filter($in, $out, &$consumed, $closing)
133
    {
134
        while ($bucket = stream_bucket_make_writeable($in)) {
135
            $lines = $this->getLines($bucket);
136
            $bucket->data = $this->filterBucketLines($lines, $consumed);
137
            stream_bucket_append($out, $bucket);
138
        }
139
        return PSFS_PASS_ON;
140
    }
141
}
142