define('PR_INTERNET_REFERENCES', mapi_prop_tag(PT_STRING8, 0x1039)); define('PR_IN_REPLY_TO_ID', mapi_prop_tag(PT_STRING8, 0x1042)); define('PR_INTERNET_MESSAGE_ID', mapi_prop_tag(PT_STRING8, 0x1035)); // for hidden folders define('PR_ATTR_HIDDEN', mapi_prop_tag(PT_BOOLEAN, 0x10f4)); /** * Addressbook detail properties. * It is not defined by MAPI, but to keep in sync with the interface of outlook we have to use these * properties. Outlook actually uses these properties for it's addressbook details. */ define('PR_HOME2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a2f)); define('PR_BUSINESS2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a1b)); define('PR_EMS_AB_PROXY_ADDRESSES', mapi_prop_tag(PT_TSTRING, 0x800f)); define('PR_EMS_AB_PROXY_ADDRESSES_MV', mapi_prop_tag(PT_MV_TSTRING, 0x800f)); define('PR_EMS_AB_MANAGER', mapi_prop_tag(PT_BINARY, 0x8005)); define('PR_EMS_AB_REPORTS', mapi_prop_tag(PT_BINARY, 0x800e)); define('PR_EMS_AB_REPORTS_MV', mapi_prop_tag(PT_MV_BINARY, 0x800e)); define('PR_EMS_AB_IS_MEMBER_OF_DL', mapi_prop_tag(PT_MV_BINARY, 0x8008)); define('PR_EMS_AB_OWNER', mapi_prop_tag(PT_BINARY, 0x800c)); define('PR_EMS_AB_ROOM_CAPACITY', mapi_prop_tag(PT_LONG, 0x807)); define('PR_EMS_AB_TAGGED_X509_CERT', mapi_prop_tag(PT_MV_BINARY, 0x8c6a)); define('PR_EC_ARCHIVE_SERVERS', mapi_prop_tag(PT_MV_TSTRING, 0x67c4)); /* zarafa contacts provider properties */ define('PR_ZC_CONTACT_STORE_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x11)); define('PR_ZC_CONTACT_FOLDER_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x12)); define('PR_ZC_CONTACT_FOLDER_NAMES', mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE + 0x13)); //Properties defined for Z-Push define('PR_TODO_ITEM_FLAGS', mapi_prop_tag(PT_LONG, 0xe2b)); define('PR_LOCAL_COMMIT_TIME_MAX', mapi_prop_tag(PT_SYSTIME, 0x670a)); define('PR_DELETED_MSG_COUNT', mapi_prop_tag(PT_LONG, 0x6640));
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; }
/** * Create a MAPI restriction in order to check if a contact has a picture * * @access public * @return array */ public static function GetContactPicRestriction() { return array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => mapi_prop_tag(PT_BOOLEAN, 0x7fff), VALUE => true)); }
/** * Check wether a call to mapi_getprops returned errors for some properties. * mapi_getprops function tries to get values of properties requested but somehow if * if a property value can not be fetched then it changes type of property tag as PT_ERROR * and returns error for that particular property, probable errors * that can be returned as value can be MAPI_E_NOT_FOUND, MAPI_E_NOT_ENOUGH_MEMORY * * @param long $property Property to check for error * @param Array $propArray An array of properties * @return mixed Gives back false when there is no error, if there is, gives the error */ function propIsError($property, $propArray) { if (array_key_exists(mapi_prop_tag(PT_ERROR, mapi_prop_id($property)), $propArray)) { return $propArray[mapi_prop_tag(PT_ERROR, mapi_prop_id($property))]; } else { return false; } }
* but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Consult LICENSE file for details * ************************************************/ include_once "syncworker.php"; include_once 'mapi/mapi.util.php'; include_once 'mapi/mapidefs.php'; include_once 'mapi/mapitags.php'; include_once 'mapi/mapicode.php'; include_once 'mapi/mapiguid.php'; define('PR_EMS_AB_THUMBNAIL_PHOTO', mapi_prop_tag(PT_BINARY, 0x8c9e)); class Kopano extends SyncWorker { const NAME = "Z-Push Kopano GAB Sync"; const VERSION = "1.1"; private $session; private $defaultstore; private $store; private $mainUser; private $targetStore; private $folderCache; private $storeCache; private $mapiprops; /** * Constructor */
function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { $mimeParams = array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\r\n", 'charset' => 'utf-8'); $mimeObject = new Mail_mimeDecode($mimeParams['input'], $mimeParams['crlf']); $message = $mimeObject->decode($mimeParams); // Open the outbox and create the message there $storeprops = mapi_getprops($this->_defaultstore, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID)); if (!isset($storeprops[PR_IPM_OUTBOX_ENTRYID])) { debugLog("Outbox not found to create message"); return false; } $outbox = mapi_msgstore_openentry($this->_defaultstore, $storeprops[PR_IPM_OUTBOX_ENTRYID]); if (!$outbox) { debugLog("Unable to open outbox"); return false; } $mapimessage = mapi_folder_createmessage($outbox); mapi_setprops($mapimessage, array(PR_SUBJECT => u2w($mimeObject->_decodeHeader($message->headers["subject"])), PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID], PR_MESSAGE_CLASS => "IPM.Note", PR_MESSAGE_DELIVERY_TIME => time())); if (isset($message->headers["x-priority"])) { switch ($message->headers["x-priority"]) { case 1: case 2: $priority = PRIO_URGENT; $importance = IMPORTANCE_HIGH; break; case 4: case 5: $priority = PRIO_NONURGENT; $importance = IMPORTANCE_LOW; break; case 3: default: $priority = PRIO_NORMAL; $importance = IMPORTANCE_NORMAL; break; } mapi_setprops($mapimessage, array(PR_IMPORTANCE => $importance, PR_PRIORITY => $priority)); } $addresses = array(); $toaddr = $ccaddr = $bccaddr = array(); if (isset($message->headers["to"])) { $toaddr = Mail_RFC822::parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = Mail_RFC822::parseAddressList($message->headers["cc"]); } if (isset($message->headers["bcc"])) { $bccaddr = Mail_RFC822::parseAddressList($message->headers["bcc"]); } // Add recipients $recips = array(); if (isset($toaddr)) { foreach (array(MAPI_TO => $toaddr, MAPI_CC => $ccaddr, MAPI_BCC => $bccaddr) as $type => $addrlist) { foreach ($addrlist as $addr) { $mapirecip[PR_ADDRTYPE] = "SMTP"; $mapirecip[PR_EMAIL_ADDRESS] = $addr->mailbox . "@" . $addr->host; if (isset($addr->personal) && strlen($addr->personal) > 0) { $mapirecip[PR_DISPLAY_NAME] = u2w($mimeObject->_decodeHeader($addr->personal)); } else { $mapirecip[PR_DISPLAY_NAME] = $mapirecip[PR_EMAIL_ADDRESS]; } $mapirecip[PR_RECIPIENT_TYPE] = $type; $mapirecip[PR_ENTRYID] = mapi_createoneoff($mapirecip[PR_DISPLAY_NAME], $mapirecip[PR_ADDRTYPE], $mapirecip[PR_EMAIL_ADDRESS]); array_push($recips, $mapirecip); } } } mapi_message_modifyrecipients($mapimessage, 0, $recips); // Loop through subparts. We currently only support real single-level // multiparts and partly multipart/related/mixed for attachments. // The PDA currently only does this because you are adding // an attachment and the type will be multipart/mixed or multipart/alternative. $body = ""; if ($message->ctype_primary == "multipart" && ($message->ctype_secondary == "mixed" || $message->ctype_secondary == "alternative")) { foreach ($message->parts as $part) { if ($part->ctype_primary == "text" && $part->ctype_secondary == "plain" && isset($part->body)) { // discard any other kind of text, like html $body .= u2w($part->body); // assume only one text body } elseif ($part->ctype_primary == "ms-tnef" || $part->ctype_secondary == "ms-tnef") { $zptnef = new ZPush_tnef($this->_defaultstore); $mapiprops = array(); $zptnef->extractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { //check if it is a recurring item $tnefrecurr = GetPropIDFromString($this->_defaultstore, "PT_BOOLEAN:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x5"); if (isset($mapiprops[$tnefrecurr])) { $this->_handleRecurringItem($mapimessage, $mapiprops); } mapi_setprops($mapimessage, $mapiprops); } else { debugLog("TNEF: Mapi props array was empty"); } } elseif ($part->ctype_primary == "multipart" && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "related")) { if (is_array($part->parts)) { foreach ($part->parts as $part2) { if (isset($part2->disposition) && ($part2->disposition == "inline" || $part2->disposition == "attachment")) { $this->_storeAttachment($mapimessage, $part2); } } } } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "calendar") { $zpical = new ZPush_ical($this->_defaultstore); $mapiprops = array(); $zpical->extractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { mapi_setprops($mapimessage, $mapiprops); } else { debugLog("ICAL: Mapi props array was empty"); } } else { $this->_storeAttachment($mapimessage, $part); } } } else { $body = u2w($message->body); } if ($forward) { $orig = $forward; } if ($reply) { $orig = $reply; } if (isset($orig) && $orig) { // Append the original text body for reply/forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); if ($fwmessage) { //update icon when forwarding or replying message if ($forward) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($reply) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); $stream = mapi_openproperty($fwmessage, PR_BODY, IID_IStream, 0, 0); $fwbody = ""; while (1) { $data = mapi_stream_read($stream, 1024); if (strlen($data) == 0) { break; } $fwbody .= $data; } if (strlen($body) > 0) { if ($forward) { // During a forward, we have to add the forward header ourselves. This is because // normally the forwarded message is added as an attachment. However, we don't want this // because it would be rather complicated to copy over the entire original message due // to the lack of IMessage::CopyTo .. $fwmessageprops = mapi_getprops($fwmessage, array(PR_SENT_REPRESENTING_NAME, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SUBJECT, PR_CLIENT_SUBMIT_TIME)); $body .= "\r\n\r\n"; $body .= "-----Original Message-----\r\n"; if (isset($fwmessageprops[PR_SENT_REPRESENTING_NAME])) { $body .= "From: " . $fwmessageprops[PR_SENT_REPRESENTING_NAME] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_TO]) && strlen($fwmessageprops[PR_DISPLAY_TO]) > 0) { $body .= "To: " . $fwmessageprops[PR_DISPLAY_TO] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_CC]) && strlen($fwmessageprops[PR_DISPLAY_CC]) > 0) { $body .= "Cc: " . $fwmessageprops[PR_DISPLAY_CC] . "\r\n"; } if (isset($fwmessageprops[PR_CLIENT_SUBMIT_TIME])) { $body .= "Sent: " . strftime("%x %X", $fwmessageprops[PR_CLIENT_SUBMIT_TIME]) . "\r\n"; } if (isset($fwmessageprops[PR_SUBJECT])) { $body .= "Subject: " . $fwmessageprops[PR_SUBJECT] . "\r\n"; } $body .= "\r\n"; } $body .= $fwbody; } } else { debugLog("Unable to open item with id {$orig} for forward/reply"); } } if ($forward) { // Add attachments from the original message in a forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); $attachtable = mapi_message_getattachmenttable($fwmessage); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { $attach = mapi_message_openattach($fwmessage, $row[PR_ATTACH_NUM]); $newattach = mapi_message_createattach($mapimessage); // Copy all attachments from old to new attachment $attachprops = mapi_getprops($attach); mapi_setprops($newattach, $attachprops); if (isset($attachprops[mapi_prop_tag(PT_ERROR, mapi_prop_id(PR_ATTACH_DATA_BIN))])) { // Data is in a stream $srcstream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); $dststream = mapi_openpropertytostream($newattach, PR_ATTACH_DATA_BIN, MAPI_MODIFY | MAPI_CREATE); while (1) { $data = mapi_stream_read($srcstream, 4096); if (strlen($data) == 0) { break; } mapi_stream_write($dststream, $data); } mapi_stream_commit($dststream); } mapi_savechanges($newattach); } } } mapi_setprops($mapimessage, array(PR_BODY => $body)); mapi_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); return true; }
print "Please enable php-mapi module for commandline usage"; exit; } define('PT_BOOLEAN', 11); /* 16-bit boolean (non-zero true) */ define('PT_STRING8', 30); /* Null terminated 8-bit character string */ define('PT_TSTRING', PT_STRING8); define('PT_BINARY', 258); /* Uninterpreted (counted byte array) */ define('PT_SYSTIME', 64); /* FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */ define('PR_DEFAULT_STORE', mapi_prop_tag(PT_BOOLEAN, 0x3400)); define('PR_ENTRYID', mapi_prop_tag(PT_BINARY, 0xfff)); define('PR_IPM_WASTEBASKET_ENTRYID', mapi_prop_tag(PT_BINARY, 0x35e3)); define('PR_CREATION_TIME', mapi_prop_tag(PT_SYSTIME, 0x3007)); function greaterDate($start_date, $daysBeforeDeleted) { return strtotime($start_date) - strtotime(date('Y-m-d G:i:s', strtotime("-{$daysBeforeDeleted} days"))) < 0 ? 1 : 0; } // Add the SVN revision number to the version $mapi_version = str_replace('-', '.', phpversion('mapi')); // Log in to Zarafa server if (version_compare($mapi_version, '7.2.0.46424', '>=')) { $l_rSession = mapi_logon_zarafa($l_sUsername, $l_sPassword, $l_sServer, NULL, NULL, NULL, NULL); } else { $l_rSession = mapi_logon_zarafa($l_sUsername, $l_sPassword, $l_sServer); } echo (mapi_last_hresult() == 0 ? "Logged in successfully" : "Some error in login") . "\n"; // Get a table with the message stores within this session $l_rTableStores = mapi_getmsgstorestable($l_rSession);
/** * 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; }
} // The purpose of this script is to process the unprocessed meeting requests in the inbox folder $l_sUsername = $argv[1]; $l_sPassword = ''; $l_sServer = 'file:///var/run/zarafa'; // Include MAPI PHP-EXT define('BASE_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . "/"); set_include_path(BASE_PATH . PATH_SEPARATOR . "/usr/share/php/"); require "mapi/mapi.util.php"; require "mapi/mapicode.php"; require "mapi/mapidefs.php"; require "mapi/mapitags.php"; require "mapi/mapiguid.php"; require "mapi/class.meetingrequest.php"; require "mapi/class.freebusypublish.php"; define('PR_PROCESSED', mapi_prop_tag(PT_BOOLEAN, 0x7d01)); if (!function_exists('hex2bin')) { function hex2bin($data) { return pack("H*", $data); } } /** * Publishing the FreeBusy information of the default calendar. The * folderentryid argument is used to check if the default calendar * should be updated or not. * * @param $store MAPIobject Store object of the store that needs publishing * @param $folderentryid binary entryid of the folder that needs to be updated. */ function publishFreeBusy($store, $l_rSession, $folderentryid = false)
define('PR_ATTR_HIDDEN', mapi_prop_tag(PT_BOOLEAN, 0x10f4)); /** * Addressbook detail properties. * It is not defined by MAPI, but to keep in sync with the interface of outlook we have to use these * properties. Outlook actually uses these properties for it's addressbook details. */ define('PR_HOME2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a2f)); define('PR_BUSINESS2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a1b)); define('PR_EMS_AB_PROXY_ADDRESSES', mapi_prop_tag(PT_TSTRING, 0x800f)); define('PR_EMS_AB_PROXY_ADDRESSES_MV', mapi_prop_tag(PT_MV_TSTRING, 0x800f)); define('PR_EMS_AB_MANAGER', mapi_prop_tag(PT_BINARY, 0x8005)); define('PR_EMS_AB_REPORTS', mapi_prop_tag(PT_BINARY, 0x800e)); define('PR_EMS_AB_REPORTS_MV', mapi_prop_tag(PT_MV_BINARY, 0x800e)); define('PR_EMS_AB_IS_MEMBER_OF_DL', mapi_prop_tag(PT_MV_BINARY, 0x8008)); define('PR_EMS_AB_OWNER', mapi_prop_tag(PT_BINARY, 0x800c)); define('PR_EMS_AB_ROOM_CAPACITY', mapi_prop_tag(PT_LONG, 0x807)); define('PR_EMS_AB_TAGGED_X509_CERT', mapi_prop_tag(PT_MV_BINARY, 0x8c6a)); define('PR_EMS_AB_THUMBNAIL_PHOTO', mapi_prop_tag(PT_MV_BINARY, 0x8c9e)); define('PR_EC_ARCHIVE_SERVERS', mapi_prop_tag(PT_MV_TSTRING, 0x67c4)); /* zarafa contacts provider properties */ define('PR_ZC_CONTACT_STORE_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x11)); define('PR_ZC_CONTACT_FOLDER_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x12)); define('PR_ZC_CONTACT_FOLDER_NAMES', mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE + 0x13)); /* zarafa specific properties for optimization of imap functionality */ define('PR_EC_IMAP_EMAIL', mapi_prop_tag(PT_BINARY, PR_EC_BASE + 0x8c)); //the complete rfc822 email define('PR_EC_IMAP_EMAIL_SIZE', mapi_prop_tag(PT_LONG, PR_EC_BASE + 0x8d)); define('PR_EC_IMAP_BODY', mapi_prop_tag(PT_STRING8, PR_EC_BASE + 0x8e)); //simplified bodystructure (mostly unused by clients) define('PR_EC_IMAP_BODYSTRUCTURE', mapi_prop_tag(PT_STRING8, PR_EC_BASE + 0x8f)); //extended bodystructure (often used by clients)
function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { $message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\r\n", 'charset' => 'utf-8')); // Open the outbox and create the message there $storeprops = mapi_getprops($this->_defaultstore, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID)); if (!isset($storeprops[PR_IPM_OUTBOX_ENTRYID])) { debugLog("Outbox not found to create message"); return false; } $outbox = mapi_msgstore_openentry($this->_defaultstore, $storeprops[PR_IPM_OUTBOX_ENTRYID]); if (!$outbox) { debugLog("Unable to open outbox"); return false; } $mapimessage = mapi_folder_createmessage($outbox); mapi_setprops($mapimessage, array(PR_SUBJECT => u2w($message->headers["subject"]), PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID], PR_MESSAGE_CLASS => "IPM.Note", PR_MESSAGE_DELIVERY_TIME => time())); if (isset($message->headers["x-priority"])) { switch ($message->headers["x-priority"]) { case 1: case 2: $priority = PRIO_URGENT; $importance = IMPORTANCE_HIGH; break; case 4: case 5: $priority = PRIO_NONURGENT; $importance = IMPORTANCE_LOW; break; case 3: default: $priority = PRIO_NORMAL; $importance = IMPORTANCE_NORMAL; break; } mapi_setprops($mapimessage, array(PR_IMPORTANCE => $importance, PR_PRIORITY => $priority)); } $addresses = array(); $toaddr = $ccaddr = $bccaddr = array(); if (isset($message->headers["to"])) { $toaddr = Mail_RFC822::parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = Mail_RFC822::parseAddressList($message->headers["cc"]); } if (isset($message->headers["bcc"])) { $bccaddr = Mail_RFC822::parseAddressList($message->headers["bcc"]); } // Add recipients $recips = array(); if (isset($toaddr)) { foreach (array(MAPI_TO => $toaddr, MAPI_CC => $ccaddr, MAPI_BCC => $bccaddr) as $type => $addrlist) { foreach ($addrlist as $addr) { $mapirecip[PR_ADDRTYPE] = "SMTP"; $mapirecip[PR_EMAIL_ADDRESS] = $addr->mailbox . "@" . $addr->host; if (isset($addr->personal) && strlen($addr->personal) > 0) { $mapirecip[PR_DISPLAY_NAME] = u2w($addr->personal); } else { $mapirecip[PR_DISPLAY_NAME] = $mapirecip[PR_EMAIL_ADDRESS]; } $mapirecip[PR_RECIPIENT_TYPE] = $type; $mapirecip[PR_ENTRYID] = mapi_createoneoff($mapirecip[PR_DISPLAY_NAME], $mapirecip[PR_ADDRTYPE], $mapirecip[PR_EMAIL_ADDRESS]); array_push($recips, $mapirecip); } } } mapi_message_modifyrecipients($mapimessage, 0, $recips); // Loop through subparts. We currently only support single-level // multiparts. The PDA currently only does this because you are adding // an attachment and the type will be multipart/mixed. if ($message->ctype_primary == "multipart" && $message->ctype_secondary == "mixed") { foreach ($message->parts as $part) { if ($part->ctype_primary == "text") { $body = u2w($part->body); } else { // attachment $attach = mapi_message_createattach($mapimessage); // Filename is present in both Content-Type: name=.. and in Content-Disposition: filename= if (isset($part->ctype_parameters["name"])) { $filename = $part->ctype_parameters["name"]; } else { if (isset($part->d_parameters["name"])) { $filename = $part->d_parameters["filename"]; } else { $filename = "untitled"; } } // Set filename and attachment type mapi_setprops($attach, array(PR_ATTACH_LONG_FILENAME => u2w($filename), PR_ATTACH_METHOD => ATTACH_BY_VALUE)); // Set attachment data mapi_setprops($attach, array(PR_ATTACH_DATA_BIN => $part->body)); // Set MIME type mapi_setprops($attach, array(PR_ATTACH_MIME_TAG => $part->ctype_primary . "/" . $part->ctype_secondary)); mapi_savechanges($attach); } } } else { $body = u2w($message->body); } if ($forward) { $orig = $forward; } if ($reply) { $orig = $reply; } if (isset($orig) && $orig) { // Append the original text body for reply/forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); if ($fwmessage) { $messageprops = mapi_getprops($fwmessage, array(PR_BODY)); if (isset($messageprops[PR_BODY])) { if ($forward) { // During a forward, we have to add the forward header ourselves. This is because // normally the forwarded message is added as an attachment. However, we don't want this // because it would be rather complicated to copy over the entire original message due // to the lack of IMessage::CopyTo .. $fwmessageprops = mapi_getprops($fwmessage, array(PR_SENT_REPRESENTING_NAME, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SUBJECT, PR_CLIENT_SUBMIT_TIME)); $body .= "\r\n\r\n"; $body .= "-----Original Message-----\r\n"; if (isset($fwmessageprops[PR_SENT_REPRESENTING_NAME])) { $body .= "From: " . $fwmessageprops[PR_SENT_REPRESENTING_NAME] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_TO]) && strlen($fwmessageprops[PR_DISPLAY_TO]) > 0) { $body .= "To: " . $fwmessageprops[PR_DISPLAY_TO] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_CC]) && strlen($fwmessageprops[PR_DISPLAY_CC]) > 0) { $body .= "Cc: " . $fwmessageprops[PR_DISPLAY_CC] . "\r\n"; } if (isset($fwmessageprops[PR_CLIENT_SUBMIT_TIME])) { $body .= "Sent: " . strftime("%x %X", $fwmessageprops[PR_CLIENT_SUBMIT_TIME]) . "\r\n"; } if (isset($fwmessageprops[PR_SUBJECT])) { $body .= "Subject: " . $fwmessageprops[PR_SUBJECT] . "\r\n"; } $body .= "\r\n"; } $body .= $messageprops[PR_BODY]; } } else { debugLog("Unable to open item with id {$orig} for forward/reply"); } } if ($forward) { // Add attachments from the original message in a forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); $attachtable = mapi_message_getattachmenttable($fwmessage); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { $attach = mapi_message_openattach($fwmessage, $row[PR_ATTACH_NUM]); $newattach = mapi_message_createattach($mapimessage); // Copy all attachments from old to new attachment $attachprops = mapi_getprops($attach); mapi_setprops($newattach, $attachprops); if (isset($attachprops[mapi_prop_tag(PT_ERROR, mapi_prop_id(PR_ATTACH_DATA_BIN))])) { // Data is in a stream $srcstream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); $dststream = mapi_openpropertytostream($newattach, PR_ATTACH_DATA_BIN, MAPI_MODIFY | MAPI_CREATE); while (1) { $data = mapi_stream_read($srcstream, 4096); if (strlen($data) == 0) { break; } mapi_stream_write($dststream, $data); } mapi_stream_commit($dststream); } mapi_savechanges($newattach); } } } mapi_setprops($mapimessage, array(PR_BODY => $body)); mapi_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); return true; }
define('PR_SCHDINFO_DELEGATE_NAMES', mapi_prop_tag(PT_MV_STRING8, 0x6844)); define('PR_DELEGATED_BY_RULE', mapi_prop_tag(PT_BOOLEAN, 0x3fe3)); // properties required in Reply mail. define('PR_INTERNET_REFERENCES', mapi_prop_tag(PT_STRING8, 0x1039)); define('PR_IN_REPLY_TO_ID', mapi_prop_tag(PT_STRING8, 0x1042)); define('PR_INTERNET_MESSAGE_ID', mapi_prop_tag(PT_STRING8, 0x1035)); // for hidden folders define('PR_ATTR_HIDDEN', mapi_prop_tag(PT_BOOLEAN, 0x10f4)); /** * Addressbook detail properties. * It is not defined by MAPI, but to keep in sync with the interface of outlook we have to use these * properties. Outlook actually uses these properties for it's addressbook details. */ define('PR_HOME2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a2f)); define('PR_BUSINESS2_TELEPHONE_NUMBER_MV', mapi_prop_tag(PT_MV_TSTRING, 0x3a1b)); define('PR_EMS_AB_PROXY_ADDRESSES', mapi_prop_tag(PT_TSTRING, 0x800f)); define('PR_EMS_AB_PROXY_ADDRESSES_MV', mapi_prop_tag(PT_MV_TSTRING, 0x800f)); define('PR_EMS_AB_MANAGER', mapi_prop_tag(PT_BINARY, 0x8005)); define('PR_EMS_AB_REPORTS', mapi_prop_tag(PT_BINARY, 0x800e)); define('PR_EMS_AB_REPORTS_MV', mapi_prop_tag(PT_MV_BINARY, 0x800e)); define('PR_EMS_AB_IS_MEMBER_OF_DL', mapi_prop_tag(PT_MV_BINARY, 0x8008)); define('PR_EMS_AB_OWNER', mapi_prop_tag(PT_BINARY, 0x800c)); define('PR_EMS_AB_ROOM_CAPACITY', mapi_prop_tag(PT_LONG, 0x807)); define('PR_EC_ARCHIVE_SERVERS', mapi_prop_tag(PT_MV_TSTRING, 0x67c4)); /* zarafa contacts provider properties */ define('PR_ZC_CONTACT_STORE_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x11)); define('PR_ZC_CONTACT_FOLDER_ENTRYIDS', mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE + 0x12)); define('PR_ZC_CONTACT_FOLDER_NAMES', mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE + 0x13)); //Properties defined for Z-Push define('PR_TODO_ITEM_FLAGS', mapi_prop_tag(PT_LONG, 0xe2b));
function mapitags() { define('PR_ENTRYID', mapi_prop_tag(PT_BINARY, 0xfff)); define('PR_MDB_PROVIDER', mapi_prop_tag(PT_BINARY, 0x3414)); define('PR_IPM_CONTACT_ENTRYID', mapi_prop_tag(PT_BINARY, 0x36d1)); define('PR_IPM_PUBLIC_FOLDERS_ENTRYID', mapi_prop_tag(PT_BINARY, 0x6631)); define('PR_DISPLAY_NAME', mapi_prop_tag(PT_TSTRING, 0x3001)); define('PR_SUBJECT', mapi_prop_tag(PT_TSTRING, 0x37)); define('PR_COMPANY_NAME', mapi_prop_tag(PT_TSTRING, 0x3a16)); define('PR_BUSINESS_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a08)); define('PR_OFFICE_TELEPHONE_NUMBER', PR_BUSINESS_TELEPHONE_NUMBER); define('PR_MOBILE_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1c)); define('PR_CELLULAR_TELEPHONE_NUMBER', PR_MOBILE_TELEPHONE_NUMBER); define('PR_BUSINESS_FAX_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a24)); define('PR_MESSAGE_CLASS', mapi_prop_tag(PT_TSTRING, 0x1a)); define('PR_ICON_INDEX', mapi_prop_tag(PT_LONG, 0x1080)); define('PR_GIVEN_NAME', mapi_prop_tag(PT_TSTRING, 0x3a06)); define('PR_MIDDLE_NAME', mapi_prop_tag(PT_TSTRING, 0x3a44)); define('PR_SURNAME', mapi_prop_tag(PT_TSTRING, 0x3a11)); define('PR_HOME_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a09)); define('PR_TITLE', mapi_prop_tag(PT_TSTRING, 0x3a17)); define('PR_DEPARTMENT_NAME', mapi_prop_tag(PT_TSTRING, 0x3a18)); define('PR_OFFICE_LOCATION', mapi_prop_tag(PT_TSTRING, 0x3a19)); define('PR_PROFESSION', mapi_prop_tag(PT_TSTRING, 0x3a46)); define('PR_MANAGER_NAME', mapi_prop_tag(PT_TSTRING, 0x3a4e)); define('PR_ASSISTANT', mapi_prop_tag(PT_TSTRING, 0x3a30)); define('PR_NICKNAME', mapi_prop_tag(PT_TSTRING, 0x3a4f)); define('PR_DISPLAY_NAME_PREFIX', mapi_prop_tag(PT_TSTRING, 0x3a45)); define('PR_SPOUSE_NAME', mapi_prop_tag(PT_TSTRING, 0x3a48)); define('PR_GENERATION', mapi_prop_tag(PT_TSTRING, 0x3a05)); define('PR_BIRTHDAY', mapi_prop_tag(PT_SYSTIME, 0x3a42)); define('PR_WEDDING_ANNIVERSARY', mapi_prop_tag(PT_SYSTIME, 0x3a41)); define('PR_SENSITIVITY', mapi_prop_tag(PT_LONG, 0x36)); define('PR_BUSINESS_HOME_PAGE', mapi_prop_tag(PT_TSTRING, 0x3a51)); define('PR_LAST_MODIFICATION_TIME', mapi_prop_tag(PT_SYSTIME, 0x3008)); define('PR_ASSISTANT_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a2e)); define('PR_BUSINESS2_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1b)); define('PR_CALLBACK_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a02)); define('PR_CAR_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1e)); define('PR_COMPANY_MAIN_PHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a57)); define('PR_HOME2_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a2f)); define('PR_HOME_FAX_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a25)); define('PR_ISDN_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a2d)); define('PR_OTHER_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1f)); define('PR_PAGER_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a21)); define('PR_PRIMARY_FAX_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a23)); define('PR_PRIMARY_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1a)); define('PR_RADIO_TELEPHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a1d)); define('PR_TELEX_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a2c)); define('PR_TTYTDD_PHONE_NUMBER', mapi_prop_tag(PT_TSTRING, 0x3a4b)); define('PR_HOME_ADDRESS_STREET', mapi_prop_tag(PT_TSTRING, 0x3a5d)); define('PR_HOME_ADDRESS_CITY', mapi_prop_tag(PT_TSTRING, 0x3a59)); define('PR_HOME_ADDRESS_STATE_OR_PROVINCE', mapi_prop_tag(PT_TSTRING, 0x3a5c)); define('PR_HOME_ADDRESS_POSTAL_CODE', mapi_prop_tag(PT_TSTRING, 0x3a5b)); define('PR_HOME_ADDRESS_COUNTRY', mapi_prop_tag(PT_TSTRING, 0x3a5a)); define('PR_OTHER_ADDRESS_STREET', mapi_prop_tag(PT_TSTRING, 0x3a63)); define('PR_OTHER_ADDRESS_CITY', mapi_prop_tag(PT_TSTRING, 0x3a5f)); define('PR_OTHER_ADDRESS_STATE_OR_PROVINCE', mapi_prop_tag(PT_TSTRING, 0x3a62)); define('PR_OTHER_ADDRESS_POSTAL_CODE', mapi_prop_tag(PT_TSTRING, 0x3a61)); define('PR_OTHER_ADDRESS_COUNTRY', mapi_prop_tag(PT_TSTRING, 0x3a60)); define('PR_COUNTRY', mapi_prop_tag(PT_TSTRING, 0x3a26)); define('PR_LOCALITY', mapi_prop_tag(PT_TSTRING, 0x3a27)); define('PR_POSTAL_ADDRESS', mapi_prop_tag(PT_TSTRING, 0x3a15)); define('PR_POSTAL_CODE', mapi_prop_tag(PT_TSTRING, 0x3a2a)); define('PR_STATE_OR_PROVINCE', mapi_prop_tag(PT_TSTRING, 0x3a28)); define('PR_STREET_ADDRESS', mapi_prop_tag(PT_TSTRING, 0x3a29)); define('PR_BODY', mapi_prop_tag(PT_TSTRING, 0x1000)); define('PR_STORE_SUPPORT_MASK', mapi_prop_tag(PT_LONG, 0x340d)); }
function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { if (WBXML_DEBUG == true) { debugLog("SendMail: forward: {$forward} reply: {$reply} parent: {$parent}\n" . $rfc822); } $mimeParams = array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'); $mimeObject = new Mail_mimeDecode($rfc822); $message = $mimeObject->decode($mimeParams); // Open the outbox and create the message there $storeprops = mapi_getprops($this->_defaultstore, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID)); if (!isset($storeprops[PR_IPM_OUTBOX_ENTRYID])) { debugLog("Outbox not found to create message"); return false; } $outbox = mapi_msgstore_openentry($this->_defaultstore, $storeprops[PR_IPM_OUTBOX_ENTRYID]); if (!$outbox) { debugLog("Unable to open outbox"); return false; } $mapimessage = mapi_folder_createmessage($outbox); mapi_setprops($mapimessage, array(PR_SUBJECT => u2wi(isset($message->headers["subject"]) ? $message->headers["subject"] : ""), PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID], PR_MESSAGE_CLASS => "IPM.Note", PR_MESSAGE_DELIVERY_TIME => time())); if (isset($message->headers["x-priority"])) { switch ($message->headers["x-priority"]) { case 1: case 2: $priority = PRIO_URGENT; $importance = IMPORTANCE_HIGH; break; case 4: case 5: $priority = PRIO_NONURGENT; $importance = IMPORTANCE_LOW; break; case 3: default: $priority = PRIO_NORMAL; $importance = IMPORTANCE_NORMAL; break; } mapi_setprops($mapimessage, array(PR_IMPORTANCE => $importance, PR_PRIORITY => $priority)); } $addresses = array(); $toaddr = $ccaddr = $bccaddr = array(); $Mail_RFC822 = new Mail_RFC822(); if (isset($message->headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); } if (isset($message->headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); } if (isset($message->headers["bcc"])) { $bccaddr = $Mail_RFC822->parseAddressList($message->headers["bcc"]); } // Add recipients $recips = array(); if (isset($toaddr)) { foreach (array(MAPI_TO => $toaddr, MAPI_CC => $ccaddr, MAPI_BCC => $bccaddr) as $type => $addrlist) { foreach ($addrlist as $addr) { $mapirecip[PR_ADDRTYPE] = "SMTP"; $mapirecip[PR_EMAIL_ADDRESS] = $addr->mailbox . "@" . $addr->host; if (isset($addr->personal) && strlen($addr->personal) > 0) { $mapirecip[PR_DISPLAY_NAME] = u2wi($addr->personal); } else { $mapirecip[PR_DISPLAY_NAME] = $mapirecip[PR_EMAIL_ADDRESS]; } $mapirecip[PR_RECIPIENT_TYPE] = $type; $mapirecip[PR_ENTRYID] = mapi_createoneoff($mapirecip[PR_DISPLAY_NAME], $mapirecip[PR_ADDRTYPE], $mapirecip[PR_EMAIL_ADDRESS]); array_push($recips, $mapirecip); } } } mapi_message_modifyrecipients($mapimessage, 0, $recips); // Loop through message subparts. $body = ""; $body_html = ""; if ($message->ctype_primary == "multipart" && ($message->ctype_secondary == "mixed" || $message->ctype_secondary == "alternative")) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; // palm pre & iPhone send forwarded messages in another subpart which are also parsed if ($part->ctype_primary == "multipart" && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related")) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } // standard body if ($part->ctype_primary == "text" && $part->ctype_secondary == "plain" && isset($part->body) && (!isset($part->disposition) || $part->disposition != "attachment")) { $body .= u2wi($part->body); // assume only one text body } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "html") { $body_html .= u2wi($part->body); } elseif ($part->ctype_primary == "ms-tnef" || $part->ctype_secondary == "ms-tnef") { $zptnef = new ZPush_tnef($this->_defaultstore); $mapiprops = array(); $zptnef->extractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { //check if it is a recurring item $tnefrecurr = GetPropIDFromString($this->_defaultstore, "PT_BOOLEAN:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x5"); if (isset($mapiprops[$tnefrecurr])) { $this->_handleRecurringItem($mapimessage, $mapiprops); } mapi_setprops($mapimessage, $mapiprops); } else { debugLog("TNEF: Mapi props array was empty"); } } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "calendar") { $zpical = new ZPush_ical($this->_defaultstore); $mapiprops = array(); $zpical->extractProps($part->body, $mapiprops); // iPhone sends a second ICS which we ignore if we can if (!isset($mapiprops[PR_MESSAGE_CLASS]) && strlen(trim($body)) == 0) { debugLog("Secondary iPhone response is being ignored!! Mail dropped!"); return true; } if (!checkMapiExtVersion("6.30") && is_array($mapiprops) && !empty($mapiprops)) { mapi_setprops($mapimessage, $mapiprops); } else { // store ics as attachment //see icalTimezoneFix function in compat.php for more information $part->body = icalTimezoneFix($part->body); $this->_storeAttachment($mapimessage, $part); debugLog("Sending ICS file as attachment"); } } else { $this->_storeAttachment($mapimessage, $part); } } } else { if ($message->ctype_primary == "text" && $message->ctype_secondary == "html") { $body_html .= u2wi($message->body); } else { $body = u2wi($message->body); } } // some devices only transmit a html body if (strlen($body) == 0 && strlen($body_html) > 0) { debugLog("only html body sent, transformed into plain text"); $body = strip_tags($body_html); } if ($forward) { $orig = $forward; } if ($reply) { $orig = $reply; } if (isset($orig) && $orig) { // Append the original text body for reply/forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); if ($fwmessage) { //update icon when forwarding or replying message if ($forward) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($reply) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); $stream = mapi_openproperty($fwmessage, PR_BODY, IID_IStream, 0, 0); $fwbody = ""; while (1) { $data = mapi_stream_read($stream, 1024); if (strlen($data) == 0) { break; } $fwbody .= $data; } $stream = mapi_openproperty($fwmessage, PR_HTML, IID_IStream, 0, 0); $fwbody_html = ""; while (1) { $data = mapi_stream_read($stream, 1024); if (strlen($data) == 0) { break; } $fwbody_html .= $data; } if ($forward) { // During a forward, we have to add the forward header ourselves. This is because // normally the forwarded message is added as an attachment. However, we don't want this // because it would be rather complicated to copy over the entire original message due // to the lack of IMessage::CopyTo .. $fwmessageprops = mapi_getprops($fwmessage, array(PR_SENT_REPRESENTING_NAME, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SUBJECT, PR_CLIENT_SUBMIT_TIME)); $fwheader = "\r\n\r\n"; $fwheader .= "-----Original Message-----\r\n"; if (isset($fwmessageprops[PR_SENT_REPRESENTING_NAME])) { $fwheader .= "From: " . $fwmessageprops[PR_SENT_REPRESENTING_NAME] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_TO]) && strlen($fwmessageprops[PR_DISPLAY_TO]) > 0) { $fwheader .= "To: " . $fwmessageprops[PR_DISPLAY_TO] . "\r\n"; } if (isset($fwmessageprops[PR_DISPLAY_CC]) && strlen($fwmessageprops[PR_DISPLAY_CC]) > 0) { $fwheader .= "Cc: " . $fwmessageprops[PR_DISPLAY_CC] . "\r\n"; } if (isset($fwmessageprops[PR_CLIENT_SUBMIT_TIME])) { $fwheader .= "Sent: " . strftime("%x %X", $fwmessageprops[PR_CLIENT_SUBMIT_TIME]) . "\r\n"; } if (isset($fwmessageprops[PR_SUBJECT])) { $fwheader .= "Subject: " . $fwmessageprops[PR_SUBJECT] . "\r\n"; } $fwheader .= "\r\n"; // add fwheader to body and body_html $body .= $fwheader; if (strlen($body_html) > 0) { $body_html .= str_ireplace("\r\n", "<br>", $fwheader); } } if (strlen($body) > 0) { $body .= $fwbody; } if (strlen($body_html) > 0) { $body_html .= $fwbody_html; } } else { debugLog("Unable to open item with id {$orig} for forward/reply"); } } if ($forward) { // Add attachments from the original message in a forward $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($parent), hex2bin($orig)); $fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); $attachtable = mapi_message_getattachmenttable($fwmessage); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { $attach = mapi_message_openattach($fwmessage, $row[PR_ATTACH_NUM]); $newattach = mapi_message_createattach($mapimessage); // Copy all attachments from old to new attachment $attachprops = mapi_getprops($attach); mapi_setprops($newattach, $attachprops); if (isset($attachprops[mapi_prop_tag(PT_ERROR, mapi_prop_id(PR_ATTACH_DATA_BIN))])) { // Data is in a stream $srcstream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); $dststream = mapi_openpropertytostream($newattach, PR_ATTACH_DATA_BIN, MAPI_MODIFY | MAPI_CREATE); while (1) { $data = mapi_stream_read($srcstream, 4096); if (strlen($data) == 0) { break; } mapi_stream_write($dststream, $data); } mapi_stream_commit($dststream); } mapi_savechanges($newattach); } } } //set PR_INTERNET_CPID to 65001 (utf-8) if store supports it and to 1252 otherwise $internetcpid = 1252; if (defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) { $internetcpid = 65001; } mapi_setprops($mapimessage, array(PR_BODY => $body, PR_INTERNET_CPID => $internetcpid)); if (strlen($body_html) > 0) { mapi_setprops($mapimessage, array(PR_HTML => $body_html)); } mapi_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); return true; }
* * This software use SabreDAV, an open source software distributed * with New BSD License. Please see <http://code.google.com/p/sabredav/> * for more information about SabreDAV * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Project page: <http://code.google.com/p/sabre-zarafa/> * */ // PHP-MAPI require_once "mapi/mapi.util.php"; require_once "mapi/mapicode.php"; require_once "mapi/mapidefs.php"; require_once "mapi/mapitags.php"; require_once "mapi/mapiguid.php"; // Add some custom properties to store whatever I need // Decided to start property IDs to 0xB600 which should not interfere with Zarafa (hope so) define('CARDDAV_CUSTOM_PROPERTY_ID', 0xb600); define('PR_CARDDAV_URI', mapi_prop_tag(PT_STRING8, CARDDAV_CUSTOM_PROPERTY_ID | 0x0)); define('PR_CARDDAV_RAW_DATA', mapi_prop_tag(PT_STRING8, CARDDAV_CUSTOM_PROPERTY_ID | 0x1)); define('PR_CARDDAV_RAW_DATA_GENERATION_TIME', mapi_prop_tag(PT_SYSTIME, CARDDAV_CUSTOM_PROPERTY_ID | 0x2)); define('PR_CARDDAV_AB_CONTACT_COUNT', mapi_prop_tag(PT_LONG, CARDDAV_CUSTOM_PROPERTY_ID | 0x3)); define('PR_CARDDAV_RAW_DATA_VERSION', mapi_prop_tag(PT_STRING8, CARDDAV_CUSTOM_PROPERTY_ID | 0x4));
/** * Copies attachments from one message to another. * * @param MAPIMessage $toMessage * @param MAPIMessage $fromMessage * * @return void */ private function copyAttachments(&$toMessage, $fromMessage) { $attachtable = mapi_message_getattachmenttable($fromMessage); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { $attach = mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]); $newattach = mapi_message_createattach($toMessage); // Copy all attachments from old to new attachment $attachprops = mapi_getprops($attach); mapi_setprops($newattach, $attachprops); if (isset($attachprops[mapi_prop_tag(PT_ERROR, mapi_prop_id(PR_ATTACH_DATA_BIN))])) { // Data is in a stream $srcstream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); $dststream = mapi_openpropertytostream($newattach, PR_ATTACH_DATA_BIN, MAPI_MODIFY | MAPI_CREATE); while (1) { $data = mapi_stream_read($srcstream, 4096); if (strlen($data) == 0) { break; } mapi_stream_write($dststream, $data); } mapi_stream_commit($dststream); } mapi_savechanges($newattach); } } }
function readCertsfromContact($emailaddress) { $email1address = GetPropIDFromString($this->_defaultstore, "PT_STRING8:{00062004-0000-0000-C000-000000000046}:0x8083"); $email2address = GetPropIDFromString($this->_defaultstore, "PT_STRING8:{00062004-0000-0000-C000-000000000046}:0x8093"); $email3address = GetPropIDFromString($this->_defaultstore, "PT_STRING8:{00062004-0000-0000-C000-000000000046}:0x80A3"); $x509certs = mapi_prop_tag(PT_MV_BINARY, 0x3a70); $rows = $this->readResolveRecipientsfromContacts($emailaddress); foreach ($rows as $entry) { $res['displayname'] = w2u($entry[PR_DISPLAY_NAME]); $res['type'] = 2; if (isset($entry[$email1address]) && $entry[$email1address] == $emailaddress) { $res['emailaddress'] = $entry[$email1address]; } else { if (isset($entry[$email2address]) && $entry[$email2address] == $emailaddress) { $res['emailaddress'] = $entry[$email2address]; } else { if (isset($entry[$email3address]) && $entry[$email3address] == $emailaddress) { $res['emailaddress'] = $entry[$email3address]; } } } $entries = array(); if (isset($entry[$x509certs]) && USERX509CERTIFICATES == true) { $certs = new Userx509Certificates(isset($entry[$x509certs]) ? $entry[$x509certs] : array()); if ($der_certs = $certs->findCertificatebySubjectValue('emailAddress', $emailaddress)) { foreach ($der_certs as $der_cert) { $entries[] = base64_encode($der_cert); } } } $res['entries'] = $entries; if (count($res['entries']) > 0) { $result[$emailaddress][] = $res; } } return $result; }
/** * Returns the error code for a given property. Helper for getNativeBodyType function. * * @param int $tag * @param array $messageprops * * @access private * @return int (MAPI_ERROR_CODE) */ private function getError($tag, $messageprops) { $prBodyError = mapi_prop_tag(PT_ERROR, mapi_prop_id($tag)); if (isset($messageprops[$prBodyError]) && mapi_is_error($messageprops[$prBodyError])) { if ($messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT || $messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) { return MAPI_E_NOT_ENOUGH_MEMORY; } } return MAPI_E_NOT_FOUND; }
function setSettings($request, $devid) { if (isset($request["oof"])) { if ($request["oof"]["oofstate"] == 1) { foreach ($request["oof"]["oofmsgs"] as $oofmsg) { switch ($oofmsg["appliesto"]) { case SYNC_SETTINGS_APPLIESTOINTERNAL: $result = mapi_setprops($this->_defaultstore, array(PR_EC_OUTOFOFFICE_MSG => utf8_to_windows1252(isset($oofmsg["replymessage"]) ? $oofmsg["replymessage"] : ""), PR_EC_OUTOFOFFICE_SUBJECT => utf8_to_windows1252(_("Out of office notification")))); break; } } $response["oof"]["status"] = mapi_setprops($this->_defaultstore, array(PR_EC_OUTOFOFFICE => $request["oof"]["oofstate"] == 1 ? true : false)); } else { $response["oof"]["status"] = mapi_setprops($this->_defaultstore, array(PR_EC_OUTOFOFFICE => $request["oof"]["oofstate"] == 1 ? true : false)); } } if (isset($request["deviceinformation"])) { if ($this->_defaultstore !== false) { //get devices settings from store $props = array(); $props[SYNC_SETTINGS_MODEL] = mapi_prop_tag(PT_MV_STRING8, 0x6890); $props[SYNC_SETTINGS_IMEI] = mapi_prop_tag(PT_MV_STRING8, 0x6891); $props[SYNC_SETTINGS_FRIENDLYNAME] = mapi_prop_tag(PT_MV_STRING8, 0x6892); $props[SYNC_SETTINGS_OS] = mapi_prop_tag(PT_MV_STRING8, 0x6893); $props[SYNC_SETTINGS_OSLANGUAGE] = mapi_prop_tag(PT_MV_STRING8, 0x6894); $props[SYNC_SETTINGS_PHONENUMBER] = mapi_prop_tag(PT_MV_STRING8, 0x6895); $props[SYNC_SETTINGS_USERAGENT] = mapi_prop_tag(PT_MV_STRING8, 0x6896); $props[SYNC_SETTINGS_ENABLEOUTBOUNDSMS] = mapi_prop_tag(PT_MV_STRING8, 0x6897); $props[SYNC_SETTINGS_MOBILEOPERATOR] = mapi_prop_tag(PT_MV_STRING8, 0x6898); $sprops = mapi_getprops($this->_defaultstore, array(0x6881101e, $props[SYNC_SETTINGS_MODEL], $props[SYNC_SETTINGS_IMEI], $props[SYNC_SETTINGS_FRIENDLYNAME], $props[SYNC_SETTINGS_OS], $props[SYNC_SETTINGS_OSLANGUAGE], $props[SYNC_SETTINGS_PHONENUMBER], $props[SYNC_SETTINGS_USERAGENT], $props[SYNC_SETTINGS_ENABLEOUTBOUNDSMS], $props[SYNC_SETTINGS_MOBILEOPERATOR])); //try to find index of current device $ak = array_search($devid, $sprops[0x6881101e]); // Set undefined properties to the amount of known device ids foreach ($props as $key => $value) { if (!isset($sprops[$value])) { for ($i = 0; $i < sizeof($sprops[0x6881101e]); $i++) { $sprops[$value][] = "undefined"; } } } if ($ak !== false) { //update settings (huh this could really occur?!?! - maybe in case of OS update) foreach ($request["deviceinformation"] as $key => $value) { if (trim($value) != "") { $sprops[$props[$key]][$ak] = $value; } else { $sprops[$props[$key]][$ak] = "undefined"; } } } else { //new device settings for the db $devicesprops[0x6881101e][] = $devid; foreach ($props as $key => $value) { if (isset($request["deviceinformation"][$key]) && trim($request["deviceinformation"][$key]) != "") { $sprops[$value][] = $request["deviceinformation"][$key]; } else { $sprops[$value][] = "undefined"; } } } // save them $response["deviceinformation"]["status"] = mapi_setprops($this->_defaultstore, $sprops); } } if (isset($request["devicepassword"])) { if ($this->_defaultstore !== false) { //get devices settings from store $props = array(); $props[SYNC_SETTINGS_PASSWORD] = mapi_prop_tag(PT_MV_STRING8, 0x689f); $pprops = mapi_getprops($this->_defaultstore, array(0x6881101e, $props[SYNC_SETTINGS_PASSWORD])); //try to find index of current device $ak = array_search($devid, $pprops[0x6881101e]); // Set undefined properties to the amount of known device ids foreach ($props as $key => $value) { if (!isset($pprops[$value])) { for ($i = 0; $i < sizeof($pprops[0x6881101e]); $i++) { $pprops[$value][] = "undefined"; } } } if ($ak !== false) { //update password if (trim($value) != "") { $pprops[$props[$key]][$ak] = $request["devicepassword"]; } else { $pprops[$props[$key]][$ak] = "undefined"; } } else { //new device password for the db $devicesprops[0x6881101e][] = $devid; foreach ($props as $key => $value) { if (isset($request["devicepassword"]) && trim($request["devicepassword"]) != "") { $pprops[$value][] = $request["devicepassword"]; } else { $pprops[$value][] = "undefined"; } } } // save them $response["devicepassword"]["status"] = mapi_setprops($this->_defaultstore, $pprops); } } return $response; }