Back to index

d-push  2.0
class.meetingrequest.php
Go to the documentation of this file.
00001 <?php
00002 /*
00003  * Copyright 2005 - 2012  Zarafa B.V.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU Affero General Public License, version 3,
00007  * as published by the Free Software Foundation with the following additional
00008  * term according to sec. 7:
00009  *
00010  * According to sec. 7 of the GNU Affero General Public License, version
00011  * 3, the terms of the AGPL are supplemented with the following terms:
00012  *
00013  * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
00014  * the Program under the AGPL does not imply a trademark license.
00015  * Therefore any rights, title and interest in our trademarks remain
00016  * entirely with us.
00017  *
00018  * However, if you propagate an unmodified version of the Program you are
00019  * allowed to use the term "Zarafa" to indicate that you distribute the
00020  * Program. Furthermore you may use our trademarks where it is necessary
00021  * to indicate the intended purpose of a product or service provided you
00022  * use it in accordance with honest practices in industrial or commercial
00023  * matters.  If you want to propagate modified versions of the Program
00024  * under the name "Zarafa" or "Zarafa Server", you may only do so if you
00025  * have a written permission by Zarafa B.V. (to acquire a permission
00026  * please contact Zarafa at trademark@zarafa.com).
00027  *
00028  * The interactive user interface of the software displays an attribution
00029  * notice containing the term "Zarafa" and/or the logo of Zarafa.
00030  * Interactive user interfaces of unmodified and modified versions must
00031  * display Appropriate Legal Notices according to sec. 5 of the GNU
00032  * Affero General Public License, version 3, when you propagate
00033  * unmodified or modified versions of the Program. In accordance with
00034  * sec. 7 b) of the GNU Affero General Public License, version 3, these
00035  * Appropriate Legal Notices must retain the logo of Zarafa or display
00036  * the words "Initial Development by Zarafa" if the display of the logo
00037  * is not reasonably feasible for technical reasons. The use of the logo
00038  * of Zarafa in Legal Notices is allowed for unmodified and modified
00039  * versions of the software.
00040  *
00041  * This program is distributed in the hope that it will be useful,
00042  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00043  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00044  * GNU Affero General Public License for more details.
00045  *
00046  * You should have received a copy of the GNU Affero General Public License
00047  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00048  *
00049  */
00050 
00051 class Meetingrequest {
00052     /*
00053      * NOTE
00054      *
00055      * This class is designed to modify and update meeting request properties
00056      * and to search for linked appointments in the calendar. It does not
00057      * - set standard properties like subject or location
00058      * - commit property changes through savechanges() (except in accept() and decline())
00059      *
00060      * To set all the other properties, just handle the item as any other appointment
00061      * item. You aren't even required to set those properties before or after using
00062      * this class. If you update properties before REsending a meeting request (ie with
00063      * a time change) you MUST first call updateMeetingRequest() so the internal counters
00064      * can be updated. You can then submit the message any way you like.
00065      *
00066      */
00067 
00068     /*
00069      * How to use
00070      * ----------
00071      *
00072      * Sending a meeting request:
00073      * - Create appointment item as normal, but as 'tentative'
00074      *   (this is the state of the item when the receiving user has received but
00075      *    not accepted the item)
00076      * - Set recipients as normally in e-mails
00077      * - Create Meetingrequest class instance
00078      * - Call setMeetingRequest(), this turns on all the meeting request properties in the
00079      *   calendar item
00080      * - Call sendMeetingRequest(), this sends a copy of the item with some extra properties
00081      *
00082      * Updating a meeting request:
00083      * - Create Meetingrequest class instance
00084      * - Call updateMeetingRequest(), this updates the counters
00085      * - Call sendMeetingRequest()
00086      *
00087      * Clicking on a an e-mail:
00088      * - Create Meetingrequest class instance
00089      * - Check isMeetingRequest(), if true:
00090      *   - Check isLocalOrganiser(), if true then ignore the message
00091      *   - Check isInCalendar(), if not call doAccept(true, false, false). This adds the item in your
00092      *     calendar as tentative without sending a response
00093      *   - Show Accept, Tentative, Decline buttons
00094      *   - When the user presses Accept, Tentative or Decline, call doAccept(false, true, true),
00095      *     doAccept(true, true, true) or doDecline(true) respectively to really accept or decline and
00096      *     send the response. This will remove the request from your inbox.
00097      * - Check isMeetingRequestResponse, if true:
00098      *   - Check isLocalOrganiser(), if not true then ignore the message
00099      *   - Call processMeetingRequestResponse()
00100      *     This will update the trackstatus of all recipients, and set the item to 'busy'
00101      *     when all the recipients have accepted.
00102      * - Check isMeetingCancellation(), if true:
00103      *   - Check isLocalOrganiser(), if true then ignore the message
00104      *   - Check isInCalendar(), if not, then ignore
00105      *     Call processMeetingCancellation()
00106      *   - Show 'Remove item' button to user
00107      *   - When userpresses button, call doCancel(), which removes the item from your
00108      *     calendar and deletes the message
00109      */
00110 
00111     // All properties for a recipient that are interesting
00112     var $recipprops = 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, PR_OBJECT_TYPE, PR_SEARCH_KEY);
00113 
00118     var $errorSetResource;
00119 
00134     function Meetingrequest($store, $message, $session = false, $enableDirectBooking = true)
00135     {
00136         $this->store = $store;
00137         $this->message = $message;
00138         $this->session = $session;
00139         // This variable string saves time information for the MR.
00140         $this->meetingTimeInfo = false;
00141         $this->enableDirectBooking = $enableDirectBooking;
00142 
00143         $properties["goid"] = "PT_BINARY:PSETID_Meeting:0x3";
00144         $properties["goid2"] = "PT_BINARY:PSETID_Meeting:0x23";
00145         $properties["type"] = "PT_STRING8:PSETID_Meeting:0x24";
00146         $properties["meetingrecurring"] = "PT_BOOLEAN:PSETID_Meeting:0x5";
00147         $properties["unknown2"] = "PT_BOOLEAN:PSETID_Meeting:0xa";
00148         $properties["attendee_critical_change"] = "PT_SYSTIME:PSETID_Meeting:0x1";
00149         $properties["owner_critical_change"] = "PT_SYSTIME:PSETID_Meeting:0x1a";
00150         $properties["meetingstatus"] = "PT_LONG:PSETID_Appointment:0x8217";
00151         $properties["responsestatus"] = "PT_LONG:PSETID_Appointment:0x8218";
00152         $properties["unknown6"] = "PT_LONG:PSETID_Meeting:0x4";
00153         $properties["replytime"] = "PT_SYSTIME:PSETID_Appointment:0x8220";
00154         $properties["usetnef"] = "PT_BOOLEAN:PSETID_Common:0x8582";
00155         $properties["recurrence_data"] = "PT_BINARY:PSETID_Appointment:0x8216";
00156         $properties["reminderminutes"] = "PT_LONG:PSETID_Common:0x8501";
00157         $properties["reminderset"] = "PT_BOOLEAN:PSETID_Common:0x8503";
00158         $properties["sendasical"] = "PT_BOOLEAN:PSETID_Appointment:0x8200";
00159         $properties["updatecounter"] = "PT_LONG:PSETID_Appointment:0x8201";                    // AppointmentSequenceNumber
00160         $properties["last_updatecounter"] = "PT_LONG:PSETID_Appointment:0x8203";            // AppointmentLastSequence
00161         $properties["unknown7"] = "PT_LONG:PSETID_Appointment:0x8202";
00162         $properties["busystatus"] = "PT_LONG:PSETID_Appointment:0x8205";
00163         $properties["intendedbusystatus"] = "PT_LONG:PSETID_Appointment:0x8224";
00164         $properties["start"] = "PT_SYSTIME:PSETID_Appointment:0x820d";
00165         $properties["responselocation"] = "PT_STRING8:PSETID_Meeting:0x2";
00166         $properties["location"] = "PT_STRING8:PSETID_Appointment:0x8208";
00167         $properties["requestsent"] = "PT_BOOLEAN:PSETID_Appointment:0x8229";        // PidLidFInvited, MeetingRequestWasSent
00168         $properties["startdate"] = "PT_SYSTIME:PSETID_Appointment:0x820d";
00169         $properties["duedate"] = "PT_SYSTIME:PSETID_Appointment:0x820e";
00170         $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516";
00171         $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517";
00172         $properties["recurring"] = "PT_BOOLEAN:PSETID_Appointment:0x8223";
00173         $properties["clipstart"] = "PT_SYSTIME:PSETID_Appointment:0x8235";
00174         $properties["clipend"] = "PT_SYSTIME:PSETID_Appointment:0x8236";
00175         $properties["start_recur_date"] = "PT_LONG:PSETID_Meeting:0xD";                // StartRecurTime
00176         $properties["start_recur_time"] = "PT_LONG:PSETID_Meeting:0xE";                // StartRecurTime
00177         $properties["end_recur_date"] = "PT_LONG:PSETID_Meeting:0xF";                // EndRecurDate
00178         $properties["end_recur_time"] = "PT_LONG:PSETID_Meeting:0x10";                // EndRecurTime
00179         $properties["is_exception"] = "PT_BOOLEAN:PSETID_Meeting:0xA";                // LID_IS_EXCEPTION
00180         $properties["apptreplyname"] = "PT_STRING8:PSETID_Appointment:0x8230";
00181         // Propose new time properties
00182         $properties["proposed_start_whole"] = "PT_SYSTIME:PSETID_Appointment:0x8250";
00183         $properties["proposed_end_whole"] = "PT_SYSTIME:PSETID_Appointment:0x8251";
00184         $properties["proposed_duration"] = "PT_LONG:PSETID_Appointment:0x8256";
00185         $properties["counter_proposal"] = "PT_BOOLEAN:PSETID_Appointment:0x8257";
00186         $properties["recurring_pattern"] = "PT_STRING8:PSETID_Appointment:0x8232";
00187         $properties["basedate"] = "PT_SYSTIME:PSETID_Appointment:0x8228";
00188         $properties["meetingtype"] = "PT_LONG:PSETID_Meeting:0x26";
00189         $properties["timezone_data"] = "PT_BINARY:PSETID_Appointment:0x8233";
00190         $properties["timezone"] = "PT_STRING8:PSETID_Appointment:0x8234";
00191         $properties["toattendeesstring"] = "PT_STRING8:PSETID_Appointment:0x823B";
00192         $properties["ccattendeesstring"] = "PT_STRING8:PSETID_Appointment:0x823C";
00193         $this->proptags = getPropIdsFromStrings($store, $properties);
00194     }
00195 
00202     function setDirectBooking($directBookingSetting)
00203     {
00204         $this->enableDirectBooking = $directBookingSetting;
00205     }
00206 
00211     function isMeetingRequest()
00212     {
00213         $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
00214 
00215         if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")
00216             return true;
00217     }
00218 
00222     function isMeetingRequestResponse()
00223     {
00224         $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
00225 
00226         if(isset($props[PR_MESSAGE_CLASS]) && strpos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp") === 0)
00227             return true;
00228     }
00229 
00233     function isMeetingCancellation()
00234     {
00235         $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS));
00236 
00237         if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Canceled")
00238             return true;
00239     }
00240 
00241 
00248     function processMeetingRequestResponseAsDelegate()
00249     {
00250         if(!$this->isMeetingRequestResponse())
00251             return;
00252 
00253         $messageprops = mapi_getprops($this->message);
00254 
00255         $goid2 = $messageprops[$this->proptags['goid2']];
00256 
00257         if(!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS]))
00258             return;
00259 
00260         // Find basedate in GlobalID(0x3), this can be a response for an occurrence
00261         $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
00262 
00263         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) {
00264             $delegatorStore = $this->getDelegatorStore($messageprops);
00265             $userStore = $delegatorStore['store'];
00266             $calFolder = $delegatorStore['calFolder'];
00267 
00268             if($calFolder){
00269                 $calendaritems = $this->findCalendarItems($goid2, $calFolder);
00270 
00271                 // $calendaritems now contains the ENTRYID's of all the calendar items to which
00272                 // this meeting request points.
00273 
00274                 // Open the calendar items, and update all the recipients of the calendar item that match
00275                 // the email address of the response.
00276                 if (!empty($calendaritems)) {
00277                      return $this->processResponse($userStore, $calendaritems[0], $basedate, $messageprops);
00278                 }else{
00279                     return false;
00280                 }
00281             }
00282         }
00283     }
00284 
00285 
00292     function processMeetingRequestResponse()
00293     {
00294         if(!$this->isLocalOrganiser())
00295             return;
00296 
00297         if(!$this->isMeetingRequestResponse())
00298             return;
00299 
00300         // Get information we need from the response message
00301         $messageprops = mapi_getprops($this->message, Array(
00302                                                     $this->proptags['goid'],
00303                                                     $this->proptags['goid2'],
00304                                                     PR_OWNER_APPT_ID,
00305                                                     PR_SENT_REPRESENTING_EMAIL_ADDRESS,
00306                                                     PR_SENT_REPRESENTING_NAME,
00307                                                     PR_SENT_REPRESENTING_ADDRTYPE,
00308                                                     PR_SENT_REPRESENTING_ENTRYID,
00309                                                     PR_MESSAGE_DELIVERY_TIME,
00310                                                     PR_MESSAGE_CLASS,
00311                                                     PR_PROCESSED,
00312                                                     $this->proptags['proposed_start_whole'],
00313                                                     $this->proptags['proposed_end_whole'],
00314                                                     $this->proptags['proposed_duration'],
00315                                                     $this->proptags['counter_proposal'],
00316                                                     $this->proptags['attendee_critical_change']));
00317 
00318         $goid2 = $messageprops[$this->proptags['goid2']];
00319 
00320         if(!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS]))
00321             return;
00322 
00323         // Find basedate in GlobalID(0x3), this can be a response for an occurrence
00324         $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
00325 
00326         $calendaritems = $this->findCalendarItems($goid2);
00327 
00328         // $calendaritems now contains the ENTRYID's of all the calendar items to which
00329         // this meeting request points.
00330 
00331         // Open the calendar items, and update all the recipients of the calendar item that match
00332         // the email address of the response.
00333         if (!empty($calendaritems)) {
00334             return $this->processResponse($this->store, $calendaritems[0], $basedate, $messageprops);
00335         }else{
00336             return false;
00337         }
00338     }
00339 
00349     function processResponse($store, $entryid, $basedate, $messageprops)
00350     {
00351         $data = array();
00352         $senderentryid = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
00353         $messageclass = $messageprops[PR_MESSAGE_CLASS];
00354         $deliverytime = $messageprops[PR_MESSAGE_DELIVERY_TIME];
00355 
00356         // Open the calendar item, find the sender in the recipient table and update all the recipients of the calendar item that match
00357         // the email address of the response.
00358         $calendaritem = mapi_msgstore_openentry($store, $entryid);
00359         $calendaritemProps = mapi_getprops($calendaritem, array($this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter']));
00360 
00361         $data["storeid"] = bin2hex($calendaritemProps[PR_STORE_ENTRYID]);
00362         $data["parententryid"] = bin2hex($calendaritemProps[PR_PARENT_ENTRYID]);
00363         $data["entryid"] = bin2hex($calendaritemProps[PR_ENTRYID]);
00364         $data["basedate"] = $basedate;
00365         $data["updatecounter"] = isset($calendaritemProps[$this->proptags['updatecounter']]) ? $calendaritemProps[$this->proptags['updatecounter']] : 0;
00366 
00370         $data["meeting_updated"] = $this->isMeetingUpdated();
00371 
00372         if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
00373             // meeting is already processed
00374             return $data;
00375         } else {
00376             mapi_setprops($this->message, Array(PR_PROCESSED => true));
00377             mapi_savechanges($this->message);
00378         }
00379 
00380         // if meeting is updated in organizer's calendar then we don't need to process
00381         // old response
00382         if($data['meeting_updated'] === true) {
00383             return $data;
00384         }
00385 
00386         // If basedate is found, then create/modify exception msg and do processing
00387         if ($basedate && $calendaritemProps[$this->proptags['recurring']]) {
00388             $recurr = new Recurrence($store, $calendaritem);
00389 
00390             // Copy properties from meeting request
00391             $exception_props = mapi_getprops($this->message, array(PR_OWNER_APPT_ID,
00392                                                 $this->proptags['proposed_start_whole'],
00393                                                 $this->proptags['proposed_end_whole'],
00394                                                 $this->proptags['proposed_duration'],
00395                                                 $this->proptags['counter_proposal']
00396                                             ));
00397 
00398             // Create/modify exception
00399             if($recurr->isException($basedate)) {
00400                 $recurr->modifyException($exception_props, $basedate);
00401             } else {
00402                 // When we are creating an exception we need copy recipients from main recurring item
00403                 $recipTable =  mapi_message_getrecipienttable($calendaritem);
00404                 $recips = mapi_table_queryallrows($recipTable, $this->recipprops);
00405 
00406                 // Retrieve actual start/due dates from calendar item.
00407                 $exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
00408                 $exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
00409 
00410                 $recurr->createException($exception_props, $basedate, false, $recips);
00411             }
00412 
00413             mapi_message_savechanges($calendaritem);
00414 
00415             $attach = $recurr->getExceptionAttachment($basedate);
00416             if ($attach) {
00417                 $recurringItem = $calendaritem;
00418                 $calendaritem = mapi_attach_openobj($attach, MAPI_MODIFY);
00419             } else {
00420                 return false;
00421             }
00422         }
00423 
00424         // Get the recipients of the calendar item
00425         $reciptable = mapi_message_getrecipienttable($calendaritem);
00426         $recipients = mapi_table_queryallrows($reciptable, $this->recipprops);
00427 
00428         // FIXME we should look at the updatecounter property and compare it
00429         // to the counter in the recipient to see if this update is actually
00430         // newer than the status in the calendar item
00431         $found = false;
00432 
00433         $totalrecips = 0;
00434         $acceptedrecips = 0;
00435         foreach($recipients as $recipient) {
00436             $totalrecips++;
00437             if(isset($recipient[PR_ENTRYID]) && $this->compareABEntryIDs($recipient[PR_ENTRYID],$senderentryid)) {
00438                 $found = true;
00439 
00444                 if (isset($recipient[PR_RECIPIENT_TRACKSTATUS_TIME]) && ($messageprops[$this->proptags['attendee_critical_change']] < $recipient[PR_RECIPIENT_TRACKSTATUS_TIME])) {
00445                     continue;
00446                 }
00447 
00448                 // The email address matches, update the row
00449                 $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
00450                 $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $messageprops[$this->proptags['attendee_critical_change']];
00451 
00452                 // If this is a counter proposal, set the proposal properties in the recipient row
00453                 if(isset($messageprops[$this->proptags['counter_proposal']]) && $messageprops[$this->proptags['counter_proposal']]){
00454                     $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
00455                     $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
00456                     $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
00457                 }
00458 
00459                 mapi_message_modifyrecipients($calendaritem, MODRECIP_MODIFY, Array($recipient));
00460             }
00461             if(isset($recipient[PR_RECIPIENT_TRACKSTATUS]) && $recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted)
00462                 $acceptedrecips++;
00463         }
00464 
00465         // If the recipient was not found in the original calendar item,
00466         // then add the recpient as a new optional recipient
00467         if(!$found) {
00468             $recipient = Array();
00469             $recipient[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
00470             $recipient[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
00471             $recipient[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
00472             $recipient[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
00473             $recipient[PR_RECIPIENT_TYPE] = MAPI_CC;
00474             $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
00475             $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $deliverytime;
00476 
00477             // If this is a counter proposal, set the proposal properties in the recipient row
00478             if(isset($messageprops[$this->proptags['counter_proposal']])){
00479                 $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
00480                 $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
00481                 $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
00482             }
00483 
00484             mapi_message_modifyrecipients($calendaritem, MODRECIP_ADD, Array($recipient));
00485             $totalrecips++;
00486             if($recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted)
00487                 $acceptedrecips++;
00488         }
00489 
00490 //TODO: Upate counter proposal number property on message
00491 /*
00492 If it is the first time this attendee has proposed a new date/time, increment the value of the PidLidAppointmentProposalNumber property on the organizer�s meeting object, by 0x00000001. If this property did not previously exist on the organizer�s meeting object, it MUST be set with a value of 0x00000001.
00493 */
00494         // If this is a counter proposal, set the counter proposal indicator boolean
00495         if(isset($messageprops[$this->proptags['counter_proposal']])){
00496             $props = Array();
00497             if($messageprops[$this->proptags['counter_proposal']]){
00498                 $props[$this->proptags['counter_proposal']] = true;
00499             }else{
00500                 $props[$this->proptags['counter_proposal']] = false;
00501             }
00502 
00503             mapi_message_setprops($calendaritem, $props);
00504         }
00505 
00506         mapi_message_savechanges($calendaritem);
00507         if (isset($attach)) {
00508             mapi_message_savechanges($attach);
00509             mapi_message_savechanges($recurringItem);
00510         }
00511 
00512         return $data;
00513     }
00514 
00515 
00520     function processMeetingCancellation()
00521     {
00522         if($this->isLocalOrganiser())
00523             return;
00524 
00525         if(!$this->isMeetingCancellation())
00526             return;
00527 
00528         if(!$this->isInCalendar())
00529             return;
00530 
00531         $listProperties = $this->proptags;
00532         $listProperties['subject'] = PR_SUBJECT;
00533         $listProperties['sent_representing_name'] = PR_SENT_REPRESENTING_NAME;
00534         $listProperties['sent_representing_address_type'] = PR_SENT_REPRESENTING_ADDRTYPE;
00535         $listProperties['sent_representing_email_address'] = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
00536         $listProperties['sent_representing_entryid'] = PR_SENT_REPRESENTING_ENTRYID;
00537         $listProperties['sent_representing_search_key'] = PR_SENT_REPRESENTING_SEARCH_KEY;
00538         $listProperties['rcvd_representing_name'] = PR_RCVD_REPRESENTING_NAME;
00539         $messageprops = mapi_getprops($this->message, $listProperties);
00540         $store = $this->store;
00541 
00542         $goid = $messageprops[$this->proptags['goid']];    //GlobalID (0x3)
00543         if(!isset($goid))
00544             return;
00545 
00546         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])){
00547             $delegatorStore = $this->getDelegatorStore($messageprops);
00548             $store = $delegatorStore['store'];
00549             $calFolder = $delegatorStore['calFolder'];
00550         } else {
00551             $calFolder = $this->openDefaultCalendar();
00552         }
00553 
00554         // First, find the items in the calendar by GOID
00555         $calendaritems = $this->findCalendarItems($goid, $calFolder);
00556         $basedate = $this->getBasedateFromGlobalID($goid);
00557 
00558         if ($basedate) {
00559             // Calendaritems with GlobalID were not found, so find main recurring item using CleanGlobalID(0x23)
00560             if (empty($calendaritems)) {
00561                 // This meeting req is of an occurrance
00562                 $goid2 = $messageprops[$this->proptags['goid2']];
00563 
00564                 // First, find the items in the calendar by GOID
00565                 $calendaritems = $this->findCalendarItems($goid2);
00566                 foreach($calendaritems as $entryid) {
00567                     // Open each calendar item and set the properties of the cancellation object
00568                     $calendaritem = mapi_msgstore_openentry($store, $entryid);
00569 
00570                     if ($calendaritem){
00571                         $calendaritemProps = mapi_getprops($calendaritem, array($this->proptags['recurring']));
00572                         if ($calendaritemProps[$this->proptags['recurring']]){
00573                             $recurr = new Recurrence($store, $calendaritem);
00574 
00575                             // Set message class
00576                             $messageprops[PR_MESSAGE_CLASS] = 'IPM.Appointment';
00577 
00578                             if($recurr->isException($basedate))
00579                                 $recurr->modifyException($messageprops, $basedate);
00580                             else
00581                                 $recurr->createException($messageprops, $basedate);
00582                         }
00583                         mapi_savechanges($calendaritem);
00584                     }
00585                 }
00586             }
00587         }
00588 
00589         if (!isset($calendaritem)) {
00590             foreach($calendaritems as $entryid) {
00591                 // Open each calendar item and set the properties of the cancellation object
00592                 $calendaritem = mapi_msgstore_openentry($store, $entryid);
00593                 mapi_message_setprops($calendaritem, $messageprops);
00594                 mapi_savechanges($calendaritem);
00595             }
00596         }
00597     }
00598 
00602     function isInCalendar() {
00603         $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME));
00604         $goid = $messageprops[$this->proptags['goid']];
00605         $goid2 = $messageprops[$this->proptags['goid2']];
00606 
00607         $basedate = $this->getBasedateFromGlobalID($goid);
00608 
00609         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])){
00610             $delegatorStore = $this->getDelegatorStore($messageprops);
00611             $calFolder = $delegatorStore['calFolder'];
00612         } else {
00613             $calFolder = $this->openDefaultCalendar();
00614         }
00620         if ($basedate) {
00621             // First try with GlobalID(0x3) (case 1)
00622             $entryid = $this->findCalendarItems($goid, $calFolder);
00623             // If not found then try with CleanGlobalID(0x23) (case 2)
00624             if (!is_array($entryid))
00625                 $entryid = $this->findCalendarItems($goid2, $calFolder);
00626         } else {
00627             $entryid = $this->findCalendarItems($goid2, $calFolder);
00628         }
00629 
00630         return is_array($entryid);
00631     }
00632 
00649     function doAccept($tentative, $sendresponse, $move, $newProposedStartTime=false, $newProposedEndTime=false, $body=false, $userAction = false, $store=false, $basedate = false)
00650     {
00651         if($this->isLocalOrganiser())
00652             return false;
00653 
00654         // Remove any previous calendar items with this goid and appt id
00655         $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID, PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_OWNER_APPT_ID, $this->proptags['updatecounter'], PR_PROCESSED, $this->proptags['recurring'], $this->proptags['intendedbusystatus'], PR_RCVD_REPRESENTING_NAME));
00656 
00664         if($messageprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request" && $userAction == false) {
00665             if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
00666                 // if meeting request is already processed then don't do anything
00667                 return false;
00668             } else {
00669                 mapi_setprops($this->message, Array(PR_PROCESSED => true));
00670                 mapi_message_savechanges($this->message);
00671             }
00672         }
00673 
00674         // If this meeting request is received by a delegate then open delegator's store.
00675         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) {
00676             $delegatorStore = $this->getDelegatorStore($messageprops);
00677 
00678             $store = $delegatorStore['store'];
00679             $calFolder = $delegatorStore['calFolder'];
00680         } else {
00681             $calFolder = $this->openDefaultCalendar();
00682             $store = $this->store;
00683         }
00684 
00685         return $this->accept($tentative, $sendresponse, $move, $newProposedStartTime, $newProposedEndTime, $body, $userAction, $store, $calFolder, $basedate);
00686     }
00687 
00688     function accept($tentative, $sendresponse, $move, $newProposedStartTime=false, $newProposedEndTime=false, $body=false, $userAction = false, $store, $calFolder, $basedate = false)
00689     {
00690         $messageprops = mapi_getprops($this->message);
00691         $isDelegate = false;
00692 
00693         if (isset($messageprops[PR_DELEGATED_BY_RULE]))
00694             $isDelegate = true;
00695 
00696         $goid = $messageprops[$this->proptags['goid2']];
00697 
00698         // Retrieve basedate from globalID, if it is not recieved as argument
00699         if (!$basedate)
00700             $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
00701 
00702         if ($sendresponse)
00703             $this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $newProposedStartTime, $newProposedEndTime, $body, $store, $basedate, $calFolder);
00704 
00705         $entryids = $this->findCalendarItems($goid, $calFolder);
00706 
00707         if(is_array($entryids)) {
00708             // Only check the first, there should only be one anyway...
00709             $previtem = mapi_msgstore_openentry($store, $entryids[0]);
00710             $prevcounterprops = mapi_getprops($previtem, array($this->proptags['updatecounter']));
00711 
00712             // Check if the existing item has an updatecounter that is lower than the request we are processing. If not, then we ignore this call, since the
00713             // meeting request is out of date.
00714             /*
00715                 if(message_counter < appointment_counter) do_nothing
00716                 if(message_counter == appointment_counter) do_something_if_the_user_tells_us (userAction == true)
00717                 if(message_counter > appointment_counter) do_something_even_automatically
00718             */
00719             if(isset($prevcounterprops[$this->proptags['updatecounter']]) && $messageprops[$this->proptags['updatecounter']] < $prevcounterprops[$this->proptags['updatecounter']]) {
00720                 return false;
00721             } else if(isset($prevcounterprops[$this->proptags['updatecounter']]) && $messageprops[$this->proptags['updatecounter']] == $prevcounterprops[$this->proptags['updatecounter']]) {
00722                 if($userAction == false && !$basedate) {
00723                     return false;
00724                 }
00725             }
00726         }
00727 
00728         // set counter proposal properties in calendar item when proposing new time
00729         // @FIXME this can be moved before call to createResponse function so that function doesn't need to recalculate duration
00730         $proposeNewTimeProps = array();
00731         if($newProposedStartTime && $newProposedEndTime) {
00732             $proposeNewTimeProps[$this->proptags['proposed_start_whole']] = $newProposedStartTime;
00733             $proposeNewTimeProps[$this->proptags['proposed_end_whole']] = $newProposedEndTime;
00734             $proposeNewTimeProps[$this->proptags['proposed_duration']] = round($newProposedEndTime - $newProposedStartTime) / 60;
00735             $proposeNewTimeProps[$this->proptags['counter_proposal']] = true;
00736         }
00737 
00748         if ($messageprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request") {
00749             // While processing the item mark it as read.
00750             mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT);
00751 
00752             // This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
00753             if ($messageprops[$this->proptags['recurring']] == true) {
00754                 $calendarItem = false;
00755 
00756                 // Find main recurring item based on GlobalID (0x3)
00757                 $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
00758                 if (is_array($items)) {
00759                     foreach($items as $key => $entryid)
00760                         $calendarItem = mapi_msgstore_openentry($store, $entryid);
00761                 }
00762 
00763                 // Recurring item not found, so create new meeting in Calendar
00764                 if (!$calendarItem)
00765                     $calendarItem = mapi_folder_createmessage($calFolder);
00766 
00767                 // Copy properties
00768                 $props = mapi_getprops($this->message);
00769                 $props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
00770                 $props[$this->proptags['meetingstatus']] = olMeetingReceived;
00771                 // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
00772                 $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
00773 
00774                 if (isset($props[$this->proptags['intendedbusystatus']])) {
00775                     if($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
00776                         $props[$this->proptags['busystatus']] = $tentative;
00777                     } else {
00778                         $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
00779                     }
00780                     // we already have intendedbusystatus value in $props so no need to copy it
00781                 } else {
00782                     $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
00783                 }
00784 
00785                 if($userAction) {
00786                     // if user has responded then set replytime
00787                     $props[$this->proptags['replytime']] = time();
00788                 }
00789 
00790                 mapi_setprops($calendarItem, $props);
00791 
00792                 // Copy attachments too
00793                 $this->replaceAttachments($this->message, $calendarItem);
00794                 // Copy recipients too
00795                 $this->replaceRecipients($this->message, $calendarItem, $isDelegate);
00796 
00797                 // Find all occurrences based on CleanGlobalID (0x23)
00798                 $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
00799                 if (is_array($items)) {
00800                     // Save all existing occurrence as exceptions
00801                     foreach($items as $entryid) {
00802                         // Open occurrence
00803                         $occurrenceItem = mapi_msgstore_openentry($store, $entryid);
00804 
00805                         // Save occurrence into main recurring item as exception
00806                         if ($occurrenceItem) {
00807                             $occurrenceItemProps = mapi_getprops($occurrenceItem, array($this->proptags['goid'], $this->proptags['recurring']));
00808 
00809                             // Find basedate of occurrence item
00810                             $basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]);
00811                             if ($basedate && $occurrenceItemProps[$this->proptags['recurring']] != true)
00812                                 $this->acceptException($calendarItem, $occurrenceItem, $basedate, true, $tentative, $userAction, $store, $isDelegate);
00813                         }
00814                     }
00815                 }
00816                 mapi_savechanges($calendarItem);
00817                 if ($move) {
00818                     $wastebasket = $this->openDefaultWastebasket();
00819                     mapi_folder_copymessages($calFolder, Array($props[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
00820                 }
00821                 $entryid = $props[PR_ENTRYID];
00822             } else {
00828                 $calendarItem = false;
00829 
00830                 // We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence.
00831                 if ($basedate) {
00832                     // Find main recurring item from CleanGlobalID of this meeting request
00833                     $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
00834                     if (is_array($items)) {
00835                         foreach($items as $key => $entryid) {
00836                             $calendarItem = mapi_msgstore_openentry($store, $entryid);
00837                         }
00838                     }
00839 
00840                     // Main recurring item is found, so now update exception
00841                     if ($calendarItem) {
00842                         $this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
00843                         $calendarItemProps = mapi_getprops($calendarItem, array(PR_ENTRYID));
00844                         $entryid = $calendarItemProps[PR_ENTRYID];
00845                     }
00846                 }
00847 
00848                 if (!$calendarItem) {
00849                     $items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
00850 
00851                     if (is_array($items))
00852                         mapi_folder_deletemessages($calFolder, $items);
00853 
00854                     if ($move) {
00855                         // All we have to do is open the default calendar,
00856                         // set the mesage class correctly to be an appointment item
00857                         // and move it to the calendar folder
00858                         $sourcefolder = $this->openParentFolder();
00859 
00860                         /* create a new calendar message, and copy the message to there,
00861                            since we want to delete (move to wastebasket) the original message */
00862                         $old_entryid = mapi_getprops($this->message, Array(PR_ENTRYID));
00863                         $calmsg = mapi_folder_createmessage($calFolder);
00864                         mapi_copyto($this->message, array(), array(), $calmsg); /* includes attachments and recipients */
00865                         /* release old message */
00866                         $message = null;
00867 
00868                         $calItemProps = Array();
00869                         $calItemProps[PR_MESSAGE_CLASS] = "IPM.Appointment";
00870 
00871                         if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
00872                             if($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
00873                                 $calItemProps[$this->proptags['busystatus']] = $tentative;
00874                             } else {
00875                                 $calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
00876                             }
00877                             $calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
00878                         } else {
00879                             $calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
00880                         }
00881 
00882                         // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
00883                         $calItemProps[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
00884                         if($userAction) {
00885                             // if user has responded then set replytime
00886                             $calItemProps[$this->proptags['replytime']] = time();
00887                         }
00888 
00889                         mapi_setprops($calmsg, $proposeNewTimeProps + $calItemProps);
00890 
00891                         // get properties which stores owner information in meeting request mails
00892                         $props = mapi_getprops($calmsg, array(PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE));
00893 
00894                         // add owner to recipient table
00895                         $recips = array();
00896                         $this->addOrganizer($props, $recips);
00897 
00898                         if($isDelegate) {
00905                             $this->setRecipsFromString($recips, $messageprops[$this->proptags['toattendeesstring']], MAPI_TO);
00906                             $this->setRecipsFromString($recips, $messageprops[$this->proptags['ccattendeesstring']], MAPI_CC);
00907                             mapi_message_modifyrecipients($calmsg, 0, $recips);
00908                         } else {
00909                             mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips);
00910                         }
00911 
00912                         mapi_message_savechanges($calmsg);
00913 
00914                         // Move the message to the wastebasket
00915                         $wastebasket = $this->openDefaultWastebasket();
00916                         mapi_folder_copymessages($sourcefolder, array($old_entryid[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
00917 
00918                         $messageprops = mapi_getprops($calmsg, array(PR_ENTRYID));
00919                         $entryid = $messageprops[PR_ENTRYID];
00920                     } else {
00921                         // Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment
00922                         $new = mapi_folder_createmessage($calFolder);
00923                         $props = mapi_getprops($this->message);
00924 
00925                         $props[PR_MESSAGE_CLASS] = "IPM.Appointment";
00926                         // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
00927                         $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
00928 
00929                         if (isset($props[$this->proptags['intendedbusystatus']])) {
00930                             if($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
00931                                 $props[$this->proptags['busystatus']] = $tentative;
00932                             } else {
00933                                 $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
00934                             }
00935                             // we already have intendedbusystatus value in $props so no need to copy it
00936                         } else {
00937                             $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
00938                         }
00939 
00940                         if($userAction) {
00941                             // if user has responded then set replytime
00942                             $props[$this->proptags['replytime']] = time();
00943                         }
00944 
00945                         mapi_setprops($new, $proposeNewTimeProps + $props);
00946 
00947                         $reciptable = mapi_message_getrecipienttable($this->message);
00948 
00949                         $recips = array();
00950                         if(!$isDelegate)
00951                             $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
00952 
00953                         $this->addOrganizer($props, $recips);
00954 
00955                         if($isDelegate) {
00962                             $this->setRecipsFromString($recips, $messageprops[$this->proptags['toattendeesstring']], MAPI_TO);
00963                             $this->setRecipsFromString($recips, $messageprops[$this->proptags['ccattendeesstring']], MAPI_CC);
00964                             mapi_message_modifyrecipients($new, 0, $recips);
00965                         } else {
00966                             mapi_message_modifyrecipients($new, MODRECIP_ADD, $recips);
00967                         }
00968                         mapi_message_savechanges($new);
00969 
00970                         $props = mapi_getprops($new, array(PR_ENTRYID));
00971                         $entryid = $props[PR_ENTRYID];
00972                     }
00973                 }
00974             }
00975         } else {
00976             // Here only properties are set on calendaritem, because user is responding from calendar.
00977             $props = array();
00978             $props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted;
00979 
00980             if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
00981                 if($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
00982                     $props[$this->proptags['busystatus']] = $tentative;
00983                 } else {
00984                     $props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
00985                 }
00986                 $props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
00987             } else {
00988                 $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
00989             }
00990 
00991             $props[$this->proptags['meetingstatus']] = olMeetingReceived;
00992             $props[$this->proptags['replytime']] = time();
00993 
00994             if ($basedate) {
00995                 $recurr = new Recurrence($store, $this->message);
00996 
00997                 // Copy recipients list
00998                 $reciptable = mapi_message_getrecipienttable($this->message);
00999                 $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
01000 
01001                 if($recurr->isException($basedate)) {
01002                     $recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips);
01003                 } else {
01004                     $props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
01005                     $props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
01006 
01007                     $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
01008                     $props[PR_SENT_REPRESENTING_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
01009                     $props[PR_SENT_REPRESENTING_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
01010                     $props[PR_SENT_REPRESENTING_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
01011 
01012                     $recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips);
01013                 }
01014             } else {
01015                 mapi_setprops($this->message, $proposeNewTimeProps + $props);
01016             }
01017             mapi_savechanges($this->message);
01018 
01019             $entryid = $messageprops[PR_ENTRYID];
01020         }
01021 
01022         return $entryid;
01023     }
01024 
01037     function doDecline($sendresponse, $store=false, $basedate = false, $body = false)
01038     {
01039         $result = true;
01040         $calendaritem = false;
01041         if($this->isLocalOrganiser())
01042             return;
01043 
01044         // Remove any previous calendar items with this goid and appt id
01045         $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME));
01046 
01047         // If this meeting request is received by a delegate then open delegator's store.
01048         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) {
01049             $delegatorStore = $this->getDelegatorStore($messageprops);
01050 
01051             $store = $delegatorStore['store'];
01052             $calFolder = $delegatorStore['calFolder'];
01053         } else {
01054             $calFolder = $this->openDefaultCalendar();
01055             $store = $this->store;
01056         }
01057 
01058         $goid = $messageprops[$this->proptags['goid']];
01059 
01060         // First, find the items in the calendar by GlobalObjid (0x3)
01061         $entryids = $this->findCalendarItems($goid, $calFolder);
01062 
01063         if (!$basedate)
01064             $basedate = $this->getBasedateFromGlobalID($goid);
01065 
01066         if($sendresponse)
01067             $this->createResponse(olResponseDeclined, false, false, $body, $store, $basedate, $calFolder);
01068 
01069         if ($basedate) {
01070             // use CleanGlobalObjid (0x23)
01071             $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
01072 
01073             foreach($calendaritems as $entryid) {
01074                 // Open each calendar item and set the properties of the cancellation object
01075                 $calendaritem = mapi_msgstore_openentry($store, $entryid);
01076 
01077                 // Recurring item is found, now delete exception
01078                 if ($calendaritem)
01079                     $this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
01080             }
01081 
01082             if ($this->isMeetingRequest())
01083                 $calendaritem = false;
01084             else
01085                 $result = false;
01086         }
01087 
01088         if (!$calendaritem) {
01089             $calendar = $this->openDefaultCalendar();
01090 
01091             if(!empty($entryids)) {
01092                 mapi_folder_deletemessages($calendar, $entryids);
01093             }
01094 
01095             // All we have to do to decline, is to move the item to the waste basket
01096             $wastebasket = $this->openDefaultWastebasket();
01097             $sourcefolder = $this->openParentFolder();
01098 
01099             $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID));
01100 
01101             // Release the message
01102             $this->message = null;
01103 
01104             // Move the message to the waste basket
01105             mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
01106         }
01107         return $result;
01108     }
01109 
01115     function doRemoveFromCalendar($basedate)
01116     {
01117         if($this->isLocalOrganiser())
01118             return false;
01119 
01120         $store = $this->store;
01121         $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_NAME, PR_MESSAGE_CLASS));
01122         $goid = $messageprops[$this->proptags['goid']];
01123 
01124         if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) {
01125             $delegatorStore = $this->getDelegatorStore($messageprops);
01126             $store = $delegatorStore['store'];
01127             $calFolder = $delegatorStore['calFolder'];
01128         } else {
01129             $calFolder = $this->openDefaultCalendar();
01130         }
01131 
01132         $wastebasket = $this->openDefaultWastebasket();
01133         $sourcefolder = $this->openParentFolder();
01134 
01135         // Check if the message is a meeting request in the inbox or a calendaritem by checking the message class
01136         if (strpos($messageprops[PR_MESSAGE_CLASS], 'IPM.Schedule.Meeting') === 0) {
01141             $basedate = false;
01142             if ($goid) {
01143                 // Retrieve GlobalID and find basedate in it.
01144                 $basedate = $this->getBasedateFromGlobalID($goid);
01145 
01146                 // Basedate found, Now find item.
01147                 if ($basedate) {
01148                     $guid = $this->setBasedateInGlobalID($goid);
01149 
01150                     // First, find the items in the calendar by GOID
01151                     $calendaritems = $this->findCalendarItems($guid, $calFolder);
01152                     if(is_array($calendaritems)) {
01153                         foreach($calendaritems as $entryid) {
01154                             // Open each calendar item and set the properties of the cancellation object
01155                             $calendaritem = mapi_msgstore_openentry($store, $entryid);
01156 
01157                             if ($calendaritem){
01158                                 $this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
01159                             }
01160                         }
01161                     }
01162                 }
01163             }
01164 
01165             // It is normal/recurring meeting item.
01166             if (!$basedate) {
01167                 if (!isset($calFolder)) $calFolder = $this->openDefaultCalendar();
01168 
01169                 $entryids = $this->findCalendarItems($goid, $calFolder);
01170 
01171                 if(is_array($entryids)){
01172                     // Move the calendaritem to the waste basket
01173                     mapi_folder_copymessages($sourcefolder, $entryids, $wastebasket, MESSAGE_MOVE);
01174                 }
01175             }
01176 
01177             // Release the message
01178             $this->message = null;
01179 
01180             // Move the message to the waste basket
01181             mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
01182 
01183         } else {
01184             // Here only properties are set on calendaritem, because user is responding from calendar.
01185             if ($basedate) { //remove the occurence
01186                 $this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
01187             } else { //remove normal/recurring meeting item.
01188                 // Move the message to the waste basket
01189                 mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
01190             }
01191         }
01192     }
01193 
01199     function doCancel()
01200     {
01201         if($this->isLocalOrganiser())
01202             return;
01203         if(!$this->isMeetingCancellation())
01204             return;
01205 
01206         // Remove any previous calendar items with this goid and appt id
01207         $messageprops = mapi_getprops($this->message, Array($this->proptags['goid']));
01208         $goid = $messageprops[$this->proptags['goid']];
01209 
01210         $entryids = $this->findCalendarItems($goid);
01211         $calendar = $this->openDefaultCalendar();
01212 
01213         mapi_folder_deletemessages($calendar, $entryids);
01214 
01215         // All we have to do to decline, is to move the item to the waste basket
01216 
01217         $wastebasket = $this->openDefaultWastebasket();
01218         $sourcefolder = $this->openParentFolder();
01219 
01220         $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID));
01221 
01222         // Release the message
01223         $this->message = null;
01224 
01225         // Move the message to the waste basket
01226         mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
01227     }
01228 
01229 
01236     function setMeetingRequest($basedate = false)
01237     {
01238         $props = mapi_getprops($this->message, Array($this->proptags['updatecounter']));
01239 
01240         // Create a new global id for this item
01241         $goid = pack("H*", "040000008200E00074C5B7101A82E00800000000");
01242         for ($i=0; $i<36; $i++)
01243             $goid .= chr(rand(0, 255));
01244 
01245         // Create a new appointment id for this item
01246         $apptid = rand();
01247 
01248         $props[PR_OWNER_APPT_ID] = $apptid;
01249         $props[PR_ICON_INDEX] = 1026;
01250         $props[$this->proptags['goid']] = $goid;
01251         $props[$this->proptags['goid2']] = $goid;
01252 
01253         if (!isset($props[$this->proptags['updatecounter']])) {
01254             $props[$this->proptags['updatecounter']] = 0;            // OL also starts sequence no with zero.
01255             $props[$this->proptags['last_updatecounter']] = 0;
01256         }
01257 
01258         mapi_setprops($this->message, $props);
01259     }
01260 
01269     function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $deletedRecips = false)
01270     {
01271         $this->includesResources = false;
01272         $this->nonAcceptingResources = Array();
01273 
01274         // Get the properties of the message
01275         $messageprops = mapi_getprops($this->message, Array($this->proptags['recurring']));
01276 
01277         /*****************************************************************************************
01278          * Submit message to non-resource recipients
01279          */
01280         // Set BusyStatus to olTentative (1)
01281         // Set MeetingStatus to olMeetingReceived
01282         // Set ResponseStatus to olResponseNotResponded
01283 
01288         if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) {
01289             // Book resource
01290             $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
01291 
01292             if (!$this->errorSetResource) {
01293                 $recurr = new Recurrence($this->openDefaultStore(), $this->message);
01294 
01295                 // First send meetingrequest for recurring item
01296                 $this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $deletedRecips);
01297 
01298                 // Then send all meeting request for all exceptions
01299                 $exceptions = $recurr->getAllExceptions();
01300                 if ($exceptions) {
01301                     foreach($exceptions as $exceptionBasedate) {
01302                         $attach = $recurr->getExceptionAttachment($exceptionBasedate);
01303 
01304                         if ($attach) {
01305                             $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
01306                             $this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $deletedRecips);
01307                             mapi_savechanges($attach);
01308                         }
01309                     }
01310                 }
01311             }
01312         } else {
01313             // Basedate found, an exception is to be send
01314             if ($basedate) {
01315                 $recurr = new Recurrence($this->openDefaultStore(), $this->message);
01316 
01317                 if ($cancel) {
01318                     //@TODO: remove occurrence from Resource's Calendar if resource was booked for whole series
01319                     $this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false);
01320                 } else {
01321                     $attach = $recurr->getExceptionAttachment($basedate);
01322 
01323                     if ($attach) {
01324                         $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
01325 
01326                         // Book resource for this occurrence
01327                         $resourceRecipData = $this->bookResources($occurrenceItem, $cancel, $prefix, $basedate);
01328 
01329                         if (!$this->errorSetResource) {
01330                             // Save all previous changes
01331                             mapi_savechanges($this->message);
01332 
01333                             $this->submitMeetingRequest($occurrenceItem, $cancel, $prefix, $basedate, $recurr, true, $deletedRecips);
01334                             mapi_savechanges($occurrenceItem);
01335                             mapi_savechanges($attach);
01336                         }
01337                     }
01338                 }
01339             } else {
01340                 // This is normal meeting
01341                 $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
01342 
01343                 if (!$this->errorSetResource) {
01344                     $this->submitMeetingRequest($this->message, $cancel, $prefix, false, false, false, $deletedRecips);
01345                 }
01346             }
01347         }
01348 
01349         if(isset($this->errorSetResource) && $this->errorSetResource){
01350             return Array(
01351                 'error' => $this->errorSetResource,
01352                 'displayname' => $this->recipientDisplayname
01353             );
01354         }else{
01355             return true;
01356         }
01357     }
01358 
01359 
01360     function getFreeBusyInfo($entryID,$start,$end)
01361     {
01362         $result = array();
01363         $fbsupport = mapi_freebusysupport_open($this->session);
01364 
01365         if(mapi_last_hresult() != NOERROR) {
01366             if(function_exists("dump")) {
01367                 dump("Error in opening freebusysupport object.");
01368             }
01369             return $result;
01370         }
01371 
01372         $fbDataArray = mapi_freebusysupport_loaddata($fbsupport, array($entryID));
01373 
01374         if($fbDataArray[0] != NULL){
01375             foreach($fbDataArray as $fbDataUser){
01376                 $rangeuser1 = mapi_freebusydata_getpublishrange($fbDataUser);
01377                 if($rangeuser1 == NULL){
01378                     return $result;
01379                 }
01380 
01381                 $enumblock = mapi_freebusydata_enumblocks($fbDataUser, $start, $end);
01382                 mapi_freebusyenumblock_reset($enumblock);
01383 
01384                 while(true){
01385                     $blocks = mapi_freebusyenumblock_next($enumblock, 100);
01386                     if(!$blocks){
01387                         break;
01388                     }
01389                     foreach($blocks as $blockItem){
01390                         $result[] = $blockItem;
01391                     }
01392                 }
01393             }
01394         }
01395 
01396         mapi_freebusysupport_close($fbsupport);
01397         return $result;
01398     }
01399 
01407     function updateMeetingRequest($basedate = false)
01408     {
01409         $messageprops = mapi_getprops($this->message, Array($this->proptags['last_updatecounter'], $this->proptags['goid']));
01410 
01411         if(!isset($messageprops[$this->proptags['last_updatecounter']]) || !isset($messageprops[$this->proptags['goid']])) {
01412             $this->setMeetingRequest($basedate);
01413         } else {
01414             $counter = $messageprops[$this->proptags['last_updatecounter']] + 1;
01415 
01416             // increment value of last_updatecounter, last_updatecounter will be common for recurring series
01417             // so even if you sending an exception only you need to update the last_updatecounter in the recurring series message
01418             // this way we can make sure that everytime we will be using a uniwue number for every operation
01419             mapi_setprops($this->message, Array($this->proptags['last_updatecounter'] => $counter));
01420         }
01421     }
01422 
01426     function isLocalOrganiser()
01427     {
01428         if($this->isMeetingRequest() || $this->isMeetingRequestResponse()) {
01429             $messageid = $this->getAppointmentEntryID();
01430 
01431             if(!isset($messageid))
01432                 return false;
01433 
01434             $message = mapi_msgstore_openentry($this->store, $messageid);
01435 
01436             $messageprops = mapi_getprops($this->message, Array($this->proptags['goid']));
01437             $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
01438             if ($basedate) {
01439                 $recurr = new Recurrence($this->store, $message);
01440                 $attach = $recurr->getExceptionAttachment($basedate);
01441                 if ($attach) {
01442                     $occurItem = mapi_attach_openobj($attach);
01443                     $occurItemProps = mapi_getprops($occurItem, Array($this->proptags['responsestatus']));
01444                 }
01445             }
01446 
01447             $messageprops = mapi_getprops($message, Array($this->proptags['responsestatus']));
01448         }
01449 
01455         if ((isset($messageprops[$this->proptags['responsestatus']]) && $messageprops[$this->proptags['responsestatus']] == olResponseOrganized)
01456             || (isset($occurItemProps[$this->proptags['responsestatus']]) && $occurItemProps[$this->proptags['responsestatus']] == olResponseOrganized))
01457             return true;
01458         else
01459             return false;
01460     }
01461 
01466     function getAppointmentEntryID()
01467     {
01468         $messageprops = mapi_getprops($this->message, Array($this->proptags['goid2']));
01469 
01470         $goid2 = $messageprops[$this->proptags['goid2']];
01471 
01472         $items = $this->findCalendarItems($goid2);
01473 
01474         if(empty($items))
01475             return;
01476 
01477         // There should be just one item. If there are more, we just take the first one
01478         return $items[0];
01479     }
01480 
01481     /***************************************************************************************************
01482      * Support functions - INTERNAL ONLY
01483      ***************************************************************************************************
01484      */
01485 
01489     function getTrackStatus($class) {
01490         $status = olRecipientTrackStatusNone;
01491         switch($class)
01492         {
01493             case "IPM.Schedule.Meeting.Resp.Pos":
01494                 $status = olRecipientTrackStatusAccepted;
01495                 break;
01496 
01497             case "IPM.Schedule.Meeting.Resp.Tent":
01498                 $status = olRecipientTrackStatusTentative;
01499                 break;
01500 
01501             case "IPM.Schedule.Meeting.Resp.Neg":
01502                 $status = olRecipientTrackStatusDeclined;
01503                 break;
01504         }
01505         return $status;
01506     }
01507 
01508     function openParentFolder() {
01509         $messageprops = mapi_getprops($this->message, Array(PR_PARENT_ENTRYID));
01510 
01511         $parentfolder = mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
01512         return $parentfolder;
01513     }
01514 
01515     function openDefaultCalendar() {
01516         return $this->openDefaultFolder(PR_IPM_APPOINTMENT_ENTRYID);
01517     }
01518 
01519     function openDefaultOutbox($store=false) {
01520         return $this->openBaseFolder(PR_IPM_OUTBOX_ENTRYID, $store);
01521     }
01522 
01523     function openDefaultWastebasket() {
01524         return $this->openBaseFolder(PR_IPM_WASTEBASKET_ENTRYID);
01525     }
01526 
01527     function getDefaultWastebasketEntryID() {
01528         return $this->getBaseEntryID(PR_IPM_WASTEBASKET_ENTRYID);
01529     }
01530 
01531     function getDefaultSentmailEntryID($store=false) {
01532         return $this->getBaseEntryID(PR_IPM_SENTMAIL_ENTRYID, $store);
01533     }
01534 
01535     function getDefaultFolderEntryID($prop) {
01536         try {
01537             $inbox = mapi_msgstore_getreceivefolder($this->store);
01538         } catch (MAPIException $e) {
01539             // public store doesn't support this method
01540             if($e->getCode() == MAPI_E_NO_SUPPORT) {
01541                 // don't propogate this error to parent handlers, if store doesn't support it
01542                 $e->setHandled();
01543                 return;
01544             }
01545         }
01546 
01547         $inboxprops = mapi_getprops($inbox, Array($prop));
01548         if(!isset($inboxprops[$prop]))
01549             return;
01550 
01551         return $inboxprops[$prop];
01552     }
01553 
01554     function openDefaultFolder($prop) {
01555         $entryid = $this->getDefaultFolderEntryID($prop);
01556         $folder = mapi_msgstore_openentry($this->store, $entryid);
01557 
01558         return $folder;
01559     }
01560 
01561     function getBaseEntryID($prop, $store=false) {
01562         $storeprops = mapi_getprops( (($store)?$store:$this->store) , Array($prop));
01563         if(!isset($storeprops[$prop]))
01564             return;
01565 
01566         return $storeprops[$prop];
01567     }
01568 
01569     function openBaseFolder($prop, $store=false) {
01570         $entryid = $this->getBaseEntryID($prop, $store);
01571         $folder = mapi_msgstore_openentry( (($store)?$store:$this->store) , $entryid);
01572 
01573         return $folder;
01574     }
01582     function createResponse($status, $proposalStartTime=false, $proposalEndTime=false, $body=false, $store, $basedate = false, $calFolder) {
01583         $messageprops = mapi_getprops($this->message, Array(PR_SENT_REPRESENTING_ENTRYID,
01584                                                             PR_SENT_REPRESENTING_EMAIL_ADDRESS,
01585                                                             PR_SENT_REPRESENTING_ADDRTYPE,
01586                                                             PR_SENT_REPRESENTING_NAME,
01587                                                             $this->proptags['goid'],
01588                                                             $this->proptags['goid2'],
01589                                                             $this->proptags['location'],
01590                                                             $this->proptags['startdate'],
01591                                                             $this->proptags['duedate'],
01592                                                             $this->proptags['recurring'],
01593                                                             $this->proptags['recurring_pattern'],
01594                                                             $this->proptags['recurrence_data'],
01595                                                             $this->proptags['timezone_data'],
01596                                                             $this->proptags['timezone'],
01597                                                             $this->proptags['updatecounter'],
01598                                                             PR_SUBJECT,
01599                                                             PR_MESSAGE_CLASS,
01600                                                             PR_OWNER_APPT_ID,
01601                                                             $this->proptags['is_exception']
01602                                     ));
01603 
01604         if ($basedate && $messageprops[PR_MESSAGE_CLASS] != "IPM.Schedule.Meeting.Request" ){
01605             // we are creating response from a recurring calendar item object
01606             // We found basedate,so opened occurrence and get properties.
01607             $recurr = new Recurrence($store, $this->message);
01608             $exception = $recurr->getExceptionAttachment($basedate);
01609 
01610             if ($exception) {
01611                 // Exception found, Now retrieve properties
01612                 $imessage = mapi_attach_openobj($exception, 0);
01613                 $imsgprops = mapi_getprops($imessage);
01614 
01615                 // If location is provided, copy it to the response
01616                 if (isset($imsgprops[$this->proptags['location']])) {
01617                     $messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']];
01618                 }
01619 
01620                 // Update $messageprops with timings of occurrence
01621                 $messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']];
01622                 $messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']];
01623 
01624                 // Meeting related properties
01625                 $props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
01626                 $props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
01627                 $props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
01628             } else {
01629                 // Exceptions is deleted.
01630                 // Update $messageprops with timings of occurrence
01631                 $messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
01632                 $messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
01633 
01634                 $props[$this->proptags['meetingstatus']] = olNonMeeting;
01635                 $props[$this->proptags['responsestatus']] = olResponseNone;
01636             }
01637 
01638             $props[$this->proptags['recurring']] = false;
01639             $props[$this->proptags['is_exception']] = true;
01640         } else {
01641             // we are creating a response from meeting request mail (it could be recurring or non-recurring)
01642             // Send all recurrence info in response, if this is a recurrence meeting.
01643             $isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
01644             $isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']];
01645             if ($isRecurring || $isException) {
01646                 if($isRecurring) {
01647                     $props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']];
01648                 }
01649                 if($isException) {
01650                     $props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']];
01651                 }
01652                 $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
01653 
01654                 $calendaritem = mapi_msgstore_openentry($this->store, $calendaritems[0]);
01655                 $recurr = new Recurrence($store, $calendaritem);
01656             }
01657         }
01658 
01659         // we are sending a response for recurring meeting request (or exception), so set some required properties
01660         if(isset($recurr) && $recurr) {
01661             if(!empty($messageprops[$this->proptags['recurring_pattern']])) {
01662                 $props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
01663             }
01664 
01665             if(!empty($messageprops[$this->proptags['recurrence_data']])) {
01666                 $props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
01667             }
01668 
01669             $props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
01670             $props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
01671 
01672             $this->generateRecurDates($recurr, $messageprops, $props);
01673         }
01674 
01675         // Create a response message
01676         $recip = Array();
01677         $recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
01678         $recip[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
01679         $recip[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
01680         $recip[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
01681         $recip[PR_RECIPIENT_TYPE] = MAPI_TO;
01682 
01683         switch($status) {
01684             case olResponseAccepted:
01685                 $classpostfix = "Pos";
01686                 $subjectprefix = _("Accepted");
01687                 break;
01688             case olResponseDeclined:
01689                 $classpostfix = "Neg";
01690                 $subjectprefix = _("Declined");
01691                 break;
01692             case olResponseTentative:
01693                 $classpostfix = "Tent";
01694                 $subjectprefix = _("Tentatively accepted");
01695                 break;
01696         }
01697 
01698         if($proposalStartTime && $proposalEndTime){
01699             // if attendee has proposed new time then change subject prefix
01700             $subjectprefix = _("New Time Proposed");
01701         }
01702 
01703         $props[PR_SUBJECT] = $subjectprefix . ": " . $messageprops[PR_SUBJECT];
01704 
01705         $props[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Resp." . $classpostfix;
01706         if(isset($messageprops[PR_OWNER_APPT_ID]))
01707             $props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
01708 
01709         // Set GLOBALID AND CLEANGLOBALID, if exception then also set basedate into GLOBALID(0x3).
01710         $props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
01711         $props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
01712         $props[$this->proptags['updatecounter']] = $messageprops[$this->proptags['updatecounter']];
01713 
01714         // get the default store, in which we have to store the accepted email by delegate or normal user.
01715         $defaultStore = $this->openDefaultStore();
01716         $props[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($defaultStore);
01717 
01718         if($proposalStartTime && $proposalEndTime){
01719             $props[$this->proptags['proposed_start_whole']] = $proposalStartTime;
01720             $props[$this->proptags['proposed_end_whole']] = $proposalEndTime;
01721             $props[$this->proptags['proposed_duration']] = round($proposalEndTime - $proposalStartTime)/60;
01722             $props[$this->proptags['counter_proposal']] = true;
01723         }
01724 
01725         //Set body message in Appointment
01726         if(isset($body)) {
01727             $props[PR_BODY] = $this->getMeetingTimeInfo() ? $this->getMeetingTimeInfo() : $body;
01728         }
01729 
01730         // PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message
01731         $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
01732         $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
01733 
01734         // Set startdate and duedate in response mail.
01735         $props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']];
01736         $props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']];
01737 
01738         // responselocation is used in the UI in Outlook on the response message
01739         if (isset($messageprops[$this->proptags['location']])) {
01740             $props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']];
01741             $props[$this->proptags['location']] = $messageprops[$this->proptags['location']];
01742         }
01743 
01744         // check if $store is set and it is not equal to $defaultStore (means its the delegation case)
01745         if(isset($store) && isset($defaultStore)) {
01746             $storeProps = mapi_getprops($store, array(PR_ENTRYID));
01747             $defaultStoreProps = mapi_getprops($defaultStore, array(PR_ENTRYID));
01748 
01749             if($storeProps[PR_ENTRYID] !== $defaultStoreProps[PR_ENTRYID]){
01750                 // get the properties of the other user (for which the logged in user is a delegate).
01751                 $storeProps = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
01752                 $addrbook = mapi_openaddressbook($this->session);
01753                 $addrbookitem = mapi_ab_openentry($addrbook, $storeProps[PR_MAILBOX_OWNER_ENTRYID]);
01754                 $addrbookitemprops = mapi_getprops($addrbookitem, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS));
01755 
01756                 // setting the following properties will ensure that the delegation part of message.
01757                 $props[PR_SENT_REPRESENTING_ENTRYID] = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
01758                 $props[PR_SENT_REPRESENTING_NAME] = $addrbookitemprops[PR_DISPLAY_NAME];
01759                 $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $addrbookitemprops[PR_EMAIL_ADDRESS];
01760                 $props[PR_SENT_REPRESENTING_ADDRTYPE] = "ZARAFA";
01761 
01762                 // get the properties of default store and set it accordingly
01763                 $defaultStoreProps = mapi_getprops($defaultStore, array(PR_MAILBOX_OWNER_ENTRYID));
01764                 $addrbookitem = mapi_ab_openentry($addrbook, $defaultStoreProps[PR_MAILBOX_OWNER_ENTRYID]);
01765                 $addrbookitemprops = mapi_getprops($addrbookitem, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS));
01766 
01767                 // set the following properties will ensure the sender's details, which will be the default user in this case.
01768                 //the function returns array($name, $emailaddr, $addrtype, $entryid, $searchkey);
01769                 $defaultUserDetails = $this->getOwnerAddress($defaultStore);
01770                 $props[PR_SENDER_ENTRYID] = $defaultUserDetails[3];
01771                 $props[PR_SENDER_EMAIL_ADDRESS] = $defaultUserDetails[1];
01772                 $props[PR_SENDER_NAME] = $defaultUserDetails[0];
01773                 $props[PR_SENDER_ADDRTYPE] = $defaultUserDetails[2];
01774             }
01775         }
01776 
01777         // pass the default store to get the required store.
01778         $outbox = $this->openDefaultOutbox($defaultStore);
01779 
01780         $message = mapi_folder_createmessage($outbox);
01781         mapi_setprops($message, $props);
01782         mapi_message_modifyrecipients($message, MODRECIP_ADD, Array($recip));
01783         mapi_message_savechanges($message);
01784         mapi_message_submitmessage($message);
01785     }
01786 
01793     function findCalendarItems($goid, $calendar = false, $use_cleanGlobalID = false) {
01794         if(!$calendar) {
01795             // Open the Calendar
01796             $calendar = $this->openDefaultCalendar();
01797         }
01798 
01799         // Find the item by restricting all items to the correct ID
01800         $restrict = Array(RES_AND, Array());
01801 
01802         array_push($restrict[1], Array(RES_PROPERTY,
01803                                                     Array(RELOP => RELOP_EQ,
01804                                                           ULPROPTAG => ($use_cleanGlobalID ? $this->proptags['goid2'] : $this->proptags['goid']),
01805                                                           VALUE => $goid
01806                                                     )
01807                                     ));
01808 
01809         $calendarcontents = mapi_folder_getcontentstable($calendar);
01810 
01811         $rows = mapi_table_queryallrows($calendarcontents, Array(PR_ENTRYID), $restrict);
01812 
01813         if(empty($rows))
01814             return;
01815 
01816         $calendaritems = Array();
01817 
01818         // In principle, there should only be one row, but we'll handle them all just in case
01819         foreach($rows as $row) {
01820             $calendaritems[] = $row[PR_ENTRYID];
01821         }
01822 
01823         return $calendaritems;
01824     }
01825 
01826     // Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the
01827     // same SMTP address when converted to SMTP
01828     function compareABEntryIDs($entryid1, $entryid2) {
01829         // If the session was not passed, just do a 'normal' compare.
01830         if(!$this->session)
01831             return $entryid1 == $entryid2;
01832 
01833         $smtp1 = $this->getSMTPAddress($entryid1);
01834         $smtp2 = $this->getSMTPAddress($entryid2);
01835 
01836         if($smtp1 == $smtp2)
01837             return true;
01838         else
01839             return false;
01840     }
01841 
01842     // Gets the SMTP address of the passed addressbook entryid
01843     function getSMTPAddress($entryid) {
01844         if(!$this->session)
01845             return false;
01846 
01847         $ab = mapi_openaddressbook($this->session);
01848 
01849         $abitem = mapi_ab_openentry($ab, $entryid);
01850 
01851         if(!$abitem)
01852             return "";
01853 
01854         $props = mapi_getprops($abitem, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS));
01855 
01856         if($props[PR_ADDRTYPE] == "SMTP") {
01857             return $props[PR_EMAIL_ADDRESS];
01858         }
01859         else return $props[PR_SMTP_ADDRESS];
01860     }
01861 
01872     function getOwnerAddress($store, $fallbackToLoggedInUser = true)
01873     {
01874         if(!$this->session)
01875             return false;
01876 
01877         $storeProps = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID));
01878 
01879         $ownerEntryId = false;
01880         if(isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) {
01881             $ownerEntryId = $storeProps[PR_USER_ENTRYID];
01882         }
01883 
01884         if(isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) {
01885             $ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
01886         }
01887 
01888         if($ownerEntryId) {
01889             $ab = mapi_openaddressbook($this->session);
01890 
01891             $zarafaUser = mapi_ab_openentry($ab, $ownerEntryId);
01892             if(!$zarafaUser)
01893                 return false;
01894 
01895             $ownerProps = mapi_getprops($zarafaUser, array(PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS));
01896 
01897             $addrType = $ownerProps[PR_ADDRTYPE];
01898             $name = $ownerProps[PR_DISPLAY_NAME];
01899             $emailAddr = $ownerProps[PR_EMAIL_ADDRESS];
01900             $searchKey = strtoupper($addrType) . ":" . strtoupper($emailAddr);
01901             $entryId = $ownerEntryId;
01902 
01903             return array($name, $emailAddr, $addrType, $entryId, $searchKey);
01904         }
01905 
01906         return false;
01907     }
01908 
01909     // Opens this session's default message store
01910     function openDefaultStore()
01911     {
01912         $storestable = mapi_getmsgstorestable($this->session);
01913         $rows = mapi_table_queryallrows($storestable, array(PR_ENTRYID, PR_DEFAULT_STORE));
01914         $entry = false;
01915 
01916         foreach($rows as $row) {
01917             if(isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
01918                 $entryid = $row[PR_ENTRYID];
01919                 break;
01920             }
01921         }
01922 
01923         if(!$entryid)
01924             return false;
01925 
01926         return mapi_openmsgstore($this->session, $entryid);
01927     }
01936     function addOrganizer($messageProps, &$recipients, $isException = false){
01937 
01938         $hasOrganizer = false;
01939         // Check if meeting already has an organizer.
01940         foreach ($recipients as $key => $recipient){
01941             if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) {
01942                 $hasOrganizer = true;
01943             } else if ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])){
01944                 // Recipients for an occurrence
01945                 $recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse;
01946             }
01947         }
01948 
01949         if (!$hasOrganizer){
01950             // Create organizer.
01951             $organizer = array();
01952             $organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID];
01953             $organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
01954             $organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
01955             $organizer[PR_RECIPIENT_TYPE] = MAPI_TO;
01956             $organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
01957             $organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP':$messageProps[PR_SENT_REPRESENTING_ADDRTYPE];
01958             $organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
01959             $organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer;
01960 
01961             // Add organizer to recipients list.
01962             array_unshift($recipients, $organizer);
01963         }
01964     }
01965 
01973     function setRecipsFromString(&$recips, $recipString, $recipType = MAPI_TO)
01974     {
01975         $extraRecipient = array();
01976         $recipArray = explode(";", $recipString);
01977 
01978         foreach($recipArray as $recip) {
01979             $recip = trim($recip);
01980             if (!empty($recip)) {
01981                 $extraRecipient[PR_RECIPIENT_TYPE] = $recipType;
01982                 $extraRecipient[PR_DISPLAY_NAME] = $recip;
01983                 array_push($recips, $extraRecipient);
01984             }
01985         }
01986 
01987     }
01988 
01996     function doRemoveExceptionFromCalendar($basedate, $message, $store)
01997     {
01998         $recurr = new Recurrence($store, $message);
01999         $recurr->createException(array(), $basedate, true);
02000         mapi_savechanges($message);
02001     }
02002 
02008     function getBasedateFromGlobalID($goid)
02009     {
02010         $hexguid = bin2hex($goid);
02011         $hexbase = substr($hexguid, 32, 8);
02012         $day = hexdec(substr($hexbase, 6, 2));
02013         $month = hexdec(substr($hexbase, 4, 2));
02014         $year = hexdec(substr($hexbase, 0, 4));
02015 
02016         if ($day && $month && $year)
02017             return gmmktime(0, 0, 0, $month, $day, $year);
02018         else
02019             return false;
02020     }
02021 
02028     function setBasedateInGlobalID($goid, $basedate = false)
02029     {
02030         $hexguid = bin2hex($goid);
02031         $year = $basedate ? sprintf('%04s', dechex(date('Y', $basedate))) : '0000';
02032         $month = $basedate ? sprintf('%02s', dechex(date('m', $basedate))) : '00';
02033         $day = $basedate ? sprintf('%02s', dechex(date('d', $basedate))) : '00';
02034 
02035         return hex2bin(strtoupper(substr($hexguid, 0, 32) . $year . $month . $day . substr($hexguid, 40)));
02036     }
02043     function replaceAttachments($copy_from, $copy_to, $copyExceptions = true)
02044     {
02045         /* remove all old attachments */
02046         $attachmentTable = mapi_message_getattachmenttable($copy_to);
02047         if($attachmentTable) {
02048             $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME));
02049 
02050             foreach($attachments as $attach_props){
02051                 /* remove exceptions too? */
02052                 if (!$copyExceptions && $attach_props[PR_ATTACH_METHOD] == 5 && isset($attach_props[PR_EXCEPTION_STARTTIME]))
02053                     continue;
02054                 mapi_message_deleteattach($copy_to, $attach_props[PR_ATTACH_NUM]);
02055             }
02056         }
02057         $attachmentTable = false;
02058 
02059         /* copy new attachments */
02060         $attachmentTable = mapi_message_getattachmenttable($copy_from);
02061         if($attachmentTable) {
02062             $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME));
02063 
02064             foreach($attachments as $attach_props){
02065                 if (!$copyExceptions && $attach_props[PR_ATTACH_METHOD] == 5 && isset($attach_props[PR_EXCEPTION_STARTTIME]))
02066                     continue;
02067 
02068                 $attach_old = mapi_message_openattach($copy_from, (int) $attach_props[PR_ATTACH_NUM]);
02069                 $attach_newResourceMsg = mapi_message_createattach($copy_to);
02070                 mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0);
02071                 mapi_savechanges($attach_newResourceMsg);
02072             }
02073         }
02074     }
02080     function replaceRecipients($copy_from, $copy_to, $isDelegate = false)
02081     {
02082         $recipienttable = mapi_message_getrecipienttable($copy_from);
02083 
02084         // If delegate, then do not add the delegate in recipients
02085         if ($isDelegate) {
02086             $delegate = mapi_getprops($copy_from, array(PR_RECEIVED_BY_EMAIL_ADDRESS));
02087             $res = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => PR_EMAIL_ADDRESS, VALUE => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]));
02088             $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $res);
02089         } else {
02090             $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops);
02091         }
02092 
02093         $copy_to_recipientTable = mapi_message_getrecipienttable($copy_to);
02094         $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID));
02095         foreach($copy_to_recipientRows as $recipient) {
02096             mapi_message_modifyrecipients($copy_to, MODRECIP_REMOVE, array($recipient));
02097         }
02098 
02099         mapi_message_modifyrecipients($copy_to, MODRECIP_ADD, $recipients);
02100     }
02107     function bookResources($message, $cancel, $prefix, $basedate = false)
02108     {
02109         if(!$this->enableDirectBooking)
02110             return array();
02111 
02112         // Get the properties of the message
02113         $messageprops = mapi_getprops($message);
02114 
02115         if ($basedate) {
02116             $recurrItemProps = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID));
02117 
02118             $messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate);
02119             $messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
02120 
02121             // Delete properties which are not needed.
02122             $deleteProps = array($this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD);
02123             foreach ($deleteProps as $propID) {
02124                 if (isset($messageprops[$propID])) {
02125                     unset($messageprops[$propID]);
02126                 }
02127             }
02128 
02129             if (isset($messageprops[$this->proptags['recurring']])) $messageprops[$this->proptags['recurring']] = false;
02130 
02131             // Set Outlook properties
02132             $messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']];
02133             $messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']];
02134             $messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']];
02135             $messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']];
02136             $messageprops[$this->proptags['attendee_critical_change']] = time();
02137             $messageprops[$this->proptags['owner_critical_change']] = time();
02138         }
02139 
02140         // Get resource recipients
02141         $getResourcesRestriction = Array(RES_AND,
02142             Array(Array(RES_PROPERTY,
02143                 Array(RELOP => RELOP_EQ,    // Equals recipient type 3: Resource
02144                     ULPROPTAG => PR_RECIPIENT_TYPE,
02145                     VALUE => array(PR_RECIPIENT_TYPE =>MAPI_BCC)
02146                 )
02147             ))
02148         );
02149         $recipienttable = mapi_message_getrecipienttable($message);
02150         $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
02151 
02152         $this->errorSetResource = false;
02153         $resourceRecipData = Array();
02154 
02155         // Put appointment into store resource users
02156         $i = 0;
02157         $len = count($resourceRecipients);
02158         while(!$this->errorSetResource && $i < $len){
02159             $request = array(array(PR_DISPLAY_NAME => $resourceRecipients[$i][PR_DISPLAY_NAME]));
02160             $ab = mapi_openaddressbook($this->session);
02161             $ret = mapi_ab_resolvename($ab, $request, EMS_AB_ADDRESS_LOOKUP);
02162             $result = mapi_last_hresult();
02163             if ($result == NOERROR){
02164                 $result = $ret[0][PR_ENTRYID];
02165             }
02166             $resourceUsername = $ret[0][PR_EMAIL_ADDRESS];
02167             $resourceABEntryID = $ret[0][PR_ENTRYID];
02168 
02169             // Get StoreEntryID by username
02170             $user_entryid = mapi_msgstore_createentryid($this->store, $resourceUsername);
02171 
02172             // Open store of the user
02173             $userStore = mapi_openmsgstore($this->session, $user_entryid);
02174             // Open root folder
02175             $userRoot = mapi_msgstore_openentry($userStore, null);
02176             // Get calendar entryID
02177             $userRootProps = mapi_getprops($userRoot, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS));
02178 
02179             // Open Calendar folder   [check hresult==0]
02180             $accessToFolder = false;
02181             try {
02182                 $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
02183                 if($calFolder){
02184                     $calFolderProps = mapi_getProps($calFolder, Array(PR_ACCESS));
02185                     if(($calFolderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) !== 0){
02186                         $accessToFolder = true;
02187                     }
02188                 }
02189             } catch (MAPIException $e) {
02190                 $e->setHandled();
02191                 $this->errorSetResource = 1; // No access
02192             }
02193 
02194             if($accessToFolder) {
02199                 // Use PR_FREEBUSY_ENTRYIDS[1] to open folder the LocalFreeBusy msg
02200                 $localFreebusyMsg = mapi_msgstore_openentry($userStore, $userRootProps[PR_FREEBUSY_ENTRYIDS][1]);
02201                 if($localFreebusyMsg){
02202                     $props = mapi_getprops($localFreebusyMsg, array(PR_PROCESS_MEETING_REQUESTS, PR_DECLINE_RECURRING_MEETING_REQUESTS, PR_DECLINE_CONFLICTING_MEETING_REQUESTS));
02203 
02204                     $acceptMeetingRequests = ($props[PR_PROCESS_MEETING_REQUESTS])?1:0;
02205                     $declineRecurringMeetingRequests = ($props[PR_DECLINE_RECURRING_MEETING_REQUESTS])?1:0;
02206                     $declineConflictingMeetingRequests = ($props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS])?1:0;
02207                     if(!$acceptMeetingRequests){
02213                         //$errorSetResource = 2;
02214                         $this->nonAcceptingResources[] = $resourceRecipients[$i];
02215                     }else{
02216                         if($declineRecurringMeetingRequests && !$cancel){
02217                             // Check if appointment is recurring
02218                             if($messageprops[ $this->proptags['recurring'] ]){
02219                                 $this->errorSetResource = 3;
02220                             }
02221                         }
02222                         if($declineConflictingMeetingRequests && !$cancel){
02223                             // Check for conflicting items
02224                             $conflicting = false;
02225 
02226                             // Open the calendar
02227                             $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
02228 
02229                             if($calFolder) {
02230                                 if ($this->isMeetingConflicting($message, $userStore, $calFolder, $messageprops))
02231                                     $conflicting = true;
02232                             } else {
02233                                 $this->errorSetResource = 1; // No access
02234                             }
02235 
02236                             if($conflicting){
02237                                 $this->errorSetResource = 4; // Conflict
02238                             }
02239                         }
02240                     }
02241                 }
02242             }
02243 
02244             if(!$this->errorSetResource && $accessToFolder){
02250                 $rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
02251 
02258                 if(empty($rows)){
02267                     $rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
02268 
02269                     $newResourceMsg = false;
02270                     if (!empty($rows)) {
02271                         // Since we are looking for recurring item, open every result and check for 'recurring' property.
02272                         foreach($rows as $row) {
02273                             $ResourceMsg = mapi_msgstore_openentry($userStore, $row);
02274                             $ResourceMsgProps = mapi_getprops($ResourceMsg, array($this->proptags['recurring']));
02275 
02276                             if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
02277                                 $newResourceMsg = $ResourceMsg;
02278                                 break;
02279                             }
02280                         }
02281                     }
02282 
02283                     // Still no results found. I giveup, create new message.
02284                     if (!$newResourceMsg)
02285                         $newResourceMsg = mapi_folder_createmessage($calFolder);
02286                 }else{
02287                     $newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]);
02288                 }
02289 
02290                 // Prefix the subject if needed
02291                 if($prefix && isset($messageprops[PR_SUBJECT])) {
02292                     $messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT];
02293                 }
02294 
02295                 // Set status to cancelled if needed
02296                 $messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy)
02297                 if($cancel) {
02298                     $messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled
02299                     $messageprops[$this->proptags['busystatus']] = fbFree; // Free
02300                 } else {
02301                     $messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
02302                 }
02303                 $messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource autmatically accepts the appointment
02304 
02305                 $messageprops[PR_MESSAGE_CLASS] = "IPM.Appointment";
02306 
02307                 // Remove the PR_ICON_INDEX as it is not needed in the sent message and it also
02308                 // confuses the Zarafa webaccess
02309                 $messageprops[PR_ICON_INDEX] = null;
02310                 $messageprops[PR_RESPONSE_REQUESTED] = true;
02311 
02312                 $addrinfo = $this->getOwnerAddress($this->store);
02313 
02314                 if($addrinfo) {
02315                     list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo;
02316 
02317                     $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
02318                     $messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
02319                     $messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
02320                     $messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
02321                     $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
02322 
02323                     $messageprops[$this->proptags['apptreplyname']] = $ownername;
02324                     $messageprops[$this->proptags['replytime']] = time();
02325                 }
02326 
02327                 if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
02328                     $recurr = new Recurrence($userStore, $newResourceMsg);
02329 
02330                     // Copy recipients list
02331                     $reciptable = mapi_message_getrecipienttable($message);
02332                     $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
02333                     // add owner to recipient table
02334                     $this->addOrganizer($messageprops, $recips, true);
02335 
02336                     // Update occurrence
02337                     if($recurr->isException($basedate))
02338                         $recurr->modifyException($messageprops, $basedate, $recips);
02339                     else
02340                         $recurr->createException($messageprops, $basedate, false, $recips);
02341                 } else {
02342 
02343                     mapi_setprops($newResourceMsg, $messageprops);
02344 
02345                     // Copy attachments
02346                     $this->replaceAttachments($message, $newResourceMsg);
02347 
02348                     // Copy all recipients too
02349                     $this->replaceRecipients($message, $newResourceMsg);
02350 
02351                     // Now add organizer also to recipient table
02352                     $recips = Array();
02353                     $this->addOrganizer($messageprops, $recips);
02354                     mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
02355                 }
02356 
02357                 mapi_savechanges($newResourceMsg);
02358 
02359                 $resourceRecipData[] = Array(
02360                     'store' => $userStore,
02361                     'folder' => $calFolder,
02362                     'msg' => $newResourceMsg,
02363                 );
02364                 $this->includesResources = true;
02365             }else{
02370                 if(!$this->errorSetResource){
02371                     $this->errorSetResource = 1;
02372                 }
02373 
02374                 for($j = 0, $len = count($resourceRecipData); $j < $len; $j++){
02375                     // Get the EntryID
02376                     $props = mapi_message_getprops($resourceRecipData[$j]['msg']);
02377 
02378                     mapi_folder_deletemessages($resourceRecipData[$j]['folder'], Array($props[PR_ENTRYID]), DELETE_HARD_DELETE);
02379                 }
02380                 $this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME];
02381             }
02382             $i++;
02383         }
02384 
02385         /**************************************************************
02386          * Set the BCC-recipients (resources) tackstatus to accepted.
02387          */
02388         // Get resource recipients
02389         $getResourcesRestriction = Array(RES_AND,
02390             Array(Array(RES_PROPERTY,
02391                 Array(RELOP => RELOP_EQ,    // Equals recipient type 3: Resource
02392                     ULPROPTAG => PR_RECIPIENT_TYPE,
02393                     VALUE => array(PR_RECIPIENT_TYPE =>MAPI_BCC)
02394                 )
02395             ))
02396         );
02397         $recipienttable = mapi_message_getrecipienttable($message);
02398         $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
02399         if(!empty($resourceRecipients)){
02400             // Set Tracking status of resource recipients to olResponseAccepted (3)
02401             for($i = 0, $len = count($resourceRecipients); $i < $len; $i++){
02402                 $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted;
02403                 $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time();
02404             }
02405             mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients);
02406         }
02407 
02408         // Publish updated free/busy information
02409         if(!$this->errorSetResource){
02410             for($i = 0, $len = count($resourceRecipData); $i < $len; $i++){
02411                 $storeProps = mapi_msgstore_getprops($resourceRecipData[$i]['store'], array(PR_MAILBOX_OWNER_ENTRYID));
02412                 if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID])){
02413                     $pub = new FreeBusyPublish($this->session, $resourceRecipData[$i]['store'], $resourceRecipData[$i]['folder'], $storeProps[PR_MAILBOX_OWNER_ENTRYID]);
02414                     $pub->publishFB(time() - (7 * 24 * 60 * 60), 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead
02415                 }
02416             }
02417         }
02418 
02419         return $resourceRecipData;
02420     }
02433     function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move = false, $tentative, $userAction = false, $store, $isDelegate = false)
02434     {
02435         $recurr = new Recurrence($store, $recurringItem);
02436 
02437         // Copy properties from meeting request
02438         $exception_props = mapi_getprops($occurrenceItem);
02439 
02440         // Copy recipients list
02441         $reciptable = mapi_message_getrecipienttable($occurrenceItem);
02442         // If delegate, then do not add the delegate in recipients
02443         if ($isDelegate) {
02444             $delegate = mapi_getprops($this->message, array(PR_RECEIVED_BY_EMAIL_ADDRESS));
02445             $res = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => PR_EMAIL_ADDRESS, VALUE => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]));
02446             $recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
02447         } else {
02448             $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
02449         }
02450 
02451 
02452         // add owner to recipient table
02453         $this->addOrganizer($exception_props, $recips, true);
02454 
02455         // add delegator to meetings
02456         if ($isDelegate) $this->addDelegator($exception_props, $recips);
02457 
02458         $exception_props[$this->proptags['meetingstatus']] = olMeetingReceived;
02459         $exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
02460         // Set basedate property (ExceptionReplaceTime)
02461 
02462         if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
02463             if($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
02464                 $exception_props[$this->proptags['busystatus']] = $tentative;
02465             } else {
02466                 $exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
02467             }
02468             // we already have intendedbusystatus value in $exception_props so no need to copy it
02469         } else {
02470             $exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
02471         }
02472 
02473         if($userAction) {
02474             // if user has responded then set replytime
02475             $exception_props[$this->proptags['replytime']] = time();
02476         }
02477 
02478         if($recurr->isException($basedate))
02479             $recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
02480         else
02481             $recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
02482 
02483         // Move the occurrenceItem to the waste basket
02484         if ($move) {
02485             $wastebasket = $this->openDefaultWastebasket();
02486             $sourcefolder = mapi_msgstore_openentry($this->store, $exception_props[PR_PARENT_ENTRYID]);
02487             mapi_folder_copymessages($sourcefolder, Array($exception_props[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE);
02488         }
02489 
02490         mapi_savechanges($recurringItem);
02491     }
02492 
02502     function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $deletedRecips = false)
02503     {
02504         $newmessageprops = $messageprops = mapi_getprops($this->message);
02505         $new = $this->createOutgoingMessage();
02506 
02507         // Copy the entire message into the new meeting request message
02508         if ($basedate) {
02509             // messageprops contains properties of whole recurring series
02510             // and newmessageprops contains properties of exception item
02511             $newmessageprops = mapi_getprops($message);
02512 
02513             // Ensure that the correct basedate is set in the new message
02514             $newmessageprops[$this->proptags['basedate']] = $basedate;
02515 
02516             // Set isRecurring to false, because this is an exception
02517             $newmessageprops[$this->proptags['recurring']] = false;
02518 
02519             // set LID_IS_EXCEPTION to true
02520             $newmessageprops[$this->proptags['is_exception']] = true;
02521 
02522             // Set to high importance
02523             if($cancel) $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;
02524 
02525             // Set startdate and enddate of exception
02526             if ($cancel && $recurObject) {
02527                 $newmessageprops[$this->proptags['startdate']] = $recurObject->getOccurrenceStart($basedate);
02528                 $newmessageprops[$this->proptags['duedate']] = $recurObject->getOccurrenceEnd($basedate);
02529             }
02530 
02531             // Set basedate in guid (0x3)
02532             $newmessageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
02533             $newmessageprops[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
02534             $newmessageprops[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
02535 
02536             // Get deleted recipiets from exception msg
02537             $restriction = Array(RES_AND,
02538                             Array(
02539                                 Array(RES_BITMASK,
02540                                     Array(    ULTYPE        =>    BMR_NEZ,
02541                                             ULPROPTAG    =>    PR_RECIPIENT_FLAGS,
02542                                             ULMASK        =>    recipExceptionalDeleted
02543                                     )
02544                                 ),
02545                                 Array(RES_BITMASK,
02546                                     Array(    ULTYPE        =>    BMR_EQZ,
02547                                             ULPROPTAG    =>    PR_RECIPIENT_FLAGS,
02548                                             ULMASK        =>    recipOrganizer
02549                                     )
02550                                 ),
02551                             )
02552             );
02553 
02554             // In direct-booking mode, we don't need to send cancellations to resources
02555             if($this->enableDirectBooking) {
02556                 $restriction[1][] = Array(RES_PROPERTY,
02557                                         Array(RELOP => RELOP_NE,    // Does not equal recipient type: MAPI_BCC (Resource)
02558                                             ULPROPTAG => PR_RECIPIENT_TYPE,
02559                                             VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC)
02560                                         )
02561                                     );
02562             }
02563 
02564             $recipienttable = mapi_message_getrecipienttable($message);
02565             $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
02566 
02567             if (!$deletedRecips) {
02568                 $deletedRecips = array_merge(array(), $recipients);
02569             } else {
02570                 $deletedRecips = array_merge($deletedRecips, $recipients);
02571             }
02572         }
02573 
02574         // Remove the PR_ICON_INDEX as it is not needed in the sent message and it also
02575         // confuses the Zarafa webaccess
02576         $newmessageprops[PR_ICON_INDEX] = null;
02577         $newmessageprops[PR_RESPONSE_REQUESTED] = true;
02578 
02579         // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
02580         $newmessageprops[PR_START_DATE] = $newmessageprops[$this->proptags['startdate']];
02581         $newmessageprops[PR_END_DATE] = $newmessageprops[$this->proptags['duedate']];
02582 
02583         // Set updatecounter/AppointmentSequenceNumber
02584         // get the value of latest updatecounter for the whole series and use it
02585         $newmessageprops[$this->proptags['updatecounter']] = $messageprops[$this->proptags['last_updatecounter']];
02586 
02587         $meetingTimeInfo = $this->getMeetingTimeInfo();
02588 
02589         if($meetingTimeInfo)
02590             $newmessageprops[PR_BODY] = $meetingTimeInfo;
02591 
02592         // Send all recurrence info in mail, if this is a recurrence meeting.
02593         if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]) {
02594             if(!empty($messageprops[$this->proptags['recurring_pattern']])) {
02595                 $newmessageprops[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
02596             }
02597             $newmessageprops[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
02598             $newmessageprops[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
02599             $newmessageprops[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
02600 
02601             if($recurObject) {
02602                 $this->generateRecurDates($recurObject, $messageprops, $newmessageprops);
02603             }
02604         }
02605 
02606         if (isset($newmessageprops[$this->proptags['counter_proposal']])) {
02607             unset($newmessageprops[$this->proptags['counter_proposal']]);
02608         }
02609 
02610         // Prefix the subject if needed
02611         if ($prefix && isset($newmessageprops[PR_SUBJECT]))
02612             $newmessageprops[PR_SUBJECT] = $prefix . $newmessageprops[PR_SUBJECT];
02613 
02614         mapi_setprops($new, $newmessageprops);
02615 
02616         // Copy attachments
02617         $this->replaceAttachments($message, $new, $copyExceptions);
02618 
02619         // Retrieve only those recipient who should receive this meeting request.
02620         $stripResourcesRestriction = Array(RES_AND,
02621                                 Array(
02622                                     Array(RES_BITMASK,
02623                                         Array(    ULTYPE        =>    BMR_EQZ,
02624                                                 ULPROPTAG    =>    PR_RECIPIENT_FLAGS,
02625                                                 ULMASK        =>    recipExceptionalDeleted
02626                                         )
02627                                     ),
02628                                     Array(RES_BITMASK,
02629                                         Array(    ULTYPE        =>    BMR_EQZ,
02630                                                 ULPROPTAG    =>    PR_RECIPIENT_FLAGS,
02631                                                 ULMASK        =>    recipOrganizer
02632                                         )
02633                                     ),
02634                                 )
02635         );
02636 
02637         // In direct-booking mode, resources do not receive a meeting request
02638         if($this->enableDirectBooking) {
02639             $stripResourcesRestriction[1][] =
02640                                     Array(RES_PROPERTY,
02641                                         Array(RELOP => RELOP_NE,    // Does not equal recipient type: MAPI_BCC (Resource)
02642                                             ULPROPTAG => PR_RECIPIENT_TYPE,
02643                                             VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC)
02644                                         )
02645                                     );
02646         }
02647 
02648         $recipienttable = mapi_message_getrecipienttable($message);
02649         $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
02650 
02651         if ($basedate && empty($recipients)) {
02652             // Retrieve full list
02653             $recipienttable = mapi_message_getrecipienttable($this->message);
02654             $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops);
02655 
02656             // Save recipients in exceptions
02657             mapi_message_modifyrecipients($message, MODRECIP_ADD, $recipients);
02658 
02659             // Now retrieve only those recipient who should receive this meeting request.
02660             $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
02661         }
02662 
02663         //@TODO: handle nonAcceptingResources/*
02668         for($i=0;$i<count($this->nonAcceptingResources);$i++){
02669             $recipients[] = $this->nonAcceptingResources[$i];
02670         }*/
02671 
02672         if(!empty($recipients)) {
02673             // Strip out the sender/"owner" recipient
02674             mapi_message_modifyrecipients($new, MODRECIP_ADD, $recipients);
02675 
02676             // Set some properties that are different in the sent request than
02677             // in the item in our calendar
02678 
02679             // we should store busystatus value to intendedbusystatus property, because busystatus for outgoing meeting request
02680             // should always be fbTentative
02681             $newmessageprops[$this->proptags['intendedbusystatus']] = isset($newmessageprops[$this->proptags['busystatus']]) ? $newmessageprops[$this->proptags['busystatus']] : $messageprops[$this->proptags['busystatus']];
02682             $newmessageprops[$this->proptags['busystatus']] = fbTentative; // The default status when not accepted
02683             $newmessageprops[$this->proptags['responsestatus']] = olResponseNotResponded; // The recipient has not responded yet
02684             $newmessageprops[$this->proptags['attendee_critical_change']] = time();
02685             $newmessageprops[$this->proptags['owner_critical_change']] = time();
02686             $newmessageprops[$this->proptags['meetingtype']] = mtgRequest;
02687 
02688             if ($cancel) {
02689                 $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Canceled";
02690                 $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
02691                 $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
02692             } else {
02693                 $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Request";
02694                 $newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
02695             }
02696 
02697             mapi_setprops($new, $newmessageprops);
02698             mapi_message_savechanges($new);
02699 
02700             // Submit message to non-resource recipients
02701             mapi_message_submitmessage($new);
02702         }
02703 
02704         // Send cancellation to deleted attendees
02705         if ($deletedRecips && !empty($deletedRecips)) {
02706             $new = $this->createOutgoingMessage();
02707 
02708             mapi_message_modifyrecipients($new, MODRECIP_ADD, $deletedRecips);
02709 
02710             $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Canceled";
02711             $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
02712             $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
02713             $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;    // HIGH Importance
02714             if (isset($newmessageprops[PR_SUBJECT])) {
02715                 $newmessageprops[PR_SUBJECT] = _('Canceled: ') . $newmessageprops[PR_SUBJECT];
02716             }
02717 
02718             mapi_setprops($new, $newmessageprops);
02719             mapi_message_savechanges($new);
02720 
02721             // Submit message to non-resource recipients
02722             mapi_message_submitmessage($new);
02723         }
02724 
02725         // Set properties on meeting object in calendar
02726         // Set requestsent to 'true' (turns on 'tracking', etc)
02727         $props = array();
02728         $props[$this->proptags['meetingstatus']] = olMeeting;
02729         $props[$this->proptags['responsestatus']] = olResponseOrganized;
02730         $props[$this->proptags['requestsent']] = (!empty($recipients)) || ($this->includesResources && !$this->errorSetResource);
02731         $props[$this->proptags['attendee_critical_change']] = time();
02732         $props[$this->proptags['owner_critical_change']] = time();
02733         $props[$this->proptags['meetingtype']] = mtgRequest;
02734         // save the new updatecounter to exception/recurring series/normal meeting
02735         $props[$this->proptags['updatecounter']] = $newmessageprops[$this->proptags['updatecounter']];
02736 
02737         // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
02738         $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
02739         $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
02740 
02741         mapi_setprops($message, $props);
02742 
02743         // saving of these properties on calendar item should be handled by caller function
02744         // based on sending meeting request was successfull or not
02745     }
02746 
02758     function generateRecurDates($recurObject, $messageprops, &$newmessageprops)
02759     {
02760         if($messageprops[$this->proptags['startdate']] && $messageprops[$this->proptags['duedate']]) {
02761             $startDate = date("Y:n:j:G:i:s", $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['startdate']]));
02762             $endDate = date("Y:n:j:G:i:s", $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['duedate']]));
02763 
02764             $startDate = explode(":", $startDate);
02765             $endDate = explode(":", $endDate);
02766 
02767             // [0] => year, [1] => month, [2] => day, [3] => hour, [4] => minutes, [5] => seconds
02768             // RecurStartDate = year * 512 + month_number * 32 + day_number
02769             $newmessageprops[$this->proptags["start_recur_date"]] = (((int) $startDate[0]) * 512) + (((int) $startDate[1]) * 32) + ((int) $startDate[2]);
02770             // RecurStartTime = hour * 4096 + minutes * 64 + seconds
02771             $newmessageprops[$this->proptags["start_recur_time"]] = (((int) $startDate[3]) * 4096) + (((int) $startDate[4]) * 64) + ((int) $startDate[5]);
02772 
02773             $newmessageprops[$this->proptags["end_recur_date"]] = (((int) $endDate[0]) * 512) + (((int) $endDate[1]) * 32) + ((int) $endDate[2]);
02774             $newmessageprops[$this->proptags["end_recur_time"]] = (((int) $endDate[3]) * 4096) + (((int) $endDate[4]) * 64) + ((int) $endDate[5]);
02775         }
02776     }
02777 
02778     function createOutgoingMessage()
02779     {
02780         $sentprops = array();
02781         $outbox = $this->openDefaultOutbox($this->openDefaultStore());
02782 
02783         $outgoing = mapi_folder_createmessage($outbox);
02784         if(!$outgoing) return false;
02785 
02786         $addrinfo = $this->getOwnerAddress($this->store);
02787         if($addrinfo) {
02788             list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo;
02789             $sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
02790             $sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
02791             $sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
02792             $sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
02793             $sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
02794         }
02795 
02796         $sentprops[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($this->openDefaultStore());
02797 
02798         mapi_setprops($outgoing, $sentprops);
02799 
02800         return $outgoing;
02801     }
02802 
02807     function isMeetingOutOfDate()
02808     {
02809         $result = false;
02810         $store = $this->store;
02811         $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['meetingtype'], $this->proptags['owner_critical_change']));
02812 
02813         if (isset($props[$this->proptags['meetingtype']]) && ($props[$this->proptags['meetingtype']] & mtgOutOfDate) == mtgOutOfDate) {
02814             return true;
02815         }
02816 
02817         // get the basedate to check for exception
02818         $basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]);
02819 
02820         $calendarItems = $this->getCorrespondedCalendarItems();
02821 
02822         foreach($calendarItems as $calendarItem) {
02823             if ($calendarItem) {
02824                 $calendarItemProps = mapi_getprops($calendarItem, array(
02825                                                         $this->proptags['owner_critical_change'],
02826                                                         $this->proptags['updatecounter'],
02827                                                         $this->proptags['recurring']
02828                                         ));
02829 
02830                 // If these items is recurring and basedate is found then open exception to compare it with meeting request
02831                 if (isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] && $basedate) {
02832                     $recurr = new Recurrence($store, $calendarItem);
02833 
02834                     if ($recurr->isException($basedate)) {
02835                         $attach = $recurr->getExceptionAttachment($basedate);
02836                         $exception = mapi_attach_openobj($attach, 0);
02837                         $occurrenceItemProps = mapi_getprops($exception, array(
02838                                                         $this->proptags['owner_critical_change'],
02839                                                         $this->proptags['updatecounter']
02840                                             ));
02841                     }
02842 
02843                     // we found the exception, compare with it
02844                     if(isset($occurrenceItemProps)) {
02845                         if ((isset($occurrenceItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $occurrenceItemProps[$this->proptags['updatecounter']])
02846                             || (isset($occurrenceItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $occurrenceItemProps[$this->proptags['owner_critical_change']])) {
02847 
02848                             mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033));
02849                             mapi_savechanges($this->message);
02850                             $result = true;
02851                         }
02852                     } else {
02853                         // we are not able to find exception, could mean that a significant change has occured on series
02854                         // and it deleted all exceptions, so compare with series
02855                         if ((isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']])
02856                             || (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']])) {
02857 
02858                             mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033));
02859                             mapi_savechanges($this->message);
02860                             $result = true;
02861                         }
02862                     }
02863                 } else {
02864                     // normal / recurring series
02865                     if ((isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']])
02866                             || (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']])) {
02867 
02868                         mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033));
02869                         mapi_savechanges($this->message);
02870                         $result = true;
02871                     }
02872                 }
02873             }
02874         }
02875 
02876         return $result;
02877     }
02878 
02884     function isMeetingUpdated()
02885     {
02886         $result = false;
02887         $store = $this->store;
02888         $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['owner_critical_change'], $this->proptags['updatecounter']));
02889 
02890         $calendarItems = $this->getCorrespondedCalendarItems();
02891 
02892         foreach($calendarItems as $calendarItem) {
02893             if ($calendarItem) {
02894                 $calendarItemProps = mapi_getprops($calendarItem, array(
02895                                                     $this->proptags['updatecounter'],
02896                                                     $this->proptags['recurring']
02897                                     ));
02898 
02899                 if(isset($calendarItemProps[$this->proptags['updatecounter']]) && isset($props[$this->proptags['updatecounter']]) && $calendarItemProps[$this->proptags['updatecounter']] > $props[$this->proptags['updatecounter']]) {
02900                     $result = true;
02901                 }
02902             }
02903         }
02904 
02905         return $result;
02906     }
02907 
02920     function checkSignificantChanges($oldProps, $basedate, $isRecurrenceChanged = false)
02921     {
02922         $message = null;
02923         $attach = null;
02924 
02925         // If basedate is specified then we need to open exception message to clear recipient responses
02926         if($basedate) {
02927             $recurrence = new Recurrence($this->store, $this->message);
02928             if($recurrence->isException($basedate)){
02929                 $attach = $recurrence->getExceptionAttachment($basedate);
02930                 if ($attach) {
02931                     $message = mapi_attach_openobj($attach, MAPI_MODIFY);
02932                 }
02933             }
02934         } else {
02935             // use normal message or recurring series message
02936             $message = $this->message;
02937         }
02938 
02939         if(!$message) {
02940             return;
02941         }
02942 
02943         $newProps = mapi_getprops($message, array($this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']));
02944 
02945         // Check whether message is updated or not.
02946         if(isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
02947             return;
02948         }
02949 
02950         if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']])
02951             || ($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']])
02952             || $isRecurrenceChanged) {
02953             $this->clearRecipientResponse($message);
02954 
02955             mapi_setprops($message, array($this->proptags['owner_critical_change'] => time()));
02956 
02957             mapi_savechanges($message);
02958             if ($attach) { // Also save attachment Object.
02959                 mapi_savechanges($attach);
02960             }
02961         }
02962     }
02963 
02968     function clearRecipientResponse($message)
02969     {
02970         $recipTable = mapi_message_getrecipienttable($message);
02971         $recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops);
02972 
02973         foreach($recipsRows as $recipient) {
02974             if(($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer){
02975                 // Recipient is attendee, set the trackstatus to "Not Responded"
02976                 $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
02977             } else {
02978                 // Recipient is organizer, this is not possible, but for safety
02979                 // it is best to clear the trackstatus for him as well by setting
02980                 // the trackstatus to "Organized".
02981                 $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
02982             }
02983             mapi_message_modifyrecipients($message, MODRECIP_MODIFY, array($recipient));
02984         }
02985     }
02986 
02992     function getCorrespondedCalendarItems()
02993     {
02994         $store = $this->store;
02995         $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME));
02996 
02997         $basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]);
02998 
02999         // If Delegate is processing mr for Delegator then retrieve Delegator's store and calendar.
03000         if (isset($props[PR_RCVD_REPRESENTING_NAME])) {
03001             $delegatorStore = $this->getDelegatorStore($props);
03002             $store = $delegatorStore['store'];
03003             $calFolder = $delegatorStore['calFolder'];
03004         } else {
03005             $calFolder = $this->openDefaultCalendar();
03006         }
03007 
03008         // Finding item in calendar with GlobalID(0x3), not necessary that attendee is having recurring item, he/she can also have only a occurrence
03009         $entryids = $this->findCalendarItems($props[$this->proptags['goid']], $calFolder);
03010 
03011         // Basedate found, so this meeting request is an update of an occurrence.
03012         if ($basedate) {
03013             if (!$entryids) {
03014                 // Find main recurring item in calendar with GlobalID(0x23)
03015                 $entryids = $this->findCalendarItems($props[$this->proptags['goid2']], $calFolder);
03016             }
03017         }
03018 
03019         $calendarItems = array();
03020         if ($entryids) {
03021             foreach($entryids as $entryid) {
03022                 $calendarItems[] = mapi_msgstore_openentry($store, $entryid);
03023             }
03024         }
03025 
03026         return $calendarItems;
03027     }
03028 
03034     function isMeetingConflicting($message = false, $userStore = false, $calFolder = false, $msgprops = false)
03035     {
03036         $returnValue = false;
03037         $conflicting = false;
03038         $noOfInstances = 0;
03039 
03040         if (!$message) $message = $this->message;
03041 
03042         if (!$userStore) $userStore = $this->store;
03043 
03044         if (!$calFolder) {
03045             $root = mapi_msgstore_openentry($userStore);
03046             $rootprops = mapi_getprops($root, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS));
03047 
03048             if(!isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID])) {
03049                 return;
03050             }
03051 
03052             $calFolder = mapi_msgstore_openentry($userStore, $rootprops[PR_IPM_APPOINTMENT_ENTRYID]);
03053         }
03054 
03055         if (!$msgprops) $msgprops = mapi_getprops($message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['recurring'], $this->proptags['clipstart'], $this->proptags['clipend']));
03056 
03057         if ($calFolder) {
03058             // Meeting request is recurring, so get all occurrence and check for each occurrence whether it conflicts with other appointments in Calendar.
03059             if (isset($msgprops[$this->proptags['recurring']]) && $msgprops[$this->proptags['recurring']]) {
03060                 // Apply recurrence class and retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
03061                 $recurr = new Recurrence($userStore, $message);
03062                 $items = $recurr->getItems($msgprops[$this->proptags['clipstart']], $msgprops[$this->proptags['clipend']] * (24*24*60), 30);
03063 
03064                 foreach ($items as $item) {
03065                     // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
03066                     $calendarItems = $recurr->getCalendarItems($userStore, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], array($this->proptags['goid'], $this->proptags['busystatus'], PR_OWNER_APPT_ID));
03067 
03068                     foreach ($calendarItems as $calendarItem) {
03069                         if ($calendarItem[$this->proptags['busystatus']] != fbFree) {
03074                             if(isset($calendarItem[$this->proptags['goid']])) {
03075                                 if ($calendarItem[$this->proptags['goid']] !== $msgprops[$this->proptags['goid']]) {
03076                                     $noOfInstances++;
03077                                     break;
03078                                 }
03079                             } else {
03080                                 $noOfInstances++;
03081                                 break;
03082                             }
03083                         }
03084                     }
03085                 }
03086 
03087                 $returnValue = $noOfInstances;
03088             } else {
03089                 // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
03090                 $items = getCalendarItems($userStore, $calFolder, $msgprops[$this->proptags['startdate']], $msgprops[$this->proptags['duedate']], array($this->proptags['goid'], $this->proptags['busystatus'], PR_OWNER_APPT_ID));
03091 
03092                 foreach($items as $item) {
03093                     if ($item[$this->proptags['busystatus']] != fbFree) {
03094                         if(isset($item[$this->proptags['goid']])) {
03095                             if (($item[$this->proptags['goid']] !== $msgprops[$this->proptags['goid']])
03096                                 && ($item[$this->proptags['goid']] !== $msgprops[$this->proptags['goid2']])) {
03097                                 $conflicting = true;
03098                                 break;
03099                             }
03100                         } else {
03101                             $conflicting = true;
03102                             break;
03103                         }
03104                     }
03105                 }
03106 
03107                 if ($conflicting) $returnValue = true;
03108             }
03109         }
03110         return $returnValue;
03111     }
03112 
03121     function addDelegator($messageProps, &$recipients)
03122     {
03123         $hasDelegator = false;
03124         // Check if meeting already has an organizer.
03125         foreach ($recipients as $key => $recipient){
03126             if (isset($messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) && $recipient[PR_EMAIL_ADDRESS] == $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS])
03127                 $hasDelegator = true;
03128         }
03129 
03130         if (!$hasDelegator){
03131             // Create delegator.
03132             $delegator = array();
03133             $delegator[PR_ENTRYID] = $messageProps[PR_RCVD_REPRESENTING_ENTRYID];
03134             $delegator[PR_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
03135             $delegator[PR_EMAIL_ADDRESS] = $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS];
03136             $delegator[PR_RECIPIENT_TYPE] = MAPI_TO;
03137             $delegator[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
03138             $delegator[PR_ADDRTYPE] = empty($messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]) ? 'SMTP':$messageProps[PR_RCVD_REPRESENTING_ADDRTYPE];
03139             $delegator[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
03140             $delegator[PR_RECIPIENT_FLAGS] = recipSendable;
03141 
03142             // Add organizer to recipients list.
03143             array_unshift($recipients, $delegator);
03144         }
03145     }
03146 
03147     function getDelegatorStore($messageprops)
03148     {
03149         // Find the organiser of appointment in addressbook
03150         $delegatorName = array(array(PR_DISPLAY_NAME => $messageprops[PR_RCVD_REPRESENTING_NAME]));
03151         $ab = mapi_openaddressbook($this->session);
03152         $user = mapi_ab_resolvename($ab, $delegatorName, EMS_AB_ADDRESS_LOOKUP);
03153 
03154         // Get StoreEntryID by username
03155         $delegatorEntryid = mapi_msgstore_createentryid($this->store, $user[0][PR_EMAIL_ADDRESS]);
03156         // Open store of the delegator
03157         $delegatorStore = mapi_openmsgstore($this->session, $delegatorEntryid);
03158         // Open root folder
03159         $delegatorRoot = mapi_msgstore_openentry($delegatorStore, null);
03160         // Get calendar entryID
03161         $delegatorRootProps = mapi_getprops($delegatorRoot, array(PR_IPM_APPOINTMENT_ENTRYID));
03162         // Open the calendar Folder
03163         $calFolder = mapi_msgstore_openentry($delegatorStore, $delegatorRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
03164 
03165         return Array('store' => $delegatorStore, 'calFolder' => $calFolder);
03166     }
03167 
03174     function getMeetingTimeInfo()
03175     {
03176         return $this->meetingTimeInfo;
03177     }
03178 
03185     function setMeetingTimeInfo($meetingTimeInfo)
03186     {
03187         $this->meetingTimeInfo = $meetingTimeInfo;
03188     }
03189 }
03190 ?>