Overview

Packages

  • Compress

Classes

  • Horde_Compress
  • Horde_Compress_Base
  • Horde_Compress_Dbx
  • Horde_Compress_Exception
  • Horde_Compress_Gzip
  • Horde_Compress_Rar
  • Horde_Compress_Tar
  • Horde_Compress_Tnef
  • Horde_Compress_Translation
  • Horde_Compress_Zip
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Compress_Tnef class allows MS-TNEF data to be displayed.
  4:  *
  5:  * The TNEF rendering is based on code by:
  6:  *   Graham Norbury <gnorbury@bondcar.com>
  7:  * Original design by:
  8:  *   Thomas Boll <tb@boll.ch>, Mark Simpson <damned@world.std.com>
  9:  *
 10:  * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
 11:  *
 12:  * See the enclosed file COPYING for license information (LGPL). If you
 13:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 14:  *
 15:  * @author   Jan Schneider <jan@horde.org>
 16:  * @author   Michael Slusarz <slusarz@horde.org>
 17:  * @category Horde
 18:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 19:  * @package  Compress
 20:  */
 21: class Horde_Compress_Tnef extends Horde_Compress_Base
 22: {
 23:     const SIGNATURE = 0x223e9f78;
 24:     const LVL_MESSAGE = 0x01;
 25:     const LVL_ATTACHMENT = 0x02;
 26: 
 27:     const ASUBJECT = 0x88004;
 28:     const AMCLASS = 0x78008;
 29:     const ATTACHDATA = 0x6800f;
 30:     const AFILENAME = 0x18010;
 31:     const ARENDDATA = 0x69002;
 32:     const AMAPIATTRS = 0x69005;
 33:     const AVERSION = 0x89006;
 34: 
 35:     const MAPI_NULL = 0x0001;
 36:     const MAPI_SHORT = 0x0002;
 37:     const MAPI_INT = 0x0003;
 38:     const MAPI_FLOAT = 0x0004;
 39:     const MAPI_DOUBLE = 0x0005;
 40:     const MAPI_CURRENCY = 0x0006;
 41:     const MAPI_APPTIME = 0x0007;
 42:     const MAPI_ERROR = 0x000a;
 43:     const MAPI_BOOLEAN = 0x000b;
 44:     const MAPI_OBJECT = 0x000d;
 45:     const MAPI_INT8BYTE = 0x0014;
 46:     const MAPI_STRING = 0x001e;
 47:     const MAPI_UNICODE_STRING = 0x001f;
 48:     const MAPI_SYSTIME = 0x0040;
 49:     const MAPI_CLSID = 0x0048;
 50:     const MAPI_BINARY = 0x0102;
 51: 
 52:     const MAPI_ATTACH_LONG_FILENAME = 0x3707;
 53:     const MAPI_ATTACH_MIME_TAG = 0x370E;
 54: 
 55:     const MAPI_NAMED_TYPE_ID = 0x0000;
 56:     const MAPI_NAMED_TYPE_STRING = 0x0001;
 57:     const MAPI_MV_FLAG = 0x1000;
 58: 
 59:     /**
 60:      */
 61:     public $canDecompress = true;
 62: 
 63:     /**
 64:      * @return array  The decompressed data.
 65:      * @throws Horde_Compress_Exception
 66:      */
 67:     public function decompress($data, array $params = array())
 68:     {
 69:         $out = array();
 70: 
 71:         if ($this->_geti($data, 32) == self::SIGNATURE) {
 72:             $this->_geti($data, 16);
 73: 
 74:             while (strlen($data) > 0) {
 75:                 switch ($this->_geti($data, 8)) {
 76:                 case self::LVL_MESSAGE:
 77:                     $this->_decodeMessage($data);
 78:                     break;
 79: 
 80:                 case self::LVL_ATTACHMENT:
 81:                     $this->_decodeAttachment($data, $out);
 82:                     break;
 83:                 }
 84:             }
 85:         }
 86: 
 87:         return array_reverse($out);
 88:     }
 89: 
 90:     /**
 91:      * TODO
 92:      *
 93:      * @param string &$data  The data string.
 94:      * @param integer $bits  How many bits to retrieve.
 95:      *
 96:      * @return TODO
 97:      */
 98:     protected function _getx(&$data, $bits)
 99:     {
100:         $value = null;
101: 
102:         if (strlen($data) >= $bits) {
103:             $value = substr($data, 0, $bits);
104:             $data = substr_replace($data, '', 0, $bits);
105:         }
106: 
107:         return $value;
108:     }
109: 
110:     /**
111:      * TODO
112:      *
113:      * @param string &$data  The data string.
114:      * @param integer $bits  How many bits to retrieve.
115:      *
116:      * @return TODO
117:      */
118:     protected function _geti(&$data, $bits)
119:     {
120:         $bytes = $bits / 8;
121:         $value = null;
122: 
123:         if (strlen($data) >= $bytes) {
124:             $value = ord($data[0]);
125:             if ($bytes >= 2) {
126:                 $value += (ord($data[1]) << 8);
127:             }
128:             if ($bytes >= 4) {
129:                 $value += (ord($data[2]) << 16) + (ord($data[3]) << 24);
130:             }
131:             $data = substr_replace($data, '', 0, $bytes);
132:         }
133: 
134:         return $value;
135:     }
136: 
137:     /**
138:      * TODO
139:      *
140:      * @param string &$data      The data string.
141:      * @param string $attribute  TODO
142:      */
143:     protected function _decodeAttribute(&$data, $attribute)
144:     {
145:         /* Data. */
146:         $this->_getx($data, $this->_geti($data, 32));
147: 
148:         /* Checksum. */
149:         $this->_geti($data, 16);
150:     }
151: 
152:     /**
153:      * TODO
154:      *
155:      * @param string $data             The data string.
156:      * @param array &$attachment_data  TODO
157:      */
158:     protected function _extractMapiAttributes($data, &$attachment_data)
159:     {
160:         /* Number of attributes. */
161:         $number = $this->_geti($data, 32);
162: 
163:         while ((strlen($data) > 0) && $number--) {
164:             $have_mval = false;
165:             $num_mval = 1;
166:             $named_id = $value = null;
167:             $attr_type = $this->_geti($data, 16);
168:             $attr_name = $this->_geti($data, 16);
169: 
170:             if (($attr_type & self::MAPI_MV_FLAG) != 0) {
171:                 $have_mval = true;
172:                 $attr_type = $attr_type & ~self::MAPI_MV_FLAG;
173:             }
174: 
175:             if (($attr_name >= 0x8000) && ($attr_name < 0xFFFE)) {
176:                 $this->_getx($data, 16);
177:                 $named_type = $this->_geti($data, 32);
178: 
179:                 switch ($named_type) {
180:                 case self::MAPI_NAMED_TYPE_ID:
181:                     $named_id = $this->_geti($data, 32);
182:                     $attr_name = $named_id;
183:                     break;
184: 
185:                 case self::MAPI_NAMED_TYPE_STRING:
186:                     $attr_name = 0x9999;
187:                     $idlen = $this->_geti($data, 32);
188:                     $datalen = $idlen + ((4 - ($idlen % 4)) % 4);
189:                     $named_id = substr($this->_getx($data, $datalen), 0, $idlen);
190:                     break;
191:                 }
192:             }
193: 
194:             if ($have_mval) {
195:                 $num_mval = $this->_geti($data, 32);
196:             }
197: 
198:             switch ($attr_type) {
199:             case self::MAPI_SHORT:
200:                 $value = $this->_geti($data, 16);
201:                 break;
202: 
203:             case self::MAPI_INT:
204:             case self::MAPI_BOOLEAN:
205:                 for ($i = 0; $i < $num_mval; $i++) {
206:                     $value = $this->_geti($data, 32);
207:                 }
208:                 break;
209: 
210:             case self::MAPI_FLOAT:
211:             case self::MAPI_ERROR:
212:                 $value = $this->_getx($data, 4);
213:                 break;
214: 
215:             case self::MAPI_DOUBLE:
216:             case self::MAPI_APPTIME:
217:             case self::MAPI_CURRENCY:
218:             case self::MAPI_INT8BYTE:
219:             case self::MAPI_SYSTIME:
220:                 $value = $this->_getx($data, 8);
221:                 break;
222: 
223:             case self::MAPI_STRING:
224:             case self::MAPI_UNICODE_STRING:
225:             case self::MAPI_BINARY:
226:             case self::MAPI_OBJECT:
227:                 $num_vals = ($have_mval) ? $num_mval : $this->_geti($data, 32);
228:                 for ($i = 0; $i < $num_vals; $i++) {
229:                     $length = $this->_geti($data, 32);
230: 
231:                     /* Pad to next 4 byte boundary. */
232:                     $datalen = $length + ((4 - ($length % 4)) % 4);
233: 
234:                     if ($attr_type == self::MAPI_STRING) {
235:                         --$length;
236:                     }
237: 
238:                     /* Read and truncate to length. */
239:                     $value = substr($this->_getx($data, $datalen), 0, $length);
240:                 }
241:                 break;
242:             }
243: 
244:             /* Store any interesting attributes. */
245:             switch ($attr_name) {
246:             case self::MAPI_ATTACH_LONG_FILENAME:
247:                 /* Used in preference to AFILENAME value. */
248:                 $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $value);
249:                 $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']);
250:                 break;
251: 
252:             case self::MAPI_ATTACH_MIME_TAG:
253:                 /* Is this ever set, and what is format? */
254:                 $attachment_data[0]['type'] = preg_replace('/^(.*)\/.*/', '\1', $value);
255:                 $attachment_data[0]['type'] = str_replace("\0", '', $attachment_data[0]['type']);
256:                 $attachment_data[0]['subtype'] = preg_replace('/.*\/(.*)$/', '\1', $value);
257:                 $attachment_data[0]['subtype'] = str_replace("\0", '', $attachment_data[0]['subtype']);
258:                 break;
259:             }
260:         }
261:     }
262: 
263:     /**
264:      * TODO
265:      *
266:      * @param string &$data  The data string.
267:      */
268:     protected function _decodeMessage(&$data)
269:     {
270:         $this->_decodeAttribute($data, $this->_geti($data, 32));
271:     }
272: 
273:     /**
274:      * TODO
275:      *
276:      * @param string &$data            The data string.
277:      * @param array &$attachment_data  TODO
278:      */
279:     protected function _decodeAttachment(&$data, &$attachment_data)
280:     {
281:         $attribute = $this->_geti($data, 32);
282: 
283:         switch ($attribute) {
284:         case self::ARENDDATA:
285:             /* Marks start of new attachment. */
286:             $this->_getx($data, $this->_geti($data, 32));
287: 
288:             /* Checksum */
289:             $this->_geti($data, 16);
290: 
291:             /* Add a new default data block to hold details of this
292:                attachment. Reverse order is easier to handle later! */
293:             array_unshift($attachment_data, array('type'    => 'application',
294:                                                   'subtype' => 'octet-stream',
295:                                                   'name'    => 'unknown',
296:                                                   'stream'  => ''));
297:             break;
298: 
299:         case self::AFILENAME:
300:             /* Strip path. */
301:             $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $this->_getx($data, $this->_geti($data, 32)));
302:             $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']);
303: 
304:             /* Checksum */
305:             $this->_geti($data, 16);
306:             break;
307: 
308:         case self::ATTACHDATA:
309:             /* The attachment itself. */
310:             $length = $this->_geti($data, 32);
311:             $attachment_data[0]['size'] = $length;
312:             $attachment_data[0]['stream'] = $this->_getx($data, $length);
313: 
314:             /* Checksum */
315:             $this->_geti($data, 16);
316:             break;
317: 
318:         case self::AMAPIATTRS:
319:             $length = $this->_geti($data, 32);
320:             $value = $this->_getx($data, $length);
321: 
322:             /* Checksum */
323:             $this->_geti($data, 16);
324:             $this->_extractMapiAttributes($value, $attachment_data);
325:             break;
326: 
327:         default:
328:             $this->_decodeAttribute($data, $attribute);
329:         }
330:     }
331: 
332: }
333: 
API documentation generated by ApiGen