source: trunk/zpush/backend/zarafa/tnefparser.php @ 7589

Revision 7589, 30.3 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   tnefparser.php
4* Project   :   Z-Push
5* Descr     :   This is tnef implementation for z-push.
6*               It is based on Zarafa's tnef implementation.
7*               This class does only simple reading of a
8*               tnef stream. Most importantly, we currently
9*               only support properties within the message itself,
10*               and do not support recipient tables and
11*               attachment properties within the TNEF data.
12*               This class will accept TNEF streams with data about
13*               recipients and attachments, but the information
14*               will be ignored.
15*
16* Created   :   21.06.2008
17*
18* Copyright 2007 - 2012 Zarafa Deutschland GmbH
19*
20* This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU Affero General Public License, version 3,
22* as published by the Free Software Foundation with the following additional
23* term according to sec. 7:
24*
25* According to sec. 7 of the GNU Affero General Public License, version 3,
26* the terms of the AGPL are supplemented with the following terms:
27*
28* "Zarafa" is a registered trademark of Zarafa B.V.
29* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
30* The licensing of the Program under the AGPL does not imply a trademark license.
31* Therefore any rights, title and interest in our trademarks remain entirely with us.
32*
33* However, if you propagate an unmodified version of the Program you are
34* allowed to use the term "Z-Push" to indicate that you distribute the Program.
35* Furthermore you may use our trademarks where it is necessary to indicate
36* the intended purpose of a product or service provided you use it in accordance
37* with honest practices in industrial or commercial matters.
38* If you want to propagate modified versions of the Program under the name "Z-Push",
39* you may only do so if you have a written permission by Zarafa Deutschland GmbH
40* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
41*
42* This program is distributed in the hope that it will be useful,
43* but WITHOUT ANY WARRANTY; without even the implied warranty of
44* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45* GNU Affero General Public License for more details.
46*
47* You should have received a copy of the GNU Affero General Public License
48* along with this program.  If not, see <http://www.gnu.org/licenses/>.
49*
50* Consult LICENSE file for details
51************************************************/
52/**
53 * For more information on tnef refer to:
54 * http://msdn.microsoft.com/en-us/library/ms530652(EXCHG.10).aspx
55 * http://msdn.microsoft.com/en-us/library/cc425498(EXCHG.80).aspx
56 *
57 * The mapping between Microsoft Mail IPM classes and those used in
58 * MAPI see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx
59 */
60
61class TNEFParser {
62    const TNEF_SIGNATURE = 0x223e9f78;
63    const TNEF_LVL_MESSAGE = 0x01;
64    const TNEF_LVL_ATTACHMENT = 0x02;
65    const DWORD = 32;
66    const WORD = 16;
67    const BYTE = 8;
68
69    /**
70     * Constructor
71     * We need a store in order to get the namedpropers
72     *
73     * @param mapistore     $store
74     * @param array         &$props     properties to be set
75     *
76     * @access public
77     */
78    public function TNEFParser(&$store, &$props) {
79        $this->store = $store;
80        $this->props = $props;
81    }
82
83    /**
84     * Function reads tnef stream and puts mapi properties into an array.
85     *
86     * @param string        $tnefstream
87     * @param array         &$mapiprops mapi properties
88     *
89     * @access public
90     * @return int
91     */
92    public function ExtractProps($tnefstream, &$mapiprops) {
93        $hresult = NOERROR;
94        $signature = 0; //tnef signature - 32 Bit
95        $key = 0; //a nonzero 16-bit unsigned integer
96
97        $type = 0; // 32-bit value
98        $size = 0; // 32-bit value
99        $checksum = 0; //16-bit value
100        $component = 0; //8-bit value - either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
101        $buffer = "";
102
103        //mapping between Microsoft Mail IPM classes and those in MAPI
104        $aClassMap = array(
105            "IPM.Microsoft Schedule.MtgReq"      => "IPM.Schedule.Meeting.Request",
106            "IPM.Microsoft Schedule.MtgRespP"    => "IPM.Schedule.Meeting.Resp.Pos",
107            "IPM.Microsoft Schedule.MtgRespN"    => "IPM.Schedule.Meeting.Resp.Neg",
108            "IPM.Microsoft Schedule.MtgRespA"    => "IPM.Schedule.Meeting.Resp.Tent",
109            "IPM.Microsoft Schedule.MtgCncl"     => "IPM.Schedule.Meeting.Canceled",
110            "IPM.Microsoft Mail.Non-Delivery"    => "Report.IPM.Note.NDR",
111            "IPM.Microsoft Mail.Read Receipt"    => "Report.IPM.Note.IPNRN",
112            "IPM.Microsoft Mail.Note"            => "IPM.Note",
113            "IPM.Microsoft Mail.Note"            => "IPM",
114        );
115
116        //read signature
117        $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $signature);
118        if ($hresult !== NOERROR) {
119            ZLog::Write(LOGLEVEL_WARN, "TNEF: STREAM:".bin2hex($tnefstream));
120            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef signature");
121            return $hresult;
122        }
123
124        //check signature
125        if ($signature != self::TNEF_SIGNATURE) {
126            ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt signature.");
127            return MAPI_E_CORRUPT_DATA;
128        }
129
130        //read key
131        $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $key);
132        if ($hresult !== NOERROR) {
133            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef key.");
134            return $hresult;
135        }
136
137        // File is made of blocks, with each a type and size. Component and Key are ignored.
138        while(1) {
139            //the stream is empty. exit
140            if (strlen($tnefstream) == 0) return NOERROR;
141
142            //read component - it is either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
143            $hresult = $this->readFromTnefStream($tnefstream, self::BYTE, $component);
144            if ($hresult !== NOERROR) {
145                $hresult = NOERROR; //EOF -> no error
146                return $hresult;
147                break;
148            }
149
150            //read type
151            $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $type);
152            if ($hresult !== NOERROR) {
153                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property type");
154                return $hresult;
155            }
156
157            //read size
158            $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $size);
159            if ($hresult !== NOERROR) {
160                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property size");
161                return $hresult;
162            }
163
164            if ($size == 0) {
165                // do not allocate 0 size data block
166                ZLog::Write(LOGLEVEL_WARN, "TNEF: Size is 0. Corrupt data.");
167                return MAPI_E_CORRUPT_DATA;
168            }
169
170            //read buffer
171            $hresult = $this->readBuffer($tnefstream, $size, $buffer);
172            if ($hresult !== NOERROR) {
173                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
174                return $hresult;
175            }
176
177            //read checksum
178            $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $checksum);
179            if ($hresult !== NOERROR) {
180                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
181                return $hresult;
182            }
183
184            // Loop through all the blocks of the TNEF data. We are only interested
185            // in the properties block for now (0x00069003)
186            switch ($type) {
187                case 0x00069003:
188                    $hresult = $this->readMapiProps($buffer, $size, $mapiprops);
189                    if ($hresult !== NOERROR) {
190                        ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi properties' part.");
191                        return $hresult;
192                    }
193                    break;
194                case 0x00078008: // PR_MESSAGE_CLASS
195                    $msMailClass = trim($buffer);
196                    if (array_key_exists($msMailClass, $aClassMap)) {
197                        $messageClass = $aClassMap[$msMailClass];
198                    }
199                    else {
200                        $messageClass = $msMailClass;
201                    }
202                    $mapiprops[PR_MESSAGE_CLASS] = $messageClass;
203                    break;
204                case 0x00050008: // PR_OWNER_APPT_ID
205                    $mapiprops[PR_OWNER_APPT_ID] = $buffer;
206                    break;
207                case 0x00040009: // PR_RESPONSE_REQUESTED
208                    $mapiprops[PR_RESPONSE_REQUESTED] = $buffer;
209                    break;
210
211                // --- TNEF attachemnts ---
212                case 0x00069002:
213                    break;
214                case 0x00018010:        // PR_ATTACH_FILENAME
215                    break;
216                case 0x00068011:        // PR_ATTACH_RENDERING, extra icon information
217                    break;
218                case 0x0006800f:        // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
219                    break;
220                case 0x00069005:        // Attachment property stream
221                    break;
222                default:
223                    // Ignore this block
224                    break;
225            }
226        }
227        return NOERROR;
228    }
229
230    /**
231     * Reads a given number of bits from stream and converts them from little indian in a "normal" order. The function
232     * also cuts the tnef stream after reading.
233     *
234     * @param string        &$tnefstream
235     * @param int           $bits
236     * @param array         &$element       the read element
237     *
238     * @access private
239     * @return int
240     */
241    private function readFromTnefStream(&$tnefstream, $bits, &$element) {
242        $bytes = $bits / 8;
243
244        $part = substr($tnefstream, 0, $bytes);
245        $packs = array();
246
247        switch ($bits) {
248            case self::DWORD:
249                $packs = unpack("V", $part);
250                break;
251            case self::WORD:
252                $packs = unpack("v", $part);
253                break;
254            case self::BYTE:
255                $packs[1] = ord($part[0]);
256                break;
257            default:
258                $packs = array();
259                break;
260        }
261
262        if (empty($packs) || !isset($packs[1])) return MAPI_E_CORRUPT_DATA;
263
264        $tnefstream = substr($tnefstream, $bytes);
265        $element = $packs[1];
266        return NOERROR;
267    }
268
269    /**
270     * Reads a given number of bytes from stream and puts them into $element. The function
271     * also cuts the tnef stream after reading.
272     *
273     * @param string        &$tnefstream
274     * @param int           $bytes
275     * @param array         &$element       the read element
276     *
277     * @access private
278     * @return int
279     */
280    private function readBuffer(&$tnefstream, $bytes, &$element) {
281        $element = substr($tnefstream, 0, $bytes);
282        $tnefstream = substr($tnefstream, $bytes);
283        return NOERROR;
284
285    }
286
287    /**
288     * Reads mapi props from buffer into an anrray.
289     *
290     * @param string        &$buffer
291     * @param int           $size
292     * @param array         &$mapiprops
293     *
294     * @access private
295     * @return int
296     */
297    function readMapiProps(&$buffer, $size, &$mapiprops) {
298        $nrprops = 0;
299        //get number of mapi properties
300        $hresult = $this->readFromTnefStream($buffer, self::DWORD, $nrprops);
301        if ($hresult !== NOERROR) {
302                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error getting the number of mapi properties in stream.");
303                return $hresult;
304        }
305
306        $size -= 4;
307
308        ZLog::Write(LOGLEVEL_DEBUG, "TNEF: nrprops:$nrprops");
309        //loop through all the properties and add them to our internal list
310        while($nrprops) {
311            $hresult = $this->readSingleMapiProp($buffer, $size, $read, $mapiprops);
312            if ($hresult !== NOERROR) {
313                    ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property.");
314                    ZLog::Write(LOGLEVEL_WARN, "TNEF: result: " . sprintf("0x%X", $hresult));
315
316                    return $hresult;
317            }
318            $nrprops--;
319        }
320        return NOERROR;
321    }
322
323    /**
324     * Reads a single mapi prop.
325     *
326     * @param string        &$buffer
327     * @param int           $size
328     * @param mixed         &$read
329     * @param array         &$mapiprops
330     *
331     * @access private
332     * @return int
333     */
334    private function readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) {
335        $propTag = 0;
336        $len = 0;
337        $origSize = $size;
338        $isNamedId = 0;
339        $namedProp = 0;
340        $count = 0;
341        $mvProp = 0;
342        $guid = 0;
343
344        if($size < 8) {
345            return MAPI_E_NOT_FOUND;
346        }
347
348        $hresult = $this->readFromTnefStream($buffer, self::DWORD, $propTag);
349        if ($hresult !== NOERROR) {
350            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property tag from the stream.");
351            return $hresult;
352        }
353        $size -= 4;
354        ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop type:".dechex(mapi_prop_type($propTag)));
355        ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag)));
356        if (mapi_prop_id($propTag) >= 0x8000) {
357        // Named property, first read GUID, then name/id
358            if($size < 24) {
359                ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt guid size for named property:".dechex($propTag));
360                return MAPI_E_CORRUPT_DATA;
361            }
362            //strip GUID & name/id
363            $hresult = $this->readBuffer($buffer, 16, $guid);
364            if ($hresult !== NOERROR) {
365                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
366                return $hresult;
367            }
368
369            $size -= 16;
370             //it is not used and is here only for eventual debugging
371            $readableGuid = unpack("VV/v2v/n4n", $guid);
372            $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}",$readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'],$readableGuid['n1'],$readableGuid['n2'],$readableGuid['n3'],$readableGuid['n4']);
373            ZLog::Write(LOGLEVEL_DEBUG, "TNEF: guid:$readableGuid");
374            $hresult = $this->readFromTnefStream($buffer, self::DWORD, $isNamedId);
375            if ($hresult !== NOERROR) {
376                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
377                return $hresult;
378            }
379            $size -= 4;
380
381            if($isNamedId != 0) {
382                // A string name follows
383                //read length of the property
384                $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
385                if ($hresult !== NOERROR) {
386                    ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
387                    return $hresult;
388                }
389                $size -= 4;
390                if ($size < $len) {
391                    return MAPI_E_CORRUPT_DATA;
392                }
393                //read the name of the property, eg Keywords
394                $hresult = $this->readBuffer($buffer, $len, $namedProp);
395                if ($hresult !== NOERROR) {
396                    ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
397                    return $hresult;
398                }
399
400                $size -= $len;
401
402                //Re-align
403                $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
404                $size -= $len & 3 ? 4 - ($len & 3) : 0;
405            }
406            else {
407                $hresult = $this->readFromTnefStream($buffer, self::DWORD, $namedProp);
408                if ($hresult !== NOERROR) {
409                    ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
410                    return $hresult;
411                }
412                ZLog::Write(LOGLEVEL_DEBUG, "TNEF: named: 0x".sprintf("%04x", $namedProp));
413                $size -= 4;
414            }
415
416            if ($this->store !== false) {
417                $named = mapi_getidsfromnames($this->store, array($namedProp), array(makeguid($readableGuid)));
418
419                $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0]));
420            }
421            else {
422                ZLog::Write(LOGLEVEL_WARN, "TNEF: Store not available. It is impossible to get named properties");
423            }
424        }
425        ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag)));
426        if($propTag & MV_FLAG) {
427            if($size < 4) {
428                return MAPI_E_CORRUPT_DATA;
429            }
430            //read the number of properties
431            $hresult = $this->readFromTnefStream($buffer, self::DWORD, $count);
432            if ($hresult !== NOERROR) {
433                ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading number of properties for:".dechex($propTag));
434                return $hresult;
435            }
436            $size -= 4;
437        }
438        else {
439            $count = 1;
440        }
441
442        for ($mvProp = 0; $mvProp < $count; $mvProp++) {
443            switch(mapi_prop_type($propTag) & ~MV_FLAG ) {
444                case PT_I2:
445                case PT_LONG:
446                    $hresult = $this->readBuffer($buffer, 4, $value);
447                    if ($hresult !== NOERROR) {
448                        ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
449                        return $hresult;
450                    }
451                    $value = unpack("V", $value);
452                    $value = intval($value[1], 16);
453
454                    if($propTag & MV_FLAG) {
455                        $mapiprops[$propTag][] = $value;
456                    }
457                    else {
458                        $mapiprops[$propTag] = $value;
459                    }
460                    $size -= 4;
461                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: int or long propvalue:".$value);
462                    break;
463
464                case PT_R4:
465                    if($propTag & MV_FLAG) {
466                        $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag][]);
467
468                        if ($hresult !== NOERROR) {
469                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
470                            return $hresult;
471                        }
472                    }
473                    else {
474                        $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
475                        if ($hresult !== NOERROR) {
476                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
477                            return $hresult;
478                        }
479                    }
480                    $size -= 4;
481                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
482                    break;
483
484                case PT_BOOLEAN:
485                    $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
486                        if ($hresult !== NOERROR) {
487                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
488                            return $hresult;
489                        }
490                    $size -= 4;
491                    //reported by dw2412
492                    //cast to integer as it evaluates to 1 or 0 because
493                    //a non empty string evaluates to true :(
494                    $mapiprops[$propTag] = (integer) bin2hex($mapiprops[$propTag]{0});
495                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
496                    break;
497
498
499                case PT_SYSTIME:
500                    if($size < 8) {
501                        return MAPI_E_CORRUPT_DATA;
502                    }
503                    if($propTag & MV_FLAG) {
504                        $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
505                        if ($hresult !== NOERROR) {
506                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
507                            return $hresult;
508                        }
509                    }
510                    else {
511                        $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
512                        if ($hresult !== NOERROR) {
513                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
514                            return $hresult;
515                        }
516                    }
517                    //we have to convert the filetime to an unixtime timestamp
518                    $filetime = unpack("V2v", $mapiprops[$propTag]);
519                    //php on 64-bit systems converts unsigned values differently than on 32 bit systems
520                    //we need this "fix" in order to get the same values on both types of systems
521                    $filetime['v2'] = substr(sprintf("%08x",$filetime['v2']), -8);
522                    $filetime['v1'] = substr(sprintf("%08x",$filetime['v1']), -8);
523
524                    $filetime = hexdec($filetime['v2'].$filetime['v1']);
525                    $filetime = ($filetime - 116444736000000000) / 10000000;
526                    $mapiprops[$propTag] = $filetime;
527                    // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough
528                    if ($propTag == PR_START_DATE) {
529                        $mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $filetime;
530                    }
531                    if ($propTag == PR_END_DATE) {
532                        $mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $filetime;
533                    }
534                    $size -= 8;
535                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
536                    break;
537
538                case PT_DOUBLE:
539                case PT_CURRENCY:
540                case PT_I8:
541                case PT_APPTIME:
542                    if($size < 8) {
543                        return MAPI_E_CORRUPT_DATA;
544                    }
545                    if($propTag & MV_FLAG) {
546                        $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
547                        if ($hresult !== NOERROR) {
548                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
549                            return $hresult;
550                        }
551                    }
552                    else {
553                        $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
554                        if ($hresult !== NOERROR) {
555                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
556                            return $hresult;
557                        }
558                    }
559                    $size -= 8;
560                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
561                    break;
562
563                case PT_STRING8:
564                    if($size < 8) {
565                        return MAPI_E_CORRUPT_DATA;
566                    }
567                    // Skip next 4 bytes, it's always '1' (ULONG)
568                    $buffer = substr($buffer, 4);
569                    $size -= 4;
570
571                    //read length of the property
572                    $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
573                    if ($hresult !== NOERROR) {
574                        ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
575                        return $hresult;
576                    }
577                    $size -= 4;
578                    if ($size < $len) {
579                        return MAPI_E_CORRUPT_DATA;
580                    }
581
582                    if ($propTag & MV_FLAG) {
583                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
584                        if ($hresult !== NOERROR) {
585                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
586                            return $hresult;
587                        }
588                    }
589                    else {
590                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
591                        if ($hresult !== NOERROR) {
592                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
593                            return $hresult;
594                        }
595                    }
596                    //location fix. it looks like tnef uses this value for location
597                    if (mapi_prop_id($propTag) == 0x8342) {
598                        $mapiprops[$this->props["location"]] = $mapiprops[$propTag];
599                        unset($mapiprops[$propTag]);
600                    }
601
602                    $size -= $len;
603
604                    //Re-align
605                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
606                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
607                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
608                    break;
609
610                case PT_UNICODE:
611                    if($size < 8) {
612                        return MAPI_E_CORRUPT_DATA;
613                    }
614                    // Skip next 4 bytes, it's always '1' (ULONG)
615                    $buffer = substr($buffer, 4);
616                    $size -= 4;
617
618                    //read length of the property
619                    $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
620                    if ($hresult !== NOERROR) {
621                        ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
622                        return $hresult;
623                    }
624                    $size -= 4;
625                    if ($size < $len) {
626                        return MAPI_E_CORRUPT_DATA;
627                    }
628                    //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8
629                    $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag));
630
631                    if ($propTag & MV_FLAG) {
632                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
633                        if ($hresult !== NOERROR) {
634                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
635                            return $hresult;
636                        }
637                    }
638                    else {
639                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
640                        if ($hresult !== NOERROR) {
641                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
642                            return $hresult;
643                        }
644                    }
645
646                    //location fix. it looks like tnef uses this value for location
647                    if (mapi_prop_id($propTag) == 0x8342) {
648                        $mapiprops[$this->props["location"]] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
649                        unset($mapiprops[$propTag]);
650                    }
651
652                    //convert from unicode to windows encoding
653                    if (isset($mapiprops[$propTag])) $mapiprops[$propTag] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
654                    $size -= $len;
655
656                    //Re-align
657                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
658                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
659                    if (isset($mapiprops[$propTag])) ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
660                    break;
661
662                case PT_OBJECT:        // PST sends PT_OBJECT data. Treat as PT_BINARY
663                case PT_BINARY:
664                    if($size < self::BYTE) {
665                        return MAPI_E_CORRUPT_DATA;
666                    }
667                    // Skip next 4 bytes, it's always '1' (ULONG)
668                    $buffer = substr($buffer, 4);
669                    $size -= 4;
670
671                    //read length of the property
672                    $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
673                    if ($hresult !== NOERROR) {
674                        ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
675                        return $hresult;
676                    }
677                    $size -= 4;
678
679                    if (mapi_prop_type($propTag) == PT_OBJECT) {
680                        // IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ]
681                        $buffer = substr($buffer, 16);
682                        $size -= 16;
683                        $len -= 16;
684                    }
685
686                    if ($size < $len) {
687                        return MAPI_E_CORRUPT_DATA;
688                    }
689
690                    if ($propTag & MV_FLAG) {
691                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
692                        if ($hresult !== NOERROR) {
693                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
694                            return $hresult;
695                        }
696                    }
697                    else {
698                        $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
699                        if ($hresult !== NOERROR) {
700                            ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
701                            return $hresult;
702                        }
703                    }
704
705                    $size -= $len;
706
707                    //Re-align
708                    $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
709                    $size -= $len & 3 ? 4 - ($len & 3) : 0;
710                    ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".bin2hex($mapiprops[$propTag]));
711                    break;
712
713                default:
714                    return MAPI_E_INVALID_PARAMETER;
715                    break;
716            }
717        }
718        return NOERROR;
719    }
720}
721?>
Note: See TracBrowser for help on using the repository browser.