521 lines
14 KiB
PHP
521 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_Mail
|
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
* @version $Id: Part.php 18951 2009-11-12 16:26:19Z alexander $
|
|
*/
|
|
|
|
|
|
/**
|
|
* @see Zend_Mime_Decode
|
|
*/
|
|
require_once 'Zend/Mime/Decode.php';
|
|
|
|
/**
|
|
* @see Zend_Mail_Part_Interface
|
|
*/
|
|
require_once 'Zend/Mail/Part/Interface.php';
|
|
|
|
|
|
/**
|
|
* @category Zend
|
|
* @package Zend_Mail
|
|
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_Mail_Part implements RecursiveIterator, Zend_Mail_Part_Interface
|
|
{
|
|
/**
|
|
* headers of part as array
|
|
* @var null|array
|
|
*/
|
|
protected $_headers;
|
|
|
|
/**
|
|
* raw part body
|
|
* @var null|string
|
|
*/
|
|
protected $_content;
|
|
|
|
/**
|
|
* toplines as fetched with headers
|
|
* @var string
|
|
*/
|
|
protected $_topLines = '';
|
|
|
|
/**
|
|
* parts of multipart message
|
|
* @var array
|
|
*/
|
|
protected $_parts = array();
|
|
|
|
/**
|
|
* count of parts of a multipart message
|
|
* @var null|int
|
|
*/
|
|
protected $_countParts;
|
|
|
|
/**
|
|
* current position of iterator
|
|
* @var int
|
|
*/
|
|
protected $_iterationPos = 1;
|
|
|
|
/**
|
|
* mail handler, if late fetch is active
|
|
* @var null|Zend_Mail_Storage_Abstract
|
|
*/
|
|
protected $_mail;
|
|
|
|
/**
|
|
* message number for mail handler
|
|
* @var int
|
|
*/
|
|
protected $_messageNum = 0;
|
|
|
|
/**
|
|
* Public constructor
|
|
*
|
|
* Zend_Mail_Part supports different sources for content. The possible params are:
|
|
* - handler a instance of Zend_Mail_Storage_Abstract for late fetch
|
|
* - id number of message for handler
|
|
* - raw raw content with header and body as string
|
|
* - headers headers as array (name => value) or string, if a content part is found it's used as toplines
|
|
* - noToplines ignore content found after headers in param 'headers'
|
|
* - content content as string
|
|
*
|
|
* @param array $params full message with or without headers
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
public function __construct(array $params)
|
|
{
|
|
if (isset($params['handler'])) {
|
|
if (!$params['handler'] instanceof Zend_Mail_Storage_Abstract) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('handler is not a valid mail handler');
|
|
}
|
|
if (!isset($params['id'])) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('need a message id with a handler');
|
|
}
|
|
|
|
$this->_mail = $params['handler'];
|
|
$this->_messageNum = $params['id'];
|
|
}
|
|
|
|
if (isset($params['raw'])) {
|
|
Zend_Mime_Decode::splitMessage($params['raw'], $this->_headers, $this->_content);
|
|
} else if (isset($params['headers'])) {
|
|
if (is_array($params['headers'])) {
|
|
$this->_headers = $params['headers'];
|
|
} else {
|
|
if (!empty($params['noToplines'])) {
|
|
Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $null);
|
|
} else {
|
|
Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $this->_topLines);
|
|
}
|
|
}
|
|
if (isset($params['content'])) {
|
|
$this->_content = $params['content'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if part is a multipart message
|
|
*
|
|
* @return bool if part is multipart
|
|
*/
|
|
public function isMultipart()
|
|
{
|
|
try {
|
|
return stripos($this->contentType, 'multipart/') === 0;
|
|
} catch(Zend_Mail_Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Body of part
|
|
*
|
|
* If part is multipart the raw content of this part with all sub parts is returned
|
|
*
|
|
* @return string body
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
public function getContent()
|
|
{
|
|
if ($this->_content !== null) {
|
|
return $this->_content;
|
|
}
|
|
|
|
if ($this->_mail) {
|
|
return $this->_mail->getRawContent($this->_messageNum);
|
|
} else {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('no content');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return size of part
|
|
*
|
|
* Quite simple implemented currently (not decoding). Handle with care.
|
|
*
|
|
* @return int size
|
|
*/
|
|
public function getSize() {
|
|
return strlen($this->getContent());
|
|
}
|
|
|
|
|
|
/**
|
|
* Cache content and split in parts if multipart
|
|
*
|
|
* @return null
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
protected function _cacheContent()
|
|
{
|
|
// caching content if we can't fetch parts
|
|
if ($this->_content === null && $this->_mail) {
|
|
$this->_content = $this->_mail->getRawContent($this->_messageNum);
|
|
}
|
|
|
|
if (!$this->isMultipart()) {
|
|
return;
|
|
}
|
|
|
|
// split content in parts
|
|
$boundary = $this->getHeaderField('content-type', 'boundary');
|
|
if (!$boundary) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('no boundary found in content type to split message');
|
|
}
|
|
$parts = Zend_Mime_Decode::splitMessageStruct($this->_content, $boundary);
|
|
if ($parts === null) {
|
|
return;
|
|
}
|
|
$counter = 1;
|
|
foreach ($parts as $part) {
|
|
$this->_parts[$counter++] = new self(array('headers' => $part['header'], 'content' => $part['body']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get part of multipart message
|
|
*
|
|
* @param int $num number of part starting with 1 for first part
|
|
* @return Zend_Mail_Part wanted part
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
public function getPart($num)
|
|
{
|
|
if (isset($this->_parts[$num])) {
|
|
return $this->_parts[$num];
|
|
}
|
|
|
|
if (!$this->_mail && $this->_content === null) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('part not found');
|
|
}
|
|
|
|
if ($this->_mail && $this->_mail->hasFetchPart) {
|
|
// TODO: fetch part
|
|
// return
|
|
}
|
|
|
|
$this->_cacheContent();
|
|
|
|
if (!isset($this->_parts[$num])) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception('part not found');
|
|
}
|
|
|
|
return $this->_parts[$num];
|
|
}
|
|
|
|
/**
|
|
* Count parts of a multipart part
|
|
*
|
|
* @return int number of sub-parts
|
|
*/
|
|
public function countParts()
|
|
{
|
|
if ($this->_countParts) {
|
|
return $this->_countParts;
|
|
}
|
|
|
|
$this->_countParts = count($this->_parts);
|
|
if ($this->_countParts) {
|
|
return $this->_countParts;
|
|
}
|
|
|
|
if ($this->_mail && $this->_mail->hasFetchPart) {
|
|
// TODO: fetch part
|
|
// return
|
|
}
|
|
|
|
$this->_cacheContent();
|
|
|
|
$this->_countParts = count($this->_parts);
|
|
return $this->_countParts;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get all headers
|
|
*
|
|
* The returned headers are as saved internally. All names are lowercased. The value is a string or an array
|
|
* if a header with the same name occurs more than once.
|
|
*
|
|
* @return array headers as array(name => value)
|
|
*/
|
|
public function getHeaders()
|
|
{
|
|
if ($this->_headers === null) {
|
|
if (!$this->_mail) {
|
|
$this->_headers = array();
|
|
} else {
|
|
$part = $this->_mail->getRawHeader($this->_messageNum);
|
|
Zend_Mime_Decode::splitMessage($part, $this->_headers, $null);
|
|
}
|
|
}
|
|
|
|
return $this->_headers;
|
|
}
|
|
|
|
/**
|
|
* Get a header in specificed format
|
|
*
|
|
* Internally headers that occur more than once are saved as array, all other as string. If $format
|
|
* is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
|
|
*
|
|
* @param string $name name of header, matches case-insensitive, but camel-case is replaced with dashes
|
|
* @param string $format change type of return value to 'string' or 'array'
|
|
* @return string|array value of header in wanted or internal format
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
public function getHeader($name, $format = null)
|
|
{
|
|
if ($this->_headers === null) {
|
|
$this->getHeaders();
|
|
}
|
|
|
|
$lowerName = strtolower($name);
|
|
|
|
if ($this->headerExists($name) == false) {
|
|
$lowerName = strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $name));
|
|
if($this->headerExists($lowerName) == false) {
|
|
/**
|
|
* @see Zend_Mail_Exception
|
|
*/
|
|
require_once 'Zend/Mail/Exception.php';
|
|
throw new Zend_Mail_Exception("no Header with Name $name or $lowerName found");
|
|
}
|
|
}
|
|
$name = $lowerName;
|
|
|
|
$header = $this->_headers[$name];
|
|
|
|
switch ($format) {
|
|
case 'string':
|
|
if (is_array($header)) {
|
|
$header = implode(Zend_Mime::LINEEND, $header);
|
|
}
|
|
break;
|
|
case 'array':
|
|
$header = (array)$header;
|
|
default:
|
|
// do nothing
|
|
}
|
|
|
|
return $header;
|
|
}
|
|
|
|
/**
|
|
* Check wheater the Mail part has a specific header.
|
|
*
|
|
* @param string $name
|
|
* @return boolean
|
|
*/
|
|
public function headerExists($name)
|
|
{
|
|
$name = strtolower($name);
|
|
if(isset($this->_headers[$name])) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a specific field from a header like content type or all fields as array
|
|
*
|
|
* If the header occurs more than once, only the value from the first header
|
|
* is returned.
|
|
*
|
|
* Throws a Zend_Mail_Exception if the requested header does not exist. If
|
|
* the specific header field does not exist, returns null.
|
|
*
|
|
* @param string $name name of header, like in getHeader()
|
|
* @param string $wantedPart the wanted part, default is first, if null an array with all parts is returned
|
|
* @param string $firstName key name for the first part
|
|
* @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
|
|
* @throws Zend_Exception, Zend_Mail_Exception
|
|
*/
|
|
public function getHeaderField($name, $wantedPart = 0, $firstName = 0) {
|
|
return Zend_Mime_Decode::splitHeaderField(current($this->getHeader($name, 'array')), $wantedPart, $firstName);
|
|
}
|
|
|
|
|
|
/**
|
|
* Getter for mail headers - name is matched in lowercase
|
|
*
|
|
* This getter is short for Zend_Mail_Part::getHeader($name, 'string')
|
|
*
|
|
* @see Zend_Mail_Part::getHeader()
|
|
*
|
|
* @param string $name header name
|
|
* @return string value of header
|
|
* @throws Zend_Mail_Exception
|
|
*/
|
|
public function __get($name)
|
|
{
|
|
return $this->getHeader($name, 'string');
|
|
}
|
|
|
|
/**
|
|
* Isset magic method proxy to hasHeader
|
|
*
|
|
* This method is short syntax for Zend_Mail_Part::hasHeader($name);
|
|
*
|
|
* @see Zend_Mail_Part::hasHeader
|
|
*
|
|
* @param string
|
|
* @return boolean
|
|
*/
|
|
public function __isset($name)
|
|
{
|
|
return $this->headerExists($name);
|
|
}
|
|
|
|
/**
|
|
* magic method to get content of part
|
|
*
|
|
* @return string content
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->getContent();
|
|
}
|
|
|
|
/**
|
|
* implements RecursiveIterator::hasChildren()
|
|
*
|
|
* @return bool current element has children/is multipart
|
|
*/
|
|
public function hasChildren()
|
|
{
|
|
$current = $this->current();
|
|
return $current && $current instanceof Zend_Mail_Part && $current->isMultipart();
|
|
}
|
|
|
|
/**
|
|
* implements RecursiveIterator::getChildren()
|
|
*
|
|
* @return Zend_Mail_Part same as self::current()
|
|
*/
|
|
public function getChildren()
|
|
{
|
|
return $this->current();
|
|
}
|
|
|
|
/**
|
|
* implements Iterator::valid()
|
|
*
|
|
* @return bool check if there's a current element
|
|
*/
|
|
public function valid()
|
|
{
|
|
if ($this->_countParts === null) {
|
|
$this->countParts();
|
|
}
|
|
return $this->_iterationPos && $this->_iterationPos <= $this->_countParts;
|
|
}
|
|
|
|
/**
|
|
* implements Iterator::next()
|
|
*
|
|
* @return null
|
|
*/
|
|
public function next()
|
|
{
|
|
++$this->_iterationPos;
|
|
}
|
|
|
|
/**
|
|
* implements Iterator::key()
|
|
*
|
|
* @return string key/number of current part
|
|
*/
|
|
public function key()
|
|
{
|
|
return $this->_iterationPos;
|
|
}
|
|
|
|
/**
|
|
* implements Iterator::current()
|
|
*
|
|
* @return Zend_Mail_Part current part
|
|
*/
|
|
public function current()
|
|
{
|
|
return $this->getPart($this->_iterationPos);
|
|
}
|
|
|
|
/**
|
|
* implements Iterator::rewind()
|
|
*
|
|
* @return null
|
|
*/
|
|
public function rewind()
|
|
{
|
|
$this->countParts();
|
|
$this->_iterationPos = 1;
|
|
}
|
|
}
|