bebeboutik/tools/swift/Swift/Message/Mime.php
Srv Bebeboutik 340bc7c146 push site
2016-01-04 12:48:08 +01:00

501 lines
14 KiB
PHP
Executable File

<?php
/**
* Swift Mailer MIME Library central component
* Please read the LICENSE file
* @copyright Chris Corbyn <chris@w3style.co.uk>
* @author Chris Corbyn <chris@w3style.co.uk>
* @package Swift_Message
* @license GNU Lesser General Public License
*/
require_once dirname(__FILE__) . "/../ClassLoader.php";
Swift_ClassLoader::load("Swift_File");
Swift_ClassLoader::load("Swift_Message_MimeException");
/**
* Mime is the underbelly for Messages, Attachments, Parts, Embedded Images, Forwarded Mail, etc
* In fact, every single component of the composed email is simply a new Mime document nested inside another
* When you piece an email together in this way you see just how straight-forward it really is
* @package Swift_Message
* @author Chris Corbyn <chris@w3style.co.uk>
*/
abstract class Swift_Message_Mime
{
/**
* Constant for plain-text emails
*/
const PLAIN = "text/plain";
/**
* Constant for HTML emails
*/
const HTML = "text/html";
/**
* Constant for miscellaneous mime type
*/
const MISC = "application/octet-stream";
/**
* Constant for MIME sections which must appear in the multipart/alternative section.
*/
const LEVEL_ALTERNATIVE = "alternative";
/**
* Constant for MIME sections which must appear in the multipart/related section.
*/
const LEVEL_RELATED = "related";
/**
* Constant for MIME sections which must appear in the multipart/mixed section.
*/
const LEVEL_MIXED = "mixed";
/**
* Constant for MIME sections which must appear in the multipart/mixed section.
*/
const LEVEL_TOP = "top";
/**
* Constant for safe line length in almost all places
*/
const SAFE_LENGTH = 1000; //RFC 2822
/**
* Constant for really safe line length
*/
const VERY_SAFE_LENGTH = 76; //For command line mail clients such as pine
/**
* The header part of this MIME document
* @var Swift_Message_Headers
*/
public $headers = null;
/**
* The body of the documented (unencoded)
* @var string data
*/
protected $data = "";
/**
* Maximum line length
* @var int
*/
protected $wrap = 1000; //RFC 2822
/**
* Nested mime parts
* @var array
*/
protected $children = array();
/**
* The boundary used to separate mime parts
* @var string
*/
protected $boundary = null;
/**
* The line ending characters needed
* @var string
*/
protected $LE = "\r\n";
/**
* An instance of Swift_Cache
* @var Swift_Cache
*/
protected $cache;
/**
* A list of used MIME boundaries after they're generated.
* @var array
*/
protected static $usedBoundaries = array();
/**
* Constructor
*/
public function __construct()
{
Swift_ClassLoader::load("Swift_Message_Headers");
$this->setHeaders(new Swift_Message_Headers());
Swift_ClassLoader::load("Swift_CacheFactory");
$this->cache = Swift_CacheFactory::getCache();
}
/**
* Compute a unique boundary
* @return string
*/
public static function generateBoundary()
{
do
{
$boundary = uniqid(rand(), true);
} while (in_array($boundary, self::$usedBoundaries));
self::$usedBoundaries[] = $boundary;
return "_=_swift-" . $boundary . "_=_";
}
/**
* Replace the current headers with new ones
* DO NOT DO THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
* @param Swift_Message_Headers The headers to use
*/
public function setHeaders($headers)
{
$this->headers = $headers;
}
/**
* Set the line ending character to use
* @param string The line ending sequence
* @return boolean
*/
public function setLE($le)
{
if (in_array($le, array("\r", "\n", "\r\n")))
{
$this->cache->clear("body");
$this->LE = $le;
//This change should be recursive
$this->headers->setLE($le);
foreach ($this->children as $id => $child)
{
$this->children[$id]->setLE($le);
}
return true;
}
else return false;
}
/**
* Get the line ending sequence
* @return string
*/
public function getLE()
{
return $this->LE;
}
/**
* Reset the entire cache state from this branch of the tree and traversing down through the children
*/
public function uncacheAll()
{
$this->cache->clear("body");
$this->cache->clear("append");
$this->cache->clear("headers");
$this->cache->clear("dbl_le");
$this->headers->uncacheAll();
foreach ($this->children as $id => $child)
{
$this->children[$id]->uncacheAll();
}
}
/**
* Set the content type of this MIME document
* @param string The content type to use in the same format as MIME 1.0 expects
*/
public function setContentType($type)
{
$this->headers->set("Content-Type", $type);
}
/**
* Get the content type which has been set
* The MIME 1.0 Content-Type is provided as a string
* @return string
*/
public function getContentType()
{
try {
return $this->headers->get("Content-Type");
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Set the encoding format to be used on the body of the document
* @param string The encoding type used
* @param boolean If this encoding format should be used recursively. Note, this only takes effect if no encoding is set in the children.
* @param boolean If the encoding should only be applied when the string is not ascii.
*/
public function setEncoding($encoding, $recursive=false, $non_ascii=false)
{
$this->cache->clear("body");
switch (strtolower($encoding))
{
case "q": case "qp": case "quoted-printable":
$encoding = "quoted-printable";
break;
case "b": case "base64":
$encoding = "base64";
break;
case "7bit": case "8bit": case "binary":
$encoding = strtolower($encoding);
break;
}
$data = $this->getData();
Swift_ClassLoader::load("Swift_Message_Encoder");
if ($non_ascii && is_string($data) && strlen($data) > 0 && !Swift_Message_Encoder::instance()->is7BitAscii($data))
{
$this->headers->set("Content-Transfer-Encoding", $encoding);
}
elseif (!$non_ascii || !is_string($data))
{
$this->headers->set("Content-Transfer-Encoding", $encoding);
}
if ($recursive)
{
foreach ($this->children as $id => $child)
{
if (!$child->getEncoding()) $this->children[$id]->setEncoding($encoding, $recursive, $non_ascii);
}
}
}
/**
* Get the encoding format used in this document
* @return string
*/
public function getEncoding()
{
try {
return $this->headers->get("Content-Transfer-Encoding");
} catch (Swift_Message_MimeException $e) {
return false;
}
}
/**
* Specify the string which makes up the body of this message
* HINT: You can always nest another MIME document here if you call it's build() method.
* $data can be an object of Swift_File or a string
* @param mixed The body of the document
*/
public function setData($data)
{
$this->cache->clear("body");
if ($data instanceof Swift_File) $this->data = $data;
else $this->data = (string) $data;
}
/**
* Return the string which makes up the body of this MIME document
* @return string,Swift_File
*/
public function getData()
{
return $this->data;
}
/**
* Get the data in the format suitable for sending
* @return Swift_Cache_OutputStream
* @throws Swift_FileException If the file stream given cannot be read
* @throws Swift_Message_MimeException If some required headers have been forcefully removed
*/
public function buildData()
{
Swift_ClassLoader::load("Swift_Message_Encoder");
Swift_ClassLoader::load("Swift_Cache_JointOutputStream");
if (!empty($this->children)) //If we've got some mime parts we need to stick them onto the end of the message
{
if ($this->boundary === null) $this->boundary = self::generateBoundary();
$this->headers->setAttribute("Content-Type", "boundary", $this->boundary);
$this->cache->clear("append");
foreach ($this->children as $part)
{
$this->cache->write("append", $this->LE . "--" . $this->boundary . $this->LE);
$part_stream = $part->build();
while (false !== $bytes = $part_stream->read()) $this->cache->write("append", $bytes);
}
$this->cache->write("append", $this->LE . "--" . $this->boundary . "--" . $this->LE);
}
$joint_os = new Swift_Cache_JointOutputStream();
//Try using a cached version to save some cycles (at the expense of memory)
//if ($this->cache !== null) return $this->cache . $append;
if ($this->cache->has("body"))
{
$joint_os->addStream($this->cache->getOutputStream("body"));
$joint_os->addStream($this->cache->getOutputStream("append"));
return $joint_os;
}
$is_file = ($this->getData() instanceof Swift_File);
switch ($this->getEncoding())
{
case "quoted-printable":
if ($is_file)
{
$qp_os = Swift_Message_Encoder::instance()->QPEncodeFile($this->getData(), 76, $this->LE);
while (false !== $bytes = $qp_os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->QPEncode($this->getData(), 76, 0, false, $this->LE));
}
break;
case "base64":
if ($is_file)
{
$b64_os = Swift_Message_Encoder::instance()->base64EncodeFile($this->getData(), 76, $this->LE);
while (false !== $bytes = $b64_os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->base64Encode($this->getData(), 76, 0, false, $this->LE));
}
break;
case "binary":
if ($is_file)
{
$data = $this->getData();
while (false !== $bytes = $data->read(8192))
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", $this->getData());
}
break;
case "7bit":
if ($is_file)
{
$os = Swift_Message_Encoder::instance()->encode7BitFile($this->getData(), $this->wrap, $this->LE);
while (false !== $bytes = $os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->encode7Bit($this->getData(), $this->wrap, $this->LE));
}
break;
case "8bit": default:
if ($is_file)
{
$os = Swift_Message_Encoder::instance()->encode8BitFile($this->getData(), $this->wrap, $this->LE);
while (false !== $bytes = $os->read())
$this->cache->write("body", $bytes);
}
else
{
$this->cache->write("body", Swift_Message_Encoder::instance()->encode8Bit($this->getData(), $this->wrap, $this->LE));
}
break;
}
$joint_os->addStream($this->cache->getOutputStream("body"));
$joint_os->addStream($this->cache->getOutputStream("append"));
return $joint_os;
}
/**
* Set the size at which lines wrap around (includes the CRLF)
* @param int The length of a line
*/
public function setLineWrap($len)
{
$this->cache->clear("body");
$this->wrap = (int) $len;
}
/**
* Nest a child mime part in this document
* @param Swift_Message_Mime
* @param string The identifier to use, optional
* @param int Add the part before (-1) or after (+1) the other parts
* @return string The identifier for this part
*/
public function addChild(Swift_Message_Mime $mime, $id=null, $after=1)
{
if (empty($id))
{
do
{
$id = uniqid();
} while (array_key_exists($id, $this->children));
}
$id = (string) $id;
if ($after == -1) $this->children = array_merge(array($id => $mime), $this->children);
else $this->children[$id] = $mime;
return $id;
}
/**
* Check if a child exists identified by $id
* @param string Identifier to look for
* @return boolean
*/
public function hasChild($id)
{
return array_key_exists($id, $this->children);
}
/**
* Get a child document, identified by $id
* @param string The identifier for this child
* @return Swift_Message_Mime The child document
* @throws Swift_Message_MimeException If no such child exists
*/
public function getChild($id)
{
if ($this->hasChild($id))
{
return $this->children[$id];
}
else
{
throw new Swift_Message_MimeException(
"Cannot retrieve child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
}
}
/**
* Remove a part from the document
* @param string The identifier of the child
* @throws Swift_Message_MimeException If no such part exists
*/
public function removeChild($id)
{
$id = (string) $id;
if (!$this->hasChild($id))
{
throw new Swift_Message_MimeException(
"Cannot remove child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
}
else
{
$this->children[$id] = null;
unset($this->children[$id]);
}
}
/**
* List the IDs of all children in this document
* @return array
*/
public function listChildren()
{
return array_keys($this->children);
}
/**
* Get the total number of children present in this document
* @return int
*/
public function numChildren()
{
return count($this->children);
}
/**
* Get the level at which this mime part would appear in a document
* One of "mixed", "alternative" or "related"
* @return string
*/
abstract public function getLevel();
/**
* Compile the entire MIME document into a string
* The returned string may be used in other documents if needed.
* @return Swift_Cache_OutputStream
*/
public function build()
{
$this->preBuild();
$data = $this->buildData();
$joint_os = new Swift_Cache_JointOutputStream();
$this->cache->clear("headers");
$this->cache->write("headers", $this->headers->build());
$joint_os->addStream($this->cache->getOutputStream("headers"));
$this->cache->clear("dbl_le");
$this->cache->write("dbl_le", str_repeat($this->LE, 2));
$joint_os->addStream($this->cache->getOutputStream("dbl_le"));
$joint_os->addStream($data);
return $joint_os;
//return $this->headers->build() . str_repeat($this->LE, 2) . $data;
}
/**
* Execute any logic needed prior to building
*/
abstract public function preBuild();
}