]> git.donarmstrong.com Git - roundcube.git/blob - program/lib/tnef_decoder.inc
Imported Upstream version 0.3.1
[roundcube.git] / program / lib / tnef_decoder.inc
1 <?php
2 /*
3  * tnef_decoder.php
4  *  Graham Norbury <gnorbury@bondcar.com>
5  *  (c) 2002 (GNU GPL - see ../../COPYING)
6  *
7  *  Functions for decoding TNEF attachments in native PHP
8  *
9  *  Adapted from original designs by:
10  *    Thomas Boll <tb@boll.ch>             [tnef.c]
11  *    Mark Simpson <damned@world.std.com>  [tnef-1.1.1]
12  *
13  */
14
15 define("TNEF_SIGNATURE",      0x223e9f78);
16 define("TNEF_LVL_MESSAGE",    0x01);
17 define("TNEF_LVL_ATTACHMENT", 0x02);
18
19 define("TNEF_STRING", 0x00010000);
20 define("TNEF_TEXT",   0x00020000);
21 define("TNEF_BYTE",   0x00060000);
22 define("TNEF_WORD",   0x00070000);
23 define("TNEF_DWORD",  0x00080000);
24
25 define("TNEF_ASUBJECT",   TNEF_DWORD  | 0x8004);
26 define("TNEF_AMCLASS",    TNEF_WORD   | 0x8008);
27 define("TNEF_BODYTEXT",   TNEF_TEXT   | 0x800c);
28 define("TNEF_ATTACHDATA", TNEF_BYTE   | 0x800f);
29 define("TNEF_AFILENAME",  TNEF_STRING | 0x8010);
30 define("TNEF_ARENDDATA",  TNEF_BYTE   | 0x9002);
31 define("TNEF_AMAPIATTRS", TNEF_BYTE   | 0x9005);
32 define("TNEF_AVERSION",   TNEF_DWORD  | 0x9006);
33
34 define("TNEF_MAPI_NULL",           0x0001);
35 define("TNEF_MAPI_SHORT",          0x0002);
36 define("TNEF_MAPI_INT",            0x0003);
37 define("TNEF_MAPI_FLOAT",          0x0004);
38 define("TNEF_MAPI_DOUBLE",         0x0005);
39 define("TNEF_MAPI_CURRENCY",       0x0006);
40 define("TNEF_MAPI_APPTIME",        0x0007);
41 define("TNEF_MAPI_ERROR",          0x000a);
42 define("TNEF_MAPI_BOOLEAN",        0x000b);
43 define("TNEF_MAPI_OBJECT",         0x000d);
44 define("TNEF_MAPI_INT8BYTE",       0x0014);
45 define("TNEF_MAPI_STRING",         0x001e);
46 define("TNEF_MAPI_UNICODE_STRING", 0x001f);
47 define("TNEF_MAPI_SYSTIME",        0x0040);
48 define("TNEF_MAPI_CLSID",          0x0048);
49 define("TNEF_MAPI_BINARY",         0x0102);
50
51 define("TNEF_MAPI_ATTACH_MIME_TAG",      0x370E);
52 define("TNEF_MAPI_ATTACH_LONG_FILENAME", 0x3707);
53 define("TNEF_MAPI_ATTACH_DATA",          0x3701);
54
55 function tnef_getx($size, &$buf)
56 {
57    $value = null;
58    if (strlen($buf) >= $size)
59    {
60       $value = substr($buf, 0, $size);
61       $buf = substr_replace($buf, '', 0, $size);
62    }
63    return $value;
64 }
65
66 function tnef_geti8(&$buf)
67 {
68    $value = null;
69    if (strlen($buf) >= 1)
70    {
71       $value = ord($buf{0});
72       $buf = substr_replace($buf, '', 0, 1);
73    }
74    return $value;
75 }
76
77 function tnef_geti16(&$buf)
78 {
79    $value = null;
80    if (strlen($buf) >= 2)
81    {
82       $value = ord($buf{0}) +
83                (ord($buf{1}) << 8);
84       $buf = substr_replace($buf, '', 0, 2);
85    }
86    return $value;
87 }
88
89 function tnef_geti32(&$buf)
90 {
91    $value = null;
92    if (strlen($buf) >= 4)
93    {
94       $value = ord($buf{0}) +
95                (ord($buf{1}) << 8) +
96                (ord($buf{2}) << 16) +
97                (ord($buf{3}) << 24);
98       $buf = substr_replace($buf, '', 0, 4);
99    }
100    return $value;
101 }
102
103 function tnef_decode_attribute($attribute, &$buf)
104 {
105    global $debug;
106
107    $length = tnef_geti32($buf);
108    $value = tnef_getx($length, $buf); //data
109    tnef_geti16($buf); //checksum
110
111    if ($debug)
112    {
113       printf("ATTRIBUTE[%08x] %d bytes\n", $attribute, $length);
114    }
115
116    switch($attribute)
117    {
118       case TNEF_BODYTEXT:
119          if ($debug)
120          {
121             printf("<b>Embedded message:</b><pre>%s</pre>", $value);
122          }
123          break;
124
125       default:
126    }
127 }
128
129 function extract_mapi_attrs($buf, &$attachment_data)
130 {
131    global $debug;
132
133    tnef_geti32($buf); // number of attributes
134    while(strlen($buf) > 0)
135    {
136       $value = null;
137       $length = 0;
138       $attr_type = tnef_geti16($buf);
139       $attr_name = tnef_geti16($buf);
140       if ($debug)
141       {
142          printf("mapi attribute: %04x:%04x\n", $attr_type, $attr_name);
143       }
144       switch($attr_type)
145       {
146          case TNEF_MAPI_SHORT:
147             $value = tnef_geti16($buf);
148             break;
149
150          case TNEF_MAPI_INT:
151          case TNEF_MAPI_BOOLEAN:
152             $value = tnef_geti32($buf);
153             break;
154
155          case TNEF_MAPI_FLOAT:
156             $value = tnef_getx(4, $buf);
157             break;
158
159          case TNEF_MAPI_DOUBLE:
160          case TNEF_MAPI_SYSTIME:
161             $value = tnef_getx(8, $buf);
162             break;
163
164          case TNEF_MAPI_STRING:
165          case TNEF_MAPI_UNICODE_STRING:
166          case TNEF_MAPI_BINARY:
167          case TNEF_MAPI_OBJECT:
168             $num_vals = tnef_geti32($buf);
169             for ($i = 0; $i < $num_vals; $i++) // usually just 1
170             {
171                $length = tnef_geti32($buf);
172                $buflen = $length + ((4 - ($length % 4)) % 4); // pad to next 4 byte boundary
173                $value = substr(tnef_getx($buflen, $buf), 0, $length); // read and truncate to length
174             }
175             break;
176
177          default:
178             if ($debug)
179             {
180                echo("Unknown mapi attribute!\n");
181             }
182       }
183
184       // store any interesting attributes
185       switch($attr_name)
186       {
187          case TNEF_MAPI_ATTACH_LONG_FILENAME: // used in preference to AFILENAME value
188             $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $value); // strip path
189             break;
190
191          case TNEF_MAPI_ATTACH_MIME_TAG: // Is this ever set, and what is format?
192             $attachment_data[0]['type0'] = preg_replace('/^(.*)\/.*/', '\1', $value);
193             $attachment_data[0]['type1'] = preg_replace('/.*\/(.*)$/', '\1', $value);
194             break;
195
196          case TNEF_MAPI_ATTACH_DATA:
197             tnef_getx(16, $value); // skip the next 16 bytes (unknown data)
198             array_shift($attachment_data); // eliminate the current (bogus) attachment
199             do_tnef_decode($value, $attachment_data); // recursively process the attached message
200             break;
201
202          default:
203       }
204    }
205 }
206
207 function tnef_decode_message(&$buf)
208 {
209    global $debug;
210
211    if ($debug)
212    {
213       echo("MESSAGE ");
214    }
215
216    $attribute = tnef_geti32($buf);
217    tnef_decode_attribute($attribute, $buf);
218 }
219
220 function tnef_decode_attachment(&$buf, &$attachment_data)
221 {
222    global $debug;
223
224    if ($debug)
225    {
226       echo("ATTACHMENT ");
227    }
228
229    $attribute = tnef_geti32($buf);
230    switch($attribute)
231    {    
232       case TNEF_ARENDDATA: // marks start of new attachment
233          $length = tnef_geti32($buf);
234          tnef_getx($length, $buf);
235          tnef_geti16($buf); //checksum
236          if ($debug)
237          {
238             printf("ARENDDATA[%08x]: %d bytes\n", $attribute, $length);
239          }
240          // add a new default data block to hold details of this attachment
241          // reverse order is easier to handle later!
242          array_unshift($attachment_data, array('type0'  => 'application',
243                                                'type1'  => 'octet-stream',
244                                                'name'   => 'unknown',
245                                                'stream' => ''));
246          break;
247
248       case TNEF_AFILENAME: // filename
249          $length = tnef_geti32($buf);
250          $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/',
251                                                     '\1',
252                                                     tnef_getx($length, $buf)); // strip path
253          tnef_geti16($buf); //checksum
254          if ($debug)
255          {
256             printf("AFILENAME[%08x]: %s\n", $attribute, $attachment_data[0]['name']);
257          }
258          break;
259
260       case TNEF_ATTACHDATA: // the attachment itself
261          $length = tnef_geti32($buf);
262          $attachment_data[0]['size'] = $length;
263          $attachment_data[0]['stream'] = tnef_getx($length, $buf);
264          tnef_geti16($buf); //checksum
265          if ($debug)
266          {
267             printf("ATTACHDATA[%08x]: %d bytes\n", $attribute, $length);
268          }
269          break;
270
271       case TNEF_AMAPIATTRS:
272          $length = tnef_geti32($buf);
273          $value = tnef_getx($length, $buf);
274          tnef_geti16($buf); //checksum
275          if ($debug)
276          {
277             printf("AMAPIATTRS[%08x]: %d bytes\n", $attribute, $length);
278          }
279          extract_mapi_attrs($value, $attachment_data);
280          break;
281
282       default:
283          tnef_decode_attribute($attribute, $buf);
284    }
285 }
286
287 function do_tnef_decode(&$buf, &$attachment_data)
288 {
289    global $debug;
290
291    $tnef_signature = tnef_geti32($buf);
292    if ($tnef_signature == TNEF_SIGNATURE)
293    {
294       $tnef_key = tnef_geti16($buf);
295       if ($debug)
296       {
297          printf("Signature: 0x%08x\nKey: 0x%04x\n", $tnef_signature, $tnef_key);
298       }
299
300       while (strlen($buf) > 0)
301       {
302          $lvl_type = tnef_geti8($buf);
303          switch($lvl_type)
304          {
305             case TNEF_LVL_MESSAGE:
306                tnef_decode_message($buf);
307                break;
308
309             case TNEF_LVL_ATTACHMENT:
310                tnef_decode_attachment($buf, $attachment_data);
311                break;
312
313             default:
314                if ($debug)
315                {
316                   echo("Invalid file format!");
317                }
318                break 2;
319          }
320       }
321    }
322    else
323    {
324       if ($debug)
325       {
326          echo("Invalid file format!");
327       }
328    }
329 }
330
331 function tnef_decode($buf)
332 {
333    global $debug;
334
335    $attachment_data = array();
336
337    if ($debug)
338    {
339       echo("<pre>");
340    }
341
342    do_tnef_decode($buf, $attachment_data);
343
344    if ($debug)
345    {
346       echo("</pre>");
347    }
348    return array_reverse($attachment_data);
349
350 }
351
352 ?>