/system/vendor/swift/Swift/Message/Mime.php
PHP | 500 lines | 303 code | 12 blank | 185 comment | 35 complexity | 5957a4b0c13b125be5703718d98529b7 MD5 | raw file
Possible License(s): LGPL-2.1
- <?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();
- }