Server IP : 184.154.167.98 / Your IP : 3.144.224.116 Web Server : Apache System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : puertode ( 1767) PHP Version : 7.2.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/puertode/public_html/mesa/include/ |
Upload File : |
<?php /********************************************************************* class.tnefparse.php Parser library and data objects for Microsoft TNEF (Transport Neutral Encapsulation Format) encoded email attachments. Jared Hancock <jared@osticket.com> Peter Rotich <peter@osticket.com> Copyright (c) 2006-2014 osTicket http://www.osticket.com This algorithm based on a similar project; however the original code did not process the HTML body of the message, nor did it properly handle the Microsoft Unicode encoding found in the attributes. * The Horde's class allows MS-TNEF data to be displayed. * * The TNEF rendering is based on code by: * Graham Norbury <gnorbury@bondcar.com> * Original design by: * Thomas Boll <tb@boll.ch>, Mark Simpson <damned@world.std.com> * * Copyright 2002-2010 The Horde Project (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * * @author Jan Schneider <jan@horde.org> * @author Michael Slusarz <slusarz@horde.org> * @package Horde_Compress Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ class TnefException extends Exception {} /** * References: * http://download.microsoft.com/download/1/D/0/1D0C13E1-2961-4170-874E-FADD796200D9/%5BMS-OXTNEF%5D.pdf * http://msdn.microsoft.com/en-us/library/ee160597(v=exchg.80).aspx * http://sourceforge.net/apps/trac/mingw-w64/browser/trunk/mingw-w64-headers/include/tnef.h?rev=3952 */ class TnefStreamReader implements Iterator { const SIGNATURE = 0x223e9f78; var $pos = 0; var $length = 0; var $streams = array(); var $current = true; var $options = array( 'checksum' => true, ); function __construct($stream, $options=array()) { if (is_array($options)) $this->options += $options; $this->push($stream); // Read header if (self::SIGNATURE != ($S = $this->_geti(32))) throw new TnefException(sprintf("%08x: Invalid signature magic", $S)); $this->_geti(16); // Attach key $this->next(); // Process first block } protected function push(&$stream) { $this->streams[] = array($this->stream, $this->pos, $this->length); $this->stream = &$stream; $this->pos = 0; $this->length = strlen($stream); } protected function pop() { list($this->stream, $this->pos, $this->length) = array_pop($this->streams); } protected function _geti($bits) { $bytes = $bits / 8; switch($bytes) { case 1: $value = ord($this->stream[$this->pos]); break; case 2: $value = unpack('vval', substr($this->stream, $this->pos, 2)); $value = $value['val']; break; case 4: $value = unpack('Vval', substr($this->stream, $this->pos, 4)); $value = $value['val']; break; } $this->pos += $bytes; return $value; } protected function _getx($bytes) { $value = substr($this->stream, $this->pos, $bytes); $this->pos += $bytes; return $value; } protected function skip($bytes) { $this->pos += $bytes; } function check($block) { $sum = 0; $bytes = strlen($block['data']); $bs = 1024; for ($i=0; $i < $bytes; $i+=$bs) { $b = unpack('C*', substr($block['data'], $i, min($bs, $bytes-$i))); $sum += array_sum($b); $sum = $sum % 65536; } if ($block['checksum'] != $sum) throw new TnefException(sprintf('Corrupted block. %04x: Invalid checksum, expected %04x', $sum, $block['checksum'])); } function next() { if ($this->length - $this->pos < 11) { $this->current = false; return; } $this->current = array( 'level' => $this->_geti(8), 'type' => $this->_geti(32), 'length' => $length = $this->_geti(32), 'data' => $this->_getx($length), 'checksum' => $this->_geti(16) ); if ($this->options['checksum']) $this->check($this->current); } function current() { return $this->current; } function key() { return $this->current['type']; } function valid() { return (bool) $this->current; } function rewind() { // Skip signature and attach-key $this->pos = 6; } } /** * References: * http://msdn.microsoft.com/en-us/library/ee179447(v=exchg.80).aspx */ class TnefAttribute { // Encapsulated Attributes const AlternateRecipientAllowed = 0x0002; const AutoForwarded = 0x0005; const Importance = 0x0017; const MessageClass = 0x001a; const OriginatorDeliveryReportRequested = 0x0023; const Priority = 0x0026; const ReadReceiptRequested = 0x0029; const Sensitivity = 0x0036; const ClientSubmitTime = 0x0039; const ReceivedByEntryId = 0x003f; const ReceivedByName = 0x0040; const ReceivedRepresentingEntryId = 0x0043; const RecevedRepresentingName = 0x0044; const MessageSubmissionId = 0x0047; const ReceivedBySearchKey = 0x0051; const ReceivedRepresentingSearchKey = 0x0052; const MessageToMe = 0x0057; const MessgeCcMe = 0x0058; const ConversionTopic = 0x0070; const ConversationIndex = 0x0071; const ReceivedByAddressType = 0x0075; const ReceivedByEmailAddress = 0x0076; const TnefCorrelationKey = 0x007f; const SenderName = 0x0c1a; const HasAttachments = 0x0e1b; const NormalizedSubject = 0x0e1d; const AttachSize = 0x0e20; const AttachNumber = 0x0e21; const Body = 0x1000; const RtfSyncBodyCrc = 0x1006; const RtfSyncBodyCount = 0x1007; const RtfSyncBodyTag = 0x1008; const RtfCompressed = 0x1009; const RtfSyncPrefixCount = 0x1010; const RtfSyncTrailingCount = 0x1011; const BodyHtml = 0x1013; const BodyContentId = 0x1014; const NativeBody = 0x1016; const InternetMessageId = 0x1035; const IconIndex = 0x1080; const ImapCachedMsgsize = 0x10f0; const UrlCompName = 0x10f3; const AttributeHidden = 0x10f4; const AttributeSystem = 0x10f5; const AttributeReadOnly = 0x10f6; const CreationTime = 0x3007; const LastModificationTime = 0x3008; const AttachDataBinary = 0x3701; const AttachEncoding = 0x3702; const AttachExtension = 0x3703; const AttachFilename = 0x3704; const AttachLongFilename = 0x3707; const AttachPathname = 0x3708; const AttachTransportName = 0x370c; const AttachMimeTag = 0x370e; # Mime content-type const AttachContentId = 0x3712; const AttachmentCharset = 0x371b; const InternetCodepage = 0x3fde; const MessageLocaleId = 0x3ff1; const CreatorName = 0x3ff8; const CreatorEntryId = 0x3ff9; const LastModifierName = 0x3ffa; const LastModifierEntryId = 0x3ffb; const MessageCodepage = 0x3ffd; const SenderFlags = 0x4019; const SentRepresentingFlags = 0x401a; const ReceivedByFlags = 0x401b; const ReceivedRepresentingFlags = 0x401c; const SenderSimpleDisplayName = 0x4030; const SentRepresentingSimpleDisplayName = 0x4031; # NOTE: The M$ specification gives ambiguous values for this property const ReceivedRepresentingSimpleDisplayName = 0x4034; const CreatorSimpleDisplayName = 0x4038; const LastModifierSimpleDisplayName = 0x4039; const ContentFilterSpamConfidenceLevel = 0x4076; const MessageEditorFormat = 0x5909; static function getName($code) { static $prop_codes = false; if (!$prop_codes) { $R = new ReflectionClass(get_class()); $prop_codes = array_flip($R->getConstants()); } return $prop_codes[$code]; } } class TnefAttributeStreamReader extends TnefStreamReader { var $count = 0; const TypeUnspecified = 0x0; const TypeNull = 0x0001; const TypeInt16 = 0x0002; const TypeInt32 = 0x0003; const TypeFlt32 = 0x0004; const TypeFlt64 = 0x0005; const TypeCurency = 0x0006; const TypeAppTime = 0x0007; const TypeError = 0x000a; const TypeBoolean = 0x000b; const TypeObject = 0x000d; const TypeInt64 = 0x0014; const TypeString8 = 0x001e; const TypeUnicode = 0x001f; const TypeSystime = 0x0040; const TypeCLSID = 0x0048; const TypeBinary = 0x0102; const MAPI_NAMED_TYPE_ID = 0x0000; const MAPI_NAMED_TYPE_STRING = 0x0001; const MAPI_MV_FLAG = 0x1000; function __construct($stream) { $this->push($stream); /* Number of attributes. */ $this->count = $this->_geti(32); } function valid() { return $this->count && $this->current; } function rewind() { $this->pos = 4; } /** * Read a single typed value from the current input stream. The type is * a 16-bit number receved as an argument which should be one of the * Type* constants defined in this class. * * According to the TNEF spec, all types regardless of their actual * size, must be rounded up in size to the next multiple of four (4) * bytes. Therefore 16-bit values and a strings will need to have extra * padding consumed to keep the stream on track. */ protected function readPhpValue($type) { switch ($type) { case self::TypeUnspecified: case self::TypeNull: case self::TypeError: return null; case self::TypeInt16: // Signed 16-bit value = INT16. $int16 = unpack('v', $this->_getx(4)); $sign = $int16 & 0x8000; if ($sign) // Use two's compliment $int16 = - ((~$int16 & 0xFFFF) + 1); return $int16; case self::TypeInt32: // Signed 32-bit value = INT32. // FIXME: Convert to signed value return $this->_geti(32); case self::TypeBoolean: // 16-bit Boolean (non-zero = TRUE) list($bool) = unpack('v', $this->_getx(4)); return 0 != $bool; case self::TypeFlt32: // Signed 32-bit floating point= FLOAT. list($f) = unpack('f', $this->_getx(4)); return $f; case self::TypeFlt64: // 64-bit floating point= DOUBLE. list($d) = unpack('d', $this->_getx(8)); return $d; case self::TypeCurency: // Signed 64-bit int = OLE CURRENCY type. // FIXME: Convert to PHP double return $this->_getx(8); case self::TypeInt64: // 8-byte signed integer= INT64. $x = $this->_getx(8); if (phpversion() >= '5.6.3') list($x) = unpack('P', $x); return $x; case self::TypeAppTime: list($d) = unpack('d', $this->_getx(8)); // Application time= OLE DATE type. // Thanks, http://stackoverflow.com/a/10443946/1025836 // Convert to UNIX timestamp, UTC timezone is assumed return ($d - 25569) * 86400; case self::TypeSystime: $a = unpack('Vl/Vh', $this->_getx(8)); // return FileTimeToU64(f) / 10000000 - 11644473600 $ft = ($a['l'] / 10000000.0) + ($a['h'] * 429.4967296); return $ft - 11644473600; case self::TypeString8: case self::TypeUnicode: case self::TypeBinary: $length = $this->_geti(32); /* Pad to next 4 byte boundary. */ $datalen = $length + ((4 - ($length % 4)) % 4); // Chomp null terminator if ($type == self::TypeString8) --$length; elseif ($type == self::TypeUnicode) $length -= 2; /* Read and truncate to length. */ $text = substr($this->_getx($datalen), 0, $length); if ($type == self::TypeUnicode) { // TNEF spec says encoding is UTF-16LE $text = Charset::utf8($text, 'UTF-16LE'); } return $text; case self::TypeObject: $length = $this->_geti(32); $oid = $this->_getx(16); if (bin2hex($text) == "0703020000000000c000000000000046") { // TODO: Create stream parser for embedded TNEF stream } $this->skip($length - 16); $this->skip((4 - ($length % 4)) % 4); return null; case self::TypeCLSID: return $this->_getx(16); default: throw new TnefException(sprintf('0x%04x: Bad data type', $type)); } } function next() { if ($this->count <= 0) { return $this->current = false; } $this->count--; $have_mval = false; $named_id = $value = null; $attr_type = $this->_geti(16); $attr_name = $this->_geti(16); $data_type = $attr_type & ~self::MAPI_MV_FLAG; if (($attr_type & self::MAPI_MV_FLAG) != 0 // These are a "special case of multi-value attributes with // num_values=1 || in_array($attr_type, array( self::TypeUnicode, self::TypeString8, self::TypeBinary, self::TypeObject)) ) { $have_mval = true; } if (($attr_name >= 0x8000) && ($attr_name < 0xFFFE)) { $this->_getx(16); $named_type = $this->_geti(32); switch ($named_type) { case self::MAPI_NAMED_TYPE_ID: $named_id = $this->_geti(32); break; case self::MAPI_NAMED_TYPE_STRING: $attr_name = 0x9999; $named_id = $this->readPhpValue(self::TypeUnicode); break; } } if (!$have_mval) { $value = $this->readPhpValue($data_type); } else { $value = array(); $k = $this->_geti(32); for ($i=0; $i < $k; $i++) $value[] = $this->readPhpValue($data_type); } if (is_array($value) && ($attr_type & self::MAPI_MV_FLAG) == 0) $value = $value[0]; $this->current = array( 'type' => $attr_type, 'name' => $attr_name, 'named_id' => $named_id, 'value' => $value, ); } function key() { return $this->current['name']; } } class TnefStreamParser { const LVL_MESSAGE = 0x01; const LVL_ATTACHMENT = 0x02; const attTnefVersion = 0x89006; const attAttachData = 0x6800f; const attAttachTransportFilename = 0x18010; const attAttachRendData = 0x69002; const attAttachment = 0x69005; const attMsgProps = 0x69003; const attRecipTable = 0x69004; const attOemCodepage = 0x69007; // Message-level attributes const idMessageClass = 0x78008; const idSenderEntryId = 0x8000; # From const idSubject = 0x18004; # Subject const idClientSubmitTime = 0x38004; const idMessageDeliveryTime = 0x38005; const idMessageStatus = 0x68007; const idMessageID = 0x18009; # Message-Id const idConversationID = 0x1800b; const idBody = 0x2800c; # Body const idImportance = 0x4800d; # Priority const idLastModificationTime = 0x38020; const idOriginalMessageClass = 0x70600; const idReceivedRepresentingEmailAddress = 0x60000; const idSentRepresentingEmailAddress = 0x60001; const idStartDate = 0x030006; const idEndDate = 0x30007; const idOwnerAppointmentId = 0x50008; const idResponseRequested = 0x40009; function __construct($stream) { $this->stream = new TnefStreamReader($stream); } function getMessage() { $msg = new TnefMessage(); foreach ($this->stream as $type=>$info) { switch($type) { case self::attTnefVersion: // Ignored (for now) break; case self::attOemCodepage: $cp = unpack("Vpri/Vsec", $info['data']); $msg->_set('OemCodepage', $cp['pri']); break; // Message level attributes case self::idMessageClass: $msg->_set('MessageClass', $info['data']); break; case self::idMessageID: $msg->_set('MessageId', $info['data']); break; case self::idSubject: $msg->_set('Subject', $info['data']); break; case self::attMsgProps: // Message properties (includig body) $msg->_setProperties( new TnefAttributeStreamReader($info['data'])); break; // Attachments case self::attAttachRendData: // Marks the start of an attachment $attach = $msg->pushAttachment(); //$attach->_setRenderingData(); break; case self::attAttachment: $attach->_setProperties( new TnefAttributeStreamReader($info['data'])); break; case self::attAttachTransportFilename: $attach->_setFilename(rtrim($info['data'], "\x00")); break; case self::attAttachData: $attach->_setData($info['data']); $attach->_setDataSize($info['length']); break; } } return $msg; } } abstract class AbstractTnefObject { function _setProperties($propReader) { foreach ($propReader as $prop=>$info) { if ($tag = TnefAttribute::getName($prop)) $this->{$tag} = $info['value']; elseif ($prop == 0x9999) // Extended, "named" attribute $this->{$info['named_id']} = $info['value']; } } function _set($prop, $value) { $this->{$prop} = $value; } } class TnefMessage extends AbstractTnefObject { var $attachments = array(); function pushAttachment() { $new = new TnefAttachment(); $this->attachments[] = $new; return $new; } function getBody($type='text/html', $encoding=false) { // First select the body switch ($type) { case 'text/html': $body = $this->BodyHtml; break; default: return false; } // Figure out the source encoding (ยง5.1.2) $charset = false; if (@$this->OemCodepage) $charset = 'cp'.$this->OemCodepage; elseif (@$this->InternetCodepage) $charset = 'cp'.$this->InternetCodepage; // Transcode it if ($encoding && $charset) $body = Charset::transcode($body, $charset, $encoding); return $body; } } class TnefAttachment extends AbstractTnefObject { function _setFilename($data) { $this->TransportFilename = $data; } function _setData($data) { $this->Data = $data; } function _setDataSize($size) { $this->DataSize = $size; } function getData() { if (isset($this->Data)) return $this->Data; elseif (isset($this->AttachDataBinary)) return $this->AttachDataBinary; } function _setRenderingData($data) { // Pass } function getType() { return $this->AttachMimeTag; } function getName() { if (isset($this->AttachLongFilename)) return basename($this->AttachLongFilename); elseif (isset($this->AttachFilename)) return $this->AttachFilename; elseif (isset($this->AttachTransportName)) return $this->AttachTransportName; else return $this->TransportFilename; } }