From d2a84753162047971a86850893d260f171d3808b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 29 Jun 2020 13:59:34 +0200 Subject: [PATCH] Add support for the List-ID header to Email see #3379 --- .../files/lib/system/email/Email.class.php | 66 +++++++++++++++++++ .../lib/system/email/EmailGrammar.class.php | 1 + 2 files changed, 67 insertions(+) diff --git a/wcfsetup/install/files/lib/system/email/Email.class.php b/wcfsetup/install/files/lib/system/email/Email.class.php index 7f60265fbc..71ec1ac093 100644 --- a/wcfsetup/install/files/lib/system/email/Email.class.php +++ b/wcfsetup/install/files/lib/system/email/Email.class.php @@ -57,6 +57,20 @@ class Email { */ protected $inReplyTo = []; + /** + * List-Id header + * @var string + * @since 5.3 + */ + protected $listId = null; + + /** + * Human readable part of the List-Id header + * @var string + * @since 5.3 + */ + protected $listIdHuman = null; + /** * Date header * @var \DateTime @@ -241,6 +255,52 @@ class Email { return $this->references; } + /** + * Sets the list-label part of the email's 'List-Id'. + * + * @param string $listId + * @param string $humanReadable + * @throws \DomainException + * @since 5.3 + */ + public function setListID($listId, $humanReadable = null) { + if ($listId === null) { + $this->listId = null; + return; + } + + if (!preg_match('(^'.EmailGrammar::getGrammar('list-label').'$)', $listId)) { + throw new \DomainException("The given list id '".$listId."' is invalid."); + } + if (strlen($listId) > 200) { + throw new \DomainException("The given list id '".$listId."' is not allowed. The maximum allowed length is 200 bytes."); + } + if ($humanReadable !== null) { + $humanReadable = EmailGrammar::encodeHeader($humanReadable); + if (!preg_match('(^'.EmailGrammar::getGrammar('phrase').'$)', $humanReadable)) { + throw new \DomainException("The given human readable name '".$humanReadable."' is invalid."); + } + } + + $this->listId = $listId; + $this->listIdHuman = $humanReadable; + } + + /** + * Returns the email's full 'List-Id' including the host. Returns 'null' + * if no 'List-Id' is set. + * + * @return ?string + * @since 5.3 + */ + public function getListID() { + if ($this->listId === null) { + return null; + } + + return ($this->listIdHuman ? $this->listIdHuman.' ' : '').'<'.$this->listId.'.list-id.'.self::getHost().'>'; + } + /** * Sets the email's 'From'. * @@ -394,6 +454,9 @@ class Email { if ($this->getInReplyTo()) { $headers[] = ['in-reply-to', implode("\r\n ", $this->getInReplyTo())]; } + if ($this->getListID()) { + $headers[] = ['list-id', $this->getListID()]; + } $headers[] = ['mime-version', '1.0']; if (!$this->body) { @@ -425,6 +488,9 @@ class Email { case 'message-id': $name = 'Message-ID'; break; + case 'list-id': + $name = 'List-ID'; + break; case 'mime-version': $name = 'MIME-Version'; break; diff --git a/wcfsetup/install/files/lib/system/email/EmailGrammar.class.php b/wcfsetup/install/files/lib/system/email/EmailGrammar.class.php index 504060ebdd..60229c05c3 100644 --- a/wcfsetup/install/files/lib/system/email/EmailGrammar.class.php +++ b/wcfsetup/install/files/lib/system/email/EmailGrammar.class.php @@ -36,6 +36,7 @@ final class EmailGrammar { case 'atom': return "(?:".self::getGrammar('CFWS')."?".self::getGrammar('atext')."+".self::getGrammar('CFWS')."?)"; case 'id-left': + case 'list-label': case 'dot-atom-text': return "(?:".self::getGrammar('atext')."+(?:\\.".self::getGrammar('atext').'+)*)'; case 'no-fold-literal': -- 2.20.1