/** * Writes a SyncAppointment to MAPI * * @param mixed $mapimessage * @param SyncAppointment $message * * @access private * @return boolean */ private function setAppointment($mapimessage, $appointment) { // Get timezone info if (isset($appointment->timezone)) { $tz = $this->getTZFromSyncBlob(base64_decode($appointment->timezone)); } else { $tz = false; } //calculate duration because without it some webaccess views are broken. duration is in min $localstart = $this->getLocaltimeByTZ($appointment->starttime, $tz); $localend = $this->getLocaltimeByTZ($appointment->endtime, $tz); $duration = ($localend - $localstart) / 60; //nokia sends an yearly event with 0 mins duration but as all day event, //so make it end next day if ($appointment->starttime == $appointment->endtime && isset($appointment->alldayevent) && $appointment->alldayevent) { $duration = 1440; $appointment->endtime = $appointment->starttime + 24 * 60 * 60; $localend = $localstart + 24 * 60 * 60; } // is the transmitted UID OL compatible? // if not, encapsulate the transmitted uid $appointment->uid = Utils::GetOLUidFromICalUid($appointment->uid); mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment")); $appointmentmapping = MAPIMapping::GetAppointmentMapping(); $this->setPropsInMAPI($mapimessage, $appointment, $appointmentmapping); $appointmentprops = MAPIMapping::GetAppointmentProperties(); $appointmentprops = array_merge($this->getPropIdsFromStrings($appointmentmapping), $this->getPropIdsFromStrings($appointmentprops)); //appointment specific properties to be set $props = array(); //we also have to set the responsestatus and not only meetingstatus, so we use another mapi tag $props[$appointmentprops["responsestatus"]] = isset($appointment->responsestatus) ? $appointment->responsestatus : olResponseNone; //sensitivity is not enough to mark an appointment as private, so we use another mapi tag $private = isset($appointment->sensitivity) && $appointment->sensitivity == 0 ? false : true; // Set commonstart/commonend to start/end and remindertime to start, duration, private and cleanGlobalObjectId $props[$appointmentprops["commonstart"]] = $appointment->starttime; $props[$appointmentprops["commonend"]] = $appointment->endtime; $props[$appointmentprops["reminderstart"]] = $appointment->starttime; // Set reminder boolean to 'true' if reminder is set $props[$appointmentprops["reminderset"]] = isset($appointment->reminder) ? true : false; $props[$appointmentprops["duration"]] = $duration; $props[$appointmentprops["private"]] = $private; $props[$appointmentprops["uid"]] = $appointment->uid; // Set named prop 8510, unknown property, but enables deleting a single occurrence of a recurring // type in OLK2003. $props[$appointmentprops["sideeffects"]] = 369; if (isset($appointment->reminder) && $appointment->reminder >= 0) { // Set 'flagdueby' to correct value (start - reminderminutes) $props[$appointmentprops["flagdueby"]] = $appointment->starttime - $appointment->reminder * 60; $props[$appointmentprops["remindertime"]] = $appointment->reminder; } else { $props[$appointmentprops["reminderset"]] = false; } if (isset($appointment->asbody)) { $this->setASbody($appointment->asbody, $props, $appointmentprops); } if (isset($appointment->recurrence)) { // Set PR_ICON_INDEX to 1025 to show correct icon in category view $props[$appointmentprops["icon"]] = 1025; //if there aren't any exceptions, use the 'old style' set recurrence $noexceptions = true; $recurrence = new Recurrence($this->store, $mapimessage); $recur = array(); $this->setRecurrence($appointment, $recur); // set the recurrence type to that of the MAPI $props[$appointmentprops["recurrencetype"]] = $recur["recurrencetype"]; $starttime = $this->gmtime($localstart); $endtime = $this->gmtime($localend); //set recurrence start here because it's calculated differently for tasks and appointments $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, $tz)); $recur["startocc"] = $starttime["tm_hour"] * 60 + $starttime["tm_min"]; $recur["endocc"] = $recur["startocc"] + $duration; // Note that this may be > 24*60 if multi-day //only tasks can regenerate $recur["regen"] = false; // Process exceptions. The PDA will send all exceptions for this recurring item. if (isset($appointment->exceptions)) { foreach ($appointment->exceptions as $exception) { // we always need the base date if (!isset($exception->exceptionstarttime)) { continue; } if (isset($exception->deleted) && $exception->deleted) { // Delete exception if (!isset($recur["deleted_occurences"])) { $recur["deleted_occurences"] = array(); } array_push($recur["deleted_occurences"], $this->getDayStartOfTimestamp($exception->exceptionstarttime)); } else { // Change exception $basedate = $this->getDayStartOfTimestamp($exception->exceptionstarttime); $mapiexception = array("basedate" => $basedate); //other exception properties which are not handled in recurrence $exceptionprops = array(); if (isset($exception->starttime)) { $mapiexception["start"] = $this->getLocaltimeByTZ($exception->starttime, $tz); $exceptionprops[$appointmentprops["starttime"]] = $exception->starttime; } if (isset($exception->endtime)) { $mapiexception["end"] = $this->getLocaltimeByTZ($exception->endtime, $tz); $exceptionprops[$appointmentprops["endtime"]] = $exception->endtime; } if (isset($exception->subject)) { $exceptionprops[$appointmentprops["subject"]] = $mapiexception["subject"] = u2w($exception->subject); } if (isset($exception->location)) { $exceptionprops[$appointmentprops["location"]] = $mapiexception["location"] = u2w($exception->location); } if (isset($exception->busystatus)) { $exceptionprops[$appointmentprops["busystatus"]] = $mapiexception["busystatus"] = $exception->busystatus; } if (isset($exception->reminder)) { $exceptionprops[$appointmentprops["reminderset"]] = $mapiexception["reminder_set"] = 1; $exceptionprops[$appointmentprops["remindertime"]] = $mapiexception["remind_before"] = $exception->reminder; } if (isset($exception->alldayevent)) { $exceptionprops[$appointmentprops["alldayevent"]] = $mapiexception["alldayevent"] = $exception->alldayevent; } if (!isset($recur["changed_occurences"])) { $recur["changed_occurences"] = array(); } if (isset($exception->body)) { $exceptionprops[$appointmentprops["body"]] = u2w($exception->body); } array_push($recur["changed_occurences"], $mapiexception); if (!empty($exceptionprops)) { $noexceptions = false; if ($recurrence->isException($basedate)) { $recurrence->modifyException($exceptionprops, $basedate); } else { $recurrence->createException($exceptionprops, $basedate); } } } } } //setRecurrence deletes the attachments from an appointment if ($noexceptions) { $recurrence->setRecurrence($tz, $recur); } } else { $props[$appointmentprops["isrecurring"]] = false; } //always set the PR_SENT_REPRESENTING_* props so that the attendee status update also works with the webaccess $p = array($appointmentprops["representingentryid"], $appointmentprops["representingname"], $appointmentprops["sentrepresentingaddt"], $appointmentprops["sentrepresentingemail"], $appointmentprops["sentrepresentinsrchk"]); $representingprops = $this->getProps($mapimessage, $p); if (!isset($representingprops[$appointmentprops["representingentryid"]])) { $props[$appointmentprops["representingname"]] = Request::GetAuthUser(); $props[$appointmentprops["sentrepresentingemail"]] = Request::GetAuthUser(); $props[$appointmentprops["sentrepresentingaddt"]] = "ZARAFA"; $props[$appointmentprops["representingentryid"]] = mapi_createoneoff(Request::GetAuthUser(), "ZARAFA", Request::GetAuthUser()); $props[$appointmentprops["sentrepresentinsrchk"]] = $props[$appointmentprops["sentrepresentingaddt"]] . ":" . $props[$appointmentprops["sentrepresentingemail"]]; } // Do attendees if (isset($appointment->attendees) && is_array($appointment->attendees)) { $recips = array(); // Outlook XP requires organizer in the attendee list as well $org = array(); $org[PR_ENTRYID] = isset($representingprops[$appointmentprops["representingentryid"]]) ? $representingprops[$appointmentprops["representingentryid"]] : $props[$appointmentprops["representingentryid"]]; $org[PR_DISPLAY_NAME] = isset($representingprops[$appointmentprops["representingname"]]) ? $representingprops[$appointmentprops["representingname"]] : $props[$appointmentprops["representingname"]]; $org[PR_ADDRTYPE] = isset($representingprops[$appointmentprops["sentrepresentingaddt"]]) ? $representingprops[$appointmentprops["sentrepresentingaddt"]] : $props[$appointmentprops["sentrepresentingaddt"]]; $org[PR_EMAIL_ADDRESS] = isset($representingprops[$appointmentprops["sentrepresentingemail"]]) ? $representingprops[$appointmentprops["sentrepresentingemail"]] : $props[$appointmentprops["sentrepresentingemail"]]; $org[PR_SEARCH_KEY] = isset($representingprops[$appointmentprops["sentrepresentinsrchk"]]) ? $representingprops[$appointmentprops["sentrepresentinsrchk"]] : $props[$appointmentprops["sentrepresentinsrchk"]]; $org[PR_RECIPIENT_FLAGS] = recipOrganizer | recipSendable; $org[PR_RECIPIENT_TYPE] = MAPI_TO; array_push($recips, $org); //open addresss book for user resolve $addrbook = $this->getAddressbook(); foreach ($appointment->attendees as $attendee) { $recip = array(); $recip[PR_EMAIL_ADDRESS] = u2w($attendee->email); // lookup information in GAB if possible so we have up-to-date name for given address $userinfo = array(array(PR_DISPLAY_NAME => $recip[PR_EMAIL_ADDRESS])); $userinfo = mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP); if (mapi_last_hresult() == NOERROR) { $recip[PR_DISPLAY_NAME] = $userinfo[0][PR_DISPLAY_NAME]; $recip[PR_EMAIL_ADDRESS] = $userinfo[0][PR_EMAIL_ADDRESS]; $recip[PR_SEARCH_KEY] = $userinfo[0][PR_SEARCH_KEY]; $recip[PR_ADDRTYPE] = $userinfo[0][PR_ADDRTYPE]; $recip[PR_ENTRYID] = $userinfo[0][PR_ENTRYID]; $recip[PR_RECIPIENT_TYPE] = MAPI_TO; $recip[PR_RECIPIENT_FLAGS] = recipSendable; } else { $recip[PR_DISPLAY_NAME] = u2w($attendee->name); $recip[PR_SEARCH_KEY] = "SMTP:" . $recip[PR_EMAIL_ADDRESS] . ""; $recip[PR_ADDRTYPE] = "SMTP"; $recip[PR_RECIPIENT_TYPE] = MAPI_TO; $recip[PR_ENTRYID] = mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]); } array_push($recips, $recip); } mapi_message_modifyrecipients($mapimessage, 0, $recips); $props[$appointmentprops["icon"]] = 1026; $props[$appointmentprops["mrwassent"]] = true; } mapi_setprops($mapimessage, $props); }
/** * Function which clones current occurrence and sets appropriate properties. * The original recurring item is moved to next occurrence. *@param boolean $markComplete true if existing occurrence has to be mark complete else false. */ function regenerateTask($markComplete) { // Get all properties $taskItemProps = mapi_getprops($this->message); if (isset($this->action["subject"])) { $taskItemProps[$this->proptags["subject"]] = $this->action["subject"]; } if (isset($this->action["importance"])) { $taskItemProps[$this->proptags["importance"]] = $this->action["importance"]; } if (isset($this->action["startdate"])) { $taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"]; $taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"]; } if (isset($this->action["duedate"])) { $taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"]; $taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"]; } $folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]); $newMessage = mapi_folder_createmessage($folder); $taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted; $taskItemProps[$this->proptags["complete"]] = $markComplete; $taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0; // This occurrence has been marked as 'Complete' so disable reminder if ($markComplete) { $taskItemProps[$this->proptags["reset_reminder"]] = false; $taskItemProps[$this->proptags["reminder"]] = false; $taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"]; unset($this->action[$this->proptags['datecompleted']]); } // Recurrence ends for this item $taskItemProps[$this->proptags["dead_occurrence"]] = true; $taskItemProps[$this->proptags["task_f_creator"]] = true; //OL props $taskItemProps[$this->proptags["side_effects"]] = 1296; $taskItemProps[$this->proptags["icon_index"]] = 1280; // Copy recipients $recipienttable = mapi_message_getrecipienttable($this->message); $recipients = mapi_table_queryallrows($recipienttable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID)); $copy_to_recipientTable = mapi_message_getrecipienttable($newMessage); $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID)); foreach ($copy_to_recipientRows as $recipient) { mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient)); } mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients); // Copy attachments $attachmentTable = mapi_message_getattachmenttable($this->message); if ($attachmentTable) { $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); foreach ($attachments as $attach_props) { $attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]); $attach_newResourceMsg = mapi_message_createattach($newMessage); mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0); mapi_savechanges($attach_newResourceMsg); } } mapi_setprops($newMessage, $taskItemProps); mapi_savechanges($newMessage); // Update body of original message $msgbody = mapi_message_openproperty($this->message, PR_BODY); $msgbody = trim($this->windows1252_to_utf8($msgbody), ""); $separator = "------------\r\n"; if (!empty($msgbody) && strrpos($msgbody, $separator) === false) { $msgbody = $separator . $msgbody; $stream = mapi_openpropertytostream($this->message, PR_BODY, MAPI_CREATE | MAPI_MODIFY); mapi_stream_setsize($stream, strlen($msgbody)); mapi_stream_write($stream, $msgbody); mapi_stream_commit($stream); } // We need these properties to notify client return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID)); }
/** * Clear responses of all attendees who have replied in past. * @param MAPI_MESSAGE $message on which responses should be cleared */ function clearRecipientResponse($message) { $recipTable = mapi_message_getrecipienttable($message); $recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops); foreach ($recipsRows as $recipient) { if (($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer) { // Recipient is attendee, set the trackstatus to "Not Responded" $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; } else { // Recipient is organizer, this is not possible, but for safety // it is best to clear the trackstatus for him as well by setting // the trackstatus to "Organized". $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; } mapi_message_modifyrecipients($message, MODRECIP_MODIFY, array($recipient)); } }
/** * Function which applies the provided recipients to the exception, also checks for deleted recipients. * * The $exception_recips should be an array containing all recipients which must be applied * to the exception. This will copy all recipients from the original message and then start filter * out all recipients which are not provided by the $exception_recips list. * * @param resource $message exception attachment of recurring item * @param array $exception_recips list of recipients */ function setAllExceptionRecipients($message, $exception_recips) { $deletedRecipients = array(); $useMessageRecipients = false; $recipientTable = mapi_message_getrecipienttable($message); $recipientRows = mapi_table_queryallrows($recipientTable, $this->recipprops); if (empty($recipientRows)) { $useMessageRecipients = true; $recipientTable = mapi_message_getrecipienttable($this->message); $recipientRows = mapi_table_queryallrows($recipientTable, $this->recipprops); } // Add organizer to meeting only if it is not organized. $msgprops = mapi_getprops($message, array(PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_ADDRTYPE, $this->proptags['responsestatus'])); if (isset($msgprops[$this->proptags['responsestatus']]) && $msgprops[$this->proptags['responsestatus']] != olResponseOrganized) { $this->addOrganizer($msgprops, $exception_recips); } if (!empty($exception_recips)) { foreach ($recipientRows as $key => $recipient) { $found = false; foreach ($exception_recips as $excep_recip) { if (isset($recipient[PR_SEARCH_KEY]) && isset($excep_recip[PR_SEARCH_KEY]) && $recipient[PR_SEARCH_KEY] == $excep_recip[PR_SEARCH_KEY]) { $found = true; } } if (!$found) { $foundInDeletedRecipients = false; // Look if the $recipient is in the list of deleted recipients if (!empty($deletedRecipients)) { foreach ($deletedRecipients as $recip) { if ($recip[PR_SEARCH_KEY] == $recipient[PR_SEARCH_KEY]) { $foundInDeletedRecipients = true; break; } } } // If recipient is not in list of deleted recipient, add him if (!$foundInDeletedRecipients) { if (!isset($recipient[PR_RECIPIENT_FLAGS]) || $recipient[PR_RECIPIENT_FLAGS] != (recipReserved | recipExceptionalDeleted | recipSendable)) { $recipient[PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalDeleted; } else { $recipient[PR_RECIPIENT_FLAGS] = recipReserved | recipExceptionalDeleted | recipSendable; } $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; // No Response required $deletedRecipients[] = $recipient; } } // When $message contains a non-empty recipienttable, we must delete the recipients // before re-adding them. However, when $message is doesn't contain any recipients, // we are using the recipient table of the original message ($this->message) // rather then $message. In that case, we don't need to remove the recipients // from the $message, as the recipient table is already empty, and // mapi_message_modifyrecipients() will throw an error. if ($useMessageRecipients === false) { mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient)); } } $exception_recips = array_merge($exception_recips, $deletedRecipients); } else { $exception_recips = $recipientRows; } if (!empty($exception_recips)) { // Set the new list of recipients on the exception message, this also removes the existing recipients mapi_message_modifyrecipients($message, 0, $exception_recips); } }
/** * Adds the recipients to an email message from a RFC822 message headers. * * @param MIMEMessageHeader $headers * @param MAPIMessage $mapimessage */ private function addRecipients($headers, &$mapimessage) { $toaddr = $ccaddr = $bccaddr = array(); $Mail_RFC822 = new Mail_RFC822(); if (isset($headers["to"])) { $toaddr = $Mail_RFC822->parseAddressList($headers["to"]); } if (isset($headers["cc"])) { $ccaddr = $Mail_RFC822->parseAddressList($headers["cc"]); } if (isset($headers["bcc"])) { $bccaddr = $Mail_RFC822->parseAddressList($headers["bcc"]); } if (empty($toaddr)) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): 'To' address in RFC822 message not found or unparsable. To header: '%s'", isset($headers["to"]) ? $headers["to"] : ''), SYNC_COMMONSTATUS_MESSHASNORECIP); } // Add recipients $recips = array(); 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); }
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; }
/** Deletes all recipients from given message object * *@param $message MAPI message from which recipients are to be removed. */ function deleteAllRecipients($message) { $recipTable = mapi_message_getrecipienttable($message); $recipRows = mapi_table_queryallrows($recipTable, array(PR_ROWID)); foreach ($recipRows as $recipient) { mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient)); } }
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; }
function SendMail($rfc822, $smartdata = array(), $protocolversion = false) { if (WBXML_DEBUG === true && $protocolversion <= 14.0) { debugLog("SendMail: task " . $smartdata['task'] . " itemid: " . (isset($smartdata['itemid']) ? $smartdata['itemid'] : "") . " parent: " . (isset($smartdata['folderid']) ? $smartdata['folderid'] : "") . "\n" . $rfc822); } $mimeParams = array('decode_headers' => false, '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 => u2w($mimeObject->_decodeHeader(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"]); } if (count($toaddr) == 0 && count($ccaddr) == 0 && count($bccaddr) == 0) { debugLog("Sendmail: Message has got no recipients (no to, no cc and no bcc!)"); return 119; } // 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 message subparts. $body = ""; $body_html = ""; if ($message->ctype_primary == "multipart" && ($message->ctype_secondary == "signed" || $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 .= u2w($part->body); // assume only one text body } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "html") { $body_html .= u2w($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); } debugLog(print_r($mapiprops, true)); 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 $this->_storeAttachment($mapimessage, $part); debugLog("Sending ICS file as attachment"); } } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "x-vCalendar") { $zpical = new ZPush_ical($this->_defaultstore); $mapiprops = array(); $zpical->extractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { // dw2412 Nokia sends incomplete iCal calendar item, so we have to add properties like // message class and icon index if (isset($mapiprops[PR_MESSAGE_CLASS]) && $mapiprops[PR_MESSAGE_CLASS] == "IPM.Note" || !isset($mapiprops[PR_MESSAGE_CLASS])) { $mapiprops[PR_ICON_INDEX] = 0x404; $mapiprops[PR_OWNER_APPT_ID] = 0; $mapiprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Request"; } // dw2412 Nokia sends no location information field in case user did not type in some // thing in this field... $namedIntentedBusyStatus = GetPropIDFromString($this->_defaultstore, "PT_LONG:{00062002-0000-0000-C000-000000000046}:8224"); if (!isset($mapiprops[$namedIntentedBusyStatus])) { $mapiprops[$namedIntentedBusyStatus] = 0; } $namedLocation = GetPropIDFromString($this->_defaultstore, "PT_STRING8:{00062002-0000-0000-C000-000000000046}:0x8208"); if (!isset($mapiprops[$namedLocation])) { $mapiprops[$namedLocation] = ""; } $tnefLocation = GetPropIDFromString($this->_defaultstore, "PT_STRING8:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x2"); if (!isset($mapiprops[$tnefLocation])) { $mapiprops[$tnefLocation] = ""; } $useTNEF = GetPropIDFromString($this->_defaultstore, "PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8582"); $mapiprops[$useTNEF] = true; } else { debugLog("ICAL: Mapi props array was empty"); } } elseif ($part->ctype_primary == 'application' && $part->ctype_secondary == 'x-pkcs7-signature') { $rfc822_p7m = $this->_rfc822tosmimep7m($rfc822); $smimepart = (object) array('ctype_primary' => 'multipart', 'ctype_secondary' => 'signed', 'body' => $rfc822_p7m); debugLog(print_r($smimepart, true)); $mapiprops = array(); $mapiprops[PR_MESSAGE_CLASS] = "IPM.Note.SMIME.MultipartSigned"; $hideattachments = GetPropIDFromString($this->_defaultstore, "PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8514"); $mapiprops[$hideattachments] = true; mapi_setprops($mapimessage, $mapiprops); $this->_storeAttachment($mapimessage, $smimepart); } else { $this->_storeAttachment($mapimessage, $part); } } } else { // start dw2412 handle smime encrypted emails... if ($message->ctype_primary == "application" && $message->ctype_secondary == "x-pkcs7-mime") { $mapiprops = array(); $mapiprops[PR_MESSAGE_CLASS] = "IPM.Note.SMIME"; $hideattachments = GetPropIDFromString($this->_defaultstore, "PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8514"); $mapiprops[$hideattachments] = true; $mapicontenttype = mapi_prop_tag(PT_STRING8, 0x85e8); $mapiprops[$mapicontenttype] = $message->headers['content-type']; mapi_setprops($mapimessage, $mapiprops); $this->_storeAttachment($mapimessage, $message); } // end dw2412 handle encrypted emails // start dw2412 handle windows mobile html replies... // standard body if ($message->ctype_primary == "text" && $message->ctype_secondary == "plain" && isset($message->body) && (!isset($message->disposition) || $message->disposition != "attachment")) { $body = u2w($message->body); // assume only one text body } elseif ($message->ctype_primary == "text" && $message->ctype_secondary == "html") { $body_html = u2w($message->body); } // end dw2412 handle windows mobile html replies... // $body = u2w($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); } // START ADDED dw2412 update/create conversation index $conversationindex = false; // END ADDED dw2412 update/create conversation index if (isset($smartdata['itemid']) && $smartdata['itemid'] || isset($smartdata['longid']) && $smartdata['longid']) { // Append the original text body for reply/forward if (isset($smartdata['longid'])) { $entryid = hex2bin($smartdata['longid']); } else { $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($smartdata['folderid']), hex2bin($smartdata['itemid'])); } if (($fwmessage = mapi_msgstore_openentry($this->_defaultstore, $entryid)) === false) { debugLog("Sendmail: Provided EntryID cannot be opened. Return error code 150."); switch ($smartdata['task']) { case 'forward': return 150; case 'reply': return 150; } } if ($fwmessage) { // START CHANGED dw2412 LAST Verb Exec Props included //update icon when forwarding or replying message if ($smartdata['task'] == 'forward') { mapi_setprops($fwmessage, array(PR_LAST_VERB_EXECUTED => 0x68, PR_LAST_VERB_EXECUTION_TIME => time())); mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($smartdata['task'] == 'reply') { // START ADDED dw2412 update/create conversation index $fwmessageprops = mapi_getprops($fwmessage, array(PR_CONVERSATION_INDEX)); if (isset($fwmessageprops[PR_CONVERSATION_INDEX])) { $conversationindex = $fwmessageprops[PR_CONVERSATION_INDEX]; } // END ADDED dw2412 update/create conversation index if (sizeof($recips) > 1) { mapi_setprops($fwmessage, array(PR_LAST_VERB_EXECUTED => 0x67, PR_LAST_VERB_EXECUTION_TIME => time())); } else { mapi_setprops($fwmessage, array(PR_LAST_VERB_EXECUTED => 0x66, PR_LAST_VERB_EXECUTION_TIME => time())); } mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } else { mapi_setprops($fwmessage, array(PR_LAST_VERB_EXECUTED => 0x0, PR_LAST_VERB_EXECUTION_TIME => time())); } // END CHANGED dw2412 LAST Verb Exec Props included 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; } $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; } // dw2412 Enable this only in case of AS2.5 Protocol... in AS12 this seem // being done already by winmobile client. if ($smartdata['task'] == 'forward' && $protocolversion <= 2.5) { // 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 ($smartdata['task'] == 'forward') { // Add attachments from the original message in a forward if (isset($smartdata['longid'])) { $entryid = hex2bin($smartdata['longid']); } else { $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($smartdata['folderid']), hex2bin($smartdata['itemid'])); } $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); } } } // START ADDED dw2412 update/create conversation index if (CONVERSATIONINDEX == true) { $ci = new ConversationIndex(); if ($conversationindex) { $ci->Decode($conversationindex); $ci->Update(); } else { $ci->Create(); } mapi_setprops($mapimessage, array(PR_CONVERSATION_INDEX => $ci->Encode())); } // END ADDED dw2412 update/create conversation index //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)); } if (mapi_savechanges($mapimessage) === false || mapi_message_submitmessage($mapimessage) === false) { switch ($smartdata['task']) { case 'reply': debugLog("Sendmail: Message reply failed, sending failed at all"); return 121; default: debugLog("Sendmail: Message failed to be saved/submitted, sending failed at all"); return 120; } } return true; }
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; }