Add \wcf\system\email\Email
authorTim Düsterhus <duesterhus@woltlab.com>
Sun, 14 Jun 2015 19:37:48 +0000 (21:37 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 23 Jun 2015 22:21:07 +0000 (00:21 +0200)
It already supports handling the Email's headers, but not the body.

wcfsetup/install/files/lib/system/email/Email.class.php [new file with mode: 0644]

diff --git a/wcfsetup/install/files/lib/system/email/Email.class.php b/wcfsetup/install/files/lib/system/email/Email.class.php
new file mode 100644 (file)
index 0000000..fe2e74d
--- /dev/null
@@ -0,0 +1,394 @@
+<?php
+namespace wcf\system\email;
+use wcf\system\exception\SystemException;
+use wcf\util\DateUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a RFC 5322 message using the Mime format as defined in RFC 2045.
+ * 
+ * @author     Tim Duesterhus
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.email
+ * @category   Community Framework
+ */
+class Email {
+       /**
+        * From header
+        * @var \wcf\system\email\Mailbox
+        */
+       protected $sender = null;
+       
+       /**
+        * Reply-To header
+        * @var \wcf\system\email\Mailbox
+        */
+       protected $replyTo = null;
+       
+       /**
+        * Recipients of this email.
+        * @var array
+        */
+       protected $recipients = [ ];
+       
+       /**
+        * Message-Id header
+        * @var string
+        */
+       protected $messageID = null;
+       
+       /**
+        * References header
+        * @var array<\wcf\system\email\Mailbox>
+        */
+       protected $references = [ ];
+       
+       /**
+        * In-Reply-To header
+        * @var array<\wcf\system\email\Mailbox>
+        */
+       protected $inReplyTo = [ ];
+       
+       /**
+        * Date header
+        * @var \DateTime
+        */
+       protected $date = null;
+       
+       /**
+        * Subject header
+        * @var string
+        */
+       protected $subject = '';
+       
+       /**
+        * User specified X-* headers
+        * @var array
+        */
+       protected $extraHeaders = [ ];
+       
+       /**
+        * Mail host for use in the Message-Id
+        * @var string
+        */
+       private static $host = null;
+       
+       /**
+        * Returns the mail host for use in the Message-Id.
+        * 
+        * @return      string
+        */
+       public static function getHost() {
+               if (self::$host === null) {
+                       self::$host = (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : '';
+                       if (empty(self::$host)) {
+                               self::$host = gethostname();
+                               if (self::$host === false) {
+                                       self::$host = 'localhost';
+                               }
+                       }
+               }
+               
+               return self::$host;
+       }
+       
+       /**
+        * Sets the email's 'Subject'.
+        * 
+        * @param       string  $subject
+        */
+       public function setSubject($subject) {
+               $this->subject = $subject;
+       }
+       
+       /**
+        * Returns the email's 'Subject'.
+        * 
+        * @return      string
+        */
+       public function getSubject() {
+               return $this->subject;
+       }
+       
+       /**
+        * Sets the email's 'Date'.
+        * 
+        * @param       \DateTime       $date
+        */
+       public function setDate(\DateTime $date = null) {
+               $this->date = $date;
+       }
+       
+       /**
+        * Returns the email's 'Date'.
+        * If no header is set yet the current time will automatically be set.
+        * 
+        * @return      \DateTime
+        */
+       public function getDate() {
+               if ($this->date === null) {
+                       $this->date = DateUtil::getDateTimeByTimestamp(TIME_NOW);
+               }
+               
+               return $this->date;
+       }
+       
+       /**
+        * Sets the part left of the at sign (@) in the email's 'Message-Id'.
+        * 
+        * @param       string  $messageID
+        */
+       public function setMessageID($messageID = null) {
+               if ($messageID === null) {
+                       $this->messageID = null;
+                       return;
+               }
+               
+               if (!preg_match('(^'.EmailGrammar::getGrammar('id-left').'$)', $messageID)) {
+                       throw new SystemException("The given message id '".$messageID."' is invalid. Note: You must not specify the part right of the at sign (@).");
+               }
+               if (strlen($messageID) > 50) {
+                       throw new SystemException("The given message id '".$message."' is not allowed. The maximum allowed length is 50 bytes.");
+               }
+               
+               $this->messageID = $messageID;
+       }
+       
+       /**
+        * Returns the email's full 'Message-Id' including the host.
+        * If no header is set yet a message id will automatically be generated and set.
+        * 
+        * @return      string
+        */
+       public function getMessageID() {
+               if ($this->messageID === null) {
+                       $this->messageID = StringUtil::getRandomID();
+               }
+               
+               return '<'.$this->messageID.'@'.self::getHost().'>';
+       }
+       
+       /**
+        * Adds a message id to the email's 'In-Reply-To'.
+        * 
+        * @param       string  $messageID
+        */
+       public function addInReplyTo($messageID) {
+               if (!preg_match('(^'.EmailGrammar::getGrammar('msg-id').'$)', $messageID)) {
+                       throw new SystemException("The given reference '".$messageID."' is invalid.");
+               }
+               
+               $this->inReplyTo[$messageID] = $messageID;
+       }
+       
+       /**
+        * Removes a message id from the email's 'In-Reply-To'.
+        * 
+        * @param       string  $messageID
+        */
+       public function removeInReplyTo($messageID) {
+               unset($this->inReplyTo[$messageID]);
+       }
+       
+       /**
+        * Returns the email's 'In-Reply-To' message ids.
+        * 
+        * @return      array<string>
+        */
+       public function getInReplyTo() {
+               return $this->inReplyTo;
+       }
+       
+       /**
+        * Adds a message id to the email's 'References'.
+        * 
+        * @param       string  $messageID
+        */
+       public function addReferences($messageID) {
+               if (!preg_match('(^'.EmailGrammar::getGrammar('msg-id').'$)', $messageID)) {
+                       throw new SystemException("The given reference '".$messageID."' is invalid.");
+               }
+               
+               $this->references[$messageID] = $messageID;
+       }
+       
+       /**
+        * Removes a message id from the email's 'References'.
+        * 
+        * @param       string  $messageID
+        */
+       public function removeReferences($messageID) {
+               unset($this->references[$messageID]);
+       }
+       
+       /**
+        * Returns the email's 'References' message ids.
+        * 
+        * @return      array<string>
+        */
+       public function getReferences() {
+               return $this->references;
+       }
+       
+       /**
+        * Sets the email's 'From'.
+        * 
+        * @param       \wcf\system\email\Mailbox       $sender
+        */
+       public function setSender(Mailbox $sender = null) {
+               $this->sender = $sender;
+       }
+
+       /**
+        * Returns the email's 'From'.
+        * If no header is set yet the MAIL_FROM_ADDRESS will automatically be set.
+        * 
+        * @return      \wcf\system\email\Mailbox
+        */
+       public function getSender() {
+               if ($this->sender === null) {
+                       $this->sender = new Mailbox(MAIL_FROM_ADDRESS, MAIL_FROM_NAME);
+               }
+               
+               return $this->sender;
+       }
+       
+       /**
+        * Sets the email's 'Reply-To'.
+        * 
+        * @param       \wcf\system\email\Mailbox       $sender
+        */
+       public function setReplyTo(Mailbox $replyTo = null) {
+               $this->replyTo = $replyTo;
+       }
+       
+       /**
+        * Returns the email's 'Reply-To'.
+        * If no header is set yet the MAIL_ADMIN_ADDRESS will automatically be set.
+        * 
+        * @return      \wcf\system\email\Mailbox
+        */
+       public function getReplyTo() {
+               if ($this->replyTo === null) {
+                       $this->replyTo = new Mailbox(MAIL_ADMIN_ADDRESS);
+               }
+               
+               return $this->replyTo;
+       }
+       
+       /**
+        * Adds a recipient to this email.
+        * 
+        * @param       \wcf\system\email\Mailbox       $recipient
+        * @param       string                          $type           One of 'to', 'cc', 'bcc'
+        */
+       public function addRecipient(Mailbox $recipient, $type = 'to') {
+               switch ($type) {
+                       case 'to':
+                       case 'cc':
+                       case 'bcc':
+                       break;
+                       default:
+                               throw new SystemException("The given type '".$type."' is invalid. Must be one of 'to', 'cc', 'bcc'.");
+               }
+               
+               $this->recipients[$recipient->getAddress()] = [ $type, $recipient ];
+       }
+       
+       /**
+        * Removes a recipient from this email.
+        * 
+        * @param       \wcf\system\email\Mailbox       $recipient
+        */
+       public function removeRecipient(Mailbox $recipient) {
+               unset($this->recipients[$recipient->getAddress()]);
+       }
+       
+       /**
+        * Returns the email's recipients as an array of [ $recipient, $type ] tuples.
+        * 
+        * @return      array
+        */
+       public function getRecipients() {
+               return $this->recipients;
+       }
+       
+       /**
+        * Adds a custom X-* header to the email.
+        * 
+        * @param       string  $header
+        * @param       string  $value
+        */
+       public function addHeader($header, $value) {
+               $header = mb_strtolower($header);
+               if (!StringUtil::startsWith($header, 'x-')) {
+                       throw new SystemException("The header '".$header."' may not be set. You may only set user defined headers (starting with 'X-').");
+               }
+               
+               $this->extraHeaders[] = [ $header, EmailGrammar::encodeMimeHeader($value) ];
+       }
+       
+       /**
+        * Returns an array of [ name, value ] tuples representing the email's headers.
+        * Note: You must have set a Subject and at least one recipient, otherwise fetching the
+        *       headers will fail.
+        * 
+        * @return      array
+        */
+       public function getHeaders() {
+               $headers = [ ];
+               $to = [ ];
+               $cc = [ ];
+               foreach ($this->getRecipients() as $recipient) {
+                       if ($recipient[0] == 'to') $to[] = $recipient[1];
+                       else if ($recipient[0] == 'cc') $cc[] = $recipient[1];
+               }
+               $headers[] = [ 'from', (string) $this->getSender() ];
+               if ($this->getReplyTo()->getAddress() !== $this->getSender()->getAddress()) {
+                       $headers[] = [ 'reply-to', (string) $this->getReplyTo() ];
+               }
+               
+               if ($to) {
+                       $headers[] = [ 'to', implode(",\r\n   ", $to) ];
+               }
+               else {
+                       throw new SystemException("Cannot generate message headers, you must specify a recipient.");
+               }
+               
+               if ($cc) {
+                       $headers[] = [ 'cc', implode(",\r\n   ", $cc) ];
+               }
+               if ($this->getSubject()) {
+                       $headers[] = [ 'subject', EmailGrammar::encodeMimeHeader($this->getSubject()) ];
+               }
+               else {
+                       throw new SystemException("Cannot generate message headers, you must specify a subject.");
+               }
+               
+               $headers[] = [ 'date', $this->getDate()->format(\DateTime::RFC2822) ];
+               $headers[] = [ 'message-id', $this->getMessageID() ];
+               if ($this->getReferences()) {
+                       $headers[] = [ 'references', implode(' ', $this->getReferences()) ];
+               }
+               if ($this->getInReplyTo()) {
+                       $headers[] = [ 'in-reply-to', implode(' ', $this->getInReplyTo()) ];
+               }
+               $headers[] = [ 'mime-version', '1.0' ];
+               
+               return array_merge($headers, $this->extraHeaders);
+       }
+       
+       /**
+        * Returns the email's headers as a string.
+        * @see \wcf\system\email\Email::getHeaders()
+        * 
+        * @return      string
+        */
+       public function getHeaderString() {
+               return implode("\r\n", array_map(function ($item) {
+                       return implode(': ', $item);
+               }, $this->getHeaders()));
+       }
+}