From de53537ba3a16b153efae3777935d09879f9c394 Mon Sep 17 00:00:00 2001 From: puhr-mde <45031539+puhr-mde@users.noreply.github.com> Date: Thu, 26 Sep 2019 10:52:14 +0200 Subject: [PATCH] Cancel ICS file corrupted on Office 365 #1780 (#1842) * Cancel ICS file corrupted on Office 365 #1780 --- src/PHPMailer.php | 43 +++++++++++++- test/PHPMailerTest.php | 125 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) diff --git a/src/PHPMailer.php b/src/PHPMailer.php index df4286cc..978e00ee 100644 --- a/src/PHPMailer.php +++ b/src/PHPMailer.php @@ -50,6 +50,15 @@ class PHPMailer const ENCRYPTION_STARTTLS = 'tls'; const ENCRYPTION_SMTPS = 'ssl'; + const ICAL_METHOD_REQUEST = 'REQUEST'; + const ICAL_METHOD_PUBLISH = 'PUBLISH'; + const ICAL_METHOD_REPLY = 'REPLY'; + const ICAL_METHOD_ADD = 'ADD'; + const ICAL_METHOD_CANCEL = 'CANCEL'; + const ICAL_METHOD_REFRESH = 'REFRESH'; + const ICAL_METHOD_COUNTER = 'COUNTER'; + const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + /** * Email priority. * Options: null (default), 1 = High, 3 = Normal, 5 = low. @@ -149,6 +158,22 @@ class PHPMailer */ public $Ical = ''; + /** + * Value-array of "method" in Contenttype header "text/calendar" + * + * @var string[] + */ + protected static $IcalMethods = [ + self::ICAL_METHOD_REQUEST, + self::ICAL_METHOD_PUBLISH, + self::ICAL_METHOD_REPLY, + self::ICAL_METHOD_ADD, + self::ICAL_METHOD_CANCEL, + self::ICAL_METHOD_REFRESH, + self::ICAL_METHOD_COUNTER, + self::ICAL_METHOD_DECLINECOUNTER, + ]; + /** * The complete compiled MIME message body. * @@ -2604,7 +2629,14 @@ class PHPMailer $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= static::$LE; if (!empty($this->Ical)) { - $body .= $this->getBoundary($this->boundary[1], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', ''); + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary($this->boundary[1], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, ''); $body .= $this->encodeString($this->Ical, $this->Encoding); $body .= static::$LE; } @@ -2640,7 +2672,14 @@ class PHPMailer $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= static::$LE; if (!empty($this->Ical)) { - $body .= $this->getBoundary($this->boundary[2], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=REQUEST', ''); + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary($this->boundary[2], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, ''); $body .= $this->encodeString($this->Ical, $this->Encoding); } $body .= $this->endBoundary($this->boundary[2]); diff --git a/test/PHPMailerTest.php b/test/PHPMailerTest.php index d8a9aeb2..408f8e3a 100644 --- a/test/PHPMailerTest.php +++ b/test/PHPMailerTest.php @@ -2686,6 +2686,131 @@ EOT; $this->assertNull($subject); $this->assertInstanceOf(OAuth::class, $PHPMailer->getOAuth()); } + + /** + * Test ICal method + */ + public function testICalMethod() + { + $this->Mail->Subject .= ': ICal method'; + $this->Mail->Body = '

ICal method test.

'; + $this->Mail->AltBody = 'ICal method test.'; + $this->Mail->Ical = 'BEGIN:VCALENDAR' + . "\r\nVERSION:2.0" + . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" + . "\r\nMETHOD:CANCEL" + . "\r\nCALSCALE:GREGORIAN" + . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" + . "\r\nBEGIN:VEVENT" + . "\r\nUID:201909250755-42825@test" + . "\r\nDTSTART;20190930T080000Z" + . "\r\nSEQUENCE:2" + . "\r\nTRANSP:OPAQUE" + . "\r\nSTATUS:CONFIRMED" + . "\r\nDTEND:20190930T084500Z" + . "\r\nLOCATION:[London] London Eye" + . "\r\nSUMMARY:Test ICal method" + . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" + . "\r\n TRUE:MAILTO:attendee-test@example.com" + . "\r\nCLASS:PUBLIC" + . "\r\nDESCRIPTION:Some plain text" + . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" + . "\r\nDTSTAMP:20190925T075546Z" + . "\r\nCREATED:20190925T075709Z" + . "\r\nLAST-MODIFIED:20190925T075546Z" + . "\r\nEND:VEVENT" + . "\r\nEND:VCALENDAR"; + $this->buildBody(); + $this->Mail->preSend(); + $this->assertRegExp( + '/Content-Type: text\/calendar; method=CANCEL;/', + $this->Mail->getSentMIMEMessage(), + 'Wrong ICal method in Content-Type header' + ); + } + + /** + * Test ICal missing method to use default (REQUEST) + */ + public function testICalInvalidMethod() + { + $this->Mail->Subject .= ': ICal method'; + $this->Mail->Body = '

