diff --git a/src/TNEFDecoder/TNEFAttachment.php b/src/TNEFDecoder/TNEFAttachment.php index 214761c..f868912 100644 --- a/src/TNEFDecoder/TNEFAttachment.php +++ b/src/TNEFDecoder/TNEFAttachment.php @@ -24,6 +24,7 @@ class TNEFAttachment { var $debug; + var $validateChecksum; var $mailinfo; var $files; var $files_nested; @@ -31,9 +32,10 @@ class TNEFAttachment var $current_receiver; var $body; - function __construct($debug = false) + function __construct($debug = false, $validateChecksum = false) { $this->debug = $debug; + $this->validateChecksum = $validateChecksum; $this->files = array(); $this->attachments = array(); $this->mailinfo = new TNEFMailinfo(); @@ -158,7 +160,13 @@ function tnef_decode_attribute(&$buffer) $attribute = tnef_geti32($buffer); // attribute if $length = tnef_geti32($buffer); // length $value = tnef_getx($length, $buffer); // data - tnef_geti16($buffer); // checksum + $checksumAtt = tnef_geti16($buffer); // checksum + if ($value !== null && $this->validateChecksum) { + $checksum = array_sum(unpack('C*', $value)) & 0xFFFF; + if ($checksum !== $checksumAtt) { + throw new \Exception('Checksums do not match'); + } + } switch($attribute) { @@ -339,7 +347,7 @@ function extract_mapi_attrs(&$buffer) if ($this->debug) tnef_log("MAPI Found nested attachment. Processing new one."); tnef_getx(16, $value); // skip the next 16 bytes (unknown data) - $att = new TNEFAttachment($this->debug); + $att = new TNEFAttachment($this->debug, $this->validateChecksum); $att->decodeTnef($value); $this->attachments[] = $att; if ($this->debug) diff --git a/test/TNEFAttachmentTest.php b/test/TNEFAttachmentTest.php index 2d9d08c..491de09 100644 --- a/test/TNEFAttachmentTest.php +++ b/test/TNEFAttachmentTest.php @@ -7,45 +7,6 @@ class TNEFAttachmentTest extends TestCase { -// /** -// * Test decoding winmail.dat from email attachment encoded in base64 -// */ -// public function testDecode() { -// $buffer = base64_decode(file_get_contents(dirname(__FILE__) . "/testfiles/base64_attachment.txt")); -// file_put_contents(dirname(__FILE__) . "/testfiles/winmail.dat", $buffer); -// -// $attachment = new TNEFAttachment(); -// $attachment->decodeTnef($buffer); -// $files = $attachment->getFiles(); -// -// $this->assertEquals(4, count($files)); -// $this->assertEquals("image001.jpg", $files[0]->getName()); -// $this->assertEquals("image003.jpg", $files[1]->getName()); -// $this->assertEquals("image004.jpg", $files[2]->getName()); -// $this->assertEquals("image005.jpg", $files[3]->getName()); -// -// foreach ($files as $file) { -// $this->assertEquals("image/jpeg", $file->getType()); -// } -// -// } -// -// /** -// * Test decoding winmail.dat file from filesystem -// */ -// public function testDecode2() { -// $buffer = file_get_contents(dirname(__FILE__) . "/testfiles/winmail2.dat"); -// -// $attachment = new TNEFAttachment($buffer); -// $attachment->decodeTnef($buffer); -// $files = $attachment->getFiles(); -// -// $this->assertEquals(3, count($files)); -// $this->assertEquals("EmbeddedRTF.rtf", $files[0]->getName()); -// $this->assertEquals("zappa_av1.jpg", $files[1]->getName()); -// $this->assertEquals("bookmark.htm", $files[2]->getName()); -// } - /** * Test decoding winmail.dat file from filesystem */ @@ -53,7 +14,7 @@ public function testDecode3() { $buffer = file_get_contents(dirname(__FILE__) . "/testfiles/two-files.tnef"); - $attachment = new TNEFAttachment($buffer); + $attachment = new TNEFAttachment(); $attachment->decodeTnef($buffer); $files = $attachment->getFiles(); @@ -62,35 +23,53 @@ public function testDecode3() $this->assertEquals("README", $files[1]->getName()); } - public function testDecodeAuto() { - $files = scandir ( dirname(__FILE__) . "/testfiles/"); - foreach ($files as $file) { - $ext = explode('.', $file); - if (count($ext) == 2 && $ext[1] == "tnef") { - $buffer = file_get_contents(dirname(__FILE__) . "/testfiles/" . $file); - $list = file(dirname(__FILE__) . "/testfiles/" . $ext[0] . '.list', FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES); - $attachment = new TNEFAttachment(); - $attachment->decodeTnef($buffer); - $this->assertEquals(count($list), count($attachment->files)); - $decodedFiles = array_map(function(TNEFFileBase$file) {return $file->getName();}, $attachment->files); - - $withoutRtf = array_filter($list, array($this, "endsWithRtf")); - $withoutRtfdecoded = array_filter($decodedFiles, array($this, "endsWithRtf")); - - $rtfs = array_diff($withoutRtf, $list); - $rtfsDecoded = array_diff($withoutRtfdecoded, $decodedFiles); - - $this->assertEquals(count($rtfs), count($rtfsDecoded)); - - sort($withoutRtfdecoded); - sort($withoutRtf); - $this->assertEquals($withoutRtf, $withoutRtfdecoded); - } + /** + * @dataProvider tnefFileProvider + */ + public function testDecodeAuto($tnefFile, $listFile) { + $buffer = file_get_contents($tnefFile); + $attachment = new TNEFAttachment(false, true); + + if ($listFile === null) { + $this->expectExceptionMessage('Checksums do not match'); + } + $attachment->decodeTnef($buffer); + + $list = file($listFile, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES); + $this->assertEquals(count($list), count($attachment->files)); + $decodedFiles = array_map(function(TNEFFileBase$file) {return $file->getName();}, $attachment->files); + + $withoutRtf = array_filter($list, array($this, "endsWithRtf")); + $withoutRtfdecoded = array_filter($decodedFiles, array($this, "endsWithRtf")); + + $rtfs = array_diff($withoutRtf, $list); + $rtfsDecoded = array_diff($withoutRtfdecoded, $decodedFiles); + + $this->assertEquals(count($rtfs), count($rtfsDecoded)); + + sort($withoutRtfdecoded); + sort($withoutRtf); + $this->assertEquals($withoutRtf, $withoutRtfdecoded); + } + + public function tnefFileProvider() { + $tnefFiles = glob(dirname(__FILE__) . "/testfiles/*.tnef"); + $result = []; + foreach ($tnefFiles as $tnefFile) { + $pathinfo = pathinfo($tnefFile); + $listFile = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.list'; + + $result[] = [ + $tnefFile, + file_exists($listFile) ? $listFile : null, + ]; } + + return $result; } private function endsWithRtf($value) { - try{ + try { return explode('.', $value)[1] != 'rtf'; } catch (Throwable $e) { return false; diff --git a/test/testfiles/invalid-checksum.tnef b/test/testfiles/invalid-checksum.tnef new file mode 100644 index 0000000..f1d4741 Binary files /dev/null and b/test/testfiles/invalid-checksum.tnef differ