1<?php
2/**
3 * Copyright 2012-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file LICENSE for license information (LGPL). If you
6 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
7 *
8 * @category  Horde
9 * @copyright 2012-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
11 * @package   Imap_Client
12 */
13
14/**
15 * Object representation of an IMAP string (RFC 3501 [4.3]).
16 *
17 * @author    Michael Slusarz <slusarz@horde.org>
18 * @category  Horde
19 * @copyright 2012-2017 Horde LLC
20 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
21 * @package   Imap_Client
22 */
23class Horde_Imap_Client_Data_Format_String
24extends Horde_Imap_Client_Data_Format
25{
26    /**
27     * String filter parameters.
28     *
29     * @var string
30     */
31    protected $_filter;
32
33    /**
34     * @param array $opts  Additional options:
35     *   - eol: (boolean) If true, normalize EOLs in input. @since 2.2.0
36     *   - skipscan: (boolean) If true, don't scan input for
37     *               binary/literal/quoted data. @since 2.2.0
38     *
39     * @throws Horde_Imap_Client_Data_Format_Exception
40     */
41    public function __construct($data, array $opts = array())
42    {
43        /* String data is stored in a stream. */
44        $this->_data = new Horde_Stream_Temp();
45
46        if (empty($opts['skipscan'])) {
47            $this->_filter = $this->_filterParams();
48            stream_filter_register('horde_imap_client_string', 'Horde_Imap_Client_Data_Format_Filter_String');
49            $res = stream_filter_append($this->_data->stream, 'horde_imap_client_string', STREAM_FILTER_WRITE, $this->_filter);
50        } else {
51            $res = null;
52        }
53
54        if (empty($opts['eol'])) {
55            $res2 = null;
56        } else {
57            stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
58            $res2 = stream_filter_append($this->_data->stream, 'horde_eol', STREAM_FILTER_WRITE);
59        }
60
61        $this->_data->add($data);
62
63        if (!is_null($res)) {
64            stream_filter_remove($res);
65        }
66        if (!is_null($res2)) {
67            stream_filter_remove($res2);
68        }
69
70        if (isset($this->_filter) &&
71            $this->_filter->nonascii &&
72            !($this instanceof Horde_Imap_Client_Data_Format_String_Support_Nonascii)) {
73            throw new Horde_Imap_Client_Data_Format_Exception(
74                'String contains non-ASCII characters.'
75            );
76        }
77    }
78
79    /**
80     * Return the base string filter parameters.
81     *
82     * @return object  Filter parameters.
83     */
84    protected function _filterParams()
85    {
86        return new stdClass;
87    }
88
89    /**
90     */
91    public function __toString()
92    {
93        return $this->_data->getString(0);
94    }
95
96    /**
97     */
98    public function escape()
99    {
100        if ($this->literal()) {
101            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
102        }
103
104        return $this->quoted()
105            ? stream_get_contents($this->escapeStream())
106            : $this->_data->getString(0);
107    }
108
109    /**
110     * Return the escaped string as a stream.
111     *
112     * @return resource  The IMAP escaped stream.
113     */
114    public function escapeStream()
115    {
116        if ($this->literal()) {
117            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
118        }
119
120        rewind($this->_data->stream);
121
122        $stream = new Horde_Stream_Temp();
123        $stream->add($this->_data, true);
124
125        stream_filter_register('horde_imap_client_string_quote', 'Horde_Imap_Client_Data_Format_Filter_Quote');
126        stream_filter_append($stream->stream, 'horde_imap_client_string_quote', STREAM_FILTER_READ);
127
128        return $stream->stream;
129    }
130
131    /**
132     * Does this data item require quoted string output?
133     *
134     * @return boolean  True if quoted output is required.
135     */
136    public function quoted()
137    {
138        /* IMAP strings MUST be quoted if they are not a literal. */
139        return (!isset($this->_filter) || !$this->_filter->literal);
140    }
141
142    /**
143     * Force item to be output quoted.
144     */
145    public function forceQuoted()
146    {
147        $this->_filter = $this->_filterParams();
148        $this->_filter->binary = false;
149        $this->_filter->literal = false;
150        $this->_filter->quoted = true;
151    }
152
153    /**
154     * Does this data item require literal string output?
155     *
156     * @return boolean  True if literal output is required.
157     */
158    public function literal()
159    {
160        return (isset($this->_filter) && $this->_filter->literal);
161    }
162
163    /**
164     * Force item to be output as a literal.
165     */
166    public function forceLiteral()
167    {
168        $this->_filter = $this->_filterParams();
169        // Keep binary status, if set
170        $this->_filter->literal = true;
171        $this->_filter->quoted = false;
172    }
173
174    /**
175     * If literal output, is the data binary?
176     *
177     * @return boolean  True if the literal output is binary.
178     */
179    public function binary()
180    {
181        return (isset($this->_filter) && !empty($this->_filter->binary));
182    }
183
184    /**
185     * Force item to be output as a binary literal.
186     */
187    public function forceBinary()
188    {
189        $this->_filter = $this->_filterParams();
190        $this->_filter->binary = true;
191        $this->_filter->literal = true;
192        $this->_filter->quoted = false;
193    }
194
195    /**
196     * Return the length of the data.
197     *
198     * @since 2.2.0
199     *
200     * @return integer  Data length.
201     */
202    public function length()
203    {
204        return $this->_data->length();
205    }
206
207    /**
208     * Return the contents of the string as a stream object.
209     *
210     * @since 2.3.0
211     *
212     * @return Horde_Stream  The stream object.
213     */
214    public function getStream()
215    {
216        return $this->_data;
217    }
218
219}
220