Back to index

d-push  2.0
tnefparser.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   tnefparser.php
00004 * Project   :   Z-Push
00005 * Descr     :   This is tnef implementation for z-push.
00006 *               It is based on Zarafa's tnef implementation.
00007 *               This class does only simple reading of a
00008 *               tnef stream. Most importantly, we currently
00009 *               only support properties within the message itself,
00010 *               and do not support recipient tables and
00011 *               attachment properties within the TNEF data.
00012 *               This class will accept TNEF streams with data about
00013 *               recipients and attachments, but the information
00014 *               will be ignored.
00015 *
00016 * Created   :   21.06.2008
00017 *
00018 * Copyright 2007 - 2011 Zarafa Deutschland GmbH
00019 *
00020 * This program is free software: you can redistribute it and/or modify
00021 * it under the terms of the GNU Affero General Public License, version 3,
00022 * as published by the Free Software Foundation with the following additional
00023 * term according to sec. 7:
00024 *
00025 * According to sec. 7 of the GNU Affero General Public License, version 3,
00026 * the terms of the AGPL are supplemented with the following terms:
00027 *
00028 * "Zarafa" is a registered trademark of Zarafa B.V.
00029 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00030 * The licensing of the Program under the AGPL does not imply a trademark license.
00031 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00032 *
00033 * However, if you propagate an unmodified version of the Program you are
00034 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00035 * Furthermore you may use our trademarks where it is necessary to indicate
00036 * the intended purpose of a product or service provided you use it in accordance
00037 * with honest practices in industrial or commercial matters.
00038 * If you want to propagate modified versions of the Program under the name "Z-Push",
00039 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00040 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00041 *
00042 * This program is distributed in the hope that it will be useful,
00043 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00044 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00045 * GNU Affero General Public License for more details.
00046 *
00047 * You should have received a copy of the GNU Affero General Public License
00048 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00049 *
00050 * Consult LICENSE file for details
00051 ************************************************/
00061 class TNEFParser {
00062     const TNEF_SIGNATURE = 0x223e9f78;
00063     const TNEF_LVL_MESSAGE = 0x01;
00064     const TNEF_LVL_ATTACHMENT = 0x02;
00065     const DWORD = 32;
00066     const WORD = 16;
00067     const BYTE = 8;
00068 
00078     public function TNEFParser(&$store, &$props) {
00079         $this->store = $store;
00080         $this->props = $props;
00081     }
00082 
00092     public function ExtractProps($tnefstream, &$mapiprops) {
00093         $hresult = NOERROR;
00094         $signature = 0; //tnef signature - 32 Bit
00095         $key = 0; //a nonzero 16-bit unsigned integer
00096 
00097         $type = 0; // 32-bit value
00098         $size = 0; // 32-bit value
00099         $checksum = 0; //16-bit value
00100         $component = 0; //8-bit value - either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
00101         $buffer = "";
00102 
00103         //mapping between Microsoft Mail IPM classes and those in MAPI
00104         $aClassMap = array(
00105             "IPM.Microsoft Schedule.MtgReq"      => "IPM.Schedule.Meeting.Request",
00106             "IPM.Microsoft Schedule.MtgRespP"    => "IPM.Schedule.Meeting.Resp.Pos",
00107             "IPM.Microsoft Schedule.MtgRespN"    => "IPM.Schedule.Meeting.Resp.Neg",
00108             "IPM.Microsoft Schedule.MtgRespA"    => "IPM.Schedule.Meeting.Resp.Tent",
00109             "IPM.Microsoft Schedule.MtgCncl"     => "IPM.Schedule.Meeting.Canceled",
00110             "IPM.Microsoft Mail.Non-Delivery"    => "Report.IPM.Note.NDR",
00111             "IPM.Microsoft Mail.Read Receipt"    => "Report.IPM.Note.IPNRN",
00112             "IPM.Microsoft Mail.Note"            => "IPM.Note",
00113             "IPM.Microsoft Mail.Note"            => "IPM",
00114         );
00115 
00116         //read signature
00117         $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $signature);
00118         if ($hresult !== NOERROR) {
00119             ZLog::Write(LOGLEVEL_WARN, "TNEF: STREAM:".bin2hex($tnefstream));
00120             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef signature");
00121             return $hresult;
00122         }
00123 
00124         //check signature
00125         if ($signature != self::TNEF_SIGNATURE) {
00126             ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt signature.");
00127             return MAPI_E_CORRUPT_DATA;
00128         }
00129 
00130         //read key
00131         $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $key);
00132         if ($hresult !== NOERROR) {
00133             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef key.");
00134             return $hresult;
00135         }
00136 
00137         // File is made of blocks, with each a type and size. Component and Key are ignored.
00138         while(1) {
00139             //the stream is empty. exit
00140             if (strlen($tnefstream) == 0) return NOERROR;
00141 
00142             //read component - it is either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT
00143             $hresult = $this->readFromTnefStream($tnefstream, self::BYTE, $component);
00144             if ($hresult !== NOERROR) {
00145                 $hresult = NOERROR; //EOF -> no error
00146                 return $hresult;
00147                 break;
00148             }
00149 
00150             //read type
00151             $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $type);
00152             if ($hresult !== NOERROR) {
00153                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property type");
00154                 return $hresult;
00155             }
00156 
00157             //read size
00158             $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $size);
00159             if ($hresult !== NOERROR) {
00160                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property size");
00161                 return $hresult;
00162             }
00163 
00164             if ($size == 0) {
00165                 // do not allocate 0 size data block
00166                 ZLog::Write(LOGLEVEL_WARN, "TNEF: Size is 0. Corrupt data.");
00167                 return MAPI_E_CORRUPT_DATA;
00168             }
00169 
00170             //read buffer
00171             $hresult = $this->readBuffer($tnefstream, $size, $buffer);
00172             if ($hresult !== NOERROR) {
00173                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00174                 return $hresult;
00175             }
00176 
00177             //read checksum
00178             $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $checksum);
00179             if ($hresult !== NOERROR) {
00180                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
00181                 return $hresult;
00182             }
00183 
00184             // Loop through all the blocks of the TNEF data. We are only interested
00185             // in the properties block for now (0x00069003)
00186             switch ($type) {
00187                 case 0x00069003:
00188                     $hresult = $this->readMapiProps($buffer, $size, $mapiprops);
00189                     if ($hresult !== NOERROR) {
00190                         ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi properties' part.");
00191                         return $hresult;
00192                     }
00193                     break;
00194                 case 0x00078008: // PR_MESSAGE_CLASS
00195                     $msMailClass = trim($buffer);
00196                     if (array_key_exists($msMailClass, $aClassMap)) {
00197                         $messageClass = $aClassMap[$msMailClass];
00198                     }
00199                     else {
00200                         $messageClass = $msMailClass;
00201                     }
00202                     $mapiprops[PR_MESSAGE_CLASS] = $messageClass;
00203                     break;
00204                 case 0x00050008: // PR_OWNER_APPT_ID
00205                     $mapiprops[PR_OWNER_APPT_ID] = $buffer;
00206                     break;
00207                 case 0x00040009: // PR_RESPONSE_REQUESTED
00208                     $mapiprops[PR_RESPONSE_REQUESTED] = $buffer;
00209                     break;
00210 
00211                 // --- TNEF attachemnts ---
00212                 case 0x00069002:
00213                     break;
00214                 case 0x00018010:        // PR_ATTACH_FILENAME
00215                     break;
00216                 case 0x00068011:        // PR_ATTACH_RENDERING, extra icon information
00217                     break;
00218                 case 0x0006800f:        // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
00219                     break;
00220                 case 0x00069005:        // Attachment property stream
00221                     break;
00222                 default:
00223                     // Ignore this block
00224                     break;
00225             }
00226         }
00227         return NOERROR;
00228     }
00229 
00241     private function readFromTnefStream(&$tnefstream, $bits, &$element) {
00242         $bytes = $bits / 8;
00243 
00244         $part = substr($tnefstream, 0, $bytes);
00245         $packs = array();
00246 
00247         switch ($bits) {
00248             case self::DWORD:
00249                 $packs = unpack("V", $part);
00250                 break;
00251             case self::WORD:
00252                 $packs = unpack("v", $part);
00253                 break;
00254             case self::BYTE:
00255                 $packs[1] = ord($part[0]);
00256                 break;
00257             default:
00258                 $packs = array();
00259                 break;
00260         }
00261 
00262         if (empty($packs) || !isset($packs[1])) return MAPI_E_CORRUPT_DATA;
00263 
00264         $tnefstream = substr($tnefstream, $bytes);
00265         $element = $packs[1];
00266         return NOERROR;
00267     }
00268 
00280     private function readBuffer(&$tnefstream, $bytes, &$element) {
00281         $element = substr($tnefstream, 0, $bytes);
00282         $tnefstream = substr($tnefstream, $bytes);
00283         return NOERROR;
00284 
00285     }
00286 
00297     function readMapiProps(&$buffer, $size, &$mapiprops) {
00298         $nrprops = 0;
00299         //get number of mapi properties
00300         $hresult = $this->readFromTnefStream($buffer, self::DWORD, $nrprops);
00301         if ($hresult !== NOERROR) {
00302                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error getting the number of mapi properties in stream.");
00303                 return $hresult;
00304         }
00305 
00306         $size -= 4;
00307 
00308         ZLog::Write(LOGLEVEL_DEBUG, "TNEF: nrprops:$nrprops");
00309         //loop through all the properties and add them to our internal list
00310         while($nrprops) {
00311             $hresult = $this->readSingleMapiProp($buffer, $size, $read, $mapiprops);
00312             if ($hresult !== NOERROR) {
00313                     ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property.");
00314                     ZLog::Write(LOGLEVEL_WARN, "TNEF: result: " . sprintf("0x%X", $hresult));
00315 
00316                     return $hresult;
00317             }
00318             $nrprops--;
00319         }
00320         return NOERROR;
00321     }
00322 
00334     private function readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) {
00335         $propTag = 0;
00336         $len = 0;
00337         $origSize = $size;
00338         $isNamedId = 0;
00339         $namedProp = 0;
00340         $count = 0;
00341         $mvProp = 0;
00342         $guid = 0;
00343 
00344         if($size < 8) {
00345             return MAPI_E_NOT_FOUND;
00346         }
00347 
00348         $hresult = $this->readFromTnefStream($buffer, self::DWORD, $propTag);
00349         if ($hresult !== NOERROR) {
00350             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property tag from the stream.");
00351             return $hresult;
00352         }
00353         $size -= 4;
00354         ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop type:".dechex(mapi_prop_type($propTag)));
00355         ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag)));
00356         if (mapi_prop_id($propTag) >= 0x8000) {
00357         // Named property, first read GUID, then name/id
00358             if($size < 24) {
00359                 ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt guid size for named property:".dechex($propTag));
00360                 return MAPI_E_CORRUPT_DATA;
00361             }
00362             //strip GUID & name/id
00363             $hresult = $this->readBuffer($buffer, 16, $guid);
00364             if ($hresult !== NOERROR) {
00365                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00366                 return $hresult;
00367             }
00368 
00369             $size -= 16;
00370              //it is not used and is here only for eventual debugging
00371             $readableGuid = unpack("VV/v2v/n4n", $guid);
00372             $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}",$readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'],$readableGuid['n1'],$readableGuid['n2'],$readableGuid['n3'],$readableGuid['n4']);
00373             ZLog::Write(LOGLEVEL_DEBUG, "TNEF: guid:$readableGuid");
00374             $hresult = $this->readFromTnefStream($buffer, self::DWORD, $isNamedId);
00375             if ($hresult !== NOERROR) {
00376                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum.");
00377                 return $hresult;
00378             }
00379             $size -= 4;
00380 
00381             if($isNamedId != 0) {
00382                 // A string name follows
00383                 //read length of the property
00384                 $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
00385                 if ($hresult !== NOERROR) {
00386                     ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
00387                     return $hresult;
00388                 }
00389                 $size -= 4;
00390                 if ($size < $len) {
00391                     return MAPI_E_CORRUPT_DATA;
00392                 }
00393                 //read the name of the property, eg Keywords
00394                 $hresult = $this->readBuffer($buffer, $len, $namedProp);
00395                 if ($hresult !== NOERROR) {
00396                     ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00397                     return $hresult;
00398                 }
00399 
00400                 $size -= $len;
00401 
00402                 //Re-align
00403                 $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
00404                 $size -= $len & 3 ? 4 - ($len & 3) : 0;
00405             }
00406             else {
00407                 $hresult = $this->readFromTnefStream($buffer, self::DWORD, $namedProp);
00408                 if ($hresult !== NOERROR) {
00409                     ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
00410                     return $hresult;
00411                 }
00412                 ZLog::Write(LOGLEVEL_DEBUG, "TNEF: named: 0x".sprintf("%04x", $namedProp));
00413                 $size -= 4;
00414             }
00415 
00416             if ($this->store !== false) {
00417                 $named = mapi_getidsfromnames($this->store, array($namedProp), array(makeguid($readableGuid)));
00418 
00419                 $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0]));
00420             }
00421             else {
00422                 ZLog::Write(LOGLEVEL_WARN, "TNEF: Store not available. It is impossible to get named properties");
00423             }
00424         }
00425         ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag)));
00426         if($propTag & MV_FLAG) {
00427             if($size < 4) {
00428                 return MAPI_E_CORRUPT_DATA;
00429             }
00430             //read the number of properties
00431             $hresult = $this->readFromTnefStream($buffer, self::DWORD, $count);
00432             if ($hresult !== NOERROR) {
00433                 ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading number of properties for:".dechex($propTag));
00434                 return $hresult;
00435             }
00436             $size -= 4;
00437         }
00438         else {
00439             $count = 1;
00440         }
00441 
00442         for ($mvProp = 0; $mvProp < $count; $mvProp++) {
00443             switch(mapi_prop_type($propTag) & ~MV_FLAG ) {
00444                 case PT_I2:
00445                 case PT_LONG:
00446                     $hresult = $this->readBuffer($buffer, 4, $value);
00447                     if ($hresult !== NOERROR) {
00448                         ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00449                         return $hresult;
00450                     }
00451                     $value = unpack("V", $value);
00452                     $value = intval($value[1], 16);
00453 
00454                     if($propTag & MV_FLAG) {
00455                         $mapiprops[$propTag][] = $value;
00456                     }
00457                     else {
00458                         $mapiprops[$propTag] = $value;
00459                     }
00460                     $size -= 4;
00461                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: int or long propvalue:".$value);
00462                     break;
00463 
00464                 case PT_R4:
00465                     if($propTag & MV_FLAG) {
00466                         $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag][]);
00467 
00468                         if ($hresult !== NOERROR) {
00469                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00470                             return $hresult;
00471                         }
00472                     }
00473                     else {
00474                         $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
00475                         if ($hresult !== NOERROR) {
00476                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00477                             return $hresult;
00478                         }
00479                     }
00480                     $size -= 4;
00481                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00482                     break;
00483 
00484                 case PT_BOOLEAN:
00485                     $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]);
00486                         if ($hresult !== NOERROR) {
00487                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00488                             return $hresult;
00489                         }
00490                     $size -= 4;
00491                     //reported by dw2412
00492                     //cast to integer as it evaluates to 1 or 0 because
00493                     //a non empty string evaluates to true :(
00494                     $mapiprops[$propTag] = (integer) bin2hex($mapiprops[$propTag]{0});
00495                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00496                     break;
00497 
00498 
00499                 case PT_SYSTIME:
00500                     if($size < 8) {
00501                         return MAPI_E_CORRUPT_DATA;
00502                     }
00503                     if($propTag & MV_FLAG) {
00504                         $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
00505                         if ($hresult !== NOERROR) {
00506                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00507                             return $hresult;
00508                         }
00509                     }
00510                     else {
00511                         $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
00512                         if ($hresult !== NOERROR) {
00513                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00514                             return $hresult;
00515                         }
00516                     }
00517                     //we have to convert the filetime to an unixtime timestamp
00518                     $filetime = unpack("V2v", $mapiprops[$propTag]);
00519                     //php on 64-bit systems converts unsigned values differently than on 32 bit systems
00520                     //we need this "fix" in order to get the same values on both types of systems
00521                     $filetime['v2'] = substr(sprintf("%08x",$filetime['v2']), -8);
00522                     $filetime['v1'] = substr(sprintf("%08x",$filetime['v1']), -8);
00523 
00524                     $filetime = hexdec($filetime['v2'].$filetime['v1']);
00525                     $filetime = ($filetime - 116444736000000000) / 10000000;
00526                     $mapiprops[$propTag] = $filetime;
00527                     // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough
00528                     if ($propTag == PR_START_DATE) {
00529                         $mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $filetime;
00530                     }
00531                     if ($propTag == PR_END_DATE) {
00532                         $mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $filetime;
00533                     }
00534                     $size -= 8;
00535                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00536                     break;
00537 
00538                 case PT_DOUBLE:
00539                 case PT_CURRENCY:
00540                 case PT_I8:
00541                 case PT_APPTIME:
00542                     if($size < 8) {
00543                         return MAPI_E_CORRUPT_DATA;
00544                     }
00545                     if($propTag & MV_FLAG) {
00546                         $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]);
00547                         if ($hresult !== NOERROR) {
00548                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00549                             return $hresult;
00550                         }
00551                     }
00552                     else {
00553                         $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]);
00554                         if ($hresult !== NOERROR) {
00555                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00556                             return $hresult;
00557                         }
00558                     }
00559                     $size -= 8;
00560                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00561                     break;
00562 
00563                 case PT_STRING8:
00564                     if($size < 8) {
00565                         return MAPI_E_CORRUPT_DATA;
00566                     }
00567                     // Skip next 4 bytes, it's always '1' (ULONG)
00568                     $buffer = substr($buffer, 4);
00569                     $size -= 4;
00570 
00571                     //read length of the property
00572                     $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
00573                     if ($hresult !== NOERROR) {
00574                         ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
00575                         return $hresult;
00576                     }
00577                     $size -= 4;
00578                     if ($size < $len) {
00579                         return MAPI_E_CORRUPT_DATA;
00580                     }
00581 
00582                     if ($propTag & MV_FLAG) {
00583                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
00584                         if ($hresult !== NOERROR) {
00585                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00586                             return $hresult;
00587                         }
00588                     }
00589                     else {
00590                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
00591                         if ($hresult !== NOERROR) {
00592                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00593                             return $hresult;
00594                         }
00595                     }
00596                     //location fix. it looks like tnef uses this value for location
00597                     if (mapi_prop_id($propTag) == 0x8342) {
00598                         $mapiprops[$this->props["location"]] = $mapiprops[$propTag];
00599                         unset($mapiprops[$propTag]);
00600                     }
00601 
00602                     $size -= $len;
00603 
00604                     //Re-align
00605                     $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
00606                     $size -= $len & 3 ? 4 - ($len & 3) : 0;
00607                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00608                     break;
00609 
00610                 case PT_UNICODE:
00611                     if($size < 8) {
00612                         return MAPI_E_CORRUPT_DATA;
00613                     }
00614                     // Skip next 4 bytes, it's always '1' (ULONG)
00615                     $buffer = substr($buffer, 4);
00616                     $size -= 4;
00617 
00618                     //read length of the property
00619                     $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
00620                     if ($hresult !== NOERROR) {
00621                         ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
00622                         return $hresult;
00623                     }
00624                     $size -= 4;
00625                     if ($size < $len) {
00626                         return MAPI_E_CORRUPT_DATA;
00627                     }
00628                     //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8
00629                     $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag));
00630 
00631                     if ($propTag & MV_FLAG) {
00632                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
00633                         if ($hresult !== NOERROR) {
00634                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00635                             return $hresult;
00636                         }
00637                     }
00638                     else {
00639                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
00640                         if ($hresult !== NOERROR) {
00641                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00642                             return $hresult;
00643                         }
00644                     }
00645 
00646                     //location fix. it looks like tnef uses this value for location
00647                     if (mapi_prop_id($propTag) == 0x8342) {
00648                         $mapiprops[$this->props["location"]] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
00649                         unset($mapiprops[$propTag]);
00650                     }
00651 
00652                     //convert from unicode to windows encoding
00653                     if (isset($mapiprops[$propTag])) $mapiprops[$propTag] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]);
00654                     $size -= $len;
00655 
00656                     //Re-align
00657                     $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
00658                     $size -= $len & 3 ? 4 - ($len & 3) : 0;
00659                     if (isset($mapiprops[$propTag])) ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]);
00660                     break;
00661 
00662                 case PT_OBJECT:        // PST sends PT_OBJECT data. Treat as PT_BINARY
00663                 case PT_BINARY:
00664                     if($size < self::BYTE) {
00665                         return MAPI_E_CORRUPT_DATA;
00666                     }
00667                     // Skip next 4 bytes, it's always '1' (ULONG)
00668                     $buffer = substr($buffer, 4);
00669                     $size -= 4;
00670 
00671                     //read length of the property
00672                     $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len);
00673                     if ($hresult !== NOERROR) {
00674                         ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length");
00675                         return $hresult;
00676                     }
00677                     $size -= 4;
00678 
00679                     if (mapi_prop_type($propTag) == PT_OBJECT) {
00680                         // IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ]
00681                         $buffer = substr($buffer, 16);
00682                         $size -= 16;
00683                         $len -= 16;
00684                     }
00685 
00686                     if ($size < $len) {
00687                         return MAPI_E_CORRUPT_DATA;
00688                     }
00689 
00690                     if ($propTag & MV_FLAG) {
00691                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]);
00692                         if ($hresult !== NOERROR) {
00693                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00694                             return $hresult;
00695                         }
00696                     }
00697                     else {
00698                         $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]);
00699                         if ($hresult !== NOERROR) {
00700                             ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer");
00701                             return $hresult;
00702                         }
00703                     }
00704 
00705                     $size -= $len;
00706 
00707                     //Re-align
00708                     $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0));
00709                     $size -= $len & 3 ? 4 - ($len & 3) : 0;
00710                     ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".bin2hex($mapiprops[$propTag]));
00711                     break;
00712 
00713                 default:
00714                     return MAPI_E_INVALID_PARAMETER;
00715                     break;
00716             }
00717         }
00718         return NOERROR;
00719     }
00720 }
00721 ?>