diff --git a/phpunit.xml b/phpunit.xml
index 1101cbf..3621538 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,25 +1,17 @@
-
-
-
-
- test
-
-
-
-
-
- src/
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+ test
+
+
+
+
+
+
+
+ src/
+
+
+
diff --git a/src/TNEFDecoder/TNEFAttachment.php b/src/TNEFDecoder/TNEFAttachment.php
index f868912..55f51b1 100644
--- a/src/TNEFDecoder/TNEFAttachment.php
+++ b/src/TNEFDecoder/TNEFAttachment.php
@@ -111,15 +111,17 @@ function getBodyElements()
return $this->body;
}
- function decodeTnef(&$buffer)
+ function decodeTnef($data)
{
+ $buffer = new TNEFBuffer($data);
+
$tnef_signature = tnef_geti32($buffer);
if ($tnef_signature == TNEF_SIGNATURE) {
$tnef_key = tnef_geti16($buffer);
if ($this->debug)
tnef_log(sprintf("Signature: 0x%08x\nKey: 0x%04x\n", $tnef_signature, $tnef_key));
- while (strlen($buffer) > 0) {
+ while ($buffer->getRemainingBytes() > 0) {
$lvl_type = tnef_geti8($buffer);
switch ($lvl_type) {
@@ -133,7 +135,7 @@ function decodeTnef(&$buffer)
default:
if ($this->debug) {
- $len = strlen($buffer);
+ $len = $buffer->getRemainingBytes();
if ($len > 0)
tnef_log("Invalid file format! Unknown Level $lvl_type. Rest=$len");
}
@@ -155,7 +157,7 @@ function decodeTnef(&$buffer)
$this->files[$i]->setMessageCodePage($code_page);
}
- function tnef_decode_attribute(&$buffer)
+ function tnef_decode_attribute(TNEFBuffer $buffer)
{
$attribute = tnef_geti32($buffer); // attribute if
$length = tnef_geti32($buffer); // length
@@ -180,13 +182,13 @@ function tnef_decode_attribute(&$buffer)
case TNEF_AMAPIATTRS:
if ($this->debug)
tnef_log("mapi attrs");
- $this->extract_mapi_attrs($value);
+ $this->extract_mapi_attrs(new TNEFBuffer($value));
break;
case TNEF_AMAPIPROPS:
if ($this->debug)
tnef_log("mapi props");
- $this->extract_mapi_attrs($value);
+ $this->extract_mapi_attrs(new TNEFBuffer($value));
break;
case TNEF_AMCLASS:
@@ -208,14 +210,14 @@ function tnef_decode_attribute(&$buffer)
}
}
- function extract_mapi_attrs(&$buffer)
+ function extract_mapi_attrs(TNEFBuffer $buffer)
{
$number = tnef_geti32($buffer); // number of attributes
$props = 0;
$ended = 0;
- while ((strlen($buffer) > 0) && ($props < $number) && (!$ended))
+ while (($buffer->getRemainingBytes() > 0) && ($props < $number) && (!$ended))
{
$props++;
$value = '';
@@ -225,7 +227,7 @@ function extract_mapi_attrs(&$buffer)
$num_multivalues = 1;
$attr_type = tnef_geti16($buffer);
$attr_name = tnef_geti16($buffer);
-
+
if (($attr_type & TNEF_MAPI_MV_FLAG) != 0)
{
if ($this->debug)
@@ -346,7 +348,7 @@ function extract_mapi_attrs(&$buffer)
case TNEF_MAPI_ATTACH_DATA:
if ($this->debug)
tnef_log("MAPI Found nested attachment. Processing new one.");
- tnef_getx(16, $value); // skip the next 16 bytes (unknown data)
+ tnef_getx(16, $buffer); // skip the next 16 bytes (unknown data)
$att = new TNEFAttachment($this->debug, $this->validateChecksum);
$att->decodeTnef($value);
$this->attachments[] = $att;
@@ -378,14 +380,14 @@ function extract_mapi_attrs(&$buffer)
}
if (($this->debug) && ($ended))
{
- $len = strlen($buffer);
+ $len = $buffer->getRemainingBytes();
for ($cnt = 0; $cnt < $len; $cnt++)
{
- $ord = ord($buffer[$cnt]);
+ $ord = tnef_geti8($buffer);
if ($ord == 0)
$char = "";
else
- $char = $buffer[$cnt];
+ $char = chr($ord);
tnef_log(sprintf("Char Nr. %6d = 0x%02x = '%s'", $cnt, $ord, $char));
}
}
diff --git a/src/TNEFDecoder/TNEFBuffer.php b/src/TNEFDecoder/TNEFBuffer.php
new file mode 100644
index 0000000..5b2eb41
--- /dev/null
+++ b/src/TNEFDecoder/TNEFBuffer.php
@@ -0,0 +1,46 @@
+
+ * Copyright (c) 2003 Bernd Wiegmann
+ * Copyright (c) 2002 Graham Norburys
+ *
+ * Licensed under the GNU GPL. For full terms see the file COPYING.
+ *
+ * @package plugins
+ * @subpackage tnef_decoder
+ *
+ */
+
+class TNEFBuffer
+{
+ private string $data;
+ private int $offset;
+
+ function __construct(string $data)
+ {
+ $this->data = $data;
+ $this->offset = 0;
+ }
+
+ function getBytes(int $numBytes): ?string
+ {
+ if ($this->getRemainingBytes() < $numBytes) {
+ $this->offset = strlen($this->data);
+ return null;
+ }
+
+ $this->offset += $numBytes;
+ return substr($this->data, $this->offset - $numBytes, $numBytes);
+ }
+
+ function getRemainingBytes(): int
+ {
+ return strlen($this->data) - $this->offset;
+ }
+}
+
+
+
diff --git a/src/TNEFDecoder/TNEFDate.php b/src/TNEFDecoder/TNEFDate.php
index d2cf51d..af52020 100644
--- a/src/TNEFDecoder/TNEFDate.php
+++ b/src/TNEFDecoder/TNEFDate.php
@@ -24,7 +24,7 @@ class TNEFDate
var $minute;
var $second;
- function setTnefBuffer($buffer)
+ function setTnefBuffer(TNEFBuffer $buffer)
{
$this->year = tnef_geti16($buffer);
$this->month = tnef_geti16($buffer);
diff --git a/src/TNEFDecoder/TNEFFile.php b/src/TNEFDecoder/TNEFFile.php
index 2815159..09ab3b1 100644
--- a/src/TNEFDecoder/TNEFFile.php
+++ b/src/TNEFDecoder/TNEFFile.php
@@ -37,7 +37,6 @@ function receiveTnefAttribute($attribute, $value, $length)
// filename
//
case TNEF_AFILENAME:
-
// strip path
//
if (($pos = strrpos($value, '/')) !== FALSE)
@@ -45,12 +44,13 @@ function receiveTnefAttribute($attribute, $value, $length)
else
$this->name = $value;
+ // Strip trailing null bytes if present
+ $this->name = trim($this->name);
break;
-
// code page
//
case TNEF_AOEMCODEPAGE:
- $this->code_page = tnef_geti16($value);
+ $this->code_page = tnef_geti16(new TNEFBuffer($value));
break;
// the attachment itself
@@ -67,11 +67,11 @@ function receiveTnefAttribute($attribute, $value, $length)
case TNEF_AATTACHCREATEDATE:
$this->created = new TNEFDate();
- $this->created->setTnefBuffer($value);
+ $this->created->setTnefBuffer(new TNEFBuffer($value));
case TNEF_AATTACHMODDATE:
$this->modified = new TNEFDate();
- $this->modified->setTnefBuffer($value);
+ $this->modified->setTnefBuffer(new TNEFBuffer($value));
break;
}
}
@@ -84,7 +84,6 @@ function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unico
// used in preference to AFILENAME value
//
case TNEF_MAPI_ATTACH_LONG_FILENAME:
-
// strip path
//
if (($pos = strrpos($value, '/')) !== FALSE)
@@ -93,7 +92,6 @@ function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unico
$this->name = $value;
if ($is_unicode) $this->name_is_unicode = TRUE;
-
break;
// Is this ever set, and what is format?
@@ -101,9 +99,9 @@ function receiveMapiAttribute($attr_type, $attr_name, $value, $length, $is_unico
case TNEF_MAPI_ATTACH_MIME_TAG:
$type0 = $type1 = '';
$mime_type = explode('/', $value, 2);
- if (!empty($mime_type[0]))
+ if (!empty($mime_type[0]))
$type0 = $mime_type[0];
- if (!empty($mime_type[1]))
+ if (!empty($mime_type[1]))
$type1 = $mime_type[1];
$this->type = "$type0/$type1";
if ($is_unicode) {
diff --git a/src/TNEFDecoder/TNEFFileBase.php b/src/TNEFDecoder/TNEFFileBase.php
index 0d2d5b3..9379dff 100644
--- a/src/TNEFDecoder/TNEFFileBase.php
+++ b/src/TNEFDecoder/TNEFFileBase.php
@@ -25,7 +25,7 @@ class TNEFFileBase
var $created;
var $modified;
var $debug;
-
+
function __construct($debug)
{
$this->name = 'Untitled';
diff --git a/src/TNEFDecoder/TNEFFileRTF.php b/src/TNEFDecoder/TNEFFileRTF.php
index 68863bc..3394751 100644
--- a/src/TNEFDecoder/TNEFFileRTF.php
+++ b/src/TNEFDecoder/TNEFFileRTF.php
@@ -20,14 +20,14 @@ class TNEFFileRTF extends TNEFFileBase
const MAX_DICT_SIZE = 4096;
const INIT_DICT_SIZE = 207;
- function __construct($debug, $buffer)
+ function __construct($debug, $data)
{
parent::__construct($debug);
$this->type = "application/rtf";
$this->name = "EmbeddedRTF.rtf";
$this->debug = $debug;
- $this->decode_crtf($buffer);
+ $this->decode_crtf(new TNEFBuffer($data));
}
function getSize()
@@ -35,7 +35,7 @@ function getSize()
return $this->size;
}
- function decode_crtf(&$buffer)
+ function decode_crtf(TNEFBuffer $buffer)
{
$size_compressed = tnef_geti32($buffer);
$this->size = tnef_geti32($buffer);
@@ -61,8 +61,10 @@ function decode_crtf(&$buffer)
}
}
- function uncompress(&$data)
+ function uncompress(TNEFBuffer $buffer)
{
+ $data = tnef_getx($buffer->getRemainingBytes(), $buffer);
+
$preload = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
$length_preload = strlen($preload);
$init_dict = [];
@@ -71,7 +73,7 @@ function uncompress(&$data)
}
$init_dict = array_merge($init_dict, array_fill(count($init_dict), self::MAX_DICT_SIZE - $length_preload, ' '));
$write_offset = self::INIT_DICT_SIZE;
- $output_buffer = '';
+ $output_buffer = [];
$end = false;
$in = 0;
$l = strlen($data);
@@ -93,7 +95,7 @@ function uncompress(&$data)
for ($step = 0; $step < $actual_length; $step++) {
$read_offset = ($offset + $step) % self::MAX_DICT_SIZE;
$char = $init_dict[$read_offset];
- $output_buffer .= $char;
+ $output_buffer[] = $char;
$init_dict[$write_offset] = $char;
$write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE;
}
@@ -102,13 +104,13 @@ function uncompress(&$data)
break;
}
$val = $data[$in++];
- $output_buffer .= $val;
+ $output_buffer[] = $val;
$init_dict[$write_offset] = $val;
$write_offset = ($write_offset + 1) % self::MAX_DICT_SIZE;
}
}
}
- $this->content = $output_buffer;
+ $this->content = new TNEFBuffer(implode('', $output_buffer));
}
}
diff --git a/src/TNEFDecoder/TNEFMailinfo.php b/src/TNEFDecoder/TNEFMailinfo.php
index 35985df..1b83523 100644
--- a/src/TNEFDecoder/TNEFMailinfo.php
+++ b/src/TNEFDecoder/TNEFMailinfo.php
@@ -60,6 +60,8 @@ function getDateSent()
function receiveTnefAttribute($attribute, $value, $length)
{
+ $value = new TNEFBuffer($value);
+
switch($attribute)
{
case TNEF_AOEMCODEPAGE:
@@ -67,7 +69,7 @@ function receiveTnefAttribute($attribute, $value, $length)
break;
case TNEF_ASUBJECT:
- $this->subject = substr($value, 0, $length - 1);
+ $this->subject = tnef_getx($length - 1, $value);
break;
case TNEF_ADATERECEIVED:
diff --git a/src/TNEFDecoder/TNEFvCard.php b/src/TNEFDecoder/TNEFvCard.php
index 9a5188e..e202610 100644
--- a/src/TNEFDecoder/TNEFvCard.php
+++ b/src/TNEFDecoder/TNEFvCard.php
@@ -66,7 +66,7 @@ function getCodePage()
{
if (empty($this->code_page))
return $this->message_code_page;
- else
+ else
return $this->code_page;
}
@@ -158,7 +158,7 @@ function receiveTnefAttribute($attribute, $value, $length)
// code page
//
case TNEF_AOEMCODEPAGE:
- $this->code_page = tnef_geti16($value);
+ $this->code_page = tnef_geti16(new TNEFBuffer($value));
break;
}
@@ -238,7 +238,7 @@ function evaluateTelefoneAttribute($attr_type, $attr_name, $value, $length)
tnef_log("Setting telefone '$telefone_key' to value '$value'");
}
}
-
+
return $rc;
}
@@ -296,7 +296,7 @@ function evaluateHomepageAttribute($attr_type, $attr_name, $value, $length)
tnef_log("Setting homepage '$homepage_key' to value '$value'");
}
}
-
+
return $rc;
}
diff --git a/src/TNEFDecoder/functions.php b/src/TNEFDecoder/functions.php
index 5dbc7df..b03a937 100644
--- a/src/TNEFDecoder/functions.php
+++ b/src/TNEFDecoder/functions.php
@@ -66,19 +66,9 @@ function extension_to_mime($extension)
* TNEF decoding helper function
*
*/
-function tnef_getx($size, &$buf)
+function tnef_getx($size, TNEFBuffer $buf)
{
- $value = NULL;
- $len = strlen($buf);
- if ($len >= $size)
- {
- $value = substr($buf, 0, $size);
- $buf = substr_replace($buf, '', 0, $size);
- }
- else
- substr_replace($buf, '', 0, $len);
-
- return $value;
+ return $buf->getBytes($size);
}
@@ -87,18 +77,14 @@ function tnef_getx($size, &$buf)
* TNEF decoding helper function
*
*/
-function tnef_geti8(&$buf)
+function tnef_geti8(TNEFBuffer $buf): int
{
- $value = NULL;
- $len = strlen($buf);
- if ($len >= 1) {
- $value = ord($buf[0]);
- $buf = substr_replace($buf, '', 0, 1);
+ $bytes = $buf->getBytes(1, $buf);
+ if ($bytes === null) {
+ return null;
}
- else
- substr_replace($buf, '', 0, $len);
- return $value;
+ return ord($bytes[0]);
}
@@ -107,18 +93,15 @@ function tnef_geti8(&$buf)
* TNEF decoding helper function
*
*/
-function tnef_geti16(&$buf)
+function tnef_geti16(TNEFBuffer $buf): int
{
- $value = NULL;
- $len = strlen($buf);
- if ($len >= 2) {
- $value = ord($buf[0])
- + (ord($buf[1]) << 8);
- $buf = substr_replace($buf, '', 0, 2);
- } else
- substr_replace($buf, '', 0, $len);
-
- return $value;
+ $bytes = $buf->getBytes(2, $buf);
+ if ($bytes === null) {
+ return null;
+ }
+
+ return ord($bytes[0])
+ + (ord($bytes[1]) << 8);
}
@@ -127,18 +110,15 @@ function tnef_geti16(&$buf)
* TNEF decoding helper function
*
*/
-function tnef_geti32(&$buf)
+function tnef_geti32(TNEFBuffer $buf): int
{
- $value = NULL;
- $len = strlen($buf);
- if ($len >= 4) {
- $value = ord($buf[0])
- + (ord($buf[1]) << 8)
- + (ord($buf[2]) << 16)
- + (ord($buf[3]) << 24);
- $buf = substr_replace($buf, '', 0, 4);
- } else
- substr_replace($buf, '', 0, $len);
-
- return $value;
+ $bytes = $buf->getBytes(4, $buf);
+ if ($bytes === null) {
+ return null;
+ }
+
+ return ord($bytes[0])
+ + (ord($bytes[1]) << 8)
+ + (ord($bytes[2]) << 16)
+ + (ord($bytes[3]) << 24);
}
diff --git a/test/TNEFAttachmentTest.php b/test/TNEFAttachmentTest.php
index 491de09..b79230c 100644
--- a/test/TNEFAttachmentTest.php
+++ b/test/TNEFAttachmentTest.php
@@ -52,7 +52,7 @@ public function testDecodeAuto($tnefFile, $listFile) {
$this->assertEquals($withoutRtf, $withoutRtfdecoded);
}
- public function tnefFileProvider() {
+ public static function tnefFileProvider() {
$tnefFiles = glob(dirname(__FILE__) . "/testfiles/*.tnef");
$result = [];
foreach ($tnefFiles as $tnefFile) {
@@ -69,11 +69,11 @@ public function tnefFileProvider() {
}
private function endsWithRtf($value) {
- try {
- return explode('.', $value)[1] != 'rtf';
- } catch (Throwable $e) {
+ $arr = explode('.', $value);
+ if (count($arr) < 2) {
return false;
}
+ return $arr[1] !== 'rtf';
}
}