--- /dev/null
+<?php
+namespace wcf\system\email;
+
+/**
+ * Holds RFC 2045 and RFC 5322 grammar tokens and provides helper functions
+ * for dealing with these RFCs.
+ *
+ * @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
+ */
+final class EmailGrammar {
+ /**
+ * Returns a regular expression matching the given type in RFC 5322.
+ *
+ * @param string $type
+ * @return string
+ */
+ public static function getGrammar($type) {
+ switch ($type) {
+ case 'VCHAR':
+ return '[\x21-\x7E]';
+ case 'WSP':
+ return '[\x20\x09]';
+ case 'FWS':
+ return "(?:(?:".self::getGrammar('WSP')."*\r\n)?".self::getGrammar('WSP')."+)";
+ case 'CFWS':
+ // note: no support for comments
+ return self::getGrammar('FWS');
+ case 'quoted-pair':
+ return "(?:\\\\(?:".self::getGrammar('WSP')."|".self::getGrammar('VCHAR')."))";
+ case 'atext':
+ return "[a-zA-Z0-9!#$%&'*+-/=?^_`{|}~]";
+ case 'atom':
+ return "(?:".self::getGrammar('CFWS')."?".self::getGrammar('atext')."+".self::getGrammar('CFWS')."?)";
+ case 'id-left':
+ case 'dot-atom-text':
+ return "(?:".self::getGrammar('atext')."+(?:\\.".self::getGrammar('atext').'+)*)';
+ case 'no-fold-literal':
+ return "(?:\\[".self::getGrammar('dtext')."*\\])";
+ case 'id-right':
+ return "(?:".self::getGrammar('dot-atom-text')."|".self::getGrammar('no-fold-literal').")";
+ case 'dot-atom':
+ return "(?:".self::getGrammar('CFWS')."?".self::getGrammar('dot-atom-text').self::getGrammar('CFWS')."?)";
+ case 'qtext':
+ return '[\x21\x23-\x5B\x5D-\x7E]';
+ case 'qcontent':
+ return "(?:".self::getGrammar('qtext')."|".self::getGrammar('quoted-pair').")";
+ case 'quoted-string':
+ return "(?:".self::getGrammar('CFWS')."?\"(?:".self::getGrammar('FWS')."?".self::getGrammar('qcontent').")*".self::getGrammar('FWS')."?\"".self::getGrammar('CFWS')."?)";
+ case 'word':
+ return "(?:".self::getGrammar('atom')."|".self::getGrammar('quoted-string').")";
+ case 'display-name':
+ case 'phrase':
+ return "(?:".self::getGrammar('word')."+)";
+ case 'local-part':
+ return "(?:".self::getGrammar('dot-atom')."|".self::getGrammar('quoted-string').")";
+ case 'dtext':
+ return '[\x21-\x5A\x5E-\x7E]';
+ case 'domain-literal':
+ return "(?:".self::getGrammar('CFWS')."?\\[(?:".self::getGrammar('FWS')."?".self::getGrammar('dtext').")*".self::getGrammar('FWS')."?\\]".self::getGrammar('CFWS')."?)";
+ case 'domain':
+ return "(?:".self::getGrammar('dot-atom')."|".self::getGrammar('domain-literal').")";
+ case 'addr-spec':
+ return "(?:".self::getGrammar('local-part')."@".self::getGrammar('domain').")";
+ case 'angle-addr':
+ return "(?:".self::getGrammar('CFWS')."?<".self::getGrammar('addr-spec').">".self::getGrammar('CFWS')."?)";
+ case 'name-addr':
+ return "(?:".self::getGrammar('display-name')."?".self::getGrammar('angle-addr').")";
+ case 'mailbox':
+ return "(?:".self::getGrammar('name-addr')."|".self::getGrammar('addr-spec').")";
+ case 'msg-id':
+ return "(?:".self::getGrammar('CFWS')."?<".self::getGrammar('id-left')."@".self::getGrammar('id-right').">".self::getGrammar('CFWS')."?)";
+ }
+ }
+
+ /**
+ * Encode text using quoted printable encoding.
+ *
+ * @param string $header Header to encode
+ * @return string Encoded header
+ */
+ public static function encodeMimeHeader($header) {
+ return mb_encode_mimeheader($header, "UTF-8", "Q", "\r\n");
+ }
+
+ private function __construct() { }
+}
--- /dev/null
+<?php
+namespace wcf\system\email;
+use wcf\system\exception\SystemException;
+
+/**
+ * Represents a RFC 5322 mailbox.
+ *
+ * @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 Mailbox {
+ /**
+ * The email address of this mailbox.
+ * @var string
+ */
+ protected $address;
+
+ /**
+ * The human readable name of this mailbox.
+ * @var string
+ */
+ protected $name = null;
+
+ /**
+ * Creates a new Mailbox.
+ *
+ * @param string $address email address of this mailbox
+ * @param string $name human readable name of this mailbox (or null)
+ */
+ public function __construct($address, $name = null) {
+ if (!preg_match('(^'.EmailGrammar::getGrammar('addr-spec').'$)', $address)) {
+ throw new SystemException("The given email address '".$address."' is invalid.");
+ }
+
+ $this->address = $address;
+ $this->name = $name;
+ }
+
+ /**
+ * Returns the human readable name of this mailbox.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns the email address of this mailbox.
+ *
+ * @return string
+ */
+ public function getAddress() {
+ return $this->address;
+ }
+
+ /**
+ * Returns a string representation for use in a RFC 5233 message.
+ *
+ * @return string
+ */
+ public function __toString() {
+ if ($this->name === null) {
+ return $this->address;
+ }
+
+ $name = $this->name;
+ if (!preg_match('(^'.EmailGrammar::getGrammar('atom').'$)', $name)) {
+ if (($encoded = EmailGrammar::encodeMimeHeader($name)) === $name) {
+ $name = '"'.addcslashes($name, '\\"').'"';
+ }
+ else {
+ $name = $encoded;
+ }
+ }
+
+ return $name.' <'.$this->address.'>';
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\email;
+use wcf\data\user\User;
+
+/**
+ * Represents mailbox belonging to a specific registered user.
+ *
+ * @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 UserMailbox extends Mailbox {
+ /**
+ * User object belonging to this Mailbox
+ * @var \wcf\data\user\User
+ */
+ protected $user = null;
+
+ /**
+ * Creates a new Mailbox.
+ *
+ * @param \wcf\data\user\User $user User object belonging to this Mailbox
+ */
+ public function __construct(User $user) {
+ parent::__construct($user->email, $user->username);
+
+ $this->user = $user;
+ }
+
+ /**
+ * Returns the User object belonging to this Mailbox.
+ *
+ * @return \wcf\data\user\User
+ */
+ public function getUser() {
+ return $this->user;
+ }
+}