ICal method test.

'; + $this->Mail->AltBody = 'ICal method test.'; + $this->Mail->Ical = 'BEGIN:VCALENDAR' + . "\r\nVERSION:2.0" + . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" + . "\r\nMETHOD:INVALID" + . "\r\nCALSCALE:GREGORIAN" + . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" + . "\r\nBEGIN:VEVENT" + . "\r\nUID:201909250755-42825@test" + . "\r\nDTSTART;20190930T080000Z" + . "\r\nSEQUENCE:2" + . "\r\nTRANSP:OPAQUE" + . "\r\nSTATUS:CONFIRMED" + . "\r\nDTEND:20190930T084500Z" + . "\r\nLOCATION:[London] London Eye" + . "\r\nSUMMARY:Test ICal method" + . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" + . "\r\n TRUE:MAILTO:attendee-test@example.com" + . "\r\nCLASS:PUBLIC" + . "\r\nDESCRIPTION:Some plain text" + . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" + . "\r\nDTSTAMP:20190925T075546Z" + . "\r\nCREATED:20190925T075709Z" + . "\r\nLAST-MODIFIED:20190925T075546Z" + . "\r\nEND:VEVENT" + . "\r\nEND:VCALENDAR"; + $this->buildBody(); + $this->Mail->preSend(); + $this->assertRegExp( + '/Content-Type: text\/calendar; method=REQUEST;/', + $this->Mail->getSentMIMEMessage(), + 'Wrong ICal method in Content-Type header' + ); + } + + /** + * Test ICal invalid method to use default (REQUEST) + */ + public function testICalDefaultMethod() + { + $this->Mail->Subject .= ': ICal method'; + $this->Mail->Body = '

ICal method test.

'; + $this->Mail->AltBody = 'ICal method test.'; + $this->Mail->Ical = 'BEGIN:VCALENDAR' + . "\r\nVERSION:2.0" + . "\r\nPRODID:-//PHPMailer//PHPMailer Calendar Plugin 1.0//EN" + . "\r\nCALSCALE:GREGORIAN" + . "\r\nX-MICROSOFT-CALSCALE:GREGORIAN" + . "\r\nBEGIN:VEVENT" + . "\r\nUID:201909250755-42825@test" + . "\r\nDTSTART;20190930T080000Z" + . "\r\nSEQUENCE:2" + . "\r\nTRANSP:OPAQUE" + . "\r\nSTATUS:CONFIRMED" + . "\r\nDTEND:20190930T084500Z" + . "\r\nLOCATION:[London] London Eye" + . "\r\nSUMMARY:Test ICal method" + . "\r\nATTENDEE;CN=Attendee, Test;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=" + . "\r\n TRUE:MAILTO:attendee-test@example.com" + . "\r\nCLASS:PUBLIC" + . "\r\nDESCRIPTION:Some plain text" + . "\r\nORGANIZER;CN=\"Example, Test\":MAILTO:test@example.com" + . "\r\nDTSTAMP:20190925T075546Z" + . "\r\nCREATED:20190925T075709Z" + . "\r\nLAST-MODIFIED:20190925T075546Z" + . "\r\nEND:VEVENT" + . "\r\nEND:VCALENDAR"; + $this->buildBody(); + $this->Mail->preSend(); + $this->assertRegExp( + '/Content-Type: text\/calendar; method=REQUEST;/', + $this->Mail->getSentMIMEMessage(), + 'Wrong ICal method in Content-Type header' + ); + } } /* * This is a sample form for setting appropriate test values through a browser