diff --git a/include/class.mailparse.php b/include/class.mailparse.php
index 141d18f96419c0ffdc1fe9b9512ce74a48995ea6..f6c534245614d24dae343398bbf2490f034fcd32 100644
--- a/include/class.mailparse.php
+++ b/include/class.mailparse.php
@@ -17,6 +17,7 @@
 
 require_once(PEAR_DIR.'Mail/mimeDecode.php');
 require_once(PEAR_DIR.'Mail/RFC822.php');
+require_once(INCLUDE_DIR.'tnef_decoder.php');
 
 class Mail_Parse {
 
@@ -29,6 +30,8 @@ class Mail_Parse {
 
     var $charset ='UTF-8'; //Default charset.
 
+    var $tnef = false;      // TNEF encoded mail
+
     function Mail_parse($mimeMessage, $charset=null){
 
         $this->mime_message = $mimeMessage;
@@ -88,6 +91,17 @@ class Mail_Parse {
             }
         }
 
+        // Look for application/tnef attachment and process it
+        foreach ($this->struct->parts as $i=>$part) {
+            if (!$part->parts && $part->ctype_primary == 'application'
+                    && $part->ctype_secondary == 'ms-tnef') {
+                $tnef = new TnefStreamParser($part->body);
+                $this->tnef = $tnef->getMessage();
+                // No longer considered an attachment
+                unset($this->struct->parts[$i]);
+            }
+        }
+
         return (count($this->struct->headers) > 1);
     }
 
@@ -283,6 +297,10 @@ class Mail_Parse {
             }
         }
 
+        if ($this->tnef && !strcasecmp($ctypepart, 'text/html')
+                && ($content = $this->tnef->getBody('text/html', $this->charset)))
+            return $content;
+
         $data='';
         if($struct && $struct->parts && $recurse) {
             foreach($struct->parts as $i=>$part) {
@@ -299,6 +317,7 @@ class Mail_Parse {
     }
 
     function getAttachments($part=null){
+        $files=array();
 
         /* Consider this part as an attachment if
          *   * It has a Content-Disposition header
@@ -354,10 +373,22 @@ class Mail_Parse {
             return array($file);
         }
 
+        elseif ($this->tnef) {
+            foreach ($this->tnef->attachments as $at) {
+                $files[] = array(
+                    'cid' => @$at->AttachContentId ?: false,
+                    'data' => $at->Data,
+                    'type' => @$at->AttachMimeTag ?: false,
+                    'name' => $at->getName(),
+                    'encoding' => @$at->AttachEncoding ?: false,
+                );
+            }
+            return $files;
+        }
+
         if($part==null)
             $part=$this->getStruct();
 
-        $files=array();
         if($part->parts){
             foreach($part->parts as $k=>$p){
                 if($p && ($result=$this->getAttachments($p))) {
@@ -370,6 +401,11 @@ class Mail_Parse {
     }
 
     function getPriority(){
+        if ($this->tnef && isset($this->tnef->Importance))
+            // PidTagImportance is 0, 1, or 2
+            // http://msdn.microsoft.com/en-us/library/ee237166(v=exchg.80).aspx
+            return $this->tnef->Importance + 1;
+
         return Mail_Parse::parsePriority($this->getHeader());
     }
 
@@ -399,7 +435,10 @@ class Mail_Parse {
 
     function parse($rawemail) {
         $parser= new Mail_Parse($rawemail);
-        return ($parser && $parser->decode())?$parser:null;
+        if (!$parser->decode())
+            return null;
+
+        return $parser;
     }
 }
 
diff --git a/include/tnef_decoder.php b/include/tnef_decoder.php
new file mode 100644
index 0000000000000000000000000000000000000000..86059df5582b29da5be720f15cce99a3adde5f67
--- /dev/null
+++ b/include/tnef_decoder.php
@@ -0,0 +1,560 @@
+<?php
+/*********************************************************************
+    class.tnefparse.php
+
+    Parser library and data objects for Microsoft TNEF (Transport Neutral
+    Encapsulation Format) encoded email attachments.
+
+    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
+
+    Jared Hancock <jared@osticket.com>
+    Peter Rotich <peter@osticket.com>
+    Copyright (c)  2006-2013 osTicket
+    http://www.osticket.com
+
+    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;
+
+    function __construct($stream) {
+        $this->push($stream);
+
+        // Read header
+        if (self::SIGNATURE != $this->_geti(32))
+            throw new TnefException("Invalid signature");
+
+        $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;
+    }
+
+
+    function check($block) {
+        $bytes = unpack('*bb', $block['data']);
+        $sum = array_sum($bytes);
+        return $block['checksum'] == ($sum % 65535);
+    }
+
+    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)
+        );
+    }
+
+    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 AttachEncoding = 0x3702;
+    const AttachExtension = 0x3703;
+    const AttachFilename = 0x3704;
+    const AttachLongFilename = 0x3707;
+    const AttachPathname = 0x3708;
+    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 ContentFilterSpmnConfidenceLevel = 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);
+        $this->next();
+    }
+
+    function valid() {
+        return (bool) $this->current;
+    }
+
+    function rewind() {
+        $this->pos = 0;
+    }
+
+    protected function readPhpValue($type) {
+        switch ($type) {
+        case self::TypeUnspecified:
+        case self::TypeNull:
+        case self::TypeError:
+            return null;
+
+        case self::TypeInt16:
+            return $this->_geti(16);
+
+        case self::TypeInt32:
+            return $this->_geti(32);
+
+        case self::TypeBoolean:
+            return (bool) $this->_geti(32);
+
+        case self::TypeFlt32:
+            list($f) = unpack('f', $this->_getx(8));
+            return $f;
+
+        case self::TypeFlt64:
+            list($d) = unpack('d', $this->_getx(8));
+            return $d;
+
+        case self::TypeAppTime:
+        case self::TypeCurency:
+        case self::TypeInt64:
+            return $this->_getx(8);
+
+        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:
+        case self::TypeObject:
+            $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) {
+                $text = Format::encode($text, 'ucs2');
+            }
+
+            return $text;
+        }
+    }
+
+    function next() {
+        $this->count--;
+
+        if ($this->length - $this->pos < 12)
+            return $this->current = false;
+
+        $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::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($info['data']);
+                break;
+            case self::attAttachData:
+                $attach->_setData($info['data']);
+                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 = Format::encode($body, $charset, $encoding);
+
+        return $body;
+    }
+}
+
+class TnefAttachment extends AbstractTnefObject {
+    function _setFilename($data) {
+        $this->TransportFilename = $data;
+    }
+
+    function _setData($data) {
+        $this->Data = $data;
+    }
+
+    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;
+        else
+            return $this->TransportFilename;
+    }
+}