/** * Reads a single mapi prop. * * @param string &$buffer * @param int $size * @param mixed &$read * @param array &$mapiprops * * @access private * @return int */ private function readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) { $propTag = 0; $len = 0; $origSize = $size; $isNamedId = 0; $namedProp = 0; $count = 0; $mvProp = 0; $guid = 0; if ($size < 8) { return MAPI_E_NOT_FOUND; } $hresult = $this->readFromTnefStream($buffer, self::DWORD, $propTag); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property tag from the stream."); return $hresult; } $size -= 4; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop type:" . dechex(mapi_prop_type($propTag))); ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x" . sprintf("%04x", mapi_prop_id($propTag))); if (mapi_prop_id($propTag) >= 0x8000) { // Named property, first read GUID, then name/id if ($size < 24) { ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt guid size for named property:" . dechex($propTag)); return MAPI_E_CORRUPT_DATA; } //strip GUID & name/id $hresult = $this->readBuffer($buffer, 16, $guid); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } $size -= 16; //it is not used and is here only for eventual debugging $readableGuid = unpack("VV/v2v/n4n", $guid); $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}", $readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'], $readableGuid['n1'], $readableGuid['n2'], $readableGuid['n3'], $readableGuid['n4']); ZLog::Write(LOGLEVEL_DEBUG, "TNEF: guid:{$readableGuid}"); $hresult = $this->readFromTnefStream($buffer, self::DWORD, $isNamedId); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum."); return $hresult; } $size -= 4; if ($isNamedId != 0) { // A string name follows //read length of the property $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } //read the name of the property, eg Keywords $hresult = $this->readBuffer($buffer, $len, $namedProp); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; } else { $hresult = $this->readFromTnefStream($buffer, self::DWORD, $namedProp); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); return $hresult; } ZLog::Write(LOGLEVEL_DEBUG, "TNEF: named: 0x" . sprintf("%04x", $namedProp)); $size -= 4; } if ($this->store !== false) { $named = mapi_getidsfromnames($this->store, array($namedProp), array(makeguid($readableGuid))); $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0])); } else { ZLog::Write(LOGLEVEL_WARN, "TNEF: Store not available. It is impossible to get named properties"); } } ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x" . sprintf("%04x", mapi_prop_id($propTag)) . " " . sprintf("%04x", mapi_prop_type($propTag))); if ($propTag & MV_FLAG) { if ($size < 4) { return MAPI_E_CORRUPT_DATA; } //read the number of properties $hresult = $this->readFromTnefStream($buffer, self::DWORD, $count); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading number of properties for:" . dechex($propTag)); return $hresult; } $size -= 4; } else { $count = 1; } for ($mvProp = 0; $mvProp < $count; $mvProp++) { switch (mapi_prop_type($propTag) & ~MV_FLAG) { case PT_I2: case PT_LONG: $hresult = $this->readBuffer($buffer, 4, $value); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } $value = unpack("V", $value); $value = intval($value[1], 16); if ($propTag & MV_FLAG) { $mapiprops[$propTag][] = $value; } else { $mapiprops[$propTag] = $value; } $size -= 4; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: int or long propvalue:" . $value); break; case PT_R4: if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } $size -= 4; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); break; case PT_BOOLEAN: $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } $size -= 4; //reported by dw2412 //cast to integer as it evaluates to 1 or 0 because //a non empty string evaluates to true :( $mapiprops[$propTag] = (int) bin2hex($mapiprops[$propTag][0]); ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); break; case PT_SYSTIME: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } //we have to convert the filetime to an unixtime timestamp $filetime = unpack("V2v", $mapiprops[$propTag]); //php on 64-bit systems converts unsigned values differently than on 32 bit systems //we need this "fix" in order to get the same values on both types of systems $filetime['v2'] = substr(sprintf("%08x", $filetime['v2']), -8); $filetime['v1'] = substr(sprintf("%08x", $filetime['v1']), -8); $filetime = hexdec($filetime['v2'] . $filetime['v1']); $filetime = ($filetime - 116444736000000000) / 10000000; $mapiprops[$propTag] = $filetime; // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough if ($propTag == PR_START_DATE) { $mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $filetime; } if ($propTag == PR_END_DATE) { $mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $filetime; } $size -= 8; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); break; case PT_DOUBLE: case PT_CURRENCY: case PT_I8: case PT_APPTIME: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } $size -= 8; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); break; case PT_STRING8: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } //location fix. it looks like tnef uses this value for location if (mapi_prop_id($propTag) == 0x8342) { $mapiprops[$this->props["location"]] = $mapiprops[$propTag]; unset($mapiprops[$propTag]); } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); break; case PT_UNICODE: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8 $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag)); if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } //location fix. it looks like tnef uses this value for location if (mapi_prop_id($propTag) == 0x8342) { $mapiprops[$this->props["location"]] = iconv("UCS-2", "windows-1252", $mapiprops[$propTag]); unset($mapiprops[$propTag]); } //convert from unicode to windows encoding if (isset($mapiprops[$propTag])) { $mapiprops[$propTag] = iconv("UCS-2", "windows-1252", $mapiprops[$propTag]); } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; if (isset($mapiprops[$propTag])) { ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . $mapiprops[$propTag]); } break; case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY // PST sends PT_OBJECT data. Treat as PT_BINARY case PT_BINARY: if ($size < self::BYTE) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); return $hresult; } $size -= 4; if (mapi_prop_type($propTag) == PT_OBJECT) { // IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ] $buffer = substr($buffer, 16); $size -= 16; $len -= 16; } if ($size < $len) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); return $hresult; } } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:" . bin2hex($mapiprops[$propTag])); break; default: return MAPI_E_INVALID_PARAMETER; break; } } return NOERROR; }
/** * Gets the properties from a MAPI object and sets them in the Sync object according to mapping * * @param SyncObject &$message * @param mixed $mapimessage * @param array $mapping * * @access private * @return */ private function getPropsFromMAPI(&$message, $mapimessage, $mapping) { $messageprops = $this->getProps($mapimessage, $mapping); foreach ($mapping as $asprop => $mapiprop) { // Get long strings via openproperty if (isset($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) { if ($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT || $messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) { $messageprops[$mapiprop] = MAPIUtils::readPropStream($mapimessage, $mapiprop); } } if (isset($messageprops[$mapiprop])) { if (mapi_prop_type($mapiprop) == PT_BOOLEAN) { // Force to actual '0' or '1' if ($messageprops[$mapiprop]) { $message->{$asprop} = 1; } else { $message->{$asprop} = 0; } } else { // Special handling for PR_MESSAGE_FLAGS if ($mapiprop == PR_MESSAGE_FLAGS) { $message->{$asprop} = $messageprops[$mapiprop] & 1; } else { if ($mapiprop == PR_RTF_COMPRESSED) { //do not send rtf to the mobile continue; } else { if (is_array($messageprops[$mapiprop])) { $message->{$asprop} = array_map("w2u", $messageprops[$mapiprop]); } else { if (mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { $message->{$asprop} = w2u($messageprops[$mapiprop]); } else { $message->{$asprop} = $messageprops[$mapiprop]; } } } } } } } }
/** * Function which saves the exception data in an attachment. * @param array $exception_props the exception data (like any other MAPI appointment) * @param array $exception_recips list of recipients * @param mapi_message $copy_attach_from mapi message from which attachments should be copied * @return array properties of the exception */ function createExceptionAttachment($exception_props, $exception_recips = array(), $copy_attach_from = false) { // Create new attachment. $attachment = mapi_message_createattach($this->message); $props = array(); $props[PR_ATTACHMENT_FLAGS] = 2; $props[PR_ATTACHMENT_HIDDEN] = true; $props[PR_ATTACHMENT_LINKID] = 0; $props[PR_ATTACH_FLAGS] = 0; $props[PR_ATTACH_METHOD] = 5; $props[PR_DISPLAY_NAME] = "Exception"; $props[PR_EXCEPTION_STARTTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]); $props[PR_EXCEPTION_ENDTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["duedate"]]); mapi_message_setprops($attachment, $props); $imessage = mapi_attach_openobj($attachment, MAPI_CREATE | MAPI_MODIFY); if ($copy_attach_from) { $attachmentTable = mapi_message_getattachmenttable($copy_attach_from); if ($attachmentTable) { $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); foreach ($attachments as $attach_props) { $attach_old = mapi_message_openattach($copy_attach_from, (int) $attach_props[PR_ATTACH_NUM]); $attach_newResourceMsg = mapi_message_createattach($imessage); mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0); mapi_savechanges($attach_newResourceMsg); } } } $props = $props + $exception_props; // FIXME: the following piece of code is written to fix the creation // of an exception. This is only a quickfix as it is not yet possible // to change an existing exception. // remove mv properties when needed foreach ($props as $propTag => $propVal) { if ((mapi_prop_type($propTag) & MV_FLAG) == MV_FLAG && is_null($propVal)) { unset($props[$propTag]); } } mapi_message_setprops($imessage, $props); $this->setExceptionRecipients($imessage, $exception_recips, true); mapi_message_savechanges($imessage); mapi_message_savechanges($attachment); }
function _getPropsFromMAPI(&$message, $mapimessage, $mapping) { foreach ($mapping as $asprop => $mapipropstring) { // Get the MAPI property we need to be reading $mapiprop = $this->_getPropIDFromString($mapipropstring); $prop = mapi_getprops($mapimessage, array($mapiprop)); // Get long strings via openproperty if (isset($prop[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) { if ($prop[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == -2147024882 || $prop[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == 2147942414.0) { // 64 bit $prop = array($mapiprop => readPropStream($mapimessage, $mapiprop)); } } if (isset($prop[$mapiprop])) { if (mapi_prop_type($mapiprop) == PT_BOOLEAN) { // Force to actual '0' or '1' if ($prop[$mapiprop]) { $message->{$asprop} = 1; } else { $message->{$asprop} = 0; } } else { // Special handling for PR_MESSAGE_FLAGS if ($mapiprop == PR_MESSAGE_FLAGS) { $message->{$asprop} = $prop[$mapiprop] & 1; } else { if ($mapiprop == PR_RTF_COMPRESSED) { //do not send rtf to the mobile continue; } else { if (is_array($prop[$mapiprop])) { $message->{$asprop} = array_map("w2u", $prop[$mapiprop]); } else { if (mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { $message->{$asprop} = w2u($prop[$mapiprop]); } else { $message->{$asprop} = $prop[$mapiprop]; } } } } } } } }
function _readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) { $propTag = 0; $len = 0; $origSize = $size; $isNamedId = 0; $namedProp = 0; $count = 0; $mvProp = 0; $guid = 0; if ($size < 8) { return MAPI_E_NOT_FOUND; } $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $propTag); if ($hresult !== NOERROR) { debugLog("There was an error reading a mapi property tag from the stream."); return $hresult; } $size -= 4; //debugLog("mapi prop type:".dechex(mapi_prop_type($propTag))); //debugLog("mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))); if (mapi_prop_id($propTag) >= 0x8000) { // Named property, first read GUID, then name/id if ($size < 24) { debugLog("Corrupt guid size for named property:" . dechex($propTag)); return MAPI_E_CORRUPT_DATA; } //strip GUID & name/id $hresult = $this->_readBuffer($buffer, 16, $guid); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } $size -= 16; //it is not used and is here only for eventual debugging $readableGuid = unpack("VV/v2v/n4n", $guid); $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}", $readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'], $readableGuid['n1'], $readableGuid['n2'], $readableGuid['n3'], $readableGuid['n4']); //debugLog("guid:$readableGuid"); $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $isNamedId); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property checksum."); return $hresult; } $size -= 4; if ($isNamedId != 0) { // A string name follows //read length of the property $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len); if ($hresult !== NOERROR) { debugLog("There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } //read the name of the property, eg Keywords $hresult = $this->_readBuffer($buffer, $len, $namedProp); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; } else { $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $namedProp); if ($hresult !== NOERROR) { debugLog("There was an error reading mapi property's length"); return $hresult; } //debugLog("named: 0x".sprintf("%04x", $namedProp)); $size -= 4; } if ($this->_store !== false) { $named = mapi_getidsfromnames($this->_store, array($namedProp), array(makeguid($readableGuid))); $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0])); } else { debugLog("Store not available. It is impossible to get named properties"); } } //debugLog("mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag))); if ($propTag & MV_FLAG) { if ($size < 4) { return MAPI_E_CORRUPT_DATA; } //read the number of properties $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $count); if ($hresult !== NOERROR) { debugLog("There was an error reading number of properties for:" . dechex($propTag)); return $hresult; } $size -= 4; } else { $count = 1; } for ($mvProp = 0; $mvProp < $count; $mvProp++) { switch (mapi_prop_type($propTag) & ~MV_FLAG) { case PT_I2: case PT_LONG: $hresult = $this->_readBuffer($buffer, 4, $value); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } $value = unpack("V", $value); $value = intval($value[1], 16); if ($propTag & MV_FLAG) { $mapiprops[$propTag][] = $value; } else { $mapiprops[$propTag] = $value; } $size -= 4; //debugLog("int or long propvalue:".$value); break; case PT_R4: if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } $size -= 4; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_BOOLEAN: $hresult = $this->_readBuffer($buffer, 4, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } $size -= 4; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_SYSTIME: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } //we have to convert the filetime to an unixtime timestamp $filetime = unpack("V2v", $mapiprops[$propTag]); $filetime = hexdec(sprintf("%08x%08x", $filetime['v2'], $filetime['v1'])); $filetime = ($filetime - 116444736000000000) / 10000000; $mapiprops[$propTag] = $filetime; // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough if ($propTag == PR_START_DATE) { $namedStartTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820d"); $mapiprops[$namedStartTime] = $filetime; $namedCommonStart = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8516"); $mapiprops[$namedCommonStart] = $filetime; } if ($propTag == PR_END_DATE) { $namedEndTime = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x820e"); $mapiprops[$namedEndTime] = $filetime; $namedCommonEnd = GetPropIDFromString($this->_store, "PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8517"); $mapiprops[$namedCommonEnd] = $filetime; } $size -= 8; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_DOUBLE: case PT_CURRENCY: case PT_I8: case PT_APPTIME: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, 8, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } $size -= 8; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_STRING8: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len); if ($hresult !== NOERROR) { debugLog("There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } //location fix. it looks like tnef uses this value for location if (mapi_prop_id($propTag) == 0x8342) { $namedLocation = GetPropIDFromString($this->_store, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208"); $mapiprops[$namedLocation] = $mapiprops[$propTag]; unset($mapiprops[$propTag]); } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_UNICODE: if ($size < 8) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len); if ($hresult !== NOERROR) { debugLog("There was an error reading mapi property's length"); return $hresult; } $size -= 4; if ($size < $len) { return MAPI_E_CORRUPT_DATA; } //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8 $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag)); if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } //location fix. it looks like tnef uses this value for location if (mapi_prop_id($propTag) == 0x8342) { $namedLocation = GetPropIDFromString($this->_store, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208"); $mapiprops[$namedLocation] = $mapiprops[$propTag]; unset($mapiprops[$propTag]); $mapiprops[$namedLocation] = iconv("UCS-2", "windows-1252", $mapiprops[$namedLocation]); } //convert from unicode to windows encoding if (isset($mapiprops[$propTag])) { $mapiprops[$propTag] = iconv("UCS-2", "windows-1252", $mapiprops[$propTag]); } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; //debugLog("propvalue:".$mapiprops[$propTag]); break; case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY // PST sends PT_OBJECT data. Treat as PT_BINARY case PT_BINARY: if ($size < ZP_BYTE) { return MAPI_E_CORRUPT_DATA; } // Skip next 4 bytes, it's always '1' (ULONG) $buffer = substr($buffer, 4); $size -= 4; //read length of the property $hresult = $this->_readFromTnefStream($buffer, ZP_DWORD, $len); if ($hresult !== NOERROR) { debugLog("There was an error reading mapi property's length"); return $hresult; } $size -= 4; if (mapi_prop_type($propTag) == PT_OBJECT) { // TODO: IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ] $buffer = substr($buffer, 16); $size -= 16; $len -= 16; } if ($size < $len) { return MAPI_E_CORRUPT_DATA; } if ($propTag & MV_FLAG) { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag][]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } else { $hresult = $this->_readBuffer($buffer, $len, $mapiprops[$propTag]); if ($hresult !== NOERROR) { debugLog("There was an error reading stream property buffer"); return $hresult; } } $size -= $len; //Re-align $buffer = substr($buffer, $len & 3 ? 4 - ($len & 3) : 0); $size -= $len & 3 ? 4 - ($len & 3) : 0; //debugLog("propvalue:".bin2hex($mapiprops[$propTag])); break; default: return MAPI_E_INVALID_PARAMETER; break; } } return NOERROR; }
function _getPropsFromMAPI(&$message, $mapimessage, $mapping) { foreach ($mapping as $asprop => $mapipropstring) { // Get the MAPI property we need to be reading $mapiprop = $this->_getPropIDFromString($mapipropstring); if ($mapiprop == PR_BODY) { // special handling for PR_BODY $message->{$asprop} = str_replace("\n", "\r\n", w2u(str_replace("\r", "", mapi_openproperty($mapimessage, PR_BODY)))); if (strlen($message->{$asprop}) > 0) { $message->bodytruncated = 0; $message->bodysize = strlen($message->body); } } else { // Normal property $prop = mapi_getprops($mapimessage, array($mapiprop)); if (isset($prop[$mapiprop])) { if (mapi_prop_type($mapiprop) == PT_BOOLEAN) { // Force to actual '0' or '1' if ($prop[$mapiprop]) { $message->{$asprop} = 1; } else { $message->{$asprop} = 0; } } else { // Special handling for PR_MESSAGE_FLAGS if ($mapiprop == PR_MESSAGE_FLAGS) { $message->{$asprop} = $prop[$mapiprop] & 1; } else { if (is_array($prop[$mapiprop])) { $message->{$asprop} = array_map("w2u", $prop[$mapiprop]); } else { if (mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { $message->{$asprop} = w2u($prop[$mapiprop]); } else { $message->{$asprop} = $prop[$mapiprop]; } } } } } } } }
function setProperty($property, $value, &$props, $properties) { if (isset($value) && isset($properties[$property])) { //multi values have to be saved as an array if ($properties[$property] & MV_FLAG) { $value = explode(';', $value); } if (mapi_prop_type($properties[$property]) == PT_SYSTIME && !DATES_AS_TIMESTAMPS) { $value = strtotime($value); } if (mapi_prop_type($properties[$property]) != PT_BINARY && mapi_prop_type($properties[$property]) != PT_MV_BINARY) { if (is_array($value)) { $props[$properties[$property]] = array_map("to_windows1252", $value); } else { $props[$properties[$property]] = to_windows1252($value); } } else { $props[$properties[$property]] = to_windows1252($value); } } }