Files
server/usr/share/psa-pear/pear/php/Horde/ActiveSync/Rfc822.php
2026-01-07 20:52:11 +01:00

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);
}
}
}