239 lines
6.5 KiB
PHP
239 lines
6.5 KiB
PHP
<?php
|
|
/**
|
|
* Horde_ActiveSync_Rfc822::
|
|
*
|
|
* @license http://www.horde.org/licenses/gpl GPLv2
|
|
*
|
|
* @copyright 2010-2020 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
|
|
* @copyright 2010-2020 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. Headers from
|
|
* the original message are always used.
|
|
*
|
|
* @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)
|
|
);
|
|
$mime_stream = new Horde_Stream_Existing(array('stream' => $mime_stream));
|
|
|
|
// Since we are still using the headers sent from the device, we can
|
|
// simply zero out the position members etc...
|
|
$this->_hdr_pos = 0;
|
|
$this->_stream = $mime_stream;
|
|
$mime_stream->rewind();
|
|
}
|
|
|
|
/**
|
|
* Return the raw message data.
|
|
*
|
|
* @return stream resource
|
|
* @todo Rename to make it clear this returns a stream.
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|