/** * Sets the properties in a MAPI object according to an Sync object and a property mapping * * @param mixed $mapimessage * @param SyncObject $message * @param array $mapping * * @access private * @return */ private function setPropsInMAPI($mapimessage, $message, $mapping) { $mapiprops = $this->getPropIdsFromStrings($mapping); $unsetVars = $message->getUnsetVars(); $propsToDelete = array(); $propsToSet = array(); foreach ($mapiprops as $asprop => $mapiprop) { if (isset($message->{$asprop})) { // UTF8->windows1252.. this is ok for all numerical values if (mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { if (is_array($message->{$asprop})) { $value = array_map("u2wi", $message->{$asprop}); } else { $value = u2wi($message->{$asprop}); } } else { $value = $message->{$asprop}; } // Make sure the php values are the correct type switch (mapi_prop_type($mapiprop)) { case PT_BINARY: case PT_STRING8: settype($value, "string"); break; case PT_BOOLEAN: settype($value, "boolean"); break; case PT_SYSTIME: case PT_LONG: settype($value, "integer"); break; } // decode base64 value if ($mapiprop == PR_RTF_COMPRESSED) { $value = base64_decode($value); if (strlen($value) == 0) { continue; } // PDA will sometimes give us an empty RTF, which we'll ignore. // Note that you can still remove notes because when you remove notes it gives // a valid compressed RTF with nothing in it. } // if an "empty array" is to be saved, it the mvprop should be deleted - fixes Mantis #468 if (is_array($value) && empty($value)) { $propsToDelete[] = $mapiprop; ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->setPropsInMAPI(): Property '%s' to be deleted as it is an empty array", $asprop)); } else { // all properties will be set at once $propsToSet[$mapiprop] = $value; } } elseif (in_array($asprop, $unsetVars)) { $propsToDelete[] = $mapiprop; } } mapi_setprops($mapimessage, $propsToSet); if (mapi_last_hresult()) { Zlog::Write(LOGLEVEL_WARN, sprintf("Failed to set properties, trying to set them separately. Error code was:%x", mapi_last_hresult())); $this->setPropsIndividually($mapimessage, $propsToSet, $mapiprops); } mapi_deleteprops($mapimessage, $propsToDelete); //clean up unset($unsetVars, $propsToDelete); }
/** * Sends an e-mail * This messages needs to be saved into the 'sent items' folder * * @param SyncSendMail $sm SyncSendMail object * * @access public * @return boolean * @throws StatusException */ public function SendMail($sm) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", strlen($sm->mime), Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag), Utils::PrintAsString(isset($sm->source->folderid) ? $sm->source->folderid : false), Utils::PrintAsString($sm->saveinsent), Utils::PrintAsString(isset($sm->replacemime)))); // by splitting the message in several lines we can easily grep later foreach (preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) { ZLog::Write(LOGLEVEL_WBXML, "RFC822: " . $rfc822line); } $mimeParams = array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'); $mimeObject = new Mail_mimeDecode($sm->mime); $message = $mimeObject->decode($mimeParams); $sendMailProps = MAPIMapping::GetSendMailProperties(); $sendMailProps = getPropIdsFromStrings($this->store, $sendMailProps); // Open the outbox and create the message there $storeprops = mapi_getprops($this->store, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"])); if (isset($storeprops[$sendMailProps["outboxentryid"]])) { $outbox = mapi_msgstore_openentry($this->store, $storeprops[$sendMailProps["outboxentryid"]]); } if (!$outbox) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR); } $mapimessage = mapi_folder_createmessage($outbox); //message properties to be set $mapiprops = array(); // only save the outgoing in sent items folder if the mobile requests it $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]]; // Check if imtomapi function is available and use it to send the mime message. // It is available since ZCP 7.0.6 // @see http://jira.zarafa.com/browse/ZCP-9508 if (function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI')) { ZLog::Write(LOGLEVEL_DEBUG, "Use the mapi_inetmapi_imtomapi function"); $ab = mapi_openaddressbook($this->session); mapi_inetmapi_imtomapi($this->session, $this->store, $ab, $mapimessage, $sm->mime, array()); // Set the appSeqNr so that tracking tab can be updated for meeting request updates // @see http://jira.zarafa.com/browse/ZP-68 $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties(); $meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps); $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"])); if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) { // search for calendar items using goid $mr = new Meetingrequest($this->store, $mapimessage); $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]); if (is_array($appointments) && !empty($appointments)) { $app = mapi_msgstore_openentry($this->store, $appointments[0]); $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"])); if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) { $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]]; ZLog::Write(LOGLEVEL_DEBUG, sprintf("Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]])); } } } // Delete the PR_SENT_REPRESENTING_* properties because some android devices // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID // which results in spooler not being able to send the message. // @see http://jira.zarafa.com/browse/ZP-85 mapi_deleteprops($mapimessage, array($sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"], $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"])); if (isset($sm->source->itemid) && $sm->source->itemid) { $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { // get message's body in order to append forward or reply text $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"])); if ($sm->forwardflag) { // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } if (strlen($body) > 0) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); $fwbody = isset($cpid[$sendMailProps["internetcpid"]]) ? Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody) : w2u($fwbody); $mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . $fwbody; } if (strlen($bodyHtml) > 0) { $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); $fwbodyHtml = isset($cpid[$sendMailProps["internetcpid"]]) ? Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml) : w2u($fwbodyHtml); $mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . $fwbodyHtml; } } } mapi_setprops($mapimessage, $mapiprops); mapi_message_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); $hr = mapi_last_hresult(); if ($hr) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; } $mapiprops[$sendMailProps["subject"]] = u2wi(isset($message->headers["subject"]) ? $message->headers["subject"] : ""); $mapiprops[$sendMailProps["messageclass"]] = "IPM.Note"; $mapiprops[$sendMailProps["deliverytime"]] = time(); if (isset($message->headers["x-priority"])) { $this->getImportanceAndPriority($message->headers["x-priority"], $mapiprops, $sendMailProps); } $this->addRecipients($message->headers, $mapimessage); // 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") { if (!isset($tnefAndIcalProps)) { $tnefAndIcalProps = MAPIMapping::GetTnefAndIcalProperties(); $tnefAndIcalProps = getPropIdsFromStrings($this->store, $tnefAndIcalProps); } require_once 'tnefparser.php'; $zptnef = new TNEFParser($this->store, $tnefAndIcalProps); $zptnef->ExtractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { //check if it is a recurring item if (isset($mapiprops[$tnefAndIcalProps["tnefrecurr"]])) { MAPIUtils::handleRecurringItem($mapiprops, $tnefAndIcalProps); } } else { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->Sendmail(): TNEFParser: Mapi property array was empty"); } } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "calendar") { if (!isset($tnefAndIcalProps)) { $tnefAndIcalProps = MAPIMapping::GetTnefAndIcalProperties(); $tnefAndIcalProps = getPropIdsFromStrings($this->store, $tnefAndIcalProps); } require_once 'icalparser.php'; $zpical = new ICalParser($this->store, $tnefAndIcalProps); $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) { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->Sendmail(): Secondary iPhone response is being ignored!! Mail dropped!"); return true; } if (!Utils::CheckMapiExtVersion("6.30") && is_array($mapiprops) && !empty($mapiprops)) { mapi_setprops($mapimessage, $mapiprops); } else { // store ics as attachment //see Utils::IcalTimezoneFix() in utils.php for more information $part->body = Utils::IcalTimezoneFix($part->body); MAPIUtils::StoreAttachment($mapimessage, $part); ZLog::Write(LOGLEVEL_INFO, "ZarafaBackend->Sendmail(): Sending ICS file as attachment"); } } else { MAPIUtils::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) { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->SendMail(): only html body sent, transformed into plain text"); $body = strip_tags($body_html); } if (isset($sm->source->itemid) && $sm->source->itemid) { // Append the original text body for reply/forward $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); if ($sm->forwardflag) { // 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 .. $fwheader = $this->getForwardHeaders($fwmessage); // add fwheader to body and body_html $body .= $fwheader; if (strlen($body_html) > 0) { $body_html .= str_ireplace("\r\n", "<br>", $fwheader); } // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } if (strlen($body) > 0) { $body .= $fwbody; } if (strlen($body_html) > 0) { $body_html .= $fwbodyHtml; } } } //set PR_INTERNET_CPID to 65001 (utf-8) if store supports it and to 1252 otherwise $internetcpid = INTERNET_CPID_WINDOWS1252; if (defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) { $internetcpid = INTERNET_CPID_UTF8; } $mapiprops[$sendMailProps["body"]] = $body; $mapiprops[$sendMailProps["internetcpid"]] = $internetcpid; if (strlen($body_html) > 0) { $mapiprops[$sendMailProps["html"]] = $body_html; } //TODO if setting all properties fails, try setting them infividually like in mapiprovider mapi_setprops($mapimessage, $mapiprops); mapi_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; }
function zpa_remove_device($adminStore, $session, $user, $deviceid) { $userEntryId = @mapi_msgstore_createentryid($adminStore, $user); $userStore = @mapi_openmsgstore($session, $userEntryId); $hresult = mapi_last_hresult(); if ($hresult != NOERROR) { echo "Could not open store for {$user}. The script will exit.\n"; exit(1); } $devicesprops = mapi_getprops($userStore, array(0x6880101e, 0x6881101e, 0x6882101e, 0x6883101e, 0x68841003, 0x6885101e, 0x6886101e, 0x6887101e, 0x68881040, 0x68891040)); if (isset($devicesprops[0x6881101e]) && is_array($devicesprops[0x6881101e])) { $ak = array_search($deviceid, $devicesprops[0x6881101e]); if ($ak !== false) { if (count($devicesprops[0x6880101e]) == 1) { mapi_deleteprops($userStore, array(0x6880101e, 0x6881101e, 0x6882101e, 0x6883101e, 0x68841003, 0x6885101e, 0x6886101e, 0x6887101e, 0x68881040, 0x68891040)); } else { unset($devicesprops[0x6880101e][$ak], $devicesprops[0x6881101e][$ak], $devicesprops[0x6882101e][$ak], $devicesprops[0x6883101e][$ak], $devicesprops[0x68841003][$ak], $devicesprops[0x6885101e][$ak], $devicesprops[0x6886101e][$ak], $devicesprops[0x6887101e][$ak], $devicesprops[0x68881040][$ak], $devicesprops[0x68891040][$ak]); mapi_setprops($userStore, array(0x6880101e => isset($devicesprops[0x6880101e]) ? $devicesprops[0x6880101e] : array(), 0x6881101e => isset($devicesprops[0x6881101e]) ? $devicesprops[0x6881101e] : array(), 0x6882101e => isset($devicesprops[0x6882101e]) ? $devicesprops[0x6882101e] : array(), 0x6883101e => isset($devicesprops[0x6883101e]) ? $devicesprops[0x6883101e] : array(), 0x68841003 => isset($devicesprops[0x68841003]) ? $devicesprops[0x68841003] : array(), 0x6885101e => isset($devicesprops[0x6885101e]) ? $devicesprops[0x6885101e] : array(), 0x6886101e => isset($devicesprops[0x6886101e]) ? $devicesprops[0x6886101e] : array(), 0x6887101e => isset($devicesprops[0x6887101e]) ? $devicesprops[0x6887101e] : array(), 0x68881040 => isset($devicesprops[0x68881040]) ? $devicesprops[0x68881040] : array(), 0x68891040 => isset($devicesprops[0x68891040]) ? $devicesprops[0x68891040] : array())); } $hresult = mapi_last_hresult(); if ($hresult != NOERROR) { echo "Could not remove device from list for {$user}. Errorcode 0x" . sprintf("%x", $hresult) . ". The script will exit.\n"; exit(1); } else { echo "Removed device from list.\n"; } } else { echo "No device found with the given id.\n"; exit(1); } } else { echo "No devices found for the user {$user}.\n"; exit(1); } }
/** Reclaims ownership of a decline task * * Deletes taskrequest properties and recipients from the task message. */ function reclaimownership() { // Delete task request properties mapi_deleteprops($this->message, array($this->props['taskglobalobjid'], $this->props['tasklastuser'], $this->props['tasklastdelegate'])); mapi_setprops($this->message, array($this->props['updatecount'] => 2, $this->props['taskfcreator'] => true)); // Delete recipients $this->deleteAllRecipients($this->message); }
function _setPropsInMAPI($mapimessage, $message, $mapping) { foreach ($mapping as $asprop => $mapiprop) { if (isset($message->{$asprop})) { $mapiprop = $this->_getPropIDFromString($mapiprop); // UTF8->windows1252.. this is ok for all numerical values if (mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { if (is_array($message->{$asprop})) { $value = array_map("u2wi", $message->{$asprop}); } else { $value = u2wi($message->{$asprop}); } } else { $value = $message->{$asprop}; } // Make sure the php values are the correct type switch (mapi_prop_type($mapiprop)) { case PT_BINARY: case PT_STRING8: settype($value, "string"); break; case PT_BOOLEAN: settype($value, "boolean"); break; case PT_SYSTIME: case PT_LONG: settype($value, "integer"); break; } // decode base64 value if ($mapiprop == PR_RTF_COMPRESSED) { $value = base64_decode($value); if (strlen($value) == 0) { continue; } // PDA will sometimes give us an empty RTF, which we'll ignore. // Note that you can still remove notes because when you remove notes it gives // a valid compressed RTF with nothing in it. } mapi_setprops($mapimessage, array($mapiprop => $value)); // fixes Mantis #468 if (is_array($value) && empty($value)) { debugLog(sprintf("mapi_deleteprops() for '%s' as it is an empty array", $asprop)); mapi_deleteprops($mapimessage, array($mapiprop)); } } } }
function ImportMessageFlag($id, $flag) { $emailflag = $this->_emailflagmapping; $entryid = mapi_msgstore_entryidfromsourcekey($this->_store, hex2bin($id)); $mapimessage = mapi_msgstore_openentry($this->_store, $entryid); if ($mapimessage == false) { debugLog("Unable to openentry in ImportMessageFlag: " . sprintf("%x", mapi_last_hresult())); } else { // we need this for importing changes in the end... $flags = 0; $props = array(); $props = mapi_getprops($mapimessage, array(PR_PARENT_SOURCE_KEY, PR_SOURCE_KEY)); // so now do the job with the flags. Delete flags not being sent, set flags that are in sync packet // flagicon is just necessary for Zarafa WebAccess. Outlook does not need it. $setflags = array(); $delflags = array(); if (isset($flag->flagstatus) && $flag->flagstatus != "") { $setflags += array($this->_getPropIDFromString($emailflag["flagstatus"]) => $flag->flagstatus); switch ($flag->flagstatus) { case '2': $setflags += array($this->_getPropIDFromString($emailflag["flagicon"]) => 6); break; default: $setflags += array($this->_getPropIDFromString($emailflag["flagicon"]) => 0); break; } } else { $delflags[] = $this->_getPropIDFromString($emailflag["flagstatus"]); $delflags[] = $this->_getPropIDFromString($emailflag["flagicon"]); } // dw2412 in case the flag should be removed... just do it compatible with o2k7 if (isset($flag->flagstatus) && ($flag->flagstatus == 0 || $flag->flagstatus == "") || !isset($flag->flagstatus)) { $delflags[] = $this->_getPropIDFromString($emailflag["flagstatus"]); $delflags[] = $this->_getPropIDFromString($emailflag["flagicon"]); $delflags[] = $this->_getPropIDFromString("PT_SYSTIME:{00062003-0000-0000-C000-000000000046}:0x8104"); $delflags[] = $this->_getPropIDFromString("PT_SYSTIME:{00062003-0000-0000-C000-000000000046}:0x8105"); $delflags[] = $this->_getPropIDFromString("PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8516"); $delflags[] = $this->_getPropIDFromString("PT_SYSTIME:{00062008-0000-0000-C000-000000000046}:0x8517"); $delflags[] = $this->_getPropIDFromString("PT_SYSTIME:{00062006-0000-0000-C000-000000000046}:0x85A0"); $delflags[] = $this->_getPropIDFromString("PT_STRING8:{00062006-0000-0000-C000-000000000046}:0x85A1"); $delflags[] = $this->_getPropIDFromString("PT_STRING8:{00062006-0000-0000-C000-000000000046}:0x85A4"); $setflags += array($this->_getPropIDFromString("PT_STRING8:{00062008-0000-0000-C000-000000000046}:0x8530") => ""); $setflags += array($this->_getPropIDFromString("PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8503") => "0"); $setflags += array($this->_getPropIDFromString("PT_LONG:{00062003-0000-0000-C000-000000000046}:0x8101") => "0"); $setflags += array($this->_getPropIDFromString("PT_DOUBLE:{00062003-0000-0000-C000-000000000046}:0x8102") => "0"); $setflags += array($this->_getPropIDFromString("PT_BOOLEAN:{00062003-0000-0000-C000-000000000046}:0x811C") => "0"); $setflags += array(mapi_prop_tag(PT_LONG, 0xe2b) => "0"); //dw2412 responsible for displaying flag in O2K7, added as 'PR_TODO_ITEM_FLAGS' to the mapitags.php $setflags += array($this->_getPropIDFromString($emailflag["reminderset"]) => "0"); } else { if (isset($flag->flagtype) && $flag->flagtype != "") { $setflags += array($this->_getPropIDFromString($emailflag["flagtype"]) => $flag->flagtype); } else { $delflags[] = $this->_getPropIDFromString($emailflag["flagtype"]); } if (isset($flag->startdate) && $flag->startdate != "") { $setflags += array($this->_getPropIDFromString($emailflag["startdate"]) => $flag->startdate); } else { $delflags[] = $this->_getPropIDFromString($emailflag["startdate"]); } if (isset($flag->duedate) && $flag->duedate != "") { $setflags += array($this->_getPropIDFromString($emailflag["duedate"]) => $flag->duedate); } else { $delflags[] = $this->_getPropIDFromString($emailflag["duedate"]); } if (isset($flag->datecompleted) && $flag->datecompleted != "") { $setflags += array($this->_getPropIDFromString($emailflag["datecompleted"]) => $flag->datecompleted); } else { $delflags[] = $this->_getPropIDFromString($emailflag["datecompleted"]); } if (isset($flag->reminderset) && $flag->reminderset != "") { $setflags += array($this->_getPropIDFromString($emailflag["reminderset"]) => $flag->reminderset); } else { if ($flag->flagstatus > 0) { $setflags += array($this->_getPropIDFromString($emailflag["reminderset"]) => "0"); } else { $delflags[] = $this->_getPropIDFromString($emailflag["reminderset"]); } } if (isset($flag->remindertime) && $flag->remindertime != "") { $setflags += array($this->_getPropIDFromString($emailflag["remindertime"]) => $flag->remindertime); } else { $delflags[] = $this->_getPropIDFromString($emailflag["remindertime"]); } if (isset($flag->ordinaldate) && $flag->ordinaldate != "") { $setflags += array($this->_getPropIDFromString($emailflag["ordinaldate"]) => $flag->ordinaldate); } else { $delflags[] = $this->_getPropIDFromString($emailflag["ordinaldate"]); } if (isset($flag->subordinaldate) && $flag->subordinaldate != "") { $setflags += array($this->_getPropIDFromString($emailflag["subordinaldate"]) => $flag->subordinaldate); } else { $delflags[] = $this->_getPropIDFromString($emailflag["subordinaldate"]); } if (isset($flag->completetime) && $flag->completetime != "") { $setflags += array($this->_getPropIDFromString($emailflag["completetime"]) => $flag->completetime); } else { $delflags[] = $this->_getPropIDFromString($emailflag["completetime"]); } } // hopefully I'm doing this right. It should prevent the back sync of whole message mapi_importcontentschanges_importmessagechange($this->importer, $props, $flags, $mapimessage); mapi_setprops($mapimessage, $setflags); mapi_deleteprops($mapimessage, $delflags); mapi_savechanges($mapimessage); return true; } }
/** * Sends an e-mail * This messages needs to be saved into the 'sent items' folder * * @param SyncSendMail $sm SyncSendMail object * * @access public * @return boolean * @throws StatusException */ public function SendMail($sm) { // Check if imtomapi function is available and use it to send the mime message. // It is available since ZCP 7.0.6 // @see http://jira.zarafa.com/browse/ZCP-9508 if (!(function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI'))) { throw new StatusException("ZarafaBackend->SendMail(): ZCP version is too old, INETMAPI_IMTOMAPI is not available. Install at least ZCP version 7.0.6 or later.", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED, null, LOGLEVEL_FATAL); return false; } ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", strlen($sm->mime), Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag), Utils::PrintAsString(isset($sm->source->folderid) ? $sm->source->folderid : false), Utils::PrintAsString($sm->saveinsent), Utils::PrintAsString(isset($sm->replacemime)))); // by splitting the message in several lines we can easily grep later foreach (preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) { ZLog::Write(LOGLEVEL_WBXML, "RFC822: " . $rfc822line); } $sendMailProps = MAPIMapping::GetSendMailProperties(); $sendMailProps = getPropIdsFromStrings($this->store, $sendMailProps); // Open the outbox and create the message there $storeprops = mapi_getprops($this->store, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"])); if (isset($storeprops[$sendMailProps["outboxentryid"]])) { $outbox = mapi_msgstore_openentry($this->store, $storeprops[$sendMailProps["outboxentryid"]]); } if (!$outbox) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR); } $mapimessage = mapi_folder_createmessage($outbox); //message properties to be set $mapiprops = array(); // only save the outgoing in sent items folder if the mobile requests it $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]]; ZLog::Write(LOGLEVEL_DEBUG, "Use the mapi_inetmapi_imtomapi function"); $ab = mapi_openaddressbook($this->session); mapi_inetmapi_imtomapi($this->session, $this->store, $ab, $mapimessage, $sm->mime, array()); // Set the appSeqNr so that tracking tab can be updated for meeting request updates // @see http://jira.zarafa.com/browse/ZP-68 $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties(); $meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps); $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"])); // Convert sent message's body to UTF-8. // @see http://jira.zarafa.com/browse/ZP-505 if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sent email cpid is not unicode (%d). Set it to unicode and convert email body.", $props[$sendMailProps["internetcpid"]])); $mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8; $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); $body = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $body); $mapiprops[$sendMailProps["body"]] = $body; $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); $bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml); $mapiprops[$sendMailProps["html"]] = $bodyHtml; mapi_setprops($mapimessage, $mapiprops); } if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) { // search for calendar items using goid $mr = new Meetingrequest($this->store, $mapimessage); $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]); if (is_array($appointments) && !empty($appointments)) { $app = mapi_msgstore_openentry($this->store, $appointments[0]); $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"])); if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) { $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]]; ZLog::Write(LOGLEVEL_DEBUG, sprintf("Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]])); } } } // Delete the PR_SENT_REPRESENTING_* properties because some android devices // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID // which results in spooler not being able to send the message. // @see http://jira.zarafa.com/browse/ZP-85 mapi_deleteprops($mapimessage, array($sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"], $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"])); if (isset($sm->source->itemid) && $sm->source->itemid) { // answering an email in a public/shared folder if (!$this->Setup(ZPush::GetAdditionalSyncFolderStore($sm->source->folderid))) { throw new StatusException(sprintf("ZarafaBackend->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR); } $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { // get message's body in order to append forward or reply text if (!isset($body)) { $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); } if (!isset($bodyHtml)) { $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); } $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"])); if ($sm->forwardflag) { // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } // regarding the conversion @see ZP-470 if (strlen($body) > 0) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); // if only the old message's cpid is set, convert from old charset to utf-8 if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for plain forwarded message"); $fwbody = w2u($fwbody); } $mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . $fwbody; } if (strlen($bodyHtml) > 0) { $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); // if only new message's cpid is set, convert to UTF-8 if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for html forwarded message"); $fwbodyHtml = w2u($fwbodyHtml); } $mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . $fwbodyHtml; } } } mapi_setprops($mapimessage, $mapiprops); mapi_message_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); $hr = mapi_last_hresult(); if ($hr) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; }
function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { if (WBXML_DEBUG == true) { debugLog("SendMail: forward: {$forward} reply: {$reply} parent: {$parent}\n" . $rfc822); } // 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); //message properties to be set $mapiprops = array(); // only save the outgoing in sent items folder if the mobile requests it if (isset($storeprops[PR_IPM_SENTMAIL_ENTRYID])) { $mapiprops[PR_SENTMAIL_ENTRYID] = $storeprops[PR_IPM_SENTMAIL_ENTRYID]; } else { debugLog("PR_SENTMAIL_ENTRYID is not set. The sent message will not be moved to Sent Items."); } if ($forward) { $orig = $forward; } if ($reply) { $orig = $reply; } // Check if imtomapi function is available and use it to send the mime message. // It is available since ZCP 7.0.6 // @see http://jira.zarafa.com/browse/ZCP-9508 if (function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI')) { debugLog("Use the mapi_inetmapi_imtomapi function"); $ab = mapi_openaddressbook($this->_session); mapi_inetmapi_imtomapi($this->_session, $this->_defaultstore, $ab, $mapimessage, $rfc822, array()); // Delete the PR_SENT_REPRESENTING_* properties because some android devices // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID // which results in spooler not being able to send the message. // @see http://jira.zarafa.com/browse/ZP-85 mapi_deleteprops($mapimessage, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_SEARCH_KEY)); if (isset($orig) && $orig) { $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); if ($forward) { $this->_copyAttachments($mapimessage, $fwmessage); } $body = $this->_readPropStream($mapimessage, PR_BODY); $body_html = $this->_readPropStream($mapimessage, PR_HTML); if (strlen($body) > 0) { $fwbody = $this->_readPropStream($fwmessage, PR_BODY); $body .= $fwbody; } if (strlen($body_html) > 0) { $fwbody_html = $this->_readPropStream($fwmessage, PR_HTML); $body_html .= $fwbody_html; } mapi_setprops($mapimessage, array(PR_BODY => $body)); if (strlen($body_html) > 0) { mapi_setprops($mapimessage, array(PR_HTML => $body_html)); } } } if (!empty($mapiprops)) { mapi_setprops($mapimessage, $mapiprops); } mapi_message_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); $hr = mapi_last_hresult(); if ($hr) { debugLog(sprintf("SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult())); return false; } return true; } $mimeParams = array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'); $mimeObject = new Mail_mimeDecode($rfc822); $message = $mimeObject->decode($mimeParams); 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 (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); $fwbody = $this->_readPropStream($fwmessage, PR_BODY); $fwbody_html = $this->_readPropStream($fwmessage, PR_HTML); 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; }
/** * Updates a card * * @param mixed $addressBookId * @param string $cardUri * @param string $cardData * @return bool */ public function updateCard($addressBookId, $cardUri, $cardData) { $this->logger->info("updateCard - {$cardUri}"); if (READ_ONLY) { $this->logger->warn("Cannot update card: read-only"); return false; } // Update object properties $entryId = $this->getContactEntryId($addressBookId, $cardUri); if ($entryId === 0) { $this->logger->warn("Cannot find contact"); return false; } $mapiProperties = $this->bridge->vcardToMapiProperties($cardData); $contact = mapi_msgstore_openentry($this->bridge->getStore($addressBookId), $entryId); if (SAVE_RAW_VCARD) { // Save RAW vCard $this->logger->debug("Saving raw vcard"); $mapiProperties[PR_CARDDAV_RAW_DATA] = $cardData; $mapiProperties[PR_CARDDAV_RAW_DATA_GENERATION_TIME] = time(); } else { $this->logger->trace("Saving raw vcard skiped by config"); } // Handle contact picture if (array_key_exists('ContactPicture', $mapiProperties)) { $this->logger->debug("Updating contact picture"); $contactPicture = $mapiProperties['ContactPicture']; unset($mapiProperties['ContactPicture']); $this->bridge->setContactPicture($contact, $contactPicture); } // Remove NULL properties if (CLEAR_MISSING_PROPERTIES) { $this->logger->debug("Clearing missing properties"); $nullProperties = array(); foreach ($mapiProperties as $p => $v) { if ($v == NULL) { $nullProperties[] = $p; unset($mapiProperties[$p]); } } $dump = print_r($nullProperties, true); $this->logger->trace("Removing properties\n{$dump}"); mapi_deleteprops($contact, $nullProperties); } // Set properties $mapiProperties[PR_LAST_MODIFICATION_TIME] = time(); mapi_setprops($contact, $mapiProperties); // Save changes to backend mapi_savechanges($contact); return mapi_last_hresult() == 0; }
/** * Sets the out of office settings. * * @param SyncObject $oof * * @access private * @return void */ private function settingsOofSet(&$oof) { $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS; $props = array(); if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL || $oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) { $props[PR_EC_OUTOFOFFICE] = true; foreach ($oof->oofmessage as $oofmessage) { if (isset($oofmessage->appliesToInternal)) { $props[PR_EC_OUTOFOFFICE_MSG] = isset($oofmessage->replymessage) ? u2w($oofmessage->replymessage) : ""; $props[PR_EC_OUTOFOFFICE_SUBJECT] = "Out of office"; } } if ($oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) { if (isset($oof->starttime) && isset($oof->endtime)) { $props[PR_EC_OUTOFOFFICE_FROM] = $oof->starttime; $props[PR_EC_OUTOFOFFICE_UNTIL] = $oof->endtime; } elseif (isset($oof->starttime) || isset($oof->endtime)) { $oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR; } } else { $deleteProps = array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL); } } elseif ($oof->oofstate == SYNC_SETTINGSOOF_DISABLED) { $props[PR_EC_OUTOFOFFICE] = false; $deleteProps = array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL); } if (!empty($props)) { @mapi_setprops($this->defaultstore, $props); $result = mapi_last_hresult(); if ($result != NOERROR) { ZLog::Write(LOGLEVEL_ERROR, sprintf("Setting oof information failed (%X)", $result)); return false; } } if (!empty($deleteProps)) { @mapi_deleteprops($this->defaultstore, $deleteProps); } return true; }