-
Notifications
You must be signed in to change notification settings - Fork 127
/
Rfc822.php
245 lines (226 loc) · 7 KB
/
Rfc822.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<?php
/**
* Horde_ActiveSync_Rfc822::
*
* @license http://www.horde.org/licenses/gpl GPLv2
* NOTE: According to sec. 8 of the GENERAL PUBLIC LICENSE (GPL),
* Version 2, the distribution of the Horde_ActiveSync module in or
* to the United States of America is excluded from the scope of this
* license.
* @copyright 2010-2015 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
/**
* Horde_ActiveSync_Rfc822:: class provides functionality related to dealing
* with raw RFC822 message strings within an ActiveSync context.
*
* @license http://www.horde.org/licenses/gpl GPLv2
* NOTE: According to sec. 8 of the GENERAL PUBLIC LICENSE (GPL),
* Version 2, the distribution of the Horde_ActiveSync module in or
* to the United States of America is excluded from the scope of this
* license.
* @copyright 2010-2015 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
class Horde_ActiveSync_Rfc822
{
/**
* The memory limit for use with the PHP temp stream.
*
* @var integer
*/
public static $memoryLimit = 2097152;
/**
* Position of end of headers.
*
* @var integer
*/
protected $_hdr_pos;
/**
* The size of the EOL sequence.
*
* @var integer
*/
protected $_eol;
/**
* The raw message data in a stream.
*
* @var Horde_Stream
*/
protected $_stream;
/**
* The header text.
*
* @var string
*/
protected $_header_text;
/**
* Constructor.
*
* @param mixed $rfc822 The incoming message. Either a string
* or a stream resource.
* @param boolean $auto_add_headers Automatically add the standard
* Message-ID and User-Agent headers?
* @since 2.14.0
*/
public function __construct($rfc822, $auto_add_headers = true)
{
if (is_resource($rfc822)) {
$stream = new Horde_Stream_Existing(array('stream' => $rfc822));
$stream->rewind();
} else {
$stream = new Horde_Stream_Temp(array('max_memory' => self::$memoryLimit));
$stream->add($rfc822, true);
}
$this->_parseStream($stream);
if ($auto_add_headers) {
$this->addStandardHeaders();
}
}
/**
* Parse a Horde_Stream object to get the header and eol data.
*
* @param Horde_Stream $stream The stream object.
*/
protected function _parseStream(Horde_Stream $stream)
{
$this->_stream = $stream;
list($this->_hdr_pos, $this->_eol) = $this->_findHeader();
}
/**
* Returns the raw message with the message headers stripped.
*
* @return Horde_Stream
*/
public function getMessage()
{
// Position to after the headers.
fseek($this->_stream->stream, $this->_hdr_pos + $this->_eol);
$new_stream = new Horde_Stream_Temp(array('max_memory' => self::$memoryLimit));
$new_stream->add($this->_stream, true);
return $new_stream;
}
/**
* Replace the MIME part of the message sent from the client.
*
* @param Horde_Mime_Part $part The new MIME part.
* @since 2.19.0
*/
public function replaceMime(Horde_Mime_Part $part)
{
$mime_stream = $part->toString(array(
'stream' => true,
'headers' => false)
);
if (!empty($this->_header_text)) {
$hdr = $this->_header_text;
} else {
$this->_stream->rewind();
$hdr = $this->_stream->substring(0, $this->_hdr_pos);
}
$new_stream = Horde_Stream_Wrapper_Combine::getStream(array($hdr, $mime_stream));
$this->_parseStream(new Horde_Stream_Existing(array('stream' => $new_stream)));
}
/**
* Return the raw message data.
*
* @return stream resource
*/
public function getString()
{
if (!empty($this->_header_text)) {
return Horde_Stream_Wrapper_Combine::getStream(array($this->_header_text, $this->getMessage()->stream));
} else {
$this->_stream->rewind();
return $this->_stream->stream;
}
}
/**
* Return the message headers.
*
* @return Horde_Mime_Headers The header object.
*/
public function getHeaders()
{
if (!empty($this->_header_text)) {
$hdr_text = $this->_header_text;
} else {
$this->_stream->rewind();
$hdr_text = $this->_stream->substring(0, $this->_hdr_pos);
}
return Horde_Mime_Headers::parseHeaders($hdr_text);
}
/**
* Check for and add standard headers if needed.
*
* @since 2.14.0
*/
public function addStandardHeaders()
{
$headers = $this->getHeaders();
$updated = false;
// Check for required headers.
if (!$headers->getValue('Message-ID')) {
$headers->addMessageIdHeader();
$updated = true;
}
if (!$headers->getValue('User-Agent')) {
$headers->addUserAgentHeader();
$updated = true;
}
if (!$headers->getValue('Date')) {
$d = new Horde_Date();
$headers->addHeaderOb(Horde_Mime_Headers_Date::create());
$updated = true;
}
if ($updated) {
$this->_header_text = $headers->toString(array('charset' => 'UTF-8'));
}
}
/**
* Return a Mime object representing the entire message.
*
* @return Horde_Mime_Part The Mime object.
*/
public function getMimeObject()
{
$this->_stream->rewind();
$part = Horde_Mime_Part::parseMessage($this->_stream->getString());
$part->isBasePart(true);
return $part;
}
/**
* Return the length of the message data.
*
* @return integer
*/
public function getBytes()
{
if (!isset($this->_bytes)) {
$this->_bytes = $this->_stream->length();
}
return $this->_bytes;
}
/**
* Find the location of the end of the header text.
*
* @return array 1st element: Header position, 2nd element: Length of
* trailing EOL.
*/
protected function _findHeader()
{
// Look for the EOL that is found first in the message. Some clients
// are sending mixed EOL in S/MIME signed messages. This still doesn't
// fix "Nine" currently, as they first send \r\n, but use \n\n to
// separate the headers.
// See: https://ninefolders.plan.io/track/10606/1dcfed
switch ($this->_stream->getEOL()) {
case "\n":
return array($this->_stream->search("\n\n"), 2);
case "\r\n":
return array($this->_stream->search("\r\n\r\n"), 4);
}
}
}