/** * Publishing the FreeBusy information of the default calendar. The * folderentryid argument is used to check if the default calendar * should be updated or not. * * @param $store MAPIobject Store object of the store that needs publishing * @param $folderentryid binary entryid of the folder that needs to be updated. */ function publishFreeBusy($store, $l_rSession, $folderentryid = false) { // Publish updated free/busy information // First get default calendar from the root folder $rootFolder = mapi_msgstore_openentry($store, null); $rootFolderProps = mapi_getprops($rootFolder, array(PR_IPM_APPOINTMENT_ENTRYID)); // If no folderentryid supplied or if the supplied entryid matches the default calendar. if (!$folderentryid || $rootFolderProps[PR_IPM_APPOINTMENT_ENTRYID] == $folderentryid) { // Get the calendar and owner entryID $calendar = mapi_msgstore_openentry($store, $rootFolderProps[PR_IPM_APPOINTMENT_ENTRYID]); $storeProps = mapi_msgstore_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID)); if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID])) { // Lets share! $pub = new FreeBusyPublish($l_rSession, $store, $calendar, $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 } } }
/** * 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; }
/** * Logs off * Free/Busy information is updated for modified calendars * This is done after the synchronization process is completed * * @access public * @return boolean */ public function Logoff() { // update if the calendar which received incoming changes foreach ($this->importedFolders as $folderid => $store) { // open the root of the store $storeprops = mapi_getprops($store, array(PR_USER_ENTRYID)); $root = mapi_msgstore_openentry($store); if (!$root) { continue; } // get the entryid of the main calendar of the store and the calendar to be published $rootprops = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID)); $entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid)); // only publish free busy for the main calendar if (isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID]) && $rootprops[PR_IPM_APPOINTMENT_ENTRYID] == $entryid) { ZLog::Write(LOGLEVEL_INFO, sprintf("ZarafaBackend->Logoff(): Updating freebusy information on folder id '%s'", $folderid)); $calendar = mapi_msgstore_openentry($store, $entryid); $pub = new FreeBusyPublish($this->session, $store, $calendar, $storeprops[PR_USER_ENTRYID]); $pub->publishFB(time() - 7 * 24 * 60 * 60, 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead } } return true; }
function Logoff() { global $cmd; //do not update last sync time on ping and provision if (isset($cmd) && $cmd != '' && $cmd != 'Ping' && $cmd != 'Provision') { $this->setLastSyncTime(); } // publish free busy time after finishing the synchronization process // update if the calendar folder received incoming changes $storeprops = mapi_getprops($this->_defaultstore, array(PR_USER_ENTRYID)); $root = mapi_msgstore_openentry($this->_defaultstore); if (!$root) { return true; } $rootprops = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID)); foreach ($this->_importedFolders as $folderid) { $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid)); if ($rootprops[PR_IPM_APPOINTMENT_ENTRYID] == $entryid) { debugLog("Update freebusy for " . $folderid); $calendar = mapi_msgstore_openentry($this->_defaultstore, $entryid); $pub = new FreeBusyPublish($this->_session, $this->_defaultstore, $calendar, $storeprops[PR_USER_ENTRYID]); $pub->publishFB(time() - 7 * 24 * 60 * 60, 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead } } return true; }
function UpdateFB($addressbook, $session, $rootstore, $entryid) { $abentry = mapi_ab_openentry($addressbook, $entryid); if (!$abentry) { print "Unable to open entry in addressbook\n"; return false; } $abprops = mapi_getprops($abentry, array(PR_ACCOUNT)); $storeid = mapi_msgstore_createentryid($rootstore, $abprops[PR_ACCOUNT]); if (!$storeid) { print "Unable to get store entryid\n"; return false; } $store = mapi_openmsgstore($session, $storeid); if (!$store) { print "Unable to open store\n"; return false; } $root = mapi_msgstore_openentry($store); if (!$root) { print "Unable to open root folder\n"; return false; } $rootprops = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID)); $calendar = mapi_msgstore_openentry($store, $rootprops[PR_IPM_APPOINTMENT_ENTRYID]); $fbupdate = new FreeBusyPublish($session, $store, $calendar, $entryid); $fbupdate->publishFB(0, 0); // publish from one week ago, 6 months ahead return true; }
function MeetingResponse($requestid, $folderid, $response, &$calendarid) { // Use standard meeting response code to process meeting request $entryid = mapi_msgstore_entryidfromsourcekey($this->_defaultstore, hex2bin($folderid), hex2bin($requestid)); $mapimessage = mapi_msgstore_openentry($this->_defaultstore, $entryid); 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, $meetingrequest->isInCalendar()); break; case 2: // tentative $meetingrequest->doAccept(true, false, $meetingrequest->isInCalendar()); break; case 3: // decline $meetingrequest->doDecline(false); break; } // Update F/B $root = mapi_msgstore_openentry($this->_defaultstore); $rootprops = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID)); $calendar = mapi_msgstore_openentry($this->_defaultstore, $rootprops[PR_IPM_APPOINTMENT_ENTRYID]); $storeprops = mapi_getprops($this->_defaultstore, array(PR_USER_ENTRYID)); $pub = new FreeBusyPublish($this->_session, $this->_defaultstore, $calendar, $storeprops[PR_USER_ENTRYID]); $pub->publishFB(time() - 7 * 24 * 60 * 60, 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead // 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]); return true; }