function delete_messages($folder, $messages) { global $hard_delete_messages; if ($hard_delete_messages) { $result = mapi_folder_deletemessages($folder, $messages, DELETE_HARD_DELETE); } else { $result = mapi_folder_deletemessages($folder, $messages); } if ($result == false) { echo " [-] Failed to delete message\n"; } }
/** * Function creates meeting item in resource's calendar. *@param resource $message MAPI_message which is to create in resource's calendar *@param boolean $cancel cancel meeting *@param string $prefix prefix for subject of meeting */ function bookResources($message, $cancel, $prefix, $basedate = false) { if (!$this->enableDirectBooking) { return array(); } // Get the properties of the message $messageprops = mapi_getprops($message); if ($basedate) { $recurrItemProps = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID)); $messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate); $messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']]; // Delete properties which are not needed. $deleteProps = array($this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD); foreach ($deleteProps as $propID) { if (isset($messageprops[$propID])) { unset($messageprops[$propID]); } } if (isset($messageprops[$this->proptags['recurring']])) { $messageprops[$this->proptags['recurring']] = false; } // Set Outlook properties $messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']]; $messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']]; $messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']]; $messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']]; $messageprops[$this->proptags['attendee_critical_change']] = time(); $messageprops[$this->proptags['owner_critical_change']] = time(); } // Get resource recipients $getResourcesRestriction = array(RES_AND, array(array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => PR_RECIPIENT_TYPE, VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC))))); $recipienttable = mapi_message_getrecipienttable($message); $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction); $this->errorSetResource = false; $resourceRecipData = array(); // Put appointment into store resource users $i = 0; $len = count($resourceRecipients); while (!$this->errorSetResource && $i < $len) { $request = array(array(PR_DISPLAY_NAME => $resourceRecipients[$i][PR_DISPLAY_NAME])); $ab = mapi_openaddressbook($this->session); $ret = mapi_ab_resolvename($ab, $request, EMS_AB_ADDRESS_LOOKUP); $result = mapi_last_hresult(); if ($result == NOERROR) { $result = $ret[0][PR_ENTRYID]; } $resourceUsername = $ret[0][PR_EMAIL_ADDRESS]; $resourceABEntryID = $ret[0][PR_ENTRYID]; // Get StoreEntryID by username $user_entryid = mapi_msgstore_createentryid($this->store, $resourceUsername); // Open store of the user $userStore = mapi_openmsgstore($this->session, $user_entryid); // Open root folder $userRoot = mapi_msgstore_openentry($userStore, null); // Get calendar entryID $userRootProps = mapi_getprops($userRoot, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS)); // Open Calendar folder [check hresult==0] $accessToFolder = false; try { $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]); if ($calFolder) { $calFolderProps = mapi_getProps($calFolder, array(PR_ACCESS)); if (($calFolderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) !== 0) { $accessToFolder = true; } } } catch (MAPIException $e) { $e->setHandled(); $this->errorSetResource = 1; // No access } if ($accessToFolder) { /** * Get the LocalFreebusy message that contains the properties that * are set to accept or decline resource meeting requests */ // Use PR_FREEBUSY_ENTRYIDS[1] to open folder the LocalFreeBusy msg $localFreebusyMsg = mapi_msgstore_openentry($userStore, $userRootProps[PR_FREEBUSY_ENTRYIDS][1]); if ($localFreebusyMsg) { $props = mapi_getprops($localFreebusyMsg, array(PR_PROCESS_MEETING_REQUESTS, PR_DECLINE_RECURRING_MEETING_REQUESTS, PR_DECLINE_CONFLICTING_MEETING_REQUESTS)); $acceptMeetingRequests = $props[PR_PROCESS_MEETING_REQUESTS] ? 1 : 0; $declineRecurringMeetingRequests = $props[PR_DECLINE_RECURRING_MEETING_REQUESTS] ? 1 : 0; $declineConflictingMeetingRequests = $props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS] ? 1 : 0; if (!$acceptMeetingRequests) { /** * When a resource has not been set to automatically accept meeting requests, * the meeting request has to be sent to him rather than being put directly into * his calendar. No error should be returned. */ //$errorSetResource = 2; $this->nonAcceptingResources[] = $resourceRecipients[$i]; } else { if ($declineRecurringMeetingRequests && !$cancel) { // Check if appointment is recurring if ($messageprops[$this->proptags['recurring']]) { $this->errorSetResource = 3; } } if ($declineConflictingMeetingRequests && !$cancel) { // Check for conflicting items $conflicting = false; // Open the calendar $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]); if ($calFolder) { if ($this->isMeetingConflicting($message, $userStore, $calFolder, $messageprops)) { $conflicting = true; } } else { $this->errorSetResource = 1; // No access } if ($conflicting) { $this->errorSetResource = 4; // Conflict } } } } } if (!$this->errorSetResource && $accessToFolder) { /** * First search on GlobalID(0x3) * If (recurring and occurrence) If Resource was booked for only this occurrence then Resource should have only this occurrence in Calendar and not whole series. * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesnt matter if search is based on GlobalID. */ $rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder); /** * If no entry is found then * 1) Resource doesnt have meeting in Calendar. Seriously!! * OR * 2) We were looking for occurrence item but Resource has whole series */ if (empty($rows)) { /** * Now search on CleanGlobalID(0x23) WHY??? * Because we are looking recurring item * * Possible results of this search * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID * 2) If Resource was booked for whole series then it should return series. */ $rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true); $newResourceMsg = false; if (!empty($rows)) { // Since we are looking for recurring item, open every result and check for 'recurring' property. foreach ($rows as $row) { $ResourceMsg = mapi_msgstore_openentry($userStore, $row); $ResourceMsgProps = mapi_getprops($ResourceMsg, array($this->proptags['recurring'])); if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) { $newResourceMsg = $ResourceMsg; break; } } } // Still no results found. I giveup, create new message. if (!$newResourceMsg) { $newResourceMsg = mapi_folder_createmessage($calFolder); } } else { $newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]); } // Prefix the subject if needed if ($prefix && isset($messageprops[PR_SUBJECT])) { $messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT]; } // Set status to cancelled if needed $messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy) if ($cancel) { $messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled $messageprops[$this->proptags['busystatus']] = fbFree; // Free } else { $messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request } $messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource autmatically accepts the appointment $messageprops[PR_MESSAGE_CLASS] = "IPM.Appointment"; // Remove the PR_ICON_INDEX as it is not needed in the sent message and it also // confuses the Zarafa webaccess $messageprops[PR_ICON_INDEX] = null; $messageprops[PR_RESPONSE_REQUESTED] = true; $addrinfo = $this->getOwnerAddress($this->store); if ($addrinfo) { list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo; $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr; $messageprops[PR_SENT_REPRESENTING_NAME] = $ownername; $messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype; $messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid; $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey; $messageprops[$this->proptags['apptreplyname']] = $ownername; $messageprops[$this->proptags['replytime']] = time(); } if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) { $recurr = new Recurrence($userStore, $newResourceMsg); // Copy recipients list $reciptable = mapi_message_getrecipienttable($message); $recips = mapi_table_queryallrows($reciptable, $this->recipprops); // add owner to recipient table $this->addOrganizer($messageprops, $recips, true); // Update occurrence if ($recurr->isException($basedate)) { $recurr->modifyException($messageprops, $basedate, $recips); } else { $recurr->createException($messageprops, $basedate, false, $recips); } } else { mapi_setprops($newResourceMsg, $messageprops); // Copy attachments $this->replaceAttachments($message, $newResourceMsg); // Copy all recipients too $this->replaceRecipients($message, $newResourceMsg); // Now add organizer also to recipient table $recips = array(); $this->addOrganizer($messageprops, $recips); mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips); } mapi_savechanges($newResourceMsg); $resourceRecipData[] = array('store' => $userStore, 'folder' => $calFolder, 'msg' => $newResourceMsg); $this->includesResources = true; } else { /** * If no other errors occured and you have no access to the * folder of the resource, throw an error=1. */ if (!$this->errorSetResource) { $this->errorSetResource = 1; } for ($j = 0, $len = count($resourceRecipData); $j < $len; $j++) { // Get the EntryID $props = mapi_message_getprops($resourceRecipData[$j]['msg']); mapi_folder_deletemessages($resourceRecipData[$j]['folder'], array($props[PR_ENTRYID]), DELETE_HARD_DELETE); } $this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME]; } $i++; } /************************************************************** * Set the BCC-recipients (resources) tackstatus to accepted. */ // Get resource recipients $getResourcesRestriction = array(RES_AND, array(array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => PR_RECIPIENT_TYPE, VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC))))); $recipienttable = mapi_message_getrecipienttable($message); $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction); if (!empty($resourceRecipients)) { // Set Tracking status of resource recipients to olResponseAccepted (3) for ($i = 0, $len = count($resourceRecipients); $i < $len; $i++) { $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted; $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time(); } mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients); } // Publish updated free/busy information if (!$this->errorSetResource) { for ($i = 0, $len = count($resourceRecipData); $i < $len; $i++) { $storeProps = mapi_msgstore_getprops($resourceRecipData[$i]['store'], array(PR_MAILBOX_OWNER_ENTRYID)); if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID])) { $pub = new FreeBusyPublish($this->session, $resourceRecipData[$i]['store'], $resourceRecipData[$i]['folder'], $storeProps[PR_MAILBOX_OWNER_ENTRYID]); $pub->publishFB(time() - 7 * 24 * 60 * 60, 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead } } } return $resourceRecipData; }
/** * Imports a move of a message. This occurs when a user moves an item to another folder * * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer, * but the Zarafa importer does not support this. Therefore we currently implement it via a standard mapi * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync. * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties * of the source message to the new one and then delete the source message. * * @param string $id * @param string $newfolder destination folder * * @access public * @return boolean * @throws StatusException */ public function ImportMessageMove($id, $newfolder) { if (strtolower($newfolder) == strtolower(bin2hex($this->folderid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } // Get the entryid of the message we're moving $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id)); if (!$entryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } //open the source message $srcmessage = mapi_msgstore_openentry($this->store, $entryid); if (!$srcmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get correct mapi store for the destination folder $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); if ($dststore === false) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder)); if (!$dstentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid); if (!$dstfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newmessage = mapi_folder_createmessage($dstfolder); if (!$newmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } // Copy message mapi_copyto($srcmessage, array(), array(), $newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid); if (!$srcfolderentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid); if (!$srcfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // Save changes mapi_savechanges($newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // Delete the old message if (!mapi_folder_deletemessages($srcfolder, array($entryid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED); } $sourcekeyprops = mapi_getprops($newmessage, array(PR_SOURCE_KEY)); if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) { return bin2hex($sourcekeyprops[PR_SOURCE_KEY]); } return false; }
/** * Processes a response to a meeting request. * CalendarID is a reference and has to be set if a new calendar item is created * * @param string $requestid id of the object containing the request * @param string $folderid id of the parent folder of $requestid * @param string $response * * @access public * @return string id of the created/updated calendar obj * @throws StatusException */ public function MeetingResponse($requestid, $folderid, $response) { // Use standard meeting response code to process meeting request $reqentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid)); if (!$reqentryid) { throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } $mapimessage = mapi_msgstore_openentry($this->store, $reqentryid); if (!$mapimessage) { throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, unable to open request message for response 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } $meetingrequest = new Meetingrequest($this->store, $mapimessage); if (!$meetingrequest->isMeetingRequest()) { throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, attempt to respond to non-meeting request", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } if ($meetingrequest->isLocalOrganiser()) { throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, attempt to response to meeting request that we organized", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } // Process the meeting response. We don't have to send the actual meeting response // e-mail, because the device will send it itself. switch ($response) { case 1: // accept // accept default: $entryid = $meetingrequest->doAccept(false, false, false, false, false, false, true); // last true is the $userAction break; case 2: // tentative $entryid = $meetingrequest->doAccept(true, false, false, false, false, false, true); // last true is the $userAction break; case 3: // decline $meetingrequest->doDecline(false); break; } // F/B will be updated on logoff // We have to return the ID of the new calendar item, so do that here $calendarid = ""; if (isset($entryid)) { $newitem = mapi_msgstore_openentry($this->store, $entryid); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); } // on recurring items, the MeetingRequest class responds with a wrong entryid if ($requestid == $calendarid) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): returned calender id is the same as the requestid - re-searching", $requestid, $folderid, $response)); $props = MAPIMapping::GetMeetingRequestProperties(); $props = getPropIdsFromStrings($this->store, $props); $messageprops = mapi_getprops($mapimessage, array($props["goidtag"])); $goid = $messageprops[$props["goidtag"]]; $items = $meetingrequest->findCalendarItems($goid); if (is_array($items)) { $newitem = mapi_msgstore_openentry($this->store, $items[0]); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): found other calendar entryid", $requestid, $folderid, $response)); } if ($requestid == $calendarid) { throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error finding the accepted meeting response in the calendar", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } } // delete meeting request from Inbox $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid)); $folder = mapi_msgstore_openentry($this->store, $folderentryid); mapi_folder_deletemessages($folder, array($reqentryid), 0); return $calendarid; }
function MeetingResponse($requestid, $folderid, $response, &$calendarid) { // Use standard meeting response code to process meeting request $reqentryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid), hex2bin($requestid)); $mapimessage = mapi_msgstore_openentry($this->_defaultstore, $reqentryid); if (!$mapimessage) { debugLog("Unable to open request message for response"); return false; } $meetingrequest = new Meetingrequest($this->_defaultstore, $mapimessage); if (!$meetingrequest->isMeetingRequest()) { debugLog("Attempt to respond to non-meeting request"); return false; } if ($meetingrequest->isLocalOrganiser()) { debugLog("Attempt to response to meeting request that we organized"); return false; } // Process the meeting response. We don't have to send the actual meeting response // e-mail, because the device will send it itself. switch ($response) { case 1: // accept // accept default: $entryid = $meetingrequest->doAccept(false, false, false, false, false, false, true); // last true is the $userAction break; case 2: // tentative $entryid = $meetingrequest->doAccept(true, false, false, false, false, false, true); // last true is the $userAction break; case 3: // decline $meetingrequest->doDecline(false); break; } // F/B will be updated on logoff // We have to return the ID of the new calendar item, so do that here if (isset($entryid)) { $newitem = mapi_msgstore_openentry($this->_defaultstore, $entryid); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); } // on recurring items, the MeetingRequest class responds with a wrong entryid if ($requestid == $calendarid) { debugLog("returned calender id is the same as the requestid - re-searching"); $goidprop = GetPropIDFromString($this->_defaultstore, "PT_BINARY:{6ED8DA90-450B-101B-98DA-00AA003F1305}:0x3"); $messageprops = mapi_getprops($mapimessage, array($goidprop, PR_OWNER_APPT_ID)); $goid = $messageprops[$goidprop]; if (isset($messageprops[PR_OWNER_APPT_ID])) { $apptid = $messageprops[PR_OWNER_APPT_ID]; } else { $apptid = false; } //findCalendarItems signature was changed in 6.40.8, Mantis #485 $items = checkMapiExtVersion("6.40.8") ? $meetingrequest->findCalendarItems($goid) : $meetingrequest->findCalendarItems($goid, $apptid); if (is_array($items)) { $newitem = mapi_msgstore_openentry($this->_defaultstore, $items[0]); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); debugLog("found other calendar entryid"); } } // delete meeting request from Inbox $folderentryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid)); $folder = mapi_msgstore_openentry($this->_defaultstore, $folderentryid); mapi_folder_deletemessages($folder, array($reqentryid), 0); return true; }
$store = mapi_openmsgstore($l_rSession, $storeEntryId); $root = mapi_msgstore_openentry($store, null); $spamStoreProps = mapi_getprops($root, array(PR_ADDITIONAL_REN_ENTRYIDS)); $spamFolder = mapi_msgstore_openentry($store, $spamStoreProps[PR_ADDITIONAL_REN_ENTRYIDS][4]); $table = mapi_folder_getcontentstable($spamFolder); $spamRows = mapi_table_queryallrows($table, array(PR_ENTRYID, PR_CREATION_TIME)); echo (mapi_last_hresult() == 0 ? "Fetching messages from Junk Folder..." : "Some error in fetching...") . "\n"; if (count($spamRows) > 0) { $spamEntryIds = array(); echo "\nTotal messages in Junk folder found are : " . count($spamRows) . "\n"; for ($i = 0; $i < count($spamRows); $i++) { if (greaterDate(date("Y-m-d G:i:s", $spamRows[$i][PR_CREATION_TIME]), $daysBeforeDeleted)) { array_push($spamEntryIds, $spamRows[$i][PR_ENTRYID]); } } if (count($spamEntryIds) > 0) { echo "\nDeleting all " . count($spamEntryIds) . " message(s)...\n"; mapi_folder_deletemessages($spamFolder, $spamEntryIds); echo "" . (mapi_last_hresult() == 0 ? "\nHooray! there is no spam." : "Some error in deleting... There are still some spam messages.") . "\n"; } else { echo "\nNo message found before " . $daysBeforeDeleted . " days in Junk Folder."; } } else { echo "\nNo message found in Junk Folder."; } } else { echo "No default store found... Terminating process.\n"; } ?>
if ($storeEntryId) { $store = mapi_openmsgstore($l_rSession, $storeEntryId); $delStoreProps = mapi_getprops($store, array(PR_IPM_WASTEBASKET_ENTRYID)); $deletedFolder = mapi_msgstore_openentry($store, $delStoreProps[PR_IPM_WASTEBASKET_ENTRYID]); $table = mapi_folder_getcontentstable($deletedFolder); $delRows = mapi_table_queryallrows($table, array(PR_ENTRYID, PR_CREATION_TIME)); echo (mapi_last_hresult() == 0 ? "Fetching messages from Deleted Folder..." : "Some error in fetching...") . "\n"; if (count($delRows) > 0) { $delEntryIds = array(); echo 'Total messages in deleted folder found are : ' . count($delRows) . "\n"; for ($i = 0; $i < count($delRows); $i++) { if (greaterDate(date("Y-m-d G:i:s", $delRows[$i][PR_CREATION_TIME]), $daysBeforeDeleted)) { array_push($delEntryIds, $delRows[$i][PR_ENTRYID]); } } if (count($delEntryIds) > 0) { echo "\nDeleting all " . count($delEntryIds) . " messages...\n"; mapi_folder_deletemessages($deletedFolder, $delEntryIds); echo "" . (mapi_last_hresult() == 0 ? "Successfully deleted all messages" : "Some error in deleting... please try again later") . "\n"; } else { echo "\nNo message found before " . $daysBeforeDeleted . " days in Deleted Folder."; } } else { echo "\nNo message found in Deleted Folder."; } } else { echo "No default store found... Terminating process.\n"; } ?>
function MeetingResponse($requestid, $folderid, $response, &$calendarid) { // Use standard meeting response code to process meeting request $reqentryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid), hex2bin($requestid)); $mapimessage = mapi_msgstore_openentry($this->_defaultstore, $reqentryid); if (!$mapimessage) { debugLog("Unable to open request message for response"); return false; } $meetingrequest = new Meetingrequest($this->_defaultstore, $mapimessage); if (!$meetingrequest->isMeetingRequest()) { debugLog("Attempt to respond to non-meeting request"); return false; } if ($meetingrequest->isLocalOrganiser()) { debugLog("Attempt to response to meeting request that we organized"); return false; } // Process the meeting response. We don't have to send the actual meeting response // e-mail, because the device will send it itself. switch ($response) { case 1: // accept // accept default: $entryid = $meetingrequest->doAccept(false, false, false, false, false, false, true); // last true is the $userAction break; case 2: // tentative $entryid = $meetingrequest->doAccept(true, false, false, false, false, false, true); // last true is the $userAction break; case 3: // decline $meetingrequest->doDecline(false); break; } // F/B will be updated on logoff // We have to return the ID of the new calendar item, so do that here $newitem = mapi_msgstore_openentry($this->_defaultstore, $entryid); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); // delete meeting request from Inbox $folderentryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid)); $folder = mapi_msgstore_openentry($this->_defaultstore, $folderentryid); mapi_folder_deletemessages($folder, array($reqentryid), 0); return true; }
/** * Imports a move of a message. This occurs when a user moves an item to another folder * * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer, * but the Zarafa/Kopano importer does not support this. Therefore we currently implement it via a standard mapi * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync. * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties * of the source message to the new one and then delete the source message. * * @param string $id * @param string $newfolder destination folder * * @access public * @return boolean * @throws StatusException */ public function ImportMessageMove($id, $newfolder) { list(, $sk) = Utils::SplitMessageId($id); if (strtolower($newfolder) == strtolower(bin2hex($this->folderid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } // Get the entryid of the message we're moving $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($sk)); $srcmessage = false; if ($entryid) { //open the source message $srcmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!$entryid || !$srcmessage) { $code = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; // if we move to the trash and the source message is not found, we can also just tell the mobile that we successfully moved to avoid errors (ZP-624) if ($newfolder == ZPush::GetBackend()->GetWasteBasket()) { $code = SYNC_MOVEITEMSSTATUS_SUCCESS; } $errorCase = !$entryid ? "resolve source message id" : "open source message"; throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to %s: 0x%X", $sk, $newfolder, $errorCase, mapi_last_hresult()), $code); } // check if the source message is in the current syncinterval if (!$this->isMessageInSyncInterval($sk)) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Source message is outside the sync interval. Move not performed.", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get correct mapi store for the destination folder $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); if ($dststore === false) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder)); if (!$dstentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid); if (!$dstfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newmessage = mapi_folder_createmessage($dstfolder); if (!$newmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } // Copy message mapi_copyto($srcmessage, array(), array(), $newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid); if (!$srcfolderentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid); if (!$srcfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // Save changes mapi_savechanges($newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // Delete the old message if (!mapi_folder_deletemessages($srcfolder, array($entryid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED); } $sourcekeyprops = mapi_getprops($newmessage, array(PR_SOURCE_KEY)); if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) { $prefix = ""; // prepend the destination short folderid, if it exists $destShortId = ZPush::GetDeviceManager()->GetFolderIdForBackendId($newfolder); if ($destShortId !== $newfolder) { $prefix = $destShortId . ":"; } return $prefix . bin2hex($sourcekeyprops[PR_SOURCE_KEY]); } return false; }
/** * Deletes a card * * @param mixed $addressBookId * @param string $cardUri * @return bool */ public function deleteCard($addressBookId, $cardUri) { $this->logger->info("deleteCard({$cardUri})"); if (READ_ONLY) { $this->logger->warn("Cannot delete card: read-only"); return false; } $folder = mapi_msgstore_openentry($this->bridge->getStore($addressBookId), $addressBookId); $entryId = $this->getContactEntryId($addressBookId, $cardUri); if ($entryId === 0) { $this->logger->warn("Contact not found!"); return false; } // $folder = mapi_msgstore_openentry($this->bridge->getStore($addressBookId), $addressBookId); mapi_folder_deletemessages($folder, array($entryId)); if (mapi_last_hresult() > 0) { return false; } return true; }
/** * Removes all messages that do not match the current ChunkType. * * @param string $folderid * @param string $gabId the id of the gab where the hidden folder should be cleared. If not set (null) the default gab is used. * @param string $gabName the name of the gab where the hidden folder should be cleared. If not set (null) the default gab is used. * * @access protected * @return boolean */ protected function clearAllNotCurrentChunkType($folderid, $gabId = null, $gabName = 'default') { $store = $this->getStore($gabId, $gabName); if (!$store) { return false; } $folder = $this->getFolder($store, $folderid); $table = mapi_folder_getcontentstable($folder); if (!$table) { $this->Terminate(sprintf("Kopano->clearAllNotCurrentChunkType: Error, unable to read contents table on '%s': 0x%08X", $gabName, mapi_last_hresult())); } $restriction = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => $this->mapiprops['chunktype'], VALUE => $this->chunkType)); mapi_table_restrict($table, $restriction); $querycnt = mapi_table_getrowcount($table); if ($querycnt == 0) { $this->Log("Kopano->clearAllNotCurrentChunkType: no invalid items, done!"); } else { $this->Log(sprintf("Kopano->clearAllNotCurrentChunkType: found %d invalid items, deleting", $querycnt)); $entries = mapi_table_queryallrows($table, array(PR_ENTRYID, $this->mapiprops['chunktype'])); $entry_ids = array_reduce($entries, function ($result, $item) { $result[] = $item[PR_ENTRYID]; return $result; }, array()); mapi_folder_deletemessages($folder, array_values($entry_ids)); $this->Log("Kopano->clearAllNotCurrentChunkType: done"); } $this->Log(""); return true; }
/** * Processes a response to a meeting request. * CalendarID is a reference and has to be set if a new calendar item is created * * @param string $requestid id of the object containing the request * @param string $folderid id of the parent folder of $requestid * @param string $response * * @access public * @return string id of the created/updated calendar obj * @throws StatusException */ public function MeetingResponse($requestid, $folderid, $response) { // Use standard meeting response code to process meeting request list($fid, $requestid) = Utils::SplitMessageId($requestid); $reqentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid)); if (!$reqentryid) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } $mapimessage = mapi_msgstore_openentry($this->store, $reqentryid); if (!$mapimessage) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): Error, unable to open request message for response 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } $meetingrequest = new Meetingrequest($this->store, $mapimessage, $this->session); if (!$meetingrequest->isMeetingRequest()) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): Error, attempt to respond to non-meeting request", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } if ($meetingrequest->isLocalOrganiser()) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): Error, attempt to response to meeting request that we organized", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } // Process the meeting response. We don't have to send the actual meeting response // e-mail, because the device will send it itself. switch ($response) { case 1: // accept // accept default: $entryid = $meetingrequest->doAccept(false, false, false, false, false, false, true); // last true is the $userAction break; case 2: // tentative $entryid = $meetingrequest->doAccept(true, false, false, false, false, false, true); // last true is the $userAction break; case 3: // decline $meetingrequest->doDecline(false); break; } // F/B will be updated on logoff // We have to return the ID of the new calendar item, so do that here $calendarid = ""; $calFolderId = ""; if (isset($entryid)) { $newitem = mapi_msgstore_openentry($this->store, $entryid); // new item might be in a delegator's store. ActiveSync does not support accepting them. if (!$newitem) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): Object with entryid '%s' was not found in user's store (0x%X). It might be in a delegator's store.", $requestid, $folderid, $response, bin2hex($entryid), mapi_last_hresult()), SYNC_MEETRESPSTATUS_SERVERERROR, null, LOGLEVEL_WARN); } $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); $calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]); } // on recurring items, the MeetingRequest class responds with a wrong entryid if ($requestid == $calendarid) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): returned calender id is the same as the requestid - re-searching", $requestid, $folderid, $response)); $props = MAPIMapping::GetMeetingRequestProperties(); $props = getPropIdsFromStrings($this->store, $props); $messageprops = mapi_getprops($mapimessage, array($props["goidtag"])); $goid = $messageprops[$props["goidtag"]]; $items = $meetingrequest->findCalendarItems($goid); if (is_array($items)) { $newitem = mapi_msgstore_openentry($this->store, $items[0]); $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY)); $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); $calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): found other calendar entryid", $requestid, $folderid, $response)); } if ($requestid == $calendarid) { throw new StatusException(sprintf("BackendKopano->MeetingResponse('%s','%s', '%s'): Error finding the accepted meeting response in the calendar", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); } } // delete meeting request from Inbox $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid)); $folder = mapi_msgstore_openentry($this->store, $folderentryid); mapi_folder_deletemessages($folder, array($reqentryid), 0); $prefix = ''; // prepend the short folderid of the target calendar: if available and short ids are used if ($calFolderId) { $shortFolderId = ZPush::GetDeviceManager()->GetFolderIdForBackendId($calFolderId); if ($calFolderId != $shortFolderId) { $prefix = $shortFolderId . ':'; } } return $prefix . $calendarid; }